Code

add <makefile>
[inkscape.git] / buildtool.cpp
1 /**
2  * Simple build automation tool.
3  *
4  * Authors:
5  *   Bob Jamison
6  *
7  * Copyright (C) 2006 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 build.exe
28  * (or whatever your compiler might be) 
29  * Then
30  * build
31  * or 
32  * build {target}
33  */  
37 #include <stdio.h>
38 #include <unistd.h>
39 #include <stdarg.h>
40 #include <sys/stat.h>
41 #include <time.h>
42 #include <sys/time.h>
43 #include <dirent.h>
45 #include <string>
46 #include <map>
47 #include <set>
48 #include <vector>
50 #ifdef __WIN32__
51 #include <windows.h>
52 #endif
56 namespace buildtool
57 {
64 //########################################################################
65 //########################################################################
66 //##  X M L
67 //########################################################################
68 //########################################################################
70 // Note:  This mini-dom library comes from Pedro, another little project
71 // of mine.
73 typedef std::string String;
74 typedef unsigned int XMLCh;
77 class Namespace
78 {
79 public:
80     Namespace()
81         {}
83     Namespace(const String &prefixArg, const String &namespaceURIArg)
84         {
85         prefix       = prefixArg;
86         namespaceURI = namespaceURIArg;
87         }
89     Namespace(const Namespace &other)
90         {
91         assign(other);
92         }
94     Namespace &operator=(const Namespace &other)
95         {
96         assign(other);
97         return *this;
98         }
100     virtual ~Namespace()
101         {}
103     virtual String getPrefix()
104         { return prefix; }
106     virtual String getNamespaceURI()
107         { return namespaceURI; }
109 protected:
111     void assign(const Namespace &other)
112         {
113         prefix       = other.prefix;
114         namespaceURI = other.namespaceURI;
115         }
117     String prefix;
118     String namespaceURI;
120 };
122 class Attribute
124 public:
125     Attribute()
126         {}
128     Attribute(const String &nameArg, const String &valueArg)
129         {
130         name  = nameArg;
131         value = valueArg;
132         }
134     Attribute(const Attribute &other)
135         {
136         assign(other);
137         }
139     Attribute &operator=(const Attribute &other)
140         {
141         assign(other);
142         return *this;
143         }
145     virtual ~Attribute()
146         {}
148     virtual String getName()
149         { return name; }
151     virtual String getValue()
152         { return value; }
154 protected:
156     void assign(const Attribute &other)
157         {
158         name  = other.name;
159         value = other.value;
160         }
162     String name;
163     String value;
165 };
168 class Element
170 friend class Parser;
172 public:
173     Element()
174         {
175         parent = NULL;
176         }
178     Element(const String &nameArg)
179         {
180         parent = NULL;
181         name   = nameArg;
182         }
184     Element(const String &nameArg, const String &valueArg)
185         {
186         parent = NULL;
187         name   = nameArg;
188         value  = valueArg;
189         }
191     Element(const Element &other)
192         {
193         assign(other);
194         }
196     Element &operator=(const Element &other)
197         {
198         assign(other);
199         return *this;
200         }
202     virtual Element *clone();
204     virtual ~Element()
205         {
206         for (unsigned int i=0 ; i<children.size() ; i++)
207             delete children[i];
208         }
210     virtual String getName()
211         { return name; }
213     virtual String getValue()
214         { return value; }
216     Element *getParent()
217         { return parent; }
219     std::vector<Element *> getChildren()
220         { return children; }
222     std::vector<Element *> findElements(const String &name);
224     String getAttribute(const String &name);
226     std::vector<Attribute> &getAttributes()
227         { return attributes; } 
229     String getTagAttribute(const String &tagName, const String &attrName);
231     String getTagValue(const String &tagName);
233     void addChild(Element *child);
235     void addAttribute(const String &name, const String &value);
237     void addNamespace(const String &prefix, const String &namespaceURI);
240     /**
241      * Prettyprint an XML tree to an output stream.  Elements are indented
242      * according to element hierarchy.
243      * @param f a stream to receive the output
244      * @param elem the element to output
245      */
246     void writeIndented(FILE *f);
248     /**
249      * Prettyprint an XML tree to standard output.  This is the equivalent of
250      * writeIndented(stdout).
251      * @param elem the element to output
252      */
253     void print();
255 protected:
257     void assign(const Element &other)
258         {
259         parent     = other.parent;
260         children   = other.children;
261         attributes = other.attributes;
262         namespaces = other.namespaces;
263         name       = other.name;
264         value      = other.value;
265         }
267     void findElementsRecursive(std::vector<Element *>&res, const String &name);
269     void writeIndentedRecursive(FILE *f, int indent);
271     Element *parent;
273     std::vector<Element *>children;
275     std::vector<Attribute> attributes;
276     std::vector<Namespace> namespaces;
278     String name;
279     String value;
281 };
287 class Parser
289 public:
290     /**
291      * Constructor
292      */
293     Parser()
294         { init(); }
296     virtual ~Parser()
297         {}
299     /**
300      * Parse XML in a char buffer.
301      * @param buf a character buffer to parse
302      * @param pos position to start parsing
303      * @param len number of chars, from pos, to parse.
304      * @return a pointer to the root of the XML document;
305      */
306     Element *parse(const char *buf,int pos,int len);
308     /**
309      * Parse XML in a char buffer.
310      * @param buf a character buffer to parse
311      * @param pos position to start parsing
312      * @param len number of chars, from pos, to parse.
313      * @return a pointer to the root of the XML document;
314      */
315     Element *parse(const String &buf);
317     /**
318      * Parse a named XML file.  The file is loaded like a data file;
319      * the original format is not preserved.
320      * @param fileName the name of the file to read
321      * @return a pointer to the root of the XML document;
322      */
323     Element *parseFile(const String &fileName);
325     /**
326      * Utility method to preprocess a string for XML
327      * output, escaping its entities.
328      * @param str the string to encode
329      */
330     static String encode(const String &str);
332     /**
333      *  Removes whitespace from beginning and end of a string
334      */
335     String trim(const String &s);
337 private:
339     void init()
340         {
341         keepGoing       = true;
342         currentNode     = NULL;
343         parselen        = 0;
344         parsebuf        = NULL;
345         currentPosition = 0;
346         }
348     void getLineAndColumn(long pos, long *lineNr, long *colNr);
350     void error(char *fmt, ...);
352     int peek(long pos);
354     int match(long pos, const char *text);
356     int skipwhite(long p);
358     int getWord(int p0, String &buf);
360     int getQuoted(int p0, String &buf, int do_i_parse);
362     int parseVersion(int p0);
364     int parseDoctype(int p0);
366     int parseElement(int p0, Element *par,int depth);
368     Element *parse(XMLCh *buf,int pos,int len);
370     bool       keepGoing;
371     Element    *currentNode;
372     long       parselen;
373     XMLCh      *parsebuf;
374     String  cdatabuf;
375     long       currentPosition;
376     int        colNr;
378 };
383 //########################################################################
384 //# E L E M E N T
385 //########################################################################
387 Element *Element::clone()
389     Element *elem = new Element(name, value);
390     elem->parent     = parent;
391     elem->attributes = attributes;
392     elem->namespaces = namespaces;
394     std::vector<Element *>::iterator iter;
395     for (iter = children.begin(); iter != children.end() ; iter++)
396         {
397         elem->addChild((*iter)->clone());
398         }
399     return elem;
403 void Element::findElementsRecursive(std::vector<Element *>&res, const String &name)
405     if (getName() == name)
406         {
407         res.push_back(this);
408         }
409     for (unsigned int i=0; i<children.size() ; i++)
410         children[i]->findElementsRecursive(res, name);
413 std::vector<Element *> Element::findElements(const String &name)
415     std::vector<Element *> res;
416     findElementsRecursive(res, name);
417     return res;
420 String Element::getAttribute(const String &name)
422     for (unsigned int i=0 ; i<attributes.size() ; i++)
423         if (attributes[i].getName() ==name)
424             return attributes[i].getValue();
425     return "";
428 String Element::getTagAttribute(const String &tagName, const String &attrName)
430     std::vector<Element *>elems = findElements(tagName);
431     if (elems.size() <1)
432         return "";
433     String res = elems[0]->getAttribute(attrName);
434     return res;
437 String Element::getTagValue(const String &tagName)
439     std::vector<Element *>elems = findElements(tagName);
440     if (elems.size() <1)
441         return "";
442     String res = elems[0]->getValue();
443     return res;
446 void Element::addChild(Element *child)
448     if (!child)
449         return;
450     child->parent = this;
451     children.push_back(child);
455 void Element::addAttribute(const String &name, const String &value)
457     Attribute attr(name, value);
458     attributes.push_back(attr);
461 void Element::addNamespace(const String &prefix, const String &namespaceURI)
463     Namespace ns(prefix, namespaceURI);
464     namespaces.push_back(ns);
467 void Element::writeIndentedRecursive(FILE *f, int indent)
469     int i;
470     if (!f)
471         return;
472     //Opening tag, and attributes
473     for (i=0;i<indent;i++)
474         fputc(' ',f);
475     fprintf(f,"<%s",name.c_str());
476     for (unsigned int i=0 ; i<attributes.size() ; i++)
477         {
478         fprintf(f," %s=\"%s\"",
479               attributes[i].getName().c_str(),
480               attributes[i].getValue().c_str());
481         }
482     for (unsigned int i=0 ; i<namespaces.size() ; i++)
483         {
484         fprintf(f," xmlns:%s=\"%s\"",
485               namespaces[i].getPrefix().c_str(),
486               namespaces[i].getNamespaceURI().c_str());
487         }
488     fprintf(f,">\n");
490     //Between the tags
491     if (value.size() > 0)
492         {
493         for (int i=0;i<indent;i++)
494             fputc(' ', f);
495         fprintf(f," %s\n", value.c_str());
496         }
498     for (unsigned int i=0 ; i<children.size() ; i++)
499         children[i]->writeIndentedRecursive(f, indent+2);
501     //Closing tag
502     for (int i=0; i<indent; i++)
503         fputc(' ',f);
504     fprintf(f,"</%s>\n", name.c_str());
507 void Element::writeIndented(FILE *f)
509     writeIndentedRecursive(f, 0);
512 void Element::print()
514     writeIndented(stdout);
518 //########################################################################
519 //# P A R S E R
520 //########################################################################
524 typedef struct
525     {
526     char *escaped;
527     char value;
528     } EntityEntry;
530 static EntityEntry entities[] =
532     { "&amp;" , '&'  },
533     { "&lt;"  , '<'  },
534     { "&gt;"  , '>'  },
535     { "&apos;", '\'' },
536     { "&quot;", '"'  },
537     { NULL    , '\0' }
538 };
542 /**
543  *  Removes whitespace from beginning and end of a string
544  */
545 String Parser::trim(const String &s)
547     if (s.size() < 1)
548         return s;
549     
550     //Find first non-ws char
551     unsigned int begin = 0;
552     for ( ; begin < s.size() ; begin++)
553         {
554         if (!isspace(s[begin]))
555             break;
556         }
558     //Find first non-ws char, going in reverse
559     unsigned int end = s.size() - 1;
560     for ( ; end > begin ; end--)
561         {
562         if (!isspace(s[end]))
563             break;
564         }
565     //trace("begin:%d  end:%d", begin, end);
567     String res = s.substr(begin, end-begin+1);
568     return res;
571 void Parser::getLineAndColumn(long pos, long *lineNr, long *colNr)
573     long line = 1;
574     long col  = 1;
575     for (long i=0 ; i<pos ; i++)
576         {
577         XMLCh ch = parsebuf[i];
578         if (ch == '\n' || ch == '\r')
579             {
580             col = 0;
581             line ++;
582             }
583         else
584             col++;
585         }
586     *lineNr = line;
587     *colNr  = col;
592 void Parser::error(char *fmt, ...)
594     long lineNr;
595     long colNr;
596     getLineAndColumn(currentPosition, &lineNr, &colNr);
597     va_list args;
598     fprintf(stderr, "xml error at line %ld, column %ld:", lineNr, colNr);
599     va_start(args,fmt);
600     vfprintf(stderr,fmt,args);
601     va_end(args) ;
602     fprintf(stderr, "\n");
607 int Parser::peek(long pos)
609     if (pos >= parselen)
610         return -1;
611     currentPosition = pos;
612     int ch = parsebuf[pos];
613     //printf("ch:%c\n", ch);
614     return ch;
619 String Parser::encode(const String &str)
621     String ret;
622     for (unsigned int i=0 ; i<str.size() ; i++)
623         {
624         XMLCh ch = (XMLCh)str[i];
625         if (ch == '&')
626             ret.append("&amp;");
627         else if (ch == '<')
628             ret.append("&lt;");
629         else if (ch == '>')
630             ret.append("&gt;");
631         else if (ch == '\'')
632             ret.append("&apos;");
633         else if (ch == '"')
634             ret.append("&quot;");
635         else
636             ret.push_back(ch);
638         }
639     return ret;
643 int Parser::match(long p0, const char *text)
645     int p = p0;
646     while (*text)
647         {
648         if (peek(p) != *text)
649             return p0;
650         p++; text++;
651         }
652     return p;
657 int Parser::skipwhite(long p)
660     while (p<parselen)
661         {
662         int p2 = match(p, "<!--");
663         if (p2 > p)
664             {
665             p = p2;
666             while (p<parselen)
667               {
668               p2 = match(p, "-->");
669               if (p2 > p)
670                   {
671                   p = p2;
672                   break;
673                   }
674               p++;
675               }
676           }
677       XMLCh b = peek(p);
678       if (!isspace(b))
679           break;
680       p++;
681       }
682   return p;
685 /* modify this to allow all chars for an element or attribute name*/
686 int Parser::getWord(int p0, String &buf)
688     int p = p0;
689     while (p<parselen)
690         {
691         XMLCh b = peek(p);
692         if (b<=' ' || b=='/' || b=='>' || b=='=')
693             break;
694         buf.push_back(b);
695         p++;
696         }
697     return p;
700 int Parser::getQuoted(int p0, String &buf, int do_i_parse)
703     int p = p0;
704     if (peek(p) != '"' && peek(p) != '\'')
705         return p0;
706     p++;
708     while ( p<parselen )
709         {
710         XMLCh b = peek(p);
711         if (b=='"' || b=='\'')
712             break;
713         if (b=='&' && do_i_parse)
714             {
715             bool found = false;
716             for (EntityEntry *ee = entities ; ee->value ; ee++)
717                 {
718                 int p2 = match(p, ee->escaped);
719                 if (p2>p)
720                     {
721                     buf.push_back(ee->value);
722                     p = p2;
723                     found = true;
724                     break;
725                     }
726                 }
727             if (!found)
728                 {
729                 error("unterminated entity");
730                 return false;
731                 }
732             }
733         else
734             {
735             buf.push_back(b);
736             p++;
737             }
738         }
739     return p;
742 int Parser::parseVersion(int p0)
744     //printf("### parseVersion: %d\n", p0);
746     int p = p0;
748     p = skipwhite(p0);
750     if (peek(p) != '<')
751         return p0;
753     p++;
754     if (p>=parselen || peek(p)!='?')
755         return p0;
757     p++;
759     String buf;
761     while (p<parselen)
762         {
763         XMLCh ch = peek(p);
764         if (ch=='?')
765             {
766             p++;
767             break;
768             }
769         buf.push_back(ch);
770         p++;
771         }
773     if (peek(p) != '>')
774         return p0;
775     p++;
777     //printf("Got version:%s\n",buf.c_str());
778     return p;
781 int Parser::parseDoctype(int p0)
783     //printf("### parseDoctype: %d\n", p0);
785     int p = p0;
786     p = skipwhite(p);
788     if (p>=parselen || peek(p)!='<')
789         return p0;
791     p++;
793     if (peek(p)!='!' || peek(p+1)=='-')
794         return p0;
795     p++;
797     String buf;
798     while (p<parselen)
799         {
800         XMLCh ch = peek(p);
801         if (ch=='>')
802             {
803             p++;
804             break;
805             }
806         buf.push_back(ch);
807         p++;
808         }
810     //printf("Got doctype:%s\n",buf.c_str());
811     return p;
814 int Parser::parseElement(int p0, Element *par,int depth)
817     int p = p0;
819     int p2 = p;
821     p = skipwhite(p);
823     //## Get open tag
824     XMLCh ch = peek(p);
825     if (ch!='<')
826         return p0;
828     p++;
830     String openTagName;
831     p = skipwhite(p);
832     p = getWord(p, openTagName);
833     //printf("####tag :%s\n", openTagName.c_str());
834     p = skipwhite(p);
836     //Add element to tree
837     Element *n = new Element(openTagName);
838     n->parent = par;
839     par->addChild(n);
841     // Get attributes
842     if (peek(p) != '>')
843         {
844         while (p<parselen)
845             {
846             p = skipwhite(p);
847             ch = peek(p);
848             //printf("ch:%c\n",ch);
849             if (ch=='>')
850                 break;
851             else if (ch=='/' && p<parselen+1)
852                 {
853                 p++;
854                 p = skipwhite(p);
855                 ch = peek(p);
856                 if (ch=='>')
857                     {
858                     p++;
859                     //printf("quick close\n");
860                     return p;
861                     }
862                 }
863             String attrName;
864             p2 = getWord(p, attrName);
865             if (p2==p)
866                 break;
867             //printf("name:%s",buf);
868             p=p2;
869             p = skipwhite(p);
870             ch = peek(p);
871             //printf("ch:%c\n",ch);
872             if (ch!='=')
873                 break;
874             p++;
875             p = skipwhite(p);
876             // ch = parsebuf[p];
877             // printf("ch:%c\n",ch);
878             String attrVal;
879             p2 = getQuoted(p, attrVal, true);
880             p=p2+1;
881             //printf("name:'%s'   value:'%s'\n",attrName.c_str(),attrVal.c_str());
882             char *namestr = (char *)attrName.c_str();
883             if (strncmp(namestr, "xmlns:", 6)==0)
884                 n->addNamespace(attrName, attrVal);
885             else
886                 n->addAttribute(attrName, attrVal);
887             }
888         }
890     bool cdata = false;
892     p++;
893     // ### Get intervening data ### */
894     String data;
895     while (p<parselen)
896         {
897         //# COMMENT
898         p2 = match(p, "<!--");
899         if (!cdata && p2>p)
900             {
901             p = p2;
902             while (p<parselen)
903                 {
904                 p2 = match(p, "-->");
905                 if (p2 > p)
906                     {
907                     p = p2;
908                     break;
909                     }
910                 p++;
911                 }
912             }
914         ch = peek(p);
915         //# END TAG
916         if (ch=='<' && !cdata && peek(p+1)=='/')
917             {
918             break;
919             }
920         //# CDATA
921         p2 = match(p, "<![CDATA[");
922         if (p2 > p)
923             {
924             cdata = true;
925             p = p2;
926             continue;
927             }
929         //# CHILD ELEMENT
930         if (ch == '<')
931             {
932             p2 = parseElement(p, n, depth+1);
933             if (p2 == p)
934                 {
935                 /*
936                 printf("problem on element:%s.  p2:%d p:%d\n",
937                       openTagName.c_str(), p2, p);
938                 */
939                 return p0;
940                 }
941             p = p2;
942             continue;
943             }
944         //# ENTITY
945         if (ch=='&' && !cdata)
946             {
947             bool found = false;
948             for (EntityEntry *ee = entities ; ee->value ; ee++)
949                 {
950                 int p2 = match(p, ee->escaped);
951                 if (p2>p)
952                     {
953                     data.push_back(ee->value);
954                     p = p2;
955                     found = true;
956                     break;
957                     }
958                 }
959             if (!found)
960                 {
961                 error("unterminated entity");
962                 return -1;
963                 }
964             continue;
965             }
967         //# NONE OF THE ABOVE
968         data.push_back(ch);
969         p++;
970         }/*while*/
973     n->value = data;
974     //printf("%d : data:%s\n",p,data.c_str());
976     //## Get close tag
977     p = skipwhite(p);
978     ch = peek(p);
979     if (ch != '<')
980         {
981         error("no < for end tag\n");
982         return p0;
983         }
984     p++;
985     ch = peek(p);
986     if (ch != '/')
987         {
988         error("no / on end tag");
989         return p0;
990         }
991     p++;
992     ch = peek(p);
993     p = skipwhite(p);
994     String closeTagName;
995     p = getWord(p, closeTagName);
996     if (openTagName != closeTagName)
997         {
998         error("Mismatched closing tag.  Expected </%S>. Got '%S'.",
999                 openTagName.c_str(), closeTagName.c_str());
1000         return p0;
1001         }
1002     p = skipwhite(p);
1003     if (peek(p) != '>')
1004         {
1005         error("no > on end tag for '%s'", closeTagName.c_str());
1006         return p0;
1007         }
1008     p++;
1009     // printf("close element:%s\n",closeTagName.c_str());
1010     p = skipwhite(p);
1011     return p;
1017 Element *Parser::parse(XMLCh *buf,int pos,int len)
1019     parselen = len;
1020     parsebuf = buf;
1021     Element *rootNode = new Element("root");
1022     pos = parseVersion(pos);
1023     pos = parseDoctype(pos);
1024     pos = parseElement(pos, rootNode, 0);
1025     return rootNode;
1029 Element *Parser::parse(const char *buf, int pos, int len)
1031     XMLCh *charbuf = new XMLCh[len + 1];
1032     long i = 0;
1033     for ( ; i < len ; i++)
1034         charbuf[i] = (XMLCh)buf[i];
1035     charbuf[i] = '\0';
1037     Element *n = parse(charbuf, pos, len);
1038     delete[] charbuf;
1039     return n;
1042 Element *Parser::parse(const String &buf)
1044     long len = (long)buf.size();
1045     XMLCh *charbuf = new XMLCh[len + 1];
1046     long i = 0;
1047     for ( ; i < len ; i++)
1048         charbuf[i] = (XMLCh)buf[i];
1049     charbuf[i] = '\0';
1051     Element *n = parse(charbuf, 0, len);
1052     delete[] charbuf;
1053     return n;
1056 Element *Parser::parseFile(const String &fileName)
1059     //##### LOAD INTO A CHAR BUF, THEN CONVERT TO XMLCh
1060     FILE *f = fopen(fileName.c_str(), "rb");
1061     if (!f)
1062         return NULL;
1064     struct stat  statBuf;
1065     if (fstat(fileno(f),&statBuf)<0)
1066         {
1067         fclose(f);
1068         return NULL;
1069         }
1070     long filelen = statBuf.st_size;
1072     //printf("length:%d\n",filelen);
1073     XMLCh *charbuf = new XMLCh[filelen + 1];
1074     for (XMLCh *p=charbuf ; !feof(f) ; p++)
1075         {
1076         *p = (XMLCh)fgetc(f);
1077         }
1078     fclose(f);
1079     charbuf[filelen] = '\0';
1082     /*
1083     printf("nrbytes:%d\n",wc_count);
1084     printf("buf:%ls\n======\n",charbuf);
1085     */
1086     Element *n = parse(charbuf, 0, filelen);
1087     delete[] charbuf;
1088     return n;
1093 //########################################################################
1094 //########################################################################
1095 //##  E N D    X M L
1096 //########################################################################
1097 //########################################################################
1100 //########################################################################
1101 //########################################################################
1102 //##  U R I
1103 //########################################################################
1104 //########################################################################
1106 //This would normally be a call to a UNICODE function
1107 #define isLetter(x) isalpha(x)
1109 /**
1110  *  A class that implements the W3C URI resource reference.
1111  */
1112 class URI
1114 public:
1116     typedef enum
1117         {
1118         SCHEME_NONE =0,
1119         SCHEME_DATA,
1120         SCHEME_HTTP,
1121         SCHEME_HTTPS,
1122         SCHEME_FTP,
1123         SCHEME_FILE,
1124         SCHEME_LDAP,
1125         SCHEME_MAILTO,
1126         SCHEME_NEWS,
1127         SCHEME_TELNET
1128         } SchemeTypes;
1130     /**
1131      *
1132      */
1133     URI()
1134         {
1135         init();
1136         }
1138     /**
1139      *
1140      */
1141     URI(const String &str)
1142         {
1143         init();
1144         parse(str);
1145         }
1148     /**
1149      *
1150      */
1151     URI(const char *str)
1152         {
1153         init();
1154         String domStr = str;
1155         parse(domStr);
1156         }
1159     /**
1160      *
1161      */
1162     URI(const URI &other)
1163         {
1164         init();
1165         assign(other);
1166         }
1169     /**
1170      *
1171      */
1172     URI &operator=(const URI &other)
1173         {
1174         init();
1175         assign(other);
1176         return *this;
1177         }
1180     /**
1181      *
1182      */
1183     virtual ~URI()
1184         {}
1188     /**
1189      *
1190      */
1191     virtual bool parse(const String &str);
1193     /**
1194      *
1195      */
1196     virtual String toString() const;
1198     /**
1199      *
1200      */
1201     virtual int getScheme() const;
1203     /**
1204      *
1205      */
1206     virtual String getSchemeStr() const;
1208     /**
1209      *
1210      */
1211     virtual String getAuthority() const;
1213     /**
1214      *  Same as getAuthority, but if the port has been specified
1215      *  as host:port , the port will not be included
1216      */
1217     virtual String getHost() const;
1219     /**
1220      *
1221      */
1222     virtual int getPort() const;
1224     /**
1225      *
1226      */
1227     virtual String getPath() const;
1229     /**
1230      *
1231      */
1232     virtual String getNativePath() const;
1234     /**
1235      *
1236      */
1237     virtual bool isAbsolute() const;
1239     /**
1240      *
1241      */
1242     virtual bool isOpaque() const;
1244     /**
1245      *
1246      */
1247     virtual String getQuery() const;
1249     /**
1250      *
1251      */
1252     virtual String getFragment() const;
1254     /**
1255      *
1256      */
1257     virtual URI resolve(const URI &other) const;
1259     /**
1260      *
1261      */
1262     virtual void normalize();
1264 private:
1266     /**
1267      *
1268      */
1269     void init()
1270         {
1271         parsebuf  = NULL;
1272         parselen  = 0;
1273         scheme    = SCHEME_NONE;
1274         schemeStr = "";
1275         port      = 0;
1276         authority = "";
1277         path      = "";
1278         absolute  = false;
1279         opaque    = false;
1280         query     = "";
1281         fragment  = "";
1282         }
1285     /**
1286      *
1287      */
1288     void assign(const URI &other)
1289         {
1290         scheme    = other.scheme;
1291         schemeStr = other.schemeStr;
1292         authority = other.authority;
1293         port      = other.port;
1294         path      = other.path;
1295         absolute  = other.absolute;
1296         opaque    = other.opaque;
1297         query     = other.query;
1298         fragment  = other.fragment;
1299         }
1301     int scheme;
1303     String schemeStr;
1305     String authority;
1307     bool portSpecified;
1309     int port;
1311     String path;
1313     bool absolute;
1315     bool opaque;
1317     String query;
1319     String fragment;
1321     void error(const char *fmt, ...);
1323     void trace(const char *fmt, ...);
1326     int peek(int p);
1328     int match(int p, char *key);
1330     int parseScheme(int p);
1332     int parseHierarchicalPart(int p0);
1334     int parseQuery(int p0);
1336     int parseFragment(int p0);
1338     int parse(int p);
1340     char *parsebuf;
1342     int parselen;
1344 };
1348 typedef struct
1350     int  ival;
1351     char *sval;
1352     int  port;
1353 } LookupEntry;
1355 LookupEntry schemes[] =
1357     { URI::SCHEME_DATA,   "data:",    0 },
1358     { URI::SCHEME_HTTP,   "http:",   80 },
1359     { URI::SCHEME_HTTPS,  "https:", 443 },
1360     { URI::SCHEME_FTP,    "ftp",     12 },
1361     { URI::SCHEME_FILE,   "file:",    0 },
1362     { URI::SCHEME_LDAP,   "ldap:",  123 },
1363     { URI::SCHEME_MAILTO, "mailto:", 25 },
1364     { URI::SCHEME_NEWS,   "news:",  117 },
1365     { URI::SCHEME_TELNET, "telnet:", 23 },
1366     { 0,                  NULL,       0 }
1367 };
1370 String URI::toString() const
1372     String str = schemeStr;
1373     if (authority.size() > 0)
1374         {
1375         str.append("//");
1376         str.append(authority);
1377         }
1378     str.append(path);
1379     if (query.size() > 0)
1380         {
1381         str.append("?");
1382         str.append(query);
1383         }
1384     if (fragment.size() > 0)
1385         {
1386         str.append("#");
1387         str.append(fragment);
1388         }
1389     return str;
1393 int URI::getScheme() const
1395     return scheme;
1398 String URI::getSchemeStr() const
1400     return schemeStr;
1404 String URI::getAuthority() const
1406     String ret = authority;
1407     if (portSpecified && port>=0)
1408         {
1409         char buf[7];
1410         snprintf(buf, 6, ":%6d", port);
1411         ret.append(buf);
1412         }
1413     return ret;
1416 String URI::getHost() const
1418     return authority;
1421 int URI::getPort() const
1423     return port;
1427 String URI::getPath() const
1429     return path;
1432 String URI::getNativePath() const
1434     String npath;
1435 #ifdef __WIN32__
1436     unsigned int firstChar = 0;
1437     if (path.size() >= 3)
1438         {
1439         if (path[0] == '/' &&
1440             isLetter(path[1]) &&
1441             path[2] == ':')
1442             firstChar++;
1443          }
1444     for (unsigned int i=firstChar ; i<path.size() ; i++)
1445         {
1446         XMLCh ch = (XMLCh) path[i];
1447         if (ch == '/')
1448             npath.push_back((XMLCh)'\\');
1449         else
1450             npath.push_back(ch);
1451         }
1452 #else
1453     npath = path;
1454 #endif
1455     return npath;
1459 bool URI::isAbsolute() const
1461     return absolute;
1464 bool URI::isOpaque() const
1466     return opaque;
1470 String URI::getQuery() const
1472     return query;
1476 String URI::getFragment() const
1478     return fragment;
1482 URI URI::resolve(const URI &other) const
1484     //### According to w3c, this is handled in 3 cases
1486     //## 1
1487     if (opaque || other.isAbsolute())
1488         return other;
1490     //## 2
1491     if (other.fragment.size()  >  0 &&
1492         other.path.size()      == 0 &&
1493         other.scheme           == SCHEME_NONE &&
1494         other.authority.size() == 0 &&
1495         other.query.size()     == 0 )
1496         {
1497         URI fragUri = *this;
1498         fragUri.fragment = other.fragment;
1499         return fragUri;
1500         }
1502     //## 3 http://www.ietf.org/rfc/rfc2396.txt, section 5.2
1503     URI newUri;
1504     //# 3.1
1505     newUri.scheme    = scheme;
1506     newUri.schemeStr = schemeStr;
1507     newUri.query     = other.query;
1508     newUri.fragment  = other.fragment;
1509     if (other.authority.size() > 0)
1510         {
1511         //# 3.2
1512         if (absolute || other.absolute)
1513             newUri.absolute = true;
1514         newUri.authority = other.authority;
1515         newUri.port      = other.port;//part of authority
1516         newUri.path      = other.path;
1517         }
1518     else
1519         {
1520         //# 3.3
1521         if (other.absolute)
1522             {
1523             newUri.absolute = true;
1524             newUri.path     = other.path;
1525             }
1526         else
1527             {
1528             unsigned int pos = path.find_last_of('/');
1529             if (pos != path.npos)
1530                 {
1531                 String tpath = path.substr(0, pos+1);
1532                 tpath.append(other.path);
1533                 newUri.path = tpath;
1534                 }
1535             else
1536                 newUri.path = other.path;
1537             }
1538         }
1540     newUri.normalize();
1541     return newUri;
1545 /**
1546  *  This follows the Java URI algorithm:
1547  *   1. All "." segments are removed.
1548  *   2. If a ".." segment is preceded by a non-".." segment
1549  *          then both of these segments are removed. This step
1550  *          is repeated until it is no longer applicable.
1551  *   3. If the path is relative, and if its first segment
1552  *          contains a colon character (':'), then a "." segment
1553  *          is prepended. This prevents a relative URI with a path
1554  *          such as "a:b/c/d" from later being re-parsed as an
1555  *          opaque URI with a scheme of "a" and a scheme-specific
1556  *          part of "b/c/d". (Deviation from RFC 2396)
1557  */
1558 void URI::normalize()
1560     std::vector<String> segments;
1562     //## Collect segments
1563     if (path.size()<2)
1564         return;
1565     bool abs = false;
1566     unsigned int pos=0;
1567     if (path[0]=='/')
1568         {
1569         abs = true;
1570         pos++;
1571         }
1572     while (pos < path.size())
1573         {
1574         unsigned int pos2 = path.find('/', pos);
1575         if (pos2==path.npos)
1576             {
1577             String seg = path.substr(pos);
1578             //printf("last segment:%s\n", seg.c_str());
1579             segments.push_back(seg);
1580             break;
1581             }
1582         if (pos2>pos)
1583             {
1584             String seg = path.substr(pos, pos2-pos);
1585             //printf("segment:%s\n", seg.c_str());
1586             segments.push_back(seg);
1587             }
1588         pos = pos2;
1589         pos++;
1590         }
1592     //## Clean up (normalize) segments
1593     bool edited = false;
1594     std::vector<String>::iterator iter;
1595     for (iter=segments.begin() ; iter!=segments.end() ; )
1596         {
1597         String s = *iter;
1598         if (s == ".")
1599             {
1600             iter = segments.erase(iter);
1601             edited = true;
1602             }
1603         else if (s == ".." &&
1604                  iter != segments.begin() &&
1605                  *(iter-1) != "..")
1606             {
1607             iter--; //back up, then erase two entries
1608             iter = segments.erase(iter);
1609             iter = segments.erase(iter);
1610             edited = true;
1611             }
1612         else
1613             iter++;
1614         }
1616     //## Rebuild path, if necessary
1617     if (edited)
1618         {
1619         path.clear();
1620         if (abs)
1621             {
1622             path.append("/");
1623             }
1624         std::vector<String>::iterator iter;
1625         for (iter=segments.begin() ; iter!=segments.end() ; iter++)
1626             {
1627             if (iter != segments.begin())
1628                 path.append("/");
1629             path.append(*iter);
1630             }
1631         }
1637 //#########################################################################
1638 //# M E S S A G E S
1639 //#########################################################################
1641 void URI::error(const char *fmt, ...)
1643     va_list args;
1644     fprintf(stderr, "URI error: ");
1645     va_start(args, fmt);
1646     vfprintf(stderr, fmt, args);
1647     va_end(args);
1648     fprintf(stderr, "\n");
1651 void URI::trace(const char *fmt, ...)
1653     va_list args;
1654     fprintf(stdout, "URI: ");
1655     va_start(args, fmt);
1656     vfprintf(stdout, fmt, args);
1657     va_end(args);
1658     fprintf(stdout, "\n");
1663 //#########################################################################
1664 //# P A R S I N G
1665 //#########################################################################
1669 int URI::peek(int p)
1671     if (p<0 || p>=parselen)
1672         return -1;
1673     return parsebuf[p];
1678 int URI::match(int p0, char *key)
1680     int p = p0;
1681     while (p < parselen)
1682         {
1683         if (*key == '\0')
1684             return p;
1685         else if (*key != parsebuf[p])
1686             break;
1687         p++; key++;
1688         }
1689     return p0;
1692 //#########################################################################
1693 //#  Parsing is performed according to:
1694 //#  http://www.gbiv.com/protocols/uri/rfc/rfc3986.html#components
1695 //#########################################################################
1697 int URI::parseScheme(int p0)
1699     int p = p0;
1700     for (LookupEntry *entry = schemes; entry->sval ; entry++)
1701         {
1702         int p2 = match(p, entry->sval);
1703         if (p2 > p)
1704             {
1705             schemeStr = entry->sval;
1706             scheme    = entry->ival;
1707             port      = entry->port;
1708             p = p2;
1709             return p;
1710             }
1711         }
1713     return p;
1717 int URI::parseHierarchicalPart(int p0)
1719     int p = p0;
1720     int ch;
1722     //# Authority field (host and port, for example)
1723     int p2 = match(p, "//");
1724     if (p2 > p)
1725         {
1726         p = p2;
1727         portSpecified = false;
1728         String portStr;
1729         while (p < parselen)
1730             {
1731             ch = peek(p);
1732             if (ch == '/')
1733                 break;
1734             else if (ch == ':')
1735                 portSpecified = true;
1736             else if (portSpecified)
1737                 portStr.push_back((XMLCh)ch);
1738             else
1739                 authority.push_back((XMLCh)ch);
1740             p++;
1741             }
1742         if (portStr.size() > 0)
1743             {
1744             char *pstr = (char *)portStr.c_str();
1745             char *endStr;
1746             long val = strtol(pstr, &endStr, 10);
1747             if (endStr > pstr) //successful parse?
1748                 port = val;
1749             }
1750         }
1752     //# Are we absolute?
1753     ch = peek(p);
1754     if (isLetter(ch) && peek(p+1)==':')
1755         {
1756         absolute = true;
1757         path.push_back((XMLCh)'/');
1758         }
1759     else if (ch == '/')
1760         {
1761         absolute = true;
1762         if (p>p0) //in other words, if '/' is not the first char
1763             opaque = true;
1764         path.push_back((XMLCh)ch);
1765         p++;
1766         }
1768     while (p < parselen)
1769         {
1770         ch = peek(p);
1771         if (ch == '?' || ch == '#')
1772             break;
1773         path.push_back((XMLCh)ch);
1774         p++;
1775         }
1777     return p;
1780 int URI::parseQuery(int p0)
1782     int p = p0;
1783     int ch = peek(p);
1784     if (ch != '?')
1785         return p0;
1787     p++;
1788     while (p < parselen)
1789         {
1790         ch = peek(p);
1791         if (ch == '#')
1792             break;
1793         query.push_back((XMLCh)ch);
1794         p++;
1795         }
1798     return p;
1801 int URI::parseFragment(int p0)
1804     int p = p0;
1805     int ch = peek(p);
1806     if (ch != '#')
1807         return p0;
1809     p++;
1810     while (p < parselen)
1811         {
1812         ch = peek(p);
1813         if (ch == '?')
1814             break;
1815         fragment.push_back((XMLCh)ch);
1816         p++;
1817         }
1820     return p;
1824 int URI::parse(int p0)
1827     int p = p0;
1829     int p2 = parseScheme(p);
1830     if (p2 < 0)
1831         {
1832         error("Scheme");
1833         return -1;
1834         }
1835     p = p2;
1838     p2 = parseHierarchicalPart(p);
1839     if (p2 < 0)
1840         {
1841         error("Hierarchical part");
1842         return -1;
1843         }
1844     p = p2;
1846     p2 = parseQuery(p);
1847     if (p2 < 0)
1848         {
1849         error("Query");
1850         return -1;
1851         }
1852     p = p2;
1855     p2 = parseFragment(p);
1856     if (p2 < 0)
1857         {
1858         error("Fragment");
1859         return -1;
1860         }
1861     p = p2;
1863     return p;
1869 bool URI::parse(const String &str)
1872     parselen = str.size();
1874     String tmp;
1875     for (unsigned int i=0 ; i<str.size() ; i++)
1876         {
1877         XMLCh ch = (XMLCh) str[i];
1878         if (ch == '\\')
1879             tmp.push_back((XMLCh)'/');
1880         else
1881             tmp.push_back(ch);
1882         }
1883     parsebuf = (char *) tmp.c_str();
1886     int p = parse(0);
1887     normalize();
1889     if (p < 0)
1890         {
1891         error("Syntax error");
1892         return false;
1893         }
1895     //printf("uri:%s\n", toString().c_str());
1896     //printf("path:%s\n", path.c_str());
1898     return true;
1909 //########################################################################
1910 //########################################################################
1911 //##  M A K E
1912 //########################################################################
1913 //########################################################################
1917 //########################################################################
1918 //# M A K E    B A S E
1919 //########################################################################
1920 /**
1921  * Base class for all classes in this file
1922  */
1923 class MakeBase
1925 public:
1926     MakeBase()
1927         {}
1928     virtual ~MakeBase()
1929         {}
1931     /**
1932      *  Return the URI of the file associated with this object 
1933      */     
1934     URI getURI()
1935         { return uri; }
1937     /**
1938      * Set the uri to the given string
1939      */
1940     void setURI(const String &uristr)
1941         { uri.parse(uristr); }
1943     /**
1944      *  Resolve another path relative to this one
1945      */
1946     String resolve(const String &otherPath);
1948     /**
1949      *  Get an element attribute, performing substitutions if necessary
1950      */
1951     bool getAttribute(Element *elem, const String &name, String &result);
1953     /**
1954      * Get an element value, performing substitutions if necessary
1955      */
1956     bool getValue(Element *elem, String &result);
1958 protected:
1960     /**
1961      *  The path to the file associated with this object
1962      */     
1963     URI uri;
1966     /**
1967      *  Print a printf()-like formatted error message
1968      */
1969     void error(char *fmt, ...);
1971     /**
1972      *  Print a printf()-like formatted trace message
1973      */
1974     void status(char *fmt, ...);
1976     /**
1977      *  Print a printf()-like formatted trace message
1978      */
1979     void trace(char *fmt, ...);
1981     /**
1982      *
1983      */
1984     String getSuffix(const String &fname);
1986     /**
1987      * Break up a string into substrings delimited the characters
1988      * in delimiters.  Null-length substrings are ignored
1989      */  
1990     std::vector<String> tokenize(const String &val,
1991                               const String &delimiters);
1993     /**
1994      *  replace runs of whitespace with a space
1995      */
1996     String strip(const String &s);
1998     /**
1999      *  remove leading whitespace from each line
2000      */
2001     String leftJustify(const String &s);
2003     /**
2004      *  remove leading and trailing whitespace from string
2005      */
2006     String trim(const String &s);
2008     /**
2009      * Return the native format of the canonical
2010      * path which we store
2011      */
2012     String getNativePath(const String &path);
2014     /**
2015      * Execute a shell command.  Outbuf is a ref to a string
2016      * to catch the result.     
2017      */      
2018     bool executeCommand(const String &call,
2019                             const String &inbuf,
2020                                                 String &outbuf,
2021                                                 String &errbuf);
2022     /**
2023      * List all directories in a given base and starting directory
2024      * It is usually called like:
2025      *           bool ret = listDirectories("src", "", result);    
2026      */      
2027     bool listDirectories(const String &baseName,
2028                          const String &dirname,
2029                          std::vector<String> &res);
2031     /**
2032      * Find all files in the named directory whose short names (no path) match
2033      * the given regex pattern     
2034      */      
2035     bool listFiles(const String &baseName,
2036                    const String &dirname,
2037                    std::vector<String> &excludes,
2038                    std::vector<String> &res);
2040     /**
2041      * Parse a <patternset>
2042      */  
2043     bool getPatternSet(Element *elem,
2044                        MakeBase &propRef,
2045                                            std::vector<String> &includes,
2046                                            std::vector<String> &excludes);
2048     /**
2049      * Parse a <fileset> entry, and determine which files
2050      * should be included
2051      */  
2052     bool getFileSet(Element *elem,
2053                     MakeBase &propRef,
2054                     String &dir,
2055                                         std::vector<String> &result);
2057     /**
2058      * Return this object's property list
2059      */
2060     virtual std::map<String, String> &getProperties()
2061         { return properties; }
2063     /**
2064      * Return a named property if found, else a null string
2065      */
2066     virtual String getProperty(const String &name)
2067         {
2068         String val;
2069         std::map<String, String>::iterator iter;
2070         iter = properties.find(name);
2071         if (iter != properties.end())
2072             val = iter->second;
2073         return val;
2074         }
2077     std::map<String, String> properties;
2079     /**
2080      * Turn 'true' and 'false' into boolean values
2081      */             
2082     bool getBool(const String &str, bool &val);
2084     /**
2085      * Create a directory, making intermediate dirs
2086      * if necessary
2087      */                     
2088     bool createDirectory(const String &dirname);
2090     /**
2091      * Delete a directory and its children if desired
2092      */
2093     bool removeDirectory(const String &dirName);
2095     /**
2096      * Copy a file from one name to another. Perform only if needed
2097      */ 
2098     bool copyFile(const String &srcFile, const String &destFile);
2100     /**
2101      * Tests if the file exists and is a regular file
2102      */ 
2103     bool isRegularFile(const String &fileName);
2105     /**
2106      * Tests if the file exists and is a directory
2107      */ 
2108     bool isDirectory(const String &fileName);
2110     /**
2111      * Tests is the modification date of fileA is newer than fileB
2112      */ 
2113     bool isNewerThan(const String &fileA, const String &fileB);
2115 private:
2117     /**
2118      * replace variable refs like ${a} with their values
2119      */      
2120     bool getSubstitutions(const String &s, String &result);
2124 };
2129 /**
2130  *  Print a printf()-like formatted error message
2131  */
2132 void MakeBase::error(char *fmt, ...)
2134     va_list args;
2135     va_start(args,fmt);
2136     fprintf(stderr, "Make error: ");
2137     vfprintf(stderr, fmt, args);
2138     fprintf(stderr, "\n");
2139     va_end(args) ;
2144 /**
2145  *  Print a printf()-like formatted trace message
2146  */
2147 void MakeBase::status(char *fmt, ...)
2149     va_list args;
2150     va_start(args,fmt);
2151     //fprintf(stdout, " ");
2152     vfprintf(stdout, fmt, args);
2153     fprintf(stdout, "\n");
2154     va_end(args) ;
2159 /**
2160  *  Resolve another path relative to this one
2161  */
2162 String MakeBase::resolve(const String &otherPath)
2164     URI otherURI(otherPath);
2165     URI fullURI = uri.resolve(otherURI);
2166     String ret = fullURI.toString();
2167     return ret;
2171 /**
2172  *  Print a printf()-like formatted trace message
2173  */
2174 void MakeBase::trace(char *fmt, ...)
2176     va_list args;
2177     va_start(args,fmt);
2178     fprintf(stdout, "Make: ");
2179     vfprintf(stdout, fmt, args);
2180     fprintf(stdout, "\n");
2181     va_end(args) ;
2185 /**
2186  *  Return the suffix, if any, of a file name
2187  */
2188 String MakeBase::getSuffix(const String &fname)
2190     if (fname.size() < 2)
2191         return "";
2192     unsigned int pos = fname.find_last_of('.');
2193     if (pos == fname.npos)
2194         return "";
2195     pos++;
2196     String res = fname.substr(pos, fname.size()-pos);
2197     //trace("suffix:%s", res.c_str()); 
2198     return res;
2203 /**
2204  * Break up a string into substrings delimited the characters
2205  * in delimiters.  Null-length substrings are ignored
2206  */  
2207 std::vector<String> MakeBase::tokenize(const String &str,
2208                                 const String &delimiters)
2211     std::vector<String> res;
2212     char *del = (char *)delimiters.c_str();
2213     String dmp;
2214     for (unsigned int i=0 ; i<str.size() ; i++)
2215         {
2216         char ch = str[i];
2217         char *p = (char *)0;
2218         for (p=del ; *p ; p++)
2219             if (*p == ch)
2220                 break;
2221         if (*p)
2222             {
2223             if (dmp.size() > 0)
2224                 {
2225                 res.push_back(dmp);
2226                 dmp.clear();
2227                 }
2228             }
2229         else
2230             {
2231             dmp.push_back(ch);
2232             }
2233         }
2234     //Add tail
2235     if (dmp.size() > 0)
2236         {
2237         res.push_back(dmp);
2238         dmp.clear();
2239         }
2241     return res;
2246 /**
2247  *  replace runs of whitespace with a single space
2248  */
2249 String MakeBase::strip(const String &s)
2251     int len = s.size();
2252     String stripped;
2253     for (int i = 0 ; i<len ; i++)
2254         {
2255         char ch = s[i];
2256         if (isspace(ch))
2257             {
2258             stripped.push_back(' ');
2259             for ( ; i<len ; i++)
2260                 {
2261                 ch = s[i];
2262                 if (!isspace(ch))
2263                     {
2264                     stripped.push_back(ch);
2265                     break;
2266                     }
2267                 }
2268             }
2269         else
2270             {
2271             stripped.push_back(ch);
2272             }
2273         }
2274     return stripped;
2277 /**
2278  *  remove leading whitespace from each line
2279  */
2280 String MakeBase::leftJustify(const String &s)
2282     String out;
2283     int len = s.size();
2284     for (int i = 0 ; i<len ; )
2285         {
2286         char ch;
2287         //Skip to first visible character
2288         while (i<len)
2289             {
2290                         ch = s[i];
2291                         if (ch == '\n' || ch == '\r'
2292                           || !isspace(ch))
2293                               break;
2294                         i++;
2295                         }
2296         //Copy the rest of the line
2297                 while (i<len)
2298                     {
2299                     ch = s[i];
2300             if (ch == '\n' || ch == '\r')
2301                 {
2302                 if (ch != '\r')
2303                     out.push_back('\n');
2304                 i++;
2305                 break;
2306                 }
2307             else
2308                 {
2309                 out.push_back(ch);
2310                 }
2311             i++;
2312             }
2313         }
2314     return out;
2318 /**
2319  *  Removes whitespace from beginning and end of a string
2320  */
2321 String MakeBase::trim(const String &s)
2323     if (s.size() < 1)
2324         return s;
2325     
2326     //Find first non-ws char
2327     unsigned int begin = 0;
2328     for ( ; begin < s.size() ; begin++)
2329         {
2330         if (!isspace(s[begin]))
2331             break;
2332         }
2334     //Find first non-ws char, going in reverse
2335     unsigned int end = s.size() - 1;
2336     for ( ; end > begin ; end--)
2337         {
2338         if (!isspace(s[end]))
2339             break;
2340         }
2341     //trace("begin:%d  end:%d", begin, end);
2343     String res = s.substr(begin, end-begin+1);
2344     return res;
2347 /**
2348  * Return the native format of the canonical
2349  * path which we store
2350  */
2351 String MakeBase::getNativePath(const String &path)
2353 #ifdef __WIN32__
2354     String npath;
2355     unsigned int firstChar = 0;
2356     if (path.size() >= 3)
2357         {
2358         if (path[0] == '/' &&
2359             isalpha(path[1]) &&
2360             path[2] == ':')
2361             firstChar++;
2362         }
2363     for (unsigned int i=firstChar ; i<path.size() ; i++)
2364         {
2365         char ch = path[i];
2366         if (ch == '/')
2367             npath.push_back('\\');
2368         else
2369             npath.push_back(ch);
2370         }
2371     return npath;
2372 #else
2373     return path;
2374 #endif
2378 #ifdef __WIN32__
2379 #include <tchar.h>
2381 static String win32LastError()
2384     DWORD dw = GetLastError(); 
2386     LPVOID str;
2387     FormatMessage(
2388         FORMAT_MESSAGE_ALLOCATE_BUFFER | 
2389         FORMAT_MESSAGE_FROM_SYSTEM,
2390         NULL,
2391         dw,
2392         0,
2393         (LPTSTR) &str,
2394         0, NULL );
2395     LPTSTR p = _tcschr((const char *)str, _T('\r'));
2396     if(p != NULL)
2397         { // lose CRLF
2398         *p = _T('\0');
2399         }
2400     String ret = (char *)str;
2401     LocalFree(str);
2403     return ret;
2405 #endif
2409 /**
2410  * Execute a system call, using pipes to send data to the
2411  * program's stdin,  and reading stdout and stderr.
2412  */
2413 bool MakeBase::executeCommand(const String &command,
2414                               const String &inbuf,
2415                                                           String &outbuf,
2416                                                           String &errbuf)
2419     status("============ cmd ============\n%s\n=============================",
2420                     command.c_str());
2422 #ifdef __WIN32__
2424     /*
2425     I really hate having win32 code in this program, but the
2426     read buffer in command.com and cmd.exe are just too small
2427     for the large commands we need for compiling and linking.
2428     */
2430     bool ret = true;
2432     //# Allocate a separate buffer for safety
2433     char *paramBuf = new char[command.size() + 1];
2434     if (!paramBuf)
2435        {
2436        error("executeCommand cannot allocate command buffer");
2437            return false;
2438        }
2439     strcpy(paramBuf, (char *)command.c_str());
2441     //# Create pipes
2442     SECURITY_ATTRIBUTES saAttr; 
2443     saAttr.nLength = sizeof(SECURITY_ATTRIBUTES); 
2444     saAttr.bInheritHandle = TRUE; 
2445     saAttr.lpSecurityDescriptor = NULL; 
2446     HANDLE stdinRead,  stdinWrite;
2447     HANDLE stdoutRead, stdoutWrite;
2448     HANDLE stderrRead, stderrWrite;
2449     if (!CreatePipe(&stdinRead, &stdinWrite, &saAttr, 0))
2450             {
2451                 error("executeProgram: could not create pipe");
2452         delete[] paramBuf;
2453                 return false;
2454                 } 
2455     SetHandleInformation(stdinWrite, HANDLE_FLAG_INHERIT, 0);
2456         if (!CreatePipe(&stdoutRead, &stdoutWrite, &saAttr, 0))
2457             {
2458                 error("executeProgram: could not create pipe");
2459         delete[] paramBuf;
2460                 return false;
2461                 } 
2462     SetHandleInformation(stdoutRead, HANDLE_FLAG_INHERIT, 0);
2463         if (!CreatePipe(&stderrRead, &stderrWrite, &saAttr, 0))
2464             {
2465                 error("executeProgram: could not create pipe");
2466         delete[] paramBuf;
2467                 return false;
2468                 } 
2469     SetHandleInformation(stderrRead, HANDLE_FLAG_INHERIT, 0);
2471     // Create the process
2472     STARTUPINFO siStartupInfo;
2473     PROCESS_INFORMATION piProcessInfo;
2474     memset(&siStartupInfo, 0, sizeof(siStartupInfo));
2475     memset(&piProcessInfo, 0, sizeof(piProcessInfo));
2476     siStartupInfo.cb = sizeof(siStartupInfo);
2477     siStartupInfo.hStdError   =  stderrWrite;
2478     siStartupInfo.hStdOutput  =  stdoutWrite;
2479     siStartupInfo.hStdInput   =  stdinRead;
2480     siStartupInfo.dwFlags    |=  STARTF_USESTDHANDLES;
2481    
2482     if (!CreateProcess(NULL, paramBuf, NULL, NULL, true,
2483                 0, NULL, NULL, &siStartupInfo,
2484                 &piProcessInfo))
2485         {
2486         error("executeCommand : could not create process : %s",
2487                             win32LastError().c_str());
2488         ret = false;
2489         }
2491     DWORD bytesWritten;
2492     if (inbuf.size()>0 &&
2493         !WriteFile(stdinWrite, inbuf.c_str(), inbuf.size(), 
2494                &bytesWritten, NULL))
2495         {
2496         error("executeCommand: could not write to pipe");
2497                 return false;
2498                 }       
2499     if (!CloseHandle(stdinWrite))
2500             {           
2501         error("executeCommand: could not close write pipe");
2502                 return false;
2503                 }
2504     if (!CloseHandle(stdoutWrite))
2505             {
2506         error("executeCommand: could not close read pipe");
2507                 return false;
2508                 }
2509     if (!CloseHandle(stderrWrite))
2510             {
2511         error("executeCommand: could not close read pipe");
2512                 return false;
2513                 }
2514         while (true)
2515         {
2516         //trace("## stderr");
2517         DWORD avail;
2518         if (!PeekNamedPipe(stderrRead, NULL, 0, NULL, &avail, NULL))
2519             break;
2520         if (avail > 0)
2521             {
2522             DWORD bytesRead = 0;
2523             char readBuf[1025];
2524             if (avail>1024) avail = 1024;
2525             if (!ReadFile(stderrRead, readBuf, avail, &bytesRead, NULL)
2526                 || bytesRead == 0)
2527                 {
2528                 break;
2529                 }
2530             for (unsigned int i=0 ; i<bytesRead ; i++)
2531                 errbuf.push_back(readBuf[i]);
2532             }
2533         //trace("## stdout");
2534         if (!PeekNamedPipe(stdoutRead, NULL, 0, NULL, &avail, NULL))
2535             break;
2536         if (avail > 0)
2537             {
2538             DWORD bytesRead = 0;
2539             char readBuf[1025];
2540             if (avail>1024) avail = 1024;
2541             if (!ReadFile(stdoutRead, readBuf, avail, &bytesRead, NULL)
2542                 || bytesRead==0)
2543                 {
2544                 break;
2545                 }
2546             for (unsigned int i=0 ; i<bytesRead ; i++)
2547                 outbuf.push_back(readBuf[i]);
2548             }
2549                 DWORD exitCode;
2550         GetExitCodeProcess(piProcessInfo.hProcess, &exitCode);
2551         if (exitCode != STILL_ACTIVE)
2552             break;
2553         Sleep(100);
2554         }       
2555     //trace("outbuf:%s", outbuf.c_str());
2556     if (!CloseHandle(stdoutRead))
2557         {
2558         error("executeCommand: could not close read pipe");
2559         return false;
2560         }
2561     if (!CloseHandle(stderrRead))
2562         {
2563         error("executeCommand: could not close read pipe");
2564         return false;
2565         }
2567     DWORD exitCode;
2568     GetExitCodeProcess(piProcessInfo.hProcess, &exitCode);
2569     //trace("exit code:%d", exitCode);
2570     if (exitCode != 0)
2571         {
2572         ret = false;
2573         }
2574     
2575     // Clean up
2576     CloseHandle(piProcessInfo.hProcess);
2577     CloseHandle(piProcessInfo.hThread);
2580     return ret;
2582 #else //do it unix-style
2584     String s;
2585     FILE *f = popen(command.c_str(), "r");
2586     int errnum = 0;
2587     if (f)
2588         {
2589         while (true)
2590             {
2591             int ch = fgetc(f);
2592             if (ch < 0)
2593                 break;
2594             s.push_back((char)ch);
2595             }
2596         errnum = pclose(f);
2597         }
2598         outbuf = s;
2599         if (errnum < 0)
2600             {
2601             error("exec of command '%s' failed : %s",
2602                      command.c_str(), strerror(errno));
2603             return false;
2604             }
2605         else
2606             return true;
2608 #endif
2609
2614 bool MakeBase::listDirectories(const String &baseName,
2615                               const String &dirName,
2616                               std::vector<String> &res)
2618     res.push_back(dirName);
2619     String fullPath = baseName;
2620     if (dirName.size()>0)
2621         {
2622         fullPath.append("/");
2623         fullPath.append(dirName);
2624         }
2625     DIR *dir = opendir(fullPath.c_str());
2626     while (true)
2627         {
2628         struct dirent *de = readdir(dir);
2629         if (!de)
2630             break;
2632         //Get the directory member name
2633         String s = de->d_name;
2634         if (s.size() == 0 || s[0] == '.')
2635             continue;
2636         String childName = dirName;
2637         childName.append("/");
2638         childName.append(s);
2640         String fullChildPath = baseName;
2641         fullChildPath.append("/");
2642         fullChildPath.append(childName);
2643         struct stat finfo;
2644         String childNative = getNativePath(fullChildPath);
2645         if (stat(childNative.c_str(), &finfo)<0)
2646             {
2647             error("cannot stat file:%s", childNative.c_str());
2648             }
2649         else if (S_ISDIR(finfo.st_mode))
2650             {
2651             //trace("directory: %s", childName.c_str());
2652             if (!listDirectories(baseName, childName, res))
2653                 return false;
2654             }
2655         }
2656     closedir(dir);
2658     return true;
2662 bool MakeBase::listFiles(const String &baseDir,
2663                          const String &dirName,
2664                          std::vector<String> &excludes,
2665                          std::vector<String> &res)
2667     String fullDir = baseDir;
2668     if (dirName.size()>0)
2669         {
2670         fullDir.append("/");
2671         fullDir.append(dirName);
2672         }
2673     String dirNative = getNativePath(fullDir);
2675     std::vector<String> subdirs;
2676     DIR *dir = opendir(dirNative.c_str());
2677     while (true)
2678         {
2679         struct dirent *de = readdir(dir);
2680         if (!de)
2681             break;
2683         //Get the directory member name
2684         String s = de->d_name;
2685         if (s.size() == 0 || s[0] == '.')
2686             continue;
2687         String childName;
2688         if (dirName.size()>0)
2689             {
2690             childName.append(dirName);
2691             childName.append("/");
2692             }
2693         childName.append(s);
2694         String fullChild = baseDir;
2695         fullChild.append("/");
2696         fullChild.append(childName);
2697         
2698         if (std::find(excludes.begin(), excludes.end(), childName)
2699                         != excludes.end())
2700             {
2701             //trace("EXCLUDED:%s", childName.c_str());
2702             continue;
2703             }
2705         struct stat finfo;
2706         String nativeName = getNativePath(fullChild);
2707         if (stat(nativeName.c_str(), &finfo)<0)
2708             {
2709             error("cannot stat file:%s", childName.c_str());
2710             return false;
2711             }
2712         else if (S_ISDIR(finfo.st_mode))
2713             {
2714             //trace("directory: %s", childName.c_str());
2715             if (!listFiles(baseDir, childName, excludes, res))
2716                 return false;
2717             }
2718         else if (!S_ISREG(finfo.st_mode))
2719             {
2720             trace("not regular: %s", childName.c_str());
2721             }
2722         else
2723             {
2724             res.push_back(childName);
2725             }
2726         }
2727     closedir(dir);
2729     return true;
2739 bool MakeBase::getSubstitutions(const String &str, String &result)
2741     String s = trim(str);
2742     int len = (int)s.size();
2743     String val;
2744     for (int i=0 ; i<len ; i++)
2745         {
2746         char ch = s[i];
2747         if (ch == '$' && s[i+1] == '{')
2748                     {
2749             String varname;
2750                     int j = i+2;
2751                     for ( ; j<len ; j++)
2752                         {
2753                         ch = s[j];
2754                         if (ch == '$' && s[j+1] == '{')
2755                             {
2756                             error("attribute %s cannot have nested variable references",
2757                                    s.c_str());
2758                             return false;
2759                             }
2760                         else if (ch == '}')
2761                             {
2762                             std::map<String, String>::iterator iter;
2763                             iter = properties.find(trim(varname));
2764                             if (iter != properties.end())
2765                                 {
2766                                 val.append(iter->second);
2767                                 }
2768                             else
2769                                 {
2770                                 error("property ${%s} not found", varname.c_str());
2771                                 return false;
2772                                 }
2773                             break;
2774                             }
2775                         else
2776                             {
2777                             varname.push_back(ch);
2778                             }
2779                         }
2780                     i = j;
2781                         }
2782                 else
2783                     {
2784                     val.push_back(ch);
2785                     }
2786         }
2787     result = val;
2788     return true;
2792 bool MakeBase::getAttribute(Element *elem, const String &name,
2793                                     String &result)
2795     String s = elem->getAttribute(name);
2796     return getSubstitutions(s, result);
2800 bool MakeBase::getValue(Element *elem, String &result)
2802     String s = elem->getValue();
2803     //Replace all runs of whitespace with a single space
2804     return getSubstitutions(s, result);
2808 /**
2809  * Turn 'true' and 'false' into boolean values
2810  */                 
2811 bool MakeBase::getBool(const String &str, bool &val)
2813     if (str == "true")
2814         val = true;
2815     else if (str == "false")
2816         val = false;
2817     else
2818         {
2819         error("expected 'true' or 'false'.  found '%s'", str.c_str());
2820         return false;
2821         }
2822     return true;
2828 /**
2829  * Parse a <patternset> entry
2830  */  
2831 bool MakeBase::getPatternSet(Element *elem,
2832                           MakeBase &propRef,
2833                                                   std::vector<String> &includes,
2834                                                   std::vector<String> &excludes
2835                                                   )
2837     std::vector<Element *> children  = elem->getChildren();
2838     for (unsigned int i=0 ; i<children.size() ; i++)
2839         {
2840         Element *child = children[i];
2841         String tagName = child->getName();
2842         if (tagName == "exclude")
2843             {
2844             String fname;
2845                         if (!propRef.getAttribute(child, "name", fname))
2846                             return false;
2847             //trace("EXCLUDE: %s", fname.c_str());
2848             excludes.push_back(fname);
2849             }
2850         else if (tagName == "include")
2851             {
2852             String fname;
2853                         if (!propRef.getAttribute(child, "name", fname))
2854                             return false;
2855             //trace("INCLUDE: %s", fname.c_str());
2856             includes.push_back(fname);
2857             }
2858         }
2860     return true;
2866 /**
2867  * Parse a <fileset> entry, and determine which files
2868  * should be included
2869  */  
2870 bool MakeBase::getFileSet(Element *elem,
2871                           MakeBase &propRef,
2872                                                   String &dir,
2873                                                   std::vector<String> &result)
2875     String name = elem->getName();
2876     if (name != "fileset")
2877         {
2878         error("expected <fileset>");
2879         return false;
2880         }
2883     std::vector<String> includes;
2884     std::vector<String> excludes;
2886     //A fileset has one implied patternset
2887     if (!getPatternSet(elem, propRef, includes, excludes))
2888         {
2889         return false;
2890         }
2891     //Look for child tags, including more patternsets
2892     std::vector<Element *> children  = elem->getChildren();
2893     for (unsigned int i=0 ; i<children.size() ; i++)
2894         {
2895         Element *child = children[i];
2896         String tagName = child->getName();
2897         if (tagName == "patternset")
2898             {
2899             if (!getPatternSet(child, propRef, includes, excludes))
2900                 {
2901                 return false;
2902                 }
2903             }
2904         }
2906     //Now do the stuff
2907     //Get the base directory for reading file names
2908     if (!propRef.getAttribute(elem, "dir", dir))
2909         return false;
2911     std::vector<String> fileList;
2912     if (dir.size() > 0)
2913         {
2914         String baseDir = propRef.resolve(dir);
2915             if (!listFiles(baseDir, "", excludes, fileList))
2916                 return false;
2917             }
2918         
2919         std::vector<String>::iterator iter;
2920     for (iter=includes.begin() ; iter!=includes.end() ; iter++)
2921         {
2922         String fname = *iter;
2923         fileList.push_back(fname);
2924         }
2925         
2926         result = fileList;
2927         
2928         /*
2929         for (unsigned int i=0 ; i<result.size() ; i++)
2930             {
2931             trace("RES:%s", result[i].c_str());
2932             }
2933     */
2935     std::sort(fileList.begin(), fileList.end());
2936     
2937     return true;
2942 /**
2943  * Create a directory, making intermediate dirs
2944  * if necessary
2945  */                         
2946 bool MakeBase::createDirectory(const String &dirname)
2948     //trace("## createDirectory: %s", dirname.c_str());
2949     //## first check if it exists
2950     struct stat finfo;
2951     String nativeDir = getNativePath(dirname);
2952     char *cnative = (char *) nativeDir.c_str();
2953     if (stat(dirname.c_str(), &finfo)==0)
2954         {
2955         if (!S_ISDIR(finfo.st_mode))
2956             {
2957             error("mkdir: file %s exists but is not a directory",
2958                               cnative);
2959             return false;
2960             }
2961         else //exists
2962             {
2963             return true;
2964             }
2965         }
2967     //## 2: pull off the last path segment, if any,
2968     //## to make the dir 'above' this one, if necessary
2969     unsigned int pos = dirname.find_last_of('/');
2970     if (pos != dirname.npos)
2971         {
2972         String subpath = dirname.substr(0, pos);
2973         if (!createDirectory(subpath))
2974             return false;
2975         }
2976         
2977     //## 3: now make
2978     if (mkdir(cnative)<0)
2979         {
2980         error("cannot make directory %s", cnative);
2981         return false;
2982         }
2983         
2984     return true;
2988 /**
2989  * Remove a directory recursively
2990  */ 
2991 bool MakeBase::removeDirectory(const String &dirName)
2993     char *dname = (char *)dirName.c_str();
2995     DIR *dir = opendir(dname);
2996     if (!dir)
2997         {
2998         //# Let this fail nicely.
2999         return true;
3000         //error("error opening directory %s : %s", dname, strerror(errno));
3001         //return false;
3002         }
3003     
3004     while (true)
3005         {
3006         struct dirent *de = readdir(dir);
3007         if (!de)
3008             break;
3010         //Get the directory member name
3011         String s = de->d_name;
3012         if (s.size() == 0 || s[0] == '.')
3013             continue;
3014         String childName;
3015         if (dirName.size() > 0)
3016             {
3017             childName.append(dirName);
3018             childName.append("/");
3019             }
3020         childName.append(s);
3023         struct stat finfo;
3024         String childNative = getNativePath(childName);
3025         char *cnative = (char *)childNative.c_str();
3026         if (stat(cnative, &finfo)<0)
3027             {
3028             error("cannot stat file:%s", cnative);
3029             }
3030         else if (S_ISDIR(finfo.st_mode))
3031             {
3032             //trace("DEL dir: %s", childName.c_str());
3033                         if (!removeDirectory(childName))
3034                     {
3035                             return false;
3036                             }
3037             }
3038         else if (!S_ISREG(finfo.st_mode))
3039             {
3040             trace("not regular: %s", cnative);
3041             }
3042         else
3043             {
3044             //trace("DEL file: %s", childName.c_str());
3045             if (remove(cnative)<0)
3046                 {
3047                 error("error deleting %s : %s",
3048                                      cnative, strerror(errno));
3049                                 return false;
3050                                 }
3051             }
3052         }
3053     closedir(dir);
3055     //Now delete the directory
3056     String native = getNativePath(dirName);
3057     if (rmdir(native.c_str())<0)
3058         {
3059         error("could not delete directory %s : %s",
3060             native.c_str() , strerror(errno));
3061         return false;
3062         }
3064     return true;
3065     
3069 /**
3070  * Copy a file from one name to another. Perform only if needed
3071  */ 
3072 bool MakeBase::copyFile(const String &srcFile, const String &destFile)
3074     //# 1 Check up-to-date times
3075     String srcNative = getNativePath(srcFile);
3076     struct stat srcinfo;
3077     if (stat(srcNative.c_str(), &srcinfo)<0)
3078         {
3079         error("source file %s for copy does not exist",
3080                          srcNative.c_str());
3081         return false;
3082         }
3084     String destNative = getNativePath(destFile);
3085     struct stat destinfo;
3086     if (stat(destNative.c_str(), &destinfo)==0)
3087         {
3088         if (destinfo.st_mtime >= srcinfo.st_mtime)
3089             return true;
3090         }
3091         
3092     //# 2 prepare a destination directory if necessary
3093     unsigned int pos = destFile.find_last_of('/');
3094     if (pos != destFile.npos)
3095         {
3096         String subpath = destFile.substr(0, pos);
3097         if (!createDirectory(subpath))
3098             return false;
3099         }
3101     //# 3 do the data copy
3102 #ifndef __WIN32__
3104     FILE *srcf = fopen(srcNative.c_str(), "rb");
3105     if (!srcf)
3106         {
3107         error("copyFile cannot open '%s' for reading", srcNative.c_str());
3108         return false;
3109         }
3110     FILE *destf = fopen(destNative.c_str(), "wb");
3111     if (!destf)
3112         {
3113         error("copyFile cannot open %s for writing", srcNative.c_str());
3114         return false;
3115         }
3117     while (!feof(srcf))
3118         {
3119         int ch = fgetc(srcf);
3120         if (ch<0)
3121             break;
3122         fputc(ch, destf);
3123         }
3125     fclose(destf);
3126     fclose(srcf);
3128 #else
3129     
3130     if (!CopyFile(srcNative.c_str(), destNative.c_str(), false))
3131         {
3132         error("copyFile from %s to %s failed",
3133                      srcNative.c_str(), destNative.c_str());
3134         return false;
3135         }
3136         
3137 #endif /* __WIN32__ */
3140     return true;
3145 /**
3146  * Tests if the file exists and is a regular file
3147  */ 
3148 bool MakeBase::isRegularFile(const String &fileName)
3150     String native = getNativePath(fileName);
3151     struct stat finfo;
3152     
3153     //Exists?
3154     if (stat(native.c_str(), &finfo)<0)
3155                 return false;
3158     //check the file mode
3159     if (!S_ISREG(finfo.st_mode))
3160                 return false;
3162     return true;
3165 /**
3166  * Tests if the file exists and is a directory
3167  */ 
3168 bool MakeBase::isDirectory(const String &fileName)
3170     String native = getNativePath(fileName);
3171     struct stat finfo;
3172     
3173     //Exists?
3174     if (stat(native.c_str(), &finfo)<0)
3175                 return false;
3178     //check the file mode
3179     if (!S_ISDIR(finfo.st_mode))
3180                 return false;
3182     return true;
3187 /**
3188  * Tests is the modification of fileA is newer than fileB
3189  */ 
3190 bool MakeBase::isNewerThan(const String &fileA, const String &fileB)
3192     //trace("isNewerThan:'%s' , '%s'", fileA.c_str(), fileB.c_str());
3193     String nativeA = getNativePath(fileA);
3194     struct stat infoA;
3195     //IF source does not exist, NOT newer
3196     if (stat(nativeA.c_str(), &infoA)<0)
3197         {
3198                 return false;
3199                 }
3201     String nativeB = getNativePath(fileB);
3202     struct stat infoB;
3203     //IF dest does not exist, YES, newer
3204     if (stat(nativeB.c_str(), &infoB)<0)
3205         {
3206                 return true;
3207                 }
3209     //check the actual times
3210     if (infoA.st_mtime > infoB.st_mtime)
3211         {
3212                 return true;
3213                 }
3215     return false;
3219 //########################################################################
3220 //# P K G    C O N F I G
3221 //########################################################################
3223 /**
3224  *
3225  */
3226 class PkgConfig : public MakeBase
3229 public:
3231     /**
3232      *
3233      */
3234     PkgConfig()
3235         { init(); }
3237     /**
3238      *
3239      */
3240     PkgConfig(const String &namearg)
3241         { init(); name = namearg; }
3243     /**
3244      *
3245      */
3246     PkgConfig(const PkgConfig &other)
3247         { assign(other); }
3249     /**
3250      *
3251      */
3252     PkgConfig &operator=(const PkgConfig &other)
3253         { assign(other); return *this; }
3255     /**
3256      *
3257      */
3258     virtual ~PkgConfig()
3259         { }
3261     /**
3262      *
3263      */
3264     virtual String getName()
3265         { return name; }
3267     /**
3268      *
3269      */
3270     virtual String getDescription()
3271         { return description; }
3273     /**
3274      *
3275      */
3276     virtual String getCflags()
3277         { return cflags; }
3279     /**
3280      *
3281      */
3282     virtual String getLibs()
3283         { return libs; }
3285     /**
3286      *
3287      */
3288     virtual String getVersion()
3289         { return version; }
3291     /**
3292      *
3293      */
3294     virtual int getMajorVersion()
3295         { return majorVersion; }
3297     /**
3298      *
3299      */
3300     virtual int getMinorVersion()
3301         { return minorVersion; }
3303     /**
3304      *
3305      */
3306     virtual int getMicroVersion()
3307         { return microVersion; }
3309     /**
3310      *
3311      */
3312     virtual std::map<String, String> &getAttributes()
3313         { return attrs; }
3315     /**
3316      *
3317      */
3318     virtual std::vector<String> &getRequireList()
3319         { return requireList; }
3321     virtual bool readFile(const String &fileName);
3323 private:
3325     void init()
3326         {
3327         name         = "";
3328         description  = "";
3329         cflags       = "";
3330         libs         = "";
3331         requires     = "";
3332         version      = "";
3333         majorVersion = 0;
3334         minorVersion = 0;
3335         microVersion = 0;
3336         fileName     = "";
3337         attrs.clear();
3338         requireList.clear();
3339         }
3341     void assign(const PkgConfig &other)
3342         {
3343         name         = other.name;
3344         description  = other.description;
3345         cflags       = other.cflags;
3346         libs         = other.libs;
3347         requires     = other.requires;
3348         version      = other.version;
3349         majorVersion = other.majorVersion;
3350         minorVersion = other.minorVersion;
3351         microVersion = other.microVersion;
3352         fileName     = other.fileName;
3353         attrs        = other.attrs;
3354         requireList  = other.requireList;
3355         }
3359     int get(int pos);
3361     int skipwhite(int pos);
3363     int getword(int pos, String &ret);
3365     void parseRequires();
3367     void parseVersion();
3369     bool parse(const String &buf);
3371     void dumpAttrs();
3373     String name;
3375     String description;
3377     String cflags;
3379     String libs;
3381     String requires;
3383     String version;
3385     int majorVersion;
3387     int minorVersion;
3389     int microVersion;
3391     String fileName;
3393     std::map<String, String> attrs;
3395     std::vector<String> requireList;
3397     char *parsebuf;
3398     int parselen;
3399 };
3402 /**
3403  * Get a character from the buffer at pos.  If out of range,
3404  * return -1 for safety
3405  */
3406 int PkgConfig::get(int pos)
3408     if (pos>parselen)
3409         return -1;
3410     return parsebuf[pos];
3415 /**
3416  *  Skip over all whitespace characters beginning at pos.  Return
3417  *  the position of the first non-whitespace character.
3418  */
3419 int PkgConfig::skipwhite(int pos)
3421     while (pos < parselen)
3422         {
3423         int ch = get(pos);
3424         if (ch < 0)
3425             break;
3426         if (!isspace(ch))
3427             break;
3428         pos++;
3429         }
3430     return pos;
3434 /**
3435  *  Parse the buffer beginning at pos, for a word.  Fill
3436  *  'ret' with the result.  Return the position after the
3437  *  word.
3438  */
3439 int PkgConfig::getword(int pos, String &ret)
3441     while (pos < parselen)
3442         {
3443         int ch = get(pos);
3444         if (ch < 0)
3445             break;
3446         if (!isalnum(ch) && ch != '_' && ch != '-'&& ch != '.')
3447             break;
3448         ret.push_back((char)ch);
3449         pos++;
3450         }
3451     return pos;
3454 void PkgConfig::parseRequires()
3456     if (requires.size() == 0)
3457         return;
3458     parsebuf = (char *)requires.c_str();
3459     parselen = requires.size();
3460     int pos = 0;
3461     while (pos < parselen)
3462         {
3463         pos = skipwhite(pos);
3464         String val;
3465         int pos2 = getword(pos, val);
3466         if (pos2 == pos)
3467             break;
3468         pos = pos2;
3469         //trace("val %s", val.c_str());
3470         requireList.push_back(val);
3471         }
3474 static int getint(const String str)
3476     char *s = (char *)str.c_str();
3477     char *ends = NULL;
3478     long val = strtol(s, &ends, 10);
3479     if (ends == s)
3480         return 0L;
3481     else
3482         return val;
3485 void PkgConfig::parseVersion()
3487     if (version.size() == 0)
3488         return;
3489     String s1, s2, s3;
3490     unsigned int pos = 0;
3491     unsigned int pos2 = version.find('.', pos);
3492     if (pos2 == version.npos)
3493         {
3494         s1 = version;
3495         }
3496     else
3497         {
3498         s1 = version.substr(pos, pos2-pos);
3499         pos = pos2;
3500         pos++;
3501         if (pos < version.size())
3502             {
3503             pos2 = version.find('.', pos);
3504             if (pos2 == version.npos)
3505                 {
3506                 s2 = version.substr(pos, version.size()-pos);
3507                 }
3508             else
3509                 {
3510                 s2 = version.substr(pos, pos2-pos);
3511                 pos = pos2;
3512                 pos++;
3513                 if (pos < version.size())
3514                     s3 = version.substr(pos, pos2-pos);
3515                 }
3516             }
3517         }
3519     majorVersion = getint(s1);
3520     minorVersion = getint(s2);
3521     microVersion = getint(s3);
3522     //trace("version:%d.%d.%d", majorVersion,
3523     //          minorVersion, microVersion );
3527 bool PkgConfig::parse(const String &buf)
3529     init();
3531     parsebuf = (char *)buf.c_str();
3532     parselen = buf.size();
3533     int pos = 0;
3536     while (pos < parselen)
3537         {
3538         String attrName;
3539         pos = skipwhite(pos);
3540         int ch = get(pos);
3541         if (ch == '#')
3542             {
3543             //comment.  eat the rest of the line
3544             while (pos < parselen)
3545                 {
3546                 ch = get(pos);
3547                 if (ch == '\n' || ch < 0)
3548                     break;
3549                 pos++;
3550                 }
3551             continue;
3552             }
3553         pos = getword(pos, attrName);
3554         if (attrName.size() == 0)
3555             continue;
3556         pos = skipwhite(pos);
3557         ch = get(pos);
3558         if (ch != ':' && ch != '=')
3559             {
3560             error("expected ':' or '='");
3561             return false;
3562             }
3563         pos++;
3564         pos = skipwhite(pos);
3565         String attrVal;
3566         while (pos < parselen)
3567             {
3568             ch = get(pos);
3569             if (ch == '\n' || ch < 0)
3570                 break;
3571             else if (ch == '$' && get(pos+1) == '{')
3572                 {
3573                 //#  this is a ${substitution}
3574                 pos += 2;
3575                 String subName;
3576                 while (pos < parselen)
3577                     {
3578                     ch = get(pos);
3579                     if (ch < 0)
3580                         {
3581                         error("unterminated substitution");
3582                         return false;
3583                         }
3584                     else if (ch == '}')
3585                         break;
3586                     else
3587                         subName.push_back((char)ch);
3588                     pos++;
3589                     }
3590                 //trace("subName:%s", subName.c_str());
3591                 String subVal = attrs[subName];
3592                 //trace("subVal:%s", subVal.c_str());
3593                 attrVal.append(subVal);
3594                 }
3595             else
3596                 attrVal.push_back((char)ch);
3597             pos++;
3598             }
3600         attrVal = trim(attrVal);
3601         attrs[attrName] = attrVal;
3603         if (attrName == "Name")
3604             name = attrVal;
3605         else if (attrName == "Description")
3606             description = attrVal;
3607         else if (attrName == "Cflags")
3608             cflags = attrVal;
3609         else if (attrName == "Libs")
3610             libs = attrVal;
3611         else if (attrName == "Requires")
3612             requires = attrVal;
3613         else if (attrName == "Version")
3614             version = attrVal;
3616         //trace("name:'%s'  value:'%s'",
3617         //      attrName.c_str(), attrVal.c_str());
3618         }
3621     parseRequires();
3622     parseVersion();
3624     return true;
3627 void PkgConfig::dumpAttrs()
3629     trace("### PkgConfig attributes for %s", fileName.c_str());
3630     std::map<String, String>::iterator iter;
3631     for (iter=attrs.begin() ; iter!=attrs.end() ; iter++)
3632         {
3633         trace("   %s = %s", iter->first.c_str(), iter->second.c_str());
3634         }
3638 bool PkgConfig::readFile(const String &fileNameArg)
3640     fileName = fileNameArg;
3642     FILE *f = fopen(fileName.c_str(), "r");
3643     if (!f)
3644         {
3645         error("cannot open file '%s' for reading", fileName.c_str());
3646         return false;
3647         }
3648     String buf;
3649     while (true)
3650         {
3651         int ch = fgetc(f);
3652         if (ch < 0)
3653             break;
3654         buf.push_back((char)ch);
3655         }
3656     fclose(f);
3658     trace("####### File:\n%s", buf.c_str());
3659     if (!parse(buf))
3660         {
3661         return false;
3662         }
3664     dumpAttrs();
3666     return true;
3673 //########################################################################
3674 //# D E P T O O L
3675 //########################################################################
3679 /**
3680  *  Class which holds information for each file.
3681  */
3682 class FileRec
3684 public:
3686     typedef enum
3687         {
3688         UNKNOWN,
3689         CFILE,
3690         HFILE,
3691         OFILE
3692         } FileType;
3694     /**
3695      *  Constructor
3696      */
3697     FileRec()
3698         {init(); type = UNKNOWN;}
3700     /**
3701      *  Copy constructor
3702      */
3703     FileRec(const FileRec &other)
3704         {init(); assign(other);}
3705     /**
3706      *  Constructor
3707      */
3708     FileRec(int typeVal)
3709         {init(); type = typeVal;}
3710     /**
3711      *  Assignment operator
3712      */
3713     FileRec &operator=(const FileRec &other)
3714         {init(); assign(other); return *this;}
3717     /**
3718      *  Destructor
3719      */
3720     ~FileRec()
3721         {}
3723     /**
3724      *  Directory part of the file name
3725      */
3726     String path;
3728     /**
3729      *  Base name, sans directory and suffix
3730      */
3731     String baseName;
3733     /**
3734      *  File extension, such as cpp or h
3735      */
3736     String suffix;
3738     /**
3739      *  Type of file: CFILE, HFILE, OFILE
3740      */
3741     int type;
3743     /**
3744      * Used to list files ref'd by this one
3745      */
3746     std::map<String, FileRec *> files;
3749 private:
3751     void init()
3752         {
3753         }
3755     void assign(const FileRec &other)
3756         {
3757         type     = other.type;
3758         baseName = other.baseName;
3759         suffix   = other.suffix;
3760         files    = other.files;
3761         }
3763 };
3767 /**
3768  *  Simpler dependency record
3769  */
3770 class DepRec
3772 public:
3774     /**
3775      *  Constructor
3776      */
3777     DepRec()
3778         {init();}
3780     /**
3781      *  Copy constructor
3782      */
3783     DepRec(const DepRec &other)
3784         {init(); assign(other);}
3785     /**
3786      *  Constructor
3787      */
3788     DepRec(const String &fname)
3789         {init(); name = fname; }
3790     /**
3791      *  Assignment operator
3792      */
3793     DepRec &operator=(const DepRec &other)
3794         {init(); assign(other); return *this;}
3797     /**
3798      *  Destructor
3799      */
3800     ~DepRec()
3801         {}
3803     /**
3804      *  Directory part of the file name
3805      */
3806     String path;
3808     /**
3809      *  Base name, without the path and suffix
3810      */
3811     String name;
3813     /**
3814      *  Suffix of the source
3815      */
3816     String suffix;
3819     /**
3820      * Used to list files ref'd by this one
3821      */
3822     std::vector<String> files;
3825 private:
3827     void init()
3828         {
3829         }
3831     void assign(const DepRec &other)
3832         {
3833         path     = other.path;
3834         name     = other.name;
3835         suffix   = other.suffix;
3836         files    = other.files;
3837         }
3839 };
3842 class DepTool : public MakeBase
3844 public:
3846     /**
3847      *  Constructor
3848      */
3849     DepTool()
3850         {init();}
3852     /**
3853      *  Copy constructor
3854      */
3855     DepTool(const DepTool &other)
3856         {init(); assign(other);}
3858     /**
3859      *  Assignment operator
3860      */
3861     DepTool &operator=(const DepTool &other)
3862         {init(); assign(other); return *this;}
3865     /**
3866      *  Destructor
3867      */
3868     ~DepTool()
3869         {}
3872     /**
3873      *  Reset this section of code
3874      */
3875     virtual void init();
3876     
3877     /**
3878      *  Reset this section of code
3879      */
3880     virtual void assign(const DepTool &other)
3881         {
3882         }
3883     
3884     /**
3885      *  Sets the source directory which will be scanned
3886      */
3887     virtual void setSourceDirectory(const String &val)
3888         { sourceDir = val; }
3890     /**
3891      *  Returns the source directory which will be scanned
3892      */
3893     virtual String getSourceDirectory()
3894         { return sourceDir; }
3896     /**
3897      *  Sets the list of files within the directory to analyze
3898      */
3899     virtual void setFileList(const std::vector<String> &list)
3900         { fileList = list; }
3902     /**
3903      * Creates the list of all file names which will be
3904      * candidates for further processing.  Reads make.exclude
3905      * to see which files for directories to leave out.
3906      */
3907     virtual bool createFileList();
3910     /**
3911      *  Generates the forward dependency list
3912      */
3913     virtual bool generateDependencies();
3916     /**
3917      *  Generates the forward dependency list, saving the file
3918      */
3919     virtual bool generateDependencies(const String &);
3922     /**
3923      *  Load a dependency file
3924      */
3925     std::vector<DepRec> loadDepFile(const String &fileName);
3927     /**
3928      *  Load a dependency file, generating one if necessary
3929      */
3930     std::vector<DepRec> getDepFile(const String &fileName);
3932     /**
3933      *  Save a dependency file
3934      */
3935     bool saveDepFile(const String &fileName);
3938 private:
3941     /**
3942      *
3943      */
3944     void parseName(const String &fullname,
3945                    String &path,
3946                    String &basename,
3947                    String &suffix);
3949     /**
3950      *
3951      */
3952     int get(int pos);
3954     /**
3955      *
3956      */
3957     int skipwhite(int pos);
3959     /**
3960      *
3961      */
3962     int getword(int pos, String &ret);
3964     /**
3965      *
3966      */
3967     bool sequ(int pos, char *key);
3969     /**
3970      *
3971      */
3972     bool addIncludeFile(FileRec *frec, const String &fname);
3974     /**
3975      *
3976      */
3977     bool scanFile(const String &fname, FileRec *frec);
3979     /**
3980      *
3981      */
3982     bool processDependency(FileRec *ofile,
3983                            FileRec *include,
3984                            int depth);
3986     /**
3987      *
3988      */
3989     String sourceDir;
3991     /**
3992      *
3993      */
3994     std::vector<String> fileList;
3996     /**
3997      *
3998      */
3999     std::vector<String> directories;
4001     /**
4002      * A list of all files which will be processed for
4003      * dependencies.  This is the only list that has the actual
4004      * records.  All other lists have pointers to these records.     
4005      */
4006     std::map<String, FileRec *> allFiles;
4008     /**
4009      * The list of .o files, and the
4010      * dependencies upon them.
4011      */
4012     std::map<String, FileRec *> depFiles;
4014     int depFileSize;
4015     char *depFileBuf;
4016     
4018 };
4024 /**
4025  *  Clean up after processing.  Called by the destructor, but should
4026  *  also be called before the object is reused.
4027  */
4028 void DepTool::init()
4030     sourceDir = ".";
4032     fileList.clear();
4033     directories.clear();
4034     
4035     //clear refs
4036     depFiles.clear();
4037     //clear records
4038     std::map<String, FileRec *>::iterator iter;
4039     for (iter=allFiles.begin() ; iter!=allFiles.end() ; iter++)
4040          delete iter->second;
4042     allFiles.clear(); 
4049 /**
4050  *  Parse a full path name into path, base name, and suffix
4051  */
4052 void DepTool::parseName(const String &fullname,
4053                         String &path,
4054                         String &basename,
4055                         String &suffix)
4057     if (fullname.size() < 2)
4058         return;
4060     unsigned int pos = fullname.find_last_of('/');
4061     if (pos != fullname.npos && pos<fullname.size()-1)
4062         {
4063         path = fullname.substr(0, pos);
4064         pos++;
4065         basename = fullname.substr(pos, fullname.size()-pos);
4066         }
4067     else
4068         {
4069         path = "";
4070         basename = fullname;
4071         }
4073     pos = basename.find_last_of('.');
4074     if (pos != basename.npos && pos<basename.size()-1)
4075         {
4076         suffix   = basename.substr(pos+1, basename.size()-pos-1);
4077         basename = basename.substr(0, pos);
4078         }
4080     //trace("parsename:%s %s %s", path.c_str(),
4081     //        basename.c_str(), suffix.c_str()); 
4086 /**
4087  *  Generate our internal file list.
4088  */
4089 bool DepTool::createFileList()
4092     for (unsigned int i=0 ; i<fileList.size() ; i++)
4093         {
4094         String fileName = fileList[i];
4095         //trace("## FileName:%s", fileName.c_str());
4096         String path;
4097         String basename;
4098         String sfx;
4099         parseName(fileName, path, basename, sfx);
4100         if (sfx == "cpp" || sfx == "c" || sfx == "cxx"   ||
4101                     sfx == "cc" || sfx == "CC")
4102             {
4103             FileRec *fe         = new FileRec(FileRec::CFILE);
4104             fe->path            = path;
4105             fe->baseName        = basename;
4106             fe->suffix          = sfx;
4107             allFiles[fileName]  = fe;
4108             }
4109         else if (sfx == "h"   ||  sfx == "hh"  ||
4110                  sfx == "hpp" ||  sfx == "hxx")
4111             {
4112             FileRec *fe         = new FileRec(FileRec::HFILE);
4113             fe->path            = path;
4114             fe->baseName        = basename;
4115             fe->suffix          = sfx;
4116             allFiles[fileName]  = fe;
4117             }
4118         }
4120     if (!listDirectories(sourceDir, "", directories))
4121         return false;
4122         
4123     return true;
4130 /**
4131  * Get a character from the buffer at pos.  If out of range,
4132  * return -1 for safety
4133  */
4134 int DepTool::get(int pos)
4136     if (pos>depFileSize)
4137         return -1;
4138     return depFileBuf[pos];
4143 /**
4144  *  Skip over all whitespace characters beginning at pos.  Return
4145  *  the position of the first non-whitespace character.
4146  */
4147 int DepTool::skipwhite(int pos)
4149     while (pos < depFileSize)
4150         {
4151         int ch = get(pos);
4152         if (ch < 0)
4153             break;
4154         if (!isspace(ch))
4155             break;
4156         pos++;
4157         }
4158     return pos;
4162 /**
4163  *  Parse the buffer beginning at pos, for a word.  Fill
4164  *  'ret' with the result.  Return the position after the
4165  *  word.
4166  */
4167 int DepTool::getword(int pos, String &ret)
4169     while (pos < depFileSize)
4170         {
4171         int ch = get(pos);
4172         if (ch < 0)
4173             break;
4174         if (isspace(ch))
4175             break;
4176         ret.push_back((char)ch);
4177         pos++;
4178         }
4179     return pos;
4182 /**
4183  * Return whether the sequence of characters in the buffer
4184  * beginning at pos match the key,  for the length of the key
4185  */
4186 bool DepTool::sequ(int pos, char *key)
4188     while (*key)
4189         {
4190         if (*key != get(pos))
4191             return false;
4192         key++; pos++;
4193         }
4194     return true;
4199 /**
4200  *  Add an include file name to a file record.  If the name
4201  *  is not found in allFiles explicitly, try prepending include
4202  *  directory names to it and try again.
4203  */
4204 bool DepTool::addIncludeFile(FileRec *frec, const String &iname)
4207     std::map<String, FileRec *>::iterator iter =
4208            allFiles.find(iname);
4209     if (iter != allFiles.end()) //already exists
4210         {
4211          //h file in same dir
4212         FileRec *other = iter->second;
4213         //trace("local: '%s'", iname.c_str());
4214         frec->files[iname] = other;
4215         return true;
4216         }
4217     else 
4218         {
4219         //look in other dirs
4220         std::vector<String>::iterator diter;
4221         for (diter=directories.begin() ;
4222              diter!=directories.end() ; diter++)
4223             {
4224             String dfname = *diter;
4225             dfname.append("/");
4226             dfname.append(iname);
4227             iter = allFiles.find(dfname);
4228             if (iter != allFiles.end())
4229                 {
4230                 FileRec *other = iter->second;
4231                 //trace("other: '%s'", iname.c_str());
4232                 frec->files[dfname] = other;
4233                 return true;
4234                 }
4235             }
4236         }
4237     return true;
4242 /**
4243  *  Lightly parse a file to find the #include directives.  Do
4244  *  a bit of state machine stuff to make sure that the directive
4245  *  is valid.  (Like not in a comment).
4246  */
4247 bool DepTool::scanFile(const String &fname, FileRec *frec)
4249     String fileName;
4250     if (sourceDir.size() > 0)
4251         {
4252         fileName.append(sourceDir);
4253         fileName.append("/");
4254         }
4255     fileName.append(fname);
4256     String nativeName = getNativePath(fileName);
4257     FILE *f = fopen(nativeName.c_str(), "r");
4258     if (!f)
4259         {
4260         error("Could not open '%s' for reading", fname.c_str());
4261         return false;
4262         }
4263     String buf;
4264     while (true)
4265         {
4266         int ch = fgetc(f);
4267         if (ch < 0)
4268             break;
4269         buf.push_back((char)ch);
4270         }
4271     fclose(f);
4273     depFileSize = buf.size();
4274     depFileBuf  = (char *)buf.c_str();
4275     int pos = 0;
4278     while (pos < depFileSize)
4279         {
4280         //trace("p:%c", get(pos));
4282         //# Block comment
4283         if (get(pos) == '/' && get(pos+1) == '*')
4284             {
4285             pos += 2;
4286             while (pos < depFileSize)
4287                 {
4288                 if (get(pos) == '*' && get(pos+1) == '/')
4289                     {
4290                     pos += 2;
4291                     break;
4292                     }
4293                 else
4294                     pos++;
4295                 }
4296             }
4297         //# Line comment
4298         else if (get(pos) == '/' && get(pos+1) == '/')
4299             {
4300             pos += 2;
4301             while (pos < depFileSize)
4302                 {
4303                 if (get(pos) == '\n')
4304                     {
4305                     pos++;
4306                     break;
4307                     }
4308                 else
4309                     pos++;
4310                 }
4311             }
4312         //# #include! yaay
4313         else if (sequ(pos, "#include"))
4314             {
4315             pos += 8;
4316             pos = skipwhite(pos);
4317             String iname;
4318             pos = getword(pos, iname);
4319             if (iname.size()>2)
4320                 {
4321                 iname = iname.substr(1, iname.size()-2);
4322                 addIncludeFile(frec, iname);
4323                 }
4324             }
4325         else
4326             {
4327             pos++;
4328             }
4329         }
4331     return true;
4336 /**
4337  *  Recursively check include lists to find all files in allFiles to which
4338  *  a given file is dependent.
4339  */
4340 bool DepTool::processDependency(FileRec *ofile,
4341                              FileRec *include,
4342                              int depth)
4344     std::map<String, FileRec *>::iterator iter;
4345     for (iter=include->files.begin() ; iter!=include->files.end() ; iter++)
4346         {
4347         String fname  = iter->first;
4348         if (ofile->files.find(fname) != ofile->files.end())
4349             {
4350             //trace("file '%s' already seen", fname.c_str());
4351             continue;
4352             }
4353         FileRec *child  = iter->second;
4354         ofile->files[fname] = child;
4355       
4356         processDependency(ofile, child, depth+1);
4357         }
4360     return true;
4367 /**
4368  *  Generate the file dependency list.
4369  */
4370 bool DepTool::generateDependencies()
4372     std::map<String, FileRec *>::iterator iter;
4373     //# First pass.  Scan for all includes
4374     for (iter=allFiles.begin() ; iter!=allFiles.end() ; iter++)
4375         {
4376         FileRec *frec = iter->second;
4377         if (!scanFile(iter->first, frec))
4378             {
4379             //quit?
4380             }
4381         }
4383     //# Second pass.  Scan for all includes
4384     for (iter=allFiles.begin() ; iter!=allFiles.end() ; iter++)
4385         {
4386         FileRec *include = iter->second;
4387         if (include->type == FileRec::CFILE)
4388             {
4389             String cFileName = iter->first;
4390             FileRec *ofile      = new FileRec(FileRec::OFILE);
4391             ofile->path         = include->path;
4392             ofile->baseName     = include->baseName;
4393             ofile->suffix       = include->suffix;
4394             String fname     = include->path;
4395             if (fname.size()>0)
4396                 fname.append("/");
4397             fname.append(include->baseName);
4398             fname.append(".o");
4399             depFiles[fname]    = ofile;
4400             //add the .c file first?   no, don't
4401             //ofile->files[cFileName] = include;
4402             
4403             //trace("ofile:%s", fname.c_str());
4405             processDependency(ofile, include, 0);
4406             }
4407         }
4409       
4410     return true;
4415 /**
4416  *  High-level call to generate deps and optionally save them
4417  */
4418 bool DepTool::generateDependencies(const String &fileName)
4420     if (!createFileList())
4421         return false;
4422     if (!generateDependencies())
4423         return false;
4424     if (!saveDepFile(fileName))
4425         return false;
4426     return true;
4430 /**
4431  *   This saves the dependency cache.
4432  */
4433 bool DepTool::saveDepFile(const String &fileName)
4435     time_t tim;
4436     time(&tim);
4438     FILE *f = fopen(fileName.c_str(), "w");
4439     if (!f)
4440         {
4441         trace("cannot open '%s' for writing", fileName.c_str());
4442         }
4443     fprintf(f, "<?xml version='1.0'?>\n");
4444     fprintf(f, "<!--\n");
4445     fprintf(f, "########################################################\n");
4446     fprintf(f, "## File: build.dep\n");
4447     fprintf(f, "## Generated by BuildTool at :%s", ctime(&tim));
4448     fprintf(f, "########################################################\n");
4449     fprintf(f, "-->\n");
4451     fprintf(f, "<dependencies source='%s'>\n\n", sourceDir.c_str());
4452     std::map<String, FileRec *>::iterator iter;
4453     for (iter=depFiles.begin() ; iter!=depFiles.end() ; iter++)
4454         {
4455         FileRec *frec = iter->second;
4456         if (frec->type == FileRec::OFILE)
4457             {
4458             fprintf(f, "<object path='%s' name='%s' suffix='%s'>\n",
4459                              frec->path.c_str(), frec->baseName.c_str(), frec->suffix.c_str());
4460             std::map<String, FileRec *>::iterator citer;
4461             for (citer=frec->files.begin() ; citer!=frec->files.end() ; citer++)
4462                 {
4463                 String cfname = citer->first;
4464                 fprintf(f, "    <dep name='%s'/>\n", cfname.c_str());
4465                 }
4466             fprintf(f, "</object>\n\n");
4467             }
4468         }
4470     fprintf(f, "</dependencies>\n");
4471     fprintf(f, "\n");
4472     fprintf(f, "<!--\n");
4473     fprintf(f, "########################################################\n");
4474     fprintf(f, "## E N D\n");
4475     fprintf(f, "########################################################\n");
4476     fprintf(f, "-->\n");
4478     fclose(f);
4480     return true;
4486 /**
4487  *   This loads the dependency cache.
4488  */
4489 std::vector<DepRec> DepTool::loadDepFile(const String &depFile)
4491     std::vector<DepRec> result;
4492     
4493     Parser parser;
4494     Element *root = parser.parseFile(depFile.c_str());
4495     if (!root)
4496         {
4497         error("Could not open %s for reading", depFile.c_str());
4498         return result;
4499         }
4501     if (root->getChildren().size()==0 ||
4502         root->getChildren()[0]->getName()!="dependencies")
4503         {
4504         error("Main xml element should be <dependencies>");
4505         delete root;
4506         return result;
4507         }
4509     //########## Start parsing
4510     Element *depList = root->getChildren()[0];
4512     std::vector<Element *> objects = depList->getChildren();
4513     for (unsigned int i=0 ; i<objects.size() ; i++)
4514         {
4515         Element *objectElem = objects[i];
4516         String tagName = objectElem->getName();
4517         if (tagName == "object")
4518             {
4519             String objName   = objectElem->getAttribute("name");
4520              //trace("object:%s", objName.c_str());
4521             DepRec depObject(objName);
4522             depObject.path   = objectElem->getAttribute("path");
4523             depObject.suffix = objectElem->getAttribute("suffix");
4524             //########## DESCRIPTION
4525             std::vector<Element *> depElems = objectElem->getChildren();
4526             for (unsigned int i=0 ; i<depElems.size() ; i++)
4527                 {
4528                 Element *depElem = depElems[i];
4529                 tagName = depElem->getName();
4530                 if (tagName == "dep")
4531                     {
4532                     String depName = depElem->getAttribute("name");
4533                     //trace("    dep:%s", depName.c_str());
4534                     depObject.files.push_back(depName);
4535                     }
4536                 }
4537             //Insert into the result list, in a sorted manner
4538             bool inserted = false;
4539             std::vector<DepRec>::iterator iter;
4540             for (iter = result.begin() ; iter != result.end() ; iter++)
4541                 {
4542                 String vpath = iter->path;
4543                 vpath.append("/");
4544                 vpath.append(iter->name);
4545                 String opath = depObject.path;
4546                 opath.append("/");
4547                 opath.append(depObject.name);
4548                 if (vpath > opath)
4549                     {
4550                     inserted = true;
4551                     iter = result.insert(iter, depObject);
4552                     break;
4553                     }
4554                 }
4555             if (!inserted)
4556                 result.push_back(depObject);
4557             }
4558         }
4560     delete root;
4562     return result;
4566 /**
4567  *   This loads the dependency cache.
4568  */
4569 std::vector<DepRec> DepTool::getDepFile(const String &depFile)
4571     std::vector<DepRec> result = loadDepFile(depFile);
4572     if (result.size() == 0)
4573         {
4574         generateDependencies(depFile);
4575         result = loadDepFile(depFile);
4576         }
4577     return result;
4583 //########################################################################
4584 //# T A S K
4585 //########################################################################
4586 //forward decl
4587 class Target;
4588 class Make;
4590 /**
4591  *
4592  */
4593 class Task : public MakeBase
4596 public:
4598     typedef enum
4599         {
4600         TASK_NONE,
4601         TASK_AR,
4602         TASK_CC,
4603         TASK_COPY,
4604         TASK_DELETE,
4605         TASK_JAR,
4606         TASK_JAVAC,
4607         TASK_LINK,
4608         TASK_MAKEFILE,
4609         TASK_MKDIR,
4610         TASK_MSGFMT,
4611         TASK_RANLIB,
4612         TASK_RC,
4613         TASK_STRIP,
4614         TASK_TSTAMP
4615         } TaskType;
4616         
4618     /**
4619      *
4620      */
4621     Task(MakeBase &par) : parent(par)
4622         { init(); }
4624     /**
4625      *
4626      */
4627     Task(const Task &other) : parent(other.parent)
4628         { init(); assign(other); }
4630     /**
4631      *
4632      */
4633     Task &operator=(const Task &other)
4634         { assign(other); return *this; }
4636     /**
4637      *
4638      */
4639     virtual ~Task()
4640         { }
4643     /**
4644      *
4645      */
4646     virtual MakeBase &getParent()
4647         { return parent; }
4649      /**
4650      *
4651      */
4652     virtual int  getType()
4653         { return type; }
4655     /**
4656      *
4657      */
4658     virtual void setType(int val)
4659         { type = val; }
4661     /**
4662      *
4663      */
4664     virtual String getName()
4665         { return name; }
4667     /**
4668      *
4669      */
4670     virtual bool execute()
4671         { return true; }
4673     /**
4674      *
4675      */
4676     virtual bool parse(Element *elem)
4677         { return true; }
4679     /**
4680      *
4681      */
4682     Task *createTask(Element *elem);
4685 protected:
4687     void init()
4688         {
4689         type = TASK_NONE;
4690         name = "none";
4691         }
4693     void assign(const Task &other)
4694         {
4695         type = other.type;
4696         name = other.name;
4697         }
4698         
4699     String getAttribute(Element *elem, const String &attrName)
4700         {
4701         String str;
4702         return str;
4703         }
4705     MakeBase &parent;
4707     int type;
4709     String name;
4710 };
4715 /**
4716  * Run the "ar" command to archive .o's into a .a
4717  */
4718 class TaskAr : public Task
4720 public:
4722     TaskAr(MakeBase &par) : Task(par)
4723         {
4724                 type = TASK_AR; name = "ar";
4725                 command = "ar crv";
4726                 }
4728     virtual ~TaskAr()
4729         {}
4731     virtual bool execute()
4732         {
4733         //trace("###########HERE %d", fileSet.size());
4734         bool doit = false;
4735         
4736         String fullOut = parent.resolve(fileName);
4737         //trace("ar fullout: %s", fullOut.c_str());
4738         
4740         for (unsigned int i=0 ; i<fileSet.size() ; i++)
4741             {
4742             String fname;
4743                         if (fileSetDir.size()>0)
4744                             {
4745                             fname.append(fileSetDir);
4746                 fname.append("/");
4747                 }
4748             fname.append(fileSet[i]);
4749             String fullName = parent.resolve(fname);
4750             //trace("ar : %s/%s", fullOut.c_str(), fullName.c_str());
4751             if (isNewerThan(fullName, fullOut))
4752                 doit = true;
4753             }
4754         //trace("Needs it:%d", doit);
4755         if (!doit)
4756             {
4757             return true;
4758             }
4760         String cmd = command;
4761         cmd.append(" ");
4762         cmd.append(fullOut);
4763         for (unsigned int i=0 ; i<fileSet.size() ; i++)
4764             {
4765             String fname;
4766                         if (fileSetDir.size()>0)
4767                             {
4768                             fname.append(fileSetDir);
4769                 fname.append("/");
4770                 }
4771             fname.append(fileSet[i]);
4772             String fullName = parent.resolve(fname);
4774             cmd.append(" ");
4775             cmd.append(fullName);
4776             }
4778         String outString, errString;
4779         if (!executeCommand(cmd.c_str(), "", outString, errString))
4780             {
4781             error("AR problem: %s", errString.c_str());
4782             return false;
4783             }
4785         return true;
4786         }
4788     virtual bool parse(Element *elem)
4789         {
4790         if (!parent.getAttribute(elem, "file", fileName))
4791             return false;
4792             
4793         std::vector<Element *> children = elem->getChildren();
4794         for (unsigned int i=0 ; i<children.size() ; i++)
4795             {
4796             Element *child = children[i];
4797             String tagName = child->getName();
4798             if (tagName == "fileset")
4799                 {
4800                 if (!getFileSet(child, parent, fileSetDir, fileSet))
4801                     return false;
4802                 }
4803             }
4804         return true;
4805         }
4807 private:
4809     String command;
4810     String fileName;
4811     String fileSetDir;
4812     std::vector<String> fileSet;
4814 };
4817 /**
4818  * This task runs the C/C++ compiler.  The compiler is invoked
4819  * for all .c or .cpp files which are newer than their correcsponding
4820  * .o files.  
4821  */
4822 class TaskCC : public Task
4824 public:
4826     TaskCC(MakeBase &par) : Task(par)
4827         {
4828                 type = TASK_CC; name = "cc";
4829                 ccCommand   = "gcc";
4830                 cxxCommand  = "g++";
4831                 source      = ".";
4832                 dest        = ".";
4833                 flags       = "";
4834                 defines     = "";
4835                 includes    = "";
4836                 sourceFiles.clear();
4837         }
4839     virtual ~TaskCC()
4840         {}
4842     virtual bool execute()
4843         {
4844         DepTool depTool;
4845         depTool.setSourceDirectory(source);
4846         depTool.setFileList(sourceFiles);
4847         std::vector<DepRec> deps = depTool.getDepFile("build.dep");
4848         
4849         String incs;
4850         incs.append("-I");
4851         incs.append(parent.resolve("."));
4852         incs.append(" ");
4853         if (includes.size()>0)
4854             {
4855             incs.append(includes);
4856             incs.append(" ");
4857             }
4858         std::set<String> paths;
4859         std::vector<DepRec>::iterator viter;
4860         for (viter=deps.begin() ; viter!=deps.end() ; viter++)
4861             {
4862             DepRec dep = *viter;
4863             if (dep.path.size()>0)
4864                 paths.insert(dep.path);
4865             }
4866         if (source.size()>0)
4867             {
4868             incs.append(" -I");
4869             incs.append(parent.resolve(source));
4870             incs.append(" ");
4871             }
4872         std::set<String>::iterator setIter;
4873         for (setIter=paths.begin() ; setIter!=paths.end() ; setIter++)
4874             {
4875             incs.append(" -I");
4876             String dname;
4877             if (source.size()>0)
4878                 {
4879                 dname.append(source);
4880                 dname.append("/");
4881                 }
4882             dname.append(*setIter);
4883             incs.append(parent.resolve(dname));
4884             }
4885         std::vector<String> cfiles;
4886         for (viter=deps.begin() ; viter!=deps.end() ; viter++)
4887             {
4888             DepRec dep = *viter;
4890             //## Select command
4891             String sfx = dep.suffix;
4892             String command = ccCommand;
4893             if (sfx == "cpp" || sfx == "c++" || sfx == "cc"
4894                              || sfx == "CC")
4895                             command = cxxCommand;
4896  
4897             //## Make paths
4898             String destPath = dest;
4899             String srcPath  = source;
4900             if (dep.path.size()>0)
4901                             {
4902                 destPath.append("/");
4903                                 destPath.append(dep.path);
4904                 srcPath.append("/");
4905                                 srcPath.append(dep.path);
4906                             }
4907             //## Make sure destination directory exists
4908                         if (!createDirectory(destPath))
4909                             return false;
4910                             
4911             //## Check whether it needs to be done
4912                         String destFullName = destPath;
4913                         destFullName.append("/");
4914                         destFullName.append(dep.name);
4915                         destFullName.append(".o");
4916                         String srcFullName = srcPath;
4917                         srcFullName.append("/");
4918                         srcFullName.append(dep.name);
4919                         srcFullName.append(".");
4920                         srcFullName.append(dep.suffix);
4921             if (!isNewerThan(srcFullName, destFullName))
4922                 {
4923                 //trace("%s skipped", srcFullName.c_str());
4924                 continue;
4925                 }
4927             //## Assemble the command
4928             String cmd = command;
4929             cmd.append(" -c ");
4930             cmd.append(flags);
4931                         cmd.append(" ");
4932             cmd.append(defines);
4933                         cmd.append(" ");
4934             cmd.append(incs);
4935                         cmd.append(" ");
4936                         cmd.append(srcFullName);
4937             cmd.append(" -o ");
4938                         cmd.append(destFullName);
4940             //## Execute the command
4942             String outString, errString;
4943             if (!executeCommand(cmd.c_str(), "", outString, errString))
4944                 {
4945                 error("problem compiling: %s", errString.c_str());
4946                 return false;
4947                 }
4948             }
4949         
4950         return true;
4951         }
4953     virtual bool parse(Element *elem)
4954         {
4955         String s;
4956         if (!parent.getAttribute(elem, "command", s))
4957             return false;
4958         if (s.size()>0) { ccCommand = s; cxxCommand = s; }
4959         if (!parent.getAttribute(elem, "cc", s))
4960             return false;
4961         if (s.size()>0) ccCommand = s;
4962         if (!parent.getAttribute(elem, "cxx", s))
4963             return false;
4964         if (s.size()>0) cxxCommand = s;
4965         if (!parent.getAttribute(elem, "destdir", s))
4966             return false;
4967         if (s.size()>0) dest = s;
4969         std::vector<Element *> children = elem->getChildren();
4970         for (unsigned int i=0 ; i<children.size() ; i++)
4971             {
4972             Element *child = children[i];
4973             String tagName = child->getName();
4974             if (tagName == "flags")
4975                 {
4976                 if (!parent.getValue(child, flags))
4977                     return false;
4978                 flags = strip(flags);
4979                 }
4980             else if (tagName == "includes")
4981                 {
4982                 if (!parent.getValue(child, includes))
4983                     return false;
4984                 includes = strip(includes);
4985                 }
4986             else if (tagName == "defines")
4987                 {
4988                 if (!parent.getValue(child, defines))
4989                     return false;
4990                 defines = strip(defines);
4991                 }
4992             else if (tagName == "fileset")
4993                 {
4994                 if (!getFileSet(child, parent, source, sourceFiles))
4995                     return false;
4996                 }
4997             }
4999         return true;
5000         }
5001         
5002 protected:
5004     String ccCommand;
5005     String cxxCommand;
5006     String source;
5007     String dest;
5008     String flags;
5009     String defines;
5010     String includes;
5011     std::vector<String> sourceFiles;
5012     
5013 };
5017 /**
5018  *
5019  */
5020 class TaskCopy : public Task
5022 public:
5024     typedef enum
5025         {
5026         CP_NONE,
5027         CP_TOFILE,
5028         CP_TODIR
5029         } CopyType;
5031     TaskCopy(MakeBase &par) : Task(par)
5032         {
5033                 type = TASK_COPY; name = "copy";
5034                 cptype = CP_NONE;
5035                 verbose = false;
5036                 haveFileSet = false;
5037                 }
5039     virtual ~TaskCopy()
5040         {}
5042     virtual bool execute()
5043         {
5044         switch (cptype)
5045            {
5046            case CP_TOFILE:
5047                {
5048                if (fileName.size()>0)
5049                    {
5050                    status("          : %s", fileName.c_str());
5051                    String fullSource = parent.resolve(fileName);
5052                    String fullDest = parent.resolve(toFileName);
5053                    //trace("copy %s to file %s", fullSource.c_str(),
5054                                    //                       fullDest.c_str());
5055                                    if (!isRegularFile(fullSource))
5056                                        {
5057                        error("copy : file %s does not exist", fullSource.c_str());
5058                                        return false;
5059                                        }
5060                    if (!isNewerThan(fullSource, fullDest))
5061                        {
5062                        return true;
5063                        }
5064                    if (!copyFile(fullSource, fullDest))
5065                        return false;
5066                    status("          : 1 file copied");
5067                    }
5068                return true;
5069                }
5070            case CP_TODIR:
5071                {
5072                if (haveFileSet)
5073                    {
5074                    int nrFiles = 0;
5075                    status("          : %s", fileSetDir.c_str());
5076                    for (unsigned int i=0 ; i<fileSet.size() ; i++)
5077                        {
5078                        String fileName = fileSet[i];
5080                        String sourcePath;
5081                        if (fileSetDir.size()>0)
5082                            {
5083                            sourcePath.append(fileSetDir);
5084                            sourcePath.append("/");
5085                            }
5086                        sourcePath.append(fileName);
5087                        String fullSource = parent.resolve(sourcePath);
5088                        
5089                        //Get the immediate parent directory's base name
5090                        String baseFileSetDir = fileSetDir;
5091                        unsigned int pos = baseFileSetDir.find_last_of('/');
5092                        if (pos!=baseFileSetDir.npos &&
5093                                                       pos < baseFileSetDir.size()-1)
5094                            baseFileSetDir =
5095                                                       baseFileSetDir.substr(pos+1,
5096                                                                baseFileSetDir.size());
5097                                            //Now make the new path
5098                        String destPath;
5099                        if (toDirName.size()>0)
5100                            {
5101                            destPath.append(toDirName);
5102                            destPath.append("/");
5103                            }
5104                        if (baseFileSetDir.size()>0)
5105                            {
5106                            destPath.append(baseFileSetDir);
5107                            destPath.append("/");
5108                            }
5109                        destPath.append(fileName);
5110                        String fullDest = parent.resolve(destPath);
5111                        //trace("fileName:%s", fileName.c_str());
5112                        //trace("copy %s to new dir : %s", fullSource.c_str(),
5113                                        //                   fullDest.c_str());
5114                        if (!isNewerThan(fullSource, fullDest))
5115                            {
5116                            //trace("copy skipping %s", fullSource.c_str());
5117                            continue;
5118                            }
5119                        if (!copyFile(fullSource, fullDest))
5120                            return false;
5121                        nrFiles++;
5122                        }
5123                    status("          : %d file(s) copied", nrFiles);
5124                    }
5125                else //file source
5126                    {
5127                    //For file->dir we want only the basename of
5128                    //the source appended to the dest dir
5129                    status("          : %s", fileName.c_str());
5130                    String baseName = fileName;
5131                    unsigned int pos = baseName.find_last_of('/');
5132                    if (pos!=baseName.npos && pos<baseName.size()-1)
5133                        baseName = baseName.substr(pos+1, baseName.size());
5134                    String fullSource = parent.resolve(fileName);
5135                    String destPath;
5136                    if (toDirName.size()>0)
5137                        {
5138                        destPath.append(toDirName);
5139                        destPath.append("/");
5140                        }
5141                    destPath.append(baseName);
5142                    String fullDest = parent.resolve(destPath);
5143                    //trace("copy %s to new dir : %s", fullSource.c_str(),
5144                                    //                       fullDest.c_str());
5145                                    if (!isRegularFile(fullSource))
5146                                        {
5147                        error("copy : file %s does not exist", fullSource.c_str());
5148                                        return false;
5149                                        }
5150                    if (!isNewerThan(fullSource, fullDest))
5151                        {
5152                        return true;
5153                        }
5154                    if (!copyFile(fullSource, fullDest))
5155                        return false;
5156                    status("          : 1 file copied");
5157                    }
5158                return true;
5159                }
5160            }
5161         return true;
5162         }
5165     virtual bool parse(Element *elem)
5166         {
5167         if (!parent.getAttribute(elem, "file", fileName))
5168             return false;
5169         if (!parent.getAttribute(elem, "tofile", toFileName))
5170             return false;
5171         if (toFileName.size() > 0)
5172             cptype = CP_TOFILE;
5173         if (!parent.getAttribute(elem, "todir", toDirName))
5174             return false;
5175         if (toDirName.size() > 0)
5176             cptype = CP_TODIR;
5177         String ret;
5178         if (!parent.getAttribute(elem, "verbose", ret))
5179             return false;
5180         if (ret.size()>0 && !getBool(ret, verbose))
5181             return false;
5182             
5183         haveFileSet = false;
5184         
5185         std::vector<Element *> children = elem->getChildren();
5186         for (unsigned int i=0 ; i<children.size() ; i++)
5187             {
5188             Element *child = children[i];
5189             String tagName = child->getName();
5190             if (tagName == "fileset")
5191                 {
5192                 if (!getFileSet(child, parent, fileSetDir, fileSet))
5193                     {
5194                     error("problem getting fileset");
5195                                         return false;
5196                                         }
5197                                 haveFileSet = true;
5198                 }
5199             }
5201         //Perform validity checks
5202                 if (fileName.size()>0 && fileSet.size()>0)
5203                     {
5204                     error("<copy> can only have one of : file= and <fileset>");
5205                     return false;
5206                     }
5207         if (toFileName.size()>0 && toDirName.size()>0)
5208             {
5209             error("<copy> can only have one of : tofile= or todir=");
5210             return false;
5211             }
5212         if (haveFileSet && toDirName.size()==0)
5213             {
5214             error("a <copy> task with a <fileset> must have : todir=");
5215             return false;
5216             }
5217                 if (cptype == CP_TOFILE && fileName.size()==0)
5218                     {
5219                     error("<copy> tofile= must be associated with : file=");
5220                     return false;
5221                     }
5222                 if (cptype == CP_TODIR && fileName.size()==0 && !haveFileSet)
5223                     {
5224                     error("<copy> todir= must be associated with : file= or <fileset>");
5225                     return false;
5226                     }
5228         return true;
5229         }
5230         
5231 private:
5233     int cptype;
5234     String fileName;
5235     String fileSetDir;
5236     std::vector<String> fileSet;
5237     String toFileName;
5238     String toDirName;
5239     bool verbose;
5240     bool haveFileSet;
5241 };
5244 /**
5245  *
5246  */
5247 class TaskDelete : public Task
5249 public:
5251     typedef enum
5252         {
5253         DEL_FILE,
5254         DEL_DIR,
5255         DEL_FILESET
5256         } DeleteType;
5258     TaskDelete(MakeBase &par) : Task(par)
5259         { 
5260                   type        = TASK_DELETE;
5261                   name        = "delete";
5262                   delType     = DEL_FILE;
5263           verbose     = false;
5264           quiet       = false;
5265           failOnError = true;
5266                 }
5268     virtual ~TaskDelete()
5269         {}
5271     virtual bool execute()
5272         {
5273         struct stat finfo;
5274         switch (delType)
5275             {
5276             case DEL_FILE:
5277                 {
5278                 status("          : %s", fileName.c_str());
5279                 String fullName = parent.resolve(fileName);
5280                 char *fname = (char *)fullName.c_str();
5281                 //does not exist
5282                 if (stat(fname, &finfo)<0)
5283                     return true;
5284                 //exists but is not a regular file
5285                 if (!S_ISREG(finfo.st_mode))
5286                     {
5287                     error("<delete> failed. '%s' exists and is not a regular file",
5288                           fname);
5289                     return false;
5290                     }
5291                 if (remove(fname)<0)
5292                     {
5293                     error("<delete> failed: %s", strerror(errno));
5294                     return false;
5295                     }
5296                 return true;
5297                 }
5298             case DEL_DIR:
5299                 {
5300                 status("          : %s", dirName.c_str());
5301                 String fullDir = parent.resolve(dirName);
5302                 if (!removeDirectory(fullDir))
5303                     return false;
5304                 return true;
5305                 }
5306             }
5307         return true;
5308         }
5310     virtual bool parse(Element *elem)
5311         {
5312         if (!parent.getAttribute(elem, "file", fileName))
5313             return false;
5314         if (fileName.size() > 0)
5315             delType = DEL_FILE;
5316         if (!parent.getAttribute(elem, "dir", dirName))
5317             return false;
5318         if (dirName.size() > 0)
5319             delType = DEL_DIR;
5320         if (fileName.size()>0 && dirName.size()>0)
5321             {
5322             error("<delete> can only have one attribute of file= or dir=");
5323             return false;
5324             }
5325         String ret;
5326         if (!parent.getAttribute(elem, "verbose", ret))
5327             return false;
5328         if (ret.size()>0 && !getBool(ret, verbose))
5329             return false;
5330         if (!parent.getAttribute(elem, "quiet", ret))
5331             return false;
5332         if (ret.size()>0 && !getBool(ret, quiet))
5333             return false;
5334         if (!parent.getAttribute(elem, "failonerror", ret))
5335             return false;
5336         if (ret.size()>0 && !getBool(ret, failOnError))
5337             return false;
5338         return true;
5339         }
5341 private:
5343     int delType;
5344     String dirName;
5345     String fileName;
5346     bool verbose;
5347     bool quiet;
5348     bool failOnError;
5349 };
5352 /**
5353  *
5354  */
5355 class TaskJar : public Task
5357 public:
5359     TaskJar(MakeBase &par) : Task(par)
5360         { type = TASK_JAR; name = "jar"; }
5362     virtual ~TaskJar()
5363         {}
5365     virtual bool execute()
5366         {
5367         return true;
5368         }
5370     virtual bool parse(Element *elem)
5371         {
5372         return true;
5373         }
5374 };
5377 /**
5378  *
5379  */
5380 class TaskJavac : public Task
5382 public:
5384     TaskJavac(MakeBase &par) : Task(par)
5385         { type = TASK_JAVAC; name = "javac"; }
5387     virtual ~TaskJavac()
5388         {}
5390     virtual bool execute()
5391         {
5392         return true;
5393         }
5395     virtual bool parse(Element *elem)
5396         {
5397         return true;
5398         }
5399 };
5402 /**
5403  *
5404  */
5405 class TaskLink : public Task
5407 public:
5409     TaskLink(MakeBase &par) : Task(par)
5410         {
5411                 type = TASK_LINK; name = "link";
5412                 command = "g++";
5413                 }
5415     virtual ~TaskLink()
5416         {}
5418     virtual bool execute()
5419         {
5420         bool doit = false;
5421         String fullTarget = parent.resolve(fileName);
5422         String cmd = command;
5423         cmd.append(" -o ");
5424         cmd.append(fullTarget);
5425         cmd.append(" ");
5426         cmd.append(flags);
5427         for (unsigned int i=0 ; i<fileSet.size() ; i++)
5428             {
5429             cmd.append(" ");
5430             String obj;
5431             if (fileSetDir.size()>0)
5432                             {
5433                                 obj.append(fileSetDir);
5434                 obj.append("/");
5435                 }
5436             obj.append(fileSet[i]);
5437             String fullObj = parent.resolve(obj);
5438             cmd.append(fullObj);
5439             if (isNewerThan(fullObj, fullTarget))
5440                 doit = true;
5441             }
5442         cmd.append(" ");
5443         cmd.append(libs);
5444         if (!doit)
5445             {
5446             //trace("link not needed");
5447             return true;
5448             }
5449         //trace("LINK cmd:%s", cmd.c_str());
5452         String outString, errString;
5453         if (!executeCommand(cmd.c_str(), "", outString, errString))
5454             {
5455             error("LINK problem: %s", errString.c_str());
5456             return false;
5457             }
5458         return true;
5459         }
5461     virtual bool parse(Element *elem)
5462         {
5463         if (!parent.getAttribute(elem, "command", command))
5464             return false;
5465         if (!parent.getAttribute(elem, "out", fileName))
5466             return false;
5467             
5468         std::vector<Element *> children = elem->getChildren();
5469         for (unsigned int i=0 ; i<children.size() ; i++)
5470             {
5471             Element *child = children[i];
5472             String tagName = child->getName();
5473             if (tagName == "fileset")
5474                 {
5475                 if (!getFileSet(child, parent, fileSetDir, fileSet))
5476                     return false;
5477                 }
5478             else if (tagName == "flags")
5479                 {
5480                 if (!parent.getValue(child, flags))
5481                     return false;
5482                 flags = strip(flags);
5483                 }
5484             else if (tagName == "libs")
5485                 {
5486                 if (!parent.getValue(child, libs))
5487                     return false;
5488                 libs = strip(libs);
5489                 }
5490             }
5491         return true;
5492         }
5494 private:
5496     String command;
5497     String fileName;
5498     String flags;
5499     String libs;
5500     String fileSetDir;
5501     std::vector<String> fileSet;
5503 };
5507 /**
5508  * Create a named directory
5509  */
5510 class TaskMakeFile : public Task
5512 public:
5514     TaskMakeFile(MakeBase &par) : Task(par)
5515         { type = TASK_MAKEFILE; name = "makefile"; }
5517     virtual ~TaskMakeFile()
5518         {}
5520     virtual bool execute()
5521         {
5522         status("          : %s", fileName.c_str());
5523         String fullName = parent.resolve(fileName);
5524         if (!isNewerThan(parent.getURI().getPath(), fullName))
5525             {
5526             //trace("skipped <makefile>");
5527             return true;
5528             }
5529         //trace("fullName:%s", fullName.c_str());
5530         FILE *f = fopen(fullName.c_str(), "w");
5531         if (!f)
5532             {
5533             error("<makefile> could not open %s for writing : %s",
5534                 fullName.c_str(), strerror(errno));
5535             return false;
5536             }
5537         for (unsigned int i=0 ; i<text.size() ; i++)
5538             fputc(text[i], f);
5539         fclose(f);
5540         return true;
5541         }
5543     virtual bool parse(Element *elem)
5544         {
5545         if (!parent.getAttribute(elem, "file", fileName))
5546             return false;
5547         if (fileName.size() == 0)
5548             {
5549             error("<makefile> requires 'file=\"filename\"' attribute");
5550             return false;
5551             }
5552         if (!parent.getValue(elem, text))
5553             return false;
5554         text = leftJustify(text);
5555         //trace("dirname:%s", dirName.c_str());
5556         return true;
5557         }
5559 private:
5561     String fileName;
5562     String text;
5563 };
5567 /**
5568  * Create a named directory
5569  */
5570 class TaskMkDir : public Task
5572 public:
5574     TaskMkDir(MakeBase &par) : Task(par)
5575         { type = TASK_MKDIR; name = "mkdir"; }
5577     virtual ~TaskMkDir()
5578         {}
5580     virtual bool execute()
5581         {
5582         status("          : %s", dirName.c_str());
5583         String fullDir = parent.resolve(dirName);
5584         //trace("fullDir:%s", fullDir.c_str());
5585         if (!createDirectory(fullDir))
5586             return false;
5587         return true;
5588         }
5590     virtual bool parse(Element *elem)
5591         {
5592         if (!parent.getAttribute(elem, "dir", dirName))
5593             return false;
5594         if (dirName.size() == 0)
5595             {
5596             error("<mkdir> requires 'dir=\"dirname\"' attribute");
5597             return false;
5598             }
5599         //trace("dirname:%s", dirName.c_str());
5600         return true;
5601         }
5603 private:
5605     String dirName;
5606 };
5610 /**
5611  * Create a named directory
5612  */
5613 class TaskMsgFmt: public Task
5615 public:
5617     TaskMsgFmt(MakeBase &par) : Task(par)
5618          {
5619                  type = TASK_MSGFMT;
5620                  name = "msgfmt";
5621                  command = "msgfmt";
5622                  }
5624     virtual ~TaskMsgFmt()
5625         {}
5627     virtual bool execute()
5628         {
5629         //trace("msgfmt: %d", fileSet.size());
5630         for (unsigned int i=0 ; i<fileSet.size() ; i++)
5631             {
5632             String fileName = fileSet[i];
5633             if (getSuffix(fileName) != "po")
5634                 continue;
5635             String sourcePath;
5636                         if (fileSetDir.size()>0)
5637                             {
5638                             sourcePath.append(fileSetDir);
5639                 sourcePath.append("/");
5640                 }
5641             sourcePath.append(fileName);
5642             String fullSource = parent.resolve(sourcePath);
5644             String destPath;
5645                         if (toDirName.size()>0)
5646                             {
5647                             destPath.append(toDirName);
5648                 destPath.append("/");
5649                 }
5650             destPath.append(fileName);
5651             destPath[destPath.size()-2] = 'm';
5652             String fullDest = parent.resolve(destPath);
5654             if (!isNewerThan(fullSource, fullDest))
5655                 {
5656                 //trace("skip %s", fullSource.c_str());
5657                 continue;
5658                 }
5659                 
5660             String cmd = command;
5661             cmd.append(" ");
5662             cmd.append(fullSource);
5663             cmd.append(" -o ");
5664             cmd.append(fullDest);
5665             
5666             int pos = fullDest.find_last_of('/');
5667             if (pos>0)
5668                 {
5669                 String fullDestPath = fullDest.substr(0, pos);
5670                 if (!createDirectory(fullDestPath))
5671                     return false;
5672                 }
5676             String outString, errString;
5677             if (!executeCommand(cmd.c_str(), "", outString, errString))
5678                 {
5679                 error("<msgfmt> problem: %s", errString.c_str());
5680                 return false;
5681                 }
5682             }
5684         return true;
5685         }
5687     virtual bool parse(Element *elem)
5688         {
5689         if (!parent.getAttribute(elem, "todir", toDirName))
5690             return false;
5691             
5692         std::vector<Element *> children = elem->getChildren();
5693         for (unsigned int i=0 ; i<children.size() ; i++)
5694             {
5695             Element *child = children[i];
5696             String tagName = child->getName();
5697             if (tagName == "fileset")
5698                 {
5699                 if (!getFileSet(child, parent, fileSetDir, fileSet))
5700                     return false;
5701                 }
5702             }
5703         return true;
5704         }
5706 private:
5708     String command;
5709     String toDirName;
5710     String fileSetDir;
5711     std::vector<String> fileSet;
5713 };
5719 /**
5720  *  Process an archive to allow random access
5721  */
5722 class TaskRanlib : public Task
5724 public:
5726     TaskRanlib(MakeBase &par) : Task(par)
5727         { type = TASK_RANLIB; name = "ranlib"; }
5729     virtual ~TaskRanlib()
5730         {}
5732     virtual bool execute()
5733         {
5734         String fullName = parent.resolve(fileName);
5735         //trace("fullDir:%s", fullDir.c_str());
5736         String cmd = "ranlib ";
5737         cmd.append(fullName);
5738         String outbuf, errbuf;
5739         if (!executeCommand(cmd, "", outbuf, errbuf))
5740             return false;
5741         return true;
5742         }
5744     virtual bool parse(Element *elem)
5745         {
5746         if (!parent.getAttribute(elem, "file", fileName))
5747             return false;
5748         if (fileName.size() == 0)
5749             {
5750             error("<ranlib> requires 'file=\"fileNname\"' attribute");
5751             return false;
5752             }
5753         return true;
5754         }
5756 private:
5758     String fileName;
5759 };
5763 /**
5764  * Run the "ar" command to archive .o's into a .a
5765  */
5766 class TaskRC : public Task
5768 public:
5770     TaskRC(MakeBase &par) : Task(par)
5771         {
5772                 type = TASK_RC; name = "rc";
5773                 command = "windres -o";
5774                 }
5776     virtual ~TaskRC()
5777         {}
5779     virtual bool execute()
5780         {
5781         String fullFile = parent.resolve(fileName);
5782         String fullOut  = parent.resolve(outName);
5783         if (!isNewerThan(fullFile, fullOut))
5784             return true;
5785         String cmd = command;
5786         cmd.append(" ");
5787         cmd.append(fullOut);
5788         cmd.append(" ");
5789         cmd.append(flags);
5790         cmd.append(" ");
5791         cmd.append(fullFile);
5793         String outString, errString;
5794         if (!executeCommand(cmd.c_str(), "", outString, errString))
5795             {
5796             error("RC problem: %s", errString.c_str());
5797             return false;
5798             }
5799         return true;
5800         }
5802     virtual bool parse(Element *elem)
5803         {
5804         if (!parent.getAttribute(elem, "command", command))
5805             return false;
5806         if (!parent.getAttribute(elem, "file", fileName))
5807             return false;
5808         if (!parent.getAttribute(elem, "out", outName))
5809             return false;
5810         std::vector<Element *> children = elem->getChildren();
5811         for (unsigned int i=0 ; i<children.size() ; i++)
5812             {
5813             Element *child = children[i];
5814             String tagName = child->getName();
5815             if (tagName == "flags")
5816                 {
5817                 if (!parent.getValue(child, flags))
5818                     return false;
5819                 }
5820             }
5821         return true;
5822         }
5824 private:
5826     String command;
5827     String flags;
5828     String fileName;
5829     String outName;
5831 };
5835 /**
5836  * Strip an executable
5837  */
5838 class TaskStrip : public Task
5840 public:
5842     TaskStrip(MakeBase &par) : Task(par)
5843         { type = TASK_STRIP; name = "strip"; }
5845     virtual ~TaskStrip()
5846         {}
5848     virtual bool execute()
5849         {
5850         String fullName = parent.resolve(fileName);
5851         //trace("fullDir:%s", fullDir.c_str());
5852         String cmd = "strip ";
5853         cmd.append(fullName);
5855         String outbuf, errbuf;
5856         if (!executeCommand(cmd, "", outbuf, errbuf))
5857             return false;
5858         return true;
5859         }
5861     virtual bool parse(Element *elem)
5862         {
5863         if (!parent.getAttribute(elem, "file", fileName))
5864             return false;
5865         if (fileName.size() == 0)
5866             {
5867             error("<strip> requires 'file=\"fileNname\"' attribute");
5868             return false;
5869             }
5870         return true;
5871         }
5873 private:
5875     String fileName;
5876 };
5879 /**
5880  *
5881  */
5882 class TaskTstamp : public Task
5884 public:
5886     TaskTstamp(MakeBase &par) : Task(par)
5887         { type = TASK_TSTAMP; name = "tstamp"; }
5889     virtual ~TaskTstamp()
5890         {}
5892     virtual bool execute()
5893         {
5894         return true;
5895         }
5897     virtual bool parse(Element *elem)
5898         {
5899         trace("tstamp parse");
5900         return true;
5901         }
5902 };
5906 /**
5907  *
5908  */
5909 Task *Task::createTask(Element *elem)
5911     String tagName = elem->getName();
5912     //trace("task:%s", tagName.c_str());
5913     Task *task = NULL;
5914     if (tagName == "ar")
5915         task = new TaskAr(parent);
5916     else if (tagName == "cc")
5917         task = new TaskCC(parent);
5918     else if (tagName == "copy")
5919         task = new TaskCopy(parent);
5920     else if (tagName == "delete")
5921         task = new TaskDelete(parent);
5922     else if (tagName == "jar")
5923         task = new TaskJar(parent);
5924     else if (tagName == "javac")
5925         task = new TaskJavac(parent);
5926     else if (tagName == "link")
5927         task = new TaskLink(parent);
5928     else if (tagName == "makefile")
5929         task = new TaskMakeFile(parent);
5930     else if (tagName == "mkdir")
5931         task = new TaskMkDir(parent);
5932     else if (tagName == "msgfmt")
5933         task = new TaskMsgFmt(parent);
5934     else if (tagName == "ranlib")
5935         task = new TaskRanlib(parent);
5936     else if (tagName == "rc")
5937         task = new TaskRC(parent);
5938     else if (tagName == "strip")
5939         task = new TaskStrip(parent);
5940     else if (tagName == "tstamp")
5941         task = new TaskTstamp(parent);
5942     else
5943         {
5944         error("Unknown task '%s'", tagName.c_str());
5945         return NULL;
5946         }
5948     if (!task->parse(elem))
5949         {
5950         delete task;
5951         return NULL;
5952         }
5953     return task;
5958 //########################################################################
5959 //# T A R G E T
5960 //########################################################################
5962 /**
5963  *
5964  */
5965 class Target : public MakeBase
5968 public:
5970     /**
5971      *
5972      */
5973     Target(Make &par) : parent(par)
5974         { init(); }
5976     /**
5977      *
5978      */
5979     Target(const Target &other) : parent(other.parent)
5980         { init(); assign(other); }
5982     /**
5983      *
5984      */
5985     Target &operator=(const Target &other)
5986         { init(); assign(other); return *this; }
5988     /**
5989      *
5990      */
5991     virtual ~Target()
5992         { cleanup() ; }
5995     /**
5996      *
5997      */
5998     virtual Make &getParent()
5999         { return parent; }
6001     /**
6002      *
6003      */
6004     virtual String getName()
6005         { return name; }
6007     /**
6008      *
6009      */
6010     virtual void setName(const String &val)
6011         { name = val; }
6013     /**
6014      *
6015      */
6016     virtual String getDescription()
6017         { return description; }
6019     /**
6020      *
6021      */
6022     virtual void setDescription(const String &val)
6023         { description = val; }
6025     /**
6026      *
6027      */
6028     virtual void addDependency(const String &val)
6029         { deps.push_back(val); }
6031     /**
6032      *
6033      */
6034     virtual void parseDependencies(const String &val)
6035         { deps = tokenize(val, ", "); }
6037     /**
6038      *
6039      */
6040     virtual std::vector<String> &getDependencies()
6041         { return deps; }
6043     /**
6044      *
6045      */
6046     virtual String getIf()
6047         { return ifVar; }
6049     /**
6050      *
6051      */
6052     virtual void setIf(const String &val)
6053         { ifVar = val; }
6055     /**
6056      *
6057      */
6058     virtual String getUnless()
6059         { return unlessVar; }
6061     /**
6062      *
6063      */
6064     virtual void setUnless(const String &val)
6065         { unlessVar = val; }
6067     /**
6068      *
6069      */
6070     virtual void addTask(Task *val)
6071         { tasks.push_back(val); }
6073     /**
6074      *
6075      */
6076     virtual std::vector<Task *> &getTasks()
6077         { return tasks; }
6079 private:
6081     void init()
6082         {
6083         }
6085     void cleanup()
6086         {
6087         tasks.clear();
6088         }
6090     void assign(const Target &other)
6091         {
6092         //parent      = other.parent;
6093         name        = other.name;
6094         description = other.description;
6095         ifVar       = other.ifVar;
6096         unlessVar   = other.unlessVar;
6097         deps        = other.deps;
6098         tasks       = other.tasks;
6099         }
6101     Make &parent;
6103     String name;
6105     String description;
6107     String ifVar;
6109     String unlessVar;
6111     std::vector<String> deps;
6113     std::vector<Task *> tasks;
6115 };
6124 //########################################################################
6125 //# M A K E
6126 //########################################################################
6129 /**
6130  *
6131  */
6132 class Make : public MakeBase
6135 public:
6137     /**
6138      *
6139      */
6140     Make()
6141         { init(); }
6143     /**
6144      *
6145      */
6146     Make(const Make &other)
6147         { assign(other); }
6149     /**
6150      *
6151      */
6152     Make &operator=(const Make &other)
6153         { assign(other); return *this; }
6155     /**
6156      *
6157      */
6158     virtual ~Make()
6159         { cleanup(); }
6161     /**
6162      *
6163      */
6164     virtual std::map<String, Target> &getTargets()
6165         { return targets; }
6168     /**
6169      *
6170      */
6171     bool run();
6173     /**
6174      *
6175      */
6176     bool run(const String &target);
6180 private:
6182     /**
6183      *
6184      */
6185     void init();
6187     /**
6188      *
6189      */
6190     void cleanup();
6192     /**
6193      *
6194      */
6195     void assign(const Make &other);
6197     /**
6198      *
6199      */
6200     bool executeTask(Task &task);
6203     /**
6204      *
6205      */
6206     bool executeTarget(Target &target,
6207                  std::set<String> &targetsCompleted);
6210     /**
6211      *
6212      */
6213     bool execute();
6215     /**
6216      *
6217      */
6218     bool checkTargetDependencies(Target &prop,
6219                     std::vector<String> &depList);
6221     /**
6222      *
6223      */
6224     bool parsePropertyFile(const String &fileName,
6225                                const String &prefix);
6227     /**
6228      *
6229      */
6230     bool parseProperty(Element *elem);
6232     /**
6233      *
6234      */
6235     bool parseTask(Task &task, Element *elem);
6237     /**
6238      *
6239      */
6240     bool parseFile();
6242     /**
6243      *
6244      */
6245     std::vector<String> glob(const String &pattern);
6248     //###############
6249     //# Fields
6250     //###############
6252     String projectName;
6254     String currentTarget;
6256     String defaultTarget;
6258     String specifiedTarget;
6260     String baseDir;
6262     String description;
6263     
6264     String envAlias;
6266     //std::vector<Property> properties;
6267     
6268     std::map<String, Target> targets;
6270     std::vector<Task *> allTasks;
6273 };
6276 //########################################################################
6277 //# C L A S S  M A I N T E N A N C E
6278 //########################################################################
6280 /**
6281  *
6282  */
6283 void Make::init()
6285     uri             = "build.xml";
6286     projectName     = "";
6287     currentTarget   = "";
6288     defaultTarget   = "";
6289     specifiedTarget = "";
6290     baseDir         = "";
6291     description     = "";
6292     envAlias        = "";
6293     properties.clear();
6294     for (unsigned int i = 0 ; i < allTasks.size() ; i++)
6295         delete allTasks[i];
6296     allTasks.clear();
6301 /**
6302  *
6303  */
6304 void Make::cleanup()
6306     for (unsigned int i = 0 ; i < allTasks.size() ; i++)
6307         delete allTasks[i];
6308     allTasks.clear();
6313 /**
6314  *
6315  */
6316 void Make::assign(const Make &other)
6318     uri              = other.uri;
6319     projectName      = other.projectName;
6320     currentTarget    = other.currentTarget;
6321     defaultTarget    = other.defaultTarget;
6322     specifiedTarget  = other.specifiedTarget;
6323     baseDir          = other.baseDir;
6324     description      = other.description;
6325     properties       = other.properties;
6330 //########################################################################
6331 //# U T I L I T Y    T A S K S
6332 //########################################################################
6334 /**
6335  *  Perform a file globbing
6336  */
6337 std::vector<String> Make::glob(const String &pattern)
6339     std::vector<String> res;
6340     return res;
6344 //########################################################################
6345 //# P U B L I C    A P I
6346 //########################################################################
6350 /**
6351  *
6352  */
6353 bool Make::executeTarget(Target &target,
6354              std::set<String> &targetsCompleted)
6357     String name = target.getName();
6359     //First get any dependencies for this target
6360     std::vector<String> deps = target.getDependencies();
6361     for (unsigned int i=0 ; i<deps.size() ; i++)
6362         {
6363         String dep = deps[i];
6364         //Did we do it already?  Skip
6365         if (targetsCompleted.find(dep)!=targetsCompleted.end())
6366             continue;
6367             
6368         std::map<String, Target> &tgts =
6369                target.getParent().getTargets();
6370         std::map<String, Target>::iterator iter =
6371                tgts.find(dep);
6372         if (iter == tgts.end())
6373             {
6374             error("Target '%s' dependency '%s' not found",
6375                       name.c_str(),  dep.c_str());
6376             return false;
6377             }
6378         Target depTarget = iter->second;
6379         if (!executeTarget(depTarget, targetsCompleted))
6380             {
6381             return false;
6382             }
6383         }
6385     status("## Target : %s", name.c_str());
6387     //Now let's do the tasks
6388     std::vector<Task *> &tasks = target.getTasks();
6389     for (unsigned int i=0 ; i<tasks.size() ; i++)
6390         {
6391         Task *task = tasks[i];
6392         status("---- task : %s", task->getName().c_str());
6393         if (!task->execute())
6394             {
6395             return false;
6396             }
6397         }
6398         
6399     targetsCompleted.insert(name);
6400     
6401     return true;
6406 /**
6407  *  Main execute() method.  Start here and work
6408  *  up the dependency tree 
6409  */
6410 bool Make::execute()
6412     status("######## EXECUTE");
6414     //Determine initial target
6415     if (specifiedTarget.size()>0)
6416         {
6417         currentTarget = specifiedTarget;
6418         }
6419     else if (defaultTarget.size()>0)
6420         {
6421         currentTarget = defaultTarget;
6422         }
6423     else
6424         {
6425         error("execute: no specified or default target requested");
6426         return false;
6427         }
6429     std::map<String, Target>::iterator iter =
6430                    targets.find(currentTarget);
6431     if (iter == targets.end())
6432         {
6433         error("Initial target '%s' not found",
6434                          currentTarget.c_str());
6435         return false;
6436         }
6437         
6438     //Now run
6439     Target target = iter->second;
6440     std::set<String> targetsCompleted;
6441     if (!executeTarget(target, targetsCompleted))
6442         {
6443         return false;
6444         }
6446     status("######## EXECUTE COMPLETE");
6447     return true;
6453 /**
6454  *
6455  */
6456 bool Make::checkTargetDependencies(Target &target, 
6457                             std::vector<String> &depList)
6459     String tgtName = target.getName().c_str();
6460     depList.push_back(tgtName);
6462     std::vector<String> deps = target.getDependencies();
6463     for (unsigned int i=0 ; i<deps.size() ; i++)
6464         {
6465         String dep = deps[i];
6466         //First thing entered was the starting Target
6467         if (dep == depList[0])
6468             {
6469             error("Circular dependency '%s' found at '%s'",
6470                       dep.c_str(), tgtName.c_str());
6471             std::vector<String>::iterator diter;
6472             for (diter=depList.begin() ; diter!=depList.end() ; diter++)
6473                 {
6474                 error("  %s", diter->c_str());
6475                 }
6476             return false;
6477             }
6479         std::map<String, Target> &tgts =
6480                   target.getParent().getTargets();
6481         std::map<String, Target>::iterator titer = tgts.find(dep);
6482         if (titer == tgts.end())
6483             {
6484             error("Target '%s' dependency '%s' not found",
6485                       tgtName.c_str(), dep.c_str());
6486             return false;
6487             }
6488         if (!checkTargetDependencies(titer->second, depList))
6489             {
6490             return false;
6491             }
6492         }
6493     return true;
6500 static int getword(int pos, const String &inbuf, String &result)
6502     int p = pos;
6503     int len = (int)inbuf.size();
6504     String val;
6505     while (p < len)
6506         {
6507         char ch = inbuf[p];
6508         if (!isalnum(ch) && ch!='.' && ch!='_')
6509             break;
6510         val.push_back(ch);
6511         p++;
6512         }
6513     result = val;
6514     return p;
6520 /**
6521  *
6522  */
6523 bool Make::parsePropertyFile(const String &fileName,
6524                              const String &prefix)
6526     FILE *f = fopen(fileName.c_str(), "r");
6527     if (!f)
6528         {
6529         error("could not open property file %s", fileName.c_str());
6530         return false;
6531         }
6532     int linenr = 0;
6533     while (!feof(f))
6534         {
6535         char buf[256];
6536         if (!fgets(buf, 255, f))
6537             break;
6538         linenr++;
6539         String s = buf;
6540         s = trim(s);
6541         int len = s.size();
6542         if (len == 0)
6543             continue;
6544         if (s[0] == '#')
6545             continue;
6546         String key;
6547         String val;
6548         int p = 0;
6549         int p2 = getword(p, s, key);
6550         if (p2 <= p)
6551             {
6552             error("property file %s, line %d: expected keyword",
6553                                 fileName.c_str(), linenr);
6554                         return false;
6555                         }
6556                 if (prefix.size() > 0)
6557                     {
6558                     key.insert(0, prefix);
6559                     }
6561         //skip whitespace
6562                 for (p=p2 ; p<len ; p++)
6563                     if (!isspace(s[p]))
6564                         break;
6566         if (p>=len || s[p]!='=')
6567             {
6568             error("property file %s, line %d: expected '='",
6569                                 fileName.c_str(), linenr);
6570             return false;
6571             }
6572         p++;
6574         //skip whitespace
6575                 for ( ; p<len ; p++)
6576                     if (!isspace(s[p]))
6577                         break;
6579         /* This way expects a word after the =
6580                 p2 = getword(p, s, val);
6581         if (p2 <= p)
6582             {
6583             error("property file %s, line %d: expected value",
6584                                 fileName.c_str(), linenr);
6585                         return false;
6586                         }
6587                 */
6588         // This way gets the rest of the line after the =
6589                 if (p>=len)
6590             {
6591             error("property file %s, line %d: expected value",
6592                                 fileName.c_str(), linenr);
6593                         return false;
6594                         }
6595         val = s.substr(p);
6596                 if (key.size()==0 || val.size()==0)
6597                     continue;
6599         //trace("key:'%s' val:'%s'", key.c_str(), val.c_str());
6600             properties[key] = val;
6601         }
6602     fclose(f);
6603     return true;
6609 /**
6610  *
6611  */
6612 bool Make::parseProperty(Element *elem)
6614     std::vector<Attribute> &attrs = elem->getAttributes();
6615     for (unsigned int i=0 ; i<attrs.size() ; i++)
6616         {
6617         String attrName = attrs[i].getName();
6618         String attrVal  = attrs[i].getValue();
6620         if (attrName == "name")
6621             {
6622             String val;
6623                         if (!getAttribute(elem, "value", val))
6624                             return false;
6625             if (val.size() > 0)
6626                 {
6627                 properties[attrVal] = val;
6628                 continue;
6629                 }
6630             if (!getAttribute(elem, "location", val))
6631                 return false;
6632             if (val.size() > 0)
6633                 {
6634                 //TODO:  process a path relative to build.xml
6635                 properties[attrVal] = val;
6636                 continue;
6637                 }
6638             }
6639         else if (attrName == "file")
6640             {
6641             String prefix;
6642                         if (!getAttribute(elem, "prefix", prefix))
6643                             return false;
6644             if (prefix.size() > 0)
6645                 {
6646                 if (prefix[prefix.size()-1] != '.')
6647                     prefix.push_back('.');
6648                 }
6649             if (!parsePropertyFile(attrName, prefix))
6650                 return false;
6651             }
6652         else if (attrName == "environment")
6653             {
6654             if (envAlias.size() > 0)
6655                 {
6656                 error("environment property can only be set once");
6657                 return false;
6658                 }
6659             envAlias = attrVal;
6660             }
6661         }
6663     return true;
6669 /**
6670  *
6671  */
6672 bool Make::parseFile()
6674     status("######## PARSE");
6676     Parser parser;
6677     Element *root = parser.parseFile(uri.getNativePath());
6678     if (!root)
6679         {
6680         error("Could not open %s for reading",
6681                       uri.getNativePath().c_str());
6682         return false;
6683         }
6685     if (root->getChildren().size()==0 ||
6686         root->getChildren()[0]->getName()!="project")
6687         {
6688         error("Main xml element should be <project>");
6689         delete root;
6690         return false;
6691         }
6693     //########## Project attributes
6694     Element *project = root->getChildren()[0];
6695     String s = project->getAttribute("name");
6696     if (s.size() > 0)
6697         projectName = s;
6698     s = project->getAttribute("default");
6699     if (s.size() > 0)
6700         defaultTarget = s;
6701     s = project->getAttribute("basedir");
6702     if (s.size() > 0)
6703         baseDir = s;
6705     //######### PARSE MEMBERS
6706     std::vector<Element *> children = project->getChildren();
6707     for (unsigned int i=0 ; i<children.size() ; i++)
6708         {
6709         Element *elem = children[i];
6710         String tagName = elem->getName();
6712         //########## DESCRIPTION
6713         if (tagName == "description")
6714             {
6715             description = parser.trim(elem->getValue());
6716             }
6718         //######### PROPERTY
6719         else if (tagName == "property")
6720             {
6721             if (!parseProperty(elem))
6722                 return false;
6723             }
6725         //######### TARGET
6726         else if (tagName == "target")
6727             {
6728             String tname   = elem->getAttribute("name");
6729             String tdesc   = elem->getAttribute("description");
6730             String tdeps   = elem->getAttribute("depends");
6731             String tif     = elem->getAttribute("if");
6732             String tunless = elem->getAttribute("unless");
6733             Target target(*this);
6734             target.setName(tname);
6735             target.setDescription(tdesc);
6736             target.parseDependencies(tdeps);
6737             target.setIf(tif);
6738             target.setUnless(tunless);
6739             std::vector<Element *> telems = elem->getChildren();
6740             for (unsigned int i=0 ; i<telems.size() ; i++)
6741                 {
6742                 Element *telem = telems[i];
6743                 Task breeder(*this);
6744                 Task *task = breeder.createTask(telem);
6745                 if (!task)
6746                     return false;
6747                 allTasks.push_back(task);
6748                 target.addTask(task);
6749                 }
6751             //Check name
6752             if (tname.size() == 0)
6753                 {
6754                 error("no name for target");
6755                 return false;
6756                 }
6757             //Check for duplicate name
6758             if (targets.find(tname) != targets.end())
6759                 {
6760                 error("target '%s' already defined", tname.c_str());
6761                 return false;
6762                 }
6763             //more work than targets[tname]=target, but avoids default allocator
6764             targets.insert(std::make_pair<String, Target>(tname, target));
6765             }
6767         }
6769     std::map<String, Target>::iterator iter;
6770     for (iter = targets.begin() ; iter!= targets.end() ; iter++)
6771         {
6772         Target tgt = iter->second;
6773         std::vector<String> depList;
6774         if (!checkTargetDependencies(tgt, depList))
6775             {
6776             return false;
6777             }
6778         }
6781     delete root;
6782     status("######## PARSE COMPLETE");
6783     return true;
6787 /**
6788  *
6789  */
6790 bool Make::run()
6792     if (!parseFile())
6793         return false;
6794     if (!execute())
6795         return false;
6796     return true;
6800 /**
6801  *
6802  */
6803 bool Make::run(const String &target)
6805     status("##################################");
6806     status("#   BuildTool");
6807     status("#   version 0.2");
6808     status("##################################");
6809     specifiedTarget = target;
6810     if (!run())
6811         return false;
6812     status("##################################");
6813     status("#   BuildTool Completed");
6814     status("##################################");
6815     return true;
6824 }// namespace buildtool
6825 //########################################################################
6826 //# M A I N
6827 //########################################################################
6829 /**
6830  *  Format an error message in printf() style
6831  */
6832 static void error(char *fmt, ...)
6834     va_list ap;
6835     va_start(ap, fmt);
6836     fprintf(stderr, "BuildTool error: ");
6837     vfprintf(stderr, fmt, ap);
6838     fprintf(stderr, "\n");
6839     va_end(ap);
6843 /**
6844  * Compare a buffer with a key, for the length of the key
6845  */
6846 static bool sequ(const buildtool::String &buf, char *key)
6848     for (int i=0 ; key[i] ; i++)
6849         {
6850         if (key[i] != buf[i])
6851             return false;
6852         }        
6853     return true;
6856 /**
6857  * Parse the command-line args, get our options,
6858  * and run this thing
6859  */   
6860 static bool parseOptions(int argc, char **argv)
6862     if (argc < 1)
6863         {
6864         error("Cannot parse arguments");
6865         return false;
6866         }
6868     buildtool::String buildFile;
6869     buildtool::String target;
6871     //char *progName = argv[0];
6872     for (int i=1 ; i<argc ; i++)
6873         {
6874         buildtool::String arg = argv[i];
6875         if (sequ(arg, "--"))
6876             {
6877             if (sequ(arg, "--file=") && arg.size()>7)
6878                 {
6879                 buildFile = arg.substr(7, arg.size()-7);
6880                 }
6881             else
6882                 {
6883                 error("Unknown option:%s", arg.c_str());
6884                 return false;
6885                 }
6886             }
6887         else if (sequ(arg, "-"))
6888             {
6889             for (unsigned int p=1 ; p<arg.size() ; p++)
6890                 {
6891                 int ch = arg[p];
6892                 if (0)//put options here
6893                     {
6894                     }
6895                 else
6896                     {
6897                     error("Unknown option '%c'", ch);
6898                     return false;
6899                     }
6900                 }
6901             }
6902         else
6903             {
6904             target = arg;
6905             }
6906         }
6908     //We have the options.  Now execute them
6909     buildtool::Make make;
6910     if (buildFile.size() > 0)
6911         {
6912         make.setURI(buildFile);
6913         }
6914     if (!make.run(target))
6915         return false;
6917     return true;
6923 /*
6924 static bool runMake()
6926     buildtool::Make make;
6927     if (!make.run())
6928         return false;
6929     return true;
6933 static bool pkgConfigTest()
6935     buildtool::PkgConfig pkgConfig;
6936     if (!pkgConfig.readFile("gtk+-2.0.pc"))
6937         return false;
6938     return true;
6943 static bool depTest()
6945     buildtool::DepTool deptool;
6946     deptool.setSourceDirectory("/dev/ink/inkscape/src");
6947     if (!deptool.generateDependencies("build.dep"))
6948         return false;
6949     std::vector<buildtool::DepRec> res =
6950                deptool.loadDepFile("build.dep");
6951         if (res.size() == 0)
6952         return false;
6953     return true;
6956 static bool popenTest()
6958     buildtool::Make make;
6959     buildtool::String out, err;
6960         bool ret = make.executeCommand("gcc xx.cpp", "", out, err);
6961     printf("Popen test:%d '%s' '%s'\n", ret, out.c_str(), err.c_str());
6962     return true;
6966 static bool propFileTest()
6968     buildtool::Make make;
6969     make.parsePropertyFile("test.prop", "test.");
6970     return true;
6972 */
6974 int main(int argc, char **argv)
6977     if (!parseOptions(argc, argv))
6978         return 1;
6979     /*
6980     if (!popenTest())
6981         return 1;
6983     if (!depTest())
6984         return 1;
6985     if (!propFileTest())
6986         return 1;
6987     if (runMake())
6988         return 1;
6989     */
6990     return 0;
6994 //########################################################################
6995 //# E N D 
6996 //########################################################################