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.2"
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 * called by getSubstitutions(). This is in case a looked-up string
3282 * has substitutions also.
3283 */
3284 bool getSubstitutionsRecursive(const String &s, String &result, int depth);
3286 /**
3287 * replace variable refs in a string like ${a} with their values
3288 */
3289 bool getSubstitutions(const String &s, String &result);
3291 int line;
3294 };
3298 /**
3299 * Define the pkg-config class here, since it will be used in MakeBase method
3300 * implementations.
3301 */
3302 class PkgConfig : public MakeBase
3303 {
3305 public:
3307 /**
3308 *
3309 */
3310 PkgConfig()
3311 {
3312 path = ".";
3313 prefix = "/target";
3314 init();
3315 }
3317 /**
3318 *
3319 */
3320 PkgConfig(const PkgConfig &other)
3321 { assign(other); }
3323 /**
3324 *
3325 */
3326 PkgConfig &operator=(const PkgConfig &other)
3327 { assign(other); return *this; }
3329 /**
3330 *
3331 */
3332 virtual ~PkgConfig()
3333 { }
3335 /**
3336 *
3337 */
3338 virtual String getName()
3339 { return name; }
3341 /**
3342 *
3343 */
3344 virtual String getPath()
3345 { return path; }
3347 /**
3348 *
3349 */
3350 virtual void setPath(const String &val)
3351 { path = val; }
3353 /**
3354 *
3355 */
3356 virtual String getPrefix()
3357 { return prefix; }
3359 /**
3360 * Allow the user to override the prefix in the file
3361 */
3362 virtual void setPrefix(const String &val)
3363 { prefix = val; }
3365 /**
3366 *
3367 */
3368 virtual String getDescription()
3369 { return description; }
3371 /**
3372 *
3373 */
3374 virtual String getCflags()
3375 { return cflags; }
3377 /**
3378 *
3379 */
3380 virtual String getLibs()
3381 { return libs; }
3383 /**
3384 *
3385 */
3386 virtual String getAll()
3387 {
3388 String ret = cflags;
3389 ret.append(" ");
3390 ret.append(libs);
3391 return ret;
3392 }
3394 /**
3395 *
3396 */
3397 virtual String getVersion()
3398 { return version; }
3400 /**
3401 *
3402 */
3403 virtual int getMajorVersion()
3404 { return majorVersion; }
3406 /**
3407 *
3408 */
3409 virtual int getMinorVersion()
3410 { return minorVersion; }
3412 /**
3413 *
3414 */
3415 virtual int getMicroVersion()
3416 { return microVersion; }
3418 /**
3419 *
3420 */
3421 virtual std::map<String, String> &getAttributes()
3422 { return attrs; }
3424 /**
3425 *
3426 */
3427 virtual std::vector<String> &getRequireList()
3428 { return requireList; }
3430 /**
3431 * Read a file for its details
3432 */
3433 virtual bool readFile(const String &fileName);
3435 /**
3436 * Read a file for its details
3437 */
3438 virtual bool query(const String &name);
3440 private:
3442 void init()
3443 {
3444 //do not set path and prefix here
3445 name = "";
3446 description = "";
3447 cflags = "";
3448 libs = "";
3449 requires = "";
3450 version = "";
3451 majorVersion = 0;
3452 minorVersion = 0;
3453 microVersion = 0;
3454 fileName = "";
3455 attrs.clear();
3456 requireList.clear();
3457 }
3459 void assign(const PkgConfig &other)
3460 {
3461 name = other.name;
3462 path = other.path;
3463 prefix = other.prefix;
3464 description = other.description;
3465 cflags = other.cflags;
3466 libs = other.libs;
3467 requires = other.requires;
3468 version = other.version;
3469 majorVersion = other.majorVersion;
3470 minorVersion = other.minorVersion;
3471 microVersion = other.microVersion;
3472 fileName = other.fileName;
3473 attrs = other.attrs;
3474 requireList = other.requireList;
3475 }
3479 int get(int pos);
3481 int skipwhite(int pos);
3483 int getword(int pos, String &ret);
3485 /**
3486 * Very important
3487 */
3488 bool parseRequires();
3490 void parseVersion();
3492 bool parseLine(const String &lineBuf);
3494 bool parse(const String &buf);
3496 void dumpAttrs();
3498 String name;
3500 String path;
3502 String prefix;
3504 String description;
3506 String cflags;
3508 String libs;
3510 String requires;
3512 String version;
3514 int majorVersion;
3516 int minorVersion;
3518 int microVersion;
3520 String fileName;
3522 std::map<String, String> attrs;
3524 std::vector<String> requireList;
3526 char *parsebuf;
3527 int parselen;
3528 };
3533 /**
3534 * Print a printf()-like formatted error message
3535 */
3536 void MakeBase::error(const char *fmt, ...)
3537 {
3538 va_list args;
3539 va_start(args,fmt);
3540 fprintf(stderr, "Make error line %d: ", line);
3541 vfprintf(stderr, fmt, args);
3542 fprintf(stderr, "\n");
3543 va_end(args) ;
3544 }
3548 /**
3549 * Print a printf()-like formatted trace message
3550 */
3551 void MakeBase::status(const char *fmt, ...)
3552 {
3553 va_list args;
3554 va_start(args,fmt);
3555 //fprintf(stdout, " ");
3556 vfprintf(stdout, fmt, args);
3557 fprintf(stdout, "\n");
3558 va_end(args) ;
3559 }
3562 /**
3563 * Resolve another path relative to this one
3564 */
3565 String MakeBase::resolve(const String &otherPath)
3566 {
3567 URI otherURI(otherPath);
3568 URI fullURI = uri.resolve(otherURI);
3569 String ret = fullURI.toString();
3570 return ret;
3571 }
3574 /**
3575 * Print a printf()-like formatted trace message
3576 */
3577 void MakeBase::trace(const char *fmt, ...)
3578 {
3579 va_list args;
3580 va_start(args,fmt);
3581 fprintf(stdout, "Make: ");
3582 vfprintf(stdout, fmt, args);
3583 fprintf(stdout, "\n");
3584 va_end(args) ;
3585 }
3589 /**
3590 * Check if a given string matches a given regex pattern
3591 */
3592 bool MakeBase::regexMatch(const String &str, const String &pattern)
3593 {
3594 const TRexChar *terror = NULL;
3595 const TRexChar *cpat = pattern.c_str();
3596 TRex *expr = trex_compile(cpat, &terror);
3597 if (!expr)
3598 {
3599 if (!terror)
3600 terror = "undefined";
3601 error("compilation error [%s]!\n", terror);
3602 return false;
3603 }
3605 bool ret = true;
3607 const TRexChar *cstr = str.c_str();
3608 if (trex_match(expr, cstr))
3609 {
3610 ret = true;
3611 }
3612 else
3613 {
3614 ret = false;
3615 }
3617 trex_free(expr);
3619 return ret;
3620 }
3622 /**
3623 * Return the suffix, if any, of a file name
3624 */
3625 String MakeBase::getSuffix(const String &fname)
3626 {
3627 if (fname.size() < 2)
3628 return "";
3629 unsigned int pos = fname.find_last_of('.');
3630 if (pos == fname.npos)
3631 return "";
3632 pos++;
3633 String res = fname.substr(pos, fname.size()-pos);
3634 //trace("suffix:%s", res.c_str());
3635 return res;
3636 }
3640 /**
3641 * Break up a string into substrings delimited the characters
3642 * in delimiters. Null-length substrings are ignored
3643 */
3644 std::vector<String> MakeBase::tokenize(const String &str,
3645 const String &delimiters)
3646 {
3648 std::vector<String> res;
3649 char *del = (char *)delimiters.c_str();
3650 String dmp;
3651 for (unsigned int i=0 ; i<str.size() ; i++)
3652 {
3653 char ch = str[i];
3654 char *p = (char *)0;
3655 for (p=del ; *p ; p++)
3656 if (*p == ch)
3657 break;
3658 if (*p)
3659 {
3660 if (dmp.size() > 0)
3661 {
3662 res.push_back(dmp);
3663 dmp.clear();
3664 }
3665 }
3666 else
3667 {
3668 dmp.push_back(ch);
3669 }
3670 }
3671 //Add tail
3672 if (dmp.size() > 0)
3673 {
3674 res.push_back(dmp);
3675 dmp.clear();
3676 }
3678 return res;
3679 }
3683 /**
3684 * replace runs of whitespace with a single space
3685 */
3686 String MakeBase::strip(const String &s)
3687 {
3688 int len = s.size();
3689 String stripped;
3690 for (int i = 0 ; i<len ; i++)
3691 {
3692 char ch = s[i];
3693 if (isspace(ch))
3694 {
3695 stripped.push_back(' ');
3696 for ( ; i<len ; i++)
3697 {
3698 ch = s[i];
3699 if (!isspace(ch))
3700 {
3701 stripped.push_back(ch);
3702 break;
3703 }
3704 }
3705 }
3706 else
3707 {
3708 stripped.push_back(ch);
3709 }
3710 }
3711 return stripped;
3712 }
3714 /**
3715 * remove leading whitespace from each line
3716 */
3717 String MakeBase::leftJustify(const String &s)
3718 {
3719 String out;
3720 int len = s.size();
3721 for (int i = 0 ; i<len ; )
3722 {
3723 char ch;
3724 //Skip to first visible character
3725 while (i<len)
3726 {
3727 ch = s[i];
3728 if (ch == '\n' || ch == '\r'
3729 || !isspace(ch))
3730 break;
3731 i++;
3732 }
3733 //Copy the rest of the line
3734 while (i<len)
3735 {
3736 ch = s[i];
3737 if (ch == '\n' || ch == '\r')
3738 {
3739 if (ch != '\r')
3740 out.push_back('\n');
3741 i++;
3742 break;
3743 }
3744 else
3745 {
3746 out.push_back(ch);
3747 }
3748 i++;
3749 }
3750 }
3751 return out;
3752 }
3755 /**
3756 * Removes whitespace from beginning and end of a string
3757 */
3758 String MakeBase::trim(const String &s)
3759 {
3760 if (s.size() < 1)
3761 return s;
3763 //Find first non-ws char
3764 unsigned int begin = 0;
3765 for ( ; begin < s.size() ; begin++)
3766 {
3767 if (!isspace(s[begin]))
3768 break;
3769 }
3771 //Find first non-ws char, going in reverse
3772 unsigned int end = s.size() - 1;
3773 for ( ; end > begin ; end--)
3774 {
3775 if (!isspace(s[end]))
3776 break;
3777 }
3778 //trace("begin:%d end:%d", begin, end);
3780 String res = s.substr(begin, end-begin+1);
3781 return res;
3782 }
3785 /**
3786 * Return a lower case version of the given string
3787 */
3788 String MakeBase::toLower(const String &s)
3789 {
3790 if (s.size()==0)
3791 return s;
3793 String ret;
3794 for(unsigned int i=0; i<s.size() ; i++)
3795 {
3796 ret.push_back(tolower(s[i]));
3797 }
3798 return ret;
3799 }
3802 /**
3803 * Return the native format of the canonical
3804 * path which we store
3805 */
3806 String MakeBase::getNativePath(const String &path)
3807 {
3808 #ifdef __WIN32__
3809 String npath;
3810 unsigned int firstChar = 0;
3811 if (path.size() >= 3)
3812 {
3813 if (path[0] == '/' &&
3814 isalpha(path[1]) &&
3815 path[2] == ':')
3816 firstChar++;
3817 }
3818 for (unsigned int i=firstChar ; i<path.size() ; i++)
3819 {
3820 char ch = path[i];
3821 if (ch == '/')
3822 npath.push_back('\\');
3823 else
3824 npath.push_back(ch);
3825 }
3826 return npath;
3827 #else
3828 return path;
3829 #endif
3830 }
3833 #ifdef __WIN32__
3834 #include <tchar.h>
3836 static String win32LastError()
3837 {
3839 DWORD dw = GetLastError();
3841 LPVOID str;
3842 FormatMessage(
3843 FORMAT_MESSAGE_ALLOCATE_BUFFER |
3844 FORMAT_MESSAGE_FROM_SYSTEM,
3845 NULL,
3846 dw,
3847 0,
3848 (LPTSTR) &str,
3849 0, NULL );
3850 LPTSTR p = _tcschr((const char *)str, _T('\r'));
3851 if(p != NULL)
3852 { // lose CRLF
3853 *p = _T('\0');
3854 }
3855 String ret = (char *)str;
3856 LocalFree(str);
3858 return ret;
3859 }
3860 #endif
3864 /**
3865 * Execute a system call, using pipes to send data to the
3866 * program's stdin, and reading stdout and stderr.
3867 */
3868 bool MakeBase::executeCommand(const String &command,
3869 const String &inbuf,
3870 String &outbuf,
3871 String &errbuf)
3872 {
3874 status("============ cmd ============\n%s\n=============================",
3875 command.c_str());
3877 outbuf.clear();
3878 errbuf.clear();
3880 #ifdef __WIN32__
3882 /*
3883 I really hate having win32 code in this program, but the
3884 read buffer in command.com and cmd.exe are just too small
3885 for the large commands we need for compiling and linking.
3886 */
3888 bool ret = true;
3890 //# Allocate a separate buffer for safety
3891 char *paramBuf = new char[command.size() + 1];
3892 if (!paramBuf)
3893 {
3894 error("executeCommand cannot allocate command buffer");
3895 return false;
3896 }
3897 strcpy(paramBuf, (char *)command.c_str());
3899 //# Go to http://msdn2.microsoft.com/en-us/library/ms682499.aspx
3900 //# to see how Win32 pipes work
3902 //# Create pipes
3903 SECURITY_ATTRIBUTES saAttr;
3904 saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
3905 saAttr.bInheritHandle = TRUE;
3906 saAttr.lpSecurityDescriptor = NULL;
3907 HANDLE stdinRead, stdinWrite;
3908 HANDLE stdoutRead, stdoutWrite;
3909 HANDLE stderrRead, stderrWrite;
3910 if (!CreatePipe(&stdinRead, &stdinWrite, &saAttr, 0))
3911 {
3912 error("executeProgram: could not create pipe");
3913 delete[] paramBuf;
3914 return false;
3915 }
3916 SetHandleInformation(stdinWrite, HANDLE_FLAG_INHERIT, 0);
3917 if (!CreatePipe(&stdoutRead, &stdoutWrite, &saAttr, 0))
3918 {
3919 error("executeProgram: could not create pipe");
3920 delete[] paramBuf;
3921 return false;
3922 }
3923 SetHandleInformation(stdoutRead, HANDLE_FLAG_INHERIT, 0);
3924 if (!CreatePipe(&stderrRead, &stderrWrite, &saAttr, 0))
3925 {
3926 error("executeProgram: could not create pipe");
3927 delete[] paramBuf;
3928 return false;
3929 }
3930 SetHandleInformation(stderrRead, HANDLE_FLAG_INHERIT, 0);
3932 // Create the process
3933 STARTUPINFO siStartupInfo;
3934 PROCESS_INFORMATION piProcessInfo;
3935 memset(&siStartupInfo, 0, sizeof(siStartupInfo));
3936 memset(&piProcessInfo, 0, sizeof(piProcessInfo));
3937 siStartupInfo.cb = sizeof(siStartupInfo);
3938 siStartupInfo.hStdError = stderrWrite;
3939 siStartupInfo.hStdOutput = stdoutWrite;
3940 siStartupInfo.hStdInput = stdinRead;
3941 siStartupInfo.dwFlags |= STARTF_USESTDHANDLES;
3943 if (!CreateProcess(NULL, paramBuf, NULL, NULL, true,
3944 0, NULL, NULL, &siStartupInfo,
3945 &piProcessInfo))
3946 {
3947 error("executeCommand : could not create process : %s",
3948 win32LastError().c_str());
3949 ret = false;
3950 }
3952 delete[] paramBuf;
3954 DWORD bytesWritten;
3955 if (inbuf.size()>0 &&
3956 !WriteFile(stdinWrite, inbuf.c_str(), inbuf.size(),
3957 &bytesWritten, NULL))
3958 {
3959 error("executeCommand: could not write to pipe");
3960 return false;
3961 }
3962 if (!CloseHandle(stdinWrite))
3963 {
3964 error("executeCommand: could not close write pipe");
3965 return false;
3966 }
3967 if (!CloseHandle(stdoutWrite))
3968 {
3969 error("executeCommand: could not close read pipe");
3970 return false;
3971 }
3972 if (!CloseHandle(stderrWrite))
3973 {
3974 error("executeCommand: could not close read pipe");
3975 return false;
3976 }
3978 bool lastLoop = false;
3979 while (true)
3980 {
3981 DWORD avail;
3982 DWORD bytesRead;
3983 char readBuf[4096];
3985 //trace("## stderr");
3986 PeekNamedPipe(stderrRead, NULL, 0, NULL, &avail, NULL);
3987 if (avail > 0)
3988 {
3989 bytesRead = 0;
3990 if (avail>4096) avail = 4096;
3991 ReadFile(stderrRead, readBuf, avail, &bytesRead, NULL);
3992 if (bytesRead > 0)
3993 {
3994 for (unsigned int i=0 ; i<bytesRead ; i++)
3995 errbuf.push_back(readBuf[i]);
3996 }
3997 }
3999 //trace("## stdout");
4000 PeekNamedPipe(stdoutRead, NULL, 0, NULL, &avail, NULL);
4001 if (avail > 0)
4002 {
4003 bytesRead = 0;
4004 if (avail>4096) avail = 4096;
4005 ReadFile(stdoutRead, readBuf, avail, &bytesRead, NULL);
4006 if (bytesRead > 0)
4007 {
4008 for (unsigned int i=0 ; i<bytesRead ; i++)
4009 outbuf.push_back(readBuf[i]);
4010 }
4011 }
4013 //Was this the final check after program done?
4014 if (lastLoop)
4015 break;
4017 DWORD exitCode;
4018 GetExitCodeProcess(piProcessInfo.hProcess, &exitCode);
4019 if (exitCode != STILL_ACTIVE)
4020 lastLoop = true;
4022 Sleep(10);
4023 }
4024 //trace("outbuf:%s", outbuf.c_str());
4025 if (!CloseHandle(stdoutRead))
4026 {
4027 error("executeCommand: could not close read pipe");
4028 return false;
4029 }
4030 if (!CloseHandle(stderrRead))
4031 {
4032 error("executeCommand: could not close read pipe");
4033 return false;
4034 }
4036 DWORD exitCode;
4037 GetExitCodeProcess(piProcessInfo.hProcess, &exitCode);
4038 //trace("exit code:%d", exitCode);
4039 if (exitCode != 0)
4040 {
4041 ret = false;
4042 }
4044 CloseHandle(piProcessInfo.hProcess);
4045 CloseHandle(piProcessInfo.hThread);
4047 return ret;
4049 #else //do it unix-style
4051 String s;
4052 FILE *f = popen(command.c_str(), "r");
4053 int errnum = 0;
4054 if (f)
4055 {
4056 while (true)
4057 {
4058 int ch = fgetc(f);
4059 if (ch < 0)
4060 break;
4061 s.push_back((char)ch);
4062 }
4063 errnum = pclose(f);
4064 }
4065 outbuf = s;
4066 if (errnum != 0)
4067 {
4068 error("exec of command '%s' failed : %s",
4069 command.c_str(), strerror(errno));
4070 return false;
4071 }
4072 else
4073 return true;
4075 #endif
4076 }
4081 bool MakeBase::listDirectories(const String &baseName,
4082 const String &dirName,
4083 std::vector<String> &res)
4084 {
4085 res.push_back(dirName);
4086 String fullPath = baseName;
4087 if (dirName.size()>0)
4088 {
4089 fullPath.append("/");
4090 fullPath.append(dirName);
4091 }
4092 DIR *dir = opendir(fullPath.c_str());
4093 while (true)
4094 {
4095 struct dirent *de = readdir(dir);
4096 if (!de)
4097 break;
4099 //Get the directory member name
4100 String s = de->d_name;
4101 if (s.size() == 0 || s[0] == '.')
4102 continue;
4103 String childName = dirName;
4104 childName.append("/");
4105 childName.append(s);
4107 String fullChildPath = baseName;
4108 fullChildPath.append("/");
4109 fullChildPath.append(childName);
4110 struct stat finfo;
4111 String childNative = getNativePath(fullChildPath);
4112 if (stat(childNative.c_str(), &finfo)<0)
4113 {
4114 error("cannot stat file:%s", childNative.c_str());
4115 }
4116 else if (S_ISDIR(finfo.st_mode))
4117 {
4118 //trace("directory: %s", childName.c_str());
4119 if (!listDirectories(baseName, childName, res))
4120 return false;
4121 }
4122 }
4123 closedir(dir);
4125 return true;
4126 }
4129 bool MakeBase::listFiles(const String &baseDir,
4130 const String &dirName,
4131 std::vector<String> &res)
4132 {
4133 String fullDir = baseDir;
4134 if (dirName.size()>0)
4135 {
4136 fullDir.append("/");
4137 fullDir.append(dirName);
4138 }
4139 String dirNative = getNativePath(fullDir);
4141 std::vector<String> subdirs;
4142 DIR *dir = opendir(dirNative.c_str());
4143 if (!dir)
4144 {
4145 error("Could not open directory %s : %s",
4146 dirNative.c_str(), strerror(errno));
4147 return false;
4148 }
4149 while (true)
4150 {
4151 struct dirent *de = readdir(dir);
4152 if (!de)
4153 break;
4155 //Get the directory member name
4156 String s = de->d_name;
4157 if (s.size() == 0 || s[0] == '.')
4158 continue;
4159 String childName;
4160 if (dirName.size()>0)
4161 {
4162 childName.append(dirName);
4163 childName.append("/");
4164 }
4165 childName.append(s);
4166 String fullChild = baseDir;
4167 fullChild.append("/");
4168 fullChild.append(childName);
4170 if (isDirectory(fullChild))
4171 {
4172 //trace("directory: %s", childName.c_str());
4173 if (!listFiles(baseDir, childName, res))
4174 return false;
4175 continue;
4176 }
4177 else if (!isRegularFile(fullChild))
4178 {
4179 error("unknown file:%s", childName.c_str());
4180 return false;
4181 }
4183 //all done!
4184 res.push_back(childName);
4186 }
4187 closedir(dir);
4189 return true;
4190 }
4193 /**
4194 * Several different classes extend MakeBase. By "propRef", we mean
4195 * the one holding the properties. Likely "Make" itself
4196 */
4197 bool MakeBase::listFiles(MakeBase &propRef, FileSet &fileSet)
4198 {
4199 //before doing the list, resolve any property references
4200 //that might have been specified in the directory name, such as ${src}
4201 String fsDir = fileSet.getDirectory();
4202 String dir;
4203 if (!propRef.getSubstitutions(fsDir, dir))
4204 return false;
4205 String baseDir = propRef.resolve(dir);
4206 std::vector<String> fileList;
4207 if (!listFiles(baseDir, "", fileList))
4208 return false;
4210 std::vector<String> includes = fileSet.getIncludes();
4211 std::vector<String> excludes = fileSet.getExcludes();
4213 std::vector<String> incs;
4214 std::vector<String>::iterator iter;
4216 std::sort(fileList.begin(), fileList.end());
4218 //If there are <includes>, then add files to the output
4219 //in the order of the include list
4220 if (includes.size()==0)
4221 incs = fileList;
4222 else
4223 {
4224 for (iter = includes.begin() ; iter != includes.end() ; iter++)
4225 {
4226 String &pattern = *iter;
4227 std::vector<String>::iterator siter;
4228 for (siter = fileList.begin() ; siter != fileList.end() ; siter++)
4229 {
4230 String s = *siter;
4231 if (regexMatch(s, pattern))
4232 {
4233 //trace("INCLUDED:%s", s.c_str());
4234 incs.push_back(s);
4235 }
4236 }
4237 }
4238 }
4240 //Now trim off the <excludes>
4241 std::vector<String> res;
4242 for (iter = incs.begin() ; iter != incs.end() ; iter++)
4243 {
4244 String s = *iter;
4245 bool skipme = false;
4246 std::vector<String>::iterator siter;
4247 for (siter = excludes.begin() ; siter != excludes.end() ; siter++)
4248 {
4249 String &pattern = *siter;
4250 if (regexMatch(s, pattern))
4251 {
4252 //trace("EXCLUDED:%s", s.c_str());
4253 skipme = true;
4254 break;
4255 }
4256 }
4257 if (!skipme)
4258 res.push_back(s);
4259 }
4261 fileSet.setFiles(res);
4263 return true;
4264 }
4267 /**
4268 * 0 == all, 1 = cflags, 2 = libs
4269 */
4270 bool MakeBase::pkgConfigRecursive(const String packageName,
4271 const String &path,
4272 const String &prefix,
4273 int query,
4274 String &result,
4275 std::set<String> &deplist)
4276 {
4277 PkgConfig pkgConfig;
4278 if (path.size() > 0)
4279 pkgConfig.setPath(path);
4280 if (prefix.size() > 0)
4281 pkgConfig.setPrefix(prefix);
4282 if (!pkgConfig.query(packageName))
4283 return false;
4284 if (query == 0)
4285 result = pkgConfig.getAll();
4286 else if (query == 1)
4287 result = pkgConfig.getCflags();
4288 else
4289 result = pkgConfig.getLibs();
4290 deplist.insert(packageName);
4291 std::vector<String> list = pkgConfig.getRequireList();
4292 for (unsigned int i = 0 ; i<list.size() ; i++)
4293 {
4294 String depPkgName = list[i];
4295 if (deplist.find(depPkgName) != deplist.end())
4296 continue;
4297 String val;
4298 if (!pkgConfigRecursive(depPkgName, path, prefix, query, val, deplist))
4299 {
4300 error("Based on 'requires' attribute of package '%s'", packageName.c_str());
4301 return false;
4302 }
4303 result.append(" ");
4304 result.append(val);
4305 }
4307 return true;
4308 }
4310 bool MakeBase::pkgConfigQuery(const String &packageName, int query, String &result)
4311 {
4312 std::set<String> deplist;
4313 String path = getProperty("pkg-config-path");
4314 if (path.size()>0)
4315 path = resolve(path);
4316 String prefix = getProperty("pkg-config-prefix");
4317 String val;
4318 if (!pkgConfigRecursive(packageName, path, prefix, query, val, deplist))
4319 return false;
4320 result = val;
4321 return true;
4322 }
4326 /**
4327 * replace a variable ref like ${a} with a value
4328 */
4329 bool MakeBase::lookupProperty(const String &propertyName, String &result)
4330 {
4331 String varname = propertyName;
4332 if (envPrefix.size() > 0 &&
4333 varname.compare(0, envPrefix.size(), envPrefix) == 0)
4334 {
4335 varname = varname.substr(envPrefix.size());
4336 char *envstr = getenv(varname.c_str());
4337 if (!envstr)
4338 {
4339 error("environment variable '%s' not defined", varname.c_str());
4340 return false;
4341 }
4342 result = envstr;
4343 }
4344 else if (pcPrefix.size() > 0 &&
4345 varname.compare(0, pcPrefix.size(), pcPrefix) == 0)
4346 {
4347 varname = varname.substr(pcPrefix.size());
4348 String val;
4349 if (!pkgConfigQuery(varname, 0, val))
4350 return false;
4351 result = val;
4352 }
4353 else if (pccPrefix.size() > 0 &&
4354 varname.compare(0, pccPrefix.size(), pccPrefix) == 0)
4355 {
4356 varname = varname.substr(pccPrefix.size());
4357 String val;
4358 if (!pkgConfigQuery(varname, 1, val))
4359 return false;
4360 result = val;
4361 }
4362 else if (pclPrefix.size() > 0 &&
4363 varname.compare(0, pclPrefix.size(), pclPrefix) == 0)
4364 {
4365 varname = varname.substr(pclPrefix.size());
4366 String val;
4367 if (!pkgConfigQuery(varname, 2, val))
4368 return false;
4369 result = val;
4370 }
4371 else
4372 {
4373 std::map<String, String>::iterator iter;
4374 iter = properties.find(varname);
4375 if (iter != properties.end())
4376 {
4377 result = iter->second;
4378 }
4379 else
4380 {
4381 error("property '%s' not found", varname.c_str());
4382 return false;
4383 }
4384 }
4385 return true;
4386 }
4391 /**
4392 * Analyse a string, looking for any substitutions or other
4393 * things that need resolution
4394 */
4395 bool MakeBase::getSubstitutionsRecursive(const String &str,
4396 String &result, int depth)
4397 {
4398 if (depth > 10)
4399 {
4400 error("nesting of substitutions too deep (>10) for '%s'",
4401 str.c_str());
4402 return false;
4403 }
4404 String s = trim(str);
4405 int len = (int)s.size();
4406 String val;
4407 for (int i=0 ; i<len ; i++)
4408 {
4409 char ch = s[i];
4410 if (ch == '$' && s[i+1] == '{')
4411 {
4412 String varname;
4413 int j = i+2;
4414 for ( ; j<len ; j++)
4415 {
4416 ch = s[j];
4417 if (ch == '$' && s[j+1] == '{')
4418 {
4419 error("attribute %s cannot have nested variable references",
4420 s.c_str());
4421 return false;
4422 }
4423 else if (ch == '}')
4424 {
4425 varname = trim(varname);
4426 String varval;
4427 if (!lookupProperty(varname, varval))
4428 return false;
4429 String varval2;
4430 //Now see if the answer has ${} in it, too
4431 if (!getSubstitutionsRecursive(varval, varval2, depth + 1))
4432 return false;
4433 val.append(varval2);
4434 break;
4435 }
4436 else
4437 {
4438 varname.push_back(ch);
4439 }
4440 }
4441 i = j;
4442 }
4443 else
4444 {
4445 val.push_back(ch);
4446 }
4447 }
4448 result = val;
4449 return true;
4450 }
4452 /**
4453 * Analyse a string, looking for any substitutions or other
4454 * things that need resilution
4455 */
4456 bool MakeBase::getSubstitutions(const String &str, String &result)
4457 {
4458 return getSubstitutionsRecursive(str, result, 0);
4459 }
4463 /**
4464 * replace variable refs like ${a} with their values
4465 * Assume that the string has already been syntax validated
4466 */
4467 String MakeBase::eval(const String &s, const String &defaultVal)
4468 {
4469 if (s.size()==0)
4470 return defaultVal;
4471 String ret;
4472 if (getSubstitutions(s, ret))
4473 return ret;
4474 else
4475 return defaultVal;
4476 }
4479 /**
4480 * replace variable refs like ${a} with their values
4481 * return true or false
4482 * Assume that the string has already been syntax validated
4483 */
4484 bool MakeBase::evalBool(const String &s, bool defaultVal)
4485 {
4486 if (s.size()==0)
4487 return defaultVal;
4488 String val = eval(s, "false");
4489 if (s == "true" || s == "TRUE")
4490 return true;
4491 else
4492 return defaultVal;
4493 }
4496 /**
4497 * Get a string attribute, testing it for proper syntax and
4498 * property names.
4499 */
4500 bool MakeBase::getAttribute(Element *elem, const String &name,
4501 String &result)
4502 {
4503 String s = elem->getAttribute(name);
4504 String tmp;
4505 bool ret = getSubstitutions(s, tmp);
4506 if (ret)
4507 result = s; //assign -if- ok
4508 return ret;
4509 }
4512 /**
4513 * Get a string value, testing it for proper syntax and
4514 * property names.
4515 */
4516 bool MakeBase::getValue(Element *elem, String &result)
4517 {
4518 String s = elem->getValue();
4519 String tmp;
4520 bool ret = getSubstitutions(s, tmp);
4521 if (ret)
4522 result = s; //assign -if- ok
4523 return ret;
4524 }
4529 /**
4530 * Parse a <patternset> entry
4531 */
4532 bool MakeBase::parsePatternSet(Element *elem,
4533 MakeBase &propRef,
4534 std::vector<String> &includes,
4535 std::vector<String> &excludes
4536 )
4537 {
4538 std::vector<Element *> children = elem->getChildren();
4539 for (unsigned int i=0 ; i<children.size() ; i++)
4540 {
4541 Element *child = children[i];
4542 String tagName = child->getName();
4543 if (tagName == "exclude")
4544 {
4545 String fname;
4546 if (!propRef.getAttribute(child, "name", fname))
4547 return false;
4548 //trace("EXCLUDE: %s", fname.c_str());
4549 excludes.push_back(fname);
4550 }
4551 else if (tagName == "include")
4552 {
4553 String fname;
4554 if (!propRef.getAttribute(child, "name", fname))
4555 return false;
4556 //trace("INCLUDE: %s", fname.c_str());
4557 includes.push_back(fname);
4558 }
4559 }
4561 return true;
4562 }
4567 /**
4568 * Parse a <fileset> entry, and determine which files
4569 * should be included
4570 */
4571 bool MakeBase::parseFileSet(Element *elem,
4572 MakeBase &propRef,
4573 FileSet &fileSet)
4574 {
4575 String name = elem->getName();
4576 if (name != "fileset")
4577 {
4578 error("expected <fileset>");
4579 return false;
4580 }
4583 std::vector<String> includes;
4584 std::vector<String> excludes;
4586 //A fileset has one implied patternset
4587 if (!parsePatternSet(elem, propRef, includes, excludes))
4588 {
4589 return false;
4590 }
4591 //Look for child tags, including more patternsets
4592 std::vector<Element *> children = elem->getChildren();
4593 for (unsigned int i=0 ; i<children.size() ; i++)
4594 {
4595 Element *child = children[i];
4596 String tagName = child->getName();
4597 if (tagName == "patternset")
4598 {
4599 if (!parsePatternSet(child, propRef, includes, excludes))
4600 {
4601 return false;
4602 }
4603 }
4604 }
4606 String dir;
4607 //Now do the stuff
4608 //Get the base directory for reading file names
4609 if (!propRef.getAttribute(elem, "dir", dir))
4610 return false;
4612 fileSet.setDirectory(dir);
4613 fileSet.setIncludes(includes);
4614 fileSet.setExcludes(excludes);
4616 /*
4617 std::vector<String> fileList;
4618 if (dir.size() > 0)
4619 {
4620 String baseDir = propRef.resolve(dir);
4621 if (!listFiles(baseDir, "", includes, excludes, fileList))
4622 return false;
4623 }
4624 std::sort(fileList.begin(), fileList.end());
4625 result = fileList;
4626 */
4629 /*
4630 for (unsigned int i=0 ; i<result.size() ; i++)
4631 {
4632 trace("RES:%s", result[i].c_str());
4633 }
4634 */
4637 return true;
4638 }
4640 /**
4641 * Parse a <filelist> entry. This is far simpler than FileSet,
4642 * since no directory scanning is needed. The file names are listed
4643 * explicitly.
4644 */
4645 bool MakeBase::parseFileList(Element *elem,
4646 MakeBase &propRef,
4647 FileList &fileList)
4648 {
4649 std::vector<String> fnames;
4650 //Look for child tags, namely "file"
4651 std::vector<Element *> children = elem->getChildren();
4652 for (unsigned int i=0 ; i<children.size() ; i++)
4653 {
4654 Element *child = children[i];
4655 String tagName = child->getName();
4656 if (tagName == "file")
4657 {
4658 String fname = child->getAttribute("name");
4659 if (fname.size()==0)
4660 {
4661 error("<file> element requires name="" attribute");
4662 return false;
4663 }
4664 fnames.push_back(fname);
4665 }
4666 else
4667 {
4668 error("tag <%s> not allowed in <fileset>", tagName.c_str());
4669 return false;
4670 }
4671 }
4673 String dir;
4674 //Get the base directory for reading file names
4675 if (!propRef.getAttribute(elem, "dir", dir))
4676 return false;
4677 fileList.setDirectory(dir);
4678 fileList.setFiles(fnames);
4680 return true;
4681 }
4685 /**
4686 * Create a directory, making intermediate dirs
4687 * if necessary
4688 */
4689 bool MakeBase::createDirectory(const String &dirname)
4690 {
4691 //trace("## createDirectory: %s", dirname.c_str());
4692 //## first check if it exists
4693 struct stat finfo;
4694 String nativeDir = getNativePath(dirname);
4695 char *cnative = (char *) nativeDir.c_str();
4696 #ifdef __WIN32__
4697 if (strlen(cnative)==2 && cnative[1]==':')
4698 return true;
4699 #endif
4700 if (stat(cnative, &finfo)==0)
4701 {
4702 if (!S_ISDIR(finfo.st_mode))
4703 {
4704 error("mkdir: file %s exists but is not a directory",
4705 cnative);
4706 return false;
4707 }
4708 else //exists
4709 {
4710 return true;
4711 }
4712 }
4714 //## 2: pull off the last path segment, if any,
4715 //## to make the dir 'above' this one, if necessary
4716 unsigned int pos = dirname.find_last_of('/');
4717 if (pos>0 && pos != dirname.npos)
4718 {
4719 String subpath = dirname.substr(0, pos);
4720 //A letter root (c:) ?
4721 if (!createDirectory(subpath))
4722 return false;
4723 }
4725 //## 3: now make
4726 #ifdef __WIN32__
4727 if (mkdir(cnative)<0)
4728 #else
4729 if (mkdir(cnative, S_IRWXU | S_IRWXG | S_IRWXO)<0)
4730 #endif
4731 {
4732 error("cannot make directory '%s' : %s",
4733 cnative, strerror(errno));
4734 return false;
4735 }
4737 return true;
4738 }
4741 /**
4742 * Remove a directory recursively
4743 */
4744 bool MakeBase::removeDirectory(const String &dirName)
4745 {
4746 char *dname = (char *)dirName.c_str();
4748 DIR *dir = opendir(dname);
4749 if (!dir)
4750 {
4751 //# Let this fail nicely.
4752 return true;
4753 //error("error opening directory %s : %s", dname, strerror(errno));
4754 //return false;
4755 }
4757 while (true)
4758 {
4759 struct dirent *de = readdir(dir);
4760 if (!de)
4761 break;
4763 //Get the directory member name
4764 String s = de->d_name;
4765 if (s.size() == 0 || s[0] == '.')
4766 continue;
4767 String childName;
4768 if (dirName.size() > 0)
4769 {
4770 childName.append(dirName);
4771 childName.append("/");
4772 }
4773 childName.append(s);
4776 struct stat finfo;
4777 String childNative = getNativePath(childName);
4778 char *cnative = (char *)childNative.c_str();
4779 if (stat(cnative, &finfo)<0)
4780 {
4781 error("cannot stat file:%s", cnative);
4782 }
4783 else if (S_ISDIR(finfo.st_mode))
4784 {
4785 //trace("DEL dir: %s", childName.c_str());
4786 if (!removeDirectory(childName))
4787 {
4788 return false;
4789 }
4790 }
4791 else if (!S_ISREG(finfo.st_mode))
4792 {
4793 //trace("not regular: %s", cnative);
4794 }
4795 else
4796 {
4797 //trace("DEL file: %s", childName.c_str());
4798 if (remove(cnative)<0)
4799 {
4800 error("error deleting %s : %s",
4801 cnative, strerror(errno));
4802 return false;
4803 }
4804 }
4805 }
4806 closedir(dir);
4808 //Now delete the directory
4809 String native = getNativePath(dirName);
4810 if (rmdir(native.c_str())<0)
4811 {
4812 error("could not delete directory %s : %s",
4813 native.c_str() , strerror(errno));
4814 return false;
4815 }
4817 return true;
4819 }
4822 /**
4823 * Copy a file from one name to another. Perform only if needed
4824 */
4825 bool MakeBase::copyFile(const String &srcFile, const String &destFile)
4826 {
4827 //# 1 Check up-to-date times
4828 String srcNative = getNativePath(srcFile);
4829 struct stat srcinfo;
4830 if (stat(srcNative.c_str(), &srcinfo)<0)
4831 {
4832 error("source file %s for copy does not exist",
4833 srcNative.c_str());
4834 return false;
4835 }
4837 String destNative = getNativePath(destFile);
4838 struct stat destinfo;
4839 if (stat(destNative.c_str(), &destinfo)==0)
4840 {
4841 if (destinfo.st_mtime >= srcinfo.st_mtime)
4842 return true;
4843 }
4845 //# 2 prepare a destination directory if necessary
4846 unsigned int pos = destFile.find_last_of('/');
4847 if (pos != destFile.npos)
4848 {
4849 String subpath = destFile.substr(0, pos);
4850 if (!createDirectory(subpath))
4851 return false;
4852 }
4854 //# 3 do the data copy
4855 #ifndef __WIN32__
4857 FILE *srcf = fopen(srcNative.c_str(), "rb");
4858 if (!srcf)
4859 {
4860 error("copyFile cannot open '%s' for reading", srcNative.c_str());
4861 return false;
4862 }
4863 FILE *destf = fopen(destNative.c_str(), "wb");
4864 if (!destf)
4865 {
4866 error("copyFile cannot open %s for writing", srcNative.c_str());
4867 return false;
4868 }
4870 while (!feof(srcf))
4871 {
4872 int ch = fgetc(srcf);
4873 if (ch<0)
4874 break;
4875 fputc(ch, destf);
4876 }
4878 fclose(destf);
4879 fclose(srcf);
4881 #else
4883 if (!CopyFile(srcNative.c_str(), destNative.c_str(), false))
4884 {
4885 error("copyFile from %s to %s failed",
4886 srcNative.c_str(), destNative.c_str());
4887 return false;
4888 }
4890 #endif /* __WIN32__ */
4893 return true;
4894 }
4898 /**
4899 * Tests if the file exists and is a regular file
4900 */
4901 bool MakeBase::isRegularFile(const String &fileName)
4902 {
4903 String native = getNativePath(fileName);
4904 struct stat finfo;
4906 //Exists?
4907 if (stat(native.c_str(), &finfo)<0)
4908 return false;
4911 //check the file mode
4912 if (!S_ISREG(finfo.st_mode))
4913 return false;
4915 return true;
4916 }
4918 /**
4919 * Tests if the file exists and is a directory
4920 */
4921 bool MakeBase::isDirectory(const String &fileName)
4922 {
4923 String native = getNativePath(fileName);
4924 struct stat finfo;
4926 //Exists?
4927 if (stat(native.c_str(), &finfo)<0)
4928 return false;
4931 //check the file mode
4932 if (!S_ISDIR(finfo.st_mode))
4933 return false;
4935 return true;
4936 }
4940 /**
4941 * Tests is the modification of fileA is newer than fileB
4942 */
4943 bool MakeBase::isNewerThan(const String &fileA, const String &fileB)
4944 {
4945 //trace("isNewerThan:'%s' , '%s'", fileA.c_str(), fileB.c_str());
4946 String nativeA = getNativePath(fileA);
4947 struct stat infoA;
4948 //IF source does not exist, NOT newer
4949 if (stat(nativeA.c_str(), &infoA)<0)
4950 {
4951 return false;
4952 }
4954 String nativeB = getNativePath(fileB);
4955 struct stat infoB;
4956 //IF dest does not exist, YES, newer
4957 if (stat(nativeB.c_str(), &infoB)<0)
4958 {
4959 return true;
4960 }
4962 //check the actual times
4963 if (infoA.st_mtime > infoB.st_mtime)
4964 {
4965 return true;
4966 }
4968 return false;
4969 }
4972 //########################################################################
4973 //# P K G C O N F I G
4974 //########################################################################
4977 /**
4978 * Get a character from the buffer at pos. If out of range,
4979 * return -1 for safety
4980 */
4981 int PkgConfig::get(int pos)
4982 {
4983 if (pos>parselen)
4984 return -1;
4985 return parsebuf[pos];
4986 }
4990 /**
4991 * Skip over all whitespace characters beginning at pos. Return
4992 * the position of the first non-whitespace character.
4993 * Pkg-config is line-oriented, so check for newline
4994 */
4995 int PkgConfig::skipwhite(int pos)
4996 {
4997 while (pos < parselen)
4998 {
4999 int ch = get(pos);
5000 if (ch < 0)
5001 break;
5002 if (!isspace(ch))
5003 break;
5004 pos++;
5005 }
5006 return pos;
5007 }
5010 /**
5011 * Parse the buffer beginning at pos, for a word. Fill
5012 * 'ret' with the result. Return the position after the
5013 * word.
5014 */
5015 int PkgConfig::getword(int pos, String &ret)
5016 {
5017 while (pos < parselen)
5018 {
5019 int ch = get(pos);
5020 if (ch < 0)
5021 break;
5022 if (!isalnum(ch) && ch != '_' && ch != '-' && ch != '+' && ch != '.')
5023 break;
5024 ret.push_back((char)ch);
5025 pos++;
5026 }
5027 return pos;
5028 }
5030 bool PkgConfig::parseRequires()
5031 {
5032 if (requires.size() == 0)
5033 return true;
5034 parsebuf = (char *)requires.c_str();
5035 parselen = requires.size();
5036 int pos = 0;
5037 while (pos < parselen)
5038 {
5039 pos = skipwhite(pos);
5040 String val;
5041 int pos2 = getword(pos, val);
5042 if (pos2 == pos)
5043 break;
5044 pos = pos2;
5045 //trace("val %s", val.c_str());
5046 requireList.push_back(val);
5047 }
5048 return true;
5049 }
5052 static int getint(const String str)
5053 {
5054 char *s = (char *)str.c_str();
5055 char *ends = NULL;
5056 long val = strtol(s, &ends, 10);
5057 if (ends == s)
5058 return 0L;
5059 else
5060 return val;
5061 }
5063 void PkgConfig::parseVersion()
5064 {
5065 if (version.size() == 0)
5066 return;
5067 String s1, s2, s3;
5068 unsigned int pos = 0;
5069 unsigned int pos2 = version.find('.', pos);
5070 if (pos2 == version.npos)
5071 {
5072 s1 = version;
5073 }
5074 else
5075 {
5076 s1 = version.substr(pos, pos2-pos);
5077 pos = pos2;
5078 pos++;
5079 if (pos < version.size())
5080 {
5081 pos2 = version.find('.', pos);
5082 if (pos2 == version.npos)
5083 {
5084 s2 = version.substr(pos, version.size()-pos);
5085 }
5086 else
5087 {
5088 s2 = version.substr(pos, pos2-pos);
5089 pos = pos2;
5090 pos++;
5091 if (pos < version.size())
5092 s3 = version.substr(pos, pos2-pos);
5093 }
5094 }
5095 }
5097 majorVersion = getint(s1);
5098 minorVersion = getint(s2);
5099 microVersion = getint(s3);
5100 //trace("version:%d.%d.%d", majorVersion,
5101 // minorVersion, microVersion );
5102 }
5105 bool PkgConfig::parseLine(const String &lineBuf)
5106 {
5107 parsebuf = (char *)lineBuf.c_str();
5108 parselen = lineBuf.size();
5109 int pos = 0;
5111 while (pos < parselen)
5112 {
5113 String attrName;
5114 pos = skipwhite(pos);
5115 int ch = get(pos);
5116 if (ch == '#')
5117 {
5118 //comment. eat the rest of the line
5119 while (pos < parselen)
5120 {
5121 ch = get(pos);
5122 if (ch == '\n' || ch < 0)
5123 break;
5124 pos++;
5125 }
5126 continue;
5127 }
5128 pos = getword(pos, attrName);
5129 if (attrName.size() == 0)
5130 continue;
5132 pos = skipwhite(pos);
5133 ch = get(pos);
5134 if (ch != ':' && ch != '=')
5135 {
5136 error("expected ':' or '='");
5137 return false;
5138 }
5139 pos++;
5140 pos = skipwhite(pos);
5141 String attrVal;
5142 while (pos < parselen)
5143 {
5144 ch = get(pos);
5145 if (ch == '\n' || ch < 0)
5146 break;
5147 else if (ch == '$' && get(pos+1) == '{')
5148 {
5149 //# this is a ${substitution}
5150 pos += 2;
5151 String subName;
5152 while (pos < parselen)
5153 {
5154 ch = get(pos);
5155 if (ch < 0)
5156 {
5157 error("unterminated substitution");
5158 return false;
5159 }
5160 else if (ch == '}')
5161 break;
5162 else
5163 subName.push_back((char)ch);
5164 pos++;
5165 }
5166 //trace("subName:%s %s", subName.c_str(), prefix.c_str());
5167 if (subName == "prefix" && prefix.size()>0)
5168 {
5169 attrVal.append(prefix);
5170 //trace("prefix override:%s", prefix.c_str());
5171 }
5172 else
5173 {
5174 String subVal = attrs[subName];
5175 //trace("subVal:%s", subVal.c_str());
5176 attrVal.append(subVal);
5177 }
5178 }
5179 else
5180 attrVal.push_back((char)ch);
5181 pos++;
5182 }
5184 attrVal = trim(attrVal);
5185 attrs[attrName] = attrVal;
5187 String attrNameL = toLower(attrName);
5189 if (attrNameL == "name")
5190 name = attrVal;
5191 else if (attrNameL == "description")
5192 description = attrVal;
5193 else if (attrNameL == "cflags")
5194 cflags = attrVal;
5195 else if (attrNameL == "libs")
5196 libs = attrVal;
5197 else if (attrNameL == "requires")
5198 requires = attrVal;
5199 else if (attrNameL == "version")
5200 version = attrVal;
5202 //trace("name:'%s' value:'%s'",
5203 // attrName.c_str(), attrVal.c_str());
5204 }
5206 return true;
5207 }
5210 bool PkgConfig::parse(const String &buf)
5211 {
5212 init();
5214 String line;
5215 int lineNr = 0;
5216 for (unsigned int p=0 ; p<buf.size() ; p++)
5217 {
5218 int ch = buf[p];
5219 if (ch == '\n' || ch == '\r')
5220 {
5221 if (!parseLine(line))
5222 return false;
5223 line.clear();
5224 lineNr++;
5225 }
5226 else
5227 {
5228 line.push_back(ch);
5229 }
5230 }
5231 if (line.size()>0)
5232 {
5233 if (!parseLine(line))
5234 return false;
5235 }
5237 parseRequires();
5238 parseVersion();
5240 return true;
5241 }
5246 void PkgConfig::dumpAttrs()
5247 {
5248 //trace("### PkgConfig attributes for %s", fileName.c_str());
5249 std::map<String, String>::iterator iter;
5250 for (iter=attrs.begin() ; iter!=attrs.end() ; iter++)
5251 {
5252 trace(" %s = %s", iter->first.c_str(), iter->second.c_str());
5253 }
5254 }
5257 bool PkgConfig::readFile(const String &fname)
5258 {
5259 fileName = getNativePath(fname);
5261 FILE *f = fopen(fileName.c_str(), "r");
5262 if (!f)
5263 {
5264 error("cannot open file '%s' for reading", fileName.c_str());
5265 return false;
5266 }
5267 String buf;
5268 while (true)
5269 {
5270 int ch = fgetc(f);
5271 if (ch < 0)
5272 break;
5273 buf.push_back((char)ch);
5274 }
5275 fclose(f);
5277 //trace("####### File:\n%s", buf.c_str());
5278 if (!parse(buf))
5279 {
5280 return false;
5281 }
5283 //dumpAttrs();
5285 return true;
5286 }
5290 bool PkgConfig::query(const String &pkgName)
5291 {
5292 name = pkgName;
5294 String fname = path;
5295 fname.append("/");
5296 fname.append(name);
5297 fname.append(".pc");
5299 if (!readFile(fname))
5300 return false;
5302 return true;
5303 }
5309 //########################################################################
5310 //# D E P T O O L
5311 //########################################################################
5315 /**
5316 * Class which holds information for each file.
5317 */
5318 class FileRec
5319 {
5320 public:
5322 typedef enum
5323 {
5324 UNKNOWN,
5325 CFILE,
5326 HFILE,
5327 OFILE
5328 } FileType;
5330 /**
5331 * Constructor
5332 */
5333 FileRec()
5334 { init(); type = UNKNOWN; }
5336 /**
5337 * Copy constructor
5338 */
5339 FileRec(const FileRec &other)
5340 { init(); assign(other); }
5341 /**
5342 * Constructor
5343 */
5344 FileRec(int typeVal)
5345 { init(); type = typeVal; }
5346 /**
5347 * Assignment operator
5348 */
5349 FileRec &operator=(const FileRec &other)
5350 { init(); assign(other); return *this; }
5353 /**
5354 * Destructor
5355 */
5356 ~FileRec()
5357 {}
5359 /**
5360 * Directory part of the file name
5361 */
5362 String path;
5364 /**
5365 * Base name, sans directory and suffix
5366 */
5367 String baseName;
5369 /**
5370 * File extension, such as cpp or h
5371 */
5372 String suffix;
5374 /**
5375 * Type of file: CFILE, HFILE, OFILE
5376 */
5377 int type;
5379 /**
5380 * Used to list files ref'd by this one
5381 */
5382 std::map<String, FileRec *> files;
5385 private:
5387 void init()
5388 {
5389 }
5391 void assign(const FileRec &other)
5392 {
5393 type = other.type;
5394 baseName = other.baseName;
5395 suffix = other.suffix;
5396 files = other.files;
5397 }
5399 };
5403 /**
5404 * Simpler dependency record
5405 */
5406 class DepRec
5407 {
5408 public:
5410 /**
5411 * Constructor
5412 */
5413 DepRec()
5414 {init();}
5416 /**
5417 * Copy constructor
5418 */
5419 DepRec(const DepRec &other)
5420 {init(); assign(other);}
5421 /**
5422 * Constructor
5423 */
5424 DepRec(const String &fname)
5425 {init(); name = fname; }
5426 /**
5427 * Assignment operator
5428 */
5429 DepRec &operator=(const DepRec &other)
5430 {init(); assign(other); return *this;}
5433 /**
5434 * Destructor
5435 */
5436 ~DepRec()
5437 {}
5439 /**
5440 * Directory part of the file name
5441 */
5442 String path;
5444 /**
5445 * Base name, without the path and suffix
5446 */
5447 String name;
5449 /**
5450 * Suffix of the source
5451 */
5452 String suffix;
5455 /**
5456 * Used to list files ref'd by this one
5457 */
5458 std::vector<String> files;
5461 private:
5463 void init()
5464 {
5465 }
5467 void assign(const DepRec &other)
5468 {
5469 path = other.path;
5470 name = other.name;
5471 suffix = other.suffix;
5472 files = other.files; //avoid recursion
5473 }
5475 };
5478 class DepTool : public MakeBase
5479 {
5480 public:
5482 /**
5483 * Constructor
5484 */
5485 DepTool()
5486 { init(); }
5488 /**
5489 * Copy constructor
5490 */
5491 DepTool(const DepTool &other)
5492 { init(); assign(other); }
5494 /**
5495 * Assignment operator
5496 */
5497 DepTool &operator=(const DepTool &other)
5498 { init(); assign(other); return *this; }
5501 /**
5502 * Destructor
5503 */
5504 ~DepTool()
5505 {}
5508 /**
5509 * Reset this section of code
5510 */
5511 virtual void init();
5513 /**
5514 * Reset this section of code
5515 */
5516 virtual void assign(const DepTool &other)
5517 {
5518 }
5520 /**
5521 * Sets the source directory which will be scanned
5522 */
5523 virtual void setSourceDirectory(const String &val)
5524 { sourceDir = val; }
5526 /**
5527 * Returns the source directory which will be scanned
5528 */
5529 virtual String getSourceDirectory()
5530 { return sourceDir; }
5532 /**
5533 * Sets the list of files within the directory to analyze
5534 */
5535 virtual void setFileList(const std::vector<String> &list)
5536 { fileList = list; }
5538 /**
5539 * Creates the list of all file names which will be
5540 * candidates for further processing. Reads make.exclude
5541 * to see which files for directories to leave out.
5542 */
5543 virtual bool createFileList();
5546 /**
5547 * Generates the forward dependency list
5548 */
5549 virtual bool generateDependencies();
5552 /**
5553 * Generates the forward dependency list, saving the file
5554 */
5555 virtual bool generateDependencies(const String &);
5558 /**
5559 * Load a dependency file
5560 */
5561 std::vector<DepRec> loadDepFile(const String &fileName);
5563 /**
5564 * Load a dependency file, generating one if necessary
5565 */
5566 std::vector<DepRec> getDepFile(const String &fileName,
5567 bool forceRefresh);
5569 /**
5570 * Save a dependency file
5571 */
5572 bool saveDepFile(const String &fileName);
5575 private:
5578 /**
5579 *
5580 */
5581 void parseName(const String &fullname,
5582 String &path,
5583 String &basename,
5584 String &suffix);
5586 /**
5587 *
5588 */
5589 int get(int pos);
5591 /**
5592 *
5593 */
5594 int skipwhite(int pos);
5596 /**
5597 *
5598 */
5599 int getword(int pos, String &ret);
5601 /**
5602 *
5603 */
5604 bool sequ(int pos, const char *key);
5606 /**
5607 *
5608 */
5609 bool addIncludeFile(FileRec *frec, const String &fname);
5611 /**
5612 *
5613 */
5614 bool scanFile(const String &fname, FileRec *frec);
5616 /**
5617 *
5618 */
5619 bool processDependency(FileRec *ofile, FileRec *include);
5621 /**
5622 *
5623 */
5624 String sourceDir;
5626 /**
5627 *
5628 */
5629 std::vector<String> fileList;
5631 /**
5632 *
5633 */
5634 std::vector<String> directories;
5636 /**
5637 * A list of all files which will be processed for
5638 * dependencies.
5639 */
5640 std::map<String, FileRec *> allFiles;
5642 /**
5643 * The list of .o files, and the
5644 * dependencies upon them.
5645 */
5646 std::map<String, FileRec *> oFiles;
5648 int depFileSize;
5649 char *depFileBuf;
5651 static const int readBufSize = 8192;
5652 char readBuf[8193];//byte larger
5654 };
5660 /**
5661 * Clean up after processing. Called by the destructor, but should
5662 * also be called before the object is reused.
5663 */
5664 void DepTool::init()
5665 {
5666 sourceDir = ".";
5668 fileList.clear();
5669 directories.clear();
5671 //clear output file list
5672 std::map<String, FileRec *>::iterator iter;
5673 for (iter=oFiles.begin(); iter!=oFiles.end() ; iter++)
5674 delete iter->second;
5675 oFiles.clear();
5677 //allFiles actually contains the master copies. delete them
5678 for (iter= allFiles.begin(); iter!=allFiles.end() ; iter++)
5679 delete iter->second;
5680 allFiles.clear();
5682 }
5687 /**
5688 * Parse a full path name into path, base name, and suffix
5689 */
5690 void DepTool::parseName(const String &fullname,
5691 String &path,
5692 String &basename,
5693 String &suffix)
5694 {
5695 if (fullname.size() < 2)
5696 return;
5698 unsigned int pos = fullname.find_last_of('/');
5699 if (pos != fullname.npos && pos<fullname.size()-1)
5700 {
5701 path = fullname.substr(0, pos);
5702 pos++;
5703 basename = fullname.substr(pos, fullname.size()-pos);
5704 }
5705 else
5706 {
5707 path = "";
5708 basename = fullname;
5709 }
5711 pos = basename.find_last_of('.');
5712 if (pos != basename.npos && pos<basename.size()-1)
5713 {
5714 suffix = basename.substr(pos+1, basename.size()-pos-1);
5715 basename = basename.substr(0, pos);
5716 }
5718 //trace("parsename:%s %s %s", path.c_str(),
5719 // basename.c_str(), suffix.c_str());
5720 }
5724 /**
5725 * Generate our internal file list.
5726 */
5727 bool DepTool::createFileList()
5728 {
5730 for (unsigned int i=0 ; i<fileList.size() ; i++)
5731 {
5732 String fileName = fileList[i];
5733 //trace("## FileName:%s", fileName.c_str());
5734 String path;
5735 String basename;
5736 String sfx;
5737 parseName(fileName, path, basename, sfx);
5738 if (sfx == "cpp" || sfx == "c" || sfx == "cxx" ||
5739 sfx == "cc" || sfx == "CC")
5740 {
5741 FileRec *fe = new FileRec(FileRec::CFILE);
5742 fe->path = path;
5743 fe->baseName = basename;
5744 fe->suffix = sfx;
5745 allFiles[fileName] = fe;
5746 }
5747 else if (sfx == "h" || sfx == "hh" ||
5748 sfx == "hpp" || sfx == "hxx")
5749 {
5750 FileRec *fe = new FileRec(FileRec::HFILE);
5751 fe->path = path;
5752 fe->baseName = basename;
5753 fe->suffix = sfx;
5754 allFiles[fileName] = fe;
5755 }
5756 }
5758 if (!listDirectories(sourceDir, "", directories))
5759 return false;
5761 return true;
5762 }
5768 /**
5769 * Get a character from the buffer at pos. If out of range,
5770 * return -1 for safety
5771 */
5772 int DepTool::get(int pos)
5773 {
5774 if (pos>depFileSize)
5775 return -1;
5776 return depFileBuf[pos];
5777 }
5781 /**
5782 * Skip over all whitespace characters beginning at pos. Return
5783 * the position of the first non-whitespace character.
5784 */
5785 int DepTool::skipwhite(int pos)
5786 {
5787 while (pos < depFileSize)
5788 {
5789 int ch = get(pos);
5790 if (ch < 0)
5791 break;
5792 if (!isspace(ch))
5793 break;
5794 pos++;
5795 }
5796 return pos;
5797 }
5800 /**
5801 * Parse the buffer beginning at pos, for a word. Fill
5802 * 'ret' with the result. Return the position after the
5803 * word.
5804 */
5805 int DepTool::getword(int pos, String &ret)
5806 {
5807 while (pos < depFileSize)
5808 {
5809 int ch = get(pos);
5810 if (ch < 0)
5811 break;
5812 if (isspace(ch))
5813 break;
5814 ret.push_back((char)ch);
5815 pos++;
5816 }
5817 return pos;
5818 }
5820 /**
5821 * Return whether the sequence of characters in the buffer
5822 * beginning at pos match the key, for the length of the key
5823 */
5824 bool DepTool::sequ(int pos, const char *key)
5825 {
5826 while (*key)
5827 {
5828 if (*key != get(pos))
5829 return false;
5830 key++; pos++;
5831 }
5832 return true;
5833 }
5837 /**
5838 * Add an include file name to a file record. If the name
5839 * is not found in allFiles explicitly, try prepending include
5840 * directory names to it and try again.
5841 */
5842 bool DepTool::addIncludeFile(FileRec *frec, const String &iname)
5843 {
5844 //# if the name is an exact match to a path name
5845 //# in allFiles, like "myinc.h"
5846 std::map<String, FileRec *>::iterator iter =
5847 allFiles.find(iname);
5848 if (iter != allFiles.end()) //already exists
5849 {
5850 //h file in same dir
5851 FileRec *other = iter->second;
5852 //trace("local: '%s'", iname.c_str());
5853 frec->files[iname] = other;
5854 return true;
5855 }
5856 else
5857 {
5858 //## Ok, it was not found directly
5859 //look in other dirs
5860 std::vector<String>::iterator diter;
5861 for (diter=directories.begin() ;
5862 diter!=directories.end() ; diter++)
5863 {
5864 String dfname = *diter;
5865 dfname.append("/");
5866 dfname.append(iname);
5867 URI fullPathURI(dfname); //normalize path name
5868 String fullPath = fullPathURI.getPath();
5869 if (fullPath[0] == '/')
5870 fullPath = fullPath.substr(1);
5871 //trace("Normalized %s to %s", dfname.c_str(), fullPath.c_str());
5872 iter = allFiles.find(fullPath);
5873 if (iter != allFiles.end())
5874 {
5875 FileRec *other = iter->second;
5876 //trace("other: '%s'", iname.c_str());
5877 frec->files[fullPath] = other;
5878 return true;
5879 }
5880 }
5881 }
5882 return true;
5883 }
5887 /**
5888 * Lightly parse a file to find the #include directives. Do
5889 * a bit of state machine stuff to make sure that the directive
5890 * is valid. (Like not in a comment).
5891 */
5892 bool DepTool::scanFile(const String &fname, FileRec *frec)
5893 {
5894 String fileName;
5895 if (sourceDir.size() > 0)
5896 {
5897 fileName.append(sourceDir);
5898 fileName.append("/");
5899 }
5900 fileName.append(fname);
5901 String nativeName = getNativePath(fileName);
5902 FILE *f = fopen(nativeName.c_str(), "r");
5903 if (!f)
5904 {
5905 error("Could not open '%s' for reading", fname.c_str());
5906 return false;
5907 }
5908 String buf;
5909 while (!feof(f))
5910 {
5911 int nrbytes = fread(readBuf, 1, readBufSize, f);
5912 readBuf[nrbytes] = '\0';
5913 buf.append(readBuf);
5914 }
5915 fclose(f);
5917 depFileSize = buf.size();
5918 depFileBuf = (char *)buf.c_str();
5919 int pos = 0;
5922 while (pos < depFileSize)
5923 {
5924 //trace("p:%c", get(pos));
5926 //# Block comment
5927 if (get(pos) == '/' && get(pos+1) == '*')
5928 {
5929 pos += 2;
5930 while (pos < depFileSize)
5931 {
5932 if (get(pos) == '*' && get(pos+1) == '/')
5933 {
5934 pos += 2;
5935 break;
5936 }
5937 else
5938 pos++;
5939 }
5940 }
5941 //# Line comment
5942 else if (get(pos) == '/' && get(pos+1) == '/')
5943 {
5944 pos += 2;
5945 while (pos < depFileSize)
5946 {
5947 if (get(pos) == '\n')
5948 {
5949 pos++;
5950 break;
5951 }
5952 else
5953 pos++;
5954 }
5955 }
5956 //# #include! yaay
5957 else if (sequ(pos, "#include"))
5958 {
5959 pos += 8;
5960 pos = skipwhite(pos);
5961 String iname;
5962 pos = getword(pos, iname);
5963 if (iname.size()>2)
5964 {
5965 iname = iname.substr(1, iname.size()-2);
5966 addIncludeFile(frec, iname);
5967 }
5968 }
5969 else
5970 {
5971 pos++;
5972 }
5973 }
5975 return true;
5976 }
5980 /**
5981 * Recursively check include lists to find all files in allFiles to which
5982 * a given file is dependent.
5983 */
5984 bool DepTool::processDependency(FileRec *ofile, FileRec *include)
5985 {
5986 std::map<String, FileRec *>::iterator iter;
5987 for (iter=include->files.begin() ; iter!=include->files.end() ; iter++)
5988 {
5989 String fname = iter->first;
5990 if (ofile->files.find(fname) != ofile->files.end())
5991 {
5992 //trace("file '%s' already seen", fname.c_str());
5993 continue;
5994 }
5995 FileRec *child = iter->second;
5996 ofile->files[fname] = child;
5998 processDependency(ofile, child);
5999 }
6002 return true;
6003 }
6009 /**
6010 * Generate the file dependency list.
6011 */
6012 bool DepTool::generateDependencies()
6013 {
6014 std::map<String, FileRec *>::iterator iter;
6015 //# First pass. Scan for all includes
6016 for (iter=allFiles.begin() ; iter!=allFiles.end() ; iter++)
6017 {
6018 FileRec *frec = iter->second;
6019 if (!scanFile(iter->first, frec))
6020 {
6021 //quit?
6022 }
6023 }
6025 //# Second pass. Scan for all includes
6026 for (iter=allFiles.begin() ; iter!=allFiles.end() ; iter++)
6027 {
6028 FileRec *include = iter->second;
6029 if (include->type == FileRec::CFILE)
6030 {
6031 //String cFileName = iter->first;
6032 FileRec *ofile = new FileRec(FileRec::OFILE);
6033 ofile->path = include->path;
6034 ofile->baseName = include->baseName;
6035 ofile->suffix = include->suffix;
6036 String fname = include->path;
6037 if (fname.size()>0)
6038 fname.append("/");
6039 fname.append(include->baseName);
6040 fname.append(".o");
6041 oFiles[fname] = ofile;
6042 //add the .c file first? no, don't
6043 //ofile->files[cFileName] = include;
6045 //trace("ofile:%s", fname.c_str());
6047 processDependency(ofile, include);
6048 }
6049 }
6052 return true;
6053 }
6057 /**
6058 * High-level call to generate deps and optionally save them
6059 */
6060 bool DepTool::generateDependencies(const String &fileName)
6061 {
6062 if (!createFileList())
6063 return false;
6064 if (!generateDependencies())
6065 return false;
6066 if (!saveDepFile(fileName))
6067 return false;
6068 return true;
6069 }
6072 /**
6073 * This saves the dependency cache.
6074 */
6075 bool DepTool::saveDepFile(const String &fileName)
6076 {
6077 time_t tim;
6078 time(&tim);
6080 FILE *f = fopen(fileName.c_str(), "w");
6081 if (!f)
6082 {
6083 trace("cannot open '%s' for writing", fileName.c_str());
6084 }
6085 fprintf(f, "<?xml version='1.0'?>\n");
6086 fprintf(f, "<!--\n");
6087 fprintf(f, "########################################################\n");
6088 fprintf(f, "## File: build.dep\n");
6089 fprintf(f, "## Generated by BuildTool at :%s", ctime(&tim));
6090 fprintf(f, "########################################################\n");
6091 fprintf(f, "-->\n");
6093 fprintf(f, "<dependencies source='%s'>\n\n", sourceDir.c_str());
6094 std::map<String, FileRec *>::iterator iter;
6095 for (iter=oFiles.begin() ; iter!=oFiles.end() ; iter++)
6096 {
6097 FileRec *frec = iter->second;
6098 if (frec->type == FileRec::OFILE)
6099 {
6100 fprintf(f, "<object path='%s' name='%s' suffix='%s'>\n",
6101 frec->path.c_str(), frec->baseName.c_str(), frec->suffix.c_str());
6102 std::map<String, FileRec *>::iterator citer;
6103 for (citer=frec->files.begin() ; citer!=frec->files.end() ; citer++)
6104 {
6105 String cfname = citer->first;
6106 fprintf(f, " <dep name='%s'/>\n", cfname.c_str());
6107 }
6108 fprintf(f, "</object>\n\n");
6109 }
6110 }
6112 fprintf(f, "</dependencies>\n");
6113 fprintf(f, "\n");
6114 fprintf(f, "<!--\n");
6115 fprintf(f, "########################################################\n");
6116 fprintf(f, "## E N D\n");
6117 fprintf(f, "########################################################\n");
6118 fprintf(f, "-->\n");
6120 fclose(f);
6122 return true;
6123 }
6128 /**
6129 * This loads the dependency cache.
6130 */
6131 std::vector<DepRec> DepTool::loadDepFile(const String &depFile)
6132 {
6133 std::vector<DepRec> result;
6135 Parser parser;
6136 Element *root = parser.parseFile(depFile.c_str());
6137 if (!root)
6138 {
6139 //error("Could not open %s for reading", depFile.c_str());
6140 return result;
6141 }
6143 if (root->getChildren().size()==0 ||
6144 root->getChildren()[0]->getName()!="dependencies")
6145 {
6146 error("loadDepFile: main xml element should be <dependencies>");
6147 delete root;
6148 return result;
6149 }
6151 //########## Start parsing
6152 Element *depList = root->getChildren()[0];
6154 std::vector<Element *> objects = depList->getChildren();
6155 for (unsigned int i=0 ; i<objects.size() ; i++)
6156 {
6157 Element *objectElem = objects[i];
6158 String tagName = objectElem->getName();
6159 if (tagName != "object")
6160 {
6161 error("loadDepFile: <dependencies> should have only <object> children");
6162 return result;
6163 }
6165 String objName = objectElem->getAttribute("name");
6166 //trace("object:%s", objName.c_str());
6167 DepRec depObject(objName);
6168 depObject.path = objectElem->getAttribute("path");
6169 depObject.suffix = objectElem->getAttribute("suffix");
6170 //########## DESCRIPTION
6171 std::vector<Element *> depElems = objectElem->getChildren();
6172 for (unsigned int i=0 ; i<depElems.size() ; i++)
6173 {
6174 Element *depElem = depElems[i];
6175 tagName = depElem->getName();
6176 if (tagName != "dep")
6177 {
6178 error("loadDepFile: <object> should have only <dep> children");
6179 return result;
6180 }
6181 String depName = depElem->getAttribute("name");
6182 //trace(" dep:%s", depName.c_str());
6183 depObject.files.push_back(depName);
6184 }
6186 //Insert into the result list, in a sorted manner
6187 bool inserted = false;
6188 std::vector<DepRec>::iterator iter;
6189 for (iter = result.begin() ; iter != result.end() ; iter++)
6190 {
6191 String vpath = iter->path;
6192 vpath.append("/");
6193 vpath.append(iter->name);
6194 String opath = depObject.path;
6195 opath.append("/");
6196 opath.append(depObject.name);
6197 if (vpath > opath)
6198 {
6199 inserted = true;
6200 iter = result.insert(iter, depObject);
6201 break;
6202 }
6203 }
6204 if (!inserted)
6205 result.push_back(depObject);
6206 }
6208 delete root;
6210 return result;
6211 }
6214 /**
6215 * This loads the dependency cache.
6216 */
6217 std::vector<DepRec> DepTool::getDepFile(const String &depFile,
6218 bool forceRefresh)
6219 {
6220 std::vector<DepRec> result;
6221 if (forceRefresh)
6222 {
6223 generateDependencies(depFile);
6224 result = loadDepFile(depFile);
6225 }
6226 else
6227 {
6228 //try once
6229 result = loadDepFile(depFile);
6230 if (result.size() == 0)
6231 {
6232 //fail? try again
6233 generateDependencies(depFile);
6234 result = loadDepFile(depFile);
6235 }
6236 }
6237 return result;
6238 }
6243 //########################################################################
6244 //# T A S K
6245 //########################################################################
6246 //forward decl
6247 class Target;
6248 class Make;
6250 /**
6251 *
6252 */
6253 class Task : public MakeBase
6254 {
6256 public:
6258 typedef enum
6259 {
6260 TASK_NONE,
6261 TASK_CC,
6262 TASK_COPY,
6263 TASK_DELETE,
6264 TASK_ECHO,
6265 TASK_JAR,
6266 TASK_JAVAC,
6267 TASK_LINK,
6268 TASK_MAKEFILE,
6269 TASK_MKDIR,
6270 TASK_MSGFMT,
6271 TASK_PKG_CONFIG,
6272 TASK_RANLIB,
6273 TASK_RC,
6274 TASK_SHAREDLIB,
6275 TASK_STATICLIB,
6276 TASK_STRIP,
6277 TASK_TOUCH,
6278 TASK_TSTAMP
6279 } TaskType;
6282 /**
6283 *
6284 */
6285 Task(MakeBase &par) : parent(par)
6286 { init(); }
6288 /**
6289 *
6290 */
6291 Task(const Task &other) : parent(other.parent)
6292 { init(); assign(other); }
6294 /**
6295 *
6296 */
6297 Task &operator=(const Task &other)
6298 { assign(other); return *this; }
6300 /**
6301 *
6302 */
6303 virtual ~Task()
6304 { }
6307 /**
6308 *
6309 */
6310 virtual MakeBase &getParent()
6311 { return parent; }
6313 /**
6314 *
6315 */
6316 virtual int getType()
6317 { return type; }
6319 /**
6320 *
6321 */
6322 virtual void setType(int val)
6323 { type = val; }
6325 /**
6326 *
6327 */
6328 virtual String getName()
6329 { return name; }
6331 /**
6332 *
6333 */
6334 virtual bool execute()
6335 { return true; }
6337 /**
6338 *
6339 */
6340 virtual bool parse(Element *elem)
6341 { return true; }
6343 /**
6344 *
6345 */
6346 Task *createTask(Element *elem, int lineNr);
6349 protected:
6351 void init()
6352 {
6353 type = TASK_NONE;
6354 name = "none";
6355 }
6357 void assign(const Task &other)
6358 {
6359 type = other.type;
6360 name = other.name;
6361 }
6363 /**
6364 * Show task status
6365 */
6366 void taskstatus(const char *fmt, ...)
6367 {
6368 va_list args;
6369 va_start(args,fmt);
6370 fprintf(stdout, " %s : ", name.c_str());
6371 vfprintf(stdout, fmt, args);
6372 fprintf(stdout, "\n");
6373 va_end(args) ;
6374 }
6376 String getAttribute(Element *elem, const String &attrName)
6377 {
6378 String str;
6379 return str;
6380 }
6382 MakeBase &parent;
6384 int type;
6386 String name;
6387 };
6391 /**
6392 * This task runs the C/C++ compiler. The compiler is invoked
6393 * for all .c or .cpp files which are newer than their correcsponding
6394 * .o files.
6395 */
6396 class TaskCC : public Task
6397 {
6398 public:
6400 TaskCC(MakeBase &par) : Task(par)
6401 {
6402 type = TASK_CC;
6403 name = "cc";
6404 }
6406 virtual ~TaskCC()
6407 {}
6409 virtual bool isExcludedInc(const String &dirname)
6410 {
6411 for (unsigned int i=0 ; i<excludeInc.size() ; i++)
6412 {
6413 String fname = excludeInc[i];
6414 if (fname == dirname)
6415 return true;
6416 }
6417 return false;
6418 }
6420 virtual bool execute()
6421 {
6422 //evaluate our parameters
6423 String command = parent.eval(commandOpt, "gcc");
6424 String ccCommand = parent.eval(ccCommandOpt, "gcc");
6425 String cxxCommand = parent.eval(cxxCommandOpt, "g++");
6426 String source = parent.eval(sourceOpt, ".");
6427 String dest = parent.eval(destOpt, ".");
6428 String flags = parent.eval(flagsOpt, "");
6429 String defines = parent.eval(definesOpt, "");
6430 String includes = parent.eval(includesOpt, "");
6431 bool continueOnError = parent.evalBool(continueOnErrorOpt, true);
6432 bool refreshCache = parent.evalBool(refreshCacheOpt, false);
6434 if (!listFiles(parent, fileSet))
6435 return false;
6437 FILE *f = NULL;
6438 f = fopen("compile.lst", "w");
6440 //refreshCache is probably false here, unless specified otherwise
6441 String fullName = parent.resolve("build.dep");
6442 if (refreshCache || isNewerThan(parent.getURI().getPath(), fullName))
6443 {
6444 taskstatus("regenerating C/C++ dependency cache");
6445 refreshCache = true;
6446 }
6448 DepTool depTool;
6449 depTool.setSourceDirectory(source);
6450 depTool.setFileList(fileSet.getFiles());
6451 std::vector<DepRec> deps =
6452 depTool.getDepFile("build.dep", refreshCache);
6454 String incs;
6455 incs.append("-I");
6456 incs.append(parent.resolve("."));
6457 incs.append(" ");
6458 if (includes.size()>0)
6459 {
6460 incs.append(includes);
6461 incs.append(" ");
6462 }
6463 std::set<String> paths;
6464 std::vector<DepRec>::iterator viter;
6465 for (viter=deps.begin() ; viter!=deps.end() ; viter++)
6466 {
6467 DepRec dep = *viter;
6468 if (dep.path.size()>0)
6469 paths.insert(dep.path);
6470 }
6471 if (source.size()>0)
6472 {
6473 incs.append(" -I");
6474 incs.append(parent.resolve(source));
6475 incs.append(" ");
6476 }
6477 std::set<String>::iterator setIter;
6478 for (setIter=paths.begin() ; setIter!=paths.end() ; setIter++)
6479 {
6480 String dirName = *setIter;
6481 //check excludeInc to see if we dont want to include this dir
6482 if (isExcludedInc(dirName))
6483 continue;
6484 incs.append(" -I");
6485 String dname;
6486 if (source.size()>0)
6487 {
6488 dname.append(source);
6489 dname.append("/");
6490 }
6491 dname.append(dirName);
6492 incs.append(parent.resolve(dname));
6493 }
6495 /**
6496 * Compile each of the C files that need it
6497 */
6498 bool errorOccurred = false;
6499 std::vector<String> cfiles;
6500 for (viter=deps.begin() ; viter!=deps.end() ; viter++)
6501 {
6502 DepRec dep = *viter;
6504 //## Select command
6505 String sfx = dep.suffix;
6506 String command = ccCommand;
6507 if (sfx == "cpp" || sfx == "cxx" || sfx == "c++" ||
6508 sfx == "cc" || sfx == "CC")
6509 command = cxxCommand;
6511 //## Make paths
6512 String destPath = dest;
6513 String srcPath = source;
6514 if (dep.path.size()>0)
6515 {
6516 destPath.append("/");
6517 destPath.append(dep.path);
6518 srcPath.append("/");
6519 srcPath.append(dep.path);
6520 }
6521 //## Make sure destination directory exists
6522 if (!createDirectory(destPath))
6523 return false;
6525 //## Check whether it needs to be done
6526 String destName;
6527 if (destPath.size()>0)
6528 {
6529 destName.append(destPath);
6530 destName.append("/");
6531 }
6532 destName.append(dep.name);
6533 destName.append(".o");
6534 String destFullName = parent.resolve(destName);
6535 String srcName;
6536 if (srcPath.size()>0)
6537 {
6538 srcName.append(srcPath);
6539 srcName.append("/");
6540 }
6541 srcName.append(dep.name);
6542 srcName.append(".");
6543 srcName.append(dep.suffix);
6544 String srcFullName = parent.resolve(srcName);
6545 bool compileMe = false;
6546 //# First we check if the source is newer than the .o
6547 if (isNewerThan(srcFullName, destFullName))
6548 {
6549 taskstatus("compile of %s required by source: %s",
6550 destFullName.c_str(), srcFullName.c_str());
6551 compileMe = true;
6552 }
6553 else
6554 {
6555 //# secondly, we check if any of the included dependencies
6556 //# of the .c/.cpp is newer than the .o
6557 for (unsigned int i=0 ; i<dep.files.size() ; i++)
6558 {
6559 String depName;
6560 if (source.size()>0)
6561 {
6562 depName.append(source);
6563 depName.append("/");
6564 }
6565 depName.append(dep.files[i]);
6566 String depFullName = parent.resolve(depName);
6567 bool depRequires = isNewerThan(depFullName, destFullName);
6568 //trace("%d %s %s\n", depRequires,
6569 // destFullName.c_str(), depFullName.c_str());
6570 if (depRequires)
6571 {
6572 taskstatus("compile of %s required by included: %s",
6573 destFullName.c_str(), depFullName.c_str());
6574 compileMe = true;
6575 break;
6576 }
6577 }
6578 }
6579 if (!compileMe)
6580 {
6581 continue;
6582 }
6584 //## Assemble the command
6585 String cmd = command;
6586 cmd.append(" -c ");
6587 cmd.append(flags);
6588 cmd.append(" ");
6589 cmd.append(defines);
6590 cmd.append(" ");
6591 cmd.append(incs);
6592 cmd.append(" ");
6593 cmd.append(srcFullName);
6594 cmd.append(" -o ");
6595 cmd.append(destFullName);
6597 //## Execute the command
6599 String outString, errString;
6600 bool ret = executeCommand(cmd.c_str(), "", outString, errString);
6602 if (f)
6603 {
6604 fprintf(f, "########################### File : %s\n",
6605 srcFullName.c_str());
6606 fprintf(f, "#### COMMAND ###\n");
6607 int col = 0;
6608 for (unsigned int i = 0 ; i < cmd.size() ; i++)
6609 {
6610 char ch = cmd[i];
6611 if (isspace(ch) && col > 63)
6612 {
6613 fputc('\n', f);
6614 col = 0;
6615 }
6616 else
6617 {
6618 fputc(ch, f);
6619 col++;
6620 }
6621 if (col > 76)
6622 {
6623 fputc('\n', f);
6624 col = 0;
6625 }
6626 }
6627 fprintf(f, "\n");
6628 fprintf(f, "#### STDOUT ###\n%s\n", outString.c_str());
6629 fprintf(f, "#### STDERR ###\n%s\n\n", errString.c_str());
6630 fflush(f);
6631 }
6632 if (!ret)
6633 {
6634 error("problem compiling: %s", errString.c_str());
6635 errorOccurred = true;
6636 }
6637 if (errorOccurred && !continueOnError)
6638 break;
6639 }
6641 if (f)
6642 {
6643 fclose(f);
6644 }
6646 return !errorOccurred;
6647 }
6650 virtual bool parse(Element *elem)
6651 {
6652 String s;
6653 if (!parent.getAttribute(elem, "command", commandOpt))
6654 return false;
6655 if (commandOpt.size()>0)
6656 { cxxCommandOpt = ccCommandOpt = commandOpt; }
6657 if (!parent.getAttribute(elem, "cc", ccCommandOpt))
6658 return false;
6659 if (!parent.getAttribute(elem, "cxx", cxxCommandOpt))
6660 return false;
6661 if (!parent.getAttribute(elem, "destdir", destOpt))
6662 return false;
6663 if (!parent.getAttribute(elem, "continueOnError", continueOnErrorOpt))
6664 return false;
6665 if (!parent.getAttribute(elem, "refreshCache", refreshCacheOpt))
6666 return false;
6668 std::vector<Element *> children = elem->getChildren();
6669 for (unsigned int i=0 ; i<children.size() ; i++)
6670 {
6671 Element *child = children[i];
6672 String tagName = child->getName();
6673 if (tagName == "flags")
6674 {
6675 if (!parent.getValue(child, flagsOpt))
6676 return false;
6677 flagsOpt = strip(flagsOpt);
6678 }
6679 else if (tagName == "includes")
6680 {
6681 if (!parent.getValue(child, includesOpt))
6682 return false;
6683 includesOpt = strip(includesOpt);
6684 }
6685 else if (tagName == "defines")
6686 {
6687 if (!parent.getValue(child, definesOpt))
6688 return false;
6689 definesOpt = strip(definesOpt);
6690 }
6691 else if (tagName == "fileset")
6692 {
6693 if (!parseFileSet(child, parent, fileSet))
6694 return false;
6695 sourceOpt = fileSet.getDirectory();
6696 }
6697 else if (tagName == "excludeinc")
6698 {
6699 if (!parseFileList(child, parent, excludeInc))
6700 return false;
6701 }
6702 }
6704 return true;
6705 }
6707 protected:
6709 String commandOpt;
6710 String ccCommandOpt;
6711 String cxxCommandOpt;
6712 String sourceOpt;
6713 String destOpt;
6714 String flagsOpt;
6715 String definesOpt;
6716 String includesOpt;
6717 String continueOnErrorOpt;
6718 String refreshCacheOpt;
6719 FileSet fileSet;
6720 FileList excludeInc;
6722 };
6726 /**
6727 *
6728 */
6729 class TaskCopy : public Task
6730 {
6731 public:
6733 typedef enum
6734 {
6735 CP_NONE,
6736 CP_TOFILE,
6737 CP_TODIR
6738 } CopyType;
6740 TaskCopy(MakeBase &par) : Task(par)
6741 {
6742 type = TASK_COPY;
6743 name = "copy";
6744 cptype = CP_NONE;
6745 haveFileSet = false;
6746 }
6748 virtual ~TaskCopy()
6749 {}
6751 virtual bool execute()
6752 {
6753 String fileName = parent.eval(fileNameOpt , ".");
6754 String toFileName = parent.eval(toFileNameOpt , ".");
6755 String toDirName = parent.eval(toDirNameOpt , ".");
6756 bool verbose = parent.evalBool(verboseOpt, false);
6757 switch (cptype)
6758 {
6759 case CP_TOFILE:
6760 {
6761 if (fileName.size()>0)
6762 {
6763 taskstatus("%s to %s",
6764 fileName.c_str(), toFileName.c_str());
6765 String fullSource = parent.resolve(fileName);
6766 String fullDest = parent.resolve(toFileName);
6767 if (verbose)
6768 taskstatus("copy %s to file %s", fullSource.c_str(),
6769 fullDest.c_str());
6770 if (!isRegularFile(fullSource))
6771 {
6772 error("copy : file %s does not exist", fullSource.c_str());
6773 return false;
6774 }
6775 if (!isNewerThan(fullSource, fullDest))
6776 {
6777 taskstatus("skipped");
6778 return true;
6779 }
6780 if (!copyFile(fullSource, fullDest))
6781 return false;
6782 taskstatus("1 file copied");
6783 }
6784 return true;
6785 }
6786 case CP_TODIR:
6787 {
6788 if (haveFileSet)
6789 {
6790 if (!listFiles(parent, fileSet))
6791 return false;
6792 String fileSetDir = parent.eval(fileSet.getDirectory(), ".");
6794 taskstatus("%s to %s",
6795 fileSetDir.c_str(), toDirName.c_str());
6797 int nrFiles = 0;
6798 for (unsigned int i=0 ; i<fileSet.size() ; i++)
6799 {
6800 String fileName = fileSet[i];
6802 String sourcePath;
6803 if (fileSetDir.size()>0)
6804 {
6805 sourcePath.append(fileSetDir);
6806 sourcePath.append("/");
6807 }
6808 sourcePath.append(fileName);
6809 String fullSource = parent.resolve(sourcePath);
6811 //Get the immediate parent directory's base name
6812 String baseFileSetDir = fileSetDir;
6813 unsigned int pos = baseFileSetDir.find_last_of('/');
6814 if (pos!=baseFileSetDir.npos &&
6815 pos < baseFileSetDir.size()-1)
6816 baseFileSetDir =
6817 baseFileSetDir.substr(pos+1,
6818 baseFileSetDir.size());
6819 //Now make the new path
6820 String destPath;
6821 if (toDirName.size()>0)
6822 {
6823 destPath.append(toDirName);
6824 destPath.append("/");
6825 }
6826 if (baseFileSetDir.size()>0)
6827 {
6828 destPath.append(baseFileSetDir);
6829 destPath.append("/");
6830 }
6831 destPath.append(fileName);
6832 String fullDest = parent.resolve(destPath);
6833 //trace("fileName:%s", fileName.c_str());
6834 if (verbose)
6835 taskstatus("copy %s to new dir : %s",
6836 fullSource.c_str(), fullDest.c_str());
6837 if (!isNewerThan(fullSource, fullDest))
6838 {
6839 if (verbose)
6840 taskstatus("copy skipping %s", fullSource.c_str());
6841 continue;
6842 }
6843 if (!copyFile(fullSource, fullDest))
6844 return false;
6845 nrFiles++;
6846 }
6847 taskstatus("%d file(s) copied", nrFiles);
6848 }
6849 else //file source
6850 {
6851 //For file->dir we want only the basename of
6852 //the source appended to the dest dir
6853 taskstatus("%s to %s",
6854 fileName.c_str(), toDirName.c_str());
6855 String baseName = fileName;
6856 unsigned int pos = baseName.find_last_of('/');
6857 if (pos!=baseName.npos && pos<baseName.size()-1)
6858 baseName = baseName.substr(pos+1, baseName.size());
6859 String fullSource = parent.resolve(fileName);
6860 String destPath;
6861 if (toDirName.size()>0)
6862 {
6863 destPath.append(toDirName);
6864 destPath.append("/");
6865 }
6866 destPath.append(baseName);
6867 String fullDest = parent.resolve(destPath);
6868 if (verbose)
6869 taskstatus("file %s to new dir : %s", fullSource.c_str(),
6870 fullDest.c_str());
6871 if (!isRegularFile(fullSource))
6872 {
6873 error("copy : file %s does not exist", fullSource.c_str());
6874 return false;
6875 }
6876 if (!isNewerThan(fullSource, fullDest))
6877 {
6878 taskstatus("skipped");
6879 return true;
6880 }
6881 if (!copyFile(fullSource, fullDest))
6882 return false;
6883 taskstatus("1 file copied");
6884 }
6885 return true;
6886 }
6887 }
6888 return true;
6889 }
6892 virtual bool parse(Element *elem)
6893 {
6894 if (!parent.getAttribute(elem, "file", fileNameOpt))
6895 return false;
6896 if (!parent.getAttribute(elem, "tofile", toFileNameOpt))
6897 return false;
6898 if (toFileNameOpt.size() > 0)
6899 cptype = CP_TOFILE;
6900 if (!parent.getAttribute(elem, "todir", toDirNameOpt))
6901 return false;
6902 if (toDirNameOpt.size() > 0)
6903 cptype = CP_TODIR;
6904 if (!parent.getAttribute(elem, "verbose", verboseOpt))
6905 return false;
6907 haveFileSet = false;
6909 std::vector<Element *> children = elem->getChildren();
6910 for (unsigned int i=0 ; i<children.size() ; i++)
6911 {
6912 Element *child = children[i];
6913 String tagName = child->getName();
6914 if (tagName == "fileset")
6915 {
6916 if (!parseFileSet(child, parent, fileSet))
6917 {
6918 error("problem getting fileset");
6919 return false;
6920 }
6921 haveFileSet = true;
6922 }
6923 }
6925 //Perform validity checks
6926 if (fileNameOpt.size()>0 && fileSet.size()>0)
6927 {
6928 error("<copy> can only have one of : file= and <fileset>");
6929 return false;
6930 }
6931 if (toFileNameOpt.size()>0 && toDirNameOpt.size()>0)
6932 {
6933 error("<copy> can only have one of : tofile= or todir=");
6934 return false;
6935 }
6936 if (haveFileSet && toDirNameOpt.size()==0)
6937 {
6938 error("a <copy> task with a <fileset> must have : todir=");
6939 return false;
6940 }
6941 if (cptype == CP_TOFILE && fileNameOpt.size()==0)
6942 {
6943 error("<copy> tofile= must be associated with : file=");
6944 return false;
6945 }
6946 if (cptype == CP_TODIR && fileNameOpt.size()==0 && !haveFileSet)
6947 {
6948 error("<copy> todir= must be associated with : file= or <fileset>");
6949 return false;
6950 }
6952 return true;
6953 }
6955 private:
6957 int cptype;
6958 bool haveFileSet;
6960 FileSet fileSet;
6961 String fileNameOpt;
6962 String toFileNameOpt;
6963 String toDirNameOpt;
6964 String verboseOpt;
6965 };
6968 /**
6969 *
6970 */
6971 class TaskDelete : public Task
6972 {
6973 public:
6975 typedef enum
6976 {
6977 DEL_FILE,
6978 DEL_DIR,
6979 DEL_FILESET
6980 } DeleteType;
6982 TaskDelete(MakeBase &par) : Task(par)
6983 {
6984 type = TASK_DELETE;
6985 name = "delete";
6986 delType = DEL_FILE;
6987 }
6989 virtual ~TaskDelete()
6990 {}
6992 virtual bool execute()
6993 {
6994 String dirName = parent.eval(dirNameOpt, ".");
6995 String fileName = parent.eval(fileNameOpt, ".");
6996 bool verbose = parent.evalBool(verboseOpt, false);
6997 bool quiet = parent.evalBool(quietOpt, false);
6998 bool failOnError = parent.evalBool(failOnErrorOpt, true);
6999 struct stat finfo;
7000 switch (delType)
7001 {
7002 case DEL_FILE:
7003 {
7004 taskstatus("file: %s", fileName.c_str());
7005 String fullName = parent.resolve(fileName);
7006 char *fname = (char *)fullName.c_str();
7007 if (!quiet && verbose)
7008 taskstatus("path: %s", fname);
7009 //does not exist
7010 if (stat(fname, &finfo)<0)
7011 {
7012 if (failOnError)
7013 return false;
7014 else
7015 return true;
7016 }
7017 //exists but is not a regular file
7018 if (!S_ISREG(finfo.st_mode))
7019 {
7020 error("<delete> failed. '%s' exists and is not a regular file",
7021 fname);
7022 return false;
7023 }
7024 if (remove(fname)<0)
7025 {
7026 error("<delete> failed: %s", strerror(errno));
7027 return false;
7028 }
7029 return true;
7030 }
7031 case DEL_DIR:
7032 {
7033 taskstatus("dir: %s", dirName.c_str());
7034 String fullDir = parent.resolve(dirName);
7035 if (!quiet && verbose)
7036 taskstatus("path: %s", fullDir.c_str());
7037 if (!removeDirectory(fullDir))
7038 return false;
7039 return true;
7040 }
7041 }
7042 return true;
7043 }
7045 virtual bool parse(Element *elem)
7046 {
7047 if (!parent.getAttribute(elem, "file", fileNameOpt))
7048 return false;
7049 if (fileNameOpt.size() > 0)
7050 delType = DEL_FILE;
7051 if (!parent.getAttribute(elem, "dir", dirNameOpt))
7052 return false;
7053 if (dirNameOpt.size() > 0)
7054 delType = DEL_DIR;
7055 if (fileNameOpt.size()>0 && dirNameOpt.size()>0)
7056 {
7057 error("<delete> can have one attribute of file= or dir=");
7058 return false;
7059 }
7060 if (fileNameOpt.size()==0 && dirNameOpt.size()==0)
7061 {
7062 error("<delete> must have one attribute of file= or dir=");
7063 return false;
7064 }
7065 if (!parent.getAttribute(elem, "verbose", verboseOpt))
7066 return false;
7067 if (!parent.getAttribute(elem, "quiet", quietOpt))
7068 return false;
7069 if (!parent.getAttribute(elem, "failonerror", failOnErrorOpt))
7070 return false;
7071 return true;
7072 }
7074 private:
7076 int delType;
7077 String dirNameOpt;
7078 String fileNameOpt;
7079 String verboseOpt;
7080 String quietOpt;
7081 String failOnErrorOpt;
7082 };
7085 /**
7086 * Send a message to stdout
7087 */
7088 class TaskEcho : public Task
7089 {
7090 public:
7092 TaskEcho(MakeBase &par) : Task(par)
7093 { type = TASK_ECHO; name = "echo"; }
7095 virtual ~TaskEcho()
7096 {}
7098 virtual bool execute()
7099 {
7100 //let message have priority over text
7101 String message = parent.eval(messageOpt, "");
7102 String text = parent.eval(textOpt, "");
7103 if (message.size() > 0)
7104 {
7105 fprintf(stdout, "%s\n", message.c_str());
7106 }
7107 else if (text.size() > 0)
7108 {
7109 fprintf(stdout, "%s\n", text.c_str());
7110 }
7111 return true;
7112 }
7114 virtual bool parse(Element *elem)
7115 {
7116 if (!parent.getValue(elem, textOpt))
7117 return false;
7118 textOpt = leftJustify(textOpt);
7119 if (!parent.getAttribute(elem, "message", messageOpt))
7120 return false;
7121 return true;
7122 }
7124 private:
7126 String messageOpt;
7127 String textOpt;
7128 };
7132 /**
7133 *
7134 */
7135 class TaskJar : public Task
7136 {
7137 public:
7139 TaskJar(MakeBase &par) : Task(par)
7140 { type = TASK_JAR; name = "jar"; }
7142 virtual ~TaskJar()
7143 {}
7145 virtual bool execute()
7146 {
7147 String command = parent.eval(commandOpt, "jar");
7148 String basedir = parent.eval(basedirOpt, ".");
7149 String destfile = parent.eval(destfileOpt, ".");
7151 String cmd = command;
7152 cmd.append(" -cf ");
7153 cmd.append(destfile);
7154 cmd.append(" -C ");
7155 cmd.append(basedir);
7156 cmd.append(" .");
7158 String execCmd = cmd;
7160 String outString, errString;
7161 bool ret = executeCommand(execCmd.c_str(), "", outString, errString);
7162 if (!ret)
7163 {
7164 error("<jar> command '%s' failed :\n %s",
7165 execCmd.c_str(), errString.c_str());
7166 return false;
7167 }
7168 return true;
7169 }
7171 virtual bool parse(Element *elem)
7172 {
7173 if (!parent.getAttribute(elem, "command", commandOpt))
7174 return false;
7175 if (!parent.getAttribute(elem, "basedir", basedirOpt))
7176 return false;
7177 if (!parent.getAttribute(elem, "destfile", destfileOpt))
7178 return false;
7179 if (basedirOpt.size() == 0 || destfileOpt.size() == 0)
7180 {
7181 error("<jar> required both basedir and destfile attributes to be set");
7182 return false;
7183 }
7184 return true;
7185 }
7187 private:
7189 String commandOpt;
7190 String basedirOpt;
7191 String destfileOpt;
7192 };
7195 /**
7196 *
7197 */
7198 class TaskJavac : public Task
7199 {
7200 public:
7202 TaskJavac(MakeBase &par) : Task(par)
7203 {
7204 type = TASK_JAVAC; name = "javac";
7205 }
7207 virtual ~TaskJavac()
7208 {}
7210 virtual bool execute()
7211 {
7212 String command = parent.eval(commandOpt, "javac");
7213 String srcdir = parent.eval(srcdirOpt, ".");
7214 String destdir = parent.eval(destdirOpt, ".");
7215 String target = parent.eval(targetOpt, "");
7217 std::vector<String> fileList;
7218 if (!listFiles(srcdir, "", fileList))
7219 {
7220 return false;
7221 }
7222 String cmd = command;
7223 cmd.append(" -d ");
7224 cmd.append(destdir);
7225 cmd.append(" -classpath ");
7226 cmd.append(destdir);
7227 cmd.append(" -sourcepath ");
7228 cmd.append(srcdir);
7229 cmd.append(" ");
7230 if (target.size()>0)
7231 {
7232 cmd.append(" -target ");
7233 cmd.append(target);
7234 cmd.append(" ");
7235 }
7236 String fname = "javalist.btool";
7237 FILE *f = fopen(fname.c_str(), "w");
7238 int count = 0;
7239 for (unsigned int i=0 ; i<fileList.size() ; i++)
7240 {
7241 String fname = fileList[i];
7242 String srcName = fname;
7243 if (fname.size()<6) //x.java
7244 continue;
7245 if (fname.compare(fname.size()-5, 5, ".java") != 0)
7246 continue;
7247 String baseName = fname.substr(0, fname.size()-5);
7248 String destName = baseName;
7249 destName.append(".class");
7251 String fullSrc = srcdir;
7252 fullSrc.append("/");
7253 fullSrc.append(fname);
7254 String fullDest = destdir;
7255 fullDest.append("/");
7256 fullDest.append(destName);
7257 //trace("fullsrc:%s fulldest:%s", fullSrc.c_str(), fullDest.c_str());
7258 if (!isNewerThan(fullSrc, fullDest))
7259 continue;
7261 count++;
7262 fprintf(f, "%s\n", fullSrc.c_str());
7263 }
7264 fclose(f);
7265 if (!count)
7266 {
7267 taskstatus("nothing to do");
7268 return true;
7269 }
7271 taskstatus("compiling %d files", count);
7273 String execCmd = cmd;
7274 execCmd.append("@");
7275 execCmd.append(fname);
7277 String outString, errString;
7278 bool ret = executeCommand(execCmd.c_str(), "", outString, errString);
7279 if (!ret)
7280 {
7281 error("<javac> command '%s' failed :\n %s",
7282 execCmd.c_str(), errString.c_str());
7283 return false;
7284 }
7285 return true;
7286 }
7288 virtual bool parse(Element *elem)
7289 {
7290 if (!parent.getAttribute(elem, "command", commandOpt))
7291 return false;
7292 if (!parent.getAttribute(elem, "srcdir", srcdirOpt))
7293 return false;
7294 if (!parent.getAttribute(elem, "destdir", destdirOpt))
7295 return false;
7296 if (srcdirOpt.size() == 0 || destdirOpt.size() == 0)
7297 {
7298 error("<javac> required both srcdir and destdir attributes to be set");
7299 return false;
7300 }
7301 if (!parent.getAttribute(elem, "target", targetOpt))
7302 return false;
7303 return true;
7304 }
7306 private:
7308 String commandOpt;
7309 String srcdirOpt;
7310 String destdirOpt;
7311 String targetOpt;
7313 };
7316 /**
7317 *
7318 */
7319 class TaskLink : public Task
7320 {
7321 public:
7323 TaskLink(MakeBase &par) : Task(par)
7324 {
7325 type = TASK_LINK; name = "link";
7326 }
7328 virtual ~TaskLink()
7329 {}
7331 virtual bool execute()
7332 {
7333 String command = parent.eval(commandOpt, "g++");
7334 String fileName = parent.eval(fileNameOpt, "");
7335 String flags = parent.eval(flagsOpt, "");
7336 String libs = parent.eval(libsOpt, "");
7337 bool doStrip = parent.evalBool(doStripOpt, false);
7338 String symFileName = parent.eval(symFileNameOpt, "");
7339 String stripCommand = parent.eval(stripCommandOpt, "strip");
7340 String objcopyCommand = parent.eval(objcopyCommandOpt, "objcopy");
7342 if (!listFiles(parent, fileSet))
7343 return false;
7344 String fileSetDir = parent.eval(fileSet.getDirectory(), ".");
7345 //trace("%d files in %s", fileSet.size(), fileSetDir.c_str());
7346 bool doit = false;
7347 String fullTarget = parent.resolve(fileName);
7348 String cmd = command;
7349 cmd.append(" -o ");
7350 cmd.append(fullTarget);
7351 cmd.append(" ");
7352 cmd.append(flags);
7353 for (unsigned int i=0 ; i<fileSet.size() ; i++)
7354 {
7355 cmd.append(" ");
7356 String obj;
7357 if (fileSetDir.size()>0)
7358 {
7359 obj.append(fileSetDir);
7360 obj.append("/");
7361 }
7362 obj.append(fileSet[i]);
7363 String fullObj = parent.resolve(obj);
7364 String nativeFullObj = getNativePath(fullObj);
7365 cmd.append(nativeFullObj);
7366 //trace("link: tgt:%s obj:%s", fullTarget.c_str(),
7367 // fullObj.c_str());
7368 if (isNewerThan(fullObj, fullTarget))
7369 doit = true;
7370 }
7371 cmd.append(" ");
7372 cmd.append(libs);
7373 if (!doit)
7374 {
7375 //trace("link not needed");
7376 return true;
7377 }
7378 //trace("LINK cmd:%s", cmd.c_str());
7381 String outbuf, errbuf;
7382 if (!executeCommand(cmd.c_str(), "", outbuf, errbuf))
7383 {
7384 error("LINK problem: %s", errbuf.c_str());
7385 return false;
7386 }
7388 if (symFileName.size()>0)
7389 {
7390 String symFullName = parent.resolve(symFileName);
7391 cmd = objcopyCommand;
7392 cmd.append(" --only-keep-debug ");
7393 cmd.append(getNativePath(fullTarget));
7394 cmd.append(" ");
7395 cmd.append(getNativePath(symFullName));
7396 if (!executeCommand(cmd, "", outbuf, errbuf))
7397 {
7398 error("<strip> symbol file failed : %s", errbuf.c_str());
7399 return false;
7400 }
7401 }
7403 if (doStrip)
7404 {
7405 cmd = stripCommand;
7406 cmd.append(" ");
7407 cmd.append(getNativePath(fullTarget));
7408 if (!executeCommand(cmd, "", outbuf, errbuf))
7409 {
7410 error("<strip> failed : %s", errbuf.c_str());
7411 return false;
7412 }
7413 }
7415 return true;
7416 }
7418 virtual bool parse(Element *elem)
7419 {
7420 if (!parent.getAttribute(elem, "command", commandOpt))
7421 return false;
7422 if (!parent.getAttribute(elem, "objcopycommand", objcopyCommandOpt))
7423 return false;
7424 if (!parent.getAttribute(elem, "stripcommand", stripCommandOpt))
7425 return false;
7426 if (!parent.getAttribute(elem, "out", fileNameOpt))
7427 return false;
7428 if (!parent.getAttribute(elem, "strip", doStripOpt))
7429 return false;
7430 if (!parent.getAttribute(elem, "symfile", symFileNameOpt))
7431 return false;
7433 std::vector<Element *> children = elem->getChildren();
7434 for (unsigned int i=0 ; i<children.size() ; i++)
7435 {
7436 Element *child = children[i];
7437 String tagName = child->getName();
7438 if (tagName == "fileset")
7439 {
7440 if (!parseFileSet(child, parent, fileSet))
7441 return false;
7442 }
7443 else if (tagName == "flags")
7444 {
7445 if (!parent.getValue(child, flagsOpt))
7446 return false;
7447 flagsOpt = strip(flagsOpt);
7448 }
7449 else if (tagName == "libs")
7450 {
7451 if (!parent.getValue(child, libsOpt))
7452 return false;
7453 libsOpt = strip(libsOpt);
7454 }
7455 }
7456 return true;
7457 }
7459 private:
7461 FileSet fileSet;
7463 String commandOpt;
7464 String fileNameOpt;
7465 String flagsOpt;
7466 String libsOpt;
7467 String doStripOpt;
7468 String symFileNameOpt;
7469 String stripCommandOpt;
7470 String objcopyCommandOpt;
7472 };
7476 /**
7477 * Create a named file
7478 */
7479 class TaskMakeFile : public Task
7480 {
7481 public:
7483 TaskMakeFile(MakeBase &par) : Task(par)
7484 { type = TASK_MAKEFILE; name = "makefile"; }
7486 virtual ~TaskMakeFile()
7487 {}
7489 virtual bool execute()
7490 {
7491 String fileName = parent.eval(fileNameOpt, "");
7492 String text = parent.eval(textOpt, "");
7494 taskstatus("%s", fileName.c_str());
7495 String fullName = parent.resolve(fileName);
7496 if (!isNewerThan(parent.getURI().getPath(), fullName))
7497 {
7498 //trace("skipped <makefile>");
7499 return true;
7500 }
7501 String fullNative = getNativePath(fullName);
7502 //trace("fullName:%s", fullName.c_str());
7503 FILE *f = fopen(fullNative.c_str(), "w");
7504 if (!f)
7505 {
7506 error("<makefile> could not open %s for writing : %s",
7507 fullName.c_str(), strerror(errno));
7508 return false;
7509 }
7510 for (unsigned int i=0 ; i<text.size() ; i++)
7511 fputc(text[i], f);
7512 fputc('\n', f);
7513 fclose(f);
7514 return true;
7515 }
7517 virtual bool parse(Element *elem)
7518 {
7519 if (!parent.getAttribute(elem, "file", fileNameOpt))
7520 return false;
7521 if (fileNameOpt.size() == 0)
7522 {
7523 error("<makefile> requires 'file=\"filename\"' attribute");
7524 return false;
7525 }
7526 if (!parent.getValue(elem, textOpt))
7527 return false;
7528 textOpt = leftJustify(textOpt);
7529 //trace("dirname:%s", dirName.c_str());
7530 return true;
7531 }
7533 private:
7535 String fileNameOpt;
7536 String textOpt;
7537 };
7541 /**
7542 * Create a named directory
7543 */
7544 class TaskMkDir : public Task
7545 {
7546 public:
7548 TaskMkDir(MakeBase &par) : Task(par)
7549 { type = TASK_MKDIR; name = "mkdir"; }
7551 virtual ~TaskMkDir()
7552 {}
7554 virtual bool execute()
7555 {
7556 String dirName = parent.eval(dirNameOpt, ".");
7558 taskstatus("%s", dirName.c_str());
7559 String fullDir = parent.resolve(dirName);
7560 //trace("fullDir:%s", fullDir.c_str());
7561 if (!createDirectory(fullDir))
7562 return false;
7563 return true;
7564 }
7566 virtual bool parse(Element *elem)
7567 {
7568 if (!parent.getAttribute(elem, "dir", dirNameOpt))
7569 return false;
7570 if (dirNameOpt.size() == 0)
7571 {
7572 error("<mkdir> requires 'dir=\"dirname\"' attribute");
7573 return false;
7574 }
7575 return true;
7576 }
7578 private:
7580 String dirNameOpt;
7581 };
7585 /**
7586 * Create a named directory
7587 */
7588 class TaskMsgFmt: public Task
7589 {
7590 public:
7592 TaskMsgFmt(MakeBase &par) : Task(par)
7593 { type = TASK_MSGFMT; name = "msgfmt"; }
7595 virtual ~TaskMsgFmt()
7596 {}
7598 virtual bool execute()
7599 {
7600 String command = parent.eval(commandOpt, "msgfmt");
7601 String toDirName = parent.eval(toDirNameOpt, ".");
7602 String outName = parent.eval(outNameOpt, "");
7603 bool owndir = parent.evalBool(owndirOpt, false);
7605 if (!listFiles(parent, fileSet))
7606 return false;
7607 String fileSetDir = fileSet.getDirectory();
7609 //trace("msgfmt: %d", fileSet.size());
7610 for (unsigned int i=0 ; i<fileSet.size() ; i++)
7611 {
7612 String fileName = fileSet[i];
7613 if (getSuffix(fileName) != "po")
7614 continue;
7615 String sourcePath;
7616 if (fileSetDir.size()>0)
7617 {
7618 sourcePath.append(fileSetDir);
7619 sourcePath.append("/");
7620 }
7621 sourcePath.append(fileName);
7622 String fullSource = parent.resolve(sourcePath);
7624 String destPath;
7625 if (toDirName.size()>0)
7626 {
7627 destPath.append(toDirName);
7628 destPath.append("/");
7629 }
7630 if (owndir)
7631 {
7632 String subdir = fileName;
7633 unsigned int pos = subdir.find_last_of('.');
7634 if (pos != subdir.npos)
7635 subdir = subdir.substr(0, pos);
7636 destPath.append(subdir);
7637 destPath.append("/");
7638 }
7639 //Pick the output file name
7640 if (outName.size() > 0)
7641 {
7642 destPath.append(outName);
7643 }
7644 else
7645 {
7646 destPath.append(fileName);
7647 destPath[destPath.size()-2] = 'm';
7648 }
7650 String fullDest = parent.resolve(destPath);
7652 if (!isNewerThan(fullSource, fullDest))
7653 {
7654 //trace("skip %s", fullSource.c_str());
7655 continue;
7656 }
7658 String cmd = command;
7659 cmd.append(" ");
7660 cmd.append(fullSource);
7661 cmd.append(" -o ");
7662 cmd.append(fullDest);
7664 int pos = fullDest.find_last_of('/');
7665 if (pos>0)
7666 {
7667 String fullDestPath = fullDest.substr(0, pos);
7668 if (!createDirectory(fullDestPath))
7669 return false;
7670 }
7674 String outString, errString;
7675 if (!executeCommand(cmd.c_str(), "", outString, errString))
7676 {
7677 error("<msgfmt> problem: %s", errString.c_str());
7678 return false;
7679 }
7680 }
7682 return true;
7683 }
7685 virtual bool parse(Element *elem)
7686 {
7687 if (!parent.getAttribute(elem, "command", commandOpt))
7688 return false;
7689 if (!parent.getAttribute(elem, "todir", toDirNameOpt))
7690 return false;
7691 if (!parent.getAttribute(elem, "out", outNameOpt))
7692 return false;
7693 if (!parent.getAttribute(elem, "owndir", owndirOpt))
7694 return false;
7696 std::vector<Element *> children = elem->getChildren();
7697 for (unsigned int i=0 ; i<children.size() ; i++)
7698 {
7699 Element *child = children[i];
7700 String tagName = child->getName();
7701 if (tagName == "fileset")
7702 {
7703 if (!parseFileSet(child, parent, fileSet))
7704 return false;
7705 }
7706 }
7707 return true;
7708 }
7710 private:
7712 FileSet fileSet;
7714 String commandOpt;
7715 String toDirNameOpt;
7716 String outNameOpt;
7717 String owndirOpt;
7719 };
7723 /**
7724 * Perform a Package-Config query similar to pkg-config
7725 */
7726 class TaskPkgConfig : public Task
7727 {
7728 public:
7730 typedef enum
7731 {
7732 PKG_CONFIG_QUERY_CFLAGS,
7733 PKG_CONFIG_QUERY_LIBS,
7734 PKG_CONFIG_QUERY_ALL
7735 } QueryTypes;
7737 TaskPkgConfig(MakeBase &par) : Task(par)
7738 {
7739 type = TASK_PKG_CONFIG;
7740 name = "pkg-config";
7741 }
7743 virtual ~TaskPkgConfig()
7744 {}
7746 virtual bool execute()
7747 {
7748 String pkgName = parent.eval(pkgNameOpt, "");
7749 String prefix = parent.eval(prefixOpt, "");
7750 String propName = parent.eval(propNameOpt, "");
7751 String pkgConfigPath = parent.eval(pkgConfigPathOpt,"");
7752 String query = parent.eval(queryOpt, "all");
7754 String path = parent.resolve(pkgConfigPath);
7755 PkgConfig pkgconfig;
7756 pkgconfig.setPath(path);
7757 pkgconfig.setPrefix(prefix);
7758 if (!pkgconfig.query(pkgName))
7759 {
7760 error("<pkg-config> query failed for '%s", name.c_str());
7761 return false;
7762 }
7764 String val = "";
7765 if (query == "cflags")
7766 val = pkgconfig.getCflags();
7767 else if (query == "libs")
7768 val =pkgconfig.getLibs();
7769 else if (query == "all")
7770 val = pkgconfig.getAll();
7771 else
7772 {
7773 error("<pkg-config> unhandled query : %s", query.c_str());
7774 return false;
7775 }
7776 taskstatus("property %s = '%s'", propName.c_str(), val.c_str());
7777 parent.setProperty(propName, val);
7778 return true;
7779 }
7781 virtual bool parse(Element *elem)
7782 {
7783 //# NAME
7784 if (!parent.getAttribute(elem, "name", pkgNameOpt))
7785 return false;
7786 if (pkgNameOpt.size()==0)
7787 {
7788 error("<pkg-config> requires 'name=\"package\"' attribute");
7789 return false;
7790 }
7792 //# PROPERTY
7793 if (!parent.getAttribute(elem, "property", propNameOpt))
7794 return false;
7795 if (propNameOpt.size()==0)
7796 {
7797 error("<pkg-config> requires 'property=\"name\"' attribute");
7798 return false;
7799 }
7800 //# PATH
7801 if (!parent.getAttribute(elem, "path", pkgConfigPathOpt))
7802 return false;
7803 //# PREFIX
7804 if (!parent.getAttribute(elem, "prefix", prefixOpt))
7805 return false;
7806 //# QUERY
7807 if (!parent.getAttribute(elem, "query", queryOpt))
7808 return false;
7810 return true;
7811 }
7813 private:
7815 String queryOpt;
7816 String pkgNameOpt;
7817 String prefixOpt;
7818 String propNameOpt;
7819 String pkgConfigPathOpt;
7821 };
7828 /**
7829 * Process an archive to allow random access
7830 */
7831 class TaskRanlib : public Task
7832 {
7833 public:
7835 TaskRanlib(MakeBase &par) : Task(par)
7836 { type = TASK_RANLIB; name = "ranlib"; }
7838 virtual ~TaskRanlib()
7839 {}
7841 virtual bool execute()
7842 {
7843 String fileName = parent.eval(fileNameOpt, "");
7844 String command = parent.eval(commandOpt, "ranlib");
7846 String fullName = parent.resolve(fileName);
7847 //trace("fullDir:%s", fullDir.c_str());
7848 String cmd = command;
7849 cmd.append(" ");
7850 cmd.append(fullName);
7851 String outbuf, errbuf;
7852 if (!executeCommand(cmd, "", outbuf, errbuf))
7853 return false;
7854 return true;
7855 }
7857 virtual bool parse(Element *elem)
7858 {
7859 if (!parent.getAttribute(elem, "command", commandOpt))
7860 return false;
7861 if (!parent.getAttribute(elem, "file", fileNameOpt))
7862 return false;
7863 if (fileNameOpt.size() == 0)
7864 {
7865 error("<ranlib> requires 'file=\"fileNname\"' attribute");
7866 return false;
7867 }
7868 return true;
7869 }
7871 private:
7873 String fileNameOpt;
7874 String commandOpt;
7875 };
7879 /**
7880 * Compile a resource file into a binary object
7881 */
7882 class TaskRC : public Task
7883 {
7884 public:
7886 TaskRC(MakeBase &par) : Task(par)
7887 { type = TASK_RC; name = "rc"; }
7889 virtual ~TaskRC()
7890 {}
7892 virtual bool execute()
7893 {
7894 String command = parent.eval(commandOpt, "windres");
7895 String flags = parent.eval(flagsOpt, "");
7896 String fileName = parent.eval(fileNameOpt, "");
7897 String outName = parent.eval(outNameOpt, "");
7899 String fullFile = parent.resolve(fileName);
7900 String fullOut = parent.resolve(outName);
7901 if (!isNewerThan(fullFile, fullOut))
7902 return true;
7903 String cmd = command;
7904 cmd.append(" -o ");
7905 cmd.append(fullOut);
7906 cmd.append(" ");
7907 cmd.append(flags);
7908 cmd.append(" ");
7909 cmd.append(fullFile);
7911 String outString, errString;
7912 if (!executeCommand(cmd.c_str(), "", outString, errString))
7913 {
7914 error("RC problem: %s", errString.c_str());
7915 return false;
7916 }
7917 return true;
7918 }
7920 virtual bool parse(Element *elem)
7921 {
7922 if (!parent.getAttribute(elem, "command", commandOpt))
7923 return false;
7924 if (!parent.getAttribute(elem, "file", fileNameOpt))
7925 return false;
7926 if (!parent.getAttribute(elem, "out", outNameOpt))
7927 return false;
7928 std::vector<Element *> children = elem->getChildren();
7929 for (unsigned int i=0 ; i<children.size() ; i++)
7930 {
7931 Element *child = children[i];
7932 String tagName = child->getName();
7933 if (tagName == "flags")
7934 {
7935 if (!parent.getValue(child, flagsOpt))
7936 return false;
7937 }
7938 }
7939 return true;
7940 }
7942 private:
7944 String commandOpt;
7945 String flagsOpt;
7946 String fileNameOpt;
7947 String outNameOpt;
7949 };
7953 /**
7954 * Collect .o's into a .so or DLL
7955 */
7956 class TaskSharedLib : public Task
7957 {
7958 public:
7960 TaskSharedLib(MakeBase &par) : Task(par)
7961 { type = TASK_SHAREDLIB; name = "dll"; }
7963 virtual ~TaskSharedLib()
7964 {}
7966 virtual bool execute()
7967 {
7968 String command = parent.eval(commandOpt, "dllwrap");
7969 String fileName = parent.eval(fileNameOpt, "");
7970 String defFileName = parent.eval(defFileNameOpt, "");
7971 String impFileName = parent.eval(impFileNameOpt, "");
7972 String libs = parent.eval(libsOpt, "");
7974 //trace("###########HERE %d", fileSet.size());
7975 bool doit = false;
7977 String fullOut = parent.resolve(fileName);
7978 //trace("ar fullout: %s", fullOut.c_str());
7980 if (!listFiles(parent, fileSet))
7981 return false;
7982 String fileSetDir = parent.eval(fileSet.getDirectory(), ".");
7984 for (unsigned int i=0 ; i<fileSet.size() ; i++)
7985 {
7986 String fname;
7987 if (fileSetDir.size()>0)
7988 {
7989 fname.append(fileSetDir);
7990 fname.append("/");
7991 }
7992 fname.append(fileSet[i]);
7993 String fullName = parent.resolve(fname);
7994 //trace("ar : %s/%s", fullOut.c_str(), fullName.c_str());
7995 if (isNewerThan(fullName, fullOut))
7996 doit = true;
7997 }
7998 //trace("Needs it:%d", doit);
7999 if (!doit)
8000 {
8001 return true;
8002 }
8004 String cmd = "dllwrap";
8005 cmd.append(" -o ");
8006 cmd.append(fullOut);
8007 if (defFileName.size()>0)
8008 {
8009 cmd.append(" --def ");
8010 cmd.append(defFileName);
8011 cmd.append(" ");
8012 }
8013 if (impFileName.size()>0)
8014 {
8015 cmd.append(" --implib ");
8016 cmd.append(impFileName);
8017 cmd.append(" ");
8018 }
8019 for (unsigned int i=0 ; i<fileSet.size() ; i++)
8020 {
8021 String fname;
8022 if (fileSetDir.size()>0)
8023 {
8024 fname.append(fileSetDir);
8025 fname.append("/");
8026 }
8027 fname.append(fileSet[i]);
8028 String fullName = parent.resolve(fname);
8030 cmd.append(" ");
8031 cmd.append(fullName);
8032 }
8033 cmd.append(" ");
8034 cmd.append(libs);
8036 String outString, errString;
8037 if (!executeCommand(cmd.c_str(), "", outString, errString))
8038 {
8039 error("<sharedlib> problem: %s", errString.c_str());
8040 return false;
8041 }
8043 return true;
8044 }
8046 virtual bool parse(Element *elem)
8047 {
8048 if (!parent.getAttribute(elem, "command", commandOpt))
8049 return false;
8050 if (!parent.getAttribute(elem, "file", fileNameOpt))
8051 return false;
8052 if (!parent.getAttribute(elem, "import", impFileNameOpt))
8053 return false;
8054 if (!parent.getAttribute(elem, "def", defFileNameOpt))
8055 return false;
8057 std::vector<Element *> children = elem->getChildren();
8058 for (unsigned int i=0 ; i<children.size() ; i++)
8059 {
8060 Element *child = children[i];
8061 String tagName = child->getName();
8062 if (tagName == "fileset")
8063 {
8064 if (!parseFileSet(child, parent, fileSet))
8065 return false;
8066 }
8067 else if (tagName == "libs")
8068 {
8069 if (!parent.getValue(child, libsOpt))
8070 return false;
8071 libsOpt = strip(libsOpt);
8072 }
8073 }
8074 return true;
8075 }
8077 private:
8079 FileSet fileSet;
8081 String commandOpt;
8082 String fileNameOpt;
8083 String defFileNameOpt;
8084 String impFileNameOpt;
8085 String libsOpt;
8087 };
8091 /**
8092 * Run the "ar" command to archive .o's into a .a
8093 */
8094 class TaskStaticLib : public Task
8095 {
8096 public:
8098 TaskStaticLib(MakeBase &par) : Task(par)
8099 { type = TASK_STATICLIB; name = "staticlib"; }
8101 virtual ~TaskStaticLib()
8102 {}
8104 virtual bool execute()
8105 {
8106 String command = parent.eval(commandOpt, "ar crv");
8107 String fileName = parent.eval(fileNameOpt, "");
8109 bool doit = false;
8111 String fullOut = parent.resolve(fileName);
8112 //trace("ar fullout: %s", fullOut.c_str());
8114 if (!listFiles(parent, fileSet))
8115 return false;
8116 String fileSetDir = parent.eval(fileSet.getDirectory(), ".");
8117 //trace("###########HERE %s", fileSetDir.c_str());
8119 for (unsigned int i=0 ; i<fileSet.size() ; i++)
8120 {
8121 String fname;
8122 if (fileSetDir.size()>0)
8123 {
8124 fname.append(fileSetDir);
8125 fname.append("/");
8126 }
8127 fname.append(fileSet[i]);
8128 String fullName = parent.resolve(fname);
8129 //trace("ar : %s/%s", fullOut.c_str(), fullName.c_str());
8130 if (isNewerThan(fullName, fullOut))
8131 doit = true;
8132 }
8133 //trace("Needs it:%d", doit);
8134 if (!doit)
8135 {
8136 return true;
8137 }
8139 String cmd = command;
8140 cmd.append(" ");
8141 cmd.append(fullOut);
8142 for (unsigned int i=0 ; i<fileSet.size() ; i++)
8143 {
8144 String fname;
8145 if (fileSetDir.size()>0)
8146 {
8147 fname.append(fileSetDir);
8148 fname.append("/");
8149 }
8150 fname.append(fileSet[i]);
8151 String fullName = parent.resolve(fname);
8153 cmd.append(" ");
8154 cmd.append(fullName);
8155 }
8157 String outString, errString;
8158 if (!executeCommand(cmd.c_str(), "", outString, errString))
8159 {
8160 error("<staticlib> problem: %s", errString.c_str());
8161 return false;
8162 }
8164 return true;
8165 }
8168 virtual bool parse(Element *elem)
8169 {
8170 if (!parent.getAttribute(elem, "command", commandOpt))
8171 return false;
8172 if (!parent.getAttribute(elem, "file", fileNameOpt))
8173 return false;
8175 std::vector<Element *> children = elem->getChildren();
8176 for (unsigned int i=0 ; i<children.size() ; i++)
8177 {
8178 Element *child = children[i];
8179 String tagName = child->getName();
8180 if (tagName == "fileset")
8181 {
8182 if (!parseFileSet(child, parent, fileSet))
8183 return false;
8184 }
8185 }
8186 return true;
8187 }
8189 private:
8191 FileSet fileSet;
8193 String commandOpt;
8194 String fileNameOpt;
8196 };
8201 /**
8202 * Strip an executable
8203 */
8204 class TaskStrip : public Task
8205 {
8206 public:
8208 TaskStrip(MakeBase &par) : Task(par)
8209 { type = TASK_STRIP; name = "strip"; }
8211 virtual ~TaskStrip()
8212 {}
8214 virtual bool execute()
8215 {
8216 String command = parent.eval(commandOpt, "strip");
8217 String fileName = parent.eval(fileNameOpt, "");
8218 String symFileName = parent.eval(symFileNameOpt, "");
8220 String fullName = parent.resolve(fileName);
8221 //trace("fullDir:%s", fullDir.c_str());
8222 String cmd;
8223 String outbuf, errbuf;
8225 if (symFileName.size()>0)
8226 {
8227 String symFullName = parent.resolve(symFileName);
8228 cmd = "objcopy --only-keep-debug ";
8229 cmd.append(getNativePath(fullName));
8230 cmd.append(" ");
8231 cmd.append(getNativePath(symFullName));
8232 if (!executeCommand(cmd, "", outbuf, errbuf))
8233 {
8234 error("<strip> symbol file failed : %s", errbuf.c_str());
8235 return false;
8236 }
8237 }
8239 cmd = command;
8240 cmd.append(getNativePath(fullName));
8241 if (!executeCommand(cmd, "", outbuf, errbuf))
8242 {
8243 error("<strip> failed : %s", errbuf.c_str());
8244 return false;
8245 }
8246 return true;
8247 }
8249 virtual bool parse(Element *elem)
8250 {
8251 if (!parent.getAttribute(elem, "command", commandOpt))
8252 return false;
8253 if (!parent.getAttribute(elem, "file", fileNameOpt))
8254 return false;
8255 if (!parent.getAttribute(elem, "symfile", symFileNameOpt))
8256 return false;
8257 if (fileNameOpt.size() == 0)
8258 {
8259 error("<strip> requires 'file=\"fileName\"' attribute");
8260 return false;
8261 }
8262 return true;
8263 }
8265 private:
8267 String commandOpt;
8268 String fileNameOpt;
8269 String symFileNameOpt;
8270 };
8273 /**
8274 *
8275 */
8276 class TaskTouch : public Task
8277 {
8278 public:
8280 TaskTouch(MakeBase &par) : Task(par)
8281 { type = TASK_TOUCH; name = "touch"; }
8283 virtual ~TaskTouch()
8284 {}
8286 virtual bool execute()
8287 {
8288 String fileName = parent.eval(fileNameOpt, "");
8290 String fullName = parent.resolve(fileName);
8291 String nativeFile = getNativePath(fullName);
8292 if (!isRegularFile(fullName) && !isDirectory(fullName))
8293 {
8294 // S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH
8295 int ret = creat(nativeFile.c_str(), 0666);
8296 if (ret != 0)
8297 {
8298 error("<touch> could not create '%s' : %s",
8299 nativeFile.c_str(), strerror(ret));
8300 return false;
8301 }
8302 return true;
8303 }
8304 int ret = utime(nativeFile.c_str(), (struct utimbuf *)0);
8305 if (ret != 0)
8306 {
8307 error("<touch> could not update the modification time for '%s' : %s",
8308 nativeFile.c_str(), strerror(ret));
8309 return false;
8310 }
8311 return true;
8312 }
8314 virtual bool parse(Element *elem)
8315 {
8316 //trace("touch parse");
8317 if (!parent.getAttribute(elem, "file", fileNameOpt))
8318 return false;
8319 if (fileNameOpt.size() == 0)
8320 {
8321 error("<touch> requires 'file=\"fileName\"' attribute");
8322 return false;
8323 }
8324 return true;
8325 }
8327 String fileNameOpt;
8328 };
8331 /**
8332 *
8333 */
8334 class TaskTstamp : public Task
8335 {
8336 public:
8338 TaskTstamp(MakeBase &par) : Task(par)
8339 { type = TASK_TSTAMP; name = "tstamp"; }
8341 virtual ~TaskTstamp()
8342 {}
8344 virtual bool execute()
8345 {
8346 return true;
8347 }
8349 virtual bool parse(Element *elem)
8350 {
8351 //trace("tstamp parse");
8352 return true;
8353 }
8354 };
8358 /**
8359 *
8360 */
8361 Task *Task::createTask(Element *elem, int lineNr)
8362 {
8363 String tagName = elem->getName();
8364 //trace("task:%s", tagName.c_str());
8365 Task *task = NULL;
8366 if (tagName == "cc")
8367 task = new TaskCC(parent);
8368 else if (tagName == "copy")
8369 task = new TaskCopy(parent);
8370 else if (tagName == "delete")
8371 task = new TaskDelete(parent);
8372 else if (tagName == "echo")
8373 task = new TaskEcho(parent);
8374 else if (tagName == "jar")
8375 task = new TaskJar(parent);
8376 else if (tagName == "javac")
8377 task = new TaskJavac(parent);
8378 else if (tagName == "link")
8379 task = new TaskLink(parent);
8380 else if (tagName == "makefile")
8381 task = new TaskMakeFile(parent);
8382 else if (tagName == "mkdir")
8383 task = new TaskMkDir(parent);
8384 else if (tagName == "msgfmt")
8385 task = new TaskMsgFmt(parent);
8386 else if (tagName == "pkg-config")
8387 task = new TaskPkgConfig(parent);
8388 else if (tagName == "ranlib")
8389 task = new TaskRanlib(parent);
8390 else if (tagName == "rc")
8391 task = new TaskRC(parent);
8392 else if (tagName == "sharedlib")
8393 task = new TaskSharedLib(parent);
8394 else if (tagName == "staticlib")
8395 task = new TaskStaticLib(parent);
8396 else if (tagName == "strip")
8397 task = new TaskStrip(parent);
8398 else if (tagName == "touch")
8399 task = new TaskTouch(parent);
8400 else if (tagName == "tstamp")
8401 task = new TaskTstamp(parent);
8402 else
8403 {
8404 error("Unknown task '%s'", tagName.c_str());
8405 return NULL;
8406 }
8408 task->setLine(lineNr);
8410 if (!task->parse(elem))
8411 {
8412 delete task;
8413 return NULL;
8414 }
8415 return task;
8416 }
8420 //########################################################################
8421 //# T A R G E T
8422 //########################################################################
8424 /**
8425 *
8426 */
8427 class Target : public MakeBase
8428 {
8430 public:
8432 /**
8433 *
8434 */
8435 Target(Make &par) : parent(par)
8436 { init(); }
8438 /**
8439 *
8440 */
8441 Target(const Target &other) : parent(other.parent)
8442 { init(); assign(other); }
8444 /**
8445 *
8446 */
8447 Target &operator=(const Target &other)
8448 { init(); assign(other); return *this; }
8450 /**
8451 *
8452 */
8453 virtual ~Target()
8454 { cleanup() ; }
8457 /**
8458 *
8459 */
8460 virtual Make &getParent()
8461 { return parent; }
8463 /**
8464 *
8465 */
8466 virtual String getName()
8467 { return name; }
8469 /**
8470 *
8471 */
8472 virtual void setName(const String &val)
8473 { name = val; }
8475 /**
8476 *
8477 */
8478 virtual String getDescription()
8479 { return description; }
8481 /**
8482 *
8483 */
8484 virtual void setDescription(const String &val)
8485 { description = val; }
8487 /**
8488 *
8489 */
8490 virtual void addDependency(const String &val)
8491 { deps.push_back(val); }
8493 /**
8494 *
8495 */
8496 virtual void parseDependencies(const String &val)
8497 { deps = tokenize(val, ", "); }
8499 /**
8500 *
8501 */
8502 virtual std::vector<String> &getDependencies()
8503 { return deps; }
8505 /**
8506 *
8507 */
8508 virtual String getIf()
8509 { return ifVar; }
8511 /**
8512 *
8513 */
8514 virtual void setIf(const String &val)
8515 { ifVar = val; }
8517 /**
8518 *
8519 */
8520 virtual String getUnless()
8521 { return unlessVar; }
8523 /**
8524 *
8525 */
8526 virtual void setUnless(const String &val)
8527 { unlessVar = val; }
8529 /**
8530 *
8531 */
8532 virtual void addTask(Task *val)
8533 { tasks.push_back(val); }
8535 /**
8536 *
8537 */
8538 virtual std::vector<Task *> &getTasks()
8539 { return tasks; }
8541 private:
8543 void init()
8544 {
8545 }
8547 void cleanup()
8548 {
8549 tasks.clear();
8550 }
8552 void assign(const Target &other)
8553 {
8554 //parent = other.parent;
8555 name = other.name;
8556 description = other.description;
8557 ifVar = other.ifVar;
8558 unlessVar = other.unlessVar;
8559 deps = other.deps;
8560 tasks = other.tasks;
8561 }
8563 Make &parent;
8565 String name;
8567 String description;
8569 String ifVar;
8571 String unlessVar;
8573 std::vector<String> deps;
8575 std::vector<Task *> tasks;
8577 };
8586 //########################################################################
8587 //# M A K E
8588 //########################################################################
8591 /**
8592 *
8593 */
8594 class Make : public MakeBase
8595 {
8597 public:
8599 /**
8600 *
8601 */
8602 Make()
8603 { init(); }
8605 /**
8606 *
8607 */
8608 Make(const Make &other)
8609 { assign(other); }
8611 /**
8612 *
8613 */
8614 Make &operator=(const Make &other)
8615 { assign(other); return *this; }
8617 /**
8618 *
8619 */
8620 virtual ~Make()
8621 { cleanup(); }
8623 /**
8624 *
8625 */
8626 virtual std::map<String, Target> &getTargets()
8627 { return targets; }
8630 /**
8631 *
8632 */
8633 virtual String version()
8634 { return BUILDTOOL_VERSION; }
8636 /**
8637 * Overload a <property>
8638 */
8639 virtual bool specifyProperty(const String &name,
8640 const String &value);
8642 /**
8643 *
8644 */
8645 virtual bool run();
8647 /**
8648 *
8649 */
8650 virtual bool run(const String &target);
8654 private:
8656 /**
8657 *
8658 */
8659 void init();
8661 /**
8662 *
8663 */
8664 void cleanup();
8666 /**
8667 *
8668 */
8669 void assign(const Make &other);
8671 /**
8672 *
8673 */
8674 bool executeTask(Task &task);
8677 /**
8678 *
8679 */
8680 bool executeTarget(Target &target,
8681 std::set<String> &targetsCompleted);
8684 /**
8685 *
8686 */
8687 bool execute();
8689 /**
8690 *
8691 */
8692 bool checkTargetDependencies(Target &prop,
8693 std::vector<String> &depList);
8695 /**
8696 *
8697 */
8698 bool parsePropertyFile(const String &fileName,
8699 const String &prefix);
8701 /**
8702 *
8703 */
8704 bool parseProperty(Element *elem);
8706 /**
8707 *
8708 */
8709 bool parseFile();
8711 /**
8712 *
8713 */
8714 std::vector<String> glob(const String &pattern);
8717 //###############
8718 //# Fields
8719 //###############
8721 String projectName;
8723 String currentTarget;
8725 String defaultTarget;
8727 String specifiedTarget;
8729 String baseDir;
8731 String description;
8733 //std::vector<Property> properties;
8735 std::map<String, Target> targets;
8737 std::vector<Task *> allTasks;
8739 std::map<String, String> specifiedProperties;
8741 };
8744 //########################################################################
8745 //# C L A S S M A I N T E N A N C E
8746 //########################################################################
8748 /**
8749 *
8750 */
8751 void Make::init()
8752 {
8753 uri = "build.xml";
8754 projectName = "";
8755 currentTarget = "";
8756 defaultTarget = "";
8757 specifiedTarget = "";
8758 baseDir = "";
8759 description = "";
8760 envPrefix = "env.";
8761 pcPrefix = "pc.";
8762 pccPrefix = "pcc.";
8763 pclPrefix = "pcl.";
8764 properties.clear();
8765 for (unsigned int i = 0 ; i < allTasks.size() ; i++)
8766 delete allTasks[i];
8767 allTasks.clear();
8768 }
8772 /**
8773 *
8774 */
8775 void Make::cleanup()
8776 {
8777 for (unsigned int i = 0 ; i < allTasks.size() ; i++)
8778 delete allTasks[i];
8779 allTasks.clear();
8780 }
8784 /**
8785 *
8786 */
8787 void Make::assign(const Make &other)
8788 {
8789 uri = other.uri;
8790 projectName = other.projectName;
8791 currentTarget = other.currentTarget;
8792 defaultTarget = other.defaultTarget;
8793 specifiedTarget = other.specifiedTarget;
8794 baseDir = other.baseDir;
8795 description = other.description;
8796 properties = other.properties;
8797 }
8801 //########################################################################
8802 //# U T I L I T Y T A S K S
8803 //########################################################################
8805 /**
8806 * Perform a file globbing
8807 */
8808 std::vector<String> Make::glob(const String &pattern)
8809 {
8810 std::vector<String> res;
8811 return res;
8812 }
8815 //########################################################################
8816 //# P U B L I C A P I
8817 //########################################################################
8821 /**
8822 *
8823 */
8824 bool Make::executeTarget(Target &target,
8825 std::set<String> &targetsCompleted)
8826 {
8828 String name = target.getName();
8830 //First get any dependencies for this target
8831 std::vector<String> deps = target.getDependencies();
8832 for (unsigned int i=0 ; i<deps.size() ; i++)
8833 {
8834 String dep = deps[i];
8835 //Did we do it already? Skip
8836 if (targetsCompleted.find(dep)!=targetsCompleted.end())
8837 continue;
8839 std::map<String, Target> &tgts =
8840 target.getParent().getTargets();
8841 std::map<String, Target>::iterator iter =
8842 tgts.find(dep);
8843 if (iter == tgts.end())
8844 {
8845 error("Target '%s' dependency '%s' not found",
8846 name.c_str(), dep.c_str());
8847 return false;
8848 }
8849 Target depTarget = iter->second;
8850 if (!executeTarget(depTarget, targetsCompleted))
8851 {
8852 return false;
8853 }
8854 }
8856 status("##### Target : %s\n##### %s", name.c_str(),
8857 target.getDescription().c_str());
8859 //Now let's do the tasks
8860 std::vector<Task *> &tasks = target.getTasks();
8861 for (unsigned int i=0 ; i<tasks.size() ; i++)
8862 {
8863 Task *task = tasks[i];
8864 status("--- %s / %s", name.c_str(), task->getName().c_str());
8865 if (!task->execute())
8866 {
8867 return false;
8868 }
8869 }
8871 targetsCompleted.insert(name);
8873 return true;
8874 }
8878 /**
8879 * Main execute() method. Start here and work
8880 * up the dependency tree
8881 */
8882 bool Make::execute()
8883 {
8884 status("######## EXECUTE");
8886 //Determine initial target
8887 if (specifiedTarget.size()>0)
8888 {
8889 currentTarget = specifiedTarget;
8890 }
8891 else if (defaultTarget.size()>0)
8892 {
8893 currentTarget = defaultTarget;
8894 }
8895 else
8896 {
8897 error("execute: no specified or default target requested");
8898 return false;
8899 }
8901 std::map<String, Target>::iterator iter =
8902 targets.find(currentTarget);
8903 if (iter == targets.end())
8904 {
8905 error("Initial target '%s' not found",
8906 currentTarget.c_str());
8907 return false;
8908 }
8910 //Now run
8911 Target target = iter->second;
8912 std::set<String> targetsCompleted;
8913 if (!executeTarget(target, targetsCompleted))
8914 {
8915 return false;
8916 }
8918 status("######## EXECUTE COMPLETE");
8919 return true;
8920 }
8925 /**
8926 *
8927 */
8928 bool Make::checkTargetDependencies(Target &target,
8929 std::vector<String> &depList)
8930 {
8931 String tgtName = target.getName().c_str();
8932 depList.push_back(tgtName);
8934 std::vector<String> deps = target.getDependencies();
8935 for (unsigned int i=0 ; i<deps.size() ; i++)
8936 {
8937 String dep = deps[i];
8938 //First thing entered was the starting Target
8939 if (dep == depList[0])
8940 {
8941 error("Circular dependency '%s' found at '%s'",
8942 dep.c_str(), tgtName.c_str());
8943 std::vector<String>::iterator diter;
8944 for (diter=depList.begin() ; diter!=depList.end() ; diter++)
8945 {
8946 error(" %s", diter->c_str());
8947 }
8948 return false;
8949 }
8951 std::map<String, Target> &tgts =
8952 target.getParent().getTargets();
8953 std::map<String, Target>::iterator titer = tgts.find(dep);
8954 if (titer == tgts.end())
8955 {
8956 error("Target '%s' dependency '%s' not found",
8957 tgtName.c_str(), dep.c_str());
8958 return false;
8959 }
8960 if (!checkTargetDependencies(titer->second, depList))
8961 {
8962 return false;
8963 }
8964 }
8965 return true;
8966 }
8972 static int getword(int pos, const String &inbuf, String &result)
8973 {
8974 int p = pos;
8975 int len = (int)inbuf.size();
8976 String val;
8977 while (p < len)
8978 {
8979 char ch = inbuf[p];
8980 if (!isalnum(ch) && ch!='.' && ch!='_')
8981 break;
8982 val.push_back(ch);
8983 p++;
8984 }
8985 result = val;
8986 return p;
8987 }
8992 /**
8993 *
8994 */
8995 bool Make::parsePropertyFile(const String &fileName,
8996 const String &prefix)
8997 {
8998 FILE *f = fopen(fileName.c_str(), "r");
8999 if (!f)
9000 {
9001 error("could not open property file %s", fileName.c_str());
9002 return false;
9003 }
9004 int linenr = 0;
9005 while (!feof(f))
9006 {
9007 char buf[256];
9008 if (!fgets(buf, 255, f))
9009 break;
9010 linenr++;
9011 String s = buf;
9012 s = trim(s);
9013 int len = s.size();
9014 if (len == 0)
9015 continue;
9016 if (s[0] == '#')
9017 continue;
9018 String key;
9019 String val;
9020 int p = 0;
9021 int p2 = getword(p, s, key);
9022 if (p2 <= p)
9023 {
9024 error("property file %s, line %d: expected keyword",
9025 fileName.c_str(), linenr);
9026 return false;
9027 }
9028 if (prefix.size() > 0)
9029 {
9030 key.insert(0, prefix);
9031 }
9033 //skip whitespace
9034 for (p=p2 ; p<len ; p++)
9035 if (!isspace(s[p]))
9036 break;
9038 if (p>=len || s[p]!='=')
9039 {
9040 error("property file %s, line %d: expected '='",
9041 fileName.c_str(), linenr);
9042 return false;
9043 }
9044 p++;
9046 //skip whitespace
9047 for ( ; p<len ; p++)
9048 if (!isspace(s[p]))
9049 break;
9051 /* This way expects a word after the =
9052 p2 = getword(p, s, val);
9053 if (p2 <= p)
9054 {
9055 error("property file %s, line %d: expected value",
9056 fileName.c_str(), linenr);
9057 return false;
9058 }
9059 */
9060 // This way gets the rest of the line after the =
9061 if (p>=len)
9062 {
9063 error("property file %s, line %d: expected value",
9064 fileName.c_str(), linenr);
9065 return false;
9066 }
9067 val = s.substr(p);
9068 if (key.size()==0)
9069 continue;
9070 //allow property to be set, even if val=""
9072 //trace("key:'%s' val:'%s'", key.c_str(), val.c_str());
9073 //See if we wanted to overload this property
9074 std::map<String, String>::iterator iter =
9075 specifiedProperties.find(key);
9076 if (iter!=specifiedProperties.end())
9077 {
9078 val = iter->second;
9079 status("overloading property '%s' = '%s'",
9080 key.c_str(), val.c_str());
9081 }
9082 properties[key] = val;
9083 }
9084 fclose(f);
9085 return true;
9086 }
9091 /**
9092 *
9093 */
9094 bool Make::parseProperty(Element *elem)
9095 {
9096 std::vector<Attribute> &attrs = elem->getAttributes();
9097 for (unsigned int i=0 ; i<attrs.size() ; i++)
9098 {
9099 String attrName = attrs[i].getName();
9100 String attrVal = attrs[i].getValue();
9102 if (attrName == "name")
9103 {
9104 String val;
9105 if (!getAttribute(elem, "value", val))
9106 return false;
9107 if (val.size() > 0)
9108 {
9109 properties[attrVal] = val;
9110 }
9111 else
9112 {
9113 if (!getAttribute(elem, "location", val))
9114 return false;
9115 //let the property exist, even if not defined
9116 properties[attrVal] = val;
9117 }
9118 //See if we wanted to overload this property
9119 std::map<String, String>::iterator iter =
9120 specifiedProperties.find(attrVal);
9121 if (iter != specifiedProperties.end())
9122 {
9123 val = iter->second;
9124 status("overloading property '%s' = '%s'",
9125 attrVal.c_str(), val.c_str());
9126 properties[attrVal] = val;
9127 }
9128 }
9129 else if (attrName == "file")
9130 {
9131 String prefix;
9132 if (!getAttribute(elem, "prefix", prefix))
9133 return false;
9134 if (prefix.size() > 0)
9135 {
9136 if (prefix[prefix.size()-1] != '.')
9137 prefix.push_back('.');
9138 }
9139 if (!parsePropertyFile(attrName, prefix))
9140 return false;
9141 }
9142 else if (attrName == "environment")
9143 {
9144 if (attrVal.find('.') != attrVal.npos)
9145 {
9146 error("environment prefix cannot have a '.' in it");
9147 return false;
9148 }
9149 envPrefix = attrVal;
9150 envPrefix.push_back('.');
9151 }
9152 else if (attrName == "pkg-config")
9153 {
9154 if (attrVal.find('.') != attrVal.npos)
9155 {
9156 error("pkg-config prefix cannot have a '.' in it");
9157 return false;
9158 }
9159 pcPrefix = attrVal;
9160 pcPrefix.push_back('.');
9161 }
9162 else if (attrName == "pkg-config-cflags")
9163 {
9164 if (attrVal.find('.') != attrVal.npos)
9165 {
9166 error("pkg-config-cflags prefix cannot have a '.' in it");
9167 return false;
9168 }
9169 pccPrefix = attrVal;
9170 pccPrefix.push_back('.');
9171 }
9172 else if (attrName == "pkg-config-libs")
9173 {
9174 if (attrVal.find('.') != attrVal.npos)
9175 {
9176 error("pkg-config-libs prefix cannot have a '.' in it");
9177 return false;
9178 }
9179 pclPrefix = attrVal;
9180 pclPrefix.push_back('.');
9181 }
9182 }
9184 return true;
9185 }
9190 /**
9191 *
9192 */
9193 bool Make::parseFile()
9194 {
9195 status("######## PARSE : %s", uri.getPath().c_str());
9197 setLine(0);
9199 Parser parser;
9200 Element *root = parser.parseFile(uri.getNativePath());
9201 if (!root)
9202 {
9203 error("Could not open %s for reading",
9204 uri.getNativePath().c_str());
9205 return false;
9206 }
9208 setLine(root->getLine());
9210 if (root->getChildren().size()==0 ||
9211 root->getChildren()[0]->getName()!="project")
9212 {
9213 error("Main xml element should be <project>");
9214 delete root;
9215 return false;
9216 }
9218 //########## Project attributes
9219 Element *project = root->getChildren()[0];
9220 String s = project->getAttribute("name");
9221 if (s.size() > 0)
9222 projectName = s;
9223 s = project->getAttribute("default");
9224 if (s.size() > 0)
9225 defaultTarget = s;
9226 s = project->getAttribute("basedir");
9227 if (s.size() > 0)
9228 baseDir = s;
9230 //######### PARSE MEMBERS
9231 std::vector<Element *> children = project->getChildren();
9232 for (unsigned int i=0 ; i<children.size() ; i++)
9233 {
9234 Element *elem = children[i];
9235 setLine(elem->getLine());
9236 String tagName = elem->getName();
9238 //########## DESCRIPTION
9239 if (tagName == "description")
9240 {
9241 description = parser.trim(elem->getValue());
9242 }
9244 //######### PROPERTY
9245 else if (tagName == "property")
9246 {
9247 if (!parseProperty(elem))
9248 return false;
9249 }
9251 //######### TARGET
9252 else if (tagName == "target")
9253 {
9254 String tname = elem->getAttribute("name");
9255 String tdesc = elem->getAttribute("description");
9256 String tdeps = elem->getAttribute("depends");
9257 String tif = elem->getAttribute("if");
9258 String tunless = elem->getAttribute("unless");
9259 Target target(*this);
9260 target.setName(tname);
9261 target.setDescription(tdesc);
9262 target.parseDependencies(tdeps);
9263 target.setIf(tif);
9264 target.setUnless(tunless);
9265 std::vector<Element *> telems = elem->getChildren();
9266 for (unsigned int i=0 ; i<telems.size() ; i++)
9267 {
9268 Element *telem = telems[i];
9269 Task breeder(*this);
9270 Task *task = breeder.createTask(telem, telem->getLine());
9271 if (!task)
9272 return false;
9273 allTasks.push_back(task);
9274 target.addTask(task);
9275 }
9277 //Check name
9278 if (tname.size() == 0)
9279 {
9280 error("no name for target");
9281 return false;
9282 }
9283 //Check for duplicate name
9284 if (targets.find(tname) != targets.end())
9285 {
9286 error("target '%s' already defined", tname.c_str());
9287 return false;
9288 }
9289 //more work than targets[tname]=target, but avoids default allocator
9290 targets.insert(std::make_pair<String, Target>(tname, target));
9291 }
9292 //######### none of the above
9293 else
9294 {
9295 error("unknown toplevel tag: <%s>", tagName.c_str());
9296 return false;
9297 }
9299 }
9301 std::map<String, Target>::iterator iter;
9302 for (iter = targets.begin() ; iter!= targets.end() ; iter++)
9303 {
9304 Target tgt = iter->second;
9305 std::vector<String> depList;
9306 if (!checkTargetDependencies(tgt, depList))
9307 {
9308 return false;
9309 }
9310 }
9313 delete root;
9314 status("######## PARSE COMPLETE");
9315 return true;
9316 }
9319 /**
9320 * Overload a <property>
9321 */
9322 bool Make::specifyProperty(const String &name, const String &value)
9323 {
9324 if (specifiedProperties.find(name) != specifiedProperties.end())
9325 {
9326 error("Property %s already specified", name.c_str());
9327 return false;
9328 }
9329 specifiedProperties[name] = value;
9330 return true;
9331 }
9335 /**
9336 *
9337 */
9338 bool Make::run()
9339 {
9340 if (!parseFile())
9341 return false;
9343 if (!execute())
9344 return false;
9346 return true;
9347 }
9352 /**
9353 * Get a formatted MM:SS.sss time elapsed string
9354 */
9355 static String
9356 timeDiffString(struct timeval &x, struct timeval &y)
9357 {
9358 long microsX = x.tv_usec;
9359 long secondsX = x.tv_sec;
9360 long microsY = y.tv_usec;
9361 long secondsY = y.tv_sec;
9362 if (microsX < microsY)
9363 {
9364 microsX += 1000000;
9365 secondsX -= 1;
9366 }
9368 int seconds = (int)(secondsX - secondsY);
9369 int millis = (int)((microsX - microsY)/1000);
9371 int minutes = seconds/60;
9372 seconds -= minutes*60;
9373 char buf[80];
9374 snprintf(buf, 79, "%dm %d.%03ds", minutes, seconds, millis);
9375 String ret = buf;
9376 return ret;
9378 }
9380 /**
9381 *
9382 */
9383 bool Make::run(const String &target)
9384 {
9385 status("####################################################");
9386 status("# %s", version().c_str());
9387 status("####################################################");
9388 struct timeval timeStart, timeEnd;
9389 ::gettimeofday(&timeStart, NULL);
9390 specifiedTarget = target;
9391 if (!run())
9392 return false;
9393 ::gettimeofday(&timeEnd, NULL);
9394 String timeStr = timeDiffString(timeEnd, timeStart);
9395 status("####################################################");
9396 status("# BuildTool Completed : %s", timeStr.c_str());
9397 status("####################################################");
9398 return true;
9399 }
9407 }// namespace buildtool
9408 //########################################################################
9409 //# M A I N
9410 //########################################################################
9412 typedef buildtool::String String;
9414 /**
9415 * Format an error message in printf() style
9416 */
9417 static void error(const char *fmt, ...)
9418 {
9419 va_list ap;
9420 va_start(ap, fmt);
9421 fprintf(stderr, "BuildTool error: ");
9422 vfprintf(stderr, fmt, ap);
9423 fprintf(stderr, "\n");
9424 va_end(ap);
9425 }
9428 static bool parseProperty(const String &s, String &name, String &val)
9429 {
9430 int len = s.size();
9431 int i;
9432 for (i=0 ; i<len ; i++)
9433 {
9434 char ch = s[i];
9435 if (ch == '=')
9436 break;
9437 name.push_back(ch);
9438 }
9439 if (i>=len || s[i]!='=')
9440 {
9441 error("property requires -Dname=value");
9442 return false;
9443 }
9444 i++;
9445 for ( ; i<len ; i++)
9446 {
9447 char ch = s[i];
9448 val.push_back(ch);
9449 }
9450 return true;
9451 }
9454 /**
9455 * Compare a buffer with a key, for the length of the key
9456 */
9457 static bool sequ(const String &buf, const char *key)
9458 {
9459 int len = buf.size();
9460 for (int i=0 ; key[i] && i<len ; i++)
9461 {
9462 if (key[i] != buf[i])
9463 return false;
9464 }
9465 return true;
9466 }
9468 static void usage(int argc, char **argv)
9469 {
9470 printf("usage:\n");
9471 printf(" %s [options] [target]\n", argv[0]);
9472 printf("Options:\n");
9473 printf(" -help, -h print this message\n");
9474 printf(" -version print the version information and exit\n");
9475 printf(" -file <file> use given buildfile\n");
9476 printf(" -f <file> ''\n");
9477 printf(" -D<property>=<value> use value for given property\n");
9478 }
9483 /**
9484 * Parse the command-line args, get our options,
9485 * and run this thing
9486 */
9487 static bool parseOptions(int argc, char **argv)
9488 {
9489 if (argc < 1)
9490 {
9491 error("Cannot parse arguments");
9492 return false;
9493 }
9495 buildtool::Make make;
9497 String target;
9499 //char *progName = argv[0];
9500 for (int i=1 ; i<argc ; i++)
9501 {
9502 String arg = argv[i];
9503 if (arg.size()>1 && arg[0]=='-')
9504 {
9505 if (arg == "-h" || arg == "-help")
9506 {
9507 usage(argc,argv);
9508 return true;
9509 }
9510 else if (arg == "-version")
9511 {
9512 printf("%s", make.version().c_str());
9513 return true;
9514 }
9515 else if (arg == "-f" || arg == "-file")
9516 {
9517 if (i>=argc)
9518 {
9519 usage(argc, argv);
9520 return false;
9521 }
9522 i++; //eat option
9523 make.setURI(argv[i]);
9524 }
9525 else if (arg.size()>2 && sequ(arg, "-D"))
9526 {
9527 String s = arg.substr(2, arg.size());
9528 String name, value;
9529 if (!parseProperty(s, name, value))
9530 {
9531 usage(argc, argv);
9532 return false;
9533 }
9534 if (!make.specifyProperty(name, value))
9535 return false;
9536 }
9537 else
9538 {
9539 error("Unknown option:%s", arg.c_str());
9540 return false;
9541 }
9542 }
9543 else
9544 {
9545 if (target.size()>0)
9546 {
9547 error("only one initial target");
9548 usage(argc, argv);
9549 return false;
9550 }
9551 target = arg;
9552 }
9553 }
9555 //We have the options. Now execute them
9556 if (!make.run(target))
9557 return false;
9559 return true;
9560 }
9565 /*
9566 static bool runMake()
9567 {
9568 buildtool::Make make;
9569 if (!make.run())
9570 return false;
9571 return true;
9572 }
9575 static bool pkgConfigTest()
9576 {
9577 buildtool::PkgConfig pkgConfig;
9578 if (!pkgConfig.readFile("gtk+-2.0.pc"))
9579 return false;
9580 return true;
9581 }
9585 static bool depTest()
9586 {
9587 buildtool::DepTool deptool;
9588 deptool.setSourceDirectory("/dev/ink/inkscape/src");
9589 if (!deptool.generateDependencies("build.dep"))
9590 return false;
9591 std::vector<buildtool::FileRec> res =
9592 deptool.loadDepFile("build.dep");
9593 if (res.size() == 0)
9594 return false;
9595 return true;
9596 }
9598 static bool popenTest()
9599 {
9600 buildtool::Make make;
9601 buildtool::String out, err;
9602 bool ret = make.executeCommand("gcc xx.cpp", "", out, err);
9603 printf("Popen test:%d '%s' '%s'\n", ret, out.c_str(), err.c_str());
9604 return true;
9605 }
9608 static bool propFileTest()
9609 {
9610 buildtool::Make make;
9611 make.parsePropertyFile("test.prop", "test.");
9612 return true;
9613 }
9614 */
9616 int main(int argc, char **argv)
9617 {
9619 if (!parseOptions(argc, argv))
9620 return 1;
9621 /*
9622 if (!popenTest())
9623 return 1;
9625 if (!depTest())
9626 return 1;
9627 if (!propFileTest())
9628 return 1;
9629 if (runMake())
9630 return 1;
9631 */
9632 return 0;
9633 }
9636 //########################################################################
9637 //# E N D
9638 //########################################################################