Code

fix circ dep check
[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 /*
35 */
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      *  remove leading and trailing whitespace from string
1995      */
1996     String trim(const String &s);
1998     /**
1999      * Return the native format of the canonical
2000      * path which we store
2001      */
2002     String getNativePath(const String &path);
2004     /**
2005      * Execute a shell command.  Outbuf is a ref to a string
2006      * to catch the result.     
2007      */      
2008     bool executeCommand(const String &call,
2009                             const String &inbuf,
2010                                                 String &outbuf,
2011                                                 String &errbuf);
2012     /**
2013      * List all directories in a given base and starting directory
2014      * It is usually called like:
2015      *           bool ret = listDirectories("src", "", result);    
2016      */      
2017     bool listDirectories(const String &baseName,
2018                          const String &dirname,
2019                          std::vector<String> &res);
2021     /**
2022      * Find all files in the named directory whose short names (no path) match
2023      * the given regex pattern     
2024      */      
2025     bool listFiles(const String &baseName,
2026                    const String &dirname,
2027                    std::vector<String> &excludes,
2028                    std::vector<String> &res);
2030     /**
2031      * Parse a <patternset>
2032      */  
2033     bool getPatternSet(Element *elem,
2034                        MakeBase &propRef,
2035                                            std::vector<String> &includes,
2036                                            std::vector<String> &excludes);
2038     /**
2039      * Parse a <fileset> entry, and determine which files
2040      * should be included
2041      */  
2042     bool getFileSet(Element *elem,
2043                     MakeBase &propRef,
2044                     String &dir,
2045                                         std::vector<String> &result);
2047     /**
2048      * Return this object's property list
2049      */
2050     virtual std::map<String, String> &getProperties()
2051         { return properties; }
2053     /**
2054      * Return a named property if found, else a null string
2055      */
2056     virtual String getProperty(const String &name)
2057         {
2058         String val;
2059         std::map<String, String>::iterator iter;
2060         iter = properties.find(name);
2061         if (iter != properties.end())
2062             val = iter->second;
2063         return val;
2064         }
2067     std::map<String, String> properties;
2069     /**
2070      * Turn 'true' and 'false' into boolean values
2071      */             
2072     bool getBool(const String &str, bool &val);
2074     /**
2075      * Create a directory, making intermediate dirs
2076      * if necessary
2077      */                     
2078     bool createDirectory(const String &dirname);
2080     /**
2081      * Delete a directory and its children if desired
2082      */
2083     bool removeDirectory(const String &dirName);
2085     /**
2086      * Copy a file from one name to another. Perform only if needed
2087      */ 
2088     bool copyFile(const String &srcFile, const String &destFile);
2090     /**
2091      * Tests is the modification date of fileA is newer than fileB
2092      */ 
2093     bool isNewerThan(const String &fileA, const String &fileB);
2095 private:
2097     /**
2098      * replace variable refs like ${a} with their values
2099      */      
2100     bool getSubstitutions(const String &s, String &result);
2104 };
2109 /**
2110  *  Print a printf()-like formatted error message
2111  */
2112 void MakeBase::error(char *fmt, ...)
2114     va_list args;
2115     va_start(args,fmt);
2116     fprintf(stderr, "Make error: ");
2117     vfprintf(stderr, fmt, args);
2118     fprintf(stderr, "\n");
2119     va_end(args) ;
2124 /**
2125  *  Print a printf()-like formatted trace message
2126  */
2127 void MakeBase::status(char *fmt, ...)
2129     va_list args;
2130     va_start(args,fmt);
2131     //fprintf(stdout, " ");
2132     vfprintf(stdout, fmt, args);
2133     fprintf(stdout, "\n");
2134     va_end(args) ;
2139 /**
2140  *  Resolve another path relative to this one
2141  */
2142 String MakeBase::resolve(const String &otherPath)
2144     URI otherURI(otherPath);
2145     URI fullURI = uri.resolve(otherURI);
2146     String ret = fullURI.toString();
2147     return ret;
2151 /**
2152  *  Print a printf()-like formatted trace message
2153  */
2154 void MakeBase::trace(char *fmt, ...)
2156     va_list args;
2157     va_start(args,fmt);
2158     fprintf(stdout, "Make: ");
2159     vfprintf(stdout, fmt, args);
2160     fprintf(stdout, "\n");
2161     va_end(args) ;
2165 /**
2166  *  Return the suffix, if any, of a file name
2167  */
2168 String MakeBase::getSuffix(const String &fname)
2170     if (fname.size() < 2)
2171         return "";
2172     unsigned int pos = fname.find_last_of('.');
2173     if (pos == fname.npos)
2174         return "";
2175     pos++;
2176     String res = fname.substr(pos, fname.size()-pos);
2177     //trace("suffix:%s", res.c_str()); 
2178     return res;
2183 /**
2184  * Break up a string into substrings delimited the characters
2185  * in delimiters.  Null-length substrings are ignored
2186  */  
2187 std::vector<String> MakeBase::tokenize(const String &str,
2188                                 const String &delimiters)
2191     std::vector<String> res;
2192     char *del = (char *)delimiters.c_str();
2193     String dmp;
2194     for (unsigned int i=0 ; i<str.size() ; i++)
2195         {
2196         char ch = str[i];
2197         char *p = (char *)0;
2198         for (p=del ; *p ; p++)
2199             if (*p == ch)
2200                 break;
2201         if (*p)
2202             {
2203             if (dmp.size() > 0)
2204                 {
2205                 res.push_back(dmp);
2206                 dmp.clear();
2207                 }
2208             }
2209         else
2210             {
2211             dmp.push_back(ch);
2212             }
2213         }
2214     //Add tail
2215     if (dmp.size() > 0)
2216         {
2217         res.push_back(dmp);
2218         dmp.clear();
2219         }
2221     return res;
2226 /**
2227  *  Removes whitespace from beginning and end of a string
2228  */
2229 String MakeBase::trim(const String &s)
2231     if (s.size() < 1)
2232         return s;
2233     
2234     //Find first non-ws char
2235     unsigned int begin = 0;
2236     for ( ; begin < s.size() ; begin++)
2237         {
2238         if (!isspace(s[begin]))
2239             break;
2240         }
2242     //Find first non-ws char, going in reverse
2243     unsigned int end = s.size() - 1;
2244     for ( ; end > begin ; end--)
2245         {
2246         if (!isspace(s[end]))
2247             break;
2248         }
2249     //trace("begin:%d  end:%d", begin, end);
2251     String res = s.substr(begin, end-begin+1);
2252     return res;
2255 /**
2256  * Return the native format of the canonical
2257  * path which we store
2258  */
2259 String MakeBase::getNativePath(const String &path)
2261 #ifdef __WIN32__
2262     String npath;
2263     unsigned int firstChar = 0;
2264     if (path.size() >= 3)
2265         {
2266         if (path[0] == '/' &&
2267             isalpha(path[1]) &&
2268             path[2] == ':')
2269             firstChar++;
2270         }
2271     for (unsigned int i=firstChar ; i<path.size() ; i++)
2272         {
2273         char ch = path[i];
2274         if (ch == '/')
2275             npath.push_back('\\');
2276         else
2277             npath.push_back(ch);
2278         }
2279     return npath;
2280 #else
2281     return path;
2282 #endif
2286 #ifdef __WIN32__
2287 #include <tchar.h>
2289 static String win32LastError()
2292     DWORD dw = GetLastError(); 
2294     LPVOID str;
2295     FormatMessage(
2296         FORMAT_MESSAGE_ALLOCATE_BUFFER | 
2297         FORMAT_MESSAGE_FROM_SYSTEM,
2298         NULL,
2299         dw,
2300         0,
2301         (LPTSTR) &str,
2302         0, NULL );
2303     LPTSTR p = _tcschr((const char *)str, _T('\r'));
2304     if(p != NULL)
2305         { // lose CRLF
2306         *p = _T('\0');
2307         }
2308     String ret = (char *)str;
2309     LocalFree(str);
2311     return ret;
2313 #endif
2317 /**
2318  * Execute a system call, using pipes to send data to the
2319  * program's stdin,  and reading stdout and stderr.
2320  */
2321 bool MakeBase::executeCommand(const String &command,
2322                               const String &inbuf,
2323                                                           String &outbuf,
2324                                                           String &errbuf)
2327     status("============ cmd ============\n%s\n=============================",
2328                     command.c_str());
2330 #ifdef __WIN32__
2332     /*
2333     I really hate having win32 code in this program, but the
2334     read buffer in command.com and cmd.exe are just too small
2335     for the large commands we need for compiling and linking.
2336     */
2338     bool ret = true;
2340     //# Allocate a separate buffer for safety
2341     char *paramBuf = new char[command.size() + 1];
2342     if (!paramBuf)
2343        {
2344        error("executeCommand cannot allocate command buffer");
2345            return false;
2346        }
2347     strcpy(paramBuf, (char *)command.c_str());
2349     //# Create pipes
2350     SECURITY_ATTRIBUTES saAttr; 
2351     saAttr.nLength = sizeof(SECURITY_ATTRIBUTES); 
2352     saAttr.bInheritHandle = TRUE; 
2353     saAttr.lpSecurityDescriptor = NULL; 
2354     HANDLE stdinRead,  stdinWrite;
2355     HANDLE stdoutRead, stdoutWrite;
2356     HANDLE stderrRead, stderrWrite;
2357     if (!CreatePipe(&stdinRead, &stdinWrite, &saAttr, 0))
2358             {
2359                 error("executeProgram: could not create pipe");
2360         delete[] paramBuf;
2361                 return false;
2362                 } 
2363     SetHandleInformation(stdinWrite, HANDLE_FLAG_INHERIT, 0);
2364         if (!CreatePipe(&stdoutRead, &stdoutWrite, &saAttr, 0))
2365             {
2366                 error("executeProgram: could not create pipe");
2367         delete[] paramBuf;
2368                 return false;
2369                 } 
2370     SetHandleInformation(stdoutRead, HANDLE_FLAG_INHERIT, 0);
2371         if (!CreatePipe(&stderrRead, &stderrWrite, &saAttr, 0))
2372             {
2373                 error("executeProgram: could not create pipe");
2374         delete[] paramBuf;
2375                 return false;
2376                 } 
2377     SetHandleInformation(stderrRead, HANDLE_FLAG_INHERIT, 0);
2379     // Create the process
2380     STARTUPINFO siStartupInfo;
2381     PROCESS_INFORMATION piProcessInfo;
2382     memset(&siStartupInfo, 0, sizeof(siStartupInfo));
2383     memset(&piProcessInfo, 0, sizeof(piProcessInfo));
2384     siStartupInfo.cb = sizeof(siStartupInfo);
2385     siStartupInfo.hStdError   =  stderrWrite;
2386     siStartupInfo.hStdOutput  =  stdoutWrite;
2387     siStartupInfo.hStdInput   =  stdinRead;
2388     siStartupInfo.dwFlags    |=  STARTF_USESTDHANDLES;
2389    
2390     if (!CreateProcess(NULL, paramBuf, NULL, NULL, true,
2391                 0, NULL, NULL, &siStartupInfo,
2392                 &piProcessInfo))
2393         {
2394         error("executeCommand : could not create process : %s",
2395                             win32LastError().c_str());
2396         ret = false;
2397         }
2399     DWORD bytesWritten;
2400     if (inbuf.size()>0 &&
2401         !WriteFile(stdinWrite, inbuf.c_str(), inbuf.size(), 
2402                &bytesWritten, NULL))
2403         {
2404         error("executeCommand: could not write to pipe");
2405                 return false;
2406                 }       
2407     if (!CloseHandle(stdinWrite))
2408             {           
2409         error("executeCommand: could not close write pipe");
2410                 return false;
2411                 }
2412     if (!CloseHandle(stdoutWrite))
2413             {
2414         error("executeCommand: could not close read pipe");
2415                 return false;
2416                 }
2417     if (!CloseHandle(stderrWrite))
2418             {
2419         error("executeCommand: could not close read pipe");
2420                 return false;
2421                 }
2422         while (true)
2423         {
2424         //trace("## stderr");
2425         DWORD avail;
2426         if (!PeekNamedPipe(stderrRead, NULL, 0, NULL, &avail, NULL))
2427             break;
2428         if (avail > 0)
2429             {
2430             DWORD bytesRead = 0;
2431             char readBuf[1025];
2432             if (avail>1024) avail = 1024;
2433             if (!ReadFile(stderrRead, readBuf, avail, &bytesRead, NULL)
2434                 || bytesRead == 0)
2435                 {
2436                 break;
2437                 }
2438             for (unsigned int i=0 ; i<bytesRead ; i++)
2439                 errbuf.push_back(readBuf[i]);
2440             }
2441         //trace("## stdout");
2442         if (!PeekNamedPipe(stdoutRead, NULL, 0, NULL, &avail, NULL))
2443             break;
2444         if (avail > 0)
2445             {
2446             DWORD bytesRead = 0;
2447             char readBuf[1025];
2448             if (avail>1024) avail = 1024;
2449             if (!ReadFile(stdoutRead, readBuf, avail, &bytesRead, NULL)
2450                 || bytesRead==0)
2451                 {
2452                 break;
2453                 }
2454             for (unsigned int i=0 ; i<bytesRead ; i++)
2455                 outbuf.push_back(readBuf[i]);
2456             }
2457                 DWORD exitCode;
2458         GetExitCodeProcess(piProcessInfo.hProcess, &exitCode);
2459         if (exitCode != STILL_ACTIVE)
2460             break;
2461         Sleep(100);
2462         }       
2463     //trace("outbuf:%s", outbuf.c_str());
2464     if (!CloseHandle(stdoutRead))
2465         {
2466         error("executeCommand: could not close read pipe");
2467         return false;
2468         }
2469     if (!CloseHandle(stderrRead))
2470         {
2471         error("executeCommand: could not close read pipe");
2472         return false;
2473         }
2475     DWORD exitCode;
2476     GetExitCodeProcess(piProcessInfo.hProcess, &exitCode);
2477     //trace("exit code:%d", exitCode);
2478     if (exitCode != 0)
2479         {
2480         ret = false;
2481         }
2482     
2483     // Clean up
2484     CloseHandle(piProcessInfo.hProcess);
2485     CloseHandle(piProcessInfo.hThread);
2488     return ret;
2490 #else //do it unix-style
2492     String s;
2493     FILE *f = popen(command.c_str(), "r");
2494     int errnum = 0;
2495     if (f)
2496         {
2497         while (true)
2498             {
2499             int ch = fgetc(f);
2500             if (ch < 0)
2501                 break;
2502             s.push_back((char)ch);
2503             }
2504         errnum = pclose(f);
2505         }
2506         outbuf = s;
2507         if (errnum < 0)
2508             {
2509             error("exec of command '%s' failed : %s",
2510                      command.c_str(), strerror(errno));
2511             return false;
2512             }
2513         else
2514             return true;
2516 #endif
2517
2522 bool MakeBase::listDirectories(const String &baseName,
2523                               const String &dirName,
2524                               std::vector<String> &res)
2526     res.push_back(dirName);
2527     String fullPath = baseName;
2528     if (dirName.size()>0)
2529         {
2530         fullPath.append("/");
2531         fullPath.append(dirName);
2532         }
2533     DIR *dir = opendir(fullPath.c_str());
2534     while (true)
2535         {
2536         struct dirent *de = readdir(dir);
2537         if (!de)
2538             break;
2540         //Get the directory member name
2541         String s = de->d_name;
2542         if (s.size() == 0 || s[0] == '.')
2543             continue;
2544         String childName = dirName;
2545         childName.append("/");
2546         childName.append(s);
2548         String fullChildPath = baseName;
2549         fullChildPath.append("/");
2550         fullChildPath.append(childName);
2551         struct stat finfo;
2552         String childNative = getNativePath(fullChildPath);
2553         if (stat(childNative.c_str(), &finfo)<0)
2554             {
2555             error("cannot stat file:%s", childNative.c_str());
2556             }
2557         else if (S_ISDIR(finfo.st_mode))
2558             {
2559             //trace("directory: %s", childName.c_str());
2560             if (!listDirectories(baseName, childName, res))
2561                 return false;
2562             }
2563         }
2564     closedir(dir);
2566     return true;
2570 bool MakeBase::listFiles(const String &baseDir,
2571                          const String &dirName,
2572                          std::vector<String> &excludes,
2573                          std::vector<String> &res)
2575     String fullDir = baseDir;
2576     if (dirName.size()>0)
2577         {
2578         fullDir.append("/");
2579         fullDir.append(dirName);
2580         }
2581     String dirNative = getNativePath(fullDir);
2583     std::vector<String> subdirs;
2584     DIR *dir = opendir(dirNative.c_str());
2585     while (true)
2586         {
2587         struct dirent *de = readdir(dir);
2588         if (!de)
2589             break;
2591         //Get the directory member name
2592         String s = de->d_name;
2593         if (s.size() == 0 || s[0] == '.')
2594             continue;
2595         String childName;
2596         if (dirName.size()>0)
2597             {
2598             childName.append(dirName);
2599             childName.append("/");
2600             }
2601         childName.append(s);
2602         String fullChild = baseDir;
2603         fullChild.append("/");
2604         fullChild.append(childName);
2605         
2606         if (std::find(excludes.begin(), excludes.end(), childName)
2607                         != excludes.end())
2608             {
2609             //trace("EXCLUDED:%s", childName.c_str());
2610             continue;
2611             }
2613         struct stat finfo;
2614         String nativeName = getNativePath(fullChild);
2615         if (stat(nativeName.c_str(), &finfo)<0)
2616             {
2617             error("cannot stat file:%s", childName.c_str());
2618             return false;
2619             }
2620         else if (S_ISDIR(finfo.st_mode))
2621             {
2622             //trace("directory: %s", childName.c_str());
2623             if (!listFiles(baseDir, childName, excludes, res))
2624                 return false;
2625             }
2626         else if (!S_ISREG(finfo.st_mode))
2627             {
2628             trace("not regular: %s", childName.c_str());
2629             }
2630         else
2631             {
2632             res.push_back(childName);
2633             }
2634         }
2635     closedir(dir);
2637     return true;
2647 bool MakeBase::getSubstitutions(const String &str, String &result)
2649     String s = trim(str);
2650     int len = (int)s.size();
2651     String val;
2652     for (int i=0 ; i<len ; i++)
2653         {
2654         char ch = s[i];
2655         if (ch == '$' && s[i+1] == '{')
2656                     {
2657             String varname;
2658                     int j = i+2;
2659                     for ( ; j<len ; j++)
2660                         {
2661                         ch = s[j];
2662                         if (ch == '$' && s[j+1] == '{')
2663                             {
2664                             error("attribute %s cannot have nested variable references",
2665                                    s.c_str());
2666                             return false;
2667                             }
2668                         else if (ch == '}')
2669                             {
2670                             std::map<String, String>::iterator iter;
2671                             iter = properties.find(trim(varname));
2672                             if (iter != properties.end())
2673                                 {
2674                                 val.append(iter->second);
2675                                 }
2676                             else
2677                                 {
2678                                 error("property ${%s} not found", varname.c_str());
2679                                 return false;
2680                                 }
2681                             break;
2682                             }
2683                         else
2684                             {
2685                             varname.push_back(ch);
2686                             }
2687                         }
2688                     i = j;
2689                         }
2690                 else
2691                     {
2692                     val.push_back(ch);
2693                     }
2694         }
2695     result = val;
2696     return true;
2700 bool MakeBase::getAttribute(Element *elem, const String &name,
2701                                     String &result)
2703     String s = elem->getAttribute(name);
2704     return getSubstitutions(s, result);
2708 bool MakeBase::getValue(Element *elem, String &result)
2710     String s = elem->getValue();
2711     int len = s.size();
2712     //Replace all runs of whitespace with a single space
2713     String stripped; 
2714     for (int i = 0 ; i<len ; i++)
2715         {
2716         char ch = s[i];
2717         if (isspace(ch))
2718             {
2719             stripped.push_back(' ');
2720             for ( ; i<len ; i++)
2721                 {
2722                 ch = s[i];
2723                 if (!isspace(ch))
2724                     {
2725                     stripped.push_back(ch);
2726                     break;
2727                     }
2728                 }
2729             }
2730         else
2731             {
2732             stripped.push_back(ch);
2733             }
2734         }
2735     return getSubstitutions(stripped, result);
2739 /**
2740  * Turn 'true' and 'false' into boolean values
2741  */                 
2742 bool MakeBase::getBool(const String &str, bool &val)
2744     if (str == "true")
2745         val = true;
2746     else if (str == "false")
2747         val = false;
2748     else
2749         {
2750         error("expected 'true' or 'false'.  found '%s'", str.c_str());
2751         return false;
2752         }
2753     return true;
2759 /**
2760  * Parse a <patternset> entry
2761  */  
2762 bool MakeBase::getPatternSet(Element *elem,
2763                           MakeBase &propRef,
2764                                                   std::vector<String> &includes,
2765                                                   std::vector<String> &excludes
2766                                                   )
2768     std::vector<Element *> children  = elem->getChildren();
2769     for (unsigned int i=0 ; i<children.size() ; i++)
2770         {
2771         Element *child = children[i];
2772         String tagName = child->getName();
2773         if (tagName == "exclude")
2774             {
2775             String fname;
2776                         if (!propRef.getAttribute(child, "name", fname))
2777                             return false;
2778             //trace("EXCLUDE: %s", fname.c_str());
2779             excludes.push_back(fname);
2780             }
2781         else if (tagName == "include")
2782             {
2783             String fname;
2784                         if (!propRef.getAttribute(child, "name", fname))
2785                             return false;
2786             //trace("INCLUDE: %s", fname.c_str());
2787             includes.push_back(fname);
2788             }
2789         }
2791     return true;
2797 /**
2798  * Parse a <fileset> entry, and determine which files
2799  * should be included
2800  */  
2801 bool MakeBase::getFileSet(Element *elem,
2802                           MakeBase &propRef,
2803                                                   String &dir,
2804                                                   std::vector<String> &result)
2806     String name = elem->getName();
2807     if (name != "fileset")
2808         {
2809         error("expected <fileset>");
2810         return false;
2811         }
2814     std::vector<String> includes;
2815     std::vector<String> excludes;
2817     //A fileset has one implied patternset
2818     if (!getPatternSet(elem, propRef, includes, excludes))
2819         {
2820         return false;
2821         }
2822     //Look for child tags, including more patternsets
2823     std::vector<Element *> children  = elem->getChildren();
2824     for (unsigned int i=0 ; i<children.size() ; i++)
2825         {
2826         Element *child = children[i];
2827         String tagName = child->getName();
2828         if (tagName == "patternset")
2829             {
2830             if (!getPatternSet(child, propRef, includes, excludes))
2831                 {
2832                 return false;
2833                 }
2834             }
2835         }
2837     //Now do the stuff
2838     //Get the base directory for reading file names
2839     if (!propRef.getAttribute(elem, "dir", dir))
2840         return false;
2842     std::vector<String> fileList;
2843     if (dir.size() > 0)
2844         {
2845         String baseDir = propRef.resolve(dir);
2846             if (!listFiles(baseDir, "", excludes, fileList))
2847                 return false;
2848             }
2849         
2850         std::vector<String>::iterator iter;
2851     for (iter=includes.begin() ; iter!=includes.end() ; iter++)
2852         {
2853         String fname = *iter;
2854         fileList.push_back(fname);
2855         }
2856         
2857         result = fileList;
2858         
2859         /*
2860         for (unsigned int i=0 ; i<result.size() ; i++)
2861             {
2862             trace("RES:%s", result[i].c_str());
2863             }
2864     */
2866     std::sort(fileList.begin(), fileList.end());
2867     
2868     return true;
2873 /**
2874  * Create a directory, making intermediate dirs
2875  * if necessary
2876  */                         
2877 bool MakeBase::createDirectory(const String &dirname)
2879     //trace("## createDirectory: %s", dirname.c_str());
2880     //## first check if it exists
2881     struct stat finfo;
2882     String nativeDir = getNativePath(dirname);
2883     char *cnative = (char *) nativeDir.c_str();
2884     if (stat(dirname.c_str(), &finfo)==0)
2885         {
2886         if (!S_ISDIR(finfo.st_mode))
2887             {
2888             error("mkdir: file %s exists but is not a directory",
2889                               cnative);
2890             return false;
2891             }
2892         else //exists
2893             {
2894             return true;
2895             }
2896         }
2898     //## 2: pull off the last path segment, if any,
2899     //## to make the dir 'above' this one, if necessary
2900     unsigned int pos = dirname.find_last_of('/');
2901     if (pos != dirname.npos)
2902         {
2903         String subpath = dirname.substr(0, pos);
2904         if (!createDirectory(subpath))
2905             return false;
2906         }
2907         
2908     //## 3: now make
2909     if (mkdir(cnative)<0)
2910         {
2911         error("cannot make directory %s", cnative);
2912         return false;
2913         }
2914         
2915     return true;
2919 /**
2920  * Remove a directory recursively
2921  */ 
2922 bool MakeBase::removeDirectory(const String &dirName)
2924     char *dname = (char *)dirName.c_str();
2926     DIR *dir = opendir(dname);
2927     if (!dir)
2928         {
2929         //# Let this fail nicely.
2930         return true;
2931         //error("error opening directory %s : %s", dname, strerror(errno));
2932         //return false;
2933         }
2934     
2935     while (true)
2936         {
2937         struct dirent *de = readdir(dir);
2938         if (!de)
2939             break;
2941         //Get the directory member name
2942         String s = de->d_name;
2943         if (s.size() == 0 || s[0] == '.')
2944             continue;
2945         String childName;
2946         if (dirName.size() > 0)
2947             {
2948             childName.append(dirName);
2949             childName.append("/");
2950             }
2951         childName.append(s);
2954         struct stat finfo;
2955         String childNative = getNativePath(childName);
2956         char *cnative = (char *)childNative.c_str();
2957         if (stat(cnative, &finfo)<0)
2958             {
2959             error("cannot stat file:%s", cnative);
2960             }
2961         else if (S_ISDIR(finfo.st_mode))
2962             {
2963             //trace("DEL dir: %s", childName.c_str());
2964                         if (!removeDirectory(childName))
2965                     {
2966                             return false;
2967                             }
2968             }
2969         else if (!S_ISREG(finfo.st_mode))
2970             {
2971             trace("not regular: %s", cnative);
2972             }
2973         else
2974             {
2975             //trace("DEL file: %s", childName.c_str());
2976             if (remove(cnative)<0)
2977                 {
2978                 error("error deleting %s : %s",
2979                                      cnative, strerror(errno));
2980                                 return false;
2981                                 }
2982             }
2983         }
2984     closedir(dir);
2986     //Now delete the directory
2987     String native = getNativePath(dirName);
2988     if (rmdir(native.c_str())<0)
2989         {
2990         error("could not delete directory %s : %s",
2991             native.c_str() , strerror(errno));
2992         return false;
2993         }
2995     return true;
2996     
3000 /**
3001  * Copy a file from one name to another. Perform only if needed
3002  */ 
3003 bool MakeBase::copyFile(const String &srcFile, const String &destFile)
3005     //# 1 Check up-to-date times
3006     String srcNative = getNativePath(srcFile);
3007     struct stat srcinfo;
3008     if (stat(srcNative.c_str(), &srcinfo)<0)
3009         {
3010         error("source file %s for copy does not exist",
3011                          srcNative.c_str());
3012         return false;
3013         }
3015     String destNative = getNativePath(destFile);
3016     struct stat destinfo;
3017     if (stat(destNative.c_str(), &destinfo)==0)
3018         {
3019         if (destinfo.st_mtime >= srcinfo.st_mtime)
3020             return true;
3021         }
3022         
3023     //# 2 prepare a destination directory if necessary
3024     unsigned int pos = destFile.find_last_of('/');
3025     if (pos != destFile.npos)
3026         {
3027         String subpath = destFile.substr(0, pos);
3028         if (!createDirectory(subpath))
3029             return false;
3030         }
3032     //# 3 do the data copy
3033     FILE *srcf = fopen(srcNative.c_str(), "rb");
3034     if (!srcf)
3035         {
3036         error("copyFile cannot open '%s' for reading", srcNative.c_str());
3037         return false;
3038         }
3039     FILE *destf = fopen(destNative.c_str(), "wb");
3040     if (!destf)
3041         {
3042         error("copyFile cannot open %s for writing", srcNative.c_str());
3043         return false;
3044         }
3046     while (!feof(srcf))
3047         {
3048         int ch = fgetc(srcf);
3049         if (ch<0)
3050             break;
3051         fputc(ch, destf);
3052         }
3054     fclose(destf);
3055     fclose(srcf);
3059     return true;
3064 /**
3065  * Tests is the modification of fileA is newer than fileB
3066  */ 
3067 bool MakeBase::isNewerThan(const String &fileA, const String &fileB)
3069     //trace("isNewerThan:'%s' , '%s'", fileA.c_str(), fileB.c_str());
3070     String nativeA = getNativePath(fileA);
3071     struct stat infoA;
3072     //IF source does not exist, NOT newer
3073     if (stat(nativeA.c_str(), &infoA)<0)
3074         {
3075                 return false;
3076                 }
3078     String nativeB = getNativePath(fileB);
3079     struct stat infoB;
3080     //IF dest does not exist, YES, newer
3081     if (stat(nativeB.c_str(), &infoB)<0)
3082         {
3083                 return true;
3084                 }
3086     //check the actual times
3087     if (infoA.st_mtime > infoB.st_mtime)
3088         {
3089                 return true;
3090                 }
3092     return false;
3096 //########################################################################
3097 //# P K G    C O N F I G
3098 //########################################################################
3100 /**
3101  *
3102  */
3103 class PkgConfig : public MakeBase
3106 public:
3108     /**
3109      *
3110      */
3111     PkgConfig()
3112         { init(); }
3114     /**
3115      *
3116      */
3117     PkgConfig(const String &namearg)
3118         { init(); name = namearg; }
3120     /**
3121      *
3122      */
3123     PkgConfig(const PkgConfig &other)
3124         { assign(other); }
3126     /**
3127      *
3128      */
3129     PkgConfig &operator=(const PkgConfig &other)
3130         { assign(other); return *this; }
3132     /**
3133      *
3134      */
3135     virtual ~PkgConfig()
3136         { }
3138     /**
3139      *
3140      */
3141     virtual String getName()
3142         { return name; }
3144     /**
3145      *
3146      */
3147     virtual String getDescription()
3148         { return description; }
3150     /**
3151      *
3152      */
3153     virtual String getCflags()
3154         { return cflags; }
3156     /**
3157      *
3158      */
3159     virtual String getLibs()
3160         { return libs; }
3162     /**
3163      *
3164      */
3165     virtual String getVersion()
3166         { return version; }
3168     /**
3169      *
3170      */
3171     virtual int getMajorVersion()
3172         { return majorVersion; }
3174     /**
3175      *
3176      */
3177     virtual int getMinorVersion()
3178         { return minorVersion; }
3180     /**
3181      *
3182      */
3183     virtual int getMicroVersion()
3184         { return microVersion; }
3186     /**
3187      *
3188      */
3189     virtual std::map<String, String> &getAttributes()
3190         { return attrs; }
3192     /**
3193      *
3194      */
3195     virtual std::vector<String> &getRequireList()
3196         { return requireList; }
3198     virtual bool readFile(const String &fileName);
3200 private:
3202     void init()
3203         {
3204         name         = "";
3205         description  = "";
3206         cflags       = "";
3207         libs         = "";
3208         requires     = "";
3209         version      = "";
3210         majorVersion = 0;
3211         minorVersion = 0;
3212         microVersion = 0;
3213         fileName     = "";
3214         attrs.clear();
3215         requireList.clear();
3216         }
3218     void assign(const PkgConfig &other)
3219         {
3220         name         = other.name;
3221         description  = other.description;
3222         cflags       = other.cflags;
3223         libs         = other.libs;
3224         requires     = other.requires;
3225         version      = other.version;
3226         majorVersion = other.majorVersion;
3227         minorVersion = other.minorVersion;
3228         microVersion = other.microVersion;
3229         fileName     = other.fileName;
3230         attrs        = other.attrs;
3231         requireList  = other.requireList;
3232         }
3236     int get(int pos);
3238     int skipwhite(int pos);
3240     int getword(int pos, String &ret);
3242     void parseRequires();
3244     void parseVersion();
3246     bool parse(const String &buf);
3248     void dumpAttrs();
3250     String name;
3252     String description;
3254     String cflags;
3256     String libs;
3258     String requires;
3260     String version;
3262     int majorVersion;
3264     int minorVersion;
3266     int microVersion;
3268     String fileName;
3270     std::map<String, String> attrs;
3272     std::vector<String> requireList;
3274     char *parsebuf;
3275     int parselen;
3276 };
3279 /**
3280  * Get a character from the buffer at pos.  If out of range,
3281  * return -1 for safety
3282  */
3283 int PkgConfig::get(int pos)
3285     if (pos>parselen)
3286         return -1;
3287     return parsebuf[pos];
3292 /**
3293  *  Skip over all whitespace characters beginning at pos.  Return
3294  *  the position of the first non-whitespace character.
3295  */
3296 int PkgConfig::skipwhite(int pos)
3298     while (pos < parselen)
3299         {
3300         int ch = get(pos);
3301         if (ch < 0)
3302             break;
3303         if (!isspace(ch))
3304             break;
3305         pos++;
3306         }
3307     return pos;
3311 /**
3312  *  Parse the buffer beginning at pos, for a word.  Fill
3313  *  'ret' with the result.  Return the position after the
3314  *  word.
3315  */
3316 int PkgConfig::getword(int pos, String &ret)
3318     while (pos < parselen)
3319         {
3320         int ch = get(pos);
3321         if (ch < 0)
3322             break;
3323         if (!isalnum(ch) && ch != '_' && ch != '-'&& ch != '.')
3324             break;
3325         ret.push_back((char)ch);
3326         pos++;
3327         }
3328     return pos;
3331 void PkgConfig::parseRequires()
3333     if (requires.size() == 0)
3334         return;
3335     parsebuf = (char *)requires.c_str();
3336     parselen = requires.size();
3337     int pos = 0;
3338     while (pos < parselen)
3339         {
3340         pos = skipwhite(pos);
3341         String val;
3342         int pos2 = getword(pos, val);
3343         if (pos2 == pos)
3344             break;
3345         pos = pos2;
3346         //trace("val %s", val.c_str());
3347         requireList.push_back(val);
3348         }
3351 static int getint(const String str)
3353     char *s = (char *)str.c_str();
3354     char *ends = NULL;
3355     long val = strtol(s, &ends, 10);
3356     if (ends == s)
3357         return 0L;
3358     else
3359         return val;
3362 void PkgConfig::parseVersion()
3364     if (version.size() == 0)
3365         return;
3366     String s1, s2, s3;
3367     unsigned int pos = 0;
3368     unsigned int pos2 = version.find('.', pos);
3369     if (pos2 == version.npos)
3370         {
3371         s1 = version;
3372         }
3373     else
3374         {
3375         s1 = version.substr(pos, pos2-pos);
3376         pos = pos2;
3377         pos++;
3378         if (pos < version.size())
3379             {
3380             pos2 = version.find('.', pos);
3381             if (pos2 == version.npos)
3382                 {
3383                 s2 = version.substr(pos, version.size()-pos);
3384                 }
3385             else
3386                 {
3387                 s2 = version.substr(pos, pos2-pos);
3388                 pos = pos2;
3389                 pos++;
3390                 if (pos < version.size())
3391                     s3 = version.substr(pos, pos2-pos);
3392                 }
3393             }
3394         }
3396     majorVersion = getint(s1);
3397     minorVersion = getint(s2);
3398     microVersion = getint(s3);
3399     //trace("version:%d.%d.%d", majorVersion,
3400     //          minorVersion, microVersion );
3404 bool PkgConfig::parse(const String &buf)
3406     init();
3408     parsebuf = (char *)buf.c_str();
3409     parselen = buf.size();
3410     int pos = 0;
3413     while (pos < parselen)
3414         {
3415         String attrName;
3416         pos = skipwhite(pos);
3417         int ch = get(pos);
3418         if (ch == '#')
3419             {
3420             //comment.  eat the rest of the line
3421             while (pos < parselen)
3422                 {
3423                 ch = get(pos);
3424                 if (ch == '\n' || ch < 0)
3425                     break;
3426                 pos++;
3427                 }
3428             continue;
3429             }
3430         pos = getword(pos, attrName);
3431         if (attrName.size() == 0)
3432             continue;
3433         pos = skipwhite(pos);
3434         ch = get(pos);
3435         if (ch != ':' && ch != '=')
3436             {
3437             error("expected ':' or '='");
3438             return false;
3439             }
3440         pos++;
3441         pos = skipwhite(pos);
3442         String attrVal;
3443         while (pos < parselen)
3444             {
3445             ch = get(pos);
3446             if (ch == '\n' || ch < 0)
3447                 break;
3448             else if (ch == '$' && get(pos+1) == '{')
3449                 {
3450                 //#  this is a ${substitution}
3451                 pos += 2;
3452                 String subName;
3453                 while (pos < parselen)
3454                     {
3455                     ch = get(pos);
3456                     if (ch < 0)
3457                         {
3458                         error("unterminated substitution");
3459                         return false;
3460                         }
3461                     else if (ch == '}')
3462                         break;
3463                     else
3464                         subName.push_back((char)ch);
3465                     pos++;
3466                     }
3467                 //trace("subName:%s", subName.c_str());
3468                 String subVal = attrs[subName];
3469                 //trace("subVal:%s", subVal.c_str());
3470                 attrVal.append(subVal);
3471                 }
3472             else
3473                 attrVal.push_back((char)ch);
3474             pos++;
3475             }
3477         attrVal = trim(attrVal);
3478         attrs[attrName] = attrVal;
3480         if (attrName == "Name")
3481             name = attrVal;
3482         else if (attrName == "Description")
3483             description = attrVal;
3484         else if (attrName == "Cflags")
3485             cflags = attrVal;
3486         else if (attrName == "Libs")
3487             libs = attrVal;
3488         else if (attrName == "Requires")
3489             requires = attrVal;
3490         else if (attrName == "Version")
3491             version = attrVal;
3493         //trace("name:'%s'  value:'%s'",
3494         //      attrName.c_str(), attrVal.c_str());
3495         }
3498     parseRequires();
3499     parseVersion();
3501     return true;
3504 void PkgConfig::dumpAttrs()
3506     trace("### PkgConfig attributes for %s", fileName.c_str());
3507     std::map<String, String>::iterator iter;
3508     for (iter=attrs.begin() ; iter!=attrs.end() ; iter++)
3509         {
3510         trace("   %s = %s", iter->first.c_str(), iter->second.c_str());
3511         }
3515 bool PkgConfig::readFile(const String &fileNameArg)
3517     fileName = fileNameArg;
3519     FILE *f = fopen(fileName.c_str(), "r");
3520     if (!f)
3521         {
3522         error("cannot open file '%s' for reading", fileName.c_str());
3523         return false;
3524         }
3525     String buf;
3526     while (true)
3527         {
3528         int ch = fgetc(f);
3529         if (ch < 0)
3530             break;
3531         buf.push_back((char)ch);
3532         }
3533     fclose(f);
3535     trace("####### File:\n%s", buf.c_str());
3536     if (!parse(buf))
3537         {
3538         return false;
3539         }
3541     dumpAttrs();
3543     return true;
3550 //########################################################################
3551 //# D E P T O O L
3552 //########################################################################
3556 /**
3557  *  Class which holds information for each file.
3558  */
3559 class FileRec
3561 public:
3563     typedef enum
3564         {
3565         UNKNOWN,
3566         CFILE,
3567         HFILE,
3568         OFILE
3569         } FileType;
3571     /**
3572      *  Constructor
3573      */
3574     FileRec()
3575         {init(); type = UNKNOWN;}
3577     /**
3578      *  Copy constructor
3579      */
3580     FileRec(const FileRec &other)
3581         {init(); assign(other);}
3582     /**
3583      *  Constructor
3584      */
3585     FileRec(int typeVal)
3586         {init(); type = typeVal;}
3587     /**
3588      *  Assignment operator
3589      */
3590     FileRec &operator=(const FileRec &other)
3591         {init(); assign(other); return *this;}
3594     /**
3595      *  Destructor
3596      */
3597     ~FileRec()
3598         {}
3600     /**
3601      *  Directory part of the file name
3602      */
3603     String path;
3605     /**
3606      *  Base name, sans directory and suffix
3607      */
3608     String baseName;
3610     /**
3611      *  File extension, such as cpp or h
3612      */
3613     String suffix;
3615     /**
3616      *  Type of file: CFILE, HFILE, OFILE
3617      */
3618     int type;
3620     /**
3621      * Used to list files ref'd by this one
3622      */
3623     std::map<String, FileRec *> files;
3626 private:
3628     void init()
3629         {
3630         }
3632     void assign(const FileRec &other)
3633         {
3634         type     = other.type;
3635         baseName = other.baseName;
3636         suffix   = other.suffix;
3637         files    = other.files;
3638         }
3640 };
3644 /**
3645  *  Simpler dependency record
3646  */
3647 class DepRec
3649 public:
3651     /**
3652      *  Constructor
3653      */
3654     DepRec()
3655         {init();}
3657     /**
3658      *  Copy constructor
3659      */
3660     DepRec(const DepRec &other)
3661         {init(); assign(other);}
3662     /**
3663      *  Constructor
3664      */
3665     DepRec(const String &fname)
3666         {init(); name = fname; }
3667     /**
3668      *  Assignment operator
3669      */
3670     DepRec &operator=(const DepRec &other)
3671         {init(); assign(other); return *this;}
3674     /**
3675      *  Destructor
3676      */
3677     ~DepRec()
3678         {}
3680     /**
3681      *  Directory part of the file name
3682      */
3683     String path;
3685     /**
3686      *  Base name, without the path and suffix
3687      */
3688     String name;
3690     /**
3691      *  Suffix of the source
3692      */
3693     String suffix;
3696     /**
3697      * Used to list files ref'd by this one
3698      */
3699     std::vector<String> files;
3702 private:
3704     void init()
3705         {
3706         }
3708     void assign(const DepRec &other)
3709         {
3710         path     = other.path;
3711         name     = other.name;
3712         suffix   = other.suffix;
3713         files    = other.files;
3714         }
3716 };
3719 class DepTool : public MakeBase
3721 public:
3723     /**
3724      *  Constructor
3725      */
3726     DepTool()
3727         {init();}
3729     /**
3730      *  Copy constructor
3731      */
3732     DepTool(const DepTool &other)
3733         {init(); assign(other);}
3735     /**
3736      *  Assignment operator
3737      */
3738     DepTool &operator=(const DepTool &other)
3739         {init(); assign(other); return *this;}
3742     /**
3743      *  Destructor
3744      */
3745     ~DepTool()
3746         {}
3749     /**
3750      *  Reset this section of code
3751      */
3752     virtual void init();
3753     
3754     /**
3755      *  Reset this section of code
3756      */
3757     virtual void assign(const DepTool &other)
3758         {
3759         }
3760     
3761     /**
3762      *  Sets the source directory which will be scanned
3763      */
3764     virtual void setSourceDirectory(const String &val)
3765         { sourceDir = val; }
3767     /**
3768      *  Returns the source directory which will be scanned
3769      */
3770     virtual String getSourceDirectory()
3771         { return sourceDir; }
3773     /**
3774      *  Sets the list of files within the directory to analyze
3775      */
3776     virtual void setFileList(const std::vector<String> &list)
3777         { fileList = list; }
3779     /**
3780      * Creates the list of all file names which will be
3781      * candidates for further processing.  Reads make.exclude
3782      * to see which files for directories to leave out.
3783      */
3784     virtual bool createFileList();
3787     /**
3788      *  Generates the forward dependency list
3789      */
3790     virtual bool generateDependencies();
3793     /**
3794      *  Generates the forward dependency list, saving the file
3795      */
3796     virtual bool generateDependencies(const String &);
3799     /**
3800      *  Load a dependency file
3801      */
3802     std::vector<DepRec> loadDepFile(const String &fileName);
3804     /**
3805      *  Load a dependency file, generating one if necessary
3806      */
3807     std::vector<DepRec> getDepFile(const String &fileName);
3809     /**
3810      *  Save a dependency file
3811      */
3812     bool saveDepFile(const String &fileName);
3815 private:
3818     /**
3819      *
3820      */
3821     void parseName(const String &fullname,
3822                    String &path,
3823                    String &basename,
3824                    String &suffix);
3826     /**
3827      *
3828      */
3829     int get(int pos);
3831     /**
3832      *
3833      */
3834     int skipwhite(int pos);
3836     /**
3837      *
3838      */
3839     int getword(int pos, String &ret);
3841     /**
3842      *
3843      */
3844     bool sequ(int pos, char *key);
3846     /**
3847      *
3848      */
3849     bool addIncludeFile(FileRec *frec, const String &fname);
3851     /**
3852      *
3853      */
3854     bool scanFile(const String &fname, FileRec *frec);
3856     /**
3857      *
3858      */
3859     bool processDependency(FileRec *ofile,
3860                            FileRec *include,
3861                            int depth);
3863     /**
3864      *
3865      */
3866     String sourceDir;
3868     /**
3869      *
3870      */
3871     std::vector<String> fileList;
3873     /**
3874      *
3875      */
3876     std::vector<String> directories;
3878     /**
3879      * A list of all files which will be processed for
3880      * dependencies.  This is the only list that has the actual
3881      * records.  All other lists have pointers to these records.     
3882      */
3883     std::map<String, FileRec *> allFiles;
3885     /**
3886      * The list of .o files, and the
3887      * dependencies upon them.
3888      */
3889     std::map<String, FileRec *> depFiles;
3891     int depFileSize;
3892     char *depFileBuf;
3893     
3895 };
3901 /**
3902  *  Clean up after processing.  Called by the destructor, but should
3903  *  also be called before the object is reused.
3904  */
3905 void DepTool::init()
3907     sourceDir = ".";
3909     fileList.clear();
3910     directories.clear();
3911     
3912     //clear refs
3913     depFiles.clear();
3914     //clear records
3915     std::map<String, FileRec *>::iterator iter;
3916     for (iter=allFiles.begin() ; iter!=allFiles.end() ; iter++)
3917          delete iter->second;
3919     allFiles.clear(); 
3926 /**
3927  *  Parse a full path name into path, base name, and suffix
3928  */
3929 void DepTool::parseName(const String &fullname,
3930                         String &path,
3931                         String &basename,
3932                         String &suffix)
3934     if (fullname.size() < 2)
3935         return;
3937     unsigned int pos = fullname.find_last_of('/');
3938     if (pos != fullname.npos && pos<fullname.size()-1)
3939         {
3940         path = fullname.substr(0, pos);
3941         pos++;
3942         basename = fullname.substr(pos, fullname.size()-pos);
3943         }
3944     else
3945         {
3946         path = "";
3947         basename = fullname;
3948         }
3950     pos = basename.find_last_of('.');
3951     if (pos != basename.npos && pos<basename.size()-1)
3952         {
3953         suffix   = basename.substr(pos+1, basename.size()-pos-1);
3954         basename = basename.substr(0, pos);
3955         }
3957     //trace("parsename:%s %s %s", path.c_str(),
3958     //        basename.c_str(), suffix.c_str()); 
3963 /**
3964  *  Generate our internal file list.
3965  */
3966 bool DepTool::createFileList()
3969     for (unsigned int i=0 ; i<fileList.size() ; i++)
3970         {
3971         String fileName = fileList[i];
3972         //trace("## FileName:%s", fileName.c_str());
3973         String path;
3974         String basename;
3975         String sfx;
3976         parseName(fileName, path, basename, sfx);
3977         if (sfx == "cpp" || sfx == "c" || sfx == "cxx"   ||
3978                     sfx == "cc" || sfx == "CC")
3979             {
3980             FileRec *fe         = new FileRec(FileRec::CFILE);
3981             fe->path            = path;
3982             fe->baseName        = basename;
3983             fe->suffix          = sfx;
3984             allFiles[fileName]  = fe;
3985             }
3986         else if (sfx == "h"   ||  sfx == "hh"  ||
3987                  sfx == "hpp" ||  sfx == "hxx")
3988             {
3989             FileRec *fe         = new FileRec(FileRec::HFILE);
3990             fe->path            = path;
3991             fe->baseName        = basename;
3992             fe->suffix          = sfx;
3993             allFiles[fileName]  = fe;
3994             }
3995         }
3997     if (!listDirectories(sourceDir, "", directories))
3998         return false;
3999         
4000     return true;
4007 /**
4008  * Get a character from the buffer at pos.  If out of range,
4009  * return -1 for safety
4010  */
4011 int DepTool::get(int pos)
4013     if (pos>depFileSize)
4014         return -1;
4015     return depFileBuf[pos];
4020 /**
4021  *  Skip over all whitespace characters beginning at pos.  Return
4022  *  the position of the first non-whitespace character.
4023  */
4024 int DepTool::skipwhite(int pos)
4026     while (pos < depFileSize)
4027         {
4028         int ch = get(pos);
4029         if (ch < 0)
4030             break;
4031         if (!isspace(ch))
4032             break;
4033         pos++;
4034         }
4035     return pos;
4039 /**
4040  *  Parse the buffer beginning at pos, for a word.  Fill
4041  *  'ret' with the result.  Return the position after the
4042  *  word.
4043  */
4044 int DepTool::getword(int pos, String &ret)
4046     while (pos < depFileSize)
4047         {
4048         int ch = get(pos);
4049         if (ch < 0)
4050             break;
4051         if (isspace(ch))
4052             break;
4053         ret.push_back((char)ch);
4054         pos++;
4055         }
4056     return pos;
4059 /**
4060  * Return whether the sequence of characters in the buffer
4061  * beginning at pos match the key,  for the length of the key
4062  */
4063 bool DepTool::sequ(int pos, char *key)
4065     while (*key)
4066         {
4067         if (*key != get(pos))
4068             return false;
4069         key++; pos++;
4070         }
4071     return true;
4076 /**
4077  *  Add an include file name to a file record.  If the name
4078  *  is not found in allFiles explicitly, try prepending include
4079  *  directory names to it and try again.
4080  */
4081 bool DepTool::addIncludeFile(FileRec *frec, const String &iname)
4084     std::map<String, FileRec *>::iterator iter =
4085            allFiles.find(iname);
4086     if (iter != allFiles.end()) //already exists
4087         {
4088          //h file in same dir
4089         FileRec *other = iter->second;
4090         //trace("local: '%s'", iname.c_str());
4091         frec->files[iname] = other;
4092         return true;
4093         }
4094     else 
4095         {
4096         //look in other dirs
4097         std::vector<String>::iterator diter;
4098         for (diter=directories.begin() ;
4099              diter!=directories.end() ; diter++)
4100             {
4101             String dfname = *diter;
4102             dfname.append("/");
4103             dfname.append(iname);
4104             iter = allFiles.find(dfname);
4105             if (iter != allFiles.end())
4106                 {
4107                 FileRec *other = iter->second;
4108                 //trace("other: '%s'", iname.c_str());
4109                 frec->files[dfname] = other;
4110                 return true;
4111                 }
4112             }
4113         }
4114     return true;
4119 /**
4120  *  Lightly parse a file to find the #include directives.  Do
4121  *  a bit of state machine stuff to make sure that the directive
4122  *  is valid.  (Like not in a comment).
4123  */
4124 bool DepTool::scanFile(const String &fname, FileRec *frec)
4126     String fileName;
4127     if (sourceDir.size() > 0)
4128         {
4129         fileName.append(sourceDir);
4130         fileName.append("/");
4131         }
4132     fileName.append(fname);
4133     String nativeName = getNativePath(fileName);
4134     FILE *f = fopen(nativeName.c_str(), "r");
4135     if (!f)
4136         {
4137         error("Could not open '%s' for reading", fname.c_str());
4138         return false;
4139         }
4140     String buf;
4141     while (true)
4142         {
4143         int ch = fgetc(f);
4144         if (ch < 0)
4145             break;
4146         buf.push_back((char)ch);
4147         }
4148     fclose(f);
4150     depFileSize = buf.size();
4151     depFileBuf  = (char *)buf.c_str();
4152     int pos = 0;
4155     while (pos < depFileSize)
4156         {
4157         //trace("p:%c", get(pos));
4159         //# Block comment
4160         if (get(pos) == '/' && get(pos+1) == '*')
4161             {
4162             pos += 2;
4163             while (pos < depFileSize)
4164                 {
4165                 if (get(pos) == '*' && get(pos+1) == '/')
4166                     {
4167                     pos += 2;
4168                     break;
4169                     }
4170                 else
4171                     pos++;
4172                 }
4173             }
4174         //# Line comment
4175         else if (get(pos) == '/' && get(pos+1) == '/')
4176             {
4177             pos += 2;
4178             while (pos < depFileSize)
4179                 {
4180                 if (get(pos) == '\n')
4181                     {
4182                     pos++;
4183                     break;
4184                     }
4185                 else
4186                     pos++;
4187                 }
4188             }
4189         //# #include! yaay
4190         else if (sequ(pos, "#include"))
4191             {
4192             pos += 8;
4193             pos = skipwhite(pos);
4194             String iname;
4195             pos = getword(pos, iname);
4196             if (iname.size()>2)
4197                 {
4198                 iname = iname.substr(1, iname.size()-2);
4199                 addIncludeFile(frec, iname);
4200                 }
4201             }
4202         else
4203             {
4204             pos++;
4205             }
4206         }
4208     return true;
4213 /**
4214  *  Recursively check include lists to find all files in allFiles to which
4215  *  a given file is dependent.
4216  */
4217 bool DepTool::processDependency(FileRec *ofile,
4218                              FileRec *include,
4219                              int depth)
4221     std::map<String, FileRec *>::iterator iter;
4222     for (iter=include->files.begin() ; iter!=include->files.end() ; iter++)
4223         {
4224         String fname  = iter->first;
4225         if (ofile->files.find(fname) != ofile->files.end())
4226             {
4227             //trace("file '%s' already seen", fname.c_str());
4228             continue;
4229             }
4230         FileRec *child  = iter->second;
4231         ofile->files[fname] = child;
4232       
4233         processDependency(ofile, child, depth+1);
4234         }
4237     return true;
4244 /**
4245  *  Generate the file dependency list.
4246  */
4247 bool DepTool::generateDependencies()
4249     std::map<String, FileRec *>::iterator iter;
4250     //# First pass.  Scan for all includes
4251     for (iter=allFiles.begin() ; iter!=allFiles.end() ; iter++)
4252         {
4253         FileRec *frec = iter->second;
4254         if (!scanFile(iter->first, frec))
4255             {
4256             //quit?
4257             }
4258         }
4260     //# Second pass.  Scan for all includes
4261     for (iter=allFiles.begin() ; iter!=allFiles.end() ; iter++)
4262         {
4263         FileRec *include = iter->second;
4264         if (include->type == FileRec::CFILE)
4265             {
4266             String cFileName = iter->first;
4267             FileRec *ofile      = new FileRec(FileRec::OFILE);
4268             ofile->path         = include->path;
4269             ofile->baseName     = include->baseName;
4270             ofile->suffix       = include->suffix;
4271             String fname     = include->path;
4272             if (fname.size()>0)
4273                 fname.append("/");
4274             fname.append(include->baseName);
4275             fname.append(".o");
4276             depFiles[fname]    = ofile;
4277             //add the .c file first?   no, don't
4278             //ofile->files[cFileName] = include;
4279             
4280             //trace("ofile:%s", fname.c_str());
4282             processDependency(ofile, include, 0);
4283             }
4284         }
4286       
4287     return true;
4292 /**
4293  *  High-level call to generate deps and optionally save them
4294  */
4295 bool DepTool::generateDependencies(const String &fileName)
4297     if (!createFileList())
4298         return false;
4299     if (!generateDependencies())
4300         return false;
4301     if (!saveDepFile(fileName))
4302         return false;
4303     return true;
4307 /**
4308  *   This saves the dependency cache.
4309  */
4310 bool DepTool::saveDepFile(const String &fileName)
4312     time_t tim;
4313     time(&tim);
4315     FILE *f = fopen(fileName.c_str(), "w");
4316     if (!f)
4317         {
4318         trace("cannot open '%s' for writing", fileName.c_str());
4319         }
4320     fprintf(f, "<?xml version='1.0'?>\n");
4321     fprintf(f, "<!--\n");
4322     fprintf(f, "########################################################\n");
4323     fprintf(f, "## File: build.dep\n");
4324     fprintf(f, "## Generated by BuildTool at :%s", ctime(&tim));
4325     fprintf(f, "########################################################\n");
4326     fprintf(f, "-->\n");
4328     fprintf(f, "<dependencies source='%s'>\n\n", sourceDir.c_str());
4329     std::map<String, FileRec *>::iterator iter;
4330     for (iter=depFiles.begin() ; iter!=depFiles.end() ; iter++)
4331         {
4332         FileRec *frec = iter->second;
4333         if (frec->type == FileRec::OFILE)
4334             {
4335             fprintf(f, "<object path='%s' name='%s' suffix='%s'>\n",
4336                              frec->path.c_str(), frec->baseName.c_str(), frec->suffix.c_str());
4337             std::map<String, FileRec *>::iterator citer;
4338             for (citer=frec->files.begin() ; citer!=frec->files.end() ; citer++)
4339                 {
4340                 String cfname = citer->first;
4341                 fprintf(f, "    <dep name='%s'/>\n", cfname.c_str());
4342                 }
4343             fprintf(f, "</object>\n\n");
4344             }
4345         }
4347     fprintf(f, "</dependencies>\n");
4348     fprintf(f, "\n");
4349     fprintf(f, "<!--\n");
4350     fprintf(f, "########################################################\n");
4351     fprintf(f, "## E N D\n");
4352     fprintf(f, "########################################################\n");
4353     fprintf(f, "-->\n");
4355     fclose(f);
4357     return true;
4363 /**
4364  *   This loads the dependency cache.
4365  */
4366 std::vector<DepRec> DepTool::loadDepFile(const String &depFile)
4368     std::vector<DepRec> result;
4369     
4370     Parser parser;
4371     Element *root = parser.parseFile(depFile.c_str());
4372     if (!root)
4373         {
4374         error("Could not open %s for reading", depFile.c_str());
4375         return result;
4376         }
4378     if (root->getChildren().size()==0 ||
4379         root->getChildren()[0]->getName()!="dependencies")
4380         {
4381         error("Main xml element should be <dependencies>");
4382         delete root;
4383         return result;
4384         }
4386     //########## Start parsing
4387     Element *depList = root->getChildren()[0];
4389     std::vector<Element *> objects = depList->getChildren();
4390     for (unsigned int i=0 ; i<objects.size() ; i++)
4391         {
4392         Element *objectElem = objects[i];
4393         String tagName = objectElem->getName();
4394         if (tagName == "object")
4395             {
4396             String objName   = objectElem->getAttribute("name");
4397              //trace("object:%s", objName.c_str());
4398             DepRec depObject(objName);
4399             depObject.path   = objectElem->getAttribute("path");
4400             depObject.suffix = objectElem->getAttribute("suffix");
4401             //########## DESCRIPTION
4402             std::vector<Element *> depElems = objectElem->getChildren();
4403             for (unsigned int i=0 ; i<depElems.size() ; i++)
4404                 {
4405                 Element *depElem = depElems[i];
4406                 tagName = depElem->getName();
4407                 if (tagName == "dep")
4408                     {
4409                     String depName = depElem->getAttribute("name");
4410                     //trace("    dep:%s", depName.c_str());
4411                     depObject.files.push_back(depName);
4412                     }
4413                 }
4414             //Insert into the result list, in a sorted manner
4415             bool inserted = false;
4416             std::vector<DepRec>::iterator iter;
4417             for (iter = result.begin() ; iter != result.end() ; iter++)
4418                 {
4419                 if (iter->path > depObject.path)
4420                     {
4421                     inserted = true;
4422                     iter = result.insert(iter, depObject);
4423                     break;
4424                     }
4425                 else if (iter->path == depObject.path)
4426                     {
4427                     if (iter->name > depObject.name)
4428                         {
4429                         inserted = true;
4430                         iter = result.insert(iter, depObject);
4431                         break;
4432                         }
4433                     }
4434                 }
4435             if (!inserted)
4436                 result.push_back(depObject);
4437             }
4438         }
4440     delete root;
4442     return result;
4446 /**
4447  *   This loads the dependency cache.
4448  */
4449 std::vector<DepRec> DepTool::getDepFile(const String &depFile)
4451     std::vector<DepRec> result = loadDepFile(depFile);
4452     if (result.size() == 0)
4453         {
4454         generateDependencies(depFile);
4455         result = loadDepFile(depFile);
4456         }
4457     return result;
4463 //########################################################################
4464 //# T A S K
4465 //########################################################################
4466 //forward decl
4467 class Target;
4468 class Make;
4470 /**
4471  *
4472  */
4473 class Task : public MakeBase
4476 public:
4478     typedef enum
4479         {
4480         TASK_NONE,
4481         TASK_AR,
4482         TASK_CC,
4483         TASK_COPY,
4484         TASK_DELETE,
4485         TASK_JAR,
4486         TASK_JAVAC,
4487         TASK_LINK,
4488         TASK_MKDIR,
4489         TASK_MSGFMT,
4490         TASK_RANLIB,
4491         TASK_RC,
4492         TASK_STRIP,
4493         TASK_TSTAMP
4494         } TaskType;
4495         
4497     /**
4498      *
4499      */
4500     Task(MakeBase &par) : parent(par)
4501         { init(); }
4503     /**
4504      *
4505      */
4506     Task(const Task &other) : parent(other.parent)
4507         { init(); assign(other); }
4509     /**
4510      *
4511      */
4512     Task &operator=(const Task &other)
4513         { assign(other); return *this; }
4515     /**
4516      *
4517      */
4518     virtual ~Task()
4519         { }
4522     /**
4523      *
4524      */
4525     virtual MakeBase &getParent()
4526         { return parent; }
4528      /**
4529      *
4530      */
4531     virtual int  getType()
4532         { return type; }
4534     /**
4535      *
4536      */
4537     virtual void setType(int val)
4538         { type = val; }
4540     /**
4541      *
4542      */
4543     virtual String getName()
4544         { return name; }
4546     /**
4547      *
4548      */
4549     virtual bool execute()
4550         { return true; }
4552     /**
4553      *
4554      */
4555     virtual bool parse(Element *elem)
4556         { return true; }
4558     /**
4559      *
4560      */
4561     Task *createTask(Element *elem);
4564 protected:
4566     void init()
4567         {
4568         type = TASK_NONE;
4569         name = "none";
4570         }
4572     void assign(const Task &other)
4573         {
4574         type = other.type;
4575         name = other.name;
4576         }
4577         
4578     String getAttribute(Element *elem, const String &attrName)
4579         {
4580         String str;
4581         return str;
4582         }
4584     MakeBase &parent;
4586     int type;
4588     String name;
4589 };
4594 /**
4595  * Run the "ar" command to archive .o's into a .a
4596  */
4597 class TaskAr : public Task
4599 public:
4601     TaskAr(MakeBase &par) : Task(par)
4602         {
4603                 type = TASK_AR; name = "ar";
4604                 command = "ar crv";
4605                 }
4607     virtual ~TaskAr()
4608         {}
4610     virtual bool execute()
4611         {
4612         //trace("###########HERE %d", fileSet.size());
4613         bool doit = false;
4614         
4615         String fullOut = parent.resolve(fileName);
4616         //trace("ar fullout: %s", fullOut.c_str());
4617         
4619         for (unsigned int i=0 ; i<fileSet.size() ; i++)
4620             {
4621             String fname;
4622                         if (fileSetDir.size()>0)
4623                             {
4624                             fname.append(fileSetDir);
4625                 fname.append("/");
4626                 }
4627             fname.append(fileSet[i]);
4628             String fullName = parent.resolve(fname);
4629             //trace("ar : %s/%s", fullOut.c_str(), fullName.c_str());
4630             if (isNewerThan(fullName, fullOut))
4631                 doit = true;
4632             }
4633         //trace("Needs it:%d", doit);
4634         if (!doit)
4635             {
4636             return true;
4637             }
4639         String cmd = command;
4640         cmd.append(" ");
4641         cmd.append(fullOut);
4642         for (unsigned int i=0 ; i<fileSet.size() ; i++)
4643             {
4644             String fname;
4645                         if (fileSetDir.size()>0)
4646                             {
4647                             fname.append(fileSetDir);
4648                 fname.append("/");
4649                 }
4650             fname.append(fileSet[i]);
4651             String fullName = parent.resolve(fname);
4653             cmd.append(" ");
4654             cmd.append(fullName);
4655             }
4657         String outString, errString;
4658         if (!executeCommand(cmd.c_str(), "", outString, errString))
4659             {
4660             error("AR problem: %s", errString.c_str());
4661             return false;
4662             }
4664         return true;
4665         }
4667     virtual bool parse(Element *elem)
4668         {
4669         if (!parent.getAttribute(elem, "file", fileName))
4670             return false;
4671             
4672         std::vector<Element *> children = elem->getChildren();
4673         for (unsigned int i=0 ; i<children.size() ; i++)
4674             {
4675             Element *child = children[i];
4676             String tagName = child->getName();
4677             if (tagName == "fileset")
4678                 {
4679                 if (!getFileSet(child, parent, fileSetDir, fileSet))
4680                     return false;
4681                 }
4682             }
4683         return true;
4684         }
4686 private:
4688     String command;
4689     String fileName;
4690     String fileSetDir;
4691     std::vector<String> fileSet;
4693 };
4696 /**
4697  * This task runs the C/C++ compiler.  The compiler is invoked
4698  * for all .c or .cpp files which are newer than their correcsponding
4699  * .o files.  
4700  */
4701 class TaskCC : public Task
4703 public:
4705     TaskCC(MakeBase &par) : Task(par)
4706         {
4707                 type = TASK_CC; name = "cc";
4708                 ccCommand   = "gcc";
4709                 cxxCommand  = "g++";
4710                 source      = ".";
4711                 dest        = ".";
4712                 flags       = "";
4713                 defines     = "";
4714                 includes    = "";
4715                 sourceFiles.clear();
4716         }
4718     virtual ~TaskCC()
4719         {}
4721     virtual bool execute()
4722         {
4723         DepTool depTool;
4724         depTool.setSourceDirectory(source);
4725         depTool.setFileList(sourceFiles);
4726         std::vector<DepRec> deps = depTool.getDepFile("build.dep");
4727         
4728         String incs;
4729         incs.append("-I");
4730         incs.append(parent.resolve("."));
4731         incs.append(" ");
4732         if (includes.size()>0)
4733             {
4734             incs.append(includes);
4735             incs.append(" ");
4736             }
4737         std::set<String> paths;
4738         std::vector<DepRec>::iterator viter;
4739         for (viter=deps.begin() ; viter!=deps.end() ; viter++)
4740             {
4741             DepRec dep = *viter;
4742             if (dep.path.size()>0)
4743                 paths.insert(dep.path);
4744             }
4745         if (source.size()>0)
4746             {
4747             incs.append(" -I");
4748             incs.append(parent.resolve(source));
4749             incs.append(" ");
4750             }
4751         std::set<String>::iterator setIter;
4752         for (setIter=paths.begin() ; setIter!=paths.end() ; setIter++)
4753             {
4754             incs.append(" -I");
4755             String dname;
4756             if (source.size()>0)
4757                 {
4758                 dname.append(source);
4759                 dname.append("/");
4760                 }
4761             dname.append(*setIter);
4762             incs.append(parent.resolve(dname));
4763             }
4764         std::vector<String> cfiles;
4765         for (viter=deps.begin() ; viter!=deps.end() ; viter++)
4766             {
4767             DepRec dep = *viter;
4769             //## Select command
4770             String sfx = dep.suffix;
4771             String command = ccCommand;
4772             if (sfx == "cpp" || sfx == "c++" || sfx == "cc"
4773                              || sfx == "CC")
4774                             command = cxxCommand;
4775  
4776             //## Make paths
4777             String destPath = dest;
4778             String srcPath  = source;
4779             if (dep.path.size()>0)
4780                             {
4781                 destPath.append("/");
4782                                 destPath.append(dep.path);
4783                 srcPath.append("/");
4784                                 srcPath.append(dep.path);
4785                             }
4786             //## Make sure destination directory exists
4787                         if (!createDirectory(destPath))
4788                             return false;
4789                             
4790             //## Check whether it needs to be done
4791                         String destFullName = destPath;
4792                         destFullName.append("/");
4793                         destFullName.append(dep.name);
4794                         destFullName.append(".o");
4795                         String srcFullName = srcPath;
4796                         srcFullName.append("/");
4797                         srcFullName.append(dep.name);
4798                         srcFullName.append(".");
4799                         srcFullName.append(dep.suffix);
4800             if (!isNewerThan(srcFullName, destFullName))
4801                 {
4802                 //trace("%s skipped", srcFullName.c_str());
4803                 continue;
4804                 }
4806             //## Assemble the command
4807             String cmd = command;
4808             cmd.append(" -c ");
4809             cmd.append(flags);
4810                         cmd.append(" ");
4811             cmd.append(defines);
4812                         cmd.append(" ");
4813             cmd.append(incs);
4814                         cmd.append(" ");
4815                         cmd.append(srcFullName);
4816             cmd.append(" -o ");
4817                         cmd.append(destFullName);
4819             //## Execute the command
4821             String outString, errString;
4822             if (!executeCommand(cmd.c_str(), "", outString, errString))
4823                 {
4824                 error("problem compiling: %s", errString.c_str());
4825                 return false;
4826                 }
4827             }
4828         
4829         return true;
4830         }
4832     virtual bool parse(Element *elem)
4833         {
4834         String s;
4835         if (!parent.getAttribute(elem, "command", s))
4836             return false;
4837         if (s.size()>0) { ccCommand = s; cxxCommand = s; }
4838         if (!parent.getAttribute(elem, "cc", s))
4839             return false;
4840         if (s.size()>0) ccCommand = s;
4841         if (!parent.getAttribute(elem, "cxx", s))
4842             return false;
4843         if (s.size()>0) cxxCommand = s;
4844         if (!parent.getAttribute(elem, "destdir", s))
4845             return false;
4846         if (s.size()>0) dest = s;
4848         std::vector<Element *> children = elem->getChildren();
4849         for (unsigned int i=0 ; i<children.size() ; i++)
4850             {
4851             Element *child = children[i];
4852             String tagName = child->getName();
4853             if (tagName == "flags")
4854                 {
4855                 if (!parent.getValue(child, flags))
4856                     return false;
4857                 }
4858             else if (tagName == "includes")
4859                 {
4860                 if (!parent.getValue(child, includes))
4861                     return false;
4862                 }
4863             else if (tagName == "defines")
4864                 {
4865                 if (!parent.getValue(child, defines))
4866                     return false;
4867                 }
4868             else if (tagName == "fileset")
4869                 {
4870                 if (!getFileSet(child, parent, source, sourceFiles))
4871                     return false;
4872                 }
4873             }
4875         return true;
4876         }
4877         
4878 protected:
4880     String ccCommand;
4881     String cxxCommand;
4882     String source;
4883     String dest;
4884     String flags;
4885     String defines;
4886     String includes;
4887     std::vector<String> sourceFiles;
4888     
4889 };
4893 /**
4894  *
4895  */
4896 class TaskCopy : public Task
4898 public:
4900     typedef enum
4901         {
4902         CP_NONE,
4903         CP_TOFILE,
4904         CP_TODIR
4905         } CopyType;
4907     TaskCopy(MakeBase &par) : Task(par)
4908         {
4909                 type = TASK_COPY; name = "copy";
4910                 cptype = CP_NONE;
4911                 verbose = false;
4912                 haveFileSet = false;
4913                 }
4915     virtual ~TaskCopy()
4916         {}
4918     virtual bool execute()
4919         {
4920         switch (cptype)
4921            {
4922            case CP_TOFILE:
4923                {
4924                if (fileName.size()>0)
4925                    {
4926                    status("          : %s", fileName.c_str());
4927                    String fullSource = parent.resolve(fileName);
4928                    String fullDest = parent.resolve(toFileName);
4929                    //trace("copy %s to file %s", fullSource.c_str(),
4930                                    //                       fullDest.c_str());
4931                    if (!isNewerThan(fullSource, fullDest))
4932                        {
4933                        return true;
4934                        }
4935                    if (!copyFile(fullSource, fullDest))
4936                        return false;
4937                    status("          : 1 file copied");
4938                    }
4939                return true;
4940                }
4941            case CP_TODIR:
4942                {
4943                if (haveFileSet)
4944                    {
4945                    int nrFiles = 0;
4946                    status("          : %s", fileSetDir.c_str());
4947                    for (unsigned int i=0 ; i<fileSet.size() ; i++)
4948                        {
4949                        String fileName = fileSet[i];
4951                        String sourcePath;
4952                        if (fileSetDir.size()>0)
4953                            {
4954                            sourcePath.append(fileSetDir);
4955                            sourcePath.append("/");
4956                            }
4957                        sourcePath.append(fileName);
4958                        String fullSource = parent.resolve(sourcePath);
4959                        
4960                        //Get the immediate parent directory's base name
4961                        String baseFileSetDir = fileSetDir;
4962                        unsigned int pos = baseFileSetDir.find_last_of('/');
4963                        if (pos!=baseFileSetDir.npos &&
4964                                                       pos < baseFileSetDir.size()-1)
4965                            baseFileSetDir =
4966                                                       baseFileSetDir.substr(pos+1,
4967                                                                baseFileSetDir.size());
4968                                            //Now make the new path
4969                        String destPath;
4970                        if (toDirName.size()>0)
4971                            {
4972                            destPath.append(toDirName);
4973                            destPath.append("/");
4974                            }
4975                        if (baseFileSetDir.size()>0)
4976                            {
4977                            destPath.append(baseFileSetDir);
4978                            destPath.append("/");
4979                            }
4980                        destPath.append(fileName);
4981                        String fullDest = parent.resolve(destPath);
4982                        //trace("fileName:%s", fileName.c_str());
4983                        //trace("copy %s to new dir : %s", fullSource.c_str(),
4984                                        //                   fullDest.c_str());
4985                        if (!isNewerThan(fullSource, fullDest))
4986                            {
4987                            //trace("copy skipping %s", fullSource.c_str());
4988                            continue;
4989                            }
4990                        if (!copyFile(fullSource, fullDest))
4991                            return false;
4992                        nrFiles++;
4993                        }
4994                    status("          : %d file(s) copied", nrFiles);
4995                    }
4996                else //file source
4997                    {
4998                    //For file->dir we want only the basename of
4999                    //the source appended to the dest dir
5000                    status("          : %s", fileName.c_str());
5001                    String baseName = fileName;
5002                    unsigned int pos = baseName.find_last_of('/');
5003                    if (pos!=baseName.npos && pos<baseName.size()-1)
5004                        baseName = baseName.substr(pos+1, baseName.size());
5005                    String fullSource = parent.resolve(fileName);
5006                    String destPath;
5007                    if (toDirName.size()>0)
5008                        {
5009                        destPath.append(toDirName);
5010                        destPath.append("/");
5011                        }
5012                    destPath.append(baseName);
5013                    String fullDest = parent.resolve(destPath);
5014                    //trace("copy %s to new dir : %s", fullSource.c_str(),
5015                                    //                       fullDest.c_str());
5016                    if (!isNewerThan(fullSource, fullDest))
5017                        {
5018                        return true;
5019                        }
5020                    if (!copyFile(fullSource, fullDest))
5021                        return false;
5022                    status("          : 1 file copied");
5023                    }
5024                return true;
5025                }
5026            }
5027         return true;
5028         }
5031     virtual bool parse(Element *elem)
5032         {
5033         if (!parent.getAttribute(elem, "file", fileName))
5034             return false;
5035         if (!parent.getAttribute(elem, "tofile", toFileName))
5036             return false;
5037         if (toFileName.size() > 0)
5038             cptype = CP_TOFILE;
5039         if (!parent.getAttribute(elem, "todir", toDirName))
5040             return false;
5041         if (toDirName.size() > 0)
5042             cptype = CP_TODIR;
5043         String ret;
5044         if (!parent.getAttribute(elem, "verbose", ret))
5045             return false;
5046         if (ret.size()>0 && !getBool(ret, verbose))
5047             return false;
5048             
5049         haveFileSet = false;
5050         
5051         std::vector<Element *> children = elem->getChildren();
5052         for (unsigned int i=0 ; i<children.size() ; i++)
5053             {
5054             Element *child = children[i];
5055             String tagName = child->getName();
5056             if (tagName == "fileset")
5057                 {
5058                 if (!getFileSet(child, parent, fileSetDir, fileSet))
5059                     {
5060                     error("problem getting fileset");
5061                                         return false;
5062                                         }
5063                                 haveFileSet = true;
5064                 }
5065             }
5067         //Perform validity checks
5068                 if (fileName.size()>0 && fileSet.size()>0)
5069                     {
5070                     error("<copy> can only have one of : file= and <fileset>");
5071                     return false;
5072                     }
5073         if (toFileName.size()>0 && toDirName.size()>0)
5074             {
5075             error("<copy> can only have one of : tofile= or todir=");
5076             return false;
5077             }
5078         if (haveFileSet && toDirName.size()==0)
5079             {
5080             error("a <copy> task with a <fileset> must have : todir=");
5081             return false;
5082             }
5083                 if (cptype == CP_TOFILE && fileName.size()==0)
5084                     {
5085                     error("<copy> tofile= must be associated with : file=");
5086                     return false;
5087                     }
5088                 if (cptype == CP_TODIR && fileName.size()==0 && !haveFileSet)
5089                     {
5090                     error("<copy> todir= must be associated with : file= or <fileset>");
5091                     return false;
5092                     }
5094         return true;
5095         }
5096         
5097 private:
5099     int cptype;
5100     String fileName;
5101     String fileSetDir;
5102     std::vector<String> fileSet;
5103     String toFileName;
5104     String toDirName;
5105     bool verbose;
5106     bool haveFileSet;
5107 };
5110 /**
5111  *
5112  */
5113 class TaskDelete : public Task
5115 public:
5117     typedef enum
5118         {
5119         DEL_FILE,
5120         DEL_DIR,
5121         DEL_FILESET
5122         } DeleteType;
5124     TaskDelete(MakeBase &par) : Task(par)
5125         { 
5126                   type        = TASK_DELETE;
5127                   name        = "delete";
5128                   delType     = DEL_FILE;
5129           verbose     = false;
5130           quiet       = false;
5131           failOnError = true;
5132                 }
5134     virtual ~TaskDelete()
5135         {}
5137     virtual bool execute()
5138         {
5139         struct stat finfo;
5140         switch (delType)
5141             {
5142             case DEL_FILE:
5143                 {
5144                 status("          : %s", fileName.c_str());
5145                 String fullName = parent.resolve(fileName);
5146                 char *fname = (char *)fullName.c_str();
5147                 //does not exist
5148                 if (stat(fname, &finfo)<0)
5149                     return true;
5150                 //exists but is not a regular file
5151                 if (!S_ISREG(finfo.st_mode))
5152                     {
5153                     error("<delete> failed. '%s' exists and is not a regular file",
5154                           fname);
5155                     return false;
5156                     }
5157                 if (remove(fname)<0)
5158                     {
5159                     error("<delete> failed: %s", strerror(errno));
5160                     return false;
5161                     }
5162                 return true;
5163                 }
5164             case DEL_DIR:
5165                 {
5166                 status("          : %s", dirName.c_str());
5167                 String fullDir = parent.resolve(dirName);
5168                 if (!removeDirectory(fullDir))
5169                     return false;
5170                 return true;
5171                 }
5172             }
5173         return true;
5174         }
5176     virtual bool parse(Element *elem)
5177         {
5178         if (!parent.getAttribute(elem, "file", fileName))
5179             return false;
5180         if (fileName.size() > 0)
5181             delType = DEL_FILE;
5182         if (!parent.getAttribute(elem, "dir", dirName))
5183             return false;
5184         if (dirName.size() > 0)
5185             delType = DEL_DIR;
5186         if (fileName.size()>0 && dirName.size()>0)
5187             {
5188             error("<delete> can only have one attribute of file= or dir=");
5189             return false;
5190             }
5191         String ret;
5192         if (!parent.getAttribute(elem, "verbose", ret))
5193             return false;
5194         if (ret.size()>0 && !getBool(ret, verbose))
5195             return false;
5196         if (!parent.getAttribute(elem, "quiet", ret))
5197             return false;
5198         if (ret.size()>0 && !getBool(ret, quiet))
5199             return false;
5200         if (!parent.getAttribute(elem, "failonerror", ret))
5201             return false;
5202         if (ret.size()>0 && !getBool(ret, failOnError))
5203             return false;
5204         return true;
5205         }
5207 private:
5209     int delType;
5210     String dirName;
5211     String fileName;
5212     bool verbose;
5213     bool quiet;
5214     bool failOnError;
5215 };
5218 /**
5219  *
5220  */
5221 class TaskJar : public Task
5223 public:
5225     TaskJar(MakeBase &par) : Task(par)
5226         { type = TASK_JAR; name = "jar"; }
5228     virtual ~TaskJar()
5229         {}
5231     virtual bool execute()
5232         {
5233         return true;
5234         }
5236     virtual bool parse(Element *elem)
5237         {
5238         return true;
5239         }
5240 };
5243 /**
5244  *
5245  */
5246 class TaskJavac : public Task
5248 public:
5250     TaskJavac(MakeBase &par) : Task(par)
5251         { type = TASK_JAVAC; name = "javac"; }
5253     virtual ~TaskJavac()
5254         {}
5256     virtual bool execute()
5257         {
5258         return true;
5259         }
5261     virtual bool parse(Element *elem)
5262         {
5263         return true;
5264         }
5265 };
5268 /**
5269  *
5270  */
5271 class TaskLink : public Task
5273 public:
5275     TaskLink(MakeBase &par) : Task(par)
5276         {
5277                 type = TASK_LINK; name = "link";
5278                 command = "g++";
5279                 }
5281     virtual ~TaskLink()
5282         {}
5284     virtual bool execute()
5285         {
5286         bool doit = false;
5287         String fullTarget = parent.resolve(fileName);
5288         String cmd = command;
5289         cmd.append(" -o ");
5290         cmd.append(fullTarget);
5291         cmd.append(" ");
5292         cmd.append(flags);
5293         for (unsigned int i=0 ; i<fileSet.size() ; i++)
5294             {
5295             cmd.append(" ");
5296             String obj;
5297             if (fileSetDir.size()>0)
5298                             {
5299                                 obj.append(fileSetDir);
5300                 obj.append("/");
5301                 }
5302             obj.append(fileSet[i]);
5303             String fullObj = parent.resolve(obj);
5304             cmd.append(fullObj);
5305             if (isNewerThan(fullObj, fullTarget))
5306                 doit = true;
5307             }
5308         cmd.append(" ");
5309         cmd.append(libs);
5310         if (!doit)
5311             {
5312             //trace("link not needed");
5313             return true;
5314             }
5315         //trace("LINK cmd:%s", cmd.c_str());
5318         String outString, errString;
5319         if (!executeCommand(cmd.c_str(), "", outString, errString))
5320             {
5321             error("LINK problem: %s", errString.c_str());
5322             return false;
5323             }
5324         return true;
5325         }
5327     virtual bool parse(Element *elem)
5328         {
5329         if (!parent.getAttribute(elem, "command", command))
5330             return false;
5331         if (!parent.getAttribute(elem, "out", fileName))
5332             return false;
5333             
5334         std::vector<Element *> children = elem->getChildren();
5335         for (unsigned int i=0 ; i<children.size() ; i++)
5336             {
5337             Element *child = children[i];
5338             String tagName = child->getName();
5339             if (tagName == "fileset")
5340                 {
5341                 if (!getFileSet(child, parent, fileSetDir, fileSet))
5342                     return false;
5343                 }
5344             else if (tagName == "flags")
5345                 {
5346                 if (!parent.getValue(child, flags))
5347                     return false;
5348                 }
5349             else if (tagName == "libs")
5350                 {
5351                 if (!parent.getValue(child, libs))
5352                     return false;
5353                 }
5354             }
5355         return true;
5356         }
5358 private:
5360     String command;
5361     String fileName;
5362     String flags;
5363     String libs;
5364     String fileSetDir;
5365     std::vector<String> fileSet;
5367 };
5371 /**
5372  * Create a named directory
5373  */
5374 class TaskMkDir : public Task
5376 public:
5378     TaskMkDir(MakeBase &par) : Task(par)
5379         { type = TASK_MKDIR; name = "mkdir"; }
5381     virtual ~TaskMkDir()
5382         {}
5384     virtual bool execute()
5385         {
5386         status("          : %s", dirName.c_str());
5387         String fullDir = parent.resolve(dirName);
5388         //trace("fullDir:%s", fullDir.c_str());
5389         if (!createDirectory(fullDir))
5390             return false;
5391         return true;
5392         }
5394     virtual bool parse(Element *elem)
5395         {
5396         if (!parent.getAttribute(elem, "dir", dirName))
5397             return false;
5398         if (dirName.size() == 0)
5399             {
5400             error("<mkdir> requires 'dir=\"dirname\"' attribute");
5401             return false;
5402             }
5403         //trace("dirname:%s", dirName.c_str());
5404         return true;
5405         }
5407 private:
5409     String dirName;
5410 };
5414 /**
5415  * Create a named directory
5416  */
5417 class TaskMsgFmt: public Task
5419 public:
5421     TaskMsgFmt(MakeBase &par) : Task(par)
5422          {
5423                  type = TASK_MSGFMT;
5424                  name = "msgfmt";
5425                  command = "msgfmt";
5426                  }
5428     virtual ~TaskMsgFmt()
5429         {}
5431     virtual bool execute()
5432         {
5433         //trace("msgfmt: %d", fileSet.size());
5434         for (unsigned int i=0 ; i<fileSet.size() ; i++)
5435             {
5436             String fileName = fileSet[i];
5437             if (getSuffix(fileName) != "po")
5438                 continue;
5439             String sourcePath;
5440                         if (fileSetDir.size()>0)
5441                             {
5442                             sourcePath.append(fileSetDir);
5443                 sourcePath.append("/");
5444                 }
5445             sourcePath.append(fileName);
5446             String fullSource = parent.resolve(sourcePath);
5448             String destPath;
5449                         if (toDirName.size()>0)
5450                             {
5451                             destPath.append(toDirName);
5452                 destPath.append("/");
5453                 }
5454             destPath.append(fileName);
5455             destPath[destPath.size()-2] = 'm';
5456             String fullDest = parent.resolve(destPath);
5458             if (!isNewerThan(fullSource, fullDest))
5459                 {
5460                 //trace("skip %s", fullSource.c_str());
5461                 continue;
5462                 }
5463                 
5464             String cmd = command;
5465             cmd.append(" ");
5466             cmd.append(fullSource);
5467             cmd.append(" -o ");
5468             cmd.append(fullDest);
5469             
5470             int pos = fullDest.find_last_of('/');
5471             if (pos>0)
5472                 {
5473                 String fullDestPath = fullDest.substr(0, pos);
5474                 if (!createDirectory(fullDestPath))
5475                     return false;
5476                 }
5480             String outString, errString;
5481             if (!executeCommand(cmd.c_str(), "", outString, errString))
5482                 {
5483                 error("<msgfmt> problem: %s", errString.c_str());
5484                 return false;
5485                 }
5486             }
5488         return true;
5489         }
5491     virtual bool parse(Element *elem)
5492         {
5493         if (!parent.getAttribute(elem, "todir", toDirName))
5494             return false;
5495             
5496         std::vector<Element *> children = elem->getChildren();
5497         for (unsigned int i=0 ; i<children.size() ; i++)
5498             {
5499             Element *child = children[i];
5500             String tagName = child->getName();
5501             if (tagName == "fileset")
5502                 {
5503                 if (!getFileSet(child, parent, fileSetDir, fileSet))
5504                     return false;
5505                 }
5506             }
5507         return true;
5508         }
5510 private:
5512     String command;
5513     String toDirName;
5514     String fileSetDir;
5515     std::vector<String> fileSet;
5517 };
5523 /**
5524  *  Process an archive to allow random access
5525  */
5526 class TaskRanlib : public Task
5528 public:
5530     TaskRanlib(MakeBase &par) : Task(par)
5531         { type = TASK_RANLIB; name = "ranlib"; }
5533     virtual ~TaskRanlib()
5534         {}
5536     virtual bool execute()
5537         {
5538         String fullName = parent.resolve(fileName);
5539         //trace("fullDir:%s", fullDir.c_str());
5540         String cmd = "ranlib ";
5541         cmd.append(fullName);
5542         String outbuf, errbuf;
5543         if (!executeCommand(cmd, "", outbuf, errbuf))
5544             return false;
5545         return true;
5546         }
5548     virtual bool parse(Element *elem)
5549         {
5550         if (!parent.getAttribute(elem, "file", fileName))
5551             return false;
5552         if (fileName.size() == 0)
5553             {
5554             error("<ranlib> requires 'file=\"fileNname\"' attribute");
5555             return false;
5556             }
5557         return true;
5558         }
5560 private:
5562     String fileName;
5563 };
5567 /**
5568  * Run the "ar" command to archive .o's into a .a
5569  */
5570 class TaskRC : public Task
5572 public:
5574     TaskRC(MakeBase &par) : Task(par)
5575         {
5576                 type = TASK_RC; name = "rc";
5577                 command = "windres -o";
5578                 }
5580     virtual ~TaskRC()
5581         {}
5583     virtual bool execute()
5584         {
5585         String fullFile = parent.resolve(fileName);
5586         String fullOut  = parent.resolve(outName);
5587         if (!isNewerThan(fullFile, fullOut))
5588             return true;
5589         String cmd = command;
5590         cmd.append(" ");
5591         cmd.append(fullOut);
5592         cmd.append(" ");
5593         cmd.append(flags);
5594         cmd.append(" ");
5595         cmd.append(fullFile);
5597         String outString, errString;
5598         if (!executeCommand(cmd.c_str(), "", outString, errString))
5599             {
5600             error("RC problem: %s", errString.c_str());
5601             return false;
5602             }
5603         return true;
5604         }
5606     virtual bool parse(Element *elem)
5607         {
5608         if (!parent.getAttribute(elem, "command", command))
5609             return false;
5610         if (!parent.getAttribute(elem, "file", fileName))
5611             return false;
5612         if (!parent.getAttribute(elem, "out", outName))
5613             return false;
5614         std::vector<Element *> children = elem->getChildren();
5615         for (unsigned int i=0 ; i<children.size() ; i++)
5616             {
5617             Element *child = children[i];
5618             String tagName = child->getName();
5619             if (tagName == "flags")
5620                 {
5621                 if (!parent.getValue(child, flags))
5622                     return false;
5623                 }
5624             }
5625         return true;
5626         }
5628 private:
5630     String command;
5631     String flags;
5632     String fileName;
5633     String outName;
5635 };
5639 /**
5640  * Strip an executable
5641  */
5642 class TaskStrip : public Task
5644 public:
5646     TaskStrip(MakeBase &par) : Task(par)
5647         { type = TASK_STRIP; name = "strip"; }
5649     virtual ~TaskStrip()
5650         {}
5652     virtual bool execute()
5653         {
5654         String fullName = parent.resolve(fileName);
5655         //trace("fullDir:%s", fullDir.c_str());
5656         String cmd = "strip ";
5657         cmd.append(fullName);
5659         String outbuf, errbuf;
5660         if (!executeCommand(cmd, "", outbuf, errbuf))
5661             return false;
5662         return true;
5663         }
5665     virtual bool parse(Element *elem)
5666         {
5667         if (!parent.getAttribute(elem, "file", fileName))
5668             return false;
5669         if (fileName.size() == 0)
5670             {
5671             error("<strip> requires 'file=\"fileNname\"' attribute");
5672             return false;
5673             }
5674         return true;
5675         }
5677 private:
5679     String fileName;
5680 };
5683 /**
5684  *
5685  */
5686 class TaskTstamp : public Task
5688 public:
5690     TaskTstamp(MakeBase &par) : Task(par)
5691         { type = TASK_TSTAMP; name = "tstamp"; }
5693     virtual ~TaskTstamp()
5694         {}
5696     virtual bool execute()
5697         {
5698         return true;
5699         }
5701     virtual bool parse(Element *elem)
5702         {
5703         trace("tstamp parse");
5704         return true;
5705         }
5706 };
5710 /**
5711  *
5712  */
5713 Task *Task::createTask(Element *elem)
5715     String tagName = elem->getName();
5716     //trace("task:%s", tagName.c_str());
5717     Task *task = NULL;
5718     if (tagName == "ar")
5719         task = new TaskAr(parent);
5720     else if (tagName == "cc")
5721         task = new TaskCC(parent);
5722     else if (tagName == "copy")
5723         task = new TaskCopy(parent);
5724     else if (tagName == "delete")
5725         task = new TaskDelete(parent);
5726     else if (tagName == "jar")
5727         task = new TaskJar(parent);
5728     else if (tagName == "javac")
5729         task = new TaskJavac(parent);
5730     else if (tagName == "link")
5731         task = new TaskLink(parent);
5732     else if (tagName == "mkdir")
5733         task = new TaskMkDir(parent);
5734     else if (tagName == "msgfmt")
5735         task = new TaskMsgFmt(parent);
5736     else if (tagName == "ranlib")
5737         task = new TaskRanlib(parent);
5738     else if (tagName == "rc")
5739         task = new TaskRC(parent);
5740     else if (tagName == "strip")
5741         task = new TaskStrip(parent);
5742     else if (tagName == "tstamp")
5743         task = new TaskTstamp(parent);
5744     else
5745         {
5746         error("Unknown task '%s'", tagName.c_str());
5747         return NULL;
5748         }
5750     if (!task->parse(elem))
5751         {
5752         delete task;
5753         return NULL;
5754         }
5755     return task;
5760 //########################################################################
5761 //# T A R G E T
5762 //########################################################################
5764 /**
5765  *
5766  */
5767 class Target : public MakeBase
5770 public:
5772     /**
5773      *
5774      */
5775     Target(Make &par) : parent(par)
5776         { init(); }
5778     /**
5779      *
5780      */
5781     Target(const Target &other) : parent(other.parent)
5782         { init(); assign(other); }
5784     /**
5785      *
5786      */
5787     Target &operator=(const Target &other)
5788         { init(); assign(other); return *this; }
5790     /**
5791      *
5792      */
5793     virtual ~Target()
5794         { cleanup() ; }
5797     /**
5798      *
5799      */
5800     virtual Make &getParent()
5801         { return parent; }
5803     /**
5804      *
5805      */
5806     virtual String getName()
5807         { return name; }
5809     /**
5810      *
5811      */
5812     virtual void setName(const String &val)
5813         { name = val; }
5815     /**
5816      *
5817      */
5818     virtual String getDescription()
5819         { return description; }
5821     /**
5822      *
5823      */
5824     virtual void setDescription(const String &val)
5825         { description = val; }
5827     /**
5828      *
5829      */
5830     virtual void addDependency(const String &val)
5831         { deps.push_back(val); }
5833     /**
5834      *
5835      */
5836     virtual void parseDependencies(const String &val)
5837         { deps = tokenize(val, ", "); }
5839     /**
5840      *
5841      */
5842     virtual std::vector<String> &getDependencies()
5843         { return deps; }
5845     /**
5846      *
5847      */
5848     virtual String getIf()
5849         { return ifVar; }
5851     /**
5852      *
5853      */
5854     virtual void setIf(const String &val)
5855         { ifVar = val; }
5857     /**
5858      *
5859      */
5860     virtual String getUnless()
5861         { return unlessVar; }
5863     /**
5864      *
5865      */
5866     virtual void setUnless(const String &val)
5867         { unlessVar = val; }
5869     /**
5870      *
5871      */
5872     virtual void addTask(Task *val)
5873         { tasks.push_back(val); }
5875     /**
5876      *
5877      */
5878     virtual std::vector<Task *> &getTasks()
5879         { return tasks; }
5881 private:
5883     void init()
5884         {
5885         }
5887     void cleanup()
5888         {
5889         tasks.clear();
5890         }
5892     void assign(const Target &other)
5893         {
5894         //parent      = other.parent;
5895         name        = other.name;
5896         description = other.description;
5897         ifVar       = other.ifVar;
5898         unlessVar   = other.unlessVar;
5899         deps        = other.deps;
5900         tasks       = other.tasks;
5901         }
5903     Make &parent;
5905     String name;
5907     String description;
5909     String ifVar;
5911     String unlessVar;
5913     std::vector<String> deps;
5915     std::vector<Task *> tasks;
5917 };
5926 //########################################################################
5927 //# M A K E
5928 //########################################################################
5931 /**
5932  *
5933  */
5934 class Make : public MakeBase
5937 public:
5939     /**
5940      *
5941      */
5942     Make()
5943         { init(); }
5945     /**
5946      *
5947      */
5948     Make(const Make &other)
5949         { assign(other); }
5951     /**
5952      *
5953      */
5954     Make &operator=(const Make &other)
5955         { assign(other); return *this; }
5957     /**
5958      *
5959      */
5960     virtual ~Make()
5961         { cleanup(); }
5963     /**
5964      *
5965      */
5966     virtual std::map<String, Target> &getTargets()
5967         { return targets; }
5970     /**
5971      *
5972      */
5973     bool run();
5975     /**
5976      *
5977      */
5978     bool run(const String &target);
5982 private:
5984     /**
5985      *
5986      */
5987     void init();
5989     /**
5990      *
5991      */
5992     void cleanup();
5994     /**
5995      *
5996      */
5997     void assign(const Make &other);
5999     /**
6000      *
6001      */
6002     bool executeTask(Task &task);
6005     /**
6006      *
6007      */
6008     bool executeTarget(Target &target,
6009                  std::set<String> &targetsCompleted);
6012     /**
6013      *
6014      */
6015     bool execute();
6017     /**
6018      *
6019      */
6020     bool checkTargetDependencies(Target &prop,
6021                     std::vector<String> &depList);
6023     /**
6024      *
6025      */
6026     bool parsePropertyFile(const String &fileName,
6027                                const String &prefix);
6029     /**
6030      *
6031      */
6032     bool parseProperty(Element *elem);
6034     /**
6035      *
6036      */
6037     bool parseTask(Task &task, Element *elem);
6039     /**
6040      *
6041      */
6042     bool parseFile();
6044     /**
6045      *
6046      */
6047     std::vector<String> glob(const String &pattern);
6050     //###############
6051     //# Fields
6052     //###############
6054     String projectName;
6056     String currentTarget;
6058     String defaultTarget;
6060     String specifiedTarget;
6062     String baseDir;
6064     String description;
6065     
6066     String envAlias;
6068     //std::vector<Property> properties;
6069     
6070     std::map<String, Target> targets;
6072     std::vector<Task *> allTasks;
6075 };
6078 //########################################################################
6079 //# C L A S S  M A I N T E N A N C E
6080 //########################################################################
6082 /**
6083  *
6084  */
6085 void Make::init()
6087     uri             = "build.xml";
6088     projectName     = "";
6089     currentTarget   = "";
6090     defaultTarget   = "";
6091     specifiedTarget = "";
6092     baseDir         = "";
6093     description     = "";
6094     envAlias        = "";
6095     properties.clear();
6096     for (unsigned int i = 0 ; i < allTasks.size() ; i++)
6097         delete allTasks[i];
6098     allTasks.clear();
6103 /**
6104  *
6105  */
6106 void Make::cleanup()
6108     for (unsigned int i = 0 ; i < allTasks.size() ; i++)
6109         delete allTasks[i];
6110     allTasks.clear();
6115 /**
6116  *
6117  */
6118 void Make::assign(const Make &other)
6120     uri              = other.uri;
6121     projectName      = other.projectName;
6122     currentTarget    = other.currentTarget;
6123     defaultTarget    = other.defaultTarget;
6124     specifiedTarget  = other.specifiedTarget;
6125     baseDir          = other.baseDir;
6126     description      = other.description;
6127     properties       = other.properties;
6132 //########################################################################
6133 //# U T I L I T Y    T A S K S
6134 //########################################################################
6136 /**
6137  *  Perform a file globbing
6138  */
6139 std::vector<String> Make::glob(const String &pattern)
6141     std::vector<String> res;
6142     return res;
6146 //########################################################################
6147 //# P U B L I C    A P I
6148 //########################################################################
6152 /**
6153  *
6154  */
6155 bool Make::executeTarget(Target &target,
6156              std::set<String> &targetsCompleted)
6159     String name = target.getName();
6161     //First get any dependencies for this target
6162     std::vector<String> deps = target.getDependencies();
6163     for (unsigned int i=0 ; i<deps.size() ; i++)
6164         {
6165         String dep = deps[i];
6166         //Did we do it already?  Skip
6167         if (targetsCompleted.find(dep)!=targetsCompleted.end())
6168             continue;
6169             
6170         std::map<String, Target> &tgts =
6171                target.getParent().getTargets();
6172         std::map<String, Target>::iterator iter =
6173                tgts.find(dep);
6174         if (iter == tgts.end())
6175             {
6176             error("Target '%s' dependency '%s' not found",
6177                       name.c_str(),  dep.c_str());
6178             return false;
6179             }
6180         Target depTarget = iter->second;
6181         if (!executeTarget(depTarget, targetsCompleted))
6182             {
6183             return false;
6184             }
6185         }
6187     status("## Target : %s", name.c_str());
6189     //Now let's do the tasks
6190     std::vector<Task *> &tasks = target.getTasks();
6191     for (unsigned int i=0 ; i<tasks.size() ; i++)
6192         {
6193         Task *task = tasks[i];
6194         status("---- task : %s", task->getName().c_str());
6195         if (!task->execute())
6196             {
6197             return false;
6198             }
6199         }
6200         
6201     targetsCompleted.insert(name);
6202     
6203     return true;
6208 /**
6209  *  Main execute() method.  Start here and work
6210  *  up the dependency tree 
6211  */
6212 bool Make::execute()
6214     status("######## EXECUTE");
6216     //Determine initial target
6217     if (specifiedTarget.size()>0)
6218         {
6219         currentTarget = specifiedTarget;
6220         }
6221     else if (defaultTarget.size()>0)
6222         {
6223         currentTarget = defaultTarget;
6224         }
6225     else
6226         {
6227         error("execute: no specified or default target requested");
6228         return false;
6229         }
6231     std::map<String, Target>::iterator iter =
6232                    targets.find(currentTarget);
6233     if (iter == targets.end())
6234         {
6235         error("Initial target '%s' not found",
6236                          currentTarget.c_str());
6237         return false;
6238         }
6239         
6240     //Now run
6241     Target target = iter->second;
6242     std::set<String> targetsCompleted;
6243     if (!executeTarget(target, targetsCompleted))
6244         {
6245         return false;
6246         }
6248     status("######## EXECUTE COMPLETE");
6249     return true;
6255 /**
6256  *
6257  */
6258 bool Make::checkTargetDependencies(Target &target, 
6259                             std::vector<String> &depList)
6261     String tgtName = target.getName().c_str();
6262     depList.push_back(tgtName);
6264     std::vector<String> deps = target.getDependencies();
6265     for (unsigned int i=0 ; i<deps.size() ; i++)
6266         {
6267         String dep = deps[i];
6268         //First thing entered was the starting Target
6269         if (dep == depList[0])
6270             {
6271             error("Circular dependency '%s' found at '%s'",
6272                       dep.c_str(), tgtName.c_str());
6273             std::vector<String>::iterator diter;
6274             for (diter=depList.begin() ; diter!=depList.end() ; diter++)
6275                 {
6276                 error("  %s", diter->c_str());
6277                 }
6278             return false;
6279             }
6281         std::map<String, Target> &tgts =
6282                   target.getParent().getTargets();
6283         std::map<String, Target>::iterator titer = tgts.find(dep);
6284         if (titer == tgts.end())
6285             {
6286             error("Target '%s' dependency '%s' not found",
6287                       tgtName.c_str(), dep.c_str());
6288             return false;
6289             }
6290         if (!checkTargetDependencies(titer->second, depList))
6291             {
6292             return false;
6293             }
6294         }
6295     return true;
6302 static int getword(int pos, const String &inbuf, String &result)
6304     int p = pos;
6305     int len = (int)inbuf.size();
6306     String val;
6307     while (p < len)
6308         {
6309         char ch = inbuf[p];
6310         if (!isalnum(ch) && ch!='.' && ch!='_')
6311             break;
6312         val.push_back(ch);
6313         p++;
6314         }
6315     result = val;
6316     return p;
6322 /**
6323  *
6324  */
6325 bool Make::parsePropertyFile(const String &fileName,
6326                              const String &prefix)
6328     FILE *f = fopen(fileName.c_str(), "r");
6329     if (!f)
6330         {
6331         error("could not open property file %s", fileName.c_str());
6332         return false;
6333         }
6334     int linenr = 0;
6335     while (!feof(f))
6336         {
6337         char buf[256];
6338         if (!fgets(buf, 255, f))
6339             break;
6340         linenr++;
6341         String s = buf;
6342         s = trim(s);
6343         int len = s.size();
6344         if (len == 0)
6345             continue;
6346         if (s[0] == '#')
6347             continue;
6348         String key;
6349         String val;
6350         int p = 0;
6351         int p2 = getword(p, s, key);
6352         if (p2 <= p)
6353             {
6354             error("property file %s, line %d: expected keyword",
6355                                 fileName.c_str(), linenr);
6356                         return false;
6357                         }
6358                 if (prefix.size() > 0)
6359                     {
6360                     key.insert(0, prefix);
6361                     }
6363         //skip whitespace
6364                 for (p=p2 ; p<len ; p++)
6365                     if (!isspace(s[p]))
6366                         break;
6368         if (p>=len || s[p]!='=')
6369             {
6370             error("property file %s, line %d: expected '='",
6371                                 fileName.c_str(), linenr);
6372             return false;
6373             }
6374         p++;
6376         //skip whitespace
6377                 for ( ; p<len ; p++)
6378                     if (!isspace(s[p]))
6379                         break;
6381         /* This way expects a word after the =
6382                 p2 = getword(p, s, val);
6383         if (p2 <= p)
6384             {
6385             error("property file %s, line %d: expected value",
6386                                 fileName.c_str(), linenr);
6387                         return false;
6388                         }
6389                 */
6390         // This way gets the rest of the line after the =
6391                 if (p>=len)
6392             {
6393             error("property file %s, line %d: expected value",
6394                                 fileName.c_str(), linenr);
6395                         return false;
6396                         }
6397         val = s.substr(p);
6398                 if (key.size()==0 || val.size()==0)
6399                     continue;
6401         //trace("key:'%s' val:'%s'", key.c_str(), val.c_str());
6402             properties[key] = val;
6403         }
6404     fclose(f);
6405     return true;
6411 /**
6412  *
6413  */
6414 bool Make::parseProperty(Element *elem)
6416     std::vector<Attribute> &attrs = elem->getAttributes();
6417     for (unsigned int i=0 ; i<attrs.size() ; i++)
6418         {
6419         String attrName = attrs[i].getName();
6420         String attrVal  = attrs[i].getValue();
6422         if (attrName == "name")
6423             {
6424             String val;
6425                         if (!getAttribute(elem, "value", val))
6426                             return false;
6427             if (val.size() > 0)
6428                 {
6429                 properties[attrVal] = val;
6430                 continue;
6431                 }
6432             if (!getAttribute(elem, "location", val))
6433                 return false;
6434             if (val.size() > 0)
6435                 {
6436                 //TODO:  process a path relative to build.xml
6437                 properties[attrVal] = val;
6438                 continue;
6439                 }
6440             }
6441         else if (attrName == "file")
6442             {
6443             String prefix;
6444                         if (!getAttribute(elem, "prefix", prefix))
6445                             return false;
6446             if (prefix.size() > 0)
6447                 {
6448                 if (prefix[prefix.size()-1] != '.')
6449                     prefix.push_back('.');
6450                 }
6451             if (!parsePropertyFile(attrName, prefix))
6452                 return false;
6453             }
6454         else if (attrName == "environment")
6455             {
6456             if (envAlias.size() > 0)
6457                 {
6458                 error("environment property can only be set once");
6459                 return false;
6460                 }
6461             envAlias = attrVal;
6462             }
6463         }
6465     return true;
6471 /**
6472  *
6473  */
6474 bool Make::parseFile()
6476     status("######## PARSE");
6478     Parser parser;
6479     Element *root = parser.parseFile(uri.getNativePath());
6480     if (!root)
6481         {
6482         error("Could not open %s for reading",
6483                       uri.getNativePath().c_str());
6484         return false;
6485         }
6487     if (root->getChildren().size()==0 ||
6488         root->getChildren()[0]->getName()!="project")
6489         {
6490         error("Main xml element should be <project>");
6491         delete root;
6492         return false;
6493         }
6495     //########## Project attributes
6496     Element *project = root->getChildren()[0];
6497     String s = project->getAttribute("name");
6498     if (s.size() > 0)
6499         projectName = s;
6500     s = project->getAttribute("default");
6501     if (s.size() > 0)
6502         defaultTarget = s;
6503     s = project->getAttribute("basedir");
6504     if (s.size() > 0)
6505         baseDir = s;
6507     //######### PARSE MEMBERS
6508     std::vector<Element *> children = project->getChildren();
6509     for (unsigned int i=0 ; i<children.size() ; i++)
6510         {
6511         Element *elem = children[i];
6512         String tagName = elem->getName();
6514         //########## DESCRIPTION
6515         if (tagName == "description")
6516             {
6517             description = parser.trim(elem->getValue());
6518             }
6520         //######### PROPERTY
6521         else if (tagName == "property")
6522             {
6523             if (!parseProperty(elem))
6524                 return false;
6525             }
6527         //######### TARGET
6528         else if (tagName == "target")
6529             {
6530             String tname   = elem->getAttribute("name");
6531             String tdesc   = elem->getAttribute("description");
6532             String tdeps   = elem->getAttribute("depends");
6533             String tif     = elem->getAttribute("if");
6534             String tunless = elem->getAttribute("unless");
6535             Target target(*this);
6536             target.setName(tname);
6537             target.setDescription(tdesc);
6538             target.parseDependencies(tdeps);
6539             target.setIf(tif);
6540             target.setUnless(tunless);
6541             std::vector<Element *> telems = elem->getChildren();
6542             for (unsigned int i=0 ; i<telems.size() ; i++)
6543                 {
6544                 Element *telem = telems[i];
6545                 Task breeder(*this);
6546                 Task *task = breeder.createTask(telem);
6547                 if (!task)
6548                     return false;
6549                 allTasks.push_back(task);
6550                 target.addTask(task);
6551                 }
6553             //Check name
6554             if (tname.size() == 0)
6555                 {
6556                 error("no name for target");
6557                 return false;
6558                 }
6559             //Check for duplicate name
6560             if (targets.find(tname) != targets.end())
6561                 {
6562                 error("target '%s' already defined", tname.c_str());
6563                 return false;
6564                 }
6565             //more work than targets[tname]=target, but avoids default allocator
6566             targets.insert(std::make_pair<String, Target>(tname, target));
6567             }
6569         }
6571     std::map<String, Target>::iterator iter;
6572     for (iter = targets.begin() ; iter!= targets.end() ; iter++)
6573         {
6574         Target tgt = iter->second;
6575         std::vector<String> depList;
6576         if (!checkTargetDependencies(tgt, depList))
6577             {
6578             return false;
6579             }
6580         }
6583     delete root;
6584     status("######## PARSE COMPLETE");
6585     return true;
6589 /**
6590  *
6591  */
6592 bool Make::run()
6594     if (!parseFile())
6595         return false;
6596     if (!execute())
6597         return false;
6598     return true;
6602 /**
6603  *
6604  */
6605 bool Make::run(const String &target)
6607     status("##################################");
6608     status("#   BuildTool");
6609     status("#   version 0.2");
6610     status("##################################");
6611     specifiedTarget = target;
6612     if (!run())
6613         return false;
6614     status("##################################");
6615     status("#   BuildTool Completed");
6616     status("##################################");
6617     return true;
6626 }// namespace buildtool
6627 //########################################################################
6628 //# M A I N
6629 //########################################################################
6631 /**
6632  *  Format an error message in printf() style
6633  */
6634 static void error(char *fmt, ...)
6636     va_list ap;
6637     va_start(ap, fmt);
6638     fprintf(stderr, "BuildTool error: ");
6639     vfprintf(stderr, fmt, ap);
6640     fprintf(stderr, "\n");
6641     va_end(ap);
6645 /**
6646  * Compare a buffer with a key, for the length of the key
6647  */
6648 static bool sequ(const buildtool::String &buf, char *key)
6650     for (int i=0 ; key[i] ; i++)
6651         {
6652         if (key[i] != buf[i])
6653             return false;
6654         }        
6655     return true;
6658 /**
6659  * Parse the command-line args, get our options,
6660  * and run this thing
6661  */   
6662 static bool parseOptions(int argc, char **argv)
6664     if (argc < 1)
6665         {
6666         error("Cannot parse arguments");
6667         return false;
6668         }
6670     buildtool::String buildFile;
6671     buildtool::String target;
6673     //char *progName = argv[0];
6674     for (int i=1 ; i<argc ; i++)
6675         {
6676         buildtool::String arg = argv[i];
6677         if (sequ(arg, "--"))
6678             {
6679             if (sequ(arg, "--file=") && arg.size()>7)
6680                 {
6681                 buildFile = arg.substr(7, arg.size()-7);
6682                 }
6683             else
6684                 {
6685                 error("Unknown option:%s", arg.c_str());
6686                 return false;
6687                 }
6688             }
6689         else if (sequ(arg, "-"))
6690             {
6691             for (unsigned int p=1 ; p<arg.size() ; p++)
6692                 {
6693                 int ch = arg[p];
6694                 if (0)//put options here
6695                     {
6696                     }
6697                 else
6698                     {
6699                     error("Unknown option '%c'", ch);
6700                     return false;
6701                     }
6702                 }
6703             }
6704         else
6705             {
6706             target = arg;
6707             }
6708         }
6710     //We have the options.  Now execute them
6711     buildtool::Make make;
6712     if (buildFile.size() > 0)
6713         {
6714         make.setURI(buildFile);
6715         }
6716     if (!make.run(target))
6717         return false;
6719     return true;
6725 /*
6726 static bool runMake()
6728     buildtool::Make make;
6729     if (!make.run())
6730         return false;
6731     return true;
6735 static bool pkgConfigTest()
6737     buildtool::PkgConfig pkgConfig;
6738     if (!pkgConfig.readFile("gtk+-2.0.pc"))
6739         return false;
6740     return true;
6745 static bool depTest()
6747     buildtool::DepTool deptool;
6748     deptool.setSourceDirectory("/dev/ink/inkscape/src");
6749     if (!deptool.generateDependencies("build.dep"))
6750         return false;
6751     std::vector<buildtool::DepRec> res =
6752                deptool.loadDepFile("build.dep");
6753         if (res.size() == 0)
6754         return false;
6755     return true;
6758 static bool popenTest()
6760     buildtool::Make make;
6761     buildtool::String out, err;
6762         bool ret = make.executeCommand("gcc xx.cpp", "", out, err);
6763     printf("Popen test:%d '%s' '%s'\n", ret, out.c_str(), err.c_str());
6764     return true;
6768 static bool propFileTest()
6770     buildtool::Make make;
6771     make.parsePropertyFile("test.prop", "test.");
6772     return true;
6774 */
6776 int main(int argc, char **argv)
6779     if (!parseOptions(argc, argv))
6780         return 1;
6781     /*
6782     if (!popenTest())
6783         return 1;
6785     if (!depTest())
6786         return 1;
6787     if (!propFileTest())
6788         return 1;
6789     if (runMake())
6790         return 1;
6791     */
6792     return 0;
6796 //########################################################################
6797 //# E N D 
6798 //########################################################################