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.3"
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 //fprintf(stdout, " ");
3555 va_start(args,fmt);
3556 vfprintf(stdout, fmt, args);
3557 va_end(args);
3558 fprintf(stdout, "\n");
3559 fflush(stdout);
3560 }
3563 /**
3564 * Print a printf()-like formatted trace message
3565 */
3566 void MakeBase::trace(const char *fmt, ...)
3567 {
3568 va_list args;
3569 fprintf(stdout, "Make: ");
3570 va_start(args,fmt);
3571 vfprintf(stdout, fmt, args);
3572 va_end(args) ;
3573 fprintf(stdout, "\n");
3574 fflush(stdout);
3575 }
3579 /**
3580 * Resolve another path relative to this one
3581 */
3582 String MakeBase::resolve(const String &otherPath)
3583 {
3584 URI otherURI(otherPath);
3585 URI fullURI = uri.resolve(otherURI);
3586 String ret = fullURI.toString();
3587 return ret;
3588 }
3592 /**
3593 * Check if a given string matches a given regex pattern
3594 */
3595 bool MakeBase::regexMatch(const String &str, const String &pattern)
3596 {
3597 const TRexChar *terror = NULL;
3598 const TRexChar *cpat = pattern.c_str();
3599 TRex *expr = trex_compile(cpat, &terror);
3600 if (!expr)
3601 {
3602 if (!terror)
3603 terror = "undefined";
3604 error("compilation error [%s]!\n", terror);
3605 return false;
3606 }
3608 bool ret = true;
3610 const TRexChar *cstr = str.c_str();
3611 if (trex_match(expr, cstr))
3612 {
3613 ret = true;
3614 }
3615 else
3616 {
3617 ret = false;
3618 }
3620 trex_free(expr);
3622 return ret;
3623 }
3625 /**
3626 * Return the suffix, if any, of a file name
3627 */
3628 String MakeBase::getSuffix(const String &fname)
3629 {
3630 if (fname.size() < 2)
3631 return "";
3632 unsigned int pos = fname.find_last_of('.');
3633 if (pos == fname.npos)
3634 return "";
3635 pos++;
3636 String res = fname.substr(pos, fname.size()-pos);
3637 //trace("suffix:%s", res.c_str());
3638 return res;
3639 }
3643 /**
3644 * Break up a string into substrings delimited the characters
3645 * in delimiters. Null-length substrings are ignored
3646 */
3647 std::vector<String> MakeBase::tokenize(const String &str,
3648 const String &delimiters)
3649 {
3651 std::vector<String> res;
3652 char *del = (char *)delimiters.c_str();
3653 String dmp;
3654 for (unsigned int i=0 ; i<str.size() ; i++)
3655 {
3656 char ch = str[i];
3657 char *p = (char *)0;
3658 for (p=del ; *p ; p++)
3659 if (*p == ch)
3660 break;
3661 if (*p)
3662 {
3663 if (dmp.size() > 0)
3664 {
3665 res.push_back(dmp);
3666 dmp.clear();
3667 }
3668 }
3669 else
3670 {
3671 dmp.push_back(ch);
3672 }
3673 }
3674 //Add tail
3675 if (dmp.size() > 0)
3676 {
3677 res.push_back(dmp);
3678 dmp.clear();
3679 }
3681 return res;
3682 }
3686 /**
3687 * replace runs of whitespace with a single space
3688 */
3689 String MakeBase::strip(const String &s)
3690 {
3691 int len = s.size();
3692 String stripped;
3693 for (int i = 0 ; i<len ; i++)
3694 {
3695 char ch = s[i];
3696 if (isspace(ch))
3697 {
3698 stripped.push_back(' ');
3699 for ( ; i<len ; i++)
3700 {
3701 ch = s[i];
3702 if (!isspace(ch))
3703 {
3704 stripped.push_back(ch);
3705 break;
3706 }
3707 }
3708 }
3709 else
3710 {
3711 stripped.push_back(ch);
3712 }
3713 }
3714 return stripped;
3715 }
3717 /**
3718 * remove leading whitespace from each line
3719 */
3720 String MakeBase::leftJustify(const String &s)
3721 {
3722 String out;
3723 int len = s.size();
3724 for (int i = 0 ; i<len ; )
3725 {
3726 char ch;
3727 //Skip to first visible character
3728 while (i<len)
3729 {
3730 ch = s[i];
3731 if (ch == '\n' || ch == '\r'
3732 || !isspace(ch))
3733 break;
3734 i++;
3735 }
3736 //Copy the rest of the line
3737 while (i<len)
3738 {
3739 ch = s[i];
3740 if (ch == '\n' || ch == '\r')
3741 {
3742 if (ch != '\r')
3743 out.push_back('\n');
3744 i++;
3745 break;
3746 }
3747 else
3748 {
3749 out.push_back(ch);
3750 }
3751 i++;
3752 }
3753 }
3754 return out;
3755 }
3758 /**
3759 * Removes whitespace from beginning and end of a string
3760 */
3761 String MakeBase::trim(const String &s)
3762 {
3763 if (s.size() < 1)
3764 return s;
3766 //Find first non-ws char
3767 unsigned int begin = 0;
3768 for ( ; begin < s.size() ; begin++)
3769 {
3770 if (!isspace(s[begin]))
3771 break;
3772 }
3774 //Find first non-ws char, going in reverse
3775 unsigned int end = s.size() - 1;
3776 for ( ; end > begin ; end--)
3777 {
3778 if (!isspace(s[end]))
3779 break;
3780 }
3781 //trace("begin:%d end:%d", begin, end);
3783 String res = s.substr(begin, end-begin+1);
3784 return res;
3785 }
3788 /**
3789 * Return a lower case version of the given string
3790 */
3791 String MakeBase::toLower(const String &s)
3792 {
3793 if (s.size()==0)
3794 return s;
3796 String ret;
3797 for(unsigned int i=0; i<s.size() ; i++)
3798 {
3799 ret.push_back(tolower(s[i]));
3800 }
3801 return ret;
3802 }
3805 /**
3806 * Return the native format of the canonical
3807 * path which we store
3808 */
3809 String MakeBase::getNativePath(const String &path)
3810 {
3811 #ifdef __WIN32__
3812 String npath;
3813 unsigned int firstChar = 0;
3814 if (path.size() >= 3)
3815 {
3816 if (path[0] == '/' &&
3817 isalpha(path[1]) &&
3818 path[2] == ':')
3819 firstChar++;
3820 }
3821 for (unsigned int i=firstChar ; i<path.size() ; i++)
3822 {
3823 char ch = path[i];
3824 if (ch == '/')
3825 npath.push_back('\\');
3826 else
3827 npath.push_back(ch);
3828 }
3829 return npath;
3830 #else
3831 return path;
3832 #endif
3833 }
3836 #ifdef __WIN32__
3837 #include <tchar.h>
3839 static String win32LastError()
3840 {
3842 DWORD dw = GetLastError();
3844 LPVOID str;
3845 FormatMessage(
3846 FORMAT_MESSAGE_ALLOCATE_BUFFER |
3847 FORMAT_MESSAGE_FROM_SYSTEM,
3848 NULL,
3849 dw,
3850 0,
3851 (LPTSTR) &str,
3852 0, NULL );
3853 LPTSTR p = _tcschr((const char *)str, _T('\r'));
3854 if(p != NULL)
3855 { // lose CRLF
3856 *p = _T('\0');
3857 }
3858 String ret = (char *)str;
3859 LocalFree(str);
3861 return ret;
3862 }
3863 #endif
3867 /**
3868 * Execute a system call, using pipes to send data to the
3869 * program's stdin, and reading stdout and stderr.
3870 */
3871 bool MakeBase::executeCommand(const String &command,
3872 const String &inbuf,
3873 String &outbuf,
3874 String &errbuf)
3875 {
3877 status("============ cmd ============\n%s\n=============================",
3878 command.c_str());
3880 outbuf.clear();
3881 errbuf.clear();
3883 #ifdef __WIN32__
3885 /*
3886 I really hate having win32 code in this program, but the
3887 read buffer in command.com and cmd.exe are just too small
3888 for the large commands we need for compiling and linking.
3889 */
3891 bool ret = true;
3893 //# Allocate a separate buffer for safety
3894 char *paramBuf = new char[command.size() + 1];
3895 if (!paramBuf)
3896 {
3897 error("executeCommand cannot allocate command buffer");
3898 return false;
3899 }
3900 strcpy(paramBuf, (char *)command.c_str());
3902 //# Go to http://msdn2.microsoft.com/en-us/library/ms682499.aspx
3903 //# to see how Win32 pipes work
3905 //# Create pipes
3906 SECURITY_ATTRIBUTES saAttr;
3907 saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
3908 saAttr.bInheritHandle = TRUE;
3909 saAttr.lpSecurityDescriptor = NULL;
3910 HANDLE stdinRead, stdinWrite;
3911 HANDLE stdoutRead, stdoutWrite;
3912 HANDLE stderrRead, stderrWrite;
3913 if (!CreatePipe(&stdinRead, &stdinWrite, &saAttr, 0))
3914 {
3915 error("executeProgram: could not create pipe");
3916 delete[] paramBuf;
3917 return false;
3918 }
3919 SetHandleInformation(stdinWrite, HANDLE_FLAG_INHERIT, 0);
3920 if (!CreatePipe(&stdoutRead, &stdoutWrite, &saAttr, 0))
3921 {
3922 error("executeProgram: could not create pipe");
3923 delete[] paramBuf;
3924 return false;
3925 }
3926 SetHandleInformation(stdoutRead, HANDLE_FLAG_INHERIT, 0);
3927 if (!CreatePipe(&stderrRead, &stderrWrite, &saAttr, 0))
3928 {
3929 error("executeProgram: could not create pipe");
3930 delete[] paramBuf;
3931 return false;
3932 }
3933 SetHandleInformation(stderrRead, HANDLE_FLAG_INHERIT, 0);
3935 // Create the process
3936 STARTUPINFO siStartupInfo;
3937 PROCESS_INFORMATION piProcessInfo;
3938 memset(&siStartupInfo, 0, sizeof(siStartupInfo));
3939 memset(&piProcessInfo, 0, sizeof(piProcessInfo));
3940 siStartupInfo.cb = sizeof(siStartupInfo);
3941 siStartupInfo.hStdError = stderrWrite;
3942 siStartupInfo.hStdOutput = stdoutWrite;
3943 siStartupInfo.hStdInput = stdinRead;
3944 siStartupInfo.dwFlags |= STARTF_USESTDHANDLES;
3946 if (!CreateProcess(NULL, paramBuf, NULL, NULL, true,
3947 0, NULL, NULL, &siStartupInfo,
3948 &piProcessInfo))
3949 {
3950 error("executeCommand : could not create process : %s",
3951 win32LastError().c_str());
3952 ret = false;
3953 }
3955 delete[] paramBuf;
3957 DWORD bytesWritten;
3958 if (inbuf.size()>0 &&
3959 !WriteFile(stdinWrite, inbuf.c_str(), inbuf.size(),
3960 &bytesWritten, NULL))
3961 {
3962 error("executeCommand: could not write to pipe");
3963 return false;
3964 }
3965 if (!CloseHandle(stdinWrite))
3966 {
3967 error("executeCommand: could not close write pipe");
3968 return false;
3969 }
3970 if (!CloseHandle(stdoutWrite))
3971 {
3972 error("executeCommand: could not close read pipe");
3973 return false;
3974 }
3975 if (!CloseHandle(stderrWrite))
3976 {
3977 error("executeCommand: could not close read pipe");
3978 return false;
3979 }
3981 bool lastLoop = false;
3982 while (true)
3983 {
3984 DWORD avail;
3985 DWORD bytesRead;
3986 char readBuf[4096];
3988 //trace("## stderr");
3989 PeekNamedPipe(stderrRead, NULL, 0, NULL, &avail, NULL);
3990 if (avail > 0)
3991 {
3992 bytesRead = 0;
3993 if (avail>4096) avail = 4096;
3994 ReadFile(stderrRead, readBuf, avail, &bytesRead, NULL);
3995 if (bytesRead > 0)
3996 {
3997 for (unsigned int i=0 ; i<bytesRead ; i++)
3998 errbuf.push_back(readBuf[i]);
3999 }
4000 }
4002 //trace("## stdout");
4003 PeekNamedPipe(stdoutRead, NULL, 0, NULL, &avail, NULL);
4004 if (avail > 0)
4005 {
4006 bytesRead = 0;
4007 if (avail>4096) avail = 4096;
4008 ReadFile(stdoutRead, readBuf, avail, &bytesRead, NULL);
4009 if (bytesRead > 0)
4010 {
4011 for (unsigned int i=0 ; i<bytesRead ; i++)
4012 outbuf.push_back(readBuf[i]);
4013 }
4014 }
4016 //Was this the final check after program done?
4017 if (lastLoop)
4018 break;
4020 DWORD exitCode;
4021 GetExitCodeProcess(piProcessInfo.hProcess, &exitCode);
4022 if (exitCode != STILL_ACTIVE)
4023 lastLoop = true;
4025 Sleep(10);
4026 }
4027 //trace("outbuf:%s", outbuf.c_str());
4028 if (!CloseHandle(stdoutRead))
4029 {
4030 error("executeCommand: could not close read pipe");
4031 return false;
4032 }
4033 if (!CloseHandle(stderrRead))
4034 {
4035 error("executeCommand: could not close read pipe");
4036 return false;
4037 }
4039 DWORD exitCode;
4040 GetExitCodeProcess(piProcessInfo.hProcess, &exitCode);
4041 //trace("exit code:%d", exitCode);
4042 if (exitCode != 0)
4043 {
4044 ret = false;
4045 }
4047 CloseHandle(piProcessInfo.hProcess);
4048 CloseHandle(piProcessInfo.hThread);
4050 return ret;
4052 #else //do it unix-style
4054 String pipeCommand = command;
4055 pipeCommand.append(" 2>&1");
4056 String s;
4057 FILE *f = popen(pipeCommand.c_str(), "r");
4058 int errnum = 0;
4059 if (f)
4060 {
4061 while (true)
4062 {
4063 int ch = fgetc(f);
4064 if (ch < 0)
4065 break;
4066 s.push_back((char)ch);
4067 }
4068 errnum = pclose(f);
4069 }
4070 outbuf = s;
4071 if (errnum != 0)
4072 {
4073 error("exec of command '%s' failed : %s",
4074 command.c_str(), strerror(errno));
4075 return false;
4076 }
4077 else
4078 return true;
4080 #endif
4081 }
4086 bool MakeBase::listDirectories(const String &baseName,
4087 const String &dirName,
4088 std::vector<String> &res)
4089 {
4090 res.push_back(dirName);
4091 String fullPath = baseName;
4092 if (dirName.size()>0)
4093 {
4094 fullPath.append("/");
4095 fullPath.append(dirName);
4096 }
4097 DIR *dir = opendir(fullPath.c_str());
4098 while (true)
4099 {
4100 struct dirent *de = readdir(dir);
4101 if (!de)
4102 break;
4104 //Get the directory member name
4105 String s = de->d_name;
4106 if (s.size() == 0 || s[0] == '.')
4107 continue;
4108 String childName = dirName;
4109 childName.append("/");
4110 childName.append(s);
4112 String fullChildPath = baseName;
4113 fullChildPath.append("/");
4114 fullChildPath.append(childName);
4115 struct stat finfo;
4116 String childNative = getNativePath(fullChildPath);
4117 if (stat(childNative.c_str(), &finfo)<0)
4118 {
4119 error("cannot stat file:%s", childNative.c_str());
4120 }
4121 else if (S_ISDIR(finfo.st_mode))
4122 {
4123 //trace("directory: %s", childName.c_str());
4124 if (!listDirectories(baseName, childName, res))
4125 return false;
4126 }
4127 }
4128 closedir(dir);
4130 return true;
4131 }
4134 bool MakeBase::listFiles(const String &baseDir,
4135 const String &dirName,
4136 std::vector<String> &res)
4137 {
4138 String fullDir = baseDir;
4139 if (dirName.size()>0)
4140 {
4141 fullDir.append("/");
4142 fullDir.append(dirName);
4143 }
4144 String dirNative = getNativePath(fullDir);
4146 std::vector<String> subdirs;
4147 DIR *dir = opendir(dirNative.c_str());
4148 if (!dir)
4149 {
4150 error("Could not open directory %s : %s",
4151 dirNative.c_str(), strerror(errno));
4152 return false;
4153 }
4154 while (true)
4155 {
4156 struct dirent *de = readdir(dir);
4157 if (!de)
4158 break;
4160 //Get the directory member name
4161 String s = de->d_name;
4162 if (s.size() == 0 || s[0] == '.')
4163 continue;
4164 String childName;
4165 if (dirName.size()>0)
4166 {
4167 childName.append(dirName);
4168 childName.append("/");
4169 }
4170 childName.append(s);
4171 String fullChild = baseDir;
4172 fullChild.append("/");
4173 fullChild.append(childName);
4175 if (isDirectory(fullChild))
4176 {
4177 //trace("directory: %s", childName.c_str());
4178 if (!listFiles(baseDir, childName, res))
4179 return false;
4180 continue;
4181 }
4182 else if (!isRegularFile(fullChild))
4183 {
4184 error("unknown file:%s", childName.c_str());
4185 return false;
4186 }
4188 //all done!
4189 res.push_back(childName);
4191 }
4192 closedir(dir);
4194 return true;
4195 }
4198 /**
4199 * Several different classes extend MakeBase. By "propRef", we mean
4200 * the one holding the properties. Likely "Make" itself
4201 */
4202 bool MakeBase::listFiles(MakeBase &propRef, FileSet &fileSet)
4203 {
4204 //before doing the list, resolve any property references
4205 //that might have been specified in the directory name, such as ${src}
4206 String fsDir = fileSet.getDirectory();
4207 String dir;
4208 if (!propRef.getSubstitutions(fsDir, dir))
4209 return false;
4210 String baseDir = propRef.resolve(dir);
4211 std::vector<String> fileList;
4212 if (!listFiles(baseDir, "", fileList))
4213 return false;
4215 std::vector<String> includes = fileSet.getIncludes();
4216 std::vector<String> excludes = fileSet.getExcludes();
4218 std::vector<String> incs;
4219 std::vector<String>::iterator iter;
4221 std::sort(fileList.begin(), fileList.end());
4223 //If there are <includes>, then add files to the output
4224 //in the order of the include list
4225 if (includes.size()==0)
4226 incs = fileList;
4227 else
4228 {
4229 for (iter = includes.begin() ; iter != includes.end() ; iter++)
4230 {
4231 String &pattern = *iter;
4232 std::vector<String>::iterator siter;
4233 for (siter = fileList.begin() ; siter != fileList.end() ; siter++)
4234 {
4235 String s = *siter;
4236 if (regexMatch(s, pattern))
4237 {
4238 //trace("INCLUDED:%s", s.c_str());
4239 incs.push_back(s);
4240 }
4241 }
4242 }
4243 }
4245 //Now trim off the <excludes>
4246 std::vector<String> res;
4247 for (iter = incs.begin() ; iter != incs.end() ; iter++)
4248 {
4249 String s = *iter;
4250 bool skipme = false;
4251 std::vector<String>::iterator siter;
4252 for (siter = excludes.begin() ; siter != excludes.end() ; siter++)
4253 {
4254 String &pattern = *siter;
4255 if (regexMatch(s, pattern))
4256 {
4257 //trace("EXCLUDED:%s", s.c_str());
4258 skipme = true;
4259 break;
4260 }
4261 }
4262 if (!skipme)
4263 res.push_back(s);
4264 }
4266 fileSet.setFiles(res);
4268 return true;
4269 }
4272 /**
4273 * 0 == all, 1 = cflags, 2 = libs
4274 */
4275 bool MakeBase::pkgConfigRecursive(const String packageName,
4276 const String &path,
4277 const String &prefix,
4278 int query,
4279 String &result,
4280 std::set<String> &deplist)
4281 {
4282 PkgConfig pkgConfig;
4283 if (path.size() > 0)
4284 pkgConfig.setPath(path);
4285 if (prefix.size() > 0)
4286 pkgConfig.setPrefix(prefix);
4287 if (!pkgConfig.query(packageName))
4288 return false;
4289 if (query == 0)
4290 result = pkgConfig.getAll();
4291 else if (query == 1)
4292 result = pkgConfig.getCflags();
4293 else
4294 result = pkgConfig.getLibs();
4295 deplist.insert(packageName);
4296 std::vector<String> list = pkgConfig.getRequireList();
4297 for (unsigned int i = 0 ; i<list.size() ; i++)
4298 {
4299 String depPkgName = list[i];
4300 if (deplist.find(depPkgName) != deplist.end())
4301 continue;
4302 String val;
4303 if (!pkgConfigRecursive(depPkgName, path, prefix, query, val, deplist))
4304 {
4305 error("Based on 'requires' attribute of package '%s'", packageName.c_str());
4306 return false;
4307 }
4308 result.append(" ");
4309 result.append(val);
4310 }
4312 return true;
4313 }
4315 bool MakeBase::pkgConfigQuery(const String &packageName, int query, String &result)
4316 {
4317 std::set<String> deplist;
4318 String path = getProperty("pkg-config-path");
4319 if (path.size()>0)
4320 path = resolve(path);
4321 String prefix = getProperty("pkg-config-prefix");
4322 String val;
4323 if (!pkgConfigRecursive(packageName, path, prefix, query, val, deplist))
4324 return false;
4325 result = val;
4326 return true;
4327 }
4331 /**
4332 * replace a variable ref like ${a} with a value
4333 */
4334 bool MakeBase::lookupProperty(const String &propertyName, String &result)
4335 {
4336 String varname = propertyName;
4337 if (envPrefix.size() > 0 &&
4338 varname.compare(0, envPrefix.size(), envPrefix) == 0)
4339 {
4340 varname = varname.substr(envPrefix.size());
4341 char *envstr = getenv(varname.c_str());
4342 if (!envstr)
4343 {
4344 error("environment variable '%s' not defined", varname.c_str());
4345 return false;
4346 }
4347 result = envstr;
4348 }
4349 else if (pcPrefix.size() > 0 &&
4350 varname.compare(0, pcPrefix.size(), pcPrefix) == 0)
4351 {
4352 varname = varname.substr(pcPrefix.size());
4353 String val;
4354 if (!pkgConfigQuery(varname, 0, val))
4355 return false;
4356 result = val;
4357 }
4358 else if (pccPrefix.size() > 0 &&
4359 varname.compare(0, pccPrefix.size(), pccPrefix) == 0)
4360 {
4361 varname = varname.substr(pccPrefix.size());
4362 String val;
4363 if (!pkgConfigQuery(varname, 1, val))
4364 return false;
4365 result = val;
4366 }
4367 else if (pclPrefix.size() > 0 &&
4368 varname.compare(0, pclPrefix.size(), pclPrefix) == 0)
4369 {
4370 varname = varname.substr(pclPrefix.size());
4371 String val;
4372 if (!pkgConfigQuery(varname, 2, val))
4373 return false;
4374 result = val;
4375 }
4376 else
4377 {
4378 std::map<String, String>::iterator iter;
4379 iter = properties.find(varname);
4380 if (iter != properties.end())
4381 {
4382 result = iter->second;
4383 }
4384 else
4385 {
4386 error("property '%s' not found", varname.c_str());
4387 return false;
4388 }
4389 }
4390 return true;
4391 }
4396 /**
4397 * Analyse a string, looking for any substitutions or other
4398 * things that need resolution
4399 */
4400 bool MakeBase::getSubstitutionsRecursive(const String &str,
4401 String &result, int depth)
4402 {
4403 if (depth > 10)
4404 {
4405 error("nesting of substitutions too deep (>10) for '%s'",
4406 str.c_str());
4407 return false;
4408 }
4409 String s = trim(str);
4410 int len = (int)s.size();
4411 String val;
4412 for (int i=0 ; i<len ; i++)
4413 {
4414 char ch = s[i];
4415 if (ch == '$' && s[i+1] == '{')
4416 {
4417 String varname;
4418 int j = i+2;
4419 for ( ; j<len ; j++)
4420 {
4421 ch = s[j];
4422 if (ch == '$' && s[j+1] == '{')
4423 {
4424 error("attribute %s cannot have nested variable references",
4425 s.c_str());
4426 return false;
4427 }
4428 else if (ch == '}')
4429 {
4430 varname = trim(varname);
4431 String varval;
4432 if (!lookupProperty(varname, varval))
4433 return false;
4434 String varval2;
4435 //Now see if the answer has ${} in it, too
4436 if (!getSubstitutionsRecursive(varval, varval2, depth + 1))
4437 return false;
4438 val.append(varval2);
4439 break;
4440 }
4441 else
4442 {
4443 varname.push_back(ch);
4444 }
4445 }
4446 i = j;
4447 }
4448 else
4449 {
4450 val.push_back(ch);
4451 }
4452 }
4453 result = val;
4454 return true;
4455 }
4457 /**
4458 * Analyse a string, looking for any substitutions or other
4459 * things that need resilution
4460 */
4461 bool MakeBase::getSubstitutions(const String &str, String &result)
4462 {
4463 return getSubstitutionsRecursive(str, result, 0);
4464 }
4468 /**
4469 * replace variable refs like ${a} with their values
4470 * Assume that the string has already been syntax validated
4471 */
4472 String MakeBase::eval(const String &s, const String &defaultVal)
4473 {
4474 if (s.size()==0)
4475 return defaultVal;
4476 String ret;
4477 if (getSubstitutions(s, ret))
4478 return ret;
4479 else
4480 return defaultVal;
4481 }
4484 /**
4485 * replace variable refs like ${a} with their values
4486 * return true or false
4487 * Assume that the string has already been syntax validated
4488 */
4489 bool MakeBase::evalBool(const String &s, bool defaultVal)
4490 {
4491 if (s.size()==0)
4492 return defaultVal;
4493 String val = eval(s, "false");
4494 if (s == "true" || s == "TRUE")
4495 return true;
4496 else
4497 return defaultVal;
4498 }
4501 /**
4502 * Get a string attribute, testing it for proper syntax and
4503 * property names.
4504 */
4505 bool MakeBase::getAttribute(Element *elem, const String &name,
4506 String &result)
4507 {
4508 String s = elem->getAttribute(name);
4509 String tmp;
4510 bool ret = getSubstitutions(s, tmp);
4511 if (ret)
4512 result = s; //assign -if- ok
4513 return ret;
4514 }
4517 /**
4518 * Get a string value, testing it for proper syntax and
4519 * property names.
4520 */
4521 bool MakeBase::getValue(Element *elem, String &result)
4522 {
4523 String s = elem->getValue();
4524 String tmp;
4525 bool ret = getSubstitutions(s, tmp);
4526 if (ret)
4527 result = s; //assign -if- ok
4528 return ret;
4529 }
4534 /**
4535 * Parse a <patternset> entry
4536 */
4537 bool MakeBase::parsePatternSet(Element *elem,
4538 MakeBase &propRef,
4539 std::vector<String> &includes,
4540 std::vector<String> &excludes
4541 )
4542 {
4543 std::vector<Element *> children = elem->getChildren();
4544 for (unsigned int i=0 ; i<children.size() ; i++)
4545 {
4546 Element *child = children[i];
4547 String tagName = child->getName();
4548 if (tagName == "exclude")
4549 {
4550 String fname;
4551 if (!propRef.getAttribute(child, "name", fname))
4552 return false;
4553 //trace("EXCLUDE: %s", fname.c_str());
4554 excludes.push_back(fname);
4555 }
4556 else if (tagName == "include")
4557 {
4558 String fname;
4559 if (!propRef.getAttribute(child, "name", fname))
4560 return false;
4561 //trace("INCLUDE: %s", fname.c_str());
4562 includes.push_back(fname);
4563 }
4564 }
4566 return true;
4567 }
4572 /**
4573 * Parse a <fileset> entry, and determine which files
4574 * should be included
4575 */
4576 bool MakeBase::parseFileSet(Element *elem,
4577 MakeBase &propRef,
4578 FileSet &fileSet)
4579 {
4580 String name = elem->getName();
4581 if (name != "fileset")
4582 {
4583 error("expected <fileset>");
4584 return false;
4585 }
4588 std::vector<String> includes;
4589 std::vector<String> excludes;
4591 //A fileset has one implied patternset
4592 if (!parsePatternSet(elem, propRef, includes, excludes))
4593 {
4594 return false;
4595 }
4596 //Look for child tags, including more patternsets
4597 std::vector<Element *> children = elem->getChildren();
4598 for (unsigned int i=0 ; i<children.size() ; i++)
4599 {
4600 Element *child = children[i];
4601 String tagName = child->getName();
4602 if (tagName == "patternset")
4603 {
4604 if (!parsePatternSet(child, propRef, includes, excludes))
4605 {
4606 return false;
4607 }
4608 }
4609 }
4611 String dir;
4612 //Now do the stuff
4613 //Get the base directory for reading file names
4614 if (!propRef.getAttribute(elem, "dir", dir))
4615 return false;
4617 fileSet.setDirectory(dir);
4618 fileSet.setIncludes(includes);
4619 fileSet.setExcludes(excludes);
4621 /*
4622 std::vector<String> fileList;
4623 if (dir.size() > 0)
4624 {
4625 String baseDir = propRef.resolve(dir);
4626 if (!listFiles(baseDir, "", includes, excludes, fileList))
4627 return false;
4628 }
4629 std::sort(fileList.begin(), fileList.end());
4630 result = fileList;
4631 */
4634 /*
4635 for (unsigned int i=0 ; i<result.size() ; i++)
4636 {
4637 trace("RES:%s", result[i].c_str());
4638 }
4639 */
4642 return true;
4643 }
4645 /**
4646 * Parse a <filelist> entry. This is far simpler than FileSet,
4647 * since no directory scanning is needed. The file names are listed
4648 * explicitly.
4649 */
4650 bool MakeBase::parseFileList(Element *elem,
4651 MakeBase &propRef,
4652 FileList &fileList)
4653 {
4654 std::vector<String> fnames;
4655 //Look for child tags, namely "file"
4656 std::vector<Element *> children = elem->getChildren();
4657 for (unsigned int i=0 ; i<children.size() ; i++)
4658 {
4659 Element *child = children[i];
4660 String tagName = child->getName();
4661 if (tagName == "file")
4662 {
4663 String fname = child->getAttribute("name");
4664 if (fname.size()==0)
4665 {
4666 error("<file> element requires name="" attribute");
4667 return false;
4668 }
4669 fnames.push_back(fname);
4670 }
4671 else
4672 {
4673 error("tag <%s> not allowed in <fileset>", tagName.c_str());
4674 return false;
4675 }
4676 }
4678 String dir;
4679 //Get the base directory for reading file names
4680 if (!propRef.getAttribute(elem, "dir", dir))
4681 return false;
4682 fileList.setDirectory(dir);
4683 fileList.setFiles(fnames);
4685 return true;
4686 }
4690 /**
4691 * Create a directory, making intermediate dirs
4692 * if necessary
4693 */
4694 bool MakeBase::createDirectory(const String &dirname)
4695 {
4696 //trace("## createDirectory: %s", dirname.c_str());
4697 //## first check if it exists
4698 struct stat finfo;
4699 String nativeDir = getNativePath(dirname);
4700 char *cnative = (char *) nativeDir.c_str();
4701 #ifdef __WIN32__
4702 if (strlen(cnative)==2 && cnative[1]==':')
4703 return true;
4704 #endif
4705 if (stat(cnative, &finfo)==0)
4706 {
4707 if (!S_ISDIR(finfo.st_mode))
4708 {
4709 error("mkdir: file %s exists but is not a directory",
4710 cnative);
4711 return false;
4712 }
4713 else //exists
4714 {
4715 return true;
4716 }
4717 }
4719 //## 2: pull off the last path segment, if any,
4720 //## to make the dir 'above' this one, if necessary
4721 unsigned int pos = dirname.find_last_of('/');
4722 if (pos>0 && pos != dirname.npos)
4723 {
4724 String subpath = dirname.substr(0, pos);
4725 //A letter root (c:) ?
4726 if (!createDirectory(subpath))
4727 return false;
4728 }
4730 //## 3: now make
4731 #ifdef __WIN32__
4732 if (mkdir(cnative)<0)
4733 #else
4734 if (mkdir(cnative, S_IRWXU | S_IRWXG | S_IRWXO)<0)
4735 #endif
4736 {
4737 error("cannot make directory '%s' : %s",
4738 cnative, strerror(errno));
4739 return false;
4740 }
4742 return true;
4743 }
4746 /**
4747 * Remove a directory recursively
4748 */
4749 bool MakeBase::removeDirectory(const String &dirName)
4750 {
4751 char *dname = (char *)dirName.c_str();
4753 DIR *dir = opendir(dname);
4754 if (!dir)
4755 {
4756 //# Let this fail nicely.
4757 return true;
4758 //error("error opening directory %s : %s", dname, strerror(errno));
4759 //return false;
4760 }
4762 while (true)
4763 {
4764 struct dirent *de = readdir(dir);
4765 if (!de)
4766 break;
4768 //Get the directory member name
4769 String s = de->d_name;
4770 if (s.size() == 0 || s[0] == '.')
4771 continue;
4772 String childName;
4773 if (dirName.size() > 0)
4774 {
4775 childName.append(dirName);
4776 childName.append("/");
4777 }
4778 childName.append(s);
4781 struct stat finfo;
4782 String childNative = getNativePath(childName);
4783 char *cnative = (char *)childNative.c_str();
4784 if (stat(cnative, &finfo)<0)
4785 {
4786 error("cannot stat file:%s", cnative);
4787 }
4788 else if (S_ISDIR(finfo.st_mode))
4789 {
4790 //trace("DEL dir: %s", childName.c_str());
4791 if (!removeDirectory(childName))
4792 {
4793 return false;
4794 }
4795 }
4796 else if (!S_ISREG(finfo.st_mode))
4797 {
4798 //trace("not regular: %s", cnative);
4799 }
4800 else
4801 {
4802 //trace("DEL file: %s", childName.c_str());
4803 if (remove(cnative)<0)
4804 {
4805 error("error deleting %s : %s",
4806 cnative, strerror(errno));
4807 return false;
4808 }
4809 }
4810 }
4811 closedir(dir);
4813 //Now delete the directory
4814 String native = getNativePath(dirName);
4815 if (rmdir(native.c_str())<0)
4816 {
4817 error("could not delete directory %s : %s",
4818 native.c_str() , strerror(errno));
4819 return false;
4820 }
4822 return true;
4824 }
4827 /**
4828 * Copy a file from one name to another. Perform only if needed
4829 */
4830 bool MakeBase::copyFile(const String &srcFile, const String &destFile)
4831 {
4832 //# 1 Check up-to-date times
4833 String srcNative = getNativePath(srcFile);
4834 struct stat srcinfo;
4835 if (stat(srcNative.c_str(), &srcinfo)<0)
4836 {
4837 error("source file %s for copy does not exist",
4838 srcNative.c_str());
4839 return false;
4840 }
4842 String destNative = getNativePath(destFile);
4843 struct stat destinfo;
4844 if (stat(destNative.c_str(), &destinfo)==0)
4845 {
4846 if (destinfo.st_mtime >= srcinfo.st_mtime)
4847 return true;
4848 }
4850 //# 2 prepare a destination directory if necessary
4851 unsigned int pos = destFile.find_last_of('/');
4852 if (pos != destFile.npos)
4853 {
4854 String subpath = destFile.substr(0, pos);
4855 if (!createDirectory(subpath))
4856 return false;
4857 }
4859 //# 3 do the data copy
4860 #ifndef __WIN32__
4862 FILE *srcf = fopen(srcNative.c_str(), "rb");
4863 if (!srcf)
4864 {
4865 error("copyFile cannot open '%s' for reading", srcNative.c_str());
4866 return false;
4867 }
4868 FILE *destf = fopen(destNative.c_str(), "wb");
4869 if (!destf)
4870 {
4871 error("copyFile cannot open %s for writing", srcNative.c_str());
4872 return false;
4873 }
4875 while (!feof(srcf))
4876 {
4877 int ch = fgetc(srcf);
4878 if (ch<0)
4879 break;
4880 fputc(ch, destf);
4881 }
4883 fclose(destf);
4884 fclose(srcf);
4886 #else
4888 if (!CopyFile(srcNative.c_str(), destNative.c_str(), false))
4889 {
4890 error("copyFile from %s to %s failed",
4891 srcNative.c_str(), destNative.c_str());
4892 return false;
4893 }
4895 #endif /* __WIN32__ */
4898 return true;
4899 }
4903 /**
4904 * Tests if the file exists and is a regular file
4905 */
4906 bool MakeBase::isRegularFile(const String &fileName)
4907 {
4908 String native = getNativePath(fileName);
4909 struct stat finfo;
4911 //Exists?
4912 if (stat(native.c_str(), &finfo)<0)
4913 return false;
4916 //check the file mode
4917 if (!S_ISREG(finfo.st_mode))
4918 return false;
4920 return true;
4921 }
4923 /**
4924 * Tests if the file exists and is a directory
4925 */
4926 bool MakeBase::isDirectory(const String &fileName)
4927 {
4928 String native = getNativePath(fileName);
4929 struct stat finfo;
4931 //Exists?
4932 if (stat(native.c_str(), &finfo)<0)
4933 return false;
4936 //check the file mode
4937 if (!S_ISDIR(finfo.st_mode))
4938 return false;
4940 return true;
4941 }
4945 /**
4946 * Tests is the modification of fileA is newer than fileB
4947 */
4948 bool MakeBase::isNewerThan(const String &fileA, const String &fileB)
4949 {
4950 //trace("isNewerThan:'%s' , '%s'", fileA.c_str(), fileB.c_str());
4951 String nativeA = getNativePath(fileA);
4952 struct stat infoA;
4953 //IF source does not exist, NOT newer
4954 if (stat(nativeA.c_str(), &infoA)<0)
4955 {
4956 return false;
4957 }
4959 String nativeB = getNativePath(fileB);
4960 struct stat infoB;
4961 //IF dest does not exist, YES, newer
4962 if (stat(nativeB.c_str(), &infoB)<0)
4963 {
4964 return true;
4965 }
4967 //check the actual times
4968 if (infoA.st_mtime > infoB.st_mtime)
4969 {
4970 return true;
4971 }
4973 return false;
4974 }
4977 //########################################################################
4978 //# P K G C O N F I G
4979 //########################################################################
4982 /**
4983 * Get a character from the buffer at pos. If out of range,
4984 * return -1 for safety
4985 */
4986 int PkgConfig::get(int pos)
4987 {
4988 if (pos>parselen)
4989 return -1;
4990 return parsebuf[pos];
4991 }
4995 /**
4996 * Skip over all whitespace characters beginning at pos. Return
4997 * the position of the first non-whitespace character.
4998 * Pkg-config is line-oriented, so check for newline
4999 */
5000 int PkgConfig::skipwhite(int pos)
5001 {
5002 while (pos < parselen)
5003 {
5004 int ch = get(pos);
5005 if (ch < 0)
5006 break;
5007 if (!isspace(ch))
5008 break;
5009 pos++;
5010 }
5011 return pos;
5012 }
5015 /**
5016 * Parse the buffer beginning at pos, for a word. Fill
5017 * 'ret' with the result. Return the position after the
5018 * word.
5019 */
5020 int PkgConfig::getword(int pos, String &ret)
5021 {
5022 while (pos < parselen)
5023 {
5024 int ch = get(pos);
5025 if (ch < 0)
5026 break;
5027 if (!isalnum(ch) && ch != '_' && ch != '-' && ch != '+' && ch != '.')
5028 break;
5029 ret.push_back((char)ch);
5030 pos++;
5031 }
5032 return pos;
5033 }
5035 bool PkgConfig::parseRequires()
5036 {
5037 if (requires.size() == 0)
5038 return true;
5039 parsebuf = (char *)requires.c_str();
5040 parselen = requires.size();
5041 int pos = 0;
5042 while (pos < parselen)
5043 {
5044 pos = skipwhite(pos);
5045 String val;
5046 int pos2 = getword(pos, val);
5047 if (pos2 == pos)
5048 break;
5049 pos = pos2;
5050 //trace("val %s", val.c_str());
5051 requireList.push_back(val);
5052 }
5053 return true;
5054 }
5057 static int getint(const String str)
5058 {
5059 char *s = (char *)str.c_str();
5060 char *ends = NULL;
5061 long val = strtol(s, &ends, 10);
5062 if (ends == s)
5063 return 0L;
5064 else
5065 return val;
5066 }
5068 void PkgConfig::parseVersion()
5069 {
5070 if (version.size() == 0)
5071 return;
5072 String s1, s2, s3;
5073 unsigned int pos = 0;
5074 unsigned int pos2 = version.find('.', pos);
5075 if (pos2 == version.npos)
5076 {
5077 s1 = version;
5078 }
5079 else
5080 {
5081 s1 = version.substr(pos, pos2-pos);
5082 pos = pos2;
5083 pos++;
5084 if (pos < version.size())
5085 {
5086 pos2 = version.find('.', pos);
5087 if (pos2 == version.npos)
5088 {
5089 s2 = version.substr(pos, version.size()-pos);
5090 }
5091 else
5092 {
5093 s2 = version.substr(pos, pos2-pos);
5094 pos = pos2;
5095 pos++;
5096 if (pos < version.size())
5097 s3 = version.substr(pos, pos2-pos);
5098 }
5099 }
5100 }
5102 majorVersion = getint(s1);
5103 minorVersion = getint(s2);
5104 microVersion = getint(s3);
5105 //trace("version:%d.%d.%d", majorVersion,
5106 // minorVersion, microVersion );
5107 }
5110 bool PkgConfig::parseLine(const String &lineBuf)
5111 {
5112 parsebuf = (char *)lineBuf.c_str();
5113 parselen = lineBuf.size();
5114 int pos = 0;
5116 while (pos < parselen)
5117 {
5118 String attrName;
5119 pos = skipwhite(pos);
5120 int ch = get(pos);
5121 if (ch == '#')
5122 {
5123 //comment. eat the rest of the line
5124 while (pos < parselen)
5125 {
5126 ch = get(pos);
5127 if (ch == '\n' || ch < 0)
5128 break;
5129 pos++;
5130 }
5131 continue;
5132 }
5133 pos = getword(pos, attrName);
5134 if (attrName.size() == 0)
5135 continue;
5137 pos = skipwhite(pos);
5138 ch = get(pos);
5139 if (ch != ':' && ch != '=')
5140 {
5141 error("expected ':' or '='");
5142 return false;
5143 }
5144 pos++;
5145 pos = skipwhite(pos);
5146 String attrVal;
5147 while (pos < parselen)
5148 {
5149 ch = get(pos);
5150 if (ch == '\n' || ch < 0)
5151 break;
5152 else if (ch == '$' && get(pos+1) == '{')
5153 {
5154 //# this is a ${substitution}
5155 pos += 2;
5156 String subName;
5157 while (pos < parselen)
5158 {
5159 ch = get(pos);
5160 if (ch < 0)
5161 {
5162 error("unterminated substitution");
5163 return false;
5164 }
5165 else if (ch == '}')
5166 break;
5167 else
5168 subName.push_back((char)ch);
5169 pos++;
5170 }
5171 //trace("subName:%s %s", subName.c_str(), prefix.c_str());
5172 if (subName == "prefix" && prefix.size()>0)
5173 {
5174 attrVal.append(prefix);
5175 //trace("prefix override:%s", prefix.c_str());
5176 }
5177 else
5178 {
5179 String subVal = attrs[subName];
5180 //trace("subVal:%s", subVal.c_str());
5181 attrVal.append(subVal);
5182 }
5183 }
5184 else
5185 attrVal.push_back((char)ch);
5186 pos++;
5187 }
5189 attrVal = trim(attrVal);
5190 attrs[attrName] = attrVal;
5192 String attrNameL = toLower(attrName);
5194 if (attrNameL == "name")
5195 name = attrVal;
5196 else if (attrNameL == "description")
5197 description = attrVal;
5198 else if (attrNameL == "cflags")
5199 cflags = attrVal;
5200 else if (attrNameL == "libs")
5201 libs = attrVal;
5202 else if (attrNameL == "requires")
5203 requires = attrVal;
5204 else if (attrNameL == "version")
5205 version = attrVal;
5207 //trace("name:'%s' value:'%s'",
5208 // attrName.c_str(), attrVal.c_str());
5209 }
5211 return true;
5212 }
5215 bool PkgConfig::parse(const String &buf)
5216 {
5217 init();
5219 String line;
5220 int lineNr = 0;
5221 for (unsigned int p=0 ; p<buf.size() ; p++)
5222 {
5223 int ch = buf[p];
5224 if (ch == '\n' || ch == '\r')
5225 {
5226 if (!parseLine(line))
5227 return false;
5228 line.clear();
5229 lineNr++;
5230 }
5231 else
5232 {
5233 line.push_back(ch);
5234 }
5235 }
5236 if (line.size()>0)
5237 {
5238 if (!parseLine(line))
5239 return false;
5240 }
5242 parseRequires();
5243 parseVersion();
5245 return true;
5246 }
5251 void PkgConfig::dumpAttrs()
5252 {
5253 //trace("### PkgConfig attributes for %s", fileName.c_str());
5254 std::map<String, String>::iterator iter;
5255 for (iter=attrs.begin() ; iter!=attrs.end() ; iter++)
5256 {
5257 trace(" %s = %s", iter->first.c_str(), iter->second.c_str());
5258 }
5259 }
5262 bool PkgConfig::readFile(const String &fname)
5263 {
5264 fileName = getNativePath(fname);
5266 FILE *f = fopen(fileName.c_str(), "r");
5267 if (!f)
5268 {
5269 error("cannot open file '%s' for reading", fileName.c_str());
5270 return false;
5271 }
5272 String buf;
5273 while (true)
5274 {
5275 int ch = fgetc(f);
5276 if (ch < 0)
5277 break;
5278 buf.push_back((char)ch);
5279 }
5280 fclose(f);
5282 //trace("####### File:\n%s", buf.c_str());
5283 if (!parse(buf))
5284 {
5285 return false;
5286 }
5288 //dumpAttrs();
5290 return true;
5291 }
5295 bool PkgConfig::query(const String &pkgName)
5296 {
5297 name = pkgName;
5299 String fname = path;
5300 fname.append("/");
5301 fname.append(name);
5302 fname.append(".pc");
5304 if (!readFile(fname))
5305 return false;
5307 return true;
5308 }
5314 //########################################################################
5315 //# D E P T O O L
5316 //########################################################################
5320 /**
5321 * Class which holds information for each file.
5322 */
5323 class FileRec
5324 {
5325 public:
5327 typedef enum
5328 {
5329 UNKNOWN,
5330 CFILE,
5331 HFILE,
5332 OFILE
5333 } FileType;
5335 /**
5336 * Constructor
5337 */
5338 FileRec()
5339 { init(); type = UNKNOWN; }
5341 /**
5342 * Copy constructor
5343 */
5344 FileRec(const FileRec &other)
5345 { init(); assign(other); }
5346 /**
5347 * Constructor
5348 */
5349 FileRec(int typeVal)
5350 { init(); type = typeVal; }
5351 /**
5352 * Assignment operator
5353 */
5354 FileRec &operator=(const FileRec &other)
5355 { init(); assign(other); return *this; }
5358 /**
5359 * Destructor
5360 */
5361 ~FileRec()
5362 {}
5364 /**
5365 * Directory part of the file name
5366 */
5367 String path;
5369 /**
5370 * Base name, sans directory and suffix
5371 */
5372 String baseName;
5374 /**
5375 * File extension, such as cpp or h
5376 */
5377 String suffix;
5379 /**
5380 * Type of file: CFILE, HFILE, OFILE
5381 */
5382 int type;
5384 /**
5385 * Used to list files ref'd by this one
5386 */
5387 std::map<String, FileRec *> files;
5390 private:
5392 void init()
5393 {
5394 }
5396 void assign(const FileRec &other)
5397 {
5398 type = other.type;
5399 baseName = other.baseName;
5400 suffix = other.suffix;
5401 files = other.files;
5402 }
5404 };
5408 /**
5409 * Simpler dependency record
5410 */
5411 class DepRec
5412 {
5413 public:
5415 /**
5416 * Constructor
5417 */
5418 DepRec()
5419 {init();}
5421 /**
5422 * Copy constructor
5423 */
5424 DepRec(const DepRec &other)
5425 {init(); assign(other);}
5426 /**
5427 * Constructor
5428 */
5429 DepRec(const String &fname)
5430 {init(); name = fname; }
5431 /**
5432 * Assignment operator
5433 */
5434 DepRec &operator=(const DepRec &other)
5435 {init(); assign(other); return *this;}
5438 /**
5439 * Destructor
5440 */
5441 ~DepRec()
5442 {}
5444 /**
5445 * Directory part of the file name
5446 */
5447 String path;
5449 /**
5450 * Base name, without the path and suffix
5451 */
5452 String name;
5454 /**
5455 * Suffix of the source
5456 */
5457 String suffix;
5460 /**
5461 * Used to list files ref'd by this one
5462 */
5463 std::vector<String> files;
5466 private:
5468 void init()
5469 {
5470 }
5472 void assign(const DepRec &other)
5473 {
5474 path = other.path;
5475 name = other.name;
5476 suffix = other.suffix;
5477 files = other.files; //avoid recursion
5478 }
5480 };
5483 class DepTool : public MakeBase
5484 {
5485 public:
5487 /**
5488 * Constructor
5489 */
5490 DepTool()
5491 { init(); }
5493 /**
5494 * Copy constructor
5495 */
5496 DepTool(const DepTool &other)
5497 { init(); assign(other); }
5499 /**
5500 * Assignment operator
5501 */
5502 DepTool &operator=(const DepTool &other)
5503 { init(); assign(other); return *this; }
5506 /**
5507 * Destructor
5508 */
5509 ~DepTool()
5510 {}
5513 /**
5514 * Reset this section of code
5515 */
5516 virtual void init();
5518 /**
5519 * Reset this section of code
5520 */
5521 virtual void assign(const DepTool &other)
5522 {
5523 }
5525 /**
5526 * Sets the source directory which will be scanned
5527 */
5528 virtual void setSourceDirectory(const String &val)
5529 { sourceDir = val; }
5531 /**
5532 * Returns the source directory which will be scanned
5533 */
5534 virtual String getSourceDirectory()
5535 { return sourceDir; }
5537 /**
5538 * Sets the list of files within the directory to analyze
5539 */
5540 virtual void setFileList(const std::vector<String> &list)
5541 { fileList = list; }
5543 /**
5544 * Creates the list of all file names which will be
5545 * candidates for further processing. Reads make.exclude
5546 * to see which files for directories to leave out.
5547 */
5548 virtual bool createFileList();
5551 /**
5552 * Generates the forward dependency list
5553 */
5554 virtual bool generateDependencies();
5557 /**
5558 * Generates the forward dependency list, saving the file
5559 */
5560 virtual bool generateDependencies(const String &);
5563 /**
5564 * Load a dependency file
5565 */
5566 std::vector<DepRec> loadDepFile(const String &fileName);
5568 /**
5569 * Load a dependency file, generating one if necessary
5570 */
5571 std::vector<DepRec> getDepFile(const String &fileName,
5572 bool forceRefresh);
5574 /**
5575 * Save a dependency file
5576 */
5577 bool saveDepFile(const String &fileName);
5580 private:
5583 /**
5584 *
5585 */
5586 void parseName(const String &fullname,
5587 String &path,
5588 String &basename,
5589 String &suffix);
5591 /**
5592 *
5593 */
5594 int get(int pos);
5596 /**
5597 *
5598 */
5599 int skipwhite(int pos);
5601 /**
5602 *
5603 */
5604 int getword(int pos, String &ret);
5606 /**
5607 *
5608 */
5609 bool sequ(int pos, const char *key);
5611 /**
5612 *
5613 */
5614 bool addIncludeFile(FileRec *frec, const String &fname);
5616 /**
5617 *
5618 */
5619 bool scanFile(const String &fname, FileRec *frec);
5621 /**
5622 *
5623 */
5624 bool processDependency(FileRec *ofile, FileRec *include);
5626 /**
5627 *
5628 */
5629 String sourceDir;
5631 /**
5632 *
5633 */
5634 std::vector<String> fileList;
5636 /**
5637 *
5638 */
5639 std::vector<String> directories;
5641 /**
5642 * A list of all files which will be processed for
5643 * dependencies.
5644 */
5645 std::map<String, FileRec *> allFiles;
5647 /**
5648 * The list of .o files, and the
5649 * dependencies upon them.
5650 */
5651 std::map<String, FileRec *> oFiles;
5653 int depFileSize;
5654 char *depFileBuf;
5656 static const int readBufSize = 8192;
5657 char readBuf[8193];//byte larger
5659 };
5665 /**
5666 * Clean up after processing. Called by the destructor, but should
5667 * also be called before the object is reused.
5668 */
5669 void DepTool::init()
5670 {
5671 sourceDir = ".";
5673 fileList.clear();
5674 directories.clear();
5676 //clear output file list
5677 std::map<String, FileRec *>::iterator iter;
5678 for (iter=oFiles.begin(); iter!=oFiles.end() ; iter++)
5679 delete iter->second;
5680 oFiles.clear();
5682 //allFiles actually contains the master copies. delete them
5683 for (iter= allFiles.begin(); iter!=allFiles.end() ; iter++)
5684 delete iter->second;
5685 allFiles.clear();
5687 }
5692 /**
5693 * Parse a full path name into path, base name, and suffix
5694 */
5695 void DepTool::parseName(const String &fullname,
5696 String &path,
5697 String &basename,
5698 String &suffix)
5699 {
5700 if (fullname.size() < 2)
5701 return;
5703 unsigned int pos = fullname.find_last_of('/');
5704 if (pos != fullname.npos && pos<fullname.size()-1)
5705 {
5706 path = fullname.substr(0, pos);
5707 pos++;
5708 basename = fullname.substr(pos, fullname.size()-pos);
5709 }
5710 else
5711 {
5712 path = "";
5713 basename = fullname;
5714 }
5716 pos = basename.find_last_of('.');
5717 if (pos != basename.npos && pos<basename.size()-1)
5718 {
5719 suffix = basename.substr(pos+1, basename.size()-pos-1);
5720 basename = basename.substr(0, pos);
5721 }
5723 //trace("parsename:%s %s %s", path.c_str(),
5724 // basename.c_str(), suffix.c_str());
5725 }
5729 /**
5730 * Generate our internal file list.
5731 */
5732 bool DepTool::createFileList()
5733 {
5735 for (unsigned int i=0 ; i<fileList.size() ; i++)
5736 {
5737 String fileName = fileList[i];
5738 //trace("## FileName:%s", fileName.c_str());
5739 String path;
5740 String basename;
5741 String sfx;
5742 parseName(fileName, path, basename, sfx);
5743 if (sfx == "cpp" || sfx == "c" || sfx == "cxx" ||
5744 sfx == "cc" || sfx == "CC")
5745 {
5746 FileRec *fe = new FileRec(FileRec::CFILE);
5747 fe->path = path;
5748 fe->baseName = basename;
5749 fe->suffix = sfx;
5750 allFiles[fileName] = fe;
5751 }
5752 else if (sfx == "h" || sfx == "hh" ||
5753 sfx == "hpp" || sfx == "hxx")
5754 {
5755 FileRec *fe = new FileRec(FileRec::HFILE);
5756 fe->path = path;
5757 fe->baseName = basename;
5758 fe->suffix = sfx;
5759 allFiles[fileName] = fe;
5760 }
5761 }
5763 if (!listDirectories(sourceDir, "", directories))
5764 return false;
5766 return true;
5767 }
5773 /**
5774 * Get a character from the buffer at pos. If out of range,
5775 * return -1 for safety
5776 */
5777 int DepTool::get(int pos)
5778 {
5779 if (pos>depFileSize)
5780 return -1;
5781 return depFileBuf[pos];
5782 }
5786 /**
5787 * Skip over all whitespace characters beginning at pos. Return
5788 * the position of the first non-whitespace character.
5789 */
5790 int DepTool::skipwhite(int pos)
5791 {
5792 while (pos < depFileSize)
5793 {
5794 int ch = get(pos);
5795 if (ch < 0)
5796 break;
5797 if (!isspace(ch))
5798 break;
5799 pos++;
5800 }
5801 return pos;
5802 }
5805 /**
5806 * Parse the buffer beginning at pos, for a word. Fill
5807 * 'ret' with the result. Return the position after the
5808 * word.
5809 */
5810 int DepTool::getword(int pos, String &ret)
5811 {
5812 while (pos < depFileSize)
5813 {
5814 int ch = get(pos);
5815 if (ch < 0)
5816 break;
5817 if (isspace(ch))
5818 break;
5819 ret.push_back((char)ch);
5820 pos++;
5821 }
5822 return pos;
5823 }
5825 /**
5826 * Return whether the sequence of characters in the buffer
5827 * beginning at pos match the key, for the length of the key
5828 */
5829 bool DepTool::sequ(int pos, const char *key)
5830 {
5831 while (*key)
5832 {
5833 if (*key != get(pos))
5834 return false;
5835 key++; pos++;
5836 }
5837 return true;
5838 }
5842 /**
5843 * Add an include file name to a file record. If the name
5844 * is not found in allFiles explicitly, try prepending include
5845 * directory names to it and try again.
5846 */
5847 bool DepTool::addIncludeFile(FileRec *frec, const String &iname)
5848 {
5849 //# if the name is an exact match to a path name
5850 //# in allFiles, like "myinc.h"
5851 std::map<String, FileRec *>::iterator iter =
5852 allFiles.find(iname);
5853 if (iter != allFiles.end()) //already exists
5854 {
5855 //h file in same dir
5856 FileRec *other = iter->second;
5857 //trace("local: '%s'", iname.c_str());
5858 frec->files[iname] = other;
5859 return true;
5860 }
5861 else
5862 {
5863 //## Ok, it was not found directly
5864 //look in other dirs
5865 std::vector<String>::iterator diter;
5866 for (diter=directories.begin() ;
5867 diter!=directories.end() ; diter++)
5868 {
5869 String dfname = *diter;
5870 dfname.append("/");
5871 dfname.append(iname);
5872 URI fullPathURI(dfname); //normalize path name
5873 String fullPath = fullPathURI.getPath();
5874 if (fullPath[0] == '/')
5875 fullPath = fullPath.substr(1);
5876 //trace("Normalized %s to %s", dfname.c_str(), fullPath.c_str());
5877 iter = allFiles.find(fullPath);
5878 if (iter != allFiles.end())
5879 {
5880 FileRec *other = iter->second;
5881 //trace("other: '%s'", iname.c_str());
5882 frec->files[fullPath] = other;
5883 return true;
5884 }
5885 }
5886 }
5887 return true;
5888 }
5892 /**
5893 * Lightly parse a file to find the #include directives. Do
5894 * a bit of state machine stuff to make sure that the directive
5895 * is valid. (Like not in a comment).
5896 */
5897 bool DepTool::scanFile(const String &fname, FileRec *frec)
5898 {
5899 String fileName;
5900 if (sourceDir.size() > 0)
5901 {
5902 fileName.append(sourceDir);
5903 fileName.append("/");
5904 }
5905 fileName.append(fname);
5906 String nativeName = getNativePath(fileName);
5907 FILE *f = fopen(nativeName.c_str(), "r");
5908 if (!f)
5909 {
5910 error("Could not open '%s' for reading", fname.c_str());
5911 return false;
5912 }
5913 String buf;
5914 while (!feof(f))
5915 {
5916 int nrbytes = fread(readBuf, 1, readBufSize, f);
5917 readBuf[nrbytes] = '\0';
5918 buf.append(readBuf);
5919 }
5920 fclose(f);
5922 depFileSize = buf.size();
5923 depFileBuf = (char *)buf.c_str();
5924 int pos = 0;
5927 while (pos < depFileSize)
5928 {
5929 //trace("p:%c", get(pos));
5931 //# Block comment
5932 if (get(pos) == '/' && get(pos+1) == '*')
5933 {
5934 pos += 2;
5935 while (pos < depFileSize)
5936 {
5937 if (get(pos) == '*' && get(pos+1) == '/')
5938 {
5939 pos += 2;
5940 break;
5941 }
5942 else
5943 pos++;
5944 }
5945 }
5946 //# Line comment
5947 else if (get(pos) == '/' && get(pos+1) == '/')
5948 {
5949 pos += 2;
5950 while (pos < depFileSize)
5951 {
5952 if (get(pos) == '\n')
5953 {
5954 pos++;
5955 break;
5956 }
5957 else
5958 pos++;
5959 }
5960 }
5961 //# #include! yaay
5962 else if (sequ(pos, "#include"))
5963 {
5964 pos += 8;
5965 pos = skipwhite(pos);
5966 String iname;
5967 pos = getword(pos, iname);
5968 if (iname.size()>2)
5969 {
5970 iname = iname.substr(1, iname.size()-2);
5971 addIncludeFile(frec, iname);
5972 }
5973 }
5974 else
5975 {
5976 pos++;
5977 }
5978 }
5980 return true;
5981 }
5985 /**
5986 * Recursively check include lists to find all files in allFiles to which
5987 * a given file is dependent.
5988 */
5989 bool DepTool::processDependency(FileRec *ofile, FileRec *include)
5990 {
5991 std::map<String, FileRec *>::iterator iter;
5992 for (iter=include->files.begin() ; iter!=include->files.end() ; iter++)
5993 {
5994 String fname = iter->first;
5995 if (ofile->files.find(fname) != ofile->files.end())
5996 {
5997 //trace("file '%s' already seen", fname.c_str());
5998 continue;
5999 }
6000 FileRec *child = iter->second;
6001 ofile->files[fname] = child;
6003 processDependency(ofile, child);
6004 }
6007 return true;
6008 }
6014 /**
6015 * Generate the file dependency list.
6016 */
6017 bool DepTool::generateDependencies()
6018 {
6019 std::map<String, FileRec *>::iterator iter;
6020 //# First pass. Scan for all includes
6021 for (iter=allFiles.begin() ; iter!=allFiles.end() ; iter++)
6022 {
6023 FileRec *frec = iter->second;
6024 if (!scanFile(iter->first, frec))
6025 {
6026 //quit?
6027 }
6028 }
6030 //# Second pass. Scan for all includes
6031 for (iter=allFiles.begin() ; iter!=allFiles.end() ; iter++)
6032 {
6033 FileRec *include = iter->second;
6034 if (include->type == FileRec::CFILE)
6035 {
6036 //String cFileName = iter->first;
6037 FileRec *ofile = new FileRec(FileRec::OFILE);
6038 ofile->path = include->path;
6039 ofile->baseName = include->baseName;
6040 ofile->suffix = include->suffix;
6041 String fname = include->path;
6042 if (fname.size()>0)
6043 fname.append("/");
6044 fname.append(include->baseName);
6045 fname.append(".o");
6046 oFiles[fname] = ofile;
6047 //add the .c file first? no, don't
6048 //ofile->files[cFileName] = include;
6050 //trace("ofile:%s", fname.c_str());
6052 processDependency(ofile, include);
6053 }
6054 }
6057 return true;
6058 }
6062 /**
6063 * High-level call to generate deps and optionally save them
6064 */
6065 bool DepTool::generateDependencies(const String &fileName)
6066 {
6067 if (!createFileList())
6068 return false;
6069 if (!generateDependencies())
6070 return false;
6071 if (!saveDepFile(fileName))
6072 return false;
6073 return true;
6074 }
6077 /**
6078 * This saves the dependency cache.
6079 */
6080 bool DepTool::saveDepFile(const String &fileName)
6081 {
6082 time_t tim;
6083 time(&tim);
6085 FILE *f = fopen(fileName.c_str(), "w");
6086 if (!f)
6087 {
6088 trace("cannot open '%s' for writing", fileName.c_str());
6089 }
6090 fprintf(f, "<?xml version='1.0'?>\n");
6091 fprintf(f, "<!--\n");
6092 fprintf(f, "########################################################\n");
6093 fprintf(f, "## File: build.dep\n");
6094 fprintf(f, "## Generated by BuildTool at :%s", ctime(&tim));
6095 fprintf(f, "########################################################\n");
6096 fprintf(f, "-->\n");
6098 fprintf(f, "<dependencies source='%s'>\n\n", sourceDir.c_str());
6099 std::map<String, FileRec *>::iterator iter;
6100 for (iter=oFiles.begin() ; iter!=oFiles.end() ; iter++)
6101 {
6102 FileRec *frec = iter->second;
6103 if (frec->type == FileRec::OFILE)
6104 {
6105 fprintf(f, "<object path='%s' name='%s' suffix='%s'>\n",
6106 frec->path.c_str(), frec->baseName.c_str(), frec->suffix.c_str());
6107 std::map<String, FileRec *>::iterator citer;
6108 for (citer=frec->files.begin() ; citer!=frec->files.end() ; citer++)
6109 {
6110 String cfname = citer->first;
6111 fprintf(f, " <dep name='%s'/>\n", cfname.c_str());
6112 }
6113 fprintf(f, "</object>\n\n");
6114 }
6115 }
6117 fprintf(f, "</dependencies>\n");
6118 fprintf(f, "\n");
6119 fprintf(f, "<!--\n");
6120 fprintf(f, "########################################################\n");
6121 fprintf(f, "## E N D\n");
6122 fprintf(f, "########################################################\n");
6123 fprintf(f, "-->\n");
6125 fclose(f);
6127 return true;
6128 }
6133 /**
6134 * This loads the dependency cache.
6135 */
6136 std::vector<DepRec> DepTool::loadDepFile(const String &depFile)
6137 {
6138 std::vector<DepRec> result;
6140 Parser parser;
6141 Element *root = parser.parseFile(depFile.c_str());
6142 if (!root)
6143 {
6144 //error("Could not open %s for reading", depFile.c_str());
6145 return result;
6146 }
6148 if (root->getChildren().size()==0 ||
6149 root->getChildren()[0]->getName()!="dependencies")
6150 {
6151 error("loadDepFile: main xml element should be <dependencies>");
6152 delete root;
6153 return result;
6154 }
6156 //########## Start parsing
6157 Element *depList = root->getChildren()[0];
6159 std::vector<Element *> objects = depList->getChildren();
6160 for (unsigned int i=0 ; i<objects.size() ; i++)
6161 {
6162 Element *objectElem = objects[i];
6163 String tagName = objectElem->getName();
6164 if (tagName != "object")
6165 {
6166 error("loadDepFile: <dependencies> should have only <object> children");
6167 return result;
6168 }
6170 String objName = objectElem->getAttribute("name");
6171 //trace("object:%s", objName.c_str());
6172 DepRec depObject(objName);
6173 depObject.path = objectElem->getAttribute("path");
6174 depObject.suffix = objectElem->getAttribute("suffix");
6175 //########## DESCRIPTION
6176 std::vector<Element *> depElems = objectElem->getChildren();
6177 for (unsigned int i=0 ; i<depElems.size() ; i++)
6178 {
6179 Element *depElem = depElems[i];
6180 tagName = depElem->getName();
6181 if (tagName != "dep")
6182 {
6183 error("loadDepFile: <object> should have only <dep> children");
6184 return result;
6185 }
6186 String depName = depElem->getAttribute("name");
6187 //trace(" dep:%s", depName.c_str());
6188 depObject.files.push_back(depName);
6189 }
6191 //Insert into the result list, in a sorted manner
6192 bool inserted = false;
6193 std::vector<DepRec>::iterator iter;
6194 for (iter = result.begin() ; iter != result.end() ; iter++)
6195 {
6196 String vpath = iter->path;
6197 vpath.append("/");
6198 vpath.append(iter->name);
6199 String opath = depObject.path;
6200 opath.append("/");
6201 opath.append(depObject.name);
6202 if (vpath > opath)
6203 {
6204 inserted = true;
6205 iter = result.insert(iter, depObject);
6206 break;
6207 }
6208 }
6209 if (!inserted)
6210 result.push_back(depObject);
6211 }
6213 delete root;
6215 return result;
6216 }
6219 /**
6220 * This loads the dependency cache.
6221 */
6222 std::vector<DepRec> DepTool::getDepFile(const String &depFile,
6223 bool forceRefresh)
6224 {
6225 std::vector<DepRec> result;
6226 if (forceRefresh)
6227 {
6228 generateDependencies(depFile);
6229 result = loadDepFile(depFile);
6230 }
6231 else
6232 {
6233 //try once
6234 result = loadDepFile(depFile);
6235 if (result.size() == 0)
6236 {
6237 //fail? try again
6238 generateDependencies(depFile);
6239 result = loadDepFile(depFile);
6240 }
6241 }
6242 return result;
6243 }
6248 //########################################################################
6249 //# T A S K
6250 //########################################################################
6251 //forward decl
6252 class Target;
6253 class Make;
6255 /**
6256 *
6257 */
6258 class Task : public MakeBase
6259 {
6261 public:
6263 typedef enum
6264 {
6265 TASK_NONE,
6266 TASK_CC,
6267 TASK_COPY,
6268 TASK_DELETE,
6269 TASK_ECHO,
6270 TASK_JAR,
6271 TASK_JAVAC,
6272 TASK_LINK,
6273 TASK_MAKEFILE,
6274 TASK_MKDIR,
6275 TASK_MSGFMT,
6276 TASK_PKG_CONFIG,
6277 TASK_RANLIB,
6278 TASK_RC,
6279 TASK_SHAREDLIB,
6280 TASK_STATICLIB,
6281 TASK_STRIP,
6282 TASK_TOUCH,
6283 TASK_TSTAMP
6284 } TaskType;
6287 /**
6288 *
6289 */
6290 Task(MakeBase &par) : parent(par)
6291 { init(); }
6293 /**
6294 *
6295 */
6296 Task(const Task &other) : parent(other.parent)
6297 { init(); assign(other); }
6299 /**
6300 *
6301 */
6302 Task &operator=(const Task &other)
6303 { assign(other); return *this; }
6305 /**
6306 *
6307 */
6308 virtual ~Task()
6309 { }
6312 /**
6313 *
6314 */
6315 virtual MakeBase &getParent()
6316 { return parent; }
6318 /**
6319 *
6320 */
6321 virtual int getType()
6322 { return type; }
6324 /**
6325 *
6326 */
6327 virtual void setType(int val)
6328 { type = val; }
6330 /**
6331 *
6332 */
6333 virtual String getName()
6334 { return name; }
6336 /**
6337 *
6338 */
6339 virtual bool execute()
6340 { return true; }
6342 /**
6343 *
6344 */
6345 virtual bool parse(Element *elem)
6346 { return true; }
6348 /**
6349 *
6350 */
6351 Task *createTask(Element *elem, int lineNr);
6354 protected:
6356 void init()
6357 {
6358 type = TASK_NONE;
6359 name = "none";
6360 }
6362 void assign(const Task &other)
6363 {
6364 type = other.type;
6365 name = other.name;
6366 }
6368 /**
6369 * Show task status
6370 */
6371 void taskstatus(const char *fmt, ...)
6372 {
6373 va_list args;
6374 va_start(args,fmt);
6375 fprintf(stdout, " %s : ", name.c_str());
6376 vfprintf(stdout, fmt, args);
6377 fprintf(stdout, "\n");
6378 va_end(args) ;
6379 }
6381 String getAttribute(Element *elem, const String &attrName)
6382 {
6383 String str;
6384 return str;
6385 }
6387 MakeBase &parent;
6389 int type;
6391 String name;
6392 };
6396 /**
6397 * This task runs the C/C++ compiler. The compiler is invoked
6398 * for all .c or .cpp files which are newer than their correcsponding
6399 * .o files.
6400 */
6401 class TaskCC : public Task
6402 {
6403 public:
6405 TaskCC(MakeBase &par) : Task(par)
6406 {
6407 type = TASK_CC;
6408 name = "cc";
6409 }
6411 virtual ~TaskCC()
6412 {}
6414 virtual bool isExcludedInc(const String &dirname)
6415 {
6416 for (unsigned int i=0 ; i<excludeInc.size() ; i++)
6417 {
6418 String fname = excludeInc[i];
6419 if (fname == dirname)
6420 return true;
6421 }
6422 return false;
6423 }
6425 virtual bool execute()
6426 {
6427 //evaluate our parameters
6428 String command = parent.eval(commandOpt, "gcc");
6429 String ccCommand = parent.eval(ccCommandOpt, "gcc");
6430 String cxxCommand = parent.eval(cxxCommandOpt, "g++");
6431 String source = parent.eval(sourceOpt, ".");
6432 String dest = parent.eval(destOpt, ".");
6433 String flags = parent.eval(flagsOpt, "");
6434 String defines = parent.eval(definesOpt, "");
6435 String includes = parent.eval(includesOpt, "");
6436 bool continueOnError = parent.evalBool(continueOnErrorOpt, true);
6437 bool refreshCache = parent.evalBool(refreshCacheOpt, false);
6439 if (!listFiles(parent, fileSet))
6440 return false;
6442 FILE *f = NULL;
6443 f = fopen("compile.lst", "w");
6445 //refreshCache is probably false here, unless specified otherwise
6446 String fullName = parent.resolve("build.dep");
6447 if (refreshCache || isNewerThan(parent.getURI().getPath(), fullName))
6448 {
6449 taskstatus("regenerating C/C++ dependency cache");
6450 refreshCache = true;
6451 }
6453 DepTool depTool;
6454 depTool.setSourceDirectory(source);
6455 depTool.setFileList(fileSet.getFiles());
6456 std::vector<DepRec> deps =
6457 depTool.getDepFile("build.dep", refreshCache);
6459 String incs;
6460 incs.append("-I");
6461 incs.append(parent.resolve("."));
6462 incs.append(" ");
6463 if (includes.size()>0)
6464 {
6465 incs.append(includes);
6466 incs.append(" ");
6467 }
6468 std::set<String> paths;
6469 std::vector<DepRec>::iterator viter;
6470 for (viter=deps.begin() ; viter!=deps.end() ; viter++)
6471 {
6472 DepRec dep = *viter;
6473 if (dep.path.size()>0)
6474 paths.insert(dep.path);
6475 }
6476 if (source.size()>0)
6477 {
6478 incs.append(" -I");
6479 incs.append(parent.resolve(source));
6480 incs.append(" ");
6481 }
6482 std::set<String>::iterator setIter;
6483 for (setIter=paths.begin() ; setIter!=paths.end() ; setIter++)
6484 {
6485 String dirName = *setIter;
6486 //check excludeInc to see if we dont want to include this dir
6487 if (isExcludedInc(dirName))
6488 continue;
6489 incs.append(" -I");
6490 String dname;
6491 if (source.size()>0)
6492 {
6493 dname.append(source);
6494 dname.append("/");
6495 }
6496 dname.append(dirName);
6497 incs.append(parent.resolve(dname));
6498 }
6500 /**
6501 * Compile each of the C files that need it
6502 */
6503 bool errorOccurred = false;
6504 std::vector<String> cfiles;
6505 for (viter=deps.begin() ; viter!=deps.end() ; viter++)
6506 {
6507 DepRec dep = *viter;
6509 //## Select command
6510 String sfx = dep.suffix;
6511 String command = ccCommand;
6512 if (sfx == "cpp" || sfx == "cxx" || sfx == "c++" ||
6513 sfx == "cc" || sfx == "CC")
6514 command = cxxCommand;
6516 //## Make paths
6517 String destPath = dest;
6518 String srcPath = source;
6519 if (dep.path.size()>0)
6520 {
6521 destPath.append("/");
6522 destPath.append(dep.path);
6523 srcPath.append("/");
6524 srcPath.append(dep.path);
6525 }
6526 //## Make sure destination directory exists
6527 if (!createDirectory(destPath))
6528 return false;
6530 //## Check whether it needs to be done
6531 String destName;
6532 if (destPath.size()>0)
6533 {
6534 destName.append(destPath);
6535 destName.append("/");
6536 }
6537 destName.append(dep.name);
6538 destName.append(".o");
6539 String destFullName = parent.resolve(destName);
6540 String srcName;
6541 if (srcPath.size()>0)
6542 {
6543 srcName.append(srcPath);
6544 srcName.append("/");
6545 }
6546 srcName.append(dep.name);
6547 srcName.append(".");
6548 srcName.append(dep.suffix);
6549 String srcFullName = parent.resolve(srcName);
6550 bool compileMe = false;
6551 //# First we check if the source is newer than the .o
6552 if (isNewerThan(srcFullName, destFullName))
6553 {
6554 taskstatus("compile of %s required by source: %s",
6555 destFullName.c_str(), srcFullName.c_str());
6556 compileMe = true;
6557 }
6558 else
6559 {
6560 //# secondly, we check if any of the included dependencies
6561 //# of the .c/.cpp is newer than the .o
6562 for (unsigned int i=0 ; i<dep.files.size() ; i++)
6563 {
6564 String depName;
6565 if (source.size()>0)
6566 {
6567 depName.append(source);
6568 depName.append("/");
6569 }
6570 depName.append(dep.files[i]);
6571 String depFullName = parent.resolve(depName);
6572 bool depRequires = isNewerThan(depFullName, destFullName);
6573 //trace("%d %s %s\n", depRequires,
6574 // destFullName.c_str(), depFullName.c_str());
6575 if (depRequires)
6576 {
6577 taskstatus("compile of %s required by included: %s",
6578 destFullName.c_str(), depFullName.c_str());
6579 compileMe = true;
6580 break;
6581 }
6582 }
6583 }
6584 if (!compileMe)
6585 {
6586 continue;
6587 }
6589 //## Assemble the command
6590 String cmd = command;
6591 cmd.append(" -c ");
6592 cmd.append(flags);
6593 cmd.append(" ");
6594 cmd.append(defines);
6595 cmd.append(" ");
6596 cmd.append(incs);
6597 cmd.append(" ");
6598 cmd.append(srcFullName);
6599 cmd.append(" -o ");
6600 cmd.append(destFullName);
6602 //## Execute the command
6604 String outString, errString;
6605 bool ret = executeCommand(cmd.c_str(), "", outString, errString);
6607 if (f)
6608 {
6609 fprintf(f, "########################### File : %s\n",
6610 srcFullName.c_str());
6611 fprintf(f, "#### COMMAND ###\n");
6612 int col = 0;
6613 for (unsigned int i = 0 ; i < cmd.size() ; i++)
6614 {
6615 char ch = cmd[i];
6616 if (isspace(ch) && col > 63)
6617 {
6618 fputc('\n', f);
6619 col = 0;
6620 }
6621 else
6622 {
6623 fputc(ch, f);
6624 col++;
6625 }
6626 if (col > 76)
6627 {
6628 fputc('\n', f);
6629 col = 0;
6630 }
6631 }
6632 fprintf(f, "\n");
6633 fprintf(f, "#### STDOUT ###\n%s\n", outString.c_str());
6634 fprintf(f, "#### STDERR ###\n%s\n\n", errString.c_str());
6635 fflush(f);
6636 }
6637 if (!ret)
6638 {
6639 error("problem compiling: %s", errString.c_str());
6640 errorOccurred = true;
6641 }
6642 if (errorOccurred && !continueOnError)
6643 break;
6644 }
6646 if (f)
6647 {
6648 fclose(f);
6649 }
6651 return !errorOccurred;
6652 }
6655 virtual bool parse(Element *elem)
6656 {
6657 String s;
6658 if (!parent.getAttribute(elem, "command", commandOpt))
6659 return false;
6660 if (commandOpt.size()>0)
6661 { cxxCommandOpt = ccCommandOpt = commandOpt; }
6662 if (!parent.getAttribute(elem, "cc", ccCommandOpt))
6663 return false;
6664 if (!parent.getAttribute(elem, "cxx", cxxCommandOpt))
6665 return false;
6666 if (!parent.getAttribute(elem, "destdir", destOpt))
6667 return false;
6668 if (!parent.getAttribute(elem, "continueOnError", continueOnErrorOpt))
6669 return false;
6670 if (!parent.getAttribute(elem, "refreshCache", refreshCacheOpt))
6671 return false;
6673 std::vector<Element *> children = elem->getChildren();
6674 for (unsigned int i=0 ; i<children.size() ; i++)
6675 {
6676 Element *child = children[i];
6677 String tagName = child->getName();
6678 if (tagName == "flags")
6679 {
6680 if (!parent.getValue(child, flagsOpt))
6681 return false;
6682 flagsOpt = strip(flagsOpt);
6683 }
6684 else if (tagName == "includes")
6685 {
6686 if (!parent.getValue(child, includesOpt))
6687 return false;
6688 includesOpt = strip(includesOpt);
6689 }
6690 else if (tagName == "defines")
6691 {
6692 if (!parent.getValue(child, definesOpt))
6693 return false;
6694 definesOpt = strip(definesOpt);
6695 }
6696 else if (tagName == "fileset")
6697 {
6698 if (!parseFileSet(child, parent, fileSet))
6699 return false;
6700 sourceOpt = fileSet.getDirectory();
6701 }
6702 else if (tagName == "excludeinc")
6703 {
6704 if (!parseFileList(child, parent, excludeInc))
6705 return false;
6706 }
6707 }
6709 return true;
6710 }
6712 protected:
6714 String commandOpt;
6715 String ccCommandOpt;
6716 String cxxCommandOpt;
6717 String sourceOpt;
6718 String destOpt;
6719 String flagsOpt;
6720 String definesOpt;
6721 String includesOpt;
6722 String continueOnErrorOpt;
6723 String refreshCacheOpt;
6724 FileSet fileSet;
6725 FileList excludeInc;
6727 };
6731 /**
6732 *
6733 */
6734 class TaskCopy : public Task
6735 {
6736 public:
6738 typedef enum
6739 {
6740 CP_NONE,
6741 CP_TOFILE,
6742 CP_TODIR
6743 } CopyType;
6745 TaskCopy(MakeBase &par) : Task(par)
6746 {
6747 type = TASK_COPY;
6748 name = "copy";
6749 cptype = CP_NONE;
6750 haveFileSet = false;
6751 }
6753 virtual ~TaskCopy()
6754 {}
6756 virtual bool execute()
6757 {
6758 String fileName = parent.eval(fileNameOpt , ".");
6759 String toFileName = parent.eval(toFileNameOpt , ".");
6760 String toDirName = parent.eval(toDirNameOpt , ".");
6761 bool verbose = parent.evalBool(verboseOpt, false);
6762 switch (cptype)
6763 {
6764 case CP_TOFILE:
6765 {
6766 if (fileName.size()>0)
6767 {
6768 taskstatus("%s to %s",
6769 fileName.c_str(), toFileName.c_str());
6770 String fullSource = parent.resolve(fileName);
6771 String fullDest = parent.resolve(toFileName);
6772 if (verbose)
6773 taskstatus("copy %s to file %s", fullSource.c_str(),
6774 fullDest.c_str());
6775 if (!isRegularFile(fullSource))
6776 {
6777 error("copy : file %s does not exist", fullSource.c_str());
6778 return false;
6779 }
6780 if (!isNewerThan(fullSource, fullDest))
6781 {
6782 taskstatus("skipped");
6783 return true;
6784 }
6785 if (!copyFile(fullSource, fullDest))
6786 return false;
6787 taskstatus("1 file copied");
6788 }
6789 return true;
6790 }
6791 case CP_TODIR:
6792 {
6793 if (haveFileSet)
6794 {
6795 if (!listFiles(parent, fileSet))
6796 return false;
6797 String fileSetDir = parent.eval(fileSet.getDirectory(), ".");
6799 taskstatus("%s to %s",
6800 fileSetDir.c_str(), toDirName.c_str());
6802 int nrFiles = 0;
6803 for (unsigned int i=0 ; i<fileSet.size() ; i++)
6804 {
6805 String fileName = fileSet[i];
6807 String sourcePath;
6808 if (fileSetDir.size()>0)
6809 {
6810 sourcePath.append(fileSetDir);
6811 sourcePath.append("/");
6812 }
6813 sourcePath.append(fileName);
6814 String fullSource = parent.resolve(sourcePath);
6816 //Get the immediate parent directory's base name
6817 String baseFileSetDir = fileSetDir;
6818 unsigned int pos = baseFileSetDir.find_last_of('/');
6819 if (pos!=baseFileSetDir.npos &&
6820 pos < baseFileSetDir.size()-1)
6821 baseFileSetDir =
6822 baseFileSetDir.substr(pos+1,
6823 baseFileSetDir.size());
6824 //Now make the new path
6825 String destPath;
6826 if (toDirName.size()>0)
6827 {
6828 destPath.append(toDirName);
6829 destPath.append("/");
6830 }
6831 if (baseFileSetDir.size()>0)
6832 {
6833 destPath.append(baseFileSetDir);
6834 destPath.append("/");
6835 }
6836 destPath.append(fileName);
6837 String fullDest = parent.resolve(destPath);
6838 //trace("fileName:%s", fileName.c_str());
6839 if (verbose)
6840 taskstatus("copy %s to new dir : %s",
6841 fullSource.c_str(), fullDest.c_str());
6842 if (!isNewerThan(fullSource, fullDest))
6843 {
6844 if (verbose)
6845 taskstatus("copy skipping %s", fullSource.c_str());
6846 continue;
6847 }
6848 if (!copyFile(fullSource, fullDest))
6849 return false;
6850 nrFiles++;
6851 }
6852 taskstatus("%d file(s) copied", nrFiles);
6853 }
6854 else //file source
6855 {
6856 //For file->dir we want only the basename of
6857 //the source appended to the dest dir
6858 taskstatus("%s to %s",
6859 fileName.c_str(), toDirName.c_str());
6860 String baseName = fileName;
6861 unsigned int pos = baseName.find_last_of('/');
6862 if (pos!=baseName.npos && pos<baseName.size()-1)
6863 baseName = baseName.substr(pos+1, baseName.size());
6864 String fullSource = parent.resolve(fileName);
6865 String destPath;
6866 if (toDirName.size()>0)
6867 {
6868 destPath.append(toDirName);
6869 destPath.append("/");
6870 }
6871 destPath.append(baseName);
6872 String fullDest = parent.resolve(destPath);
6873 if (verbose)
6874 taskstatus("file %s to new dir : %s", fullSource.c_str(),
6875 fullDest.c_str());
6876 if (!isRegularFile(fullSource))
6877 {
6878 error("copy : file %s does not exist", fullSource.c_str());
6879 return false;
6880 }
6881 if (!isNewerThan(fullSource, fullDest))
6882 {
6883 taskstatus("skipped");
6884 return true;
6885 }
6886 if (!copyFile(fullSource, fullDest))
6887 return false;
6888 taskstatus("1 file copied");
6889 }
6890 return true;
6891 }
6892 }
6893 return true;
6894 }
6897 virtual bool parse(Element *elem)
6898 {
6899 if (!parent.getAttribute(elem, "file", fileNameOpt))
6900 return false;
6901 if (!parent.getAttribute(elem, "tofile", toFileNameOpt))
6902 return false;
6903 if (toFileNameOpt.size() > 0)
6904 cptype = CP_TOFILE;
6905 if (!parent.getAttribute(elem, "todir", toDirNameOpt))
6906 return false;
6907 if (toDirNameOpt.size() > 0)
6908 cptype = CP_TODIR;
6909 if (!parent.getAttribute(elem, "verbose", verboseOpt))
6910 return false;
6912 haveFileSet = false;
6914 std::vector<Element *> children = elem->getChildren();
6915 for (unsigned int i=0 ; i<children.size() ; i++)
6916 {
6917 Element *child = children[i];
6918 String tagName = child->getName();
6919 if (tagName == "fileset")
6920 {
6921 if (!parseFileSet(child, parent, fileSet))
6922 {
6923 error("problem getting fileset");
6924 return false;
6925 }
6926 haveFileSet = true;
6927 }
6928 }
6930 //Perform validity checks
6931 if (fileNameOpt.size()>0 && fileSet.size()>0)
6932 {
6933 error("<copy> can only have one of : file= and <fileset>");
6934 return false;
6935 }
6936 if (toFileNameOpt.size()>0 && toDirNameOpt.size()>0)
6937 {
6938 error("<copy> can only have one of : tofile= or todir=");
6939 return false;
6940 }
6941 if (haveFileSet && toDirNameOpt.size()==0)
6942 {
6943 error("a <copy> task with a <fileset> must have : todir=");
6944 return false;
6945 }
6946 if (cptype == CP_TOFILE && fileNameOpt.size()==0)
6947 {
6948 error("<copy> tofile= must be associated with : file=");
6949 return false;
6950 }
6951 if (cptype == CP_TODIR && fileNameOpt.size()==0 && !haveFileSet)
6952 {
6953 error("<copy> todir= must be associated with : file= or <fileset>");
6954 return false;
6955 }
6957 return true;
6958 }
6960 private:
6962 int cptype;
6963 bool haveFileSet;
6965 FileSet fileSet;
6966 String fileNameOpt;
6967 String toFileNameOpt;
6968 String toDirNameOpt;
6969 String verboseOpt;
6970 };
6973 /**
6974 *
6975 */
6976 class TaskDelete : public Task
6977 {
6978 public:
6980 typedef enum
6981 {
6982 DEL_FILE,
6983 DEL_DIR,
6984 DEL_FILESET
6985 } DeleteType;
6987 TaskDelete(MakeBase &par) : Task(par)
6988 {
6989 type = TASK_DELETE;
6990 name = "delete";
6991 delType = DEL_FILE;
6992 }
6994 virtual ~TaskDelete()
6995 {}
6997 virtual bool execute()
6998 {
6999 String dirName = parent.eval(dirNameOpt, ".");
7000 String fileName = parent.eval(fileNameOpt, ".");
7001 bool verbose = parent.evalBool(verboseOpt, false);
7002 bool quiet = parent.evalBool(quietOpt, false);
7003 bool failOnError = parent.evalBool(failOnErrorOpt, true);
7004 struct stat finfo;
7005 switch (delType)
7006 {
7007 case DEL_FILE:
7008 {
7009 taskstatus("file: %s", fileName.c_str());
7010 String fullName = parent.resolve(fileName);
7011 char *fname = (char *)fullName.c_str();
7012 if (!quiet && verbose)
7013 taskstatus("path: %s", fname);
7014 //does not exist
7015 if (stat(fname, &finfo)<0)
7016 {
7017 if (failOnError)
7018 return false;
7019 else
7020 return true;
7021 }
7022 //exists but is not a regular file
7023 if (!S_ISREG(finfo.st_mode))
7024 {
7025 error("<delete> failed. '%s' exists and is not a regular file",
7026 fname);
7027 return false;
7028 }
7029 if (remove(fname)<0)
7030 {
7031 error("<delete> failed: %s", strerror(errno));
7032 return false;
7033 }
7034 return true;
7035 }
7036 case DEL_DIR:
7037 {
7038 taskstatus("dir: %s", dirName.c_str());
7039 String fullDir = parent.resolve(dirName);
7040 if (!quiet && verbose)
7041 taskstatus("path: %s", fullDir.c_str());
7042 if (!removeDirectory(fullDir))
7043 return false;
7044 return true;
7045 }
7046 }
7047 return true;
7048 }
7050 virtual bool parse(Element *elem)
7051 {
7052 if (!parent.getAttribute(elem, "file", fileNameOpt))
7053 return false;
7054 if (fileNameOpt.size() > 0)
7055 delType = DEL_FILE;
7056 if (!parent.getAttribute(elem, "dir", dirNameOpt))
7057 return false;
7058 if (dirNameOpt.size() > 0)
7059 delType = DEL_DIR;
7060 if (fileNameOpt.size()>0 && dirNameOpt.size()>0)
7061 {
7062 error("<delete> can have one attribute of file= or dir=");
7063 return false;
7064 }
7065 if (fileNameOpt.size()==0 && dirNameOpt.size()==0)
7066 {
7067 error("<delete> must have one attribute of file= or dir=");
7068 return false;
7069 }
7070 if (!parent.getAttribute(elem, "verbose", verboseOpt))
7071 return false;
7072 if (!parent.getAttribute(elem, "quiet", quietOpt))
7073 return false;
7074 if (!parent.getAttribute(elem, "failonerror", failOnErrorOpt))
7075 return false;
7076 return true;
7077 }
7079 private:
7081 int delType;
7082 String dirNameOpt;
7083 String fileNameOpt;
7084 String verboseOpt;
7085 String quietOpt;
7086 String failOnErrorOpt;
7087 };
7090 /**
7091 * Send a message to stdout
7092 */
7093 class TaskEcho : public Task
7094 {
7095 public:
7097 TaskEcho(MakeBase &par) : Task(par)
7098 { type = TASK_ECHO; name = "echo"; }
7100 virtual ~TaskEcho()
7101 {}
7103 virtual bool execute()
7104 {
7105 //let message have priority over text
7106 String message = parent.eval(messageOpt, "");
7107 String text = parent.eval(textOpt, "");
7108 if (message.size() > 0)
7109 {
7110 fprintf(stdout, "%s\n", message.c_str());
7111 }
7112 else if (text.size() > 0)
7113 {
7114 fprintf(stdout, "%s\n", text.c_str());
7115 }
7116 return true;
7117 }
7119 virtual bool parse(Element *elem)
7120 {
7121 if (!parent.getValue(elem, textOpt))
7122 return false;
7123 textOpt = leftJustify(textOpt);
7124 if (!parent.getAttribute(elem, "message", messageOpt))
7125 return false;
7126 return true;
7127 }
7129 private:
7131 String messageOpt;
7132 String textOpt;
7133 };
7137 /**
7138 *
7139 */
7140 class TaskJar : public Task
7141 {
7142 public:
7144 TaskJar(MakeBase &par) : Task(par)
7145 { type = TASK_JAR; name = "jar"; }
7147 virtual ~TaskJar()
7148 {}
7150 virtual bool execute()
7151 {
7152 String command = parent.eval(commandOpt, "jar");
7153 String basedir = parent.eval(basedirOpt, ".");
7154 String destfile = parent.eval(destfileOpt, ".");
7156 String cmd = command;
7157 cmd.append(" -cf ");
7158 cmd.append(destfile);
7159 cmd.append(" -C ");
7160 cmd.append(basedir);
7161 cmd.append(" .");
7163 String execCmd = cmd;
7165 String outString, errString;
7166 bool ret = executeCommand(execCmd.c_str(), "", outString, errString);
7167 if (!ret)
7168 {
7169 error("<jar> command '%s' failed :\n %s",
7170 execCmd.c_str(), errString.c_str());
7171 return false;
7172 }
7173 return true;
7174 }
7176 virtual bool parse(Element *elem)
7177 {
7178 if (!parent.getAttribute(elem, "command", commandOpt))
7179 return false;
7180 if (!parent.getAttribute(elem, "basedir", basedirOpt))
7181 return false;
7182 if (!parent.getAttribute(elem, "destfile", destfileOpt))
7183 return false;
7184 if (basedirOpt.size() == 0 || destfileOpt.size() == 0)
7185 {
7186 error("<jar> required both basedir and destfile attributes to be set");
7187 return false;
7188 }
7189 return true;
7190 }
7192 private:
7194 String commandOpt;
7195 String basedirOpt;
7196 String destfileOpt;
7197 };
7200 /**
7201 *
7202 */
7203 class TaskJavac : public Task
7204 {
7205 public:
7207 TaskJavac(MakeBase &par) : Task(par)
7208 {
7209 type = TASK_JAVAC; name = "javac";
7210 }
7212 virtual ~TaskJavac()
7213 {}
7215 virtual bool execute()
7216 {
7217 String command = parent.eval(commandOpt, "javac");
7218 String srcdir = parent.eval(srcdirOpt, ".");
7219 String destdir = parent.eval(destdirOpt, ".");
7220 String target = parent.eval(targetOpt, "");
7222 std::vector<String> fileList;
7223 if (!listFiles(srcdir, "", fileList))
7224 {
7225 return false;
7226 }
7227 String cmd = command;
7228 cmd.append(" -d ");
7229 cmd.append(destdir);
7230 cmd.append(" -classpath ");
7231 cmd.append(destdir);
7232 cmd.append(" -sourcepath ");
7233 cmd.append(srcdir);
7234 cmd.append(" ");
7235 if (target.size()>0)
7236 {
7237 cmd.append(" -target ");
7238 cmd.append(target);
7239 cmd.append(" ");
7240 }
7241 String fname = "javalist.btool";
7242 FILE *f = fopen(fname.c_str(), "w");
7243 int count = 0;
7244 for (unsigned int i=0 ; i<fileList.size() ; i++)
7245 {
7246 String fname = fileList[i];
7247 String srcName = fname;
7248 if (fname.size()<6) //x.java
7249 continue;
7250 if (fname.compare(fname.size()-5, 5, ".java") != 0)
7251 continue;
7252 String baseName = fname.substr(0, fname.size()-5);
7253 String destName = baseName;
7254 destName.append(".class");
7256 String fullSrc = srcdir;
7257 fullSrc.append("/");
7258 fullSrc.append(fname);
7259 String fullDest = destdir;
7260 fullDest.append("/");
7261 fullDest.append(destName);
7262 //trace("fullsrc:%s fulldest:%s", fullSrc.c_str(), fullDest.c_str());
7263 if (!isNewerThan(fullSrc, fullDest))
7264 continue;
7266 count++;
7267 fprintf(f, "%s\n", fullSrc.c_str());
7268 }
7269 fclose(f);
7270 if (!count)
7271 {
7272 taskstatus("nothing to do");
7273 return true;
7274 }
7276 taskstatus("compiling %d files", count);
7278 String execCmd = cmd;
7279 execCmd.append("@");
7280 execCmd.append(fname);
7282 String outString, errString;
7283 bool ret = executeCommand(execCmd.c_str(), "", outString, errString);
7284 if (!ret)
7285 {
7286 error("<javac> command '%s' failed :\n %s",
7287 execCmd.c_str(), errString.c_str());
7288 return false;
7289 }
7290 return true;
7291 }
7293 virtual bool parse(Element *elem)
7294 {
7295 if (!parent.getAttribute(elem, "command", commandOpt))
7296 return false;
7297 if (!parent.getAttribute(elem, "srcdir", srcdirOpt))
7298 return false;
7299 if (!parent.getAttribute(elem, "destdir", destdirOpt))
7300 return false;
7301 if (srcdirOpt.size() == 0 || destdirOpt.size() == 0)
7302 {
7303 error("<javac> required both srcdir and destdir attributes to be set");
7304 return false;
7305 }
7306 if (!parent.getAttribute(elem, "target", targetOpt))
7307 return false;
7308 return true;
7309 }
7311 private:
7313 String commandOpt;
7314 String srcdirOpt;
7315 String destdirOpt;
7316 String targetOpt;
7318 };
7321 /**
7322 *
7323 */
7324 class TaskLink : public Task
7325 {
7326 public:
7328 TaskLink(MakeBase &par) : Task(par)
7329 {
7330 type = TASK_LINK; name = "link";
7331 }
7333 virtual ~TaskLink()
7334 {}
7336 virtual bool execute()
7337 {
7338 String command = parent.eval(commandOpt, "g++");
7339 String fileName = parent.eval(fileNameOpt, "");
7340 String flags = parent.eval(flagsOpt, "");
7341 String libs = parent.eval(libsOpt, "");
7342 bool doStrip = parent.evalBool(doStripOpt, false);
7343 String symFileName = parent.eval(symFileNameOpt, "");
7344 String stripCommand = parent.eval(stripCommandOpt, "strip");
7345 String objcopyCommand = parent.eval(objcopyCommandOpt, "objcopy");
7347 if (!listFiles(parent, fileSet))
7348 return false;
7349 String fileSetDir = parent.eval(fileSet.getDirectory(), ".");
7350 //trace("%d files in %s", fileSet.size(), fileSetDir.c_str());
7351 bool doit = false;
7352 String fullTarget = parent.resolve(fileName);
7353 String cmd = command;
7354 cmd.append(" -o ");
7355 cmd.append(fullTarget);
7356 cmd.append(" ");
7357 cmd.append(flags);
7358 for (unsigned int i=0 ; i<fileSet.size() ; i++)
7359 {
7360 cmd.append(" ");
7361 String obj;
7362 if (fileSetDir.size()>0)
7363 {
7364 obj.append(fileSetDir);
7365 obj.append("/");
7366 }
7367 obj.append(fileSet[i]);
7368 String fullObj = parent.resolve(obj);
7369 String nativeFullObj = getNativePath(fullObj);
7370 cmd.append(nativeFullObj);
7371 //trace("link: tgt:%s obj:%s", fullTarget.c_str(),
7372 // fullObj.c_str());
7373 if (isNewerThan(fullObj, fullTarget))
7374 doit = true;
7375 }
7376 cmd.append(" ");
7377 cmd.append(libs);
7378 if (!doit)
7379 {
7380 //trace("link not needed");
7381 return true;
7382 }
7383 //trace("LINK cmd:%s", cmd.c_str());
7386 String outbuf, errbuf;
7387 if (!executeCommand(cmd.c_str(), "", outbuf, errbuf))
7388 {
7389 error("LINK problem: %s", errbuf.c_str());
7390 return false;
7391 }
7393 if (symFileName.size()>0)
7394 {
7395 String symFullName = parent.resolve(symFileName);
7396 cmd = objcopyCommand;
7397 cmd.append(" --only-keep-debug ");
7398 cmd.append(getNativePath(fullTarget));
7399 cmd.append(" ");
7400 cmd.append(getNativePath(symFullName));
7401 if (!executeCommand(cmd, "", outbuf, errbuf))
7402 {
7403 error("<strip> symbol file failed : %s", errbuf.c_str());
7404 return false;
7405 }
7406 }
7408 if (doStrip)
7409 {
7410 cmd = stripCommand;
7411 cmd.append(" ");
7412 cmd.append(getNativePath(fullTarget));
7413 if (!executeCommand(cmd, "", outbuf, errbuf))
7414 {
7415 error("<strip> failed : %s", errbuf.c_str());
7416 return false;
7417 }
7418 }
7420 return true;
7421 }
7423 virtual bool parse(Element *elem)
7424 {
7425 if (!parent.getAttribute(elem, "command", commandOpt))
7426 return false;
7427 if (!parent.getAttribute(elem, "objcopycommand", objcopyCommandOpt))
7428 return false;
7429 if (!parent.getAttribute(elem, "stripcommand", stripCommandOpt))
7430 return false;
7431 if (!parent.getAttribute(elem, "out", fileNameOpt))
7432 return false;
7433 if (!parent.getAttribute(elem, "strip", doStripOpt))
7434 return false;
7435 if (!parent.getAttribute(elem, "symfile", symFileNameOpt))
7436 return false;
7438 std::vector<Element *> children = elem->getChildren();
7439 for (unsigned int i=0 ; i<children.size() ; i++)
7440 {
7441 Element *child = children[i];
7442 String tagName = child->getName();
7443 if (tagName == "fileset")
7444 {
7445 if (!parseFileSet(child, parent, fileSet))
7446 return false;
7447 }
7448 else if (tagName == "flags")
7449 {
7450 if (!parent.getValue(child, flagsOpt))
7451 return false;
7452 flagsOpt = strip(flagsOpt);
7453 }
7454 else if (tagName == "libs")
7455 {
7456 if (!parent.getValue(child, libsOpt))
7457 return false;
7458 libsOpt = strip(libsOpt);
7459 }
7460 }
7461 return true;
7462 }
7464 private:
7466 FileSet fileSet;
7468 String commandOpt;
7469 String fileNameOpt;
7470 String flagsOpt;
7471 String libsOpt;
7472 String doStripOpt;
7473 String symFileNameOpt;
7474 String stripCommandOpt;
7475 String objcopyCommandOpt;
7477 };
7481 /**
7482 * Create a named file
7483 */
7484 class TaskMakeFile : public Task
7485 {
7486 public:
7488 TaskMakeFile(MakeBase &par) : Task(par)
7489 { type = TASK_MAKEFILE; name = "makefile"; }
7491 virtual ~TaskMakeFile()
7492 {}
7494 virtual bool execute()
7495 {
7496 String fileName = parent.eval(fileNameOpt, "");
7497 String text = parent.eval(textOpt, "");
7499 taskstatus("%s", fileName.c_str());
7500 String fullName = parent.resolve(fileName);
7501 if (!isNewerThan(parent.getURI().getPath(), fullName))
7502 {
7503 //trace("skipped <makefile>");
7504 return true;
7505 }
7506 String fullNative = getNativePath(fullName);
7507 //trace("fullName:%s", fullName.c_str());
7508 FILE *f = fopen(fullNative.c_str(), "w");
7509 if (!f)
7510 {
7511 error("<makefile> could not open %s for writing : %s",
7512 fullName.c_str(), strerror(errno));
7513 return false;
7514 }
7515 for (unsigned int i=0 ; i<text.size() ; i++)
7516 fputc(text[i], f);
7517 fputc('\n', f);
7518 fclose(f);
7519 return true;
7520 }
7522 virtual bool parse(Element *elem)
7523 {
7524 if (!parent.getAttribute(elem, "file", fileNameOpt))
7525 return false;
7526 if (fileNameOpt.size() == 0)
7527 {
7528 error("<makefile> requires 'file=\"filename\"' attribute");
7529 return false;
7530 }
7531 if (!parent.getValue(elem, textOpt))
7532 return false;
7533 textOpt = leftJustify(textOpt);
7534 //trace("dirname:%s", dirName.c_str());
7535 return true;
7536 }
7538 private:
7540 String fileNameOpt;
7541 String textOpt;
7542 };
7546 /**
7547 * Create a named directory
7548 */
7549 class TaskMkDir : public Task
7550 {
7551 public:
7553 TaskMkDir(MakeBase &par) : Task(par)
7554 { type = TASK_MKDIR; name = "mkdir"; }
7556 virtual ~TaskMkDir()
7557 {}
7559 virtual bool execute()
7560 {
7561 String dirName = parent.eval(dirNameOpt, ".");
7563 taskstatus("%s", dirName.c_str());
7564 String fullDir = parent.resolve(dirName);
7565 //trace("fullDir:%s", fullDir.c_str());
7566 if (!createDirectory(fullDir))
7567 return false;
7568 return true;
7569 }
7571 virtual bool parse(Element *elem)
7572 {
7573 if (!parent.getAttribute(elem, "dir", dirNameOpt))
7574 return false;
7575 if (dirNameOpt.size() == 0)
7576 {
7577 error("<mkdir> requires 'dir=\"dirname\"' attribute");
7578 return false;
7579 }
7580 return true;
7581 }
7583 private:
7585 String dirNameOpt;
7586 };
7590 /**
7591 * Create a named directory
7592 */
7593 class TaskMsgFmt: public Task
7594 {
7595 public:
7597 TaskMsgFmt(MakeBase &par) : Task(par)
7598 { type = TASK_MSGFMT; name = "msgfmt"; }
7600 virtual ~TaskMsgFmt()
7601 {}
7603 virtual bool execute()
7604 {
7605 String command = parent.eval(commandOpt, "msgfmt");
7606 String toDirName = parent.eval(toDirNameOpt, ".");
7607 String outName = parent.eval(outNameOpt, "");
7608 bool owndir = parent.evalBool(owndirOpt, false);
7610 if (!listFiles(parent, fileSet))
7611 return false;
7612 String fileSetDir = fileSet.getDirectory();
7614 //trace("msgfmt: %d", fileSet.size());
7615 for (unsigned int i=0 ; i<fileSet.size() ; i++)
7616 {
7617 String fileName = fileSet[i];
7618 if (getSuffix(fileName) != "po")
7619 continue;
7620 String sourcePath;
7621 if (fileSetDir.size()>0)
7622 {
7623 sourcePath.append(fileSetDir);
7624 sourcePath.append("/");
7625 }
7626 sourcePath.append(fileName);
7627 String fullSource = parent.resolve(sourcePath);
7629 String destPath;
7630 if (toDirName.size()>0)
7631 {
7632 destPath.append(toDirName);
7633 destPath.append("/");
7634 }
7635 if (owndir)
7636 {
7637 String subdir = fileName;
7638 unsigned int pos = subdir.find_last_of('.');
7639 if (pos != subdir.npos)
7640 subdir = subdir.substr(0, pos);
7641 destPath.append(subdir);
7642 destPath.append("/");
7643 }
7644 //Pick the output file name
7645 if (outName.size() > 0)
7646 {
7647 destPath.append(outName);
7648 }
7649 else
7650 {
7651 destPath.append(fileName);
7652 destPath[destPath.size()-2] = 'm';
7653 }
7655 String fullDest = parent.resolve(destPath);
7657 if (!isNewerThan(fullSource, fullDest))
7658 {
7659 //trace("skip %s", fullSource.c_str());
7660 continue;
7661 }
7663 String cmd = command;
7664 cmd.append(" ");
7665 cmd.append(fullSource);
7666 cmd.append(" -o ");
7667 cmd.append(fullDest);
7669 int pos = fullDest.find_last_of('/');
7670 if (pos>0)
7671 {
7672 String fullDestPath = fullDest.substr(0, pos);
7673 if (!createDirectory(fullDestPath))
7674 return false;
7675 }
7679 String outString, errString;
7680 if (!executeCommand(cmd.c_str(), "", outString, errString))
7681 {
7682 error("<msgfmt> problem: %s", errString.c_str());
7683 return false;
7684 }
7685 }
7687 return true;
7688 }
7690 virtual bool parse(Element *elem)
7691 {
7692 if (!parent.getAttribute(elem, "command", commandOpt))
7693 return false;
7694 if (!parent.getAttribute(elem, "todir", toDirNameOpt))
7695 return false;
7696 if (!parent.getAttribute(elem, "out", outNameOpt))
7697 return false;
7698 if (!parent.getAttribute(elem, "owndir", owndirOpt))
7699 return false;
7701 std::vector<Element *> children = elem->getChildren();
7702 for (unsigned int i=0 ; i<children.size() ; i++)
7703 {
7704 Element *child = children[i];
7705 String tagName = child->getName();
7706 if (tagName == "fileset")
7707 {
7708 if (!parseFileSet(child, parent, fileSet))
7709 return false;
7710 }
7711 }
7712 return true;
7713 }
7715 private:
7717 FileSet fileSet;
7719 String commandOpt;
7720 String toDirNameOpt;
7721 String outNameOpt;
7722 String owndirOpt;
7724 };
7728 /**
7729 * Perform a Package-Config query similar to pkg-config
7730 */
7731 class TaskPkgConfig : public Task
7732 {
7733 public:
7735 typedef enum
7736 {
7737 PKG_CONFIG_QUERY_CFLAGS,
7738 PKG_CONFIG_QUERY_LIBS,
7739 PKG_CONFIG_QUERY_ALL
7740 } QueryTypes;
7742 TaskPkgConfig(MakeBase &par) : Task(par)
7743 {
7744 type = TASK_PKG_CONFIG;
7745 name = "pkg-config";
7746 }
7748 virtual ~TaskPkgConfig()
7749 {}
7751 virtual bool execute()
7752 {
7753 String pkgName = parent.eval(pkgNameOpt, "");
7754 String prefix = parent.eval(prefixOpt, "");
7755 String propName = parent.eval(propNameOpt, "");
7756 String pkgConfigPath = parent.eval(pkgConfigPathOpt,"");
7757 String query = parent.eval(queryOpt, "all");
7759 String path = parent.resolve(pkgConfigPath);
7760 PkgConfig pkgconfig;
7761 pkgconfig.setPath(path);
7762 pkgconfig.setPrefix(prefix);
7763 if (!pkgconfig.query(pkgName))
7764 {
7765 error("<pkg-config> query failed for '%s", name.c_str());
7766 return false;
7767 }
7769 String val = "";
7770 if (query == "cflags")
7771 val = pkgconfig.getCflags();
7772 else if (query == "libs")
7773 val =pkgconfig.getLibs();
7774 else if (query == "all")
7775 val = pkgconfig.getAll();
7776 else
7777 {
7778 error("<pkg-config> unhandled query : %s", query.c_str());
7779 return false;
7780 }
7781 taskstatus("property %s = '%s'", propName.c_str(), val.c_str());
7782 parent.setProperty(propName, val);
7783 return true;
7784 }
7786 virtual bool parse(Element *elem)
7787 {
7788 //# NAME
7789 if (!parent.getAttribute(elem, "name", pkgNameOpt))
7790 return false;
7791 if (pkgNameOpt.size()==0)
7792 {
7793 error("<pkg-config> requires 'name=\"package\"' attribute");
7794 return false;
7795 }
7797 //# PROPERTY
7798 if (!parent.getAttribute(elem, "property", propNameOpt))
7799 return false;
7800 if (propNameOpt.size()==0)
7801 {
7802 error("<pkg-config> requires 'property=\"name\"' attribute");
7803 return false;
7804 }
7805 //# PATH
7806 if (!parent.getAttribute(elem, "path", pkgConfigPathOpt))
7807 return false;
7808 //# PREFIX
7809 if (!parent.getAttribute(elem, "prefix", prefixOpt))
7810 return false;
7811 //# QUERY
7812 if (!parent.getAttribute(elem, "query", queryOpt))
7813 return false;
7815 return true;
7816 }
7818 private:
7820 String queryOpt;
7821 String pkgNameOpt;
7822 String prefixOpt;
7823 String propNameOpt;
7824 String pkgConfigPathOpt;
7826 };
7833 /**
7834 * Process an archive to allow random access
7835 */
7836 class TaskRanlib : public Task
7837 {
7838 public:
7840 TaskRanlib(MakeBase &par) : Task(par)
7841 { type = TASK_RANLIB; name = "ranlib"; }
7843 virtual ~TaskRanlib()
7844 {}
7846 virtual bool execute()
7847 {
7848 String fileName = parent.eval(fileNameOpt, "");
7849 String command = parent.eval(commandOpt, "ranlib");
7851 String fullName = parent.resolve(fileName);
7852 //trace("fullDir:%s", fullDir.c_str());
7853 String cmd = command;
7854 cmd.append(" ");
7855 cmd.append(fullName);
7856 String outbuf, errbuf;
7857 if (!executeCommand(cmd, "", outbuf, errbuf))
7858 return false;
7859 return true;
7860 }
7862 virtual bool parse(Element *elem)
7863 {
7864 if (!parent.getAttribute(elem, "command", commandOpt))
7865 return false;
7866 if (!parent.getAttribute(elem, "file", fileNameOpt))
7867 return false;
7868 if (fileNameOpt.size() == 0)
7869 {
7870 error("<ranlib> requires 'file=\"fileNname\"' attribute");
7871 return false;
7872 }
7873 return true;
7874 }
7876 private:
7878 String fileNameOpt;
7879 String commandOpt;
7880 };
7884 /**
7885 * Compile a resource file into a binary object
7886 */
7887 class TaskRC : public Task
7888 {
7889 public:
7891 TaskRC(MakeBase &par) : Task(par)
7892 { type = TASK_RC; name = "rc"; }
7894 virtual ~TaskRC()
7895 {}
7897 virtual bool execute()
7898 {
7899 String command = parent.eval(commandOpt, "windres");
7900 String flags = parent.eval(flagsOpt, "");
7901 String fileName = parent.eval(fileNameOpt, "");
7902 String outName = parent.eval(outNameOpt, "");
7904 String fullFile = parent.resolve(fileName);
7905 String fullOut = parent.resolve(outName);
7906 if (!isNewerThan(fullFile, fullOut))
7907 return true;
7908 String cmd = command;
7909 cmd.append(" -o ");
7910 cmd.append(fullOut);
7911 cmd.append(" ");
7912 cmd.append(flags);
7913 cmd.append(" ");
7914 cmd.append(fullFile);
7916 String outString, errString;
7917 if (!executeCommand(cmd.c_str(), "", outString, errString))
7918 {
7919 error("RC problem: %s", errString.c_str());
7920 return false;
7921 }
7922 return true;
7923 }
7925 virtual bool parse(Element *elem)
7926 {
7927 if (!parent.getAttribute(elem, "command", commandOpt))
7928 return false;
7929 if (!parent.getAttribute(elem, "file", fileNameOpt))
7930 return false;
7931 if (!parent.getAttribute(elem, "out", outNameOpt))
7932 return false;
7933 std::vector<Element *> children = elem->getChildren();
7934 for (unsigned int i=0 ; i<children.size() ; i++)
7935 {
7936 Element *child = children[i];
7937 String tagName = child->getName();
7938 if (tagName == "flags")
7939 {
7940 if (!parent.getValue(child, flagsOpt))
7941 return false;
7942 }
7943 }
7944 return true;
7945 }
7947 private:
7949 String commandOpt;
7950 String flagsOpt;
7951 String fileNameOpt;
7952 String outNameOpt;
7954 };
7958 /**
7959 * Collect .o's into a .so or DLL
7960 */
7961 class TaskSharedLib : public Task
7962 {
7963 public:
7965 TaskSharedLib(MakeBase &par) : Task(par)
7966 { type = TASK_SHAREDLIB; name = "dll"; }
7968 virtual ~TaskSharedLib()
7969 {}
7971 virtual bool execute()
7972 {
7973 String command = parent.eval(commandOpt, "dllwrap");
7974 String fileName = parent.eval(fileNameOpt, "");
7975 String defFileName = parent.eval(defFileNameOpt, "");
7976 String impFileName = parent.eval(impFileNameOpt, "");
7977 String libs = parent.eval(libsOpt, "");
7979 //trace("###########HERE %d", fileSet.size());
7980 bool doit = false;
7982 String fullOut = parent.resolve(fileName);
7983 //trace("ar fullout: %s", fullOut.c_str());
7985 if (!listFiles(parent, fileSet))
7986 return false;
7987 String fileSetDir = parent.eval(fileSet.getDirectory(), ".");
7989 for (unsigned int i=0 ; i<fileSet.size() ; i++)
7990 {
7991 String fname;
7992 if (fileSetDir.size()>0)
7993 {
7994 fname.append(fileSetDir);
7995 fname.append("/");
7996 }
7997 fname.append(fileSet[i]);
7998 String fullName = parent.resolve(fname);
7999 //trace("ar : %s/%s", fullOut.c_str(), fullName.c_str());
8000 if (isNewerThan(fullName, fullOut))
8001 doit = true;
8002 }
8003 //trace("Needs it:%d", doit);
8004 if (!doit)
8005 {
8006 return true;
8007 }
8009 String cmd = "dllwrap";
8010 cmd.append(" -o ");
8011 cmd.append(fullOut);
8012 if (defFileName.size()>0)
8013 {
8014 cmd.append(" --def ");
8015 cmd.append(defFileName);
8016 cmd.append(" ");
8017 }
8018 if (impFileName.size()>0)
8019 {
8020 cmd.append(" --implib ");
8021 cmd.append(impFileName);
8022 cmd.append(" ");
8023 }
8024 for (unsigned int i=0 ; i<fileSet.size() ; i++)
8025 {
8026 String fname;
8027 if (fileSetDir.size()>0)
8028 {
8029 fname.append(fileSetDir);
8030 fname.append("/");
8031 }
8032 fname.append(fileSet[i]);
8033 String fullName = parent.resolve(fname);
8035 cmd.append(" ");
8036 cmd.append(fullName);
8037 }
8038 cmd.append(" ");
8039 cmd.append(libs);
8041 String outString, errString;
8042 if (!executeCommand(cmd.c_str(), "", outString, errString))
8043 {
8044 error("<sharedlib> problem: %s", errString.c_str());
8045 return false;
8046 }
8048 return true;
8049 }
8051 virtual bool parse(Element *elem)
8052 {
8053 if (!parent.getAttribute(elem, "command", commandOpt))
8054 return false;
8055 if (!parent.getAttribute(elem, "file", fileNameOpt))
8056 return false;
8057 if (!parent.getAttribute(elem, "import", impFileNameOpt))
8058 return false;
8059 if (!parent.getAttribute(elem, "def", defFileNameOpt))
8060 return false;
8062 std::vector<Element *> children = elem->getChildren();
8063 for (unsigned int i=0 ; i<children.size() ; i++)
8064 {
8065 Element *child = children[i];
8066 String tagName = child->getName();
8067 if (tagName == "fileset")
8068 {
8069 if (!parseFileSet(child, parent, fileSet))
8070 return false;
8071 }
8072 else if (tagName == "libs")
8073 {
8074 if (!parent.getValue(child, libsOpt))
8075 return false;
8076 libsOpt = strip(libsOpt);
8077 }
8078 }
8079 return true;
8080 }
8082 private:
8084 FileSet fileSet;
8086 String commandOpt;
8087 String fileNameOpt;
8088 String defFileNameOpt;
8089 String impFileNameOpt;
8090 String libsOpt;
8092 };
8096 /**
8097 * Run the "ar" command to archive .o's into a .a
8098 */
8099 class TaskStaticLib : public Task
8100 {
8101 public:
8103 TaskStaticLib(MakeBase &par) : Task(par)
8104 { type = TASK_STATICLIB; name = "staticlib"; }
8106 virtual ~TaskStaticLib()
8107 {}
8109 virtual bool execute()
8110 {
8111 String command = parent.eval(commandOpt, "ar crv");
8112 String fileName = parent.eval(fileNameOpt, "");
8114 bool doit = false;
8116 String fullOut = parent.resolve(fileName);
8117 //trace("ar fullout: %s", fullOut.c_str());
8119 if (!listFiles(parent, fileSet))
8120 return false;
8121 String fileSetDir = parent.eval(fileSet.getDirectory(), ".");
8122 //trace("###########HERE %s", fileSetDir.c_str());
8124 for (unsigned int i=0 ; i<fileSet.size() ; i++)
8125 {
8126 String fname;
8127 if (fileSetDir.size()>0)
8128 {
8129 fname.append(fileSetDir);
8130 fname.append("/");
8131 }
8132 fname.append(fileSet[i]);
8133 String fullName = parent.resolve(fname);
8134 //trace("ar : %s/%s", fullOut.c_str(), fullName.c_str());
8135 if (isNewerThan(fullName, fullOut))
8136 doit = true;
8137 }
8138 //trace("Needs it:%d", doit);
8139 if (!doit)
8140 {
8141 return true;
8142 }
8144 String cmd = command;
8145 cmd.append(" ");
8146 cmd.append(fullOut);
8147 for (unsigned int i=0 ; i<fileSet.size() ; i++)
8148 {
8149 String fname;
8150 if (fileSetDir.size()>0)
8151 {
8152 fname.append(fileSetDir);
8153 fname.append("/");
8154 }
8155 fname.append(fileSet[i]);
8156 String fullName = parent.resolve(fname);
8158 cmd.append(" ");
8159 cmd.append(fullName);
8160 }
8162 String outString, errString;
8163 if (!executeCommand(cmd.c_str(), "", outString, errString))
8164 {
8165 error("<staticlib> problem: %s", errString.c_str());
8166 return false;
8167 }
8169 return true;
8170 }
8173 virtual bool parse(Element *elem)
8174 {
8175 if (!parent.getAttribute(elem, "command", commandOpt))
8176 return false;
8177 if (!parent.getAttribute(elem, "file", fileNameOpt))
8178 return false;
8180 std::vector<Element *> children = elem->getChildren();
8181 for (unsigned int i=0 ; i<children.size() ; i++)
8182 {
8183 Element *child = children[i];
8184 String tagName = child->getName();
8185 if (tagName == "fileset")
8186 {
8187 if (!parseFileSet(child, parent, fileSet))
8188 return false;
8189 }
8190 }
8191 return true;
8192 }
8194 private:
8196 FileSet fileSet;
8198 String commandOpt;
8199 String fileNameOpt;
8201 };
8206 /**
8207 * Strip an executable
8208 */
8209 class TaskStrip : public Task
8210 {
8211 public:
8213 TaskStrip(MakeBase &par) : Task(par)
8214 { type = TASK_STRIP; name = "strip"; }
8216 virtual ~TaskStrip()
8217 {}
8219 virtual bool execute()
8220 {
8221 String command = parent.eval(commandOpt, "strip");
8222 String fileName = parent.eval(fileNameOpt, "");
8223 String symFileName = parent.eval(symFileNameOpt, "");
8225 String fullName = parent.resolve(fileName);
8226 //trace("fullDir:%s", fullDir.c_str());
8227 String cmd;
8228 String outbuf, errbuf;
8230 if (symFileName.size()>0)
8231 {
8232 String symFullName = parent.resolve(symFileName);
8233 cmd = "objcopy --only-keep-debug ";
8234 cmd.append(getNativePath(fullName));
8235 cmd.append(" ");
8236 cmd.append(getNativePath(symFullName));
8237 if (!executeCommand(cmd, "", outbuf, errbuf))
8238 {
8239 error("<strip> symbol file failed : %s", errbuf.c_str());
8240 return false;
8241 }
8242 }
8244 cmd = command;
8245 cmd.append(getNativePath(fullName));
8246 if (!executeCommand(cmd, "", outbuf, errbuf))
8247 {
8248 error("<strip> failed : %s", errbuf.c_str());
8249 return false;
8250 }
8251 return true;
8252 }
8254 virtual bool parse(Element *elem)
8255 {
8256 if (!parent.getAttribute(elem, "command", commandOpt))
8257 return false;
8258 if (!parent.getAttribute(elem, "file", fileNameOpt))
8259 return false;
8260 if (!parent.getAttribute(elem, "symfile", symFileNameOpt))
8261 return false;
8262 if (fileNameOpt.size() == 0)
8263 {
8264 error("<strip> requires 'file=\"fileName\"' attribute");
8265 return false;
8266 }
8267 return true;
8268 }
8270 private:
8272 String commandOpt;
8273 String fileNameOpt;
8274 String symFileNameOpt;
8275 };
8278 /**
8279 *
8280 */
8281 class TaskTouch : public Task
8282 {
8283 public:
8285 TaskTouch(MakeBase &par) : Task(par)
8286 { type = TASK_TOUCH; name = "touch"; }
8288 virtual ~TaskTouch()
8289 {}
8291 virtual bool execute()
8292 {
8293 String fileName = parent.eval(fileNameOpt, "");
8295 String fullName = parent.resolve(fileName);
8296 String nativeFile = getNativePath(fullName);
8297 if (!isRegularFile(fullName) && !isDirectory(fullName))
8298 {
8299 // S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH
8300 int ret = creat(nativeFile.c_str(), 0666);
8301 if (ret != 0)
8302 {
8303 error("<touch> could not create '%s' : %s",
8304 nativeFile.c_str(), strerror(ret));
8305 return false;
8306 }
8307 return true;
8308 }
8309 int ret = utime(nativeFile.c_str(), (struct utimbuf *)0);
8310 if (ret != 0)
8311 {
8312 error("<touch> could not update the modification time for '%s' : %s",
8313 nativeFile.c_str(), strerror(ret));
8314 return false;
8315 }
8316 return true;
8317 }
8319 virtual bool parse(Element *elem)
8320 {
8321 //trace("touch parse");
8322 if (!parent.getAttribute(elem, "file", fileNameOpt))
8323 return false;
8324 if (fileNameOpt.size() == 0)
8325 {
8326 error("<touch> requires 'file=\"fileName\"' attribute");
8327 return false;
8328 }
8329 return true;
8330 }
8332 String fileNameOpt;
8333 };
8336 /**
8337 *
8338 */
8339 class TaskTstamp : public Task
8340 {
8341 public:
8343 TaskTstamp(MakeBase &par) : Task(par)
8344 { type = TASK_TSTAMP; name = "tstamp"; }
8346 virtual ~TaskTstamp()
8347 {}
8349 virtual bool execute()
8350 {
8351 return true;
8352 }
8354 virtual bool parse(Element *elem)
8355 {
8356 //trace("tstamp parse");
8357 return true;
8358 }
8359 };
8363 /**
8364 *
8365 */
8366 Task *Task::createTask(Element *elem, int lineNr)
8367 {
8368 String tagName = elem->getName();
8369 //trace("task:%s", tagName.c_str());
8370 Task *task = NULL;
8371 if (tagName == "cc")
8372 task = new TaskCC(parent);
8373 else if (tagName == "copy")
8374 task = new TaskCopy(parent);
8375 else if (tagName == "delete")
8376 task = new TaskDelete(parent);
8377 else if (tagName == "echo")
8378 task = new TaskEcho(parent);
8379 else if (tagName == "jar")
8380 task = new TaskJar(parent);
8381 else if (tagName == "javac")
8382 task = new TaskJavac(parent);
8383 else if (tagName == "link")
8384 task = new TaskLink(parent);
8385 else if (tagName == "makefile")
8386 task = new TaskMakeFile(parent);
8387 else if (tagName == "mkdir")
8388 task = new TaskMkDir(parent);
8389 else if (tagName == "msgfmt")
8390 task = new TaskMsgFmt(parent);
8391 else if (tagName == "pkg-config")
8392 task = new TaskPkgConfig(parent);
8393 else if (tagName == "ranlib")
8394 task = new TaskRanlib(parent);
8395 else if (tagName == "rc")
8396 task = new TaskRC(parent);
8397 else if (tagName == "sharedlib")
8398 task = new TaskSharedLib(parent);
8399 else if (tagName == "staticlib")
8400 task = new TaskStaticLib(parent);
8401 else if (tagName == "strip")
8402 task = new TaskStrip(parent);
8403 else if (tagName == "touch")
8404 task = new TaskTouch(parent);
8405 else if (tagName == "tstamp")
8406 task = new TaskTstamp(parent);
8407 else
8408 {
8409 error("Unknown task '%s'", tagName.c_str());
8410 return NULL;
8411 }
8413 task->setLine(lineNr);
8415 if (!task->parse(elem))
8416 {
8417 delete task;
8418 return NULL;
8419 }
8420 return task;
8421 }
8425 //########################################################################
8426 //# T A R G E T
8427 //########################################################################
8429 /**
8430 *
8431 */
8432 class Target : public MakeBase
8433 {
8435 public:
8437 /**
8438 *
8439 */
8440 Target(Make &par) : parent(par)
8441 { init(); }
8443 /**
8444 *
8445 */
8446 Target(const Target &other) : parent(other.parent)
8447 { init(); assign(other); }
8449 /**
8450 *
8451 */
8452 Target &operator=(const Target &other)
8453 { init(); assign(other); return *this; }
8455 /**
8456 *
8457 */
8458 virtual ~Target()
8459 { cleanup() ; }
8462 /**
8463 *
8464 */
8465 virtual Make &getParent()
8466 { return parent; }
8468 /**
8469 *
8470 */
8471 virtual String getName()
8472 { return name; }
8474 /**
8475 *
8476 */
8477 virtual void setName(const String &val)
8478 { name = val; }
8480 /**
8481 *
8482 */
8483 virtual String getDescription()
8484 { return description; }
8486 /**
8487 *
8488 */
8489 virtual void setDescription(const String &val)
8490 { description = val; }
8492 /**
8493 *
8494 */
8495 virtual void addDependency(const String &val)
8496 { deps.push_back(val); }
8498 /**
8499 *
8500 */
8501 virtual void parseDependencies(const String &val)
8502 { deps = tokenize(val, ", "); }
8504 /**
8505 *
8506 */
8507 virtual std::vector<String> &getDependencies()
8508 { return deps; }
8510 /**
8511 *
8512 */
8513 virtual String getIf()
8514 { return ifVar; }
8516 /**
8517 *
8518 */
8519 virtual void setIf(const String &val)
8520 { ifVar = val; }
8522 /**
8523 *
8524 */
8525 virtual String getUnless()
8526 { return unlessVar; }
8528 /**
8529 *
8530 */
8531 virtual void setUnless(const String &val)
8532 { unlessVar = val; }
8534 /**
8535 *
8536 */
8537 virtual void addTask(Task *val)
8538 { tasks.push_back(val); }
8540 /**
8541 *
8542 */
8543 virtual std::vector<Task *> &getTasks()
8544 { return tasks; }
8546 private:
8548 void init()
8549 {
8550 }
8552 void cleanup()
8553 {
8554 tasks.clear();
8555 }
8557 void assign(const Target &other)
8558 {
8559 //parent = other.parent;
8560 name = other.name;
8561 description = other.description;
8562 ifVar = other.ifVar;
8563 unlessVar = other.unlessVar;
8564 deps = other.deps;
8565 tasks = other.tasks;
8566 }
8568 Make &parent;
8570 String name;
8572 String description;
8574 String ifVar;
8576 String unlessVar;
8578 std::vector<String> deps;
8580 std::vector<Task *> tasks;
8582 };
8591 //########################################################################
8592 //# M A K E
8593 //########################################################################
8596 /**
8597 *
8598 */
8599 class Make : public MakeBase
8600 {
8602 public:
8604 /**
8605 *
8606 */
8607 Make()
8608 { init(); }
8610 /**
8611 *
8612 */
8613 Make(const Make &other)
8614 { assign(other); }
8616 /**
8617 *
8618 */
8619 Make &operator=(const Make &other)
8620 { assign(other); return *this; }
8622 /**
8623 *
8624 */
8625 virtual ~Make()
8626 { cleanup(); }
8628 /**
8629 *
8630 */
8631 virtual std::map<String, Target> &getTargets()
8632 { return targets; }
8635 /**
8636 *
8637 */
8638 virtual String version()
8639 { return BUILDTOOL_VERSION; }
8641 /**
8642 * Overload a <property>
8643 */
8644 virtual bool specifyProperty(const String &name,
8645 const String &value);
8647 /**
8648 *
8649 */
8650 virtual bool run();
8652 /**
8653 *
8654 */
8655 virtual bool run(const String &target);
8659 private:
8661 /**
8662 *
8663 */
8664 void init();
8666 /**
8667 *
8668 */
8669 void cleanup();
8671 /**
8672 *
8673 */
8674 void assign(const Make &other);
8676 /**
8677 *
8678 */
8679 bool executeTask(Task &task);
8682 /**
8683 *
8684 */
8685 bool executeTarget(Target &target,
8686 std::set<String> &targetsCompleted);
8689 /**
8690 *
8691 */
8692 bool execute();
8694 /**
8695 *
8696 */
8697 bool checkTargetDependencies(Target &prop,
8698 std::vector<String> &depList);
8700 /**
8701 *
8702 */
8703 bool parsePropertyFile(const String &fileName,
8704 const String &prefix);
8706 /**
8707 *
8708 */
8709 bool parseProperty(Element *elem);
8711 /**
8712 *
8713 */
8714 bool parseFile();
8716 /**
8717 *
8718 */
8719 std::vector<String> glob(const String &pattern);
8722 //###############
8723 //# Fields
8724 //###############
8726 String projectName;
8728 String currentTarget;
8730 String defaultTarget;
8732 String specifiedTarget;
8734 String baseDir;
8736 String description;
8738 //std::vector<Property> properties;
8740 std::map<String, Target> targets;
8742 std::vector<Task *> allTasks;
8744 std::map<String, String> specifiedProperties;
8746 };
8749 //########################################################################
8750 //# C L A S S M A I N T E N A N C E
8751 //########################################################################
8753 /**
8754 *
8755 */
8756 void Make::init()
8757 {
8758 uri = "build.xml";
8759 projectName = "";
8760 currentTarget = "";
8761 defaultTarget = "";
8762 specifiedTarget = "";
8763 baseDir = "";
8764 description = "";
8765 envPrefix = "env.";
8766 pcPrefix = "pc.";
8767 pccPrefix = "pcc.";
8768 pclPrefix = "pcl.";
8769 properties.clear();
8770 for (unsigned int i = 0 ; i < allTasks.size() ; i++)
8771 delete allTasks[i];
8772 allTasks.clear();
8773 }
8777 /**
8778 *
8779 */
8780 void Make::cleanup()
8781 {
8782 for (unsigned int i = 0 ; i < allTasks.size() ; i++)
8783 delete allTasks[i];
8784 allTasks.clear();
8785 }
8789 /**
8790 *
8791 */
8792 void Make::assign(const Make &other)
8793 {
8794 uri = other.uri;
8795 projectName = other.projectName;
8796 currentTarget = other.currentTarget;
8797 defaultTarget = other.defaultTarget;
8798 specifiedTarget = other.specifiedTarget;
8799 baseDir = other.baseDir;
8800 description = other.description;
8801 properties = other.properties;
8802 }
8806 //########################################################################
8807 //# U T I L I T Y T A S K S
8808 //########################################################################
8810 /**
8811 * Perform a file globbing
8812 */
8813 std::vector<String> Make::glob(const String &pattern)
8814 {
8815 std::vector<String> res;
8816 return res;
8817 }
8820 //########################################################################
8821 //# P U B L I C A P I
8822 //########################################################################
8826 /**
8827 *
8828 */
8829 bool Make::executeTarget(Target &target,
8830 std::set<String> &targetsCompleted)
8831 {
8833 String name = target.getName();
8835 //First get any dependencies for this target
8836 std::vector<String> deps = target.getDependencies();
8837 for (unsigned int i=0 ; i<deps.size() ; i++)
8838 {
8839 String dep = deps[i];
8840 //Did we do it already? Skip
8841 if (targetsCompleted.find(dep)!=targetsCompleted.end())
8842 continue;
8844 std::map<String, Target> &tgts =
8845 target.getParent().getTargets();
8846 std::map<String, Target>::iterator iter =
8847 tgts.find(dep);
8848 if (iter == tgts.end())
8849 {
8850 error("Target '%s' dependency '%s' not found",
8851 name.c_str(), dep.c_str());
8852 return false;
8853 }
8854 Target depTarget = iter->second;
8855 if (!executeTarget(depTarget, targetsCompleted))
8856 {
8857 return false;
8858 }
8859 }
8861 status("##### Target : %s\n##### %s", name.c_str(),
8862 target.getDescription().c_str());
8864 //Now let's do the tasks
8865 std::vector<Task *> &tasks = target.getTasks();
8866 for (unsigned int i=0 ; i<tasks.size() ; i++)
8867 {
8868 Task *task = tasks[i];
8869 status("--- %s / %s", name.c_str(), task->getName().c_str());
8870 if (!task->execute())
8871 {
8872 return false;
8873 }
8874 }
8876 targetsCompleted.insert(name);
8878 return true;
8879 }
8883 /**
8884 * Main execute() method. Start here and work
8885 * up the dependency tree
8886 */
8887 bool Make::execute()
8888 {
8889 status("######## EXECUTE");
8891 //Determine initial target
8892 if (specifiedTarget.size()>0)
8893 {
8894 currentTarget = specifiedTarget;
8895 }
8896 else if (defaultTarget.size()>0)
8897 {
8898 currentTarget = defaultTarget;
8899 }
8900 else
8901 {
8902 error("execute: no specified or default target requested");
8903 return false;
8904 }
8906 std::map<String, Target>::iterator iter =
8907 targets.find(currentTarget);
8908 if (iter == targets.end())
8909 {
8910 error("Initial target '%s' not found",
8911 currentTarget.c_str());
8912 return false;
8913 }
8915 //Now run
8916 Target target = iter->second;
8917 std::set<String> targetsCompleted;
8918 if (!executeTarget(target, targetsCompleted))
8919 {
8920 return false;
8921 }
8923 status("######## EXECUTE COMPLETE");
8924 return true;
8925 }
8930 /**
8931 *
8932 */
8933 bool Make::checkTargetDependencies(Target &target,
8934 std::vector<String> &depList)
8935 {
8936 String tgtName = target.getName().c_str();
8937 depList.push_back(tgtName);
8939 std::vector<String> deps = target.getDependencies();
8940 for (unsigned int i=0 ; i<deps.size() ; i++)
8941 {
8942 String dep = deps[i];
8943 //First thing entered was the starting Target
8944 if (dep == depList[0])
8945 {
8946 error("Circular dependency '%s' found at '%s'",
8947 dep.c_str(), tgtName.c_str());
8948 std::vector<String>::iterator diter;
8949 for (diter=depList.begin() ; diter!=depList.end() ; diter++)
8950 {
8951 error(" %s", diter->c_str());
8952 }
8953 return false;
8954 }
8956 std::map<String, Target> &tgts =
8957 target.getParent().getTargets();
8958 std::map<String, Target>::iterator titer = tgts.find(dep);
8959 if (titer == tgts.end())
8960 {
8961 error("Target '%s' dependency '%s' not found",
8962 tgtName.c_str(), dep.c_str());
8963 return false;
8964 }
8965 if (!checkTargetDependencies(titer->second, depList))
8966 {
8967 return false;
8968 }
8969 }
8970 return true;
8971 }
8977 static int getword(int pos, const String &inbuf, String &result)
8978 {
8979 int p = pos;
8980 int len = (int)inbuf.size();
8981 String val;
8982 while (p < len)
8983 {
8984 char ch = inbuf[p];
8985 if (!isalnum(ch) && ch!='.' && ch!='_')
8986 break;
8987 val.push_back(ch);
8988 p++;
8989 }
8990 result = val;
8991 return p;
8992 }
8997 /**
8998 *
8999 */
9000 bool Make::parsePropertyFile(const String &fileName,
9001 const String &prefix)
9002 {
9003 FILE *f = fopen(fileName.c_str(), "r");
9004 if (!f)
9005 {
9006 error("could not open property file %s", fileName.c_str());
9007 return false;
9008 }
9009 int linenr = 0;
9010 while (!feof(f))
9011 {
9012 char buf[256];
9013 if (!fgets(buf, 255, f))
9014 break;
9015 linenr++;
9016 String s = buf;
9017 s = trim(s);
9018 int len = s.size();
9019 if (len == 0)
9020 continue;
9021 if (s[0] == '#')
9022 continue;
9023 String key;
9024 String val;
9025 int p = 0;
9026 int p2 = getword(p, s, key);
9027 if (p2 <= p)
9028 {
9029 error("property file %s, line %d: expected keyword",
9030 fileName.c_str(), linenr);
9031 return false;
9032 }
9033 if (prefix.size() > 0)
9034 {
9035 key.insert(0, prefix);
9036 }
9038 //skip whitespace
9039 for (p=p2 ; p<len ; p++)
9040 if (!isspace(s[p]))
9041 break;
9043 if (p>=len || s[p]!='=')
9044 {
9045 error("property file %s, line %d: expected '='",
9046 fileName.c_str(), linenr);
9047 return false;
9048 }
9049 p++;
9051 //skip whitespace
9052 for ( ; p<len ; p++)
9053 if (!isspace(s[p]))
9054 break;
9056 /* This way expects a word after the =
9057 p2 = getword(p, s, val);
9058 if (p2 <= p)
9059 {
9060 error("property file %s, line %d: expected value",
9061 fileName.c_str(), linenr);
9062 return false;
9063 }
9064 */
9065 // This way gets the rest of the line after the =
9066 if (p>=len)
9067 {
9068 error("property file %s, line %d: expected value",
9069 fileName.c_str(), linenr);
9070 return false;
9071 }
9072 val = s.substr(p);
9073 if (key.size()==0)
9074 continue;
9075 //allow property to be set, even if val=""
9077 //trace("key:'%s' val:'%s'", key.c_str(), val.c_str());
9078 //See if we wanted to overload this property
9079 std::map<String, String>::iterator iter =
9080 specifiedProperties.find(key);
9081 if (iter!=specifiedProperties.end())
9082 {
9083 val = iter->second;
9084 status("overloading property '%s' = '%s'",
9085 key.c_str(), val.c_str());
9086 }
9087 properties[key] = val;
9088 }
9089 fclose(f);
9090 return true;
9091 }
9096 /**
9097 *
9098 */
9099 bool Make::parseProperty(Element *elem)
9100 {
9101 std::vector<Attribute> &attrs = elem->getAttributes();
9102 for (unsigned int i=0 ; i<attrs.size() ; i++)
9103 {
9104 String attrName = attrs[i].getName();
9105 String attrVal = attrs[i].getValue();
9107 if (attrName == "name")
9108 {
9109 String val;
9110 if (!getAttribute(elem, "value", val))
9111 return false;
9112 if (val.size() > 0)
9113 {
9114 properties[attrVal] = val;
9115 }
9116 else
9117 {
9118 if (!getAttribute(elem, "location", val))
9119 return false;
9120 //let the property exist, even if not defined
9121 properties[attrVal] = val;
9122 }
9123 //See if we wanted to overload this property
9124 std::map<String, String>::iterator iter =
9125 specifiedProperties.find(attrVal);
9126 if (iter != specifiedProperties.end())
9127 {
9128 val = iter->second;
9129 status("overloading property '%s' = '%s'",
9130 attrVal.c_str(), val.c_str());
9131 properties[attrVal] = val;
9132 }
9133 }
9134 else if (attrName == "file")
9135 {
9136 String prefix;
9137 if (!getAttribute(elem, "prefix", prefix))
9138 return false;
9139 if (prefix.size() > 0)
9140 {
9141 if (prefix[prefix.size()-1] != '.')
9142 prefix.push_back('.');
9143 }
9144 if (!parsePropertyFile(attrName, prefix))
9145 return false;
9146 }
9147 else if (attrName == "environment")
9148 {
9149 if (attrVal.find('.') != attrVal.npos)
9150 {
9151 error("environment prefix cannot have a '.' in it");
9152 return false;
9153 }
9154 envPrefix = attrVal;
9155 envPrefix.push_back('.');
9156 }
9157 else if (attrName == "pkg-config")
9158 {
9159 if (attrVal.find('.') != attrVal.npos)
9160 {
9161 error("pkg-config prefix cannot have a '.' in it");
9162 return false;
9163 }
9164 pcPrefix = attrVal;
9165 pcPrefix.push_back('.');
9166 }
9167 else if (attrName == "pkg-config-cflags")
9168 {
9169 if (attrVal.find('.') != attrVal.npos)
9170 {
9171 error("pkg-config-cflags prefix cannot have a '.' in it");
9172 return false;
9173 }
9174 pccPrefix = attrVal;
9175 pccPrefix.push_back('.');
9176 }
9177 else if (attrName == "pkg-config-libs")
9178 {
9179 if (attrVal.find('.') != attrVal.npos)
9180 {
9181 error("pkg-config-libs prefix cannot have a '.' in it");
9182 return false;
9183 }
9184 pclPrefix = attrVal;
9185 pclPrefix.push_back('.');
9186 }
9187 }
9189 return true;
9190 }
9195 /**
9196 *
9197 */
9198 bool Make::parseFile()
9199 {
9200 status("######## PARSE : %s", uri.getPath().c_str());
9202 setLine(0);
9204 Parser parser;
9205 Element *root = parser.parseFile(uri.getNativePath());
9206 if (!root)
9207 {
9208 error("Could not open %s for reading",
9209 uri.getNativePath().c_str());
9210 return false;
9211 }
9213 setLine(root->getLine());
9215 if (root->getChildren().size()==0 ||
9216 root->getChildren()[0]->getName()!="project")
9217 {
9218 error("Main xml element should be <project>");
9219 delete root;
9220 return false;
9221 }
9223 //########## Project attributes
9224 Element *project = root->getChildren()[0];
9225 String s = project->getAttribute("name");
9226 if (s.size() > 0)
9227 projectName = s;
9228 s = project->getAttribute("default");
9229 if (s.size() > 0)
9230 defaultTarget = s;
9231 s = project->getAttribute("basedir");
9232 if (s.size() > 0)
9233 baseDir = s;
9235 //######### PARSE MEMBERS
9236 std::vector<Element *> children = project->getChildren();
9237 for (unsigned int i=0 ; i<children.size() ; i++)
9238 {
9239 Element *elem = children[i];
9240 setLine(elem->getLine());
9241 String tagName = elem->getName();
9243 //########## DESCRIPTION
9244 if (tagName == "description")
9245 {
9246 description = parser.trim(elem->getValue());
9247 }
9249 //######### PROPERTY
9250 else if (tagName == "property")
9251 {
9252 if (!parseProperty(elem))
9253 return false;
9254 }
9256 //######### TARGET
9257 else if (tagName == "target")
9258 {
9259 String tname = elem->getAttribute("name");
9260 String tdesc = elem->getAttribute("description");
9261 String tdeps = elem->getAttribute("depends");
9262 String tif = elem->getAttribute("if");
9263 String tunless = elem->getAttribute("unless");
9264 Target target(*this);
9265 target.setName(tname);
9266 target.setDescription(tdesc);
9267 target.parseDependencies(tdeps);
9268 target.setIf(tif);
9269 target.setUnless(tunless);
9270 std::vector<Element *> telems = elem->getChildren();
9271 for (unsigned int i=0 ; i<telems.size() ; i++)
9272 {
9273 Element *telem = telems[i];
9274 Task breeder(*this);
9275 Task *task = breeder.createTask(telem, telem->getLine());
9276 if (!task)
9277 return false;
9278 allTasks.push_back(task);
9279 target.addTask(task);
9280 }
9282 //Check name
9283 if (tname.size() == 0)
9284 {
9285 error("no name for target");
9286 return false;
9287 }
9288 //Check for duplicate name
9289 if (targets.find(tname) != targets.end())
9290 {
9291 error("target '%s' already defined", tname.c_str());
9292 return false;
9293 }
9294 //more work than targets[tname]=target, but avoids default allocator
9295 targets.insert(std::make_pair<String, Target>(tname, target));
9296 }
9297 //######### none of the above
9298 else
9299 {
9300 error("unknown toplevel tag: <%s>", tagName.c_str());
9301 return false;
9302 }
9304 }
9306 std::map<String, Target>::iterator iter;
9307 for (iter = targets.begin() ; iter!= targets.end() ; iter++)
9308 {
9309 Target tgt = iter->second;
9310 std::vector<String> depList;
9311 if (!checkTargetDependencies(tgt, depList))
9312 {
9313 return false;
9314 }
9315 }
9318 delete root;
9319 status("######## PARSE COMPLETE");
9320 return true;
9321 }
9324 /**
9325 * Overload a <property>
9326 */
9327 bool Make::specifyProperty(const String &name, const String &value)
9328 {
9329 if (specifiedProperties.find(name) != specifiedProperties.end())
9330 {
9331 error("Property %s already specified", name.c_str());
9332 return false;
9333 }
9334 specifiedProperties[name] = value;
9335 return true;
9336 }
9340 /**
9341 *
9342 */
9343 bool Make::run()
9344 {
9345 if (!parseFile())
9346 return false;
9348 if (!execute())
9349 return false;
9351 return true;
9352 }
9357 /**
9358 * Get a formatted MM:SS.sss time elapsed string
9359 */
9360 static String
9361 timeDiffString(struct timeval &x, struct timeval &y)
9362 {
9363 long microsX = x.tv_usec;
9364 long secondsX = x.tv_sec;
9365 long microsY = y.tv_usec;
9366 long secondsY = y.tv_sec;
9367 if (microsX < microsY)
9368 {
9369 microsX += 1000000;
9370 secondsX -= 1;
9371 }
9373 int seconds = (int)(secondsX - secondsY);
9374 int millis = (int)((microsX - microsY)/1000);
9376 int minutes = seconds/60;
9377 seconds -= minutes*60;
9378 char buf[80];
9379 snprintf(buf, 79, "%dm %d.%03ds", minutes, seconds, millis);
9380 String ret = buf;
9381 return ret;
9383 }
9385 /**
9386 *
9387 */
9388 bool Make::run(const String &target)
9389 {
9390 status("####################################################");
9391 status("# %s", version().c_str());
9392 status("####################################################");
9393 struct timeval timeStart, timeEnd;
9394 ::gettimeofday(&timeStart, NULL);
9395 specifiedTarget = target;
9396 if (!run())
9397 return false;
9398 ::gettimeofday(&timeEnd, NULL);
9399 String timeStr = timeDiffString(timeEnd, timeStart);
9400 status("####################################################");
9401 status("# BuildTool Completed : %s", timeStr.c_str());
9402 status("####################################################");
9403 return true;
9404 }
9412 }// namespace buildtool
9413 //########################################################################
9414 //# M A I N
9415 //########################################################################
9417 typedef buildtool::String String;
9419 /**
9420 * Format an error message in printf() style
9421 */
9422 static void error(const char *fmt, ...)
9423 {
9424 va_list ap;
9425 va_start(ap, fmt);
9426 fprintf(stderr, "BuildTool error: ");
9427 vfprintf(stderr, fmt, ap);
9428 fprintf(stderr, "\n");
9429 va_end(ap);
9430 }
9433 static bool parseProperty(const String &s, String &name, String &val)
9434 {
9435 int len = s.size();
9436 int i;
9437 for (i=0 ; i<len ; i++)
9438 {
9439 char ch = s[i];
9440 if (ch == '=')
9441 break;
9442 name.push_back(ch);
9443 }
9444 if (i>=len || s[i]!='=')
9445 {
9446 error("property requires -Dname=value");
9447 return false;
9448 }
9449 i++;
9450 for ( ; i<len ; i++)
9451 {
9452 char ch = s[i];
9453 val.push_back(ch);
9454 }
9455 return true;
9456 }
9459 /**
9460 * Compare a buffer with a key, for the length of the key
9461 */
9462 static bool sequ(const String &buf, const char *key)
9463 {
9464 int len = buf.size();
9465 for (int i=0 ; key[i] && i<len ; i++)
9466 {
9467 if (key[i] != buf[i])
9468 return false;
9469 }
9470 return true;
9471 }
9473 static void usage(int argc, char **argv)
9474 {
9475 printf("usage:\n");
9476 printf(" %s [options] [target]\n", argv[0]);
9477 printf("Options:\n");
9478 printf(" -help, -h print this message\n");
9479 printf(" -version print the version information and exit\n");
9480 printf(" -file <file> use given buildfile\n");
9481 printf(" -f <file> ''\n");
9482 printf(" -D<property>=<value> use value for given property\n");
9483 }
9488 /**
9489 * Parse the command-line args, get our options,
9490 * and run this thing
9491 */
9492 static bool parseOptions(int argc, char **argv)
9493 {
9494 if (argc < 1)
9495 {
9496 error("Cannot parse arguments");
9497 return false;
9498 }
9500 buildtool::Make make;
9502 String target;
9504 //char *progName = argv[0];
9505 for (int i=1 ; i<argc ; i++)
9506 {
9507 String arg = argv[i];
9508 if (arg.size()>1 && arg[0]=='-')
9509 {
9510 if (arg == "-h" || arg == "-help")
9511 {
9512 usage(argc,argv);
9513 return true;
9514 }
9515 else if (arg == "-version")
9516 {
9517 printf("%s", make.version().c_str());
9518 return true;
9519 }
9520 else if (arg == "-f" || arg == "-file")
9521 {
9522 if (i>=argc)
9523 {
9524 usage(argc, argv);
9525 return false;
9526 }
9527 i++; //eat option
9528 make.setURI(argv[i]);
9529 }
9530 else if (arg.size()>2 && sequ(arg, "-D"))
9531 {
9532 String s = arg.substr(2, arg.size());
9533 String name, value;
9534 if (!parseProperty(s, name, value))
9535 {
9536 usage(argc, argv);
9537 return false;
9538 }
9539 if (!make.specifyProperty(name, value))
9540 return false;
9541 }
9542 else
9543 {
9544 error("Unknown option:%s", arg.c_str());
9545 return false;
9546 }
9547 }
9548 else
9549 {
9550 if (target.size()>0)
9551 {
9552 error("only one initial target");
9553 usage(argc, argv);
9554 return false;
9555 }
9556 target = arg;
9557 }
9558 }
9560 //We have the options. Now execute them
9561 if (!make.run(target))
9562 return false;
9564 return true;
9565 }
9570 /*
9571 static bool runMake()
9572 {
9573 buildtool::Make make;
9574 if (!make.run())
9575 return false;
9576 return true;
9577 }
9580 static bool pkgConfigTest()
9581 {
9582 buildtool::PkgConfig pkgConfig;
9583 if (!pkgConfig.readFile("gtk+-2.0.pc"))
9584 return false;
9585 return true;
9586 }
9590 static bool depTest()
9591 {
9592 buildtool::DepTool deptool;
9593 deptool.setSourceDirectory("/dev/ink/inkscape/src");
9594 if (!deptool.generateDependencies("build.dep"))
9595 return false;
9596 std::vector<buildtool::FileRec> res =
9597 deptool.loadDepFile("build.dep");
9598 if (res.size() == 0)
9599 return false;
9600 return true;
9601 }
9603 static bool popenTest()
9604 {
9605 buildtool::Make make;
9606 buildtool::String out, err;
9607 bool ret = make.executeCommand("gcc xx.cpp", "", out, err);
9608 printf("Popen test:%d '%s' '%s'\n", ret, out.c_str(), err.c_str());
9609 return true;
9610 }
9613 static bool propFileTest()
9614 {
9615 buildtool::Make make;
9616 make.parsePropertyFile("test.prop", "test.");
9617 return true;
9618 }
9619 */
9621 int main(int argc, char **argv)
9622 {
9624 if (!parseOptions(argc, argv))
9625 return 1;
9626 /*
9627 if (!popenTest())
9628 return 1;
9630 if (!depTest())
9631 return 1;
9632 if (!propFileTest())
9633 return 1;
9634 if (runMake())
9635 return 1;
9636 */
9637 return 0;
9638 }
9641 //########################################################################
9642 //# E N D
9643 //########################################################################