ac7a1a8ae31da6e67d4ebf73ddeeb5cb15038f17
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.6.13, 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; }
2921 protected:
2923 /**
2924 * The path to the file associated with this object
2925 */
2926 URI uri;
2929 /**
2930 * Print a printf()-like formatted error message
2931 */
2932 void error(const char *fmt, ...);
2934 /**
2935 * Print a printf()-like formatted trace message
2936 */
2937 void status(const char *fmt, ...);
2939 /**
2940 * Print a printf()-like formatted trace message
2941 */
2942 void trace(const char *fmt, ...);
2944 /**
2945 * Check if a given string matches a given regex pattern
2946 */
2947 bool regexMatch(const String &str, const String &pattern);
2949 /**
2950 *
2951 */
2952 String getSuffix(const String &fname);
2954 /**
2955 * Break up a string into substrings delimited the characters
2956 * in delimiters. Null-length substrings are ignored
2957 */
2958 std::vector<String> tokenize(const String &val,
2959 const String &delimiters);
2961 /**
2962 * replace runs of whitespace with a space
2963 */
2964 String strip(const String &s);
2966 /**
2967 * remove leading whitespace from each line
2968 */
2969 String leftJustify(const String &s);
2971 /**
2972 * remove leading and trailing whitespace from string
2973 */
2974 String trim(const String &s);
2976 /**
2977 * Return the native format of the canonical
2978 * path which we store
2979 */
2980 String getNativePath(const String &path);
2982 /**
2983 * Execute a shell command. Outbuf is a ref to a string
2984 * to catch the result.
2985 */
2986 bool executeCommand(const String &call,
2987 const String &inbuf,
2988 String &outbuf,
2989 String &errbuf);
2990 /**
2991 * List all directories in a given base and starting directory
2992 * It is usually called like:
2993 * bool ret = listDirectories("src", "", result);
2994 */
2995 bool listDirectories(const String &baseName,
2996 const String &dirname,
2997 std::vector<String> &res);
2999 /**
3000 * Find all files in the named directory
3001 */
3002 bool listFiles(const String &baseName,
3003 const String &dirname,
3004 std::vector<String> &result);
3006 /**
3007 * Perform a listing for a fileset
3008 */
3009 bool listFiles(MakeBase &propRef, FileSet &fileSet);
3011 /**
3012 * Parse a <patternset>
3013 */
3014 bool parsePatternSet(Element *elem,
3015 MakeBase &propRef,
3016 std::vector<String> &includes,
3017 std::vector<String> &excludes);
3019 /**
3020 * Parse a <fileset> entry, and determine which files
3021 * should be included
3022 */
3023 bool parseFileSet(Element *elem,
3024 MakeBase &propRef,
3025 FileSet &fileSet);
3027 /**
3028 * Return this object's property list
3029 */
3030 virtual std::map<String, String> &getProperties()
3031 { return properties; }
3033 /**
3034 * Return a named property if found, else a null string
3035 */
3036 virtual String getProperty(const String &name)
3037 {
3038 String val;
3039 std::map<String, String>::iterator iter;
3040 iter = properties.find(name);
3041 if (iter != properties.end())
3042 val = iter->second;
3043 return val;
3044 }
3047 std::map<String, String> properties;
3049 /**
3050 * Turn 'true' and 'false' into boolean values
3051 */
3052 bool getBool(const String &str, bool &val);
3054 /**
3055 * Create a directory, making intermediate dirs
3056 * if necessary
3057 */
3058 bool createDirectory(const String &dirname);
3060 /**
3061 * Delete a directory and its children if desired
3062 */
3063 bool removeDirectory(const String &dirName);
3065 /**
3066 * Copy a file from one name to another. Perform only if needed
3067 */
3068 bool copyFile(const String &srcFile, const String &destFile);
3070 /**
3071 * Tests if the file exists and is a regular file
3072 */
3073 bool isRegularFile(const String &fileName);
3075 /**
3076 * Tests if the file exists and is a directory
3077 */
3078 bool isDirectory(const String &fileName);
3080 /**
3081 * Tests is the modification date of fileA is newer than fileB
3082 */
3083 bool isNewerThan(const String &fileA, const String &fileB);
3085 private:
3087 /**
3088 * replace variable refs like ${a} with their values
3089 */
3090 bool getSubstitutions(const String &s, String &result);
3092 int line;
3095 };
3100 /**
3101 * Print a printf()-like formatted error message
3102 */
3103 void MakeBase::error(const char *fmt, ...)
3104 {
3105 va_list args;
3106 va_start(args,fmt);
3107 fprintf(stderr, "Make error line %d: ", line);
3108 vfprintf(stderr, fmt, args);
3109 fprintf(stderr, "\n");
3110 va_end(args) ;
3111 }
3115 /**
3116 * Print a printf()-like formatted trace message
3117 */
3118 void MakeBase::status(const char *fmt, ...)
3119 {
3120 va_list args;
3121 va_start(args,fmt);
3122 //fprintf(stdout, " ");
3123 vfprintf(stdout, fmt, args);
3124 fprintf(stdout, "\n");
3125 va_end(args) ;
3126 }
3130 /**
3131 * Resolve another path relative to this one
3132 */
3133 String MakeBase::resolve(const String &otherPath)
3134 {
3135 URI otherURI(otherPath);
3136 URI fullURI = uri.resolve(otherURI);
3137 String ret = fullURI.toString();
3138 return ret;
3139 }
3142 /**
3143 * Print a printf()-like formatted trace message
3144 */
3145 void MakeBase::trace(const char *fmt, ...)
3146 {
3147 va_list args;
3148 va_start(args,fmt);
3149 fprintf(stdout, "Make: ");
3150 vfprintf(stdout, fmt, args);
3151 fprintf(stdout, "\n");
3152 va_end(args) ;
3153 }
3157 /**
3158 * Check if a given string matches a given regex pattern
3159 */
3160 bool MakeBase::regexMatch(const String &str, const String &pattern)
3161 {
3162 const TRexChar *terror = NULL;
3163 const TRexChar *cpat = pattern.c_str();
3164 TRex *expr = trex_compile(cpat, &terror);
3165 if (!expr)
3166 {
3167 if (!terror)
3168 terror = "undefined";
3169 error("compilation error [%s]!\n", terror);
3170 return false;
3171 }
3173 bool ret = true;
3175 const TRexChar *cstr = str.c_str();
3176 if (trex_match(expr, cstr))
3177 {
3178 ret = true;
3179 }
3180 else
3181 {
3182 ret = false;
3183 }
3185 trex_free(expr);
3187 return ret;
3188 }
3190 /**
3191 * Return the suffix, if any, of a file name
3192 */
3193 String MakeBase::getSuffix(const String &fname)
3194 {
3195 if (fname.size() < 2)
3196 return "";
3197 unsigned int pos = fname.find_last_of('.');
3198 if (pos == fname.npos)
3199 return "";
3200 pos++;
3201 String res = fname.substr(pos, fname.size()-pos);
3202 //trace("suffix:%s", res.c_str());
3203 return res;
3204 }
3208 /**
3209 * Break up a string into substrings delimited the characters
3210 * in delimiters. Null-length substrings are ignored
3211 */
3212 std::vector<String> MakeBase::tokenize(const String &str,
3213 const String &delimiters)
3214 {
3216 std::vector<String> res;
3217 char *del = (char *)delimiters.c_str();
3218 String dmp;
3219 for (unsigned int i=0 ; i<str.size() ; i++)
3220 {
3221 char ch = str[i];
3222 char *p = (char *)0;
3223 for (p=del ; *p ; p++)
3224 if (*p == ch)
3225 break;
3226 if (*p)
3227 {
3228 if (dmp.size() > 0)
3229 {
3230 res.push_back(dmp);
3231 dmp.clear();
3232 }
3233 }
3234 else
3235 {
3236 dmp.push_back(ch);
3237 }
3238 }
3239 //Add tail
3240 if (dmp.size() > 0)
3241 {
3242 res.push_back(dmp);
3243 dmp.clear();
3244 }
3246 return res;
3247 }
3251 /**
3252 * replace runs of whitespace with a single space
3253 */
3254 String MakeBase::strip(const String &s)
3255 {
3256 int len = s.size();
3257 String stripped;
3258 for (int i = 0 ; i<len ; i++)
3259 {
3260 char ch = s[i];
3261 if (isspace(ch))
3262 {
3263 stripped.push_back(' ');
3264 for ( ; i<len ; i++)
3265 {
3266 ch = s[i];
3267 if (!isspace(ch))
3268 {
3269 stripped.push_back(ch);
3270 break;
3271 }
3272 }
3273 }
3274 else
3275 {
3276 stripped.push_back(ch);
3277 }
3278 }
3279 return stripped;
3280 }
3282 /**
3283 * remove leading whitespace from each line
3284 */
3285 String MakeBase::leftJustify(const String &s)
3286 {
3287 String out;
3288 int len = s.size();
3289 for (int i = 0 ; i<len ; )
3290 {
3291 char ch;
3292 //Skip to first visible character
3293 while (i<len)
3294 {
3295 ch = s[i];
3296 if (ch == '\n' || ch == '\r'
3297 || !isspace(ch))
3298 break;
3299 i++;
3300 }
3301 //Copy the rest of the line
3302 while (i<len)
3303 {
3304 ch = s[i];
3305 if (ch == '\n' || ch == '\r')
3306 {
3307 if (ch != '\r')
3308 out.push_back('\n');
3309 i++;
3310 break;
3311 }
3312 else
3313 {
3314 out.push_back(ch);
3315 }
3316 i++;
3317 }
3318 }
3319 return out;
3320 }
3323 /**
3324 * Removes whitespace from beginning and end of a string
3325 */
3326 String MakeBase::trim(const String &s)
3327 {
3328 if (s.size() < 1)
3329 return s;
3331 //Find first non-ws char
3332 unsigned int begin = 0;
3333 for ( ; begin < s.size() ; begin++)
3334 {
3335 if (!isspace(s[begin]))
3336 break;
3337 }
3339 //Find first non-ws char, going in reverse
3340 unsigned int end = s.size() - 1;
3341 for ( ; end > begin ; end--)
3342 {
3343 if (!isspace(s[end]))
3344 break;
3345 }
3346 //trace("begin:%d end:%d", begin, end);
3348 String res = s.substr(begin, end-begin+1);
3349 return res;
3350 }
3352 /**
3353 * Return the native format of the canonical
3354 * path which we store
3355 */
3356 String MakeBase::getNativePath(const String &path)
3357 {
3358 #ifdef __WIN32__
3359 String npath;
3360 unsigned int firstChar = 0;
3361 if (path.size() >= 3)
3362 {
3363 if (path[0] == '/' &&
3364 isalpha(path[1]) &&
3365 path[2] == ':')
3366 firstChar++;
3367 }
3368 for (unsigned int i=firstChar ; i<path.size() ; i++)
3369 {
3370 char ch = path[i];
3371 if (ch == '/')
3372 npath.push_back('\\');
3373 else
3374 npath.push_back(ch);
3375 }
3376 return npath;
3377 #else
3378 return path;
3379 #endif
3380 }
3383 #ifdef __WIN32__
3384 #include <tchar.h>
3386 static String win32LastError()
3387 {
3389 DWORD dw = GetLastError();
3391 LPVOID str;
3392 FormatMessage(
3393 FORMAT_MESSAGE_ALLOCATE_BUFFER |
3394 FORMAT_MESSAGE_FROM_SYSTEM,
3395 NULL,
3396 dw,
3397 0,
3398 (LPTSTR) &str,
3399 0, NULL );
3400 LPTSTR p = _tcschr((const char *)str, _T('\r'));
3401 if(p != NULL)
3402 { // lose CRLF
3403 *p = _T('\0');
3404 }
3405 String ret = (char *)str;
3406 LocalFree(str);
3408 return ret;
3409 }
3410 #endif
3414 /**
3415 * Execute a system call, using pipes to send data to the
3416 * program's stdin, and reading stdout and stderr.
3417 */
3418 bool MakeBase::executeCommand(const String &command,
3419 const String &inbuf,
3420 String &outbuf,
3421 String &errbuf)
3422 {
3424 status("============ cmd ============\n%s\n=============================",
3425 command.c_str());
3427 outbuf.clear();
3428 errbuf.clear();
3430 #ifdef __WIN32__
3432 /*
3433 I really hate having win32 code in this program, but the
3434 read buffer in command.com and cmd.exe are just too small
3435 for the large commands we need for compiling and linking.
3436 */
3438 bool ret = true;
3440 //# Allocate a separate buffer for safety
3441 char *paramBuf = new char[command.size() + 1];
3442 if (!paramBuf)
3443 {
3444 error("executeCommand cannot allocate command buffer");
3445 return false;
3446 }
3447 strcpy(paramBuf, (char *)command.c_str());
3449 //# Go to http://msdn2.microsoft.com/en-us/library/ms682499.aspx
3450 //# to see how Win32 pipes work
3452 //# Create pipes
3453 SECURITY_ATTRIBUTES saAttr;
3454 saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
3455 saAttr.bInheritHandle = TRUE;
3456 saAttr.lpSecurityDescriptor = NULL;
3457 HANDLE stdinRead, stdinWrite;
3458 HANDLE stdoutRead, stdoutWrite;
3459 HANDLE stderrRead, stderrWrite;
3460 if (!CreatePipe(&stdinRead, &stdinWrite, &saAttr, 0))
3461 {
3462 error("executeProgram: could not create pipe");
3463 delete[] paramBuf;
3464 return false;
3465 }
3466 SetHandleInformation(stdinWrite, HANDLE_FLAG_INHERIT, 0);
3467 if (!CreatePipe(&stdoutRead, &stdoutWrite, &saAttr, 0))
3468 {
3469 error("executeProgram: could not create pipe");
3470 delete[] paramBuf;
3471 return false;
3472 }
3473 SetHandleInformation(stdoutRead, HANDLE_FLAG_INHERIT, 0);
3474 if (!CreatePipe(&stderrRead, &stderrWrite, &saAttr, 0))
3475 {
3476 error("executeProgram: could not create pipe");
3477 delete[] paramBuf;
3478 return false;
3479 }
3480 SetHandleInformation(stderrRead, HANDLE_FLAG_INHERIT, 0);
3482 // Create the process
3483 STARTUPINFO siStartupInfo;
3484 PROCESS_INFORMATION piProcessInfo;
3485 memset(&siStartupInfo, 0, sizeof(siStartupInfo));
3486 memset(&piProcessInfo, 0, sizeof(piProcessInfo));
3487 siStartupInfo.cb = sizeof(siStartupInfo);
3488 siStartupInfo.hStdError = stderrWrite;
3489 siStartupInfo.hStdOutput = stdoutWrite;
3490 siStartupInfo.hStdInput = stdinRead;
3491 siStartupInfo.dwFlags |= STARTF_USESTDHANDLES;
3493 if (!CreateProcess(NULL, paramBuf, NULL, NULL, true,
3494 0, NULL, NULL, &siStartupInfo,
3495 &piProcessInfo))
3496 {
3497 error("executeCommand : could not create process : %s",
3498 win32LastError().c_str());
3499 ret = false;
3500 }
3502 delete[] paramBuf;
3504 DWORD bytesWritten;
3505 if (inbuf.size()>0 &&
3506 !WriteFile(stdinWrite, inbuf.c_str(), inbuf.size(),
3507 &bytesWritten, NULL))
3508 {
3509 error("executeCommand: could not write to pipe");
3510 return false;
3511 }
3512 if (!CloseHandle(stdinWrite))
3513 {
3514 error("executeCommand: could not close write pipe");
3515 return false;
3516 }
3517 if (!CloseHandle(stdoutWrite))
3518 {
3519 error("executeCommand: could not close read pipe");
3520 return false;
3521 }
3522 if (!CloseHandle(stderrWrite))
3523 {
3524 error("executeCommand: could not close read pipe");
3525 return false;
3526 }
3528 bool lastLoop = false;
3529 while (true)
3530 {
3531 DWORD avail;
3532 DWORD bytesRead;
3533 char readBuf[4096];
3535 //trace("## stderr");
3536 PeekNamedPipe(stderrRead, NULL, 0, NULL, &avail, NULL);
3537 if (avail > 0)
3538 {
3539 bytesRead = 0;
3540 if (avail>4096) avail = 4096;
3541 ReadFile(stderrRead, readBuf, avail, &bytesRead, NULL);
3542 if (bytesRead > 0)
3543 {
3544 for (unsigned int i=0 ; i<bytesRead ; i++)
3545 errbuf.push_back(readBuf[i]);
3546 }
3547 }
3549 //trace("## stdout");
3550 PeekNamedPipe(stdoutRead, NULL, 0, NULL, &avail, NULL);
3551 if (avail > 0)
3552 {
3553 bytesRead = 0;
3554 if (avail>4096) avail = 4096;
3555 ReadFile(stdoutRead, readBuf, avail, &bytesRead, NULL);
3556 if (bytesRead > 0)
3557 {
3558 for (unsigned int i=0 ; i<bytesRead ; i++)
3559 outbuf.push_back(readBuf[i]);
3560 }
3561 }
3563 //Was this the final check after program done?
3564 if (lastLoop)
3565 break;
3567 DWORD exitCode;
3568 GetExitCodeProcess(piProcessInfo.hProcess, &exitCode);
3569 if (exitCode != STILL_ACTIVE)
3570 lastLoop = true;
3572 Sleep(10);
3573 }
3574 //trace("outbuf:%s", outbuf.c_str());
3575 if (!CloseHandle(stdoutRead))
3576 {
3577 error("executeCommand: could not close read pipe");
3578 return false;
3579 }
3580 if (!CloseHandle(stderrRead))
3581 {
3582 error("executeCommand: could not close read pipe");
3583 return false;
3584 }
3586 DWORD exitCode;
3587 GetExitCodeProcess(piProcessInfo.hProcess, &exitCode);
3588 //trace("exit code:%d", exitCode);
3589 if (exitCode != 0)
3590 {
3591 ret = false;
3592 }
3594 CloseHandle(piProcessInfo.hProcess);
3595 CloseHandle(piProcessInfo.hThread);
3597 return ret;
3599 #else //do it unix-style
3601 String s;
3602 FILE *f = popen(command.c_str(), "r");
3603 int errnum = 0;
3604 if (f)
3605 {
3606 while (true)
3607 {
3608 int ch = fgetc(f);
3609 if (ch < 0)
3610 break;
3611 s.push_back((char)ch);
3612 }
3613 errnum = pclose(f);
3614 }
3615 outbuf = s;
3616 if (errnum != 0)
3617 {
3618 error("exec of command '%s' failed : %s",
3619 command.c_str(), strerror(errno));
3620 return false;
3621 }
3622 else
3623 return true;
3625 #endif
3626 }
3631 bool MakeBase::listDirectories(const String &baseName,
3632 const String &dirName,
3633 std::vector<String> &res)
3634 {
3635 res.push_back(dirName);
3636 String fullPath = baseName;
3637 if (dirName.size()>0)
3638 {
3639 fullPath.append("/");
3640 fullPath.append(dirName);
3641 }
3642 DIR *dir = opendir(fullPath.c_str());
3643 while (true)
3644 {
3645 struct dirent *de = readdir(dir);
3646 if (!de)
3647 break;
3649 //Get the directory member name
3650 String s = de->d_name;
3651 if (s.size() == 0 || s[0] == '.')
3652 continue;
3653 String childName = dirName;
3654 childName.append("/");
3655 childName.append(s);
3657 String fullChildPath = baseName;
3658 fullChildPath.append("/");
3659 fullChildPath.append(childName);
3660 struct stat finfo;
3661 String childNative = getNativePath(fullChildPath);
3662 if (stat(childNative.c_str(), &finfo)<0)
3663 {
3664 error("cannot stat file:%s", childNative.c_str());
3665 }
3666 else if (S_ISDIR(finfo.st_mode))
3667 {
3668 //trace("directory: %s", childName.c_str());
3669 if (!listDirectories(baseName, childName, res))
3670 return false;
3671 }
3672 }
3673 closedir(dir);
3675 return true;
3676 }
3679 bool MakeBase::listFiles(const String &baseDir,
3680 const String &dirName,
3681 std::vector<String> &res)
3682 {
3683 String fullDir = baseDir;
3684 if (dirName.size()>0)
3685 {
3686 fullDir.append("/");
3687 fullDir.append(dirName);
3688 }
3689 String dirNative = getNativePath(fullDir);
3691 std::vector<String> subdirs;
3692 DIR *dir = opendir(dirNative.c_str());
3693 if (!dir)
3694 {
3695 error("Could not open directory %s : %s",
3696 dirNative.c_str(), strerror(errno));
3697 return false;
3698 }
3699 while (true)
3700 {
3701 struct dirent *de = readdir(dir);
3702 if (!de)
3703 break;
3705 //Get the directory member name
3706 String s = de->d_name;
3707 if (s.size() == 0 || s[0] == '.')
3708 continue;
3709 String childName;
3710 if (dirName.size()>0)
3711 {
3712 childName.append(dirName);
3713 childName.append("/");
3714 }
3715 childName.append(s);
3716 String fullChild = baseDir;
3717 fullChild.append("/");
3718 fullChild.append(childName);
3720 if (isDirectory(fullChild))
3721 {
3722 //trace("directory: %s", childName.c_str());
3723 if (!listFiles(baseDir, childName, res))
3724 return false;
3725 continue;
3726 }
3727 else if (!isRegularFile(fullChild))
3728 {
3729 error("unknown file:%s", childName.c_str());
3730 return false;
3731 }
3733 //all done!
3734 res.push_back(childName);
3736 }
3737 closedir(dir);
3739 return true;
3740 }
3743 bool MakeBase::listFiles(MakeBase &propRef, FileSet &fileSet)
3744 {
3745 String baseDir = propRef.resolve(fileSet.getDirectory());
3746 std::vector<String> fileList;
3747 if (!listFiles(baseDir, "", fileList))
3748 return false;
3750 std::vector<String> includes = fileSet.getIncludes();
3751 std::vector<String> excludes = fileSet.getExcludes();
3753 std::vector<String> incs;
3754 std::vector<String>::iterator iter;
3756 std::sort(fileList.begin(), fileList.end());
3758 //If there are <includes>, then add files to the output
3759 //in the order of the include list
3760 if (includes.size()==0)
3761 incs = fileList;
3762 else
3763 {
3764 for (iter = includes.begin() ; iter != includes.end() ; iter++)
3765 {
3766 String pattern = *iter;
3767 std::vector<String>::iterator siter;
3768 for (siter = fileList.begin() ; siter != fileList.end() ; siter++)
3769 {
3770 String s = *siter;
3771 if (regexMatch(s, pattern))
3772 {
3773 //trace("INCLUDED:%s", s.c_str());
3774 incs.push_back(s);
3775 }
3776 }
3777 }
3778 }
3780 //Now trim off the <excludes>
3781 std::vector<String> res;
3782 for (iter = incs.begin() ; iter != incs.end() ; iter++)
3783 {
3784 String s = *iter;
3785 bool skipme = false;
3786 std::vector<String>::iterator siter;
3787 for (siter = excludes.begin() ; siter != excludes.end() ; siter++)
3788 {
3789 String pattern = *siter;
3790 if (regexMatch(s, pattern))
3791 {
3792 //trace("EXCLUDED:%s", s.c_str());
3793 skipme = true;
3794 break;
3795 }
3796 }
3797 if (!skipme)
3798 res.push_back(s);
3799 }
3801 fileSet.setFiles(res);
3803 return true;
3804 }
3810 bool MakeBase::getSubstitutions(const String &str, String &result)
3811 {
3812 String s = trim(str);
3813 int len = (int)s.size();
3814 String val;
3815 for (int i=0 ; i<len ; i++)
3816 {
3817 char ch = s[i];
3818 if (ch == '$' && s[i+1] == '{')
3819 {
3820 String varname;
3821 int j = i+2;
3822 for ( ; j<len ; j++)
3823 {
3824 ch = s[j];
3825 if (ch == '$' && s[j+1] == '{')
3826 {
3827 error("attribute %s cannot have nested variable references",
3828 s.c_str());
3829 return false;
3830 }
3831 else if (ch == '}')
3832 {
3833 std::map<String, String>::iterator iter;
3834 iter = properties.find(trim(varname));
3835 if (iter != properties.end())
3836 {
3837 val.append(iter->second);
3838 }
3839 else
3840 {
3841 error("property ${%s} not found", varname.c_str());
3842 return false;
3843 }
3844 break;
3845 }
3846 else
3847 {
3848 varname.push_back(ch);
3849 }
3850 }
3851 i = j;
3852 }
3853 else
3854 {
3855 val.push_back(ch);
3856 }
3857 }
3858 result = val;
3859 return true;
3860 }
3863 bool MakeBase::getAttribute(Element *elem, const String &name,
3864 String &result)
3865 {
3866 String s = elem->getAttribute(name);
3867 return getSubstitutions(s, result);
3868 }
3871 bool MakeBase::getValue(Element *elem, String &result)
3872 {
3873 String s = elem->getValue();
3874 //Replace all runs of whitespace with a single space
3875 return getSubstitutions(s, result);
3876 }
3879 /**
3880 * Turn 'true' and 'false' into boolean values
3881 */
3882 bool MakeBase::getBool(const String &str, bool &val)
3883 {
3884 if (str == "true")
3885 val = true;
3886 else if (str == "false")
3887 val = false;
3888 else
3889 {
3890 error("expected 'true' or 'false'. found '%s'", str.c_str());
3891 return false;
3892 }
3893 return true;
3894 }
3899 /**
3900 * Parse a <patternset> entry
3901 */
3902 bool MakeBase::parsePatternSet(Element *elem,
3903 MakeBase &propRef,
3904 std::vector<String> &includes,
3905 std::vector<String> &excludes
3906 )
3907 {
3908 std::vector<Element *> children = elem->getChildren();
3909 for (unsigned int i=0 ; i<children.size() ; i++)
3910 {
3911 Element *child = children[i];
3912 String tagName = child->getName();
3913 if (tagName == "exclude")
3914 {
3915 String fname;
3916 if (!propRef.getAttribute(child, "name", fname))
3917 return false;
3918 //trace("EXCLUDE: %s", fname.c_str());
3919 excludes.push_back(fname);
3920 }
3921 else if (tagName == "include")
3922 {
3923 String fname;
3924 if (!propRef.getAttribute(child, "name", fname))
3925 return false;
3926 //trace("INCLUDE: %s", fname.c_str());
3927 includes.push_back(fname);
3928 }
3929 }
3931 return true;
3932 }
3937 /**
3938 * Parse a <fileset> entry, and determine which files
3939 * should be included
3940 */
3941 bool MakeBase::parseFileSet(Element *elem,
3942 MakeBase &propRef,
3943 FileSet &fileSet)
3944 {
3945 String name = elem->getName();
3946 if (name != "fileset")
3947 {
3948 error("expected <fileset>");
3949 return false;
3950 }
3953 std::vector<String> includes;
3954 std::vector<String> excludes;
3956 //A fileset has one implied patternset
3957 if (!parsePatternSet(elem, propRef, includes, excludes))
3958 {
3959 return false;
3960 }
3961 //Look for child tags, including more patternsets
3962 std::vector<Element *> children = elem->getChildren();
3963 for (unsigned int i=0 ; i<children.size() ; i++)
3964 {
3965 Element *child = children[i];
3966 String tagName = child->getName();
3967 if (tagName == "patternset")
3968 {
3969 if (!parsePatternSet(child, propRef, includes, excludes))
3970 {
3971 return false;
3972 }
3973 }
3974 }
3976 String dir;
3977 //Now do the stuff
3978 //Get the base directory for reading file names
3979 if (!propRef.getAttribute(elem, "dir", dir))
3980 return false;
3982 fileSet.setDirectory(dir);
3983 fileSet.setIncludes(includes);
3984 fileSet.setExcludes(excludes);
3986 /*
3987 std::vector<String> fileList;
3988 if (dir.size() > 0)
3989 {
3990 String baseDir = propRef.resolve(dir);
3991 if (!listFiles(baseDir, "", includes, excludes, fileList))
3992 return false;
3993 }
3994 std::sort(fileList.begin(), fileList.end());
3995 result = fileList;
3996 */
3999 /*
4000 for (unsigned int i=0 ; i<result.size() ; i++)
4001 {
4002 trace("RES:%s", result[i].c_str());
4003 }
4004 */
4007 return true;
4008 }
4012 /**
4013 * Create a directory, making intermediate dirs
4014 * if necessary
4015 */
4016 bool MakeBase::createDirectory(const String &dirname)
4017 {
4018 //trace("## createDirectory: %s", dirname.c_str());
4019 //## first check if it exists
4020 struct stat finfo;
4021 String nativeDir = getNativePath(dirname);
4022 char *cnative = (char *) nativeDir.c_str();
4023 #ifdef __WIN32__
4024 if (strlen(cnative)==2 && cnative[1]==':')
4025 return true;
4026 #endif
4027 if (stat(cnative, &finfo)==0)
4028 {
4029 if (!S_ISDIR(finfo.st_mode))
4030 {
4031 error("mkdir: file %s exists but is not a directory",
4032 cnative);
4033 return false;
4034 }
4035 else //exists
4036 {
4037 return true;
4038 }
4039 }
4041 //## 2: pull off the last path segment, if any,
4042 //## to make the dir 'above' this one, if necessary
4043 unsigned int pos = dirname.find_last_of('/');
4044 if (pos>0 && pos != dirname.npos)
4045 {
4046 String subpath = dirname.substr(0, pos);
4047 //A letter root (c:) ?
4048 if (!createDirectory(subpath))
4049 return false;
4050 }
4052 //## 3: now make
4053 #ifdef __WIN32__
4054 if (mkdir(cnative)<0)
4055 #else
4056 if (mkdir(cnative, S_IRWXU | S_IRWXG | S_IRWXO)<0)
4057 #endif
4058 {
4059 error("cannot make directory '%s' : %s",
4060 cnative, strerror(errno));
4061 return false;
4062 }
4064 return true;
4065 }
4068 /**
4069 * Remove a directory recursively
4070 */
4071 bool MakeBase::removeDirectory(const String &dirName)
4072 {
4073 char *dname = (char *)dirName.c_str();
4075 DIR *dir = opendir(dname);
4076 if (!dir)
4077 {
4078 //# Let this fail nicely.
4079 return true;
4080 //error("error opening directory %s : %s", dname, strerror(errno));
4081 //return false;
4082 }
4084 while (true)
4085 {
4086 struct dirent *de = readdir(dir);
4087 if (!de)
4088 break;
4090 //Get the directory member name
4091 String s = de->d_name;
4092 if (s.size() == 0 || s[0] == '.')
4093 continue;
4094 String childName;
4095 if (dirName.size() > 0)
4096 {
4097 childName.append(dirName);
4098 childName.append("/");
4099 }
4100 childName.append(s);
4103 struct stat finfo;
4104 String childNative = getNativePath(childName);
4105 char *cnative = (char *)childNative.c_str();
4106 if (stat(cnative, &finfo)<0)
4107 {
4108 error("cannot stat file:%s", cnative);
4109 }
4110 else if (S_ISDIR(finfo.st_mode))
4111 {
4112 //trace("DEL dir: %s", childName.c_str());
4113 if (!removeDirectory(childName))
4114 {
4115 return false;
4116 }
4117 }
4118 else if (!S_ISREG(finfo.st_mode))
4119 {
4120 //trace("not regular: %s", cnative);
4121 }
4122 else
4123 {
4124 //trace("DEL file: %s", childName.c_str());
4125 if (remove(cnative)<0)
4126 {
4127 error("error deleting %s : %s",
4128 cnative, strerror(errno));
4129 return false;
4130 }
4131 }
4132 }
4133 closedir(dir);
4135 //Now delete the directory
4136 String native = getNativePath(dirName);
4137 if (rmdir(native.c_str())<0)
4138 {
4139 error("could not delete directory %s : %s",
4140 native.c_str() , strerror(errno));
4141 return false;
4142 }
4144 return true;
4146 }
4149 /**
4150 * Copy a file from one name to another. Perform only if needed
4151 */
4152 bool MakeBase::copyFile(const String &srcFile, const String &destFile)
4153 {
4154 //# 1 Check up-to-date times
4155 String srcNative = getNativePath(srcFile);
4156 struct stat srcinfo;
4157 if (stat(srcNative.c_str(), &srcinfo)<0)
4158 {
4159 error("source file %s for copy does not exist",
4160 srcNative.c_str());
4161 return false;
4162 }
4164 String destNative = getNativePath(destFile);
4165 struct stat destinfo;
4166 if (stat(destNative.c_str(), &destinfo)==0)
4167 {
4168 if (destinfo.st_mtime >= srcinfo.st_mtime)
4169 return true;
4170 }
4172 //# 2 prepare a destination directory if necessary
4173 unsigned int pos = destFile.find_last_of('/');
4174 if (pos != destFile.npos)
4175 {
4176 String subpath = destFile.substr(0, pos);
4177 if (!createDirectory(subpath))
4178 return false;
4179 }
4181 //# 3 do the data copy
4182 #ifndef __WIN32__
4184 FILE *srcf = fopen(srcNative.c_str(), "rb");
4185 if (!srcf)
4186 {
4187 error("copyFile cannot open '%s' for reading", srcNative.c_str());
4188 return false;
4189 }
4190 FILE *destf = fopen(destNative.c_str(), "wb");
4191 if (!destf)
4192 {
4193 error("copyFile cannot open %s for writing", srcNative.c_str());
4194 return false;
4195 }
4197 while (!feof(srcf))
4198 {
4199 int ch = fgetc(srcf);
4200 if (ch<0)
4201 break;
4202 fputc(ch, destf);
4203 }
4205 fclose(destf);
4206 fclose(srcf);
4208 #else
4210 if (!CopyFile(srcNative.c_str(), destNative.c_str(), false))
4211 {
4212 error("copyFile from %s to %s failed",
4213 srcNative.c_str(), destNative.c_str());
4214 return false;
4215 }
4217 #endif /* __WIN32__ */
4220 return true;
4221 }
4225 /**
4226 * Tests if the file exists and is a regular file
4227 */
4228 bool MakeBase::isRegularFile(const String &fileName)
4229 {
4230 String native = getNativePath(fileName);
4231 struct stat finfo;
4233 //Exists?
4234 if (stat(native.c_str(), &finfo)<0)
4235 return false;
4238 //check the file mode
4239 if (!S_ISREG(finfo.st_mode))
4240 return false;
4242 return true;
4243 }
4245 /**
4246 * Tests if the file exists and is a directory
4247 */
4248 bool MakeBase::isDirectory(const String &fileName)
4249 {
4250 String native = getNativePath(fileName);
4251 struct stat finfo;
4253 //Exists?
4254 if (stat(native.c_str(), &finfo)<0)
4255 return false;
4258 //check the file mode
4259 if (!S_ISDIR(finfo.st_mode))
4260 return false;
4262 return true;
4263 }
4267 /**
4268 * Tests is the modification of fileA is newer than fileB
4269 */
4270 bool MakeBase::isNewerThan(const String &fileA, const String &fileB)
4271 {
4272 //trace("isNewerThan:'%s' , '%s'", fileA.c_str(), fileB.c_str());
4273 String nativeA = getNativePath(fileA);
4274 struct stat infoA;
4275 //IF source does not exist, NOT newer
4276 if (stat(nativeA.c_str(), &infoA)<0)
4277 {
4278 return false;
4279 }
4281 String nativeB = getNativePath(fileB);
4282 struct stat infoB;
4283 //IF dest does not exist, YES, newer
4284 if (stat(nativeB.c_str(), &infoB)<0)
4285 {
4286 return true;
4287 }
4289 //check the actual times
4290 if (infoA.st_mtime > infoB.st_mtime)
4291 {
4292 return true;
4293 }
4295 return false;
4296 }
4299 //########################################################################
4300 //# P K G C O N F I G
4301 //########################################################################
4303 /**
4304 *
4305 */
4306 class PkgConfig : public MakeBase
4307 {
4309 public:
4311 /**
4312 *
4313 */
4314 PkgConfig()
4315 { init(); }
4317 /**
4318 *
4319 */
4320 PkgConfig(const String &namearg)
4321 { init(); name = namearg; }
4323 /**
4324 *
4325 */
4326 PkgConfig(const PkgConfig &other)
4327 { assign(other); }
4329 /**
4330 *
4331 */
4332 PkgConfig &operator=(const PkgConfig &other)
4333 { assign(other); return *this; }
4335 /**
4336 *
4337 */
4338 virtual ~PkgConfig()
4339 { }
4341 /**
4342 *
4343 */
4344 virtual String getName()
4345 { return name; }
4347 /**
4348 *
4349 */
4350 virtual String getDescription()
4351 { return description; }
4353 /**
4354 *
4355 */
4356 virtual String getCflags()
4357 { return cflags; }
4359 /**
4360 *
4361 */
4362 virtual String getLibs()
4363 { return libs; }
4365 /**
4366 *
4367 */
4368 virtual String getVersion()
4369 { return version; }
4371 /**
4372 *
4373 */
4374 virtual int getMajorVersion()
4375 { return majorVersion; }
4377 /**
4378 *
4379 */
4380 virtual int getMinorVersion()
4381 { return minorVersion; }
4383 /**
4384 *
4385 */
4386 virtual int getMicroVersion()
4387 { return microVersion; }
4389 /**
4390 *
4391 */
4392 virtual std::map<String, String> &getAttributes()
4393 { return attrs; }
4395 /**
4396 *
4397 */
4398 virtual std::vector<String> &getRequireList()
4399 { return requireList; }
4401 virtual bool readFile(const String &fileName);
4403 private:
4405 void init()
4406 {
4407 name = "";
4408 description = "";
4409 cflags = "";
4410 libs = "";
4411 requires = "";
4412 version = "";
4413 majorVersion = 0;
4414 minorVersion = 0;
4415 microVersion = 0;
4416 fileName = "";
4417 attrs.clear();
4418 requireList.clear();
4419 }
4421 void assign(const PkgConfig &other)
4422 {
4423 name = other.name;
4424 description = other.description;
4425 cflags = other.cflags;
4426 libs = other.libs;
4427 requires = other.requires;
4428 version = other.version;
4429 majorVersion = other.majorVersion;
4430 minorVersion = other.minorVersion;
4431 microVersion = other.microVersion;
4432 fileName = other.fileName;
4433 attrs = other.attrs;
4434 requireList = other.requireList;
4435 }
4439 int get(int pos);
4441 int skipwhite(int pos);
4443 int getword(int pos, String &ret);
4445 void parseRequires();
4447 void parseVersion();
4449 bool parse(const String &buf);
4451 void dumpAttrs();
4453 String name;
4455 String description;
4457 String cflags;
4459 String libs;
4461 String requires;
4463 String version;
4465 int majorVersion;
4467 int minorVersion;
4469 int microVersion;
4471 String fileName;
4473 std::map<String, String> attrs;
4475 std::vector<String> requireList;
4477 char *parsebuf;
4478 int parselen;
4479 };
4482 /**
4483 * Get a character from the buffer at pos. If out of range,
4484 * return -1 for safety
4485 */
4486 int PkgConfig::get(int pos)
4487 {
4488 if (pos>parselen)
4489 return -1;
4490 return parsebuf[pos];
4491 }
4495 /**
4496 * Skip over all whitespace characters beginning at pos. Return
4497 * the position of the first non-whitespace character.
4498 */
4499 int PkgConfig::skipwhite(int pos)
4500 {
4501 while (pos < parselen)
4502 {
4503 int ch = get(pos);
4504 if (ch < 0)
4505 break;
4506 if (!isspace(ch))
4507 break;
4508 pos++;
4509 }
4510 return pos;
4511 }
4514 /**
4515 * Parse the buffer beginning at pos, for a word. Fill
4516 * 'ret' with the result. Return the position after the
4517 * word.
4518 */
4519 int PkgConfig::getword(int pos, String &ret)
4520 {
4521 while (pos < parselen)
4522 {
4523 int ch = get(pos);
4524 if (ch < 0)
4525 break;
4526 if (!isalnum(ch) && ch != '_' && ch != '-'&& ch != '.')
4527 break;
4528 ret.push_back((char)ch);
4529 pos++;
4530 }
4531 return pos;
4532 }
4534 void PkgConfig::parseRequires()
4535 {
4536 if (requires.size() == 0)
4537 return;
4538 parsebuf = (char *)requires.c_str();
4539 parselen = requires.size();
4540 int pos = 0;
4541 while (pos < parselen)
4542 {
4543 pos = skipwhite(pos);
4544 String val;
4545 int pos2 = getword(pos, val);
4546 if (pos2 == pos)
4547 break;
4548 pos = pos2;
4549 //trace("val %s", val.c_str());
4550 requireList.push_back(val);
4551 }
4552 }
4554 static int getint(const String str)
4555 {
4556 char *s = (char *)str.c_str();
4557 char *ends = NULL;
4558 long val = strtol(s, &ends, 10);
4559 if (ends == s)
4560 return 0L;
4561 else
4562 return val;
4563 }
4565 void PkgConfig::parseVersion()
4566 {
4567 if (version.size() == 0)
4568 return;
4569 String s1, s2, s3;
4570 unsigned int pos = 0;
4571 unsigned int pos2 = version.find('.', pos);
4572 if (pos2 == version.npos)
4573 {
4574 s1 = version;
4575 }
4576 else
4577 {
4578 s1 = version.substr(pos, pos2-pos);
4579 pos = pos2;
4580 pos++;
4581 if (pos < version.size())
4582 {
4583 pos2 = version.find('.', pos);
4584 if (pos2 == version.npos)
4585 {
4586 s2 = version.substr(pos, version.size()-pos);
4587 }
4588 else
4589 {
4590 s2 = version.substr(pos, pos2-pos);
4591 pos = pos2;
4592 pos++;
4593 if (pos < version.size())
4594 s3 = version.substr(pos, pos2-pos);
4595 }
4596 }
4597 }
4599 majorVersion = getint(s1);
4600 minorVersion = getint(s2);
4601 microVersion = getint(s3);
4602 //trace("version:%d.%d.%d", majorVersion,
4603 // minorVersion, microVersion );
4604 }
4607 bool PkgConfig::parse(const String &buf)
4608 {
4609 init();
4611 parsebuf = (char *)buf.c_str();
4612 parselen = buf.size();
4613 int pos = 0;
4616 while (pos < parselen)
4617 {
4618 String attrName;
4619 pos = skipwhite(pos);
4620 int ch = get(pos);
4621 if (ch == '#')
4622 {
4623 //comment. eat the rest of the line
4624 while (pos < parselen)
4625 {
4626 ch = get(pos);
4627 if (ch == '\n' || ch < 0)
4628 break;
4629 pos++;
4630 }
4631 continue;
4632 }
4633 pos = getword(pos, attrName);
4634 if (attrName.size() == 0)
4635 continue;
4636 pos = skipwhite(pos);
4637 ch = get(pos);
4638 if (ch != ':' && ch != '=')
4639 {
4640 error("expected ':' or '='");
4641 return false;
4642 }
4643 pos++;
4644 pos = skipwhite(pos);
4645 String attrVal;
4646 while (pos < parselen)
4647 {
4648 ch = get(pos);
4649 if (ch == '\n' || ch < 0)
4650 break;
4651 else if (ch == '$' && get(pos+1) == '{')
4652 {
4653 //# this is a ${substitution}
4654 pos += 2;
4655 String subName;
4656 while (pos < parselen)
4657 {
4658 ch = get(pos);
4659 if (ch < 0)
4660 {
4661 error("unterminated substitution");
4662 return false;
4663 }
4664 else if (ch == '}')
4665 break;
4666 else
4667 subName.push_back((char)ch);
4668 pos++;
4669 }
4670 //trace("subName:%s", subName.c_str());
4671 String subVal = attrs[subName];
4672 //trace("subVal:%s", subVal.c_str());
4673 attrVal.append(subVal);
4674 }
4675 else
4676 attrVal.push_back((char)ch);
4677 pos++;
4678 }
4680 attrVal = trim(attrVal);
4681 attrs[attrName] = attrVal;
4683 if (attrName == "Name")
4684 name = attrVal;
4685 else if (attrName == "Description")
4686 description = attrVal;
4687 else if (attrName == "Cflags")
4688 cflags = attrVal;
4689 else if (attrName == "Libs")
4690 libs = attrVal;
4691 else if (attrName == "Requires")
4692 requires = attrVal;
4693 else if (attrName == "Version")
4694 version = attrVal;
4696 //trace("name:'%s' value:'%s'",
4697 // attrName.c_str(), attrVal.c_str());
4698 }
4701 parseRequires();
4702 parseVersion();
4704 return true;
4705 }
4707 void PkgConfig::dumpAttrs()
4708 {
4709 //trace("### PkgConfig attributes for %s", fileName.c_str());
4710 std::map<String, String>::iterator iter;
4711 for (iter=attrs.begin() ; iter!=attrs.end() ; iter++)
4712 {
4713 trace(" %s = %s", iter->first.c_str(), iter->second.c_str());
4714 }
4715 }
4718 bool PkgConfig::readFile(const String &fileNameArg)
4719 {
4720 fileName = fileNameArg;
4722 FILE *f = fopen(fileName.c_str(), "r");
4723 if (!f)
4724 {
4725 error("cannot open file '%s' for reading", fileName.c_str());
4726 return false;
4727 }
4728 String buf;
4729 while (true)
4730 {
4731 int ch = fgetc(f);
4732 if (ch < 0)
4733 break;
4734 buf.push_back((char)ch);
4735 }
4736 fclose(f);
4738 //trace("####### File:\n%s", buf.c_str());
4739 if (!parse(buf))
4740 {
4741 return false;
4742 }
4744 dumpAttrs();
4746 return true;
4747 }
4753 //########################################################################
4754 //# D E P T O O L
4755 //########################################################################
4759 /**
4760 * Class which holds information for each file.
4761 */
4762 class FileRec
4763 {
4764 public:
4766 typedef enum
4767 {
4768 UNKNOWN,
4769 CFILE,
4770 HFILE,
4771 OFILE
4772 } FileType;
4774 /**
4775 * Constructor
4776 */
4777 FileRec()
4778 { init(); type = UNKNOWN; }
4780 /**
4781 * Copy constructor
4782 */
4783 FileRec(const FileRec &other)
4784 { init(); assign(other); }
4785 /**
4786 * Constructor
4787 */
4788 FileRec(int typeVal)
4789 { init(); type = typeVal; }
4790 /**
4791 * Assignment operator
4792 */
4793 FileRec &operator=(const FileRec &other)
4794 { init(); assign(other); return *this; }
4797 /**
4798 * Destructor
4799 */
4800 ~FileRec()
4801 {}
4803 /**
4804 * Directory part of the file name
4805 */
4806 String path;
4808 /**
4809 * Base name, sans directory and suffix
4810 */
4811 String baseName;
4813 /**
4814 * File extension, such as cpp or h
4815 */
4816 String suffix;
4818 /**
4819 * Type of file: CFILE, HFILE, OFILE
4820 */
4821 int type;
4823 /**
4824 * Used to list files ref'd by this one
4825 */
4826 std::map<String, FileRec *> files;
4829 private:
4831 void init()
4832 {
4833 }
4835 void assign(const FileRec &other)
4836 {
4837 type = other.type;
4838 baseName = other.baseName;
4839 suffix = other.suffix;
4840 files = other.files;
4841 }
4843 };
4847 /**
4848 * Simpler dependency record
4849 */
4850 class DepRec
4851 {
4852 public:
4854 /**
4855 * Constructor
4856 */
4857 DepRec()
4858 {init();}
4860 /**
4861 * Copy constructor
4862 */
4863 DepRec(const DepRec &other)
4864 {init(); assign(other);}
4865 /**
4866 * Constructor
4867 */
4868 DepRec(const String &fname)
4869 {init(); name = fname; }
4870 /**
4871 * Assignment operator
4872 */
4873 DepRec &operator=(const DepRec &other)
4874 {init(); assign(other); return *this;}
4877 /**
4878 * Destructor
4879 */
4880 ~DepRec()
4881 {}
4883 /**
4884 * Directory part of the file name
4885 */
4886 String path;
4888 /**
4889 * Base name, without the path and suffix
4890 */
4891 String name;
4893 /**
4894 * Suffix of the source
4895 */
4896 String suffix;
4899 /**
4900 * Used to list files ref'd by this one
4901 */
4902 std::vector<String> files;
4905 private:
4907 void init()
4908 {
4909 }
4911 void assign(const DepRec &other)
4912 {
4913 path = other.path;
4914 name = other.name;
4915 suffix = other.suffix;
4916 files = other.files; //avoid recursion
4917 }
4919 };
4922 class DepTool : public MakeBase
4923 {
4924 public:
4926 /**
4927 * Constructor
4928 */
4929 DepTool()
4930 { init(); }
4932 /**
4933 * Copy constructor
4934 */
4935 DepTool(const DepTool &other)
4936 { init(); assign(other); }
4938 /**
4939 * Assignment operator
4940 */
4941 DepTool &operator=(const DepTool &other)
4942 { init(); assign(other); return *this; }
4945 /**
4946 * Destructor
4947 */
4948 ~DepTool()
4949 {}
4952 /**
4953 * Reset this section of code
4954 */
4955 virtual void init();
4957 /**
4958 * Reset this section of code
4959 */
4960 virtual void assign(const DepTool &other)
4961 {
4962 }
4964 /**
4965 * Sets the source directory which will be scanned
4966 */
4967 virtual void setSourceDirectory(const String &val)
4968 { sourceDir = val; }
4970 /**
4971 * Returns the source directory which will be scanned
4972 */
4973 virtual String getSourceDirectory()
4974 { return sourceDir; }
4976 /**
4977 * Sets the list of files within the directory to analyze
4978 */
4979 virtual void setFileList(const std::vector<String> &list)
4980 { fileList = list; }
4982 /**
4983 * Creates the list of all file names which will be
4984 * candidates for further processing. Reads make.exclude
4985 * to see which files for directories to leave out.
4986 */
4987 virtual bool createFileList();
4990 /**
4991 * Generates the forward dependency list
4992 */
4993 virtual bool generateDependencies();
4996 /**
4997 * Generates the forward dependency list, saving the file
4998 */
4999 virtual bool generateDependencies(const String &);
5002 /**
5003 * Load a dependency file
5004 */
5005 std::vector<DepRec> loadDepFile(const String &fileName);
5007 /**
5008 * Load a dependency file, generating one if necessary
5009 */
5010 std::vector<DepRec> getDepFile(const String &fileName,
5011 bool forceRefresh);
5013 /**
5014 * Save a dependency file
5015 */
5016 bool saveDepFile(const String &fileName);
5019 private:
5022 /**
5023 *
5024 */
5025 void parseName(const String &fullname,
5026 String &path,
5027 String &basename,
5028 String &suffix);
5030 /**
5031 *
5032 */
5033 int get(int pos);
5035 /**
5036 *
5037 */
5038 int skipwhite(int pos);
5040 /**
5041 *
5042 */
5043 int getword(int pos, String &ret);
5045 /**
5046 *
5047 */
5048 bool sequ(int pos, const char *key);
5050 /**
5051 *
5052 */
5053 bool addIncludeFile(FileRec *frec, const String &fname);
5055 /**
5056 *
5057 */
5058 bool scanFile(const String &fname, FileRec *frec);
5060 /**
5061 *
5062 */
5063 bool processDependency(FileRec *ofile, FileRec *include);
5065 /**
5066 *
5067 */
5068 String sourceDir;
5070 /**
5071 *
5072 */
5073 std::vector<String> fileList;
5075 /**
5076 *
5077 */
5078 std::vector<String> directories;
5080 /**
5081 * A list of all files which will be processed for
5082 * dependencies.
5083 */
5084 std::map<String, FileRec *> allFiles;
5086 /**
5087 * The list of .o files, and the
5088 * dependencies upon them.
5089 */
5090 std::map<String, FileRec *> oFiles;
5092 int depFileSize;
5093 char *depFileBuf;
5095 static const int readBufSize = 8192;
5096 char readBuf[8193];//byte larger
5098 };
5104 /**
5105 * Clean up after processing. Called by the destructor, but should
5106 * also be called before the object is reused.
5107 */
5108 void DepTool::init()
5109 {
5110 sourceDir = ".";
5112 fileList.clear();
5113 directories.clear();
5115 //clear output file list
5116 std::map<String, FileRec *>::iterator iter;
5117 for (iter=oFiles.begin(); iter!=oFiles.end() ; iter++)
5118 delete iter->second;
5119 oFiles.clear();
5121 //allFiles actually contains the master copies. delete them
5122 for (iter= allFiles.begin(); iter!=allFiles.end() ; iter++)
5123 delete iter->second;
5124 allFiles.clear();
5126 }
5131 /**
5132 * Parse a full path name into path, base name, and suffix
5133 */
5134 void DepTool::parseName(const String &fullname,
5135 String &path,
5136 String &basename,
5137 String &suffix)
5138 {
5139 if (fullname.size() < 2)
5140 return;
5142 unsigned int pos = fullname.find_last_of('/');
5143 if (pos != fullname.npos && pos<fullname.size()-1)
5144 {
5145 path = fullname.substr(0, pos);
5146 pos++;
5147 basename = fullname.substr(pos, fullname.size()-pos);
5148 }
5149 else
5150 {
5151 path = "";
5152 basename = fullname;
5153 }
5155 pos = basename.find_last_of('.');
5156 if (pos != basename.npos && pos<basename.size()-1)
5157 {
5158 suffix = basename.substr(pos+1, basename.size()-pos-1);
5159 basename = basename.substr(0, pos);
5160 }
5162 //trace("parsename:%s %s %s", path.c_str(),
5163 // basename.c_str(), suffix.c_str());
5164 }
5168 /**
5169 * Generate our internal file list.
5170 */
5171 bool DepTool::createFileList()
5172 {
5174 for (unsigned int i=0 ; i<fileList.size() ; i++)
5175 {
5176 String fileName = fileList[i];
5177 //trace("## FileName:%s", fileName.c_str());
5178 String path;
5179 String basename;
5180 String sfx;
5181 parseName(fileName, path, basename, sfx);
5182 if (sfx == "cpp" || sfx == "c" || sfx == "cxx" ||
5183 sfx == "cc" || sfx == "CC")
5184 {
5185 FileRec *fe = new FileRec(FileRec::CFILE);
5186 fe->path = path;
5187 fe->baseName = basename;
5188 fe->suffix = sfx;
5189 allFiles[fileName] = fe;
5190 }
5191 else if (sfx == "h" || sfx == "hh" ||
5192 sfx == "hpp" || sfx == "hxx")
5193 {
5194 FileRec *fe = new FileRec(FileRec::HFILE);
5195 fe->path = path;
5196 fe->baseName = basename;
5197 fe->suffix = sfx;
5198 allFiles[fileName] = fe;
5199 }
5200 }
5202 if (!listDirectories(sourceDir, "", directories))
5203 return false;
5205 return true;
5206 }
5212 /**
5213 * Get a character from the buffer at pos. If out of range,
5214 * return -1 for safety
5215 */
5216 int DepTool::get(int pos)
5217 {
5218 if (pos>depFileSize)
5219 return -1;
5220 return depFileBuf[pos];
5221 }
5225 /**
5226 * Skip over all whitespace characters beginning at pos. Return
5227 * the position of the first non-whitespace character.
5228 */
5229 int DepTool::skipwhite(int pos)
5230 {
5231 while (pos < depFileSize)
5232 {
5233 int ch = get(pos);
5234 if (ch < 0)
5235 break;
5236 if (!isspace(ch))
5237 break;
5238 pos++;
5239 }
5240 return pos;
5241 }
5244 /**
5245 * Parse the buffer beginning at pos, for a word. Fill
5246 * 'ret' with the result. Return the position after the
5247 * word.
5248 */
5249 int DepTool::getword(int pos, String &ret)
5250 {
5251 while (pos < depFileSize)
5252 {
5253 int ch = get(pos);
5254 if (ch < 0)
5255 break;
5256 if (isspace(ch))
5257 break;
5258 ret.push_back((char)ch);
5259 pos++;
5260 }
5261 return pos;
5262 }
5264 /**
5265 * Return whether the sequence of characters in the buffer
5266 * beginning at pos match the key, for the length of the key
5267 */
5268 bool DepTool::sequ(int pos, const char *key)
5269 {
5270 while (*key)
5271 {
5272 if (*key != get(pos))
5273 return false;
5274 key++; pos++;
5275 }
5276 return true;
5277 }
5281 /**
5282 * Add an include file name to a file record. If the name
5283 * is not found in allFiles explicitly, try prepending include
5284 * directory names to it and try again.
5285 */
5286 bool DepTool::addIncludeFile(FileRec *frec, const String &iname)
5287 {
5288 //# if the name is an exact match to a path name
5289 //# in allFiles, like "myinc.h"
5290 std::map<String, FileRec *>::iterator iter =
5291 allFiles.find(iname);
5292 if (iter != allFiles.end()) //already exists
5293 {
5294 //h file in same dir
5295 FileRec *other = iter->second;
5296 //trace("local: '%s'", iname.c_str());
5297 frec->files[iname] = other;
5298 return true;
5299 }
5300 else
5301 {
5302 //## Ok, it was not found directly
5303 //look in other dirs
5304 std::vector<String>::iterator diter;
5305 for (diter=directories.begin() ;
5306 diter!=directories.end() ; diter++)
5307 {
5308 String dfname = *diter;
5309 dfname.append("/");
5310 dfname.append(iname);
5311 URI fullPathURI(dfname); //normalize path name
5312 String fullPath = fullPathURI.getPath();
5313 if (fullPath[0] == '/')
5314 fullPath = fullPath.substr(1);
5315 //trace("Normalized %s to %s", dfname.c_str(), fullPath.c_str());
5316 iter = allFiles.find(fullPath);
5317 if (iter != allFiles.end())
5318 {
5319 FileRec *other = iter->second;
5320 //trace("other: '%s'", iname.c_str());
5321 frec->files[fullPath] = other;
5322 return true;
5323 }
5324 }
5325 }
5326 return true;
5327 }
5331 /**
5332 * Lightly parse a file to find the #include directives. Do
5333 * a bit of state machine stuff to make sure that the directive
5334 * is valid. (Like not in a comment).
5335 */
5336 bool DepTool::scanFile(const String &fname, FileRec *frec)
5337 {
5338 String fileName;
5339 if (sourceDir.size() > 0)
5340 {
5341 fileName.append(sourceDir);
5342 fileName.append("/");
5343 }
5344 fileName.append(fname);
5345 String nativeName = getNativePath(fileName);
5346 FILE *f = fopen(nativeName.c_str(), "r");
5347 if (!f)
5348 {
5349 error("Could not open '%s' for reading", fname.c_str());
5350 return false;
5351 }
5352 String buf;
5353 while (!feof(f))
5354 {
5355 int len = fread(readBuf, 1, readBufSize, f);
5356 readBuf[len] = '\0';
5357 buf.append(readBuf);
5358 }
5359 fclose(f);
5361 depFileSize = buf.size();
5362 depFileBuf = (char *)buf.c_str();
5363 int pos = 0;
5366 while (pos < depFileSize)
5367 {
5368 //trace("p:%c", get(pos));
5370 //# Block comment
5371 if (get(pos) == '/' && get(pos+1) == '*')
5372 {
5373 pos += 2;
5374 while (pos < depFileSize)
5375 {
5376 if (get(pos) == '*' && get(pos+1) == '/')
5377 {
5378 pos += 2;
5379 break;
5380 }
5381 else
5382 pos++;
5383 }
5384 }
5385 //# Line comment
5386 else if (get(pos) == '/' && get(pos+1) == '/')
5387 {
5388 pos += 2;
5389 while (pos < depFileSize)
5390 {
5391 if (get(pos) == '\n')
5392 {
5393 pos++;
5394 break;
5395 }
5396 else
5397 pos++;
5398 }
5399 }
5400 //# #include! yaay
5401 else if (sequ(pos, "#include"))
5402 {
5403 pos += 8;
5404 pos = skipwhite(pos);
5405 String iname;
5406 pos = getword(pos, iname);
5407 if (iname.size()>2)
5408 {
5409 iname = iname.substr(1, iname.size()-2);
5410 addIncludeFile(frec, iname);
5411 }
5412 }
5413 else
5414 {
5415 pos++;
5416 }
5417 }
5419 return true;
5420 }
5424 /**
5425 * Recursively check include lists to find all files in allFiles to which
5426 * a given file is dependent.
5427 */
5428 bool DepTool::processDependency(FileRec *ofile, FileRec *include)
5429 {
5430 std::map<String, FileRec *>::iterator iter;
5431 for (iter=include->files.begin() ; iter!=include->files.end() ; iter++)
5432 {
5433 String fname = iter->first;
5434 if (ofile->files.find(fname) != ofile->files.end())
5435 {
5436 //trace("file '%s' already seen", fname.c_str());
5437 continue;
5438 }
5439 FileRec *child = iter->second;
5440 ofile->files[fname] = child;
5442 processDependency(ofile, child);
5443 }
5446 return true;
5447 }
5453 /**
5454 * Generate the file dependency list.
5455 */
5456 bool DepTool::generateDependencies()
5457 {
5458 std::map<String, FileRec *>::iterator iter;
5459 //# First pass. Scan for all includes
5460 for (iter=allFiles.begin() ; iter!=allFiles.end() ; iter++)
5461 {
5462 FileRec *frec = iter->second;
5463 if (!scanFile(iter->first, frec))
5464 {
5465 //quit?
5466 }
5467 }
5469 //# Second pass. Scan for all includes
5470 for (iter=allFiles.begin() ; iter!=allFiles.end() ; iter++)
5471 {
5472 FileRec *include = iter->second;
5473 if (include->type == FileRec::CFILE)
5474 {
5475 String cFileName = iter->first;
5476 FileRec *ofile = new FileRec(FileRec::OFILE);
5477 ofile->path = include->path;
5478 ofile->baseName = include->baseName;
5479 ofile->suffix = include->suffix;
5480 String fname = include->path;
5481 if (fname.size()>0)
5482 fname.append("/");
5483 fname.append(include->baseName);
5484 fname.append(".o");
5485 oFiles[fname] = ofile;
5486 //add the .c file first? no, don't
5487 //ofile->files[cFileName] = include;
5489 //trace("ofile:%s", fname.c_str());
5491 processDependency(ofile, include);
5492 }
5493 }
5496 return true;
5497 }
5501 /**
5502 * High-level call to generate deps and optionally save them
5503 */
5504 bool DepTool::generateDependencies(const String &fileName)
5505 {
5506 if (!createFileList())
5507 return false;
5508 if (!generateDependencies())
5509 return false;
5510 if (!saveDepFile(fileName))
5511 return false;
5512 return true;
5513 }
5516 /**
5517 * This saves the dependency cache.
5518 */
5519 bool DepTool::saveDepFile(const String &fileName)
5520 {
5521 time_t tim;
5522 time(&tim);
5524 FILE *f = fopen(fileName.c_str(), "w");
5525 if (!f)
5526 {
5527 trace("cannot open '%s' for writing", fileName.c_str());
5528 }
5529 fprintf(f, "<?xml version='1.0'?>\n");
5530 fprintf(f, "<!--\n");
5531 fprintf(f, "########################################################\n");
5532 fprintf(f, "## File: build.dep\n");
5533 fprintf(f, "## Generated by BuildTool at :%s", ctime(&tim));
5534 fprintf(f, "########################################################\n");
5535 fprintf(f, "-->\n");
5537 fprintf(f, "<dependencies source='%s'>\n\n", sourceDir.c_str());
5538 std::map<String, FileRec *>::iterator iter;
5539 for (iter=oFiles.begin() ; iter!=oFiles.end() ; iter++)
5540 {
5541 FileRec *frec = iter->second;
5542 if (frec->type == FileRec::OFILE)
5543 {
5544 fprintf(f, "<object path='%s' name='%s' suffix='%s'>\n",
5545 frec->path.c_str(), frec->baseName.c_str(), frec->suffix.c_str());
5546 std::map<String, FileRec *>::iterator citer;
5547 for (citer=frec->files.begin() ; citer!=frec->files.end() ; citer++)
5548 {
5549 String cfname = citer->first;
5550 fprintf(f, " <dep name='%s'/>\n", cfname.c_str());
5551 }
5552 fprintf(f, "</object>\n\n");
5553 }
5554 }
5556 fprintf(f, "</dependencies>\n");
5557 fprintf(f, "\n");
5558 fprintf(f, "<!--\n");
5559 fprintf(f, "########################################################\n");
5560 fprintf(f, "## E N D\n");
5561 fprintf(f, "########################################################\n");
5562 fprintf(f, "-->\n");
5564 fclose(f);
5566 return true;
5567 }
5572 /**
5573 * This loads the dependency cache.
5574 */
5575 std::vector<DepRec> DepTool::loadDepFile(const String &depFile)
5576 {
5577 std::vector<DepRec> result;
5579 Parser parser;
5580 Element *root = parser.parseFile(depFile.c_str());
5581 if (!root)
5582 {
5583 //error("Could not open %s for reading", depFile.c_str());
5584 return result;
5585 }
5587 if (root->getChildren().size()==0 ||
5588 root->getChildren()[0]->getName()!="dependencies")
5589 {
5590 error("loadDepFile: main xml element should be <dependencies>");
5591 delete root;
5592 return result;
5593 }
5595 //########## Start parsing
5596 Element *depList = root->getChildren()[0];
5598 std::vector<Element *> objects = depList->getChildren();
5599 for (unsigned int i=0 ; i<objects.size() ; i++)
5600 {
5601 Element *objectElem = objects[i];
5602 String tagName = objectElem->getName();
5603 if (tagName != "object")
5604 {
5605 error("loadDepFile: <dependencies> should have only <object> children");
5606 return result;
5607 }
5609 String objName = objectElem->getAttribute("name");
5610 //trace("object:%s", objName.c_str());
5611 DepRec depObject(objName);
5612 depObject.path = objectElem->getAttribute("path");
5613 depObject.suffix = objectElem->getAttribute("suffix");
5614 //########## DESCRIPTION
5615 std::vector<Element *> depElems = objectElem->getChildren();
5616 for (unsigned int i=0 ; i<depElems.size() ; i++)
5617 {
5618 Element *depElem = depElems[i];
5619 tagName = depElem->getName();
5620 if (tagName != "dep")
5621 {
5622 error("loadDepFile: <object> should have only <dep> children");
5623 return result;
5624 }
5625 String depName = depElem->getAttribute("name");
5626 //trace(" dep:%s", depName.c_str());
5627 depObject.files.push_back(depName);
5628 }
5630 //Insert into the result list, in a sorted manner
5631 bool inserted = false;
5632 std::vector<DepRec>::iterator iter;
5633 for (iter = result.begin() ; iter != result.end() ; iter++)
5634 {
5635 String vpath = iter->path;
5636 vpath.append("/");
5637 vpath.append(iter->name);
5638 String opath = depObject.path;
5639 opath.append("/");
5640 opath.append(depObject.name);
5641 if (vpath > opath)
5642 {
5643 inserted = true;
5644 iter = result.insert(iter, depObject);
5645 break;
5646 }
5647 }
5648 if (!inserted)
5649 result.push_back(depObject);
5650 }
5652 delete root;
5654 return result;
5655 }
5658 /**
5659 * This loads the dependency cache.
5660 */
5661 std::vector<DepRec> DepTool::getDepFile(const String &depFile,
5662 bool forceRefresh)
5663 {
5664 std::vector<DepRec> result;
5665 if (forceRefresh)
5666 {
5667 generateDependencies(depFile);
5668 result = loadDepFile(depFile);
5669 }
5670 else
5671 {
5672 //try once
5673 result = loadDepFile(depFile);
5674 if (result.size() == 0)
5675 {
5676 //fail? try again
5677 generateDependencies(depFile);
5678 result = loadDepFile(depFile);
5679 }
5680 }
5681 return result;
5682 }
5687 //########################################################################
5688 //# T A S K
5689 //########################################################################
5690 //forward decl
5691 class Target;
5692 class Make;
5694 /**
5695 *
5696 */
5697 class Task : public MakeBase
5698 {
5700 public:
5702 typedef enum
5703 {
5704 TASK_NONE,
5705 TASK_CC,
5706 TASK_COPY,
5707 TASK_DELETE,
5708 TASK_JAR,
5709 TASK_JAVAC,
5710 TASK_LINK,
5711 TASK_MAKEFILE,
5712 TASK_MKDIR,
5713 TASK_MSGFMT,
5714 TASK_PKG_CONFIG,
5715 TASK_RANLIB,
5716 TASK_RC,
5717 TASK_SHAREDLIB,
5718 TASK_STATICLIB,
5719 TASK_STRIP,
5720 TASK_TOUCH,
5721 TASK_TSTAMP
5722 } TaskType;
5725 /**
5726 *
5727 */
5728 Task(MakeBase &par) : parent(par)
5729 { init(); }
5731 /**
5732 *
5733 */
5734 Task(const Task &other) : parent(other.parent)
5735 { init(); assign(other); }
5737 /**
5738 *
5739 */
5740 Task &operator=(const Task &other)
5741 { assign(other); return *this; }
5743 /**
5744 *
5745 */
5746 virtual ~Task()
5747 { }
5750 /**
5751 *
5752 */
5753 virtual MakeBase &getParent()
5754 { return parent; }
5756 /**
5757 *
5758 */
5759 virtual int getType()
5760 { return type; }
5762 /**
5763 *
5764 */
5765 virtual void setType(int val)
5766 { type = val; }
5768 /**
5769 *
5770 */
5771 virtual String getName()
5772 { return name; }
5774 /**
5775 *
5776 */
5777 virtual bool execute()
5778 { return true; }
5780 /**
5781 *
5782 */
5783 virtual bool parse(Element *elem)
5784 { return true; }
5786 /**
5787 *
5788 */
5789 Task *createTask(Element *elem, int lineNr);
5792 protected:
5794 void init()
5795 {
5796 type = TASK_NONE;
5797 name = "none";
5798 }
5800 void assign(const Task &other)
5801 {
5802 type = other.type;
5803 name = other.name;
5804 }
5806 String getAttribute(Element *elem, const String &attrName)
5807 {
5808 String str;
5809 return str;
5810 }
5812 MakeBase &parent;
5814 int type;
5816 String name;
5817 };
5821 /**
5822 * This task runs the C/C++ compiler. The compiler is invoked
5823 * for all .c or .cpp files which are newer than their correcsponding
5824 * .o files.
5825 */
5826 class TaskCC : public Task
5827 {
5828 public:
5830 TaskCC(MakeBase &par) : Task(par)
5831 {
5832 type = TASK_CC; name = "cc";
5833 ccCommand = "gcc";
5834 cxxCommand = "g++";
5835 source = ".";
5836 dest = ".";
5837 flags = "";
5838 defines = "";
5839 includes = "";
5840 fileSet.clear();
5841 }
5843 virtual ~TaskCC()
5844 {}
5846 virtual bool needsCompiling(const FileRec &depRec,
5847 const String &src, const String &dest)
5848 {
5849 return false;
5850 }
5852 virtual bool execute()
5853 {
5854 if (!listFiles(parent, fileSet))
5855 return false;
5857 FILE *f = NULL;
5858 f = fopen("compile.lst", "w");
5860 bool refreshCache = false;
5861 String fullName = parent.resolve("build.dep");
5862 if (isNewerThan(parent.getURI().getPath(), fullName))
5863 {
5864 status(" : regenerating C/C++ dependency cache");
5865 refreshCache = true;
5866 }
5868 DepTool depTool;
5869 depTool.setSourceDirectory(source);
5870 depTool.setFileList(fileSet.getFiles());
5871 std::vector<DepRec> deps =
5872 depTool.getDepFile("build.dep", refreshCache);
5874 String incs;
5875 incs.append("-I");
5876 incs.append(parent.resolve("."));
5877 incs.append(" ");
5878 if (includes.size()>0)
5879 {
5880 incs.append(includes);
5881 incs.append(" ");
5882 }
5883 std::set<String> paths;
5884 std::vector<DepRec>::iterator viter;
5885 for (viter=deps.begin() ; viter!=deps.end() ; viter++)
5886 {
5887 DepRec dep = *viter;
5888 if (dep.path.size()>0)
5889 paths.insert(dep.path);
5890 }
5891 if (source.size()>0)
5892 {
5893 incs.append(" -I");
5894 incs.append(parent.resolve(source));
5895 incs.append(" ");
5896 }
5897 std::set<String>::iterator setIter;
5898 for (setIter=paths.begin() ; setIter!=paths.end() ; setIter++)
5899 {
5900 incs.append(" -I");
5901 String dname;
5902 if (source.size()>0)
5903 {
5904 dname.append(source);
5905 dname.append("/");
5906 }
5907 dname.append(*setIter);
5908 incs.append(parent.resolve(dname));
5909 }
5910 std::vector<String> cfiles;
5911 for (viter=deps.begin() ; viter!=deps.end() ; viter++)
5912 {
5913 DepRec dep = *viter;
5915 //## Select command
5916 String sfx = dep.suffix;
5917 String command = ccCommand;
5918 if (sfx == "cpp" || sfx == "cxx" || sfx == "c++" ||
5919 sfx == "cc" || sfx == "CC")
5920 command = cxxCommand;
5922 //## Make paths
5923 String destPath = dest;
5924 String srcPath = source;
5925 if (dep.path.size()>0)
5926 {
5927 destPath.append("/");
5928 destPath.append(dep.path);
5929 srcPath.append("/");
5930 srcPath.append(dep.path);
5931 }
5932 //## Make sure destination directory exists
5933 if (!createDirectory(destPath))
5934 return false;
5936 //## Check whether it needs to be done
5937 String destName;
5938 if (destPath.size()>0)
5939 {
5940 destName.append(destPath);
5941 destName.append("/");
5942 }
5943 destName.append(dep.name);
5944 destName.append(".o");
5945 String destFullName = parent.resolve(destName);
5946 String srcName;
5947 if (srcPath.size()>0)
5948 {
5949 srcName.append(srcPath);
5950 srcName.append("/");
5951 }
5952 srcName.append(dep.name);
5953 srcName.append(".");
5954 srcName.append(dep.suffix);
5955 String srcFullName = parent.resolve(srcName);
5956 bool compileMe = false;
5957 //# First we check if the source is newer than the .o
5958 if (isNewerThan(srcFullName, destFullName))
5959 {
5960 status(" : compile of %s required by %s",
5961 destFullName.c_str(), srcFullName.c_str());
5962 compileMe = true;
5963 }
5964 else
5965 {
5966 //# secondly, we check if any of the included dependencies
5967 //# of the .c/.cpp is newer than the .o
5968 for (unsigned int i=0 ; i<dep.files.size() ; i++)
5969 {
5970 String depName;
5971 if (source.size()>0)
5972 {
5973 depName.append(source);
5974 depName.append("/");
5975 }
5976 depName.append(dep.files[i]);
5977 String depFullName = parent.resolve(depName);
5978 bool depRequires = isNewerThan(depFullName, destFullName);
5979 //trace("%d %s %s\n", depRequires,
5980 // destFullName.c_str(), depFullName.c_str());
5981 if (depRequires)
5982 {
5983 status(" : compile of %s required by %s",
5984 destFullName.c_str(), depFullName.c_str());
5985 compileMe = true;
5986 break;
5987 }
5988 }
5989 }
5990 if (!compileMe)
5991 {
5992 continue;
5993 }
5995 //## Assemble the command
5996 String cmd = command;
5997 cmd.append(" -c ");
5998 cmd.append(flags);
5999 cmd.append(" ");
6000 cmd.append(defines);
6001 cmd.append(" ");
6002 cmd.append(incs);
6003 cmd.append(" ");
6004 cmd.append(srcFullName);
6005 cmd.append(" -o ");
6006 cmd.append(destFullName);
6008 //## Execute the command
6010 String outString, errString;
6011 bool ret = executeCommand(cmd.c_str(), "", outString, errString);
6013 if (f)
6014 {
6015 fprintf(f, "########################### File : %s\n",
6016 srcFullName.c_str());
6017 fprintf(f, "#### COMMAND ###\n");
6018 int col = 0;
6019 for (unsigned int i = 0 ; i < cmd.size() ; i++)
6020 {
6021 char ch = cmd[i];
6022 if (isspace(ch) && col > 63)
6023 {
6024 fputc('\n', f);
6025 col = 0;
6026 }
6027 else
6028 {
6029 fputc(ch, f);
6030 col++;
6031 }
6032 if (col > 76)
6033 {
6034 fputc('\n', f);
6035 col = 0;
6036 }
6037 }
6038 fprintf(f, "\n");
6039 fprintf(f, "#### STDOUT ###\n%s\n", outString.c_str());
6040 fprintf(f, "#### STDERR ###\n%s\n\n", errString.c_str());
6041 }
6042 if (!ret)
6043 {
6044 error("problem compiling: %s", errString.c_str());
6045 return false;
6046 }
6048 }
6050 if (f)
6051 {
6052 fclose(f);
6053 }
6055 return true;
6056 }
6058 virtual bool parse(Element *elem)
6059 {
6060 String s;
6061 if (!parent.getAttribute(elem, "command", s))
6062 return false;
6063 if (s.size()>0) { ccCommand = s; cxxCommand = s; }
6064 if (!parent.getAttribute(elem, "cc", s))
6065 return false;
6066 if (s.size()>0) ccCommand = s;
6067 if (!parent.getAttribute(elem, "cxx", s))
6068 return false;
6069 if (s.size()>0) cxxCommand = s;
6070 if (!parent.getAttribute(elem, "destdir", s))
6071 return false;
6072 if (s.size()>0) dest = s;
6074 std::vector<Element *> children = elem->getChildren();
6075 for (unsigned int i=0 ; i<children.size() ; i++)
6076 {
6077 Element *child = children[i];
6078 String tagName = child->getName();
6079 if (tagName == "flags")
6080 {
6081 if (!parent.getValue(child, flags))
6082 return false;
6083 flags = strip(flags);
6084 }
6085 else if (tagName == "includes")
6086 {
6087 if (!parent.getValue(child, includes))
6088 return false;
6089 includes = strip(includes);
6090 }
6091 else if (tagName == "defines")
6092 {
6093 if (!parent.getValue(child, defines))
6094 return false;
6095 defines = strip(defines);
6096 }
6097 else if (tagName == "fileset")
6098 {
6099 if (!parseFileSet(child, parent, fileSet))
6100 return false;
6101 source = fileSet.getDirectory();
6102 }
6103 }
6105 return true;
6106 }
6108 protected:
6110 String ccCommand;
6111 String cxxCommand;
6112 String source;
6113 String dest;
6114 String flags;
6115 String defines;
6116 String includes;
6117 FileSet fileSet;
6119 };
6123 /**
6124 *
6125 */
6126 class TaskCopy : public Task
6127 {
6128 public:
6130 typedef enum
6131 {
6132 CP_NONE,
6133 CP_TOFILE,
6134 CP_TODIR
6135 } CopyType;
6137 TaskCopy(MakeBase &par) : Task(par)
6138 {
6139 type = TASK_COPY; name = "copy";
6140 cptype = CP_NONE;
6141 verbose = false;
6142 haveFileSet = false;
6143 }
6145 virtual ~TaskCopy()
6146 {}
6148 virtual bool execute()
6149 {
6150 switch (cptype)
6151 {
6152 case CP_TOFILE:
6153 {
6154 if (fileName.size()>0)
6155 {
6156 status(" : %s to %s",
6157 fileName.c_str(), toFileName.c_str());
6158 String fullSource = parent.resolve(fileName);
6159 String fullDest = parent.resolve(toFileName);
6160 //trace("copy %s to file %s", fullSource.c_str(),
6161 // fullDest.c_str());
6162 if (!isRegularFile(fullSource))
6163 {
6164 error("copy : file %s does not exist", fullSource.c_str());
6165 return false;
6166 }
6167 if (!isNewerThan(fullSource, fullDest))
6168 {
6169 status(" : skipped");
6170 return true;
6171 }
6172 if (!copyFile(fullSource, fullDest))
6173 return false;
6174 status(" : 1 file copied");
6175 }
6176 return true;
6177 }
6178 case CP_TODIR:
6179 {
6180 if (haveFileSet)
6181 {
6182 if (!listFiles(parent, fileSet))
6183 return false;
6184 String fileSetDir = fileSet.getDirectory();
6186 status(" : %s to %s",
6187 fileSetDir.c_str(), toDirName.c_str());
6189 int nrFiles = 0;
6190 for (unsigned int i=0 ; i<fileSet.size() ; i++)
6191 {
6192 String fileName = fileSet[i];
6194 String sourcePath;
6195 if (fileSetDir.size()>0)
6196 {
6197 sourcePath.append(fileSetDir);
6198 sourcePath.append("/");
6199 }
6200 sourcePath.append(fileName);
6201 String fullSource = parent.resolve(sourcePath);
6203 //Get the immediate parent directory's base name
6204 String baseFileSetDir = fileSetDir;
6205 unsigned int pos = baseFileSetDir.find_last_of('/');
6206 if (pos!=baseFileSetDir.npos &&
6207 pos < baseFileSetDir.size()-1)
6208 baseFileSetDir =
6209 baseFileSetDir.substr(pos+1,
6210 baseFileSetDir.size());
6211 //Now make the new path
6212 String destPath;
6213 if (toDirName.size()>0)
6214 {
6215 destPath.append(toDirName);
6216 destPath.append("/");
6217 }
6218 if (baseFileSetDir.size()>0)
6219 {
6220 destPath.append(baseFileSetDir);
6221 destPath.append("/");
6222 }
6223 destPath.append(fileName);
6224 String fullDest = parent.resolve(destPath);
6225 //trace("fileName:%s", fileName.c_str());
6226 //trace("copy %s to new dir : %s", fullSource.c_str(),
6227 // fullDest.c_str());
6228 if (!isNewerThan(fullSource, fullDest))
6229 {
6230 //trace("copy skipping %s", fullSource.c_str());
6231 continue;
6232 }
6233 if (!copyFile(fullSource, fullDest))
6234 return false;
6235 nrFiles++;
6236 }
6237 status(" : %d file(s) copied", nrFiles);
6238 }
6239 else //file source
6240 {
6241 //For file->dir we want only the basename of
6242 //the source appended to the dest dir
6243 status(" : %s to %s",
6244 fileName.c_str(), toDirName.c_str());
6245 String baseName = fileName;
6246 unsigned int pos = baseName.find_last_of('/');
6247 if (pos!=baseName.npos && pos<baseName.size()-1)
6248 baseName = baseName.substr(pos+1, baseName.size());
6249 String fullSource = parent.resolve(fileName);
6250 String destPath;
6251 if (toDirName.size()>0)
6252 {
6253 destPath.append(toDirName);
6254 destPath.append("/");
6255 }
6256 destPath.append(baseName);
6257 String fullDest = parent.resolve(destPath);
6258 //trace("copy %s to new dir : %s", fullSource.c_str(),
6259 // fullDest.c_str());
6260 if (!isRegularFile(fullSource))
6261 {
6262 error("copy : file %s does not exist", fullSource.c_str());
6263 return false;
6264 }
6265 if (!isNewerThan(fullSource, fullDest))
6266 {
6267 status(" : skipped");
6268 return true;
6269 }
6270 if (!copyFile(fullSource, fullDest))
6271 return false;
6272 status(" : 1 file copied");
6273 }
6274 return true;
6275 }
6276 }
6277 return true;
6278 }
6281 virtual bool parse(Element *elem)
6282 {
6283 if (!parent.getAttribute(elem, "file", fileName))
6284 return false;
6285 if (!parent.getAttribute(elem, "tofile", toFileName))
6286 return false;
6287 if (toFileName.size() > 0)
6288 cptype = CP_TOFILE;
6289 if (!parent.getAttribute(elem, "todir", toDirName))
6290 return false;
6291 if (toDirName.size() > 0)
6292 cptype = CP_TODIR;
6293 String ret;
6294 if (!parent.getAttribute(elem, "verbose", ret))
6295 return false;
6296 if (ret.size()>0 && !getBool(ret, verbose))
6297 return false;
6299 haveFileSet = false;
6301 std::vector<Element *> children = elem->getChildren();
6302 for (unsigned int i=0 ; i<children.size() ; i++)
6303 {
6304 Element *child = children[i];
6305 String tagName = child->getName();
6306 if (tagName == "fileset")
6307 {
6308 if (!parseFileSet(child, parent, fileSet))
6309 {
6310 error("problem getting fileset");
6311 return false;
6312 }
6313 haveFileSet = true;
6314 }
6315 }
6317 //Perform validity checks
6318 if (fileName.size()>0 && fileSet.size()>0)
6319 {
6320 error("<copy> can only have one of : file= and <fileset>");
6321 return false;
6322 }
6323 if (toFileName.size()>0 && toDirName.size()>0)
6324 {
6325 error("<copy> can only have one of : tofile= or todir=");
6326 return false;
6327 }
6328 if (haveFileSet && toDirName.size()==0)
6329 {
6330 error("a <copy> task with a <fileset> must have : todir=");
6331 return false;
6332 }
6333 if (cptype == CP_TOFILE && fileName.size()==0)
6334 {
6335 error("<copy> tofile= must be associated with : file=");
6336 return false;
6337 }
6338 if (cptype == CP_TODIR && fileName.size()==0 && !haveFileSet)
6339 {
6340 error("<copy> todir= must be associated with : file= or <fileset>");
6341 return false;
6342 }
6344 return true;
6345 }
6347 private:
6349 int cptype;
6350 String fileName;
6351 FileSet fileSet;
6352 String toFileName;
6353 String toDirName;
6354 bool verbose;
6355 bool haveFileSet;
6356 };
6359 /**
6360 *
6361 */
6362 class TaskDelete : public Task
6363 {
6364 public:
6366 typedef enum
6367 {
6368 DEL_FILE,
6369 DEL_DIR,
6370 DEL_FILESET
6371 } DeleteType;
6373 TaskDelete(MakeBase &par) : Task(par)
6374 {
6375 type = TASK_DELETE;
6376 name = "delete";
6377 delType = DEL_FILE;
6378 verbose = false;
6379 quiet = false;
6380 failOnError = true;
6381 }
6383 virtual ~TaskDelete()
6384 {}
6386 virtual bool execute()
6387 {
6388 struct stat finfo;
6389 switch (delType)
6390 {
6391 case DEL_FILE:
6392 {
6393 status(" : %s", fileName.c_str());
6394 String fullName = parent.resolve(fileName);
6395 char *fname = (char *)fullName.c_str();
6396 //does not exist
6397 if (stat(fname, &finfo)<0)
6398 return true;
6399 //exists but is not a regular file
6400 if (!S_ISREG(finfo.st_mode))
6401 {
6402 error("<delete> failed. '%s' exists and is not a regular file",
6403 fname);
6404 return false;
6405 }
6406 if (remove(fname)<0)
6407 {
6408 error("<delete> failed: %s", strerror(errno));
6409 return false;
6410 }
6411 return true;
6412 }
6413 case DEL_DIR:
6414 {
6415 status(" : %s", dirName.c_str());
6416 String fullDir = parent.resolve(dirName);
6417 if (!removeDirectory(fullDir))
6418 return false;
6419 return true;
6420 }
6421 }
6422 return true;
6423 }
6425 virtual bool parse(Element *elem)
6426 {
6427 if (!parent.getAttribute(elem, "file", fileName))
6428 return false;
6429 if (fileName.size() > 0)
6430 delType = DEL_FILE;
6431 if (!parent.getAttribute(elem, "dir", dirName))
6432 return false;
6433 if (dirName.size() > 0)
6434 delType = DEL_DIR;
6435 if (fileName.size()>0 && dirName.size()>0)
6436 {
6437 error("<delete> can have one attribute of file= or dir=");
6438 return false;
6439 }
6440 if (fileName.size()==0 && dirName.size()==0)
6441 {
6442 error("<delete> must have one attribute of file= or dir=");
6443 return false;
6444 }
6445 String ret;
6446 if (!parent.getAttribute(elem, "verbose", ret))
6447 return false;
6448 if (ret.size()>0 && !getBool(ret, verbose))
6449 return false;
6450 if (!parent.getAttribute(elem, "quiet", ret))
6451 return false;
6452 if (ret.size()>0 && !getBool(ret, quiet))
6453 return false;
6454 if (!parent.getAttribute(elem, "failonerror", ret))
6455 return false;
6456 if (ret.size()>0 && !getBool(ret, failOnError))
6457 return false;
6458 return true;
6459 }
6461 private:
6463 int delType;
6464 String dirName;
6465 String fileName;
6466 bool verbose;
6467 bool quiet;
6468 bool failOnError;
6469 };
6472 /**
6473 *
6474 */
6475 class TaskJar : public Task
6476 {
6477 public:
6479 TaskJar(MakeBase &par) : Task(par)
6480 { type = TASK_JAR; name = "jar"; }
6482 virtual ~TaskJar()
6483 {}
6485 virtual bool execute()
6486 {
6487 return true;
6488 }
6490 virtual bool parse(Element *elem)
6491 {
6492 return true;
6493 }
6494 };
6497 /**
6498 *
6499 */
6500 class TaskJavac : public Task
6501 {
6502 public:
6504 TaskJavac(MakeBase &par) : Task(par)
6505 { type = TASK_JAVAC; name = "javac"; }
6507 virtual ~TaskJavac()
6508 {}
6510 virtual bool execute()
6511 {
6512 return true;
6513 }
6515 virtual bool parse(Element *elem)
6516 {
6517 return true;
6518 }
6519 };
6522 /**
6523 *
6524 */
6525 class TaskLink : public Task
6526 {
6527 public:
6529 TaskLink(MakeBase &par) : Task(par)
6530 {
6531 type = TASK_LINK; name = "link";
6532 command = "g++";
6533 doStrip = false;
6534 stripCommand = "strip";
6535 objcopyCommand = "objcopy";
6536 }
6538 virtual ~TaskLink()
6539 {}
6541 virtual bool execute()
6542 {
6543 if (!listFiles(parent, fileSet))
6544 return false;
6545 String fileSetDir = fileSet.getDirectory();
6546 //trace("%d files in %s", fileSet.size(), fileSetDir.c_str());
6547 bool doit = false;
6548 String fullTarget = parent.resolve(fileName);
6549 String cmd = command;
6550 cmd.append(" -o ");
6551 cmd.append(fullTarget);
6552 cmd.append(" ");
6553 cmd.append(flags);
6554 for (unsigned int i=0 ; i<fileSet.size() ; i++)
6555 {
6556 cmd.append(" ");
6557 String obj;
6558 if (fileSetDir.size()>0)
6559 {
6560 obj.append(fileSetDir);
6561 obj.append("/");
6562 }
6563 obj.append(fileSet[i]);
6564 String fullObj = parent.resolve(obj);
6565 String nativeFullObj = getNativePath(fullObj);
6566 cmd.append(nativeFullObj);
6567 //trace("link: tgt:%s obj:%s", fullTarget.c_str(),
6568 // fullObj.c_str());
6569 if (isNewerThan(fullObj, fullTarget))
6570 doit = true;
6571 }
6572 cmd.append(" ");
6573 cmd.append(libs);
6574 if (!doit)
6575 {
6576 //trace("link not needed");
6577 return true;
6578 }
6579 //trace("LINK cmd:%s", cmd.c_str());
6582 String outbuf, errbuf;
6583 if (!executeCommand(cmd.c_str(), "", outbuf, errbuf))
6584 {
6585 error("LINK problem: %s", errbuf.c_str());
6586 return false;
6587 }
6589 if (symFileName.size()>0)
6590 {
6591 String symFullName = parent.resolve(symFileName);
6592 cmd = objcopyCommand;
6593 cmd.append(" --only-keep-debug ");
6594 cmd.append(getNativePath(fullTarget));
6595 cmd.append(" ");
6596 cmd.append(getNativePath(symFullName));
6597 if (!executeCommand(cmd, "", outbuf, errbuf))
6598 {
6599 error("<strip> symbol file failed : %s", errbuf.c_str());
6600 return false;
6601 }
6602 }
6604 if (doStrip)
6605 {
6606 cmd = stripCommand;
6607 cmd.append(" ");
6608 cmd.append(getNativePath(fullTarget));
6609 if (!executeCommand(cmd, "", outbuf, errbuf))
6610 {
6611 error("<strip> failed : %s", errbuf.c_str());
6612 return false;
6613 }
6614 }
6616 return true;
6617 }
6619 virtual bool parse(Element *elem)
6620 {
6621 String s;
6622 if (!parent.getAttribute(elem, "command", s))
6623 return false;
6624 if (s.size()>0)
6625 command = s;
6626 if (!parent.getAttribute(elem, "objcopycommand", s))
6627 return false;
6628 if (s.size()>0)
6629 objcopyCommand = s;
6630 if (!parent.getAttribute(elem, "stripcommand", s))
6631 return false;
6632 if (s.size()>0)
6633 stripCommand = s;
6634 if (!parent.getAttribute(elem, "out", fileName))
6635 return false;
6636 if (!parent.getAttribute(elem, "strip", s))
6637 return false;
6638 if (s.size()>0 && !getBool(s, doStrip))
6639 return false;
6640 if (!parent.getAttribute(elem, "symfile", symFileName))
6641 return false;
6643 std::vector<Element *> children = elem->getChildren();
6644 for (unsigned int i=0 ; i<children.size() ; i++)
6645 {
6646 Element *child = children[i];
6647 String tagName = child->getName();
6648 if (tagName == "fileset")
6649 {
6650 if (!parseFileSet(child, parent, fileSet))
6651 return false;
6652 }
6653 else if (tagName == "flags")
6654 {
6655 if (!parent.getValue(child, flags))
6656 return false;
6657 flags = strip(flags);
6658 }
6659 else if (tagName == "libs")
6660 {
6661 if (!parent.getValue(child, libs))
6662 return false;
6663 libs = strip(libs);
6664 }
6665 }
6666 return true;
6667 }
6669 private:
6671 String command;
6672 String fileName;
6673 String flags;
6674 String libs;
6675 FileSet fileSet;
6676 bool doStrip;
6677 String symFileName;
6678 String stripCommand;
6679 String objcopyCommand;
6681 };
6685 /**
6686 * Create a named directory
6687 */
6688 class TaskMakeFile : public Task
6689 {
6690 public:
6692 TaskMakeFile(MakeBase &par) : Task(par)
6693 { type = TASK_MAKEFILE; name = "makefile"; }
6695 virtual ~TaskMakeFile()
6696 {}
6698 virtual bool execute()
6699 {
6700 status(" : %s", fileName.c_str());
6701 String fullName = parent.resolve(fileName);
6702 if (!isNewerThan(parent.getURI().getPath(), fullName))
6703 {
6704 //trace("skipped <makefile>");
6705 return true;
6706 }
6707 //trace("fullName:%s", fullName.c_str());
6708 FILE *f = fopen(fullName.c_str(), "w");
6709 if (!f)
6710 {
6711 error("<makefile> could not open %s for writing : %s",
6712 fullName.c_str(), strerror(errno));
6713 return false;
6714 }
6715 for (unsigned int i=0 ; i<text.size() ; i++)
6716 fputc(text[i], f);
6717 fputc('\n', f);
6718 fclose(f);
6719 return true;
6720 }
6722 virtual bool parse(Element *elem)
6723 {
6724 if (!parent.getAttribute(elem, "file", fileName))
6725 return false;
6726 if (fileName.size() == 0)
6727 {
6728 error("<makefile> requires 'file=\"filename\"' attribute");
6729 return false;
6730 }
6731 if (!parent.getValue(elem, text))
6732 return false;
6733 text = leftJustify(text);
6734 //trace("dirname:%s", dirName.c_str());
6735 return true;
6736 }
6738 private:
6740 String fileName;
6741 String text;
6742 };
6746 /**
6747 * Create a named directory
6748 */
6749 class TaskMkDir : public Task
6750 {
6751 public:
6753 TaskMkDir(MakeBase &par) : Task(par)
6754 { type = TASK_MKDIR; name = "mkdir"; }
6756 virtual ~TaskMkDir()
6757 {}
6759 virtual bool execute()
6760 {
6761 status(" : %s", dirName.c_str());
6762 String fullDir = parent.resolve(dirName);
6763 //trace("fullDir:%s", fullDir.c_str());
6764 if (!createDirectory(fullDir))
6765 return false;
6766 return true;
6767 }
6769 virtual bool parse(Element *elem)
6770 {
6771 if (!parent.getAttribute(elem, "dir", dirName))
6772 return false;
6773 if (dirName.size() == 0)
6774 {
6775 error("<mkdir> requires 'dir=\"dirname\"' attribute");
6776 return false;
6777 }
6778 return true;
6779 }
6781 private:
6783 String dirName;
6784 };
6788 /**
6789 * Create a named directory
6790 */
6791 class TaskMsgFmt: public Task
6792 {
6793 public:
6795 TaskMsgFmt(MakeBase &par) : Task(par)
6796 {
6797 type = TASK_MSGFMT;
6798 name = "msgfmt";
6799 command = "msgfmt";
6800 owndir = false;
6801 outName = "";
6802 }
6804 virtual ~TaskMsgFmt()
6805 {}
6807 virtual bool execute()
6808 {
6809 if (!listFiles(parent, fileSet))
6810 return false;
6811 String fileSetDir = fileSet.getDirectory();
6813 //trace("msgfmt: %d", fileSet.size());
6814 for (unsigned int i=0 ; i<fileSet.size() ; i++)
6815 {
6816 String fileName = fileSet[i];
6817 if (getSuffix(fileName) != "po")
6818 continue;
6819 String sourcePath;
6820 if (fileSetDir.size()>0)
6821 {
6822 sourcePath.append(fileSetDir);
6823 sourcePath.append("/");
6824 }
6825 sourcePath.append(fileName);
6826 String fullSource = parent.resolve(sourcePath);
6828 String destPath;
6829 if (toDirName.size()>0)
6830 {
6831 destPath.append(toDirName);
6832 destPath.append("/");
6833 }
6834 if (owndir)
6835 {
6836 String subdir = fileName;
6837 unsigned int pos = subdir.find_last_of('.');
6838 if (pos != subdir.npos)
6839 subdir = subdir.substr(0, pos);
6840 destPath.append(subdir);
6841 destPath.append("/");
6842 }
6843 //Pick the output file name
6844 if (outName.size() > 0)
6845 {
6846 destPath.append(outName);
6847 }
6848 else
6849 {
6850 destPath.append(fileName);
6851 destPath[destPath.size()-2] = 'm';
6852 }
6854 String fullDest = parent.resolve(destPath);
6856 if (!isNewerThan(fullSource, fullDest))
6857 {
6858 //trace("skip %s", fullSource.c_str());
6859 continue;
6860 }
6862 String cmd = command;
6863 cmd.append(" ");
6864 cmd.append(fullSource);
6865 cmd.append(" -o ");
6866 cmd.append(fullDest);
6868 int pos = fullDest.find_last_of('/');
6869 if (pos>0)
6870 {
6871 String fullDestPath = fullDest.substr(0, pos);
6872 if (!createDirectory(fullDestPath))
6873 return false;
6874 }
6878 String outString, errString;
6879 if (!executeCommand(cmd.c_str(), "", outString, errString))
6880 {
6881 error("<msgfmt> problem: %s", errString.c_str());
6882 return false;
6883 }
6884 }
6886 return true;
6887 }
6889 virtual bool parse(Element *elem)
6890 {
6891 String s;
6892 if (!parent.getAttribute(elem, "command", s))
6893 return false;
6894 if (s.size()>0)
6895 command = s;
6896 if (!parent.getAttribute(elem, "todir", toDirName))
6897 return false;
6898 if (!parent.getAttribute(elem, "out", outName))
6899 return false;
6900 if (!parent.getAttribute(elem, "owndir", s))
6901 return false;
6902 if (s.size()>0 && !getBool(s, owndir))
6903 return false;
6905 std::vector<Element *> children = elem->getChildren();
6906 for (unsigned int i=0 ; i<children.size() ; i++)
6907 {
6908 Element *child = children[i];
6909 String tagName = child->getName();
6910 if (tagName == "fileset")
6911 {
6912 if (!parseFileSet(child, parent, fileSet))
6913 return false;
6914 }
6915 }
6916 return true;
6917 }
6919 private:
6921 String command;
6922 String toDirName;
6923 String outName;
6924 FileSet fileSet;
6925 bool owndir;
6927 };
6931 /**
6932 * Perform a Package-Config query similar to pkg-config
6933 */
6934 class TaskPkgConfig : public Task
6935 {
6936 public:
6938 typedef enum
6939 {
6940 PKG_CONFIG_QUERY_CFLAGS,
6941 PKG_CONFIG_QUERY_LIBS,
6942 PKG_CONFIG_QUERY_BOTH
6943 } QueryTypes;
6945 TaskPkgConfig(MakeBase &par) : Task(par)
6946 {
6947 type = TASK_PKG_CONFIG; name = "pkg-config";
6948 }
6950 virtual ~TaskPkgConfig()
6951 {}
6953 virtual bool execute()
6954 {
6955 String path = parent.resolve(pkg_config_path);
6956 //fill this in
6957 return true;
6958 }
6960 virtual bool parse(Element *elem)
6961 {
6962 String s;
6963 if (!parent.getAttribute(elem, "path", s))
6964 return false;
6965 if (s.size()>0)
6966 pkg_config_path = s;
6967 if (!parent.getAttribute(elem, "query", s))
6968 return false;
6969 if (s == "cflags")
6970 query = PKG_CONFIG_QUERY_CFLAGS;
6971 else if (s == "libs")
6972 query = PKG_CONFIG_QUERY_LIBS;
6973 else if (s == "both")
6974 query = PKG_CONFIG_QUERY_BOTH;
6975 else
6976 {
6977 error("<pkg-config> requires 'query=\"type\"' attribute");
6978 error("where type = cflags, libs, or both");
6979 return false;
6980 }
6981 return true;
6982 }
6984 private:
6986 String pkg_config_path;
6987 int query;
6989 };
6996 /**
6997 * Process an archive to allow random access
6998 */
6999 class TaskRanlib : public Task
7000 {
7001 public:
7003 TaskRanlib(MakeBase &par) : Task(par)
7004 {
7005 type = TASK_RANLIB; name = "ranlib";
7006 command = "ranlib";
7007 }
7009 virtual ~TaskRanlib()
7010 {}
7012 virtual bool execute()
7013 {
7014 String fullName = parent.resolve(fileName);
7015 //trace("fullDir:%s", fullDir.c_str());
7016 String cmd = command;
7017 cmd.append(" ");
7018 cmd.append(fullName);
7019 String outbuf, errbuf;
7020 if (!executeCommand(cmd, "", outbuf, errbuf))
7021 return false;
7022 return true;
7023 }
7025 virtual bool parse(Element *elem)
7026 {
7027 String s;
7028 if (!parent.getAttribute(elem, "command", s))
7029 return false;
7030 if (s.size()>0)
7031 command = s;
7032 if (!parent.getAttribute(elem, "file", fileName))
7033 return false;
7034 if (fileName.size() == 0)
7035 {
7036 error("<ranlib> requires 'file=\"fileNname\"' attribute");
7037 return false;
7038 }
7039 return true;
7040 }
7042 private:
7044 String fileName;
7045 String command;
7046 };
7050 /**
7051 * Run the "ar" command to archive .o's into a .a
7052 */
7053 class TaskRC : public Task
7054 {
7055 public:
7057 TaskRC(MakeBase &par) : Task(par)
7058 {
7059 type = TASK_RC; name = "rc";
7060 command = "windres";
7061 }
7063 virtual ~TaskRC()
7064 {}
7066 virtual bool execute()
7067 {
7068 String fullFile = parent.resolve(fileName);
7069 String fullOut = parent.resolve(outName);
7070 if (!isNewerThan(fullFile, fullOut))
7071 return true;
7072 String cmd = command;
7073 cmd.append(" -o ");
7074 cmd.append(fullOut);
7075 cmd.append(" ");
7076 cmd.append(flags);
7077 cmd.append(" ");
7078 cmd.append(fullFile);
7080 String outString, errString;
7081 if (!executeCommand(cmd.c_str(), "", outString, errString))
7082 {
7083 error("RC problem: %s", errString.c_str());
7084 return false;
7085 }
7086 return true;
7087 }
7089 virtual bool parse(Element *elem)
7090 {
7091 if (!parent.getAttribute(elem, "command", command))
7092 return false;
7093 if (!parent.getAttribute(elem, "file", fileName))
7094 return false;
7095 if (!parent.getAttribute(elem, "out", outName))
7096 return false;
7097 std::vector<Element *> children = elem->getChildren();
7098 for (unsigned int i=0 ; i<children.size() ; i++)
7099 {
7100 Element *child = children[i];
7101 String tagName = child->getName();
7102 if (tagName == "flags")
7103 {
7104 if (!parent.getValue(child, flags))
7105 return false;
7106 }
7107 }
7108 return true;
7109 }
7111 private:
7113 String command;
7114 String flags;
7115 String fileName;
7116 String outName;
7118 };
7122 /**
7123 * Collect .o's into a .so or DLL
7124 */
7125 class TaskSharedLib : public Task
7126 {
7127 public:
7129 TaskSharedLib(MakeBase &par) : Task(par)
7130 {
7131 type = TASK_SHAREDLIB; name = "dll";
7132 command = "dllwrap";
7133 }
7135 virtual ~TaskSharedLib()
7136 {}
7138 virtual bool execute()
7139 {
7140 //trace("###########HERE %d", fileSet.size());
7141 bool doit = false;
7143 String fullOut = parent.resolve(fileName);
7144 //trace("ar fullout: %s", fullOut.c_str());
7146 if (!listFiles(parent, fileSet))
7147 return false;
7148 String fileSetDir = fileSet.getDirectory();
7150 for (unsigned int i=0 ; i<fileSet.size() ; i++)
7151 {
7152 String fname;
7153 if (fileSetDir.size()>0)
7154 {
7155 fname.append(fileSetDir);
7156 fname.append("/");
7157 }
7158 fname.append(fileSet[i]);
7159 String fullName = parent.resolve(fname);
7160 //trace("ar : %s/%s", fullOut.c_str(), fullName.c_str());
7161 if (isNewerThan(fullName, fullOut))
7162 doit = true;
7163 }
7164 //trace("Needs it:%d", doit);
7165 if (!doit)
7166 {
7167 return true;
7168 }
7170 String cmd = "dllwrap";
7171 cmd.append(" -o ");
7172 cmd.append(fullOut);
7173 if (defFileName.size()>0)
7174 {
7175 cmd.append(" --def ");
7176 cmd.append(defFileName);
7177 cmd.append(" ");
7178 }
7179 if (impFileName.size()>0)
7180 {
7181 cmd.append(" --implib ");
7182 cmd.append(impFileName);
7183 cmd.append(" ");
7184 }
7185 for (unsigned int i=0 ; i<fileSet.size() ; i++)
7186 {
7187 String fname;
7188 if (fileSetDir.size()>0)
7189 {
7190 fname.append(fileSetDir);
7191 fname.append("/");
7192 }
7193 fname.append(fileSet[i]);
7194 String fullName = parent.resolve(fname);
7196 cmd.append(" ");
7197 cmd.append(fullName);
7198 }
7199 cmd.append(" ");
7200 cmd.append(libs);
7202 String outString, errString;
7203 if (!executeCommand(cmd.c_str(), "", outString, errString))
7204 {
7205 error("<sharedlib> problem: %s", errString.c_str());
7206 return false;
7207 }
7209 return true;
7210 }
7212 virtual bool parse(Element *elem)
7213 {
7214 if (!parent.getAttribute(elem, "file", fileName))
7215 return false;
7216 if (!parent.getAttribute(elem, "import", impFileName))
7217 return false;
7218 if (!parent.getAttribute(elem, "def", defFileName))
7219 return false;
7221 std::vector<Element *> children = elem->getChildren();
7222 for (unsigned int i=0 ; i<children.size() ; i++)
7223 {
7224 Element *child = children[i];
7225 String tagName = child->getName();
7226 if (tagName == "fileset")
7227 {
7228 if (!parseFileSet(child, parent, fileSet))
7229 return false;
7230 }
7231 else if (tagName == "libs")
7232 {
7233 if (!parent.getValue(child, libs))
7234 return false;
7235 libs = strip(libs);
7236 }
7237 }
7238 return true;
7239 }
7241 private:
7243 String command;
7244 String fileName;
7245 String defFileName;
7246 String impFileName;
7247 FileSet fileSet;
7248 String libs;
7250 };
7254 /**
7255 * Run the "ar" command to archive .o's into a .a
7256 */
7257 class TaskStaticLib : public Task
7258 {
7259 public:
7261 TaskStaticLib(MakeBase &par) : Task(par)
7262 {
7263 type = TASK_STATICLIB; name = "staticlib";
7264 command = "ar crv";
7265 }
7267 virtual ~TaskStaticLib()
7268 {}
7270 virtual bool execute()
7271 {
7272 //trace("###########HERE %d", fileSet.size());
7273 bool doit = false;
7275 String fullOut = parent.resolve(fileName);
7276 //trace("ar fullout: %s", fullOut.c_str());
7278 if (!listFiles(parent, fileSet))
7279 return false;
7280 String fileSetDir = fileSet.getDirectory();
7282 for (unsigned int i=0 ; i<fileSet.size() ; i++)
7283 {
7284 String fname;
7285 if (fileSetDir.size()>0)
7286 {
7287 fname.append(fileSetDir);
7288 fname.append("/");
7289 }
7290 fname.append(fileSet[i]);
7291 String fullName = parent.resolve(fname);
7292 //trace("ar : %s/%s", fullOut.c_str(), fullName.c_str());
7293 if (isNewerThan(fullName, fullOut))
7294 doit = true;
7295 }
7296 //trace("Needs it:%d", doit);
7297 if (!doit)
7298 {
7299 return true;
7300 }
7302 String cmd = command;
7303 cmd.append(" ");
7304 cmd.append(fullOut);
7305 for (unsigned int i=0 ; i<fileSet.size() ; i++)
7306 {
7307 String fname;
7308 if (fileSetDir.size()>0)
7309 {
7310 fname.append(fileSetDir);
7311 fname.append("/");
7312 }
7313 fname.append(fileSet[i]);
7314 String fullName = parent.resolve(fname);
7316 cmd.append(" ");
7317 cmd.append(fullName);
7318 }
7320 String outString, errString;
7321 if (!executeCommand(cmd.c_str(), "", outString, errString))
7322 {
7323 error("<staticlib> problem: %s", errString.c_str());
7324 return false;
7325 }
7327 return true;
7328 }
7331 virtual bool parse(Element *elem)
7332 {
7333 String s;
7334 if (!parent.getAttribute(elem, "command", s))
7335 return false;
7336 if (s.size()>0)
7337 command = s;
7338 if (!parent.getAttribute(elem, "file", fileName))
7339 return false;
7341 std::vector<Element *> children = elem->getChildren();
7342 for (unsigned int i=0 ; i<children.size() ; i++)
7343 {
7344 Element *child = children[i];
7345 String tagName = child->getName();
7346 if (tagName == "fileset")
7347 {
7348 if (!parseFileSet(child, parent, fileSet))
7349 return false;
7350 }
7351 }
7352 return true;
7353 }
7355 private:
7357 String command;
7358 String fileName;
7359 FileSet fileSet;
7361 };
7366 /**
7367 * Strip an executable
7368 */
7369 class TaskStrip : public Task
7370 {
7371 public:
7373 TaskStrip(MakeBase &par) : Task(par)
7374 { type = TASK_STRIP; name = "strip"; }
7376 virtual ~TaskStrip()
7377 {}
7379 virtual bool execute()
7380 {
7381 String fullName = parent.resolve(fileName);
7382 //trace("fullDir:%s", fullDir.c_str());
7383 String cmd;
7384 String outbuf, errbuf;
7386 if (symFileName.size()>0)
7387 {
7388 String symFullName = parent.resolve(symFileName);
7389 cmd = "objcopy --only-keep-debug ";
7390 cmd.append(getNativePath(fullName));
7391 cmd.append(" ");
7392 cmd.append(getNativePath(symFullName));
7393 if (!executeCommand(cmd, "", outbuf, errbuf))
7394 {
7395 error("<strip> symbol file failed : %s", errbuf.c_str());
7396 return false;
7397 }
7398 }
7400 cmd = "strip ";
7401 cmd.append(getNativePath(fullName));
7402 if (!executeCommand(cmd, "", outbuf, errbuf))
7403 {
7404 error("<strip> failed : %s", errbuf.c_str());
7405 return false;
7406 }
7407 return true;
7408 }
7410 virtual bool parse(Element *elem)
7411 {
7412 if (!parent.getAttribute(elem, "file", fileName))
7413 return false;
7414 if (!parent.getAttribute(elem, "symfile", symFileName))
7415 return false;
7416 if (fileName.size() == 0)
7417 {
7418 error("<strip> requires 'file=\"fileName\"' attribute");
7419 return false;
7420 }
7421 return true;
7422 }
7424 private:
7426 String fileName;
7427 String symFileName;
7428 };
7431 /**
7432 *
7433 */
7434 class TaskTouch : public Task
7435 {
7436 public:
7438 TaskTouch(MakeBase &par) : Task(par)
7439 { type = TASK_TOUCH; name = "touch"; }
7441 virtual ~TaskTouch()
7442 {}
7444 virtual bool execute()
7445 {
7446 String fullName = parent.resolve(fileName);
7447 String nativeFile = getNativePath(fullName);
7448 if (!isRegularFile(fullName) && !isDirectory(fullName))
7449 {
7450 // S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH
7451 int ret = creat(nativeFile.c_str(), 0666);
7452 if (ret != 0)
7453 {
7454 error("<touch> could not create '%s' : %s",
7455 nativeFile.c_str(), strerror(ret));
7456 return false;
7457 }
7458 return true;
7459 }
7460 int ret = utime(nativeFile.c_str(), (struct utimbuf *)0);
7461 if (ret != 0)
7462 {
7463 error("<touch> could not update the modification time for '%s' : %s",
7464 nativeFile.c_str(), strerror(ret));
7465 return false;
7466 }
7467 return true;
7468 }
7470 virtual bool parse(Element *elem)
7471 {
7472 //trace("touch parse");
7473 if (!parent.getAttribute(elem, "file", fileName))
7474 return false;
7475 if (fileName.size() == 0)
7476 {
7477 error("<touch> requires 'file=\"fileName\"' attribute");
7478 return false;
7479 }
7480 return true;
7481 }
7483 String fileName;
7484 };
7487 /**
7488 *
7489 */
7490 class TaskTstamp : public Task
7491 {
7492 public:
7494 TaskTstamp(MakeBase &par) : Task(par)
7495 { type = TASK_TSTAMP; name = "tstamp"; }
7497 virtual ~TaskTstamp()
7498 {}
7500 virtual bool execute()
7501 {
7502 return true;
7503 }
7505 virtual bool parse(Element *elem)
7506 {
7507 //trace("tstamp parse");
7508 return true;
7509 }
7510 };
7514 /**
7515 *
7516 */
7517 Task *Task::createTask(Element *elem, int lineNr)
7518 {
7519 String tagName = elem->getName();
7520 //trace("task:%s", tagName.c_str());
7521 Task *task = NULL;
7522 if (tagName == "cc")
7523 task = new TaskCC(parent);
7524 else if (tagName == "copy")
7525 task = new TaskCopy(parent);
7526 else if (tagName == "delete")
7527 task = new TaskDelete(parent);
7528 else if (tagName == "jar")
7529 task = new TaskJar(parent);
7530 else if (tagName == "javac")
7531 task = new TaskJavac(parent);
7532 else if (tagName == "link")
7533 task = new TaskLink(parent);
7534 else if (tagName == "makefile")
7535 task = new TaskMakeFile(parent);
7536 else if (tagName == "mkdir")
7537 task = new TaskMkDir(parent);
7538 else if (tagName == "msgfmt")
7539 task = new TaskMsgFmt(parent);
7540 else if (tagName == "ranlib")
7541 task = new TaskRanlib(parent);
7542 else if (tagName == "rc")
7543 task = new TaskRC(parent);
7544 else if (tagName == "sharedlib")
7545 task = new TaskSharedLib(parent);
7546 else if (tagName == "staticlib")
7547 task = new TaskStaticLib(parent);
7548 else if (tagName == "strip")
7549 task = new TaskStrip(parent);
7550 else if (tagName == "touch")
7551 task = new TaskTouch(parent);
7552 else if (tagName == "tstamp")
7553 task = new TaskTstamp(parent);
7554 else
7555 {
7556 error("Unknown task '%s'", tagName.c_str());
7557 return NULL;
7558 }
7560 task->setLine(lineNr);
7562 if (!task->parse(elem))
7563 {
7564 delete task;
7565 return NULL;
7566 }
7567 return task;
7568 }
7572 //########################################################################
7573 //# T A R G E T
7574 //########################################################################
7576 /**
7577 *
7578 */
7579 class Target : public MakeBase
7580 {
7582 public:
7584 /**
7585 *
7586 */
7587 Target(Make &par) : parent(par)
7588 { init(); }
7590 /**
7591 *
7592 */
7593 Target(const Target &other) : parent(other.parent)
7594 { init(); assign(other); }
7596 /**
7597 *
7598 */
7599 Target &operator=(const Target &other)
7600 { init(); assign(other); return *this; }
7602 /**
7603 *
7604 */
7605 virtual ~Target()
7606 { cleanup() ; }
7609 /**
7610 *
7611 */
7612 virtual Make &getParent()
7613 { return parent; }
7615 /**
7616 *
7617 */
7618 virtual String getName()
7619 { return name; }
7621 /**
7622 *
7623 */
7624 virtual void setName(const String &val)
7625 { name = val; }
7627 /**
7628 *
7629 */
7630 virtual String getDescription()
7631 { return description; }
7633 /**
7634 *
7635 */
7636 virtual void setDescription(const String &val)
7637 { description = val; }
7639 /**
7640 *
7641 */
7642 virtual void addDependency(const String &val)
7643 { deps.push_back(val); }
7645 /**
7646 *
7647 */
7648 virtual void parseDependencies(const String &val)
7649 { deps = tokenize(val, ", "); }
7651 /**
7652 *
7653 */
7654 virtual std::vector<String> &getDependencies()
7655 { return deps; }
7657 /**
7658 *
7659 */
7660 virtual String getIf()
7661 { return ifVar; }
7663 /**
7664 *
7665 */
7666 virtual void setIf(const String &val)
7667 { ifVar = val; }
7669 /**
7670 *
7671 */
7672 virtual String getUnless()
7673 { return unlessVar; }
7675 /**
7676 *
7677 */
7678 virtual void setUnless(const String &val)
7679 { unlessVar = val; }
7681 /**
7682 *
7683 */
7684 virtual void addTask(Task *val)
7685 { tasks.push_back(val); }
7687 /**
7688 *
7689 */
7690 virtual std::vector<Task *> &getTasks()
7691 { return tasks; }
7693 private:
7695 void init()
7696 {
7697 }
7699 void cleanup()
7700 {
7701 tasks.clear();
7702 }
7704 void assign(const Target &other)
7705 {
7706 //parent = other.parent;
7707 name = other.name;
7708 description = other.description;
7709 ifVar = other.ifVar;
7710 unlessVar = other.unlessVar;
7711 deps = other.deps;
7712 tasks = other.tasks;
7713 }
7715 Make &parent;
7717 String name;
7719 String description;
7721 String ifVar;
7723 String unlessVar;
7725 std::vector<String> deps;
7727 std::vector<Task *> tasks;
7729 };
7738 //########################################################################
7739 //# M A K E
7740 //########################################################################
7743 /**
7744 *
7745 */
7746 class Make : public MakeBase
7747 {
7749 public:
7751 /**
7752 *
7753 */
7754 Make()
7755 { init(); }
7757 /**
7758 *
7759 */
7760 Make(const Make &other)
7761 { assign(other); }
7763 /**
7764 *
7765 */
7766 Make &operator=(const Make &other)
7767 { assign(other); return *this; }
7769 /**
7770 *
7771 */
7772 virtual ~Make()
7773 { cleanup(); }
7775 /**
7776 *
7777 */
7778 virtual std::map<String, Target> &getTargets()
7779 { return targets; }
7782 /**
7783 *
7784 */
7785 virtual String version()
7786 { return BUILDTOOL_VERSION; }
7788 /**
7789 * Overload a <property>
7790 */
7791 virtual bool specifyProperty(const String &name,
7792 const String &value);
7794 /**
7795 *
7796 */
7797 virtual bool run();
7799 /**
7800 *
7801 */
7802 virtual bool run(const String &target);
7806 private:
7808 /**
7809 *
7810 */
7811 void init();
7813 /**
7814 *
7815 */
7816 void cleanup();
7818 /**
7819 *
7820 */
7821 void assign(const Make &other);
7823 /**
7824 *
7825 */
7826 bool executeTask(Task &task);
7829 /**
7830 *
7831 */
7832 bool executeTarget(Target &target,
7833 std::set<String> &targetsCompleted);
7836 /**
7837 *
7838 */
7839 bool execute();
7841 /**
7842 *
7843 */
7844 bool checkTargetDependencies(Target &prop,
7845 std::vector<String> &depList);
7847 /**
7848 *
7849 */
7850 bool parsePropertyFile(const String &fileName,
7851 const String &prefix);
7853 /**
7854 *
7855 */
7856 bool parseProperty(Element *elem);
7858 /**
7859 *
7860 */
7861 bool parseFile();
7863 /**
7864 *
7865 */
7866 std::vector<String> glob(const String &pattern);
7869 //###############
7870 //# Fields
7871 //###############
7873 String projectName;
7875 String currentTarget;
7877 String defaultTarget;
7879 String specifiedTarget;
7881 String baseDir;
7883 String description;
7885 String envAlias;
7887 //std::vector<Property> properties;
7889 std::map<String, Target> targets;
7891 std::vector<Task *> allTasks;
7893 std::map<String, String> specifiedProperties;
7895 };
7898 //########################################################################
7899 //# C L A S S M A I N T E N A N C E
7900 //########################################################################
7902 /**
7903 *
7904 */
7905 void Make::init()
7906 {
7907 uri = "build.xml";
7908 projectName = "";
7909 currentTarget = "";
7910 defaultTarget = "";
7911 specifiedTarget = "";
7912 baseDir = "";
7913 description = "";
7914 envAlias = "";
7915 properties.clear();
7916 for (unsigned int i = 0 ; i < allTasks.size() ; i++)
7917 delete allTasks[i];
7918 allTasks.clear();
7919 }
7923 /**
7924 *
7925 */
7926 void Make::cleanup()
7927 {
7928 for (unsigned int i = 0 ; i < allTasks.size() ; i++)
7929 delete allTasks[i];
7930 allTasks.clear();
7931 }
7935 /**
7936 *
7937 */
7938 void Make::assign(const Make &other)
7939 {
7940 uri = other.uri;
7941 projectName = other.projectName;
7942 currentTarget = other.currentTarget;
7943 defaultTarget = other.defaultTarget;
7944 specifiedTarget = other.specifiedTarget;
7945 baseDir = other.baseDir;
7946 description = other.description;
7947 properties = other.properties;
7948 }
7952 //########################################################################
7953 //# U T I L I T Y T A S K S
7954 //########################################################################
7956 /**
7957 * Perform a file globbing
7958 */
7959 std::vector<String> Make::glob(const String &pattern)
7960 {
7961 std::vector<String> res;
7962 return res;
7963 }
7966 //########################################################################
7967 //# P U B L I C A P I
7968 //########################################################################
7972 /**
7973 *
7974 */
7975 bool Make::executeTarget(Target &target,
7976 std::set<String> &targetsCompleted)
7977 {
7979 String name = target.getName();
7981 //First get any dependencies for this target
7982 std::vector<String> deps = target.getDependencies();
7983 for (unsigned int i=0 ; i<deps.size() ; i++)
7984 {
7985 String dep = deps[i];
7986 //Did we do it already? Skip
7987 if (targetsCompleted.find(dep)!=targetsCompleted.end())
7988 continue;
7990 std::map<String, Target> &tgts =
7991 target.getParent().getTargets();
7992 std::map<String, Target>::iterator iter =
7993 tgts.find(dep);
7994 if (iter == tgts.end())
7995 {
7996 error("Target '%s' dependency '%s' not found",
7997 name.c_str(), dep.c_str());
7998 return false;
7999 }
8000 Target depTarget = iter->second;
8001 if (!executeTarget(depTarget, targetsCompleted))
8002 {
8003 return false;
8004 }
8005 }
8007 status("## Target : %s", name.c_str());
8009 //Now let's do the tasks
8010 std::vector<Task *> &tasks = target.getTasks();
8011 for (unsigned int i=0 ; i<tasks.size() ; i++)
8012 {
8013 Task *task = tasks[i];
8014 status("---- task : %s", task->getName().c_str());
8015 if (!task->execute())
8016 {
8017 return false;
8018 }
8019 }
8021 targetsCompleted.insert(name);
8023 return true;
8024 }
8028 /**
8029 * Main execute() method. Start here and work
8030 * up the dependency tree
8031 */
8032 bool Make::execute()
8033 {
8034 status("######## EXECUTE");
8036 //Determine initial target
8037 if (specifiedTarget.size()>0)
8038 {
8039 currentTarget = specifiedTarget;
8040 }
8041 else if (defaultTarget.size()>0)
8042 {
8043 currentTarget = defaultTarget;
8044 }
8045 else
8046 {
8047 error("execute: no specified or default target requested");
8048 return false;
8049 }
8051 std::map<String, Target>::iterator iter =
8052 targets.find(currentTarget);
8053 if (iter == targets.end())
8054 {
8055 error("Initial target '%s' not found",
8056 currentTarget.c_str());
8057 return false;
8058 }
8060 //Now run
8061 Target target = iter->second;
8062 std::set<String> targetsCompleted;
8063 if (!executeTarget(target, targetsCompleted))
8064 {
8065 return false;
8066 }
8068 status("######## EXECUTE COMPLETE");
8069 return true;
8070 }
8075 /**
8076 *
8077 */
8078 bool Make::checkTargetDependencies(Target &target,
8079 std::vector<String> &depList)
8080 {
8081 String tgtName = target.getName().c_str();
8082 depList.push_back(tgtName);
8084 std::vector<String> deps = target.getDependencies();
8085 for (unsigned int i=0 ; i<deps.size() ; i++)
8086 {
8087 String dep = deps[i];
8088 //First thing entered was the starting Target
8089 if (dep == depList[0])
8090 {
8091 error("Circular dependency '%s' found at '%s'",
8092 dep.c_str(), tgtName.c_str());
8093 std::vector<String>::iterator diter;
8094 for (diter=depList.begin() ; diter!=depList.end() ; diter++)
8095 {
8096 error(" %s", diter->c_str());
8097 }
8098 return false;
8099 }
8101 std::map<String, Target> &tgts =
8102 target.getParent().getTargets();
8103 std::map<String, Target>::iterator titer = tgts.find(dep);
8104 if (titer == tgts.end())
8105 {
8106 error("Target '%s' dependency '%s' not found",
8107 tgtName.c_str(), dep.c_str());
8108 return false;
8109 }
8110 if (!checkTargetDependencies(titer->second, depList))
8111 {
8112 return false;
8113 }
8114 }
8115 return true;
8116 }
8122 static int getword(int pos, const String &inbuf, String &result)
8123 {
8124 int p = pos;
8125 int len = (int)inbuf.size();
8126 String val;
8127 while (p < len)
8128 {
8129 char ch = inbuf[p];
8130 if (!isalnum(ch) && ch!='.' && ch!='_')
8131 break;
8132 val.push_back(ch);
8133 p++;
8134 }
8135 result = val;
8136 return p;
8137 }
8142 /**
8143 *
8144 */
8145 bool Make::parsePropertyFile(const String &fileName,
8146 const String &prefix)
8147 {
8148 FILE *f = fopen(fileName.c_str(), "r");
8149 if (!f)
8150 {
8151 error("could not open property file %s", fileName.c_str());
8152 return false;
8153 }
8154 int linenr = 0;
8155 while (!feof(f))
8156 {
8157 char buf[256];
8158 if (!fgets(buf, 255, f))
8159 break;
8160 linenr++;
8161 String s = buf;
8162 s = trim(s);
8163 int len = s.size();
8164 if (len == 0)
8165 continue;
8166 if (s[0] == '#')
8167 continue;
8168 String key;
8169 String val;
8170 int p = 0;
8171 int p2 = getword(p, s, key);
8172 if (p2 <= p)
8173 {
8174 error("property file %s, line %d: expected keyword",
8175 fileName.c_str(), linenr);
8176 return false;
8177 }
8178 if (prefix.size() > 0)
8179 {
8180 key.insert(0, prefix);
8181 }
8183 //skip whitespace
8184 for (p=p2 ; p<len ; p++)
8185 if (!isspace(s[p]))
8186 break;
8188 if (p>=len || s[p]!='=')
8189 {
8190 error("property file %s, line %d: expected '='",
8191 fileName.c_str(), linenr);
8192 return false;
8193 }
8194 p++;
8196 //skip whitespace
8197 for ( ; p<len ; p++)
8198 if (!isspace(s[p]))
8199 break;
8201 /* This way expects a word after the =
8202 p2 = getword(p, s, val);
8203 if (p2 <= p)
8204 {
8205 error("property file %s, line %d: expected value",
8206 fileName.c_str(), linenr);
8207 return false;
8208 }
8209 */
8210 // This way gets the rest of the line after the =
8211 if (p>=len)
8212 {
8213 error("property file %s, line %d: expected value",
8214 fileName.c_str(), linenr);
8215 return false;
8216 }
8217 val = s.substr(p);
8218 if (key.size()==0)
8219 continue;
8220 //allow property to be set, even if val=""
8222 //trace("key:'%s' val:'%s'", key.c_str(), val.c_str());
8223 //See if we wanted to overload this property
8224 std::map<String, String>::iterator iter =
8225 specifiedProperties.find(key);
8226 if (iter!=specifiedProperties.end())
8227 {
8228 val = iter->second;
8229 status("overloading property '%s' = '%s'",
8230 key.c_str(), val.c_str());
8231 }
8232 properties[key] = val;
8233 }
8234 fclose(f);
8235 return true;
8236 }
8241 /**
8242 *
8243 */
8244 bool Make::parseProperty(Element *elem)
8245 {
8246 std::vector<Attribute> &attrs = elem->getAttributes();
8247 for (unsigned int i=0 ; i<attrs.size() ; i++)
8248 {
8249 String attrName = attrs[i].getName();
8250 String attrVal = attrs[i].getValue();
8252 if (attrName == "name")
8253 {
8254 String val;
8255 if (!getAttribute(elem, "value", val))
8256 return false;
8257 if (val.size() > 0)
8258 {
8259 properties[attrVal] = val;
8260 }
8261 else
8262 {
8263 if (!getAttribute(elem, "location", val))
8264 return false;
8265 //let the property exist, even if not defined
8266 properties[attrVal] = val;
8267 }
8268 //See if we wanted to overload this property
8269 std::map<String, String>::iterator iter =
8270 specifiedProperties.find(attrVal);
8271 if (iter != specifiedProperties.end())
8272 {
8273 val = iter->second;
8274 status("overloading property '%s' = '%s'",
8275 attrVal.c_str(), val.c_str());
8276 properties[attrVal] = val;
8277 }
8278 }
8279 else if (attrName == "file")
8280 {
8281 String prefix;
8282 if (!getAttribute(elem, "prefix", prefix))
8283 return false;
8284 if (prefix.size() > 0)
8285 {
8286 if (prefix[prefix.size()-1] != '.')
8287 prefix.push_back('.');
8288 }
8289 if (!parsePropertyFile(attrName, prefix))
8290 return false;
8291 }
8292 else if (attrName == "environment")
8293 {
8294 if (envAlias.size() > 0)
8295 {
8296 error("environment property can only be set once");
8297 return false;
8298 }
8299 envAlias = attrVal;
8300 }
8301 }
8303 return true;
8304 }
8309 /**
8310 *
8311 */
8312 bool Make::parseFile()
8313 {
8314 status("######## PARSE : %s", uri.getPath().c_str());
8316 setLine(0);
8318 Parser parser;
8319 Element *root = parser.parseFile(uri.getNativePath());
8320 if (!root)
8321 {
8322 error("Could not open %s for reading",
8323 uri.getNativePath().c_str());
8324 return false;
8325 }
8327 setLine(root->getLine());
8329 if (root->getChildren().size()==0 ||
8330 root->getChildren()[0]->getName()!="project")
8331 {
8332 error("Main xml element should be <project>");
8333 delete root;
8334 return false;
8335 }
8337 //########## Project attributes
8338 Element *project = root->getChildren()[0];
8339 String s = project->getAttribute("name");
8340 if (s.size() > 0)
8341 projectName = s;
8342 s = project->getAttribute("default");
8343 if (s.size() > 0)
8344 defaultTarget = s;
8345 s = project->getAttribute("basedir");
8346 if (s.size() > 0)
8347 baseDir = s;
8349 //######### PARSE MEMBERS
8350 std::vector<Element *> children = project->getChildren();
8351 for (unsigned int i=0 ; i<children.size() ; i++)
8352 {
8353 Element *elem = children[i];
8354 setLine(elem->getLine());
8355 String tagName = elem->getName();
8357 //########## DESCRIPTION
8358 if (tagName == "description")
8359 {
8360 description = parser.trim(elem->getValue());
8361 }
8363 //######### PROPERTY
8364 else if (tagName == "property")
8365 {
8366 if (!parseProperty(elem))
8367 return false;
8368 }
8370 //######### TARGET
8371 else if (tagName == "target")
8372 {
8373 String tname = elem->getAttribute("name");
8374 String tdesc = elem->getAttribute("description");
8375 String tdeps = elem->getAttribute("depends");
8376 String tif = elem->getAttribute("if");
8377 String tunless = elem->getAttribute("unless");
8378 Target target(*this);
8379 target.setName(tname);
8380 target.setDescription(tdesc);
8381 target.parseDependencies(tdeps);
8382 target.setIf(tif);
8383 target.setUnless(tunless);
8384 std::vector<Element *> telems = elem->getChildren();
8385 for (unsigned int i=0 ; i<telems.size() ; i++)
8386 {
8387 Element *telem = telems[i];
8388 Task breeder(*this);
8389 Task *task = breeder.createTask(telem, telem->getLine());
8390 if (!task)
8391 return false;
8392 allTasks.push_back(task);
8393 target.addTask(task);
8394 }
8396 //Check name
8397 if (tname.size() == 0)
8398 {
8399 error("no name for target");
8400 return false;
8401 }
8402 //Check for duplicate name
8403 if (targets.find(tname) != targets.end())
8404 {
8405 error("target '%s' already defined", tname.c_str());
8406 return false;
8407 }
8408 //more work than targets[tname]=target, but avoids default allocator
8409 targets.insert(std::make_pair<String, Target>(tname, target));
8410 }
8411 //######### none of the above
8412 else
8413 {
8414 error("unknown toplevel tag: <%s>", tagName.c_str());
8415 return false;
8416 }
8418 }
8420 std::map<String, Target>::iterator iter;
8421 for (iter = targets.begin() ; iter!= targets.end() ; iter++)
8422 {
8423 Target tgt = iter->second;
8424 std::vector<String> depList;
8425 if (!checkTargetDependencies(tgt, depList))
8426 {
8427 return false;
8428 }
8429 }
8432 delete root;
8433 status("######## PARSE COMPLETE");
8434 return true;
8435 }
8438 /**
8439 * Overload a <property>
8440 */
8441 bool Make::specifyProperty(const String &name, const String &value)
8442 {
8443 if (specifiedProperties.find(name) != specifiedProperties.end())
8444 {
8445 error("Property %s already specified", name.c_str());
8446 return false;
8447 }
8448 specifiedProperties[name] = value;
8449 return true;
8450 }
8454 /**
8455 *
8456 */
8457 bool Make::run()
8458 {
8459 if (!parseFile())
8460 return false;
8462 if (!execute())
8463 return false;
8465 return true;
8466 }
8471 /**
8472 * Get a formatted MM:SS.sss time elapsed string
8473 */
8474 static String
8475 timeDiffString(struct timeval &x, struct timeval &y)
8476 {
8477 long microsX = x.tv_usec;
8478 long secondsX = x.tv_sec;
8479 long microsY = y.tv_usec;
8480 long secondsY = y.tv_sec;
8481 if (microsX < microsY)
8482 {
8483 microsX += 1000000;
8484 secondsX -= 1;
8485 }
8487 int seconds = (int)(secondsX - secondsY);
8488 int millis = (int)((microsX - microsY)/1000);
8490 int minutes = seconds/60;
8491 seconds -= minutes*60;
8492 char buf[80];
8493 snprintf(buf, 79, "%dm %d.%03ds", minutes, seconds, millis);
8494 String ret = buf;
8495 return ret;
8497 }
8499 /**
8500 *
8501 */
8502 bool Make::run(const String &target)
8503 {
8504 status("####################################################");
8505 status("# %s", version().c_str());
8506 status("####################################################");
8507 struct timeval timeStart, timeEnd;
8508 ::gettimeofday(&timeStart, NULL);
8509 specifiedTarget = target;
8510 if (!run())
8511 return false;
8512 ::gettimeofday(&timeEnd, NULL);
8513 String timeStr = timeDiffString(timeEnd, timeStart);
8514 status("####################################################");
8515 status("# BuildTool Completed : %s", timeStr.c_str());
8516 status("####################################################");
8517 return true;
8518 }
8526 }// namespace buildtool
8527 //########################################################################
8528 //# M A I N
8529 //########################################################################
8531 typedef buildtool::String String;
8533 /**
8534 * Format an error message in printf() style
8535 */
8536 static void error(const char *fmt, ...)
8537 {
8538 va_list ap;
8539 va_start(ap, fmt);
8540 fprintf(stderr, "BuildTool error: ");
8541 vfprintf(stderr, fmt, ap);
8542 fprintf(stderr, "\n");
8543 va_end(ap);
8544 }
8547 static bool parseProperty(const String &s, String &name, String &val)
8548 {
8549 int len = s.size();
8550 int i;
8551 for (i=0 ; i<len ; i++)
8552 {
8553 char ch = s[i];
8554 if (ch == '=')
8555 break;
8556 name.push_back(ch);
8557 }
8558 if (i>=len || s[i]!='=')
8559 {
8560 error("property requires -Dname=value");
8561 return false;
8562 }
8563 i++;
8564 for ( ; i<len ; i++)
8565 {
8566 char ch = s[i];
8567 val.push_back(ch);
8568 }
8569 return true;
8570 }
8573 /**
8574 * Compare a buffer with a key, for the length of the key
8575 */
8576 static bool sequ(const String &buf, const char *key)
8577 {
8578 int len = buf.size();
8579 for (int i=0 ; key[i] && i<len ; i++)
8580 {
8581 if (key[i] != buf[i])
8582 return false;
8583 }
8584 return true;
8585 }
8587 static void usage(int argc, char **argv)
8588 {
8589 printf("usage:\n");
8590 printf(" %s [options] [target]\n", argv[0]);
8591 printf("Options:\n");
8592 printf(" -help, -h print this message\n");
8593 printf(" -version print the version information and exit\n");
8594 printf(" -file <file> use given buildfile\n");
8595 printf(" -f <file> ''\n");
8596 printf(" -D<property>=<value> use value for given property\n");
8597 }
8602 /**
8603 * Parse the command-line args, get our options,
8604 * and run this thing
8605 */
8606 static bool parseOptions(int argc, char **argv)
8607 {
8608 if (argc < 1)
8609 {
8610 error("Cannot parse arguments");
8611 return false;
8612 }
8614 buildtool::Make make;
8616 String target;
8618 //char *progName = argv[0];
8619 for (int i=1 ; i<argc ; i++)
8620 {
8621 String arg = argv[i];
8622 if (arg.size()>1 && arg[0]=='-')
8623 {
8624 if (arg == "-h" || arg == "-help")
8625 {
8626 usage(argc,argv);
8627 return true;
8628 }
8629 else if (arg == "-version")
8630 {
8631 printf("%s", make.version().c_str());
8632 return true;
8633 }
8634 else if (arg == "-f" || arg == "-file")
8635 {
8636 if (i>=argc)
8637 {
8638 usage(argc, argv);
8639 return false;
8640 }
8641 i++; //eat option
8642 make.setURI(argv[i]);
8643 }
8644 else if (arg.size()>2 && sequ(arg, "-D"))
8645 {
8646 String s = arg.substr(2, s.size());
8647 String name, value;
8648 if (!parseProperty(s, name, value))
8649 {
8650 usage(argc, argv);
8651 return false;
8652 }
8653 if (!make.specifyProperty(name, value))
8654 return false;
8655 }
8656 else
8657 {
8658 error("Unknown option:%s", arg.c_str());
8659 return false;
8660 }
8661 }
8662 else
8663 {
8664 if (target.size()>0)
8665 {
8666 error("only one initial target");
8667 usage(argc, argv);
8668 return false;
8669 }
8670 target = arg;
8671 }
8672 }
8674 //We have the options. Now execute them
8675 if (!make.run(target))
8676 return false;
8678 return true;
8679 }
8684 /*
8685 static bool runMake()
8686 {
8687 buildtool::Make make;
8688 if (!make.run())
8689 return false;
8690 return true;
8691 }
8694 static bool pkgConfigTest()
8695 {
8696 buildtool::PkgConfig pkgConfig;
8697 if (!pkgConfig.readFile("gtk+-2.0.pc"))
8698 return false;
8699 return true;
8700 }
8704 static bool depTest()
8705 {
8706 buildtool::DepTool deptool;
8707 deptool.setSourceDirectory("/dev/ink/inkscape/src");
8708 if (!deptool.generateDependencies("build.dep"))
8709 return false;
8710 std::vector<buildtool::FileRec> res =
8711 deptool.loadDepFile("build.dep");
8712 if (res.size() == 0)
8713 return false;
8714 return true;
8715 }
8717 static bool popenTest()
8718 {
8719 buildtool::Make make;
8720 buildtool::String out, err;
8721 bool ret = make.executeCommand("gcc xx.cpp", "", out, err);
8722 printf("Popen test:%d '%s' '%s'\n", ret, out.c_str(), err.c_str());
8723 return true;
8724 }
8727 static bool propFileTest()
8728 {
8729 buildtool::Make make;
8730 make.parsePropertyFile("test.prop", "test.");
8731 return true;
8732 }
8733 */
8735 int main(int argc, char **argv)
8736 {
8738 if (!parseOptions(argc, argv))
8739 return 1;
8740 /*
8741 if (!popenTest())
8742 return 1;
8744 if (!depTest())
8745 return 1;
8746 if (!propFileTest())
8747 return 1;
8748 if (runMake())
8749 return 1;
8750 */
8751 return 0;
8752 }
8755 //########################################################################
8756 //# E N D
8757 //########################################################################