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
3869 /**
3870 * Execute a system call, using pipes to send data to the
3871 * program's stdin, and reading stdout and stderr.
3872 */
3873 bool MakeBase::executeCommand(const String &command,
3874 const String &inbuf,
3875 String &outbuf,
3876 String &errbuf)
3877 {
3879 status("============ cmd ============\n%s\n=============================",
3880 command.c_str());
3882 outbuf.clear();
3883 errbuf.clear();
3885 #ifdef __WIN32__
3887 /*
3888 I really hate having win32 code in this program, but the
3889 read buffer in command.com and cmd.exe are just too small
3890 for the large commands we need for compiling and linking.
3891 */
3893 bool ret = true;
3895 //# Allocate a separate buffer for safety
3896 char *paramBuf = new char[command.size() + 1];
3897 if (!paramBuf)
3898 {
3899 error("executeCommand cannot allocate command buffer");
3900 return false;
3901 }
3902 strcpy(paramBuf, (char *)command.c_str());
3904 //# Go to http://msdn2.microsoft.com/en-us/library/ms682499.aspx
3905 //# to see how Win32 pipes work
3907 //# Create pipes
3908 SECURITY_ATTRIBUTES saAttr;
3909 saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
3910 saAttr.bInheritHandle = TRUE;
3911 saAttr.lpSecurityDescriptor = NULL;
3912 HANDLE stdinRead, stdinWrite;
3913 HANDLE stdoutRead, stdoutWrite;
3914 HANDLE stderrRead, stderrWrite;
3915 if (!CreatePipe(&stdinRead, &stdinWrite, &saAttr, 0))
3916 {
3917 error("executeProgram: could not create pipe");
3918 delete[] paramBuf;
3919 return false;
3920 }
3921 SetHandleInformation(stdinWrite, HANDLE_FLAG_INHERIT, 0);
3922 if (!CreatePipe(&stdoutRead, &stdoutWrite, &saAttr, 0))
3923 {
3924 error("executeProgram: could not create pipe");
3925 delete[] paramBuf;
3926 return false;
3927 }
3928 SetHandleInformation(stdoutRead, HANDLE_FLAG_INHERIT, 0);
3929 if (!CreatePipe(&stderrRead, &stderrWrite, &saAttr, 0))
3930 {
3931 error("executeProgram: could not create pipe");
3932 delete[] paramBuf;
3933 return false;
3934 }
3935 SetHandleInformation(stderrRead, HANDLE_FLAG_INHERIT, 0);
3937 // Create the process
3938 STARTUPINFO siStartupInfo;
3939 PROCESS_INFORMATION piProcessInfo;
3940 memset(&siStartupInfo, 0, sizeof(siStartupInfo));
3941 memset(&piProcessInfo, 0, sizeof(piProcessInfo));
3942 siStartupInfo.cb = sizeof(siStartupInfo);
3943 siStartupInfo.hStdError = stderrWrite;
3944 siStartupInfo.hStdOutput = stdoutWrite;
3945 siStartupInfo.hStdInput = stdinRead;
3946 siStartupInfo.dwFlags |= STARTF_USESTDHANDLES;
3948 if (!CreateProcess(NULL, paramBuf, NULL, NULL, true,
3949 0, NULL, NULL, &siStartupInfo,
3950 &piProcessInfo))
3951 {
3952 error("executeCommand : could not create process : %s",
3953 win32LastError().c_str());
3954 ret = false;
3955 }
3957 delete[] paramBuf;
3959 DWORD bytesWritten;
3960 if (inbuf.size()>0 &&
3961 !WriteFile(stdinWrite, inbuf.c_str(), inbuf.size(),
3962 &bytesWritten, NULL))
3963 {
3964 error("executeCommand: could not write to pipe");
3965 return false;
3966 }
3967 if (!CloseHandle(stdinWrite))
3968 {
3969 error("executeCommand: could not close write pipe");
3970 return false;
3971 }
3972 if (!CloseHandle(stdoutWrite))
3973 {
3974 error("executeCommand: could not close read pipe");
3975 return false;
3976 }
3977 if (!CloseHandle(stderrWrite))
3978 {
3979 error("executeCommand: could not close read pipe");
3980 return false;
3981 }
3983 bool lastLoop = false;
3984 while (true)
3985 {
3986 DWORD avail;
3987 DWORD bytesRead;
3988 char readBuf[4096];
3990 //trace("## stderr");
3991 PeekNamedPipe(stderrRead, NULL, 0, NULL, &avail, NULL);
3992 if (avail > 0)
3993 {
3994 bytesRead = 0;
3995 if (avail>4096) avail = 4096;
3996 ReadFile(stderrRead, readBuf, avail, &bytesRead, NULL);
3997 if (bytesRead > 0)
3998 {
3999 for (unsigned int i=0 ; i<bytesRead ; i++)
4000 errbuf.push_back(readBuf[i]);
4001 }
4002 }
4004 //trace("## stdout");
4005 PeekNamedPipe(stdoutRead, NULL, 0, NULL, &avail, NULL);
4006 if (avail > 0)
4007 {
4008 bytesRead = 0;
4009 if (avail>4096) avail = 4096;
4010 ReadFile(stdoutRead, readBuf, avail, &bytesRead, NULL);
4011 if (bytesRead > 0)
4012 {
4013 for (unsigned int i=0 ; i<bytesRead ; i++)
4014 outbuf.push_back(readBuf[i]);
4015 }
4016 }
4018 //Was this the final check after program done?
4019 if (lastLoop)
4020 break;
4022 DWORD exitCode;
4023 GetExitCodeProcess(piProcessInfo.hProcess, &exitCode);
4024 if (exitCode != STILL_ACTIVE)
4025 lastLoop = true;
4027 Sleep(10);
4028 }
4029 //trace("outbuf:%s", outbuf.c_str());
4030 if (!CloseHandle(stdoutRead))
4031 {
4032 error("executeCommand: could not close read pipe");
4033 return false;
4034 }
4035 if (!CloseHandle(stderrRead))
4036 {
4037 error("executeCommand: could not close read pipe");
4038 return false;
4039 }
4041 DWORD exitCode;
4042 GetExitCodeProcess(piProcessInfo.hProcess, &exitCode);
4043 //trace("exit code:%d", exitCode);
4044 if (exitCode != 0)
4045 {
4046 ret = false;
4047 }
4049 CloseHandle(piProcessInfo.hProcess);
4050 CloseHandle(piProcessInfo.hThread);
4052 return ret;
4054 #else //do it unix-style
4056 String pipeCommand = command;
4057 pipeCommand.append(" 2>&1");
4058 String s;
4059 FILE *f = popen(pipeCommand.c_str(), "r");
4060 int errnum = 0;
4061 if (f)
4062 {
4063 while (true)
4064 {
4065 int ch = fgetc(f);
4066 if (ch < 0)
4067 break;
4068 s.push_back((char)ch);
4069 }
4070 errnum = pclose(f);
4071 }
4072 outbuf = s;
4073 if (errnum != 0)
4074 {
4075 error("exec of command '%s' failed : %s",
4076 command.c_str(), strerror(errno));
4077 return false;
4078 }
4079 else
4080 return true;
4082 #endif
4083 }
4088 bool MakeBase::listDirectories(const String &baseName,
4089 const String &dirName,
4090 std::vector<String> &res)
4091 {
4092 res.push_back(dirName);
4093 String fullPath = baseName;
4094 if (dirName.size()>0)
4095 {
4096 fullPath.append("/");
4097 fullPath.append(dirName);
4098 }
4099 DIR *dir = opendir(fullPath.c_str());
4100 while (true)
4101 {
4102 struct dirent *de = readdir(dir);
4103 if (!de)
4104 break;
4106 //Get the directory member name
4107 String s = de->d_name;
4108 if (s.size() == 0 || s[0] == '.')
4109 continue;
4110 String childName = dirName;
4111 childName.append("/");
4112 childName.append(s);
4114 String fullChildPath = baseName;
4115 fullChildPath.append("/");
4116 fullChildPath.append(childName);
4117 struct stat finfo;
4118 String childNative = getNativePath(fullChildPath);
4119 if (stat(childNative.c_str(), &finfo)<0)
4120 {
4121 error("cannot stat file:%s", childNative.c_str());
4122 }
4123 else if (S_ISDIR(finfo.st_mode))
4124 {
4125 //trace("directory: %s", childName.c_str());
4126 if (!listDirectories(baseName, childName, res))
4127 return false;
4128 }
4129 }
4130 closedir(dir);
4132 return true;
4133 }
4136 bool MakeBase::listFiles(const String &baseDir,
4137 const String &dirName,
4138 std::vector<String> &res)
4139 {
4140 String fullDir = baseDir;
4141 if (dirName.size()>0)
4142 {
4143 fullDir.append("/");
4144 fullDir.append(dirName);
4145 }
4146 String dirNative = getNativePath(fullDir);
4148 std::vector<String> subdirs;
4149 DIR *dir = opendir(dirNative.c_str());
4150 if (!dir)
4151 {
4152 error("Could not open directory %s : %s",
4153 dirNative.c_str(), strerror(errno));
4154 return false;
4155 }
4156 while (true)
4157 {
4158 struct dirent *de = readdir(dir);
4159 if (!de)
4160 break;
4162 //Get the directory member name
4163 String s = de->d_name;
4164 if (s.size() == 0 || s[0] == '.')
4165 continue;
4166 String childName;
4167 if (dirName.size()>0)
4168 {
4169 childName.append(dirName);
4170 childName.append("/");
4171 }
4172 childName.append(s);
4173 String fullChild = baseDir;
4174 fullChild.append("/");
4175 fullChild.append(childName);
4177 if (isDirectory(fullChild))
4178 {
4179 //trace("directory: %s", childName.c_str());
4180 if (!listFiles(baseDir, childName, res))
4181 return false;
4182 continue;
4183 }
4184 else if (!isRegularFile(fullChild))
4185 {
4186 error("unknown file:%s", childName.c_str());
4187 return false;
4188 }
4190 //all done!
4191 res.push_back(childName);
4193 }
4194 closedir(dir);
4196 return true;
4197 }
4200 /**
4201 * Several different classes extend MakeBase. By "propRef", we mean
4202 * the one holding the properties. Likely "Make" itself
4203 */
4204 bool MakeBase::listFiles(MakeBase &propRef, FileSet &fileSet)
4205 {
4206 //before doing the list, resolve any property references
4207 //that might have been specified in the directory name, such as ${src}
4208 String fsDir = fileSet.getDirectory();
4209 String dir;
4210 if (!propRef.getSubstitutions(fsDir, dir))
4211 return false;
4212 String baseDir = propRef.resolve(dir);
4213 std::vector<String> fileList;
4214 if (!listFiles(baseDir, "", fileList))
4215 return false;
4217 std::vector<String> includes = fileSet.getIncludes();
4218 std::vector<String> excludes = fileSet.getExcludes();
4220 std::vector<String> incs;
4221 std::vector<String>::iterator iter;
4223 std::sort(fileList.begin(), fileList.end());
4225 //If there are <includes>, then add files to the output
4226 //in the order of the include list
4227 if (includes.size()==0)
4228 incs = fileList;
4229 else
4230 {
4231 for (iter = includes.begin() ; iter != includes.end() ; iter++)
4232 {
4233 String &pattern = *iter;
4234 std::vector<String>::iterator siter;
4235 for (siter = fileList.begin() ; siter != fileList.end() ; siter++)
4236 {
4237 String s = *siter;
4238 if (regexMatch(s, pattern))
4239 {
4240 //trace("INCLUDED:%s", s.c_str());
4241 incs.push_back(s);
4242 }
4243 }
4244 }
4245 }
4247 //Now trim off the <excludes>
4248 std::vector<String> res;
4249 for (iter = incs.begin() ; iter != incs.end() ; iter++)
4250 {
4251 String s = *iter;
4252 bool skipme = false;
4253 std::vector<String>::iterator siter;
4254 for (siter = excludes.begin() ; siter != excludes.end() ; siter++)
4255 {
4256 String &pattern = *siter;
4257 if (regexMatch(s, pattern))
4258 {
4259 //trace("EXCLUDED:%s", s.c_str());
4260 skipme = true;
4261 break;
4262 }
4263 }
4264 if (!skipme)
4265 res.push_back(s);
4266 }
4268 fileSet.setFiles(res);
4270 return true;
4271 }
4274 /**
4275 * 0 == all, 1 = cflags, 2 = libs
4276 */
4277 bool MakeBase::pkgConfigRecursive(const String packageName,
4278 const String &path,
4279 const String &prefix,
4280 int query,
4281 String &result,
4282 std::set<String> &deplist)
4283 {
4284 PkgConfig pkgConfig;
4285 if (path.size() > 0)
4286 pkgConfig.setPath(path);
4287 if (prefix.size() > 0)
4288 pkgConfig.setPrefix(prefix);
4289 if (!pkgConfig.query(packageName))
4290 return false;
4291 if (query == 0)
4292 result = pkgConfig.getAll();
4293 else if (query == 1)
4294 result = pkgConfig.getCflags();
4295 else
4296 result = pkgConfig.getLibs();
4297 deplist.insert(packageName);
4298 std::vector<String> list = pkgConfig.getRequireList();
4299 for (unsigned int i = 0 ; i<list.size() ; i++)
4300 {
4301 String depPkgName = list[i];
4302 if (deplist.find(depPkgName) != deplist.end())
4303 continue;
4304 String val;
4305 if (!pkgConfigRecursive(depPkgName, path, prefix, query, val, deplist))
4306 {
4307 error("Based on 'requires' attribute of package '%s'", packageName.c_str());
4308 return false;
4309 }
4310 result.append(" ");
4311 result.append(val);
4312 }
4314 return true;
4315 }
4317 bool MakeBase::pkgConfigQuery(const String &packageName, int query, String &result)
4318 {
4319 std::set<String> deplist;
4320 String path = getProperty("pkg-config-path");
4321 if (path.size()>0)
4322 path = resolve(path);
4323 String prefix = getProperty("pkg-config-prefix");
4324 String val;
4325 if (!pkgConfigRecursive(packageName, path, prefix, query, val, deplist))
4326 return false;
4327 result = val;
4328 return true;
4329 }
4333 /**
4334 * replace a variable ref like ${a} with a value
4335 */
4336 bool MakeBase::lookupProperty(const String &propertyName, String &result)
4337 {
4338 String varname = propertyName;
4339 if (envPrefix.size() > 0 &&
4340 varname.compare(0, envPrefix.size(), envPrefix) == 0)
4341 {
4342 varname = varname.substr(envPrefix.size());
4343 char *envstr = getenv(varname.c_str());
4344 if (!envstr)
4345 {
4346 error("environment variable '%s' not defined", varname.c_str());
4347 return false;
4348 }
4349 result = envstr;
4350 }
4351 else if (pcPrefix.size() > 0 &&
4352 varname.compare(0, pcPrefix.size(), pcPrefix) == 0)
4353 {
4354 varname = varname.substr(pcPrefix.size());
4355 String val;
4356 if (!pkgConfigQuery(varname, 0, val))
4357 return false;
4358 result = val;
4359 }
4360 else if (pccPrefix.size() > 0 &&
4361 varname.compare(0, pccPrefix.size(), pccPrefix) == 0)
4362 {
4363 varname = varname.substr(pccPrefix.size());
4364 String val;
4365 if (!pkgConfigQuery(varname, 1, val))
4366 return false;
4367 result = val;
4368 }
4369 else if (pclPrefix.size() > 0 &&
4370 varname.compare(0, pclPrefix.size(), pclPrefix) == 0)
4371 {
4372 varname = varname.substr(pclPrefix.size());
4373 String val;
4374 if (!pkgConfigQuery(varname, 2, val))
4375 return false;
4376 result = val;
4377 }
4378 else
4379 {
4380 std::map<String, String>::iterator iter;
4381 iter = properties.find(varname);
4382 if (iter != properties.end())
4383 {
4384 result = iter->second;
4385 }
4386 else
4387 {
4388 error("property '%s' not found", varname.c_str());
4389 return false;
4390 }
4391 }
4392 return true;
4393 }
4398 /**
4399 * Analyse a string, looking for any substitutions or other
4400 * things that need resolution
4401 */
4402 bool MakeBase::getSubstitutionsRecursive(const String &str,
4403 String &result, int depth)
4404 {
4405 if (depth > 10)
4406 {
4407 error("nesting of substitutions too deep (>10) for '%s'",
4408 str.c_str());
4409 return false;
4410 }
4411 String s = trim(str);
4412 int len = (int)s.size();
4413 String val;
4414 for (int i=0 ; i<len ; i++)
4415 {
4416 char ch = s[i];
4417 if (ch == '$' && s[i+1] == '{')
4418 {
4419 String varname;
4420 int j = i+2;
4421 for ( ; j<len ; j++)
4422 {
4423 ch = s[j];
4424 if (ch == '$' && s[j+1] == '{')
4425 {
4426 error("attribute %s cannot have nested variable references",
4427 s.c_str());
4428 return false;
4429 }
4430 else if (ch == '}')
4431 {
4432 varname = trim(varname);
4433 String varval;
4434 if (!lookupProperty(varname, varval))
4435 return false;
4436 String varval2;
4437 //Now see if the answer has ${} in it, too
4438 if (!getSubstitutionsRecursive(varval, varval2, depth + 1))
4439 return false;
4440 val.append(varval2);
4441 break;
4442 }
4443 else
4444 {
4445 varname.push_back(ch);
4446 }
4447 }
4448 i = j;
4449 }
4450 else
4451 {
4452 val.push_back(ch);
4453 }
4454 }
4455 result = val;
4456 return true;
4457 }
4459 /**
4460 * Analyse a string, looking for any substitutions or other
4461 * things that need resilution
4462 */
4463 bool MakeBase::getSubstitutions(const String &str, String &result)
4464 {
4465 return getSubstitutionsRecursive(str, result, 0);
4466 }
4470 /**
4471 * replace variable refs like ${a} with their values
4472 * Assume that the string has already been syntax validated
4473 */
4474 String MakeBase::eval(const String &s, const String &defaultVal)
4475 {
4476 if (s.size()==0)
4477 return defaultVal;
4478 String ret;
4479 if (getSubstitutions(s, ret))
4480 return ret;
4481 else
4482 return defaultVal;
4483 }
4486 /**
4487 * replace variable refs like ${a} with their values
4488 * return true or false
4489 * Assume that the string has already been syntax validated
4490 */
4491 bool MakeBase::evalBool(const String &s, bool defaultVal)
4492 {
4493 if (s.size()==0)
4494 return defaultVal;
4495 String val = eval(s, "false");
4496 if (s == "true" || s == "TRUE")
4497 return true;
4498 else
4499 return defaultVal;
4500 }
4503 /**
4504 * Get a string attribute, testing it for proper syntax and
4505 * property names.
4506 */
4507 bool MakeBase::getAttribute(Element *elem, const String &name,
4508 String &result)
4509 {
4510 String s = elem->getAttribute(name);
4511 String tmp;
4512 bool ret = getSubstitutions(s, tmp);
4513 if (ret)
4514 result = s; //assign -if- ok
4515 return ret;
4516 }
4519 /**
4520 * Get a string value, testing it for proper syntax and
4521 * property names.
4522 */
4523 bool MakeBase::getValue(Element *elem, String &result)
4524 {
4525 String s = elem->getValue();
4526 String tmp;
4527 bool ret = getSubstitutions(s, tmp);
4528 if (ret)
4529 result = s; //assign -if- ok
4530 return ret;
4531 }
4536 /**
4537 * Parse a <patternset> entry
4538 */
4539 bool MakeBase::parsePatternSet(Element *elem,
4540 MakeBase &propRef,
4541 std::vector<String> &includes,
4542 std::vector<String> &excludes
4543 )
4544 {
4545 std::vector<Element *> children = elem->getChildren();
4546 for (unsigned int i=0 ; i<children.size() ; i++)
4547 {
4548 Element *child = children[i];
4549 String tagName = child->getName();
4550 if (tagName == "exclude")
4551 {
4552 String fname;
4553 if (!propRef.getAttribute(child, "name", fname))
4554 return false;
4555 //trace("EXCLUDE: %s", fname.c_str());
4556 excludes.push_back(fname);
4557 }
4558 else if (tagName == "include")
4559 {
4560 String fname;
4561 if (!propRef.getAttribute(child, "name", fname))
4562 return false;
4563 //trace("INCLUDE: %s", fname.c_str());
4564 includes.push_back(fname);
4565 }
4566 }
4568 return true;
4569 }
4574 /**
4575 * Parse a <fileset> entry, and determine which files
4576 * should be included
4577 */
4578 bool MakeBase::parseFileSet(Element *elem,
4579 MakeBase &propRef,
4580 FileSet &fileSet)
4581 {
4582 String name = elem->getName();
4583 if (name != "fileset")
4584 {
4585 error("expected <fileset>");
4586 return false;
4587 }
4590 std::vector<String> includes;
4591 std::vector<String> excludes;
4593 //A fileset has one implied patternset
4594 if (!parsePatternSet(elem, propRef, includes, excludes))
4595 {
4596 return false;
4597 }
4598 //Look for child tags, including more patternsets
4599 std::vector<Element *> children = elem->getChildren();
4600 for (unsigned int i=0 ; i<children.size() ; i++)
4601 {
4602 Element *child = children[i];
4603 String tagName = child->getName();
4604 if (tagName == "patternset")
4605 {
4606 if (!parsePatternSet(child, propRef, includes, excludes))
4607 {
4608 return false;
4609 }
4610 }
4611 }
4613 String dir;
4614 //Now do the stuff
4615 //Get the base directory for reading file names
4616 if (!propRef.getAttribute(elem, "dir", dir))
4617 return false;
4619 fileSet.setDirectory(dir);
4620 fileSet.setIncludes(includes);
4621 fileSet.setExcludes(excludes);
4623 /*
4624 std::vector<String> fileList;
4625 if (dir.size() > 0)
4626 {
4627 String baseDir = propRef.resolve(dir);
4628 if (!listFiles(baseDir, "", includes, excludes, fileList))
4629 return false;
4630 }
4631 std::sort(fileList.begin(), fileList.end());
4632 result = fileList;
4633 */
4636 /*
4637 for (unsigned int i=0 ; i<result.size() ; i++)
4638 {
4639 trace("RES:%s", result[i].c_str());
4640 }
4641 */
4644 return true;
4645 }
4647 /**
4648 * Parse a <filelist> entry. This is far simpler than FileSet,
4649 * since no directory scanning is needed. The file names are listed
4650 * explicitly.
4651 */
4652 bool MakeBase::parseFileList(Element *elem,
4653 MakeBase &propRef,
4654 FileList &fileList)
4655 {
4656 std::vector<String> fnames;
4657 //Look for child tags, namely "file"
4658 std::vector<Element *> children = elem->getChildren();
4659 for (unsigned int i=0 ; i<children.size() ; i++)
4660 {
4661 Element *child = children[i];
4662 String tagName = child->getName();
4663 if (tagName == "file")
4664 {
4665 String fname = child->getAttribute("name");
4666 if (fname.size()==0)
4667 {
4668 error("<file> element requires name="" attribute");
4669 return false;
4670 }
4671 fnames.push_back(fname);
4672 }
4673 else
4674 {
4675 error("tag <%s> not allowed in <fileset>", tagName.c_str());
4676 return false;
4677 }
4678 }
4680 String dir;
4681 //Get the base directory for reading file names
4682 if (!propRef.getAttribute(elem, "dir", dir))
4683 return false;
4684 fileList.setDirectory(dir);
4685 fileList.setFiles(fnames);
4687 return true;
4688 }
4692 /**
4693 * Create a directory, making intermediate dirs
4694 * if necessary
4695 */
4696 bool MakeBase::createDirectory(const String &dirname)
4697 {
4698 //trace("## createDirectory: %s", dirname.c_str());
4699 //## first check if it exists
4700 struct stat finfo;
4701 String nativeDir = getNativePath(dirname);
4702 char *cnative = (char *) nativeDir.c_str();
4703 #ifdef __WIN32__
4704 if (strlen(cnative)==2 && cnative[1]==':')
4705 return true;
4706 #endif
4707 if (stat(cnative, &finfo)==0)
4708 {
4709 if (!S_ISDIR(finfo.st_mode))
4710 {
4711 error("mkdir: file %s exists but is not a directory",
4712 cnative);
4713 return false;
4714 }
4715 else //exists
4716 {
4717 return true;
4718 }
4719 }
4721 //## 2: pull off the last path segment, if any,
4722 //## to make the dir 'above' this one, if necessary
4723 unsigned int pos = dirname.find_last_of('/');
4724 if (pos>0 && pos != dirname.npos)
4725 {
4726 String subpath = dirname.substr(0, pos);
4727 //A letter root (c:) ?
4728 if (!createDirectory(subpath))
4729 return false;
4730 }
4732 //## 3: now make
4733 #ifdef __WIN32__
4734 if (mkdir(cnative)<0)
4735 #else
4736 if (mkdir(cnative, S_IRWXU | S_IRWXG | S_IRWXO)<0)
4737 #endif
4738 {
4739 error("cannot make directory '%s' : %s",
4740 cnative, strerror(errno));
4741 return false;
4742 }
4744 return true;
4745 }
4748 /**
4749 * Remove a directory recursively
4750 */
4751 bool MakeBase::removeDirectory(const String &dirName)
4752 {
4753 char *dname = (char *)dirName.c_str();
4755 DIR *dir = opendir(dname);
4756 if (!dir)
4757 {
4758 //# Let this fail nicely.
4759 return true;
4760 //error("error opening directory %s : %s", dname, strerror(errno));
4761 //return false;
4762 }
4764 while (true)
4765 {
4766 struct dirent *de = readdir(dir);
4767 if (!de)
4768 break;
4770 //Get the directory member name
4771 String s = de->d_name;
4772 if (s.size() == 0 || s[0] == '.')
4773 continue;
4774 String childName;
4775 if (dirName.size() > 0)
4776 {
4777 childName.append(dirName);
4778 childName.append("/");
4779 }
4780 childName.append(s);
4783 struct stat finfo;
4784 String childNative = getNativePath(childName);
4785 char *cnative = (char *)childNative.c_str();
4786 if (stat(cnative, &finfo)<0)
4787 {
4788 error("cannot stat file:%s", cnative);
4789 }
4790 else if (S_ISDIR(finfo.st_mode))
4791 {
4792 //trace("DEL dir: %s", childName.c_str());
4793 if (!removeDirectory(childName))
4794 {
4795 return false;
4796 }
4797 }
4798 else if (!S_ISREG(finfo.st_mode))
4799 {
4800 //trace("not regular: %s", cnative);
4801 }
4802 else
4803 {
4804 //trace("DEL file: %s", childName.c_str());
4805 if (remove(cnative)<0)
4806 {
4807 error("error deleting %s : %s",
4808 cnative, strerror(errno));
4809 return false;
4810 }
4811 }
4812 }
4813 closedir(dir);
4815 //Now delete the directory
4816 String native = getNativePath(dirName);
4817 if (rmdir(native.c_str())<0)
4818 {
4819 error("could not delete directory %s : %s",
4820 native.c_str() , strerror(errno));
4821 return false;
4822 }
4824 return true;
4826 }
4829 /**
4830 * Copy a file from one name to another. Perform only if needed
4831 */
4832 bool MakeBase::copyFile(const String &srcFile, const String &destFile)
4833 {
4834 //# 1 Check up-to-date times
4835 String srcNative = getNativePath(srcFile);
4836 struct stat srcinfo;
4837 if (stat(srcNative.c_str(), &srcinfo)<0)
4838 {
4839 error("source file %s for copy does not exist",
4840 srcNative.c_str());
4841 return false;
4842 }
4844 String destNative = getNativePath(destFile);
4845 struct stat destinfo;
4846 if (stat(destNative.c_str(), &destinfo)==0)
4847 {
4848 if (destinfo.st_mtime >= srcinfo.st_mtime)
4849 return true;
4850 }
4852 //# 2 prepare a destination directory if necessary
4853 unsigned int pos = destFile.find_last_of('/');
4854 if (pos != destFile.npos)
4855 {
4856 String subpath = destFile.substr(0, pos);
4857 if (!createDirectory(subpath))
4858 return false;
4859 }
4861 //# 3 do the data copy
4862 #ifndef __WIN32__
4864 FILE *srcf = fopen(srcNative.c_str(), "rb");
4865 if (!srcf)
4866 {
4867 error("copyFile cannot open '%s' for reading", srcNative.c_str());
4868 return false;
4869 }
4870 FILE *destf = fopen(destNative.c_str(), "wb");
4871 if (!destf)
4872 {
4873 error("copyFile cannot open %s for writing", srcNative.c_str());
4874 return false;
4875 }
4877 while (!feof(srcf))
4878 {
4879 int ch = fgetc(srcf);
4880 if (ch<0)
4881 break;
4882 fputc(ch, destf);
4883 }
4885 fclose(destf);
4886 fclose(srcf);
4888 #else
4890 if (!CopyFile(srcNative.c_str(), destNative.c_str(), false))
4891 {
4892 error("copyFile from %s to %s failed",
4893 srcNative.c_str(), destNative.c_str());
4894 return false;
4895 }
4897 #endif /* __WIN32__ */
4900 return true;
4901 }
4905 /**
4906 * Tests if the file exists and is a regular file
4907 */
4908 bool MakeBase::isRegularFile(const String &fileName)
4909 {
4910 String native = getNativePath(fileName);
4911 struct stat finfo;
4913 //Exists?
4914 if (stat(native.c_str(), &finfo)<0)
4915 return false;
4918 //check the file mode
4919 if (!S_ISREG(finfo.st_mode))
4920 return false;
4922 return true;
4923 }
4925 /**
4926 * Tests if the file exists and is a directory
4927 */
4928 bool MakeBase::isDirectory(const String &fileName)
4929 {
4930 String native = getNativePath(fileName);
4931 struct stat finfo;
4933 //Exists?
4934 if (stat(native.c_str(), &finfo)<0)
4935 return false;
4938 //check the file mode
4939 if (!S_ISDIR(finfo.st_mode))
4940 return false;
4942 return true;
4943 }
4947 /**
4948 * Tests is the modification of fileA is newer than fileB
4949 */
4950 bool MakeBase::isNewerThan(const String &fileA, const String &fileB)
4951 {
4952 //trace("isNewerThan:'%s' , '%s'", fileA.c_str(), fileB.c_str());
4953 String nativeA = getNativePath(fileA);
4954 struct stat infoA;
4955 //IF source does not exist, NOT newer
4956 if (stat(nativeA.c_str(), &infoA)<0)
4957 {
4958 return false;
4959 }
4961 String nativeB = getNativePath(fileB);
4962 struct stat infoB;
4963 //IF dest does not exist, YES, newer
4964 if (stat(nativeB.c_str(), &infoB)<0)
4965 {
4966 return true;
4967 }
4969 //check the actual times
4970 if (infoA.st_mtime > infoB.st_mtime)
4971 {
4972 return true;
4973 }
4975 return false;
4976 }
4979 //########################################################################
4980 //# P K G C O N F I G
4981 //########################################################################
4984 /**
4985 * Get a character from the buffer at pos. If out of range,
4986 * return -1 for safety
4987 */
4988 int PkgConfig::get(int pos)
4989 {
4990 if (pos>parselen)
4991 return -1;
4992 return parsebuf[pos];
4993 }
4997 /**
4998 * Skip over all whitespace characters beginning at pos. Return
4999 * the position of the first non-whitespace character.
5000 * Pkg-config is line-oriented, so check for newline
5001 */
5002 int PkgConfig::skipwhite(int pos)
5003 {
5004 while (pos < parselen)
5005 {
5006 int ch = get(pos);
5007 if (ch < 0)
5008 break;
5009 if (!isspace(ch))
5010 break;
5011 pos++;
5012 }
5013 return pos;
5014 }
5017 /**
5018 * Parse the buffer beginning at pos, for a word. Fill
5019 * 'ret' with the result. Return the position after the
5020 * word.
5021 */
5022 int PkgConfig::getword(int pos, String &ret)
5023 {
5024 while (pos < parselen)
5025 {
5026 int ch = get(pos);
5027 if (ch < 0)
5028 break;
5029 if (!isalnum(ch) && ch != '_' && ch != '-' && ch != '+' && ch != '.')
5030 break;
5031 ret.push_back((char)ch);
5032 pos++;
5033 }
5034 return pos;
5035 }
5037 bool PkgConfig::parseRequires()
5038 {
5039 if (requires.size() == 0)
5040 return true;
5041 parsebuf = (char *)requires.c_str();
5042 parselen = requires.size();
5043 int pos = 0;
5044 while (pos < parselen)
5045 {
5046 pos = skipwhite(pos);
5047 String val;
5048 int pos2 = getword(pos, val);
5049 if (pos2 == pos)
5050 break;
5051 pos = pos2;
5052 //trace("val %s", val.c_str());
5053 requireList.push_back(val);
5054 }
5055 return true;
5056 }
5059 static int getint(const String str)
5060 {
5061 char *s = (char *)str.c_str();
5062 char *ends = NULL;
5063 long val = strtol(s, &ends, 10);
5064 if (ends == s)
5065 return 0L;
5066 else
5067 return val;
5068 }
5070 void PkgConfig::parseVersion()
5071 {
5072 if (version.size() == 0)
5073 return;
5074 String s1, s2, s3;
5075 unsigned int pos = 0;
5076 unsigned int pos2 = version.find('.', pos);
5077 if (pos2 == version.npos)
5078 {
5079 s1 = version;
5080 }
5081 else
5082 {
5083 s1 = version.substr(pos, pos2-pos);
5084 pos = pos2;
5085 pos++;
5086 if (pos < version.size())
5087 {
5088 pos2 = version.find('.', pos);
5089 if (pos2 == version.npos)
5090 {
5091 s2 = version.substr(pos, version.size()-pos);
5092 }
5093 else
5094 {
5095 s2 = version.substr(pos, pos2-pos);
5096 pos = pos2;
5097 pos++;
5098 if (pos < version.size())
5099 s3 = version.substr(pos, pos2-pos);
5100 }
5101 }
5102 }
5104 majorVersion = getint(s1);
5105 minorVersion = getint(s2);
5106 microVersion = getint(s3);
5107 //trace("version:%d.%d.%d", majorVersion,
5108 // minorVersion, microVersion );
5109 }
5112 bool PkgConfig::parseLine(const String &lineBuf)
5113 {
5114 parsebuf = (char *)lineBuf.c_str();
5115 parselen = lineBuf.size();
5116 int pos = 0;
5118 while (pos < parselen)
5119 {
5120 String attrName;
5121 pos = skipwhite(pos);
5122 int ch = get(pos);
5123 if (ch == '#')
5124 {
5125 //comment. eat the rest of the line
5126 while (pos < parselen)
5127 {
5128 ch = get(pos);
5129 if (ch == '\n' || ch < 0)
5130 break;
5131 pos++;
5132 }
5133 continue;
5134 }
5135 pos = getword(pos, attrName);
5136 if (attrName.size() == 0)
5137 continue;
5139 pos = skipwhite(pos);
5140 ch = get(pos);
5141 if (ch != ':' && ch != '=')
5142 {
5143 error("expected ':' or '='");
5144 return false;
5145 }
5146 pos++;
5147 pos = skipwhite(pos);
5148 String attrVal;
5149 while (pos < parselen)
5150 {
5151 ch = get(pos);
5152 if (ch == '\n' || ch < 0)
5153 break;
5154 else if (ch == '$' && get(pos+1) == '{')
5155 {
5156 //# this is a ${substitution}
5157 pos += 2;
5158 String subName;
5159 while (pos < parselen)
5160 {
5161 ch = get(pos);
5162 if (ch < 0)
5163 {
5164 error("unterminated substitution");
5165 return false;
5166 }
5167 else if (ch == '}')
5168 break;
5169 else
5170 subName.push_back((char)ch);
5171 pos++;
5172 }
5173 //trace("subName:%s %s", subName.c_str(), prefix.c_str());
5174 if (subName == "prefix" && prefix.size()>0)
5175 {
5176 attrVal.append(prefix);
5177 //trace("prefix override:%s", prefix.c_str());
5178 }
5179 else
5180 {
5181 String subVal = attrs[subName];
5182 //trace("subVal:%s", subVal.c_str());
5183 attrVal.append(subVal);
5184 }
5185 }
5186 else
5187 attrVal.push_back((char)ch);
5188 pos++;
5189 }
5191 attrVal = trim(attrVal);
5192 attrs[attrName] = attrVal;
5194 String attrNameL = toLower(attrName);
5196 if (attrNameL == "name")
5197 name = attrVal;
5198 else if (attrNameL == "description")
5199 description = attrVal;
5200 else if (attrNameL == "cflags")
5201 cflags = attrVal;
5202 else if (attrNameL == "libs")
5203 libs = attrVal;
5204 else if (attrNameL == "requires")
5205 requires = attrVal;
5206 else if (attrNameL == "version")
5207 version = attrVal;
5209 //trace("name:'%s' value:'%s'",
5210 // attrName.c_str(), attrVal.c_str());
5211 }
5213 return true;
5214 }
5217 bool PkgConfig::parse(const String &buf)
5218 {
5219 init();
5221 String line;
5222 int lineNr = 0;
5223 for (unsigned int p=0 ; p<buf.size() ; p++)
5224 {
5225 int ch = buf[p];
5226 if (ch == '\n' || ch == '\r')
5227 {
5228 if (!parseLine(line))
5229 return false;
5230 line.clear();
5231 lineNr++;
5232 }
5233 else
5234 {
5235 line.push_back(ch);
5236 }
5237 }
5238 if (line.size()>0)
5239 {
5240 if (!parseLine(line))
5241 return false;
5242 }
5244 parseRequires();
5245 parseVersion();
5247 return true;
5248 }
5253 void PkgConfig::dumpAttrs()
5254 {
5255 //trace("### PkgConfig attributes for %s", fileName.c_str());
5256 std::map<String, String>::iterator iter;
5257 for (iter=attrs.begin() ; iter!=attrs.end() ; iter++)
5258 {
5259 trace(" %s = %s", iter->first.c_str(), iter->second.c_str());
5260 }
5261 }
5264 bool PkgConfig::readFile(const String &fname)
5265 {
5266 fileName = getNativePath(fname);
5268 FILE *f = fopen(fileName.c_str(), "r");
5269 if (!f)
5270 {
5271 error("cannot open file '%s' for reading", fileName.c_str());
5272 return false;
5273 }
5274 String buf;
5275 while (true)
5276 {
5277 int ch = fgetc(f);
5278 if (ch < 0)
5279 break;
5280 buf.push_back((char)ch);
5281 }
5282 fclose(f);
5284 //trace("####### File:\n%s", buf.c_str());
5285 if (!parse(buf))
5286 {
5287 return false;
5288 }
5290 //dumpAttrs();
5292 return true;
5293 }
5297 bool PkgConfig::query(const String &pkgName)
5298 {
5299 name = pkgName;
5301 String fname = path;
5302 fname.append("/");
5303 fname.append(name);
5304 fname.append(".pc");
5306 if (!readFile(fname))
5307 return false;
5309 return true;
5310 }
5316 //########################################################################
5317 //# D E P T O O L
5318 //########################################################################
5322 /**
5323 * Class which holds information for each file.
5324 */
5325 class FileRec
5326 {
5327 public:
5329 typedef enum
5330 {
5331 UNKNOWN,
5332 CFILE,
5333 HFILE,
5334 OFILE
5335 } FileType;
5337 /**
5338 * Constructor
5339 */
5340 FileRec()
5341 { init(); type = UNKNOWN; }
5343 /**
5344 * Copy constructor
5345 */
5346 FileRec(const FileRec &other)
5347 { init(); assign(other); }
5348 /**
5349 * Constructor
5350 */
5351 FileRec(int typeVal)
5352 { init(); type = typeVal; }
5353 /**
5354 * Assignment operator
5355 */
5356 FileRec &operator=(const FileRec &other)
5357 { init(); assign(other); return *this; }
5360 /**
5361 * Destructor
5362 */
5363 ~FileRec()
5364 {}
5366 /**
5367 * Directory part of the file name
5368 */
5369 String path;
5371 /**
5372 * Base name, sans directory and suffix
5373 */
5374 String baseName;
5376 /**
5377 * File extension, such as cpp or h
5378 */
5379 String suffix;
5381 /**
5382 * Type of file: CFILE, HFILE, OFILE
5383 */
5384 int type;
5386 /**
5387 * Used to list files ref'd by this one
5388 */
5389 std::map<String, FileRec *> files;
5392 private:
5394 void init()
5395 {
5396 }
5398 void assign(const FileRec &other)
5399 {
5400 type = other.type;
5401 baseName = other.baseName;
5402 suffix = other.suffix;
5403 files = other.files;
5404 }
5406 };
5410 /**
5411 * Simpler dependency record
5412 */
5413 class DepRec
5414 {
5415 public:
5417 /**
5418 * Constructor
5419 */
5420 DepRec()
5421 {init();}
5423 /**
5424 * Copy constructor
5425 */
5426 DepRec(const DepRec &other)
5427 {init(); assign(other);}
5428 /**
5429 * Constructor
5430 */
5431 DepRec(const String &fname)
5432 {init(); name = fname; }
5433 /**
5434 * Assignment operator
5435 */
5436 DepRec &operator=(const DepRec &other)
5437 {init(); assign(other); return *this;}
5440 /**
5441 * Destructor
5442 */
5443 ~DepRec()
5444 {}
5446 /**
5447 * Directory part of the file name
5448 */
5449 String path;
5451 /**
5452 * Base name, without the path and suffix
5453 */
5454 String name;
5456 /**
5457 * Suffix of the source
5458 */
5459 String suffix;
5462 /**
5463 * Used to list files ref'd by this one
5464 */
5465 std::vector<String> files;
5468 private:
5470 void init()
5471 {
5472 }
5474 void assign(const DepRec &other)
5475 {
5476 path = other.path;
5477 name = other.name;
5478 suffix = other.suffix;
5479 files = other.files; //avoid recursion
5480 }
5482 };
5485 class DepTool : public MakeBase
5486 {
5487 public:
5489 /**
5490 * Constructor
5491 */
5492 DepTool()
5493 { init(); }
5495 /**
5496 * Copy constructor
5497 */
5498 DepTool(const DepTool &other)
5499 { init(); assign(other); }
5501 /**
5502 * Assignment operator
5503 */
5504 DepTool &operator=(const DepTool &other)
5505 { init(); assign(other); return *this; }
5508 /**
5509 * Destructor
5510 */
5511 ~DepTool()
5512 {}
5515 /**
5516 * Reset this section of code
5517 */
5518 virtual void init();
5520 /**
5521 * Reset this section of code
5522 */
5523 virtual void assign(const DepTool &other)
5524 {
5525 }
5527 /**
5528 * Sets the source directory which will be scanned
5529 */
5530 virtual void setSourceDirectory(const String &val)
5531 { sourceDir = val; }
5533 /**
5534 * Returns the source directory which will be scanned
5535 */
5536 virtual String getSourceDirectory()
5537 { return sourceDir; }
5539 /**
5540 * Sets the list of files within the directory to analyze
5541 */
5542 virtual void setFileList(const std::vector<String> &list)
5543 { fileList = list; }
5545 /**
5546 * Creates the list of all file names which will be
5547 * candidates for further processing. Reads make.exclude
5548 * to see which files for directories to leave out.
5549 */
5550 virtual bool createFileList();
5553 /**
5554 * Generates the forward dependency list
5555 */
5556 virtual bool generateDependencies();
5559 /**
5560 * Generates the forward dependency list, saving the file
5561 */
5562 virtual bool generateDependencies(const String &);
5565 /**
5566 * Load a dependency file
5567 */
5568 std::vector<DepRec> loadDepFile(const String &fileName);
5570 /**
5571 * Load a dependency file, generating one if necessary
5572 */
5573 std::vector<DepRec> getDepFile(const String &fileName,
5574 bool forceRefresh);
5576 /**
5577 * Save a dependency file
5578 */
5579 bool saveDepFile(const String &fileName);
5582 private:
5585 /**
5586 *
5587 */
5588 void parseName(const String &fullname,
5589 String &path,
5590 String &basename,
5591 String &suffix);
5593 /**
5594 *
5595 */
5596 int get(int pos);
5598 /**
5599 *
5600 */
5601 int skipwhite(int pos);
5603 /**
5604 *
5605 */
5606 int getword(int pos, String &ret);
5608 /**
5609 *
5610 */
5611 bool sequ(int pos, const char *key);
5613 /**
5614 *
5615 */
5616 bool addIncludeFile(FileRec *frec, const String &fname);
5618 /**
5619 *
5620 */
5621 bool scanFile(const String &fname, FileRec *frec);
5623 /**
5624 *
5625 */
5626 bool processDependency(FileRec *ofile, FileRec *include);
5628 /**
5629 *
5630 */
5631 String sourceDir;
5633 /**
5634 *
5635 */
5636 std::vector<String> fileList;
5638 /**
5639 *
5640 */
5641 std::vector<String> directories;
5643 /**
5644 * A list of all files which will be processed for
5645 * dependencies.
5646 */
5647 std::map<String, FileRec *> allFiles;
5649 /**
5650 * The list of .o files, and the
5651 * dependencies upon them.
5652 */
5653 std::map<String, FileRec *> oFiles;
5655 int depFileSize;
5656 char *depFileBuf;
5658 static const int readBufSize = 8192;
5659 char readBuf[8193];//byte larger
5661 };
5667 /**
5668 * Clean up after processing. Called by the destructor, but should
5669 * also be called before the object is reused.
5670 */
5671 void DepTool::init()
5672 {
5673 sourceDir = ".";
5675 fileList.clear();
5676 directories.clear();
5678 //clear output file list
5679 std::map<String, FileRec *>::iterator iter;
5680 for (iter=oFiles.begin(); iter!=oFiles.end() ; iter++)
5681 delete iter->second;
5682 oFiles.clear();
5684 //allFiles actually contains the master copies. delete them
5685 for (iter= allFiles.begin(); iter!=allFiles.end() ; iter++)
5686 delete iter->second;
5687 allFiles.clear();
5689 }
5694 /**
5695 * Parse a full path name into path, base name, and suffix
5696 */
5697 void DepTool::parseName(const String &fullname,
5698 String &path,
5699 String &basename,
5700 String &suffix)
5701 {
5702 if (fullname.size() < 2)
5703 return;
5705 unsigned int pos = fullname.find_last_of('/');
5706 if (pos != fullname.npos && pos<fullname.size()-1)
5707 {
5708 path = fullname.substr(0, pos);
5709 pos++;
5710 basename = fullname.substr(pos, fullname.size()-pos);
5711 }
5712 else
5713 {
5714 path = "";
5715 basename = fullname;
5716 }
5718 pos = basename.find_last_of('.');
5719 if (pos != basename.npos && pos<basename.size()-1)
5720 {
5721 suffix = basename.substr(pos+1, basename.size()-pos-1);
5722 basename = basename.substr(0, pos);
5723 }
5725 //trace("parsename:%s %s %s", path.c_str(),
5726 // basename.c_str(), suffix.c_str());
5727 }
5731 /**
5732 * Generate our internal file list.
5733 */
5734 bool DepTool::createFileList()
5735 {
5737 for (unsigned int i=0 ; i<fileList.size() ; i++)
5738 {
5739 String fileName = fileList[i];
5740 //trace("## FileName:%s", fileName.c_str());
5741 String path;
5742 String basename;
5743 String sfx;
5744 parseName(fileName, path, basename, sfx);
5745 if (sfx == "cpp" || sfx == "c" || sfx == "cxx" ||
5746 sfx == "cc" || sfx == "CC")
5747 {
5748 FileRec *fe = new FileRec(FileRec::CFILE);
5749 fe->path = path;
5750 fe->baseName = basename;
5751 fe->suffix = sfx;
5752 allFiles[fileName] = fe;
5753 }
5754 else if (sfx == "h" || sfx == "hh" ||
5755 sfx == "hpp" || sfx == "hxx")
5756 {
5757 FileRec *fe = new FileRec(FileRec::HFILE);
5758 fe->path = path;
5759 fe->baseName = basename;
5760 fe->suffix = sfx;
5761 allFiles[fileName] = fe;
5762 }
5763 }
5765 if (!listDirectories(sourceDir, "", directories))
5766 return false;
5768 return true;
5769 }
5775 /**
5776 * Get a character from the buffer at pos. If out of range,
5777 * return -1 for safety
5778 */
5779 int DepTool::get(int pos)
5780 {
5781 if (pos>depFileSize)
5782 return -1;
5783 return depFileBuf[pos];
5784 }
5788 /**
5789 * Skip over all whitespace characters beginning at pos. Return
5790 * the position of the first non-whitespace character.
5791 */
5792 int DepTool::skipwhite(int pos)
5793 {
5794 while (pos < depFileSize)
5795 {
5796 int ch = get(pos);
5797 if (ch < 0)
5798 break;
5799 if (!isspace(ch))
5800 break;
5801 pos++;
5802 }
5803 return pos;
5804 }
5807 /**
5808 * Parse the buffer beginning at pos, for a word. Fill
5809 * 'ret' with the result. Return the position after the
5810 * word.
5811 */
5812 int DepTool::getword(int pos, String &ret)
5813 {
5814 while (pos < depFileSize)
5815 {
5816 int ch = get(pos);
5817 if (ch < 0)
5818 break;
5819 if (isspace(ch))
5820 break;
5821 ret.push_back((char)ch);
5822 pos++;
5823 }
5824 return pos;
5825 }
5827 /**
5828 * Return whether the sequence of characters in the buffer
5829 * beginning at pos match the key, for the length of the key
5830 */
5831 bool DepTool::sequ(int pos, const char *key)
5832 {
5833 while (*key)
5834 {
5835 if (*key != get(pos))
5836 return false;
5837 key++; pos++;
5838 }
5839 return true;
5840 }
5844 /**
5845 * Add an include file name to a file record. If the name
5846 * is not found in allFiles explicitly, try prepending include
5847 * directory names to it and try again.
5848 */
5849 bool DepTool::addIncludeFile(FileRec *frec, const String &iname)
5850 {
5851 //# if the name is an exact match to a path name
5852 //# in allFiles, like "myinc.h"
5853 std::map<String, FileRec *>::iterator iter =
5854 allFiles.find(iname);
5855 if (iter != allFiles.end()) //already exists
5856 {
5857 //h file in same dir
5858 FileRec *other = iter->second;
5859 //trace("local: '%s'", iname.c_str());
5860 frec->files[iname] = other;
5861 return true;
5862 }
5863 else
5864 {
5865 //## Ok, it was not found directly
5866 //look in other dirs
5867 std::vector<String>::iterator diter;
5868 for (diter=directories.begin() ;
5869 diter!=directories.end() ; diter++)
5870 {
5871 String dfname = *diter;
5872 dfname.append("/");
5873 dfname.append(iname);
5874 URI fullPathURI(dfname); //normalize path name
5875 String fullPath = fullPathURI.getPath();
5876 if (fullPath[0] == '/')
5877 fullPath = fullPath.substr(1);
5878 //trace("Normalized %s to %s", dfname.c_str(), fullPath.c_str());
5879 iter = allFiles.find(fullPath);
5880 if (iter != allFiles.end())
5881 {
5882 FileRec *other = iter->second;
5883 //trace("other: '%s'", iname.c_str());
5884 frec->files[fullPath] = other;
5885 return true;
5886 }
5887 }
5888 }
5889 return true;
5890 }
5894 /**
5895 * Lightly parse a file to find the #include directives. Do
5896 * a bit of state machine stuff to make sure that the directive
5897 * is valid. (Like not in a comment).
5898 */
5899 bool DepTool::scanFile(const String &fname, FileRec *frec)
5900 {
5901 String fileName;
5902 if (sourceDir.size() > 0)
5903 {
5904 fileName.append(sourceDir);
5905 fileName.append("/");
5906 }
5907 fileName.append(fname);
5908 String nativeName = getNativePath(fileName);
5909 FILE *f = fopen(nativeName.c_str(), "r");
5910 if (!f)
5911 {
5912 error("Could not open '%s' for reading", fname.c_str());
5913 return false;
5914 }
5915 String buf;
5916 while (!feof(f))
5917 {
5918 int nrbytes = fread(readBuf, 1, readBufSize, f);
5919 readBuf[nrbytes] = '\0';
5920 buf.append(readBuf);
5921 }
5922 fclose(f);
5924 depFileSize = buf.size();
5925 depFileBuf = (char *)buf.c_str();
5926 int pos = 0;
5929 while (pos < depFileSize)
5930 {
5931 //trace("p:%c", get(pos));
5933 //# Block comment
5934 if (get(pos) == '/' && get(pos+1) == '*')
5935 {
5936 pos += 2;
5937 while (pos < depFileSize)
5938 {
5939 if (get(pos) == '*' && get(pos+1) == '/')
5940 {
5941 pos += 2;
5942 break;
5943 }
5944 else
5945 pos++;
5946 }
5947 }
5948 //# Line comment
5949 else if (get(pos) == '/' && get(pos+1) == '/')
5950 {
5951 pos += 2;
5952 while (pos < depFileSize)
5953 {
5954 if (get(pos) == '\n')
5955 {
5956 pos++;
5957 break;
5958 }
5959 else
5960 pos++;
5961 }
5962 }
5963 //# #include! yaay
5964 else if (sequ(pos, "#include"))
5965 {
5966 pos += 8;
5967 pos = skipwhite(pos);
5968 String iname;
5969 pos = getword(pos, iname);
5970 if (iname.size()>2)
5971 {
5972 iname = iname.substr(1, iname.size()-2);
5973 addIncludeFile(frec, iname);
5974 }
5975 }
5976 else
5977 {
5978 pos++;
5979 }
5980 }
5982 return true;
5983 }
5987 /**
5988 * Recursively check include lists to find all files in allFiles to which
5989 * a given file is dependent.
5990 */
5991 bool DepTool::processDependency(FileRec *ofile, FileRec *include)
5992 {
5993 std::map<String, FileRec *>::iterator iter;
5994 for (iter=include->files.begin() ; iter!=include->files.end() ; iter++)
5995 {
5996 String fname = iter->first;
5997 if (ofile->files.find(fname) != ofile->files.end())
5998 {
5999 //trace("file '%s' already seen", fname.c_str());
6000 continue;
6001 }
6002 FileRec *child = iter->second;
6003 ofile->files[fname] = child;
6005 processDependency(ofile, child);
6006 }
6009 return true;
6010 }
6016 /**
6017 * Generate the file dependency list.
6018 */
6019 bool DepTool::generateDependencies()
6020 {
6021 std::map<String, FileRec *>::iterator iter;
6022 //# First pass. Scan for all includes
6023 for (iter=allFiles.begin() ; iter!=allFiles.end() ; iter++)
6024 {
6025 FileRec *frec = iter->second;
6026 if (!scanFile(iter->first, frec))
6027 {
6028 //quit?
6029 }
6030 }
6032 //# Second pass. Scan for all includes
6033 for (iter=allFiles.begin() ; iter!=allFiles.end() ; iter++)
6034 {
6035 FileRec *include = iter->second;
6036 if (include->type == FileRec::CFILE)
6037 {
6038 //String cFileName = iter->first;
6039 FileRec *ofile = new FileRec(FileRec::OFILE);
6040 ofile->path = include->path;
6041 ofile->baseName = include->baseName;
6042 ofile->suffix = include->suffix;
6043 String fname = include->path;
6044 if (fname.size()>0)
6045 fname.append("/");
6046 fname.append(include->baseName);
6047 fname.append(".o");
6048 oFiles[fname] = ofile;
6049 //add the .c file first? no, don't
6050 //ofile->files[cFileName] = include;
6052 //trace("ofile:%s", fname.c_str());
6054 processDependency(ofile, include);
6055 }
6056 }
6059 return true;
6060 }
6064 /**
6065 * High-level call to generate deps and optionally save them
6066 */
6067 bool DepTool::generateDependencies(const String &fileName)
6068 {
6069 if (!createFileList())
6070 return false;
6071 if (!generateDependencies())
6072 return false;
6073 if (!saveDepFile(fileName))
6074 return false;
6075 return true;
6076 }
6079 /**
6080 * This saves the dependency cache.
6081 */
6082 bool DepTool::saveDepFile(const String &fileName)
6083 {
6084 time_t tim;
6085 time(&tim);
6087 FILE *f = fopen(fileName.c_str(), "w");
6088 if (!f)
6089 {
6090 trace("cannot open '%s' for writing", fileName.c_str());
6091 }
6092 fprintf(f, "<?xml version='1.0'?>\n");
6093 fprintf(f, "<!--\n");
6094 fprintf(f, "########################################################\n");
6095 fprintf(f, "## File: build.dep\n");
6096 fprintf(f, "## Generated by BuildTool at :%s", ctime(&tim));
6097 fprintf(f, "########################################################\n");
6098 fprintf(f, "-->\n");
6100 fprintf(f, "<dependencies source='%s'>\n\n", sourceDir.c_str());
6101 std::map<String, FileRec *>::iterator iter;
6102 for (iter=oFiles.begin() ; iter!=oFiles.end() ; iter++)
6103 {
6104 FileRec *frec = iter->second;
6105 if (frec->type == FileRec::OFILE)
6106 {
6107 fprintf(f, "<object path='%s' name='%s' suffix='%s'>\n",
6108 frec->path.c_str(), frec->baseName.c_str(), frec->suffix.c_str());
6109 std::map<String, FileRec *>::iterator citer;
6110 for (citer=frec->files.begin() ; citer!=frec->files.end() ; citer++)
6111 {
6112 String cfname = citer->first;
6113 fprintf(f, " <dep name='%s'/>\n", cfname.c_str());
6114 }
6115 fprintf(f, "</object>\n\n");
6116 }
6117 }
6119 fprintf(f, "</dependencies>\n");
6120 fprintf(f, "\n");
6121 fprintf(f, "<!--\n");
6122 fprintf(f, "########################################################\n");
6123 fprintf(f, "## E N D\n");
6124 fprintf(f, "########################################################\n");
6125 fprintf(f, "-->\n");
6127 fclose(f);
6129 return true;
6130 }
6135 /**
6136 * This loads the dependency cache.
6137 */
6138 std::vector<DepRec> DepTool::loadDepFile(const String &depFile)
6139 {
6140 std::vector<DepRec> result;
6142 Parser parser;
6143 Element *root = parser.parseFile(depFile.c_str());
6144 if (!root)
6145 {
6146 //error("Could not open %s for reading", depFile.c_str());
6147 return result;
6148 }
6150 if (root->getChildren().size()==0 ||
6151 root->getChildren()[0]->getName()!="dependencies")
6152 {
6153 error("loadDepFile: main xml element should be <dependencies>");
6154 delete root;
6155 return result;
6156 }
6158 //########## Start parsing
6159 Element *depList = root->getChildren()[0];
6161 std::vector<Element *> objects = depList->getChildren();
6162 for (unsigned int i=0 ; i<objects.size() ; i++)
6163 {
6164 Element *objectElem = objects[i];
6165 String tagName = objectElem->getName();
6166 if (tagName != "object")
6167 {
6168 error("loadDepFile: <dependencies> should have only <object> children");
6169 return result;
6170 }
6172 String objName = objectElem->getAttribute("name");
6173 //trace("object:%s", objName.c_str());
6174 DepRec depObject(objName);
6175 depObject.path = objectElem->getAttribute("path");
6176 depObject.suffix = objectElem->getAttribute("suffix");
6177 //########## DESCRIPTION
6178 std::vector<Element *> depElems = objectElem->getChildren();
6179 for (unsigned int i=0 ; i<depElems.size() ; i++)
6180 {
6181 Element *depElem = depElems[i];
6182 tagName = depElem->getName();
6183 if (tagName != "dep")
6184 {
6185 error("loadDepFile: <object> should have only <dep> children");
6186 return result;
6187 }
6188 String depName = depElem->getAttribute("name");
6189 //trace(" dep:%s", depName.c_str());
6190 depObject.files.push_back(depName);
6191 }
6193 //Insert into the result list, in a sorted manner
6194 bool inserted = false;
6195 std::vector<DepRec>::iterator iter;
6196 for (iter = result.begin() ; iter != result.end() ; iter++)
6197 {
6198 String vpath = iter->path;
6199 vpath.append("/");
6200 vpath.append(iter->name);
6201 String opath = depObject.path;
6202 opath.append("/");
6203 opath.append(depObject.name);
6204 if (vpath > opath)
6205 {
6206 inserted = true;
6207 iter = result.insert(iter, depObject);
6208 break;
6209 }
6210 }
6211 if (!inserted)
6212 result.push_back(depObject);
6213 }
6215 delete root;
6217 return result;
6218 }
6221 /**
6222 * This loads the dependency cache.
6223 */
6224 std::vector<DepRec> DepTool::getDepFile(const String &depFile,
6225 bool forceRefresh)
6226 {
6227 std::vector<DepRec> result;
6228 if (forceRefresh)
6229 {
6230 generateDependencies(depFile);
6231 result = loadDepFile(depFile);
6232 }
6233 else
6234 {
6235 //try once
6236 result = loadDepFile(depFile);
6237 if (result.size() == 0)
6238 {
6239 //fail? try again
6240 generateDependencies(depFile);
6241 result = loadDepFile(depFile);
6242 }
6243 }
6244 return result;
6245 }
6250 //########################################################################
6251 //# T A S K
6252 //########################################################################
6253 //forward decl
6254 class Target;
6255 class Make;
6257 /**
6258 *
6259 */
6260 class Task : public MakeBase
6261 {
6263 public:
6265 typedef enum
6266 {
6267 TASK_NONE,
6268 TASK_CC,
6269 TASK_COPY,
6270 TASK_DELETE,
6271 TASK_ECHO,
6272 TASK_JAR,
6273 TASK_JAVAC,
6274 TASK_LINK,
6275 TASK_MAKEFILE,
6276 TASK_MKDIR,
6277 TASK_MSGFMT,
6278 TASK_PKG_CONFIG,
6279 TASK_RANLIB,
6280 TASK_RC,
6281 TASK_SHAREDLIB,
6282 TASK_STATICLIB,
6283 TASK_STRIP,
6284 TASK_TOUCH,
6285 TASK_TSTAMP
6286 } TaskType;
6289 /**
6290 *
6291 */
6292 Task(MakeBase &par) : parent(par)
6293 { init(); }
6295 /**
6296 *
6297 */
6298 Task(const Task &other) : parent(other.parent)
6299 { init(); assign(other); }
6301 /**
6302 *
6303 */
6304 Task &operator=(const Task &other)
6305 { assign(other); return *this; }
6307 /**
6308 *
6309 */
6310 virtual ~Task()
6311 { }
6314 /**
6315 *
6316 */
6317 virtual MakeBase &getParent()
6318 { return parent; }
6320 /**
6321 *
6322 */
6323 virtual int getType()
6324 { return type; }
6326 /**
6327 *
6328 */
6329 virtual void setType(int val)
6330 { type = val; }
6332 /**
6333 *
6334 */
6335 virtual String getName()
6336 { return name; }
6338 /**
6339 *
6340 */
6341 virtual bool execute()
6342 { return true; }
6344 /**
6345 *
6346 */
6347 virtual bool parse(Element *elem)
6348 { return true; }
6350 /**
6351 *
6352 */
6353 Task *createTask(Element *elem, int lineNr);
6356 protected:
6358 void init()
6359 {
6360 type = TASK_NONE;
6361 name = "none";
6362 }
6364 void assign(const Task &other)
6365 {
6366 type = other.type;
6367 name = other.name;
6368 }
6370 /**
6371 * Show task status
6372 */
6373 void taskstatus(const char *fmt, ...)
6374 {
6375 va_list args;
6376 va_start(args,fmt);
6377 fprintf(stdout, " %s : ", name.c_str());
6378 vfprintf(stdout, fmt, args);
6379 fprintf(stdout, "\n");
6380 va_end(args) ;
6381 }
6383 String getAttribute(Element *elem, const String &attrName)
6384 {
6385 String str;
6386 return str;
6387 }
6389 MakeBase &parent;
6391 int type;
6393 String name;
6394 };
6398 /**
6399 * This task runs the C/C++ compiler. The compiler is invoked
6400 * for all .c or .cpp files which are newer than their correcsponding
6401 * .o files.
6402 */
6403 class TaskCC : public Task
6404 {
6405 public:
6407 TaskCC(MakeBase &par) : Task(par)
6408 {
6409 type = TASK_CC;
6410 name = "cc";
6411 }
6413 virtual ~TaskCC()
6414 {}
6416 virtual bool isExcludedInc(const String &dirname)
6417 {
6418 for (unsigned int i=0 ; i<excludeInc.size() ; i++)
6419 {
6420 String fname = excludeInc[i];
6421 if (fname == dirname)
6422 return true;
6423 }
6424 return false;
6425 }
6427 virtual bool execute()
6428 {
6429 //evaluate our parameters
6430 String command = parent.eval(commandOpt, "gcc");
6431 String ccCommand = parent.eval(ccCommandOpt, "gcc");
6432 String cxxCommand = parent.eval(cxxCommandOpt, "g++");
6433 String source = parent.eval(sourceOpt, ".");
6434 String dest = parent.eval(destOpt, ".");
6435 String flags = parent.eval(flagsOpt, "");
6436 String defines = parent.eval(definesOpt, "");
6437 String includes = parent.eval(includesOpt, "");
6438 bool continueOnError = parent.evalBool(continueOnErrorOpt, true);
6439 bool refreshCache = parent.evalBool(refreshCacheOpt, false);
6441 if (!listFiles(parent, fileSet))
6442 return false;
6444 FILE *f = NULL;
6445 f = fopen("compile.lst", "w");
6447 //refreshCache is probably false here, unless specified otherwise
6448 String fullName = parent.resolve("build.dep");
6449 if (refreshCache || isNewerThan(parent.getURI().getPath(), fullName))
6450 {
6451 taskstatus("regenerating C/C++ dependency cache");
6452 refreshCache = true;
6453 }
6455 DepTool depTool;
6456 depTool.setSourceDirectory(source);
6457 depTool.setFileList(fileSet.getFiles());
6458 std::vector<DepRec> deps =
6459 depTool.getDepFile("build.dep", refreshCache);
6461 String incs;
6462 incs.append("-I");
6463 incs.append(parent.resolve("."));
6464 incs.append(" ");
6465 if (includes.size()>0)
6466 {
6467 incs.append(includes);
6468 incs.append(" ");
6469 }
6470 std::set<String> paths;
6471 std::vector<DepRec>::iterator viter;
6472 for (viter=deps.begin() ; viter!=deps.end() ; viter++)
6473 {
6474 DepRec dep = *viter;
6475 if (dep.path.size()>0)
6476 paths.insert(dep.path);
6477 }
6478 if (source.size()>0)
6479 {
6480 incs.append(" -I");
6481 incs.append(parent.resolve(source));
6482 incs.append(" ");
6483 }
6484 std::set<String>::iterator setIter;
6485 for (setIter=paths.begin() ; setIter!=paths.end() ; setIter++)
6486 {
6487 String dirName = *setIter;
6488 //check excludeInc to see if we dont want to include this dir
6489 if (isExcludedInc(dirName))
6490 continue;
6491 incs.append(" -I");
6492 String dname;
6493 if (source.size()>0)
6494 {
6495 dname.append(source);
6496 dname.append("/");
6497 }
6498 dname.append(dirName);
6499 incs.append(parent.resolve(dname));
6500 }
6502 /**
6503 * Compile each of the C files that need it
6504 */
6505 bool errorOccurred = false;
6506 std::vector<String> cfiles;
6507 for (viter=deps.begin() ; viter!=deps.end() ; viter++)
6508 {
6509 DepRec dep = *viter;
6511 //## Select command
6512 String sfx = dep.suffix;
6513 String command = ccCommand;
6514 if (sfx == "cpp" || sfx == "cxx" || sfx == "c++" ||
6515 sfx == "cc" || sfx == "CC")
6516 command = cxxCommand;
6518 //## Make paths
6519 String destPath = dest;
6520 String srcPath = source;
6521 if (dep.path.size()>0)
6522 {
6523 destPath.append("/");
6524 destPath.append(dep.path);
6525 srcPath.append("/");
6526 srcPath.append(dep.path);
6527 }
6528 //## Make sure destination directory exists
6529 if (!createDirectory(destPath))
6530 return false;
6532 //## Check whether it needs to be done
6533 String destName;
6534 if (destPath.size()>0)
6535 {
6536 destName.append(destPath);
6537 destName.append("/");
6538 }
6539 destName.append(dep.name);
6540 destName.append(".o");
6541 String destFullName = parent.resolve(destName);
6542 String srcName;
6543 if (srcPath.size()>0)
6544 {
6545 srcName.append(srcPath);
6546 srcName.append("/");
6547 }
6548 srcName.append(dep.name);
6549 srcName.append(".");
6550 srcName.append(dep.suffix);
6551 String srcFullName = parent.resolve(srcName);
6552 bool compileMe = false;
6553 //# First we check if the source is newer than the .o
6554 if (isNewerThan(srcFullName, destFullName))
6555 {
6556 taskstatus("compile of %s required by source: %s",
6557 destFullName.c_str(), srcFullName.c_str());
6558 compileMe = true;
6559 }
6560 else
6561 {
6562 //# secondly, we check if any of the included dependencies
6563 //# of the .c/.cpp is newer than the .o
6564 for (unsigned int i=0 ; i<dep.files.size() ; i++)
6565 {
6566 String depName;
6567 if (source.size()>0)
6568 {
6569 depName.append(source);
6570 depName.append("/");
6571 }
6572 depName.append(dep.files[i]);
6573 String depFullName = parent.resolve(depName);
6574 bool depRequires = isNewerThan(depFullName, destFullName);
6575 //trace("%d %s %s\n", depRequires,
6576 // destFullName.c_str(), depFullName.c_str());
6577 if (depRequires)
6578 {
6579 taskstatus("compile of %s required by included: %s",
6580 destFullName.c_str(), depFullName.c_str());
6581 compileMe = true;
6582 break;
6583 }
6584 }
6585 }
6586 if (!compileMe)
6587 {
6588 continue;
6589 }
6591 //## Assemble the command
6592 String cmd = command;
6593 cmd.append(" -c ");
6594 cmd.append(flags);
6595 cmd.append(" ");
6596 cmd.append(defines);
6597 cmd.append(" ");
6598 cmd.append(incs);
6599 cmd.append(" ");
6600 cmd.append(srcFullName);
6601 cmd.append(" -o ");
6602 cmd.append(destFullName);
6604 //## Execute the command
6606 String outString, errString;
6607 bool ret = executeCommand(cmd.c_str(), "", outString, errString);
6609 if (f)
6610 {
6611 fprintf(f, "########################### File : %s\n",
6612 srcFullName.c_str());
6613 fprintf(f, "#### COMMAND ###\n");
6614 int col = 0;
6615 for (unsigned int i = 0 ; i < cmd.size() ; i++)
6616 {
6617 char ch = cmd[i];
6618 if (isspace(ch) && col > 63)
6619 {
6620 fputc('\n', f);
6621 col = 0;
6622 }
6623 else
6624 {
6625 fputc(ch, f);
6626 col++;
6627 }
6628 if (col > 76)
6629 {
6630 fputc('\n', f);
6631 col = 0;
6632 }
6633 }
6634 fprintf(f, "\n");
6635 fprintf(f, "#### STDOUT ###\n%s\n", outString.c_str());
6636 fprintf(f, "#### STDERR ###\n%s\n\n", errString.c_str());
6637 fflush(f);
6638 }
6639 if (!ret)
6640 {
6641 error("problem compiling: %s", errString.c_str());
6642 errorOccurred = true;
6643 }
6644 if (errorOccurred && !continueOnError)
6645 break;
6646 }
6648 if (f)
6649 {
6650 fclose(f);
6651 }
6653 return !errorOccurred;
6654 }
6657 virtual bool parse(Element *elem)
6658 {
6659 String s;
6660 if (!parent.getAttribute(elem, "command", commandOpt))
6661 return false;
6662 if (commandOpt.size()>0)
6663 { cxxCommandOpt = ccCommandOpt = commandOpt; }
6664 if (!parent.getAttribute(elem, "cc", ccCommandOpt))
6665 return false;
6666 if (!parent.getAttribute(elem, "cxx", cxxCommandOpt))
6667 return false;
6668 if (!parent.getAttribute(elem, "destdir", destOpt))
6669 return false;
6670 if (!parent.getAttribute(elem, "continueOnError", continueOnErrorOpt))
6671 return false;
6672 if (!parent.getAttribute(elem, "refreshCache", refreshCacheOpt))
6673 return false;
6675 std::vector<Element *> children = elem->getChildren();
6676 for (unsigned int i=0 ; i<children.size() ; i++)
6677 {
6678 Element *child = children[i];
6679 String tagName = child->getName();
6680 if (tagName == "flags")
6681 {
6682 if (!parent.getValue(child, flagsOpt))
6683 return false;
6684 flagsOpt = strip(flagsOpt);
6685 }
6686 else if (tagName == "includes")
6687 {
6688 if (!parent.getValue(child, includesOpt))
6689 return false;
6690 includesOpt = strip(includesOpt);
6691 }
6692 else if (tagName == "defines")
6693 {
6694 if (!parent.getValue(child, definesOpt))
6695 return false;
6696 definesOpt = strip(definesOpt);
6697 }
6698 else if (tagName == "fileset")
6699 {
6700 if (!parseFileSet(child, parent, fileSet))
6701 return false;
6702 sourceOpt = fileSet.getDirectory();
6703 }
6704 else if (tagName == "excludeinc")
6705 {
6706 if (!parseFileList(child, parent, excludeInc))
6707 return false;
6708 }
6709 }
6711 return true;
6712 }
6714 protected:
6716 String commandOpt;
6717 String ccCommandOpt;
6718 String cxxCommandOpt;
6719 String sourceOpt;
6720 String destOpt;
6721 String flagsOpt;
6722 String definesOpt;
6723 String includesOpt;
6724 String continueOnErrorOpt;
6725 String refreshCacheOpt;
6726 FileSet fileSet;
6727 FileList excludeInc;
6729 };
6733 /**
6734 *
6735 */
6736 class TaskCopy : public Task
6737 {
6738 public:
6740 typedef enum
6741 {
6742 CP_NONE,
6743 CP_TOFILE,
6744 CP_TODIR
6745 } CopyType;
6747 TaskCopy(MakeBase &par) : Task(par)
6748 {
6749 type = TASK_COPY;
6750 name = "copy";
6751 cptype = CP_NONE;
6752 haveFileSet = false;
6753 }
6755 virtual ~TaskCopy()
6756 {}
6758 virtual bool execute()
6759 {
6760 String fileName = parent.eval(fileNameOpt , ".");
6761 String toFileName = parent.eval(toFileNameOpt , ".");
6762 String toDirName = parent.eval(toDirNameOpt , ".");
6763 bool verbose = parent.evalBool(verboseOpt, false);
6764 switch (cptype)
6765 {
6766 case CP_TOFILE:
6767 {
6768 if (fileName.size()>0)
6769 {
6770 taskstatus("%s to %s",
6771 fileName.c_str(), toFileName.c_str());
6772 String fullSource = parent.resolve(fileName);
6773 String fullDest = parent.resolve(toFileName);
6774 if (verbose)
6775 taskstatus("copy %s to file %s", fullSource.c_str(),
6776 fullDest.c_str());
6777 if (!isRegularFile(fullSource))
6778 {
6779 error("copy : file %s does not exist", fullSource.c_str());
6780 return false;
6781 }
6782 if (!isNewerThan(fullSource, fullDest))
6783 {
6784 taskstatus("skipped");
6785 return true;
6786 }
6787 if (!copyFile(fullSource, fullDest))
6788 return false;
6789 taskstatus("1 file copied");
6790 }
6791 return true;
6792 }
6793 case CP_TODIR:
6794 {
6795 if (haveFileSet)
6796 {
6797 if (!listFiles(parent, fileSet))
6798 return false;
6799 String fileSetDir = parent.eval(fileSet.getDirectory(), ".");
6801 taskstatus("%s to %s",
6802 fileSetDir.c_str(), toDirName.c_str());
6804 int nrFiles = 0;
6805 for (unsigned int i=0 ; i<fileSet.size() ; i++)
6806 {
6807 String fileName = fileSet[i];
6809 String sourcePath;
6810 if (fileSetDir.size()>0)
6811 {
6812 sourcePath.append(fileSetDir);
6813 sourcePath.append("/");
6814 }
6815 sourcePath.append(fileName);
6816 String fullSource = parent.resolve(sourcePath);
6818 //Get the immediate parent directory's base name
6819 String baseFileSetDir = fileSetDir;
6820 unsigned int pos = baseFileSetDir.find_last_of('/');
6821 if (pos!=baseFileSetDir.npos &&
6822 pos < baseFileSetDir.size()-1)
6823 baseFileSetDir =
6824 baseFileSetDir.substr(pos+1,
6825 baseFileSetDir.size());
6826 //Now make the new path
6827 String destPath;
6828 if (toDirName.size()>0)
6829 {
6830 destPath.append(toDirName);
6831 destPath.append("/");
6832 }
6833 if (baseFileSetDir.size()>0)
6834 {
6835 destPath.append(baseFileSetDir);
6836 destPath.append("/");
6837 }
6838 destPath.append(fileName);
6839 String fullDest = parent.resolve(destPath);
6840 //trace("fileName:%s", fileName.c_str());
6841 if (verbose)
6842 taskstatus("copy %s to new dir : %s",
6843 fullSource.c_str(), fullDest.c_str());
6844 if (!isNewerThan(fullSource, fullDest))
6845 {
6846 if (verbose)
6847 taskstatus("copy skipping %s", fullSource.c_str());
6848 continue;
6849 }
6850 if (!copyFile(fullSource, fullDest))
6851 return false;
6852 nrFiles++;
6853 }
6854 taskstatus("%d file(s) copied", nrFiles);
6855 }
6856 else //file source
6857 {
6858 //For file->dir we want only the basename of
6859 //the source appended to the dest dir
6860 taskstatus("%s to %s",
6861 fileName.c_str(), toDirName.c_str());
6862 String baseName = fileName;
6863 unsigned int pos = baseName.find_last_of('/');
6864 if (pos!=baseName.npos && pos<baseName.size()-1)
6865 baseName = baseName.substr(pos+1, baseName.size());
6866 String fullSource = parent.resolve(fileName);
6867 String destPath;
6868 if (toDirName.size()>0)
6869 {
6870 destPath.append(toDirName);
6871 destPath.append("/");
6872 }
6873 destPath.append(baseName);
6874 String fullDest = parent.resolve(destPath);
6875 if (verbose)
6876 taskstatus("file %s to new dir : %s", fullSource.c_str(),
6877 fullDest.c_str());
6878 if (!isRegularFile(fullSource))
6879 {
6880 error("copy : file %s does not exist", fullSource.c_str());
6881 return false;
6882 }
6883 if (!isNewerThan(fullSource, fullDest))
6884 {
6885 taskstatus("skipped");
6886 return true;
6887 }
6888 if (!copyFile(fullSource, fullDest))
6889 return false;
6890 taskstatus("1 file copied");
6891 }
6892 return true;
6893 }
6894 }
6895 return true;
6896 }
6899 virtual bool parse(Element *elem)
6900 {
6901 if (!parent.getAttribute(elem, "file", fileNameOpt))
6902 return false;
6903 if (!parent.getAttribute(elem, "tofile", toFileNameOpt))
6904 return false;
6905 if (toFileNameOpt.size() > 0)
6906 cptype = CP_TOFILE;
6907 if (!parent.getAttribute(elem, "todir", toDirNameOpt))
6908 return false;
6909 if (toDirNameOpt.size() > 0)
6910 cptype = CP_TODIR;
6911 if (!parent.getAttribute(elem, "verbose", verboseOpt))
6912 return false;
6914 haveFileSet = false;
6916 std::vector<Element *> children = elem->getChildren();
6917 for (unsigned int i=0 ; i<children.size() ; i++)
6918 {
6919 Element *child = children[i];
6920 String tagName = child->getName();
6921 if (tagName == "fileset")
6922 {
6923 if (!parseFileSet(child, parent, fileSet))
6924 {
6925 error("problem getting fileset");
6926 return false;
6927 }
6928 haveFileSet = true;
6929 }
6930 }
6932 //Perform validity checks
6933 if (fileNameOpt.size()>0 && fileSet.size()>0)
6934 {
6935 error("<copy> can only have one of : file= and <fileset>");
6936 return false;
6937 }
6938 if (toFileNameOpt.size()>0 && toDirNameOpt.size()>0)
6939 {
6940 error("<copy> can only have one of : tofile= or todir=");
6941 return false;
6942 }
6943 if (haveFileSet && toDirNameOpt.size()==0)
6944 {
6945 error("a <copy> task with a <fileset> must have : todir=");
6946 return false;
6947 }
6948 if (cptype == CP_TOFILE && fileNameOpt.size()==0)
6949 {
6950 error("<copy> tofile= must be associated with : file=");
6951 return false;
6952 }
6953 if (cptype == CP_TODIR && fileNameOpt.size()==0 && !haveFileSet)
6954 {
6955 error("<copy> todir= must be associated with : file= or <fileset>");
6956 return false;
6957 }
6959 return true;
6960 }
6962 private:
6964 int cptype;
6965 bool haveFileSet;
6967 FileSet fileSet;
6968 String fileNameOpt;
6969 String toFileNameOpt;
6970 String toDirNameOpt;
6971 String verboseOpt;
6972 };
6975 /**
6976 *
6977 */
6978 class TaskDelete : public Task
6979 {
6980 public:
6982 typedef enum
6983 {
6984 DEL_FILE,
6985 DEL_DIR,
6986 DEL_FILESET
6987 } DeleteType;
6989 TaskDelete(MakeBase &par) : Task(par)
6990 {
6991 type = TASK_DELETE;
6992 name = "delete";
6993 delType = DEL_FILE;
6994 }
6996 virtual ~TaskDelete()
6997 {}
6999 virtual bool execute()
7000 {
7001 String dirName = parent.eval(dirNameOpt, ".");
7002 String fileName = parent.eval(fileNameOpt, ".");
7003 bool verbose = parent.evalBool(verboseOpt, false);
7004 bool quiet = parent.evalBool(quietOpt, false);
7005 bool failOnError = parent.evalBool(failOnErrorOpt, true);
7006 struct stat finfo;
7007 switch (delType)
7008 {
7009 case DEL_FILE:
7010 {
7011 taskstatus("file: %s", fileName.c_str());
7012 String fullName = parent.resolve(fileName);
7013 char *fname = (char *)fullName.c_str();
7014 if (!quiet && verbose)
7015 taskstatus("path: %s", fname);
7016 //does not exist
7017 if (stat(fname, &finfo)<0)
7018 {
7019 if (failOnError)
7020 return false;
7021 else
7022 return true;
7023 }
7024 //exists but is not a regular file
7025 if (!S_ISREG(finfo.st_mode))
7026 {
7027 error("<delete> failed. '%s' exists and is not a regular file",
7028 fname);
7029 return false;
7030 }
7031 if (remove(fname)<0)
7032 {
7033 error("<delete> failed: %s", strerror(errno));
7034 return false;
7035 }
7036 return true;
7037 }
7038 case DEL_DIR:
7039 {
7040 taskstatus("dir: %s", dirName.c_str());
7041 String fullDir = parent.resolve(dirName);
7042 if (!quiet && verbose)
7043 taskstatus("path: %s", fullDir.c_str());
7044 if (!removeDirectory(fullDir))
7045 return false;
7046 return true;
7047 }
7048 }
7049 return true;
7050 }
7052 virtual bool parse(Element *elem)
7053 {
7054 if (!parent.getAttribute(elem, "file", fileNameOpt))
7055 return false;
7056 if (fileNameOpt.size() > 0)
7057 delType = DEL_FILE;
7058 if (!parent.getAttribute(elem, "dir", dirNameOpt))
7059 return false;
7060 if (dirNameOpt.size() > 0)
7061 delType = DEL_DIR;
7062 if (fileNameOpt.size()>0 && dirNameOpt.size()>0)
7063 {
7064 error("<delete> can have one attribute of file= or dir=");
7065 return false;
7066 }
7067 if (fileNameOpt.size()==0 && dirNameOpt.size()==0)
7068 {
7069 error("<delete> must have one attribute of file= or dir=");
7070 return false;
7071 }
7072 if (!parent.getAttribute(elem, "verbose", verboseOpt))
7073 return false;
7074 if (!parent.getAttribute(elem, "quiet", quietOpt))
7075 return false;
7076 if (!parent.getAttribute(elem, "failonerror", failOnErrorOpt))
7077 return false;
7078 return true;
7079 }
7081 private:
7083 int delType;
7084 String dirNameOpt;
7085 String fileNameOpt;
7086 String verboseOpt;
7087 String quietOpt;
7088 String failOnErrorOpt;
7089 };
7092 /**
7093 * Send a message to stdout
7094 */
7095 class TaskEcho : public Task
7096 {
7097 public:
7099 TaskEcho(MakeBase &par) : Task(par)
7100 { type = TASK_ECHO; name = "echo"; }
7102 virtual ~TaskEcho()
7103 {}
7105 virtual bool execute()
7106 {
7107 //let message have priority over text
7108 String message = parent.eval(messageOpt, "");
7109 String text = parent.eval(textOpt, "");
7110 if (message.size() > 0)
7111 {
7112 fprintf(stdout, "%s\n", message.c_str());
7113 }
7114 else if (text.size() > 0)
7115 {
7116 fprintf(stdout, "%s\n", text.c_str());
7117 }
7118 return true;
7119 }
7121 virtual bool parse(Element *elem)
7122 {
7123 if (!parent.getValue(elem, textOpt))
7124 return false;
7125 textOpt = leftJustify(textOpt);
7126 if (!parent.getAttribute(elem, "message", messageOpt))
7127 return false;
7128 return true;
7129 }
7131 private:
7133 String messageOpt;
7134 String textOpt;
7135 };
7139 /**
7140 *
7141 */
7142 class TaskJar : public Task
7143 {
7144 public:
7146 TaskJar(MakeBase &par) : Task(par)
7147 { type = TASK_JAR; name = "jar"; }
7149 virtual ~TaskJar()
7150 {}
7152 virtual bool execute()
7153 {
7154 String command = parent.eval(commandOpt, "jar");
7155 String basedir = parent.eval(basedirOpt, ".");
7156 String destfile = parent.eval(destfileOpt, ".");
7158 String cmd = command;
7159 cmd.append(" -cf ");
7160 cmd.append(destfile);
7161 cmd.append(" -C ");
7162 cmd.append(basedir);
7163 cmd.append(" .");
7165 String execCmd = cmd;
7167 String outString, errString;
7168 bool ret = executeCommand(execCmd.c_str(), "", outString, errString);
7169 if (!ret)
7170 {
7171 error("<jar> command '%s' failed :\n %s",
7172 execCmd.c_str(), errString.c_str());
7173 return false;
7174 }
7175 return true;
7176 }
7178 virtual bool parse(Element *elem)
7179 {
7180 if (!parent.getAttribute(elem, "command", commandOpt))
7181 return false;
7182 if (!parent.getAttribute(elem, "basedir", basedirOpt))
7183 return false;
7184 if (!parent.getAttribute(elem, "destfile", destfileOpt))
7185 return false;
7186 if (basedirOpt.size() == 0 || destfileOpt.size() == 0)
7187 {
7188 error("<jar> required both basedir and destfile attributes to be set");
7189 return false;
7190 }
7191 return true;
7192 }
7194 private:
7196 String commandOpt;
7197 String basedirOpt;
7198 String destfileOpt;
7199 };
7202 /**
7203 *
7204 */
7205 class TaskJavac : public Task
7206 {
7207 public:
7209 TaskJavac(MakeBase &par) : Task(par)
7210 {
7211 type = TASK_JAVAC; name = "javac";
7212 }
7214 virtual ~TaskJavac()
7215 {}
7217 virtual bool execute()
7218 {
7219 String command = parent.eval(commandOpt, "javac");
7220 String srcdir = parent.eval(srcdirOpt, ".");
7221 String destdir = parent.eval(destdirOpt, ".");
7222 String target = parent.eval(targetOpt, "");
7224 std::vector<String> fileList;
7225 if (!listFiles(srcdir, "", fileList))
7226 {
7227 return false;
7228 }
7229 String cmd = command;
7230 cmd.append(" -d ");
7231 cmd.append(destdir);
7232 cmd.append(" -classpath ");
7233 cmd.append(destdir);
7234 cmd.append(" -sourcepath ");
7235 cmd.append(srcdir);
7236 cmd.append(" ");
7237 if (target.size()>0)
7238 {
7239 cmd.append(" -target ");
7240 cmd.append(target);
7241 cmd.append(" ");
7242 }
7243 String fname = "javalist.btool";
7244 FILE *f = fopen(fname.c_str(), "w");
7245 int count = 0;
7246 for (unsigned int i=0 ; i<fileList.size() ; i++)
7247 {
7248 String fname = fileList[i];
7249 String srcName = fname;
7250 if (fname.size()<6) //x.java
7251 continue;
7252 if (fname.compare(fname.size()-5, 5, ".java") != 0)
7253 continue;
7254 String baseName = fname.substr(0, fname.size()-5);
7255 String destName = baseName;
7256 destName.append(".class");
7258 String fullSrc = srcdir;
7259 fullSrc.append("/");
7260 fullSrc.append(fname);
7261 String fullDest = destdir;
7262 fullDest.append("/");
7263 fullDest.append(destName);
7264 //trace("fullsrc:%s fulldest:%s", fullSrc.c_str(), fullDest.c_str());
7265 if (!isNewerThan(fullSrc, fullDest))
7266 continue;
7268 count++;
7269 fprintf(f, "%s\n", fullSrc.c_str());
7270 }
7271 fclose(f);
7272 if (!count)
7273 {
7274 taskstatus("nothing to do");
7275 return true;
7276 }
7278 taskstatus("compiling %d files", count);
7280 String execCmd = cmd;
7281 execCmd.append("@");
7282 execCmd.append(fname);
7284 String outString, errString;
7285 bool ret = executeCommand(execCmd.c_str(), "", outString, errString);
7286 if (!ret)
7287 {
7288 error("<javac> command '%s' failed :\n %s",
7289 execCmd.c_str(), errString.c_str());
7290 return false;
7291 }
7292 return true;
7293 }
7295 virtual bool parse(Element *elem)
7296 {
7297 if (!parent.getAttribute(elem, "command", commandOpt))
7298 return false;
7299 if (!parent.getAttribute(elem, "srcdir", srcdirOpt))
7300 return false;
7301 if (!parent.getAttribute(elem, "destdir", destdirOpt))
7302 return false;
7303 if (srcdirOpt.size() == 0 || destdirOpt.size() == 0)
7304 {
7305 error("<javac> required both srcdir and destdir attributes to be set");
7306 return false;
7307 }
7308 if (!parent.getAttribute(elem, "target", targetOpt))
7309 return false;
7310 return true;
7311 }
7313 private:
7315 String commandOpt;
7316 String srcdirOpt;
7317 String destdirOpt;
7318 String targetOpt;
7320 };
7323 /**
7324 *
7325 */
7326 class TaskLink : public Task
7327 {
7328 public:
7330 TaskLink(MakeBase &par) : Task(par)
7331 {
7332 type = TASK_LINK; name = "link";
7333 }
7335 virtual ~TaskLink()
7336 {}
7338 virtual bool execute()
7339 {
7340 String command = parent.eval(commandOpt, "g++");
7341 String fileName = parent.eval(fileNameOpt, "");
7342 String flags = parent.eval(flagsOpt, "");
7343 String libs = parent.eval(libsOpt, "");
7344 bool doStrip = parent.evalBool(doStripOpt, false);
7345 String symFileName = parent.eval(symFileNameOpt, "");
7346 String stripCommand = parent.eval(stripCommandOpt, "strip");
7347 String objcopyCommand = parent.eval(objcopyCommandOpt, "objcopy");
7349 if (!listFiles(parent, fileSet))
7350 return false;
7351 String fileSetDir = parent.eval(fileSet.getDirectory(), ".");
7352 //trace("%d files in %s", fileSet.size(), fileSetDir.c_str());
7353 bool doit = false;
7354 String fullTarget = parent.resolve(fileName);
7355 String cmd = command;
7356 cmd.append(" -o ");
7357 cmd.append(fullTarget);
7358 cmd.append(" ");
7359 cmd.append(flags);
7360 for (unsigned int i=0 ; i<fileSet.size() ; i++)
7361 {
7362 cmd.append(" ");
7363 String obj;
7364 if (fileSetDir.size()>0)
7365 {
7366 obj.append(fileSetDir);
7367 obj.append("/");
7368 }
7369 obj.append(fileSet[i]);
7370 String fullObj = parent.resolve(obj);
7371 String nativeFullObj = getNativePath(fullObj);
7372 cmd.append(nativeFullObj);
7373 //trace("link: tgt:%s obj:%s", fullTarget.c_str(),
7374 // fullObj.c_str());
7375 if (isNewerThan(fullObj, fullTarget))
7376 doit = true;
7377 }
7378 cmd.append(" ");
7379 cmd.append(libs);
7380 if (!doit)
7381 {
7382 //trace("link not needed");
7383 return true;
7384 }
7385 //trace("LINK cmd:%s", cmd.c_str());
7388 String outbuf, errbuf;
7389 if (!executeCommand(cmd.c_str(), "", outbuf, errbuf))
7390 {
7391 error("LINK problem: %s", errbuf.c_str());
7392 return false;
7393 }
7395 if (symFileName.size()>0)
7396 {
7397 String symFullName = parent.resolve(symFileName);
7398 cmd = objcopyCommand;
7399 cmd.append(" --only-keep-debug ");
7400 cmd.append(getNativePath(fullTarget));
7401 cmd.append(" ");
7402 cmd.append(getNativePath(symFullName));
7403 if (!executeCommand(cmd, "", outbuf, errbuf))
7404 {
7405 error("<strip> symbol file failed : %s", errbuf.c_str());
7406 return false;
7407 }
7408 }
7410 if (doStrip)
7411 {
7412 cmd = stripCommand;
7413 cmd.append(" ");
7414 cmd.append(getNativePath(fullTarget));
7415 if (!executeCommand(cmd, "", outbuf, errbuf))
7416 {
7417 error("<strip> failed : %s", errbuf.c_str());
7418 return false;
7419 }
7420 }
7422 return true;
7423 }
7425 virtual bool parse(Element *elem)
7426 {
7427 if (!parent.getAttribute(elem, "command", commandOpt))
7428 return false;
7429 if (!parent.getAttribute(elem, "objcopycommand", objcopyCommandOpt))
7430 return false;
7431 if (!parent.getAttribute(elem, "stripcommand", stripCommandOpt))
7432 return false;
7433 if (!parent.getAttribute(elem, "out", fileNameOpt))
7434 return false;
7435 if (!parent.getAttribute(elem, "strip", doStripOpt))
7436 return false;
7437 if (!parent.getAttribute(elem, "symfile", symFileNameOpt))
7438 return false;
7440 std::vector<Element *> children = elem->getChildren();
7441 for (unsigned int i=0 ; i<children.size() ; i++)
7442 {
7443 Element *child = children[i];
7444 String tagName = child->getName();
7445 if (tagName == "fileset")
7446 {
7447 if (!parseFileSet(child, parent, fileSet))
7448 return false;
7449 }
7450 else if (tagName == "flags")
7451 {
7452 if (!parent.getValue(child, flagsOpt))
7453 return false;
7454 flagsOpt = strip(flagsOpt);
7455 }
7456 else if (tagName == "libs")
7457 {
7458 if (!parent.getValue(child, libsOpt))
7459 return false;
7460 libsOpt = strip(libsOpt);
7461 }
7462 }
7463 return true;
7464 }
7466 private:
7468 FileSet fileSet;
7470 String commandOpt;
7471 String fileNameOpt;
7472 String flagsOpt;
7473 String libsOpt;
7474 String doStripOpt;
7475 String symFileNameOpt;
7476 String stripCommandOpt;
7477 String objcopyCommandOpt;
7479 };
7483 /**
7484 * Create a named file
7485 */
7486 class TaskMakeFile : public Task
7487 {
7488 public:
7490 TaskMakeFile(MakeBase &par) : Task(par)
7491 { type = TASK_MAKEFILE; name = "makefile"; }
7493 virtual ~TaskMakeFile()
7494 {}
7496 virtual bool execute()
7497 {
7498 String fileName = parent.eval(fileNameOpt, "");
7499 String text = parent.eval(textOpt, "");
7501 taskstatus("%s", fileName.c_str());
7502 String fullName = parent.resolve(fileName);
7503 if (!isNewerThan(parent.getURI().getPath(), fullName))
7504 {
7505 //trace("skipped <makefile>");
7506 return true;
7507 }
7508 String fullNative = getNativePath(fullName);
7509 //trace("fullName:%s", fullName.c_str());
7510 FILE *f = fopen(fullNative.c_str(), "w");
7511 if (!f)
7512 {
7513 error("<makefile> could not open %s for writing : %s",
7514 fullName.c_str(), strerror(errno));
7515 return false;
7516 }
7517 for (unsigned int i=0 ; i<text.size() ; i++)
7518 fputc(text[i], f);
7519 fputc('\n', f);
7520 fclose(f);
7521 return true;
7522 }
7524 virtual bool parse(Element *elem)
7525 {
7526 if (!parent.getAttribute(elem, "file", fileNameOpt))
7527 return false;
7528 if (fileNameOpt.size() == 0)
7529 {
7530 error("<makefile> requires 'file=\"filename\"' attribute");
7531 return false;
7532 }
7533 if (!parent.getValue(elem, textOpt))
7534 return false;
7535 textOpt = leftJustify(textOpt);
7536 //trace("dirname:%s", dirName.c_str());
7537 return true;
7538 }
7540 private:
7542 String fileNameOpt;
7543 String textOpt;
7544 };
7548 /**
7549 * Create a named directory
7550 */
7551 class TaskMkDir : public Task
7552 {
7553 public:
7555 TaskMkDir(MakeBase &par) : Task(par)
7556 { type = TASK_MKDIR; name = "mkdir"; }
7558 virtual ~TaskMkDir()
7559 {}
7561 virtual bool execute()
7562 {
7563 String dirName = parent.eval(dirNameOpt, ".");
7565 taskstatus("%s", dirName.c_str());
7566 String fullDir = parent.resolve(dirName);
7567 //trace("fullDir:%s", fullDir.c_str());
7568 if (!createDirectory(fullDir))
7569 return false;
7570 return true;
7571 }
7573 virtual bool parse(Element *elem)
7574 {
7575 if (!parent.getAttribute(elem, "dir", dirNameOpt))
7576 return false;
7577 if (dirNameOpt.size() == 0)
7578 {
7579 error("<mkdir> requires 'dir=\"dirname\"' attribute");
7580 return false;
7581 }
7582 return true;
7583 }
7585 private:
7587 String dirNameOpt;
7588 };
7592 /**
7593 * Create a named directory
7594 */
7595 class TaskMsgFmt: public Task
7596 {
7597 public:
7599 TaskMsgFmt(MakeBase &par) : Task(par)
7600 { type = TASK_MSGFMT; name = "msgfmt"; }
7602 virtual ~TaskMsgFmt()
7603 {}
7605 virtual bool execute()
7606 {
7607 String command = parent.eval(commandOpt, "msgfmt");
7608 String toDirName = parent.eval(toDirNameOpt, ".");
7609 String outName = parent.eval(outNameOpt, "");
7610 bool owndir = parent.evalBool(owndirOpt, false);
7612 if (!listFiles(parent, fileSet))
7613 return false;
7614 String fileSetDir = fileSet.getDirectory();
7616 //trace("msgfmt: %d", fileSet.size());
7617 for (unsigned int i=0 ; i<fileSet.size() ; i++)
7618 {
7619 String fileName = fileSet[i];
7620 if (getSuffix(fileName) != "po")
7621 continue;
7622 String sourcePath;
7623 if (fileSetDir.size()>0)
7624 {
7625 sourcePath.append(fileSetDir);
7626 sourcePath.append("/");
7627 }
7628 sourcePath.append(fileName);
7629 String fullSource = parent.resolve(sourcePath);
7631 String destPath;
7632 if (toDirName.size()>0)
7633 {
7634 destPath.append(toDirName);
7635 destPath.append("/");
7636 }
7637 if (owndir)
7638 {
7639 String subdir = fileName;
7640 unsigned int pos = subdir.find_last_of('.');
7641 if (pos != subdir.npos)
7642 subdir = subdir.substr(0, pos);
7643 destPath.append(subdir);
7644 destPath.append("/");
7645 }
7646 //Pick the output file name
7647 if (outName.size() > 0)
7648 {
7649 destPath.append(outName);
7650 }
7651 else
7652 {
7653 destPath.append(fileName);
7654 destPath[destPath.size()-2] = 'm';
7655 }
7657 String fullDest = parent.resolve(destPath);
7659 if (!isNewerThan(fullSource, fullDest))
7660 {
7661 //trace("skip %s", fullSource.c_str());
7662 continue;
7663 }
7665 String cmd = command;
7666 cmd.append(" ");
7667 cmd.append(fullSource);
7668 cmd.append(" -o ");
7669 cmd.append(fullDest);
7671 int pos = fullDest.find_last_of('/');
7672 if (pos>0)
7673 {
7674 String fullDestPath = fullDest.substr(0, pos);
7675 if (!createDirectory(fullDestPath))
7676 return false;
7677 }
7681 String outString, errString;
7682 if (!executeCommand(cmd.c_str(), "", outString, errString))
7683 {
7684 error("<msgfmt> problem: %s", errString.c_str());
7685 return false;
7686 }
7687 }
7689 return true;
7690 }
7692 virtual bool parse(Element *elem)
7693 {
7694 if (!parent.getAttribute(elem, "command", commandOpt))
7695 return false;
7696 if (!parent.getAttribute(elem, "todir", toDirNameOpt))
7697 return false;
7698 if (!parent.getAttribute(elem, "out", outNameOpt))
7699 return false;
7700 if (!parent.getAttribute(elem, "owndir", owndirOpt))
7701 return false;
7703 std::vector<Element *> children = elem->getChildren();
7704 for (unsigned int i=0 ; i<children.size() ; i++)
7705 {
7706 Element *child = children[i];
7707 String tagName = child->getName();
7708 if (tagName == "fileset")
7709 {
7710 if (!parseFileSet(child, parent, fileSet))
7711 return false;
7712 }
7713 }
7714 return true;
7715 }
7717 private:
7719 FileSet fileSet;
7721 String commandOpt;
7722 String toDirNameOpt;
7723 String outNameOpt;
7724 String owndirOpt;
7726 };
7730 /**
7731 * Perform a Package-Config query similar to pkg-config
7732 */
7733 class TaskPkgConfig : public Task
7734 {
7735 public:
7737 typedef enum
7738 {
7739 PKG_CONFIG_QUERY_CFLAGS,
7740 PKG_CONFIG_QUERY_LIBS,
7741 PKG_CONFIG_QUERY_ALL
7742 } QueryTypes;
7744 TaskPkgConfig(MakeBase &par) : Task(par)
7745 {
7746 type = TASK_PKG_CONFIG;
7747 name = "pkg-config";
7748 }
7750 virtual ~TaskPkgConfig()
7751 {}
7753 virtual bool execute()
7754 {
7755 String pkgName = parent.eval(pkgNameOpt, "");
7756 String prefix = parent.eval(prefixOpt, "");
7757 String propName = parent.eval(propNameOpt, "");
7758 String pkgConfigPath = parent.eval(pkgConfigPathOpt,"");
7759 String query = parent.eval(queryOpt, "all");
7761 String path = parent.resolve(pkgConfigPath);
7762 PkgConfig pkgconfig;
7763 pkgconfig.setPath(path);
7764 pkgconfig.setPrefix(prefix);
7765 if (!pkgconfig.query(pkgName))
7766 {
7767 error("<pkg-config> query failed for '%s", name.c_str());
7768 return false;
7769 }
7771 String val = "";
7772 if (query == "cflags")
7773 val = pkgconfig.getCflags();
7774 else if (query == "libs")
7775 val =pkgconfig.getLibs();
7776 else if (query == "all")
7777 val = pkgconfig.getAll();
7778 else
7779 {
7780 error("<pkg-config> unhandled query : %s", query.c_str());
7781 return false;
7782 }
7783 taskstatus("property %s = '%s'", propName.c_str(), val.c_str());
7784 parent.setProperty(propName, val);
7785 return true;
7786 }
7788 virtual bool parse(Element *elem)
7789 {
7790 //# NAME
7791 if (!parent.getAttribute(elem, "name", pkgNameOpt))
7792 return false;
7793 if (pkgNameOpt.size()==0)
7794 {
7795 error("<pkg-config> requires 'name=\"package\"' attribute");
7796 return false;
7797 }
7799 //# PROPERTY
7800 if (!parent.getAttribute(elem, "property", propNameOpt))
7801 return false;
7802 if (propNameOpt.size()==0)
7803 {
7804 error("<pkg-config> requires 'property=\"name\"' attribute");
7805 return false;
7806 }
7807 //# PATH
7808 if (!parent.getAttribute(elem, "path", pkgConfigPathOpt))
7809 return false;
7810 //# PREFIX
7811 if (!parent.getAttribute(elem, "prefix", prefixOpt))
7812 return false;
7813 //# QUERY
7814 if (!parent.getAttribute(elem, "query", queryOpt))
7815 return false;
7817 return true;
7818 }
7820 private:
7822 String queryOpt;
7823 String pkgNameOpt;
7824 String prefixOpt;
7825 String propNameOpt;
7826 String pkgConfigPathOpt;
7828 };
7835 /**
7836 * Process an archive to allow random access
7837 */
7838 class TaskRanlib : public Task
7839 {
7840 public:
7842 TaskRanlib(MakeBase &par) : Task(par)
7843 { type = TASK_RANLIB; name = "ranlib"; }
7845 virtual ~TaskRanlib()
7846 {}
7848 virtual bool execute()
7849 {
7850 String fileName = parent.eval(fileNameOpt, "");
7851 String command = parent.eval(commandOpt, "ranlib");
7853 String fullName = parent.resolve(fileName);
7854 //trace("fullDir:%s", fullDir.c_str());
7855 String cmd = command;
7856 cmd.append(" ");
7857 cmd.append(fullName);
7858 String outbuf, errbuf;
7859 if (!executeCommand(cmd, "", outbuf, errbuf))
7860 return false;
7861 return true;
7862 }
7864 virtual bool parse(Element *elem)
7865 {
7866 if (!parent.getAttribute(elem, "command", commandOpt))
7867 return false;
7868 if (!parent.getAttribute(elem, "file", fileNameOpt))
7869 return false;
7870 if (fileNameOpt.size() == 0)
7871 {
7872 error("<ranlib> requires 'file=\"fileNname\"' attribute");
7873 return false;
7874 }
7875 return true;
7876 }
7878 private:
7880 String fileNameOpt;
7881 String commandOpt;
7882 };
7886 /**
7887 * Compile a resource file into a binary object
7888 */
7889 class TaskRC : public Task
7890 {
7891 public:
7893 TaskRC(MakeBase &par) : Task(par)
7894 { type = TASK_RC; name = "rc"; }
7896 virtual ~TaskRC()
7897 {}
7899 virtual bool execute()
7900 {
7901 String command = parent.eval(commandOpt, "windres");
7902 String flags = parent.eval(flagsOpt, "");
7903 String fileName = parent.eval(fileNameOpt, "");
7904 String outName = parent.eval(outNameOpt, "");
7906 String fullFile = parent.resolve(fileName);
7907 String fullOut = parent.resolve(outName);
7908 if (!isNewerThan(fullFile, fullOut))
7909 return true;
7910 String cmd = command;
7911 cmd.append(" -o ");
7912 cmd.append(fullOut);
7913 cmd.append(" ");
7914 cmd.append(flags);
7915 cmd.append(" ");
7916 cmd.append(fullFile);
7918 String outString, errString;
7919 if (!executeCommand(cmd.c_str(), "", outString, errString))
7920 {
7921 error("RC problem: %s", errString.c_str());
7922 return false;
7923 }
7924 return true;
7925 }
7927 virtual bool parse(Element *elem)
7928 {
7929 if (!parent.getAttribute(elem, "command", commandOpt))
7930 return false;
7931 if (!parent.getAttribute(elem, "file", fileNameOpt))
7932 return false;
7933 if (!parent.getAttribute(elem, "out", outNameOpt))
7934 return false;
7935 std::vector<Element *> children = elem->getChildren();
7936 for (unsigned int i=0 ; i<children.size() ; i++)
7937 {
7938 Element *child = children[i];
7939 String tagName = child->getName();
7940 if (tagName == "flags")
7941 {
7942 if (!parent.getValue(child, flagsOpt))
7943 return false;
7944 }
7945 }
7946 return true;
7947 }
7949 private:
7951 String commandOpt;
7952 String flagsOpt;
7953 String fileNameOpt;
7954 String outNameOpt;
7956 };
7960 /**
7961 * Collect .o's into a .so or DLL
7962 */
7963 class TaskSharedLib : public Task
7964 {
7965 public:
7967 TaskSharedLib(MakeBase &par) : Task(par)
7968 { type = TASK_SHAREDLIB; name = "dll"; }
7970 virtual ~TaskSharedLib()
7971 {}
7973 virtual bool execute()
7974 {
7975 String command = parent.eval(commandOpt, "dllwrap");
7976 String fileName = parent.eval(fileNameOpt, "");
7977 String defFileName = parent.eval(defFileNameOpt, "");
7978 String impFileName = parent.eval(impFileNameOpt, "");
7979 String libs = parent.eval(libsOpt, "");
7981 //trace("###########HERE %d", fileSet.size());
7982 bool doit = false;
7984 String fullOut = parent.resolve(fileName);
7985 //trace("ar fullout: %s", fullOut.c_str());
7987 if (!listFiles(parent, fileSet))
7988 return false;
7989 String fileSetDir = parent.eval(fileSet.getDirectory(), ".");
7991 for (unsigned int i=0 ; i<fileSet.size() ; i++)
7992 {
7993 String fname;
7994 if (fileSetDir.size()>0)
7995 {
7996 fname.append(fileSetDir);
7997 fname.append("/");
7998 }
7999 fname.append(fileSet[i]);
8000 String fullName = parent.resolve(fname);
8001 //trace("ar : %s/%s", fullOut.c_str(), fullName.c_str());
8002 if (isNewerThan(fullName, fullOut))
8003 doit = true;
8004 }
8005 //trace("Needs it:%d", doit);
8006 if (!doit)
8007 {
8008 return true;
8009 }
8011 String cmd = "dllwrap";
8012 cmd.append(" -o ");
8013 cmd.append(fullOut);
8014 if (defFileName.size()>0)
8015 {
8016 cmd.append(" --def ");
8017 cmd.append(defFileName);
8018 cmd.append(" ");
8019 }
8020 if (impFileName.size()>0)
8021 {
8022 cmd.append(" --implib ");
8023 cmd.append(impFileName);
8024 cmd.append(" ");
8025 }
8026 for (unsigned int i=0 ; i<fileSet.size() ; i++)
8027 {
8028 String fname;
8029 if (fileSetDir.size()>0)
8030 {
8031 fname.append(fileSetDir);
8032 fname.append("/");
8033 }
8034 fname.append(fileSet[i]);
8035 String fullName = parent.resolve(fname);
8037 cmd.append(" ");
8038 cmd.append(fullName);
8039 }
8040 cmd.append(" ");
8041 cmd.append(libs);
8043 String outString, errString;
8044 if (!executeCommand(cmd.c_str(), "", outString, errString))
8045 {
8046 error("<sharedlib> problem: %s", errString.c_str());
8047 return false;
8048 }
8050 return true;
8051 }
8053 virtual bool parse(Element *elem)
8054 {
8055 if (!parent.getAttribute(elem, "command", commandOpt))
8056 return false;
8057 if (!parent.getAttribute(elem, "file", fileNameOpt))
8058 return false;
8059 if (!parent.getAttribute(elem, "import", impFileNameOpt))
8060 return false;
8061 if (!parent.getAttribute(elem, "def", defFileNameOpt))
8062 return false;
8064 std::vector<Element *> children = elem->getChildren();
8065 for (unsigned int i=0 ; i<children.size() ; i++)
8066 {
8067 Element *child = children[i];
8068 String tagName = child->getName();
8069 if (tagName == "fileset")
8070 {
8071 if (!parseFileSet(child, parent, fileSet))
8072 return false;
8073 }
8074 else if (tagName == "libs")
8075 {
8076 if (!parent.getValue(child, libsOpt))
8077 return false;
8078 libsOpt = strip(libsOpt);
8079 }
8080 }
8081 return true;
8082 }
8084 private:
8086 FileSet fileSet;
8088 String commandOpt;
8089 String fileNameOpt;
8090 String defFileNameOpt;
8091 String impFileNameOpt;
8092 String libsOpt;
8094 };
8098 /**
8099 * Run the "ar" command to archive .o's into a .a
8100 */
8101 class TaskStaticLib : public Task
8102 {
8103 public:
8105 TaskStaticLib(MakeBase &par) : Task(par)
8106 { type = TASK_STATICLIB; name = "staticlib"; }
8108 virtual ~TaskStaticLib()
8109 {}
8111 virtual bool execute()
8112 {
8113 String command = parent.eval(commandOpt, "ar crv");
8114 String fileName = parent.eval(fileNameOpt, "");
8116 bool doit = false;
8118 String fullOut = parent.resolve(fileName);
8119 //trace("ar fullout: %s", fullOut.c_str());
8121 if (!listFiles(parent, fileSet))
8122 return false;
8123 String fileSetDir = parent.eval(fileSet.getDirectory(), ".");
8124 //trace("###########HERE %s", fileSetDir.c_str());
8126 for (unsigned int i=0 ; i<fileSet.size() ; i++)
8127 {
8128 String fname;
8129 if (fileSetDir.size()>0)
8130 {
8131 fname.append(fileSetDir);
8132 fname.append("/");
8133 }
8134 fname.append(fileSet[i]);
8135 String fullName = parent.resolve(fname);
8136 //trace("ar : %s/%s", fullOut.c_str(), fullName.c_str());
8137 if (isNewerThan(fullName, fullOut))
8138 doit = true;
8139 }
8140 //trace("Needs it:%d", doit);
8141 if (!doit)
8142 {
8143 return true;
8144 }
8146 String cmd = command;
8147 cmd.append(" ");
8148 cmd.append(fullOut);
8149 for (unsigned int i=0 ; i<fileSet.size() ; i++)
8150 {
8151 String fname;
8152 if (fileSetDir.size()>0)
8153 {
8154 fname.append(fileSetDir);
8155 fname.append("/");
8156 }
8157 fname.append(fileSet[i]);
8158 String fullName = parent.resolve(fname);
8160 cmd.append(" ");
8161 cmd.append(fullName);
8162 }
8164 String outString, errString;
8165 if (!executeCommand(cmd.c_str(), "", outString, errString))
8166 {
8167 error("<staticlib> problem: %s", errString.c_str());
8168 return false;
8169 }
8171 return true;
8172 }
8175 virtual bool parse(Element *elem)
8176 {
8177 if (!parent.getAttribute(elem, "command", commandOpt))
8178 return false;
8179 if (!parent.getAttribute(elem, "file", fileNameOpt))
8180 return false;
8182 std::vector<Element *> children = elem->getChildren();
8183 for (unsigned int i=0 ; i<children.size() ; i++)
8184 {
8185 Element *child = children[i];
8186 String tagName = child->getName();
8187 if (tagName == "fileset")
8188 {
8189 if (!parseFileSet(child, parent, fileSet))
8190 return false;
8191 }
8192 }
8193 return true;
8194 }
8196 private:
8198 FileSet fileSet;
8200 String commandOpt;
8201 String fileNameOpt;
8203 };
8208 /**
8209 * Strip an executable
8210 */
8211 class TaskStrip : public Task
8212 {
8213 public:
8215 TaskStrip(MakeBase &par) : Task(par)
8216 { type = TASK_STRIP; name = "strip"; }
8218 virtual ~TaskStrip()
8219 {}
8221 virtual bool execute()
8222 {
8223 String command = parent.eval(commandOpt, "strip");
8224 String fileName = parent.eval(fileNameOpt, "");
8225 String symFileName = parent.eval(symFileNameOpt, "");
8227 String fullName = parent.resolve(fileName);
8228 //trace("fullDir:%s", fullDir.c_str());
8229 String cmd;
8230 String outbuf, errbuf;
8232 if (symFileName.size()>0)
8233 {
8234 String symFullName = parent.resolve(symFileName);
8235 cmd = "objcopy --only-keep-debug ";
8236 cmd.append(getNativePath(fullName));
8237 cmd.append(" ");
8238 cmd.append(getNativePath(symFullName));
8239 if (!executeCommand(cmd, "", outbuf, errbuf))
8240 {
8241 error("<strip> symbol file failed : %s", errbuf.c_str());
8242 return false;
8243 }
8244 }
8246 cmd = command;
8247 cmd.append(getNativePath(fullName));
8248 if (!executeCommand(cmd, "", outbuf, errbuf))
8249 {
8250 error("<strip> failed : %s", errbuf.c_str());
8251 return false;
8252 }
8253 return true;
8254 }
8256 virtual bool parse(Element *elem)
8257 {
8258 if (!parent.getAttribute(elem, "command", commandOpt))
8259 return false;
8260 if (!parent.getAttribute(elem, "file", fileNameOpt))
8261 return false;
8262 if (!parent.getAttribute(elem, "symfile", symFileNameOpt))
8263 return false;
8264 if (fileNameOpt.size() == 0)
8265 {
8266 error("<strip> requires 'file=\"fileName\"' attribute");
8267 return false;
8268 }
8269 return true;
8270 }
8272 private:
8274 String commandOpt;
8275 String fileNameOpt;
8276 String symFileNameOpt;
8277 };
8280 /**
8281 *
8282 */
8283 class TaskTouch : public Task
8284 {
8285 public:
8287 TaskTouch(MakeBase &par) : Task(par)
8288 { type = TASK_TOUCH; name = "touch"; }
8290 virtual ~TaskTouch()
8291 {}
8293 virtual bool execute()
8294 {
8295 String fileName = parent.eval(fileNameOpt, "");
8297 String fullName = parent.resolve(fileName);
8298 String nativeFile = getNativePath(fullName);
8299 if (!isRegularFile(fullName) && !isDirectory(fullName))
8300 {
8301 // S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH
8302 int ret = creat(nativeFile.c_str(), 0666);
8303 if (ret != 0)
8304 {
8305 error("<touch> could not create '%s' : %s",
8306 nativeFile.c_str(), strerror(ret));
8307 return false;
8308 }
8309 return true;
8310 }
8311 int ret = utime(nativeFile.c_str(), (struct utimbuf *)0);
8312 if (ret != 0)
8313 {
8314 error("<touch> could not update the modification time for '%s' : %s",
8315 nativeFile.c_str(), strerror(ret));
8316 return false;
8317 }
8318 return true;
8319 }
8321 virtual bool parse(Element *elem)
8322 {
8323 //trace("touch parse");
8324 if (!parent.getAttribute(elem, "file", fileNameOpt))
8325 return false;
8326 if (fileNameOpt.size() == 0)
8327 {
8328 error("<touch> requires 'file=\"fileName\"' attribute");
8329 return false;
8330 }
8331 return true;
8332 }
8334 String fileNameOpt;
8335 };
8338 /**
8339 *
8340 */
8341 class TaskTstamp : public Task
8342 {
8343 public:
8345 TaskTstamp(MakeBase &par) : Task(par)
8346 { type = TASK_TSTAMP; name = "tstamp"; }
8348 virtual ~TaskTstamp()
8349 {}
8351 virtual bool execute()
8352 {
8353 return true;
8354 }
8356 virtual bool parse(Element *elem)
8357 {
8358 //trace("tstamp parse");
8359 return true;
8360 }
8361 };
8365 /**
8366 *
8367 */
8368 Task *Task::createTask(Element *elem, int lineNr)
8369 {
8370 String tagName = elem->getName();
8371 //trace("task:%s", tagName.c_str());
8372 Task *task = NULL;
8373 if (tagName == "cc")
8374 task = new TaskCC(parent);
8375 else if (tagName == "copy")
8376 task = new TaskCopy(parent);
8377 else if (tagName == "delete")
8378 task = new TaskDelete(parent);
8379 else if (tagName == "echo")
8380 task = new TaskEcho(parent);
8381 else if (tagName == "jar")
8382 task = new TaskJar(parent);
8383 else if (tagName == "javac")
8384 task = new TaskJavac(parent);
8385 else if (tagName == "link")
8386 task = new TaskLink(parent);
8387 else if (tagName == "makefile")
8388 task = new TaskMakeFile(parent);
8389 else if (tagName == "mkdir")
8390 task = new TaskMkDir(parent);
8391 else if (tagName == "msgfmt")
8392 task = new TaskMsgFmt(parent);
8393 else if (tagName == "pkg-config")
8394 task = new TaskPkgConfig(parent);
8395 else if (tagName == "ranlib")
8396 task = new TaskRanlib(parent);
8397 else if (tagName == "rc")
8398 task = new TaskRC(parent);
8399 else if (tagName == "sharedlib")
8400 task = new TaskSharedLib(parent);
8401 else if (tagName == "staticlib")
8402 task = new TaskStaticLib(parent);
8403 else if (tagName == "strip")
8404 task = new TaskStrip(parent);
8405 else if (tagName == "touch")
8406 task = new TaskTouch(parent);
8407 else if (tagName == "tstamp")
8408 task = new TaskTstamp(parent);
8409 else
8410 {
8411 error("Unknown task '%s'", tagName.c_str());
8412 return NULL;
8413 }
8415 task->setLine(lineNr);
8417 if (!task->parse(elem))
8418 {
8419 delete task;
8420 return NULL;
8421 }
8422 return task;
8423 }
8427 //########################################################################
8428 //# T A R G E T
8429 //########################################################################
8431 /**
8432 *
8433 */
8434 class Target : public MakeBase
8435 {
8437 public:
8439 /**
8440 *
8441 */
8442 Target(Make &par) : parent(par)
8443 { init(); }
8445 /**
8446 *
8447 */
8448 Target(const Target &other) : parent(other.parent)
8449 { init(); assign(other); }
8451 /**
8452 *
8453 */
8454 Target &operator=(const Target &other)
8455 { init(); assign(other); return *this; }
8457 /**
8458 *
8459 */
8460 virtual ~Target()
8461 { cleanup() ; }
8464 /**
8465 *
8466 */
8467 virtual Make &getParent()
8468 { return parent; }
8470 /**
8471 *
8472 */
8473 virtual String getName()
8474 { return name; }
8476 /**
8477 *
8478 */
8479 virtual void setName(const String &val)
8480 { name = val; }
8482 /**
8483 *
8484 */
8485 virtual String getDescription()
8486 { return description; }
8488 /**
8489 *
8490 */
8491 virtual void setDescription(const String &val)
8492 { description = val; }
8494 /**
8495 *
8496 */
8497 virtual void addDependency(const String &val)
8498 { deps.push_back(val); }
8500 /**
8501 *
8502 */
8503 virtual void parseDependencies(const String &val)
8504 { deps = tokenize(val, ", "); }
8506 /**
8507 *
8508 */
8509 virtual std::vector<String> &getDependencies()
8510 { return deps; }
8512 /**
8513 *
8514 */
8515 virtual String getIf()
8516 { return ifVar; }
8518 /**
8519 *
8520 */
8521 virtual void setIf(const String &val)
8522 { ifVar = val; }
8524 /**
8525 *
8526 */
8527 virtual String getUnless()
8528 { return unlessVar; }
8530 /**
8531 *
8532 */
8533 virtual void setUnless(const String &val)
8534 { unlessVar = val; }
8536 /**
8537 *
8538 */
8539 virtual void addTask(Task *val)
8540 { tasks.push_back(val); }
8542 /**
8543 *
8544 */
8545 virtual std::vector<Task *> &getTasks()
8546 { return tasks; }
8548 private:
8550 void init()
8551 {
8552 }
8554 void cleanup()
8555 {
8556 tasks.clear();
8557 }
8559 void assign(const Target &other)
8560 {
8561 //parent = other.parent;
8562 name = other.name;
8563 description = other.description;
8564 ifVar = other.ifVar;
8565 unlessVar = other.unlessVar;
8566 deps = other.deps;
8567 tasks = other.tasks;
8568 }
8570 Make &parent;
8572 String name;
8574 String description;
8576 String ifVar;
8578 String unlessVar;
8580 std::vector<String> deps;
8582 std::vector<Task *> tasks;
8584 };
8593 //########################################################################
8594 //# M A K E
8595 //########################################################################
8598 /**
8599 *
8600 */
8601 class Make : public MakeBase
8602 {
8604 public:
8606 /**
8607 *
8608 */
8609 Make()
8610 { init(); }
8612 /**
8613 *
8614 */
8615 Make(const Make &other)
8616 { assign(other); }
8618 /**
8619 *
8620 */
8621 Make &operator=(const Make &other)
8622 { assign(other); return *this; }
8624 /**
8625 *
8626 */
8627 virtual ~Make()
8628 { cleanup(); }
8630 /**
8631 *
8632 */
8633 virtual std::map<String, Target> &getTargets()
8634 { return targets; }
8637 /**
8638 *
8639 */
8640 virtual String version()
8641 { return BUILDTOOL_VERSION; }
8643 /**
8644 * Overload a <property>
8645 */
8646 virtual bool specifyProperty(const String &name,
8647 const String &value);
8649 /**
8650 *
8651 */
8652 virtual bool run();
8654 /**
8655 *
8656 */
8657 virtual bool run(const String &target);
8661 private:
8663 /**
8664 *
8665 */
8666 void init();
8668 /**
8669 *
8670 */
8671 void cleanup();
8673 /**
8674 *
8675 */
8676 void assign(const Make &other);
8678 /**
8679 *
8680 */
8681 bool executeTask(Task &task);
8684 /**
8685 *
8686 */
8687 bool executeTarget(Target &target,
8688 std::set<String> &targetsCompleted);
8691 /**
8692 *
8693 */
8694 bool execute();
8696 /**
8697 *
8698 */
8699 bool checkTargetDependencies(Target &prop,
8700 std::vector<String> &depList);
8702 /**
8703 *
8704 */
8705 bool parsePropertyFile(const String &fileName,
8706 const String &prefix);
8708 /**
8709 *
8710 */
8711 bool parseProperty(Element *elem);
8713 /**
8714 *
8715 */
8716 bool parseFile();
8718 /**
8719 *
8720 */
8721 std::vector<String> glob(const String &pattern);
8724 //###############
8725 //# Fields
8726 //###############
8728 String projectName;
8730 String currentTarget;
8732 String defaultTarget;
8734 String specifiedTarget;
8736 String baseDir;
8738 String description;
8740 //std::vector<Property> properties;
8742 std::map<String, Target> targets;
8744 std::vector<Task *> allTasks;
8746 std::map<String, String> specifiedProperties;
8748 };
8751 //########################################################################
8752 //# C L A S S M A I N T E N A N C E
8753 //########################################################################
8755 /**
8756 *
8757 */
8758 void Make::init()
8759 {
8760 uri = "build.xml";
8761 projectName = "";
8762 currentTarget = "";
8763 defaultTarget = "";
8764 specifiedTarget = "";
8765 baseDir = "";
8766 description = "";
8767 envPrefix = "env.";
8768 pcPrefix = "pc.";
8769 pccPrefix = "pcc.";
8770 pclPrefix = "pcl.";
8771 properties.clear();
8772 for (unsigned int i = 0 ; i < allTasks.size() ; i++)
8773 delete allTasks[i];
8774 allTasks.clear();
8775 }
8779 /**
8780 *
8781 */
8782 void Make::cleanup()
8783 {
8784 for (unsigned int i = 0 ; i < allTasks.size() ; i++)
8785 delete allTasks[i];
8786 allTasks.clear();
8787 }
8791 /**
8792 *
8793 */
8794 void Make::assign(const Make &other)
8795 {
8796 uri = other.uri;
8797 projectName = other.projectName;
8798 currentTarget = other.currentTarget;
8799 defaultTarget = other.defaultTarget;
8800 specifiedTarget = other.specifiedTarget;
8801 baseDir = other.baseDir;
8802 description = other.description;
8803 properties = other.properties;
8804 }
8808 //########################################################################
8809 //# U T I L I T Y T A S K S
8810 //########################################################################
8812 /**
8813 * Perform a file globbing
8814 */
8815 std::vector<String> Make::glob(const String &pattern)
8816 {
8817 std::vector<String> res;
8818 return res;
8819 }
8822 //########################################################################
8823 //# P U B L I C A P I
8824 //########################################################################
8828 /**
8829 *
8830 */
8831 bool Make::executeTarget(Target &target,
8832 std::set<String> &targetsCompleted)
8833 {
8835 String name = target.getName();
8837 //First get any dependencies for this target
8838 std::vector<String> deps = target.getDependencies();
8839 for (unsigned int i=0 ; i<deps.size() ; i++)
8840 {
8841 String dep = deps[i];
8842 //Did we do it already? Skip
8843 if (targetsCompleted.find(dep)!=targetsCompleted.end())
8844 continue;
8846 std::map<String, Target> &tgts =
8847 target.getParent().getTargets();
8848 std::map<String, Target>::iterator iter =
8849 tgts.find(dep);
8850 if (iter == tgts.end())
8851 {
8852 error("Target '%s' dependency '%s' not found",
8853 name.c_str(), dep.c_str());
8854 return false;
8855 }
8856 Target depTarget = iter->second;
8857 if (!executeTarget(depTarget, targetsCompleted))
8858 {
8859 return false;
8860 }
8861 }
8863 status("##### Target : %s\n##### %s", name.c_str(),
8864 target.getDescription().c_str());
8866 //Now let's do the tasks
8867 std::vector<Task *> &tasks = target.getTasks();
8868 for (unsigned int i=0 ; i<tasks.size() ; i++)
8869 {
8870 Task *task = tasks[i];
8871 status("--- %s / %s", name.c_str(), task->getName().c_str());
8872 if (!task->execute())
8873 {
8874 return false;
8875 }
8876 }
8878 targetsCompleted.insert(name);
8880 return true;
8881 }
8885 /**
8886 * Main execute() method. Start here and work
8887 * up the dependency tree
8888 */
8889 bool Make::execute()
8890 {
8891 status("######## EXECUTE");
8893 //Determine initial target
8894 if (specifiedTarget.size()>0)
8895 {
8896 currentTarget = specifiedTarget;
8897 }
8898 else if (defaultTarget.size()>0)
8899 {
8900 currentTarget = defaultTarget;
8901 }
8902 else
8903 {
8904 error("execute: no specified or default target requested");
8905 return false;
8906 }
8908 std::map<String, Target>::iterator iter =
8909 targets.find(currentTarget);
8910 if (iter == targets.end())
8911 {
8912 error("Initial target '%s' not found",
8913 currentTarget.c_str());
8914 return false;
8915 }
8917 //Now run
8918 Target target = iter->second;
8919 std::set<String> targetsCompleted;
8920 if (!executeTarget(target, targetsCompleted))
8921 {
8922 return false;
8923 }
8925 status("######## EXECUTE COMPLETE");
8926 return true;
8927 }
8932 /**
8933 *
8934 */
8935 bool Make::checkTargetDependencies(Target &target,
8936 std::vector<String> &depList)
8937 {
8938 String tgtName = target.getName().c_str();
8939 depList.push_back(tgtName);
8941 std::vector<String> deps = target.getDependencies();
8942 for (unsigned int i=0 ; i<deps.size() ; i++)
8943 {
8944 String dep = deps[i];
8945 //First thing entered was the starting Target
8946 if (dep == depList[0])
8947 {
8948 error("Circular dependency '%s' found at '%s'",
8949 dep.c_str(), tgtName.c_str());
8950 std::vector<String>::iterator diter;
8951 for (diter=depList.begin() ; diter!=depList.end() ; diter++)
8952 {
8953 error(" %s", diter->c_str());
8954 }
8955 return false;
8956 }
8958 std::map<String, Target> &tgts =
8959 target.getParent().getTargets();
8960 std::map<String, Target>::iterator titer = tgts.find(dep);
8961 if (titer == tgts.end())
8962 {
8963 error("Target '%s' dependency '%s' not found",
8964 tgtName.c_str(), dep.c_str());
8965 return false;
8966 }
8967 if (!checkTargetDependencies(titer->second, depList))
8968 {
8969 return false;
8970 }
8971 }
8972 return true;
8973 }
8979 static int getword(int pos, const String &inbuf, String &result)
8980 {
8981 int p = pos;
8982 int len = (int)inbuf.size();
8983 String val;
8984 while (p < len)
8985 {
8986 char ch = inbuf[p];
8987 if (!isalnum(ch) && ch!='.' && ch!='_')
8988 break;
8989 val.push_back(ch);
8990 p++;
8991 }
8992 result = val;
8993 return p;
8994 }
8999 /**
9000 *
9001 */
9002 bool Make::parsePropertyFile(const String &fileName,
9003 const String &prefix)
9004 {
9005 FILE *f = fopen(fileName.c_str(), "r");
9006 if (!f)
9007 {
9008 error("could not open property file %s", fileName.c_str());
9009 return false;
9010 }
9011 int linenr = 0;
9012 while (!feof(f))
9013 {
9014 char buf[256];
9015 if (!fgets(buf, 255, f))
9016 break;
9017 linenr++;
9018 String s = buf;
9019 s = trim(s);
9020 int len = s.size();
9021 if (len == 0)
9022 continue;
9023 if (s[0] == '#')
9024 continue;
9025 String key;
9026 String val;
9027 int p = 0;
9028 int p2 = getword(p, s, key);
9029 if (p2 <= p)
9030 {
9031 error("property file %s, line %d: expected keyword",
9032 fileName.c_str(), linenr);
9033 return false;
9034 }
9035 if (prefix.size() > 0)
9036 {
9037 key.insert(0, prefix);
9038 }
9040 //skip whitespace
9041 for (p=p2 ; p<len ; p++)
9042 if (!isspace(s[p]))
9043 break;
9045 if (p>=len || s[p]!='=')
9046 {
9047 error("property file %s, line %d: expected '='",
9048 fileName.c_str(), linenr);
9049 return false;
9050 }
9051 p++;
9053 //skip whitespace
9054 for ( ; p<len ; p++)
9055 if (!isspace(s[p]))
9056 break;
9058 /* This way expects a word after the =
9059 p2 = getword(p, s, val);
9060 if (p2 <= p)
9061 {
9062 error("property file %s, line %d: expected value",
9063 fileName.c_str(), linenr);
9064 return false;
9065 }
9066 */
9067 // This way gets the rest of the line after the =
9068 if (p>=len)
9069 {
9070 error("property file %s, line %d: expected value",
9071 fileName.c_str(), linenr);
9072 return false;
9073 }
9074 val = s.substr(p);
9075 if (key.size()==0)
9076 continue;
9077 //allow property to be set, even if val=""
9079 //trace("key:'%s' val:'%s'", key.c_str(), val.c_str());
9080 //See if we wanted to overload this property
9081 std::map<String, String>::iterator iter =
9082 specifiedProperties.find(key);
9083 if (iter!=specifiedProperties.end())
9084 {
9085 val = iter->second;
9086 status("overloading property '%s' = '%s'",
9087 key.c_str(), val.c_str());
9088 }
9089 properties[key] = val;
9090 }
9091 fclose(f);
9092 return true;
9093 }
9098 /**
9099 *
9100 */
9101 bool Make::parseProperty(Element *elem)
9102 {
9103 std::vector<Attribute> &attrs = elem->getAttributes();
9104 for (unsigned int i=0 ; i<attrs.size() ; i++)
9105 {
9106 String attrName = attrs[i].getName();
9107 String attrVal = attrs[i].getValue();
9109 if (attrName == "name")
9110 {
9111 String val;
9112 if (!getAttribute(elem, "value", val))
9113 return false;
9114 if (val.size() > 0)
9115 {
9116 properties[attrVal] = val;
9117 }
9118 else
9119 {
9120 if (!getAttribute(elem, "location", val))
9121 return false;
9122 //let the property exist, even if not defined
9123 properties[attrVal] = val;
9124 }
9125 //See if we wanted to overload this property
9126 std::map<String, String>::iterator iter =
9127 specifiedProperties.find(attrVal);
9128 if (iter != specifiedProperties.end())
9129 {
9130 val = iter->second;
9131 status("overloading property '%s' = '%s'",
9132 attrVal.c_str(), val.c_str());
9133 properties[attrVal] = val;
9134 }
9135 }
9136 else if (attrName == "file")
9137 {
9138 String prefix;
9139 if (!getAttribute(elem, "prefix", prefix))
9140 return false;
9141 if (prefix.size() > 0)
9142 {
9143 if (prefix[prefix.size()-1] != '.')
9144 prefix.push_back('.');
9145 }
9146 if (!parsePropertyFile(attrName, prefix))
9147 return false;
9148 }
9149 else if (attrName == "environment")
9150 {
9151 if (attrVal.find('.') != attrVal.npos)
9152 {
9153 error("environment prefix cannot have a '.' in it");
9154 return false;
9155 }
9156 envPrefix = attrVal;
9157 envPrefix.push_back('.');
9158 }
9159 else if (attrName == "pkg-config")
9160 {
9161 if (attrVal.find('.') != attrVal.npos)
9162 {
9163 error("pkg-config prefix cannot have a '.' in it");
9164 return false;
9165 }
9166 pcPrefix = attrVal;
9167 pcPrefix.push_back('.');
9168 }
9169 else if (attrName == "pkg-config-cflags")
9170 {
9171 if (attrVal.find('.') != attrVal.npos)
9172 {
9173 error("pkg-config-cflags prefix cannot have a '.' in it");
9174 return false;
9175 }
9176 pccPrefix = attrVal;
9177 pccPrefix.push_back('.');
9178 }
9179 else if (attrName == "pkg-config-libs")
9180 {
9181 if (attrVal.find('.') != attrVal.npos)
9182 {
9183 error("pkg-config-libs prefix cannot have a '.' in it");
9184 return false;
9185 }
9186 pclPrefix = attrVal;
9187 pclPrefix.push_back('.');
9188 }
9189 }
9191 return true;
9192 }
9197 /**
9198 *
9199 */
9200 bool Make::parseFile()
9201 {
9202 status("######## PARSE : %s", uri.getPath().c_str());
9204 setLine(0);
9206 Parser parser;
9207 Element *root = parser.parseFile(uri.getNativePath());
9208 if (!root)
9209 {
9210 error("Could not open %s for reading",
9211 uri.getNativePath().c_str());
9212 return false;
9213 }
9215 setLine(root->getLine());
9217 if (root->getChildren().size()==0 ||
9218 root->getChildren()[0]->getName()!="project")
9219 {
9220 error("Main xml element should be <project>");
9221 delete root;
9222 return false;
9223 }
9225 //########## Project attributes
9226 Element *project = root->getChildren()[0];
9227 String s = project->getAttribute("name");
9228 if (s.size() > 0)
9229 projectName = s;
9230 s = project->getAttribute("default");
9231 if (s.size() > 0)
9232 defaultTarget = s;
9233 s = project->getAttribute("basedir");
9234 if (s.size() > 0)
9235 baseDir = s;
9237 //######### PARSE MEMBERS
9238 std::vector<Element *> children = project->getChildren();
9239 for (unsigned int i=0 ; i<children.size() ; i++)
9240 {
9241 Element *elem = children[i];
9242 setLine(elem->getLine());
9243 String tagName = elem->getName();
9245 //########## DESCRIPTION
9246 if (tagName == "description")
9247 {
9248 description = parser.trim(elem->getValue());
9249 }
9251 //######### PROPERTY
9252 else if (tagName == "property")
9253 {
9254 if (!parseProperty(elem))
9255 return false;
9256 }
9258 //######### TARGET
9259 else if (tagName == "target")
9260 {
9261 String tname = elem->getAttribute("name");
9262 String tdesc = elem->getAttribute("description");
9263 String tdeps = elem->getAttribute("depends");
9264 String tif = elem->getAttribute("if");
9265 String tunless = elem->getAttribute("unless");
9266 Target target(*this);
9267 target.setName(tname);
9268 target.setDescription(tdesc);
9269 target.parseDependencies(tdeps);
9270 target.setIf(tif);
9271 target.setUnless(tunless);
9272 std::vector<Element *> telems = elem->getChildren();
9273 for (unsigned int i=0 ; i<telems.size() ; i++)
9274 {
9275 Element *telem = telems[i];
9276 Task breeder(*this);
9277 Task *task = breeder.createTask(telem, telem->getLine());
9278 if (!task)
9279 return false;
9280 allTasks.push_back(task);
9281 target.addTask(task);
9282 }
9284 //Check name
9285 if (tname.size() == 0)
9286 {
9287 error("no name for target");
9288 return false;
9289 }
9290 //Check for duplicate name
9291 if (targets.find(tname) != targets.end())
9292 {
9293 error("target '%s' already defined", tname.c_str());
9294 return false;
9295 }
9296 //more work than targets[tname]=target, but avoids default allocator
9297 targets.insert(std::make_pair<String, Target>(tname, target));
9298 }
9299 //######### none of the above
9300 else
9301 {
9302 error("unknown toplevel tag: <%s>", tagName.c_str());
9303 return false;
9304 }
9306 }
9308 std::map<String, Target>::iterator iter;
9309 for (iter = targets.begin() ; iter!= targets.end() ; iter++)
9310 {
9311 Target tgt = iter->second;
9312 std::vector<String> depList;
9313 if (!checkTargetDependencies(tgt, depList))
9314 {
9315 return false;
9316 }
9317 }
9320 delete root;
9321 status("######## PARSE COMPLETE");
9322 return true;
9323 }
9326 /**
9327 * Overload a <property>
9328 */
9329 bool Make::specifyProperty(const String &name, const String &value)
9330 {
9331 if (specifiedProperties.find(name) != specifiedProperties.end())
9332 {
9333 error("Property %s already specified", name.c_str());
9334 return false;
9335 }
9336 specifiedProperties[name] = value;
9337 return true;
9338 }
9342 /**
9343 *
9344 */
9345 bool Make::run()
9346 {
9347 if (!parseFile())
9348 return false;
9350 if (!execute())
9351 return false;
9353 return true;
9354 }
9359 /**
9360 * Get a formatted MM:SS.sss time elapsed string
9361 */
9362 static String
9363 timeDiffString(struct timeval &x, struct timeval &y)
9364 {
9365 long microsX = x.tv_usec;
9366 long secondsX = x.tv_sec;
9367 long microsY = y.tv_usec;
9368 long secondsY = y.tv_sec;
9369 if (microsX < microsY)
9370 {
9371 microsX += 1000000;
9372 secondsX -= 1;
9373 }
9375 int seconds = (int)(secondsX - secondsY);
9376 int millis = (int)((microsX - microsY)/1000);
9378 int minutes = seconds/60;
9379 seconds -= minutes*60;
9380 char buf[80];
9381 snprintf(buf, 79, "%dm %d.%03ds", minutes, seconds, millis);
9382 String ret = buf;
9383 return ret;
9385 }
9387 /**
9388 *
9389 */
9390 bool Make::run(const String &target)
9391 {
9392 status("####################################################");
9393 status("# %s", version().c_str());
9394 status("####################################################");
9395 struct timeval timeStart, timeEnd;
9396 ::gettimeofday(&timeStart, NULL);
9397 specifiedTarget = target;
9398 if (!run())
9399 return false;
9400 ::gettimeofday(&timeEnd, NULL);
9401 String timeStr = timeDiffString(timeEnd, timeStart);
9402 status("####################################################");
9403 status("# BuildTool Completed : %s", timeStr.c_str());
9404 status("####################################################");
9405 return true;
9406 }
9414 }// namespace buildtool
9415 //########################################################################
9416 //# M A I N
9417 //########################################################################
9419 typedef buildtool::String String;
9421 /**
9422 * Format an error message in printf() style
9423 */
9424 static void error(const char *fmt, ...)
9425 {
9426 va_list ap;
9427 va_start(ap, fmt);
9428 fprintf(stderr, "BuildTool error: ");
9429 vfprintf(stderr, fmt, ap);
9430 fprintf(stderr, "\n");
9431 va_end(ap);
9432 }
9435 static bool parseProperty(const String &s, String &name, String &val)
9436 {
9437 int len = s.size();
9438 int i;
9439 for (i=0 ; i<len ; i++)
9440 {
9441 char ch = s[i];
9442 if (ch == '=')
9443 break;
9444 name.push_back(ch);
9445 }
9446 if (i>=len || s[i]!='=')
9447 {
9448 error("property requires -Dname=value");
9449 return false;
9450 }
9451 i++;
9452 for ( ; i<len ; i++)
9453 {
9454 char ch = s[i];
9455 val.push_back(ch);
9456 }
9457 return true;
9458 }
9461 /**
9462 * Compare a buffer with a key, for the length of the key
9463 */
9464 static bool sequ(const String &buf, const char *key)
9465 {
9466 int len = buf.size();
9467 for (int i=0 ; key[i] && i<len ; i++)
9468 {
9469 if (key[i] != buf[i])
9470 return false;
9471 }
9472 return true;
9473 }
9475 static void usage(int argc, char **argv)
9476 {
9477 printf("usage:\n");
9478 printf(" %s [options] [target]\n", argv[0]);
9479 printf("Options:\n");
9480 printf(" -help, -h print this message\n");
9481 printf(" -version print the version information and exit\n");
9482 printf(" -file <file> use given buildfile\n");
9483 printf(" -f <file> ''\n");
9484 printf(" -D<property>=<value> use value for given property\n");
9485 }
9490 /**
9491 * Parse the command-line args, get our options,
9492 * and run this thing
9493 */
9494 static bool parseOptions(int argc, char **argv)
9495 {
9496 if (argc < 1)
9497 {
9498 error("Cannot parse arguments");
9499 return false;
9500 }
9502 buildtool::Make make;
9504 String target;
9506 //char *progName = argv[0];
9507 for (int i=1 ; i<argc ; i++)
9508 {
9509 String arg = argv[i];
9510 if (arg.size()>1 && arg[0]=='-')
9511 {
9512 if (arg == "-h" || arg == "-help")
9513 {
9514 usage(argc,argv);
9515 return true;
9516 }
9517 else if (arg == "-version")
9518 {
9519 printf("%s", make.version().c_str());
9520 return true;
9521 }
9522 else if (arg == "-f" || arg == "-file")
9523 {
9524 if (i>=argc)
9525 {
9526 usage(argc, argv);
9527 return false;
9528 }
9529 i++; //eat option
9530 make.setURI(argv[i]);
9531 }
9532 else if (arg.size()>2 && sequ(arg, "-D"))
9533 {
9534 String s = arg.substr(2, arg.size());
9535 String name, value;
9536 if (!parseProperty(s, name, value))
9537 {
9538 usage(argc, argv);
9539 return false;
9540 }
9541 if (!make.specifyProperty(name, value))
9542 return false;
9543 }
9544 else
9545 {
9546 error("Unknown option:%s", arg.c_str());
9547 return false;
9548 }
9549 }
9550 else
9551 {
9552 if (target.size()>0)
9553 {
9554 error("only one initial target");
9555 usage(argc, argv);
9556 return false;
9557 }
9558 target = arg;
9559 }
9560 }
9562 //We have the options. Now execute them
9563 if (!make.run(target))
9564 return false;
9566 return true;
9567 }
9572 /*
9573 static bool runMake()
9574 {
9575 buildtool::Make make;
9576 if (!make.run())
9577 return false;
9578 return true;
9579 }
9582 static bool pkgConfigTest()
9583 {
9584 buildtool::PkgConfig pkgConfig;
9585 if (!pkgConfig.readFile("gtk+-2.0.pc"))
9586 return false;
9587 return true;
9588 }
9592 static bool depTest()
9593 {
9594 buildtool::DepTool deptool;
9595 deptool.setSourceDirectory("/dev/ink/inkscape/src");
9596 if (!deptool.generateDependencies("build.dep"))
9597 return false;
9598 std::vector<buildtool::FileRec> res =
9599 deptool.loadDepFile("build.dep");
9600 if (res.size() == 0)
9601 return false;
9602 return true;
9603 }
9605 static bool popenTest()
9606 {
9607 buildtool::Make make;
9608 buildtool::String out, err;
9609 bool ret = make.executeCommand("gcc xx.cpp", "", out, err);
9610 printf("Popen test:%d '%s' '%s'\n", ret, out.c_str(), err.c_str());
9611 return true;
9612 }
9615 static bool propFileTest()
9616 {
9617 buildtool::Make make;
9618 make.parsePropertyFile("test.prop", "test.");
9619 return true;
9620 }
9621 */
9623 int main(int argc, char **argv)
9624 {
9626 if (!parseOptions(argc, argv))
9627 return 1;
9628 /*
9629 if (!popenTest())
9630 return 1;
9632 if (!depTest())
9633 return 1;
9634 if (!propFileTest())
9635 return 1;
9636 if (runMake())
9637 return 1;
9638 */
9639 return 0;
9640 }
9643 //########################################################################
9644 //# E N D
9645 //########################################################################