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.1"
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 resilution
4394 */
4395 bool MakeBase::getSubstitutionsRecursive(const String &str,
4396 String &result, int depth)
4397 {
4398 if (depth > 4)
4399 {
4400 error("getSubstitutions: nesting of substitutions too deep");
4401 return false;
4402 }
4403 String s = trim(str);
4404 int len = (int)s.size();
4405 String val;
4406 for (int i=0 ; i<len ; i++)
4407 {
4408 char ch = s[i];
4409 if (ch == '$' && s[i+1] == '{')
4410 {
4411 String varname;
4412 int j = i+2;
4413 for ( ; j<len ; j++)
4414 {
4415 ch = s[j];
4416 if (ch == '$' && s[j+1] == '{')
4417 {
4418 error("attribute %s cannot have nested variable references",
4419 s.c_str());
4420 return false;
4421 }
4422 else if (ch == '}')
4423 {
4424 varname = trim(varname);
4425 String varval;
4426 if (!lookupProperty(varname, varval))
4427 return false;
4428 String varval2;
4429 //Now see if the answer has ${} in it, too
4430 if (!getSubstitutionsRecursive(varval, varval2, depth + 1))
4431 return false;
4432 val.append(varval2);
4433 break;
4434 }
4435 else
4436 {
4437 varname.push_back(ch);
4438 }
4439 }
4440 i = j;
4441 }
4442 else
4443 {
4444 val.push_back(ch);
4445 }
4446 }
4447 result = val;
4448 return true;
4449 }
4451 /**
4452 * Analyse a string, looking for any substitutions or other
4453 * things that need resilution
4454 */
4455 bool MakeBase::getSubstitutions(const String &str, String &result)
4456 {
4457 return getSubstitutionsRecursive(str, result, 0);
4458 }
4462 /**
4463 * replace variable refs like ${a} with their values
4464 * Assume that the string has already been syntax validated
4465 */
4466 String MakeBase::eval(const String &s, const String &defaultVal)
4467 {
4468 if (s.size()==0)
4469 return defaultVal;
4470 String ret;
4471 if (getSubstitutions(s, ret))
4472 return ret;
4473 else
4474 return defaultVal;
4475 }
4478 /**
4479 * replace variable refs like ${a} with their values
4480 * return true or false
4481 * Assume that the string has already been syntax validated
4482 */
4483 bool MakeBase::evalBool(const String &s, bool defaultVal)
4484 {
4485 if (s.size()==0)
4486 return defaultVal;
4487 String val = eval(s, "false");
4488 if (s == "true" || s == "TRUE")
4489 return true;
4490 else
4491 return defaultVal;
4492 }
4495 /**
4496 * Get a string attribute, testing it for proper syntax and
4497 * property names.
4498 */
4499 bool MakeBase::getAttribute(Element *elem, const String &name,
4500 String &result)
4501 {
4502 String s = elem->getAttribute(name);
4503 String tmp;
4504 bool ret = getSubstitutions(s, tmp);
4505 if (ret)
4506 result = s; //assign -if- ok
4507 return ret;
4508 }
4511 /**
4512 * Get a string value, testing it for proper syntax and
4513 * property names.
4514 */
4515 bool MakeBase::getValue(Element *elem, String &result)
4516 {
4517 String s = elem->getValue();
4518 String tmp;
4519 bool ret = getSubstitutions(s, tmp);
4520 if (ret)
4521 result = s; //assign -if- ok
4522 return ret;
4523 }
4528 /**
4529 * Parse a <patternset> entry
4530 */
4531 bool MakeBase::parsePatternSet(Element *elem,
4532 MakeBase &propRef,
4533 std::vector<String> &includes,
4534 std::vector<String> &excludes
4535 )
4536 {
4537 std::vector<Element *> children = elem->getChildren();
4538 for (unsigned int i=0 ; i<children.size() ; i++)
4539 {
4540 Element *child = children[i];
4541 String tagName = child->getName();
4542 if (tagName == "exclude")
4543 {
4544 String fname;
4545 if (!propRef.getAttribute(child, "name", fname))
4546 return false;
4547 //trace("EXCLUDE: %s", fname.c_str());
4548 excludes.push_back(fname);
4549 }
4550 else if (tagName == "include")
4551 {
4552 String fname;
4553 if (!propRef.getAttribute(child, "name", fname))
4554 return false;
4555 //trace("INCLUDE: %s", fname.c_str());
4556 includes.push_back(fname);
4557 }
4558 }
4560 return true;
4561 }
4566 /**
4567 * Parse a <fileset> entry, and determine which files
4568 * should be included
4569 */
4570 bool MakeBase::parseFileSet(Element *elem,
4571 MakeBase &propRef,
4572 FileSet &fileSet)
4573 {
4574 String name = elem->getName();
4575 if (name != "fileset")
4576 {
4577 error("expected <fileset>");
4578 return false;
4579 }
4582 std::vector<String> includes;
4583 std::vector<String> excludes;
4585 //A fileset has one implied patternset
4586 if (!parsePatternSet(elem, propRef, includes, excludes))
4587 {
4588 return false;
4589 }
4590 //Look for child tags, including more patternsets
4591 std::vector<Element *> children = elem->getChildren();
4592 for (unsigned int i=0 ; i<children.size() ; i++)
4593 {
4594 Element *child = children[i];
4595 String tagName = child->getName();
4596 if (tagName == "patternset")
4597 {
4598 if (!parsePatternSet(child, propRef, includes, excludes))
4599 {
4600 return false;
4601 }
4602 }
4603 }
4605 String dir;
4606 //Now do the stuff
4607 //Get the base directory for reading file names
4608 if (!propRef.getAttribute(elem, "dir", dir))
4609 return false;
4611 fileSet.setDirectory(dir);
4612 fileSet.setIncludes(includes);
4613 fileSet.setExcludes(excludes);
4615 /*
4616 std::vector<String> fileList;
4617 if (dir.size() > 0)
4618 {
4619 String baseDir = propRef.resolve(dir);
4620 if (!listFiles(baseDir, "", includes, excludes, fileList))
4621 return false;
4622 }
4623 std::sort(fileList.begin(), fileList.end());
4624 result = fileList;
4625 */
4628 /*
4629 for (unsigned int i=0 ; i<result.size() ; i++)
4630 {
4631 trace("RES:%s", result[i].c_str());
4632 }
4633 */
4636 return true;
4637 }
4639 /**
4640 * Parse a <filelist> entry. This is far simpler than FileSet,
4641 * since no directory scanning is needed. The file names are listed
4642 * explicitly.
4643 */
4644 bool MakeBase::parseFileList(Element *elem,
4645 MakeBase &propRef,
4646 FileList &fileList)
4647 {
4648 std::vector<String> fnames;
4649 //Look for child tags, namely "file"
4650 std::vector<Element *> children = elem->getChildren();
4651 for (unsigned int i=0 ; i<children.size() ; i++)
4652 {
4653 Element *child = children[i];
4654 String tagName = child->getName();
4655 if (tagName == "file")
4656 {
4657 String fname = child->getAttribute("name");
4658 if (fname.size()==0)
4659 {
4660 error("<file> element requires name="" attribute");
4661 return false;
4662 }
4663 fnames.push_back(fname);
4664 }
4665 else
4666 {
4667 error("tag <%s> not allowed in <fileset>", tagName.c_str());
4668 return false;
4669 }
4670 }
4672 String dir;
4673 //Get the base directory for reading file names
4674 if (!propRef.getAttribute(elem, "dir", dir))
4675 return false;
4676 fileList.setDirectory(dir);
4677 fileList.setFiles(fnames);
4679 return true;
4680 }
4684 /**
4685 * Create a directory, making intermediate dirs
4686 * if necessary
4687 */
4688 bool MakeBase::createDirectory(const String &dirname)
4689 {
4690 //trace("## createDirectory: %s", dirname.c_str());
4691 //## first check if it exists
4692 struct stat finfo;
4693 String nativeDir = getNativePath(dirname);
4694 char *cnative = (char *) nativeDir.c_str();
4695 #ifdef __WIN32__
4696 if (strlen(cnative)==2 && cnative[1]==':')
4697 return true;
4698 #endif
4699 if (stat(cnative, &finfo)==0)
4700 {
4701 if (!S_ISDIR(finfo.st_mode))
4702 {
4703 error("mkdir: file %s exists but is not a directory",
4704 cnative);
4705 return false;
4706 }
4707 else //exists
4708 {
4709 return true;
4710 }
4711 }
4713 //## 2: pull off the last path segment, if any,
4714 //## to make the dir 'above' this one, if necessary
4715 unsigned int pos = dirname.find_last_of('/');
4716 if (pos>0 && pos != dirname.npos)
4717 {
4718 String subpath = dirname.substr(0, pos);
4719 //A letter root (c:) ?
4720 if (!createDirectory(subpath))
4721 return false;
4722 }
4724 //## 3: now make
4725 #ifdef __WIN32__
4726 if (mkdir(cnative)<0)
4727 #else
4728 if (mkdir(cnative, S_IRWXU | S_IRWXG | S_IRWXO)<0)
4729 #endif
4730 {
4731 error("cannot make directory '%s' : %s",
4732 cnative, strerror(errno));
4733 return false;
4734 }
4736 return true;
4737 }
4740 /**
4741 * Remove a directory recursively
4742 */
4743 bool MakeBase::removeDirectory(const String &dirName)
4744 {
4745 char *dname = (char *)dirName.c_str();
4747 DIR *dir = opendir(dname);
4748 if (!dir)
4749 {
4750 //# Let this fail nicely.
4751 return true;
4752 //error("error opening directory %s : %s", dname, strerror(errno));
4753 //return false;
4754 }
4756 while (true)
4757 {
4758 struct dirent *de = readdir(dir);
4759 if (!de)
4760 break;
4762 //Get the directory member name
4763 String s = de->d_name;
4764 if (s.size() == 0 || s[0] == '.')
4765 continue;
4766 String childName;
4767 if (dirName.size() > 0)
4768 {
4769 childName.append(dirName);
4770 childName.append("/");
4771 }
4772 childName.append(s);
4775 struct stat finfo;
4776 String childNative = getNativePath(childName);
4777 char *cnative = (char *)childNative.c_str();
4778 if (stat(cnative, &finfo)<0)
4779 {
4780 error("cannot stat file:%s", cnative);
4781 }
4782 else if (S_ISDIR(finfo.st_mode))
4783 {
4784 //trace("DEL dir: %s", childName.c_str());
4785 if (!removeDirectory(childName))
4786 {
4787 return false;
4788 }
4789 }
4790 else if (!S_ISREG(finfo.st_mode))
4791 {
4792 //trace("not regular: %s", cnative);
4793 }
4794 else
4795 {
4796 //trace("DEL file: %s", childName.c_str());
4797 if (remove(cnative)<0)
4798 {
4799 error("error deleting %s : %s",
4800 cnative, strerror(errno));
4801 return false;
4802 }
4803 }
4804 }
4805 closedir(dir);
4807 //Now delete the directory
4808 String native = getNativePath(dirName);
4809 if (rmdir(native.c_str())<0)
4810 {
4811 error("could not delete directory %s : %s",
4812 native.c_str() , strerror(errno));
4813 return false;
4814 }
4816 return true;
4818 }
4821 /**
4822 * Copy a file from one name to another. Perform only if needed
4823 */
4824 bool MakeBase::copyFile(const String &srcFile, const String &destFile)
4825 {
4826 //# 1 Check up-to-date times
4827 String srcNative = getNativePath(srcFile);
4828 struct stat srcinfo;
4829 if (stat(srcNative.c_str(), &srcinfo)<0)
4830 {
4831 error("source file %s for copy does not exist",
4832 srcNative.c_str());
4833 return false;
4834 }
4836 String destNative = getNativePath(destFile);
4837 struct stat destinfo;
4838 if (stat(destNative.c_str(), &destinfo)==0)
4839 {
4840 if (destinfo.st_mtime >= srcinfo.st_mtime)
4841 return true;
4842 }
4844 //# 2 prepare a destination directory if necessary
4845 unsigned int pos = destFile.find_last_of('/');
4846 if (pos != destFile.npos)
4847 {
4848 String subpath = destFile.substr(0, pos);
4849 if (!createDirectory(subpath))
4850 return false;
4851 }
4853 //# 3 do the data copy
4854 #ifndef __WIN32__
4856 FILE *srcf = fopen(srcNative.c_str(), "rb");
4857 if (!srcf)
4858 {
4859 error("copyFile cannot open '%s' for reading", srcNative.c_str());
4860 return false;
4861 }
4862 FILE *destf = fopen(destNative.c_str(), "wb");
4863 if (!destf)
4864 {
4865 error("copyFile cannot open %s for writing", srcNative.c_str());
4866 return false;
4867 }
4869 while (!feof(srcf))
4870 {
4871 int ch = fgetc(srcf);
4872 if (ch<0)
4873 break;
4874 fputc(ch, destf);
4875 }
4877 fclose(destf);
4878 fclose(srcf);
4880 #else
4882 if (!CopyFile(srcNative.c_str(), destNative.c_str(), false))
4883 {
4884 error("copyFile from %s to %s failed",
4885 srcNative.c_str(), destNative.c_str());
4886 return false;
4887 }
4889 #endif /* __WIN32__ */
4892 return true;
4893 }
4897 /**
4898 * Tests if the file exists and is a regular file
4899 */
4900 bool MakeBase::isRegularFile(const String &fileName)
4901 {
4902 String native = getNativePath(fileName);
4903 struct stat finfo;
4905 //Exists?
4906 if (stat(native.c_str(), &finfo)<0)
4907 return false;
4910 //check the file mode
4911 if (!S_ISREG(finfo.st_mode))
4912 return false;
4914 return true;
4915 }
4917 /**
4918 * Tests if the file exists and is a directory
4919 */
4920 bool MakeBase::isDirectory(const String &fileName)
4921 {
4922 String native = getNativePath(fileName);
4923 struct stat finfo;
4925 //Exists?
4926 if (stat(native.c_str(), &finfo)<0)
4927 return false;
4930 //check the file mode
4931 if (!S_ISDIR(finfo.st_mode))
4932 return false;
4934 return true;
4935 }
4939 /**
4940 * Tests is the modification of fileA is newer than fileB
4941 */
4942 bool MakeBase::isNewerThan(const String &fileA, const String &fileB)
4943 {
4944 //trace("isNewerThan:'%s' , '%s'", fileA.c_str(), fileB.c_str());
4945 String nativeA = getNativePath(fileA);
4946 struct stat infoA;
4947 //IF source does not exist, NOT newer
4948 if (stat(nativeA.c_str(), &infoA)<0)
4949 {
4950 return false;
4951 }
4953 String nativeB = getNativePath(fileB);
4954 struct stat infoB;
4955 //IF dest does not exist, YES, newer
4956 if (stat(nativeB.c_str(), &infoB)<0)
4957 {
4958 return true;
4959 }
4961 //check the actual times
4962 if (infoA.st_mtime > infoB.st_mtime)
4963 {
4964 return true;
4965 }
4967 return false;
4968 }
4971 //########################################################################
4972 //# P K G C O N F I G
4973 //########################################################################
4976 /**
4977 * Get a character from the buffer at pos. If out of range,
4978 * return -1 for safety
4979 */
4980 int PkgConfig::get(int pos)
4981 {
4982 if (pos>parselen)
4983 return -1;
4984 return parsebuf[pos];
4985 }
4989 /**
4990 * Skip over all whitespace characters beginning at pos. Return
4991 * the position of the first non-whitespace character.
4992 * Pkg-config is line-oriented, so check for newline
4993 */
4994 int PkgConfig::skipwhite(int pos)
4995 {
4996 while (pos < parselen)
4997 {
4998 int ch = get(pos);
4999 if (ch < 0)
5000 break;
5001 if (!isspace(ch))
5002 break;
5003 pos++;
5004 }
5005 return pos;
5006 }
5009 /**
5010 * Parse the buffer beginning at pos, for a word. Fill
5011 * 'ret' with the result. Return the position after the
5012 * word.
5013 */
5014 int PkgConfig::getword(int pos, String &ret)
5015 {
5016 while (pos < parselen)
5017 {
5018 int ch = get(pos);
5019 if (ch < 0)
5020 break;
5021 if (!isalnum(ch) && ch != '_' && ch != '-' && ch != '+' && ch != '.')
5022 break;
5023 ret.push_back((char)ch);
5024 pos++;
5025 }
5026 return pos;
5027 }
5029 bool PkgConfig::parseRequires()
5030 {
5031 if (requires.size() == 0)
5032 return true;
5033 parsebuf = (char *)requires.c_str();
5034 parselen = requires.size();
5035 int pos = 0;
5036 while (pos < parselen)
5037 {
5038 pos = skipwhite(pos);
5039 String val;
5040 int pos2 = getword(pos, val);
5041 if (pos2 == pos)
5042 break;
5043 pos = pos2;
5044 //trace("val %s", val.c_str());
5045 requireList.push_back(val);
5046 }
5047 return true;
5048 }
5051 static int getint(const String str)
5052 {
5053 char *s = (char *)str.c_str();
5054 char *ends = NULL;
5055 long val = strtol(s, &ends, 10);
5056 if (ends == s)
5057 return 0L;
5058 else
5059 return val;
5060 }
5062 void PkgConfig::parseVersion()
5063 {
5064 if (version.size() == 0)
5065 return;
5066 String s1, s2, s3;
5067 unsigned int pos = 0;
5068 unsigned int pos2 = version.find('.', pos);
5069 if (pos2 == version.npos)
5070 {
5071 s1 = version;
5072 }
5073 else
5074 {
5075 s1 = version.substr(pos, pos2-pos);
5076 pos = pos2;
5077 pos++;
5078 if (pos < version.size())
5079 {
5080 pos2 = version.find('.', pos);
5081 if (pos2 == version.npos)
5082 {
5083 s2 = version.substr(pos, version.size()-pos);
5084 }
5085 else
5086 {
5087 s2 = version.substr(pos, pos2-pos);
5088 pos = pos2;
5089 pos++;
5090 if (pos < version.size())
5091 s3 = version.substr(pos, pos2-pos);
5092 }
5093 }
5094 }
5096 majorVersion = getint(s1);
5097 minorVersion = getint(s2);
5098 microVersion = getint(s3);
5099 //trace("version:%d.%d.%d", majorVersion,
5100 // minorVersion, microVersion );
5101 }
5104 bool PkgConfig::parseLine(const String &lineBuf)
5105 {
5106 parsebuf = (char *)lineBuf.c_str();
5107 parselen = lineBuf.size();
5108 int pos = 0;
5110 while (pos < parselen)
5111 {
5112 String attrName;
5113 pos = skipwhite(pos);
5114 int ch = get(pos);
5115 if (ch == '#')
5116 {
5117 //comment. eat the rest of the line
5118 while (pos < parselen)
5119 {
5120 ch = get(pos);
5121 if (ch == '\n' || ch < 0)
5122 break;
5123 pos++;
5124 }
5125 continue;
5126 }
5127 pos = getword(pos, attrName);
5128 if (attrName.size() == 0)
5129 continue;
5131 pos = skipwhite(pos);
5132 ch = get(pos);
5133 if (ch != ':' && ch != '=')
5134 {
5135 error("expected ':' or '='");
5136 return false;
5137 }
5138 pos++;
5139 pos = skipwhite(pos);
5140 String attrVal;
5141 while (pos < parselen)
5142 {
5143 ch = get(pos);
5144 if (ch == '\n' || ch < 0)
5145 break;
5146 else if (ch == '$' && get(pos+1) == '{')
5147 {
5148 //# this is a ${substitution}
5149 pos += 2;
5150 String subName;
5151 while (pos < parselen)
5152 {
5153 ch = get(pos);
5154 if (ch < 0)
5155 {
5156 error("unterminated substitution");
5157 return false;
5158 }
5159 else if (ch == '}')
5160 break;
5161 else
5162 subName.push_back((char)ch);
5163 pos++;
5164 }
5165 //trace("subName:%s %s", subName.c_str(), prefix.c_str());
5166 if (subName == "prefix" && prefix.size()>0)
5167 {
5168 attrVal.append(prefix);
5169 //trace("prefix override:%s", prefix.c_str());
5170 }
5171 else
5172 {
5173 String subVal = attrs[subName];
5174 //trace("subVal:%s", subVal.c_str());
5175 attrVal.append(subVal);
5176 }
5177 }
5178 else
5179 attrVal.push_back((char)ch);
5180 pos++;
5181 }
5183 attrVal = trim(attrVal);
5184 attrs[attrName] = attrVal;
5186 String attrNameL = toLower(attrName);
5188 if (attrNameL == "name")
5189 name = attrVal;
5190 else if (attrNameL == "description")
5191 description = attrVal;
5192 else if (attrNameL == "cflags")
5193 cflags = attrVal;
5194 else if (attrNameL == "libs")
5195 libs = attrVal;
5196 else if (attrNameL == "requires")
5197 requires = attrVal;
5198 else if (attrNameL == "version")
5199 version = attrVal;
5201 //trace("name:'%s' value:'%s'",
5202 // attrName.c_str(), attrVal.c_str());
5203 }
5205 return true;
5206 }
5209 bool PkgConfig::parse(const String &buf)
5210 {
5211 init();
5213 String line;
5214 int lineNr = 0;
5215 for (unsigned int p=0 ; p<buf.size() ; p++)
5216 {
5217 int ch = buf[p];
5218 if (ch == '\n' || ch == '\r')
5219 {
5220 if (!parseLine(line))
5221 return false;
5222 line.clear();
5223 lineNr++;
5224 }
5225 else
5226 {
5227 line.push_back(ch);
5228 }
5229 }
5230 if (line.size()>0)
5231 {
5232 if (!parseLine(line))
5233 return false;
5234 }
5236 parseRequires();
5237 parseVersion();
5239 return true;
5240 }
5245 void PkgConfig::dumpAttrs()
5246 {
5247 //trace("### PkgConfig attributes for %s", fileName.c_str());
5248 std::map<String, String>::iterator iter;
5249 for (iter=attrs.begin() ; iter!=attrs.end() ; iter++)
5250 {
5251 trace(" %s = %s", iter->first.c_str(), iter->second.c_str());
5252 }
5253 }
5256 bool PkgConfig::readFile(const String &fname)
5257 {
5258 fileName = getNativePath(fname);
5260 FILE *f = fopen(fileName.c_str(), "r");
5261 if (!f)
5262 {
5263 error("cannot open file '%s' for reading", fileName.c_str());
5264 return false;
5265 }
5266 String buf;
5267 while (true)
5268 {
5269 int ch = fgetc(f);
5270 if (ch < 0)
5271 break;
5272 buf.push_back((char)ch);
5273 }
5274 fclose(f);
5276 //trace("####### File:\n%s", buf.c_str());
5277 if (!parse(buf))
5278 {
5279 return false;
5280 }
5282 //dumpAttrs();
5284 return true;
5285 }
5289 bool PkgConfig::query(const String &pkgName)
5290 {
5291 name = pkgName;
5293 String fname = path;
5294 fname.append("/");
5295 fname.append(name);
5296 fname.append(".pc");
5298 if (!readFile(fname))
5299 return false;
5301 return true;
5302 }
5308 //########################################################################
5309 //# D E P T O O L
5310 //########################################################################
5314 /**
5315 * Class which holds information for each file.
5316 */
5317 class FileRec
5318 {
5319 public:
5321 typedef enum
5322 {
5323 UNKNOWN,
5324 CFILE,
5325 HFILE,
5326 OFILE
5327 } FileType;
5329 /**
5330 * Constructor
5331 */
5332 FileRec()
5333 { init(); type = UNKNOWN; }
5335 /**
5336 * Copy constructor
5337 */
5338 FileRec(const FileRec &other)
5339 { init(); assign(other); }
5340 /**
5341 * Constructor
5342 */
5343 FileRec(int typeVal)
5344 { init(); type = typeVal; }
5345 /**
5346 * Assignment operator
5347 */
5348 FileRec &operator=(const FileRec &other)
5349 { init(); assign(other); return *this; }
5352 /**
5353 * Destructor
5354 */
5355 ~FileRec()
5356 {}
5358 /**
5359 * Directory part of the file name
5360 */
5361 String path;
5363 /**
5364 * Base name, sans directory and suffix
5365 */
5366 String baseName;
5368 /**
5369 * File extension, such as cpp or h
5370 */
5371 String suffix;
5373 /**
5374 * Type of file: CFILE, HFILE, OFILE
5375 */
5376 int type;
5378 /**
5379 * Used to list files ref'd by this one
5380 */
5381 std::map<String, FileRec *> files;
5384 private:
5386 void init()
5387 {
5388 }
5390 void assign(const FileRec &other)
5391 {
5392 type = other.type;
5393 baseName = other.baseName;
5394 suffix = other.suffix;
5395 files = other.files;
5396 }
5398 };
5402 /**
5403 * Simpler dependency record
5404 */
5405 class DepRec
5406 {
5407 public:
5409 /**
5410 * Constructor
5411 */
5412 DepRec()
5413 {init();}
5415 /**
5416 * Copy constructor
5417 */
5418 DepRec(const DepRec &other)
5419 {init(); assign(other);}
5420 /**
5421 * Constructor
5422 */
5423 DepRec(const String &fname)
5424 {init(); name = fname; }
5425 /**
5426 * Assignment operator
5427 */
5428 DepRec &operator=(const DepRec &other)
5429 {init(); assign(other); return *this;}
5432 /**
5433 * Destructor
5434 */
5435 ~DepRec()
5436 {}
5438 /**
5439 * Directory part of the file name
5440 */
5441 String path;
5443 /**
5444 * Base name, without the path and suffix
5445 */
5446 String name;
5448 /**
5449 * Suffix of the source
5450 */
5451 String suffix;
5454 /**
5455 * Used to list files ref'd by this one
5456 */
5457 std::vector<String> files;
5460 private:
5462 void init()
5463 {
5464 }
5466 void assign(const DepRec &other)
5467 {
5468 path = other.path;
5469 name = other.name;
5470 suffix = other.suffix;
5471 files = other.files; //avoid recursion
5472 }
5474 };
5477 class DepTool : public MakeBase
5478 {
5479 public:
5481 /**
5482 * Constructor
5483 */
5484 DepTool()
5485 { init(); }
5487 /**
5488 * Copy constructor
5489 */
5490 DepTool(const DepTool &other)
5491 { init(); assign(other); }
5493 /**
5494 * Assignment operator
5495 */
5496 DepTool &operator=(const DepTool &other)
5497 { init(); assign(other); return *this; }
5500 /**
5501 * Destructor
5502 */
5503 ~DepTool()
5504 {}
5507 /**
5508 * Reset this section of code
5509 */
5510 virtual void init();
5512 /**
5513 * Reset this section of code
5514 */
5515 virtual void assign(const DepTool &other)
5516 {
5517 }
5519 /**
5520 * Sets the source directory which will be scanned
5521 */
5522 virtual void setSourceDirectory(const String &val)
5523 { sourceDir = val; }
5525 /**
5526 * Returns the source directory which will be scanned
5527 */
5528 virtual String getSourceDirectory()
5529 { return sourceDir; }
5531 /**
5532 * Sets the list of files within the directory to analyze
5533 */
5534 virtual void setFileList(const std::vector<String> &list)
5535 { fileList = list; }
5537 /**
5538 * Creates the list of all file names which will be
5539 * candidates for further processing. Reads make.exclude
5540 * to see which files for directories to leave out.
5541 */
5542 virtual bool createFileList();
5545 /**
5546 * Generates the forward dependency list
5547 */
5548 virtual bool generateDependencies();
5551 /**
5552 * Generates the forward dependency list, saving the file
5553 */
5554 virtual bool generateDependencies(const String &);
5557 /**
5558 * Load a dependency file
5559 */
5560 std::vector<DepRec> loadDepFile(const String &fileName);
5562 /**
5563 * Load a dependency file, generating one if necessary
5564 */
5565 std::vector<DepRec> getDepFile(const String &fileName,
5566 bool forceRefresh);
5568 /**
5569 * Save a dependency file
5570 */
5571 bool saveDepFile(const String &fileName);
5574 private:
5577 /**
5578 *
5579 */
5580 void parseName(const String &fullname,
5581 String &path,
5582 String &basename,
5583 String &suffix);
5585 /**
5586 *
5587 */
5588 int get(int pos);
5590 /**
5591 *
5592 */
5593 int skipwhite(int pos);
5595 /**
5596 *
5597 */
5598 int getword(int pos, String &ret);
5600 /**
5601 *
5602 */
5603 bool sequ(int pos, const char *key);
5605 /**
5606 *
5607 */
5608 bool addIncludeFile(FileRec *frec, const String &fname);
5610 /**
5611 *
5612 */
5613 bool scanFile(const String &fname, FileRec *frec);
5615 /**
5616 *
5617 */
5618 bool processDependency(FileRec *ofile, FileRec *include);
5620 /**
5621 *
5622 */
5623 String sourceDir;
5625 /**
5626 *
5627 */
5628 std::vector<String> fileList;
5630 /**
5631 *
5632 */
5633 std::vector<String> directories;
5635 /**
5636 * A list of all files which will be processed for
5637 * dependencies.
5638 */
5639 std::map<String, FileRec *> allFiles;
5641 /**
5642 * The list of .o files, and the
5643 * dependencies upon them.
5644 */
5645 std::map<String, FileRec *> oFiles;
5647 int depFileSize;
5648 char *depFileBuf;
5650 static const int readBufSize = 8192;
5651 char readBuf[8193];//byte larger
5653 };
5659 /**
5660 * Clean up after processing. Called by the destructor, but should
5661 * also be called before the object is reused.
5662 */
5663 void DepTool::init()
5664 {
5665 sourceDir = ".";
5667 fileList.clear();
5668 directories.clear();
5670 //clear output file list
5671 std::map<String, FileRec *>::iterator iter;
5672 for (iter=oFiles.begin(); iter!=oFiles.end() ; iter++)
5673 delete iter->second;
5674 oFiles.clear();
5676 //allFiles actually contains the master copies. delete them
5677 for (iter= allFiles.begin(); iter!=allFiles.end() ; iter++)
5678 delete iter->second;
5679 allFiles.clear();
5681 }
5686 /**
5687 * Parse a full path name into path, base name, and suffix
5688 */
5689 void DepTool::parseName(const String &fullname,
5690 String &path,
5691 String &basename,
5692 String &suffix)
5693 {
5694 if (fullname.size() < 2)
5695 return;
5697 unsigned int pos = fullname.find_last_of('/');
5698 if (pos != fullname.npos && pos<fullname.size()-1)
5699 {
5700 path = fullname.substr(0, pos);
5701 pos++;
5702 basename = fullname.substr(pos, fullname.size()-pos);
5703 }
5704 else
5705 {
5706 path = "";
5707 basename = fullname;
5708 }
5710 pos = basename.find_last_of('.');
5711 if (pos != basename.npos && pos<basename.size()-1)
5712 {
5713 suffix = basename.substr(pos+1, basename.size()-pos-1);
5714 basename = basename.substr(0, pos);
5715 }
5717 //trace("parsename:%s %s %s", path.c_str(),
5718 // basename.c_str(), suffix.c_str());
5719 }
5723 /**
5724 * Generate our internal file list.
5725 */
5726 bool DepTool::createFileList()
5727 {
5729 for (unsigned int i=0 ; i<fileList.size() ; i++)
5730 {
5731 String fileName = fileList[i];
5732 //trace("## FileName:%s", fileName.c_str());
5733 String path;
5734 String basename;
5735 String sfx;
5736 parseName(fileName, path, basename, sfx);
5737 if (sfx == "cpp" || sfx == "c" || sfx == "cxx" ||
5738 sfx == "cc" || sfx == "CC")
5739 {
5740 FileRec *fe = new FileRec(FileRec::CFILE);
5741 fe->path = path;
5742 fe->baseName = basename;
5743 fe->suffix = sfx;
5744 allFiles[fileName] = fe;
5745 }
5746 else if (sfx == "h" || sfx == "hh" ||
5747 sfx == "hpp" || sfx == "hxx")
5748 {
5749 FileRec *fe = new FileRec(FileRec::HFILE);
5750 fe->path = path;
5751 fe->baseName = basename;
5752 fe->suffix = sfx;
5753 allFiles[fileName] = fe;
5754 }
5755 }
5757 if (!listDirectories(sourceDir, "", directories))
5758 return false;
5760 return true;
5761 }
5767 /**
5768 * Get a character from the buffer at pos. If out of range,
5769 * return -1 for safety
5770 */
5771 int DepTool::get(int pos)
5772 {
5773 if (pos>depFileSize)
5774 return -1;
5775 return depFileBuf[pos];
5776 }
5780 /**
5781 * Skip over all whitespace characters beginning at pos. Return
5782 * the position of the first non-whitespace character.
5783 */
5784 int DepTool::skipwhite(int pos)
5785 {
5786 while (pos < depFileSize)
5787 {
5788 int ch = get(pos);
5789 if (ch < 0)
5790 break;
5791 if (!isspace(ch))
5792 break;
5793 pos++;
5794 }
5795 return pos;
5796 }
5799 /**
5800 * Parse the buffer beginning at pos, for a word. Fill
5801 * 'ret' with the result. Return the position after the
5802 * word.
5803 */
5804 int DepTool::getword(int pos, String &ret)
5805 {
5806 while (pos < depFileSize)
5807 {
5808 int ch = get(pos);
5809 if (ch < 0)
5810 break;
5811 if (isspace(ch))
5812 break;
5813 ret.push_back((char)ch);
5814 pos++;
5815 }
5816 return pos;
5817 }
5819 /**
5820 * Return whether the sequence of characters in the buffer
5821 * beginning at pos match the key, for the length of the key
5822 */
5823 bool DepTool::sequ(int pos, const char *key)
5824 {
5825 while (*key)
5826 {
5827 if (*key != get(pos))
5828 return false;
5829 key++; pos++;
5830 }
5831 return true;
5832 }
5836 /**
5837 * Add an include file name to a file record. If the name
5838 * is not found in allFiles explicitly, try prepending include
5839 * directory names to it and try again.
5840 */
5841 bool DepTool::addIncludeFile(FileRec *frec, const String &iname)
5842 {
5843 //# if the name is an exact match to a path name
5844 //# in allFiles, like "myinc.h"
5845 std::map<String, FileRec *>::iterator iter =
5846 allFiles.find(iname);
5847 if (iter != allFiles.end()) //already exists
5848 {
5849 //h file in same dir
5850 FileRec *other = iter->second;
5851 //trace("local: '%s'", iname.c_str());
5852 frec->files[iname] = other;
5853 return true;
5854 }
5855 else
5856 {
5857 //## Ok, it was not found directly
5858 //look in other dirs
5859 std::vector<String>::iterator diter;
5860 for (diter=directories.begin() ;
5861 diter!=directories.end() ; diter++)
5862 {
5863 String dfname = *diter;
5864 dfname.append("/");
5865 dfname.append(iname);
5866 URI fullPathURI(dfname); //normalize path name
5867 String fullPath = fullPathURI.getPath();
5868 if (fullPath[0] == '/')
5869 fullPath = fullPath.substr(1);
5870 //trace("Normalized %s to %s", dfname.c_str(), fullPath.c_str());
5871 iter = allFiles.find(fullPath);
5872 if (iter != allFiles.end())
5873 {
5874 FileRec *other = iter->second;
5875 //trace("other: '%s'", iname.c_str());
5876 frec->files[fullPath] = other;
5877 return true;
5878 }
5879 }
5880 }
5881 return true;
5882 }
5886 /**
5887 * Lightly parse a file to find the #include directives. Do
5888 * a bit of state machine stuff to make sure that the directive
5889 * is valid. (Like not in a comment).
5890 */
5891 bool DepTool::scanFile(const String &fname, FileRec *frec)
5892 {
5893 String fileName;
5894 if (sourceDir.size() > 0)
5895 {
5896 fileName.append(sourceDir);
5897 fileName.append("/");
5898 }
5899 fileName.append(fname);
5900 String nativeName = getNativePath(fileName);
5901 FILE *f = fopen(nativeName.c_str(), "r");
5902 if (!f)
5903 {
5904 error("Could not open '%s' for reading", fname.c_str());
5905 return false;
5906 }
5907 String buf;
5908 while (!feof(f))
5909 {
5910 int nrbytes = fread(readBuf, 1, readBufSize, f);
5911 readBuf[nrbytes] = '\0';
5912 buf.append(readBuf);
5913 }
5914 fclose(f);
5916 depFileSize = buf.size();
5917 depFileBuf = (char *)buf.c_str();
5918 int pos = 0;
5921 while (pos < depFileSize)
5922 {
5923 //trace("p:%c", get(pos));
5925 //# Block comment
5926 if (get(pos) == '/' && get(pos+1) == '*')
5927 {
5928 pos += 2;
5929 while (pos < depFileSize)
5930 {
5931 if (get(pos) == '*' && get(pos+1) == '/')
5932 {
5933 pos += 2;
5934 break;
5935 }
5936 else
5937 pos++;
5938 }
5939 }
5940 //# Line comment
5941 else if (get(pos) == '/' && get(pos+1) == '/')
5942 {
5943 pos += 2;
5944 while (pos < depFileSize)
5945 {
5946 if (get(pos) == '\n')
5947 {
5948 pos++;
5949 break;
5950 }
5951 else
5952 pos++;
5953 }
5954 }
5955 //# #include! yaay
5956 else if (sequ(pos, "#include"))
5957 {
5958 pos += 8;
5959 pos = skipwhite(pos);
5960 String iname;
5961 pos = getword(pos, iname);
5962 if (iname.size()>2)
5963 {
5964 iname = iname.substr(1, iname.size()-2);
5965 addIncludeFile(frec, iname);
5966 }
5967 }
5968 else
5969 {
5970 pos++;
5971 }
5972 }
5974 return true;
5975 }
5979 /**
5980 * Recursively check include lists to find all files in allFiles to which
5981 * a given file is dependent.
5982 */
5983 bool DepTool::processDependency(FileRec *ofile, FileRec *include)
5984 {
5985 std::map<String, FileRec *>::iterator iter;
5986 for (iter=include->files.begin() ; iter!=include->files.end() ; iter++)
5987 {
5988 String fname = iter->first;
5989 if (ofile->files.find(fname) != ofile->files.end())
5990 {
5991 //trace("file '%s' already seen", fname.c_str());
5992 continue;
5993 }
5994 FileRec *child = iter->second;
5995 ofile->files[fname] = child;
5997 processDependency(ofile, child);
5998 }
6001 return true;
6002 }
6008 /**
6009 * Generate the file dependency list.
6010 */
6011 bool DepTool::generateDependencies()
6012 {
6013 std::map<String, FileRec *>::iterator iter;
6014 //# First pass. Scan for all includes
6015 for (iter=allFiles.begin() ; iter!=allFiles.end() ; iter++)
6016 {
6017 FileRec *frec = iter->second;
6018 if (!scanFile(iter->first, frec))
6019 {
6020 //quit?
6021 }
6022 }
6024 //# Second pass. Scan for all includes
6025 for (iter=allFiles.begin() ; iter!=allFiles.end() ; iter++)
6026 {
6027 FileRec *include = iter->second;
6028 if (include->type == FileRec::CFILE)
6029 {
6030 //String cFileName = iter->first;
6031 FileRec *ofile = new FileRec(FileRec::OFILE);
6032 ofile->path = include->path;
6033 ofile->baseName = include->baseName;
6034 ofile->suffix = include->suffix;
6035 String fname = include->path;
6036 if (fname.size()>0)
6037 fname.append("/");
6038 fname.append(include->baseName);
6039 fname.append(".o");
6040 oFiles[fname] = ofile;
6041 //add the .c file first? no, don't
6042 //ofile->files[cFileName] = include;
6044 //trace("ofile:%s", fname.c_str());
6046 processDependency(ofile, include);
6047 }
6048 }
6051 return true;
6052 }
6056 /**
6057 * High-level call to generate deps and optionally save them
6058 */
6059 bool DepTool::generateDependencies(const String &fileName)
6060 {
6061 if (!createFileList())
6062 return false;
6063 if (!generateDependencies())
6064 return false;
6065 if (!saveDepFile(fileName))
6066 return false;
6067 return true;
6068 }
6071 /**
6072 * This saves the dependency cache.
6073 */
6074 bool DepTool::saveDepFile(const String &fileName)
6075 {
6076 time_t tim;
6077 time(&tim);
6079 FILE *f = fopen(fileName.c_str(), "w");
6080 if (!f)
6081 {
6082 trace("cannot open '%s' for writing", fileName.c_str());
6083 }
6084 fprintf(f, "<?xml version='1.0'?>\n");
6085 fprintf(f, "<!--\n");
6086 fprintf(f, "########################################################\n");
6087 fprintf(f, "## File: build.dep\n");
6088 fprintf(f, "## Generated by BuildTool at :%s", ctime(&tim));
6089 fprintf(f, "########################################################\n");
6090 fprintf(f, "-->\n");
6092 fprintf(f, "<dependencies source='%s'>\n\n", sourceDir.c_str());
6093 std::map<String, FileRec *>::iterator iter;
6094 for (iter=oFiles.begin() ; iter!=oFiles.end() ; iter++)
6095 {
6096 FileRec *frec = iter->second;
6097 if (frec->type == FileRec::OFILE)
6098 {
6099 fprintf(f, "<object path='%s' name='%s' suffix='%s'>\n",
6100 frec->path.c_str(), frec->baseName.c_str(), frec->suffix.c_str());
6101 std::map<String, FileRec *>::iterator citer;
6102 for (citer=frec->files.begin() ; citer!=frec->files.end() ; citer++)
6103 {
6104 String cfname = citer->first;
6105 fprintf(f, " <dep name='%s'/>\n", cfname.c_str());
6106 }
6107 fprintf(f, "</object>\n\n");
6108 }
6109 }
6111 fprintf(f, "</dependencies>\n");
6112 fprintf(f, "\n");
6113 fprintf(f, "<!--\n");
6114 fprintf(f, "########################################################\n");
6115 fprintf(f, "## E N D\n");
6116 fprintf(f, "########################################################\n");
6117 fprintf(f, "-->\n");
6119 fclose(f);
6121 return true;
6122 }
6127 /**
6128 * This loads the dependency cache.
6129 */
6130 std::vector<DepRec> DepTool::loadDepFile(const String &depFile)
6131 {
6132 std::vector<DepRec> result;
6134 Parser parser;
6135 Element *root = parser.parseFile(depFile.c_str());
6136 if (!root)
6137 {
6138 //error("Could not open %s for reading", depFile.c_str());
6139 return result;
6140 }
6142 if (root->getChildren().size()==0 ||
6143 root->getChildren()[0]->getName()!="dependencies")
6144 {
6145 error("loadDepFile: main xml element should be <dependencies>");
6146 delete root;
6147 return result;
6148 }
6150 //########## Start parsing
6151 Element *depList = root->getChildren()[0];
6153 std::vector<Element *> objects = depList->getChildren();
6154 for (unsigned int i=0 ; i<objects.size() ; i++)
6155 {
6156 Element *objectElem = objects[i];
6157 String tagName = objectElem->getName();
6158 if (tagName != "object")
6159 {
6160 error("loadDepFile: <dependencies> should have only <object> children");
6161 return result;
6162 }
6164 String objName = objectElem->getAttribute("name");
6165 //trace("object:%s", objName.c_str());
6166 DepRec depObject(objName);
6167 depObject.path = objectElem->getAttribute("path");
6168 depObject.suffix = objectElem->getAttribute("suffix");
6169 //########## DESCRIPTION
6170 std::vector<Element *> depElems = objectElem->getChildren();
6171 for (unsigned int i=0 ; i<depElems.size() ; i++)
6172 {
6173 Element *depElem = depElems[i];
6174 tagName = depElem->getName();
6175 if (tagName != "dep")
6176 {
6177 error("loadDepFile: <object> should have only <dep> children");
6178 return result;
6179 }
6180 String depName = depElem->getAttribute("name");
6181 //trace(" dep:%s", depName.c_str());
6182 depObject.files.push_back(depName);
6183 }
6185 //Insert into the result list, in a sorted manner
6186 bool inserted = false;
6187 std::vector<DepRec>::iterator iter;
6188 for (iter = result.begin() ; iter != result.end() ; iter++)
6189 {
6190 String vpath = iter->path;
6191 vpath.append("/");
6192 vpath.append(iter->name);
6193 String opath = depObject.path;
6194 opath.append("/");
6195 opath.append(depObject.name);
6196 if (vpath > opath)
6197 {
6198 inserted = true;
6199 iter = result.insert(iter, depObject);
6200 break;
6201 }
6202 }
6203 if (!inserted)
6204 result.push_back(depObject);
6205 }
6207 delete root;
6209 return result;
6210 }
6213 /**
6214 * This loads the dependency cache.
6215 */
6216 std::vector<DepRec> DepTool::getDepFile(const String &depFile,
6217 bool forceRefresh)
6218 {
6219 std::vector<DepRec> result;
6220 if (forceRefresh)
6221 {
6222 generateDependencies(depFile);
6223 result = loadDepFile(depFile);
6224 }
6225 else
6226 {
6227 //try once
6228 result = loadDepFile(depFile);
6229 if (result.size() == 0)
6230 {
6231 //fail? try again
6232 generateDependencies(depFile);
6233 result = loadDepFile(depFile);
6234 }
6235 }
6236 return result;
6237 }
6242 //########################################################################
6243 //# T A S K
6244 //########################################################################
6245 //forward decl
6246 class Target;
6247 class Make;
6249 /**
6250 *
6251 */
6252 class Task : public MakeBase
6253 {
6255 public:
6257 typedef enum
6258 {
6259 TASK_NONE,
6260 TASK_CC,
6261 TASK_COPY,
6262 TASK_DELETE,
6263 TASK_ECHO,
6264 TASK_JAR,
6265 TASK_JAVAC,
6266 TASK_LINK,
6267 TASK_MAKEFILE,
6268 TASK_MKDIR,
6269 TASK_MSGFMT,
6270 TASK_PKG_CONFIG,
6271 TASK_RANLIB,
6272 TASK_RC,
6273 TASK_SHAREDLIB,
6274 TASK_STATICLIB,
6275 TASK_STRIP,
6276 TASK_TOUCH,
6277 TASK_TSTAMP
6278 } TaskType;
6281 /**
6282 *
6283 */
6284 Task(MakeBase &par) : parent(par)
6285 { init(); }
6287 /**
6288 *
6289 */
6290 Task(const Task &other) : parent(other.parent)
6291 { init(); assign(other); }
6293 /**
6294 *
6295 */
6296 Task &operator=(const Task &other)
6297 { assign(other); return *this; }
6299 /**
6300 *
6301 */
6302 virtual ~Task()
6303 { }
6306 /**
6307 *
6308 */
6309 virtual MakeBase &getParent()
6310 { return parent; }
6312 /**
6313 *
6314 */
6315 virtual int getType()
6316 { return type; }
6318 /**
6319 *
6320 */
6321 virtual void setType(int val)
6322 { type = val; }
6324 /**
6325 *
6326 */
6327 virtual String getName()
6328 { return name; }
6330 /**
6331 *
6332 */
6333 virtual bool execute()
6334 { return true; }
6336 /**
6337 *
6338 */
6339 virtual bool parse(Element *elem)
6340 { return true; }
6342 /**
6343 *
6344 */
6345 Task *createTask(Element *elem, int lineNr);
6348 protected:
6350 void init()
6351 {
6352 type = TASK_NONE;
6353 name = "none";
6354 }
6356 void assign(const Task &other)
6357 {
6358 type = other.type;
6359 name = other.name;
6360 }
6362 /**
6363 * Show task status
6364 */
6365 void taskstatus(const char *fmt, ...)
6366 {
6367 va_list args;
6368 va_start(args,fmt);
6369 fprintf(stdout, " %s : ", name.c_str());
6370 vfprintf(stdout, fmt, args);
6371 fprintf(stdout, "\n");
6372 va_end(args) ;
6373 }
6375 String getAttribute(Element *elem, const String &attrName)
6376 {
6377 String str;
6378 return str;
6379 }
6381 MakeBase &parent;
6383 int type;
6385 String name;
6386 };
6390 /**
6391 * This task runs the C/C++ compiler. The compiler is invoked
6392 * for all .c or .cpp files which are newer than their correcsponding
6393 * .o files.
6394 */
6395 class TaskCC : public Task
6396 {
6397 public:
6399 TaskCC(MakeBase &par) : Task(par)
6400 {
6401 type = TASK_CC;
6402 name = "cc";
6403 }
6405 virtual ~TaskCC()
6406 {}
6408 virtual bool isExcludedInc(const String &dirname)
6409 {
6410 for (unsigned int i=0 ; i<excludeInc.size() ; i++)
6411 {
6412 String fname = excludeInc[i];
6413 if (fname == dirname)
6414 return true;
6415 }
6416 return false;
6417 }
6419 virtual bool execute()
6420 {
6421 //evaluate our parameters
6422 String command = parent.eval(commandOpt, "gcc");
6423 String ccCommand = parent.eval(ccCommandOpt, "gcc");
6424 String cxxCommand = parent.eval(cxxCommandOpt, "g++");
6425 String source = parent.eval(sourceOpt, ".");
6426 String dest = parent.eval(destOpt, ".");
6427 String flags = parent.eval(flagsOpt, "");
6428 String defines = parent.eval(definesOpt, "");
6429 String includes = parent.eval(includesOpt, "");
6430 bool continueOnError = parent.evalBool(continueOnErrorOpt, true);
6431 bool refreshCache = parent.evalBool(refreshCacheOpt, false);
6433 if (!listFiles(parent, fileSet))
6434 return false;
6436 FILE *f = NULL;
6437 f = fopen("compile.lst", "w");
6439 //refreshCache is probably false here, unless specified otherwise
6440 String fullName = parent.resolve("build.dep");
6441 if (refreshCache || isNewerThan(parent.getURI().getPath(), fullName))
6442 {
6443 taskstatus("regenerating C/C++ dependency cache");
6444 refreshCache = true;
6445 }
6447 DepTool depTool;
6448 depTool.setSourceDirectory(source);
6449 depTool.setFileList(fileSet.getFiles());
6450 std::vector<DepRec> deps =
6451 depTool.getDepFile("build.dep", refreshCache);
6453 String incs;
6454 incs.append("-I");
6455 incs.append(parent.resolve("."));
6456 incs.append(" ");
6457 if (includes.size()>0)
6458 {
6459 incs.append(includes);
6460 incs.append(" ");
6461 }
6462 std::set<String> paths;
6463 std::vector<DepRec>::iterator viter;
6464 for (viter=deps.begin() ; viter!=deps.end() ; viter++)
6465 {
6466 DepRec dep = *viter;
6467 if (dep.path.size()>0)
6468 paths.insert(dep.path);
6469 }
6470 if (source.size()>0)
6471 {
6472 incs.append(" -I");
6473 incs.append(parent.resolve(source));
6474 incs.append(" ");
6475 }
6476 std::set<String>::iterator setIter;
6477 for (setIter=paths.begin() ; setIter!=paths.end() ; setIter++)
6478 {
6479 String dirName = *setIter;
6480 //check excludeInc to see if we dont want to include this dir
6481 if (isExcludedInc(dirName))
6482 continue;
6483 incs.append(" -I");
6484 String dname;
6485 if (source.size()>0)
6486 {
6487 dname.append(source);
6488 dname.append("/");
6489 }
6490 dname.append(dirName);
6491 incs.append(parent.resolve(dname));
6492 }
6494 /**
6495 * Compile each of the C files that need it
6496 */
6497 bool errorOccurred = false;
6498 std::vector<String> cfiles;
6499 for (viter=deps.begin() ; viter!=deps.end() ; viter++)
6500 {
6501 DepRec dep = *viter;
6503 //## Select command
6504 String sfx = dep.suffix;
6505 String command = ccCommand;
6506 if (sfx == "cpp" || sfx == "cxx" || sfx == "c++" ||
6507 sfx == "cc" || sfx == "CC")
6508 command = cxxCommand;
6510 //## Make paths
6511 String destPath = dest;
6512 String srcPath = source;
6513 if (dep.path.size()>0)
6514 {
6515 destPath.append("/");
6516 destPath.append(dep.path);
6517 srcPath.append("/");
6518 srcPath.append(dep.path);
6519 }
6520 //## Make sure destination directory exists
6521 if (!createDirectory(destPath))
6522 return false;
6524 //## Check whether it needs to be done
6525 String destName;
6526 if (destPath.size()>0)
6527 {
6528 destName.append(destPath);
6529 destName.append("/");
6530 }
6531 destName.append(dep.name);
6532 destName.append(".o");
6533 String destFullName = parent.resolve(destName);
6534 String srcName;
6535 if (srcPath.size()>0)
6536 {
6537 srcName.append(srcPath);
6538 srcName.append("/");
6539 }
6540 srcName.append(dep.name);
6541 srcName.append(".");
6542 srcName.append(dep.suffix);
6543 String srcFullName = parent.resolve(srcName);
6544 bool compileMe = false;
6545 //# First we check if the source is newer than the .o
6546 if (isNewerThan(srcFullName, destFullName))
6547 {
6548 taskstatus("compile of %s required by source: %s",
6549 destFullName.c_str(), srcFullName.c_str());
6550 compileMe = true;
6551 }
6552 else
6553 {
6554 //# secondly, we check if any of the included dependencies
6555 //# of the .c/.cpp is newer than the .o
6556 for (unsigned int i=0 ; i<dep.files.size() ; i++)
6557 {
6558 String depName;
6559 if (source.size()>0)
6560 {
6561 depName.append(source);
6562 depName.append("/");
6563 }
6564 depName.append(dep.files[i]);
6565 String depFullName = parent.resolve(depName);
6566 bool depRequires = isNewerThan(depFullName, destFullName);
6567 //trace("%d %s %s\n", depRequires,
6568 // destFullName.c_str(), depFullName.c_str());
6569 if (depRequires)
6570 {
6571 taskstatus("compile of %s required by included: %s",
6572 destFullName.c_str(), depFullName.c_str());
6573 compileMe = true;
6574 break;
6575 }
6576 }
6577 }
6578 if (!compileMe)
6579 {
6580 continue;
6581 }
6583 //## Assemble the command
6584 String cmd = command;
6585 cmd.append(" -c ");
6586 cmd.append(flags);
6587 cmd.append(" ");
6588 cmd.append(defines);
6589 cmd.append(" ");
6590 cmd.append(incs);
6591 cmd.append(" ");
6592 cmd.append(srcFullName);
6593 cmd.append(" -o ");
6594 cmd.append(destFullName);
6596 //## Execute the command
6598 String outString, errString;
6599 bool ret = executeCommand(cmd.c_str(), "", outString, errString);
6601 if (f)
6602 {
6603 fprintf(f, "########################### File : %s\n",
6604 srcFullName.c_str());
6605 fprintf(f, "#### COMMAND ###\n");
6606 int col = 0;
6607 for (unsigned int i = 0 ; i < cmd.size() ; i++)
6608 {
6609 char ch = cmd[i];
6610 if (isspace(ch) && col > 63)
6611 {
6612 fputc('\n', f);
6613 col = 0;
6614 }
6615 else
6616 {
6617 fputc(ch, f);
6618 col++;
6619 }
6620 if (col > 76)
6621 {
6622 fputc('\n', f);
6623 col = 0;
6624 }
6625 }
6626 fprintf(f, "\n");
6627 fprintf(f, "#### STDOUT ###\n%s\n", outString.c_str());
6628 fprintf(f, "#### STDERR ###\n%s\n\n", errString.c_str());
6629 fflush(f);
6630 }
6631 if (!ret)
6632 {
6633 error("problem compiling: %s", errString.c_str());
6634 errorOccurred = true;
6635 }
6636 if (errorOccurred && !continueOnError)
6637 break;
6638 }
6640 if (f)
6641 {
6642 fclose(f);
6643 }
6645 return !errorOccurred;
6646 }
6649 virtual bool parse(Element *elem)
6650 {
6651 String s;
6652 if (!parent.getAttribute(elem, "command", commandOpt))
6653 return false;
6654 if (commandOpt.size()>0)
6655 { cxxCommandOpt = ccCommandOpt = commandOpt; }
6656 if (!parent.getAttribute(elem, "cc", ccCommandOpt))
6657 return false;
6658 if (!parent.getAttribute(elem, "cxx", cxxCommandOpt))
6659 return false;
6660 if (!parent.getAttribute(elem, "destdir", destOpt))
6661 return false;
6662 if (!parent.getAttribute(elem, "continueOnError", continueOnErrorOpt))
6663 return false;
6664 if (!parent.getAttribute(elem, "refreshCache", refreshCacheOpt))
6665 return false;
6667 std::vector<Element *> children = elem->getChildren();
6668 for (unsigned int i=0 ; i<children.size() ; i++)
6669 {
6670 Element *child = children[i];
6671 String tagName = child->getName();
6672 if (tagName == "flags")
6673 {
6674 if (!parent.getValue(child, flagsOpt))
6675 return false;
6676 flagsOpt = strip(flagsOpt);
6677 }
6678 else if (tagName == "includes")
6679 {
6680 if (!parent.getValue(child, includesOpt))
6681 return false;
6682 includesOpt = strip(includesOpt);
6683 }
6684 else if (tagName == "defines")
6685 {
6686 if (!parent.getValue(child, definesOpt))
6687 return false;
6688 definesOpt = strip(definesOpt);
6689 }
6690 else if (tagName == "fileset")
6691 {
6692 if (!parseFileSet(child, parent, fileSet))
6693 return false;
6694 sourceOpt = fileSet.getDirectory();
6695 }
6696 else if (tagName == "excludeinc")
6697 {
6698 if (!parseFileList(child, parent, excludeInc))
6699 return false;
6700 }
6701 }
6703 return true;
6704 }
6706 protected:
6708 String commandOpt;
6709 String ccCommandOpt;
6710 String cxxCommandOpt;
6711 String sourceOpt;
6712 String destOpt;
6713 String flagsOpt;
6714 String definesOpt;
6715 String includesOpt;
6716 String continueOnErrorOpt;
6717 String refreshCacheOpt;
6718 FileSet fileSet;
6719 FileList excludeInc;
6721 };
6725 /**
6726 *
6727 */
6728 class TaskCopy : public Task
6729 {
6730 public:
6732 typedef enum
6733 {
6734 CP_NONE,
6735 CP_TOFILE,
6736 CP_TODIR
6737 } CopyType;
6739 TaskCopy(MakeBase &par) : Task(par)
6740 {
6741 type = TASK_COPY;
6742 name = "copy";
6743 cptype = CP_NONE;
6744 haveFileSet = false;
6745 }
6747 virtual ~TaskCopy()
6748 {}
6750 virtual bool execute()
6751 {
6752 String fileName = parent.eval(fileNameOpt , ".");
6753 String toFileName = parent.eval(toFileNameOpt , ".");
6754 String toDirName = parent.eval(toDirNameOpt , ".");
6755 bool verbose = parent.evalBool(verboseOpt, false);
6756 switch (cptype)
6757 {
6758 case CP_TOFILE:
6759 {
6760 if (fileName.size()>0)
6761 {
6762 taskstatus("%s to %s",
6763 fileName.c_str(), toFileName.c_str());
6764 String fullSource = parent.resolve(fileName);
6765 String fullDest = parent.resolve(toFileName);
6766 //trace("copy %s to file %s", fullSource.c_str(),
6767 // fullDest.c_str());
6768 if (!isRegularFile(fullSource))
6769 {
6770 error("copy : file %s does not exist", fullSource.c_str());
6771 return false;
6772 }
6773 if (!isNewerThan(fullSource, fullDest))
6774 {
6775 taskstatus("skipped");
6776 return true;
6777 }
6778 if (!copyFile(fullSource, fullDest))
6779 return false;
6780 taskstatus("1 file copied");
6781 }
6782 return true;
6783 }
6784 case CP_TODIR:
6785 {
6786 if (haveFileSet)
6787 {
6788 if (!listFiles(parent, fileSet))
6789 return false;
6790 String fileSetDir = parent.eval(fileSet.getDirectory(), ".");
6792 taskstatus("%s to %s",
6793 fileSetDir.c_str(), toDirName.c_str());
6795 int nrFiles = 0;
6796 for (unsigned int i=0 ; i<fileSet.size() ; i++)
6797 {
6798 String fileName = fileSet[i];
6800 String sourcePath;
6801 if (fileSetDir.size()>0)
6802 {
6803 sourcePath.append(fileSetDir);
6804 sourcePath.append("/");
6805 }
6806 sourcePath.append(fileName);
6807 String fullSource = parent.resolve(sourcePath);
6809 //Get the immediate parent directory's base name
6810 String baseFileSetDir = fileSetDir;
6811 unsigned int pos = baseFileSetDir.find_last_of('/');
6812 if (pos!=baseFileSetDir.npos &&
6813 pos < baseFileSetDir.size()-1)
6814 baseFileSetDir =
6815 baseFileSetDir.substr(pos+1,
6816 baseFileSetDir.size());
6817 //Now make the new path
6818 String destPath;
6819 if (toDirName.size()>0)
6820 {
6821 destPath.append(toDirName);
6822 destPath.append("/");
6823 }
6824 if (baseFileSetDir.size()>0)
6825 {
6826 destPath.append(baseFileSetDir);
6827 destPath.append("/");
6828 }
6829 destPath.append(fileName);
6830 String fullDest = parent.resolve(destPath);
6831 //trace("fileName:%s", fileName.c_str());
6832 //trace("copy %s to new dir : %s", fullSource.c_str(),
6833 // fullDest.c_str());
6834 if (!isNewerThan(fullSource, fullDest))
6835 {
6836 //trace("copy skipping %s", fullSource.c_str());
6837 continue;
6838 }
6839 if (!copyFile(fullSource, fullDest))
6840 return false;
6841 nrFiles++;
6842 }
6843 taskstatus("%d file(s) copied", nrFiles);
6844 }
6845 else //file source
6846 {
6847 //For file->dir we want only the basename of
6848 //the source appended to the dest dir
6849 taskstatus("%s to %s",
6850 fileName.c_str(), toDirName.c_str());
6851 String baseName = fileName;
6852 unsigned int pos = baseName.find_last_of('/');
6853 if (pos!=baseName.npos && pos<baseName.size()-1)
6854 baseName = baseName.substr(pos+1, baseName.size());
6855 String fullSource = parent.resolve(fileName);
6856 String destPath;
6857 if (toDirName.size()>0)
6858 {
6859 destPath.append(toDirName);
6860 destPath.append("/");
6861 }
6862 destPath.append(baseName);
6863 String fullDest = parent.resolve(destPath);
6864 //trace("copy %s to new dir : %s", fullSource.c_str(),
6865 // fullDest.c_str());
6866 if (!isRegularFile(fullSource))
6867 {
6868 error("copy : file %s does not exist", fullSource.c_str());
6869 return false;
6870 }
6871 if (!isNewerThan(fullSource, fullDest))
6872 {
6873 taskstatus("skipped");
6874 return true;
6875 }
6876 if (!copyFile(fullSource, fullDest))
6877 return false;
6878 taskstatus("1 file copied");
6879 }
6880 return true;
6881 }
6882 }
6883 return true;
6884 }
6887 virtual bool parse(Element *elem)
6888 {
6889 if (!parent.getAttribute(elem, "file", fileNameOpt))
6890 return false;
6891 if (!parent.getAttribute(elem, "tofile", toFileNameOpt))
6892 return false;
6893 if (toFileNameOpt.size() > 0)
6894 cptype = CP_TOFILE;
6895 if (!parent.getAttribute(elem, "todir", toDirNameOpt))
6896 return false;
6897 if (toDirNameOpt.size() > 0)
6898 cptype = CP_TODIR;
6899 if (!parent.getAttribute(elem, "verbose", verboseOpt))
6900 return false;
6902 haveFileSet = false;
6904 std::vector<Element *> children = elem->getChildren();
6905 for (unsigned int i=0 ; i<children.size() ; i++)
6906 {
6907 Element *child = children[i];
6908 String tagName = child->getName();
6909 if (tagName == "fileset")
6910 {
6911 if (!parseFileSet(child, parent, fileSet))
6912 {
6913 error("problem getting fileset");
6914 return false;
6915 }
6916 haveFileSet = true;
6917 }
6918 }
6920 //Perform validity checks
6921 if (fileNameOpt.size()>0 && fileSet.size()>0)
6922 {
6923 error("<copy> can only have one of : file= and <fileset>");
6924 return false;
6925 }
6926 if (toFileNameOpt.size()>0 && toDirNameOpt.size()>0)
6927 {
6928 error("<copy> can only have one of : tofile= or todir=");
6929 return false;
6930 }
6931 if (haveFileSet && toDirNameOpt.size()==0)
6932 {
6933 error("a <copy> task with a <fileset> must have : todir=");
6934 return false;
6935 }
6936 if (cptype == CP_TOFILE && fileNameOpt.size()==0)
6937 {
6938 error("<copy> tofile= must be associated with : file=");
6939 return false;
6940 }
6941 if (cptype == CP_TODIR && fileNameOpt.size()==0 && !haveFileSet)
6942 {
6943 error("<copy> todir= must be associated with : file= or <fileset>");
6944 return false;
6945 }
6947 return true;
6948 }
6950 private:
6952 int cptype;
6953 bool haveFileSet;
6955 FileSet fileSet;
6956 String fileNameOpt;
6957 String toFileNameOpt;
6958 String toDirNameOpt;
6959 String verboseOpt;
6960 };
6963 /**
6964 *
6965 */
6966 class TaskDelete : public Task
6967 {
6968 public:
6970 typedef enum
6971 {
6972 DEL_FILE,
6973 DEL_DIR,
6974 DEL_FILESET
6975 } DeleteType;
6977 TaskDelete(MakeBase &par) : Task(par)
6978 {
6979 type = TASK_DELETE;
6980 name = "delete";
6981 delType = DEL_FILE;
6982 }
6984 virtual ~TaskDelete()
6985 {}
6987 virtual bool execute()
6988 {
6989 String dirName = parent.eval(dirNameOpt, ".");
6990 String fileName = parent.eval(fileNameOpt, ".");
6991 bool verbose = parent.evalBool(verboseOpt, false);
6992 bool quiet = parent.evalBool(quietOpt, false);
6993 bool failOnError = parent.evalBool(failOnErrorOpt, true);
6994 struct stat finfo;
6995 switch (delType)
6996 {
6997 case DEL_FILE:
6998 {
6999 status(" : %s", fileName.c_str());
7000 String fullName = parent.resolve(fileName);
7001 char *fname = (char *)fullName.c_str();
7002 //does not exist
7003 if (stat(fname, &finfo)<0)
7004 return true;
7005 //exists but is not a regular file
7006 if (!S_ISREG(finfo.st_mode))
7007 {
7008 error("<delete> failed. '%s' exists and is not a regular file",
7009 fname);
7010 return false;
7011 }
7012 if (remove(fname)<0)
7013 {
7014 error("<delete> failed: %s", strerror(errno));
7015 return false;
7016 }
7017 return true;
7018 }
7019 case DEL_DIR:
7020 {
7021 taskstatus("%s", dirName.c_str());
7022 String fullDir = parent.resolve(dirName);
7023 if (!removeDirectory(fullDir))
7024 return false;
7025 return true;
7026 }
7027 }
7028 return true;
7029 }
7031 virtual bool parse(Element *elem)
7032 {
7033 if (!parent.getAttribute(elem, "file", fileNameOpt))
7034 return false;
7035 if (fileNameOpt.size() > 0)
7036 delType = DEL_FILE;
7037 if (!parent.getAttribute(elem, "dir", dirNameOpt))
7038 return false;
7039 if (dirNameOpt.size() > 0)
7040 delType = DEL_DIR;
7041 if (fileNameOpt.size()>0 && dirNameOpt.size()>0)
7042 {
7043 error("<delete> can have one attribute of file= or dir=");
7044 return false;
7045 }
7046 if (fileNameOpt.size()==0 && dirNameOpt.size()==0)
7047 {
7048 error("<delete> must have one attribute of file= or dir=");
7049 return false;
7050 }
7051 if (!parent.getAttribute(elem, "verbose", verboseOpt))
7052 return false;
7053 if (!parent.getAttribute(elem, "quiet", quietOpt))
7054 return false;
7055 if (!parent.getAttribute(elem, "failonerror", failOnErrorOpt))
7056 return false;
7057 return true;
7058 }
7060 private:
7062 int delType;
7063 String dirNameOpt;
7064 String fileNameOpt;
7065 String verboseOpt;
7066 String quietOpt;
7067 String failOnErrorOpt;
7068 };
7071 /**
7072 * Send a message to stdout
7073 */
7074 class TaskEcho : public Task
7075 {
7076 public:
7078 TaskEcho(MakeBase &par) : Task(par)
7079 { type = TASK_ECHO; name = "echo"; }
7081 virtual ~TaskEcho()
7082 {}
7084 virtual bool execute()
7085 {
7086 //let message have priority over text
7087 String message = parent.eval(messageOpt, "");
7088 String text = parent.eval(textOpt, "");
7089 if (message.size() > 0)
7090 {
7091 fprintf(stdout, "%s\n", message.c_str());
7092 }
7093 else if (text.size() > 0)
7094 {
7095 fprintf(stdout, "%s\n", text.c_str());
7096 }
7097 return true;
7098 }
7100 virtual bool parse(Element *elem)
7101 {
7102 if (!parent.getValue(elem, textOpt))
7103 return false;
7104 textOpt = leftJustify(textOpt);
7105 if (!parent.getAttribute(elem, "message", messageOpt))
7106 return false;
7107 return true;
7108 }
7110 private:
7112 String messageOpt;
7113 String textOpt;
7114 };
7118 /**
7119 *
7120 */
7121 class TaskJar : public Task
7122 {
7123 public:
7125 TaskJar(MakeBase &par) : Task(par)
7126 { type = TASK_JAR; name = "jar"; }
7128 virtual ~TaskJar()
7129 {}
7131 virtual bool execute()
7132 {
7133 String command = parent.eval(commandOpt, "jar");
7134 String basedir = parent.eval(basedirOpt, ".");
7135 String destfile = parent.eval(destfileOpt, ".");
7137 String cmd = command;
7138 cmd.append(" -cf ");
7139 cmd.append(destfile);
7140 cmd.append(" -C ");
7141 cmd.append(basedir);
7142 cmd.append(" .");
7144 String execCmd = cmd;
7146 String outString, errString;
7147 bool ret = executeCommand(execCmd.c_str(), "", outString, errString);
7148 if (!ret)
7149 {
7150 error("<jar> command '%s' failed :\n %s",
7151 execCmd.c_str(), errString.c_str());
7152 return false;
7153 }
7154 return true;
7155 }
7157 virtual bool parse(Element *elem)
7158 {
7159 if (!parent.getAttribute(elem, "command", commandOpt))
7160 return false;
7161 if (!parent.getAttribute(elem, "basedir", basedirOpt))
7162 return false;
7163 if (!parent.getAttribute(elem, "destfile", destfileOpt))
7164 return false;
7165 if (basedirOpt.size() == 0 || destfileOpt.size() == 0)
7166 {
7167 error("<jar> required both basedir and destfile attributes to be set");
7168 return false;
7169 }
7170 return true;
7171 }
7173 private:
7175 String commandOpt;
7176 String basedirOpt;
7177 String destfileOpt;
7178 };
7181 /**
7182 *
7183 */
7184 class TaskJavac : public Task
7185 {
7186 public:
7188 TaskJavac(MakeBase &par) : Task(par)
7189 {
7190 type = TASK_JAVAC; name = "javac";
7191 }
7193 virtual ~TaskJavac()
7194 {}
7196 virtual bool execute()
7197 {
7198 String command = parent.eval(commandOpt, "javac");
7199 String srcdir = parent.eval(srcdirOpt, ".");
7200 String destdir = parent.eval(destdirOpt, ".");
7201 String target = parent.eval(targetOpt, "");
7203 std::vector<String> fileList;
7204 if (!listFiles(srcdir, "", fileList))
7205 {
7206 return false;
7207 }
7208 String cmd = command;
7209 cmd.append(" -d ");
7210 cmd.append(destdir);
7211 cmd.append(" -classpath ");
7212 cmd.append(destdir);
7213 cmd.append(" -sourcepath ");
7214 cmd.append(srcdir);
7215 cmd.append(" ");
7216 if (target.size()>0)
7217 {
7218 cmd.append(" -target ");
7219 cmd.append(target);
7220 cmd.append(" ");
7221 }
7222 String fname = "javalist.btool";
7223 FILE *f = fopen(fname.c_str(), "w");
7224 int count = 0;
7225 for (unsigned int i=0 ; i<fileList.size() ; i++)
7226 {
7227 String fname = fileList[i];
7228 String srcName = fname;
7229 if (fname.size()<6) //x.java
7230 continue;
7231 if (fname.compare(fname.size()-5, 5, ".java") != 0)
7232 continue;
7233 String baseName = fname.substr(0, fname.size()-5);
7234 String destName = baseName;
7235 destName.append(".class");
7237 String fullSrc = srcdir;
7238 fullSrc.append("/");
7239 fullSrc.append(fname);
7240 String fullDest = destdir;
7241 fullDest.append("/");
7242 fullDest.append(destName);
7243 //trace("fullsrc:%s fulldest:%s", fullSrc.c_str(), fullDest.c_str());
7244 if (!isNewerThan(fullSrc, fullDest))
7245 continue;
7247 count++;
7248 fprintf(f, "%s\n", fullSrc.c_str());
7249 }
7250 fclose(f);
7251 if (!count)
7252 {
7253 taskstatus("nothing to do");
7254 return true;
7255 }
7257 taskstatus("compiling %d files", count);
7259 String execCmd = cmd;
7260 execCmd.append("@");
7261 execCmd.append(fname);
7263 String outString, errString;
7264 bool ret = executeCommand(execCmd.c_str(), "", outString, errString);
7265 if (!ret)
7266 {
7267 error("<javac> command '%s' failed :\n %s",
7268 execCmd.c_str(), errString.c_str());
7269 return false;
7270 }
7271 return true;
7272 }
7274 virtual bool parse(Element *elem)
7275 {
7276 if (!parent.getAttribute(elem, "command", commandOpt))
7277 return false;
7278 if (!parent.getAttribute(elem, "srcdir", srcdirOpt))
7279 return false;
7280 if (!parent.getAttribute(elem, "destdir", destdirOpt))
7281 return false;
7282 if (srcdirOpt.size() == 0 || destdirOpt.size() == 0)
7283 {
7284 error("<javac> required both srcdir and destdir attributes to be set");
7285 return false;
7286 }
7287 if (!parent.getAttribute(elem, "target", targetOpt))
7288 return false;
7289 return true;
7290 }
7292 private:
7294 String commandOpt;
7295 String srcdirOpt;
7296 String destdirOpt;
7297 String targetOpt;
7299 };
7302 /**
7303 *
7304 */
7305 class TaskLink : public Task
7306 {
7307 public:
7309 TaskLink(MakeBase &par) : Task(par)
7310 {
7311 type = TASK_LINK; name = "link";
7312 }
7314 virtual ~TaskLink()
7315 {}
7317 virtual bool execute()
7318 {
7319 String command = parent.eval(commandOpt, "g++");
7320 String fileName = parent.eval(fileNameOpt, "");
7321 String flags = parent.eval(flagsOpt, "");
7322 String libs = parent.eval(libsOpt, "");
7323 bool doStrip = parent.evalBool(doStripOpt, false);
7324 String symFileName = parent.eval(symFileNameOpt, "");
7325 String stripCommand = parent.eval(stripCommandOpt, "strip");
7326 String objcopyCommand = parent.eval(objcopyCommandOpt, "objcopy");
7328 if (!listFiles(parent, fileSet))
7329 return false;
7330 String fileSetDir = parent.eval(fileSet.getDirectory(), ".");
7331 //trace("%d files in %s", fileSet.size(), fileSetDir.c_str());
7332 bool doit = false;
7333 String fullTarget = parent.resolve(fileName);
7334 String cmd = command;
7335 cmd.append(" -o ");
7336 cmd.append(fullTarget);
7337 cmd.append(" ");
7338 cmd.append(flags);
7339 for (unsigned int i=0 ; i<fileSet.size() ; i++)
7340 {
7341 cmd.append(" ");
7342 String obj;
7343 if (fileSetDir.size()>0)
7344 {
7345 obj.append(fileSetDir);
7346 obj.append("/");
7347 }
7348 obj.append(fileSet[i]);
7349 String fullObj = parent.resolve(obj);
7350 String nativeFullObj = getNativePath(fullObj);
7351 cmd.append(nativeFullObj);
7352 //trace("link: tgt:%s obj:%s", fullTarget.c_str(),
7353 // fullObj.c_str());
7354 if (isNewerThan(fullObj, fullTarget))
7355 doit = true;
7356 }
7357 cmd.append(" ");
7358 cmd.append(libs);
7359 if (!doit)
7360 {
7361 //trace("link not needed");
7362 return true;
7363 }
7364 //trace("LINK cmd:%s", cmd.c_str());
7367 String outbuf, errbuf;
7368 if (!executeCommand(cmd.c_str(), "", outbuf, errbuf))
7369 {
7370 error("LINK problem: %s", errbuf.c_str());
7371 return false;
7372 }
7374 if (symFileName.size()>0)
7375 {
7376 String symFullName = parent.resolve(symFileName);
7377 cmd = objcopyCommand;
7378 cmd.append(" --only-keep-debug ");
7379 cmd.append(getNativePath(fullTarget));
7380 cmd.append(" ");
7381 cmd.append(getNativePath(symFullName));
7382 if (!executeCommand(cmd, "", outbuf, errbuf))
7383 {
7384 error("<strip> symbol file failed : %s", errbuf.c_str());
7385 return false;
7386 }
7387 }
7389 if (doStrip)
7390 {
7391 cmd = stripCommand;
7392 cmd.append(" ");
7393 cmd.append(getNativePath(fullTarget));
7394 if (!executeCommand(cmd, "", outbuf, errbuf))
7395 {
7396 error("<strip> failed : %s", errbuf.c_str());
7397 return false;
7398 }
7399 }
7401 return true;
7402 }
7404 virtual bool parse(Element *elem)
7405 {
7406 if (!parent.getAttribute(elem, "command", commandOpt))
7407 return false;
7408 if (!parent.getAttribute(elem, "objcopycommand", objcopyCommandOpt))
7409 return false;
7410 if (!parent.getAttribute(elem, "stripcommand", stripCommandOpt))
7411 return false;
7412 if (!parent.getAttribute(elem, "out", fileNameOpt))
7413 return false;
7414 if (!parent.getAttribute(elem, "strip", doStripOpt))
7415 return false;
7416 if (!parent.getAttribute(elem, "symfile", symFileNameOpt))
7417 return false;
7419 std::vector<Element *> children = elem->getChildren();
7420 for (unsigned int i=0 ; i<children.size() ; i++)
7421 {
7422 Element *child = children[i];
7423 String tagName = child->getName();
7424 if (tagName == "fileset")
7425 {
7426 if (!parseFileSet(child, parent, fileSet))
7427 return false;
7428 }
7429 else if (tagName == "flags")
7430 {
7431 if (!parent.getValue(child, flagsOpt))
7432 return false;
7433 flagsOpt = strip(flagsOpt);
7434 }
7435 else if (tagName == "libs")
7436 {
7437 if (!parent.getValue(child, libsOpt))
7438 return false;
7439 libsOpt = strip(libsOpt);
7440 }
7441 }
7442 return true;
7443 }
7445 private:
7447 FileSet fileSet;
7449 String commandOpt;
7450 String fileNameOpt;
7451 String flagsOpt;
7452 String libsOpt;
7453 String doStripOpt;
7454 String symFileNameOpt;
7455 String stripCommandOpt;
7456 String objcopyCommandOpt;
7458 };
7462 /**
7463 * Create a named file
7464 */
7465 class TaskMakeFile : public Task
7466 {
7467 public:
7469 TaskMakeFile(MakeBase &par) : Task(par)
7470 { type = TASK_MAKEFILE; name = "makefile"; }
7472 virtual ~TaskMakeFile()
7473 {}
7475 virtual bool execute()
7476 {
7477 String fileName = parent.eval(fileNameOpt, "");
7478 String text = parent.eval(textOpt, "");
7480 taskstatus("%s", fileName.c_str());
7481 String fullName = parent.resolve(fileName);
7482 if (!isNewerThan(parent.getURI().getPath(), fullName))
7483 {
7484 //trace("skipped <makefile>");
7485 return true;
7486 }
7487 String fullNative = getNativePath(fullName);
7488 //trace("fullName:%s", fullName.c_str());
7489 FILE *f = fopen(fullNative.c_str(), "w");
7490 if (!f)
7491 {
7492 error("<makefile> could not open %s for writing : %s",
7493 fullName.c_str(), strerror(errno));
7494 return false;
7495 }
7496 for (unsigned int i=0 ; i<text.size() ; i++)
7497 fputc(text[i], f);
7498 fputc('\n', f);
7499 fclose(f);
7500 return true;
7501 }
7503 virtual bool parse(Element *elem)
7504 {
7505 if (!parent.getAttribute(elem, "file", fileNameOpt))
7506 return false;
7507 if (fileNameOpt.size() == 0)
7508 {
7509 error("<makefile> requires 'file=\"filename\"' attribute");
7510 return false;
7511 }
7512 if (!parent.getValue(elem, textOpt))
7513 return false;
7514 textOpt = leftJustify(textOpt);
7515 //trace("dirname:%s", dirName.c_str());
7516 return true;
7517 }
7519 private:
7521 String fileNameOpt;
7522 String textOpt;
7523 };
7527 /**
7528 * Create a named directory
7529 */
7530 class TaskMkDir : public Task
7531 {
7532 public:
7534 TaskMkDir(MakeBase &par) : Task(par)
7535 { type = TASK_MKDIR; name = "mkdir"; }
7537 virtual ~TaskMkDir()
7538 {}
7540 virtual bool execute()
7541 {
7542 String dirName = parent.eval(dirNameOpt, ".");
7544 taskstatus("%s", dirName.c_str());
7545 String fullDir = parent.resolve(dirName);
7546 //trace("fullDir:%s", fullDir.c_str());
7547 if (!createDirectory(fullDir))
7548 return false;
7549 return true;
7550 }
7552 virtual bool parse(Element *elem)
7553 {
7554 if (!parent.getAttribute(elem, "dir", dirNameOpt))
7555 return false;
7556 if (dirNameOpt.size() == 0)
7557 {
7558 error("<mkdir> requires 'dir=\"dirname\"' attribute");
7559 return false;
7560 }
7561 return true;
7562 }
7564 private:
7566 String dirNameOpt;
7567 };
7571 /**
7572 * Create a named directory
7573 */
7574 class TaskMsgFmt: public Task
7575 {
7576 public:
7578 TaskMsgFmt(MakeBase &par) : Task(par)
7579 { type = TASK_MSGFMT; name = "msgfmt"; }
7581 virtual ~TaskMsgFmt()
7582 {}
7584 virtual bool execute()
7585 {
7586 String command = parent.eval(commandOpt, "msgfmt");
7587 String toDirName = parent.eval(toDirNameOpt, ".");
7588 String outName = parent.eval(outNameOpt, "");
7589 bool owndir = parent.evalBool(owndirOpt, false);
7591 if (!listFiles(parent, fileSet))
7592 return false;
7593 String fileSetDir = fileSet.getDirectory();
7595 //trace("msgfmt: %d", fileSet.size());
7596 for (unsigned int i=0 ; i<fileSet.size() ; i++)
7597 {
7598 String fileName = fileSet[i];
7599 if (getSuffix(fileName) != "po")
7600 continue;
7601 String sourcePath;
7602 if (fileSetDir.size()>0)
7603 {
7604 sourcePath.append(fileSetDir);
7605 sourcePath.append("/");
7606 }
7607 sourcePath.append(fileName);
7608 String fullSource = parent.resolve(sourcePath);
7610 String destPath;
7611 if (toDirName.size()>0)
7612 {
7613 destPath.append(toDirName);
7614 destPath.append("/");
7615 }
7616 if (owndir)
7617 {
7618 String subdir = fileName;
7619 unsigned int pos = subdir.find_last_of('.');
7620 if (pos != subdir.npos)
7621 subdir = subdir.substr(0, pos);
7622 destPath.append(subdir);
7623 destPath.append("/");
7624 }
7625 //Pick the output file name
7626 if (outName.size() > 0)
7627 {
7628 destPath.append(outName);
7629 }
7630 else
7631 {
7632 destPath.append(fileName);
7633 destPath[destPath.size()-2] = 'm';
7634 }
7636 String fullDest = parent.resolve(destPath);
7638 if (!isNewerThan(fullSource, fullDest))
7639 {
7640 //trace("skip %s", fullSource.c_str());
7641 continue;
7642 }
7644 String cmd = command;
7645 cmd.append(" ");
7646 cmd.append(fullSource);
7647 cmd.append(" -o ");
7648 cmd.append(fullDest);
7650 int pos = fullDest.find_last_of('/');
7651 if (pos>0)
7652 {
7653 String fullDestPath = fullDest.substr(0, pos);
7654 if (!createDirectory(fullDestPath))
7655 return false;
7656 }
7660 String outString, errString;
7661 if (!executeCommand(cmd.c_str(), "", outString, errString))
7662 {
7663 error("<msgfmt> problem: %s", errString.c_str());
7664 return false;
7665 }
7666 }
7668 return true;
7669 }
7671 virtual bool parse(Element *elem)
7672 {
7673 if (!parent.getAttribute(elem, "command", commandOpt))
7674 return false;
7675 if (!parent.getAttribute(elem, "todir", toDirNameOpt))
7676 return false;
7677 if (!parent.getAttribute(elem, "out", outNameOpt))
7678 return false;
7679 if (!parent.getAttribute(elem, "owndir", owndirOpt))
7680 return false;
7682 std::vector<Element *> children = elem->getChildren();
7683 for (unsigned int i=0 ; i<children.size() ; i++)
7684 {
7685 Element *child = children[i];
7686 String tagName = child->getName();
7687 if (tagName == "fileset")
7688 {
7689 if (!parseFileSet(child, parent, fileSet))
7690 return false;
7691 }
7692 }
7693 return true;
7694 }
7696 private:
7698 FileSet fileSet;
7700 String commandOpt;
7701 String toDirNameOpt;
7702 String outNameOpt;
7703 String owndirOpt;
7705 };
7709 /**
7710 * Perform a Package-Config query similar to pkg-config
7711 */
7712 class TaskPkgConfig : public Task
7713 {
7714 public:
7716 typedef enum
7717 {
7718 PKG_CONFIG_QUERY_CFLAGS,
7719 PKG_CONFIG_QUERY_LIBS,
7720 PKG_CONFIG_QUERY_ALL
7721 } QueryTypes;
7723 TaskPkgConfig(MakeBase &par) : Task(par)
7724 {
7725 type = TASK_PKG_CONFIG;
7726 name = "pkg-config";
7727 }
7729 virtual ~TaskPkgConfig()
7730 {}
7732 virtual bool execute()
7733 {
7734 String pkgName = parent.eval(pkgNameOpt, "");
7735 String prefix = parent.eval(prefixOpt, "");
7736 String propName = parent.eval(propNameOpt, "");
7737 String pkgConfigPath = parent.eval(pkgConfigPathOpt,"");
7738 String query = parent.eval(queryOpt, "all");
7740 String path = parent.resolve(pkgConfigPath);
7741 PkgConfig pkgconfig;
7742 pkgconfig.setPath(path);
7743 pkgconfig.setPrefix(prefix);
7744 if (!pkgconfig.query(pkgName))
7745 {
7746 error("<pkg-config> query failed for '%s", name.c_str());
7747 return false;
7748 }
7750 String val = "";
7751 if (query == "cflags")
7752 val = pkgconfig.getCflags();
7753 else if (query == "libs")
7754 val =pkgconfig.getLibs();
7755 else if (query == "all")
7756 val = pkgconfig.getAll();
7757 else
7758 {
7759 error("<pkg-config> unhandled query : %s", query.c_str());
7760 return false;
7761 }
7762 taskstatus("property %s = '%s'", propName.c_str(), val.c_str());
7763 parent.setProperty(propName, val);
7764 return true;
7765 }
7767 virtual bool parse(Element *elem)
7768 {
7769 //# NAME
7770 if (!parent.getAttribute(elem, "name", pkgNameOpt))
7771 return false;
7772 if (pkgNameOpt.size()==0)
7773 {
7774 error("<pkg-config> requires 'name=\"package\"' attribute");
7775 return false;
7776 }
7778 //# PROPERTY
7779 if (!parent.getAttribute(elem, "property", propNameOpt))
7780 return false;
7781 if (propNameOpt.size()==0)
7782 {
7783 error("<pkg-config> requires 'property=\"name\"' attribute");
7784 return false;
7785 }
7786 //# PATH
7787 if (!parent.getAttribute(elem, "path", pkgConfigPathOpt))
7788 return false;
7789 //# PREFIX
7790 if (!parent.getAttribute(elem, "prefix", prefixOpt))
7791 return false;
7792 //# QUERY
7793 if (!parent.getAttribute(elem, "query", queryOpt))
7794 return false;
7796 return true;
7797 }
7799 private:
7801 String queryOpt;
7802 String pkgNameOpt;
7803 String prefixOpt;
7804 String propNameOpt;
7805 String pkgConfigPathOpt;
7807 };
7814 /**
7815 * Process an archive to allow random access
7816 */
7817 class TaskRanlib : public Task
7818 {
7819 public:
7821 TaskRanlib(MakeBase &par) : Task(par)
7822 { type = TASK_RANLIB; name = "ranlib"; }
7824 virtual ~TaskRanlib()
7825 {}
7827 virtual bool execute()
7828 {
7829 String fileName = parent.eval(fileNameOpt, "");
7830 String command = parent.eval(commandOpt, "ranlib");
7832 String fullName = parent.resolve(fileName);
7833 //trace("fullDir:%s", fullDir.c_str());
7834 String cmd = command;
7835 cmd.append(" ");
7836 cmd.append(fullName);
7837 String outbuf, errbuf;
7838 if (!executeCommand(cmd, "", outbuf, errbuf))
7839 return false;
7840 return true;
7841 }
7843 virtual bool parse(Element *elem)
7844 {
7845 if (!parent.getAttribute(elem, "command", commandOpt))
7846 return false;
7847 if (!parent.getAttribute(elem, "file", fileNameOpt))
7848 return false;
7849 if (fileNameOpt.size() == 0)
7850 {
7851 error("<ranlib> requires 'file=\"fileNname\"' attribute");
7852 return false;
7853 }
7854 return true;
7855 }
7857 private:
7859 String fileNameOpt;
7860 String commandOpt;
7861 };
7865 /**
7866 * Compile a resource file into a binary object
7867 */
7868 class TaskRC : public Task
7869 {
7870 public:
7872 TaskRC(MakeBase &par) : Task(par)
7873 { type = TASK_RC; name = "rc"; }
7875 virtual ~TaskRC()
7876 {}
7878 virtual bool execute()
7879 {
7880 String command = parent.eval(commandOpt, "windres");
7881 String flags = parent.eval(flagsOpt, "");
7882 String fileName = parent.eval(fileNameOpt, "");
7883 String outName = parent.eval(outNameOpt, "");
7885 String fullFile = parent.resolve(fileName);
7886 String fullOut = parent.resolve(outName);
7887 if (!isNewerThan(fullFile, fullOut))
7888 return true;
7889 String cmd = command;
7890 cmd.append(" -o ");
7891 cmd.append(fullOut);
7892 cmd.append(" ");
7893 cmd.append(flags);
7894 cmd.append(" ");
7895 cmd.append(fullFile);
7897 String outString, errString;
7898 if (!executeCommand(cmd.c_str(), "", outString, errString))
7899 {
7900 error("RC problem: %s", errString.c_str());
7901 return false;
7902 }
7903 return true;
7904 }
7906 virtual bool parse(Element *elem)
7907 {
7908 if (!parent.getAttribute(elem, "command", commandOpt))
7909 return false;
7910 if (!parent.getAttribute(elem, "file", fileNameOpt))
7911 return false;
7912 if (!parent.getAttribute(elem, "out", outNameOpt))
7913 return false;
7914 std::vector<Element *> children = elem->getChildren();
7915 for (unsigned int i=0 ; i<children.size() ; i++)
7916 {
7917 Element *child = children[i];
7918 String tagName = child->getName();
7919 if (tagName == "flags")
7920 {
7921 if (!parent.getValue(child, flagsOpt))
7922 return false;
7923 }
7924 }
7925 return true;
7926 }
7928 private:
7930 String commandOpt;
7931 String flagsOpt;
7932 String fileNameOpt;
7933 String outNameOpt;
7935 };
7939 /**
7940 * Collect .o's into a .so or DLL
7941 */
7942 class TaskSharedLib : public Task
7943 {
7944 public:
7946 TaskSharedLib(MakeBase &par) : Task(par)
7947 { type = TASK_SHAREDLIB; name = "dll"; }
7949 virtual ~TaskSharedLib()
7950 {}
7952 virtual bool execute()
7953 {
7954 String command = parent.eval(commandOpt, "dllwrap");
7955 String fileName = parent.eval(fileNameOpt, "");
7956 String defFileName = parent.eval(defFileNameOpt, "");
7957 String impFileName = parent.eval(impFileNameOpt, "");
7958 String libs = parent.eval(libsOpt, "");
7960 //trace("###########HERE %d", fileSet.size());
7961 bool doit = false;
7963 String fullOut = parent.resolve(fileName);
7964 //trace("ar fullout: %s", fullOut.c_str());
7966 if (!listFiles(parent, fileSet))
7967 return false;
7968 String fileSetDir = parent.eval(fileSet.getDirectory(), ".");
7970 for (unsigned int i=0 ; i<fileSet.size() ; i++)
7971 {
7972 String fname;
7973 if (fileSetDir.size()>0)
7974 {
7975 fname.append(fileSetDir);
7976 fname.append("/");
7977 }
7978 fname.append(fileSet[i]);
7979 String fullName = parent.resolve(fname);
7980 //trace("ar : %s/%s", fullOut.c_str(), fullName.c_str());
7981 if (isNewerThan(fullName, fullOut))
7982 doit = true;
7983 }
7984 //trace("Needs it:%d", doit);
7985 if (!doit)
7986 {
7987 return true;
7988 }
7990 String cmd = "dllwrap";
7991 cmd.append(" -o ");
7992 cmd.append(fullOut);
7993 if (defFileName.size()>0)
7994 {
7995 cmd.append(" --def ");
7996 cmd.append(defFileName);
7997 cmd.append(" ");
7998 }
7999 if (impFileName.size()>0)
8000 {
8001 cmd.append(" --implib ");
8002 cmd.append(impFileName);
8003 cmd.append(" ");
8004 }
8005 for (unsigned int i=0 ; i<fileSet.size() ; i++)
8006 {
8007 String fname;
8008 if (fileSetDir.size()>0)
8009 {
8010 fname.append(fileSetDir);
8011 fname.append("/");
8012 }
8013 fname.append(fileSet[i]);
8014 String fullName = parent.resolve(fname);
8016 cmd.append(" ");
8017 cmd.append(fullName);
8018 }
8019 cmd.append(" ");
8020 cmd.append(libs);
8022 String outString, errString;
8023 if (!executeCommand(cmd.c_str(), "", outString, errString))
8024 {
8025 error("<sharedlib> problem: %s", errString.c_str());
8026 return false;
8027 }
8029 return true;
8030 }
8032 virtual bool parse(Element *elem)
8033 {
8034 if (!parent.getAttribute(elem, "command", commandOpt))
8035 return false;
8036 if (!parent.getAttribute(elem, "file", fileNameOpt))
8037 return false;
8038 if (!parent.getAttribute(elem, "import", impFileNameOpt))
8039 return false;
8040 if (!parent.getAttribute(elem, "def", defFileNameOpt))
8041 return false;
8043 std::vector<Element *> children = elem->getChildren();
8044 for (unsigned int i=0 ; i<children.size() ; i++)
8045 {
8046 Element *child = children[i];
8047 String tagName = child->getName();
8048 if (tagName == "fileset")
8049 {
8050 if (!parseFileSet(child, parent, fileSet))
8051 return false;
8052 }
8053 else if (tagName == "libs")
8054 {
8055 if (!parent.getValue(child, libsOpt))
8056 return false;
8057 libsOpt = strip(libsOpt);
8058 }
8059 }
8060 return true;
8061 }
8063 private:
8065 FileSet fileSet;
8067 String commandOpt;
8068 String fileNameOpt;
8069 String defFileNameOpt;
8070 String impFileNameOpt;
8071 String libsOpt;
8073 };
8077 /**
8078 * Run the "ar" command to archive .o's into a .a
8079 */
8080 class TaskStaticLib : public Task
8081 {
8082 public:
8084 TaskStaticLib(MakeBase &par) : Task(par)
8085 { type = TASK_STATICLIB; name = "staticlib"; }
8087 virtual ~TaskStaticLib()
8088 {}
8090 virtual bool execute()
8091 {
8092 String command = parent.eval(commandOpt, "ar crv");
8093 String fileName = parent.eval(fileNameOpt, "");
8095 bool doit = false;
8097 String fullOut = parent.resolve(fileName);
8098 //trace("ar fullout: %s", fullOut.c_str());
8100 if (!listFiles(parent, fileSet))
8101 return false;
8102 String fileSetDir = parent.eval(fileSet.getDirectory(), ".");
8103 //trace("###########HERE %s", fileSetDir.c_str());
8105 for (unsigned int i=0 ; i<fileSet.size() ; i++)
8106 {
8107 String fname;
8108 if (fileSetDir.size()>0)
8109 {
8110 fname.append(fileSetDir);
8111 fname.append("/");
8112 }
8113 fname.append(fileSet[i]);
8114 String fullName = parent.resolve(fname);
8115 //trace("ar : %s/%s", fullOut.c_str(), fullName.c_str());
8116 if (isNewerThan(fullName, fullOut))
8117 doit = true;
8118 }
8119 //trace("Needs it:%d", doit);
8120 if (!doit)
8121 {
8122 return true;
8123 }
8125 String cmd = command;
8126 cmd.append(" ");
8127 cmd.append(fullOut);
8128 for (unsigned int i=0 ; i<fileSet.size() ; i++)
8129 {
8130 String fname;
8131 if (fileSetDir.size()>0)
8132 {
8133 fname.append(fileSetDir);
8134 fname.append("/");
8135 }
8136 fname.append(fileSet[i]);
8137 String fullName = parent.resolve(fname);
8139 cmd.append(" ");
8140 cmd.append(fullName);
8141 }
8143 String outString, errString;
8144 if (!executeCommand(cmd.c_str(), "", outString, errString))
8145 {
8146 error("<staticlib> problem: %s", errString.c_str());
8147 return false;
8148 }
8150 return true;
8151 }
8154 virtual bool parse(Element *elem)
8155 {
8156 if (!parent.getAttribute(elem, "command", commandOpt))
8157 return false;
8158 if (!parent.getAttribute(elem, "file", fileNameOpt))
8159 return false;
8161 std::vector<Element *> children = elem->getChildren();
8162 for (unsigned int i=0 ; i<children.size() ; i++)
8163 {
8164 Element *child = children[i];
8165 String tagName = child->getName();
8166 if (tagName == "fileset")
8167 {
8168 if (!parseFileSet(child, parent, fileSet))
8169 return false;
8170 }
8171 }
8172 return true;
8173 }
8175 private:
8177 FileSet fileSet;
8179 String commandOpt;
8180 String fileNameOpt;
8182 };
8187 /**
8188 * Strip an executable
8189 */
8190 class TaskStrip : public Task
8191 {
8192 public:
8194 TaskStrip(MakeBase &par) : Task(par)
8195 { type = TASK_STRIP; name = "strip"; }
8197 virtual ~TaskStrip()
8198 {}
8200 virtual bool execute()
8201 {
8202 String command = parent.eval(commandOpt, "strip");
8203 String fileName = parent.eval(fileNameOpt, "");
8204 String symFileName = parent.eval(symFileNameOpt, "");
8206 String fullName = parent.resolve(fileName);
8207 //trace("fullDir:%s", fullDir.c_str());
8208 String cmd;
8209 String outbuf, errbuf;
8211 if (symFileName.size()>0)
8212 {
8213 String symFullName = parent.resolve(symFileName);
8214 cmd = "objcopy --only-keep-debug ";
8215 cmd.append(getNativePath(fullName));
8216 cmd.append(" ");
8217 cmd.append(getNativePath(symFullName));
8218 if (!executeCommand(cmd, "", outbuf, errbuf))
8219 {
8220 error("<strip> symbol file failed : %s", errbuf.c_str());
8221 return false;
8222 }
8223 }
8225 cmd = command;
8226 cmd.append(getNativePath(fullName));
8227 if (!executeCommand(cmd, "", outbuf, errbuf))
8228 {
8229 error("<strip> failed : %s", errbuf.c_str());
8230 return false;
8231 }
8232 return true;
8233 }
8235 virtual bool parse(Element *elem)
8236 {
8237 if (!parent.getAttribute(elem, "command", commandOpt))
8238 return false;
8239 if (!parent.getAttribute(elem, "file", fileNameOpt))
8240 return false;
8241 if (!parent.getAttribute(elem, "symfile", symFileNameOpt))
8242 return false;
8243 if (fileNameOpt.size() == 0)
8244 {
8245 error("<strip> requires 'file=\"fileName\"' attribute");
8246 return false;
8247 }
8248 return true;
8249 }
8251 private:
8253 String commandOpt;
8254 String fileNameOpt;
8255 String symFileNameOpt;
8256 };
8259 /**
8260 *
8261 */
8262 class TaskTouch : public Task
8263 {
8264 public:
8266 TaskTouch(MakeBase &par) : Task(par)
8267 { type = TASK_TOUCH; name = "touch"; }
8269 virtual ~TaskTouch()
8270 {}
8272 virtual bool execute()
8273 {
8274 String fileName = parent.eval(fileNameOpt, "");
8276 String fullName = parent.resolve(fileName);
8277 String nativeFile = getNativePath(fullName);
8278 if (!isRegularFile(fullName) && !isDirectory(fullName))
8279 {
8280 // S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH
8281 int ret = creat(nativeFile.c_str(), 0666);
8282 if (ret != 0)
8283 {
8284 error("<touch> could not create '%s' : %s",
8285 nativeFile.c_str(), strerror(ret));
8286 return false;
8287 }
8288 return true;
8289 }
8290 int ret = utime(nativeFile.c_str(), (struct utimbuf *)0);
8291 if (ret != 0)
8292 {
8293 error("<touch> could not update the modification time for '%s' : %s",
8294 nativeFile.c_str(), strerror(ret));
8295 return false;
8296 }
8297 return true;
8298 }
8300 virtual bool parse(Element *elem)
8301 {
8302 //trace("touch parse");
8303 if (!parent.getAttribute(elem, "file", fileNameOpt))
8304 return false;
8305 if (fileNameOpt.size() == 0)
8306 {
8307 error("<touch> requires 'file=\"fileName\"' attribute");
8308 return false;
8309 }
8310 return true;
8311 }
8313 String fileNameOpt;
8314 };
8317 /**
8318 *
8319 */
8320 class TaskTstamp : public Task
8321 {
8322 public:
8324 TaskTstamp(MakeBase &par) : Task(par)
8325 { type = TASK_TSTAMP; name = "tstamp"; }
8327 virtual ~TaskTstamp()
8328 {}
8330 virtual bool execute()
8331 {
8332 return true;
8333 }
8335 virtual bool parse(Element *elem)
8336 {
8337 //trace("tstamp parse");
8338 return true;
8339 }
8340 };
8344 /**
8345 *
8346 */
8347 Task *Task::createTask(Element *elem, int lineNr)
8348 {
8349 String tagName = elem->getName();
8350 //trace("task:%s", tagName.c_str());
8351 Task *task = NULL;
8352 if (tagName == "cc")
8353 task = new TaskCC(parent);
8354 else if (tagName == "copy")
8355 task = new TaskCopy(parent);
8356 else if (tagName == "delete")
8357 task = new TaskDelete(parent);
8358 else if (tagName == "echo")
8359 task = new TaskEcho(parent);
8360 else if (tagName == "jar")
8361 task = new TaskJar(parent);
8362 else if (tagName == "javac")
8363 task = new TaskJavac(parent);
8364 else if (tagName == "link")
8365 task = new TaskLink(parent);
8366 else if (tagName == "makefile")
8367 task = new TaskMakeFile(parent);
8368 else if (tagName == "mkdir")
8369 task = new TaskMkDir(parent);
8370 else if (tagName == "msgfmt")
8371 task = new TaskMsgFmt(parent);
8372 else if (tagName == "pkg-config")
8373 task = new TaskPkgConfig(parent);
8374 else if (tagName == "ranlib")
8375 task = new TaskRanlib(parent);
8376 else if (tagName == "rc")
8377 task = new TaskRC(parent);
8378 else if (tagName == "sharedlib")
8379 task = new TaskSharedLib(parent);
8380 else if (tagName == "staticlib")
8381 task = new TaskStaticLib(parent);
8382 else if (tagName == "strip")
8383 task = new TaskStrip(parent);
8384 else if (tagName == "touch")
8385 task = new TaskTouch(parent);
8386 else if (tagName == "tstamp")
8387 task = new TaskTstamp(parent);
8388 else
8389 {
8390 error("Unknown task '%s'", tagName.c_str());
8391 return NULL;
8392 }
8394 task->setLine(lineNr);
8396 if (!task->parse(elem))
8397 {
8398 delete task;
8399 return NULL;
8400 }
8401 return task;
8402 }
8406 //########################################################################
8407 //# T A R G E T
8408 //########################################################################
8410 /**
8411 *
8412 */
8413 class Target : public MakeBase
8414 {
8416 public:
8418 /**
8419 *
8420 */
8421 Target(Make &par) : parent(par)
8422 { init(); }
8424 /**
8425 *
8426 */
8427 Target(const Target &other) : parent(other.parent)
8428 { init(); assign(other); }
8430 /**
8431 *
8432 */
8433 Target &operator=(const Target &other)
8434 { init(); assign(other); return *this; }
8436 /**
8437 *
8438 */
8439 virtual ~Target()
8440 { cleanup() ; }
8443 /**
8444 *
8445 */
8446 virtual Make &getParent()
8447 { return parent; }
8449 /**
8450 *
8451 */
8452 virtual String getName()
8453 { return name; }
8455 /**
8456 *
8457 */
8458 virtual void setName(const String &val)
8459 { name = val; }
8461 /**
8462 *
8463 */
8464 virtual String getDescription()
8465 { return description; }
8467 /**
8468 *
8469 */
8470 virtual void setDescription(const String &val)
8471 { description = val; }
8473 /**
8474 *
8475 */
8476 virtual void addDependency(const String &val)
8477 { deps.push_back(val); }
8479 /**
8480 *
8481 */
8482 virtual void parseDependencies(const String &val)
8483 { deps = tokenize(val, ", "); }
8485 /**
8486 *
8487 */
8488 virtual std::vector<String> &getDependencies()
8489 { return deps; }
8491 /**
8492 *
8493 */
8494 virtual String getIf()
8495 { return ifVar; }
8497 /**
8498 *
8499 */
8500 virtual void setIf(const String &val)
8501 { ifVar = val; }
8503 /**
8504 *
8505 */
8506 virtual String getUnless()
8507 { return unlessVar; }
8509 /**
8510 *
8511 */
8512 virtual void setUnless(const String &val)
8513 { unlessVar = val; }
8515 /**
8516 *
8517 */
8518 virtual void addTask(Task *val)
8519 { tasks.push_back(val); }
8521 /**
8522 *
8523 */
8524 virtual std::vector<Task *> &getTasks()
8525 { return tasks; }
8527 private:
8529 void init()
8530 {
8531 }
8533 void cleanup()
8534 {
8535 tasks.clear();
8536 }
8538 void assign(const Target &other)
8539 {
8540 //parent = other.parent;
8541 name = other.name;
8542 description = other.description;
8543 ifVar = other.ifVar;
8544 unlessVar = other.unlessVar;
8545 deps = other.deps;
8546 tasks = other.tasks;
8547 }
8549 Make &parent;
8551 String name;
8553 String description;
8555 String ifVar;
8557 String unlessVar;
8559 std::vector<String> deps;
8561 std::vector<Task *> tasks;
8563 };
8572 //########################################################################
8573 //# M A K E
8574 //########################################################################
8577 /**
8578 *
8579 */
8580 class Make : public MakeBase
8581 {
8583 public:
8585 /**
8586 *
8587 */
8588 Make()
8589 { init(); }
8591 /**
8592 *
8593 */
8594 Make(const Make &other)
8595 { assign(other); }
8597 /**
8598 *
8599 */
8600 Make &operator=(const Make &other)
8601 { assign(other); return *this; }
8603 /**
8604 *
8605 */
8606 virtual ~Make()
8607 { cleanup(); }
8609 /**
8610 *
8611 */
8612 virtual std::map<String, Target> &getTargets()
8613 { return targets; }
8616 /**
8617 *
8618 */
8619 virtual String version()
8620 { return BUILDTOOL_VERSION; }
8622 /**
8623 * Overload a <property>
8624 */
8625 virtual bool specifyProperty(const String &name,
8626 const String &value);
8628 /**
8629 *
8630 */
8631 virtual bool run();
8633 /**
8634 *
8635 */
8636 virtual bool run(const String &target);
8640 private:
8642 /**
8643 *
8644 */
8645 void init();
8647 /**
8648 *
8649 */
8650 void cleanup();
8652 /**
8653 *
8654 */
8655 void assign(const Make &other);
8657 /**
8658 *
8659 */
8660 bool executeTask(Task &task);
8663 /**
8664 *
8665 */
8666 bool executeTarget(Target &target,
8667 std::set<String> &targetsCompleted);
8670 /**
8671 *
8672 */
8673 bool execute();
8675 /**
8676 *
8677 */
8678 bool checkTargetDependencies(Target &prop,
8679 std::vector<String> &depList);
8681 /**
8682 *
8683 */
8684 bool parsePropertyFile(const String &fileName,
8685 const String &prefix);
8687 /**
8688 *
8689 */
8690 bool parseProperty(Element *elem);
8692 /**
8693 *
8694 */
8695 bool parseFile();
8697 /**
8698 *
8699 */
8700 std::vector<String> glob(const String &pattern);
8703 //###############
8704 //# Fields
8705 //###############
8707 String projectName;
8709 String currentTarget;
8711 String defaultTarget;
8713 String specifiedTarget;
8715 String baseDir;
8717 String description;
8719 //std::vector<Property> properties;
8721 std::map<String, Target> targets;
8723 std::vector<Task *> allTasks;
8725 std::map<String, String> specifiedProperties;
8727 };
8730 //########################################################################
8731 //# C L A S S M A I N T E N A N C E
8732 //########################################################################
8734 /**
8735 *
8736 */
8737 void Make::init()
8738 {
8739 uri = "build.xml";
8740 projectName = "";
8741 currentTarget = "";
8742 defaultTarget = "";
8743 specifiedTarget = "";
8744 baseDir = "";
8745 description = "";
8746 envPrefix = "env.";
8747 pcPrefix = "pc.";
8748 pccPrefix = "pcc.";
8749 pclPrefix = "pcl.";
8750 properties.clear();
8751 for (unsigned int i = 0 ; i < allTasks.size() ; i++)
8752 delete allTasks[i];
8753 allTasks.clear();
8754 }
8758 /**
8759 *
8760 */
8761 void Make::cleanup()
8762 {
8763 for (unsigned int i = 0 ; i < allTasks.size() ; i++)
8764 delete allTasks[i];
8765 allTasks.clear();
8766 }
8770 /**
8771 *
8772 */
8773 void Make::assign(const Make &other)
8774 {
8775 uri = other.uri;
8776 projectName = other.projectName;
8777 currentTarget = other.currentTarget;
8778 defaultTarget = other.defaultTarget;
8779 specifiedTarget = other.specifiedTarget;
8780 baseDir = other.baseDir;
8781 description = other.description;
8782 properties = other.properties;
8783 }
8787 //########################################################################
8788 //# U T I L I T Y T A S K S
8789 //########################################################################
8791 /**
8792 * Perform a file globbing
8793 */
8794 std::vector<String> Make::glob(const String &pattern)
8795 {
8796 std::vector<String> res;
8797 return res;
8798 }
8801 //########################################################################
8802 //# P U B L I C A P I
8803 //########################################################################
8807 /**
8808 *
8809 */
8810 bool Make::executeTarget(Target &target,
8811 std::set<String> &targetsCompleted)
8812 {
8814 String name = target.getName();
8816 //First get any dependencies for this target
8817 std::vector<String> deps = target.getDependencies();
8818 for (unsigned int i=0 ; i<deps.size() ; i++)
8819 {
8820 String dep = deps[i];
8821 //Did we do it already? Skip
8822 if (targetsCompleted.find(dep)!=targetsCompleted.end())
8823 continue;
8825 std::map<String, Target> &tgts =
8826 target.getParent().getTargets();
8827 std::map<String, Target>::iterator iter =
8828 tgts.find(dep);
8829 if (iter == tgts.end())
8830 {
8831 error("Target '%s' dependency '%s' not found",
8832 name.c_str(), dep.c_str());
8833 return false;
8834 }
8835 Target depTarget = iter->second;
8836 if (!executeTarget(depTarget, targetsCompleted))
8837 {
8838 return false;
8839 }
8840 }
8842 status("##### Target : %s\n##### %s", name.c_str(),
8843 target.getDescription().c_str());
8845 //Now let's do the tasks
8846 std::vector<Task *> &tasks = target.getTasks();
8847 for (unsigned int i=0 ; i<tasks.size() ; i++)
8848 {
8849 Task *task = tasks[i];
8850 status("--- %s / %s", name.c_str(), task->getName().c_str());
8851 if (!task->execute())
8852 {
8853 return false;
8854 }
8855 }
8857 targetsCompleted.insert(name);
8859 return true;
8860 }
8864 /**
8865 * Main execute() method. Start here and work
8866 * up the dependency tree
8867 */
8868 bool Make::execute()
8869 {
8870 status("######## EXECUTE");
8872 //Determine initial target
8873 if (specifiedTarget.size()>0)
8874 {
8875 currentTarget = specifiedTarget;
8876 }
8877 else if (defaultTarget.size()>0)
8878 {
8879 currentTarget = defaultTarget;
8880 }
8881 else
8882 {
8883 error("execute: no specified or default target requested");
8884 return false;
8885 }
8887 std::map<String, Target>::iterator iter =
8888 targets.find(currentTarget);
8889 if (iter == targets.end())
8890 {
8891 error("Initial target '%s' not found",
8892 currentTarget.c_str());
8893 return false;
8894 }
8896 //Now run
8897 Target target = iter->second;
8898 std::set<String> targetsCompleted;
8899 if (!executeTarget(target, targetsCompleted))
8900 {
8901 return false;
8902 }
8904 status("######## EXECUTE COMPLETE");
8905 return true;
8906 }
8911 /**
8912 *
8913 */
8914 bool Make::checkTargetDependencies(Target &target,
8915 std::vector<String> &depList)
8916 {
8917 String tgtName = target.getName().c_str();
8918 depList.push_back(tgtName);
8920 std::vector<String> deps = target.getDependencies();
8921 for (unsigned int i=0 ; i<deps.size() ; i++)
8922 {
8923 String dep = deps[i];
8924 //First thing entered was the starting Target
8925 if (dep == depList[0])
8926 {
8927 error("Circular dependency '%s' found at '%s'",
8928 dep.c_str(), tgtName.c_str());
8929 std::vector<String>::iterator diter;
8930 for (diter=depList.begin() ; diter!=depList.end() ; diter++)
8931 {
8932 error(" %s", diter->c_str());
8933 }
8934 return false;
8935 }
8937 std::map<String, Target> &tgts =
8938 target.getParent().getTargets();
8939 std::map<String, Target>::iterator titer = tgts.find(dep);
8940 if (titer == tgts.end())
8941 {
8942 error("Target '%s' dependency '%s' not found",
8943 tgtName.c_str(), dep.c_str());
8944 return false;
8945 }
8946 if (!checkTargetDependencies(titer->second, depList))
8947 {
8948 return false;
8949 }
8950 }
8951 return true;
8952 }
8958 static int getword(int pos, const String &inbuf, String &result)
8959 {
8960 int p = pos;
8961 int len = (int)inbuf.size();
8962 String val;
8963 while (p < len)
8964 {
8965 char ch = inbuf[p];
8966 if (!isalnum(ch) && ch!='.' && ch!='_')
8967 break;
8968 val.push_back(ch);
8969 p++;
8970 }
8971 result = val;
8972 return p;
8973 }
8978 /**
8979 *
8980 */
8981 bool Make::parsePropertyFile(const String &fileName,
8982 const String &prefix)
8983 {
8984 FILE *f = fopen(fileName.c_str(), "r");
8985 if (!f)
8986 {
8987 error("could not open property file %s", fileName.c_str());
8988 return false;
8989 }
8990 int linenr = 0;
8991 while (!feof(f))
8992 {
8993 char buf[256];
8994 if (!fgets(buf, 255, f))
8995 break;
8996 linenr++;
8997 String s = buf;
8998 s = trim(s);
8999 int len = s.size();
9000 if (len == 0)
9001 continue;
9002 if (s[0] == '#')
9003 continue;
9004 String key;
9005 String val;
9006 int p = 0;
9007 int p2 = getword(p, s, key);
9008 if (p2 <= p)
9009 {
9010 error("property file %s, line %d: expected keyword",
9011 fileName.c_str(), linenr);
9012 return false;
9013 }
9014 if (prefix.size() > 0)
9015 {
9016 key.insert(0, prefix);
9017 }
9019 //skip whitespace
9020 for (p=p2 ; p<len ; p++)
9021 if (!isspace(s[p]))
9022 break;
9024 if (p>=len || s[p]!='=')
9025 {
9026 error("property file %s, line %d: expected '='",
9027 fileName.c_str(), linenr);
9028 return false;
9029 }
9030 p++;
9032 //skip whitespace
9033 for ( ; p<len ; p++)
9034 if (!isspace(s[p]))
9035 break;
9037 /* This way expects a word after the =
9038 p2 = getword(p, s, val);
9039 if (p2 <= p)
9040 {
9041 error("property file %s, line %d: expected value",
9042 fileName.c_str(), linenr);
9043 return false;
9044 }
9045 */
9046 // This way gets the rest of the line after the =
9047 if (p>=len)
9048 {
9049 error("property file %s, line %d: expected value",
9050 fileName.c_str(), linenr);
9051 return false;
9052 }
9053 val = s.substr(p);
9054 if (key.size()==0)
9055 continue;
9056 //allow property to be set, even if val=""
9058 //trace("key:'%s' val:'%s'", key.c_str(), val.c_str());
9059 //See if we wanted to overload this property
9060 std::map<String, String>::iterator iter =
9061 specifiedProperties.find(key);
9062 if (iter!=specifiedProperties.end())
9063 {
9064 val = iter->second;
9065 status("overloading property '%s' = '%s'",
9066 key.c_str(), val.c_str());
9067 }
9068 properties[key] = val;
9069 }
9070 fclose(f);
9071 return true;
9072 }
9077 /**
9078 *
9079 */
9080 bool Make::parseProperty(Element *elem)
9081 {
9082 std::vector<Attribute> &attrs = elem->getAttributes();
9083 for (unsigned int i=0 ; i<attrs.size() ; i++)
9084 {
9085 String attrName = attrs[i].getName();
9086 String attrVal = attrs[i].getValue();
9088 if (attrName == "name")
9089 {
9090 String val;
9091 if (!getAttribute(elem, "value", val))
9092 return false;
9093 if (val.size() > 0)
9094 {
9095 properties[attrVal] = val;
9096 }
9097 else
9098 {
9099 if (!getAttribute(elem, "location", val))
9100 return false;
9101 //let the property exist, even if not defined
9102 properties[attrVal] = val;
9103 }
9104 //See if we wanted to overload this property
9105 std::map<String, String>::iterator iter =
9106 specifiedProperties.find(attrVal);
9107 if (iter != specifiedProperties.end())
9108 {
9109 val = iter->second;
9110 status("overloading property '%s' = '%s'",
9111 attrVal.c_str(), val.c_str());
9112 properties[attrVal] = val;
9113 }
9114 }
9115 else if (attrName == "file")
9116 {
9117 String prefix;
9118 if (!getAttribute(elem, "prefix", prefix))
9119 return false;
9120 if (prefix.size() > 0)
9121 {
9122 if (prefix[prefix.size()-1] != '.')
9123 prefix.push_back('.');
9124 }
9125 if (!parsePropertyFile(attrName, prefix))
9126 return false;
9127 }
9128 else if (attrName == "environment")
9129 {
9130 if (attrVal.find('.') != attrVal.npos)
9131 {
9132 error("environment prefix cannot have a '.' in it");
9133 return false;
9134 }
9135 envPrefix = attrVal;
9136 envPrefix.push_back('.');
9137 }
9138 else if (attrName == "pkg-config")
9139 {
9140 if (attrVal.find('.') != attrVal.npos)
9141 {
9142 error("pkg-config prefix cannot have a '.' in it");
9143 return false;
9144 }
9145 pcPrefix = attrVal;
9146 pcPrefix.push_back('.');
9147 }
9148 else if (attrName == "pkg-config-cflags")
9149 {
9150 if (attrVal.find('.') != attrVal.npos)
9151 {
9152 error("pkg-config-cflags prefix cannot have a '.' in it");
9153 return false;
9154 }
9155 pccPrefix = attrVal;
9156 pccPrefix.push_back('.');
9157 }
9158 else if (attrName == "pkg-config-libs")
9159 {
9160 if (attrVal.find('.') != attrVal.npos)
9161 {
9162 error("pkg-config-libs prefix cannot have a '.' in it");
9163 return false;
9164 }
9165 pclPrefix = attrVal;
9166 pclPrefix.push_back('.');
9167 }
9168 }
9170 return true;
9171 }
9176 /**
9177 *
9178 */
9179 bool Make::parseFile()
9180 {
9181 status("######## PARSE : %s", uri.getPath().c_str());
9183 setLine(0);
9185 Parser parser;
9186 Element *root = parser.parseFile(uri.getNativePath());
9187 if (!root)
9188 {
9189 error("Could not open %s for reading",
9190 uri.getNativePath().c_str());
9191 return false;
9192 }
9194 setLine(root->getLine());
9196 if (root->getChildren().size()==0 ||
9197 root->getChildren()[0]->getName()!="project")
9198 {
9199 error("Main xml element should be <project>");
9200 delete root;
9201 return false;
9202 }
9204 //########## Project attributes
9205 Element *project = root->getChildren()[0];
9206 String s = project->getAttribute("name");
9207 if (s.size() > 0)
9208 projectName = s;
9209 s = project->getAttribute("default");
9210 if (s.size() > 0)
9211 defaultTarget = s;
9212 s = project->getAttribute("basedir");
9213 if (s.size() > 0)
9214 baseDir = s;
9216 //######### PARSE MEMBERS
9217 std::vector<Element *> children = project->getChildren();
9218 for (unsigned int i=0 ; i<children.size() ; i++)
9219 {
9220 Element *elem = children[i];
9221 setLine(elem->getLine());
9222 String tagName = elem->getName();
9224 //########## DESCRIPTION
9225 if (tagName == "description")
9226 {
9227 description = parser.trim(elem->getValue());
9228 }
9230 //######### PROPERTY
9231 else if (tagName == "property")
9232 {
9233 if (!parseProperty(elem))
9234 return false;
9235 }
9237 //######### TARGET
9238 else if (tagName == "target")
9239 {
9240 String tname = elem->getAttribute("name");
9241 String tdesc = elem->getAttribute("description");
9242 String tdeps = elem->getAttribute("depends");
9243 String tif = elem->getAttribute("if");
9244 String tunless = elem->getAttribute("unless");
9245 Target target(*this);
9246 target.setName(tname);
9247 target.setDescription(tdesc);
9248 target.parseDependencies(tdeps);
9249 target.setIf(tif);
9250 target.setUnless(tunless);
9251 std::vector<Element *> telems = elem->getChildren();
9252 for (unsigned int i=0 ; i<telems.size() ; i++)
9253 {
9254 Element *telem = telems[i];
9255 Task breeder(*this);
9256 Task *task = breeder.createTask(telem, telem->getLine());
9257 if (!task)
9258 return false;
9259 allTasks.push_back(task);
9260 target.addTask(task);
9261 }
9263 //Check name
9264 if (tname.size() == 0)
9265 {
9266 error("no name for target");
9267 return false;
9268 }
9269 //Check for duplicate name
9270 if (targets.find(tname) != targets.end())
9271 {
9272 error("target '%s' already defined", tname.c_str());
9273 return false;
9274 }
9275 //more work than targets[tname]=target, but avoids default allocator
9276 targets.insert(std::make_pair<String, Target>(tname, target));
9277 }
9278 //######### none of the above
9279 else
9280 {
9281 error("unknown toplevel tag: <%s>", tagName.c_str());
9282 return false;
9283 }
9285 }
9287 std::map<String, Target>::iterator iter;
9288 for (iter = targets.begin() ; iter!= targets.end() ; iter++)
9289 {
9290 Target tgt = iter->second;
9291 std::vector<String> depList;
9292 if (!checkTargetDependencies(tgt, depList))
9293 {
9294 return false;
9295 }
9296 }
9299 delete root;
9300 status("######## PARSE COMPLETE");
9301 return true;
9302 }
9305 /**
9306 * Overload a <property>
9307 */
9308 bool Make::specifyProperty(const String &name, const String &value)
9309 {
9310 if (specifiedProperties.find(name) != specifiedProperties.end())
9311 {
9312 error("Property %s already specified", name.c_str());
9313 return false;
9314 }
9315 specifiedProperties[name] = value;
9316 return true;
9317 }
9321 /**
9322 *
9323 */
9324 bool Make::run()
9325 {
9326 if (!parseFile())
9327 return false;
9329 if (!execute())
9330 return false;
9332 return true;
9333 }
9338 /**
9339 * Get a formatted MM:SS.sss time elapsed string
9340 */
9341 static String
9342 timeDiffString(struct timeval &x, struct timeval &y)
9343 {
9344 long microsX = x.tv_usec;
9345 long secondsX = x.tv_sec;
9346 long microsY = y.tv_usec;
9347 long secondsY = y.tv_sec;
9348 if (microsX < microsY)
9349 {
9350 microsX += 1000000;
9351 secondsX -= 1;
9352 }
9354 int seconds = (int)(secondsX - secondsY);
9355 int millis = (int)((microsX - microsY)/1000);
9357 int minutes = seconds/60;
9358 seconds -= minutes*60;
9359 char buf[80];
9360 snprintf(buf, 79, "%dm %d.%03ds", minutes, seconds, millis);
9361 String ret = buf;
9362 return ret;
9364 }
9366 /**
9367 *
9368 */
9369 bool Make::run(const String &target)
9370 {
9371 status("####################################################");
9372 status("# %s", version().c_str());
9373 status("####################################################");
9374 struct timeval timeStart, timeEnd;
9375 ::gettimeofday(&timeStart, NULL);
9376 specifiedTarget = target;
9377 if (!run())
9378 return false;
9379 ::gettimeofday(&timeEnd, NULL);
9380 String timeStr = timeDiffString(timeEnd, timeStart);
9381 status("####################################################");
9382 status("# BuildTool Completed : %s", timeStr.c_str());
9383 status("####################################################");
9384 return true;
9385 }
9393 }// namespace buildtool
9394 //########################################################################
9395 //# M A I N
9396 //########################################################################
9398 typedef buildtool::String String;
9400 /**
9401 * Format an error message in printf() style
9402 */
9403 static void error(const char *fmt, ...)
9404 {
9405 va_list ap;
9406 va_start(ap, fmt);
9407 fprintf(stderr, "BuildTool error: ");
9408 vfprintf(stderr, fmt, ap);
9409 fprintf(stderr, "\n");
9410 va_end(ap);
9411 }
9414 static bool parseProperty(const String &s, String &name, String &val)
9415 {
9416 int len = s.size();
9417 int i;
9418 for (i=0 ; i<len ; i++)
9419 {
9420 char ch = s[i];
9421 if (ch == '=')
9422 break;
9423 name.push_back(ch);
9424 }
9425 if (i>=len || s[i]!='=')
9426 {
9427 error("property requires -Dname=value");
9428 return false;
9429 }
9430 i++;
9431 for ( ; i<len ; i++)
9432 {
9433 char ch = s[i];
9434 val.push_back(ch);
9435 }
9436 return true;
9437 }
9440 /**
9441 * Compare a buffer with a key, for the length of the key
9442 */
9443 static bool sequ(const String &buf, const char *key)
9444 {
9445 int len = buf.size();
9446 for (int i=0 ; key[i] && i<len ; i++)
9447 {
9448 if (key[i] != buf[i])
9449 return false;
9450 }
9451 return true;
9452 }
9454 static void usage(int argc, char **argv)
9455 {
9456 printf("usage:\n");
9457 printf(" %s [options] [target]\n", argv[0]);
9458 printf("Options:\n");
9459 printf(" -help, -h print this message\n");
9460 printf(" -version print the version information and exit\n");
9461 printf(" -file <file> use given buildfile\n");
9462 printf(" -f <file> ''\n");
9463 printf(" -D<property>=<value> use value for given property\n");
9464 }
9469 /**
9470 * Parse the command-line args, get our options,
9471 * and run this thing
9472 */
9473 static bool parseOptions(int argc, char **argv)
9474 {
9475 if (argc < 1)
9476 {
9477 error("Cannot parse arguments");
9478 return false;
9479 }
9481 buildtool::Make make;
9483 String target;
9485 //char *progName = argv[0];
9486 for (int i=1 ; i<argc ; i++)
9487 {
9488 String arg = argv[i];
9489 if (arg.size()>1 && arg[0]=='-')
9490 {
9491 if (arg == "-h" || arg == "-help")
9492 {
9493 usage(argc,argv);
9494 return true;
9495 }
9496 else if (arg == "-version")
9497 {
9498 printf("%s", make.version().c_str());
9499 return true;
9500 }
9501 else if (arg == "-f" || arg == "-file")
9502 {
9503 if (i>=argc)
9504 {
9505 usage(argc, argv);
9506 return false;
9507 }
9508 i++; //eat option
9509 make.setURI(argv[i]);
9510 }
9511 else if (arg.size()>2 && sequ(arg, "-D"))
9512 {
9513 String s = arg.substr(2, arg.size());
9514 String name, value;
9515 if (!parseProperty(s, name, value))
9516 {
9517 usage(argc, argv);
9518 return false;
9519 }
9520 if (!make.specifyProperty(name, value))
9521 return false;
9522 }
9523 else
9524 {
9525 error("Unknown option:%s", arg.c_str());
9526 return false;
9527 }
9528 }
9529 else
9530 {
9531 if (target.size()>0)
9532 {
9533 error("only one initial target");
9534 usage(argc, argv);
9535 return false;
9536 }
9537 target = arg;
9538 }
9539 }
9541 //We have the options. Now execute them
9542 if (!make.run(target))
9543 return false;
9545 return true;
9546 }
9551 /*
9552 static bool runMake()
9553 {
9554 buildtool::Make make;
9555 if (!make.run())
9556 return false;
9557 return true;
9558 }
9561 static bool pkgConfigTest()
9562 {
9563 buildtool::PkgConfig pkgConfig;
9564 if (!pkgConfig.readFile("gtk+-2.0.pc"))
9565 return false;
9566 return true;
9567 }
9571 static bool depTest()
9572 {
9573 buildtool::DepTool deptool;
9574 deptool.setSourceDirectory("/dev/ink/inkscape/src");
9575 if (!deptool.generateDependencies("build.dep"))
9576 return false;
9577 std::vector<buildtool::FileRec> res =
9578 deptool.loadDepFile("build.dep");
9579 if (res.size() == 0)
9580 return false;
9581 return true;
9582 }
9584 static bool popenTest()
9585 {
9586 buildtool::Make make;
9587 buildtool::String out, err;
9588 bool ret = make.executeCommand("gcc xx.cpp", "", out, err);
9589 printf("Popen test:%d '%s' '%s'\n", ret, out.c_str(), err.c_str());
9590 return true;
9591 }
9594 static bool propFileTest()
9595 {
9596 buildtool::Make make;
9597 make.parsePropertyFile("test.prop", "test.");
9598 return true;
9599 }
9600 */
9602 int main(int argc, char **argv)
9603 {
9605 if (!parseOptions(argc, argv))
9606 return 1;
9607 /*
9608 if (!popenTest())
9609 return 1;
9611 if (!depTest())
9612 return 1;
9613 if (!propFileTest())
9614 return 1;
9615 if (runMake())
9616 return 1;
9617 */
9618 return 0;
9619 }
9622 //########################################################################
9623 //# E N D
9624 //########################################################################