1 /**
2 * Simple build automation tool.
3 *
4 * Authors:
5 * Bob Jamison
6 * Jasper van de Gronde
7 *
8 * Copyright (C) 2006-2008 Bob Jamison
9 *
10 * This library is free software; you can redistribute it and/or
11 * modify it under the terms of the GNU Lesser General Public
12 * License as published by the Free Software Foundation; either
13 * version 2.1 of the License, or (at your option) any later version.
14 *
15 * This library is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 * Lesser General Public License for more details.
19 *
20 * You should have received a copy of the GNU Lesser General Public
21 * License along with this library; if not, write to the Free Software
22 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
23 */
25 /**
26 * To use this file, compile with:
27 * <pre>
28 * g++ -O3 buildtool.cpp -o btool.exe
29 * (or whatever your compiler might be)
30 * Then
31 * btool
32 * or
33 * btool {target}
34 *
35 * Note: if you are using MinGW, and a not very recent version of it,
36 * gettimeofday() might be missing. If so, just build this file with
37 * this command:
38 * g++ -O3 -DNEED_GETTIMEOFDAY buildtool.cpp -o btool.exe
39 *
40 */
42 #define BUILDTOOL_VERSION "BuildTool v0.9.6"
44 #include <stdio.h>
45 #include <fcntl.h>
46 #include <unistd.h>
47 #include <stdarg.h>
48 #include <sys/stat.h>
49 #include <time.h>
50 #include <sys/time.h>
51 #include <utime.h>
52 #include <dirent.h>
54 #include <string>
55 #include <map>
56 #include <set>
57 #include <vector>
58 #include <algorithm>
61 #ifdef __WIN32__
62 #include <windows.h>
63 #endif
66 #include <errno.h>
69 //########################################################################
70 //# Definition of gettimeofday() for those who don't have it
71 //########################################################################
72 #ifdef NEED_GETTIMEOFDAY
73 #include <sys/timeb.h>
75 struct timezone {
76 int tz_minuteswest; /* minutes west of Greenwich */
77 int tz_dsttime; /* type of dst correction */
78 };
80 static int gettimeofday (struct timeval *tv, struct timezone *tz)
81 {
82 struct _timeb tb;
84 if (!tv)
85 return (-1);
87 _ftime (&tb);
88 tv->tv_sec = tb.time;
89 tv->tv_usec = tb.millitm * 1000 + 500;
90 if (tz)
91 {
92 tz->tz_minuteswest = -60 * _timezone;
93 tz->tz_dsttime = _daylight;
94 }
95 return 0;
96 }
98 #endif
106 namespace buildtool
107 {
112 //########################################################################
113 //########################################################################
114 //## R E G E X P
115 //########################################################################
116 //########################################################################
118 /**
119 * This is the T-Rex regular expression library, which we
120 * gratefully acknowledge. It's clean code and small size allow
121 * us to embed it in BuildTool without adding a dependency
122 *
123 */
125 //begin trex.h
127 #ifndef _TREX_H_
128 #define _TREX_H_
129 /***************************************************************
130 T-Rex a tiny regular expression library
132 Copyright (C) 2003-2006 Alberto Demichelis
134 This software is provided 'as-is', without any express
135 or implied warranty. In no event will the authors be held
136 liable for any damages arising from the use of this software.
138 Permission is granted to anyone to use this software for
139 any purpose, including commercial applications, and to alter
140 it and redistribute it freely, subject to the following restrictions:
142 1. The origin of this software must not be misrepresented;
143 you must not claim that you wrote the original software.
144 If you use this software in a product, an acknowledgment
145 in the product documentation would be appreciated but
146 is not required.
148 2. Altered source versions must be plainly marked as such,
149 and must not be misrepresented as being the original software.
151 3. This notice may not be removed or altered from any
152 source distribution.
154 ****************************************************************/
156 #ifdef _UNICODE
157 #define TRexChar unsigned short
158 #define MAX_CHAR 0xFFFF
159 #define _TREXC(c) L##c
160 #define trex_strlen wcslen
161 #define trex_printf wprintf
162 #else
163 #define TRexChar char
164 #define MAX_CHAR 0xFF
165 #define _TREXC(c) (c)
166 #define trex_strlen strlen
167 #define trex_printf printf
168 #endif
170 #ifndef TREX_API
171 #define TREX_API extern
172 #endif
174 #define TRex_True 1
175 #define TRex_False 0
177 typedef unsigned int TRexBool;
178 typedef struct TRex TRex;
180 typedef struct {
181 const TRexChar *begin;
182 int len;
183 } TRexMatch;
185 TREX_API TRex *trex_compile(const TRexChar *pattern,const TRexChar **error);
186 TREX_API void trex_free(TRex *exp);
187 TREX_API TRexBool trex_match(TRex* exp,const TRexChar* text);
188 TREX_API TRexBool trex_search(TRex* exp,const TRexChar* text, const TRexChar** out_begin, const TRexChar** out_end);
189 TREX_API TRexBool trex_searchrange(TRex* exp,const TRexChar* text_begin,const TRexChar* text_end,const TRexChar** out_begin, const TRexChar** out_end);
190 TREX_API int trex_getsubexpcount(TRex* exp);
191 TREX_API TRexBool trex_getsubexp(TRex* exp, int n, TRexMatch *subexp);
193 #endif
195 //end trex.h
197 //start trex.c
200 #include <stdio.h>
201 #include <string>
203 /* see copyright notice in trex.h */
204 #include <string.h>
205 #include <stdlib.h>
206 #include <ctype.h>
207 #include <setjmp.h>
208 //#include "trex.h"
210 #ifdef _UNICODE
211 #define scisprint iswprint
212 #define scstrlen wcslen
213 #define scprintf wprintf
214 #define _SC(x) L(x)
215 #else
216 #define scisprint isprint
217 #define scstrlen strlen
218 #define scprintf printf
219 #define _SC(x) (x)
220 #endif
222 #ifdef _DEBUG
223 #include <stdio.h>
225 static const TRexChar *g_nnames[] =
226 {
227 _SC("NONE"),_SC("OP_GREEDY"), _SC("OP_OR"),
228 _SC("OP_EXPR"),_SC("OP_NOCAPEXPR"),_SC("OP_DOT"), _SC("OP_CLASS"),
229 _SC("OP_CCLASS"),_SC("OP_NCLASS"),_SC("OP_RANGE"),_SC("OP_CHAR"),
230 _SC("OP_EOL"),_SC("OP_BOL"),_SC("OP_WB")
231 };
233 #endif
234 #define OP_GREEDY (MAX_CHAR+1) // * + ? {n}
235 #define OP_OR (MAX_CHAR+2)
236 #define OP_EXPR (MAX_CHAR+3) //parentesis ()
237 #define OP_NOCAPEXPR (MAX_CHAR+4) //parentesis (?:)
238 #define OP_DOT (MAX_CHAR+5)
239 #define OP_CLASS (MAX_CHAR+6)
240 #define OP_CCLASS (MAX_CHAR+7)
241 #define OP_NCLASS (MAX_CHAR+8) //negates class the [^
242 #define OP_RANGE (MAX_CHAR+9)
243 #define OP_CHAR (MAX_CHAR+10)
244 #define OP_EOL (MAX_CHAR+11)
245 #define OP_BOL (MAX_CHAR+12)
246 #define OP_WB (MAX_CHAR+13)
248 #define TREX_SYMBOL_ANY_CHAR ('.')
249 #define TREX_SYMBOL_GREEDY_ONE_OR_MORE ('+')
250 #define TREX_SYMBOL_GREEDY_ZERO_OR_MORE ('*')
251 #define TREX_SYMBOL_GREEDY_ZERO_OR_ONE ('?')
252 #define TREX_SYMBOL_BRANCH ('|')
253 #define TREX_SYMBOL_END_OF_STRING ('$')
254 #define TREX_SYMBOL_BEGINNING_OF_STRING ('^')
255 #define TREX_SYMBOL_ESCAPE_CHAR ('\\')
258 typedef int TRexNodeType;
260 typedef struct tagTRexNode{
261 TRexNodeType type;
262 int left;
263 int right;
264 int next;
265 }TRexNode;
267 struct TRex{
268 const TRexChar *_eol;
269 const TRexChar *_bol;
270 const TRexChar *_p;
271 int _first;
272 int _op;
273 TRexNode *_nodes;
274 int _nallocated;
275 int _nsize;
276 int _nsubexpr;
277 TRexMatch *_matches;
278 int _currsubexp;
279 void *_jmpbuf;
280 const TRexChar **_error;
281 };
283 static int trex_list(TRex *exp);
285 static int trex_newnode(TRex *exp, TRexNodeType type)
286 {
287 TRexNode n;
288 int newid;
289 n.type = type;
290 n.next = n.right = n.left = -1;
291 if(type == OP_EXPR)
292 n.right = exp->_nsubexpr++;
293 if(exp->_nallocated < (exp->_nsize + 1)) {
294 //int oldsize = exp->_nallocated;
295 exp->_nallocated *= 2;
296 exp->_nodes = (TRexNode *)realloc(exp->_nodes, exp->_nallocated * sizeof(TRexNode));
297 }
298 exp->_nodes[exp->_nsize++] = n;
299 newid = exp->_nsize - 1;
300 return (int)newid;
301 }
303 static void trex_error(TRex *exp,const TRexChar *error)
304 {
305 if(exp->_error) *exp->_error = error;
306 longjmp(*((jmp_buf*)exp->_jmpbuf),-1);
307 }
309 static void trex_expect(TRex *exp, int n){
310 if((*exp->_p) != n)
311 trex_error(exp, _SC("expected paren"));
312 exp->_p++;
313 }
315 static TRexChar trex_escapechar(TRex *exp)
316 {
317 if(*exp->_p == TREX_SYMBOL_ESCAPE_CHAR){
318 exp->_p++;
319 switch(*exp->_p) {
320 case 'v': exp->_p++; return '\v';
321 case 'n': exp->_p++; return '\n';
322 case 't': exp->_p++; return '\t';
323 case 'r': exp->_p++; return '\r';
324 case 'f': exp->_p++; return '\f';
325 default: return (*exp->_p++);
326 }
327 } else if(!scisprint(*exp->_p)) trex_error(exp,_SC("letter expected"));
328 return (*exp->_p++);
329 }
331 static int trex_charclass(TRex *exp,int classid)
332 {
333 int n = trex_newnode(exp,OP_CCLASS);
334 exp->_nodes[n].left = classid;
335 return n;
336 }
338 static int trex_charnode(TRex *exp,TRexBool isclass)
339 {
340 TRexChar t;
341 if(*exp->_p == TREX_SYMBOL_ESCAPE_CHAR) {
342 exp->_p++;
343 switch(*exp->_p) {
344 case 'n': exp->_p++; return trex_newnode(exp,'\n');
345 case 't': exp->_p++; return trex_newnode(exp,'\t');
346 case 'r': exp->_p++; return trex_newnode(exp,'\r');
347 case 'f': exp->_p++; return trex_newnode(exp,'\f');
348 case 'v': exp->_p++; return trex_newnode(exp,'\v');
349 case 'a': case 'A': case 'w': case 'W': case 's': case 'S':
350 case 'd': case 'D': case 'x': case 'X': case 'c': case 'C':
351 case 'p': case 'P': case 'l': case 'u':
352 {
353 t = *exp->_p; exp->_p++;
354 return trex_charclass(exp,t);
355 }
356 case 'b':
357 case 'B':
358 if(!isclass) {
359 int node = trex_newnode(exp,OP_WB);
360 exp->_nodes[node].left = *exp->_p;
361 exp->_p++;
362 return node;
363 } //else default
364 default:
365 t = *exp->_p; exp->_p++;
366 return trex_newnode(exp,t);
367 }
368 }
369 else if(!scisprint(*exp->_p)) {
371 trex_error(exp,_SC("letter expected"));
372 }
373 t = *exp->_p; exp->_p++;
374 return trex_newnode(exp,t);
375 }
376 static int trex_class(TRex *exp)
377 {
378 int ret = -1;
379 int first = -1,chain;
380 if(*exp->_p == TREX_SYMBOL_BEGINNING_OF_STRING){
381 ret = trex_newnode(exp,OP_NCLASS);
382 exp->_p++;
383 }else ret = trex_newnode(exp,OP_CLASS);
385 if(*exp->_p == ']') trex_error(exp,_SC("empty class"));
386 chain = ret;
387 while(*exp->_p != ']' && exp->_p != exp->_eol) {
388 if(*exp->_p == '-' && first != -1){
389 int r,t;
390 if(*exp->_p++ == ']') trex_error(exp,_SC("unfinished range"));
391 r = trex_newnode(exp,OP_RANGE);
392 if(first>*exp->_p) trex_error(exp,_SC("invalid range"));
393 if(exp->_nodes[first].type == OP_CCLASS) trex_error(exp,_SC("cannot use character classes in ranges"));
394 exp->_nodes[r].left = exp->_nodes[first].type;
395 t = trex_escapechar(exp);
396 exp->_nodes[r].right = t;
397 exp->_nodes[chain].next = r;
398 chain = r;
399 first = -1;
400 }
401 else{
402 if(first!=-1){
403 int c = first;
404 exp->_nodes[chain].next = c;
405 chain = c;
406 first = trex_charnode(exp,TRex_True);
407 }
408 else{
409 first = trex_charnode(exp,TRex_True);
410 }
411 }
412 }
413 if(first!=-1){
414 int c = first;
415 exp->_nodes[chain].next = c;
416 chain = c;
417 first = -1;
418 }
419 /* hack? */
420 exp->_nodes[ret].left = exp->_nodes[ret].next;
421 exp->_nodes[ret].next = -1;
422 return ret;
423 }
425 static int trex_parsenumber(TRex *exp)
426 {
427 int ret = *exp->_p-'0';
428 int positions = 10;
429 exp->_p++;
430 while(isdigit(*exp->_p)) {
431 ret = ret*10+(*exp->_p++-'0');
432 if(positions==1000000000) trex_error(exp,_SC("overflow in numeric constant"));
433 positions *= 10;
434 };
435 return ret;
436 }
438 static int trex_element(TRex *exp)
439 {
440 int ret = -1;
441 switch(*exp->_p)
442 {
443 case '(': {
444 int expr,newn;
445 exp->_p++;
448 if(*exp->_p =='?') {
449 exp->_p++;
450 trex_expect(exp,':');
451 expr = trex_newnode(exp,OP_NOCAPEXPR);
452 }
453 else
454 expr = trex_newnode(exp,OP_EXPR);
455 newn = trex_list(exp);
456 exp->_nodes[expr].left = newn;
457 ret = expr;
458 trex_expect(exp,')');
459 }
460 break;
461 case '[':
462 exp->_p++;
463 ret = trex_class(exp);
464 trex_expect(exp,']');
465 break;
466 case TREX_SYMBOL_END_OF_STRING: exp->_p++; ret = trex_newnode(exp,OP_EOL);break;
467 case TREX_SYMBOL_ANY_CHAR: exp->_p++; ret = trex_newnode(exp,OP_DOT);break;
468 default:
469 ret = trex_charnode(exp,TRex_False);
470 break;
471 }
473 {
474 int op;
475 TRexBool isgreedy = TRex_False;
476 unsigned short p0 = 0, p1 = 0;
477 switch(*exp->_p){
478 case TREX_SYMBOL_GREEDY_ZERO_OR_MORE: p0 = 0; p1 = 0xFFFF; exp->_p++; isgreedy = TRex_True; break;
479 case TREX_SYMBOL_GREEDY_ONE_OR_MORE: p0 = 1; p1 = 0xFFFF; exp->_p++; isgreedy = TRex_True; break;
480 case TREX_SYMBOL_GREEDY_ZERO_OR_ONE: p0 = 0; p1 = 1; exp->_p++; isgreedy = TRex_True; break;
481 case '{':
482 exp->_p++;
483 if(!isdigit(*exp->_p)) trex_error(exp,_SC("number expected"));
484 p0 = (unsigned short)trex_parsenumber(exp);
485 /*******************************/
486 switch(*exp->_p) {
487 case '}':
488 p1 = p0; exp->_p++;
489 break;
490 case ',':
491 exp->_p++;
492 p1 = 0xFFFF;
493 if(isdigit(*exp->_p)){
494 p1 = (unsigned short)trex_parsenumber(exp);
495 }
496 trex_expect(exp,'}');
497 break;
498 default:
499 trex_error(exp,_SC(", or } expected"));
500 }
501 /*******************************/
502 isgreedy = TRex_True;
503 break;
505 }
506 if(isgreedy) {
507 int nnode = trex_newnode(exp,OP_GREEDY);
508 op = OP_GREEDY;
509 exp->_nodes[nnode].left = ret;
510 exp->_nodes[nnode].right = ((p0)<<16)|p1;
511 ret = nnode;
512 }
513 }
514 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')) {
515 int nnode = trex_element(exp);
516 exp->_nodes[ret].next = nnode;
517 }
519 return ret;
520 }
522 static int trex_list(TRex *exp)
523 {
524 int ret=-1,e;
525 if(*exp->_p == TREX_SYMBOL_BEGINNING_OF_STRING) {
526 exp->_p++;
527 ret = trex_newnode(exp,OP_BOL);
528 }
529 e = trex_element(exp);
530 if(ret != -1) {
531 exp->_nodes[ret].next = e;
532 }
533 else ret = e;
535 if(*exp->_p == TREX_SYMBOL_BRANCH) {
536 int temp,tright;
537 exp->_p++;
538 temp = trex_newnode(exp,OP_OR);
539 exp->_nodes[temp].left = ret;
540 tright = trex_list(exp);
541 exp->_nodes[temp].right = tright;
542 ret = temp;
543 }
544 return ret;
545 }
547 static TRexBool trex_matchcclass(int cclass,TRexChar c)
548 {
549 switch(cclass) {
550 case 'a': return isalpha(c)?TRex_True:TRex_False;
551 case 'A': return !isalpha(c)?TRex_True:TRex_False;
552 case 'w': return (isalnum(c) || c == '_')?TRex_True:TRex_False;
553 case 'W': return (!isalnum(c) && c != '_')?TRex_True:TRex_False;
554 case 's': return isspace(c)?TRex_True:TRex_False;
555 case 'S': return !isspace(c)?TRex_True:TRex_False;
556 case 'd': return isdigit(c)?TRex_True:TRex_False;
557 case 'D': return !isdigit(c)?TRex_True:TRex_False;
558 case 'x': return isxdigit(c)?TRex_True:TRex_False;
559 case 'X': return !isxdigit(c)?TRex_True:TRex_False;
560 case 'c': return iscntrl(c)?TRex_True:TRex_False;
561 case 'C': return !iscntrl(c)?TRex_True:TRex_False;
562 case 'p': return ispunct(c)?TRex_True:TRex_False;
563 case 'P': return !ispunct(c)?TRex_True:TRex_False;
564 case 'l': return islower(c)?TRex_True:TRex_False;
565 case 'u': return isupper(c)?TRex_True:TRex_False;
566 }
567 return TRex_False; /*cannot happen*/
568 }
570 static TRexBool trex_matchclass(TRex* exp,TRexNode *node,TRexChar c)
571 {
572 do {
573 switch(node->type) {
574 case OP_RANGE:
575 if(c >= node->left && c <= node->right) return TRex_True;
576 break;
577 case OP_CCLASS:
578 if(trex_matchcclass(node->left,c)) return TRex_True;
579 break;
580 default:
581 if(c == node->type)return TRex_True;
582 }
583 } while((node->next != -1) && (node = &exp->_nodes[node->next]));
584 return TRex_False;
585 }
587 static const TRexChar *trex_matchnode(TRex* exp,TRexNode *node,const TRexChar *str,TRexNode *next)
588 {
590 TRexNodeType type = node->type;
591 switch(type) {
592 case OP_GREEDY: {
593 //TRexNode *greedystop = (node->next != -1) ? &exp->_nodes[node->next] : NULL;
594 TRexNode *greedystop = NULL;
595 int p0 = (node->right >> 16)&0x0000FFFF, p1 = node->right&0x0000FFFF, nmaches = 0;
596 const TRexChar *s=str, *good = str;
598 if(node->next != -1) {
599 greedystop = &exp->_nodes[node->next];
600 }
601 else {
602 greedystop = next;
603 }
605 while((nmaches == 0xFFFF || nmaches < p1)) {
607 const TRexChar *stop;
608 if(!(s = trex_matchnode(exp,&exp->_nodes[node->left],s,greedystop)))
609 break;
610 nmaches++;
611 good=s;
612 if(greedystop) {
613 //checks that 0 matches satisfy the expression(if so skips)
614 //if not would always stop(for instance if is a '?')
615 if(greedystop->type != OP_GREEDY ||
616 (greedystop->type == OP_GREEDY && ((greedystop->right >> 16)&0x0000FFFF) != 0))
617 {
618 TRexNode *gnext = NULL;
619 if(greedystop->next != -1) {
620 gnext = &exp->_nodes[greedystop->next];
621 }else if(next && next->next != -1){
622 gnext = &exp->_nodes[next->next];
623 }
624 stop = trex_matchnode(exp,greedystop,s,gnext);
625 if(stop) {
626 //if satisfied stop it
627 if(p0 == p1 && p0 == nmaches) break;
628 else if(nmaches >= p0 && p1 == 0xFFFF) break;
629 else if(nmaches >= p0 && nmaches <= p1) break;
630 }
631 }
632 }
634 if(s >= exp->_eol)
635 break;
636 }
637 if(p0 == p1 && p0 == nmaches) return good;
638 else if(nmaches >= p0 && p1 == 0xFFFF) return good;
639 else if(nmaches >= p0 && nmaches <= p1) return good;
640 return NULL;
641 }
642 case OP_OR: {
643 const TRexChar *asd = str;
644 TRexNode *temp=&exp->_nodes[node->left];
645 while( (asd = trex_matchnode(exp,temp,asd,NULL)) ) {
646 if(temp->next != -1)
647 temp = &exp->_nodes[temp->next];
648 else
649 return asd;
650 }
651 asd = str;
652 temp = &exp->_nodes[node->right];
653 while( (asd = trex_matchnode(exp,temp,asd,NULL)) ) {
654 if(temp->next != -1)
655 temp = &exp->_nodes[temp->next];
656 else
657 return asd;
658 }
659 return NULL;
660 break;
661 }
662 case OP_EXPR:
663 case OP_NOCAPEXPR:{
664 TRexNode *n = &exp->_nodes[node->left];
665 const TRexChar *cur = str;
666 int capture = -1;
667 if(node->type != OP_NOCAPEXPR && node->right == exp->_currsubexp) {
668 capture = exp->_currsubexp;
669 exp->_matches[capture].begin = cur;
670 exp->_currsubexp++;
671 }
673 do {
674 TRexNode *subnext = NULL;
675 if(n->next != -1) {
676 subnext = &exp->_nodes[n->next];
677 }else {
678 subnext = next;
679 }
680 if(!(cur = trex_matchnode(exp,n,cur,subnext))) {
681 if(capture != -1){
682 exp->_matches[capture].begin = 0;
683 exp->_matches[capture].len = 0;
684 }
685 return NULL;
686 }
687 } while((n->next != -1) && (n = &exp->_nodes[n->next]));
689 if(capture != -1)
690 exp->_matches[capture].len = cur - exp->_matches[capture].begin;
691 return cur;
692 }
693 case OP_WB:
694 if((str == exp->_bol && !isspace(*str))
695 || (str == exp->_eol && !isspace(*(str-1)))
696 || (!isspace(*str) && isspace(*(str+1)))
697 || (isspace(*str) && !isspace(*(str+1))) ) {
698 return (node->left == 'b')?str:NULL;
699 }
700 return (node->left == 'b')?NULL:str;
701 case OP_BOL:
702 if(str == exp->_bol) return str;
703 return NULL;
704 case OP_EOL:
705 if(str == exp->_eol) return str;
706 return NULL;
707 case OP_DOT:{
708 *str++;
709 }
710 return str;
711 case OP_NCLASS:
712 case OP_CLASS:
713 if(trex_matchclass(exp,&exp->_nodes[node->left],*str)?(type == OP_CLASS?TRex_True:TRex_False):(type == OP_NCLASS?TRex_True:TRex_False)) {
714 *str++;
715 return str;
716 }
717 return NULL;
718 case OP_CCLASS:
719 if(trex_matchcclass(node->left,*str)) {
720 *str++;
721 return str;
722 }
723 return NULL;
724 default: /* char */
725 if(*str != node->type) return NULL;
726 *str++;
727 return str;
728 }
729 return NULL;
730 }
732 /* public api */
733 TRex *trex_compile(const TRexChar *pattern,const TRexChar **error)
734 {
735 TRex *exp = (TRex *)malloc(sizeof(TRex));
736 exp->_eol = exp->_bol = NULL;
737 exp->_p = pattern;
738 exp->_nallocated = (int)scstrlen(pattern) * sizeof(TRexChar);
739 exp->_nodes = (TRexNode *)malloc(exp->_nallocated * sizeof(TRexNode));
740 exp->_nsize = 0;
741 exp->_matches = 0;
742 exp->_nsubexpr = 0;
743 exp->_first = trex_newnode(exp,OP_EXPR);
744 exp->_error = error;
745 exp->_jmpbuf = malloc(sizeof(jmp_buf));
746 if(setjmp(*((jmp_buf*)exp->_jmpbuf)) == 0) {
747 int res = trex_list(exp);
748 exp->_nodes[exp->_first].left = res;
749 if(*exp->_p!='\0')
750 trex_error(exp,_SC("unexpected character"));
751 #ifdef _DEBUG
752 {
753 int nsize,i;
754 TRexNode *t;
755 nsize = exp->_nsize;
756 t = &exp->_nodes[0];
757 scprintf(_SC("\n"));
758 for(i = 0;i < nsize; i++) {
759 if(exp->_nodes[i].type>MAX_CHAR)
760 scprintf(_SC("[%02d] %10s "),i,g_nnames[exp->_nodes[i].type-MAX_CHAR]);
761 else
762 scprintf(_SC("[%02d] %10c "),i,exp->_nodes[i].type);
763 scprintf(_SC("left %02d right %02d next %02d\n"),exp->_nodes[i].left,exp->_nodes[i].right,exp->_nodes[i].next);
764 }
765 scprintf(_SC("\n"));
766 }
767 #endif
768 exp->_matches = (TRexMatch *) malloc(exp->_nsubexpr * sizeof(TRexMatch));
769 memset(exp->_matches,0,exp->_nsubexpr * sizeof(TRexMatch));
770 }
771 else{
772 trex_free(exp);
773 return NULL;
774 }
775 return exp;
776 }
778 void trex_free(TRex *exp)
779 {
780 if(exp) {
781 if(exp->_nodes) free(exp->_nodes);
782 if(exp->_jmpbuf) free(exp->_jmpbuf);
783 if(exp->_matches) free(exp->_matches);
784 free(exp);
785 }
786 }
788 TRexBool trex_match(TRex* exp,const TRexChar* text)
789 {
790 const TRexChar* res = NULL;
791 exp->_bol = text;
792 exp->_eol = text + scstrlen(text);
793 exp->_currsubexp = 0;
794 res = trex_matchnode(exp,exp->_nodes,text,NULL);
795 if(res == NULL || res != exp->_eol)
796 return TRex_False;
797 return TRex_True;
798 }
800 TRexBool trex_searchrange(TRex* exp,const TRexChar* text_begin,const TRexChar* text_end,const TRexChar** out_begin, const TRexChar** out_end)
801 {
802 const TRexChar *cur = NULL;
803 int node = exp->_first;
804 if(text_begin >= text_end) return TRex_False;
805 exp->_bol = text_begin;
806 exp->_eol = text_end;
807 do {
808 cur = text_begin;
809 while(node != -1) {
810 exp->_currsubexp = 0;
811 cur = trex_matchnode(exp,&exp->_nodes[node],cur,NULL);
812 if(!cur)
813 break;
814 node = exp->_nodes[node].next;
815 }
816 *text_begin++;
817 } while(cur == NULL && text_begin != text_end);
819 if(cur == NULL)
820 return TRex_False;
822 --text_begin;
824 if(out_begin) *out_begin = text_begin;
825 if(out_end) *out_end = cur;
826 return TRex_True;
827 }
829 TRexBool trex_search(TRex* exp,const TRexChar* text, const TRexChar** out_begin, const TRexChar** out_end)
830 {
831 return trex_searchrange(exp,text,text + scstrlen(text),out_begin,out_end);
832 }
834 int trex_getsubexpcount(TRex* exp)
835 {
836 return exp->_nsubexpr;
837 }
839 TRexBool trex_getsubexp(TRex* exp, int n, TRexMatch *subexp)
840 {
841 if( n<0 || n >= exp->_nsubexpr) return TRex_False;
842 *subexp = exp->_matches[n];
843 return TRex_True;
844 }
847 //########################################################################
848 //########################################################################
849 //## E N D R E G E X P
850 //########################################################################
851 //########################################################################
857 //########################################################################
858 //########################################################################
859 //## X M L
860 //########################################################################
861 //########################################################################
863 // Note: This mini-dom library comes from Pedro, another little project
864 // of mine.
866 typedef std::string String;
867 typedef unsigned int XMLCh;
870 class Namespace
871 {
872 public:
873 Namespace()
874 {}
876 Namespace(const String &prefixArg, const String &namespaceURIArg)
877 {
878 prefix = prefixArg;
879 namespaceURI = namespaceURIArg;
880 }
882 Namespace(const Namespace &other)
883 {
884 assign(other);
885 }
887 Namespace &operator=(const Namespace &other)
888 {
889 assign(other);
890 return *this;
891 }
893 virtual ~Namespace()
894 {}
896 virtual String getPrefix()
897 { return prefix; }
899 virtual String getNamespaceURI()
900 { return namespaceURI; }
902 protected:
904 void assign(const Namespace &other)
905 {
906 prefix = other.prefix;
907 namespaceURI = other.namespaceURI;
908 }
910 String prefix;
911 String namespaceURI;
913 };
915 class Attribute
916 {
917 public:
918 Attribute()
919 {}
921 Attribute(const String &nameArg, const String &valueArg)
922 {
923 name = nameArg;
924 value = valueArg;
925 }
927 Attribute(const Attribute &other)
928 {
929 assign(other);
930 }
932 Attribute &operator=(const Attribute &other)
933 {
934 assign(other);
935 return *this;
936 }
938 virtual ~Attribute()
939 {}
941 virtual String getName()
942 { return name; }
944 virtual String getValue()
945 { return value; }
947 protected:
949 void assign(const Attribute &other)
950 {
951 name = other.name;
952 value = other.value;
953 }
955 String name;
956 String value;
958 };
961 class Element
962 {
963 friend class Parser;
965 public:
966 Element()
967 {
968 init();
969 }
971 Element(const String &nameArg)
972 {
973 init();
974 name = nameArg;
975 }
977 Element(const String &nameArg, const String &valueArg)
978 {
979 init();
980 name = nameArg;
981 value = valueArg;
982 }
984 Element(const Element &other)
985 {
986 assign(other);
987 }
989 Element &operator=(const Element &other)
990 {
991 assign(other);
992 return *this;
993 }
995 virtual Element *clone();
997 virtual ~Element()
998 {
999 for (unsigned int i=0 ; i<children.size() ; i++)
1000 delete children[i];
1001 }
1003 virtual String getName()
1004 { return name; }
1006 virtual String getValue()
1007 { return value; }
1009 Element *getParent()
1010 { return parent; }
1012 std::vector<Element *> getChildren()
1013 { return children; }
1015 std::vector<Element *> findElements(const String &name);
1017 String getAttribute(const String &name);
1019 std::vector<Attribute> &getAttributes()
1020 { return attributes; }
1022 String getTagAttribute(const String &tagName, const String &attrName);
1024 String getTagValue(const String &tagName);
1026 void addChild(Element *child);
1028 void addAttribute(const String &name, const String &value);
1030 void addNamespace(const String &prefix, const String &namespaceURI);
1033 /**
1034 * Prettyprint an XML tree to an output stream. Elements are indented
1035 * according to element hierarchy.
1036 * @param f a stream to receive the output
1037 * @param elem the element to output
1038 */
1039 void writeIndented(FILE *f);
1041 /**
1042 * Prettyprint an XML tree to standard output. This is the equivalent of
1043 * writeIndented(stdout).
1044 * @param elem the element to output
1045 */
1046 void print();
1048 int getLine()
1049 { return line; }
1051 protected:
1053 void init()
1054 {
1055 parent = NULL;
1056 line = 0;
1057 }
1059 void assign(const Element &other)
1060 {
1061 parent = other.parent;
1062 children = other.children;
1063 attributes = other.attributes;
1064 namespaces = other.namespaces;
1065 name = other.name;
1066 value = other.value;
1067 line = other.line;
1068 }
1070 void findElementsRecursive(std::vector<Element *>&res, const String &name);
1072 void writeIndentedRecursive(FILE *f, int indent);
1074 Element *parent;
1076 std::vector<Element *>children;
1078 std::vector<Attribute> attributes;
1079 std::vector<Namespace> namespaces;
1081 String name;
1082 String value;
1084 int line;
1085 };
1091 class Parser
1092 {
1093 public:
1094 /**
1095 * Constructor
1096 */
1097 Parser()
1098 { init(); }
1100 virtual ~Parser()
1101 {}
1103 /**
1104 * Parse XML in a char buffer.
1105 * @param buf a character buffer to parse
1106 * @param pos position to start parsing
1107 * @param len number of chars, from pos, to parse.
1108 * @return a pointer to the root of the XML document;
1109 */
1110 Element *parse(const char *buf,int pos,int len);
1112 /**
1113 * Parse XML in a char buffer.
1114 * @param buf a character buffer to parse
1115 * @param pos position to start parsing
1116 * @param len number of chars, from pos, to parse.
1117 * @return a pointer to the root of the XML document;
1118 */
1119 Element *parse(const String &buf);
1121 /**
1122 * Parse a named XML file. The file is loaded like a data file;
1123 * the original format is not preserved.
1124 * @param fileName the name of the file to read
1125 * @return a pointer to the root of the XML document;
1126 */
1127 Element *parseFile(const String &fileName);
1129 /**
1130 * Utility method to preprocess a string for XML
1131 * output, escaping its entities.
1132 * @param str the string to encode
1133 */
1134 static String encode(const String &str);
1136 /**
1137 * Removes whitespace from beginning and end of a string
1138 */
1139 String trim(const String &s);
1141 private:
1143 void init()
1144 {
1145 keepGoing = true;
1146 currentNode = NULL;
1147 parselen = 0;
1148 parsebuf = NULL;
1149 currentPosition = 0;
1150 }
1152 int countLines(int begin, int end);
1154 void getLineAndColumn(int pos, int *lineNr, int *colNr);
1156 void error(const char *fmt, ...);
1158 int peek(int pos);
1160 int match(int pos, const char *text);
1162 int skipwhite(int p);
1164 int getWord(int p0, String &buf);
1166 int getQuoted(int p0, String &buf, int do_i_parse);
1168 int parseVersion(int p0);
1170 int parseDoctype(int p0);
1172 int parseElement(int p0, Element *par,int depth);
1174 Element *parse(XMLCh *buf,int pos,int len);
1176 bool keepGoing;
1177 Element *currentNode;
1178 int parselen;
1179 XMLCh *parsebuf;
1180 String cdatabuf;
1181 int currentPosition;
1182 };
1187 //########################################################################
1188 //# E L E M E N T
1189 //########################################################################
1191 Element *Element::clone()
1192 {
1193 Element *elem = new Element(name, value);
1194 elem->parent = parent;
1195 elem->attributes = attributes;
1196 elem->namespaces = namespaces;
1197 elem->line = line;
1199 std::vector<Element *>::iterator iter;
1200 for (iter = children.begin(); iter != children.end() ; iter++)
1201 {
1202 elem->addChild((*iter)->clone());
1203 }
1204 return elem;
1205 }
1208 void Element::findElementsRecursive(std::vector<Element *>&res, const String &name)
1209 {
1210 if (getName() == name)
1211 {
1212 res.push_back(this);
1213 }
1214 for (unsigned int i=0; i<children.size() ; i++)
1215 children[i]->findElementsRecursive(res, name);
1216 }
1218 std::vector<Element *> Element::findElements(const String &name)
1219 {
1220 std::vector<Element *> res;
1221 findElementsRecursive(res, name);
1222 return res;
1223 }
1225 String Element::getAttribute(const String &name)
1226 {
1227 for (unsigned int i=0 ; i<attributes.size() ; i++)
1228 if (attributes[i].getName() ==name)
1229 return attributes[i].getValue();
1230 return "";
1231 }
1233 String Element::getTagAttribute(const String &tagName, const String &attrName)
1234 {
1235 std::vector<Element *>elems = findElements(tagName);
1236 if (elems.size() <1)
1237 return "";
1238 String res = elems[0]->getAttribute(attrName);
1239 return res;
1240 }
1242 String Element::getTagValue(const String &tagName)
1243 {
1244 std::vector<Element *>elems = findElements(tagName);
1245 if (elems.size() <1)
1246 return "";
1247 String res = elems[0]->getValue();
1248 return res;
1249 }
1251 void Element::addChild(Element *child)
1252 {
1253 if (!child)
1254 return;
1255 child->parent = this;
1256 children.push_back(child);
1257 }
1260 void Element::addAttribute(const String &name, const String &value)
1261 {
1262 Attribute attr(name, value);
1263 attributes.push_back(attr);
1264 }
1266 void Element::addNamespace(const String &prefix, const String &namespaceURI)
1267 {
1268 Namespace ns(prefix, namespaceURI);
1269 namespaces.push_back(ns);
1270 }
1272 void Element::writeIndentedRecursive(FILE *f, int indent)
1273 {
1274 int i;
1275 if (!f)
1276 return;
1277 //Opening tag, and attributes
1278 for (i=0;i<indent;i++)
1279 fputc(' ',f);
1280 fprintf(f,"<%s",name.c_str());
1281 for (unsigned int i=0 ; i<attributes.size() ; i++)
1282 {
1283 fprintf(f," %s=\"%s\"",
1284 attributes[i].getName().c_str(),
1285 attributes[i].getValue().c_str());
1286 }
1287 for (unsigned int i=0 ; i<namespaces.size() ; i++)
1288 {
1289 fprintf(f," xmlns:%s=\"%s\"",
1290 namespaces[i].getPrefix().c_str(),
1291 namespaces[i].getNamespaceURI().c_str());
1292 }
1293 fprintf(f,">\n");
1295 //Between the tags
1296 if (value.size() > 0)
1297 {
1298 for (int i=0;i<indent;i++)
1299 fputc(' ', f);
1300 fprintf(f," %s\n", value.c_str());
1301 }
1303 for (unsigned int i=0 ; i<children.size() ; i++)
1304 children[i]->writeIndentedRecursive(f, indent+2);
1306 //Closing tag
1307 for (int i=0; i<indent; i++)
1308 fputc(' ',f);
1309 fprintf(f,"</%s>\n", name.c_str());
1310 }
1312 void Element::writeIndented(FILE *f)
1313 {
1314 writeIndentedRecursive(f, 0);
1315 }
1317 void Element::print()
1318 {
1319 writeIndented(stdout);
1320 }
1323 //########################################################################
1324 //# P A R S E R
1325 //########################################################################
1329 typedef struct
1330 {
1331 const char *escaped;
1332 char value;
1333 } EntityEntry;
1335 static EntityEntry entities[] =
1336 {
1337 { "&" , '&' },
1338 { "<" , '<' },
1339 { ">" , '>' },
1340 { "'", '\'' },
1341 { """, '"' },
1342 { NULL , '\0' }
1343 };
1347 /**
1348 * Removes whitespace from beginning and end of a string
1349 */
1350 String Parser::trim(const String &s)
1351 {
1352 if (s.size() < 1)
1353 return s;
1355 //Find first non-ws char
1356 unsigned int begin = 0;
1357 for ( ; begin < s.size() ; begin++)
1358 {
1359 if (!isspace(s[begin]))
1360 break;
1361 }
1363 //Find first non-ws char, going in reverse
1364 unsigned int end = s.size() - 1;
1365 for ( ; end > begin ; end--)
1366 {
1367 if (!isspace(s[end]))
1368 break;
1369 }
1370 //trace("begin:%d end:%d", begin, end);
1372 String res = s.substr(begin, end-begin+1);
1373 return res;
1374 }
1377 int Parser::countLines(int begin, int end)
1378 {
1379 int count = 0;
1380 for (int i=begin ; i<end ; i++)
1381 {
1382 XMLCh ch = parsebuf[i];
1383 if (ch == '\n' || ch == '\r')
1384 count++;
1385 }
1386 return count;
1387 }
1390 void Parser::getLineAndColumn(int pos, int *lineNr, int *colNr)
1391 {
1392 int line = 1;
1393 int col = 1;
1394 for (long i=0 ; i<pos ; i++)
1395 {
1396 XMLCh ch = parsebuf[i];
1397 if (ch == '\n' || ch == '\r')
1398 {
1399 col = 0;
1400 line ++;
1401 }
1402 else
1403 col++;
1404 }
1405 *lineNr = line;
1406 *colNr = col;
1408 }
1411 void Parser::error(const char *fmt, ...)
1412 {
1413 int lineNr;
1414 int colNr;
1415 getLineAndColumn(currentPosition, &lineNr, &colNr);
1416 va_list args;
1417 fprintf(stderr, "xml error at line %d, column %d:", lineNr, colNr);
1418 va_start(args,fmt);
1419 vfprintf(stderr,fmt,args);
1420 va_end(args) ;
1421 fprintf(stderr, "\n");
1422 }
1426 int Parser::peek(int pos)
1427 {
1428 if (pos >= parselen)
1429 return -1;
1430 currentPosition = pos;
1431 int ch = parsebuf[pos];
1432 //printf("ch:%c\n", ch);
1433 return ch;
1434 }
1438 String Parser::encode(const String &str)
1439 {
1440 String ret;
1441 for (unsigned int i=0 ; i<str.size() ; i++)
1442 {
1443 XMLCh ch = (XMLCh)str[i];
1444 if (ch == '&')
1445 ret.append("&");
1446 else if (ch == '<')
1447 ret.append("<");
1448 else if (ch == '>')
1449 ret.append(">");
1450 else if (ch == '\'')
1451 ret.append("'");
1452 else if (ch == '"')
1453 ret.append(""");
1454 else
1455 ret.push_back(ch);
1457 }
1458 return ret;
1459 }
1462 int Parser::match(int p0, const char *text)
1463 {
1464 int p = p0;
1465 while (*text)
1466 {
1467 if (peek(p) != *text)
1468 return p0;
1469 p++; text++;
1470 }
1471 return p;
1472 }
1476 int Parser::skipwhite(int p)
1477 {
1479 while (p<parselen)
1480 {
1481 int p2 = match(p, "<!--");
1482 if (p2 > p)
1483 {
1484 p = p2;
1485 while (p<parselen)
1486 {
1487 p2 = match(p, "-->");
1488 if (p2 > p)
1489 {
1490 p = p2;
1491 break;
1492 }
1493 p++;
1494 }
1495 }
1496 XMLCh b = peek(p);
1497 if (!isspace(b))
1498 break;
1499 p++;
1500 }
1501 return p;
1502 }
1504 /* modify this to allow all chars for an element or attribute name*/
1505 int Parser::getWord(int p0, String &buf)
1506 {
1507 int p = p0;
1508 while (p<parselen)
1509 {
1510 XMLCh b = peek(p);
1511 if (b<=' ' || b=='/' || b=='>' || b=='=')
1512 break;
1513 buf.push_back(b);
1514 p++;
1515 }
1516 return p;
1517 }
1519 int Parser::getQuoted(int p0, String &buf, int do_i_parse)
1520 {
1522 int p = p0;
1523 if (peek(p) != '"' && peek(p) != '\'')
1524 return p0;
1525 p++;
1527 while ( p<parselen )
1528 {
1529 XMLCh b = peek(p);
1530 if (b=='"' || b=='\'')
1531 break;
1532 if (b=='&' && do_i_parse)
1533 {
1534 bool found = false;
1535 for (EntityEntry *ee = entities ; ee->value ; ee++)
1536 {
1537 int p2 = match(p, ee->escaped);
1538 if (p2>p)
1539 {
1540 buf.push_back(ee->value);
1541 p = p2;
1542 found = true;
1543 break;
1544 }
1545 }
1546 if (!found)
1547 {
1548 error("unterminated entity");
1549 return false;
1550 }
1551 }
1552 else
1553 {
1554 buf.push_back(b);
1555 p++;
1556 }
1557 }
1558 return p;
1559 }
1561 int Parser::parseVersion(int p0)
1562 {
1563 //printf("### parseVersion: %d\n", p0);
1565 int p = p0;
1567 p = skipwhite(p0);
1569 if (peek(p) != '<')
1570 return p0;
1572 p++;
1573 if (p>=parselen || peek(p)!='?')
1574 return p0;
1576 p++;
1578 String buf;
1580 while (p<parselen)
1581 {
1582 XMLCh ch = peek(p);
1583 if (ch=='?')
1584 {
1585 p++;
1586 break;
1587 }
1588 buf.push_back(ch);
1589 p++;
1590 }
1592 if (peek(p) != '>')
1593 return p0;
1594 p++;
1596 //printf("Got version:%s\n",buf.c_str());
1597 return p;
1598 }
1600 int Parser::parseDoctype(int p0)
1601 {
1602 //printf("### parseDoctype: %d\n", p0);
1604 int p = p0;
1605 p = skipwhite(p);
1607 if (p>=parselen || peek(p)!='<')
1608 return p0;
1610 p++;
1612 if (peek(p)!='!' || peek(p+1)=='-')
1613 return p0;
1614 p++;
1616 String buf;
1617 while (p<parselen)
1618 {
1619 XMLCh ch = peek(p);
1620 if (ch=='>')
1621 {
1622 p++;
1623 break;
1624 }
1625 buf.push_back(ch);
1626 p++;
1627 }
1629 //printf("Got doctype:%s\n",buf.c_str());
1630 return p;
1631 }
1635 int Parser::parseElement(int p0, Element *par,int lineNr)
1636 {
1638 int p = p0;
1640 int p2 = p;
1642 p = skipwhite(p);
1644 //## Get open tag
1645 XMLCh ch = peek(p);
1646 if (ch!='<')
1647 return p0;
1649 //int line, col;
1650 //getLineAndColumn(p, &line, &col);
1652 p++;
1654 String openTagName;
1655 p = skipwhite(p);
1656 p = getWord(p, openTagName);
1657 //printf("####tag :%s\n", openTagName.c_str());
1658 p = skipwhite(p);
1660 //Add element to tree
1661 Element *n = new Element(openTagName);
1662 n->line = lineNr + countLines(p0, p);
1663 n->parent = par;
1664 par->addChild(n);
1666 // Get attributes
1667 if (peek(p) != '>')
1668 {
1669 while (p<parselen)
1670 {
1671 p = skipwhite(p);
1672 ch = peek(p);
1673 //printf("ch:%c\n",ch);
1674 if (ch=='>')
1675 break;
1676 else if (ch=='/' && p<parselen+1)
1677 {
1678 p++;
1679 p = skipwhite(p);
1680 ch = peek(p);
1681 if (ch=='>')
1682 {
1683 p++;
1684 //printf("quick close\n");
1685 return p;
1686 }
1687 }
1688 String attrName;
1689 p2 = getWord(p, attrName);
1690 if (p2==p)
1691 break;
1692 //printf("name:%s",buf);
1693 p=p2;
1694 p = skipwhite(p);
1695 ch = peek(p);
1696 //printf("ch:%c\n",ch);
1697 if (ch!='=')
1698 break;
1699 p++;
1700 p = skipwhite(p);
1701 // ch = parsebuf[p];
1702 // printf("ch:%c\n",ch);
1703 String attrVal;
1704 p2 = getQuoted(p, attrVal, true);
1705 p=p2+1;
1706 //printf("name:'%s' value:'%s'\n",attrName.c_str(),attrVal.c_str());
1707 char *namestr = (char *)attrName.c_str();
1708 if (strncmp(namestr, "xmlns:", 6)==0)
1709 n->addNamespace(attrName, attrVal);
1710 else
1711 n->addAttribute(attrName, attrVal);
1712 }
1713 }
1715 bool cdata = false;
1717 p++;
1718 // ### Get intervening data ### */
1719 String data;
1720 while (p<parselen)
1721 {
1722 //# COMMENT
1723 p2 = match(p, "<!--");
1724 if (!cdata && p2>p)
1725 {
1726 p = p2;
1727 while (p<parselen)
1728 {
1729 p2 = match(p, "-->");
1730 if (p2 > p)
1731 {
1732 p = p2;
1733 break;
1734 }
1735 p++;
1736 }
1737 }
1739 ch = peek(p);
1740 //# END TAG
1741 if (ch=='<' && !cdata && peek(p+1)=='/')
1742 {
1743 break;
1744 }
1745 //# CDATA
1746 p2 = match(p, "<![CDATA[");
1747 if (p2 > p)
1748 {
1749 cdata = true;
1750 p = p2;
1751 continue;
1752 }
1754 //# CHILD ELEMENT
1755 if (ch == '<')
1756 {
1757 p2 = parseElement(p, n, lineNr + countLines(p0, p));
1758 if (p2 == p)
1759 {
1760 /*
1761 printf("problem on element:%s. p2:%d p:%d\n",
1762 openTagName.c_str(), p2, p);
1763 */
1764 return p0;
1765 }
1766 p = p2;
1767 continue;
1768 }
1769 //# ENTITY
1770 if (ch=='&' && !cdata)
1771 {
1772 bool found = false;
1773 for (EntityEntry *ee = entities ; ee->value ; ee++)
1774 {
1775 int p2 = match(p, ee->escaped);
1776 if (p2>p)
1777 {
1778 data.push_back(ee->value);
1779 p = p2;
1780 found = true;
1781 break;
1782 }
1783 }
1784 if (!found)
1785 {
1786 error("unterminated entity");
1787 return -1;
1788 }
1789 continue;
1790 }
1792 //# NONE OF THE ABOVE
1793 data.push_back(ch);
1794 p++;
1795 }/*while*/
1798 n->value = data;
1799 //printf("%d : data:%s\n",p,data.c_str());
1801 //## Get close tag
1802 p = skipwhite(p);
1803 ch = peek(p);
1804 if (ch != '<')
1805 {
1806 error("no < for end tag\n");
1807 return p0;
1808 }
1809 p++;
1810 ch = peek(p);
1811 if (ch != '/')
1812 {
1813 error("no / on end tag");
1814 return p0;
1815 }
1816 p++;
1817 ch = peek(p);
1818 p = skipwhite(p);
1819 String closeTagName;
1820 p = getWord(p, closeTagName);
1821 if (openTagName != closeTagName)
1822 {
1823 error("Mismatched closing tag. Expected </%S>. Got '%S'.",
1824 openTagName.c_str(), closeTagName.c_str());
1825 return p0;
1826 }
1827 p = skipwhite(p);
1828 if (peek(p) != '>')
1829 {
1830 error("no > on end tag for '%s'", closeTagName.c_str());
1831 return p0;
1832 }
1833 p++;
1834 // printf("close element:%s\n",closeTagName.c_str());
1835 p = skipwhite(p);
1836 return p;
1837 }
1842 Element *Parser::parse(XMLCh *buf,int pos,int len)
1843 {
1844 parselen = len;
1845 parsebuf = buf;
1846 Element *rootNode = new Element("root");
1847 pos = parseVersion(pos);
1848 pos = parseDoctype(pos);
1849 pos = parseElement(pos, rootNode, 1);
1850 return rootNode;
1851 }
1854 Element *Parser::parse(const char *buf, int pos, int len)
1855 {
1856 XMLCh *charbuf = new XMLCh[len + 1];
1857 long i = 0;
1858 for ( ; i < len ; i++)
1859 charbuf[i] = (XMLCh)buf[i];
1860 charbuf[i] = '\0';
1862 Element *n = parse(charbuf, pos, len);
1863 delete[] charbuf;
1864 return n;
1865 }
1867 Element *Parser::parse(const String &buf)
1868 {
1869 long len = (long)buf.size();
1870 XMLCh *charbuf = new XMLCh[len + 1];
1871 long i = 0;
1872 for ( ; i < len ; i++)
1873 charbuf[i] = (XMLCh)buf[i];
1874 charbuf[i] = '\0';
1876 Element *n = parse(charbuf, 0, len);
1877 delete[] charbuf;
1878 return n;
1879 }
1881 Element *Parser::parseFile(const String &fileName)
1882 {
1884 //##### LOAD INTO A CHAR BUF, THEN CONVERT TO XMLCh
1885 FILE *f = fopen(fileName.c_str(), "rb");
1886 if (!f)
1887 return NULL;
1889 struct stat statBuf;
1890 if (fstat(fileno(f),&statBuf)<0)
1891 {
1892 fclose(f);
1893 return NULL;
1894 }
1895 long filelen = statBuf.st_size;
1897 //printf("length:%d\n",filelen);
1898 XMLCh *charbuf = new XMLCh[filelen + 1];
1899 for (XMLCh *p=charbuf ; !feof(f) ; p++)
1900 {
1901 *p = (XMLCh)fgetc(f);
1902 }
1903 fclose(f);
1904 charbuf[filelen] = '\0';
1907 /*
1908 printf("nrbytes:%d\n",wc_count);
1909 printf("buf:%ls\n======\n",charbuf);
1910 */
1911 Element *n = parse(charbuf, 0, filelen);
1912 delete[] charbuf;
1913 return n;
1914 }
1916 //########################################################################
1917 //########################################################################
1918 //## E N D X M L
1919 //########################################################################
1920 //########################################################################
1927 //########################################################################
1928 //########################################################################
1929 //## U R I
1930 //########################################################################
1931 //########################################################################
1933 //This would normally be a call to a UNICODE function
1934 #define isLetter(x) isalpha(x)
1936 /**
1937 * A class that implements the W3C URI resource reference.
1938 */
1939 class URI
1940 {
1941 public:
1943 typedef enum
1944 {
1945 SCHEME_NONE =0,
1946 SCHEME_DATA,
1947 SCHEME_HTTP,
1948 SCHEME_HTTPS,
1949 SCHEME_FTP,
1950 SCHEME_FILE,
1951 SCHEME_LDAP,
1952 SCHEME_MAILTO,
1953 SCHEME_NEWS,
1954 SCHEME_TELNET
1955 } SchemeTypes;
1957 /**
1958 *
1959 */
1960 URI()
1961 {
1962 init();
1963 }
1965 /**
1966 *
1967 */
1968 URI(const String &str)
1969 {
1970 init();
1971 parse(str);
1972 }
1975 /**
1976 *
1977 */
1978 URI(const char *str)
1979 {
1980 init();
1981 String domStr = str;
1982 parse(domStr);
1983 }
1986 /**
1987 *
1988 */
1989 URI(const URI &other)
1990 {
1991 init();
1992 assign(other);
1993 }
1996 /**
1997 *
1998 */
1999 URI &operator=(const URI &other)
2000 {
2001 init();
2002 assign(other);
2003 return *this;
2004 }
2007 /**
2008 *
2009 */
2010 virtual ~URI()
2011 {}
2015 /**
2016 *
2017 */
2018 virtual bool parse(const String &str);
2020 /**
2021 *
2022 */
2023 virtual String toString() const;
2025 /**
2026 *
2027 */
2028 virtual int getScheme() const;
2030 /**
2031 *
2032 */
2033 virtual String getSchemeStr() const;
2035 /**
2036 *
2037 */
2038 virtual String getAuthority() const;
2040 /**
2041 * Same as getAuthority, but if the port has been specified
2042 * as host:port , the port will not be included
2043 */
2044 virtual String getHost() const;
2046 /**
2047 *
2048 */
2049 virtual int getPort() const;
2051 /**
2052 *
2053 */
2054 virtual String getPath() const;
2056 /**
2057 *
2058 */
2059 virtual String getNativePath() const;
2061 /**
2062 *
2063 */
2064 virtual bool isAbsolute() const;
2066 /**
2067 *
2068 */
2069 virtual bool isOpaque() const;
2071 /**
2072 *
2073 */
2074 virtual String getQuery() const;
2076 /**
2077 *
2078 */
2079 virtual String getFragment() const;
2081 /**
2082 *
2083 */
2084 virtual URI resolve(const URI &other) const;
2086 /**
2087 *
2088 */
2089 virtual void normalize();
2091 private:
2093 /**
2094 *
2095 */
2096 void init()
2097 {
2098 parsebuf = NULL;
2099 parselen = 0;
2100 scheme = SCHEME_NONE;
2101 schemeStr = "";
2102 port = 0;
2103 authority = "";
2104 path = "";
2105 absolute = false;
2106 opaque = false;
2107 query = "";
2108 fragment = "";
2109 }
2112 /**
2113 *
2114 */
2115 void assign(const URI &other)
2116 {
2117 scheme = other.scheme;
2118 schemeStr = other.schemeStr;
2119 authority = other.authority;
2120 port = other.port;
2121 path = other.path;
2122 absolute = other.absolute;
2123 opaque = other.opaque;
2124 query = other.query;
2125 fragment = other.fragment;
2126 }
2128 int scheme;
2130 String schemeStr;
2132 String authority;
2134 bool portSpecified;
2136 int port;
2138 String path;
2140 bool absolute;
2142 bool opaque;
2144 String query;
2146 String fragment;
2148 void error(const char *fmt, ...);
2150 void trace(const char *fmt, ...);
2153 int peek(int p);
2155 int match(int p, const char *key);
2157 int parseScheme(int p);
2159 int parseHierarchicalPart(int p0);
2161 int parseQuery(int p0);
2163 int parseFragment(int p0);
2165 int parse(int p);
2167 char *parsebuf;
2169 int parselen;
2171 };
2175 typedef struct
2176 {
2177 int ival;
2178 const char *sval;
2179 int port;
2180 } LookupEntry;
2182 LookupEntry schemes[] =
2183 {
2184 { URI::SCHEME_DATA, "data:", 0 },
2185 { URI::SCHEME_HTTP, "http:", 80 },
2186 { URI::SCHEME_HTTPS, "https:", 443 },
2187 { URI::SCHEME_FTP, "ftp", 12 },
2188 { URI::SCHEME_FILE, "file:", 0 },
2189 { URI::SCHEME_LDAP, "ldap:", 123 },
2190 { URI::SCHEME_MAILTO, "mailto:", 25 },
2191 { URI::SCHEME_NEWS, "news:", 117 },
2192 { URI::SCHEME_TELNET, "telnet:", 23 },
2193 { 0, NULL, 0 }
2194 };
2197 String URI::toString() const
2198 {
2199 String str = schemeStr;
2200 if (authority.size() > 0)
2201 {
2202 str.append("//");
2203 str.append(authority);
2204 }
2205 str.append(path);
2206 if (query.size() > 0)
2207 {
2208 str.append("?");
2209 str.append(query);
2210 }
2211 if (fragment.size() > 0)
2212 {
2213 str.append("#");
2214 str.append(fragment);
2215 }
2216 return str;
2217 }
2220 int URI::getScheme() const
2221 {
2222 return scheme;
2223 }
2225 String URI::getSchemeStr() const
2226 {
2227 return schemeStr;
2228 }
2231 String URI::getAuthority() const
2232 {
2233 String ret = authority;
2234 if (portSpecified && port>=0)
2235 {
2236 char buf[7];
2237 snprintf(buf, 6, ":%6d", port);
2238 ret.append(buf);
2239 }
2240 return ret;
2241 }
2243 String URI::getHost() const
2244 {
2245 return authority;
2246 }
2248 int URI::getPort() const
2249 {
2250 return port;
2251 }
2254 String URI::getPath() const
2255 {
2256 return path;
2257 }
2259 String URI::getNativePath() const
2260 {
2261 String npath;
2262 #ifdef __WIN32__
2263 unsigned int firstChar = 0;
2264 if (path.size() >= 3)
2265 {
2266 if (path[0] == '/' &&
2267 isLetter(path[1]) &&
2268 path[2] == ':')
2269 firstChar++;
2270 }
2271 for (unsigned int i=firstChar ; i<path.size() ; i++)
2272 {
2273 XMLCh ch = (XMLCh) path[i];
2274 if (ch == '/')
2275 npath.push_back((XMLCh)'\\');
2276 else
2277 npath.push_back(ch);
2278 }
2279 #else
2280 npath = path;
2281 #endif
2282 return npath;
2283 }
2286 bool URI::isAbsolute() const
2287 {
2288 return absolute;
2289 }
2291 bool URI::isOpaque() const
2292 {
2293 return opaque;
2294 }
2297 String URI::getQuery() const
2298 {
2299 return query;
2300 }
2303 String URI::getFragment() const
2304 {
2305 return fragment;
2306 }
2309 URI URI::resolve(const URI &other) const
2310 {
2311 //### According to w3c, this is handled in 3 cases
2313 //## 1
2314 if (opaque || other.isAbsolute())
2315 return other;
2317 //## 2
2318 if (other.fragment.size() > 0 &&
2319 other.path.size() == 0 &&
2320 other.scheme == SCHEME_NONE &&
2321 other.authority.size() == 0 &&
2322 other.query.size() == 0 )
2323 {
2324 URI fragUri = *this;
2325 fragUri.fragment = other.fragment;
2326 return fragUri;
2327 }
2329 //## 3 http://www.ietf.org/rfc/rfc2396.txt, section 5.2
2330 URI newUri;
2331 //# 3.1
2332 newUri.scheme = scheme;
2333 newUri.schemeStr = schemeStr;
2334 newUri.query = other.query;
2335 newUri.fragment = other.fragment;
2336 if (other.authority.size() > 0)
2337 {
2338 //# 3.2
2339 if (absolute || other.absolute)
2340 newUri.absolute = true;
2341 newUri.authority = other.authority;
2342 newUri.port = other.port;//part of authority
2343 newUri.path = other.path;
2344 }
2345 else
2346 {
2347 //# 3.3
2348 if (other.absolute)
2349 {
2350 newUri.absolute = true;
2351 newUri.path = other.path;
2352 }
2353 else
2354 {
2355 unsigned int pos = path.find_last_of('/');
2356 if (pos != path.npos)
2357 {
2358 String tpath = path.substr(0, pos+1);
2359 tpath.append(other.path);
2360 newUri.path = tpath;
2361 }
2362 else
2363 newUri.path = other.path;
2364 }
2365 }
2367 newUri.normalize();
2368 return newUri;
2369 }
2373 /**
2374 * This follows the Java URI algorithm:
2375 * 1. All "." segments are removed.
2376 * 2. If a ".." segment is preceded by a non-".." segment
2377 * then both of these segments are removed. This step
2378 * is repeated until it is no longer applicable.
2379 * 3. If the path is relative, and if its first segment
2380 * contains a colon character (':'), then a "." segment
2381 * is prepended. This prevents a relative URI with a path
2382 * such as "a:b/c/d" from later being re-parsed as an
2383 * opaque URI with a scheme of "a" and a scheme-specific
2384 * part of "b/c/d". (Deviation from RFC 2396)
2385 */
2386 void URI::normalize()
2387 {
2388 std::vector<String> segments;
2390 //## Collect segments
2391 if (path.size()<2)
2392 return;
2393 bool abs = false;
2394 unsigned int pos=0;
2395 if (path[0]=='/')
2396 {
2397 abs = true;
2398 pos++;
2399 }
2400 while (pos < path.size())
2401 {
2402 unsigned int pos2 = path.find('/', pos);
2403 if (pos2==path.npos)
2404 {
2405 String seg = path.substr(pos);
2406 //printf("last segment:%s\n", seg.c_str());
2407 segments.push_back(seg);
2408 break;
2409 }
2410 if (pos2>pos)
2411 {
2412 String seg = path.substr(pos, pos2-pos);
2413 //printf("segment:%s\n", seg.c_str());
2414 segments.push_back(seg);
2415 }
2416 pos = pos2;
2417 pos++;
2418 }
2420 //## Clean up (normalize) segments
2421 bool edited = false;
2422 std::vector<String>::iterator iter;
2423 for (iter=segments.begin() ; iter!=segments.end() ; )
2424 {
2425 String s = *iter;
2426 if (s == ".")
2427 {
2428 iter = segments.erase(iter);
2429 edited = true;
2430 }
2431 else if (s == ".." &&
2432 iter != segments.begin() &&
2433 *(iter-1) != "..")
2434 {
2435 iter--; //back up, then erase two entries
2436 iter = segments.erase(iter);
2437 iter = segments.erase(iter);
2438 edited = true;
2439 }
2440 else
2441 iter++;
2442 }
2444 //## Rebuild path, if necessary
2445 if (edited)
2446 {
2447 path.clear();
2448 if (abs)
2449 {
2450 path.append("/");
2451 }
2452 std::vector<String>::iterator iter;
2453 for (iter=segments.begin() ; iter!=segments.end() ; iter++)
2454 {
2455 if (iter != segments.begin())
2456 path.append("/");
2457 path.append(*iter);
2458 }
2459 }
2461 }
2465 //#########################################################################
2466 //# M E S S A G E S
2467 //#########################################################################
2469 void URI::error(const char *fmt, ...)
2470 {
2471 va_list args;
2472 fprintf(stderr, "URI error: ");
2473 va_start(args, fmt);
2474 vfprintf(stderr, fmt, args);
2475 va_end(args);
2476 fprintf(stderr, "\n");
2477 }
2479 void URI::trace(const char *fmt, ...)
2480 {
2481 va_list args;
2482 fprintf(stdout, "URI: ");
2483 va_start(args, fmt);
2484 vfprintf(stdout, fmt, args);
2485 va_end(args);
2486 fprintf(stdout, "\n");
2487 }
2492 //#########################################################################
2493 //# P A R S I N G
2494 //#########################################################################
2498 int URI::peek(int p)
2499 {
2500 if (p<0 || p>=parselen)
2501 return -1;
2502 return parsebuf[p];
2503 }
2507 int URI::match(int p0, const char *key)
2508 {
2509 int p = p0;
2510 while (p < parselen)
2511 {
2512 if (*key == '\0')
2513 return p;
2514 else if (*key != parsebuf[p])
2515 break;
2516 p++; key++;
2517 }
2518 return p0;
2519 }
2521 //#########################################################################
2522 //# Parsing is performed according to:
2523 //# http://www.gbiv.com/protocols/uri/rfc/rfc3986.html#components
2524 //#########################################################################
2526 int URI::parseScheme(int p0)
2527 {
2528 int p = p0;
2529 for (LookupEntry *entry = schemes; entry->sval ; entry++)
2530 {
2531 int p2 = match(p, entry->sval);
2532 if (p2 > p)
2533 {
2534 schemeStr = entry->sval;
2535 scheme = entry->ival;
2536 port = entry->port;
2537 p = p2;
2538 return p;
2539 }
2540 }
2542 return p;
2543 }
2546 int URI::parseHierarchicalPart(int p0)
2547 {
2548 int p = p0;
2549 int ch;
2551 //# Authority field (host and port, for example)
2552 int p2 = match(p, "//");
2553 if (p2 > p)
2554 {
2555 p = p2;
2556 portSpecified = false;
2557 String portStr;
2558 while (p < parselen)
2559 {
2560 ch = peek(p);
2561 if (ch == '/')
2562 break;
2563 else if (ch == ':')
2564 portSpecified = true;
2565 else if (portSpecified)
2566 portStr.push_back((XMLCh)ch);
2567 else
2568 authority.push_back((XMLCh)ch);
2569 p++;
2570 }
2571 if (portStr.size() > 0)
2572 {
2573 char *pstr = (char *)portStr.c_str();
2574 char *endStr;
2575 long val = strtol(pstr, &endStr, 10);
2576 if (endStr > pstr) //successful parse?
2577 port = val;
2578 }
2579 }
2581 //# Are we absolute?
2582 ch = peek(p);
2583 if (isLetter(ch) && peek(p+1)==':')
2584 {
2585 absolute = true;
2586 path.push_back((XMLCh)'/');
2587 }
2588 else if (ch == '/')
2589 {
2590 absolute = true;
2591 if (p>p0) //in other words, if '/' is not the first char
2592 opaque = true;
2593 path.push_back((XMLCh)ch);
2594 p++;
2595 }
2597 while (p < parselen)
2598 {
2599 ch = peek(p);
2600 if (ch == '?' || ch == '#')
2601 break;
2602 path.push_back((XMLCh)ch);
2603 p++;
2604 }
2606 return p;
2607 }
2609 int URI::parseQuery(int p0)
2610 {
2611 int p = p0;
2612 int ch = peek(p);
2613 if (ch != '?')
2614 return p0;
2616 p++;
2617 while (p < parselen)
2618 {
2619 ch = peek(p);
2620 if (ch == '#')
2621 break;
2622 query.push_back((XMLCh)ch);
2623 p++;
2624 }
2627 return p;
2628 }
2630 int URI::parseFragment(int p0)
2631 {
2633 int p = p0;
2634 int ch = peek(p);
2635 if (ch != '#')
2636 return p0;
2638 p++;
2639 while (p < parselen)
2640 {
2641 ch = peek(p);
2642 if (ch == '?')
2643 break;
2644 fragment.push_back((XMLCh)ch);
2645 p++;
2646 }
2649 return p;
2650 }
2653 int URI::parse(int p0)
2654 {
2656 int p = p0;
2658 int p2 = parseScheme(p);
2659 if (p2 < 0)
2660 {
2661 error("Scheme");
2662 return -1;
2663 }
2664 p = p2;
2667 p2 = parseHierarchicalPart(p);
2668 if (p2 < 0)
2669 {
2670 error("Hierarchical part");
2671 return -1;
2672 }
2673 p = p2;
2675 p2 = parseQuery(p);
2676 if (p2 < 0)
2677 {
2678 error("Query");
2679 return -1;
2680 }
2681 p = p2;
2684 p2 = parseFragment(p);
2685 if (p2 < 0)
2686 {
2687 error("Fragment");
2688 return -1;
2689 }
2690 p = p2;
2692 return p;
2694 }
2698 bool URI::parse(const String &str)
2699 {
2700 init();
2702 parselen = str.size();
2704 String tmp;
2705 for (unsigned int i=0 ; i<str.size() ; i++)
2706 {
2707 XMLCh ch = (XMLCh) str[i];
2708 if (ch == '\\')
2709 tmp.push_back((XMLCh)'/');
2710 else
2711 tmp.push_back(ch);
2712 }
2713 parsebuf = (char *) tmp.c_str();
2716 int p = parse(0);
2717 normalize();
2719 if (p < 0)
2720 {
2721 error("Syntax error");
2722 return false;
2723 }
2725 //printf("uri:%s\n", toString().c_str());
2726 //printf("path:%s\n", path.c_str());
2728 return true;
2730 }
2739 //########################################################################
2740 //########################################################################
2741 //## M A K E
2742 //########################################################################
2743 //########################################################################
2745 //########################################################################
2746 //# Stat cache to speed up stat requests
2747 //########################################################################
2748 struct StatResult {
2749 int result;
2750 struct stat statInfo;
2751 };
2752 typedef std::map<String, StatResult> statCacheType;
2753 static statCacheType statCache;
2754 static int cachedStat(const String &f, struct stat *s) {
2755 //printf("Stat path: %s\n", f.c_str());
2756 std::pair<statCacheType::iterator, bool> result = statCache.insert(statCacheType::value_type(f, StatResult()));
2757 if (result.second) {
2758 result.first->second.result = stat(f.c_str(), &(result.first->second.statInfo));
2759 }
2760 *s = result.first->second.statInfo;
2761 return result.first->second.result;
2762 }
2763 static void removeFromStatCache(const String f) {
2764 //printf("Removing from cache: %s\n", f.c_str());
2765 statCache.erase(f);
2766 }
2768 //########################################################################
2769 //# Dir cache to speed up dir requests
2770 //########################################################################
2771 /*struct DirListing {
2772 bool available;
2773 std::vector<String> files;
2774 std::vector<String> dirs;
2775 };
2776 typedef std::map<String, DirListing > dirCacheType;
2777 static dirCacheType dirCache;
2778 static const DirListing &cachedDir(String fullDir)
2779 {
2780 String dirNative = getNativePath(fullDir);
2781 std::pair<dirCacheType::iterator,bool> result = dirCache.insert(dirCacheType::value_type(dirNative, DirListing()));
2782 if (result.second) {
2783 DIR *dir = opendir(dirNative.c_str());
2784 if (!dir)
2785 {
2786 error("Could not open directory %s : %s",
2787 dirNative.c_str(), strerror(errno));
2788 result.first->second.available = false;
2789 }
2790 else
2791 {
2792 result.first->second.available = true;
2793 while (true)
2794 {
2795 struct dirent *de = readdir(dir);
2796 if (!de)
2797 break;
2799 //Get the directory member name
2800 String s = de->d_name;
2801 if (s.size() == 0 || s[0] == '.')
2802 continue;
2803 String childName;
2804 if (dirName.size()>0)
2805 {
2806 childName.append(dirName);
2807 childName.append("/");
2808 }
2809 childName.append(s);
2810 String fullChild = baseDir;
2811 fullChild.append("/");
2812 fullChild.append(childName);
2814 if (isDirectory(fullChild))
2815 {
2816 //trace("directory: %s", childName.c_str());
2817 if (!listFiles(baseDir, childName, res))
2818 return false;
2819 continue;
2820 }
2821 else if (!isRegularFile(fullChild))
2822 {
2823 error("unknown file:%s", childName.c_str());
2824 return false;
2825 }
2827 //all done!
2828 res.push_back(childName);
2830 }
2831 closedir(dir);
2832 }
2833 }
2834 return result.first->second;
2835 }*/
2837 //########################################################################
2838 //# F I L E S E T
2839 //########################################################################
2840 /**
2841 * This is the descriptor for a <fileset> item
2842 */
2843 class FileSet
2844 {
2845 public:
2847 /**
2848 *
2849 */
2850 FileSet()
2851 {}
2853 /**
2854 *
2855 */
2856 FileSet(const FileSet &other)
2857 { assign(other); }
2859 /**
2860 *
2861 */
2862 FileSet &operator=(const FileSet &other)
2863 { assign(other); return *this; }
2865 /**
2866 *
2867 */
2868 virtual ~FileSet()
2869 {}
2871 /**
2872 *
2873 */
2874 String getDirectory() const
2875 { return directory; }
2877 /**
2878 *
2879 */
2880 void setDirectory(const String &val)
2881 { directory = val; }
2883 /**
2884 *
2885 */
2886 void setFiles(const std::vector<String> &val)
2887 { files = val; }
2889 /**
2890 *
2891 */
2892 std::vector<String> getFiles() const
2893 { return files; }
2895 /**
2896 *
2897 */
2898 void setIncludes(const std::vector<String> &val)
2899 { includes = val; }
2901 /**
2902 *
2903 */
2904 std::vector<String> getIncludes() const
2905 { return includes; }
2907 /**
2908 *
2909 */
2910 void setExcludes(const std::vector<String> &val)
2911 { excludes = val; }
2913 /**
2914 *
2915 */
2916 std::vector<String> getExcludes() const
2917 { return excludes; }
2919 /**
2920 *
2921 */
2922 unsigned int size() const
2923 { return files.size(); }
2925 /**
2926 *
2927 */
2928 String operator[](int index) const
2929 { return files[index]; }
2931 /**
2932 *
2933 */
2934 void clear()
2935 {
2936 directory = "";
2937 files.clear();
2938 includes.clear();
2939 excludes.clear();
2940 }
2943 private:
2945 void assign(const FileSet &other)
2946 {
2947 directory = other.directory;
2948 files = other.files;
2949 includes = other.includes;
2950 excludes = other.excludes;
2951 }
2953 String directory;
2954 std::vector<String> files;
2955 std::vector<String> includes;
2956 std::vector<String> excludes;
2957 };
2960 //########################################################################
2961 //# F I L E L I S T
2962 //########################################################################
2963 /**
2964 * This is a simpler, explicitly-named list of files
2965 */
2966 class FileList
2967 {
2968 public:
2970 /**
2971 *
2972 */
2973 FileList()
2974 {}
2976 /**
2977 *
2978 */
2979 FileList(const FileList &other)
2980 { assign(other); }
2982 /**
2983 *
2984 */
2985 FileList &operator=(const FileList &other)
2986 { assign(other); return *this; }
2988 /**
2989 *
2990 */
2991 virtual ~FileList()
2992 {}
2994 /**
2995 *
2996 */
2997 String getDirectory()
2998 { return directory; }
3000 /**
3001 *
3002 */
3003 void setDirectory(const String &val)
3004 { directory = val; }
3006 /**
3007 *
3008 */
3009 void setFiles(const std::vector<String> &val)
3010 { files = val; }
3012 /**
3013 *
3014 */
3015 std::vector<String> getFiles()
3016 { return files; }
3018 /**
3019 *
3020 */
3021 unsigned int size()
3022 { return files.size(); }
3024 /**
3025 *
3026 */
3027 String operator[](int index)
3028 { return files[index]; }
3030 /**
3031 *
3032 */
3033 void clear()
3034 {
3035 directory = "";
3036 files.clear();
3037 }
3040 private:
3042 void assign(const FileList &other)
3043 {
3044 directory = other.directory;
3045 files = other.files;
3046 }
3048 String directory;
3049 std::vector<String> files;
3050 };
3055 //########################################################################
3056 //# M A K E B A S E
3057 //########################################################################
3058 /**
3059 * Base class for all classes in this file
3060 */
3061 class MakeBase
3062 {
3063 public:
3065 MakeBase()
3066 { line = 0; }
3067 virtual ~MakeBase()
3068 {}
3070 /**
3071 * Return the URI of the file associated with this object
3072 */
3073 URI getURI()
3074 { return uri; }
3076 /**
3077 * Set the uri to the given string
3078 */
3079 void setURI(const String &uristr)
3080 { uri.parse(uristr); }
3082 /**
3083 * Resolve another path relative to this one
3084 */
3085 String resolve(const String &otherPath);
3087 /**
3088 * replace variable refs like ${a} with their values
3089 * Assume that the string has already been syntax validated
3090 */
3091 String eval(const String &s, const String &defaultVal);
3093 /**
3094 * replace variable refs like ${a} with their values
3095 * return true or false
3096 * Assume that the string has already been syntax validated
3097 */
3098 bool evalBool(const String &s, bool defaultVal);
3100 /**
3101 * Get an element attribute, performing substitutions if necessary
3102 */
3103 bool getAttribute(Element *elem, const String &name, String &result);
3105 /**
3106 * Get an element value, performing substitutions if necessary
3107 */
3108 bool getValue(Element *elem, String &result);
3110 /**
3111 * Set the current line number in the file
3112 */
3113 void setLine(int val)
3114 { line = val; }
3116 /**
3117 * Get the current line number in the file
3118 */
3119 int getLine()
3120 { return line; }
3123 /**
3124 * Set a property to a given value
3125 */
3126 virtual void setProperty(const String &name, const String &val)
3127 {
3128 properties[name] = val;
3129 }
3131 /**
3132 * Return a named property is found, else a null string
3133 */
3134 virtual String getProperty(const String &name)
3135 {
3136 String val;
3137 std::map<String, String>::iterator iter = properties.find(name);
3138 if (iter != properties.end())
3139 val = iter->second;
3140 String sval;
3141 if (!getSubstitutions(val, sval))
3142 return false;
3143 return sval;
3144 }
3146 /**
3147 * Return true if a named property is found, else false
3148 */
3149 virtual bool hasProperty(const String &name)
3150 {
3151 std::map<String, String>::iterator iter = properties.find(name);
3152 if (iter == properties.end())
3153 return false;
3154 return true;
3155 }
3158 protected:
3160 /**
3161 * The path to the file associated with this object
3162 */
3163 URI uri;
3165 /**
3166 * If this prefix is seen in a substitution, use an environment
3167 * variable.
3168 * example: <property environment="env"/>
3169 * ${env.JAVA_HOME}
3170 */
3171 String envPrefix;
3173 /**
3174 * If this prefix is seen in a substitution, use as a
3175 * pkg-config 'all' query
3176 * example: <property pkg-config="pc"/>
3177 * ${pc.gtkmm}
3178 */
3179 String pcPrefix;
3181 /**
3182 * If this prefix is seen in a substitution, use as a
3183 * pkg-config 'cflags' query
3184 * example: <property pkg-config="pcc"/>
3185 * ${pcc.gtkmm}
3186 */
3187 String pccPrefix;
3189 /**
3190 * If this prefix is seen in a substitution, use as a
3191 * pkg-config 'libs' query
3192 * example: <property pkg-config="pcl"/>
3193 * ${pcl.gtkmm}
3194 */
3195 String pclPrefix;
3201 /**
3202 * Print a printf()-like formatted error message
3203 */
3204 void error(const char *fmt, ...);
3206 /**
3207 * Print a printf()-like formatted trace message
3208 */
3209 void status(const char *fmt, ...);
3211 /**
3212 * Show target status
3213 */
3214 void targetstatus(const char *fmt, ...);
3216 /**
3217 * Print a printf()-like formatted trace message
3218 */
3219 void trace(const char *fmt, ...);
3221 /**
3222 * Check if a given string matches a given regex pattern
3223 */
3224 bool regexMatch(const String &str, const String &pattern);
3226 /**
3227 *
3228 */
3229 String getSuffix(const String &fname);
3231 /**
3232 * Break up a string into substrings delimited the characters
3233 * in delimiters. Null-length substrings are ignored
3234 */
3235 std::vector<String> tokenize(const String &val,
3236 const String &delimiters);
3238 /**
3239 * replace runs of whitespace with a space
3240 */
3241 String strip(const String &s);
3243 /**
3244 * remove leading whitespace from each line
3245 */
3246 String leftJustify(const String &s);
3248 /**
3249 * remove leading and trailing whitespace from string
3250 */
3251 String trim(const String &s);
3253 /**
3254 * Return a lower case version of the given string
3255 */
3256 String toLower(const String &s);
3258 /**
3259 * Return the native format of the canonical
3260 * path which we store
3261 */
3262 String getNativePath(const String &path);
3264 /**
3265 * Execute a shell command. Outbuf is a ref to a string
3266 * to catch the result.
3267 */
3268 bool executeCommand(const String &call,
3269 const String &inbuf,
3270 String &outbuf,
3271 String &errbuf);
3272 /**
3273 * List all directories in a given base and starting directory
3274 * It is usually called like:
3275 * bool ret = listDirectories("src", "", result);
3276 */
3277 bool listDirectories(const String &baseName,
3278 const String &dirname,
3279 std::vector<String> &res);
3281 /**
3282 * Find all files in the named directory
3283 */
3284 bool listFiles(const String &baseName,
3285 const String &dirname,
3286 std::vector<String> &result);
3288 /**
3289 * Perform a listing for a fileset
3290 */
3291 bool listFiles(MakeBase &propRef, FileSet &fileSet);
3293 /**
3294 * Parse a <patternset>
3295 */
3296 bool parsePatternSet(Element *elem,
3297 MakeBase &propRef,
3298 std::vector<String> &includes,
3299 std::vector<String> &excludes);
3301 /**
3302 * Parse a <fileset> entry, and determine which files
3303 * should be included
3304 */
3305 bool parseFileSet(Element *elem,
3306 MakeBase &propRef,
3307 FileSet &fileSet);
3308 /**
3309 * Parse a <filelist> entry
3310 */
3311 bool parseFileList(Element *elem,
3312 MakeBase &propRef,
3313 FileList &fileList);
3315 /**
3316 * Return this object's property list
3317 */
3318 virtual std::map<String, String> &getProperties()
3319 { return properties; }
3322 std::map<String, String> properties;
3324 /**
3325 * Create a directory, making intermediate dirs
3326 * if necessary
3327 */
3328 bool createDirectory(const String &dirname);
3330 /**
3331 * Delete a directory and its children if desired
3332 */
3333 bool removeDirectory(const String &dirName);
3335 /**
3336 * Copy a file from one name to another. Perform only if needed
3337 */
3338 bool copyFile(const String &srcFile, const String &destFile);
3340 /**
3341 * Delete a file
3342 */
3343 bool removeFile(const String &file);
3345 /**
3346 * Tests if the file exists
3347 */
3348 bool fileExists(const String &fileName);
3350 /**
3351 * Tests if the file exists and is a regular file
3352 */
3353 bool isRegularFile(const String &fileName);
3355 /**
3356 * Tests if the file exists and is a directory
3357 */
3358 bool isDirectory(const String &fileName);
3360 /**
3361 * Tests is the modification date of fileA is newer than fileB
3362 */
3363 bool isNewerThan(const String &fileA, const String &fileB);
3365 private:
3367 bool pkgConfigRecursive(const String packageName,
3368 const String &path,
3369 const String &prefix,
3370 int query,
3371 String &result,
3372 std::set<String> &deplist);
3374 /**
3375 * utility method to query for "all", "cflags", or "libs" for this package and its
3376 * dependencies. 0, 1, 2
3377 */
3378 bool pkgConfigQuery(const String &packageName, int query, String &result);
3380 /**
3381 * replace a variable ref like ${a} with a value
3382 */
3383 bool lookupProperty(const String &s, String &result);
3385 /**
3386 * called by getSubstitutions(). This is in case a looked-up string
3387 * has substitutions also.
3388 */
3389 bool getSubstitutionsRecursive(const String &s, String &result, int depth);
3391 /**
3392 * replace variable refs in a string like ${a} with their values
3393 */
3394 bool getSubstitutions(const String &s, String &result);
3396 int line;
3399 };
3403 /**
3404 * Define the pkg-config class here, since it will be used in MakeBase method
3405 * implementations.
3406 */
3407 class PkgConfig : public MakeBase
3408 {
3410 public:
3412 /**
3413 *
3414 */
3415 PkgConfig()
3416 {
3417 path = ".";
3418 prefix = "/target";
3419 init();
3420 }
3422 /**
3423 *
3424 */
3425 PkgConfig(const PkgConfig &other)
3426 { assign(other); }
3428 /**
3429 *
3430 */
3431 PkgConfig &operator=(const PkgConfig &other)
3432 { assign(other); return *this; }
3434 /**
3435 *
3436 */
3437 virtual ~PkgConfig()
3438 { }
3440 /**
3441 *
3442 */
3443 virtual String getName()
3444 { return name; }
3446 /**
3447 *
3448 */
3449 virtual String getPath()
3450 { return path; }
3452 /**
3453 *
3454 */
3455 virtual void setPath(const String &val)
3456 { path = val; }
3458 /**
3459 *
3460 */
3461 virtual String getPrefix()
3462 { return prefix; }
3464 /**
3465 * Allow the user to override the prefix in the file
3466 */
3467 virtual void setPrefix(const String &val)
3468 { prefix = val; }
3470 /**
3471 *
3472 */
3473 virtual String getDescription()
3474 { return description; }
3476 /**
3477 *
3478 */
3479 virtual String getCflags()
3480 { return cflags; }
3482 /**
3483 *
3484 */
3485 virtual String getLibs()
3486 { return libs; }
3488 /**
3489 *
3490 */
3491 virtual String getAll()
3492 {
3493 String ret = cflags;
3494 ret.append(" ");
3495 ret.append(libs);
3496 return ret;
3497 }
3499 /**
3500 *
3501 */
3502 virtual String getVersion()
3503 { return version; }
3505 /**
3506 *
3507 */
3508 virtual int getMajorVersion()
3509 { return majorVersion; }
3511 /**
3512 *
3513 */
3514 virtual int getMinorVersion()
3515 { return minorVersion; }
3517 /**
3518 *
3519 */
3520 virtual int getMicroVersion()
3521 { return microVersion; }
3523 /**
3524 *
3525 */
3526 virtual std::map<String, String> &getAttributes()
3527 { return attrs; }
3529 /**
3530 *
3531 */
3532 virtual std::vector<String> &getRequireList()
3533 { return requireList; }
3535 /**
3536 * Read a file for its details
3537 */
3538 virtual bool readFile(const String &fileName);
3540 /**
3541 * Read a file for its details
3542 */
3543 virtual bool query(const String &name);
3545 private:
3547 void init()
3548 {
3549 //do not set path and prefix here
3550 name = "";
3551 description = "";
3552 cflags = "";
3553 libs = "";
3554 requires = "";
3555 version = "";
3556 majorVersion = 0;
3557 minorVersion = 0;
3558 microVersion = 0;
3559 fileName = "";
3560 attrs.clear();
3561 requireList.clear();
3562 }
3564 void assign(const PkgConfig &other)
3565 {
3566 name = other.name;
3567 path = other.path;
3568 prefix = other.prefix;
3569 description = other.description;
3570 cflags = other.cflags;
3571 libs = other.libs;
3572 requires = other.requires;
3573 version = other.version;
3574 majorVersion = other.majorVersion;
3575 minorVersion = other.minorVersion;
3576 microVersion = other.microVersion;
3577 fileName = other.fileName;
3578 attrs = other.attrs;
3579 requireList = other.requireList;
3580 }
3584 int get(int pos);
3586 int skipwhite(int pos);
3588 int getword(int pos, String &ret);
3590 /**
3591 * Very important
3592 */
3593 bool parseRequires();
3595 void parseVersion();
3597 bool parseLine(const String &lineBuf);
3599 bool parse(const String &buf);
3601 void dumpAttrs();
3603 String name;
3605 String path;
3607 String prefix;
3609 String description;
3611 String cflags;
3613 String libs;
3615 String requires;
3617 String version;
3619 int majorVersion;
3621 int minorVersion;
3623 int microVersion;
3625 String fileName;
3627 std::map<String, String> attrs;
3629 std::vector<String> requireList;
3631 char *parsebuf;
3632 int parselen;
3633 };
3638 /**
3639 * Print a printf()-like formatted error message
3640 */
3641 void MakeBase::error(const char *fmt, ...)
3642 {
3643 va_list args;
3644 va_start(args,fmt);
3645 fprintf(stderr, "Make error line %d: ", line);
3646 vfprintf(stderr, fmt, args);
3647 fprintf(stderr, "\n");
3648 va_end(args) ;
3649 }
3653 /**
3654 * Print a printf()-like formatted trace message
3655 */
3656 void MakeBase::status(const char *fmt, ...)
3657 {
3658 va_list args;
3659 //fprintf(stdout, " ");
3660 va_start(args,fmt);
3661 vfprintf(stdout, fmt, args);
3662 va_end(args);
3663 fprintf(stdout, "\n");
3664 fflush(stdout);
3665 }
3668 /**
3669 * Print a printf()-like formatted trace message
3670 */
3671 void MakeBase::trace(const char *fmt, ...)
3672 {
3673 va_list args;
3674 fprintf(stdout, "Make: ");
3675 va_start(args,fmt);
3676 vfprintf(stdout, fmt, args);
3677 va_end(args) ;
3678 fprintf(stdout, "\n");
3679 fflush(stdout);
3680 }
3684 /**
3685 * Resolve another path relative to this one
3686 */
3687 String MakeBase::resolve(const String &otherPath)
3688 {
3689 URI otherURI(otherPath);
3690 URI fullURI = uri.resolve(otherURI);
3691 String ret = fullURI.toString();
3692 return ret;
3693 }
3697 /**
3698 * Check if a given string matches a given regex pattern
3699 */
3700 bool MakeBase::regexMatch(const String &str, const String &pattern)
3701 {
3702 const TRexChar *terror = NULL;
3703 const TRexChar *cpat = pattern.c_str();
3704 TRex *expr = trex_compile(cpat, &terror);
3705 if (!expr)
3706 {
3707 if (!terror)
3708 terror = "undefined";
3709 error("compilation error [%s]!\n", terror);
3710 return false;
3711 }
3713 bool ret = true;
3715 const TRexChar *cstr = str.c_str();
3716 if (trex_match(expr, cstr))
3717 {
3718 ret = true;
3719 }
3720 else
3721 {
3722 ret = false;
3723 }
3725 trex_free(expr);
3727 return ret;
3728 }
3730 /**
3731 * Return the suffix, if any, of a file name
3732 */
3733 String MakeBase::getSuffix(const String &fname)
3734 {
3735 if (fname.size() < 2)
3736 return "";
3737 unsigned int pos = fname.find_last_of('.');
3738 if (pos == fname.npos)
3739 return "";
3740 pos++;
3741 String res = fname.substr(pos, fname.size()-pos);
3742 //trace("suffix:%s", res.c_str());
3743 return res;
3744 }
3748 /**
3749 * Break up a string into substrings delimited the characters
3750 * in delimiters. Null-length substrings are ignored
3751 */
3752 std::vector<String> MakeBase::tokenize(const String &str,
3753 const String &delimiters)
3754 {
3756 std::vector<String> res;
3757 char *del = (char *)delimiters.c_str();
3758 String dmp;
3759 for (unsigned int i=0 ; i<str.size() ; i++)
3760 {
3761 char ch = str[i];
3762 char *p = (char *)0;
3763 for (p=del ; *p ; p++)
3764 if (*p == ch)
3765 break;
3766 if (*p)
3767 {
3768 if (dmp.size() > 0)
3769 {
3770 res.push_back(dmp);
3771 dmp.clear();
3772 }
3773 }
3774 else
3775 {
3776 dmp.push_back(ch);
3777 }
3778 }
3779 //Add tail
3780 if (dmp.size() > 0)
3781 {
3782 res.push_back(dmp);
3783 dmp.clear();
3784 }
3786 return res;
3787 }
3791 /**
3792 * replace runs of whitespace with a single space
3793 */
3794 String MakeBase::strip(const String &s)
3795 {
3796 int len = s.size();
3797 String stripped;
3798 for (int i = 0 ; i<len ; i++)
3799 {
3800 char ch = s[i];
3801 if (isspace(ch))
3802 {
3803 stripped.push_back(' ');
3804 for ( ; i<len ; i++)
3805 {
3806 ch = s[i];
3807 if (!isspace(ch))
3808 {
3809 stripped.push_back(ch);
3810 break;
3811 }
3812 }
3813 }
3814 else
3815 {
3816 stripped.push_back(ch);
3817 }
3818 }
3819 return stripped;
3820 }
3822 /**
3823 * remove leading whitespace from each line
3824 */
3825 String MakeBase::leftJustify(const String &s)
3826 {
3827 String out;
3828 int len = s.size();
3829 for (int i = 0 ; i<len ; )
3830 {
3831 char ch;
3832 //Skip to first visible character
3833 while (i<len)
3834 {
3835 ch = s[i];
3836 if (ch == '\n' || ch == '\r'
3837 || !isspace(ch))
3838 break;
3839 i++;
3840 }
3841 //Copy the rest of the line
3842 while (i<len)
3843 {
3844 ch = s[i];
3845 if (ch == '\n' || ch == '\r')
3846 {
3847 if (ch != '\r')
3848 out.push_back('\n');
3849 i++;
3850 break;
3851 }
3852 else
3853 {
3854 out.push_back(ch);
3855 }
3856 i++;
3857 }
3858 }
3859 return out;
3860 }
3863 /**
3864 * Removes whitespace from beginning and end of a string
3865 */
3866 String MakeBase::trim(const String &s)
3867 {
3868 if (s.size() < 1)
3869 return s;
3871 //Find first non-ws char
3872 unsigned int begin = 0;
3873 for ( ; begin < s.size() ; begin++)
3874 {
3875 if (!isspace(s[begin]))
3876 break;
3877 }
3879 //Find first non-ws char, going in reverse
3880 unsigned int end = s.size() - 1;
3881 for ( ; end > begin ; end--)
3882 {
3883 if (!isspace(s[end]))
3884 break;
3885 }
3886 //trace("begin:%d end:%d", begin, end);
3888 String res = s.substr(begin, end-begin+1);
3889 return res;
3890 }
3893 /**
3894 * Return a lower case version of the given string
3895 */
3896 String MakeBase::toLower(const String &s)
3897 {
3898 if (s.size()==0)
3899 return s;
3901 String ret;
3902 for(unsigned int i=0; i<s.size() ; i++)
3903 {
3904 ret.push_back(tolower(s[i]));
3905 }
3906 return ret;
3907 }
3910 /**
3911 * Return the native format of the canonical
3912 * path which we store
3913 */
3914 String MakeBase::getNativePath(const String &path)
3915 {
3916 #ifdef __WIN32__
3917 String npath;
3918 unsigned int firstChar = 0;
3919 if (path.size() >= 3)
3920 {
3921 if (path[0] == '/' &&
3922 isalpha(path[1]) &&
3923 path[2] == ':')
3924 firstChar++;
3925 }
3926 for (unsigned int i=firstChar ; i<path.size() ; i++)
3927 {
3928 char ch = path[i];
3929 if (ch == '/')
3930 npath.push_back('\\');
3931 else
3932 npath.push_back(ch);
3933 }
3934 return npath;
3935 #else
3936 return path;
3937 #endif
3938 }
3941 #ifdef __WIN32__
3942 #include <tchar.h>
3944 static String win32LastError()
3945 {
3947 DWORD dw = GetLastError();
3949 LPVOID str;
3950 FormatMessage(
3951 FORMAT_MESSAGE_ALLOCATE_BUFFER |
3952 FORMAT_MESSAGE_FROM_SYSTEM,
3953 NULL,
3954 dw,
3955 0,
3956 (LPTSTR) &str,
3957 0, NULL );
3958 LPTSTR p = _tcschr((const char *)str, _T('\r'));
3959 if(p != NULL)
3960 { // lose CRLF
3961 *p = _T('\0');
3962 }
3963 String ret = (char *)str;
3964 LocalFree(str);
3966 return ret;
3967 }
3968 #endif
3973 #ifdef __WIN32__
3975 /**
3976 * Execute a system call, using pipes to send data to the
3977 * program's stdin, and reading stdout and stderr.
3978 */
3979 bool MakeBase::executeCommand(const String &command,
3980 const String &inbuf,
3981 String &outbuf,
3982 String &errbuf)
3983 {
3985 status("============ cmd ============\n%s\n=============================",
3986 command.c_str());
3988 outbuf.clear();
3989 errbuf.clear();
3992 /*
3993 I really hate having win32 code in this program, but the
3994 read buffer in command.com and cmd.exe are just too small
3995 for the large commands we need for compiling and linking.
3996 */
3998 bool ret = true;
4000 //# Allocate a separate buffer for safety
4001 char *paramBuf = new char[command.size() + 1];
4002 if (!paramBuf)
4003 {
4004 error("executeCommand cannot allocate command buffer");
4005 return false;
4006 }
4007 strcpy(paramBuf, (char *)command.c_str());
4009 //# Go to http://msdn2.microsoft.com/en-us/library/ms682499.aspx
4010 //# to see how Win32 pipes work
4012 //# Create pipes
4013 SECURITY_ATTRIBUTES saAttr;
4014 saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
4015 saAttr.bInheritHandle = TRUE;
4016 saAttr.lpSecurityDescriptor = NULL;
4017 HANDLE stdinRead, stdinWrite;
4018 HANDLE stdoutRead, stdoutWrite;
4019 HANDLE stderrRead, stderrWrite;
4020 if (!CreatePipe(&stdinRead, &stdinWrite, &saAttr, 0))
4021 {
4022 error("executeProgram: could not create pipe");
4023 delete[] paramBuf;
4024 return false;
4025 }
4026 SetHandleInformation(stdinWrite, HANDLE_FLAG_INHERIT, 0);
4027 if (!CreatePipe(&stdoutRead, &stdoutWrite, &saAttr, 0))
4028 {
4029 error("executeProgram: could not create pipe");
4030 delete[] paramBuf;
4031 return false;
4032 }
4033 SetHandleInformation(stdoutRead, HANDLE_FLAG_INHERIT, 0);
4034 if (&outbuf != &errbuf) {
4035 if (!CreatePipe(&stderrRead, &stderrWrite, &saAttr, 0))
4036 {
4037 error("executeProgram: could not create pipe");
4038 delete[] paramBuf;
4039 return false;
4040 }
4041 SetHandleInformation(stderrRead, HANDLE_FLAG_INHERIT, 0);
4042 } else {
4043 stderrRead = stdoutRead;
4044 stderrWrite = stdoutWrite;
4045 }
4047 // Create the process
4048 STARTUPINFO siStartupInfo;
4049 PROCESS_INFORMATION piProcessInfo;
4050 memset(&siStartupInfo, 0, sizeof(siStartupInfo));
4051 memset(&piProcessInfo, 0, sizeof(piProcessInfo));
4052 siStartupInfo.cb = sizeof(siStartupInfo);
4053 siStartupInfo.hStdError = stderrWrite;
4054 siStartupInfo.hStdOutput = stdoutWrite;
4055 siStartupInfo.hStdInput = stdinRead;
4056 siStartupInfo.dwFlags |= STARTF_USESTDHANDLES;
4058 if (!CreateProcess(NULL, paramBuf, NULL, NULL, true,
4059 0, NULL, NULL, &siStartupInfo,
4060 &piProcessInfo))
4061 {
4062 error("executeCommand : could not create process : %s",
4063 win32LastError().c_str());
4064 ret = false;
4065 }
4067 delete[] paramBuf;
4069 DWORD bytesWritten;
4070 if (inbuf.size()>0 &&
4071 !WriteFile(stdinWrite, inbuf.c_str(), inbuf.size(),
4072 &bytesWritten, NULL))
4073 {
4074 error("executeCommand: could not write to pipe");
4075 return false;
4076 }
4077 if (!CloseHandle(stdinWrite))
4078 {
4079 error("executeCommand: could not close write pipe");
4080 return false;
4081 }
4082 if (!CloseHandle(stdoutWrite))
4083 {
4084 error("executeCommand: could not close read pipe");
4085 return false;
4086 }
4087 if (stdoutWrite != stderrWrite && !CloseHandle(stderrWrite))
4088 {
4089 error("executeCommand: could not close read pipe");
4090 return false;
4091 }
4093 bool lastLoop = false;
4094 while (true)
4095 {
4096 DWORD avail;
4097 DWORD bytesRead;
4098 char readBuf[4096];
4100 //trace("## stderr");
4101 PeekNamedPipe(stderrRead, NULL, 0, NULL, &avail, NULL);
4102 if (avail > 0)
4103 {
4104 bytesRead = 0;
4105 if (avail>4096) avail = 4096;
4106 ReadFile(stderrRead, readBuf, avail, &bytesRead, NULL);
4107 if (bytesRead > 0)
4108 {
4109 for (unsigned int i=0 ; i<bytesRead ; i++)
4110 errbuf.push_back(readBuf[i]);
4111 }
4112 }
4114 //trace("## stdout");
4115 PeekNamedPipe(stdoutRead, NULL, 0, NULL, &avail, NULL);
4116 if (avail > 0)
4117 {
4118 bytesRead = 0;
4119 if (avail>4096) avail = 4096;
4120 ReadFile(stdoutRead, readBuf, avail, &bytesRead, NULL);
4121 if (bytesRead > 0)
4122 {
4123 for (unsigned int i=0 ; i<bytesRead ; i++)
4124 outbuf.push_back(readBuf[i]);
4125 }
4126 }
4128 //Was this the final check after program done?
4129 if (lastLoop)
4130 break;
4132 DWORD exitCode;
4133 GetExitCodeProcess(piProcessInfo.hProcess, &exitCode);
4134 if (exitCode != STILL_ACTIVE)
4135 lastLoop = true;
4137 Sleep(10);
4138 }
4139 //trace("outbuf:%s", outbuf.c_str());
4140 if (!CloseHandle(stdoutRead))
4141 {
4142 error("executeCommand: could not close read pipe");
4143 return false;
4144 }
4145 if (stdoutRead != stderrRead && !CloseHandle(stderrRead))
4146 {
4147 error("executeCommand: could not close read pipe");
4148 return false;
4149 }
4151 DWORD exitCode;
4152 GetExitCodeProcess(piProcessInfo.hProcess, &exitCode);
4153 //trace("exit code:%d", exitCode);
4154 if (exitCode != 0)
4155 {
4156 ret = false;
4157 }
4159 CloseHandle(piProcessInfo.hProcess);
4160 CloseHandle(piProcessInfo.hThread);
4162 return ret;
4164 }
4166 #else /*do it unix style*/
4168 #include <sys/wait.h>
4172 /**
4173 * Execute a system call, using pipes to send data to the
4174 * program's stdin, and reading stdout and stderr.
4175 */
4176 bool MakeBase::executeCommand(const String &command,
4177 const String &inbuf,
4178 String &outbuf,
4179 String &errbuf)
4180 {
4182 status("============ cmd ============\n%s\n=============================",
4183 command.c_str());
4185 outbuf.clear();
4186 errbuf.clear();
4189 int outfds[2];
4190 if (pipe(outfds) < 0)
4191 return false;
4192 int errfds[2];
4193 if (pipe(errfds) < 0)
4194 return false;
4195 int pid = fork();
4196 if (pid < 0)
4197 {
4198 close(outfds[0]);
4199 close(outfds[1]);
4200 close(errfds[0]);
4201 close(errfds[1]);
4202 error("launch of command '%s' failed : %s",
4203 command.c_str(), strerror(errno));
4204 return false;
4205 }
4206 else if (pid > 0) // parent
4207 {
4208 close(outfds[1]);
4209 close(errfds[1]);
4210 }
4211 else // == 0, child
4212 {
4213 close(outfds[0]);
4214 dup2(outfds[1], STDOUT_FILENO);
4215 close(outfds[1]);
4216 close(errfds[0]);
4217 dup2(errfds[1], STDERR_FILENO);
4218 close(errfds[1]);
4220 char *args[4];
4221 args[0] = (char *)"sh";
4222 args[1] = (char *)"-c";
4223 args[2] = (char *)command.c_str();
4224 args[3] = NULL;
4225 execv("/bin/sh", args);
4226 exit(EXIT_FAILURE);
4227 }
4229 String outb;
4230 String errb;
4232 int outRead = outfds[0];
4233 int errRead = errfds[0];
4234 int max = outRead;
4235 if (errRead > max)
4236 max = errRead;
4238 bool outOpen = true;
4239 bool errOpen = true;
4241 while (outOpen || errOpen)
4242 {
4243 char ch;
4244 fd_set fdset;
4245 FD_ZERO(&fdset);
4246 if (outOpen)
4247 FD_SET(outRead, &fdset);
4248 if (errOpen)
4249 FD_SET(errRead, &fdset);
4250 int ret = select(max+1, &fdset, NULL, NULL, NULL);
4251 if (ret < 0)
4252 break;
4253 if (FD_ISSET(outRead, &fdset))
4254 {
4255 if (read(outRead, &ch, 1) <= 0)
4256 { outOpen = false; }
4257 else if (ch <= 0)
4258 { /* outOpen = false; */ }
4259 else
4260 { outb.push_back(ch); }
4261 }
4262 if (FD_ISSET(errRead, &fdset))
4263 {
4264 if (read(errRead, &ch, 1) <= 0)
4265 { errOpen = false; }
4266 else if (ch <= 0)
4267 { /* errOpen = false; */ }
4268 else
4269 { errb.push_back(ch); }
4270 }
4271 }
4273 int childReturnValue;
4274 wait(&childReturnValue);
4276 close(outRead);
4277 close(errRead);
4279 outbuf = outb;
4280 errbuf = errb;
4282 if (childReturnValue != 0)
4283 {
4284 error("exec of command '%s' failed : %s",
4285 command.c_str(), strerror(childReturnValue));
4286 return false;
4287 }
4289 return true;
4290 }
4292 #endif
4297 bool MakeBase::listDirectories(const String &baseName,
4298 const String &dirName,
4299 std::vector<String> &res)
4300 {
4301 res.push_back(dirName);
4302 String fullPath = baseName;
4303 if (dirName.size()>0)
4304 {
4305 if (dirName[0]!='/') fullPath.append("/");
4306 fullPath.append(dirName);
4307 }
4308 DIR *dir = opendir(fullPath.c_str());
4309 while (true)
4310 {
4311 struct dirent *de = readdir(dir);
4312 if (!de)
4313 break;
4315 //Get the directory member name
4316 String s = de->d_name;
4317 if (s.size() == 0 || s[0] == '.')
4318 continue;
4319 String childName = dirName;
4320 childName.append("/");
4321 childName.append(s);
4323 String fullChildPath = baseName;
4324 fullChildPath.append("/");
4325 fullChildPath.append(childName);
4326 struct stat finfo;
4327 String childNative = getNativePath(fullChildPath);
4328 if (cachedStat(childNative, &finfo)<0)
4329 {
4330 error("cannot stat file:%s", childNative.c_str());
4331 }
4332 else if (S_ISDIR(finfo.st_mode))
4333 {
4334 //trace("directory: %s", childName.c_str());
4335 if (!listDirectories(baseName, childName, res))
4336 return false;
4337 }
4338 }
4339 closedir(dir);
4341 return true;
4342 }
4345 bool MakeBase::listFiles(const String &baseDir,
4346 const String &dirName,
4347 std::vector<String> &res)
4348 {
4349 String fullDir = baseDir;
4350 if (dirName.size()>0)
4351 {
4352 fullDir.append("/");
4353 fullDir.append(dirName);
4354 }
4355 String dirNative = getNativePath(fullDir);
4357 std::vector<String> subdirs;
4358 DIR *dir = opendir(dirNative.c_str());
4359 if (!dir)
4360 {
4361 error("Could not open directory %s : %s",
4362 dirNative.c_str(), strerror(errno));
4363 return false;
4364 }
4365 while (true)
4366 {
4367 struct dirent *de = readdir(dir);
4368 if (!de)
4369 break;
4371 //Get the directory member name
4372 String s = de->d_name;
4373 if (s.size() == 0 || s[0] == '.')
4374 continue;
4375 String childName;
4376 if (dirName.size()>0)
4377 {
4378 childName.append(dirName);
4379 childName.append("/");
4380 }
4381 childName.append(s);
4382 String fullChild = baseDir;
4383 fullChild.append("/");
4384 fullChild.append(childName);
4386 if (isDirectory(fullChild))
4387 {
4388 //trace("directory: %s", childName.c_str());
4389 if (!listFiles(baseDir, childName, res))
4390 return false;
4391 continue;
4392 }
4393 else if (!isRegularFile(fullChild))
4394 {
4395 error("unknown file:%s", childName.c_str());
4396 return false;
4397 }
4399 //all done!
4400 res.push_back(childName);
4402 }
4403 closedir(dir);
4405 return true;
4406 }
4409 /**
4410 * Several different classes extend MakeBase. By "propRef", we mean
4411 * the one holding the properties. Likely "Make" itself
4412 */
4413 bool MakeBase::listFiles(MakeBase &propRef, FileSet &fileSet)
4414 {
4415 //before doing the list, resolve any property references
4416 //that might have been specified in the directory name, such as ${src}
4417 String fsDir = fileSet.getDirectory();
4418 String dir;
4419 if (!propRef.getSubstitutions(fsDir, dir))
4420 return false;
4421 String baseDir = propRef.resolve(dir);
4422 std::vector<String> fileList;
4423 if (!listFiles(baseDir, "", fileList))
4424 return false;
4426 std::vector<String> includes = fileSet.getIncludes();
4427 std::vector<String> excludes = fileSet.getExcludes();
4429 std::vector<String> incs;
4430 std::vector<String>::iterator iter;
4432 std::sort(fileList.begin(), fileList.end());
4434 //If there are <includes>, then add files to the output
4435 //in the order of the include list
4436 if (includes.size()==0)
4437 incs = fileList;
4438 else
4439 {
4440 for (iter = includes.begin() ; iter != includes.end() ; iter++)
4441 {
4442 String &pattern = *iter;
4443 std::vector<String>::iterator siter;
4444 for (siter = fileList.begin() ; siter != fileList.end() ; siter++)
4445 {
4446 String s = *siter;
4447 if (regexMatch(s, pattern))
4448 {
4449 //trace("INCLUDED:%s", s.c_str());
4450 incs.push_back(s);
4451 }
4452 }
4453 }
4454 }
4456 //Now trim off the <excludes>
4457 std::vector<String> res;
4458 for (iter = incs.begin() ; iter != incs.end() ; iter++)
4459 {
4460 String s = *iter;
4461 bool skipme = false;
4462 std::vector<String>::iterator siter;
4463 for (siter = excludes.begin() ; siter != excludes.end() ; siter++)
4464 {
4465 String &pattern = *siter;
4466 if (regexMatch(s, pattern))
4467 {
4468 //trace("EXCLUDED:%s", s.c_str());
4469 skipme = true;
4470 break;
4471 }
4472 }
4473 if (!skipme)
4474 res.push_back(s);
4475 }
4477 fileSet.setFiles(res);
4479 return true;
4480 }
4483 /**
4484 * 0 == all, 1 = cflags, 2 = libs
4485 */
4486 bool MakeBase::pkgConfigRecursive(const String packageName,
4487 const String &path,
4488 const String &prefix,
4489 int query,
4490 String &result,
4491 std::set<String> &deplist)
4492 {
4493 PkgConfig pkgConfig;
4494 if (path.size() > 0)
4495 pkgConfig.setPath(path);
4496 if (prefix.size() > 0)
4497 pkgConfig.setPrefix(prefix);
4498 if (!pkgConfig.query(packageName))
4499 return false;
4500 if (query == 0)
4501 result = pkgConfig.getAll();
4502 else if (query == 1)
4503 result = pkgConfig.getCflags();
4504 else
4505 result = pkgConfig.getLibs();
4506 deplist.insert(packageName);
4507 std::vector<String> list = pkgConfig.getRequireList();
4508 for (unsigned int i = 0 ; i<list.size() ; i++)
4509 {
4510 String depPkgName = list[i];
4511 if (deplist.find(depPkgName) != deplist.end())
4512 continue;
4513 String val;
4514 if (!pkgConfigRecursive(depPkgName, path, prefix, query, val, deplist))
4515 {
4516 error("Based on 'requires' attribute of package '%s'", packageName.c_str());
4517 return false;
4518 }
4519 result.append(" ");
4520 result.append(val);
4521 }
4523 return true;
4524 }
4526 bool MakeBase::pkgConfigQuery(const String &packageName, int query, String &result)
4527 {
4528 std::set<String> deplist;
4529 String path = getProperty("pkg-config-path");
4530 if (path.size()>0)
4531 path = resolve(path);
4532 String prefix = getProperty("pkg-config-prefix");
4533 String val;
4534 if (!pkgConfigRecursive(packageName, path, prefix, query, val, deplist))
4535 return false;
4536 result = val;
4537 return true;
4538 }
4542 /**
4543 * replace a variable ref like ${a} with a value
4544 */
4545 bool MakeBase::lookupProperty(const String &propertyName, String &result)
4546 {
4547 String varname = propertyName;
4548 if (envPrefix.size() > 0 &&
4549 varname.compare(0, envPrefix.size(), envPrefix) == 0)
4550 {
4551 varname = varname.substr(envPrefix.size());
4552 char *envstr = getenv(varname.c_str());
4553 if (!envstr)
4554 {
4555 error("environment variable '%s' not defined", varname.c_str());
4556 return false;
4557 }
4558 result = envstr;
4559 }
4560 else if (pcPrefix.size() > 0 &&
4561 varname.compare(0, pcPrefix.size(), pcPrefix) == 0)
4562 {
4563 varname = varname.substr(pcPrefix.size());
4564 String val;
4565 if (!pkgConfigQuery(varname, 0, val))
4566 return false;
4567 result = val;
4568 }
4569 else if (pccPrefix.size() > 0 &&
4570 varname.compare(0, pccPrefix.size(), pccPrefix) == 0)
4571 {
4572 varname = varname.substr(pccPrefix.size());
4573 String val;
4574 if (!pkgConfigQuery(varname, 1, val))
4575 return false;
4576 result = val;
4577 }
4578 else if (pclPrefix.size() > 0 &&
4579 varname.compare(0, pclPrefix.size(), pclPrefix) == 0)
4580 {
4581 varname = varname.substr(pclPrefix.size());
4582 String val;
4583 if (!pkgConfigQuery(varname, 2, val))
4584 return false;
4585 result = val;
4586 }
4587 else
4588 {
4589 std::map<String, String>::iterator iter;
4590 iter = properties.find(varname);
4591 if (iter != properties.end())
4592 {
4593 result = iter->second;
4594 }
4595 else
4596 {
4597 error("property '%s' not found", varname.c_str());
4598 return false;
4599 }
4600 }
4601 return true;
4602 }
4607 /**
4608 * Analyse a string, looking for any substitutions or other
4609 * things that need resolution
4610 */
4611 bool MakeBase::getSubstitutionsRecursive(const String &str,
4612 String &result, int depth)
4613 {
4614 if (depth > 10)
4615 {
4616 error("nesting of substitutions too deep (>10) for '%s'",
4617 str.c_str());
4618 return false;
4619 }
4620 String s = trim(str);
4621 int len = (int)s.size();
4622 String val;
4623 for (int i=0 ; i<len ; i++)
4624 {
4625 char ch = s[i];
4626 if (ch == '$' && s[i+1] == '{')
4627 {
4628 String varname;
4629 int j = i+2;
4630 for ( ; j<len ; j++)
4631 {
4632 ch = s[j];
4633 if (ch == '$' && s[j+1] == '{')
4634 {
4635 error("attribute %s cannot have nested variable references",
4636 s.c_str());
4637 return false;
4638 }
4639 else if (ch == '}')
4640 {
4641 varname = trim(varname);
4642 String varval;
4643 if (!lookupProperty(varname, varval))
4644 return false;
4645 String varval2;
4646 //Now see if the answer has ${} in it, too
4647 if (!getSubstitutionsRecursive(varval, varval2, depth + 1))
4648 return false;
4649 val.append(varval2);
4650 break;
4651 }
4652 else
4653 {
4654 varname.push_back(ch);
4655 }
4656 }
4657 i = j;
4658 }
4659 else
4660 {
4661 val.push_back(ch);
4662 }
4663 }
4664 result = val;
4665 return true;
4666 }
4668 /**
4669 * Analyse a string, looking for any substitutions or other
4670 * things that need resilution
4671 */
4672 bool MakeBase::getSubstitutions(const String &str, String &result)
4673 {
4674 return getSubstitutionsRecursive(str, result, 0);
4675 }
4679 /**
4680 * replace variable refs like ${a} with their values
4681 * Assume that the string has already been syntax validated
4682 */
4683 String MakeBase::eval(const String &s, const String &defaultVal)
4684 {
4685 if (s.size()==0)
4686 return defaultVal;
4687 String ret;
4688 if (getSubstitutions(s, ret))
4689 return ret;
4690 else
4691 return defaultVal;
4692 }
4695 /**
4696 * replace variable refs like ${a} with their values
4697 * return true or false
4698 * Assume that the string has already been syntax validated
4699 */
4700 bool MakeBase::evalBool(const String &s, bool defaultVal)
4701 {
4702 if (s.size()==0)
4703 return defaultVal;
4704 String val = eval(s, "false");
4705 if (val.size()==0)
4706 return defaultVal;
4707 if (val == "true" || val == "TRUE")
4708 return true;
4709 else
4710 return false;
4711 }
4714 /**
4715 * Get a string attribute, testing it for proper syntax and
4716 * property names.
4717 */
4718 bool MakeBase::getAttribute(Element *elem, const String &name,
4719 String &result)
4720 {
4721 String s = elem->getAttribute(name);
4722 String tmp;
4723 bool ret = getSubstitutions(s, tmp);
4724 if (ret)
4725 result = s; //assign -if- ok
4726 return ret;
4727 }
4730 /**
4731 * Get a string value, testing it for proper syntax and
4732 * property names.
4733 */
4734 bool MakeBase::getValue(Element *elem, String &result)
4735 {
4736 String s = elem->getValue();
4737 String tmp;
4738 bool ret = getSubstitutions(s, tmp);
4739 if (ret)
4740 result = s; //assign -if- ok
4741 return ret;
4742 }
4747 /**
4748 * Parse a <patternset> entry
4749 */
4750 bool MakeBase::parsePatternSet(Element *elem,
4751 MakeBase &propRef,
4752 std::vector<String> &includes,
4753 std::vector<String> &excludes
4754 )
4755 {
4756 std::vector<Element *> children = elem->getChildren();
4757 for (unsigned int i=0 ; i<children.size() ; i++)
4758 {
4759 Element *child = children[i];
4760 String tagName = child->getName();
4761 if (tagName == "exclude")
4762 {
4763 String fname;
4764 if (!propRef.getAttribute(child, "name", fname))
4765 return false;
4766 //trace("EXCLUDE: %s", fname.c_str());
4767 excludes.push_back(fname);
4768 }
4769 else if (tagName == "include")
4770 {
4771 String fname;
4772 if (!propRef.getAttribute(child, "name", fname))
4773 return false;
4774 //trace("INCLUDE: %s", fname.c_str());
4775 includes.push_back(fname);
4776 }
4777 }
4779 return true;
4780 }
4785 /**
4786 * Parse a <fileset> entry, and determine which files
4787 * should be included
4788 */
4789 bool MakeBase::parseFileSet(Element *elem,
4790 MakeBase &propRef,
4791 FileSet &fileSet)
4792 {
4793 String name = elem->getName();
4794 if (name != "fileset")
4795 {
4796 error("expected <fileset>");
4797 return false;
4798 }
4801 std::vector<String> includes;
4802 std::vector<String> excludes;
4804 //A fileset has one implied patternset
4805 if (!parsePatternSet(elem, propRef, includes, excludes))
4806 {
4807 return false;
4808 }
4809 //Look for child tags, including more patternsets
4810 std::vector<Element *> children = elem->getChildren();
4811 for (unsigned int i=0 ; i<children.size() ; i++)
4812 {
4813 Element *child = children[i];
4814 String tagName = child->getName();
4815 if (tagName == "patternset")
4816 {
4817 if (!parsePatternSet(child, propRef, includes, excludes))
4818 {
4819 return false;
4820 }
4821 }
4822 }
4824 String dir;
4825 //Now do the stuff
4826 //Get the base directory for reading file names
4827 if (!propRef.getAttribute(elem, "dir", dir))
4828 return false;
4830 fileSet.setDirectory(dir);
4831 fileSet.setIncludes(includes);
4832 fileSet.setExcludes(excludes);
4834 /*
4835 std::vector<String> fileList;
4836 if (dir.size() > 0)
4837 {
4838 String baseDir = propRef.resolve(dir);
4839 if (!listFiles(baseDir, "", includes, excludes, fileList))
4840 return false;
4841 }
4842 std::sort(fileList.begin(), fileList.end());
4843 result = fileList;
4844 */
4847 /*
4848 for (unsigned int i=0 ; i<result.size() ; i++)
4849 {
4850 trace("RES:%s", result[i].c_str());
4851 }
4852 */
4855 return true;
4856 }
4858 /**
4859 * Parse a <filelist> entry. This is far simpler than FileSet,
4860 * since no directory scanning is needed. The file names are listed
4861 * explicitly.
4862 */
4863 bool MakeBase::parseFileList(Element *elem,
4864 MakeBase &propRef,
4865 FileList &fileList)
4866 {
4867 std::vector<String> fnames;
4868 //Look for child tags, namely "file"
4869 std::vector<Element *> children = elem->getChildren();
4870 for (unsigned int i=0 ; i<children.size() ; i++)
4871 {
4872 Element *child = children[i];
4873 String tagName = child->getName();
4874 if (tagName == "file")
4875 {
4876 String fname = child->getAttribute("name");
4877 if (fname.size()==0)
4878 {
4879 error("<file> element requires name="" attribute");
4880 return false;
4881 }
4882 fnames.push_back(fname);
4883 }
4884 else
4885 {
4886 error("tag <%s> not allowed in <fileset>", tagName.c_str());
4887 return false;
4888 }
4889 }
4891 String dir;
4892 //Get the base directory for reading file names
4893 if (!propRef.getAttribute(elem, "dir", dir))
4894 return false;
4895 fileList.setDirectory(dir);
4896 fileList.setFiles(fnames);
4898 return true;
4899 }
4903 /**
4904 * Create a directory, making intermediate dirs
4905 * if necessary
4906 */
4907 bool MakeBase::createDirectory(const String &dirname)
4908 {
4909 //trace("## createDirectory: %s", dirname.c_str());
4910 //## first check if it exists
4911 struct stat finfo;
4912 String nativeDir = getNativePath(dirname);
4913 char *cnative = (char *) nativeDir.c_str();
4914 #ifdef __WIN32__
4915 if (strlen(cnative)==2 && cnative[1]==':')
4916 return true;
4917 #endif
4918 if (cachedStat(nativeDir, &finfo)==0)
4919 {
4920 if (!S_ISDIR(finfo.st_mode))
4921 {
4922 error("mkdir: file %s exists but is not a directory",
4923 cnative);
4924 return false;
4925 }
4926 else //exists
4927 {
4928 return true;
4929 }
4930 }
4932 //## 2: pull off the last path segment, if any,
4933 //## to make the dir 'above' this one, if necessary
4934 unsigned int pos = dirname.find_last_of('/');
4935 if (pos>0 && pos != dirname.npos)
4936 {
4937 String subpath = dirname.substr(0, pos);
4938 //A letter root (c:) ?
4939 if (!createDirectory(subpath))
4940 return false;
4941 }
4943 //## 3: now make
4944 #ifdef __WIN32__
4945 if (mkdir(cnative)<0)
4946 #else
4947 if (mkdir(cnative, S_IRWXU | S_IRWXG | S_IRWXO)<0)
4948 #endif
4949 {
4950 error("cannot make directory '%s' : %s",
4951 cnative, strerror(errno));
4952 return false;
4953 }
4955 removeFromStatCache(nativeDir);
4957 return true;
4958 }
4961 /**
4962 * Remove a directory recursively
4963 */
4964 bool MakeBase::removeDirectory(const String &dirName)
4965 {
4966 char *dname = (char *)dirName.c_str();
4968 DIR *dir = opendir(dname);
4969 if (!dir)
4970 {
4971 //# Let this fail nicely.
4972 return true;
4973 //error("error opening directory %s : %s", dname, strerror(errno));
4974 //return false;
4975 }
4977 while (true)
4978 {
4979 struct dirent *de = readdir(dir);
4980 if (!de)
4981 break;
4983 //Get the directory member name
4984 String s = de->d_name;
4985 if (s.size() == 0 || s[0] == '.')
4986 continue;
4987 String childName;
4988 if (dirName.size() > 0)
4989 {
4990 childName.append(dirName);
4991 childName.append("/");
4992 }
4993 childName.append(s);
4996 struct stat finfo;
4997 String childNative = getNativePath(childName);
4998 char *cnative = (char *)childNative.c_str();
4999 if (cachedStat(childNative, &finfo)<0)
5000 {
5001 error("cannot stat file:%s", cnative);
5002 }
5003 else if (S_ISDIR(finfo.st_mode))
5004 {
5005 //trace("DEL dir: %s", childName.c_str());
5006 if (!removeDirectory(childName))
5007 {
5008 return false;
5009 }
5010 }
5011 else if (!S_ISREG(finfo.st_mode))
5012 {
5013 //trace("not regular: %s", cnative);
5014 }
5015 else
5016 {
5017 //trace("DEL file: %s", childName.c_str());
5018 if (!removeFile(childName))
5019 {
5020 return false;
5021 }
5022 }
5023 }
5024 closedir(dir);
5026 //Now delete the directory
5027 String native = getNativePath(dirName);
5028 if (rmdir(native.c_str())<0)
5029 {
5030 error("could not delete directory %s : %s",
5031 native.c_str() , strerror(errno));
5032 return false;
5033 }
5035 removeFromStatCache(native);
5037 return true;
5039 }
5042 /**
5043 * Copy a file from one name to another. Perform only if needed
5044 */
5045 bool MakeBase::copyFile(const String &srcFile, const String &destFile)
5046 {
5047 //# 1 Check up-to-date times
5048 String srcNative = getNativePath(srcFile);
5049 struct stat srcinfo;
5050 if (cachedStat(srcNative, &srcinfo)<0)
5051 {
5052 error("source file %s for copy does not exist",
5053 srcNative.c_str());
5054 return false;
5055 }
5057 String destNative = getNativePath(destFile);
5058 struct stat destinfo;
5059 if (cachedStat(destNative, &destinfo)==0)
5060 {
5061 if (destinfo.st_mtime >= srcinfo.st_mtime)
5062 return true;
5063 }
5065 //# 2 prepare a destination directory if necessary
5066 unsigned int pos = destFile.find_last_of('/');
5067 if (pos != destFile.npos)
5068 {
5069 String subpath = destFile.substr(0, pos);
5070 if (!createDirectory(subpath))
5071 return false;
5072 }
5074 //# 3 do the data copy
5075 #ifndef __WIN32__
5077 FILE *srcf = fopen(srcNative.c_str(), "rb");
5078 if (!srcf)
5079 {
5080 error("copyFile cannot open '%s' for reading", srcNative.c_str());
5081 return false;
5082 }
5083 FILE *destf = fopen(destNative.c_str(), "wb");
5084 if (!destf)
5085 {
5086 error("copyFile cannot open %s for writing", srcNative.c_str());
5087 return false;
5088 }
5090 while (!feof(srcf))
5091 {
5092 int ch = fgetc(srcf);
5093 if (ch<0)
5094 break;
5095 fputc(ch, destf);
5096 }
5098 fclose(destf);
5099 fclose(srcf);
5101 #else
5103 if (!CopyFile(srcNative.c_str(), destNative.c_str(), false))
5104 {
5105 error("copyFile from %s to %s failed",
5106 srcNative.c_str(), destNative.c_str());
5107 return false;
5108 }
5110 #endif /* __WIN32__ */
5112 removeFromStatCache(destNative);
5114 return true;
5115 }
5118 /**
5119 * Delete a file
5120 */
5121 bool MakeBase::removeFile(const String &file)
5122 {
5123 String native = getNativePath(file);
5125 if (!fileExists(native))
5126 {
5127 return true;
5128 }
5130 #ifdef WIN32
5131 // On Windows 'remove' will only delete files
5133 if (remove(native.c_str())<0)
5134 {
5135 if (errno==EACCES)
5136 {
5137 error("File %s is read-only", native.c_str());
5138 }
5139 else if (errno==ENOENT)
5140 {
5141 error("File %s does not exist or is a directory", native.c_str());
5142 }
5143 else
5144 {
5145 error("Failed to delete file %s: %s", native.c_str(), strerror(errno));
5146 }
5147 return false;
5148 }
5150 #else
5152 if (!isRegularFile(native))
5153 {
5154 error("File %s does not exist or is not a regular file", native.c_str());
5155 return false;
5156 }
5158 if (remove(native.c_str())<0)
5159 {
5160 if (errno==EACCES)
5161 {
5162 error("File %s is read-only", native.c_str());
5163 }
5164 else
5165 {
5166 error(
5167 errno==EACCES ? "File %s is read-only" :
5168 errno==ENOENT ? "File %s does not exist or is a directory" :
5169 "Failed to delete file %s: %s", native.c_str());
5170 }
5171 return false;
5172 }
5174 #endif
5176 removeFromStatCache(native);
5178 return true;
5179 }
5182 /**
5183 * Tests if the file exists
5184 */
5185 bool MakeBase::fileExists(const String &fileName)
5186 {
5187 String native = getNativePath(fileName);
5188 struct stat finfo;
5190 //Exists?
5191 if (cachedStat(native, &finfo)<0)
5192 return false;
5194 return true;
5195 }
5198 /**
5199 * Tests if the file exists and is a regular file
5200 */
5201 bool MakeBase::isRegularFile(const String &fileName)
5202 {
5203 String native = getNativePath(fileName);
5204 struct stat finfo;
5206 //Exists?
5207 if (cachedStat(native, &finfo)<0)
5208 return false;
5211 //check the file mode
5212 if (!S_ISREG(finfo.st_mode))
5213 return false;
5215 return true;
5216 }
5218 /**
5219 * Tests if the file exists and is a directory
5220 */
5221 bool MakeBase::isDirectory(const String &fileName)
5222 {
5223 String native = getNativePath(fileName);
5224 struct stat finfo;
5226 //Exists?
5227 if (cachedStat(native, &finfo)<0)
5228 return false;
5231 //check the file mode
5232 if (!S_ISDIR(finfo.st_mode))
5233 return false;
5235 return true;
5236 }
5240 /**
5241 * Tests is the modification of fileA is newer than fileB
5242 */
5243 bool MakeBase::isNewerThan(const String &fileA, const String &fileB)
5244 {
5245 //trace("isNewerThan:'%s' , '%s'", fileA.c_str(), fileB.c_str());
5246 String nativeA = getNativePath(fileA);
5247 struct stat infoA;
5248 //IF source does not exist, NOT newer
5249 if (cachedStat(nativeA, &infoA)<0)
5250 {
5251 return false;
5252 }
5254 String nativeB = getNativePath(fileB);
5255 struct stat infoB;
5256 //IF dest does not exist, YES, newer
5257 if (cachedStat(nativeB, &infoB)<0)
5258 {
5259 return true;
5260 }
5262 //check the actual times
5263 if (infoA.st_mtime > infoB.st_mtime)
5264 {
5265 return true;
5266 }
5268 return false;
5269 }
5272 //########################################################################
5273 //# P K G C O N F I G
5274 //########################################################################
5277 /**
5278 * Get a character from the buffer at pos. If out of range,
5279 * return -1 for safety
5280 */
5281 int PkgConfig::get(int pos)
5282 {
5283 if (pos>parselen)
5284 return -1;
5285 return parsebuf[pos];
5286 }
5290 /**
5291 * Skip over all whitespace characters beginning at pos. Return
5292 * the position of the first non-whitespace character.
5293 * Pkg-config is line-oriented, so check for newline
5294 */
5295 int PkgConfig::skipwhite(int pos)
5296 {
5297 while (pos < parselen)
5298 {
5299 int ch = get(pos);
5300 if (ch < 0)
5301 break;
5302 if (!isspace(ch))
5303 break;
5304 pos++;
5305 }
5306 return pos;
5307 }
5310 /**
5311 * Parse the buffer beginning at pos, for a word. Fill
5312 * 'ret' with the result. Return the position after the
5313 * word.
5314 */
5315 int PkgConfig::getword(int pos, String &ret)
5316 {
5317 while (pos < parselen)
5318 {
5319 int ch = get(pos);
5320 if (ch < 0)
5321 break;
5322 if (!isalnum(ch) && ch != '_' && ch != '-' && ch != '+' && ch != '.')
5323 break;
5324 ret.push_back((char)ch);
5325 pos++;
5326 }
5327 return pos;
5328 }
5330 bool PkgConfig::parseRequires()
5331 {
5332 if (requires.size() == 0)
5333 return true;
5334 parsebuf = (char *)requires.c_str();
5335 parselen = requires.size();
5336 int pos = 0;
5337 while (pos < parselen)
5338 {
5339 pos = skipwhite(pos);
5340 String val;
5341 int pos2 = getword(pos, val);
5342 if (pos2 == pos)
5343 break;
5344 pos = pos2;
5345 //trace("val %s", val.c_str());
5346 requireList.push_back(val);
5347 }
5348 return true;
5349 }
5352 static int getint(const String str)
5353 {
5354 char *s = (char *)str.c_str();
5355 char *ends = NULL;
5356 long val = strtol(s, &ends, 10);
5357 if (ends == s)
5358 return 0L;
5359 else
5360 return val;
5361 }
5363 void PkgConfig::parseVersion()
5364 {
5365 if (version.size() == 0)
5366 return;
5367 String s1, s2, s3;
5368 unsigned int pos = 0;
5369 unsigned int pos2 = version.find('.', pos);
5370 if (pos2 == version.npos)
5371 {
5372 s1 = version;
5373 }
5374 else
5375 {
5376 s1 = version.substr(pos, pos2-pos);
5377 pos = pos2;
5378 pos++;
5379 if (pos < version.size())
5380 {
5381 pos2 = version.find('.', pos);
5382 if (pos2 == version.npos)
5383 {
5384 s2 = version.substr(pos, version.size()-pos);
5385 }
5386 else
5387 {
5388 s2 = version.substr(pos, pos2-pos);
5389 pos = pos2;
5390 pos++;
5391 if (pos < version.size())
5392 s3 = version.substr(pos, pos2-pos);
5393 }
5394 }
5395 }
5397 majorVersion = getint(s1);
5398 minorVersion = getint(s2);
5399 microVersion = getint(s3);
5400 //trace("version:%d.%d.%d", majorVersion,
5401 // minorVersion, microVersion );
5402 }
5405 bool PkgConfig::parseLine(const String &lineBuf)
5406 {
5407 parsebuf = (char *)lineBuf.c_str();
5408 parselen = lineBuf.size();
5409 int pos = 0;
5411 while (pos < parselen)
5412 {
5413 String attrName;
5414 pos = skipwhite(pos);
5415 int ch = get(pos);
5416 if (ch == '#')
5417 {
5418 //comment. eat the rest of the line
5419 while (pos < parselen)
5420 {
5421 ch = get(pos);
5422 if (ch == '\n' || ch < 0)
5423 break;
5424 pos++;
5425 }
5426 continue;
5427 }
5428 pos = getword(pos, attrName);
5429 if (attrName.size() == 0)
5430 continue;
5432 pos = skipwhite(pos);
5433 ch = get(pos);
5434 if (ch != ':' && ch != '=')
5435 {
5436 error("expected ':' or '='");
5437 return false;
5438 }
5439 pos++;
5440 pos = skipwhite(pos);
5441 String attrVal;
5442 while (pos < parselen)
5443 {
5444 ch = get(pos);
5445 if (ch == '\n' || ch < 0)
5446 break;
5447 else if (ch == '$' && get(pos+1) == '{')
5448 {
5449 //# this is a ${substitution}
5450 pos += 2;
5451 String subName;
5452 while (pos < parselen)
5453 {
5454 ch = get(pos);
5455 if (ch < 0)
5456 {
5457 error("unterminated substitution");
5458 return false;
5459 }
5460 else if (ch == '}')
5461 break;
5462 else
5463 subName.push_back((char)ch);
5464 pos++;
5465 }
5466 //trace("subName:%s %s", subName.c_str(), prefix.c_str());
5467 if (subName == "prefix" && prefix.size()>0)
5468 {
5469 attrVal.append(prefix);
5470 //trace("prefix override:%s", prefix.c_str());
5471 }
5472 else
5473 {
5474 String subVal = attrs[subName];
5475 //trace("subVal:%s", subVal.c_str());
5476 attrVal.append(subVal);
5477 }
5478 }
5479 else
5480 attrVal.push_back((char)ch);
5481 pos++;
5482 }
5484 attrVal = trim(attrVal);
5485 attrs[attrName] = attrVal;
5487 String attrNameL = toLower(attrName);
5489 if (attrNameL == "name")
5490 name = attrVal;
5491 else if (attrNameL == "description")
5492 description = attrVal;
5493 else if (attrNameL == "cflags")
5494 cflags = attrVal;
5495 else if (attrNameL == "libs")
5496 libs = attrVal;
5497 else if (attrNameL == "requires")
5498 requires = attrVal;
5499 else if (attrNameL == "version")
5500 version = attrVal;
5502 //trace("name:'%s' value:'%s'",
5503 // attrName.c_str(), attrVal.c_str());
5504 }
5506 return true;
5507 }
5510 bool PkgConfig::parse(const String &buf)
5511 {
5512 init();
5514 String line;
5515 int lineNr = 0;
5516 for (unsigned int p=0 ; p<buf.size() ; p++)
5517 {
5518 int ch = buf[p];
5519 if (ch == '\n' || ch == '\r')
5520 {
5521 if (!parseLine(line))
5522 return false;
5523 line.clear();
5524 lineNr++;
5525 }
5526 else
5527 {
5528 line.push_back(ch);
5529 }
5530 }
5531 if (line.size()>0)
5532 {
5533 if (!parseLine(line))
5534 return false;
5535 }
5537 parseRequires();
5538 parseVersion();
5540 return true;
5541 }
5546 void PkgConfig::dumpAttrs()
5547 {
5548 //trace("### PkgConfig attributes for %s", fileName.c_str());
5549 std::map<String, String>::iterator iter;
5550 for (iter=attrs.begin() ; iter!=attrs.end() ; iter++)
5551 {
5552 trace(" %s = %s", iter->first.c_str(), iter->second.c_str());
5553 }
5554 }
5557 bool PkgConfig::readFile(const String &fname)
5558 {
5559 fileName = getNativePath(fname);
5561 FILE *f = fopen(fileName.c_str(), "r");
5562 if (!f)
5563 {
5564 error("cannot open file '%s' for reading", fileName.c_str());
5565 return false;
5566 }
5567 String buf;
5568 while (true)
5569 {
5570 int ch = fgetc(f);
5571 if (ch < 0)
5572 break;
5573 buf.push_back((char)ch);
5574 }
5575 fclose(f);
5577 //trace("####### File:\n%s", buf.c_str());
5578 if (!parse(buf))
5579 {
5580 return false;
5581 }
5583 //dumpAttrs();
5585 return true;
5586 }
5590 bool PkgConfig::query(const String &pkgName)
5591 {
5592 name = pkgName;
5594 String fname = path;
5595 fname.append("/");
5596 fname.append(name);
5597 fname.append(".pc");
5599 if (!readFile(fname))
5600 {
5601 error("Cannot find package '%s'. Do you have it installed?",
5602 pkgName.c_str());
5603 return false;
5604 }
5606 return true;
5607 }
5613 //########################################################################
5614 //# D E P T O O L
5615 //########################################################################
5619 /**
5620 * Class which holds information for each file.
5621 */
5622 class FileRec
5623 {
5624 public:
5626 typedef enum
5627 {
5628 UNKNOWN,
5629 CFILE,
5630 HFILE,
5631 OFILE
5632 } FileType;
5634 /**
5635 * Constructor
5636 */
5637 FileRec()
5638 { init(); type = UNKNOWN; }
5640 /**
5641 * Copy constructor
5642 */
5643 FileRec(const FileRec &other)
5644 { init(); assign(other); }
5645 /**
5646 * Constructor
5647 */
5648 FileRec(int typeVal)
5649 { init(); type = typeVal; }
5650 /**
5651 * Assignment operator
5652 */
5653 FileRec &operator=(const FileRec &other)
5654 { init(); assign(other); return *this; }
5657 /**
5658 * Destructor
5659 */
5660 ~FileRec()
5661 {}
5663 /**
5664 * Directory part of the file name
5665 */
5666 String path;
5668 /**
5669 * Base name, sans directory and suffix
5670 */
5671 String baseName;
5673 /**
5674 * File extension, such as cpp or h
5675 */
5676 String suffix;
5678 /**
5679 * Type of file: CFILE, HFILE, OFILE
5680 */
5681 int type;
5683 /**
5684 * Used to list files ref'd by this one
5685 */
5686 std::map<String, FileRec *> files;
5689 private:
5691 void init()
5692 {
5693 }
5695 void assign(const FileRec &other)
5696 {
5697 type = other.type;
5698 baseName = other.baseName;
5699 suffix = other.suffix;
5700 files = other.files;
5701 }
5703 };
5707 /**
5708 * Simpler dependency record
5709 */
5710 class DepRec
5711 {
5712 public:
5714 /**
5715 * Constructor
5716 */
5717 DepRec()
5718 {init();}
5720 /**
5721 * Copy constructor
5722 */
5723 DepRec(const DepRec &other)
5724 {init(); assign(other);}
5725 /**
5726 * Constructor
5727 */
5728 DepRec(const String &fname)
5729 {init(); name = fname; }
5730 /**
5731 * Assignment operator
5732 */
5733 DepRec &operator=(const DepRec &other)
5734 {init(); assign(other); return *this;}
5737 /**
5738 * Destructor
5739 */
5740 ~DepRec()
5741 {}
5743 /**
5744 * Directory part of the file name
5745 */
5746 String path;
5748 /**
5749 * Base name, without the path and suffix
5750 */
5751 String name;
5753 /**
5754 * Suffix of the source
5755 */
5756 String suffix;
5759 /**
5760 * Used to list files ref'd by this one
5761 */
5762 std::vector<String> files;
5765 private:
5767 void init()
5768 {
5769 }
5771 void assign(const DepRec &other)
5772 {
5773 path = other.path;
5774 name = other.name;
5775 suffix = other.suffix;
5776 files = other.files; //avoid recursion
5777 }
5779 };
5782 class DepTool : public MakeBase
5783 {
5784 public:
5786 /**
5787 * Constructor
5788 */
5789 DepTool()
5790 { init(); }
5792 /**
5793 * Copy constructor
5794 */
5795 DepTool(const DepTool &other)
5796 { init(); assign(other); }
5798 /**
5799 * Assignment operator
5800 */
5801 DepTool &operator=(const DepTool &other)
5802 { init(); assign(other); return *this; }
5805 /**
5806 * Destructor
5807 */
5808 ~DepTool()
5809 {}
5812 /**
5813 * Reset this section of code
5814 */
5815 virtual void init();
5817 /**
5818 * Reset this section of code
5819 */
5820 virtual void assign(const DepTool &other)
5821 {
5822 }
5824 /**
5825 * Sets the source directory which will be scanned
5826 */
5827 virtual void setSourceDirectory(const String &val)
5828 { sourceDir = val; }
5830 /**
5831 * Returns the source directory which will be scanned
5832 */
5833 virtual String getSourceDirectory()
5834 { return sourceDir; }
5836 /**
5837 * Sets the list of files within the directory to analyze
5838 */
5839 virtual void setFileList(const std::vector<String> &list)
5840 { fileList = list; }
5842 /**
5843 * Creates the list of all file names which will be
5844 * candidates for further processing. Reads make.exclude
5845 * to see which files for directories to leave out.
5846 */
5847 virtual bool createFileList();
5850 /**
5851 * Generates the forward dependency list
5852 */
5853 virtual bool generateDependencies();
5856 /**
5857 * Generates the forward dependency list, saving the file
5858 */
5859 virtual bool generateDependencies(const String &);
5862 /**
5863 * Load a dependency file
5864 */
5865 std::vector<DepRec> loadDepFile(const String &fileName);
5867 /**
5868 * Load a dependency file, generating one if necessary
5869 */
5870 std::vector<DepRec> getDepFile(const String &fileName,
5871 bool forceRefresh);
5873 /**
5874 * Save a dependency file
5875 */
5876 bool saveDepFile(const String &fileName);
5879 private:
5882 /**
5883 *
5884 */
5885 void parseName(const String &fullname,
5886 String &path,
5887 String &basename,
5888 String &suffix);
5890 /**
5891 *
5892 */
5893 int get(int pos);
5895 /**
5896 *
5897 */
5898 int skipwhite(int pos);
5900 /**
5901 *
5902 */
5903 int getword(int pos, String &ret);
5905 /**
5906 *
5907 */
5908 bool sequ(int pos, const char *key);
5910 /**
5911 *
5912 */
5913 bool addIncludeFile(FileRec *frec, const String &fname);
5915 /**
5916 *
5917 */
5918 bool scanFile(const String &fname, FileRec *frec);
5920 /**
5921 *
5922 */
5923 bool processDependency(FileRec *ofile, FileRec *include);
5925 /**
5926 *
5927 */
5928 String sourceDir;
5930 /**
5931 *
5932 */
5933 std::vector<String> fileList;
5935 /**
5936 *
5937 */
5938 std::vector<String> directories;
5940 /**
5941 * A list of all files which will be processed for
5942 * dependencies.
5943 */
5944 std::map<String, FileRec *> allFiles;
5946 /**
5947 * The list of .o files, and the
5948 * dependencies upon them.
5949 */
5950 std::map<String, FileRec *> oFiles;
5952 int depFileSize;
5953 char *depFileBuf;
5955 static const int readBufSize = 8192;
5956 char readBuf[8193];//byte larger
5958 };
5964 /**
5965 * Clean up after processing. Called by the destructor, but should
5966 * also be called before the object is reused.
5967 */
5968 void DepTool::init()
5969 {
5970 sourceDir = ".";
5972 fileList.clear();
5973 directories.clear();
5975 //clear output file list
5976 std::map<String, FileRec *>::iterator iter;
5977 for (iter=oFiles.begin(); iter!=oFiles.end() ; iter++)
5978 delete iter->second;
5979 oFiles.clear();
5981 //allFiles actually contains the master copies. delete them
5982 for (iter= allFiles.begin(); iter!=allFiles.end() ; iter++)
5983 delete iter->second;
5984 allFiles.clear();
5986 }
5991 /**
5992 * Parse a full path name into path, base name, and suffix
5993 */
5994 void DepTool::parseName(const String &fullname,
5995 String &path,
5996 String &basename,
5997 String &suffix)
5998 {
5999 if (fullname.size() < 2)
6000 return;
6002 unsigned int pos = fullname.find_last_of('/');
6003 if (pos != fullname.npos && pos<fullname.size()-1)
6004 {
6005 path = fullname.substr(0, pos);
6006 pos++;
6007 basename = fullname.substr(pos, fullname.size()-pos);
6008 }
6009 else
6010 {
6011 path = "";
6012 basename = fullname;
6013 }
6015 pos = basename.find_last_of('.');
6016 if (pos != basename.npos && pos<basename.size()-1)
6017 {
6018 suffix = basename.substr(pos+1, basename.size()-pos-1);
6019 basename = basename.substr(0, pos);
6020 }
6022 //trace("parsename:%s %s %s", path.c_str(),
6023 // basename.c_str(), suffix.c_str());
6024 }
6028 /**
6029 * Generate our internal file list.
6030 */
6031 bool DepTool::createFileList()
6032 {
6034 for (unsigned int i=0 ; i<fileList.size() ; i++)
6035 {
6036 String fileName = fileList[i];
6037 //trace("## FileName:%s", fileName.c_str());
6038 String path;
6039 String basename;
6040 String sfx;
6041 parseName(fileName, path, basename, sfx);
6042 if (sfx == "cpp" || sfx == "c" || sfx == "cxx" ||
6043 sfx == "cc" || sfx == "CC")
6044 {
6045 FileRec *fe = new FileRec(FileRec::CFILE);
6046 fe->path = path;
6047 fe->baseName = basename;
6048 fe->suffix = sfx;
6049 allFiles[fileName] = fe;
6050 }
6051 else if (sfx == "h" || sfx == "hh" ||
6052 sfx == "hpp" || sfx == "hxx")
6053 {
6054 FileRec *fe = new FileRec(FileRec::HFILE);
6055 fe->path = path;
6056 fe->baseName = basename;
6057 fe->suffix = sfx;
6058 allFiles[fileName] = fe;
6059 }
6060 }
6062 if (!listDirectories(sourceDir, "", directories))
6063 return false;
6065 return true;
6066 }
6072 /**
6073 * Get a character from the buffer at pos. If out of range,
6074 * return -1 for safety
6075 */
6076 int DepTool::get(int pos)
6077 {
6078 if (pos>depFileSize)
6079 return -1;
6080 return depFileBuf[pos];
6081 }
6085 /**
6086 * Skip over all whitespace characters beginning at pos. Return
6087 * the position of the first non-whitespace character.
6088 */
6089 int DepTool::skipwhite(int pos)
6090 {
6091 while (pos < depFileSize)
6092 {
6093 int ch = get(pos);
6094 if (ch < 0)
6095 break;
6096 if (!isspace(ch))
6097 break;
6098 pos++;
6099 }
6100 return pos;
6101 }
6104 /**
6105 * Parse the buffer beginning at pos, for a word. Fill
6106 * 'ret' with the result. Return the position after the
6107 * word.
6108 */
6109 int DepTool::getword(int pos, String &ret)
6110 {
6111 while (pos < depFileSize)
6112 {
6113 int ch = get(pos);
6114 if (ch < 0)
6115 break;
6116 if (isspace(ch))
6117 break;
6118 ret.push_back((char)ch);
6119 pos++;
6120 }
6121 return pos;
6122 }
6124 /**
6125 * Return whether the sequence of characters in the buffer
6126 * beginning at pos match the key, for the length of the key
6127 */
6128 bool DepTool::sequ(int pos, const char *key)
6129 {
6130 while (*key)
6131 {
6132 if (*key != get(pos))
6133 return false;
6134 key++; pos++;
6135 }
6136 return true;
6137 }
6141 /**
6142 * Add an include file name to a file record. If the name
6143 * is not found in allFiles explicitly, try prepending include
6144 * directory names to it and try again.
6145 */
6146 bool DepTool::addIncludeFile(FileRec *frec, const String &iname)
6147 {
6148 //# if the name is an exact match to a path name
6149 //# in allFiles, like "myinc.h"
6150 std::map<String, FileRec *>::iterator iter =
6151 allFiles.find(iname);
6152 if (iter != allFiles.end()) //already exists
6153 {
6154 //h file in same dir
6155 FileRec *other = iter->second;
6156 //trace("local: '%s'", iname.c_str());
6157 frec->files[iname] = other;
6158 return true;
6159 }
6160 else
6161 {
6162 //## Ok, it was not found directly
6163 //look in other dirs
6164 std::vector<String>::iterator diter;
6165 for (diter=directories.begin() ;
6166 diter!=directories.end() ; diter++)
6167 {
6168 String dfname = *diter;
6169 dfname.append("/");
6170 dfname.append(iname);
6171 URI fullPathURI(dfname); //normalize path name
6172 String fullPath = fullPathURI.getPath();
6173 if (fullPath[0] == '/')
6174 fullPath = fullPath.substr(1);
6175 //trace("Normalized %s to %s", dfname.c_str(), fullPath.c_str());
6176 iter = allFiles.find(fullPath);
6177 if (iter != allFiles.end())
6178 {
6179 FileRec *other = iter->second;
6180 //trace("other: '%s'", iname.c_str());
6181 frec->files[fullPath] = other;
6182 return true;
6183 }
6184 }
6185 }
6186 return true;
6187 }
6191 /**
6192 * Lightly parse a file to find the #include directives. Do
6193 * a bit of state machine stuff to make sure that the directive
6194 * is valid. (Like not in a comment).
6195 */
6196 bool DepTool::scanFile(const String &fname, FileRec *frec)
6197 {
6198 String fileName;
6199 if (sourceDir.size() > 0)
6200 {
6201 fileName.append(sourceDir);
6202 fileName.append("/");
6203 }
6204 fileName.append(fname);
6205 String nativeName = getNativePath(fileName);
6206 FILE *f = fopen(nativeName.c_str(), "r");
6207 if (!f)
6208 {
6209 error("Could not open '%s' for reading", fname.c_str());
6210 return false;
6211 }
6212 String buf;
6213 while (!feof(f))
6214 {
6215 int nrbytes = fread(readBuf, 1, readBufSize, f);
6216 readBuf[nrbytes] = '\0';
6217 buf.append(readBuf);
6218 }
6219 fclose(f);
6221 depFileSize = buf.size();
6222 depFileBuf = (char *)buf.c_str();
6223 int pos = 0;
6226 while (pos < depFileSize)
6227 {
6228 //trace("p:%c", get(pos));
6230 //# Block comment
6231 if (get(pos) == '/' && get(pos+1) == '*')
6232 {
6233 pos += 2;
6234 while (pos < depFileSize)
6235 {
6236 if (get(pos) == '*' && get(pos+1) == '/')
6237 {
6238 pos += 2;
6239 break;
6240 }
6241 else
6242 pos++;
6243 }
6244 }
6245 //# Line comment
6246 else if (get(pos) == '/' && get(pos+1) == '/')
6247 {
6248 pos += 2;
6249 while (pos < depFileSize)
6250 {
6251 if (get(pos) == '\n')
6252 {
6253 pos++;
6254 break;
6255 }
6256 else
6257 pos++;
6258 }
6259 }
6260 //# #include! yaay
6261 else if (sequ(pos, "#include"))
6262 {
6263 pos += 8;
6264 pos = skipwhite(pos);
6265 String iname;
6266 pos = getword(pos, iname);
6267 if (iname.size()>2)
6268 {
6269 iname = iname.substr(1, iname.size()-2);
6270 addIncludeFile(frec, iname);
6271 }
6272 }
6273 else
6274 {
6275 pos++;
6276 }
6277 }
6279 return true;
6280 }
6284 /**
6285 * Recursively check include lists to find all files in allFiles to which
6286 * a given file is dependent.
6287 */
6288 bool DepTool::processDependency(FileRec *ofile, FileRec *include)
6289 {
6290 std::map<String, FileRec *>::iterator iter;
6291 for (iter=include->files.begin() ; iter!=include->files.end() ; iter++)
6292 {
6293 String fname = iter->first;
6294 if (ofile->files.find(fname) != ofile->files.end())
6295 {
6296 //trace("file '%s' already seen", fname.c_str());
6297 continue;
6298 }
6299 FileRec *child = iter->second;
6300 ofile->files[fname] = child;
6302 processDependency(ofile, child);
6303 }
6306 return true;
6307 }
6313 /**
6314 * Generate the file dependency list.
6315 */
6316 bool DepTool::generateDependencies()
6317 {
6318 std::map<String, FileRec *>::iterator iter;
6319 //# First pass. Scan for all includes
6320 for (iter=allFiles.begin() ; iter!=allFiles.end() ; iter++)
6321 {
6322 FileRec *frec = iter->second;
6323 if (!scanFile(iter->first, frec))
6324 {
6325 //quit?
6326 }
6327 }
6329 //# Second pass. Scan for all includes
6330 for (iter=allFiles.begin() ; iter!=allFiles.end() ; iter++)
6331 {
6332 FileRec *include = iter->second;
6333 if (include->type == FileRec::CFILE)
6334 {
6335 //String cFileName = iter->first;
6336 FileRec *ofile = new FileRec(FileRec::OFILE);
6337 ofile->path = include->path;
6338 ofile->baseName = include->baseName;
6339 ofile->suffix = include->suffix;
6340 String fname = include->path;
6341 if (fname.size()>0)
6342 fname.append("/");
6343 fname.append(include->baseName);
6344 fname.append(".o");
6345 oFiles[fname] = ofile;
6346 //add the .c file first? no, don't
6347 //ofile->files[cFileName] = include;
6349 //trace("ofile:%s", fname.c_str());
6351 processDependency(ofile, include);
6352 }
6353 }
6356 return true;
6357 }
6361 /**
6362 * High-level call to generate deps and optionally save them
6363 */
6364 bool DepTool::generateDependencies(const String &fileName)
6365 {
6366 if (!createFileList())
6367 return false;
6368 if (!generateDependencies())
6369 return false;
6370 if (!saveDepFile(fileName))
6371 return false;
6372 return true;
6373 }
6376 /**
6377 * This saves the dependency cache.
6378 */
6379 bool DepTool::saveDepFile(const String &fileName)
6380 {
6381 time_t tim;
6382 time(&tim);
6384 FILE *f = fopen(fileName.c_str(), "w");
6385 if (!f)
6386 {
6387 trace("cannot open '%s' for writing", fileName.c_str());
6388 }
6389 fprintf(f, "<?xml version='1.0'?>\n");
6390 fprintf(f, "<!--\n");
6391 fprintf(f, "########################################################\n");
6392 fprintf(f, "## File: build.dep\n");
6393 fprintf(f, "## Generated by BuildTool at :%s", ctime(&tim));
6394 fprintf(f, "########################################################\n");
6395 fprintf(f, "-->\n");
6397 fprintf(f, "<dependencies source='%s'>\n\n", sourceDir.c_str());
6398 std::map<String, FileRec *>::iterator iter;
6399 for (iter=oFiles.begin() ; iter!=oFiles.end() ; iter++)
6400 {
6401 FileRec *frec = iter->second;
6402 if (frec->type == FileRec::OFILE)
6403 {
6404 fprintf(f, "<object path='%s' name='%s' suffix='%s'>\n",
6405 frec->path.c_str(), frec->baseName.c_str(), frec->suffix.c_str());
6406 std::map<String, FileRec *>::iterator citer;
6407 for (citer=frec->files.begin() ; citer!=frec->files.end() ; citer++)
6408 {
6409 String cfname = citer->first;
6410 fprintf(f, " <dep name='%s'/>\n", cfname.c_str());
6411 }
6412 fprintf(f, "</object>\n\n");
6413 }
6414 }
6416 fprintf(f, "</dependencies>\n");
6417 fprintf(f, "\n");
6418 fprintf(f, "<!--\n");
6419 fprintf(f, "########################################################\n");
6420 fprintf(f, "## E N D\n");
6421 fprintf(f, "########################################################\n");
6422 fprintf(f, "-->\n");
6424 fclose(f);
6426 return true;
6427 }
6432 /**
6433 * This loads the dependency cache.
6434 */
6435 std::vector<DepRec> DepTool::loadDepFile(const String &depFile)
6436 {
6437 std::vector<DepRec> result;
6439 Parser parser;
6440 Element *root = parser.parseFile(depFile.c_str());
6441 if (!root)
6442 {
6443 //error("Could not open %s for reading", depFile.c_str());
6444 return result;
6445 }
6447 if (root->getChildren().size()==0 ||
6448 root->getChildren()[0]->getName()!="dependencies")
6449 {
6450 error("loadDepFile: main xml element should be <dependencies>");
6451 delete root;
6452 return result;
6453 }
6455 //########## Start parsing
6456 Element *depList = root->getChildren()[0];
6458 std::vector<Element *> objects = depList->getChildren();
6459 for (unsigned int i=0 ; i<objects.size() ; i++)
6460 {
6461 Element *objectElem = objects[i];
6462 String tagName = objectElem->getName();
6463 if (tagName != "object")
6464 {
6465 error("loadDepFile: <dependencies> should have only <object> children");
6466 return result;
6467 }
6469 String objName = objectElem->getAttribute("name");
6470 //trace("object:%s", objName.c_str());
6471 DepRec depObject(objName);
6472 depObject.path = objectElem->getAttribute("path");
6473 depObject.suffix = objectElem->getAttribute("suffix");
6474 //########## DESCRIPTION
6475 std::vector<Element *> depElems = objectElem->getChildren();
6476 for (unsigned int i=0 ; i<depElems.size() ; i++)
6477 {
6478 Element *depElem = depElems[i];
6479 tagName = depElem->getName();
6480 if (tagName != "dep")
6481 {
6482 error("loadDepFile: <object> should have only <dep> children");
6483 return result;
6484 }
6485 String depName = depElem->getAttribute("name");
6486 //trace(" dep:%s", depName.c_str());
6487 depObject.files.push_back(depName);
6488 }
6490 //Insert into the result list, in a sorted manner
6491 bool inserted = false;
6492 std::vector<DepRec>::iterator iter;
6493 for (iter = result.begin() ; iter != result.end() ; iter++)
6494 {
6495 String vpath = iter->path;
6496 vpath.append("/");
6497 vpath.append(iter->name);
6498 String opath = depObject.path;
6499 opath.append("/");
6500 opath.append(depObject.name);
6501 if (vpath > opath)
6502 {
6503 inserted = true;
6504 iter = result.insert(iter, depObject);
6505 break;
6506 }
6507 }
6508 if (!inserted)
6509 result.push_back(depObject);
6510 }
6512 delete root;
6514 return result;
6515 }
6518 /**
6519 * This loads the dependency cache.
6520 */
6521 std::vector<DepRec> DepTool::getDepFile(const String &depFile,
6522 bool forceRefresh)
6523 {
6524 std::vector<DepRec> result;
6525 if (forceRefresh)
6526 {
6527 generateDependencies(depFile);
6528 result = loadDepFile(depFile);
6529 }
6530 else
6531 {
6532 //try once
6533 result = loadDepFile(depFile);
6534 if (result.size() == 0)
6535 {
6536 //fail? try again
6537 generateDependencies(depFile);
6538 result = loadDepFile(depFile);
6539 }
6540 }
6541 return result;
6542 }
6547 //########################################################################
6548 //# T A S K
6549 //########################################################################
6550 //forward decl
6551 class Target;
6552 class Make;
6554 /**
6555 *
6556 */
6557 class Task : public MakeBase
6558 {
6560 public:
6562 typedef enum
6563 {
6564 TASK_NONE,
6565 TASK_CC,
6566 TASK_COPY,
6567 TASK_CXXTEST_PART,
6568 TASK_CXXTEST_ROOT,
6569 TASK_CXXTEST_RUN,
6570 TASK_DELETE,
6571 TASK_ECHO,
6572 TASK_JAR,
6573 TASK_JAVAC,
6574 TASK_LINK,
6575 TASK_MAKEFILE,
6576 TASK_MKDIR,
6577 TASK_MSGFMT,
6578 TASK_PKG_CONFIG,
6579 TASK_RANLIB,
6580 TASK_RC,
6581 TASK_SHAREDLIB,
6582 TASK_STATICLIB,
6583 TASK_STRIP,
6584 TASK_TOUCH,
6585 TASK_TSTAMP
6586 } TaskType;
6589 /**
6590 *
6591 */
6592 Task(MakeBase &par) : parent(par)
6593 { init(); }
6595 /**
6596 *
6597 */
6598 Task(const Task &other) : parent(other.parent)
6599 { init(); assign(other); }
6601 /**
6602 *
6603 */
6604 Task &operator=(const Task &other)
6605 { assign(other); return *this; }
6607 /**
6608 *
6609 */
6610 virtual ~Task()
6611 { }
6614 /**
6615 *
6616 */
6617 virtual MakeBase &getParent()
6618 { return parent; }
6620 /**
6621 *
6622 */
6623 virtual int getType()
6624 { return type; }
6626 /**
6627 *
6628 */
6629 virtual void setType(int val)
6630 { type = val; }
6632 /**
6633 *
6634 */
6635 virtual String getName()
6636 { return name; }
6638 /**
6639 *
6640 */
6641 virtual bool execute()
6642 { return true; }
6644 /**
6645 *
6646 */
6647 virtual bool parse(Element *elem)
6648 { return true; }
6650 /**
6651 *
6652 */
6653 Task *createTask(Element *elem, int lineNr);
6656 protected:
6658 void init()
6659 {
6660 type = TASK_NONE;
6661 name = "none";
6662 }
6664 void assign(const Task &other)
6665 {
6666 type = other.type;
6667 name = other.name;
6668 }
6670 /**
6671 * Show task status
6672 */
6673 void taskstatus(const char *fmt, ...)
6674 {
6675 va_list args;
6676 va_start(args,fmt);
6677 fprintf(stdout, " %s : ", name.c_str());
6678 vfprintf(stdout, fmt, args);
6679 fprintf(stdout, "\n");
6680 va_end(args) ;
6681 }
6683 String getAttribute(Element *elem, const String &attrName)
6684 {
6685 String str;
6686 return str;
6687 }
6689 MakeBase &parent;
6691 int type;
6693 String name;
6694 };
6698 /**
6699 * This task runs the C/C++ compiler. The compiler is invoked
6700 * for all .c or .cpp files which are newer than their correcsponding
6701 * .o files.
6702 */
6703 class TaskCC : public Task
6704 {
6705 public:
6707 TaskCC(MakeBase &par) : Task(par)
6708 {
6709 type = TASK_CC;
6710 name = "cc";
6711 }
6713 virtual ~TaskCC()
6714 {}
6716 virtual bool isExcludedInc(const String &dirname)
6717 {
6718 for (unsigned int i=0 ; i<excludeInc.size() ; i++)
6719 {
6720 String fname = excludeInc[i];
6721 if (fname == dirname)
6722 return true;
6723 }
6724 return false;
6725 }
6727 virtual bool execute()
6728 {
6729 //evaluate our parameters
6730 String command = parent.eval(commandOpt, "gcc");
6731 String ccCommand = parent.eval(ccCommandOpt, "gcc");
6732 String cxxCommand = parent.eval(cxxCommandOpt, "g++");
6733 String source = parent.eval(sourceOpt, ".");
6734 String dest = parent.eval(destOpt, ".");
6735 String flags = parent.eval(flagsOpt, "");
6736 String defines = parent.eval(definesOpt, "");
6737 String includes = parent.eval(includesOpt, "");
6738 bool continueOnError = parent.evalBool(continueOnErrorOpt, true);
6739 bool refreshCache = parent.evalBool(refreshCacheOpt, false);
6741 if (!listFiles(parent, fileSet))
6742 return false;
6744 FILE *f = NULL;
6745 f = fopen("compile.lst", "w");
6747 //refreshCache is probably false here, unless specified otherwise
6748 String fullName = parent.resolve("build.dep");
6749 if (refreshCache || isNewerThan(parent.getURI().getPath(), fullName))
6750 {
6751 taskstatus("regenerating C/C++ dependency cache");
6752 refreshCache = true;
6753 }
6755 DepTool depTool;
6756 depTool.setSourceDirectory(source);
6757 depTool.setFileList(fileSet.getFiles());
6758 std::vector<DepRec> deps =
6759 depTool.getDepFile("build.dep", refreshCache);
6761 String incs;
6762 incs.append("-I");
6763 incs.append(parent.resolve("."));
6764 incs.append(" ");
6765 if (includes.size()>0)
6766 {
6767 incs.append(includes);
6768 incs.append(" ");
6769 }
6770 std::set<String> paths;
6771 std::vector<DepRec>::iterator viter;
6772 for (viter=deps.begin() ; viter!=deps.end() ; viter++)
6773 {
6774 DepRec dep = *viter;
6775 if (dep.path.size()>0)
6776 paths.insert(dep.path);
6777 }
6778 if (source.size()>0)
6779 {
6780 incs.append(" -I");
6781 incs.append(parent.resolve(source));
6782 incs.append(" ");
6783 }
6784 std::set<String>::iterator setIter;
6785 for (setIter=paths.begin() ; setIter!=paths.end() ; setIter++)
6786 {
6787 String dirName = *setIter;
6788 //check excludeInc to see if we dont want to include this dir
6789 if (isExcludedInc(dirName))
6790 continue;
6791 incs.append(" -I");
6792 String dname;
6793 if (source.size()>0)
6794 {
6795 dname.append(source);
6796 dname.append("/");
6797 }
6798 dname.append(dirName);
6799 incs.append(parent.resolve(dname));
6800 }
6802 /**
6803 * Compile each of the C files that need it
6804 */
6805 bool errorOccurred = false;
6806 std::vector<String> cfiles;
6807 for (viter=deps.begin() ; viter!=deps.end() ; viter++)
6808 {
6809 DepRec dep = *viter;
6811 //## Select command
6812 String sfx = dep.suffix;
6813 String command = ccCommand;
6814 if (sfx == "cpp" || sfx == "cxx" || sfx == "c++" ||
6815 sfx == "cc" || sfx == "CC")
6816 command = cxxCommand;
6818 //## Make paths
6819 String destPath = dest;
6820 String srcPath = source;
6821 if (dep.path.size()>0)
6822 {
6823 destPath.append("/");
6824 destPath.append(dep.path);
6825 srcPath.append("/");
6826 srcPath.append(dep.path);
6827 }
6828 //## Make sure destination directory exists
6829 if (!createDirectory(destPath))
6830 return false;
6832 //## Check whether it needs to be done
6833 String destName;
6834 if (destPath.size()>0)
6835 {
6836 destName.append(destPath);
6837 destName.append("/");
6838 }
6839 destName.append(dep.name);
6840 destName.append(".o");
6841 String destFullName = parent.resolve(destName);
6842 String srcName;
6843 if (srcPath.size()>0)
6844 {
6845 srcName.append(srcPath);
6846 srcName.append("/");
6847 }
6848 srcName.append(dep.name);
6849 srcName.append(".");
6850 srcName.append(dep.suffix);
6851 String srcFullName = parent.resolve(srcName);
6852 bool compileMe = false;
6853 //# First we check if the source is newer than the .o
6854 if (isNewerThan(srcFullName, destFullName))
6855 {
6856 taskstatus("compile of %s required by source: %s",
6857 destFullName.c_str(), srcFullName.c_str());
6858 compileMe = true;
6859 }
6860 else
6861 {
6862 //# secondly, we check if any of the included dependencies
6863 //# of the .c/.cpp is newer than the .o
6864 for (unsigned int i=0 ; i<dep.files.size() ; i++)
6865 {
6866 String depName;
6867 if (source.size()>0)
6868 {
6869 depName.append(source);
6870 depName.append("/");
6871 }
6872 depName.append(dep.files[i]);
6873 String depFullName = parent.resolve(depName);
6874 bool depRequires = isNewerThan(depFullName, destFullName);
6875 //trace("%d %s %s\n", depRequires,
6876 // destFullName.c_str(), depFullName.c_str());
6877 if (depRequires)
6878 {
6879 taskstatus("compile of %s required by included: %s",
6880 destFullName.c_str(), depFullName.c_str());
6881 compileMe = true;
6882 break;
6883 }
6884 }
6885 }
6886 if (!compileMe)
6887 {
6888 continue;
6889 }
6891 //## Assemble the command
6892 String cmd = command;
6893 cmd.append(" -c ");
6894 cmd.append(flags);
6895 cmd.append(" ");
6896 cmd.append(defines);
6897 cmd.append(" ");
6898 cmd.append(incs);
6899 cmd.append(" ");
6900 cmd.append(srcFullName);
6901 cmd.append(" -o ");
6902 cmd.append(destFullName);
6904 //## Execute the command
6906 String outString, errString;
6907 bool ret = executeCommand(cmd.c_str(), "", outString, errString);
6909 if (f)
6910 {
6911 fprintf(f, "########################### File : %s\n",
6912 srcFullName.c_str());
6913 fprintf(f, "#### COMMAND ###\n");
6914 int col = 0;
6915 for (unsigned int i = 0 ; i < cmd.size() ; i++)
6916 {
6917 char ch = cmd[i];
6918 if (isspace(ch) && col > 63)
6919 {
6920 fputc('\n', f);
6921 col = 0;
6922 }
6923 else
6924 {
6925 fputc(ch, f);
6926 col++;
6927 }
6928 if (col > 76)
6929 {
6930 fputc('\n', f);
6931 col = 0;
6932 }
6933 }
6934 fprintf(f, "\n");
6935 fprintf(f, "#### STDOUT ###\n%s\n", outString.c_str());
6936 fprintf(f, "#### STDERR ###\n%s\n\n", errString.c_str());
6937 fflush(f);
6938 }
6939 if (!ret)
6940 {
6941 error("problem compiling: %s", errString.c_str());
6942 errorOccurred = true;
6943 }
6944 if (errorOccurred && !continueOnError)
6945 break;
6947 removeFromStatCache(getNativePath(destFullName));
6948 }
6950 if (f)
6951 {
6952 fclose(f);
6953 }
6955 return !errorOccurred;
6956 }
6959 virtual bool parse(Element *elem)
6960 {
6961 String s;
6962 if (!parent.getAttribute(elem, "command", commandOpt))
6963 return false;
6964 if (commandOpt.size()>0)
6965 { cxxCommandOpt = ccCommandOpt = commandOpt; }
6966 if (!parent.getAttribute(elem, "cc", ccCommandOpt))
6967 return false;
6968 if (!parent.getAttribute(elem, "cxx", cxxCommandOpt))
6969 return false;
6970 if (!parent.getAttribute(elem, "destdir", destOpt))
6971 return false;
6972 if (!parent.getAttribute(elem, "continueOnError", continueOnErrorOpt))
6973 return false;
6974 if (!parent.getAttribute(elem, "refreshCache", refreshCacheOpt))
6975 return false;
6977 std::vector<Element *> children = elem->getChildren();
6978 for (unsigned int i=0 ; i<children.size() ; i++)
6979 {
6980 Element *child = children[i];
6981 String tagName = child->getName();
6982 if (tagName == "flags")
6983 {
6984 if (!parent.getValue(child, flagsOpt))
6985 return false;
6986 flagsOpt = strip(flagsOpt);
6987 }
6988 else if (tagName == "includes")
6989 {
6990 if (!parent.getValue(child, includesOpt))
6991 return false;
6992 includesOpt = strip(includesOpt);
6993 }
6994 else if (tagName == "defines")
6995 {
6996 if (!parent.getValue(child, definesOpt))
6997 return false;
6998 definesOpt = strip(definesOpt);
6999 }
7000 else if (tagName == "fileset")
7001 {
7002 if (!parseFileSet(child, parent, fileSet))
7003 return false;
7004 sourceOpt = fileSet.getDirectory();
7005 }
7006 else if (tagName == "excludeinc")
7007 {
7008 if (!parseFileList(child, parent, excludeInc))
7009 return false;
7010 }
7011 }
7013 return true;
7014 }
7016 protected:
7018 String commandOpt;
7019 String ccCommandOpt;
7020 String cxxCommandOpt;
7021 String sourceOpt;
7022 String destOpt;
7023 String flagsOpt;
7024 String definesOpt;
7025 String includesOpt;
7026 String continueOnErrorOpt;
7027 String refreshCacheOpt;
7028 FileSet fileSet;
7029 FileList excludeInc;
7031 };
7035 /**
7036 *
7037 */
7038 class TaskCopy : public Task
7039 {
7040 public:
7042 typedef enum
7043 {
7044 CP_NONE,
7045 CP_TOFILE,
7046 CP_TODIR
7047 } CopyType;
7049 TaskCopy(MakeBase &par) : Task(par)
7050 {
7051 type = TASK_COPY;
7052 name = "copy";
7053 cptype = CP_NONE;
7054 haveFileSet = false;
7055 }
7057 virtual ~TaskCopy()
7058 {}
7060 virtual bool execute()
7061 {
7062 String fileName = parent.eval(fileNameOpt , ".");
7063 String toFileName = parent.eval(toFileNameOpt , ".");
7064 String toDirName = parent.eval(toDirNameOpt , ".");
7065 bool verbose = parent.evalBool(verboseOpt, false);
7066 switch (cptype)
7067 {
7068 case CP_TOFILE:
7069 {
7070 if (fileName.size()>0)
7071 {
7072 taskstatus("%s to %s",
7073 fileName.c_str(), toFileName.c_str());
7074 String fullSource = parent.resolve(fileName);
7075 String fullDest = parent.resolve(toFileName);
7076 if (verbose)
7077 taskstatus("copy %s to file %s", fullSource.c_str(),
7078 fullDest.c_str());
7079 if (!isRegularFile(fullSource))
7080 {
7081 error("copy : file %s does not exist", fullSource.c_str());
7082 return false;
7083 }
7084 if (!isNewerThan(fullSource, fullDest))
7085 {
7086 taskstatus("skipped");
7087 return true;
7088 }
7089 if (!copyFile(fullSource, fullDest))
7090 return false;
7091 taskstatus("1 file copied");
7092 }
7093 return true;
7094 }
7095 case CP_TODIR:
7096 {
7097 if (haveFileSet)
7098 {
7099 if (!listFiles(parent, fileSet))
7100 return false;
7101 String fileSetDir = parent.eval(fileSet.getDirectory(), ".");
7103 taskstatus("%s to %s",
7104 fileSetDir.c_str(), toDirName.c_str());
7106 int nrFiles = 0;
7107 for (unsigned int i=0 ; i<fileSet.size() ; i++)
7108 {
7109 String fileName = fileSet[i];
7111 String sourcePath;
7112 if (fileSetDir.size()>0)
7113 {
7114 sourcePath.append(fileSetDir);
7115 sourcePath.append("/");
7116 }
7117 sourcePath.append(fileName);
7118 String fullSource = parent.resolve(sourcePath);
7120 //Get the immediate parent directory's base name
7121 String baseFileSetDir = fileSetDir;
7122 unsigned int pos = baseFileSetDir.find_last_of('/');
7123 if (pos!=baseFileSetDir.npos &&
7124 pos < baseFileSetDir.size()-1)
7125 baseFileSetDir =
7126 baseFileSetDir.substr(pos+1,
7127 baseFileSetDir.size());
7128 //Now make the new path
7129 String destPath;
7130 if (toDirName.size()>0)
7131 {
7132 destPath.append(toDirName);
7133 destPath.append("/");
7134 }
7135 if (baseFileSetDir.size()>0)
7136 {
7137 destPath.append(baseFileSetDir);
7138 destPath.append("/");
7139 }
7140 destPath.append(fileName);
7141 String fullDest = parent.resolve(destPath);
7142 //trace("fileName:%s", fileName.c_str());
7143 if (verbose)
7144 taskstatus("copy %s to new dir : %s",
7145 fullSource.c_str(), fullDest.c_str());
7146 if (!isNewerThan(fullSource, fullDest))
7147 {
7148 if (verbose)
7149 taskstatus("copy skipping %s", fullSource.c_str());
7150 continue;
7151 }
7152 if (!copyFile(fullSource, fullDest))
7153 return false;
7154 nrFiles++;
7155 }
7156 taskstatus("%d file(s) copied", nrFiles);
7157 }
7158 else //file source
7159 {
7160 //For file->dir we want only the basename of
7161 //the source appended to the dest dir
7162 taskstatus("%s to %s",
7163 fileName.c_str(), toDirName.c_str());
7164 String baseName = fileName;
7165 unsigned int pos = baseName.find_last_of('/');
7166 if (pos!=baseName.npos && pos<baseName.size()-1)
7167 baseName = baseName.substr(pos+1, baseName.size());
7168 String fullSource = parent.resolve(fileName);
7169 String destPath;
7170 if (toDirName.size()>0)
7171 {
7172 destPath.append(toDirName);
7173 destPath.append("/");
7174 }
7175 destPath.append(baseName);
7176 String fullDest = parent.resolve(destPath);
7177 if (verbose)
7178 taskstatus("file %s to new dir : %s", fullSource.c_str(),
7179 fullDest.c_str());
7180 if (!isRegularFile(fullSource))
7181 {
7182 error("copy : file %s does not exist", fullSource.c_str());
7183 return false;
7184 }
7185 if (!isNewerThan(fullSource, fullDest))
7186 {
7187 taskstatus("skipped");
7188 return true;
7189 }
7190 if (!copyFile(fullSource, fullDest))
7191 return false;
7192 taskstatus("1 file copied");
7193 }
7194 return true;
7195 }
7196 }
7197 return true;
7198 }
7201 virtual bool parse(Element *elem)
7202 {
7203 if (!parent.getAttribute(elem, "file", fileNameOpt))
7204 return false;
7205 if (!parent.getAttribute(elem, "tofile", toFileNameOpt))
7206 return false;
7207 if (toFileNameOpt.size() > 0)
7208 cptype = CP_TOFILE;
7209 if (!parent.getAttribute(elem, "todir", toDirNameOpt))
7210 return false;
7211 if (toDirNameOpt.size() > 0)
7212 cptype = CP_TODIR;
7213 if (!parent.getAttribute(elem, "verbose", verboseOpt))
7214 return false;
7216 haveFileSet = false;
7218 std::vector<Element *> children = elem->getChildren();
7219 for (unsigned int i=0 ; i<children.size() ; i++)
7220 {
7221 Element *child = children[i];
7222 String tagName = child->getName();
7223 if (tagName == "fileset")
7224 {
7225 if (!parseFileSet(child, parent, fileSet))
7226 {
7227 error("problem getting fileset");
7228 return false;
7229 }
7230 haveFileSet = true;
7231 }
7232 }
7234 //Perform validity checks
7235 if (fileNameOpt.size()>0 && fileSet.size()>0)
7236 {
7237 error("<copy> can only have one of : file= and <fileset>");
7238 return false;
7239 }
7240 if (toFileNameOpt.size()>0 && toDirNameOpt.size()>0)
7241 {
7242 error("<copy> can only have one of : tofile= or todir=");
7243 return false;
7244 }
7245 if (haveFileSet && toDirNameOpt.size()==0)
7246 {
7247 error("a <copy> task with a <fileset> must have : todir=");
7248 return false;
7249 }
7250 if (cptype == CP_TOFILE && fileNameOpt.size()==0)
7251 {
7252 error("<copy> tofile= must be associated with : file=");
7253 return false;
7254 }
7255 if (cptype == CP_TODIR && fileNameOpt.size()==0 && !haveFileSet)
7256 {
7257 error("<copy> todir= must be associated with : file= or <fileset>");
7258 return false;
7259 }
7261 return true;
7262 }
7264 private:
7266 int cptype;
7267 bool haveFileSet;
7269 FileSet fileSet;
7270 String fileNameOpt;
7271 String toFileNameOpt;
7272 String toDirNameOpt;
7273 String verboseOpt;
7274 };
7277 /**
7278 * Generate CxxTest files
7279 */
7280 class TaskCxxTestPart: public Task
7281 {
7282 public:
7284 TaskCxxTestPart(MakeBase &par) : Task(par)
7285 {
7286 type = TASK_CXXTEST_PART;
7287 name = "cxxtestpart";
7288 }
7290 virtual ~TaskCxxTestPart()
7291 {}
7293 virtual bool execute()
7294 {
7295 if (!listFiles(parent, fileSet))
7296 return false;
7297 String fileSetDir = parent.eval(fileSet.getDirectory(), ".");
7299 String fullDest = parent.resolve(parent.eval(destPathOpt, "."));
7300 String cmd = parent.eval(commandOpt, "cxxtestgen.py");
7301 cmd.append(" --part -o ");
7302 cmd.append(fullDest);
7304 unsigned int newFiles = 0;
7305 for (unsigned int i=0 ; i<fileSet.size() ; i++)
7306 {
7307 String fileName = fileSet[i];
7308 if (getSuffix(fileName) != "h")
7309 continue;
7310 String sourcePath;
7311 if (fileSetDir.size()>0)
7312 {
7313 sourcePath.append(fileSetDir);
7314 sourcePath.append("/");
7315 }
7316 sourcePath.append(fileName);
7317 String fullSource = parent.resolve(sourcePath);
7319 cmd.append(" ");
7320 cmd.append(fullSource);
7321 if (isNewerThan(fullSource, fullDest)) newFiles++;
7322 }
7324 if (newFiles>0) {
7325 size_t const lastSlash = fullDest.find_last_of('/');
7326 if (lastSlash != fullDest.npos) {
7327 String directory(fullDest, 0, lastSlash);
7328 if (!createDirectory(directory))
7329 return false;
7330 }
7332 String outString, errString;
7333 if (!executeCommand(cmd.c_str(), "", outString, errString))
7334 {
7335 error("<cxxtestpart> problem: %s", errString.c_str());
7336 return false;
7337 }
7338 removeFromStatCache(getNativePath(fullDest));
7339 }
7341 return true;
7342 }
7344 virtual bool parse(Element *elem)
7345 {
7346 if (!parent.getAttribute(elem, "command", commandOpt))
7347 return false;
7348 if (!parent.getAttribute(elem, "out", destPathOpt))
7349 return false;
7351 std::vector<Element *> children = elem->getChildren();
7352 for (unsigned int i=0 ; i<children.size() ; i++)
7353 {
7354 Element *child = children[i];
7355 String tagName = child->getName();
7356 if (tagName == "fileset")
7357 {
7358 if (!parseFileSet(child, parent, fileSet))
7359 return false;
7360 }
7361 }
7362 return true;
7363 }
7365 private:
7367 String commandOpt;
7368 String destPathOpt;
7369 FileSet fileSet;
7371 };
7374 /**
7375 * Generate the CxxTest root file
7376 */
7377 class TaskCxxTestRoot: public Task
7378 {
7379 public:
7381 TaskCxxTestRoot(MakeBase &par) : Task(par)
7382 {
7383 type = TASK_CXXTEST_ROOT;
7384 name = "cxxtestroot";
7385 }
7387 virtual ~TaskCxxTestRoot()
7388 {}
7390 virtual bool execute()
7391 {
7392 if (!listFiles(parent, fileSet))
7393 return false;
7394 String fileSetDir = parent.eval(fileSet.getDirectory(), ".");
7395 unsigned int newFiles = 0;
7397 String fullDest = parent.resolve(parent.eval(destPathOpt, "."));
7398 String cmd = parent.eval(commandOpt, "cxxtestgen.py");
7399 cmd.append(" --root -o ");
7400 cmd.append(fullDest);
7401 String templateFile = parent.eval(templateFileOpt, "");
7402 if (templateFile.size()>0) {
7403 String fullTemplate = parent.resolve(templateFile);
7404 cmd.append(" --template=");
7405 cmd.append(fullTemplate);
7406 if (isNewerThan(fullTemplate, fullDest)) newFiles++;
7407 }
7409 for (unsigned int i=0 ; i<fileSet.size() ; i++)
7410 {
7411 String fileName = fileSet[i];
7412 if (getSuffix(fileName) != "h")
7413 continue;
7414 String sourcePath;
7415 if (fileSetDir.size()>0)
7416 {
7417 sourcePath.append(fileSetDir);
7418 sourcePath.append("/");
7419 }
7420 sourcePath.append(fileName);
7421 String fullSource = parent.resolve(sourcePath);
7423 cmd.append(" ");
7424 cmd.append(fullSource);
7425 if (isNewerThan(fullSource, fullDest)) newFiles++;
7426 }
7428 if (newFiles>0) {
7429 size_t const lastSlash = fullDest.find_last_of('/');
7430 if (lastSlash != fullDest.npos) {
7431 String directory(fullDest, 0, lastSlash);
7432 if (!createDirectory(directory))
7433 return false;
7434 }
7436 String outString, errString;
7437 if (!executeCommand(cmd.c_str(), "", outString, errString))
7438 {
7439 error("<cxxtestroot> problem: %s", errString.c_str());
7440 return false;
7441 }
7442 removeFromStatCache(getNativePath(fullDest));
7443 }
7445 return true;
7446 }
7448 virtual bool parse(Element *elem)
7449 {
7450 if (!parent.getAttribute(elem, "command", commandOpt))
7451 return false;
7452 if (!parent.getAttribute(elem, "template", templateFileOpt))
7453 return false;
7454 if (!parent.getAttribute(elem, "out", destPathOpt))
7455 return false;
7457 std::vector<Element *> children = elem->getChildren();
7458 for (unsigned int i=0 ; i<children.size() ; i++)
7459 {
7460 Element *child = children[i];
7461 String tagName = child->getName();
7462 if (tagName == "fileset")
7463 {
7464 if (!parseFileSet(child, parent, fileSet))
7465 return false;
7466 }
7467 }
7468 return true;
7469 }
7471 private:
7473 String commandOpt;
7474 String templateFileOpt;
7475 String destPathOpt;
7476 FileSet fileSet;
7478 };
7481 /**
7482 * Execute the CxxTest test executable
7483 */
7484 class TaskCxxTestRun: public Task
7485 {
7486 public:
7488 TaskCxxTestRun(MakeBase &par) : Task(par)
7489 {
7490 type = TASK_CXXTEST_RUN;
7491 name = "cxxtestrun";
7492 }
7494 virtual ~TaskCxxTestRun()
7495 {}
7497 virtual bool execute()
7498 {
7499 unsigned int newFiles = 0;
7501 String workingDir = parent.resolve(parent.eval(workingDirOpt, "inkscape"));
7502 String rawCmd = parent.eval(commandOpt, "build/cxxtests");
7504 String cmdExe;
7505 if (fileExists(rawCmd)) {
7506 cmdExe = rawCmd;
7507 } else if (fileExists(rawCmd + ".exe")) {
7508 cmdExe = rawCmd + ".exe";
7509 } else {
7510 error("<cxxtestrun> problem: cxxtests executable not found! (command=\"%s\")", rawCmd.c_str());
7511 }
7512 // Note that the log file names are based on the exact name used to call cxxtests (it uses argv[0] + ".log"/".xml")
7513 if (isNewerThan(cmdExe, rawCmd + ".log") || isNewerThan(cmdExe, rawCmd + ".xml")) newFiles++;
7515 // Prepend the necessary ../'s
7516 String cmd = rawCmd;
7517 unsigned int workingDirDepth = 0;
7518 bool wasSlash = true;
7519 for(size_t i=0; i<workingDir.size(); i++) {
7520 // This assumes no . and .. parts
7521 if (wasSlash && workingDir[i]!='/') workingDirDepth++;
7522 wasSlash = workingDir[i] == '/';
7523 }
7524 for(size_t i=0; i<workingDirDepth; i++) {
7525 cmd = "../" + cmd;
7526 }
7528 if (newFiles>0) {
7529 char olddir[1024];
7530 if (workingDir.size()>0) {
7531 // TODO: Double-check usage of getcwd and handle chdir errors
7532 getcwd(olddir, 1024);
7533 chdir(workingDir.c_str());
7534 }
7536 String outString;
7537 if (!executeCommand(cmd.c_str(), "", outString, outString))
7538 {
7539 error("<cxxtestrun> problem: %s", outString.c_str());
7540 return false;
7541 }
7543 if (workingDir.size()>0) {
7544 // TODO: Handle errors?
7545 chdir(olddir);
7546 }
7548 removeFromStatCache(getNativePath(cmd + ".log"));
7549 removeFromStatCache(getNativePath(cmd + ".xml"));
7550 }
7552 return true;
7553 }
7555 virtual bool parse(Element *elem)
7556 {
7557 if (!parent.getAttribute(elem, "command", commandOpt))
7558 return false;
7559 if (!parent.getAttribute(elem, "workingdir", workingDirOpt))
7560 return false;
7561 return true;
7562 }
7564 private:
7566 String commandOpt;
7567 String workingDirOpt;
7569 };
7572 /**
7573 *
7574 */
7575 class TaskDelete : public Task
7576 {
7577 public:
7579 typedef enum
7580 {
7581 DEL_FILE,
7582 DEL_DIR,
7583 DEL_FILESET
7584 } DeleteType;
7586 TaskDelete(MakeBase &par) : Task(par)
7587 {
7588 type = TASK_DELETE;
7589 name = "delete";
7590 delType = DEL_FILE;
7591 }
7593 virtual ~TaskDelete()
7594 {}
7596 virtual bool execute()
7597 {
7598 String dirName = parent.eval(dirNameOpt, ".");
7599 String fileName = parent.eval(fileNameOpt, ".");
7600 bool verbose = parent.evalBool(verboseOpt, false);
7601 bool quiet = parent.evalBool(quietOpt, false);
7602 bool failOnError = parent.evalBool(failOnErrorOpt, true);
7603 switch (delType)
7604 {
7605 case DEL_FILE:
7606 {
7607 taskstatus("file: %s", fileName.c_str());
7608 String fullName = parent.resolve(fileName);
7609 char *fname = (char *)fullName.c_str();
7610 if (!quiet && verbose)
7611 taskstatus("path: %s", fname);
7612 if (failOnError && !removeFile(fullName))
7613 {
7614 //error("Could not delete file '%s'", fullName.c_str());
7615 return false;
7616 }
7617 return true;
7618 }
7619 case DEL_DIR:
7620 {
7621 taskstatus("dir: %s", dirName.c_str());
7622 String fullDir = parent.resolve(dirName);
7623 if (!quiet && verbose)
7624 taskstatus("path: %s", fullDir.c_str());
7625 if (failOnError && !removeDirectory(fullDir))
7626 {
7627 //error("Could not delete directory '%s'", fullDir.c_str());
7628 return false;
7629 }
7630 return true;
7631 }
7632 }
7633 return true;
7634 }
7636 virtual bool parse(Element *elem)
7637 {
7638 if (!parent.getAttribute(elem, "file", fileNameOpt))
7639 return false;
7640 if (fileNameOpt.size() > 0)
7641 delType = DEL_FILE;
7642 if (!parent.getAttribute(elem, "dir", dirNameOpt))
7643 return false;
7644 if (dirNameOpt.size() > 0)
7645 delType = DEL_DIR;
7646 if (fileNameOpt.size()>0 && dirNameOpt.size()>0)
7647 {
7648 error("<delete> can have one attribute of file= or dir=");
7649 return false;
7650 }
7651 if (fileNameOpt.size()==0 && dirNameOpt.size()==0)
7652 {
7653 error("<delete> must have one attribute of file= or dir=");
7654 return false;
7655 }
7656 if (!parent.getAttribute(elem, "verbose", verboseOpt))
7657 return false;
7658 if (!parent.getAttribute(elem, "quiet", quietOpt))
7659 return false;
7660 if (!parent.getAttribute(elem, "failonerror", failOnErrorOpt))
7661 return false;
7662 return true;
7663 }
7665 private:
7667 int delType;
7668 String dirNameOpt;
7669 String fileNameOpt;
7670 String verboseOpt;
7671 String quietOpt;
7672 String failOnErrorOpt;
7673 };
7676 /**
7677 * Send a message to stdout
7678 */
7679 class TaskEcho : public Task
7680 {
7681 public:
7683 TaskEcho(MakeBase &par) : Task(par)
7684 { type = TASK_ECHO; name = "echo"; }
7686 virtual ~TaskEcho()
7687 {}
7689 virtual bool execute()
7690 {
7691 //let message have priority over text
7692 String message = parent.eval(messageOpt, "");
7693 String text = parent.eval(textOpt, "");
7694 if (message.size() > 0)
7695 {
7696 fprintf(stdout, "%s\n", message.c_str());
7697 }
7698 else if (text.size() > 0)
7699 {
7700 fprintf(stdout, "%s\n", text.c_str());
7701 }
7702 return true;
7703 }
7705 virtual bool parse(Element *elem)
7706 {
7707 if (!parent.getValue(elem, textOpt))
7708 return false;
7709 textOpt = leftJustify(textOpt);
7710 if (!parent.getAttribute(elem, "message", messageOpt))
7711 return false;
7712 return true;
7713 }
7715 private:
7717 String messageOpt;
7718 String textOpt;
7719 };
7723 /**
7724 *
7725 */
7726 class TaskJar : public Task
7727 {
7728 public:
7730 TaskJar(MakeBase &par) : Task(par)
7731 { type = TASK_JAR; name = "jar"; }
7733 virtual ~TaskJar()
7734 {}
7736 virtual bool execute()
7737 {
7738 String command = parent.eval(commandOpt, "jar");
7739 String basedir = parent.eval(basedirOpt, ".");
7740 String destfile = parent.eval(destfileOpt, ".");
7742 String cmd = command;
7743 cmd.append(" -cf ");
7744 cmd.append(destfile);
7745 cmd.append(" -C ");
7746 cmd.append(basedir);
7747 cmd.append(" .");
7749 String execCmd = cmd;
7751 String outString, errString;
7752 bool ret = executeCommand(execCmd.c_str(), "", outString, errString);
7753 if (!ret)
7754 {
7755 error("<jar> command '%s' failed :\n %s",
7756 execCmd.c_str(), errString.c_str());
7757 return false;
7758 }
7759 removeFromStatCache(getNativePath(destfile));
7760 return true;
7761 }
7763 virtual bool parse(Element *elem)
7764 {
7765 if (!parent.getAttribute(elem, "command", commandOpt))
7766 return false;
7767 if (!parent.getAttribute(elem, "basedir", basedirOpt))
7768 return false;
7769 if (!parent.getAttribute(elem, "destfile", destfileOpt))
7770 return false;
7771 if (basedirOpt.size() == 0 || destfileOpt.size() == 0)
7772 {
7773 error("<jar> required both basedir and destfile attributes to be set");
7774 return false;
7775 }
7776 return true;
7777 }
7779 private:
7781 String commandOpt;
7782 String basedirOpt;
7783 String destfileOpt;
7784 };
7787 /**
7788 *
7789 */
7790 class TaskJavac : public Task
7791 {
7792 public:
7794 TaskJavac(MakeBase &par) : Task(par)
7795 {
7796 type = TASK_JAVAC; name = "javac";
7797 }
7799 virtual ~TaskJavac()
7800 {}
7802 virtual bool execute()
7803 {
7804 String command = parent.eval(commandOpt, "javac");
7805 String srcdir = parent.eval(srcdirOpt, ".");
7806 String destdir = parent.eval(destdirOpt, ".");
7807 String target = parent.eval(targetOpt, "");
7809 std::vector<String> fileList;
7810 if (!listFiles(srcdir, "", fileList))
7811 {
7812 return false;
7813 }
7814 String cmd = command;
7815 cmd.append(" -d ");
7816 cmd.append(destdir);
7817 cmd.append(" -classpath ");
7818 cmd.append(destdir);
7819 cmd.append(" -sourcepath ");
7820 cmd.append(srcdir);
7821 cmd.append(" ");
7822 if (target.size()>0)
7823 {
7824 cmd.append(" -target ");
7825 cmd.append(target);
7826 cmd.append(" ");
7827 }
7828 String fname = "javalist.btool";
7829 FILE *f = fopen(fname.c_str(), "w");
7830 int count = 0;
7831 for (unsigned int i=0 ; i<fileList.size() ; i++)
7832 {
7833 String fname = fileList[i];
7834 String srcName = fname;
7835 if (fname.size()<6) //x.java
7836 continue;
7837 if (fname.compare(fname.size()-5, 5, ".java") != 0)
7838 continue;
7839 String baseName = fname.substr(0, fname.size()-5);
7840 String destName = baseName;
7841 destName.append(".class");
7843 String fullSrc = srcdir;
7844 fullSrc.append("/");
7845 fullSrc.append(fname);
7846 String fullDest = destdir;
7847 fullDest.append("/");
7848 fullDest.append(destName);
7849 //trace("fullsrc:%s fulldest:%s", fullSrc.c_str(), fullDest.c_str());
7850 if (!isNewerThan(fullSrc, fullDest))
7851 continue;
7853 count++;
7854 fprintf(f, "%s\n", fullSrc.c_str());
7855 }
7856 fclose(f);
7857 if (!count)
7858 {
7859 taskstatus("nothing to do");
7860 return true;
7861 }
7863 taskstatus("compiling %d files", count);
7865 String execCmd = cmd;
7866 execCmd.append("@");
7867 execCmd.append(fname);
7869 String outString, errString;
7870 bool ret = executeCommand(execCmd.c_str(), "", outString, errString);
7871 if (!ret)
7872 {
7873 error("<javac> command '%s' failed :\n %s",
7874 execCmd.c_str(), errString.c_str());
7875 return false;
7876 }
7877 // TODO:
7878 //removeFromStatCache(getNativePath(........));
7879 return true;
7880 }
7882 virtual bool parse(Element *elem)
7883 {
7884 if (!parent.getAttribute(elem, "command", commandOpt))
7885 return false;
7886 if (!parent.getAttribute(elem, "srcdir", srcdirOpt))
7887 return false;
7888 if (!parent.getAttribute(elem, "destdir", destdirOpt))
7889 return false;
7890 if (srcdirOpt.size() == 0 || destdirOpt.size() == 0)
7891 {
7892 error("<javac> required both srcdir and destdir attributes to be set");
7893 return false;
7894 }
7895 if (!parent.getAttribute(elem, "target", targetOpt))
7896 return false;
7897 return true;
7898 }
7900 private:
7902 String commandOpt;
7903 String srcdirOpt;
7904 String destdirOpt;
7905 String targetOpt;
7907 };
7910 /**
7911 *
7912 */
7913 class TaskLink : public Task
7914 {
7915 public:
7917 TaskLink(MakeBase &par) : Task(par)
7918 {
7919 type = TASK_LINK; name = "link";
7920 }
7922 virtual ~TaskLink()
7923 {}
7925 virtual bool execute()
7926 {
7927 String command = parent.eval(commandOpt, "g++");
7928 String fileName = parent.eval(fileNameOpt, "");
7929 String flags = parent.eval(flagsOpt, "");
7930 String libs = parent.eval(libsOpt, "");
7931 bool doStrip = parent.evalBool(doStripOpt, false);
7932 String symFileName = parent.eval(symFileNameOpt, "");
7933 String stripCommand = parent.eval(stripCommandOpt, "strip");
7934 String objcopyCommand = parent.eval(objcopyCommandOpt, "objcopy");
7936 if (!listFiles(parent, fileSet))
7937 return false;
7938 String fileSetDir = parent.eval(fileSet.getDirectory(), ".");
7939 //trace("%d files in %s", fileSet.size(), fileSetDir.c_str());
7940 bool doit = false;
7941 String fullTarget = parent.resolve(fileName);
7942 String cmd = command;
7943 cmd.append(" -o ");
7944 cmd.append(fullTarget);
7945 cmd.append(" ");
7946 cmd.append(flags);
7947 for (unsigned int i=0 ; i<fileSet.size() ; i++)
7948 {
7949 cmd.append(" ");
7950 String obj;
7951 if (fileSetDir.size()>0)
7952 {
7953 obj.append(fileSetDir);
7954 obj.append("/");
7955 }
7956 obj.append(fileSet[i]);
7957 String fullObj = parent.resolve(obj);
7958 String nativeFullObj = getNativePath(fullObj);
7959 cmd.append(nativeFullObj);
7960 //trace("link: tgt:%s obj:%s", fullTarget.c_str(),
7961 // fullObj.c_str());
7962 if (isNewerThan(fullObj, fullTarget))
7963 doit = true;
7964 }
7965 cmd.append(" ");
7966 cmd.append(libs);
7967 if (!doit)
7968 {
7969 //trace("link not needed");
7970 return true;
7971 }
7972 //trace("LINK cmd:%s", cmd.c_str());
7975 String outbuf, errbuf;
7976 if (!executeCommand(cmd.c_str(), "", outbuf, errbuf))
7977 {
7978 error("LINK problem: %s", errbuf.c_str());
7979 return false;
7980 }
7981 removeFromStatCache(getNativePath(fullTarget));
7983 if (symFileName.size()>0)
7984 {
7985 String symFullName = parent.resolve(symFileName);
7986 cmd = objcopyCommand;
7987 cmd.append(" --only-keep-debug ");
7988 cmd.append(getNativePath(fullTarget));
7989 cmd.append(" ");
7990 cmd.append(getNativePath(symFullName));
7991 if (!executeCommand(cmd, "", outbuf, errbuf))
7992 {
7993 error("<strip> symbol file failed : %s", errbuf.c_str());
7994 return false;
7995 }
7996 removeFromStatCache(getNativePath(symFullName));
7997 }
7999 if (doStrip)
8000 {
8001 cmd = stripCommand;
8002 cmd.append(" ");
8003 cmd.append(getNativePath(fullTarget));
8004 if (!executeCommand(cmd, "", outbuf, errbuf))
8005 {
8006 error("<strip> failed : %s", errbuf.c_str());
8007 return false;
8008 }
8009 removeFromStatCache(getNativePath(fullTarget));
8010 }
8012 return true;
8013 }
8015 virtual bool parse(Element *elem)
8016 {
8017 if (!parent.getAttribute(elem, "command", commandOpt))
8018 return false;
8019 if (!parent.getAttribute(elem, "objcopycommand", objcopyCommandOpt))
8020 return false;
8021 if (!parent.getAttribute(elem, "stripcommand", stripCommandOpt))
8022 return false;
8023 if (!parent.getAttribute(elem, "out", fileNameOpt))
8024 return false;
8025 if (!parent.getAttribute(elem, "strip", doStripOpt))
8026 return false;
8027 if (!parent.getAttribute(elem, "symfile", symFileNameOpt))
8028 return false;
8030 std::vector<Element *> children = elem->getChildren();
8031 for (unsigned int i=0 ; i<children.size() ; i++)
8032 {
8033 Element *child = children[i];
8034 String tagName = child->getName();
8035 if (tagName == "fileset")
8036 {
8037 if (!parseFileSet(child, parent, fileSet))
8038 return false;
8039 }
8040 else if (tagName == "flags")
8041 {
8042 if (!parent.getValue(child, flagsOpt))
8043 return false;
8044 flagsOpt = strip(flagsOpt);
8045 }
8046 else if (tagName == "libs")
8047 {
8048 if (!parent.getValue(child, libsOpt))
8049 return false;
8050 libsOpt = strip(libsOpt);
8051 }
8052 }
8053 return true;
8054 }
8056 private:
8058 FileSet fileSet;
8060 String commandOpt;
8061 String fileNameOpt;
8062 String flagsOpt;
8063 String libsOpt;
8064 String doStripOpt;
8065 String symFileNameOpt;
8066 String stripCommandOpt;
8067 String objcopyCommandOpt;
8069 };
8073 /**
8074 * Create a named file
8075 */
8076 class TaskMakeFile : public Task
8077 {
8078 public:
8080 TaskMakeFile(MakeBase &par) : Task(par)
8081 { type = TASK_MAKEFILE; name = "makefile"; }
8083 virtual ~TaskMakeFile()
8084 {}
8086 virtual bool execute()
8087 {
8088 String fileName = parent.eval(fileNameOpt, "");
8089 String text = parent.eval(textOpt, "");
8091 taskstatus("%s", fileName.c_str());
8092 String fullName = parent.resolve(fileName);
8093 if (!isNewerThan(parent.getURI().getPath(), fullName))
8094 {
8095 //trace("skipped <makefile>");
8096 return true;
8097 }
8098 String fullNative = getNativePath(fullName);
8099 //trace("fullName:%s", fullName.c_str());
8100 FILE *f = fopen(fullNative.c_str(), "w");
8101 if (!f)
8102 {
8103 error("<makefile> could not open %s for writing : %s",
8104 fullName.c_str(), strerror(errno));
8105 return false;
8106 }
8107 for (unsigned int i=0 ; i<text.size() ; i++)
8108 fputc(text[i], f);
8109 fputc('\n', f);
8110 fclose(f);
8111 removeFromStatCache(fullNative);
8112 return true;
8113 }
8115 virtual bool parse(Element *elem)
8116 {
8117 if (!parent.getAttribute(elem, "file", fileNameOpt))
8118 return false;
8119 if (fileNameOpt.size() == 0)
8120 {
8121 error("<makefile> requires 'file=\"filename\"' attribute");
8122 return false;
8123 }
8124 if (!parent.getValue(elem, textOpt))
8125 return false;
8126 textOpt = leftJustify(textOpt);
8127 //trace("dirname:%s", dirName.c_str());
8128 return true;
8129 }
8131 private:
8133 String fileNameOpt;
8134 String textOpt;
8135 };
8139 /**
8140 * Create a named directory
8141 */
8142 class TaskMkDir : public Task
8143 {
8144 public:
8146 TaskMkDir(MakeBase &par) : Task(par)
8147 { type = TASK_MKDIR; name = "mkdir"; }
8149 virtual ~TaskMkDir()
8150 {}
8152 virtual bool execute()
8153 {
8154 String dirName = parent.eval(dirNameOpt, ".");
8156 taskstatus("%s", dirName.c_str());
8157 String fullDir = parent.resolve(dirName);
8158 //trace("fullDir:%s", fullDir.c_str());
8159 if (!createDirectory(fullDir))
8160 return false;
8161 return true;
8162 }
8164 virtual bool parse(Element *elem)
8165 {
8166 if (!parent.getAttribute(elem, "dir", dirNameOpt))
8167 return false;
8168 if (dirNameOpt.size() == 0)
8169 {
8170 error("<mkdir> requires 'dir=\"dirname\"' attribute");
8171 return false;
8172 }
8173 return true;
8174 }
8176 private:
8178 String dirNameOpt;
8179 };
8183 /**
8184 * Create a named directory
8185 */
8186 class TaskMsgFmt: public Task
8187 {
8188 public:
8190 TaskMsgFmt(MakeBase &par) : Task(par)
8191 { type = TASK_MSGFMT; name = "msgfmt"; }
8193 virtual ~TaskMsgFmt()
8194 {}
8196 virtual bool execute()
8197 {
8198 String command = parent.eval(commandOpt, "msgfmt");
8199 String toDirName = parent.eval(toDirNameOpt, ".");
8200 String outName = parent.eval(outNameOpt, "");
8201 bool owndir = parent.evalBool(owndirOpt, false);
8203 if (!listFiles(parent, fileSet))
8204 return false;
8205 String fileSetDir = fileSet.getDirectory();
8207 //trace("msgfmt: %d", fileSet.size());
8208 for (unsigned int i=0 ; i<fileSet.size() ; i++)
8209 {
8210 String fileName = fileSet[i];
8211 if (getSuffix(fileName) != "po")
8212 continue;
8213 String sourcePath;
8214 if (fileSetDir.size()>0)
8215 {
8216 sourcePath.append(fileSetDir);
8217 sourcePath.append("/");
8218 }
8219 sourcePath.append(fileName);
8220 String fullSource = parent.resolve(sourcePath);
8222 String destPath;
8223 if (toDirName.size()>0)
8224 {
8225 destPath.append(toDirName);
8226 destPath.append("/");
8227 }
8228 if (owndir)
8229 {
8230 String subdir = fileName;
8231 unsigned int pos = subdir.find_last_of('.');
8232 if (pos != subdir.npos)
8233 subdir = subdir.substr(0, pos);
8234 destPath.append(subdir);
8235 destPath.append("/");
8236 }
8237 //Pick the output file name
8238 if (outName.size() > 0)
8239 {
8240 destPath.append(outName);
8241 }
8242 else
8243 {
8244 destPath.append(fileName);
8245 destPath[destPath.size()-2] = 'm';
8246 }
8248 String fullDest = parent.resolve(destPath);
8250 if (!isNewerThan(fullSource, fullDest))
8251 {
8252 //trace("skip %s", fullSource.c_str());
8253 continue;
8254 }
8256 String cmd = command;
8257 cmd.append(" ");
8258 cmd.append(fullSource);
8259 cmd.append(" -o ");
8260 cmd.append(fullDest);
8262 int pos = fullDest.find_last_of('/');
8263 if (pos>0)
8264 {
8265 String fullDestPath = fullDest.substr(0, pos);
8266 if (!createDirectory(fullDestPath))
8267 return false;
8268 }
8272 String outString, errString;
8273 if (!executeCommand(cmd.c_str(), "", outString, errString))
8274 {
8275 error("<msgfmt> problem: %s", errString.c_str());
8276 return false;
8277 }
8278 removeFromStatCache(getNativePath(fullDest));
8279 }
8281 return true;
8282 }
8284 virtual bool parse(Element *elem)
8285 {
8286 if (!parent.getAttribute(elem, "command", commandOpt))
8287 return false;
8288 if (!parent.getAttribute(elem, "todir", toDirNameOpt))
8289 return false;
8290 if (!parent.getAttribute(elem, "out", outNameOpt))
8291 return false;
8292 if (!parent.getAttribute(elem, "owndir", owndirOpt))
8293 return false;
8295 std::vector<Element *> children = elem->getChildren();
8296 for (unsigned int i=0 ; i<children.size() ; i++)
8297 {
8298 Element *child = children[i];
8299 String tagName = child->getName();
8300 if (tagName == "fileset")
8301 {
8302 if (!parseFileSet(child, parent, fileSet))
8303 return false;
8304 }
8305 }
8306 return true;
8307 }
8309 private:
8311 FileSet fileSet;
8313 String commandOpt;
8314 String toDirNameOpt;
8315 String outNameOpt;
8316 String owndirOpt;
8318 };
8322 /**
8323 * Perform a Package-Config query similar to pkg-config
8324 */
8325 class TaskPkgConfig : public Task
8326 {
8327 public:
8329 typedef enum
8330 {
8331 PKG_CONFIG_QUERY_CFLAGS,
8332 PKG_CONFIG_QUERY_LIBS,
8333 PKG_CONFIG_QUERY_ALL
8334 } QueryTypes;
8336 TaskPkgConfig(MakeBase &par) : Task(par)
8337 {
8338 type = TASK_PKG_CONFIG;
8339 name = "pkg-config";
8340 }
8342 virtual ~TaskPkgConfig()
8343 {}
8345 virtual bool execute()
8346 {
8347 String pkgName = parent.eval(pkgNameOpt, "");
8348 String prefix = parent.eval(prefixOpt, "");
8349 String propName = parent.eval(propNameOpt, "");
8350 String pkgConfigPath = parent.eval(pkgConfigPathOpt,"");
8351 String query = parent.eval(queryOpt, "all");
8353 String path = parent.resolve(pkgConfigPath);
8354 PkgConfig pkgconfig;
8355 pkgconfig.setPath(path);
8356 pkgconfig.setPrefix(prefix);
8357 if (!pkgconfig.query(pkgName))
8358 {
8359 error("<pkg-config> query failed for '%s", name.c_str());
8360 return false;
8361 }
8363 String val = "";
8364 if (query == "cflags")
8365 val = pkgconfig.getCflags();
8366 else if (query == "libs")
8367 val =pkgconfig.getLibs();
8368 else if (query == "all")
8369 val = pkgconfig.getAll();
8370 else
8371 {
8372 error("<pkg-config> unhandled query : %s", query.c_str());
8373 return false;
8374 }
8375 taskstatus("property %s = '%s'", propName.c_str(), val.c_str());
8376 parent.setProperty(propName, val);
8377 return true;
8378 }
8380 virtual bool parse(Element *elem)
8381 {
8382 //# NAME
8383 if (!parent.getAttribute(elem, "name", pkgNameOpt))
8384 return false;
8385 if (pkgNameOpt.size()==0)
8386 {
8387 error("<pkg-config> requires 'name=\"package\"' attribute");
8388 return false;
8389 }
8391 //# PROPERTY
8392 if (!parent.getAttribute(elem, "property", propNameOpt))
8393 return false;
8394 if (propNameOpt.size()==0)
8395 {
8396 error("<pkg-config> requires 'property=\"name\"' attribute");
8397 return false;
8398 }
8399 //# PATH
8400 if (!parent.getAttribute(elem, "path", pkgConfigPathOpt))
8401 return false;
8402 //# PREFIX
8403 if (!parent.getAttribute(elem, "prefix", prefixOpt))
8404 return false;
8405 //# QUERY
8406 if (!parent.getAttribute(elem, "query", queryOpt))
8407 return false;
8409 return true;
8410 }
8412 private:
8414 String queryOpt;
8415 String pkgNameOpt;
8416 String prefixOpt;
8417 String propNameOpt;
8418 String pkgConfigPathOpt;
8420 };
8427 /**
8428 * Process an archive to allow random access
8429 */
8430 class TaskRanlib : public Task
8431 {
8432 public:
8434 TaskRanlib(MakeBase &par) : Task(par)
8435 { type = TASK_RANLIB; name = "ranlib"; }
8437 virtual ~TaskRanlib()
8438 {}
8440 virtual bool execute()
8441 {
8442 String fileName = parent.eval(fileNameOpt, "");
8443 String command = parent.eval(commandOpt, "ranlib");
8445 String fullName = parent.resolve(fileName);
8446 //trace("fullDir:%s", fullDir.c_str());
8447 String cmd = command;
8448 cmd.append(" ");
8449 cmd.append(fullName);
8450 String outbuf, errbuf;
8451 if (!executeCommand(cmd, "", outbuf, errbuf))
8452 return false;
8453 // TODO:
8454 //removeFromStatCache(getNativePath(fullDest));
8455 return true;
8456 }
8458 virtual bool parse(Element *elem)
8459 {
8460 if (!parent.getAttribute(elem, "command", commandOpt))
8461 return false;
8462 if (!parent.getAttribute(elem, "file", fileNameOpt))
8463 return false;
8464 if (fileNameOpt.size() == 0)
8465 {
8466 error("<ranlib> requires 'file=\"fileNname\"' attribute");
8467 return false;
8468 }
8469 return true;
8470 }
8472 private:
8474 String fileNameOpt;
8475 String commandOpt;
8476 };
8480 /**
8481 * Compile a resource file into a binary object
8482 */
8483 class TaskRC : public Task
8484 {
8485 public:
8487 TaskRC(MakeBase &par) : Task(par)
8488 { type = TASK_RC; name = "rc"; }
8490 virtual ~TaskRC()
8491 {}
8493 virtual bool execute()
8494 {
8495 String command = parent.eval(commandOpt, "windres");
8496 String flags = parent.eval(flagsOpt, "");
8497 String fileName = parent.eval(fileNameOpt, "");
8498 String outName = parent.eval(outNameOpt, "");
8500 String fullFile = parent.resolve(fileName);
8501 String fullOut = parent.resolve(outName);
8502 if (!isNewerThan(fullFile, fullOut))
8503 return true;
8504 String cmd = command;
8505 cmd.append(" -o ");
8506 cmd.append(fullOut);
8507 cmd.append(" ");
8508 cmd.append(flags);
8509 cmd.append(" ");
8510 cmd.append(fullFile);
8512 String outString, errString;
8513 if (!executeCommand(cmd.c_str(), "", outString, errString))
8514 {
8515 error("RC problem: %s", errString.c_str());
8516 return false;
8517 }
8518 removeFromStatCache(getNativePath(fullOut));
8519 return true;
8520 }
8522 virtual bool parse(Element *elem)
8523 {
8524 if (!parent.getAttribute(elem, "command", commandOpt))
8525 return false;
8526 if (!parent.getAttribute(elem, "file", fileNameOpt))
8527 return false;
8528 if (!parent.getAttribute(elem, "out", outNameOpt))
8529 return false;
8530 std::vector<Element *> children = elem->getChildren();
8531 for (unsigned int i=0 ; i<children.size() ; i++)
8532 {
8533 Element *child = children[i];
8534 String tagName = child->getName();
8535 if (tagName == "flags")
8536 {
8537 if (!parent.getValue(child, flagsOpt))
8538 return false;
8539 }
8540 }
8541 return true;
8542 }
8544 private:
8546 String commandOpt;
8547 String flagsOpt;
8548 String fileNameOpt;
8549 String outNameOpt;
8551 };
8555 /**
8556 * Collect .o's into a .so or DLL
8557 */
8558 class TaskSharedLib : public Task
8559 {
8560 public:
8562 TaskSharedLib(MakeBase &par) : Task(par)
8563 { type = TASK_SHAREDLIB; name = "dll"; }
8565 virtual ~TaskSharedLib()
8566 {}
8568 virtual bool execute()
8569 {
8570 String command = parent.eval(commandOpt, "dllwrap");
8571 String fileName = parent.eval(fileNameOpt, "");
8572 String defFileName = parent.eval(defFileNameOpt, "");
8573 String impFileName = parent.eval(impFileNameOpt, "");
8574 String libs = parent.eval(libsOpt, "");
8576 //trace("###########HERE %d", fileSet.size());
8577 bool doit = false;
8579 String fullOut = parent.resolve(fileName);
8580 //trace("ar fullout: %s", fullOut.c_str());
8582 if (!listFiles(parent, fileSet))
8583 return false;
8584 String fileSetDir = parent.eval(fileSet.getDirectory(), ".");
8586 for (unsigned int i=0 ; i<fileSet.size() ; i++)
8587 {
8588 String fname;
8589 if (fileSetDir.size()>0)
8590 {
8591 fname.append(fileSetDir);
8592 fname.append("/");
8593 }
8594 fname.append(fileSet[i]);
8595 String fullName = parent.resolve(fname);
8596 //trace("ar : %s/%s", fullOut.c_str(), fullName.c_str());
8597 if (isNewerThan(fullName, fullOut))
8598 doit = true;
8599 }
8600 //trace("Needs it:%d", doit);
8601 if (!doit)
8602 {
8603 return true;
8604 }
8606 String cmd = "dllwrap";
8607 cmd.append(" -o ");
8608 cmd.append(fullOut);
8609 if (defFileName.size()>0)
8610 {
8611 cmd.append(" --def ");
8612 cmd.append(defFileName);
8613 cmd.append(" ");
8614 }
8615 if (impFileName.size()>0)
8616 {
8617 cmd.append(" --implib ");
8618 cmd.append(impFileName);
8619 cmd.append(" ");
8620 }
8621 for (unsigned int i=0 ; i<fileSet.size() ; i++)
8622 {
8623 String fname;
8624 if (fileSetDir.size()>0)
8625 {
8626 fname.append(fileSetDir);
8627 fname.append("/");
8628 }
8629 fname.append(fileSet[i]);
8630 String fullName = parent.resolve(fname);
8632 cmd.append(" ");
8633 cmd.append(fullName);
8634 }
8635 cmd.append(" ");
8636 cmd.append(libs);
8638 String outString, errString;
8639 if (!executeCommand(cmd.c_str(), "", outString, errString))
8640 {
8641 error("<sharedlib> problem: %s", errString.c_str());
8642 return false;
8643 }
8644 removeFromStatCache(getNativePath(fullOut));
8645 return true;
8646 }
8648 virtual bool parse(Element *elem)
8649 {
8650 if (!parent.getAttribute(elem, "command", commandOpt))
8651 return false;
8652 if (!parent.getAttribute(elem, "file", fileNameOpt))
8653 return false;
8654 if (!parent.getAttribute(elem, "import", impFileNameOpt))
8655 return false;
8656 if (!parent.getAttribute(elem, "def", defFileNameOpt))
8657 return false;
8659 std::vector<Element *> children = elem->getChildren();
8660 for (unsigned int i=0 ; i<children.size() ; i++)
8661 {
8662 Element *child = children[i];
8663 String tagName = child->getName();
8664 if (tagName == "fileset")
8665 {
8666 if (!parseFileSet(child, parent, fileSet))
8667 return false;
8668 }
8669 else if (tagName == "libs")
8670 {
8671 if (!parent.getValue(child, libsOpt))
8672 return false;
8673 libsOpt = strip(libsOpt);
8674 }
8675 }
8676 return true;
8677 }
8679 private:
8681 FileSet fileSet;
8683 String commandOpt;
8684 String fileNameOpt;
8685 String defFileNameOpt;
8686 String impFileNameOpt;
8687 String libsOpt;
8689 };
8693 /**
8694 * Run the "ar" command to archive .o's into a .a
8695 */
8696 class TaskStaticLib : public Task
8697 {
8698 public:
8700 TaskStaticLib(MakeBase &par) : Task(par)
8701 { type = TASK_STATICLIB; name = "staticlib"; }
8703 virtual ~TaskStaticLib()
8704 {}
8706 virtual bool execute()
8707 {
8708 String command = parent.eval(commandOpt, "ar crv");
8709 String fileName = parent.eval(fileNameOpt, "");
8711 bool doit = false;
8713 String fullOut = parent.resolve(fileName);
8714 //trace("ar fullout: %s", fullOut.c_str());
8716 if (!listFiles(parent, fileSet))
8717 return false;
8718 String fileSetDir = parent.eval(fileSet.getDirectory(), ".");
8719 //trace("###########HERE %s", fileSetDir.c_str());
8721 for (unsigned int i=0 ; i<fileSet.size() ; i++)
8722 {
8723 String fname;
8724 if (fileSetDir.size()>0)
8725 {
8726 fname.append(fileSetDir);
8727 fname.append("/");
8728 }
8729 fname.append(fileSet[i]);
8730 String fullName = parent.resolve(fname);
8731 //trace("ar : %s/%s", fullOut.c_str(), fullName.c_str());
8732 if (isNewerThan(fullName, fullOut))
8733 doit = true;
8734 }
8735 //trace("Needs it:%d", doit);
8736 if (!doit)
8737 {
8738 return true;
8739 }
8741 String cmd = command;
8742 cmd.append(" ");
8743 cmd.append(fullOut);
8744 for (unsigned int i=0 ; i<fileSet.size() ; i++)
8745 {
8746 String fname;
8747 if (fileSetDir.size()>0)
8748 {
8749 fname.append(fileSetDir);
8750 fname.append("/");
8751 }
8752 fname.append(fileSet[i]);
8753 String fullName = parent.resolve(fname);
8755 cmd.append(" ");
8756 cmd.append(fullName);
8757 }
8759 String outString, errString;
8760 if (!executeCommand(cmd.c_str(), "", outString, errString))
8761 {
8762 error("<staticlib> problem: %s", errString.c_str());
8763 return false;
8764 }
8765 removeFromStatCache(getNativePath(fullOut));
8766 return true;
8767 }
8770 virtual bool parse(Element *elem)
8771 {
8772 if (!parent.getAttribute(elem, "command", commandOpt))
8773 return false;
8774 if (!parent.getAttribute(elem, "file", fileNameOpt))
8775 return false;
8777 std::vector<Element *> children = elem->getChildren();
8778 for (unsigned int i=0 ; i<children.size() ; i++)
8779 {
8780 Element *child = children[i];
8781 String tagName = child->getName();
8782 if (tagName == "fileset")
8783 {
8784 if (!parseFileSet(child, parent, fileSet))
8785 return false;
8786 }
8787 }
8788 return true;
8789 }
8791 private:
8793 FileSet fileSet;
8795 String commandOpt;
8796 String fileNameOpt;
8798 };
8803 /**
8804 * Strip an executable
8805 */
8806 class TaskStrip : public Task
8807 {
8808 public:
8810 TaskStrip(MakeBase &par) : Task(par)
8811 { type = TASK_STRIP; name = "strip"; }
8813 virtual ~TaskStrip()
8814 {}
8816 virtual bool execute()
8817 {
8818 String command = parent.eval(commandOpt, "strip");
8819 String fileName = parent.eval(fileNameOpt, "");
8820 String symFileName = parent.eval(symFileNameOpt, "");
8822 String fullName = parent.resolve(fileName);
8823 //trace("fullDir:%s", fullDir.c_str());
8824 String cmd;
8825 String outbuf, errbuf;
8827 if (symFileName.size()>0)
8828 {
8829 String symFullName = parent.resolve(symFileName);
8830 cmd = "objcopy --only-keep-debug ";
8831 cmd.append(getNativePath(fullName));
8832 cmd.append(" ");
8833 cmd.append(getNativePath(symFullName));
8834 if (!executeCommand(cmd, "", outbuf, errbuf))
8835 {
8836 error("<strip> symbol file failed : %s", errbuf.c_str());
8837 return false;
8838 }
8839 }
8841 cmd = command;
8842 cmd.append(getNativePath(fullName));
8843 if (!executeCommand(cmd, "", outbuf, errbuf))
8844 {
8845 error("<strip> failed : %s", errbuf.c_str());
8846 return false;
8847 }
8848 removeFromStatCache(getNativePath(fullName));
8849 return true;
8850 }
8852 virtual bool parse(Element *elem)
8853 {
8854 if (!parent.getAttribute(elem, "command", commandOpt))
8855 return false;
8856 if (!parent.getAttribute(elem, "file", fileNameOpt))
8857 return false;
8858 if (!parent.getAttribute(elem, "symfile", symFileNameOpt))
8859 return false;
8860 if (fileNameOpt.size() == 0)
8861 {
8862 error("<strip> requires 'file=\"fileName\"' attribute");
8863 return false;
8864 }
8865 return true;
8866 }
8868 private:
8870 String commandOpt;
8871 String fileNameOpt;
8872 String symFileNameOpt;
8873 };
8876 /**
8877 *
8878 */
8879 class TaskTouch : public Task
8880 {
8881 public:
8883 TaskTouch(MakeBase &par) : Task(par)
8884 { type = TASK_TOUCH; name = "touch"; }
8886 virtual ~TaskTouch()
8887 {}
8889 virtual bool execute()
8890 {
8891 String fileName = parent.eval(fileNameOpt, "");
8893 String fullName = parent.resolve(fileName);
8894 String nativeFile = getNativePath(fullName);
8895 if (!isRegularFile(fullName) && !isDirectory(fullName))
8896 {
8897 // S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH
8898 int ret = creat(nativeFile.c_str(), 0666);
8899 if (ret != 0)
8900 {
8901 error("<touch> could not create '%s' : %s",
8902 nativeFile.c_str(), strerror(ret));
8903 return false;
8904 }
8905 return true;
8906 }
8907 int ret = utime(nativeFile.c_str(), (struct utimbuf *)0);
8908 if (ret != 0)
8909 {
8910 error("<touch> could not update the modification time for '%s' : %s",
8911 nativeFile.c_str(), strerror(ret));
8912 return false;
8913 }
8914 removeFromStatCache(nativeFile);
8915 return true;
8916 }
8918 virtual bool parse(Element *elem)
8919 {
8920 //trace("touch parse");
8921 if (!parent.getAttribute(elem, "file", fileNameOpt))
8922 return false;
8923 if (fileNameOpt.size() == 0)
8924 {
8925 error("<touch> requires 'file=\"fileName\"' attribute");
8926 return false;
8927 }
8928 return true;
8929 }
8931 String fileNameOpt;
8932 };
8935 /**
8936 *
8937 */
8938 class TaskTstamp : public Task
8939 {
8940 public:
8942 TaskTstamp(MakeBase &par) : Task(par)
8943 { type = TASK_TSTAMP; name = "tstamp"; }
8945 virtual ~TaskTstamp()
8946 {}
8948 virtual bool execute()
8949 {
8950 return true;
8951 }
8953 virtual bool parse(Element *elem)
8954 {
8955 //trace("tstamp parse");
8956 return true;
8957 }
8958 };
8962 /**
8963 *
8964 */
8965 Task *Task::createTask(Element *elem, int lineNr)
8966 {
8967 String tagName = elem->getName();
8968 //trace("task:%s", tagName.c_str());
8969 Task *task = NULL;
8970 if (tagName == "cc")
8971 task = new TaskCC(parent);
8972 else if (tagName == "copy")
8973 task = new TaskCopy(parent);
8974 else if (tagName == "cxxtestpart")
8975 task = new TaskCxxTestPart(parent);
8976 else if (tagName == "cxxtestroot")
8977 task = new TaskCxxTestRoot(parent);
8978 else if (tagName == "cxxtestrun")
8979 task = new TaskCxxTestRun(parent);
8980 else if (tagName == "delete")
8981 task = new TaskDelete(parent);
8982 else if (tagName == "echo")
8983 task = new TaskEcho(parent);
8984 else if (tagName == "jar")
8985 task = new TaskJar(parent);
8986 else if (tagName == "javac")
8987 task = new TaskJavac(parent);
8988 else if (tagName == "link")
8989 task = new TaskLink(parent);
8990 else if (tagName == "makefile")
8991 task = new TaskMakeFile(parent);
8992 else if (tagName == "mkdir")
8993 task = new TaskMkDir(parent);
8994 else if (tagName == "msgfmt")
8995 task = new TaskMsgFmt(parent);
8996 else if (tagName == "pkg-config")
8997 task = new TaskPkgConfig(parent);
8998 else if (tagName == "ranlib")
8999 task = new TaskRanlib(parent);
9000 else if (tagName == "rc")
9001 task = new TaskRC(parent);
9002 else if (tagName == "sharedlib")
9003 task = new TaskSharedLib(parent);
9004 else if (tagName == "staticlib")
9005 task = new TaskStaticLib(parent);
9006 else if (tagName == "strip")
9007 task = new TaskStrip(parent);
9008 else if (tagName == "touch")
9009 task = new TaskTouch(parent);
9010 else if (tagName == "tstamp")
9011 task = new TaskTstamp(parent);
9012 else
9013 {
9014 error("Unknown task '%s'", tagName.c_str());
9015 return NULL;
9016 }
9018 task->setLine(lineNr);
9020 if (!task->parse(elem))
9021 {
9022 delete task;
9023 return NULL;
9024 }
9025 return task;
9026 }
9030 //########################################################################
9031 //# T A R G E T
9032 //########################################################################
9034 /**
9035 *
9036 */
9037 class Target : public MakeBase
9038 {
9040 public:
9042 /**
9043 *
9044 */
9045 Target(Make &par) : parent(par)
9046 { init(); }
9048 /**
9049 *
9050 */
9051 Target(const Target &other) : parent(other.parent)
9052 { init(); assign(other); }
9054 /**
9055 *
9056 */
9057 Target &operator=(const Target &other)
9058 { init(); assign(other); return *this; }
9060 /**
9061 *
9062 */
9063 virtual ~Target()
9064 { cleanup() ; }
9067 /**
9068 *
9069 */
9070 virtual Make &getParent()
9071 { return parent; }
9073 /**
9074 *
9075 */
9076 virtual String getName()
9077 { return name; }
9079 /**
9080 *
9081 */
9082 virtual void setName(const String &val)
9083 { name = val; }
9085 /**
9086 *
9087 */
9088 virtual String getDescription()
9089 { return description; }
9091 /**
9092 *
9093 */
9094 virtual void setDescription(const String &val)
9095 { description = val; }
9097 /**
9098 *
9099 */
9100 virtual void addDependency(const String &val)
9101 { deps.push_back(val); }
9103 /**
9104 *
9105 */
9106 virtual void parseDependencies(const String &val)
9107 { deps = tokenize(val, ", "); }
9109 /**
9110 *
9111 */
9112 virtual std::vector<String> &getDependencies()
9113 { return deps; }
9115 /**
9116 *
9117 */
9118 virtual String getIf()
9119 { return ifVar; }
9121 /**
9122 *
9123 */
9124 virtual void setIf(const String &val)
9125 { ifVar = val; }
9127 /**
9128 *
9129 */
9130 virtual String getUnless()
9131 { return unlessVar; }
9133 /**
9134 *
9135 */
9136 virtual void setUnless(const String &val)
9137 { unlessVar = val; }
9139 /**
9140 *
9141 */
9142 virtual void addTask(Task *val)
9143 { tasks.push_back(val); }
9145 /**
9146 *
9147 */
9148 virtual std::vector<Task *> &getTasks()
9149 { return tasks; }
9151 private:
9153 void init()
9154 {
9155 }
9157 void cleanup()
9158 {
9159 tasks.clear();
9160 }
9162 void assign(const Target &other)
9163 {
9164 //parent = other.parent;
9165 name = other.name;
9166 description = other.description;
9167 ifVar = other.ifVar;
9168 unlessVar = other.unlessVar;
9169 deps = other.deps;
9170 tasks = other.tasks;
9171 }
9173 Make &parent;
9175 String name;
9177 String description;
9179 String ifVar;
9181 String unlessVar;
9183 std::vector<String> deps;
9185 std::vector<Task *> tasks;
9187 };
9196 //########################################################################
9197 //# M A K E
9198 //########################################################################
9201 /**
9202 *
9203 */
9204 class Make : public MakeBase
9205 {
9207 public:
9209 /**
9210 *
9211 */
9212 Make()
9213 { init(); }
9215 /**
9216 *
9217 */
9218 Make(const Make &other)
9219 { assign(other); }
9221 /**
9222 *
9223 */
9224 Make &operator=(const Make &other)
9225 { assign(other); return *this; }
9227 /**
9228 *
9229 */
9230 virtual ~Make()
9231 { cleanup(); }
9233 /**
9234 *
9235 */
9236 virtual std::map<String, Target> &getTargets()
9237 { return targets; }
9240 /**
9241 *
9242 */
9243 virtual String version()
9244 { return BUILDTOOL_VERSION; }
9246 /**
9247 * Overload a <property>
9248 */
9249 virtual bool specifyProperty(const String &name,
9250 const String &value);
9252 /**
9253 *
9254 */
9255 virtual bool run();
9257 /**
9258 *
9259 */
9260 virtual bool run(const String &target);
9264 private:
9266 /**
9267 *
9268 */
9269 void init();
9271 /**
9272 *
9273 */
9274 void cleanup();
9276 /**
9277 *
9278 */
9279 void assign(const Make &other);
9281 /**
9282 *
9283 */
9284 bool executeTask(Task &task);
9287 /**
9288 *
9289 */
9290 bool executeTarget(Target &target,
9291 std::set<String> &targetsCompleted);
9294 /**
9295 *
9296 */
9297 bool execute();
9299 /**
9300 *
9301 */
9302 bool checkTargetDependencies(Target &prop,
9303 std::vector<String> &depList);
9305 /**
9306 *
9307 */
9308 bool parsePropertyFile(const String &fileName,
9309 const String &prefix);
9311 /**
9312 *
9313 */
9314 bool parseProperty(Element *elem);
9316 /**
9317 *
9318 */
9319 bool parseFile();
9321 /**
9322 *
9323 */
9324 std::vector<String> glob(const String &pattern);
9327 //###############
9328 //# Fields
9329 //###############
9331 String projectName;
9333 String currentTarget;
9335 String defaultTarget;
9337 String specifiedTarget;
9339 String baseDir;
9341 String description;
9343 //std::vector<Property> properties;
9345 std::map<String, Target> targets;
9347 std::vector<Task *> allTasks;
9349 std::map<String, String> specifiedProperties;
9351 };
9354 //########################################################################
9355 //# C L A S S M A I N T E N A N C E
9356 //########################################################################
9358 /**
9359 *
9360 */
9361 void Make::init()
9362 {
9363 uri = "build.xml";
9364 projectName = "";
9365 currentTarget = "";
9366 defaultTarget = "";
9367 specifiedTarget = "";
9368 baseDir = "";
9369 description = "";
9370 envPrefix = "env.";
9371 pcPrefix = "pc.";
9372 pccPrefix = "pcc.";
9373 pclPrefix = "pcl.";
9374 properties.clear();
9375 for (unsigned int i = 0 ; i < allTasks.size() ; i++)
9376 delete allTasks[i];
9377 allTasks.clear();
9378 }
9382 /**
9383 *
9384 */
9385 void Make::cleanup()
9386 {
9387 for (unsigned int i = 0 ; i < allTasks.size() ; i++)
9388 delete allTasks[i];
9389 allTasks.clear();
9390 }
9394 /**
9395 *
9396 */
9397 void Make::assign(const Make &other)
9398 {
9399 uri = other.uri;
9400 projectName = other.projectName;
9401 currentTarget = other.currentTarget;
9402 defaultTarget = other.defaultTarget;
9403 specifiedTarget = other.specifiedTarget;
9404 baseDir = other.baseDir;
9405 description = other.description;
9406 properties = other.properties;
9407 }
9411 //########################################################################
9412 //# U T I L I T Y T A S K S
9413 //########################################################################
9415 /**
9416 * Perform a file globbing
9417 */
9418 std::vector<String> Make::glob(const String &pattern)
9419 {
9420 std::vector<String> res;
9421 return res;
9422 }
9425 //########################################################################
9426 //# P U B L I C A P I
9427 //########################################################################
9431 /**
9432 *
9433 */
9434 bool Make::executeTarget(Target &target,
9435 std::set<String> &targetsCompleted)
9436 {
9438 String name = target.getName();
9440 //First get any dependencies for this target
9441 std::vector<String> deps = target.getDependencies();
9442 for (unsigned int i=0 ; i<deps.size() ; i++)
9443 {
9444 String dep = deps[i];
9445 //Did we do it already? Skip
9446 if (targetsCompleted.find(dep)!=targetsCompleted.end())
9447 continue;
9449 std::map<String, Target> &tgts =
9450 target.getParent().getTargets();
9451 std::map<String, Target>::iterator iter =
9452 tgts.find(dep);
9453 if (iter == tgts.end())
9454 {
9455 error("Target '%s' dependency '%s' not found",
9456 name.c_str(), dep.c_str());
9457 return false;
9458 }
9459 Target depTarget = iter->second;
9460 if (!executeTarget(depTarget, targetsCompleted))
9461 {
9462 return false;
9463 }
9464 }
9466 status("##### Target : %s\n##### %s", name.c_str(),
9467 target.getDescription().c_str());
9469 //Now let's do the tasks
9470 std::vector<Task *> &tasks = target.getTasks();
9471 for (unsigned int i=0 ; i<tasks.size() ; i++)
9472 {
9473 Task *task = tasks[i];
9474 status("--- %s / %s", name.c_str(), task->getName().c_str());
9475 if (!task->execute())
9476 {
9477 return false;
9478 }
9479 }
9481 targetsCompleted.insert(name);
9483 return true;
9484 }
9488 /**
9489 * Main execute() method. Start here and work
9490 * up the dependency tree
9491 */
9492 bool Make::execute()
9493 {
9494 status("######## EXECUTE");
9496 //Determine initial target
9497 if (specifiedTarget.size()>0)
9498 {
9499 currentTarget = specifiedTarget;
9500 }
9501 else if (defaultTarget.size()>0)
9502 {
9503 currentTarget = defaultTarget;
9504 }
9505 else
9506 {
9507 error("execute: no specified or default target requested");
9508 return false;
9509 }
9511 std::map<String, Target>::iterator iter =
9512 targets.find(currentTarget);
9513 if (iter == targets.end())
9514 {
9515 error("Initial target '%s' not found",
9516 currentTarget.c_str());
9517 return false;
9518 }
9520 //Now run
9521 Target target = iter->second;
9522 std::set<String> targetsCompleted;
9523 if (!executeTarget(target, targetsCompleted))
9524 {
9525 return false;
9526 }
9528 status("######## EXECUTE COMPLETE");
9529 return true;
9530 }
9535 /**
9536 *
9537 */
9538 bool Make::checkTargetDependencies(Target &target,
9539 std::vector<String> &depList)
9540 {
9541 String tgtName = target.getName().c_str();
9542 depList.push_back(tgtName);
9544 std::vector<String> deps = target.getDependencies();
9545 for (unsigned int i=0 ; i<deps.size() ; i++)
9546 {
9547 String dep = deps[i];
9548 //First thing entered was the starting Target
9549 if (dep == depList[0])
9550 {
9551 error("Circular dependency '%s' found at '%s'",
9552 dep.c_str(), tgtName.c_str());
9553 std::vector<String>::iterator diter;
9554 for (diter=depList.begin() ; diter!=depList.end() ; diter++)
9555 {
9556 error(" %s", diter->c_str());
9557 }
9558 return false;
9559 }
9561 std::map<String, Target> &tgts =
9562 target.getParent().getTargets();
9563 std::map<String, Target>::iterator titer = tgts.find(dep);
9564 if (titer == tgts.end())
9565 {
9566 error("Target '%s' dependency '%s' not found",
9567 tgtName.c_str(), dep.c_str());
9568 return false;
9569 }
9570 if (!checkTargetDependencies(titer->second, depList))
9571 {
9572 return false;
9573 }
9574 }
9575 return true;
9576 }
9582 static int getword(int pos, const String &inbuf, String &result)
9583 {
9584 int p = pos;
9585 int len = (int)inbuf.size();
9586 String val;
9587 while (p < len)
9588 {
9589 char ch = inbuf[p];
9590 if (!isalnum(ch) && ch!='.' && ch!='_')
9591 break;
9592 val.push_back(ch);
9593 p++;
9594 }
9595 result = val;
9596 return p;
9597 }
9602 /**
9603 *
9604 */
9605 bool Make::parsePropertyFile(const String &fileName,
9606 const String &prefix)
9607 {
9608 FILE *f = fopen(fileName.c_str(), "r");
9609 if (!f)
9610 {
9611 error("could not open property file %s", fileName.c_str());
9612 return false;
9613 }
9614 int linenr = 0;
9615 while (!feof(f))
9616 {
9617 char buf[256];
9618 if (!fgets(buf, 255, f))
9619 break;
9620 linenr++;
9621 String s = buf;
9622 s = trim(s);
9623 int len = s.size();
9624 if (len == 0)
9625 continue;
9626 if (s[0] == '#')
9627 continue;
9628 String key;
9629 String val;
9630 int p = 0;
9631 int p2 = getword(p, s, key);
9632 if (p2 <= p)
9633 {
9634 error("property file %s, line %d: expected keyword",
9635 fileName.c_str(), linenr);
9636 return false;
9637 }
9638 if (prefix.size() > 0)
9639 {
9640 key.insert(0, prefix);
9641 }
9643 //skip whitespace
9644 for (p=p2 ; p<len ; p++)
9645 if (!isspace(s[p]))
9646 break;
9648 if (p>=len || s[p]!='=')
9649 {
9650 error("property file %s, line %d: expected '='",
9651 fileName.c_str(), linenr);
9652 return false;
9653 }
9654 p++;
9656 //skip whitespace
9657 for ( ; p<len ; p++)
9658 if (!isspace(s[p]))
9659 break;
9661 /* This way expects a word after the =
9662 p2 = getword(p, s, val);
9663 if (p2 <= p)
9664 {
9665 error("property file %s, line %d: expected value",
9666 fileName.c_str(), linenr);
9667 return false;
9668 }
9669 */
9670 // This way gets the rest of the line after the =
9671 if (p>=len)
9672 {
9673 error("property file %s, line %d: expected value",
9674 fileName.c_str(), linenr);
9675 return false;
9676 }
9677 val = s.substr(p);
9678 if (key.size()==0)
9679 continue;
9680 //allow property to be set, even if val=""
9682 //trace("key:'%s' val:'%s'", key.c_str(), val.c_str());
9683 //See if we wanted to overload this property
9684 std::map<String, String>::iterator iter =
9685 specifiedProperties.find(key);
9686 if (iter!=specifiedProperties.end())
9687 {
9688 val = iter->second;
9689 status("overloading property '%s' = '%s'",
9690 key.c_str(), val.c_str());
9691 }
9692 properties[key] = val;
9693 }
9694 fclose(f);
9695 return true;
9696 }
9701 /**
9702 *
9703 */
9704 bool Make::parseProperty(Element *elem)
9705 {
9706 std::vector<Attribute> &attrs = elem->getAttributes();
9707 for (unsigned int i=0 ; i<attrs.size() ; i++)
9708 {
9709 String attrName = attrs[i].getName();
9710 String attrVal = attrs[i].getValue();
9712 if (attrName == "name")
9713 {
9714 String val;
9715 if (!getAttribute(elem, "value", val))
9716 return false;
9717 if (val.size() > 0)
9718 {
9719 properties[attrVal] = val;
9720 }
9721 else
9722 {
9723 if (!getAttribute(elem, "location", val))
9724 return false;
9725 //let the property exist, even if not defined
9726 properties[attrVal] = val;
9727 }
9728 //See if we wanted to overload this property
9729 std::map<String, String>::iterator iter =
9730 specifiedProperties.find(attrVal);
9731 if (iter != specifiedProperties.end())
9732 {
9733 val = iter->second;
9734 status("overloading property '%s' = '%s'",
9735 attrVal.c_str(), val.c_str());
9736 properties[attrVal] = val;
9737 }
9738 }
9739 else if (attrName == "file")
9740 {
9741 String prefix;
9742 if (!getAttribute(elem, "prefix", prefix))
9743 return false;
9744 if (prefix.size() > 0)
9745 {
9746 if (prefix[prefix.size()-1] != '.')
9747 prefix.push_back('.');
9748 }
9749 if (!parsePropertyFile(attrName, prefix))
9750 return false;
9751 }
9752 else if (attrName == "environment")
9753 {
9754 if (attrVal.find('.') != attrVal.npos)
9755 {
9756 error("environment prefix cannot have a '.' in it");
9757 return false;
9758 }
9759 envPrefix = attrVal;
9760 envPrefix.push_back('.');
9761 }
9762 else if (attrName == "pkg-config")
9763 {
9764 if (attrVal.find('.') != attrVal.npos)
9765 {
9766 error("pkg-config prefix cannot have a '.' in it");
9767 return false;
9768 }
9769 pcPrefix = attrVal;
9770 pcPrefix.push_back('.');
9771 }
9772 else if (attrName == "pkg-config-cflags")
9773 {
9774 if (attrVal.find('.') != attrVal.npos)
9775 {
9776 error("pkg-config-cflags prefix cannot have a '.' in it");
9777 return false;
9778 }
9779 pccPrefix = attrVal;
9780 pccPrefix.push_back('.');
9781 }
9782 else if (attrName == "pkg-config-libs")
9783 {
9784 if (attrVal.find('.') != attrVal.npos)
9785 {
9786 error("pkg-config-libs prefix cannot have a '.' in it");
9787 return false;
9788 }
9789 pclPrefix = attrVal;
9790 pclPrefix.push_back('.');
9791 }
9792 }
9794 return true;
9795 }
9800 /**
9801 *
9802 */
9803 bool Make::parseFile()
9804 {
9805 status("######## PARSE : %s", uri.getPath().c_str());
9807 setLine(0);
9809 Parser parser;
9810 Element *root = parser.parseFile(uri.getNativePath());
9811 if (!root)
9812 {
9813 error("Could not open %s for reading",
9814 uri.getNativePath().c_str());
9815 return false;
9816 }
9818 setLine(root->getLine());
9820 if (root->getChildren().size()==0 ||
9821 root->getChildren()[0]->getName()!="project")
9822 {
9823 error("Main xml element should be <project>");
9824 delete root;
9825 return false;
9826 }
9828 //########## Project attributes
9829 Element *project = root->getChildren()[0];
9830 String s = project->getAttribute("name");
9831 if (s.size() > 0)
9832 projectName = s;
9833 s = project->getAttribute("default");
9834 if (s.size() > 0)
9835 defaultTarget = s;
9836 s = project->getAttribute("basedir");
9837 if (s.size() > 0)
9838 baseDir = s;
9840 //######### PARSE MEMBERS
9841 std::vector<Element *> children = project->getChildren();
9842 for (unsigned int i=0 ; i<children.size() ; i++)
9843 {
9844 Element *elem = children[i];
9845 setLine(elem->getLine());
9846 String tagName = elem->getName();
9848 //########## DESCRIPTION
9849 if (tagName == "description")
9850 {
9851 description = parser.trim(elem->getValue());
9852 }
9854 //######### PROPERTY
9855 else if (tagName == "property")
9856 {
9857 if (!parseProperty(elem))
9858 return false;
9859 }
9861 //######### TARGET
9862 else if (tagName == "target")
9863 {
9864 String tname = elem->getAttribute("name");
9865 String tdesc = elem->getAttribute("description");
9866 String tdeps = elem->getAttribute("depends");
9867 String tif = elem->getAttribute("if");
9868 String tunless = elem->getAttribute("unless");
9869 Target target(*this);
9870 target.setName(tname);
9871 target.setDescription(tdesc);
9872 target.parseDependencies(tdeps);
9873 target.setIf(tif);
9874 target.setUnless(tunless);
9875 std::vector<Element *> telems = elem->getChildren();
9876 for (unsigned int i=0 ; i<telems.size() ; i++)
9877 {
9878 Element *telem = telems[i];
9879 Task breeder(*this);
9880 Task *task = breeder.createTask(telem, telem->getLine());
9881 if (!task)
9882 return false;
9883 allTasks.push_back(task);
9884 target.addTask(task);
9885 }
9887 //Check name
9888 if (tname.size() == 0)
9889 {
9890 error("no name for target");
9891 return false;
9892 }
9893 //Check for duplicate name
9894 if (targets.find(tname) != targets.end())
9895 {
9896 error("target '%s' already defined", tname.c_str());
9897 return false;
9898 }
9899 //more work than targets[tname]=target, but avoids default allocator
9900 targets.insert(std::make_pair<String, Target>(tname, target));
9901 }
9902 //######### none of the above
9903 else
9904 {
9905 error("unknown toplevel tag: <%s>", tagName.c_str());
9906 return false;
9907 }
9909 }
9911 std::map<String, Target>::iterator iter;
9912 for (iter = targets.begin() ; iter!= targets.end() ; iter++)
9913 {
9914 Target tgt = iter->second;
9915 std::vector<String> depList;
9916 if (!checkTargetDependencies(tgt, depList))
9917 {
9918 return false;
9919 }
9920 }
9923 delete root;
9924 status("######## PARSE COMPLETE");
9925 return true;
9926 }
9929 /**
9930 * Overload a <property>
9931 */
9932 bool Make::specifyProperty(const String &name, const String &value)
9933 {
9934 if (specifiedProperties.find(name) != specifiedProperties.end())
9935 {
9936 error("Property %s already specified", name.c_str());
9937 return false;
9938 }
9939 specifiedProperties[name] = value;
9940 return true;
9941 }
9945 /**
9946 *
9947 */
9948 bool Make::run()
9949 {
9950 if (!parseFile())
9951 return false;
9953 if (!execute())
9954 return false;
9956 return true;
9957 }
9962 /**
9963 * Get a formatted MM:SS.sss time elapsed string
9964 */
9965 static String
9966 timeDiffString(struct timeval &x, struct timeval &y)
9967 {
9968 long microsX = x.tv_usec;
9969 long secondsX = x.tv_sec;
9970 long microsY = y.tv_usec;
9971 long secondsY = y.tv_sec;
9972 if (microsX < microsY)
9973 {
9974 microsX += 1000000;
9975 secondsX -= 1;
9976 }
9978 int seconds = (int)(secondsX - secondsY);
9979 int millis = (int)((microsX - microsY)/1000);
9981 int minutes = seconds/60;
9982 seconds -= minutes*60;
9983 char buf[80];
9984 snprintf(buf, 79, "%dm %d.%03ds", minutes, seconds, millis);
9985 String ret = buf;
9986 return ret;
9988 }
9990 /**
9991 *
9992 */
9993 bool Make::run(const String &target)
9994 {
9995 status("####################################################");
9996 status("# %s", version().c_str());
9997 status("####################################################");
9998 struct timeval timeStart, timeEnd;
9999 ::gettimeofday(&timeStart, NULL);
10000 specifiedTarget = target;
10001 if (!run())
10002 return false;
10003 ::gettimeofday(&timeEnd, NULL);
10004 String timeStr = timeDiffString(timeEnd, timeStart);
10005 status("####################################################");
10006 status("# BuildTool Completed : %s", timeStr.c_str());
10007 status("####################################################");
10008 return true;
10009 }
10017 }// namespace buildtool
10018 //########################################################################
10019 //# M A I N
10020 //########################################################################
10022 typedef buildtool::String String;
10024 /**
10025 * Format an error message in printf() style
10026 */
10027 static void error(const char *fmt, ...)
10028 {
10029 va_list ap;
10030 va_start(ap, fmt);
10031 fprintf(stderr, "BuildTool error: ");
10032 vfprintf(stderr, fmt, ap);
10033 fprintf(stderr, "\n");
10034 va_end(ap);
10035 }
10038 static bool parseProperty(const String &s, String &name, String &val)
10039 {
10040 int len = s.size();
10041 int i;
10042 for (i=0 ; i<len ; i++)
10043 {
10044 char ch = s[i];
10045 if (ch == '=')
10046 break;
10047 name.push_back(ch);
10048 }
10049 if (i>=len || s[i]!='=')
10050 {
10051 error("property requires -Dname=value");
10052 return false;
10053 }
10054 i++;
10055 for ( ; i<len ; i++)
10056 {
10057 char ch = s[i];
10058 val.push_back(ch);
10059 }
10060 return true;
10061 }
10064 /**
10065 * Compare a buffer with a key, for the length of the key
10066 */
10067 static bool sequ(const String &buf, const char *key)
10068 {
10069 int len = buf.size();
10070 for (int i=0 ; key[i] && i<len ; i++)
10071 {
10072 if (key[i] != buf[i])
10073 return false;
10074 }
10075 return true;
10076 }
10078 static void usage(int argc, char **argv)
10079 {
10080 printf("usage:\n");
10081 printf(" %s [options] [target]\n", argv[0]);
10082 printf("Options:\n");
10083 printf(" -help, -h print this message\n");
10084 printf(" -version print the version information and exit\n");
10085 printf(" -file <file> use given buildfile\n");
10086 printf(" -f <file> ''\n");
10087 printf(" -D<property>=<value> use value for given property\n");
10088 }
10093 /**
10094 * Parse the command-line args, get our options,
10095 * and run this thing
10096 */
10097 static bool parseOptions(int argc, char **argv)
10098 {
10099 if (argc < 1)
10100 {
10101 error("Cannot parse arguments");
10102 return false;
10103 }
10105 buildtool::Make make;
10107 String target;
10109 //char *progName = argv[0];
10110 for (int i=1 ; i<argc ; i++)
10111 {
10112 String arg = argv[i];
10113 if (arg.size()>1 && arg[0]=='-')
10114 {
10115 if (arg == "-h" || arg == "-help")
10116 {
10117 usage(argc,argv);
10118 return true;
10119 }
10120 else if (arg == "-version")
10121 {
10122 printf("%s", make.version().c_str());
10123 return true;
10124 }
10125 else if (arg == "-f" || arg == "-file")
10126 {
10127 if (i>=argc)
10128 {
10129 usage(argc, argv);
10130 return false;
10131 }
10132 i++; //eat option
10133 make.setURI(argv[i]);
10134 }
10135 else if (arg.size()>2 && sequ(arg, "-D"))
10136 {
10137 String s = arg.substr(2, arg.size());
10138 String name, value;
10139 if (!parseProperty(s, name, value))
10140 {
10141 usage(argc, argv);
10142 return false;
10143 }
10144 if (!make.specifyProperty(name, value))
10145 return false;
10146 }
10147 else
10148 {
10149 error("Unknown option:%s", arg.c_str());
10150 return false;
10151 }
10152 }
10153 else
10154 {
10155 if (target.size()>0)
10156 {
10157 error("only one initial target");
10158 usage(argc, argv);
10159 return false;
10160 }
10161 target = arg;
10162 }
10163 }
10165 //We have the options. Now execute them
10166 if (!make.run(target))
10167 return false;
10169 return true;
10170 }
10175 /*
10176 static bool runMake()
10177 {
10178 buildtool::Make make;
10179 if (!make.run())
10180 return false;
10181 return true;
10182 }
10185 static bool pkgConfigTest()
10186 {
10187 buildtool::PkgConfig pkgConfig;
10188 if (!pkgConfig.readFile("gtk+-2.0.pc"))
10189 return false;
10190 return true;
10191 }
10195 static bool depTest()
10196 {
10197 buildtool::DepTool deptool;
10198 deptool.setSourceDirectory("/dev/ink/inkscape/src");
10199 if (!deptool.generateDependencies("build.dep"))
10200 return false;
10201 std::vector<buildtool::FileRec> res =
10202 deptool.loadDepFile("build.dep");
10203 if (res.size() == 0)
10204 return false;
10205 return true;
10206 }
10208 static bool popenTest()
10209 {
10210 buildtool::Make make;
10211 buildtool::String out, err;
10212 bool ret = make.executeCommand("gcc xx.cpp", "", out, err);
10213 printf("Popen test:%d '%s' '%s'\n", ret, out.c_str(), err.c_str());
10214 return true;
10215 }
10218 static bool propFileTest()
10219 {
10220 buildtool::Make make;
10221 make.parsePropertyFile("test.prop", "test.");
10222 return true;
10223 }
10224 */
10226 int main(int argc, char **argv)
10227 {
10229 if (!parseOptions(argc, argv))
10230 return 1;
10231 /*
10232 if (!popenTest())
10233 return 1;
10235 if (!depTest())
10236 return 1;
10237 if (!propFileTest())
10238 return 1;
10239 if (runMake())
10240 return 1;
10241 */
10242 return 0;
10243 }
10246 //########################################################################
10247 //# E N D
10248 //########################################################################