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