1 /**
2 * Simple build automation tool.
3 *
4 * Authors:
5 * Bob Jamison
6 *
7 * Copyright (C) 2006 Bob Jamison
8 *
9 * This library is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU Lesser General Public
11 * License as published by the Free Software Foundation; either
12 * version 2.1 of the License, or (at your option) any later version.
13 *
14 * This library is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * Lesser General Public License for more details.
18 *
19 * You should have received a copy of the GNU Lesser General Public
20 * License along with this library; if not, write to the Free Software
21 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
22 */
24 /*
35 */
37 #include <stdio.h>
38 #include <unistd.h>
39 #include <stdarg.h>
40 #include <sys/stat.h>
41 #include <time.h>
42 #include <sys/time.h>
43 #include <dirent.h>
45 #include <string>
46 #include <map>
47 #include <set>
48 #include <vector>
50 #ifdef __WIN32__
51 #include <windows.h>
52 #endif
56 namespace buildtool
57 {
64 //########################################################################
65 //########################################################################
66 //## X M L
67 //########################################################################
68 //########################################################################
70 // Note: This mini-dom library comes from Pedro, another little project
71 // of mine.
73 typedef std::string String;
74 typedef unsigned int XMLCh;
77 class Namespace
78 {
79 public:
80 Namespace()
81 {}
83 Namespace(const String &prefixArg, const String &namespaceURIArg)
84 {
85 prefix = prefixArg;
86 namespaceURI = namespaceURIArg;
87 }
89 Namespace(const Namespace &other)
90 {
91 assign(other);
92 }
94 Namespace &operator=(const Namespace &other)
95 {
96 assign(other);
97 return *this;
98 }
100 virtual ~Namespace()
101 {}
103 virtual String getPrefix()
104 { return prefix; }
106 virtual String getNamespaceURI()
107 { return namespaceURI; }
109 protected:
111 void assign(const Namespace &other)
112 {
113 prefix = other.prefix;
114 namespaceURI = other.namespaceURI;
115 }
117 String prefix;
118 String namespaceURI;
120 };
122 class Attribute
123 {
124 public:
125 Attribute()
126 {}
128 Attribute(const String &nameArg, const String &valueArg)
129 {
130 name = nameArg;
131 value = valueArg;
132 }
134 Attribute(const Attribute &other)
135 {
136 assign(other);
137 }
139 Attribute &operator=(const Attribute &other)
140 {
141 assign(other);
142 return *this;
143 }
145 virtual ~Attribute()
146 {}
148 virtual String getName()
149 { return name; }
151 virtual String getValue()
152 { return value; }
154 protected:
156 void assign(const Attribute &other)
157 {
158 name = other.name;
159 value = other.value;
160 }
162 String name;
163 String value;
165 };
168 class Element
169 {
170 friend class Parser;
172 public:
173 Element()
174 {
175 parent = NULL;
176 }
178 Element(const String &nameArg)
179 {
180 parent = NULL;
181 name = nameArg;
182 }
184 Element(const String &nameArg, const String &valueArg)
185 {
186 parent = NULL;
187 name = nameArg;
188 value = valueArg;
189 }
191 Element(const Element &other)
192 {
193 assign(other);
194 }
196 Element &operator=(const Element &other)
197 {
198 assign(other);
199 return *this;
200 }
202 virtual Element *clone();
204 virtual ~Element()
205 {
206 for (unsigned int i=0 ; i<children.size() ; i++)
207 delete children[i];
208 }
210 virtual String getName()
211 { return name; }
213 virtual String getValue()
214 { return value; }
216 Element *getParent()
217 { return parent; }
219 std::vector<Element *> getChildren()
220 { return children; }
222 std::vector<Element *> findElements(const String &name);
224 String getAttribute(const String &name);
226 std::vector<Attribute> &getAttributes()
227 { return attributes; }
229 String getTagAttribute(const String &tagName, const String &attrName);
231 String getTagValue(const String &tagName);
233 void addChild(Element *child);
235 void addAttribute(const String &name, const String &value);
237 void addNamespace(const String &prefix, const String &namespaceURI);
240 /**
241 * Prettyprint an XML tree to an output stream. Elements are indented
242 * according to element hierarchy.
243 * @param f a stream to receive the output
244 * @param elem the element to output
245 */
246 void writeIndented(FILE *f);
248 /**
249 * Prettyprint an XML tree to standard output. This is the equivalent of
250 * writeIndented(stdout).
251 * @param elem the element to output
252 */
253 void print();
255 protected:
257 void assign(const Element &other)
258 {
259 parent = other.parent;
260 children = other.children;
261 attributes = other.attributes;
262 namespaces = other.namespaces;
263 name = other.name;
264 value = other.value;
265 }
267 void findElementsRecursive(std::vector<Element *>&res, const String &name);
269 void writeIndentedRecursive(FILE *f, int indent);
271 Element *parent;
273 std::vector<Element *>children;
275 std::vector<Attribute> attributes;
276 std::vector<Namespace> namespaces;
278 String name;
279 String value;
281 };
287 class Parser
288 {
289 public:
290 /**
291 * Constructor
292 */
293 Parser()
294 { init(); }
296 virtual ~Parser()
297 {}
299 /**
300 * Parse XML in a char buffer.
301 * @param buf a character buffer to parse
302 * @param pos position to start parsing
303 * @param len number of chars, from pos, to parse.
304 * @return a pointer to the root of the XML document;
305 */
306 Element *parse(const char *buf,int pos,int len);
308 /**
309 * Parse XML in a char buffer.
310 * @param buf a character buffer to parse
311 * @param pos position to start parsing
312 * @param len number of chars, from pos, to parse.
313 * @return a pointer to the root of the XML document;
314 */
315 Element *parse(const String &buf);
317 /**
318 * Parse a named XML file. The file is loaded like a data file;
319 * the original format is not preserved.
320 * @param fileName the name of the file to read
321 * @return a pointer to the root of the XML document;
322 */
323 Element *parseFile(const String &fileName);
325 /**
326 * Utility method to preprocess a string for XML
327 * output, escaping its entities.
328 * @param str the string to encode
329 */
330 static String encode(const String &str);
332 /**
333 * Removes whitespace from beginning and end of a string
334 */
335 String trim(const String &s);
337 private:
339 void init()
340 {
341 keepGoing = true;
342 currentNode = NULL;
343 parselen = 0;
344 parsebuf = NULL;
345 currentPosition = 0;
346 }
348 void getLineAndColumn(long pos, long *lineNr, long *colNr);
350 void error(char *fmt, ...);
352 int peek(long pos);
354 int match(long pos, const char *text);
356 int skipwhite(long p);
358 int getWord(int p0, String &buf);
360 int getQuoted(int p0, String &buf, int do_i_parse);
362 int parseVersion(int p0);
364 int parseDoctype(int p0);
366 int parseElement(int p0, Element *par,int depth);
368 Element *parse(XMLCh *buf,int pos,int len);
370 bool keepGoing;
371 Element *currentNode;
372 long parselen;
373 XMLCh *parsebuf;
374 String cdatabuf;
375 long currentPosition;
376 int colNr;
378 };
383 //########################################################################
384 //# E L E M E N T
385 //########################################################################
387 Element *Element::clone()
388 {
389 Element *elem = new Element(name, value);
390 elem->parent = parent;
391 elem->attributes = attributes;
392 elem->namespaces = namespaces;
394 std::vector<Element *>::iterator iter;
395 for (iter = children.begin(); iter != children.end() ; iter++)
396 {
397 elem->addChild((*iter)->clone());
398 }
399 return elem;
400 }
403 void Element::findElementsRecursive(std::vector<Element *>&res, const String &name)
404 {
405 if (getName() == name)
406 {
407 res.push_back(this);
408 }
409 for (unsigned int i=0; i<children.size() ; i++)
410 children[i]->findElementsRecursive(res, name);
411 }
413 std::vector<Element *> Element::findElements(const String &name)
414 {
415 std::vector<Element *> res;
416 findElementsRecursive(res, name);
417 return res;
418 }
420 String Element::getAttribute(const String &name)
421 {
422 for (unsigned int i=0 ; i<attributes.size() ; i++)
423 if (attributes[i].getName() ==name)
424 return attributes[i].getValue();
425 return "";
426 }
428 String Element::getTagAttribute(const String &tagName, const String &attrName)
429 {
430 std::vector<Element *>elems = findElements(tagName);
431 if (elems.size() <1)
432 return "";
433 String res = elems[0]->getAttribute(attrName);
434 return res;
435 }
437 String Element::getTagValue(const String &tagName)
438 {
439 std::vector<Element *>elems = findElements(tagName);
440 if (elems.size() <1)
441 return "";
442 String res = elems[0]->getValue();
443 return res;
444 }
446 void Element::addChild(Element *child)
447 {
448 if (!child)
449 return;
450 child->parent = this;
451 children.push_back(child);
452 }
455 void Element::addAttribute(const String &name, const String &value)
456 {
457 Attribute attr(name, value);
458 attributes.push_back(attr);
459 }
461 void Element::addNamespace(const String &prefix, const String &namespaceURI)
462 {
463 Namespace ns(prefix, namespaceURI);
464 namespaces.push_back(ns);
465 }
467 void Element::writeIndentedRecursive(FILE *f, int indent)
468 {
469 int i;
470 if (!f)
471 return;
472 //Opening tag, and attributes
473 for (i=0;i<indent;i++)
474 fputc(' ',f);
475 fprintf(f,"<%s",name.c_str());
476 for (unsigned int i=0 ; i<attributes.size() ; i++)
477 {
478 fprintf(f," %s=\"%s\"",
479 attributes[i].getName().c_str(),
480 attributes[i].getValue().c_str());
481 }
482 for (unsigned int i=0 ; i<namespaces.size() ; i++)
483 {
484 fprintf(f," xmlns:%s=\"%s\"",
485 namespaces[i].getPrefix().c_str(),
486 namespaces[i].getNamespaceURI().c_str());
487 }
488 fprintf(f,">\n");
490 //Between the tags
491 if (value.size() > 0)
492 {
493 for (int i=0;i<indent;i++)
494 fputc(' ', f);
495 fprintf(f," %s\n", value.c_str());
496 }
498 for (unsigned int i=0 ; i<children.size() ; i++)
499 children[i]->writeIndentedRecursive(f, indent+2);
501 //Closing tag
502 for (int i=0; i<indent; i++)
503 fputc(' ',f);
504 fprintf(f,"</%s>\n", name.c_str());
505 }
507 void Element::writeIndented(FILE *f)
508 {
509 writeIndentedRecursive(f, 0);
510 }
512 void Element::print()
513 {
514 writeIndented(stdout);
515 }
518 //########################################################################
519 //# P A R S E R
520 //########################################################################
524 typedef struct
525 {
526 char *escaped;
527 char value;
528 } EntityEntry;
530 static EntityEntry entities[] =
531 {
532 { "&" , '&' },
533 { "<" , '<' },
534 { ">" , '>' },
535 { "'", '\'' },
536 { """, '"' },
537 { NULL , '\0' }
538 };
542 /**
543 * Removes whitespace from beginning and end of a string
544 */
545 String Parser::trim(const String &s)
546 {
547 if (s.size() < 1)
548 return s;
550 //Find first non-ws char
551 unsigned int begin = 0;
552 for ( ; begin < s.size() ; begin++)
553 {
554 if (!isspace(s[begin]))
555 break;
556 }
558 //Find first non-ws char, going in reverse
559 unsigned int end = s.size() - 1;
560 for ( ; end > begin ; end--)
561 {
562 if (!isspace(s[end]))
563 break;
564 }
565 //trace("begin:%d end:%d", begin, end);
567 String res = s.substr(begin, end-begin+1);
568 return res;
569 }
571 void Parser::getLineAndColumn(long pos, long *lineNr, long *colNr)
572 {
573 long line = 1;
574 long col = 1;
575 for (long i=0 ; i<pos ; i++)
576 {
577 XMLCh ch = parsebuf[i];
578 if (ch == '\n' || ch == '\r')
579 {
580 col = 0;
581 line ++;
582 }
583 else
584 col++;
585 }
586 *lineNr = line;
587 *colNr = col;
589 }
592 void Parser::error(char *fmt, ...)
593 {
594 long lineNr;
595 long colNr;
596 getLineAndColumn(currentPosition, &lineNr, &colNr);
597 va_list args;
598 fprintf(stderr, "xml error at line %ld, column %ld:", lineNr, colNr);
599 va_start(args,fmt);
600 vfprintf(stderr,fmt,args);
601 va_end(args) ;
602 fprintf(stderr, "\n");
603 }
607 int Parser::peek(long pos)
608 {
609 if (pos >= parselen)
610 return -1;
611 currentPosition = pos;
612 int ch = parsebuf[pos];
613 //printf("ch:%c\n", ch);
614 return ch;
615 }
619 String Parser::encode(const String &str)
620 {
621 String ret;
622 for (unsigned int i=0 ; i<str.size() ; i++)
623 {
624 XMLCh ch = (XMLCh)str[i];
625 if (ch == '&')
626 ret.append("&");
627 else if (ch == '<')
628 ret.append("<");
629 else if (ch == '>')
630 ret.append(">");
631 else if (ch == '\'')
632 ret.append("'");
633 else if (ch == '"')
634 ret.append(""");
635 else
636 ret.push_back(ch);
638 }
639 return ret;
640 }
643 int Parser::match(long p0, const char *text)
644 {
645 int p = p0;
646 while (*text)
647 {
648 if (peek(p) != *text)
649 return p0;
650 p++; text++;
651 }
652 return p;
653 }
657 int Parser::skipwhite(long p)
658 {
660 while (p<parselen)
661 {
662 int p2 = match(p, "<!--");
663 if (p2 > p)
664 {
665 p = p2;
666 while (p<parselen)
667 {
668 p2 = match(p, "-->");
669 if (p2 > p)
670 {
671 p = p2;
672 break;
673 }
674 p++;
675 }
676 }
677 XMLCh b = peek(p);
678 if (!isspace(b))
679 break;
680 p++;
681 }
682 return p;
683 }
685 /* modify this to allow all chars for an element or attribute name*/
686 int Parser::getWord(int p0, String &buf)
687 {
688 int p = p0;
689 while (p<parselen)
690 {
691 XMLCh b = peek(p);
692 if (b<=' ' || b=='/' || b=='>' || b=='=')
693 break;
694 buf.push_back(b);
695 p++;
696 }
697 return p;
698 }
700 int Parser::getQuoted(int p0, String &buf, int do_i_parse)
701 {
703 int p = p0;
704 if (peek(p) != '"' && peek(p) != '\'')
705 return p0;
706 p++;
708 while ( p<parselen )
709 {
710 XMLCh b = peek(p);
711 if (b=='"' || b=='\'')
712 break;
713 if (b=='&' && do_i_parse)
714 {
715 bool found = false;
716 for (EntityEntry *ee = entities ; ee->value ; ee++)
717 {
718 int p2 = match(p, ee->escaped);
719 if (p2>p)
720 {
721 buf.push_back(ee->value);
722 p = p2;
723 found = true;
724 break;
725 }
726 }
727 if (!found)
728 {
729 error("unterminated entity");
730 return false;
731 }
732 }
733 else
734 {
735 buf.push_back(b);
736 p++;
737 }
738 }
739 return p;
740 }
742 int Parser::parseVersion(int p0)
743 {
744 //printf("### parseVersion: %d\n", p0);
746 int p = p0;
748 p = skipwhite(p0);
750 if (peek(p) != '<')
751 return p0;
753 p++;
754 if (p>=parselen || peek(p)!='?')
755 return p0;
757 p++;
759 String buf;
761 while (p<parselen)
762 {
763 XMLCh ch = peek(p);
764 if (ch=='?')
765 {
766 p++;
767 break;
768 }
769 buf.push_back(ch);
770 p++;
771 }
773 if (peek(p) != '>')
774 return p0;
775 p++;
777 //printf("Got version:%s\n",buf.c_str());
778 return p;
779 }
781 int Parser::parseDoctype(int p0)
782 {
783 //printf("### parseDoctype: %d\n", p0);
785 int p = p0;
786 p = skipwhite(p);
788 if (p>=parselen || peek(p)!='<')
789 return p0;
791 p++;
793 if (peek(p)!='!' || peek(p+1)=='-')
794 return p0;
795 p++;
797 String buf;
798 while (p<parselen)
799 {
800 XMLCh ch = peek(p);
801 if (ch=='>')
802 {
803 p++;
804 break;
805 }
806 buf.push_back(ch);
807 p++;
808 }
810 //printf("Got doctype:%s\n",buf.c_str());
811 return p;
812 }
814 int Parser::parseElement(int p0, Element *par,int depth)
815 {
817 int p = p0;
819 int p2 = p;
821 p = skipwhite(p);
823 //## Get open tag
824 XMLCh ch = peek(p);
825 if (ch!='<')
826 return p0;
828 p++;
830 String openTagName;
831 p = skipwhite(p);
832 p = getWord(p, openTagName);
833 //printf("####tag :%s\n", openTagName.c_str());
834 p = skipwhite(p);
836 //Add element to tree
837 Element *n = new Element(openTagName);
838 n->parent = par;
839 par->addChild(n);
841 // Get attributes
842 if (peek(p) != '>')
843 {
844 while (p<parselen)
845 {
846 p = skipwhite(p);
847 ch = peek(p);
848 //printf("ch:%c\n",ch);
849 if (ch=='>')
850 break;
851 else if (ch=='/' && p<parselen+1)
852 {
853 p++;
854 p = skipwhite(p);
855 ch = peek(p);
856 if (ch=='>')
857 {
858 p++;
859 //printf("quick close\n");
860 return p;
861 }
862 }
863 String attrName;
864 p2 = getWord(p, attrName);
865 if (p2==p)
866 break;
867 //printf("name:%s",buf);
868 p=p2;
869 p = skipwhite(p);
870 ch = peek(p);
871 //printf("ch:%c\n",ch);
872 if (ch!='=')
873 break;
874 p++;
875 p = skipwhite(p);
876 // ch = parsebuf[p];
877 // printf("ch:%c\n",ch);
878 String attrVal;
879 p2 = getQuoted(p, attrVal, true);
880 p=p2+1;
881 //printf("name:'%s' value:'%s'\n",attrName.c_str(),attrVal.c_str());
882 char *namestr = (char *)attrName.c_str();
883 if (strncmp(namestr, "xmlns:", 6)==0)
884 n->addNamespace(attrName, attrVal);
885 else
886 n->addAttribute(attrName, attrVal);
887 }
888 }
890 bool cdata = false;
892 p++;
893 // ### Get intervening data ### */
894 String data;
895 while (p<parselen)
896 {
897 //# COMMENT
898 p2 = match(p, "<!--");
899 if (!cdata && p2>p)
900 {
901 p = p2;
902 while (p<parselen)
903 {
904 p2 = match(p, "-->");
905 if (p2 > p)
906 {
907 p = p2;
908 break;
909 }
910 p++;
911 }
912 }
914 ch = peek(p);
915 //# END TAG
916 if (ch=='<' && !cdata && peek(p+1)=='/')
917 {
918 break;
919 }
920 //# CDATA
921 p2 = match(p, "<![CDATA[");
922 if (p2 > p)
923 {
924 cdata = true;
925 p = p2;
926 continue;
927 }
929 //# CHILD ELEMENT
930 if (ch == '<')
931 {
932 p2 = parseElement(p, n, depth+1);
933 if (p2 == p)
934 {
935 /*
936 printf("problem on element:%s. p2:%d p:%d\n",
937 openTagName.c_str(), p2, p);
938 */
939 return p0;
940 }
941 p = p2;
942 continue;
943 }
944 //# ENTITY
945 if (ch=='&' && !cdata)
946 {
947 bool found = false;
948 for (EntityEntry *ee = entities ; ee->value ; ee++)
949 {
950 int p2 = match(p, ee->escaped);
951 if (p2>p)
952 {
953 data.push_back(ee->value);
954 p = p2;
955 found = true;
956 break;
957 }
958 }
959 if (!found)
960 {
961 error("unterminated entity");
962 return -1;
963 }
964 continue;
965 }
967 //# NONE OF THE ABOVE
968 data.push_back(ch);
969 p++;
970 }/*while*/
973 n->value = data;
974 //printf("%d : data:%s\n",p,data.c_str());
976 //## Get close tag
977 p = skipwhite(p);
978 ch = peek(p);
979 if (ch != '<')
980 {
981 error("no < for end tag\n");
982 return p0;
983 }
984 p++;
985 ch = peek(p);
986 if (ch != '/')
987 {
988 error("no / on end tag");
989 return p0;
990 }
991 p++;
992 ch = peek(p);
993 p = skipwhite(p);
994 String closeTagName;
995 p = getWord(p, closeTagName);
996 if (openTagName != closeTagName)
997 {
998 error("Mismatched closing tag. Expected </%S>. Got '%S'.",
999 openTagName.c_str(), closeTagName.c_str());
1000 return p0;
1001 }
1002 p = skipwhite(p);
1003 if (peek(p) != '>')
1004 {
1005 error("no > on end tag for '%s'", closeTagName.c_str());
1006 return p0;
1007 }
1008 p++;
1009 // printf("close element:%s\n",closeTagName.c_str());
1010 p = skipwhite(p);
1011 return p;
1012 }
1017 Element *Parser::parse(XMLCh *buf,int pos,int len)
1018 {
1019 parselen = len;
1020 parsebuf = buf;
1021 Element *rootNode = new Element("root");
1022 pos = parseVersion(pos);
1023 pos = parseDoctype(pos);
1024 pos = parseElement(pos, rootNode, 0);
1025 return rootNode;
1026 }
1029 Element *Parser::parse(const char *buf, int pos, int len)
1030 {
1031 XMLCh *charbuf = new XMLCh[len + 1];
1032 long i = 0;
1033 for ( ; i < len ; i++)
1034 charbuf[i] = (XMLCh)buf[i];
1035 charbuf[i] = '\0';
1037 Element *n = parse(charbuf, pos, len);
1038 delete[] charbuf;
1039 return n;
1040 }
1042 Element *Parser::parse(const String &buf)
1043 {
1044 long len = (long)buf.size();
1045 XMLCh *charbuf = new XMLCh[len + 1];
1046 long i = 0;
1047 for ( ; i < len ; i++)
1048 charbuf[i] = (XMLCh)buf[i];
1049 charbuf[i] = '\0';
1051 Element *n = parse(charbuf, 0, len);
1052 delete[] charbuf;
1053 return n;
1054 }
1056 Element *Parser::parseFile(const String &fileName)
1057 {
1059 //##### LOAD INTO A CHAR BUF, THEN CONVERT TO XMLCh
1060 FILE *f = fopen(fileName.c_str(), "rb");
1061 if (!f)
1062 return NULL;
1064 struct stat statBuf;
1065 if (fstat(fileno(f),&statBuf)<0)
1066 {
1067 fclose(f);
1068 return NULL;
1069 }
1070 long filelen = statBuf.st_size;
1072 //printf("length:%d\n",filelen);
1073 XMLCh *charbuf = new XMLCh[filelen + 1];
1074 for (XMLCh *p=charbuf ; !feof(f) ; p++)
1075 {
1076 *p = (XMLCh)fgetc(f);
1077 }
1078 fclose(f);
1079 charbuf[filelen] = '\0';
1082 /*
1083 printf("nrbytes:%d\n",wc_count);
1084 printf("buf:%ls\n======\n",charbuf);
1085 */
1086 Element *n = parse(charbuf, 0, filelen);
1087 delete[] charbuf;
1088 return n;
1089 }
1093 //########################################################################
1094 //########################################################################
1095 //## E N D X M L
1096 //########################################################################
1097 //########################################################################
1100 //########################################################################
1101 //########################################################################
1102 //## U R I
1103 //########################################################################
1104 //########################################################################
1106 //This would normally be a call to a UNICODE function
1107 #define isLetter(x) isalpha(x)
1109 /**
1110 * A class that implements the W3C URI resource reference.
1111 */
1112 class URI
1113 {
1114 public:
1116 typedef enum
1117 {
1118 SCHEME_NONE =0,
1119 SCHEME_DATA,
1120 SCHEME_HTTP,
1121 SCHEME_HTTPS,
1122 SCHEME_FTP,
1123 SCHEME_FILE,
1124 SCHEME_LDAP,
1125 SCHEME_MAILTO,
1126 SCHEME_NEWS,
1127 SCHEME_TELNET
1128 } SchemeTypes;
1130 /**
1131 *
1132 */
1133 URI()
1134 {
1135 init();
1136 }
1138 /**
1139 *
1140 */
1141 URI(const String &str)
1142 {
1143 init();
1144 parse(str);
1145 }
1148 /**
1149 *
1150 */
1151 URI(const char *str)
1152 {
1153 init();
1154 String domStr = str;
1155 parse(domStr);
1156 }
1159 /**
1160 *
1161 */
1162 URI(const URI &other)
1163 {
1164 init();
1165 assign(other);
1166 }
1169 /**
1170 *
1171 */
1172 URI &operator=(const URI &other)
1173 {
1174 init();
1175 assign(other);
1176 return *this;
1177 }
1180 /**
1181 *
1182 */
1183 virtual ~URI()
1184 {}
1188 /**
1189 *
1190 */
1191 virtual bool parse(const String &str);
1193 /**
1194 *
1195 */
1196 virtual String toString() const;
1198 /**
1199 *
1200 */
1201 virtual int getScheme() const;
1203 /**
1204 *
1205 */
1206 virtual String getSchemeStr() const;
1208 /**
1209 *
1210 */
1211 virtual String getAuthority() const;
1213 /**
1214 * Same as getAuthority, but if the port has been specified
1215 * as host:port , the port will not be included
1216 */
1217 virtual String getHost() const;
1219 /**
1220 *
1221 */
1222 virtual int getPort() const;
1224 /**
1225 *
1226 */
1227 virtual String getPath() const;
1229 /**
1230 *
1231 */
1232 virtual String getNativePath() const;
1234 /**
1235 *
1236 */
1237 virtual bool isAbsolute() const;
1239 /**
1240 *
1241 */
1242 virtual bool isOpaque() const;
1244 /**
1245 *
1246 */
1247 virtual String getQuery() const;
1249 /**
1250 *
1251 */
1252 virtual String getFragment() const;
1254 /**
1255 *
1256 */
1257 virtual URI resolve(const URI &other) const;
1259 /**
1260 *
1261 */
1262 virtual void normalize();
1264 private:
1266 /**
1267 *
1268 */
1269 void init()
1270 {
1271 parsebuf = NULL;
1272 parselen = 0;
1273 scheme = SCHEME_NONE;
1274 schemeStr = "";
1275 port = 0;
1276 authority = "";
1277 path = "";
1278 absolute = false;
1279 opaque = false;
1280 query = "";
1281 fragment = "";
1282 }
1285 /**
1286 *
1287 */
1288 void assign(const URI &other)
1289 {
1290 scheme = other.scheme;
1291 schemeStr = other.schemeStr;
1292 authority = other.authority;
1293 port = other.port;
1294 path = other.path;
1295 absolute = other.absolute;
1296 opaque = other.opaque;
1297 query = other.query;
1298 fragment = other.fragment;
1299 }
1301 int scheme;
1303 String schemeStr;
1305 String authority;
1307 bool portSpecified;
1309 int port;
1311 String path;
1313 bool absolute;
1315 bool opaque;
1317 String query;
1319 String fragment;
1321 void error(const char *fmt, ...);
1323 void trace(const char *fmt, ...);
1326 int peek(int p);
1328 int match(int p, char *key);
1330 int parseScheme(int p);
1332 int parseHierarchicalPart(int p0);
1334 int parseQuery(int p0);
1336 int parseFragment(int p0);
1338 int parse(int p);
1340 char *parsebuf;
1342 int parselen;
1344 };
1348 typedef struct
1349 {
1350 int ival;
1351 char *sval;
1352 int port;
1353 } LookupEntry;
1355 LookupEntry schemes[] =
1356 {
1357 { URI::SCHEME_DATA, "data:", 0 },
1358 { URI::SCHEME_HTTP, "http:", 80 },
1359 { URI::SCHEME_HTTPS, "https:", 443 },
1360 { URI::SCHEME_FTP, "ftp", 12 },
1361 { URI::SCHEME_FILE, "file:", 0 },
1362 { URI::SCHEME_LDAP, "ldap:", 123 },
1363 { URI::SCHEME_MAILTO, "mailto:", 25 },
1364 { URI::SCHEME_NEWS, "news:", 117 },
1365 { URI::SCHEME_TELNET, "telnet:", 23 },
1366 { 0, NULL, 0 }
1367 };
1370 String URI::toString() const
1371 {
1372 String str = schemeStr;
1373 if (authority.size() > 0)
1374 {
1375 str.append("//");
1376 str.append(authority);
1377 }
1378 str.append(path);
1379 if (query.size() > 0)
1380 {
1381 str.append("?");
1382 str.append(query);
1383 }
1384 if (fragment.size() > 0)
1385 {
1386 str.append("#");
1387 str.append(fragment);
1388 }
1389 return str;
1390 }
1393 int URI::getScheme() const
1394 {
1395 return scheme;
1396 }
1398 String URI::getSchemeStr() const
1399 {
1400 return schemeStr;
1401 }
1404 String URI::getAuthority() const
1405 {
1406 String ret = authority;
1407 if (portSpecified && port>=0)
1408 {
1409 char buf[7];
1410 snprintf(buf, 6, ":%6d", port);
1411 ret.append(buf);
1412 }
1413 return ret;
1414 }
1416 String URI::getHost() const
1417 {
1418 return authority;
1419 }
1421 int URI::getPort() const
1422 {
1423 return port;
1424 }
1427 String URI::getPath() const
1428 {
1429 return path;
1430 }
1432 String URI::getNativePath() const
1433 {
1434 String npath;
1435 #ifdef __WIN32__
1436 unsigned int firstChar = 0;
1437 if (path.size() >= 3)
1438 {
1439 if (path[0] == '/' &&
1440 isLetter(path[1]) &&
1441 path[2] == ':')
1442 firstChar++;
1443 }
1444 for (unsigned int i=firstChar ; i<path.size() ; i++)
1445 {
1446 XMLCh ch = (XMLCh) path[i];
1447 if (ch == '/')
1448 npath.push_back((XMLCh)'\\');
1449 else
1450 npath.push_back(ch);
1451 }
1452 #else
1453 npath = path;
1454 #endif
1455 return npath;
1456 }
1459 bool URI::isAbsolute() const
1460 {
1461 return absolute;
1462 }
1464 bool URI::isOpaque() const
1465 {
1466 return opaque;
1467 }
1470 String URI::getQuery() const
1471 {
1472 return query;
1473 }
1476 String URI::getFragment() const
1477 {
1478 return fragment;
1479 }
1482 URI URI::resolve(const URI &other) const
1483 {
1484 //### According to w3c, this is handled in 3 cases
1486 //## 1
1487 if (opaque || other.isAbsolute())
1488 return other;
1490 //## 2
1491 if (other.fragment.size() > 0 &&
1492 other.path.size() == 0 &&
1493 other.scheme == SCHEME_NONE &&
1494 other.authority.size() == 0 &&
1495 other.query.size() == 0 )
1496 {
1497 URI fragUri = *this;
1498 fragUri.fragment = other.fragment;
1499 return fragUri;
1500 }
1502 //## 3 http://www.ietf.org/rfc/rfc2396.txt, section 5.2
1503 URI newUri;
1504 //# 3.1
1505 newUri.scheme = scheme;
1506 newUri.schemeStr = schemeStr;
1507 newUri.query = other.query;
1508 newUri.fragment = other.fragment;
1509 if (other.authority.size() > 0)
1510 {
1511 //# 3.2
1512 if (absolute || other.absolute)
1513 newUri.absolute = true;
1514 newUri.authority = other.authority;
1515 newUri.port = other.port;//part of authority
1516 newUri.path = other.path;
1517 }
1518 else
1519 {
1520 //# 3.3
1521 if (other.absolute)
1522 {
1523 newUri.absolute = true;
1524 newUri.path = other.path;
1525 }
1526 else
1527 {
1528 unsigned int pos = path.find_last_of('/');
1529 if (pos != path.npos)
1530 {
1531 String tpath = path.substr(0, pos+1);
1532 tpath.append(other.path);
1533 newUri.path = tpath;
1534 }
1535 else
1536 newUri.path = other.path;
1537 }
1538 }
1540 newUri.normalize();
1541 return newUri;
1542 }
1545 /**
1546 * This follows the Java URI algorithm:
1547 * 1. All "." segments are removed.
1548 * 2. If a ".." segment is preceded by a non-".." segment
1549 * then both of these segments are removed. This step
1550 * is repeated until it is no longer applicable.
1551 * 3. If the path is relative, and if its first segment
1552 * contains a colon character (':'), then a "." segment
1553 * is prepended. This prevents a relative URI with a path
1554 * such as "a:b/c/d" from later being re-parsed as an
1555 * opaque URI with a scheme of "a" and a scheme-specific
1556 * part of "b/c/d". (Deviation from RFC 2396)
1557 */
1558 void URI::normalize()
1559 {
1560 std::vector<String> segments;
1562 //## Collect segments
1563 if (path.size()<2)
1564 return;
1565 bool abs = false;
1566 unsigned int pos=0;
1567 if (path[0]=='/')
1568 {
1569 abs = true;
1570 pos++;
1571 }
1572 while (pos < path.size())
1573 {
1574 unsigned int pos2 = path.find('/', pos);
1575 if (pos2==path.npos)
1576 {
1577 String seg = path.substr(pos);
1578 //printf("last segment:%s\n", seg.c_str());
1579 segments.push_back(seg);
1580 break;
1581 }
1582 if (pos2>pos)
1583 {
1584 String seg = path.substr(pos, pos2-pos);
1585 //printf("segment:%s\n", seg.c_str());
1586 segments.push_back(seg);
1587 }
1588 pos = pos2;
1589 pos++;
1590 }
1592 //## Clean up (normalize) segments
1593 bool edited = false;
1594 std::vector<String>::iterator iter;
1595 for (iter=segments.begin() ; iter!=segments.end() ; )
1596 {
1597 String s = *iter;
1598 if (s == ".")
1599 {
1600 iter = segments.erase(iter);
1601 edited = true;
1602 }
1603 else if (s == ".." &&
1604 iter != segments.begin() &&
1605 *(iter-1) != "..")
1606 {
1607 iter--; //back up, then erase two entries
1608 iter = segments.erase(iter);
1609 iter = segments.erase(iter);
1610 edited = true;
1611 }
1612 else
1613 iter++;
1614 }
1616 //## Rebuild path, if necessary
1617 if (edited)
1618 {
1619 path.clear();
1620 if (abs)
1621 {
1622 path.append("/");
1623 }
1624 std::vector<String>::iterator iter;
1625 for (iter=segments.begin() ; iter!=segments.end() ; iter++)
1626 {
1627 if (iter != segments.begin())
1628 path.append("/");
1629 path.append(*iter);
1630 }
1631 }
1633 }
1637 //#########################################################################
1638 //# M E S S A G E S
1639 //#########################################################################
1641 void URI::error(const char *fmt, ...)
1642 {
1643 va_list args;
1644 fprintf(stderr, "URI error: ");
1645 va_start(args, fmt);
1646 vfprintf(stderr, fmt, args);
1647 va_end(args);
1648 fprintf(stderr, "\n");
1649 }
1651 void URI::trace(const char *fmt, ...)
1652 {
1653 va_list args;
1654 fprintf(stdout, "URI: ");
1655 va_start(args, fmt);
1656 vfprintf(stdout, fmt, args);
1657 va_end(args);
1658 fprintf(stdout, "\n");
1659 }
1663 //#########################################################################
1664 //# P A R S I N G
1665 //#########################################################################
1669 int URI::peek(int p)
1670 {
1671 if (p<0 || p>=parselen)
1672 return -1;
1673 return parsebuf[p];
1674 }
1678 int URI::match(int p0, char *key)
1679 {
1680 int p = p0;
1681 while (p < parselen)
1682 {
1683 if (*key == '\0')
1684 return p;
1685 else if (*key != parsebuf[p])
1686 break;
1687 p++; key++;
1688 }
1689 return p0;
1690 }
1692 //#########################################################################
1693 //# Parsing is performed according to:
1694 //# http://www.gbiv.com/protocols/uri/rfc/rfc3986.html#components
1695 //#########################################################################
1697 int URI::parseScheme(int p0)
1698 {
1699 int p = p0;
1700 for (LookupEntry *entry = schemes; entry->sval ; entry++)
1701 {
1702 int p2 = match(p, entry->sval);
1703 if (p2 > p)
1704 {
1705 schemeStr = entry->sval;
1706 scheme = entry->ival;
1707 port = entry->port;
1708 p = p2;
1709 return p;
1710 }
1711 }
1713 return p;
1714 }
1717 int URI::parseHierarchicalPart(int p0)
1718 {
1719 int p = p0;
1720 int ch;
1722 //# Authority field (host and port, for example)
1723 int p2 = match(p, "//");
1724 if (p2 > p)
1725 {
1726 p = p2;
1727 portSpecified = false;
1728 String portStr;
1729 while (p < parselen)
1730 {
1731 ch = peek(p);
1732 if (ch == '/')
1733 break;
1734 else if (ch == ':')
1735 portSpecified = true;
1736 else if (portSpecified)
1737 portStr.push_back((XMLCh)ch);
1738 else
1739 authority.push_back((XMLCh)ch);
1740 p++;
1741 }
1742 if (portStr.size() > 0)
1743 {
1744 char *pstr = (char *)portStr.c_str();
1745 char *endStr;
1746 long val = strtol(pstr, &endStr, 10);
1747 if (endStr > pstr) //successful parse?
1748 port = val;
1749 }
1750 }
1752 //# Are we absolute?
1753 ch = peek(p);
1754 if (isLetter(ch) && peek(p+1)==':')
1755 {
1756 absolute = true;
1757 path.push_back((XMLCh)'/');
1758 }
1759 else if (ch == '/')
1760 {
1761 absolute = true;
1762 if (p>p0) //in other words, if '/' is not the first char
1763 opaque = true;
1764 path.push_back((XMLCh)ch);
1765 p++;
1766 }
1768 while (p < parselen)
1769 {
1770 ch = peek(p);
1771 if (ch == '?' || ch == '#')
1772 break;
1773 path.push_back((XMLCh)ch);
1774 p++;
1775 }
1777 return p;
1778 }
1780 int URI::parseQuery(int p0)
1781 {
1782 int p = p0;
1783 int ch = peek(p);
1784 if (ch != '?')
1785 return p0;
1787 p++;
1788 while (p < parselen)
1789 {
1790 ch = peek(p);
1791 if (ch == '#')
1792 break;
1793 query.push_back((XMLCh)ch);
1794 p++;
1795 }
1798 return p;
1799 }
1801 int URI::parseFragment(int p0)
1802 {
1804 int p = p0;
1805 int ch = peek(p);
1806 if (ch != '#')
1807 return p0;
1809 p++;
1810 while (p < parselen)
1811 {
1812 ch = peek(p);
1813 if (ch == '?')
1814 break;
1815 fragment.push_back((XMLCh)ch);
1816 p++;
1817 }
1820 return p;
1821 }
1824 int URI::parse(int p0)
1825 {
1827 int p = p0;
1829 int p2 = parseScheme(p);
1830 if (p2 < 0)
1831 {
1832 error("Scheme");
1833 return -1;
1834 }
1835 p = p2;
1838 p2 = parseHierarchicalPart(p);
1839 if (p2 < 0)
1840 {
1841 error("Hierarchical part");
1842 return -1;
1843 }
1844 p = p2;
1846 p2 = parseQuery(p);
1847 if (p2 < 0)
1848 {
1849 error("Query");
1850 return -1;
1851 }
1852 p = p2;
1855 p2 = parseFragment(p);
1856 if (p2 < 0)
1857 {
1858 error("Fragment");
1859 return -1;
1860 }
1861 p = p2;
1863 return p;
1865 }
1869 bool URI::parse(const String &str)
1870 {
1872 parselen = str.size();
1874 String tmp;
1875 for (unsigned int i=0 ; i<str.size() ; i++)
1876 {
1877 XMLCh ch = (XMLCh) str[i];
1878 if (ch == '\\')
1879 tmp.push_back((XMLCh)'/');
1880 else
1881 tmp.push_back(ch);
1882 }
1883 parsebuf = (char *) tmp.c_str();
1886 int p = parse(0);
1887 normalize();
1889 if (p < 0)
1890 {
1891 error("Syntax error");
1892 return false;
1893 }
1895 //printf("uri:%s\n", toString().c_str());
1896 //printf("path:%s\n", path.c_str());
1898 return true;
1900 }
1909 //########################################################################
1910 //########################################################################
1911 //## M A K E
1912 //########################################################################
1913 //########################################################################
1917 //########################################################################
1918 //# M A K E B A S E
1919 //########################################################################
1920 /**
1921 * Base class for all classes in this file
1922 */
1923 class MakeBase
1924 {
1925 public:
1926 MakeBase()
1927 {}
1928 virtual ~MakeBase()
1929 {}
1931 /**
1932 * Return the URI of the file associated with this object
1933 */
1934 URI getURI()
1935 { return uri; }
1937 /**
1938 * Set the uri to the given string
1939 */
1940 void setURI(const String &uristr)
1941 { uri.parse(uristr); }
1943 /**
1944 * Resolve another path relative to this one
1945 */
1946 String resolve(const String &otherPath);
1948 /**
1949 * Get an element attribute, performing substitutions if necessary
1950 */
1951 bool getAttribute(Element *elem, const String &name, String &result);
1953 /**
1954 * Get an element value, performing substitutions if necessary
1955 */
1956 bool getValue(Element *elem, String &result);
1958 protected:
1960 /**
1961 * The path to the file associated with this object
1962 */
1963 URI uri;
1966 /**
1967 * Print a printf()-like formatted error message
1968 */
1969 void error(char *fmt, ...);
1971 /**
1972 * Print a printf()-like formatted trace message
1973 */
1974 void status(char *fmt, ...);
1976 /**
1977 * Print a printf()-like formatted trace message
1978 */
1979 void trace(char *fmt, ...);
1981 /**
1982 *
1983 */
1984 String getSuffix(const String &fname);
1986 /**
1987 * Break up a string into substrings delimited the characters
1988 * in delimiters. Null-length substrings are ignored
1989 */
1990 std::vector<String> tokenize(const String &val,
1991 const String &delimiters);
1993 /**
1994 * remove leading and trailing whitespace from string
1995 */
1996 String trim(const String &s);
1998 /**
1999 * Return the native format of the canonical
2000 * path which we store
2001 */
2002 String getNativePath(const String &path);
2004 /**
2005 * Execute a shell command. Outbuf is a ref to a string
2006 * to catch the result.
2007 */
2008 bool executeCommand(const String &call,
2009 const String &inbuf,
2010 String &outbuf,
2011 String &errbuf);
2012 /**
2013 * List all directories in a given base and starting directory
2014 * It is usually called like:
2015 * bool ret = listDirectories("src", "", result);
2016 */
2017 bool listDirectories(const String &baseName,
2018 const String &dirname,
2019 std::vector<String> &res);
2021 /**
2022 * Find all files in the named directory whose short names (no path) match
2023 * the given regex pattern
2024 */
2025 bool listFiles(const String &baseName,
2026 const String &dirname,
2027 std::vector<String> &excludes,
2028 std::vector<String> &res);
2030 /**
2031 * Parse a <patternset>
2032 */
2033 bool getPatternSet(Element *elem,
2034 MakeBase &propRef,
2035 std::vector<String> &includes,
2036 std::vector<String> &excludes);
2038 /**
2039 * Parse a <fileset> entry, and determine which files
2040 * should be included
2041 */
2042 bool getFileSet(Element *elem,
2043 MakeBase &propRef,
2044 String &dir,
2045 std::vector<String> &result);
2047 /**
2048 * Return this object's property list
2049 */
2050 virtual std::map<String, String> &getProperties()
2051 { return properties; }
2053 /**
2054 * Return a named property if found, else a null string
2055 */
2056 virtual String getProperty(const String &name)
2057 {
2058 String val;
2059 std::map<String, String>::iterator iter;
2060 iter = properties.find(name);
2061 if (iter != properties.end())
2062 val = iter->second;
2063 return val;
2064 }
2067 std::map<String, String> properties;
2069 /**
2070 * Turn 'true' and 'false' into boolean values
2071 */
2072 bool getBool(const String &str, bool &val);
2074 /**
2075 * Create a directory, making intermediate dirs
2076 * if necessary
2077 */
2078 bool createDirectory(const String &dirname);
2080 /**
2081 * Delete a directory and its children if desired
2082 */
2083 bool removeDirectory(const String &dirName);
2085 /**
2086 * Copy a file from one name to another. Perform only if needed
2087 */
2088 bool copyFile(const String &srcFile, const String &destFile);
2090 /**
2091 * Tests is the modification date of fileA is newer than fileB
2092 */
2093 bool isNewerThan(const String &fileA, const String &fileB);
2095 private:
2097 /**
2098 * replace variable refs like ${a} with their values
2099 */
2100 bool getSubstitutions(const String &s, String &result);
2104 };
2109 /**
2110 * Print a printf()-like formatted error message
2111 */
2112 void MakeBase::error(char *fmt, ...)
2113 {
2114 va_list args;
2115 va_start(args,fmt);
2116 fprintf(stderr, "Make error: ");
2117 vfprintf(stderr, fmt, args);
2118 fprintf(stderr, "\n");
2119 va_end(args) ;
2120 }
2124 /**
2125 * Print a printf()-like formatted trace message
2126 */
2127 void MakeBase::status(char *fmt, ...)
2128 {
2129 va_list args;
2130 va_start(args,fmt);
2131 //fprintf(stdout, " ");
2132 vfprintf(stdout, fmt, args);
2133 fprintf(stdout, "\n");
2134 va_end(args) ;
2135 }
2139 /**
2140 * Resolve another path relative to this one
2141 */
2142 String MakeBase::resolve(const String &otherPath)
2143 {
2144 URI otherURI(otherPath);
2145 URI fullURI = uri.resolve(otherURI);
2146 String ret = fullURI.toString();
2147 return ret;
2148 }
2151 /**
2152 * Print a printf()-like formatted trace message
2153 */
2154 void MakeBase::trace(char *fmt, ...)
2155 {
2156 va_list args;
2157 va_start(args,fmt);
2158 fprintf(stdout, "Make: ");
2159 vfprintf(stdout, fmt, args);
2160 fprintf(stdout, "\n");
2161 va_end(args) ;
2162 }
2165 /**
2166 * Return the suffix, if any, of a file name
2167 */
2168 String MakeBase::getSuffix(const String &fname)
2169 {
2170 if (fname.size() < 2)
2171 return "";
2172 unsigned int pos = fname.find_last_of('.');
2173 if (pos == fname.npos)
2174 return "";
2175 pos++;
2176 String res = fname.substr(pos, fname.size()-pos);
2177 //trace("suffix:%s", res.c_str());
2178 return res;
2179 }
2183 /**
2184 * Break up a string into substrings delimited the characters
2185 * in delimiters. Null-length substrings are ignored
2186 */
2187 std::vector<String> MakeBase::tokenize(const String &str,
2188 const String &delimiters)
2189 {
2191 std::vector<String> res;
2192 char *del = (char *)delimiters.c_str();
2193 String dmp;
2194 for (unsigned int i=0 ; i<str.size() ; i++)
2195 {
2196 char ch = str[i];
2197 char *p = (char *)0;
2198 for (p=del ; *p ; p++)
2199 if (*p == ch)
2200 break;
2201 if (*p)
2202 {
2203 if (dmp.size() > 0)
2204 {
2205 res.push_back(dmp);
2206 dmp.clear();
2207 }
2208 }
2209 else
2210 {
2211 dmp.push_back(ch);
2212 }
2213 }
2214 //Add tail
2215 if (dmp.size() > 0)
2216 {
2217 res.push_back(dmp);
2218 dmp.clear();
2219 }
2221 return res;
2222 }
2226 /**
2227 * Removes whitespace from beginning and end of a string
2228 */
2229 String MakeBase::trim(const String &s)
2230 {
2231 if (s.size() < 1)
2232 return s;
2234 //Find first non-ws char
2235 unsigned int begin = 0;
2236 for ( ; begin < s.size() ; begin++)
2237 {
2238 if (!isspace(s[begin]))
2239 break;
2240 }
2242 //Find first non-ws char, going in reverse
2243 unsigned int end = s.size() - 1;
2244 for ( ; end > begin ; end--)
2245 {
2246 if (!isspace(s[end]))
2247 break;
2248 }
2249 //trace("begin:%d end:%d", begin, end);
2251 String res = s.substr(begin, end-begin+1);
2252 return res;
2253 }
2255 /**
2256 * Return the native format of the canonical
2257 * path which we store
2258 */
2259 String MakeBase::getNativePath(const String &path)
2260 {
2261 #ifdef __WIN32__
2262 String npath;
2263 unsigned int firstChar = 0;
2264 if (path.size() >= 3)
2265 {
2266 if (path[0] == '/' &&
2267 isalpha(path[1]) &&
2268 path[2] == ':')
2269 firstChar++;
2270 }
2271 for (unsigned int i=firstChar ; i<path.size() ; i++)
2272 {
2273 char ch = path[i];
2274 if (ch == '/')
2275 npath.push_back('\\');
2276 else
2277 npath.push_back(ch);
2278 }
2279 return npath;
2280 #else
2281 return path;
2282 #endif
2283 }
2286 #ifdef __WIN32__
2287 #include <tchar.h>
2289 static String win32LastError()
2290 {
2292 DWORD dw = GetLastError();
2294 LPVOID str;
2295 FormatMessage(
2296 FORMAT_MESSAGE_ALLOCATE_BUFFER |
2297 FORMAT_MESSAGE_FROM_SYSTEM,
2298 NULL,
2299 dw,
2300 0,
2301 (LPTSTR) &str,
2302 0, NULL );
2303 LPTSTR p = _tcschr((const char *)str, _T('\r'));
2304 if(p != NULL)
2305 { // lose CRLF
2306 *p = _T('\0');
2307 }
2308 String ret = (char *)str;
2309 LocalFree(str);
2311 return ret;
2312 }
2313 #endif
2317 /**
2318 * Execute a system call, using pipes to send data to the
2319 * program's stdin, and reading stdout and stderr.
2320 */
2321 bool MakeBase::executeCommand(const String &command,
2322 const String &inbuf,
2323 String &outbuf,
2324 String &errbuf)
2325 {
2327 status("============ cmd ============\n%s\n=============================",
2328 command.c_str());
2330 #ifdef __WIN32__
2332 /*
2333 I really hate having win32 code in this program, but the
2334 read buffer in command.com and cmd.exe are just too small
2335 for the large commands we need for compiling and linking.
2336 */
2338 bool ret = true;
2340 //# Allocate a separate buffer for safety
2341 char *paramBuf = new char[command.size() + 1];
2342 if (!paramBuf)
2343 {
2344 error("executeCommand cannot allocate command buffer");
2345 return false;
2346 }
2347 strcpy(paramBuf, (char *)command.c_str());
2349 //# Create pipes
2350 SECURITY_ATTRIBUTES saAttr;
2351 saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
2352 saAttr.bInheritHandle = TRUE;
2353 saAttr.lpSecurityDescriptor = NULL;
2354 HANDLE stdinRead, stdinWrite;
2355 HANDLE stdoutRead, stdoutWrite;
2356 HANDLE stderrRead, stderrWrite;
2357 if (!CreatePipe(&stdinRead, &stdinWrite, &saAttr, 0))
2358 {
2359 error("executeProgram: could not create pipe");
2360 delete[] paramBuf;
2361 return false;
2362 }
2363 SetHandleInformation(stdinWrite, HANDLE_FLAG_INHERIT, 0);
2364 if (!CreatePipe(&stdoutRead, &stdoutWrite, &saAttr, 0))
2365 {
2366 error("executeProgram: could not create pipe");
2367 delete[] paramBuf;
2368 return false;
2369 }
2370 SetHandleInformation(stdoutRead, HANDLE_FLAG_INHERIT, 0);
2371 if (!CreatePipe(&stderrRead, &stderrWrite, &saAttr, 0))
2372 {
2373 error("executeProgram: could not create pipe");
2374 delete[] paramBuf;
2375 return false;
2376 }
2377 SetHandleInformation(stderrRead, HANDLE_FLAG_INHERIT, 0);
2379 // Create the process
2380 STARTUPINFO siStartupInfo;
2381 PROCESS_INFORMATION piProcessInfo;
2382 memset(&siStartupInfo, 0, sizeof(siStartupInfo));
2383 memset(&piProcessInfo, 0, sizeof(piProcessInfo));
2384 siStartupInfo.cb = sizeof(siStartupInfo);
2385 siStartupInfo.hStdError = stderrWrite;
2386 siStartupInfo.hStdOutput = stdoutWrite;
2387 siStartupInfo.hStdInput = stdinRead;
2388 siStartupInfo.dwFlags |= STARTF_USESTDHANDLES;
2390 if (!CreateProcess(NULL, paramBuf, NULL, NULL, true,
2391 0, NULL, NULL, &siStartupInfo,
2392 &piProcessInfo))
2393 {
2394 error("executeCommand : could not create process : %s",
2395 win32LastError().c_str());
2396 ret = false;
2397 }
2399 DWORD bytesWritten;
2400 if (inbuf.size()>0 &&
2401 !WriteFile(stdinWrite, inbuf.c_str(), inbuf.size(),
2402 &bytesWritten, NULL))
2403 {
2404 error("executeCommand: could not write to pipe");
2405 return false;
2406 }
2407 if (!CloseHandle(stdinWrite))
2408 {
2409 error("executeCommand: could not close write pipe");
2410 return false;
2411 }
2412 if (!CloseHandle(stdoutWrite))
2413 {
2414 error("executeCommand: could not close read pipe");
2415 return false;
2416 }
2417 if (!CloseHandle(stderrWrite))
2418 {
2419 error("executeCommand: could not close read pipe");
2420 return false;
2421 }
2422 while (true)
2423 {
2424 //trace("## stderr");
2425 DWORD avail;
2426 if (!PeekNamedPipe(stderrRead, NULL, 0, NULL, &avail, NULL))
2427 break;
2428 if (avail > 0)
2429 {
2430 DWORD bytesRead = 0;
2431 char readBuf[1025];
2432 if (avail>1024) avail = 1024;
2433 if (!ReadFile(stderrRead, readBuf, avail, &bytesRead, NULL)
2434 || bytesRead == 0)
2435 {
2436 break;
2437 }
2438 for (unsigned int i=0 ; i<bytesRead ; i++)
2439 errbuf.push_back(readBuf[i]);
2440 }
2441 //trace("## stdout");
2442 if (!PeekNamedPipe(stdoutRead, NULL, 0, NULL, &avail, NULL))
2443 break;
2444 if (avail > 0)
2445 {
2446 DWORD bytesRead = 0;
2447 char readBuf[1025];
2448 if (avail>1024) avail = 1024;
2449 if (!ReadFile(stdoutRead, readBuf, avail, &bytesRead, NULL)
2450 || bytesRead==0)
2451 {
2452 break;
2453 }
2454 for (unsigned int i=0 ; i<bytesRead ; i++)
2455 outbuf.push_back(readBuf[i]);
2456 }
2457 DWORD exitCode;
2458 GetExitCodeProcess(piProcessInfo.hProcess, &exitCode);
2459 if (exitCode != STILL_ACTIVE)
2460 break;
2461 Sleep(100);
2462 }
2463 //trace("outbuf:%s", outbuf.c_str());
2464 if (!CloseHandle(stdoutRead))
2465 {
2466 error("executeCommand: could not close read pipe");
2467 return false;
2468 }
2469 if (!CloseHandle(stderrRead))
2470 {
2471 error("executeCommand: could not close read pipe");
2472 return false;
2473 }
2475 DWORD exitCode;
2476 GetExitCodeProcess(piProcessInfo.hProcess, &exitCode);
2477 //trace("exit code:%d", exitCode);
2478 if (exitCode != 0)
2479 {
2480 ret = false;
2481 }
2483 // Clean up
2484 CloseHandle(piProcessInfo.hProcess);
2485 CloseHandle(piProcessInfo.hThread);
2488 return ret;
2490 #else //do it unix-style
2492 String s;
2493 FILE *f = popen(command.c_str(), "r");
2494 int errnum = 0;
2495 if (f)
2496 {
2497 while (true)
2498 {
2499 int ch = fgetc(f);
2500 if (ch < 0)
2501 break;
2502 s.push_back((char)ch);
2503 }
2504 errnum = pclose(f);
2505 }
2506 outbuf = s;
2507 if (errnum < 0)
2508 {
2509 error("exec of command '%s' failed : %s",
2510 command.c_str(), strerror(errno));
2511 return false;
2512 }
2513 else
2514 return true;
2516 #endif
2517 }
2522 bool MakeBase::listDirectories(const String &baseName,
2523 const String &dirName,
2524 std::vector<String> &res)
2525 {
2526 res.push_back(dirName);
2527 String fullPath = baseName;
2528 if (dirName.size()>0)
2529 {
2530 fullPath.append("/");
2531 fullPath.append(dirName);
2532 }
2533 DIR *dir = opendir(fullPath.c_str());
2534 while (true)
2535 {
2536 struct dirent *de = readdir(dir);
2537 if (!de)
2538 break;
2540 //Get the directory member name
2541 String s = de->d_name;
2542 if (s.size() == 0 || s[0] == '.')
2543 continue;
2544 String childName = dirName;
2545 childName.append("/");
2546 childName.append(s);
2548 String fullChildPath = baseName;
2549 fullChildPath.append("/");
2550 fullChildPath.append(childName);
2551 struct stat finfo;
2552 String childNative = getNativePath(fullChildPath);
2553 if (stat(childNative.c_str(), &finfo)<0)
2554 {
2555 error("cannot stat file:%s", childNative.c_str());
2556 }
2557 else if (S_ISDIR(finfo.st_mode))
2558 {
2559 //trace("directory: %s", childName.c_str());
2560 if (!listDirectories(baseName, childName, res))
2561 return false;
2562 }
2563 }
2564 closedir(dir);
2566 return true;
2567 }
2570 bool MakeBase::listFiles(const String &baseDir,
2571 const String &dirName,
2572 std::vector<String> &excludes,
2573 std::vector<String> &res)
2574 {
2575 String fullDir = baseDir;
2576 if (dirName.size()>0)
2577 {
2578 fullDir.append("/");
2579 fullDir.append(dirName);
2580 }
2581 String dirNative = getNativePath(fullDir);
2583 std::vector<String> subdirs;
2584 DIR *dir = opendir(dirNative.c_str());
2585 while (true)
2586 {
2587 struct dirent *de = readdir(dir);
2588 if (!de)
2589 break;
2591 //Get the directory member name
2592 String s = de->d_name;
2593 if (s.size() == 0 || s[0] == '.')
2594 continue;
2595 String childName;
2596 if (dirName.size()>0)
2597 {
2598 childName.append(dirName);
2599 childName.append("/");
2600 }
2601 childName.append(s);
2602 String fullChild = baseDir;
2603 fullChild.append("/");
2604 fullChild.append(childName);
2606 if (std::find(excludes.begin(), excludes.end(), childName)
2607 != excludes.end())
2608 {
2609 //trace("EXCLUDED:%s", childName.c_str());
2610 continue;
2611 }
2613 struct stat finfo;
2614 String nativeName = getNativePath(fullChild);
2615 if (stat(nativeName.c_str(), &finfo)<0)
2616 {
2617 error("cannot stat file:%s", childName.c_str());
2618 return false;
2619 }
2620 else if (S_ISDIR(finfo.st_mode))
2621 {
2622 //trace("directory: %s", childName.c_str());
2623 if (!listFiles(baseDir, childName, excludes, res))
2624 return false;
2625 }
2626 else if (!S_ISREG(finfo.st_mode))
2627 {
2628 trace("not regular: %s", childName.c_str());
2629 }
2630 else
2631 {
2632 res.push_back(childName);
2633 }
2634 }
2635 closedir(dir);
2637 return true;
2638 }
2647 bool MakeBase::getSubstitutions(const String &str, String &result)
2648 {
2649 String s = trim(str);
2650 int len = (int)s.size();
2651 String val;
2652 for (int i=0 ; i<len ; i++)
2653 {
2654 char ch = s[i];
2655 if (ch == '$' && s[i+1] == '{')
2656 {
2657 String varname;
2658 int j = i+2;
2659 for ( ; j<len ; j++)
2660 {
2661 ch = s[j];
2662 if (ch == '$' && s[j+1] == '{')
2663 {
2664 error("attribute %s cannot have nested variable references",
2665 s.c_str());
2666 return false;
2667 }
2668 else if (ch == '}')
2669 {
2670 std::map<String, String>::iterator iter;
2671 iter = properties.find(trim(varname));
2672 if (iter != properties.end())
2673 {
2674 val.append(iter->second);
2675 }
2676 else
2677 {
2678 error("property ${%s} not found", varname.c_str());
2679 return false;
2680 }
2681 break;
2682 }
2683 else
2684 {
2685 varname.push_back(ch);
2686 }
2687 }
2688 i = j;
2689 }
2690 else
2691 {
2692 val.push_back(ch);
2693 }
2694 }
2695 result = val;
2696 return true;
2697 }
2700 bool MakeBase::getAttribute(Element *elem, const String &name,
2701 String &result)
2702 {
2703 String s = elem->getAttribute(name);
2704 return getSubstitutions(s, result);
2705 }
2708 bool MakeBase::getValue(Element *elem, String &result)
2709 {
2710 String s = elem->getValue();
2711 int len = s.size();
2712 //Replace all runs of whitespace with a single space
2713 String stripped;
2714 for (int i = 0 ; i<len ; i++)
2715 {
2716 char ch = s[i];
2717 if (isspace(ch))
2718 {
2719 stripped.push_back(' ');
2720 for ( ; i<len ; i++)
2721 {
2722 ch = s[i];
2723 if (!isspace(ch))
2724 {
2725 stripped.push_back(ch);
2726 break;
2727 }
2728 }
2729 }
2730 else
2731 {
2732 stripped.push_back(ch);
2733 }
2734 }
2735 return getSubstitutions(stripped, result);
2736 }
2739 /**
2740 * Turn 'true' and 'false' into boolean values
2741 */
2742 bool MakeBase::getBool(const String &str, bool &val)
2743 {
2744 if (str == "true")
2745 val = true;
2746 else if (str == "false")
2747 val = false;
2748 else
2749 {
2750 error("expected 'true' or 'false'. found '%s'", str.c_str());
2751 return false;
2752 }
2753 return true;
2754 }
2759 /**
2760 * Parse a <patternset> entry
2761 */
2762 bool MakeBase::getPatternSet(Element *elem,
2763 MakeBase &propRef,
2764 std::vector<String> &includes,
2765 std::vector<String> &excludes
2766 )
2767 {
2768 std::vector<Element *> children = elem->getChildren();
2769 for (unsigned int i=0 ; i<children.size() ; i++)
2770 {
2771 Element *child = children[i];
2772 String tagName = child->getName();
2773 if (tagName == "exclude")
2774 {
2775 String fname;
2776 if (!propRef.getAttribute(child, "name", fname))
2777 return false;
2778 //trace("EXCLUDE: %s", fname.c_str());
2779 excludes.push_back(fname);
2780 }
2781 else if (tagName == "include")
2782 {
2783 String fname;
2784 if (!propRef.getAttribute(child, "name", fname))
2785 return false;
2786 //trace("INCLUDE: %s", fname.c_str());
2787 includes.push_back(fname);
2788 }
2789 }
2791 return true;
2792 }
2797 /**
2798 * Parse a <fileset> entry, and determine which files
2799 * should be included
2800 */
2801 bool MakeBase::getFileSet(Element *elem,
2802 MakeBase &propRef,
2803 String &dir,
2804 std::vector<String> &result)
2805 {
2806 String name = elem->getName();
2807 if (name != "fileset")
2808 {
2809 error("expected <fileset>");
2810 return false;
2811 }
2814 std::vector<String> includes;
2815 std::vector<String> excludes;
2817 //A fileset has one implied patternset
2818 if (!getPatternSet(elem, propRef, includes, excludes))
2819 {
2820 return false;
2821 }
2822 //Look for child tags, including more patternsets
2823 std::vector<Element *> children = elem->getChildren();
2824 for (unsigned int i=0 ; i<children.size() ; i++)
2825 {
2826 Element *child = children[i];
2827 String tagName = child->getName();
2828 if (tagName == "patternset")
2829 {
2830 if (!getPatternSet(child, propRef, includes, excludes))
2831 {
2832 return false;
2833 }
2834 }
2835 }
2837 //Now do the stuff
2838 //Get the base directory for reading file names
2839 if (!propRef.getAttribute(elem, "dir", dir))
2840 return false;
2842 std::vector<String> fileList;
2843 if (dir.size() > 0)
2844 {
2845 String baseDir = propRef.resolve(dir);
2846 if (!listFiles(baseDir, "", excludes, fileList))
2847 return false;
2848 }
2850 std::vector<String>::iterator iter;
2851 for (iter=includes.begin() ; iter!=includes.end() ; iter++)
2852 {
2853 String fname = *iter;
2854 fileList.push_back(fname);
2855 }
2857 result = fileList;
2859 /*
2860 for (unsigned int i=0 ; i<result.size() ; i++)
2861 {
2862 trace("RES:%s", result[i].c_str());
2863 }
2864 */
2866 std::sort(fileList.begin(), fileList.end());
2868 return true;
2869 }
2873 /**
2874 * Create a directory, making intermediate dirs
2875 * if necessary
2876 */
2877 bool MakeBase::createDirectory(const String &dirname)
2878 {
2879 //trace("## createDirectory: %s", dirname.c_str());
2880 //## first check if it exists
2881 struct stat finfo;
2882 String nativeDir = getNativePath(dirname);
2883 char *cnative = (char *) nativeDir.c_str();
2884 if (stat(dirname.c_str(), &finfo)==0)
2885 {
2886 if (!S_ISDIR(finfo.st_mode))
2887 {
2888 error("mkdir: file %s exists but is not a directory",
2889 cnative);
2890 return false;
2891 }
2892 else //exists
2893 {
2894 return true;
2895 }
2896 }
2898 //## 2: pull off the last path segment, if any,
2899 //## to make the dir 'above' this one, if necessary
2900 unsigned int pos = dirname.find_last_of('/');
2901 if (pos != dirname.npos)
2902 {
2903 String subpath = dirname.substr(0, pos);
2904 if (!createDirectory(subpath))
2905 return false;
2906 }
2908 //## 3: now make
2909 if (mkdir(cnative)<0)
2910 {
2911 error("cannot make directory %s", cnative);
2912 return false;
2913 }
2915 return true;
2916 }
2919 /**
2920 * Remove a directory recursively
2921 */
2922 bool MakeBase::removeDirectory(const String &dirName)
2923 {
2924 char *dname = (char *)dirName.c_str();
2926 DIR *dir = opendir(dname);
2927 if (!dir)
2928 {
2929 //# Let this fail nicely.
2930 return true;
2931 //error("error opening directory %s : %s", dname, strerror(errno));
2932 //return false;
2933 }
2935 while (true)
2936 {
2937 struct dirent *de = readdir(dir);
2938 if (!de)
2939 break;
2941 //Get the directory member name
2942 String s = de->d_name;
2943 if (s.size() == 0 || s[0] == '.')
2944 continue;
2945 String childName;
2946 if (dirName.size() > 0)
2947 {
2948 childName.append(dirName);
2949 childName.append("/");
2950 }
2951 childName.append(s);
2954 struct stat finfo;
2955 String childNative = getNativePath(childName);
2956 char *cnative = (char *)childNative.c_str();
2957 if (stat(cnative, &finfo)<0)
2958 {
2959 error("cannot stat file:%s", cnative);
2960 }
2961 else if (S_ISDIR(finfo.st_mode))
2962 {
2963 //trace("DEL dir: %s", childName.c_str());
2964 if (!removeDirectory(childName))
2965 {
2966 return false;
2967 }
2968 }
2969 else if (!S_ISREG(finfo.st_mode))
2970 {
2971 trace("not regular: %s", cnative);
2972 }
2973 else
2974 {
2975 //trace("DEL file: %s", childName.c_str());
2976 if (remove(cnative)<0)
2977 {
2978 error("error deleting %s : %s",
2979 cnative, strerror(errno));
2980 return false;
2981 }
2982 }
2983 }
2984 closedir(dir);
2986 //Now delete the directory
2987 String native = getNativePath(dirName);
2988 if (rmdir(native.c_str())<0)
2989 {
2990 error("could not delete directory %s : %s",
2991 native.c_str() , strerror(errno));
2992 return false;
2993 }
2995 return true;
2997 }
3000 /**
3001 * Copy a file from one name to another. Perform only if needed
3002 */
3003 bool MakeBase::copyFile(const String &srcFile, const String &destFile)
3004 {
3005 //# 1 Check up-to-date times
3006 String srcNative = getNativePath(srcFile);
3007 struct stat srcinfo;
3008 if (stat(srcNative.c_str(), &srcinfo)<0)
3009 {
3010 error("source file %s for copy does not exist",
3011 srcNative.c_str());
3012 return false;
3013 }
3015 String destNative = getNativePath(destFile);
3016 struct stat destinfo;
3017 if (stat(destNative.c_str(), &destinfo)==0)
3018 {
3019 if (destinfo.st_mtime >= srcinfo.st_mtime)
3020 return true;
3021 }
3023 //# 2 prepare a destination directory if necessary
3024 unsigned int pos = destFile.find_last_of('/');
3025 if (pos != destFile.npos)
3026 {
3027 String subpath = destFile.substr(0, pos);
3028 if (!createDirectory(subpath))
3029 return false;
3030 }
3032 //# 3 do the data copy
3033 FILE *srcf = fopen(srcNative.c_str(), "rb");
3034 if (!srcf)
3035 {
3036 error("copyFile cannot open '%s' for reading", srcNative.c_str());
3037 return false;
3038 }
3039 FILE *destf = fopen(destNative.c_str(), "wb");
3040 if (!destf)
3041 {
3042 error("copyFile cannot open %s for writing", srcNative.c_str());
3043 return false;
3044 }
3046 while (!feof(srcf))
3047 {
3048 int ch = fgetc(srcf);
3049 if (ch<0)
3050 break;
3051 fputc(ch, destf);
3052 }
3054 fclose(destf);
3055 fclose(srcf);
3059 return true;
3060 }
3064 /**
3065 * Tests is the modification of fileA is newer than fileB
3066 */
3067 bool MakeBase::isNewerThan(const String &fileA, const String &fileB)
3068 {
3069 //trace("isNewerThan:'%s' , '%s'", fileA.c_str(), fileB.c_str());
3070 String nativeA = getNativePath(fileA);
3071 struct stat infoA;
3072 //IF source does not exist, NOT newer
3073 if (stat(nativeA.c_str(), &infoA)<0)
3074 {
3075 return false;
3076 }
3078 String nativeB = getNativePath(fileB);
3079 struct stat infoB;
3080 //IF dest does not exist, YES, newer
3081 if (stat(nativeB.c_str(), &infoB)<0)
3082 {
3083 return true;
3084 }
3086 //check the actual times
3087 if (infoA.st_mtime > infoB.st_mtime)
3088 {
3089 return true;
3090 }
3092 return false;
3093 }
3096 //########################################################################
3097 //# P K G C O N F I G
3098 //########################################################################
3100 /**
3101 *
3102 */
3103 class PkgConfig : public MakeBase
3104 {
3106 public:
3108 /**
3109 *
3110 */
3111 PkgConfig()
3112 { init(); }
3114 /**
3115 *
3116 */
3117 PkgConfig(const String &namearg)
3118 { init(); name = namearg; }
3120 /**
3121 *
3122 */
3123 PkgConfig(const PkgConfig &other)
3124 { assign(other); }
3126 /**
3127 *
3128 */
3129 PkgConfig &operator=(const PkgConfig &other)
3130 { assign(other); return *this; }
3132 /**
3133 *
3134 */
3135 virtual ~PkgConfig()
3136 { }
3138 /**
3139 *
3140 */
3141 virtual String getName()
3142 { return name; }
3144 /**
3145 *
3146 */
3147 virtual String getDescription()
3148 { return description; }
3150 /**
3151 *
3152 */
3153 virtual String getCflags()
3154 { return cflags; }
3156 /**
3157 *
3158 */
3159 virtual String getLibs()
3160 { return libs; }
3162 /**
3163 *
3164 */
3165 virtual String getVersion()
3166 { return version; }
3168 /**
3169 *
3170 */
3171 virtual int getMajorVersion()
3172 { return majorVersion; }
3174 /**
3175 *
3176 */
3177 virtual int getMinorVersion()
3178 { return minorVersion; }
3180 /**
3181 *
3182 */
3183 virtual int getMicroVersion()
3184 { return microVersion; }
3186 /**
3187 *
3188 */
3189 virtual std::map<String, String> &getAttributes()
3190 { return attrs; }
3192 /**
3193 *
3194 */
3195 virtual std::vector<String> &getRequireList()
3196 { return requireList; }
3198 virtual bool readFile(const String &fileName);
3200 private:
3202 void init()
3203 {
3204 name = "";
3205 description = "";
3206 cflags = "";
3207 libs = "";
3208 requires = "";
3209 version = "";
3210 majorVersion = 0;
3211 minorVersion = 0;
3212 microVersion = 0;
3213 fileName = "";
3214 attrs.clear();
3215 requireList.clear();
3216 }
3218 void assign(const PkgConfig &other)
3219 {
3220 name = other.name;
3221 description = other.description;
3222 cflags = other.cflags;
3223 libs = other.libs;
3224 requires = other.requires;
3225 version = other.version;
3226 majorVersion = other.majorVersion;
3227 minorVersion = other.minorVersion;
3228 microVersion = other.microVersion;
3229 fileName = other.fileName;
3230 attrs = other.attrs;
3231 requireList = other.requireList;
3232 }
3236 int get(int pos);
3238 int skipwhite(int pos);
3240 int getword(int pos, String &ret);
3242 void parseRequires();
3244 void parseVersion();
3246 bool parse(const String &buf);
3248 void dumpAttrs();
3250 String name;
3252 String description;
3254 String cflags;
3256 String libs;
3258 String requires;
3260 String version;
3262 int majorVersion;
3264 int minorVersion;
3266 int microVersion;
3268 String fileName;
3270 std::map<String, String> attrs;
3272 std::vector<String> requireList;
3274 char *parsebuf;
3275 int parselen;
3276 };
3279 /**
3280 * Get a character from the buffer at pos. If out of range,
3281 * return -1 for safety
3282 */
3283 int PkgConfig::get(int pos)
3284 {
3285 if (pos>parselen)
3286 return -1;
3287 return parsebuf[pos];
3288 }
3292 /**
3293 * Skip over all whitespace characters beginning at pos. Return
3294 * the position of the first non-whitespace character.
3295 */
3296 int PkgConfig::skipwhite(int pos)
3297 {
3298 while (pos < parselen)
3299 {
3300 int ch = get(pos);
3301 if (ch < 0)
3302 break;
3303 if (!isspace(ch))
3304 break;
3305 pos++;
3306 }
3307 return pos;
3308 }
3311 /**
3312 * Parse the buffer beginning at pos, for a word. Fill
3313 * 'ret' with the result. Return the position after the
3314 * word.
3315 */
3316 int PkgConfig::getword(int pos, String &ret)
3317 {
3318 while (pos < parselen)
3319 {
3320 int ch = get(pos);
3321 if (ch < 0)
3322 break;
3323 if (!isalnum(ch) && ch != '_' && ch != '-'&& ch != '.')
3324 break;
3325 ret.push_back((char)ch);
3326 pos++;
3327 }
3328 return pos;
3329 }
3331 void PkgConfig::parseRequires()
3332 {
3333 if (requires.size() == 0)
3334 return;
3335 parsebuf = (char *)requires.c_str();
3336 parselen = requires.size();
3337 int pos = 0;
3338 while (pos < parselen)
3339 {
3340 pos = skipwhite(pos);
3341 String val;
3342 int pos2 = getword(pos, val);
3343 if (pos2 == pos)
3344 break;
3345 pos = pos2;
3346 //trace("val %s", val.c_str());
3347 requireList.push_back(val);
3348 }
3349 }
3351 static int getint(const String str)
3352 {
3353 char *s = (char *)str.c_str();
3354 char *ends = NULL;
3355 long val = strtol(s, &ends, 10);
3356 if (ends == s)
3357 return 0L;
3358 else
3359 return val;
3360 }
3362 void PkgConfig::parseVersion()
3363 {
3364 if (version.size() == 0)
3365 return;
3366 String s1, s2, s3;
3367 unsigned int pos = 0;
3368 unsigned int pos2 = version.find('.', pos);
3369 if (pos2 == version.npos)
3370 {
3371 s1 = version;
3372 }
3373 else
3374 {
3375 s1 = version.substr(pos, pos2-pos);
3376 pos = pos2;
3377 pos++;
3378 if (pos < version.size())
3379 {
3380 pos2 = version.find('.', pos);
3381 if (pos2 == version.npos)
3382 {
3383 s2 = version.substr(pos, version.size()-pos);
3384 }
3385 else
3386 {
3387 s2 = version.substr(pos, pos2-pos);
3388 pos = pos2;
3389 pos++;
3390 if (pos < version.size())
3391 s3 = version.substr(pos, pos2-pos);
3392 }
3393 }
3394 }
3396 majorVersion = getint(s1);
3397 minorVersion = getint(s2);
3398 microVersion = getint(s3);
3399 //trace("version:%d.%d.%d", majorVersion,
3400 // minorVersion, microVersion );
3401 }
3404 bool PkgConfig::parse(const String &buf)
3405 {
3406 init();
3408 parsebuf = (char *)buf.c_str();
3409 parselen = buf.size();
3410 int pos = 0;
3413 while (pos < parselen)
3414 {
3415 String attrName;
3416 pos = skipwhite(pos);
3417 int ch = get(pos);
3418 if (ch == '#')
3419 {
3420 //comment. eat the rest of the line
3421 while (pos < parselen)
3422 {
3423 ch = get(pos);
3424 if (ch == '\n' || ch < 0)
3425 break;
3426 pos++;
3427 }
3428 continue;
3429 }
3430 pos = getword(pos, attrName);
3431 if (attrName.size() == 0)
3432 continue;
3433 pos = skipwhite(pos);
3434 ch = get(pos);
3435 if (ch != ':' && ch != '=')
3436 {
3437 error("expected ':' or '='");
3438 return false;
3439 }
3440 pos++;
3441 pos = skipwhite(pos);
3442 String attrVal;
3443 while (pos < parselen)
3444 {
3445 ch = get(pos);
3446 if (ch == '\n' || ch < 0)
3447 break;
3448 else if (ch == '$' && get(pos+1) == '{')
3449 {
3450 //# this is a ${substitution}
3451 pos += 2;
3452 String subName;
3453 while (pos < parselen)
3454 {
3455 ch = get(pos);
3456 if (ch < 0)
3457 {
3458 error("unterminated substitution");
3459 return false;
3460 }
3461 else if (ch == '}')
3462 break;
3463 else
3464 subName.push_back((char)ch);
3465 pos++;
3466 }
3467 //trace("subName:%s", subName.c_str());
3468 String subVal = attrs[subName];
3469 //trace("subVal:%s", subVal.c_str());
3470 attrVal.append(subVal);
3471 }
3472 else
3473 attrVal.push_back((char)ch);
3474 pos++;
3475 }
3477 attrVal = trim(attrVal);
3478 attrs[attrName] = attrVal;
3480 if (attrName == "Name")
3481 name = attrVal;
3482 else if (attrName == "Description")
3483 description = attrVal;
3484 else if (attrName == "Cflags")
3485 cflags = attrVal;
3486 else if (attrName == "Libs")
3487 libs = attrVal;
3488 else if (attrName == "Requires")
3489 requires = attrVal;
3490 else if (attrName == "Version")
3491 version = attrVal;
3493 //trace("name:'%s' value:'%s'",
3494 // attrName.c_str(), attrVal.c_str());
3495 }
3498 parseRequires();
3499 parseVersion();
3501 return true;
3502 }
3504 void PkgConfig::dumpAttrs()
3505 {
3506 trace("### PkgConfig attributes for %s", fileName.c_str());
3507 std::map<String, String>::iterator iter;
3508 for (iter=attrs.begin() ; iter!=attrs.end() ; iter++)
3509 {
3510 trace(" %s = %s", iter->first.c_str(), iter->second.c_str());
3511 }
3512 }
3515 bool PkgConfig::readFile(const String &fileNameArg)
3516 {
3517 fileName = fileNameArg;
3519 FILE *f = fopen(fileName.c_str(), "r");
3520 if (!f)
3521 {
3522 error("cannot open file '%s' for reading", fileName.c_str());
3523 return false;
3524 }
3525 String buf;
3526 while (true)
3527 {
3528 int ch = fgetc(f);
3529 if (ch < 0)
3530 break;
3531 buf.push_back((char)ch);
3532 }
3533 fclose(f);
3535 trace("####### File:\n%s", buf.c_str());
3536 if (!parse(buf))
3537 {
3538 return false;
3539 }
3541 dumpAttrs();
3543 return true;
3544 }
3550 //########################################################################
3551 //# D E P T O O L
3552 //########################################################################
3556 /**
3557 * Class which holds information for each file.
3558 */
3559 class FileRec
3560 {
3561 public:
3563 typedef enum
3564 {
3565 UNKNOWN,
3566 CFILE,
3567 HFILE,
3568 OFILE
3569 } FileType;
3571 /**
3572 * Constructor
3573 */
3574 FileRec()
3575 {init(); type = UNKNOWN;}
3577 /**
3578 * Copy constructor
3579 */
3580 FileRec(const FileRec &other)
3581 {init(); assign(other);}
3582 /**
3583 * Constructor
3584 */
3585 FileRec(int typeVal)
3586 {init(); type = typeVal;}
3587 /**
3588 * Assignment operator
3589 */
3590 FileRec &operator=(const FileRec &other)
3591 {init(); assign(other); return *this;}
3594 /**
3595 * Destructor
3596 */
3597 ~FileRec()
3598 {}
3600 /**
3601 * Directory part of the file name
3602 */
3603 String path;
3605 /**
3606 * Base name, sans directory and suffix
3607 */
3608 String baseName;
3610 /**
3611 * File extension, such as cpp or h
3612 */
3613 String suffix;
3615 /**
3616 * Type of file: CFILE, HFILE, OFILE
3617 */
3618 int type;
3620 /**
3621 * Used to list files ref'd by this one
3622 */
3623 std::map<String, FileRec *> files;
3626 private:
3628 void init()
3629 {
3630 }
3632 void assign(const FileRec &other)
3633 {
3634 type = other.type;
3635 baseName = other.baseName;
3636 suffix = other.suffix;
3637 files = other.files;
3638 }
3640 };
3644 /**
3645 * Simpler dependency record
3646 */
3647 class DepRec
3648 {
3649 public:
3651 /**
3652 * Constructor
3653 */
3654 DepRec()
3655 {init();}
3657 /**
3658 * Copy constructor
3659 */
3660 DepRec(const DepRec &other)
3661 {init(); assign(other);}
3662 /**
3663 * Constructor
3664 */
3665 DepRec(const String &fname)
3666 {init(); name = fname; }
3667 /**
3668 * Assignment operator
3669 */
3670 DepRec &operator=(const DepRec &other)
3671 {init(); assign(other); return *this;}
3674 /**
3675 * Destructor
3676 */
3677 ~DepRec()
3678 {}
3680 /**
3681 * Directory part of the file name
3682 */
3683 String path;
3685 /**
3686 * Base name, without the path and suffix
3687 */
3688 String name;
3690 /**
3691 * Suffix of the source
3692 */
3693 String suffix;
3696 /**
3697 * Used to list files ref'd by this one
3698 */
3699 std::vector<String> files;
3702 private:
3704 void init()
3705 {
3706 }
3708 void assign(const DepRec &other)
3709 {
3710 path = other.path;
3711 name = other.name;
3712 suffix = other.suffix;
3713 files = other.files;
3714 }
3716 };
3719 class DepTool : public MakeBase
3720 {
3721 public:
3723 /**
3724 * Constructor
3725 */
3726 DepTool()
3727 {init();}
3729 /**
3730 * Copy constructor
3731 */
3732 DepTool(const DepTool &other)
3733 {init(); assign(other);}
3735 /**
3736 * Assignment operator
3737 */
3738 DepTool &operator=(const DepTool &other)
3739 {init(); assign(other); return *this;}
3742 /**
3743 * Destructor
3744 */
3745 ~DepTool()
3746 {}
3749 /**
3750 * Reset this section of code
3751 */
3752 virtual void init();
3754 /**
3755 * Reset this section of code
3756 */
3757 virtual void assign(const DepTool &other)
3758 {
3759 }
3761 /**
3762 * Sets the source directory which will be scanned
3763 */
3764 virtual void setSourceDirectory(const String &val)
3765 { sourceDir = val; }
3767 /**
3768 * Returns the source directory which will be scanned
3769 */
3770 virtual String getSourceDirectory()
3771 { return sourceDir; }
3773 /**
3774 * Sets the list of files within the directory to analyze
3775 */
3776 virtual void setFileList(const std::vector<String> &list)
3777 { fileList = list; }
3779 /**
3780 * Creates the list of all file names which will be
3781 * candidates for further processing. Reads make.exclude
3782 * to see which files for directories to leave out.
3783 */
3784 virtual bool createFileList();
3787 /**
3788 * Generates the forward dependency list
3789 */
3790 virtual bool generateDependencies();
3793 /**
3794 * Generates the forward dependency list, saving the file
3795 */
3796 virtual bool generateDependencies(const String &);
3799 /**
3800 * Load a dependency file
3801 */
3802 std::vector<DepRec> loadDepFile(const String &fileName);
3804 /**
3805 * Load a dependency file, generating one if necessary
3806 */
3807 std::vector<DepRec> getDepFile(const String &fileName);
3809 /**
3810 * Save a dependency file
3811 */
3812 bool saveDepFile(const String &fileName);
3815 private:
3818 /**
3819 *
3820 */
3821 void parseName(const String &fullname,
3822 String &path,
3823 String &basename,
3824 String &suffix);
3826 /**
3827 *
3828 */
3829 int get(int pos);
3831 /**
3832 *
3833 */
3834 int skipwhite(int pos);
3836 /**
3837 *
3838 */
3839 int getword(int pos, String &ret);
3841 /**
3842 *
3843 */
3844 bool sequ(int pos, char *key);
3846 /**
3847 *
3848 */
3849 bool addIncludeFile(FileRec *frec, const String &fname);
3851 /**
3852 *
3853 */
3854 bool scanFile(const String &fname, FileRec *frec);
3856 /**
3857 *
3858 */
3859 bool processDependency(FileRec *ofile,
3860 FileRec *include,
3861 int depth);
3863 /**
3864 *
3865 */
3866 String sourceDir;
3868 /**
3869 *
3870 */
3871 std::vector<String> fileList;
3873 /**
3874 *
3875 */
3876 std::vector<String> directories;
3878 /**
3879 * A list of all files which will be processed for
3880 * dependencies. This is the only list that has the actual
3881 * records. All other lists have pointers to these records.
3882 */
3883 std::map<String, FileRec *> allFiles;
3885 /**
3886 * The list of .o files, and the
3887 * dependencies upon them.
3888 */
3889 std::map<String, FileRec *> depFiles;
3891 int depFileSize;
3892 char *depFileBuf;
3895 };
3901 /**
3902 * Clean up after processing. Called by the destructor, but should
3903 * also be called before the object is reused.
3904 */
3905 void DepTool::init()
3906 {
3907 sourceDir = ".";
3909 fileList.clear();
3910 directories.clear();
3912 //clear refs
3913 depFiles.clear();
3914 //clear records
3915 std::map<String, FileRec *>::iterator iter;
3916 for (iter=allFiles.begin() ; iter!=allFiles.end() ; iter++)
3917 delete iter->second;
3919 allFiles.clear();
3921 }
3926 /**
3927 * Parse a full path name into path, base name, and suffix
3928 */
3929 void DepTool::parseName(const String &fullname,
3930 String &path,
3931 String &basename,
3932 String &suffix)
3933 {
3934 if (fullname.size() < 2)
3935 return;
3937 unsigned int pos = fullname.find_last_of('/');
3938 if (pos != fullname.npos && pos<fullname.size()-1)
3939 {
3940 path = fullname.substr(0, pos);
3941 pos++;
3942 basename = fullname.substr(pos, fullname.size()-pos);
3943 }
3944 else
3945 {
3946 path = "";
3947 basename = fullname;
3948 }
3950 pos = basename.find_last_of('.');
3951 if (pos != basename.npos && pos<basename.size()-1)
3952 {
3953 suffix = basename.substr(pos+1, basename.size()-pos-1);
3954 basename = basename.substr(0, pos);
3955 }
3957 //trace("parsename:%s %s %s", path.c_str(),
3958 // basename.c_str(), suffix.c_str());
3959 }
3963 /**
3964 * Generate our internal file list.
3965 */
3966 bool DepTool::createFileList()
3967 {
3969 for (unsigned int i=0 ; i<fileList.size() ; i++)
3970 {
3971 String fileName = fileList[i];
3972 //trace("## FileName:%s", fileName.c_str());
3973 String path;
3974 String basename;
3975 String sfx;
3976 parseName(fileName, path, basename, sfx);
3977 if (sfx == "cpp" || sfx == "c" || sfx == "cxx" ||
3978 sfx == "cc" || sfx == "CC")
3979 {
3980 FileRec *fe = new FileRec(FileRec::CFILE);
3981 fe->path = path;
3982 fe->baseName = basename;
3983 fe->suffix = sfx;
3984 allFiles[fileName] = fe;
3985 }
3986 else if (sfx == "h" || sfx == "hh" ||
3987 sfx == "hpp" || sfx == "hxx")
3988 {
3989 FileRec *fe = new FileRec(FileRec::HFILE);
3990 fe->path = path;
3991 fe->baseName = basename;
3992 fe->suffix = sfx;
3993 allFiles[fileName] = fe;
3994 }
3995 }
3997 if (!listDirectories(sourceDir, "", directories))
3998 return false;
4000 return true;
4001 }
4007 /**
4008 * Get a character from the buffer at pos. If out of range,
4009 * return -1 for safety
4010 */
4011 int DepTool::get(int pos)
4012 {
4013 if (pos>depFileSize)
4014 return -1;
4015 return depFileBuf[pos];
4016 }
4020 /**
4021 * Skip over all whitespace characters beginning at pos. Return
4022 * the position of the first non-whitespace character.
4023 */
4024 int DepTool::skipwhite(int pos)
4025 {
4026 while (pos < depFileSize)
4027 {
4028 int ch = get(pos);
4029 if (ch < 0)
4030 break;
4031 if (!isspace(ch))
4032 break;
4033 pos++;
4034 }
4035 return pos;
4036 }
4039 /**
4040 * Parse the buffer beginning at pos, for a word. Fill
4041 * 'ret' with the result. Return the position after the
4042 * word.
4043 */
4044 int DepTool::getword(int pos, String &ret)
4045 {
4046 while (pos < depFileSize)
4047 {
4048 int ch = get(pos);
4049 if (ch < 0)
4050 break;
4051 if (isspace(ch))
4052 break;
4053 ret.push_back((char)ch);
4054 pos++;
4055 }
4056 return pos;
4057 }
4059 /**
4060 * Return whether the sequence of characters in the buffer
4061 * beginning at pos match the key, for the length of the key
4062 */
4063 bool DepTool::sequ(int pos, char *key)
4064 {
4065 while (*key)
4066 {
4067 if (*key != get(pos))
4068 return false;
4069 key++; pos++;
4070 }
4071 return true;
4072 }
4076 /**
4077 * Add an include file name to a file record. If the name
4078 * is not found in allFiles explicitly, try prepending include
4079 * directory names to it and try again.
4080 */
4081 bool DepTool::addIncludeFile(FileRec *frec, const String &iname)
4082 {
4084 std::map<String, FileRec *>::iterator iter =
4085 allFiles.find(iname);
4086 if (iter != allFiles.end()) //already exists
4087 {
4088 //h file in same dir
4089 FileRec *other = iter->second;
4090 //trace("local: '%s'", iname.c_str());
4091 frec->files[iname] = other;
4092 return true;
4093 }
4094 else
4095 {
4096 //look in other dirs
4097 std::vector<String>::iterator diter;
4098 for (diter=directories.begin() ;
4099 diter!=directories.end() ; diter++)
4100 {
4101 String dfname = *diter;
4102 dfname.append("/");
4103 dfname.append(iname);
4104 iter = allFiles.find(dfname);
4105 if (iter != allFiles.end())
4106 {
4107 FileRec *other = iter->second;
4108 //trace("other: '%s'", iname.c_str());
4109 frec->files[dfname] = other;
4110 return true;
4111 }
4112 }
4113 }
4114 return true;
4115 }
4119 /**
4120 * Lightly parse a file to find the #include directives. Do
4121 * a bit of state machine stuff to make sure that the directive
4122 * is valid. (Like not in a comment).
4123 */
4124 bool DepTool::scanFile(const String &fname, FileRec *frec)
4125 {
4126 String fileName;
4127 if (sourceDir.size() > 0)
4128 {
4129 fileName.append(sourceDir);
4130 fileName.append("/");
4131 }
4132 fileName.append(fname);
4133 String nativeName = getNativePath(fileName);
4134 FILE *f = fopen(nativeName.c_str(), "r");
4135 if (!f)
4136 {
4137 error("Could not open '%s' for reading", fname.c_str());
4138 return false;
4139 }
4140 String buf;
4141 while (true)
4142 {
4143 int ch = fgetc(f);
4144 if (ch < 0)
4145 break;
4146 buf.push_back((char)ch);
4147 }
4148 fclose(f);
4150 depFileSize = buf.size();
4151 depFileBuf = (char *)buf.c_str();
4152 int pos = 0;
4155 while (pos < depFileSize)
4156 {
4157 //trace("p:%c", get(pos));
4159 //# Block comment
4160 if (get(pos) == '/' && get(pos+1) == '*')
4161 {
4162 pos += 2;
4163 while (pos < depFileSize)
4164 {
4165 if (get(pos) == '*' && get(pos+1) == '/')
4166 {
4167 pos += 2;
4168 break;
4169 }
4170 else
4171 pos++;
4172 }
4173 }
4174 //# Line comment
4175 else if (get(pos) == '/' && get(pos+1) == '/')
4176 {
4177 pos += 2;
4178 while (pos < depFileSize)
4179 {
4180 if (get(pos) == '\n')
4181 {
4182 pos++;
4183 break;
4184 }
4185 else
4186 pos++;
4187 }
4188 }
4189 //# #include! yaay
4190 else if (sequ(pos, "#include"))
4191 {
4192 pos += 8;
4193 pos = skipwhite(pos);
4194 String iname;
4195 pos = getword(pos, iname);
4196 if (iname.size()>2)
4197 {
4198 iname = iname.substr(1, iname.size()-2);
4199 addIncludeFile(frec, iname);
4200 }
4201 }
4202 else
4203 {
4204 pos++;
4205 }
4206 }
4208 return true;
4209 }
4213 /**
4214 * Recursively check include lists to find all files in allFiles to which
4215 * a given file is dependent.
4216 */
4217 bool DepTool::processDependency(FileRec *ofile,
4218 FileRec *include,
4219 int depth)
4220 {
4221 std::map<String, FileRec *>::iterator iter;
4222 for (iter=include->files.begin() ; iter!=include->files.end() ; iter++)
4223 {
4224 String fname = iter->first;
4225 if (ofile->files.find(fname) != ofile->files.end())
4226 {
4227 //trace("file '%s' already seen", fname.c_str());
4228 continue;
4229 }
4230 FileRec *child = iter->second;
4231 ofile->files[fname] = child;
4233 processDependency(ofile, child, depth+1);
4234 }
4237 return true;
4238 }
4244 /**
4245 * Generate the file dependency list.
4246 */
4247 bool DepTool::generateDependencies()
4248 {
4249 std::map<String, FileRec *>::iterator iter;
4250 //# First pass. Scan for all includes
4251 for (iter=allFiles.begin() ; iter!=allFiles.end() ; iter++)
4252 {
4253 FileRec *frec = iter->second;
4254 if (!scanFile(iter->first, frec))
4255 {
4256 //quit?
4257 }
4258 }
4260 //# Second pass. Scan for all includes
4261 for (iter=allFiles.begin() ; iter!=allFiles.end() ; iter++)
4262 {
4263 FileRec *include = iter->second;
4264 if (include->type == FileRec::CFILE)
4265 {
4266 String cFileName = iter->first;
4267 FileRec *ofile = new FileRec(FileRec::OFILE);
4268 ofile->path = include->path;
4269 ofile->baseName = include->baseName;
4270 ofile->suffix = include->suffix;
4271 String fname = include->path;
4272 if (fname.size()>0)
4273 fname.append("/");
4274 fname.append(include->baseName);
4275 fname.append(".o");
4276 depFiles[fname] = ofile;
4277 //add the .c file first? no, don't
4278 //ofile->files[cFileName] = include;
4280 //trace("ofile:%s", fname.c_str());
4282 processDependency(ofile, include, 0);
4283 }
4284 }
4287 return true;
4288 }
4292 /**
4293 * High-level call to generate deps and optionally save them
4294 */
4295 bool DepTool::generateDependencies(const String &fileName)
4296 {
4297 if (!createFileList())
4298 return false;
4299 if (!generateDependencies())
4300 return false;
4301 if (!saveDepFile(fileName))
4302 return false;
4303 return true;
4304 }
4307 /**
4308 * This saves the dependency cache.
4309 */
4310 bool DepTool::saveDepFile(const String &fileName)
4311 {
4312 time_t tim;
4313 time(&tim);
4315 FILE *f = fopen(fileName.c_str(), "w");
4316 if (!f)
4317 {
4318 trace("cannot open '%s' for writing", fileName.c_str());
4319 }
4320 fprintf(f, "<?xml version='1.0'?>\n");
4321 fprintf(f, "<!--\n");
4322 fprintf(f, "########################################################\n");
4323 fprintf(f, "## File: build.dep\n");
4324 fprintf(f, "## Generated by BuildTool at :%s", ctime(&tim));
4325 fprintf(f, "########################################################\n");
4326 fprintf(f, "-->\n");
4328 fprintf(f, "<dependencies source='%s'>\n\n", sourceDir.c_str());
4329 std::map<String, FileRec *>::iterator iter;
4330 for (iter=depFiles.begin() ; iter!=depFiles.end() ; iter++)
4331 {
4332 FileRec *frec = iter->second;
4333 if (frec->type == FileRec::OFILE)
4334 {
4335 fprintf(f, "<object path='%s' name='%s' suffix='%s'>\n",
4336 frec->path.c_str(), frec->baseName.c_str(), frec->suffix.c_str());
4337 std::map<String, FileRec *>::iterator citer;
4338 for (citer=frec->files.begin() ; citer!=frec->files.end() ; citer++)
4339 {
4340 String cfname = citer->first;
4341 fprintf(f, " <dep name='%s'/>\n", cfname.c_str());
4342 }
4343 fprintf(f, "</object>\n\n");
4344 }
4345 }
4347 fprintf(f, "</dependencies>\n");
4348 fprintf(f, "\n");
4349 fprintf(f, "<!--\n");
4350 fprintf(f, "########################################################\n");
4351 fprintf(f, "## E N D\n");
4352 fprintf(f, "########################################################\n");
4353 fprintf(f, "-->\n");
4355 fclose(f);
4357 return true;
4358 }
4363 /**
4364 * This loads the dependency cache.
4365 */
4366 std::vector<DepRec> DepTool::loadDepFile(const String &depFile)
4367 {
4368 std::vector<DepRec> result;
4370 Parser parser;
4371 Element *root = parser.parseFile(depFile.c_str());
4372 if (!root)
4373 {
4374 error("Could not open %s for reading", depFile.c_str());
4375 return result;
4376 }
4378 if (root->getChildren().size()==0 ||
4379 root->getChildren()[0]->getName()!="dependencies")
4380 {
4381 error("Main xml element should be <dependencies>");
4382 delete root;
4383 return result;
4384 }
4386 //########## Start parsing
4387 Element *depList = root->getChildren()[0];
4389 std::vector<Element *> objects = depList->getChildren();
4390 for (unsigned int i=0 ; i<objects.size() ; i++)
4391 {
4392 Element *objectElem = objects[i];
4393 String tagName = objectElem->getName();
4394 if (tagName == "object")
4395 {
4396 String objName = objectElem->getAttribute("name");
4397 //trace("object:%s", objName.c_str());
4398 DepRec depObject(objName);
4399 depObject.path = objectElem->getAttribute("path");
4400 depObject.suffix = objectElem->getAttribute("suffix");
4401 //########## DESCRIPTION
4402 std::vector<Element *> depElems = objectElem->getChildren();
4403 for (unsigned int i=0 ; i<depElems.size() ; i++)
4404 {
4405 Element *depElem = depElems[i];
4406 tagName = depElem->getName();
4407 if (tagName == "dep")
4408 {
4409 String depName = depElem->getAttribute("name");
4410 //trace(" dep:%s", depName.c_str());
4411 depObject.files.push_back(depName);
4412 }
4413 }
4414 //Insert into the result list, in a sorted manner
4415 bool inserted = false;
4416 std::vector<DepRec>::iterator iter;
4417 for (iter = result.begin() ; iter != result.end() ; iter++)
4418 {
4419 String vpath = iter->path;
4420 vpath.append("/");
4421 vpath.append(iter->name);
4422 String opath = depObject.path;
4423 opath.append("/");
4424 opath.append(depObject.name);
4425 if (vpath > opath)
4426 {
4427 inserted = true;
4428 iter = result.insert(iter, depObject);
4429 break;
4430 }
4431 }
4432 if (!inserted)
4433 result.push_back(depObject);
4434 }
4435 }
4437 delete root;
4439 return result;
4440 }
4443 /**
4444 * This loads the dependency cache.
4445 */
4446 std::vector<DepRec> DepTool::getDepFile(const String &depFile)
4447 {
4448 std::vector<DepRec> result = loadDepFile(depFile);
4449 if (result.size() == 0)
4450 {
4451 generateDependencies(depFile);
4452 result = loadDepFile(depFile);
4453 }
4454 return result;
4455 }
4460 //########################################################################
4461 //# T A S K
4462 //########################################################################
4463 //forward decl
4464 class Target;
4465 class Make;
4467 /**
4468 *
4469 */
4470 class Task : public MakeBase
4471 {
4473 public:
4475 typedef enum
4476 {
4477 TASK_NONE,
4478 TASK_AR,
4479 TASK_CC,
4480 TASK_COPY,
4481 TASK_DELETE,
4482 TASK_JAR,
4483 TASK_JAVAC,
4484 TASK_LINK,
4485 TASK_MKDIR,
4486 TASK_MSGFMT,
4487 TASK_RANLIB,
4488 TASK_RC,
4489 TASK_STRIP,
4490 TASK_TSTAMP
4491 } TaskType;
4494 /**
4495 *
4496 */
4497 Task(MakeBase &par) : parent(par)
4498 { init(); }
4500 /**
4501 *
4502 */
4503 Task(const Task &other) : parent(other.parent)
4504 { init(); assign(other); }
4506 /**
4507 *
4508 */
4509 Task &operator=(const Task &other)
4510 { assign(other); return *this; }
4512 /**
4513 *
4514 */
4515 virtual ~Task()
4516 { }
4519 /**
4520 *
4521 */
4522 virtual MakeBase &getParent()
4523 { return parent; }
4525 /**
4526 *
4527 */
4528 virtual int getType()
4529 { return type; }
4531 /**
4532 *
4533 */
4534 virtual void setType(int val)
4535 { type = val; }
4537 /**
4538 *
4539 */
4540 virtual String getName()
4541 { return name; }
4543 /**
4544 *
4545 */
4546 virtual bool execute()
4547 { return true; }
4549 /**
4550 *
4551 */
4552 virtual bool parse(Element *elem)
4553 { return true; }
4555 /**
4556 *
4557 */
4558 Task *createTask(Element *elem);
4561 protected:
4563 void init()
4564 {
4565 type = TASK_NONE;
4566 name = "none";
4567 }
4569 void assign(const Task &other)
4570 {
4571 type = other.type;
4572 name = other.name;
4573 }
4575 String getAttribute(Element *elem, const String &attrName)
4576 {
4577 String str;
4578 return str;
4579 }
4581 MakeBase &parent;
4583 int type;
4585 String name;
4586 };
4591 /**
4592 * Run the "ar" command to archive .o's into a .a
4593 */
4594 class TaskAr : public Task
4595 {
4596 public:
4598 TaskAr(MakeBase &par) : Task(par)
4599 {
4600 type = TASK_AR; name = "ar";
4601 command = "ar crv";
4602 }
4604 virtual ~TaskAr()
4605 {}
4607 virtual bool execute()
4608 {
4609 //trace("###########HERE %d", fileSet.size());
4610 bool doit = false;
4612 String fullOut = parent.resolve(fileName);
4613 //trace("ar fullout: %s", fullOut.c_str());
4616 for (unsigned int i=0 ; i<fileSet.size() ; i++)
4617 {
4618 String fname;
4619 if (fileSetDir.size()>0)
4620 {
4621 fname.append(fileSetDir);
4622 fname.append("/");
4623 }
4624 fname.append(fileSet[i]);
4625 String fullName = parent.resolve(fname);
4626 //trace("ar : %s/%s", fullOut.c_str(), fullName.c_str());
4627 if (isNewerThan(fullName, fullOut))
4628 doit = true;
4629 }
4630 //trace("Needs it:%d", doit);
4631 if (!doit)
4632 {
4633 return true;
4634 }
4636 String cmd = command;
4637 cmd.append(" ");
4638 cmd.append(fullOut);
4639 for (unsigned int i=0 ; i<fileSet.size() ; i++)
4640 {
4641 String fname;
4642 if (fileSetDir.size()>0)
4643 {
4644 fname.append(fileSetDir);
4645 fname.append("/");
4646 }
4647 fname.append(fileSet[i]);
4648 String fullName = parent.resolve(fname);
4650 cmd.append(" ");
4651 cmd.append(fullName);
4652 }
4654 String outString, errString;
4655 if (!executeCommand(cmd.c_str(), "", outString, errString))
4656 {
4657 error("AR problem: %s", errString.c_str());
4658 return false;
4659 }
4661 return true;
4662 }
4664 virtual bool parse(Element *elem)
4665 {
4666 if (!parent.getAttribute(elem, "file", fileName))
4667 return false;
4669 std::vector<Element *> children = elem->getChildren();
4670 for (unsigned int i=0 ; i<children.size() ; i++)
4671 {
4672 Element *child = children[i];
4673 String tagName = child->getName();
4674 if (tagName == "fileset")
4675 {
4676 if (!getFileSet(child, parent, fileSetDir, fileSet))
4677 return false;
4678 }
4679 }
4680 return true;
4681 }
4683 private:
4685 String command;
4686 String fileName;
4687 String fileSetDir;
4688 std::vector<String> fileSet;
4690 };
4693 /**
4694 * This task runs the C/C++ compiler. The compiler is invoked
4695 * for all .c or .cpp files which are newer than their correcsponding
4696 * .o files.
4697 */
4698 class TaskCC : public Task
4699 {
4700 public:
4702 TaskCC(MakeBase &par) : Task(par)
4703 {
4704 type = TASK_CC; name = "cc";
4705 ccCommand = "gcc";
4706 cxxCommand = "g++";
4707 source = ".";
4708 dest = ".";
4709 flags = "";
4710 defines = "";
4711 includes = "";
4712 sourceFiles.clear();
4713 }
4715 virtual ~TaskCC()
4716 {}
4718 virtual bool execute()
4719 {
4720 DepTool depTool;
4721 depTool.setSourceDirectory(source);
4722 depTool.setFileList(sourceFiles);
4723 std::vector<DepRec> deps = depTool.getDepFile("build.dep");
4725 String incs;
4726 incs.append("-I");
4727 incs.append(parent.resolve("."));
4728 incs.append(" ");
4729 if (includes.size()>0)
4730 {
4731 incs.append(includes);
4732 incs.append(" ");
4733 }
4734 std::set<String> paths;
4735 std::vector<DepRec>::iterator viter;
4736 for (viter=deps.begin() ; viter!=deps.end() ; viter++)
4737 {
4738 DepRec dep = *viter;
4739 if (dep.path.size()>0)
4740 paths.insert(dep.path);
4741 }
4742 if (source.size()>0)
4743 {
4744 incs.append(" -I");
4745 incs.append(parent.resolve(source));
4746 incs.append(" ");
4747 }
4748 std::set<String>::iterator setIter;
4749 for (setIter=paths.begin() ; setIter!=paths.end() ; setIter++)
4750 {
4751 incs.append(" -I");
4752 String dname;
4753 if (source.size()>0)
4754 {
4755 dname.append(source);
4756 dname.append("/");
4757 }
4758 dname.append(*setIter);
4759 incs.append(parent.resolve(dname));
4760 }
4761 std::vector<String> cfiles;
4762 for (viter=deps.begin() ; viter!=deps.end() ; viter++)
4763 {
4764 DepRec dep = *viter;
4766 //## Select command
4767 String sfx = dep.suffix;
4768 String command = ccCommand;
4769 if (sfx == "cpp" || sfx == "c++" || sfx == "cc"
4770 || sfx == "CC")
4771 command = cxxCommand;
4773 //## Make paths
4774 String destPath = dest;
4775 String srcPath = source;
4776 if (dep.path.size()>0)
4777 {
4778 destPath.append("/");
4779 destPath.append(dep.path);
4780 srcPath.append("/");
4781 srcPath.append(dep.path);
4782 }
4783 //## Make sure destination directory exists
4784 if (!createDirectory(destPath))
4785 return false;
4787 //## Check whether it needs to be done
4788 String destFullName = destPath;
4789 destFullName.append("/");
4790 destFullName.append(dep.name);
4791 destFullName.append(".o");
4792 String srcFullName = srcPath;
4793 srcFullName.append("/");
4794 srcFullName.append(dep.name);
4795 srcFullName.append(".");
4796 srcFullName.append(dep.suffix);
4797 if (!isNewerThan(srcFullName, destFullName))
4798 {
4799 //trace("%s skipped", srcFullName.c_str());
4800 continue;
4801 }
4803 //## Assemble the command
4804 String cmd = command;
4805 cmd.append(" -c ");
4806 cmd.append(flags);
4807 cmd.append(" ");
4808 cmd.append(defines);
4809 cmd.append(" ");
4810 cmd.append(incs);
4811 cmd.append(" ");
4812 cmd.append(srcFullName);
4813 cmd.append(" -o ");
4814 cmd.append(destFullName);
4816 //## Execute the command
4818 String outString, errString;
4819 if (!executeCommand(cmd.c_str(), "", outString, errString))
4820 {
4821 error("problem compiling: %s", errString.c_str());
4822 return false;
4823 }
4824 }
4826 return true;
4827 }
4829 virtual bool parse(Element *elem)
4830 {
4831 String s;
4832 if (!parent.getAttribute(elem, "command", s))
4833 return false;
4834 if (s.size()>0) { ccCommand = s; cxxCommand = s; }
4835 if (!parent.getAttribute(elem, "cc", s))
4836 return false;
4837 if (s.size()>0) ccCommand = s;
4838 if (!parent.getAttribute(elem, "cxx", s))
4839 return false;
4840 if (s.size()>0) cxxCommand = s;
4841 if (!parent.getAttribute(elem, "destdir", s))
4842 return false;
4843 if (s.size()>0) dest = s;
4845 std::vector<Element *> children = elem->getChildren();
4846 for (unsigned int i=0 ; i<children.size() ; i++)
4847 {
4848 Element *child = children[i];
4849 String tagName = child->getName();
4850 if (tagName == "flags")
4851 {
4852 if (!parent.getValue(child, flags))
4853 return false;
4854 }
4855 else if (tagName == "includes")
4856 {
4857 if (!parent.getValue(child, includes))
4858 return false;
4859 }
4860 else if (tagName == "defines")
4861 {
4862 if (!parent.getValue(child, defines))
4863 return false;
4864 }
4865 else if (tagName == "fileset")
4866 {
4867 if (!getFileSet(child, parent, source, sourceFiles))
4868 return false;
4869 }
4870 }
4872 return true;
4873 }
4875 protected:
4877 String ccCommand;
4878 String cxxCommand;
4879 String source;
4880 String dest;
4881 String flags;
4882 String defines;
4883 String includes;
4884 std::vector<String> sourceFiles;
4886 };
4890 /**
4891 *
4892 */
4893 class TaskCopy : public Task
4894 {
4895 public:
4897 typedef enum
4898 {
4899 CP_NONE,
4900 CP_TOFILE,
4901 CP_TODIR
4902 } CopyType;
4904 TaskCopy(MakeBase &par) : Task(par)
4905 {
4906 type = TASK_COPY; name = "copy";
4907 cptype = CP_NONE;
4908 verbose = false;
4909 haveFileSet = false;
4910 }
4912 virtual ~TaskCopy()
4913 {}
4915 virtual bool execute()
4916 {
4917 switch (cptype)
4918 {
4919 case CP_TOFILE:
4920 {
4921 if (fileName.size()>0)
4922 {
4923 status(" : %s", fileName.c_str());
4924 String fullSource = parent.resolve(fileName);
4925 String fullDest = parent.resolve(toFileName);
4926 //trace("copy %s to file %s", fullSource.c_str(),
4927 // fullDest.c_str());
4928 if (!isNewerThan(fullSource, fullDest))
4929 {
4930 return true;
4931 }
4932 if (!copyFile(fullSource, fullDest))
4933 return false;
4934 status(" : 1 file copied");
4935 }
4936 return true;
4937 }
4938 case CP_TODIR:
4939 {
4940 if (haveFileSet)
4941 {
4942 int nrFiles = 0;
4943 status(" : %s", fileSetDir.c_str());
4944 for (unsigned int i=0 ; i<fileSet.size() ; i++)
4945 {
4946 String fileName = fileSet[i];
4948 String sourcePath;
4949 if (fileSetDir.size()>0)
4950 {
4951 sourcePath.append(fileSetDir);
4952 sourcePath.append("/");
4953 }
4954 sourcePath.append(fileName);
4955 String fullSource = parent.resolve(sourcePath);
4957 //Get the immediate parent directory's base name
4958 String baseFileSetDir = fileSetDir;
4959 unsigned int pos = baseFileSetDir.find_last_of('/');
4960 if (pos!=baseFileSetDir.npos &&
4961 pos < baseFileSetDir.size()-1)
4962 baseFileSetDir =
4963 baseFileSetDir.substr(pos+1,
4964 baseFileSetDir.size());
4965 //Now make the new path
4966 String destPath;
4967 if (toDirName.size()>0)
4968 {
4969 destPath.append(toDirName);
4970 destPath.append("/");
4971 }
4972 if (baseFileSetDir.size()>0)
4973 {
4974 destPath.append(baseFileSetDir);
4975 destPath.append("/");
4976 }
4977 destPath.append(fileName);
4978 String fullDest = parent.resolve(destPath);
4979 //trace("fileName:%s", fileName.c_str());
4980 //trace("copy %s to new dir : %s", fullSource.c_str(),
4981 // fullDest.c_str());
4982 if (!isNewerThan(fullSource, fullDest))
4983 {
4984 //trace("copy skipping %s", fullSource.c_str());
4985 continue;
4986 }
4987 if (!copyFile(fullSource, fullDest))
4988 return false;
4989 nrFiles++;
4990 }
4991 status(" : %d file(s) copied", nrFiles);
4992 }
4993 else //file source
4994 {
4995 //For file->dir we want only the basename of
4996 //the source appended to the dest dir
4997 status(" : %s", fileName.c_str());
4998 String baseName = fileName;
4999 unsigned int pos = baseName.find_last_of('/');
5000 if (pos!=baseName.npos && pos<baseName.size()-1)
5001 baseName = baseName.substr(pos+1, baseName.size());
5002 String fullSource = parent.resolve(fileName);
5003 String destPath;
5004 if (toDirName.size()>0)
5005 {
5006 destPath.append(toDirName);
5007 destPath.append("/");
5008 }
5009 destPath.append(baseName);
5010 String fullDest = parent.resolve(destPath);
5011 //trace("copy %s to new dir : %s", fullSource.c_str(),
5012 // fullDest.c_str());
5013 if (!isNewerThan(fullSource, fullDest))
5014 {
5015 return true;
5016 }
5017 if (!copyFile(fullSource, fullDest))
5018 return false;
5019 status(" : 1 file copied");
5020 }
5021 return true;
5022 }
5023 }
5024 return true;
5025 }
5028 virtual bool parse(Element *elem)
5029 {
5030 if (!parent.getAttribute(elem, "file", fileName))
5031 return false;
5032 if (!parent.getAttribute(elem, "tofile", toFileName))
5033 return false;
5034 if (toFileName.size() > 0)
5035 cptype = CP_TOFILE;
5036 if (!parent.getAttribute(elem, "todir", toDirName))
5037 return false;
5038 if (toDirName.size() > 0)
5039 cptype = CP_TODIR;
5040 String ret;
5041 if (!parent.getAttribute(elem, "verbose", ret))
5042 return false;
5043 if (ret.size()>0 && !getBool(ret, verbose))
5044 return false;
5046 haveFileSet = false;
5048 std::vector<Element *> children = elem->getChildren();
5049 for (unsigned int i=0 ; i<children.size() ; i++)
5050 {
5051 Element *child = children[i];
5052 String tagName = child->getName();
5053 if (tagName == "fileset")
5054 {
5055 if (!getFileSet(child, parent, fileSetDir, fileSet))
5056 {
5057 error("problem getting fileset");
5058 return false;
5059 }
5060 haveFileSet = true;
5061 }
5062 }
5064 //Perform validity checks
5065 if (fileName.size()>0 && fileSet.size()>0)
5066 {
5067 error("<copy> can only have one of : file= and <fileset>");
5068 return false;
5069 }
5070 if (toFileName.size()>0 && toDirName.size()>0)
5071 {
5072 error("<copy> can only have one of : tofile= or todir=");
5073 return false;
5074 }
5075 if (haveFileSet && toDirName.size()==0)
5076 {
5077 error("a <copy> task with a <fileset> must have : todir=");
5078 return false;
5079 }
5080 if (cptype == CP_TOFILE && fileName.size()==0)
5081 {
5082 error("<copy> tofile= must be associated with : file=");
5083 return false;
5084 }
5085 if (cptype == CP_TODIR && fileName.size()==0 && !haveFileSet)
5086 {
5087 error("<copy> todir= must be associated with : file= or <fileset>");
5088 return false;
5089 }
5091 return true;
5092 }
5094 private:
5096 int cptype;
5097 String fileName;
5098 String fileSetDir;
5099 std::vector<String> fileSet;
5100 String toFileName;
5101 String toDirName;
5102 bool verbose;
5103 bool haveFileSet;
5104 };
5107 /**
5108 *
5109 */
5110 class TaskDelete : public Task
5111 {
5112 public:
5114 typedef enum
5115 {
5116 DEL_FILE,
5117 DEL_DIR,
5118 DEL_FILESET
5119 } DeleteType;
5121 TaskDelete(MakeBase &par) : Task(par)
5122 {
5123 type = TASK_DELETE;
5124 name = "delete";
5125 delType = DEL_FILE;
5126 verbose = false;
5127 quiet = false;
5128 failOnError = true;
5129 }
5131 virtual ~TaskDelete()
5132 {}
5134 virtual bool execute()
5135 {
5136 struct stat finfo;
5137 switch (delType)
5138 {
5139 case DEL_FILE:
5140 {
5141 status(" : %s", fileName.c_str());
5142 String fullName = parent.resolve(fileName);
5143 char *fname = (char *)fullName.c_str();
5144 //does not exist
5145 if (stat(fname, &finfo)<0)
5146 return true;
5147 //exists but is not a regular file
5148 if (!S_ISREG(finfo.st_mode))
5149 {
5150 error("<delete> failed. '%s' exists and is not a regular file",
5151 fname);
5152 return false;
5153 }
5154 if (remove(fname)<0)
5155 {
5156 error("<delete> failed: %s", strerror(errno));
5157 return false;
5158 }
5159 return true;
5160 }
5161 case DEL_DIR:
5162 {
5163 status(" : %s", dirName.c_str());
5164 String fullDir = parent.resolve(dirName);
5165 if (!removeDirectory(fullDir))
5166 return false;
5167 return true;
5168 }
5169 }
5170 return true;
5171 }
5173 virtual bool parse(Element *elem)
5174 {
5175 if (!parent.getAttribute(elem, "file", fileName))
5176 return false;
5177 if (fileName.size() > 0)
5178 delType = DEL_FILE;
5179 if (!parent.getAttribute(elem, "dir", dirName))
5180 return false;
5181 if (dirName.size() > 0)
5182 delType = DEL_DIR;
5183 if (fileName.size()>0 && dirName.size()>0)
5184 {
5185 error("<delete> can only have one attribute of file= or dir=");
5186 return false;
5187 }
5188 String ret;
5189 if (!parent.getAttribute(elem, "verbose", ret))
5190 return false;
5191 if (ret.size()>0 && !getBool(ret, verbose))
5192 return false;
5193 if (!parent.getAttribute(elem, "quiet", ret))
5194 return false;
5195 if (ret.size()>0 && !getBool(ret, quiet))
5196 return false;
5197 if (!parent.getAttribute(elem, "failonerror", ret))
5198 return false;
5199 if (ret.size()>0 && !getBool(ret, failOnError))
5200 return false;
5201 return true;
5202 }
5204 private:
5206 int delType;
5207 String dirName;
5208 String fileName;
5209 bool verbose;
5210 bool quiet;
5211 bool failOnError;
5212 };
5215 /**
5216 *
5217 */
5218 class TaskJar : public Task
5219 {
5220 public:
5222 TaskJar(MakeBase &par) : Task(par)
5223 { type = TASK_JAR; name = "jar"; }
5225 virtual ~TaskJar()
5226 {}
5228 virtual bool execute()
5229 {
5230 return true;
5231 }
5233 virtual bool parse(Element *elem)
5234 {
5235 return true;
5236 }
5237 };
5240 /**
5241 *
5242 */
5243 class TaskJavac : public Task
5244 {
5245 public:
5247 TaskJavac(MakeBase &par) : Task(par)
5248 { type = TASK_JAVAC; name = "javac"; }
5250 virtual ~TaskJavac()
5251 {}
5253 virtual bool execute()
5254 {
5255 return true;
5256 }
5258 virtual bool parse(Element *elem)
5259 {
5260 return true;
5261 }
5262 };
5265 /**
5266 *
5267 */
5268 class TaskLink : public Task
5269 {
5270 public:
5272 TaskLink(MakeBase &par) : Task(par)
5273 {
5274 type = TASK_LINK; name = "link";
5275 command = "g++";
5276 }
5278 virtual ~TaskLink()
5279 {}
5281 virtual bool execute()
5282 {
5283 bool doit = false;
5284 String fullTarget = parent.resolve(fileName);
5285 String cmd = command;
5286 cmd.append(" -o ");
5287 cmd.append(fullTarget);
5288 cmd.append(" ");
5289 cmd.append(flags);
5290 for (unsigned int i=0 ; i<fileSet.size() ; i++)
5291 {
5292 cmd.append(" ");
5293 String obj;
5294 if (fileSetDir.size()>0)
5295 {
5296 obj.append(fileSetDir);
5297 obj.append("/");
5298 }
5299 obj.append(fileSet[i]);
5300 String fullObj = parent.resolve(obj);
5301 cmd.append(fullObj);
5302 if (isNewerThan(fullObj, fullTarget))
5303 doit = true;
5304 }
5305 cmd.append(" ");
5306 cmd.append(libs);
5307 if (!doit)
5308 {
5309 //trace("link not needed");
5310 return true;
5311 }
5312 //trace("LINK cmd:%s", cmd.c_str());
5315 String outString, errString;
5316 if (!executeCommand(cmd.c_str(), "", outString, errString))
5317 {
5318 error("LINK problem: %s", errString.c_str());
5319 return false;
5320 }
5321 return true;
5322 }
5324 virtual bool parse(Element *elem)
5325 {
5326 if (!parent.getAttribute(elem, "command", command))
5327 return false;
5328 if (!parent.getAttribute(elem, "out", fileName))
5329 return false;
5331 std::vector<Element *> children = elem->getChildren();
5332 for (unsigned int i=0 ; i<children.size() ; i++)
5333 {
5334 Element *child = children[i];
5335 String tagName = child->getName();
5336 if (tagName == "fileset")
5337 {
5338 if (!getFileSet(child, parent, fileSetDir, fileSet))
5339 return false;
5340 }
5341 else if (tagName == "flags")
5342 {
5343 if (!parent.getValue(child, flags))
5344 return false;
5345 }
5346 else if (tagName == "libs")
5347 {
5348 if (!parent.getValue(child, libs))
5349 return false;
5350 }
5351 }
5352 return true;
5353 }
5355 private:
5357 String command;
5358 String fileName;
5359 String flags;
5360 String libs;
5361 String fileSetDir;
5362 std::vector<String> fileSet;
5364 };
5368 /**
5369 * Create a named directory
5370 */
5371 class TaskMkDir : public Task
5372 {
5373 public:
5375 TaskMkDir(MakeBase &par) : Task(par)
5376 { type = TASK_MKDIR; name = "mkdir"; }
5378 virtual ~TaskMkDir()
5379 {}
5381 virtual bool execute()
5382 {
5383 status(" : %s", dirName.c_str());
5384 String fullDir = parent.resolve(dirName);
5385 //trace("fullDir:%s", fullDir.c_str());
5386 if (!createDirectory(fullDir))
5387 return false;
5388 return true;
5389 }
5391 virtual bool parse(Element *elem)
5392 {
5393 if (!parent.getAttribute(elem, "dir", dirName))
5394 return false;
5395 if (dirName.size() == 0)
5396 {
5397 error("<mkdir> requires 'dir=\"dirname\"' attribute");
5398 return false;
5399 }
5400 //trace("dirname:%s", dirName.c_str());
5401 return true;
5402 }
5404 private:
5406 String dirName;
5407 };
5411 /**
5412 * Create a named directory
5413 */
5414 class TaskMsgFmt: public Task
5415 {
5416 public:
5418 TaskMsgFmt(MakeBase &par) : Task(par)
5419 {
5420 type = TASK_MSGFMT;
5421 name = "msgfmt";
5422 command = "msgfmt";
5423 }
5425 virtual ~TaskMsgFmt()
5426 {}
5428 virtual bool execute()
5429 {
5430 //trace("msgfmt: %d", fileSet.size());
5431 for (unsigned int i=0 ; i<fileSet.size() ; i++)
5432 {
5433 String fileName = fileSet[i];
5434 if (getSuffix(fileName) != "po")
5435 continue;
5436 String sourcePath;
5437 if (fileSetDir.size()>0)
5438 {
5439 sourcePath.append(fileSetDir);
5440 sourcePath.append("/");
5441 }
5442 sourcePath.append(fileName);
5443 String fullSource = parent.resolve(sourcePath);
5445 String destPath;
5446 if (toDirName.size()>0)
5447 {
5448 destPath.append(toDirName);
5449 destPath.append("/");
5450 }
5451 destPath.append(fileName);
5452 destPath[destPath.size()-2] = 'm';
5453 String fullDest = parent.resolve(destPath);
5455 if (!isNewerThan(fullSource, fullDest))
5456 {
5457 //trace("skip %s", fullSource.c_str());
5458 continue;
5459 }
5461 String cmd = command;
5462 cmd.append(" ");
5463 cmd.append(fullSource);
5464 cmd.append(" -o ");
5465 cmd.append(fullDest);
5467 int pos = fullDest.find_last_of('/');
5468 if (pos>0)
5469 {
5470 String fullDestPath = fullDest.substr(0, pos);
5471 if (!createDirectory(fullDestPath))
5472 return false;
5473 }
5477 String outString, errString;
5478 if (!executeCommand(cmd.c_str(), "", outString, errString))
5479 {
5480 error("<msgfmt> problem: %s", errString.c_str());
5481 return false;
5482 }
5483 }
5485 return true;
5486 }
5488 virtual bool parse(Element *elem)
5489 {
5490 if (!parent.getAttribute(elem, "todir", toDirName))
5491 return false;
5493 std::vector<Element *> children = elem->getChildren();
5494 for (unsigned int i=0 ; i<children.size() ; i++)
5495 {
5496 Element *child = children[i];
5497 String tagName = child->getName();
5498 if (tagName == "fileset")
5499 {
5500 if (!getFileSet(child, parent, fileSetDir, fileSet))
5501 return false;
5502 }
5503 }
5504 return true;
5505 }
5507 private:
5509 String command;
5510 String toDirName;
5511 String fileSetDir;
5512 std::vector<String> fileSet;
5514 };
5520 /**
5521 * Process an archive to allow random access
5522 */
5523 class TaskRanlib : public Task
5524 {
5525 public:
5527 TaskRanlib(MakeBase &par) : Task(par)
5528 { type = TASK_RANLIB; name = "ranlib"; }
5530 virtual ~TaskRanlib()
5531 {}
5533 virtual bool execute()
5534 {
5535 String fullName = parent.resolve(fileName);
5536 //trace("fullDir:%s", fullDir.c_str());
5537 String cmd = "ranlib ";
5538 cmd.append(fullName);
5539 String outbuf, errbuf;
5540 if (!executeCommand(cmd, "", outbuf, errbuf))
5541 return false;
5542 return true;
5543 }
5545 virtual bool parse(Element *elem)
5546 {
5547 if (!parent.getAttribute(elem, "file", fileName))
5548 return false;
5549 if (fileName.size() == 0)
5550 {
5551 error("<ranlib> requires 'file=\"fileNname\"' attribute");
5552 return false;
5553 }
5554 return true;
5555 }
5557 private:
5559 String fileName;
5560 };
5564 /**
5565 * Run the "ar" command to archive .o's into a .a
5566 */
5567 class TaskRC : public Task
5568 {
5569 public:
5571 TaskRC(MakeBase &par) : Task(par)
5572 {
5573 type = TASK_RC; name = "rc";
5574 command = "windres -o";
5575 }
5577 virtual ~TaskRC()
5578 {}
5580 virtual bool execute()
5581 {
5582 String fullFile = parent.resolve(fileName);
5583 String fullOut = parent.resolve(outName);
5584 if (!isNewerThan(fullFile, fullOut))
5585 return true;
5586 String cmd = command;
5587 cmd.append(" ");
5588 cmd.append(fullOut);
5589 cmd.append(" ");
5590 cmd.append(flags);
5591 cmd.append(" ");
5592 cmd.append(fullFile);
5594 String outString, errString;
5595 if (!executeCommand(cmd.c_str(), "", outString, errString))
5596 {
5597 error("RC problem: %s", errString.c_str());
5598 return false;
5599 }
5600 return true;
5601 }
5603 virtual bool parse(Element *elem)
5604 {
5605 if (!parent.getAttribute(elem, "command", command))
5606 return false;
5607 if (!parent.getAttribute(elem, "file", fileName))
5608 return false;
5609 if (!parent.getAttribute(elem, "out", outName))
5610 return false;
5611 std::vector<Element *> children = elem->getChildren();
5612 for (unsigned int i=0 ; i<children.size() ; i++)
5613 {
5614 Element *child = children[i];
5615 String tagName = child->getName();
5616 if (tagName == "flags")
5617 {
5618 if (!parent.getValue(child, flags))
5619 return false;
5620 }
5621 }
5622 return true;
5623 }
5625 private:
5627 String command;
5628 String flags;
5629 String fileName;
5630 String outName;
5632 };
5636 /**
5637 * Strip an executable
5638 */
5639 class TaskStrip : public Task
5640 {
5641 public:
5643 TaskStrip(MakeBase &par) : Task(par)
5644 { type = TASK_STRIP; name = "strip"; }
5646 virtual ~TaskStrip()
5647 {}
5649 virtual bool execute()
5650 {
5651 String fullName = parent.resolve(fileName);
5652 //trace("fullDir:%s", fullDir.c_str());
5653 String cmd = "strip ";
5654 cmd.append(fullName);
5656 String outbuf, errbuf;
5657 if (!executeCommand(cmd, "", outbuf, errbuf))
5658 return false;
5659 return true;
5660 }
5662 virtual bool parse(Element *elem)
5663 {
5664 if (!parent.getAttribute(elem, "file", fileName))
5665 return false;
5666 if (fileName.size() == 0)
5667 {
5668 error("<strip> requires 'file=\"fileNname\"' attribute");
5669 return false;
5670 }
5671 return true;
5672 }
5674 private:
5676 String fileName;
5677 };
5680 /**
5681 *
5682 */
5683 class TaskTstamp : public Task
5684 {
5685 public:
5687 TaskTstamp(MakeBase &par) : Task(par)
5688 { type = TASK_TSTAMP; name = "tstamp"; }
5690 virtual ~TaskTstamp()
5691 {}
5693 virtual bool execute()
5694 {
5695 return true;
5696 }
5698 virtual bool parse(Element *elem)
5699 {
5700 trace("tstamp parse");
5701 return true;
5702 }
5703 };
5707 /**
5708 *
5709 */
5710 Task *Task::createTask(Element *elem)
5711 {
5712 String tagName = elem->getName();
5713 //trace("task:%s", tagName.c_str());
5714 Task *task = NULL;
5715 if (tagName == "ar")
5716 task = new TaskAr(parent);
5717 else if (tagName == "cc")
5718 task = new TaskCC(parent);
5719 else if (tagName == "copy")
5720 task = new TaskCopy(parent);
5721 else if (tagName == "delete")
5722 task = new TaskDelete(parent);
5723 else if (tagName == "jar")
5724 task = new TaskJar(parent);
5725 else if (tagName == "javac")
5726 task = new TaskJavac(parent);
5727 else if (tagName == "link")
5728 task = new TaskLink(parent);
5729 else if (tagName == "mkdir")
5730 task = new TaskMkDir(parent);
5731 else if (tagName == "msgfmt")
5732 task = new TaskMsgFmt(parent);
5733 else if (tagName == "ranlib")
5734 task = new TaskRanlib(parent);
5735 else if (tagName == "rc")
5736 task = new TaskRC(parent);
5737 else if (tagName == "strip")
5738 task = new TaskStrip(parent);
5739 else if (tagName == "tstamp")
5740 task = new TaskTstamp(parent);
5741 else
5742 {
5743 error("Unknown task '%s'", tagName.c_str());
5744 return NULL;
5745 }
5747 if (!task->parse(elem))
5748 {
5749 delete task;
5750 return NULL;
5751 }
5752 return task;
5753 }
5757 //########################################################################
5758 //# T A R G E T
5759 //########################################################################
5761 /**
5762 *
5763 */
5764 class Target : public MakeBase
5765 {
5767 public:
5769 /**
5770 *
5771 */
5772 Target(Make &par) : parent(par)
5773 { init(); }
5775 /**
5776 *
5777 */
5778 Target(const Target &other) : parent(other.parent)
5779 { init(); assign(other); }
5781 /**
5782 *
5783 */
5784 Target &operator=(const Target &other)
5785 { init(); assign(other); return *this; }
5787 /**
5788 *
5789 */
5790 virtual ~Target()
5791 { cleanup() ; }
5794 /**
5795 *
5796 */
5797 virtual Make &getParent()
5798 { return parent; }
5800 /**
5801 *
5802 */
5803 virtual String getName()
5804 { return name; }
5806 /**
5807 *
5808 */
5809 virtual void setName(const String &val)
5810 { name = val; }
5812 /**
5813 *
5814 */
5815 virtual String getDescription()
5816 { return description; }
5818 /**
5819 *
5820 */
5821 virtual void setDescription(const String &val)
5822 { description = val; }
5824 /**
5825 *
5826 */
5827 virtual void addDependency(const String &val)
5828 { deps.push_back(val); }
5830 /**
5831 *
5832 */
5833 virtual void parseDependencies(const String &val)
5834 { deps = tokenize(val, ", "); }
5836 /**
5837 *
5838 */
5839 virtual std::vector<String> &getDependencies()
5840 { return deps; }
5842 /**
5843 *
5844 */
5845 virtual String getIf()
5846 { return ifVar; }
5848 /**
5849 *
5850 */
5851 virtual void setIf(const String &val)
5852 { ifVar = val; }
5854 /**
5855 *
5856 */
5857 virtual String getUnless()
5858 { return unlessVar; }
5860 /**
5861 *
5862 */
5863 virtual void setUnless(const String &val)
5864 { unlessVar = val; }
5866 /**
5867 *
5868 */
5869 virtual void addTask(Task *val)
5870 { tasks.push_back(val); }
5872 /**
5873 *
5874 */
5875 virtual std::vector<Task *> &getTasks()
5876 { return tasks; }
5878 private:
5880 void init()
5881 {
5882 }
5884 void cleanup()
5885 {
5886 tasks.clear();
5887 }
5889 void assign(const Target &other)
5890 {
5891 //parent = other.parent;
5892 name = other.name;
5893 description = other.description;
5894 ifVar = other.ifVar;
5895 unlessVar = other.unlessVar;
5896 deps = other.deps;
5897 tasks = other.tasks;
5898 }
5900 Make &parent;
5902 String name;
5904 String description;
5906 String ifVar;
5908 String unlessVar;
5910 std::vector<String> deps;
5912 std::vector<Task *> tasks;
5914 };
5923 //########################################################################
5924 //# M A K E
5925 //########################################################################
5928 /**
5929 *
5930 */
5931 class Make : public MakeBase
5932 {
5934 public:
5936 /**
5937 *
5938 */
5939 Make()
5940 { init(); }
5942 /**
5943 *
5944 */
5945 Make(const Make &other)
5946 { assign(other); }
5948 /**
5949 *
5950 */
5951 Make &operator=(const Make &other)
5952 { assign(other); return *this; }
5954 /**
5955 *
5956 */
5957 virtual ~Make()
5958 { cleanup(); }
5960 /**
5961 *
5962 */
5963 virtual std::map<String, Target> &getTargets()
5964 { return targets; }
5967 /**
5968 *
5969 */
5970 bool run();
5972 /**
5973 *
5974 */
5975 bool run(const String &target);
5979 private:
5981 /**
5982 *
5983 */
5984 void init();
5986 /**
5987 *
5988 */
5989 void cleanup();
5991 /**
5992 *
5993 */
5994 void assign(const Make &other);
5996 /**
5997 *
5998 */
5999 bool executeTask(Task &task);
6002 /**
6003 *
6004 */
6005 bool executeTarget(Target &target,
6006 std::set<String> &targetsCompleted);
6009 /**
6010 *
6011 */
6012 bool execute();
6014 /**
6015 *
6016 */
6017 bool checkTargetDependencies(Target &prop,
6018 std::vector<String> &depList);
6020 /**
6021 *
6022 */
6023 bool parsePropertyFile(const String &fileName,
6024 const String &prefix);
6026 /**
6027 *
6028 */
6029 bool parseProperty(Element *elem);
6031 /**
6032 *
6033 */
6034 bool parseTask(Task &task, Element *elem);
6036 /**
6037 *
6038 */
6039 bool parseFile();
6041 /**
6042 *
6043 */
6044 std::vector<String> glob(const String &pattern);
6047 //###############
6048 //# Fields
6049 //###############
6051 String projectName;
6053 String currentTarget;
6055 String defaultTarget;
6057 String specifiedTarget;
6059 String baseDir;
6061 String description;
6063 String envAlias;
6065 //std::vector<Property> properties;
6067 std::map<String, Target> targets;
6069 std::vector<Task *> allTasks;
6072 };
6075 //########################################################################
6076 //# C L A S S M A I N T E N A N C E
6077 //########################################################################
6079 /**
6080 *
6081 */
6082 void Make::init()
6083 {
6084 uri = "build.xml";
6085 projectName = "";
6086 currentTarget = "";
6087 defaultTarget = "";
6088 specifiedTarget = "";
6089 baseDir = "";
6090 description = "";
6091 envAlias = "";
6092 properties.clear();
6093 for (unsigned int i = 0 ; i < allTasks.size() ; i++)
6094 delete allTasks[i];
6095 allTasks.clear();
6096 }
6100 /**
6101 *
6102 */
6103 void Make::cleanup()
6104 {
6105 for (unsigned int i = 0 ; i < allTasks.size() ; i++)
6106 delete allTasks[i];
6107 allTasks.clear();
6108 }
6112 /**
6113 *
6114 */
6115 void Make::assign(const Make &other)
6116 {
6117 uri = other.uri;
6118 projectName = other.projectName;
6119 currentTarget = other.currentTarget;
6120 defaultTarget = other.defaultTarget;
6121 specifiedTarget = other.specifiedTarget;
6122 baseDir = other.baseDir;
6123 description = other.description;
6124 properties = other.properties;
6125 }
6129 //########################################################################
6130 //# U T I L I T Y T A S K S
6131 //########################################################################
6133 /**
6134 * Perform a file globbing
6135 */
6136 std::vector<String> Make::glob(const String &pattern)
6137 {
6138 std::vector<String> res;
6139 return res;
6140 }
6143 //########################################################################
6144 //# P U B L I C A P I
6145 //########################################################################
6149 /**
6150 *
6151 */
6152 bool Make::executeTarget(Target &target,
6153 std::set<String> &targetsCompleted)
6154 {
6156 String name = target.getName();
6158 //First get any dependencies for this target
6159 std::vector<String> deps = target.getDependencies();
6160 for (unsigned int i=0 ; i<deps.size() ; i++)
6161 {
6162 String dep = deps[i];
6163 //Did we do it already? Skip
6164 if (targetsCompleted.find(dep)!=targetsCompleted.end())
6165 continue;
6167 std::map<String, Target> &tgts =
6168 target.getParent().getTargets();
6169 std::map<String, Target>::iterator iter =
6170 tgts.find(dep);
6171 if (iter == tgts.end())
6172 {
6173 error("Target '%s' dependency '%s' not found",
6174 name.c_str(), dep.c_str());
6175 return false;
6176 }
6177 Target depTarget = iter->second;
6178 if (!executeTarget(depTarget, targetsCompleted))
6179 {
6180 return false;
6181 }
6182 }
6184 status("## Target : %s", name.c_str());
6186 //Now let's do the tasks
6187 std::vector<Task *> &tasks = target.getTasks();
6188 for (unsigned int i=0 ; i<tasks.size() ; i++)
6189 {
6190 Task *task = tasks[i];
6191 status("---- task : %s", task->getName().c_str());
6192 if (!task->execute())
6193 {
6194 return false;
6195 }
6196 }
6198 targetsCompleted.insert(name);
6200 return true;
6201 }
6205 /**
6206 * Main execute() method. Start here and work
6207 * up the dependency tree
6208 */
6209 bool Make::execute()
6210 {
6211 status("######## EXECUTE");
6213 //Determine initial target
6214 if (specifiedTarget.size()>0)
6215 {
6216 currentTarget = specifiedTarget;
6217 }
6218 else if (defaultTarget.size()>0)
6219 {
6220 currentTarget = defaultTarget;
6221 }
6222 else
6223 {
6224 error("execute: no specified or default target requested");
6225 return false;
6226 }
6228 std::map<String, Target>::iterator iter =
6229 targets.find(currentTarget);
6230 if (iter == targets.end())
6231 {
6232 error("Initial target '%s' not found",
6233 currentTarget.c_str());
6234 return false;
6235 }
6237 //Now run
6238 Target target = iter->second;
6239 std::set<String> targetsCompleted;
6240 if (!executeTarget(target, targetsCompleted))
6241 {
6242 return false;
6243 }
6245 status("######## EXECUTE COMPLETE");
6246 return true;
6247 }
6252 /**
6253 *
6254 */
6255 bool Make::checkTargetDependencies(Target &target,
6256 std::vector<String> &depList)
6257 {
6258 String tgtName = target.getName().c_str();
6259 depList.push_back(tgtName);
6261 std::vector<String> deps = target.getDependencies();
6262 for (unsigned int i=0 ; i<deps.size() ; i++)
6263 {
6264 String dep = deps[i];
6265 //First thing entered was the starting Target
6266 if (dep == depList[0])
6267 {
6268 error("Circular dependency '%s' found at '%s'",
6269 dep.c_str(), tgtName.c_str());
6270 std::vector<String>::iterator diter;
6271 for (diter=depList.begin() ; diter!=depList.end() ; diter++)
6272 {
6273 error(" %s", diter->c_str());
6274 }
6275 return false;
6276 }
6278 std::map<String, Target> &tgts =
6279 target.getParent().getTargets();
6280 std::map<String, Target>::iterator titer = tgts.find(dep);
6281 if (titer == tgts.end())
6282 {
6283 error("Target '%s' dependency '%s' not found",
6284 tgtName.c_str(), dep.c_str());
6285 return false;
6286 }
6287 if (!checkTargetDependencies(titer->second, depList))
6288 {
6289 return false;
6290 }
6291 }
6292 return true;
6293 }
6299 static int getword(int pos, const String &inbuf, String &result)
6300 {
6301 int p = pos;
6302 int len = (int)inbuf.size();
6303 String val;
6304 while (p < len)
6305 {
6306 char ch = inbuf[p];
6307 if (!isalnum(ch) && ch!='.' && ch!='_')
6308 break;
6309 val.push_back(ch);
6310 p++;
6311 }
6312 result = val;
6313 return p;
6314 }
6319 /**
6320 *
6321 */
6322 bool Make::parsePropertyFile(const String &fileName,
6323 const String &prefix)
6324 {
6325 FILE *f = fopen(fileName.c_str(), "r");
6326 if (!f)
6327 {
6328 error("could not open property file %s", fileName.c_str());
6329 return false;
6330 }
6331 int linenr = 0;
6332 while (!feof(f))
6333 {
6334 char buf[256];
6335 if (!fgets(buf, 255, f))
6336 break;
6337 linenr++;
6338 String s = buf;
6339 s = trim(s);
6340 int len = s.size();
6341 if (len == 0)
6342 continue;
6343 if (s[0] == '#')
6344 continue;
6345 String key;
6346 String val;
6347 int p = 0;
6348 int p2 = getword(p, s, key);
6349 if (p2 <= p)
6350 {
6351 error("property file %s, line %d: expected keyword",
6352 fileName.c_str(), linenr);
6353 return false;
6354 }
6355 if (prefix.size() > 0)
6356 {
6357 key.insert(0, prefix);
6358 }
6360 //skip whitespace
6361 for (p=p2 ; p<len ; p++)
6362 if (!isspace(s[p]))
6363 break;
6365 if (p>=len || s[p]!='=')
6366 {
6367 error("property file %s, line %d: expected '='",
6368 fileName.c_str(), linenr);
6369 return false;
6370 }
6371 p++;
6373 //skip whitespace
6374 for ( ; p<len ; p++)
6375 if (!isspace(s[p]))
6376 break;
6378 /* This way expects a word after the =
6379 p2 = getword(p, s, val);
6380 if (p2 <= p)
6381 {
6382 error("property file %s, line %d: expected value",
6383 fileName.c_str(), linenr);
6384 return false;
6385 }
6386 */
6387 // This way gets the rest of the line after the =
6388 if (p>=len)
6389 {
6390 error("property file %s, line %d: expected value",
6391 fileName.c_str(), linenr);
6392 return false;
6393 }
6394 val = s.substr(p);
6395 if (key.size()==0 || val.size()==0)
6396 continue;
6398 //trace("key:'%s' val:'%s'", key.c_str(), val.c_str());
6399 properties[key] = val;
6400 }
6401 fclose(f);
6402 return true;
6403 }
6408 /**
6409 *
6410 */
6411 bool Make::parseProperty(Element *elem)
6412 {
6413 std::vector<Attribute> &attrs = elem->getAttributes();
6414 for (unsigned int i=0 ; i<attrs.size() ; i++)
6415 {
6416 String attrName = attrs[i].getName();
6417 String attrVal = attrs[i].getValue();
6419 if (attrName == "name")
6420 {
6421 String val;
6422 if (!getAttribute(elem, "value", val))
6423 return false;
6424 if (val.size() > 0)
6425 {
6426 properties[attrVal] = val;
6427 continue;
6428 }
6429 if (!getAttribute(elem, "location", val))
6430 return false;
6431 if (val.size() > 0)
6432 {
6433 //TODO: process a path relative to build.xml
6434 properties[attrVal] = val;
6435 continue;
6436 }
6437 }
6438 else if (attrName == "file")
6439 {
6440 String prefix;
6441 if (!getAttribute(elem, "prefix", prefix))
6442 return false;
6443 if (prefix.size() > 0)
6444 {
6445 if (prefix[prefix.size()-1] != '.')
6446 prefix.push_back('.');
6447 }
6448 if (!parsePropertyFile(attrName, prefix))
6449 return false;
6450 }
6451 else if (attrName == "environment")
6452 {
6453 if (envAlias.size() > 0)
6454 {
6455 error("environment property can only be set once");
6456 return false;
6457 }
6458 envAlias = attrVal;
6459 }
6460 }
6462 return true;
6463 }
6468 /**
6469 *
6470 */
6471 bool Make::parseFile()
6472 {
6473 status("######## PARSE");
6475 Parser parser;
6476 Element *root = parser.parseFile(uri.getNativePath());
6477 if (!root)
6478 {
6479 error("Could not open %s for reading",
6480 uri.getNativePath().c_str());
6481 return false;
6482 }
6484 if (root->getChildren().size()==0 ||
6485 root->getChildren()[0]->getName()!="project")
6486 {
6487 error("Main xml element should be <project>");
6488 delete root;
6489 return false;
6490 }
6492 //########## Project attributes
6493 Element *project = root->getChildren()[0];
6494 String s = project->getAttribute("name");
6495 if (s.size() > 0)
6496 projectName = s;
6497 s = project->getAttribute("default");
6498 if (s.size() > 0)
6499 defaultTarget = s;
6500 s = project->getAttribute("basedir");
6501 if (s.size() > 0)
6502 baseDir = s;
6504 //######### PARSE MEMBERS
6505 std::vector<Element *> children = project->getChildren();
6506 for (unsigned int i=0 ; i<children.size() ; i++)
6507 {
6508 Element *elem = children[i];
6509 String tagName = elem->getName();
6511 //########## DESCRIPTION
6512 if (tagName == "description")
6513 {
6514 description = parser.trim(elem->getValue());
6515 }
6517 //######### PROPERTY
6518 else if (tagName == "property")
6519 {
6520 if (!parseProperty(elem))
6521 return false;
6522 }
6524 //######### TARGET
6525 else if (tagName == "target")
6526 {
6527 String tname = elem->getAttribute("name");
6528 String tdesc = elem->getAttribute("description");
6529 String tdeps = elem->getAttribute("depends");
6530 String tif = elem->getAttribute("if");
6531 String tunless = elem->getAttribute("unless");
6532 Target target(*this);
6533 target.setName(tname);
6534 target.setDescription(tdesc);
6535 target.parseDependencies(tdeps);
6536 target.setIf(tif);
6537 target.setUnless(tunless);
6538 std::vector<Element *> telems = elem->getChildren();
6539 for (unsigned int i=0 ; i<telems.size() ; i++)
6540 {
6541 Element *telem = telems[i];
6542 Task breeder(*this);
6543 Task *task = breeder.createTask(telem);
6544 if (!task)
6545 return false;
6546 allTasks.push_back(task);
6547 target.addTask(task);
6548 }
6550 //Check name
6551 if (tname.size() == 0)
6552 {
6553 error("no name for target");
6554 return false;
6555 }
6556 //Check for duplicate name
6557 if (targets.find(tname) != targets.end())
6558 {
6559 error("target '%s' already defined", tname.c_str());
6560 return false;
6561 }
6562 //more work than targets[tname]=target, but avoids default allocator
6563 targets.insert(std::make_pair<String, Target>(tname, target));
6564 }
6566 }
6568 std::map<String, Target>::iterator iter;
6569 for (iter = targets.begin() ; iter!= targets.end() ; iter++)
6570 {
6571 Target tgt = iter->second;
6572 std::vector<String> depList;
6573 if (!checkTargetDependencies(tgt, depList))
6574 {
6575 return false;
6576 }
6577 }
6580 delete root;
6581 status("######## PARSE COMPLETE");
6582 return true;
6583 }
6586 /**
6587 *
6588 */
6589 bool Make::run()
6590 {
6591 if (!parseFile())
6592 return false;
6593 if (!execute())
6594 return false;
6595 return true;
6596 }
6599 /**
6600 *
6601 */
6602 bool Make::run(const String &target)
6603 {
6604 status("##################################");
6605 status("# BuildTool");
6606 status("# version 0.2");
6607 status("##################################");
6608 specifiedTarget = target;
6609 if (!run())
6610 return false;
6611 status("##################################");
6612 status("# BuildTool Completed");
6613 status("##################################");
6614 return true;
6615 }
6623 }// namespace buildtool
6624 //########################################################################
6625 //# M A I N
6626 //########################################################################
6628 /**
6629 * Format an error message in printf() style
6630 */
6631 static void error(char *fmt, ...)
6632 {
6633 va_list ap;
6634 va_start(ap, fmt);
6635 fprintf(stderr, "BuildTool error: ");
6636 vfprintf(stderr, fmt, ap);
6637 fprintf(stderr, "\n");
6638 va_end(ap);
6639 }
6642 /**
6643 * Compare a buffer with a key, for the length of the key
6644 */
6645 static bool sequ(const buildtool::String &buf, char *key)
6646 {
6647 for (int i=0 ; key[i] ; i++)
6648 {
6649 if (key[i] != buf[i])
6650 return false;
6651 }
6652 return true;
6653 }
6655 /**
6656 * Parse the command-line args, get our options,
6657 * and run this thing
6658 */
6659 static bool parseOptions(int argc, char **argv)
6660 {
6661 if (argc < 1)
6662 {
6663 error("Cannot parse arguments");
6664 return false;
6665 }
6667 buildtool::String buildFile;
6668 buildtool::String target;
6670 //char *progName = argv[0];
6671 for (int i=1 ; i<argc ; i++)
6672 {
6673 buildtool::String arg = argv[i];
6674 if (sequ(arg, "--"))
6675 {
6676 if (sequ(arg, "--file=") && arg.size()>7)
6677 {
6678 buildFile = arg.substr(7, arg.size()-7);
6679 }
6680 else
6681 {
6682 error("Unknown option:%s", arg.c_str());
6683 return false;
6684 }
6685 }
6686 else if (sequ(arg, "-"))
6687 {
6688 for (unsigned int p=1 ; p<arg.size() ; p++)
6689 {
6690 int ch = arg[p];
6691 if (0)//put options here
6692 {
6693 }
6694 else
6695 {
6696 error("Unknown option '%c'", ch);
6697 return false;
6698 }
6699 }
6700 }
6701 else
6702 {
6703 target = arg;
6704 }
6705 }
6707 //We have the options. Now execute them
6708 buildtool::Make make;
6709 if (buildFile.size() > 0)
6710 {
6711 make.setURI(buildFile);
6712 }
6713 if (!make.run(target))
6714 return false;
6716 return true;
6717 }
6722 /*
6723 static bool runMake()
6724 {
6725 buildtool::Make make;
6726 if (!make.run())
6727 return false;
6728 return true;
6729 }
6732 static bool pkgConfigTest()
6733 {
6734 buildtool::PkgConfig pkgConfig;
6735 if (!pkgConfig.readFile("gtk+-2.0.pc"))
6736 return false;
6737 return true;
6738 }
6742 static bool depTest()
6743 {
6744 buildtool::DepTool deptool;
6745 deptool.setSourceDirectory("/dev/ink/inkscape/src");
6746 if (!deptool.generateDependencies("build.dep"))
6747 return false;
6748 std::vector<buildtool::DepRec> res =
6749 deptool.loadDepFile("build.dep");
6750 if (res.size() == 0)
6751 return false;
6752 return true;
6753 }
6755 static bool popenTest()
6756 {
6757 buildtool::Make make;
6758 buildtool::String out, err;
6759 bool ret = make.executeCommand("gcc xx.cpp", "", out, err);
6760 printf("Popen test:%d '%s' '%s'\n", ret, out.c_str(), err.c_str());
6761 return true;
6762 }
6765 static bool propFileTest()
6766 {
6767 buildtool::Make make;
6768 make.parsePropertyFile("test.prop", "test.");
6769 return true;
6770 }
6771 */
6773 int main(int argc, char **argv)
6774 {
6776 if (!parseOptions(argc, argv))
6777 return 1;
6778 /*
6779 if (!popenTest())
6780 return 1;
6782 if (!depTest())
6783 return 1;
6784 if (!propFileTest())
6785 return 1;
6786 if (runMake())
6787 return 1;
6788 */
6789 return 0;
6790 }
6793 //########################################################################
6794 //# E N D
6795 //########################################################################