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 || ch <= 0)
4148 outOpen = false;
4149 else
4150 outb.push_back(ch);
4151 }
4152 if (FD_ISSET(errRead, &fdset))
4153 {
4154 if (read(errRead, &ch, 1) <= 0 || ch <= 0)
4155 errOpen = false;
4156 else
4157 errb.push_back(ch);
4158 }
4159 }
4161 int childReturnValue;
4162 wait(&childReturnValue);
4164 close(outRead);
4165 close(errRead);
4167 outbuf = outb;
4168 errbuf = errb;
4170 if (childReturnValue != 0)
4171 {
4172 error("exec of command '%s' failed : %s",
4173 command.c_str(), strerror(childReturnValue));
4174 return false;
4175 }
4177 return true;
4178 }
4180 #endif
4185 bool MakeBase::listDirectories(const String &baseName,
4186 const String &dirName,
4187 std::vector<String> &res)
4188 {
4189 res.push_back(dirName);
4190 String fullPath = baseName;
4191 if (dirName.size()>0)
4192 {
4193 fullPath.append("/");
4194 fullPath.append(dirName);
4195 }
4196 DIR *dir = opendir(fullPath.c_str());
4197 while (true)
4198 {
4199 struct dirent *de = readdir(dir);
4200 if (!de)
4201 break;
4203 //Get the directory member name
4204 String s = de->d_name;
4205 if (s.size() == 0 || s[0] == '.')
4206 continue;
4207 String childName = dirName;
4208 childName.append("/");
4209 childName.append(s);
4211 String fullChildPath = baseName;
4212 fullChildPath.append("/");
4213 fullChildPath.append(childName);
4214 struct stat finfo;
4215 String childNative = getNativePath(fullChildPath);
4216 if (stat(childNative.c_str(), &finfo)<0)
4217 {
4218 error("cannot stat file:%s", childNative.c_str());
4219 }
4220 else if (S_ISDIR(finfo.st_mode))
4221 {
4222 //trace("directory: %s", childName.c_str());
4223 if (!listDirectories(baseName, childName, res))
4224 return false;
4225 }
4226 }
4227 closedir(dir);
4229 return true;
4230 }
4233 bool MakeBase::listFiles(const String &baseDir,
4234 const String &dirName,
4235 std::vector<String> &res)
4236 {
4237 String fullDir = baseDir;
4238 if (dirName.size()>0)
4239 {
4240 fullDir.append("/");
4241 fullDir.append(dirName);
4242 }
4243 String dirNative = getNativePath(fullDir);
4245 std::vector<String> subdirs;
4246 DIR *dir = opendir(dirNative.c_str());
4247 if (!dir)
4248 {
4249 error("Could not open directory %s : %s",
4250 dirNative.c_str(), strerror(errno));
4251 return false;
4252 }
4253 while (true)
4254 {
4255 struct dirent *de = readdir(dir);
4256 if (!de)
4257 break;
4259 //Get the directory member name
4260 String s = de->d_name;
4261 if (s.size() == 0 || s[0] == '.')
4262 continue;
4263 String childName;
4264 if (dirName.size()>0)
4265 {
4266 childName.append(dirName);
4267 childName.append("/");
4268 }
4269 childName.append(s);
4270 String fullChild = baseDir;
4271 fullChild.append("/");
4272 fullChild.append(childName);
4274 if (isDirectory(fullChild))
4275 {
4276 //trace("directory: %s", childName.c_str());
4277 if (!listFiles(baseDir, childName, res))
4278 return false;
4279 continue;
4280 }
4281 else if (!isRegularFile(fullChild))
4282 {
4283 error("unknown file:%s", childName.c_str());
4284 return false;
4285 }
4287 //all done!
4288 res.push_back(childName);
4290 }
4291 closedir(dir);
4293 return true;
4294 }
4297 /**
4298 * Several different classes extend MakeBase. By "propRef", we mean
4299 * the one holding the properties. Likely "Make" itself
4300 */
4301 bool MakeBase::listFiles(MakeBase &propRef, FileSet &fileSet)
4302 {
4303 //before doing the list, resolve any property references
4304 //that might have been specified in the directory name, such as ${src}
4305 String fsDir = fileSet.getDirectory();
4306 String dir;
4307 if (!propRef.getSubstitutions(fsDir, dir))
4308 return false;
4309 String baseDir = propRef.resolve(dir);
4310 std::vector<String> fileList;
4311 if (!listFiles(baseDir, "", fileList))
4312 return false;
4314 std::vector<String> includes = fileSet.getIncludes();
4315 std::vector<String> excludes = fileSet.getExcludes();
4317 std::vector<String> incs;
4318 std::vector<String>::iterator iter;
4320 std::sort(fileList.begin(), fileList.end());
4322 //If there are <includes>, then add files to the output
4323 //in the order of the include list
4324 if (includes.size()==0)
4325 incs = fileList;
4326 else
4327 {
4328 for (iter = includes.begin() ; iter != includes.end() ; iter++)
4329 {
4330 String &pattern = *iter;
4331 std::vector<String>::iterator siter;
4332 for (siter = fileList.begin() ; siter != fileList.end() ; siter++)
4333 {
4334 String s = *siter;
4335 if (regexMatch(s, pattern))
4336 {
4337 //trace("INCLUDED:%s", s.c_str());
4338 incs.push_back(s);
4339 }
4340 }
4341 }
4342 }
4344 //Now trim off the <excludes>
4345 std::vector<String> res;
4346 for (iter = incs.begin() ; iter != incs.end() ; iter++)
4347 {
4348 String s = *iter;
4349 bool skipme = false;
4350 std::vector<String>::iterator siter;
4351 for (siter = excludes.begin() ; siter != excludes.end() ; siter++)
4352 {
4353 String &pattern = *siter;
4354 if (regexMatch(s, pattern))
4355 {
4356 //trace("EXCLUDED:%s", s.c_str());
4357 skipme = true;
4358 break;
4359 }
4360 }
4361 if (!skipme)
4362 res.push_back(s);
4363 }
4365 fileSet.setFiles(res);
4367 return true;
4368 }
4371 /**
4372 * 0 == all, 1 = cflags, 2 = libs
4373 */
4374 bool MakeBase::pkgConfigRecursive(const String packageName,
4375 const String &path,
4376 const String &prefix,
4377 int query,
4378 String &result,
4379 std::set<String> &deplist)
4380 {
4381 PkgConfig pkgConfig;
4382 if (path.size() > 0)
4383 pkgConfig.setPath(path);
4384 if (prefix.size() > 0)
4385 pkgConfig.setPrefix(prefix);
4386 if (!pkgConfig.query(packageName))
4387 return false;
4388 if (query == 0)
4389 result = pkgConfig.getAll();
4390 else if (query == 1)
4391 result = pkgConfig.getCflags();
4392 else
4393 result = pkgConfig.getLibs();
4394 deplist.insert(packageName);
4395 std::vector<String> list = pkgConfig.getRequireList();
4396 for (unsigned int i = 0 ; i<list.size() ; i++)
4397 {
4398 String depPkgName = list[i];
4399 if (deplist.find(depPkgName) != deplist.end())
4400 continue;
4401 String val;
4402 if (!pkgConfigRecursive(depPkgName, path, prefix, query, val, deplist))
4403 {
4404 error("Based on 'requires' attribute of package '%s'", packageName.c_str());
4405 return false;
4406 }
4407 result.append(" ");
4408 result.append(val);
4409 }
4411 return true;
4412 }
4414 bool MakeBase::pkgConfigQuery(const String &packageName, int query, String &result)
4415 {
4416 std::set<String> deplist;
4417 String path = getProperty("pkg-config-path");
4418 if (path.size()>0)
4419 path = resolve(path);
4420 String prefix = getProperty("pkg-config-prefix");
4421 String val;
4422 if (!pkgConfigRecursive(packageName, path, prefix, query, val, deplist))
4423 return false;
4424 result = val;
4425 return true;
4426 }
4430 /**
4431 * replace a variable ref like ${a} with a value
4432 */
4433 bool MakeBase::lookupProperty(const String &propertyName, String &result)
4434 {
4435 String varname = propertyName;
4436 if (envPrefix.size() > 0 &&
4437 varname.compare(0, envPrefix.size(), envPrefix) == 0)
4438 {
4439 varname = varname.substr(envPrefix.size());
4440 char *envstr = getenv(varname.c_str());
4441 if (!envstr)
4442 {
4443 error("environment variable '%s' not defined", varname.c_str());
4444 return false;
4445 }
4446 result = envstr;
4447 }
4448 else if (pcPrefix.size() > 0 &&
4449 varname.compare(0, pcPrefix.size(), pcPrefix) == 0)
4450 {
4451 varname = varname.substr(pcPrefix.size());
4452 String val;
4453 if (!pkgConfigQuery(varname, 0, val))
4454 return false;
4455 result = val;
4456 }
4457 else if (pccPrefix.size() > 0 &&
4458 varname.compare(0, pccPrefix.size(), pccPrefix) == 0)
4459 {
4460 varname = varname.substr(pccPrefix.size());
4461 String val;
4462 if (!pkgConfigQuery(varname, 1, val))
4463 return false;
4464 result = val;
4465 }
4466 else if (pclPrefix.size() > 0 &&
4467 varname.compare(0, pclPrefix.size(), pclPrefix) == 0)
4468 {
4469 varname = varname.substr(pclPrefix.size());
4470 String val;
4471 if (!pkgConfigQuery(varname, 2, val))
4472 return false;
4473 result = val;
4474 }
4475 else
4476 {
4477 std::map<String, String>::iterator iter;
4478 iter = properties.find(varname);
4479 if (iter != properties.end())
4480 {
4481 result = iter->second;
4482 }
4483 else
4484 {
4485 error("property '%s' not found", varname.c_str());
4486 return false;
4487 }
4488 }
4489 return true;
4490 }
4495 /**
4496 * Analyse a string, looking for any substitutions or other
4497 * things that need resolution
4498 */
4499 bool MakeBase::getSubstitutionsRecursive(const String &str,
4500 String &result, int depth)
4501 {
4502 if (depth > 10)
4503 {
4504 error("nesting of substitutions too deep (>10) for '%s'",
4505 str.c_str());
4506 return false;
4507 }
4508 String s = trim(str);
4509 int len = (int)s.size();
4510 String val;
4511 for (int i=0 ; i<len ; i++)
4512 {
4513 char ch = s[i];
4514 if (ch == '$' && s[i+1] == '{')
4515 {
4516 String varname;
4517 int j = i+2;
4518 for ( ; j<len ; j++)
4519 {
4520 ch = s[j];
4521 if (ch == '$' && s[j+1] == '{')
4522 {
4523 error("attribute %s cannot have nested variable references",
4524 s.c_str());
4525 return false;
4526 }
4527 else if (ch == '}')
4528 {
4529 varname = trim(varname);
4530 String varval;
4531 if (!lookupProperty(varname, varval))
4532 return false;
4533 String varval2;
4534 //Now see if the answer has ${} in it, too
4535 if (!getSubstitutionsRecursive(varval, varval2, depth + 1))
4536 return false;
4537 val.append(varval2);
4538 break;
4539 }
4540 else
4541 {
4542 varname.push_back(ch);
4543 }
4544 }
4545 i = j;
4546 }
4547 else
4548 {
4549 val.push_back(ch);
4550 }
4551 }
4552 result = val;
4553 return true;
4554 }
4556 /**
4557 * Analyse a string, looking for any substitutions or other
4558 * things that need resilution
4559 */
4560 bool MakeBase::getSubstitutions(const String &str, String &result)
4561 {
4562 return getSubstitutionsRecursive(str, result, 0);
4563 }
4567 /**
4568 * replace variable refs like ${a} with their values
4569 * Assume that the string has already been syntax validated
4570 */
4571 String MakeBase::eval(const String &s, const String &defaultVal)
4572 {
4573 if (s.size()==0)
4574 return defaultVal;
4575 String ret;
4576 if (getSubstitutions(s, ret))
4577 return ret;
4578 else
4579 return defaultVal;
4580 }
4583 /**
4584 * replace variable refs like ${a} with their values
4585 * return true or false
4586 * Assume that the string has already been syntax validated
4587 */
4588 bool MakeBase::evalBool(const String &s, bool defaultVal)
4589 {
4590 if (s.size()==0)
4591 return defaultVal;
4592 String val = eval(s, "false");
4593 if (val.size()==0)
4594 return defaultVal;
4595 if (val == "true" || val == "TRUE")
4596 return true;
4597 else
4598 return false;
4599 }
4602 /**
4603 * Get a string attribute, testing it for proper syntax and
4604 * property names.
4605 */
4606 bool MakeBase::getAttribute(Element *elem, const String &name,
4607 String &result)
4608 {
4609 String s = elem->getAttribute(name);
4610 String tmp;
4611 bool ret = getSubstitutions(s, tmp);
4612 if (ret)
4613 result = s; //assign -if- ok
4614 return ret;
4615 }
4618 /**
4619 * Get a string value, testing it for proper syntax and
4620 * property names.
4621 */
4622 bool MakeBase::getValue(Element *elem, String &result)
4623 {
4624 String s = elem->getValue();
4625 String tmp;
4626 bool ret = getSubstitutions(s, tmp);
4627 if (ret)
4628 result = s; //assign -if- ok
4629 return ret;
4630 }
4635 /**
4636 * Parse a <patternset> entry
4637 */
4638 bool MakeBase::parsePatternSet(Element *elem,
4639 MakeBase &propRef,
4640 std::vector<String> &includes,
4641 std::vector<String> &excludes
4642 )
4643 {
4644 std::vector<Element *> children = elem->getChildren();
4645 for (unsigned int i=0 ; i<children.size() ; i++)
4646 {
4647 Element *child = children[i];
4648 String tagName = child->getName();
4649 if (tagName == "exclude")
4650 {
4651 String fname;
4652 if (!propRef.getAttribute(child, "name", fname))
4653 return false;
4654 //trace("EXCLUDE: %s", fname.c_str());
4655 excludes.push_back(fname);
4656 }
4657 else if (tagName == "include")
4658 {
4659 String fname;
4660 if (!propRef.getAttribute(child, "name", fname))
4661 return false;
4662 //trace("INCLUDE: %s", fname.c_str());
4663 includes.push_back(fname);
4664 }
4665 }
4667 return true;
4668 }
4673 /**
4674 * Parse a <fileset> entry, and determine which files
4675 * should be included
4676 */
4677 bool MakeBase::parseFileSet(Element *elem,
4678 MakeBase &propRef,
4679 FileSet &fileSet)
4680 {
4681 String name = elem->getName();
4682 if (name != "fileset")
4683 {
4684 error("expected <fileset>");
4685 return false;
4686 }
4689 std::vector<String> includes;
4690 std::vector<String> excludes;
4692 //A fileset has one implied patternset
4693 if (!parsePatternSet(elem, propRef, includes, excludes))
4694 {
4695 return false;
4696 }
4697 //Look for child tags, including more patternsets
4698 std::vector<Element *> children = elem->getChildren();
4699 for (unsigned int i=0 ; i<children.size() ; i++)
4700 {
4701 Element *child = children[i];
4702 String tagName = child->getName();
4703 if (tagName == "patternset")
4704 {
4705 if (!parsePatternSet(child, propRef, includes, excludes))
4706 {
4707 return false;
4708 }
4709 }
4710 }
4712 String dir;
4713 //Now do the stuff
4714 //Get the base directory for reading file names
4715 if (!propRef.getAttribute(elem, "dir", dir))
4716 return false;
4718 fileSet.setDirectory(dir);
4719 fileSet.setIncludes(includes);
4720 fileSet.setExcludes(excludes);
4722 /*
4723 std::vector<String> fileList;
4724 if (dir.size() > 0)
4725 {
4726 String baseDir = propRef.resolve(dir);
4727 if (!listFiles(baseDir, "", includes, excludes, fileList))
4728 return false;
4729 }
4730 std::sort(fileList.begin(), fileList.end());
4731 result = fileList;
4732 */
4735 /*
4736 for (unsigned int i=0 ; i<result.size() ; i++)
4737 {
4738 trace("RES:%s", result[i].c_str());
4739 }
4740 */
4743 return true;
4744 }
4746 /**
4747 * Parse a <filelist> entry. This is far simpler than FileSet,
4748 * since no directory scanning is needed. The file names are listed
4749 * explicitly.
4750 */
4751 bool MakeBase::parseFileList(Element *elem,
4752 MakeBase &propRef,
4753 FileList &fileList)
4754 {
4755 std::vector<String> fnames;
4756 //Look for child tags, namely "file"
4757 std::vector<Element *> children = elem->getChildren();
4758 for (unsigned int i=0 ; i<children.size() ; i++)
4759 {
4760 Element *child = children[i];
4761 String tagName = child->getName();
4762 if (tagName == "file")
4763 {
4764 String fname = child->getAttribute("name");
4765 if (fname.size()==0)
4766 {
4767 error("<file> element requires name="" attribute");
4768 return false;
4769 }
4770 fnames.push_back(fname);
4771 }
4772 else
4773 {
4774 error("tag <%s> not allowed in <fileset>", tagName.c_str());
4775 return false;
4776 }
4777 }
4779 String dir;
4780 //Get the base directory for reading file names
4781 if (!propRef.getAttribute(elem, "dir", dir))
4782 return false;
4783 fileList.setDirectory(dir);
4784 fileList.setFiles(fnames);
4786 return true;
4787 }
4791 /**
4792 * Create a directory, making intermediate dirs
4793 * if necessary
4794 */
4795 bool MakeBase::createDirectory(const String &dirname)
4796 {
4797 //trace("## createDirectory: %s", dirname.c_str());
4798 //## first check if it exists
4799 struct stat finfo;
4800 String nativeDir = getNativePath(dirname);
4801 char *cnative = (char *) nativeDir.c_str();
4802 #ifdef __WIN32__
4803 if (strlen(cnative)==2 && cnative[1]==':')
4804 return true;
4805 #endif
4806 if (stat(cnative, &finfo)==0)
4807 {
4808 if (!S_ISDIR(finfo.st_mode))
4809 {
4810 error("mkdir: file %s exists but is not a directory",
4811 cnative);
4812 return false;
4813 }
4814 else //exists
4815 {
4816 return true;
4817 }
4818 }
4820 //## 2: pull off the last path segment, if any,
4821 //## to make the dir 'above' this one, if necessary
4822 unsigned int pos = dirname.find_last_of('/');
4823 if (pos>0 && pos != dirname.npos)
4824 {
4825 String subpath = dirname.substr(0, pos);
4826 //A letter root (c:) ?
4827 if (!createDirectory(subpath))
4828 return false;
4829 }
4831 //## 3: now make
4832 #ifdef __WIN32__
4833 if (mkdir(cnative)<0)
4834 #else
4835 if (mkdir(cnative, S_IRWXU | S_IRWXG | S_IRWXO)<0)
4836 #endif
4837 {
4838 error("cannot make directory '%s' : %s",
4839 cnative, strerror(errno));
4840 return false;
4841 }
4843 return true;
4844 }
4847 /**
4848 * Remove a directory recursively
4849 */
4850 bool MakeBase::removeDirectory(const String &dirName)
4851 {
4852 char *dname = (char *)dirName.c_str();
4854 DIR *dir = opendir(dname);
4855 if (!dir)
4856 {
4857 //# Let this fail nicely.
4858 return true;
4859 //error("error opening directory %s : %s", dname, strerror(errno));
4860 //return false;
4861 }
4863 while (true)
4864 {
4865 struct dirent *de = readdir(dir);
4866 if (!de)
4867 break;
4869 //Get the directory member name
4870 String s = de->d_name;
4871 if (s.size() == 0 || s[0] == '.')
4872 continue;
4873 String childName;
4874 if (dirName.size() > 0)
4875 {
4876 childName.append(dirName);
4877 childName.append("/");
4878 }
4879 childName.append(s);
4882 struct stat finfo;
4883 String childNative = getNativePath(childName);
4884 char *cnative = (char *)childNative.c_str();
4885 if (stat(cnative, &finfo)<0)
4886 {
4887 error("cannot stat file:%s", cnative);
4888 }
4889 else if (S_ISDIR(finfo.st_mode))
4890 {
4891 //trace("DEL dir: %s", childName.c_str());
4892 if (!removeDirectory(childName))
4893 {
4894 return false;
4895 }
4896 }
4897 else if (!S_ISREG(finfo.st_mode))
4898 {
4899 //trace("not regular: %s", cnative);
4900 }
4901 else
4902 {
4903 //trace("DEL file: %s", childName.c_str());
4904 if (remove(cnative)<0)
4905 {
4906 error("error deleting %s : %s",
4907 cnative, strerror(errno));
4908 return false;
4909 }
4910 }
4911 }
4912 closedir(dir);
4914 //Now delete the directory
4915 String native = getNativePath(dirName);
4916 if (rmdir(native.c_str())<0)
4917 {
4918 error("could not delete directory %s : %s",
4919 native.c_str() , strerror(errno));
4920 return false;
4921 }
4923 return true;
4925 }
4928 /**
4929 * Copy a file from one name to another. Perform only if needed
4930 */
4931 bool MakeBase::copyFile(const String &srcFile, const String &destFile)
4932 {
4933 //# 1 Check up-to-date times
4934 String srcNative = getNativePath(srcFile);
4935 struct stat srcinfo;
4936 if (stat(srcNative.c_str(), &srcinfo)<0)
4937 {
4938 error("source file %s for copy does not exist",
4939 srcNative.c_str());
4940 return false;
4941 }
4943 String destNative = getNativePath(destFile);
4944 struct stat destinfo;
4945 if (stat(destNative.c_str(), &destinfo)==0)
4946 {
4947 if (destinfo.st_mtime >= srcinfo.st_mtime)
4948 return true;
4949 }
4951 //# 2 prepare a destination directory if necessary
4952 unsigned int pos = destFile.find_last_of('/');
4953 if (pos != destFile.npos)
4954 {
4955 String subpath = destFile.substr(0, pos);
4956 if (!createDirectory(subpath))
4957 return false;
4958 }
4960 //# 3 do the data copy
4961 #ifndef __WIN32__
4963 FILE *srcf = fopen(srcNative.c_str(), "rb");
4964 if (!srcf)
4965 {
4966 error("copyFile cannot open '%s' for reading", srcNative.c_str());
4967 return false;
4968 }
4969 FILE *destf = fopen(destNative.c_str(), "wb");
4970 if (!destf)
4971 {
4972 error("copyFile cannot open %s for writing", srcNative.c_str());
4973 return false;
4974 }
4976 while (!feof(srcf))
4977 {
4978 int ch = fgetc(srcf);
4979 if (ch<0)
4980 break;
4981 fputc(ch, destf);
4982 }
4984 fclose(destf);
4985 fclose(srcf);
4987 #else
4989 if (!CopyFile(srcNative.c_str(), destNative.c_str(), false))
4990 {
4991 error("copyFile from %s to %s failed",
4992 srcNative.c_str(), destNative.c_str());
4993 return false;
4994 }
4996 #endif /* __WIN32__ */
4999 return true;
5000 }
5004 /**
5005 * Tests if the file exists and is a regular file
5006 */
5007 bool MakeBase::isRegularFile(const String &fileName)
5008 {
5009 String native = getNativePath(fileName);
5010 struct stat finfo;
5012 //Exists?
5013 if (stat(native.c_str(), &finfo)<0)
5014 return false;
5017 //check the file mode
5018 if (!S_ISREG(finfo.st_mode))
5019 return false;
5021 return true;
5022 }
5024 /**
5025 * Tests if the file exists and is a directory
5026 */
5027 bool MakeBase::isDirectory(const String &fileName)
5028 {
5029 String native = getNativePath(fileName);
5030 struct stat finfo;
5032 //Exists?
5033 if (stat(native.c_str(), &finfo)<0)
5034 return false;
5037 //check the file mode
5038 if (!S_ISDIR(finfo.st_mode))
5039 return false;
5041 return true;
5042 }
5046 /**
5047 * Tests is the modification of fileA is newer than fileB
5048 */
5049 bool MakeBase::isNewerThan(const String &fileA, const String &fileB)
5050 {
5051 //trace("isNewerThan:'%s' , '%s'", fileA.c_str(), fileB.c_str());
5052 String nativeA = getNativePath(fileA);
5053 struct stat infoA;
5054 //IF source does not exist, NOT newer
5055 if (stat(nativeA.c_str(), &infoA)<0)
5056 {
5057 return false;
5058 }
5060 String nativeB = getNativePath(fileB);
5061 struct stat infoB;
5062 //IF dest does not exist, YES, newer
5063 if (stat(nativeB.c_str(), &infoB)<0)
5064 {
5065 return true;
5066 }
5068 //check the actual times
5069 if (infoA.st_mtime > infoB.st_mtime)
5070 {
5071 return true;
5072 }
5074 return false;
5075 }
5078 //########################################################################
5079 //# P K G C O N F I G
5080 //########################################################################
5083 /**
5084 * Get a character from the buffer at pos. If out of range,
5085 * return -1 for safety
5086 */
5087 int PkgConfig::get(int pos)
5088 {
5089 if (pos>parselen)
5090 return -1;
5091 return parsebuf[pos];
5092 }
5096 /**
5097 * Skip over all whitespace characters beginning at pos. Return
5098 * the position of the first non-whitespace character.
5099 * Pkg-config is line-oriented, so check for newline
5100 */
5101 int PkgConfig::skipwhite(int pos)
5102 {
5103 while (pos < parselen)
5104 {
5105 int ch = get(pos);
5106 if (ch < 0)
5107 break;
5108 if (!isspace(ch))
5109 break;
5110 pos++;
5111 }
5112 return pos;
5113 }
5116 /**
5117 * Parse the buffer beginning at pos, for a word. Fill
5118 * 'ret' with the result. Return the position after the
5119 * word.
5120 */
5121 int PkgConfig::getword(int pos, String &ret)
5122 {
5123 while (pos < parselen)
5124 {
5125 int ch = get(pos);
5126 if (ch < 0)
5127 break;
5128 if (!isalnum(ch) && ch != '_' && ch != '-' && ch != '+' && ch != '.')
5129 break;
5130 ret.push_back((char)ch);
5131 pos++;
5132 }
5133 return pos;
5134 }
5136 bool PkgConfig::parseRequires()
5137 {
5138 if (requires.size() == 0)
5139 return true;
5140 parsebuf = (char *)requires.c_str();
5141 parselen = requires.size();
5142 int pos = 0;
5143 while (pos < parselen)
5144 {
5145 pos = skipwhite(pos);
5146 String val;
5147 int pos2 = getword(pos, val);
5148 if (pos2 == pos)
5149 break;
5150 pos = pos2;
5151 //trace("val %s", val.c_str());
5152 requireList.push_back(val);
5153 }
5154 return true;
5155 }
5158 static int getint(const String str)
5159 {
5160 char *s = (char *)str.c_str();
5161 char *ends = NULL;
5162 long val = strtol(s, &ends, 10);
5163 if (ends == s)
5164 return 0L;
5165 else
5166 return val;
5167 }
5169 void PkgConfig::parseVersion()
5170 {
5171 if (version.size() == 0)
5172 return;
5173 String s1, s2, s3;
5174 unsigned int pos = 0;
5175 unsigned int pos2 = version.find('.', pos);
5176 if (pos2 == version.npos)
5177 {
5178 s1 = version;
5179 }
5180 else
5181 {
5182 s1 = version.substr(pos, pos2-pos);
5183 pos = pos2;
5184 pos++;
5185 if (pos < version.size())
5186 {
5187 pos2 = version.find('.', pos);
5188 if (pos2 == version.npos)
5189 {
5190 s2 = version.substr(pos, version.size()-pos);
5191 }
5192 else
5193 {
5194 s2 = version.substr(pos, pos2-pos);
5195 pos = pos2;
5196 pos++;
5197 if (pos < version.size())
5198 s3 = version.substr(pos, pos2-pos);
5199 }
5200 }
5201 }
5203 majorVersion = getint(s1);
5204 minorVersion = getint(s2);
5205 microVersion = getint(s3);
5206 //trace("version:%d.%d.%d", majorVersion,
5207 // minorVersion, microVersion );
5208 }
5211 bool PkgConfig::parseLine(const String &lineBuf)
5212 {
5213 parsebuf = (char *)lineBuf.c_str();
5214 parselen = lineBuf.size();
5215 int pos = 0;
5217 while (pos < parselen)
5218 {
5219 String attrName;
5220 pos = skipwhite(pos);
5221 int ch = get(pos);
5222 if (ch == '#')
5223 {
5224 //comment. eat the rest of the line
5225 while (pos < parselen)
5226 {
5227 ch = get(pos);
5228 if (ch == '\n' || ch < 0)
5229 break;
5230 pos++;
5231 }
5232 continue;
5233 }
5234 pos = getword(pos, attrName);
5235 if (attrName.size() == 0)
5236 continue;
5238 pos = skipwhite(pos);
5239 ch = get(pos);
5240 if (ch != ':' && ch != '=')
5241 {
5242 error("expected ':' or '='");
5243 return false;
5244 }
5245 pos++;
5246 pos = skipwhite(pos);
5247 String attrVal;
5248 while (pos < parselen)
5249 {
5250 ch = get(pos);
5251 if (ch == '\n' || ch < 0)
5252 break;
5253 else if (ch == '$' && get(pos+1) == '{')
5254 {
5255 //# this is a ${substitution}
5256 pos += 2;
5257 String subName;
5258 while (pos < parselen)
5259 {
5260 ch = get(pos);
5261 if (ch < 0)
5262 {
5263 error("unterminated substitution");
5264 return false;
5265 }
5266 else if (ch == '}')
5267 break;
5268 else
5269 subName.push_back((char)ch);
5270 pos++;
5271 }
5272 //trace("subName:%s %s", subName.c_str(), prefix.c_str());
5273 if (subName == "prefix" && prefix.size()>0)
5274 {
5275 attrVal.append(prefix);
5276 //trace("prefix override:%s", prefix.c_str());
5277 }
5278 else
5279 {
5280 String subVal = attrs[subName];
5281 //trace("subVal:%s", subVal.c_str());
5282 attrVal.append(subVal);
5283 }
5284 }
5285 else
5286 attrVal.push_back((char)ch);
5287 pos++;
5288 }
5290 attrVal = trim(attrVal);
5291 attrs[attrName] = attrVal;
5293 String attrNameL = toLower(attrName);
5295 if (attrNameL == "name")
5296 name = attrVal;
5297 else if (attrNameL == "description")
5298 description = attrVal;
5299 else if (attrNameL == "cflags")
5300 cflags = attrVal;
5301 else if (attrNameL == "libs")
5302 libs = attrVal;
5303 else if (attrNameL == "requires")
5304 requires = attrVal;
5305 else if (attrNameL == "version")
5306 version = attrVal;
5308 //trace("name:'%s' value:'%s'",
5309 // attrName.c_str(), attrVal.c_str());
5310 }
5312 return true;
5313 }
5316 bool PkgConfig::parse(const String &buf)
5317 {
5318 init();
5320 String line;
5321 int lineNr = 0;
5322 for (unsigned int p=0 ; p<buf.size() ; p++)
5323 {
5324 int ch = buf[p];
5325 if (ch == '\n' || ch == '\r')
5326 {
5327 if (!parseLine(line))
5328 return false;
5329 line.clear();
5330 lineNr++;
5331 }
5332 else
5333 {
5334 line.push_back(ch);
5335 }
5336 }
5337 if (line.size()>0)
5338 {
5339 if (!parseLine(line))
5340 return false;
5341 }
5343 parseRequires();
5344 parseVersion();
5346 return true;
5347 }
5352 void PkgConfig::dumpAttrs()
5353 {
5354 //trace("### PkgConfig attributes for %s", fileName.c_str());
5355 std::map<String, String>::iterator iter;
5356 for (iter=attrs.begin() ; iter!=attrs.end() ; iter++)
5357 {
5358 trace(" %s = %s", iter->first.c_str(), iter->second.c_str());
5359 }
5360 }
5363 bool PkgConfig::readFile(const String &fname)
5364 {
5365 fileName = getNativePath(fname);
5367 FILE *f = fopen(fileName.c_str(), "r");
5368 if (!f)
5369 {
5370 error("cannot open file '%s' for reading", fileName.c_str());
5371 return false;
5372 }
5373 String buf;
5374 while (true)
5375 {
5376 int ch = fgetc(f);
5377 if (ch < 0)
5378 break;
5379 buf.push_back((char)ch);
5380 }
5381 fclose(f);
5383 //trace("####### File:\n%s", buf.c_str());
5384 if (!parse(buf))
5385 {
5386 return false;
5387 }
5389 //dumpAttrs();
5391 return true;
5392 }
5396 bool PkgConfig::query(const String &pkgName)
5397 {
5398 name = pkgName;
5400 String fname = path;
5401 fname.append("/");
5402 fname.append(name);
5403 fname.append(".pc");
5405 if (!readFile(fname))
5406 return false;
5408 return true;
5409 }
5415 //########################################################################
5416 //# D E P T O O L
5417 //########################################################################
5421 /**
5422 * Class which holds information for each file.
5423 */
5424 class FileRec
5425 {
5426 public:
5428 typedef enum
5429 {
5430 UNKNOWN,
5431 CFILE,
5432 HFILE,
5433 OFILE
5434 } FileType;
5436 /**
5437 * Constructor
5438 */
5439 FileRec()
5440 { init(); type = UNKNOWN; }
5442 /**
5443 * Copy constructor
5444 */
5445 FileRec(const FileRec &other)
5446 { init(); assign(other); }
5447 /**
5448 * Constructor
5449 */
5450 FileRec(int typeVal)
5451 { init(); type = typeVal; }
5452 /**
5453 * Assignment operator
5454 */
5455 FileRec &operator=(const FileRec &other)
5456 { init(); assign(other); return *this; }
5459 /**
5460 * Destructor
5461 */
5462 ~FileRec()
5463 {}
5465 /**
5466 * Directory part of the file name
5467 */
5468 String path;
5470 /**
5471 * Base name, sans directory and suffix
5472 */
5473 String baseName;
5475 /**
5476 * File extension, such as cpp or h
5477 */
5478 String suffix;
5480 /**
5481 * Type of file: CFILE, HFILE, OFILE
5482 */
5483 int type;
5485 /**
5486 * Used to list files ref'd by this one
5487 */
5488 std::map<String, FileRec *> files;
5491 private:
5493 void init()
5494 {
5495 }
5497 void assign(const FileRec &other)
5498 {
5499 type = other.type;
5500 baseName = other.baseName;
5501 suffix = other.suffix;
5502 files = other.files;
5503 }
5505 };
5509 /**
5510 * Simpler dependency record
5511 */
5512 class DepRec
5513 {
5514 public:
5516 /**
5517 * Constructor
5518 */
5519 DepRec()
5520 {init();}
5522 /**
5523 * Copy constructor
5524 */
5525 DepRec(const DepRec &other)
5526 {init(); assign(other);}
5527 /**
5528 * Constructor
5529 */
5530 DepRec(const String &fname)
5531 {init(); name = fname; }
5532 /**
5533 * Assignment operator
5534 */
5535 DepRec &operator=(const DepRec &other)
5536 {init(); assign(other); return *this;}
5539 /**
5540 * Destructor
5541 */
5542 ~DepRec()
5543 {}
5545 /**
5546 * Directory part of the file name
5547 */
5548 String path;
5550 /**
5551 * Base name, without the path and suffix
5552 */
5553 String name;
5555 /**
5556 * Suffix of the source
5557 */
5558 String suffix;
5561 /**
5562 * Used to list files ref'd by this one
5563 */
5564 std::vector<String> files;
5567 private:
5569 void init()
5570 {
5571 }
5573 void assign(const DepRec &other)
5574 {
5575 path = other.path;
5576 name = other.name;
5577 suffix = other.suffix;
5578 files = other.files; //avoid recursion
5579 }
5581 };
5584 class DepTool : public MakeBase
5585 {
5586 public:
5588 /**
5589 * Constructor
5590 */
5591 DepTool()
5592 { init(); }
5594 /**
5595 * Copy constructor
5596 */
5597 DepTool(const DepTool &other)
5598 { init(); assign(other); }
5600 /**
5601 * Assignment operator
5602 */
5603 DepTool &operator=(const DepTool &other)
5604 { init(); assign(other); return *this; }
5607 /**
5608 * Destructor
5609 */
5610 ~DepTool()
5611 {}
5614 /**
5615 * Reset this section of code
5616 */
5617 virtual void init();
5619 /**
5620 * Reset this section of code
5621 */
5622 virtual void assign(const DepTool &other)
5623 {
5624 }
5626 /**
5627 * Sets the source directory which will be scanned
5628 */
5629 virtual void setSourceDirectory(const String &val)
5630 { sourceDir = val; }
5632 /**
5633 * Returns the source directory which will be scanned
5634 */
5635 virtual String getSourceDirectory()
5636 { return sourceDir; }
5638 /**
5639 * Sets the list of files within the directory to analyze
5640 */
5641 virtual void setFileList(const std::vector<String> &list)
5642 { fileList = list; }
5644 /**
5645 * Creates the list of all file names which will be
5646 * candidates for further processing. Reads make.exclude
5647 * to see which files for directories to leave out.
5648 */
5649 virtual bool createFileList();
5652 /**
5653 * Generates the forward dependency list
5654 */
5655 virtual bool generateDependencies();
5658 /**
5659 * Generates the forward dependency list, saving the file
5660 */
5661 virtual bool generateDependencies(const String &);
5664 /**
5665 * Load a dependency file
5666 */
5667 std::vector<DepRec> loadDepFile(const String &fileName);
5669 /**
5670 * Load a dependency file, generating one if necessary
5671 */
5672 std::vector<DepRec> getDepFile(const String &fileName,
5673 bool forceRefresh);
5675 /**
5676 * Save a dependency file
5677 */
5678 bool saveDepFile(const String &fileName);
5681 private:
5684 /**
5685 *
5686 */
5687 void parseName(const String &fullname,
5688 String &path,
5689 String &basename,
5690 String &suffix);
5692 /**
5693 *
5694 */
5695 int get(int pos);
5697 /**
5698 *
5699 */
5700 int skipwhite(int pos);
5702 /**
5703 *
5704 */
5705 int getword(int pos, String &ret);
5707 /**
5708 *
5709 */
5710 bool sequ(int pos, const char *key);
5712 /**
5713 *
5714 */
5715 bool addIncludeFile(FileRec *frec, const String &fname);
5717 /**
5718 *
5719 */
5720 bool scanFile(const String &fname, FileRec *frec);
5722 /**
5723 *
5724 */
5725 bool processDependency(FileRec *ofile, FileRec *include);
5727 /**
5728 *
5729 */
5730 String sourceDir;
5732 /**
5733 *
5734 */
5735 std::vector<String> fileList;
5737 /**
5738 *
5739 */
5740 std::vector<String> directories;
5742 /**
5743 * A list of all files which will be processed for
5744 * dependencies.
5745 */
5746 std::map<String, FileRec *> allFiles;
5748 /**
5749 * The list of .o files, and the
5750 * dependencies upon them.
5751 */
5752 std::map<String, FileRec *> oFiles;
5754 int depFileSize;
5755 char *depFileBuf;
5757 static const int readBufSize = 8192;
5758 char readBuf[8193];//byte larger
5760 };
5766 /**
5767 * Clean up after processing. Called by the destructor, but should
5768 * also be called before the object is reused.
5769 */
5770 void DepTool::init()
5771 {
5772 sourceDir = ".";
5774 fileList.clear();
5775 directories.clear();
5777 //clear output file list
5778 std::map<String, FileRec *>::iterator iter;
5779 for (iter=oFiles.begin(); iter!=oFiles.end() ; iter++)
5780 delete iter->second;
5781 oFiles.clear();
5783 //allFiles actually contains the master copies. delete them
5784 for (iter= allFiles.begin(); iter!=allFiles.end() ; iter++)
5785 delete iter->second;
5786 allFiles.clear();
5788 }
5793 /**
5794 * Parse a full path name into path, base name, and suffix
5795 */
5796 void DepTool::parseName(const String &fullname,
5797 String &path,
5798 String &basename,
5799 String &suffix)
5800 {
5801 if (fullname.size() < 2)
5802 return;
5804 unsigned int pos = fullname.find_last_of('/');
5805 if (pos != fullname.npos && pos<fullname.size()-1)
5806 {
5807 path = fullname.substr(0, pos);
5808 pos++;
5809 basename = fullname.substr(pos, fullname.size()-pos);
5810 }
5811 else
5812 {
5813 path = "";
5814 basename = fullname;
5815 }
5817 pos = basename.find_last_of('.');
5818 if (pos != basename.npos && pos<basename.size()-1)
5819 {
5820 suffix = basename.substr(pos+1, basename.size()-pos-1);
5821 basename = basename.substr(0, pos);
5822 }
5824 //trace("parsename:%s %s %s", path.c_str(),
5825 // basename.c_str(), suffix.c_str());
5826 }
5830 /**
5831 * Generate our internal file list.
5832 */
5833 bool DepTool::createFileList()
5834 {
5836 for (unsigned int i=0 ; i<fileList.size() ; i++)
5837 {
5838 String fileName = fileList[i];
5839 //trace("## FileName:%s", fileName.c_str());
5840 String path;
5841 String basename;
5842 String sfx;
5843 parseName(fileName, path, basename, sfx);
5844 if (sfx == "cpp" || sfx == "c" || sfx == "cxx" ||
5845 sfx == "cc" || sfx == "CC")
5846 {
5847 FileRec *fe = new FileRec(FileRec::CFILE);
5848 fe->path = path;
5849 fe->baseName = basename;
5850 fe->suffix = sfx;
5851 allFiles[fileName] = fe;
5852 }
5853 else if (sfx == "h" || sfx == "hh" ||
5854 sfx == "hpp" || sfx == "hxx")
5855 {
5856 FileRec *fe = new FileRec(FileRec::HFILE);
5857 fe->path = path;
5858 fe->baseName = basename;
5859 fe->suffix = sfx;
5860 allFiles[fileName] = fe;
5861 }
5862 }
5864 if (!listDirectories(sourceDir, "", directories))
5865 return false;
5867 return true;
5868 }
5874 /**
5875 * Get a character from the buffer at pos. If out of range,
5876 * return -1 for safety
5877 */
5878 int DepTool::get(int pos)
5879 {
5880 if (pos>depFileSize)
5881 return -1;
5882 return depFileBuf[pos];
5883 }
5887 /**
5888 * Skip over all whitespace characters beginning at pos. Return
5889 * the position of the first non-whitespace character.
5890 */
5891 int DepTool::skipwhite(int pos)
5892 {
5893 while (pos < depFileSize)
5894 {
5895 int ch = get(pos);
5896 if (ch < 0)
5897 break;
5898 if (!isspace(ch))
5899 break;
5900 pos++;
5901 }
5902 return pos;
5903 }
5906 /**
5907 * Parse the buffer beginning at pos, for a word. Fill
5908 * 'ret' with the result. Return the position after the
5909 * word.
5910 */
5911 int DepTool::getword(int pos, String &ret)
5912 {
5913 while (pos < depFileSize)
5914 {
5915 int ch = get(pos);
5916 if (ch < 0)
5917 break;
5918 if (isspace(ch))
5919 break;
5920 ret.push_back((char)ch);
5921 pos++;
5922 }
5923 return pos;
5924 }
5926 /**
5927 * Return whether the sequence of characters in the buffer
5928 * beginning at pos match the key, for the length of the key
5929 */
5930 bool DepTool::sequ(int pos, const char *key)
5931 {
5932 while (*key)
5933 {
5934 if (*key != get(pos))
5935 return false;
5936 key++; pos++;
5937 }
5938 return true;
5939 }
5943 /**
5944 * Add an include file name to a file record. If the name
5945 * is not found in allFiles explicitly, try prepending include
5946 * directory names to it and try again.
5947 */
5948 bool DepTool::addIncludeFile(FileRec *frec, const String &iname)
5949 {
5950 //# if the name is an exact match to a path name
5951 //# in allFiles, like "myinc.h"
5952 std::map<String, FileRec *>::iterator iter =
5953 allFiles.find(iname);
5954 if (iter != allFiles.end()) //already exists
5955 {
5956 //h file in same dir
5957 FileRec *other = iter->second;
5958 //trace("local: '%s'", iname.c_str());
5959 frec->files[iname] = other;
5960 return true;
5961 }
5962 else
5963 {
5964 //## Ok, it was not found directly
5965 //look in other dirs
5966 std::vector<String>::iterator diter;
5967 for (diter=directories.begin() ;
5968 diter!=directories.end() ; diter++)
5969 {
5970 String dfname = *diter;
5971 dfname.append("/");
5972 dfname.append(iname);
5973 URI fullPathURI(dfname); //normalize path name
5974 String fullPath = fullPathURI.getPath();
5975 if (fullPath[0] == '/')
5976 fullPath = fullPath.substr(1);
5977 //trace("Normalized %s to %s", dfname.c_str(), fullPath.c_str());
5978 iter = allFiles.find(fullPath);
5979 if (iter != allFiles.end())
5980 {
5981 FileRec *other = iter->second;
5982 //trace("other: '%s'", iname.c_str());
5983 frec->files[fullPath] = other;
5984 return true;
5985 }
5986 }
5987 }
5988 return true;
5989 }
5993 /**
5994 * Lightly parse a file to find the #include directives. Do
5995 * a bit of state machine stuff to make sure that the directive
5996 * is valid. (Like not in a comment).
5997 */
5998 bool DepTool::scanFile(const String &fname, FileRec *frec)
5999 {
6000 String fileName;
6001 if (sourceDir.size() > 0)
6002 {
6003 fileName.append(sourceDir);
6004 fileName.append("/");
6005 }
6006 fileName.append(fname);
6007 String nativeName = getNativePath(fileName);
6008 FILE *f = fopen(nativeName.c_str(), "r");
6009 if (!f)
6010 {
6011 error("Could not open '%s' for reading", fname.c_str());
6012 return false;
6013 }
6014 String buf;
6015 while (!feof(f))
6016 {
6017 int nrbytes = fread(readBuf, 1, readBufSize, f);
6018 readBuf[nrbytes] = '\0';
6019 buf.append(readBuf);
6020 }
6021 fclose(f);
6023 depFileSize = buf.size();
6024 depFileBuf = (char *)buf.c_str();
6025 int pos = 0;
6028 while (pos < depFileSize)
6029 {
6030 //trace("p:%c", get(pos));
6032 //# Block comment
6033 if (get(pos) == '/' && get(pos+1) == '*')
6034 {
6035 pos += 2;
6036 while (pos < depFileSize)
6037 {
6038 if (get(pos) == '*' && get(pos+1) == '/')
6039 {
6040 pos += 2;
6041 break;
6042 }
6043 else
6044 pos++;
6045 }
6046 }
6047 //# Line comment
6048 else if (get(pos) == '/' && get(pos+1) == '/')
6049 {
6050 pos += 2;
6051 while (pos < depFileSize)
6052 {
6053 if (get(pos) == '\n')
6054 {
6055 pos++;
6056 break;
6057 }
6058 else
6059 pos++;
6060 }
6061 }
6062 //# #include! yaay
6063 else if (sequ(pos, "#include"))
6064 {
6065 pos += 8;
6066 pos = skipwhite(pos);
6067 String iname;
6068 pos = getword(pos, iname);
6069 if (iname.size()>2)
6070 {
6071 iname = iname.substr(1, iname.size()-2);
6072 addIncludeFile(frec, iname);
6073 }
6074 }
6075 else
6076 {
6077 pos++;
6078 }
6079 }
6081 return true;
6082 }
6086 /**
6087 * Recursively check include lists to find all files in allFiles to which
6088 * a given file is dependent.
6089 */
6090 bool DepTool::processDependency(FileRec *ofile, FileRec *include)
6091 {
6092 std::map<String, FileRec *>::iterator iter;
6093 for (iter=include->files.begin() ; iter!=include->files.end() ; iter++)
6094 {
6095 String fname = iter->first;
6096 if (ofile->files.find(fname) != ofile->files.end())
6097 {
6098 //trace("file '%s' already seen", fname.c_str());
6099 continue;
6100 }
6101 FileRec *child = iter->second;
6102 ofile->files[fname] = child;
6104 processDependency(ofile, child);
6105 }
6108 return true;
6109 }
6115 /**
6116 * Generate the file dependency list.
6117 */
6118 bool DepTool::generateDependencies()
6119 {
6120 std::map<String, FileRec *>::iterator iter;
6121 //# First pass. Scan for all includes
6122 for (iter=allFiles.begin() ; iter!=allFiles.end() ; iter++)
6123 {
6124 FileRec *frec = iter->second;
6125 if (!scanFile(iter->first, frec))
6126 {
6127 //quit?
6128 }
6129 }
6131 //# Second pass. Scan for all includes
6132 for (iter=allFiles.begin() ; iter!=allFiles.end() ; iter++)
6133 {
6134 FileRec *include = iter->second;
6135 if (include->type == FileRec::CFILE)
6136 {
6137 //String cFileName = iter->first;
6138 FileRec *ofile = new FileRec(FileRec::OFILE);
6139 ofile->path = include->path;
6140 ofile->baseName = include->baseName;
6141 ofile->suffix = include->suffix;
6142 String fname = include->path;
6143 if (fname.size()>0)
6144 fname.append("/");
6145 fname.append(include->baseName);
6146 fname.append(".o");
6147 oFiles[fname] = ofile;
6148 //add the .c file first? no, don't
6149 //ofile->files[cFileName] = include;
6151 //trace("ofile:%s", fname.c_str());
6153 processDependency(ofile, include);
6154 }
6155 }
6158 return true;
6159 }
6163 /**
6164 * High-level call to generate deps and optionally save them
6165 */
6166 bool DepTool::generateDependencies(const String &fileName)
6167 {
6168 if (!createFileList())
6169 return false;
6170 if (!generateDependencies())
6171 return false;
6172 if (!saveDepFile(fileName))
6173 return false;
6174 return true;
6175 }
6178 /**
6179 * This saves the dependency cache.
6180 */
6181 bool DepTool::saveDepFile(const String &fileName)
6182 {
6183 time_t tim;
6184 time(&tim);
6186 FILE *f = fopen(fileName.c_str(), "w");
6187 if (!f)
6188 {
6189 trace("cannot open '%s' for writing", fileName.c_str());
6190 }
6191 fprintf(f, "<?xml version='1.0'?>\n");
6192 fprintf(f, "<!--\n");
6193 fprintf(f, "########################################################\n");
6194 fprintf(f, "## File: build.dep\n");
6195 fprintf(f, "## Generated by BuildTool at :%s", ctime(&tim));
6196 fprintf(f, "########################################################\n");
6197 fprintf(f, "-->\n");
6199 fprintf(f, "<dependencies source='%s'>\n\n", sourceDir.c_str());
6200 std::map<String, FileRec *>::iterator iter;
6201 for (iter=oFiles.begin() ; iter!=oFiles.end() ; iter++)
6202 {
6203 FileRec *frec = iter->second;
6204 if (frec->type == FileRec::OFILE)
6205 {
6206 fprintf(f, "<object path='%s' name='%s' suffix='%s'>\n",
6207 frec->path.c_str(), frec->baseName.c_str(), frec->suffix.c_str());
6208 std::map<String, FileRec *>::iterator citer;
6209 for (citer=frec->files.begin() ; citer!=frec->files.end() ; citer++)
6210 {
6211 String cfname = citer->first;
6212 fprintf(f, " <dep name='%s'/>\n", cfname.c_str());
6213 }
6214 fprintf(f, "</object>\n\n");
6215 }
6216 }
6218 fprintf(f, "</dependencies>\n");
6219 fprintf(f, "\n");
6220 fprintf(f, "<!--\n");
6221 fprintf(f, "########################################################\n");
6222 fprintf(f, "## E N D\n");
6223 fprintf(f, "########################################################\n");
6224 fprintf(f, "-->\n");
6226 fclose(f);
6228 return true;
6229 }
6234 /**
6235 * This loads the dependency cache.
6236 */
6237 std::vector<DepRec> DepTool::loadDepFile(const String &depFile)
6238 {
6239 std::vector<DepRec> result;
6241 Parser parser;
6242 Element *root = parser.parseFile(depFile.c_str());
6243 if (!root)
6244 {
6245 //error("Could not open %s for reading", depFile.c_str());
6246 return result;
6247 }
6249 if (root->getChildren().size()==0 ||
6250 root->getChildren()[0]->getName()!="dependencies")
6251 {
6252 error("loadDepFile: main xml element should be <dependencies>");
6253 delete root;
6254 return result;
6255 }
6257 //########## Start parsing
6258 Element *depList = root->getChildren()[0];
6260 std::vector<Element *> objects = depList->getChildren();
6261 for (unsigned int i=0 ; i<objects.size() ; i++)
6262 {
6263 Element *objectElem = objects[i];
6264 String tagName = objectElem->getName();
6265 if (tagName != "object")
6266 {
6267 error("loadDepFile: <dependencies> should have only <object> children");
6268 return result;
6269 }
6271 String objName = objectElem->getAttribute("name");
6272 //trace("object:%s", objName.c_str());
6273 DepRec depObject(objName);
6274 depObject.path = objectElem->getAttribute("path");
6275 depObject.suffix = objectElem->getAttribute("suffix");
6276 //########## DESCRIPTION
6277 std::vector<Element *> depElems = objectElem->getChildren();
6278 for (unsigned int i=0 ; i<depElems.size() ; i++)
6279 {
6280 Element *depElem = depElems[i];
6281 tagName = depElem->getName();
6282 if (tagName != "dep")
6283 {
6284 error("loadDepFile: <object> should have only <dep> children");
6285 return result;
6286 }
6287 String depName = depElem->getAttribute("name");
6288 //trace(" dep:%s", depName.c_str());
6289 depObject.files.push_back(depName);
6290 }
6292 //Insert into the result list, in a sorted manner
6293 bool inserted = false;
6294 std::vector<DepRec>::iterator iter;
6295 for (iter = result.begin() ; iter != result.end() ; iter++)
6296 {
6297 String vpath = iter->path;
6298 vpath.append("/");
6299 vpath.append(iter->name);
6300 String opath = depObject.path;
6301 opath.append("/");
6302 opath.append(depObject.name);
6303 if (vpath > opath)
6304 {
6305 inserted = true;
6306 iter = result.insert(iter, depObject);
6307 break;
6308 }
6309 }
6310 if (!inserted)
6311 result.push_back(depObject);
6312 }
6314 delete root;
6316 return result;
6317 }
6320 /**
6321 * This loads the dependency cache.
6322 */
6323 std::vector<DepRec> DepTool::getDepFile(const String &depFile,
6324 bool forceRefresh)
6325 {
6326 std::vector<DepRec> result;
6327 if (forceRefresh)
6328 {
6329 generateDependencies(depFile);
6330 result = loadDepFile(depFile);
6331 }
6332 else
6333 {
6334 //try once
6335 result = loadDepFile(depFile);
6336 if (result.size() == 0)
6337 {
6338 //fail? try again
6339 generateDependencies(depFile);
6340 result = loadDepFile(depFile);
6341 }
6342 }
6343 return result;
6344 }
6349 //########################################################################
6350 //# T A S K
6351 //########################################################################
6352 //forward decl
6353 class Target;
6354 class Make;
6356 /**
6357 *
6358 */
6359 class Task : public MakeBase
6360 {
6362 public:
6364 typedef enum
6365 {
6366 TASK_NONE,
6367 TASK_CC,
6368 TASK_COPY,
6369 TASK_DELETE,
6370 TASK_ECHO,
6371 TASK_JAR,
6372 TASK_JAVAC,
6373 TASK_LINK,
6374 TASK_MAKEFILE,
6375 TASK_MKDIR,
6376 TASK_MSGFMT,
6377 TASK_PKG_CONFIG,
6378 TASK_RANLIB,
6379 TASK_RC,
6380 TASK_SHAREDLIB,
6381 TASK_STATICLIB,
6382 TASK_STRIP,
6383 TASK_TOUCH,
6384 TASK_TSTAMP
6385 } TaskType;
6388 /**
6389 *
6390 */
6391 Task(MakeBase &par) : parent(par)
6392 { init(); }
6394 /**
6395 *
6396 */
6397 Task(const Task &other) : parent(other.parent)
6398 { init(); assign(other); }
6400 /**
6401 *
6402 */
6403 Task &operator=(const Task &other)
6404 { assign(other); return *this; }
6406 /**
6407 *
6408 */
6409 virtual ~Task()
6410 { }
6413 /**
6414 *
6415 */
6416 virtual MakeBase &getParent()
6417 { return parent; }
6419 /**
6420 *
6421 */
6422 virtual int getType()
6423 { return type; }
6425 /**
6426 *
6427 */
6428 virtual void setType(int val)
6429 { type = val; }
6431 /**
6432 *
6433 */
6434 virtual String getName()
6435 { return name; }
6437 /**
6438 *
6439 */
6440 virtual bool execute()
6441 { return true; }
6443 /**
6444 *
6445 */
6446 virtual bool parse(Element *elem)
6447 { return true; }
6449 /**
6450 *
6451 */
6452 Task *createTask(Element *elem, int lineNr);
6455 protected:
6457 void init()
6458 {
6459 type = TASK_NONE;
6460 name = "none";
6461 }
6463 void assign(const Task &other)
6464 {
6465 type = other.type;
6466 name = other.name;
6467 }
6469 /**
6470 * Show task status
6471 */
6472 void taskstatus(const char *fmt, ...)
6473 {
6474 va_list args;
6475 va_start(args,fmt);
6476 fprintf(stdout, " %s : ", name.c_str());
6477 vfprintf(stdout, fmt, args);
6478 fprintf(stdout, "\n");
6479 va_end(args) ;
6480 }
6482 String getAttribute(Element *elem, const String &attrName)
6483 {
6484 String str;
6485 return str;
6486 }
6488 MakeBase &parent;
6490 int type;
6492 String name;
6493 };
6497 /**
6498 * This task runs the C/C++ compiler. The compiler is invoked
6499 * for all .c or .cpp files which are newer than their correcsponding
6500 * .o files.
6501 */
6502 class TaskCC : public Task
6503 {
6504 public:
6506 TaskCC(MakeBase &par) : Task(par)
6507 {
6508 type = TASK_CC;
6509 name = "cc";
6510 }
6512 virtual ~TaskCC()
6513 {}
6515 virtual bool isExcludedInc(const String &dirname)
6516 {
6517 for (unsigned int i=0 ; i<excludeInc.size() ; i++)
6518 {
6519 String fname = excludeInc[i];
6520 if (fname == dirname)
6521 return true;
6522 }
6523 return false;
6524 }
6526 virtual bool execute()
6527 {
6528 //evaluate our parameters
6529 String command = parent.eval(commandOpt, "gcc");
6530 String ccCommand = parent.eval(ccCommandOpt, "gcc");
6531 String cxxCommand = parent.eval(cxxCommandOpt, "g++");
6532 String source = parent.eval(sourceOpt, ".");
6533 String dest = parent.eval(destOpt, ".");
6534 String flags = parent.eval(flagsOpt, "");
6535 String defines = parent.eval(definesOpt, "");
6536 String includes = parent.eval(includesOpt, "");
6537 bool continueOnError = parent.evalBool(continueOnErrorOpt, true);
6538 bool refreshCache = parent.evalBool(refreshCacheOpt, false);
6540 if (!listFiles(parent, fileSet))
6541 return false;
6543 FILE *f = NULL;
6544 f = fopen("compile.lst", "w");
6546 //refreshCache is probably false here, unless specified otherwise
6547 String fullName = parent.resolve("build.dep");
6548 if (refreshCache || isNewerThan(parent.getURI().getPath(), fullName))
6549 {
6550 taskstatus("regenerating C/C++ dependency cache");
6551 refreshCache = true;
6552 }
6554 DepTool depTool;
6555 depTool.setSourceDirectory(source);
6556 depTool.setFileList(fileSet.getFiles());
6557 std::vector<DepRec> deps =
6558 depTool.getDepFile("build.dep", refreshCache);
6560 String incs;
6561 incs.append("-I");
6562 incs.append(parent.resolve("."));
6563 incs.append(" ");
6564 if (includes.size()>0)
6565 {
6566 incs.append(includes);
6567 incs.append(" ");
6568 }
6569 std::set<String> paths;
6570 std::vector<DepRec>::iterator viter;
6571 for (viter=deps.begin() ; viter!=deps.end() ; viter++)
6572 {
6573 DepRec dep = *viter;
6574 if (dep.path.size()>0)
6575 paths.insert(dep.path);
6576 }
6577 if (source.size()>0)
6578 {
6579 incs.append(" -I");
6580 incs.append(parent.resolve(source));
6581 incs.append(" ");
6582 }
6583 std::set<String>::iterator setIter;
6584 for (setIter=paths.begin() ; setIter!=paths.end() ; setIter++)
6585 {
6586 String dirName = *setIter;
6587 //check excludeInc to see if we dont want to include this dir
6588 if (isExcludedInc(dirName))
6589 continue;
6590 incs.append(" -I");
6591 String dname;
6592 if (source.size()>0)
6593 {
6594 dname.append(source);
6595 dname.append("/");
6596 }
6597 dname.append(dirName);
6598 incs.append(parent.resolve(dname));
6599 }
6601 /**
6602 * Compile each of the C files that need it
6603 */
6604 bool errorOccurred = false;
6605 std::vector<String> cfiles;
6606 for (viter=deps.begin() ; viter!=deps.end() ; viter++)
6607 {
6608 DepRec dep = *viter;
6610 //## Select command
6611 String sfx = dep.suffix;
6612 String command = ccCommand;
6613 if (sfx == "cpp" || sfx == "cxx" || sfx == "c++" ||
6614 sfx == "cc" || sfx == "CC")
6615 command = cxxCommand;
6617 //## Make paths
6618 String destPath = dest;
6619 String srcPath = source;
6620 if (dep.path.size()>0)
6621 {
6622 destPath.append("/");
6623 destPath.append(dep.path);
6624 srcPath.append("/");
6625 srcPath.append(dep.path);
6626 }
6627 //## Make sure destination directory exists
6628 if (!createDirectory(destPath))
6629 return false;
6631 //## Check whether it needs to be done
6632 String destName;
6633 if (destPath.size()>0)
6634 {
6635 destName.append(destPath);
6636 destName.append("/");
6637 }
6638 destName.append(dep.name);
6639 destName.append(".o");
6640 String destFullName = parent.resolve(destName);
6641 String srcName;
6642 if (srcPath.size()>0)
6643 {
6644 srcName.append(srcPath);
6645 srcName.append("/");
6646 }
6647 srcName.append(dep.name);
6648 srcName.append(".");
6649 srcName.append(dep.suffix);
6650 String srcFullName = parent.resolve(srcName);
6651 bool compileMe = false;
6652 //# First we check if the source is newer than the .o
6653 if (isNewerThan(srcFullName, destFullName))
6654 {
6655 taskstatus("compile of %s required by source: %s",
6656 destFullName.c_str(), srcFullName.c_str());
6657 compileMe = true;
6658 }
6659 else
6660 {
6661 //# secondly, we check if any of the included dependencies
6662 //# of the .c/.cpp is newer than the .o
6663 for (unsigned int i=0 ; i<dep.files.size() ; i++)
6664 {
6665 String depName;
6666 if (source.size()>0)
6667 {
6668 depName.append(source);
6669 depName.append("/");
6670 }
6671 depName.append(dep.files[i]);
6672 String depFullName = parent.resolve(depName);
6673 bool depRequires = isNewerThan(depFullName, destFullName);
6674 //trace("%d %s %s\n", depRequires,
6675 // destFullName.c_str(), depFullName.c_str());
6676 if (depRequires)
6677 {
6678 taskstatus("compile of %s required by included: %s",
6679 destFullName.c_str(), depFullName.c_str());
6680 compileMe = true;
6681 break;
6682 }
6683 }
6684 }
6685 if (!compileMe)
6686 {
6687 continue;
6688 }
6690 //## Assemble the command
6691 String cmd = command;
6692 cmd.append(" -c ");
6693 cmd.append(flags);
6694 cmd.append(" ");
6695 cmd.append(defines);
6696 cmd.append(" ");
6697 cmd.append(incs);
6698 cmd.append(" ");
6699 cmd.append(srcFullName);
6700 cmd.append(" -o ");
6701 cmd.append(destFullName);
6703 //## Execute the command
6705 String outString, errString;
6706 bool ret = executeCommand(cmd.c_str(), "", outString, errString);
6708 if (f)
6709 {
6710 fprintf(f, "########################### File : %s\n",
6711 srcFullName.c_str());
6712 fprintf(f, "#### COMMAND ###\n");
6713 int col = 0;
6714 for (unsigned int i = 0 ; i < cmd.size() ; i++)
6715 {
6716 char ch = cmd[i];
6717 if (isspace(ch) && col > 63)
6718 {
6719 fputc('\n', f);
6720 col = 0;
6721 }
6722 else
6723 {
6724 fputc(ch, f);
6725 col++;
6726 }
6727 if (col > 76)
6728 {
6729 fputc('\n', f);
6730 col = 0;
6731 }
6732 }
6733 fprintf(f, "\n");
6734 fprintf(f, "#### STDOUT ###\n%s\n", outString.c_str());
6735 fprintf(f, "#### STDERR ###\n%s\n\n", errString.c_str());
6736 fflush(f);
6737 }
6738 if (!ret)
6739 {
6740 error("problem compiling: %s", errString.c_str());
6741 errorOccurred = true;
6742 }
6743 if (errorOccurred && !continueOnError)
6744 break;
6745 }
6747 if (f)
6748 {
6749 fclose(f);
6750 }
6752 return !errorOccurred;
6753 }
6756 virtual bool parse(Element *elem)
6757 {
6758 String s;
6759 if (!parent.getAttribute(elem, "command", commandOpt))
6760 return false;
6761 if (commandOpt.size()>0)
6762 { cxxCommandOpt = ccCommandOpt = commandOpt; }
6763 if (!parent.getAttribute(elem, "cc", ccCommandOpt))
6764 return false;
6765 if (!parent.getAttribute(elem, "cxx", cxxCommandOpt))
6766 return false;
6767 if (!parent.getAttribute(elem, "destdir", destOpt))
6768 return false;
6769 if (!parent.getAttribute(elem, "continueOnError", continueOnErrorOpt))
6770 return false;
6771 if (!parent.getAttribute(elem, "refreshCache", refreshCacheOpt))
6772 return false;
6774 std::vector<Element *> children = elem->getChildren();
6775 for (unsigned int i=0 ; i<children.size() ; i++)
6776 {
6777 Element *child = children[i];
6778 String tagName = child->getName();
6779 if (tagName == "flags")
6780 {
6781 if (!parent.getValue(child, flagsOpt))
6782 return false;
6783 flagsOpt = strip(flagsOpt);
6784 }
6785 else if (tagName == "includes")
6786 {
6787 if (!parent.getValue(child, includesOpt))
6788 return false;
6789 includesOpt = strip(includesOpt);
6790 }
6791 else if (tagName == "defines")
6792 {
6793 if (!parent.getValue(child, definesOpt))
6794 return false;
6795 definesOpt = strip(definesOpt);
6796 }
6797 else if (tagName == "fileset")
6798 {
6799 if (!parseFileSet(child, parent, fileSet))
6800 return false;
6801 sourceOpt = fileSet.getDirectory();
6802 }
6803 else if (tagName == "excludeinc")
6804 {
6805 if (!parseFileList(child, parent, excludeInc))
6806 return false;
6807 }
6808 }
6810 return true;
6811 }
6813 protected:
6815 String commandOpt;
6816 String ccCommandOpt;
6817 String cxxCommandOpt;
6818 String sourceOpt;
6819 String destOpt;
6820 String flagsOpt;
6821 String definesOpt;
6822 String includesOpt;
6823 String continueOnErrorOpt;
6824 String refreshCacheOpt;
6825 FileSet fileSet;
6826 FileList excludeInc;
6828 };
6832 /**
6833 *
6834 */
6835 class TaskCopy : public Task
6836 {
6837 public:
6839 typedef enum
6840 {
6841 CP_NONE,
6842 CP_TOFILE,
6843 CP_TODIR
6844 } CopyType;
6846 TaskCopy(MakeBase &par) : Task(par)
6847 {
6848 type = TASK_COPY;
6849 name = "copy";
6850 cptype = CP_NONE;
6851 haveFileSet = false;
6852 }
6854 virtual ~TaskCopy()
6855 {}
6857 virtual bool execute()
6858 {
6859 String fileName = parent.eval(fileNameOpt , ".");
6860 String toFileName = parent.eval(toFileNameOpt , ".");
6861 String toDirName = parent.eval(toDirNameOpt , ".");
6862 bool verbose = parent.evalBool(verboseOpt, false);
6863 switch (cptype)
6864 {
6865 case CP_TOFILE:
6866 {
6867 if (fileName.size()>0)
6868 {
6869 taskstatus("%s to %s",
6870 fileName.c_str(), toFileName.c_str());
6871 String fullSource = parent.resolve(fileName);
6872 String fullDest = parent.resolve(toFileName);
6873 if (verbose)
6874 taskstatus("copy %s to file %s", fullSource.c_str(),
6875 fullDest.c_str());
6876 if (!isRegularFile(fullSource))
6877 {
6878 error("copy : file %s does not exist", fullSource.c_str());
6879 return false;
6880 }
6881 if (!isNewerThan(fullSource, fullDest))
6882 {
6883 taskstatus("skipped");
6884 return true;
6885 }
6886 if (!copyFile(fullSource, fullDest))
6887 return false;
6888 taskstatus("1 file copied");
6889 }
6890 return true;
6891 }
6892 case CP_TODIR:
6893 {
6894 if (haveFileSet)
6895 {
6896 if (!listFiles(parent, fileSet))
6897 return false;
6898 String fileSetDir = parent.eval(fileSet.getDirectory(), ".");
6900 taskstatus("%s to %s",
6901 fileSetDir.c_str(), toDirName.c_str());
6903 int nrFiles = 0;
6904 for (unsigned int i=0 ; i<fileSet.size() ; i++)
6905 {
6906 String fileName = fileSet[i];
6908 String sourcePath;
6909 if (fileSetDir.size()>0)
6910 {
6911 sourcePath.append(fileSetDir);
6912 sourcePath.append("/");
6913 }
6914 sourcePath.append(fileName);
6915 String fullSource = parent.resolve(sourcePath);
6917 //Get the immediate parent directory's base name
6918 String baseFileSetDir = fileSetDir;
6919 unsigned int pos = baseFileSetDir.find_last_of('/');
6920 if (pos!=baseFileSetDir.npos &&
6921 pos < baseFileSetDir.size()-1)
6922 baseFileSetDir =
6923 baseFileSetDir.substr(pos+1,
6924 baseFileSetDir.size());
6925 //Now make the new path
6926 String destPath;
6927 if (toDirName.size()>0)
6928 {
6929 destPath.append(toDirName);
6930 destPath.append("/");
6931 }
6932 if (baseFileSetDir.size()>0)
6933 {
6934 destPath.append(baseFileSetDir);
6935 destPath.append("/");
6936 }
6937 destPath.append(fileName);
6938 String fullDest = parent.resolve(destPath);
6939 //trace("fileName:%s", fileName.c_str());
6940 if (verbose)
6941 taskstatus("copy %s to new dir : %s",
6942 fullSource.c_str(), fullDest.c_str());
6943 if (!isNewerThan(fullSource, fullDest))
6944 {
6945 if (verbose)
6946 taskstatus("copy skipping %s", fullSource.c_str());
6947 continue;
6948 }
6949 if (!copyFile(fullSource, fullDest))
6950 return false;
6951 nrFiles++;
6952 }
6953 taskstatus("%d file(s) copied", nrFiles);
6954 }
6955 else //file source
6956 {
6957 //For file->dir we want only the basename of
6958 //the source appended to the dest dir
6959 taskstatus("%s to %s",
6960 fileName.c_str(), toDirName.c_str());
6961 String baseName = fileName;
6962 unsigned int pos = baseName.find_last_of('/');
6963 if (pos!=baseName.npos && pos<baseName.size()-1)
6964 baseName = baseName.substr(pos+1, baseName.size());
6965 String fullSource = parent.resolve(fileName);
6966 String destPath;
6967 if (toDirName.size()>0)
6968 {
6969 destPath.append(toDirName);
6970 destPath.append("/");
6971 }
6972 destPath.append(baseName);
6973 String fullDest = parent.resolve(destPath);
6974 if (verbose)
6975 taskstatus("file %s to new dir : %s", fullSource.c_str(),
6976 fullDest.c_str());
6977 if (!isRegularFile(fullSource))
6978 {
6979 error("copy : file %s does not exist", fullSource.c_str());
6980 return false;
6981 }
6982 if (!isNewerThan(fullSource, fullDest))
6983 {
6984 taskstatus("skipped");
6985 return true;
6986 }
6987 if (!copyFile(fullSource, fullDest))
6988 return false;
6989 taskstatus("1 file copied");
6990 }
6991 return true;
6992 }
6993 }
6994 return true;
6995 }
6998 virtual bool parse(Element *elem)
6999 {
7000 if (!parent.getAttribute(elem, "file", fileNameOpt))
7001 return false;
7002 if (!parent.getAttribute(elem, "tofile", toFileNameOpt))
7003 return false;
7004 if (toFileNameOpt.size() > 0)
7005 cptype = CP_TOFILE;
7006 if (!parent.getAttribute(elem, "todir", toDirNameOpt))
7007 return false;
7008 if (toDirNameOpt.size() > 0)
7009 cptype = CP_TODIR;
7010 if (!parent.getAttribute(elem, "verbose", verboseOpt))
7011 return false;
7013 haveFileSet = false;
7015 std::vector<Element *> children = elem->getChildren();
7016 for (unsigned int i=0 ; i<children.size() ; i++)
7017 {
7018 Element *child = children[i];
7019 String tagName = child->getName();
7020 if (tagName == "fileset")
7021 {
7022 if (!parseFileSet(child, parent, fileSet))
7023 {
7024 error("problem getting fileset");
7025 return false;
7026 }
7027 haveFileSet = true;
7028 }
7029 }
7031 //Perform validity checks
7032 if (fileNameOpt.size()>0 && fileSet.size()>0)
7033 {
7034 error("<copy> can only have one of : file= and <fileset>");
7035 return false;
7036 }
7037 if (toFileNameOpt.size()>0 && toDirNameOpt.size()>0)
7038 {
7039 error("<copy> can only have one of : tofile= or todir=");
7040 return false;
7041 }
7042 if (haveFileSet && toDirNameOpt.size()==0)
7043 {
7044 error("a <copy> task with a <fileset> must have : todir=");
7045 return false;
7046 }
7047 if (cptype == CP_TOFILE && fileNameOpt.size()==0)
7048 {
7049 error("<copy> tofile= must be associated with : file=");
7050 return false;
7051 }
7052 if (cptype == CP_TODIR && fileNameOpt.size()==0 && !haveFileSet)
7053 {
7054 error("<copy> todir= must be associated with : file= or <fileset>");
7055 return false;
7056 }
7058 return true;
7059 }
7061 private:
7063 int cptype;
7064 bool haveFileSet;
7066 FileSet fileSet;
7067 String fileNameOpt;
7068 String toFileNameOpt;
7069 String toDirNameOpt;
7070 String verboseOpt;
7071 };
7074 /**
7075 *
7076 */
7077 class TaskDelete : public Task
7078 {
7079 public:
7081 typedef enum
7082 {
7083 DEL_FILE,
7084 DEL_DIR,
7085 DEL_FILESET
7086 } DeleteType;
7088 TaskDelete(MakeBase &par) : Task(par)
7089 {
7090 type = TASK_DELETE;
7091 name = "delete";
7092 delType = DEL_FILE;
7093 }
7095 virtual ~TaskDelete()
7096 {}
7098 virtual bool execute()
7099 {
7100 String dirName = parent.eval(dirNameOpt, ".");
7101 String fileName = parent.eval(fileNameOpt, ".");
7102 bool verbose = parent.evalBool(verboseOpt, false);
7103 bool quiet = parent.evalBool(quietOpt, false);
7104 bool failOnError = parent.evalBool(failOnErrorOpt, true);
7105 struct stat finfo;
7106 switch (delType)
7107 {
7108 case DEL_FILE:
7109 {
7110 taskstatus("file: %s", fileName.c_str());
7111 String fullName = parent.resolve(fileName);
7112 char *fname = (char *)fullName.c_str();
7113 if (!quiet && verbose)
7114 taskstatus("path: %s", fname);
7115 //does not exist
7116 if (stat(fname, &finfo)<0)
7117 {
7118 if (failOnError)
7119 return false;
7120 else
7121 return true;
7122 }
7123 //exists but is not a regular file
7124 if (!S_ISREG(finfo.st_mode))
7125 {
7126 error("<delete> failed. '%s' exists and is not a regular file",
7127 fname);
7128 return false;
7129 }
7130 if (remove(fname)<0)
7131 {
7132 error("<delete> failed: %s", strerror(errno));
7133 return false;
7134 }
7135 return true;
7136 }
7137 case DEL_DIR:
7138 {
7139 taskstatus("dir: %s", dirName.c_str());
7140 String fullDir = parent.resolve(dirName);
7141 if (!quiet && verbose)
7142 taskstatus("path: %s", fullDir.c_str());
7143 if (!removeDirectory(fullDir))
7144 return false;
7145 return true;
7146 }
7147 }
7148 return true;
7149 }
7151 virtual bool parse(Element *elem)
7152 {
7153 if (!parent.getAttribute(elem, "file", fileNameOpt))
7154 return false;
7155 if (fileNameOpt.size() > 0)
7156 delType = DEL_FILE;
7157 if (!parent.getAttribute(elem, "dir", dirNameOpt))
7158 return false;
7159 if (dirNameOpt.size() > 0)
7160 delType = DEL_DIR;
7161 if (fileNameOpt.size()>0 && dirNameOpt.size()>0)
7162 {
7163 error("<delete> can have one attribute of file= or dir=");
7164 return false;
7165 }
7166 if (fileNameOpt.size()==0 && dirNameOpt.size()==0)
7167 {
7168 error("<delete> must have one attribute of file= or dir=");
7169 return false;
7170 }
7171 if (!parent.getAttribute(elem, "verbose", verboseOpt))
7172 return false;
7173 if (!parent.getAttribute(elem, "quiet", quietOpt))
7174 return false;
7175 if (!parent.getAttribute(elem, "failonerror", failOnErrorOpt))
7176 return false;
7177 return true;
7178 }
7180 private:
7182 int delType;
7183 String dirNameOpt;
7184 String fileNameOpt;
7185 String verboseOpt;
7186 String quietOpt;
7187 String failOnErrorOpt;
7188 };
7191 /**
7192 * Send a message to stdout
7193 */
7194 class TaskEcho : public Task
7195 {
7196 public:
7198 TaskEcho(MakeBase &par) : Task(par)
7199 { type = TASK_ECHO; name = "echo"; }
7201 virtual ~TaskEcho()
7202 {}
7204 virtual bool execute()
7205 {
7206 //let message have priority over text
7207 String message = parent.eval(messageOpt, "");
7208 String text = parent.eval(textOpt, "");
7209 if (message.size() > 0)
7210 {
7211 fprintf(stdout, "%s\n", message.c_str());
7212 }
7213 else if (text.size() > 0)
7214 {
7215 fprintf(stdout, "%s\n", text.c_str());
7216 }
7217 return true;
7218 }
7220 virtual bool parse(Element *elem)
7221 {
7222 if (!parent.getValue(elem, textOpt))
7223 return false;
7224 textOpt = leftJustify(textOpt);
7225 if (!parent.getAttribute(elem, "message", messageOpt))
7226 return false;
7227 return true;
7228 }
7230 private:
7232 String messageOpt;
7233 String textOpt;
7234 };
7238 /**
7239 *
7240 */
7241 class TaskJar : public Task
7242 {
7243 public:
7245 TaskJar(MakeBase &par) : Task(par)
7246 { type = TASK_JAR; name = "jar"; }
7248 virtual ~TaskJar()
7249 {}
7251 virtual bool execute()
7252 {
7253 String command = parent.eval(commandOpt, "jar");
7254 String basedir = parent.eval(basedirOpt, ".");
7255 String destfile = parent.eval(destfileOpt, ".");
7257 String cmd = command;
7258 cmd.append(" -cf ");
7259 cmd.append(destfile);
7260 cmd.append(" -C ");
7261 cmd.append(basedir);
7262 cmd.append(" .");
7264 String execCmd = cmd;
7266 String outString, errString;
7267 bool ret = executeCommand(execCmd.c_str(), "", outString, errString);
7268 if (!ret)
7269 {
7270 error("<jar> command '%s' failed :\n %s",
7271 execCmd.c_str(), errString.c_str());
7272 return false;
7273 }
7274 return true;
7275 }
7277 virtual bool parse(Element *elem)
7278 {
7279 if (!parent.getAttribute(elem, "command", commandOpt))
7280 return false;
7281 if (!parent.getAttribute(elem, "basedir", basedirOpt))
7282 return false;
7283 if (!parent.getAttribute(elem, "destfile", destfileOpt))
7284 return false;
7285 if (basedirOpt.size() == 0 || destfileOpt.size() == 0)
7286 {
7287 error("<jar> required both basedir and destfile attributes to be set");
7288 return false;
7289 }
7290 return true;
7291 }
7293 private:
7295 String commandOpt;
7296 String basedirOpt;
7297 String destfileOpt;
7298 };
7301 /**
7302 *
7303 */
7304 class TaskJavac : public Task
7305 {
7306 public:
7308 TaskJavac(MakeBase &par) : Task(par)
7309 {
7310 type = TASK_JAVAC; name = "javac";
7311 }
7313 virtual ~TaskJavac()
7314 {}
7316 virtual bool execute()
7317 {
7318 String command = parent.eval(commandOpt, "javac");
7319 String srcdir = parent.eval(srcdirOpt, ".");
7320 String destdir = parent.eval(destdirOpt, ".");
7321 String target = parent.eval(targetOpt, "");
7323 std::vector<String> fileList;
7324 if (!listFiles(srcdir, "", fileList))
7325 {
7326 return false;
7327 }
7328 String cmd = command;
7329 cmd.append(" -d ");
7330 cmd.append(destdir);
7331 cmd.append(" -classpath ");
7332 cmd.append(destdir);
7333 cmd.append(" -sourcepath ");
7334 cmd.append(srcdir);
7335 cmd.append(" ");
7336 if (target.size()>0)
7337 {
7338 cmd.append(" -target ");
7339 cmd.append(target);
7340 cmd.append(" ");
7341 }
7342 String fname = "javalist.btool";
7343 FILE *f = fopen(fname.c_str(), "w");
7344 int count = 0;
7345 for (unsigned int i=0 ; i<fileList.size() ; i++)
7346 {
7347 String fname = fileList[i];
7348 String srcName = fname;
7349 if (fname.size()<6) //x.java
7350 continue;
7351 if (fname.compare(fname.size()-5, 5, ".java") != 0)
7352 continue;
7353 String baseName = fname.substr(0, fname.size()-5);
7354 String destName = baseName;
7355 destName.append(".class");
7357 String fullSrc = srcdir;
7358 fullSrc.append("/");
7359 fullSrc.append(fname);
7360 String fullDest = destdir;
7361 fullDest.append("/");
7362 fullDest.append(destName);
7363 //trace("fullsrc:%s fulldest:%s", fullSrc.c_str(), fullDest.c_str());
7364 if (!isNewerThan(fullSrc, fullDest))
7365 continue;
7367 count++;
7368 fprintf(f, "%s\n", fullSrc.c_str());
7369 }
7370 fclose(f);
7371 if (!count)
7372 {
7373 taskstatus("nothing to do");
7374 return true;
7375 }
7377 taskstatus("compiling %d files", count);
7379 String execCmd = cmd;
7380 execCmd.append("@");
7381 execCmd.append(fname);
7383 String outString, errString;
7384 bool ret = executeCommand(execCmd.c_str(), "", outString, errString);
7385 if (!ret)
7386 {
7387 error("<javac> command '%s' failed :\n %s",
7388 execCmd.c_str(), errString.c_str());
7389 return false;
7390 }
7391 return true;
7392 }
7394 virtual bool parse(Element *elem)
7395 {
7396 if (!parent.getAttribute(elem, "command", commandOpt))
7397 return false;
7398 if (!parent.getAttribute(elem, "srcdir", srcdirOpt))
7399 return false;
7400 if (!parent.getAttribute(elem, "destdir", destdirOpt))
7401 return false;
7402 if (srcdirOpt.size() == 0 || destdirOpt.size() == 0)
7403 {
7404 error("<javac> required both srcdir and destdir attributes to be set");
7405 return false;
7406 }
7407 if (!parent.getAttribute(elem, "target", targetOpt))
7408 return false;
7409 return true;
7410 }
7412 private:
7414 String commandOpt;
7415 String srcdirOpt;
7416 String destdirOpt;
7417 String targetOpt;
7419 };
7422 /**
7423 *
7424 */
7425 class TaskLink : public Task
7426 {
7427 public:
7429 TaskLink(MakeBase &par) : Task(par)
7430 {
7431 type = TASK_LINK; name = "link";
7432 }
7434 virtual ~TaskLink()
7435 {}
7437 virtual bool execute()
7438 {
7439 String command = parent.eval(commandOpt, "g++");
7440 String fileName = parent.eval(fileNameOpt, "");
7441 String flags = parent.eval(flagsOpt, "");
7442 String libs = parent.eval(libsOpt, "");
7443 bool doStrip = parent.evalBool(doStripOpt, false);
7444 String symFileName = parent.eval(symFileNameOpt, "");
7445 String stripCommand = parent.eval(stripCommandOpt, "strip");
7446 String objcopyCommand = parent.eval(objcopyCommandOpt, "objcopy");
7448 if (!listFiles(parent, fileSet))
7449 return false;
7450 String fileSetDir = parent.eval(fileSet.getDirectory(), ".");
7451 //trace("%d files in %s", fileSet.size(), fileSetDir.c_str());
7452 bool doit = false;
7453 String fullTarget = parent.resolve(fileName);
7454 String cmd = command;
7455 cmd.append(" -o ");
7456 cmd.append(fullTarget);
7457 cmd.append(" ");
7458 cmd.append(flags);
7459 for (unsigned int i=0 ; i<fileSet.size() ; i++)
7460 {
7461 cmd.append(" ");
7462 String obj;
7463 if (fileSetDir.size()>0)
7464 {
7465 obj.append(fileSetDir);
7466 obj.append("/");
7467 }
7468 obj.append(fileSet[i]);
7469 String fullObj = parent.resolve(obj);
7470 String nativeFullObj = getNativePath(fullObj);
7471 cmd.append(nativeFullObj);
7472 //trace("link: tgt:%s obj:%s", fullTarget.c_str(),
7473 // fullObj.c_str());
7474 if (isNewerThan(fullObj, fullTarget))
7475 doit = true;
7476 }
7477 cmd.append(" ");
7478 cmd.append(libs);
7479 if (!doit)
7480 {
7481 //trace("link not needed");
7482 return true;
7483 }
7484 //trace("LINK cmd:%s", cmd.c_str());
7487 String outbuf, errbuf;
7488 if (!executeCommand(cmd.c_str(), "", outbuf, errbuf))
7489 {
7490 error("LINK problem: %s", errbuf.c_str());
7491 return false;
7492 }
7494 if (symFileName.size()>0)
7495 {
7496 String symFullName = parent.resolve(symFileName);
7497 cmd = objcopyCommand;
7498 cmd.append(" --only-keep-debug ");
7499 cmd.append(getNativePath(fullTarget));
7500 cmd.append(" ");
7501 cmd.append(getNativePath(symFullName));
7502 if (!executeCommand(cmd, "", outbuf, errbuf))
7503 {
7504 error("<strip> symbol file failed : %s", errbuf.c_str());
7505 return false;
7506 }
7507 }
7509 if (doStrip)
7510 {
7511 cmd = stripCommand;
7512 cmd.append(" ");
7513 cmd.append(getNativePath(fullTarget));
7514 if (!executeCommand(cmd, "", outbuf, errbuf))
7515 {
7516 error("<strip> failed : %s", errbuf.c_str());
7517 return false;
7518 }
7519 }
7521 return true;
7522 }
7524 virtual bool parse(Element *elem)
7525 {
7526 if (!parent.getAttribute(elem, "command", commandOpt))
7527 return false;
7528 if (!parent.getAttribute(elem, "objcopycommand", objcopyCommandOpt))
7529 return false;
7530 if (!parent.getAttribute(elem, "stripcommand", stripCommandOpt))
7531 return false;
7532 if (!parent.getAttribute(elem, "out", fileNameOpt))
7533 return false;
7534 if (!parent.getAttribute(elem, "strip", doStripOpt))
7535 return false;
7536 if (!parent.getAttribute(elem, "symfile", symFileNameOpt))
7537 return false;
7539 std::vector<Element *> children = elem->getChildren();
7540 for (unsigned int i=0 ; i<children.size() ; i++)
7541 {
7542 Element *child = children[i];
7543 String tagName = child->getName();
7544 if (tagName == "fileset")
7545 {
7546 if (!parseFileSet(child, parent, fileSet))
7547 return false;
7548 }
7549 else if (tagName == "flags")
7550 {
7551 if (!parent.getValue(child, flagsOpt))
7552 return false;
7553 flagsOpt = strip(flagsOpt);
7554 }
7555 else if (tagName == "libs")
7556 {
7557 if (!parent.getValue(child, libsOpt))
7558 return false;
7559 libsOpt = strip(libsOpt);
7560 }
7561 }
7562 return true;
7563 }
7565 private:
7567 FileSet fileSet;
7569 String commandOpt;
7570 String fileNameOpt;
7571 String flagsOpt;
7572 String libsOpt;
7573 String doStripOpt;
7574 String symFileNameOpt;
7575 String stripCommandOpt;
7576 String objcopyCommandOpt;
7578 };
7582 /**
7583 * Create a named file
7584 */
7585 class TaskMakeFile : public Task
7586 {
7587 public:
7589 TaskMakeFile(MakeBase &par) : Task(par)
7590 { type = TASK_MAKEFILE; name = "makefile"; }
7592 virtual ~TaskMakeFile()
7593 {}
7595 virtual bool execute()
7596 {
7597 String fileName = parent.eval(fileNameOpt, "");
7598 String text = parent.eval(textOpt, "");
7600 taskstatus("%s", fileName.c_str());
7601 String fullName = parent.resolve(fileName);
7602 if (!isNewerThan(parent.getURI().getPath(), fullName))
7603 {
7604 //trace("skipped <makefile>");
7605 return true;
7606 }
7607 String fullNative = getNativePath(fullName);
7608 //trace("fullName:%s", fullName.c_str());
7609 FILE *f = fopen(fullNative.c_str(), "w");
7610 if (!f)
7611 {
7612 error("<makefile> could not open %s for writing : %s",
7613 fullName.c_str(), strerror(errno));
7614 return false;
7615 }
7616 for (unsigned int i=0 ; i<text.size() ; i++)
7617 fputc(text[i], f);
7618 fputc('\n', f);
7619 fclose(f);
7620 return true;
7621 }
7623 virtual bool parse(Element *elem)
7624 {
7625 if (!parent.getAttribute(elem, "file", fileNameOpt))
7626 return false;
7627 if (fileNameOpt.size() == 0)
7628 {
7629 error("<makefile> requires 'file=\"filename\"' attribute");
7630 return false;
7631 }
7632 if (!parent.getValue(elem, textOpt))
7633 return false;
7634 textOpt = leftJustify(textOpt);
7635 //trace("dirname:%s", dirName.c_str());
7636 return true;
7637 }
7639 private:
7641 String fileNameOpt;
7642 String textOpt;
7643 };
7647 /**
7648 * Create a named directory
7649 */
7650 class TaskMkDir : public Task
7651 {
7652 public:
7654 TaskMkDir(MakeBase &par) : Task(par)
7655 { type = TASK_MKDIR; name = "mkdir"; }
7657 virtual ~TaskMkDir()
7658 {}
7660 virtual bool execute()
7661 {
7662 String dirName = parent.eval(dirNameOpt, ".");
7664 taskstatus("%s", dirName.c_str());
7665 String fullDir = parent.resolve(dirName);
7666 //trace("fullDir:%s", fullDir.c_str());
7667 if (!createDirectory(fullDir))
7668 return false;
7669 return true;
7670 }
7672 virtual bool parse(Element *elem)
7673 {
7674 if (!parent.getAttribute(elem, "dir", dirNameOpt))
7675 return false;
7676 if (dirNameOpt.size() == 0)
7677 {
7678 error("<mkdir> requires 'dir=\"dirname\"' attribute");
7679 return false;
7680 }
7681 return true;
7682 }
7684 private:
7686 String dirNameOpt;
7687 };
7691 /**
7692 * Create a named directory
7693 */
7694 class TaskMsgFmt: public Task
7695 {
7696 public:
7698 TaskMsgFmt(MakeBase &par) : Task(par)
7699 { type = TASK_MSGFMT; name = "msgfmt"; }
7701 virtual ~TaskMsgFmt()
7702 {}
7704 virtual bool execute()
7705 {
7706 String command = parent.eval(commandOpt, "msgfmt");
7707 String toDirName = parent.eval(toDirNameOpt, ".");
7708 String outName = parent.eval(outNameOpt, "");
7709 bool owndir = parent.evalBool(owndirOpt, false);
7711 if (!listFiles(parent, fileSet))
7712 return false;
7713 String fileSetDir = fileSet.getDirectory();
7715 //trace("msgfmt: %d", fileSet.size());
7716 for (unsigned int i=0 ; i<fileSet.size() ; i++)
7717 {
7718 String fileName = fileSet[i];
7719 if (getSuffix(fileName) != "po")
7720 continue;
7721 String sourcePath;
7722 if (fileSetDir.size()>0)
7723 {
7724 sourcePath.append(fileSetDir);
7725 sourcePath.append("/");
7726 }
7727 sourcePath.append(fileName);
7728 String fullSource = parent.resolve(sourcePath);
7730 String destPath;
7731 if (toDirName.size()>0)
7732 {
7733 destPath.append(toDirName);
7734 destPath.append("/");
7735 }
7736 if (owndir)
7737 {
7738 String subdir = fileName;
7739 unsigned int pos = subdir.find_last_of('.');
7740 if (pos != subdir.npos)
7741 subdir = subdir.substr(0, pos);
7742 destPath.append(subdir);
7743 destPath.append("/");
7744 }
7745 //Pick the output file name
7746 if (outName.size() > 0)
7747 {
7748 destPath.append(outName);
7749 }
7750 else
7751 {
7752 destPath.append(fileName);
7753 destPath[destPath.size()-2] = 'm';
7754 }
7756 String fullDest = parent.resolve(destPath);
7758 if (!isNewerThan(fullSource, fullDest))
7759 {
7760 //trace("skip %s", fullSource.c_str());
7761 continue;
7762 }
7764 String cmd = command;
7765 cmd.append(" ");
7766 cmd.append(fullSource);
7767 cmd.append(" -o ");
7768 cmd.append(fullDest);
7770 int pos = fullDest.find_last_of('/');
7771 if (pos>0)
7772 {
7773 String fullDestPath = fullDest.substr(0, pos);
7774 if (!createDirectory(fullDestPath))
7775 return false;
7776 }
7780 String outString, errString;
7781 if (!executeCommand(cmd.c_str(), "", outString, errString))
7782 {
7783 error("<msgfmt> problem: %s", errString.c_str());
7784 return false;
7785 }
7786 }
7788 return true;
7789 }
7791 virtual bool parse(Element *elem)
7792 {
7793 if (!parent.getAttribute(elem, "command", commandOpt))
7794 return false;
7795 if (!parent.getAttribute(elem, "todir", toDirNameOpt))
7796 return false;
7797 if (!parent.getAttribute(elem, "out", outNameOpt))
7798 return false;
7799 if (!parent.getAttribute(elem, "owndir", owndirOpt))
7800 return false;
7802 std::vector<Element *> children = elem->getChildren();
7803 for (unsigned int i=0 ; i<children.size() ; i++)
7804 {
7805 Element *child = children[i];
7806 String tagName = child->getName();
7807 if (tagName == "fileset")
7808 {
7809 if (!parseFileSet(child, parent, fileSet))
7810 return false;
7811 }
7812 }
7813 return true;
7814 }
7816 private:
7818 FileSet fileSet;
7820 String commandOpt;
7821 String toDirNameOpt;
7822 String outNameOpt;
7823 String owndirOpt;
7825 };
7829 /**
7830 * Perform a Package-Config query similar to pkg-config
7831 */
7832 class TaskPkgConfig : public Task
7833 {
7834 public:
7836 typedef enum
7837 {
7838 PKG_CONFIG_QUERY_CFLAGS,
7839 PKG_CONFIG_QUERY_LIBS,
7840 PKG_CONFIG_QUERY_ALL
7841 } QueryTypes;
7843 TaskPkgConfig(MakeBase &par) : Task(par)
7844 {
7845 type = TASK_PKG_CONFIG;
7846 name = "pkg-config";
7847 }
7849 virtual ~TaskPkgConfig()
7850 {}
7852 virtual bool execute()
7853 {
7854 String pkgName = parent.eval(pkgNameOpt, "");
7855 String prefix = parent.eval(prefixOpt, "");
7856 String propName = parent.eval(propNameOpt, "");
7857 String pkgConfigPath = parent.eval(pkgConfigPathOpt,"");
7858 String query = parent.eval(queryOpt, "all");
7860 String path = parent.resolve(pkgConfigPath);
7861 PkgConfig pkgconfig;
7862 pkgconfig.setPath(path);
7863 pkgconfig.setPrefix(prefix);
7864 if (!pkgconfig.query(pkgName))
7865 {
7866 error("<pkg-config> query failed for '%s", name.c_str());
7867 return false;
7868 }
7870 String val = "";
7871 if (query == "cflags")
7872 val = pkgconfig.getCflags();
7873 else if (query == "libs")
7874 val =pkgconfig.getLibs();
7875 else if (query == "all")
7876 val = pkgconfig.getAll();
7877 else
7878 {
7879 error("<pkg-config> unhandled query : %s", query.c_str());
7880 return false;
7881 }
7882 taskstatus("property %s = '%s'", propName.c_str(), val.c_str());
7883 parent.setProperty(propName, val);
7884 return true;
7885 }
7887 virtual bool parse(Element *elem)
7888 {
7889 //# NAME
7890 if (!parent.getAttribute(elem, "name", pkgNameOpt))
7891 return false;
7892 if (pkgNameOpt.size()==0)
7893 {
7894 error("<pkg-config> requires 'name=\"package\"' attribute");
7895 return false;
7896 }
7898 //# PROPERTY
7899 if (!parent.getAttribute(elem, "property", propNameOpt))
7900 return false;
7901 if (propNameOpt.size()==0)
7902 {
7903 error("<pkg-config> requires 'property=\"name\"' attribute");
7904 return false;
7905 }
7906 //# PATH
7907 if (!parent.getAttribute(elem, "path", pkgConfigPathOpt))
7908 return false;
7909 //# PREFIX
7910 if (!parent.getAttribute(elem, "prefix", prefixOpt))
7911 return false;
7912 //# QUERY
7913 if (!parent.getAttribute(elem, "query", queryOpt))
7914 return false;
7916 return true;
7917 }
7919 private:
7921 String queryOpt;
7922 String pkgNameOpt;
7923 String prefixOpt;
7924 String propNameOpt;
7925 String pkgConfigPathOpt;
7927 };
7934 /**
7935 * Process an archive to allow random access
7936 */
7937 class TaskRanlib : public Task
7938 {
7939 public:
7941 TaskRanlib(MakeBase &par) : Task(par)
7942 { type = TASK_RANLIB; name = "ranlib"; }
7944 virtual ~TaskRanlib()
7945 {}
7947 virtual bool execute()
7948 {
7949 String fileName = parent.eval(fileNameOpt, "");
7950 String command = parent.eval(commandOpt, "ranlib");
7952 String fullName = parent.resolve(fileName);
7953 //trace("fullDir:%s", fullDir.c_str());
7954 String cmd = command;
7955 cmd.append(" ");
7956 cmd.append(fullName);
7957 String outbuf, errbuf;
7958 if (!executeCommand(cmd, "", outbuf, errbuf))
7959 return false;
7960 return true;
7961 }
7963 virtual bool parse(Element *elem)
7964 {
7965 if (!parent.getAttribute(elem, "command", commandOpt))
7966 return false;
7967 if (!parent.getAttribute(elem, "file", fileNameOpt))
7968 return false;
7969 if (fileNameOpt.size() == 0)
7970 {
7971 error("<ranlib> requires 'file=\"fileNname\"' attribute");
7972 return false;
7973 }
7974 return true;
7975 }
7977 private:
7979 String fileNameOpt;
7980 String commandOpt;
7981 };
7985 /**
7986 * Compile a resource file into a binary object
7987 */
7988 class TaskRC : public Task
7989 {
7990 public:
7992 TaskRC(MakeBase &par) : Task(par)
7993 { type = TASK_RC; name = "rc"; }
7995 virtual ~TaskRC()
7996 {}
7998 virtual bool execute()
7999 {
8000 String command = parent.eval(commandOpt, "windres");
8001 String flags = parent.eval(flagsOpt, "");
8002 String fileName = parent.eval(fileNameOpt, "");
8003 String outName = parent.eval(outNameOpt, "");
8005 String fullFile = parent.resolve(fileName);
8006 String fullOut = parent.resolve(outName);
8007 if (!isNewerThan(fullFile, fullOut))
8008 return true;
8009 String cmd = command;
8010 cmd.append(" -o ");
8011 cmd.append(fullOut);
8012 cmd.append(" ");
8013 cmd.append(flags);
8014 cmd.append(" ");
8015 cmd.append(fullFile);
8017 String outString, errString;
8018 if (!executeCommand(cmd.c_str(), "", outString, errString))
8019 {
8020 error("RC problem: %s", errString.c_str());
8021 return false;
8022 }
8023 return true;
8024 }
8026 virtual bool parse(Element *elem)
8027 {
8028 if (!parent.getAttribute(elem, "command", commandOpt))
8029 return false;
8030 if (!parent.getAttribute(elem, "file", fileNameOpt))
8031 return false;
8032 if (!parent.getAttribute(elem, "out", outNameOpt))
8033 return false;
8034 std::vector<Element *> children = elem->getChildren();
8035 for (unsigned int i=0 ; i<children.size() ; i++)
8036 {
8037 Element *child = children[i];
8038 String tagName = child->getName();
8039 if (tagName == "flags")
8040 {
8041 if (!parent.getValue(child, flagsOpt))
8042 return false;
8043 }
8044 }
8045 return true;
8046 }
8048 private:
8050 String commandOpt;
8051 String flagsOpt;
8052 String fileNameOpt;
8053 String outNameOpt;
8055 };
8059 /**
8060 * Collect .o's into a .so or DLL
8061 */
8062 class TaskSharedLib : public Task
8063 {
8064 public:
8066 TaskSharedLib(MakeBase &par) : Task(par)
8067 { type = TASK_SHAREDLIB; name = "dll"; }
8069 virtual ~TaskSharedLib()
8070 {}
8072 virtual bool execute()
8073 {
8074 String command = parent.eval(commandOpt, "dllwrap");
8075 String fileName = parent.eval(fileNameOpt, "");
8076 String defFileName = parent.eval(defFileNameOpt, "");
8077 String impFileName = parent.eval(impFileNameOpt, "");
8078 String libs = parent.eval(libsOpt, "");
8080 //trace("###########HERE %d", fileSet.size());
8081 bool doit = false;
8083 String fullOut = parent.resolve(fileName);
8084 //trace("ar fullout: %s", fullOut.c_str());
8086 if (!listFiles(parent, fileSet))
8087 return false;
8088 String fileSetDir = parent.eval(fileSet.getDirectory(), ".");
8090 for (unsigned int i=0 ; i<fileSet.size() ; i++)
8091 {
8092 String fname;
8093 if (fileSetDir.size()>0)
8094 {
8095 fname.append(fileSetDir);
8096 fname.append("/");
8097 }
8098 fname.append(fileSet[i]);
8099 String fullName = parent.resolve(fname);
8100 //trace("ar : %s/%s", fullOut.c_str(), fullName.c_str());
8101 if (isNewerThan(fullName, fullOut))
8102 doit = true;
8103 }
8104 //trace("Needs it:%d", doit);
8105 if (!doit)
8106 {
8107 return true;
8108 }
8110 String cmd = "dllwrap";
8111 cmd.append(" -o ");
8112 cmd.append(fullOut);
8113 if (defFileName.size()>0)
8114 {
8115 cmd.append(" --def ");
8116 cmd.append(defFileName);
8117 cmd.append(" ");
8118 }
8119 if (impFileName.size()>0)
8120 {
8121 cmd.append(" --implib ");
8122 cmd.append(impFileName);
8123 cmd.append(" ");
8124 }
8125 for (unsigned int i=0 ; i<fileSet.size() ; i++)
8126 {
8127 String fname;
8128 if (fileSetDir.size()>0)
8129 {
8130 fname.append(fileSetDir);
8131 fname.append("/");
8132 }
8133 fname.append(fileSet[i]);
8134 String fullName = parent.resolve(fname);
8136 cmd.append(" ");
8137 cmd.append(fullName);
8138 }
8139 cmd.append(" ");
8140 cmd.append(libs);
8142 String outString, errString;
8143 if (!executeCommand(cmd.c_str(), "", outString, errString))
8144 {
8145 error("<sharedlib> problem: %s", errString.c_str());
8146 return false;
8147 }
8149 return true;
8150 }
8152 virtual bool parse(Element *elem)
8153 {
8154 if (!parent.getAttribute(elem, "command", commandOpt))
8155 return false;
8156 if (!parent.getAttribute(elem, "file", fileNameOpt))
8157 return false;
8158 if (!parent.getAttribute(elem, "import", impFileNameOpt))
8159 return false;
8160 if (!parent.getAttribute(elem, "def", defFileNameOpt))
8161 return false;
8163 std::vector<Element *> children = elem->getChildren();
8164 for (unsigned int i=0 ; i<children.size() ; i++)
8165 {
8166 Element *child = children[i];
8167 String tagName = child->getName();
8168 if (tagName == "fileset")
8169 {
8170 if (!parseFileSet(child, parent, fileSet))
8171 return false;
8172 }
8173 else if (tagName == "libs")
8174 {
8175 if (!parent.getValue(child, libsOpt))
8176 return false;
8177 libsOpt = strip(libsOpt);
8178 }
8179 }
8180 return true;
8181 }
8183 private:
8185 FileSet fileSet;
8187 String commandOpt;
8188 String fileNameOpt;
8189 String defFileNameOpt;
8190 String impFileNameOpt;
8191 String libsOpt;
8193 };
8197 /**
8198 * Run the "ar" command to archive .o's into a .a
8199 */
8200 class TaskStaticLib : public Task
8201 {
8202 public:
8204 TaskStaticLib(MakeBase &par) : Task(par)
8205 { type = TASK_STATICLIB; name = "staticlib"; }
8207 virtual ~TaskStaticLib()
8208 {}
8210 virtual bool execute()
8211 {
8212 String command = parent.eval(commandOpt, "ar crv");
8213 String fileName = parent.eval(fileNameOpt, "");
8215 bool doit = false;
8217 String fullOut = parent.resolve(fileName);
8218 //trace("ar fullout: %s", fullOut.c_str());
8220 if (!listFiles(parent, fileSet))
8221 return false;
8222 String fileSetDir = parent.eval(fileSet.getDirectory(), ".");
8223 //trace("###########HERE %s", fileSetDir.c_str());
8225 for (unsigned int i=0 ; i<fileSet.size() ; i++)
8226 {
8227 String fname;
8228 if (fileSetDir.size()>0)
8229 {
8230 fname.append(fileSetDir);
8231 fname.append("/");
8232 }
8233 fname.append(fileSet[i]);
8234 String fullName = parent.resolve(fname);
8235 //trace("ar : %s/%s", fullOut.c_str(), fullName.c_str());
8236 if (isNewerThan(fullName, fullOut))
8237 doit = true;
8238 }
8239 //trace("Needs it:%d", doit);
8240 if (!doit)
8241 {
8242 return true;
8243 }
8245 String cmd = command;
8246 cmd.append(" ");
8247 cmd.append(fullOut);
8248 for (unsigned int i=0 ; i<fileSet.size() ; i++)
8249 {
8250 String fname;
8251 if (fileSetDir.size()>0)
8252 {
8253 fname.append(fileSetDir);
8254 fname.append("/");
8255 }
8256 fname.append(fileSet[i]);
8257 String fullName = parent.resolve(fname);
8259 cmd.append(" ");
8260 cmd.append(fullName);
8261 }
8263 String outString, errString;
8264 if (!executeCommand(cmd.c_str(), "", outString, errString))
8265 {
8266 error("<staticlib> problem: %s", errString.c_str());
8267 return false;
8268 }
8270 return true;
8271 }
8274 virtual bool parse(Element *elem)
8275 {
8276 if (!parent.getAttribute(elem, "command", commandOpt))
8277 return false;
8278 if (!parent.getAttribute(elem, "file", fileNameOpt))
8279 return false;
8281 std::vector<Element *> children = elem->getChildren();
8282 for (unsigned int i=0 ; i<children.size() ; i++)
8283 {
8284 Element *child = children[i];
8285 String tagName = child->getName();
8286 if (tagName == "fileset")
8287 {
8288 if (!parseFileSet(child, parent, fileSet))
8289 return false;
8290 }
8291 }
8292 return true;
8293 }
8295 private:
8297 FileSet fileSet;
8299 String commandOpt;
8300 String fileNameOpt;
8302 };
8307 /**
8308 * Strip an executable
8309 */
8310 class TaskStrip : public Task
8311 {
8312 public:
8314 TaskStrip(MakeBase &par) : Task(par)
8315 { type = TASK_STRIP; name = "strip"; }
8317 virtual ~TaskStrip()
8318 {}
8320 virtual bool execute()
8321 {
8322 String command = parent.eval(commandOpt, "strip");
8323 String fileName = parent.eval(fileNameOpt, "");
8324 String symFileName = parent.eval(symFileNameOpt, "");
8326 String fullName = parent.resolve(fileName);
8327 //trace("fullDir:%s", fullDir.c_str());
8328 String cmd;
8329 String outbuf, errbuf;
8331 if (symFileName.size()>0)
8332 {
8333 String symFullName = parent.resolve(symFileName);
8334 cmd = "objcopy --only-keep-debug ";
8335 cmd.append(getNativePath(fullName));
8336 cmd.append(" ");
8337 cmd.append(getNativePath(symFullName));
8338 if (!executeCommand(cmd, "", outbuf, errbuf))
8339 {
8340 error("<strip> symbol file failed : %s", errbuf.c_str());
8341 return false;
8342 }
8343 }
8345 cmd = command;
8346 cmd.append(getNativePath(fullName));
8347 if (!executeCommand(cmd, "", outbuf, errbuf))
8348 {
8349 error("<strip> failed : %s", errbuf.c_str());
8350 return false;
8351 }
8352 return true;
8353 }
8355 virtual bool parse(Element *elem)
8356 {
8357 if (!parent.getAttribute(elem, "command", commandOpt))
8358 return false;
8359 if (!parent.getAttribute(elem, "file", fileNameOpt))
8360 return false;
8361 if (!parent.getAttribute(elem, "symfile", symFileNameOpt))
8362 return false;
8363 if (fileNameOpt.size() == 0)
8364 {
8365 error("<strip> requires 'file=\"fileName\"' attribute");
8366 return false;
8367 }
8368 return true;
8369 }
8371 private:
8373 String commandOpt;
8374 String fileNameOpt;
8375 String symFileNameOpt;
8376 };
8379 /**
8380 *
8381 */
8382 class TaskTouch : public Task
8383 {
8384 public:
8386 TaskTouch(MakeBase &par) : Task(par)
8387 { type = TASK_TOUCH; name = "touch"; }
8389 virtual ~TaskTouch()
8390 {}
8392 virtual bool execute()
8393 {
8394 String fileName = parent.eval(fileNameOpt, "");
8396 String fullName = parent.resolve(fileName);
8397 String nativeFile = getNativePath(fullName);
8398 if (!isRegularFile(fullName) && !isDirectory(fullName))
8399 {
8400 // S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH
8401 int ret = creat(nativeFile.c_str(), 0666);
8402 if (ret != 0)
8403 {
8404 error("<touch> could not create '%s' : %s",
8405 nativeFile.c_str(), strerror(ret));
8406 return false;
8407 }
8408 return true;
8409 }
8410 int ret = utime(nativeFile.c_str(), (struct utimbuf *)0);
8411 if (ret != 0)
8412 {
8413 error("<touch> could not update the modification time for '%s' : %s",
8414 nativeFile.c_str(), strerror(ret));
8415 return false;
8416 }
8417 return true;
8418 }
8420 virtual bool parse(Element *elem)
8421 {
8422 //trace("touch parse");
8423 if (!parent.getAttribute(elem, "file", fileNameOpt))
8424 return false;
8425 if (fileNameOpt.size() == 0)
8426 {
8427 error("<touch> requires 'file=\"fileName\"' attribute");
8428 return false;
8429 }
8430 return true;
8431 }
8433 String fileNameOpt;
8434 };
8437 /**
8438 *
8439 */
8440 class TaskTstamp : public Task
8441 {
8442 public:
8444 TaskTstamp(MakeBase &par) : Task(par)
8445 { type = TASK_TSTAMP; name = "tstamp"; }
8447 virtual ~TaskTstamp()
8448 {}
8450 virtual bool execute()
8451 {
8452 return true;
8453 }
8455 virtual bool parse(Element *elem)
8456 {
8457 //trace("tstamp parse");
8458 return true;
8459 }
8460 };
8464 /**
8465 *
8466 */
8467 Task *Task::createTask(Element *elem, int lineNr)
8468 {
8469 String tagName = elem->getName();
8470 //trace("task:%s", tagName.c_str());
8471 Task *task = NULL;
8472 if (tagName == "cc")
8473 task = new TaskCC(parent);
8474 else if (tagName == "copy")
8475 task = new TaskCopy(parent);
8476 else if (tagName == "delete")
8477 task = new TaskDelete(parent);
8478 else if (tagName == "echo")
8479 task = new TaskEcho(parent);
8480 else if (tagName == "jar")
8481 task = new TaskJar(parent);
8482 else if (tagName == "javac")
8483 task = new TaskJavac(parent);
8484 else if (tagName == "link")
8485 task = new TaskLink(parent);
8486 else if (tagName == "makefile")
8487 task = new TaskMakeFile(parent);
8488 else if (tagName == "mkdir")
8489 task = new TaskMkDir(parent);
8490 else if (tagName == "msgfmt")
8491 task = new TaskMsgFmt(parent);
8492 else if (tagName == "pkg-config")
8493 task = new TaskPkgConfig(parent);
8494 else if (tagName == "ranlib")
8495 task = new TaskRanlib(parent);
8496 else if (tagName == "rc")
8497 task = new TaskRC(parent);
8498 else if (tagName == "sharedlib")
8499 task = new TaskSharedLib(parent);
8500 else if (tagName == "staticlib")
8501 task = new TaskStaticLib(parent);
8502 else if (tagName == "strip")
8503 task = new TaskStrip(parent);
8504 else if (tagName == "touch")
8505 task = new TaskTouch(parent);
8506 else if (tagName == "tstamp")
8507 task = new TaskTstamp(parent);
8508 else
8509 {
8510 error("Unknown task '%s'", tagName.c_str());
8511 return NULL;
8512 }
8514 task->setLine(lineNr);
8516 if (!task->parse(elem))
8517 {
8518 delete task;
8519 return NULL;
8520 }
8521 return task;
8522 }
8526 //########################################################################
8527 //# T A R G E T
8528 //########################################################################
8530 /**
8531 *
8532 */
8533 class Target : public MakeBase
8534 {
8536 public:
8538 /**
8539 *
8540 */
8541 Target(Make &par) : parent(par)
8542 { init(); }
8544 /**
8545 *
8546 */
8547 Target(const Target &other) : parent(other.parent)
8548 { init(); assign(other); }
8550 /**
8551 *
8552 */
8553 Target &operator=(const Target &other)
8554 { init(); assign(other); return *this; }
8556 /**
8557 *
8558 */
8559 virtual ~Target()
8560 { cleanup() ; }
8563 /**
8564 *
8565 */
8566 virtual Make &getParent()
8567 { return parent; }
8569 /**
8570 *
8571 */
8572 virtual String getName()
8573 { return name; }
8575 /**
8576 *
8577 */
8578 virtual void setName(const String &val)
8579 { name = val; }
8581 /**
8582 *
8583 */
8584 virtual String getDescription()
8585 { return description; }
8587 /**
8588 *
8589 */
8590 virtual void setDescription(const String &val)
8591 { description = val; }
8593 /**
8594 *
8595 */
8596 virtual void addDependency(const String &val)
8597 { deps.push_back(val); }
8599 /**
8600 *
8601 */
8602 virtual void parseDependencies(const String &val)
8603 { deps = tokenize(val, ", "); }
8605 /**
8606 *
8607 */
8608 virtual std::vector<String> &getDependencies()
8609 { return deps; }
8611 /**
8612 *
8613 */
8614 virtual String getIf()
8615 { return ifVar; }
8617 /**
8618 *
8619 */
8620 virtual void setIf(const String &val)
8621 { ifVar = val; }
8623 /**
8624 *
8625 */
8626 virtual String getUnless()
8627 { return unlessVar; }
8629 /**
8630 *
8631 */
8632 virtual void setUnless(const String &val)
8633 { unlessVar = val; }
8635 /**
8636 *
8637 */
8638 virtual void addTask(Task *val)
8639 { tasks.push_back(val); }
8641 /**
8642 *
8643 */
8644 virtual std::vector<Task *> &getTasks()
8645 { return tasks; }
8647 private:
8649 void init()
8650 {
8651 }
8653 void cleanup()
8654 {
8655 tasks.clear();
8656 }
8658 void assign(const Target &other)
8659 {
8660 //parent = other.parent;
8661 name = other.name;
8662 description = other.description;
8663 ifVar = other.ifVar;
8664 unlessVar = other.unlessVar;
8665 deps = other.deps;
8666 tasks = other.tasks;
8667 }
8669 Make &parent;
8671 String name;
8673 String description;
8675 String ifVar;
8677 String unlessVar;
8679 std::vector<String> deps;
8681 std::vector<Task *> tasks;
8683 };
8692 //########################################################################
8693 //# M A K E
8694 //########################################################################
8697 /**
8698 *
8699 */
8700 class Make : public MakeBase
8701 {
8703 public:
8705 /**
8706 *
8707 */
8708 Make()
8709 { init(); }
8711 /**
8712 *
8713 */
8714 Make(const Make &other)
8715 { assign(other); }
8717 /**
8718 *
8719 */
8720 Make &operator=(const Make &other)
8721 { assign(other); return *this; }
8723 /**
8724 *
8725 */
8726 virtual ~Make()
8727 { cleanup(); }
8729 /**
8730 *
8731 */
8732 virtual std::map<String, Target> &getTargets()
8733 { return targets; }
8736 /**
8737 *
8738 */
8739 virtual String version()
8740 { return BUILDTOOL_VERSION; }
8742 /**
8743 * Overload a <property>
8744 */
8745 virtual bool specifyProperty(const String &name,
8746 const String &value);
8748 /**
8749 *
8750 */
8751 virtual bool run();
8753 /**
8754 *
8755 */
8756 virtual bool run(const String &target);
8760 private:
8762 /**
8763 *
8764 */
8765 void init();
8767 /**
8768 *
8769 */
8770 void cleanup();
8772 /**
8773 *
8774 */
8775 void assign(const Make &other);
8777 /**
8778 *
8779 */
8780 bool executeTask(Task &task);
8783 /**
8784 *
8785 */
8786 bool executeTarget(Target &target,
8787 std::set<String> &targetsCompleted);
8790 /**
8791 *
8792 */
8793 bool execute();
8795 /**
8796 *
8797 */
8798 bool checkTargetDependencies(Target &prop,
8799 std::vector<String> &depList);
8801 /**
8802 *
8803 */
8804 bool parsePropertyFile(const String &fileName,
8805 const String &prefix);
8807 /**
8808 *
8809 */
8810 bool parseProperty(Element *elem);
8812 /**
8813 *
8814 */
8815 bool parseFile();
8817 /**
8818 *
8819 */
8820 std::vector<String> glob(const String &pattern);
8823 //###############
8824 //# Fields
8825 //###############
8827 String projectName;
8829 String currentTarget;
8831 String defaultTarget;
8833 String specifiedTarget;
8835 String baseDir;
8837 String description;
8839 //std::vector<Property> properties;
8841 std::map<String, Target> targets;
8843 std::vector<Task *> allTasks;
8845 std::map<String, String> specifiedProperties;
8847 };
8850 //########################################################################
8851 //# C L A S S M A I N T E N A N C E
8852 //########################################################################
8854 /**
8855 *
8856 */
8857 void Make::init()
8858 {
8859 uri = "build.xml";
8860 projectName = "";
8861 currentTarget = "";
8862 defaultTarget = "";
8863 specifiedTarget = "";
8864 baseDir = "";
8865 description = "";
8866 envPrefix = "env.";
8867 pcPrefix = "pc.";
8868 pccPrefix = "pcc.";
8869 pclPrefix = "pcl.";
8870 properties.clear();
8871 for (unsigned int i = 0 ; i < allTasks.size() ; i++)
8872 delete allTasks[i];
8873 allTasks.clear();
8874 }
8878 /**
8879 *
8880 */
8881 void Make::cleanup()
8882 {
8883 for (unsigned int i = 0 ; i < allTasks.size() ; i++)
8884 delete allTasks[i];
8885 allTasks.clear();
8886 }
8890 /**
8891 *
8892 */
8893 void Make::assign(const Make &other)
8894 {
8895 uri = other.uri;
8896 projectName = other.projectName;
8897 currentTarget = other.currentTarget;
8898 defaultTarget = other.defaultTarget;
8899 specifiedTarget = other.specifiedTarget;
8900 baseDir = other.baseDir;
8901 description = other.description;
8902 properties = other.properties;
8903 }
8907 //########################################################################
8908 //# U T I L I T Y T A S K S
8909 //########################################################################
8911 /**
8912 * Perform a file globbing
8913 */
8914 std::vector<String> Make::glob(const String &pattern)
8915 {
8916 std::vector<String> res;
8917 return res;
8918 }
8921 //########################################################################
8922 //# P U B L I C A P I
8923 //########################################################################
8927 /**
8928 *
8929 */
8930 bool Make::executeTarget(Target &target,
8931 std::set<String> &targetsCompleted)
8932 {
8934 String name = target.getName();
8936 //First get any dependencies for this target
8937 std::vector<String> deps = target.getDependencies();
8938 for (unsigned int i=0 ; i<deps.size() ; i++)
8939 {
8940 String dep = deps[i];
8941 //Did we do it already? Skip
8942 if (targetsCompleted.find(dep)!=targetsCompleted.end())
8943 continue;
8945 std::map<String, Target> &tgts =
8946 target.getParent().getTargets();
8947 std::map<String, Target>::iterator iter =
8948 tgts.find(dep);
8949 if (iter == tgts.end())
8950 {
8951 error("Target '%s' dependency '%s' not found",
8952 name.c_str(), dep.c_str());
8953 return false;
8954 }
8955 Target depTarget = iter->second;
8956 if (!executeTarget(depTarget, targetsCompleted))
8957 {
8958 return false;
8959 }
8960 }
8962 status("##### Target : %s\n##### %s", name.c_str(),
8963 target.getDescription().c_str());
8965 //Now let's do the tasks
8966 std::vector<Task *> &tasks = target.getTasks();
8967 for (unsigned int i=0 ; i<tasks.size() ; i++)
8968 {
8969 Task *task = tasks[i];
8970 status("--- %s / %s", name.c_str(), task->getName().c_str());
8971 if (!task->execute())
8972 {
8973 return false;
8974 }
8975 }
8977 targetsCompleted.insert(name);
8979 return true;
8980 }
8984 /**
8985 * Main execute() method. Start here and work
8986 * up the dependency tree
8987 */
8988 bool Make::execute()
8989 {
8990 status("######## EXECUTE");
8992 //Determine initial target
8993 if (specifiedTarget.size()>0)
8994 {
8995 currentTarget = specifiedTarget;
8996 }
8997 else if (defaultTarget.size()>0)
8998 {
8999 currentTarget = defaultTarget;
9000 }
9001 else
9002 {
9003 error("execute: no specified or default target requested");
9004 return false;
9005 }
9007 std::map<String, Target>::iterator iter =
9008 targets.find(currentTarget);
9009 if (iter == targets.end())
9010 {
9011 error("Initial target '%s' not found",
9012 currentTarget.c_str());
9013 return false;
9014 }
9016 //Now run
9017 Target target = iter->second;
9018 std::set<String> targetsCompleted;
9019 if (!executeTarget(target, targetsCompleted))
9020 {
9021 return false;
9022 }
9024 status("######## EXECUTE COMPLETE");
9025 return true;
9026 }
9031 /**
9032 *
9033 */
9034 bool Make::checkTargetDependencies(Target &target,
9035 std::vector<String> &depList)
9036 {
9037 String tgtName = target.getName().c_str();
9038 depList.push_back(tgtName);
9040 std::vector<String> deps = target.getDependencies();
9041 for (unsigned int i=0 ; i<deps.size() ; i++)
9042 {
9043 String dep = deps[i];
9044 //First thing entered was the starting Target
9045 if (dep == depList[0])
9046 {
9047 error("Circular dependency '%s' found at '%s'",
9048 dep.c_str(), tgtName.c_str());
9049 std::vector<String>::iterator diter;
9050 for (diter=depList.begin() ; diter!=depList.end() ; diter++)
9051 {
9052 error(" %s", diter->c_str());
9053 }
9054 return false;
9055 }
9057 std::map<String, Target> &tgts =
9058 target.getParent().getTargets();
9059 std::map<String, Target>::iterator titer = tgts.find(dep);
9060 if (titer == tgts.end())
9061 {
9062 error("Target '%s' dependency '%s' not found",
9063 tgtName.c_str(), dep.c_str());
9064 return false;
9065 }
9066 if (!checkTargetDependencies(titer->second, depList))
9067 {
9068 return false;
9069 }
9070 }
9071 return true;
9072 }
9078 static int getword(int pos, const String &inbuf, String &result)
9079 {
9080 int p = pos;
9081 int len = (int)inbuf.size();
9082 String val;
9083 while (p < len)
9084 {
9085 char ch = inbuf[p];
9086 if (!isalnum(ch) && ch!='.' && ch!='_')
9087 break;
9088 val.push_back(ch);
9089 p++;
9090 }
9091 result = val;
9092 return p;
9093 }
9098 /**
9099 *
9100 */
9101 bool Make::parsePropertyFile(const String &fileName,
9102 const String &prefix)
9103 {
9104 FILE *f = fopen(fileName.c_str(), "r");
9105 if (!f)
9106 {
9107 error("could not open property file %s", fileName.c_str());
9108 return false;
9109 }
9110 int linenr = 0;
9111 while (!feof(f))
9112 {
9113 char buf[256];
9114 if (!fgets(buf, 255, f))
9115 break;
9116 linenr++;
9117 String s = buf;
9118 s = trim(s);
9119 int len = s.size();
9120 if (len == 0)
9121 continue;
9122 if (s[0] == '#')
9123 continue;
9124 String key;
9125 String val;
9126 int p = 0;
9127 int p2 = getword(p, s, key);
9128 if (p2 <= p)
9129 {
9130 error("property file %s, line %d: expected keyword",
9131 fileName.c_str(), linenr);
9132 return false;
9133 }
9134 if (prefix.size() > 0)
9135 {
9136 key.insert(0, prefix);
9137 }
9139 //skip whitespace
9140 for (p=p2 ; p<len ; p++)
9141 if (!isspace(s[p]))
9142 break;
9144 if (p>=len || s[p]!='=')
9145 {
9146 error("property file %s, line %d: expected '='",
9147 fileName.c_str(), linenr);
9148 return false;
9149 }
9150 p++;
9152 //skip whitespace
9153 for ( ; p<len ; p++)
9154 if (!isspace(s[p]))
9155 break;
9157 /* This way expects a word after the =
9158 p2 = getword(p, s, val);
9159 if (p2 <= p)
9160 {
9161 error("property file %s, line %d: expected value",
9162 fileName.c_str(), linenr);
9163 return false;
9164 }
9165 */
9166 // This way gets the rest of the line after the =
9167 if (p>=len)
9168 {
9169 error("property file %s, line %d: expected value",
9170 fileName.c_str(), linenr);
9171 return false;
9172 }
9173 val = s.substr(p);
9174 if (key.size()==0)
9175 continue;
9176 //allow property to be set, even if val=""
9178 //trace("key:'%s' val:'%s'", key.c_str(), val.c_str());
9179 //See if we wanted to overload this property
9180 std::map<String, String>::iterator iter =
9181 specifiedProperties.find(key);
9182 if (iter!=specifiedProperties.end())
9183 {
9184 val = iter->second;
9185 status("overloading property '%s' = '%s'",
9186 key.c_str(), val.c_str());
9187 }
9188 properties[key] = val;
9189 }
9190 fclose(f);
9191 return true;
9192 }
9197 /**
9198 *
9199 */
9200 bool Make::parseProperty(Element *elem)
9201 {
9202 std::vector<Attribute> &attrs = elem->getAttributes();
9203 for (unsigned int i=0 ; i<attrs.size() ; i++)
9204 {
9205 String attrName = attrs[i].getName();
9206 String attrVal = attrs[i].getValue();
9208 if (attrName == "name")
9209 {
9210 String val;
9211 if (!getAttribute(elem, "value", val))
9212 return false;
9213 if (val.size() > 0)
9214 {
9215 properties[attrVal] = val;
9216 }
9217 else
9218 {
9219 if (!getAttribute(elem, "location", val))
9220 return false;
9221 //let the property exist, even if not defined
9222 properties[attrVal] = val;
9223 }
9224 //See if we wanted to overload this property
9225 std::map<String, String>::iterator iter =
9226 specifiedProperties.find(attrVal);
9227 if (iter != specifiedProperties.end())
9228 {
9229 val = iter->second;
9230 status("overloading property '%s' = '%s'",
9231 attrVal.c_str(), val.c_str());
9232 properties[attrVal] = val;
9233 }
9234 }
9235 else if (attrName == "file")
9236 {
9237 String prefix;
9238 if (!getAttribute(elem, "prefix", prefix))
9239 return false;
9240 if (prefix.size() > 0)
9241 {
9242 if (prefix[prefix.size()-1] != '.')
9243 prefix.push_back('.');
9244 }
9245 if (!parsePropertyFile(attrName, prefix))
9246 return false;
9247 }
9248 else if (attrName == "environment")
9249 {
9250 if (attrVal.find('.') != attrVal.npos)
9251 {
9252 error("environment prefix cannot have a '.' in it");
9253 return false;
9254 }
9255 envPrefix = attrVal;
9256 envPrefix.push_back('.');
9257 }
9258 else if (attrName == "pkg-config")
9259 {
9260 if (attrVal.find('.') != attrVal.npos)
9261 {
9262 error("pkg-config prefix cannot have a '.' in it");
9263 return false;
9264 }
9265 pcPrefix = attrVal;
9266 pcPrefix.push_back('.');
9267 }
9268 else if (attrName == "pkg-config-cflags")
9269 {
9270 if (attrVal.find('.') != attrVal.npos)
9271 {
9272 error("pkg-config-cflags prefix cannot have a '.' in it");
9273 return false;
9274 }
9275 pccPrefix = attrVal;
9276 pccPrefix.push_back('.');
9277 }
9278 else if (attrName == "pkg-config-libs")
9279 {
9280 if (attrVal.find('.') != attrVal.npos)
9281 {
9282 error("pkg-config-libs prefix cannot have a '.' in it");
9283 return false;
9284 }
9285 pclPrefix = attrVal;
9286 pclPrefix.push_back('.');
9287 }
9288 }
9290 return true;
9291 }
9296 /**
9297 *
9298 */
9299 bool Make::parseFile()
9300 {
9301 status("######## PARSE : %s", uri.getPath().c_str());
9303 setLine(0);
9305 Parser parser;
9306 Element *root = parser.parseFile(uri.getNativePath());
9307 if (!root)
9308 {
9309 error("Could not open %s for reading",
9310 uri.getNativePath().c_str());
9311 return false;
9312 }
9314 setLine(root->getLine());
9316 if (root->getChildren().size()==0 ||
9317 root->getChildren()[0]->getName()!="project")
9318 {
9319 error("Main xml element should be <project>");
9320 delete root;
9321 return false;
9322 }
9324 //########## Project attributes
9325 Element *project = root->getChildren()[0];
9326 String s = project->getAttribute("name");
9327 if (s.size() > 0)
9328 projectName = s;
9329 s = project->getAttribute("default");
9330 if (s.size() > 0)
9331 defaultTarget = s;
9332 s = project->getAttribute("basedir");
9333 if (s.size() > 0)
9334 baseDir = s;
9336 //######### PARSE MEMBERS
9337 std::vector<Element *> children = project->getChildren();
9338 for (unsigned int i=0 ; i<children.size() ; i++)
9339 {
9340 Element *elem = children[i];
9341 setLine(elem->getLine());
9342 String tagName = elem->getName();
9344 //########## DESCRIPTION
9345 if (tagName == "description")
9346 {
9347 description = parser.trim(elem->getValue());
9348 }
9350 //######### PROPERTY
9351 else if (tagName == "property")
9352 {
9353 if (!parseProperty(elem))
9354 return false;
9355 }
9357 //######### TARGET
9358 else if (tagName == "target")
9359 {
9360 String tname = elem->getAttribute("name");
9361 String tdesc = elem->getAttribute("description");
9362 String tdeps = elem->getAttribute("depends");
9363 String tif = elem->getAttribute("if");
9364 String tunless = elem->getAttribute("unless");
9365 Target target(*this);
9366 target.setName(tname);
9367 target.setDescription(tdesc);
9368 target.parseDependencies(tdeps);
9369 target.setIf(tif);
9370 target.setUnless(tunless);
9371 std::vector<Element *> telems = elem->getChildren();
9372 for (unsigned int i=0 ; i<telems.size() ; i++)
9373 {
9374 Element *telem = telems[i];
9375 Task breeder(*this);
9376 Task *task = breeder.createTask(telem, telem->getLine());
9377 if (!task)
9378 return false;
9379 allTasks.push_back(task);
9380 target.addTask(task);
9381 }
9383 //Check name
9384 if (tname.size() == 0)
9385 {
9386 error("no name for target");
9387 return false;
9388 }
9389 //Check for duplicate name
9390 if (targets.find(tname) != targets.end())
9391 {
9392 error("target '%s' already defined", tname.c_str());
9393 return false;
9394 }
9395 //more work than targets[tname]=target, but avoids default allocator
9396 targets.insert(std::make_pair<String, Target>(tname, target));
9397 }
9398 //######### none of the above
9399 else
9400 {
9401 error("unknown toplevel tag: <%s>", tagName.c_str());
9402 return false;
9403 }
9405 }
9407 std::map<String, Target>::iterator iter;
9408 for (iter = targets.begin() ; iter!= targets.end() ; iter++)
9409 {
9410 Target tgt = iter->second;
9411 std::vector<String> depList;
9412 if (!checkTargetDependencies(tgt, depList))
9413 {
9414 return false;
9415 }
9416 }
9419 delete root;
9420 status("######## PARSE COMPLETE");
9421 return true;
9422 }
9425 /**
9426 * Overload a <property>
9427 */
9428 bool Make::specifyProperty(const String &name, const String &value)
9429 {
9430 if (specifiedProperties.find(name) != specifiedProperties.end())
9431 {
9432 error("Property %s already specified", name.c_str());
9433 return false;
9434 }
9435 specifiedProperties[name] = value;
9436 return true;
9437 }
9441 /**
9442 *
9443 */
9444 bool Make::run()
9445 {
9446 if (!parseFile())
9447 return false;
9449 if (!execute())
9450 return false;
9452 return true;
9453 }
9458 /**
9459 * Get a formatted MM:SS.sss time elapsed string
9460 */
9461 static String
9462 timeDiffString(struct timeval &x, struct timeval &y)
9463 {
9464 long microsX = x.tv_usec;
9465 long secondsX = x.tv_sec;
9466 long microsY = y.tv_usec;
9467 long secondsY = y.tv_sec;
9468 if (microsX < microsY)
9469 {
9470 microsX += 1000000;
9471 secondsX -= 1;
9472 }
9474 int seconds = (int)(secondsX - secondsY);
9475 int millis = (int)((microsX - microsY)/1000);
9477 int minutes = seconds/60;
9478 seconds -= minutes*60;
9479 char buf[80];
9480 snprintf(buf, 79, "%dm %d.%03ds", minutes, seconds, millis);
9481 String ret = buf;
9482 return ret;
9484 }
9486 /**
9487 *
9488 */
9489 bool Make::run(const String &target)
9490 {
9491 status("####################################################");
9492 status("# %s", version().c_str());
9493 status("####################################################");
9494 struct timeval timeStart, timeEnd;
9495 ::gettimeofday(&timeStart, NULL);
9496 specifiedTarget = target;
9497 if (!run())
9498 return false;
9499 ::gettimeofday(&timeEnd, NULL);
9500 String timeStr = timeDiffString(timeEnd, timeStart);
9501 status("####################################################");
9502 status("# BuildTool Completed : %s", timeStr.c_str());
9503 status("####################################################");
9504 return true;
9505 }
9513 }// namespace buildtool
9514 //########################################################################
9515 //# M A I N
9516 //########################################################################
9518 typedef buildtool::String String;
9520 /**
9521 * Format an error message in printf() style
9522 */
9523 static void error(const char *fmt, ...)
9524 {
9525 va_list ap;
9526 va_start(ap, fmt);
9527 fprintf(stderr, "BuildTool error: ");
9528 vfprintf(stderr, fmt, ap);
9529 fprintf(stderr, "\n");
9530 va_end(ap);
9531 }
9534 static bool parseProperty(const String &s, String &name, String &val)
9535 {
9536 int len = s.size();
9537 int i;
9538 for (i=0 ; i<len ; i++)
9539 {
9540 char ch = s[i];
9541 if (ch == '=')
9542 break;
9543 name.push_back(ch);
9544 }
9545 if (i>=len || s[i]!='=')
9546 {
9547 error("property requires -Dname=value");
9548 return false;
9549 }
9550 i++;
9551 for ( ; i<len ; i++)
9552 {
9553 char ch = s[i];
9554 val.push_back(ch);
9555 }
9556 return true;
9557 }
9560 /**
9561 * Compare a buffer with a key, for the length of the key
9562 */
9563 static bool sequ(const String &buf, const char *key)
9564 {
9565 int len = buf.size();
9566 for (int i=0 ; key[i] && i<len ; i++)
9567 {
9568 if (key[i] != buf[i])
9569 return false;
9570 }
9571 return true;
9572 }
9574 static void usage(int argc, char **argv)
9575 {
9576 printf("usage:\n");
9577 printf(" %s [options] [target]\n", argv[0]);
9578 printf("Options:\n");
9579 printf(" -help, -h print this message\n");
9580 printf(" -version print the version information and exit\n");
9581 printf(" -file <file> use given buildfile\n");
9582 printf(" -f <file> ''\n");
9583 printf(" -D<property>=<value> use value for given property\n");
9584 }
9589 /**
9590 * Parse the command-line args, get our options,
9591 * and run this thing
9592 */
9593 static bool parseOptions(int argc, char **argv)
9594 {
9595 if (argc < 1)
9596 {
9597 error("Cannot parse arguments");
9598 return false;
9599 }
9601 buildtool::Make make;
9603 String target;
9605 //char *progName = argv[0];
9606 for (int i=1 ; i<argc ; i++)
9607 {
9608 String arg = argv[i];
9609 if (arg.size()>1 && arg[0]=='-')
9610 {
9611 if (arg == "-h" || arg == "-help")
9612 {
9613 usage(argc,argv);
9614 return true;
9615 }
9616 else if (arg == "-version")
9617 {
9618 printf("%s", make.version().c_str());
9619 return true;
9620 }
9621 else if (arg == "-f" || arg == "-file")
9622 {
9623 if (i>=argc)
9624 {
9625 usage(argc, argv);
9626 return false;
9627 }
9628 i++; //eat option
9629 make.setURI(argv[i]);
9630 }
9631 else if (arg.size()>2 && sequ(arg, "-D"))
9632 {
9633 String s = arg.substr(2, arg.size());
9634 String name, value;
9635 if (!parseProperty(s, name, value))
9636 {
9637 usage(argc, argv);
9638 return false;
9639 }
9640 if (!make.specifyProperty(name, value))
9641 return false;
9642 }
9643 else
9644 {
9645 error("Unknown option:%s", arg.c_str());
9646 return false;
9647 }
9648 }
9649 else
9650 {
9651 if (target.size()>0)
9652 {
9653 error("only one initial target");
9654 usage(argc, argv);
9655 return false;
9656 }
9657 target = arg;
9658 }
9659 }
9661 //We have the options. Now execute them
9662 if (!make.run(target))
9663 return false;
9665 return true;
9666 }
9671 /*
9672 static bool runMake()
9673 {
9674 buildtool::Make make;
9675 if (!make.run())
9676 return false;
9677 return true;
9678 }
9681 static bool pkgConfigTest()
9682 {
9683 buildtool::PkgConfig pkgConfig;
9684 if (!pkgConfig.readFile("gtk+-2.0.pc"))
9685 return false;
9686 return true;
9687 }
9691 static bool depTest()
9692 {
9693 buildtool::DepTool deptool;
9694 deptool.setSourceDirectory("/dev/ink/inkscape/src");
9695 if (!deptool.generateDependencies("build.dep"))
9696 return false;
9697 std::vector<buildtool::FileRec> res =
9698 deptool.loadDepFile("build.dep");
9699 if (res.size() == 0)
9700 return false;
9701 return true;
9702 }
9704 static bool popenTest()
9705 {
9706 buildtool::Make make;
9707 buildtool::String out, err;
9708 bool ret = make.executeCommand("gcc xx.cpp", "", out, err);
9709 printf("Popen test:%d '%s' '%s'\n", ret, out.c_str(), err.c_str());
9710 return true;
9711 }
9714 static bool propFileTest()
9715 {
9716 buildtool::Make make;
9717 make.parsePropertyFile("test.prop", "test.");
9718 return true;
9719 }
9720 */
9722 int main(int argc, char **argv)
9723 {
9725 if (!parseOptions(argc, argv))
9726 return 1;
9727 /*
9728 if (!popenTest())
9729 return 1;
9731 if (!depTest())
9732 return 1;
9733 if (!propFileTest())
9734 return 1;
9735 if (runMake())
9736 return 1;
9737 */
9738 return 0;
9739 }
9742 //########################################################################
9743 //# E N D
9744 //########################################################################