1 /**
2 * Simple build automation tool.
3 *
4 * Authors:
5 * Bob Jamison
6 *
7 * Copyright (C) 2006-2008 Bob Jamison
8 *
9 * This library is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU Lesser General Public
11 * License as published by the Free Software Foundation; either
12 * version 2.1 of the License, or (at your option) any later version.
13 *
14 * This library is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * Lesser General Public License for more details.
18 *
19 * You should have received a copy of the GNU Lesser General Public
20 * License along with this library; if not, write to the Free Software
21 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
22 */
24 /**
25 * To use this file, compile with:
26 * <pre>
27 * g++ -O3 buildtool.cpp -o btool.exe
28 * (or whatever your compiler might be)
29 * Then
30 * btool
31 * or
32 * btool {target}
33 *
34 * Note: if you are using MinGW, and a not very recent version of it,
35 * gettimeofday() might be missing. If so, just build this file with
36 * this command:
37 * g++ -O3 -DNEED_GETTIMEOFDAY buildtool.cpp -o btool.exe
38 *
39 */
41 #define BUILDTOOL_VERSION "BuildTool v0.9.4"
43 #include <stdio.h>
44 #include <fcntl.h>
45 #include <unistd.h>
46 #include <stdarg.h>
47 #include <sys/stat.h>
48 #include <time.h>
49 #include <sys/time.h>
50 #include <utime.h>
51 #include <dirent.h>
53 #include <string>
54 #include <map>
55 #include <set>
56 #include <vector>
57 #include <algorithm>
60 #ifdef __WIN32__
61 #include <windows.h>
62 #endif
65 #include <errno.h>
68 //########################################################################
69 //# Definition of gettimeofday() for those who don't have it
70 //########################################################################
71 #ifdef NEED_GETTIMEOFDAY
72 #include <sys/timeb.h>
74 struct timezone {
75 int tz_minuteswest; /* minutes west of Greenwich */
76 int tz_dsttime; /* type of dst correction */
77 };
79 static int gettimeofday (struct timeval *tv, struct timezone *tz)
80 {
81 struct _timeb tb;
83 if (!tv)
84 return (-1);
86 _ftime (&tb);
87 tv->tv_sec = tb.time;
88 tv->tv_usec = tb.millitm * 1000 + 500;
89 if (tz)
90 {
91 tz->tz_minuteswest = -60 * _timezone;
92 tz->tz_dsttime = _daylight;
93 }
94 return 0;
95 }
97 #endif
105 namespace buildtool
106 {
111 //########################################################################
112 //########################################################################
113 //## R E G E X P
114 //########################################################################
115 //########################################################################
117 /**
118 * This is the T-Rex regular expression library, which we
119 * gratefully acknowledge. It's clean code and small size allow
120 * us to embed it in BuildTool without adding a dependency
121 *
122 */
124 //begin trex.h
126 #ifndef _TREX_H_
127 #define _TREX_H_
128 /***************************************************************
129 T-Rex a tiny regular expression library
131 Copyright (C) 2003-2006 Alberto Demichelis
133 This software is provided 'as-is', without any express
134 or implied warranty. In no event will the authors be held
135 liable for any damages arising from the use of this software.
137 Permission is granted to anyone to use this software for
138 any purpose, including commercial applications, and to alter
139 it and redistribute it freely, subject to the following restrictions:
141 1. The origin of this software must not be misrepresented;
142 you must not claim that you wrote the original software.
143 If you use this software in a product, an acknowledgment
144 in the product documentation would be appreciated but
145 is not required.
147 2. Altered source versions must be plainly marked as such,
148 and must not be misrepresented as being the original software.
150 3. This notice may not be removed or altered from any
151 source distribution.
153 ****************************************************************/
155 #ifdef _UNICODE
156 #define TRexChar unsigned short
157 #define MAX_CHAR 0xFFFF
158 #define _TREXC(c) L##c
159 #define trex_strlen wcslen
160 #define trex_printf wprintf
161 #else
162 #define TRexChar char
163 #define MAX_CHAR 0xFF
164 #define _TREXC(c) (c)
165 #define trex_strlen strlen
166 #define trex_printf printf
167 #endif
169 #ifndef TREX_API
170 #define TREX_API extern
171 #endif
173 #define TRex_True 1
174 #define TRex_False 0
176 typedef unsigned int TRexBool;
177 typedef struct TRex TRex;
179 typedef struct {
180 const TRexChar *begin;
181 int len;
182 } TRexMatch;
184 TREX_API TRex *trex_compile(const TRexChar *pattern,const TRexChar **error);
185 TREX_API void trex_free(TRex *exp);
186 TREX_API TRexBool trex_match(TRex* exp,const TRexChar* text);
187 TREX_API TRexBool trex_search(TRex* exp,const TRexChar* text, const TRexChar** out_begin, const TRexChar** out_end);
188 TREX_API TRexBool trex_searchrange(TRex* exp,const TRexChar* text_begin,const TRexChar* text_end,const TRexChar** out_begin, const TRexChar** out_end);
189 TREX_API int trex_getsubexpcount(TRex* exp);
190 TREX_API TRexBool trex_getsubexp(TRex* exp, int n, TRexMatch *subexp);
192 #endif
194 //end trex.h
196 //start trex.c
199 #include <stdio.h>
200 #include <string>
202 /* see copyright notice in trex.h */
203 #include <string.h>
204 #include <stdlib.h>
205 #include <ctype.h>
206 #include <setjmp.h>
207 //#include "trex.h"
209 #ifdef _UINCODE
210 #define scisprint iswprint
211 #define scstrlen wcslen
212 #define scprintf wprintf
213 #define _SC(x) L(x)
214 #else
215 #define scisprint isprint
216 #define scstrlen strlen
217 #define scprintf printf
218 #define _SC(x) (x)
219 #endif
221 #ifdef _DEBUG
222 #include <stdio.h>
224 static const TRexChar *g_nnames[] =
225 {
226 _SC("NONE"),_SC("OP_GREEDY"), _SC("OP_OR"),
227 _SC("OP_EXPR"),_SC("OP_NOCAPEXPR"),_SC("OP_DOT"), _SC("OP_CLASS"),
228 _SC("OP_CCLASS"),_SC("OP_NCLASS"),_SC("OP_RANGE"),_SC("OP_CHAR"),
229 _SC("OP_EOL"),_SC("OP_BOL"),_SC("OP_WB")
230 };
232 #endif
233 #define OP_GREEDY (MAX_CHAR+1) // * + ? {n}
234 #define OP_OR (MAX_CHAR+2)
235 #define OP_EXPR (MAX_CHAR+3) //parentesis ()
236 #define OP_NOCAPEXPR (MAX_CHAR+4) //parentesis (?:)
237 #define OP_DOT (MAX_CHAR+5)
238 #define OP_CLASS (MAX_CHAR+6)
239 #define OP_CCLASS (MAX_CHAR+7)
240 #define OP_NCLASS (MAX_CHAR+8) //negates class the [^
241 #define OP_RANGE (MAX_CHAR+9)
242 #define OP_CHAR (MAX_CHAR+10)
243 #define OP_EOL (MAX_CHAR+11)
244 #define OP_BOL (MAX_CHAR+12)
245 #define OP_WB (MAX_CHAR+13)
247 #define TREX_SYMBOL_ANY_CHAR ('.')
248 #define TREX_SYMBOL_GREEDY_ONE_OR_MORE ('+')
249 #define TREX_SYMBOL_GREEDY_ZERO_OR_MORE ('*')
250 #define TREX_SYMBOL_GREEDY_ZERO_OR_ONE ('?')
251 #define TREX_SYMBOL_BRANCH ('|')
252 #define TREX_SYMBOL_END_OF_STRING ('$')
253 #define TREX_SYMBOL_BEGINNING_OF_STRING ('^')
254 #define TREX_SYMBOL_ESCAPE_CHAR ('\\')
257 typedef int TRexNodeType;
259 typedef struct tagTRexNode{
260 TRexNodeType type;
261 int left;
262 int right;
263 int next;
264 }TRexNode;
266 struct TRex{
267 const TRexChar *_eol;
268 const TRexChar *_bol;
269 const TRexChar *_p;
270 int _first;
271 int _op;
272 TRexNode *_nodes;
273 int _nallocated;
274 int _nsize;
275 int _nsubexpr;
276 TRexMatch *_matches;
277 int _currsubexp;
278 void *_jmpbuf;
279 const TRexChar **_error;
280 };
282 static int trex_list(TRex *exp);
284 static int trex_newnode(TRex *exp, TRexNodeType type)
285 {
286 TRexNode n;
287 int newid;
288 n.type = type;
289 n.next = n.right = n.left = -1;
290 if(type == OP_EXPR)
291 n.right = exp->_nsubexpr++;
292 if(exp->_nallocated < (exp->_nsize + 1)) {
293 //int oldsize = exp->_nallocated;
294 exp->_nallocated *= 2;
295 exp->_nodes = (TRexNode *)realloc(exp->_nodes, exp->_nallocated * sizeof(TRexNode));
296 }
297 exp->_nodes[exp->_nsize++] = n;
298 newid = exp->_nsize - 1;
299 return (int)newid;
300 }
302 static void trex_error(TRex *exp,const TRexChar *error)
303 {
304 if(exp->_error) *exp->_error = error;
305 longjmp(*((jmp_buf*)exp->_jmpbuf),-1);
306 }
308 static void trex_expect(TRex *exp, int n){
309 if((*exp->_p) != n)
310 trex_error(exp, _SC("expected paren"));
311 exp->_p++;
312 }
314 static TRexChar trex_escapechar(TRex *exp)
315 {
316 if(*exp->_p == TREX_SYMBOL_ESCAPE_CHAR){
317 exp->_p++;
318 switch(*exp->_p) {
319 case 'v': exp->_p++; return '\v';
320 case 'n': exp->_p++; return '\n';
321 case 't': exp->_p++; return '\t';
322 case 'r': exp->_p++; return '\r';
323 case 'f': exp->_p++; return '\f';
324 default: return (*exp->_p++);
325 }
326 } else if(!scisprint(*exp->_p)) trex_error(exp,_SC("letter expected"));
327 return (*exp->_p++);
328 }
330 static int trex_charclass(TRex *exp,int classid)
331 {
332 int n = trex_newnode(exp,OP_CCLASS);
333 exp->_nodes[n].left = classid;
334 return n;
335 }
337 static int trex_charnode(TRex *exp,TRexBool isclass)
338 {
339 TRexChar t;
340 if(*exp->_p == TREX_SYMBOL_ESCAPE_CHAR) {
341 exp->_p++;
342 switch(*exp->_p) {
343 case 'n': exp->_p++; return trex_newnode(exp,'\n');
344 case 't': exp->_p++; return trex_newnode(exp,'\t');
345 case 'r': exp->_p++; return trex_newnode(exp,'\r');
346 case 'f': exp->_p++; return trex_newnode(exp,'\f');
347 case 'v': exp->_p++; return trex_newnode(exp,'\v');
348 case 'a': case 'A': case 'w': case 'W': case 's': case 'S':
349 case 'd': case 'D': case 'x': case 'X': case 'c': case 'C':
350 case 'p': case 'P': case 'l': case 'u':
351 {
352 t = *exp->_p; exp->_p++;
353 return trex_charclass(exp,t);
354 }
355 case 'b':
356 case 'B':
357 if(!isclass) {
358 int node = trex_newnode(exp,OP_WB);
359 exp->_nodes[node].left = *exp->_p;
360 exp->_p++;
361 return node;
362 } //else default
363 default:
364 t = *exp->_p; exp->_p++;
365 return trex_newnode(exp,t);
366 }
367 }
368 else if(!scisprint(*exp->_p)) {
370 trex_error(exp,_SC("letter expected"));
371 }
372 t = *exp->_p; exp->_p++;
373 return trex_newnode(exp,t);
374 }
375 static int trex_class(TRex *exp)
376 {
377 int ret = -1;
378 int first = -1,chain;
379 if(*exp->_p == TREX_SYMBOL_BEGINNING_OF_STRING){
380 ret = trex_newnode(exp,OP_NCLASS);
381 exp->_p++;
382 }else ret = trex_newnode(exp,OP_CLASS);
384 if(*exp->_p == ']') trex_error(exp,_SC("empty class"));
385 chain = ret;
386 while(*exp->_p != ']' && exp->_p != exp->_eol) {
387 if(*exp->_p == '-' && first != -1){
388 int r,t;
389 if(*exp->_p++ == ']') trex_error(exp,_SC("unfinished range"));
390 r = trex_newnode(exp,OP_RANGE);
391 if(first>*exp->_p) trex_error(exp,_SC("invalid range"));
392 if(exp->_nodes[first].type == OP_CCLASS) trex_error(exp,_SC("cannot use character classes in ranges"));
393 exp->_nodes[r].left = exp->_nodes[first].type;
394 t = trex_escapechar(exp);
395 exp->_nodes[r].right = t;
396 exp->_nodes[chain].next = r;
397 chain = r;
398 first = -1;
399 }
400 else{
401 if(first!=-1){
402 int c = first;
403 exp->_nodes[chain].next = c;
404 chain = c;
405 first = trex_charnode(exp,TRex_True);
406 }
407 else{
408 first = trex_charnode(exp,TRex_True);
409 }
410 }
411 }
412 if(first!=-1){
413 int c = first;
414 exp->_nodes[chain].next = c;
415 chain = c;
416 first = -1;
417 }
418 /* hack? */
419 exp->_nodes[ret].left = exp->_nodes[ret].next;
420 exp->_nodes[ret].next = -1;
421 return ret;
422 }
424 static int trex_parsenumber(TRex *exp)
425 {
426 int ret = *exp->_p-'0';
427 int positions = 10;
428 exp->_p++;
429 while(isdigit(*exp->_p)) {
430 ret = ret*10+(*exp->_p++-'0');
431 if(positions==1000000000) trex_error(exp,_SC("overflow in numeric constant"));
432 positions *= 10;
433 };
434 return ret;
435 }
437 static int trex_element(TRex *exp)
438 {
439 int ret = -1;
440 switch(*exp->_p)
441 {
442 case '(': {
443 int expr,newn;
444 exp->_p++;
447 if(*exp->_p =='?') {
448 exp->_p++;
449 trex_expect(exp,':');
450 expr = trex_newnode(exp,OP_NOCAPEXPR);
451 }
452 else
453 expr = trex_newnode(exp,OP_EXPR);
454 newn = trex_list(exp);
455 exp->_nodes[expr].left = newn;
456 ret = expr;
457 trex_expect(exp,')');
458 }
459 break;
460 case '[':
461 exp->_p++;
462 ret = trex_class(exp);
463 trex_expect(exp,']');
464 break;
465 case TREX_SYMBOL_END_OF_STRING: exp->_p++; ret = trex_newnode(exp,OP_EOL);break;
466 case TREX_SYMBOL_ANY_CHAR: exp->_p++; ret = trex_newnode(exp,OP_DOT);break;
467 default:
468 ret = trex_charnode(exp,TRex_False);
469 break;
470 }
472 {
473 int op;
474 TRexBool isgreedy = TRex_False;
475 unsigned short p0 = 0, p1 = 0;
476 switch(*exp->_p){
477 case TREX_SYMBOL_GREEDY_ZERO_OR_MORE: p0 = 0; p1 = 0xFFFF; exp->_p++; isgreedy = TRex_True; break;
478 case TREX_SYMBOL_GREEDY_ONE_OR_MORE: p0 = 1; p1 = 0xFFFF; exp->_p++; isgreedy = TRex_True; break;
479 case TREX_SYMBOL_GREEDY_ZERO_OR_ONE: p0 = 0; p1 = 1; exp->_p++; isgreedy = TRex_True; break;
480 case '{':
481 exp->_p++;
482 if(!isdigit(*exp->_p)) trex_error(exp,_SC("number expected"));
483 p0 = (unsigned short)trex_parsenumber(exp);
484 /*******************************/
485 switch(*exp->_p) {
486 case '}':
487 p1 = p0; exp->_p++;
488 break;
489 case ',':
490 exp->_p++;
491 p1 = 0xFFFF;
492 if(isdigit(*exp->_p)){
493 p1 = (unsigned short)trex_parsenumber(exp);
494 }
495 trex_expect(exp,'}');
496 break;
497 default:
498 trex_error(exp,_SC(", or } expected"));
499 }
500 /*******************************/
501 isgreedy = TRex_True;
502 break;
504 }
505 if(isgreedy) {
506 int nnode = trex_newnode(exp,OP_GREEDY);
507 op = OP_GREEDY;
508 exp->_nodes[nnode].left = ret;
509 exp->_nodes[nnode].right = ((p0)<<16)|p1;
510 ret = nnode;
511 }
512 }
513 if((*exp->_p != TREX_SYMBOL_BRANCH) && (*exp->_p != ')') && (*exp->_p != TREX_SYMBOL_GREEDY_ZERO_OR_MORE) && (*exp->_p != TREX_SYMBOL_GREEDY_ONE_OR_MORE) && (*exp->_p != '\0')) {
514 int nnode = trex_element(exp);
515 exp->_nodes[ret].next = nnode;
516 }
518 return ret;
519 }
521 static int trex_list(TRex *exp)
522 {
523 int ret=-1,e;
524 if(*exp->_p == TREX_SYMBOL_BEGINNING_OF_STRING) {
525 exp->_p++;
526 ret = trex_newnode(exp,OP_BOL);
527 }
528 e = trex_element(exp);
529 if(ret != -1) {
530 exp->_nodes[ret].next = e;
531 }
532 else ret = e;
534 if(*exp->_p == TREX_SYMBOL_BRANCH) {
535 int temp,tright;
536 exp->_p++;
537 temp = trex_newnode(exp,OP_OR);
538 exp->_nodes[temp].left = ret;
539 tright = trex_list(exp);
540 exp->_nodes[temp].right = tright;
541 ret = temp;
542 }
543 return ret;
544 }
546 static TRexBool trex_matchcclass(int cclass,TRexChar c)
547 {
548 switch(cclass) {
549 case 'a': return isalpha(c)?TRex_True:TRex_False;
550 case 'A': return !isalpha(c)?TRex_True:TRex_False;
551 case 'w': return (isalnum(c) || c == '_')?TRex_True:TRex_False;
552 case 'W': return (!isalnum(c) && c != '_')?TRex_True:TRex_False;
553 case 's': return isspace(c)?TRex_True:TRex_False;
554 case 'S': return !isspace(c)?TRex_True:TRex_False;
555 case 'd': return isdigit(c)?TRex_True:TRex_False;
556 case 'D': return !isdigit(c)?TRex_True:TRex_False;
557 case 'x': return isxdigit(c)?TRex_True:TRex_False;
558 case 'X': return !isxdigit(c)?TRex_True:TRex_False;
559 case 'c': return iscntrl(c)?TRex_True:TRex_False;
560 case 'C': return !iscntrl(c)?TRex_True:TRex_False;
561 case 'p': return ispunct(c)?TRex_True:TRex_False;
562 case 'P': return !ispunct(c)?TRex_True:TRex_False;
563 case 'l': return islower(c)?TRex_True:TRex_False;
564 case 'u': return isupper(c)?TRex_True:TRex_False;
565 }
566 return TRex_False; /*cannot happen*/
567 }
569 static TRexBool trex_matchclass(TRex* exp,TRexNode *node,TRexChar c)
570 {
571 do {
572 switch(node->type) {
573 case OP_RANGE:
574 if(c >= node->left && c <= node->right) return TRex_True;
575 break;
576 case OP_CCLASS:
577 if(trex_matchcclass(node->left,c)) return TRex_True;
578 break;
579 default:
580 if(c == node->type)return TRex_True;
581 }
582 } while((node->next != -1) && (node = &exp->_nodes[node->next]));
583 return TRex_False;
584 }
586 static const TRexChar *trex_matchnode(TRex* exp,TRexNode *node,const TRexChar *str,TRexNode *next)
587 {
589 TRexNodeType type = node->type;
590 switch(type) {
591 case OP_GREEDY: {
592 //TRexNode *greedystop = (node->next != -1) ? &exp->_nodes[node->next] : NULL;
593 TRexNode *greedystop = NULL;
594 int p0 = (node->right >> 16)&0x0000FFFF, p1 = node->right&0x0000FFFF, nmaches = 0;
595 const TRexChar *s=str, *good = str;
597 if(node->next != -1) {
598 greedystop = &exp->_nodes[node->next];
599 }
600 else {
601 greedystop = next;
602 }
604 while((nmaches == 0xFFFF || nmaches < p1)) {
606 const TRexChar *stop;
607 if(!(s = trex_matchnode(exp,&exp->_nodes[node->left],s,greedystop)))
608 break;
609 nmaches++;
610 good=s;
611 if(greedystop) {
612 //checks that 0 matches satisfy the expression(if so skips)
613 //if not would always stop(for instance if is a '?')
614 if(greedystop->type != OP_GREEDY ||
615 (greedystop->type == OP_GREEDY && ((greedystop->right >> 16)&0x0000FFFF) != 0))
616 {
617 TRexNode *gnext = NULL;
618 if(greedystop->next != -1) {
619 gnext = &exp->_nodes[greedystop->next];
620 }else if(next && next->next != -1){
621 gnext = &exp->_nodes[next->next];
622 }
623 stop = trex_matchnode(exp,greedystop,s,gnext);
624 if(stop) {
625 //if satisfied stop it
626 if(p0 == p1 && p0 == nmaches) break;
627 else if(nmaches >= p0 && p1 == 0xFFFF) break;
628 else if(nmaches >= p0 && nmaches <= p1) break;
629 }
630 }
631 }
633 if(s >= exp->_eol)
634 break;
635 }
636 if(p0 == p1 && p0 == nmaches) return good;
637 else if(nmaches >= p0 && p1 == 0xFFFF) return good;
638 else if(nmaches >= p0 && nmaches <= p1) return good;
639 return NULL;
640 }
641 case OP_OR: {
642 const TRexChar *asd = str;
643 TRexNode *temp=&exp->_nodes[node->left];
644 while( (asd = trex_matchnode(exp,temp,asd,NULL)) ) {
645 if(temp->next != -1)
646 temp = &exp->_nodes[temp->next];
647 else
648 return asd;
649 }
650 asd = str;
651 temp = &exp->_nodes[node->right];
652 while( (asd = trex_matchnode(exp,temp,asd,NULL)) ) {
653 if(temp->next != -1)
654 temp = &exp->_nodes[temp->next];
655 else
656 return asd;
657 }
658 return NULL;
659 break;
660 }
661 case OP_EXPR:
662 case OP_NOCAPEXPR:{
663 TRexNode *n = &exp->_nodes[node->left];
664 const TRexChar *cur = str;
665 int capture = -1;
666 if(node->type != OP_NOCAPEXPR && node->right == exp->_currsubexp) {
667 capture = exp->_currsubexp;
668 exp->_matches[capture].begin = cur;
669 exp->_currsubexp++;
670 }
672 do {
673 TRexNode *subnext = NULL;
674 if(n->next != -1) {
675 subnext = &exp->_nodes[n->next];
676 }else {
677 subnext = next;
678 }
679 if(!(cur = trex_matchnode(exp,n,cur,subnext))) {
680 if(capture != -1){
681 exp->_matches[capture].begin = 0;
682 exp->_matches[capture].len = 0;
683 }
684 return NULL;
685 }
686 } while((n->next != -1) && (n = &exp->_nodes[n->next]));
688 if(capture != -1)
689 exp->_matches[capture].len = cur - exp->_matches[capture].begin;
690 return cur;
691 }
692 case OP_WB:
693 if((str == exp->_bol && !isspace(*str))
694 || (str == exp->_eol && !isspace(*(str-1)))
695 || (!isspace(*str) && isspace(*(str+1)))
696 || (isspace(*str) && !isspace(*(str+1))) ) {
697 return (node->left == 'b')?str:NULL;
698 }
699 return (node->left == 'b')?NULL:str;
700 case OP_BOL:
701 if(str == exp->_bol) return str;
702 return NULL;
703 case OP_EOL:
704 if(str == exp->_eol) return str;
705 return NULL;
706 case OP_DOT:{
707 *str++;
708 }
709 return str;
710 case OP_NCLASS:
711 case OP_CLASS:
712 if(trex_matchclass(exp,&exp->_nodes[node->left],*str)?(type == OP_CLASS?TRex_True:TRex_False):(type == OP_NCLASS?TRex_True:TRex_False)) {
713 *str++;
714 return str;
715 }
716 return NULL;
717 case OP_CCLASS:
718 if(trex_matchcclass(node->left,*str)) {
719 *str++;
720 return str;
721 }
722 return NULL;
723 default: /* char */
724 if(*str != node->type) return NULL;
725 *str++;
726 return str;
727 }
728 return NULL;
729 }
731 /* public api */
732 TRex *trex_compile(const TRexChar *pattern,const TRexChar **error)
733 {
734 TRex *exp = (TRex *)malloc(sizeof(TRex));
735 exp->_eol = exp->_bol = NULL;
736 exp->_p = pattern;
737 exp->_nallocated = (int)scstrlen(pattern) * sizeof(TRexChar);
738 exp->_nodes = (TRexNode *)malloc(exp->_nallocated * sizeof(TRexNode));
739 exp->_nsize = 0;
740 exp->_matches = 0;
741 exp->_nsubexpr = 0;
742 exp->_first = trex_newnode(exp,OP_EXPR);
743 exp->_error = error;
744 exp->_jmpbuf = malloc(sizeof(jmp_buf));
745 if(setjmp(*((jmp_buf*)exp->_jmpbuf)) == 0) {
746 int res = trex_list(exp);
747 exp->_nodes[exp->_first].left = res;
748 if(*exp->_p!='\0')
749 trex_error(exp,_SC("unexpected character"));
750 #ifdef _DEBUG
751 {
752 int nsize,i;
753 TRexNode *t;
754 nsize = exp->_nsize;
755 t = &exp->_nodes[0];
756 scprintf(_SC("\n"));
757 for(i = 0;i < nsize; i++) {
758 if(exp->_nodes[i].type>MAX_CHAR)
759 scprintf(_SC("[%02d] %10s "),i,g_nnames[exp->_nodes[i].type-MAX_CHAR]);
760 else
761 scprintf(_SC("[%02d] %10c "),i,exp->_nodes[i].type);
762 scprintf(_SC("left %02d right %02d next %02d\n"),exp->_nodes[i].left,exp->_nodes[i].right,exp->_nodes[i].next);
763 }
764 scprintf(_SC("\n"));
765 }
766 #endif
767 exp->_matches = (TRexMatch *) malloc(exp->_nsubexpr * sizeof(TRexMatch));
768 memset(exp->_matches,0,exp->_nsubexpr * sizeof(TRexMatch));
769 }
770 else{
771 trex_free(exp);
772 return NULL;
773 }
774 return exp;
775 }
777 void trex_free(TRex *exp)
778 {
779 if(exp) {
780 if(exp->_nodes) free(exp->_nodes);
781 if(exp->_jmpbuf) free(exp->_jmpbuf);
782 if(exp->_matches) free(exp->_matches);
783 free(exp);
784 }
785 }
787 TRexBool trex_match(TRex* exp,const TRexChar* text)
788 {
789 const TRexChar* res = NULL;
790 exp->_bol = text;
791 exp->_eol = text + scstrlen(text);
792 exp->_currsubexp = 0;
793 res = trex_matchnode(exp,exp->_nodes,text,NULL);
794 if(res == NULL || res != exp->_eol)
795 return TRex_False;
796 return TRex_True;
797 }
799 TRexBool trex_searchrange(TRex* exp,const TRexChar* text_begin,const TRexChar* text_end,const TRexChar** out_begin, const TRexChar** out_end)
800 {
801 const TRexChar *cur = NULL;
802 int node = exp->_first;
803 if(text_begin >= text_end) return TRex_False;
804 exp->_bol = text_begin;
805 exp->_eol = text_end;
806 do {
807 cur = text_begin;
808 while(node != -1) {
809 exp->_currsubexp = 0;
810 cur = trex_matchnode(exp,&exp->_nodes[node],cur,NULL);
811 if(!cur)
812 break;
813 node = exp->_nodes[node].next;
814 }
815 *text_begin++;
816 } while(cur == NULL && text_begin != text_end);
818 if(cur == NULL)
819 return TRex_False;
821 --text_begin;
823 if(out_begin) *out_begin = text_begin;
824 if(out_end) *out_end = cur;
825 return TRex_True;
826 }
828 TRexBool trex_search(TRex* exp,const TRexChar* text, const TRexChar** out_begin, const TRexChar** out_end)
829 {
830 return trex_searchrange(exp,text,text + scstrlen(text),out_begin,out_end);
831 }
833 int trex_getsubexpcount(TRex* exp)
834 {
835 return exp->_nsubexpr;
836 }
838 TRexBool trex_getsubexp(TRex* exp, int n, TRexMatch *subexp)
839 {
840 if( n<0 || n >= exp->_nsubexpr) return TRex_False;
841 *subexp = exp->_matches[n];
842 return TRex_True;
843 }
846 //########################################################################
847 //########################################################################
848 //## E N D R E G E X P
849 //########################################################################
850 //########################################################################
856 //########################################################################
857 //########################################################################
858 //## X M L
859 //########################################################################
860 //########################################################################
862 // Note: This mini-dom library comes from Pedro, another little project
863 // of mine.
865 typedef std::string String;
866 typedef unsigned int XMLCh;
869 class Namespace
870 {
871 public:
872 Namespace()
873 {}
875 Namespace(const String &prefixArg, const String &namespaceURIArg)
876 {
877 prefix = prefixArg;
878 namespaceURI = namespaceURIArg;
879 }
881 Namespace(const Namespace &other)
882 {
883 assign(other);
884 }
886 Namespace &operator=(const Namespace &other)
887 {
888 assign(other);
889 return *this;
890 }
892 virtual ~Namespace()
893 {}
895 virtual String getPrefix()
896 { return prefix; }
898 virtual String getNamespaceURI()
899 { return namespaceURI; }
901 protected:
903 void assign(const Namespace &other)
904 {
905 prefix = other.prefix;
906 namespaceURI = other.namespaceURI;
907 }
909 String prefix;
910 String namespaceURI;
912 };
914 class Attribute
915 {
916 public:
917 Attribute()
918 {}
920 Attribute(const String &nameArg, const String &valueArg)
921 {
922 name = nameArg;
923 value = valueArg;
924 }
926 Attribute(const Attribute &other)
927 {
928 assign(other);
929 }
931 Attribute &operator=(const Attribute &other)
932 {
933 assign(other);
934 return *this;
935 }
937 virtual ~Attribute()
938 {}
940 virtual String getName()
941 { return name; }
943 virtual String getValue()
944 { return value; }
946 protected:
948 void assign(const Attribute &other)
949 {
950 name = other.name;
951 value = other.value;
952 }
954 String name;
955 String value;
957 };
960 class Element
961 {
962 friend class Parser;
964 public:
965 Element()
966 {
967 init();
968 }
970 Element(const String &nameArg)
971 {
972 init();
973 name = nameArg;
974 }
976 Element(const String &nameArg, const String &valueArg)
977 {
978 init();
979 name = nameArg;
980 value = valueArg;
981 }
983 Element(const Element &other)
984 {
985 assign(other);
986 }
988 Element &operator=(const Element &other)
989 {
990 assign(other);
991 return *this;
992 }
994 virtual Element *clone();
996 virtual ~Element()
997 {
998 for (unsigned int i=0 ; i<children.size() ; i++)
999 delete children[i];
1000 }
1002 virtual String getName()
1003 { return name; }
1005 virtual String getValue()
1006 { return value; }
1008 Element *getParent()
1009 { return parent; }
1011 std::vector<Element *> getChildren()
1012 { return children; }
1014 std::vector<Element *> findElements(const String &name);
1016 String getAttribute(const String &name);
1018 std::vector<Attribute> &getAttributes()
1019 { return attributes; }
1021 String getTagAttribute(const String &tagName, const String &attrName);
1023 String getTagValue(const String &tagName);
1025 void addChild(Element *child);
1027 void addAttribute(const String &name, const String &value);
1029 void addNamespace(const String &prefix, const String &namespaceURI);
1032 /**
1033 * Prettyprint an XML tree to an output stream. Elements are indented
1034 * according to element hierarchy.
1035 * @param f a stream to receive the output
1036 * @param elem the element to output
1037 */
1038 void writeIndented(FILE *f);
1040 /**
1041 * Prettyprint an XML tree to standard output. This is the equivalent of
1042 * writeIndented(stdout).
1043 * @param elem the element to output
1044 */
1045 void print();
1047 int getLine()
1048 { return line; }
1050 protected:
1052 void init()
1053 {
1054 parent = NULL;
1055 line = 0;
1056 }
1058 void assign(const Element &other)
1059 {
1060 parent = other.parent;
1061 children = other.children;
1062 attributes = other.attributes;
1063 namespaces = other.namespaces;
1064 name = other.name;
1065 value = other.value;
1066 line = other.line;
1067 }
1069 void findElementsRecursive(std::vector<Element *>&res, const String &name);
1071 void writeIndentedRecursive(FILE *f, int indent);
1073 Element *parent;
1075 std::vector<Element *>children;
1077 std::vector<Attribute> attributes;
1078 std::vector<Namespace> namespaces;
1080 String name;
1081 String value;
1083 int line;
1084 };
1090 class Parser
1091 {
1092 public:
1093 /**
1094 * Constructor
1095 */
1096 Parser()
1097 { init(); }
1099 virtual ~Parser()
1100 {}
1102 /**
1103 * Parse XML in a char buffer.
1104 * @param buf a character buffer to parse
1105 * @param pos position to start parsing
1106 * @param len number of chars, from pos, to parse.
1107 * @return a pointer to the root of the XML document;
1108 */
1109 Element *parse(const char *buf,int pos,int len);
1111 /**
1112 * Parse XML in a char buffer.
1113 * @param buf a character buffer to parse
1114 * @param pos position to start parsing
1115 * @param len number of chars, from pos, to parse.
1116 * @return a pointer to the root of the XML document;
1117 */
1118 Element *parse(const String &buf);
1120 /**
1121 * Parse a named XML file. The file is loaded like a data file;
1122 * the original format is not preserved.
1123 * @param fileName the name of the file to read
1124 * @return a pointer to the root of the XML document;
1125 */
1126 Element *parseFile(const String &fileName);
1128 /**
1129 * Utility method to preprocess a string for XML
1130 * output, escaping its entities.
1131 * @param str the string to encode
1132 */
1133 static String encode(const String &str);
1135 /**
1136 * Removes whitespace from beginning and end of a string
1137 */
1138 String trim(const String &s);
1140 private:
1142 void init()
1143 {
1144 keepGoing = true;
1145 currentNode = NULL;
1146 parselen = 0;
1147 parsebuf = NULL;
1148 currentPosition = 0;
1149 }
1151 int countLines(int begin, int end);
1153 void getLineAndColumn(int pos, int *lineNr, int *colNr);
1155 void error(const char *fmt, ...);
1157 int peek(int pos);
1159 int match(int pos, const char *text);
1161 int skipwhite(int p);
1163 int getWord(int p0, String &buf);
1165 int getQuoted(int p0, String &buf, int do_i_parse);
1167 int parseVersion(int p0);
1169 int parseDoctype(int p0);
1171 int parseElement(int p0, Element *par,int depth);
1173 Element *parse(XMLCh *buf,int pos,int len);
1175 bool keepGoing;
1176 Element *currentNode;
1177 int parselen;
1178 XMLCh *parsebuf;
1179 String cdatabuf;
1180 int currentPosition;
1181 };
1186 //########################################################################
1187 //# E L E M E N T
1188 //########################################################################
1190 Element *Element::clone()
1191 {
1192 Element *elem = new Element(name, value);
1193 elem->parent = parent;
1194 elem->attributes = attributes;
1195 elem->namespaces = namespaces;
1196 elem->line = line;
1198 std::vector<Element *>::iterator iter;
1199 for (iter = children.begin(); iter != children.end() ; iter++)
1200 {
1201 elem->addChild((*iter)->clone());
1202 }
1203 return elem;
1204 }
1207 void Element::findElementsRecursive(std::vector<Element *>&res, const String &name)
1208 {
1209 if (getName() == name)
1210 {
1211 res.push_back(this);
1212 }
1213 for (unsigned int i=0; i<children.size() ; i++)
1214 children[i]->findElementsRecursive(res, name);
1215 }
1217 std::vector<Element *> Element::findElements(const String &name)
1218 {
1219 std::vector<Element *> res;
1220 findElementsRecursive(res, name);
1221 return res;
1222 }
1224 String Element::getAttribute(const String &name)
1225 {
1226 for (unsigned int i=0 ; i<attributes.size() ; i++)
1227 if (attributes[i].getName() ==name)
1228 return attributes[i].getValue();
1229 return "";
1230 }
1232 String Element::getTagAttribute(const String &tagName, const String &attrName)
1233 {
1234 std::vector<Element *>elems = findElements(tagName);
1235 if (elems.size() <1)
1236 return "";
1237 String res = elems[0]->getAttribute(attrName);
1238 return res;
1239 }
1241 String Element::getTagValue(const String &tagName)
1242 {
1243 std::vector<Element *>elems = findElements(tagName);
1244 if (elems.size() <1)
1245 return "";
1246 String res = elems[0]->getValue();
1247 return res;
1248 }
1250 void Element::addChild(Element *child)
1251 {
1252 if (!child)
1253 return;
1254 child->parent = this;
1255 children.push_back(child);
1256 }
1259 void Element::addAttribute(const String &name, const String &value)
1260 {
1261 Attribute attr(name, value);
1262 attributes.push_back(attr);
1263 }
1265 void Element::addNamespace(const String &prefix, const String &namespaceURI)
1266 {
1267 Namespace ns(prefix, namespaceURI);
1268 namespaces.push_back(ns);
1269 }
1271 void Element::writeIndentedRecursive(FILE *f, int indent)
1272 {
1273 int i;
1274 if (!f)
1275 return;
1276 //Opening tag, and attributes
1277 for (i=0;i<indent;i++)
1278 fputc(' ',f);
1279 fprintf(f,"<%s",name.c_str());
1280 for (unsigned int i=0 ; i<attributes.size() ; i++)
1281 {
1282 fprintf(f," %s=\"%s\"",
1283 attributes[i].getName().c_str(),
1284 attributes[i].getValue().c_str());
1285 }
1286 for (unsigned int i=0 ; i<namespaces.size() ; i++)
1287 {
1288 fprintf(f," xmlns:%s=\"%s\"",
1289 namespaces[i].getPrefix().c_str(),
1290 namespaces[i].getNamespaceURI().c_str());
1291 }
1292 fprintf(f,">\n");
1294 //Between the tags
1295 if (value.size() > 0)
1296 {
1297 for (int i=0;i<indent;i++)
1298 fputc(' ', f);
1299 fprintf(f," %s\n", value.c_str());
1300 }
1302 for (unsigned int i=0 ; i<children.size() ; i++)
1303 children[i]->writeIndentedRecursive(f, indent+2);
1305 //Closing tag
1306 for (int i=0; i<indent; i++)
1307 fputc(' ',f);
1308 fprintf(f,"</%s>\n", name.c_str());
1309 }
1311 void Element::writeIndented(FILE *f)
1312 {
1313 writeIndentedRecursive(f, 0);
1314 }
1316 void Element::print()
1317 {
1318 writeIndented(stdout);
1319 }
1322 //########################################################################
1323 //# P A R S E R
1324 //########################################################################
1328 typedef struct
1329 {
1330 const char *escaped;
1331 char value;
1332 } EntityEntry;
1334 static EntityEntry entities[] =
1335 {
1336 { "&" , '&' },
1337 { "<" , '<' },
1338 { ">" , '>' },
1339 { "'", '\'' },
1340 { """, '"' },
1341 { NULL , '\0' }
1342 };
1346 /**
1347 * Removes whitespace from beginning and end of a string
1348 */
1349 String Parser::trim(const String &s)
1350 {
1351 if (s.size() < 1)
1352 return s;
1354 //Find first non-ws char
1355 unsigned int begin = 0;
1356 for ( ; begin < s.size() ; begin++)
1357 {
1358 if (!isspace(s[begin]))
1359 break;
1360 }
1362 //Find first non-ws char, going in reverse
1363 unsigned int end = s.size() - 1;
1364 for ( ; end > begin ; end--)
1365 {
1366 if (!isspace(s[end]))
1367 break;
1368 }
1369 //trace("begin:%d end:%d", begin, end);
1371 String res = s.substr(begin, end-begin+1);
1372 return res;
1373 }
1376 int Parser::countLines(int begin, int end)
1377 {
1378 int count = 0;
1379 for (int i=begin ; i<end ; i++)
1380 {
1381 XMLCh ch = parsebuf[i];
1382 if (ch == '\n' || ch == '\r')
1383 count++;
1384 }
1385 return count;
1386 }
1389 void Parser::getLineAndColumn(int pos, int *lineNr, int *colNr)
1390 {
1391 int line = 1;
1392 int col = 1;
1393 for (long i=0 ; i<pos ; i++)
1394 {
1395 XMLCh ch = parsebuf[i];
1396 if (ch == '\n' || ch == '\r')
1397 {
1398 col = 0;
1399 line ++;
1400 }
1401 else
1402 col++;
1403 }
1404 *lineNr = line;
1405 *colNr = col;
1407 }
1410 void Parser::error(const char *fmt, ...)
1411 {
1412 int lineNr;
1413 int colNr;
1414 getLineAndColumn(currentPosition, &lineNr, &colNr);
1415 va_list args;
1416 fprintf(stderr, "xml error at line %d, column %d:", lineNr, colNr);
1417 va_start(args,fmt);
1418 vfprintf(stderr,fmt,args);
1419 va_end(args) ;
1420 fprintf(stderr, "\n");
1421 }
1425 int Parser::peek(int pos)
1426 {
1427 if (pos >= parselen)
1428 return -1;
1429 currentPosition = pos;
1430 int ch = parsebuf[pos];
1431 //printf("ch:%c\n", ch);
1432 return ch;
1433 }
1437 String Parser::encode(const String &str)
1438 {
1439 String ret;
1440 for (unsigned int i=0 ; i<str.size() ; i++)
1441 {
1442 XMLCh ch = (XMLCh)str[i];
1443 if (ch == '&')
1444 ret.append("&");
1445 else if (ch == '<')
1446 ret.append("<");
1447 else if (ch == '>')
1448 ret.append(">");
1449 else if (ch == '\'')
1450 ret.append("'");
1451 else if (ch == '"')
1452 ret.append(""");
1453 else
1454 ret.push_back(ch);
1456 }
1457 return ret;
1458 }
1461 int Parser::match(int p0, const char *text)
1462 {
1463 int p = p0;
1464 while (*text)
1465 {
1466 if (peek(p) != *text)
1467 return p0;
1468 p++; text++;
1469 }
1470 return p;
1471 }
1475 int Parser::skipwhite(int p)
1476 {
1478 while (p<parselen)
1479 {
1480 int p2 = match(p, "<!--");
1481 if (p2 > p)
1482 {
1483 p = p2;
1484 while (p<parselen)
1485 {
1486 p2 = match(p, "-->");
1487 if (p2 > p)
1488 {
1489 p = p2;
1490 break;
1491 }
1492 p++;
1493 }
1494 }
1495 XMLCh b = peek(p);
1496 if (!isspace(b))
1497 break;
1498 p++;
1499 }
1500 return p;
1501 }
1503 /* modify this to allow all chars for an element or attribute name*/
1504 int Parser::getWord(int p0, String &buf)
1505 {
1506 int p = p0;
1507 while (p<parselen)
1508 {
1509 XMLCh b = peek(p);
1510 if (b<=' ' || b=='/' || b=='>' || b=='=')
1511 break;
1512 buf.push_back(b);
1513 p++;
1514 }
1515 return p;
1516 }
1518 int Parser::getQuoted(int p0, String &buf, int do_i_parse)
1519 {
1521 int p = p0;
1522 if (peek(p) != '"' && peek(p) != '\'')
1523 return p0;
1524 p++;
1526 while ( p<parselen )
1527 {
1528 XMLCh b = peek(p);
1529 if (b=='"' || b=='\'')
1530 break;
1531 if (b=='&' && do_i_parse)
1532 {
1533 bool found = false;
1534 for (EntityEntry *ee = entities ; ee->value ; ee++)
1535 {
1536 int p2 = match(p, ee->escaped);
1537 if (p2>p)
1538 {
1539 buf.push_back(ee->value);
1540 p = p2;
1541 found = true;
1542 break;
1543 }
1544 }
1545 if (!found)
1546 {
1547 error("unterminated entity");
1548 return false;
1549 }
1550 }
1551 else
1552 {
1553 buf.push_back(b);
1554 p++;
1555 }
1556 }
1557 return p;
1558 }
1560 int Parser::parseVersion(int p0)
1561 {
1562 //printf("### parseVersion: %d\n", p0);
1564 int p = p0;
1566 p = skipwhite(p0);
1568 if (peek(p) != '<')
1569 return p0;
1571 p++;
1572 if (p>=parselen || peek(p)!='?')
1573 return p0;
1575 p++;
1577 String buf;
1579 while (p<parselen)
1580 {
1581 XMLCh ch = peek(p);
1582 if (ch=='?')
1583 {
1584 p++;
1585 break;
1586 }
1587 buf.push_back(ch);
1588 p++;
1589 }
1591 if (peek(p) != '>')
1592 return p0;
1593 p++;
1595 //printf("Got version:%s\n",buf.c_str());
1596 return p;
1597 }
1599 int Parser::parseDoctype(int p0)
1600 {
1601 //printf("### parseDoctype: %d\n", p0);
1603 int p = p0;
1604 p = skipwhite(p);
1606 if (p>=parselen || peek(p)!='<')
1607 return p0;
1609 p++;
1611 if (peek(p)!='!' || peek(p+1)=='-')
1612 return p0;
1613 p++;
1615 String buf;
1616 while (p<parselen)
1617 {
1618 XMLCh ch = peek(p);
1619 if (ch=='>')
1620 {
1621 p++;
1622 break;
1623 }
1624 buf.push_back(ch);
1625 p++;
1626 }
1628 //printf("Got doctype:%s\n",buf.c_str());
1629 return p;
1630 }
1634 int Parser::parseElement(int p0, Element *par,int lineNr)
1635 {
1637 int p = p0;
1639 int p2 = p;
1641 p = skipwhite(p);
1643 //## Get open tag
1644 XMLCh ch = peek(p);
1645 if (ch!='<')
1646 return p0;
1648 //int line, col;
1649 //getLineAndColumn(p, &line, &col);
1651 p++;
1653 String openTagName;
1654 p = skipwhite(p);
1655 p = getWord(p, openTagName);
1656 //printf("####tag :%s\n", openTagName.c_str());
1657 p = skipwhite(p);
1659 //Add element to tree
1660 Element *n = new Element(openTagName);
1661 n->line = lineNr + countLines(p0, p);
1662 n->parent = par;
1663 par->addChild(n);
1665 // Get attributes
1666 if (peek(p) != '>')
1667 {
1668 while (p<parselen)
1669 {
1670 p = skipwhite(p);
1671 ch = peek(p);
1672 //printf("ch:%c\n",ch);
1673 if (ch=='>')
1674 break;
1675 else if (ch=='/' && p<parselen+1)
1676 {
1677 p++;
1678 p = skipwhite(p);
1679 ch = peek(p);
1680 if (ch=='>')
1681 {
1682 p++;
1683 //printf("quick close\n");
1684 return p;
1685 }
1686 }
1687 String attrName;
1688 p2 = getWord(p, attrName);
1689 if (p2==p)
1690 break;
1691 //printf("name:%s",buf);
1692 p=p2;
1693 p = skipwhite(p);
1694 ch = peek(p);
1695 //printf("ch:%c\n",ch);
1696 if (ch!='=')
1697 break;
1698 p++;
1699 p = skipwhite(p);
1700 // ch = parsebuf[p];
1701 // printf("ch:%c\n",ch);
1702 String attrVal;
1703 p2 = getQuoted(p, attrVal, true);
1704 p=p2+1;
1705 //printf("name:'%s' value:'%s'\n",attrName.c_str(),attrVal.c_str());
1706 char *namestr = (char *)attrName.c_str();
1707 if (strncmp(namestr, "xmlns:", 6)==0)
1708 n->addNamespace(attrName, attrVal);
1709 else
1710 n->addAttribute(attrName, attrVal);
1711 }
1712 }
1714 bool cdata = false;
1716 p++;
1717 // ### Get intervening data ### */
1718 String data;
1719 while (p<parselen)
1720 {
1721 //# COMMENT
1722 p2 = match(p, "<!--");
1723 if (!cdata && p2>p)
1724 {
1725 p = p2;
1726 while (p<parselen)
1727 {
1728 p2 = match(p, "-->");
1729 if (p2 > p)
1730 {
1731 p = p2;
1732 break;
1733 }
1734 p++;
1735 }
1736 }
1738 ch = peek(p);
1739 //# END TAG
1740 if (ch=='<' && !cdata && peek(p+1)=='/')
1741 {
1742 break;
1743 }
1744 //# CDATA
1745 p2 = match(p, "<![CDATA[");
1746 if (p2 > p)
1747 {
1748 cdata = true;
1749 p = p2;
1750 continue;
1751 }
1753 //# CHILD ELEMENT
1754 if (ch == '<')
1755 {
1756 p2 = parseElement(p, n, lineNr + countLines(p0, p));
1757 if (p2 == p)
1758 {
1759 /*
1760 printf("problem on element:%s. p2:%d p:%d\n",
1761 openTagName.c_str(), p2, p);
1762 */
1763 return p0;
1764 }
1765 p = p2;
1766 continue;
1767 }
1768 //# ENTITY
1769 if (ch=='&' && !cdata)
1770 {
1771 bool found = false;
1772 for (EntityEntry *ee = entities ; ee->value ; ee++)
1773 {
1774 int p2 = match(p, ee->escaped);
1775 if (p2>p)
1776 {
1777 data.push_back(ee->value);
1778 p = p2;
1779 found = true;
1780 break;
1781 }
1782 }
1783 if (!found)
1784 {
1785 error("unterminated entity");
1786 return -1;
1787 }
1788 continue;
1789 }
1791 //# NONE OF THE ABOVE
1792 data.push_back(ch);
1793 p++;
1794 }/*while*/
1797 n->value = data;
1798 //printf("%d : data:%s\n",p,data.c_str());
1800 //## Get close tag
1801 p = skipwhite(p);
1802 ch = peek(p);
1803 if (ch != '<')
1804 {
1805 error("no < for end tag\n");
1806 return p0;
1807 }
1808 p++;
1809 ch = peek(p);
1810 if (ch != '/')
1811 {
1812 error("no / on end tag");
1813 return p0;
1814 }
1815 p++;
1816 ch = peek(p);
1817 p = skipwhite(p);
1818 String closeTagName;
1819 p = getWord(p, closeTagName);
1820 if (openTagName != closeTagName)
1821 {
1822 error("Mismatched closing tag. Expected </%S>. Got '%S'.",
1823 openTagName.c_str(), closeTagName.c_str());
1824 return p0;
1825 }
1826 p = skipwhite(p);
1827 if (peek(p) != '>')
1828 {
1829 error("no > on end tag for '%s'", closeTagName.c_str());
1830 return p0;
1831 }
1832 p++;
1833 // printf("close element:%s\n",closeTagName.c_str());
1834 p = skipwhite(p);
1835 return p;
1836 }
1841 Element *Parser::parse(XMLCh *buf,int pos,int len)
1842 {
1843 parselen = len;
1844 parsebuf = buf;
1845 Element *rootNode = new Element("root");
1846 pos = parseVersion(pos);
1847 pos = parseDoctype(pos);
1848 pos = parseElement(pos, rootNode, 1);
1849 return rootNode;
1850 }
1853 Element *Parser::parse(const char *buf, int pos, int len)
1854 {
1855 XMLCh *charbuf = new XMLCh[len + 1];
1856 long i = 0;
1857 for ( ; i < len ; i++)
1858 charbuf[i] = (XMLCh)buf[i];
1859 charbuf[i] = '\0';
1861 Element *n = parse(charbuf, pos, len);
1862 delete[] charbuf;
1863 return n;
1864 }
1866 Element *Parser::parse(const String &buf)
1867 {
1868 long len = (long)buf.size();
1869 XMLCh *charbuf = new XMLCh[len + 1];
1870 long i = 0;
1871 for ( ; i < len ; i++)
1872 charbuf[i] = (XMLCh)buf[i];
1873 charbuf[i] = '\0';
1875 Element *n = parse(charbuf, 0, len);
1876 delete[] charbuf;
1877 return n;
1878 }
1880 Element *Parser::parseFile(const String &fileName)
1881 {
1883 //##### LOAD INTO A CHAR BUF, THEN CONVERT TO XMLCh
1884 FILE *f = fopen(fileName.c_str(), "rb");
1885 if (!f)
1886 return NULL;
1888 struct stat statBuf;
1889 if (fstat(fileno(f),&statBuf)<0)
1890 {
1891 fclose(f);
1892 return NULL;
1893 }
1894 long filelen = statBuf.st_size;
1896 //printf("length:%d\n",filelen);
1897 XMLCh *charbuf = new XMLCh[filelen + 1];
1898 for (XMLCh *p=charbuf ; !feof(f) ; p++)
1899 {
1900 *p = (XMLCh)fgetc(f);
1901 }
1902 fclose(f);
1903 charbuf[filelen] = '\0';
1906 /*
1907 printf("nrbytes:%d\n",wc_count);
1908 printf("buf:%ls\n======\n",charbuf);
1909 */
1910 Element *n = parse(charbuf, 0, filelen);
1911 delete[] charbuf;
1912 return n;
1913 }
1915 //########################################################################
1916 //########################################################################
1917 //## E N D X M L
1918 //########################################################################
1919 //########################################################################
1926 //########################################################################
1927 //########################################################################
1928 //## U R I
1929 //########################################################################
1930 //########################################################################
1932 //This would normally be a call to a UNICODE function
1933 #define isLetter(x) isalpha(x)
1935 /**
1936 * A class that implements the W3C URI resource reference.
1937 */
1938 class URI
1939 {
1940 public:
1942 typedef enum
1943 {
1944 SCHEME_NONE =0,
1945 SCHEME_DATA,
1946 SCHEME_HTTP,
1947 SCHEME_HTTPS,
1948 SCHEME_FTP,
1949 SCHEME_FILE,
1950 SCHEME_LDAP,
1951 SCHEME_MAILTO,
1952 SCHEME_NEWS,
1953 SCHEME_TELNET
1954 } SchemeTypes;
1956 /**
1957 *
1958 */
1959 URI()
1960 {
1961 init();
1962 }
1964 /**
1965 *
1966 */
1967 URI(const String &str)
1968 {
1969 init();
1970 parse(str);
1971 }
1974 /**
1975 *
1976 */
1977 URI(const char *str)
1978 {
1979 init();
1980 String domStr = str;
1981 parse(domStr);
1982 }
1985 /**
1986 *
1987 */
1988 URI(const URI &other)
1989 {
1990 init();
1991 assign(other);
1992 }
1995 /**
1996 *
1997 */
1998 URI &operator=(const URI &other)
1999 {
2000 init();
2001 assign(other);
2002 return *this;
2003 }
2006 /**
2007 *
2008 */
2009 virtual ~URI()
2010 {}
2014 /**
2015 *
2016 */
2017 virtual bool parse(const String &str);
2019 /**
2020 *
2021 */
2022 virtual String toString() const;
2024 /**
2025 *
2026 */
2027 virtual int getScheme() const;
2029 /**
2030 *
2031 */
2032 virtual String getSchemeStr() const;
2034 /**
2035 *
2036 */
2037 virtual String getAuthority() const;
2039 /**
2040 * Same as getAuthority, but if the port has been specified
2041 * as host:port , the port will not be included
2042 */
2043 virtual String getHost() const;
2045 /**
2046 *
2047 */
2048 virtual int getPort() const;
2050 /**
2051 *
2052 */
2053 virtual String getPath() const;
2055 /**
2056 *
2057 */
2058 virtual String getNativePath() const;
2060 /**
2061 *
2062 */
2063 virtual bool isAbsolute() const;
2065 /**
2066 *
2067 */
2068 virtual bool isOpaque() const;
2070 /**
2071 *
2072 */
2073 virtual String getQuery() const;
2075 /**
2076 *
2077 */
2078 virtual String getFragment() const;
2080 /**
2081 *
2082 */
2083 virtual URI resolve(const URI &other) const;
2085 /**
2086 *
2087 */
2088 virtual void normalize();
2090 private:
2092 /**
2093 *
2094 */
2095 void init()
2096 {
2097 parsebuf = NULL;
2098 parselen = 0;
2099 scheme = SCHEME_NONE;
2100 schemeStr = "";
2101 port = 0;
2102 authority = "";
2103 path = "";
2104 absolute = false;
2105 opaque = false;
2106 query = "";
2107 fragment = "";
2108 }
2111 /**
2112 *
2113 */
2114 void assign(const URI &other)
2115 {
2116 scheme = other.scheme;
2117 schemeStr = other.schemeStr;
2118 authority = other.authority;
2119 port = other.port;
2120 path = other.path;
2121 absolute = other.absolute;
2122 opaque = other.opaque;
2123 query = other.query;
2124 fragment = other.fragment;
2125 }
2127 int scheme;
2129 String schemeStr;
2131 String authority;
2133 bool portSpecified;
2135 int port;
2137 String path;
2139 bool absolute;
2141 bool opaque;
2143 String query;
2145 String fragment;
2147 void error(const char *fmt, ...);
2149 void trace(const char *fmt, ...);
2152 int peek(int p);
2154 int match(int p, const char *key);
2156 int parseScheme(int p);
2158 int parseHierarchicalPart(int p0);
2160 int parseQuery(int p0);
2162 int parseFragment(int p0);
2164 int parse(int p);
2166 char *parsebuf;
2168 int parselen;
2170 };
2174 typedef struct
2175 {
2176 int ival;
2177 const char *sval;
2178 int port;
2179 } LookupEntry;
2181 LookupEntry schemes[] =
2182 {
2183 { URI::SCHEME_DATA, "data:", 0 },
2184 { URI::SCHEME_HTTP, "http:", 80 },
2185 { URI::SCHEME_HTTPS, "https:", 443 },
2186 { URI::SCHEME_FTP, "ftp", 12 },
2187 { URI::SCHEME_FILE, "file:", 0 },
2188 { URI::SCHEME_LDAP, "ldap:", 123 },
2189 { URI::SCHEME_MAILTO, "mailto:", 25 },
2190 { URI::SCHEME_NEWS, "news:", 117 },
2191 { URI::SCHEME_TELNET, "telnet:", 23 },
2192 { 0, NULL, 0 }
2193 };
2196 String URI::toString() const
2197 {
2198 String str = schemeStr;
2199 if (authority.size() > 0)
2200 {
2201 str.append("//");
2202 str.append(authority);
2203 }
2204 str.append(path);
2205 if (query.size() > 0)
2206 {
2207 str.append("?");
2208 str.append(query);
2209 }
2210 if (fragment.size() > 0)
2211 {
2212 str.append("#");
2213 str.append(fragment);
2214 }
2215 return str;
2216 }
2219 int URI::getScheme() const
2220 {
2221 return scheme;
2222 }
2224 String URI::getSchemeStr() const
2225 {
2226 return schemeStr;
2227 }
2230 String URI::getAuthority() const
2231 {
2232 String ret = authority;
2233 if (portSpecified && port>=0)
2234 {
2235 char buf[7];
2236 snprintf(buf, 6, ":%6d", port);
2237 ret.append(buf);
2238 }
2239 return ret;
2240 }
2242 String URI::getHost() const
2243 {
2244 return authority;
2245 }
2247 int URI::getPort() const
2248 {
2249 return port;
2250 }
2253 String URI::getPath() const
2254 {
2255 return path;
2256 }
2258 String URI::getNativePath() const
2259 {
2260 String npath;
2261 #ifdef __WIN32__
2262 unsigned int firstChar = 0;
2263 if (path.size() >= 3)
2264 {
2265 if (path[0] == '/' &&
2266 isLetter(path[1]) &&
2267 path[2] == ':')
2268 firstChar++;
2269 }
2270 for (unsigned int i=firstChar ; i<path.size() ; i++)
2271 {
2272 XMLCh ch = (XMLCh) path[i];
2273 if (ch == '/')
2274 npath.push_back((XMLCh)'\\');
2275 else
2276 npath.push_back(ch);
2277 }
2278 #else
2279 npath = path;
2280 #endif
2281 return npath;
2282 }
2285 bool URI::isAbsolute() const
2286 {
2287 return absolute;
2288 }
2290 bool URI::isOpaque() const
2291 {
2292 return opaque;
2293 }
2296 String URI::getQuery() const
2297 {
2298 return query;
2299 }
2302 String URI::getFragment() const
2303 {
2304 return fragment;
2305 }
2308 URI URI::resolve(const URI &other) const
2309 {
2310 //### According to w3c, this is handled in 3 cases
2312 //## 1
2313 if (opaque || other.isAbsolute())
2314 return other;
2316 //## 2
2317 if (other.fragment.size() > 0 &&
2318 other.path.size() == 0 &&
2319 other.scheme == SCHEME_NONE &&
2320 other.authority.size() == 0 &&
2321 other.query.size() == 0 )
2322 {
2323 URI fragUri = *this;
2324 fragUri.fragment = other.fragment;
2325 return fragUri;
2326 }
2328 //## 3 http://www.ietf.org/rfc/rfc2396.txt, section 5.2
2329 URI newUri;
2330 //# 3.1
2331 newUri.scheme = scheme;
2332 newUri.schemeStr = schemeStr;
2333 newUri.query = other.query;
2334 newUri.fragment = other.fragment;
2335 if (other.authority.size() > 0)
2336 {
2337 //# 3.2
2338 if (absolute || other.absolute)
2339 newUri.absolute = true;
2340 newUri.authority = other.authority;
2341 newUri.port = other.port;//part of authority
2342 newUri.path = other.path;
2343 }
2344 else
2345 {
2346 //# 3.3
2347 if (other.absolute)
2348 {
2349 newUri.absolute = true;
2350 newUri.path = other.path;
2351 }
2352 else
2353 {
2354 unsigned int pos = path.find_last_of('/');
2355 if (pos != path.npos)
2356 {
2357 String tpath = path.substr(0, pos+1);
2358 tpath.append(other.path);
2359 newUri.path = tpath;
2360 }
2361 else
2362 newUri.path = other.path;
2363 }
2364 }
2366 newUri.normalize();
2367 return newUri;
2368 }
2372 /**
2373 * This follows the Java URI algorithm:
2374 * 1. All "." segments are removed.
2375 * 2. If a ".." segment is preceded by a non-".." segment
2376 * then both of these segments are removed. This step
2377 * is repeated until it is no longer applicable.
2378 * 3. If the path is relative, and if its first segment
2379 * contains a colon character (':'), then a "." segment
2380 * is prepended. This prevents a relative URI with a path
2381 * such as "a:b/c/d" from later being re-parsed as an
2382 * opaque URI with a scheme of "a" and a scheme-specific
2383 * part of "b/c/d". (Deviation from RFC 2396)
2384 */
2385 void URI::normalize()
2386 {
2387 std::vector<String> segments;
2389 //## Collect segments
2390 if (path.size()<2)
2391 return;
2392 bool abs = false;
2393 unsigned int pos=0;
2394 if (path[0]=='/')
2395 {
2396 abs = true;
2397 pos++;
2398 }
2399 while (pos < path.size())
2400 {
2401 unsigned int pos2 = path.find('/', pos);
2402 if (pos2==path.npos)
2403 {
2404 String seg = path.substr(pos);
2405 //printf("last segment:%s\n", seg.c_str());
2406 segments.push_back(seg);
2407 break;
2408 }
2409 if (pos2>pos)
2410 {
2411 String seg = path.substr(pos, pos2-pos);
2412 //printf("segment:%s\n", seg.c_str());
2413 segments.push_back(seg);
2414 }
2415 pos = pos2;
2416 pos++;
2417 }
2419 //## Clean up (normalize) segments
2420 bool edited = false;
2421 std::vector<String>::iterator iter;
2422 for (iter=segments.begin() ; iter!=segments.end() ; )
2423 {
2424 String s = *iter;
2425 if (s == ".")
2426 {
2427 iter = segments.erase(iter);
2428 edited = true;
2429 }
2430 else if (s == ".." &&
2431 iter != segments.begin() &&
2432 *(iter-1) != "..")
2433 {
2434 iter--; //back up, then erase two entries
2435 iter = segments.erase(iter);
2436 iter = segments.erase(iter);
2437 edited = true;
2438 }
2439 else
2440 iter++;
2441 }
2443 //## Rebuild path, if necessary
2444 if (edited)
2445 {
2446 path.clear();
2447 if (abs)
2448 {
2449 path.append("/");
2450 }
2451 std::vector<String>::iterator iter;
2452 for (iter=segments.begin() ; iter!=segments.end() ; iter++)
2453 {
2454 if (iter != segments.begin())
2455 path.append("/");
2456 path.append(*iter);
2457 }
2458 }
2460 }
2464 //#########################################################################
2465 //# M E S S A G E S
2466 //#########################################################################
2468 void URI::error(const char *fmt, ...)
2469 {
2470 va_list args;
2471 fprintf(stderr, "URI error: ");
2472 va_start(args, fmt);
2473 vfprintf(stderr, fmt, args);
2474 va_end(args);
2475 fprintf(stderr, "\n");
2476 }
2478 void URI::trace(const char *fmt, ...)
2479 {
2480 va_list args;
2481 fprintf(stdout, "URI: ");
2482 va_start(args, fmt);
2483 vfprintf(stdout, fmt, args);
2484 va_end(args);
2485 fprintf(stdout, "\n");
2486 }
2491 //#########################################################################
2492 //# P A R S I N G
2493 //#########################################################################
2497 int URI::peek(int p)
2498 {
2499 if (p<0 || p>=parselen)
2500 return -1;
2501 return parsebuf[p];
2502 }
2506 int URI::match(int p0, const char *key)
2507 {
2508 int p = p0;
2509 while (p < parselen)
2510 {
2511 if (*key == '\0')
2512 return p;
2513 else if (*key != parsebuf[p])
2514 break;
2515 p++; key++;
2516 }
2517 return p0;
2518 }
2520 //#########################################################################
2521 //# Parsing is performed according to:
2522 //# http://www.gbiv.com/protocols/uri/rfc/rfc3986.html#components
2523 //#########################################################################
2525 int URI::parseScheme(int p0)
2526 {
2527 int p = p0;
2528 for (LookupEntry *entry = schemes; entry->sval ; entry++)
2529 {
2530 int p2 = match(p, entry->sval);
2531 if (p2 > p)
2532 {
2533 schemeStr = entry->sval;
2534 scheme = entry->ival;
2535 port = entry->port;
2536 p = p2;
2537 return p;
2538 }
2539 }
2541 return p;
2542 }
2545 int URI::parseHierarchicalPart(int p0)
2546 {
2547 int p = p0;
2548 int ch;
2550 //# Authority field (host and port, for example)
2551 int p2 = match(p, "//");
2552 if (p2 > p)
2553 {
2554 p = p2;
2555 portSpecified = false;
2556 String portStr;
2557 while (p < parselen)
2558 {
2559 ch = peek(p);
2560 if (ch == '/')
2561 break;
2562 else if (ch == ':')
2563 portSpecified = true;
2564 else if (portSpecified)
2565 portStr.push_back((XMLCh)ch);
2566 else
2567 authority.push_back((XMLCh)ch);
2568 p++;
2569 }
2570 if (portStr.size() > 0)
2571 {
2572 char *pstr = (char *)portStr.c_str();
2573 char *endStr;
2574 long val = strtol(pstr, &endStr, 10);
2575 if (endStr > pstr) //successful parse?
2576 port = val;
2577 }
2578 }
2580 //# Are we absolute?
2581 ch = peek(p);
2582 if (isLetter(ch) && peek(p+1)==':')
2583 {
2584 absolute = true;
2585 path.push_back((XMLCh)'/');
2586 }
2587 else if (ch == '/')
2588 {
2589 absolute = true;
2590 if (p>p0) //in other words, if '/' is not the first char
2591 opaque = true;
2592 path.push_back((XMLCh)ch);
2593 p++;
2594 }
2596 while (p < parselen)
2597 {
2598 ch = peek(p);
2599 if (ch == '?' || ch == '#')
2600 break;
2601 path.push_back((XMLCh)ch);
2602 p++;
2603 }
2605 return p;
2606 }
2608 int URI::parseQuery(int p0)
2609 {
2610 int p = p0;
2611 int ch = peek(p);
2612 if (ch != '?')
2613 return p0;
2615 p++;
2616 while (p < parselen)
2617 {
2618 ch = peek(p);
2619 if (ch == '#')
2620 break;
2621 query.push_back((XMLCh)ch);
2622 p++;
2623 }
2626 return p;
2627 }
2629 int URI::parseFragment(int p0)
2630 {
2632 int p = p0;
2633 int ch = peek(p);
2634 if (ch != '#')
2635 return p0;
2637 p++;
2638 while (p < parselen)
2639 {
2640 ch = peek(p);
2641 if (ch == '?')
2642 break;
2643 fragment.push_back((XMLCh)ch);
2644 p++;
2645 }
2648 return p;
2649 }
2652 int URI::parse(int p0)
2653 {
2655 int p = p0;
2657 int p2 = parseScheme(p);
2658 if (p2 < 0)
2659 {
2660 error("Scheme");
2661 return -1;
2662 }
2663 p = p2;
2666 p2 = parseHierarchicalPart(p);
2667 if (p2 < 0)
2668 {
2669 error("Hierarchical part");
2670 return -1;
2671 }
2672 p = p2;
2674 p2 = parseQuery(p);
2675 if (p2 < 0)
2676 {
2677 error("Query");
2678 return -1;
2679 }
2680 p = p2;
2683 p2 = parseFragment(p);
2684 if (p2 < 0)
2685 {
2686 error("Fragment");
2687 return -1;
2688 }
2689 p = p2;
2691 return p;
2693 }
2697 bool URI::parse(const String &str)
2698 {
2699 init();
2701 parselen = str.size();
2703 String tmp;
2704 for (unsigned int i=0 ; i<str.size() ; i++)
2705 {
2706 XMLCh ch = (XMLCh) str[i];
2707 if (ch == '\\')
2708 tmp.push_back((XMLCh)'/');
2709 else
2710 tmp.push_back(ch);
2711 }
2712 parsebuf = (char *) tmp.c_str();
2715 int p = parse(0);
2716 normalize();
2718 if (p < 0)
2719 {
2720 error("Syntax error");
2721 return false;
2722 }
2724 //printf("uri:%s\n", toString().c_str());
2725 //printf("path:%s\n", path.c_str());
2727 return true;
2729 }
2738 //########################################################################
2739 //########################################################################
2740 //## M A K E
2741 //########################################################################
2742 //########################################################################
2744 //########################################################################
2745 //# F I L E S E T
2746 //########################################################################
2747 /**
2748 * This is the descriptor for a <fileset> item
2749 */
2750 class FileSet
2751 {
2752 public:
2754 /**
2755 *
2756 */
2757 FileSet()
2758 {}
2760 /**
2761 *
2762 */
2763 FileSet(const FileSet &other)
2764 { assign(other); }
2766 /**
2767 *
2768 */
2769 FileSet &operator=(const FileSet &other)
2770 { assign(other); return *this; }
2772 /**
2773 *
2774 */
2775 virtual ~FileSet()
2776 {}
2778 /**
2779 *
2780 */
2781 String getDirectory()
2782 { return directory; }
2784 /**
2785 *
2786 */
2787 void setDirectory(const String &val)
2788 { directory = val; }
2790 /**
2791 *
2792 */
2793 void setFiles(const std::vector<String> &val)
2794 { files = val; }
2796 /**
2797 *
2798 */
2799 std::vector<String> getFiles()
2800 { return files; }
2802 /**
2803 *
2804 */
2805 void setIncludes(const std::vector<String> &val)
2806 { includes = val; }
2808 /**
2809 *
2810 */
2811 std::vector<String> getIncludes()
2812 { return includes; }
2814 /**
2815 *
2816 */
2817 void setExcludes(const std::vector<String> &val)
2818 { excludes = val; }
2820 /**
2821 *
2822 */
2823 std::vector<String> getExcludes()
2824 { return excludes; }
2826 /**
2827 *
2828 */
2829 unsigned int size()
2830 { return files.size(); }
2832 /**
2833 *
2834 */
2835 String operator[](int index)
2836 { return files[index]; }
2838 /**
2839 *
2840 */
2841 void clear()
2842 {
2843 directory = "";
2844 files.clear();
2845 includes.clear();
2846 excludes.clear();
2847 }
2850 private:
2852 void assign(const FileSet &other)
2853 {
2854 directory = other.directory;
2855 files = other.files;
2856 includes = other.includes;
2857 excludes = other.excludes;
2858 }
2860 String directory;
2861 std::vector<String> files;
2862 std::vector<String> includes;
2863 std::vector<String> excludes;
2864 };
2867 //########################################################################
2868 //# F I L E L I S T
2869 //########################################################################
2870 /**
2871 * This is a simpler, explicitly-named list of files
2872 */
2873 class FileList
2874 {
2875 public:
2877 /**
2878 *
2879 */
2880 FileList()
2881 {}
2883 /**
2884 *
2885 */
2886 FileList(const FileList &other)
2887 { assign(other); }
2889 /**
2890 *
2891 */
2892 FileList &operator=(const FileList &other)
2893 { assign(other); return *this; }
2895 /**
2896 *
2897 */
2898 virtual ~FileList()
2899 {}
2901 /**
2902 *
2903 */
2904 String getDirectory()
2905 { return directory; }
2907 /**
2908 *
2909 */
2910 void setDirectory(const String &val)
2911 { directory = val; }
2913 /**
2914 *
2915 */
2916 void setFiles(const std::vector<String> &val)
2917 { files = val; }
2919 /**
2920 *
2921 */
2922 std::vector<String> getFiles()
2923 { return files; }
2925 /**
2926 *
2927 */
2928 unsigned int size()
2929 { return files.size(); }
2931 /**
2932 *
2933 */
2934 String operator[](int index)
2935 { return files[index]; }
2937 /**
2938 *
2939 */
2940 void clear()
2941 {
2942 directory = "";
2943 files.clear();
2944 }
2947 private:
2949 void assign(const FileList &other)
2950 {
2951 directory = other.directory;
2952 files = other.files;
2953 }
2955 String directory;
2956 std::vector<String> files;
2957 };
2962 //########################################################################
2963 //# M A K E B A S E
2964 //########################################################################
2965 /**
2966 * Base class for all classes in this file
2967 */
2968 class MakeBase
2969 {
2970 public:
2972 MakeBase()
2973 { line = 0; }
2974 virtual ~MakeBase()
2975 {}
2977 /**
2978 * Return the URI of the file associated with this object
2979 */
2980 URI getURI()
2981 { return uri; }
2983 /**
2984 * Set the uri to the given string
2985 */
2986 void setURI(const String &uristr)
2987 { uri.parse(uristr); }
2989 /**
2990 * Resolve another path relative to this one
2991 */
2992 String resolve(const String &otherPath);
2994 /**
2995 * replace variable refs like ${a} with their values
2996 * Assume that the string has already been syntax validated
2997 */
2998 String eval(const String &s, const String &defaultVal);
3000 /**
3001 * replace variable refs like ${a} with their values
3002 * return true or false
3003 * Assume that the string has already been syntax validated
3004 */
3005 bool evalBool(const String &s, bool defaultVal);
3007 /**
3008 * Get an element attribute, performing substitutions if necessary
3009 */
3010 bool getAttribute(Element *elem, const String &name, String &result);
3012 /**
3013 * Get an element value, performing substitutions if necessary
3014 */
3015 bool getValue(Element *elem, String &result);
3017 /**
3018 * Set the current line number in the file
3019 */
3020 void setLine(int val)
3021 { line = val; }
3023 /**
3024 * Get the current line number in the file
3025 */
3026 int getLine()
3027 { return line; }
3030 /**
3031 * Set a property to a given value
3032 */
3033 virtual void setProperty(const String &name, const String &val)
3034 {
3035 properties[name] = val;
3036 }
3038 /**
3039 * Return a named property is found, else a null string
3040 */
3041 virtual String getProperty(const String &name)
3042 {
3043 String val;
3044 std::map<String, String>::iterator iter = properties.find(name);
3045 if (iter != properties.end())
3046 val = iter->second;
3047 String sval;
3048 if (!getSubstitutions(val, sval))
3049 return false;
3050 return sval;
3051 }
3053 /**
3054 * Return true if a named property is found, else false
3055 */
3056 virtual bool hasProperty(const String &name)
3057 {
3058 std::map<String, String>::iterator iter = properties.find(name);
3059 if (iter == properties.end())
3060 return false;
3061 return true;
3062 }
3065 protected:
3067 /**
3068 * The path to the file associated with this object
3069 */
3070 URI uri;
3072 /**
3073 * If this prefix is seen in a substitution, use an environment
3074 * variable.
3075 * example: <property environment="env"/>
3076 * ${env.JAVA_HOME}
3077 */
3078 String envPrefix;
3080 /**
3081 * If this prefix is seen in a substitution, use as a
3082 * pkg-config 'all' query
3083 * example: <property pkg-config="pc"/>
3084 * ${pc.gtkmm}
3085 */
3086 String pcPrefix;
3088 /**
3089 * If this prefix is seen in a substitution, use as a
3090 * pkg-config 'cflags' query
3091 * example: <property pkg-config="pcc"/>
3092 * ${pcc.gtkmm}
3093 */
3094 String pccPrefix;
3096 /**
3097 * If this prefix is seen in a substitution, use as a
3098 * pkg-config 'libs' query
3099 * example: <property pkg-config="pcl"/>
3100 * ${pcl.gtkmm}
3101 */
3102 String pclPrefix;
3108 /**
3109 * Print a printf()-like formatted error message
3110 */
3111 void error(const char *fmt, ...);
3113 /**
3114 * Print a printf()-like formatted trace message
3115 */
3116 void status(const char *fmt, ...);
3118 /**
3119 * Show target status
3120 */
3121 void targetstatus(const char *fmt, ...);
3123 /**
3124 * Print a printf()-like formatted trace message
3125 */
3126 void trace(const char *fmt, ...);
3128 /**
3129 * Check if a given string matches a given regex pattern
3130 */
3131 bool regexMatch(const String &str, const String &pattern);
3133 /**
3134 *
3135 */
3136 String getSuffix(const String &fname);
3138 /**
3139 * Break up a string into substrings delimited the characters
3140 * in delimiters. Null-length substrings are ignored
3141 */
3142 std::vector<String> tokenize(const String &val,
3143 const String &delimiters);
3145 /**
3146 * replace runs of whitespace with a space
3147 */
3148 String strip(const String &s);
3150 /**
3151 * remove leading whitespace from each line
3152 */
3153 String leftJustify(const String &s);
3155 /**
3156 * remove leading and trailing whitespace from string
3157 */
3158 String trim(const String &s);
3160 /**
3161 * Return a lower case version of the given string
3162 */
3163 String toLower(const String &s);
3165 /**
3166 * Return the native format of the canonical
3167 * path which we store
3168 */
3169 String getNativePath(const String &path);
3171 /**
3172 * Execute a shell command. Outbuf is a ref to a string
3173 * to catch the result.
3174 */
3175 bool executeCommand(const String &call,
3176 const String &inbuf,
3177 String &outbuf,
3178 String &errbuf);
3179 /**
3180 * List all directories in a given base and starting directory
3181 * It is usually called like:
3182 * bool ret = listDirectories("src", "", result);
3183 */
3184 bool listDirectories(const String &baseName,
3185 const String &dirname,
3186 std::vector<String> &res);
3188 /**
3189 * Find all files in the named directory
3190 */
3191 bool listFiles(const String &baseName,
3192 const String &dirname,
3193 std::vector<String> &result);
3195 /**
3196 * Perform a listing for a fileset
3197 */
3198 bool listFiles(MakeBase &propRef, FileSet &fileSet);
3200 /**
3201 * Parse a <patternset>
3202 */
3203 bool parsePatternSet(Element *elem,
3204 MakeBase &propRef,
3205 std::vector<String> &includes,
3206 std::vector<String> &excludes);
3208 /**
3209 * Parse a <fileset> entry, and determine which files
3210 * should be included
3211 */
3212 bool parseFileSet(Element *elem,
3213 MakeBase &propRef,
3214 FileSet &fileSet);
3215 /**
3216 * Parse a <filelist> entry
3217 */
3218 bool parseFileList(Element *elem,
3219 MakeBase &propRef,
3220 FileList &fileList);
3222 /**
3223 * Return this object's property list
3224 */
3225 virtual std::map<String, String> &getProperties()
3226 { return properties; }
3229 std::map<String, String> properties;
3231 /**
3232 * Create a directory, making intermediate dirs
3233 * if necessary
3234 */
3235 bool createDirectory(const String &dirname);
3237 /**
3238 * Delete a directory and its children if desired
3239 */
3240 bool removeDirectory(const String &dirName);
3242 /**
3243 * Copy a file from one name to another. Perform only if needed
3244 */
3245 bool copyFile(const String &srcFile, const String &destFile);
3247 /**
3248 * Tests if the file exists and is a regular file
3249 */
3250 bool isRegularFile(const String &fileName);
3252 /**
3253 * Tests if the file exists and is a directory
3254 */
3255 bool isDirectory(const String &fileName);
3257 /**
3258 * Tests is the modification date of fileA is newer than fileB
3259 */
3260 bool isNewerThan(const String &fileA, const String &fileB);
3262 private:
3264 bool pkgConfigRecursive(const String packageName,
3265 const String &path,
3266 const String &prefix,
3267 int query,
3268 String &result,
3269 std::set<String> &deplist);
3271 /**
3272 * utility method to query for "all", "cflags", or "libs" for this package and its
3273 * dependencies. 0, 1, 2
3274 */
3275 bool pkgConfigQuery(const String &packageName, int query, String &result);
3277 /**
3278 * replace a variable ref like ${a} with a value
3279 */
3280 bool lookupProperty(const String &s, String &result);
3282 /**
3283 * called by getSubstitutions(). This is in case a looked-up string
3284 * has substitutions also.
3285 */
3286 bool getSubstitutionsRecursive(const String &s, String &result, int depth);
3288 /**
3289 * replace variable refs in a string like ${a} with their values
3290 */
3291 bool getSubstitutions(const String &s, String &result);
3293 int line;
3296 };
3300 /**
3301 * Define the pkg-config class here, since it will be used in MakeBase method
3302 * implementations.
3303 */
3304 class PkgConfig : public MakeBase
3305 {
3307 public:
3309 /**
3310 *
3311 */
3312 PkgConfig()
3313 {
3314 path = ".";
3315 prefix = "/target";
3316 init();
3317 }
3319 /**
3320 *
3321 */
3322 PkgConfig(const PkgConfig &other)
3323 { assign(other); }
3325 /**
3326 *
3327 */
3328 PkgConfig &operator=(const PkgConfig &other)
3329 { assign(other); return *this; }
3331 /**
3332 *
3333 */
3334 virtual ~PkgConfig()
3335 { }
3337 /**
3338 *
3339 */
3340 virtual String getName()
3341 { return name; }
3343 /**
3344 *
3345 */
3346 virtual String getPath()
3347 { return path; }
3349 /**
3350 *
3351 */
3352 virtual void setPath(const String &val)
3353 { path = val; }
3355 /**
3356 *
3357 */
3358 virtual String getPrefix()
3359 { return prefix; }
3361 /**
3362 * Allow the user to override the prefix in the file
3363 */
3364 virtual void setPrefix(const String &val)
3365 { prefix = val; }
3367 /**
3368 *
3369 */
3370 virtual String getDescription()
3371 { return description; }
3373 /**
3374 *
3375 */
3376 virtual String getCflags()
3377 { return cflags; }
3379 /**
3380 *
3381 */
3382 virtual String getLibs()
3383 { return libs; }
3385 /**
3386 *
3387 */
3388 virtual String getAll()
3389 {
3390 String ret = cflags;
3391 ret.append(" ");
3392 ret.append(libs);
3393 return ret;
3394 }
3396 /**
3397 *
3398 */
3399 virtual String getVersion()
3400 { return version; }
3402 /**
3403 *
3404 */
3405 virtual int getMajorVersion()
3406 { return majorVersion; }
3408 /**
3409 *
3410 */
3411 virtual int getMinorVersion()
3412 { return minorVersion; }
3414 /**
3415 *
3416 */
3417 virtual int getMicroVersion()
3418 { return microVersion; }
3420 /**
3421 *
3422 */
3423 virtual std::map<String, String> &getAttributes()
3424 { return attrs; }
3426 /**
3427 *
3428 */
3429 virtual std::vector<String> &getRequireList()
3430 { return requireList; }
3432 /**
3433 * Read a file for its details
3434 */
3435 virtual bool readFile(const String &fileName);
3437 /**
3438 * Read a file for its details
3439 */
3440 virtual bool query(const String &name);
3442 private:
3444 void init()
3445 {
3446 //do not set path and prefix here
3447 name = "";
3448 description = "";
3449 cflags = "";
3450 libs = "";
3451 requires = "";
3452 version = "";
3453 majorVersion = 0;
3454 minorVersion = 0;
3455 microVersion = 0;
3456 fileName = "";
3457 attrs.clear();
3458 requireList.clear();
3459 }
3461 void assign(const PkgConfig &other)
3462 {
3463 name = other.name;
3464 path = other.path;
3465 prefix = other.prefix;
3466 description = other.description;
3467 cflags = other.cflags;
3468 libs = other.libs;
3469 requires = other.requires;
3470 version = other.version;
3471 majorVersion = other.majorVersion;
3472 minorVersion = other.minorVersion;
3473 microVersion = other.microVersion;
3474 fileName = other.fileName;
3475 attrs = other.attrs;
3476 requireList = other.requireList;
3477 }
3481 int get(int pos);
3483 int skipwhite(int pos);
3485 int getword(int pos, String &ret);
3487 /**
3488 * Very important
3489 */
3490 bool parseRequires();
3492 void parseVersion();
3494 bool parseLine(const String &lineBuf);
3496 bool parse(const String &buf);
3498 void dumpAttrs();
3500 String name;
3502 String path;
3504 String prefix;
3506 String description;
3508 String cflags;
3510 String libs;
3512 String requires;
3514 String version;
3516 int majorVersion;
3518 int minorVersion;
3520 int microVersion;
3522 String fileName;
3524 std::map<String, String> attrs;
3526 std::vector<String> requireList;
3528 char *parsebuf;
3529 int parselen;
3530 };
3535 /**
3536 * Print a printf()-like formatted error message
3537 */
3538 void MakeBase::error(const char *fmt, ...)
3539 {
3540 va_list args;
3541 va_start(args,fmt);
3542 fprintf(stderr, "Make error line %d: ", line);
3543 vfprintf(stderr, fmt, args);
3544 fprintf(stderr, "\n");
3545 va_end(args) ;
3546 }
3550 /**
3551 * Print a printf()-like formatted trace message
3552 */
3553 void MakeBase::status(const char *fmt, ...)
3554 {
3555 va_list args;
3556 //fprintf(stdout, " ");
3557 va_start(args,fmt);
3558 vfprintf(stdout, fmt, args);
3559 va_end(args);
3560 fprintf(stdout, "\n");
3561 fflush(stdout);
3562 }
3565 /**
3566 * Print a printf()-like formatted trace message
3567 */
3568 void MakeBase::trace(const char *fmt, ...)
3569 {
3570 va_list args;
3571 fprintf(stdout, "Make: ");
3572 va_start(args,fmt);
3573 vfprintf(stdout, fmt, args);
3574 va_end(args) ;
3575 fprintf(stdout, "\n");
3576 fflush(stdout);
3577 }
3581 /**
3582 * Resolve another path relative to this one
3583 */
3584 String MakeBase::resolve(const String &otherPath)
3585 {
3586 URI otherURI(otherPath);
3587 URI fullURI = uri.resolve(otherURI);
3588 String ret = fullURI.toString();
3589 return ret;
3590 }
3594 /**
3595 * Check if a given string matches a given regex pattern
3596 */
3597 bool MakeBase::regexMatch(const String &str, const String &pattern)
3598 {
3599 const TRexChar *terror = NULL;
3600 const TRexChar *cpat = pattern.c_str();
3601 TRex *expr = trex_compile(cpat, &terror);
3602 if (!expr)
3603 {
3604 if (!terror)
3605 terror = "undefined";
3606 error("compilation error [%s]!\n", terror);
3607 return false;
3608 }
3610 bool ret = true;
3612 const TRexChar *cstr = str.c_str();
3613 if (trex_match(expr, cstr))
3614 {
3615 ret = true;
3616 }
3617 else
3618 {
3619 ret = false;
3620 }
3622 trex_free(expr);
3624 return ret;
3625 }
3627 /**
3628 * Return the suffix, if any, of a file name
3629 */
3630 String MakeBase::getSuffix(const String &fname)
3631 {
3632 if (fname.size() < 2)
3633 return "";
3634 unsigned int pos = fname.find_last_of('.');
3635 if (pos == fname.npos)
3636 return "";
3637 pos++;
3638 String res = fname.substr(pos, fname.size()-pos);
3639 //trace("suffix:%s", res.c_str());
3640 return res;
3641 }
3645 /**
3646 * Break up a string into substrings delimited the characters
3647 * in delimiters. Null-length substrings are ignored
3648 */
3649 std::vector<String> MakeBase::tokenize(const String &str,
3650 const String &delimiters)
3651 {
3653 std::vector<String> res;
3654 char *del = (char *)delimiters.c_str();
3655 String dmp;
3656 for (unsigned int i=0 ; i<str.size() ; i++)
3657 {
3658 char ch = str[i];
3659 char *p = (char *)0;
3660 for (p=del ; *p ; p++)
3661 if (*p == ch)
3662 break;
3663 if (*p)
3664 {
3665 if (dmp.size() > 0)
3666 {
3667 res.push_back(dmp);
3668 dmp.clear();
3669 }
3670 }
3671 else
3672 {
3673 dmp.push_back(ch);
3674 }
3675 }
3676 //Add tail
3677 if (dmp.size() > 0)
3678 {
3679 res.push_back(dmp);
3680 dmp.clear();
3681 }
3683 return res;
3684 }
3688 /**
3689 * replace runs of whitespace with a single space
3690 */
3691 String MakeBase::strip(const String &s)
3692 {
3693 int len = s.size();
3694 String stripped;
3695 for (int i = 0 ; i<len ; i++)
3696 {
3697 char ch = s[i];
3698 if (isspace(ch))
3699 {
3700 stripped.push_back(' ');
3701 for ( ; i<len ; i++)
3702 {
3703 ch = s[i];
3704 if (!isspace(ch))
3705 {
3706 stripped.push_back(ch);
3707 break;
3708 }
3709 }
3710 }
3711 else
3712 {
3713 stripped.push_back(ch);
3714 }
3715 }
3716 return stripped;
3717 }
3719 /**
3720 * remove leading whitespace from each line
3721 */
3722 String MakeBase::leftJustify(const String &s)
3723 {
3724 String out;
3725 int len = s.size();
3726 for (int i = 0 ; i<len ; )
3727 {
3728 char ch;
3729 //Skip to first visible character
3730 while (i<len)
3731 {
3732 ch = s[i];
3733 if (ch == '\n' || ch == '\r'
3734 || !isspace(ch))
3735 break;
3736 i++;
3737 }
3738 //Copy the rest of the line
3739 while (i<len)
3740 {
3741 ch = s[i];
3742 if (ch == '\n' || ch == '\r')
3743 {
3744 if (ch != '\r')
3745 out.push_back('\n');
3746 i++;
3747 break;
3748 }
3749 else
3750 {
3751 out.push_back(ch);
3752 }
3753 i++;
3754 }
3755 }
3756 return out;
3757 }
3760 /**
3761 * Removes whitespace from beginning and end of a string
3762 */
3763 String MakeBase::trim(const String &s)
3764 {
3765 if (s.size() < 1)
3766 return s;
3768 //Find first non-ws char
3769 unsigned int begin = 0;
3770 for ( ; begin < s.size() ; begin++)
3771 {
3772 if (!isspace(s[begin]))
3773 break;
3774 }
3776 //Find first non-ws char, going in reverse
3777 unsigned int end = s.size() - 1;
3778 for ( ; end > begin ; end--)
3779 {
3780 if (!isspace(s[end]))
3781 break;
3782 }
3783 //trace("begin:%d end:%d", begin, end);
3785 String res = s.substr(begin, end-begin+1);
3786 return res;
3787 }
3790 /**
3791 * Return a lower case version of the given string
3792 */
3793 String MakeBase::toLower(const String &s)
3794 {
3795 if (s.size()==0)
3796 return s;
3798 String ret;
3799 for(unsigned int i=0; i<s.size() ; i++)
3800 {
3801 ret.push_back(tolower(s[i]));
3802 }
3803 return ret;
3804 }
3807 /**
3808 * Return the native format of the canonical
3809 * path which we store
3810 */
3811 String MakeBase::getNativePath(const String &path)
3812 {
3813 #ifdef __WIN32__
3814 String npath;
3815 unsigned int firstChar = 0;
3816 if (path.size() >= 3)
3817 {
3818 if (path[0] == '/' &&
3819 isalpha(path[1]) &&
3820 path[2] == ':')
3821 firstChar++;
3822 }
3823 for (unsigned int i=firstChar ; i<path.size() ; i++)
3824 {
3825 char ch = path[i];
3826 if (ch == '/')
3827 npath.push_back('\\');
3828 else
3829 npath.push_back(ch);
3830 }
3831 return npath;
3832 #else
3833 return path;
3834 #endif
3835 }
3838 #ifdef __WIN32__
3839 #include <tchar.h>
3841 static String win32LastError()
3842 {
3844 DWORD dw = GetLastError();
3846 LPVOID str;
3847 FormatMessage(
3848 FORMAT_MESSAGE_ALLOCATE_BUFFER |
3849 FORMAT_MESSAGE_FROM_SYSTEM,
3850 NULL,
3851 dw,
3852 0,
3853 (LPTSTR) &str,
3854 0, NULL );
3855 LPTSTR p = _tcschr((const char *)str, _T('\r'));
3856 if(p != NULL)
3857 { // lose CRLF
3858 *p = _T('\0');
3859 }
3860 String ret = (char *)str;
3861 LocalFree(str);
3863 return ret;
3864 }
3865 #endif
3870 #ifdef __WIN32__
3872 /**
3873 * Execute a system call, using pipes to send data to the
3874 * program's stdin, and reading stdout and stderr.
3875 */
3876 bool MakeBase::executeCommand(const String &command,
3877 const String &inbuf,
3878 String &outbuf,
3879 String &errbuf)
3880 {
3882 status("============ cmd ============\n%s\n=============================",
3883 command.c_str());
3885 outbuf.clear();
3886 errbuf.clear();
3889 /*
3890 I really hate having win32 code in this program, but the
3891 read buffer in command.com and cmd.exe are just too small
3892 for the large commands we need for compiling and linking.
3893 */
3895 bool ret = true;
3897 //# Allocate a separate buffer for safety
3898 char *paramBuf = new char[command.size() + 1];
3899 if (!paramBuf)
3900 {
3901 error("executeCommand cannot allocate command buffer");
3902 return false;
3903 }
3904 strcpy(paramBuf, (char *)command.c_str());
3906 //# Go to http://msdn2.microsoft.com/en-us/library/ms682499.aspx
3907 //# to see how Win32 pipes work
3909 //# Create pipes
3910 SECURITY_ATTRIBUTES saAttr;
3911 saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
3912 saAttr.bInheritHandle = TRUE;
3913 saAttr.lpSecurityDescriptor = NULL;
3914 HANDLE stdinRead, stdinWrite;
3915 HANDLE stdoutRead, stdoutWrite;
3916 HANDLE stderrRead, stderrWrite;
3917 if (!CreatePipe(&stdinRead, &stdinWrite, &saAttr, 0))
3918 {
3919 error("executeProgram: could not create pipe");
3920 delete[] paramBuf;
3921 return false;
3922 }
3923 SetHandleInformation(stdinWrite, HANDLE_FLAG_INHERIT, 0);
3924 if (!CreatePipe(&stdoutRead, &stdoutWrite, &saAttr, 0))
3925 {
3926 error("executeProgram: could not create pipe");
3927 delete[] paramBuf;
3928 return false;
3929 }
3930 SetHandleInformation(stdoutRead, HANDLE_FLAG_INHERIT, 0);
3931 if (!CreatePipe(&stderrRead, &stderrWrite, &saAttr, 0))
3932 {
3933 error("executeProgram: could not create pipe");
3934 delete[] paramBuf;
3935 return false;
3936 }
3937 SetHandleInformation(stderrRead, HANDLE_FLAG_INHERIT, 0);
3939 // Create the process
3940 STARTUPINFO siStartupInfo;
3941 PROCESS_INFORMATION piProcessInfo;
3942 memset(&siStartupInfo, 0, sizeof(siStartupInfo));
3943 memset(&piProcessInfo, 0, sizeof(piProcessInfo));
3944 siStartupInfo.cb = sizeof(siStartupInfo);
3945 siStartupInfo.hStdError = stderrWrite;
3946 siStartupInfo.hStdOutput = stdoutWrite;
3947 siStartupInfo.hStdInput = stdinRead;
3948 siStartupInfo.dwFlags |= STARTF_USESTDHANDLES;
3950 if (!CreateProcess(NULL, paramBuf, NULL, NULL, true,
3951 0, NULL, NULL, &siStartupInfo,
3952 &piProcessInfo))
3953 {
3954 error("executeCommand : could not create process : %s",
3955 win32LastError().c_str());
3956 ret = false;
3957 }
3959 delete[] paramBuf;
3961 DWORD bytesWritten;
3962 if (inbuf.size()>0 &&
3963 !WriteFile(stdinWrite, inbuf.c_str(), inbuf.size(),
3964 &bytesWritten, NULL))
3965 {
3966 error("executeCommand: could not write to pipe");
3967 return false;
3968 }
3969 if (!CloseHandle(stdinWrite))
3970 {
3971 error("executeCommand: could not close write pipe");
3972 return false;
3973 }
3974 if (!CloseHandle(stdoutWrite))
3975 {
3976 error("executeCommand: could not close read pipe");
3977 return false;
3978 }
3979 if (!CloseHandle(stderrWrite))
3980 {
3981 error("executeCommand: could not close read pipe");
3982 return false;
3983 }
3985 bool lastLoop = false;
3986 while (true)
3987 {
3988 DWORD avail;
3989 DWORD bytesRead;
3990 char readBuf[4096];
3992 //trace("## stderr");
3993 PeekNamedPipe(stderrRead, NULL, 0, NULL, &avail, NULL);
3994 if (avail > 0)
3995 {
3996 bytesRead = 0;
3997 if (avail>4096) avail = 4096;
3998 ReadFile(stderrRead, readBuf, avail, &bytesRead, NULL);
3999 if (bytesRead > 0)
4000 {
4001 for (unsigned int i=0 ; i<bytesRead ; i++)
4002 errbuf.push_back(readBuf[i]);
4003 }
4004 }
4006 //trace("## stdout");
4007 PeekNamedPipe(stdoutRead, NULL, 0, NULL, &avail, NULL);
4008 if (avail > 0)
4009 {
4010 bytesRead = 0;
4011 if (avail>4096) avail = 4096;
4012 ReadFile(stdoutRead, readBuf, avail, &bytesRead, NULL);
4013 if (bytesRead > 0)
4014 {
4015 for (unsigned int i=0 ; i<bytesRead ; i++)
4016 outbuf.push_back(readBuf[i]);
4017 }
4018 }
4020 //Was this the final check after program done?
4021 if (lastLoop)
4022 break;
4024 DWORD exitCode;
4025 GetExitCodeProcess(piProcessInfo.hProcess, &exitCode);
4026 if (exitCode != STILL_ACTIVE)
4027 lastLoop = true;
4029 Sleep(10);
4030 }
4031 //trace("outbuf:%s", outbuf.c_str());
4032 if (!CloseHandle(stdoutRead))
4033 {
4034 error("executeCommand: could not close read pipe");
4035 return false;
4036 }
4037 if (!CloseHandle(stderrRead))
4038 {
4039 error("executeCommand: could not close read pipe");
4040 return false;
4041 }
4043 DWORD exitCode;
4044 GetExitCodeProcess(piProcessInfo.hProcess, &exitCode);
4045 //trace("exit code:%d", exitCode);
4046 if (exitCode != 0)
4047 {
4048 ret = false;
4049 }
4051 CloseHandle(piProcessInfo.hProcess);
4052 CloseHandle(piProcessInfo.hThread);
4054 return ret;
4056 }
4058 #else /*do it unix style*/
4060 #include <sys/wait.h>
4064 /**
4065 * Execute a system call, using pipes to send data to the
4066 * program's stdin, and reading stdout and stderr.
4067 */
4068 bool MakeBase::executeCommand(const String &command,
4069 const String &inbuf,
4070 String &outbuf,
4071 String &errbuf)
4072 {
4074 status("============ cmd ============\n%s\n=============================",
4075 command.c_str());
4077 outbuf.clear();
4078 errbuf.clear();
4081 int outfds[2];
4082 if (pipe(outfds) < 0)
4083 return false;
4084 int errfds[2];
4085 if (pipe(errfds) < 0)
4086 return false;
4087 int pid = fork();
4088 if (pid < 0)
4089 {
4090 close(outfds[0]);
4091 close(outfds[1]);
4092 close(errfds[0]);
4093 close(errfds[1]);
4094 error("launch of command '%s' failed : %s",
4095 command.c_str(), strerror(errno));
4096 return false;
4097 }
4098 else if (pid > 0) // parent
4099 {
4100 close(outfds[1]);
4101 close(errfds[1]);
4102 }
4103 else // == 0, child
4104 {
4105 close(outfds[0]);
4106 dup2(outfds[1], STDOUT_FILENO);
4107 close(outfds[1]);
4108 close(errfds[0]);
4109 dup2(errfds[1], STDERR_FILENO);
4110 close(errfds[1]);
4112 char *args[4];
4113 args[0] = (char *)"sh";
4114 args[1] = (char *)"-c";
4115 args[2] = (char *)command.c_str();
4116 args[3] = NULL;
4117 execv("/bin/sh", args);
4118 _exit(EXIT_FAILURE);
4119 }
4121 String outb;
4122 String errb;
4124 int outRead = outfds[0];
4125 int errRead = errfds[0];
4126 int max = outRead;
4127 if (errRead > max)
4128 max = errRead;
4130 bool outOpen = true;
4131 bool errOpen = true;
4133 while (outOpen || errOpen)
4134 {
4135 char ch;
4136 fd_set fdset;
4137 FD_ZERO(&fdset);
4138 if (outOpen)
4139 FD_SET(outRead, &fdset);
4140 if (errOpen)
4141 FD_SET(errRead, &fdset);
4142 int ret = select(max+1, &fdset, NULL, NULL, NULL);
4143 if (ret < 0)
4144 break;
4145 if (FD_ISSET(outRead, &fdset))
4146 {
4147 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 return false;
5412 return true;
5413 }
5419 //########################################################################
5420 //# D E P T O O L
5421 //########################################################################
5425 /**
5426 * Class which holds information for each file.
5427 */
5428 class FileRec
5429 {
5430 public:
5432 typedef enum
5433 {
5434 UNKNOWN,
5435 CFILE,
5436 HFILE,
5437 OFILE
5438 } FileType;
5440 /**
5441 * Constructor
5442 */
5443 FileRec()
5444 { init(); type = UNKNOWN; }
5446 /**
5447 * Copy constructor
5448 */
5449 FileRec(const FileRec &other)
5450 { init(); assign(other); }
5451 /**
5452 * Constructor
5453 */
5454 FileRec(int typeVal)
5455 { init(); type = typeVal; }
5456 /**
5457 * Assignment operator
5458 */
5459 FileRec &operator=(const FileRec &other)
5460 { init(); assign(other); return *this; }
5463 /**
5464 * Destructor
5465 */
5466 ~FileRec()
5467 {}
5469 /**
5470 * Directory part of the file name
5471 */
5472 String path;
5474 /**
5475 * Base name, sans directory and suffix
5476 */
5477 String baseName;
5479 /**
5480 * File extension, such as cpp or h
5481 */
5482 String suffix;
5484 /**
5485 * Type of file: CFILE, HFILE, OFILE
5486 */
5487 int type;
5489 /**
5490 * Used to list files ref'd by this one
5491 */
5492 std::map<String, FileRec *> files;
5495 private:
5497 void init()
5498 {
5499 }
5501 void assign(const FileRec &other)
5502 {
5503 type = other.type;
5504 baseName = other.baseName;
5505 suffix = other.suffix;
5506 files = other.files;
5507 }
5509 };
5513 /**
5514 * Simpler dependency record
5515 */
5516 class DepRec
5517 {
5518 public:
5520 /**
5521 * Constructor
5522 */
5523 DepRec()
5524 {init();}
5526 /**
5527 * Copy constructor
5528 */
5529 DepRec(const DepRec &other)
5530 {init(); assign(other);}
5531 /**
5532 * Constructor
5533 */
5534 DepRec(const String &fname)
5535 {init(); name = fname; }
5536 /**
5537 * Assignment operator
5538 */
5539 DepRec &operator=(const DepRec &other)
5540 {init(); assign(other); return *this;}
5543 /**
5544 * Destructor
5545 */
5546 ~DepRec()
5547 {}
5549 /**
5550 * Directory part of the file name
5551 */
5552 String path;
5554 /**
5555 * Base name, without the path and suffix
5556 */
5557 String name;
5559 /**
5560 * Suffix of the source
5561 */
5562 String suffix;
5565 /**
5566 * Used to list files ref'd by this one
5567 */
5568 std::vector<String> files;
5571 private:
5573 void init()
5574 {
5575 }
5577 void assign(const DepRec &other)
5578 {
5579 path = other.path;
5580 name = other.name;
5581 suffix = other.suffix;
5582 files = other.files; //avoid recursion
5583 }
5585 };
5588 class DepTool : public MakeBase
5589 {
5590 public:
5592 /**
5593 * Constructor
5594 */
5595 DepTool()
5596 { init(); }
5598 /**
5599 * Copy constructor
5600 */
5601 DepTool(const DepTool &other)
5602 { init(); assign(other); }
5604 /**
5605 * Assignment operator
5606 */
5607 DepTool &operator=(const DepTool &other)
5608 { init(); assign(other); return *this; }
5611 /**
5612 * Destructor
5613 */
5614 ~DepTool()
5615 {}
5618 /**
5619 * Reset this section of code
5620 */
5621 virtual void init();
5623 /**
5624 * Reset this section of code
5625 */
5626 virtual void assign(const DepTool &other)
5627 {
5628 }
5630 /**
5631 * Sets the source directory which will be scanned
5632 */
5633 virtual void setSourceDirectory(const String &val)
5634 { sourceDir = val; }
5636 /**
5637 * Returns the source directory which will be scanned
5638 */
5639 virtual String getSourceDirectory()
5640 { return sourceDir; }
5642 /**
5643 * Sets the list of files within the directory to analyze
5644 */
5645 virtual void setFileList(const std::vector<String> &list)
5646 { fileList = list; }
5648 /**
5649 * Creates the list of all file names which will be
5650 * candidates for further processing. Reads make.exclude
5651 * to see which files for directories to leave out.
5652 */
5653 virtual bool createFileList();
5656 /**
5657 * Generates the forward dependency list
5658 */
5659 virtual bool generateDependencies();
5662 /**
5663 * Generates the forward dependency list, saving the file
5664 */
5665 virtual bool generateDependencies(const String &);
5668 /**
5669 * Load a dependency file
5670 */
5671 std::vector<DepRec> loadDepFile(const String &fileName);
5673 /**
5674 * Load a dependency file, generating one if necessary
5675 */
5676 std::vector<DepRec> getDepFile(const String &fileName,
5677 bool forceRefresh);
5679 /**
5680 * Save a dependency file
5681 */
5682 bool saveDepFile(const String &fileName);
5685 private:
5688 /**
5689 *
5690 */
5691 void parseName(const String &fullname,
5692 String &path,
5693 String &basename,
5694 String &suffix);
5696 /**
5697 *
5698 */
5699 int get(int pos);
5701 /**
5702 *
5703 */
5704 int skipwhite(int pos);
5706 /**
5707 *
5708 */
5709 int getword(int pos, String &ret);
5711 /**
5712 *
5713 */
5714 bool sequ(int pos, const char *key);
5716 /**
5717 *
5718 */
5719 bool addIncludeFile(FileRec *frec, const String &fname);
5721 /**
5722 *
5723 */
5724 bool scanFile(const String &fname, FileRec *frec);
5726 /**
5727 *
5728 */
5729 bool processDependency(FileRec *ofile, FileRec *include);
5731 /**
5732 *
5733 */
5734 String sourceDir;
5736 /**
5737 *
5738 */
5739 std::vector<String> fileList;
5741 /**
5742 *
5743 */
5744 std::vector<String> directories;
5746 /**
5747 * A list of all files which will be processed for
5748 * dependencies.
5749 */
5750 std::map<String, FileRec *> allFiles;
5752 /**
5753 * The list of .o files, and the
5754 * dependencies upon them.
5755 */
5756 std::map<String, FileRec *> oFiles;
5758 int depFileSize;
5759 char *depFileBuf;
5761 static const int readBufSize = 8192;
5762 char readBuf[8193];//byte larger
5764 };
5770 /**
5771 * Clean up after processing. Called by the destructor, but should
5772 * also be called before the object is reused.
5773 */
5774 void DepTool::init()
5775 {
5776 sourceDir = ".";
5778 fileList.clear();
5779 directories.clear();
5781 //clear output file list
5782 std::map<String, FileRec *>::iterator iter;
5783 for (iter=oFiles.begin(); iter!=oFiles.end() ; iter++)
5784 delete iter->second;
5785 oFiles.clear();
5787 //allFiles actually contains the master copies. delete them
5788 for (iter= allFiles.begin(); iter!=allFiles.end() ; iter++)
5789 delete iter->second;
5790 allFiles.clear();
5792 }
5797 /**
5798 * Parse a full path name into path, base name, and suffix
5799 */
5800 void DepTool::parseName(const String &fullname,
5801 String &path,
5802 String &basename,
5803 String &suffix)
5804 {
5805 if (fullname.size() < 2)
5806 return;
5808 unsigned int pos = fullname.find_last_of('/');
5809 if (pos != fullname.npos && pos<fullname.size()-1)
5810 {
5811 path = fullname.substr(0, pos);
5812 pos++;
5813 basename = fullname.substr(pos, fullname.size()-pos);
5814 }
5815 else
5816 {
5817 path = "";
5818 basename = fullname;
5819 }
5821 pos = basename.find_last_of('.');
5822 if (pos != basename.npos && pos<basename.size()-1)
5823 {
5824 suffix = basename.substr(pos+1, basename.size()-pos-1);
5825 basename = basename.substr(0, pos);
5826 }
5828 //trace("parsename:%s %s %s", path.c_str(),
5829 // basename.c_str(), suffix.c_str());
5830 }
5834 /**
5835 * Generate our internal file list.
5836 */
5837 bool DepTool::createFileList()
5838 {
5840 for (unsigned int i=0 ; i<fileList.size() ; i++)
5841 {
5842 String fileName = fileList[i];
5843 //trace("## FileName:%s", fileName.c_str());
5844 String path;
5845 String basename;
5846 String sfx;
5847 parseName(fileName, path, basename, sfx);
5848 if (sfx == "cpp" || sfx == "c" || sfx == "cxx" ||
5849 sfx == "cc" || sfx == "CC")
5850 {
5851 FileRec *fe = new FileRec(FileRec::CFILE);
5852 fe->path = path;
5853 fe->baseName = basename;
5854 fe->suffix = sfx;
5855 allFiles[fileName] = fe;
5856 }
5857 else if (sfx == "h" || sfx == "hh" ||
5858 sfx == "hpp" || sfx == "hxx")
5859 {
5860 FileRec *fe = new FileRec(FileRec::HFILE);
5861 fe->path = path;
5862 fe->baseName = basename;
5863 fe->suffix = sfx;
5864 allFiles[fileName] = fe;
5865 }
5866 }
5868 if (!listDirectories(sourceDir, "", directories))
5869 return false;
5871 return true;
5872 }
5878 /**
5879 * Get a character from the buffer at pos. If out of range,
5880 * return -1 for safety
5881 */
5882 int DepTool::get(int pos)
5883 {
5884 if (pos>depFileSize)
5885 return -1;
5886 return depFileBuf[pos];
5887 }
5891 /**
5892 * Skip over all whitespace characters beginning at pos. Return
5893 * the position of the first non-whitespace character.
5894 */
5895 int DepTool::skipwhite(int pos)
5896 {
5897 while (pos < depFileSize)
5898 {
5899 int ch = get(pos);
5900 if (ch < 0)
5901 break;
5902 if (!isspace(ch))
5903 break;
5904 pos++;
5905 }
5906 return pos;
5907 }
5910 /**
5911 * Parse the buffer beginning at pos, for a word. Fill
5912 * 'ret' with the result. Return the position after the
5913 * word.
5914 */
5915 int DepTool::getword(int pos, String &ret)
5916 {
5917 while (pos < depFileSize)
5918 {
5919 int ch = get(pos);
5920 if (ch < 0)
5921 break;
5922 if (isspace(ch))
5923 break;
5924 ret.push_back((char)ch);
5925 pos++;
5926 }
5927 return pos;
5928 }
5930 /**
5931 * Return whether the sequence of characters in the buffer
5932 * beginning at pos match the key, for the length of the key
5933 */
5934 bool DepTool::sequ(int pos, const char *key)
5935 {
5936 while (*key)
5937 {
5938 if (*key != get(pos))
5939 return false;
5940 key++; pos++;
5941 }
5942 return true;
5943 }
5947 /**
5948 * Add an include file name to a file record. If the name
5949 * is not found in allFiles explicitly, try prepending include
5950 * directory names to it and try again.
5951 */
5952 bool DepTool::addIncludeFile(FileRec *frec, const String &iname)
5953 {
5954 //# if the name is an exact match to a path name
5955 //# in allFiles, like "myinc.h"
5956 std::map<String, FileRec *>::iterator iter =
5957 allFiles.find(iname);
5958 if (iter != allFiles.end()) //already exists
5959 {
5960 //h file in same dir
5961 FileRec *other = iter->second;
5962 //trace("local: '%s'", iname.c_str());
5963 frec->files[iname] = other;
5964 return true;
5965 }
5966 else
5967 {
5968 //## Ok, it was not found directly
5969 //look in other dirs
5970 std::vector<String>::iterator diter;
5971 for (diter=directories.begin() ;
5972 diter!=directories.end() ; diter++)
5973 {
5974 String dfname = *diter;
5975 dfname.append("/");
5976 dfname.append(iname);
5977 URI fullPathURI(dfname); //normalize path name
5978 String fullPath = fullPathURI.getPath();
5979 if (fullPath[0] == '/')
5980 fullPath = fullPath.substr(1);
5981 //trace("Normalized %s to %s", dfname.c_str(), fullPath.c_str());
5982 iter = allFiles.find(fullPath);
5983 if (iter != allFiles.end())
5984 {
5985 FileRec *other = iter->second;
5986 //trace("other: '%s'", iname.c_str());
5987 frec->files[fullPath] = other;
5988 return true;
5989 }
5990 }
5991 }
5992 return true;
5993 }
5997 /**
5998 * Lightly parse a file to find the #include directives. Do
5999 * a bit of state machine stuff to make sure that the directive
6000 * is valid. (Like not in a comment).
6001 */
6002 bool DepTool::scanFile(const String &fname, FileRec *frec)
6003 {
6004 String fileName;
6005 if (sourceDir.size() > 0)
6006 {
6007 fileName.append(sourceDir);
6008 fileName.append("/");
6009 }
6010 fileName.append(fname);
6011 String nativeName = getNativePath(fileName);
6012 FILE *f = fopen(nativeName.c_str(), "r");
6013 if (!f)
6014 {
6015 error("Could not open '%s' for reading", fname.c_str());
6016 return false;
6017 }
6018 String buf;
6019 while (!feof(f))
6020 {
6021 int nrbytes = fread(readBuf, 1, readBufSize, f);
6022 readBuf[nrbytes] = '\0';
6023 buf.append(readBuf);
6024 }
6025 fclose(f);
6027 depFileSize = buf.size();
6028 depFileBuf = (char *)buf.c_str();
6029 int pos = 0;
6032 while (pos < depFileSize)
6033 {
6034 //trace("p:%c", get(pos));
6036 //# Block comment
6037 if (get(pos) == '/' && get(pos+1) == '*')
6038 {
6039 pos += 2;
6040 while (pos < depFileSize)
6041 {
6042 if (get(pos) == '*' && get(pos+1) == '/')
6043 {
6044 pos += 2;
6045 break;
6046 }
6047 else
6048 pos++;
6049 }
6050 }
6051 //# Line comment
6052 else if (get(pos) == '/' && get(pos+1) == '/')
6053 {
6054 pos += 2;
6055 while (pos < depFileSize)
6056 {
6057 if (get(pos) == '\n')
6058 {
6059 pos++;
6060 break;
6061 }
6062 else
6063 pos++;
6064 }
6065 }
6066 //# #include! yaay
6067 else if (sequ(pos, "#include"))
6068 {
6069 pos += 8;
6070 pos = skipwhite(pos);
6071 String iname;
6072 pos = getword(pos, iname);
6073 if (iname.size()>2)
6074 {
6075 iname = iname.substr(1, iname.size()-2);
6076 addIncludeFile(frec, iname);
6077 }
6078 }
6079 else
6080 {
6081 pos++;
6082 }
6083 }
6085 return true;
6086 }
6090 /**
6091 * Recursively check include lists to find all files in allFiles to which
6092 * a given file is dependent.
6093 */
6094 bool DepTool::processDependency(FileRec *ofile, FileRec *include)
6095 {
6096 std::map<String, FileRec *>::iterator iter;
6097 for (iter=include->files.begin() ; iter!=include->files.end() ; iter++)
6098 {
6099 String fname = iter->first;
6100 if (ofile->files.find(fname) != ofile->files.end())
6101 {
6102 //trace("file '%s' already seen", fname.c_str());
6103 continue;
6104 }
6105 FileRec *child = iter->second;
6106 ofile->files[fname] = child;
6108 processDependency(ofile, child);
6109 }
6112 return true;
6113 }
6119 /**
6120 * Generate the file dependency list.
6121 */
6122 bool DepTool::generateDependencies()
6123 {
6124 std::map<String, FileRec *>::iterator iter;
6125 //# First pass. Scan for all includes
6126 for (iter=allFiles.begin() ; iter!=allFiles.end() ; iter++)
6127 {
6128 FileRec *frec = iter->second;
6129 if (!scanFile(iter->first, frec))
6130 {
6131 //quit?
6132 }
6133 }
6135 //# Second pass. Scan for all includes
6136 for (iter=allFiles.begin() ; iter!=allFiles.end() ; iter++)
6137 {
6138 FileRec *include = iter->second;
6139 if (include->type == FileRec::CFILE)
6140 {
6141 //String cFileName = iter->first;
6142 FileRec *ofile = new FileRec(FileRec::OFILE);
6143 ofile->path = include->path;
6144 ofile->baseName = include->baseName;
6145 ofile->suffix = include->suffix;
6146 String fname = include->path;
6147 if (fname.size()>0)
6148 fname.append("/");
6149 fname.append(include->baseName);
6150 fname.append(".o");
6151 oFiles[fname] = ofile;
6152 //add the .c file first? no, don't
6153 //ofile->files[cFileName] = include;
6155 //trace("ofile:%s", fname.c_str());
6157 processDependency(ofile, include);
6158 }
6159 }
6162 return true;
6163 }
6167 /**
6168 * High-level call to generate deps and optionally save them
6169 */
6170 bool DepTool::generateDependencies(const String &fileName)
6171 {
6172 if (!createFileList())
6173 return false;
6174 if (!generateDependencies())
6175 return false;
6176 if (!saveDepFile(fileName))
6177 return false;
6178 return true;
6179 }
6182 /**
6183 * This saves the dependency cache.
6184 */
6185 bool DepTool::saveDepFile(const String &fileName)
6186 {
6187 time_t tim;
6188 time(&tim);
6190 FILE *f = fopen(fileName.c_str(), "w");
6191 if (!f)
6192 {
6193 trace("cannot open '%s' for writing", fileName.c_str());
6194 }
6195 fprintf(f, "<?xml version='1.0'?>\n");
6196 fprintf(f, "<!--\n");
6197 fprintf(f, "########################################################\n");
6198 fprintf(f, "## File: build.dep\n");
6199 fprintf(f, "## Generated by BuildTool at :%s", ctime(&tim));
6200 fprintf(f, "########################################################\n");
6201 fprintf(f, "-->\n");
6203 fprintf(f, "<dependencies source='%s'>\n\n", sourceDir.c_str());
6204 std::map<String, FileRec *>::iterator iter;
6205 for (iter=oFiles.begin() ; iter!=oFiles.end() ; iter++)
6206 {
6207 FileRec *frec = iter->second;
6208 if (frec->type == FileRec::OFILE)
6209 {
6210 fprintf(f, "<object path='%s' name='%s' suffix='%s'>\n",
6211 frec->path.c_str(), frec->baseName.c_str(), frec->suffix.c_str());
6212 std::map<String, FileRec *>::iterator citer;
6213 for (citer=frec->files.begin() ; citer!=frec->files.end() ; citer++)
6214 {
6215 String cfname = citer->first;
6216 fprintf(f, " <dep name='%s'/>\n", cfname.c_str());
6217 }
6218 fprintf(f, "</object>\n\n");
6219 }
6220 }
6222 fprintf(f, "</dependencies>\n");
6223 fprintf(f, "\n");
6224 fprintf(f, "<!--\n");
6225 fprintf(f, "########################################################\n");
6226 fprintf(f, "## E N D\n");
6227 fprintf(f, "########################################################\n");
6228 fprintf(f, "-->\n");
6230 fclose(f);
6232 return true;
6233 }
6238 /**
6239 * This loads the dependency cache.
6240 */
6241 std::vector<DepRec> DepTool::loadDepFile(const String &depFile)
6242 {
6243 std::vector<DepRec> result;
6245 Parser parser;
6246 Element *root = parser.parseFile(depFile.c_str());
6247 if (!root)
6248 {
6249 //error("Could not open %s for reading", depFile.c_str());
6250 return result;
6251 }
6253 if (root->getChildren().size()==0 ||
6254 root->getChildren()[0]->getName()!="dependencies")
6255 {
6256 error("loadDepFile: main xml element should be <dependencies>");
6257 delete root;
6258 return result;
6259 }
6261 //########## Start parsing
6262 Element *depList = root->getChildren()[0];
6264 std::vector<Element *> objects = depList->getChildren();
6265 for (unsigned int i=0 ; i<objects.size() ; i++)
6266 {
6267 Element *objectElem = objects[i];
6268 String tagName = objectElem->getName();
6269 if (tagName != "object")
6270 {
6271 error("loadDepFile: <dependencies> should have only <object> children");
6272 return result;
6273 }
6275 String objName = objectElem->getAttribute("name");
6276 //trace("object:%s", objName.c_str());
6277 DepRec depObject(objName);
6278 depObject.path = objectElem->getAttribute("path");
6279 depObject.suffix = objectElem->getAttribute("suffix");
6280 //########## DESCRIPTION
6281 std::vector<Element *> depElems = objectElem->getChildren();
6282 for (unsigned int i=0 ; i<depElems.size() ; i++)
6283 {
6284 Element *depElem = depElems[i];
6285 tagName = depElem->getName();
6286 if (tagName != "dep")
6287 {
6288 error("loadDepFile: <object> should have only <dep> children");
6289 return result;
6290 }
6291 String depName = depElem->getAttribute("name");
6292 //trace(" dep:%s", depName.c_str());
6293 depObject.files.push_back(depName);
6294 }
6296 //Insert into the result list, in a sorted manner
6297 bool inserted = false;
6298 std::vector<DepRec>::iterator iter;
6299 for (iter = result.begin() ; iter != result.end() ; iter++)
6300 {
6301 String vpath = iter->path;
6302 vpath.append("/");
6303 vpath.append(iter->name);
6304 String opath = depObject.path;
6305 opath.append("/");
6306 opath.append(depObject.name);
6307 if (vpath > opath)
6308 {
6309 inserted = true;
6310 iter = result.insert(iter, depObject);
6311 break;
6312 }
6313 }
6314 if (!inserted)
6315 result.push_back(depObject);
6316 }
6318 delete root;
6320 return result;
6321 }
6324 /**
6325 * This loads the dependency cache.
6326 */
6327 std::vector<DepRec> DepTool::getDepFile(const String &depFile,
6328 bool forceRefresh)
6329 {
6330 std::vector<DepRec> result;
6331 if (forceRefresh)
6332 {
6333 generateDependencies(depFile);
6334 result = loadDepFile(depFile);
6335 }
6336 else
6337 {
6338 //try once
6339 result = loadDepFile(depFile);
6340 if (result.size() == 0)
6341 {
6342 //fail? try again
6343 generateDependencies(depFile);
6344 result = loadDepFile(depFile);
6345 }
6346 }
6347 return result;
6348 }
6353 //########################################################################
6354 //# T A S K
6355 //########################################################################
6356 //forward decl
6357 class Target;
6358 class Make;
6360 /**
6361 *
6362 */
6363 class Task : public MakeBase
6364 {
6366 public:
6368 typedef enum
6369 {
6370 TASK_NONE,
6371 TASK_CC,
6372 TASK_COPY,
6373 TASK_DELETE,
6374 TASK_ECHO,
6375 TASK_JAR,
6376 TASK_JAVAC,
6377 TASK_LINK,
6378 TASK_MAKEFILE,
6379 TASK_MKDIR,
6380 TASK_MSGFMT,
6381 TASK_PKG_CONFIG,
6382 TASK_RANLIB,
6383 TASK_RC,
6384 TASK_SHAREDLIB,
6385 TASK_STATICLIB,
6386 TASK_STRIP,
6387 TASK_TOUCH,
6388 TASK_TSTAMP
6389 } TaskType;
6392 /**
6393 *
6394 */
6395 Task(MakeBase &par) : parent(par)
6396 { init(); }
6398 /**
6399 *
6400 */
6401 Task(const Task &other) : parent(other.parent)
6402 { init(); assign(other); }
6404 /**
6405 *
6406 */
6407 Task &operator=(const Task &other)
6408 { assign(other); return *this; }
6410 /**
6411 *
6412 */
6413 virtual ~Task()
6414 { }
6417 /**
6418 *
6419 */
6420 virtual MakeBase &getParent()
6421 { return parent; }
6423 /**
6424 *
6425 */
6426 virtual int getType()
6427 { return type; }
6429 /**
6430 *
6431 */
6432 virtual void setType(int val)
6433 { type = val; }
6435 /**
6436 *
6437 */
6438 virtual String getName()
6439 { return name; }
6441 /**
6442 *
6443 */
6444 virtual bool execute()
6445 { return true; }
6447 /**
6448 *
6449 */
6450 virtual bool parse(Element *elem)
6451 { return true; }
6453 /**
6454 *
6455 */
6456 Task *createTask(Element *elem, int lineNr);
6459 protected:
6461 void init()
6462 {
6463 type = TASK_NONE;
6464 name = "none";
6465 }
6467 void assign(const Task &other)
6468 {
6469 type = other.type;
6470 name = other.name;
6471 }
6473 /**
6474 * Show task status
6475 */
6476 void taskstatus(const char *fmt, ...)
6477 {
6478 va_list args;
6479 va_start(args,fmt);
6480 fprintf(stdout, " %s : ", name.c_str());
6481 vfprintf(stdout, fmt, args);
6482 fprintf(stdout, "\n");
6483 va_end(args) ;
6484 }
6486 String getAttribute(Element *elem, const String &attrName)
6487 {
6488 String str;
6489 return str;
6490 }
6492 MakeBase &parent;
6494 int type;
6496 String name;
6497 };
6501 /**
6502 * This task runs the C/C++ compiler. The compiler is invoked
6503 * for all .c or .cpp files which are newer than their correcsponding
6504 * .o files.
6505 */
6506 class TaskCC : public Task
6507 {
6508 public:
6510 TaskCC(MakeBase &par) : Task(par)
6511 {
6512 type = TASK_CC;
6513 name = "cc";
6514 }
6516 virtual ~TaskCC()
6517 {}
6519 virtual bool isExcludedInc(const String &dirname)
6520 {
6521 for (unsigned int i=0 ; i<excludeInc.size() ; i++)
6522 {
6523 String fname = excludeInc[i];
6524 if (fname == dirname)
6525 return true;
6526 }
6527 return false;
6528 }
6530 virtual bool execute()
6531 {
6532 //evaluate our parameters
6533 String command = parent.eval(commandOpt, "gcc");
6534 String ccCommand = parent.eval(ccCommandOpt, "gcc");
6535 String cxxCommand = parent.eval(cxxCommandOpt, "g++");
6536 String source = parent.eval(sourceOpt, ".");
6537 String dest = parent.eval(destOpt, ".");
6538 String flags = parent.eval(flagsOpt, "");
6539 String defines = parent.eval(definesOpt, "");
6540 String includes = parent.eval(includesOpt, "");
6541 bool continueOnError = parent.evalBool(continueOnErrorOpt, true);
6542 bool refreshCache = parent.evalBool(refreshCacheOpt, false);
6544 if (!listFiles(parent, fileSet))
6545 return false;
6547 FILE *f = NULL;
6548 f = fopen("compile.lst", "w");
6550 //refreshCache is probably false here, unless specified otherwise
6551 String fullName = parent.resolve("build.dep");
6552 if (refreshCache || isNewerThan(parent.getURI().getPath(), fullName))
6553 {
6554 taskstatus("regenerating C/C++ dependency cache");
6555 refreshCache = true;
6556 }
6558 DepTool depTool;
6559 depTool.setSourceDirectory(source);
6560 depTool.setFileList(fileSet.getFiles());
6561 std::vector<DepRec> deps =
6562 depTool.getDepFile("build.dep", refreshCache);
6564 String incs;
6565 incs.append("-I");
6566 incs.append(parent.resolve("."));
6567 incs.append(" ");
6568 if (includes.size()>0)
6569 {
6570 incs.append(includes);
6571 incs.append(" ");
6572 }
6573 std::set<String> paths;
6574 std::vector<DepRec>::iterator viter;
6575 for (viter=deps.begin() ; viter!=deps.end() ; viter++)
6576 {
6577 DepRec dep = *viter;
6578 if (dep.path.size()>0)
6579 paths.insert(dep.path);
6580 }
6581 if (source.size()>0)
6582 {
6583 incs.append(" -I");
6584 incs.append(parent.resolve(source));
6585 incs.append(" ");
6586 }
6587 std::set<String>::iterator setIter;
6588 for (setIter=paths.begin() ; setIter!=paths.end() ; setIter++)
6589 {
6590 String dirName = *setIter;
6591 //check excludeInc to see if we dont want to include this dir
6592 if (isExcludedInc(dirName))
6593 continue;
6594 incs.append(" -I");
6595 String dname;
6596 if (source.size()>0)
6597 {
6598 dname.append(source);
6599 dname.append("/");
6600 }
6601 dname.append(dirName);
6602 incs.append(parent.resolve(dname));
6603 }
6605 /**
6606 * Compile each of the C files that need it
6607 */
6608 bool errorOccurred = false;
6609 std::vector<String> cfiles;
6610 for (viter=deps.begin() ; viter!=deps.end() ; viter++)
6611 {
6612 DepRec dep = *viter;
6614 //## Select command
6615 String sfx = dep.suffix;
6616 String command = ccCommand;
6617 if (sfx == "cpp" || sfx == "cxx" || sfx == "c++" ||
6618 sfx == "cc" || sfx == "CC")
6619 command = cxxCommand;
6621 //## Make paths
6622 String destPath = dest;
6623 String srcPath = source;
6624 if (dep.path.size()>0)
6625 {
6626 destPath.append("/");
6627 destPath.append(dep.path);
6628 srcPath.append("/");
6629 srcPath.append(dep.path);
6630 }
6631 //## Make sure destination directory exists
6632 if (!createDirectory(destPath))
6633 return false;
6635 //## Check whether it needs to be done
6636 String destName;
6637 if (destPath.size()>0)
6638 {
6639 destName.append(destPath);
6640 destName.append("/");
6641 }
6642 destName.append(dep.name);
6643 destName.append(".o");
6644 String destFullName = parent.resolve(destName);
6645 String srcName;
6646 if (srcPath.size()>0)
6647 {
6648 srcName.append(srcPath);
6649 srcName.append("/");
6650 }
6651 srcName.append(dep.name);
6652 srcName.append(".");
6653 srcName.append(dep.suffix);
6654 String srcFullName = parent.resolve(srcName);
6655 bool compileMe = false;
6656 //# First we check if the source is newer than the .o
6657 if (isNewerThan(srcFullName, destFullName))
6658 {
6659 taskstatus("compile of %s required by source: %s",
6660 destFullName.c_str(), srcFullName.c_str());
6661 compileMe = true;
6662 }
6663 else
6664 {
6665 //# secondly, we check if any of the included dependencies
6666 //# of the .c/.cpp is newer than the .o
6667 for (unsigned int i=0 ; i<dep.files.size() ; i++)
6668 {
6669 String depName;
6670 if (source.size()>0)
6671 {
6672 depName.append(source);
6673 depName.append("/");
6674 }
6675 depName.append(dep.files[i]);
6676 String depFullName = parent.resolve(depName);
6677 bool depRequires = isNewerThan(depFullName, destFullName);
6678 //trace("%d %s %s\n", depRequires,
6679 // destFullName.c_str(), depFullName.c_str());
6680 if (depRequires)
6681 {
6682 taskstatus("compile of %s required by included: %s",
6683 destFullName.c_str(), depFullName.c_str());
6684 compileMe = true;
6685 break;
6686 }
6687 }
6688 }
6689 if (!compileMe)
6690 {
6691 continue;
6692 }
6694 //## Assemble the command
6695 String cmd = command;
6696 cmd.append(" -c ");
6697 cmd.append(flags);
6698 cmd.append(" ");
6699 cmd.append(defines);
6700 cmd.append(" ");
6701 cmd.append(incs);
6702 cmd.append(" ");
6703 cmd.append(srcFullName);
6704 cmd.append(" -o ");
6705 cmd.append(destFullName);
6707 //## Execute the command
6709 String outString, errString;
6710 bool ret = executeCommand(cmd.c_str(), "", outString, errString);
6712 if (f)
6713 {
6714 fprintf(f, "########################### File : %s\n",
6715 srcFullName.c_str());
6716 fprintf(f, "#### COMMAND ###\n");
6717 int col = 0;
6718 for (unsigned int i = 0 ; i < cmd.size() ; i++)
6719 {
6720 char ch = cmd[i];
6721 if (isspace(ch) && col > 63)
6722 {
6723 fputc('\n', f);
6724 col = 0;
6725 }
6726 else
6727 {
6728 fputc(ch, f);
6729 col++;
6730 }
6731 if (col > 76)
6732 {
6733 fputc('\n', f);
6734 col = 0;
6735 }
6736 }
6737 fprintf(f, "\n");
6738 fprintf(f, "#### STDOUT ###\n%s\n", outString.c_str());
6739 fprintf(f, "#### STDERR ###\n%s\n\n", errString.c_str());
6740 fflush(f);
6741 }
6742 if (!ret)
6743 {
6744 error("problem compiling: %s", errString.c_str());
6745 errorOccurred = true;
6746 }
6747 if (errorOccurred && !continueOnError)
6748 break;
6749 }
6751 if (f)
6752 {
6753 fclose(f);
6754 }
6756 return !errorOccurred;
6757 }
6760 virtual bool parse(Element *elem)
6761 {
6762 String s;
6763 if (!parent.getAttribute(elem, "command", commandOpt))
6764 return false;
6765 if (commandOpt.size()>0)
6766 { cxxCommandOpt = ccCommandOpt = commandOpt; }
6767 if (!parent.getAttribute(elem, "cc", ccCommandOpt))
6768 return false;
6769 if (!parent.getAttribute(elem, "cxx", cxxCommandOpt))
6770 return false;
6771 if (!parent.getAttribute(elem, "destdir", destOpt))
6772 return false;
6773 if (!parent.getAttribute(elem, "continueOnError", continueOnErrorOpt))
6774 return false;
6775 if (!parent.getAttribute(elem, "refreshCache", refreshCacheOpt))
6776 return false;
6778 std::vector<Element *> children = elem->getChildren();
6779 for (unsigned int i=0 ; i<children.size() ; i++)
6780 {
6781 Element *child = children[i];
6782 String tagName = child->getName();
6783 if (tagName == "flags")
6784 {
6785 if (!parent.getValue(child, flagsOpt))
6786 return false;
6787 flagsOpt = strip(flagsOpt);
6788 }
6789 else if (tagName == "includes")
6790 {
6791 if (!parent.getValue(child, includesOpt))
6792 return false;
6793 includesOpt = strip(includesOpt);
6794 }
6795 else if (tagName == "defines")
6796 {
6797 if (!parent.getValue(child, definesOpt))
6798 return false;
6799 definesOpt = strip(definesOpt);
6800 }
6801 else if (tagName == "fileset")
6802 {
6803 if (!parseFileSet(child, parent, fileSet))
6804 return false;
6805 sourceOpt = fileSet.getDirectory();
6806 }
6807 else if (tagName == "excludeinc")
6808 {
6809 if (!parseFileList(child, parent, excludeInc))
6810 return false;
6811 }
6812 }
6814 return true;
6815 }
6817 protected:
6819 String commandOpt;
6820 String ccCommandOpt;
6821 String cxxCommandOpt;
6822 String sourceOpt;
6823 String destOpt;
6824 String flagsOpt;
6825 String definesOpt;
6826 String includesOpt;
6827 String continueOnErrorOpt;
6828 String refreshCacheOpt;
6829 FileSet fileSet;
6830 FileList excludeInc;
6832 };
6836 /**
6837 *
6838 */
6839 class TaskCopy : public Task
6840 {
6841 public:
6843 typedef enum
6844 {
6845 CP_NONE,
6846 CP_TOFILE,
6847 CP_TODIR
6848 } CopyType;
6850 TaskCopy(MakeBase &par) : Task(par)
6851 {
6852 type = TASK_COPY;
6853 name = "copy";
6854 cptype = CP_NONE;
6855 haveFileSet = false;
6856 }
6858 virtual ~TaskCopy()
6859 {}
6861 virtual bool execute()
6862 {
6863 String fileName = parent.eval(fileNameOpt , ".");
6864 String toFileName = parent.eval(toFileNameOpt , ".");
6865 String toDirName = parent.eval(toDirNameOpt , ".");
6866 bool verbose = parent.evalBool(verboseOpt, false);
6867 switch (cptype)
6868 {
6869 case CP_TOFILE:
6870 {
6871 if (fileName.size()>0)
6872 {
6873 taskstatus("%s to %s",
6874 fileName.c_str(), toFileName.c_str());
6875 String fullSource = parent.resolve(fileName);
6876 String fullDest = parent.resolve(toFileName);
6877 if (verbose)
6878 taskstatus("copy %s to file %s", fullSource.c_str(),
6879 fullDest.c_str());
6880 if (!isRegularFile(fullSource))
6881 {
6882 error("copy : file %s does not exist", fullSource.c_str());
6883 return false;
6884 }
6885 if (!isNewerThan(fullSource, fullDest))
6886 {
6887 taskstatus("skipped");
6888 return true;
6889 }
6890 if (!copyFile(fullSource, fullDest))
6891 return false;
6892 taskstatus("1 file copied");
6893 }
6894 return true;
6895 }
6896 case CP_TODIR:
6897 {
6898 if (haveFileSet)
6899 {
6900 if (!listFiles(parent, fileSet))
6901 return false;
6902 String fileSetDir = parent.eval(fileSet.getDirectory(), ".");
6904 taskstatus("%s to %s",
6905 fileSetDir.c_str(), toDirName.c_str());
6907 int nrFiles = 0;
6908 for (unsigned int i=0 ; i<fileSet.size() ; i++)
6909 {
6910 String fileName = fileSet[i];
6912 String sourcePath;
6913 if (fileSetDir.size()>0)
6914 {
6915 sourcePath.append(fileSetDir);
6916 sourcePath.append("/");
6917 }
6918 sourcePath.append(fileName);
6919 String fullSource = parent.resolve(sourcePath);
6921 //Get the immediate parent directory's base name
6922 String baseFileSetDir = fileSetDir;
6923 unsigned int pos = baseFileSetDir.find_last_of('/');
6924 if (pos!=baseFileSetDir.npos &&
6925 pos < baseFileSetDir.size()-1)
6926 baseFileSetDir =
6927 baseFileSetDir.substr(pos+1,
6928 baseFileSetDir.size());
6929 //Now make the new path
6930 String destPath;
6931 if (toDirName.size()>0)
6932 {
6933 destPath.append(toDirName);
6934 destPath.append("/");
6935 }
6936 if (baseFileSetDir.size()>0)
6937 {
6938 destPath.append(baseFileSetDir);
6939 destPath.append("/");
6940 }
6941 destPath.append(fileName);
6942 String fullDest = parent.resolve(destPath);
6943 //trace("fileName:%s", fileName.c_str());
6944 if (verbose)
6945 taskstatus("copy %s to new dir : %s",
6946 fullSource.c_str(), fullDest.c_str());
6947 if (!isNewerThan(fullSource, fullDest))
6948 {
6949 if (verbose)
6950 taskstatus("copy skipping %s", fullSource.c_str());
6951 continue;
6952 }
6953 if (!copyFile(fullSource, fullDest))
6954 return false;
6955 nrFiles++;
6956 }
6957 taskstatus("%d file(s) copied", nrFiles);
6958 }
6959 else //file source
6960 {
6961 //For file->dir we want only the basename of
6962 //the source appended to the dest dir
6963 taskstatus("%s to %s",
6964 fileName.c_str(), toDirName.c_str());
6965 String baseName = fileName;
6966 unsigned int pos = baseName.find_last_of('/');
6967 if (pos!=baseName.npos && pos<baseName.size()-1)
6968 baseName = baseName.substr(pos+1, baseName.size());
6969 String fullSource = parent.resolve(fileName);
6970 String destPath;
6971 if (toDirName.size()>0)
6972 {
6973 destPath.append(toDirName);
6974 destPath.append("/");
6975 }
6976 destPath.append(baseName);
6977 String fullDest = parent.resolve(destPath);
6978 if (verbose)
6979 taskstatus("file %s to new dir : %s", fullSource.c_str(),
6980 fullDest.c_str());
6981 if (!isRegularFile(fullSource))
6982 {
6983 error("copy : file %s does not exist", fullSource.c_str());
6984 return false;
6985 }
6986 if (!isNewerThan(fullSource, fullDest))
6987 {
6988 taskstatus("skipped");
6989 return true;
6990 }
6991 if (!copyFile(fullSource, fullDest))
6992 return false;
6993 taskstatus("1 file copied");
6994 }
6995 return true;
6996 }
6997 }
6998 return true;
6999 }
7002 virtual bool parse(Element *elem)
7003 {
7004 if (!parent.getAttribute(elem, "file", fileNameOpt))
7005 return false;
7006 if (!parent.getAttribute(elem, "tofile", toFileNameOpt))
7007 return false;
7008 if (toFileNameOpt.size() > 0)
7009 cptype = CP_TOFILE;
7010 if (!parent.getAttribute(elem, "todir", toDirNameOpt))
7011 return false;
7012 if (toDirNameOpt.size() > 0)
7013 cptype = CP_TODIR;
7014 if (!parent.getAttribute(elem, "verbose", verboseOpt))
7015 return false;
7017 haveFileSet = false;
7019 std::vector<Element *> children = elem->getChildren();
7020 for (unsigned int i=0 ; i<children.size() ; i++)
7021 {
7022 Element *child = children[i];
7023 String tagName = child->getName();
7024 if (tagName == "fileset")
7025 {
7026 if (!parseFileSet(child, parent, fileSet))
7027 {
7028 error("problem getting fileset");
7029 return false;
7030 }
7031 haveFileSet = true;
7032 }
7033 }
7035 //Perform validity checks
7036 if (fileNameOpt.size()>0 && fileSet.size()>0)
7037 {
7038 error("<copy> can only have one of : file= and <fileset>");
7039 return false;
7040 }
7041 if (toFileNameOpt.size()>0 && toDirNameOpt.size()>0)
7042 {
7043 error("<copy> can only have one of : tofile= or todir=");
7044 return false;
7045 }
7046 if (haveFileSet && toDirNameOpt.size()==0)
7047 {
7048 error("a <copy> task with a <fileset> must have : todir=");
7049 return false;
7050 }
7051 if (cptype == CP_TOFILE && fileNameOpt.size()==0)
7052 {
7053 error("<copy> tofile= must be associated with : file=");
7054 return false;
7055 }
7056 if (cptype == CP_TODIR && fileNameOpt.size()==0 && !haveFileSet)
7057 {
7058 error("<copy> todir= must be associated with : file= or <fileset>");
7059 return false;
7060 }
7062 return true;
7063 }
7065 private:
7067 int cptype;
7068 bool haveFileSet;
7070 FileSet fileSet;
7071 String fileNameOpt;
7072 String toFileNameOpt;
7073 String toDirNameOpt;
7074 String verboseOpt;
7075 };
7078 /**
7079 *
7080 */
7081 class TaskDelete : public Task
7082 {
7083 public:
7085 typedef enum
7086 {
7087 DEL_FILE,
7088 DEL_DIR,
7089 DEL_FILESET
7090 } DeleteType;
7092 TaskDelete(MakeBase &par) : Task(par)
7093 {
7094 type = TASK_DELETE;
7095 name = "delete";
7096 delType = DEL_FILE;
7097 }
7099 virtual ~TaskDelete()
7100 {}
7102 virtual bool execute()
7103 {
7104 String dirName = parent.eval(dirNameOpt, ".");
7105 String fileName = parent.eval(fileNameOpt, ".");
7106 bool verbose = parent.evalBool(verboseOpt, false);
7107 bool quiet = parent.evalBool(quietOpt, false);
7108 bool failOnError = parent.evalBool(failOnErrorOpt, true);
7109 struct stat finfo;
7110 switch (delType)
7111 {
7112 case DEL_FILE:
7113 {
7114 taskstatus("file: %s", fileName.c_str());
7115 String fullName = parent.resolve(fileName);
7116 char *fname = (char *)fullName.c_str();
7117 if (!quiet && verbose)
7118 taskstatus("path: %s", fname);
7119 //does not exist
7120 if (stat(fname, &finfo)<0)
7121 {
7122 if (failOnError)
7123 return false;
7124 else
7125 return true;
7126 }
7127 //exists but is not a regular file
7128 if (!S_ISREG(finfo.st_mode))
7129 {
7130 error("<delete> failed. '%s' exists and is not a regular file",
7131 fname);
7132 return false;
7133 }
7134 if (remove(fname)<0)
7135 {
7136 error("<delete> failed: %s", strerror(errno));
7137 return false;
7138 }
7139 return true;
7140 }
7141 case DEL_DIR:
7142 {
7143 taskstatus("dir: %s", dirName.c_str());
7144 String fullDir = parent.resolve(dirName);
7145 if (!quiet && verbose)
7146 taskstatus("path: %s", fullDir.c_str());
7147 if (!removeDirectory(fullDir))
7148 return false;
7149 return true;
7150 }
7151 }
7152 return true;
7153 }
7155 virtual bool parse(Element *elem)
7156 {
7157 if (!parent.getAttribute(elem, "file", fileNameOpt))
7158 return false;
7159 if (fileNameOpt.size() > 0)
7160 delType = DEL_FILE;
7161 if (!parent.getAttribute(elem, "dir", dirNameOpt))
7162 return false;
7163 if (dirNameOpt.size() > 0)
7164 delType = DEL_DIR;
7165 if (fileNameOpt.size()>0 && dirNameOpt.size()>0)
7166 {
7167 error("<delete> can have one attribute of file= or dir=");
7168 return false;
7169 }
7170 if (fileNameOpt.size()==0 && dirNameOpt.size()==0)
7171 {
7172 error("<delete> must have one attribute of file= or dir=");
7173 return false;
7174 }
7175 if (!parent.getAttribute(elem, "verbose", verboseOpt))
7176 return false;
7177 if (!parent.getAttribute(elem, "quiet", quietOpt))
7178 return false;
7179 if (!parent.getAttribute(elem, "failonerror", failOnErrorOpt))
7180 return false;
7181 return true;
7182 }
7184 private:
7186 int delType;
7187 String dirNameOpt;
7188 String fileNameOpt;
7189 String verboseOpt;
7190 String quietOpt;
7191 String failOnErrorOpt;
7192 };
7195 /**
7196 * Send a message to stdout
7197 */
7198 class TaskEcho : public Task
7199 {
7200 public:
7202 TaskEcho(MakeBase &par) : Task(par)
7203 { type = TASK_ECHO; name = "echo"; }
7205 virtual ~TaskEcho()
7206 {}
7208 virtual bool execute()
7209 {
7210 //let message have priority over text
7211 String message = parent.eval(messageOpt, "");
7212 String text = parent.eval(textOpt, "");
7213 if (message.size() > 0)
7214 {
7215 fprintf(stdout, "%s\n", message.c_str());
7216 }
7217 else if (text.size() > 0)
7218 {
7219 fprintf(stdout, "%s\n", text.c_str());
7220 }
7221 return true;
7222 }
7224 virtual bool parse(Element *elem)
7225 {
7226 if (!parent.getValue(elem, textOpt))
7227 return false;
7228 textOpt = leftJustify(textOpt);
7229 if (!parent.getAttribute(elem, "message", messageOpt))
7230 return false;
7231 return true;
7232 }
7234 private:
7236 String messageOpt;
7237 String textOpt;
7238 };
7242 /**
7243 *
7244 */
7245 class TaskJar : public Task
7246 {
7247 public:
7249 TaskJar(MakeBase &par) : Task(par)
7250 { type = TASK_JAR; name = "jar"; }
7252 virtual ~TaskJar()
7253 {}
7255 virtual bool execute()
7256 {
7257 String command = parent.eval(commandOpt, "jar");
7258 String basedir = parent.eval(basedirOpt, ".");
7259 String destfile = parent.eval(destfileOpt, ".");
7261 String cmd = command;
7262 cmd.append(" -cf ");
7263 cmd.append(destfile);
7264 cmd.append(" -C ");
7265 cmd.append(basedir);
7266 cmd.append(" .");
7268 String execCmd = cmd;
7270 String outString, errString;
7271 bool ret = executeCommand(execCmd.c_str(), "", outString, errString);
7272 if (!ret)
7273 {
7274 error("<jar> command '%s' failed :\n %s",
7275 execCmd.c_str(), errString.c_str());
7276 return false;
7277 }
7278 return true;
7279 }
7281 virtual bool parse(Element *elem)
7282 {
7283 if (!parent.getAttribute(elem, "command", commandOpt))
7284 return false;
7285 if (!parent.getAttribute(elem, "basedir", basedirOpt))
7286 return false;
7287 if (!parent.getAttribute(elem, "destfile", destfileOpt))
7288 return false;
7289 if (basedirOpt.size() == 0 || destfileOpt.size() == 0)
7290 {
7291 error("<jar> required both basedir and destfile attributes to be set");
7292 return false;
7293 }
7294 return true;
7295 }
7297 private:
7299 String commandOpt;
7300 String basedirOpt;
7301 String destfileOpt;
7302 };
7305 /**
7306 *
7307 */
7308 class TaskJavac : public Task
7309 {
7310 public:
7312 TaskJavac(MakeBase &par) : Task(par)
7313 {
7314 type = TASK_JAVAC; name = "javac";
7315 }
7317 virtual ~TaskJavac()
7318 {}
7320 virtual bool execute()
7321 {
7322 String command = parent.eval(commandOpt, "javac");
7323 String srcdir = parent.eval(srcdirOpt, ".");
7324 String destdir = parent.eval(destdirOpt, ".");
7325 String target = parent.eval(targetOpt, "");
7327 std::vector<String> fileList;
7328 if (!listFiles(srcdir, "", fileList))
7329 {
7330 return false;
7331 }
7332 String cmd = command;
7333 cmd.append(" -d ");
7334 cmd.append(destdir);
7335 cmd.append(" -classpath ");
7336 cmd.append(destdir);
7337 cmd.append(" -sourcepath ");
7338 cmd.append(srcdir);
7339 cmd.append(" ");
7340 if (target.size()>0)
7341 {
7342 cmd.append(" -target ");
7343 cmd.append(target);
7344 cmd.append(" ");
7345 }
7346 String fname = "javalist.btool";
7347 FILE *f = fopen(fname.c_str(), "w");
7348 int count = 0;
7349 for (unsigned int i=0 ; i<fileList.size() ; i++)
7350 {
7351 String fname = fileList[i];
7352 String srcName = fname;
7353 if (fname.size()<6) //x.java
7354 continue;
7355 if (fname.compare(fname.size()-5, 5, ".java") != 0)
7356 continue;
7357 String baseName = fname.substr(0, fname.size()-5);
7358 String destName = baseName;
7359 destName.append(".class");
7361 String fullSrc = srcdir;
7362 fullSrc.append("/");
7363 fullSrc.append(fname);
7364 String fullDest = destdir;
7365 fullDest.append("/");
7366 fullDest.append(destName);
7367 //trace("fullsrc:%s fulldest:%s", fullSrc.c_str(), fullDest.c_str());
7368 if (!isNewerThan(fullSrc, fullDest))
7369 continue;
7371 count++;
7372 fprintf(f, "%s\n", fullSrc.c_str());
7373 }
7374 fclose(f);
7375 if (!count)
7376 {
7377 taskstatus("nothing to do");
7378 return true;
7379 }
7381 taskstatus("compiling %d files", count);
7383 String execCmd = cmd;
7384 execCmd.append("@");
7385 execCmd.append(fname);
7387 String outString, errString;
7388 bool ret = executeCommand(execCmd.c_str(), "", outString, errString);
7389 if (!ret)
7390 {
7391 error("<javac> command '%s' failed :\n %s",
7392 execCmd.c_str(), errString.c_str());
7393 return false;
7394 }
7395 return true;
7396 }
7398 virtual bool parse(Element *elem)
7399 {
7400 if (!parent.getAttribute(elem, "command", commandOpt))
7401 return false;
7402 if (!parent.getAttribute(elem, "srcdir", srcdirOpt))
7403 return false;
7404 if (!parent.getAttribute(elem, "destdir", destdirOpt))
7405 return false;
7406 if (srcdirOpt.size() == 0 || destdirOpt.size() == 0)
7407 {
7408 error("<javac> required both srcdir and destdir attributes to be set");
7409 return false;
7410 }
7411 if (!parent.getAttribute(elem, "target", targetOpt))
7412 return false;
7413 return true;
7414 }
7416 private:
7418 String commandOpt;
7419 String srcdirOpt;
7420 String destdirOpt;
7421 String targetOpt;
7423 };
7426 /**
7427 *
7428 */
7429 class TaskLink : public Task
7430 {
7431 public:
7433 TaskLink(MakeBase &par) : Task(par)
7434 {
7435 type = TASK_LINK; name = "link";
7436 }
7438 virtual ~TaskLink()
7439 {}
7441 virtual bool execute()
7442 {
7443 String command = parent.eval(commandOpt, "g++");
7444 String fileName = parent.eval(fileNameOpt, "");
7445 String flags = parent.eval(flagsOpt, "");
7446 String libs = parent.eval(libsOpt, "");
7447 bool doStrip = parent.evalBool(doStripOpt, false);
7448 String symFileName = parent.eval(symFileNameOpt, "");
7449 String stripCommand = parent.eval(stripCommandOpt, "strip");
7450 String objcopyCommand = parent.eval(objcopyCommandOpt, "objcopy");
7452 if (!listFiles(parent, fileSet))
7453 return false;
7454 String fileSetDir = parent.eval(fileSet.getDirectory(), ".");
7455 //trace("%d files in %s", fileSet.size(), fileSetDir.c_str());
7456 bool doit = false;
7457 String fullTarget = parent.resolve(fileName);
7458 String cmd = command;
7459 cmd.append(" -o ");
7460 cmd.append(fullTarget);
7461 cmd.append(" ");
7462 cmd.append(flags);
7463 for (unsigned int i=0 ; i<fileSet.size() ; i++)
7464 {
7465 cmd.append(" ");
7466 String obj;
7467 if (fileSetDir.size()>0)
7468 {
7469 obj.append(fileSetDir);
7470 obj.append("/");
7471 }
7472 obj.append(fileSet[i]);
7473 String fullObj = parent.resolve(obj);
7474 String nativeFullObj = getNativePath(fullObj);
7475 cmd.append(nativeFullObj);
7476 //trace("link: tgt:%s obj:%s", fullTarget.c_str(),
7477 // fullObj.c_str());
7478 if (isNewerThan(fullObj, fullTarget))
7479 doit = true;
7480 }
7481 cmd.append(" ");
7482 cmd.append(libs);
7483 if (!doit)
7484 {
7485 //trace("link not needed");
7486 return true;
7487 }
7488 //trace("LINK cmd:%s", cmd.c_str());
7491 String outbuf, errbuf;
7492 if (!executeCommand(cmd.c_str(), "", outbuf, errbuf))
7493 {
7494 error("LINK problem: %s", errbuf.c_str());
7495 return false;
7496 }
7498 if (symFileName.size()>0)
7499 {
7500 String symFullName = parent.resolve(symFileName);
7501 cmd = objcopyCommand;
7502 cmd.append(" --only-keep-debug ");
7503 cmd.append(getNativePath(fullTarget));
7504 cmd.append(" ");
7505 cmd.append(getNativePath(symFullName));
7506 if (!executeCommand(cmd, "", outbuf, errbuf))
7507 {
7508 error("<strip> symbol file failed : %s", errbuf.c_str());
7509 return false;
7510 }
7511 }
7513 if (doStrip)
7514 {
7515 cmd = stripCommand;
7516 cmd.append(" ");
7517 cmd.append(getNativePath(fullTarget));
7518 if (!executeCommand(cmd, "", outbuf, errbuf))
7519 {
7520 error("<strip> failed : %s", errbuf.c_str());
7521 return false;
7522 }
7523 }
7525 return true;
7526 }
7528 virtual bool parse(Element *elem)
7529 {
7530 if (!parent.getAttribute(elem, "command", commandOpt))
7531 return false;
7532 if (!parent.getAttribute(elem, "objcopycommand", objcopyCommandOpt))
7533 return false;
7534 if (!parent.getAttribute(elem, "stripcommand", stripCommandOpt))
7535 return false;
7536 if (!parent.getAttribute(elem, "out", fileNameOpt))
7537 return false;
7538 if (!parent.getAttribute(elem, "strip", doStripOpt))
7539 return false;
7540 if (!parent.getAttribute(elem, "symfile", symFileNameOpt))
7541 return false;
7543 std::vector<Element *> children = elem->getChildren();
7544 for (unsigned int i=0 ; i<children.size() ; i++)
7545 {
7546 Element *child = children[i];
7547 String tagName = child->getName();
7548 if (tagName == "fileset")
7549 {
7550 if (!parseFileSet(child, parent, fileSet))
7551 return false;
7552 }
7553 else if (tagName == "flags")
7554 {
7555 if (!parent.getValue(child, flagsOpt))
7556 return false;
7557 flagsOpt = strip(flagsOpt);
7558 }
7559 else if (tagName == "libs")
7560 {
7561 if (!parent.getValue(child, libsOpt))
7562 return false;
7563 libsOpt = strip(libsOpt);
7564 }
7565 }
7566 return true;
7567 }
7569 private:
7571 FileSet fileSet;
7573 String commandOpt;
7574 String fileNameOpt;
7575 String flagsOpt;
7576 String libsOpt;
7577 String doStripOpt;
7578 String symFileNameOpt;
7579 String stripCommandOpt;
7580 String objcopyCommandOpt;
7582 };
7586 /**
7587 * Create a named file
7588 */
7589 class TaskMakeFile : public Task
7590 {
7591 public:
7593 TaskMakeFile(MakeBase &par) : Task(par)
7594 { type = TASK_MAKEFILE; name = "makefile"; }
7596 virtual ~TaskMakeFile()
7597 {}
7599 virtual bool execute()
7600 {
7601 String fileName = parent.eval(fileNameOpt, "");
7602 String text = parent.eval(textOpt, "");
7604 taskstatus("%s", fileName.c_str());
7605 String fullName = parent.resolve(fileName);
7606 if (!isNewerThan(parent.getURI().getPath(), fullName))
7607 {
7608 //trace("skipped <makefile>");
7609 return true;
7610 }
7611 String fullNative = getNativePath(fullName);
7612 //trace("fullName:%s", fullName.c_str());
7613 FILE *f = fopen(fullNative.c_str(), "w");
7614 if (!f)
7615 {
7616 error("<makefile> could not open %s for writing : %s",
7617 fullName.c_str(), strerror(errno));
7618 return false;
7619 }
7620 for (unsigned int i=0 ; i<text.size() ; i++)
7621 fputc(text[i], f);
7622 fputc('\n', f);
7623 fclose(f);
7624 return true;
7625 }
7627 virtual bool parse(Element *elem)
7628 {
7629 if (!parent.getAttribute(elem, "file", fileNameOpt))
7630 return false;
7631 if (fileNameOpt.size() == 0)
7632 {
7633 error("<makefile> requires 'file=\"filename\"' attribute");
7634 return false;
7635 }
7636 if (!parent.getValue(elem, textOpt))
7637 return false;
7638 textOpt = leftJustify(textOpt);
7639 //trace("dirname:%s", dirName.c_str());
7640 return true;
7641 }
7643 private:
7645 String fileNameOpt;
7646 String textOpt;
7647 };
7651 /**
7652 * Create a named directory
7653 */
7654 class TaskMkDir : public Task
7655 {
7656 public:
7658 TaskMkDir(MakeBase &par) : Task(par)
7659 { type = TASK_MKDIR; name = "mkdir"; }
7661 virtual ~TaskMkDir()
7662 {}
7664 virtual bool execute()
7665 {
7666 String dirName = parent.eval(dirNameOpt, ".");
7668 taskstatus("%s", dirName.c_str());
7669 String fullDir = parent.resolve(dirName);
7670 //trace("fullDir:%s", fullDir.c_str());
7671 if (!createDirectory(fullDir))
7672 return false;
7673 return true;
7674 }
7676 virtual bool parse(Element *elem)
7677 {
7678 if (!parent.getAttribute(elem, "dir", dirNameOpt))
7679 return false;
7680 if (dirNameOpt.size() == 0)
7681 {
7682 error("<mkdir> requires 'dir=\"dirname\"' attribute");
7683 return false;
7684 }
7685 return true;
7686 }
7688 private:
7690 String dirNameOpt;
7691 };
7695 /**
7696 * Create a named directory
7697 */
7698 class TaskMsgFmt: public Task
7699 {
7700 public:
7702 TaskMsgFmt(MakeBase &par) : Task(par)
7703 { type = TASK_MSGFMT; name = "msgfmt"; }
7705 virtual ~TaskMsgFmt()
7706 {}
7708 virtual bool execute()
7709 {
7710 String command = parent.eval(commandOpt, "msgfmt");
7711 String toDirName = parent.eval(toDirNameOpt, ".");
7712 String outName = parent.eval(outNameOpt, "");
7713 bool owndir = parent.evalBool(owndirOpt, false);
7715 if (!listFiles(parent, fileSet))
7716 return false;
7717 String fileSetDir = fileSet.getDirectory();
7719 //trace("msgfmt: %d", fileSet.size());
7720 for (unsigned int i=0 ; i<fileSet.size() ; i++)
7721 {
7722 String fileName = fileSet[i];
7723 if (getSuffix(fileName) != "po")
7724 continue;
7725 String sourcePath;
7726 if (fileSetDir.size()>0)
7727 {
7728 sourcePath.append(fileSetDir);
7729 sourcePath.append("/");
7730 }
7731 sourcePath.append(fileName);
7732 String fullSource = parent.resolve(sourcePath);
7734 String destPath;
7735 if (toDirName.size()>0)
7736 {
7737 destPath.append(toDirName);
7738 destPath.append("/");
7739 }
7740 if (owndir)
7741 {
7742 String subdir = fileName;
7743 unsigned int pos = subdir.find_last_of('.');
7744 if (pos != subdir.npos)
7745 subdir = subdir.substr(0, pos);
7746 destPath.append(subdir);
7747 destPath.append("/");
7748 }
7749 //Pick the output file name
7750 if (outName.size() > 0)
7751 {
7752 destPath.append(outName);
7753 }
7754 else
7755 {
7756 destPath.append(fileName);
7757 destPath[destPath.size()-2] = 'm';
7758 }
7760 String fullDest = parent.resolve(destPath);
7762 if (!isNewerThan(fullSource, fullDest))
7763 {
7764 //trace("skip %s", fullSource.c_str());
7765 continue;
7766 }
7768 String cmd = command;
7769 cmd.append(" ");
7770 cmd.append(fullSource);
7771 cmd.append(" -o ");
7772 cmd.append(fullDest);
7774 int pos = fullDest.find_last_of('/');
7775 if (pos>0)
7776 {
7777 String fullDestPath = fullDest.substr(0, pos);
7778 if (!createDirectory(fullDestPath))
7779 return false;
7780 }
7784 String outString, errString;
7785 if (!executeCommand(cmd.c_str(), "", outString, errString))
7786 {
7787 error("<msgfmt> problem: %s", errString.c_str());
7788 return false;
7789 }
7790 }
7792 return true;
7793 }
7795 virtual bool parse(Element *elem)
7796 {
7797 if (!parent.getAttribute(elem, "command", commandOpt))
7798 return false;
7799 if (!parent.getAttribute(elem, "todir", toDirNameOpt))
7800 return false;
7801 if (!parent.getAttribute(elem, "out", outNameOpt))
7802 return false;
7803 if (!parent.getAttribute(elem, "owndir", owndirOpt))
7804 return false;
7806 std::vector<Element *> children = elem->getChildren();
7807 for (unsigned int i=0 ; i<children.size() ; i++)
7808 {
7809 Element *child = children[i];
7810 String tagName = child->getName();
7811 if (tagName == "fileset")
7812 {
7813 if (!parseFileSet(child, parent, fileSet))
7814 return false;
7815 }
7816 }
7817 return true;
7818 }
7820 private:
7822 FileSet fileSet;
7824 String commandOpt;
7825 String toDirNameOpt;
7826 String outNameOpt;
7827 String owndirOpt;
7829 };
7833 /**
7834 * Perform a Package-Config query similar to pkg-config
7835 */
7836 class TaskPkgConfig : public Task
7837 {
7838 public:
7840 typedef enum
7841 {
7842 PKG_CONFIG_QUERY_CFLAGS,
7843 PKG_CONFIG_QUERY_LIBS,
7844 PKG_CONFIG_QUERY_ALL
7845 } QueryTypes;
7847 TaskPkgConfig(MakeBase &par) : Task(par)
7848 {
7849 type = TASK_PKG_CONFIG;
7850 name = "pkg-config";
7851 }
7853 virtual ~TaskPkgConfig()
7854 {}
7856 virtual bool execute()
7857 {
7858 String pkgName = parent.eval(pkgNameOpt, "");
7859 String prefix = parent.eval(prefixOpt, "");
7860 String propName = parent.eval(propNameOpt, "");
7861 String pkgConfigPath = parent.eval(pkgConfigPathOpt,"");
7862 String query = parent.eval(queryOpt, "all");
7864 String path = parent.resolve(pkgConfigPath);
7865 PkgConfig pkgconfig;
7866 pkgconfig.setPath(path);
7867 pkgconfig.setPrefix(prefix);
7868 if (!pkgconfig.query(pkgName))
7869 {
7870 error("<pkg-config> query failed for '%s", name.c_str());
7871 return false;
7872 }
7874 String val = "";
7875 if (query == "cflags")
7876 val = pkgconfig.getCflags();
7877 else if (query == "libs")
7878 val =pkgconfig.getLibs();
7879 else if (query == "all")
7880 val = pkgconfig.getAll();
7881 else
7882 {
7883 error("<pkg-config> unhandled query : %s", query.c_str());
7884 return false;
7885 }
7886 taskstatus("property %s = '%s'", propName.c_str(), val.c_str());
7887 parent.setProperty(propName, val);
7888 return true;
7889 }
7891 virtual bool parse(Element *elem)
7892 {
7893 //# NAME
7894 if (!parent.getAttribute(elem, "name", pkgNameOpt))
7895 return false;
7896 if (pkgNameOpt.size()==0)
7897 {
7898 error("<pkg-config> requires 'name=\"package\"' attribute");
7899 return false;
7900 }
7902 //# PROPERTY
7903 if (!parent.getAttribute(elem, "property", propNameOpt))
7904 return false;
7905 if (propNameOpt.size()==0)
7906 {
7907 error("<pkg-config> requires 'property=\"name\"' attribute");
7908 return false;
7909 }
7910 //# PATH
7911 if (!parent.getAttribute(elem, "path", pkgConfigPathOpt))
7912 return false;
7913 //# PREFIX
7914 if (!parent.getAttribute(elem, "prefix", prefixOpt))
7915 return false;
7916 //# QUERY
7917 if (!parent.getAttribute(elem, "query", queryOpt))
7918 return false;
7920 return true;
7921 }
7923 private:
7925 String queryOpt;
7926 String pkgNameOpt;
7927 String prefixOpt;
7928 String propNameOpt;
7929 String pkgConfigPathOpt;
7931 };
7938 /**
7939 * Process an archive to allow random access
7940 */
7941 class TaskRanlib : public Task
7942 {
7943 public:
7945 TaskRanlib(MakeBase &par) : Task(par)
7946 { type = TASK_RANLIB; name = "ranlib"; }
7948 virtual ~TaskRanlib()
7949 {}
7951 virtual bool execute()
7952 {
7953 String fileName = parent.eval(fileNameOpt, "");
7954 String command = parent.eval(commandOpt, "ranlib");
7956 String fullName = parent.resolve(fileName);
7957 //trace("fullDir:%s", fullDir.c_str());
7958 String cmd = command;
7959 cmd.append(" ");
7960 cmd.append(fullName);
7961 String outbuf, errbuf;
7962 if (!executeCommand(cmd, "", outbuf, errbuf))
7963 return false;
7964 return true;
7965 }
7967 virtual bool parse(Element *elem)
7968 {
7969 if (!parent.getAttribute(elem, "command", commandOpt))
7970 return false;
7971 if (!parent.getAttribute(elem, "file", fileNameOpt))
7972 return false;
7973 if (fileNameOpt.size() == 0)
7974 {
7975 error("<ranlib> requires 'file=\"fileNname\"' attribute");
7976 return false;
7977 }
7978 return true;
7979 }
7981 private:
7983 String fileNameOpt;
7984 String commandOpt;
7985 };
7989 /**
7990 * Compile a resource file into a binary object
7991 */
7992 class TaskRC : public Task
7993 {
7994 public:
7996 TaskRC(MakeBase &par) : Task(par)
7997 { type = TASK_RC; name = "rc"; }
7999 virtual ~TaskRC()
8000 {}
8002 virtual bool execute()
8003 {
8004 String command = parent.eval(commandOpt, "windres");
8005 String flags = parent.eval(flagsOpt, "");
8006 String fileName = parent.eval(fileNameOpt, "");
8007 String outName = parent.eval(outNameOpt, "");
8009 String fullFile = parent.resolve(fileName);
8010 String fullOut = parent.resolve(outName);
8011 if (!isNewerThan(fullFile, fullOut))
8012 return true;
8013 String cmd = command;
8014 cmd.append(" -o ");
8015 cmd.append(fullOut);
8016 cmd.append(" ");
8017 cmd.append(flags);
8018 cmd.append(" ");
8019 cmd.append(fullFile);
8021 String outString, errString;
8022 if (!executeCommand(cmd.c_str(), "", outString, errString))
8023 {
8024 error("RC problem: %s", errString.c_str());
8025 return false;
8026 }
8027 return true;
8028 }
8030 virtual bool parse(Element *elem)
8031 {
8032 if (!parent.getAttribute(elem, "command", commandOpt))
8033 return false;
8034 if (!parent.getAttribute(elem, "file", fileNameOpt))
8035 return false;
8036 if (!parent.getAttribute(elem, "out", outNameOpt))
8037 return false;
8038 std::vector<Element *> children = elem->getChildren();
8039 for (unsigned int i=0 ; i<children.size() ; i++)
8040 {
8041 Element *child = children[i];
8042 String tagName = child->getName();
8043 if (tagName == "flags")
8044 {
8045 if (!parent.getValue(child, flagsOpt))
8046 return false;
8047 }
8048 }
8049 return true;
8050 }
8052 private:
8054 String commandOpt;
8055 String flagsOpt;
8056 String fileNameOpt;
8057 String outNameOpt;
8059 };
8063 /**
8064 * Collect .o's into a .so or DLL
8065 */
8066 class TaskSharedLib : public Task
8067 {
8068 public:
8070 TaskSharedLib(MakeBase &par) : Task(par)
8071 { type = TASK_SHAREDLIB; name = "dll"; }
8073 virtual ~TaskSharedLib()
8074 {}
8076 virtual bool execute()
8077 {
8078 String command = parent.eval(commandOpt, "dllwrap");
8079 String fileName = parent.eval(fileNameOpt, "");
8080 String defFileName = parent.eval(defFileNameOpt, "");
8081 String impFileName = parent.eval(impFileNameOpt, "");
8082 String libs = parent.eval(libsOpt, "");
8084 //trace("###########HERE %d", fileSet.size());
8085 bool doit = false;
8087 String fullOut = parent.resolve(fileName);
8088 //trace("ar fullout: %s", fullOut.c_str());
8090 if (!listFiles(parent, fileSet))
8091 return false;
8092 String fileSetDir = parent.eval(fileSet.getDirectory(), ".");
8094 for (unsigned int i=0 ; i<fileSet.size() ; i++)
8095 {
8096 String fname;
8097 if (fileSetDir.size()>0)
8098 {
8099 fname.append(fileSetDir);
8100 fname.append("/");
8101 }
8102 fname.append(fileSet[i]);
8103 String fullName = parent.resolve(fname);
8104 //trace("ar : %s/%s", fullOut.c_str(), fullName.c_str());
8105 if (isNewerThan(fullName, fullOut))
8106 doit = true;
8107 }
8108 //trace("Needs it:%d", doit);
8109 if (!doit)
8110 {
8111 return true;
8112 }
8114 String cmd = "dllwrap";
8115 cmd.append(" -o ");
8116 cmd.append(fullOut);
8117 if (defFileName.size()>0)
8118 {
8119 cmd.append(" --def ");
8120 cmd.append(defFileName);
8121 cmd.append(" ");
8122 }
8123 if (impFileName.size()>0)
8124 {
8125 cmd.append(" --implib ");
8126 cmd.append(impFileName);
8127 cmd.append(" ");
8128 }
8129 for (unsigned int i=0 ; i<fileSet.size() ; i++)
8130 {
8131 String fname;
8132 if (fileSetDir.size()>0)
8133 {
8134 fname.append(fileSetDir);
8135 fname.append("/");
8136 }
8137 fname.append(fileSet[i]);
8138 String fullName = parent.resolve(fname);
8140 cmd.append(" ");
8141 cmd.append(fullName);
8142 }
8143 cmd.append(" ");
8144 cmd.append(libs);
8146 String outString, errString;
8147 if (!executeCommand(cmd.c_str(), "", outString, errString))
8148 {
8149 error("<sharedlib> problem: %s", errString.c_str());
8150 return false;
8151 }
8153 return true;
8154 }
8156 virtual bool parse(Element *elem)
8157 {
8158 if (!parent.getAttribute(elem, "command", commandOpt))
8159 return false;
8160 if (!parent.getAttribute(elem, "file", fileNameOpt))
8161 return false;
8162 if (!parent.getAttribute(elem, "import", impFileNameOpt))
8163 return false;
8164 if (!parent.getAttribute(elem, "def", defFileNameOpt))
8165 return false;
8167 std::vector<Element *> children = elem->getChildren();
8168 for (unsigned int i=0 ; i<children.size() ; i++)
8169 {
8170 Element *child = children[i];
8171 String tagName = child->getName();
8172 if (tagName == "fileset")
8173 {
8174 if (!parseFileSet(child, parent, fileSet))
8175 return false;
8176 }
8177 else if (tagName == "libs")
8178 {
8179 if (!parent.getValue(child, libsOpt))
8180 return false;
8181 libsOpt = strip(libsOpt);
8182 }
8183 }
8184 return true;
8185 }
8187 private:
8189 FileSet fileSet;
8191 String commandOpt;
8192 String fileNameOpt;
8193 String defFileNameOpt;
8194 String impFileNameOpt;
8195 String libsOpt;
8197 };
8201 /**
8202 * Run the "ar" command to archive .o's into a .a
8203 */
8204 class TaskStaticLib : public Task
8205 {
8206 public:
8208 TaskStaticLib(MakeBase &par) : Task(par)
8209 { type = TASK_STATICLIB; name = "staticlib"; }
8211 virtual ~TaskStaticLib()
8212 {}
8214 virtual bool execute()
8215 {
8216 String command = parent.eval(commandOpt, "ar crv");
8217 String fileName = parent.eval(fileNameOpt, "");
8219 bool doit = false;
8221 String fullOut = parent.resolve(fileName);
8222 //trace("ar fullout: %s", fullOut.c_str());
8224 if (!listFiles(parent, fileSet))
8225 return false;
8226 String fileSetDir = parent.eval(fileSet.getDirectory(), ".");
8227 //trace("###########HERE %s", fileSetDir.c_str());
8229 for (unsigned int i=0 ; i<fileSet.size() ; i++)
8230 {
8231 String fname;
8232 if (fileSetDir.size()>0)
8233 {
8234 fname.append(fileSetDir);
8235 fname.append("/");
8236 }
8237 fname.append(fileSet[i]);
8238 String fullName = parent.resolve(fname);
8239 //trace("ar : %s/%s", fullOut.c_str(), fullName.c_str());
8240 if (isNewerThan(fullName, fullOut))
8241 doit = true;
8242 }
8243 //trace("Needs it:%d", doit);
8244 if (!doit)
8245 {
8246 return true;
8247 }
8249 String cmd = command;
8250 cmd.append(" ");
8251 cmd.append(fullOut);
8252 for (unsigned int i=0 ; i<fileSet.size() ; i++)
8253 {
8254 String fname;
8255 if (fileSetDir.size()>0)
8256 {
8257 fname.append(fileSetDir);
8258 fname.append("/");
8259 }
8260 fname.append(fileSet[i]);
8261 String fullName = parent.resolve(fname);
8263 cmd.append(" ");
8264 cmd.append(fullName);
8265 }
8267 String outString, errString;
8268 if (!executeCommand(cmd.c_str(), "", outString, errString))
8269 {
8270 error("<staticlib> problem: %s", errString.c_str());
8271 return false;
8272 }
8274 return true;
8275 }
8278 virtual bool parse(Element *elem)
8279 {
8280 if (!parent.getAttribute(elem, "command", commandOpt))
8281 return false;
8282 if (!parent.getAttribute(elem, "file", fileNameOpt))
8283 return false;
8285 std::vector<Element *> children = elem->getChildren();
8286 for (unsigned int i=0 ; i<children.size() ; i++)
8287 {
8288 Element *child = children[i];
8289 String tagName = child->getName();
8290 if (tagName == "fileset")
8291 {
8292 if (!parseFileSet(child, parent, fileSet))
8293 return false;
8294 }
8295 }
8296 return true;
8297 }
8299 private:
8301 FileSet fileSet;
8303 String commandOpt;
8304 String fileNameOpt;
8306 };
8311 /**
8312 * Strip an executable
8313 */
8314 class TaskStrip : public Task
8315 {
8316 public:
8318 TaskStrip(MakeBase &par) : Task(par)
8319 { type = TASK_STRIP; name = "strip"; }
8321 virtual ~TaskStrip()
8322 {}
8324 virtual bool execute()
8325 {
8326 String command = parent.eval(commandOpt, "strip");
8327 String fileName = parent.eval(fileNameOpt, "");
8328 String symFileName = parent.eval(symFileNameOpt, "");
8330 String fullName = parent.resolve(fileName);
8331 //trace("fullDir:%s", fullDir.c_str());
8332 String cmd;
8333 String outbuf, errbuf;
8335 if (symFileName.size()>0)
8336 {
8337 String symFullName = parent.resolve(symFileName);
8338 cmd = "objcopy --only-keep-debug ";
8339 cmd.append(getNativePath(fullName));
8340 cmd.append(" ");
8341 cmd.append(getNativePath(symFullName));
8342 if (!executeCommand(cmd, "", outbuf, errbuf))
8343 {
8344 error("<strip> symbol file failed : %s", errbuf.c_str());
8345 return false;
8346 }
8347 }
8349 cmd = command;
8350 cmd.append(getNativePath(fullName));
8351 if (!executeCommand(cmd, "", outbuf, errbuf))
8352 {
8353 error("<strip> failed : %s", errbuf.c_str());
8354 return false;
8355 }
8356 return true;
8357 }
8359 virtual bool parse(Element *elem)
8360 {
8361 if (!parent.getAttribute(elem, "command", commandOpt))
8362 return false;
8363 if (!parent.getAttribute(elem, "file", fileNameOpt))
8364 return false;
8365 if (!parent.getAttribute(elem, "symfile", symFileNameOpt))
8366 return false;
8367 if (fileNameOpt.size() == 0)
8368 {
8369 error("<strip> requires 'file=\"fileName\"' attribute");
8370 return false;
8371 }
8372 return true;
8373 }
8375 private:
8377 String commandOpt;
8378 String fileNameOpt;
8379 String symFileNameOpt;
8380 };
8383 /**
8384 *
8385 */
8386 class TaskTouch : public Task
8387 {
8388 public:
8390 TaskTouch(MakeBase &par) : Task(par)
8391 { type = TASK_TOUCH; name = "touch"; }
8393 virtual ~TaskTouch()
8394 {}
8396 virtual bool execute()
8397 {
8398 String fileName = parent.eval(fileNameOpt, "");
8400 String fullName = parent.resolve(fileName);
8401 String nativeFile = getNativePath(fullName);
8402 if (!isRegularFile(fullName) && !isDirectory(fullName))
8403 {
8404 // S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH
8405 int ret = creat(nativeFile.c_str(), 0666);
8406 if (ret != 0)
8407 {
8408 error("<touch> could not create '%s' : %s",
8409 nativeFile.c_str(), strerror(ret));
8410 return false;
8411 }
8412 return true;
8413 }
8414 int ret = utime(nativeFile.c_str(), (struct utimbuf *)0);
8415 if (ret != 0)
8416 {
8417 error("<touch> could not update the modification time for '%s' : %s",
8418 nativeFile.c_str(), strerror(ret));
8419 return false;
8420 }
8421 return true;
8422 }
8424 virtual bool parse(Element *elem)
8425 {
8426 //trace("touch parse");
8427 if (!parent.getAttribute(elem, "file", fileNameOpt))
8428 return false;
8429 if (fileNameOpt.size() == 0)
8430 {
8431 error("<touch> requires 'file=\"fileName\"' attribute");
8432 return false;
8433 }
8434 return true;
8435 }
8437 String fileNameOpt;
8438 };
8441 /**
8442 *
8443 */
8444 class TaskTstamp : public Task
8445 {
8446 public:
8448 TaskTstamp(MakeBase &par) : Task(par)
8449 { type = TASK_TSTAMP; name = "tstamp"; }
8451 virtual ~TaskTstamp()
8452 {}
8454 virtual bool execute()
8455 {
8456 return true;
8457 }
8459 virtual bool parse(Element *elem)
8460 {
8461 //trace("tstamp parse");
8462 return true;
8463 }
8464 };
8468 /**
8469 *
8470 */
8471 Task *Task::createTask(Element *elem, int lineNr)
8472 {
8473 String tagName = elem->getName();
8474 //trace("task:%s", tagName.c_str());
8475 Task *task = NULL;
8476 if (tagName == "cc")
8477 task = new TaskCC(parent);
8478 else if (tagName == "copy")
8479 task = new TaskCopy(parent);
8480 else if (tagName == "delete")
8481 task = new TaskDelete(parent);
8482 else if (tagName == "echo")
8483 task = new TaskEcho(parent);
8484 else if (tagName == "jar")
8485 task = new TaskJar(parent);
8486 else if (tagName == "javac")
8487 task = new TaskJavac(parent);
8488 else if (tagName == "link")
8489 task = new TaskLink(parent);
8490 else if (tagName == "makefile")
8491 task = new TaskMakeFile(parent);
8492 else if (tagName == "mkdir")
8493 task = new TaskMkDir(parent);
8494 else if (tagName == "msgfmt")
8495 task = new TaskMsgFmt(parent);
8496 else if (tagName == "pkg-config")
8497 task = new TaskPkgConfig(parent);
8498 else if (tagName == "ranlib")
8499 task = new TaskRanlib(parent);
8500 else if (tagName == "rc")
8501 task = new TaskRC(parent);
8502 else if (tagName == "sharedlib")
8503 task = new TaskSharedLib(parent);
8504 else if (tagName == "staticlib")
8505 task = new TaskStaticLib(parent);
8506 else if (tagName == "strip")
8507 task = new TaskStrip(parent);
8508 else if (tagName == "touch")
8509 task = new TaskTouch(parent);
8510 else if (tagName == "tstamp")
8511 task = new TaskTstamp(parent);
8512 else
8513 {
8514 error("Unknown task '%s'", tagName.c_str());
8515 return NULL;
8516 }
8518 task->setLine(lineNr);
8520 if (!task->parse(elem))
8521 {
8522 delete task;
8523 return NULL;
8524 }
8525 return task;
8526 }
8530 //########################################################################
8531 //# T A R G E T
8532 //########################################################################
8534 /**
8535 *
8536 */
8537 class Target : public MakeBase
8538 {
8540 public:
8542 /**
8543 *
8544 */
8545 Target(Make &par) : parent(par)
8546 { init(); }
8548 /**
8549 *
8550 */
8551 Target(const Target &other) : parent(other.parent)
8552 { init(); assign(other); }
8554 /**
8555 *
8556 */
8557 Target &operator=(const Target &other)
8558 { init(); assign(other); return *this; }
8560 /**
8561 *
8562 */
8563 virtual ~Target()
8564 { cleanup() ; }
8567 /**
8568 *
8569 */
8570 virtual Make &getParent()
8571 { return parent; }
8573 /**
8574 *
8575 */
8576 virtual String getName()
8577 { return name; }
8579 /**
8580 *
8581 */
8582 virtual void setName(const String &val)
8583 { name = val; }
8585 /**
8586 *
8587 */
8588 virtual String getDescription()
8589 { return description; }
8591 /**
8592 *
8593 */
8594 virtual void setDescription(const String &val)
8595 { description = val; }
8597 /**
8598 *
8599 */
8600 virtual void addDependency(const String &val)
8601 { deps.push_back(val); }
8603 /**
8604 *
8605 */
8606 virtual void parseDependencies(const String &val)
8607 { deps = tokenize(val, ", "); }
8609 /**
8610 *
8611 */
8612 virtual std::vector<String> &getDependencies()
8613 { return deps; }
8615 /**
8616 *
8617 */
8618 virtual String getIf()
8619 { return ifVar; }
8621 /**
8622 *
8623 */
8624 virtual void setIf(const String &val)
8625 { ifVar = val; }
8627 /**
8628 *
8629 */
8630 virtual String getUnless()
8631 { return unlessVar; }
8633 /**
8634 *
8635 */
8636 virtual void setUnless(const String &val)
8637 { unlessVar = val; }
8639 /**
8640 *
8641 */
8642 virtual void addTask(Task *val)
8643 { tasks.push_back(val); }
8645 /**
8646 *
8647 */
8648 virtual std::vector<Task *> &getTasks()
8649 { return tasks; }
8651 private:
8653 void init()
8654 {
8655 }
8657 void cleanup()
8658 {
8659 tasks.clear();
8660 }
8662 void assign(const Target &other)
8663 {
8664 //parent = other.parent;
8665 name = other.name;
8666 description = other.description;
8667 ifVar = other.ifVar;
8668 unlessVar = other.unlessVar;
8669 deps = other.deps;
8670 tasks = other.tasks;
8671 }
8673 Make &parent;
8675 String name;
8677 String description;
8679 String ifVar;
8681 String unlessVar;
8683 std::vector<String> deps;
8685 std::vector<Task *> tasks;
8687 };
8696 //########################################################################
8697 //# M A K E
8698 //########################################################################
8701 /**
8702 *
8703 */
8704 class Make : public MakeBase
8705 {
8707 public:
8709 /**
8710 *
8711 */
8712 Make()
8713 { init(); }
8715 /**
8716 *
8717 */
8718 Make(const Make &other)
8719 { assign(other); }
8721 /**
8722 *
8723 */
8724 Make &operator=(const Make &other)
8725 { assign(other); return *this; }
8727 /**
8728 *
8729 */
8730 virtual ~Make()
8731 { cleanup(); }
8733 /**
8734 *
8735 */
8736 virtual std::map<String, Target> &getTargets()
8737 { return targets; }
8740 /**
8741 *
8742 */
8743 virtual String version()
8744 { return BUILDTOOL_VERSION; }
8746 /**
8747 * Overload a <property>
8748 */
8749 virtual bool specifyProperty(const String &name,
8750 const String &value);
8752 /**
8753 *
8754 */
8755 virtual bool run();
8757 /**
8758 *
8759 */
8760 virtual bool run(const String &target);
8764 private:
8766 /**
8767 *
8768 */
8769 void init();
8771 /**
8772 *
8773 */
8774 void cleanup();
8776 /**
8777 *
8778 */
8779 void assign(const Make &other);
8781 /**
8782 *
8783 */
8784 bool executeTask(Task &task);
8787 /**
8788 *
8789 */
8790 bool executeTarget(Target &target,
8791 std::set<String> &targetsCompleted);
8794 /**
8795 *
8796 */
8797 bool execute();
8799 /**
8800 *
8801 */
8802 bool checkTargetDependencies(Target &prop,
8803 std::vector<String> &depList);
8805 /**
8806 *
8807 */
8808 bool parsePropertyFile(const String &fileName,
8809 const String &prefix);
8811 /**
8812 *
8813 */
8814 bool parseProperty(Element *elem);
8816 /**
8817 *
8818 */
8819 bool parseFile();
8821 /**
8822 *
8823 */
8824 std::vector<String> glob(const String &pattern);
8827 //###############
8828 //# Fields
8829 //###############
8831 String projectName;
8833 String currentTarget;
8835 String defaultTarget;
8837 String specifiedTarget;
8839 String baseDir;
8841 String description;
8843 //std::vector<Property> properties;
8845 std::map<String, Target> targets;
8847 std::vector<Task *> allTasks;
8849 std::map<String, String> specifiedProperties;
8851 };
8854 //########################################################################
8855 //# C L A S S M A I N T E N A N C E
8856 //########################################################################
8858 /**
8859 *
8860 */
8861 void Make::init()
8862 {
8863 uri = "build.xml";
8864 projectName = "";
8865 currentTarget = "";
8866 defaultTarget = "";
8867 specifiedTarget = "";
8868 baseDir = "";
8869 description = "";
8870 envPrefix = "env.";
8871 pcPrefix = "pc.";
8872 pccPrefix = "pcc.";
8873 pclPrefix = "pcl.";
8874 properties.clear();
8875 for (unsigned int i = 0 ; i < allTasks.size() ; i++)
8876 delete allTasks[i];
8877 allTasks.clear();
8878 }
8882 /**
8883 *
8884 */
8885 void Make::cleanup()
8886 {
8887 for (unsigned int i = 0 ; i < allTasks.size() ; i++)
8888 delete allTasks[i];
8889 allTasks.clear();
8890 }
8894 /**
8895 *
8896 */
8897 void Make::assign(const Make &other)
8898 {
8899 uri = other.uri;
8900 projectName = other.projectName;
8901 currentTarget = other.currentTarget;
8902 defaultTarget = other.defaultTarget;
8903 specifiedTarget = other.specifiedTarget;
8904 baseDir = other.baseDir;
8905 description = other.description;
8906 properties = other.properties;
8907 }
8911 //########################################################################
8912 //# U T I L I T Y T A S K S
8913 //########################################################################
8915 /**
8916 * Perform a file globbing
8917 */
8918 std::vector<String> Make::glob(const String &pattern)
8919 {
8920 std::vector<String> res;
8921 return res;
8922 }
8925 //########################################################################
8926 //# P U B L I C A P I
8927 //########################################################################
8931 /**
8932 *
8933 */
8934 bool Make::executeTarget(Target &target,
8935 std::set<String> &targetsCompleted)
8936 {
8938 String name = target.getName();
8940 //First get any dependencies for this target
8941 std::vector<String> deps = target.getDependencies();
8942 for (unsigned int i=0 ; i<deps.size() ; i++)
8943 {
8944 String dep = deps[i];
8945 //Did we do it already? Skip
8946 if (targetsCompleted.find(dep)!=targetsCompleted.end())
8947 continue;
8949 std::map<String, Target> &tgts =
8950 target.getParent().getTargets();
8951 std::map<String, Target>::iterator iter =
8952 tgts.find(dep);
8953 if (iter == tgts.end())
8954 {
8955 error("Target '%s' dependency '%s' not found",
8956 name.c_str(), dep.c_str());
8957 return false;
8958 }
8959 Target depTarget = iter->second;
8960 if (!executeTarget(depTarget, targetsCompleted))
8961 {
8962 return false;
8963 }
8964 }
8966 status("##### Target : %s\n##### %s", name.c_str(),
8967 target.getDescription().c_str());
8969 //Now let's do the tasks
8970 std::vector<Task *> &tasks = target.getTasks();
8971 for (unsigned int i=0 ; i<tasks.size() ; i++)
8972 {
8973 Task *task = tasks[i];
8974 status("--- %s / %s", name.c_str(), task->getName().c_str());
8975 if (!task->execute())
8976 {
8977 return false;
8978 }
8979 }
8981 targetsCompleted.insert(name);
8983 return true;
8984 }
8988 /**
8989 * Main execute() method. Start here and work
8990 * up the dependency tree
8991 */
8992 bool Make::execute()
8993 {
8994 status("######## EXECUTE");
8996 //Determine initial target
8997 if (specifiedTarget.size()>0)
8998 {
8999 currentTarget = specifiedTarget;
9000 }
9001 else if (defaultTarget.size()>0)
9002 {
9003 currentTarget = defaultTarget;
9004 }
9005 else
9006 {
9007 error("execute: no specified or default target requested");
9008 return false;
9009 }
9011 std::map<String, Target>::iterator iter =
9012 targets.find(currentTarget);
9013 if (iter == targets.end())
9014 {
9015 error("Initial target '%s' not found",
9016 currentTarget.c_str());
9017 return false;
9018 }
9020 //Now run
9021 Target target = iter->second;
9022 std::set<String> targetsCompleted;
9023 if (!executeTarget(target, targetsCompleted))
9024 {
9025 return false;
9026 }
9028 status("######## EXECUTE COMPLETE");
9029 return true;
9030 }
9035 /**
9036 *
9037 */
9038 bool Make::checkTargetDependencies(Target &target,
9039 std::vector<String> &depList)
9040 {
9041 String tgtName = target.getName().c_str();
9042 depList.push_back(tgtName);
9044 std::vector<String> deps = target.getDependencies();
9045 for (unsigned int i=0 ; i<deps.size() ; i++)
9046 {
9047 String dep = deps[i];
9048 //First thing entered was the starting Target
9049 if (dep == depList[0])
9050 {
9051 error("Circular dependency '%s' found at '%s'",
9052 dep.c_str(), tgtName.c_str());
9053 std::vector<String>::iterator diter;
9054 for (diter=depList.begin() ; diter!=depList.end() ; diter++)
9055 {
9056 error(" %s", diter->c_str());
9057 }
9058 return false;
9059 }
9061 std::map<String, Target> &tgts =
9062 target.getParent().getTargets();
9063 std::map<String, Target>::iterator titer = tgts.find(dep);
9064 if (titer == tgts.end())
9065 {
9066 error("Target '%s' dependency '%s' not found",
9067 tgtName.c_str(), dep.c_str());
9068 return false;
9069 }
9070 if (!checkTargetDependencies(titer->second, depList))
9071 {
9072 return false;
9073 }
9074 }
9075 return true;
9076 }
9082 static int getword(int pos, const String &inbuf, String &result)
9083 {
9084 int p = pos;
9085 int len = (int)inbuf.size();
9086 String val;
9087 while (p < len)
9088 {
9089 char ch = inbuf[p];
9090 if (!isalnum(ch) && ch!='.' && ch!='_')
9091 break;
9092 val.push_back(ch);
9093 p++;
9094 }
9095 result = val;
9096 return p;
9097 }
9102 /**
9103 *
9104 */
9105 bool Make::parsePropertyFile(const String &fileName,
9106 const String &prefix)
9107 {
9108 FILE *f = fopen(fileName.c_str(), "r");
9109 if (!f)
9110 {
9111 error("could not open property file %s", fileName.c_str());
9112 return false;
9113 }
9114 int linenr = 0;
9115 while (!feof(f))
9116 {
9117 char buf[256];
9118 if (!fgets(buf, 255, f))
9119 break;
9120 linenr++;
9121 String s = buf;
9122 s = trim(s);
9123 int len = s.size();
9124 if (len == 0)
9125 continue;
9126 if (s[0] == '#')
9127 continue;
9128 String key;
9129 String val;
9130 int p = 0;
9131 int p2 = getword(p, s, key);
9132 if (p2 <= p)
9133 {
9134 error("property file %s, line %d: expected keyword",
9135 fileName.c_str(), linenr);
9136 return false;
9137 }
9138 if (prefix.size() > 0)
9139 {
9140 key.insert(0, prefix);
9141 }
9143 //skip whitespace
9144 for (p=p2 ; p<len ; p++)
9145 if (!isspace(s[p]))
9146 break;
9148 if (p>=len || s[p]!='=')
9149 {
9150 error("property file %s, line %d: expected '='",
9151 fileName.c_str(), linenr);
9152 return false;
9153 }
9154 p++;
9156 //skip whitespace
9157 for ( ; p<len ; p++)
9158 if (!isspace(s[p]))
9159 break;
9161 /* This way expects a word after the =
9162 p2 = getword(p, s, val);
9163 if (p2 <= p)
9164 {
9165 error("property file %s, line %d: expected value",
9166 fileName.c_str(), linenr);
9167 return false;
9168 }
9169 */
9170 // This way gets the rest of the line after the =
9171 if (p>=len)
9172 {
9173 error("property file %s, line %d: expected value",
9174 fileName.c_str(), linenr);
9175 return false;
9176 }
9177 val = s.substr(p);
9178 if (key.size()==0)
9179 continue;
9180 //allow property to be set, even if val=""
9182 //trace("key:'%s' val:'%s'", key.c_str(), val.c_str());
9183 //See if we wanted to overload this property
9184 std::map<String, String>::iterator iter =
9185 specifiedProperties.find(key);
9186 if (iter!=specifiedProperties.end())
9187 {
9188 val = iter->second;
9189 status("overloading property '%s' = '%s'",
9190 key.c_str(), val.c_str());
9191 }
9192 properties[key] = val;
9193 }
9194 fclose(f);
9195 return true;
9196 }
9201 /**
9202 *
9203 */
9204 bool Make::parseProperty(Element *elem)
9205 {
9206 std::vector<Attribute> &attrs = elem->getAttributes();
9207 for (unsigned int i=0 ; i<attrs.size() ; i++)
9208 {
9209 String attrName = attrs[i].getName();
9210 String attrVal = attrs[i].getValue();
9212 if (attrName == "name")
9213 {
9214 String val;
9215 if (!getAttribute(elem, "value", val))
9216 return false;
9217 if (val.size() > 0)
9218 {
9219 properties[attrVal] = val;
9220 }
9221 else
9222 {
9223 if (!getAttribute(elem, "location", val))
9224 return false;
9225 //let the property exist, even if not defined
9226 properties[attrVal] = val;
9227 }
9228 //See if we wanted to overload this property
9229 std::map<String, String>::iterator iter =
9230 specifiedProperties.find(attrVal);
9231 if (iter != specifiedProperties.end())
9232 {
9233 val = iter->second;
9234 status("overloading property '%s' = '%s'",
9235 attrVal.c_str(), val.c_str());
9236 properties[attrVal] = val;
9237 }
9238 }
9239 else if (attrName == "file")
9240 {
9241 String prefix;
9242 if (!getAttribute(elem, "prefix", prefix))
9243 return false;
9244 if (prefix.size() > 0)
9245 {
9246 if (prefix[prefix.size()-1] != '.')
9247 prefix.push_back('.');
9248 }
9249 if (!parsePropertyFile(attrName, prefix))
9250 return false;
9251 }
9252 else if (attrName == "environment")
9253 {
9254 if (attrVal.find('.') != attrVal.npos)
9255 {
9256 error("environment prefix cannot have a '.' in it");
9257 return false;
9258 }
9259 envPrefix = attrVal;
9260 envPrefix.push_back('.');
9261 }
9262 else if (attrName == "pkg-config")
9263 {
9264 if (attrVal.find('.') != attrVal.npos)
9265 {
9266 error("pkg-config prefix cannot have a '.' in it");
9267 return false;
9268 }
9269 pcPrefix = attrVal;
9270 pcPrefix.push_back('.');
9271 }
9272 else if (attrName == "pkg-config-cflags")
9273 {
9274 if (attrVal.find('.') != attrVal.npos)
9275 {
9276 error("pkg-config-cflags prefix cannot have a '.' in it");
9277 return false;
9278 }
9279 pccPrefix = attrVal;
9280 pccPrefix.push_back('.');
9281 }
9282 else if (attrName == "pkg-config-libs")
9283 {
9284 if (attrVal.find('.') != attrVal.npos)
9285 {
9286 error("pkg-config-libs prefix cannot have a '.' in it");
9287 return false;
9288 }
9289 pclPrefix = attrVal;
9290 pclPrefix.push_back('.');
9291 }
9292 }
9294 return true;
9295 }
9300 /**
9301 *
9302 */
9303 bool Make::parseFile()
9304 {
9305 status("######## PARSE : %s", uri.getPath().c_str());
9307 setLine(0);
9309 Parser parser;
9310 Element *root = parser.parseFile(uri.getNativePath());
9311 if (!root)
9312 {
9313 error("Could not open %s for reading",
9314 uri.getNativePath().c_str());
9315 return false;
9316 }
9318 setLine(root->getLine());
9320 if (root->getChildren().size()==0 ||
9321 root->getChildren()[0]->getName()!="project")
9322 {
9323 error("Main xml element should be <project>");
9324 delete root;
9325 return false;
9326 }
9328 //########## Project attributes
9329 Element *project = root->getChildren()[0];
9330 String s = project->getAttribute("name");
9331 if (s.size() > 0)
9332 projectName = s;
9333 s = project->getAttribute("default");
9334 if (s.size() > 0)
9335 defaultTarget = s;
9336 s = project->getAttribute("basedir");
9337 if (s.size() > 0)
9338 baseDir = s;
9340 //######### PARSE MEMBERS
9341 std::vector<Element *> children = project->getChildren();
9342 for (unsigned int i=0 ; i<children.size() ; i++)
9343 {
9344 Element *elem = children[i];
9345 setLine(elem->getLine());
9346 String tagName = elem->getName();
9348 //########## DESCRIPTION
9349 if (tagName == "description")
9350 {
9351 description = parser.trim(elem->getValue());
9352 }
9354 //######### PROPERTY
9355 else if (tagName == "property")
9356 {
9357 if (!parseProperty(elem))
9358 return false;
9359 }
9361 //######### TARGET
9362 else if (tagName == "target")
9363 {
9364 String tname = elem->getAttribute("name");
9365 String tdesc = elem->getAttribute("description");
9366 String tdeps = elem->getAttribute("depends");
9367 String tif = elem->getAttribute("if");
9368 String tunless = elem->getAttribute("unless");
9369 Target target(*this);
9370 target.setName(tname);
9371 target.setDescription(tdesc);
9372 target.parseDependencies(tdeps);
9373 target.setIf(tif);
9374 target.setUnless(tunless);
9375 std::vector<Element *> telems = elem->getChildren();
9376 for (unsigned int i=0 ; i<telems.size() ; i++)
9377 {
9378 Element *telem = telems[i];
9379 Task breeder(*this);
9380 Task *task = breeder.createTask(telem, telem->getLine());
9381 if (!task)
9382 return false;
9383 allTasks.push_back(task);
9384 target.addTask(task);
9385 }
9387 //Check name
9388 if (tname.size() == 0)
9389 {
9390 error("no name for target");
9391 return false;
9392 }
9393 //Check for duplicate name
9394 if (targets.find(tname) != targets.end())
9395 {
9396 error("target '%s' already defined", tname.c_str());
9397 return false;
9398 }
9399 //more work than targets[tname]=target, but avoids default allocator
9400 targets.insert(std::make_pair<String, Target>(tname, target));
9401 }
9402 //######### none of the above
9403 else
9404 {
9405 error("unknown toplevel tag: <%s>", tagName.c_str());
9406 return false;
9407 }
9409 }
9411 std::map<String, Target>::iterator iter;
9412 for (iter = targets.begin() ; iter!= targets.end() ; iter++)
9413 {
9414 Target tgt = iter->second;
9415 std::vector<String> depList;
9416 if (!checkTargetDependencies(tgt, depList))
9417 {
9418 return false;
9419 }
9420 }
9423 delete root;
9424 status("######## PARSE COMPLETE");
9425 return true;
9426 }
9429 /**
9430 * Overload a <property>
9431 */
9432 bool Make::specifyProperty(const String &name, const String &value)
9433 {
9434 if (specifiedProperties.find(name) != specifiedProperties.end())
9435 {
9436 error("Property %s already specified", name.c_str());
9437 return false;
9438 }
9439 specifiedProperties[name] = value;
9440 return true;
9441 }
9445 /**
9446 *
9447 */
9448 bool Make::run()
9449 {
9450 if (!parseFile())
9451 return false;
9453 if (!execute())
9454 return false;
9456 return true;
9457 }
9462 /**
9463 * Get a formatted MM:SS.sss time elapsed string
9464 */
9465 static String
9466 timeDiffString(struct timeval &x, struct timeval &y)
9467 {
9468 long microsX = x.tv_usec;
9469 long secondsX = x.tv_sec;
9470 long microsY = y.tv_usec;
9471 long secondsY = y.tv_sec;
9472 if (microsX < microsY)
9473 {
9474 microsX += 1000000;
9475 secondsX -= 1;
9476 }
9478 int seconds = (int)(secondsX - secondsY);
9479 int millis = (int)((microsX - microsY)/1000);
9481 int minutes = seconds/60;
9482 seconds -= minutes*60;
9483 char buf[80];
9484 snprintf(buf, 79, "%dm %d.%03ds", minutes, seconds, millis);
9485 String ret = buf;
9486 return ret;
9488 }
9490 /**
9491 *
9492 */
9493 bool Make::run(const String &target)
9494 {
9495 status("####################################################");
9496 status("# %s", version().c_str());
9497 status("####################################################");
9498 struct timeval timeStart, timeEnd;
9499 ::gettimeofday(&timeStart, NULL);
9500 specifiedTarget = target;
9501 if (!run())
9502 return false;
9503 ::gettimeofday(&timeEnd, NULL);
9504 String timeStr = timeDiffString(timeEnd, timeStart);
9505 status("####################################################");
9506 status("# BuildTool Completed : %s", timeStr.c_str());
9507 status("####################################################");
9508 return true;
9509 }
9517 }// namespace buildtool
9518 //########################################################################
9519 //# M A I N
9520 //########################################################################
9522 typedef buildtool::String String;
9524 /**
9525 * Format an error message in printf() style
9526 */
9527 static void error(const char *fmt, ...)
9528 {
9529 va_list ap;
9530 va_start(ap, fmt);
9531 fprintf(stderr, "BuildTool error: ");
9532 vfprintf(stderr, fmt, ap);
9533 fprintf(stderr, "\n");
9534 va_end(ap);
9535 }
9538 static bool parseProperty(const String &s, String &name, String &val)
9539 {
9540 int len = s.size();
9541 int i;
9542 for (i=0 ; i<len ; i++)
9543 {
9544 char ch = s[i];
9545 if (ch == '=')
9546 break;
9547 name.push_back(ch);
9548 }
9549 if (i>=len || s[i]!='=')
9550 {
9551 error("property requires -Dname=value");
9552 return false;
9553 }
9554 i++;
9555 for ( ; i<len ; i++)
9556 {
9557 char ch = s[i];
9558 val.push_back(ch);
9559 }
9560 return true;
9561 }
9564 /**
9565 * Compare a buffer with a key, for the length of the key
9566 */
9567 static bool sequ(const String &buf, const char *key)
9568 {
9569 int len = buf.size();
9570 for (int i=0 ; key[i] && i<len ; i++)
9571 {
9572 if (key[i] != buf[i])
9573 return false;
9574 }
9575 return true;
9576 }
9578 static void usage(int argc, char **argv)
9579 {
9580 printf("usage:\n");
9581 printf(" %s [options] [target]\n", argv[0]);
9582 printf("Options:\n");
9583 printf(" -help, -h print this message\n");
9584 printf(" -version print the version information and exit\n");
9585 printf(" -file <file> use given buildfile\n");
9586 printf(" -f <file> ''\n");
9587 printf(" -D<property>=<value> use value for given property\n");
9588 }
9593 /**
9594 * Parse the command-line args, get our options,
9595 * and run this thing
9596 */
9597 static bool parseOptions(int argc, char **argv)
9598 {
9599 if (argc < 1)
9600 {
9601 error("Cannot parse arguments");
9602 return false;
9603 }
9605 buildtool::Make make;
9607 String target;
9609 //char *progName = argv[0];
9610 for (int i=1 ; i<argc ; i++)
9611 {
9612 String arg = argv[i];
9613 if (arg.size()>1 && arg[0]=='-')
9614 {
9615 if (arg == "-h" || arg == "-help")
9616 {
9617 usage(argc,argv);
9618 return true;
9619 }
9620 else if (arg == "-version")
9621 {
9622 printf("%s", make.version().c_str());
9623 return true;
9624 }
9625 else if (arg == "-f" || arg == "-file")
9626 {
9627 if (i>=argc)
9628 {
9629 usage(argc, argv);
9630 return false;
9631 }
9632 i++; //eat option
9633 make.setURI(argv[i]);
9634 }
9635 else if (arg.size()>2 && sequ(arg, "-D"))
9636 {
9637 String s = arg.substr(2, arg.size());
9638 String name, value;
9639 if (!parseProperty(s, name, value))
9640 {
9641 usage(argc, argv);
9642 return false;
9643 }
9644 if (!make.specifyProperty(name, value))
9645 return false;
9646 }
9647 else
9648 {
9649 error("Unknown option:%s", arg.c_str());
9650 return false;
9651 }
9652 }
9653 else
9654 {
9655 if (target.size()>0)
9656 {
9657 error("only one initial target");
9658 usage(argc, argv);
9659 return false;
9660 }
9661 target = arg;
9662 }
9663 }
9665 //We have the options. Now execute them
9666 if (!make.run(target))
9667 return false;
9669 return true;
9670 }
9675 /*
9676 static bool runMake()
9677 {
9678 buildtool::Make make;
9679 if (!make.run())
9680 return false;
9681 return true;
9682 }
9685 static bool pkgConfigTest()
9686 {
9687 buildtool::PkgConfig pkgConfig;
9688 if (!pkgConfig.readFile("gtk+-2.0.pc"))
9689 return false;
9690 return true;
9691 }
9695 static bool depTest()
9696 {
9697 buildtool::DepTool deptool;
9698 deptool.setSourceDirectory("/dev/ink/inkscape/src");
9699 if (!deptool.generateDependencies("build.dep"))
9700 return false;
9701 std::vector<buildtool::FileRec> res =
9702 deptool.loadDepFile("build.dep");
9703 if (res.size() == 0)
9704 return false;
9705 return true;
9706 }
9708 static bool popenTest()
9709 {
9710 buildtool::Make make;
9711 buildtool::String out, err;
9712 bool ret = make.executeCommand("gcc xx.cpp", "", out, err);
9713 printf("Popen test:%d '%s' '%s'\n", ret, out.c_str(), err.c_str());
9714 return true;
9715 }
9718 static bool propFileTest()
9719 {
9720 buildtool::Make make;
9721 make.parsePropertyFile("test.prop", "test.");
9722 return true;
9723 }
9724 */
9726 int main(int argc, char **argv)
9727 {
9729 if (!parseOptions(argc, argv))
9730 return 1;
9731 /*
9732 if (!popenTest())
9733 return 1;
9735 if (!depTest())
9736 return 1;
9737 if (!propFileTest())
9738 return 1;
9739 if (runMake())
9740 return 1;
9741 */
9742 return 0;
9743 }
9746 //########################################################################
9747 //# E N D
9748 //########################################################################