6c38eddb71775cb33b5582ea2dbb4bf6ec83677a
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.0"
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>
58 #ifdef __WIN32__
59 #include <windows.h>
60 #endif
63 #include <errno.h>
66 //########################################################################
67 //# Definition of gettimeofday() for those who don't have it
68 //########################################################################
69 #ifdef NEED_GETTIMEOFDAY
70 #include <sys/timeb.h>
72 struct timezone {
73 int tz_minuteswest; /* minutes west of Greenwich */
74 int tz_dsttime; /* type of dst correction */
75 };
77 static int gettimeofday (struct timeval *tv, struct timezone *tz)
78 {
79 struct _timeb tb;
81 if (!tv)
82 return (-1);
84 _ftime (&tb);
85 tv->tv_sec = tb.time;
86 tv->tv_usec = tb.millitm * 1000 + 500;
87 if (tz)
88 {
89 tz->tz_minuteswest = -60 * _timezone;
90 tz->tz_dsttime = _daylight;
91 }
92 return 0;
93 }
95 #endif
103 namespace buildtool
104 {
109 //########################################################################
110 //########################################################################
111 //## R E G E X P
112 //########################################################################
113 //########################################################################
115 /**
116 * This is the T-Rex regular expression library, which we
117 * gratefully acknowledge. It's clean code and small size allow
118 * us to embed it in BuildTool without adding a dependency
119 *
120 */
122 //begin trex.h
124 #ifndef _TREX_H_
125 #define _TREX_H_
126 /***************************************************************
127 T-Rex a tiny regular expression library
129 Copyright (C) 2003-2006 Alberto Demichelis
131 This software is provided 'as-is', without any express
132 or implied warranty. In no event will the authors be held
133 liable for any damages arising from the use of this software.
135 Permission is granted to anyone to use this software for
136 any purpose, including commercial applications, and to alter
137 it and redistribute it freely, subject to the following restrictions:
139 1. The origin of this software must not be misrepresented;
140 you must not claim that you wrote the original software.
141 If you use this software in a product, an acknowledgment
142 in the product documentation would be appreciated but
143 is not required.
145 2. Altered source versions must be plainly marked as such,
146 and must not be misrepresented as being the original software.
148 3. This notice may not be removed or altered from any
149 source distribution.
151 ****************************************************************/
153 #ifdef _UNICODE
154 #define TRexChar unsigned short
155 #define MAX_CHAR 0xFFFF
156 #define _TREXC(c) L##c
157 #define trex_strlen wcslen
158 #define trex_printf wprintf
159 #else
160 #define TRexChar char
161 #define MAX_CHAR 0xFF
162 #define _TREXC(c) (c)
163 #define trex_strlen strlen
164 #define trex_printf printf
165 #endif
167 #ifndef TREX_API
168 #define TREX_API extern
169 #endif
171 #define TRex_True 1
172 #define TRex_False 0
174 typedef unsigned int TRexBool;
175 typedef struct TRex TRex;
177 typedef struct {
178 const TRexChar *begin;
179 int len;
180 } TRexMatch;
182 TREX_API TRex *trex_compile(const TRexChar *pattern,const TRexChar **error);
183 TREX_API void trex_free(TRex *exp);
184 TREX_API TRexBool trex_match(TRex* exp,const TRexChar* text);
185 TREX_API TRexBool trex_search(TRex* exp,const TRexChar* text, const TRexChar** out_begin, const TRexChar** out_end);
186 TREX_API TRexBool trex_searchrange(TRex* exp,const TRexChar* text_begin,const TRexChar* text_end,const TRexChar** out_begin, const TRexChar** out_end);
187 TREX_API int trex_getsubexpcount(TRex* exp);
188 TREX_API TRexBool trex_getsubexp(TRex* exp, int n, TRexMatch *subexp);
190 #endif
192 //end trex.h
194 //start trex.c
197 #include <stdio.h>
198 #include <string>
200 /* see copyright notice in trex.h */
201 #include <string.h>
202 #include <stdlib.h>
203 #include <ctype.h>
204 #include <setjmp.h>
205 //#include "trex.h"
207 #ifdef _UINCODE
208 #define scisprint iswprint
209 #define scstrlen wcslen
210 #define scprintf wprintf
211 #define _SC(x) L(x)
212 #else
213 #define scisprint isprint
214 #define scstrlen strlen
215 #define scprintf printf
216 #define _SC(x) (x)
217 #endif
219 #ifdef _DEBUG
220 #include <stdio.h>
222 static const TRexChar *g_nnames[] =
223 {
224 _SC("NONE"),_SC("OP_GREEDY"), _SC("OP_OR"),
225 _SC("OP_EXPR"),_SC("OP_NOCAPEXPR"),_SC("OP_DOT"), _SC("OP_CLASS"),
226 _SC("OP_CCLASS"),_SC("OP_NCLASS"),_SC("OP_RANGE"),_SC("OP_CHAR"),
227 _SC("OP_EOL"),_SC("OP_BOL"),_SC("OP_WB")
228 };
230 #endif
231 #define OP_GREEDY (MAX_CHAR+1) // * + ? {n}
232 #define OP_OR (MAX_CHAR+2)
233 #define OP_EXPR (MAX_CHAR+3) //parentesis ()
234 #define OP_NOCAPEXPR (MAX_CHAR+4) //parentesis (?:)
235 #define OP_DOT (MAX_CHAR+5)
236 #define OP_CLASS (MAX_CHAR+6)
237 #define OP_CCLASS (MAX_CHAR+7)
238 #define OP_NCLASS (MAX_CHAR+8) //negates class the [^
239 #define OP_RANGE (MAX_CHAR+9)
240 #define OP_CHAR (MAX_CHAR+10)
241 #define OP_EOL (MAX_CHAR+11)
242 #define OP_BOL (MAX_CHAR+12)
243 #define OP_WB (MAX_CHAR+13)
245 #define TREX_SYMBOL_ANY_CHAR ('.')
246 #define TREX_SYMBOL_GREEDY_ONE_OR_MORE ('+')
247 #define TREX_SYMBOL_GREEDY_ZERO_OR_MORE ('*')
248 #define TREX_SYMBOL_GREEDY_ZERO_OR_ONE ('?')
249 #define TREX_SYMBOL_BRANCH ('|')
250 #define TREX_SYMBOL_END_OF_STRING ('$')
251 #define TREX_SYMBOL_BEGINNING_OF_STRING ('^')
252 #define TREX_SYMBOL_ESCAPE_CHAR ('\\')
255 typedef int TRexNodeType;
257 typedef struct tagTRexNode{
258 TRexNodeType type;
259 int left;
260 int right;
261 int next;
262 }TRexNode;
264 struct TRex{
265 const TRexChar *_eol;
266 const TRexChar *_bol;
267 const TRexChar *_p;
268 int _first;
269 int _op;
270 TRexNode *_nodes;
271 int _nallocated;
272 int _nsize;
273 int _nsubexpr;
274 TRexMatch *_matches;
275 int _currsubexp;
276 void *_jmpbuf;
277 const TRexChar **_error;
278 };
280 static int trex_list(TRex *exp);
282 static int trex_newnode(TRex *exp, TRexNodeType type)
283 {
284 TRexNode n;
285 int newid;
286 n.type = type;
287 n.next = n.right = n.left = -1;
288 if(type == OP_EXPR)
289 n.right = exp->_nsubexpr++;
290 if(exp->_nallocated < (exp->_nsize + 1)) {
291 //int oldsize = exp->_nallocated;
292 exp->_nallocated *= 2;
293 exp->_nodes = (TRexNode *)realloc(exp->_nodes, exp->_nallocated * sizeof(TRexNode));
294 }
295 exp->_nodes[exp->_nsize++] = n;
296 newid = exp->_nsize - 1;
297 return (int)newid;
298 }
300 static void trex_error(TRex *exp,const TRexChar *error)
301 {
302 if(exp->_error) *exp->_error = error;
303 longjmp(*((jmp_buf*)exp->_jmpbuf),-1);
304 }
306 static void trex_expect(TRex *exp, int n){
307 if((*exp->_p) != n)
308 trex_error(exp, _SC("expected paren"));
309 exp->_p++;
310 }
312 static TRexChar trex_escapechar(TRex *exp)
313 {
314 if(*exp->_p == TREX_SYMBOL_ESCAPE_CHAR){
315 exp->_p++;
316 switch(*exp->_p) {
317 case 'v': exp->_p++; return '\v';
318 case 'n': exp->_p++; return '\n';
319 case 't': exp->_p++; return '\t';
320 case 'r': exp->_p++; return '\r';
321 case 'f': exp->_p++; return '\f';
322 default: return (*exp->_p++);
323 }
324 } else if(!scisprint(*exp->_p)) trex_error(exp,_SC("letter expected"));
325 return (*exp->_p++);
326 }
328 static int trex_charclass(TRex *exp,int classid)
329 {
330 int n = trex_newnode(exp,OP_CCLASS);
331 exp->_nodes[n].left = classid;
332 return n;
333 }
335 static int trex_charnode(TRex *exp,TRexBool isclass)
336 {
337 TRexChar t;
338 if(*exp->_p == TREX_SYMBOL_ESCAPE_CHAR) {
339 exp->_p++;
340 switch(*exp->_p) {
341 case 'n': exp->_p++; return trex_newnode(exp,'\n');
342 case 't': exp->_p++; return trex_newnode(exp,'\t');
343 case 'r': exp->_p++; return trex_newnode(exp,'\r');
344 case 'f': exp->_p++; return trex_newnode(exp,'\f');
345 case 'v': exp->_p++; return trex_newnode(exp,'\v');
346 case 'a': case 'A': case 'w': case 'W': case 's': case 'S':
347 case 'd': case 'D': case 'x': case 'X': case 'c': case 'C':
348 case 'p': case 'P': case 'l': case 'u':
349 {
350 t = *exp->_p; exp->_p++;
351 return trex_charclass(exp,t);
352 }
353 case 'b':
354 case 'B':
355 if(!isclass) {
356 int node = trex_newnode(exp,OP_WB);
357 exp->_nodes[node].left = *exp->_p;
358 exp->_p++;
359 return node;
360 } //else default
361 default:
362 t = *exp->_p; exp->_p++;
363 return trex_newnode(exp,t);
364 }
365 }
366 else if(!scisprint(*exp->_p)) {
368 trex_error(exp,_SC("letter expected"));
369 }
370 t = *exp->_p; exp->_p++;
371 return trex_newnode(exp,t);
372 }
373 static int trex_class(TRex *exp)
374 {
375 int ret = -1;
376 int first = -1,chain;
377 if(*exp->_p == TREX_SYMBOL_BEGINNING_OF_STRING){
378 ret = trex_newnode(exp,OP_NCLASS);
379 exp->_p++;
380 }else ret = trex_newnode(exp,OP_CLASS);
382 if(*exp->_p == ']') trex_error(exp,_SC("empty class"));
383 chain = ret;
384 while(*exp->_p != ']' && exp->_p != exp->_eol) {
385 if(*exp->_p == '-' && first != -1){
386 int r,t;
387 if(*exp->_p++ == ']') trex_error(exp,_SC("unfinished range"));
388 r = trex_newnode(exp,OP_RANGE);
389 if(first>*exp->_p) trex_error(exp,_SC("invalid range"));
390 if(exp->_nodes[first].type == OP_CCLASS) trex_error(exp,_SC("cannot use character classes in ranges"));
391 exp->_nodes[r].left = exp->_nodes[first].type;
392 t = trex_escapechar(exp);
393 exp->_nodes[r].right = t;
394 exp->_nodes[chain].next = r;
395 chain = r;
396 first = -1;
397 }
398 else{
399 if(first!=-1){
400 int c = first;
401 exp->_nodes[chain].next = c;
402 chain = c;
403 first = trex_charnode(exp,TRex_True);
404 }
405 else{
406 first = trex_charnode(exp,TRex_True);
407 }
408 }
409 }
410 if(first!=-1){
411 int c = first;
412 exp->_nodes[chain].next = c;
413 chain = c;
414 first = -1;
415 }
416 /* hack? */
417 exp->_nodes[ret].left = exp->_nodes[ret].next;
418 exp->_nodes[ret].next = -1;
419 return ret;
420 }
422 static int trex_parsenumber(TRex *exp)
423 {
424 int ret = *exp->_p-'0';
425 int positions = 10;
426 exp->_p++;
427 while(isdigit(*exp->_p)) {
428 ret = ret*10+(*exp->_p++-'0');
429 if(positions==1000000000) trex_error(exp,_SC("overflow in numeric constant"));
430 positions *= 10;
431 };
432 return ret;
433 }
435 static int trex_element(TRex *exp)
436 {
437 int ret = -1;
438 switch(*exp->_p)
439 {
440 case '(': {
441 int expr,newn;
442 exp->_p++;
445 if(*exp->_p =='?') {
446 exp->_p++;
447 trex_expect(exp,':');
448 expr = trex_newnode(exp,OP_NOCAPEXPR);
449 }
450 else
451 expr = trex_newnode(exp,OP_EXPR);
452 newn = trex_list(exp);
453 exp->_nodes[expr].left = newn;
454 ret = expr;
455 trex_expect(exp,')');
456 }
457 break;
458 case '[':
459 exp->_p++;
460 ret = trex_class(exp);
461 trex_expect(exp,']');
462 break;
463 case TREX_SYMBOL_END_OF_STRING: exp->_p++; ret = trex_newnode(exp,OP_EOL);break;
464 case TREX_SYMBOL_ANY_CHAR: exp->_p++; ret = trex_newnode(exp,OP_DOT);break;
465 default:
466 ret = trex_charnode(exp,TRex_False);
467 break;
468 }
470 {
471 int op;
472 TRexBool isgreedy = TRex_False;
473 unsigned short p0 = 0, p1 = 0;
474 switch(*exp->_p){
475 case TREX_SYMBOL_GREEDY_ZERO_OR_MORE: p0 = 0; p1 = 0xFFFF; exp->_p++; isgreedy = TRex_True; break;
476 case TREX_SYMBOL_GREEDY_ONE_OR_MORE: p0 = 1; p1 = 0xFFFF; exp->_p++; isgreedy = TRex_True; break;
477 case TREX_SYMBOL_GREEDY_ZERO_OR_ONE: p0 = 0; p1 = 1; exp->_p++; isgreedy = TRex_True; break;
478 case '{':
479 exp->_p++;
480 if(!isdigit(*exp->_p)) trex_error(exp,_SC("number expected"));
481 p0 = (unsigned short)trex_parsenumber(exp);
482 /*******************************/
483 switch(*exp->_p) {
484 case '}':
485 p1 = p0; exp->_p++;
486 break;
487 case ',':
488 exp->_p++;
489 p1 = 0xFFFF;
490 if(isdigit(*exp->_p)){
491 p1 = (unsigned short)trex_parsenumber(exp);
492 }
493 trex_expect(exp,'}');
494 break;
495 default:
496 trex_error(exp,_SC(", or } expected"));
497 }
498 /*******************************/
499 isgreedy = TRex_True;
500 break;
502 }
503 if(isgreedy) {
504 int nnode = trex_newnode(exp,OP_GREEDY);
505 op = OP_GREEDY;
506 exp->_nodes[nnode].left = ret;
507 exp->_nodes[nnode].right = ((p0)<<16)|p1;
508 ret = nnode;
509 }
510 }
511 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')) {
512 int nnode = trex_element(exp);
513 exp->_nodes[ret].next = nnode;
514 }
516 return ret;
517 }
519 static int trex_list(TRex *exp)
520 {
521 int ret=-1,e;
522 if(*exp->_p == TREX_SYMBOL_BEGINNING_OF_STRING) {
523 exp->_p++;
524 ret = trex_newnode(exp,OP_BOL);
525 }
526 e = trex_element(exp);
527 if(ret != -1) {
528 exp->_nodes[ret].next = e;
529 }
530 else ret = e;
532 if(*exp->_p == TREX_SYMBOL_BRANCH) {
533 int temp,tright;
534 exp->_p++;
535 temp = trex_newnode(exp,OP_OR);
536 exp->_nodes[temp].left = ret;
537 tright = trex_list(exp);
538 exp->_nodes[temp].right = tright;
539 ret = temp;
540 }
541 return ret;
542 }
544 static TRexBool trex_matchcclass(int cclass,TRexChar c)
545 {
546 switch(cclass) {
547 case 'a': return isalpha(c)?TRex_True:TRex_False;
548 case 'A': return !isalpha(c)?TRex_True:TRex_False;
549 case 'w': return (isalnum(c) || c == '_')?TRex_True:TRex_False;
550 case 'W': return (!isalnum(c) && c != '_')?TRex_True:TRex_False;
551 case 's': return isspace(c)?TRex_True:TRex_False;
552 case 'S': return !isspace(c)?TRex_True:TRex_False;
553 case 'd': return isdigit(c)?TRex_True:TRex_False;
554 case 'D': return !isdigit(c)?TRex_True:TRex_False;
555 case 'x': return isxdigit(c)?TRex_True:TRex_False;
556 case 'X': return !isxdigit(c)?TRex_True:TRex_False;
557 case 'c': return iscntrl(c)?TRex_True:TRex_False;
558 case 'C': return !iscntrl(c)?TRex_True:TRex_False;
559 case 'p': return ispunct(c)?TRex_True:TRex_False;
560 case 'P': return !ispunct(c)?TRex_True:TRex_False;
561 case 'l': return islower(c)?TRex_True:TRex_False;
562 case 'u': return isupper(c)?TRex_True:TRex_False;
563 }
564 return TRex_False; /*cannot happen*/
565 }
567 static TRexBool trex_matchclass(TRex* exp,TRexNode *node,TRexChar c)
568 {
569 do {
570 switch(node->type) {
571 case OP_RANGE:
572 if(c >= node->left && c <= node->right) return TRex_True;
573 break;
574 case OP_CCLASS:
575 if(trex_matchcclass(node->left,c)) return TRex_True;
576 break;
577 default:
578 if(c == node->type)return TRex_True;
579 }
580 } while((node->next != -1) && (node = &exp->_nodes[node->next]));
581 return TRex_False;
582 }
584 static const TRexChar *trex_matchnode(TRex* exp,TRexNode *node,const TRexChar *str,TRexNode *next)
585 {
587 TRexNodeType type = node->type;
588 switch(type) {
589 case OP_GREEDY: {
590 //TRexNode *greedystop = (node->next != -1) ? &exp->_nodes[node->next] : NULL;
591 TRexNode *greedystop = NULL;
592 int p0 = (node->right >> 16)&0x0000FFFF, p1 = node->right&0x0000FFFF, nmaches = 0;
593 const TRexChar *s=str, *good = str;
595 if(node->next != -1) {
596 greedystop = &exp->_nodes[node->next];
597 }
598 else {
599 greedystop = next;
600 }
602 while((nmaches == 0xFFFF || nmaches < p1)) {
604 const TRexChar *stop;
605 if(!(s = trex_matchnode(exp,&exp->_nodes[node->left],s,greedystop)))
606 break;
607 nmaches++;
608 good=s;
609 if(greedystop) {
610 //checks that 0 matches satisfy the expression(if so skips)
611 //if not would always stop(for instance if is a '?')
612 if(greedystop->type != OP_GREEDY ||
613 (greedystop->type == OP_GREEDY && ((greedystop->right >> 16)&0x0000FFFF) != 0))
614 {
615 TRexNode *gnext = NULL;
616 if(greedystop->next != -1) {
617 gnext = &exp->_nodes[greedystop->next];
618 }else if(next && next->next != -1){
619 gnext = &exp->_nodes[next->next];
620 }
621 stop = trex_matchnode(exp,greedystop,s,gnext);
622 if(stop) {
623 //if satisfied stop it
624 if(p0 == p1 && p0 == nmaches) break;
625 else if(nmaches >= p0 && p1 == 0xFFFF) break;
626 else if(nmaches >= p0 && nmaches <= p1) break;
627 }
628 }
629 }
631 if(s >= exp->_eol)
632 break;
633 }
634 if(p0 == p1 && p0 == nmaches) return good;
635 else if(nmaches >= p0 && p1 == 0xFFFF) return good;
636 else if(nmaches >= p0 && nmaches <= p1) return good;
637 return NULL;
638 }
639 case OP_OR: {
640 const TRexChar *asd = str;
641 TRexNode *temp=&exp->_nodes[node->left];
642 while( (asd = trex_matchnode(exp,temp,asd,NULL)) ) {
643 if(temp->next != -1)
644 temp = &exp->_nodes[temp->next];
645 else
646 return asd;
647 }
648 asd = str;
649 temp = &exp->_nodes[node->right];
650 while( (asd = trex_matchnode(exp,temp,asd,NULL)) ) {
651 if(temp->next != -1)
652 temp = &exp->_nodes[temp->next];
653 else
654 return asd;
655 }
656 return NULL;
657 break;
658 }
659 case OP_EXPR:
660 case OP_NOCAPEXPR:{
661 TRexNode *n = &exp->_nodes[node->left];
662 const TRexChar *cur = str;
663 int capture = -1;
664 if(node->type != OP_NOCAPEXPR && node->right == exp->_currsubexp) {
665 capture = exp->_currsubexp;
666 exp->_matches[capture].begin = cur;
667 exp->_currsubexp++;
668 }
670 do {
671 TRexNode *subnext = NULL;
672 if(n->next != -1) {
673 subnext = &exp->_nodes[n->next];
674 }else {
675 subnext = next;
676 }
677 if(!(cur = trex_matchnode(exp,n,cur,subnext))) {
678 if(capture != -1){
679 exp->_matches[capture].begin = 0;
680 exp->_matches[capture].len = 0;
681 }
682 return NULL;
683 }
684 } while((n->next != -1) && (n = &exp->_nodes[n->next]));
686 if(capture != -1)
687 exp->_matches[capture].len = cur - exp->_matches[capture].begin;
688 return cur;
689 }
690 case OP_WB:
691 if(str == exp->_bol && !isspace(*str)
692 || (str == exp->_eol && !isspace(*(str-1)))
693 || (!isspace(*str) && isspace(*(str+1)))
694 || (isspace(*str) && !isspace(*(str+1))) ) {
695 return (node->left == 'b')?str:NULL;
696 }
697 return (node->left == 'b')?NULL:str;
698 case OP_BOL:
699 if(str == exp->_bol) return str;
700 return NULL;
701 case OP_EOL:
702 if(str == exp->_eol) return str;
703 return NULL;
704 case OP_DOT:{
705 *str++;
706 }
707 return str;
708 case OP_NCLASS:
709 case OP_CLASS:
710 if(trex_matchclass(exp,&exp->_nodes[node->left],*str)?(type == OP_CLASS?TRex_True:TRex_False):(type == OP_NCLASS?TRex_True:TRex_False)) {
711 *str++;
712 return str;
713 }
714 return NULL;
715 case OP_CCLASS:
716 if(trex_matchcclass(node->left,*str)) {
717 *str++;
718 return str;
719 }
720 return NULL;
721 default: /* char */
722 if(*str != node->type) return NULL;
723 *str++;
724 return str;
725 }
726 return NULL;
727 }
729 /* public api */
730 TRex *trex_compile(const TRexChar *pattern,const TRexChar **error)
731 {
732 TRex *exp = (TRex *)malloc(sizeof(TRex));
733 exp->_eol = exp->_bol = NULL;
734 exp->_p = pattern;
735 exp->_nallocated = (int)scstrlen(pattern) * sizeof(TRexChar);
736 exp->_nodes = (TRexNode *)malloc(exp->_nallocated * sizeof(TRexNode));
737 exp->_nsize = 0;
738 exp->_matches = 0;
739 exp->_nsubexpr = 0;
740 exp->_first = trex_newnode(exp,OP_EXPR);
741 exp->_error = error;
742 exp->_jmpbuf = malloc(sizeof(jmp_buf));
743 if(setjmp(*((jmp_buf*)exp->_jmpbuf)) == 0) {
744 int res = trex_list(exp);
745 exp->_nodes[exp->_first].left = res;
746 if(*exp->_p!='\0')
747 trex_error(exp,_SC("unexpected character"));
748 #ifdef _DEBUG
749 {
750 int nsize,i;
751 TRexNode *t;
752 nsize = exp->_nsize;
753 t = &exp->_nodes[0];
754 scprintf(_SC("\n"));
755 for(i = 0;i < nsize; i++) {
756 if(exp->_nodes[i].type>MAX_CHAR)
757 scprintf(_SC("[%02d] %10s "),i,g_nnames[exp->_nodes[i].type-MAX_CHAR]);
758 else
759 scprintf(_SC("[%02d] %10c "),i,exp->_nodes[i].type);
760 scprintf(_SC("left %02d right %02d next %02d\n"),exp->_nodes[i].left,exp->_nodes[i].right,exp->_nodes[i].next);
761 }
762 scprintf(_SC("\n"));
763 }
764 #endif
765 exp->_matches = (TRexMatch *) malloc(exp->_nsubexpr * sizeof(TRexMatch));
766 memset(exp->_matches,0,exp->_nsubexpr * sizeof(TRexMatch));
767 }
768 else{
769 trex_free(exp);
770 return NULL;
771 }
772 return exp;
773 }
775 void trex_free(TRex *exp)
776 {
777 if(exp) {
778 if(exp->_nodes) free(exp->_nodes);
779 if(exp->_jmpbuf) free(exp->_jmpbuf);
780 if(exp->_matches) free(exp->_matches);
781 free(exp);
782 }
783 }
785 TRexBool trex_match(TRex* exp,const TRexChar* text)
786 {
787 const TRexChar* res = NULL;
788 exp->_bol = text;
789 exp->_eol = text + scstrlen(text);
790 exp->_currsubexp = 0;
791 res = trex_matchnode(exp,exp->_nodes,text,NULL);
792 if(res == NULL || res != exp->_eol)
793 return TRex_False;
794 return TRex_True;
795 }
797 TRexBool trex_searchrange(TRex* exp,const TRexChar* text_begin,const TRexChar* text_end,const TRexChar** out_begin, const TRexChar** out_end)
798 {
799 const TRexChar *cur = NULL;
800 int node = exp->_first;
801 if(text_begin >= text_end) return TRex_False;
802 exp->_bol = text_begin;
803 exp->_eol = text_end;
804 do {
805 cur = text_begin;
806 while(node != -1) {
807 exp->_currsubexp = 0;
808 cur = trex_matchnode(exp,&exp->_nodes[node],cur,NULL);
809 if(!cur)
810 break;
811 node = exp->_nodes[node].next;
812 }
813 *text_begin++;
814 } while(cur == NULL && text_begin != text_end);
816 if(cur == NULL)
817 return TRex_False;
819 --text_begin;
821 if(out_begin) *out_begin = text_begin;
822 if(out_end) *out_end = cur;
823 return TRex_True;
824 }
826 TRexBool trex_search(TRex* exp,const TRexChar* text, const TRexChar** out_begin, const TRexChar** out_end)
827 {
828 return trex_searchrange(exp,text,text + scstrlen(text),out_begin,out_end);
829 }
831 int trex_getsubexpcount(TRex* exp)
832 {
833 return exp->_nsubexpr;
834 }
836 TRexBool trex_getsubexp(TRex* exp, int n, TRexMatch *subexp)
837 {
838 if( n<0 || n >= exp->_nsubexpr) return TRex_False;
839 *subexp = exp->_matches[n];
840 return TRex_True;
841 }
844 //########################################################################
845 //########################################################################
846 //## E N D R E G E X P
847 //########################################################################
848 //########################################################################
854 //########################################################################
855 //########################################################################
856 //## X M L
857 //########################################################################
858 //########################################################################
860 // Note: This mini-dom library comes from Pedro, another little project
861 // of mine.
863 typedef std::string String;
864 typedef unsigned int XMLCh;
867 class Namespace
868 {
869 public:
870 Namespace()
871 {}
873 Namespace(const String &prefixArg, const String &namespaceURIArg)
874 {
875 prefix = prefixArg;
876 namespaceURI = namespaceURIArg;
877 }
879 Namespace(const Namespace &other)
880 {
881 assign(other);
882 }
884 Namespace &operator=(const Namespace &other)
885 {
886 assign(other);
887 return *this;
888 }
890 virtual ~Namespace()
891 {}
893 virtual String getPrefix()
894 { return prefix; }
896 virtual String getNamespaceURI()
897 { return namespaceURI; }
899 protected:
901 void assign(const Namespace &other)
902 {
903 prefix = other.prefix;
904 namespaceURI = other.namespaceURI;
905 }
907 String prefix;
908 String namespaceURI;
910 };
912 class Attribute
913 {
914 public:
915 Attribute()
916 {}
918 Attribute(const String &nameArg, const String &valueArg)
919 {
920 name = nameArg;
921 value = valueArg;
922 }
924 Attribute(const Attribute &other)
925 {
926 assign(other);
927 }
929 Attribute &operator=(const Attribute &other)
930 {
931 assign(other);
932 return *this;
933 }
935 virtual ~Attribute()
936 {}
938 virtual String getName()
939 { return name; }
941 virtual String getValue()
942 { return value; }
944 protected:
946 void assign(const Attribute &other)
947 {
948 name = other.name;
949 value = other.value;
950 }
952 String name;
953 String value;
955 };
958 class Element
959 {
960 friend class Parser;
962 public:
963 Element()
964 {
965 init();
966 }
968 Element(const String &nameArg)
969 {
970 init();
971 name = nameArg;
972 }
974 Element(const String &nameArg, const String &valueArg)
975 {
976 init();
977 name = nameArg;
978 value = valueArg;
979 }
981 Element(const Element &other)
982 {
983 assign(other);
984 }
986 Element &operator=(const Element &other)
987 {
988 assign(other);
989 return *this;
990 }
992 virtual Element *clone();
994 virtual ~Element()
995 {
996 for (unsigned int i=0 ; i<children.size() ; i++)
997 delete children[i];
998 }
1000 virtual String getName()
1001 { return name; }
1003 virtual String getValue()
1004 { return value; }
1006 Element *getParent()
1007 { return parent; }
1009 std::vector<Element *> getChildren()
1010 { return children; }
1012 std::vector<Element *> findElements(const String &name);
1014 String getAttribute(const String &name);
1016 std::vector<Attribute> &getAttributes()
1017 { return attributes; }
1019 String getTagAttribute(const String &tagName, const String &attrName);
1021 String getTagValue(const String &tagName);
1023 void addChild(Element *child);
1025 void addAttribute(const String &name, const String &value);
1027 void addNamespace(const String &prefix, const String &namespaceURI);
1030 /**
1031 * Prettyprint an XML tree to an output stream. Elements are indented
1032 * according to element hierarchy.
1033 * @param f a stream to receive the output
1034 * @param elem the element to output
1035 */
1036 void writeIndented(FILE *f);
1038 /**
1039 * Prettyprint an XML tree to standard output. This is the equivalent of
1040 * writeIndented(stdout).
1041 * @param elem the element to output
1042 */
1043 void print();
1045 int getLine()
1046 { return line; }
1048 protected:
1050 void init()
1051 {
1052 parent = NULL;
1053 line = 0;
1054 }
1056 void assign(const Element &other)
1057 {
1058 parent = other.parent;
1059 children = other.children;
1060 attributes = other.attributes;
1061 namespaces = other.namespaces;
1062 name = other.name;
1063 value = other.value;
1064 line = other.line;
1065 }
1067 void findElementsRecursive(std::vector<Element *>&res, const String &name);
1069 void writeIndentedRecursive(FILE *f, int indent);
1071 Element *parent;
1073 std::vector<Element *>children;
1075 std::vector<Attribute> attributes;
1076 std::vector<Namespace> namespaces;
1078 String name;
1079 String value;
1081 int line;
1082 };
1088 class Parser
1089 {
1090 public:
1091 /**
1092 * Constructor
1093 */
1094 Parser()
1095 { init(); }
1097 virtual ~Parser()
1098 {}
1100 /**
1101 * Parse XML in a char buffer.
1102 * @param buf a character buffer to parse
1103 * @param pos position to start parsing
1104 * @param len number of chars, from pos, to parse.
1105 * @return a pointer to the root of the XML document;
1106 */
1107 Element *parse(const char *buf,int pos,int len);
1109 /**
1110 * Parse XML in a char buffer.
1111 * @param buf a character buffer to parse
1112 * @param pos position to start parsing
1113 * @param len number of chars, from pos, to parse.
1114 * @return a pointer to the root of the XML document;
1115 */
1116 Element *parse(const String &buf);
1118 /**
1119 * Parse a named XML file. The file is loaded like a data file;
1120 * the original format is not preserved.
1121 * @param fileName the name of the file to read
1122 * @return a pointer to the root of the XML document;
1123 */
1124 Element *parseFile(const String &fileName);
1126 /**
1127 * Utility method to preprocess a string for XML
1128 * output, escaping its entities.
1129 * @param str the string to encode
1130 */
1131 static String encode(const String &str);
1133 /**
1134 * Removes whitespace from beginning and end of a string
1135 */
1136 String trim(const String &s);
1138 private:
1140 void init()
1141 {
1142 keepGoing = true;
1143 currentNode = NULL;
1144 parselen = 0;
1145 parsebuf = NULL;
1146 currentPosition = 0;
1147 }
1149 int countLines(int begin, int end);
1151 void getLineAndColumn(int pos, int *lineNr, int *colNr);
1153 void error(const char *fmt, ...);
1155 int peek(int pos);
1157 int match(int pos, const char *text);
1159 int skipwhite(int p);
1161 int getWord(int p0, String &buf);
1163 int getQuoted(int p0, String &buf, int do_i_parse);
1165 int parseVersion(int p0);
1167 int parseDoctype(int p0);
1169 int parseElement(int p0, Element *par,int depth);
1171 Element *parse(XMLCh *buf,int pos,int len);
1173 bool keepGoing;
1174 Element *currentNode;
1175 int parselen;
1176 XMLCh *parsebuf;
1177 String cdatabuf;
1178 int currentPosition;
1179 };
1184 //########################################################################
1185 //# E L E M E N T
1186 //########################################################################
1188 Element *Element::clone()
1189 {
1190 Element *elem = new Element(name, value);
1191 elem->parent = parent;
1192 elem->attributes = attributes;
1193 elem->namespaces = namespaces;
1194 elem->line = line;
1196 std::vector<Element *>::iterator iter;
1197 for (iter = children.begin(); iter != children.end() ; iter++)
1198 {
1199 elem->addChild((*iter)->clone());
1200 }
1201 return elem;
1202 }
1205 void Element::findElementsRecursive(std::vector<Element *>&res, const String &name)
1206 {
1207 if (getName() == name)
1208 {
1209 res.push_back(this);
1210 }
1211 for (unsigned int i=0; i<children.size() ; i++)
1212 children[i]->findElementsRecursive(res, name);
1213 }
1215 std::vector<Element *> Element::findElements(const String &name)
1216 {
1217 std::vector<Element *> res;
1218 findElementsRecursive(res, name);
1219 return res;
1220 }
1222 String Element::getAttribute(const String &name)
1223 {
1224 for (unsigned int i=0 ; i<attributes.size() ; i++)
1225 if (attributes[i].getName() ==name)
1226 return attributes[i].getValue();
1227 return "";
1228 }
1230 String Element::getTagAttribute(const String &tagName, const String &attrName)
1231 {
1232 std::vector<Element *>elems = findElements(tagName);
1233 if (elems.size() <1)
1234 return "";
1235 String res = elems[0]->getAttribute(attrName);
1236 return res;
1237 }
1239 String Element::getTagValue(const String &tagName)
1240 {
1241 std::vector<Element *>elems = findElements(tagName);
1242 if (elems.size() <1)
1243 return "";
1244 String res = elems[0]->getValue();
1245 return res;
1246 }
1248 void Element::addChild(Element *child)
1249 {
1250 if (!child)
1251 return;
1252 child->parent = this;
1253 children.push_back(child);
1254 }
1257 void Element::addAttribute(const String &name, const String &value)
1258 {
1259 Attribute attr(name, value);
1260 attributes.push_back(attr);
1261 }
1263 void Element::addNamespace(const String &prefix, const String &namespaceURI)
1264 {
1265 Namespace ns(prefix, namespaceURI);
1266 namespaces.push_back(ns);
1267 }
1269 void Element::writeIndentedRecursive(FILE *f, int indent)
1270 {
1271 int i;
1272 if (!f)
1273 return;
1274 //Opening tag, and attributes
1275 for (i=0;i<indent;i++)
1276 fputc(' ',f);
1277 fprintf(f,"<%s",name.c_str());
1278 for (unsigned int i=0 ; i<attributes.size() ; i++)
1279 {
1280 fprintf(f," %s=\"%s\"",
1281 attributes[i].getName().c_str(),
1282 attributes[i].getValue().c_str());
1283 }
1284 for (unsigned int i=0 ; i<namespaces.size() ; i++)
1285 {
1286 fprintf(f," xmlns:%s=\"%s\"",
1287 namespaces[i].getPrefix().c_str(),
1288 namespaces[i].getNamespaceURI().c_str());
1289 }
1290 fprintf(f,">\n");
1292 //Between the tags
1293 if (value.size() > 0)
1294 {
1295 for (int i=0;i<indent;i++)
1296 fputc(' ', f);
1297 fprintf(f," %s\n", value.c_str());
1298 }
1300 for (unsigned int i=0 ; i<children.size() ; i++)
1301 children[i]->writeIndentedRecursive(f, indent+2);
1303 //Closing tag
1304 for (int i=0; i<indent; i++)
1305 fputc(' ',f);
1306 fprintf(f,"</%s>\n", name.c_str());
1307 }
1309 void Element::writeIndented(FILE *f)
1310 {
1311 writeIndentedRecursive(f, 0);
1312 }
1314 void Element::print()
1315 {
1316 writeIndented(stdout);
1317 }
1320 //########################################################################
1321 //# P A R S E R
1322 //########################################################################
1326 typedef struct
1327 {
1328 const char *escaped;
1329 char value;
1330 } EntityEntry;
1332 static EntityEntry entities[] =
1333 {
1334 { "&" , '&' },
1335 { "<" , '<' },
1336 { ">" , '>' },
1337 { "'", '\'' },
1338 { """, '"' },
1339 { NULL , '\0' }
1340 };
1344 /**
1345 * Removes whitespace from beginning and end of a string
1346 */
1347 String Parser::trim(const String &s)
1348 {
1349 if (s.size() < 1)
1350 return s;
1352 //Find first non-ws char
1353 unsigned int begin = 0;
1354 for ( ; begin < s.size() ; begin++)
1355 {
1356 if (!isspace(s[begin]))
1357 break;
1358 }
1360 //Find first non-ws char, going in reverse
1361 unsigned int end = s.size() - 1;
1362 for ( ; end > begin ; end--)
1363 {
1364 if (!isspace(s[end]))
1365 break;
1366 }
1367 //trace("begin:%d end:%d", begin, end);
1369 String res = s.substr(begin, end-begin+1);
1370 return res;
1371 }
1374 int Parser::countLines(int begin, int end)
1375 {
1376 int count = 0;
1377 for (int i=begin ; i<end ; i++)
1378 {
1379 XMLCh ch = parsebuf[i];
1380 if (ch == '\n' || ch == '\r')
1381 count++;
1382 }
1383 return count;
1384 }
1387 void Parser::getLineAndColumn(int pos, int *lineNr, int *colNr)
1388 {
1389 int line = 1;
1390 int col = 1;
1391 for (long i=0 ; i<pos ; i++)
1392 {
1393 XMLCh ch = parsebuf[i];
1394 if (ch == '\n' || ch == '\r')
1395 {
1396 col = 0;
1397 line ++;
1398 }
1399 else
1400 col++;
1401 }
1402 *lineNr = line;
1403 *colNr = col;
1405 }
1408 void Parser::error(const char *fmt, ...)
1409 {
1410 int lineNr;
1411 int colNr;
1412 getLineAndColumn(currentPosition, &lineNr, &colNr);
1413 va_list args;
1414 fprintf(stderr, "xml error at line %d, column %d:", lineNr, colNr);
1415 va_start(args,fmt);
1416 vfprintf(stderr,fmt,args);
1417 va_end(args) ;
1418 fprintf(stderr, "\n");
1419 }
1423 int Parser::peek(int pos)
1424 {
1425 if (pos >= parselen)
1426 return -1;
1427 currentPosition = pos;
1428 int ch = parsebuf[pos];
1429 //printf("ch:%c\n", ch);
1430 return ch;
1431 }
1435 String Parser::encode(const String &str)
1436 {
1437 String ret;
1438 for (unsigned int i=0 ; i<str.size() ; i++)
1439 {
1440 XMLCh ch = (XMLCh)str[i];
1441 if (ch == '&')
1442 ret.append("&");
1443 else 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
1452 ret.push_back(ch);
1454 }
1455 return ret;
1456 }
1459 int Parser::match(int p0, const char *text)
1460 {
1461 int p = p0;
1462 while (*text)
1463 {
1464 if (peek(p) != *text)
1465 return p0;
1466 p++; text++;
1467 }
1468 return p;
1469 }
1473 int Parser::skipwhite(int p)
1474 {
1476 while (p<parselen)
1477 {
1478 int p2 = match(p, "<!--");
1479 if (p2 > p)
1480 {
1481 p = p2;
1482 while (p<parselen)
1483 {
1484 p2 = match(p, "-->");
1485 if (p2 > p)
1486 {
1487 p = p2;
1488 break;
1489 }
1490 p++;
1491 }
1492 }
1493 XMLCh b = peek(p);
1494 if (!isspace(b))
1495 break;
1496 p++;
1497 }
1498 return p;
1499 }
1501 /* modify this to allow all chars for an element or attribute name*/
1502 int Parser::getWord(int p0, String &buf)
1503 {
1504 int p = p0;
1505 while (p<parselen)
1506 {
1507 XMLCh b = peek(p);
1508 if (b<=' ' || b=='/' || b=='>' || b=='=')
1509 break;
1510 buf.push_back(b);
1511 p++;
1512 }
1513 return p;
1514 }
1516 int Parser::getQuoted(int p0, String &buf, int do_i_parse)
1517 {
1519 int p = p0;
1520 if (peek(p) != '"' && peek(p) != '\'')
1521 return p0;
1522 p++;
1524 while ( p<parselen )
1525 {
1526 XMLCh b = peek(p);
1527 if (b=='"' || b=='\'')
1528 break;
1529 if (b=='&' && do_i_parse)
1530 {
1531 bool found = false;
1532 for (EntityEntry *ee = entities ; ee->value ; ee++)
1533 {
1534 int p2 = match(p, ee->escaped);
1535 if (p2>p)
1536 {
1537 buf.push_back(ee->value);
1538 p = p2;
1539 found = true;
1540 break;
1541 }
1542 }
1543 if (!found)
1544 {
1545 error("unterminated entity");
1546 return false;
1547 }
1548 }
1549 else
1550 {
1551 buf.push_back(b);
1552 p++;
1553 }
1554 }
1555 return p;
1556 }
1558 int Parser::parseVersion(int p0)
1559 {
1560 //printf("### parseVersion: %d\n", p0);
1562 int p = p0;
1564 p = skipwhite(p0);
1566 if (peek(p) != '<')
1567 return p0;
1569 p++;
1570 if (p>=parselen || peek(p)!='?')
1571 return p0;
1573 p++;
1575 String buf;
1577 while (p<parselen)
1578 {
1579 XMLCh ch = peek(p);
1580 if (ch=='?')
1581 {
1582 p++;
1583 break;
1584 }
1585 buf.push_back(ch);
1586 p++;
1587 }
1589 if (peek(p) != '>')
1590 return p0;
1591 p++;
1593 //printf("Got version:%s\n",buf.c_str());
1594 return p;
1595 }
1597 int Parser::parseDoctype(int p0)
1598 {
1599 //printf("### parseDoctype: %d\n", p0);
1601 int p = p0;
1602 p = skipwhite(p);
1604 if (p>=parselen || peek(p)!='<')
1605 return p0;
1607 p++;
1609 if (peek(p)!='!' || peek(p+1)=='-')
1610 return p0;
1611 p++;
1613 String buf;
1614 while (p<parselen)
1615 {
1616 XMLCh ch = peek(p);
1617 if (ch=='>')
1618 {
1619 p++;
1620 break;
1621 }
1622 buf.push_back(ch);
1623 p++;
1624 }
1626 //printf("Got doctype:%s\n",buf.c_str());
1627 return p;
1628 }
1632 int Parser::parseElement(int p0, Element *par,int lineNr)
1633 {
1635 int p = p0;
1637 int p2 = p;
1639 p = skipwhite(p);
1641 //## Get open tag
1642 XMLCh ch = peek(p);
1643 if (ch!='<')
1644 return p0;
1646 //int line, col;
1647 //getLineAndColumn(p, &line, &col);
1649 p++;
1651 String openTagName;
1652 p = skipwhite(p);
1653 p = getWord(p, openTagName);
1654 //printf("####tag :%s\n", openTagName.c_str());
1655 p = skipwhite(p);
1657 //Add element to tree
1658 Element *n = new Element(openTagName);
1659 n->line = lineNr + countLines(p0, p);
1660 n->parent = par;
1661 par->addChild(n);
1663 // Get attributes
1664 if (peek(p) != '>')
1665 {
1666 while (p<parselen)
1667 {
1668 p = skipwhite(p);
1669 ch = peek(p);
1670 //printf("ch:%c\n",ch);
1671 if (ch=='>')
1672 break;
1673 else if (ch=='/' && p<parselen+1)
1674 {
1675 p++;
1676 p = skipwhite(p);
1677 ch = peek(p);
1678 if (ch=='>')
1679 {
1680 p++;
1681 //printf("quick close\n");
1682 return p;
1683 }
1684 }
1685 String attrName;
1686 p2 = getWord(p, attrName);
1687 if (p2==p)
1688 break;
1689 //printf("name:%s",buf);
1690 p=p2;
1691 p = skipwhite(p);
1692 ch = peek(p);
1693 //printf("ch:%c\n",ch);
1694 if (ch!='=')
1695 break;
1696 p++;
1697 p = skipwhite(p);
1698 // ch = parsebuf[p];
1699 // printf("ch:%c\n",ch);
1700 String attrVal;
1701 p2 = getQuoted(p, attrVal, true);
1702 p=p2+1;
1703 //printf("name:'%s' value:'%s'\n",attrName.c_str(),attrVal.c_str());
1704 char *namestr = (char *)attrName.c_str();
1705 if (strncmp(namestr, "xmlns:", 6)==0)
1706 n->addNamespace(attrName, attrVal);
1707 else
1708 n->addAttribute(attrName, attrVal);
1709 }
1710 }
1712 bool cdata = false;
1714 p++;
1715 // ### Get intervening data ### */
1716 String data;
1717 while (p<parselen)
1718 {
1719 //# COMMENT
1720 p2 = match(p, "<!--");
1721 if (!cdata && p2>p)
1722 {
1723 p = p2;
1724 while (p<parselen)
1725 {
1726 p2 = match(p, "-->");
1727 if (p2 > p)
1728 {
1729 p = p2;
1730 break;
1731 }
1732 p++;
1733 }
1734 }
1736 ch = peek(p);
1737 //# END TAG
1738 if (ch=='<' && !cdata && peek(p+1)=='/')
1739 {
1740 break;
1741 }
1742 //# CDATA
1743 p2 = match(p, "<![CDATA[");
1744 if (p2 > p)
1745 {
1746 cdata = true;
1747 p = p2;
1748 continue;
1749 }
1751 //# CHILD ELEMENT
1752 if (ch == '<')
1753 {
1754 p2 = parseElement(p, n, lineNr + countLines(p0, p));
1755 if (p2 == p)
1756 {
1757 /*
1758 printf("problem on element:%s. p2:%d p:%d\n",
1759 openTagName.c_str(), p2, p);
1760 */
1761 return p0;
1762 }
1763 p = p2;
1764 continue;
1765 }
1766 //# ENTITY
1767 if (ch=='&' && !cdata)
1768 {
1769 bool found = false;
1770 for (EntityEntry *ee = entities ; ee->value ; ee++)
1771 {
1772 int p2 = match(p, ee->escaped);
1773 if (p2>p)
1774 {
1775 data.push_back(ee->value);
1776 p = p2;
1777 found = true;
1778 break;
1779 }
1780 }
1781 if (!found)
1782 {
1783 error("unterminated entity");
1784 return -1;
1785 }
1786 continue;
1787 }
1789 //# NONE OF THE ABOVE
1790 data.push_back(ch);
1791 p++;
1792 }/*while*/
1795 n->value = data;
1796 //printf("%d : data:%s\n",p,data.c_str());
1798 //## Get close tag
1799 p = skipwhite(p);
1800 ch = peek(p);
1801 if (ch != '<')
1802 {
1803 error("no < for end tag\n");
1804 return p0;
1805 }
1806 p++;
1807 ch = peek(p);
1808 if (ch != '/')
1809 {
1810 error("no / on end tag");
1811 return p0;
1812 }
1813 p++;
1814 ch = peek(p);
1815 p = skipwhite(p);
1816 String closeTagName;
1817 p = getWord(p, closeTagName);
1818 if (openTagName != closeTagName)
1819 {
1820 error("Mismatched closing tag. Expected </%S>. Got '%S'.",
1821 openTagName.c_str(), closeTagName.c_str());
1822 return p0;
1823 }
1824 p = skipwhite(p);
1825 if (peek(p) != '>')
1826 {
1827 error("no > on end tag for '%s'", closeTagName.c_str());
1828 return p0;
1829 }
1830 p++;
1831 // printf("close element:%s\n",closeTagName.c_str());
1832 p = skipwhite(p);
1833 return p;
1834 }
1839 Element *Parser::parse(XMLCh *buf,int pos,int len)
1840 {
1841 parselen = len;
1842 parsebuf = buf;
1843 Element *rootNode = new Element("root");
1844 pos = parseVersion(pos);
1845 pos = parseDoctype(pos);
1846 pos = parseElement(pos, rootNode, 1);
1847 return rootNode;
1848 }
1851 Element *Parser::parse(const char *buf, int pos, int len)
1852 {
1853 XMLCh *charbuf = new XMLCh[len + 1];
1854 long i = 0;
1855 for ( ; i < len ; i++)
1856 charbuf[i] = (XMLCh)buf[i];
1857 charbuf[i] = '\0';
1859 Element *n = parse(charbuf, pos, len);
1860 delete[] charbuf;
1861 return n;
1862 }
1864 Element *Parser::parse(const String &buf)
1865 {
1866 long len = (long)buf.size();
1867 XMLCh *charbuf = new XMLCh[len + 1];
1868 long i = 0;
1869 for ( ; i < len ; i++)
1870 charbuf[i] = (XMLCh)buf[i];
1871 charbuf[i] = '\0';
1873 Element *n = parse(charbuf, 0, len);
1874 delete[] charbuf;
1875 return n;
1876 }
1878 Element *Parser::parseFile(const String &fileName)
1879 {
1881 //##### LOAD INTO A CHAR BUF, THEN CONVERT TO XMLCh
1882 FILE *f = fopen(fileName.c_str(), "rb");
1883 if (!f)
1884 return NULL;
1886 struct stat statBuf;
1887 if (fstat(fileno(f),&statBuf)<0)
1888 {
1889 fclose(f);
1890 return NULL;
1891 }
1892 long filelen = statBuf.st_size;
1894 //printf("length:%d\n",filelen);
1895 XMLCh *charbuf = new XMLCh[filelen + 1];
1896 for (XMLCh *p=charbuf ; !feof(f) ; p++)
1897 {
1898 *p = (XMLCh)fgetc(f);
1899 }
1900 fclose(f);
1901 charbuf[filelen] = '\0';
1904 /*
1905 printf("nrbytes:%d\n",wc_count);
1906 printf("buf:%ls\n======\n",charbuf);
1907 */
1908 Element *n = parse(charbuf, 0, filelen);
1909 delete[] charbuf;
1910 return n;
1911 }
1913 //########################################################################
1914 //########################################################################
1915 //## E N D X M L
1916 //########################################################################
1917 //########################################################################
1924 //########################################################################
1925 //########################################################################
1926 //## U R I
1927 //########################################################################
1928 //########################################################################
1930 //This would normally be a call to a UNICODE function
1931 #define isLetter(x) isalpha(x)
1933 /**
1934 * A class that implements the W3C URI resource reference.
1935 */
1936 class URI
1937 {
1938 public:
1940 typedef enum
1941 {
1942 SCHEME_NONE =0,
1943 SCHEME_DATA,
1944 SCHEME_HTTP,
1945 SCHEME_HTTPS,
1946 SCHEME_FTP,
1947 SCHEME_FILE,
1948 SCHEME_LDAP,
1949 SCHEME_MAILTO,
1950 SCHEME_NEWS,
1951 SCHEME_TELNET
1952 } SchemeTypes;
1954 /**
1955 *
1956 */
1957 URI()
1958 {
1959 init();
1960 }
1962 /**
1963 *
1964 */
1965 URI(const String &str)
1966 {
1967 init();
1968 parse(str);
1969 }
1972 /**
1973 *
1974 */
1975 URI(const char *str)
1976 {
1977 init();
1978 String domStr = str;
1979 parse(domStr);
1980 }
1983 /**
1984 *
1985 */
1986 URI(const URI &other)
1987 {
1988 init();
1989 assign(other);
1990 }
1993 /**
1994 *
1995 */
1996 URI &operator=(const URI &other)
1997 {
1998 init();
1999 assign(other);
2000 return *this;
2001 }
2004 /**
2005 *
2006 */
2007 virtual ~URI()
2008 {}
2012 /**
2013 *
2014 */
2015 virtual bool parse(const String &str);
2017 /**
2018 *
2019 */
2020 virtual String toString() const;
2022 /**
2023 *
2024 */
2025 virtual int getScheme() const;
2027 /**
2028 *
2029 */
2030 virtual String getSchemeStr() const;
2032 /**
2033 *
2034 */
2035 virtual String getAuthority() const;
2037 /**
2038 * Same as getAuthority, but if the port has been specified
2039 * as host:port , the port will not be included
2040 */
2041 virtual String getHost() const;
2043 /**
2044 *
2045 */
2046 virtual int getPort() const;
2048 /**
2049 *
2050 */
2051 virtual String getPath() const;
2053 /**
2054 *
2055 */
2056 virtual String getNativePath() const;
2058 /**
2059 *
2060 */
2061 virtual bool isAbsolute() const;
2063 /**
2064 *
2065 */
2066 virtual bool isOpaque() const;
2068 /**
2069 *
2070 */
2071 virtual String getQuery() const;
2073 /**
2074 *
2075 */
2076 virtual String getFragment() const;
2078 /**
2079 *
2080 */
2081 virtual URI resolve(const URI &other) const;
2083 /**
2084 *
2085 */
2086 virtual void normalize();
2088 private:
2090 /**
2091 *
2092 */
2093 void init()
2094 {
2095 parsebuf = NULL;
2096 parselen = 0;
2097 scheme = SCHEME_NONE;
2098 schemeStr = "";
2099 port = 0;
2100 authority = "";
2101 path = "";
2102 absolute = false;
2103 opaque = false;
2104 query = "";
2105 fragment = "";
2106 }
2109 /**
2110 *
2111 */
2112 void assign(const URI &other)
2113 {
2114 scheme = other.scheme;
2115 schemeStr = other.schemeStr;
2116 authority = other.authority;
2117 port = other.port;
2118 path = other.path;
2119 absolute = other.absolute;
2120 opaque = other.opaque;
2121 query = other.query;
2122 fragment = other.fragment;
2123 }
2125 int scheme;
2127 String schemeStr;
2129 String authority;
2131 bool portSpecified;
2133 int port;
2135 String path;
2137 bool absolute;
2139 bool opaque;
2141 String query;
2143 String fragment;
2145 void error(const char *fmt, ...);
2147 void trace(const char *fmt, ...);
2150 int peek(int p);
2152 int match(int p, const char *key);
2154 int parseScheme(int p);
2156 int parseHierarchicalPart(int p0);
2158 int parseQuery(int p0);
2160 int parseFragment(int p0);
2162 int parse(int p);
2164 char *parsebuf;
2166 int parselen;
2168 };
2172 typedef struct
2173 {
2174 int ival;
2175 const char *sval;
2176 int port;
2177 } LookupEntry;
2179 LookupEntry schemes[] =
2180 {
2181 { URI::SCHEME_DATA, "data:", 0 },
2182 { URI::SCHEME_HTTP, "http:", 80 },
2183 { URI::SCHEME_HTTPS, "https:", 443 },
2184 { URI::SCHEME_FTP, "ftp", 12 },
2185 { URI::SCHEME_FILE, "file:", 0 },
2186 { URI::SCHEME_LDAP, "ldap:", 123 },
2187 { URI::SCHEME_MAILTO, "mailto:", 25 },
2188 { URI::SCHEME_NEWS, "news:", 117 },
2189 { URI::SCHEME_TELNET, "telnet:", 23 },
2190 { 0, NULL, 0 }
2191 };
2194 String URI::toString() const
2195 {
2196 String str = schemeStr;
2197 if (authority.size() > 0)
2198 {
2199 str.append("//");
2200 str.append(authority);
2201 }
2202 str.append(path);
2203 if (query.size() > 0)
2204 {
2205 str.append("?");
2206 str.append(query);
2207 }
2208 if (fragment.size() > 0)
2209 {
2210 str.append("#");
2211 str.append(fragment);
2212 }
2213 return str;
2214 }
2217 int URI::getScheme() const
2218 {
2219 return scheme;
2220 }
2222 String URI::getSchemeStr() const
2223 {
2224 return schemeStr;
2225 }
2228 String URI::getAuthority() const
2229 {
2230 String ret = authority;
2231 if (portSpecified && port>=0)
2232 {
2233 char buf[7];
2234 snprintf(buf, 6, ":%6d", port);
2235 ret.append(buf);
2236 }
2237 return ret;
2238 }
2240 String URI::getHost() const
2241 {
2242 return authority;
2243 }
2245 int URI::getPort() const
2246 {
2247 return port;
2248 }
2251 String URI::getPath() const
2252 {
2253 return path;
2254 }
2256 String URI::getNativePath() const
2257 {
2258 String npath;
2259 #ifdef __WIN32__
2260 unsigned int firstChar = 0;
2261 if (path.size() >= 3)
2262 {
2263 if (path[0] == '/' &&
2264 isLetter(path[1]) &&
2265 path[2] == ':')
2266 firstChar++;
2267 }
2268 for (unsigned int i=firstChar ; i<path.size() ; i++)
2269 {
2270 XMLCh ch = (XMLCh) path[i];
2271 if (ch == '/')
2272 npath.push_back((XMLCh)'\\');
2273 else
2274 npath.push_back(ch);
2275 }
2276 #else
2277 npath = path;
2278 #endif
2279 return npath;
2280 }
2283 bool URI::isAbsolute() const
2284 {
2285 return absolute;
2286 }
2288 bool URI::isOpaque() const
2289 {
2290 return opaque;
2291 }
2294 String URI::getQuery() const
2295 {
2296 return query;
2297 }
2300 String URI::getFragment() const
2301 {
2302 return fragment;
2303 }
2306 URI URI::resolve(const URI &other) const
2307 {
2308 //### According to w3c, this is handled in 3 cases
2310 //## 1
2311 if (opaque || other.isAbsolute())
2312 return other;
2314 //## 2
2315 if (other.fragment.size() > 0 &&
2316 other.path.size() == 0 &&
2317 other.scheme == SCHEME_NONE &&
2318 other.authority.size() == 0 &&
2319 other.query.size() == 0 )
2320 {
2321 URI fragUri = *this;
2322 fragUri.fragment = other.fragment;
2323 return fragUri;
2324 }
2326 //## 3 http://www.ietf.org/rfc/rfc2396.txt, section 5.2
2327 URI newUri;
2328 //# 3.1
2329 newUri.scheme = scheme;
2330 newUri.schemeStr = schemeStr;
2331 newUri.query = other.query;
2332 newUri.fragment = other.fragment;
2333 if (other.authority.size() > 0)
2334 {
2335 //# 3.2
2336 if (absolute || other.absolute)
2337 newUri.absolute = true;
2338 newUri.authority = other.authority;
2339 newUri.port = other.port;//part of authority
2340 newUri.path = other.path;
2341 }
2342 else
2343 {
2344 //# 3.3
2345 if (other.absolute)
2346 {
2347 newUri.absolute = true;
2348 newUri.path = other.path;
2349 }
2350 else
2351 {
2352 unsigned int pos = path.find_last_of('/');
2353 if (pos != path.npos)
2354 {
2355 String tpath = path.substr(0, pos+1);
2356 tpath.append(other.path);
2357 newUri.path = tpath;
2358 }
2359 else
2360 newUri.path = other.path;
2361 }
2362 }
2364 newUri.normalize();
2365 return newUri;
2366 }
2370 /**
2371 * This follows the Java URI algorithm:
2372 * 1. All "." segments are removed.
2373 * 2. If a ".." segment is preceded by a non-".." segment
2374 * then both of these segments are removed. This step
2375 * is repeated until it is no longer applicable.
2376 * 3. If the path is relative, and if its first segment
2377 * contains a colon character (':'), then a "." segment
2378 * is prepended. This prevents a relative URI with a path
2379 * such as "a:b/c/d" from later being re-parsed as an
2380 * opaque URI with a scheme of "a" and a scheme-specific
2381 * part of "b/c/d". (Deviation from RFC 2396)
2382 */
2383 void URI::normalize()
2384 {
2385 std::vector<String> segments;
2387 //## Collect segments
2388 if (path.size()<2)
2389 return;
2390 bool abs = false;
2391 unsigned int pos=0;
2392 if (path[0]=='/')
2393 {
2394 abs = true;
2395 pos++;
2396 }
2397 while (pos < path.size())
2398 {
2399 unsigned int pos2 = path.find('/', pos);
2400 if (pos2==path.npos)
2401 {
2402 String seg = path.substr(pos);
2403 //printf("last segment:%s\n", seg.c_str());
2404 segments.push_back(seg);
2405 break;
2406 }
2407 if (pos2>pos)
2408 {
2409 String seg = path.substr(pos, pos2-pos);
2410 //printf("segment:%s\n", seg.c_str());
2411 segments.push_back(seg);
2412 }
2413 pos = pos2;
2414 pos++;
2415 }
2417 //## Clean up (normalize) segments
2418 bool edited = false;
2419 std::vector<String>::iterator iter;
2420 for (iter=segments.begin() ; iter!=segments.end() ; )
2421 {
2422 String s = *iter;
2423 if (s == ".")
2424 {
2425 iter = segments.erase(iter);
2426 edited = true;
2427 }
2428 else if (s == ".." &&
2429 iter != segments.begin() &&
2430 *(iter-1) != "..")
2431 {
2432 iter--; //back up, then erase two entries
2433 iter = segments.erase(iter);
2434 iter = segments.erase(iter);
2435 edited = true;
2436 }
2437 else
2438 iter++;
2439 }
2441 //## Rebuild path, if necessary
2442 if (edited)
2443 {
2444 path.clear();
2445 if (abs)
2446 {
2447 path.append("/");
2448 }
2449 std::vector<String>::iterator iter;
2450 for (iter=segments.begin() ; iter!=segments.end() ; iter++)
2451 {
2452 if (iter != segments.begin())
2453 path.append("/");
2454 path.append(*iter);
2455 }
2456 }
2458 }
2462 //#########################################################################
2463 //# M E S S A G E S
2464 //#########################################################################
2466 void URI::error(const char *fmt, ...)
2467 {
2468 va_list args;
2469 fprintf(stderr, "URI error: ");
2470 va_start(args, fmt);
2471 vfprintf(stderr, fmt, args);
2472 va_end(args);
2473 fprintf(stderr, "\n");
2474 }
2476 void URI::trace(const char *fmt, ...)
2477 {
2478 va_list args;
2479 fprintf(stdout, "URI: ");
2480 va_start(args, fmt);
2481 vfprintf(stdout, fmt, args);
2482 va_end(args);
2483 fprintf(stdout, "\n");
2484 }
2489 //#########################################################################
2490 //# P A R S I N G
2491 //#########################################################################
2495 int URI::peek(int p)
2496 {
2497 if (p<0 || p>=parselen)
2498 return -1;
2499 return parsebuf[p];
2500 }
2504 int URI::match(int p0, const char *key)
2505 {
2506 int p = p0;
2507 while (p < parselen)
2508 {
2509 if (*key == '\0')
2510 return p;
2511 else if (*key != parsebuf[p])
2512 break;
2513 p++; key++;
2514 }
2515 return p0;
2516 }
2518 //#########################################################################
2519 //# Parsing is performed according to:
2520 //# http://www.gbiv.com/protocols/uri/rfc/rfc3986.html#components
2521 //#########################################################################
2523 int URI::parseScheme(int p0)
2524 {
2525 int p = p0;
2526 for (LookupEntry *entry = schemes; entry->sval ; entry++)
2527 {
2528 int p2 = match(p, entry->sval);
2529 if (p2 > p)
2530 {
2531 schemeStr = entry->sval;
2532 scheme = entry->ival;
2533 port = entry->port;
2534 p = p2;
2535 return p;
2536 }
2537 }
2539 return p;
2540 }
2543 int URI::parseHierarchicalPart(int p0)
2544 {
2545 int p = p0;
2546 int ch;
2548 //# Authority field (host and port, for example)
2549 int p2 = match(p, "//");
2550 if (p2 > p)
2551 {
2552 p = p2;
2553 portSpecified = false;
2554 String portStr;
2555 while (p < parselen)
2556 {
2557 ch = peek(p);
2558 if (ch == '/')
2559 break;
2560 else if (ch == ':')
2561 portSpecified = true;
2562 else if (portSpecified)
2563 portStr.push_back((XMLCh)ch);
2564 else
2565 authority.push_back((XMLCh)ch);
2566 p++;
2567 }
2568 if (portStr.size() > 0)
2569 {
2570 char *pstr = (char *)portStr.c_str();
2571 char *endStr;
2572 long val = strtol(pstr, &endStr, 10);
2573 if (endStr > pstr) //successful parse?
2574 port = val;
2575 }
2576 }
2578 //# Are we absolute?
2579 ch = peek(p);
2580 if (isLetter(ch) && peek(p+1)==':')
2581 {
2582 absolute = true;
2583 path.push_back((XMLCh)'/');
2584 }
2585 else if (ch == '/')
2586 {
2587 absolute = true;
2588 if (p>p0) //in other words, if '/' is not the first char
2589 opaque = true;
2590 path.push_back((XMLCh)ch);
2591 p++;
2592 }
2594 while (p < parselen)
2595 {
2596 ch = peek(p);
2597 if (ch == '?' || ch == '#')
2598 break;
2599 path.push_back((XMLCh)ch);
2600 p++;
2601 }
2603 return p;
2604 }
2606 int URI::parseQuery(int p0)
2607 {
2608 int p = p0;
2609 int ch = peek(p);
2610 if (ch != '?')
2611 return p0;
2613 p++;
2614 while (p < parselen)
2615 {
2616 ch = peek(p);
2617 if (ch == '#')
2618 break;
2619 query.push_back((XMLCh)ch);
2620 p++;
2621 }
2624 return p;
2625 }
2627 int URI::parseFragment(int p0)
2628 {
2630 int p = p0;
2631 int ch = peek(p);
2632 if (ch != '#')
2633 return p0;
2635 p++;
2636 while (p < parselen)
2637 {
2638 ch = peek(p);
2639 if (ch == '?')
2640 break;
2641 fragment.push_back((XMLCh)ch);
2642 p++;
2643 }
2646 return p;
2647 }
2650 int URI::parse(int p0)
2651 {
2653 int p = p0;
2655 int p2 = parseScheme(p);
2656 if (p2 < 0)
2657 {
2658 error("Scheme");
2659 return -1;
2660 }
2661 p = p2;
2664 p2 = parseHierarchicalPart(p);
2665 if (p2 < 0)
2666 {
2667 error("Hierarchical part");
2668 return -1;
2669 }
2670 p = p2;
2672 p2 = parseQuery(p);
2673 if (p2 < 0)
2674 {
2675 error("Query");
2676 return -1;
2677 }
2678 p = p2;
2681 p2 = parseFragment(p);
2682 if (p2 < 0)
2683 {
2684 error("Fragment");
2685 return -1;
2686 }
2687 p = p2;
2689 return p;
2691 }
2695 bool URI::parse(const String &str)
2696 {
2697 init();
2699 parselen = str.size();
2701 String tmp;
2702 for (unsigned int i=0 ; i<str.size() ; i++)
2703 {
2704 XMLCh ch = (XMLCh) str[i];
2705 if (ch == '\\')
2706 tmp.push_back((XMLCh)'/');
2707 else
2708 tmp.push_back(ch);
2709 }
2710 parsebuf = (char *) tmp.c_str();
2713 int p = parse(0);
2714 normalize();
2716 if (p < 0)
2717 {
2718 error("Syntax error");
2719 return false;
2720 }
2722 //printf("uri:%s\n", toString().c_str());
2723 //printf("path:%s\n", path.c_str());
2725 return true;
2727 }
2736 //########################################################################
2737 //########################################################################
2738 //## M A K E
2739 //########################################################################
2740 //########################################################################
2742 //########################################################################
2743 //# F I L E S E T
2744 //########################################################################
2745 /**
2746 * This is the descriptor for a <fileset> item
2747 */
2748 class FileSet
2749 {
2750 public:
2752 /**
2753 *
2754 */
2755 FileSet()
2756 {}
2758 /**
2759 *
2760 */
2761 FileSet(const FileSet &other)
2762 { assign(other); }
2764 /**
2765 *
2766 */
2767 FileSet &operator=(const FileSet &other)
2768 { assign(other); return *this; }
2770 /**
2771 *
2772 */
2773 virtual ~FileSet()
2774 {}
2776 /**
2777 *
2778 */
2779 String getDirectory()
2780 { return directory; }
2782 /**
2783 *
2784 */
2785 void setDirectory(const String &val)
2786 { directory = val; }
2788 /**
2789 *
2790 */
2791 void setFiles(const std::vector<String> &val)
2792 { files = val; }
2794 /**
2795 *
2796 */
2797 std::vector<String> getFiles()
2798 { return files; }
2800 /**
2801 *
2802 */
2803 void setIncludes(const std::vector<String> &val)
2804 { includes = val; }
2806 /**
2807 *
2808 */
2809 std::vector<String> getIncludes()
2810 { return includes; }
2812 /**
2813 *
2814 */
2815 void setExcludes(const std::vector<String> &val)
2816 { excludes = val; }
2818 /**
2819 *
2820 */
2821 std::vector<String> getExcludes()
2822 { return excludes; }
2824 /**
2825 *
2826 */
2827 unsigned int size()
2828 { return files.size(); }
2830 /**
2831 *
2832 */
2833 String operator[](int index)
2834 { return files[index]; }
2836 /**
2837 *
2838 */
2839 void clear()
2840 {
2841 directory = "";
2842 files.clear();
2843 includes.clear();
2844 excludes.clear();
2845 }
2848 private:
2850 void assign(const FileSet &other)
2851 {
2852 directory = other.directory;
2853 files = other.files;
2854 includes = other.includes;
2855 excludes = other.excludes;
2856 }
2858 String directory;
2859 std::vector<String> files;
2860 std::vector<String> includes;
2861 std::vector<String> excludes;
2862 };
2865 //########################################################################
2866 //# F I L E L I S T
2867 //########################################################################
2868 /**
2869 * This is a simpler, explicitly-named list of files
2870 */
2871 class FileList
2872 {
2873 public:
2875 /**
2876 *
2877 */
2878 FileList()
2879 {}
2881 /**
2882 *
2883 */
2884 FileList(const FileList &other)
2885 { assign(other); }
2887 /**
2888 *
2889 */
2890 FileList &operator=(const FileList &other)
2891 { assign(other); return *this; }
2893 /**
2894 *
2895 */
2896 virtual ~FileList()
2897 {}
2899 /**
2900 *
2901 */
2902 String getDirectory()
2903 { return directory; }
2905 /**
2906 *
2907 */
2908 void setDirectory(const String &val)
2909 { directory = val; }
2911 /**
2912 *
2913 */
2914 void setFiles(const std::vector<String> &val)
2915 { files = val; }
2917 /**
2918 *
2919 */
2920 std::vector<String> getFiles()
2921 { return files; }
2923 /**
2924 *
2925 */
2926 unsigned int size()
2927 { return files.size(); }
2929 /**
2930 *
2931 */
2932 String operator[](int index)
2933 { return files[index]; }
2935 /**
2936 *
2937 */
2938 void clear()
2939 {
2940 directory = "";
2941 files.clear();
2942 }
2945 private:
2947 void assign(const FileList &other)
2948 {
2949 directory = other.directory;
2950 files = other.files;
2951 }
2953 String directory;
2954 std::vector<String> files;
2955 };
2960 //########################################################################
2961 //# M A K E B A S E
2962 //########################################################################
2963 /**
2964 * Base class for all classes in this file
2965 */
2966 class MakeBase
2967 {
2968 public:
2970 MakeBase()
2971 { line = 0; }
2972 virtual ~MakeBase()
2973 {}
2975 /**
2976 * Return the URI of the file associated with this object
2977 */
2978 URI getURI()
2979 { return uri; }
2981 /**
2982 * Set the uri to the given string
2983 */
2984 void setURI(const String &uristr)
2985 { uri.parse(uristr); }
2987 /**
2988 * Resolve another path relative to this one
2989 */
2990 String resolve(const String &otherPath);
2992 /**
2993 * replace variable refs like ${a} with their values
2994 * Assume that the string has already been syntax validated
2995 */
2996 String eval(const String &s, const String &defaultVal);
2998 /**
2999 * replace variable refs like ${a} with their values
3000 * return true or false
3001 * Assume that the string has already been syntax validated
3002 */
3003 bool evalBool(const String &s, bool defaultVal);
3005 /**
3006 * Get an element attribute, performing substitutions if necessary
3007 */
3008 bool getAttribute(Element *elem, const String &name, String &result);
3010 /**
3011 * Get an element value, performing substitutions if necessary
3012 */
3013 bool getValue(Element *elem, String &result);
3015 /**
3016 * Set the current line number in the file
3017 */
3018 void setLine(int val)
3019 { line = val; }
3021 /**
3022 * Get the current line number in the file
3023 */
3024 int getLine()
3025 { return line; }
3028 /**
3029 * Set a property to a given value
3030 */
3031 virtual void setProperty(const String &name, const String &val)
3032 {
3033 properties[name] = val;
3034 }
3036 /**
3037 * Return a named property is found, else a null string
3038 */
3039 virtual String getProperty(const String &name)
3040 {
3041 String val;
3042 std::map<String, String>::iterator iter = properties.find(name);
3043 if (iter != properties.end())
3044 val = iter->second;
3045 String sval;
3046 if (!getSubstitutions(val, sval))
3047 return false;
3048 return sval;
3049 }
3051 /**
3052 * Return true if a named property is found, else false
3053 */
3054 virtual bool hasProperty(const String &name)
3055 {
3056 std::map<String, String>::iterator iter = properties.find(name);
3057 if (iter == properties.end())
3058 return false;
3059 return true;
3060 }
3063 protected:
3065 /**
3066 * The path to the file associated with this object
3067 */
3068 URI uri;
3070 /**
3071 * If this prefix is seen in a substitution, use an environment
3072 * variable.
3073 * example: <property environment="env"/>
3074 * ${env.JAVA_HOME}
3075 */
3076 String envPrefix;
3078 /**
3079 * If this prefix is seen in a substitution, use as a
3080 * pkg-config 'all' query
3081 * example: <property pkg-config="pc"/>
3082 * ${pc.gtkmm}
3083 */
3084 String pcPrefix;
3086 /**
3087 * If this prefix is seen in a substitution, use as a
3088 * pkg-config 'cflags' query
3089 * example: <property pkg-config="pcc"/>
3090 * ${pcc.gtkmm}
3091 */
3092 String pccPrefix;
3094 /**
3095 * If this prefix is seen in a substitution, use as a
3096 * pkg-config 'libs' query
3097 * example: <property pkg-config="pcl"/>
3098 * ${pcl.gtkmm}
3099 */
3100 String pclPrefix;
3106 /**
3107 * Print a printf()-like formatted error message
3108 */
3109 void error(const char *fmt, ...);
3111 /**
3112 * Print a printf()-like formatted trace message
3113 */
3114 void status(const char *fmt, ...);
3116 /**
3117 * Show target status
3118 */
3119 void targetstatus(const char *fmt, ...);
3121 /**
3122 * Print a printf()-like formatted trace message
3123 */
3124 void trace(const char *fmt, ...);
3126 /**
3127 * Check if a given string matches a given regex pattern
3128 */
3129 bool regexMatch(const String &str, const String &pattern);
3131 /**
3132 *
3133 */
3134 String getSuffix(const String &fname);
3136 /**
3137 * Break up a string into substrings delimited the characters
3138 * in delimiters. Null-length substrings are ignored
3139 */
3140 std::vector<String> tokenize(const String &val,
3141 const String &delimiters);
3143 /**
3144 * replace runs of whitespace with a space
3145 */
3146 String strip(const String &s);
3148 /**
3149 * remove leading whitespace from each line
3150 */
3151 String leftJustify(const String &s);
3153 /**
3154 * remove leading and trailing whitespace from string
3155 */
3156 String trim(const String &s);
3158 /**
3159 * Return a lower case version of the given string
3160 */
3161 String toLower(const String &s);
3163 /**
3164 * Return the native format of the canonical
3165 * path which we store
3166 */
3167 String getNativePath(const String &path);
3169 /**
3170 * Execute a shell command. Outbuf is a ref to a string
3171 * to catch the result.
3172 */
3173 bool executeCommand(const String &call,
3174 const String &inbuf,
3175 String &outbuf,
3176 String &errbuf);
3177 /**
3178 * List all directories in a given base and starting directory
3179 * It is usually called like:
3180 * bool ret = listDirectories("src", "", result);
3181 */
3182 bool listDirectories(const String &baseName,
3183 const String &dirname,
3184 std::vector<String> &res);
3186 /**
3187 * Find all files in the named directory
3188 */
3189 bool listFiles(const String &baseName,
3190 const String &dirname,
3191 std::vector<String> &result);
3193 /**
3194 * Perform a listing for a fileset
3195 */
3196 bool listFiles(MakeBase &propRef, FileSet &fileSet);
3198 /**
3199 * Parse a <patternset>
3200 */
3201 bool parsePatternSet(Element *elem,
3202 MakeBase &propRef,
3203 std::vector<String> &includes,
3204 std::vector<String> &excludes);
3206 /**
3207 * Parse a <fileset> entry, and determine which files
3208 * should be included
3209 */
3210 bool parseFileSet(Element *elem,
3211 MakeBase &propRef,
3212 FileSet &fileSet);
3213 /**
3214 * Parse a <filelist> entry
3215 */
3216 bool parseFileList(Element *elem,
3217 MakeBase &propRef,
3218 FileList &fileList);
3220 /**
3221 * Return this object's property list
3222 */
3223 virtual std::map<String, String> &getProperties()
3224 { return properties; }
3227 std::map<String, String> properties;
3229 /**
3230 * Create a directory, making intermediate dirs
3231 * if necessary
3232 */
3233 bool createDirectory(const String &dirname);
3235 /**
3236 * Delete a directory and its children if desired
3237 */
3238 bool removeDirectory(const String &dirName);
3240 /**
3241 * Copy a file from one name to another. Perform only if needed
3242 */
3243 bool copyFile(const String &srcFile, const String &destFile);
3245 /**
3246 * Tests if the file exists and is a regular file
3247 */
3248 bool isRegularFile(const String &fileName);
3250 /**
3251 * Tests if the file exists and is a directory
3252 */
3253 bool isDirectory(const String &fileName);
3255 /**
3256 * Tests is the modification date of fileA is newer than fileB
3257 */
3258 bool isNewerThan(const String &fileA, const String &fileB);
3260 private:
3262 bool pkgConfigRecursive(const String packageName,
3263 const String &path,
3264 const String &prefix,
3265 int query,
3266 String &result,
3267 std::set<String> &deplist);
3269 /**
3270 * utility method to query for "all", "cflags", or "libs" for this package and its
3271 * dependencies. 0, 1, 2
3272 */
3273 bool pkgConfigQuery(const String &packageName, int query, String &result);
3275 /**
3276 * replace a variable ref like ${a} with a value
3277 */
3278 bool lookupProperty(const String &s, String &result);
3280 /**
3281 * replace variable refs in a sting like ${a} with their values
3282 */
3283 bool getSubstitutions(const String &s, String &result);
3285 int line;
3288 };
3292 /**
3293 * Define the pkg-config class here, since it will be used in MakeBase method
3294 * implementations.
3295 */
3296 class PkgConfig : public MakeBase
3297 {
3299 public:
3301 /**
3302 *
3303 */
3304 PkgConfig()
3305 {
3306 path = ".";
3307 prefix = "/target";
3308 init();
3309 }
3311 /**
3312 *
3313 */
3314 PkgConfig(const PkgConfig &other)
3315 { assign(other); }
3317 /**
3318 *
3319 */
3320 PkgConfig &operator=(const PkgConfig &other)
3321 { assign(other); return *this; }
3323 /**
3324 *
3325 */
3326 virtual ~PkgConfig()
3327 { }
3329 /**
3330 *
3331 */
3332 virtual String getName()
3333 { return name; }
3335 /**
3336 *
3337 */
3338 virtual String getPath()
3339 { return path; }
3341 /**
3342 *
3343 */
3344 virtual void setPath(const String &val)
3345 { path = val; }
3347 /**
3348 *
3349 */
3350 virtual String getPrefix()
3351 { return prefix; }
3353 /**
3354 * Allow the user to override the prefix in the file
3355 */
3356 virtual void setPrefix(const String &val)
3357 { prefix = val; }
3359 /**
3360 *
3361 */
3362 virtual String getDescription()
3363 { return description; }
3365 /**
3366 *
3367 */
3368 virtual String getCflags()
3369 { return cflags; }
3371 /**
3372 *
3373 */
3374 virtual String getLibs()
3375 { return libs; }
3377 /**
3378 *
3379 */
3380 virtual String getAll()
3381 {
3382 String ret = cflags;
3383 ret.append(" ");
3384 ret.append(libs);
3385 return ret;
3386 }
3388 /**
3389 *
3390 */
3391 virtual String getVersion()
3392 { return version; }
3394 /**
3395 *
3396 */
3397 virtual int getMajorVersion()
3398 { return majorVersion; }
3400 /**
3401 *
3402 */
3403 virtual int getMinorVersion()
3404 { return minorVersion; }
3406 /**
3407 *
3408 */
3409 virtual int getMicroVersion()
3410 { return microVersion; }
3412 /**
3413 *
3414 */
3415 virtual std::map<String, String> &getAttributes()
3416 { return attrs; }
3418 /**
3419 *
3420 */
3421 virtual std::vector<String> &getRequireList()
3422 { return requireList; }
3424 /**
3425 * Read a file for its details
3426 */
3427 virtual bool readFile(const String &fileName);
3429 /**
3430 * Read a file for its details
3431 */
3432 virtual bool query(const String &name);
3434 private:
3436 void init()
3437 {
3438 //do not set path and prefix here
3439 name = "";
3440 description = "";
3441 cflags = "";
3442 libs = "";
3443 requires = "";
3444 version = "";
3445 majorVersion = 0;
3446 minorVersion = 0;
3447 microVersion = 0;
3448 fileName = "";
3449 attrs.clear();
3450 requireList.clear();
3451 }
3453 void assign(const PkgConfig &other)
3454 {
3455 name = other.name;
3456 path = other.path;
3457 prefix = other.prefix;
3458 description = other.description;
3459 cflags = other.cflags;
3460 libs = other.libs;
3461 requires = other.requires;
3462 version = other.version;
3463 majorVersion = other.majorVersion;
3464 minorVersion = other.minorVersion;
3465 microVersion = other.microVersion;
3466 fileName = other.fileName;
3467 attrs = other.attrs;
3468 requireList = other.requireList;
3469 }
3473 int get(int pos);
3475 int skipwhite(int pos);
3477 int getword(int pos, String &ret);
3479 /**
3480 * Very important
3481 */
3482 bool parseRequires();
3484 void parseVersion();
3486 bool parseLine(const String &lineBuf);
3488 bool parse(const String &buf);
3490 void dumpAttrs();
3492 String name;
3494 String path;
3496 String prefix;
3498 String description;
3500 String cflags;
3502 String libs;
3504 String requires;
3506 String version;
3508 int majorVersion;
3510 int minorVersion;
3512 int microVersion;
3514 String fileName;
3516 std::map<String, String> attrs;
3518 std::vector<String> requireList;
3520 char *parsebuf;
3521 int parselen;
3522 };
3527 /**
3528 * Print a printf()-like formatted error message
3529 */
3530 void MakeBase::error(const char *fmt, ...)
3531 {
3532 va_list args;
3533 va_start(args,fmt);
3534 fprintf(stderr, "Make error line %d: ", line);
3535 vfprintf(stderr, fmt, args);
3536 fprintf(stderr, "\n");
3537 va_end(args) ;
3538 }
3542 /**
3543 * Print a printf()-like formatted trace message
3544 */
3545 void MakeBase::status(const char *fmt, ...)
3546 {
3547 va_list args;
3548 va_start(args,fmt);
3549 //fprintf(stdout, " ");
3550 vfprintf(stdout, fmt, args);
3551 fprintf(stdout, "\n");
3552 va_end(args) ;
3553 }
3556 /**
3557 * Resolve another path relative to this one
3558 */
3559 String MakeBase::resolve(const String &otherPath)
3560 {
3561 URI otherURI(otherPath);
3562 URI fullURI = uri.resolve(otherURI);
3563 String ret = fullURI.toString();
3564 return ret;
3565 }
3568 /**
3569 * Print a printf()-like formatted trace message
3570 */
3571 void MakeBase::trace(const char *fmt, ...)
3572 {
3573 va_list args;
3574 va_start(args,fmt);
3575 fprintf(stdout, "Make: ");
3576 vfprintf(stdout, fmt, args);
3577 fprintf(stdout, "\n");
3578 va_end(args) ;
3579 }
3583 /**
3584 * Check if a given string matches a given regex pattern
3585 */
3586 bool MakeBase::regexMatch(const String &str, const String &pattern)
3587 {
3588 const TRexChar *terror = NULL;
3589 const TRexChar *cpat = pattern.c_str();
3590 TRex *expr = trex_compile(cpat, &terror);
3591 if (!expr)
3592 {
3593 if (!terror)
3594 terror = "undefined";
3595 error("compilation error [%s]!\n", terror);
3596 return false;
3597 }
3599 bool ret = true;
3601 const TRexChar *cstr = str.c_str();
3602 if (trex_match(expr, cstr))
3603 {
3604 ret = true;
3605 }
3606 else
3607 {
3608 ret = false;
3609 }
3611 trex_free(expr);
3613 return ret;
3614 }
3616 /**
3617 * Return the suffix, if any, of a file name
3618 */
3619 String MakeBase::getSuffix(const String &fname)
3620 {
3621 if (fname.size() < 2)
3622 return "";
3623 unsigned int pos = fname.find_last_of('.');
3624 if (pos == fname.npos)
3625 return "";
3626 pos++;
3627 String res = fname.substr(pos, fname.size()-pos);
3628 //trace("suffix:%s", res.c_str());
3629 return res;
3630 }
3634 /**
3635 * Break up a string into substrings delimited the characters
3636 * in delimiters. Null-length substrings are ignored
3637 */
3638 std::vector<String> MakeBase::tokenize(const String &str,
3639 const String &delimiters)
3640 {
3642 std::vector<String> res;
3643 char *del = (char *)delimiters.c_str();
3644 String dmp;
3645 for (unsigned int i=0 ; i<str.size() ; i++)
3646 {
3647 char ch = str[i];
3648 char *p = (char *)0;
3649 for (p=del ; *p ; p++)
3650 if (*p == ch)
3651 break;
3652 if (*p)
3653 {
3654 if (dmp.size() > 0)
3655 {
3656 res.push_back(dmp);
3657 dmp.clear();
3658 }
3659 }
3660 else
3661 {
3662 dmp.push_back(ch);
3663 }
3664 }
3665 //Add tail
3666 if (dmp.size() > 0)
3667 {
3668 res.push_back(dmp);
3669 dmp.clear();
3670 }
3672 return res;
3673 }
3677 /**
3678 * replace runs of whitespace with a single space
3679 */
3680 String MakeBase::strip(const String &s)
3681 {
3682 int len = s.size();
3683 String stripped;
3684 for (int i = 0 ; i<len ; i++)
3685 {
3686 char ch = s[i];
3687 if (isspace(ch))
3688 {
3689 stripped.push_back(' ');
3690 for ( ; i<len ; i++)
3691 {
3692 ch = s[i];
3693 if (!isspace(ch))
3694 {
3695 stripped.push_back(ch);
3696 break;
3697 }
3698 }
3699 }
3700 else
3701 {
3702 stripped.push_back(ch);
3703 }
3704 }
3705 return stripped;
3706 }
3708 /**
3709 * remove leading whitespace from each line
3710 */
3711 String MakeBase::leftJustify(const String &s)
3712 {
3713 String out;
3714 int len = s.size();
3715 for (int i = 0 ; i<len ; )
3716 {
3717 char ch;
3718 //Skip to first visible character
3719 while (i<len)
3720 {
3721 ch = s[i];
3722 if (ch == '\n' || ch == '\r'
3723 || !isspace(ch))
3724 break;
3725 i++;
3726 }
3727 //Copy the rest of the line
3728 while (i<len)
3729 {
3730 ch = s[i];
3731 if (ch == '\n' || ch == '\r')
3732 {
3733 if (ch != '\r')
3734 out.push_back('\n');
3735 i++;
3736 break;
3737 }
3738 else
3739 {
3740 out.push_back(ch);
3741 }
3742 i++;
3743 }
3744 }
3745 return out;
3746 }
3749 /**
3750 * Removes whitespace from beginning and end of a string
3751 */
3752 String MakeBase::trim(const String &s)
3753 {
3754 if (s.size() < 1)
3755 return s;
3757 //Find first non-ws char
3758 unsigned int begin = 0;
3759 for ( ; begin < s.size() ; begin++)
3760 {
3761 if (!isspace(s[begin]))
3762 break;
3763 }
3765 //Find first non-ws char, going in reverse
3766 unsigned int end = s.size() - 1;
3767 for ( ; end > begin ; end--)
3768 {
3769 if (!isspace(s[end]))
3770 break;
3771 }
3772 //trace("begin:%d end:%d", begin, end);
3774 String res = s.substr(begin, end-begin+1);
3775 return res;
3776 }
3779 /**
3780 * Return a lower case version of the given string
3781 */
3782 String MakeBase::toLower(const String &s)
3783 {
3784 if (s.size()==0)
3785 return s;
3787 String ret;
3788 for(unsigned int i=0; i<s.size() ; i++)
3789 {
3790 ret.push_back(tolower(s[i]));
3791 }
3792 return ret;
3793 }
3796 /**
3797 * Return the native format of the canonical
3798 * path which we store
3799 */
3800 String MakeBase::getNativePath(const String &path)
3801 {
3802 #ifdef __WIN32__
3803 String npath;
3804 unsigned int firstChar = 0;
3805 if (path.size() >= 3)
3806 {
3807 if (path[0] == '/' &&
3808 isalpha(path[1]) &&
3809 path[2] == ':')
3810 firstChar++;
3811 }
3812 for (unsigned int i=firstChar ; i<path.size() ; i++)
3813 {
3814 char ch = path[i];
3815 if (ch == '/')
3816 npath.push_back('\\');
3817 else
3818 npath.push_back(ch);
3819 }
3820 return npath;
3821 #else
3822 return path;
3823 #endif
3824 }
3827 #ifdef __WIN32__
3828 #include <tchar.h>
3830 static String win32LastError()
3831 {
3833 DWORD dw = GetLastError();
3835 LPVOID str;
3836 FormatMessage(
3837 FORMAT_MESSAGE_ALLOCATE_BUFFER |
3838 FORMAT_MESSAGE_FROM_SYSTEM,
3839 NULL,
3840 dw,
3841 0,
3842 (LPTSTR) &str,
3843 0, NULL );
3844 LPTSTR p = _tcschr((const char *)str, _T('\r'));
3845 if(p != NULL)
3846 { // lose CRLF
3847 *p = _T('\0');
3848 }
3849 String ret = (char *)str;
3850 LocalFree(str);
3852 return ret;
3853 }
3854 #endif
3858 /**
3859 * Execute a system call, using pipes to send data to the
3860 * program's stdin, and reading stdout and stderr.
3861 */
3862 bool MakeBase::executeCommand(const String &command,
3863 const String &inbuf,
3864 String &outbuf,
3865 String &errbuf)
3866 {
3868 status("============ cmd ============\n%s\n=============================",
3869 command.c_str());
3871 outbuf.clear();
3872 errbuf.clear();
3874 #ifdef __WIN32__
3876 /*
3877 I really hate having win32 code in this program, but the
3878 read buffer in command.com and cmd.exe are just too small
3879 for the large commands we need for compiling and linking.
3880 */
3882 bool ret = true;
3884 //# Allocate a separate buffer for safety
3885 char *paramBuf = new char[command.size() + 1];
3886 if (!paramBuf)
3887 {
3888 error("executeCommand cannot allocate command buffer");
3889 return false;
3890 }
3891 strcpy(paramBuf, (char *)command.c_str());
3893 //# Go to http://msdn2.microsoft.com/en-us/library/ms682499.aspx
3894 //# to see how Win32 pipes work
3896 //# Create pipes
3897 SECURITY_ATTRIBUTES saAttr;
3898 saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
3899 saAttr.bInheritHandle = TRUE;
3900 saAttr.lpSecurityDescriptor = NULL;
3901 HANDLE stdinRead, stdinWrite;
3902 HANDLE stdoutRead, stdoutWrite;
3903 HANDLE stderrRead, stderrWrite;
3904 if (!CreatePipe(&stdinRead, &stdinWrite, &saAttr, 0))
3905 {
3906 error("executeProgram: could not create pipe");
3907 delete[] paramBuf;
3908 return false;
3909 }
3910 SetHandleInformation(stdinWrite, HANDLE_FLAG_INHERIT, 0);
3911 if (!CreatePipe(&stdoutRead, &stdoutWrite, &saAttr, 0))
3912 {
3913 error("executeProgram: could not create pipe");
3914 delete[] paramBuf;
3915 return false;
3916 }
3917 SetHandleInformation(stdoutRead, HANDLE_FLAG_INHERIT, 0);
3918 if (!CreatePipe(&stderrRead, &stderrWrite, &saAttr, 0))
3919 {
3920 error("executeProgram: could not create pipe");
3921 delete[] paramBuf;
3922 return false;
3923 }
3924 SetHandleInformation(stderrRead, HANDLE_FLAG_INHERIT, 0);
3926 // Create the process
3927 STARTUPINFO siStartupInfo;
3928 PROCESS_INFORMATION piProcessInfo;
3929 memset(&siStartupInfo, 0, sizeof(siStartupInfo));
3930 memset(&piProcessInfo, 0, sizeof(piProcessInfo));
3931 siStartupInfo.cb = sizeof(siStartupInfo);
3932 siStartupInfo.hStdError = stderrWrite;
3933 siStartupInfo.hStdOutput = stdoutWrite;
3934 siStartupInfo.hStdInput = stdinRead;
3935 siStartupInfo.dwFlags |= STARTF_USESTDHANDLES;
3937 if (!CreateProcess(NULL, paramBuf, NULL, NULL, true,
3938 0, NULL, NULL, &siStartupInfo,
3939 &piProcessInfo))
3940 {
3941 error("executeCommand : could not create process : %s",
3942 win32LastError().c_str());
3943 ret = false;
3944 }
3946 delete[] paramBuf;
3948 DWORD bytesWritten;
3949 if (inbuf.size()>0 &&
3950 !WriteFile(stdinWrite, inbuf.c_str(), inbuf.size(),
3951 &bytesWritten, NULL))
3952 {
3953 error("executeCommand: could not write to pipe");
3954 return false;
3955 }
3956 if (!CloseHandle(stdinWrite))
3957 {
3958 error("executeCommand: could not close write pipe");
3959 return false;
3960 }
3961 if (!CloseHandle(stdoutWrite))
3962 {
3963 error("executeCommand: could not close read pipe");
3964 return false;
3965 }
3966 if (!CloseHandle(stderrWrite))
3967 {
3968 error("executeCommand: could not close read pipe");
3969 return false;
3970 }
3972 bool lastLoop = false;
3973 while (true)
3974 {
3975 DWORD avail;
3976 DWORD bytesRead;
3977 char readBuf[4096];
3979 //trace("## stderr");
3980 PeekNamedPipe(stderrRead, NULL, 0, NULL, &avail, NULL);
3981 if (avail > 0)
3982 {
3983 bytesRead = 0;
3984 if (avail>4096) avail = 4096;
3985 ReadFile(stderrRead, readBuf, avail, &bytesRead, NULL);
3986 if (bytesRead > 0)
3987 {
3988 for (unsigned int i=0 ; i<bytesRead ; i++)
3989 errbuf.push_back(readBuf[i]);
3990 }
3991 }
3993 //trace("## stdout");
3994 PeekNamedPipe(stdoutRead, NULL, 0, NULL, &avail, NULL);
3995 if (avail > 0)
3996 {
3997 bytesRead = 0;
3998 if (avail>4096) avail = 4096;
3999 ReadFile(stdoutRead, readBuf, avail, &bytesRead, NULL);
4000 if (bytesRead > 0)
4001 {
4002 for (unsigned int i=0 ; i<bytesRead ; i++)
4003 outbuf.push_back(readBuf[i]);
4004 }
4005 }
4007 //Was this the final check after program done?
4008 if (lastLoop)
4009 break;
4011 DWORD exitCode;
4012 GetExitCodeProcess(piProcessInfo.hProcess, &exitCode);
4013 if (exitCode != STILL_ACTIVE)
4014 lastLoop = true;
4016 Sleep(10);
4017 }
4018 //trace("outbuf:%s", outbuf.c_str());
4019 if (!CloseHandle(stdoutRead))
4020 {
4021 error("executeCommand: could not close read pipe");
4022 return false;
4023 }
4024 if (!CloseHandle(stderrRead))
4025 {
4026 error("executeCommand: could not close read pipe");
4027 return false;
4028 }
4030 DWORD exitCode;
4031 GetExitCodeProcess(piProcessInfo.hProcess, &exitCode);
4032 //trace("exit code:%d", exitCode);
4033 if (exitCode != 0)
4034 {
4035 ret = false;
4036 }
4038 CloseHandle(piProcessInfo.hProcess);
4039 CloseHandle(piProcessInfo.hThread);
4041 return ret;
4043 #else //do it unix-style
4045 String s;
4046 FILE *f = popen(command.c_str(), "r");
4047 int errnum = 0;
4048 if (f)
4049 {
4050 while (true)
4051 {
4052 int ch = fgetc(f);
4053 if (ch < 0)
4054 break;
4055 s.push_back((char)ch);
4056 }
4057 errnum = pclose(f);
4058 }
4059 outbuf = s;
4060 if (errnum != 0)
4061 {
4062 error("exec of command '%s' failed : %s",
4063 command.c_str(), strerror(errno));
4064 return false;
4065 }
4066 else
4067 return true;
4069 #endif
4070 }
4075 bool MakeBase::listDirectories(const String &baseName,
4076 const String &dirName,
4077 std::vector<String> &res)
4078 {
4079 res.push_back(dirName);
4080 String fullPath = baseName;
4081 if (dirName.size()>0)
4082 {
4083 fullPath.append("/");
4084 fullPath.append(dirName);
4085 }
4086 DIR *dir = opendir(fullPath.c_str());
4087 while (true)
4088 {
4089 struct dirent *de = readdir(dir);
4090 if (!de)
4091 break;
4093 //Get the directory member name
4094 String s = de->d_name;
4095 if (s.size() == 0 || s[0] == '.')
4096 continue;
4097 String childName = dirName;
4098 childName.append("/");
4099 childName.append(s);
4101 String fullChildPath = baseName;
4102 fullChildPath.append("/");
4103 fullChildPath.append(childName);
4104 struct stat finfo;
4105 String childNative = getNativePath(fullChildPath);
4106 if (stat(childNative.c_str(), &finfo)<0)
4107 {
4108 error("cannot stat file:%s", childNative.c_str());
4109 }
4110 else if (S_ISDIR(finfo.st_mode))
4111 {
4112 //trace("directory: %s", childName.c_str());
4113 if (!listDirectories(baseName, childName, res))
4114 return false;
4115 }
4116 }
4117 closedir(dir);
4119 return true;
4120 }
4123 bool MakeBase::listFiles(const String &baseDir,
4124 const String &dirName,
4125 std::vector<String> &res)
4126 {
4127 String fullDir = baseDir;
4128 if (dirName.size()>0)
4129 {
4130 fullDir.append("/");
4131 fullDir.append(dirName);
4132 }
4133 String dirNative = getNativePath(fullDir);
4135 std::vector<String> subdirs;
4136 DIR *dir = opendir(dirNative.c_str());
4137 if (!dir)
4138 {
4139 error("Could not open directory %s : %s",
4140 dirNative.c_str(), strerror(errno));
4141 return false;
4142 }
4143 while (true)
4144 {
4145 struct dirent *de = readdir(dir);
4146 if (!de)
4147 break;
4149 //Get the directory member name
4150 String s = de->d_name;
4151 if (s.size() == 0 || s[0] == '.')
4152 continue;
4153 String childName;
4154 if (dirName.size()>0)
4155 {
4156 childName.append(dirName);
4157 childName.append("/");
4158 }
4159 childName.append(s);
4160 String fullChild = baseDir;
4161 fullChild.append("/");
4162 fullChild.append(childName);
4164 if (isDirectory(fullChild))
4165 {
4166 //trace("directory: %s", childName.c_str());
4167 if (!listFiles(baseDir, childName, res))
4168 return false;
4169 continue;
4170 }
4171 else if (!isRegularFile(fullChild))
4172 {
4173 error("unknown file:%s", childName.c_str());
4174 return false;
4175 }
4177 //all done!
4178 res.push_back(childName);
4180 }
4181 closedir(dir);
4183 return true;
4184 }
4187 /**
4188 * Several different classes extend MakeBase. By "propRef", we mean
4189 * the one holding the properties. Likely "Make" itself
4190 */
4191 bool MakeBase::listFiles(MakeBase &propRef, FileSet &fileSet)
4192 {
4193 //before doing the list, resolve any property references
4194 //that might have been specified in the directory name, such as ${src}
4195 String fsDir = fileSet.getDirectory();
4196 String dir;
4197 if (!propRef.getSubstitutions(fsDir, dir))
4198 return false;
4199 String baseDir = propRef.resolve(dir);
4200 std::vector<String> fileList;
4201 if (!listFiles(baseDir, "", fileList))
4202 return false;
4204 std::vector<String> includes = fileSet.getIncludes();
4205 std::vector<String> excludes = fileSet.getExcludes();
4207 std::vector<String> incs;
4208 std::vector<String>::iterator iter;
4210 std::sort(fileList.begin(), fileList.end());
4212 //If there are <includes>, then add files to the output
4213 //in the order of the include list
4214 if (includes.size()==0)
4215 incs = fileList;
4216 else
4217 {
4218 for (iter = includes.begin() ; iter != includes.end() ; iter++)
4219 {
4220 String &pattern = *iter;
4221 std::vector<String>::iterator siter;
4222 for (siter = fileList.begin() ; siter != fileList.end() ; siter++)
4223 {
4224 String s = *siter;
4225 if (regexMatch(s, pattern))
4226 {
4227 //trace("INCLUDED:%s", s.c_str());
4228 incs.push_back(s);
4229 }
4230 }
4231 }
4232 }
4234 //Now trim off the <excludes>
4235 std::vector<String> res;
4236 for (iter = incs.begin() ; iter != incs.end() ; iter++)
4237 {
4238 String s = *iter;
4239 bool skipme = false;
4240 std::vector<String>::iterator siter;
4241 for (siter = excludes.begin() ; siter != excludes.end() ; siter++)
4242 {
4243 String &pattern = *siter;
4244 if (regexMatch(s, pattern))
4245 {
4246 //trace("EXCLUDED:%s", s.c_str());
4247 skipme = true;
4248 break;
4249 }
4250 }
4251 if (!skipme)
4252 res.push_back(s);
4253 }
4255 fileSet.setFiles(res);
4257 return true;
4258 }
4261 /**
4262 * 0 == all, 1 = cflags, 2 = libs
4263 */
4264 bool MakeBase::pkgConfigRecursive(const String packageName,
4265 const String &path,
4266 const String &prefix,
4267 int query,
4268 String &result,
4269 std::set<String> &deplist)
4270 {
4271 PkgConfig pkgConfig;
4272 if (path.size() > 0)
4273 pkgConfig.setPath(path);
4274 if (prefix.size() > 0)
4275 pkgConfig.setPrefix(prefix);
4276 if (!pkgConfig.query(packageName))
4277 return false;
4278 if (query == 0)
4279 result = pkgConfig.getAll();
4280 else if (query == 1)
4281 result = pkgConfig.getCflags();
4282 else
4283 result = pkgConfig.getLibs();
4284 deplist.insert(packageName);
4285 std::vector<String> list = pkgConfig.getRequireList();
4286 for (unsigned int i = 0 ; i<list.size() ; i++)
4287 {
4288 String depPkgName = list[i];
4289 if (deplist.find(depPkgName) != deplist.end())
4290 continue;
4291 String val;
4292 if (!pkgConfigRecursive(depPkgName, path, prefix, query, val, deplist))
4293 {
4294 error("Based on 'requires' attribute of package '%s'", packageName.c_str());
4295 return false;
4296 }
4297 result.append(" ");
4298 result.append(val);
4299 }
4301 return true;
4302 }
4304 bool MakeBase::pkgConfigQuery(const String &packageName, int query, String &result)
4305 {
4306 std::set<String> deplist;
4307 String path = getProperty("pkg-config-path");
4308 if (path.size()>0)
4309 path = resolve(path);
4310 String prefix = getProperty("pkg-config-prefix");
4311 String val;
4312 if (!pkgConfigRecursive(packageName, path, prefix, query, val, deplist))
4313 return false;
4314 result = val;
4315 return true;
4316 }
4320 /**
4321 * replace a variable ref like ${a} with a value
4322 */
4323 bool MakeBase::lookupProperty(const String &propertyName, String &result)
4324 {
4325 String varname = propertyName;
4326 if (envPrefix.size() > 0 &&
4327 varname.compare(0, envPrefix.size(), envPrefix) == 0)
4328 {
4329 varname = varname.substr(envPrefix.size());
4330 char *envstr = getenv(varname.c_str());
4331 if (!envstr)
4332 {
4333 error("environment variable '%s' not defined", varname.c_str());
4334 return false;
4335 }
4336 result = envstr;
4337 }
4338 else if (pcPrefix.size() > 0 &&
4339 varname.compare(0, pcPrefix.size(), pcPrefix) == 0)
4340 {
4341 varname = varname.substr(pcPrefix.size());
4342 String val;
4343 if (!pkgConfigQuery(varname, 0, val))
4344 return false;
4345 result = val;
4346 }
4347 else if (pccPrefix.size() > 0 &&
4348 varname.compare(0, pccPrefix.size(), pccPrefix) == 0)
4349 {
4350 varname = varname.substr(pccPrefix.size());
4351 String val;
4352 if (!pkgConfigQuery(varname, 1, val))
4353 return false;
4354 result = val;
4355 }
4356 else if (pclPrefix.size() > 0 &&
4357 varname.compare(0, pclPrefix.size(), pclPrefix) == 0)
4358 {
4359 varname = varname.substr(pclPrefix.size());
4360 String val;
4361 if (!pkgConfigQuery(varname, 2, val))
4362 return false;
4363 result = val;
4364 }
4365 else
4366 {
4367 std::map<String, String>::iterator iter;
4368 iter = properties.find(varname);
4369 if (iter != properties.end())
4370 {
4371 result = iter->second;
4372 }
4373 else
4374 {
4375 error("property '%s' not found", varname.c_str());
4376 return false;
4377 }
4378 }
4379 return true;
4380 }
4385 /**
4386 * Analyse a string, looking for any substitutions or other
4387 * things that need resilution
4388 */
4389 bool MakeBase::getSubstitutions(const String &str, String &result)
4390 {
4391 String s = trim(str);
4392 int len = (int)s.size();
4393 String val;
4394 for (int i=0 ; i<len ; i++)
4395 {
4396 char ch = s[i];
4397 if (ch == '$' && s[i+1] == '{')
4398 {
4399 String varname;
4400 int j = i+2;
4401 for ( ; j<len ; j++)
4402 {
4403 ch = s[j];
4404 if (ch == '$' && s[j+1] == '{')
4405 {
4406 error("attribute %s cannot have nested variable references",
4407 s.c_str());
4408 return false;
4409 }
4410 else if (ch == '}')
4411 {
4412 varname = trim(varname);
4413 String varval;
4414 if (!lookupProperty(varname, varval))
4415 return false;
4416 val.append(varval);
4417 break;
4418 }
4419 else
4420 {
4421 varname.push_back(ch);
4422 }
4423 }
4424 i = j;
4425 }
4426 else
4427 {
4428 val.push_back(ch);
4429 }
4430 }
4431 result = val;
4432 return true;
4433 }
4437 /**
4438 * replace variable refs like ${a} with their values
4439 * Assume that the string has already been syntax validated
4440 */
4441 String MakeBase::eval(const String &s, const String &defaultVal)
4442 {
4443 if (s.size()==0)
4444 return defaultVal;
4445 String ret;
4446 if (getSubstitutions(s, ret))
4447 return ret;
4448 else
4449 return defaultVal;
4450 }
4453 /**
4454 * replace variable refs like ${a} with their values
4455 * return true or false
4456 * Assume that the string has already been syntax validated
4457 */
4458 bool MakeBase::evalBool(const String &s, bool defaultVal)
4459 {
4460 if (s.size()==0)
4461 return defaultVal;
4462 String val = eval(s, "false");
4463 if (s == "true" || s == "TRUE")
4464 return true;
4465 else
4466 return defaultVal;
4467 }
4470 /**
4471 * Get a string attribute, testing it for proper syntax and
4472 * property names.
4473 */
4474 bool MakeBase::getAttribute(Element *elem, const String &name,
4475 String &result)
4476 {
4477 String s = elem->getAttribute(name);
4478 String tmp;
4479 bool ret = getSubstitutions(s, tmp);
4480 if (ret)
4481 result = s; //assign -if- ok
4482 return ret;
4483 }
4486 /**
4487 * Get a string value, testing it for proper syntax and
4488 * property names.
4489 */
4490 bool MakeBase::getValue(Element *elem, String &result)
4491 {
4492 String s = elem->getValue();
4493 String tmp;
4494 bool ret = getSubstitutions(s, tmp);
4495 if (ret)
4496 result = s; //assign -if- ok
4497 return ret;
4498 }
4503 /**
4504 * Parse a <patternset> entry
4505 */
4506 bool MakeBase::parsePatternSet(Element *elem,
4507 MakeBase &propRef,
4508 std::vector<String> &includes,
4509 std::vector<String> &excludes
4510 )
4511 {
4512 std::vector<Element *> children = elem->getChildren();
4513 for (unsigned int i=0 ; i<children.size() ; i++)
4514 {
4515 Element *child = children[i];
4516 String tagName = child->getName();
4517 if (tagName == "exclude")
4518 {
4519 String fname;
4520 if (!propRef.getAttribute(child, "name", fname))
4521 return false;
4522 //trace("EXCLUDE: %s", fname.c_str());
4523 excludes.push_back(fname);
4524 }
4525 else if (tagName == "include")
4526 {
4527 String fname;
4528 if (!propRef.getAttribute(child, "name", fname))
4529 return false;
4530 //trace("INCLUDE: %s", fname.c_str());
4531 includes.push_back(fname);
4532 }
4533 }
4535 return true;
4536 }
4541 /**
4542 * Parse a <fileset> entry, and determine which files
4543 * should be included
4544 */
4545 bool MakeBase::parseFileSet(Element *elem,
4546 MakeBase &propRef,
4547 FileSet &fileSet)
4548 {
4549 String name = elem->getName();
4550 if (name != "fileset")
4551 {
4552 error("expected <fileset>");
4553 return false;
4554 }
4557 std::vector<String> includes;
4558 std::vector<String> excludes;
4560 //A fileset has one implied patternset
4561 if (!parsePatternSet(elem, propRef, includes, excludes))
4562 {
4563 return false;
4564 }
4565 //Look for child tags, including more patternsets
4566 std::vector<Element *> children = elem->getChildren();
4567 for (unsigned int i=0 ; i<children.size() ; i++)
4568 {
4569 Element *child = children[i];
4570 String tagName = child->getName();
4571 if (tagName == "patternset")
4572 {
4573 if (!parsePatternSet(child, propRef, includes, excludes))
4574 {
4575 return false;
4576 }
4577 }
4578 }
4580 String dir;
4581 //Now do the stuff
4582 //Get the base directory for reading file names
4583 if (!propRef.getAttribute(elem, "dir", dir))
4584 return false;
4586 fileSet.setDirectory(dir);
4587 fileSet.setIncludes(includes);
4588 fileSet.setExcludes(excludes);
4590 /*
4591 std::vector<String> fileList;
4592 if (dir.size() > 0)
4593 {
4594 String baseDir = propRef.resolve(dir);
4595 if (!listFiles(baseDir, "", includes, excludes, fileList))
4596 return false;
4597 }
4598 std::sort(fileList.begin(), fileList.end());
4599 result = fileList;
4600 */
4603 /*
4604 for (unsigned int i=0 ; i<result.size() ; i++)
4605 {
4606 trace("RES:%s", result[i].c_str());
4607 }
4608 */
4611 return true;
4612 }
4614 /**
4615 * Parse a <filelist> entry. This is far simpler than FileSet,
4616 * since no directory scanning is needed. The file names are listed
4617 * explicitly.
4618 */
4619 bool MakeBase::parseFileList(Element *elem,
4620 MakeBase &propRef,
4621 FileList &fileList)
4622 {
4623 std::vector<String> fnames;
4624 //Look for child tags, namely "file"
4625 std::vector<Element *> children = elem->getChildren();
4626 for (unsigned int i=0 ; i<children.size() ; i++)
4627 {
4628 Element *child = children[i];
4629 String tagName = child->getName();
4630 if (tagName == "file")
4631 {
4632 String fname = child->getAttribute("name");
4633 if (fname.size()==0)
4634 {
4635 error("<file> element requires name="" attribute");
4636 return false;
4637 }
4638 fnames.push_back(fname);
4639 }
4640 else
4641 {
4642 error("tag <%s> not allowed in <fileset>", tagName.c_str());
4643 return false;
4644 }
4645 }
4647 String dir;
4648 //Get the base directory for reading file names
4649 if (!propRef.getAttribute(elem, "dir", dir))
4650 return false;
4651 fileList.setDirectory(dir);
4652 fileList.setFiles(fnames);
4654 return true;
4655 }
4659 /**
4660 * Create a directory, making intermediate dirs
4661 * if necessary
4662 */
4663 bool MakeBase::createDirectory(const String &dirname)
4664 {
4665 //trace("## createDirectory: %s", dirname.c_str());
4666 //## first check if it exists
4667 struct stat finfo;
4668 String nativeDir = getNativePath(dirname);
4669 char *cnative = (char *) nativeDir.c_str();
4670 #ifdef __WIN32__
4671 if (strlen(cnative)==2 && cnative[1]==':')
4672 return true;
4673 #endif
4674 if (stat(cnative, &finfo)==0)
4675 {
4676 if (!S_ISDIR(finfo.st_mode))
4677 {
4678 error("mkdir: file %s exists but is not a directory",
4679 cnative);
4680 return false;
4681 }
4682 else //exists
4683 {
4684 return true;
4685 }
4686 }
4688 //## 2: pull off the last path segment, if any,
4689 //## to make the dir 'above' this one, if necessary
4690 unsigned int pos = dirname.find_last_of('/');
4691 if (pos>0 && pos != dirname.npos)
4692 {
4693 String subpath = dirname.substr(0, pos);
4694 //A letter root (c:) ?
4695 if (!createDirectory(subpath))
4696 return false;
4697 }
4699 //## 3: now make
4700 #ifdef __WIN32__
4701 if (mkdir(cnative)<0)
4702 #else
4703 if (mkdir(cnative, S_IRWXU | S_IRWXG | S_IRWXO)<0)
4704 #endif
4705 {
4706 error("cannot make directory '%s' : %s",
4707 cnative, strerror(errno));
4708 return false;
4709 }
4711 return true;
4712 }
4715 /**
4716 * Remove a directory recursively
4717 */
4718 bool MakeBase::removeDirectory(const String &dirName)
4719 {
4720 char *dname = (char *)dirName.c_str();
4722 DIR *dir = opendir(dname);
4723 if (!dir)
4724 {
4725 //# Let this fail nicely.
4726 return true;
4727 //error("error opening directory %s : %s", dname, strerror(errno));
4728 //return false;
4729 }
4731 while (true)
4732 {
4733 struct dirent *de = readdir(dir);
4734 if (!de)
4735 break;
4737 //Get the directory member name
4738 String s = de->d_name;
4739 if (s.size() == 0 || s[0] == '.')
4740 continue;
4741 String childName;
4742 if (dirName.size() > 0)
4743 {
4744 childName.append(dirName);
4745 childName.append("/");
4746 }
4747 childName.append(s);
4750 struct stat finfo;
4751 String childNative = getNativePath(childName);
4752 char *cnative = (char *)childNative.c_str();
4753 if (stat(cnative, &finfo)<0)
4754 {
4755 error("cannot stat file:%s", cnative);
4756 }
4757 else if (S_ISDIR(finfo.st_mode))
4758 {
4759 //trace("DEL dir: %s", childName.c_str());
4760 if (!removeDirectory(childName))
4761 {
4762 return false;
4763 }
4764 }
4765 else if (!S_ISREG(finfo.st_mode))
4766 {
4767 //trace("not regular: %s", cnative);
4768 }
4769 else
4770 {
4771 //trace("DEL file: %s", childName.c_str());
4772 if (remove(cnative)<0)
4773 {
4774 error("error deleting %s : %s",
4775 cnative, strerror(errno));
4776 return false;
4777 }
4778 }
4779 }
4780 closedir(dir);
4782 //Now delete the directory
4783 String native = getNativePath(dirName);
4784 if (rmdir(native.c_str())<0)
4785 {
4786 error("could not delete directory %s : %s",
4787 native.c_str() , strerror(errno));
4788 return false;
4789 }
4791 return true;
4793 }
4796 /**
4797 * Copy a file from one name to another. Perform only if needed
4798 */
4799 bool MakeBase::copyFile(const String &srcFile, const String &destFile)
4800 {
4801 //# 1 Check up-to-date times
4802 String srcNative = getNativePath(srcFile);
4803 struct stat srcinfo;
4804 if (stat(srcNative.c_str(), &srcinfo)<0)
4805 {
4806 error("source file %s for copy does not exist",
4807 srcNative.c_str());
4808 return false;
4809 }
4811 String destNative = getNativePath(destFile);
4812 struct stat destinfo;
4813 if (stat(destNative.c_str(), &destinfo)==0)
4814 {
4815 if (destinfo.st_mtime >= srcinfo.st_mtime)
4816 return true;
4817 }
4819 //# 2 prepare a destination directory if necessary
4820 unsigned int pos = destFile.find_last_of('/');
4821 if (pos != destFile.npos)
4822 {
4823 String subpath = destFile.substr(0, pos);
4824 if (!createDirectory(subpath))
4825 return false;
4826 }
4828 //# 3 do the data copy
4829 #ifndef __WIN32__
4831 FILE *srcf = fopen(srcNative.c_str(), "rb");
4832 if (!srcf)
4833 {
4834 error("copyFile cannot open '%s' for reading", srcNative.c_str());
4835 return false;
4836 }
4837 FILE *destf = fopen(destNative.c_str(), "wb");
4838 if (!destf)
4839 {
4840 error("copyFile cannot open %s for writing", srcNative.c_str());
4841 return false;
4842 }
4844 while (!feof(srcf))
4845 {
4846 int ch = fgetc(srcf);
4847 if (ch<0)
4848 break;
4849 fputc(ch, destf);
4850 }
4852 fclose(destf);
4853 fclose(srcf);
4855 #else
4857 if (!CopyFile(srcNative.c_str(), destNative.c_str(), false))
4858 {
4859 error("copyFile from %s to %s failed",
4860 srcNative.c_str(), destNative.c_str());
4861 return false;
4862 }
4864 #endif /* __WIN32__ */
4867 return true;
4868 }
4872 /**
4873 * Tests if the file exists and is a regular file
4874 */
4875 bool MakeBase::isRegularFile(const String &fileName)
4876 {
4877 String native = getNativePath(fileName);
4878 struct stat finfo;
4880 //Exists?
4881 if (stat(native.c_str(), &finfo)<0)
4882 return false;
4885 //check the file mode
4886 if (!S_ISREG(finfo.st_mode))
4887 return false;
4889 return true;
4890 }
4892 /**
4893 * Tests if the file exists and is a directory
4894 */
4895 bool MakeBase::isDirectory(const String &fileName)
4896 {
4897 String native = getNativePath(fileName);
4898 struct stat finfo;
4900 //Exists?
4901 if (stat(native.c_str(), &finfo)<0)
4902 return false;
4905 //check the file mode
4906 if (!S_ISDIR(finfo.st_mode))
4907 return false;
4909 return true;
4910 }
4914 /**
4915 * Tests is the modification of fileA is newer than fileB
4916 */
4917 bool MakeBase::isNewerThan(const String &fileA, const String &fileB)
4918 {
4919 //trace("isNewerThan:'%s' , '%s'", fileA.c_str(), fileB.c_str());
4920 String nativeA = getNativePath(fileA);
4921 struct stat infoA;
4922 //IF source does not exist, NOT newer
4923 if (stat(nativeA.c_str(), &infoA)<0)
4924 {
4925 return false;
4926 }
4928 String nativeB = getNativePath(fileB);
4929 struct stat infoB;
4930 //IF dest does not exist, YES, newer
4931 if (stat(nativeB.c_str(), &infoB)<0)
4932 {
4933 return true;
4934 }
4936 //check the actual times
4937 if (infoA.st_mtime > infoB.st_mtime)
4938 {
4939 return true;
4940 }
4942 return false;
4943 }
4946 //########################################################################
4947 //# P K G C O N F I G
4948 //########################################################################
4951 /**
4952 * Get a character from the buffer at pos. If out of range,
4953 * return -1 for safety
4954 */
4955 int PkgConfig::get(int pos)
4956 {
4957 if (pos>parselen)
4958 return -1;
4959 return parsebuf[pos];
4960 }
4964 /**
4965 * Skip over all whitespace characters beginning at pos. Return
4966 * the position of the first non-whitespace character.
4967 * Pkg-config is line-oriented, so check for newline
4968 */
4969 int PkgConfig::skipwhite(int pos)
4970 {
4971 while (pos < parselen)
4972 {
4973 int ch = get(pos);
4974 if (ch < 0)
4975 break;
4976 if (!isspace(ch))
4977 break;
4978 pos++;
4979 }
4980 return pos;
4981 }
4984 /**
4985 * Parse the buffer beginning at pos, for a word. Fill
4986 * 'ret' with the result. Return the position after the
4987 * word.
4988 */
4989 int PkgConfig::getword(int pos, String &ret)
4990 {
4991 while (pos < parselen)
4992 {
4993 int ch = get(pos);
4994 if (ch < 0)
4995 break;
4996 if (!isalnum(ch) && ch != '_' && ch != '-' && ch != '+' && ch != '.')
4997 break;
4998 ret.push_back((char)ch);
4999 pos++;
5000 }
5001 return pos;
5002 }
5004 bool PkgConfig::parseRequires()
5005 {
5006 if (requires.size() == 0)
5007 return true;
5008 parsebuf = (char *)requires.c_str();
5009 parselen = requires.size();
5010 int pos = 0;
5011 while (pos < parselen)
5012 {
5013 pos = skipwhite(pos);
5014 String val;
5015 int pos2 = getword(pos, val);
5016 if (pos2 == pos)
5017 break;
5018 pos = pos2;
5019 //trace("val %s", val.c_str());
5020 requireList.push_back(val);
5021 }
5022 return true;
5023 }
5026 static int getint(const String str)
5027 {
5028 char *s = (char *)str.c_str();
5029 char *ends = NULL;
5030 long val = strtol(s, &ends, 10);
5031 if (ends == s)
5032 return 0L;
5033 else
5034 return val;
5035 }
5037 void PkgConfig::parseVersion()
5038 {
5039 if (version.size() == 0)
5040 return;
5041 String s1, s2, s3;
5042 unsigned int pos = 0;
5043 unsigned int pos2 = version.find('.', pos);
5044 if (pos2 == version.npos)
5045 {
5046 s1 = version;
5047 }
5048 else
5049 {
5050 s1 = version.substr(pos, pos2-pos);
5051 pos = pos2;
5052 pos++;
5053 if (pos < version.size())
5054 {
5055 pos2 = version.find('.', pos);
5056 if (pos2 == version.npos)
5057 {
5058 s2 = version.substr(pos, version.size()-pos);
5059 }
5060 else
5061 {
5062 s2 = version.substr(pos, pos2-pos);
5063 pos = pos2;
5064 pos++;
5065 if (pos < version.size())
5066 s3 = version.substr(pos, pos2-pos);
5067 }
5068 }
5069 }
5071 majorVersion = getint(s1);
5072 minorVersion = getint(s2);
5073 microVersion = getint(s3);
5074 //trace("version:%d.%d.%d", majorVersion,
5075 // minorVersion, microVersion );
5076 }
5079 bool PkgConfig::parseLine(const String &lineBuf)
5080 {
5081 parsebuf = (char *)lineBuf.c_str();
5082 parselen = lineBuf.size();
5083 int pos = 0;
5085 while (pos < parselen)
5086 {
5087 String attrName;
5088 pos = skipwhite(pos);
5089 int ch = get(pos);
5090 if (ch == '#')
5091 {
5092 //comment. eat the rest of the line
5093 while (pos < parselen)
5094 {
5095 ch = get(pos);
5096 if (ch == '\n' || ch < 0)
5097 break;
5098 pos++;
5099 }
5100 continue;
5101 }
5102 pos = getword(pos, attrName);
5103 if (attrName.size() == 0)
5104 continue;
5106 pos = skipwhite(pos);
5107 ch = get(pos);
5108 if (ch != ':' && ch != '=')
5109 {
5110 error("expected ':' or '='");
5111 return false;
5112 }
5113 pos++;
5114 pos = skipwhite(pos);
5115 String attrVal;
5116 while (pos < parselen)
5117 {
5118 ch = get(pos);
5119 if (ch == '\n' || ch < 0)
5120 break;
5121 else if (ch == '$' && get(pos+1) == '{')
5122 {
5123 //# this is a ${substitution}
5124 pos += 2;
5125 String subName;
5126 while (pos < parselen)
5127 {
5128 ch = get(pos);
5129 if (ch < 0)
5130 {
5131 error("unterminated substitution");
5132 return false;
5133 }
5134 else if (ch == '}')
5135 break;
5136 else
5137 subName.push_back((char)ch);
5138 pos++;
5139 }
5140 //trace("subName:%s %s", subName.c_str(), prefix.c_str());
5141 if (subName == "prefix" && prefix.size()>0)
5142 {
5143 attrVal.append(prefix);
5144 //trace("prefix override:%s", prefix.c_str());
5145 }
5146 else
5147 {
5148 String subVal = attrs[subName];
5149 //trace("subVal:%s", subVal.c_str());
5150 attrVal.append(subVal);
5151 }
5152 }
5153 else
5154 attrVal.push_back((char)ch);
5155 pos++;
5156 }
5158 attrVal = trim(attrVal);
5159 attrs[attrName] = attrVal;
5161 String attrNameL = toLower(attrName);
5163 if (attrNameL == "name")
5164 name = attrVal;
5165 else if (attrNameL == "description")
5166 description = attrVal;
5167 else if (attrNameL == "cflags")
5168 cflags = attrVal;
5169 else if (attrNameL == "libs")
5170 libs = attrVal;
5171 else if (attrNameL == "requires")
5172 requires = attrVal;
5173 else if (attrNameL == "version")
5174 version = attrVal;
5176 //trace("name:'%s' value:'%s'",
5177 // attrName.c_str(), attrVal.c_str());
5178 }
5180 return true;
5181 }
5184 bool PkgConfig::parse(const String &buf)
5185 {
5186 init();
5188 String line;
5189 int lineNr = 0;
5190 for (unsigned int p=0 ; p<buf.size() ; p++)
5191 {
5192 int ch = buf[p];
5193 if (ch == '\n' || ch == '\r')
5194 {
5195 if (!parseLine(line))
5196 return false;
5197 line.clear();
5198 lineNr++;
5199 }
5200 else
5201 {
5202 line.push_back(ch);
5203 }
5204 }
5205 if (line.size()>0)
5206 {
5207 if (!parseLine(line))
5208 return false;
5209 }
5211 parseRequires();
5212 parseVersion();
5214 return true;
5215 }
5220 void PkgConfig::dumpAttrs()
5221 {
5222 //trace("### PkgConfig attributes for %s", fileName.c_str());
5223 std::map<String, String>::iterator iter;
5224 for (iter=attrs.begin() ; iter!=attrs.end() ; iter++)
5225 {
5226 trace(" %s = %s", iter->first.c_str(), iter->second.c_str());
5227 }
5228 }
5231 bool PkgConfig::readFile(const String &fname)
5232 {
5233 fileName = getNativePath(fname);
5235 FILE *f = fopen(fileName.c_str(), "r");
5236 if (!f)
5237 {
5238 error("cannot open file '%s' for reading", fileName.c_str());
5239 return false;
5240 }
5241 String buf;
5242 while (true)
5243 {
5244 int ch = fgetc(f);
5245 if (ch < 0)
5246 break;
5247 buf.push_back((char)ch);
5248 }
5249 fclose(f);
5251 //trace("####### File:\n%s", buf.c_str());
5252 if (!parse(buf))
5253 {
5254 return false;
5255 }
5257 //dumpAttrs();
5259 return true;
5260 }
5264 bool PkgConfig::query(const String &pkgName)
5265 {
5266 name = pkgName;
5268 String fname = path;
5269 fname.append("/");
5270 fname.append(name);
5271 fname.append(".pc");
5273 if (!readFile(fname))
5274 return false;
5276 return true;
5277 }
5283 //########################################################################
5284 //# D E P T O O L
5285 //########################################################################
5289 /**
5290 * Class which holds information for each file.
5291 */
5292 class FileRec
5293 {
5294 public:
5296 typedef enum
5297 {
5298 UNKNOWN,
5299 CFILE,
5300 HFILE,
5301 OFILE
5302 } FileType;
5304 /**
5305 * Constructor
5306 */
5307 FileRec()
5308 { init(); type = UNKNOWN; }
5310 /**
5311 * Copy constructor
5312 */
5313 FileRec(const FileRec &other)
5314 { init(); assign(other); }
5315 /**
5316 * Constructor
5317 */
5318 FileRec(int typeVal)
5319 { init(); type = typeVal; }
5320 /**
5321 * Assignment operator
5322 */
5323 FileRec &operator=(const FileRec &other)
5324 { init(); assign(other); return *this; }
5327 /**
5328 * Destructor
5329 */
5330 ~FileRec()
5331 {}
5333 /**
5334 * Directory part of the file name
5335 */
5336 String path;
5338 /**
5339 * Base name, sans directory and suffix
5340 */
5341 String baseName;
5343 /**
5344 * File extension, such as cpp or h
5345 */
5346 String suffix;
5348 /**
5349 * Type of file: CFILE, HFILE, OFILE
5350 */
5351 int type;
5353 /**
5354 * Used to list files ref'd by this one
5355 */
5356 std::map<String, FileRec *> files;
5359 private:
5361 void init()
5362 {
5363 }
5365 void assign(const FileRec &other)
5366 {
5367 type = other.type;
5368 baseName = other.baseName;
5369 suffix = other.suffix;
5370 files = other.files;
5371 }
5373 };
5377 /**
5378 * Simpler dependency record
5379 */
5380 class DepRec
5381 {
5382 public:
5384 /**
5385 * Constructor
5386 */
5387 DepRec()
5388 {init();}
5390 /**
5391 * Copy constructor
5392 */
5393 DepRec(const DepRec &other)
5394 {init(); assign(other);}
5395 /**
5396 * Constructor
5397 */
5398 DepRec(const String &fname)
5399 {init(); name = fname; }
5400 /**
5401 * Assignment operator
5402 */
5403 DepRec &operator=(const DepRec &other)
5404 {init(); assign(other); return *this;}
5407 /**
5408 * Destructor
5409 */
5410 ~DepRec()
5411 {}
5413 /**
5414 * Directory part of the file name
5415 */
5416 String path;
5418 /**
5419 * Base name, without the path and suffix
5420 */
5421 String name;
5423 /**
5424 * Suffix of the source
5425 */
5426 String suffix;
5429 /**
5430 * Used to list files ref'd by this one
5431 */
5432 std::vector<String> files;
5435 private:
5437 void init()
5438 {
5439 }
5441 void assign(const DepRec &other)
5442 {
5443 path = other.path;
5444 name = other.name;
5445 suffix = other.suffix;
5446 files = other.files; //avoid recursion
5447 }
5449 };
5452 class DepTool : public MakeBase
5453 {
5454 public:
5456 /**
5457 * Constructor
5458 */
5459 DepTool()
5460 { init(); }
5462 /**
5463 * Copy constructor
5464 */
5465 DepTool(const DepTool &other)
5466 { init(); assign(other); }
5468 /**
5469 * Assignment operator
5470 */
5471 DepTool &operator=(const DepTool &other)
5472 { init(); assign(other); return *this; }
5475 /**
5476 * Destructor
5477 */
5478 ~DepTool()
5479 {}
5482 /**
5483 * Reset this section of code
5484 */
5485 virtual void init();
5487 /**
5488 * Reset this section of code
5489 */
5490 virtual void assign(const DepTool &other)
5491 {
5492 }
5494 /**
5495 * Sets the source directory which will be scanned
5496 */
5497 virtual void setSourceDirectory(const String &val)
5498 { sourceDir = val; }
5500 /**
5501 * Returns the source directory which will be scanned
5502 */
5503 virtual String getSourceDirectory()
5504 { return sourceDir; }
5506 /**
5507 * Sets the list of files within the directory to analyze
5508 */
5509 virtual void setFileList(const std::vector<String> &list)
5510 { fileList = list; }
5512 /**
5513 * Creates the list of all file names which will be
5514 * candidates for further processing. Reads make.exclude
5515 * to see which files for directories to leave out.
5516 */
5517 virtual bool createFileList();
5520 /**
5521 * Generates the forward dependency list
5522 */
5523 virtual bool generateDependencies();
5526 /**
5527 * Generates the forward dependency list, saving the file
5528 */
5529 virtual bool generateDependencies(const String &);
5532 /**
5533 * Load a dependency file
5534 */
5535 std::vector<DepRec> loadDepFile(const String &fileName);
5537 /**
5538 * Load a dependency file, generating one if necessary
5539 */
5540 std::vector<DepRec> getDepFile(const String &fileName,
5541 bool forceRefresh);
5543 /**
5544 * Save a dependency file
5545 */
5546 bool saveDepFile(const String &fileName);
5549 private:
5552 /**
5553 *
5554 */
5555 void parseName(const String &fullname,
5556 String &path,
5557 String &basename,
5558 String &suffix);
5560 /**
5561 *
5562 */
5563 int get(int pos);
5565 /**
5566 *
5567 */
5568 int skipwhite(int pos);
5570 /**
5571 *
5572 */
5573 int getword(int pos, String &ret);
5575 /**
5576 *
5577 */
5578 bool sequ(int pos, const char *key);
5580 /**
5581 *
5582 */
5583 bool addIncludeFile(FileRec *frec, const String &fname);
5585 /**
5586 *
5587 */
5588 bool scanFile(const String &fname, FileRec *frec);
5590 /**
5591 *
5592 */
5593 bool processDependency(FileRec *ofile, FileRec *include);
5595 /**
5596 *
5597 */
5598 String sourceDir;
5600 /**
5601 *
5602 */
5603 std::vector<String> fileList;
5605 /**
5606 *
5607 */
5608 std::vector<String> directories;
5610 /**
5611 * A list of all files which will be processed for
5612 * dependencies.
5613 */
5614 std::map<String, FileRec *> allFiles;
5616 /**
5617 * The list of .o files, and the
5618 * dependencies upon them.
5619 */
5620 std::map<String, FileRec *> oFiles;
5622 int depFileSize;
5623 char *depFileBuf;
5625 static const int readBufSize = 8192;
5626 char readBuf[8193];//byte larger
5628 };
5634 /**
5635 * Clean up after processing. Called by the destructor, but should
5636 * also be called before the object is reused.
5637 */
5638 void DepTool::init()
5639 {
5640 sourceDir = ".";
5642 fileList.clear();
5643 directories.clear();
5645 //clear output file list
5646 std::map<String, FileRec *>::iterator iter;
5647 for (iter=oFiles.begin(); iter!=oFiles.end() ; iter++)
5648 delete iter->second;
5649 oFiles.clear();
5651 //allFiles actually contains the master copies. delete them
5652 for (iter= allFiles.begin(); iter!=allFiles.end() ; iter++)
5653 delete iter->second;
5654 allFiles.clear();
5656 }
5661 /**
5662 * Parse a full path name into path, base name, and suffix
5663 */
5664 void DepTool::parseName(const String &fullname,
5665 String &path,
5666 String &basename,
5667 String &suffix)
5668 {
5669 if (fullname.size() < 2)
5670 return;
5672 unsigned int pos = fullname.find_last_of('/');
5673 if (pos != fullname.npos && pos<fullname.size()-1)
5674 {
5675 path = fullname.substr(0, pos);
5676 pos++;
5677 basename = fullname.substr(pos, fullname.size()-pos);
5678 }
5679 else
5680 {
5681 path = "";
5682 basename = fullname;
5683 }
5685 pos = basename.find_last_of('.');
5686 if (pos != basename.npos && pos<basename.size()-1)
5687 {
5688 suffix = basename.substr(pos+1, basename.size()-pos-1);
5689 basename = basename.substr(0, pos);
5690 }
5692 //trace("parsename:%s %s %s", path.c_str(),
5693 // basename.c_str(), suffix.c_str());
5694 }
5698 /**
5699 * Generate our internal file list.
5700 */
5701 bool DepTool::createFileList()
5702 {
5704 for (unsigned int i=0 ; i<fileList.size() ; i++)
5705 {
5706 String fileName = fileList[i];
5707 //trace("## FileName:%s", fileName.c_str());
5708 String path;
5709 String basename;
5710 String sfx;
5711 parseName(fileName, path, basename, sfx);
5712 if (sfx == "cpp" || sfx == "c" || sfx == "cxx" ||
5713 sfx == "cc" || sfx == "CC")
5714 {
5715 FileRec *fe = new FileRec(FileRec::CFILE);
5716 fe->path = path;
5717 fe->baseName = basename;
5718 fe->suffix = sfx;
5719 allFiles[fileName] = fe;
5720 }
5721 else if (sfx == "h" || sfx == "hh" ||
5722 sfx == "hpp" || sfx == "hxx")
5723 {
5724 FileRec *fe = new FileRec(FileRec::HFILE);
5725 fe->path = path;
5726 fe->baseName = basename;
5727 fe->suffix = sfx;
5728 allFiles[fileName] = fe;
5729 }
5730 }
5732 if (!listDirectories(sourceDir, "", directories))
5733 return false;
5735 return true;
5736 }
5742 /**
5743 * Get a character from the buffer at pos. If out of range,
5744 * return -1 for safety
5745 */
5746 int DepTool::get(int pos)
5747 {
5748 if (pos>depFileSize)
5749 return -1;
5750 return depFileBuf[pos];
5751 }
5755 /**
5756 * Skip over all whitespace characters beginning at pos. Return
5757 * the position of the first non-whitespace character.
5758 */
5759 int DepTool::skipwhite(int pos)
5760 {
5761 while (pos < depFileSize)
5762 {
5763 int ch = get(pos);
5764 if (ch < 0)
5765 break;
5766 if (!isspace(ch))
5767 break;
5768 pos++;
5769 }
5770 return pos;
5771 }
5774 /**
5775 * Parse the buffer beginning at pos, for a word. Fill
5776 * 'ret' with the result. Return the position after the
5777 * word.
5778 */
5779 int DepTool::getword(int pos, String &ret)
5780 {
5781 while (pos < depFileSize)
5782 {
5783 int ch = get(pos);
5784 if (ch < 0)
5785 break;
5786 if (isspace(ch))
5787 break;
5788 ret.push_back((char)ch);
5789 pos++;
5790 }
5791 return pos;
5792 }
5794 /**
5795 * Return whether the sequence of characters in the buffer
5796 * beginning at pos match the key, for the length of the key
5797 */
5798 bool DepTool::sequ(int pos, const char *key)
5799 {
5800 while (*key)
5801 {
5802 if (*key != get(pos))
5803 return false;
5804 key++; pos++;
5805 }
5806 return true;
5807 }
5811 /**
5812 * Add an include file name to a file record. If the name
5813 * is not found in allFiles explicitly, try prepending include
5814 * directory names to it and try again.
5815 */
5816 bool DepTool::addIncludeFile(FileRec *frec, const String &iname)
5817 {
5818 //# if the name is an exact match to a path name
5819 //# in allFiles, like "myinc.h"
5820 std::map<String, FileRec *>::iterator iter =
5821 allFiles.find(iname);
5822 if (iter != allFiles.end()) //already exists
5823 {
5824 //h file in same dir
5825 FileRec *other = iter->second;
5826 //trace("local: '%s'", iname.c_str());
5827 frec->files[iname] = other;
5828 return true;
5829 }
5830 else
5831 {
5832 //## Ok, it was not found directly
5833 //look in other dirs
5834 std::vector<String>::iterator diter;
5835 for (diter=directories.begin() ;
5836 diter!=directories.end() ; diter++)
5837 {
5838 String dfname = *diter;
5839 dfname.append("/");
5840 dfname.append(iname);
5841 URI fullPathURI(dfname); //normalize path name
5842 String fullPath = fullPathURI.getPath();
5843 if (fullPath[0] == '/')
5844 fullPath = fullPath.substr(1);
5845 //trace("Normalized %s to %s", dfname.c_str(), fullPath.c_str());
5846 iter = allFiles.find(fullPath);
5847 if (iter != allFiles.end())
5848 {
5849 FileRec *other = iter->second;
5850 //trace("other: '%s'", iname.c_str());
5851 frec->files[fullPath] = other;
5852 return true;
5853 }
5854 }
5855 }
5856 return true;
5857 }
5861 /**
5862 * Lightly parse a file to find the #include directives. Do
5863 * a bit of state machine stuff to make sure that the directive
5864 * is valid. (Like not in a comment).
5865 */
5866 bool DepTool::scanFile(const String &fname, FileRec *frec)
5867 {
5868 String fileName;
5869 if (sourceDir.size() > 0)
5870 {
5871 fileName.append(sourceDir);
5872 fileName.append("/");
5873 }
5874 fileName.append(fname);
5875 String nativeName = getNativePath(fileName);
5876 FILE *f = fopen(nativeName.c_str(), "r");
5877 if (!f)
5878 {
5879 error("Could not open '%s' for reading", fname.c_str());
5880 return false;
5881 }
5882 String buf;
5883 while (!feof(f))
5884 {
5885 int nrbytes = fread(readBuf, 1, readBufSize, f);
5886 readBuf[nrbytes] = '\0';
5887 buf.append(readBuf);
5888 }
5889 fclose(f);
5891 depFileSize = buf.size();
5892 depFileBuf = (char *)buf.c_str();
5893 int pos = 0;
5896 while (pos < depFileSize)
5897 {
5898 //trace("p:%c", get(pos));
5900 //# Block comment
5901 if (get(pos) == '/' && get(pos+1) == '*')
5902 {
5903 pos += 2;
5904 while (pos < depFileSize)
5905 {
5906 if (get(pos) == '*' && get(pos+1) == '/')
5907 {
5908 pos += 2;
5909 break;
5910 }
5911 else
5912 pos++;
5913 }
5914 }
5915 //# Line comment
5916 else if (get(pos) == '/' && get(pos+1) == '/')
5917 {
5918 pos += 2;
5919 while (pos < depFileSize)
5920 {
5921 if (get(pos) == '\n')
5922 {
5923 pos++;
5924 break;
5925 }
5926 else
5927 pos++;
5928 }
5929 }
5930 //# #include! yaay
5931 else if (sequ(pos, "#include"))
5932 {
5933 pos += 8;
5934 pos = skipwhite(pos);
5935 String iname;
5936 pos = getword(pos, iname);
5937 if (iname.size()>2)
5938 {
5939 iname = iname.substr(1, iname.size()-2);
5940 addIncludeFile(frec, iname);
5941 }
5942 }
5943 else
5944 {
5945 pos++;
5946 }
5947 }
5949 return true;
5950 }
5954 /**
5955 * Recursively check include lists to find all files in allFiles to which
5956 * a given file is dependent.
5957 */
5958 bool DepTool::processDependency(FileRec *ofile, FileRec *include)
5959 {
5960 std::map<String, FileRec *>::iterator iter;
5961 for (iter=include->files.begin() ; iter!=include->files.end() ; iter++)
5962 {
5963 String fname = iter->first;
5964 if (ofile->files.find(fname) != ofile->files.end())
5965 {
5966 //trace("file '%s' already seen", fname.c_str());
5967 continue;
5968 }
5969 FileRec *child = iter->second;
5970 ofile->files[fname] = child;
5972 processDependency(ofile, child);
5973 }
5976 return true;
5977 }
5983 /**
5984 * Generate the file dependency list.
5985 */
5986 bool DepTool::generateDependencies()
5987 {
5988 std::map<String, FileRec *>::iterator iter;
5989 //# First pass. Scan for all includes
5990 for (iter=allFiles.begin() ; iter!=allFiles.end() ; iter++)
5991 {
5992 FileRec *frec = iter->second;
5993 if (!scanFile(iter->first, frec))
5994 {
5995 //quit?
5996 }
5997 }
5999 //# Second pass. Scan for all includes
6000 for (iter=allFiles.begin() ; iter!=allFiles.end() ; iter++)
6001 {
6002 FileRec *include = iter->second;
6003 if (include->type == FileRec::CFILE)
6004 {
6005 //String cFileName = iter->first;
6006 FileRec *ofile = new FileRec(FileRec::OFILE);
6007 ofile->path = include->path;
6008 ofile->baseName = include->baseName;
6009 ofile->suffix = include->suffix;
6010 String fname = include->path;
6011 if (fname.size()>0)
6012 fname.append("/");
6013 fname.append(include->baseName);
6014 fname.append(".o");
6015 oFiles[fname] = ofile;
6016 //add the .c file first? no, don't
6017 //ofile->files[cFileName] = include;
6019 //trace("ofile:%s", fname.c_str());
6021 processDependency(ofile, include);
6022 }
6023 }
6026 return true;
6027 }
6031 /**
6032 * High-level call to generate deps and optionally save them
6033 */
6034 bool DepTool::generateDependencies(const String &fileName)
6035 {
6036 if (!createFileList())
6037 return false;
6038 if (!generateDependencies())
6039 return false;
6040 if (!saveDepFile(fileName))
6041 return false;
6042 return true;
6043 }
6046 /**
6047 * This saves the dependency cache.
6048 */
6049 bool DepTool::saveDepFile(const String &fileName)
6050 {
6051 time_t tim;
6052 time(&tim);
6054 FILE *f = fopen(fileName.c_str(), "w");
6055 if (!f)
6056 {
6057 trace("cannot open '%s' for writing", fileName.c_str());
6058 }
6059 fprintf(f, "<?xml version='1.0'?>\n");
6060 fprintf(f, "<!--\n");
6061 fprintf(f, "########################################################\n");
6062 fprintf(f, "## File: build.dep\n");
6063 fprintf(f, "## Generated by BuildTool at :%s", ctime(&tim));
6064 fprintf(f, "########################################################\n");
6065 fprintf(f, "-->\n");
6067 fprintf(f, "<dependencies source='%s'>\n\n", sourceDir.c_str());
6068 std::map<String, FileRec *>::iterator iter;
6069 for (iter=oFiles.begin() ; iter!=oFiles.end() ; iter++)
6070 {
6071 FileRec *frec = iter->second;
6072 if (frec->type == FileRec::OFILE)
6073 {
6074 fprintf(f, "<object path='%s' name='%s' suffix='%s'>\n",
6075 frec->path.c_str(), frec->baseName.c_str(), frec->suffix.c_str());
6076 std::map<String, FileRec *>::iterator citer;
6077 for (citer=frec->files.begin() ; citer!=frec->files.end() ; citer++)
6078 {
6079 String cfname = citer->first;
6080 fprintf(f, " <dep name='%s'/>\n", cfname.c_str());
6081 }
6082 fprintf(f, "</object>\n\n");
6083 }
6084 }
6086 fprintf(f, "</dependencies>\n");
6087 fprintf(f, "\n");
6088 fprintf(f, "<!--\n");
6089 fprintf(f, "########################################################\n");
6090 fprintf(f, "## E N D\n");
6091 fprintf(f, "########################################################\n");
6092 fprintf(f, "-->\n");
6094 fclose(f);
6096 return true;
6097 }
6102 /**
6103 * This loads the dependency cache.
6104 */
6105 std::vector<DepRec> DepTool::loadDepFile(const String &depFile)
6106 {
6107 std::vector<DepRec> result;
6109 Parser parser;
6110 Element *root = parser.parseFile(depFile.c_str());
6111 if (!root)
6112 {
6113 //error("Could not open %s for reading", depFile.c_str());
6114 return result;
6115 }
6117 if (root->getChildren().size()==0 ||
6118 root->getChildren()[0]->getName()!="dependencies")
6119 {
6120 error("loadDepFile: main xml element should be <dependencies>");
6121 delete root;
6122 return result;
6123 }
6125 //########## Start parsing
6126 Element *depList = root->getChildren()[0];
6128 std::vector<Element *> objects = depList->getChildren();
6129 for (unsigned int i=0 ; i<objects.size() ; i++)
6130 {
6131 Element *objectElem = objects[i];
6132 String tagName = objectElem->getName();
6133 if (tagName != "object")
6134 {
6135 error("loadDepFile: <dependencies> should have only <object> children");
6136 return result;
6137 }
6139 String objName = objectElem->getAttribute("name");
6140 //trace("object:%s", objName.c_str());
6141 DepRec depObject(objName);
6142 depObject.path = objectElem->getAttribute("path");
6143 depObject.suffix = objectElem->getAttribute("suffix");
6144 //########## DESCRIPTION
6145 std::vector<Element *> depElems = objectElem->getChildren();
6146 for (unsigned int i=0 ; i<depElems.size() ; i++)
6147 {
6148 Element *depElem = depElems[i];
6149 tagName = depElem->getName();
6150 if (tagName != "dep")
6151 {
6152 error("loadDepFile: <object> should have only <dep> children");
6153 return result;
6154 }
6155 String depName = depElem->getAttribute("name");
6156 //trace(" dep:%s", depName.c_str());
6157 depObject.files.push_back(depName);
6158 }
6160 //Insert into the result list, in a sorted manner
6161 bool inserted = false;
6162 std::vector<DepRec>::iterator iter;
6163 for (iter = result.begin() ; iter != result.end() ; iter++)
6164 {
6165 String vpath = iter->path;
6166 vpath.append("/");
6167 vpath.append(iter->name);
6168 String opath = depObject.path;
6169 opath.append("/");
6170 opath.append(depObject.name);
6171 if (vpath > opath)
6172 {
6173 inserted = true;
6174 iter = result.insert(iter, depObject);
6175 break;
6176 }
6177 }
6178 if (!inserted)
6179 result.push_back(depObject);
6180 }
6182 delete root;
6184 return result;
6185 }
6188 /**
6189 * This loads the dependency cache.
6190 */
6191 std::vector<DepRec> DepTool::getDepFile(const String &depFile,
6192 bool forceRefresh)
6193 {
6194 std::vector<DepRec> result;
6195 if (forceRefresh)
6196 {
6197 generateDependencies(depFile);
6198 result = loadDepFile(depFile);
6199 }
6200 else
6201 {
6202 //try once
6203 result = loadDepFile(depFile);
6204 if (result.size() == 0)
6205 {
6206 //fail? try again
6207 generateDependencies(depFile);
6208 result = loadDepFile(depFile);
6209 }
6210 }
6211 return result;
6212 }
6217 //########################################################################
6218 //# T A S K
6219 //########################################################################
6220 //forward decl
6221 class Target;
6222 class Make;
6224 /**
6225 *
6226 */
6227 class Task : public MakeBase
6228 {
6230 public:
6232 typedef enum
6233 {
6234 TASK_NONE,
6235 TASK_CC,
6236 TASK_COPY,
6237 TASK_DELETE,
6238 TASK_ECHO,
6239 TASK_JAR,
6240 TASK_JAVAC,
6241 TASK_LINK,
6242 TASK_MAKEFILE,
6243 TASK_MKDIR,
6244 TASK_MSGFMT,
6245 TASK_PKG_CONFIG,
6246 TASK_RANLIB,
6247 TASK_RC,
6248 TASK_SHAREDLIB,
6249 TASK_STATICLIB,
6250 TASK_STRIP,
6251 TASK_TOUCH,
6252 TASK_TSTAMP
6253 } TaskType;
6256 /**
6257 *
6258 */
6259 Task(MakeBase &par) : parent(par)
6260 { init(); }
6262 /**
6263 *
6264 */
6265 Task(const Task &other) : parent(other.parent)
6266 { init(); assign(other); }
6268 /**
6269 *
6270 */
6271 Task &operator=(const Task &other)
6272 { assign(other); return *this; }
6274 /**
6275 *
6276 */
6277 virtual ~Task()
6278 { }
6281 /**
6282 *
6283 */
6284 virtual MakeBase &getParent()
6285 { return parent; }
6287 /**
6288 *
6289 */
6290 virtual int getType()
6291 { return type; }
6293 /**
6294 *
6295 */
6296 virtual void setType(int val)
6297 { type = val; }
6299 /**
6300 *
6301 */
6302 virtual String getName()
6303 { return name; }
6305 /**
6306 *
6307 */
6308 virtual bool execute()
6309 { return true; }
6311 /**
6312 *
6313 */
6314 virtual bool parse(Element *elem)
6315 { return true; }
6317 /**
6318 *
6319 */
6320 Task *createTask(Element *elem, int lineNr);
6323 protected:
6325 void init()
6326 {
6327 type = TASK_NONE;
6328 name = "none";
6329 }
6331 void assign(const Task &other)
6332 {
6333 type = other.type;
6334 name = other.name;
6335 }
6337 /**
6338 * Show task status
6339 */
6340 void taskstatus(const char *fmt, ...)
6341 {
6342 va_list args;
6343 va_start(args,fmt);
6344 fprintf(stdout, " %s : ", name.c_str());
6345 vfprintf(stdout, fmt, args);
6346 fprintf(stdout, "\n");
6347 va_end(args) ;
6348 }
6350 String getAttribute(Element *elem, const String &attrName)
6351 {
6352 String str;
6353 return str;
6354 }
6356 MakeBase &parent;
6358 int type;
6360 String name;
6361 };
6365 /**
6366 * This task runs the C/C++ compiler. The compiler is invoked
6367 * for all .c or .cpp files which are newer than their correcsponding
6368 * .o files.
6369 */
6370 class TaskCC : public Task
6371 {
6372 public:
6374 TaskCC(MakeBase &par) : Task(par)
6375 {
6376 type = TASK_CC;
6377 name = "cc";
6378 }
6380 virtual ~TaskCC()
6381 {}
6383 virtual bool isExcludedInc(const String &dirname)
6384 {
6385 for (unsigned int i=0 ; i<excludeInc.size() ; i++)
6386 {
6387 String fname = excludeInc[i];
6388 if (fname == dirname)
6389 return true;
6390 }
6391 return false;
6392 }
6394 virtual bool execute()
6395 {
6396 //evaluate our parameters
6397 String command = parent.eval(commandOpt, "gcc");
6398 String ccCommand = parent.eval(ccCommandOpt, "gcc");
6399 String cxxCommand = parent.eval(cxxCommandOpt, "g++");
6400 String source = parent.eval(sourceOpt, ".");
6401 String dest = parent.eval(destOpt, ".");
6402 String flags = parent.eval(flagsOpt, "");
6403 String defines = parent.eval(definesOpt, "");
6404 String includes = parent.eval(includesOpt, "");
6405 bool continueOnError = parent.evalBool(continueOnErrorOpt, true);
6406 bool refreshCache = parent.evalBool(refreshCacheOpt, false);
6408 if (!listFiles(parent, fileSet))
6409 return false;
6411 FILE *f = NULL;
6412 f = fopen("compile.lst", "w");
6414 //refreshCache is probably false here, unless specified otherwise
6415 String fullName = parent.resolve("build.dep");
6416 if (refreshCache || isNewerThan(parent.getURI().getPath(), fullName))
6417 {
6418 taskstatus("regenerating C/C++ dependency cache");
6419 refreshCache = true;
6420 }
6422 DepTool depTool;
6423 depTool.setSourceDirectory(source);
6424 depTool.setFileList(fileSet.getFiles());
6425 std::vector<DepRec> deps =
6426 depTool.getDepFile("build.dep", refreshCache);
6428 String incs;
6429 incs.append("-I");
6430 incs.append(parent.resolve("."));
6431 incs.append(" ");
6432 if (includes.size()>0)
6433 {
6434 incs.append(includes);
6435 incs.append(" ");
6436 }
6437 std::set<String> paths;
6438 std::vector<DepRec>::iterator viter;
6439 for (viter=deps.begin() ; viter!=deps.end() ; viter++)
6440 {
6441 DepRec dep = *viter;
6442 if (dep.path.size()>0)
6443 paths.insert(dep.path);
6444 }
6445 if (source.size()>0)
6446 {
6447 incs.append(" -I");
6448 incs.append(parent.resolve(source));
6449 incs.append(" ");
6450 }
6451 std::set<String>::iterator setIter;
6452 for (setIter=paths.begin() ; setIter!=paths.end() ; setIter++)
6453 {
6454 String dirName = *setIter;
6455 //check excludeInc to see if we dont want to include this dir
6456 if (isExcludedInc(dirName))
6457 continue;
6458 incs.append(" -I");
6459 String dname;
6460 if (source.size()>0)
6461 {
6462 dname.append(source);
6463 dname.append("/");
6464 }
6465 dname.append(dirName);
6466 incs.append(parent.resolve(dname));
6467 }
6469 /**
6470 * Compile each of the C files that need it
6471 */
6472 bool errorOccurred = false;
6473 std::vector<String> cfiles;
6474 for (viter=deps.begin() ; viter!=deps.end() ; viter++)
6475 {
6476 DepRec dep = *viter;
6478 //## Select command
6479 String sfx = dep.suffix;
6480 String command = ccCommand;
6481 if (sfx == "cpp" || sfx == "cxx" || sfx == "c++" ||
6482 sfx == "cc" || sfx == "CC")
6483 command = cxxCommand;
6485 //## Make paths
6486 String destPath = dest;
6487 String srcPath = source;
6488 if (dep.path.size()>0)
6489 {
6490 destPath.append("/");
6491 destPath.append(dep.path);
6492 srcPath.append("/");
6493 srcPath.append(dep.path);
6494 }
6495 //## Make sure destination directory exists
6496 if (!createDirectory(destPath))
6497 return false;
6499 //## Check whether it needs to be done
6500 String destName;
6501 if (destPath.size()>0)
6502 {
6503 destName.append(destPath);
6504 destName.append("/");
6505 }
6506 destName.append(dep.name);
6507 destName.append(".o");
6508 String destFullName = parent.resolve(destName);
6509 String srcName;
6510 if (srcPath.size()>0)
6511 {
6512 srcName.append(srcPath);
6513 srcName.append("/");
6514 }
6515 srcName.append(dep.name);
6516 srcName.append(".");
6517 srcName.append(dep.suffix);
6518 String srcFullName = parent.resolve(srcName);
6519 bool compileMe = false;
6520 //# First we check if the source is newer than the .o
6521 if (isNewerThan(srcFullName, destFullName))
6522 {
6523 taskstatus("compile of %s required by source: %s",
6524 destFullName.c_str(), srcFullName.c_str());
6525 compileMe = true;
6526 }
6527 else
6528 {
6529 //# secondly, we check if any of the included dependencies
6530 //# of the .c/.cpp is newer than the .o
6531 for (unsigned int i=0 ; i<dep.files.size() ; i++)
6532 {
6533 String depName;
6534 if (source.size()>0)
6535 {
6536 depName.append(source);
6537 depName.append("/");
6538 }
6539 depName.append(dep.files[i]);
6540 String depFullName = parent.resolve(depName);
6541 bool depRequires = isNewerThan(depFullName, destFullName);
6542 //trace("%d %s %s\n", depRequires,
6543 // destFullName.c_str(), depFullName.c_str());
6544 if (depRequires)
6545 {
6546 taskstatus("compile of %s required by included: %s",
6547 destFullName.c_str(), depFullName.c_str());
6548 compileMe = true;
6549 break;
6550 }
6551 }
6552 }
6553 if (!compileMe)
6554 {
6555 continue;
6556 }
6558 //## Assemble the command
6559 String cmd = command;
6560 cmd.append(" -c ");
6561 cmd.append(flags);
6562 cmd.append(" ");
6563 cmd.append(defines);
6564 cmd.append(" ");
6565 cmd.append(incs);
6566 cmd.append(" ");
6567 cmd.append(srcFullName);
6568 cmd.append(" -o ");
6569 cmd.append(destFullName);
6571 //## Execute the command
6573 String outString, errString;
6574 bool ret = executeCommand(cmd.c_str(), "", outString, errString);
6576 if (f)
6577 {
6578 fprintf(f, "########################### File : %s\n",
6579 srcFullName.c_str());
6580 fprintf(f, "#### COMMAND ###\n");
6581 int col = 0;
6582 for (unsigned int i = 0 ; i < cmd.size() ; i++)
6583 {
6584 char ch = cmd[i];
6585 if (isspace(ch) && col > 63)
6586 {
6587 fputc('\n', f);
6588 col = 0;
6589 }
6590 else
6591 {
6592 fputc(ch, f);
6593 col++;
6594 }
6595 if (col > 76)
6596 {
6597 fputc('\n', f);
6598 col = 0;
6599 }
6600 }
6601 fprintf(f, "\n");
6602 fprintf(f, "#### STDOUT ###\n%s\n", outString.c_str());
6603 fprintf(f, "#### STDERR ###\n%s\n\n", errString.c_str());
6604 fflush(f);
6605 }
6606 if (!ret)
6607 {
6608 error("problem compiling: %s", errString.c_str());
6609 errorOccurred = true;
6610 }
6611 if (errorOccurred && !continueOnError)
6612 break;
6613 }
6615 if (f)
6616 {
6617 fclose(f);
6618 }
6620 return !errorOccurred;
6621 }
6624 virtual bool parse(Element *elem)
6625 {
6626 String s;
6627 if (!parent.getAttribute(elem, "command", commandOpt))
6628 return false;
6629 if (commandOpt.size()>0)
6630 { cxxCommandOpt = ccCommandOpt = commandOpt; }
6631 if (!parent.getAttribute(elem, "cc", ccCommandOpt))
6632 return false;
6633 if (!parent.getAttribute(elem, "cxx", cxxCommandOpt))
6634 return false;
6635 if (!parent.getAttribute(elem, "destdir", destOpt))
6636 return false;
6637 if (!parent.getAttribute(elem, "continueOnError", continueOnErrorOpt))
6638 return false;
6639 if (!parent.getAttribute(elem, "refreshCache", refreshCacheOpt))
6640 return false;
6642 std::vector<Element *> children = elem->getChildren();
6643 for (unsigned int i=0 ; i<children.size() ; i++)
6644 {
6645 Element *child = children[i];
6646 String tagName = child->getName();
6647 if (tagName == "flags")
6648 {
6649 if (!parent.getValue(child, flagsOpt))
6650 return false;
6651 flagsOpt = strip(flagsOpt);
6652 }
6653 else if (tagName == "includes")
6654 {
6655 if (!parent.getValue(child, includesOpt))
6656 return false;
6657 includesOpt = strip(includesOpt);
6658 }
6659 else if (tagName == "defines")
6660 {
6661 if (!parent.getValue(child, definesOpt))
6662 return false;
6663 definesOpt = strip(definesOpt);
6664 }
6665 else if (tagName == "fileset")
6666 {
6667 if (!parseFileSet(child, parent, fileSet))
6668 return false;
6669 sourceOpt = fileSet.getDirectory();
6670 }
6671 else if (tagName == "excludeinc")
6672 {
6673 if (!parseFileList(child, parent, excludeInc))
6674 return false;
6675 }
6676 }
6678 return true;
6679 }
6681 protected:
6683 String commandOpt;
6684 String ccCommandOpt;
6685 String cxxCommandOpt;
6686 String sourceOpt;
6687 String destOpt;
6688 String flagsOpt;
6689 String definesOpt;
6690 String includesOpt;
6691 String continueOnErrorOpt;
6692 String refreshCacheOpt;
6693 FileSet fileSet;
6694 FileList excludeInc;
6696 };
6700 /**
6701 *
6702 */
6703 class TaskCopy : public Task
6704 {
6705 public:
6707 typedef enum
6708 {
6709 CP_NONE,
6710 CP_TOFILE,
6711 CP_TODIR
6712 } CopyType;
6714 TaskCopy(MakeBase &par) : Task(par)
6715 {
6716 type = TASK_COPY;
6717 name = "copy";
6718 cptype = CP_NONE;
6719 haveFileSet = false;
6720 }
6722 virtual ~TaskCopy()
6723 {}
6725 virtual bool execute()
6726 {
6727 String fileName = parent.eval(fileNameOpt , ".");
6728 String toFileName = parent.eval(toFileNameOpt , ".");
6729 String toDirName = parent.eval(toDirNameOpt , ".");
6730 bool verbose = parent.evalBool(verboseOpt, false);
6731 switch (cptype)
6732 {
6733 case CP_TOFILE:
6734 {
6735 if (fileName.size()>0)
6736 {
6737 taskstatus("%s to %s",
6738 fileName.c_str(), toFileName.c_str());
6739 String fullSource = parent.resolve(fileName);
6740 String fullDest = parent.resolve(toFileName);
6741 //trace("copy %s to file %s", fullSource.c_str(),
6742 // fullDest.c_str());
6743 if (!isRegularFile(fullSource))
6744 {
6745 error("copy : file %s does not exist", fullSource.c_str());
6746 return false;
6747 }
6748 if (!isNewerThan(fullSource, fullDest))
6749 {
6750 taskstatus("skipped");
6751 return true;
6752 }
6753 if (!copyFile(fullSource, fullDest))
6754 return false;
6755 taskstatus("1 file copied");
6756 }
6757 return true;
6758 }
6759 case CP_TODIR:
6760 {
6761 if (haveFileSet)
6762 {
6763 if (!listFiles(parent, fileSet))
6764 return false;
6765 String fileSetDir = parent.eval(fileSet.getDirectory(), ".");
6767 taskstatus("%s to %s",
6768 fileSetDir.c_str(), toDirName.c_str());
6770 int nrFiles = 0;
6771 for (unsigned int i=0 ; i<fileSet.size() ; i++)
6772 {
6773 String fileName = fileSet[i];
6775 String sourcePath;
6776 if (fileSetDir.size()>0)
6777 {
6778 sourcePath.append(fileSetDir);
6779 sourcePath.append("/");
6780 }
6781 sourcePath.append(fileName);
6782 String fullSource = parent.resolve(sourcePath);
6784 //Get the immediate parent directory's base name
6785 String baseFileSetDir = fileSetDir;
6786 unsigned int pos = baseFileSetDir.find_last_of('/');
6787 if (pos!=baseFileSetDir.npos &&
6788 pos < baseFileSetDir.size()-1)
6789 baseFileSetDir =
6790 baseFileSetDir.substr(pos+1,
6791 baseFileSetDir.size());
6792 //Now make the new path
6793 String destPath;
6794 if (toDirName.size()>0)
6795 {
6796 destPath.append(toDirName);
6797 destPath.append("/");
6798 }
6799 if (baseFileSetDir.size()>0)
6800 {
6801 destPath.append(baseFileSetDir);
6802 destPath.append("/");
6803 }
6804 destPath.append(fileName);
6805 String fullDest = parent.resolve(destPath);
6806 //trace("fileName:%s", fileName.c_str());
6807 //trace("copy %s to new dir : %s", fullSource.c_str(),
6808 // fullDest.c_str());
6809 if (!isNewerThan(fullSource, fullDest))
6810 {
6811 //trace("copy skipping %s", fullSource.c_str());
6812 continue;
6813 }
6814 if (!copyFile(fullSource, fullDest))
6815 return false;
6816 nrFiles++;
6817 }
6818 taskstatus("%d file(s) copied", nrFiles);
6819 }
6820 else //file source
6821 {
6822 //For file->dir we want only the basename of
6823 //the source appended to the dest dir
6824 taskstatus("%s to %s",
6825 fileName.c_str(), toDirName.c_str());
6826 String baseName = fileName;
6827 unsigned int pos = baseName.find_last_of('/');
6828 if (pos!=baseName.npos && pos<baseName.size()-1)
6829 baseName = baseName.substr(pos+1, baseName.size());
6830 String fullSource = parent.resolve(fileName);
6831 String destPath;
6832 if (toDirName.size()>0)
6833 {
6834 destPath.append(toDirName);
6835 destPath.append("/");
6836 }
6837 destPath.append(baseName);
6838 String fullDest = parent.resolve(destPath);
6839 //trace("copy %s to new dir : %s", fullSource.c_str(),
6840 // fullDest.c_str());
6841 if (!isRegularFile(fullSource))
6842 {
6843 error("copy : file %s does not exist", fullSource.c_str());
6844 return false;
6845 }
6846 if (!isNewerThan(fullSource, fullDest))
6847 {
6848 taskstatus("skipped");
6849 return true;
6850 }
6851 if (!copyFile(fullSource, fullDest))
6852 return false;
6853 taskstatus("1 file copied");
6854 }
6855 return true;
6856 }
6857 }
6858 return true;
6859 }
6862 virtual bool parse(Element *elem)
6863 {
6864 if (!parent.getAttribute(elem, "file", fileNameOpt))
6865 return false;
6866 if (!parent.getAttribute(elem, "tofile", toFileNameOpt))
6867 return false;
6868 if (toFileNameOpt.size() > 0)
6869 cptype = CP_TOFILE;
6870 if (!parent.getAttribute(elem, "todir", toDirNameOpt))
6871 return false;
6872 if (toDirNameOpt.size() > 0)
6873 cptype = CP_TODIR;
6874 if (!parent.getAttribute(elem, "verbose", verboseOpt))
6875 return false;
6877 haveFileSet = false;
6879 std::vector<Element *> children = elem->getChildren();
6880 for (unsigned int i=0 ; i<children.size() ; i++)
6881 {
6882 Element *child = children[i];
6883 String tagName = child->getName();
6884 if (tagName == "fileset")
6885 {
6886 if (!parseFileSet(child, parent, fileSet))
6887 {
6888 error("problem getting fileset");
6889 return false;
6890 }
6891 haveFileSet = true;
6892 }
6893 }
6895 //Perform validity checks
6896 if (fileNameOpt.size()>0 && fileSet.size()>0)
6897 {
6898 error("<copy> can only have one of : file= and <fileset>");
6899 return false;
6900 }
6901 if (toFileNameOpt.size()>0 && toDirNameOpt.size()>0)
6902 {
6903 error("<copy> can only have one of : tofile= or todir=");
6904 return false;
6905 }
6906 if (haveFileSet && toDirNameOpt.size()==0)
6907 {
6908 error("a <copy> task with a <fileset> must have : todir=");
6909 return false;
6910 }
6911 if (cptype == CP_TOFILE && fileNameOpt.size()==0)
6912 {
6913 error("<copy> tofile= must be associated with : file=");
6914 return false;
6915 }
6916 if (cptype == CP_TODIR && fileNameOpt.size()==0 && !haveFileSet)
6917 {
6918 error("<copy> todir= must be associated with : file= or <fileset>");
6919 return false;
6920 }
6922 return true;
6923 }
6925 private:
6927 int cptype;
6928 bool haveFileSet;
6930 FileSet fileSet;
6931 String fileNameOpt;
6932 String toFileNameOpt;
6933 String toDirNameOpt;
6934 String verboseOpt;
6935 };
6938 /**
6939 *
6940 */
6941 class TaskDelete : public Task
6942 {
6943 public:
6945 typedef enum
6946 {
6947 DEL_FILE,
6948 DEL_DIR,
6949 DEL_FILESET
6950 } DeleteType;
6952 TaskDelete(MakeBase &par) : Task(par)
6953 {
6954 type = TASK_DELETE;
6955 name = "delete";
6956 delType = DEL_FILE;
6957 }
6959 virtual ~TaskDelete()
6960 {}
6962 virtual bool execute()
6963 {
6964 String dirName = parent.eval(dirNameOpt, ".");
6965 String fileName = parent.eval(fileNameOpt, ".");
6966 bool verbose = parent.evalBool(verboseOpt, false);
6967 bool quiet = parent.evalBool(quietOpt, false);
6968 bool failOnError = parent.evalBool(failOnErrorOpt, true);
6969 struct stat finfo;
6970 switch (delType)
6971 {
6972 case DEL_FILE:
6973 {
6974 status(" : %s", fileName.c_str());
6975 String fullName = parent.resolve(fileName);
6976 char *fname = (char *)fullName.c_str();
6977 //does not exist
6978 if (stat(fname, &finfo)<0)
6979 return true;
6980 //exists but is not a regular file
6981 if (!S_ISREG(finfo.st_mode))
6982 {
6983 error("<delete> failed. '%s' exists and is not a regular file",
6984 fname);
6985 return false;
6986 }
6987 if (remove(fname)<0)
6988 {
6989 error("<delete> failed: %s", strerror(errno));
6990 return false;
6991 }
6992 return true;
6993 }
6994 case DEL_DIR:
6995 {
6996 taskstatus("%s", dirName.c_str());
6997 String fullDir = parent.resolve(dirName);
6998 if (!removeDirectory(fullDir))
6999 return false;
7000 return true;
7001 }
7002 }
7003 return true;
7004 }
7006 virtual bool parse(Element *elem)
7007 {
7008 if (!parent.getAttribute(elem, "file", fileNameOpt))
7009 return false;
7010 if (fileNameOpt.size() > 0)
7011 delType = DEL_FILE;
7012 if (!parent.getAttribute(elem, "dir", dirNameOpt))
7013 return false;
7014 if (dirNameOpt.size() > 0)
7015 delType = DEL_DIR;
7016 if (fileNameOpt.size()>0 && dirNameOpt.size()>0)
7017 {
7018 error("<delete> can have one attribute of file= or dir=");
7019 return false;
7020 }
7021 if (fileNameOpt.size()==0 && dirNameOpt.size()==0)
7022 {
7023 error("<delete> must have one attribute of file= or dir=");
7024 return false;
7025 }
7026 if (!parent.getAttribute(elem, "verbose", verboseOpt))
7027 return false;
7028 if (!parent.getAttribute(elem, "quiet", quietOpt))
7029 return false;
7030 if (!parent.getAttribute(elem, "failonerror", failOnErrorOpt))
7031 return false;
7032 return true;
7033 }
7035 private:
7037 int delType;
7038 String dirNameOpt;
7039 String fileNameOpt;
7040 String verboseOpt;
7041 String quietOpt;
7042 String failOnErrorOpt;
7043 };
7046 /**
7047 * Send a message to stdout
7048 */
7049 class TaskEcho : public Task
7050 {
7051 public:
7053 TaskEcho(MakeBase &par) : Task(par)
7054 { type = TASK_ECHO; name = "echo"; }
7056 virtual ~TaskEcho()
7057 {}
7059 virtual bool execute()
7060 {
7061 //let message have priority over text
7062 String message = parent.eval(messageOpt, "");
7063 String text = parent.eval(textOpt, "");
7064 if (message.size() > 0)
7065 {
7066 fprintf(stdout, "%s\n", message.c_str());
7067 }
7068 else if (text.size() > 0)
7069 {
7070 fprintf(stdout, "%s\n", text.c_str());
7071 }
7072 return true;
7073 }
7075 virtual bool parse(Element *elem)
7076 {
7077 if (!parent.getValue(elem, textOpt))
7078 return false;
7079 textOpt = leftJustify(textOpt);
7080 if (!parent.getAttribute(elem, "message", messageOpt))
7081 return false;
7082 return true;
7083 }
7085 private:
7087 String messageOpt;
7088 String textOpt;
7089 };
7093 /**
7094 *
7095 */
7096 class TaskJar : public Task
7097 {
7098 public:
7100 TaskJar(MakeBase &par) : Task(par)
7101 { type = TASK_JAR; name = "jar"; }
7103 virtual ~TaskJar()
7104 {}
7106 virtual bool execute()
7107 {
7108 String command = parent.eval(commandOpt, "jar");
7109 String basedir = parent.eval(basedirOpt, ".");
7110 String destfile = parent.eval(destfileOpt, ".");
7112 String cmd = command;
7113 cmd.append(" -cf ");
7114 cmd.append(destfile);
7115 cmd.append(" -C ");
7116 cmd.append(basedir);
7117 cmd.append(" .");
7119 String execCmd = cmd;
7121 String outString, errString;
7122 bool ret = executeCommand(execCmd.c_str(), "", outString, errString);
7123 if (!ret)
7124 {
7125 error("<jar> command '%s' failed :\n %s",
7126 execCmd.c_str(), errString.c_str());
7127 return false;
7128 }
7129 return true;
7130 }
7132 virtual bool parse(Element *elem)
7133 {
7134 if (!parent.getAttribute(elem, "command", commandOpt))
7135 return false;
7136 if (!parent.getAttribute(elem, "basedir", basedirOpt))
7137 return false;
7138 if (!parent.getAttribute(elem, "destfile", destfileOpt))
7139 return false;
7140 if (basedirOpt.size() == 0 || destfileOpt.size() == 0)
7141 {
7142 error("<jar> required both basedir and destfile attributes to be set");
7143 return false;
7144 }
7145 return true;
7146 }
7148 private:
7150 String commandOpt;
7151 String basedirOpt;
7152 String destfileOpt;
7153 };
7156 /**
7157 *
7158 */
7159 class TaskJavac : public Task
7160 {
7161 public:
7163 TaskJavac(MakeBase &par) : Task(par)
7164 {
7165 type = TASK_JAVAC; name = "javac";
7166 }
7168 virtual ~TaskJavac()
7169 {}
7171 virtual bool execute()
7172 {
7173 String command = parent.eval(commandOpt, "javac");
7174 String srcdir = parent.eval(srcdirOpt, ".");
7175 String destdir = parent.eval(destdirOpt, ".");
7176 String target = parent.eval(targetOpt, "");
7178 std::vector<String> fileList;
7179 if (!listFiles(srcdir, "", fileList))
7180 {
7181 return false;
7182 }
7183 String cmd = command;
7184 cmd.append(" -d ");
7185 cmd.append(destdir);
7186 cmd.append(" -classpath ");
7187 cmd.append(destdir);
7188 cmd.append(" -sourcepath ");
7189 cmd.append(srcdir);
7190 cmd.append(" ");
7191 if (target.size()>0)
7192 {
7193 cmd.append(" -target ");
7194 cmd.append(target);
7195 cmd.append(" ");
7196 }
7197 String fname = "javalist.btool";
7198 FILE *f = fopen(fname.c_str(), "w");
7199 int count = 0;
7200 for (unsigned int i=0 ; i<fileList.size() ; i++)
7201 {
7202 String fname = fileList[i];
7203 String srcName = fname;
7204 if (fname.size()<6) //x.java
7205 continue;
7206 if (fname.compare(fname.size()-5, 5, ".java") != 0)
7207 continue;
7208 String baseName = fname.substr(0, fname.size()-5);
7209 String destName = baseName;
7210 destName.append(".class");
7212 String fullSrc = srcdir;
7213 fullSrc.append("/");
7214 fullSrc.append(fname);
7215 String fullDest = destdir;
7216 fullDest.append("/");
7217 fullDest.append(destName);
7218 //trace("fullsrc:%s fulldest:%s", fullSrc.c_str(), fullDest.c_str());
7219 if (!isNewerThan(fullSrc, fullDest))
7220 continue;
7222 count++;
7223 fprintf(f, "%s\n", fullSrc.c_str());
7224 }
7225 fclose(f);
7226 if (!count)
7227 {
7228 taskstatus("nothing to do");
7229 return true;
7230 }
7232 taskstatus("compiling %d files", count);
7234 String execCmd = cmd;
7235 execCmd.append("@");
7236 execCmd.append(fname);
7238 String outString, errString;
7239 bool ret = executeCommand(execCmd.c_str(), "", outString, errString);
7240 if (!ret)
7241 {
7242 error("<javac> command '%s' failed :\n %s",
7243 execCmd.c_str(), errString.c_str());
7244 return false;
7245 }
7246 return true;
7247 }
7249 virtual bool parse(Element *elem)
7250 {
7251 if (!parent.getAttribute(elem, "command", commandOpt))
7252 return false;
7253 if (!parent.getAttribute(elem, "srcdir", srcdirOpt))
7254 return false;
7255 if (!parent.getAttribute(elem, "destdir", destdirOpt))
7256 return false;
7257 if (srcdirOpt.size() == 0 || destdirOpt.size() == 0)
7258 {
7259 error("<javac> required both srcdir and destdir attributes to be set");
7260 return false;
7261 }
7262 if (!parent.getAttribute(elem, "target", targetOpt))
7263 return false;
7264 return true;
7265 }
7267 private:
7269 String commandOpt;
7270 String srcdirOpt;
7271 String destdirOpt;
7272 String targetOpt;
7274 };
7277 /**
7278 *
7279 */
7280 class TaskLink : public Task
7281 {
7282 public:
7284 TaskLink(MakeBase &par) : Task(par)
7285 {
7286 type = TASK_LINK; name = "link";
7287 }
7289 virtual ~TaskLink()
7290 {}
7292 virtual bool execute()
7293 {
7294 String command = parent.eval(commandOpt, "g++");
7295 String fileName = parent.eval(fileNameOpt, "");
7296 String flags = parent.eval(flagsOpt, "");
7297 String libs = parent.eval(libsOpt, "");
7298 bool doStrip = parent.evalBool(doStripOpt, false);
7299 String symFileName = parent.eval(symFileNameOpt, "");
7300 String stripCommand = parent.eval(stripCommandOpt, "strip");
7301 String objcopyCommand = parent.eval(objcopyCommandOpt, "objcopy");
7303 if (!listFiles(parent, fileSet))
7304 return false;
7305 String fileSetDir = parent.eval(fileSet.getDirectory(), ".");
7306 //trace("%d files in %s", fileSet.size(), fileSetDir.c_str());
7307 bool doit = false;
7308 String fullTarget = parent.resolve(fileName);
7309 String cmd = command;
7310 cmd.append(" -o ");
7311 cmd.append(fullTarget);
7312 cmd.append(" ");
7313 cmd.append(flags);
7314 for (unsigned int i=0 ; i<fileSet.size() ; i++)
7315 {
7316 cmd.append(" ");
7317 String obj;
7318 if (fileSetDir.size()>0)
7319 {
7320 obj.append(fileSetDir);
7321 obj.append("/");
7322 }
7323 obj.append(fileSet[i]);
7324 String fullObj = parent.resolve(obj);
7325 String nativeFullObj = getNativePath(fullObj);
7326 cmd.append(nativeFullObj);
7327 //trace("link: tgt:%s obj:%s", fullTarget.c_str(),
7328 // fullObj.c_str());
7329 if (isNewerThan(fullObj, fullTarget))
7330 doit = true;
7331 }
7332 cmd.append(" ");
7333 cmd.append(libs);
7334 if (!doit)
7335 {
7336 //trace("link not needed");
7337 return true;
7338 }
7339 //trace("LINK cmd:%s", cmd.c_str());
7342 String outbuf, errbuf;
7343 if (!executeCommand(cmd.c_str(), "", outbuf, errbuf))
7344 {
7345 error("LINK problem: %s", errbuf.c_str());
7346 return false;
7347 }
7349 if (symFileName.size()>0)
7350 {
7351 String symFullName = parent.resolve(symFileName);
7352 cmd = objcopyCommand;
7353 cmd.append(" --only-keep-debug ");
7354 cmd.append(getNativePath(fullTarget));
7355 cmd.append(" ");
7356 cmd.append(getNativePath(symFullName));
7357 if (!executeCommand(cmd, "", outbuf, errbuf))
7358 {
7359 error("<strip> symbol file failed : %s", errbuf.c_str());
7360 return false;
7361 }
7362 }
7364 if (doStrip)
7365 {
7366 cmd = stripCommand;
7367 cmd.append(" ");
7368 cmd.append(getNativePath(fullTarget));
7369 if (!executeCommand(cmd, "", outbuf, errbuf))
7370 {
7371 error("<strip> failed : %s", errbuf.c_str());
7372 return false;
7373 }
7374 }
7376 return true;
7377 }
7379 virtual bool parse(Element *elem)
7380 {
7381 if (!parent.getAttribute(elem, "command", commandOpt))
7382 return false;
7383 if (!parent.getAttribute(elem, "objcopycommand", objcopyCommandOpt))
7384 return false;
7385 if (!parent.getAttribute(elem, "stripcommand", stripCommandOpt))
7386 return false;
7387 if (!parent.getAttribute(elem, "out", fileNameOpt))
7388 return false;
7389 if (!parent.getAttribute(elem, "strip", doStripOpt))
7390 return false;
7391 if (!parent.getAttribute(elem, "symfile", symFileNameOpt))
7392 return false;
7394 std::vector<Element *> children = elem->getChildren();
7395 for (unsigned int i=0 ; i<children.size() ; i++)
7396 {
7397 Element *child = children[i];
7398 String tagName = child->getName();
7399 if (tagName == "fileset")
7400 {
7401 if (!parseFileSet(child, parent, fileSet))
7402 return false;
7403 }
7404 else if (tagName == "flags")
7405 {
7406 if (!parent.getValue(child, flagsOpt))
7407 return false;
7408 flagsOpt = strip(flagsOpt);
7409 }
7410 else if (tagName == "libs")
7411 {
7412 if (!parent.getValue(child, libsOpt))
7413 return false;
7414 libsOpt = strip(libsOpt);
7415 }
7416 }
7417 return true;
7418 }
7420 private:
7422 FileSet fileSet;
7424 String commandOpt;
7425 String fileNameOpt;
7426 String flagsOpt;
7427 String libsOpt;
7428 String doStripOpt;
7429 String symFileNameOpt;
7430 String stripCommandOpt;
7431 String objcopyCommandOpt;
7433 };
7437 /**
7438 * Create a named file
7439 */
7440 class TaskMakeFile : public Task
7441 {
7442 public:
7444 TaskMakeFile(MakeBase &par) : Task(par)
7445 { type = TASK_MAKEFILE; name = "makefile"; }
7447 virtual ~TaskMakeFile()
7448 {}
7450 virtual bool execute()
7451 {
7452 String fileName = parent.eval(fileNameOpt, "");
7453 String text = parent.eval(textOpt, "");
7455 taskstatus("%s", fileName.c_str());
7456 String fullName = parent.resolve(fileName);
7457 if (!isNewerThan(parent.getURI().getPath(), fullName))
7458 {
7459 //trace("skipped <makefile>");
7460 return true;
7461 }
7462 String fullNative = getNativePath(fullName);
7463 //trace("fullName:%s", fullName.c_str());
7464 FILE *f = fopen(fullNative.c_str(), "w");
7465 if (!f)
7466 {
7467 error("<makefile> could not open %s for writing : %s",
7468 fullName.c_str(), strerror(errno));
7469 return false;
7470 }
7471 for (unsigned int i=0 ; i<text.size() ; i++)
7472 fputc(text[i], f);
7473 fputc('\n', f);
7474 fclose(f);
7475 return true;
7476 }
7478 virtual bool parse(Element *elem)
7479 {
7480 if (!parent.getAttribute(elem, "file", fileNameOpt))
7481 return false;
7482 if (fileNameOpt.size() == 0)
7483 {
7484 error("<makefile> requires 'file=\"filename\"' attribute");
7485 return false;
7486 }
7487 if (!parent.getValue(elem, textOpt))
7488 return false;
7489 textOpt = leftJustify(textOpt);
7490 //trace("dirname:%s", dirName.c_str());
7491 return true;
7492 }
7494 private:
7496 String fileNameOpt;
7497 String textOpt;
7498 };
7502 /**
7503 * Create a named directory
7504 */
7505 class TaskMkDir : public Task
7506 {
7507 public:
7509 TaskMkDir(MakeBase &par) : Task(par)
7510 { type = TASK_MKDIR; name = "mkdir"; }
7512 virtual ~TaskMkDir()
7513 {}
7515 virtual bool execute()
7516 {
7517 String dirName = parent.eval(dirNameOpt, ".");
7519 taskstatus("%s", dirName.c_str());
7520 String fullDir = parent.resolve(dirName);
7521 //trace("fullDir:%s", fullDir.c_str());
7522 if (!createDirectory(fullDir))
7523 return false;
7524 return true;
7525 }
7527 virtual bool parse(Element *elem)
7528 {
7529 if (!parent.getAttribute(elem, "dir", dirNameOpt))
7530 return false;
7531 if (dirNameOpt.size() == 0)
7532 {
7533 error("<mkdir> requires 'dir=\"dirname\"' attribute");
7534 return false;
7535 }
7536 return true;
7537 }
7539 private:
7541 String dirNameOpt;
7542 };
7546 /**
7547 * Create a named directory
7548 */
7549 class TaskMsgFmt: public Task
7550 {
7551 public:
7553 TaskMsgFmt(MakeBase &par) : Task(par)
7554 { type = TASK_MSGFMT; name = "msgfmt"; }
7556 virtual ~TaskMsgFmt()
7557 {}
7559 virtual bool execute()
7560 {
7561 String command = parent.eval(commandOpt, "msgfmt");
7562 String toDirName = parent.eval(toDirNameOpt, ".");
7563 String outName = parent.eval(outNameOpt, "");
7564 bool owndir = parent.evalBool(owndirOpt, false);
7566 if (!listFiles(parent, fileSet))
7567 return false;
7568 String fileSetDir = fileSet.getDirectory();
7570 //trace("msgfmt: %d", fileSet.size());
7571 for (unsigned int i=0 ; i<fileSet.size() ; i++)
7572 {
7573 String fileName = fileSet[i];
7574 if (getSuffix(fileName) != "po")
7575 continue;
7576 String sourcePath;
7577 if (fileSetDir.size()>0)
7578 {
7579 sourcePath.append(fileSetDir);
7580 sourcePath.append("/");
7581 }
7582 sourcePath.append(fileName);
7583 String fullSource = parent.resolve(sourcePath);
7585 String destPath;
7586 if (toDirName.size()>0)
7587 {
7588 destPath.append(toDirName);
7589 destPath.append("/");
7590 }
7591 if (owndir)
7592 {
7593 String subdir = fileName;
7594 unsigned int pos = subdir.find_last_of('.');
7595 if (pos != subdir.npos)
7596 subdir = subdir.substr(0, pos);
7597 destPath.append(subdir);
7598 destPath.append("/");
7599 }
7600 //Pick the output file name
7601 if (outName.size() > 0)
7602 {
7603 destPath.append(outName);
7604 }
7605 else
7606 {
7607 destPath.append(fileName);
7608 destPath[destPath.size()-2] = 'm';
7609 }
7611 String fullDest = parent.resolve(destPath);
7613 if (!isNewerThan(fullSource, fullDest))
7614 {
7615 //trace("skip %s", fullSource.c_str());
7616 continue;
7617 }
7619 String cmd = command;
7620 cmd.append(" ");
7621 cmd.append(fullSource);
7622 cmd.append(" -o ");
7623 cmd.append(fullDest);
7625 int pos = fullDest.find_last_of('/');
7626 if (pos>0)
7627 {
7628 String fullDestPath = fullDest.substr(0, pos);
7629 if (!createDirectory(fullDestPath))
7630 return false;
7631 }
7635 String outString, errString;
7636 if (!executeCommand(cmd.c_str(), "", outString, errString))
7637 {
7638 error("<msgfmt> problem: %s", errString.c_str());
7639 return false;
7640 }
7641 }
7643 return true;
7644 }
7646 virtual bool parse(Element *elem)
7647 {
7648 if (!parent.getAttribute(elem, "command", commandOpt))
7649 return false;
7650 if (!parent.getAttribute(elem, "todir", toDirNameOpt))
7651 return false;
7652 if (!parent.getAttribute(elem, "out", outNameOpt))
7653 return false;
7654 if (!parent.getAttribute(elem, "owndir", owndirOpt))
7655 return false;
7657 std::vector<Element *> children = elem->getChildren();
7658 for (unsigned int i=0 ; i<children.size() ; i++)
7659 {
7660 Element *child = children[i];
7661 String tagName = child->getName();
7662 if (tagName == "fileset")
7663 {
7664 if (!parseFileSet(child, parent, fileSet))
7665 return false;
7666 }
7667 }
7668 return true;
7669 }
7671 private:
7673 FileSet fileSet;
7675 String commandOpt;
7676 String toDirNameOpt;
7677 String outNameOpt;
7678 String owndirOpt;
7680 };
7684 /**
7685 * Perform a Package-Config query similar to pkg-config
7686 */
7687 class TaskPkgConfig : public Task
7688 {
7689 public:
7691 typedef enum
7692 {
7693 PKG_CONFIG_QUERY_CFLAGS,
7694 PKG_CONFIG_QUERY_LIBS,
7695 PKG_CONFIG_QUERY_ALL
7696 } QueryTypes;
7698 TaskPkgConfig(MakeBase &par) : Task(par)
7699 {
7700 type = TASK_PKG_CONFIG;
7701 name = "pkg-config";
7702 }
7704 virtual ~TaskPkgConfig()
7705 {}
7707 virtual bool execute()
7708 {
7709 String pkgName = parent.eval(pkgNameOpt, "");
7710 String prefix = parent.eval(prefixOpt, "");
7711 String propName = parent.eval(propNameOpt, "");
7712 String pkgConfigPath = parent.eval(pkgConfigPathOpt,"");
7713 String query = parent.eval(queryOpt, "all");
7715 String path = parent.resolve(pkgConfigPath);
7716 PkgConfig pkgconfig;
7717 pkgconfig.setPath(path);
7718 pkgconfig.setPrefix(prefix);
7719 if (!pkgconfig.query(pkgName))
7720 {
7721 error("<pkg-config> query failed for '%s", name.c_str());
7722 return false;
7723 }
7725 String val = "";
7726 if (query == "cflags")
7727 val = pkgconfig.getCflags();
7728 else if (query == "libs")
7729 val =pkgconfig.getLibs();
7730 else if (query == "all")
7731 val = pkgconfig.getAll();
7732 else
7733 {
7734 error("<pkg-config> unhandled query : %s", query.c_str());
7735 return false;
7736 }
7737 taskstatus("property %s = '%s'", propName.c_str(), val.c_str());
7738 parent.setProperty(propName, val);
7739 return true;
7740 }
7742 virtual bool parse(Element *elem)
7743 {
7744 //# NAME
7745 if (!parent.getAttribute(elem, "name", pkgNameOpt))
7746 return false;
7747 if (pkgNameOpt.size()==0)
7748 {
7749 error("<pkg-config> requires 'name=\"package\"' attribute");
7750 return false;
7751 }
7753 //# PROPERTY
7754 if (!parent.getAttribute(elem, "property", propNameOpt))
7755 return false;
7756 if (propNameOpt.size()==0)
7757 {
7758 error("<pkg-config> requires 'property=\"name\"' attribute");
7759 return false;
7760 }
7761 //# PATH
7762 if (!parent.getAttribute(elem, "path", pkgConfigPathOpt))
7763 return false;
7764 //# PREFIX
7765 if (!parent.getAttribute(elem, "prefix", prefixOpt))
7766 return false;
7767 //# QUERY
7768 if (!parent.getAttribute(elem, "query", queryOpt))
7769 return false;
7771 return true;
7772 }
7774 private:
7776 String queryOpt;
7777 String pkgNameOpt;
7778 String prefixOpt;
7779 String propNameOpt;
7780 String pkgConfigPathOpt;
7782 };
7789 /**
7790 * Process an archive to allow random access
7791 */
7792 class TaskRanlib : public Task
7793 {
7794 public:
7796 TaskRanlib(MakeBase &par) : Task(par)
7797 { type = TASK_RANLIB; name = "ranlib"; }
7799 virtual ~TaskRanlib()
7800 {}
7802 virtual bool execute()
7803 {
7804 String fileName = parent.eval(fileNameOpt, "");
7805 String command = parent.eval(commandOpt, "ranlib");
7807 String fullName = parent.resolve(fileName);
7808 //trace("fullDir:%s", fullDir.c_str());
7809 String cmd = command;
7810 cmd.append(" ");
7811 cmd.append(fullName);
7812 String outbuf, errbuf;
7813 if (!executeCommand(cmd, "", outbuf, errbuf))
7814 return false;
7815 return true;
7816 }
7818 virtual bool parse(Element *elem)
7819 {
7820 if (!parent.getAttribute(elem, "command", commandOpt))
7821 return false;
7822 if (!parent.getAttribute(elem, "file", fileNameOpt))
7823 return false;
7824 if (fileNameOpt.size() == 0)
7825 {
7826 error("<ranlib> requires 'file=\"fileNname\"' attribute");
7827 return false;
7828 }
7829 return true;
7830 }
7832 private:
7834 String fileNameOpt;
7835 String commandOpt;
7836 };
7840 /**
7841 * Compile a resource file into a .res
7842 */
7843 class TaskRC : public Task
7844 {
7845 public:
7847 TaskRC(MakeBase &par) : Task(par)
7848 { type = TASK_RC; name = "rc"; }
7850 virtual ~TaskRC()
7851 {}
7853 virtual bool execute()
7854 {
7855 String command = parent.eval(commandOpt, "windres");
7856 String flags = parent.eval(flagsOpt, "");
7857 String fileName = parent.eval(fileNameOpt, "");
7858 String outName = parent.eval(outNameOpt, "");
7860 String fullFile = parent.resolve(fileName);
7861 String fullOut = parent.resolve(outName);
7862 if (!isNewerThan(fullFile, fullOut))
7863 return true;
7864 String cmd = command;
7865 cmd.append(" -o ");
7866 cmd.append(fullOut);
7867 cmd.append(" ");
7868 cmd.append(flags);
7869 cmd.append(" ");
7870 cmd.append(fullFile);
7872 String outString, errString;
7873 if (!executeCommand(cmd.c_str(), "", outString, errString))
7874 {
7875 error("RC problem: %s", errString.c_str());
7876 return false;
7877 }
7878 return true;
7879 }
7881 virtual bool parse(Element *elem)
7882 {
7883 if (!parent.getAttribute(elem, "command", commandOpt))
7884 return false;
7885 if (!parent.getAttribute(elem, "file", fileNameOpt))
7886 return false;
7887 if (!parent.getAttribute(elem, "out", outNameOpt))
7888 return false;
7889 std::vector<Element *> children = elem->getChildren();
7890 for (unsigned int i=0 ; i<children.size() ; i++)
7891 {
7892 Element *child = children[i];
7893 String tagName = child->getName();
7894 if (tagName == "flags")
7895 {
7896 if (!parent.getValue(child, flagsOpt))
7897 return false;
7898 }
7899 }
7900 return true;
7901 }
7903 private:
7905 String commandOpt;
7906 String flagsOpt;
7907 String fileNameOpt;
7908 String outNameOpt;
7910 };
7914 /**
7915 * Collect .o's into a .so or DLL
7916 */
7917 class TaskSharedLib : public Task
7918 {
7919 public:
7921 TaskSharedLib(MakeBase &par) : Task(par)
7922 { type = TASK_SHAREDLIB; name = "dll"; }
7924 virtual ~TaskSharedLib()
7925 {}
7927 virtual bool execute()
7928 {
7929 String command = parent.eval(commandOpt, "dllwrap");
7930 String fileName = parent.eval(fileNameOpt, "");
7931 String defFileName = parent.eval(defFileNameOpt, "");
7932 String impFileName = parent.eval(impFileNameOpt, "");
7933 String libs = parent.eval(libsOpt, "");
7935 //trace("###########HERE %d", fileSet.size());
7936 bool doit = false;
7938 String fullOut = parent.resolve(fileName);
7939 //trace("ar fullout: %s", fullOut.c_str());
7941 if (!listFiles(parent, fileSet))
7942 return false;
7943 String fileSetDir = parent.eval(fileSet.getDirectory(), ".");
7945 for (unsigned int i=0 ; i<fileSet.size() ; i++)
7946 {
7947 String fname;
7948 if (fileSetDir.size()>0)
7949 {
7950 fname.append(fileSetDir);
7951 fname.append("/");
7952 }
7953 fname.append(fileSet[i]);
7954 String fullName = parent.resolve(fname);
7955 //trace("ar : %s/%s", fullOut.c_str(), fullName.c_str());
7956 if (isNewerThan(fullName, fullOut))
7957 doit = true;
7958 }
7959 //trace("Needs it:%d", doit);
7960 if (!doit)
7961 {
7962 return true;
7963 }
7965 String cmd = "dllwrap";
7966 cmd.append(" -o ");
7967 cmd.append(fullOut);
7968 if (defFileName.size()>0)
7969 {
7970 cmd.append(" --def ");
7971 cmd.append(defFileName);
7972 cmd.append(" ");
7973 }
7974 if (impFileName.size()>0)
7975 {
7976 cmd.append(" --implib ");
7977 cmd.append(impFileName);
7978 cmd.append(" ");
7979 }
7980 for (unsigned int i=0 ; i<fileSet.size() ; i++)
7981 {
7982 String fname;
7983 if (fileSetDir.size()>0)
7984 {
7985 fname.append(fileSetDir);
7986 fname.append("/");
7987 }
7988 fname.append(fileSet[i]);
7989 String fullName = parent.resolve(fname);
7991 cmd.append(" ");
7992 cmd.append(fullName);
7993 }
7994 cmd.append(" ");
7995 cmd.append(libs);
7997 String outString, errString;
7998 if (!executeCommand(cmd.c_str(), "", outString, errString))
7999 {
8000 error("<sharedlib> problem: %s", errString.c_str());
8001 return false;
8002 }
8004 return true;
8005 }
8007 virtual bool parse(Element *elem)
8008 {
8009 if (!parent.getAttribute(elem, "command", commandOpt))
8010 return false;
8011 if (!parent.getAttribute(elem, "file", fileNameOpt))
8012 return false;
8013 if (!parent.getAttribute(elem, "import", impFileNameOpt))
8014 return false;
8015 if (!parent.getAttribute(elem, "def", defFileNameOpt))
8016 return false;
8018 std::vector<Element *> children = elem->getChildren();
8019 for (unsigned int i=0 ; i<children.size() ; i++)
8020 {
8021 Element *child = children[i];
8022 String tagName = child->getName();
8023 if (tagName == "fileset")
8024 {
8025 if (!parseFileSet(child, parent, fileSet))
8026 return false;
8027 }
8028 else if (tagName == "libs")
8029 {
8030 if (!parent.getValue(child, libsOpt))
8031 return false;
8032 libsOpt = strip(libsOpt);
8033 }
8034 }
8035 return true;
8036 }
8038 private:
8040 FileSet fileSet;
8042 String commandOpt;
8043 String fileNameOpt;
8044 String defFileNameOpt;
8045 String impFileNameOpt;
8046 String libsOpt;
8048 };
8052 /**
8053 * Run the "ar" command to archive .o's into a .a
8054 */
8055 class TaskStaticLib : public Task
8056 {
8057 public:
8059 TaskStaticLib(MakeBase &par) : Task(par)
8060 { type = TASK_STATICLIB; name = "staticlib"; }
8062 virtual ~TaskStaticLib()
8063 {}
8065 virtual bool execute()
8066 {
8067 String command = parent.eval(commandOpt, "ar crv");
8068 String fileName = parent.eval(fileNameOpt, "");
8070 bool doit = false;
8072 String fullOut = parent.resolve(fileName);
8073 //trace("ar fullout: %s", fullOut.c_str());
8075 if (!listFiles(parent, fileSet))
8076 return false;
8077 String fileSetDir = parent.eval(fileSet.getDirectory(), ".");
8078 //trace("###########HERE %s", fileSetDir.c_str());
8080 for (unsigned int i=0 ; i<fileSet.size() ; i++)
8081 {
8082 String fname;
8083 if (fileSetDir.size()>0)
8084 {
8085 fname.append(fileSetDir);
8086 fname.append("/");
8087 }
8088 fname.append(fileSet[i]);
8089 String fullName = parent.resolve(fname);
8090 //trace("ar : %s/%s", fullOut.c_str(), fullName.c_str());
8091 if (isNewerThan(fullName, fullOut))
8092 doit = true;
8093 }
8094 //trace("Needs it:%d", doit);
8095 if (!doit)
8096 {
8097 return true;
8098 }
8100 String cmd = command;
8101 cmd.append(" ");
8102 cmd.append(fullOut);
8103 for (unsigned int i=0 ; i<fileSet.size() ; i++)
8104 {
8105 String fname;
8106 if (fileSetDir.size()>0)
8107 {
8108 fname.append(fileSetDir);
8109 fname.append("/");
8110 }
8111 fname.append(fileSet[i]);
8112 String fullName = parent.resolve(fname);
8114 cmd.append(" ");
8115 cmd.append(fullName);
8116 }
8118 String outString, errString;
8119 if (!executeCommand(cmd.c_str(), "", outString, errString))
8120 {
8121 error("<staticlib> problem: %s", errString.c_str());
8122 return false;
8123 }
8125 return true;
8126 }
8129 virtual bool parse(Element *elem)
8130 {
8131 if (!parent.getAttribute(elem, "command", commandOpt))
8132 return false;
8133 if (!parent.getAttribute(elem, "file", fileNameOpt))
8134 return false;
8136 std::vector<Element *> children = elem->getChildren();
8137 for (unsigned int i=0 ; i<children.size() ; i++)
8138 {
8139 Element *child = children[i];
8140 String tagName = child->getName();
8141 if (tagName == "fileset")
8142 {
8143 if (!parseFileSet(child, parent, fileSet))
8144 return false;
8145 }
8146 }
8147 return true;
8148 }
8150 private:
8152 FileSet fileSet;
8154 String commandOpt;
8155 String fileNameOpt;
8157 };
8162 /**
8163 * Strip an executable
8164 */
8165 class TaskStrip : public Task
8166 {
8167 public:
8169 TaskStrip(MakeBase &par) : Task(par)
8170 { type = TASK_STRIP; name = "strip"; }
8172 virtual ~TaskStrip()
8173 {}
8175 virtual bool execute()
8176 {
8177 String command = parent.eval(commandOpt, "strip");
8178 String fileName = parent.eval(fileNameOpt, "");
8179 String symFileName = parent.eval(symFileNameOpt, "");
8181 String fullName = parent.resolve(fileName);
8182 //trace("fullDir:%s", fullDir.c_str());
8183 String cmd;
8184 String outbuf, errbuf;
8186 if (symFileName.size()>0)
8187 {
8188 String symFullName = parent.resolve(symFileName);
8189 cmd = "objcopy --only-keep-debug ";
8190 cmd.append(getNativePath(fullName));
8191 cmd.append(" ");
8192 cmd.append(getNativePath(symFullName));
8193 if (!executeCommand(cmd, "", outbuf, errbuf))
8194 {
8195 error("<strip> symbol file failed : %s", errbuf.c_str());
8196 return false;
8197 }
8198 }
8200 cmd = command;
8201 cmd.append(getNativePath(fullName));
8202 if (!executeCommand(cmd, "", outbuf, errbuf))
8203 {
8204 error("<strip> failed : %s", errbuf.c_str());
8205 return false;
8206 }
8207 return true;
8208 }
8210 virtual bool parse(Element *elem)
8211 {
8212 if (!parent.getAttribute(elem, "command", commandOpt))
8213 return false;
8214 if (!parent.getAttribute(elem, "file", fileNameOpt))
8215 return false;
8216 if (!parent.getAttribute(elem, "symfile", symFileNameOpt))
8217 return false;
8218 if (fileNameOpt.size() == 0)
8219 {
8220 error("<strip> requires 'file=\"fileName\"' attribute");
8221 return false;
8222 }
8223 return true;
8224 }
8226 private:
8228 String commandOpt;
8229 String fileNameOpt;
8230 String symFileNameOpt;
8231 };
8234 /**
8235 *
8236 */
8237 class TaskTouch : public Task
8238 {
8239 public:
8241 TaskTouch(MakeBase &par) : Task(par)
8242 { type = TASK_TOUCH; name = "touch"; }
8244 virtual ~TaskTouch()
8245 {}
8247 virtual bool execute()
8248 {
8249 String fileName = parent.eval(fileNameOpt, "");
8251 String fullName = parent.resolve(fileName);
8252 String nativeFile = getNativePath(fullName);
8253 if (!isRegularFile(fullName) && !isDirectory(fullName))
8254 {
8255 // S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH
8256 int ret = creat(nativeFile.c_str(), 0666);
8257 if (ret != 0)
8258 {
8259 error("<touch> could not create '%s' : %s",
8260 nativeFile.c_str(), strerror(ret));
8261 return false;
8262 }
8263 return true;
8264 }
8265 int ret = utime(nativeFile.c_str(), (struct utimbuf *)0);
8266 if (ret != 0)
8267 {
8268 error("<touch> could not update the modification time for '%s' : %s",
8269 nativeFile.c_str(), strerror(ret));
8270 return false;
8271 }
8272 return true;
8273 }
8275 virtual bool parse(Element *elem)
8276 {
8277 //trace("touch parse");
8278 if (!parent.getAttribute(elem, "file", fileNameOpt))
8279 return false;
8280 if (fileNameOpt.size() == 0)
8281 {
8282 error("<touch> requires 'file=\"fileName\"' attribute");
8283 return false;
8284 }
8285 return true;
8286 }
8288 String fileNameOpt;
8289 };
8292 /**
8293 *
8294 */
8295 class TaskTstamp : public Task
8296 {
8297 public:
8299 TaskTstamp(MakeBase &par) : Task(par)
8300 { type = TASK_TSTAMP; name = "tstamp"; }
8302 virtual ~TaskTstamp()
8303 {}
8305 virtual bool execute()
8306 {
8307 return true;
8308 }
8310 virtual bool parse(Element *elem)
8311 {
8312 //trace("tstamp parse");
8313 return true;
8314 }
8315 };
8319 /**
8320 *
8321 */
8322 Task *Task::createTask(Element *elem, int lineNr)
8323 {
8324 String tagName = elem->getName();
8325 //trace("task:%s", tagName.c_str());
8326 Task *task = NULL;
8327 if (tagName == "cc")
8328 task = new TaskCC(parent);
8329 else if (tagName == "copy")
8330 task = new TaskCopy(parent);
8331 else if (tagName == "delete")
8332 task = new TaskDelete(parent);
8333 else if (tagName == "echo")
8334 task = new TaskEcho(parent);
8335 else if (tagName == "jar")
8336 task = new TaskJar(parent);
8337 else if (tagName == "javac")
8338 task = new TaskJavac(parent);
8339 else if (tagName == "link")
8340 task = new TaskLink(parent);
8341 else if (tagName == "makefile")
8342 task = new TaskMakeFile(parent);
8343 else if (tagName == "mkdir")
8344 task = new TaskMkDir(parent);
8345 else if (tagName == "msgfmt")
8346 task = new TaskMsgFmt(parent);
8347 else if (tagName == "pkg-config")
8348 task = new TaskPkgConfig(parent);
8349 else if (tagName == "ranlib")
8350 task = new TaskRanlib(parent);
8351 else if (tagName == "rc")
8352 task = new TaskRC(parent);
8353 else if (tagName == "sharedlib")
8354 task = new TaskSharedLib(parent);
8355 else if (tagName == "staticlib")
8356 task = new TaskStaticLib(parent);
8357 else if (tagName == "strip")
8358 task = new TaskStrip(parent);
8359 else if (tagName == "touch")
8360 task = new TaskTouch(parent);
8361 else if (tagName == "tstamp")
8362 task = new TaskTstamp(parent);
8363 else
8364 {
8365 error("Unknown task '%s'", tagName.c_str());
8366 return NULL;
8367 }
8369 task->setLine(lineNr);
8371 if (!task->parse(elem))
8372 {
8373 delete task;
8374 return NULL;
8375 }
8376 return task;
8377 }
8381 //########################################################################
8382 //# T A R G E T
8383 //########################################################################
8385 /**
8386 *
8387 */
8388 class Target : public MakeBase
8389 {
8391 public:
8393 /**
8394 *
8395 */
8396 Target(Make &par) : parent(par)
8397 { init(); }
8399 /**
8400 *
8401 */
8402 Target(const Target &other) : parent(other.parent)
8403 { init(); assign(other); }
8405 /**
8406 *
8407 */
8408 Target &operator=(const Target &other)
8409 { init(); assign(other); return *this; }
8411 /**
8412 *
8413 */
8414 virtual ~Target()
8415 { cleanup() ; }
8418 /**
8419 *
8420 */
8421 virtual Make &getParent()
8422 { return parent; }
8424 /**
8425 *
8426 */
8427 virtual String getName()
8428 { return name; }
8430 /**
8431 *
8432 */
8433 virtual void setName(const String &val)
8434 { name = val; }
8436 /**
8437 *
8438 */
8439 virtual String getDescription()
8440 { return description; }
8442 /**
8443 *
8444 */
8445 virtual void setDescription(const String &val)
8446 { description = val; }
8448 /**
8449 *
8450 */
8451 virtual void addDependency(const String &val)
8452 { deps.push_back(val); }
8454 /**
8455 *
8456 */
8457 virtual void parseDependencies(const String &val)
8458 { deps = tokenize(val, ", "); }
8460 /**
8461 *
8462 */
8463 virtual std::vector<String> &getDependencies()
8464 { return deps; }
8466 /**
8467 *
8468 */
8469 virtual String getIf()
8470 { return ifVar; }
8472 /**
8473 *
8474 */
8475 virtual void setIf(const String &val)
8476 { ifVar = val; }
8478 /**
8479 *
8480 */
8481 virtual String getUnless()
8482 { return unlessVar; }
8484 /**
8485 *
8486 */
8487 virtual void setUnless(const String &val)
8488 { unlessVar = val; }
8490 /**
8491 *
8492 */
8493 virtual void addTask(Task *val)
8494 { tasks.push_back(val); }
8496 /**
8497 *
8498 */
8499 virtual std::vector<Task *> &getTasks()
8500 { return tasks; }
8502 private:
8504 void init()
8505 {
8506 }
8508 void cleanup()
8509 {
8510 tasks.clear();
8511 }
8513 void assign(const Target &other)
8514 {
8515 //parent = other.parent;
8516 name = other.name;
8517 description = other.description;
8518 ifVar = other.ifVar;
8519 unlessVar = other.unlessVar;
8520 deps = other.deps;
8521 tasks = other.tasks;
8522 }
8524 Make &parent;
8526 String name;
8528 String description;
8530 String ifVar;
8532 String unlessVar;
8534 std::vector<String> deps;
8536 std::vector<Task *> tasks;
8538 };
8547 //########################################################################
8548 //# M A K E
8549 //########################################################################
8552 /**
8553 *
8554 */
8555 class Make : public MakeBase
8556 {
8558 public:
8560 /**
8561 *
8562 */
8563 Make()
8564 { init(); }
8566 /**
8567 *
8568 */
8569 Make(const Make &other)
8570 { assign(other); }
8572 /**
8573 *
8574 */
8575 Make &operator=(const Make &other)
8576 { assign(other); return *this; }
8578 /**
8579 *
8580 */
8581 virtual ~Make()
8582 { cleanup(); }
8584 /**
8585 *
8586 */
8587 virtual std::map<String, Target> &getTargets()
8588 { return targets; }
8591 /**
8592 *
8593 */
8594 virtual String version()
8595 { return BUILDTOOL_VERSION; }
8597 /**
8598 * Overload a <property>
8599 */
8600 virtual bool specifyProperty(const String &name,
8601 const String &value);
8603 /**
8604 *
8605 */
8606 virtual bool run();
8608 /**
8609 *
8610 */
8611 virtual bool run(const String &target);
8615 private:
8617 /**
8618 *
8619 */
8620 void init();
8622 /**
8623 *
8624 */
8625 void cleanup();
8627 /**
8628 *
8629 */
8630 void assign(const Make &other);
8632 /**
8633 *
8634 */
8635 bool executeTask(Task &task);
8638 /**
8639 *
8640 */
8641 bool executeTarget(Target &target,
8642 std::set<String> &targetsCompleted);
8645 /**
8646 *
8647 */
8648 bool execute();
8650 /**
8651 *
8652 */
8653 bool checkTargetDependencies(Target &prop,
8654 std::vector<String> &depList);
8656 /**
8657 *
8658 */
8659 bool parsePropertyFile(const String &fileName,
8660 const String &prefix);
8662 /**
8663 *
8664 */
8665 bool parseProperty(Element *elem);
8667 /**
8668 *
8669 */
8670 bool parseFile();
8672 /**
8673 *
8674 */
8675 std::vector<String> glob(const String &pattern);
8678 //###############
8679 //# Fields
8680 //###############
8682 String projectName;
8684 String currentTarget;
8686 String defaultTarget;
8688 String specifiedTarget;
8690 String baseDir;
8692 String description;
8694 //std::vector<Property> properties;
8696 std::map<String, Target> targets;
8698 std::vector<Task *> allTasks;
8700 std::map<String, String> specifiedProperties;
8702 };
8705 //########################################################################
8706 //# C L A S S M A I N T E N A N C E
8707 //########################################################################
8709 /**
8710 *
8711 */
8712 void Make::init()
8713 {
8714 uri = "build.xml";
8715 projectName = "";
8716 currentTarget = "";
8717 defaultTarget = "";
8718 specifiedTarget = "";
8719 baseDir = "";
8720 description = "";
8721 envPrefix = "env.";
8722 pcPrefix = "pc.";
8723 pccPrefix = "pcc.";
8724 pclPrefix = "pcl.";
8725 properties.clear();
8726 for (unsigned int i = 0 ; i < allTasks.size() ; i++)
8727 delete allTasks[i];
8728 allTasks.clear();
8729 }
8733 /**
8734 *
8735 */
8736 void Make::cleanup()
8737 {
8738 for (unsigned int i = 0 ; i < allTasks.size() ; i++)
8739 delete allTasks[i];
8740 allTasks.clear();
8741 }
8745 /**
8746 *
8747 */
8748 void Make::assign(const Make &other)
8749 {
8750 uri = other.uri;
8751 projectName = other.projectName;
8752 currentTarget = other.currentTarget;
8753 defaultTarget = other.defaultTarget;
8754 specifiedTarget = other.specifiedTarget;
8755 baseDir = other.baseDir;
8756 description = other.description;
8757 properties = other.properties;
8758 }
8762 //########################################################################
8763 //# U T I L I T Y T A S K S
8764 //########################################################################
8766 /**
8767 * Perform a file globbing
8768 */
8769 std::vector<String> Make::glob(const String &pattern)
8770 {
8771 std::vector<String> res;
8772 return res;
8773 }
8776 //########################################################################
8777 //# P U B L I C A P I
8778 //########################################################################
8782 /**
8783 *
8784 */
8785 bool Make::executeTarget(Target &target,
8786 std::set<String> &targetsCompleted)
8787 {
8789 String name = target.getName();
8791 //First get any dependencies for this target
8792 std::vector<String> deps = target.getDependencies();
8793 for (unsigned int i=0 ; i<deps.size() ; i++)
8794 {
8795 String dep = deps[i];
8796 //Did we do it already? Skip
8797 if (targetsCompleted.find(dep)!=targetsCompleted.end())
8798 continue;
8800 std::map<String, Target> &tgts =
8801 target.getParent().getTargets();
8802 std::map<String, Target>::iterator iter =
8803 tgts.find(dep);
8804 if (iter == tgts.end())
8805 {
8806 error("Target '%s' dependency '%s' not found",
8807 name.c_str(), dep.c_str());
8808 return false;
8809 }
8810 Target depTarget = iter->second;
8811 if (!executeTarget(depTarget, targetsCompleted))
8812 {
8813 return false;
8814 }
8815 }
8817 status("##### Target : %s\n##### %s", name.c_str(),
8818 target.getDescription().c_str());
8820 //Now let's do the tasks
8821 std::vector<Task *> &tasks = target.getTasks();
8822 for (unsigned int i=0 ; i<tasks.size() ; i++)
8823 {
8824 Task *task = tasks[i];
8825 status("--- %s / %s", name.c_str(), task->getName().c_str());
8826 if (!task->execute())
8827 {
8828 return false;
8829 }
8830 }
8832 targetsCompleted.insert(name);
8834 return true;
8835 }
8839 /**
8840 * Main execute() method. Start here and work
8841 * up the dependency tree
8842 */
8843 bool Make::execute()
8844 {
8845 status("######## EXECUTE");
8847 //Determine initial target
8848 if (specifiedTarget.size()>0)
8849 {
8850 currentTarget = specifiedTarget;
8851 }
8852 else if (defaultTarget.size()>0)
8853 {
8854 currentTarget = defaultTarget;
8855 }
8856 else
8857 {
8858 error("execute: no specified or default target requested");
8859 return false;
8860 }
8862 std::map<String, Target>::iterator iter =
8863 targets.find(currentTarget);
8864 if (iter == targets.end())
8865 {
8866 error("Initial target '%s' not found",
8867 currentTarget.c_str());
8868 return false;
8869 }
8871 //Now run
8872 Target target = iter->second;
8873 std::set<String> targetsCompleted;
8874 if (!executeTarget(target, targetsCompleted))
8875 {
8876 return false;
8877 }
8879 status("######## EXECUTE COMPLETE");
8880 return true;
8881 }
8886 /**
8887 *
8888 */
8889 bool Make::checkTargetDependencies(Target &target,
8890 std::vector<String> &depList)
8891 {
8892 String tgtName = target.getName().c_str();
8893 depList.push_back(tgtName);
8895 std::vector<String> deps = target.getDependencies();
8896 for (unsigned int i=0 ; i<deps.size() ; i++)
8897 {
8898 String dep = deps[i];
8899 //First thing entered was the starting Target
8900 if (dep == depList[0])
8901 {
8902 error("Circular dependency '%s' found at '%s'",
8903 dep.c_str(), tgtName.c_str());
8904 std::vector<String>::iterator diter;
8905 for (diter=depList.begin() ; diter!=depList.end() ; diter++)
8906 {
8907 error(" %s", diter->c_str());
8908 }
8909 return false;
8910 }
8912 std::map<String, Target> &tgts =
8913 target.getParent().getTargets();
8914 std::map<String, Target>::iterator titer = tgts.find(dep);
8915 if (titer == tgts.end())
8916 {
8917 error("Target '%s' dependency '%s' not found",
8918 tgtName.c_str(), dep.c_str());
8919 return false;
8920 }
8921 if (!checkTargetDependencies(titer->second, depList))
8922 {
8923 return false;
8924 }
8925 }
8926 return true;
8927 }
8933 static int getword(int pos, const String &inbuf, String &result)
8934 {
8935 int p = pos;
8936 int len = (int)inbuf.size();
8937 String val;
8938 while (p < len)
8939 {
8940 char ch = inbuf[p];
8941 if (!isalnum(ch) && ch!='.' && ch!='_')
8942 break;
8943 val.push_back(ch);
8944 p++;
8945 }
8946 result = val;
8947 return p;
8948 }
8953 /**
8954 *
8955 */
8956 bool Make::parsePropertyFile(const String &fileName,
8957 const String &prefix)
8958 {
8959 FILE *f = fopen(fileName.c_str(), "r");
8960 if (!f)
8961 {
8962 error("could not open property file %s", fileName.c_str());
8963 return false;
8964 }
8965 int linenr = 0;
8966 while (!feof(f))
8967 {
8968 char buf[256];
8969 if (!fgets(buf, 255, f))
8970 break;
8971 linenr++;
8972 String s = buf;
8973 s = trim(s);
8974 int len = s.size();
8975 if (len == 0)
8976 continue;
8977 if (s[0] == '#')
8978 continue;
8979 String key;
8980 String val;
8981 int p = 0;
8982 int p2 = getword(p, s, key);
8983 if (p2 <= p)
8984 {
8985 error("property file %s, line %d: expected keyword",
8986 fileName.c_str(), linenr);
8987 return false;
8988 }
8989 if (prefix.size() > 0)
8990 {
8991 key.insert(0, prefix);
8992 }
8994 //skip whitespace
8995 for (p=p2 ; p<len ; p++)
8996 if (!isspace(s[p]))
8997 break;
8999 if (p>=len || s[p]!='=')
9000 {
9001 error("property file %s, line %d: expected '='",
9002 fileName.c_str(), linenr);
9003 return false;
9004 }
9005 p++;
9007 //skip whitespace
9008 for ( ; p<len ; p++)
9009 if (!isspace(s[p]))
9010 break;
9012 /* This way expects a word after the =
9013 p2 = getword(p, s, val);
9014 if (p2 <= p)
9015 {
9016 error("property file %s, line %d: expected value",
9017 fileName.c_str(), linenr);
9018 return false;
9019 }
9020 */
9021 // This way gets the rest of the line after the =
9022 if (p>=len)
9023 {
9024 error("property file %s, line %d: expected value",
9025 fileName.c_str(), linenr);
9026 return false;
9027 }
9028 val = s.substr(p);
9029 if (key.size()==0)
9030 continue;
9031 //allow property to be set, even if val=""
9033 //trace("key:'%s' val:'%s'", key.c_str(), val.c_str());
9034 //See if we wanted to overload this property
9035 std::map<String, String>::iterator iter =
9036 specifiedProperties.find(key);
9037 if (iter!=specifiedProperties.end())
9038 {
9039 val = iter->second;
9040 status("overloading property '%s' = '%s'",
9041 key.c_str(), val.c_str());
9042 }
9043 properties[key] = val;
9044 }
9045 fclose(f);
9046 return true;
9047 }
9052 /**
9053 *
9054 */
9055 bool Make::parseProperty(Element *elem)
9056 {
9057 std::vector<Attribute> &attrs = elem->getAttributes();
9058 for (unsigned int i=0 ; i<attrs.size() ; i++)
9059 {
9060 String attrName = attrs[i].getName();
9061 String attrVal = attrs[i].getValue();
9063 if (attrName == "name")
9064 {
9065 String val;
9066 if (!getAttribute(elem, "value", val))
9067 return false;
9068 if (val.size() > 0)
9069 {
9070 properties[attrVal] = val;
9071 }
9072 else
9073 {
9074 if (!getAttribute(elem, "location", val))
9075 return false;
9076 //let the property exist, even if not defined
9077 properties[attrVal] = val;
9078 }
9079 //See if we wanted to overload this property
9080 std::map<String, String>::iterator iter =
9081 specifiedProperties.find(attrVal);
9082 if (iter != specifiedProperties.end())
9083 {
9084 val = iter->second;
9085 status("overloading property '%s' = '%s'",
9086 attrVal.c_str(), val.c_str());
9087 properties[attrVal] = val;
9088 }
9089 }
9090 else if (attrName == "file")
9091 {
9092 String prefix;
9093 if (!getAttribute(elem, "prefix", prefix))
9094 return false;
9095 if (prefix.size() > 0)
9096 {
9097 if (prefix[prefix.size()-1] != '.')
9098 prefix.push_back('.');
9099 }
9100 if (!parsePropertyFile(attrName, prefix))
9101 return false;
9102 }
9103 else if (attrName == "environment")
9104 {
9105 if (attrVal.find('.') != attrVal.npos)
9106 {
9107 error("environment prefix cannot have a '.' in it");
9108 return false;
9109 }
9110 envPrefix = attrVal;
9111 envPrefix.push_back('.');
9112 }
9113 else if (attrName == "pkg-config")
9114 {
9115 if (attrVal.find('.') != attrVal.npos)
9116 {
9117 error("pkg-config prefix cannot have a '.' in it");
9118 return false;
9119 }
9120 pcPrefix = attrVal;
9121 pcPrefix.push_back('.');
9122 }
9123 else if (attrName == "pkg-config-cflags")
9124 {
9125 if (attrVal.find('.') != attrVal.npos)
9126 {
9127 error("pkg-config-cflags prefix cannot have a '.' in it");
9128 return false;
9129 }
9130 pccPrefix = attrVal;
9131 pccPrefix.push_back('.');
9132 }
9133 else if (attrName == "pkg-config-libs")
9134 {
9135 if (attrVal.find('.') != attrVal.npos)
9136 {
9137 error("pkg-config-libs prefix cannot have a '.' in it");
9138 return false;
9139 }
9140 pclPrefix = attrVal;
9141 pclPrefix.push_back('.');
9142 }
9143 }
9145 return true;
9146 }
9151 /**
9152 *
9153 */
9154 bool Make::parseFile()
9155 {
9156 status("######## PARSE : %s", uri.getPath().c_str());
9158 setLine(0);
9160 Parser parser;
9161 Element *root = parser.parseFile(uri.getNativePath());
9162 if (!root)
9163 {
9164 error("Could not open %s for reading",
9165 uri.getNativePath().c_str());
9166 return false;
9167 }
9169 setLine(root->getLine());
9171 if (root->getChildren().size()==0 ||
9172 root->getChildren()[0]->getName()!="project")
9173 {
9174 error("Main xml element should be <project>");
9175 delete root;
9176 return false;
9177 }
9179 //########## Project attributes
9180 Element *project = root->getChildren()[0];
9181 String s = project->getAttribute("name");
9182 if (s.size() > 0)
9183 projectName = s;
9184 s = project->getAttribute("default");
9185 if (s.size() > 0)
9186 defaultTarget = s;
9187 s = project->getAttribute("basedir");
9188 if (s.size() > 0)
9189 baseDir = s;
9191 //######### PARSE MEMBERS
9192 std::vector<Element *> children = project->getChildren();
9193 for (unsigned int i=0 ; i<children.size() ; i++)
9194 {
9195 Element *elem = children[i];
9196 setLine(elem->getLine());
9197 String tagName = elem->getName();
9199 //########## DESCRIPTION
9200 if (tagName == "description")
9201 {
9202 description = parser.trim(elem->getValue());
9203 }
9205 //######### PROPERTY
9206 else if (tagName == "property")
9207 {
9208 if (!parseProperty(elem))
9209 return false;
9210 }
9212 //######### TARGET
9213 else if (tagName == "target")
9214 {
9215 String tname = elem->getAttribute("name");
9216 String tdesc = elem->getAttribute("description");
9217 String tdeps = elem->getAttribute("depends");
9218 String tif = elem->getAttribute("if");
9219 String tunless = elem->getAttribute("unless");
9220 Target target(*this);
9221 target.setName(tname);
9222 target.setDescription(tdesc);
9223 target.parseDependencies(tdeps);
9224 target.setIf(tif);
9225 target.setUnless(tunless);
9226 std::vector<Element *> telems = elem->getChildren();
9227 for (unsigned int i=0 ; i<telems.size() ; i++)
9228 {
9229 Element *telem = telems[i];
9230 Task breeder(*this);
9231 Task *task = breeder.createTask(telem, telem->getLine());
9232 if (!task)
9233 return false;
9234 allTasks.push_back(task);
9235 target.addTask(task);
9236 }
9238 //Check name
9239 if (tname.size() == 0)
9240 {
9241 error("no name for target");
9242 return false;
9243 }
9244 //Check for duplicate name
9245 if (targets.find(tname) != targets.end())
9246 {
9247 error("target '%s' already defined", tname.c_str());
9248 return false;
9249 }
9250 //more work than targets[tname]=target, but avoids default allocator
9251 targets.insert(std::make_pair<String, Target>(tname, target));
9252 }
9253 //######### none of the above
9254 else
9255 {
9256 error("unknown toplevel tag: <%s>", tagName.c_str());
9257 return false;
9258 }
9260 }
9262 std::map<String, Target>::iterator iter;
9263 for (iter = targets.begin() ; iter!= targets.end() ; iter++)
9264 {
9265 Target tgt = iter->second;
9266 std::vector<String> depList;
9267 if (!checkTargetDependencies(tgt, depList))
9268 {
9269 return false;
9270 }
9271 }
9274 delete root;
9275 status("######## PARSE COMPLETE");
9276 return true;
9277 }
9280 /**
9281 * Overload a <property>
9282 */
9283 bool Make::specifyProperty(const String &name, const String &value)
9284 {
9285 if (specifiedProperties.find(name) != specifiedProperties.end())
9286 {
9287 error("Property %s already specified", name.c_str());
9288 return false;
9289 }
9290 specifiedProperties[name] = value;
9291 return true;
9292 }
9296 /**
9297 *
9298 */
9299 bool Make::run()
9300 {
9301 if (!parseFile())
9302 return false;
9304 if (!execute())
9305 return false;
9307 return true;
9308 }
9313 /**
9314 * Get a formatted MM:SS.sss time elapsed string
9315 */
9316 static String
9317 timeDiffString(struct timeval &x, struct timeval &y)
9318 {
9319 long microsX = x.tv_usec;
9320 long secondsX = x.tv_sec;
9321 long microsY = y.tv_usec;
9322 long secondsY = y.tv_sec;
9323 if (microsX < microsY)
9324 {
9325 microsX += 1000000;
9326 secondsX -= 1;
9327 }
9329 int seconds = (int)(secondsX - secondsY);
9330 int millis = (int)((microsX - microsY)/1000);
9332 int minutes = seconds/60;
9333 seconds -= minutes*60;
9334 char buf[80];
9335 snprintf(buf, 79, "%dm %d.%03ds", minutes, seconds, millis);
9336 String ret = buf;
9337 return ret;
9339 }
9341 /**
9342 *
9343 */
9344 bool Make::run(const String &target)
9345 {
9346 status("####################################################");
9347 status("# %s", version().c_str());
9348 status("####################################################");
9349 struct timeval timeStart, timeEnd;
9350 ::gettimeofday(&timeStart, NULL);
9351 specifiedTarget = target;
9352 if (!run())
9353 return false;
9354 ::gettimeofday(&timeEnd, NULL);
9355 String timeStr = timeDiffString(timeEnd, timeStart);
9356 status("####################################################");
9357 status("# BuildTool Completed : %s", timeStr.c_str());
9358 status("####################################################");
9359 return true;
9360 }
9368 }// namespace buildtool
9369 //########################################################################
9370 //# M A I N
9371 //########################################################################
9373 typedef buildtool::String String;
9375 /**
9376 * Format an error message in printf() style
9377 */
9378 static void error(const char *fmt, ...)
9379 {
9380 va_list ap;
9381 va_start(ap, fmt);
9382 fprintf(stderr, "BuildTool error: ");
9383 vfprintf(stderr, fmt, ap);
9384 fprintf(stderr, "\n");
9385 va_end(ap);
9386 }
9389 static bool parseProperty(const String &s, String &name, String &val)
9390 {
9391 int len = s.size();
9392 int i;
9393 for (i=0 ; i<len ; i++)
9394 {
9395 char ch = s[i];
9396 if (ch == '=')
9397 break;
9398 name.push_back(ch);
9399 }
9400 if (i>=len || s[i]!='=')
9401 {
9402 error("property requires -Dname=value");
9403 return false;
9404 }
9405 i++;
9406 for ( ; i<len ; i++)
9407 {
9408 char ch = s[i];
9409 val.push_back(ch);
9410 }
9411 return true;
9412 }
9415 /**
9416 * Compare a buffer with a key, for the length of the key
9417 */
9418 static bool sequ(const String &buf, const char *key)
9419 {
9420 int len = buf.size();
9421 for (int i=0 ; key[i] && i<len ; i++)
9422 {
9423 if (key[i] != buf[i])
9424 return false;
9425 }
9426 return true;
9427 }
9429 static void usage(int argc, char **argv)
9430 {
9431 printf("usage:\n");
9432 printf(" %s [options] [target]\n", argv[0]);
9433 printf("Options:\n");
9434 printf(" -help, -h print this message\n");
9435 printf(" -version print the version information and exit\n");
9436 printf(" -file <file> use given buildfile\n");
9437 printf(" -f <file> ''\n");
9438 printf(" -D<property>=<value> use value for given property\n");
9439 }
9444 /**
9445 * Parse the command-line args, get our options,
9446 * and run this thing
9447 */
9448 static bool parseOptions(int argc, char **argv)
9449 {
9450 if (argc < 1)
9451 {
9452 error("Cannot parse arguments");
9453 return false;
9454 }
9456 buildtool::Make make;
9458 String target;
9460 //char *progName = argv[0];
9461 for (int i=1 ; i<argc ; i++)
9462 {
9463 String arg = argv[i];
9464 if (arg.size()>1 && arg[0]=='-')
9465 {
9466 if (arg == "-h" || arg == "-help")
9467 {
9468 usage(argc,argv);
9469 return true;
9470 }
9471 else if (arg == "-version")
9472 {
9473 printf("%s", make.version().c_str());
9474 return true;
9475 }
9476 else if (arg == "-f" || arg == "-file")
9477 {
9478 if (i>=argc)
9479 {
9480 usage(argc, argv);
9481 return false;
9482 }
9483 i++; //eat option
9484 make.setURI(argv[i]);
9485 }
9486 else if (arg.size()>2 && sequ(arg, "-D"))
9487 {
9488 String s = arg.substr(2, arg.size());
9489 String name, value;
9490 if (!parseProperty(s, name, value))
9491 {
9492 usage(argc, argv);
9493 return false;
9494 }
9495 if (!make.specifyProperty(name, value))
9496 return false;
9497 }
9498 else
9499 {
9500 error("Unknown option:%s", arg.c_str());
9501 return false;
9502 }
9503 }
9504 else
9505 {
9506 if (target.size()>0)
9507 {
9508 error("only one initial target");
9509 usage(argc, argv);
9510 return false;
9511 }
9512 target = arg;
9513 }
9514 }
9516 //We have the options. Now execute them
9517 if (!make.run(target))
9518 return false;
9520 return true;
9521 }
9526 /*
9527 static bool runMake()
9528 {
9529 buildtool::Make make;
9530 if (!make.run())
9531 return false;
9532 return true;
9533 }
9536 static bool pkgConfigTest()
9537 {
9538 buildtool::PkgConfig pkgConfig;
9539 if (!pkgConfig.readFile("gtk+-2.0.pc"))
9540 return false;
9541 return true;
9542 }
9546 static bool depTest()
9547 {
9548 buildtool::DepTool deptool;
9549 deptool.setSourceDirectory("/dev/ink/inkscape/src");
9550 if (!deptool.generateDependencies("build.dep"))
9551 return false;
9552 std::vector<buildtool::FileRec> res =
9553 deptool.loadDepFile("build.dep");
9554 if (res.size() == 0)
9555 return false;
9556 return true;
9557 }
9559 static bool popenTest()
9560 {
9561 buildtool::Make make;
9562 buildtool::String out, err;
9563 bool ret = make.executeCommand("gcc xx.cpp", "", out, err);
9564 printf("Popen test:%d '%s' '%s'\n", ret, out.c_str(), err.c_str());
9565 return true;
9566 }
9569 static bool propFileTest()
9570 {
9571 buildtool::Make make;
9572 make.parsePropertyFile("test.prop", "test.");
9573 return true;
9574 }
9575 */
9577 int main(int argc, char **argv)
9578 {
9580 if (!parseOptions(argc, argv))
9581 return 1;
9582 /*
9583 if (!popenTest())
9584 return 1;
9586 if (!depTest())
9587 return 1;
9588 if (!propFileTest())
9589 return 1;
9590 if (runMake())
9591 return 1;
9592 */
9593 return 0;
9594 }
9597 //########################################################################
9598 //# E N D
9599 //########################################################################