Code

library tweaks
[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                 String vpath = iter->path;
4420                 vpath.append("/");
4421                 vpath.append(iter->name);
4422                 String opath = depObject.path;
4423                 opath.append("/");
4424                 opath.append(depObject.name);
4425                 if (vpath > opath)
4426                     {
4427                     inserted = true;
4428                     iter = result.insert(iter, depObject);
4429                     break;
4430                     }
4431                 }
4432             if (!inserted)
4433                 result.push_back(depObject);
4434             }
4435         }
4437     delete root;
4439     return result;
4443 /**
4444  *   This loads the dependency cache.
4445  */
4446 std::vector<DepRec> DepTool::getDepFile(const String &depFile)
4448     std::vector<DepRec> result = loadDepFile(depFile);
4449     if (result.size() == 0)
4450         {
4451         generateDependencies(depFile);
4452         result = loadDepFile(depFile);
4453         }
4454     return result;
4460 //########################################################################
4461 //# T A S K
4462 //########################################################################
4463 //forward decl
4464 class Target;
4465 class Make;
4467 /**
4468  *
4469  */
4470 class Task : public MakeBase
4473 public:
4475     typedef enum
4476         {
4477         TASK_NONE,
4478         TASK_AR,
4479         TASK_CC,
4480         TASK_COPY,
4481         TASK_DELETE,
4482         TASK_JAR,
4483         TASK_JAVAC,
4484         TASK_LINK,
4485         TASK_MKDIR,
4486         TASK_MSGFMT,
4487         TASK_RANLIB,
4488         TASK_RC,
4489         TASK_STRIP,
4490         TASK_TSTAMP
4491         } TaskType;
4492         
4494     /**
4495      *
4496      */
4497     Task(MakeBase &par) : parent(par)
4498         { init(); }
4500     /**
4501      *
4502      */
4503     Task(const Task &other) : parent(other.parent)
4504         { init(); assign(other); }
4506     /**
4507      *
4508      */
4509     Task &operator=(const Task &other)
4510         { assign(other); return *this; }
4512     /**
4513      *
4514      */
4515     virtual ~Task()
4516         { }
4519     /**
4520      *
4521      */
4522     virtual MakeBase &getParent()
4523         { return parent; }
4525      /**
4526      *
4527      */
4528     virtual int  getType()
4529         { return type; }
4531     /**
4532      *
4533      */
4534     virtual void setType(int val)
4535         { type = val; }
4537     /**
4538      *
4539      */
4540     virtual String getName()
4541         { return name; }
4543     /**
4544      *
4545      */
4546     virtual bool execute()
4547         { return true; }
4549     /**
4550      *
4551      */
4552     virtual bool parse(Element *elem)
4553         { return true; }
4555     /**
4556      *
4557      */
4558     Task *createTask(Element *elem);
4561 protected:
4563     void init()
4564         {
4565         type = TASK_NONE;
4566         name = "none";
4567         }
4569     void assign(const Task &other)
4570         {
4571         type = other.type;
4572         name = other.name;
4573         }
4574         
4575     String getAttribute(Element *elem, const String &attrName)
4576         {
4577         String str;
4578         return str;
4579         }
4581     MakeBase &parent;
4583     int type;
4585     String name;
4586 };
4591 /**
4592  * Run the "ar" command to archive .o's into a .a
4593  */
4594 class TaskAr : public Task
4596 public:
4598     TaskAr(MakeBase &par) : Task(par)
4599         {
4600                 type = TASK_AR; name = "ar";
4601                 command = "ar crv";
4602                 }
4604     virtual ~TaskAr()
4605         {}
4607     virtual bool execute()
4608         {
4609         //trace("###########HERE %d", fileSet.size());
4610         bool doit = false;
4611         
4612         String fullOut = parent.resolve(fileName);
4613         //trace("ar fullout: %s", fullOut.c_str());
4614         
4616         for (unsigned int i=0 ; i<fileSet.size() ; i++)
4617             {
4618             String fname;
4619                         if (fileSetDir.size()>0)
4620                             {
4621                             fname.append(fileSetDir);
4622                 fname.append("/");
4623                 }
4624             fname.append(fileSet[i]);
4625             String fullName = parent.resolve(fname);
4626             //trace("ar : %s/%s", fullOut.c_str(), fullName.c_str());
4627             if (isNewerThan(fullName, fullOut))
4628                 doit = true;
4629             }
4630         //trace("Needs it:%d", doit);
4631         if (!doit)
4632             {
4633             return true;
4634             }
4636         String cmd = command;
4637         cmd.append(" ");
4638         cmd.append(fullOut);
4639         for (unsigned int i=0 ; i<fileSet.size() ; i++)
4640             {
4641             String fname;
4642                         if (fileSetDir.size()>0)
4643                             {
4644                             fname.append(fileSetDir);
4645                 fname.append("/");
4646                 }
4647             fname.append(fileSet[i]);
4648             String fullName = parent.resolve(fname);
4650             cmd.append(" ");
4651             cmd.append(fullName);
4652             }
4654         String outString, errString;
4655         if (!executeCommand(cmd.c_str(), "", outString, errString))
4656             {
4657             error("AR problem: %s", errString.c_str());
4658             return false;
4659             }
4661         return true;
4662         }
4664     virtual bool parse(Element *elem)
4665         {
4666         if (!parent.getAttribute(elem, "file", fileName))
4667             return false;
4668             
4669         std::vector<Element *> children = elem->getChildren();
4670         for (unsigned int i=0 ; i<children.size() ; i++)
4671             {
4672             Element *child = children[i];
4673             String tagName = child->getName();
4674             if (tagName == "fileset")
4675                 {
4676                 if (!getFileSet(child, parent, fileSetDir, fileSet))
4677                     return false;
4678                 }
4679             }
4680         return true;
4681         }
4683 private:
4685     String command;
4686     String fileName;
4687     String fileSetDir;
4688     std::vector<String> fileSet;
4690 };
4693 /**
4694  * This task runs the C/C++ compiler.  The compiler is invoked
4695  * for all .c or .cpp files which are newer than their correcsponding
4696  * .o files.  
4697  */
4698 class TaskCC : public Task
4700 public:
4702     TaskCC(MakeBase &par) : Task(par)
4703         {
4704                 type = TASK_CC; name = "cc";
4705                 ccCommand   = "gcc";
4706                 cxxCommand  = "g++";
4707                 source      = ".";
4708                 dest        = ".";
4709                 flags       = "";
4710                 defines     = "";
4711                 includes    = "";
4712                 sourceFiles.clear();
4713         }
4715     virtual ~TaskCC()
4716         {}
4718     virtual bool execute()
4719         {
4720         DepTool depTool;
4721         depTool.setSourceDirectory(source);
4722         depTool.setFileList(sourceFiles);
4723         std::vector<DepRec> deps = depTool.getDepFile("build.dep");
4724         
4725         String incs;
4726         incs.append("-I");
4727         incs.append(parent.resolve("."));
4728         incs.append(" ");
4729         if (includes.size()>0)
4730             {
4731             incs.append(includes);
4732             incs.append(" ");
4733             }
4734         std::set<String> paths;
4735         std::vector<DepRec>::iterator viter;
4736         for (viter=deps.begin() ; viter!=deps.end() ; viter++)
4737             {
4738             DepRec dep = *viter;
4739             if (dep.path.size()>0)
4740                 paths.insert(dep.path);
4741             }
4742         if (source.size()>0)
4743             {
4744             incs.append(" -I");
4745             incs.append(parent.resolve(source));
4746             incs.append(" ");
4747             }
4748         std::set<String>::iterator setIter;
4749         for (setIter=paths.begin() ; setIter!=paths.end() ; setIter++)
4750             {
4751             incs.append(" -I");
4752             String dname;
4753             if (source.size()>0)
4754                 {
4755                 dname.append(source);
4756                 dname.append("/");
4757                 }
4758             dname.append(*setIter);
4759             incs.append(parent.resolve(dname));
4760             }
4761         std::vector<String> cfiles;
4762         for (viter=deps.begin() ; viter!=deps.end() ; viter++)
4763             {
4764             DepRec dep = *viter;
4766             //## Select command
4767             String sfx = dep.suffix;
4768             String command = ccCommand;
4769             if (sfx == "cpp" || sfx == "c++" || sfx == "cc"
4770                              || sfx == "CC")
4771                             command = cxxCommand;
4772  
4773             //## Make paths
4774             String destPath = dest;
4775             String srcPath  = source;
4776             if (dep.path.size()>0)
4777                             {
4778                 destPath.append("/");
4779                                 destPath.append(dep.path);
4780                 srcPath.append("/");
4781                                 srcPath.append(dep.path);
4782                             }
4783             //## Make sure destination directory exists
4784                         if (!createDirectory(destPath))
4785                             return false;
4786                             
4787             //## Check whether it needs to be done
4788                         String destFullName = destPath;
4789                         destFullName.append("/");
4790                         destFullName.append(dep.name);
4791                         destFullName.append(".o");
4792                         String srcFullName = srcPath;
4793                         srcFullName.append("/");
4794                         srcFullName.append(dep.name);
4795                         srcFullName.append(".");
4796                         srcFullName.append(dep.suffix);
4797             if (!isNewerThan(srcFullName, destFullName))
4798                 {
4799                 //trace("%s skipped", srcFullName.c_str());
4800                 continue;
4801                 }
4803             //## Assemble the command
4804             String cmd = command;
4805             cmd.append(" -c ");
4806             cmd.append(flags);
4807                         cmd.append(" ");
4808             cmd.append(defines);
4809                         cmd.append(" ");
4810             cmd.append(incs);
4811                         cmd.append(" ");
4812                         cmd.append(srcFullName);
4813             cmd.append(" -o ");
4814                         cmd.append(destFullName);
4816             //## Execute the command
4818             String outString, errString;
4819             if (!executeCommand(cmd.c_str(), "", outString, errString))
4820                 {
4821                 error("problem compiling: %s", errString.c_str());
4822                 return false;
4823                 }
4824             }
4825         
4826         return true;
4827         }
4829     virtual bool parse(Element *elem)
4830         {
4831         String s;
4832         if (!parent.getAttribute(elem, "command", s))
4833             return false;
4834         if (s.size()>0) { ccCommand = s; cxxCommand = s; }
4835         if (!parent.getAttribute(elem, "cc", s))
4836             return false;
4837         if (s.size()>0) ccCommand = s;
4838         if (!parent.getAttribute(elem, "cxx", s))
4839             return false;
4840         if (s.size()>0) cxxCommand = s;
4841         if (!parent.getAttribute(elem, "destdir", s))
4842             return false;
4843         if (s.size()>0) dest = s;
4845         std::vector<Element *> children = elem->getChildren();
4846         for (unsigned int i=0 ; i<children.size() ; i++)
4847             {
4848             Element *child = children[i];
4849             String tagName = child->getName();
4850             if (tagName == "flags")
4851                 {
4852                 if (!parent.getValue(child, flags))
4853                     return false;
4854                 }
4855             else if (tagName == "includes")
4856                 {
4857                 if (!parent.getValue(child, includes))
4858                     return false;
4859                 }
4860             else if (tagName == "defines")
4861                 {
4862                 if (!parent.getValue(child, defines))
4863                     return false;
4864                 }
4865             else if (tagName == "fileset")
4866                 {
4867                 if (!getFileSet(child, parent, source, sourceFiles))
4868                     return false;
4869                 }
4870             }
4872         return true;
4873         }
4874         
4875 protected:
4877     String ccCommand;
4878     String cxxCommand;
4879     String source;
4880     String dest;
4881     String flags;
4882     String defines;
4883     String includes;
4884     std::vector<String> sourceFiles;
4885     
4886 };
4890 /**
4891  *
4892  */
4893 class TaskCopy : public Task
4895 public:
4897     typedef enum
4898         {
4899         CP_NONE,
4900         CP_TOFILE,
4901         CP_TODIR
4902         } CopyType;
4904     TaskCopy(MakeBase &par) : Task(par)
4905         {
4906                 type = TASK_COPY; name = "copy";
4907                 cptype = CP_NONE;
4908                 verbose = false;
4909                 haveFileSet = false;
4910                 }
4912     virtual ~TaskCopy()
4913         {}
4915     virtual bool execute()
4916         {
4917         switch (cptype)
4918            {
4919            case CP_TOFILE:
4920                {
4921                if (fileName.size()>0)
4922                    {
4923                    status("          : %s", fileName.c_str());
4924                    String fullSource = parent.resolve(fileName);
4925                    String fullDest = parent.resolve(toFileName);
4926                    //trace("copy %s to file %s", fullSource.c_str(),
4927                                    //                       fullDest.c_str());
4928                    if (!isNewerThan(fullSource, fullDest))
4929                        {
4930                        return true;
4931                        }
4932                    if (!copyFile(fullSource, fullDest))
4933                        return false;
4934                    status("          : 1 file copied");
4935                    }
4936                return true;
4937                }
4938            case CP_TODIR:
4939                {
4940                if (haveFileSet)
4941                    {
4942                    int nrFiles = 0;
4943                    status("          : %s", fileSetDir.c_str());
4944                    for (unsigned int i=0 ; i<fileSet.size() ; i++)
4945                        {
4946                        String fileName = fileSet[i];
4948                        String sourcePath;
4949                        if (fileSetDir.size()>0)
4950                            {
4951                            sourcePath.append(fileSetDir);
4952                            sourcePath.append("/");
4953                            }
4954                        sourcePath.append(fileName);
4955                        String fullSource = parent.resolve(sourcePath);
4956                        
4957                        //Get the immediate parent directory's base name
4958                        String baseFileSetDir = fileSetDir;
4959                        unsigned int pos = baseFileSetDir.find_last_of('/');
4960                        if (pos!=baseFileSetDir.npos &&
4961                                                       pos < baseFileSetDir.size()-1)
4962                            baseFileSetDir =
4963                                                       baseFileSetDir.substr(pos+1,
4964                                                                baseFileSetDir.size());
4965                                            //Now make the new path
4966                        String destPath;
4967                        if (toDirName.size()>0)
4968                            {
4969                            destPath.append(toDirName);
4970                            destPath.append("/");
4971                            }
4972                        if (baseFileSetDir.size()>0)
4973                            {
4974                            destPath.append(baseFileSetDir);
4975                            destPath.append("/");
4976                            }
4977                        destPath.append(fileName);
4978                        String fullDest = parent.resolve(destPath);
4979                        //trace("fileName:%s", fileName.c_str());
4980                        //trace("copy %s to new dir : %s", fullSource.c_str(),
4981                                        //                   fullDest.c_str());
4982                        if (!isNewerThan(fullSource, fullDest))
4983                            {
4984                            //trace("copy skipping %s", fullSource.c_str());
4985                            continue;
4986                            }
4987                        if (!copyFile(fullSource, fullDest))
4988                            return false;
4989                        nrFiles++;
4990                        }
4991                    status("          : %d file(s) copied", nrFiles);
4992                    }
4993                else //file source
4994                    {
4995                    //For file->dir we want only the basename of
4996                    //the source appended to the dest dir
4997                    status("          : %s", fileName.c_str());
4998                    String baseName = fileName;
4999                    unsigned int pos = baseName.find_last_of('/');
5000                    if (pos!=baseName.npos && pos<baseName.size()-1)
5001                        baseName = baseName.substr(pos+1, baseName.size());
5002                    String fullSource = parent.resolve(fileName);
5003                    String destPath;
5004                    if (toDirName.size()>0)
5005                        {
5006                        destPath.append(toDirName);
5007                        destPath.append("/");
5008                        }
5009                    destPath.append(baseName);
5010                    String fullDest = parent.resolve(destPath);
5011                    //trace("copy %s to new dir : %s", fullSource.c_str(),
5012                                    //                       fullDest.c_str());
5013                    if (!isNewerThan(fullSource, fullDest))
5014                        {
5015                        return true;
5016                        }
5017                    if (!copyFile(fullSource, fullDest))
5018                        return false;
5019                    status("          : 1 file copied");
5020                    }
5021                return true;
5022                }
5023            }
5024         return true;
5025         }
5028     virtual bool parse(Element *elem)
5029         {
5030         if (!parent.getAttribute(elem, "file", fileName))
5031             return false;
5032         if (!parent.getAttribute(elem, "tofile", toFileName))
5033             return false;
5034         if (toFileName.size() > 0)
5035             cptype = CP_TOFILE;
5036         if (!parent.getAttribute(elem, "todir", toDirName))
5037             return false;
5038         if (toDirName.size() > 0)
5039             cptype = CP_TODIR;
5040         String ret;
5041         if (!parent.getAttribute(elem, "verbose", ret))
5042             return false;
5043         if (ret.size()>0 && !getBool(ret, verbose))
5044             return false;
5045             
5046         haveFileSet = false;
5047         
5048         std::vector<Element *> children = elem->getChildren();
5049         for (unsigned int i=0 ; i<children.size() ; i++)
5050             {
5051             Element *child = children[i];
5052             String tagName = child->getName();
5053             if (tagName == "fileset")
5054                 {
5055                 if (!getFileSet(child, parent, fileSetDir, fileSet))
5056                     {
5057                     error("problem getting fileset");
5058                                         return false;
5059                                         }
5060                                 haveFileSet = true;
5061                 }
5062             }
5064         //Perform validity checks
5065                 if (fileName.size()>0 && fileSet.size()>0)
5066                     {
5067                     error("<copy> can only have one of : file= and <fileset>");
5068                     return false;
5069                     }
5070         if (toFileName.size()>0 && toDirName.size()>0)
5071             {
5072             error("<copy> can only have one of : tofile= or todir=");
5073             return false;
5074             }
5075         if (haveFileSet && toDirName.size()==0)
5076             {
5077             error("a <copy> task with a <fileset> must have : todir=");
5078             return false;
5079             }
5080                 if (cptype == CP_TOFILE && fileName.size()==0)
5081                     {
5082                     error("<copy> tofile= must be associated with : file=");
5083                     return false;
5084                     }
5085                 if (cptype == CP_TODIR && fileName.size()==0 && !haveFileSet)
5086                     {
5087                     error("<copy> todir= must be associated with : file= or <fileset>");
5088                     return false;
5089                     }
5091         return true;
5092         }
5093         
5094 private:
5096     int cptype;
5097     String fileName;
5098     String fileSetDir;
5099     std::vector<String> fileSet;
5100     String toFileName;
5101     String toDirName;
5102     bool verbose;
5103     bool haveFileSet;
5104 };
5107 /**
5108  *
5109  */
5110 class TaskDelete : public Task
5112 public:
5114     typedef enum
5115         {
5116         DEL_FILE,
5117         DEL_DIR,
5118         DEL_FILESET
5119         } DeleteType;
5121     TaskDelete(MakeBase &par) : Task(par)
5122         { 
5123                   type        = TASK_DELETE;
5124                   name        = "delete";
5125                   delType     = DEL_FILE;
5126           verbose     = false;
5127           quiet       = false;
5128           failOnError = true;
5129                 }
5131     virtual ~TaskDelete()
5132         {}
5134     virtual bool execute()
5135         {
5136         struct stat finfo;
5137         switch (delType)
5138             {
5139             case DEL_FILE:
5140                 {
5141                 status("          : %s", fileName.c_str());
5142                 String fullName = parent.resolve(fileName);
5143                 char *fname = (char *)fullName.c_str();
5144                 //does not exist
5145                 if (stat(fname, &finfo)<0)
5146                     return true;
5147                 //exists but is not a regular file
5148                 if (!S_ISREG(finfo.st_mode))
5149                     {
5150                     error("<delete> failed. '%s' exists and is not a regular file",
5151                           fname);
5152                     return false;
5153                     }
5154                 if (remove(fname)<0)
5155                     {
5156                     error("<delete> failed: %s", strerror(errno));
5157                     return false;
5158                     }
5159                 return true;
5160                 }
5161             case DEL_DIR:
5162                 {
5163                 status("          : %s", dirName.c_str());
5164                 String fullDir = parent.resolve(dirName);
5165                 if (!removeDirectory(fullDir))
5166                     return false;
5167                 return true;
5168                 }
5169             }
5170         return true;
5171         }
5173     virtual bool parse(Element *elem)
5174         {
5175         if (!parent.getAttribute(elem, "file", fileName))
5176             return false;
5177         if (fileName.size() > 0)
5178             delType = DEL_FILE;
5179         if (!parent.getAttribute(elem, "dir", dirName))
5180             return false;
5181         if (dirName.size() > 0)
5182             delType = DEL_DIR;
5183         if (fileName.size()>0 && dirName.size()>0)
5184             {
5185             error("<delete> can only have one attribute of file= or dir=");
5186             return false;
5187             }
5188         String ret;
5189         if (!parent.getAttribute(elem, "verbose", ret))
5190             return false;
5191         if (ret.size()>0 && !getBool(ret, verbose))
5192             return false;
5193         if (!parent.getAttribute(elem, "quiet", ret))
5194             return false;
5195         if (ret.size()>0 && !getBool(ret, quiet))
5196             return false;
5197         if (!parent.getAttribute(elem, "failonerror", ret))
5198             return false;
5199         if (ret.size()>0 && !getBool(ret, failOnError))
5200             return false;
5201         return true;
5202         }
5204 private:
5206     int delType;
5207     String dirName;
5208     String fileName;
5209     bool verbose;
5210     bool quiet;
5211     bool failOnError;
5212 };
5215 /**
5216  *
5217  */
5218 class TaskJar : public Task
5220 public:
5222     TaskJar(MakeBase &par) : Task(par)
5223         { type = TASK_JAR; name = "jar"; }
5225     virtual ~TaskJar()
5226         {}
5228     virtual bool execute()
5229         {
5230         return true;
5231         }
5233     virtual bool parse(Element *elem)
5234         {
5235         return true;
5236         }
5237 };
5240 /**
5241  *
5242  */
5243 class TaskJavac : public Task
5245 public:
5247     TaskJavac(MakeBase &par) : Task(par)
5248         { type = TASK_JAVAC; name = "javac"; }
5250     virtual ~TaskJavac()
5251         {}
5253     virtual bool execute()
5254         {
5255         return true;
5256         }
5258     virtual bool parse(Element *elem)
5259         {
5260         return true;
5261         }
5262 };
5265 /**
5266  *
5267  */
5268 class TaskLink : public Task
5270 public:
5272     TaskLink(MakeBase &par) : Task(par)
5273         {
5274                 type = TASK_LINK; name = "link";
5275                 command = "g++";
5276                 }
5278     virtual ~TaskLink()
5279         {}
5281     virtual bool execute()
5282         {
5283         bool doit = false;
5284         String fullTarget = parent.resolve(fileName);
5285         String cmd = command;
5286         cmd.append(" -o ");
5287         cmd.append(fullTarget);
5288         cmd.append(" ");
5289         cmd.append(flags);
5290         for (unsigned int i=0 ; i<fileSet.size() ; i++)
5291             {
5292             cmd.append(" ");
5293             String obj;
5294             if (fileSetDir.size()>0)
5295                             {
5296                                 obj.append(fileSetDir);
5297                 obj.append("/");
5298                 }
5299             obj.append(fileSet[i]);
5300             String fullObj = parent.resolve(obj);
5301             cmd.append(fullObj);
5302             if (isNewerThan(fullObj, fullTarget))
5303                 doit = true;
5304             }
5305         cmd.append(" ");
5306         cmd.append(libs);
5307         if (!doit)
5308             {
5309             //trace("link not needed");
5310             return true;
5311             }
5312         //trace("LINK cmd:%s", cmd.c_str());
5315         String outString, errString;
5316         if (!executeCommand(cmd.c_str(), "", outString, errString))
5317             {
5318             error("LINK problem: %s", errString.c_str());
5319             return false;
5320             }
5321         return true;
5322         }
5324     virtual bool parse(Element *elem)
5325         {
5326         if (!parent.getAttribute(elem, "command", command))
5327             return false;
5328         if (!parent.getAttribute(elem, "out", fileName))
5329             return false;
5330             
5331         std::vector<Element *> children = elem->getChildren();
5332         for (unsigned int i=0 ; i<children.size() ; i++)
5333             {
5334             Element *child = children[i];
5335             String tagName = child->getName();
5336             if (tagName == "fileset")
5337                 {
5338                 if (!getFileSet(child, parent, fileSetDir, fileSet))
5339                     return false;
5340                 }
5341             else if (tagName == "flags")
5342                 {
5343                 if (!parent.getValue(child, flags))
5344                     return false;
5345                 }
5346             else if (tagName == "libs")
5347                 {
5348                 if (!parent.getValue(child, libs))
5349                     return false;
5350                 }
5351             }
5352         return true;
5353         }
5355 private:
5357     String command;
5358     String fileName;
5359     String flags;
5360     String libs;
5361     String fileSetDir;
5362     std::vector<String> fileSet;
5364 };
5368 /**
5369  * Create a named directory
5370  */
5371 class TaskMkDir : public Task
5373 public:
5375     TaskMkDir(MakeBase &par) : Task(par)
5376         { type = TASK_MKDIR; name = "mkdir"; }
5378     virtual ~TaskMkDir()
5379         {}
5381     virtual bool execute()
5382         {
5383         status("          : %s", dirName.c_str());
5384         String fullDir = parent.resolve(dirName);
5385         //trace("fullDir:%s", fullDir.c_str());
5386         if (!createDirectory(fullDir))
5387             return false;
5388         return true;
5389         }
5391     virtual bool parse(Element *elem)
5392         {
5393         if (!parent.getAttribute(elem, "dir", dirName))
5394             return false;
5395         if (dirName.size() == 0)
5396             {
5397             error("<mkdir> requires 'dir=\"dirname\"' attribute");
5398             return false;
5399             }
5400         //trace("dirname:%s", dirName.c_str());
5401         return true;
5402         }
5404 private:
5406     String dirName;
5407 };
5411 /**
5412  * Create a named directory
5413  */
5414 class TaskMsgFmt: public Task
5416 public:
5418     TaskMsgFmt(MakeBase &par) : Task(par)
5419          {
5420                  type = TASK_MSGFMT;
5421                  name = "msgfmt";
5422                  command = "msgfmt";
5423                  }
5425     virtual ~TaskMsgFmt()
5426         {}
5428     virtual bool execute()
5429         {
5430         //trace("msgfmt: %d", fileSet.size());
5431         for (unsigned int i=0 ; i<fileSet.size() ; i++)
5432             {
5433             String fileName = fileSet[i];
5434             if (getSuffix(fileName) != "po")
5435                 continue;
5436             String sourcePath;
5437                         if (fileSetDir.size()>0)
5438                             {
5439                             sourcePath.append(fileSetDir);
5440                 sourcePath.append("/");
5441                 }
5442             sourcePath.append(fileName);
5443             String fullSource = parent.resolve(sourcePath);
5445             String destPath;
5446                         if (toDirName.size()>0)
5447                             {
5448                             destPath.append(toDirName);
5449                 destPath.append("/");
5450                 }
5451             destPath.append(fileName);
5452             destPath[destPath.size()-2] = 'm';
5453             String fullDest = parent.resolve(destPath);
5455             if (!isNewerThan(fullSource, fullDest))
5456                 {
5457                 //trace("skip %s", fullSource.c_str());
5458                 continue;
5459                 }
5460                 
5461             String cmd = command;
5462             cmd.append(" ");
5463             cmd.append(fullSource);
5464             cmd.append(" -o ");
5465             cmd.append(fullDest);
5466             
5467             int pos = fullDest.find_last_of('/');
5468             if (pos>0)
5469                 {
5470                 String fullDestPath = fullDest.substr(0, pos);
5471                 if (!createDirectory(fullDestPath))
5472                     return false;
5473                 }
5477             String outString, errString;
5478             if (!executeCommand(cmd.c_str(), "", outString, errString))
5479                 {
5480                 error("<msgfmt> problem: %s", errString.c_str());
5481                 return false;
5482                 }
5483             }
5485         return true;
5486         }
5488     virtual bool parse(Element *elem)
5489         {
5490         if (!parent.getAttribute(elem, "todir", toDirName))
5491             return false;
5492             
5493         std::vector<Element *> children = elem->getChildren();
5494         for (unsigned int i=0 ; i<children.size() ; i++)
5495             {
5496             Element *child = children[i];
5497             String tagName = child->getName();
5498             if (tagName == "fileset")
5499                 {
5500                 if (!getFileSet(child, parent, fileSetDir, fileSet))
5501                     return false;
5502                 }
5503             }
5504         return true;
5505         }
5507 private:
5509     String command;
5510     String toDirName;
5511     String fileSetDir;
5512     std::vector<String> fileSet;
5514 };
5520 /**
5521  *  Process an archive to allow random access
5522  */
5523 class TaskRanlib : public Task
5525 public:
5527     TaskRanlib(MakeBase &par) : Task(par)
5528         { type = TASK_RANLIB; name = "ranlib"; }
5530     virtual ~TaskRanlib()
5531         {}
5533     virtual bool execute()
5534         {
5535         String fullName = parent.resolve(fileName);
5536         //trace("fullDir:%s", fullDir.c_str());
5537         String cmd = "ranlib ";
5538         cmd.append(fullName);
5539         String outbuf, errbuf;
5540         if (!executeCommand(cmd, "", outbuf, errbuf))
5541             return false;
5542         return true;
5543         }
5545     virtual bool parse(Element *elem)
5546         {
5547         if (!parent.getAttribute(elem, "file", fileName))
5548             return false;
5549         if (fileName.size() == 0)
5550             {
5551             error("<ranlib> requires 'file=\"fileNname\"' attribute");
5552             return false;
5553             }
5554         return true;
5555         }
5557 private:
5559     String fileName;
5560 };
5564 /**
5565  * Run the "ar" command to archive .o's into a .a
5566  */
5567 class TaskRC : public Task
5569 public:
5571     TaskRC(MakeBase &par) : Task(par)
5572         {
5573                 type = TASK_RC; name = "rc";
5574                 command = "windres -o";
5575                 }
5577     virtual ~TaskRC()
5578         {}
5580     virtual bool execute()
5581         {
5582         String fullFile = parent.resolve(fileName);
5583         String fullOut  = parent.resolve(outName);
5584         if (!isNewerThan(fullFile, fullOut))
5585             return true;
5586         String cmd = command;
5587         cmd.append(" ");
5588         cmd.append(fullOut);
5589         cmd.append(" ");
5590         cmd.append(flags);
5591         cmd.append(" ");
5592         cmd.append(fullFile);
5594         String outString, errString;
5595         if (!executeCommand(cmd.c_str(), "", outString, errString))
5596             {
5597             error("RC problem: %s", errString.c_str());
5598             return false;
5599             }
5600         return true;
5601         }
5603     virtual bool parse(Element *elem)
5604         {
5605         if (!parent.getAttribute(elem, "command", command))
5606             return false;
5607         if (!parent.getAttribute(elem, "file", fileName))
5608             return false;
5609         if (!parent.getAttribute(elem, "out", outName))
5610             return false;
5611         std::vector<Element *> children = elem->getChildren();
5612         for (unsigned int i=0 ; i<children.size() ; i++)
5613             {
5614             Element *child = children[i];
5615             String tagName = child->getName();
5616             if (tagName == "flags")
5617                 {
5618                 if (!parent.getValue(child, flags))
5619                     return false;
5620                 }
5621             }
5622         return true;
5623         }
5625 private:
5627     String command;
5628     String flags;
5629     String fileName;
5630     String outName;
5632 };
5636 /**
5637  * Strip an executable
5638  */
5639 class TaskStrip : public Task
5641 public:
5643     TaskStrip(MakeBase &par) : Task(par)
5644         { type = TASK_STRIP; name = "strip"; }
5646     virtual ~TaskStrip()
5647         {}
5649     virtual bool execute()
5650         {
5651         String fullName = parent.resolve(fileName);
5652         //trace("fullDir:%s", fullDir.c_str());
5653         String cmd = "strip ";
5654         cmd.append(fullName);
5656         String outbuf, errbuf;
5657         if (!executeCommand(cmd, "", outbuf, errbuf))
5658             return false;
5659         return true;
5660         }
5662     virtual bool parse(Element *elem)
5663         {
5664         if (!parent.getAttribute(elem, "file", fileName))
5665             return false;
5666         if (fileName.size() == 0)
5667             {
5668             error("<strip> requires 'file=\"fileNname\"' attribute");
5669             return false;
5670             }
5671         return true;
5672         }
5674 private:
5676     String fileName;
5677 };
5680 /**
5681  *
5682  */
5683 class TaskTstamp : public Task
5685 public:
5687     TaskTstamp(MakeBase &par) : Task(par)
5688         { type = TASK_TSTAMP; name = "tstamp"; }
5690     virtual ~TaskTstamp()
5691         {}
5693     virtual bool execute()
5694         {
5695         return true;
5696         }
5698     virtual bool parse(Element *elem)
5699         {
5700         trace("tstamp parse");
5701         return true;
5702         }
5703 };
5707 /**
5708  *
5709  */
5710 Task *Task::createTask(Element *elem)
5712     String tagName = elem->getName();
5713     //trace("task:%s", tagName.c_str());
5714     Task *task = NULL;
5715     if (tagName == "ar")
5716         task = new TaskAr(parent);
5717     else if (tagName == "cc")
5718         task = new TaskCC(parent);
5719     else if (tagName == "copy")
5720         task = new TaskCopy(parent);
5721     else if (tagName == "delete")
5722         task = new TaskDelete(parent);
5723     else if (tagName == "jar")
5724         task = new TaskJar(parent);
5725     else if (tagName == "javac")
5726         task = new TaskJavac(parent);
5727     else if (tagName == "link")
5728         task = new TaskLink(parent);
5729     else if (tagName == "mkdir")
5730         task = new TaskMkDir(parent);
5731     else if (tagName == "msgfmt")
5732         task = new TaskMsgFmt(parent);
5733     else if (tagName == "ranlib")
5734         task = new TaskRanlib(parent);
5735     else if (tagName == "rc")
5736         task = new TaskRC(parent);
5737     else if (tagName == "strip")
5738         task = new TaskStrip(parent);
5739     else if (tagName == "tstamp")
5740         task = new TaskTstamp(parent);
5741     else
5742         {
5743         error("Unknown task '%s'", tagName.c_str());
5744         return NULL;
5745         }
5747     if (!task->parse(elem))
5748         {
5749         delete task;
5750         return NULL;
5751         }
5752     return task;
5757 //########################################################################
5758 //# T A R G E T
5759 //########################################################################
5761 /**
5762  *
5763  */
5764 class Target : public MakeBase
5767 public:
5769     /**
5770      *
5771      */
5772     Target(Make &par) : parent(par)
5773         { init(); }
5775     /**
5776      *
5777      */
5778     Target(const Target &other) : parent(other.parent)
5779         { init(); assign(other); }
5781     /**
5782      *
5783      */
5784     Target &operator=(const Target &other)
5785         { init(); assign(other); return *this; }
5787     /**
5788      *
5789      */
5790     virtual ~Target()
5791         { cleanup() ; }
5794     /**
5795      *
5796      */
5797     virtual Make &getParent()
5798         { return parent; }
5800     /**
5801      *
5802      */
5803     virtual String getName()
5804         { return name; }
5806     /**
5807      *
5808      */
5809     virtual void setName(const String &val)
5810         { name = val; }
5812     /**
5813      *
5814      */
5815     virtual String getDescription()
5816         { return description; }
5818     /**
5819      *
5820      */
5821     virtual void setDescription(const String &val)
5822         { description = val; }
5824     /**
5825      *
5826      */
5827     virtual void addDependency(const String &val)
5828         { deps.push_back(val); }
5830     /**
5831      *
5832      */
5833     virtual void parseDependencies(const String &val)
5834         { deps = tokenize(val, ", "); }
5836     /**
5837      *
5838      */
5839     virtual std::vector<String> &getDependencies()
5840         { return deps; }
5842     /**
5843      *
5844      */
5845     virtual String getIf()
5846         { return ifVar; }
5848     /**
5849      *
5850      */
5851     virtual void setIf(const String &val)
5852         { ifVar = val; }
5854     /**
5855      *
5856      */
5857     virtual String getUnless()
5858         { return unlessVar; }
5860     /**
5861      *
5862      */
5863     virtual void setUnless(const String &val)
5864         { unlessVar = val; }
5866     /**
5867      *
5868      */
5869     virtual void addTask(Task *val)
5870         { tasks.push_back(val); }
5872     /**
5873      *
5874      */
5875     virtual std::vector<Task *> &getTasks()
5876         { return tasks; }
5878 private:
5880     void init()
5881         {
5882         }
5884     void cleanup()
5885         {
5886         tasks.clear();
5887         }
5889     void assign(const Target &other)
5890         {
5891         //parent      = other.parent;
5892         name        = other.name;
5893         description = other.description;
5894         ifVar       = other.ifVar;
5895         unlessVar   = other.unlessVar;
5896         deps        = other.deps;
5897         tasks       = other.tasks;
5898         }
5900     Make &parent;
5902     String name;
5904     String description;
5906     String ifVar;
5908     String unlessVar;
5910     std::vector<String> deps;
5912     std::vector<Task *> tasks;
5914 };
5923 //########################################################################
5924 //# M A K E
5925 //########################################################################
5928 /**
5929  *
5930  */
5931 class Make : public MakeBase
5934 public:
5936     /**
5937      *
5938      */
5939     Make()
5940         { init(); }
5942     /**
5943      *
5944      */
5945     Make(const Make &other)
5946         { assign(other); }
5948     /**
5949      *
5950      */
5951     Make &operator=(const Make &other)
5952         { assign(other); return *this; }
5954     /**
5955      *
5956      */
5957     virtual ~Make()
5958         { cleanup(); }
5960     /**
5961      *
5962      */
5963     virtual std::map<String, Target> &getTargets()
5964         { return targets; }
5967     /**
5968      *
5969      */
5970     bool run();
5972     /**
5973      *
5974      */
5975     bool run(const String &target);
5979 private:
5981     /**
5982      *
5983      */
5984     void init();
5986     /**
5987      *
5988      */
5989     void cleanup();
5991     /**
5992      *
5993      */
5994     void assign(const Make &other);
5996     /**
5997      *
5998      */
5999     bool executeTask(Task &task);
6002     /**
6003      *
6004      */
6005     bool executeTarget(Target &target,
6006                  std::set<String> &targetsCompleted);
6009     /**
6010      *
6011      */
6012     bool execute();
6014     /**
6015      *
6016      */
6017     bool checkTargetDependencies(Target &prop,
6018                     std::vector<String> &depList);
6020     /**
6021      *
6022      */
6023     bool parsePropertyFile(const String &fileName,
6024                                const String &prefix);
6026     /**
6027      *
6028      */
6029     bool parseProperty(Element *elem);
6031     /**
6032      *
6033      */
6034     bool parseTask(Task &task, Element *elem);
6036     /**
6037      *
6038      */
6039     bool parseFile();
6041     /**
6042      *
6043      */
6044     std::vector<String> glob(const String &pattern);
6047     //###############
6048     //# Fields
6049     //###############
6051     String projectName;
6053     String currentTarget;
6055     String defaultTarget;
6057     String specifiedTarget;
6059     String baseDir;
6061     String description;
6062     
6063     String envAlias;
6065     //std::vector<Property> properties;
6066     
6067     std::map<String, Target> targets;
6069     std::vector<Task *> allTasks;
6072 };
6075 //########################################################################
6076 //# C L A S S  M A I N T E N A N C E
6077 //########################################################################
6079 /**
6080  *
6081  */
6082 void Make::init()
6084     uri             = "build.xml";
6085     projectName     = "";
6086     currentTarget   = "";
6087     defaultTarget   = "";
6088     specifiedTarget = "";
6089     baseDir         = "";
6090     description     = "";
6091     envAlias        = "";
6092     properties.clear();
6093     for (unsigned int i = 0 ; i < allTasks.size() ; i++)
6094         delete allTasks[i];
6095     allTasks.clear();
6100 /**
6101  *
6102  */
6103 void Make::cleanup()
6105     for (unsigned int i = 0 ; i < allTasks.size() ; i++)
6106         delete allTasks[i];
6107     allTasks.clear();
6112 /**
6113  *
6114  */
6115 void Make::assign(const Make &other)
6117     uri              = other.uri;
6118     projectName      = other.projectName;
6119     currentTarget    = other.currentTarget;
6120     defaultTarget    = other.defaultTarget;
6121     specifiedTarget  = other.specifiedTarget;
6122     baseDir          = other.baseDir;
6123     description      = other.description;
6124     properties       = other.properties;
6129 //########################################################################
6130 //# U T I L I T Y    T A S K S
6131 //########################################################################
6133 /**
6134  *  Perform a file globbing
6135  */
6136 std::vector<String> Make::glob(const String &pattern)
6138     std::vector<String> res;
6139     return res;
6143 //########################################################################
6144 //# P U B L I C    A P I
6145 //########################################################################
6149 /**
6150  *
6151  */
6152 bool Make::executeTarget(Target &target,
6153              std::set<String> &targetsCompleted)
6156     String name = target.getName();
6158     //First get any dependencies for this target
6159     std::vector<String> deps = target.getDependencies();
6160     for (unsigned int i=0 ; i<deps.size() ; i++)
6161         {
6162         String dep = deps[i];
6163         //Did we do it already?  Skip
6164         if (targetsCompleted.find(dep)!=targetsCompleted.end())
6165             continue;
6166             
6167         std::map<String, Target> &tgts =
6168                target.getParent().getTargets();
6169         std::map<String, Target>::iterator iter =
6170                tgts.find(dep);
6171         if (iter == tgts.end())
6172             {
6173             error("Target '%s' dependency '%s' not found",
6174                       name.c_str(),  dep.c_str());
6175             return false;
6176             }
6177         Target depTarget = iter->second;
6178         if (!executeTarget(depTarget, targetsCompleted))
6179             {
6180             return false;
6181             }
6182         }
6184     status("## Target : %s", name.c_str());
6186     //Now let's do the tasks
6187     std::vector<Task *> &tasks = target.getTasks();
6188     for (unsigned int i=0 ; i<tasks.size() ; i++)
6189         {
6190         Task *task = tasks[i];
6191         status("---- task : %s", task->getName().c_str());
6192         if (!task->execute())
6193             {
6194             return false;
6195             }
6196         }
6197         
6198     targetsCompleted.insert(name);
6199     
6200     return true;
6205 /**
6206  *  Main execute() method.  Start here and work
6207  *  up the dependency tree 
6208  */
6209 bool Make::execute()
6211     status("######## EXECUTE");
6213     //Determine initial target
6214     if (specifiedTarget.size()>0)
6215         {
6216         currentTarget = specifiedTarget;
6217         }
6218     else if (defaultTarget.size()>0)
6219         {
6220         currentTarget = defaultTarget;
6221         }
6222     else
6223         {
6224         error("execute: no specified or default target requested");
6225         return false;
6226         }
6228     std::map<String, Target>::iterator iter =
6229                    targets.find(currentTarget);
6230     if (iter == targets.end())
6231         {
6232         error("Initial target '%s' not found",
6233                          currentTarget.c_str());
6234         return false;
6235         }
6236         
6237     //Now run
6238     Target target = iter->second;
6239     std::set<String> targetsCompleted;
6240     if (!executeTarget(target, targetsCompleted))
6241         {
6242         return false;
6243         }
6245     status("######## EXECUTE COMPLETE");
6246     return true;
6252 /**
6253  *
6254  */
6255 bool Make::checkTargetDependencies(Target &target, 
6256                             std::vector<String> &depList)
6258     String tgtName = target.getName().c_str();
6259     depList.push_back(tgtName);
6261     std::vector<String> deps = target.getDependencies();
6262     for (unsigned int i=0 ; i<deps.size() ; i++)
6263         {
6264         String dep = deps[i];
6265         //First thing entered was the starting Target
6266         if (dep == depList[0])
6267             {
6268             error("Circular dependency '%s' found at '%s'",
6269                       dep.c_str(), tgtName.c_str());
6270             std::vector<String>::iterator diter;
6271             for (diter=depList.begin() ; diter!=depList.end() ; diter++)
6272                 {
6273                 error("  %s", diter->c_str());
6274                 }
6275             return false;
6276             }
6278         std::map<String, Target> &tgts =
6279                   target.getParent().getTargets();
6280         std::map<String, Target>::iterator titer = tgts.find(dep);
6281         if (titer == tgts.end())
6282             {
6283             error("Target '%s' dependency '%s' not found",
6284                       tgtName.c_str(), dep.c_str());
6285             return false;
6286             }
6287         if (!checkTargetDependencies(titer->second, depList))
6288             {
6289             return false;
6290             }
6291         }
6292     return true;
6299 static int getword(int pos, const String &inbuf, String &result)
6301     int p = pos;
6302     int len = (int)inbuf.size();
6303     String val;
6304     while (p < len)
6305         {
6306         char ch = inbuf[p];
6307         if (!isalnum(ch) && ch!='.' && ch!='_')
6308             break;
6309         val.push_back(ch);
6310         p++;
6311         }
6312     result = val;
6313     return p;
6319 /**
6320  *
6321  */
6322 bool Make::parsePropertyFile(const String &fileName,
6323                              const String &prefix)
6325     FILE *f = fopen(fileName.c_str(), "r");
6326     if (!f)
6327         {
6328         error("could not open property file %s", fileName.c_str());
6329         return false;
6330         }
6331     int linenr = 0;
6332     while (!feof(f))
6333         {
6334         char buf[256];
6335         if (!fgets(buf, 255, f))
6336             break;
6337         linenr++;
6338         String s = buf;
6339         s = trim(s);
6340         int len = s.size();
6341         if (len == 0)
6342             continue;
6343         if (s[0] == '#')
6344             continue;
6345         String key;
6346         String val;
6347         int p = 0;
6348         int p2 = getword(p, s, key);
6349         if (p2 <= p)
6350             {
6351             error("property file %s, line %d: expected keyword",
6352                                 fileName.c_str(), linenr);
6353                         return false;
6354                         }
6355                 if (prefix.size() > 0)
6356                     {
6357                     key.insert(0, prefix);
6358                     }
6360         //skip whitespace
6361                 for (p=p2 ; p<len ; p++)
6362                     if (!isspace(s[p]))
6363                         break;
6365         if (p>=len || s[p]!='=')
6366             {
6367             error("property file %s, line %d: expected '='",
6368                                 fileName.c_str(), linenr);
6369             return false;
6370             }
6371         p++;
6373         //skip whitespace
6374                 for ( ; p<len ; p++)
6375                     if (!isspace(s[p]))
6376                         break;
6378         /* This way expects a word after the =
6379                 p2 = getword(p, s, val);
6380         if (p2 <= p)
6381             {
6382             error("property file %s, line %d: expected value",
6383                                 fileName.c_str(), linenr);
6384                         return false;
6385                         }
6386                 */
6387         // This way gets the rest of the line after the =
6388                 if (p>=len)
6389             {
6390             error("property file %s, line %d: expected value",
6391                                 fileName.c_str(), linenr);
6392                         return false;
6393                         }
6394         val = s.substr(p);
6395                 if (key.size()==0 || val.size()==0)
6396                     continue;
6398         //trace("key:'%s' val:'%s'", key.c_str(), val.c_str());
6399             properties[key] = val;
6400         }
6401     fclose(f);
6402     return true;
6408 /**
6409  *
6410  */
6411 bool Make::parseProperty(Element *elem)
6413     std::vector<Attribute> &attrs = elem->getAttributes();
6414     for (unsigned int i=0 ; i<attrs.size() ; i++)
6415         {
6416         String attrName = attrs[i].getName();
6417         String attrVal  = attrs[i].getValue();
6419         if (attrName == "name")
6420             {
6421             String val;
6422                         if (!getAttribute(elem, "value", val))
6423                             return false;
6424             if (val.size() > 0)
6425                 {
6426                 properties[attrVal] = val;
6427                 continue;
6428                 }
6429             if (!getAttribute(elem, "location", val))
6430                 return false;
6431             if (val.size() > 0)
6432                 {
6433                 //TODO:  process a path relative to build.xml
6434                 properties[attrVal] = val;
6435                 continue;
6436                 }
6437             }
6438         else if (attrName == "file")
6439             {
6440             String prefix;
6441                         if (!getAttribute(elem, "prefix", prefix))
6442                             return false;
6443             if (prefix.size() > 0)
6444                 {
6445                 if (prefix[prefix.size()-1] != '.')
6446                     prefix.push_back('.');
6447                 }
6448             if (!parsePropertyFile(attrName, prefix))
6449                 return false;
6450             }
6451         else if (attrName == "environment")
6452             {
6453             if (envAlias.size() > 0)
6454                 {
6455                 error("environment property can only be set once");
6456                 return false;
6457                 }
6458             envAlias = attrVal;
6459             }
6460         }
6462     return true;
6468 /**
6469  *
6470  */
6471 bool Make::parseFile()
6473     status("######## PARSE");
6475     Parser parser;
6476     Element *root = parser.parseFile(uri.getNativePath());
6477     if (!root)
6478         {
6479         error("Could not open %s for reading",
6480                       uri.getNativePath().c_str());
6481         return false;
6482         }
6484     if (root->getChildren().size()==0 ||
6485         root->getChildren()[0]->getName()!="project")
6486         {
6487         error("Main xml element should be <project>");
6488         delete root;
6489         return false;
6490         }
6492     //########## Project attributes
6493     Element *project = root->getChildren()[0];
6494     String s = project->getAttribute("name");
6495     if (s.size() > 0)
6496         projectName = s;
6497     s = project->getAttribute("default");
6498     if (s.size() > 0)
6499         defaultTarget = s;
6500     s = project->getAttribute("basedir");
6501     if (s.size() > 0)
6502         baseDir = s;
6504     //######### PARSE MEMBERS
6505     std::vector<Element *> children = project->getChildren();
6506     for (unsigned int i=0 ; i<children.size() ; i++)
6507         {
6508         Element *elem = children[i];
6509         String tagName = elem->getName();
6511         //########## DESCRIPTION
6512         if (tagName == "description")
6513             {
6514             description = parser.trim(elem->getValue());
6515             }
6517         //######### PROPERTY
6518         else if (tagName == "property")
6519             {
6520             if (!parseProperty(elem))
6521                 return false;
6522             }
6524         //######### TARGET
6525         else if (tagName == "target")
6526             {
6527             String tname   = elem->getAttribute("name");
6528             String tdesc   = elem->getAttribute("description");
6529             String tdeps   = elem->getAttribute("depends");
6530             String tif     = elem->getAttribute("if");
6531             String tunless = elem->getAttribute("unless");
6532             Target target(*this);
6533             target.setName(tname);
6534             target.setDescription(tdesc);
6535             target.parseDependencies(tdeps);
6536             target.setIf(tif);
6537             target.setUnless(tunless);
6538             std::vector<Element *> telems = elem->getChildren();
6539             for (unsigned int i=0 ; i<telems.size() ; i++)
6540                 {
6541                 Element *telem = telems[i];
6542                 Task breeder(*this);
6543                 Task *task = breeder.createTask(telem);
6544                 if (!task)
6545                     return false;
6546                 allTasks.push_back(task);
6547                 target.addTask(task);
6548                 }
6550             //Check name
6551             if (tname.size() == 0)
6552                 {
6553                 error("no name for target");
6554                 return false;
6555                 }
6556             //Check for duplicate name
6557             if (targets.find(tname) != targets.end())
6558                 {
6559                 error("target '%s' already defined", tname.c_str());
6560                 return false;
6561                 }
6562             //more work than targets[tname]=target, but avoids default allocator
6563             targets.insert(std::make_pair<String, Target>(tname, target));
6564             }
6566         }
6568     std::map<String, Target>::iterator iter;
6569     for (iter = targets.begin() ; iter!= targets.end() ; iter++)
6570         {
6571         Target tgt = iter->second;
6572         std::vector<String> depList;
6573         if (!checkTargetDependencies(tgt, depList))
6574             {
6575             return false;
6576             }
6577         }
6580     delete root;
6581     status("######## PARSE COMPLETE");
6582     return true;
6586 /**
6587  *
6588  */
6589 bool Make::run()
6591     if (!parseFile())
6592         return false;
6593     if (!execute())
6594         return false;
6595     return true;
6599 /**
6600  *
6601  */
6602 bool Make::run(const String &target)
6604     status("##################################");
6605     status("#   BuildTool");
6606     status("#   version 0.2");
6607     status("##################################");
6608     specifiedTarget = target;
6609     if (!run())
6610         return false;
6611     status("##################################");
6612     status("#   BuildTool Completed");
6613     status("##################################");
6614     return true;
6623 }// namespace buildtool
6624 //########################################################################
6625 //# M A I N
6626 //########################################################################
6628 /**
6629  *  Format an error message in printf() style
6630  */
6631 static void error(char *fmt, ...)
6633     va_list ap;
6634     va_start(ap, fmt);
6635     fprintf(stderr, "BuildTool error: ");
6636     vfprintf(stderr, fmt, ap);
6637     fprintf(stderr, "\n");
6638     va_end(ap);
6642 /**
6643  * Compare a buffer with a key, for the length of the key
6644  */
6645 static bool sequ(const buildtool::String &buf, char *key)
6647     for (int i=0 ; key[i] ; i++)
6648         {
6649         if (key[i] != buf[i])
6650             return false;
6651         }        
6652     return true;
6655 /**
6656  * Parse the command-line args, get our options,
6657  * and run this thing
6658  */   
6659 static bool parseOptions(int argc, char **argv)
6661     if (argc < 1)
6662         {
6663         error("Cannot parse arguments");
6664         return false;
6665         }
6667     buildtool::String buildFile;
6668     buildtool::String target;
6670     //char *progName = argv[0];
6671     for (int i=1 ; i<argc ; i++)
6672         {
6673         buildtool::String arg = argv[i];
6674         if (sequ(arg, "--"))
6675             {
6676             if (sequ(arg, "--file=") && arg.size()>7)
6677                 {
6678                 buildFile = arg.substr(7, arg.size()-7);
6679                 }
6680             else
6681                 {
6682                 error("Unknown option:%s", arg.c_str());
6683                 return false;
6684                 }
6685             }
6686         else if (sequ(arg, "-"))
6687             {
6688             for (unsigned int p=1 ; p<arg.size() ; p++)
6689                 {
6690                 int ch = arg[p];
6691                 if (0)//put options here
6692                     {
6693                     }
6694                 else
6695                     {
6696                     error("Unknown option '%c'", ch);
6697                     return false;
6698                     }
6699                 }
6700             }
6701         else
6702             {
6703             target = arg;
6704             }
6705         }
6707     //We have the options.  Now execute them
6708     buildtool::Make make;
6709     if (buildFile.size() > 0)
6710         {
6711         make.setURI(buildFile);
6712         }
6713     if (!make.run(target))
6714         return false;
6716     return true;
6722 /*
6723 static bool runMake()
6725     buildtool::Make make;
6726     if (!make.run())
6727         return false;
6728     return true;
6732 static bool pkgConfigTest()
6734     buildtool::PkgConfig pkgConfig;
6735     if (!pkgConfig.readFile("gtk+-2.0.pc"))
6736         return false;
6737     return true;
6742 static bool depTest()
6744     buildtool::DepTool deptool;
6745     deptool.setSourceDirectory("/dev/ink/inkscape/src");
6746     if (!deptool.generateDependencies("build.dep"))
6747         return false;
6748     std::vector<buildtool::DepRec> res =
6749                deptool.loadDepFile("build.dep");
6750         if (res.size() == 0)
6751         return false;
6752     return true;
6755 static bool popenTest()
6757     buildtool::Make make;
6758     buildtool::String out, err;
6759         bool ret = make.executeCommand("gcc xx.cpp", "", out, err);
6760     printf("Popen test:%d '%s' '%s'\n", ret, out.c_str(), err.c_str());
6761     return true;
6765 static bool propFileTest()
6767     buildtool::Make make;
6768     make.parsePropertyFile("test.prop", "test.");
6769     return true;
6771 */
6773 int main(int argc, char **argv)
6776     if (!parseOptions(argc, argv))
6777         return 1;
6778     /*
6779     if (!popenTest())
6780         return 1;
6782     if (!depTest())
6783         return 1;
6784     if (!propFileTest())
6785         return 1;
6786     if (runMake())
6787         return 1;
6788     */
6789     return 0;
6793 //########################################################################
6794 //# E N D 
6795 //########################################################################