1 /**
2 * Simple build automation tool.
3 *
4 * Authors:
5 * Bob Jamison
6 *
7 * Copyright (C) 2006-2008 Bob Jamison
8 *
9 * This library is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU Lesser General Public
11 * License as published by the Free Software Foundation; either
12 * version 2.1 of the License, or (at your option) any later version.
13 *
14 * This library is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * Lesser General Public License for more details.
18 *
19 * You should have received a copy of the GNU Lesser General Public
20 * License along with this library; if not, write to the Free Software
21 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
22 */
24 /**
25 * To use this file, compile with:
26 * <pre>
27 * g++ -O3 buildtool.cpp -o btool.exe
28 * (or whatever your compiler might be)
29 * Then
30 * btool
31 * or
32 * btool {target}
33 *
34 * Note: if you are using MinGW, and a not very recent version of it,
35 * gettimeofday() might be missing. If so, just build this file with
36 * this command:
37 * g++ -O3 -DNEED_GETTIMEOFDAY buildtool.cpp -o btool.exe
38 *
39 */
41 #define BUILDTOOL_VERSION "BuildTool v0.9.4"
43 #include <stdio.h>
44 #include <fcntl.h>
45 #include <unistd.h>
46 #include <stdarg.h>
47 #include <sys/stat.h>
48 #include <time.h>
49 #include <sys/time.h>
50 #include <utime.h>
51 #include <dirent.h>
53 #include <string>
54 #include <map>
55 #include <set>
56 #include <vector>
57 #include <algorithm>
60 #ifdef __WIN32__
61 #include <windows.h>
62 #else
63 #include <sys/wait.h>
64 #endif
67 #include <errno.h>
70 //########################################################################
71 //# Definition of gettimeofday() for those who don't have it
72 //########################################################################
73 #ifdef NEED_GETTIMEOFDAY
74 #include <sys/timeb.h>
76 struct timezone {
77 int tz_minuteswest; /* minutes west of Greenwich */
78 int tz_dsttime; /* type of dst correction */
79 };
81 static int gettimeofday (struct timeval *tv, struct timezone *tz)
82 {
83 struct _timeb tb;
85 if (!tv)
86 return (-1);
88 _ftime (&tb);
89 tv->tv_sec = tb.time;
90 tv->tv_usec = tb.millitm * 1000 + 500;
91 if (tz)
92 {
93 tz->tz_minuteswest = -60 * _timezone;
94 tz->tz_dsttime = _daylight;
95 }
96 return 0;
97 }
99 #endif
107 namespace buildtool
108 {
113 //########################################################################
114 //########################################################################
115 //## R E G E X P
116 //########################################################################
117 //########################################################################
119 /**
120 * This is the T-Rex regular expression library, which we
121 * gratefully acknowledge. It's clean code and small size allow
122 * us to embed it in BuildTool without adding a dependency
123 *
124 */
126 //begin trex.h
128 #ifndef _TREX_H_
129 #define _TREX_H_
130 /***************************************************************
131 T-Rex a tiny regular expression library
133 Copyright (C) 2003-2006 Alberto Demichelis
135 This software is provided 'as-is', without any express
136 or implied warranty. In no event will the authors be held
137 liable for any damages arising from the use of this software.
139 Permission is granted to anyone to use this software for
140 any purpose, including commercial applications, and to alter
141 it and redistribute it freely, subject to the following restrictions:
143 1. The origin of this software must not be misrepresented;
144 you must not claim that you wrote the original software.
145 If you use this software in a product, an acknowledgment
146 in the product documentation would be appreciated but
147 is not required.
149 2. Altered source versions must be plainly marked as such,
150 and must not be misrepresented as being the original software.
152 3. This notice may not be removed or altered from any
153 source distribution.
155 ****************************************************************/
157 #ifdef _UNICODE
158 #define TRexChar unsigned short
159 #define MAX_CHAR 0xFFFF
160 #define _TREXC(c) L##c
161 #define trex_strlen wcslen
162 #define trex_printf wprintf
163 #else
164 #define TRexChar char
165 #define MAX_CHAR 0xFF
166 #define _TREXC(c) (c)
167 #define trex_strlen strlen
168 #define trex_printf printf
169 #endif
171 #ifndef TREX_API
172 #define TREX_API extern
173 #endif
175 #define TRex_True 1
176 #define TRex_False 0
178 typedef unsigned int TRexBool;
179 typedef struct TRex TRex;
181 typedef struct {
182 const TRexChar *begin;
183 int len;
184 } TRexMatch;
186 TREX_API TRex *trex_compile(const TRexChar *pattern,const TRexChar **error);
187 TREX_API void trex_free(TRex *exp);
188 TREX_API TRexBool trex_match(TRex* exp,const TRexChar* text);
189 TREX_API TRexBool trex_search(TRex* exp,const TRexChar* text, const TRexChar** out_begin, const TRexChar** out_end);
190 TREX_API TRexBool trex_searchrange(TRex* exp,const TRexChar* text_begin,const TRexChar* text_end,const TRexChar** out_begin, const TRexChar** out_end);
191 TREX_API int trex_getsubexpcount(TRex* exp);
192 TREX_API TRexBool trex_getsubexp(TRex* exp, int n, TRexMatch *subexp);
194 #endif
196 //end trex.h
198 //start trex.c
201 #include <stdio.h>
202 #include <string>
204 /* see copyright notice in trex.h */
205 #include <string.h>
206 #include <stdlib.h>
207 #include <ctype.h>
208 #include <setjmp.h>
209 //#include "trex.h"
211 #ifdef _UINCODE
212 #define scisprint iswprint
213 #define scstrlen wcslen
214 #define scprintf wprintf
215 #define _SC(x) L(x)
216 #else
217 #define scisprint isprint
218 #define scstrlen strlen
219 #define scprintf printf
220 #define _SC(x) (x)
221 #endif
223 #ifdef _DEBUG
224 #include <stdio.h>
226 static const TRexChar *g_nnames[] =
227 {
228 _SC("NONE"),_SC("OP_GREEDY"), _SC("OP_OR"),
229 _SC("OP_EXPR"),_SC("OP_NOCAPEXPR"),_SC("OP_DOT"), _SC("OP_CLASS"),
230 _SC("OP_CCLASS"),_SC("OP_NCLASS"),_SC("OP_RANGE"),_SC("OP_CHAR"),
231 _SC("OP_EOL"),_SC("OP_BOL"),_SC("OP_WB")
232 };
234 #endif
235 #define OP_GREEDY (MAX_CHAR+1) // * + ? {n}
236 #define OP_OR (MAX_CHAR+2)
237 #define OP_EXPR (MAX_CHAR+3) //parentesis ()
238 #define OP_NOCAPEXPR (MAX_CHAR+4) //parentesis (?:)
239 #define OP_DOT (MAX_CHAR+5)
240 #define OP_CLASS (MAX_CHAR+6)
241 #define OP_CCLASS (MAX_CHAR+7)
242 #define OP_NCLASS (MAX_CHAR+8) //negates class the [^
243 #define OP_RANGE (MAX_CHAR+9)
244 #define OP_CHAR (MAX_CHAR+10)
245 #define OP_EOL (MAX_CHAR+11)
246 #define OP_BOL (MAX_CHAR+12)
247 #define OP_WB (MAX_CHAR+13)
249 #define TREX_SYMBOL_ANY_CHAR ('.')
250 #define TREX_SYMBOL_GREEDY_ONE_OR_MORE ('+')
251 #define TREX_SYMBOL_GREEDY_ZERO_OR_MORE ('*')
252 #define TREX_SYMBOL_GREEDY_ZERO_OR_ONE ('?')
253 #define TREX_SYMBOL_BRANCH ('|')
254 #define TREX_SYMBOL_END_OF_STRING ('$')
255 #define TREX_SYMBOL_BEGINNING_OF_STRING ('^')
256 #define TREX_SYMBOL_ESCAPE_CHAR ('\\')
259 typedef int TRexNodeType;
261 typedef struct tagTRexNode{
262 TRexNodeType type;
263 int left;
264 int right;
265 int next;
266 }TRexNode;
268 struct TRex{
269 const TRexChar *_eol;
270 const TRexChar *_bol;
271 const TRexChar *_p;
272 int _first;
273 int _op;
274 TRexNode *_nodes;
275 int _nallocated;
276 int _nsize;
277 int _nsubexpr;
278 TRexMatch *_matches;
279 int _currsubexp;
280 void *_jmpbuf;
281 const TRexChar **_error;
282 };
284 static int trex_list(TRex *exp);
286 static int trex_newnode(TRex *exp, TRexNodeType type)
287 {
288 TRexNode n;
289 int newid;
290 n.type = type;
291 n.next = n.right = n.left = -1;
292 if(type == OP_EXPR)
293 n.right = exp->_nsubexpr++;
294 if(exp->_nallocated < (exp->_nsize + 1)) {
295 //int oldsize = exp->_nallocated;
296 exp->_nallocated *= 2;
297 exp->_nodes = (TRexNode *)realloc(exp->_nodes, exp->_nallocated * sizeof(TRexNode));
298 }
299 exp->_nodes[exp->_nsize++] = n;
300 newid = exp->_nsize - 1;
301 return (int)newid;
302 }
304 static void trex_error(TRex *exp,const TRexChar *error)
305 {
306 if(exp->_error) *exp->_error = error;
307 longjmp(*((jmp_buf*)exp->_jmpbuf),-1);
308 }
310 static void trex_expect(TRex *exp, int n){
311 if((*exp->_p) != n)
312 trex_error(exp, _SC("expected paren"));
313 exp->_p++;
314 }
316 static TRexChar trex_escapechar(TRex *exp)
317 {
318 if(*exp->_p == TREX_SYMBOL_ESCAPE_CHAR){
319 exp->_p++;
320 switch(*exp->_p) {
321 case 'v': exp->_p++; return '\v';
322 case 'n': exp->_p++; return '\n';
323 case 't': exp->_p++; return '\t';
324 case 'r': exp->_p++; return '\r';
325 case 'f': exp->_p++; return '\f';
326 default: return (*exp->_p++);
327 }
328 } else if(!scisprint(*exp->_p)) trex_error(exp,_SC("letter expected"));
329 return (*exp->_p++);
330 }
332 static int trex_charclass(TRex *exp,int classid)
333 {
334 int n = trex_newnode(exp,OP_CCLASS);
335 exp->_nodes[n].left = classid;
336 return n;
337 }
339 static int trex_charnode(TRex *exp,TRexBool isclass)
340 {
341 TRexChar t;
342 if(*exp->_p == TREX_SYMBOL_ESCAPE_CHAR) {
343 exp->_p++;
344 switch(*exp->_p) {
345 case 'n': exp->_p++; return trex_newnode(exp,'\n');
346 case 't': exp->_p++; return trex_newnode(exp,'\t');
347 case 'r': exp->_p++; return trex_newnode(exp,'\r');
348 case 'f': exp->_p++; return trex_newnode(exp,'\f');
349 case 'v': exp->_p++; return trex_newnode(exp,'\v');
350 case 'a': case 'A': case 'w': case 'W': case 's': case 'S':
351 case 'd': case 'D': case 'x': case 'X': case 'c': case 'C':
352 case 'p': case 'P': case 'l': case 'u':
353 {
354 t = *exp->_p; exp->_p++;
355 return trex_charclass(exp,t);
356 }
357 case 'b':
358 case 'B':
359 if(!isclass) {
360 int node = trex_newnode(exp,OP_WB);
361 exp->_nodes[node].left = *exp->_p;
362 exp->_p++;
363 return node;
364 } //else default
365 default:
366 t = *exp->_p; exp->_p++;
367 return trex_newnode(exp,t);
368 }
369 }
370 else if(!scisprint(*exp->_p)) {
372 trex_error(exp,_SC("letter expected"));
373 }
374 t = *exp->_p; exp->_p++;
375 return trex_newnode(exp,t);
376 }
377 static int trex_class(TRex *exp)
378 {
379 int ret = -1;
380 int first = -1,chain;
381 if(*exp->_p == TREX_SYMBOL_BEGINNING_OF_STRING){
382 ret = trex_newnode(exp,OP_NCLASS);
383 exp->_p++;
384 }else ret = trex_newnode(exp,OP_CLASS);
386 if(*exp->_p == ']') trex_error(exp,_SC("empty class"));
387 chain = ret;
388 while(*exp->_p != ']' && exp->_p != exp->_eol) {
389 if(*exp->_p == '-' && first != -1){
390 int r,t;
391 if(*exp->_p++ == ']') trex_error(exp,_SC("unfinished range"));
392 r = trex_newnode(exp,OP_RANGE);
393 if(first>*exp->_p) trex_error(exp,_SC("invalid range"));
394 if(exp->_nodes[first].type == OP_CCLASS) trex_error(exp,_SC("cannot use character classes in ranges"));
395 exp->_nodes[r].left = exp->_nodes[first].type;
396 t = trex_escapechar(exp);
397 exp->_nodes[r].right = t;
398 exp->_nodes[chain].next = r;
399 chain = r;
400 first = -1;
401 }
402 else{
403 if(first!=-1){
404 int c = first;
405 exp->_nodes[chain].next = c;
406 chain = c;
407 first = trex_charnode(exp,TRex_True);
408 }
409 else{
410 first = trex_charnode(exp,TRex_True);
411 }
412 }
413 }
414 if(first!=-1){
415 int c = first;
416 exp->_nodes[chain].next = c;
417 chain = c;
418 first = -1;
419 }
420 /* hack? */
421 exp->_nodes[ret].left = exp->_nodes[ret].next;
422 exp->_nodes[ret].next = -1;
423 return ret;
424 }
426 static int trex_parsenumber(TRex *exp)
427 {
428 int ret = *exp->_p-'0';
429 int positions = 10;
430 exp->_p++;
431 while(isdigit(*exp->_p)) {
432 ret = ret*10+(*exp->_p++-'0');
433 if(positions==1000000000) trex_error(exp,_SC("overflow in numeric constant"));
434 positions *= 10;
435 };
436 return ret;
437 }
439 static int trex_element(TRex *exp)
440 {
441 int ret = -1;
442 switch(*exp->_p)
443 {
444 case '(': {
445 int expr,newn;
446 exp->_p++;
449 if(*exp->_p =='?') {
450 exp->_p++;
451 trex_expect(exp,':');
452 expr = trex_newnode(exp,OP_NOCAPEXPR);
453 }
454 else
455 expr = trex_newnode(exp,OP_EXPR);
456 newn = trex_list(exp);
457 exp->_nodes[expr].left = newn;
458 ret = expr;
459 trex_expect(exp,')');
460 }
461 break;
462 case '[':
463 exp->_p++;
464 ret = trex_class(exp);
465 trex_expect(exp,']');
466 break;
467 case TREX_SYMBOL_END_OF_STRING: exp->_p++; ret = trex_newnode(exp,OP_EOL);break;
468 case TREX_SYMBOL_ANY_CHAR: exp->_p++; ret = trex_newnode(exp,OP_DOT);break;
469 default:
470 ret = trex_charnode(exp,TRex_False);
471 break;
472 }
474 {
475 int op;
476 TRexBool isgreedy = TRex_False;
477 unsigned short p0 = 0, p1 = 0;
478 switch(*exp->_p){
479 case TREX_SYMBOL_GREEDY_ZERO_OR_MORE: p0 = 0; p1 = 0xFFFF; exp->_p++; isgreedy = TRex_True; break;
480 case TREX_SYMBOL_GREEDY_ONE_OR_MORE: p0 = 1; p1 = 0xFFFF; exp->_p++; isgreedy = TRex_True; break;
481 case TREX_SYMBOL_GREEDY_ZERO_OR_ONE: p0 = 0; p1 = 1; exp->_p++; isgreedy = TRex_True; break;
482 case '{':
483 exp->_p++;
484 if(!isdigit(*exp->_p)) trex_error(exp,_SC("number expected"));
485 p0 = (unsigned short)trex_parsenumber(exp);
486 /*******************************/
487 switch(*exp->_p) {
488 case '}':
489 p1 = p0; exp->_p++;
490 break;
491 case ',':
492 exp->_p++;
493 p1 = 0xFFFF;
494 if(isdigit(*exp->_p)){
495 p1 = (unsigned short)trex_parsenumber(exp);
496 }
497 trex_expect(exp,'}');
498 break;
499 default:
500 trex_error(exp,_SC(", or } expected"));
501 }
502 /*******************************/
503 isgreedy = TRex_True;
504 break;
506 }
507 if(isgreedy) {
508 int nnode = trex_newnode(exp,OP_GREEDY);
509 op = OP_GREEDY;
510 exp->_nodes[nnode].left = ret;
511 exp->_nodes[nnode].right = ((p0)<<16)|p1;
512 ret = nnode;
513 }
514 }
515 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')) {
516 int nnode = trex_element(exp);
517 exp->_nodes[ret].next = nnode;
518 }
520 return ret;
521 }
523 static int trex_list(TRex *exp)
524 {
525 int ret=-1,e;
526 if(*exp->_p == TREX_SYMBOL_BEGINNING_OF_STRING) {
527 exp->_p++;
528 ret = trex_newnode(exp,OP_BOL);
529 }
530 e = trex_element(exp);
531 if(ret != -1) {
532 exp->_nodes[ret].next = e;
533 }
534 else ret = e;
536 if(*exp->_p == TREX_SYMBOL_BRANCH) {
537 int temp,tright;
538 exp->_p++;
539 temp = trex_newnode(exp,OP_OR);
540 exp->_nodes[temp].left = ret;
541 tright = trex_list(exp);
542 exp->_nodes[temp].right = tright;
543 ret = temp;
544 }
545 return ret;
546 }
548 static TRexBool trex_matchcclass(int cclass,TRexChar c)
549 {
550 switch(cclass) {
551 case 'a': return isalpha(c)?TRex_True:TRex_False;
552 case 'A': return !isalpha(c)?TRex_True:TRex_False;
553 case 'w': return (isalnum(c) || c == '_')?TRex_True:TRex_False;
554 case 'W': return (!isalnum(c) && c != '_')?TRex_True:TRex_False;
555 case 's': return isspace(c)?TRex_True:TRex_False;
556 case 'S': return !isspace(c)?TRex_True:TRex_False;
557 case 'd': return isdigit(c)?TRex_True:TRex_False;
558 case 'D': return !isdigit(c)?TRex_True:TRex_False;
559 case 'x': return isxdigit(c)?TRex_True:TRex_False;
560 case 'X': return !isxdigit(c)?TRex_True:TRex_False;
561 case 'c': return iscntrl(c)?TRex_True:TRex_False;
562 case 'C': return !iscntrl(c)?TRex_True:TRex_False;
563 case 'p': return ispunct(c)?TRex_True:TRex_False;
564 case 'P': return !ispunct(c)?TRex_True:TRex_False;
565 case 'l': return islower(c)?TRex_True:TRex_False;
566 case 'u': return isupper(c)?TRex_True:TRex_False;
567 }
568 return TRex_False; /*cannot happen*/
569 }
571 static TRexBool trex_matchclass(TRex* exp,TRexNode *node,TRexChar c)
572 {
573 do {
574 switch(node->type) {
575 case OP_RANGE:
576 if(c >= node->left && c <= node->right) return TRex_True;
577 break;
578 case OP_CCLASS:
579 if(trex_matchcclass(node->left,c)) return TRex_True;
580 break;
581 default:
582 if(c == node->type)return TRex_True;
583 }
584 } while((node->next != -1) && (node = &exp->_nodes[node->next]));
585 return TRex_False;
586 }
588 static const TRexChar *trex_matchnode(TRex* exp,TRexNode *node,const TRexChar *str,TRexNode *next)
589 {
591 TRexNodeType type = node->type;
592 switch(type) {
593 case OP_GREEDY: {
594 //TRexNode *greedystop = (node->next != -1) ? &exp->_nodes[node->next] : NULL;
595 TRexNode *greedystop = NULL;
596 int p0 = (node->right >> 16)&0x0000FFFF, p1 = node->right&0x0000FFFF, nmaches = 0;
597 const TRexChar *s=str, *good = str;
599 if(node->next != -1) {
600 greedystop = &exp->_nodes[node->next];
601 }
602 else {
603 greedystop = next;
604 }
606 while((nmaches == 0xFFFF || nmaches < p1)) {
608 const TRexChar *stop;
609 if(!(s = trex_matchnode(exp,&exp->_nodes[node->left],s,greedystop)))
610 break;
611 nmaches++;
612 good=s;
613 if(greedystop) {
614 //checks that 0 matches satisfy the expression(if so skips)
615 //if not would always stop(for instance if is a '?')
616 if(greedystop->type != OP_GREEDY ||
617 (greedystop->type == OP_GREEDY && ((greedystop->right >> 16)&0x0000FFFF) != 0))
618 {
619 TRexNode *gnext = NULL;
620 if(greedystop->next != -1) {
621 gnext = &exp->_nodes[greedystop->next];
622 }else if(next && next->next != -1){
623 gnext = &exp->_nodes[next->next];
624 }
625 stop = trex_matchnode(exp,greedystop,s,gnext);
626 if(stop) {
627 //if satisfied stop it
628 if(p0 == p1 && p0 == nmaches) break;
629 else if(nmaches >= p0 && p1 == 0xFFFF) break;
630 else if(nmaches >= p0 && nmaches <= p1) break;
631 }
632 }
633 }
635 if(s >= exp->_eol)
636 break;
637 }
638 if(p0 == p1 && p0 == nmaches) return good;
639 else if(nmaches >= p0 && p1 == 0xFFFF) return good;
640 else if(nmaches >= p0 && nmaches <= p1) return good;
641 return NULL;
642 }
643 case OP_OR: {
644 const TRexChar *asd = str;
645 TRexNode *temp=&exp->_nodes[node->left];
646 while( (asd = trex_matchnode(exp,temp,asd,NULL)) ) {
647 if(temp->next != -1)
648 temp = &exp->_nodes[temp->next];
649 else
650 return asd;
651 }
652 asd = str;
653 temp = &exp->_nodes[node->right];
654 while( (asd = trex_matchnode(exp,temp,asd,NULL)) ) {
655 if(temp->next != -1)
656 temp = &exp->_nodes[temp->next];
657 else
658 return asd;
659 }
660 return NULL;
661 break;
662 }
663 case OP_EXPR:
664 case OP_NOCAPEXPR:{
665 TRexNode *n = &exp->_nodes[node->left];
666 const TRexChar *cur = str;
667 int capture = -1;
668 if(node->type != OP_NOCAPEXPR && node->right == exp->_currsubexp) {
669 capture = exp->_currsubexp;
670 exp->_matches[capture].begin = cur;
671 exp->_currsubexp++;
672 }
674 do {
675 TRexNode *subnext = NULL;
676 if(n->next != -1) {
677 subnext = &exp->_nodes[n->next];
678 }else {
679 subnext = next;
680 }
681 if(!(cur = trex_matchnode(exp,n,cur,subnext))) {
682 if(capture != -1){
683 exp->_matches[capture].begin = 0;
684 exp->_matches[capture].len = 0;
685 }
686 return NULL;
687 }
688 } while((n->next != -1) && (n = &exp->_nodes[n->next]));
690 if(capture != -1)
691 exp->_matches[capture].len = cur - exp->_matches[capture].begin;
692 return cur;
693 }
694 case OP_WB:
695 if((str == exp->_bol && !isspace(*str))
696 || (str == exp->_eol && !isspace(*(str-1)))
697 || (!isspace(*str) && isspace(*(str+1)))
698 || (isspace(*str) && !isspace(*(str+1))) ) {
699 return (node->left == 'b')?str:NULL;
700 }
701 return (node->left == 'b')?NULL:str;
702 case OP_BOL:
703 if(str == exp->_bol) return str;
704 return NULL;
705 case OP_EOL:
706 if(str == exp->_eol) return str;
707 return NULL;
708 case OP_DOT:{
709 *str++;
710 }
711 return str;
712 case OP_NCLASS:
713 case OP_CLASS:
714 if(trex_matchclass(exp,&exp->_nodes[node->left],*str)?(type == OP_CLASS?TRex_True:TRex_False):(type == OP_NCLASS?TRex_True:TRex_False)) {
715 *str++;
716 return str;
717 }
718 return NULL;
719 case OP_CCLASS:
720 if(trex_matchcclass(node->left,*str)) {
721 *str++;
722 return str;
723 }
724 return NULL;
725 default: /* char */
726 if(*str != node->type) return NULL;
727 *str++;
728 return str;
729 }
730 return NULL;
731 }
733 /* public api */
734 TRex *trex_compile(const TRexChar *pattern,const TRexChar **error)
735 {
736 TRex *exp = (TRex *)malloc(sizeof(TRex));
737 exp->_eol = exp->_bol = NULL;
738 exp->_p = pattern;
739 exp->_nallocated = (int)scstrlen(pattern) * sizeof(TRexChar);
740 exp->_nodes = (TRexNode *)malloc(exp->_nallocated * sizeof(TRexNode));
741 exp->_nsize = 0;
742 exp->_matches = 0;
743 exp->_nsubexpr = 0;
744 exp->_first = trex_newnode(exp,OP_EXPR);
745 exp->_error = error;
746 exp->_jmpbuf = malloc(sizeof(jmp_buf));
747 if(setjmp(*((jmp_buf*)exp->_jmpbuf)) == 0) {
748 int res = trex_list(exp);
749 exp->_nodes[exp->_first].left = res;
750 if(*exp->_p!='\0')
751 trex_error(exp,_SC("unexpected character"));
752 #ifdef _DEBUG
753 {
754 int nsize,i;
755 TRexNode *t;
756 nsize = exp->_nsize;
757 t = &exp->_nodes[0];
758 scprintf(_SC("\n"));
759 for(i = 0;i < nsize; i++) {
760 if(exp->_nodes[i].type>MAX_CHAR)
761 scprintf(_SC("[%02d] %10s "),i,g_nnames[exp->_nodes[i].type-MAX_CHAR]);
762 else
763 scprintf(_SC("[%02d] %10c "),i,exp->_nodes[i].type);
764 scprintf(_SC("left %02d right %02d next %02d\n"),exp->_nodes[i].left,exp->_nodes[i].right,exp->_nodes[i].next);
765 }
766 scprintf(_SC("\n"));
767 }
768 #endif
769 exp->_matches = (TRexMatch *) malloc(exp->_nsubexpr * sizeof(TRexMatch));
770 memset(exp->_matches,0,exp->_nsubexpr * sizeof(TRexMatch));
771 }
772 else{
773 trex_free(exp);
774 return NULL;
775 }
776 return exp;
777 }
779 void trex_free(TRex *exp)
780 {
781 if(exp) {
782 if(exp->_nodes) free(exp->_nodes);
783 if(exp->_jmpbuf) free(exp->_jmpbuf);
784 if(exp->_matches) free(exp->_matches);
785 free(exp);
786 }
787 }
789 TRexBool trex_match(TRex* exp,const TRexChar* text)
790 {
791 const TRexChar* res = NULL;
792 exp->_bol = text;
793 exp->_eol = text + scstrlen(text);
794 exp->_currsubexp = 0;
795 res = trex_matchnode(exp,exp->_nodes,text,NULL);
796 if(res == NULL || res != exp->_eol)
797 return TRex_False;
798 return TRex_True;
799 }
801 TRexBool trex_searchrange(TRex* exp,const TRexChar* text_begin,const TRexChar* text_end,const TRexChar** out_begin, const TRexChar** out_end)
802 {
803 const TRexChar *cur = NULL;
804 int node = exp->_first;
805 if(text_begin >= text_end) return TRex_False;
806 exp->_bol = text_begin;
807 exp->_eol = text_end;
808 do {
809 cur = text_begin;
810 while(node != -1) {
811 exp->_currsubexp = 0;
812 cur = trex_matchnode(exp,&exp->_nodes[node],cur,NULL);
813 if(!cur)
814 break;
815 node = exp->_nodes[node].next;
816 }
817 *text_begin++;
818 } while(cur == NULL && text_begin != text_end);
820 if(cur == NULL)
821 return TRex_False;
823 --text_begin;
825 if(out_begin) *out_begin = text_begin;
826 if(out_end) *out_end = cur;
827 return TRex_True;
828 }
830 TRexBool trex_search(TRex* exp,const TRexChar* text, const TRexChar** out_begin, const TRexChar** out_end)
831 {
832 return trex_searchrange(exp,text,text + scstrlen(text),out_begin,out_end);
833 }
835 int trex_getsubexpcount(TRex* exp)
836 {
837 return exp->_nsubexpr;
838 }
840 TRexBool trex_getsubexp(TRex* exp, int n, TRexMatch *subexp)
841 {
842 if( n<0 || n >= exp->_nsubexpr) return TRex_False;
843 *subexp = exp->_matches[n];
844 return TRex_True;
845 }
848 //########################################################################
849 //########################################################################
850 //## E N D R E G E X P
851 //########################################################################
852 //########################################################################
858 //########################################################################
859 //########################################################################
860 //## X M L
861 //########################################################################
862 //########################################################################
864 // Note: This mini-dom library comes from Pedro, another little project
865 // of mine.
867 typedef std::string String;
868 typedef unsigned int XMLCh;
871 class Namespace
872 {
873 public:
874 Namespace()
875 {}
877 Namespace(const String &prefixArg, const String &namespaceURIArg)
878 {
879 prefix = prefixArg;
880 namespaceURI = namespaceURIArg;
881 }
883 Namespace(const Namespace &other)
884 {
885 assign(other);
886 }
888 Namespace &operator=(const Namespace &other)
889 {
890 assign(other);
891 return *this;
892 }
894 virtual ~Namespace()
895 {}
897 virtual String getPrefix()
898 { return prefix; }
900 virtual String getNamespaceURI()
901 { return namespaceURI; }
903 protected:
905 void assign(const Namespace &other)
906 {
907 prefix = other.prefix;
908 namespaceURI = other.namespaceURI;
909 }
911 String prefix;
912 String namespaceURI;
914 };
916 class Attribute
917 {
918 public:
919 Attribute()
920 {}
922 Attribute(const String &nameArg, const String &valueArg)
923 {
924 name = nameArg;
925 value = valueArg;
926 }
928 Attribute(const Attribute &other)
929 {
930 assign(other);
931 }
933 Attribute &operator=(const Attribute &other)
934 {
935 assign(other);
936 return *this;
937 }
939 virtual ~Attribute()
940 {}
942 virtual String getName()
943 { return name; }
945 virtual String getValue()
946 { return value; }
948 protected:
950 void assign(const Attribute &other)
951 {
952 name = other.name;
953 value = other.value;
954 }
956 String name;
957 String value;
959 };
962 class Element
963 {
964 friend class Parser;
966 public:
967 Element()
968 {
969 init();
970 }
972 Element(const String &nameArg)
973 {
974 init();
975 name = nameArg;
976 }
978 Element(const String &nameArg, const String &valueArg)
979 {
980 init();
981 name = nameArg;
982 value = valueArg;
983 }
985 Element(const Element &other)
986 {
987 assign(other);
988 }
990 Element &operator=(const Element &other)
991 {
992 assign(other);
993 return *this;
994 }
996 virtual Element *clone();
998 virtual ~Element()
999 {
1000 for (unsigned int i=0 ; i<children.size() ; i++)
1001 delete children[i];
1002 }
1004 virtual String getName()
1005 { return name; }
1007 virtual String getValue()
1008 { return value; }
1010 Element *getParent()
1011 { return parent; }
1013 std::vector<Element *> getChildren()
1014 { return children; }
1016 std::vector<Element *> findElements(const String &name);
1018 String getAttribute(const String &name);
1020 std::vector<Attribute> &getAttributes()
1021 { return attributes; }
1023 String getTagAttribute(const String &tagName, const String &attrName);
1025 String getTagValue(const String &tagName);
1027 void addChild(Element *child);
1029 void addAttribute(const String &name, const String &value);
1031 void addNamespace(const String &prefix, const String &namespaceURI);
1034 /**
1035 * Prettyprint an XML tree to an output stream. Elements are indented
1036 * according to element hierarchy.
1037 * @param f a stream to receive the output
1038 * @param elem the element to output
1039 */
1040 void writeIndented(FILE *f);
1042 /**
1043 * Prettyprint an XML tree to standard output. This is the equivalent of
1044 * writeIndented(stdout).
1045 * @param elem the element to output
1046 */
1047 void print();
1049 int getLine()
1050 { return line; }
1052 protected:
1054 void init()
1055 {
1056 parent = NULL;
1057 line = 0;
1058 }
1060 void assign(const Element &other)
1061 {
1062 parent = other.parent;
1063 children = other.children;
1064 attributes = other.attributes;
1065 namespaces = other.namespaces;
1066 name = other.name;
1067 value = other.value;
1068 line = other.line;
1069 }
1071 void findElementsRecursive(std::vector<Element *>&res, const String &name);
1073 void writeIndentedRecursive(FILE *f, int indent);
1075 Element *parent;
1077 std::vector<Element *>children;
1079 std::vector<Attribute> attributes;
1080 std::vector<Namespace> namespaces;
1082 String name;
1083 String value;
1085 int line;
1086 };
1092 class Parser
1093 {
1094 public:
1095 /**
1096 * Constructor
1097 */
1098 Parser()
1099 { init(); }
1101 virtual ~Parser()
1102 {}
1104 /**
1105 * Parse XML in a char buffer.
1106 * @param buf a character buffer to parse
1107 * @param pos position to start parsing
1108 * @param len number of chars, from pos, to parse.
1109 * @return a pointer to the root of the XML document;
1110 */
1111 Element *parse(const char *buf,int pos,int len);
1113 /**
1114 * Parse XML in a char buffer.
1115 * @param buf a character buffer to parse
1116 * @param pos position to start parsing
1117 * @param len number of chars, from pos, to parse.
1118 * @return a pointer to the root of the XML document;
1119 */
1120 Element *parse(const String &buf);
1122 /**
1123 * Parse a named XML file. The file is loaded like a data file;
1124 * the original format is not preserved.
1125 * @param fileName the name of the file to read
1126 * @return a pointer to the root of the XML document;
1127 */
1128 Element *parseFile(const String &fileName);
1130 /**
1131 * Utility method to preprocess a string for XML
1132 * output, escaping its entities.
1133 * @param str the string to encode
1134 */
1135 static String encode(const String &str);
1137 /**
1138 * Removes whitespace from beginning and end of a string
1139 */
1140 String trim(const String &s);
1142 private:
1144 void init()
1145 {
1146 keepGoing = true;
1147 currentNode = NULL;
1148 parselen = 0;
1149 parsebuf = NULL;
1150 currentPosition = 0;
1151 }
1153 int countLines(int begin, int end);
1155 void getLineAndColumn(int pos, int *lineNr, int *colNr);
1157 void error(const char *fmt, ...);
1159 int peek(int pos);
1161 int match(int pos, const char *text);
1163 int skipwhite(int p);
1165 int getWord(int p0, String &buf);
1167 int getQuoted(int p0, String &buf, int do_i_parse);
1169 int parseVersion(int p0);
1171 int parseDoctype(int p0);
1173 int parseElement(int p0, Element *par,int depth);
1175 Element *parse(XMLCh *buf,int pos,int len);
1177 bool keepGoing;
1178 Element *currentNode;
1179 int parselen;
1180 XMLCh *parsebuf;
1181 String cdatabuf;
1182 int currentPosition;
1183 };
1188 //########################################################################
1189 //# E L E M E N T
1190 //########################################################################
1192 Element *Element::clone()
1193 {
1194 Element *elem = new Element(name, value);
1195 elem->parent = parent;
1196 elem->attributes = attributes;
1197 elem->namespaces = namespaces;
1198 elem->line = line;
1200 std::vector<Element *>::iterator iter;
1201 for (iter = children.begin(); iter != children.end() ; iter++)
1202 {
1203 elem->addChild((*iter)->clone());
1204 }
1205 return elem;
1206 }
1209 void Element::findElementsRecursive(std::vector<Element *>&res, const String &name)
1210 {
1211 if (getName() == name)
1212 {
1213 res.push_back(this);
1214 }
1215 for (unsigned int i=0; i<children.size() ; i++)
1216 children[i]->findElementsRecursive(res, name);
1217 }
1219 std::vector<Element *> Element::findElements(const String &name)
1220 {
1221 std::vector<Element *> res;
1222 findElementsRecursive(res, name);
1223 return res;
1224 }
1226 String Element::getAttribute(const String &name)
1227 {
1228 for (unsigned int i=0 ; i<attributes.size() ; i++)
1229 if (attributes[i].getName() ==name)
1230 return attributes[i].getValue();
1231 return "";
1232 }
1234 String Element::getTagAttribute(const String &tagName, const String &attrName)
1235 {
1236 std::vector<Element *>elems = findElements(tagName);
1237 if (elems.size() <1)
1238 return "";
1239 String res = elems[0]->getAttribute(attrName);
1240 return res;
1241 }
1243 String Element::getTagValue(const String &tagName)
1244 {
1245 std::vector<Element *>elems = findElements(tagName);
1246 if (elems.size() <1)
1247 return "";
1248 String res = elems[0]->getValue();
1249 return res;
1250 }
1252 void Element::addChild(Element *child)
1253 {
1254 if (!child)
1255 return;
1256 child->parent = this;
1257 children.push_back(child);
1258 }
1261 void Element::addAttribute(const String &name, const String &value)
1262 {
1263 Attribute attr(name, value);
1264 attributes.push_back(attr);
1265 }
1267 void Element::addNamespace(const String &prefix, const String &namespaceURI)
1268 {
1269 Namespace ns(prefix, namespaceURI);
1270 namespaces.push_back(ns);
1271 }
1273 void Element::writeIndentedRecursive(FILE *f, int indent)
1274 {
1275 int i;
1276 if (!f)
1277 return;
1278 //Opening tag, and attributes
1279 for (i=0;i<indent;i++)
1280 fputc(' ',f);
1281 fprintf(f,"<%s",name.c_str());
1282 for (unsigned int i=0 ; i<attributes.size() ; i++)
1283 {
1284 fprintf(f," %s=\"%s\"",
1285 attributes[i].getName().c_str(),
1286 attributes[i].getValue().c_str());
1287 }
1288 for (unsigned int i=0 ; i<namespaces.size() ; i++)
1289 {
1290 fprintf(f," xmlns:%s=\"%s\"",
1291 namespaces[i].getPrefix().c_str(),
1292 namespaces[i].getNamespaceURI().c_str());
1293 }
1294 fprintf(f,">\n");
1296 //Between the tags
1297 if (value.size() > 0)
1298 {
1299 for (int i=0;i<indent;i++)
1300 fputc(' ', f);
1301 fprintf(f," %s\n", value.c_str());
1302 }
1304 for (unsigned int i=0 ; i<children.size() ; i++)
1305 children[i]->writeIndentedRecursive(f, indent+2);
1307 //Closing tag
1308 for (int i=0; i<indent; i++)
1309 fputc(' ',f);
1310 fprintf(f,"</%s>\n", name.c_str());
1311 }
1313 void Element::writeIndented(FILE *f)
1314 {
1315 writeIndentedRecursive(f, 0);
1316 }
1318 void Element::print()
1319 {
1320 writeIndented(stdout);
1321 }
1324 //########################################################################
1325 //# P A R S E R
1326 //########################################################################
1330 typedef struct
1331 {
1332 const char *escaped;
1333 char value;
1334 } EntityEntry;
1336 static EntityEntry entities[] =
1337 {
1338 { "&" , '&' },
1339 { "<" , '<' },
1340 { ">" , '>' },
1341 { "'", '\'' },
1342 { """, '"' },
1343 { NULL , '\0' }
1344 };
1348 /**
1349 * Removes whitespace from beginning and end of a string
1350 */
1351 String Parser::trim(const String &s)
1352 {
1353 if (s.size() < 1)
1354 return s;
1356 //Find first non-ws char
1357 unsigned int begin = 0;
1358 for ( ; begin < s.size() ; begin++)
1359 {
1360 if (!isspace(s[begin]))
1361 break;
1362 }
1364 //Find first non-ws char, going in reverse
1365 unsigned int end = s.size() - 1;
1366 for ( ; end > begin ; end--)
1367 {
1368 if (!isspace(s[end]))
1369 break;
1370 }
1371 //trace("begin:%d end:%d", begin, end);
1373 String res = s.substr(begin, end-begin+1);
1374 return res;
1375 }
1378 int Parser::countLines(int begin, int end)
1379 {
1380 int count = 0;
1381 for (int i=begin ; i<end ; i++)
1382 {
1383 XMLCh ch = parsebuf[i];
1384 if (ch == '\n' || ch == '\r')
1385 count++;
1386 }
1387 return count;
1388 }
1391 void Parser::getLineAndColumn(int pos, int *lineNr, int *colNr)
1392 {
1393 int line = 1;
1394 int col = 1;
1395 for (long i=0 ; i<pos ; i++)
1396 {
1397 XMLCh ch = parsebuf[i];
1398 if (ch == '\n' || ch == '\r')
1399 {
1400 col = 0;
1401 line ++;
1402 }
1403 else
1404 col++;
1405 }
1406 *lineNr = line;
1407 *colNr = col;
1409 }
1412 void Parser::error(const char *fmt, ...)
1413 {
1414 int lineNr;
1415 int colNr;
1416 getLineAndColumn(currentPosition, &lineNr, &colNr);
1417 va_list args;
1418 fprintf(stderr, "xml error at line %d, column %d:", lineNr, colNr);
1419 va_start(args,fmt);
1420 vfprintf(stderr,fmt,args);
1421 va_end(args) ;
1422 fprintf(stderr, "\n");
1423 }
1427 int Parser::peek(int pos)
1428 {
1429 if (pos >= parselen)
1430 return -1;
1431 currentPosition = pos;
1432 int ch = parsebuf[pos];
1433 //printf("ch:%c\n", ch);
1434 return ch;
1435 }
1439 String Parser::encode(const String &str)
1440 {
1441 String ret;
1442 for (unsigned int i=0 ; i<str.size() ; i++)
1443 {
1444 XMLCh ch = (XMLCh)str[i];
1445 if (ch == '&')
1446 ret.append("&");
1447 else if (ch == '<')
1448 ret.append("<");
1449 else if (ch == '>')
1450 ret.append(">");
1451 else if (ch == '\'')
1452 ret.append("'");
1453 else if (ch == '"')
1454 ret.append(""");
1455 else
1456 ret.push_back(ch);
1458 }
1459 return ret;
1460 }
1463 int Parser::match(int p0, const char *text)
1464 {
1465 int p = p0;
1466 while (*text)
1467 {
1468 if (peek(p) != *text)
1469 return p0;
1470 p++; text++;
1471 }
1472 return p;
1473 }
1477 int Parser::skipwhite(int p)
1478 {
1480 while (p<parselen)
1481 {
1482 int p2 = match(p, "<!--");
1483 if (p2 > p)
1484 {
1485 p = p2;
1486 while (p<parselen)
1487 {
1488 p2 = match(p, "-->");
1489 if (p2 > p)
1490 {
1491 p = p2;
1492 break;
1493 }
1494 p++;
1495 }
1496 }
1497 XMLCh b = peek(p);
1498 if (!isspace(b))
1499 break;
1500 p++;
1501 }
1502 return p;
1503 }
1505 /* modify this to allow all chars for an element or attribute name*/
1506 int Parser::getWord(int p0, String &buf)
1507 {
1508 int p = p0;
1509 while (p<parselen)
1510 {
1511 XMLCh b = peek(p);
1512 if (b<=' ' || b=='/' || b=='>' || b=='=')
1513 break;
1514 buf.push_back(b);
1515 p++;
1516 }
1517 return p;
1518 }
1520 int Parser::getQuoted(int p0, String &buf, int do_i_parse)
1521 {
1523 int p = p0;
1524 if (peek(p) != '"' && peek(p) != '\'')
1525 return p0;
1526 p++;
1528 while ( p<parselen )
1529 {
1530 XMLCh b = peek(p);
1531 if (b=='"' || b=='\'')
1532 break;
1533 if (b=='&' && do_i_parse)
1534 {
1535 bool found = false;
1536 for (EntityEntry *ee = entities ; ee->value ; ee++)
1537 {
1538 int p2 = match(p, ee->escaped);
1539 if (p2>p)
1540 {
1541 buf.push_back(ee->value);
1542 p = p2;
1543 found = true;
1544 break;
1545 }
1546 }
1547 if (!found)
1548 {
1549 error("unterminated entity");
1550 return false;
1551 }
1552 }
1553 else
1554 {
1555 buf.push_back(b);
1556 p++;
1557 }
1558 }
1559 return p;
1560 }
1562 int Parser::parseVersion(int p0)
1563 {
1564 //printf("### parseVersion: %d\n", p0);
1566 int p = p0;
1568 p = skipwhite(p0);
1570 if (peek(p) != '<')
1571 return p0;
1573 p++;
1574 if (p>=parselen || peek(p)!='?')
1575 return p0;
1577 p++;
1579 String buf;
1581 while (p<parselen)
1582 {
1583 XMLCh ch = peek(p);
1584 if (ch=='?')
1585 {
1586 p++;
1587 break;
1588 }
1589 buf.push_back(ch);
1590 p++;
1591 }
1593 if (peek(p) != '>')
1594 return p0;
1595 p++;
1597 //printf("Got version:%s\n",buf.c_str());
1598 return p;
1599 }
1601 int Parser::parseDoctype(int p0)
1602 {
1603 //printf("### parseDoctype: %d\n", p0);
1605 int p = p0;
1606 p = skipwhite(p);
1608 if (p>=parselen || peek(p)!='<')
1609 return p0;
1611 p++;
1613 if (peek(p)!='!' || peek(p+1)=='-')
1614 return p0;
1615 p++;
1617 String buf;
1618 while (p<parselen)
1619 {
1620 XMLCh ch = peek(p);
1621 if (ch=='>')
1622 {
1623 p++;
1624 break;
1625 }
1626 buf.push_back(ch);
1627 p++;
1628 }
1630 //printf("Got doctype:%s\n",buf.c_str());
1631 return p;
1632 }
1636 int Parser::parseElement(int p0, Element *par,int lineNr)
1637 {
1639 int p = p0;
1641 int p2 = p;
1643 p = skipwhite(p);
1645 //## Get open tag
1646 XMLCh ch = peek(p);
1647 if (ch!='<')
1648 return p0;
1650 //int line, col;
1651 //getLineAndColumn(p, &line, &col);
1653 p++;
1655 String openTagName;
1656 p = skipwhite(p);
1657 p = getWord(p, openTagName);
1658 //printf("####tag :%s\n", openTagName.c_str());
1659 p = skipwhite(p);
1661 //Add element to tree
1662 Element *n = new Element(openTagName);
1663 n->line = lineNr + countLines(p0, p);
1664 n->parent = par;
1665 par->addChild(n);
1667 // Get attributes
1668 if (peek(p) != '>')
1669 {
1670 while (p<parselen)
1671 {
1672 p = skipwhite(p);
1673 ch = peek(p);
1674 //printf("ch:%c\n",ch);
1675 if (ch=='>')
1676 break;
1677 else if (ch=='/' && p<parselen+1)
1678 {
1679 p++;
1680 p = skipwhite(p);
1681 ch = peek(p);
1682 if (ch=='>')
1683 {
1684 p++;
1685 //printf("quick close\n");
1686 return p;
1687 }
1688 }
1689 String attrName;
1690 p2 = getWord(p, attrName);
1691 if (p2==p)
1692 break;
1693 //printf("name:%s",buf);
1694 p=p2;
1695 p = skipwhite(p);
1696 ch = peek(p);
1697 //printf("ch:%c\n",ch);
1698 if (ch!='=')
1699 break;
1700 p++;
1701 p = skipwhite(p);
1702 // ch = parsebuf[p];
1703 // printf("ch:%c\n",ch);
1704 String attrVal;
1705 p2 = getQuoted(p, attrVal, true);
1706 p=p2+1;
1707 //printf("name:'%s' value:'%s'\n",attrName.c_str(),attrVal.c_str());
1708 char *namestr = (char *)attrName.c_str();
1709 if (strncmp(namestr, "xmlns:", 6)==0)
1710 n->addNamespace(attrName, attrVal);
1711 else
1712 n->addAttribute(attrName, attrVal);
1713 }
1714 }
1716 bool cdata = false;
1718 p++;
1719 // ### Get intervening data ### */
1720 String data;
1721 while (p<parselen)
1722 {
1723 //# COMMENT
1724 p2 = match(p, "<!--");
1725 if (!cdata && p2>p)
1726 {
1727 p = p2;
1728 while (p<parselen)
1729 {
1730 p2 = match(p, "-->");
1731 if (p2 > p)
1732 {
1733 p = p2;
1734 break;
1735 }
1736 p++;
1737 }
1738 }
1740 ch = peek(p);
1741 //# END TAG
1742 if (ch=='<' && !cdata && peek(p+1)=='/')
1743 {
1744 break;
1745 }
1746 //# CDATA
1747 p2 = match(p, "<![CDATA[");
1748 if (p2 > p)
1749 {
1750 cdata = true;
1751 p = p2;
1752 continue;
1753 }
1755 //# CHILD ELEMENT
1756 if (ch == '<')
1757 {
1758 p2 = parseElement(p, n, lineNr + countLines(p0, p));
1759 if (p2 == p)
1760 {
1761 /*
1762 printf("problem on element:%s. p2:%d p:%d\n",
1763 openTagName.c_str(), p2, p);
1764 */
1765 return p0;
1766 }
1767 p = p2;
1768 continue;
1769 }
1770 //# ENTITY
1771 if (ch=='&' && !cdata)
1772 {
1773 bool found = false;
1774 for (EntityEntry *ee = entities ; ee->value ; ee++)
1775 {
1776 int p2 = match(p, ee->escaped);
1777 if (p2>p)
1778 {
1779 data.push_back(ee->value);
1780 p = p2;
1781 found = true;
1782 break;
1783 }
1784 }
1785 if (!found)
1786 {
1787 error("unterminated entity");
1788 return -1;
1789 }
1790 continue;
1791 }
1793 //# NONE OF THE ABOVE
1794 data.push_back(ch);
1795 p++;
1796 }/*while*/
1799 n->value = data;
1800 //printf("%d : data:%s\n",p,data.c_str());
1802 //## Get close tag
1803 p = skipwhite(p);
1804 ch = peek(p);
1805 if (ch != '<')
1806 {
1807 error("no < for end tag\n");
1808 return p0;
1809 }
1810 p++;
1811 ch = peek(p);
1812 if (ch != '/')
1813 {
1814 error("no / on end tag");
1815 return p0;
1816 }
1817 p++;
1818 ch = peek(p);
1819 p = skipwhite(p);
1820 String closeTagName;
1821 p = getWord(p, closeTagName);
1822 if (openTagName != closeTagName)
1823 {
1824 error("Mismatched closing tag. Expected </%S>. Got '%S'.",
1825 openTagName.c_str(), closeTagName.c_str());
1826 return p0;
1827 }
1828 p = skipwhite(p);
1829 if (peek(p) != '>')
1830 {
1831 error("no > on end tag for '%s'", closeTagName.c_str());
1832 return p0;
1833 }
1834 p++;
1835 // printf("close element:%s\n",closeTagName.c_str());
1836 p = skipwhite(p);
1837 return p;
1838 }
1843 Element *Parser::parse(XMLCh *buf,int pos,int len)
1844 {
1845 parselen = len;
1846 parsebuf = buf;
1847 Element *rootNode = new Element("root");
1848 pos = parseVersion(pos);
1849 pos = parseDoctype(pos);
1850 pos = parseElement(pos, rootNode, 1);
1851 return rootNode;
1852 }
1855 Element *Parser::parse(const char *buf, int pos, int len)
1856 {
1857 XMLCh *charbuf = new XMLCh[len + 1];
1858 long i = 0;
1859 for ( ; i < len ; i++)
1860 charbuf[i] = (XMLCh)buf[i];
1861 charbuf[i] = '\0';
1863 Element *n = parse(charbuf, pos, len);
1864 delete[] charbuf;
1865 return n;
1866 }
1868 Element *Parser::parse(const String &buf)
1869 {
1870 long len = (long)buf.size();
1871 XMLCh *charbuf = new XMLCh[len + 1];
1872 long i = 0;
1873 for ( ; i < len ; i++)
1874 charbuf[i] = (XMLCh)buf[i];
1875 charbuf[i] = '\0';
1877 Element *n = parse(charbuf, 0, len);
1878 delete[] charbuf;
1879 return n;
1880 }
1882 Element *Parser::parseFile(const String &fileName)
1883 {
1885 //##### LOAD INTO A CHAR BUF, THEN CONVERT TO XMLCh
1886 FILE *f = fopen(fileName.c_str(), "rb");
1887 if (!f)
1888 return NULL;
1890 struct stat statBuf;
1891 if (fstat(fileno(f),&statBuf)<0)
1892 {
1893 fclose(f);
1894 return NULL;
1895 }
1896 long filelen = statBuf.st_size;
1898 //printf("length:%d\n",filelen);
1899 XMLCh *charbuf = new XMLCh[filelen + 1];
1900 for (XMLCh *p=charbuf ; !feof(f) ; p++)
1901 {
1902 *p = (XMLCh)fgetc(f);
1903 }
1904 fclose(f);
1905 charbuf[filelen] = '\0';
1908 /*
1909 printf("nrbytes:%d\n",wc_count);
1910 printf("buf:%ls\n======\n",charbuf);
1911 */
1912 Element *n = parse(charbuf, 0, filelen);
1913 delete[] charbuf;
1914 return n;
1915 }
1917 //########################################################################
1918 //########################################################################
1919 //## E N D X M L
1920 //########################################################################
1921 //########################################################################
1928 //########################################################################
1929 //########################################################################
1930 //## U R I
1931 //########################################################################
1932 //########################################################################
1934 //This would normally be a call to a UNICODE function
1935 #define isLetter(x) isalpha(x)
1937 /**
1938 * A class that implements the W3C URI resource reference.
1939 */
1940 class URI
1941 {
1942 public:
1944 typedef enum
1945 {
1946 SCHEME_NONE =0,
1947 SCHEME_DATA,
1948 SCHEME_HTTP,
1949 SCHEME_HTTPS,
1950 SCHEME_FTP,
1951 SCHEME_FILE,
1952 SCHEME_LDAP,
1953 SCHEME_MAILTO,
1954 SCHEME_NEWS,
1955 SCHEME_TELNET
1956 } SchemeTypes;
1958 /**
1959 *
1960 */
1961 URI()
1962 {
1963 init();
1964 }
1966 /**
1967 *
1968 */
1969 URI(const String &str)
1970 {
1971 init();
1972 parse(str);
1973 }
1976 /**
1977 *
1978 */
1979 URI(const char *str)
1980 {
1981 init();
1982 String domStr = str;
1983 parse(domStr);
1984 }
1987 /**
1988 *
1989 */
1990 URI(const URI &other)
1991 {
1992 init();
1993 assign(other);
1994 }
1997 /**
1998 *
1999 */
2000 URI &operator=(const URI &other)
2001 {
2002 init();
2003 assign(other);
2004 return *this;
2005 }
2008 /**
2009 *
2010 */
2011 virtual ~URI()
2012 {}
2016 /**
2017 *
2018 */
2019 virtual bool parse(const String &str);
2021 /**
2022 *
2023 */
2024 virtual String toString() const;
2026 /**
2027 *
2028 */
2029 virtual int getScheme() const;
2031 /**
2032 *
2033 */
2034 virtual String getSchemeStr() const;
2036 /**
2037 *
2038 */
2039 virtual String getAuthority() const;
2041 /**
2042 * Same as getAuthority, but if the port has been specified
2043 * as host:port , the port will not be included
2044 */
2045 virtual String getHost() const;
2047 /**
2048 *
2049 */
2050 virtual int getPort() const;
2052 /**
2053 *
2054 */
2055 virtual String getPath() const;
2057 /**
2058 *
2059 */
2060 virtual String getNativePath() const;
2062 /**
2063 *
2064 */
2065 virtual bool isAbsolute() const;
2067 /**
2068 *
2069 */
2070 virtual bool isOpaque() const;
2072 /**
2073 *
2074 */
2075 virtual String getQuery() const;
2077 /**
2078 *
2079 */
2080 virtual String getFragment() const;
2082 /**
2083 *
2084 */
2085 virtual URI resolve(const URI &other) const;
2087 /**
2088 *
2089 */
2090 virtual void normalize();
2092 private:
2094 /**
2095 *
2096 */
2097 void init()
2098 {
2099 parsebuf = NULL;
2100 parselen = 0;
2101 scheme = SCHEME_NONE;
2102 schemeStr = "";
2103 port = 0;
2104 authority = "";
2105 path = "";
2106 absolute = false;
2107 opaque = false;
2108 query = "";
2109 fragment = "";
2110 }
2113 /**
2114 *
2115 */
2116 void assign(const URI &other)
2117 {
2118 scheme = other.scheme;
2119 schemeStr = other.schemeStr;
2120 authority = other.authority;
2121 port = other.port;
2122 path = other.path;
2123 absolute = other.absolute;
2124 opaque = other.opaque;
2125 query = other.query;
2126 fragment = other.fragment;
2127 }
2129 int scheme;
2131 String schemeStr;
2133 String authority;
2135 bool portSpecified;
2137 int port;
2139 String path;
2141 bool absolute;
2143 bool opaque;
2145 String query;
2147 String fragment;
2149 void error(const char *fmt, ...);
2151 void trace(const char *fmt, ...);
2154 int peek(int p);
2156 int match(int p, const char *key);
2158 int parseScheme(int p);
2160 int parseHierarchicalPart(int p0);
2162 int parseQuery(int p0);
2164 int parseFragment(int p0);
2166 int parse(int p);
2168 char *parsebuf;
2170 int parselen;
2172 };
2176 typedef struct
2177 {
2178 int ival;
2179 const char *sval;
2180 int port;
2181 } LookupEntry;
2183 LookupEntry schemes[] =
2184 {
2185 { URI::SCHEME_DATA, "data:", 0 },
2186 { URI::SCHEME_HTTP, "http:", 80 },
2187 { URI::SCHEME_HTTPS, "https:", 443 },
2188 { URI::SCHEME_FTP, "ftp", 12 },
2189 { URI::SCHEME_FILE, "file:", 0 },
2190 { URI::SCHEME_LDAP, "ldap:", 123 },
2191 { URI::SCHEME_MAILTO, "mailto:", 25 },
2192 { URI::SCHEME_NEWS, "news:", 117 },
2193 { URI::SCHEME_TELNET, "telnet:", 23 },
2194 { 0, NULL, 0 }
2195 };
2198 String URI::toString() const
2199 {
2200 String str = schemeStr;
2201 if (authority.size() > 0)
2202 {
2203 str.append("//");
2204 str.append(authority);
2205 }
2206 str.append(path);
2207 if (query.size() > 0)
2208 {
2209 str.append("?");
2210 str.append(query);
2211 }
2212 if (fragment.size() > 0)
2213 {
2214 str.append("#");
2215 str.append(fragment);
2216 }
2217 return str;
2218 }
2221 int URI::getScheme() const
2222 {
2223 return scheme;
2224 }
2226 String URI::getSchemeStr() const
2227 {
2228 return schemeStr;
2229 }
2232 String URI::getAuthority() const
2233 {
2234 String ret = authority;
2235 if (portSpecified && port>=0)
2236 {
2237 char buf[7];
2238 snprintf(buf, 6, ":%6d", port);
2239 ret.append(buf);
2240 }
2241 return ret;
2242 }
2244 String URI::getHost() const
2245 {
2246 return authority;
2247 }
2249 int URI::getPort() const
2250 {
2251 return port;
2252 }
2255 String URI::getPath() const
2256 {
2257 return path;
2258 }
2260 String URI::getNativePath() const
2261 {
2262 String npath;
2263 #ifdef __WIN32__
2264 unsigned int firstChar = 0;
2265 if (path.size() >= 3)
2266 {
2267 if (path[0] == '/' &&
2268 isLetter(path[1]) &&
2269 path[2] == ':')
2270 firstChar++;
2271 }
2272 for (unsigned int i=firstChar ; i<path.size() ; i++)
2273 {
2274 XMLCh ch = (XMLCh) path[i];
2275 if (ch == '/')
2276 npath.push_back((XMLCh)'\\');
2277 else
2278 npath.push_back(ch);
2279 }
2280 #else
2281 npath = path;
2282 #endif
2283 return npath;
2284 }
2287 bool URI::isAbsolute() const
2288 {
2289 return absolute;
2290 }
2292 bool URI::isOpaque() const
2293 {
2294 return opaque;
2295 }
2298 String URI::getQuery() const
2299 {
2300 return query;
2301 }
2304 String URI::getFragment() const
2305 {
2306 return fragment;
2307 }
2310 URI URI::resolve(const URI &other) const
2311 {
2312 //### According to w3c, this is handled in 3 cases
2314 //## 1
2315 if (opaque || other.isAbsolute())
2316 return other;
2318 //## 2
2319 if (other.fragment.size() > 0 &&
2320 other.path.size() == 0 &&
2321 other.scheme == SCHEME_NONE &&
2322 other.authority.size() == 0 &&
2323 other.query.size() == 0 )
2324 {
2325 URI fragUri = *this;
2326 fragUri.fragment = other.fragment;
2327 return fragUri;
2328 }
2330 //## 3 http://www.ietf.org/rfc/rfc2396.txt, section 5.2
2331 URI newUri;
2332 //# 3.1
2333 newUri.scheme = scheme;
2334 newUri.schemeStr = schemeStr;
2335 newUri.query = other.query;
2336 newUri.fragment = other.fragment;
2337 if (other.authority.size() > 0)
2338 {
2339 //# 3.2
2340 if (absolute || other.absolute)
2341 newUri.absolute = true;
2342 newUri.authority = other.authority;
2343 newUri.port = other.port;//part of authority
2344 newUri.path = other.path;
2345 }
2346 else
2347 {
2348 //# 3.3
2349 if (other.absolute)
2350 {
2351 newUri.absolute = true;
2352 newUri.path = other.path;
2353 }
2354 else
2355 {
2356 unsigned int pos = path.find_last_of('/');
2357 if (pos != path.npos)
2358 {
2359 String tpath = path.substr(0, pos+1);
2360 tpath.append(other.path);
2361 newUri.path = tpath;
2362 }
2363 else
2364 newUri.path = other.path;
2365 }
2366 }
2368 newUri.normalize();
2369 return newUri;
2370 }
2374 /**
2375 * This follows the Java URI algorithm:
2376 * 1. All "." segments are removed.
2377 * 2. If a ".." segment is preceded by a non-".." segment
2378 * then both of these segments are removed. This step
2379 * is repeated until it is no longer applicable.
2380 * 3. If the path is relative, and if its first segment
2381 * contains a colon character (':'), then a "." segment
2382 * is prepended. This prevents a relative URI with a path
2383 * such as "a:b/c/d" from later being re-parsed as an
2384 * opaque URI with a scheme of "a" and a scheme-specific
2385 * part of "b/c/d". (Deviation from RFC 2396)
2386 */
2387 void URI::normalize()
2388 {
2389 std::vector<String> segments;
2391 //## Collect segments
2392 if (path.size()<2)
2393 return;
2394 bool abs = false;
2395 unsigned int pos=0;
2396 if (path[0]=='/')
2397 {
2398 abs = true;
2399 pos++;
2400 }
2401 while (pos < path.size())
2402 {
2403 unsigned int pos2 = path.find('/', pos);
2404 if (pos2==path.npos)
2405 {
2406 String seg = path.substr(pos);
2407 //printf("last segment:%s\n", seg.c_str());
2408 segments.push_back(seg);
2409 break;
2410 }
2411 if (pos2>pos)
2412 {
2413 String seg = path.substr(pos, pos2-pos);
2414 //printf("segment:%s\n", seg.c_str());
2415 segments.push_back(seg);
2416 }
2417 pos = pos2;
2418 pos++;
2419 }
2421 //## Clean up (normalize) segments
2422 bool edited = false;
2423 std::vector<String>::iterator iter;
2424 for (iter=segments.begin() ; iter!=segments.end() ; )
2425 {
2426 String s = *iter;
2427 if (s == ".")
2428 {
2429 iter = segments.erase(iter);
2430 edited = true;
2431 }
2432 else if (s == ".." &&
2433 iter != segments.begin() &&
2434 *(iter-1) != "..")
2435 {
2436 iter--; //back up, then erase two entries
2437 iter = segments.erase(iter);
2438 iter = segments.erase(iter);
2439 edited = true;
2440 }
2441 else
2442 iter++;
2443 }
2445 //## Rebuild path, if necessary
2446 if (edited)
2447 {
2448 path.clear();
2449 if (abs)
2450 {
2451 path.append("/");
2452 }
2453 std::vector<String>::iterator iter;
2454 for (iter=segments.begin() ; iter!=segments.end() ; iter++)
2455 {
2456 if (iter != segments.begin())
2457 path.append("/");
2458 path.append(*iter);
2459 }
2460 }
2462 }
2466 //#########################################################################
2467 //# M E S S A G E S
2468 //#########################################################################
2470 void URI::error(const char *fmt, ...)
2471 {
2472 va_list args;
2473 fprintf(stderr, "URI error: ");
2474 va_start(args, fmt);
2475 vfprintf(stderr, fmt, args);
2476 va_end(args);
2477 fprintf(stderr, "\n");
2478 }
2480 void URI::trace(const char *fmt, ...)
2481 {
2482 va_list args;
2483 fprintf(stdout, "URI: ");
2484 va_start(args, fmt);
2485 vfprintf(stdout, fmt, args);
2486 va_end(args);
2487 fprintf(stdout, "\n");
2488 }
2493 //#########################################################################
2494 //# P A R S I N G
2495 //#########################################################################
2499 int URI::peek(int p)
2500 {
2501 if (p<0 || p>=parselen)
2502 return -1;
2503 return parsebuf[p];
2504 }
2508 int URI::match(int p0, const char *key)
2509 {
2510 int p = p0;
2511 while (p < parselen)
2512 {
2513 if (*key == '\0')
2514 return p;
2515 else if (*key != parsebuf[p])
2516 break;
2517 p++; key++;
2518 }
2519 return p0;
2520 }
2522 //#########################################################################
2523 //# Parsing is performed according to:
2524 //# http://www.gbiv.com/protocols/uri/rfc/rfc3986.html#components
2525 //#########################################################################
2527 int URI::parseScheme(int p0)
2528 {
2529 int p = p0;
2530 for (LookupEntry *entry = schemes; entry->sval ; entry++)
2531 {
2532 int p2 = match(p, entry->sval);
2533 if (p2 > p)
2534 {
2535 schemeStr = entry->sval;
2536 scheme = entry->ival;
2537 port = entry->port;
2538 p = p2;
2539 return p;
2540 }
2541 }
2543 return p;
2544 }
2547 int URI::parseHierarchicalPart(int p0)
2548 {
2549 int p = p0;
2550 int ch;
2552 //# Authority field (host and port, for example)
2553 int p2 = match(p, "//");
2554 if (p2 > p)
2555 {
2556 p = p2;
2557 portSpecified = false;
2558 String portStr;
2559 while (p < parselen)
2560 {
2561 ch = peek(p);
2562 if (ch == '/')
2563 break;
2564 else if (ch == ':')
2565 portSpecified = true;
2566 else if (portSpecified)
2567 portStr.push_back((XMLCh)ch);
2568 else
2569 authority.push_back((XMLCh)ch);
2570 p++;
2571 }
2572 if (portStr.size() > 0)
2573 {
2574 char *pstr = (char *)portStr.c_str();
2575 char *endStr;
2576 long val = strtol(pstr, &endStr, 10);
2577 if (endStr > pstr) //successful parse?
2578 port = val;
2579 }
2580 }
2582 //# Are we absolute?
2583 ch = peek(p);
2584 if (isLetter(ch) && peek(p+1)==':')
2585 {
2586 absolute = true;
2587 path.push_back((XMLCh)'/');
2588 }
2589 else if (ch == '/')
2590 {
2591 absolute = true;
2592 if (p>p0) //in other words, if '/' is not the first char
2593 opaque = true;
2594 path.push_back((XMLCh)ch);
2595 p++;
2596 }
2598 while (p < parselen)
2599 {
2600 ch = peek(p);
2601 if (ch == '?' || ch == '#')
2602 break;
2603 path.push_back((XMLCh)ch);
2604 p++;
2605 }
2607 return p;
2608 }
2610 int URI::parseQuery(int p0)
2611 {
2612 int p = p0;
2613 int ch = peek(p);
2614 if (ch != '?')
2615 return p0;
2617 p++;
2618 while (p < parselen)
2619 {
2620 ch = peek(p);
2621 if (ch == '#')
2622 break;
2623 query.push_back((XMLCh)ch);
2624 p++;
2625 }
2628 return p;
2629 }
2631 int URI::parseFragment(int p0)
2632 {
2634 int p = p0;
2635 int ch = peek(p);
2636 if (ch != '#')
2637 return p0;
2639 p++;
2640 while (p < parselen)
2641 {
2642 ch = peek(p);
2643 if (ch == '?')
2644 break;
2645 fragment.push_back((XMLCh)ch);
2646 p++;
2647 }
2650 return p;
2651 }
2654 int URI::parse(int p0)
2655 {
2657 int p = p0;
2659 int p2 = parseScheme(p);
2660 if (p2 < 0)
2661 {
2662 error("Scheme");
2663 return -1;
2664 }
2665 p = p2;
2668 p2 = parseHierarchicalPart(p);
2669 if (p2 < 0)
2670 {
2671 error("Hierarchical part");
2672 return -1;
2673 }
2674 p = p2;
2676 p2 = parseQuery(p);
2677 if (p2 < 0)
2678 {
2679 error("Query");
2680 return -1;
2681 }
2682 p = p2;
2685 p2 = parseFragment(p);
2686 if (p2 < 0)
2687 {
2688 error("Fragment");
2689 return -1;
2690 }
2691 p = p2;
2693 return p;
2695 }
2699 bool URI::parse(const String &str)
2700 {
2701 init();
2703 parselen = str.size();
2705 String tmp;
2706 for (unsigned int i=0 ; i<str.size() ; i++)
2707 {
2708 XMLCh ch = (XMLCh) str[i];
2709 if (ch == '\\')
2710 tmp.push_back((XMLCh)'/');
2711 else
2712 tmp.push_back(ch);
2713 }
2714 parsebuf = (char *) tmp.c_str();
2717 int p = parse(0);
2718 normalize();
2720 if (p < 0)
2721 {
2722 error("Syntax error");
2723 return false;
2724 }
2726 //printf("uri:%s\n", toString().c_str());
2727 //printf("path:%s\n", path.c_str());
2729 return true;
2731 }
2740 //########################################################################
2741 //########################################################################
2742 //## M A K E
2743 //########################################################################
2744 //########################################################################
2746 //########################################################################
2747 //# F I L E S E T
2748 //########################################################################
2749 /**
2750 * This is the descriptor for a <fileset> item
2751 */
2752 class FileSet
2753 {
2754 public:
2756 /**
2757 *
2758 */
2759 FileSet()
2760 {}
2762 /**
2763 *
2764 */
2765 FileSet(const FileSet &other)
2766 { assign(other); }
2768 /**
2769 *
2770 */
2771 FileSet &operator=(const FileSet &other)
2772 { assign(other); return *this; }
2774 /**
2775 *
2776 */
2777 virtual ~FileSet()
2778 {}
2780 /**
2781 *
2782 */
2783 String getDirectory()
2784 { return directory; }
2786 /**
2787 *
2788 */
2789 void setDirectory(const String &val)
2790 { directory = val; }
2792 /**
2793 *
2794 */
2795 void setFiles(const std::vector<String> &val)
2796 { files = val; }
2798 /**
2799 *
2800 */
2801 std::vector<String> getFiles()
2802 { return files; }
2804 /**
2805 *
2806 */
2807 void setIncludes(const std::vector<String> &val)
2808 { includes = val; }
2810 /**
2811 *
2812 */
2813 std::vector<String> getIncludes()
2814 { return includes; }
2816 /**
2817 *
2818 */
2819 void setExcludes(const std::vector<String> &val)
2820 { excludes = val; }
2822 /**
2823 *
2824 */
2825 std::vector<String> getExcludes()
2826 { return excludes; }
2828 /**
2829 *
2830 */
2831 unsigned int size()
2832 { return files.size(); }
2834 /**
2835 *
2836 */
2837 String operator[](int index)
2838 { return files[index]; }
2840 /**
2841 *
2842 */
2843 void clear()
2844 {
2845 directory = "";
2846 files.clear();
2847 includes.clear();
2848 excludes.clear();
2849 }
2852 private:
2854 void assign(const FileSet &other)
2855 {
2856 directory = other.directory;
2857 files = other.files;
2858 includes = other.includes;
2859 excludes = other.excludes;
2860 }
2862 String directory;
2863 std::vector<String> files;
2864 std::vector<String> includes;
2865 std::vector<String> excludes;
2866 };
2869 //########################################################################
2870 //# F I L E L I S T
2871 //########################################################################
2872 /**
2873 * This is a simpler, explicitly-named list of files
2874 */
2875 class FileList
2876 {
2877 public:
2879 /**
2880 *
2881 */
2882 FileList()
2883 {}
2885 /**
2886 *
2887 */
2888 FileList(const FileList &other)
2889 { assign(other); }
2891 /**
2892 *
2893 */
2894 FileList &operator=(const FileList &other)
2895 { assign(other); return *this; }
2897 /**
2898 *
2899 */
2900 virtual ~FileList()
2901 {}
2903 /**
2904 *
2905 */
2906 String getDirectory()
2907 { return directory; }
2909 /**
2910 *
2911 */
2912 void setDirectory(const String &val)
2913 { directory = val; }
2915 /**
2916 *
2917 */
2918 void setFiles(const std::vector<String> &val)
2919 { files = val; }
2921 /**
2922 *
2923 */
2924 std::vector<String> getFiles()
2925 { return files; }
2927 /**
2928 *
2929 */
2930 unsigned int size()
2931 { return files.size(); }
2933 /**
2934 *
2935 */
2936 String operator[](int index)
2937 { return files[index]; }
2939 /**
2940 *
2941 */
2942 void clear()
2943 {
2944 directory = "";
2945 files.clear();
2946 }
2949 private:
2951 void assign(const FileList &other)
2952 {
2953 directory = other.directory;
2954 files = other.files;
2955 }
2957 String directory;
2958 std::vector<String> files;
2959 };
2964 //########################################################################
2965 //# M A K E B A S E
2966 //########################################################################
2967 /**
2968 * Base class for all classes in this file
2969 */
2970 class MakeBase
2971 {
2972 public:
2974 MakeBase()
2975 { line = 0; }
2976 virtual ~MakeBase()
2977 {}
2979 /**
2980 * Return the URI of the file associated with this object
2981 */
2982 URI getURI()
2983 { return uri; }
2985 /**
2986 * Set the uri to the given string
2987 */
2988 void setURI(const String &uristr)
2989 { uri.parse(uristr); }
2991 /**
2992 * Resolve another path relative to this one
2993 */
2994 String resolve(const String &otherPath);
2996 /**
2997 * replace variable refs like ${a} with their values
2998 * Assume that the string has already been syntax validated
2999 */
3000 String eval(const String &s, const String &defaultVal);
3002 /**
3003 * replace variable refs like ${a} with their values
3004 * return true or false
3005 * Assume that the string has already been syntax validated
3006 */
3007 bool evalBool(const String &s, bool defaultVal);
3009 /**
3010 * Get an element attribute, performing substitutions if necessary
3011 */
3012 bool getAttribute(Element *elem, const String &name, String &result);
3014 /**
3015 * Get an element value, performing substitutions if necessary
3016 */
3017 bool getValue(Element *elem, String &result);
3019 /**
3020 * Set the current line number in the file
3021 */
3022 void setLine(int val)
3023 { line = val; }
3025 /**
3026 * Get the current line number in the file
3027 */
3028 int getLine()
3029 { return line; }
3032 /**
3033 * Set a property to a given value
3034 */
3035 virtual void setProperty(const String &name, const String &val)
3036 {
3037 properties[name] = val;
3038 }
3040 /**
3041 * Return a named property is found, else a null string
3042 */
3043 virtual String getProperty(const String &name)
3044 {
3045 String val;
3046 std::map<String, String>::iterator iter = properties.find(name);
3047 if (iter != properties.end())
3048 val = iter->second;
3049 String sval;
3050 if (!getSubstitutions(val, sval))
3051 return false;
3052 return sval;
3053 }
3055 /**
3056 * Return true if a named property is found, else false
3057 */
3058 virtual bool hasProperty(const String &name)
3059 {
3060 std::map<String, String>::iterator iter = properties.find(name);
3061 if (iter == properties.end())
3062 return false;
3063 return true;
3064 }
3067 protected:
3069 /**
3070 * The path to the file associated with this object
3071 */
3072 URI uri;
3074 /**
3075 * If this prefix is seen in a substitution, use an environment
3076 * variable.
3077 * example: <property environment="env"/>
3078 * ${env.JAVA_HOME}
3079 */
3080 String envPrefix;
3082 /**
3083 * If this prefix is seen in a substitution, use as a
3084 * pkg-config 'all' query
3085 * example: <property pkg-config="pc"/>
3086 * ${pc.gtkmm}
3087 */
3088 String pcPrefix;
3090 /**
3091 * If this prefix is seen in a substitution, use as a
3092 * pkg-config 'cflags' query
3093 * example: <property pkg-config="pcc"/>
3094 * ${pcc.gtkmm}
3095 */
3096 String pccPrefix;
3098 /**
3099 * If this prefix is seen in a substitution, use as a
3100 * pkg-config 'libs' query
3101 * example: <property pkg-config="pcl"/>
3102 * ${pcl.gtkmm}
3103 */
3104 String pclPrefix;
3110 /**
3111 * Print a printf()-like formatted error message
3112 */
3113 void error(const char *fmt, ...);
3115 /**
3116 * Print a printf()-like formatted trace message
3117 */
3118 void status(const char *fmt, ...);
3120 /**
3121 * Show target status
3122 */
3123 void targetstatus(const char *fmt, ...);
3125 /**
3126 * Print a printf()-like formatted trace message
3127 */
3128 void trace(const char *fmt, ...);
3130 /**
3131 * Check if a given string matches a given regex pattern
3132 */
3133 bool regexMatch(const String &str, const String &pattern);
3135 /**
3136 *
3137 */
3138 String getSuffix(const String &fname);
3140 /**
3141 * Break up a string into substrings delimited the characters
3142 * in delimiters. Null-length substrings are ignored
3143 */
3144 std::vector<String> tokenize(const String &val,
3145 const String &delimiters);
3147 /**
3148 * replace runs of whitespace with a space
3149 */
3150 String strip(const String &s);
3152 /**
3153 * remove leading whitespace from each line
3154 */
3155 String leftJustify(const String &s);
3157 /**
3158 * remove leading and trailing whitespace from string
3159 */
3160 String trim(const String &s);
3162 /**
3163 * Return a lower case version of the given string
3164 */
3165 String toLower(const String &s);
3167 /**
3168 * Return the native format of the canonical
3169 * path which we store
3170 */
3171 String getNativePath(const String &path);
3173 /**
3174 * Execute a shell command. Outbuf is a ref to a string
3175 * to catch the result.
3176 */
3177 bool executeCommand(const String &call,
3178 const String &inbuf,
3179 String &outbuf,
3180 String &errbuf);
3181 /**
3182 * List all directories in a given base and starting directory
3183 * It is usually called like:
3184 * bool ret = listDirectories("src", "", result);
3185 */
3186 bool listDirectories(const String &baseName,
3187 const String &dirname,
3188 std::vector<String> &res);
3190 /**
3191 * Find all files in the named directory
3192 */
3193 bool listFiles(const String &baseName,
3194 const String &dirname,
3195 std::vector<String> &result);
3197 /**
3198 * Perform a listing for a fileset
3199 */
3200 bool listFiles(MakeBase &propRef, FileSet &fileSet);
3202 /**
3203 * Parse a <patternset>
3204 */
3205 bool parsePatternSet(Element *elem,
3206 MakeBase &propRef,
3207 std::vector<String> &includes,
3208 std::vector<String> &excludes);
3210 /**
3211 * Parse a <fileset> entry, and determine which files
3212 * should be included
3213 */
3214 bool parseFileSet(Element *elem,
3215 MakeBase &propRef,
3216 FileSet &fileSet);
3217 /**
3218 * Parse a <filelist> entry
3219 */
3220 bool parseFileList(Element *elem,
3221 MakeBase &propRef,
3222 FileList &fileList);
3224 /**
3225 * Return this object's property list
3226 */
3227 virtual std::map<String, String> &getProperties()
3228 { return properties; }
3231 std::map<String, String> properties;
3233 /**
3234 * Create a directory, making intermediate dirs
3235 * if necessary
3236 */
3237 bool createDirectory(const String &dirname);
3239 /**
3240 * Delete a directory and its children if desired
3241 */
3242 bool removeDirectory(const String &dirName);
3244 /**
3245 * Copy a file from one name to another. Perform only if needed
3246 */
3247 bool copyFile(const String &srcFile, const String &destFile);
3249 /**
3250 * Tests if the file exists and is a regular file
3251 */
3252 bool isRegularFile(const String &fileName);
3254 /**
3255 * Tests if the file exists and is a directory
3256 */
3257 bool isDirectory(const String &fileName);
3259 /**
3260 * Tests is the modification date of fileA is newer than fileB
3261 */
3262 bool isNewerThan(const String &fileA, const String &fileB);
3264 private:
3266 bool pkgConfigRecursive(const String packageName,
3267 const String &path,
3268 const String &prefix,
3269 int query,
3270 String &result,
3271 std::set<String> &deplist);
3273 /**
3274 * utility method to query for "all", "cflags", or "libs" for this package and its
3275 * dependencies. 0, 1, 2
3276 */
3277 bool pkgConfigQuery(const String &packageName, int query, String &result);
3279 /**
3280 * replace a variable ref like ${a} with a value
3281 */
3282 bool lookupProperty(const String &s, String &result);
3284 /**
3285 * called by getSubstitutions(). This is in case a looked-up string
3286 * has substitutions also.
3287 */
3288 bool getSubstitutionsRecursive(const String &s, String &result, int depth);
3290 /**
3291 * replace variable refs in a string like ${a} with their values
3292 */
3293 bool getSubstitutions(const String &s, String &result);
3295 int line;
3298 };
3302 /**
3303 * Define the pkg-config class here, since it will be used in MakeBase method
3304 * implementations.
3305 */
3306 class PkgConfig : public MakeBase
3307 {
3309 public:
3311 /**
3312 *
3313 */
3314 PkgConfig()
3315 {
3316 path = ".";
3317 prefix = "/target";
3318 init();
3319 }
3321 /**
3322 *
3323 */
3324 PkgConfig(const PkgConfig &other)
3325 { assign(other); }
3327 /**
3328 *
3329 */
3330 PkgConfig &operator=(const PkgConfig &other)
3331 { assign(other); return *this; }
3333 /**
3334 *
3335 */
3336 virtual ~PkgConfig()
3337 { }
3339 /**
3340 *
3341 */
3342 virtual String getName()
3343 { return name; }
3345 /**
3346 *
3347 */
3348 virtual String getPath()
3349 { return path; }
3351 /**
3352 *
3353 */
3354 virtual void setPath(const String &val)
3355 { path = val; }
3357 /**
3358 *
3359 */
3360 virtual String getPrefix()
3361 { return prefix; }
3363 /**
3364 * Allow the user to override the prefix in the file
3365 */
3366 virtual void setPrefix(const String &val)
3367 { prefix = val; }
3369 /**
3370 *
3371 */
3372 virtual String getDescription()
3373 { return description; }
3375 /**
3376 *
3377 */
3378 virtual String getCflags()
3379 { return cflags; }
3381 /**
3382 *
3383 */
3384 virtual String getLibs()
3385 { return libs; }
3387 /**
3388 *
3389 */
3390 virtual String getAll()
3391 {
3392 String ret = cflags;
3393 ret.append(" ");
3394 ret.append(libs);
3395 return ret;
3396 }
3398 /**
3399 *
3400 */
3401 virtual String getVersion()
3402 { return version; }
3404 /**
3405 *
3406 */
3407 virtual int getMajorVersion()
3408 { return majorVersion; }
3410 /**
3411 *
3412 */
3413 virtual int getMinorVersion()
3414 { return minorVersion; }
3416 /**
3417 *
3418 */
3419 virtual int getMicroVersion()
3420 { return microVersion; }
3422 /**
3423 *
3424 */
3425 virtual std::map<String, String> &getAttributes()
3426 { return attrs; }
3428 /**
3429 *
3430 */
3431 virtual std::vector<String> &getRequireList()
3432 { return requireList; }
3434 /**
3435 * Read a file for its details
3436 */
3437 virtual bool readFile(const String &fileName);
3439 /**
3440 * Read a file for its details
3441 */
3442 virtual bool query(const String &name);
3444 private:
3446 void init()
3447 {
3448 //do not set path and prefix here
3449 name = "";
3450 description = "";
3451 cflags = "";
3452 libs = "";
3453 requires = "";
3454 version = "";
3455 majorVersion = 0;
3456 minorVersion = 0;
3457 microVersion = 0;
3458 fileName = "";
3459 attrs.clear();
3460 requireList.clear();
3461 }
3463 void assign(const PkgConfig &other)
3464 {
3465 name = other.name;
3466 path = other.path;
3467 prefix = other.prefix;
3468 description = other.description;
3469 cflags = other.cflags;
3470 libs = other.libs;
3471 requires = other.requires;
3472 version = other.version;
3473 majorVersion = other.majorVersion;
3474 minorVersion = other.minorVersion;
3475 microVersion = other.microVersion;
3476 fileName = other.fileName;
3477 attrs = other.attrs;
3478 requireList = other.requireList;
3479 }
3483 int get(int pos);
3485 int skipwhite(int pos);
3487 int getword(int pos, String &ret);
3489 /**
3490 * Very important
3491 */
3492 bool parseRequires();
3494 void parseVersion();
3496 bool parseLine(const String &lineBuf);
3498 bool parse(const String &buf);
3500 void dumpAttrs();
3502 String name;
3504 String path;
3506 String prefix;
3508 String description;
3510 String cflags;
3512 String libs;
3514 String requires;
3516 String version;
3518 int majorVersion;
3520 int minorVersion;
3522 int microVersion;
3524 String fileName;
3526 std::map<String, String> attrs;
3528 std::vector<String> requireList;
3530 char *parsebuf;
3531 int parselen;
3532 };
3537 /**
3538 * Print a printf()-like formatted error message
3539 */
3540 void MakeBase::error(const char *fmt, ...)
3541 {
3542 va_list args;
3543 va_start(args,fmt);
3544 fprintf(stderr, "Make error line %d: ", line);
3545 vfprintf(stderr, fmt, args);
3546 fprintf(stderr, "\n");
3547 va_end(args) ;
3548 }
3552 /**
3553 * Print a printf()-like formatted trace message
3554 */
3555 void MakeBase::status(const char *fmt, ...)
3556 {
3557 va_list args;
3558 //fprintf(stdout, " ");
3559 va_start(args,fmt);
3560 vfprintf(stdout, fmt, args);
3561 va_end(args);
3562 fprintf(stdout, "\n");
3563 fflush(stdout);
3564 }
3567 /**
3568 * Print a printf()-like formatted trace message
3569 */
3570 void MakeBase::trace(const char *fmt, ...)
3571 {
3572 va_list args;
3573 fprintf(stdout, "Make: ");
3574 va_start(args,fmt);
3575 vfprintf(stdout, fmt, args);
3576 va_end(args) ;
3577 fprintf(stdout, "\n");
3578 fflush(stdout);
3579 }
3583 /**
3584 * Resolve another path relative to this one
3585 */
3586 String MakeBase::resolve(const String &otherPath)
3587 {
3588 URI otherURI(otherPath);
3589 URI fullURI = uri.resolve(otherURI);
3590 String ret = fullURI.toString();
3591 return ret;
3592 }
3596 /**
3597 * Check if a given string matches a given regex pattern
3598 */
3599 bool MakeBase::regexMatch(const String &str, const String &pattern)
3600 {
3601 const TRexChar *terror = NULL;
3602 const TRexChar *cpat = pattern.c_str();
3603 TRex *expr = trex_compile(cpat, &terror);
3604 if (!expr)
3605 {
3606 if (!terror)
3607 terror = "undefined";
3608 error("compilation error [%s]!\n", terror);
3609 return false;
3610 }
3612 bool ret = true;
3614 const TRexChar *cstr = str.c_str();
3615 if (trex_match(expr, cstr))
3616 {
3617 ret = true;
3618 }
3619 else
3620 {
3621 ret = false;
3622 }
3624 trex_free(expr);
3626 return ret;
3627 }
3629 /**
3630 * Return the suffix, if any, of a file name
3631 */
3632 String MakeBase::getSuffix(const String &fname)
3633 {
3634 if (fname.size() < 2)
3635 return "";
3636 unsigned int pos = fname.find_last_of('.');
3637 if (pos == fname.npos)
3638 return "";
3639 pos++;
3640 String res = fname.substr(pos, fname.size()-pos);
3641 //trace("suffix:%s", res.c_str());
3642 return res;
3643 }
3647 /**
3648 * Break up a string into substrings delimited the characters
3649 * in delimiters. Null-length substrings are ignored
3650 */
3651 std::vector<String> MakeBase::tokenize(const String &str,
3652 const String &delimiters)
3653 {
3655 std::vector<String> res;
3656 char *del = (char *)delimiters.c_str();
3657 String dmp;
3658 for (unsigned int i=0 ; i<str.size() ; i++)
3659 {
3660 char ch = str[i];
3661 char *p = (char *)0;
3662 for (p=del ; *p ; p++)
3663 if (*p == ch)
3664 break;
3665 if (*p)
3666 {
3667 if (dmp.size() > 0)
3668 {
3669 res.push_back(dmp);
3670 dmp.clear();
3671 }
3672 }
3673 else
3674 {
3675 dmp.push_back(ch);
3676 }
3677 }
3678 //Add tail
3679 if (dmp.size() > 0)
3680 {
3681 res.push_back(dmp);
3682 dmp.clear();
3683 }
3685 return res;
3686 }
3690 /**
3691 * replace runs of whitespace with a single space
3692 */
3693 String MakeBase::strip(const String &s)
3694 {
3695 int len = s.size();
3696 String stripped;
3697 for (int i = 0 ; i<len ; i++)
3698 {
3699 char ch = s[i];
3700 if (isspace(ch))
3701 {
3702 stripped.push_back(' ');
3703 for ( ; i<len ; i++)
3704 {
3705 ch = s[i];
3706 if (!isspace(ch))
3707 {
3708 stripped.push_back(ch);
3709 break;
3710 }
3711 }
3712 }
3713 else
3714 {
3715 stripped.push_back(ch);
3716 }
3717 }
3718 return stripped;
3719 }
3721 /**
3722 * remove leading whitespace from each line
3723 */
3724 String MakeBase::leftJustify(const String &s)
3725 {
3726 String out;
3727 int len = s.size();
3728 for (int i = 0 ; i<len ; )
3729 {
3730 char ch;
3731 //Skip to first visible character
3732 while (i<len)
3733 {
3734 ch = s[i];
3735 if (ch == '\n' || ch == '\r'
3736 || !isspace(ch))
3737 break;
3738 i++;
3739 }
3740 //Copy the rest of the line
3741 while (i<len)
3742 {
3743 ch = s[i];
3744 if (ch == '\n' || ch == '\r')
3745 {
3746 if (ch != '\r')
3747 out.push_back('\n');
3748 i++;
3749 break;
3750 }
3751 else
3752 {
3753 out.push_back(ch);
3754 }
3755 i++;
3756 }
3757 }
3758 return out;
3759 }
3762 /**
3763 * Removes whitespace from beginning and end of a string
3764 */
3765 String MakeBase::trim(const String &s)
3766 {
3767 if (s.size() < 1)
3768 return s;
3770 //Find first non-ws char
3771 unsigned int begin = 0;
3772 for ( ; begin < s.size() ; begin++)
3773 {
3774 if (!isspace(s[begin]))
3775 break;
3776 }
3778 //Find first non-ws char, going in reverse
3779 unsigned int end = s.size() - 1;
3780 for ( ; end > begin ; end--)
3781 {
3782 if (!isspace(s[end]))
3783 break;
3784 }
3785 //trace("begin:%d end:%d", begin, end);
3787 String res = s.substr(begin, end-begin+1);
3788 return res;
3789 }
3792 /**
3793 * Return a lower case version of the given string
3794 */
3795 String MakeBase::toLower(const String &s)
3796 {
3797 if (s.size()==0)
3798 return s;
3800 String ret;
3801 for(unsigned int i=0; i<s.size() ; i++)
3802 {
3803 ret.push_back(tolower(s[i]));
3804 }
3805 return ret;
3806 }
3809 /**
3810 * Return the native format of the canonical
3811 * path which we store
3812 */
3813 String MakeBase::getNativePath(const String &path)
3814 {
3815 #ifdef __WIN32__
3816 String npath;
3817 unsigned int firstChar = 0;
3818 if (path.size() >= 3)
3819 {
3820 if (path[0] == '/' &&
3821 isalpha(path[1]) &&
3822 path[2] == ':')
3823 firstChar++;
3824 }
3825 for (unsigned int i=firstChar ; i<path.size() ; i++)
3826 {
3827 char ch = path[i];
3828 if (ch == '/')
3829 npath.push_back('\\');
3830 else
3831 npath.push_back(ch);
3832 }
3833 return npath;
3834 #else
3835 return path;
3836 #endif
3837 }
3840 #ifdef __WIN32__
3841 #include <tchar.h>
3843 static String win32LastError()
3844 {
3846 DWORD dw = GetLastError();
3848 LPVOID str;
3849 FormatMessage(
3850 FORMAT_MESSAGE_ALLOCATE_BUFFER |
3851 FORMAT_MESSAGE_FROM_SYSTEM,
3852 NULL,
3853 dw,
3854 0,
3855 (LPTSTR) &str,
3856 0, NULL );
3857 LPTSTR p = _tcschr((const char *)str, _T('\r'));
3858 if(p != NULL)
3859 { // lose CRLF
3860 *p = _T('\0');
3861 }
3862 String ret = (char *)str;
3863 LocalFree(str);
3865 return ret;
3866 }
3867 #endif
3872 #ifdef __WIN32__
3874 /**
3875 * Execute a system call, using pipes to send data to the
3876 * program's stdin, and reading stdout and stderr.
3877 */
3878 bool MakeBase::executeCommand(const String &command,
3879 const String &inbuf,
3880 String &outbuf,
3881 String &errbuf)
3882 {
3884 status("============ cmd ============\n%s\n=============================",
3885 command.c_str());
3887 outbuf.clear();
3888 errbuf.clear();
3891 /*
3892 I really hate having win32 code in this program, but the
3893 read buffer in command.com and cmd.exe are just too small
3894 for the large commands we need for compiling and linking.
3895 */
3897 bool ret = true;
3899 //# Allocate a separate buffer for safety
3900 char *paramBuf = new char[command.size() + 1];
3901 if (!paramBuf)
3902 {
3903 error("executeCommand cannot allocate command buffer");
3904 return false;
3905 }
3906 strcpy(paramBuf, (char *)command.c_str());
3908 //# Go to http://msdn2.microsoft.com/en-us/library/ms682499.aspx
3909 //# to see how Win32 pipes work
3911 //# Create pipes
3912 SECURITY_ATTRIBUTES saAttr;
3913 saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
3914 saAttr.bInheritHandle = TRUE;
3915 saAttr.lpSecurityDescriptor = NULL;
3916 HANDLE stdinRead, stdinWrite;
3917 HANDLE stdoutRead, stdoutWrite;
3918 HANDLE stderrRead, stderrWrite;
3919 if (!CreatePipe(&stdinRead, &stdinWrite, &saAttr, 0))
3920 {
3921 error("executeProgram: could not create pipe");
3922 delete[] paramBuf;
3923 return false;
3924 }
3925 SetHandleInformation(stdinWrite, HANDLE_FLAG_INHERIT, 0);
3926 if (!CreatePipe(&stdoutRead, &stdoutWrite, &saAttr, 0))
3927 {
3928 error("executeProgram: could not create pipe");
3929 delete[] paramBuf;
3930 return false;
3931 }
3932 SetHandleInformation(stdoutRead, HANDLE_FLAG_INHERIT, 0);
3933 if (!CreatePipe(&stderrRead, &stderrWrite, &saAttr, 0))
3934 {
3935 error("executeProgram: could not create pipe");
3936 delete[] paramBuf;
3937 return false;
3938 }
3939 SetHandleInformation(stderrRead, HANDLE_FLAG_INHERIT, 0);
3941 // Create the process
3942 STARTUPINFO siStartupInfo;
3943 PROCESS_INFORMATION piProcessInfo;
3944 memset(&siStartupInfo, 0, sizeof(siStartupInfo));
3945 memset(&piProcessInfo, 0, sizeof(piProcessInfo));
3946 siStartupInfo.cb = sizeof(siStartupInfo);
3947 siStartupInfo.hStdError = stderrWrite;
3948 siStartupInfo.hStdOutput = stdoutWrite;
3949 siStartupInfo.hStdInput = stdinRead;
3950 siStartupInfo.dwFlags |= STARTF_USESTDHANDLES;
3952 if (!CreateProcess(NULL, paramBuf, NULL, NULL, true,
3953 0, NULL, NULL, &siStartupInfo,
3954 &piProcessInfo))
3955 {
3956 error("executeCommand : could not create process : %s",
3957 win32LastError().c_str());
3958 ret = false;
3959 }
3961 delete[] paramBuf;
3963 DWORD bytesWritten;
3964 if (inbuf.size()>0 &&
3965 !WriteFile(stdinWrite, inbuf.c_str(), inbuf.size(),
3966 &bytesWritten, NULL))
3967 {
3968 error("executeCommand: could not write to pipe");
3969 return false;
3970 }
3971 if (!CloseHandle(stdinWrite))
3972 {
3973 error("executeCommand: could not close write pipe");
3974 return false;
3975 }
3976 if (!CloseHandle(stdoutWrite))
3977 {
3978 error("executeCommand: could not close read pipe");
3979 return false;
3980 }
3981 if (!CloseHandle(stderrWrite))
3982 {
3983 error("executeCommand: could not close read pipe");
3984 return false;
3985 }
3987 bool lastLoop = false;
3988 while (true)
3989 {
3990 DWORD avail;
3991 DWORD bytesRead;
3992 char readBuf[4096];
3994 //trace("## stderr");
3995 PeekNamedPipe(stderrRead, NULL, 0, NULL, &avail, NULL);
3996 if (avail > 0)
3997 {
3998 bytesRead = 0;
3999 if (avail>4096) avail = 4096;
4000 ReadFile(stderrRead, readBuf, avail, &bytesRead, NULL);
4001 if (bytesRead > 0)
4002 {
4003 for (unsigned int i=0 ; i<bytesRead ; i++)
4004 errbuf.push_back(readBuf[i]);
4005 }
4006 }
4008 //trace("## stdout");
4009 PeekNamedPipe(stdoutRead, NULL, 0, NULL, &avail, NULL);
4010 if (avail > 0)
4011 {
4012 bytesRead = 0;
4013 if (avail>4096) avail = 4096;
4014 ReadFile(stdoutRead, readBuf, avail, &bytesRead, NULL);
4015 if (bytesRead > 0)
4016 {
4017 for (unsigned int i=0 ; i<bytesRead ; i++)
4018 outbuf.push_back(readBuf[i]);
4019 }
4020 }
4022 //Was this the final check after program done?
4023 if (lastLoop)
4024 break;
4026 DWORD exitCode;
4027 GetExitCodeProcess(piProcessInfo.hProcess, &exitCode);
4028 if (exitCode != STILL_ACTIVE)
4029 lastLoop = true;
4031 Sleep(10);
4032 }
4033 //trace("outbuf:%s", outbuf.c_str());
4034 if (!CloseHandle(stdoutRead))
4035 {
4036 error("executeCommand: could not close read pipe");
4037 return false;
4038 }
4039 if (!CloseHandle(stderrRead))
4040 {
4041 error("executeCommand: could not close read pipe");
4042 return false;
4043 }
4045 DWORD exitCode;
4046 GetExitCodeProcess(piProcessInfo.hProcess, &exitCode);
4047 //trace("exit code:%d", exitCode);
4048 if (exitCode != 0)
4049 {
4050 ret = false;
4051 }
4053 CloseHandle(piProcessInfo.hProcess);
4054 CloseHandle(piProcessInfo.hThread);
4056 return ret;
4058 }
4060 #else /*do it unix style*/
4062 /**
4063 * Execute a system call, using pipes to send data to the
4064 * program's stdin, and reading stdout and stderr.
4065 */
4066 bool MakeBase::executeCommand(const String &command,
4067 const String &inbuf,
4068 String &outbuf,
4069 String &errbuf)
4070 {
4072 status("============ cmd ============\n%s\n=============================",
4073 command.c_str());
4075 outbuf.clear();
4076 errbuf.clear();
4079 int outfds[2];
4080 if (pipe(outfds) < 0)
4081 return false;
4082 int errfds[2];
4083 if (pipe(errfds) < 0)
4084 return false;
4085 int pid = fork();
4086 if (pid < 0)
4087 {
4088 close(outfds[0]);
4089 close(outfds[1]);
4090 close(errfds[0]);
4091 close(errfds[1]);
4092 error("launch of command '%s' failed : %s",
4093 command.c_str(), strerror(errno));
4094 return false;
4095 }
4096 else if (pid > 0) // parent
4097 {
4098 close(outfds[1]);
4099 close(errfds[1]);
4100 }
4101 else // == 0, child
4102 {
4103 close(outfds[0]);
4104 dup2(outfds[1], STDOUT_FILENO);
4105 close(outfds[1]);
4106 close(errfds[0]);
4107 dup2(errfds[1], STDERR_FILENO);
4108 close(errfds[1]);
4110 char *args[4];
4111 args[0] = (char *)"sh";
4112 args[1] = (char *)"-c";
4113 args[2] = (char *)command.c_str();
4114 args[3] = NULL;
4115 execv("/bin/sh", args);
4116 _exit(EXIT_FAILURE);
4117 }
4119 String outb;
4120 String errb;
4122 while (1)
4123 {
4124 unsigned char outch;
4125 int rout = read(outfds[0], &outch, 1);
4126 if (rout>0)
4127 outb.push_back(outch);
4128 unsigned char errch;
4129 int rerr = read(errfds[0], &errch, 1);
4130 if (rerr>0)
4131 errb.push_back(errch);
4132 if (rout <=0 && rerr <=0)
4133 break;
4134 }
4136 int childReturnValue;
4137 wait(&childReturnValue);
4139 close(outfds[0]);
4140 close(errfds[0]);
4142 outbuf = outb;
4143 errbuf = errb;
4145 if (childReturnValue != 0)
4146 {
4147 error("exec of command '%s' failed : %s",
4148 command.c_str(), strerror(childReturnValue));
4149 return false;
4150 }
4152 return true;
4153 }
4155 #endif
4160 bool MakeBase::listDirectories(const String &baseName,
4161 const String &dirName,
4162 std::vector<String> &res)
4163 {
4164 res.push_back(dirName);
4165 String fullPath = baseName;
4166 if (dirName.size()>0)
4167 {
4168 fullPath.append("/");
4169 fullPath.append(dirName);
4170 }
4171 DIR *dir = opendir(fullPath.c_str());
4172 while (true)
4173 {
4174 struct dirent *de = readdir(dir);
4175 if (!de)
4176 break;
4178 //Get the directory member name
4179 String s = de->d_name;
4180 if (s.size() == 0 || s[0] == '.')
4181 continue;
4182 String childName = dirName;
4183 childName.append("/");
4184 childName.append(s);
4186 String fullChildPath = baseName;
4187 fullChildPath.append("/");
4188 fullChildPath.append(childName);
4189 struct stat finfo;
4190 String childNative = getNativePath(fullChildPath);
4191 if (stat(childNative.c_str(), &finfo)<0)
4192 {
4193 error("cannot stat file:%s", childNative.c_str());
4194 }
4195 else if (S_ISDIR(finfo.st_mode))
4196 {
4197 //trace("directory: %s", childName.c_str());
4198 if (!listDirectories(baseName, childName, res))
4199 return false;
4200 }
4201 }
4202 closedir(dir);
4204 return true;
4205 }
4208 bool MakeBase::listFiles(const String &baseDir,
4209 const String &dirName,
4210 std::vector<String> &res)
4211 {
4212 String fullDir = baseDir;
4213 if (dirName.size()>0)
4214 {
4215 fullDir.append("/");
4216 fullDir.append(dirName);
4217 }
4218 String dirNative = getNativePath(fullDir);
4220 std::vector<String> subdirs;
4221 DIR *dir = opendir(dirNative.c_str());
4222 if (!dir)
4223 {
4224 error("Could not open directory %s : %s",
4225 dirNative.c_str(), strerror(errno));
4226 return false;
4227 }
4228 while (true)
4229 {
4230 struct dirent *de = readdir(dir);
4231 if (!de)
4232 break;
4234 //Get the directory member name
4235 String s = de->d_name;
4236 if (s.size() == 0 || s[0] == '.')
4237 continue;
4238 String childName;
4239 if (dirName.size()>0)
4240 {
4241 childName.append(dirName);
4242 childName.append("/");
4243 }
4244 childName.append(s);
4245 String fullChild = baseDir;
4246 fullChild.append("/");
4247 fullChild.append(childName);
4249 if (isDirectory(fullChild))
4250 {
4251 //trace("directory: %s", childName.c_str());
4252 if (!listFiles(baseDir, childName, res))
4253 return false;
4254 continue;
4255 }
4256 else if (!isRegularFile(fullChild))
4257 {
4258 error("unknown file:%s", childName.c_str());
4259 return false;
4260 }
4262 //all done!
4263 res.push_back(childName);
4265 }
4266 closedir(dir);
4268 return true;
4269 }
4272 /**
4273 * Several different classes extend MakeBase. By "propRef", we mean
4274 * the one holding the properties. Likely "Make" itself
4275 */
4276 bool MakeBase::listFiles(MakeBase &propRef, FileSet &fileSet)
4277 {
4278 //before doing the list, resolve any property references
4279 //that might have been specified in the directory name, such as ${src}
4280 String fsDir = fileSet.getDirectory();
4281 String dir;
4282 if (!propRef.getSubstitutions(fsDir, dir))
4283 return false;
4284 String baseDir = propRef.resolve(dir);
4285 std::vector<String> fileList;
4286 if (!listFiles(baseDir, "", fileList))
4287 return false;
4289 std::vector<String> includes = fileSet.getIncludes();
4290 std::vector<String> excludes = fileSet.getExcludes();
4292 std::vector<String> incs;
4293 std::vector<String>::iterator iter;
4295 std::sort(fileList.begin(), fileList.end());
4297 //If there are <includes>, then add files to the output
4298 //in the order of the include list
4299 if (includes.size()==0)
4300 incs = fileList;
4301 else
4302 {
4303 for (iter = includes.begin() ; iter != includes.end() ; iter++)
4304 {
4305 String &pattern = *iter;
4306 std::vector<String>::iterator siter;
4307 for (siter = fileList.begin() ; siter != fileList.end() ; siter++)
4308 {
4309 String s = *siter;
4310 if (regexMatch(s, pattern))
4311 {
4312 //trace("INCLUDED:%s", s.c_str());
4313 incs.push_back(s);
4314 }
4315 }
4316 }
4317 }
4319 //Now trim off the <excludes>
4320 std::vector<String> res;
4321 for (iter = incs.begin() ; iter != incs.end() ; iter++)
4322 {
4323 String s = *iter;
4324 bool skipme = false;
4325 std::vector<String>::iterator siter;
4326 for (siter = excludes.begin() ; siter != excludes.end() ; siter++)
4327 {
4328 String &pattern = *siter;
4329 if (regexMatch(s, pattern))
4330 {
4331 //trace("EXCLUDED:%s", s.c_str());
4332 skipme = true;
4333 break;
4334 }
4335 }
4336 if (!skipme)
4337 res.push_back(s);
4338 }
4340 fileSet.setFiles(res);
4342 return true;
4343 }
4346 /**
4347 * 0 == all, 1 = cflags, 2 = libs
4348 */
4349 bool MakeBase::pkgConfigRecursive(const String packageName,
4350 const String &path,
4351 const String &prefix,
4352 int query,
4353 String &result,
4354 std::set<String> &deplist)
4355 {
4356 PkgConfig pkgConfig;
4357 if (path.size() > 0)
4358 pkgConfig.setPath(path);
4359 if (prefix.size() > 0)
4360 pkgConfig.setPrefix(prefix);
4361 if (!pkgConfig.query(packageName))
4362 return false;
4363 if (query == 0)
4364 result = pkgConfig.getAll();
4365 else if (query == 1)
4366 result = pkgConfig.getCflags();
4367 else
4368 result = pkgConfig.getLibs();
4369 deplist.insert(packageName);
4370 std::vector<String> list = pkgConfig.getRequireList();
4371 for (unsigned int i = 0 ; i<list.size() ; i++)
4372 {
4373 String depPkgName = list[i];
4374 if (deplist.find(depPkgName) != deplist.end())
4375 continue;
4376 String val;
4377 if (!pkgConfigRecursive(depPkgName, path, prefix, query, val, deplist))
4378 {
4379 error("Based on 'requires' attribute of package '%s'", packageName.c_str());
4380 return false;
4381 }
4382 result.append(" ");
4383 result.append(val);
4384 }
4386 return true;
4387 }
4389 bool MakeBase::pkgConfigQuery(const String &packageName, int query, String &result)
4390 {
4391 std::set<String> deplist;
4392 String path = getProperty("pkg-config-path");
4393 if (path.size()>0)
4394 path = resolve(path);
4395 String prefix = getProperty("pkg-config-prefix");
4396 String val;
4397 if (!pkgConfigRecursive(packageName, path, prefix, query, val, deplist))
4398 return false;
4399 result = val;
4400 return true;
4401 }
4405 /**
4406 * replace a variable ref like ${a} with a value
4407 */
4408 bool MakeBase::lookupProperty(const String &propertyName, String &result)
4409 {
4410 String varname = propertyName;
4411 if (envPrefix.size() > 0 &&
4412 varname.compare(0, envPrefix.size(), envPrefix) == 0)
4413 {
4414 varname = varname.substr(envPrefix.size());
4415 char *envstr = getenv(varname.c_str());
4416 if (!envstr)
4417 {
4418 error("environment variable '%s' not defined", varname.c_str());
4419 return false;
4420 }
4421 result = envstr;
4422 }
4423 else if (pcPrefix.size() > 0 &&
4424 varname.compare(0, pcPrefix.size(), pcPrefix) == 0)
4425 {
4426 varname = varname.substr(pcPrefix.size());
4427 String val;
4428 if (!pkgConfigQuery(varname, 0, val))
4429 return false;
4430 result = val;
4431 }
4432 else if (pccPrefix.size() > 0 &&
4433 varname.compare(0, pccPrefix.size(), pccPrefix) == 0)
4434 {
4435 varname = varname.substr(pccPrefix.size());
4436 String val;
4437 if (!pkgConfigQuery(varname, 1, val))
4438 return false;
4439 result = val;
4440 }
4441 else if (pclPrefix.size() > 0 &&
4442 varname.compare(0, pclPrefix.size(), pclPrefix) == 0)
4443 {
4444 varname = varname.substr(pclPrefix.size());
4445 String val;
4446 if (!pkgConfigQuery(varname, 2, val))
4447 return false;
4448 result = val;
4449 }
4450 else
4451 {
4452 std::map<String, String>::iterator iter;
4453 iter = properties.find(varname);
4454 if (iter != properties.end())
4455 {
4456 result = iter->second;
4457 }
4458 else
4459 {
4460 error("property '%s' not found", varname.c_str());
4461 return false;
4462 }
4463 }
4464 return true;
4465 }
4470 /**
4471 * Analyse a string, looking for any substitutions or other
4472 * things that need resolution
4473 */
4474 bool MakeBase::getSubstitutionsRecursive(const String &str,
4475 String &result, int depth)
4476 {
4477 if (depth > 10)
4478 {
4479 error("nesting of substitutions too deep (>10) for '%s'",
4480 str.c_str());
4481 return false;
4482 }
4483 String s = trim(str);
4484 int len = (int)s.size();
4485 String val;
4486 for (int i=0 ; i<len ; i++)
4487 {
4488 char ch = s[i];
4489 if (ch == '$' && s[i+1] == '{')
4490 {
4491 String varname;
4492 int j = i+2;
4493 for ( ; j<len ; j++)
4494 {
4495 ch = s[j];
4496 if (ch == '$' && s[j+1] == '{')
4497 {
4498 error("attribute %s cannot have nested variable references",
4499 s.c_str());
4500 return false;
4501 }
4502 else if (ch == '}')
4503 {
4504 varname = trim(varname);
4505 String varval;
4506 if (!lookupProperty(varname, varval))
4507 return false;
4508 String varval2;
4509 //Now see if the answer has ${} in it, too
4510 if (!getSubstitutionsRecursive(varval, varval2, depth + 1))
4511 return false;
4512 val.append(varval2);
4513 break;
4514 }
4515 else
4516 {
4517 varname.push_back(ch);
4518 }
4519 }
4520 i = j;
4521 }
4522 else
4523 {
4524 val.push_back(ch);
4525 }
4526 }
4527 result = val;
4528 return true;
4529 }
4531 /**
4532 * Analyse a string, looking for any substitutions or other
4533 * things that need resilution
4534 */
4535 bool MakeBase::getSubstitutions(const String &str, String &result)
4536 {
4537 return getSubstitutionsRecursive(str, result, 0);
4538 }
4542 /**
4543 * replace variable refs like ${a} with their values
4544 * Assume that the string has already been syntax validated
4545 */
4546 String MakeBase::eval(const String &s, const String &defaultVal)
4547 {
4548 if (s.size()==0)
4549 return defaultVal;
4550 String ret;
4551 if (getSubstitutions(s, ret))
4552 return ret;
4553 else
4554 return defaultVal;
4555 }
4558 /**
4559 * replace variable refs like ${a} with their values
4560 * return true or false
4561 * Assume that the string has already been syntax validated
4562 */
4563 bool MakeBase::evalBool(const String &s, bool defaultVal)
4564 {
4565 if (s.size()==0)
4566 return defaultVal;
4567 String val = eval(s, "false");
4568 if (val.size()==0)
4569 return defaultVal;
4570 if (val == "true" || val == "TRUE")
4571 return true;
4572 else
4573 return false;
4574 }
4577 /**
4578 * Get a string attribute, testing it for proper syntax and
4579 * property names.
4580 */
4581 bool MakeBase::getAttribute(Element *elem, const String &name,
4582 String &result)
4583 {
4584 String s = elem->getAttribute(name);
4585 String tmp;
4586 bool ret = getSubstitutions(s, tmp);
4587 if (ret)
4588 result = s; //assign -if- ok
4589 return ret;
4590 }
4593 /**
4594 * Get a string value, testing it for proper syntax and
4595 * property names.
4596 */
4597 bool MakeBase::getValue(Element *elem, String &result)
4598 {
4599 String s = elem->getValue();
4600 String tmp;
4601 bool ret = getSubstitutions(s, tmp);
4602 if (ret)
4603 result = s; //assign -if- ok
4604 return ret;
4605 }
4610 /**
4611 * Parse a <patternset> entry
4612 */
4613 bool MakeBase::parsePatternSet(Element *elem,
4614 MakeBase &propRef,
4615 std::vector<String> &includes,
4616 std::vector<String> &excludes
4617 )
4618 {
4619 std::vector<Element *> children = elem->getChildren();
4620 for (unsigned int i=0 ; i<children.size() ; i++)
4621 {
4622 Element *child = children[i];
4623 String tagName = child->getName();
4624 if (tagName == "exclude")
4625 {
4626 String fname;
4627 if (!propRef.getAttribute(child, "name", fname))
4628 return false;
4629 //trace("EXCLUDE: %s", fname.c_str());
4630 excludes.push_back(fname);
4631 }
4632 else if (tagName == "include")
4633 {
4634 String fname;
4635 if (!propRef.getAttribute(child, "name", fname))
4636 return false;
4637 //trace("INCLUDE: %s", fname.c_str());
4638 includes.push_back(fname);
4639 }
4640 }
4642 return true;
4643 }
4648 /**
4649 * Parse a <fileset> entry, and determine which files
4650 * should be included
4651 */
4652 bool MakeBase::parseFileSet(Element *elem,
4653 MakeBase &propRef,
4654 FileSet &fileSet)
4655 {
4656 String name = elem->getName();
4657 if (name != "fileset")
4658 {
4659 error("expected <fileset>");
4660 return false;
4661 }
4664 std::vector<String> includes;
4665 std::vector<String> excludes;
4667 //A fileset has one implied patternset
4668 if (!parsePatternSet(elem, propRef, includes, excludes))
4669 {
4670 return false;
4671 }
4672 //Look for child tags, including more patternsets
4673 std::vector<Element *> children = elem->getChildren();
4674 for (unsigned int i=0 ; i<children.size() ; i++)
4675 {
4676 Element *child = children[i];
4677 String tagName = child->getName();
4678 if (tagName == "patternset")
4679 {
4680 if (!parsePatternSet(child, propRef, includes, excludes))
4681 {
4682 return false;
4683 }
4684 }
4685 }
4687 String dir;
4688 //Now do the stuff
4689 //Get the base directory for reading file names
4690 if (!propRef.getAttribute(elem, "dir", dir))
4691 return false;
4693 fileSet.setDirectory(dir);
4694 fileSet.setIncludes(includes);
4695 fileSet.setExcludes(excludes);
4697 /*
4698 std::vector<String> fileList;
4699 if (dir.size() > 0)
4700 {
4701 String baseDir = propRef.resolve(dir);
4702 if (!listFiles(baseDir, "", includes, excludes, fileList))
4703 return false;
4704 }
4705 std::sort(fileList.begin(), fileList.end());
4706 result = fileList;
4707 */
4710 /*
4711 for (unsigned int i=0 ; i<result.size() ; i++)
4712 {
4713 trace("RES:%s", result[i].c_str());
4714 }
4715 */
4718 return true;
4719 }
4721 /**
4722 * Parse a <filelist> entry. This is far simpler than FileSet,
4723 * since no directory scanning is needed. The file names are listed
4724 * explicitly.
4725 */
4726 bool MakeBase::parseFileList(Element *elem,
4727 MakeBase &propRef,
4728 FileList &fileList)
4729 {
4730 std::vector<String> fnames;
4731 //Look for child tags, namely "file"
4732 std::vector<Element *> children = elem->getChildren();
4733 for (unsigned int i=0 ; i<children.size() ; i++)
4734 {
4735 Element *child = children[i];
4736 String tagName = child->getName();
4737 if (tagName == "file")
4738 {
4739 String fname = child->getAttribute("name");
4740 if (fname.size()==0)
4741 {
4742 error("<file> element requires name="" attribute");
4743 return false;
4744 }
4745 fnames.push_back(fname);
4746 }
4747 else
4748 {
4749 error("tag <%s> not allowed in <fileset>", tagName.c_str());
4750 return false;
4751 }
4752 }
4754 String dir;
4755 //Get the base directory for reading file names
4756 if (!propRef.getAttribute(elem, "dir", dir))
4757 return false;
4758 fileList.setDirectory(dir);
4759 fileList.setFiles(fnames);
4761 return true;
4762 }
4766 /**
4767 * Create a directory, making intermediate dirs
4768 * if necessary
4769 */
4770 bool MakeBase::createDirectory(const String &dirname)
4771 {
4772 //trace("## createDirectory: %s", dirname.c_str());
4773 //## first check if it exists
4774 struct stat finfo;
4775 String nativeDir = getNativePath(dirname);
4776 char *cnative = (char *) nativeDir.c_str();
4777 #ifdef __WIN32__
4778 if (strlen(cnative)==2 && cnative[1]==':')
4779 return true;
4780 #endif
4781 if (stat(cnative, &finfo)==0)
4782 {
4783 if (!S_ISDIR(finfo.st_mode))
4784 {
4785 error("mkdir: file %s exists but is not a directory",
4786 cnative);
4787 return false;
4788 }
4789 else //exists
4790 {
4791 return true;
4792 }
4793 }
4795 //## 2: pull off the last path segment, if any,
4796 //## to make the dir 'above' this one, if necessary
4797 unsigned int pos = dirname.find_last_of('/');
4798 if (pos>0 && pos != dirname.npos)
4799 {
4800 String subpath = dirname.substr(0, pos);
4801 //A letter root (c:) ?
4802 if (!createDirectory(subpath))
4803 return false;
4804 }
4806 //## 3: now make
4807 #ifdef __WIN32__
4808 if (mkdir(cnative)<0)
4809 #else
4810 if (mkdir(cnative, S_IRWXU | S_IRWXG | S_IRWXO)<0)
4811 #endif
4812 {
4813 error("cannot make directory '%s' : %s",
4814 cnative, strerror(errno));
4815 return false;
4816 }
4818 return true;
4819 }
4822 /**
4823 * Remove a directory recursively
4824 */
4825 bool MakeBase::removeDirectory(const String &dirName)
4826 {
4827 char *dname = (char *)dirName.c_str();
4829 DIR *dir = opendir(dname);
4830 if (!dir)
4831 {
4832 //# Let this fail nicely.
4833 return true;
4834 //error("error opening directory %s : %s", dname, strerror(errno));
4835 //return false;
4836 }
4838 while (true)
4839 {
4840 struct dirent *de = readdir(dir);
4841 if (!de)
4842 break;
4844 //Get the directory member name
4845 String s = de->d_name;
4846 if (s.size() == 0 || s[0] == '.')
4847 continue;
4848 String childName;
4849 if (dirName.size() > 0)
4850 {
4851 childName.append(dirName);
4852 childName.append("/");
4853 }
4854 childName.append(s);
4857 struct stat finfo;
4858 String childNative = getNativePath(childName);
4859 char *cnative = (char *)childNative.c_str();
4860 if (stat(cnative, &finfo)<0)
4861 {
4862 error("cannot stat file:%s", cnative);
4863 }
4864 else if (S_ISDIR(finfo.st_mode))
4865 {
4866 //trace("DEL dir: %s", childName.c_str());
4867 if (!removeDirectory(childName))
4868 {
4869 return false;
4870 }
4871 }
4872 else if (!S_ISREG(finfo.st_mode))
4873 {
4874 //trace("not regular: %s", cnative);
4875 }
4876 else
4877 {
4878 //trace("DEL file: %s", childName.c_str());
4879 if (remove(cnative)<0)
4880 {
4881 error("error deleting %s : %s",
4882 cnative, strerror(errno));
4883 return false;
4884 }
4885 }
4886 }
4887 closedir(dir);
4889 //Now delete the directory
4890 String native = getNativePath(dirName);
4891 if (rmdir(native.c_str())<0)
4892 {
4893 error("could not delete directory %s : %s",
4894 native.c_str() , strerror(errno));
4895 return false;
4896 }
4898 return true;
4900 }
4903 /**
4904 * Copy a file from one name to another. Perform only if needed
4905 */
4906 bool MakeBase::copyFile(const String &srcFile, const String &destFile)
4907 {
4908 //# 1 Check up-to-date times
4909 String srcNative = getNativePath(srcFile);
4910 struct stat srcinfo;
4911 if (stat(srcNative.c_str(), &srcinfo)<0)
4912 {
4913 error("source file %s for copy does not exist",
4914 srcNative.c_str());
4915 return false;
4916 }
4918 String destNative = getNativePath(destFile);
4919 struct stat destinfo;
4920 if (stat(destNative.c_str(), &destinfo)==0)
4921 {
4922 if (destinfo.st_mtime >= srcinfo.st_mtime)
4923 return true;
4924 }
4926 //# 2 prepare a destination directory if necessary
4927 unsigned int pos = destFile.find_last_of('/');
4928 if (pos != destFile.npos)
4929 {
4930 String subpath = destFile.substr(0, pos);
4931 if (!createDirectory(subpath))
4932 return false;
4933 }
4935 //# 3 do the data copy
4936 #ifndef __WIN32__
4938 FILE *srcf = fopen(srcNative.c_str(), "rb");
4939 if (!srcf)
4940 {
4941 error("copyFile cannot open '%s' for reading", srcNative.c_str());
4942 return false;
4943 }
4944 FILE *destf = fopen(destNative.c_str(), "wb");
4945 if (!destf)
4946 {
4947 error("copyFile cannot open %s for writing", srcNative.c_str());
4948 return false;
4949 }
4951 while (!feof(srcf))
4952 {
4953 int ch = fgetc(srcf);
4954 if (ch<0)
4955 break;
4956 fputc(ch, destf);
4957 }
4959 fclose(destf);
4960 fclose(srcf);
4962 #else
4964 if (!CopyFile(srcNative.c_str(), destNative.c_str(), false))
4965 {
4966 error("copyFile from %s to %s failed",
4967 srcNative.c_str(), destNative.c_str());
4968 return false;
4969 }
4971 #endif /* __WIN32__ */
4974 return true;
4975 }
4979 /**
4980 * Tests if the file exists and is a regular file
4981 */
4982 bool MakeBase::isRegularFile(const String &fileName)
4983 {
4984 String native = getNativePath(fileName);
4985 struct stat finfo;
4987 //Exists?
4988 if (stat(native.c_str(), &finfo)<0)
4989 return false;
4992 //check the file mode
4993 if (!S_ISREG(finfo.st_mode))
4994 return false;
4996 return true;
4997 }
4999 /**
5000 * Tests if the file exists and is a directory
5001 */
5002 bool MakeBase::isDirectory(const String &fileName)
5003 {
5004 String native = getNativePath(fileName);
5005 struct stat finfo;
5007 //Exists?
5008 if (stat(native.c_str(), &finfo)<0)
5009 return false;
5012 //check the file mode
5013 if (!S_ISDIR(finfo.st_mode))
5014 return false;
5016 return true;
5017 }
5021 /**
5022 * Tests is the modification of fileA is newer than fileB
5023 */
5024 bool MakeBase::isNewerThan(const String &fileA, const String &fileB)
5025 {
5026 //trace("isNewerThan:'%s' , '%s'", fileA.c_str(), fileB.c_str());
5027 String nativeA = getNativePath(fileA);
5028 struct stat infoA;
5029 //IF source does not exist, NOT newer
5030 if (stat(nativeA.c_str(), &infoA)<0)
5031 {
5032 return false;
5033 }
5035 String nativeB = getNativePath(fileB);
5036 struct stat infoB;
5037 //IF dest does not exist, YES, newer
5038 if (stat(nativeB.c_str(), &infoB)<0)
5039 {
5040 return true;
5041 }
5043 //check the actual times
5044 if (infoA.st_mtime > infoB.st_mtime)
5045 {
5046 return true;
5047 }
5049 return false;
5050 }
5053 //########################################################################
5054 //# P K G C O N F I G
5055 //########################################################################
5058 /**
5059 * Get a character from the buffer at pos. If out of range,
5060 * return -1 for safety
5061 */
5062 int PkgConfig::get(int pos)
5063 {
5064 if (pos>parselen)
5065 return -1;
5066 return parsebuf[pos];
5067 }
5071 /**
5072 * Skip over all whitespace characters beginning at pos. Return
5073 * the position of the first non-whitespace character.
5074 * Pkg-config is line-oriented, so check for newline
5075 */
5076 int PkgConfig::skipwhite(int pos)
5077 {
5078 while (pos < parselen)
5079 {
5080 int ch = get(pos);
5081 if (ch < 0)
5082 break;
5083 if (!isspace(ch))
5084 break;
5085 pos++;
5086 }
5087 return pos;
5088 }
5091 /**
5092 * Parse the buffer beginning at pos, for a word. Fill
5093 * 'ret' with the result. Return the position after the
5094 * word.
5095 */
5096 int PkgConfig::getword(int pos, String &ret)
5097 {
5098 while (pos < parselen)
5099 {
5100 int ch = get(pos);
5101 if (ch < 0)
5102 break;
5103 if (!isalnum(ch) && ch != '_' && ch != '-' && ch != '+' && ch != '.')
5104 break;
5105 ret.push_back((char)ch);
5106 pos++;
5107 }
5108 return pos;
5109 }
5111 bool PkgConfig::parseRequires()
5112 {
5113 if (requires.size() == 0)
5114 return true;
5115 parsebuf = (char *)requires.c_str();
5116 parselen = requires.size();
5117 int pos = 0;
5118 while (pos < parselen)
5119 {
5120 pos = skipwhite(pos);
5121 String val;
5122 int pos2 = getword(pos, val);
5123 if (pos2 == pos)
5124 break;
5125 pos = pos2;
5126 //trace("val %s", val.c_str());
5127 requireList.push_back(val);
5128 }
5129 return true;
5130 }
5133 static int getint(const String str)
5134 {
5135 char *s = (char *)str.c_str();
5136 char *ends = NULL;
5137 long val = strtol(s, &ends, 10);
5138 if (ends == s)
5139 return 0L;
5140 else
5141 return val;
5142 }
5144 void PkgConfig::parseVersion()
5145 {
5146 if (version.size() == 0)
5147 return;
5148 String s1, s2, s3;
5149 unsigned int pos = 0;
5150 unsigned int pos2 = version.find('.', pos);
5151 if (pos2 == version.npos)
5152 {
5153 s1 = version;
5154 }
5155 else
5156 {
5157 s1 = version.substr(pos, pos2-pos);
5158 pos = pos2;
5159 pos++;
5160 if (pos < version.size())
5161 {
5162 pos2 = version.find('.', pos);
5163 if (pos2 == version.npos)
5164 {
5165 s2 = version.substr(pos, version.size()-pos);
5166 }
5167 else
5168 {
5169 s2 = version.substr(pos, pos2-pos);
5170 pos = pos2;
5171 pos++;
5172 if (pos < version.size())
5173 s3 = version.substr(pos, pos2-pos);
5174 }
5175 }
5176 }
5178 majorVersion = getint(s1);
5179 minorVersion = getint(s2);
5180 microVersion = getint(s3);
5181 //trace("version:%d.%d.%d", majorVersion,
5182 // minorVersion, microVersion );
5183 }
5186 bool PkgConfig::parseLine(const String &lineBuf)
5187 {
5188 parsebuf = (char *)lineBuf.c_str();
5189 parselen = lineBuf.size();
5190 int pos = 0;
5192 while (pos < parselen)
5193 {
5194 String attrName;
5195 pos = skipwhite(pos);
5196 int ch = get(pos);
5197 if (ch == '#')
5198 {
5199 //comment. eat the rest of the line
5200 while (pos < parselen)
5201 {
5202 ch = get(pos);
5203 if (ch == '\n' || ch < 0)
5204 break;
5205 pos++;
5206 }
5207 continue;
5208 }
5209 pos = getword(pos, attrName);
5210 if (attrName.size() == 0)
5211 continue;
5213 pos = skipwhite(pos);
5214 ch = get(pos);
5215 if (ch != ':' && ch != '=')
5216 {
5217 error("expected ':' or '='");
5218 return false;
5219 }
5220 pos++;
5221 pos = skipwhite(pos);
5222 String attrVal;
5223 while (pos < parselen)
5224 {
5225 ch = get(pos);
5226 if (ch == '\n' || ch < 0)
5227 break;
5228 else if (ch == '$' && get(pos+1) == '{')
5229 {
5230 //# this is a ${substitution}
5231 pos += 2;
5232 String subName;
5233 while (pos < parselen)
5234 {
5235 ch = get(pos);
5236 if (ch < 0)
5237 {
5238 error("unterminated substitution");
5239 return false;
5240 }
5241 else if (ch == '}')
5242 break;
5243 else
5244 subName.push_back((char)ch);
5245 pos++;
5246 }
5247 //trace("subName:%s %s", subName.c_str(), prefix.c_str());
5248 if (subName == "prefix" && prefix.size()>0)
5249 {
5250 attrVal.append(prefix);
5251 //trace("prefix override:%s", prefix.c_str());
5252 }
5253 else
5254 {
5255 String subVal = attrs[subName];
5256 //trace("subVal:%s", subVal.c_str());
5257 attrVal.append(subVal);
5258 }
5259 }
5260 else
5261 attrVal.push_back((char)ch);
5262 pos++;
5263 }
5265 attrVal = trim(attrVal);
5266 attrs[attrName] = attrVal;
5268 String attrNameL = toLower(attrName);
5270 if (attrNameL == "name")
5271 name = attrVal;
5272 else if (attrNameL == "description")
5273 description = attrVal;
5274 else if (attrNameL == "cflags")
5275 cflags = attrVal;
5276 else if (attrNameL == "libs")
5277 libs = attrVal;
5278 else if (attrNameL == "requires")
5279 requires = attrVal;
5280 else if (attrNameL == "version")
5281 version = attrVal;
5283 //trace("name:'%s' value:'%s'",
5284 // attrName.c_str(), attrVal.c_str());
5285 }
5287 return true;
5288 }
5291 bool PkgConfig::parse(const String &buf)
5292 {
5293 init();
5295 String line;
5296 int lineNr = 0;
5297 for (unsigned int p=0 ; p<buf.size() ; p++)
5298 {
5299 int ch = buf[p];
5300 if (ch == '\n' || ch == '\r')
5301 {
5302 if (!parseLine(line))
5303 return false;
5304 line.clear();
5305 lineNr++;
5306 }
5307 else
5308 {
5309 line.push_back(ch);
5310 }
5311 }
5312 if (line.size()>0)
5313 {
5314 if (!parseLine(line))
5315 return false;
5316 }
5318 parseRequires();
5319 parseVersion();
5321 return true;
5322 }
5327 void PkgConfig::dumpAttrs()
5328 {
5329 //trace("### PkgConfig attributes for %s", fileName.c_str());
5330 std::map<String, String>::iterator iter;
5331 for (iter=attrs.begin() ; iter!=attrs.end() ; iter++)
5332 {
5333 trace(" %s = %s", iter->first.c_str(), iter->second.c_str());
5334 }
5335 }
5338 bool PkgConfig::readFile(const String &fname)
5339 {
5340 fileName = getNativePath(fname);
5342 FILE *f = fopen(fileName.c_str(), "r");
5343 if (!f)
5344 {
5345 error("cannot open file '%s' for reading", fileName.c_str());
5346 return false;
5347 }
5348 String buf;
5349 while (true)
5350 {
5351 int ch = fgetc(f);
5352 if (ch < 0)
5353 break;
5354 buf.push_back((char)ch);
5355 }
5356 fclose(f);
5358 //trace("####### File:\n%s", buf.c_str());
5359 if (!parse(buf))
5360 {
5361 return false;
5362 }
5364 //dumpAttrs();
5366 return true;
5367 }
5371 bool PkgConfig::query(const String &pkgName)
5372 {
5373 name = pkgName;
5375 String fname = path;
5376 fname.append("/");
5377 fname.append(name);
5378 fname.append(".pc");
5380 if (!readFile(fname))
5381 return false;
5383 return true;
5384 }
5390 //########################################################################
5391 //# D E P T O O L
5392 //########################################################################
5396 /**
5397 * Class which holds information for each file.
5398 */
5399 class FileRec
5400 {
5401 public:
5403 typedef enum
5404 {
5405 UNKNOWN,
5406 CFILE,
5407 HFILE,
5408 OFILE
5409 } FileType;
5411 /**
5412 * Constructor
5413 */
5414 FileRec()
5415 { init(); type = UNKNOWN; }
5417 /**
5418 * Copy constructor
5419 */
5420 FileRec(const FileRec &other)
5421 { init(); assign(other); }
5422 /**
5423 * Constructor
5424 */
5425 FileRec(int typeVal)
5426 { init(); type = typeVal; }
5427 /**
5428 * Assignment operator
5429 */
5430 FileRec &operator=(const FileRec &other)
5431 { init(); assign(other); return *this; }
5434 /**
5435 * Destructor
5436 */
5437 ~FileRec()
5438 {}
5440 /**
5441 * Directory part of the file name
5442 */
5443 String path;
5445 /**
5446 * Base name, sans directory and suffix
5447 */
5448 String baseName;
5450 /**
5451 * File extension, such as cpp or h
5452 */
5453 String suffix;
5455 /**
5456 * Type of file: CFILE, HFILE, OFILE
5457 */
5458 int type;
5460 /**
5461 * Used to list files ref'd by this one
5462 */
5463 std::map<String, FileRec *> files;
5466 private:
5468 void init()
5469 {
5470 }
5472 void assign(const FileRec &other)
5473 {
5474 type = other.type;
5475 baseName = other.baseName;
5476 suffix = other.suffix;
5477 files = other.files;
5478 }
5480 };
5484 /**
5485 * Simpler dependency record
5486 */
5487 class DepRec
5488 {
5489 public:
5491 /**
5492 * Constructor
5493 */
5494 DepRec()
5495 {init();}
5497 /**
5498 * Copy constructor
5499 */
5500 DepRec(const DepRec &other)
5501 {init(); assign(other);}
5502 /**
5503 * Constructor
5504 */
5505 DepRec(const String &fname)
5506 {init(); name = fname; }
5507 /**
5508 * Assignment operator
5509 */
5510 DepRec &operator=(const DepRec &other)
5511 {init(); assign(other); return *this;}
5514 /**
5515 * Destructor
5516 */
5517 ~DepRec()
5518 {}
5520 /**
5521 * Directory part of the file name
5522 */
5523 String path;
5525 /**
5526 * Base name, without the path and suffix
5527 */
5528 String name;
5530 /**
5531 * Suffix of the source
5532 */
5533 String suffix;
5536 /**
5537 * Used to list files ref'd by this one
5538 */
5539 std::vector<String> files;
5542 private:
5544 void init()
5545 {
5546 }
5548 void assign(const DepRec &other)
5549 {
5550 path = other.path;
5551 name = other.name;
5552 suffix = other.suffix;
5553 files = other.files; //avoid recursion
5554 }
5556 };
5559 class DepTool : public MakeBase
5560 {
5561 public:
5563 /**
5564 * Constructor
5565 */
5566 DepTool()
5567 { init(); }
5569 /**
5570 * Copy constructor
5571 */
5572 DepTool(const DepTool &other)
5573 { init(); assign(other); }
5575 /**
5576 * Assignment operator
5577 */
5578 DepTool &operator=(const DepTool &other)
5579 { init(); assign(other); return *this; }
5582 /**
5583 * Destructor
5584 */
5585 ~DepTool()
5586 {}
5589 /**
5590 * Reset this section of code
5591 */
5592 virtual void init();
5594 /**
5595 * Reset this section of code
5596 */
5597 virtual void assign(const DepTool &other)
5598 {
5599 }
5601 /**
5602 * Sets the source directory which will be scanned
5603 */
5604 virtual void setSourceDirectory(const String &val)
5605 { sourceDir = val; }
5607 /**
5608 * Returns the source directory which will be scanned
5609 */
5610 virtual String getSourceDirectory()
5611 { return sourceDir; }
5613 /**
5614 * Sets the list of files within the directory to analyze
5615 */
5616 virtual void setFileList(const std::vector<String> &list)
5617 { fileList = list; }
5619 /**
5620 * Creates the list of all file names which will be
5621 * candidates for further processing. Reads make.exclude
5622 * to see which files for directories to leave out.
5623 */
5624 virtual bool createFileList();
5627 /**
5628 * Generates the forward dependency list
5629 */
5630 virtual bool generateDependencies();
5633 /**
5634 * Generates the forward dependency list, saving the file
5635 */
5636 virtual bool generateDependencies(const String &);
5639 /**
5640 * Load a dependency file
5641 */
5642 std::vector<DepRec> loadDepFile(const String &fileName);
5644 /**
5645 * Load a dependency file, generating one if necessary
5646 */
5647 std::vector<DepRec> getDepFile(const String &fileName,
5648 bool forceRefresh);
5650 /**
5651 * Save a dependency file
5652 */
5653 bool saveDepFile(const String &fileName);
5656 private:
5659 /**
5660 *
5661 */
5662 void parseName(const String &fullname,
5663 String &path,
5664 String &basename,
5665 String &suffix);
5667 /**
5668 *
5669 */
5670 int get(int pos);
5672 /**
5673 *
5674 */
5675 int skipwhite(int pos);
5677 /**
5678 *
5679 */
5680 int getword(int pos, String &ret);
5682 /**
5683 *
5684 */
5685 bool sequ(int pos, const char *key);
5687 /**
5688 *
5689 */
5690 bool addIncludeFile(FileRec *frec, const String &fname);
5692 /**
5693 *
5694 */
5695 bool scanFile(const String &fname, FileRec *frec);
5697 /**
5698 *
5699 */
5700 bool processDependency(FileRec *ofile, FileRec *include);
5702 /**
5703 *
5704 */
5705 String sourceDir;
5707 /**
5708 *
5709 */
5710 std::vector<String> fileList;
5712 /**
5713 *
5714 */
5715 std::vector<String> directories;
5717 /**
5718 * A list of all files which will be processed for
5719 * dependencies.
5720 */
5721 std::map<String, FileRec *> allFiles;
5723 /**
5724 * The list of .o files, and the
5725 * dependencies upon them.
5726 */
5727 std::map<String, FileRec *> oFiles;
5729 int depFileSize;
5730 char *depFileBuf;
5732 static const int readBufSize = 8192;
5733 char readBuf[8193];//byte larger
5735 };
5741 /**
5742 * Clean up after processing. Called by the destructor, but should
5743 * also be called before the object is reused.
5744 */
5745 void DepTool::init()
5746 {
5747 sourceDir = ".";
5749 fileList.clear();
5750 directories.clear();
5752 //clear output file list
5753 std::map<String, FileRec *>::iterator iter;
5754 for (iter=oFiles.begin(); iter!=oFiles.end() ; iter++)
5755 delete iter->second;
5756 oFiles.clear();
5758 //allFiles actually contains the master copies. delete them
5759 for (iter= allFiles.begin(); iter!=allFiles.end() ; iter++)
5760 delete iter->second;
5761 allFiles.clear();
5763 }
5768 /**
5769 * Parse a full path name into path, base name, and suffix
5770 */
5771 void DepTool::parseName(const String &fullname,
5772 String &path,
5773 String &basename,
5774 String &suffix)
5775 {
5776 if (fullname.size() < 2)
5777 return;
5779 unsigned int pos = fullname.find_last_of('/');
5780 if (pos != fullname.npos && pos<fullname.size()-1)
5781 {
5782 path = fullname.substr(0, pos);
5783 pos++;
5784 basename = fullname.substr(pos, fullname.size()-pos);
5785 }
5786 else
5787 {
5788 path = "";
5789 basename = fullname;
5790 }
5792 pos = basename.find_last_of('.');
5793 if (pos != basename.npos && pos<basename.size()-1)
5794 {
5795 suffix = basename.substr(pos+1, basename.size()-pos-1);
5796 basename = basename.substr(0, pos);
5797 }
5799 //trace("parsename:%s %s %s", path.c_str(),
5800 // basename.c_str(), suffix.c_str());
5801 }
5805 /**
5806 * Generate our internal file list.
5807 */
5808 bool DepTool::createFileList()
5809 {
5811 for (unsigned int i=0 ; i<fileList.size() ; i++)
5812 {
5813 String fileName = fileList[i];
5814 //trace("## FileName:%s", fileName.c_str());
5815 String path;
5816 String basename;
5817 String sfx;
5818 parseName(fileName, path, basename, sfx);
5819 if (sfx == "cpp" || sfx == "c" || sfx == "cxx" ||
5820 sfx == "cc" || sfx == "CC")
5821 {
5822 FileRec *fe = new FileRec(FileRec::CFILE);
5823 fe->path = path;
5824 fe->baseName = basename;
5825 fe->suffix = sfx;
5826 allFiles[fileName] = fe;
5827 }
5828 else if (sfx == "h" || sfx == "hh" ||
5829 sfx == "hpp" || sfx == "hxx")
5830 {
5831 FileRec *fe = new FileRec(FileRec::HFILE);
5832 fe->path = path;
5833 fe->baseName = basename;
5834 fe->suffix = sfx;
5835 allFiles[fileName] = fe;
5836 }
5837 }
5839 if (!listDirectories(sourceDir, "", directories))
5840 return false;
5842 return true;
5843 }
5849 /**
5850 * Get a character from the buffer at pos. If out of range,
5851 * return -1 for safety
5852 */
5853 int DepTool::get(int pos)
5854 {
5855 if (pos>depFileSize)
5856 return -1;
5857 return depFileBuf[pos];
5858 }
5862 /**
5863 * Skip over all whitespace characters beginning at pos. Return
5864 * the position of the first non-whitespace character.
5865 */
5866 int DepTool::skipwhite(int pos)
5867 {
5868 while (pos < depFileSize)
5869 {
5870 int ch = get(pos);
5871 if (ch < 0)
5872 break;
5873 if (!isspace(ch))
5874 break;
5875 pos++;
5876 }
5877 return pos;
5878 }
5881 /**
5882 * Parse the buffer beginning at pos, for a word. Fill
5883 * 'ret' with the result. Return the position after the
5884 * word.
5885 */
5886 int DepTool::getword(int pos, String &ret)
5887 {
5888 while (pos < depFileSize)
5889 {
5890 int ch = get(pos);
5891 if (ch < 0)
5892 break;
5893 if (isspace(ch))
5894 break;
5895 ret.push_back((char)ch);
5896 pos++;
5897 }
5898 return pos;
5899 }
5901 /**
5902 * Return whether the sequence of characters in the buffer
5903 * beginning at pos match the key, for the length of the key
5904 */
5905 bool DepTool::sequ(int pos, const char *key)
5906 {
5907 while (*key)
5908 {
5909 if (*key != get(pos))
5910 return false;
5911 key++; pos++;
5912 }
5913 return true;
5914 }
5918 /**
5919 * Add an include file name to a file record. If the name
5920 * is not found in allFiles explicitly, try prepending include
5921 * directory names to it and try again.
5922 */
5923 bool DepTool::addIncludeFile(FileRec *frec, const String &iname)
5924 {
5925 //# if the name is an exact match to a path name
5926 //# in allFiles, like "myinc.h"
5927 std::map<String, FileRec *>::iterator iter =
5928 allFiles.find(iname);
5929 if (iter != allFiles.end()) //already exists
5930 {
5931 //h file in same dir
5932 FileRec *other = iter->second;
5933 //trace("local: '%s'", iname.c_str());
5934 frec->files[iname] = other;
5935 return true;
5936 }
5937 else
5938 {
5939 //## Ok, it was not found directly
5940 //look in other dirs
5941 std::vector<String>::iterator diter;
5942 for (diter=directories.begin() ;
5943 diter!=directories.end() ; diter++)
5944 {
5945 String dfname = *diter;
5946 dfname.append("/");
5947 dfname.append(iname);
5948 URI fullPathURI(dfname); //normalize path name
5949 String fullPath = fullPathURI.getPath();
5950 if (fullPath[0] == '/')
5951 fullPath = fullPath.substr(1);
5952 //trace("Normalized %s to %s", dfname.c_str(), fullPath.c_str());
5953 iter = allFiles.find(fullPath);
5954 if (iter != allFiles.end())
5955 {
5956 FileRec *other = iter->second;
5957 //trace("other: '%s'", iname.c_str());
5958 frec->files[fullPath] = other;
5959 return true;
5960 }
5961 }
5962 }
5963 return true;
5964 }
5968 /**
5969 * Lightly parse a file to find the #include directives. Do
5970 * a bit of state machine stuff to make sure that the directive
5971 * is valid. (Like not in a comment).
5972 */
5973 bool DepTool::scanFile(const String &fname, FileRec *frec)
5974 {
5975 String fileName;
5976 if (sourceDir.size() > 0)
5977 {
5978 fileName.append(sourceDir);
5979 fileName.append("/");
5980 }
5981 fileName.append(fname);
5982 String nativeName = getNativePath(fileName);
5983 FILE *f = fopen(nativeName.c_str(), "r");
5984 if (!f)
5985 {
5986 error("Could not open '%s' for reading", fname.c_str());
5987 return false;
5988 }
5989 String buf;
5990 while (!feof(f))
5991 {
5992 int nrbytes = fread(readBuf, 1, readBufSize, f);
5993 readBuf[nrbytes] = '\0';
5994 buf.append(readBuf);
5995 }
5996 fclose(f);
5998 depFileSize = buf.size();
5999 depFileBuf = (char *)buf.c_str();
6000 int pos = 0;
6003 while (pos < depFileSize)
6004 {
6005 //trace("p:%c", get(pos));
6007 //# Block comment
6008 if (get(pos) == '/' && get(pos+1) == '*')
6009 {
6010 pos += 2;
6011 while (pos < depFileSize)
6012 {
6013 if (get(pos) == '*' && get(pos+1) == '/')
6014 {
6015 pos += 2;
6016 break;
6017 }
6018 else
6019 pos++;
6020 }
6021 }
6022 //# Line comment
6023 else if (get(pos) == '/' && get(pos+1) == '/')
6024 {
6025 pos += 2;
6026 while (pos < depFileSize)
6027 {
6028 if (get(pos) == '\n')
6029 {
6030 pos++;
6031 break;
6032 }
6033 else
6034 pos++;
6035 }
6036 }
6037 //# #include! yaay
6038 else if (sequ(pos, "#include"))
6039 {
6040 pos += 8;
6041 pos = skipwhite(pos);
6042 String iname;
6043 pos = getword(pos, iname);
6044 if (iname.size()>2)
6045 {
6046 iname = iname.substr(1, iname.size()-2);
6047 addIncludeFile(frec, iname);
6048 }
6049 }
6050 else
6051 {
6052 pos++;
6053 }
6054 }
6056 return true;
6057 }
6061 /**
6062 * Recursively check include lists to find all files in allFiles to which
6063 * a given file is dependent.
6064 */
6065 bool DepTool::processDependency(FileRec *ofile, FileRec *include)
6066 {
6067 std::map<String, FileRec *>::iterator iter;
6068 for (iter=include->files.begin() ; iter!=include->files.end() ; iter++)
6069 {
6070 String fname = iter->first;
6071 if (ofile->files.find(fname) != ofile->files.end())
6072 {
6073 //trace("file '%s' already seen", fname.c_str());
6074 continue;
6075 }
6076 FileRec *child = iter->second;
6077 ofile->files[fname] = child;
6079 processDependency(ofile, child);
6080 }
6083 return true;
6084 }
6090 /**
6091 * Generate the file dependency list.
6092 */
6093 bool DepTool::generateDependencies()
6094 {
6095 std::map<String, FileRec *>::iterator iter;
6096 //# First pass. Scan for all includes
6097 for (iter=allFiles.begin() ; iter!=allFiles.end() ; iter++)
6098 {
6099 FileRec *frec = iter->second;
6100 if (!scanFile(iter->first, frec))
6101 {
6102 //quit?
6103 }
6104 }
6106 //# Second pass. Scan for all includes
6107 for (iter=allFiles.begin() ; iter!=allFiles.end() ; iter++)
6108 {
6109 FileRec *include = iter->second;
6110 if (include->type == FileRec::CFILE)
6111 {
6112 //String cFileName = iter->first;
6113 FileRec *ofile = new FileRec(FileRec::OFILE);
6114 ofile->path = include->path;
6115 ofile->baseName = include->baseName;
6116 ofile->suffix = include->suffix;
6117 String fname = include->path;
6118 if (fname.size()>0)
6119 fname.append("/");
6120 fname.append(include->baseName);
6121 fname.append(".o");
6122 oFiles[fname] = ofile;
6123 //add the .c file first? no, don't
6124 //ofile->files[cFileName] = include;
6126 //trace("ofile:%s", fname.c_str());
6128 processDependency(ofile, include);
6129 }
6130 }
6133 return true;
6134 }
6138 /**
6139 * High-level call to generate deps and optionally save them
6140 */
6141 bool DepTool::generateDependencies(const String &fileName)
6142 {
6143 if (!createFileList())
6144 return false;
6145 if (!generateDependencies())
6146 return false;
6147 if (!saveDepFile(fileName))
6148 return false;
6149 return true;
6150 }
6153 /**
6154 * This saves the dependency cache.
6155 */
6156 bool DepTool::saveDepFile(const String &fileName)
6157 {
6158 time_t tim;
6159 time(&tim);
6161 FILE *f = fopen(fileName.c_str(), "w");
6162 if (!f)
6163 {
6164 trace("cannot open '%s' for writing", fileName.c_str());
6165 }
6166 fprintf(f, "<?xml version='1.0'?>\n");
6167 fprintf(f, "<!--\n");
6168 fprintf(f, "########################################################\n");
6169 fprintf(f, "## File: build.dep\n");
6170 fprintf(f, "## Generated by BuildTool at :%s", ctime(&tim));
6171 fprintf(f, "########################################################\n");
6172 fprintf(f, "-->\n");
6174 fprintf(f, "<dependencies source='%s'>\n\n", sourceDir.c_str());
6175 std::map<String, FileRec *>::iterator iter;
6176 for (iter=oFiles.begin() ; iter!=oFiles.end() ; iter++)
6177 {
6178 FileRec *frec = iter->second;
6179 if (frec->type == FileRec::OFILE)
6180 {
6181 fprintf(f, "<object path='%s' name='%s' suffix='%s'>\n",
6182 frec->path.c_str(), frec->baseName.c_str(), frec->suffix.c_str());
6183 std::map<String, FileRec *>::iterator citer;
6184 for (citer=frec->files.begin() ; citer!=frec->files.end() ; citer++)
6185 {
6186 String cfname = citer->first;
6187 fprintf(f, " <dep name='%s'/>\n", cfname.c_str());
6188 }
6189 fprintf(f, "</object>\n\n");
6190 }
6191 }
6193 fprintf(f, "</dependencies>\n");
6194 fprintf(f, "\n");
6195 fprintf(f, "<!--\n");
6196 fprintf(f, "########################################################\n");
6197 fprintf(f, "## E N D\n");
6198 fprintf(f, "########################################################\n");
6199 fprintf(f, "-->\n");
6201 fclose(f);
6203 return true;
6204 }
6209 /**
6210 * This loads the dependency cache.
6211 */
6212 std::vector<DepRec> DepTool::loadDepFile(const String &depFile)
6213 {
6214 std::vector<DepRec> result;
6216 Parser parser;
6217 Element *root = parser.parseFile(depFile.c_str());
6218 if (!root)
6219 {
6220 //error("Could not open %s for reading", depFile.c_str());
6221 return result;
6222 }
6224 if (root->getChildren().size()==0 ||
6225 root->getChildren()[0]->getName()!="dependencies")
6226 {
6227 error("loadDepFile: main xml element should be <dependencies>");
6228 delete root;
6229 return result;
6230 }
6232 //########## Start parsing
6233 Element *depList = root->getChildren()[0];
6235 std::vector<Element *> objects = depList->getChildren();
6236 for (unsigned int i=0 ; i<objects.size() ; i++)
6237 {
6238 Element *objectElem = objects[i];
6239 String tagName = objectElem->getName();
6240 if (tagName != "object")
6241 {
6242 error("loadDepFile: <dependencies> should have only <object> children");
6243 return result;
6244 }
6246 String objName = objectElem->getAttribute("name");
6247 //trace("object:%s", objName.c_str());
6248 DepRec depObject(objName);
6249 depObject.path = objectElem->getAttribute("path");
6250 depObject.suffix = objectElem->getAttribute("suffix");
6251 //########## DESCRIPTION
6252 std::vector<Element *> depElems = objectElem->getChildren();
6253 for (unsigned int i=0 ; i<depElems.size() ; i++)
6254 {
6255 Element *depElem = depElems[i];
6256 tagName = depElem->getName();
6257 if (tagName != "dep")
6258 {
6259 error("loadDepFile: <object> should have only <dep> children");
6260 return result;
6261 }
6262 String depName = depElem->getAttribute("name");
6263 //trace(" dep:%s", depName.c_str());
6264 depObject.files.push_back(depName);
6265 }
6267 //Insert into the result list, in a sorted manner
6268 bool inserted = false;
6269 std::vector<DepRec>::iterator iter;
6270 for (iter = result.begin() ; iter != result.end() ; iter++)
6271 {
6272 String vpath = iter->path;
6273 vpath.append("/");
6274 vpath.append(iter->name);
6275 String opath = depObject.path;
6276 opath.append("/");
6277 opath.append(depObject.name);
6278 if (vpath > opath)
6279 {
6280 inserted = true;
6281 iter = result.insert(iter, depObject);
6282 break;
6283 }
6284 }
6285 if (!inserted)
6286 result.push_back(depObject);
6287 }
6289 delete root;
6291 return result;
6292 }
6295 /**
6296 * This loads the dependency cache.
6297 */
6298 std::vector<DepRec> DepTool::getDepFile(const String &depFile,
6299 bool forceRefresh)
6300 {
6301 std::vector<DepRec> result;
6302 if (forceRefresh)
6303 {
6304 generateDependencies(depFile);
6305 result = loadDepFile(depFile);
6306 }
6307 else
6308 {
6309 //try once
6310 result = loadDepFile(depFile);
6311 if (result.size() == 0)
6312 {
6313 //fail? try again
6314 generateDependencies(depFile);
6315 result = loadDepFile(depFile);
6316 }
6317 }
6318 return result;
6319 }
6324 //########################################################################
6325 //# T A S K
6326 //########################################################################
6327 //forward decl
6328 class Target;
6329 class Make;
6331 /**
6332 *
6333 */
6334 class Task : public MakeBase
6335 {
6337 public:
6339 typedef enum
6340 {
6341 TASK_NONE,
6342 TASK_CC,
6343 TASK_COPY,
6344 TASK_DELETE,
6345 TASK_ECHO,
6346 TASK_JAR,
6347 TASK_JAVAC,
6348 TASK_LINK,
6349 TASK_MAKEFILE,
6350 TASK_MKDIR,
6351 TASK_MSGFMT,
6352 TASK_PKG_CONFIG,
6353 TASK_RANLIB,
6354 TASK_RC,
6355 TASK_SHAREDLIB,
6356 TASK_STATICLIB,
6357 TASK_STRIP,
6358 TASK_TOUCH,
6359 TASK_TSTAMP
6360 } TaskType;
6363 /**
6364 *
6365 */
6366 Task(MakeBase &par) : parent(par)
6367 { init(); }
6369 /**
6370 *
6371 */
6372 Task(const Task &other) : parent(other.parent)
6373 { init(); assign(other); }
6375 /**
6376 *
6377 */
6378 Task &operator=(const Task &other)
6379 { assign(other); return *this; }
6381 /**
6382 *
6383 */
6384 virtual ~Task()
6385 { }
6388 /**
6389 *
6390 */
6391 virtual MakeBase &getParent()
6392 { return parent; }
6394 /**
6395 *
6396 */
6397 virtual int getType()
6398 { return type; }
6400 /**
6401 *
6402 */
6403 virtual void setType(int val)
6404 { type = val; }
6406 /**
6407 *
6408 */
6409 virtual String getName()
6410 { return name; }
6412 /**
6413 *
6414 */
6415 virtual bool execute()
6416 { return true; }
6418 /**
6419 *
6420 */
6421 virtual bool parse(Element *elem)
6422 { return true; }
6424 /**
6425 *
6426 */
6427 Task *createTask(Element *elem, int lineNr);
6430 protected:
6432 void init()
6433 {
6434 type = TASK_NONE;
6435 name = "none";
6436 }
6438 void assign(const Task &other)
6439 {
6440 type = other.type;
6441 name = other.name;
6442 }
6444 /**
6445 * Show task status
6446 */
6447 void taskstatus(const char *fmt, ...)
6448 {
6449 va_list args;
6450 va_start(args,fmt);
6451 fprintf(stdout, " %s : ", name.c_str());
6452 vfprintf(stdout, fmt, args);
6453 fprintf(stdout, "\n");
6454 va_end(args) ;
6455 }
6457 String getAttribute(Element *elem, const String &attrName)
6458 {
6459 String str;
6460 return str;
6461 }
6463 MakeBase &parent;
6465 int type;
6467 String name;
6468 };
6472 /**
6473 * This task runs the C/C++ compiler. The compiler is invoked
6474 * for all .c or .cpp files which are newer than their correcsponding
6475 * .o files.
6476 */
6477 class TaskCC : public Task
6478 {
6479 public:
6481 TaskCC(MakeBase &par) : Task(par)
6482 {
6483 type = TASK_CC;
6484 name = "cc";
6485 }
6487 virtual ~TaskCC()
6488 {}
6490 virtual bool isExcludedInc(const String &dirname)
6491 {
6492 for (unsigned int i=0 ; i<excludeInc.size() ; i++)
6493 {
6494 String fname = excludeInc[i];
6495 if (fname == dirname)
6496 return true;
6497 }
6498 return false;
6499 }
6501 virtual bool execute()
6502 {
6503 //evaluate our parameters
6504 String command = parent.eval(commandOpt, "gcc");
6505 String ccCommand = parent.eval(ccCommandOpt, "gcc");
6506 String cxxCommand = parent.eval(cxxCommandOpt, "g++");
6507 String source = parent.eval(sourceOpt, ".");
6508 String dest = parent.eval(destOpt, ".");
6509 String flags = parent.eval(flagsOpt, "");
6510 String defines = parent.eval(definesOpt, "");
6511 String includes = parent.eval(includesOpt, "");
6512 bool continueOnError = parent.evalBool(continueOnErrorOpt, true);
6513 bool refreshCache = parent.evalBool(refreshCacheOpt, false);
6515 if (!listFiles(parent, fileSet))
6516 return false;
6518 FILE *f = NULL;
6519 f = fopen("compile.lst", "w");
6521 //refreshCache is probably false here, unless specified otherwise
6522 String fullName = parent.resolve("build.dep");
6523 if (refreshCache || isNewerThan(parent.getURI().getPath(), fullName))
6524 {
6525 taskstatus("regenerating C/C++ dependency cache");
6526 refreshCache = true;
6527 }
6529 DepTool depTool;
6530 depTool.setSourceDirectory(source);
6531 depTool.setFileList(fileSet.getFiles());
6532 std::vector<DepRec> deps =
6533 depTool.getDepFile("build.dep", refreshCache);
6535 String incs;
6536 incs.append("-I");
6537 incs.append(parent.resolve("."));
6538 incs.append(" ");
6539 if (includes.size()>0)
6540 {
6541 incs.append(includes);
6542 incs.append(" ");
6543 }
6544 std::set<String> paths;
6545 std::vector<DepRec>::iterator viter;
6546 for (viter=deps.begin() ; viter!=deps.end() ; viter++)
6547 {
6548 DepRec dep = *viter;
6549 if (dep.path.size()>0)
6550 paths.insert(dep.path);
6551 }
6552 if (source.size()>0)
6553 {
6554 incs.append(" -I");
6555 incs.append(parent.resolve(source));
6556 incs.append(" ");
6557 }
6558 std::set<String>::iterator setIter;
6559 for (setIter=paths.begin() ; setIter!=paths.end() ; setIter++)
6560 {
6561 String dirName = *setIter;
6562 //check excludeInc to see if we dont want to include this dir
6563 if (isExcludedInc(dirName))
6564 continue;
6565 incs.append(" -I");
6566 String dname;
6567 if (source.size()>0)
6568 {
6569 dname.append(source);
6570 dname.append("/");
6571 }
6572 dname.append(dirName);
6573 incs.append(parent.resolve(dname));
6574 }
6576 /**
6577 * Compile each of the C files that need it
6578 */
6579 bool errorOccurred = false;
6580 std::vector<String> cfiles;
6581 for (viter=deps.begin() ; viter!=deps.end() ; viter++)
6582 {
6583 DepRec dep = *viter;
6585 //## Select command
6586 String sfx = dep.suffix;
6587 String command = ccCommand;
6588 if (sfx == "cpp" || sfx == "cxx" || sfx == "c++" ||
6589 sfx == "cc" || sfx == "CC")
6590 command = cxxCommand;
6592 //## Make paths
6593 String destPath = dest;
6594 String srcPath = source;
6595 if (dep.path.size()>0)
6596 {
6597 destPath.append("/");
6598 destPath.append(dep.path);
6599 srcPath.append("/");
6600 srcPath.append(dep.path);
6601 }
6602 //## Make sure destination directory exists
6603 if (!createDirectory(destPath))
6604 return false;
6606 //## Check whether it needs to be done
6607 String destName;
6608 if (destPath.size()>0)
6609 {
6610 destName.append(destPath);
6611 destName.append("/");
6612 }
6613 destName.append(dep.name);
6614 destName.append(".o");
6615 String destFullName = parent.resolve(destName);
6616 String srcName;
6617 if (srcPath.size()>0)
6618 {
6619 srcName.append(srcPath);
6620 srcName.append("/");
6621 }
6622 srcName.append(dep.name);
6623 srcName.append(".");
6624 srcName.append(dep.suffix);
6625 String srcFullName = parent.resolve(srcName);
6626 bool compileMe = false;
6627 //# First we check if the source is newer than the .o
6628 if (isNewerThan(srcFullName, destFullName))
6629 {
6630 taskstatus("compile of %s required by source: %s",
6631 destFullName.c_str(), srcFullName.c_str());
6632 compileMe = true;
6633 }
6634 else
6635 {
6636 //# secondly, we check if any of the included dependencies
6637 //# of the .c/.cpp is newer than the .o
6638 for (unsigned int i=0 ; i<dep.files.size() ; i++)
6639 {
6640 String depName;
6641 if (source.size()>0)
6642 {
6643 depName.append(source);
6644 depName.append("/");
6645 }
6646 depName.append(dep.files[i]);
6647 String depFullName = parent.resolve(depName);
6648 bool depRequires = isNewerThan(depFullName, destFullName);
6649 //trace("%d %s %s\n", depRequires,
6650 // destFullName.c_str(), depFullName.c_str());
6651 if (depRequires)
6652 {
6653 taskstatus("compile of %s required by included: %s",
6654 destFullName.c_str(), depFullName.c_str());
6655 compileMe = true;
6656 break;
6657 }
6658 }
6659 }
6660 if (!compileMe)
6661 {
6662 continue;
6663 }
6665 //## Assemble the command
6666 String cmd = command;
6667 cmd.append(" -c ");
6668 cmd.append(flags);
6669 cmd.append(" ");
6670 cmd.append(defines);
6671 cmd.append(" ");
6672 cmd.append(incs);
6673 cmd.append(" ");
6674 cmd.append(srcFullName);
6675 cmd.append(" -o ");
6676 cmd.append(destFullName);
6678 //## Execute the command
6680 String outString, errString;
6681 bool ret = executeCommand(cmd.c_str(), "", outString, errString);
6683 if (f)
6684 {
6685 fprintf(f, "########################### File : %s\n",
6686 srcFullName.c_str());
6687 fprintf(f, "#### COMMAND ###\n");
6688 int col = 0;
6689 for (unsigned int i = 0 ; i < cmd.size() ; i++)
6690 {
6691 char ch = cmd[i];
6692 if (isspace(ch) && col > 63)
6693 {
6694 fputc('\n', f);
6695 col = 0;
6696 }
6697 else
6698 {
6699 fputc(ch, f);
6700 col++;
6701 }
6702 if (col > 76)
6703 {
6704 fputc('\n', f);
6705 col = 0;
6706 }
6707 }
6708 fprintf(f, "\n");
6709 fprintf(f, "#### STDOUT ###\n%s\n", outString.c_str());
6710 fprintf(f, "#### STDERR ###\n%s\n\n", errString.c_str());
6711 fflush(f);
6712 }
6713 if (!ret)
6714 {
6715 error("problem compiling: %s", errString.c_str());
6716 errorOccurred = true;
6717 }
6718 if (errorOccurred && !continueOnError)
6719 break;
6720 }
6722 if (f)
6723 {
6724 fclose(f);
6725 }
6727 return !errorOccurred;
6728 }
6731 virtual bool parse(Element *elem)
6732 {
6733 String s;
6734 if (!parent.getAttribute(elem, "command", commandOpt))
6735 return false;
6736 if (commandOpt.size()>0)
6737 { cxxCommandOpt = ccCommandOpt = commandOpt; }
6738 if (!parent.getAttribute(elem, "cc", ccCommandOpt))
6739 return false;
6740 if (!parent.getAttribute(elem, "cxx", cxxCommandOpt))
6741 return false;
6742 if (!parent.getAttribute(elem, "destdir", destOpt))
6743 return false;
6744 if (!parent.getAttribute(elem, "continueOnError", continueOnErrorOpt))
6745 return false;
6746 if (!parent.getAttribute(elem, "refreshCache", refreshCacheOpt))
6747 return false;
6749 std::vector<Element *> children = elem->getChildren();
6750 for (unsigned int i=0 ; i<children.size() ; i++)
6751 {
6752 Element *child = children[i];
6753 String tagName = child->getName();
6754 if (tagName == "flags")
6755 {
6756 if (!parent.getValue(child, flagsOpt))
6757 return false;
6758 flagsOpt = strip(flagsOpt);
6759 }
6760 else if (tagName == "includes")
6761 {
6762 if (!parent.getValue(child, includesOpt))
6763 return false;
6764 includesOpt = strip(includesOpt);
6765 }
6766 else if (tagName == "defines")
6767 {
6768 if (!parent.getValue(child, definesOpt))
6769 return false;
6770 definesOpt = strip(definesOpt);
6771 }
6772 else if (tagName == "fileset")
6773 {
6774 if (!parseFileSet(child, parent, fileSet))
6775 return false;
6776 sourceOpt = fileSet.getDirectory();
6777 }
6778 else if (tagName == "excludeinc")
6779 {
6780 if (!parseFileList(child, parent, excludeInc))
6781 return false;
6782 }
6783 }
6785 return true;
6786 }
6788 protected:
6790 String commandOpt;
6791 String ccCommandOpt;
6792 String cxxCommandOpt;
6793 String sourceOpt;
6794 String destOpt;
6795 String flagsOpt;
6796 String definesOpt;
6797 String includesOpt;
6798 String continueOnErrorOpt;
6799 String refreshCacheOpt;
6800 FileSet fileSet;
6801 FileList excludeInc;
6803 };
6807 /**
6808 *
6809 */
6810 class TaskCopy : public Task
6811 {
6812 public:
6814 typedef enum
6815 {
6816 CP_NONE,
6817 CP_TOFILE,
6818 CP_TODIR
6819 } CopyType;
6821 TaskCopy(MakeBase &par) : Task(par)
6822 {
6823 type = TASK_COPY;
6824 name = "copy";
6825 cptype = CP_NONE;
6826 haveFileSet = false;
6827 }
6829 virtual ~TaskCopy()
6830 {}
6832 virtual bool execute()
6833 {
6834 String fileName = parent.eval(fileNameOpt , ".");
6835 String toFileName = parent.eval(toFileNameOpt , ".");
6836 String toDirName = parent.eval(toDirNameOpt , ".");
6837 bool verbose = parent.evalBool(verboseOpt, false);
6838 switch (cptype)
6839 {
6840 case CP_TOFILE:
6841 {
6842 if (fileName.size()>0)
6843 {
6844 taskstatus("%s to %s",
6845 fileName.c_str(), toFileName.c_str());
6846 String fullSource = parent.resolve(fileName);
6847 String fullDest = parent.resolve(toFileName);
6848 if (verbose)
6849 taskstatus("copy %s to file %s", fullSource.c_str(),
6850 fullDest.c_str());
6851 if (!isRegularFile(fullSource))
6852 {
6853 error("copy : file %s does not exist", fullSource.c_str());
6854 return false;
6855 }
6856 if (!isNewerThan(fullSource, fullDest))
6857 {
6858 taskstatus("skipped");
6859 return true;
6860 }
6861 if (!copyFile(fullSource, fullDest))
6862 return false;
6863 taskstatus("1 file copied");
6864 }
6865 return true;
6866 }
6867 case CP_TODIR:
6868 {
6869 if (haveFileSet)
6870 {
6871 if (!listFiles(parent, fileSet))
6872 return false;
6873 String fileSetDir = parent.eval(fileSet.getDirectory(), ".");
6875 taskstatus("%s to %s",
6876 fileSetDir.c_str(), toDirName.c_str());
6878 int nrFiles = 0;
6879 for (unsigned int i=0 ; i<fileSet.size() ; i++)
6880 {
6881 String fileName = fileSet[i];
6883 String sourcePath;
6884 if (fileSetDir.size()>0)
6885 {
6886 sourcePath.append(fileSetDir);
6887 sourcePath.append("/");
6888 }
6889 sourcePath.append(fileName);
6890 String fullSource = parent.resolve(sourcePath);
6892 //Get the immediate parent directory's base name
6893 String baseFileSetDir = fileSetDir;
6894 unsigned int pos = baseFileSetDir.find_last_of('/');
6895 if (pos!=baseFileSetDir.npos &&
6896 pos < baseFileSetDir.size()-1)
6897 baseFileSetDir =
6898 baseFileSetDir.substr(pos+1,
6899 baseFileSetDir.size());
6900 //Now make the new path
6901 String destPath;
6902 if (toDirName.size()>0)
6903 {
6904 destPath.append(toDirName);
6905 destPath.append("/");
6906 }
6907 if (baseFileSetDir.size()>0)
6908 {
6909 destPath.append(baseFileSetDir);
6910 destPath.append("/");
6911 }
6912 destPath.append(fileName);
6913 String fullDest = parent.resolve(destPath);
6914 //trace("fileName:%s", fileName.c_str());
6915 if (verbose)
6916 taskstatus("copy %s to new dir : %s",
6917 fullSource.c_str(), fullDest.c_str());
6918 if (!isNewerThan(fullSource, fullDest))
6919 {
6920 if (verbose)
6921 taskstatus("copy skipping %s", fullSource.c_str());
6922 continue;
6923 }
6924 if (!copyFile(fullSource, fullDest))
6925 return false;
6926 nrFiles++;
6927 }
6928 taskstatus("%d file(s) copied", nrFiles);
6929 }
6930 else //file source
6931 {
6932 //For file->dir we want only the basename of
6933 //the source appended to the dest dir
6934 taskstatus("%s to %s",
6935 fileName.c_str(), toDirName.c_str());
6936 String baseName = fileName;
6937 unsigned int pos = baseName.find_last_of('/');
6938 if (pos!=baseName.npos && pos<baseName.size()-1)
6939 baseName = baseName.substr(pos+1, baseName.size());
6940 String fullSource = parent.resolve(fileName);
6941 String destPath;
6942 if (toDirName.size()>0)
6943 {
6944 destPath.append(toDirName);
6945 destPath.append("/");
6946 }
6947 destPath.append(baseName);
6948 String fullDest = parent.resolve(destPath);
6949 if (verbose)
6950 taskstatus("file %s to new dir : %s", fullSource.c_str(),
6951 fullDest.c_str());
6952 if (!isRegularFile(fullSource))
6953 {
6954 error("copy : file %s does not exist", fullSource.c_str());
6955 return false;
6956 }
6957 if (!isNewerThan(fullSource, fullDest))
6958 {
6959 taskstatus("skipped");
6960 return true;
6961 }
6962 if (!copyFile(fullSource, fullDest))
6963 return false;
6964 taskstatus("1 file copied");
6965 }
6966 return true;
6967 }
6968 }
6969 return true;
6970 }
6973 virtual bool parse(Element *elem)
6974 {
6975 if (!parent.getAttribute(elem, "file", fileNameOpt))
6976 return false;
6977 if (!parent.getAttribute(elem, "tofile", toFileNameOpt))
6978 return false;
6979 if (toFileNameOpt.size() > 0)
6980 cptype = CP_TOFILE;
6981 if (!parent.getAttribute(elem, "todir", toDirNameOpt))
6982 return false;
6983 if (toDirNameOpt.size() > 0)
6984 cptype = CP_TODIR;
6985 if (!parent.getAttribute(elem, "verbose", verboseOpt))
6986 return false;
6988 haveFileSet = false;
6990 std::vector<Element *> children = elem->getChildren();
6991 for (unsigned int i=0 ; i<children.size() ; i++)
6992 {
6993 Element *child = children[i];
6994 String tagName = child->getName();
6995 if (tagName == "fileset")
6996 {
6997 if (!parseFileSet(child, parent, fileSet))
6998 {
6999 error("problem getting fileset");
7000 return false;
7001 }
7002 haveFileSet = true;
7003 }
7004 }
7006 //Perform validity checks
7007 if (fileNameOpt.size()>0 && fileSet.size()>0)
7008 {
7009 error("<copy> can only have one of : file= and <fileset>");
7010 return false;
7011 }
7012 if (toFileNameOpt.size()>0 && toDirNameOpt.size()>0)
7013 {
7014 error("<copy> can only have one of : tofile= or todir=");
7015 return false;
7016 }
7017 if (haveFileSet && toDirNameOpt.size()==0)
7018 {
7019 error("a <copy> task with a <fileset> must have : todir=");
7020 return false;
7021 }
7022 if (cptype == CP_TOFILE && fileNameOpt.size()==0)
7023 {
7024 error("<copy> tofile= must be associated with : file=");
7025 return false;
7026 }
7027 if (cptype == CP_TODIR && fileNameOpt.size()==0 && !haveFileSet)
7028 {
7029 error("<copy> todir= must be associated with : file= or <fileset>");
7030 return false;
7031 }
7033 return true;
7034 }
7036 private:
7038 int cptype;
7039 bool haveFileSet;
7041 FileSet fileSet;
7042 String fileNameOpt;
7043 String toFileNameOpt;
7044 String toDirNameOpt;
7045 String verboseOpt;
7046 };
7049 /**
7050 *
7051 */
7052 class TaskDelete : public Task
7053 {
7054 public:
7056 typedef enum
7057 {
7058 DEL_FILE,
7059 DEL_DIR,
7060 DEL_FILESET
7061 } DeleteType;
7063 TaskDelete(MakeBase &par) : Task(par)
7064 {
7065 type = TASK_DELETE;
7066 name = "delete";
7067 delType = DEL_FILE;
7068 }
7070 virtual ~TaskDelete()
7071 {}
7073 virtual bool execute()
7074 {
7075 String dirName = parent.eval(dirNameOpt, ".");
7076 String fileName = parent.eval(fileNameOpt, ".");
7077 bool verbose = parent.evalBool(verboseOpt, false);
7078 bool quiet = parent.evalBool(quietOpt, false);
7079 bool failOnError = parent.evalBool(failOnErrorOpt, true);
7080 struct stat finfo;
7081 switch (delType)
7082 {
7083 case DEL_FILE:
7084 {
7085 taskstatus("file: %s", fileName.c_str());
7086 String fullName = parent.resolve(fileName);
7087 char *fname = (char *)fullName.c_str();
7088 if (!quiet && verbose)
7089 taskstatus("path: %s", fname);
7090 //does not exist
7091 if (stat(fname, &finfo)<0)
7092 {
7093 if (failOnError)
7094 return false;
7095 else
7096 return true;
7097 }
7098 //exists but is not a regular file
7099 if (!S_ISREG(finfo.st_mode))
7100 {
7101 error("<delete> failed. '%s' exists and is not a regular file",
7102 fname);
7103 return false;
7104 }
7105 if (remove(fname)<0)
7106 {
7107 error("<delete> failed: %s", strerror(errno));
7108 return false;
7109 }
7110 return true;
7111 }
7112 case DEL_DIR:
7113 {
7114 taskstatus("dir: %s", dirName.c_str());
7115 String fullDir = parent.resolve(dirName);
7116 if (!quiet && verbose)
7117 taskstatus("path: %s", fullDir.c_str());
7118 if (!removeDirectory(fullDir))
7119 return false;
7120 return true;
7121 }
7122 }
7123 return true;
7124 }
7126 virtual bool parse(Element *elem)
7127 {
7128 if (!parent.getAttribute(elem, "file", fileNameOpt))
7129 return false;
7130 if (fileNameOpt.size() > 0)
7131 delType = DEL_FILE;
7132 if (!parent.getAttribute(elem, "dir", dirNameOpt))
7133 return false;
7134 if (dirNameOpt.size() > 0)
7135 delType = DEL_DIR;
7136 if (fileNameOpt.size()>0 && dirNameOpt.size()>0)
7137 {
7138 error("<delete> can have one attribute of file= or dir=");
7139 return false;
7140 }
7141 if (fileNameOpt.size()==0 && dirNameOpt.size()==0)
7142 {
7143 error("<delete> must have one attribute of file= or dir=");
7144 return false;
7145 }
7146 if (!parent.getAttribute(elem, "verbose", verboseOpt))
7147 return false;
7148 if (!parent.getAttribute(elem, "quiet", quietOpt))
7149 return false;
7150 if (!parent.getAttribute(elem, "failonerror", failOnErrorOpt))
7151 return false;
7152 return true;
7153 }
7155 private:
7157 int delType;
7158 String dirNameOpt;
7159 String fileNameOpt;
7160 String verboseOpt;
7161 String quietOpt;
7162 String failOnErrorOpt;
7163 };
7166 /**
7167 * Send a message to stdout
7168 */
7169 class TaskEcho : public Task
7170 {
7171 public:
7173 TaskEcho(MakeBase &par) : Task(par)
7174 { type = TASK_ECHO; name = "echo"; }
7176 virtual ~TaskEcho()
7177 {}
7179 virtual bool execute()
7180 {
7181 //let message have priority over text
7182 String message = parent.eval(messageOpt, "");
7183 String text = parent.eval(textOpt, "");
7184 if (message.size() > 0)
7185 {
7186 fprintf(stdout, "%s\n", message.c_str());
7187 }
7188 else if (text.size() > 0)
7189 {
7190 fprintf(stdout, "%s\n", text.c_str());
7191 }
7192 return true;
7193 }
7195 virtual bool parse(Element *elem)
7196 {
7197 if (!parent.getValue(elem, textOpt))
7198 return false;
7199 textOpt = leftJustify(textOpt);
7200 if (!parent.getAttribute(elem, "message", messageOpt))
7201 return false;
7202 return true;
7203 }
7205 private:
7207 String messageOpt;
7208 String textOpt;
7209 };
7213 /**
7214 *
7215 */
7216 class TaskJar : public Task
7217 {
7218 public:
7220 TaskJar(MakeBase &par) : Task(par)
7221 { type = TASK_JAR; name = "jar"; }
7223 virtual ~TaskJar()
7224 {}
7226 virtual bool execute()
7227 {
7228 String command = parent.eval(commandOpt, "jar");
7229 String basedir = parent.eval(basedirOpt, ".");
7230 String destfile = parent.eval(destfileOpt, ".");
7232 String cmd = command;
7233 cmd.append(" -cf ");
7234 cmd.append(destfile);
7235 cmd.append(" -C ");
7236 cmd.append(basedir);
7237 cmd.append(" .");
7239 String execCmd = cmd;
7241 String outString, errString;
7242 bool ret = executeCommand(execCmd.c_str(), "", outString, errString);
7243 if (!ret)
7244 {
7245 error("<jar> command '%s' failed :\n %s",
7246 execCmd.c_str(), errString.c_str());
7247 return false;
7248 }
7249 return true;
7250 }
7252 virtual bool parse(Element *elem)
7253 {
7254 if (!parent.getAttribute(elem, "command", commandOpt))
7255 return false;
7256 if (!parent.getAttribute(elem, "basedir", basedirOpt))
7257 return false;
7258 if (!parent.getAttribute(elem, "destfile", destfileOpt))
7259 return false;
7260 if (basedirOpt.size() == 0 || destfileOpt.size() == 0)
7261 {
7262 error("<jar> required both basedir and destfile attributes to be set");
7263 return false;
7264 }
7265 return true;
7266 }
7268 private:
7270 String commandOpt;
7271 String basedirOpt;
7272 String destfileOpt;
7273 };
7276 /**
7277 *
7278 */
7279 class TaskJavac : public Task
7280 {
7281 public:
7283 TaskJavac(MakeBase &par) : Task(par)
7284 {
7285 type = TASK_JAVAC; name = "javac";
7286 }
7288 virtual ~TaskJavac()
7289 {}
7291 virtual bool execute()
7292 {
7293 String command = parent.eval(commandOpt, "javac");
7294 String srcdir = parent.eval(srcdirOpt, ".");
7295 String destdir = parent.eval(destdirOpt, ".");
7296 String target = parent.eval(targetOpt, "");
7298 std::vector<String> fileList;
7299 if (!listFiles(srcdir, "", fileList))
7300 {
7301 return false;
7302 }
7303 String cmd = command;
7304 cmd.append(" -d ");
7305 cmd.append(destdir);
7306 cmd.append(" -classpath ");
7307 cmd.append(destdir);
7308 cmd.append(" -sourcepath ");
7309 cmd.append(srcdir);
7310 cmd.append(" ");
7311 if (target.size()>0)
7312 {
7313 cmd.append(" -target ");
7314 cmd.append(target);
7315 cmd.append(" ");
7316 }
7317 String fname = "javalist.btool";
7318 FILE *f = fopen(fname.c_str(), "w");
7319 int count = 0;
7320 for (unsigned int i=0 ; i<fileList.size() ; i++)
7321 {
7322 String fname = fileList[i];
7323 String srcName = fname;
7324 if (fname.size()<6) //x.java
7325 continue;
7326 if (fname.compare(fname.size()-5, 5, ".java") != 0)
7327 continue;
7328 String baseName = fname.substr(0, fname.size()-5);
7329 String destName = baseName;
7330 destName.append(".class");
7332 String fullSrc = srcdir;
7333 fullSrc.append("/");
7334 fullSrc.append(fname);
7335 String fullDest = destdir;
7336 fullDest.append("/");
7337 fullDest.append(destName);
7338 //trace("fullsrc:%s fulldest:%s", fullSrc.c_str(), fullDest.c_str());
7339 if (!isNewerThan(fullSrc, fullDest))
7340 continue;
7342 count++;
7343 fprintf(f, "%s\n", fullSrc.c_str());
7344 }
7345 fclose(f);
7346 if (!count)
7347 {
7348 taskstatus("nothing to do");
7349 return true;
7350 }
7352 taskstatus("compiling %d files", count);
7354 String execCmd = cmd;
7355 execCmd.append("@");
7356 execCmd.append(fname);
7358 String outString, errString;
7359 bool ret = executeCommand(execCmd.c_str(), "", outString, errString);
7360 if (!ret)
7361 {
7362 error("<javac> command '%s' failed :\n %s",
7363 execCmd.c_str(), errString.c_str());
7364 return false;
7365 }
7366 return true;
7367 }
7369 virtual bool parse(Element *elem)
7370 {
7371 if (!parent.getAttribute(elem, "command", commandOpt))
7372 return false;
7373 if (!parent.getAttribute(elem, "srcdir", srcdirOpt))
7374 return false;
7375 if (!parent.getAttribute(elem, "destdir", destdirOpt))
7376 return false;
7377 if (srcdirOpt.size() == 0 || destdirOpt.size() == 0)
7378 {
7379 error("<javac> required both srcdir and destdir attributes to be set");
7380 return false;
7381 }
7382 if (!parent.getAttribute(elem, "target", targetOpt))
7383 return false;
7384 return true;
7385 }
7387 private:
7389 String commandOpt;
7390 String srcdirOpt;
7391 String destdirOpt;
7392 String targetOpt;
7394 };
7397 /**
7398 *
7399 */
7400 class TaskLink : public Task
7401 {
7402 public:
7404 TaskLink(MakeBase &par) : Task(par)
7405 {
7406 type = TASK_LINK; name = "link";
7407 }
7409 virtual ~TaskLink()
7410 {}
7412 virtual bool execute()
7413 {
7414 String command = parent.eval(commandOpt, "g++");
7415 String fileName = parent.eval(fileNameOpt, "");
7416 String flags = parent.eval(flagsOpt, "");
7417 String libs = parent.eval(libsOpt, "");
7418 bool doStrip = parent.evalBool(doStripOpt, false);
7419 String symFileName = parent.eval(symFileNameOpt, "");
7420 String stripCommand = parent.eval(stripCommandOpt, "strip");
7421 String objcopyCommand = parent.eval(objcopyCommandOpt, "objcopy");
7423 if (!listFiles(parent, fileSet))
7424 return false;
7425 String fileSetDir = parent.eval(fileSet.getDirectory(), ".");
7426 //trace("%d files in %s", fileSet.size(), fileSetDir.c_str());
7427 bool doit = false;
7428 String fullTarget = parent.resolve(fileName);
7429 String cmd = command;
7430 cmd.append(" -o ");
7431 cmd.append(fullTarget);
7432 cmd.append(" ");
7433 cmd.append(flags);
7434 for (unsigned int i=0 ; i<fileSet.size() ; i++)
7435 {
7436 cmd.append(" ");
7437 String obj;
7438 if (fileSetDir.size()>0)
7439 {
7440 obj.append(fileSetDir);
7441 obj.append("/");
7442 }
7443 obj.append(fileSet[i]);
7444 String fullObj = parent.resolve(obj);
7445 String nativeFullObj = getNativePath(fullObj);
7446 cmd.append(nativeFullObj);
7447 //trace("link: tgt:%s obj:%s", fullTarget.c_str(),
7448 // fullObj.c_str());
7449 if (isNewerThan(fullObj, fullTarget))
7450 doit = true;
7451 }
7452 cmd.append(" ");
7453 cmd.append(libs);
7454 if (!doit)
7455 {
7456 //trace("link not needed");
7457 return true;
7458 }
7459 //trace("LINK cmd:%s", cmd.c_str());
7462 String outbuf, errbuf;
7463 if (!executeCommand(cmd.c_str(), "", outbuf, errbuf))
7464 {
7465 error("LINK problem: %s", errbuf.c_str());
7466 return false;
7467 }
7469 if (symFileName.size()>0)
7470 {
7471 String symFullName = parent.resolve(symFileName);
7472 cmd = objcopyCommand;
7473 cmd.append(" --only-keep-debug ");
7474 cmd.append(getNativePath(fullTarget));
7475 cmd.append(" ");
7476 cmd.append(getNativePath(symFullName));
7477 if (!executeCommand(cmd, "", outbuf, errbuf))
7478 {
7479 error("<strip> symbol file failed : %s", errbuf.c_str());
7480 return false;
7481 }
7482 }
7484 if (doStrip)
7485 {
7486 cmd = stripCommand;
7487 cmd.append(" ");
7488 cmd.append(getNativePath(fullTarget));
7489 if (!executeCommand(cmd, "", outbuf, errbuf))
7490 {
7491 error("<strip> failed : %s", errbuf.c_str());
7492 return false;
7493 }
7494 }
7496 return true;
7497 }
7499 virtual bool parse(Element *elem)
7500 {
7501 if (!parent.getAttribute(elem, "command", commandOpt))
7502 return false;
7503 if (!parent.getAttribute(elem, "objcopycommand", objcopyCommandOpt))
7504 return false;
7505 if (!parent.getAttribute(elem, "stripcommand", stripCommandOpt))
7506 return false;
7507 if (!parent.getAttribute(elem, "out", fileNameOpt))
7508 return false;
7509 if (!parent.getAttribute(elem, "strip", doStripOpt))
7510 return false;
7511 if (!parent.getAttribute(elem, "symfile", symFileNameOpt))
7512 return false;
7514 std::vector<Element *> children = elem->getChildren();
7515 for (unsigned int i=0 ; i<children.size() ; i++)
7516 {
7517 Element *child = children[i];
7518 String tagName = child->getName();
7519 if (tagName == "fileset")
7520 {
7521 if (!parseFileSet(child, parent, fileSet))
7522 return false;
7523 }
7524 else if (tagName == "flags")
7525 {
7526 if (!parent.getValue(child, flagsOpt))
7527 return false;
7528 flagsOpt = strip(flagsOpt);
7529 }
7530 else if (tagName == "libs")
7531 {
7532 if (!parent.getValue(child, libsOpt))
7533 return false;
7534 libsOpt = strip(libsOpt);
7535 }
7536 }
7537 return true;
7538 }
7540 private:
7542 FileSet fileSet;
7544 String commandOpt;
7545 String fileNameOpt;
7546 String flagsOpt;
7547 String libsOpt;
7548 String doStripOpt;
7549 String symFileNameOpt;
7550 String stripCommandOpt;
7551 String objcopyCommandOpt;
7553 };
7557 /**
7558 * Create a named file
7559 */
7560 class TaskMakeFile : public Task
7561 {
7562 public:
7564 TaskMakeFile(MakeBase &par) : Task(par)
7565 { type = TASK_MAKEFILE; name = "makefile"; }
7567 virtual ~TaskMakeFile()
7568 {}
7570 virtual bool execute()
7571 {
7572 String fileName = parent.eval(fileNameOpt, "");
7573 String text = parent.eval(textOpt, "");
7575 taskstatus("%s", fileName.c_str());
7576 String fullName = parent.resolve(fileName);
7577 if (!isNewerThan(parent.getURI().getPath(), fullName))
7578 {
7579 //trace("skipped <makefile>");
7580 return true;
7581 }
7582 String fullNative = getNativePath(fullName);
7583 //trace("fullName:%s", fullName.c_str());
7584 FILE *f = fopen(fullNative.c_str(), "w");
7585 if (!f)
7586 {
7587 error("<makefile> could not open %s for writing : %s",
7588 fullName.c_str(), strerror(errno));
7589 return false;
7590 }
7591 for (unsigned int i=0 ; i<text.size() ; i++)
7592 fputc(text[i], f);
7593 fputc('\n', f);
7594 fclose(f);
7595 return true;
7596 }
7598 virtual bool parse(Element *elem)
7599 {
7600 if (!parent.getAttribute(elem, "file", fileNameOpt))
7601 return false;
7602 if (fileNameOpt.size() == 0)
7603 {
7604 error("<makefile> requires 'file=\"filename\"' attribute");
7605 return false;
7606 }
7607 if (!parent.getValue(elem, textOpt))
7608 return false;
7609 textOpt = leftJustify(textOpt);
7610 //trace("dirname:%s", dirName.c_str());
7611 return true;
7612 }
7614 private:
7616 String fileNameOpt;
7617 String textOpt;
7618 };
7622 /**
7623 * Create a named directory
7624 */
7625 class TaskMkDir : public Task
7626 {
7627 public:
7629 TaskMkDir(MakeBase &par) : Task(par)
7630 { type = TASK_MKDIR; name = "mkdir"; }
7632 virtual ~TaskMkDir()
7633 {}
7635 virtual bool execute()
7636 {
7637 String dirName = parent.eval(dirNameOpt, ".");
7639 taskstatus("%s", dirName.c_str());
7640 String fullDir = parent.resolve(dirName);
7641 //trace("fullDir:%s", fullDir.c_str());
7642 if (!createDirectory(fullDir))
7643 return false;
7644 return true;
7645 }
7647 virtual bool parse(Element *elem)
7648 {
7649 if (!parent.getAttribute(elem, "dir", dirNameOpt))
7650 return false;
7651 if (dirNameOpt.size() == 0)
7652 {
7653 error("<mkdir> requires 'dir=\"dirname\"' attribute");
7654 return false;
7655 }
7656 return true;
7657 }
7659 private:
7661 String dirNameOpt;
7662 };
7666 /**
7667 * Create a named directory
7668 */
7669 class TaskMsgFmt: public Task
7670 {
7671 public:
7673 TaskMsgFmt(MakeBase &par) : Task(par)
7674 { type = TASK_MSGFMT; name = "msgfmt"; }
7676 virtual ~TaskMsgFmt()
7677 {}
7679 virtual bool execute()
7680 {
7681 String command = parent.eval(commandOpt, "msgfmt");
7682 String toDirName = parent.eval(toDirNameOpt, ".");
7683 String outName = parent.eval(outNameOpt, "");
7684 bool owndir = parent.evalBool(owndirOpt, false);
7686 if (!listFiles(parent, fileSet))
7687 return false;
7688 String fileSetDir = fileSet.getDirectory();
7690 //trace("msgfmt: %d", fileSet.size());
7691 for (unsigned int i=0 ; i<fileSet.size() ; i++)
7692 {
7693 String fileName = fileSet[i];
7694 if (getSuffix(fileName) != "po")
7695 continue;
7696 String sourcePath;
7697 if (fileSetDir.size()>0)
7698 {
7699 sourcePath.append(fileSetDir);
7700 sourcePath.append("/");
7701 }
7702 sourcePath.append(fileName);
7703 String fullSource = parent.resolve(sourcePath);
7705 String destPath;
7706 if (toDirName.size()>0)
7707 {
7708 destPath.append(toDirName);
7709 destPath.append("/");
7710 }
7711 if (owndir)
7712 {
7713 String subdir = fileName;
7714 unsigned int pos = subdir.find_last_of('.');
7715 if (pos != subdir.npos)
7716 subdir = subdir.substr(0, pos);
7717 destPath.append(subdir);
7718 destPath.append("/");
7719 }
7720 //Pick the output file name
7721 if (outName.size() > 0)
7722 {
7723 destPath.append(outName);
7724 }
7725 else
7726 {
7727 destPath.append(fileName);
7728 destPath[destPath.size()-2] = 'm';
7729 }
7731 String fullDest = parent.resolve(destPath);
7733 if (!isNewerThan(fullSource, fullDest))
7734 {
7735 //trace("skip %s", fullSource.c_str());
7736 continue;
7737 }
7739 String cmd = command;
7740 cmd.append(" ");
7741 cmd.append(fullSource);
7742 cmd.append(" -o ");
7743 cmd.append(fullDest);
7745 int pos = fullDest.find_last_of('/');
7746 if (pos>0)
7747 {
7748 String fullDestPath = fullDest.substr(0, pos);
7749 if (!createDirectory(fullDestPath))
7750 return false;
7751 }
7755 String outString, errString;
7756 if (!executeCommand(cmd.c_str(), "", outString, errString))
7757 {
7758 error("<msgfmt> problem: %s", errString.c_str());
7759 return false;
7760 }
7761 }
7763 return true;
7764 }
7766 virtual bool parse(Element *elem)
7767 {
7768 if (!parent.getAttribute(elem, "command", commandOpt))
7769 return false;
7770 if (!parent.getAttribute(elem, "todir", toDirNameOpt))
7771 return false;
7772 if (!parent.getAttribute(elem, "out", outNameOpt))
7773 return false;
7774 if (!parent.getAttribute(elem, "owndir", owndirOpt))
7775 return false;
7777 std::vector<Element *> children = elem->getChildren();
7778 for (unsigned int i=0 ; i<children.size() ; i++)
7779 {
7780 Element *child = children[i];
7781 String tagName = child->getName();
7782 if (tagName == "fileset")
7783 {
7784 if (!parseFileSet(child, parent, fileSet))
7785 return false;
7786 }
7787 }
7788 return true;
7789 }
7791 private:
7793 FileSet fileSet;
7795 String commandOpt;
7796 String toDirNameOpt;
7797 String outNameOpt;
7798 String owndirOpt;
7800 };
7804 /**
7805 * Perform a Package-Config query similar to pkg-config
7806 */
7807 class TaskPkgConfig : public Task
7808 {
7809 public:
7811 typedef enum
7812 {
7813 PKG_CONFIG_QUERY_CFLAGS,
7814 PKG_CONFIG_QUERY_LIBS,
7815 PKG_CONFIG_QUERY_ALL
7816 } QueryTypes;
7818 TaskPkgConfig(MakeBase &par) : Task(par)
7819 {
7820 type = TASK_PKG_CONFIG;
7821 name = "pkg-config";
7822 }
7824 virtual ~TaskPkgConfig()
7825 {}
7827 virtual bool execute()
7828 {
7829 String pkgName = parent.eval(pkgNameOpt, "");
7830 String prefix = parent.eval(prefixOpt, "");
7831 String propName = parent.eval(propNameOpt, "");
7832 String pkgConfigPath = parent.eval(pkgConfigPathOpt,"");
7833 String query = parent.eval(queryOpt, "all");
7835 String path = parent.resolve(pkgConfigPath);
7836 PkgConfig pkgconfig;
7837 pkgconfig.setPath(path);
7838 pkgconfig.setPrefix(prefix);
7839 if (!pkgconfig.query(pkgName))
7840 {
7841 error("<pkg-config> query failed for '%s", name.c_str());
7842 return false;
7843 }
7845 String val = "";
7846 if (query == "cflags")
7847 val = pkgconfig.getCflags();
7848 else if (query == "libs")
7849 val =pkgconfig.getLibs();
7850 else if (query == "all")
7851 val = pkgconfig.getAll();
7852 else
7853 {
7854 error("<pkg-config> unhandled query : %s", query.c_str());
7855 return false;
7856 }
7857 taskstatus("property %s = '%s'", propName.c_str(), val.c_str());
7858 parent.setProperty(propName, val);
7859 return true;
7860 }
7862 virtual bool parse(Element *elem)
7863 {
7864 //# NAME
7865 if (!parent.getAttribute(elem, "name", pkgNameOpt))
7866 return false;
7867 if (pkgNameOpt.size()==0)
7868 {
7869 error("<pkg-config> requires 'name=\"package\"' attribute");
7870 return false;
7871 }
7873 //# PROPERTY
7874 if (!parent.getAttribute(elem, "property", propNameOpt))
7875 return false;
7876 if (propNameOpt.size()==0)
7877 {
7878 error("<pkg-config> requires 'property=\"name\"' attribute");
7879 return false;
7880 }
7881 //# PATH
7882 if (!parent.getAttribute(elem, "path", pkgConfigPathOpt))
7883 return false;
7884 //# PREFIX
7885 if (!parent.getAttribute(elem, "prefix", prefixOpt))
7886 return false;
7887 //# QUERY
7888 if (!parent.getAttribute(elem, "query", queryOpt))
7889 return false;
7891 return true;
7892 }
7894 private:
7896 String queryOpt;
7897 String pkgNameOpt;
7898 String prefixOpt;
7899 String propNameOpt;
7900 String pkgConfigPathOpt;
7902 };
7909 /**
7910 * Process an archive to allow random access
7911 */
7912 class TaskRanlib : public Task
7913 {
7914 public:
7916 TaskRanlib(MakeBase &par) : Task(par)
7917 { type = TASK_RANLIB; name = "ranlib"; }
7919 virtual ~TaskRanlib()
7920 {}
7922 virtual bool execute()
7923 {
7924 String fileName = parent.eval(fileNameOpt, "");
7925 String command = parent.eval(commandOpt, "ranlib");
7927 String fullName = parent.resolve(fileName);
7928 //trace("fullDir:%s", fullDir.c_str());
7929 String cmd = command;
7930 cmd.append(" ");
7931 cmd.append(fullName);
7932 String outbuf, errbuf;
7933 if (!executeCommand(cmd, "", outbuf, errbuf))
7934 return false;
7935 return true;
7936 }
7938 virtual bool parse(Element *elem)
7939 {
7940 if (!parent.getAttribute(elem, "command", commandOpt))
7941 return false;
7942 if (!parent.getAttribute(elem, "file", fileNameOpt))
7943 return false;
7944 if (fileNameOpt.size() == 0)
7945 {
7946 error("<ranlib> requires 'file=\"fileNname\"' attribute");
7947 return false;
7948 }
7949 return true;
7950 }
7952 private:
7954 String fileNameOpt;
7955 String commandOpt;
7956 };
7960 /**
7961 * Compile a resource file into a binary object
7962 */
7963 class TaskRC : public Task
7964 {
7965 public:
7967 TaskRC(MakeBase &par) : Task(par)
7968 { type = TASK_RC; name = "rc"; }
7970 virtual ~TaskRC()
7971 {}
7973 virtual bool execute()
7974 {
7975 String command = parent.eval(commandOpt, "windres");
7976 String flags = parent.eval(flagsOpt, "");
7977 String fileName = parent.eval(fileNameOpt, "");
7978 String outName = parent.eval(outNameOpt, "");
7980 String fullFile = parent.resolve(fileName);
7981 String fullOut = parent.resolve(outName);
7982 if (!isNewerThan(fullFile, fullOut))
7983 return true;
7984 String cmd = command;
7985 cmd.append(" -o ");
7986 cmd.append(fullOut);
7987 cmd.append(" ");
7988 cmd.append(flags);
7989 cmd.append(" ");
7990 cmd.append(fullFile);
7992 String outString, errString;
7993 if (!executeCommand(cmd.c_str(), "", outString, errString))
7994 {
7995 error("RC problem: %s", errString.c_str());
7996 return false;
7997 }
7998 return true;
7999 }
8001 virtual bool parse(Element *elem)
8002 {
8003 if (!parent.getAttribute(elem, "command", commandOpt))
8004 return false;
8005 if (!parent.getAttribute(elem, "file", fileNameOpt))
8006 return false;
8007 if (!parent.getAttribute(elem, "out", outNameOpt))
8008 return false;
8009 std::vector<Element *> children = elem->getChildren();
8010 for (unsigned int i=0 ; i<children.size() ; i++)
8011 {
8012 Element *child = children[i];
8013 String tagName = child->getName();
8014 if (tagName == "flags")
8015 {
8016 if (!parent.getValue(child, flagsOpt))
8017 return false;
8018 }
8019 }
8020 return true;
8021 }
8023 private:
8025 String commandOpt;
8026 String flagsOpt;
8027 String fileNameOpt;
8028 String outNameOpt;
8030 };
8034 /**
8035 * Collect .o's into a .so or DLL
8036 */
8037 class TaskSharedLib : public Task
8038 {
8039 public:
8041 TaskSharedLib(MakeBase &par) : Task(par)
8042 { type = TASK_SHAREDLIB; name = "dll"; }
8044 virtual ~TaskSharedLib()
8045 {}
8047 virtual bool execute()
8048 {
8049 String command = parent.eval(commandOpt, "dllwrap");
8050 String fileName = parent.eval(fileNameOpt, "");
8051 String defFileName = parent.eval(defFileNameOpt, "");
8052 String impFileName = parent.eval(impFileNameOpt, "");
8053 String libs = parent.eval(libsOpt, "");
8055 //trace("###########HERE %d", fileSet.size());
8056 bool doit = false;
8058 String fullOut = parent.resolve(fileName);
8059 //trace("ar fullout: %s", fullOut.c_str());
8061 if (!listFiles(parent, fileSet))
8062 return false;
8063 String fileSetDir = parent.eval(fileSet.getDirectory(), ".");
8065 for (unsigned int i=0 ; i<fileSet.size() ; i++)
8066 {
8067 String fname;
8068 if (fileSetDir.size()>0)
8069 {
8070 fname.append(fileSetDir);
8071 fname.append("/");
8072 }
8073 fname.append(fileSet[i]);
8074 String fullName = parent.resolve(fname);
8075 //trace("ar : %s/%s", fullOut.c_str(), fullName.c_str());
8076 if (isNewerThan(fullName, fullOut))
8077 doit = true;
8078 }
8079 //trace("Needs it:%d", doit);
8080 if (!doit)
8081 {
8082 return true;
8083 }
8085 String cmd = "dllwrap";
8086 cmd.append(" -o ");
8087 cmd.append(fullOut);
8088 if (defFileName.size()>0)
8089 {
8090 cmd.append(" --def ");
8091 cmd.append(defFileName);
8092 cmd.append(" ");
8093 }
8094 if (impFileName.size()>0)
8095 {
8096 cmd.append(" --implib ");
8097 cmd.append(impFileName);
8098 cmd.append(" ");
8099 }
8100 for (unsigned int i=0 ; i<fileSet.size() ; i++)
8101 {
8102 String fname;
8103 if (fileSetDir.size()>0)
8104 {
8105 fname.append(fileSetDir);
8106 fname.append("/");
8107 }
8108 fname.append(fileSet[i]);
8109 String fullName = parent.resolve(fname);
8111 cmd.append(" ");
8112 cmd.append(fullName);
8113 }
8114 cmd.append(" ");
8115 cmd.append(libs);
8117 String outString, errString;
8118 if (!executeCommand(cmd.c_str(), "", outString, errString))
8119 {
8120 error("<sharedlib> problem: %s", errString.c_str());
8121 return false;
8122 }
8124 return true;
8125 }
8127 virtual bool parse(Element *elem)
8128 {
8129 if (!parent.getAttribute(elem, "command", commandOpt))
8130 return false;
8131 if (!parent.getAttribute(elem, "file", fileNameOpt))
8132 return false;
8133 if (!parent.getAttribute(elem, "import", impFileNameOpt))
8134 return false;
8135 if (!parent.getAttribute(elem, "def", defFileNameOpt))
8136 return false;
8138 std::vector<Element *> children = elem->getChildren();
8139 for (unsigned int i=0 ; i<children.size() ; i++)
8140 {
8141 Element *child = children[i];
8142 String tagName = child->getName();
8143 if (tagName == "fileset")
8144 {
8145 if (!parseFileSet(child, parent, fileSet))
8146 return false;
8147 }
8148 else if (tagName == "libs")
8149 {
8150 if (!parent.getValue(child, libsOpt))
8151 return false;
8152 libsOpt = strip(libsOpt);
8153 }
8154 }
8155 return true;
8156 }
8158 private:
8160 FileSet fileSet;
8162 String commandOpt;
8163 String fileNameOpt;
8164 String defFileNameOpt;
8165 String impFileNameOpt;
8166 String libsOpt;
8168 };
8172 /**
8173 * Run the "ar" command to archive .o's into a .a
8174 */
8175 class TaskStaticLib : public Task
8176 {
8177 public:
8179 TaskStaticLib(MakeBase &par) : Task(par)
8180 { type = TASK_STATICLIB; name = "staticlib"; }
8182 virtual ~TaskStaticLib()
8183 {}
8185 virtual bool execute()
8186 {
8187 String command = parent.eval(commandOpt, "ar crv");
8188 String fileName = parent.eval(fileNameOpt, "");
8190 bool doit = false;
8192 String fullOut = parent.resolve(fileName);
8193 //trace("ar fullout: %s", fullOut.c_str());
8195 if (!listFiles(parent, fileSet))
8196 return false;
8197 String fileSetDir = parent.eval(fileSet.getDirectory(), ".");
8198 //trace("###########HERE %s", fileSetDir.c_str());
8200 for (unsigned int i=0 ; i<fileSet.size() ; i++)
8201 {
8202 String fname;
8203 if (fileSetDir.size()>0)
8204 {
8205 fname.append(fileSetDir);
8206 fname.append("/");
8207 }
8208 fname.append(fileSet[i]);
8209 String fullName = parent.resolve(fname);
8210 //trace("ar : %s/%s", fullOut.c_str(), fullName.c_str());
8211 if (isNewerThan(fullName, fullOut))
8212 doit = true;
8213 }
8214 //trace("Needs it:%d", doit);
8215 if (!doit)
8216 {
8217 return true;
8218 }
8220 String cmd = command;
8221 cmd.append(" ");
8222 cmd.append(fullOut);
8223 for (unsigned int i=0 ; i<fileSet.size() ; i++)
8224 {
8225 String fname;
8226 if (fileSetDir.size()>0)
8227 {
8228 fname.append(fileSetDir);
8229 fname.append("/");
8230 }
8231 fname.append(fileSet[i]);
8232 String fullName = parent.resolve(fname);
8234 cmd.append(" ");
8235 cmd.append(fullName);
8236 }
8238 String outString, errString;
8239 if (!executeCommand(cmd.c_str(), "", outString, errString))
8240 {
8241 error("<staticlib> problem: %s", errString.c_str());
8242 return false;
8243 }
8245 return true;
8246 }
8249 virtual bool parse(Element *elem)
8250 {
8251 if (!parent.getAttribute(elem, "command", commandOpt))
8252 return false;
8253 if (!parent.getAttribute(elem, "file", fileNameOpt))
8254 return false;
8256 std::vector<Element *> children = elem->getChildren();
8257 for (unsigned int i=0 ; i<children.size() ; i++)
8258 {
8259 Element *child = children[i];
8260 String tagName = child->getName();
8261 if (tagName == "fileset")
8262 {
8263 if (!parseFileSet(child, parent, fileSet))
8264 return false;
8265 }
8266 }
8267 return true;
8268 }
8270 private:
8272 FileSet fileSet;
8274 String commandOpt;
8275 String fileNameOpt;
8277 };
8282 /**
8283 * Strip an executable
8284 */
8285 class TaskStrip : public Task
8286 {
8287 public:
8289 TaskStrip(MakeBase &par) : Task(par)
8290 { type = TASK_STRIP; name = "strip"; }
8292 virtual ~TaskStrip()
8293 {}
8295 virtual bool execute()
8296 {
8297 String command = parent.eval(commandOpt, "strip");
8298 String fileName = parent.eval(fileNameOpt, "");
8299 String symFileName = parent.eval(symFileNameOpt, "");
8301 String fullName = parent.resolve(fileName);
8302 //trace("fullDir:%s", fullDir.c_str());
8303 String cmd;
8304 String outbuf, errbuf;
8306 if (symFileName.size()>0)
8307 {
8308 String symFullName = parent.resolve(symFileName);
8309 cmd = "objcopy --only-keep-debug ";
8310 cmd.append(getNativePath(fullName));
8311 cmd.append(" ");
8312 cmd.append(getNativePath(symFullName));
8313 if (!executeCommand(cmd, "", outbuf, errbuf))
8314 {
8315 error("<strip> symbol file failed : %s", errbuf.c_str());
8316 return false;
8317 }
8318 }
8320 cmd = command;
8321 cmd.append(getNativePath(fullName));
8322 if (!executeCommand(cmd, "", outbuf, errbuf))
8323 {
8324 error("<strip> failed : %s", errbuf.c_str());
8325 return false;
8326 }
8327 return true;
8328 }
8330 virtual bool parse(Element *elem)
8331 {
8332 if (!parent.getAttribute(elem, "command", commandOpt))
8333 return false;
8334 if (!parent.getAttribute(elem, "file", fileNameOpt))
8335 return false;
8336 if (!parent.getAttribute(elem, "symfile", symFileNameOpt))
8337 return false;
8338 if (fileNameOpt.size() == 0)
8339 {
8340 error("<strip> requires 'file=\"fileName\"' attribute");
8341 return false;
8342 }
8343 return true;
8344 }
8346 private:
8348 String commandOpt;
8349 String fileNameOpt;
8350 String symFileNameOpt;
8351 };
8354 /**
8355 *
8356 */
8357 class TaskTouch : public Task
8358 {
8359 public:
8361 TaskTouch(MakeBase &par) : Task(par)
8362 { type = TASK_TOUCH; name = "touch"; }
8364 virtual ~TaskTouch()
8365 {}
8367 virtual bool execute()
8368 {
8369 String fileName = parent.eval(fileNameOpt, "");
8371 String fullName = parent.resolve(fileName);
8372 String nativeFile = getNativePath(fullName);
8373 if (!isRegularFile(fullName) && !isDirectory(fullName))
8374 {
8375 // S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH
8376 int ret = creat(nativeFile.c_str(), 0666);
8377 if (ret != 0)
8378 {
8379 error("<touch> could not create '%s' : %s",
8380 nativeFile.c_str(), strerror(ret));
8381 return false;
8382 }
8383 return true;
8384 }
8385 int ret = utime(nativeFile.c_str(), (struct utimbuf *)0);
8386 if (ret != 0)
8387 {
8388 error("<touch> could not update the modification time for '%s' : %s",
8389 nativeFile.c_str(), strerror(ret));
8390 return false;
8391 }
8392 return true;
8393 }
8395 virtual bool parse(Element *elem)
8396 {
8397 //trace("touch parse");
8398 if (!parent.getAttribute(elem, "file", fileNameOpt))
8399 return false;
8400 if (fileNameOpt.size() == 0)
8401 {
8402 error("<touch> requires 'file=\"fileName\"' attribute");
8403 return false;
8404 }
8405 return true;
8406 }
8408 String fileNameOpt;
8409 };
8412 /**
8413 *
8414 */
8415 class TaskTstamp : public Task
8416 {
8417 public:
8419 TaskTstamp(MakeBase &par) : Task(par)
8420 { type = TASK_TSTAMP; name = "tstamp"; }
8422 virtual ~TaskTstamp()
8423 {}
8425 virtual bool execute()
8426 {
8427 return true;
8428 }
8430 virtual bool parse(Element *elem)
8431 {
8432 //trace("tstamp parse");
8433 return true;
8434 }
8435 };
8439 /**
8440 *
8441 */
8442 Task *Task::createTask(Element *elem, int lineNr)
8443 {
8444 String tagName = elem->getName();
8445 //trace("task:%s", tagName.c_str());
8446 Task *task = NULL;
8447 if (tagName == "cc")
8448 task = new TaskCC(parent);
8449 else if (tagName == "copy")
8450 task = new TaskCopy(parent);
8451 else if (tagName == "delete")
8452 task = new TaskDelete(parent);
8453 else if (tagName == "echo")
8454 task = new TaskEcho(parent);
8455 else if (tagName == "jar")
8456 task = new TaskJar(parent);
8457 else if (tagName == "javac")
8458 task = new TaskJavac(parent);
8459 else if (tagName == "link")
8460 task = new TaskLink(parent);
8461 else if (tagName == "makefile")
8462 task = new TaskMakeFile(parent);
8463 else if (tagName == "mkdir")
8464 task = new TaskMkDir(parent);
8465 else if (tagName == "msgfmt")
8466 task = new TaskMsgFmt(parent);
8467 else if (tagName == "pkg-config")
8468 task = new TaskPkgConfig(parent);
8469 else if (tagName == "ranlib")
8470 task = new TaskRanlib(parent);
8471 else if (tagName == "rc")
8472 task = new TaskRC(parent);
8473 else if (tagName == "sharedlib")
8474 task = new TaskSharedLib(parent);
8475 else if (tagName == "staticlib")
8476 task = new TaskStaticLib(parent);
8477 else if (tagName == "strip")
8478 task = new TaskStrip(parent);
8479 else if (tagName == "touch")
8480 task = new TaskTouch(parent);
8481 else if (tagName == "tstamp")
8482 task = new TaskTstamp(parent);
8483 else
8484 {
8485 error("Unknown task '%s'", tagName.c_str());
8486 return NULL;
8487 }
8489 task->setLine(lineNr);
8491 if (!task->parse(elem))
8492 {
8493 delete task;
8494 return NULL;
8495 }
8496 return task;
8497 }
8501 //########################################################################
8502 //# T A R G E T
8503 //########################################################################
8505 /**
8506 *
8507 */
8508 class Target : public MakeBase
8509 {
8511 public:
8513 /**
8514 *
8515 */
8516 Target(Make &par) : parent(par)
8517 { init(); }
8519 /**
8520 *
8521 */
8522 Target(const Target &other) : parent(other.parent)
8523 { init(); assign(other); }
8525 /**
8526 *
8527 */
8528 Target &operator=(const Target &other)
8529 { init(); assign(other); return *this; }
8531 /**
8532 *
8533 */
8534 virtual ~Target()
8535 { cleanup() ; }
8538 /**
8539 *
8540 */
8541 virtual Make &getParent()
8542 { return parent; }
8544 /**
8545 *
8546 */
8547 virtual String getName()
8548 { return name; }
8550 /**
8551 *
8552 */
8553 virtual void setName(const String &val)
8554 { name = val; }
8556 /**
8557 *
8558 */
8559 virtual String getDescription()
8560 { return description; }
8562 /**
8563 *
8564 */
8565 virtual void setDescription(const String &val)
8566 { description = val; }
8568 /**
8569 *
8570 */
8571 virtual void addDependency(const String &val)
8572 { deps.push_back(val); }
8574 /**
8575 *
8576 */
8577 virtual void parseDependencies(const String &val)
8578 { deps = tokenize(val, ", "); }
8580 /**
8581 *
8582 */
8583 virtual std::vector<String> &getDependencies()
8584 { return deps; }
8586 /**
8587 *
8588 */
8589 virtual String getIf()
8590 { return ifVar; }
8592 /**
8593 *
8594 */
8595 virtual void setIf(const String &val)
8596 { ifVar = val; }
8598 /**
8599 *
8600 */
8601 virtual String getUnless()
8602 { return unlessVar; }
8604 /**
8605 *
8606 */
8607 virtual void setUnless(const String &val)
8608 { unlessVar = val; }
8610 /**
8611 *
8612 */
8613 virtual void addTask(Task *val)
8614 { tasks.push_back(val); }
8616 /**
8617 *
8618 */
8619 virtual std::vector<Task *> &getTasks()
8620 { return tasks; }
8622 private:
8624 void init()
8625 {
8626 }
8628 void cleanup()
8629 {
8630 tasks.clear();
8631 }
8633 void assign(const Target &other)
8634 {
8635 //parent = other.parent;
8636 name = other.name;
8637 description = other.description;
8638 ifVar = other.ifVar;
8639 unlessVar = other.unlessVar;
8640 deps = other.deps;
8641 tasks = other.tasks;
8642 }
8644 Make &parent;
8646 String name;
8648 String description;
8650 String ifVar;
8652 String unlessVar;
8654 std::vector<String> deps;
8656 std::vector<Task *> tasks;
8658 };
8667 //########################################################################
8668 //# M A K E
8669 //########################################################################
8672 /**
8673 *
8674 */
8675 class Make : public MakeBase
8676 {
8678 public:
8680 /**
8681 *
8682 */
8683 Make()
8684 { init(); }
8686 /**
8687 *
8688 */
8689 Make(const Make &other)
8690 { assign(other); }
8692 /**
8693 *
8694 */
8695 Make &operator=(const Make &other)
8696 { assign(other); return *this; }
8698 /**
8699 *
8700 */
8701 virtual ~Make()
8702 { cleanup(); }
8704 /**
8705 *
8706 */
8707 virtual std::map<String, Target> &getTargets()
8708 { return targets; }
8711 /**
8712 *
8713 */
8714 virtual String version()
8715 { return BUILDTOOL_VERSION; }
8717 /**
8718 * Overload a <property>
8719 */
8720 virtual bool specifyProperty(const String &name,
8721 const String &value);
8723 /**
8724 *
8725 */
8726 virtual bool run();
8728 /**
8729 *
8730 */
8731 virtual bool run(const String &target);
8735 private:
8737 /**
8738 *
8739 */
8740 void init();
8742 /**
8743 *
8744 */
8745 void cleanup();
8747 /**
8748 *
8749 */
8750 void assign(const Make &other);
8752 /**
8753 *
8754 */
8755 bool executeTask(Task &task);
8758 /**
8759 *
8760 */
8761 bool executeTarget(Target &target,
8762 std::set<String> &targetsCompleted);
8765 /**
8766 *
8767 */
8768 bool execute();
8770 /**
8771 *
8772 */
8773 bool checkTargetDependencies(Target &prop,
8774 std::vector<String> &depList);
8776 /**
8777 *
8778 */
8779 bool parsePropertyFile(const String &fileName,
8780 const String &prefix);
8782 /**
8783 *
8784 */
8785 bool parseProperty(Element *elem);
8787 /**
8788 *
8789 */
8790 bool parseFile();
8792 /**
8793 *
8794 */
8795 std::vector<String> glob(const String &pattern);
8798 //###############
8799 //# Fields
8800 //###############
8802 String projectName;
8804 String currentTarget;
8806 String defaultTarget;
8808 String specifiedTarget;
8810 String baseDir;
8812 String description;
8814 //std::vector<Property> properties;
8816 std::map<String, Target> targets;
8818 std::vector<Task *> allTasks;
8820 std::map<String, String> specifiedProperties;
8822 };
8825 //########################################################################
8826 //# C L A S S M A I N T E N A N C E
8827 //########################################################################
8829 /**
8830 *
8831 */
8832 void Make::init()
8833 {
8834 uri = "build.xml";
8835 projectName = "";
8836 currentTarget = "";
8837 defaultTarget = "";
8838 specifiedTarget = "";
8839 baseDir = "";
8840 description = "";
8841 envPrefix = "env.";
8842 pcPrefix = "pc.";
8843 pccPrefix = "pcc.";
8844 pclPrefix = "pcl.";
8845 properties.clear();
8846 for (unsigned int i = 0 ; i < allTasks.size() ; i++)
8847 delete allTasks[i];
8848 allTasks.clear();
8849 }
8853 /**
8854 *
8855 */
8856 void Make::cleanup()
8857 {
8858 for (unsigned int i = 0 ; i < allTasks.size() ; i++)
8859 delete allTasks[i];
8860 allTasks.clear();
8861 }
8865 /**
8866 *
8867 */
8868 void Make::assign(const Make &other)
8869 {
8870 uri = other.uri;
8871 projectName = other.projectName;
8872 currentTarget = other.currentTarget;
8873 defaultTarget = other.defaultTarget;
8874 specifiedTarget = other.specifiedTarget;
8875 baseDir = other.baseDir;
8876 description = other.description;
8877 properties = other.properties;
8878 }
8882 //########################################################################
8883 //# U T I L I T Y T A S K S
8884 //########################################################################
8886 /**
8887 * Perform a file globbing
8888 */
8889 std::vector<String> Make::glob(const String &pattern)
8890 {
8891 std::vector<String> res;
8892 return res;
8893 }
8896 //########################################################################
8897 //# P U B L I C A P I
8898 //########################################################################
8902 /**
8903 *
8904 */
8905 bool Make::executeTarget(Target &target,
8906 std::set<String> &targetsCompleted)
8907 {
8909 String name = target.getName();
8911 //First get any dependencies for this target
8912 std::vector<String> deps = target.getDependencies();
8913 for (unsigned int i=0 ; i<deps.size() ; i++)
8914 {
8915 String dep = deps[i];
8916 //Did we do it already? Skip
8917 if (targetsCompleted.find(dep)!=targetsCompleted.end())
8918 continue;
8920 std::map<String, Target> &tgts =
8921 target.getParent().getTargets();
8922 std::map<String, Target>::iterator iter =
8923 tgts.find(dep);
8924 if (iter == tgts.end())
8925 {
8926 error("Target '%s' dependency '%s' not found",
8927 name.c_str(), dep.c_str());
8928 return false;
8929 }
8930 Target depTarget = iter->second;
8931 if (!executeTarget(depTarget, targetsCompleted))
8932 {
8933 return false;
8934 }
8935 }
8937 status("##### Target : %s\n##### %s", name.c_str(),
8938 target.getDescription().c_str());
8940 //Now let's do the tasks
8941 std::vector<Task *> &tasks = target.getTasks();
8942 for (unsigned int i=0 ; i<tasks.size() ; i++)
8943 {
8944 Task *task = tasks[i];
8945 status("--- %s / %s", name.c_str(), task->getName().c_str());
8946 if (!task->execute())
8947 {
8948 return false;
8949 }
8950 }
8952 targetsCompleted.insert(name);
8954 return true;
8955 }
8959 /**
8960 * Main execute() method. Start here and work
8961 * up the dependency tree
8962 */
8963 bool Make::execute()
8964 {
8965 status("######## EXECUTE");
8967 //Determine initial target
8968 if (specifiedTarget.size()>0)
8969 {
8970 currentTarget = specifiedTarget;
8971 }
8972 else if (defaultTarget.size()>0)
8973 {
8974 currentTarget = defaultTarget;
8975 }
8976 else
8977 {
8978 error("execute: no specified or default target requested");
8979 return false;
8980 }
8982 std::map<String, Target>::iterator iter =
8983 targets.find(currentTarget);
8984 if (iter == targets.end())
8985 {
8986 error("Initial target '%s' not found",
8987 currentTarget.c_str());
8988 return false;
8989 }
8991 //Now run
8992 Target target = iter->second;
8993 std::set<String> targetsCompleted;
8994 if (!executeTarget(target, targetsCompleted))
8995 {
8996 return false;
8997 }
8999 status("######## EXECUTE COMPLETE");
9000 return true;
9001 }
9006 /**
9007 *
9008 */
9009 bool Make::checkTargetDependencies(Target &target,
9010 std::vector<String> &depList)
9011 {
9012 String tgtName = target.getName().c_str();
9013 depList.push_back(tgtName);
9015 std::vector<String> deps = target.getDependencies();
9016 for (unsigned int i=0 ; i<deps.size() ; i++)
9017 {
9018 String dep = deps[i];
9019 //First thing entered was the starting Target
9020 if (dep == depList[0])
9021 {
9022 error("Circular dependency '%s' found at '%s'",
9023 dep.c_str(), tgtName.c_str());
9024 std::vector<String>::iterator diter;
9025 for (diter=depList.begin() ; diter!=depList.end() ; diter++)
9026 {
9027 error(" %s", diter->c_str());
9028 }
9029 return false;
9030 }
9032 std::map<String, Target> &tgts =
9033 target.getParent().getTargets();
9034 std::map<String, Target>::iterator titer = tgts.find(dep);
9035 if (titer == tgts.end())
9036 {
9037 error("Target '%s' dependency '%s' not found",
9038 tgtName.c_str(), dep.c_str());
9039 return false;
9040 }
9041 if (!checkTargetDependencies(titer->second, depList))
9042 {
9043 return false;
9044 }
9045 }
9046 return true;
9047 }
9053 static int getword(int pos, const String &inbuf, String &result)
9054 {
9055 int p = pos;
9056 int len = (int)inbuf.size();
9057 String val;
9058 while (p < len)
9059 {
9060 char ch = inbuf[p];
9061 if (!isalnum(ch) && ch!='.' && ch!='_')
9062 break;
9063 val.push_back(ch);
9064 p++;
9065 }
9066 result = val;
9067 return p;
9068 }
9073 /**
9074 *
9075 */
9076 bool Make::parsePropertyFile(const String &fileName,
9077 const String &prefix)
9078 {
9079 FILE *f = fopen(fileName.c_str(), "r");
9080 if (!f)
9081 {
9082 error("could not open property file %s", fileName.c_str());
9083 return false;
9084 }
9085 int linenr = 0;
9086 while (!feof(f))
9087 {
9088 char buf[256];
9089 if (!fgets(buf, 255, f))
9090 break;
9091 linenr++;
9092 String s = buf;
9093 s = trim(s);
9094 int len = s.size();
9095 if (len == 0)
9096 continue;
9097 if (s[0] == '#')
9098 continue;
9099 String key;
9100 String val;
9101 int p = 0;
9102 int p2 = getword(p, s, key);
9103 if (p2 <= p)
9104 {
9105 error("property file %s, line %d: expected keyword",
9106 fileName.c_str(), linenr);
9107 return false;
9108 }
9109 if (prefix.size() > 0)
9110 {
9111 key.insert(0, prefix);
9112 }
9114 //skip whitespace
9115 for (p=p2 ; p<len ; p++)
9116 if (!isspace(s[p]))
9117 break;
9119 if (p>=len || s[p]!='=')
9120 {
9121 error("property file %s, line %d: expected '='",
9122 fileName.c_str(), linenr);
9123 return false;
9124 }
9125 p++;
9127 //skip whitespace
9128 for ( ; p<len ; p++)
9129 if (!isspace(s[p]))
9130 break;
9132 /* This way expects a word after the =
9133 p2 = getword(p, s, val);
9134 if (p2 <= p)
9135 {
9136 error("property file %s, line %d: expected value",
9137 fileName.c_str(), linenr);
9138 return false;
9139 }
9140 */
9141 // This way gets the rest of the line after the =
9142 if (p>=len)
9143 {
9144 error("property file %s, line %d: expected value",
9145 fileName.c_str(), linenr);
9146 return false;
9147 }
9148 val = s.substr(p);
9149 if (key.size()==0)
9150 continue;
9151 //allow property to be set, even if val=""
9153 //trace("key:'%s' val:'%s'", key.c_str(), val.c_str());
9154 //See if we wanted to overload this property
9155 std::map<String, String>::iterator iter =
9156 specifiedProperties.find(key);
9157 if (iter!=specifiedProperties.end())
9158 {
9159 val = iter->second;
9160 status("overloading property '%s' = '%s'",
9161 key.c_str(), val.c_str());
9162 }
9163 properties[key] = val;
9164 }
9165 fclose(f);
9166 return true;
9167 }
9172 /**
9173 *
9174 */
9175 bool Make::parseProperty(Element *elem)
9176 {
9177 std::vector<Attribute> &attrs = elem->getAttributes();
9178 for (unsigned int i=0 ; i<attrs.size() ; i++)
9179 {
9180 String attrName = attrs[i].getName();
9181 String attrVal = attrs[i].getValue();
9183 if (attrName == "name")
9184 {
9185 String val;
9186 if (!getAttribute(elem, "value", val))
9187 return false;
9188 if (val.size() > 0)
9189 {
9190 properties[attrVal] = val;
9191 }
9192 else
9193 {
9194 if (!getAttribute(elem, "location", val))
9195 return false;
9196 //let the property exist, even if not defined
9197 properties[attrVal] = val;
9198 }
9199 //See if we wanted to overload this property
9200 std::map<String, String>::iterator iter =
9201 specifiedProperties.find(attrVal);
9202 if (iter != specifiedProperties.end())
9203 {
9204 val = iter->second;
9205 status("overloading property '%s' = '%s'",
9206 attrVal.c_str(), val.c_str());
9207 properties[attrVal] = val;
9208 }
9209 }
9210 else if (attrName == "file")
9211 {
9212 String prefix;
9213 if (!getAttribute(elem, "prefix", prefix))
9214 return false;
9215 if (prefix.size() > 0)
9216 {
9217 if (prefix[prefix.size()-1] != '.')
9218 prefix.push_back('.');
9219 }
9220 if (!parsePropertyFile(attrName, prefix))
9221 return false;
9222 }
9223 else if (attrName == "environment")
9224 {
9225 if (attrVal.find('.') != attrVal.npos)
9226 {
9227 error("environment prefix cannot have a '.' in it");
9228 return false;
9229 }
9230 envPrefix = attrVal;
9231 envPrefix.push_back('.');
9232 }
9233 else if (attrName == "pkg-config")
9234 {
9235 if (attrVal.find('.') != attrVal.npos)
9236 {
9237 error("pkg-config prefix cannot have a '.' in it");
9238 return false;
9239 }
9240 pcPrefix = attrVal;
9241 pcPrefix.push_back('.');
9242 }
9243 else if (attrName == "pkg-config-cflags")
9244 {
9245 if (attrVal.find('.') != attrVal.npos)
9246 {
9247 error("pkg-config-cflags prefix cannot have a '.' in it");
9248 return false;
9249 }
9250 pccPrefix = attrVal;
9251 pccPrefix.push_back('.');
9252 }
9253 else if (attrName == "pkg-config-libs")
9254 {
9255 if (attrVal.find('.') != attrVal.npos)
9256 {
9257 error("pkg-config-libs prefix cannot have a '.' in it");
9258 return false;
9259 }
9260 pclPrefix = attrVal;
9261 pclPrefix.push_back('.');
9262 }
9263 }
9265 return true;
9266 }
9271 /**
9272 *
9273 */
9274 bool Make::parseFile()
9275 {
9276 status("######## PARSE : %s", uri.getPath().c_str());
9278 setLine(0);
9280 Parser parser;
9281 Element *root = parser.parseFile(uri.getNativePath());
9282 if (!root)
9283 {
9284 error("Could not open %s for reading",
9285 uri.getNativePath().c_str());
9286 return false;
9287 }
9289 setLine(root->getLine());
9291 if (root->getChildren().size()==0 ||
9292 root->getChildren()[0]->getName()!="project")
9293 {
9294 error("Main xml element should be <project>");
9295 delete root;
9296 return false;
9297 }
9299 //########## Project attributes
9300 Element *project = root->getChildren()[0];
9301 String s = project->getAttribute("name");
9302 if (s.size() > 0)
9303 projectName = s;
9304 s = project->getAttribute("default");
9305 if (s.size() > 0)
9306 defaultTarget = s;
9307 s = project->getAttribute("basedir");
9308 if (s.size() > 0)
9309 baseDir = s;
9311 //######### PARSE MEMBERS
9312 std::vector<Element *> children = project->getChildren();
9313 for (unsigned int i=0 ; i<children.size() ; i++)
9314 {
9315 Element *elem = children[i];
9316 setLine(elem->getLine());
9317 String tagName = elem->getName();
9319 //########## DESCRIPTION
9320 if (tagName == "description")
9321 {
9322 description = parser.trim(elem->getValue());
9323 }
9325 //######### PROPERTY
9326 else if (tagName == "property")
9327 {
9328 if (!parseProperty(elem))
9329 return false;
9330 }
9332 //######### TARGET
9333 else if (tagName == "target")
9334 {
9335 String tname = elem->getAttribute("name");
9336 String tdesc = elem->getAttribute("description");
9337 String tdeps = elem->getAttribute("depends");
9338 String tif = elem->getAttribute("if");
9339 String tunless = elem->getAttribute("unless");
9340 Target target(*this);
9341 target.setName(tname);
9342 target.setDescription(tdesc);
9343 target.parseDependencies(tdeps);
9344 target.setIf(tif);
9345 target.setUnless(tunless);
9346 std::vector<Element *> telems = elem->getChildren();
9347 for (unsigned int i=0 ; i<telems.size() ; i++)
9348 {
9349 Element *telem = telems[i];
9350 Task breeder(*this);
9351 Task *task = breeder.createTask(telem, telem->getLine());
9352 if (!task)
9353 return false;
9354 allTasks.push_back(task);
9355 target.addTask(task);
9356 }
9358 //Check name
9359 if (tname.size() == 0)
9360 {
9361 error("no name for target");
9362 return false;
9363 }
9364 //Check for duplicate name
9365 if (targets.find(tname) != targets.end())
9366 {
9367 error("target '%s' already defined", tname.c_str());
9368 return false;
9369 }
9370 //more work than targets[tname]=target, but avoids default allocator
9371 targets.insert(std::make_pair<String, Target>(tname, target));
9372 }
9373 //######### none of the above
9374 else
9375 {
9376 error("unknown toplevel tag: <%s>", tagName.c_str());
9377 return false;
9378 }
9380 }
9382 std::map<String, Target>::iterator iter;
9383 for (iter = targets.begin() ; iter!= targets.end() ; iter++)
9384 {
9385 Target tgt = iter->second;
9386 std::vector<String> depList;
9387 if (!checkTargetDependencies(tgt, depList))
9388 {
9389 return false;
9390 }
9391 }
9394 delete root;
9395 status("######## PARSE COMPLETE");
9396 return true;
9397 }
9400 /**
9401 * Overload a <property>
9402 */
9403 bool Make::specifyProperty(const String &name, const String &value)
9404 {
9405 if (specifiedProperties.find(name) != specifiedProperties.end())
9406 {
9407 error("Property %s already specified", name.c_str());
9408 return false;
9409 }
9410 specifiedProperties[name] = value;
9411 return true;
9412 }
9416 /**
9417 *
9418 */
9419 bool Make::run()
9420 {
9421 if (!parseFile())
9422 return false;
9424 if (!execute())
9425 return false;
9427 return true;
9428 }
9433 /**
9434 * Get a formatted MM:SS.sss time elapsed string
9435 */
9436 static String
9437 timeDiffString(struct timeval &x, struct timeval &y)
9438 {
9439 long microsX = x.tv_usec;
9440 long secondsX = x.tv_sec;
9441 long microsY = y.tv_usec;
9442 long secondsY = y.tv_sec;
9443 if (microsX < microsY)
9444 {
9445 microsX += 1000000;
9446 secondsX -= 1;
9447 }
9449 int seconds = (int)(secondsX - secondsY);
9450 int millis = (int)((microsX - microsY)/1000);
9452 int minutes = seconds/60;
9453 seconds -= minutes*60;
9454 char buf[80];
9455 snprintf(buf, 79, "%dm %d.%03ds", minutes, seconds, millis);
9456 String ret = buf;
9457 return ret;
9459 }
9461 /**
9462 *
9463 */
9464 bool Make::run(const String &target)
9465 {
9466 status("####################################################");
9467 status("# %s", version().c_str());
9468 status("####################################################");
9469 struct timeval timeStart, timeEnd;
9470 ::gettimeofday(&timeStart, NULL);
9471 specifiedTarget = target;
9472 if (!run())
9473 return false;
9474 ::gettimeofday(&timeEnd, NULL);
9475 String timeStr = timeDiffString(timeEnd, timeStart);
9476 status("####################################################");
9477 status("# BuildTool Completed : %s", timeStr.c_str());
9478 status("####################################################");
9479 return true;
9480 }
9488 }// namespace buildtool
9489 //########################################################################
9490 //# M A I N
9491 //########################################################################
9493 typedef buildtool::String String;
9495 /**
9496 * Format an error message in printf() style
9497 */
9498 static void error(const char *fmt, ...)
9499 {
9500 va_list ap;
9501 va_start(ap, fmt);
9502 fprintf(stderr, "BuildTool error: ");
9503 vfprintf(stderr, fmt, ap);
9504 fprintf(stderr, "\n");
9505 va_end(ap);
9506 }
9509 static bool parseProperty(const String &s, String &name, String &val)
9510 {
9511 int len = s.size();
9512 int i;
9513 for (i=0 ; i<len ; i++)
9514 {
9515 char ch = s[i];
9516 if (ch == '=')
9517 break;
9518 name.push_back(ch);
9519 }
9520 if (i>=len || s[i]!='=')
9521 {
9522 error("property requires -Dname=value");
9523 return false;
9524 }
9525 i++;
9526 for ( ; i<len ; i++)
9527 {
9528 char ch = s[i];
9529 val.push_back(ch);
9530 }
9531 return true;
9532 }
9535 /**
9536 * Compare a buffer with a key, for the length of the key
9537 */
9538 static bool sequ(const String &buf, const char *key)
9539 {
9540 int len = buf.size();
9541 for (int i=0 ; key[i] && i<len ; i++)
9542 {
9543 if (key[i] != buf[i])
9544 return false;
9545 }
9546 return true;
9547 }
9549 static void usage(int argc, char **argv)
9550 {
9551 printf("usage:\n");
9552 printf(" %s [options] [target]\n", argv[0]);
9553 printf("Options:\n");
9554 printf(" -help, -h print this message\n");
9555 printf(" -version print the version information and exit\n");
9556 printf(" -file <file> use given buildfile\n");
9557 printf(" -f <file> ''\n");
9558 printf(" -D<property>=<value> use value for given property\n");
9559 }
9564 /**
9565 * Parse the command-line args, get our options,
9566 * and run this thing
9567 */
9568 static bool parseOptions(int argc, char **argv)
9569 {
9570 if (argc < 1)
9571 {
9572 error("Cannot parse arguments");
9573 return false;
9574 }
9576 buildtool::Make make;
9578 String target;
9580 //char *progName = argv[0];
9581 for (int i=1 ; i<argc ; i++)
9582 {
9583 String arg = argv[i];
9584 if (arg.size()>1 && arg[0]=='-')
9585 {
9586 if (arg == "-h" || arg == "-help")
9587 {
9588 usage(argc,argv);
9589 return true;
9590 }
9591 else if (arg == "-version")
9592 {
9593 printf("%s", make.version().c_str());
9594 return true;
9595 }
9596 else if (arg == "-f" || arg == "-file")
9597 {
9598 if (i>=argc)
9599 {
9600 usage(argc, argv);
9601 return false;
9602 }
9603 i++; //eat option
9604 make.setURI(argv[i]);
9605 }
9606 else if (arg.size()>2 && sequ(arg, "-D"))
9607 {
9608 String s = arg.substr(2, arg.size());
9609 String name, value;
9610 if (!parseProperty(s, name, value))
9611 {
9612 usage(argc, argv);
9613 return false;
9614 }
9615 if (!make.specifyProperty(name, value))
9616 return false;
9617 }
9618 else
9619 {
9620 error("Unknown option:%s", arg.c_str());
9621 return false;
9622 }
9623 }
9624 else
9625 {
9626 if (target.size()>0)
9627 {
9628 error("only one initial target");
9629 usage(argc, argv);
9630 return false;
9631 }
9632 target = arg;
9633 }
9634 }
9636 //We have the options. Now execute them
9637 if (!make.run(target))
9638 return false;
9640 return true;
9641 }
9646 /*
9647 static bool runMake()
9648 {
9649 buildtool::Make make;
9650 if (!make.run())
9651 return false;
9652 return true;
9653 }
9656 static bool pkgConfigTest()
9657 {
9658 buildtool::PkgConfig pkgConfig;
9659 if (!pkgConfig.readFile("gtk+-2.0.pc"))
9660 return false;
9661 return true;
9662 }
9666 static bool depTest()
9667 {
9668 buildtool::DepTool deptool;
9669 deptool.setSourceDirectory("/dev/ink/inkscape/src");
9670 if (!deptool.generateDependencies("build.dep"))
9671 return false;
9672 std::vector<buildtool::FileRec> res =
9673 deptool.loadDepFile("build.dep");
9674 if (res.size() == 0)
9675 return false;
9676 return true;
9677 }
9679 static bool popenTest()
9680 {
9681 buildtool::Make make;
9682 buildtool::String out, err;
9683 bool ret = make.executeCommand("gcc xx.cpp", "", out, err);
9684 printf("Popen test:%d '%s' '%s'\n", ret, out.c_str(), err.c_str());
9685 return true;
9686 }
9689 static bool propFileTest()
9690 {
9691 buildtool::Make make;
9692 make.parsePropertyFile("test.prop", "test.");
9693 return true;
9694 }
9695 */
9697 int main(int argc, char **argv)
9698 {
9700 if (!parseOptions(argc, argv))
9701 return 1;
9702 /*
9703 if (!popenTest())
9704 return 1;
9706 if (!depTest())
9707 return 1;
9708 if (!propFileTest())
9709 return 1;
9710 if (runMake())
9711 return 1;
9712 */
9713 return 0;
9714 }
9717 //########################################################################
9718 //# E N D
9719 //########################################################################