Code

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>
46 #include <string>
47 #include <map>
48 #include <set>
49 #include <vector>
51 #ifdef __WIN32__
52 #include <windows.h>
53 #endif
57 namespace buildtool
58 {
65 //########################################################################
66 //########################################################################
67 //##  X M L
68 //########################################################################
69 //########################################################################
71 // Note:  This mini-dom library comes from Pedro, another little project
72 // of mine.
74 typedef std::string String;
75 typedef unsigned int XMLCh;
78 class Namespace
79 {
80 public:
81     Namespace()
82         {}
84     Namespace(const String &prefixArg, const String &namespaceURIArg)
85         {
86         prefix       = prefixArg;
87         namespaceURI = namespaceURIArg;
88         }
90     Namespace(const Namespace &other)
91         {
92         assign(other);
93         }
95     Namespace &operator=(const Namespace &other)
96         {
97         assign(other);
98         return *this;
99         }
101     virtual ~Namespace()
102         {}
104     virtual String getPrefix()
105         { return prefix; }
107     virtual String getNamespaceURI()
108         { return namespaceURI; }
110 protected:
112     void assign(const Namespace &other)
113         {
114         prefix       = other.prefix;
115         namespaceURI = other.namespaceURI;
116         }
118     String prefix;
119     String namespaceURI;
121 };
123 class Attribute
125 public:
126     Attribute()
127         {}
129     Attribute(const String &nameArg, const String &valueArg)
130         {
131         name  = nameArg;
132         value = valueArg;
133         }
135     Attribute(const Attribute &other)
136         {
137         assign(other);
138         }
140     Attribute &operator=(const Attribute &other)
141         {
142         assign(other);
143         return *this;
144         }
146     virtual ~Attribute()
147         {}
149     virtual String getName()
150         { return name; }
152     virtual String getValue()
153         { return value; }
155 protected:
157     void assign(const Attribute &other)
158         {
159         name  = other.name;
160         value = other.value;
161         }
163     String name;
164     String value;
166 };
169 class Element
171 friend class Parser;
173 public:
174     Element()
175         {
176         parent = NULL;
177         }
179     Element(const String &nameArg)
180         {
181         parent = NULL;
182         name   = nameArg;
183         }
185     Element(const String &nameArg, const String &valueArg)
186         {
187         parent = NULL;
188         name   = nameArg;
189         value  = valueArg;
190         }
192     Element(const Element &other)
193         {
194         assign(other);
195         }
197     Element &operator=(const Element &other)
198         {
199         assign(other);
200         return *this;
201         }
203     virtual Element *clone();
205     virtual ~Element()
206         {
207         for (unsigned int i=0 ; i<children.size() ; i++)
208             delete children[i];
209         }
211     virtual String getName()
212         { return name; }
214     virtual String getValue()
215         { return value; }
217     Element *getParent()
218         { return parent; }
220     std::vector<Element *> getChildren()
221         { return children; }
223     std::vector<Element *> findElements(const String &name);
225     String getAttribute(const String &name);
227     std::vector<Attribute> &getAttributes()
228         { return attributes; } 
230     String getTagAttribute(const String &tagName, const String &attrName);
232     String getTagValue(const String &tagName);
234     void addChild(Element *child);
236     void addAttribute(const String &name, const String &value);
238     void addNamespace(const String &prefix, const String &namespaceURI);
241     /**
242      * Prettyprint an XML tree to an output stream.  Elements are indented
243      * according to element hierarchy.
244      * @param f a stream to receive the output
245      * @param elem the element to output
246      */
247     void writeIndented(FILE *f);
249     /**
250      * Prettyprint an XML tree to standard output.  This is the equivalent of
251      * writeIndented(stdout).
252      * @param elem the element to output
253      */
254     void print();
256 protected:
258     void assign(const Element &other)
259         {
260         parent     = other.parent;
261         children   = other.children;
262         attributes = other.attributes;
263         namespaces = other.namespaces;
264         name       = other.name;
265         value      = other.value;
266         }
268     void findElementsRecursive(std::vector<Element *>&res, const String &name);
270     void writeIndentedRecursive(FILE *f, int indent);
272     Element *parent;
274     std::vector<Element *>children;
276     std::vector<Attribute> attributes;
277     std::vector<Namespace> namespaces;
279     String name;
280     String value;
282 };
288 class Parser
290 public:
291     /**
292      * Constructor
293      */
294     Parser()
295         { init(); }
297     virtual ~Parser()
298         {}
300     /**
301      * Parse XML in a char buffer.
302      * @param buf a character buffer to parse
303      * @param pos position to start parsing
304      * @param len number of chars, from pos, to parse.
305      * @return a pointer to the root of the XML document;
306      */
307     Element *parse(const char *buf,int pos,int len);
309     /**
310      * Parse XML in a char buffer.
311      * @param buf a character buffer to parse
312      * @param pos position to start parsing
313      * @param len number of chars, from pos, to parse.
314      * @return a pointer to the root of the XML document;
315      */
316     Element *parse(const String &buf);
318     /**
319      * Parse a named XML file.  The file is loaded like a data file;
320      * the original format is not preserved.
321      * @param fileName the name of the file to read
322      * @return a pointer to the root of the XML document;
323      */
324     Element *parseFile(const String &fileName);
326     /**
327      * Utility method to preprocess a string for XML
328      * output, escaping its entities.
329      * @param str the string to encode
330      */
331     static String encode(const String &str);
333     /**
334      *  Removes whitespace from beginning and end of a string
335      */
336     String trim(const String &s);
338 private:
340     void init()
341         {
342         keepGoing       = true;
343         currentNode     = NULL;
344         parselen        = 0;
345         parsebuf        = NULL;
346         currentPosition = 0;
347         }
349     void getLineAndColumn(long pos, long *lineNr, long *colNr);
351     void error(char *fmt, ...);
353     int peek(long pos);
355     int match(long pos, const char *text);
357     int skipwhite(long p);
359     int getWord(int p0, String &buf);
361     int getQuoted(int p0, String &buf, int do_i_parse);
363     int parseVersion(int p0);
365     int parseDoctype(int p0);
367     int parseElement(int p0, Element *par,int depth);
369     Element *parse(XMLCh *buf,int pos,int len);
371     bool       keepGoing;
372     Element    *currentNode;
373     long       parselen;
374     XMLCh      *parsebuf;
375     String  cdatabuf;
376     long       currentPosition;
377     int        colNr;
379 };
384 //########################################################################
385 //# E L E M E N T
386 //########################################################################
388 Element *Element::clone()
390     Element *elem = new Element(name, value);
391     elem->parent     = parent;
392     elem->attributes = attributes;
393     elem->namespaces = namespaces;
395     std::vector<Element *>::iterator iter;
396     for (iter = children.begin(); iter != children.end() ; iter++)
397         {
398         elem->addChild((*iter)->clone());
399         }
400     return elem;
404 void Element::findElementsRecursive(std::vector<Element *>&res, const String &name)
406     if (getName() == name)
407         {
408         res.push_back(this);
409         }
410     for (unsigned int i=0; i<children.size() ; i++)
411         children[i]->findElementsRecursive(res, name);
414 std::vector<Element *> Element::findElements(const String &name)
416     std::vector<Element *> res;
417     findElementsRecursive(res, name);
418     return res;
421 String Element::getAttribute(const String &name)
423     for (unsigned int i=0 ; i<attributes.size() ; i++)
424         if (attributes[i].getName() ==name)
425             return attributes[i].getValue();
426     return "";
429 String Element::getTagAttribute(const String &tagName, const String &attrName)
431     std::vector<Element *>elems = findElements(tagName);
432     if (elems.size() <1)
433         return "";
434     String res = elems[0]->getAttribute(attrName);
435     return res;
438 String Element::getTagValue(const String &tagName)
440     std::vector<Element *>elems = findElements(tagName);
441     if (elems.size() <1)
442         return "";
443     String res = elems[0]->getValue();
444     return res;
447 void Element::addChild(Element *child)
449     if (!child)
450         return;
451     child->parent = this;
452     children.push_back(child);
456 void Element::addAttribute(const String &name, const String &value)
458     Attribute attr(name, value);
459     attributes.push_back(attr);
462 void Element::addNamespace(const String &prefix, const String &namespaceURI)
464     Namespace ns(prefix, namespaceURI);
465     namespaces.push_back(ns);
468 void Element::writeIndentedRecursive(FILE *f, int indent)
470     int i;
471     if (!f)
472         return;
473     //Opening tag, and attributes
474     for (i=0;i<indent;i++)
475         fputc(' ',f);
476     fprintf(f,"<%s",name.c_str());
477     for (unsigned int i=0 ; i<attributes.size() ; i++)
478         {
479         fprintf(f," %s=\"%s\"",
480               attributes[i].getName().c_str(),
481               attributes[i].getValue().c_str());
482         }
483     for (unsigned int i=0 ; i<namespaces.size() ; i++)
484         {
485         fprintf(f," xmlns:%s=\"%s\"",
486               namespaces[i].getPrefix().c_str(),
487               namespaces[i].getNamespaceURI().c_str());
488         }
489     fprintf(f,">\n");
491     //Between the tags
492     if (value.size() > 0)
493         {
494         for (int i=0;i<indent;i++)
495             fputc(' ', f);
496         fprintf(f," %s\n", value.c_str());
497         }
499     for (unsigned int i=0 ; i<children.size() ; i++)
500         children[i]->writeIndentedRecursive(f, indent+2);
502     //Closing tag
503     for (int i=0; i<indent; i++)
504         fputc(' ',f);
505     fprintf(f,"</%s>\n", name.c_str());
508 void Element::writeIndented(FILE *f)
510     writeIndentedRecursive(f, 0);
513 void Element::print()
515     writeIndented(stdout);
519 //########################################################################
520 //# P A R S E R
521 //########################################################################
525 typedef struct
526     {
527     char *escaped;
528     char value;
529     } EntityEntry;
531 static EntityEntry entities[] =
533     { "&amp;" , '&'  },
534     { "&lt;"  , '<'  },
535     { "&gt;"  , '>'  },
536     { "&apos;", '\'' },
537     { "&quot;", '"'  },
538     { NULL    , '\0' }
539 };
543 /**
544  *  Removes whitespace from beginning and end of a string
545  */
546 String Parser::trim(const String &s)
548     if (s.size() < 1)
549         return s;
550     
551     //Find first non-ws char
552     unsigned int begin = 0;
553     for ( ; begin < s.size() ; begin++)
554         {
555         if (!isspace(s[begin]))
556             break;
557         }
559     //Find first non-ws char, going in reverse
560     unsigned int end = s.size() - 1;
561     for ( ; end > begin ; end--)
562         {
563         if (!isspace(s[end]))
564             break;
565         }
566     //trace("begin:%d  end:%d", begin, end);
568     String res = s.substr(begin, end-begin+1);
569     return res;
572 void Parser::getLineAndColumn(long pos, long *lineNr, long *colNr)
574     long line = 1;
575     long col  = 1;
576     for (long i=0 ; i<pos ; i++)
577         {
578         XMLCh ch = parsebuf[i];
579         if (ch == '\n' || ch == '\r')
580             {
581             col = 0;
582             line ++;
583             }
584         else
585             col++;
586         }
587     *lineNr = line;
588     *colNr  = col;
593 void Parser::error(char *fmt, ...)
595     long lineNr;
596     long colNr;
597     getLineAndColumn(currentPosition, &lineNr, &colNr);
598     va_list args;
599     fprintf(stderr, "xml error at line %ld, column %ld:", lineNr, colNr);
600     va_start(args,fmt);
601     vfprintf(stderr,fmt,args);
602     va_end(args) ;
603     fprintf(stderr, "\n");
608 int Parser::peek(long pos)
610     if (pos >= parselen)
611         return -1;
612     currentPosition = pos;
613     int ch = parsebuf[pos];
614     //printf("ch:%c\n", ch);
615     return ch;
620 String Parser::encode(const String &str)
622     String ret;
623     for (unsigned int i=0 ; i<str.size() ; i++)
624         {
625         XMLCh ch = (XMLCh)str[i];
626         if (ch == '&')
627             ret.append("&amp;");
628         else if (ch == '<')
629             ret.append("&lt;");
630         else if (ch == '>')
631             ret.append("&gt;");
632         else if (ch == '\'')
633             ret.append("&apos;");
634         else if (ch == '"')
635             ret.append("&quot;");
636         else
637             ret.push_back(ch);
639         }
640     return ret;
644 int Parser::match(long p0, const char *text)
646     int p = p0;
647     while (*text)
648         {
649         if (peek(p) != *text)
650             return p0;
651         p++; text++;
652         }
653     return p;
658 int Parser::skipwhite(long p)
661     while (p<parselen)
662         {
663         int p2 = match(p, "<!--");
664         if (p2 > p)
665             {
666             p = p2;
667             while (p<parselen)
668               {
669               p2 = match(p, "-->");
670               if (p2 > p)
671                   {
672                   p = p2;
673                   break;
674                   }
675               p++;
676               }
677           }
678       XMLCh b = peek(p);
679       if (!isspace(b))
680           break;
681       p++;
682       }
683   return p;
686 /* modify this to allow all chars for an element or attribute name*/
687 int Parser::getWord(int p0, String &buf)
689     int p = p0;
690     while (p<parselen)
691         {
692         XMLCh b = peek(p);
693         if (b<=' ' || b=='/' || b=='>' || b=='=')
694             break;
695         buf.push_back(b);
696         p++;
697         }
698     return p;
701 int Parser::getQuoted(int p0, String &buf, int do_i_parse)
704     int p = p0;
705     if (peek(p) != '"' && peek(p) != '\'')
706         return p0;
707     p++;
709     while ( p<parselen )
710         {
711         XMLCh b = peek(p);
712         if (b=='"' || b=='\'')
713             break;
714         if (b=='&' && do_i_parse)
715             {
716             bool found = false;
717             for (EntityEntry *ee = entities ; ee->value ; ee++)
718                 {
719                 int p2 = match(p, ee->escaped);
720                 if (p2>p)
721                     {
722                     buf.push_back(ee->value);
723                     p = p2;
724                     found = true;
725                     break;
726                     }
727                 }
728             if (!found)
729                 {
730                 error("unterminated entity");
731                 return false;
732                 }
733             }
734         else
735             {
736             buf.push_back(b);
737             p++;
738             }
739         }
740     return p;
743 int Parser::parseVersion(int p0)
745     //printf("### parseVersion: %d\n", p0);
747     int p = p0;
749     p = skipwhite(p0);
751     if (peek(p) != '<')
752         return p0;
754     p++;
755     if (p>=parselen || peek(p)!='?')
756         return p0;
758     p++;
760     String buf;
762     while (p<parselen)
763         {
764         XMLCh ch = peek(p);
765         if (ch=='?')
766             {
767             p++;
768             break;
769             }
770         buf.push_back(ch);
771         p++;
772         }
774     if (peek(p) != '>')
775         return p0;
776     p++;
778     //printf("Got version:%s\n",buf.c_str());
779     return p;
782 int Parser::parseDoctype(int p0)
784     //printf("### parseDoctype: %d\n", p0);
786     int p = p0;
787     p = skipwhite(p);
789     if (p>=parselen || peek(p)!='<')
790         return p0;
792     p++;
794     if (peek(p)!='!' || peek(p+1)=='-')
795         return p0;
796     p++;
798     String buf;
799     while (p<parselen)
800         {
801         XMLCh ch = peek(p);
802         if (ch=='>')
803             {
804             p++;
805             break;
806             }
807         buf.push_back(ch);
808         p++;
809         }
811     //printf("Got doctype:%s\n",buf.c_str());
812     return p;
815 int Parser::parseElement(int p0, Element *par,int depth)
818     int p = p0;
820     int p2 = p;
822     p = skipwhite(p);
824     //## Get open tag
825     XMLCh ch = peek(p);
826     if (ch!='<')
827         return p0;
829     p++;
831     String openTagName;
832     p = skipwhite(p);
833     p = getWord(p, openTagName);
834     //printf("####tag :%s\n", openTagName.c_str());
835     p = skipwhite(p);
837     //Add element to tree
838     Element *n = new Element(openTagName);
839     n->parent = par;
840     par->addChild(n);
842     // Get attributes
843     if (peek(p) != '>')
844         {
845         while (p<parselen)
846             {
847             p = skipwhite(p);
848             ch = peek(p);
849             //printf("ch:%c\n",ch);
850             if (ch=='>')
851                 break;
852             else if (ch=='/' && p<parselen+1)
853                 {
854                 p++;
855                 p = skipwhite(p);
856                 ch = peek(p);
857                 if (ch=='>')
858                     {
859                     p++;
860                     //printf("quick close\n");
861                     return p;
862                     }
863                 }
864             String attrName;
865             p2 = getWord(p, attrName);
866             if (p2==p)
867                 break;
868             //printf("name:%s",buf);
869             p=p2;
870             p = skipwhite(p);
871             ch = peek(p);
872             //printf("ch:%c\n",ch);
873             if (ch!='=')
874                 break;
875             p++;
876             p = skipwhite(p);
877             // ch = parsebuf[p];
878             // printf("ch:%c\n",ch);
879             String attrVal;
880             p2 = getQuoted(p, attrVal, true);
881             p=p2+1;
882             //printf("name:'%s'   value:'%s'\n",attrName.c_str(),attrVal.c_str());
883             char *namestr = (char *)attrName.c_str();
884             if (strncmp(namestr, "xmlns:", 6)==0)
885                 n->addNamespace(attrName, attrVal);
886             else
887                 n->addAttribute(attrName, attrVal);
888             }
889         }
891     bool cdata = false;
893     p++;
894     // ### Get intervening data ### */
895     String data;
896     while (p<parselen)
897         {
898         //# COMMENT
899         p2 = match(p, "<!--");
900         if (!cdata && p2>p)
901             {
902             p = p2;
903             while (p<parselen)
904                 {
905                 p2 = match(p, "-->");
906                 if (p2 > p)
907                     {
908                     p = p2;
909                     break;
910                     }
911                 p++;
912                 }
913             }
915         ch = peek(p);
916         //# END TAG
917         if (ch=='<' && !cdata && peek(p+1)=='/')
918             {
919             break;
920             }
921         //# CDATA
922         p2 = match(p, "<![CDATA[");
923         if (p2 > p)
924             {
925             cdata = true;
926             p = p2;
927             continue;
928             }
930         //# CHILD ELEMENT
931         if (ch == '<')
932             {
933             p2 = parseElement(p, n, depth+1);
934             if (p2 == p)
935                 {
936                 /*
937                 printf("problem on element:%s.  p2:%d p:%d\n",
938                       openTagName.c_str(), p2, p);
939                 */
940                 return p0;
941                 }
942             p = p2;
943             continue;
944             }
945         //# ENTITY
946         if (ch=='&' && !cdata)
947             {
948             bool found = false;
949             for (EntityEntry *ee = entities ; ee->value ; ee++)
950                 {
951                 int p2 = match(p, ee->escaped);
952                 if (p2>p)
953                     {
954                     data.push_back(ee->value);
955                     p = p2;
956                     found = true;
957                     break;
958                     }
959                 }
960             if (!found)
961                 {
962                 error("unterminated entity");
963                 return -1;
964                 }
965             continue;
966             }
968         //# NONE OF THE ABOVE
969         data.push_back(ch);
970         p++;
971         }/*while*/
974     n->value = data;
975     //printf("%d : data:%s\n",p,data.c_str());
977     //## Get close tag
978     p = skipwhite(p);
979     ch = peek(p);
980     if (ch != '<')
981         {
982         error("no < for end tag\n");
983         return p0;
984         }
985     p++;
986     ch = peek(p);
987     if (ch != '/')
988         {
989         error("no / on end tag");
990         return p0;
991         }
992     p++;
993     ch = peek(p);
994     p = skipwhite(p);
995     String closeTagName;
996     p = getWord(p, closeTagName);
997     if (openTagName != closeTagName)
998         {
999         error("Mismatched closing tag.  Expected </%S>. Got '%S'.",
1000                 openTagName.c_str(), closeTagName.c_str());
1001         return p0;
1002         }
1003     p = skipwhite(p);
1004     if (peek(p) != '>')
1005         {
1006         error("no > on end tag for '%s'", closeTagName.c_str());
1007         return p0;
1008         }
1009     p++;
1010     // printf("close element:%s\n",closeTagName.c_str());
1011     p = skipwhite(p);
1012     return p;
1018 Element *Parser::parse(XMLCh *buf,int pos,int len)
1020     parselen = len;
1021     parsebuf = buf;
1022     Element *rootNode = new Element("root");
1023     pos = parseVersion(pos);
1024     pos = parseDoctype(pos);
1025     pos = parseElement(pos, rootNode, 0);
1026     return rootNode;
1030 Element *Parser::parse(const char *buf, int pos, int len)
1032     XMLCh *charbuf = new XMLCh[len + 1];
1033     long i = 0;
1034     for ( ; i < len ; i++)
1035         charbuf[i] = (XMLCh)buf[i];
1036     charbuf[i] = '\0';
1038     Element *n = parse(charbuf, pos, len);
1039     delete[] charbuf;
1040     return n;
1043 Element *Parser::parse(const String &buf)
1045     long len = (long)buf.size();
1046     XMLCh *charbuf = new XMLCh[len + 1];
1047     long i = 0;
1048     for ( ; i < len ; i++)
1049         charbuf[i] = (XMLCh)buf[i];
1050     charbuf[i] = '\0';
1052     Element *n = parse(charbuf, 0, len);
1053     delete[] charbuf;
1054     return n;
1057 Element *Parser::parseFile(const String &fileName)
1060     //##### LOAD INTO A CHAR BUF, THEN CONVERT TO XMLCh
1061     FILE *f = fopen(fileName.c_str(), "rb");
1062     if (!f)
1063         return NULL;
1065     struct stat  statBuf;
1066     if (fstat(fileno(f),&statBuf)<0)
1067         {
1068         fclose(f);
1069         return NULL;
1070         }
1071     long filelen = statBuf.st_size;
1073     //printf("length:%d\n",filelen);
1074     XMLCh *charbuf = new XMLCh[filelen + 1];
1075     for (XMLCh *p=charbuf ; !feof(f) ; p++)
1076         {
1077         *p = (XMLCh)fgetc(f);
1078         }
1079     fclose(f);
1080     charbuf[filelen] = '\0';
1083     /*
1084     printf("nrbytes:%d\n",wc_count);
1085     printf("buf:%ls\n======\n",charbuf);
1086     */
1087     Element *n = parse(charbuf, 0, filelen);
1088     delete[] charbuf;
1089     return n;
1094 //########################################################################
1095 //########################################################################
1096 //##  E N D    X M L
1097 //########################################################################
1098 //########################################################################
1101 //########################################################################
1102 //########################################################################
1103 //##  U R I
1104 //########################################################################
1105 //########################################################################
1107 //This would normally be a call to a UNICODE function
1108 #define isLetter(x) isalpha(x)
1110 /**
1111  *  A class that implements the W3C URI resource reference.
1112  */
1113 class URI
1115 public:
1117     typedef enum
1118         {
1119         SCHEME_NONE =0,
1120         SCHEME_DATA,
1121         SCHEME_HTTP,
1122         SCHEME_HTTPS,
1123         SCHEME_FTP,
1124         SCHEME_FILE,
1125         SCHEME_LDAP,
1126         SCHEME_MAILTO,
1127         SCHEME_NEWS,
1128         SCHEME_TELNET
1129         } SchemeTypes;
1131     /**
1132      *
1133      */
1134     URI()
1135         {
1136         init();
1137         }
1139     /**
1140      *
1141      */
1142     URI(const String &str)
1143         {
1144         init();
1145         parse(str);
1146         }
1149     /**
1150      *
1151      */
1152     URI(const char *str)
1153         {
1154         init();
1155         String domStr = str;
1156         parse(domStr);
1157         }
1160     /**
1161      *
1162      */
1163     URI(const URI &other)
1164         {
1165         init();
1166         assign(other);
1167         }
1170     /**
1171      *
1172      */
1173     URI &operator=(const URI &other)
1174         {
1175         init();
1176         assign(other);
1177         return *this;
1178         }
1181     /**
1182      *
1183      */
1184     ~URI()
1185         {}
1189     /**
1190      *
1191      */
1192     virtual bool parse(const String &str);
1194     /**
1195      *
1196      */
1197     virtual String toString() const;
1199     /**
1200      *
1201      */
1202     virtual int getScheme() const;
1204     /**
1205      *
1206      */
1207     virtual String getSchemeStr() const;
1209     /**
1210      *
1211      */
1212     virtual String getAuthority() const;
1214     /**
1215      *  Same as getAuthority, but if the port has been specified
1216      *  as host:port , the port will not be included
1217      */
1218     virtual String getHost() const;
1220     /**
1221      *
1222      */
1223     virtual int getPort() const;
1225     /**
1226      *
1227      */
1228     virtual String getPath() const;
1230     /**
1231      *
1232      */
1233     virtual String getNativePath() const;
1235     /**
1236      *
1237      */
1238     virtual bool isAbsolute() const;
1240     /**
1241      *
1242      */
1243     virtual bool isOpaque() const;
1245     /**
1246      *
1247      */
1248     virtual String getQuery() const;
1250     /**
1251      *
1252      */
1253     virtual String getFragment() const;
1255     /**
1256      *
1257      */
1258     virtual URI resolve(const URI &other) const;
1260     /**
1261      *
1262      */
1263     virtual void normalize();
1265 private:
1267     /**
1268      *
1269      */
1270     void init()
1271         {
1272         parsebuf  = NULL;
1273         parselen  = 0;
1274         scheme    = SCHEME_NONE;
1275         schemeStr = "";
1276         port      = 0;
1277         authority = "";
1278         path      = "";
1279         absolute  = false;
1280         opaque    = false;
1281         query     = "";
1282         fragment  = "";
1283         }
1286     /**
1287      *
1288      */
1289     void assign(const URI &other)
1290         {
1291         scheme    = other.scheme;
1292         schemeStr = other.schemeStr;
1293         authority = other.authority;
1294         port      = other.port;
1295         path      = other.path;
1296         absolute  = other.absolute;
1297         opaque    = other.opaque;
1298         query     = other.query;
1299         fragment  = other.fragment;
1300         }
1302     int scheme;
1304     String schemeStr;
1306     String authority;
1308     bool portSpecified;
1310     int port;
1312     String path;
1314     bool absolute;
1316     bool opaque;
1318     String query;
1320     String fragment;
1322     void error(const char *fmt, ...);
1324     void trace(const char *fmt, ...);
1327     int peek(int p);
1329     int match(int p, char *key);
1331     int parseScheme(int p);
1333     int parseHierarchicalPart(int p0);
1335     int parseQuery(int p0);
1337     int parseFragment(int p0);
1339     int parse(int p);
1341     char *parsebuf;
1343     int parselen;
1345 };
1349 typedef struct
1351     int  ival;
1352     char *sval;
1353     int  port;
1354 } LookupEntry;
1356 LookupEntry schemes[] =
1358     { URI::SCHEME_DATA,   "data:",    0 },
1359     { URI::SCHEME_HTTP,   "http:",   80 },
1360     { URI::SCHEME_HTTPS,  "https:", 443 },
1361     { URI::SCHEME_FTP,    "ftp",     12 },
1362     { URI::SCHEME_FILE,   "file:",    0 },
1363     { URI::SCHEME_LDAP,   "ldap:",  123 },
1364     { URI::SCHEME_MAILTO, "mailto:", 25 },
1365     { URI::SCHEME_NEWS,   "news:",  117 },
1366     { URI::SCHEME_TELNET, "telnet:", 23 },
1367     { 0,                  NULL,       0 }
1368 };
1371 String URI::toString() const
1373     String str = schemeStr;
1374     if (authority.size() > 0)
1375         {
1376         str.append("//");
1377         str.append(authority);
1378         }
1379     str.append(path);
1380     if (query.size() > 0)
1381         {
1382         str.append("?");
1383         str.append(query);
1384         }
1385     if (fragment.size() > 0)
1386         {
1387         str.append("#");
1388         str.append(fragment);
1389         }
1390     return str;
1394 int URI::getScheme() const
1396     return scheme;
1399 String URI::getSchemeStr() const
1401     return schemeStr;
1405 String URI::getAuthority() const
1407     String ret = authority;
1408     if (portSpecified && port>=0)
1409         {
1410         char buf[7];
1411         snprintf(buf, 6, ":%6d", port);
1412         ret.append(buf);
1413         }
1414     return ret;
1417 String URI::getHost() const
1419     return authority;
1422 int URI::getPort() const
1424     return port;
1428 String URI::getPath() const
1430     return path;
1433 String URI::getNativePath() const
1435     String npath;
1436 #ifdef __WIN32__
1437     unsigned int firstChar = 0;
1438     if (path.size() >= 3)
1439         {
1440         if (path[0] == '/' &&
1441             isLetter(path[1]) &&
1442             path[2] == ':')
1443             firstChar++;
1444          }
1445     for (unsigned int i=firstChar ; i<path.size() ; i++)
1446         {
1447         XMLCh ch = (XMLCh) path[i];
1448         if (ch == '/')
1449             npath.push_back((XMLCh)'\\');
1450         else
1451             npath.push_back(ch);
1452         }
1453 #else
1454     npath = path;
1455 #endif
1456     return npath;
1460 bool URI::isAbsolute() const
1462     return absolute;
1465 bool URI::isOpaque() const
1467     return opaque;
1471 String URI::getQuery() const
1473     return query;
1477 String URI::getFragment() const
1479     return fragment;
1483 URI URI::resolve(const URI &other) const
1485     //### According to w3c, this is handled in 3 cases
1487     //## 1
1488     if (opaque || other.isAbsolute())
1489         return other;
1491     //## 2
1492     if (other.fragment.size()  >  0 &&
1493         other.path.size()      == 0 &&
1494         other.scheme           == SCHEME_NONE &&
1495         other.authority.size() == 0 &&
1496         other.query.size()     == 0 )
1497         {
1498         URI fragUri = *this;
1499         fragUri.fragment = other.fragment;
1500         return fragUri;
1501         }
1503     //## 3 http://www.ietf.org/rfc/rfc2396.txt, section 5.2
1504     URI newUri;
1505     //# 3.1
1506     newUri.scheme    = scheme;
1507     newUri.schemeStr = schemeStr;
1508     newUri.query     = other.query;
1509     newUri.fragment  = other.fragment;
1510     if (other.authority.size() > 0)
1511         {
1512         //# 3.2
1513         if (absolute || other.absolute)
1514             newUri.absolute = true;
1515         newUri.authority = other.authority;
1516         newUri.port      = other.port;//part of authority
1517         newUri.path      = other.path;
1518         }
1519     else
1520         {
1521         //# 3.3
1522         if (other.absolute)
1523             {
1524             newUri.absolute = true;
1525             newUri.path     = other.path;
1526             }
1527         else
1528             {
1529             unsigned int pos = path.find_last_of('/');
1530             if (pos != path.npos)
1531                 {
1532                 String tpath = path.substr(0, pos+1);
1533                 tpath.append(other.path);
1534                 newUri.path = tpath;
1535                 }
1536             else
1537                 newUri.path = other.path;
1538             }
1539         }
1541     newUri.normalize();
1542     return newUri;
1546 /**
1547  *  This follows the Java URI algorithm:
1548  *   1. All "." segments are removed.
1549  *   2. If a ".." segment is preceded by a non-".." segment
1550  *          then both of these segments are removed. This step
1551  *          is repeated until it is no longer applicable.
1552  *   3. If the path is relative, and if its first segment
1553  *          contains a colon character (':'), then a "." segment
1554  *          is prepended. This prevents a relative URI with a path
1555  *          such as "a:b/c/d" from later being re-parsed as an
1556  *          opaque URI with a scheme of "a" and a scheme-specific
1557  *          part of "b/c/d". (Deviation from RFC 2396)
1558  */
1559 void URI::normalize()
1561     std::vector<String> segments;
1563     //## Collect segments
1564     if (path.size()<2)
1565         return;
1566     bool abs = false;
1567     unsigned int pos=0;
1568     if (path[0]=='/')
1569         {
1570         abs = true;
1571         pos++;
1572         }
1573     while (pos < path.size())
1574         {
1575         unsigned int pos2 = path.find('/', pos);
1576         if (pos2==path.npos)
1577             {
1578             String seg = path.substr(pos);
1579             //printf("last segment:%s\n", seg.c_str());
1580             segments.push_back(seg);
1581             break;
1582             }
1583         if (pos2>pos)
1584             {
1585             String seg = path.substr(pos, pos2-pos);
1586             //printf("segment:%s\n", seg.c_str());
1587             segments.push_back(seg);
1588             }
1589         pos = pos2;
1590         pos++;
1591         }
1593     //## Clean up (normalize) segments
1594     bool edited = false;
1595     std::vector<String>::iterator iter;
1596     for (iter=segments.begin() ; iter!=segments.end() ; )
1597         {
1598         String s = *iter;
1599         if (s == ".")
1600             {
1601             iter = segments.erase(iter);
1602             edited = true;
1603             }
1604         else if (s == ".." &&
1605                  iter != segments.begin() &&
1606                  *(iter-1) != "..")
1607             {
1608             iter--; //back up, then erase two entries
1609             iter = segments.erase(iter);
1610             iter = segments.erase(iter);
1611             edited = true;
1612             }
1613         else
1614             iter++;
1615         }
1617     //## Rebuild path, if necessary
1618     if (edited)
1619         {
1620         path.clear();
1621         if (abs)
1622             {
1623             path.append("/");
1624             }
1625         std::vector<String>::iterator iter;
1626         for (iter=segments.begin() ; iter!=segments.end() ; iter++)
1627             {
1628             if (iter != segments.begin())
1629                 path.append("/");
1630             path.append(*iter);
1631             }
1632         }
1638 //#########################################################################
1639 //# M E S S A G E S
1640 //#########################################################################
1642 void URI::error(const char *fmt, ...)
1644     va_list args;
1645     fprintf(stderr, "URI error: ");
1646     va_start(args, fmt);
1647     vfprintf(stderr, fmt, args);
1648     va_end(args);
1649     fprintf(stderr, "\n");
1652 void URI::trace(const char *fmt, ...)
1654     va_list args;
1655     fprintf(stdout, "URI: ");
1656     va_start(args, fmt);
1657     vfprintf(stdout, fmt, args);
1658     va_end(args);
1659     fprintf(stdout, "\n");
1664 //#########################################################################
1665 //# P A R S I N G
1666 //#########################################################################
1670 int URI::peek(int p)
1672     if (p<0 || p>=parselen)
1673         return -1;
1674     return parsebuf[p];
1679 int URI::match(int p0, char *key)
1681     int p = p0;
1682     while (p < parselen)
1683         {
1684         if (*key == '\0')
1685             return p;
1686         else if (*key != parsebuf[p])
1687             break;
1688         p++; key++;
1689         }
1690     return p0;
1693 //#########################################################################
1694 //#  Parsing is performed according to:
1695 //#  http://www.gbiv.com/protocols/uri/rfc/rfc3986.html#components
1696 //#########################################################################
1698 int URI::parseScheme(int p0)
1700     int p = p0;
1701     for (LookupEntry *entry = schemes; entry->sval ; entry++)
1702         {
1703         int p2 = match(p, entry->sval);
1704         if (p2 > p)
1705             {
1706             schemeStr = entry->sval;
1707             scheme    = entry->ival;
1708             port      = entry->port;
1709             p = p2;
1710             return p;
1711             }
1712         }
1714     return p;
1718 int URI::parseHierarchicalPart(int p0)
1720     int p = p0;
1721     int ch;
1723     //# Authority field (host and port, for example)
1724     int p2 = match(p, "//");
1725     if (p2 > p)
1726         {
1727         p = p2;
1728         portSpecified = false;
1729         String portStr;
1730         while (p < parselen)
1731             {
1732             ch = peek(p);
1733             if (ch == '/')
1734                 break;
1735             else if (ch == ':')
1736                 portSpecified = true;
1737             else if (portSpecified)
1738                 portStr.push_back((XMLCh)ch);
1739             else
1740                 authority.push_back((XMLCh)ch);
1741             p++;
1742             }
1743         if (portStr.size() > 0)
1744             {
1745             char *pstr = (char *)portStr.c_str();
1746             char *endStr;
1747             long val = strtol(pstr, &endStr, 10);
1748             if (endStr > pstr) //successful parse?
1749                 port = val;
1750             }
1751         }
1753     //# Are we absolute?
1754     ch = peek(p);
1755     if (isLetter(ch) && peek(p+1)==':')
1756         {
1757         absolute = true;
1758         path.push_back((XMLCh)'/');
1759         }
1760     else if (ch == '/')
1761         {
1762         absolute = true;
1763         if (p>p0) //in other words, if '/' is not the first char
1764             opaque = true;
1765         path.push_back((XMLCh)ch);
1766         p++;
1767         }
1769     while (p < parselen)
1770         {
1771         ch = peek(p);
1772         if (ch == '?' || ch == '#')
1773             break;
1774         path.push_back((XMLCh)ch);
1775         p++;
1776         }
1778     return p;
1781 int URI::parseQuery(int p0)
1783     int p = p0;
1784     int ch = peek(p);
1785     if (ch != '?')
1786         return p0;
1788     p++;
1789     while (p < parselen)
1790         {
1791         ch = peek(p);
1792         if (ch == '#')
1793             break;
1794         query.push_back((XMLCh)ch);
1795         p++;
1796         }
1799     return p;
1802 int URI::parseFragment(int p0)
1805     int p = p0;
1806     int ch = peek(p);
1807     if (ch != '#')
1808         return p0;
1810     p++;
1811     while (p < parselen)
1812         {
1813         ch = peek(p);
1814         if (ch == '?')
1815             break;
1816         fragment.push_back((XMLCh)ch);
1817         p++;
1818         }
1821     return p;
1825 int URI::parse(int p0)
1828     int p = p0;
1830     int p2 = parseScheme(p);
1831     if (p2 < 0)
1832         {
1833         error("Scheme");
1834         return -1;
1835         }
1836     p = p2;
1839     p2 = parseHierarchicalPart(p);
1840     if (p2 < 0)
1841         {
1842         error("Hierarchical part");
1843         return -1;
1844         }
1845     p = p2;
1847     p2 = parseQuery(p);
1848     if (p2 < 0)
1849         {
1850         error("Query");
1851         return -1;
1852         }
1853     p = p2;
1856     p2 = parseFragment(p);
1857     if (p2 < 0)
1858         {
1859         error("Fragment");
1860         return -1;
1861         }
1862     p = p2;
1864     return p;
1870 bool URI::parse(const String &str)
1873     parselen = str.size();
1875     String tmp;
1876     for (unsigned int i=0 ; i<str.size() ; i++)
1877         {
1878         XMLCh ch = (XMLCh) str[i];
1879         if (ch == '\\')
1880             tmp.push_back((XMLCh)'/');
1881         else
1882             tmp.push_back(ch);
1883         }
1884     parsebuf = (char *) tmp.c_str();
1887     int p = parse(0);
1888     normalize();
1890     if (p < 0)
1891         {
1892         error("Syntax error");
1893         return false;
1894         }
1896     //printf("uri:%s\n", toString().c_str());
1897     //printf("path:%s\n", path.c_str());
1899     return true;
1910 //########################################################################
1911 //########################################################################
1912 //##  M A K E
1913 //########################################################################
1914 //########################################################################
1918 //########################################################################
1919 //# M A K E    B A S E
1920 //########################################################################
1921 /**
1922  * Base class for all classes in this file
1923  */
1924 class MakeBase
1926 public:
1927     MakeBase()
1928         {}
1929     virtual ~MakeBase()
1930         {}
1932     /**
1933      *  Return the URI of the file associated with this object 
1934      */     
1935     URI getURI()
1936         { return uri; }
1938     /**
1939      * Set the uri to the given string
1940      */
1941     void setURI(const String &uristr)
1942         { uri.parse(uristr); }
1944     /**
1945      *  Resolve another path relative to this one
1946      */
1947     String resolve(const String &otherPath);
1949     /**
1950      *  Get an element attribute, performing substitutions if necessary
1951      */
1952     bool getAttribute(Element *elem, const String &name, String &result);
1954     /**
1955      * Get an element value, performing substitutions if necessary
1956      */
1957     bool getValue(Element *elem, String &result);
1959 protected:
1961     /**
1962      *  The path to the file associated with this object
1963      */     
1964     URI uri;
1967     /**
1968      *  Print a printf()-like formatted error message
1969      */
1970     void error(char *fmt, ...);
1972     /**
1973      *  Print a printf()-like formatted trace message
1974      */
1975     void status(char *fmt, ...);
1977     /**
1978      *  Print a printf()-like formatted trace message
1979      */
1980     void trace(char *fmt, ...);
1982     /**
1983      *
1984      */
1985     String getSuffix(const String &fname);
1987     /**
1988      * Break up a string into substrings delimited the characters
1989      * in delimiters.  Null-length substrings are ignored
1990      */  
1991     std::vector<String> tokenize(const String &val,
1992                               const String &delimiters);
1994     /**
1995      *  remove leading and trailing whitespace from string
1996      */
1997     String trim(const String &s);
1999     /**
2000      * Return the native format of the canonical
2001      * path which we store
2002      */
2003     String getNativePath(const String &path);
2005     /**
2006      * Execute a shell command.  Outbuf is a ref to a string
2007      * to catch the result.     
2008      */      
2009     bool executeCommand(const String &call,
2010                             const String &inbuf,
2011                                                 String &outbuf,
2012                                                 String &errbuf);
2013     /**
2014      * List all directories in a given base and starting directory
2015      * It is usually called like:
2016      *           bool ret = listDirectories("src", "", result);    
2017      */      
2018     bool listDirectories(const String &baseName,
2019                          const String &dirname,
2020                          std::vector<String> &res);
2022     /**
2023      * Find all files in the named directory whose short names (no path) match
2024      * the given regex pattern     
2025      */      
2026     bool listFiles(const String &baseName,
2027                    const String &dirname,
2028                    std::vector<String> &excludes,
2029                    std::vector<String> &res);
2031     /**
2032      * Parse a <patternset>
2033      */  
2034     bool getPatternSet(Element *elem,
2035                        MakeBase &propRef,
2036                                            std::vector<String> &includes,
2037                                            std::vector<String> &excludes);
2039     /**
2040      * Parse a <fileset> entry, and determine which files
2041      * should be included
2042      */  
2043     bool getFileSet(Element *elem,
2044                     MakeBase &propRef,
2045                     String &dir,
2046                                         std::vector<String> &result);
2048     /**
2049      * Return this object's property list
2050      */
2051     virtual std::map<String, String> &getProperties()
2052         { return properties; }
2054     /**
2055      * Return a named property if found, else a null string
2056      */
2057     virtual String getProperty(const String &name)
2058         {
2059         String val;
2060         std::map<String, String>::iterator iter;
2061         iter = properties.find(name);
2062         if (iter != properties.end())
2063             val = iter->second;
2064         return val;
2065         }
2068     std::map<String, String> properties;
2070     /**
2071      * Turn 'true' and 'false' into boolean values
2072      */             
2073     bool getBool(const String &str, bool &val);
2075     /**
2076      * Create a directory, making intermediate dirs
2077      * if necessary
2078      */                     
2079     bool createDirectory(const String &dirname);
2081     /**
2082      * Delete a directory and its children if desired
2083      */
2084     bool removeDirectory(const String &dirName);
2086     /**
2087      * Copy a file from one name to another. Perform only if needed
2088      */ 
2089     bool copyFile(const String &srcFile, const String &destFile);
2091     /**
2092      * Tests is the modification date of fileA is newer than fileB
2093      */ 
2094     bool isNewerThan(const String &fileA, const String &fileB);
2096 private:
2098     /**
2099      * replace variable refs like ${a} with their values
2100      */      
2101     bool getSubstitutions(const String &s, String &result);
2105 };
2110 /**
2111  *  Print a printf()-like formatted error message
2112  */
2113 void MakeBase::error(char *fmt, ...)
2115     va_list args;
2116     va_start(args,fmt);
2117     fprintf(stderr, "Make error: ");
2118     vfprintf(stderr, fmt, args);
2119     fprintf(stderr, "\n");
2120     va_end(args) ;
2125 /**
2126  *  Print a printf()-like formatted trace message
2127  */
2128 void MakeBase::status(char *fmt, ...)
2130     va_list args;
2131     va_start(args,fmt);
2132     fprintf(stdout, "-");
2133     vfprintf(stdout, fmt, args);
2134     fprintf(stdout, "\n");
2135     va_end(args) ;
2140 /**
2141  *  Resolve another path relative to this one
2142  */
2143 String MakeBase::resolve(const String &otherPath)
2145     URI otherURI(otherPath);
2146     URI fullURI = uri.resolve(otherURI);
2147     String ret = fullURI.toString();
2148     return ret;
2152 /**
2153  *  Print a printf()-like formatted trace message
2154  */
2155 void MakeBase::trace(char *fmt, ...)
2157     va_list args;
2158     va_start(args,fmt);
2159     fprintf(stdout, "Make: ");
2160     vfprintf(stdout, fmt, args);
2161     fprintf(stdout, "\n");
2162     va_end(args) ;
2166 /**
2167  *  Return the suffix, if any, of a file name
2168  */
2169 String MakeBase::getSuffix(const String &fname)
2171     if (fname.size() < 2)
2172         return "";
2173     unsigned int pos = fname.find_last_of('.');
2174     if (pos == fname.npos)
2175         return "";
2176     pos++;
2177     String res = fname.substr(pos, fname.size()-pos);
2178     //trace("suffix:%s", res.c_str()); 
2179     return res;
2184 /**
2185  * Break up a string into substrings delimited the characters
2186  * in delimiters.  Null-length substrings are ignored
2187  */  
2188 std::vector<String> MakeBase::tokenize(const String &str,
2189                                 const String &delimiters)
2192     std::vector<String> res;
2193     char *del = (char *)delimiters.c_str();
2194     String dmp;
2195     for (unsigned int i=0 ; i<str.size() ; i++)
2196         {
2197         char ch = str[i];
2198         char *p = (char *)0;
2199         for (p=del ; *p ; p++)
2200             if (*p == ch)
2201                 break;
2202         if (*p)
2203             {
2204             if (dmp.size() > 0)
2205                 {
2206                 res.push_back(dmp);
2207                 dmp.clear();
2208                 }
2209             }
2210         else
2211             {
2212             dmp.push_back(ch);
2213             }
2214         }
2215     //Add tail
2216     if (dmp.size() > 0)
2217         {
2218         res.push_back(dmp);
2219         dmp.clear();
2220         }
2222     return res;
2227 /**
2228  *  Removes whitespace from beginning and end of a string
2229  */
2230 String MakeBase::trim(const String &s)
2232     if (s.size() < 1)
2233         return s;
2234     
2235     //Find first non-ws char
2236     unsigned int begin = 0;
2237     for ( ; begin < s.size() ; begin++)
2238         {
2239         if (!isspace(s[begin]))
2240             break;
2241         }
2243     //Find first non-ws char, going in reverse
2244     unsigned int end = s.size() - 1;
2245     for ( ; end > begin ; end--)
2246         {
2247         if (!isspace(s[end]))
2248             break;
2249         }
2250     //trace("begin:%d  end:%d", begin, end);
2252     String res = s.substr(begin, end-begin+1);
2253     return res;
2256 /**
2257  * Return the native format of the canonical
2258  * path which we store
2259  */
2260 String MakeBase::getNativePath(const String &path)
2262 #ifdef __WIN32__
2263     String npath;
2264     unsigned int firstChar = 0;
2265     if (path.size() >= 3)
2266         {
2267         if (path[0] == '/' &&
2268             isalpha(path[1]) &&
2269             path[2] == ':')
2270             firstChar++;
2271         }
2272     for (unsigned int i=firstChar ; i<path.size() ; i++)
2273         {
2274         char ch = path[i];
2275         if (ch == '/')
2276             npath.push_back('\\');
2277         else
2278             npath.push_back(ch);
2279         }
2280     return npath;
2281 #else
2282     return path;
2283 #endif
2287 #ifdef __WIN32__
2288 #include <tchar.h>
2290 static String win32LastError()
2293     DWORD dw = GetLastError(); 
2295     LPVOID str;
2296     FormatMessage(
2297         FORMAT_MESSAGE_ALLOCATE_BUFFER | 
2298         FORMAT_MESSAGE_FROM_SYSTEM,
2299         NULL,
2300         dw,
2301         0,
2302         (LPTSTR) &str,
2303         0, NULL );
2304     LPTSTR p = _tcschr((const char *)str, _T('\r'));
2305     if(p != NULL)
2306         { // lose CRLF
2307         *p = _T('\0');
2308         }
2309     String ret = (char *)str;
2310     LocalFree(str);
2312     return ret;
2314 #endif
2318 /**
2319  * Execute a system call via the shell
2320  */
2321 bool MakeBase::executeCommand(const String &command,
2322                               const String &inbuf,
2323                                                           String &outbuf,
2324                                                           String &errbuf)
2326 #ifdef __WIN32__
2328     bool ret = true;
2330     //# Allocate a separate buffer for safety
2331     char *paramBuf = new char[command.size() + 1];
2332     if (!paramBuf)
2333        {
2334        error("executeCommand cannot allocate command buffer");
2335            return false;
2336        }
2337     strcpy(paramBuf, (char *)command.c_str());
2339     //# Create pipes
2340     SECURITY_ATTRIBUTES saAttr; 
2341     saAttr.nLength = sizeof(SECURITY_ATTRIBUTES); 
2342     saAttr.bInheritHandle = TRUE; 
2343     saAttr.lpSecurityDescriptor = NULL; 
2344     HANDLE stdinRead,  stdinWrite;
2345     HANDLE stdoutRead, stdoutWrite;
2346     HANDLE stderrRead, stderrWrite;
2347     if (!CreatePipe(&stdinRead, &stdinWrite, &saAttr, 0))
2348             {
2349                 error("executeProgram: could not create pipe");
2350         delete[] paramBuf;
2351                 return false;
2352                 } 
2353     SetHandleInformation(stdinWrite, HANDLE_FLAG_INHERIT, 0);
2354         if (!CreatePipe(&stdoutRead, &stdoutWrite, &saAttr, 0))
2355             {
2356                 error("executeProgram: could not create pipe");
2357         delete[] paramBuf;
2358                 return false;
2359                 } 
2360     SetHandleInformation(stdoutRead, HANDLE_FLAG_INHERIT, 0);
2361         if (!CreatePipe(&stderrRead, &stderrWrite, &saAttr, 0))
2362             {
2363                 error("executeProgram: could not create pipe");
2364         delete[] paramBuf;
2365                 return false;
2366                 } 
2367     SetHandleInformation(stderrRead, HANDLE_FLAG_INHERIT, 0);
2369     // Create the process
2370     STARTUPINFO siStartupInfo;
2371     PROCESS_INFORMATION piProcessInfo;
2372     memset(&siStartupInfo, 0, sizeof(siStartupInfo));
2373     memset(&piProcessInfo, 0, sizeof(piProcessInfo));
2374     siStartupInfo.cb = sizeof(siStartupInfo);
2375     siStartupInfo.hStdError   =  stderrWrite;
2376     siStartupInfo.hStdOutput  =  stdoutWrite;
2377     siStartupInfo.hStdInput   =  stdinRead;
2378     siStartupInfo.dwFlags    |=  STARTF_USESTDHANDLES;
2379    
2380     if (!CreateProcess(NULL, paramBuf, NULL, NULL, true,
2381                 0, NULL, NULL, &siStartupInfo,
2382                 &piProcessInfo))
2383         {
2384         error("executeCommand : could not create process : %s",
2385                             win32LastError().c_str());
2386         ret = false;
2387         }
2389     DWORD bytesWritten;
2390     if (inbuf.size()>0 &&
2391         !WriteFile(stdinWrite, inbuf.c_str(), inbuf.size(), 
2392                &bytesWritten, NULL))
2393         {
2394         error("executeCommand: could not write to pipe");
2395                 return false;
2396                 }       
2397     if (!CloseHandle(stdinWrite))
2398             {           
2399         error("executeCommand: could not close write pipe");
2400                 return false;
2401                 }
2402     if (!CloseHandle(stdoutWrite))
2403             {
2404         error("executeCommand: could not close read pipe");
2405                 return false;
2406                 }
2407     if (!CloseHandle(stderrWrite))
2408             {
2409         error("executeCommand: could not close read pipe");
2410                 return false;
2411                 }
2412         while (true)
2413         {
2414         //trace("## stderr");
2415         DWORD avail;
2416         if (!PeekNamedPipe(stderrRead, NULL, 0, NULL, &avail, NULL))
2417             break;
2418         if (avail > 0)
2419             {
2420             DWORD bytesRead = 0;
2421             char readBuf[1025];
2422             if (avail>1024) avail = 1024;
2423             if (!ReadFile(stderrRead, readBuf, avail, &bytesRead, NULL)
2424                 || bytesRead == 0)
2425                 {
2426                 break;
2427                 }
2428             for (int i=0 ; i<bytesRead ; i++)
2429                 errbuf.push_back(readBuf[i]);
2430             }
2431         //trace("## stdout");
2432         if (!PeekNamedPipe(stdoutRead, NULL, 0, NULL, &avail, NULL))
2433             break;
2434         if (avail > 0)
2435             {
2436             DWORD bytesRead = 0;
2437             char readBuf[1025];
2438             if (avail>1024) avail = 1024;
2439             if (!ReadFile(stdoutRead, readBuf, avail, &bytesRead, NULL)
2440                 || bytesRead==0)
2441                 {
2442                 break;
2443                 }
2444             for (int i=0 ; i<bytesRead ; i++)
2445                 outbuf.push_back(readBuf[i]);
2446             }
2447                 DWORD exitCode;
2448         GetExitCodeProcess(piProcessInfo.hProcess, &exitCode);
2449         if (exitCode != STILL_ACTIVE)
2450             break;
2451         Sleep(100);
2452         }       
2453     //trace("outbuf:%s", outbuf.c_str());
2454     if (!CloseHandle(stdoutRead))
2455         {
2456         error("executeCommand: could not close read pipe");
2457         return false;
2458         }
2459     if (!CloseHandle(stderrRead))
2460         {
2461         error("executeCommand: could not close read pipe");
2462         return false;
2463         }
2465     DWORD exitCode;
2466     GetExitCodeProcess(piProcessInfo.hProcess, &exitCode);
2467     //trace("exit code:%d", exitCode);
2468     if (exitCode != 0)
2469         {
2470         ret = false;
2471         }
2472     
2473     // Clean up
2474     CloseHandle(piProcessInfo.hProcess);
2475     CloseHandle(piProcessInfo.hThread);
2478     return ret;
2480 #else //do it unix-style
2482     String s;
2483     FILE *f = popen(command.c_str(), "r");
2484     int errnum = 0;
2485     if (f)
2486         {
2487         while (true)
2488             {
2489             int ch = fgetc(f);
2490             if (ch < 0)
2491                 break;
2492             s.push_back((char)ch);
2493             }
2494         errnum = pclose(f);
2495         }
2496         outbuf = s;
2497         if (errnum < 0)
2498             {
2499             error("exec of command '%s' failed : %s",
2500                      command.c_str(), strerror(errno));
2501             return false;
2502             }
2503         else
2504             return true;
2506 #endif
2507
2512 bool MakeBase::listDirectories(const String &baseName,
2513                               const String &dirName,
2514                               std::vector<String> &res)
2516     res.push_back(dirName);
2517     String fullPath = baseName;
2518     if (dirName.size()>0)
2519         {
2520         fullPath.append("/");
2521         fullPath.append(dirName);
2522         }
2523     DIR *dir = opendir(fullPath.c_str());
2524     while (true)
2525         {
2526         struct dirent *de = readdir(dir);
2527         if (!de)
2528             break;
2530         //Get the directory member name
2531         String s = de->d_name;
2532         if (s.size() == 0 || s[0] == '.')
2533             continue;
2534         String childName = dirName;
2535         childName.append("/");
2536         childName.append(s);
2538         String fullChildPath = baseName;
2539         fullChildPath.append("/");
2540         fullChildPath.append(childName);
2541         struct stat finfo;
2542         String childNative = getNativePath(fullChildPath);
2543         if (stat(childNative.c_str(), &finfo)<0)
2544             {
2545             error("cannot stat file:%s", childNative.c_str());
2546             }
2547         else if (S_ISDIR(finfo.st_mode))
2548             {
2549             //trace("directory: %s", childName.c_str());
2550             if (!listDirectories(baseName, childName, res))
2551                 return false;
2552             }
2553         }
2554     closedir(dir);
2556     return true;
2560 bool MakeBase::listFiles(const String &baseDir,
2561                          const String &dirName,
2562                          std::vector<String> &excludes,
2563                          std::vector<String> &res)
2565     String fullDir = baseDir;
2566     if (dirName.size()>0)
2567         {
2568         fullDir.append("/");
2569         fullDir.append(dirName);
2570         }
2571     String dirNative = getNativePath(fullDir);
2573     std::vector<String> subdirs;
2574     DIR *dir = opendir(dirNative.c_str());
2575     while (true)
2576         {
2577         struct dirent *de = readdir(dir);
2578         if (!de)
2579             break;
2581         //Get the directory member name
2582         String s = de->d_name;
2583         if (s.size() == 0 || s[0] == '.')
2584             continue;
2585         String childName;
2586         if (dirName.size()>0)
2587             {
2588             childName.append(dirName);
2589             childName.append("/");
2590             }
2591         childName.append(s);
2592         String fullChild = baseDir;
2593         fullChild.append("/");
2594         fullChild.append(childName);
2595         
2596         if (std::find(excludes.begin(), excludes.end(), childName)
2597                         != excludes.end())
2598             {
2599             //trace("EXCLUDED:%s", childName.c_str());
2600             continue;
2601             }
2603         struct stat finfo;
2604         String nativeName = getNativePath(fullChild);
2605         if (stat(nativeName.c_str(), &finfo)<0)
2606             {
2607             error("cannot stat file:%s", childName.c_str());
2608             return false;
2609             }
2610         else if (S_ISDIR(finfo.st_mode))
2611             {
2612             //trace("directory: %s", childName.c_str());
2613             if (!listFiles(baseDir, childName, excludes, res))
2614                 return false;
2615             }
2616         else if (!S_ISREG(finfo.st_mode))
2617             {
2618             trace("not regular: %s", childName.c_str());
2619             }
2620         else
2621             {
2622             res.push_back(childName);
2623             }
2624         }
2625     closedir(dir);
2627     return true;
2637 bool MakeBase::getSubstitutions(const String &str, String &result)
2639     String s = trim(str);
2640     int len = (int)s.size();
2641     String val;
2642     for (int i=0 ; i<len ; i++)
2643         {
2644         char ch = s[i];
2645         if (ch == '$' && s[i+1] == '{')
2646                     {
2647             String varname;
2648                     int j = i+2;
2649                     for ( ; j<len ; j++)
2650                         {
2651                         ch = s[j];
2652                         if (ch == '$' && s[j+1] == '{')
2653                             {
2654                             error("attribute %s cannot have nested variable references",
2655                                    s.c_str());
2656                             return false;
2657                             }
2658                         else if (ch == '}')
2659                             {
2660                             std::map<String, String>::iterator iter;
2661                             iter = properties.find(trim(varname));
2662                             if (iter != properties.end())
2663                                 {
2664                                 val.append(iter->second);
2665                                 }
2666                             else
2667                                 {
2668                                 error("property ${%s} not found", varname.c_str());
2669                                 return false;
2670                                 }
2671                             break;
2672                             }
2673                         else
2674                             {
2675                             varname.push_back(ch);
2676                             }
2677                         }
2678                     i = j;
2679                         }
2680                 else
2681                     {
2682                     val.push_back(ch);
2683                     }
2684         }
2685     result = val;
2686     return true;
2690 bool MakeBase::getAttribute(Element *elem, const String &name,
2691                                     String &result)
2693     String s = elem->getAttribute(name);
2694     return getSubstitutions(s, result);
2698 bool MakeBase::getValue(Element *elem, String &result)
2700     String s = elem->getValue();
2701     int len = s.size();
2702     //Replace all runs of whitespace with a single space
2703     String stripped; 
2704     for (int i = 0 ; i<len ; i++)
2705         {
2706         char ch = s[i];
2707         if (isspace(ch))
2708             {
2709             stripped.push_back(' ');
2710             for ( ; i<len ; i++)
2711                 {
2712                 ch = s[i];
2713                 if (!isspace(ch))
2714                     {
2715                     stripped.push_back(ch);
2716                     break;
2717                     }
2718                 }
2719             }
2720         else
2721             {
2722             stripped.push_back(ch);
2723             }
2724         }
2725     return getSubstitutions(stripped, result);
2729 /**
2730  * Turn 'true' and 'false' into boolean values
2731  */                 
2732 bool MakeBase::getBool(const String &str, bool &val)
2734     if (str == "true")
2735         val = true;
2736     else if (str == "false")
2737         val = false;
2738     else
2739         {
2740         error("expected 'true' or 'false'.  found '%s'", str.c_str());
2741         return false;
2742         }
2743     return true;
2749 /**
2750  * Parse a <patternset> entry
2751  */  
2752 bool MakeBase::getPatternSet(Element *elem,
2753                           MakeBase &propRef,
2754                                                   std::vector<String> &includes,
2755                                                   std::vector<String> &excludes
2756                                                   )
2758     std::vector<Element *> children  = elem->getChildren();
2759     for (unsigned int i=0 ; i<children.size() ; i++)
2760         {
2761         Element *child = children[i];
2762         String tagName = child->getName();
2763         if (tagName == "exclude")
2764             {
2765             String fname;
2766                         if (!propRef.getAttribute(child, "name", fname))
2767                             return false;
2768             //trace("EXCLUDE: %s", fname.c_str());
2769             excludes.push_back(fname);
2770             }
2771         else if (tagName == "include")
2772             {
2773             String fname;
2774                         if (!propRef.getAttribute(child, "name", fname))
2775                             return false;
2776             //trace("INCLUDE: %s", fname.c_str());
2777             includes.push_back(fname);
2778             }
2779         }
2781     return true;
2787 /**
2788  * Parse a <fileset> entry, and determine which files
2789  * should be included
2790  */  
2791 bool MakeBase::getFileSet(Element *elem,
2792                           MakeBase &propRef,
2793                                                   String &dir,
2794                                                   std::vector<String> &result)
2796     String name = elem->getName();
2797     if (name != "fileset")
2798         {
2799         error("expected <fileset>");
2800         return false;
2801         }
2804     std::vector<String> includes;
2805     std::vector<String> excludes;
2807     //A fileset has one implied patternset
2808     if (!getPatternSet(elem, propRef, includes, excludes))
2809         {
2810         return false;
2811         }
2812     //Look for child tags, including more patternsets
2813     std::vector<Element *> children  = elem->getChildren();
2814     for (unsigned int i=0 ; i<children.size() ; i++)
2815         {
2816         Element *child = children[i];
2817         String tagName = child->getName();
2818         if (tagName == "patternset")
2819             {
2820             if (!getPatternSet(child, propRef, includes, excludes))
2821                 {
2822                 return false;
2823                 }
2824             }
2825         }
2827     //Now do the stuff
2828     //Get the base directory for reading file names
2829     bool doDir = true;
2830     if (!propRef.getAttribute(elem, "dir", dir))
2831         return false;
2833     std::vector<String> fileList;
2834     if (dir.size() > 0)
2835         {
2836         String baseDir = propRef.resolve(dir);
2837             if (!listFiles(baseDir, "", excludes, fileList))
2838                 return false;
2839             }
2840         
2841         std::vector<String>::iterator iter;
2842     for (iter=includes.begin() ; iter!=includes.end() ; iter++)
2843         {
2844         String fname = *iter;
2845         fileList.push_back(fname);
2846         }
2847         
2848         result = fileList;
2849         
2850         /*
2851         for (unsigned int i=0 ; i<result.size() ; i++)
2852             {
2853             trace("RES:%s", result[i].c_str());
2854             }
2855     */
2857     std::sort(fileList.begin(), fileList.end());
2858     
2859     return true;
2864 /**
2865  * Create a directory, making intermediate dirs
2866  * if necessary
2867  */                         
2868 bool MakeBase::createDirectory(const String &dirname)
2870     //trace("## createDirectory: %s", dirname.c_str());
2871     //## first check if it exists
2872     struct stat finfo;
2873     String nativeDir = getNativePath(dirname);
2874     char *cnative = (char *) nativeDir.c_str();
2875     if (stat(dirname.c_str(), &finfo)==0)
2876         {
2877         if (!S_ISDIR(finfo.st_mode))
2878             {
2879             error("mkdir: file %s exists but is not a directory",
2880                               cnative);
2881             return false;
2882             }
2883         else //exists
2884             {
2885             return true;
2886             }
2887         }
2889     //## 2: pull off the last path segment, if any,
2890     //## to make the dir 'above' this one, if necessary
2891     unsigned int pos = dirname.find_last_of('/');
2892     if (pos != dirname.npos)
2893         {
2894         String subpath = dirname.substr(0, pos);
2895         if (!createDirectory(subpath))
2896             return false;
2897         }
2898         
2899     //## 3: now make
2900     if (mkdir(cnative)<0)
2901         {
2902         error("cannot make directory %s", cnative);
2903         return false;
2904         }
2905         
2906     return true;
2910 /**
2911  * Remove a directory recursively
2912  */ 
2913 bool MakeBase::removeDirectory(const String &dirName)
2915     char *dname = (char *)dirName.c_str();
2917     DIR *dir = opendir(dname);
2918     if (!dir)
2919         {
2920         //# Let this fail nicely.
2921         return true;
2922         //error("error opening directory %s : %s", dname, strerror(errno));
2923         //return false;
2924         }
2925     
2926     while (true)
2927         {
2928         struct dirent *de = readdir(dir);
2929         if (!de)
2930             break;
2932         //Get the directory member name
2933         String s = de->d_name;
2934         if (s.size() == 0 || s[0] == '.')
2935             continue;
2936         String childName;
2937         if (dirName.size() > 0)
2938             {
2939             childName.append(dirName);
2940             childName.append("/");
2941             }
2942         childName.append(s);
2945         struct stat finfo;
2946         String childNative = getNativePath(childName);
2947         char *cnative = (char *)childNative.c_str();
2948         if (stat(cnative, &finfo)<0)
2949             {
2950             error("cannot stat file:%s", cnative);
2951             }
2952         else if (S_ISDIR(finfo.st_mode))
2953             {
2954             //trace("DEL dir: %s", childName.c_str());
2955                         if (!removeDirectory(childName))
2956                     {
2957                             return false;
2958                             }
2959             }
2960         else if (!S_ISREG(finfo.st_mode))
2961             {
2962             trace("not regular: %s", cnative);
2963             }
2964         else
2965             {
2966             //trace("DEL file: %s", childName.c_str());
2967             if (remove(cnative)<0)
2968                 {
2969                 error("error deleting %s : %s",
2970                                      cnative, strerror(errno));
2971                                 return false;
2972                                 }
2973             }
2974         }
2975     closedir(dir);
2977     //Now delete the directory
2978     String native = getNativePath(dirName);
2979     if (rmdir(native.c_str())<0)
2980         {
2981         error("could not delete directory %s : %s",
2982             native.c_str() , strerror(errno));
2983         return false;
2984         }
2986     return true;
2987     
2991 /**
2992  * Copy a file from one name to another. Perform only if needed
2993  */ 
2994 bool MakeBase::copyFile(const String &srcFile, const String &destFile)
2996     //# 1 Check up-to-date times
2997     String srcNative = getNativePath(srcFile);
2998     struct stat srcinfo;
2999     if (stat(srcNative.c_str(), &srcinfo)<0)
3000         {
3001         error("source file %s for copy does not exist",
3002                          srcNative.c_str());
3003         return false;
3004         }
3006     String destNative = getNativePath(destFile);
3007     struct stat destinfo;
3008     if (stat(destNative.c_str(), &destinfo)==0)
3009         {
3010         if (destinfo.st_mtime >= srcinfo.st_mtime)
3011             return true;
3012         }
3013         
3014     //# 2 prepare a destination directory if necessary
3015     unsigned int pos = destFile.find_last_of('/');
3016     if (pos != destFile.npos)
3017         {
3018         String subpath = destFile.substr(0, pos);
3019         if (!createDirectory(subpath))
3020             return false;
3021         }
3023     //# 3 do the data copy
3024     FILE *srcf = fopen(srcNative.c_str(), "rb");
3025     if (!srcf)
3026         {
3027         error("copyFile cannot open '%s' for reading", srcNative.c_str());
3028         return false;
3029         }
3030     FILE *destf = fopen(destNative.c_str(), "wb");
3031     if (!destf)
3032         {
3033         error("copyFile cannot open %s for writing", srcNative.c_str());
3034         return false;
3035         }
3037     while (!feof(srcf))
3038         {
3039         int ch = fgetc(srcf);
3040         if (ch<0)
3041             break;
3042         fputc(ch, destf);
3043         }
3045     fclose(destf);
3046     fclose(srcf);
3050     return true;
3055 /**
3056  * Tests is the modification of fileA is newer than fileB
3057  */ 
3058 bool MakeBase::isNewerThan(const String &fileA, const String &fileB)
3060     //trace("isNewerThan:'%s' , '%s'", fileA.c_str(), fileB.c_str());
3061     String nativeA = getNativePath(fileA);
3062     struct stat infoA;
3063     //IF source does not exist, NOT newer
3064     if (stat(nativeA.c_str(), &infoA)<0)
3065         {
3066                 return false;
3067                 }
3069     String nativeB = getNativePath(fileB);
3070     struct stat infoB;
3071     //IF dest does not exist, YES, newer
3072     if (stat(nativeB.c_str(), &infoB)<0)
3073         {
3074                 return true;
3075                 }
3077     //check the actual times
3078     if (infoA.st_mtime > infoB.st_mtime)
3079         {
3080                 return true;
3081                 }
3083     return false;
3087 //########################################################################
3088 //# P K G    C O N F I G
3089 //########################################################################
3091 /**
3092  *
3093  */
3094 class PkgConfig : public MakeBase
3097 public:
3099     /**
3100      *
3101      */
3102     PkgConfig()
3103         { init(); }
3105     /**
3106      *
3107      */
3108     PkgConfig(const String &namearg)
3109         { init(); name = namearg; }
3111     /**
3112      *
3113      */
3114     PkgConfig(const PkgConfig &other)
3115         { assign(other); }
3117     /**
3118      *
3119      */
3120     PkgConfig &operator=(const PkgConfig &other)
3121         { assign(other); return *this; }
3123     /**
3124      *
3125      */
3126     virtual ~PkgConfig()
3127         { }
3129     /**
3130      *
3131      */
3132     virtual String getName()
3133         { return name; }
3135     /**
3136      *
3137      */
3138     virtual String getDescription()
3139         { return description; }
3141     /**
3142      *
3143      */
3144     virtual String getCflags()
3145         { return cflags; }
3147     /**
3148      *
3149      */
3150     virtual String getLibs()
3151         { return libs; }
3153     /**
3154      *
3155      */
3156     virtual String getVersion()
3157         { return version; }
3159     /**
3160      *
3161      */
3162     virtual int getMajorVersion()
3163         { return majorVersion; }
3165     /**
3166      *
3167      */
3168     virtual int getMinorVersion()
3169         { return minorVersion; }
3171     /**
3172      *
3173      */
3174     virtual int getMicroVersion()
3175         { return microVersion; }
3177     /**
3178      *
3179      */
3180     virtual std::map<String, String> &getAttributes()
3181         { return attrs; }
3183     /**
3184      *
3185      */
3186     virtual std::vector<String> &getRequireList()
3187         { return requireList; }
3189     virtual bool readFile(const String &fileName);
3191 private:
3193     void init()
3194         {
3195         name         = "";
3196         description  = "";
3197         cflags       = "";
3198         libs         = "";
3199         requires     = "";
3200         version      = "";
3201         majorVersion = 0;
3202         minorVersion = 0;
3203         microVersion = 0;
3204         fileName     = "";
3205         attrs.clear();
3206         requireList.clear();
3207         }
3209     void assign(const PkgConfig &other)
3210         {
3211         name         = other.name;
3212         description  = other.description;
3213         cflags       = other.cflags;
3214         libs         = other.libs;
3215         requires     = other.requires;
3216         version      = other.version;
3217         majorVersion = other.majorVersion;
3218         minorVersion = other.minorVersion;
3219         microVersion = other.microVersion;
3220         fileName     = other.fileName;
3221         attrs        = other.attrs;
3222         requireList  = other.requireList;
3223         }
3227     int get(int pos);
3229     int skipwhite(int pos);
3231     int getword(int pos, String &ret);
3233     void parseRequires();
3235     void parseVersion();
3237     bool parse(const String &buf);
3239     void dumpAttrs();
3241     String name;
3243     String description;
3245     String cflags;
3247     String libs;
3249     String requires;
3251     String version;
3253     int majorVersion;
3255     int minorVersion;
3257     int microVersion;
3259     String fileName;
3261     std::map<String, String> attrs;
3263     std::vector<String> requireList;
3265     char *parsebuf;
3266     int parselen;
3267 };
3270 /**
3271  * Get a character from the buffer at pos.  If out of range,
3272  * return -1 for safety
3273  */
3274 int PkgConfig::get(int pos)
3276     if (pos>parselen)
3277         return -1;
3278     return parsebuf[pos];
3283 /**
3284  *  Skip over all whitespace characters beginning at pos.  Return
3285  *  the position of the first non-whitespace character.
3286  */
3287 int PkgConfig::skipwhite(int pos)
3289     while (pos < parselen)
3290         {
3291         int ch = get(pos);
3292         if (ch < 0)
3293             break;
3294         if (!isspace(ch))
3295             break;
3296         pos++;
3297         }
3298     return pos;
3302 /**
3303  *  Parse the buffer beginning at pos, for a word.  Fill
3304  *  'ret' with the result.  Return the position after the
3305  *  word.
3306  */
3307 int PkgConfig::getword(int pos, String &ret)
3309     while (pos < parselen)
3310         {
3311         int ch = get(pos);
3312         if (ch < 0)
3313             break;
3314         if (!isalnum(ch) && ch != '_' && ch != '-'&& ch != '.')
3315             break;
3316         ret.push_back((char)ch);
3317         pos++;
3318         }
3319     return pos;
3322 void PkgConfig::parseRequires()
3324     if (requires.size() == 0)
3325         return;
3326     parsebuf = (char *)requires.c_str();
3327     parselen = requires.size();
3328     int pos = 0;
3329     while (pos < parselen)
3330         {
3331         pos = skipwhite(pos);
3332         String val;
3333         int pos2 = getword(pos, val);
3334         if (pos2 == pos)
3335             break;
3336         pos = pos2;
3337         //trace("val %s", val.c_str());
3338         requireList.push_back(val);
3339         }
3342 static int getint(const String str)
3344     char *s = (char *)str.c_str();
3345     char *ends = NULL;
3346     long val = strtol(s, &ends, 10);
3347     if (ends == s)
3348         return 0L;
3349     else
3350         return val;
3353 void PkgConfig::parseVersion()
3355     if (version.size() == 0)
3356         return;
3357     String s1, s2, s3;
3358     unsigned int pos = 0;
3359     unsigned int pos2 = version.find('.', pos);
3360     if (pos2 == version.npos)
3361         {
3362         s1 = version;
3363         }
3364     else
3365         {
3366         s1 = version.substr(pos, pos2-pos);
3367         pos = pos2;
3368         pos++;
3369         if (pos < version.size())
3370             {
3371             pos2 = version.find('.', pos);
3372             if (pos2 == version.npos)
3373                 {
3374                 s2 = version.substr(pos, version.size()-pos);
3375                 }
3376             else
3377                 {
3378                 s2 = version.substr(pos, pos2-pos);
3379                 pos = pos2;
3380                 pos++;
3381                 if (pos < version.size())
3382                     s3 = version.substr(pos, pos2-pos);
3383                 }
3384             }
3385         }
3387     majorVersion = getint(s1);
3388     minorVersion = getint(s2);
3389     microVersion = getint(s3);
3390     //trace("version:%d.%d.%d", majorVersion,
3391     //          minorVersion, microVersion );
3395 bool PkgConfig::parse(const String &buf)
3397     init();
3399     parsebuf = (char *)buf.c_str();
3400     parselen = buf.size();
3401     int pos = 0;
3404     while (pos < parselen)
3405         {
3406         String attrName;
3407         pos = skipwhite(pos);
3408         int ch = get(pos);
3409         if (ch == '#')
3410             {
3411             //comment.  eat the rest of the line
3412             while (pos < parselen)
3413                 {
3414                 ch = get(pos);
3415                 if (ch == '\n' || ch < 0)
3416                     break;
3417                 pos++;
3418                 }
3419             continue;
3420             }
3421         pos = getword(pos, attrName);
3422         if (attrName.size() == 0)
3423             continue;
3424         pos = skipwhite(pos);
3425         ch = get(pos);
3426         if (ch != ':' && ch != '=')
3427             {
3428             error("expected ':' or '='");
3429             return false;
3430             }
3431         pos++;
3432         pos = skipwhite(pos);
3433         String attrVal;
3434         while (pos < parselen)
3435             {
3436             ch = get(pos);
3437             if (ch == '\n' || ch < 0)
3438                 break;
3439             else if (ch == '$' && get(pos+1) == '{')
3440                 {
3441                 //#  this is a ${substitution}
3442                 pos += 2;
3443                 String subName;
3444                 while (pos < parselen)
3445                     {
3446                     ch = get(pos);
3447                     if (ch < 0)
3448                         {
3449                         error("unterminated substitution");
3450                         return false;
3451                         }
3452                     else if (ch == '}')
3453                         break;
3454                     else
3455                         subName.push_back((char)ch);
3456                     pos++;
3457                     }
3458                 //trace("subName:%s", subName.c_str());
3459                 String subVal = attrs[subName];
3460                 //trace("subVal:%s", subVal.c_str());
3461                 attrVal.append(subVal);
3462                 }
3463             else
3464                 attrVal.push_back((char)ch);
3465             pos++;
3466             }
3468         attrVal = trim(attrVal);
3469         attrs[attrName] = attrVal;
3471         if (attrName == "Name")
3472             name = attrVal;
3473         else if (attrName == "Description")
3474             description = attrVal;
3475         else if (attrName == "Cflags")
3476             cflags = attrVal;
3477         else if (attrName == "Libs")
3478             libs = attrVal;
3479         else if (attrName == "Requires")
3480             requires = attrVal;
3481         else if (attrName == "Version")
3482             version = attrVal;
3484         //trace("name:'%s'  value:'%s'",
3485         //      attrName.c_str(), attrVal.c_str());
3486         }
3489     parseRequires();
3490     parseVersion();
3492     return true;
3495 void PkgConfig::dumpAttrs()
3497     trace("### PkgConfig attributes for %s", fileName.c_str());
3498     std::map<String, String>::iterator iter;
3499     for (iter=attrs.begin() ; iter!=attrs.end() ; iter++)
3500         {
3501         trace("   %s = %s", iter->first.c_str(), iter->second.c_str());
3502         }
3506 bool PkgConfig::readFile(const String &fileNameArg)
3508     fileName = fileNameArg;
3510     FILE *f = fopen(fileName.c_str(), "r");
3511     if (!f)
3512         {
3513         error("cannot open file '%s' for reading", fileName.c_str());
3514         return false;
3515         }
3516     String buf;
3517     while (true)
3518         {
3519         int ch = fgetc(f);
3520         if (ch < 0)
3521             break;
3522         buf.push_back((char)ch);
3523         }
3524     fclose(f);
3526     trace("####### File:\n%s", buf.c_str());
3527     if (!parse(buf))
3528         {
3529         return false;
3530         }
3532     dumpAttrs();
3534     return true;
3541 //########################################################################
3542 //# D E P T O O L
3543 //########################################################################
3547 /**
3548  *  Class which holds information for each file.
3549  */
3550 class FileRec
3552 public:
3554     typedef enum
3555         {
3556         UNKNOWN,
3557         CFILE,
3558         HFILE,
3559         OFILE
3560         } FileType;
3562     /**
3563      *  Constructor
3564      */
3565     FileRec()
3566         {init(); type = UNKNOWN;}
3568     /**
3569      *  Copy constructor
3570      */
3571     FileRec(const FileRec &other)
3572         {init(); assign(other);}
3573     /**
3574      *  Constructor
3575      */
3576     FileRec(int typeVal)
3577         {init(); type = typeVal;}
3578     /**
3579      *  Assignment operator
3580      */
3581     FileRec &operator=(const FileRec &other)
3582         {init(); assign(other); return *this;}
3585     /**
3586      *  Destructor
3587      */
3588     ~FileRec()
3589         {}
3591     /**
3592      *  Directory part of the file name
3593      */
3594     String path;
3596     /**
3597      *  Base name, sans directory and suffix
3598      */
3599     String baseName;
3601     /**
3602      *  File extension, such as cpp or h
3603      */
3604     String suffix;
3606     /**
3607      *  Type of file: CFILE, HFILE, OFILE
3608      */
3609     int type;
3611     /**
3612      * Used to list files ref'd by this one
3613      */
3614     std::map<String, FileRec *> files;
3617 private:
3619     void init()
3620         {
3621         }
3623     void assign(const FileRec &other)
3624         {
3625         type     = other.type;
3626         baseName = other.baseName;
3627         suffix   = other.suffix;
3628         files    = other.files;
3629         }
3631 };
3635 /**
3636  *  Simpler dependency record
3637  */
3638 class DepRec
3640 public:
3642     /**
3643      *  Constructor
3644      */
3645     DepRec()
3646         {init();}
3648     /**
3649      *  Copy constructor
3650      */
3651     DepRec(const DepRec &other)
3652         {init(); assign(other);}
3653     /**
3654      *  Constructor
3655      */
3656     DepRec(const String &fname)
3657         {init(); name = fname; }
3658     /**
3659      *  Assignment operator
3660      */
3661     DepRec &operator=(const DepRec &other)
3662         {init(); assign(other); return *this;}
3665     /**
3666      *  Destructor
3667      */
3668     ~DepRec()
3669         {}
3671     /**
3672      *  Directory part of the file name
3673      */
3674     String path;
3676     /**
3677      *  Base name, without the path and suffix
3678      */
3679     String name;
3681     /**
3682      *  Suffix of the source
3683      */
3684     String suffix;
3687     /**
3688      * Used to list files ref'd by this one
3689      */
3690     std::vector<String> files;
3693 private:
3695     void init()
3696         {
3697         }
3699     void assign(const DepRec &other)
3700         {
3701         path     = other.path;
3702         name     = other.name;
3703         suffix   = other.suffix;
3704         files    = other.files;
3705         }
3707 };
3710 class DepTool : public MakeBase
3712 public:
3714     /**
3715      *  Constructor
3716      */
3717     DepTool()
3718         {init();}
3720     /**
3721      *  Copy constructor
3722      */
3723     DepTool(const DepTool &other)
3724         {init(); assign(other);}
3726     /**
3727      *  Assignment operator
3728      */
3729     DepTool &operator=(const DepTool &other)
3730         {init(); assign(other); return *this;}
3733     /**
3734      *  Destructor
3735      */
3736     ~DepTool()
3737         {}
3740     /**
3741      *  Reset this section of code
3742      */
3743     virtual void init();
3744     
3745     /**
3746      *  Reset this section of code
3747      */
3748     virtual void assign(const DepTool &other)
3749         {
3750         }
3751     
3752     /**
3753      *  Sets the source directory which will be scanned
3754      */
3755     virtual void setSourceDirectory(const String &val)
3756         { sourceDir = val; }
3758     /**
3759      *  Returns the source directory which will be scanned
3760      */
3761     virtual String getSourceDirectory()
3762         { return sourceDir; }
3764     /**
3765      *  Sets the list of files within the directory to analyze
3766      */
3767     virtual void setFileList(const std::vector<String> &list)
3768         { fileList = list; }
3770     /**
3771      * Creates the list of all file names which will be
3772      * candidates for further processing.  Reads make.exclude
3773      * to see which files for directories to leave out.
3774      */
3775     virtual bool createFileList();
3778     /**
3779      *  Generates the forward dependency list
3780      */
3781     virtual bool generateDependencies();
3784     /**
3785      *  Generates the forward dependency list, saving the file
3786      */
3787     virtual bool generateDependencies(const String &);
3790     /**
3791      *  Load a dependency file
3792      */
3793     std::vector<DepRec> loadDepFile(const String &fileName);
3795     /**
3796      *  Load a dependency file, generating one if necessary
3797      */
3798     std::vector<DepRec> getDepFile(const String &fileName);
3800     /**
3801      *  Save a dependency file
3802      */
3803     bool saveDepFile(const String &fileName);
3806 private:
3809     /**
3810      *
3811      */
3812     void parseName(const String &fullname,
3813                    String &path,
3814                    String &basename,
3815                    String &suffix);
3817     /**
3818      *
3819      */
3820     int get(int pos);
3822     /**
3823      *
3824      */
3825     int skipwhite(int pos);
3827     /**
3828      *
3829      */
3830     int getword(int pos, String &ret);
3832     /**
3833      *
3834      */
3835     bool sequ(int pos, char *key);
3837     /**
3838      *
3839      */
3840     bool addIncludeFile(FileRec *frec, const String &fname);
3842     /**
3843      *
3844      */
3845     bool scanFile(const String &fname, FileRec *frec);
3847     /**
3848      *
3849      */
3850     bool processDependency(FileRec *ofile,
3851                            FileRec *include,
3852                            int depth);
3854     /**
3855      *
3856      */
3857     String sourceDir;
3859     /**
3860      *
3861      */
3862     std::vector<String> fileList;
3864     /**
3865      *
3866      */
3867     std::vector<String> directories;
3869     /**
3870      * A list of all files which will be processed for
3871      * dependencies.  This is the only list that has the actual
3872      * records.  All other lists have pointers to these records.     
3873      */
3874     std::map<String, FileRec *> allFiles;
3876     /**
3877      * The list of .o files, and the
3878      * dependencies upon them.
3879      */
3880     std::map<String, FileRec *> depFiles;
3882     int depFileSize;
3883     char *depFileBuf;
3884     
3886 };
3892 /**
3893  *  Clean up after processing.  Called by the destructor, but should
3894  *  also be called before the object is reused.
3895  */
3896 void DepTool::init()
3898     sourceDir = ".";
3900     fileList.clear();
3901     directories.clear();
3902     
3903     //clear refs
3904     depFiles.clear();
3905     //clear records
3906     std::map<String, FileRec *>::iterator iter;
3907     for (iter=allFiles.begin() ; iter!=allFiles.end() ; iter++)
3908          delete iter->second;
3910     allFiles.clear(); 
3917 /**
3918  *  Parse a full path name into path, base name, and suffix
3919  */
3920 void DepTool::parseName(const String &fullname,
3921                         String &path,
3922                         String &basename,
3923                         String &suffix)
3925     if (fullname.size() < 2)
3926         return;
3928     unsigned int pos = fullname.find_last_of('/');
3929     if (pos != fullname.npos && pos<fullname.size()-1)
3930         {
3931         path = fullname.substr(0, pos);
3932         pos++;
3933         basename = fullname.substr(pos, fullname.size()-pos);
3934         }
3935     else
3936         {
3937         path = "";
3938         basename = fullname;
3939         }
3941     pos = basename.find_last_of('.');
3942     if (pos != basename.npos && pos<basename.size()-1)
3943         {
3944         suffix   = basename.substr(pos+1, basename.size()-pos-1);
3945         basename = basename.substr(0, pos);
3946         }
3948     //trace("parsename:%s %s %s", path.c_str(),
3949     //        basename.c_str(), suffix.c_str()); 
3954 /**
3955  *  Generate our internal file list.
3956  */
3957 bool DepTool::createFileList()
3960     for (unsigned int i=0 ; i<fileList.size() ; i++)
3961         {
3962         String fileName = fileList[i];
3963         //trace("## FileName:%s", fileName.c_str());
3964         String path;
3965         String basename;
3966         String sfx;
3967         parseName(fileName, path, basename, sfx);
3968         if (sfx == "cpp" || sfx == "c" || sfx == "cxx"   ||
3969                     sfx == "cc" || sfx == "CC")
3970             {
3971             FileRec *fe         = new FileRec(FileRec::CFILE);
3972             fe->path            = path;
3973             fe->baseName        = basename;
3974             fe->suffix          = sfx;
3975             allFiles[fileName]  = fe;
3976             }
3977         else if (sfx == "h"   ||  sfx == "hh"  ||
3978                  sfx == "hpp" ||  sfx == "hxx")
3979             {
3980             FileRec *fe         = new FileRec(FileRec::HFILE);
3981             fe->path            = path;
3982             fe->baseName        = basename;
3983             fe->suffix          = sfx;
3984             allFiles[fileName]  = fe;
3985             }
3986         }
3988     if (!listDirectories(sourceDir, "", directories))
3989         return false;
3990         
3991     return true;
3998 /**
3999  * Get a character from the buffer at pos.  If out of range,
4000  * return -1 for safety
4001  */
4002 int DepTool::get(int pos)
4004     if (pos>depFileSize)
4005         return -1;
4006     return depFileBuf[pos];
4011 /**
4012  *  Skip over all whitespace characters beginning at pos.  Return
4013  *  the position of the first non-whitespace character.
4014  */
4015 int DepTool::skipwhite(int pos)
4017     while (pos < depFileSize)
4018         {
4019         int ch = get(pos);
4020         if (ch < 0)
4021             break;
4022         if (!isspace(ch))
4023             break;
4024         pos++;
4025         }
4026     return pos;
4030 /**
4031  *  Parse the buffer beginning at pos, for a word.  Fill
4032  *  'ret' with the result.  Return the position after the
4033  *  word.
4034  */
4035 int DepTool::getword(int pos, String &ret)
4037     while (pos < depFileSize)
4038         {
4039         int ch = get(pos);
4040         if (ch < 0)
4041             break;
4042         if (isspace(ch))
4043             break;
4044         ret.push_back((char)ch);
4045         pos++;
4046         }
4047     return pos;
4050 /**
4051  * Return whether the sequence of characters in the buffer
4052  * beginning at pos match the key,  for the length of the key
4053  */
4054 bool DepTool::sequ(int pos, char *key)
4056     while (*key)
4057         {
4058         if (*key != get(pos))
4059             return false;
4060         key++; pos++;
4061         }
4062     return true;
4067 /**
4068  *  Add an include file name to a file record.  If the name
4069  *  is not found in allFiles explicitly, try prepending include
4070  *  directory names to it and try again.
4071  */
4072 bool DepTool::addIncludeFile(FileRec *frec, const String &iname)
4075     std::map<String, FileRec *>::iterator iter =
4076            allFiles.find(iname);
4077     if (iter != allFiles.end()) //already exists
4078         {
4079          //h file in same dir
4080         FileRec *other = iter->second;
4081         //trace("local: '%s'", iname.c_str());
4082         frec->files[iname] = other;
4083         return true;
4084         }
4085     else 
4086         {
4087         //look in other dirs
4088         std::vector<String>::iterator diter;
4089         for (diter=directories.begin() ;
4090              diter!=directories.end() ; diter++)
4091             {
4092             String dfname = *diter;
4093             dfname.append("/");
4094             dfname.append(iname);
4095             iter = allFiles.find(dfname);
4096             if (iter != allFiles.end())
4097                 {
4098                 FileRec *other = iter->second;
4099                 //trace("other: '%s'", iname.c_str());
4100                 frec->files[dfname] = other;
4101                 return true;
4102                 }
4103             }
4104         }
4105     return true;
4110 /**
4111  *  Lightly parse a file to find the #include directives.  Do
4112  *  a bit of state machine stuff to make sure that the directive
4113  *  is valid.  (Like not in a comment).
4114  */
4115 bool DepTool::scanFile(const String &fname, FileRec *frec)
4117     String fileName;
4118     if (sourceDir.size() > 0)
4119         {
4120         fileName.append(sourceDir);
4121         fileName.append("/");
4122         }
4123     fileName.append(fname);
4124     String nativeName = getNativePath(fileName);
4125     FILE *f = fopen(nativeName.c_str(), "r");
4126     if (!f)
4127         {
4128         error("Could not open '%s' for reading", fname.c_str());
4129         return false;
4130         }
4131     String buf;
4132     while (true)
4133         {
4134         int ch = fgetc(f);
4135         if (ch < 0)
4136             break;
4137         buf.push_back((char)ch);
4138         }
4139     fclose(f);
4141     depFileSize = buf.size();
4142     depFileBuf  = (char *)buf.c_str();
4143     int pos = 0;
4146     while (pos < depFileSize)
4147         {
4148         //trace("p:%c", get(pos));
4150         //# Block comment
4151         if (get(pos) == '/' && get(pos+1) == '*')
4152             {
4153             pos += 2;
4154             while (pos < depFileSize)
4155                 {
4156                 if (get(pos) == '*' && get(pos+1) == '/')
4157                     {
4158                     pos += 2;
4159                     break;
4160                     }
4161                 else
4162                     pos++;
4163                 }
4164             }
4165         //# Line comment
4166         else if (get(pos) == '/' && get(pos+1) == '/')
4167             {
4168             pos += 2;
4169             while (pos < depFileSize)
4170                 {
4171                 if (get(pos) == '\n')
4172                     {
4173                     pos++;
4174                     break;
4175                     }
4176                 else
4177                     pos++;
4178                 }
4179             }
4180         //# #include! yaay
4181         else if (sequ(pos, "#include"))
4182             {
4183             pos += 8;
4184             pos = skipwhite(pos);
4185             String iname;
4186             pos = getword(pos, iname);
4187             if (iname.size()>2)
4188                 {
4189                 iname = iname.substr(1, iname.size()-2);
4190                 addIncludeFile(frec, iname);
4191                 }
4192             }
4193         else
4194             {
4195             pos++;
4196             }
4197         }
4199     return true;
4204 /**
4205  *  Recursively check include lists to find all files in allFiles to which
4206  *  a given file is dependent.
4207  */
4208 bool DepTool::processDependency(FileRec *ofile,
4209                              FileRec *include,
4210                              int depth)
4212     std::map<String, FileRec *>::iterator iter;
4213     for (iter=include->files.begin() ; iter!=include->files.end() ; iter++)
4214         {
4215         String fname  = iter->first;
4216         if (ofile->files.find(fname) != ofile->files.end())
4217             {
4218             //trace("file '%s' already seen", fname.c_str());
4219             continue;
4220             }
4221         FileRec *child  = iter->second;
4222         ofile->files[fname] = child;
4223       
4224         processDependency(ofile, child, depth+1);
4225         }
4228     return true;
4235 /**
4236  *  Generate the file dependency list.
4237  */
4238 bool DepTool::generateDependencies()
4240     std::map<String, FileRec *>::iterator iter;
4241     //# First pass.  Scan for all includes
4242     for (iter=allFiles.begin() ; iter!=allFiles.end() ; iter++)
4243         {
4244         FileRec *frec = iter->second;
4245         if (!scanFile(iter->first, frec))
4246             {
4247             //quit?
4248             }
4249         }
4251     //# Second pass.  Scan for all includes
4252     for (iter=allFiles.begin() ; iter!=allFiles.end() ; iter++)
4253         {
4254         FileRec *include = iter->second;
4255         if (include->type == FileRec::CFILE)
4256             {
4257             String cFileName = iter->first;
4258             FileRec *ofile      = new FileRec(FileRec::OFILE);
4259             ofile->path         = include->path;
4260             ofile->baseName     = include->baseName;
4261             ofile->suffix       = include->suffix;
4262             String fname     = include->path;
4263             if (fname.size()>0)
4264                 fname.append("/");
4265             fname.append(include->baseName);
4266             fname.append(".o");
4267             depFiles[fname]    = ofile;
4268             //add the .c file first?   no, don't
4269             //ofile->files[cFileName] = include;
4270             
4271             //trace("ofile:%s", fname.c_str());
4273             processDependency(ofile, include, 0);
4274             }
4275         }
4277       
4278     return true;
4283 /**
4284  *  High-level call to generate deps and optionally save them
4285  */
4286 bool DepTool::generateDependencies(const String &fileName)
4288     if (!createFileList())
4289         return false;
4290     if (!generateDependencies())
4291         return false;
4292     if (!saveDepFile(fileName))
4293         return false;
4294     return true;
4298 /**
4299  *   This saves the dependency cache.
4300  */
4301 bool DepTool::saveDepFile(const String &fileName)
4303     time_t tim;
4304     time(&tim);
4306     FILE *f = fopen(fileName.c_str(), "w");
4307     if (!f)
4308         {
4309         trace("cannot open '%s' for writing", fileName.c_str());
4310         }
4311     fprintf(f, "<?xml version='1.0'?>\n");
4312     fprintf(f, "<!--\n");
4313     fprintf(f, "########################################################\n");
4314     fprintf(f, "## File: build.dep\n");
4315     fprintf(f, "## Generated by BuildTool at :%s", ctime(&tim));
4316     fprintf(f, "########################################################\n");
4317     fprintf(f, "-->\n");
4319     fprintf(f, "<dependencies source='%s'>\n\n", sourceDir.c_str());
4320     std::map<String, FileRec *>::iterator iter;
4321     for (iter=depFiles.begin() ; iter!=depFiles.end() ; iter++)
4322         {
4323         FileRec *frec = iter->second;
4324         if (frec->type == FileRec::OFILE)
4325             {
4326             fprintf(f, "<object path='%s' name='%s' suffix='%s'>\n",
4327                              frec->path.c_str(), frec->baseName.c_str(), frec->suffix.c_str());
4328             std::map<String, FileRec *>::iterator citer;
4329             for (citer=frec->files.begin() ; citer!=frec->files.end() ; citer++)
4330                 {
4331                 String cfname = citer->first;
4332                 fprintf(f, "    <dep name='%s'/>\n", cfname.c_str());
4333                 }
4334             fprintf(f, "</object>\n\n");
4335             }
4336         }
4338     fprintf(f, "</dependencies>\n");
4339     fprintf(f, "\n");
4340     fprintf(f, "<!--\n");
4341     fprintf(f, "########################################################\n");
4342     fprintf(f, "## E N D\n");
4343     fprintf(f, "########################################################\n");
4344     fprintf(f, "-->\n");
4346     fclose(f);
4348     return true;
4354 /**
4355  *   This loads the dependency cache.
4356  */
4357 std::vector<DepRec> DepTool::loadDepFile(const String &depFile)
4359     std::vector<DepRec> result;
4360     
4361     Parser parser;
4362     Element *root = parser.parseFile(depFile.c_str());
4363     if (!root)
4364         {
4365         error("Could not open %s for reading", depFile.c_str());
4366         return result;
4367         }
4369     if (root->getChildren().size()==0 ||
4370         root->getChildren()[0]->getName()!="dependencies")
4371         {
4372         error("Main xml element should be <dependencies>");
4373         delete root;
4374         return result;
4375         }
4377     //########## Start parsing
4378     Element *depList = root->getChildren()[0];
4380     std::vector<Element *> objects = depList->getChildren();
4381     for (unsigned int i=0 ; i<objects.size() ; i++)
4382         {
4383         Element *objectElem = objects[i];
4384         String tagName = objectElem->getName();
4385         if (tagName == "object")
4386             {
4387             String objName   = objectElem->getAttribute("name");
4388              //trace("object:%s", objName.c_str());
4389             DepRec depObject(objName);
4390             depObject.path   = objectElem->getAttribute("path");
4391             depObject.suffix = objectElem->getAttribute("suffix");
4392             //########## DESCRIPTION
4393             std::vector<Element *> depElems = objectElem->getChildren();
4394             for (unsigned int i=0 ; i<depElems.size() ; i++)
4395                 {
4396                 Element *depElem = depElems[i];
4397                 tagName = depElem->getName();
4398                 if (tagName == "dep")
4399                     {
4400                     String depName = depElem->getAttribute("name");
4401                     //trace("    dep:%s", depName.c_str());
4402                     depObject.files.push_back(depName);
4403                     }
4404                 }
4405             result.push_back(depObject);
4406             }
4407         }
4409     delete root;
4411     return result;
4415 /**
4416  *   This loads the dependency cache.
4417  */
4418 std::vector<DepRec> DepTool::getDepFile(const String &depFile)
4420     std::vector<DepRec> result = loadDepFile(depFile);
4421     if (result.size() == 0)
4422         {
4423         generateDependencies(depFile);
4424         result = loadDepFile(depFile);
4425         }
4426     return result;
4432 //########################################################################
4433 //# T A S K
4434 //########################################################################
4435 //forward decl
4436 class Target;
4437 class Make;
4439 /**
4440  *
4441  */
4442 class Task : public MakeBase
4445 public:
4447     typedef enum
4448         {
4449         TASK_NONE,
4450         TASK_AR,
4451         TASK_CC,
4452         TASK_COPY,
4453         TASK_DELETE,
4454         TASK_JAR,
4455         TASK_JAVAC,
4456         TASK_LINK,
4457         TASK_MKDIR,
4458         TASK_MSGFMT,
4459         TASK_RANLIB,
4460         TASK_RC,
4461         TASK_STRIP,
4462         TASK_TSTAMP
4463         } TaskType;
4464         
4466     /**
4467      *
4468      */
4469     Task(MakeBase &par) : parent(par)
4470         { init(); }
4472     /**
4473      *
4474      */
4475     Task(const Task &other) : parent(other.parent)
4476         { init(); assign(other); }
4478     /**
4479      *
4480      */
4481     Task &operator=(const Task &other)
4482         { assign(other); return *this; }
4484     /**
4485      *
4486      */
4487     virtual ~Task()
4488         { }
4491     /**
4492      *
4493      */
4494     virtual MakeBase &getParent()
4495         { return parent; }
4497      /**
4498      *
4499      */
4500     virtual int  getType()
4501         { return type; }
4503     /**
4504      *
4505      */
4506     virtual void setType(int val)
4507         { type = val; }
4509     /**
4510      *
4511      */
4512     virtual String getName()
4513         { return name; }
4515     /**
4516      *
4517      */
4518     virtual bool execute()
4519         { return true; }
4521     /**
4522      *
4523      */
4524     virtual bool parse(Element *elem)
4525         { return true; }
4527     /**
4528      *
4529      */
4530     Task *createTask(Element *elem);
4533 protected:
4535     void init()
4536         {
4537         type = TASK_NONE;
4538         name = "none";
4539         }
4541     void assign(const Task &other)
4542         {
4543         type = other.type;
4544         name = other.name;
4545         }
4546         
4547     String getAttribute(Element *elem, const String &attrName)
4548         {
4549         String str;
4550         return str;
4551         }
4553     MakeBase &parent;
4555     int type;
4557     String name;
4558 };
4563 /**
4564  * Run the "ar" command to archive .o's into a .a
4565  */
4566 class TaskAr : public Task
4568 public:
4570     TaskAr(MakeBase &par) : Task(par)
4571         {
4572                 type = TASK_AR; name = "ar";
4573                 command = "ar crv";
4574                 }
4576     virtual ~TaskAr()
4577         {}
4579     virtual bool execute()
4580         {
4581         //trace("###########HERE %d", fileSet.size());
4582         bool doit = false;
4583         
4584         String fullOut = parent.resolve(fileName);
4585         //trace("ar fullout: %s", fullOut.c_str());
4586         
4588         for (unsigned int i=0 ; i<fileSet.size() ; i++)
4589             {
4590             String fname;
4591                         if (fileSetDir.size()>0)
4592                             {
4593                             fname.append(fileSetDir);
4594                 fname.append("/");
4595                 }
4596             fname.append(fileSet[i]);
4597             String fullName = parent.resolve(fname);
4598             //trace("ar : %s/%s", fullOut.c_str(), fullName.c_str());
4599             if (isNewerThan(fullName, fullOut))
4600                 doit = true;
4601             }
4602         //trace("Needs it:%d", doit);
4603         if (!doit)
4604             {
4605             return true;
4606             }
4608         String cmd = command;
4609         cmd.append(" ");
4610         cmd.append(fullOut);
4611         for (unsigned int i=0 ; i<fileSet.size() ; i++)
4612             {
4613             String fname;
4614                         if (fileSetDir.size()>0)
4615                             {
4616                             fname.append(fileSetDir);
4617                 fname.append("/");
4618                 }
4619             fname.append(fileSet[i]);
4620             String fullName = parent.resolve(fname);
4622             cmd.append(" ");
4623             cmd.append(fullName);
4624             }
4625         //trace("AR %d: %s", fileSet.size(), cmd.c_str());
4626         String outString, errString;
4627         if (!executeCommand(cmd.c_str(), "", outString, errString))
4628             {
4629             error("AR problem: %s", errString.c_str());
4630             return false;
4631             }
4633         return true;
4634         }
4636     virtual bool parse(Element *elem)
4637         {
4638         if (!parent.getAttribute(elem, "file", fileName))
4639             return false;
4640             
4641         std::vector<Element *> children = elem->getChildren();
4642         for (unsigned int i=0 ; i<children.size() ; i++)
4643             {
4644             Element *child = children[i];
4645             String tagName = child->getName();
4646             if (tagName == "fileset")
4647                 {
4648                 if (!getFileSet(child, parent, fileSetDir, fileSet))
4649                     return false;
4650                 }
4651             }
4652         return true;
4653         }
4655 private:
4657     String command;
4658     String fileName;
4659     String fileSetDir;
4660     std::vector<String> fileSet;
4662 };
4665 /**
4666  * This task runs the C/C++ compiler.  The compiler is invoked
4667  * for all .c or .cpp files which are newer than their correcsponding
4668  * .o files.  
4669  */
4670 class TaskCC : public Task
4672 public:
4674     TaskCC(MakeBase &par) : Task(par)
4675         {
4676                 type = TASK_CC; name = "cc";
4677                 ccCommand   = "gcc";
4678                 cxxCommand  = "g++";
4679                 source      = ".";
4680                 dest        = ".";
4681                 flags       = "";
4682                 defines     = "";
4683                 includes    = "";
4684                 sourceFiles.clear();
4685         }
4687     virtual ~TaskCC()
4688         {}
4690     virtual bool execute()
4691         {
4692         DepTool depTool;
4693         depTool.setSourceDirectory(source);
4694         depTool.setFileList(sourceFiles);
4695         std::vector<DepRec> deps = depTool.getDepFile("build.dep");
4696         
4697         String incs;
4698         incs.append("-I");
4699         incs.append(parent.resolve("."));
4700         incs.append(" ");
4701         if (includes.size()>0)
4702             {
4703             incs.append(includes);
4704             incs.append(" ");
4705             }
4706         std::set<String> paths;
4707         std::vector<DepRec>::iterator viter;
4708         for (viter=deps.begin() ; viter!=deps.end() ; viter++)
4709             {
4710             DepRec dep = *viter;
4711             if (dep.path.size()>0)
4712                 paths.insert(dep.path);
4713             }
4714         if (source.size()>0)
4715             {
4716             incs.append(" -I");
4717             incs.append(parent.resolve(source));
4718             incs.append(" ");
4719             }
4720         std::set<String>::iterator setIter;
4721         for (setIter=paths.begin() ; setIter!=paths.end() ; setIter++)
4722             {
4723             incs.append(" -I");
4724             String dname;
4725             if (source.size()>0)
4726                 {
4727                 dname.append(source);
4728                 dname.append("/");
4729                 }
4730             dname.append(*setIter);
4731             incs.append(parent.resolve(dname));
4732             }
4733         std::vector<String> cfiles;
4734         for (viter=deps.begin() ; viter!=deps.end() ; viter++)
4735             {
4736             DepRec dep = *viter;
4738             //## Select command
4739             String sfx = dep.suffix;
4740             String command = ccCommand;
4741             if (sfx == "cpp" || sfx == "c++" || sfx == "cc"
4742                              || sfx == "CC")
4743                             command = cxxCommand;
4744  
4745             //## Make paths
4746             String destPath = dest;
4747             String srcPath  = source;
4748             if (dep.path.size()>0)
4749                             {
4750                 destPath.append("/");
4751                                 destPath.append(dep.path);
4752                 srcPath.append("/");
4753                                 srcPath.append(dep.path);
4754                             }
4755             //## Make sure destination directory exists
4756                         if (!createDirectory(destPath))
4757                             return false;
4758                             
4759             //## Check whether it needs to be done
4760                         String destFullName = destPath;
4761                         destFullName.append("/");
4762                         destFullName.append(dep.name);
4763                         destFullName.append(".o");
4764                         String srcFullName = srcPath;
4765                         srcFullName.append("/");
4766                         srcFullName.append(dep.name);
4767                         srcFullName.append(".");
4768                         srcFullName.append(dep.suffix);
4769             if (!isNewerThan(srcFullName, destFullName))
4770                 {
4771                 //trace("%s skipped", srcFullName.c_str());
4772                 continue;
4773                 }
4775             //## Assemble the command
4776             String cmd = command;
4777             cmd.append(" -c ");
4778             cmd.append(flags);
4779                         cmd.append(" ");
4780             cmd.append(defines);
4781                         cmd.append(" ");
4782             cmd.append(incs);
4783                         cmd.append(" ");
4784                         cmd.append(srcFullName);
4785             cmd.append(" -o ");
4786                         cmd.append(destFullName);
4788             //## Execute the command
4789             trace("cmd: %s", cmd.c_str());
4790             String outString, errString;
4791             if (!executeCommand(cmd.c_str(), "", outString, errString))
4792                 {
4793                 error("problem compiling: %s", errString.c_str());
4794                 return false;
4795                 }
4796             }
4797         
4798         return true;
4799         }
4801     virtual bool parse(Element *elem)
4802         {
4803         String s;
4804         if (!parent.getAttribute(elem, "command", s))
4805             return false;
4806         if (s.size()>0) { ccCommand = s; cxxCommand = s; }
4807         if (!parent.getAttribute(elem, "cc", s))
4808             return false;
4809         if (s.size()>0) ccCommand = s;
4810         if (!parent.getAttribute(elem, "cxx", s))
4811             return false;
4812         if (s.size()>0) cxxCommand = s;
4813         if (!parent.getAttribute(elem, "destdir", s))
4814             return false;
4815         if (s.size()>0) dest = s;
4817         std::vector<Element *> children = elem->getChildren();
4818         for (unsigned int i=0 ; i<children.size() ; i++)
4819             {
4820             Element *child = children[i];
4821             String tagName = child->getName();
4822             if (tagName == "flags")
4823                 {
4824                 if (!parent.getValue(child, flags))
4825                     return false;
4826                 }
4827             else if (tagName == "includes")
4828                 {
4829                 if (!parent.getValue(child, includes))
4830                     return false;
4831                 }
4832             else if (tagName == "defines")
4833                 {
4834                 if (!parent.getValue(child, defines))
4835                     return false;
4836                 }
4837             else if (tagName == "fileset")
4838                 {
4839                 if (!getFileSet(child, parent, source, sourceFiles))
4840                     return false;
4841                 }
4842             }
4844         return true;
4845         }
4846         
4847 protected:
4849     String ccCommand;
4850     String cxxCommand;
4851     String source;
4852     String dest;
4853     String flags;
4854     String defines;
4855     String includes;
4856     std::vector<String> sourceFiles;
4857     
4858 };
4862 /**
4863  *
4864  */
4865 class TaskCopy : public Task
4867 public:
4869     typedef enum
4870         {
4871         CP_NONE,
4872         CP_TOFILE,
4873         CP_TODIR
4874         } CopyType;
4876     TaskCopy(MakeBase &par) : Task(par)
4877         {
4878                 type = TASK_COPY; name = "copy";
4879                 cptype = CP_NONE;
4880                 verbose = false;
4881                 haveFileSet = false;
4882                 }
4884     virtual ~TaskCopy()
4885         {}
4887     virtual bool execute()
4888         {
4889         switch (cptype)
4890            {
4891            case CP_TOFILE:
4892                {
4893                if (fileName.size()>0)
4894                    {
4895                    String fullSource = parent.resolve(fileName);
4896                    String fullDest = parent.resolve(toFileName);
4897                    //trace("copy %s to file %s", fullSource.c_str(),
4898                                    //                       fullDest.c_str());
4899                    if (!isNewerThan(fullSource, fullDest))
4900                        {
4901                        return true;
4902                        }
4903                    if (!copyFile(fullSource, fullDest))
4904                        return false;
4905                    }
4906                return true;
4907                }
4908            case CP_TODIR:
4909                {
4910                if (haveFileSet)
4911                    {
4912                    for (unsigned int i=0 ; i<fileSet.size() ; i++)
4913                        {
4914                        String fileName = fileSet[i];
4916                        String sourcePath;
4917                        if (fileSetDir.size()>0)
4918                            {
4919                            sourcePath.append(fileSetDir);
4920                            sourcePath.append("/");
4921                            }
4922                        sourcePath.append(fileName);
4923                        String fullSource = parent.resolve(sourcePath);
4924                        
4925                        //Get the immediate parent directory's base name
4926                        String baseFileSetDir = fileSetDir;
4927                        int pos = baseFileSetDir.find_last_of('/');
4928                        if (pos>0 && pos < baseFileSetDir.size()-1)
4929                            baseFileSetDir =
4930                                                       baseFileSetDir.substr(pos+1,
4931                                                                baseFileSetDir.size());
4932                                            //Now make the new path
4933                        String destPath;
4934                        if (toDirName.size()>0)
4935                            {
4936                            destPath.append(toDirName);
4937                            destPath.append("/");
4938                            }
4939                        if (baseFileSetDir.size()>0)
4940                            {
4941                            destPath.append(baseFileSetDir);
4942                            destPath.append("/");
4943                            }
4944                        destPath.append(fileName);
4945                        String fullDest = parent.resolve(destPath);
4946                        //trace("fileName:%s", fileName.c_str());
4947                        //trace("copy %s to new dir : %s", fullSource.c_str(),
4948                                        //                   fullDest.c_str());
4949                        if (!isNewerThan(fullSource, fullDest))
4950                            {
4951                            //trace("copy skipping %s", fullSource.c_str());
4952                            continue;
4953                            }
4954                        if (!copyFile(fullSource, fullDest))
4955                            return false;
4956                        }
4957                    }
4958                else //file source
4959                    {
4960                    //For file->dir we want only the basename of
4961                    //the source appended to the dest dir
4962                    String baseName = fileName;
4963                    int pos = baseName.find_last_of('/');
4964                    if (pos > 0 && pos<baseName.size()-1)
4965                        baseName = baseName.substr(pos+1, baseName.size());
4966                    String fullSource = parent.resolve(fileName);
4967                    String destPath;
4968                    if (toDirName.size()>0)
4969                        {
4970                        destPath.append(toDirName);
4971                        destPath.append("/");
4972                        }
4973                    destPath.append(baseName);
4974                    String fullDest = parent.resolve(destPath);
4975                    //trace("copy %s to new dir : %s", fullSource.c_str(),
4976                                    //                       fullDest.c_str());
4977                    if (!isNewerThan(fullSource, fullDest))
4978                        {
4979                        return true;
4980                        }
4981                    if (!copyFile(fullSource, fullDest))
4982                        return false;
4983                    }
4984                return true;
4985                }
4986            }
4987         return true;
4988         }
4991     virtual bool parse(Element *elem)
4992         {
4993         if (!parent.getAttribute(elem, "file", fileName))
4994             return false;
4995         if (!parent.getAttribute(elem, "tofile", toFileName))
4996             return false;
4997         if (toFileName.size() > 0)
4998             cptype = CP_TOFILE;
4999         if (!parent.getAttribute(elem, "todir", toDirName))
5000             return false;
5001         if (toDirName.size() > 0)
5002             cptype = CP_TODIR;
5003         String ret;
5004         if (!parent.getAttribute(elem, "verbose", ret))
5005             return false;
5006         if (ret.size()>0 && !getBool(ret, verbose))
5007             return false;
5008             
5009         haveFileSet = false;
5010         
5011         std::vector<Element *> children = elem->getChildren();
5012         for (unsigned int i=0 ; i<children.size() ; i++)
5013             {
5014             Element *child = children[i];
5015             String tagName = child->getName();
5016             if (tagName == "fileset")
5017                 {
5018                 if (!getFileSet(child, parent, fileSetDir, fileSet))
5019                     {
5020                     error("problem getting fileset");
5021                                         return false;
5022                                         }
5023                                 haveFileSet = true;
5024                 }
5025             }
5027         //Perform validity checks
5028                 if (fileName.size()>0 && fileSet.size()>0)
5029                     {
5030                     error("<copy> can only have one of : file= and <fileset>");
5031                     return false;
5032                     }
5033         if (toFileName.size()>0 && toDirName.size()>0)
5034             {
5035             error("<copy> can only have one of : tofile= or todir=");
5036             return false;
5037             }
5038         if (haveFileSet && toDirName.size()==0)
5039             {
5040             error("a <copy> task with a <fileset> must have : todir=");
5041             return false;
5042             }
5043                 if (cptype == CP_TOFILE && fileName.size()==0)
5044                     {
5045                     error("<copy> tofile= must be associated with : file=");
5046                     return false;
5047                     }
5048                 if (cptype == CP_TODIR && fileName.size()==0 && !haveFileSet)
5049                     {
5050                     error("<copy> todir= must be associated with : file= or <fileset>");
5051                     return false;
5052                     }
5054         return true;
5055         }
5056         
5057 private:
5059     int cptype;
5060     String fileName;
5061     String fileSetDir;
5062     std::vector<String> fileSet;
5063     String toFileName;
5064     String toDirName;
5065     bool verbose;
5066     bool haveFileSet;
5067 };
5070 /**
5071  *
5072  */
5073 class TaskDelete : public Task
5075 public:
5077     typedef enum
5078         {
5079         DEL_FILE,
5080         DEL_DIR,
5081         DEL_FILESET
5082         } DeleteType;
5084     TaskDelete(MakeBase &par) : Task(par)
5085         { 
5086                   type        = TASK_DELETE;
5087                   name        = "delete";
5088                   delType     = DEL_FILE;
5089           verbose     = false;
5090           quiet       = false;
5091           failOnError = true;
5092                 }
5094     virtual ~TaskDelete()
5095         {}
5097     virtual bool execute()
5098         {
5099         struct stat finfo;
5100         switch (delType)
5101             {
5102             case DEL_FILE:
5103                 {
5104                 String fullName = parent.resolve(fileName);
5105                 char *fname = (char *)fullName.c_str();
5106                 //does not exist
5107                 if (stat(fname, &finfo)<0)
5108                     return true;
5109                 //exists but is not a regular file
5110                 if (!S_ISREG(finfo.st_mode))
5111                     {
5112                     error("<delete> failed. '%s' exists and is not a regular file",
5113                           fname);
5114                     return false;
5115                     }
5116                 if (remove(fname)<0)
5117                     {
5118                     error("<delete> failed: %s", strerror(errno));
5119                     return false;
5120                     }
5121                 return true;
5122                 }
5123             case DEL_DIR:
5124                 {
5125                 String fullDir = parent.resolve(dirName);
5126                 char *dname = (char *)fullDir.c_str();
5127                 if (!removeDirectory(fullDir))
5128                     return false;
5129                 return true;
5130                 }
5131             }
5132         return true;
5133         }
5135     virtual bool parse(Element *elem)
5136         {
5137         if (!parent.getAttribute(elem, "file", fileName))
5138             return false;
5139         if (fileName.size() > 0)
5140             delType = DEL_FILE;
5141         if (!parent.getAttribute(elem, "dir", dirName))
5142             return false;
5143         if (dirName.size() > 0)
5144             delType = DEL_DIR;
5145         if (fileName.size()>0 && dirName.size()>0)
5146             {
5147             error("<delete> can only have one attribute of file= or dir=");
5148             return false;
5149             }
5150         String ret;
5151         if (!parent.getAttribute(elem, "verbose", ret))
5152             return false;
5153         if (ret.size()>0 && !getBool(ret, verbose))
5154             return false;
5155         if (!parent.getAttribute(elem, "quiet", ret))
5156             return false;
5157         if (ret.size()>0 && !getBool(ret, quiet))
5158             return false;
5159         if (!parent.getAttribute(elem, "failonerror", ret))
5160             return false;
5161         if (ret.size()>0 && !getBool(ret, failOnError))
5162             return false;
5163         return true;
5164         }
5166 private:
5168     int delType;
5169     String dirName;
5170     String fileName;
5171     bool verbose;
5172     bool quiet;
5173     bool failOnError;
5174 };
5177 /**
5178  *
5179  */
5180 class TaskJar : public Task
5182 public:
5184     TaskJar(MakeBase &par) : Task(par)
5185         { type = TASK_JAR; name = "jar"; }
5187     virtual ~TaskJar()
5188         {}
5190     virtual bool execute()
5191         {
5192         return true;
5193         }
5195     virtual bool parse(Element *elem)
5196         {
5197         return true;
5198         }
5199 };
5202 /**
5203  *
5204  */
5205 class TaskJavac : public Task
5207 public:
5209     TaskJavac(MakeBase &par) : Task(par)
5210         { type = TASK_JAVAC; name = "javac"; }
5212     virtual ~TaskJavac()
5213         {}
5215     virtual bool execute()
5216         {
5217         return true;
5218         }
5220     virtual bool parse(Element *elem)
5221         {
5222         return true;
5223         }
5224 };
5227 /**
5228  *
5229  */
5230 class TaskLink : public Task
5232 public:
5234     TaskLink(MakeBase &par) : Task(par)
5235         {
5236                 type = TASK_LINK; name = "link";
5237                 command = "g++";
5238                 }
5240     virtual ~TaskLink()
5241         {}
5243     virtual bool execute()
5244         {
5245         bool doit = false;
5246         String fullTarget = parent.resolve(fileName);
5247         String cmd = command;
5248         cmd.append(" -o ");
5249         cmd.append(fullTarget);
5250         cmd.append(" ");
5251         cmd.append(flags);
5252         for (unsigned int i=0 ; i<fileSet.size() ; i++)
5253             {
5254             cmd.append(" ");
5255             String obj;
5256             if (fileSetDir.size()>0)
5257                             {
5258                                 obj.append(fileSetDir);
5259                 obj.append("/");
5260                 }
5261             obj.append(fileSet[i]);
5262             String fullObj = parent.resolve(obj);
5263             cmd.append(fullObj);
5264             if (isNewerThan(fullObj, fullTarget))
5265                 doit = true;
5266             }
5267         cmd.append(" ");
5268         cmd.append(libs);
5269         if (!doit)
5270             {
5271             //trace("link not needed");
5272             return true;
5273             }
5274         //trace("LINK cmd:%s", cmd.c_str());
5276         String outString, errString;
5277         if (!executeCommand(cmd.c_str(), "", outString, errString))
5278             {
5279             error("LINK problem: %s", errString.c_str());
5280             return false;
5281             }
5282         return true;
5283         }
5285     virtual bool parse(Element *elem)
5286         {
5287         if (!parent.getAttribute(elem, "command", command))
5288             return false;
5289         if (!parent.getAttribute(elem, "out", fileName))
5290             return false;
5291             
5292         std::vector<Element *> children = elem->getChildren();
5293         for (unsigned int i=0 ; i<children.size() ; i++)
5294             {
5295             Element *child = children[i];
5296             String tagName = child->getName();
5297             if (tagName == "fileset")
5298                 {
5299                 if (!getFileSet(child, parent, fileSetDir, fileSet))
5300                     return false;
5301                 }
5302             else if (tagName == "flags")
5303                 {
5304                 if (!parent.getValue(child, flags))
5305                     return false;
5306                 }
5307             else if (tagName == "libs")
5308                 {
5309                 if (!parent.getValue(child, libs))
5310                     return false;
5311                 }
5312             }
5313         return true;
5314         }
5316 private:
5318     String command;
5319     String fileName;
5320     String flags;
5321     String libs;
5322     String fileSetDir;
5323     std::vector<String> fileSet;
5325 };
5329 /**
5330  * Create a named directory
5331  */
5332 class TaskMkDir : public Task
5334 public:
5336     TaskMkDir(MakeBase &par) : Task(par)
5337         { type = TASK_MKDIR; name = "mkdir"; }
5339     virtual ~TaskMkDir()
5340         {}
5342     virtual bool execute()
5343         {
5344         String fullDir = parent.resolve(dirName);
5345         //trace("fullDir:%s", fullDir.c_str());
5346         if (!createDirectory(fullDir))
5347             return false;
5348         return true;
5349         }
5351     virtual bool parse(Element *elem)
5352         {
5353         if (!parent.getAttribute(elem, "dir", dirName))
5354             return false;
5355         if (dirName.size() == 0)
5356             {
5357             error("<mkdir> requires 'dir=\"dirname\"' attribute");
5358             return false;
5359             }
5360         //trace("dirname:%s", dirName.c_str());
5361         return true;
5362         }
5364 private:
5366     String dirName;
5367 };
5371 /**
5372  * Create a named directory
5373  */
5374 class TaskMsgFmt: public Task
5376 public:
5378     TaskMsgFmt(MakeBase &par) : Task(par)
5379          {
5380                  type = TASK_MSGFMT;
5381                  name = "msgfmt";
5382                  command = "msgfmt";
5383                  }
5385     virtual ~TaskMsgFmt()
5386         {}
5388     virtual bool execute()
5389         {
5390         //trace("msgfmt: %d", fileSet.size());
5391         bool doit = false;
5392         
5393         String fullDest = parent.resolve(toDirName);
5395         for (unsigned int i=0 ; i<fileSet.size() ; i++)
5396             {
5397             String fileName = fileSet[i];
5398             if (getSuffix(fileName) != "po")
5399                 continue;
5400             String sourcePath;
5401                         if (fileSetDir.size()>0)
5402                             {
5403                             sourcePath.append(fileSetDir);
5404                 sourcePath.append("/");
5405                 }
5406             sourcePath.append(fileName);
5407             String fullSource = parent.resolve(sourcePath);
5409             String destPath;
5410                         if (toDirName.size()>0)
5411                             {
5412                             destPath.append(toDirName);
5413                 destPath.append("/");
5414                 }
5415             destPath.append(fileName);
5416             destPath[destPath.size()-2] = 'm';
5417             String fullDest = parent.resolve(destPath);
5419             if (!isNewerThan(fullSource, fullDest))
5420                 {
5421                 //trace("skip %s", fullSource.c_str());
5422                 continue;
5423                 }
5424                 
5425             String cmd = command;
5426             cmd.append(" ");
5427             cmd.append(fullSource);
5428             cmd.append(" -o ");
5429             cmd.append(fullDest);
5430             
5431             int pos = fullDest.find_last_of('/');
5432             if (pos>0)
5433                 {
5434                 String fullDestPath = fullDest.substr(0, pos);
5435                 if (!createDirectory(fullDestPath))
5436                     return false;
5437                 }
5439             String outString, errString;
5440             if (!executeCommand(cmd.c_str(), "", outString, errString))
5441                 {
5442                 error("<msgfmt> problem: %s", errString.c_str());
5443                 return false;
5444                 }
5445             }
5447         return true;
5448         }
5450     virtual bool parse(Element *elem)
5451         {
5452         if (!parent.getAttribute(elem, "todir", toDirName))
5453             return false;
5454             
5455         std::vector<Element *> children = elem->getChildren();
5456         for (unsigned int i=0 ; i<children.size() ; i++)
5457             {
5458             Element *child = children[i];
5459             String tagName = child->getName();
5460             if (tagName == "fileset")
5461                 {
5462                 if (!getFileSet(child, parent, fileSetDir, fileSet))
5463                     return false;
5464                 }
5465             }
5466         return true;
5467         }
5469 private:
5471     String command;
5472     String toDirName;
5473     String fileSetDir;
5474     std::vector<String> fileSet;
5476 };
5482 /**
5483  *  Process an archive to allow random access
5484  */
5485 class TaskRanlib : public Task
5487 public:
5489     TaskRanlib(MakeBase &par) : Task(par)
5490         { type = TASK_RANLIB; name = "ranlib"; }
5492     virtual ~TaskRanlib()
5493         {}
5495     virtual bool execute()
5496         {
5497         String fullName = parent.resolve(fileName);
5498         //trace("fullDir:%s", fullDir.c_str());
5499         String cmd = "ranlib ";
5500         cmd.append(fullName);
5501                 trace("<ranlib> cmd:%s", cmd.c_str());        
5502         String outbuf, errbuf;
5503         if (!executeCommand(cmd, "", outbuf, errbuf))
5504             return false;
5505         return true;
5506         }
5508     virtual bool parse(Element *elem)
5509         {
5510         if (!parent.getAttribute(elem, "file", fileName))
5511             return false;
5512         if (fileName.size() == 0)
5513             {
5514             error("<ranlib> requires 'file=\"fileNname\"' attribute");
5515             return false;
5516             }
5517         return true;
5518         }
5520 private:
5522     String fileName;
5523 };
5527 /**
5528  * Run the "ar" command to archive .o's into a .a
5529  */
5530 class TaskRC : public Task
5532 public:
5534     TaskRC(MakeBase &par) : Task(par)
5535         {
5536                 type = TASK_RC; name = "rc";
5537                 command = "windres -o";
5538                 }
5540     virtual ~TaskRC()
5541         {}
5543     virtual bool execute()
5544         {
5545         String fullFile = parent.resolve(fileName);
5546         String fullOut  = parent.resolve(outName);
5547         if (!isNewerThan(fullFile, fullOut))
5548             return true;
5549         String cmd = command;
5550         cmd.append(" ");
5551         cmd.append(fullOut);
5552         cmd.append(" ");
5553         cmd.append(flags);
5554         cmd.append(" ");
5555         cmd.append(fullFile);
5557         trace("cmd: %s", cmd.c_str());
5558         String outString, errString;
5559         if (!executeCommand(cmd.c_str(), "", outString, errString))
5560             {
5561             error("RC problem: %s", errString.c_str());
5562             return false;
5563             }
5564         return true;
5565         }
5567     virtual bool parse(Element *elem)
5568         {
5569         if (!parent.getAttribute(elem, "command", command))
5570             return false;
5571         if (!parent.getAttribute(elem, "file", fileName))
5572             return false;
5573         if (!parent.getAttribute(elem, "out", outName))
5574             return false;
5575         std::vector<Element *> children = elem->getChildren();
5576         for (unsigned int i=0 ; i<children.size() ; i++)
5577             {
5578             Element *child = children[i];
5579             String tagName = child->getName();
5580             if (tagName == "flags")
5581                 {
5582                 if (!parent.getValue(child, flags))
5583                     return false;
5584                 }
5585             }
5586         return true;
5587         }
5589 private:
5591     String command;
5592     String flags;
5593     String fileName;
5594     String outName;
5596 };
5600 /**
5601  * Strip an executable
5602  */
5603 class TaskStrip : public Task
5605 public:
5607     TaskStrip(MakeBase &par) : Task(par)
5608         { type = TASK_STRIP; name = "strip"; }
5610     virtual ~TaskStrip()
5611         {}
5613     virtual bool execute()
5614         {
5615         String fullName = parent.resolve(fileName);
5616         //trace("fullDir:%s", fullDir.c_str());
5617         String cmd = "strip ";
5618         cmd.append(fullName);
5619                 trace("<strip> cmd:%s", cmd.c_str());        
5620         String outbuf, errbuf;
5621         if (!executeCommand(cmd, "", outbuf, errbuf))
5622             return false;
5623         return true;
5624         }
5626     virtual bool parse(Element *elem)
5627         {
5628         if (!parent.getAttribute(elem, "file", fileName))
5629             return false;
5630         if (fileName.size() == 0)
5631             {
5632             error("<strip> requires 'file=\"fileNname\"' attribute");
5633             return false;
5634             }
5635         return true;
5636         }
5638 private:
5640     String fileName;
5641 };
5644 /**
5645  *
5646  */
5647 class TaskTstamp : public Task
5649 public:
5651     TaskTstamp(MakeBase &par) : Task(par)
5652         { type = TASK_TSTAMP; name = "tstamp"; }
5654     virtual ~TaskTstamp()
5655         {}
5657     virtual bool execute()
5658         {
5659         return true;
5660         }
5662     virtual bool parse(Element *elem)
5663         {
5664         trace("tstamp parse");
5665         return true;
5666         }
5667 };
5671 /**
5672  *
5673  */
5674 Task *Task::createTask(Element *elem)
5676     String tagName = elem->getName();
5677     //trace("task:%s", tagName.c_str());
5678     Task *task = NULL;
5679     if (tagName == "ar")
5680         task = new TaskAr(parent);
5681     else if (tagName == "cc")
5682         task = new TaskCC(parent);
5683     else if (tagName == "copy")
5684         task = new TaskCopy(parent);
5685     else if (tagName == "delete")
5686         task = new TaskDelete(parent);
5687     else if (tagName == "jar")
5688         task = new TaskJar(parent);
5689     else if (tagName == "javac")
5690         task = new TaskJavac(parent);
5691     else if (tagName == "link")
5692         task = new TaskLink(parent);
5693     else if (tagName == "mkdir")
5694         task = new TaskMkDir(parent);
5695     else if (tagName == "msgfmt")
5696         task = new TaskMsgFmt(parent);
5697     else if (tagName == "ranlib")
5698         task = new TaskRanlib(parent);
5699     else if (tagName == "rc")
5700         task = new TaskRC(parent);
5701     else if (tagName == "strip")
5702         task = new TaskStrip(parent);
5703     else if (tagName == "tstamp")
5704         task = new TaskTstamp(parent);
5705     else
5706         {
5707         error("Unknown task '%s'", tagName.c_str());
5708         return NULL;
5709         }
5711     if (!task->parse(elem))
5712         {
5713         delete task;
5714         return NULL;
5715         }
5716     return task;
5721 //########################################################################
5722 //# T A R G E T
5723 //########################################################################
5725 /**
5726  *
5727  */
5728 class Target : public MakeBase
5731 public:
5733     /**
5734      *
5735      */
5736     Target(Make &par) : parent(par)
5737         { init(); }
5739     /**
5740      *
5741      */
5742     Target(const Target &other) : parent(other.parent)
5743         { init(); assign(other); }
5745     /**
5746      *
5747      */
5748     Target &operator=(const Target &other)
5749         { init(); assign(other); return *this; }
5751     /**
5752      *
5753      */
5754     virtual ~Target()
5755         { cleanup() ; }
5758     /**
5759      *
5760      */
5761     virtual Make &getParent()
5762         { return parent; }
5764     /**
5765      *
5766      */
5767     virtual String getName()
5768         { return name; }
5770     /**
5771      *
5772      */
5773     virtual void setName(const String &val)
5774         { name = val; }
5776     /**
5777      *
5778      */
5779     virtual String getDescription()
5780         { return description; }
5782     /**
5783      *
5784      */
5785     virtual void setDescription(const String &val)
5786         { description = val; }
5788     /**
5789      *
5790      */
5791     virtual void addDependency(const String &val)
5792         { deps.push_back(val); }
5794     /**
5795      *
5796      */
5797     virtual void parseDependencies(const String &val)
5798         { deps = tokenize(val, ", "); }
5800     /**
5801      *
5802      */
5803     virtual std::vector<String> &getDependencies()
5804         { return deps; }
5806     /**
5807      *
5808      */
5809     virtual String getIf()
5810         { return ifVar; }
5812     /**
5813      *
5814      */
5815     virtual void setIf(const String &val)
5816         { ifVar = val; }
5818     /**
5819      *
5820      */
5821     virtual String getUnless()
5822         { return unlessVar; }
5824     /**
5825      *
5826      */
5827     virtual void setUnless(const String &val)
5828         { unlessVar = val; }
5830     /**
5831      *
5832      */
5833     virtual void addTask(Task *val)
5834         { tasks.push_back(val); }
5836     /**
5837      *
5838      */
5839     virtual std::vector<Task *> &getTasks()
5840         { return tasks; }
5842 private:
5844     void init()
5845         {
5846         }
5848     void cleanup()
5849         {
5850         tasks.clear();
5851         }
5853     void assign(const Target &other)
5854         {
5855         //parent      = other.parent;
5856         name        = other.name;
5857         description = other.description;
5858         ifVar       = other.ifVar;
5859         unlessVar   = other.unlessVar;
5860         deps        = other.deps;
5861         tasks       = other.tasks;
5862         }
5864     Make &parent;
5866     String name;
5868     String description;
5870     String ifVar;
5872     String unlessVar;
5874     std::vector<String> deps;
5876     std::vector<Task *> tasks;
5878 };
5887 //########################################################################
5888 //# M A K E
5889 //########################################################################
5892 /**
5893  *
5894  */
5895 class Make : public MakeBase
5898 public:
5900     /**
5901      *
5902      */
5903     Make()
5904         { init(); }
5906     /**
5907      *
5908      */
5909     Make(const Make &other)
5910         { assign(other); }
5912     /**
5913      *
5914      */
5915     Make &operator=(const Make &other)
5916         { assign(other); return *this; }
5918     /**
5919      *
5920      */
5921     virtual ~Make()
5922         { cleanup(); }
5924     /**
5925      *
5926      */
5927     virtual std::map<String, Target> &getTargets()
5928         { return targets; }
5931     /**
5932      *
5933      */
5934     bool run();
5936     /**
5937      *
5938      */
5939     bool run(const String &target);
5943 private:
5945     /**
5946      *
5947      */
5948     void init();
5950     /**
5951      *
5952      */
5953     void cleanup();
5955     /**
5956      *
5957      */
5958     void assign(const Make &other);
5960     /**
5961      *
5962      */
5963     bool executeTask(Task &task);
5966     /**
5967      *
5968      */
5969     bool executeTarget(Target &target,
5970                  std::set<String> &targetsCompleted);
5973     /**
5974      *
5975      */
5976     bool execute();
5978     /**
5979      *
5980      */
5981     bool checkTargetDependencies(Target &prop,
5982                     std::set<String> &depList);
5984     /**
5985      *
5986      */
5987     bool parsePropertyFile(const String &fileName,
5988                                const String &prefix);
5990     /**
5991      *
5992      */
5993     bool parseProperty(Element *elem);
5995     /**
5996      *
5997      */
5998     bool parseTask(Task &task, Element *elem);
6000     /**
6001      *
6002      */
6003     bool parseFile();
6005     /**
6006      *
6007      */
6008     std::vector<String> glob(const String &pattern);
6011     //###############
6012     //# Fields
6013     //###############
6015     String projectName;
6017     String currentTarget;
6019     String defaultTarget;
6021     String specifiedTarget;
6023     String baseDir;
6025     String description;
6026     
6027     String envAlias;
6029     //std::vector<Property> properties;
6030     
6031     std::map<String, Target> targets;
6033     std::vector<Task *> allTasks;
6036 };
6039 //########################################################################
6040 //# C L A S S  M A I N T E N A N C E
6041 //########################################################################
6043 /**
6044  *
6045  */
6046 void Make::init()
6048     uri             = "build.xml";
6049     projectName     = "";
6050     currentTarget   = "";
6051     defaultTarget   = "";
6052     specifiedTarget = "";
6053     baseDir         = "";
6054     description     = "";
6055     envAlias        = "";
6056     properties.clear();
6057     for (unsigned int i = 0 ; i < allTasks.size() ; i++)
6058         delete allTasks[i];
6059     allTasks.clear();
6064 /**
6065  *
6066  */
6067 void Make::cleanup()
6069     for (unsigned int i = 0 ; i < allTasks.size() ; i++)
6070         delete allTasks[i];
6071     allTasks.clear();
6076 /**
6077  *
6078  */
6079 void Make::assign(const Make &other)
6081     uri              = other.uri;
6082     projectName      = other.projectName;
6083     currentTarget    = other.currentTarget;
6084     defaultTarget    = other.defaultTarget;
6085     specifiedTarget  = other.specifiedTarget;
6086     baseDir          = other.baseDir;
6087     description      = other.description;
6088     properties       = other.properties;
6093 //########################################################################
6094 //# U T I L I T Y    T A S K S
6095 //########################################################################
6097 /**
6098  *  Perform a file globbing
6099  */
6100 std::vector<String> Make::glob(const String &pattern)
6102     std::vector<String> res;
6103     return res;
6107 //########################################################################
6108 //# P U B L I C    A P I
6109 //########################################################################
6113 /**
6114  *
6115  */
6116 bool Make::executeTarget(Target &target,
6117              std::set<String> &targetsCompleted)
6120     String name = target.getName();
6122     //First get any dependencies for this target
6123     std::vector<String> deps = target.getDependencies();
6124     for (unsigned int i=0 ; i<deps.size() ; i++)
6125         {
6126         String dep = deps[i];
6127         //Did we do it already?  Skip
6128         if (targetsCompleted.find(dep)!=targetsCompleted.end())
6129             continue;
6130             
6131         std::map<String, Target> &tgts =
6132                target.getParent().getTargets();
6133         std::map<String, Target>::iterator iter =
6134                tgts.find(dep);
6135         if (iter == tgts.end())
6136             {
6137             error("Target '%s' dependency '%s' not found",
6138                       name.c_str(),  dep.c_str());
6139             return false;
6140             }
6141         Target depTarget = iter->second;
6142         if (!executeTarget(depTarget, targetsCompleted))
6143             {
6144             return false;
6145             }
6146         }
6148     status("##### Target : %s", name.c_str());
6150     //Now let's do the tasks
6151     std::vector<Task *> &tasks = target.getTasks();
6152     for (unsigned int i=0 ; i<tasks.size() ; i++)
6153         {
6154         Task *task = tasks[i];
6155         status("----- Task : %s", task->getName().c_str());
6156         if (!task->execute())
6157             {
6158             return false;
6159             }
6160         }
6161         
6162     targetsCompleted.insert(name);
6163     
6164     return true;
6169 /**
6170  *  Main execute() method.  Start here and work
6171  *  up the dependency tree 
6172  */
6173 bool Make::execute()
6175     status("######## EXECUTE");
6177     //Determine initial target
6178     if (specifiedTarget.size()>0)
6179         {
6180         currentTarget = specifiedTarget;
6181         }
6182     else if (defaultTarget.size()>0)
6183         {
6184         currentTarget = defaultTarget;
6185         }
6186     else
6187         {
6188         error("execute: no specified or default target requested");
6189         return false;
6190         }
6192     std::map<String, Target>::iterator iter =
6193                    targets.find(currentTarget);
6194     if (iter == targets.end())
6195         {
6196         error("Initial target '%s' not found",
6197                          currentTarget.c_str());
6198         return false;
6199         }
6200         
6201     //Now run
6202     Target target = iter->second;
6203     std::set<String> targetsCompleted;
6204     if (!executeTarget(target, targetsCompleted))
6205         {
6206         return false;
6207         }
6209     status("######## EXECUTE COMPLETE");
6210     return true;
6216 /**
6217  *
6218  */
6219 bool Make::checkTargetDependencies(Target &target, 
6220                             std::set<String> &depList)
6222     String tgtName = target.getName().c_str();
6223     depList.insert(tgtName);
6225     std::vector<String> deps = target.getDependencies();
6226     for (unsigned int i=0 ; i<deps.size() ; i++)
6227         {
6228         String dep = deps[i];
6229         std::set<String>::iterator diter = depList.find(dep);
6230         if (diter != depList.end())
6231             {
6232             error("Circular dependency '%s' found at '%s'",
6233                       dep.c_str(), tgtName.c_str());
6234             return false;
6235             }
6237         std::map<String, Target> &tgts =
6238                   target.getParent().getTargets();
6239         std::map<String, Target>::iterator titer = tgts.find(dep);
6240         if (titer == tgts.end())
6241             {
6242             error("Target '%s' dependency '%s' not found",
6243                       tgtName.c_str(), dep.c_str());
6244             return false;
6245             }
6246         if (!checkTargetDependencies(titer->second, depList))
6247             {
6248             return false;
6249             }
6250         }
6251     return true;
6258 static int getword(int pos, const String &inbuf, String &result)
6260     int p = pos;
6261     int len = (int)inbuf.size();
6262     String val;
6263     while (p < len)
6264         {
6265         char ch = inbuf[p];
6266         if (!isalnum(ch) && ch!='.' && ch!='_')
6267             break;
6268         val.push_back(ch);
6269         p++;
6270         }
6271     result = val;
6272     return p;
6278 /**
6279  *
6280  */
6281 bool Make::parsePropertyFile(const String &fileName,
6282                              const String &prefix)
6284     FILE *f = fopen(fileName.c_str(), "r");
6285     if (!f)
6286         {
6287         error("could not open property file %s", fileName.c_str());
6288         return false;
6289         }
6290     int linenr = 0;
6291     while (!feof(f))
6292         {
6293         char buf[256];
6294         if (!fgets(buf, 255, f))
6295             break;
6296         linenr++;
6297         String s = buf;
6298         s = trim(s);
6299         int len = s.size();
6300         if (len == 0)
6301             continue;
6302         if (s[0] == '#')
6303             continue;
6304         String key;
6305         String val;
6306         int p = 0;
6307         int p2 = getword(p, s, key);
6308         if (p2 <= p)
6309             {
6310             error("property file %s, line %d: expected keyword",
6311                                 fileName.c_str(), linenr);
6312                         return false;
6313                         }
6314                 if (prefix.size() > 0)
6315                     {
6316                     key.insert(0, prefix);
6317                     }
6319         //skip whitespace
6320                 for (p=p2 ; p<len ; p++)
6321                     if (!isspace(s[p]))
6322                         break;
6324         if (p>=len || s[p]!='=')
6325             {
6326             error("property file %s, line %d: expected '='",
6327                                 fileName.c_str(), linenr);
6328             return false;
6329             }
6330         p++;
6332         //skip whitespace
6333                 for ( ; p<len ; p++)
6334                     if (!isspace(s[p]))
6335                         break;
6337         /* This way expects a word after the =
6338                 p2 = getword(p, s, val);
6339         if (p2 <= p)
6340             {
6341             error("property file %s, line %d: expected value",
6342                                 fileName.c_str(), linenr);
6343                         return false;
6344                         }
6345                 */
6346         // This way gets the rest of the line after the =
6347                 if (p>=len)
6348             {
6349             error("property file %s, line %d: expected value",
6350                                 fileName.c_str(), linenr);
6351                         return false;
6352                         }
6353         val = s.substr(p);
6354                 if (key.size()==0 || val.size()==0)
6355                     continue;
6357         //trace("key:'%s' val:'%s'", key.c_str(), val.c_str());
6358             properties[key] = val;
6359         }
6360     fclose(f);
6361     return true;
6367 /**
6368  *
6369  */
6370 bool Make::parseProperty(Element *elem)
6372     std::vector<Attribute> &attrs = elem->getAttributes();
6373     for (unsigned int i=0 ; i<attrs.size() ; i++)
6374         {
6375         String attrName = attrs[i].getName();
6376         String attrVal  = attrs[i].getValue();
6378         if (attrName == "name")
6379             {
6380             String val;
6381                         if (!getAttribute(elem, "value", val))
6382                             return false;
6383             if (val.size() > 0)
6384                 {
6385                 properties[attrVal] = val;
6386                 continue;
6387                 }
6388             if (!getAttribute(elem, "location", val))
6389                 return false;
6390             if (val.size() > 0)
6391                 {
6392                 //TODO:  process a path relative to build.xml
6393                 properties[attrVal] = val;
6394                 continue;
6395                 }
6396             }
6397         else if (attrName == "file")
6398             {
6399             String prefix;
6400                         if (!getAttribute(elem, "prefix", prefix))
6401                             return false;
6402             if (prefix.size() > 0)
6403                 {
6404                 if (prefix[prefix.size()-1] != '.')
6405                     prefix.push_back('.');
6406                 }
6407             if (!parsePropertyFile(attrName, prefix))
6408                 return false;
6409             }
6410         else if (attrName == "environment")
6411             {
6412             if (envAlias.size() > 0)
6413                 {
6414                 error("environment property can only be set once");
6415                 return false;
6416                 }
6417             envAlias = attrVal;
6418             }
6419         }
6421     return true;
6427 /**
6428  *
6429  */
6430 bool Make::parseFile()
6432     status("######## PARSE");
6434     Parser parser;
6435     Element *root = parser.parseFile(uri.getNativePath());
6436     if (!root)
6437         {
6438         error("Could not open %s for reading",
6439                       uri.getNativePath().c_str());
6440         return false;
6441         }
6443     if (root->getChildren().size()==0 ||
6444         root->getChildren()[0]->getName()!="project")
6445         {
6446         error("Main xml element should be <project>");
6447         delete root;
6448         return false;
6449         }
6451     //########## Project attributes
6452     Element *project = root->getChildren()[0];
6453     String s = project->getAttribute("name");
6454     if (s.size() > 0)
6455         projectName = s;
6456     s = project->getAttribute("default");
6457     if (s.size() > 0)
6458         defaultTarget = s;
6459     s = project->getAttribute("basedir");
6460     if (s.size() > 0)
6461         baseDir = s;
6463     //######### PARSE MEMBERS
6464     std::vector<Element *> children = project->getChildren();
6465     for (unsigned int i=0 ; i<children.size() ; i++)
6466         {
6467         Element *elem = children[i];
6468         String tagName = elem->getName();
6470         //########## DESCRIPTION
6471         if (tagName == "description")
6472             {
6473             description = parser.trim(elem->getValue());
6474             }
6476         //######### PROPERTY
6477         else if (tagName == "property")
6478             {
6479             if (!parseProperty(elem))
6480                 return false;
6481             }
6483         //######### TARGET
6484         else if (tagName == "target")
6485             {
6486             String tname   = elem->getAttribute("name");
6487             String tdesc   = elem->getAttribute("description");
6488             String tdeps   = elem->getAttribute("depends");
6489             String tif     = elem->getAttribute("if");
6490             String tunless = elem->getAttribute("unless");
6491             Target target(*this);
6492             target.setName(tname);
6493             target.setDescription(tdesc);
6494             target.parseDependencies(tdeps);
6495             target.setIf(tif);
6496             target.setUnless(tunless);
6497             std::vector<Element *> telems = elem->getChildren();
6498             for (unsigned int i=0 ; i<telems.size() ; i++)
6499                 {
6500                 Element *telem = telems[i];
6501                 Task breeder(*this);
6502                 Task *task = breeder.createTask(telem);
6503                 if (!task)
6504                     return false;
6505                 allTasks.push_back(task);
6506                 target.addTask(task);
6507                 }
6509             //Check name
6510             if (tname.size() == 0)
6511                 {
6512                 error("no name for target");
6513                 return false;
6514                 }
6515             //Check for duplicate name
6516             if (targets.find(tname) != targets.end())
6517                 {
6518                 error("target '%s' already defined", tname.c_str());
6519                 return false;
6520                 }
6521             //more work than targets[tname]=target, but avoids default allocator
6522             targets.insert(std::make_pair<String, Target>(tname, target));
6523             }
6525         }
6527     std::map<String, Target>::iterator iter;
6528     for (iter = targets.begin() ; iter!= targets.end() ; iter++)
6529         {
6530         Target tgt = iter->second;
6531         std::set<String> depList;
6532         if (!checkTargetDependencies(tgt, depList))
6533             {
6534             return false;
6535             }
6536         }
6539     delete root;
6540     status("######## PARSE COMPLETE");
6541     return true;
6545 /**
6546  *
6547  */
6548 bool Make::run()
6550     if (!parseFile())
6551         return false;
6552     if (!execute())
6553         return false;
6554     return true;
6558 /**
6559  *
6560  */
6561 bool Make::run(const String &target)
6563     status("##################################");
6564     status("#   BuildTool");
6565     status("#   version 0.2");
6566     status("##################################");
6567     specifiedTarget = target;
6568     if (!run())
6569         return false;
6570     status("##################################");
6571     status("#   BuildTool Completed");
6572     status("##################################");
6573     return true;
6582 }// namespace buildtool
6583 //########################################################################
6584 //# M A I N
6585 //########################################################################
6587 /**
6588  *  Format an error message in printf() style
6589  */
6590 static void error(char *fmt, ...)
6592     va_list ap;
6593     va_start(ap, fmt);
6594     fprintf(stderr, "BuildTool error: ");
6595     vfprintf(stderr, fmt, ap);
6596     fprintf(stderr, "\n");
6597     va_end(ap);
6601 /**
6602  * Compare a buffer with a key, for the length of the key
6603  */
6604 static bool sequ(const buildtool::String &buf, char *key)
6606     for (int i=0 ; key[i] ; i++)
6607         {
6608         if (key[i] != buf[i])
6609             return false;
6610         }        
6611     return true;
6615 static bool parseOptions(int argc, char **argv)
6617     if (argc < 1)
6618         {
6619         error("Cannot parse arguments");
6620         return false;
6621         }
6623     buildtool::String buildFile;
6624     buildtool::String target;
6626     //char *progName = argv[0];
6627     for (int i=1 ; i<argc ; i++)
6628         {
6629         buildtool::String arg = argv[i];
6630         if (sequ(arg, "--"))
6631             {
6632             if (sequ(arg, "--file=") && arg.size()>7)
6633                 {
6634                 buildFile = arg.substr(7, arg.size()-7);
6635                 }
6636             else
6637                 {
6638                 error("Unknown option:%s", arg.c_str());
6639                 return false;
6640                 }
6641             }
6642         else if (sequ(arg, "-"))
6643             {
6644             for (unsigned int p=1 ; p<arg.size() ; p++)
6645                 {
6646                 int ch = arg[p];
6647                 if (0)//put options here
6648                     {
6649                     }
6650                 else
6651                     {
6652                     error("Unknown option '%c'", ch);
6653                     return false;
6654                     }
6655                 }
6656             }
6657         else
6658             {
6659             target = arg;
6660             }
6661         }
6663     //We have the options.  Now execute them
6664     buildtool::Make make;
6665     if (buildFile.size() > 0)
6666         {
6667         make.setURI(buildFile);
6668         }
6669     if (!make.run(target))
6670         return false;
6672     return true;
6675 static bool runMake()
6677     buildtool::Make make;
6678     if (!make.run())
6679         return false;
6680     return true;
6683 /*
6684 static bool pkgConfigTest()
6686     buildtool::PkgConfig pkgConfig;
6687     if (!pkgConfig.readFile("gtk+-2.0.pc"))
6688         return false;
6689     return true;
6694 static bool depTest()
6696     buildtool::DepTool deptool;
6697     deptool.setSourceDirectory("/dev/ink/inkscape/src");
6698     if (!deptool.generateDependencies("build.dep"))
6699         return false;
6700     std::vector<buildtool::DepRec> res =
6701                deptool.loadDepFile("build.dep");
6702         if (res.size() == 0)
6703         return false;
6704     return true;
6707 static bool popenTest()
6709     buildtool::Make make;
6710     buildtool::String out, err;
6711         bool ret = make.executeCommand("gcc xx.cpp", "", out, err);
6712     printf("Popen test:%d '%s' '%s'\n", ret, out.c_str(), err.c_str());
6713     return true;
6717 static bool propFileTest()
6719     buildtool::Make make;
6720     make.parsePropertyFile("test.prop", "test.");
6721     return true;
6723 */
6725 int main(int argc, char **argv)
6728     if (!parseOptions(argc, argv))
6729         return 1;
6730     /*
6731     if (!popenTest())
6732         return 1;
6734     if (!depTest())
6735         return 1;
6736     if (!propFileTest())
6737         return 1;
6738     if (runMake())
6739         return 1;
6740     */
6741     return 0;
6745 //########################################################################
6746 //# E N D 
6747 //########################################################################