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 struct stat finfo;
7604 switch (delType)
7605 {
7606 case DEL_FILE:
7607 {
7608 taskstatus("file: %s", fileName.c_str());
7609 String fullName = parent.resolve(fileName);
7610 char *fname = (char *)fullName.c_str();
7611 if (!quiet && verbose)
7612 taskstatus("path: %s", fname);
7613 if (!removeFile(fullName))
7614 {
7615 //error("Could not delete file '%s'", fullName.c_str());
7616 return false;
7617 }
7618 return true;
7619 }
7620 case DEL_DIR:
7621 {
7622 taskstatus("dir: %s", dirName.c_str());
7623 String fullDir = parent.resolve(dirName);
7624 if (!quiet && verbose)
7625 taskstatus("path: %s", fullDir.c_str());
7626 if (!removeDirectory(fullDir))
7627 {
7628 //error("Could not delete directory '%s'", fullDir.c_str());
7629 return false;
7630 }
7631 return true;
7632 }
7633 }
7634 return true;
7635 }
7637 virtual bool parse(Element *elem)
7638 {
7639 if (!parent.getAttribute(elem, "file", fileNameOpt))
7640 return false;
7641 if (fileNameOpt.size() > 0)
7642 delType = DEL_FILE;
7643 if (!parent.getAttribute(elem, "dir", dirNameOpt))
7644 return false;
7645 if (dirNameOpt.size() > 0)
7646 delType = DEL_DIR;
7647 if (fileNameOpt.size()>0 && dirNameOpt.size()>0)
7648 {
7649 error("<delete> can have one attribute of file= or dir=");
7650 return false;
7651 }
7652 if (fileNameOpt.size()==0 && dirNameOpt.size()==0)
7653 {
7654 error("<delete> must have one attribute of file= or dir=");
7655 return false;
7656 }
7657 if (!parent.getAttribute(elem, "verbose", verboseOpt))
7658 return false;
7659 if (!parent.getAttribute(elem, "quiet", quietOpt))
7660 return false;
7661 if (!parent.getAttribute(elem, "failonerror", failOnErrorOpt))
7662 return false;
7663 return true;
7664 }
7666 private:
7668 int delType;
7669 String dirNameOpt;
7670 String fileNameOpt;
7671 String verboseOpt;
7672 String quietOpt;
7673 String failOnErrorOpt;
7674 };
7677 /**
7678 * Send a message to stdout
7679 */
7680 class TaskEcho : public Task
7681 {
7682 public:
7684 TaskEcho(MakeBase &par) : Task(par)
7685 { type = TASK_ECHO; name = "echo"; }
7687 virtual ~TaskEcho()
7688 {}
7690 virtual bool execute()
7691 {
7692 //let message have priority over text
7693 String message = parent.eval(messageOpt, "");
7694 String text = parent.eval(textOpt, "");
7695 if (message.size() > 0)
7696 {
7697 fprintf(stdout, "%s\n", message.c_str());
7698 }
7699 else if (text.size() > 0)
7700 {
7701 fprintf(stdout, "%s\n", text.c_str());
7702 }
7703 return true;
7704 }
7706 virtual bool parse(Element *elem)
7707 {
7708 if (!parent.getValue(elem, textOpt))
7709 return false;
7710 textOpt = leftJustify(textOpt);
7711 if (!parent.getAttribute(elem, "message", messageOpt))
7712 return false;
7713 return true;
7714 }
7716 private:
7718 String messageOpt;
7719 String textOpt;
7720 };
7724 /**
7725 *
7726 */
7727 class TaskJar : public Task
7728 {
7729 public:
7731 TaskJar(MakeBase &par) : Task(par)
7732 { type = TASK_JAR; name = "jar"; }
7734 virtual ~TaskJar()
7735 {}
7737 virtual bool execute()
7738 {
7739 String command = parent.eval(commandOpt, "jar");
7740 String basedir = parent.eval(basedirOpt, ".");
7741 String destfile = parent.eval(destfileOpt, ".");
7743 String cmd = command;
7744 cmd.append(" -cf ");
7745 cmd.append(destfile);
7746 cmd.append(" -C ");
7747 cmd.append(basedir);
7748 cmd.append(" .");
7750 String execCmd = cmd;
7752 String outString, errString;
7753 bool ret = executeCommand(execCmd.c_str(), "", outString, errString);
7754 if (!ret)
7755 {
7756 error("<jar> command '%s' failed :\n %s",
7757 execCmd.c_str(), errString.c_str());
7758 return false;
7759 }
7760 removeFromStatCache(getNativePath(destfile));
7761 return true;
7762 }
7764 virtual bool parse(Element *elem)
7765 {
7766 if (!parent.getAttribute(elem, "command", commandOpt))
7767 return false;
7768 if (!parent.getAttribute(elem, "basedir", basedirOpt))
7769 return false;
7770 if (!parent.getAttribute(elem, "destfile", destfileOpt))
7771 return false;
7772 if (basedirOpt.size() == 0 || destfileOpt.size() == 0)
7773 {
7774 error("<jar> required both basedir and destfile attributes to be set");
7775 return false;
7776 }
7777 return true;
7778 }
7780 private:
7782 String commandOpt;
7783 String basedirOpt;
7784 String destfileOpt;
7785 };
7788 /**
7789 *
7790 */
7791 class TaskJavac : public Task
7792 {
7793 public:
7795 TaskJavac(MakeBase &par) : Task(par)
7796 {
7797 type = TASK_JAVAC; name = "javac";
7798 }
7800 virtual ~TaskJavac()
7801 {}
7803 virtual bool execute()
7804 {
7805 String command = parent.eval(commandOpt, "javac");
7806 String srcdir = parent.eval(srcdirOpt, ".");
7807 String destdir = parent.eval(destdirOpt, ".");
7808 String target = parent.eval(targetOpt, "");
7810 std::vector<String> fileList;
7811 if (!listFiles(srcdir, "", fileList))
7812 {
7813 return false;
7814 }
7815 String cmd = command;
7816 cmd.append(" -d ");
7817 cmd.append(destdir);
7818 cmd.append(" -classpath ");
7819 cmd.append(destdir);
7820 cmd.append(" -sourcepath ");
7821 cmd.append(srcdir);
7822 cmd.append(" ");
7823 if (target.size()>0)
7824 {
7825 cmd.append(" -target ");
7826 cmd.append(target);
7827 cmd.append(" ");
7828 }
7829 String fname = "javalist.btool";
7830 FILE *f = fopen(fname.c_str(), "w");
7831 int count = 0;
7832 for (unsigned int i=0 ; i<fileList.size() ; i++)
7833 {
7834 String fname = fileList[i];
7835 String srcName = fname;
7836 if (fname.size()<6) //x.java
7837 continue;
7838 if (fname.compare(fname.size()-5, 5, ".java") != 0)
7839 continue;
7840 String baseName = fname.substr(0, fname.size()-5);
7841 String destName = baseName;
7842 destName.append(".class");
7844 String fullSrc = srcdir;
7845 fullSrc.append("/");
7846 fullSrc.append(fname);
7847 String fullDest = destdir;
7848 fullDest.append("/");
7849 fullDest.append(destName);
7850 //trace("fullsrc:%s fulldest:%s", fullSrc.c_str(), fullDest.c_str());
7851 if (!isNewerThan(fullSrc, fullDest))
7852 continue;
7854 count++;
7855 fprintf(f, "%s\n", fullSrc.c_str());
7856 }
7857 fclose(f);
7858 if (!count)
7859 {
7860 taskstatus("nothing to do");
7861 return true;
7862 }
7864 taskstatus("compiling %d files", count);
7866 String execCmd = cmd;
7867 execCmd.append("@");
7868 execCmd.append(fname);
7870 String outString, errString;
7871 bool ret = executeCommand(execCmd.c_str(), "", outString, errString);
7872 if (!ret)
7873 {
7874 error("<javac> command '%s' failed :\n %s",
7875 execCmd.c_str(), errString.c_str());
7876 return false;
7877 }
7878 // TODO:
7879 //removeFromStatCache(getNativePath(........));
7880 return true;
7881 }
7883 virtual bool parse(Element *elem)
7884 {
7885 if (!parent.getAttribute(elem, "command", commandOpt))
7886 return false;
7887 if (!parent.getAttribute(elem, "srcdir", srcdirOpt))
7888 return false;
7889 if (!parent.getAttribute(elem, "destdir", destdirOpt))
7890 return false;
7891 if (srcdirOpt.size() == 0 || destdirOpt.size() == 0)
7892 {
7893 error("<javac> required both srcdir and destdir attributes to be set");
7894 return false;
7895 }
7896 if (!parent.getAttribute(elem, "target", targetOpt))
7897 return false;
7898 return true;
7899 }
7901 private:
7903 String commandOpt;
7904 String srcdirOpt;
7905 String destdirOpt;
7906 String targetOpt;
7908 };
7911 /**
7912 *
7913 */
7914 class TaskLink : public Task
7915 {
7916 public:
7918 TaskLink(MakeBase &par) : Task(par)
7919 {
7920 type = TASK_LINK; name = "link";
7921 }
7923 virtual ~TaskLink()
7924 {}
7926 virtual bool execute()
7927 {
7928 String command = parent.eval(commandOpt, "g++");
7929 String fileName = parent.eval(fileNameOpt, "");
7930 String flags = parent.eval(flagsOpt, "");
7931 String libs = parent.eval(libsOpt, "");
7932 bool doStrip = parent.evalBool(doStripOpt, false);
7933 String symFileName = parent.eval(symFileNameOpt, "");
7934 String stripCommand = parent.eval(stripCommandOpt, "strip");
7935 String objcopyCommand = parent.eval(objcopyCommandOpt, "objcopy");
7937 if (!listFiles(parent, fileSet))
7938 return false;
7939 String fileSetDir = parent.eval(fileSet.getDirectory(), ".");
7940 //trace("%d files in %s", fileSet.size(), fileSetDir.c_str());
7941 bool doit = false;
7942 String fullTarget = parent.resolve(fileName);
7943 String cmd = command;
7944 cmd.append(" -o ");
7945 cmd.append(fullTarget);
7946 cmd.append(" ");
7947 cmd.append(flags);
7948 for (unsigned int i=0 ; i<fileSet.size() ; i++)
7949 {
7950 cmd.append(" ");
7951 String obj;
7952 if (fileSetDir.size()>0)
7953 {
7954 obj.append(fileSetDir);
7955 obj.append("/");
7956 }
7957 obj.append(fileSet[i]);
7958 String fullObj = parent.resolve(obj);
7959 String nativeFullObj = getNativePath(fullObj);
7960 cmd.append(nativeFullObj);
7961 //trace("link: tgt:%s obj:%s", fullTarget.c_str(),
7962 // fullObj.c_str());
7963 if (isNewerThan(fullObj, fullTarget))
7964 doit = true;
7965 }
7966 cmd.append(" ");
7967 cmd.append(libs);
7968 if (!doit)
7969 {
7970 //trace("link not needed");
7971 return true;
7972 }
7973 //trace("LINK cmd:%s", cmd.c_str());
7976 String outbuf, errbuf;
7977 if (!executeCommand(cmd.c_str(), "", outbuf, errbuf))
7978 {
7979 error("LINK problem: %s", errbuf.c_str());
7980 return false;
7981 }
7982 removeFromStatCache(getNativePath(fullTarget));
7984 if (symFileName.size()>0)
7985 {
7986 String symFullName = parent.resolve(symFileName);
7987 cmd = objcopyCommand;
7988 cmd.append(" --only-keep-debug ");
7989 cmd.append(getNativePath(fullTarget));
7990 cmd.append(" ");
7991 cmd.append(getNativePath(symFullName));
7992 if (!executeCommand(cmd, "", outbuf, errbuf))
7993 {
7994 error("<strip> symbol file failed : %s", errbuf.c_str());
7995 return false;
7996 }
7997 removeFromStatCache(getNativePath(symFullName));
7998 }
8000 if (doStrip)
8001 {
8002 cmd = stripCommand;
8003 cmd.append(" ");
8004 cmd.append(getNativePath(fullTarget));
8005 if (!executeCommand(cmd, "", outbuf, errbuf))
8006 {
8007 error("<strip> failed : %s", errbuf.c_str());
8008 return false;
8009 }
8010 removeFromStatCache(getNativePath(fullTarget));
8011 }
8013 return true;
8014 }
8016 virtual bool parse(Element *elem)
8017 {
8018 if (!parent.getAttribute(elem, "command", commandOpt))
8019 return false;
8020 if (!parent.getAttribute(elem, "objcopycommand", objcopyCommandOpt))
8021 return false;
8022 if (!parent.getAttribute(elem, "stripcommand", stripCommandOpt))
8023 return false;
8024 if (!parent.getAttribute(elem, "out", fileNameOpt))
8025 return false;
8026 if (!parent.getAttribute(elem, "strip", doStripOpt))
8027 return false;
8028 if (!parent.getAttribute(elem, "symfile", symFileNameOpt))
8029 return false;
8031 std::vector<Element *> children = elem->getChildren();
8032 for (unsigned int i=0 ; i<children.size() ; i++)
8033 {
8034 Element *child = children[i];
8035 String tagName = child->getName();
8036 if (tagName == "fileset")
8037 {
8038 if (!parseFileSet(child, parent, fileSet))
8039 return false;
8040 }
8041 else if (tagName == "flags")
8042 {
8043 if (!parent.getValue(child, flagsOpt))
8044 return false;
8045 flagsOpt = strip(flagsOpt);
8046 }
8047 else if (tagName == "libs")
8048 {
8049 if (!parent.getValue(child, libsOpt))
8050 return false;
8051 libsOpt = strip(libsOpt);
8052 }
8053 }
8054 return true;
8055 }
8057 private:
8059 FileSet fileSet;
8061 String commandOpt;
8062 String fileNameOpt;
8063 String flagsOpt;
8064 String libsOpt;
8065 String doStripOpt;
8066 String symFileNameOpt;
8067 String stripCommandOpt;
8068 String objcopyCommandOpt;
8070 };
8074 /**
8075 * Create a named file
8076 */
8077 class TaskMakeFile : public Task
8078 {
8079 public:
8081 TaskMakeFile(MakeBase &par) : Task(par)
8082 { type = TASK_MAKEFILE; name = "makefile"; }
8084 virtual ~TaskMakeFile()
8085 {}
8087 virtual bool execute()
8088 {
8089 String fileName = parent.eval(fileNameOpt, "");
8090 String text = parent.eval(textOpt, "");
8092 taskstatus("%s", fileName.c_str());
8093 String fullName = parent.resolve(fileName);
8094 if (!isNewerThan(parent.getURI().getPath(), fullName))
8095 {
8096 //trace("skipped <makefile>");
8097 return true;
8098 }
8099 String fullNative = getNativePath(fullName);
8100 //trace("fullName:%s", fullName.c_str());
8101 FILE *f = fopen(fullNative.c_str(), "w");
8102 if (!f)
8103 {
8104 error("<makefile> could not open %s for writing : %s",
8105 fullName.c_str(), strerror(errno));
8106 return false;
8107 }
8108 for (unsigned int i=0 ; i<text.size() ; i++)
8109 fputc(text[i], f);
8110 fputc('\n', f);
8111 fclose(f);
8112 removeFromStatCache(fullNative);
8113 return true;
8114 }
8116 virtual bool parse(Element *elem)
8117 {
8118 if (!parent.getAttribute(elem, "file", fileNameOpt))
8119 return false;
8120 if (fileNameOpt.size() == 0)
8121 {
8122 error("<makefile> requires 'file=\"filename\"' attribute");
8123 return false;
8124 }
8125 if (!parent.getValue(elem, textOpt))
8126 return false;
8127 textOpt = leftJustify(textOpt);
8128 //trace("dirname:%s", dirName.c_str());
8129 return true;
8130 }
8132 private:
8134 String fileNameOpt;
8135 String textOpt;
8136 };
8140 /**
8141 * Create a named directory
8142 */
8143 class TaskMkDir : public Task
8144 {
8145 public:
8147 TaskMkDir(MakeBase &par) : Task(par)
8148 { type = TASK_MKDIR; name = "mkdir"; }
8150 virtual ~TaskMkDir()
8151 {}
8153 virtual bool execute()
8154 {
8155 String dirName = parent.eval(dirNameOpt, ".");
8157 taskstatus("%s", dirName.c_str());
8158 String fullDir = parent.resolve(dirName);
8159 //trace("fullDir:%s", fullDir.c_str());
8160 if (!createDirectory(fullDir))
8161 return false;
8162 return true;
8163 }
8165 virtual bool parse(Element *elem)
8166 {
8167 if (!parent.getAttribute(elem, "dir", dirNameOpt))
8168 return false;
8169 if (dirNameOpt.size() == 0)
8170 {
8171 error("<mkdir> requires 'dir=\"dirname\"' attribute");
8172 return false;
8173 }
8174 return true;
8175 }
8177 private:
8179 String dirNameOpt;
8180 };
8184 /**
8185 * Create a named directory
8186 */
8187 class TaskMsgFmt: public Task
8188 {
8189 public:
8191 TaskMsgFmt(MakeBase &par) : Task(par)
8192 { type = TASK_MSGFMT; name = "msgfmt"; }
8194 virtual ~TaskMsgFmt()
8195 {}
8197 virtual bool execute()
8198 {
8199 String command = parent.eval(commandOpt, "msgfmt");
8200 String toDirName = parent.eval(toDirNameOpt, ".");
8201 String outName = parent.eval(outNameOpt, "");
8202 bool owndir = parent.evalBool(owndirOpt, false);
8204 if (!listFiles(parent, fileSet))
8205 return false;
8206 String fileSetDir = fileSet.getDirectory();
8208 //trace("msgfmt: %d", fileSet.size());
8209 for (unsigned int i=0 ; i<fileSet.size() ; i++)
8210 {
8211 String fileName = fileSet[i];
8212 if (getSuffix(fileName) != "po")
8213 continue;
8214 String sourcePath;
8215 if (fileSetDir.size()>0)
8216 {
8217 sourcePath.append(fileSetDir);
8218 sourcePath.append("/");
8219 }
8220 sourcePath.append(fileName);
8221 String fullSource = parent.resolve(sourcePath);
8223 String destPath;
8224 if (toDirName.size()>0)
8225 {
8226 destPath.append(toDirName);
8227 destPath.append("/");
8228 }
8229 if (owndir)
8230 {
8231 String subdir = fileName;
8232 unsigned int pos = subdir.find_last_of('.');
8233 if (pos != subdir.npos)
8234 subdir = subdir.substr(0, pos);
8235 destPath.append(subdir);
8236 destPath.append("/");
8237 }
8238 //Pick the output file name
8239 if (outName.size() > 0)
8240 {
8241 destPath.append(outName);
8242 }
8243 else
8244 {
8245 destPath.append(fileName);
8246 destPath[destPath.size()-2] = 'm';
8247 }
8249 String fullDest = parent.resolve(destPath);
8251 if (!isNewerThan(fullSource, fullDest))
8252 {
8253 //trace("skip %s", fullSource.c_str());
8254 continue;
8255 }
8257 String cmd = command;
8258 cmd.append(" ");
8259 cmd.append(fullSource);
8260 cmd.append(" -o ");
8261 cmd.append(fullDest);
8263 int pos = fullDest.find_last_of('/');
8264 if (pos>0)
8265 {
8266 String fullDestPath = fullDest.substr(0, pos);
8267 if (!createDirectory(fullDestPath))
8268 return false;
8269 }
8273 String outString, errString;
8274 if (!executeCommand(cmd.c_str(), "", outString, errString))
8275 {
8276 error("<msgfmt> problem: %s", errString.c_str());
8277 return false;
8278 }
8279 removeFromStatCache(getNativePath(fullDest));
8280 }
8282 return true;
8283 }
8285 virtual bool parse(Element *elem)
8286 {
8287 if (!parent.getAttribute(elem, "command", commandOpt))
8288 return false;
8289 if (!parent.getAttribute(elem, "todir", toDirNameOpt))
8290 return false;
8291 if (!parent.getAttribute(elem, "out", outNameOpt))
8292 return false;
8293 if (!parent.getAttribute(elem, "owndir", owndirOpt))
8294 return false;
8296 std::vector<Element *> children = elem->getChildren();
8297 for (unsigned int i=0 ; i<children.size() ; i++)
8298 {
8299 Element *child = children[i];
8300 String tagName = child->getName();
8301 if (tagName == "fileset")
8302 {
8303 if (!parseFileSet(child, parent, fileSet))
8304 return false;
8305 }
8306 }
8307 return true;
8308 }
8310 private:
8312 FileSet fileSet;
8314 String commandOpt;
8315 String toDirNameOpt;
8316 String outNameOpt;
8317 String owndirOpt;
8319 };
8323 /**
8324 * Perform a Package-Config query similar to pkg-config
8325 */
8326 class TaskPkgConfig : public Task
8327 {
8328 public:
8330 typedef enum
8331 {
8332 PKG_CONFIG_QUERY_CFLAGS,
8333 PKG_CONFIG_QUERY_LIBS,
8334 PKG_CONFIG_QUERY_ALL
8335 } QueryTypes;
8337 TaskPkgConfig(MakeBase &par) : Task(par)
8338 {
8339 type = TASK_PKG_CONFIG;
8340 name = "pkg-config";
8341 }
8343 virtual ~TaskPkgConfig()
8344 {}
8346 virtual bool execute()
8347 {
8348 String pkgName = parent.eval(pkgNameOpt, "");
8349 String prefix = parent.eval(prefixOpt, "");
8350 String propName = parent.eval(propNameOpt, "");
8351 String pkgConfigPath = parent.eval(pkgConfigPathOpt,"");
8352 String query = parent.eval(queryOpt, "all");
8354 String path = parent.resolve(pkgConfigPath);
8355 PkgConfig pkgconfig;
8356 pkgconfig.setPath(path);
8357 pkgconfig.setPrefix(prefix);
8358 if (!pkgconfig.query(pkgName))
8359 {
8360 error("<pkg-config> query failed for '%s", name.c_str());
8361 return false;
8362 }
8364 String val = "";
8365 if (query == "cflags")
8366 val = pkgconfig.getCflags();
8367 else if (query == "libs")
8368 val =pkgconfig.getLibs();
8369 else if (query == "all")
8370 val = pkgconfig.getAll();
8371 else
8372 {
8373 error("<pkg-config> unhandled query : %s", query.c_str());
8374 return false;
8375 }
8376 taskstatus("property %s = '%s'", propName.c_str(), val.c_str());
8377 parent.setProperty(propName, val);
8378 return true;
8379 }
8381 virtual bool parse(Element *elem)
8382 {
8383 //# NAME
8384 if (!parent.getAttribute(elem, "name", pkgNameOpt))
8385 return false;
8386 if (pkgNameOpt.size()==0)
8387 {
8388 error("<pkg-config> requires 'name=\"package\"' attribute");
8389 return false;
8390 }
8392 //# PROPERTY
8393 if (!parent.getAttribute(elem, "property", propNameOpt))
8394 return false;
8395 if (propNameOpt.size()==0)
8396 {
8397 error("<pkg-config> requires 'property=\"name\"' attribute");
8398 return false;
8399 }
8400 //# PATH
8401 if (!parent.getAttribute(elem, "path", pkgConfigPathOpt))
8402 return false;
8403 //# PREFIX
8404 if (!parent.getAttribute(elem, "prefix", prefixOpt))
8405 return false;
8406 //# QUERY
8407 if (!parent.getAttribute(elem, "query", queryOpt))
8408 return false;
8410 return true;
8411 }
8413 private:
8415 String queryOpt;
8416 String pkgNameOpt;
8417 String prefixOpt;
8418 String propNameOpt;
8419 String pkgConfigPathOpt;
8421 };
8428 /**
8429 * Process an archive to allow random access
8430 */
8431 class TaskRanlib : public Task
8432 {
8433 public:
8435 TaskRanlib(MakeBase &par) : Task(par)
8436 { type = TASK_RANLIB; name = "ranlib"; }
8438 virtual ~TaskRanlib()
8439 {}
8441 virtual bool execute()
8442 {
8443 String fileName = parent.eval(fileNameOpt, "");
8444 String command = parent.eval(commandOpt, "ranlib");
8446 String fullName = parent.resolve(fileName);
8447 //trace("fullDir:%s", fullDir.c_str());
8448 String cmd = command;
8449 cmd.append(" ");
8450 cmd.append(fullName);
8451 String outbuf, errbuf;
8452 if (!executeCommand(cmd, "", outbuf, errbuf))
8453 return false;
8454 // TODO:
8455 //removeFromStatCache(getNativePath(fullDest));
8456 return true;
8457 }
8459 virtual bool parse(Element *elem)
8460 {
8461 if (!parent.getAttribute(elem, "command", commandOpt))
8462 return false;
8463 if (!parent.getAttribute(elem, "file", fileNameOpt))
8464 return false;
8465 if (fileNameOpt.size() == 0)
8466 {
8467 error("<ranlib> requires 'file=\"fileNname\"' attribute");
8468 return false;
8469 }
8470 return true;
8471 }
8473 private:
8475 String fileNameOpt;
8476 String commandOpt;
8477 };
8481 /**
8482 * Compile a resource file into a binary object
8483 */
8484 class TaskRC : public Task
8485 {
8486 public:
8488 TaskRC(MakeBase &par) : Task(par)
8489 { type = TASK_RC; name = "rc"; }
8491 virtual ~TaskRC()
8492 {}
8494 virtual bool execute()
8495 {
8496 String command = parent.eval(commandOpt, "windres");
8497 String flags = parent.eval(flagsOpt, "");
8498 String fileName = parent.eval(fileNameOpt, "");
8499 String outName = parent.eval(outNameOpt, "");
8501 String fullFile = parent.resolve(fileName);
8502 String fullOut = parent.resolve(outName);
8503 if (!isNewerThan(fullFile, fullOut))
8504 return true;
8505 String cmd = command;
8506 cmd.append(" -o ");
8507 cmd.append(fullOut);
8508 cmd.append(" ");
8509 cmd.append(flags);
8510 cmd.append(" ");
8511 cmd.append(fullFile);
8513 String outString, errString;
8514 if (!executeCommand(cmd.c_str(), "", outString, errString))
8515 {
8516 error("RC problem: %s", errString.c_str());
8517 return false;
8518 }
8519 removeFromStatCache(getNativePath(fullOut));
8520 return true;
8521 }
8523 virtual bool parse(Element *elem)
8524 {
8525 if (!parent.getAttribute(elem, "command", commandOpt))
8526 return false;
8527 if (!parent.getAttribute(elem, "file", fileNameOpt))
8528 return false;
8529 if (!parent.getAttribute(elem, "out", outNameOpt))
8530 return false;
8531 std::vector<Element *> children = elem->getChildren();
8532 for (unsigned int i=0 ; i<children.size() ; i++)
8533 {
8534 Element *child = children[i];
8535 String tagName = child->getName();
8536 if (tagName == "flags")
8537 {
8538 if (!parent.getValue(child, flagsOpt))
8539 return false;
8540 }
8541 }
8542 return true;
8543 }
8545 private:
8547 String commandOpt;
8548 String flagsOpt;
8549 String fileNameOpt;
8550 String outNameOpt;
8552 };
8556 /**
8557 * Collect .o's into a .so or DLL
8558 */
8559 class TaskSharedLib : public Task
8560 {
8561 public:
8563 TaskSharedLib(MakeBase &par) : Task(par)
8564 { type = TASK_SHAREDLIB; name = "dll"; }
8566 virtual ~TaskSharedLib()
8567 {}
8569 virtual bool execute()
8570 {
8571 String command = parent.eval(commandOpt, "dllwrap");
8572 String fileName = parent.eval(fileNameOpt, "");
8573 String defFileName = parent.eval(defFileNameOpt, "");
8574 String impFileName = parent.eval(impFileNameOpt, "");
8575 String libs = parent.eval(libsOpt, "");
8577 //trace("###########HERE %d", fileSet.size());
8578 bool doit = false;
8580 String fullOut = parent.resolve(fileName);
8581 //trace("ar fullout: %s", fullOut.c_str());
8583 if (!listFiles(parent, fileSet))
8584 return false;
8585 String fileSetDir = parent.eval(fileSet.getDirectory(), ".");
8587 for (unsigned int i=0 ; i<fileSet.size() ; i++)
8588 {
8589 String fname;
8590 if (fileSetDir.size()>0)
8591 {
8592 fname.append(fileSetDir);
8593 fname.append("/");
8594 }
8595 fname.append(fileSet[i]);
8596 String fullName = parent.resolve(fname);
8597 //trace("ar : %s/%s", fullOut.c_str(), fullName.c_str());
8598 if (isNewerThan(fullName, fullOut))
8599 doit = true;
8600 }
8601 //trace("Needs it:%d", doit);
8602 if (!doit)
8603 {
8604 return true;
8605 }
8607 String cmd = "dllwrap";
8608 cmd.append(" -o ");
8609 cmd.append(fullOut);
8610 if (defFileName.size()>0)
8611 {
8612 cmd.append(" --def ");
8613 cmd.append(defFileName);
8614 cmd.append(" ");
8615 }
8616 if (impFileName.size()>0)
8617 {
8618 cmd.append(" --implib ");
8619 cmd.append(impFileName);
8620 cmd.append(" ");
8621 }
8622 for (unsigned int i=0 ; i<fileSet.size() ; i++)
8623 {
8624 String fname;
8625 if (fileSetDir.size()>0)
8626 {
8627 fname.append(fileSetDir);
8628 fname.append("/");
8629 }
8630 fname.append(fileSet[i]);
8631 String fullName = parent.resolve(fname);
8633 cmd.append(" ");
8634 cmd.append(fullName);
8635 }
8636 cmd.append(" ");
8637 cmd.append(libs);
8639 String outString, errString;
8640 if (!executeCommand(cmd.c_str(), "", outString, errString))
8641 {
8642 error("<sharedlib> problem: %s", errString.c_str());
8643 return false;
8644 }
8645 removeFromStatCache(getNativePath(fullOut));
8646 return true;
8647 }
8649 virtual bool parse(Element *elem)
8650 {
8651 if (!parent.getAttribute(elem, "command", commandOpt))
8652 return false;
8653 if (!parent.getAttribute(elem, "file", fileNameOpt))
8654 return false;
8655 if (!parent.getAttribute(elem, "import", impFileNameOpt))
8656 return false;
8657 if (!parent.getAttribute(elem, "def", defFileNameOpt))
8658 return false;
8660 std::vector<Element *> children = elem->getChildren();
8661 for (unsigned int i=0 ; i<children.size() ; i++)
8662 {
8663 Element *child = children[i];
8664 String tagName = child->getName();
8665 if (tagName == "fileset")
8666 {
8667 if (!parseFileSet(child, parent, fileSet))
8668 return false;
8669 }
8670 else if (tagName == "libs")
8671 {
8672 if (!parent.getValue(child, libsOpt))
8673 return false;
8674 libsOpt = strip(libsOpt);
8675 }
8676 }
8677 return true;
8678 }
8680 private:
8682 FileSet fileSet;
8684 String commandOpt;
8685 String fileNameOpt;
8686 String defFileNameOpt;
8687 String impFileNameOpt;
8688 String libsOpt;
8690 };
8694 /**
8695 * Run the "ar" command to archive .o's into a .a
8696 */
8697 class TaskStaticLib : public Task
8698 {
8699 public:
8701 TaskStaticLib(MakeBase &par) : Task(par)
8702 { type = TASK_STATICLIB; name = "staticlib"; }
8704 virtual ~TaskStaticLib()
8705 {}
8707 virtual bool execute()
8708 {
8709 String command = parent.eval(commandOpt, "ar crv");
8710 String fileName = parent.eval(fileNameOpt, "");
8712 bool doit = false;
8714 String fullOut = parent.resolve(fileName);
8715 //trace("ar fullout: %s", fullOut.c_str());
8717 if (!listFiles(parent, fileSet))
8718 return false;
8719 String fileSetDir = parent.eval(fileSet.getDirectory(), ".");
8720 //trace("###########HERE %s", fileSetDir.c_str());
8722 for (unsigned int i=0 ; i<fileSet.size() ; i++)
8723 {
8724 String fname;
8725 if (fileSetDir.size()>0)
8726 {
8727 fname.append(fileSetDir);
8728 fname.append("/");
8729 }
8730 fname.append(fileSet[i]);
8731 String fullName = parent.resolve(fname);
8732 //trace("ar : %s/%s", fullOut.c_str(), fullName.c_str());
8733 if (isNewerThan(fullName, fullOut))
8734 doit = true;
8735 }
8736 //trace("Needs it:%d", doit);
8737 if (!doit)
8738 {
8739 return true;
8740 }
8742 String cmd = command;
8743 cmd.append(" ");
8744 cmd.append(fullOut);
8745 for (unsigned int i=0 ; i<fileSet.size() ; i++)
8746 {
8747 String fname;
8748 if (fileSetDir.size()>0)
8749 {
8750 fname.append(fileSetDir);
8751 fname.append("/");
8752 }
8753 fname.append(fileSet[i]);
8754 String fullName = parent.resolve(fname);
8756 cmd.append(" ");
8757 cmd.append(fullName);
8758 }
8760 String outString, errString;
8761 if (!executeCommand(cmd.c_str(), "", outString, errString))
8762 {
8763 error("<staticlib> problem: %s", errString.c_str());
8764 return false;
8765 }
8766 removeFromStatCache(getNativePath(fullOut));
8767 return true;
8768 }
8771 virtual bool parse(Element *elem)
8772 {
8773 if (!parent.getAttribute(elem, "command", commandOpt))
8774 return false;
8775 if (!parent.getAttribute(elem, "file", fileNameOpt))
8776 return false;
8778 std::vector<Element *> children = elem->getChildren();
8779 for (unsigned int i=0 ; i<children.size() ; i++)
8780 {
8781 Element *child = children[i];
8782 String tagName = child->getName();
8783 if (tagName == "fileset")
8784 {
8785 if (!parseFileSet(child, parent, fileSet))
8786 return false;
8787 }
8788 }
8789 return true;
8790 }
8792 private:
8794 FileSet fileSet;
8796 String commandOpt;
8797 String fileNameOpt;
8799 };
8804 /**
8805 * Strip an executable
8806 */
8807 class TaskStrip : public Task
8808 {
8809 public:
8811 TaskStrip(MakeBase &par) : Task(par)
8812 { type = TASK_STRIP; name = "strip"; }
8814 virtual ~TaskStrip()
8815 {}
8817 virtual bool execute()
8818 {
8819 String command = parent.eval(commandOpt, "strip");
8820 String fileName = parent.eval(fileNameOpt, "");
8821 String symFileName = parent.eval(symFileNameOpt, "");
8823 String fullName = parent.resolve(fileName);
8824 //trace("fullDir:%s", fullDir.c_str());
8825 String cmd;
8826 String outbuf, errbuf;
8828 if (symFileName.size()>0)
8829 {
8830 String symFullName = parent.resolve(symFileName);
8831 cmd = "objcopy --only-keep-debug ";
8832 cmd.append(getNativePath(fullName));
8833 cmd.append(" ");
8834 cmd.append(getNativePath(symFullName));
8835 if (!executeCommand(cmd, "", outbuf, errbuf))
8836 {
8837 error("<strip> symbol file failed : %s", errbuf.c_str());
8838 return false;
8839 }
8840 }
8842 cmd = command;
8843 cmd.append(getNativePath(fullName));
8844 if (!executeCommand(cmd, "", outbuf, errbuf))
8845 {
8846 error("<strip> failed : %s", errbuf.c_str());
8847 return false;
8848 }
8849 removeFromStatCache(getNativePath(fullName));
8850 return true;
8851 }
8853 virtual bool parse(Element *elem)
8854 {
8855 if (!parent.getAttribute(elem, "command", commandOpt))
8856 return false;
8857 if (!parent.getAttribute(elem, "file", fileNameOpt))
8858 return false;
8859 if (!parent.getAttribute(elem, "symfile", symFileNameOpt))
8860 return false;
8861 if (fileNameOpt.size() == 0)
8862 {
8863 error("<strip> requires 'file=\"fileName\"' attribute");
8864 return false;
8865 }
8866 return true;
8867 }
8869 private:
8871 String commandOpt;
8872 String fileNameOpt;
8873 String symFileNameOpt;
8874 };
8877 /**
8878 *
8879 */
8880 class TaskTouch : public Task
8881 {
8882 public:
8884 TaskTouch(MakeBase &par) : Task(par)
8885 { type = TASK_TOUCH; name = "touch"; }
8887 virtual ~TaskTouch()
8888 {}
8890 virtual bool execute()
8891 {
8892 String fileName = parent.eval(fileNameOpt, "");
8894 String fullName = parent.resolve(fileName);
8895 String nativeFile = getNativePath(fullName);
8896 if (!isRegularFile(fullName) && !isDirectory(fullName))
8897 {
8898 // S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH
8899 int ret = creat(nativeFile.c_str(), 0666);
8900 if (ret != 0)
8901 {
8902 error("<touch> could not create '%s' : %s",
8903 nativeFile.c_str(), strerror(ret));
8904 return false;
8905 }
8906 return true;
8907 }
8908 int ret = utime(nativeFile.c_str(), (struct utimbuf *)0);
8909 if (ret != 0)
8910 {
8911 error("<touch> could not update the modification time for '%s' : %s",
8912 nativeFile.c_str(), strerror(ret));
8913 return false;
8914 }
8915 removeFromStatCache(nativeFile);
8916 return true;
8917 }
8919 virtual bool parse(Element *elem)
8920 {
8921 //trace("touch parse");
8922 if (!parent.getAttribute(elem, "file", fileNameOpt))
8923 return false;
8924 if (fileNameOpt.size() == 0)
8925 {
8926 error("<touch> requires 'file=\"fileName\"' attribute");
8927 return false;
8928 }
8929 return true;
8930 }
8932 String fileNameOpt;
8933 };
8936 /**
8937 *
8938 */
8939 class TaskTstamp : public Task
8940 {
8941 public:
8943 TaskTstamp(MakeBase &par) : Task(par)
8944 { type = TASK_TSTAMP; name = "tstamp"; }
8946 virtual ~TaskTstamp()
8947 {}
8949 virtual bool execute()
8950 {
8951 return true;
8952 }
8954 virtual bool parse(Element *elem)
8955 {
8956 //trace("tstamp parse");
8957 return true;
8958 }
8959 };
8963 /**
8964 *
8965 */
8966 Task *Task::createTask(Element *elem, int lineNr)
8967 {
8968 String tagName = elem->getName();
8969 //trace("task:%s", tagName.c_str());
8970 Task *task = NULL;
8971 if (tagName == "cc")
8972 task = new TaskCC(parent);
8973 else if (tagName == "copy")
8974 task = new TaskCopy(parent);
8975 else if (tagName == "cxxtestpart")
8976 task = new TaskCxxTestPart(parent);
8977 else if (tagName == "cxxtestroot")
8978 task = new TaskCxxTestRoot(parent);
8979 else if (tagName == "cxxtestrun")
8980 task = new TaskCxxTestRun(parent);
8981 else if (tagName == "delete")
8982 task = new TaskDelete(parent);
8983 else if (tagName == "echo")
8984 task = new TaskEcho(parent);
8985 else if (tagName == "jar")
8986 task = new TaskJar(parent);
8987 else if (tagName == "javac")
8988 task = new TaskJavac(parent);
8989 else if (tagName == "link")
8990 task = new TaskLink(parent);
8991 else if (tagName == "makefile")
8992 task = new TaskMakeFile(parent);
8993 else if (tagName == "mkdir")
8994 task = new TaskMkDir(parent);
8995 else if (tagName == "msgfmt")
8996 task = new TaskMsgFmt(parent);
8997 else if (tagName == "pkg-config")
8998 task = new TaskPkgConfig(parent);
8999 else if (tagName == "ranlib")
9000 task = new TaskRanlib(parent);
9001 else if (tagName == "rc")
9002 task = new TaskRC(parent);
9003 else if (tagName == "sharedlib")
9004 task = new TaskSharedLib(parent);
9005 else if (tagName == "staticlib")
9006 task = new TaskStaticLib(parent);
9007 else if (tagName == "strip")
9008 task = new TaskStrip(parent);
9009 else if (tagName == "touch")
9010 task = new TaskTouch(parent);
9011 else if (tagName == "tstamp")
9012 task = new TaskTstamp(parent);
9013 else
9014 {
9015 error("Unknown task '%s'", tagName.c_str());
9016 return NULL;
9017 }
9019 task->setLine(lineNr);
9021 if (!task->parse(elem))
9022 {
9023 delete task;
9024 return NULL;
9025 }
9026 return task;
9027 }
9031 //########################################################################
9032 //# T A R G E T
9033 //########################################################################
9035 /**
9036 *
9037 */
9038 class Target : public MakeBase
9039 {
9041 public:
9043 /**
9044 *
9045 */
9046 Target(Make &par) : parent(par)
9047 { init(); }
9049 /**
9050 *
9051 */
9052 Target(const Target &other) : parent(other.parent)
9053 { init(); assign(other); }
9055 /**
9056 *
9057 */
9058 Target &operator=(const Target &other)
9059 { init(); assign(other); return *this; }
9061 /**
9062 *
9063 */
9064 virtual ~Target()
9065 { cleanup() ; }
9068 /**
9069 *
9070 */
9071 virtual Make &getParent()
9072 { return parent; }
9074 /**
9075 *
9076 */
9077 virtual String getName()
9078 { return name; }
9080 /**
9081 *
9082 */
9083 virtual void setName(const String &val)
9084 { name = val; }
9086 /**
9087 *
9088 */
9089 virtual String getDescription()
9090 { return description; }
9092 /**
9093 *
9094 */
9095 virtual void setDescription(const String &val)
9096 { description = val; }
9098 /**
9099 *
9100 */
9101 virtual void addDependency(const String &val)
9102 { deps.push_back(val); }
9104 /**
9105 *
9106 */
9107 virtual void parseDependencies(const String &val)
9108 { deps = tokenize(val, ", "); }
9110 /**
9111 *
9112 */
9113 virtual std::vector<String> &getDependencies()
9114 { return deps; }
9116 /**
9117 *
9118 */
9119 virtual String getIf()
9120 { return ifVar; }
9122 /**
9123 *
9124 */
9125 virtual void setIf(const String &val)
9126 { ifVar = val; }
9128 /**
9129 *
9130 */
9131 virtual String getUnless()
9132 { return unlessVar; }
9134 /**
9135 *
9136 */
9137 virtual void setUnless(const String &val)
9138 { unlessVar = val; }
9140 /**
9141 *
9142 */
9143 virtual void addTask(Task *val)
9144 { tasks.push_back(val); }
9146 /**
9147 *
9148 */
9149 virtual std::vector<Task *> &getTasks()
9150 { return tasks; }
9152 private:
9154 void init()
9155 {
9156 }
9158 void cleanup()
9159 {
9160 tasks.clear();
9161 }
9163 void assign(const Target &other)
9164 {
9165 //parent = other.parent;
9166 name = other.name;
9167 description = other.description;
9168 ifVar = other.ifVar;
9169 unlessVar = other.unlessVar;
9170 deps = other.deps;
9171 tasks = other.tasks;
9172 }
9174 Make &parent;
9176 String name;
9178 String description;
9180 String ifVar;
9182 String unlessVar;
9184 std::vector<String> deps;
9186 std::vector<Task *> tasks;
9188 };
9197 //########################################################################
9198 //# M A K E
9199 //########################################################################
9202 /**
9203 *
9204 */
9205 class Make : public MakeBase
9206 {
9208 public:
9210 /**
9211 *
9212 */
9213 Make()
9214 { init(); }
9216 /**
9217 *
9218 */
9219 Make(const Make &other)
9220 { assign(other); }
9222 /**
9223 *
9224 */
9225 Make &operator=(const Make &other)
9226 { assign(other); return *this; }
9228 /**
9229 *
9230 */
9231 virtual ~Make()
9232 { cleanup(); }
9234 /**
9235 *
9236 */
9237 virtual std::map<String, Target> &getTargets()
9238 { return targets; }
9241 /**
9242 *
9243 */
9244 virtual String version()
9245 { return BUILDTOOL_VERSION; }
9247 /**
9248 * Overload a <property>
9249 */
9250 virtual bool specifyProperty(const String &name,
9251 const String &value);
9253 /**
9254 *
9255 */
9256 virtual bool run();
9258 /**
9259 *
9260 */
9261 virtual bool run(const String &target);
9265 private:
9267 /**
9268 *
9269 */
9270 void init();
9272 /**
9273 *
9274 */
9275 void cleanup();
9277 /**
9278 *
9279 */
9280 void assign(const Make &other);
9282 /**
9283 *
9284 */
9285 bool executeTask(Task &task);
9288 /**
9289 *
9290 */
9291 bool executeTarget(Target &target,
9292 std::set<String> &targetsCompleted);
9295 /**
9296 *
9297 */
9298 bool execute();
9300 /**
9301 *
9302 */
9303 bool checkTargetDependencies(Target &prop,
9304 std::vector<String> &depList);
9306 /**
9307 *
9308 */
9309 bool parsePropertyFile(const String &fileName,
9310 const String &prefix);
9312 /**
9313 *
9314 */
9315 bool parseProperty(Element *elem);
9317 /**
9318 *
9319 */
9320 bool parseFile();
9322 /**
9323 *
9324 */
9325 std::vector<String> glob(const String &pattern);
9328 //###############
9329 //# Fields
9330 //###############
9332 String projectName;
9334 String currentTarget;
9336 String defaultTarget;
9338 String specifiedTarget;
9340 String baseDir;
9342 String description;
9344 //std::vector<Property> properties;
9346 std::map<String, Target> targets;
9348 std::vector<Task *> allTasks;
9350 std::map<String, String> specifiedProperties;
9352 };
9355 //########################################################################
9356 //# C L A S S M A I N T E N A N C E
9357 //########################################################################
9359 /**
9360 *
9361 */
9362 void Make::init()
9363 {
9364 uri = "build.xml";
9365 projectName = "";
9366 currentTarget = "";
9367 defaultTarget = "";
9368 specifiedTarget = "";
9369 baseDir = "";
9370 description = "";
9371 envPrefix = "env.";
9372 pcPrefix = "pc.";
9373 pccPrefix = "pcc.";
9374 pclPrefix = "pcl.";
9375 properties.clear();
9376 for (unsigned int i = 0 ; i < allTasks.size() ; i++)
9377 delete allTasks[i];
9378 allTasks.clear();
9379 }
9383 /**
9384 *
9385 */
9386 void Make::cleanup()
9387 {
9388 for (unsigned int i = 0 ; i < allTasks.size() ; i++)
9389 delete allTasks[i];
9390 allTasks.clear();
9391 }
9395 /**
9396 *
9397 */
9398 void Make::assign(const Make &other)
9399 {
9400 uri = other.uri;
9401 projectName = other.projectName;
9402 currentTarget = other.currentTarget;
9403 defaultTarget = other.defaultTarget;
9404 specifiedTarget = other.specifiedTarget;
9405 baseDir = other.baseDir;
9406 description = other.description;
9407 properties = other.properties;
9408 }
9412 //########################################################################
9413 //# U T I L I T Y T A S K S
9414 //########################################################################
9416 /**
9417 * Perform a file globbing
9418 */
9419 std::vector<String> Make::glob(const String &pattern)
9420 {
9421 std::vector<String> res;
9422 return res;
9423 }
9426 //########################################################################
9427 //# P U B L I C A P I
9428 //########################################################################
9432 /**
9433 *
9434 */
9435 bool Make::executeTarget(Target &target,
9436 std::set<String> &targetsCompleted)
9437 {
9439 String name = target.getName();
9441 //First get any dependencies for this target
9442 std::vector<String> deps = target.getDependencies();
9443 for (unsigned int i=0 ; i<deps.size() ; i++)
9444 {
9445 String dep = deps[i];
9446 //Did we do it already? Skip
9447 if (targetsCompleted.find(dep)!=targetsCompleted.end())
9448 continue;
9450 std::map<String, Target> &tgts =
9451 target.getParent().getTargets();
9452 std::map<String, Target>::iterator iter =
9453 tgts.find(dep);
9454 if (iter == tgts.end())
9455 {
9456 error("Target '%s' dependency '%s' not found",
9457 name.c_str(), dep.c_str());
9458 return false;
9459 }
9460 Target depTarget = iter->second;
9461 if (!executeTarget(depTarget, targetsCompleted))
9462 {
9463 return false;
9464 }
9465 }
9467 status("##### Target : %s\n##### %s", name.c_str(),
9468 target.getDescription().c_str());
9470 //Now let's do the tasks
9471 std::vector<Task *> &tasks = target.getTasks();
9472 for (unsigned int i=0 ; i<tasks.size() ; i++)
9473 {
9474 Task *task = tasks[i];
9475 status("--- %s / %s", name.c_str(), task->getName().c_str());
9476 if (!task->execute())
9477 {
9478 return false;
9479 }
9480 }
9482 targetsCompleted.insert(name);
9484 return true;
9485 }
9489 /**
9490 * Main execute() method. Start here and work
9491 * up the dependency tree
9492 */
9493 bool Make::execute()
9494 {
9495 status("######## EXECUTE");
9497 //Determine initial target
9498 if (specifiedTarget.size()>0)
9499 {
9500 currentTarget = specifiedTarget;
9501 }
9502 else if (defaultTarget.size()>0)
9503 {
9504 currentTarget = defaultTarget;
9505 }
9506 else
9507 {
9508 error("execute: no specified or default target requested");
9509 return false;
9510 }
9512 std::map<String, Target>::iterator iter =
9513 targets.find(currentTarget);
9514 if (iter == targets.end())
9515 {
9516 error("Initial target '%s' not found",
9517 currentTarget.c_str());
9518 return false;
9519 }
9521 //Now run
9522 Target target = iter->second;
9523 std::set<String> targetsCompleted;
9524 if (!executeTarget(target, targetsCompleted))
9525 {
9526 return false;
9527 }
9529 status("######## EXECUTE COMPLETE");
9530 return true;
9531 }
9536 /**
9537 *
9538 */
9539 bool Make::checkTargetDependencies(Target &target,
9540 std::vector<String> &depList)
9541 {
9542 String tgtName = target.getName().c_str();
9543 depList.push_back(tgtName);
9545 std::vector<String> deps = target.getDependencies();
9546 for (unsigned int i=0 ; i<deps.size() ; i++)
9547 {
9548 String dep = deps[i];
9549 //First thing entered was the starting Target
9550 if (dep == depList[0])
9551 {
9552 error("Circular dependency '%s' found at '%s'",
9553 dep.c_str(), tgtName.c_str());
9554 std::vector<String>::iterator diter;
9555 for (diter=depList.begin() ; diter!=depList.end() ; diter++)
9556 {
9557 error(" %s", diter->c_str());
9558 }
9559 return false;
9560 }
9562 std::map<String, Target> &tgts =
9563 target.getParent().getTargets();
9564 std::map<String, Target>::iterator titer = tgts.find(dep);
9565 if (titer == tgts.end())
9566 {
9567 error("Target '%s' dependency '%s' not found",
9568 tgtName.c_str(), dep.c_str());
9569 return false;
9570 }
9571 if (!checkTargetDependencies(titer->second, depList))
9572 {
9573 return false;
9574 }
9575 }
9576 return true;
9577 }
9583 static int getword(int pos, const String &inbuf, String &result)
9584 {
9585 int p = pos;
9586 int len = (int)inbuf.size();
9587 String val;
9588 while (p < len)
9589 {
9590 char ch = inbuf[p];
9591 if (!isalnum(ch) && ch!='.' && ch!='_')
9592 break;
9593 val.push_back(ch);
9594 p++;
9595 }
9596 result = val;
9597 return p;
9598 }
9603 /**
9604 *
9605 */
9606 bool Make::parsePropertyFile(const String &fileName,
9607 const String &prefix)
9608 {
9609 FILE *f = fopen(fileName.c_str(), "r");
9610 if (!f)
9611 {
9612 error("could not open property file %s", fileName.c_str());
9613 return false;
9614 }
9615 int linenr = 0;
9616 while (!feof(f))
9617 {
9618 char buf[256];
9619 if (!fgets(buf, 255, f))
9620 break;
9621 linenr++;
9622 String s = buf;
9623 s = trim(s);
9624 int len = s.size();
9625 if (len == 0)
9626 continue;
9627 if (s[0] == '#')
9628 continue;
9629 String key;
9630 String val;
9631 int p = 0;
9632 int p2 = getword(p, s, key);
9633 if (p2 <= p)
9634 {
9635 error("property file %s, line %d: expected keyword",
9636 fileName.c_str(), linenr);
9637 return false;
9638 }
9639 if (prefix.size() > 0)
9640 {
9641 key.insert(0, prefix);
9642 }
9644 //skip whitespace
9645 for (p=p2 ; p<len ; p++)
9646 if (!isspace(s[p]))
9647 break;
9649 if (p>=len || s[p]!='=')
9650 {
9651 error("property file %s, line %d: expected '='",
9652 fileName.c_str(), linenr);
9653 return false;
9654 }
9655 p++;
9657 //skip whitespace
9658 for ( ; p<len ; p++)
9659 if (!isspace(s[p]))
9660 break;
9662 /* This way expects a word after the =
9663 p2 = getword(p, s, val);
9664 if (p2 <= p)
9665 {
9666 error("property file %s, line %d: expected value",
9667 fileName.c_str(), linenr);
9668 return false;
9669 }
9670 */
9671 // This way gets the rest of the line after the =
9672 if (p>=len)
9673 {
9674 error("property file %s, line %d: expected value",
9675 fileName.c_str(), linenr);
9676 return false;
9677 }
9678 val = s.substr(p);
9679 if (key.size()==0)
9680 continue;
9681 //allow property to be set, even if val=""
9683 //trace("key:'%s' val:'%s'", key.c_str(), val.c_str());
9684 //See if we wanted to overload this property
9685 std::map<String, String>::iterator iter =
9686 specifiedProperties.find(key);
9687 if (iter!=specifiedProperties.end())
9688 {
9689 val = iter->second;
9690 status("overloading property '%s' = '%s'",
9691 key.c_str(), val.c_str());
9692 }
9693 properties[key] = val;
9694 }
9695 fclose(f);
9696 return true;
9697 }
9702 /**
9703 *
9704 */
9705 bool Make::parseProperty(Element *elem)
9706 {
9707 std::vector<Attribute> &attrs = elem->getAttributes();
9708 for (unsigned int i=0 ; i<attrs.size() ; i++)
9709 {
9710 String attrName = attrs[i].getName();
9711 String attrVal = attrs[i].getValue();
9713 if (attrName == "name")
9714 {
9715 String val;
9716 if (!getAttribute(elem, "value", val))
9717 return false;
9718 if (val.size() > 0)
9719 {
9720 properties[attrVal] = val;
9721 }
9722 else
9723 {
9724 if (!getAttribute(elem, "location", val))
9725 return false;
9726 //let the property exist, even if not defined
9727 properties[attrVal] = val;
9728 }
9729 //See if we wanted to overload this property
9730 std::map<String, String>::iterator iter =
9731 specifiedProperties.find(attrVal);
9732 if (iter != specifiedProperties.end())
9733 {
9734 val = iter->second;
9735 status("overloading property '%s' = '%s'",
9736 attrVal.c_str(), val.c_str());
9737 properties[attrVal] = val;
9738 }
9739 }
9740 else if (attrName == "file")
9741 {
9742 String prefix;
9743 if (!getAttribute(elem, "prefix", prefix))
9744 return false;
9745 if (prefix.size() > 0)
9746 {
9747 if (prefix[prefix.size()-1] != '.')
9748 prefix.push_back('.');
9749 }
9750 if (!parsePropertyFile(attrName, prefix))
9751 return false;
9752 }
9753 else if (attrName == "environment")
9754 {
9755 if (attrVal.find('.') != attrVal.npos)
9756 {
9757 error("environment prefix cannot have a '.' in it");
9758 return false;
9759 }
9760 envPrefix = attrVal;
9761 envPrefix.push_back('.');
9762 }
9763 else if (attrName == "pkg-config")
9764 {
9765 if (attrVal.find('.') != attrVal.npos)
9766 {
9767 error("pkg-config prefix cannot have a '.' in it");
9768 return false;
9769 }
9770 pcPrefix = attrVal;
9771 pcPrefix.push_back('.');
9772 }
9773 else if (attrName == "pkg-config-cflags")
9774 {
9775 if (attrVal.find('.') != attrVal.npos)
9776 {
9777 error("pkg-config-cflags prefix cannot have a '.' in it");
9778 return false;
9779 }
9780 pccPrefix = attrVal;
9781 pccPrefix.push_back('.');
9782 }
9783 else if (attrName == "pkg-config-libs")
9784 {
9785 if (attrVal.find('.') != attrVal.npos)
9786 {
9787 error("pkg-config-libs prefix cannot have a '.' in it");
9788 return false;
9789 }
9790 pclPrefix = attrVal;
9791 pclPrefix.push_back('.');
9792 }
9793 }
9795 return true;
9796 }
9801 /**
9802 *
9803 */
9804 bool Make::parseFile()
9805 {
9806 status("######## PARSE : %s", uri.getPath().c_str());
9808 setLine(0);
9810 Parser parser;
9811 Element *root = parser.parseFile(uri.getNativePath());
9812 if (!root)
9813 {
9814 error("Could not open %s for reading",
9815 uri.getNativePath().c_str());
9816 return false;
9817 }
9819 setLine(root->getLine());
9821 if (root->getChildren().size()==0 ||
9822 root->getChildren()[0]->getName()!="project")
9823 {
9824 error("Main xml element should be <project>");
9825 delete root;
9826 return false;
9827 }
9829 //########## Project attributes
9830 Element *project = root->getChildren()[0];
9831 String s = project->getAttribute("name");
9832 if (s.size() > 0)
9833 projectName = s;
9834 s = project->getAttribute("default");
9835 if (s.size() > 0)
9836 defaultTarget = s;
9837 s = project->getAttribute("basedir");
9838 if (s.size() > 0)
9839 baseDir = s;
9841 //######### PARSE MEMBERS
9842 std::vector<Element *> children = project->getChildren();
9843 for (unsigned int i=0 ; i<children.size() ; i++)
9844 {
9845 Element *elem = children[i];
9846 setLine(elem->getLine());
9847 String tagName = elem->getName();
9849 //########## DESCRIPTION
9850 if (tagName == "description")
9851 {
9852 description = parser.trim(elem->getValue());
9853 }
9855 //######### PROPERTY
9856 else if (tagName == "property")
9857 {
9858 if (!parseProperty(elem))
9859 return false;
9860 }
9862 //######### TARGET
9863 else if (tagName == "target")
9864 {
9865 String tname = elem->getAttribute("name");
9866 String tdesc = elem->getAttribute("description");
9867 String tdeps = elem->getAttribute("depends");
9868 String tif = elem->getAttribute("if");
9869 String tunless = elem->getAttribute("unless");
9870 Target target(*this);
9871 target.setName(tname);
9872 target.setDescription(tdesc);
9873 target.parseDependencies(tdeps);
9874 target.setIf(tif);
9875 target.setUnless(tunless);
9876 std::vector<Element *> telems = elem->getChildren();
9877 for (unsigned int i=0 ; i<telems.size() ; i++)
9878 {
9879 Element *telem = telems[i];
9880 Task breeder(*this);
9881 Task *task = breeder.createTask(telem, telem->getLine());
9882 if (!task)
9883 return false;
9884 allTasks.push_back(task);
9885 target.addTask(task);
9886 }
9888 //Check name
9889 if (tname.size() == 0)
9890 {
9891 error("no name for target");
9892 return false;
9893 }
9894 //Check for duplicate name
9895 if (targets.find(tname) != targets.end())
9896 {
9897 error("target '%s' already defined", tname.c_str());
9898 return false;
9899 }
9900 //more work than targets[tname]=target, but avoids default allocator
9901 targets.insert(std::make_pair<String, Target>(tname, target));
9902 }
9903 //######### none of the above
9904 else
9905 {
9906 error("unknown toplevel tag: <%s>", tagName.c_str());
9907 return false;
9908 }
9910 }
9912 std::map<String, Target>::iterator iter;
9913 for (iter = targets.begin() ; iter!= targets.end() ; iter++)
9914 {
9915 Target tgt = iter->second;
9916 std::vector<String> depList;
9917 if (!checkTargetDependencies(tgt, depList))
9918 {
9919 return false;
9920 }
9921 }
9924 delete root;
9925 status("######## PARSE COMPLETE");
9926 return true;
9927 }
9930 /**
9931 * Overload a <property>
9932 */
9933 bool Make::specifyProperty(const String &name, const String &value)
9934 {
9935 if (specifiedProperties.find(name) != specifiedProperties.end())
9936 {
9937 error("Property %s already specified", name.c_str());
9938 return false;
9939 }
9940 specifiedProperties[name] = value;
9941 return true;
9942 }
9946 /**
9947 *
9948 */
9949 bool Make::run()
9950 {
9951 if (!parseFile())
9952 return false;
9954 if (!execute())
9955 return false;
9957 return true;
9958 }
9963 /**
9964 * Get a formatted MM:SS.sss time elapsed string
9965 */
9966 static String
9967 timeDiffString(struct timeval &x, struct timeval &y)
9968 {
9969 long microsX = x.tv_usec;
9970 long secondsX = x.tv_sec;
9971 long microsY = y.tv_usec;
9972 long secondsY = y.tv_sec;
9973 if (microsX < microsY)
9974 {
9975 microsX += 1000000;
9976 secondsX -= 1;
9977 }
9979 int seconds = (int)(secondsX - secondsY);
9980 int millis = (int)((microsX - microsY)/1000);
9982 int minutes = seconds/60;
9983 seconds -= minutes*60;
9984 char buf[80];
9985 snprintf(buf, 79, "%dm %d.%03ds", minutes, seconds, millis);
9986 String ret = buf;
9987 return ret;
9989 }
9991 /**
9992 *
9993 */
9994 bool Make::run(const String &target)
9995 {
9996 status("####################################################");
9997 status("# %s", version().c_str());
9998 status("####################################################");
9999 struct timeval timeStart, timeEnd;
10000 ::gettimeofday(&timeStart, NULL);
10001 specifiedTarget = target;
10002 if (!run())
10003 return false;
10004 ::gettimeofday(&timeEnd, NULL);
10005 String timeStr = timeDiffString(timeEnd, timeStart);
10006 status("####################################################");
10007 status("# BuildTool Completed : %s", timeStr.c_str());
10008 status("####################################################");
10009 return true;
10010 }
10018 }// namespace buildtool
10019 //########################################################################
10020 //# M A I N
10021 //########################################################################
10023 typedef buildtool::String String;
10025 /**
10026 * Format an error message in printf() style
10027 */
10028 static void error(const char *fmt, ...)
10029 {
10030 va_list ap;
10031 va_start(ap, fmt);
10032 fprintf(stderr, "BuildTool error: ");
10033 vfprintf(stderr, fmt, ap);
10034 fprintf(stderr, "\n");
10035 va_end(ap);
10036 }
10039 static bool parseProperty(const String &s, String &name, String &val)
10040 {
10041 int len = s.size();
10042 int i;
10043 for (i=0 ; i<len ; i++)
10044 {
10045 char ch = s[i];
10046 if (ch == '=')
10047 break;
10048 name.push_back(ch);
10049 }
10050 if (i>=len || s[i]!='=')
10051 {
10052 error("property requires -Dname=value");
10053 return false;
10054 }
10055 i++;
10056 for ( ; i<len ; i++)
10057 {
10058 char ch = s[i];
10059 val.push_back(ch);
10060 }
10061 return true;
10062 }
10065 /**
10066 * Compare a buffer with a key, for the length of the key
10067 */
10068 static bool sequ(const String &buf, const char *key)
10069 {
10070 int len = buf.size();
10071 for (int i=0 ; key[i] && i<len ; i++)
10072 {
10073 if (key[i] != buf[i])
10074 return false;
10075 }
10076 return true;
10077 }
10079 static void usage(int argc, char **argv)
10080 {
10081 printf("usage:\n");
10082 printf(" %s [options] [target]\n", argv[0]);
10083 printf("Options:\n");
10084 printf(" -help, -h print this message\n");
10085 printf(" -version print the version information and exit\n");
10086 printf(" -file <file> use given buildfile\n");
10087 printf(" -f <file> ''\n");
10088 printf(" -D<property>=<value> use value for given property\n");
10089 }
10094 /**
10095 * Parse the command-line args, get our options,
10096 * and run this thing
10097 */
10098 static bool parseOptions(int argc, char **argv)
10099 {
10100 if (argc < 1)
10101 {
10102 error("Cannot parse arguments");
10103 return false;
10104 }
10106 buildtool::Make make;
10108 String target;
10110 //char *progName = argv[0];
10111 for (int i=1 ; i<argc ; i++)
10112 {
10113 String arg = argv[i];
10114 if (arg.size()>1 && arg[0]=='-')
10115 {
10116 if (arg == "-h" || arg == "-help")
10117 {
10118 usage(argc,argv);
10119 return true;
10120 }
10121 else if (arg == "-version")
10122 {
10123 printf("%s", make.version().c_str());
10124 return true;
10125 }
10126 else if (arg == "-f" || arg == "-file")
10127 {
10128 if (i>=argc)
10129 {
10130 usage(argc, argv);
10131 return false;
10132 }
10133 i++; //eat option
10134 make.setURI(argv[i]);
10135 }
10136 else if (arg.size()>2 && sequ(arg, "-D"))
10137 {
10138 String s = arg.substr(2, arg.size());
10139 String name, value;
10140 if (!parseProperty(s, name, value))
10141 {
10142 usage(argc, argv);
10143 return false;
10144 }
10145 if (!make.specifyProperty(name, value))
10146 return false;
10147 }
10148 else
10149 {
10150 error("Unknown option:%s", arg.c_str());
10151 return false;
10152 }
10153 }
10154 else
10155 {
10156 if (target.size()>0)
10157 {
10158 error("only one initial target");
10159 usage(argc, argv);
10160 return false;
10161 }
10162 target = arg;
10163 }
10164 }
10166 //We have the options. Now execute them
10167 if (!make.run(target))
10168 return false;
10170 return true;
10171 }
10176 /*
10177 static bool runMake()
10178 {
10179 buildtool::Make make;
10180 if (!make.run())
10181 return false;
10182 return true;
10183 }
10186 static bool pkgConfigTest()
10187 {
10188 buildtool::PkgConfig pkgConfig;
10189 if (!pkgConfig.readFile("gtk+-2.0.pc"))
10190 return false;
10191 return true;
10192 }
10196 static bool depTest()
10197 {
10198 buildtool::DepTool deptool;
10199 deptool.setSourceDirectory("/dev/ink/inkscape/src");
10200 if (!deptool.generateDependencies("build.dep"))
10201 return false;
10202 std::vector<buildtool::FileRec> res =
10203 deptool.loadDepFile("build.dep");
10204 if (res.size() == 0)
10205 return false;
10206 return true;
10207 }
10209 static bool popenTest()
10210 {
10211 buildtool::Make make;
10212 buildtool::String out, err;
10213 bool ret = make.executeCommand("gcc xx.cpp", "", out, err);
10214 printf("Popen test:%d '%s' '%s'\n", ret, out.c_str(), err.c_str());
10215 return true;
10216 }
10219 static bool propFileTest()
10220 {
10221 buildtool::Make make;
10222 make.parsePropertyFile("test.prop", "test.");
10223 return true;
10224 }
10225 */
10227 int main(int argc, char **argv)
10228 {
10230 if (!parseOptions(argc, argv))
10231 return 1;
10232 /*
10233 if (!popenTest())
10234 return 1;
10236 if (!depTest())
10237 return 1;
10238 if (!propFileTest())
10239 return 1;
10240 if (runMake())
10241 return 1;
10242 */
10243 return 0;
10244 }
10247 //########################################################################
10248 //# E N D
10249 //########################################################################