1 /**
2 * Simple build automation tool.
3 *
4 * Authors:
5 * Bob Jamison
6 *
7 * Copyright (C) 2006-2007 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.7.1, 2007 Bob Jamison"
43 #include <stdio.h>
44 #include <fcntl.h>
45 #include <unistd.h>
46 #include <stdarg.h>
47 #include <sys/stat.h>
48 #include <time.h>
49 #include <sys/time.h>
50 #include <utime.h>
51 #include <dirent.h>
53 #include <string>
54 #include <map>
55 #include <set>
56 #include <vector>
58 #ifdef __WIN32__
59 #include <windows.h>
60 #endif
63 #include <errno.h>
66 //########################################################################
67 //# Definition of gettimeofday() for those who don't have it
68 //########################################################################
69 #ifdef NEED_GETTIMEOFDAY
70 #include <sys/timeb.h>
72 struct timezone {
73 int tz_minuteswest; /* minutes west of Greenwich */
74 int tz_dsttime; /* type of dst correction */
75 };
77 static int gettimeofday (struct timeval *tv, struct timezone *tz)
78 {
79 struct _timeb tb;
81 if (!tv)
82 return (-1);
84 _ftime (&tb);
85 tv->tv_sec = tb.time;
86 tv->tv_usec = tb.millitm * 1000 + 500;
87 if (tz)
88 {
89 tz->tz_minuteswest = -60 * _timezone;
90 tz->tz_dsttime = _daylight;
91 }
92 return 0;
93 }
95 #endif
103 namespace buildtool
104 {
109 //########################################################################
110 //########################################################################
111 //## R E G E X P
112 //########################################################################
113 //########################################################################
115 /**
116 * This is the T-Rex regular expression library, which we
117 * gratefully acknowledge. It's clean code and small size allow
118 * us to embed it in BuildTool without adding a dependency
119 *
120 */
122 //begin trex.h
124 #ifndef _TREX_H_
125 #define _TREX_H_
126 /***************************************************************
127 T-Rex a tiny regular expression library
129 Copyright (C) 2003-2006 Alberto Demichelis
131 This software is provided 'as-is', without any express
132 or implied warranty. In no event will the authors be held
133 liable for any damages arising from the use of this software.
135 Permission is granted to anyone to use this software for
136 any purpose, including commercial applications, and to alter
137 it and redistribute it freely, subject to the following restrictions:
139 1. The origin of this software must not be misrepresented;
140 you must not claim that you wrote the original software.
141 If you use this software in a product, an acknowledgment
142 in the product documentation would be appreciated but
143 is not required.
145 2. Altered source versions must be plainly marked as such,
146 and must not be misrepresented as being the original software.
148 3. This notice may not be removed or altered from any
149 source distribution.
151 ****************************************************************/
153 #ifdef _UNICODE
154 #define TRexChar unsigned short
155 #define MAX_CHAR 0xFFFF
156 #define _TREXC(c) L##c
157 #define trex_strlen wcslen
158 #define trex_printf wprintf
159 #else
160 #define TRexChar char
161 #define MAX_CHAR 0xFF
162 #define _TREXC(c) (c)
163 #define trex_strlen strlen
164 #define trex_printf printf
165 #endif
167 #ifndef TREX_API
168 #define TREX_API extern
169 #endif
171 #define TRex_True 1
172 #define TRex_False 0
174 typedef unsigned int TRexBool;
175 typedef struct TRex TRex;
177 typedef struct {
178 const TRexChar *begin;
179 int len;
180 } TRexMatch;
182 TREX_API TRex *trex_compile(const TRexChar *pattern,const TRexChar **error);
183 TREX_API void trex_free(TRex *exp);
184 TREX_API TRexBool trex_match(TRex* exp,const TRexChar* text);
185 TREX_API TRexBool trex_search(TRex* exp,const TRexChar* text, const TRexChar** out_begin, const TRexChar** out_end);
186 TREX_API TRexBool trex_searchrange(TRex* exp,const TRexChar* text_begin,const TRexChar* text_end,const TRexChar** out_begin, const TRexChar** out_end);
187 TREX_API int trex_getsubexpcount(TRex* exp);
188 TREX_API TRexBool trex_getsubexp(TRex* exp, int n, TRexMatch *subexp);
190 #endif
192 //end trex.h
194 //start trex.c
197 #include <stdio.h>
198 #include <string>
200 /* see copyright notice in trex.h */
201 #include <string.h>
202 #include <stdlib.h>
203 #include <ctype.h>
204 #include <setjmp.h>
205 //#include "trex.h"
207 #ifdef _UINCODE
208 #define scisprint iswprint
209 #define scstrlen wcslen
210 #define scprintf wprintf
211 #define _SC(x) L(x)
212 #else
213 #define scisprint isprint
214 #define scstrlen strlen
215 #define scprintf printf
216 #define _SC(x) (x)
217 #endif
219 #ifdef _DEBUG
220 #include <stdio.h>
222 static const TRexChar *g_nnames[] =
223 {
224 _SC("NONE"),_SC("OP_GREEDY"), _SC("OP_OR"),
225 _SC("OP_EXPR"),_SC("OP_NOCAPEXPR"),_SC("OP_DOT"), _SC("OP_CLASS"),
226 _SC("OP_CCLASS"),_SC("OP_NCLASS"),_SC("OP_RANGE"),_SC("OP_CHAR"),
227 _SC("OP_EOL"),_SC("OP_BOL"),_SC("OP_WB")
228 };
230 #endif
231 #define OP_GREEDY (MAX_CHAR+1) // * + ? {n}
232 #define OP_OR (MAX_CHAR+2)
233 #define OP_EXPR (MAX_CHAR+3) //parentesis ()
234 #define OP_NOCAPEXPR (MAX_CHAR+4) //parentesis (?:)
235 #define OP_DOT (MAX_CHAR+5)
236 #define OP_CLASS (MAX_CHAR+6)
237 #define OP_CCLASS (MAX_CHAR+7)
238 #define OP_NCLASS (MAX_CHAR+8) //negates class the [^
239 #define OP_RANGE (MAX_CHAR+9)
240 #define OP_CHAR (MAX_CHAR+10)
241 #define OP_EOL (MAX_CHAR+11)
242 #define OP_BOL (MAX_CHAR+12)
243 #define OP_WB (MAX_CHAR+13)
245 #define TREX_SYMBOL_ANY_CHAR ('.')
246 #define TREX_SYMBOL_GREEDY_ONE_OR_MORE ('+')
247 #define TREX_SYMBOL_GREEDY_ZERO_OR_MORE ('*')
248 #define TREX_SYMBOL_GREEDY_ZERO_OR_ONE ('?')
249 #define TREX_SYMBOL_BRANCH ('|')
250 #define TREX_SYMBOL_END_OF_STRING ('$')
251 #define TREX_SYMBOL_BEGINNING_OF_STRING ('^')
252 #define TREX_SYMBOL_ESCAPE_CHAR ('\\')
255 typedef int TRexNodeType;
257 typedef struct tagTRexNode{
258 TRexNodeType type;
259 int left;
260 int right;
261 int next;
262 }TRexNode;
264 struct TRex{
265 const TRexChar *_eol;
266 const TRexChar *_bol;
267 const TRexChar *_p;
268 int _first;
269 int _op;
270 TRexNode *_nodes;
271 int _nallocated;
272 int _nsize;
273 int _nsubexpr;
274 TRexMatch *_matches;
275 int _currsubexp;
276 void *_jmpbuf;
277 const TRexChar **_error;
278 };
280 static int trex_list(TRex *exp);
282 static int trex_newnode(TRex *exp, TRexNodeType type)
283 {
284 TRexNode n;
285 int newid;
286 n.type = type;
287 n.next = n.right = n.left = -1;
288 if(type == OP_EXPR)
289 n.right = exp->_nsubexpr++;
290 if(exp->_nallocated < (exp->_nsize + 1)) {
291 //int oldsize = exp->_nallocated;
292 exp->_nallocated *= 2;
293 exp->_nodes = (TRexNode *)realloc(exp->_nodes, exp->_nallocated * sizeof(TRexNode));
294 }
295 exp->_nodes[exp->_nsize++] = n;
296 newid = exp->_nsize - 1;
297 return (int)newid;
298 }
300 static void trex_error(TRex *exp,const TRexChar *error)
301 {
302 if(exp->_error) *exp->_error = error;
303 longjmp(*((jmp_buf*)exp->_jmpbuf),-1);
304 }
306 static void trex_expect(TRex *exp, int n){
307 if((*exp->_p) != n)
308 trex_error(exp, _SC("expected paren"));
309 exp->_p++;
310 }
312 static TRexChar trex_escapechar(TRex *exp)
313 {
314 if(*exp->_p == TREX_SYMBOL_ESCAPE_CHAR){
315 exp->_p++;
316 switch(*exp->_p) {
317 case 'v': exp->_p++; return '\v';
318 case 'n': exp->_p++; return '\n';
319 case 't': exp->_p++; return '\t';
320 case 'r': exp->_p++; return '\r';
321 case 'f': exp->_p++; return '\f';
322 default: return (*exp->_p++);
323 }
324 } else if(!scisprint(*exp->_p)) trex_error(exp,_SC("letter expected"));
325 return (*exp->_p++);
326 }
328 static int trex_charclass(TRex *exp,int classid)
329 {
330 int n = trex_newnode(exp,OP_CCLASS);
331 exp->_nodes[n].left = classid;
332 return n;
333 }
335 static int trex_charnode(TRex *exp,TRexBool isclass)
336 {
337 TRexChar t;
338 if(*exp->_p == TREX_SYMBOL_ESCAPE_CHAR) {
339 exp->_p++;
340 switch(*exp->_p) {
341 case 'n': exp->_p++; return trex_newnode(exp,'\n');
342 case 't': exp->_p++; return trex_newnode(exp,'\t');
343 case 'r': exp->_p++; return trex_newnode(exp,'\r');
344 case 'f': exp->_p++; return trex_newnode(exp,'\f');
345 case 'v': exp->_p++; return trex_newnode(exp,'\v');
346 case 'a': case 'A': case 'w': case 'W': case 's': case 'S':
347 case 'd': case 'D': case 'x': case 'X': case 'c': case 'C':
348 case 'p': case 'P': case 'l': case 'u':
349 {
350 t = *exp->_p; exp->_p++;
351 return trex_charclass(exp,t);
352 }
353 case 'b':
354 case 'B':
355 if(!isclass) {
356 int node = trex_newnode(exp,OP_WB);
357 exp->_nodes[node].left = *exp->_p;
358 exp->_p++;
359 return node;
360 } //else default
361 default:
362 t = *exp->_p; exp->_p++;
363 return trex_newnode(exp,t);
364 }
365 }
366 else if(!scisprint(*exp->_p)) {
368 trex_error(exp,_SC("letter expected"));
369 }
370 t = *exp->_p; exp->_p++;
371 return trex_newnode(exp,t);
372 }
373 static int trex_class(TRex *exp)
374 {
375 int ret = -1;
376 int first = -1,chain;
377 if(*exp->_p == TREX_SYMBOL_BEGINNING_OF_STRING){
378 ret = trex_newnode(exp,OP_NCLASS);
379 exp->_p++;
380 }else ret = trex_newnode(exp,OP_CLASS);
382 if(*exp->_p == ']') trex_error(exp,_SC("empty class"));
383 chain = ret;
384 while(*exp->_p != ']' && exp->_p != exp->_eol) {
385 if(*exp->_p == '-' && first != -1){
386 int r,t;
387 if(*exp->_p++ == ']') trex_error(exp,_SC("unfinished range"));
388 r = trex_newnode(exp,OP_RANGE);
389 if(first>*exp->_p) trex_error(exp,_SC("invalid range"));
390 if(exp->_nodes[first].type == OP_CCLASS) trex_error(exp,_SC("cannot use character classes in ranges"));
391 exp->_nodes[r].left = exp->_nodes[first].type;
392 t = trex_escapechar(exp);
393 exp->_nodes[r].right = t;
394 exp->_nodes[chain].next = r;
395 chain = r;
396 first = -1;
397 }
398 else{
399 if(first!=-1){
400 int c = first;
401 exp->_nodes[chain].next = c;
402 chain = c;
403 first = trex_charnode(exp,TRex_True);
404 }
405 else{
406 first = trex_charnode(exp,TRex_True);
407 }
408 }
409 }
410 if(first!=-1){
411 int c = first;
412 exp->_nodes[chain].next = c;
413 chain = c;
414 first = -1;
415 }
416 /* hack? */
417 exp->_nodes[ret].left = exp->_nodes[ret].next;
418 exp->_nodes[ret].next = -1;
419 return ret;
420 }
422 static int trex_parsenumber(TRex *exp)
423 {
424 int ret = *exp->_p-'0';
425 int positions = 10;
426 exp->_p++;
427 while(isdigit(*exp->_p)) {
428 ret = ret*10+(*exp->_p++-'0');
429 if(positions==1000000000) trex_error(exp,_SC("overflow in numeric constant"));
430 positions *= 10;
431 };
432 return ret;
433 }
435 static int trex_element(TRex *exp)
436 {
437 int ret = -1;
438 switch(*exp->_p)
439 {
440 case '(': {
441 int expr,newn;
442 exp->_p++;
445 if(*exp->_p =='?') {
446 exp->_p++;
447 trex_expect(exp,':');
448 expr = trex_newnode(exp,OP_NOCAPEXPR);
449 }
450 else
451 expr = trex_newnode(exp,OP_EXPR);
452 newn = trex_list(exp);
453 exp->_nodes[expr].left = newn;
454 ret = expr;
455 trex_expect(exp,')');
456 }
457 break;
458 case '[':
459 exp->_p++;
460 ret = trex_class(exp);
461 trex_expect(exp,']');
462 break;
463 case TREX_SYMBOL_END_OF_STRING: exp->_p++; ret = trex_newnode(exp,OP_EOL);break;
464 case TREX_SYMBOL_ANY_CHAR: exp->_p++; ret = trex_newnode(exp,OP_DOT);break;
465 default:
466 ret = trex_charnode(exp,TRex_False);
467 break;
468 }
470 {
471 int op;
472 TRexBool isgreedy = TRex_False;
473 unsigned short p0 = 0, p1 = 0;
474 switch(*exp->_p){
475 case TREX_SYMBOL_GREEDY_ZERO_OR_MORE: p0 = 0; p1 = 0xFFFF; exp->_p++; isgreedy = TRex_True; break;
476 case TREX_SYMBOL_GREEDY_ONE_OR_MORE: p0 = 1; p1 = 0xFFFF; exp->_p++; isgreedy = TRex_True; break;
477 case TREX_SYMBOL_GREEDY_ZERO_OR_ONE: p0 = 0; p1 = 1; exp->_p++; isgreedy = TRex_True; break;
478 case '{':
479 exp->_p++;
480 if(!isdigit(*exp->_p)) trex_error(exp,_SC("number expected"));
481 p0 = (unsigned short)trex_parsenumber(exp);
482 /*******************************/
483 switch(*exp->_p) {
484 case '}':
485 p1 = p0; exp->_p++;
486 break;
487 case ',':
488 exp->_p++;
489 p1 = 0xFFFF;
490 if(isdigit(*exp->_p)){
491 p1 = (unsigned short)trex_parsenumber(exp);
492 }
493 trex_expect(exp,'}');
494 break;
495 default:
496 trex_error(exp,_SC(", or } expected"));
497 }
498 /*******************************/
499 isgreedy = TRex_True;
500 break;
502 }
503 if(isgreedy) {
504 int nnode = trex_newnode(exp,OP_GREEDY);
505 op = OP_GREEDY;
506 exp->_nodes[nnode].left = ret;
507 exp->_nodes[nnode].right = ((p0)<<16)|p1;
508 ret = nnode;
509 }
510 }
511 if((*exp->_p != TREX_SYMBOL_BRANCH) && (*exp->_p != ')') && (*exp->_p != TREX_SYMBOL_GREEDY_ZERO_OR_MORE) && (*exp->_p != TREX_SYMBOL_GREEDY_ONE_OR_MORE) && (*exp->_p != '\0')) {
512 int nnode = trex_element(exp);
513 exp->_nodes[ret].next = nnode;
514 }
516 return ret;
517 }
519 static int trex_list(TRex *exp)
520 {
521 int ret=-1,e;
522 if(*exp->_p == TREX_SYMBOL_BEGINNING_OF_STRING) {
523 exp->_p++;
524 ret = trex_newnode(exp,OP_BOL);
525 }
526 e = trex_element(exp);
527 if(ret != -1) {
528 exp->_nodes[ret].next = e;
529 }
530 else ret = e;
532 if(*exp->_p == TREX_SYMBOL_BRANCH) {
533 int temp,tright;
534 exp->_p++;
535 temp = trex_newnode(exp,OP_OR);
536 exp->_nodes[temp].left = ret;
537 tright = trex_list(exp);
538 exp->_nodes[temp].right = tright;
539 ret = temp;
540 }
541 return ret;
542 }
544 static TRexBool trex_matchcclass(int cclass,TRexChar c)
545 {
546 switch(cclass) {
547 case 'a': return isalpha(c)?TRex_True:TRex_False;
548 case 'A': return !isalpha(c)?TRex_True:TRex_False;
549 case 'w': return (isalnum(c) || c == '_')?TRex_True:TRex_False;
550 case 'W': return (!isalnum(c) && c != '_')?TRex_True:TRex_False;
551 case 's': return isspace(c)?TRex_True:TRex_False;
552 case 'S': return !isspace(c)?TRex_True:TRex_False;
553 case 'd': return isdigit(c)?TRex_True:TRex_False;
554 case 'D': return !isdigit(c)?TRex_True:TRex_False;
555 case 'x': return isxdigit(c)?TRex_True:TRex_False;
556 case 'X': return !isxdigit(c)?TRex_True:TRex_False;
557 case 'c': return iscntrl(c)?TRex_True:TRex_False;
558 case 'C': return !iscntrl(c)?TRex_True:TRex_False;
559 case 'p': return ispunct(c)?TRex_True:TRex_False;
560 case 'P': return !ispunct(c)?TRex_True:TRex_False;
561 case 'l': return islower(c)?TRex_True:TRex_False;
562 case 'u': return isupper(c)?TRex_True:TRex_False;
563 }
564 return TRex_False; /*cannot happen*/
565 }
567 static TRexBool trex_matchclass(TRex* exp,TRexNode *node,TRexChar c)
568 {
569 do {
570 switch(node->type) {
571 case OP_RANGE:
572 if(c >= node->left && c <= node->right) return TRex_True;
573 break;
574 case OP_CCLASS:
575 if(trex_matchcclass(node->left,c)) return TRex_True;
576 break;
577 default:
578 if(c == node->type)return TRex_True;
579 }
580 } while((node->next != -1) && (node = &exp->_nodes[node->next]));
581 return TRex_False;
582 }
584 static const TRexChar *trex_matchnode(TRex* exp,TRexNode *node,const TRexChar *str,TRexNode *next)
585 {
587 TRexNodeType type = node->type;
588 switch(type) {
589 case OP_GREEDY: {
590 //TRexNode *greedystop = (node->next != -1) ? &exp->_nodes[node->next] : NULL;
591 TRexNode *greedystop = NULL;
592 int p0 = (node->right >> 16)&0x0000FFFF, p1 = node->right&0x0000FFFF, nmaches = 0;
593 const TRexChar *s=str, *good = str;
595 if(node->next != -1) {
596 greedystop = &exp->_nodes[node->next];
597 }
598 else {
599 greedystop = next;
600 }
602 while((nmaches == 0xFFFF || nmaches < p1)) {
604 const TRexChar *stop;
605 if(!(s = trex_matchnode(exp,&exp->_nodes[node->left],s,greedystop)))
606 break;
607 nmaches++;
608 good=s;
609 if(greedystop) {
610 //checks that 0 matches satisfy the expression(if so skips)
611 //if not would always stop(for instance if is a '?')
612 if(greedystop->type != OP_GREEDY ||
613 (greedystop->type == OP_GREEDY && ((greedystop->right >> 16)&0x0000FFFF) != 0))
614 {
615 TRexNode *gnext = NULL;
616 if(greedystop->next != -1) {
617 gnext = &exp->_nodes[greedystop->next];
618 }else if(next && next->next != -1){
619 gnext = &exp->_nodes[next->next];
620 }
621 stop = trex_matchnode(exp,greedystop,s,gnext);
622 if(stop) {
623 //if satisfied stop it
624 if(p0 == p1 && p0 == nmaches) break;
625 else if(nmaches >= p0 && p1 == 0xFFFF) break;
626 else if(nmaches >= p0 && nmaches <= p1) break;
627 }
628 }
629 }
631 if(s >= exp->_eol)
632 break;
633 }
634 if(p0 == p1 && p0 == nmaches) return good;
635 else if(nmaches >= p0 && p1 == 0xFFFF) return good;
636 else if(nmaches >= p0 && nmaches <= p1) return good;
637 return NULL;
638 }
639 case OP_OR: {
640 const TRexChar *asd = str;
641 TRexNode *temp=&exp->_nodes[node->left];
642 while( (asd = trex_matchnode(exp,temp,asd,NULL)) ) {
643 if(temp->next != -1)
644 temp = &exp->_nodes[temp->next];
645 else
646 return asd;
647 }
648 asd = str;
649 temp = &exp->_nodes[node->right];
650 while( (asd = trex_matchnode(exp,temp,asd,NULL)) ) {
651 if(temp->next != -1)
652 temp = &exp->_nodes[temp->next];
653 else
654 return asd;
655 }
656 return NULL;
657 break;
658 }
659 case OP_EXPR:
660 case OP_NOCAPEXPR:{
661 TRexNode *n = &exp->_nodes[node->left];
662 const TRexChar *cur = str;
663 int capture = -1;
664 if(node->type != OP_NOCAPEXPR && node->right == exp->_currsubexp) {
665 capture = exp->_currsubexp;
666 exp->_matches[capture].begin = cur;
667 exp->_currsubexp++;
668 }
670 do {
671 TRexNode *subnext = NULL;
672 if(n->next != -1) {
673 subnext = &exp->_nodes[n->next];
674 }else {
675 subnext = next;
676 }
677 if(!(cur = trex_matchnode(exp,n,cur,subnext))) {
678 if(capture != -1){
679 exp->_matches[capture].begin = 0;
680 exp->_matches[capture].len = 0;
681 }
682 return NULL;
683 }
684 } while((n->next != -1) && (n = &exp->_nodes[n->next]));
686 if(capture != -1)
687 exp->_matches[capture].len = cur - exp->_matches[capture].begin;
688 return cur;
689 }
690 case OP_WB:
691 if(str == exp->_bol && !isspace(*str)
692 || (str == exp->_eol && !isspace(*(str-1)))
693 || (!isspace(*str) && isspace(*(str+1)))
694 || (isspace(*str) && !isspace(*(str+1))) ) {
695 return (node->left == 'b')?str:NULL;
696 }
697 return (node->left == 'b')?NULL:str;
698 case OP_BOL:
699 if(str == exp->_bol) return str;
700 return NULL;
701 case OP_EOL:
702 if(str == exp->_eol) return str;
703 return NULL;
704 case OP_DOT:{
705 *str++;
706 }
707 return str;
708 case OP_NCLASS:
709 case OP_CLASS:
710 if(trex_matchclass(exp,&exp->_nodes[node->left],*str)?(type == OP_CLASS?TRex_True:TRex_False):(type == OP_NCLASS?TRex_True:TRex_False)) {
711 *str++;
712 return str;
713 }
714 return NULL;
715 case OP_CCLASS:
716 if(trex_matchcclass(node->left,*str)) {
717 *str++;
718 return str;
719 }
720 return NULL;
721 default: /* char */
722 if(*str != node->type) return NULL;
723 *str++;
724 return str;
725 }
726 return NULL;
727 }
729 /* public api */
730 TRex *trex_compile(const TRexChar *pattern,const TRexChar **error)
731 {
732 TRex *exp = (TRex *)malloc(sizeof(TRex));
733 exp->_eol = exp->_bol = NULL;
734 exp->_p = pattern;
735 exp->_nallocated = (int)scstrlen(pattern) * sizeof(TRexChar);
736 exp->_nodes = (TRexNode *)malloc(exp->_nallocated * sizeof(TRexNode));
737 exp->_nsize = 0;
738 exp->_matches = 0;
739 exp->_nsubexpr = 0;
740 exp->_first = trex_newnode(exp,OP_EXPR);
741 exp->_error = error;
742 exp->_jmpbuf = malloc(sizeof(jmp_buf));
743 if(setjmp(*((jmp_buf*)exp->_jmpbuf)) == 0) {
744 int res = trex_list(exp);
745 exp->_nodes[exp->_first].left = res;
746 if(*exp->_p!='\0')
747 trex_error(exp,_SC("unexpected character"));
748 #ifdef _DEBUG
749 {
750 int nsize,i;
751 TRexNode *t;
752 nsize = exp->_nsize;
753 t = &exp->_nodes[0];
754 scprintf(_SC("\n"));
755 for(i = 0;i < nsize; i++) {
756 if(exp->_nodes[i].type>MAX_CHAR)
757 scprintf(_SC("[%02d] %10s "),i,g_nnames[exp->_nodes[i].type-MAX_CHAR]);
758 else
759 scprintf(_SC("[%02d] %10c "),i,exp->_nodes[i].type);
760 scprintf(_SC("left %02d right %02d next %02d\n"),exp->_nodes[i].left,exp->_nodes[i].right,exp->_nodes[i].next);
761 }
762 scprintf(_SC("\n"));
763 }
764 #endif
765 exp->_matches = (TRexMatch *) malloc(exp->_nsubexpr * sizeof(TRexMatch));
766 memset(exp->_matches,0,exp->_nsubexpr * sizeof(TRexMatch));
767 }
768 else{
769 trex_free(exp);
770 return NULL;
771 }
772 return exp;
773 }
775 void trex_free(TRex *exp)
776 {
777 if(exp) {
778 if(exp->_nodes) free(exp->_nodes);
779 if(exp->_jmpbuf) free(exp->_jmpbuf);
780 if(exp->_matches) free(exp->_matches);
781 free(exp);
782 }
783 }
785 TRexBool trex_match(TRex* exp,const TRexChar* text)
786 {
787 const TRexChar* res = NULL;
788 exp->_bol = text;
789 exp->_eol = text + scstrlen(text);
790 exp->_currsubexp = 0;
791 res = trex_matchnode(exp,exp->_nodes,text,NULL);
792 if(res == NULL || res != exp->_eol)
793 return TRex_False;
794 return TRex_True;
795 }
797 TRexBool trex_searchrange(TRex* exp,const TRexChar* text_begin,const TRexChar* text_end,const TRexChar** out_begin, const TRexChar** out_end)
798 {
799 const TRexChar *cur = NULL;
800 int node = exp->_first;
801 if(text_begin >= text_end) return TRex_False;
802 exp->_bol = text_begin;
803 exp->_eol = text_end;
804 do {
805 cur = text_begin;
806 while(node != -1) {
807 exp->_currsubexp = 0;
808 cur = trex_matchnode(exp,&exp->_nodes[node],cur,NULL);
809 if(!cur)
810 break;
811 node = exp->_nodes[node].next;
812 }
813 *text_begin++;
814 } while(cur == NULL && text_begin != text_end);
816 if(cur == NULL)
817 return TRex_False;
819 --text_begin;
821 if(out_begin) *out_begin = text_begin;
822 if(out_end) *out_end = cur;
823 return TRex_True;
824 }
826 TRexBool trex_search(TRex* exp,const TRexChar* text, const TRexChar** out_begin, const TRexChar** out_end)
827 {
828 return trex_searchrange(exp,text,text + scstrlen(text),out_begin,out_end);
829 }
831 int trex_getsubexpcount(TRex* exp)
832 {
833 return exp->_nsubexpr;
834 }
836 TRexBool trex_getsubexp(TRex* exp, int n, TRexMatch *subexp)
837 {
838 if( n<0 || n >= exp->_nsubexpr) return TRex_False;
839 *subexp = exp->_matches[n];
840 return TRex_True;
841 }
844 //########################################################################
845 //########################################################################
846 //## E N D R E G E X P
847 //########################################################################
848 //########################################################################
854 //########################################################################
855 //########################################################################
856 //## X M L
857 //########################################################################
858 //########################################################################
860 // Note: This mini-dom library comes from Pedro, another little project
861 // of mine.
863 typedef std::string String;
864 typedef unsigned int XMLCh;
867 class Namespace
868 {
869 public:
870 Namespace()
871 {}
873 Namespace(const String &prefixArg, const String &namespaceURIArg)
874 {
875 prefix = prefixArg;
876 namespaceURI = namespaceURIArg;
877 }
879 Namespace(const Namespace &other)
880 {
881 assign(other);
882 }
884 Namespace &operator=(const Namespace &other)
885 {
886 assign(other);
887 return *this;
888 }
890 virtual ~Namespace()
891 {}
893 virtual String getPrefix()
894 { return prefix; }
896 virtual String getNamespaceURI()
897 { return namespaceURI; }
899 protected:
901 void assign(const Namespace &other)
902 {
903 prefix = other.prefix;
904 namespaceURI = other.namespaceURI;
905 }
907 String prefix;
908 String namespaceURI;
910 };
912 class Attribute
913 {
914 public:
915 Attribute()
916 {}
918 Attribute(const String &nameArg, const String &valueArg)
919 {
920 name = nameArg;
921 value = valueArg;
922 }
924 Attribute(const Attribute &other)
925 {
926 assign(other);
927 }
929 Attribute &operator=(const Attribute &other)
930 {
931 assign(other);
932 return *this;
933 }
935 virtual ~Attribute()
936 {}
938 virtual String getName()
939 { return name; }
941 virtual String getValue()
942 { return value; }
944 protected:
946 void assign(const Attribute &other)
947 {
948 name = other.name;
949 value = other.value;
950 }
952 String name;
953 String value;
955 };
958 class Element
959 {
960 friend class Parser;
962 public:
963 Element()
964 {
965 init();
966 }
968 Element(const String &nameArg)
969 {
970 init();
971 name = nameArg;
972 }
974 Element(const String &nameArg, const String &valueArg)
975 {
976 init();
977 name = nameArg;
978 value = valueArg;
979 }
981 Element(const Element &other)
982 {
983 assign(other);
984 }
986 Element &operator=(const Element &other)
987 {
988 assign(other);
989 return *this;
990 }
992 virtual Element *clone();
994 virtual ~Element()
995 {
996 for (unsigned int i=0 ; i<children.size() ; i++)
997 delete children[i];
998 }
1000 virtual String getName()
1001 { return name; }
1003 virtual String getValue()
1004 { return value; }
1006 Element *getParent()
1007 { return parent; }
1009 std::vector<Element *> getChildren()
1010 { return children; }
1012 std::vector<Element *> findElements(const String &name);
1014 String getAttribute(const String &name);
1016 std::vector<Attribute> &getAttributes()
1017 { return attributes; }
1019 String getTagAttribute(const String &tagName, const String &attrName);
1021 String getTagValue(const String &tagName);
1023 void addChild(Element *child);
1025 void addAttribute(const String &name, const String &value);
1027 void addNamespace(const String &prefix, const String &namespaceURI);
1030 /**
1031 * Prettyprint an XML tree to an output stream. Elements are indented
1032 * according to element hierarchy.
1033 * @param f a stream to receive the output
1034 * @param elem the element to output
1035 */
1036 void writeIndented(FILE *f);
1038 /**
1039 * Prettyprint an XML tree to standard output. This is the equivalent of
1040 * writeIndented(stdout).
1041 * @param elem the element to output
1042 */
1043 void print();
1045 int getLine()
1046 { return line; }
1048 protected:
1050 void init()
1051 {
1052 parent = NULL;
1053 line = 0;
1054 }
1056 void assign(const Element &other)
1057 {
1058 parent = other.parent;
1059 children = other.children;
1060 attributes = other.attributes;
1061 namespaces = other.namespaces;
1062 name = other.name;
1063 value = other.value;
1064 line = other.line;
1065 }
1067 void findElementsRecursive(std::vector<Element *>&res, const String &name);
1069 void writeIndentedRecursive(FILE *f, int indent);
1071 Element *parent;
1073 std::vector<Element *>children;
1075 std::vector<Attribute> attributes;
1076 std::vector<Namespace> namespaces;
1078 String name;
1079 String value;
1081 int line;
1082 };
1088 class Parser
1089 {
1090 public:
1091 /**
1092 * Constructor
1093 */
1094 Parser()
1095 { init(); }
1097 virtual ~Parser()
1098 {}
1100 /**
1101 * Parse XML in a char buffer.
1102 * @param buf a character buffer to parse
1103 * @param pos position to start parsing
1104 * @param len number of chars, from pos, to parse.
1105 * @return a pointer to the root of the XML document;
1106 */
1107 Element *parse(const char *buf,int pos,int len);
1109 /**
1110 * Parse XML in a char buffer.
1111 * @param buf a character buffer to parse
1112 * @param pos position to start parsing
1113 * @param len number of chars, from pos, to parse.
1114 * @return a pointer to the root of the XML document;
1115 */
1116 Element *parse(const String &buf);
1118 /**
1119 * Parse a named XML file. The file is loaded like a data file;
1120 * the original format is not preserved.
1121 * @param fileName the name of the file to read
1122 * @return a pointer to the root of the XML document;
1123 */
1124 Element *parseFile(const String &fileName);
1126 /**
1127 * Utility method to preprocess a string for XML
1128 * output, escaping its entities.
1129 * @param str the string to encode
1130 */
1131 static String encode(const String &str);
1133 /**
1134 * Removes whitespace from beginning and end of a string
1135 */
1136 String trim(const String &s);
1138 private:
1140 void init()
1141 {
1142 keepGoing = true;
1143 currentNode = NULL;
1144 parselen = 0;
1145 parsebuf = NULL;
1146 currentPosition = 0;
1147 }
1149 int countLines(int begin, int end);
1151 void getLineAndColumn(int pos, int *lineNr, int *colNr);
1153 void error(const char *fmt, ...);
1155 int peek(int pos);
1157 int match(int pos, const char *text);
1159 int skipwhite(int p);
1161 int getWord(int p0, String &buf);
1163 int getQuoted(int p0, String &buf, int do_i_parse);
1165 int parseVersion(int p0);
1167 int parseDoctype(int p0);
1169 int parseElement(int p0, Element *par,int depth);
1171 Element *parse(XMLCh *buf,int pos,int len);
1173 bool keepGoing;
1174 Element *currentNode;
1175 int parselen;
1176 XMLCh *parsebuf;
1177 String cdatabuf;
1178 int currentPosition;
1179 };
1184 //########################################################################
1185 //# E L E M E N T
1186 //########################################################################
1188 Element *Element::clone()
1189 {
1190 Element *elem = new Element(name, value);
1191 elem->parent = parent;
1192 elem->attributes = attributes;
1193 elem->namespaces = namespaces;
1194 elem->line = line;
1196 std::vector<Element *>::iterator iter;
1197 for (iter = children.begin(); iter != children.end() ; iter++)
1198 {
1199 elem->addChild((*iter)->clone());
1200 }
1201 return elem;
1202 }
1205 void Element::findElementsRecursive(std::vector<Element *>&res, const String &name)
1206 {
1207 if (getName() == name)
1208 {
1209 res.push_back(this);
1210 }
1211 for (unsigned int i=0; i<children.size() ; i++)
1212 children[i]->findElementsRecursive(res, name);
1213 }
1215 std::vector<Element *> Element::findElements(const String &name)
1216 {
1217 std::vector<Element *> res;
1218 findElementsRecursive(res, name);
1219 return res;
1220 }
1222 String Element::getAttribute(const String &name)
1223 {
1224 for (unsigned int i=0 ; i<attributes.size() ; i++)
1225 if (attributes[i].getName() ==name)
1226 return attributes[i].getValue();
1227 return "";
1228 }
1230 String Element::getTagAttribute(const String &tagName, const String &attrName)
1231 {
1232 std::vector<Element *>elems = findElements(tagName);
1233 if (elems.size() <1)
1234 return "";
1235 String res = elems[0]->getAttribute(attrName);
1236 return res;
1237 }
1239 String Element::getTagValue(const String &tagName)
1240 {
1241 std::vector<Element *>elems = findElements(tagName);
1242 if (elems.size() <1)
1243 return "";
1244 String res = elems[0]->getValue();
1245 return res;
1246 }
1248 void Element::addChild(Element *child)
1249 {
1250 if (!child)
1251 return;
1252 child->parent = this;
1253 children.push_back(child);
1254 }
1257 void Element::addAttribute(const String &name, const String &value)
1258 {
1259 Attribute attr(name, value);
1260 attributes.push_back(attr);
1261 }
1263 void Element::addNamespace(const String &prefix, const String &namespaceURI)
1264 {
1265 Namespace ns(prefix, namespaceURI);
1266 namespaces.push_back(ns);
1267 }
1269 void Element::writeIndentedRecursive(FILE *f, int indent)
1270 {
1271 int i;
1272 if (!f)
1273 return;
1274 //Opening tag, and attributes
1275 for (i=0;i<indent;i++)
1276 fputc(' ',f);
1277 fprintf(f,"<%s",name.c_str());
1278 for (unsigned int i=0 ; i<attributes.size() ; i++)
1279 {
1280 fprintf(f," %s=\"%s\"",
1281 attributes[i].getName().c_str(),
1282 attributes[i].getValue().c_str());
1283 }
1284 for (unsigned int i=0 ; i<namespaces.size() ; i++)
1285 {
1286 fprintf(f," xmlns:%s=\"%s\"",
1287 namespaces[i].getPrefix().c_str(),
1288 namespaces[i].getNamespaceURI().c_str());
1289 }
1290 fprintf(f,">\n");
1292 //Between the tags
1293 if (value.size() > 0)
1294 {
1295 for (int i=0;i<indent;i++)
1296 fputc(' ', f);
1297 fprintf(f," %s\n", value.c_str());
1298 }
1300 for (unsigned int i=0 ; i<children.size() ; i++)
1301 children[i]->writeIndentedRecursive(f, indent+2);
1303 //Closing tag
1304 for (int i=0; i<indent; i++)
1305 fputc(' ',f);
1306 fprintf(f,"</%s>\n", name.c_str());
1307 }
1309 void Element::writeIndented(FILE *f)
1310 {
1311 writeIndentedRecursive(f, 0);
1312 }
1314 void Element::print()
1315 {
1316 writeIndented(stdout);
1317 }
1320 //########################################################################
1321 //# P A R S E R
1322 //########################################################################
1326 typedef struct
1327 {
1328 const char *escaped;
1329 char value;
1330 } EntityEntry;
1332 static EntityEntry entities[] =
1333 {
1334 { "&" , '&' },
1335 { "<" , '<' },
1336 { ">" , '>' },
1337 { "'", '\'' },
1338 { """, '"' },
1339 { NULL , '\0' }
1340 };
1344 /**
1345 * Removes whitespace from beginning and end of a string
1346 */
1347 String Parser::trim(const String &s)
1348 {
1349 if (s.size() < 1)
1350 return s;
1352 //Find first non-ws char
1353 unsigned int begin = 0;
1354 for ( ; begin < s.size() ; begin++)
1355 {
1356 if (!isspace(s[begin]))
1357 break;
1358 }
1360 //Find first non-ws char, going in reverse
1361 unsigned int end = s.size() - 1;
1362 for ( ; end > begin ; end--)
1363 {
1364 if (!isspace(s[end]))
1365 break;
1366 }
1367 //trace("begin:%d end:%d", begin, end);
1369 String res = s.substr(begin, end-begin+1);
1370 return res;
1371 }
1374 int Parser::countLines(int begin, int end)
1375 {
1376 int count = 0;
1377 for (int i=begin ; i<end ; i++)
1378 {
1379 XMLCh ch = parsebuf[i];
1380 if (ch == '\n' || ch == '\r')
1381 count++;
1382 }
1383 return count;
1384 }
1387 void Parser::getLineAndColumn(int pos, int *lineNr, int *colNr)
1388 {
1389 int line = 1;
1390 int col = 1;
1391 for (long i=0 ; i<pos ; i++)
1392 {
1393 XMLCh ch = parsebuf[i];
1394 if (ch == '\n' || ch == '\r')
1395 {
1396 col = 0;
1397 line ++;
1398 }
1399 else
1400 col++;
1401 }
1402 *lineNr = line;
1403 *colNr = col;
1405 }
1408 void Parser::error(const char *fmt, ...)
1409 {
1410 int lineNr;
1411 int colNr;
1412 getLineAndColumn(currentPosition, &lineNr, &colNr);
1413 va_list args;
1414 fprintf(stderr, "xml error at line %d, column %d:", lineNr, colNr);
1415 va_start(args,fmt);
1416 vfprintf(stderr,fmt,args);
1417 va_end(args) ;
1418 fprintf(stderr, "\n");
1419 }
1423 int Parser::peek(int pos)
1424 {
1425 if (pos >= parselen)
1426 return -1;
1427 currentPosition = pos;
1428 int ch = parsebuf[pos];
1429 //printf("ch:%c\n", ch);
1430 return ch;
1431 }
1435 String Parser::encode(const String &str)
1436 {
1437 String ret;
1438 for (unsigned int i=0 ; i<str.size() ; i++)
1439 {
1440 XMLCh ch = (XMLCh)str[i];
1441 if (ch == '&')
1442 ret.append("&");
1443 else if (ch == '<')
1444 ret.append("<");
1445 else if (ch == '>')
1446 ret.append(">");
1447 else if (ch == '\'')
1448 ret.append("'");
1449 else if (ch == '"')
1450 ret.append(""");
1451 else
1452 ret.push_back(ch);
1454 }
1455 return ret;
1456 }
1459 int Parser::match(int p0, const char *text)
1460 {
1461 int p = p0;
1462 while (*text)
1463 {
1464 if (peek(p) != *text)
1465 return p0;
1466 p++; text++;
1467 }
1468 return p;
1469 }
1473 int Parser::skipwhite(int p)
1474 {
1476 while (p<parselen)
1477 {
1478 int p2 = match(p, "<!--");
1479 if (p2 > p)
1480 {
1481 p = p2;
1482 while (p<parselen)
1483 {
1484 p2 = match(p, "-->");
1485 if (p2 > p)
1486 {
1487 p = p2;
1488 break;
1489 }
1490 p++;
1491 }
1492 }
1493 XMLCh b = peek(p);
1494 if (!isspace(b))
1495 break;
1496 p++;
1497 }
1498 return p;
1499 }
1501 /* modify this to allow all chars for an element or attribute name*/
1502 int Parser::getWord(int p0, String &buf)
1503 {
1504 int p = p0;
1505 while (p<parselen)
1506 {
1507 XMLCh b = peek(p);
1508 if (b<=' ' || b=='/' || b=='>' || b=='=')
1509 break;
1510 buf.push_back(b);
1511 p++;
1512 }
1513 return p;
1514 }
1516 int Parser::getQuoted(int p0, String &buf, int do_i_parse)
1517 {
1519 int p = p0;
1520 if (peek(p) != '"' && peek(p) != '\'')
1521 return p0;
1522 p++;
1524 while ( p<parselen )
1525 {
1526 XMLCh b = peek(p);
1527 if (b=='"' || b=='\'')
1528 break;
1529 if (b=='&' && do_i_parse)
1530 {
1531 bool found = false;
1532 for (EntityEntry *ee = entities ; ee->value ; ee++)
1533 {
1534 int p2 = match(p, ee->escaped);
1535 if (p2>p)
1536 {
1537 buf.push_back(ee->value);
1538 p = p2;
1539 found = true;
1540 break;
1541 }
1542 }
1543 if (!found)
1544 {
1545 error("unterminated entity");
1546 return false;
1547 }
1548 }
1549 else
1550 {
1551 buf.push_back(b);
1552 p++;
1553 }
1554 }
1555 return p;
1556 }
1558 int Parser::parseVersion(int p0)
1559 {
1560 //printf("### parseVersion: %d\n", p0);
1562 int p = p0;
1564 p = skipwhite(p0);
1566 if (peek(p) != '<')
1567 return p0;
1569 p++;
1570 if (p>=parselen || peek(p)!='?')
1571 return p0;
1573 p++;
1575 String buf;
1577 while (p<parselen)
1578 {
1579 XMLCh ch = peek(p);
1580 if (ch=='?')
1581 {
1582 p++;
1583 break;
1584 }
1585 buf.push_back(ch);
1586 p++;
1587 }
1589 if (peek(p) != '>')
1590 return p0;
1591 p++;
1593 //printf("Got version:%s\n",buf.c_str());
1594 return p;
1595 }
1597 int Parser::parseDoctype(int p0)
1598 {
1599 //printf("### parseDoctype: %d\n", p0);
1601 int p = p0;
1602 p = skipwhite(p);
1604 if (p>=parselen || peek(p)!='<')
1605 return p0;
1607 p++;
1609 if (peek(p)!='!' || peek(p+1)=='-')
1610 return p0;
1611 p++;
1613 String buf;
1614 while (p<parselen)
1615 {
1616 XMLCh ch = peek(p);
1617 if (ch=='>')
1618 {
1619 p++;
1620 break;
1621 }
1622 buf.push_back(ch);
1623 p++;
1624 }
1626 //printf("Got doctype:%s\n",buf.c_str());
1627 return p;
1628 }
1632 int Parser::parseElement(int p0, Element *par,int lineNr)
1633 {
1635 int p = p0;
1637 int p2 = p;
1639 p = skipwhite(p);
1641 //## Get open tag
1642 XMLCh ch = peek(p);
1643 if (ch!='<')
1644 return p0;
1646 //int line, col;
1647 //getLineAndColumn(p, &line, &col);
1649 p++;
1651 String openTagName;
1652 p = skipwhite(p);
1653 p = getWord(p, openTagName);
1654 //printf("####tag :%s\n", openTagName.c_str());
1655 p = skipwhite(p);
1657 //Add element to tree
1658 Element *n = new Element(openTagName);
1659 n->line = lineNr + countLines(p0, p);
1660 n->parent = par;
1661 par->addChild(n);
1663 // Get attributes
1664 if (peek(p) != '>')
1665 {
1666 while (p<parselen)
1667 {
1668 p = skipwhite(p);
1669 ch = peek(p);
1670 //printf("ch:%c\n",ch);
1671 if (ch=='>')
1672 break;
1673 else if (ch=='/' && p<parselen+1)
1674 {
1675 p++;
1676 p = skipwhite(p);
1677 ch = peek(p);
1678 if (ch=='>')
1679 {
1680 p++;
1681 //printf("quick close\n");
1682 return p;
1683 }
1684 }
1685 String attrName;
1686 p2 = getWord(p, attrName);
1687 if (p2==p)
1688 break;
1689 //printf("name:%s",buf);
1690 p=p2;
1691 p = skipwhite(p);
1692 ch = peek(p);
1693 //printf("ch:%c\n",ch);
1694 if (ch!='=')
1695 break;
1696 p++;
1697 p = skipwhite(p);
1698 // ch = parsebuf[p];
1699 // printf("ch:%c\n",ch);
1700 String attrVal;
1701 p2 = getQuoted(p, attrVal, true);
1702 p=p2+1;
1703 //printf("name:'%s' value:'%s'\n",attrName.c_str(),attrVal.c_str());
1704 char *namestr = (char *)attrName.c_str();
1705 if (strncmp(namestr, "xmlns:", 6)==0)
1706 n->addNamespace(attrName, attrVal);
1707 else
1708 n->addAttribute(attrName, attrVal);
1709 }
1710 }
1712 bool cdata = false;
1714 p++;
1715 // ### Get intervening data ### */
1716 String data;
1717 while (p<parselen)
1718 {
1719 //# COMMENT
1720 p2 = match(p, "<!--");
1721 if (!cdata && p2>p)
1722 {
1723 p = p2;
1724 while (p<parselen)
1725 {
1726 p2 = match(p, "-->");
1727 if (p2 > p)
1728 {
1729 p = p2;
1730 break;
1731 }
1732 p++;
1733 }
1734 }
1736 ch = peek(p);
1737 //# END TAG
1738 if (ch=='<' && !cdata && peek(p+1)=='/')
1739 {
1740 break;
1741 }
1742 //# CDATA
1743 p2 = match(p, "<![CDATA[");
1744 if (p2 > p)
1745 {
1746 cdata = true;
1747 p = p2;
1748 continue;
1749 }
1751 //# CHILD ELEMENT
1752 if (ch == '<')
1753 {
1754 p2 = parseElement(p, n, lineNr + countLines(p0, p));
1755 if (p2 == p)
1756 {
1757 /*
1758 printf("problem on element:%s. p2:%d p:%d\n",
1759 openTagName.c_str(), p2, p);
1760 */
1761 return p0;
1762 }
1763 p = p2;
1764 continue;
1765 }
1766 //# ENTITY
1767 if (ch=='&' && !cdata)
1768 {
1769 bool found = false;
1770 for (EntityEntry *ee = entities ; ee->value ; ee++)
1771 {
1772 int p2 = match(p, ee->escaped);
1773 if (p2>p)
1774 {
1775 data.push_back(ee->value);
1776 p = p2;
1777 found = true;
1778 break;
1779 }
1780 }
1781 if (!found)
1782 {
1783 error("unterminated entity");
1784 return -1;
1785 }
1786 continue;
1787 }
1789 //# NONE OF THE ABOVE
1790 data.push_back(ch);
1791 p++;
1792 }/*while*/
1795 n->value = data;
1796 //printf("%d : data:%s\n",p,data.c_str());
1798 //## Get close tag
1799 p = skipwhite(p);
1800 ch = peek(p);
1801 if (ch != '<')
1802 {
1803 error("no < for end tag\n");
1804 return p0;
1805 }
1806 p++;
1807 ch = peek(p);
1808 if (ch != '/')
1809 {
1810 error("no / on end tag");
1811 return p0;
1812 }
1813 p++;
1814 ch = peek(p);
1815 p = skipwhite(p);
1816 String closeTagName;
1817 p = getWord(p, closeTagName);
1818 if (openTagName != closeTagName)
1819 {
1820 error("Mismatched closing tag. Expected </%S>. Got '%S'.",
1821 openTagName.c_str(), closeTagName.c_str());
1822 return p0;
1823 }
1824 p = skipwhite(p);
1825 if (peek(p) != '>')
1826 {
1827 error("no > on end tag for '%s'", closeTagName.c_str());
1828 return p0;
1829 }
1830 p++;
1831 // printf("close element:%s\n",closeTagName.c_str());
1832 p = skipwhite(p);
1833 return p;
1834 }
1839 Element *Parser::parse(XMLCh *buf,int pos,int len)
1840 {
1841 parselen = len;
1842 parsebuf = buf;
1843 Element *rootNode = new Element("root");
1844 pos = parseVersion(pos);
1845 pos = parseDoctype(pos);
1846 pos = parseElement(pos, rootNode, 1);
1847 return rootNode;
1848 }
1851 Element *Parser::parse(const char *buf, int pos, int len)
1852 {
1853 XMLCh *charbuf = new XMLCh[len + 1];
1854 long i = 0;
1855 for ( ; i < len ; i++)
1856 charbuf[i] = (XMLCh)buf[i];
1857 charbuf[i] = '\0';
1859 Element *n = parse(charbuf, pos, len);
1860 delete[] charbuf;
1861 return n;
1862 }
1864 Element *Parser::parse(const String &buf)
1865 {
1866 long len = (long)buf.size();
1867 XMLCh *charbuf = new XMLCh[len + 1];
1868 long i = 0;
1869 for ( ; i < len ; i++)
1870 charbuf[i] = (XMLCh)buf[i];
1871 charbuf[i] = '\0';
1873 Element *n = parse(charbuf, 0, len);
1874 delete[] charbuf;
1875 return n;
1876 }
1878 Element *Parser::parseFile(const String &fileName)
1879 {
1881 //##### LOAD INTO A CHAR BUF, THEN CONVERT TO XMLCh
1882 FILE *f = fopen(fileName.c_str(), "rb");
1883 if (!f)
1884 return NULL;
1886 struct stat statBuf;
1887 if (fstat(fileno(f),&statBuf)<0)
1888 {
1889 fclose(f);
1890 return NULL;
1891 }
1892 long filelen = statBuf.st_size;
1894 //printf("length:%d\n",filelen);
1895 XMLCh *charbuf = new XMLCh[filelen + 1];
1896 for (XMLCh *p=charbuf ; !feof(f) ; p++)
1897 {
1898 *p = (XMLCh)fgetc(f);
1899 }
1900 fclose(f);
1901 charbuf[filelen] = '\0';
1904 /*
1905 printf("nrbytes:%d\n",wc_count);
1906 printf("buf:%ls\n======\n",charbuf);
1907 */
1908 Element *n = parse(charbuf, 0, filelen);
1909 delete[] charbuf;
1910 return n;
1911 }
1913 //########################################################################
1914 //########################################################################
1915 //## E N D X M L
1916 //########################################################################
1917 //########################################################################
1924 //########################################################################
1925 //########################################################################
1926 //## U R I
1927 //########################################################################
1928 //########################################################################
1930 //This would normally be a call to a UNICODE function
1931 #define isLetter(x) isalpha(x)
1933 /**
1934 * A class that implements the W3C URI resource reference.
1935 */
1936 class URI
1937 {
1938 public:
1940 typedef enum
1941 {
1942 SCHEME_NONE =0,
1943 SCHEME_DATA,
1944 SCHEME_HTTP,
1945 SCHEME_HTTPS,
1946 SCHEME_FTP,
1947 SCHEME_FILE,
1948 SCHEME_LDAP,
1949 SCHEME_MAILTO,
1950 SCHEME_NEWS,
1951 SCHEME_TELNET
1952 } SchemeTypes;
1954 /**
1955 *
1956 */
1957 URI()
1958 {
1959 init();
1960 }
1962 /**
1963 *
1964 */
1965 URI(const String &str)
1966 {
1967 init();
1968 parse(str);
1969 }
1972 /**
1973 *
1974 */
1975 URI(const char *str)
1976 {
1977 init();
1978 String domStr = str;
1979 parse(domStr);
1980 }
1983 /**
1984 *
1985 */
1986 URI(const URI &other)
1987 {
1988 init();
1989 assign(other);
1990 }
1993 /**
1994 *
1995 */
1996 URI &operator=(const URI &other)
1997 {
1998 init();
1999 assign(other);
2000 return *this;
2001 }
2004 /**
2005 *
2006 */
2007 virtual ~URI()
2008 {}
2012 /**
2013 *
2014 */
2015 virtual bool parse(const String &str);
2017 /**
2018 *
2019 */
2020 virtual String toString() const;
2022 /**
2023 *
2024 */
2025 virtual int getScheme() const;
2027 /**
2028 *
2029 */
2030 virtual String getSchemeStr() const;
2032 /**
2033 *
2034 */
2035 virtual String getAuthority() const;
2037 /**
2038 * Same as getAuthority, but if the port has been specified
2039 * as host:port , the port will not be included
2040 */
2041 virtual String getHost() const;
2043 /**
2044 *
2045 */
2046 virtual int getPort() const;
2048 /**
2049 *
2050 */
2051 virtual String getPath() const;
2053 /**
2054 *
2055 */
2056 virtual String getNativePath() const;
2058 /**
2059 *
2060 */
2061 virtual bool isAbsolute() const;
2063 /**
2064 *
2065 */
2066 virtual bool isOpaque() const;
2068 /**
2069 *
2070 */
2071 virtual String getQuery() const;
2073 /**
2074 *
2075 */
2076 virtual String getFragment() const;
2078 /**
2079 *
2080 */
2081 virtual URI resolve(const URI &other) const;
2083 /**
2084 *
2085 */
2086 virtual void normalize();
2088 private:
2090 /**
2091 *
2092 */
2093 void init()
2094 {
2095 parsebuf = NULL;
2096 parselen = 0;
2097 scheme = SCHEME_NONE;
2098 schemeStr = "";
2099 port = 0;
2100 authority = "";
2101 path = "";
2102 absolute = false;
2103 opaque = false;
2104 query = "";
2105 fragment = "";
2106 }
2109 /**
2110 *
2111 */
2112 void assign(const URI &other)
2113 {
2114 scheme = other.scheme;
2115 schemeStr = other.schemeStr;
2116 authority = other.authority;
2117 port = other.port;
2118 path = other.path;
2119 absolute = other.absolute;
2120 opaque = other.opaque;
2121 query = other.query;
2122 fragment = other.fragment;
2123 }
2125 int scheme;
2127 String schemeStr;
2129 String authority;
2131 bool portSpecified;
2133 int port;
2135 String path;
2137 bool absolute;
2139 bool opaque;
2141 String query;
2143 String fragment;
2145 void error(const char *fmt, ...);
2147 void trace(const char *fmt, ...);
2150 int peek(int p);
2152 int match(int p, const char *key);
2154 int parseScheme(int p);
2156 int parseHierarchicalPart(int p0);
2158 int parseQuery(int p0);
2160 int parseFragment(int p0);
2162 int parse(int p);
2164 char *parsebuf;
2166 int parselen;
2168 };
2172 typedef struct
2173 {
2174 int ival;
2175 const char *sval;
2176 int port;
2177 } LookupEntry;
2179 LookupEntry schemes[] =
2180 {
2181 { URI::SCHEME_DATA, "data:", 0 },
2182 { URI::SCHEME_HTTP, "http:", 80 },
2183 { URI::SCHEME_HTTPS, "https:", 443 },
2184 { URI::SCHEME_FTP, "ftp", 12 },
2185 { URI::SCHEME_FILE, "file:", 0 },
2186 { URI::SCHEME_LDAP, "ldap:", 123 },
2187 { URI::SCHEME_MAILTO, "mailto:", 25 },
2188 { URI::SCHEME_NEWS, "news:", 117 },
2189 { URI::SCHEME_TELNET, "telnet:", 23 },
2190 { 0, NULL, 0 }
2191 };
2194 String URI::toString() const
2195 {
2196 String str = schemeStr;
2197 if (authority.size() > 0)
2198 {
2199 str.append("//");
2200 str.append(authority);
2201 }
2202 str.append(path);
2203 if (query.size() > 0)
2204 {
2205 str.append("?");
2206 str.append(query);
2207 }
2208 if (fragment.size() > 0)
2209 {
2210 str.append("#");
2211 str.append(fragment);
2212 }
2213 return str;
2214 }
2217 int URI::getScheme() const
2218 {
2219 return scheme;
2220 }
2222 String URI::getSchemeStr() const
2223 {
2224 return schemeStr;
2225 }
2228 String URI::getAuthority() const
2229 {
2230 String ret = authority;
2231 if (portSpecified && port>=0)
2232 {
2233 char buf[7];
2234 snprintf(buf, 6, ":%6d", port);
2235 ret.append(buf);
2236 }
2237 return ret;
2238 }
2240 String URI::getHost() const
2241 {
2242 return authority;
2243 }
2245 int URI::getPort() const
2246 {
2247 return port;
2248 }
2251 String URI::getPath() const
2252 {
2253 return path;
2254 }
2256 String URI::getNativePath() const
2257 {
2258 String npath;
2259 #ifdef __WIN32__
2260 unsigned int firstChar = 0;
2261 if (path.size() >= 3)
2262 {
2263 if (path[0] == '/' &&
2264 isLetter(path[1]) &&
2265 path[2] == ':')
2266 firstChar++;
2267 }
2268 for (unsigned int i=firstChar ; i<path.size() ; i++)
2269 {
2270 XMLCh ch = (XMLCh) path[i];
2271 if (ch == '/')
2272 npath.push_back((XMLCh)'\\');
2273 else
2274 npath.push_back(ch);
2275 }
2276 #else
2277 npath = path;
2278 #endif
2279 return npath;
2280 }
2283 bool URI::isAbsolute() const
2284 {
2285 return absolute;
2286 }
2288 bool URI::isOpaque() const
2289 {
2290 return opaque;
2291 }
2294 String URI::getQuery() const
2295 {
2296 return query;
2297 }
2300 String URI::getFragment() const
2301 {
2302 return fragment;
2303 }
2306 URI URI::resolve(const URI &other) const
2307 {
2308 //### According to w3c, this is handled in 3 cases
2310 //## 1
2311 if (opaque || other.isAbsolute())
2312 return other;
2314 //## 2
2315 if (other.fragment.size() > 0 &&
2316 other.path.size() == 0 &&
2317 other.scheme == SCHEME_NONE &&
2318 other.authority.size() == 0 &&
2319 other.query.size() == 0 )
2320 {
2321 URI fragUri = *this;
2322 fragUri.fragment = other.fragment;
2323 return fragUri;
2324 }
2326 //## 3 http://www.ietf.org/rfc/rfc2396.txt, section 5.2
2327 URI newUri;
2328 //# 3.1
2329 newUri.scheme = scheme;
2330 newUri.schemeStr = schemeStr;
2331 newUri.query = other.query;
2332 newUri.fragment = other.fragment;
2333 if (other.authority.size() > 0)
2334 {
2335 //# 3.2
2336 if (absolute || other.absolute)
2337 newUri.absolute = true;
2338 newUri.authority = other.authority;
2339 newUri.port = other.port;//part of authority
2340 newUri.path = other.path;
2341 }
2342 else
2343 {
2344 //# 3.3
2345 if (other.absolute)
2346 {
2347 newUri.absolute = true;
2348 newUri.path = other.path;
2349 }
2350 else
2351 {
2352 unsigned int pos = path.find_last_of('/');
2353 if (pos != path.npos)
2354 {
2355 String tpath = path.substr(0, pos+1);
2356 tpath.append(other.path);
2357 newUri.path = tpath;
2358 }
2359 else
2360 newUri.path = other.path;
2361 }
2362 }
2364 newUri.normalize();
2365 return newUri;
2366 }
2370 /**
2371 * This follows the Java URI algorithm:
2372 * 1. All "." segments are removed.
2373 * 2. If a ".." segment is preceded by a non-".." segment
2374 * then both of these segments are removed. This step
2375 * is repeated until it is no longer applicable.
2376 * 3. If the path is relative, and if its first segment
2377 * contains a colon character (':'), then a "." segment
2378 * is prepended. This prevents a relative URI with a path
2379 * such as "a:b/c/d" from later being re-parsed as an
2380 * opaque URI with a scheme of "a" and a scheme-specific
2381 * part of "b/c/d". (Deviation from RFC 2396)
2382 */
2383 void URI::normalize()
2384 {
2385 std::vector<String> segments;
2387 //## Collect segments
2388 if (path.size()<2)
2389 return;
2390 bool abs = false;
2391 unsigned int pos=0;
2392 if (path[0]=='/')
2393 {
2394 abs = true;
2395 pos++;
2396 }
2397 while (pos < path.size())
2398 {
2399 unsigned int pos2 = path.find('/', pos);
2400 if (pos2==path.npos)
2401 {
2402 String seg = path.substr(pos);
2403 //printf("last segment:%s\n", seg.c_str());
2404 segments.push_back(seg);
2405 break;
2406 }
2407 if (pos2>pos)
2408 {
2409 String seg = path.substr(pos, pos2-pos);
2410 //printf("segment:%s\n", seg.c_str());
2411 segments.push_back(seg);
2412 }
2413 pos = pos2;
2414 pos++;
2415 }
2417 //## Clean up (normalize) segments
2418 bool edited = false;
2419 std::vector<String>::iterator iter;
2420 for (iter=segments.begin() ; iter!=segments.end() ; )
2421 {
2422 String s = *iter;
2423 if (s == ".")
2424 {
2425 iter = segments.erase(iter);
2426 edited = true;
2427 }
2428 else if (s == ".." &&
2429 iter != segments.begin() &&
2430 *(iter-1) != "..")
2431 {
2432 iter--; //back up, then erase two entries
2433 iter = segments.erase(iter);
2434 iter = segments.erase(iter);
2435 edited = true;
2436 }
2437 else
2438 iter++;
2439 }
2441 //## Rebuild path, if necessary
2442 if (edited)
2443 {
2444 path.clear();
2445 if (abs)
2446 {
2447 path.append("/");
2448 }
2449 std::vector<String>::iterator iter;
2450 for (iter=segments.begin() ; iter!=segments.end() ; iter++)
2451 {
2452 if (iter != segments.begin())
2453 path.append("/");
2454 path.append(*iter);
2455 }
2456 }
2458 }
2462 //#########################################################################
2463 //# M E S S A G E S
2464 //#########################################################################
2466 void URI::error(const char *fmt, ...)
2467 {
2468 va_list args;
2469 fprintf(stderr, "URI error: ");
2470 va_start(args, fmt);
2471 vfprintf(stderr, fmt, args);
2472 va_end(args);
2473 fprintf(stderr, "\n");
2474 }
2476 void URI::trace(const char *fmt, ...)
2477 {
2478 va_list args;
2479 fprintf(stdout, "URI: ");
2480 va_start(args, fmt);
2481 vfprintf(stdout, fmt, args);
2482 va_end(args);
2483 fprintf(stdout, "\n");
2484 }
2489 //#########################################################################
2490 //# P A R S I N G
2491 //#########################################################################
2495 int URI::peek(int p)
2496 {
2497 if (p<0 || p>=parselen)
2498 return -1;
2499 return parsebuf[p];
2500 }
2504 int URI::match(int p0, const char *key)
2505 {
2506 int p = p0;
2507 while (p < parselen)
2508 {
2509 if (*key == '\0')
2510 return p;
2511 else if (*key != parsebuf[p])
2512 break;
2513 p++; key++;
2514 }
2515 return p0;
2516 }
2518 //#########################################################################
2519 //# Parsing is performed according to:
2520 //# http://www.gbiv.com/protocols/uri/rfc/rfc3986.html#components
2521 //#########################################################################
2523 int URI::parseScheme(int p0)
2524 {
2525 int p = p0;
2526 for (LookupEntry *entry = schemes; entry->sval ; entry++)
2527 {
2528 int p2 = match(p, entry->sval);
2529 if (p2 > p)
2530 {
2531 schemeStr = entry->sval;
2532 scheme = entry->ival;
2533 port = entry->port;
2534 p = p2;
2535 return p;
2536 }
2537 }
2539 return p;
2540 }
2543 int URI::parseHierarchicalPart(int p0)
2544 {
2545 int p = p0;
2546 int ch;
2548 //# Authority field (host and port, for example)
2549 int p2 = match(p, "//");
2550 if (p2 > p)
2551 {
2552 p = p2;
2553 portSpecified = false;
2554 String portStr;
2555 while (p < parselen)
2556 {
2557 ch = peek(p);
2558 if (ch == '/')
2559 break;
2560 else if (ch == ':')
2561 portSpecified = true;
2562 else if (portSpecified)
2563 portStr.push_back((XMLCh)ch);
2564 else
2565 authority.push_back((XMLCh)ch);
2566 p++;
2567 }
2568 if (portStr.size() > 0)
2569 {
2570 char *pstr = (char *)portStr.c_str();
2571 char *endStr;
2572 long val = strtol(pstr, &endStr, 10);
2573 if (endStr > pstr) //successful parse?
2574 port = val;
2575 }
2576 }
2578 //# Are we absolute?
2579 ch = peek(p);
2580 if (isLetter(ch) && peek(p+1)==':')
2581 {
2582 absolute = true;
2583 path.push_back((XMLCh)'/');
2584 }
2585 else if (ch == '/')
2586 {
2587 absolute = true;
2588 if (p>p0) //in other words, if '/' is not the first char
2589 opaque = true;
2590 path.push_back((XMLCh)ch);
2591 p++;
2592 }
2594 while (p < parselen)
2595 {
2596 ch = peek(p);
2597 if (ch == '?' || ch == '#')
2598 break;
2599 path.push_back((XMLCh)ch);
2600 p++;
2601 }
2603 return p;
2604 }
2606 int URI::parseQuery(int p0)
2607 {
2608 int p = p0;
2609 int ch = peek(p);
2610 if (ch != '?')
2611 return p0;
2613 p++;
2614 while (p < parselen)
2615 {
2616 ch = peek(p);
2617 if (ch == '#')
2618 break;
2619 query.push_back((XMLCh)ch);
2620 p++;
2621 }
2624 return p;
2625 }
2627 int URI::parseFragment(int p0)
2628 {
2630 int p = p0;
2631 int ch = peek(p);
2632 if (ch != '#')
2633 return p0;
2635 p++;
2636 while (p < parselen)
2637 {
2638 ch = peek(p);
2639 if (ch == '?')
2640 break;
2641 fragment.push_back((XMLCh)ch);
2642 p++;
2643 }
2646 return p;
2647 }
2650 int URI::parse(int p0)
2651 {
2653 int p = p0;
2655 int p2 = parseScheme(p);
2656 if (p2 < 0)
2657 {
2658 error("Scheme");
2659 return -1;
2660 }
2661 p = p2;
2664 p2 = parseHierarchicalPart(p);
2665 if (p2 < 0)
2666 {
2667 error("Hierarchical part");
2668 return -1;
2669 }
2670 p = p2;
2672 p2 = parseQuery(p);
2673 if (p2 < 0)
2674 {
2675 error("Query");
2676 return -1;
2677 }
2678 p = p2;
2681 p2 = parseFragment(p);
2682 if (p2 < 0)
2683 {
2684 error("Fragment");
2685 return -1;
2686 }
2687 p = p2;
2689 return p;
2691 }
2695 bool URI::parse(const String &str)
2696 {
2697 init();
2699 parselen = str.size();
2701 String tmp;
2702 for (unsigned int i=0 ; i<str.size() ; i++)
2703 {
2704 XMLCh ch = (XMLCh) str[i];
2705 if (ch == '\\')
2706 tmp.push_back((XMLCh)'/');
2707 else
2708 tmp.push_back(ch);
2709 }
2710 parsebuf = (char *) tmp.c_str();
2713 int p = parse(0);
2714 normalize();
2716 if (p < 0)
2717 {
2718 error("Syntax error");
2719 return false;
2720 }
2722 //printf("uri:%s\n", toString().c_str());
2723 //printf("path:%s\n", path.c_str());
2725 return true;
2727 }
2736 //########################################################################
2737 //########################################################################
2738 //## M A K E
2739 //########################################################################
2740 //########################################################################
2742 //########################################################################
2743 //# F I L E S E T
2744 //########################################################################
2745 /**
2746 * This is the descriptor for a <fileset> item
2747 */
2748 class FileSet
2749 {
2750 public:
2752 /**
2753 *
2754 */
2755 FileSet()
2756 {}
2758 /**
2759 *
2760 */
2761 FileSet(const FileSet &other)
2762 { assign(other); }
2764 /**
2765 *
2766 */
2767 FileSet &operator=(const FileSet &other)
2768 { assign(other); return *this; }
2770 /**
2771 *
2772 */
2773 virtual ~FileSet()
2774 {}
2776 /**
2777 *
2778 */
2779 String getDirectory()
2780 { return directory; }
2782 /**
2783 *
2784 */
2785 void setDirectory(const String &val)
2786 { directory = val; }
2788 /**
2789 *
2790 */
2791 void setFiles(const std::vector<String> &val)
2792 { files = val; }
2794 /**
2795 *
2796 */
2797 std::vector<String> getFiles()
2798 { return files; }
2800 /**
2801 *
2802 */
2803 void setIncludes(const std::vector<String> &val)
2804 { includes = val; }
2806 /**
2807 *
2808 */
2809 std::vector<String> getIncludes()
2810 { return includes; }
2812 /**
2813 *
2814 */
2815 void setExcludes(const std::vector<String> &val)
2816 { excludes = val; }
2818 /**
2819 *
2820 */
2821 std::vector<String> getExcludes()
2822 { return excludes; }
2824 /**
2825 *
2826 */
2827 unsigned int size()
2828 { return files.size(); }
2830 /**
2831 *
2832 */
2833 String operator[](int index)
2834 { return files[index]; }
2836 /**
2837 *
2838 */
2839 void clear()
2840 {
2841 directory = "";
2842 files.clear();
2843 includes.clear();
2844 excludes.clear();
2845 }
2848 private:
2850 void assign(const FileSet &other)
2851 {
2852 directory = other.directory;
2853 files = other.files;
2854 includes = other.includes;
2855 excludes = other.excludes;
2856 }
2858 String directory;
2859 std::vector<String> files;
2860 std::vector<String> includes;
2861 std::vector<String> excludes;
2862 };
2867 //########################################################################
2868 //# M A K E B A S E
2869 //########################################################################
2870 /**
2871 * Base class for all classes in this file
2872 */
2873 class MakeBase
2874 {
2875 public:
2877 MakeBase()
2878 { line = 0; }
2879 virtual ~MakeBase()
2880 {}
2882 /**
2883 * Return the URI of the file associated with this object
2884 */
2885 URI getURI()
2886 { return uri; }
2888 /**
2889 * Set the uri to the given string
2890 */
2891 void setURI(const String &uristr)
2892 { uri.parse(uristr); }
2894 /**
2895 * Resolve another path relative to this one
2896 */
2897 String resolve(const String &otherPath);
2899 /**
2900 * Get an element attribute, performing substitutions if necessary
2901 */
2902 bool getAttribute(Element *elem, const String &name, String &result);
2904 /**
2905 * Get an element value, performing substitutions if necessary
2906 */
2907 bool getValue(Element *elem, String &result);
2909 /**
2910 * Set the current line number in the file
2911 */
2912 void setLine(int val)
2913 { line = val; }
2915 /**
2916 * Get the current line number in the file
2917 */
2918 int getLine()
2919 { return line; }
2922 /**
2923 * Set a property to a given value
2924 */
2925 virtual void setProperty(const String &name, const String &val)
2926 {
2927 properties[name] = val;
2928 }
2930 /**
2931 * Return a named property is found, else a null string
2932 */
2933 virtual String getProperty(const String &name)
2934 {
2935 String val;
2936 std::map<String, String>::iterator iter = properties.find(name);
2937 if (iter != properties.end())
2938 val = iter->second;
2939 return val;
2940 }
2942 /**
2943 * Return true if a named property is found, else false
2944 */
2945 virtual bool hasProperty(const String &name)
2946 {
2947 std::map<String, String>::iterator iter = properties.find(name);
2948 if (iter == properties.end())
2949 return false;
2950 return true;
2951 }
2954 protected:
2956 /**
2957 * The path to the file associated with this object
2958 */
2959 URI uri;
2962 /**
2963 * Print a printf()-like formatted error message
2964 */
2965 void error(const char *fmt, ...);
2967 /**
2968 * Print a printf()-like formatted trace message
2969 */
2970 void status(const char *fmt, ...);
2972 /**
2973 * Print a printf()-like formatted trace message
2974 */
2975 void trace(const char *fmt, ...);
2977 /**
2978 * Check if a given string matches a given regex pattern
2979 */
2980 bool regexMatch(const String &str, const String &pattern);
2982 /**
2983 *
2984 */
2985 String getSuffix(const String &fname);
2987 /**
2988 * Break up a string into substrings delimited the characters
2989 * in delimiters. Null-length substrings are ignored
2990 */
2991 std::vector<String> tokenize(const String &val,
2992 const String &delimiters);
2994 /**
2995 * replace runs of whitespace with a space
2996 */
2997 String strip(const String &s);
2999 /**
3000 * remove leading whitespace from each line
3001 */
3002 String leftJustify(const String &s);
3004 /**
3005 * remove leading and trailing whitespace from string
3006 */
3007 String trim(const String &s);
3009 /**
3010 * Return a lower case version of the given string
3011 */
3012 String toLower(const String &s);
3014 /**
3015 * Return the native format of the canonical
3016 * path which we store
3017 */
3018 String getNativePath(const String &path);
3020 /**
3021 * Execute a shell command. Outbuf is a ref to a string
3022 * to catch the result.
3023 */
3024 bool executeCommand(const String &call,
3025 const String &inbuf,
3026 String &outbuf,
3027 String &errbuf);
3028 /**
3029 * List all directories in a given base and starting directory
3030 * It is usually called like:
3031 * bool ret = listDirectories("src", "", result);
3032 */
3033 bool listDirectories(const String &baseName,
3034 const String &dirname,
3035 std::vector<String> &res);
3037 /**
3038 * Find all files in the named directory
3039 */
3040 bool listFiles(const String &baseName,
3041 const String &dirname,
3042 std::vector<String> &result);
3044 /**
3045 * Perform a listing for a fileset
3046 */
3047 bool listFiles(MakeBase &propRef, FileSet &fileSet);
3049 /**
3050 * Parse a <patternset>
3051 */
3052 bool parsePatternSet(Element *elem,
3053 MakeBase &propRef,
3054 std::vector<String> &includes,
3055 std::vector<String> &excludes);
3057 /**
3058 * Parse a <fileset> entry, and determine which files
3059 * should be included
3060 */
3061 bool parseFileSet(Element *elem,
3062 MakeBase &propRef,
3063 FileSet &fileSet);
3065 /**
3066 * Return this object's property list
3067 */
3068 virtual std::map<String, String> &getProperties()
3069 { return properties; }
3072 std::map<String, String> properties;
3074 /**
3075 * Turn 'true' and 'false' into boolean values
3076 */
3077 bool getBool(const String &str, bool &val);
3079 /**
3080 * Create a directory, making intermediate dirs
3081 * if necessary
3082 */
3083 bool createDirectory(const String &dirname);
3085 /**
3086 * Delete a directory and its children if desired
3087 */
3088 bool removeDirectory(const String &dirName);
3090 /**
3091 * Copy a file from one name to another. Perform only if needed
3092 */
3093 bool copyFile(const String &srcFile, const String &destFile);
3095 /**
3096 * Tests if the file exists and is a regular file
3097 */
3098 bool isRegularFile(const String &fileName);
3100 /**
3101 * Tests if the file exists and is a directory
3102 */
3103 bool isDirectory(const String &fileName);
3105 /**
3106 * Tests is the modification date of fileA is newer than fileB
3107 */
3108 bool isNewerThan(const String &fileA, const String &fileB);
3110 private:
3112 /**
3113 * replace variable refs like ${a} with their values
3114 */
3115 bool getSubstitutions(const String &s, String &result);
3117 int line;
3120 };
3125 /**
3126 * Print a printf()-like formatted error message
3127 */
3128 void MakeBase::error(const char *fmt, ...)
3129 {
3130 va_list args;
3131 va_start(args,fmt);
3132 fprintf(stderr, "Make error line %d: ", line);
3133 vfprintf(stderr, fmt, args);
3134 fprintf(stderr, "\n");
3135 va_end(args) ;
3136 }
3140 /**
3141 * Print a printf()-like formatted trace message
3142 */
3143 void MakeBase::status(const char *fmt, ...)
3144 {
3145 va_list args;
3146 va_start(args,fmt);
3147 //fprintf(stdout, " ");
3148 vfprintf(stdout, fmt, args);
3149 fprintf(stdout, "\n");
3150 va_end(args) ;
3151 }
3155 /**
3156 * Resolve another path relative to this one
3157 */
3158 String MakeBase::resolve(const String &otherPath)
3159 {
3160 URI otherURI(otherPath);
3161 URI fullURI = uri.resolve(otherURI);
3162 String ret = fullURI.toString();
3163 return ret;
3164 }
3167 /**
3168 * Print a printf()-like formatted trace message
3169 */
3170 void MakeBase::trace(const char *fmt, ...)
3171 {
3172 va_list args;
3173 va_start(args,fmt);
3174 fprintf(stdout, "Make: ");
3175 vfprintf(stdout, fmt, args);
3176 fprintf(stdout, "\n");
3177 va_end(args) ;
3178 }
3182 /**
3183 * Check if a given string matches a given regex pattern
3184 */
3185 bool MakeBase::regexMatch(const String &str, const String &pattern)
3186 {
3187 const TRexChar *terror = NULL;
3188 const TRexChar *cpat = pattern.c_str();
3189 TRex *expr = trex_compile(cpat, &terror);
3190 if (!expr)
3191 {
3192 if (!terror)
3193 terror = "undefined";
3194 error("compilation error [%s]!\n", terror);
3195 return false;
3196 }
3198 bool ret = true;
3200 const TRexChar *cstr = str.c_str();
3201 if (trex_match(expr, cstr))
3202 {
3203 ret = true;
3204 }
3205 else
3206 {
3207 ret = false;
3208 }
3210 trex_free(expr);
3212 return ret;
3213 }
3215 /**
3216 * Return the suffix, if any, of a file name
3217 */
3218 String MakeBase::getSuffix(const String &fname)
3219 {
3220 if (fname.size() < 2)
3221 return "";
3222 unsigned int pos = fname.find_last_of('.');
3223 if (pos == fname.npos)
3224 return "";
3225 pos++;
3226 String res = fname.substr(pos, fname.size()-pos);
3227 //trace("suffix:%s", res.c_str());
3228 return res;
3229 }
3233 /**
3234 * Break up a string into substrings delimited the characters
3235 * in delimiters. Null-length substrings are ignored
3236 */
3237 std::vector<String> MakeBase::tokenize(const String &str,
3238 const String &delimiters)
3239 {
3241 std::vector<String> res;
3242 char *del = (char *)delimiters.c_str();
3243 String dmp;
3244 for (unsigned int i=0 ; i<str.size() ; i++)
3245 {
3246 char ch = str[i];
3247 char *p = (char *)0;
3248 for (p=del ; *p ; p++)
3249 if (*p == ch)
3250 break;
3251 if (*p)
3252 {
3253 if (dmp.size() > 0)
3254 {
3255 res.push_back(dmp);
3256 dmp.clear();
3257 }
3258 }
3259 else
3260 {
3261 dmp.push_back(ch);
3262 }
3263 }
3264 //Add tail
3265 if (dmp.size() > 0)
3266 {
3267 res.push_back(dmp);
3268 dmp.clear();
3269 }
3271 return res;
3272 }
3276 /**
3277 * replace runs of whitespace with a single space
3278 */
3279 String MakeBase::strip(const String &s)
3280 {
3281 int len = s.size();
3282 String stripped;
3283 for (int i = 0 ; i<len ; i++)
3284 {
3285 char ch = s[i];
3286 if (isspace(ch))
3287 {
3288 stripped.push_back(' ');
3289 for ( ; i<len ; i++)
3290 {
3291 ch = s[i];
3292 if (!isspace(ch))
3293 {
3294 stripped.push_back(ch);
3295 break;
3296 }
3297 }
3298 }
3299 else
3300 {
3301 stripped.push_back(ch);
3302 }
3303 }
3304 return stripped;
3305 }
3307 /**
3308 * remove leading whitespace from each line
3309 */
3310 String MakeBase::leftJustify(const String &s)
3311 {
3312 String out;
3313 int len = s.size();
3314 for (int i = 0 ; i<len ; )
3315 {
3316 char ch;
3317 //Skip to first visible character
3318 while (i<len)
3319 {
3320 ch = s[i];
3321 if (ch == '\n' || ch == '\r'
3322 || !isspace(ch))
3323 break;
3324 i++;
3325 }
3326 //Copy the rest of the line
3327 while (i<len)
3328 {
3329 ch = s[i];
3330 if (ch == '\n' || ch == '\r')
3331 {
3332 if (ch != '\r')
3333 out.push_back('\n');
3334 i++;
3335 break;
3336 }
3337 else
3338 {
3339 out.push_back(ch);
3340 }
3341 i++;
3342 }
3343 }
3344 return out;
3345 }
3348 /**
3349 * Removes whitespace from beginning and end of a string
3350 */
3351 String MakeBase::trim(const String &s)
3352 {
3353 if (s.size() < 1)
3354 return s;
3356 //Find first non-ws char
3357 unsigned int begin = 0;
3358 for ( ; begin < s.size() ; begin++)
3359 {
3360 if (!isspace(s[begin]))
3361 break;
3362 }
3364 //Find first non-ws char, going in reverse
3365 unsigned int end = s.size() - 1;
3366 for ( ; end > begin ; end--)
3367 {
3368 if (!isspace(s[end]))
3369 break;
3370 }
3371 //trace("begin:%d end:%d", begin, end);
3373 String res = s.substr(begin, end-begin+1);
3374 return res;
3375 }
3378 /**
3379 * Return a lower case version of the given string
3380 */
3381 String MakeBase::toLower(const String &s)
3382 {
3383 if (s.size()==0)
3384 return s;
3386 String ret;
3387 for(unsigned int i=0; i<s.size() ; i++)
3388 {
3389 ret.push_back(tolower(s[i]));
3390 }
3391 return ret;
3392 }
3395 /**
3396 * Return the native format of the canonical
3397 * path which we store
3398 */
3399 String MakeBase::getNativePath(const String &path)
3400 {
3401 #ifdef __WIN32__
3402 String npath;
3403 unsigned int firstChar = 0;
3404 if (path.size() >= 3)
3405 {
3406 if (path[0] == '/' &&
3407 isalpha(path[1]) &&
3408 path[2] == ':')
3409 firstChar++;
3410 }
3411 for (unsigned int i=firstChar ; i<path.size() ; i++)
3412 {
3413 char ch = path[i];
3414 if (ch == '/')
3415 npath.push_back('\\');
3416 else
3417 npath.push_back(ch);
3418 }
3419 return npath;
3420 #else
3421 return path;
3422 #endif
3423 }
3426 #ifdef __WIN32__
3427 #include <tchar.h>
3429 static String win32LastError()
3430 {
3432 DWORD dw = GetLastError();
3434 LPVOID str;
3435 FormatMessage(
3436 FORMAT_MESSAGE_ALLOCATE_BUFFER |
3437 FORMAT_MESSAGE_FROM_SYSTEM,
3438 NULL,
3439 dw,
3440 0,
3441 (LPTSTR) &str,
3442 0, NULL );
3443 LPTSTR p = _tcschr((const char *)str, _T('\r'));
3444 if(p != NULL)
3445 { // lose CRLF
3446 *p = _T('\0');
3447 }
3448 String ret = (char *)str;
3449 LocalFree(str);
3451 return ret;
3452 }
3453 #endif
3457 /**
3458 * Execute a system call, using pipes to send data to the
3459 * program's stdin, and reading stdout and stderr.
3460 */
3461 bool MakeBase::executeCommand(const String &command,
3462 const String &inbuf,
3463 String &outbuf,
3464 String &errbuf)
3465 {
3467 status("============ cmd ============\n%s\n=============================",
3468 command.c_str());
3470 outbuf.clear();
3471 errbuf.clear();
3473 #ifdef __WIN32__
3475 /*
3476 I really hate having win32 code in this program, but the
3477 read buffer in command.com and cmd.exe are just too small
3478 for the large commands we need for compiling and linking.
3479 */
3481 bool ret = true;
3483 //# Allocate a separate buffer for safety
3484 char *paramBuf = new char[command.size() + 1];
3485 if (!paramBuf)
3486 {
3487 error("executeCommand cannot allocate command buffer");
3488 return false;
3489 }
3490 strcpy(paramBuf, (char *)command.c_str());
3492 //# Go to http://msdn2.microsoft.com/en-us/library/ms682499.aspx
3493 //# to see how Win32 pipes work
3495 //# Create pipes
3496 SECURITY_ATTRIBUTES saAttr;
3497 saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
3498 saAttr.bInheritHandle = TRUE;
3499 saAttr.lpSecurityDescriptor = NULL;
3500 HANDLE stdinRead, stdinWrite;
3501 HANDLE stdoutRead, stdoutWrite;
3502 HANDLE stderrRead, stderrWrite;
3503 if (!CreatePipe(&stdinRead, &stdinWrite, &saAttr, 0))
3504 {
3505 error("executeProgram: could not create pipe");
3506 delete[] paramBuf;
3507 return false;
3508 }
3509 SetHandleInformation(stdinWrite, HANDLE_FLAG_INHERIT, 0);
3510 if (!CreatePipe(&stdoutRead, &stdoutWrite, &saAttr, 0))
3511 {
3512 error("executeProgram: could not create pipe");
3513 delete[] paramBuf;
3514 return false;
3515 }
3516 SetHandleInformation(stdoutRead, HANDLE_FLAG_INHERIT, 0);
3517 if (!CreatePipe(&stderrRead, &stderrWrite, &saAttr, 0))
3518 {
3519 error("executeProgram: could not create pipe");
3520 delete[] paramBuf;
3521 return false;
3522 }
3523 SetHandleInformation(stderrRead, HANDLE_FLAG_INHERIT, 0);
3525 // Create the process
3526 STARTUPINFO siStartupInfo;
3527 PROCESS_INFORMATION piProcessInfo;
3528 memset(&siStartupInfo, 0, sizeof(siStartupInfo));
3529 memset(&piProcessInfo, 0, sizeof(piProcessInfo));
3530 siStartupInfo.cb = sizeof(siStartupInfo);
3531 siStartupInfo.hStdError = stderrWrite;
3532 siStartupInfo.hStdOutput = stdoutWrite;
3533 siStartupInfo.hStdInput = stdinRead;
3534 siStartupInfo.dwFlags |= STARTF_USESTDHANDLES;
3536 if (!CreateProcess(NULL, paramBuf, NULL, NULL, true,
3537 0, NULL, NULL, &siStartupInfo,
3538 &piProcessInfo))
3539 {
3540 error("executeCommand : could not create process : %s",
3541 win32LastError().c_str());
3542 ret = false;
3543 }
3545 delete[] paramBuf;
3547 DWORD bytesWritten;
3548 if (inbuf.size()>0 &&
3549 !WriteFile(stdinWrite, inbuf.c_str(), inbuf.size(),
3550 &bytesWritten, NULL))
3551 {
3552 error("executeCommand: could not write to pipe");
3553 return false;
3554 }
3555 if (!CloseHandle(stdinWrite))
3556 {
3557 error("executeCommand: could not close write pipe");
3558 return false;
3559 }
3560 if (!CloseHandle(stdoutWrite))
3561 {
3562 error("executeCommand: could not close read pipe");
3563 return false;
3564 }
3565 if (!CloseHandle(stderrWrite))
3566 {
3567 error("executeCommand: could not close read pipe");
3568 return false;
3569 }
3571 bool lastLoop = false;
3572 while (true)
3573 {
3574 DWORD avail;
3575 DWORD bytesRead;
3576 char readBuf[4096];
3578 //trace("## stderr");
3579 PeekNamedPipe(stderrRead, NULL, 0, NULL, &avail, NULL);
3580 if (avail > 0)
3581 {
3582 bytesRead = 0;
3583 if (avail>4096) avail = 4096;
3584 ReadFile(stderrRead, readBuf, avail, &bytesRead, NULL);
3585 if (bytesRead > 0)
3586 {
3587 for (unsigned int i=0 ; i<bytesRead ; i++)
3588 errbuf.push_back(readBuf[i]);
3589 }
3590 }
3592 //trace("## stdout");
3593 PeekNamedPipe(stdoutRead, NULL, 0, NULL, &avail, NULL);
3594 if (avail > 0)
3595 {
3596 bytesRead = 0;
3597 if (avail>4096) avail = 4096;
3598 ReadFile(stdoutRead, readBuf, avail, &bytesRead, NULL);
3599 if (bytesRead > 0)
3600 {
3601 for (unsigned int i=0 ; i<bytesRead ; i++)
3602 outbuf.push_back(readBuf[i]);
3603 }
3604 }
3606 //Was this the final check after program done?
3607 if (lastLoop)
3608 break;
3610 DWORD exitCode;
3611 GetExitCodeProcess(piProcessInfo.hProcess, &exitCode);
3612 if (exitCode != STILL_ACTIVE)
3613 lastLoop = true;
3615 Sleep(10);
3616 }
3617 //trace("outbuf:%s", outbuf.c_str());
3618 if (!CloseHandle(stdoutRead))
3619 {
3620 error("executeCommand: could not close read pipe");
3621 return false;
3622 }
3623 if (!CloseHandle(stderrRead))
3624 {
3625 error("executeCommand: could not close read pipe");
3626 return false;
3627 }
3629 DWORD exitCode;
3630 GetExitCodeProcess(piProcessInfo.hProcess, &exitCode);
3631 //trace("exit code:%d", exitCode);
3632 if (exitCode != 0)
3633 {
3634 ret = false;
3635 }
3637 CloseHandle(piProcessInfo.hProcess);
3638 CloseHandle(piProcessInfo.hThread);
3640 return ret;
3642 #else //do it unix-style
3644 String s;
3645 FILE *f = popen(command.c_str(), "r");
3646 int errnum = 0;
3647 if (f)
3648 {
3649 while (true)
3650 {
3651 int ch = fgetc(f);
3652 if (ch < 0)
3653 break;
3654 s.push_back((char)ch);
3655 }
3656 errnum = pclose(f);
3657 }
3658 outbuf = s;
3659 if (errnum != 0)
3660 {
3661 error("exec of command '%s' failed : %s",
3662 command.c_str(), strerror(errno));
3663 return false;
3664 }
3665 else
3666 return true;
3668 #endif
3669 }
3674 bool MakeBase::listDirectories(const String &baseName,
3675 const String &dirName,
3676 std::vector<String> &res)
3677 {
3678 res.push_back(dirName);
3679 String fullPath = baseName;
3680 if (dirName.size()>0)
3681 {
3682 fullPath.append("/");
3683 fullPath.append(dirName);
3684 }
3685 DIR *dir = opendir(fullPath.c_str());
3686 while (true)
3687 {
3688 struct dirent *de = readdir(dir);
3689 if (!de)
3690 break;
3692 //Get the directory member name
3693 String s = de->d_name;
3694 if (s.size() == 0 || s[0] == '.')
3695 continue;
3696 String childName = dirName;
3697 childName.append("/");
3698 childName.append(s);
3700 String fullChildPath = baseName;
3701 fullChildPath.append("/");
3702 fullChildPath.append(childName);
3703 struct stat finfo;
3704 String childNative = getNativePath(fullChildPath);
3705 if (stat(childNative.c_str(), &finfo)<0)
3706 {
3707 error("cannot stat file:%s", childNative.c_str());
3708 }
3709 else if (S_ISDIR(finfo.st_mode))
3710 {
3711 //trace("directory: %s", childName.c_str());
3712 if (!listDirectories(baseName, childName, res))
3713 return false;
3714 }
3715 }
3716 closedir(dir);
3718 return true;
3719 }
3722 bool MakeBase::listFiles(const String &baseDir,
3723 const String &dirName,
3724 std::vector<String> &res)
3725 {
3726 String fullDir = baseDir;
3727 if (dirName.size()>0)
3728 {
3729 fullDir.append("/");
3730 fullDir.append(dirName);
3731 }
3732 String dirNative = getNativePath(fullDir);
3734 std::vector<String> subdirs;
3735 DIR *dir = opendir(dirNative.c_str());
3736 if (!dir)
3737 {
3738 error("Could not open directory %s : %s",
3739 dirNative.c_str(), strerror(errno));
3740 return false;
3741 }
3742 while (true)
3743 {
3744 struct dirent *de = readdir(dir);
3745 if (!de)
3746 break;
3748 //Get the directory member name
3749 String s = de->d_name;
3750 if (s.size() == 0 || s[0] == '.')
3751 continue;
3752 String childName;
3753 if (dirName.size()>0)
3754 {
3755 childName.append(dirName);
3756 childName.append("/");
3757 }
3758 childName.append(s);
3759 String fullChild = baseDir;
3760 fullChild.append("/");
3761 fullChild.append(childName);
3763 if (isDirectory(fullChild))
3764 {
3765 //trace("directory: %s", childName.c_str());
3766 if (!listFiles(baseDir, childName, res))
3767 return false;
3768 continue;
3769 }
3770 else if (!isRegularFile(fullChild))
3771 {
3772 error("unknown file:%s", childName.c_str());
3773 return false;
3774 }
3776 //all done!
3777 res.push_back(childName);
3779 }
3780 closedir(dir);
3782 return true;
3783 }
3786 bool MakeBase::listFiles(MakeBase &propRef, FileSet &fileSet)
3787 {
3788 String baseDir = propRef.resolve(fileSet.getDirectory());
3789 std::vector<String> fileList;
3790 if (!listFiles(baseDir, "", fileList))
3791 return false;
3793 std::vector<String> includes = fileSet.getIncludes();
3794 std::vector<String> excludes = fileSet.getExcludes();
3796 std::vector<String> incs;
3797 std::vector<String>::iterator iter;
3799 std::sort(fileList.begin(), fileList.end());
3801 //If there are <includes>, then add files to the output
3802 //in the order of the include list
3803 if (includes.size()==0)
3804 incs = fileList;
3805 else
3806 {
3807 for (iter = includes.begin() ; iter != includes.end() ; iter++)
3808 {
3809 String pattern = *iter;
3810 std::vector<String>::iterator siter;
3811 for (siter = fileList.begin() ; siter != fileList.end() ; siter++)
3812 {
3813 String s = *siter;
3814 if (regexMatch(s, pattern))
3815 {
3816 //trace("INCLUDED:%s", s.c_str());
3817 incs.push_back(s);
3818 }
3819 }
3820 }
3821 }
3823 //Now trim off the <excludes>
3824 std::vector<String> res;
3825 for (iter = incs.begin() ; iter != incs.end() ; iter++)
3826 {
3827 String s = *iter;
3828 bool skipme = false;
3829 std::vector<String>::iterator siter;
3830 for (siter = excludes.begin() ; siter != excludes.end() ; siter++)
3831 {
3832 String pattern = *siter;
3833 if (regexMatch(s, pattern))
3834 {
3835 //trace("EXCLUDED:%s", s.c_str());
3836 skipme = true;
3837 break;
3838 }
3839 }
3840 if (!skipme)
3841 res.push_back(s);
3842 }
3844 fileSet.setFiles(res);
3846 return true;
3847 }
3853 bool MakeBase::getSubstitutions(const String &str, String &result)
3854 {
3855 String s = trim(str);
3856 int len = (int)s.size();
3857 String val;
3858 for (int i=0 ; i<len ; i++)
3859 {
3860 char ch = s[i];
3861 if (ch == '$' && s[i+1] == '{')
3862 {
3863 String varname;
3864 int j = i+2;
3865 for ( ; j<len ; j++)
3866 {
3867 ch = s[j];
3868 if (ch == '$' && s[j+1] == '{')
3869 {
3870 error("attribute %s cannot have nested variable references",
3871 s.c_str());
3872 return false;
3873 }
3874 else if (ch == '}')
3875 {
3876 std::map<String, String>::iterator iter;
3877 iter = properties.find(trim(varname));
3878 if (iter != properties.end())
3879 {
3880 val.append(iter->second);
3881 }
3882 else
3883 {
3884 error("property ${%s} not found", varname.c_str());
3885 return false;
3886 }
3887 break;
3888 }
3889 else
3890 {
3891 varname.push_back(ch);
3892 }
3893 }
3894 i = j;
3895 }
3896 else
3897 {
3898 val.push_back(ch);
3899 }
3900 }
3901 result = val;
3902 return true;
3903 }
3906 bool MakeBase::getAttribute(Element *elem, const String &name,
3907 String &result)
3908 {
3909 String s = elem->getAttribute(name);
3910 return getSubstitutions(s, result);
3911 }
3914 bool MakeBase::getValue(Element *elem, String &result)
3915 {
3916 String s = elem->getValue();
3917 //Replace all runs of whitespace with a single space
3918 return getSubstitutions(s, result);
3919 }
3922 /**
3923 * Turn 'true' and 'false' into boolean values
3924 */
3925 bool MakeBase::getBool(const String &str, bool &val)
3926 {
3927 if (str == "true")
3928 val = true;
3929 else if (str == "false")
3930 val = false;
3931 else
3932 {
3933 error("expected 'true' or 'false'. found '%s'", str.c_str());
3934 return false;
3935 }
3936 return true;
3937 }
3942 /**
3943 * Parse a <patternset> entry
3944 */
3945 bool MakeBase::parsePatternSet(Element *elem,
3946 MakeBase &propRef,
3947 std::vector<String> &includes,
3948 std::vector<String> &excludes
3949 )
3950 {
3951 std::vector<Element *> children = elem->getChildren();
3952 for (unsigned int i=0 ; i<children.size() ; i++)
3953 {
3954 Element *child = children[i];
3955 String tagName = child->getName();
3956 if (tagName == "exclude")
3957 {
3958 String fname;
3959 if (!propRef.getAttribute(child, "name", fname))
3960 return false;
3961 //trace("EXCLUDE: %s", fname.c_str());
3962 excludes.push_back(fname);
3963 }
3964 else if (tagName == "include")
3965 {
3966 String fname;
3967 if (!propRef.getAttribute(child, "name", fname))
3968 return false;
3969 //trace("INCLUDE: %s", fname.c_str());
3970 includes.push_back(fname);
3971 }
3972 }
3974 return true;
3975 }
3980 /**
3981 * Parse a <fileset> entry, and determine which files
3982 * should be included
3983 */
3984 bool MakeBase::parseFileSet(Element *elem,
3985 MakeBase &propRef,
3986 FileSet &fileSet)
3987 {
3988 String name = elem->getName();
3989 if (name != "fileset")
3990 {
3991 error("expected <fileset>");
3992 return false;
3993 }
3996 std::vector<String> includes;
3997 std::vector<String> excludes;
3999 //A fileset has one implied patternset
4000 if (!parsePatternSet(elem, propRef, includes, excludes))
4001 {
4002 return false;
4003 }
4004 //Look for child tags, including more patternsets
4005 std::vector<Element *> children = elem->getChildren();
4006 for (unsigned int i=0 ; i<children.size() ; i++)
4007 {
4008 Element *child = children[i];
4009 String tagName = child->getName();
4010 if (tagName == "patternset")
4011 {
4012 if (!parsePatternSet(child, propRef, includes, excludes))
4013 {
4014 return false;
4015 }
4016 }
4017 }
4019 String dir;
4020 //Now do the stuff
4021 //Get the base directory for reading file names
4022 if (!propRef.getAttribute(elem, "dir", dir))
4023 return false;
4025 fileSet.setDirectory(dir);
4026 fileSet.setIncludes(includes);
4027 fileSet.setExcludes(excludes);
4029 /*
4030 std::vector<String> fileList;
4031 if (dir.size() > 0)
4032 {
4033 String baseDir = propRef.resolve(dir);
4034 if (!listFiles(baseDir, "", includes, excludes, fileList))
4035 return false;
4036 }
4037 std::sort(fileList.begin(), fileList.end());
4038 result = fileList;
4039 */
4042 /*
4043 for (unsigned int i=0 ; i<result.size() ; i++)
4044 {
4045 trace("RES:%s", result[i].c_str());
4046 }
4047 */
4050 return true;
4051 }
4055 /**
4056 * Create a directory, making intermediate dirs
4057 * if necessary
4058 */
4059 bool MakeBase::createDirectory(const String &dirname)
4060 {
4061 //trace("## createDirectory: %s", dirname.c_str());
4062 //## first check if it exists
4063 struct stat finfo;
4064 String nativeDir = getNativePath(dirname);
4065 char *cnative = (char *) nativeDir.c_str();
4066 #ifdef __WIN32__
4067 if (strlen(cnative)==2 && cnative[1]==':')
4068 return true;
4069 #endif
4070 if (stat(cnative, &finfo)==0)
4071 {
4072 if (!S_ISDIR(finfo.st_mode))
4073 {
4074 error("mkdir: file %s exists but is not a directory",
4075 cnative);
4076 return false;
4077 }
4078 else //exists
4079 {
4080 return true;
4081 }
4082 }
4084 //## 2: pull off the last path segment, if any,
4085 //## to make the dir 'above' this one, if necessary
4086 unsigned int pos = dirname.find_last_of('/');
4087 if (pos>0 && pos != dirname.npos)
4088 {
4089 String subpath = dirname.substr(0, pos);
4090 //A letter root (c:) ?
4091 if (!createDirectory(subpath))
4092 return false;
4093 }
4095 //## 3: now make
4096 #ifdef __WIN32__
4097 if (mkdir(cnative)<0)
4098 #else
4099 if (mkdir(cnative, S_IRWXU | S_IRWXG | S_IRWXO)<0)
4100 #endif
4101 {
4102 error("cannot make directory '%s' : %s",
4103 cnative, strerror(errno));
4104 return false;
4105 }
4107 return true;
4108 }
4111 /**
4112 * Remove a directory recursively
4113 */
4114 bool MakeBase::removeDirectory(const String &dirName)
4115 {
4116 char *dname = (char *)dirName.c_str();
4118 DIR *dir = opendir(dname);
4119 if (!dir)
4120 {
4121 //# Let this fail nicely.
4122 return true;
4123 //error("error opening directory %s : %s", dname, strerror(errno));
4124 //return false;
4125 }
4127 while (true)
4128 {
4129 struct dirent *de = readdir(dir);
4130 if (!de)
4131 break;
4133 //Get the directory member name
4134 String s = de->d_name;
4135 if (s.size() == 0 || s[0] == '.')
4136 continue;
4137 String childName;
4138 if (dirName.size() > 0)
4139 {
4140 childName.append(dirName);
4141 childName.append("/");
4142 }
4143 childName.append(s);
4146 struct stat finfo;
4147 String childNative = getNativePath(childName);
4148 char *cnative = (char *)childNative.c_str();
4149 if (stat(cnative, &finfo)<0)
4150 {
4151 error("cannot stat file:%s", cnative);
4152 }
4153 else if (S_ISDIR(finfo.st_mode))
4154 {
4155 //trace("DEL dir: %s", childName.c_str());
4156 if (!removeDirectory(childName))
4157 {
4158 return false;
4159 }
4160 }
4161 else if (!S_ISREG(finfo.st_mode))
4162 {
4163 //trace("not regular: %s", cnative);
4164 }
4165 else
4166 {
4167 //trace("DEL file: %s", childName.c_str());
4168 if (remove(cnative)<0)
4169 {
4170 error("error deleting %s : %s",
4171 cnative, strerror(errno));
4172 return false;
4173 }
4174 }
4175 }
4176 closedir(dir);
4178 //Now delete the directory
4179 String native = getNativePath(dirName);
4180 if (rmdir(native.c_str())<0)
4181 {
4182 error("could not delete directory %s : %s",
4183 native.c_str() , strerror(errno));
4184 return false;
4185 }
4187 return true;
4189 }
4192 /**
4193 * Copy a file from one name to another. Perform only if needed
4194 */
4195 bool MakeBase::copyFile(const String &srcFile, const String &destFile)
4196 {
4197 //# 1 Check up-to-date times
4198 String srcNative = getNativePath(srcFile);
4199 struct stat srcinfo;
4200 if (stat(srcNative.c_str(), &srcinfo)<0)
4201 {
4202 error("source file %s for copy does not exist",
4203 srcNative.c_str());
4204 return false;
4205 }
4207 String destNative = getNativePath(destFile);
4208 struct stat destinfo;
4209 if (stat(destNative.c_str(), &destinfo)==0)
4210 {
4211 if (destinfo.st_mtime >= srcinfo.st_mtime)
4212 return true;
4213 }
4215 //# 2 prepare a destination directory if necessary
4216 unsigned int pos = destFile.find_last_of('/');
4217 if (pos != destFile.npos)
4218 {
4219 String subpath = destFile.substr(0, pos);
4220 if (!createDirectory(subpath))
4221 return false;
4222 }
4224 //# 3 do the data copy
4225 #ifndef __WIN32__
4227 FILE *srcf = fopen(srcNative.c_str(), "rb");
4228 if (!srcf)
4229 {
4230 error("copyFile cannot open '%s' for reading", srcNative.c_str());
4231 return false;
4232 }
4233 FILE *destf = fopen(destNative.c_str(), "wb");
4234 if (!destf)
4235 {
4236 error("copyFile cannot open %s for writing", srcNative.c_str());
4237 return false;
4238 }
4240 while (!feof(srcf))
4241 {
4242 int ch = fgetc(srcf);
4243 if (ch<0)
4244 break;
4245 fputc(ch, destf);
4246 }
4248 fclose(destf);
4249 fclose(srcf);
4251 #else
4253 if (!CopyFile(srcNative.c_str(), destNative.c_str(), false))
4254 {
4255 error("copyFile from %s to %s failed",
4256 srcNative.c_str(), destNative.c_str());
4257 return false;
4258 }
4260 #endif /* __WIN32__ */
4263 return true;
4264 }
4268 /**
4269 * Tests if the file exists and is a regular file
4270 */
4271 bool MakeBase::isRegularFile(const String &fileName)
4272 {
4273 String native = getNativePath(fileName);
4274 struct stat finfo;
4276 //Exists?
4277 if (stat(native.c_str(), &finfo)<0)
4278 return false;
4281 //check the file mode
4282 if (!S_ISREG(finfo.st_mode))
4283 return false;
4285 return true;
4286 }
4288 /**
4289 * Tests if the file exists and is a directory
4290 */
4291 bool MakeBase::isDirectory(const String &fileName)
4292 {
4293 String native = getNativePath(fileName);
4294 struct stat finfo;
4296 //Exists?
4297 if (stat(native.c_str(), &finfo)<0)
4298 return false;
4301 //check the file mode
4302 if (!S_ISDIR(finfo.st_mode))
4303 return false;
4305 return true;
4306 }
4310 /**
4311 * Tests is the modification of fileA is newer than fileB
4312 */
4313 bool MakeBase::isNewerThan(const String &fileA, const String &fileB)
4314 {
4315 //trace("isNewerThan:'%s' , '%s'", fileA.c_str(), fileB.c_str());
4316 String nativeA = getNativePath(fileA);
4317 struct stat infoA;
4318 //IF source does not exist, NOT newer
4319 if (stat(nativeA.c_str(), &infoA)<0)
4320 {
4321 return false;
4322 }
4324 String nativeB = getNativePath(fileB);
4325 struct stat infoB;
4326 //IF dest does not exist, YES, newer
4327 if (stat(nativeB.c_str(), &infoB)<0)
4328 {
4329 return true;
4330 }
4332 //check the actual times
4333 if (infoA.st_mtime > infoB.st_mtime)
4334 {
4335 return true;
4336 }
4338 return false;
4339 }
4342 //########################################################################
4343 //# P K G C O N F I G
4344 //########################################################################
4346 /**
4347 *
4348 */
4349 class PkgConfig : public MakeBase
4350 {
4352 public:
4354 /**
4355 *
4356 */
4357 PkgConfig()
4358 { path="."; init(); }
4360 /**
4361 *
4362 */
4363 PkgConfig(const PkgConfig &other)
4364 { assign(other); }
4366 /**
4367 *
4368 */
4369 PkgConfig &operator=(const PkgConfig &other)
4370 { assign(other); return *this; }
4372 /**
4373 *
4374 */
4375 virtual ~PkgConfig()
4376 { }
4378 /**
4379 *
4380 */
4381 virtual String getName()
4382 { return name; }
4384 /**
4385 *
4386 */
4387 virtual String getPath()
4388 { return path; }
4390 /**
4391 *
4392 */
4393 virtual void setPath(const String &val)
4394 { path = val; }
4396 /**
4397 *
4398 */
4399 virtual String getPrefix()
4400 { return prefix; }
4402 /**
4403 * Allow the user to override the prefix in the file
4404 */
4405 virtual void setPrefix(const String &val)
4406 { prefix = val; }
4408 /**
4409 *
4410 */
4411 virtual String getDescription()
4412 { return description; }
4414 /**
4415 *
4416 */
4417 virtual String getCflags()
4418 { return cflags; }
4420 /**
4421 *
4422 */
4423 virtual String getLibs()
4424 { return libs; }
4426 /**
4427 *
4428 */
4429 virtual String getAll()
4430 {
4431 String ret = cflags;
4432 ret.append(" ");
4433 ret.append(libs);
4434 return ret;
4435 }
4437 /**
4438 *
4439 */
4440 virtual String getVersion()
4441 { return version; }
4443 /**
4444 *
4445 */
4446 virtual int getMajorVersion()
4447 { return majorVersion; }
4449 /**
4450 *
4451 */
4452 virtual int getMinorVersion()
4453 { return minorVersion; }
4455 /**
4456 *
4457 */
4458 virtual int getMicroVersion()
4459 { return microVersion; }
4461 /**
4462 *
4463 */
4464 virtual std::map<String, String> &getAttributes()
4465 { return attrs; }
4467 /**
4468 *
4469 */
4470 virtual std::vector<String> &getRequireList()
4471 { return requireList; }
4473 /**
4474 * Read a file for its details
4475 */
4476 virtual bool readFile(const String &fileName);
4478 /**
4479 * Read a file for its details
4480 */
4481 virtual bool query(const String &name);
4483 private:
4485 void init()
4486 {
4487 //do not set path or prefix here
4488 name = "";
4489 description = "";
4490 cflags = "";
4491 libs = "";
4492 requires = "";
4493 version = "";
4494 majorVersion = 0;
4495 minorVersion = 0;
4496 microVersion = 0;
4497 fileName = "";
4498 attrs.clear();
4499 requireList.clear();
4500 }
4502 void assign(const PkgConfig &other)
4503 {
4504 name = other.name;
4505 path = other.path;
4506 prefix = other.prefix;
4507 description = other.description;
4508 cflags = other.cflags;
4509 libs = other.libs;
4510 requires = other.requires;
4511 version = other.version;
4512 majorVersion = other.majorVersion;
4513 minorVersion = other.minorVersion;
4514 microVersion = other.microVersion;
4515 fileName = other.fileName;
4516 attrs = other.attrs;
4517 requireList = other.requireList;
4518 }
4522 int get(int pos);
4524 int skipwhite(int pos);
4526 int getword(int pos, String &ret);
4528 void parseRequires();
4530 void parseVersion();
4532 bool parseLine(const String &lineBuf);
4534 bool parse(const String &buf);
4536 void dumpAttrs();
4538 String name;
4540 String path;
4542 String prefix;
4544 String description;
4546 String cflags;
4548 String libs;
4550 String requires;
4552 String version;
4554 int majorVersion;
4556 int minorVersion;
4558 int microVersion;
4560 String fileName;
4562 std::map<String, String> attrs;
4564 std::vector<String> requireList;
4566 char *parsebuf;
4567 int parselen;
4568 };
4571 /**
4572 * Get a character from the buffer at pos. If out of range,
4573 * return -1 for safety
4574 */
4575 int PkgConfig::get(int pos)
4576 {
4577 if (pos>parselen)
4578 return -1;
4579 return parsebuf[pos];
4580 }
4584 /**
4585 * Skip over all whitespace characters beginning at pos. Return
4586 * the position of the first non-whitespace character.
4587 * Pkg-config is line-oriented, so check for newline
4588 */
4589 int PkgConfig::skipwhite(int pos)
4590 {
4591 while (pos < parselen)
4592 {
4593 int ch = get(pos);
4594 if (ch < 0)
4595 break;
4596 if (!isspace(ch))
4597 break;
4598 pos++;
4599 }
4600 return pos;
4601 }
4604 /**
4605 * Parse the buffer beginning at pos, for a word. Fill
4606 * 'ret' with the result. Return the position after the
4607 * word.
4608 */
4609 int PkgConfig::getword(int pos, String &ret)
4610 {
4611 while (pos < parselen)
4612 {
4613 int ch = get(pos);
4614 if (ch < 0)
4615 break;
4616 if (!isalnum(ch) && ch != '_' && ch != '-'&& ch != '.')
4617 break;
4618 ret.push_back((char)ch);
4619 pos++;
4620 }
4621 return pos;
4622 }
4624 void PkgConfig::parseRequires()
4625 {
4626 if (requires.size() == 0)
4627 return;
4628 parsebuf = (char *)requires.c_str();
4629 parselen = requires.size();
4630 int pos = 0;
4631 while (pos < parselen)
4632 {
4633 pos = skipwhite(pos);
4634 String val;
4635 int pos2 = getword(pos, val);
4636 if (pos2 == pos)
4637 break;
4638 pos = pos2;
4639 //trace("val %s", val.c_str());
4640 requireList.push_back(val);
4641 }
4642 }
4644 static int getint(const String str)
4645 {
4646 char *s = (char *)str.c_str();
4647 char *ends = NULL;
4648 long val = strtol(s, &ends, 10);
4649 if (ends == s)
4650 return 0L;
4651 else
4652 return val;
4653 }
4655 void PkgConfig::parseVersion()
4656 {
4657 if (version.size() == 0)
4658 return;
4659 String s1, s2, s3;
4660 unsigned int pos = 0;
4661 unsigned int pos2 = version.find('.', pos);
4662 if (pos2 == version.npos)
4663 {
4664 s1 = version;
4665 }
4666 else
4667 {
4668 s1 = version.substr(pos, pos2-pos);
4669 pos = pos2;
4670 pos++;
4671 if (pos < version.size())
4672 {
4673 pos2 = version.find('.', pos);
4674 if (pos2 == version.npos)
4675 {
4676 s2 = version.substr(pos, version.size()-pos);
4677 }
4678 else
4679 {
4680 s2 = version.substr(pos, pos2-pos);
4681 pos = pos2;
4682 pos++;
4683 if (pos < version.size())
4684 s3 = version.substr(pos, pos2-pos);
4685 }
4686 }
4687 }
4689 majorVersion = getint(s1);
4690 minorVersion = getint(s2);
4691 microVersion = getint(s3);
4692 //trace("version:%d.%d.%d", majorVersion,
4693 // minorVersion, microVersion );
4694 }
4697 bool PkgConfig::parseLine(const String &lineBuf)
4698 {
4699 parsebuf = (char *)lineBuf.c_str();
4700 parselen = lineBuf.size();
4701 int pos = 0;
4703 while (pos < parselen)
4704 {
4705 String attrName;
4706 pos = skipwhite(pos);
4707 int ch = get(pos);
4708 if (ch == '#')
4709 {
4710 //comment. eat the rest of the line
4711 while (pos < parselen)
4712 {
4713 ch = get(pos);
4714 if (ch == '\n' || ch < 0)
4715 break;
4716 pos++;
4717 }
4718 continue;
4719 }
4720 pos = getword(pos, attrName);
4721 if (attrName.size() == 0)
4722 continue;
4724 pos = skipwhite(pos);
4725 ch = get(pos);
4726 if (ch != ':' && ch != '=')
4727 {
4728 error("expected ':' or '='");
4729 return false;
4730 }
4731 pos++;
4732 pos = skipwhite(pos);
4733 String attrVal;
4734 while (pos < parselen)
4735 {
4736 ch = get(pos);
4737 if (ch == '\n' || ch < 0)
4738 break;
4739 else if (ch == '$' && get(pos+1) == '{')
4740 {
4741 //# this is a ${substitution}
4742 pos += 2;
4743 String subName;
4744 while (pos < parselen)
4745 {
4746 ch = get(pos);
4747 if (ch < 0)
4748 {
4749 error("unterminated substitution");
4750 return false;
4751 }
4752 else if (ch == '}')
4753 break;
4754 else
4755 subName.push_back((char)ch);
4756 pos++;
4757 }
4758 //trace("subName:%s %s", subName.c_str(), prefix.c_str());
4759 if (subName == "prefix" && prefix.size()>0)
4760 {
4761 attrVal.append(prefix);
4762 //trace("prefix override:%s", prefix.c_str());
4763 }
4764 else
4765 {
4766 String subVal = attrs[subName];
4767 //trace("subVal:%s", subVal.c_str());
4768 attrVal.append(subVal);
4769 }
4770 }
4771 else
4772 attrVal.push_back((char)ch);
4773 pos++;
4774 }
4776 attrVal = trim(attrVal);
4777 attrs[attrName] = attrVal;
4779 String attrNameL = toLower(attrName);
4781 if (attrNameL == "name")
4782 name = attrVal;
4783 else if (attrNameL == "description")
4784 description = attrVal;
4785 else if (attrNameL == "cflags")
4786 cflags = attrVal;
4787 else if (attrNameL == "libs")
4788 libs = attrVal;
4789 else if (attrNameL == "requires")
4790 requires = attrVal;
4791 else if (attrNameL == "version")
4792 version = attrVal;
4794 //trace("name:'%s' value:'%s'",
4795 // attrName.c_str(), attrVal.c_str());
4796 }
4798 return true;
4799 }
4802 bool PkgConfig::parse(const String &buf)
4803 {
4804 init();
4806 String line;
4807 int lineNr = 0;
4808 for (unsigned int p=0 ; p<buf.size() ; p++)
4809 {
4810 int ch = buf[p];
4811 if (ch == '\n' || ch == '\r')
4812 {
4813 if (!parseLine(line))
4814 return false;
4815 line.clear();
4816 lineNr++;
4817 }
4818 else
4819 {
4820 line.push_back(ch);
4821 }
4822 }
4823 if (line.size()>0)
4824 {
4825 if (!parseLine(line))
4826 return false;
4827 }
4829 parseRequires();
4830 parseVersion();
4832 return true;
4833 }
4838 void PkgConfig::dumpAttrs()
4839 {
4840 //trace("### PkgConfig attributes for %s", fileName.c_str());
4841 std::map<String, String>::iterator iter;
4842 for (iter=attrs.begin() ; iter!=attrs.end() ; iter++)
4843 {
4844 trace(" %s = %s", iter->first.c_str(), iter->second.c_str());
4845 }
4846 }
4849 bool PkgConfig::readFile(const String &fname)
4850 {
4851 fileName = getNativePath(fname);
4853 FILE *f = fopen(fileName.c_str(), "r");
4854 if (!f)
4855 {
4856 error("cannot open file '%s' for reading", fileName.c_str());
4857 return false;
4858 }
4859 String buf;
4860 while (true)
4861 {
4862 int ch = fgetc(f);
4863 if (ch < 0)
4864 break;
4865 buf.push_back((char)ch);
4866 }
4867 fclose(f);
4869 //trace("####### File:\n%s", buf.c_str());
4870 if (!parse(buf))
4871 {
4872 return false;
4873 }
4875 //dumpAttrs();
4877 return true;
4878 }
4882 bool PkgConfig::query(const String &pkgName)
4883 {
4884 name = pkgName;
4886 String fname = path;
4887 fname.append("/");
4888 fname.append(name);
4889 fname.append(".pc");
4891 if (!readFile(fname))
4892 return false;
4894 return true;
4895 }
4901 //########################################################################
4902 //# D E P T O O L
4903 //########################################################################
4907 /**
4908 * Class which holds information for each file.
4909 */
4910 class FileRec
4911 {
4912 public:
4914 typedef enum
4915 {
4916 UNKNOWN,
4917 CFILE,
4918 HFILE,
4919 OFILE
4920 } FileType;
4922 /**
4923 * Constructor
4924 */
4925 FileRec()
4926 { init(); type = UNKNOWN; }
4928 /**
4929 * Copy constructor
4930 */
4931 FileRec(const FileRec &other)
4932 { init(); assign(other); }
4933 /**
4934 * Constructor
4935 */
4936 FileRec(int typeVal)
4937 { init(); type = typeVal; }
4938 /**
4939 * Assignment operator
4940 */
4941 FileRec &operator=(const FileRec &other)
4942 { init(); assign(other); return *this; }
4945 /**
4946 * Destructor
4947 */
4948 ~FileRec()
4949 {}
4951 /**
4952 * Directory part of the file name
4953 */
4954 String path;
4956 /**
4957 * Base name, sans directory and suffix
4958 */
4959 String baseName;
4961 /**
4962 * File extension, such as cpp or h
4963 */
4964 String suffix;
4966 /**
4967 * Type of file: CFILE, HFILE, OFILE
4968 */
4969 int type;
4971 /**
4972 * Used to list files ref'd by this one
4973 */
4974 std::map<String, FileRec *> files;
4977 private:
4979 void init()
4980 {
4981 }
4983 void assign(const FileRec &other)
4984 {
4985 type = other.type;
4986 baseName = other.baseName;
4987 suffix = other.suffix;
4988 files = other.files;
4989 }
4991 };
4995 /**
4996 * Simpler dependency record
4997 */
4998 class DepRec
4999 {
5000 public:
5002 /**
5003 * Constructor
5004 */
5005 DepRec()
5006 {init();}
5008 /**
5009 * Copy constructor
5010 */
5011 DepRec(const DepRec &other)
5012 {init(); assign(other);}
5013 /**
5014 * Constructor
5015 */
5016 DepRec(const String &fname)
5017 {init(); name = fname; }
5018 /**
5019 * Assignment operator
5020 */
5021 DepRec &operator=(const DepRec &other)
5022 {init(); assign(other); return *this;}
5025 /**
5026 * Destructor
5027 */
5028 ~DepRec()
5029 {}
5031 /**
5032 * Directory part of the file name
5033 */
5034 String path;
5036 /**
5037 * Base name, without the path and suffix
5038 */
5039 String name;
5041 /**
5042 * Suffix of the source
5043 */
5044 String suffix;
5047 /**
5048 * Used to list files ref'd by this one
5049 */
5050 std::vector<String> files;
5053 private:
5055 void init()
5056 {
5057 }
5059 void assign(const DepRec &other)
5060 {
5061 path = other.path;
5062 name = other.name;
5063 suffix = other.suffix;
5064 files = other.files; //avoid recursion
5065 }
5067 };
5070 class DepTool : public MakeBase
5071 {
5072 public:
5074 /**
5075 * Constructor
5076 */
5077 DepTool()
5078 { init(); }
5080 /**
5081 * Copy constructor
5082 */
5083 DepTool(const DepTool &other)
5084 { init(); assign(other); }
5086 /**
5087 * Assignment operator
5088 */
5089 DepTool &operator=(const DepTool &other)
5090 { init(); assign(other); return *this; }
5093 /**
5094 * Destructor
5095 */
5096 ~DepTool()
5097 {}
5100 /**
5101 * Reset this section of code
5102 */
5103 virtual void init();
5105 /**
5106 * Reset this section of code
5107 */
5108 virtual void assign(const DepTool &other)
5109 {
5110 }
5112 /**
5113 * Sets the source directory which will be scanned
5114 */
5115 virtual void setSourceDirectory(const String &val)
5116 { sourceDir = val; }
5118 /**
5119 * Returns the source directory which will be scanned
5120 */
5121 virtual String getSourceDirectory()
5122 { return sourceDir; }
5124 /**
5125 * Sets the list of files within the directory to analyze
5126 */
5127 virtual void setFileList(const std::vector<String> &list)
5128 { fileList = list; }
5130 /**
5131 * Creates the list of all file names which will be
5132 * candidates for further processing. Reads make.exclude
5133 * to see which files for directories to leave out.
5134 */
5135 virtual bool createFileList();
5138 /**
5139 * Generates the forward dependency list
5140 */
5141 virtual bool generateDependencies();
5144 /**
5145 * Generates the forward dependency list, saving the file
5146 */
5147 virtual bool generateDependencies(const String &);
5150 /**
5151 * Load a dependency file
5152 */
5153 std::vector<DepRec> loadDepFile(const String &fileName);
5155 /**
5156 * Load a dependency file, generating one if necessary
5157 */
5158 std::vector<DepRec> getDepFile(const String &fileName,
5159 bool forceRefresh);
5161 /**
5162 * Save a dependency file
5163 */
5164 bool saveDepFile(const String &fileName);
5167 private:
5170 /**
5171 *
5172 */
5173 void parseName(const String &fullname,
5174 String &path,
5175 String &basename,
5176 String &suffix);
5178 /**
5179 *
5180 */
5181 int get(int pos);
5183 /**
5184 *
5185 */
5186 int skipwhite(int pos);
5188 /**
5189 *
5190 */
5191 int getword(int pos, String &ret);
5193 /**
5194 *
5195 */
5196 bool sequ(int pos, const char *key);
5198 /**
5199 *
5200 */
5201 bool addIncludeFile(FileRec *frec, const String &fname);
5203 /**
5204 *
5205 */
5206 bool scanFile(const String &fname, FileRec *frec);
5208 /**
5209 *
5210 */
5211 bool processDependency(FileRec *ofile, FileRec *include);
5213 /**
5214 *
5215 */
5216 String sourceDir;
5218 /**
5219 *
5220 */
5221 std::vector<String> fileList;
5223 /**
5224 *
5225 */
5226 std::vector<String> directories;
5228 /**
5229 * A list of all files which will be processed for
5230 * dependencies.
5231 */
5232 std::map<String, FileRec *> allFiles;
5234 /**
5235 * The list of .o files, and the
5236 * dependencies upon them.
5237 */
5238 std::map<String, FileRec *> oFiles;
5240 int depFileSize;
5241 char *depFileBuf;
5243 static const int readBufSize = 8192;
5244 char readBuf[8193];//byte larger
5246 };
5252 /**
5253 * Clean up after processing. Called by the destructor, but should
5254 * also be called before the object is reused.
5255 */
5256 void DepTool::init()
5257 {
5258 sourceDir = ".";
5260 fileList.clear();
5261 directories.clear();
5263 //clear output file list
5264 std::map<String, FileRec *>::iterator iter;
5265 for (iter=oFiles.begin(); iter!=oFiles.end() ; iter++)
5266 delete iter->second;
5267 oFiles.clear();
5269 //allFiles actually contains the master copies. delete them
5270 for (iter= allFiles.begin(); iter!=allFiles.end() ; iter++)
5271 delete iter->second;
5272 allFiles.clear();
5274 }
5279 /**
5280 * Parse a full path name into path, base name, and suffix
5281 */
5282 void DepTool::parseName(const String &fullname,
5283 String &path,
5284 String &basename,
5285 String &suffix)
5286 {
5287 if (fullname.size() < 2)
5288 return;
5290 unsigned int pos = fullname.find_last_of('/');
5291 if (pos != fullname.npos && pos<fullname.size()-1)
5292 {
5293 path = fullname.substr(0, pos);
5294 pos++;
5295 basename = fullname.substr(pos, fullname.size()-pos);
5296 }
5297 else
5298 {
5299 path = "";
5300 basename = fullname;
5301 }
5303 pos = basename.find_last_of('.');
5304 if (pos != basename.npos && pos<basename.size()-1)
5305 {
5306 suffix = basename.substr(pos+1, basename.size()-pos-1);
5307 basename = basename.substr(0, pos);
5308 }
5310 //trace("parsename:%s %s %s", path.c_str(),
5311 // basename.c_str(), suffix.c_str());
5312 }
5316 /**
5317 * Generate our internal file list.
5318 */
5319 bool DepTool::createFileList()
5320 {
5322 for (unsigned int i=0 ; i<fileList.size() ; i++)
5323 {
5324 String fileName = fileList[i];
5325 //trace("## FileName:%s", fileName.c_str());
5326 String path;
5327 String basename;
5328 String sfx;
5329 parseName(fileName, path, basename, sfx);
5330 if (sfx == "cpp" || sfx == "c" || sfx == "cxx" ||
5331 sfx == "cc" || sfx == "CC")
5332 {
5333 FileRec *fe = new FileRec(FileRec::CFILE);
5334 fe->path = path;
5335 fe->baseName = basename;
5336 fe->suffix = sfx;
5337 allFiles[fileName] = fe;
5338 }
5339 else if (sfx == "h" || sfx == "hh" ||
5340 sfx == "hpp" || sfx == "hxx")
5341 {
5342 FileRec *fe = new FileRec(FileRec::HFILE);
5343 fe->path = path;
5344 fe->baseName = basename;
5345 fe->suffix = sfx;
5346 allFiles[fileName] = fe;
5347 }
5348 }
5350 if (!listDirectories(sourceDir, "", directories))
5351 return false;
5353 return true;
5354 }
5360 /**
5361 * Get a character from the buffer at pos. If out of range,
5362 * return -1 for safety
5363 */
5364 int DepTool::get(int pos)
5365 {
5366 if (pos>depFileSize)
5367 return -1;
5368 return depFileBuf[pos];
5369 }
5373 /**
5374 * Skip over all whitespace characters beginning at pos. Return
5375 * the position of the first non-whitespace character.
5376 */
5377 int DepTool::skipwhite(int pos)
5378 {
5379 while (pos < depFileSize)
5380 {
5381 int ch = get(pos);
5382 if (ch < 0)
5383 break;
5384 if (!isspace(ch))
5385 break;
5386 pos++;
5387 }
5388 return pos;
5389 }
5392 /**
5393 * Parse the buffer beginning at pos, for a word. Fill
5394 * 'ret' with the result. Return the position after the
5395 * word.
5396 */
5397 int DepTool::getword(int pos, String &ret)
5398 {
5399 while (pos < depFileSize)
5400 {
5401 int ch = get(pos);
5402 if (ch < 0)
5403 break;
5404 if (isspace(ch))
5405 break;
5406 ret.push_back((char)ch);
5407 pos++;
5408 }
5409 return pos;
5410 }
5412 /**
5413 * Return whether the sequence of characters in the buffer
5414 * beginning at pos match the key, for the length of the key
5415 */
5416 bool DepTool::sequ(int pos, const char *key)
5417 {
5418 while (*key)
5419 {
5420 if (*key != get(pos))
5421 return false;
5422 key++; pos++;
5423 }
5424 return true;
5425 }
5429 /**
5430 * Add an include file name to a file record. If the name
5431 * is not found in allFiles explicitly, try prepending include
5432 * directory names to it and try again.
5433 */
5434 bool DepTool::addIncludeFile(FileRec *frec, const String &iname)
5435 {
5436 //# if the name is an exact match to a path name
5437 //# in allFiles, like "myinc.h"
5438 std::map<String, FileRec *>::iterator iter =
5439 allFiles.find(iname);
5440 if (iter != allFiles.end()) //already exists
5441 {
5442 //h file in same dir
5443 FileRec *other = iter->second;
5444 //trace("local: '%s'", iname.c_str());
5445 frec->files[iname] = other;
5446 return true;
5447 }
5448 else
5449 {
5450 //## Ok, it was not found directly
5451 //look in other dirs
5452 std::vector<String>::iterator diter;
5453 for (diter=directories.begin() ;
5454 diter!=directories.end() ; diter++)
5455 {
5456 String dfname = *diter;
5457 dfname.append("/");
5458 dfname.append(iname);
5459 URI fullPathURI(dfname); //normalize path name
5460 String fullPath = fullPathURI.getPath();
5461 if (fullPath[0] == '/')
5462 fullPath = fullPath.substr(1);
5463 //trace("Normalized %s to %s", dfname.c_str(), fullPath.c_str());
5464 iter = allFiles.find(fullPath);
5465 if (iter != allFiles.end())
5466 {
5467 FileRec *other = iter->second;
5468 //trace("other: '%s'", iname.c_str());
5469 frec->files[fullPath] = other;
5470 return true;
5471 }
5472 }
5473 }
5474 return true;
5475 }
5479 /**
5480 * Lightly parse a file to find the #include directives. Do
5481 * a bit of state machine stuff to make sure that the directive
5482 * is valid. (Like not in a comment).
5483 */
5484 bool DepTool::scanFile(const String &fname, FileRec *frec)
5485 {
5486 String fileName;
5487 if (sourceDir.size() > 0)
5488 {
5489 fileName.append(sourceDir);
5490 fileName.append("/");
5491 }
5492 fileName.append(fname);
5493 String nativeName = getNativePath(fileName);
5494 FILE *f = fopen(nativeName.c_str(), "r");
5495 if (!f)
5496 {
5497 error("Could not open '%s' for reading", fname.c_str());
5498 return false;
5499 }
5500 String buf;
5501 while (!feof(f))
5502 {
5503 int len = fread(readBuf, 1, readBufSize, f);
5504 readBuf[len] = '\0';
5505 buf.append(readBuf);
5506 }
5507 fclose(f);
5509 depFileSize = buf.size();
5510 depFileBuf = (char *)buf.c_str();
5511 int pos = 0;
5514 while (pos < depFileSize)
5515 {
5516 //trace("p:%c", get(pos));
5518 //# Block comment
5519 if (get(pos) == '/' && get(pos+1) == '*')
5520 {
5521 pos += 2;
5522 while (pos < depFileSize)
5523 {
5524 if (get(pos) == '*' && get(pos+1) == '/')
5525 {
5526 pos += 2;
5527 break;
5528 }
5529 else
5530 pos++;
5531 }
5532 }
5533 //# Line comment
5534 else if (get(pos) == '/' && get(pos+1) == '/')
5535 {
5536 pos += 2;
5537 while (pos < depFileSize)
5538 {
5539 if (get(pos) == '\n')
5540 {
5541 pos++;
5542 break;
5543 }
5544 else
5545 pos++;
5546 }
5547 }
5548 //# #include! yaay
5549 else if (sequ(pos, "#include"))
5550 {
5551 pos += 8;
5552 pos = skipwhite(pos);
5553 String iname;
5554 pos = getword(pos, iname);
5555 if (iname.size()>2)
5556 {
5557 iname = iname.substr(1, iname.size()-2);
5558 addIncludeFile(frec, iname);
5559 }
5560 }
5561 else
5562 {
5563 pos++;
5564 }
5565 }
5567 return true;
5568 }
5572 /**
5573 * Recursively check include lists to find all files in allFiles to which
5574 * a given file is dependent.
5575 */
5576 bool DepTool::processDependency(FileRec *ofile, FileRec *include)
5577 {
5578 std::map<String, FileRec *>::iterator iter;
5579 for (iter=include->files.begin() ; iter!=include->files.end() ; iter++)
5580 {
5581 String fname = iter->first;
5582 if (ofile->files.find(fname) != ofile->files.end())
5583 {
5584 //trace("file '%s' already seen", fname.c_str());
5585 continue;
5586 }
5587 FileRec *child = iter->second;
5588 ofile->files[fname] = child;
5590 processDependency(ofile, child);
5591 }
5594 return true;
5595 }
5601 /**
5602 * Generate the file dependency list.
5603 */
5604 bool DepTool::generateDependencies()
5605 {
5606 std::map<String, FileRec *>::iterator iter;
5607 //# First pass. Scan for all includes
5608 for (iter=allFiles.begin() ; iter!=allFiles.end() ; iter++)
5609 {
5610 FileRec *frec = iter->second;
5611 if (!scanFile(iter->first, frec))
5612 {
5613 //quit?
5614 }
5615 }
5617 //# Second pass. Scan for all includes
5618 for (iter=allFiles.begin() ; iter!=allFiles.end() ; iter++)
5619 {
5620 FileRec *include = iter->second;
5621 if (include->type == FileRec::CFILE)
5622 {
5623 String cFileName = iter->first;
5624 FileRec *ofile = new FileRec(FileRec::OFILE);
5625 ofile->path = include->path;
5626 ofile->baseName = include->baseName;
5627 ofile->suffix = include->suffix;
5628 String fname = include->path;
5629 if (fname.size()>0)
5630 fname.append("/");
5631 fname.append(include->baseName);
5632 fname.append(".o");
5633 oFiles[fname] = ofile;
5634 //add the .c file first? no, don't
5635 //ofile->files[cFileName] = include;
5637 //trace("ofile:%s", fname.c_str());
5639 processDependency(ofile, include);
5640 }
5641 }
5644 return true;
5645 }
5649 /**
5650 * High-level call to generate deps and optionally save them
5651 */
5652 bool DepTool::generateDependencies(const String &fileName)
5653 {
5654 if (!createFileList())
5655 return false;
5656 if (!generateDependencies())
5657 return false;
5658 if (!saveDepFile(fileName))
5659 return false;
5660 return true;
5661 }
5664 /**
5665 * This saves the dependency cache.
5666 */
5667 bool DepTool::saveDepFile(const String &fileName)
5668 {
5669 time_t tim;
5670 time(&tim);
5672 FILE *f = fopen(fileName.c_str(), "w");
5673 if (!f)
5674 {
5675 trace("cannot open '%s' for writing", fileName.c_str());
5676 }
5677 fprintf(f, "<?xml version='1.0'?>\n");
5678 fprintf(f, "<!--\n");
5679 fprintf(f, "########################################################\n");
5680 fprintf(f, "## File: build.dep\n");
5681 fprintf(f, "## Generated by BuildTool at :%s", ctime(&tim));
5682 fprintf(f, "########################################################\n");
5683 fprintf(f, "-->\n");
5685 fprintf(f, "<dependencies source='%s'>\n\n", sourceDir.c_str());
5686 std::map<String, FileRec *>::iterator iter;
5687 for (iter=oFiles.begin() ; iter!=oFiles.end() ; iter++)
5688 {
5689 FileRec *frec = iter->second;
5690 if (frec->type == FileRec::OFILE)
5691 {
5692 fprintf(f, "<object path='%s' name='%s' suffix='%s'>\n",
5693 frec->path.c_str(), frec->baseName.c_str(), frec->suffix.c_str());
5694 std::map<String, FileRec *>::iterator citer;
5695 for (citer=frec->files.begin() ; citer!=frec->files.end() ; citer++)
5696 {
5697 String cfname = citer->first;
5698 fprintf(f, " <dep name='%s'/>\n", cfname.c_str());
5699 }
5700 fprintf(f, "</object>\n\n");
5701 }
5702 }
5704 fprintf(f, "</dependencies>\n");
5705 fprintf(f, "\n");
5706 fprintf(f, "<!--\n");
5707 fprintf(f, "########################################################\n");
5708 fprintf(f, "## E N D\n");
5709 fprintf(f, "########################################################\n");
5710 fprintf(f, "-->\n");
5712 fclose(f);
5714 return true;
5715 }
5720 /**
5721 * This loads the dependency cache.
5722 */
5723 std::vector<DepRec> DepTool::loadDepFile(const String &depFile)
5724 {
5725 std::vector<DepRec> result;
5727 Parser parser;
5728 Element *root = parser.parseFile(depFile.c_str());
5729 if (!root)
5730 {
5731 //error("Could not open %s for reading", depFile.c_str());
5732 return result;
5733 }
5735 if (root->getChildren().size()==0 ||
5736 root->getChildren()[0]->getName()!="dependencies")
5737 {
5738 error("loadDepFile: main xml element should be <dependencies>");
5739 delete root;
5740 return result;
5741 }
5743 //########## Start parsing
5744 Element *depList = root->getChildren()[0];
5746 std::vector<Element *> objects = depList->getChildren();
5747 for (unsigned int i=0 ; i<objects.size() ; i++)
5748 {
5749 Element *objectElem = objects[i];
5750 String tagName = objectElem->getName();
5751 if (tagName != "object")
5752 {
5753 error("loadDepFile: <dependencies> should have only <object> children");
5754 return result;
5755 }
5757 String objName = objectElem->getAttribute("name");
5758 //trace("object:%s", objName.c_str());
5759 DepRec depObject(objName);
5760 depObject.path = objectElem->getAttribute("path");
5761 depObject.suffix = objectElem->getAttribute("suffix");
5762 //########## DESCRIPTION
5763 std::vector<Element *> depElems = objectElem->getChildren();
5764 for (unsigned int i=0 ; i<depElems.size() ; i++)
5765 {
5766 Element *depElem = depElems[i];
5767 tagName = depElem->getName();
5768 if (tagName != "dep")
5769 {
5770 error("loadDepFile: <object> should have only <dep> children");
5771 return result;
5772 }
5773 String depName = depElem->getAttribute("name");
5774 //trace(" dep:%s", depName.c_str());
5775 depObject.files.push_back(depName);
5776 }
5778 //Insert into the result list, in a sorted manner
5779 bool inserted = false;
5780 std::vector<DepRec>::iterator iter;
5781 for (iter = result.begin() ; iter != result.end() ; iter++)
5782 {
5783 String vpath = iter->path;
5784 vpath.append("/");
5785 vpath.append(iter->name);
5786 String opath = depObject.path;
5787 opath.append("/");
5788 opath.append(depObject.name);
5789 if (vpath > opath)
5790 {
5791 inserted = true;
5792 iter = result.insert(iter, depObject);
5793 break;
5794 }
5795 }
5796 if (!inserted)
5797 result.push_back(depObject);
5798 }
5800 delete root;
5802 return result;
5803 }
5806 /**
5807 * This loads the dependency cache.
5808 */
5809 std::vector<DepRec> DepTool::getDepFile(const String &depFile,
5810 bool forceRefresh)
5811 {
5812 std::vector<DepRec> result;
5813 if (forceRefresh)
5814 {
5815 generateDependencies(depFile);
5816 result = loadDepFile(depFile);
5817 }
5818 else
5819 {
5820 //try once
5821 result = loadDepFile(depFile);
5822 if (result.size() == 0)
5823 {
5824 //fail? try again
5825 generateDependencies(depFile);
5826 result = loadDepFile(depFile);
5827 }
5828 }
5829 return result;
5830 }
5835 //########################################################################
5836 //# T A S K
5837 //########################################################################
5838 //forward decl
5839 class Target;
5840 class Make;
5842 /**
5843 *
5844 */
5845 class Task : public MakeBase
5846 {
5848 public:
5850 typedef enum
5851 {
5852 TASK_NONE,
5853 TASK_CC,
5854 TASK_COPY,
5855 TASK_DELETE,
5856 TASK_JAR,
5857 TASK_JAVAC,
5858 TASK_LINK,
5859 TASK_MAKEFILE,
5860 TASK_MKDIR,
5861 TASK_MSGFMT,
5862 TASK_PKG_CONFIG,
5863 TASK_RANLIB,
5864 TASK_RC,
5865 TASK_SHAREDLIB,
5866 TASK_STATICLIB,
5867 TASK_STRIP,
5868 TASK_TOUCH,
5869 TASK_TSTAMP
5870 } TaskType;
5873 /**
5874 *
5875 */
5876 Task(MakeBase &par) : parent(par)
5877 { init(); }
5879 /**
5880 *
5881 */
5882 Task(const Task &other) : parent(other.parent)
5883 { init(); assign(other); }
5885 /**
5886 *
5887 */
5888 Task &operator=(const Task &other)
5889 { assign(other); return *this; }
5891 /**
5892 *
5893 */
5894 virtual ~Task()
5895 { }
5898 /**
5899 *
5900 */
5901 virtual MakeBase &getParent()
5902 { return parent; }
5904 /**
5905 *
5906 */
5907 virtual int getType()
5908 { return type; }
5910 /**
5911 *
5912 */
5913 virtual void setType(int val)
5914 { type = val; }
5916 /**
5917 *
5918 */
5919 virtual String getName()
5920 { return name; }
5922 /**
5923 *
5924 */
5925 virtual bool execute()
5926 { return true; }
5928 /**
5929 *
5930 */
5931 virtual bool parse(Element *elem)
5932 { return true; }
5934 /**
5935 *
5936 */
5937 Task *createTask(Element *elem, int lineNr);
5940 protected:
5942 void init()
5943 {
5944 type = TASK_NONE;
5945 name = "none";
5946 }
5948 void assign(const Task &other)
5949 {
5950 type = other.type;
5951 name = other.name;
5952 }
5954 String getAttribute(Element *elem, const String &attrName)
5955 {
5956 String str;
5957 return str;
5958 }
5960 MakeBase &parent;
5962 int type;
5964 String name;
5965 };
5969 /**
5970 * This task runs the C/C++ compiler. The compiler is invoked
5971 * for all .c or .cpp files which are newer than their correcsponding
5972 * .o files.
5973 */
5974 class TaskCC : public Task
5975 {
5976 public:
5978 TaskCC(MakeBase &par) : Task(par)
5979 {
5980 type = TASK_CC; name = "cc";
5981 ccCommand = "gcc";
5982 cxxCommand = "g++";
5983 source = ".";
5984 dest = ".";
5985 flags = "";
5986 defines = "";
5987 includes = "";
5988 fileSet.clear();
5989 }
5991 virtual ~TaskCC()
5992 {}
5994 virtual bool needsCompiling(const FileRec &depRec,
5995 const String &src, const String &dest)
5996 {
5997 return false;
5998 }
6000 virtual bool execute()
6001 {
6002 if (!listFiles(parent, fileSet))
6003 return false;
6005 FILE *f = NULL;
6006 f = fopen("compile.lst", "w");
6008 bool refreshCache = false;
6009 String fullName = parent.resolve("build.dep");
6010 if (isNewerThan(parent.getURI().getPath(), fullName))
6011 {
6012 status(" : regenerating C/C++ dependency cache");
6013 refreshCache = true;
6014 }
6016 DepTool depTool;
6017 depTool.setSourceDirectory(source);
6018 depTool.setFileList(fileSet.getFiles());
6019 std::vector<DepRec> deps =
6020 depTool.getDepFile("build.dep", refreshCache);
6022 String incs;
6023 incs.append("-I");
6024 incs.append(parent.resolve("."));
6025 incs.append(" ");
6026 if (includes.size()>0)
6027 {
6028 incs.append(includes);
6029 incs.append(" ");
6030 }
6031 std::set<String> paths;
6032 std::vector<DepRec>::iterator viter;
6033 for (viter=deps.begin() ; viter!=deps.end() ; viter++)
6034 {
6035 DepRec dep = *viter;
6036 if (dep.path.size()>0)
6037 paths.insert(dep.path);
6038 }
6039 if (source.size()>0)
6040 {
6041 incs.append(" -I");
6042 incs.append(parent.resolve(source));
6043 incs.append(" ");
6044 }
6045 std::set<String>::iterator setIter;
6046 for (setIter=paths.begin() ; setIter!=paths.end() ; setIter++)
6047 {
6048 incs.append(" -I");
6049 String dname;
6050 if (source.size()>0)
6051 {
6052 dname.append(source);
6053 dname.append("/");
6054 }
6055 dname.append(*setIter);
6056 incs.append(parent.resolve(dname));
6057 }
6058 std::vector<String> cfiles;
6059 for (viter=deps.begin() ; viter!=deps.end() ; viter++)
6060 {
6061 DepRec dep = *viter;
6063 //## Select command
6064 String sfx = dep.suffix;
6065 String command = ccCommand;
6066 if (sfx == "cpp" || sfx == "cxx" || sfx == "c++" ||
6067 sfx == "cc" || sfx == "CC")
6068 command = cxxCommand;
6070 //## Make paths
6071 String destPath = dest;
6072 String srcPath = source;
6073 if (dep.path.size()>0)
6074 {
6075 destPath.append("/");
6076 destPath.append(dep.path);
6077 srcPath.append("/");
6078 srcPath.append(dep.path);
6079 }
6080 //## Make sure destination directory exists
6081 if (!createDirectory(destPath))
6082 return false;
6084 //## Check whether it needs to be done
6085 String destName;
6086 if (destPath.size()>0)
6087 {
6088 destName.append(destPath);
6089 destName.append("/");
6090 }
6091 destName.append(dep.name);
6092 destName.append(".o");
6093 String destFullName = parent.resolve(destName);
6094 String srcName;
6095 if (srcPath.size()>0)
6096 {
6097 srcName.append(srcPath);
6098 srcName.append("/");
6099 }
6100 srcName.append(dep.name);
6101 srcName.append(".");
6102 srcName.append(dep.suffix);
6103 String srcFullName = parent.resolve(srcName);
6104 bool compileMe = false;
6105 //# First we check if the source is newer than the .o
6106 if (isNewerThan(srcFullName, destFullName))
6107 {
6108 status(" : compile of %s required by %s",
6109 destFullName.c_str(), srcFullName.c_str());
6110 compileMe = true;
6111 }
6112 else
6113 {
6114 //# secondly, we check if any of the included dependencies
6115 //# of the .c/.cpp is newer than the .o
6116 for (unsigned int i=0 ; i<dep.files.size() ; i++)
6117 {
6118 String depName;
6119 if (source.size()>0)
6120 {
6121 depName.append(source);
6122 depName.append("/");
6123 }
6124 depName.append(dep.files[i]);
6125 String depFullName = parent.resolve(depName);
6126 bool depRequires = isNewerThan(depFullName, destFullName);
6127 //trace("%d %s %s\n", depRequires,
6128 // destFullName.c_str(), depFullName.c_str());
6129 if (depRequires)
6130 {
6131 status(" : compile of %s required by %s",
6132 destFullName.c_str(), depFullName.c_str());
6133 compileMe = true;
6134 break;
6135 }
6136 }
6137 }
6138 if (!compileMe)
6139 {
6140 continue;
6141 }
6143 //## Assemble the command
6144 String cmd = command;
6145 cmd.append(" -c ");
6146 cmd.append(flags);
6147 cmd.append(" ");
6148 cmd.append(defines);
6149 cmd.append(" ");
6150 cmd.append(incs);
6151 cmd.append(" ");
6152 cmd.append(srcFullName);
6153 cmd.append(" -o ");
6154 cmd.append(destFullName);
6156 //## Execute the command
6158 String outString, errString;
6159 bool ret = executeCommand(cmd.c_str(), "", outString, errString);
6161 if (f)
6162 {
6163 fprintf(f, "########################### File : %s\n",
6164 srcFullName.c_str());
6165 fprintf(f, "#### COMMAND ###\n");
6166 int col = 0;
6167 for (unsigned int i = 0 ; i < cmd.size() ; i++)
6168 {
6169 char ch = cmd[i];
6170 if (isspace(ch) && col > 63)
6171 {
6172 fputc('\n', f);
6173 col = 0;
6174 }
6175 else
6176 {
6177 fputc(ch, f);
6178 col++;
6179 }
6180 if (col > 76)
6181 {
6182 fputc('\n', f);
6183 col = 0;
6184 }
6185 }
6186 fprintf(f, "\n");
6187 fprintf(f, "#### STDOUT ###\n%s\n", outString.c_str());
6188 fprintf(f, "#### STDERR ###\n%s\n\n", errString.c_str());
6189 }
6190 if (!ret)
6191 {
6192 error("problem compiling: %s", errString.c_str());
6193 return false;
6194 }
6196 }
6198 if (f)
6199 {
6200 fclose(f);
6201 }
6203 return true;
6204 }
6206 virtual bool parse(Element *elem)
6207 {
6208 String s;
6209 if (!parent.getAttribute(elem, "command", s))
6210 return false;
6211 if (s.size()>0) { ccCommand = s; cxxCommand = s; }
6212 if (!parent.getAttribute(elem, "cc", s))
6213 return false;
6214 if (s.size()>0) ccCommand = s;
6215 if (!parent.getAttribute(elem, "cxx", s))
6216 return false;
6217 if (s.size()>0) cxxCommand = s;
6218 if (!parent.getAttribute(elem, "destdir", s))
6219 return false;
6220 if (s.size()>0) dest = s;
6222 std::vector<Element *> children = elem->getChildren();
6223 for (unsigned int i=0 ; i<children.size() ; i++)
6224 {
6225 Element *child = children[i];
6226 String tagName = child->getName();
6227 if (tagName == "flags")
6228 {
6229 if (!parent.getValue(child, flags))
6230 return false;
6231 flags = strip(flags);
6232 }
6233 else if (tagName == "includes")
6234 {
6235 if (!parent.getValue(child, includes))
6236 return false;
6237 includes = strip(includes);
6238 }
6239 else if (tagName == "defines")
6240 {
6241 if (!parent.getValue(child, defines))
6242 return false;
6243 defines = strip(defines);
6244 }
6245 else if (tagName == "fileset")
6246 {
6247 if (!parseFileSet(child, parent, fileSet))
6248 return false;
6249 source = fileSet.getDirectory();
6250 }
6251 }
6253 return true;
6254 }
6256 protected:
6258 String ccCommand;
6259 String cxxCommand;
6260 String source;
6261 String dest;
6262 String flags;
6263 String defines;
6264 String includes;
6265 FileSet fileSet;
6267 };
6271 /**
6272 *
6273 */
6274 class TaskCopy : public Task
6275 {
6276 public:
6278 typedef enum
6279 {
6280 CP_NONE,
6281 CP_TOFILE,
6282 CP_TODIR
6283 } CopyType;
6285 TaskCopy(MakeBase &par) : Task(par)
6286 {
6287 type = TASK_COPY; name = "copy";
6288 cptype = CP_NONE;
6289 verbose = false;
6290 haveFileSet = false;
6291 }
6293 virtual ~TaskCopy()
6294 {}
6296 virtual bool execute()
6297 {
6298 switch (cptype)
6299 {
6300 case CP_TOFILE:
6301 {
6302 if (fileName.size()>0)
6303 {
6304 status(" : %s to %s",
6305 fileName.c_str(), toFileName.c_str());
6306 String fullSource = parent.resolve(fileName);
6307 String fullDest = parent.resolve(toFileName);
6308 //trace("copy %s to file %s", fullSource.c_str(),
6309 // fullDest.c_str());
6310 if (!isRegularFile(fullSource))
6311 {
6312 error("copy : file %s does not exist", fullSource.c_str());
6313 return false;
6314 }
6315 if (!isNewerThan(fullSource, fullDest))
6316 {
6317 status(" : skipped");
6318 return true;
6319 }
6320 if (!copyFile(fullSource, fullDest))
6321 return false;
6322 status(" : 1 file copied");
6323 }
6324 return true;
6325 }
6326 case CP_TODIR:
6327 {
6328 if (haveFileSet)
6329 {
6330 if (!listFiles(parent, fileSet))
6331 return false;
6332 String fileSetDir = fileSet.getDirectory();
6334 status(" : %s to %s",
6335 fileSetDir.c_str(), toDirName.c_str());
6337 int nrFiles = 0;
6338 for (unsigned int i=0 ; i<fileSet.size() ; i++)
6339 {
6340 String fileName = fileSet[i];
6342 String sourcePath;
6343 if (fileSetDir.size()>0)
6344 {
6345 sourcePath.append(fileSetDir);
6346 sourcePath.append("/");
6347 }
6348 sourcePath.append(fileName);
6349 String fullSource = parent.resolve(sourcePath);
6351 //Get the immediate parent directory's base name
6352 String baseFileSetDir = fileSetDir;
6353 unsigned int pos = baseFileSetDir.find_last_of('/');
6354 if (pos!=baseFileSetDir.npos &&
6355 pos < baseFileSetDir.size()-1)
6356 baseFileSetDir =
6357 baseFileSetDir.substr(pos+1,
6358 baseFileSetDir.size());
6359 //Now make the new path
6360 String destPath;
6361 if (toDirName.size()>0)
6362 {
6363 destPath.append(toDirName);
6364 destPath.append("/");
6365 }
6366 if (baseFileSetDir.size()>0)
6367 {
6368 destPath.append(baseFileSetDir);
6369 destPath.append("/");
6370 }
6371 destPath.append(fileName);
6372 String fullDest = parent.resolve(destPath);
6373 //trace("fileName:%s", fileName.c_str());
6374 //trace("copy %s to new dir : %s", fullSource.c_str(),
6375 // fullDest.c_str());
6376 if (!isNewerThan(fullSource, fullDest))
6377 {
6378 //trace("copy skipping %s", fullSource.c_str());
6379 continue;
6380 }
6381 if (!copyFile(fullSource, fullDest))
6382 return false;
6383 nrFiles++;
6384 }
6385 status(" : %d file(s) copied", nrFiles);
6386 }
6387 else //file source
6388 {
6389 //For file->dir we want only the basename of
6390 //the source appended to the dest dir
6391 status(" : %s to %s",
6392 fileName.c_str(), toDirName.c_str());
6393 String baseName = fileName;
6394 unsigned int pos = baseName.find_last_of('/');
6395 if (pos!=baseName.npos && pos<baseName.size()-1)
6396 baseName = baseName.substr(pos+1, baseName.size());
6397 String fullSource = parent.resolve(fileName);
6398 String destPath;
6399 if (toDirName.size()>0)
6400 {
6401 destPath.append(toDirName);
6402 destPath.append("/");
6403 }
6404 destPath.append(baseName);
6405 String fullDest = parent.resolve(destPath);
6406 //trace("copy %s to new dir : %s", fullSource.c_str(),
6407 // fullDest.c_str());
6408 if (!isRegularFile(fullSource))
6409 {
6410 error("copy : file %s does not exist", fullSource.c_str());
6411 return false;
6412 }
6413 if (!isNewerThan(fullSource, fullDest))
6414 {
6415 status(" : skipped");
6416 return true;
6417 }
6418 if (!copyFile(fullSource, fullDest))
6419 return false;
6420 status(" : 1 file copied");
6421 }
6422 return true;
6423 }
6424 }
6425 return true;
6426 }
6429 virtual bool parse(Element *elem)
6430 {
6431 if (!parent.getAttribute(elem, "file", fileName))
6432 return false;
6433 if (!parent.getAttribute(elem, "tofile", toFileName))
6434 return false;
6435 if (toFileName.size() > 0)
6436 cptype = CP_TOFILE;
6437 if (!parent.getAttribute(elem, "todir", toDirName))
6438 return false;
6439 if (toDirName.size() > 0)
6440 cptype = CP_TODIR;
6441 String ret;
6442 if (!parent.getAttribute(elem, "verbose", ret))
6443 return false;
6444 if (ret.size()>0 && !getBool(ret, verbose))
6445 return false;
6447 haveFileSet = false;
6449 std::vector<Element *> children = elem->getChildren();
6450 for (unsigned int i=0 ; i<children.size() ; i++)
6451 {
6452 Element *child = children[i];
6453 String tagName = child->getName();
6454 if (tagName == "fileset")
6455 {
6456 if (!parseFileSet(child, parent, fileSet))
6457 {
6458 error("problem getting fileset");
6459 return false;
6460 }
6461 haveFileSet = true;
6462 }
6463 }
6465 //Perform validity checks
6466 if (fileName.size()>0 && fileSet.size()>0)
6467 {
6468 error("<copy> can only have one of : file= and <fileset>");
6469 return false;
6470 }
6471 if (toFileName.size()>0 && toDirName.size()>0)
6472 {
6473 error("<copy> can only have one of : tofile= or todir=");
6474 return false;
6475 }
6476 if (haveFileSet && toDirName.size()==0)
6477 {
6478 error("a <copy> task with a <fileset> must have : todir=");
6479 return false;
6480 }
6481 if (cptype == CP_TOFILE && fileName.size()==0)
6482 {
6483 error("<copy> tofile= must be associated with : file=");
6484 return false;
6485 }
6486 if (cptype == CP_TODIR && fileName.size()==0 && !haveFileSet)
6487 {
6488 error("<copy> todir= must be associated with : file= or <fileset>");
6489 return false;
6490 }
6492 return true;
6493 }
6495 private:
6497 int cptype;
6498 String fileName;
6499 FileSet fileSet;
6500 String toFileName;
6501 String toDirName;
6502 bool verbose;
6503 bool haveFileSet;
6504 };
6507 /**
6508 *
6509 */
6510 class TaskDelete : public Task
6511 {
6512 public:
6514 typedef enum
6515 {
6516 DEL_FILE,
6517 DEL_DIR,
6518 DEL_FILESET
6519 } DeleteType;
6521 TaskDelete(MakeBase &par) : Task(par)
6522 {
6523 type = TASK_DELETE;
6524 name = "delete";
6525 delType = DEL_FILE;
6526 verbose = false;
6527 quiet = false;
6528 failOnError = true;
6529 }
6531 virtual ~TaskDelete()
6532 {}
6534 virtual bool execute()
6535 {
6536 struct stat finfo;
6537 switch (delType)
6538 {
6539 case DEL_FILE:
6540 {
6541 status(" : %s", fileName.c_str());
6542 String fullName = parent.resolve(fileName);
6543 char *fname = (char *)fullName.c_str();
6544 //does not exist
6545 if (stat(fname, &finfo)<0)
6546 return true;
6547 //exists but is not a regular file
6548 if (!S_ISREG(finfo.st_mode))
6549 {
6550 error("<delete> failed. '%s' exists and is not a regular file",
6551 fname);
6552 return false;
6553 }
6554 if (remove(fname)<0)
6555 {
6556 error("<delete> failed: %s", strerror(errno));
6557 return false;
6558 }
6559 return true;
6560 }
6561 case DEL_DIR:
6562 {
6563 status(" : %s", dirName.c_str());
6564 String fullDir = parent.resolve(dirName);
6565 if (!removeDirectory(fullDir))
6566 return false;
6567 return true;
6568 }
6569 }
6570 return true;
6571 }
6573 virtual bool parse(Element *elem)
6574 {
6575 if (!parent.getAttribute(elem, "file", fileName))
6576 return false;
6577 if (fileName.size() > 0)
6578 delType = DEL_FILE;
6579 if (!parent.getAttribute(elem, "dir", dirName))
6580 return false;
6581 if (dirName.size() > 0)
6582 delType = DEL_DIR;
6583 if (fileName.size()>0 && dirName.size()>0)
6584 {
6585 error("<delete> can have one attribute of file= or dir=");
6586 return false;
6587 }
6588 if (fileName.size()==0 && dirName.size()==0)
6589 {
6590 error("<delete> must have one attribute of file= or dir=");
6591 return false;
6592 }
6593 String ret;
6594 if (!parent.getAttribute(elem, "verbose", ret))
6595 return false;
6596 if (ret.size()>0 && !getBool(ret, verbose))
6597 return false;
6598 if (!parent.getAttribute(elem, "quiet", ret))
6599 return false;
6600 if (ret.size()>0 && !getBool(ret, quiet))
6601 return false;
6602 if (!parent.getAttribute(elem, "failonerror", ret))
6603 return false;
6604 if (ret.size()>0 && !getBool(ret, failOnError))
6605 return false;
6606 return true;
6607 }
6609 private:
6611 int delType;
6612 String dirName;
6613 String fileName;
6614 bool verbose;
6615 bool quiet;
6616 bool failOnError;
6617 };
6620 /**
6621 *
6622 */
6623 class TaskJar : public Task
6624 {
6625 public:
6627 TaskJar(MakeBase &par) : Task(par)
6628 { type = TASK_JAR; name = "jar"; }
6630 virtual ~TaskJar()
6631 {}
6633 virtual bool execute()
6634 {
6635 return true;
6636 }
6638 virtual bool parse(Element *elem)
6639 {
6640 return true;
6641 }
6642 };
6645 /**
6646 *
6647 */
6648 class TaskJavac : public Task
6649 {
6650 public:
6652 TaskJavac(MakeBase &par) : Task(par)
6653 { type = TASK_JAVAC; name = "javac"; }
6655 virtual ~TaskJavac()
6656 {}
6658 virtual bool execute()
6659 {
6660 return true;
6661 }
6663 virtual bool parse(Element *elem)
6664 {
6665 return true;
6666 }
6667 };
6670 /**
6671 *
6672 */
6673 class TaskLink : public Task
6674 {
6675 public:
6677 TaskLink(MakeBase &par) : Task(par)
6678 {
6679 type = TASK_LINK; name = "link";
6680 command = "g++";
6681 doStrip = false;
6682 stripCommand = "strip";
6683 objcopyCommand = "objcopy";
6684 }
6686 virtual ~TaskLink()
6687 {}
6689 virtual bool execute()
6690 {
6691 if (!listFiles(parent, fileSet))
6692 return false;
6693 String fileSetDir = fileSet.getDirectory();
6694 //trace("%d files in %s", fileSet.size(), fileSetDir.c_str());
6695 bool doit = false;
6696 String fullTarget = parent.resolve(fileName);
6697 String cmd = command;
6698 cmd.append(" -o ");
6699 cmd.append(fullTarget);
6700 cmd.append(" ");
6701 cmd.append(flags);
6702 for (unsigned int i=0 ; i<fileSet.size() ; i++)
6703 {
6704 cmd.append(" ");
6705 String obj;
6706 if (fileSetDir.size()>0)
6707 {
6708 obj.append(fileSetDir);
6709 obj.append("/");
6710 }
6711 obj.append(fileSet[i]);
6712 String fullObj = parent.resolve(obj);
6713 String nativeFullObj = getNativePath(fullObj);
6714 cmd.append(nativeFullObj);
6715 //trace("link: tgt:%s obj:%s", fullTarget.c_str(),
6716 // fullObj.c_str());
6717 if (isNewerThan(fullObj, fullTarget))
6718 doit = true;
6719 }
6720 cmd.append(" ");
6721 cmd.append(libs);
6722 if (!doit)
6723 {
6724 //trace("link not needed");
6725 return true;
6726 }
6727 //trace("LINK cmd:%s", cmd.c_str());
6730 String outbuf, errbuf;
6731 if (!executeCommand(cmd.c_str(), "", outbuf, errbuf))
6732 {
6733 error("LINK problem: %s", errbuf.c_str());
6734 return false;
6735 }
6737 if (symFileName.size()>0)
6738 {
6739 String symFullName = parent.resolve(symFileName);
6740 cmd = objcopyCommand;
6741 cmd.append(" --only-keep-debug ");
6742 cmd.append(getNativePath(fullTarget));
6743 cmd.append(" ");
6744 cmd.append(getNativePath(symFullName));
6745 if (!executeCommand(cmd, "", outbuf, errbuf))
6746 {
6747 error("<strip> symbol file failed : %s", errbuf.c_str());
6748 return false;
6749 }
6750 }
6752 if (doStrip)
6753 {
6754 cmd = stripCommand;
6755 cmd.append(" ");
6756 cmd.append(getNativePath(fullTarget));
6757 if (!executeCommand(cmd, "", outbuf, errbuf))
6758 {
6759 error("<strip> failed : %s", errbuf.c_str());
6760 return false;
6761 }
6762 }
6764 return true;
6765 }
6767 virtual bool parse(Element *elem)
6768 {
6769 String s;
6770 if (!parent.getAttribute(elem, "command", s))
6771 return false;
6772 if (s.size()>0)
6773 command = s;
6774 if (!parent.getAttribute(elem, "objcopycommand", s))
6775 return false;
6776 if (s.size()>0)
6777 objcopyCommand = s;
6778 if (!parent.getAttribute(elem, "stripcommand", s))
6779 return false;
6780 if (s.size()>0)
6781 stripCommand = s;
6782 if (!parent.getAttribute(elem, "out", fileName))
6783 return false;
6784 if (!parent.getAttribute(elem, "strip", s))
6785 return false;
6786 if (s.size()>0 && !getBool(s, doStrip))
6787 return false;
6788 if (!parent.getAttribute(elem, "symfile", symFileName))
6789 return false;
6791 std::vector<Element *> children = elem->getChildren();
6792 for (unsigned int i=0 ; i<children.size() ; i++)
6793 {
6794 Element *child = children[i];
6795 String tagName = child->getName();
6796 if (tagName == "fileset")
6797 {
6798 if (!parseFileSet(child, parent, fileSet))
6799 return false;
6800 }
6801 else if (tagName == "flags")
6802 {
6803 if (!parent.getValue(child, flags))
6804 return false;
6805 flags = strip(flags);
6806 }
6807 else if (tagName == "libs")
6808 {
6809 if (!parent.getValue(child, libs))
6810 return false;
6811 libs = strip(libs);
6812 }
6813 }
6814 return true;
6815 }
6817 private:
6819 String command;
6820 String fileName;
6821 String flags;
6822 String libs;
6823 FileSet fileSet;
6824 bool doStrip;
6825 String symFileName;
6826 String stripCommand;
6827 String objcopyCommand;
6829 };
6833 /**
6834 * Create a named directory
6835 */
6836 class TaskMakeFile : public Task
6837 {
6838 public:
6840 TaskMakeFile(MakeBase &par) : Task(par)
6841 { type = TASK_MAKEFILE; name = "makefile"; }
6843 virtual ~TaskMakeFile()
6844 {}
6846 virtual bool execute()
6847 {
6848 status(" : %s", fileName.c_str());
6849 String fullName = parent.resolve(fileName);
6850 if (!isNewerThan(parent.getURI().getPath(), fullName))
6851 {
6852 //trace("skipped <makefile>");
6853 return true;
6854 }
6855 String fullNative = getNativePath(fullName);
6856 //trace("fullName:%s", fullName.c_str());
6857 FILE *f = fopen(fullNative.c_str(), "w");
6858 if (!f)
6859 {
6860 error("<makefile> could not open %s for writing : %s",
6861 fullName.c_str(), strerror(errno));
6862 return false;
6863 }
6864 for (unsigned int i=0 ; i<text.size() ; i++)
6865 fputc(text[i], f);
6866 fputc('\n', f);
6867 fclose(f);
6868 return true;
6869 }
6871 virtual bool parse(Element *elem)
6872 {
6873 if (!parent.getAttribute(elem, "file", fileName))
6874 return false;
6875 if (fileName.size() == 0)
6876 {
6877 error("<makefile> requires 'file=\"filename\"' attribute");
6878 return false;
6879 }
6880 if (!parent.getValue(elem, text))
6881 return false;
6882 text = leftJustify(text);
6883 //trace("dirname:%s", dirName.c_str());
6884 return true;
6885 }
6887 private:
6889 String fileName;
6890 String text;
6891 };
6895 /**
6896 * Create a named directory
6897 */
6898 class TaskMkDir : public Task
6899 {
6900 public:
6902 TaskMkDir(MakeBase &par) : Task(par)
6903 { type = TASK_MKDIR; name = "mkdir"; }
6905 virtual ~TaskMkDir()
6906 {}
6908 virtual bool execute()
6909 {
6910 status(" : %s", dirName.c_str());
6911 String fullDir = parent.resolve(dirName);
6912 //trace("fullDir:%s", fullDir.c_str());
6913 if (!createDirectory(fullDir))
6914 return false;
6915 return true;
6916 }
6918 virtual bool parse(Element *elem)
6919 {
6920 if (!parent.getAttribute(elem, "dir", dirName))
6921 return false;
6922 if (dirName.size() == 0)
6923 {
6924 error("<mkdir> requires 'dir=\"dirname\"' attribute");
6925 return false;
6926 }
6927 return true;
6928 }
6930 private:
6932 String dirName;
6933 };
6937 /**
6938 * Create a named directory
6939 */
6940 class TaskMsgFmt: public Task
6941 {
6942 public:
6944 TaskMsgFmt(MakeBase &par) : Task(par)
6945 {
6946 type = TASK_MSGFMT;
6947 name = "msgfmt";
6948 command = "msgfmt";
6949 owndir = false;
6950 outName = "";
6951 }
6953 virtual ~TaskMsgFmt()
6954 {}
6956 virtual bool execute()
6957 {
6958 if (!listFiles(parent, fileSet))
6959 return false;
6960 String fileSetDir = fileSet.getDirectory();
6962 //trace("msgfmt: %d", fileSet.size());
6963 for (unsigned int i=0 ; i<fileSet.size() ; i++)
6964 {
6965 String fileName = fileSet[i];
6966 if (getSuffix(fileName) != "po")
6967 continue;
6968 String sourcePath;
6969 if (fileSetDir.size()>0)
6970 {
6971 sourcePath.append(fileSetDir);
6972 sourcePath.append("/");
6973 }
6974 sourcePath.append(fileName);
6975 String fullSource = parent.resolve(sourcePath);
6977 String destPath;
6978 if (toDirName.size()>0)
6979 {
6980 destPath.append(toDirName);
6981 destPath.append("/");
6982 }
6983 if (owndir)
6984 {
6985 String subdir = fileName;
6986 unsigned int pos = subdir.find_last_of('.');
6987 if (pos != subdir.npos)
6988 subdir = subdir.substr(0, pos);
6989 destPath.append(subdir);
6990 destPath.append("/");
6991 }
6992 //Pick the output file name
6993 if (outName.size() > 0)
6994 {
6995 destPath.append(outName);
6996 }
6997 else
6998 {
6999 destPath.append(fileName);
7000 destPath[destPath.size()-2] = 'm';
7001 }
7003 String fullDest = parent.resolve(destPath);
7005 if (!isNewerThan(fullSource, fullDest))
7006 {
7007 //trace("skip %s", fullSource.c_str());
7008 continue;
7009 }
7011 String cmd = command;
7012 cmd.append(" ");
7013 cmd.append(fullSource);
7014 cmd.append(" -o ");
7015 cmd.append(fullDest);
7017 int pos = fullDest.find_last_of('/');
7018 if (pos>0)
7019 {
7020 String fullDestPath = fullDest.substr(0, pos);
7021 if (!createDirectory(fullDestPath))
7022 return false;
7023 }
7027 String outString, errString;
7028 if (!executeCommand(cmd.c_str(), "", outString, errString))
7029 {
7030 error("<msgfmt> problem: %s", errString.c_str());
7031 return false;
7032 }
7033 }
7035 return true;
7036 }
7038 virtual bool parse(Element *elem)
7039 {
7040 String s;
7041 if (!parent.getAttribute(elem, "command", s))
7042 return false;
7043 if (s.size()>0)
7044 command = s;
7045 if (!parent.getAttribute(elem, "todir", toDirName))
7046 return false;
7047 if (!parent.getAttribute(elem, "out", outName))
7048 return false;
7049 if (!parent.getAttribute(elem, "owndir", s))
7050 return false;
7051 if (s.size()>0 && !getBool(s, owndir))
7052 return false;
7054 std::vector<Element *> children = elem->getChildren();
7055 for (unsigned int i=0 ; i<children.size() ; i++)
7056 {
7057 Element *child = children[i];
7058 String tagName = child->getName();
7059 if (tagName == "fileset")
7060 {
7061 if (!parseFileSet(child, parent, fileSet))
7062 return false;
7063 }
7064 }
7065 return true;
7066 }
7068 private:
7070 String command;
7071 String toDirName;
7072 String outName;
7073 FileSet fileSet;
7074 bool owndir;
7076 };
7080 /**
7081 * Perform a Package-Config query similar to pkg-config
7082 */
7083 class TaskPkgConfig : public Task
7084 {
7085 public:
7087 typedef enum
7088 {
7089 PKG_CONFIG_QUERY_CFLAGS,
7090 PKG_CONFIG_QUERY_LIBS,
7091 PKG_CONFIG_QUERY_ALL
7092 } QueryTypes;
7094 TaskPkgConfig(MakeBase &par) : Task(par)
7095 {
7096 type = TASK_PKG_CONFIG;
7097 name = "pkg-config";
7098 }
7100 virtual ~TaskPkgConfig()
7101 {}
7103 virtual bool execute()
7104 {
7105 String path = parent.resolve(pkg_config_path);
7106 PkgConfig pkgconfig;
7107 pkgconfig.setPath(path);
7108 pkgconfig.setPrefix(prefix);
7109 if (!pkgconfig.query(pkgName))
7110 {
7111 error("<pkg-config> query failed for '%s", name.c_str());
7112 return false;
7113 }
7114 String ret;
7115 switch (query)
7116 {
7117 case PKG_CONFIG_QUERY_CFLAGS:
7118 {
7119 ret = pkgconfig.getCflags();
7120 break;
7121 }
7122 case PKG_CONFIG_QUERY_LIBS:
7123 {
7124 ret = pkgconfig.getLibs();
7125 break;
7126 }
7127 case PKG_CONFIG_QUERY_ALL:
7128 {
7129 ret = pkgconfig.getAll();
7130 break;
7131 }
7132 default:
7133 {
7134 error("<pkg-config> unhandled query : %d", query);
7135 return false;
7136 }
7138 }
7139 status(" : %s", ret.c_str());
7140 parent.setProperty(propName, ret);
7141 return true;
7142 }
7144 virtual bool parse(Element *elem)
7145 {
7146 String s;
7147 //# NAME
7148 if (!parent.getAttribute(elem, "name", s))
7149 return false;
7150 if (s.size()>0)
7151 pkgName = s;
7152 else
7153 {
7154 error("<pkg-config> requires 'name=\"package\"' attribute");
7155 return false;
7156 }
7158 //# PROPERTY
7159 if (!parent.getAttribute(elem, "property", s))
7160 return false;
7161 if (s.size()>0)
7162 propName = s;
7163 else
7164 {
7165 error("<pkg-config> requires 'property=\"name\"' attribute");
7166 return false;
7167 }
7168 if (parent.hasProperty(propName))
7169 {
7170 error("<pkg-config> property '%s' is already defined",
7171 propName.c_str());
7172 return false;
7173 }
7174 parent.setProperty(propName, "undefined");
7176 //# PATH
7177 if (!parent.getAttribute(elem, "path", s))
7178 return false;
7179 if (s.size()>0)
7180 pkg_config_path = s;
7182 //# PREFIX
7183 if (!parent.getAttribute(elem, "prefix", s))
7184 return false;
7185 if (s.size()>0)
7186 prefix = s;
7188 //# QUERY
7189 if (!parent.getAttribute(elem, "query", s))
7190 return false;
7191 if (s == "cflags")
7192 query = PKG_CONFIG_QUERY_CFLAGS;
7193 else if (s == "libs")
7194 query = PKG_CONFIG_QUERY_LIBS;
7195 else if (s == "both")
7196 query = PKG_CONFIG_QUERY_ALL;
7197 else
7198 {
7199 error("<pkg-config> requires 'query=\"type\"' attribute");
7200 error("where type = cflags, libs, or both");
7201 return false;
7202 }
7203 return true;
7204 }
7206 private:
7208 String pkgName;
7209 String prefix;
7210 String propName;
7211 String pkg_config_path;
7212 int query;
7214 };
7221 /**
7222 * Process an archive to allow random access
7223 */
7224 class TaskRanlib : public Task
7225 {
7226 public:
7228 TaskRanlib(MakeBase &par) : Task(par)
7229 {
7230 type = TASK_RANLIB; name = "ranlib";
7231 command = "ranlib";
7232 }
7234 virtual ~TaskRanlib()
7235 {}
7237 virtual bool execute()
7238 {
7239 String fullName = parent.resolve(fileName);
7240 //trace("fullDir:%s", fullDir.c_str());
7241 String cmd = command;
7242 cmd.append(" ");
7243 cmd.append(fullName);
7244 String outbuf, errbuf;
7245 if (!executeCommand(cmd, "", outbuf, errbuf))
7246 return false;
7247 return true;
7248 }
7250 virtual bool parse(Element *elem)
7251 {
7252 String s;
7253 if (!parent.getAttribute(elem, "command", s))
7254 return false;
7255 if (s.size()>0)
7256 command = s;
7257 if (!parent.getAttribute(elem, "file", fileName))
7258 return false;
7259 if (fileName.size() == 0)
7260 {
7261 error("<ranlib> requires 'file=\"fileNname\"' attribute");
7262 return false;
7263 }
7264 return true;
7265 }
7267 private:
7269 String fileName;
7270 String command;
7271 };
7275 /**
7276 * Run the "ar" command to archive .o's into a .a
7277 */
7278 class TaskRC : public Task
7279 {
7280 public:
7282 TaskRC(MakeBase &par) : Task(par)
7283 {
7284 type = TASK_RC; name = "rc";
7285 command = "windres";
7286 }
7288 virtual ~TaskRC()
7289 {}
7291 virtual bool execute()
7292 {
7293 String fullFile = parent.resolve(fileName);
7294 String fullOut = parent.resolve(outName);
7295 if (!isNewerThan(fullFile, fullOut))
7296 return true;
7297 String cmd = command;
7298 cmd.append(" -o ");
7299 cmd.append(fullOut);
7300 cmd.append(" ");
7301 cmd.append(flags);
7302 cmd.append(" ");
7303 cmd.append(fullFile);
7305 String outString, errString;
7306 if (!executeCommand(cmd.c_str(), "", outString, errString))
7307 {
7308 error("RC problem: %s", errString.c_str());
7309 return false;
7310 }
7311 return true;
7312 }
7314 virtual bool parse(Element *elem)
7315 {
7316 if (!parent.getAttribute(elem, "command", command))
7317 return false;
7318 if (!parent.getAttribute(elem, "file", fileName))
7319 return false;
7320 if (!parent.getAttribute(elem, "out", outName))
7321 return false;
7322 std::vector<Element *> children = elem->getChildren();
7323 for (unsigned int i=0 ; i<children.size() ; i++)
7324 {
7325 Element *child = children[i];
7326 String tagName = child->getName();
7327 if (tagName == "flags")
7328 {
7329 if (!parent.getValue(child, flags))
7330 return false;
7331 }
7332 }
7333 return true;
7334 }
7336 private:
7338 String command;
7339 String flags;
7340 String fileName;
7341 String outName;
7343 };
7347 /**
7348 * Collect .o's into a .so or DLL
7349 */
7350 class TaskSharedLib : public Task
7351 {
7352 public:
7354 TaskSharedLib(MakeBase &par) : Task(par)
7355 {
7356 type = TASK_SHAREDLIB; name = "dll";
7357 command = "dllwrap";
7358 }
7360 virtual ~TaskSharedLib()
7361 {}
7363 virtual bool execute()
7364 {
7365 //trace("###########HERE %d", fileSet.size());
7366 bool doit = false;
7368 String fullOut = parent.resolve(fileName);
7369 //trace("ar fullout: %s", fullOut.c_str());
7371 if (!listFiles(parent, fileSet))
7372 return false;
7373 String fileSetDir = fileSet.getDirectory();
7375 for (unsigned int i=0 ; i<fileSet.size() ; i++)
7376 {
7377 String fname;
7378 if (fileSetDir.size()>0)
7379 {
7380 fname.append(fileSetDir);
7381 fname.append("/");
7382 }
7383 fname.append(fileSet[i]);
7384 String fullName = parent.resolve(fname);
7385 //trace("ar : %s/%s", fullOut.c_str(), fullName.c_str());
7386 if (isNewerThan(fullName, fullOut))
7387 doit = true;
7388 }
7389 //trace("Needs it:%d", doit);
7390 if (!doit)
7391 {
7392 return true;
7393 }
7395 String cmd = "dllwrap";
7396 cmd.append(" -o ");
7397 cmd.append(fullOut);
7398 if (defFileName.size()>0)
7399 {
7400 cmd.append(" --def ");
7401 cmd.append(defFileName);
7402 cmd.append(" ");
7403 }
7404 if (impFileName.size()>0)
7405 {
7406 cmd.append(" --implib ");
7407 cmd.append(impFileName);
7408 cmd.append(" ");
7409 }
7410 for (unsigned int i=0 ; i<fileSet.size() ; i++)
7411 {
7412 String fname;
7413 if (fileSetDir.size()>0)
7414 {
7415 fname.append(fileSetDir);
7416 fname.append("/");
7417 }
7418 fname.append(fileSet[i]);
7419 String fullName = parent.resolve(fname);
7421 cmd.append(" ");
7422 cmd.append(fullName);
7423 }
7424 cmd.append(" ");
7425 cmd.append(libs);
7427 String outString, errString;
7428 if (!executeCommand(cmd.c_str(), "", outString, errString))
7429 {
7430 error("<sharedlib> problem: %s", errString.c_str());
7431 return false;
7432 }
7434 return true;
7435 }
7437 virtual bool parse(Element *elem)
7438 {
7439 if (!parent.getAttribute(elem, "file", fileName))
7440 return false;
7441 if (!parent.getAttribute(elem, "import", impFileName))
7442 return false;
7443 if (!parent.getAttribute(elem, "def", defFileName))
7444 return false;
7446 std::vector<Element *> children = elem->getChildren();
7447 for (unsigned int i=0 ; i<children.size() ; i++)
7448 {
7449 Element *child = children[i];
7450 String tagName = child->getName();
7451 if (tagName == "fileset")
7452 {
7453 if (!parseFileSet(child, parent, fileSet))
7454 return false;
7455 }
7456 else if (tagName == "libs")
7457 {
7458 if (!parent.getValue(child, libs))
7459 return false;
7460 libs = strip(libs);
7461 }
7462 }
7463 return true;
7464 }
7466 private:
7468 String command;
7469 String fileName;
7470 String defFileName;
7471 String impFileName;
7472 FileSet fileSet;
7473 String libs;
7475 };
7479 /**
7480 * Run the "ar" command to archive .o's into a .a
7481 */
7482 class TaskStaticLib : public Task
7483 {
7484 public:
7486 TaskStaticLib(MakeBase &par) : Task(par)
7487 {
7488 type = TASK_STATICLIB; name = "staticlib";
7489 command = "ar crv";
7490 }
7492 virtual ~TaskStaticLib()
7493 {}
7495 virtual bool execute()
7496 {
7497 //trace("###########HERE %d", fileSet.size());
7498 bool doit = false;
7500 String fullOut = parent.resolve(fileName);
7501 //trace("ar fullout: %s", fullOut.c_str());
7503 if (!listFiles(parent, fileSet))
7504 return false;
7505 String fileSetDir = fileSet.getDirectory();
7507 for (unsigned int i=0 ; i<fileSet.size() ; i++)
7508 {
7509 String fname;
7510 if (fileSetDir.size()>0)
7511 {
7512 fname.append(fileSetDir);
7513 fname.append("/");
7514 }
7515 fname.append(fileSet[i]);
7516 String fullName = parent.resolve(fname);
7517 //trace("ar : %s/%s", fullOut.c_str(), fullName.c_str());
7518 if (isNewerThan(fullName, fullOut))
7519 doit = true;
7520 }
7521 //trace("Needs it:%d", doit);
7522 if (!doit)
7523 {
7524 return true;
7525 }
7527 String cmd = command;
7528 cmd.append(" ");
7529 cmd.append(fullOut);
7530 for (unsigned int i=0 ; i<fileSet.size() ; i++)
7531 {
7532 String fname;
7533 if (fileSetDir.size()>0)
7534 {
7535 fname.append(fileSetDir);
7536 fname.append("/");
7537 }
7538 fname.append(fileSet[i]);
7539 String fullName = parent.resolve(fname);
7541 cmd.append(" ");
7542 cmd.append(fullName);
7543 }
7545 String outString, errString;
7546 if (!executeCommand(cmd.c_str(), "", outString, errString))
7547 {
7548 error("<staticlib> problem: %s", errString.c_str());
7549 return false;
7550 }
7552 return true;
7553 }
7556 virtual bool parse(Element *elem)
7557 {
7558 String s;
7559 if (!parent.getAttribute(elem, "command", s))
7560 return false;
7561 if (s.size()>0)
7562 command = s;
7563 if (!parent.getAttribute(elem, "file", fileName))
7564 return false;
7566 std::vector<Element *> children = elem->getChildren();
7567 for (unsigned int i=0 ; i<children.size() ; i++)
7568 {
7569 Element *child = children[i];
7570 String tagName = child->getName();
7571 if (tagName == "fileset")
7572 {
7573 if (!parseFileSet(child, parent, fileSet))
7574 return false;
7575 }
7576 }
7577 return true;
7578 }
7580 private:
7582 String command;
7583 String fileName;
7584 FileSet fileSet;
7586 };
7591 /**
7592 * Strip an executable
7593 */
7594 class TaskStrip : public Task
7595 {
7596 public:
7598 TaskStrip(MakeBase &par) : Task(par)
7599 { type = TASK_STRIP; name = "strip"; }
7601 virtual ~TaskStrip()
7602 {}
7604 virtual bool execute()
7605 {
7606 String fullName = parent.resolve(fileName);
7607 //trace("fullDir:%s", fullDir.c_str());
7608 String cmd;
7609 String outbuf, errbuf;
7611 if (symFileName.size()>0)
7612 {
7613 String symFullName = parent.resolve(symFileName);
7614 cmd = "objcopy --only-keep-debug ";
7615 cmd.append(getNativePath(fullName));
7616 cmd.append(" ");
7617 cmd.append(getNativePath(symFullName));
7618 if (!executeCommand(cmd, "", outbuf, errbuf))
7619 {
7620 error("<strip> symbol file failed : %s", errbuf.c_str());
7621 return false;
7622 }
7623 }
7625 cmd = "strip ";
7626 cmd.append(getNativePath(fullName));
7627 if (!executeCommand(cmd, "", outbuf, errbuf))
7628 {
7629 error("<strip> failed : %s", errbuf.c_str());
7630 return false;
7631 }
7632 return true;
7633 }
7635 virtual bool parse(Element *elem)
7636 {
7637 if (!parent.getAttribute(elem, "file", fileName))
7638 return false;
7639 if (!parent.getAttribute(elem, "symfile", symFileName))
7640 return false;
7641 if (fileName.size() == 0)
7642 {
7643 error("<strip> requires 'file=\"fileName\"' attribute");
7644 return false;
7645 }
7646 return true;
7647 }
7649 private:
7651 String fileName;
7652 String symFileName;
7653 };
7656 /**
7657 *
7658 */
7659 class TaskTouch : public Task
7660 {
7661 public:
7663 TaskTouch(MakeBase &par) : Task(par)
7664 { type = TASK_TOUCH; name = "touch"; }
7666 virtual ~TaskTouch()
7667 {}
7669 virtual bool execute()
7670 {
7671 String fullName = parent.resolve(fileName);
7672 String nativeFile = getNativePath(fullName);
7673 if (!isRegularFile(fullName) && !isDirectory(fullName))
7674 {
7675 // S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH
7676 int ret = creat(nativeFile.c_str(), 0666);
7677 if (ret != 0)
7678 {
7679 error("<touch> could not create '%s' : %s",
7680 nativeFile.c_str(), strerror(ret));
7681 return false;
7682 }
7683 return true;
7684 }
7685 int ret = utime(nativeFile.c_str(), (struct utimbuf *)0);
7686 if (ret != 0)
7687 {
7688 error("<touch> could not update the modification time for '%s' : %s",
7689 nativeFile.c_str(), strerror(ret));
7690 return false;
7691 }
7692 return true;
7693 }
7695 virtual bool parse(Element *elem)
7696 {
7697 //trace("touch parse");
7698 if (!parent.getAttribute(elem, "file", fileName))
7699 return false;
7700 if (fileName.size() == 0)
7701 {
7702 error("<touch> requires 'file=\"fileName\"' attribute");
7703 return false;
7704 }
7705 return true;
7706 }
7708 String fileName;
7709 };
7712 /**
7713 *
7714 */
7715 class TaskTstamp : public Task
7716 {
7717 public:
7719 TaskTstamp(MakeBase &par) : Task(par)
7720 { type = TASK_TSTAMP; name = "tstamp"; }
7722 virtual ~TaskTstamp()
7723 {}
7725 virtual bool execute()
7726 {
7727 return true;
7728 }
7730 virtual bool parse(Element *elem)
7731 {
7732 //trace("tstamp parse");
7733 return true;
7734 }
7735 };
7739 /**
7740 *
7741 */
7742 Task *Task::createTask(Element *elem, int lineNr)
7743 {
7744 String tagName = elem->getName();
7745 //trace("task:%s", tagName.c_str());
7746 Task *task = NULL;
7747 if (tagName == "cc")
7748 task = new TaskCC(parent);
7749 else if (tagName == "copy")
7750 task = new TaskCopy(parent);
7751 else if (tagName == "delete")
7752 task = new TaskDelete(parent);
7753 else if (tagName == "jar")
7754 task = new TaskJar(parent);
7755 else if (tagName == "javac")
7756 task = new TaskJavac(parent);
7757 else if (tagName == "link")
7758 task = new TaskLink(parent);
7759 else if (tagName == "makefile")
7760 task = new TaskMakeFile(parent);
7761 else if (tagName == "mkdir")
7762 task = new TaskMkDir(parent);
7763 else if (tagName == "msgfmt")
7764 task = new TaskMsgFmt(parent);
7765 else if (tagName == "pkg-config")
7766 task = new TaskPkgConfig(parent);
7767 else if (tagName == "ranlib")
7768 task = new TaskRanlib(parent);
7769 else if (tagName == "rc")
7770 task = new TaskRC(parent);
7771 else if (tagName == "sharedlib")
7772 task = new TaskSharedLib(parent);
7773 else if (tagName == "staticlib")
7774 task = new TaskStaticLib(parent);
7775 else if (tagName == "strip")
7776 task = new TaskStrip(parent);
7777 else if (tagName == "touch")
7778 task = new TaskTouch(parent);
7779 else if (tagName == "tstamp")
7780 task = new TaskTstamp(parent);
7781 else
7782 {
7783 error("Unknown task '%s'", tagName.c_str());
7784 return NULL;
7785 }
7787 task->setLine(lineNr);
7789 if (!task->parse(elem))
7790 {
7791 delete task;
7792 return NULL;
7793 }
7794 return task;
7795 }
7799 //########################################################################
7800 //# T A R G E T
7801 //########################################################################
7803 /**
7804 *
7805 */
7806 class Target : public MakeBase
7807 {
7809 public:
7811 /**
7812 *
7813 */
7814 Target(Make &par) : parent(par)
7815 { init(); }
7817 /**
7818 *
7819 */
7820 Target(const Target &other) : parent(other.parent)
7821 { init(); assign(other); }
7823 /**
7824 *
7825 */
7826 Target &operator=(const Target &other)
7827 { init(); assign(other); return *this; }
7829 /**
7830 *
7831 */
7832 virtual ~Target()
7833 { cleanup() ; }
7836 /**
7837 *
7838 */
7839 virtual Make &getParent()
7840 { return parent; }
7842 /**
7843 *
7844 */
7845 virtual String getName()
7846 { return name; }
7848 /**
7849 *
7850 */
7851 virtual void setName(const String &val)
7852 { name = val; }
7854 /**
7855 *
7856 */
7857 virtual String getDescription()
7858 { return description; }
7860 /**
7861 *
7862 */
7863 virtual void setDescription(const String &val)
7864 { description = val; }
7866 /**
7867 *
7868 */
7869 virtual void addDependency(const String &val)
7870 { deps.push_back(val); }
7872 /**
7873 *
7874 */
7875 virtual void parseDependencies(const String &val)
7876 { deps = tokenize(val, ", "); }
7878 /**
7879 *
7880 */
7881 virtual std::vector<String> &getDependencies()
7882 { return deps; }
7884 /**
7885 *
7886 */
7887 virtual String getIf()
7888 { return ifVar; }
7890 /**
7891 *
7892 */
7893 virtual void setIf(const String &val)
7894 { ifVar = val; }
7896 /**
7897 *
7898 */
7899 virtual String getUnless()
7900 { return unlessVar; }
7902 /**
7903 *
7904 */
7905 virtual void setUnless(const String &val)
7906 { unlessVar = val; }
7908 /**
7909 *
7910 */
7911 virtual void addTask(Task *val)
7912 { tasks.push_back(val); }
7914 /**
7915 *
7916 */
7917 virtual std::vector<Task *> &getTasks()
7918 { return tasks; }
7920 private:
7922 void init()
7923 {
7924 }
7926 void cleanup()
7927 {
7928 tasks.clear();
7929 }
7931 void assign(const Target &other)
7932 {
7933 //parent = other.parent;
7934 name = other.name;
7935 description = other.description;
7936 ifVar = other.ifVar;
7937 unlessVar = other.unlessVar;
7938 deps = other.deps;
7939 tasks = other.tasks;
7940 }
7942 Make &parent;
7944 String name;
7946 String description;
7948 String ifVar;
7950 String unlessVar;
7952 std::vector<String> deps;
7954 std::vector<Task *> tasks;
7956 };
7965 //########################################################################
7966 //# M A K E
7967 //########################################################################
7970 /**
7971 *
7972 */
7973 class Make : public MakeBase
7974 {
7976 public:
7978 /**
7979 *
7980 */
7981 Make()
7982 { init(); }
7984 /**
7985 *
7986 */
7987 Make(const Make &other)
7988 { assign(other); }
7990 /**
7991 *
7992 */
7993 Make &operator=(const Make &other)
7994 { assign(other); return *this; }
7996 /**
7997 *
7998 */
7999 virtual ~Make()
8000 { cleanup(); }
8002 /**
8003 *
8004 */
8005 virtual std::map<String, Target> &getTargets()
8006 { return targets; }
8009 /**
8010 *
8011 */
8012 virtual String version()
8013 { return BUILDTOOL_VERSION; }
8015 /**
8016 * Overload a <property>
8017 */
8018 virtual bool specifyProperty(const String &name,
8019 const String &value);
8021 /**
8022 *
8023 */
8024 virtual bool run();
8026 /**
8027 *
8028 */
8029 virtual bool run(const String &target);
8033 private:
8035 /**
8036 *
8037 */
8038 void init();
8040 /**
8041 *
8042 */
8043 void cleanup();
8045 /**
8046 *
8047 */
8048 void assign(const Make &other);
8050 /**
8051 *
8052 */
8053 bool executeTask(Task &task);
8056 /**
8057 *
8058 */
8059 bool executeTarget(Target &target,
8060 std::set<String> &targetsCompleted);
8063 /**
8064 *
8065 */
8066 bool execute();
8068 /**
8069 *
8070 */
8071 bool checkTargetDependencies(Target &prop,
8072 std::vector<String> &depList);
8074 /**
8075 *
8076 */
8077 bool parsePropertyFile(const String &fileName,
8078 const String &prefix);
8080 /**
8081 *
8082 */
8083 bool parseProperty(Element *elem);
8085 /**
8086 *
8087 */
8088 bool parseFile();
8090 /**
8091 *
8092 */
8093 std::vector<String> glob(const String &pattern);
8096 //###############
8097 //# Fields
8098 //###############
8100 String projectName;
8102 String currentTarget;
8104 String defaultTarget;
8106 String specifiedTarget;
8108 String baseDir;
8110 String description;
8112 String envAlias;
8114 //std::vector<Property> properties;
8116 std::map<String, Target> targets;
8118 std::vector<Task *> allTasks;
8120 std::map<String, String> specifiedProperties;
8122 };
8125 //########################################################################
8126 //# C L A S S M A I N T E N A N C E
8127 //########################################################################
8129 /**
8130 *
8131 */
8132 void Make::init()
8133 {
8134 uri = "build.xml";
8135 projectName = "";
8136 currentTarget = "";
8137 defaultTarget = "";
8138 specifiedTarget = "";
8139 baseDir = "";
8140 description = "";
8141 envAlias = "";
8142 properties.clear();
8143 for (unsigned int i = 0 ; i < allTasks.size() ; i++)
8144 delete allTasks[i];
8145 allTasks.clear();
8146 }
8150 /**
8151 *
8152 */
8153 void Make::cleanup()
8154 {
8155 for (unsigned int i = 0 ; i < allTasks.size() ; i++)
8156 delete allTasks[i];
8157 allTasks.clear();
8158 }
8162 /**
8163 *
8164 */
8165 void Make::assign(const Make &other)
8166 {
8167 uri = other.uri;
8168 projectName = other.projectName;
8169 currentTarget = other.currentTarget;
8170 defaultTarget = other.defaultTarget;
8171 specifiedTarget = other.specifiedTarget;
8172 baseDir = other.baseDir;
8173 description = other.description;
8174 properties = other.properties;
8175 }
8179 //########################################################################
8180 //# U T I L I T Y T A S K S
8181 //########################################################################
8183 /**
8184 * Perform a file globbing
8185 */
8186 std::vector<String> Make::glob(const String &pattern)
8187 {
8188 std::vector<String> res;
8189 return res;
8190 }
8193 //########################################################################
8194 //# P U B L I C A P I
8195 //########################################################################
8199 /**
8200 *
8201 */
8202 bool Make::executeTarget(Target &target,
8203 std::set<String> &targetsCompleted)
8204 {
8206 String name = target.getName();
8208 //First get any dependencies for this target
8209 std::vector<String> deps = target.getDependencies();
8210 for (unsigned int i=0 ; i<deps.size() ; i++)
8211 {
8212 String dep = deps[i];
8213 //Did we do it already? Skip
8214 if (targetsCompleted.find(dep)!=targetsCompleted.end())
8215 continue;
8217 std::map<String, Target> &tgts =
8218 target.getParent().getTargets();
8219 std::map<String, Target>::iterator iter =
8220 tgts.find(dep);
8221 if (iter == tgts.end())
8222 {
8223 error("Target '%s' dependency '%s' not found",
8224 name.c_str(), dep.c_str());
8225 return false;
8226 }
8227 Target depTarget = iter->second;
8228 if (!executeTarget(depTarget, targetsCompleted))
8229 {
8230 return false;
8231 }
8232 }
8234 status("## Target : %s : %s", name.c_str(),
8235 target.getDescription().c_str());
8237 //Now let's do the tasks
8238 std::vector<Task *> &tasks = target.getTasks();
8239 for (unsigned int i=0 ; i<tasks.size() ; i++)
8240 {
8241 Task *task = tasks[i];
8242 status("---- task : %s", task->getName().c_str());
8243 if (!task->execute())
8244 {
8245 return false;
8246 }
8247 }
8249 targetsCompleted.insert(name);
8251 return true;
8252 }
8256 /**
8257 * Main execute() method. Start here and work
8258 * up the dependency tree
8259 */
8260 bool Make::execute()
8261 {
8262 status("######## EXECUTE");
8264 //Determine initial target
8265 if (specifiedTarget.size()>0)
8266 {
8267 currentTarget = specifiedTarget;
8268 }
8269 else if (defaultTarget.size()>0)
8270 {
8271 currentTarget = defaultTarget;
8272 }
8273 else
8274 {
8275 error("execute: no specified or default target requested");
8276 return false;
8277 }
8279 std::map<String, Target>::iterator iter =
8280 targets.find(currentTarget);
8281 if (iter == targets.end())
8282 {
8283 error("Initial target '%s' not found",
8284 currentTarget.c_str());
8285 return false;
8286 }
8288 //Now run
8289 Target target = iter->second;
8290 std::set<String> targetsCompleted;
8291 if (!executeTarget(target, targetsCompleted))
8292 {
8293 return false;
8294 }
8296 status("######## EXECUTE COMPLETE");
8297 return true;
8298 }
8303 /**
8304 *
8305 */
8306 bool Make::checkTargetDependencies(Target &target,
8307 std::vector<String> &depList)
8308 {
8309 String tgtName = target.getName().c_str();
8310 depList.push_back(tgtName);
8312 std::vector<String> deps = target.getDependencies();
8313 for (unsigned int i=0 ; i<deps.size() ; i++)
8314 {
8315 String dep = deps[i];
8316 //First thing entered was the starting Target
8317 if (dep == depList[0])
8318 {
8319 error("Circular dependency '%s' found at '%s'",
8320 dep.c_str(), tgtName.c_str());
8321 std::vector<String>::iterator diter;
8322 for (diter=depList.begin() ; diter!=depList.end() ; diter++)
8323 {
8324 error(" %s", diter->c_str());
8325 }
8326 return false;
8327 }
8329 std::map<String, Target> &tgts =
8330 target.getParent().getTargets();
8331 std::map<String, Target>::iterator titer = tgts.find(dep);
8332 if (titer == tgts.end())
8333 {
8334 error("Target '%s' dependency '%s' not found",
8335 tgtName.c_str(), dep.c_str());
8336 return false;
8337 }
8338 if (!checkTargetDependencies(titer->second, depList))
8339 {
8340 return false;
8341 }
8342 }
8343 return true;
8344 }
8350 static int getword(int pos, const String &inbuf, String &result)
8351 {
8352 int p = pos;
8353 int len = (int)inbuf.size();
8354 String val;
8355 while (p < len)
8356 {
8357 char ch = inbuf[p];
8358 if (!isalnum(ch) && ch!='.' && ch!='_')
8359 break;
8360 val.push_back(ch);
8361 p++;
8362 }
8363 result = val;
8364 return p;
8365 }
8370 /**
8371 *
8372 */
8373 bool Make::parsePropertyFile(const String &fileName,
8374 const String &prefix)
8375 {
8376 FILE *f = fopen(fileName.c_str(), "r");
8377 if (!f)
8378 {
8379 error("could not open property file %s", fileName.c_str());
8380 return false;
8381 }
8382 int linenr = 0;
8383 while (!feof(f))
8384 {
8385 char buf[256];
8386 if (!fgets(buf, 255, f))
8387 break;
8388 linenr++;
8389 String s = buf;
8390 s = trim(s);
8391 int len = s.size();
8392 if (len == 0)
8393 continue;
8394 if (s[0] == '#')
8395 continue;
8396 String key;
8397 String val;
8398 int p = 0;
8399 int p2 = getword(p, s, key);
8400 if (p2 <= p)
8401 {
8402 error("property file %s, line %d: expected keyword",
8403 fileName.c_str(), linenr);
8404 return false;
8405 }
8406 if (prefix.size() > 0)
8407 {
8408 key.insert(0, prefix);
8409 }
8411 //skip whitespace
8412 for (p=p2 ; p<len ; p++)
8413 if (!isspace(s[p]))
8414 break;
8416 if (p>=len || s[p]!='=')
8417 {
8418 error("property file %s, line %d: expected '='",
8419 fileName.c_str(), linenr);
8420 return false;
8421 }
8422 p++;
8424 //skip whitespace
8425 for ( ; p<len ; p++)
8426 if (!isspace(s[p]))
8427 break;
8429 /* This way expects a word after the =
8430 p2 = getword(p, s, val);
8431 if (p2 <= p)
8432 {
8433 error("property file %s, line %d: expected value",
8434 fileName.c_str(), linenr);
8435 return false;
8436 }
8437 */
8438 // This way gets the rest of the line after the =
8439 if (p>=len)
8440 {
8441 error("property file %s, line %d: expected value",
8442 fileName.c_str(), linenr);
8443 return false;
8444 }
8445 val = s.substr(p);
8446 if (key.size()==0)
8447 continue;
8448 //allow property to be set, even if val=""
8450 //trace("key:'%s' val:'%s'", key.c_str(), val.c_str());
8451 //See if we wanted to overload this property
8452 std::map<String, String>::iterator iter =
8453 specifiedProperties.find(key);
8454 if (iter!=specifiedProperties.end())
8455 {
8456 val = iter->second;
8457 status("overloading property '%s' = '%s'",
8458 key.c_str(), val.c_str());
8459 }
8460 properties[key] = val;
8461 }
8462 fclose(f);
8463 return true;
8464 }
8469 /**
8470 *
8471 */
8472 bool Make::parseProperty(Element *elem)
8473 {
8474 std::vector<Attribute> &attrs = elem->getAttributes();
8475 for (unsigned int i=0 ; i<attrs.size() ; i++)
8476 {
8477 String attrName = attrs[i].getName();
8478 String attrVal = attrs[i].getValue();
8480 if (attrName == "name")
8481 {
8482 String val;
8483 if (!getAttribute(elem, "value", val))
8484 return false;
8485 if (val.size() > 0)
8486 {
8487 properties[attrVal] = val;
8488 }
8489 else
8490 {
8491 if (!getAttribute(elem, "location", val))
8492 return false;
8493 //let the property exist, even if not defined
8494 properties[attrVal] = val;
8495 }
8496 //See if we wanted to overload this property
8497 std::map<String, String>::iterator iter =
8498 specifiedProperties.find(attrVal);
8499 if (iter != specifiedProperties.end())
8500 {
8501 val = iter->second;
8502 status("overloading property '%s' = '%s'",
8503 attrVal.c_str(), val.c_str());
8504 properties[attrVal] = val;
8505 }
8506 }
8507 else if (attrName == "file")
8508 {
8509 String prefix;
8510 if (!getAttribute(elem, "prefix", prefix))
8511 return false;
8512 if (prefix.size() > 0)
8513 {
8514 if (prefix[prefix.size()-1] != '.')
8515 prefix.push_back('.');
8516 }
8517 if (!parsePropertyFile(attrName, prefix))
8518 return false;
8519 }
8520 else if (attrName == "environment")
8521 {
8522 if (envAlias.size() > 0)
8523 {
8524 error("environment property can only be set once");
8525 return false;
8526 }
8527 envAlias = attrVal;
8528 }
8529 }
8531 return true;
8532 }
8537 /**
8538 *
8539 */
8540 bool Make::parseFile()
8541 {
8542 status("######## PARSE : %s", uri.getPath().c_str());
8544 setLine(0);
8546 Parser parser;
8547 Element *root = parser.parseFile(uri.getNativePath());
8548 if (!root)
8549 {
8550 error("Could not open %s for reading",
8551 uri.getNativePath().c_str());
8552 return false;
8553 }
8555 setLine(root->getLine());
8557 if (root->getChildren().size()==0 ||
8558 root->getChildren()[0]->getName()!="project")
8559 {
8560 error("Main xml element should be <project>");
8561 delete root;
8562 return false;
8563 }
8565 //########## Project attributes
8566 Element *project = root->getChildren()[0];
8567 String s = project->getAttribute("name");
8568 if (s.size() > 0)
8569 projectName = s;
8570 s = project->getAttribute("default");
8571 if (s.size() > 0)
8572 defaultTarget = s;
8573 s = project->getAttribute("basedir");
8574 if (s.size() > 0)
8575 baseDir = s;
8577 //######### PARSE MEMBERS
8578 std::vector<Element *> children = project->getChildren();
8579 for (unsigned int i=0 ; i<children.size() ; i++)
8580 {
8581 Element *elem = children[i];
8582 setLine(elem->getLine());
8583 String tagName = elem->getName();
8585 //########## DESCRIPTION
8586 if (tagName == "description")
8587 {
8588 description = parser.trim(elem->getValue());
8589 }
8591 //######### PROPERTY
8592 else if (tagName == "property")
8593 {
8594 if (!parseProperty(elem))
8595 return false;
8596 }
8598 //######### TARGET
8599 else if (tagName == "target")
8600 {
8601 String tname = elem->getAttribute("name");
8602 String tdesc = elem->getAttribute("description");
8603 String tdeps = elem->getAttribute("depends");
8604 String tif = elem->getAttribute("if");
8605 String tunless = elem->getAttribute("unless");
8606 Target target(*this);
8607 target.setName(tname);
8608 target.setDescription(tdesc);
8609 target.parseDependencies(tdeps);
8610 target.setIf(tif);
8611 target.setUnless(tunless);
8612 std::vector<Element *> telems = elem->getChildren();
8613 for (unsigned int i=0 ; i<telems.size() ; i++)
8614 {
8615 Element *telem = telems[i];
8616 Task breeder(*this);
8617 Task *task = breeder.createTask(telem, telem->getLine());
8618 if (!task)
8619 return false;
8620 allTasks.push_back(task);
8621 target.addTask(task);
8622 }
8624 //Check name
8625 if (tname.size() == 0)
8626 {
8627 error("no name for target");
8628 return false;
8629 }
8630 //Check for duplicate name
8631 if (targets.find(tname) != targets.end())
8632 {
8633 error("target '%s' already defined", tname.c_str());
8634 return false;
8635 }
8636 //more work than targets[tname]=target, but avoids default allocator
8637 targets.insert(std::make_pair<String, Target>(tname, target));
8638 }
8639 //######### none of the above
8640 else
8641 {
8642 error("unknown toplevel tag: <%s>", tagName.c_str());
8643 return false;
8644 }
8646 }
8648 std::map<String, Target>::iterator iter;
8649 for (iter = targets.begin() ; iter!= targets.end() ; iter++)
8650 {
8651 Target tgt = iter->second;
8652 std::vector<String> depList;
8653 if (!checkTargetDependencies(tgt, depList))
8654 {
8655 return false;
8656 }
8657 }
8660 delete root;
8661 status("######## PARSE COMPLETE");
8662 return true;
8663 }
8666 /**
8667 * Overload a <property>
8668 */
8669 bool Make::specifyProperty(const String &name, const String &value)
8670 {
8671 if (specifiedProperties.find(name) != specifiedProperties.end())
8672 {
8673 error("Property %s already specified", name.c_str());
8674 return false;
8675 }
8676 specifiedProperties[name] = value;
8677 return true;
8678 }
8682 /**
8683 *
8684 */
8685 bool Make::run()
8686 {
8687 if (!parseFile())
8688 return false;
8690 if (!execute())
8691 return false;
8693 return true;
8694 }
8699 /**
8700 * Get a formatted MM:SS.sss time elapsed string
8701 */
8702 static String
8703 timeDiffString(struct timeval &x, struct timeval &y)
8704 {
8705 long microsX = x.tv_usec;
8706 long secondsX = x.tv_sec;
8707 long microsY = y.tv_usec;
8708 long secondsY = y.tv_sec;
8709 if (microsX < microsY)
8710 {
8711 microsX += 1000000;
8712 secondsX -= 1;
8713 }
8715 int seconds = (int)(secondsX - secondsY);
8716 int millis = (int)((microsX - microsY)/1000);
8718 int minutes = seconds/60;
8719 seconds -= minutes*60;
8720 char buf[80];
8721 snprintf(buf, 79, "%dm %d.%03ds", minutes, seconds, millis);
8722 String ret = buf;
8723 return ret;
8725 }
8727 /**
8728 *
8729 */
8730 bool Make::run(const String &target)
8731 {
8732 status("####################################################");
8733 status("# %s", version().c_str());
8734 status("####################################################");
8735 struct timeval timeStart, timeEnd;
8736 ::gettimeofday(&timeStart, NULL);
8737 specifiedTarget = target;
8738 if (!run())
8739 return false;
8740 ::gettimeofday(&timeEnd, NULL);
8741 String timeStr = timeDiffString(timeEnd, timeStart);
8742 status("####################################################");
8743 status("# BuildTool Completed : %s", timeStr.c_str());
8744 status("####################################################");
8745 return true;
8746 }
8754 }// namespace buildtool
8755 //########################################################################
8756 //# M A I N
8757 //########################################################################
8759 typedef buildtool::String String;
8761 /**
8762 * Format an error message in printf() style
8763 */
8764 static void error(const char *fmt, ...)
8765 {
8766 va_list ap;
8767 va_start(ap, fmt);
8768 fprintf(stderr, "BuildTool error: ");
8769 vfprintf(stderr, fmt, ap);
8770 fprintf(stderr, "\n");
8771 va_end(ap);
8772 }
8775 static bool parseProperty(const String &s, String &name, String &val)
8776 {
8777 int len = s.size();
8778 int i;
8779 for (i=0 ; i<len ; i++)
8780 {
8781 char ch = s[i];
8782 if (ch == '=')
8783 break;
8784 name.push_back(ch);
8785 }
8786 if (i>=len || s[i]!='=')
8787 {
8788 error("property requires -Dname=value");
8789 return false;
8790 }
8791 i++;
8792 for ( ; i<len ; i++)
8793 {
8794 char ch = s[i];
8795 val.push_back(ch);
8796 }
8797 return true;
8798 }
8801 /**
8802 * Compare a buffer with a key, for the length of the key
8803 */
8804 static bool sequ(const String &buf, const char *key)
8805 {
8806 int len = buf.size();
8807 for (int i=0 ; key[i] && i<len ; i++)
8808 {
8809 if (key[i] != buf[i])
8810 return false;
8811 }
8812 return true;
8813 }
8815 static void usage(int argc, char **argv)
8816 {
8817 printf("usage:\n");
8818 printf(" %s [options] [target]\n", argv[0]);
8819 printf("Options:\n");
8820 printf(" -help, -h print this message\n");
8821 printf(" -version print the version information and exit\n");
8822 printf(" -file <file> use given buildfile\n");
8823 printf(" -f <file> ''\n");
8824 printf(" -D<property>=<value> use value for given property\n");
8825 }
8830 /**
8831 * Parse the command-line args, get our options,
8832 * and run this thing
8833 */
8834 static bool parseOptions(int argc, char **argv)
8835 {
8836 if (argc < 1)
8837 {
8838 error("Cannot parse arguments");
8839 return false;
8840 }
8842 buildtool::Make make;
8844 String target;
8846 //char *progName = argv[0];
8847 for (int i=1 ; i<argc ; i++)
8848 {
8849 String arg = argv[i];
8850 if (arg.size()>1 && arg[0]=='-')
8851 {
8852 if (arg == "-h" || arg == "-help")
8853 {
8854 usage(argc,argv);
8855 return true;
8856 }
8857 else if (arg == "-version")
8858 {
8859 printf("%s", make.version().c_str());
8860 return true;
8861 }
8862 else if (arg == "-f" || arg == "-file")
8863 {
8864 if (i>=argc)
8865 {
8866 usage(argc, argv);
8867 return false;
8868 }
8869 i++; //eat option
8870 make.setURI(argv[i]);
8871 }
8872 else if (arg.size()>2 && sequ(arg, "-D"))
8873 {
8874 String s = arg.substr(2, s.size());
8875 String name, value;
8876 if (!parseProperty(s, name, value))
8877 {
8878 usage(argc, argv);
8879 return false;
8880 }
8881 if (!make.specifyProperty(name, value))
8882 return false;
8883 }
8884 else
8885 {
8886 error("Unknown option:%s", arg.c_str());
8887 return false;
8888 }
8889 }
8890 else
8891 {
8892 if (target.size()>0)
8893 {
8894 error("only one initial target");
8895 usage(argc, argv);
8896 return false;
8897 }
8898 target = arg;
8899 }
8900 }
8902 //We have the options. Now execute them
8903 if (!make.run(target))
8904 return false;
8906 return true;
8907 }
8912 /*
8913 static bool runMake()
8914 {
8915 buildtool::Make make;
8916 if (!make.run())
8917 return false;
8918 return true;
8919 }
8922 static bool pkgConfigTest()
8923 {
8924 buildtool::PkgConfig pkgConfig;
8925 if (!pkgConfig.readFile("gtk+-2.0.pc"))
8926 return false;
8927 return true;
8928 }
8932 static bool depTest()
8933 {
8934 buildtool::DepTool deptool;
8935 deptool.setSourceDirectory("/dev/ink/inkscape/src");
8936 if (!deptool.generateDependencies("build.dep"))
8937 return false;
8938 std::vector<buildtool::FileRec> res =
8939 deptool.loadDepFile("build.dep");
8940 if (res.size() == 0)
8941 return false;
8942 return true;
8943 }
8945 static bool popenTest()
8946 {
8947 buildtool::Make make;
8948 buildtool::String out, err;
8949 bool ret = make.executeCommand("gcc xx.cpp", "", out, err);
8950 printf("Popen test:%d '%s' '%s'\n", ret, out.c_str(), err.c_str());
8951 return true;
8952 }
8955 static bool propFileTest()
8956 {
8957 buildtool::Make make;
8958 make.parsePropertyFile("test.prop", "test.");
8959 return true;
8960 }
8961 */
8963 int main(int argc, char **argv)
8964 {
8966 if (!parseOptions(argc, argv))
8967 return 1;
8968 /*
8969 if (!popenTest())
8970 return 1;
8972 if (!depTest())
8973 return 1;
8974 if (!propFileTest())
8975 return 1;
8976 if (runMake())
8977 return 1;
8978 */
8979 return 0;
8980 }
8983 //########################################################################
8984 //# E N D
8985 //########################################################################