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 if (iter->path > depObject.path)
4420 {
4421 inserted = true;
4422 iter = result.insert(iter, depObject);
4423 break;
4424 }
4425 else if (iter->path == depObject.path)
4426 {
4427 if (iter->name > depObject.name)
4428 {
4429 inserted = true;
4430 iter = result.insert(iter, depObject);
4431 break;
4432 }
4433 }
4434 }
4435 if (!inserted)
4436 result.push_back(depObject);
4437 }
4438 }
4440 delete root;
4442 return result;
4443 }
4446 /**
4447 * This loads the dependency cache.
4448 */
4449 std::vector<DepRec> DepTool::getDepFile(const String &depFile)
4450 {
4451 std::vector<DepRec> result = loadDepFile(depFile);
4452 if (result.size() == 0)
4453 {
4454 generateDependencies(depFile);
4455 result = loadDepFile(depFile);
4456 }
4457 return result;
4458 }
4463 //########################################################################
4464 //# T A S K
4465 //########################################################################
4466 //forward decl
4467 class Target;
4468 class Make;
4470 /**
4471 *
4472 */
4473 class Task : public MakeBase
4474 {
4476 public:
4478 typedef enum
4479 {
4480 TASK_NONE,
4481 TASK_AR,
4482 TASK_CC,
4483 TASK_COPY,
4484 TASK_DELETE,
4485 TASK_JAR,
4486 TASK_JAVAC,
4487 TASK_LINK,
4488 TASK_MKDIR,
4489 TASK_MSGFMT,
4490 TASK_RANLIB,
4491 TASK_RC,
4492 TASK_STRIP,
4493 TASK_TSTAMP
4494 } TaskType;
4497 /**
4498 *
4499 */
4500 Task(MakeBase &par) : parent(par)
4501 { init(); }
4503 /**
4504 *
4505 */
4506 Task(const Task &other) : parent(other.parent)
4507 { init(); assign(other); }
4509 /**
4510 *
4511 */
4512 Task &operator=(const Task &other)
4513 { assign(other); return *this; }
4515 /**
4516 *
4517 */
4518 virtual ~Task()
4519 { }
4522 /**
4523 *
4524 */
4525 virtual MakeBase &getParent()
4526 { return parent; }
4528 /**
4529 *
4530 */
4531 virtual int getType()
4532 { return type; }
4534 /**
4535 *
4536 */
4537 virtual void setType(int val)
4538 { type = val; }
4540 /**
4541 *
4542 */
4543 virtual String getName()
4544 { return name; }
4546 /**
4547 *
4548 */
4549 virtual bool execute()
4550 { return true; }
4552 /**
4553 *
4554 */
4555 virtual bool parse(Element *elem)
4556 { return true; }
4558 /**
4559 *
4560 */
4561 Task *createTask(Element *elem);
4564 protected:
4566 void init()
4567 {
4568 type = TASK_NONE;
4569 name = "none";
4570 }
4572 void assign(const Task &other)
4573 {
4574 type = other.type;
4575 name = other.name;
4576 }
4578 String getAttribute(Element *elem, const String &attrName)
4579 {
4580 String str;
4581 return str;
4582 }
4584 MakeBase &parent;
4586 int type;
4588 String name;
4589 };
4594 /**
4595 * Run the "ar" command to archive .o's into a .a
4596 */
4597 class TaskAr : public Task
4598 {
4599 public:
4601 TaskAr(MakeBase &par) : Task(par)
4602 {
4603 type = TASK_AR; name = "ar";
4604 command = "ar crv";
4605 }
4607 virtual ~TaskAr()
4608 {}
4610 virtual bool execute()
4611 {
4612 //trace("###########HERE %d", fileSet.size());
4613 bool doit = false;
4615 String fullOut = parent.resolve(fileName);
4616 //trace("ar fullout: %s", fullOut.c_str());
4619 for (unsigned int i=0 ; i<fileSet.size() ; i++)
4620 {
4621 String fname;
4622 if (fileSetDir.size()>0)
4623 {
4624 fname.append(fileSetDir);
4625 fname.append("/");
4626 }
4627 fname.append(fileSet[i]);
4628 String fullName = parent.resolve(fname);
4629 //trace("ar : %s/%s", fullOut.c_str(), fullName.c_str());
4630 if (isNewerThan(fullName, fullOut))
4631 doit = true;
4632 }
4633 //trace("Needs it:%d", doit);
4634 if (!doit)
4635 {
4636 return true;
4637 }
4639 String cmd = command;
4640 cmd.append(" ");
4641 cmd.append(fullOut);
4642 for (unsigned int i=0 ; i<fileSet.size() ; i++)
4643 {
4644 String fname;
4645 if (fileSetDir.size()>0)
4646 {
4647 fname.append(fileSetDir);
4648 fname.append("/");
4649 }
4650 fname.append(fileSet[i]);
4651 String fullName = parent.resolve(fname);
4653 cmd.append(" ");
4654 cmd.append(fullName);
4655 }
4657 String outString, errString;
4658 if (!executeCommand(cmd.c_str(), "", outString, errString))
4659 {
4660 error("AR problem: %s", errString.c_str());
4661 return false;
4662 }
4664 return true;
4665 }
4667 virtual bool parse(Element *elem)
4668 {
4669 if (!parent.getAttribute(elem, "file", fileName))
4670 return false;
4672 std::vector<Element *> children = elem->getChildren();
4673 for (unsigned int i=0 ; i<children.size() ; i++)
4674 {
4675 Element *child = children[i];
4676 String tagName = child->getName();
4677 if (tagName == "fileset")
4678 {
4679 if (!getFileSet(child, parent, fileSetDir, fileSet))
4680 return false;
4681 }
4682 }
4683 return true;
4684 }
4686 private:
4688 String command;
4689 String fileName;
4690 String fileSetDir;
4691 std::vector<String> fileSet;
4693 };
4696 /**
4697 * This task runs the C/C++ compiler. The compiler is invoked
4698 * for all .c or .cpp files which are newer than their correcsponding
4699 * .o files.
4700 */
4701 class TaskCC : public Task
4702 {
4703 public:
4705 TaskCC(MakeBase &par) : Task(par)
4706 {
4707 type = TASK_CC; name = "cc";
4708 ccCommand = "gcc";
4709 cxxCommand = "g++";
4710 source = ".";
4711 dest = ".";
4712 flags = "";
4713 defines = "";
4714 includes = "";
4715 sourceFiles.clear();
4716 }
4718 virtual ~TaskCC()
4719 {}
4721 virtual bool execute()
4722 {
4723 DepTool depTool;
4724 depTool.setSourceDirectory(source);
4725 depTool.setFileList(sourceFiles);
4726 std::vector<DepRec> deps = depTool.getDepFile("build.dep");
4728 String incs;
4729 incs.append("-I");
4730 incs.append(parent.resolve("."));
4731 incs.append(" ");
4732 if (includes.size()>0)
4733 {
4734 incs.append(includes);
4735 incs.append(" ");
4736 }
4737 std::set<String> paths;
4738 std::vector<DepRec>::iterator viter;
4739 for (viter=deps.begin() ; viter!=deps.end() ; viter++)
4740 {
4741 DepRec dep = *viter;
4742 if (dep.path.size()>0)
4743 paths.insert(dep.path);
4744 }
4745 if (source.size()>0)
4746 {
4747 incs.append(" -I");
4748 incs.append(parent.resolve(source));
4749 incs.append(" ");
4750 }
4751 std::set<String>::iterator setIter;
4752 for (setIter=paths.begin() ; setIter!=paths.end() ; setIter++)
4753 {
4754 incs.append(" -I");
4755 String dname;
4756 if (source.size()>0)
4757 {
4758 dname.append(source);
4759 dname.append("/");
4760 }
4761 dname.append(*setIter);
4762 incs.append(parent.resolve(dname));
4763 }
4764 std::vector<String> cfiles;
4765 for (viter=deps.begin() ; viter!=deps.end() ; viter++)
4766 {
4767 DepRec dep = *viter;
4769 //## Select command
4770 String sfx = dep.suffix;
4771 String command = ccCommand;
4772 if (sfx == "cpp" || sfx == "c++" || sfx == "cc"
4773 || sfx == "CC")
4774 command = cxxCommand;
4776 //## Make paths
4777 String destPath = dest;
4778 String srcPath = source;
4779 if (dep.path.size()>0)
4780 {
4781 destPath.append("/");
4782 destPath.append(dep.path);
4783 srcPath.append("/");
4784 srcPath.append(dep.path);
4785 }
4786 //## Make sure destination directory exists
4787 if (!createDirectory(destPath))
4788 return false;
4790 //## Check whether it needs to be done
4791 String destFullName = destPath;
4792 destFullName.append("/");
4793 destFullName.append(dep.name);
4794 destFullName.append(".o");
4795 String srcFullName = srcPath;
4796 srcFullName.append("/");
4797 srcFullName.append(dep.name);
4798 srcFullName.append(".");
4799 srcFullName.append(dep.suffix);
4800 if (!isNewerThan(srcFullName, destFullName))
4801 {
4802 //trace("%s skipped", srcFullName.c_str());
4803 continue;
4804 }
4806 //## Assemble the command
4807 String cmd = command;
4808 cmd.append(" -c ");
4809 cmd.append(flags);
4810 cmd.append(" ");
4811 cmd.append(defines);
4812 cmd.append(" ");
4813 cmd.append(incs);
4814 cmd.append(" ");
4815 cmd.append(srcFullName);
4816 cmd.append(" -o ");
4817 cmd.append(destFullName);
4819 //## Execute the command
4821 String outString, errString;
4822 if (!executeCommand(cmd.c_str(), "", outString, errString))
4823 {
4824 error("problem compiling: %s", errString.c_str());
4825 return false;
4826 }
4827 }
4829 return true;
4830 }
4832 virtual bool parse(Element *elem)
4833 {
4834 String s;
4835 if (!parent.getAttribute(elem, "command", s))
4836 return false;
4837 if (s.size()>0) { ccCommand = s; cxxCommand = s; }
4838 if (!parent.getAttribute(elem, "cc", s))
4839 return false;
4840 if (s.size()>0) ccCommand = s;
4841 if (!parent.getAttribute(elem, "cxx", s))
4842 return false;
4843 if (s.size()>0) cxxCommand = s;
4844 if (!parent.getAttribute(elem, "destdir", s))
4845 return false;
4846 if (s.size()>0) dest = s;
4848 std::vector<Element *> children = elem->getChildren();
4849 for (unsigned int i=0 ; i<children.size() ; i++)
4850 {
4851 Element *child = children[i];
4852 String tagName = child->getName();
4853 if (tagName == "flags")
4854 {
4855 if (!parent.getValue(child, flags))
4856 return false;
4857 }
4858 else if (tagName == "includes")
4859 {
4860 if (!parent.getValue(child, includes))
4861 return false;
4862 }
4863 else if (tagName == "defines")
4864 {
4865 if (!parent.getValue(child, defines))
4866 return false;
4867 }
4868 else if (tagName == "fileset")
4869 {
4870 if (!getFileSet(child, parent, source, sourceFiles))
4871 return false;
4872 }
4873 }
4875 return true;
4876 }
4878 protected:
4880 String ccCommand;
4881 String cxxCommand;
4882 String source;
4883 String dest;
4884 String flags;
4885 String defines;
4886 String includes;
4887 std::vector<String> sourceFiles;
4889 };
4893 /**
4894 *
4895 */
4896 class TaskCopy : public Task
4897 {
4898 public:
4900 typedef enum
4901 {
4902 CP_NONE,
4903 CP_TOFILE,
4904 CP_TODIR
4905 } CopyType;
4907 TaskCopy(MakeBase &par) : Task(par)
4908 {
4909 type = TASK_COPY; name = "copy";
4910 cptype = CP_NONE;
4911 verbose = false;
4912 haveFileSet = false;
4913 }
4915 virtual ~TaskCopy()
4916 {}
4918 virtual bool execute()
4919 {
4920 switch (cptype)
4921 {
4922 case CP_TOFILE:
4923 {
4924 if (fileName.size()>0)
4925 {
4926 status(" : %s", fileName.c_str());
4927 String fullSource = parent.resolve(fileName);
4928 String fullDest = parent.resolve(toFileName);
4929 //trace("copy %s to file %s", fullSource.c_str(),
4930 // fullDest.c_str());
4931 if (!isNewerThan(fullSource, fullDest))
4932 {
4933 return true;
4934 }
4935 if (!copyFile(fullSource, fullDest))
4936 return false;
4937 status(" : 1 file copied");
4938 }
4939 return true;
4940 }
4941 case CP_TODIR:
4942 {
4943 if (haveFileSet)
4944 {
4945 int nrFiles = 0;
4946 status(" : %s", fileSetDir.c_str());
4947 for (unsigned int i=0 ; i<fileSet.size() ; i++)
4948 {
4949 String fileName = fileSet[i];
4951 String sourcePath;
4952 if (fileSetDir.size()>0)
4953 {
4954 sourcePath.append(fileSetDir);
4955 sourcePath.append("/");
4956 }
4957 sourcePath.append(fileName);
4958 String fullSource = parent.resolve(sourcePath);
4960 //Get the immediate parent directory's base name
4961 String baseFileSetDir = fileSetDir;
4962 unsigned int pos = baseFileSetDir.find_last_of('/');
4963 if (pos!=baseFileSetDir.npos &&
4964 pos < baseFileSetDir.size()-1)
4965 baseFileSetDir =
4966 baseFileSetDir.substr(pos+1,
4967 baseFileSetDir.size());
4968 //Now make the new path
4969 String destPath;
4970 if (toDirName.size()>0)
4971 {
4972 destPath.append(toDirName);
4973 destPath.append("/");
4974 }
4975 if (baseFileSetDir.size()>0)
4976 {
4977 destPath.append(baseFileSetDir);
4978 destPath.append("/");
4979 }
4980 destPath.append(fileName);
4981 String fullDest = parent.resolve(destPath);
4982 //trace("fileName:%s", fileName.c_str());
4983 //trace("copy %s to new dir : %s", fullSource.c_str(),
4984 // fullDest.c_str());
4985 if (!isNewerThan(fullSource, fullDest))
4986 {
4987 //trace("copy skipping %s", fullSource.c_str());
4988 continue;
4989 }
4990 if (!copyFile(fullSource, fullDest))
4991 return false;
4992 nrFiles++;
4993 }
4994 status(" : %d file(s) copied", nrFiles);
4995 }
4996 else //file source
4997 {
4998 //For file->dir we want only the basename of
4999 //the source appended to the dest dir
5000 status(" : %s", fileName.c_str());
5001 String baseName = fileName;
5002 unsigned int pos = baseName.find_last_of('/');
5003 if (pos!=baseName.npos && pos<baseName.size()-1)
5004 baseName = baseName.substr(pos+1, baseName.size());
5005 String fullSource = parent.resolve(fileName);
5006 String destPath;
5007 if (toDirName.size()>0)
5008 {
5009 destPath.append(toDirName);
5010 destPath.append("/");
5011 }
5012 destPath.append(baseName);
5013 String fullDest = parent.resolve(destPath);
5014 //trace("copy %s to new dir : %s", fullSource.c_str(),
5015 // fullDest.c_str());
5016 if (!isNewerThan(fullSource, fullDest))
5017 {
5018 return true;
5019 }
5020 if (!copyFile(fullSource, fullDest))
5021 return false;
5022 status(" : 1 file copied");
5023 }
5024 return true;
5025 }
5026 }
5027 return true;
5028 }
5031 virtual bool parse(Element *elem)
5032 {
5033 if (!parent.getAttribute(elem, "file", fileName))
5034 return false;
5035 if (!parent.getAttribute(elem, "tofile", toFileName))
5036 return false;
5037 if (toFileName.size() > 0)
5038 cptype = CP_TOFILE;
5039 if (!parent.getAttribute(elem, "todir", toDirName))
5040 return false;
5041 if (toDirName.size() > 0)
5042 cptype = CP_TODIR;
5043 String ret;
5044 if (!parent.getAttribute(elem, "verbose", ret))
5045 return false;
5046 if (ret.size()>0 && !getBool(ret, verbose))
5047 return false;
5049 haveFileSet = false;
5051 std::vector<Element *> children = elem->getChildren();
5052 for (unsigned int i=0 ; i<children.size() ; i++)
5053 {
5054 Element *child = children[i];
5055 String tagName = child->getName();
5056 if (tagName == "fileset")
5057 {
5058 if (!getFileSet(child, parent, fileSetDir, fileSet))
5059 {
5060 error("problem getting fileset");
5061 return false;
5062 }
5063 haveFileSet = true;
5064 }
5065 }
5067 //Perform validity checks
5068 if (fileName.size()>0 && fileSet.size()>0)
5069 {
5070 error("<copy> can only have one of : file= and <fileset>");
5071 return false;
5072 }
5073 if (toFileName.size()>0 && toDirName.size()>0)
5074 {
5075 error("<copy> can only have one of : tofile= or todir=");
5076 return false;
5077 }
5078 if (haveFileSet && toDirName.size()==0)
5079 {
5080 error("a <copy> task with a <fileset> must have : todir=");
5081 return false;
5082 }
5083 if (cptype == CP_TOFILE && fileName.size()==0)
5084 {
5085 error("<copy> tofile= must be associated with : file=");
5086 return false;
5087 }
5088 if (cptype == CP_TODIR && fileName.size()==0 && !haveFileSet)
5089 {
5090 error("<copy> todir= must be associated with : file= or <fileset>");
5091 return false;
5092 }
5094 return true;
5095 }
5097 private:
5099 int cptype;
5100 String fileName;
5101 String fileSetDir;
5102 std::vector<String> fileSet;
5103 String toFileName;
5104 String toDirName;
5105 bool verbose;
5106 bool haveFileSet;
5107 };
5110 /**
5111 *
5112 */
5113 class TaskDelete : public Task
5114 {
5115 public:
5117 typedef enum
5118 {
5119 DEL_FILE,
5120 DEL_DIR,
5121 DEL_FILESET
5122 } DeleteType;
5124 TaskDelete(MakeBase &par) : Task(par)
5125 {
5126 type = TASK_DELETE;
5127 name = "delete";
5128 delType = DEL_FILE;
5129 verbose = false;
5130 quiet = false;
5131 failOnError = true;
5132 }
5134 virtual ~TaskDelete()
5135 {}
5137 virtual bool execute()
5138 {
5139 struct stat finfo;
5140 switch (delType)
5141 {
5142 case DEL_FILE:
5143 {
5144 status(" : %s", fileName.c_str());
5145 String fullName = parent.resolve(fileName);
5146 char *fname = (char *)fullName.c_str();
5147 //does not exist
5148 if (stat(fname, &finfo)<0)
5149 return true;
5150 //exists but is not a regular file
5151 if (!S_ISREG(finfo.st_mode))
5152 {
5153 error("<delete> failed. '%s' exists and is not a regular file",
5154 fname);
5155 return false;
5156 }
5157 if (remove(fname)<0)
5158 {
5159 error("<delete> failed: %s", strerror(errno));
5160 return false;
5161 }
5162 return true;
5163 }
5164 case DEL_DIR:
5165 {
5166 status(" : %s", dirName.c_str());
5167 String fullDir = parent.resolve(dirName);
5168 if (!removeDirectory(fullDir))
5169 return false;
5170 return true;
5171 }
5172 }
5173 return true;
5174 }
5176 virtual bool parse(Element *elem)
5177 {
5178 if (!parent.getAttribute(elem, "file", fileName))
5179 return false;
5180 if (fileName.size() > 0)
5181 delType = DEL_FILE;
5182 if (!parent.getAttribute(elem, "dir", dirName))
5183 return false;
5184 if (dirName.size() > 0)
5185 delType = DEL_DIR;
5186 if (fileName.size()>0 && dirName.size()>0)
5187 {
5188 error("<delete> can only have one attribute of file= or dir=");
5189 return false;
5190 }
5191 String ret;
5192 if (!parent.getAttribute(elem, "verbose", ret))
5193 return false;
5194 if (ret.size()>0 && !getBool(ret, verbose))
5195 return false;
5196 if (!parent.getAttribute(elem, "quiet", ret))
5197 return false;
5198 if (ret.size()>0 && !getBool(ret, quiet))
5199 return false;
5200 if (!parent.getAttribute(elem, "failonerror", ret))
5201 return false;
5202 if (ret.size()>0 && !getBool(ret, failOnError))
5203 return false;
5204 return true;
5205 }
5207 private:
5209 int delType;
5210 String dirName;
5211 String fileName;
5212 bool verbose;
5213 bool quiet;
5214 bool failOnError;
5215 };
5218 /**
5219 *
5220 */
5221 class TaskJar : public Task
5222 {
5223 public:
5225 TaskJar(MakeBase &par) : Task(par)
5226 { type = TASK_JAR; name = "jar"; }
5228 virtual ~TaskJar()
5229 {}
5231 virtual bool execute()
5232 {
5233 return true;
5234 }
5236 virtual bool parse(Element *elem)
5237 {
5238 return true;
5239 }
5240 };
5243 /**
5244 *
5245 */
5246 class TaskJavac : public Task
5247 {
5248 public:
5250 TaskJavac(MakeBase &par) : Task(par)
5251 { type = TASK_JAVAC; name = "javac"; }
5253 virtual ~TaskJavac()
5254 {}
5256 virtual bool execute()
5257 {
5258 return true;
5259 }
5261 virtual bool parse(Element *elem)
5262 {
5263 return true;
5264 }
5265 };
5268 /**
5269 *
5270 */
5271 class TaskLink : public Task
5272 {
5273 public:
5275 TaskLink(MakeBase &par) : Task(par)
5276 {
5277 type = TASK_LINK; name = "link";
5278 command = "g++";
5279 }
5281 virtual ~TaskLink()
5282 {}
5284 virtual bool execute()
5285 {
5286 bool doit = false;
5287 String fullTarget = parent.resolve(fileName);
5288 String cmd = command;
5289 cmd.append(" -o ");
5290 cmd.append(fullTarget);
5291 cmd.append(" ");
5292 cmd.append(flags);
5293 for (unsigned int i=0 ; i<fileSet.size() ; i++)
5294 {
5295 cmd.append(" ");
5296 String obj;
5297 if (fileSetDir.size()>0)
5298 {
5299 obj.append(fileSetDir);
5300 obj.append("/");
5301 }
5302 obj.append(fileSet[i]);
5303 String fullObj = parent.resolve(obj);
5304 cmd.append(fullObj);
5305 if (isNewerThan(fullObj, fullTarget))
5306 doit = true;
5307 }
5308 cmd.append(" ");
5309 cmd.append(libs);
5310 if (!doit)
5311 {
5312 //trace("link not needed");
5313 return true;
5314 }
5315 //trace("LINK cmd:%s", cmd.c_str());
5318 String outString, errString;
5319 if (!executeCommand(cmd.c_str(), "", outString, errString))
5320 {
5321 error("LINK problem: %s", errString.c_str());
5322 return false;
5323 }
5324 return true;
5325 }
5327 virtual bool parse(Element *elem)
5328 {
5329 if (!parent.getAttribute(elem, "command", command))
5330 return false;
5331 if (!parent.getAttribute(elem, "out", fileName))
5332 return false;
5334 std::vector<Element *> children = elem->getChildren();
5335 for (unsigned int i=0 ; i<children.size() ; i++)
5336 {
5337 Element *child = children[i];
5338 String tagName = child->getName();
5339 if (tagName == "fileset")
5340 {
5341 if (!getFileSet(child, parent, fileSetDir, fileSet))
5342 return false;
5343 }
5344 else if (tagName == "flags")
5345 {
5346 if (!parent.getValue(child, flags))
5347 return false;
5348 }
5349 else if (tagName == "libs")
5350 {
5351 if (!parent.getValue(child, libs))
5352 return false;
5353 }
5354 }
5355 return true;
5356 }
5358 private:
5360 String command;
5361 String fileName;
5362 String flags;
5363 String libs;
5364 String fileSetDir;
5365 std::vector<String> fileSet;
5367 };
5371 /**
5372 * Create a named directory
5373 */
5374 class TaskMkDir : public Task
5375 {
5376 public:
5378 TaskMkDir(MakeBase &par) : Task(par)
5379 { type = TASK_MKDIR; name = "mkdir"; }
5381 virtual ~TaskMkDir()
5382 {}
5384 virtual bool execute()
5385 {
5386 status(" : %s", dirName.c_str());
5387 String fullDir = parent.resolve(dirName);
5388 //trace("fullDir:%s", fullDir.c_str());
5389 if (!createDirectory(fullDir))
5390 return false;
5391 return true;
5392 }
5394 virtual bool parse(Element *elem)
5395 {
5396 if (!parent.getAttribute(elem, "dir", dirName))
5397 return false;
5398 if (dirName.size() == 0)
5399 {
5400 error("<mkdir> requires 'dir=\"dirname\"' attribute");
5401 return false;
5402 }
5403 //trace("dirname:%s", dirName.c_str());
5404 return true;
5405 }
5407 private:
5409 String dirName;
5410 };
5414 /**
5415 * Create a named directory
5416 */
5417 class TaskMsgFmt: public Task
5418 {
5419 public:
5421 TaskMsgFmt(MakeBase &par) : Task(par)
5422 {
5423 type = TASK_MSGFMT;
5424 name = "msgfmt";
5425 command = "msgfmt";
5426 }
5428 virtual ~TaskMsgFmt()
5429 {}
5431 virtual bool execute()
5432 {
5433 //trace("msgfmt: %d", fileSet.size());
5434 for (unsigned int i=0 ; i<fileSet.size() ; i++)
5435 {
5436 String fileName = fileSet[i];
5437 if (getSuffix(fileName) != "po")
5438 continue;
5439 String sourcePath;
5440 if (fileSetDir.size()>0)
5441 {
5442 sourcePath.append(fileSetDir);
5443 sourcePath.append("/");
5444 }
5445 sourcePath.append(fileName);
5446 String fullSource = parent.resolve(sourcePath);
5448 String destPath;
5449 if (toDirName.size()>0)
5450 {
5451 destPath.append(toDirName);
5452 destPath.append("/");
5453 }
5454 destPath.append(fileName);
5455 destPath[destPath.size()-2] = 'm';
5456 String fullDest = parent.resolve(destPath);
5458 if (!isNewerThan(fullSource, fullDest))
5459 {
5460 //trace("skip %s", fullSource.c_str());
5461 continue;
5462 }
5464 String cmd = command;
5465 cmd.append(" ");
5466 cmd.append(fullSource);
5467 cmd.append(" -o ");
5468 cmd.append(fullDest);
5470 int pos = fullDest.find_last_of('/');
5471 if (pos>0)
5472 {
5473 String fullDestPath = fullDest.substr(0, pos);
5474 if (!createDirectory(fullDestPath))
5475 return false;
5476 }
5480 String outString, errString;
5481 if (!executeCommand(cmd.c_str(), "", outString, errString))
5482 {
5483 error("<msgfmt> problem: %s", errString.c_str());
5484 return false;
5485 }
5486 }
5488 return true;
5489 }
5491 virtual bool parse(Element *elem)
5492 {
5493 if (!parent.getAttribute(elem, "todir", toDirName))
5494 return false;
5496 std::vector<Element *> children = elem->getChildren();
5497 for (unsigned int i=0 ; i<children.size() ; i++)
5498 {
5499 Element *child = children[i];
5500 String tagName = child->getName();
5501 if (tagName == "fileset")
5502 {
5503 if (!getFileSet(child, parent, fileSetDir, fileSet))
5504 return false;
5505 }
5506 }
5507 return true;
5508 }
5510 private:
5512 String command;
5513 String toDirName;
5514 String fileSetDir;
5515 std::vector<String> fileSet;
5517 };
5523 /**
5524 * Process an archive to allow random access
5525 */
5526 class TaskRanlib : public Task
5527 {
5528 public:
5530 TaskRanlib(MakeBase &par) : Task(par)
5531 { type = TASK_RANLIB; name = "ranlib"; }
5533 virtual ~TaskRanlib()
5534 {}
5536 virtual bool execute()
5537 {
5538 String fullName = parent.resolve(fileName);
5539 //trace("fullDir:%s", fullDir.c_str());
5540 String cmd = "ranlib ";
5541 cmd.append(fullName);
5542 String outbuf, errbuf;
5543 if (!executeCommand(cmd, "", outbuf, errbuf))
5544 return false;
5545 return true;
5546 }
5548 virtual bool parse(Element *elem)
5549 {
5550 if (!parent.getAttribute(elem, "file", fileName))
5551 return false;
5552 if (fileName.size() == 0)
5553 {
5554 error("<ranlib> requires 'file=\"fileNname\"' attribute");
5555 return false;
5556 }
5557 return true;
5558 }
5560 private:
5562 String fileName;
5563 };
5567 /**
5568 * Run the "ar" command to archive .o's into a .a
5569 */
5570 class TaskRC : public Task
5571 {
5572 public:
5574 TaskRC(MakeBase &par) : Task(par)
5575 {
5576 type = TASK_RC; name = "rc";
5577 command = "windres -o";
5578 }
5580 virtual ~TaskRC()
5581 {}
5583 virtual bool execute()
5584 {
5585 String fullFile = parent.resolve(fileName);
5586 String fullOut = parent.resolve(outName);
5587 if (!isNewerThan(fullFile, fullOut))
5588 return true;
5589 String cmd = command;
5590 cmd.append(" ");
5591 cmd.append(fullOut);
5592 cmd.append(" ");
5593 cmd.append(flags);
5594 cmd.append(" ");
5595 cmd.append(fullFile);
5597 String outString, errString;
5598 if (!executeCommand(cmd.c_str(), "", outString, errString))
5599 {
5600 error("RC problem: %s", errString.c_str());
5601 return false;
5602 }
5603 return true;
5604 }
5606 virtual bool parse(Element *elem)
5607 {
5608 if (!parent.getAttribute(elem, "command", command))
5609 return false;
5610 if (!parent.getAttribute(elem, "file", fileName))
5611 return false;
5612 if (!parent.getAttribute(elem, "out", outName))
5613 return false;
5614 std::vector<Element *> children = elem->getChildren();
5615 for (unsigned int i=0 ; i<children.size() ; i++)
5616 {
5617 Element *child = children[i];
5618 String tagName = child->getName();
5619 if (tagName == "flags")
5620 {
5621 if (!parent.getValue(child, flags))
5622 return false;
5623 }
5624 }
5625 return true;
5626 }
5628 private:
5630 String command;
5631 String flags;
5632 String fileName;
5633 String outName;
5635 };
5639 /**
5640 * Strip an executable
5641 */
5642 class TaskStrip : public Task
5643 {
5644 public:
5646 TaskStrip(MakeBase &par) : Task(par)
5647 { type = TASK_STRIP; name = "strip"; }
5649 virtual ~TaskStrip()
5650 {}
5652 virtual bool execute()
5653 {
5654 String fullName = parent.resolve(fileName);
5655 //trace("fullDir:%s", fullDir.c_str());
5656 String cmd = "strip ";
5657 cmd.append(fullName);
5659 String outbuf, errbuf;
5660 if (!executeCommand(cmd, "", outbuf, errbuf))
5661 return false;
5662 return true;
5663 }
5665 virtual bool parse(Element *elem)
5666 {
5667 if (!parent.getAttribute(elem, "file", fileName))
5668 return false;
5669 if (fileName.size() == 0)
5670 {
5671 error("<strip> requires 'file=\"fileNname\"' attribute");
5672 return false;
5673 }
5674 return true;
5675 }
5677 private:
5679 String fileName;
5680 };
5683 /**
5684 *
5685 */
5686 class TaskTstamp : public Task
5687 {
5688 public:
5690 TaskTstamp(MakeBase &par) : Task(par)
5691 { type = TASK_TSTAMP; name = "tstamp"; }
5693 virtual ~TaskTstamp()
5694 {}
5696 virtual bool execute()
5697 {
5698 return true;
5699 }
5701 virtual bool parse(Element *elem)
5702 {
5703 trace("tstamp parse");
5704 return true;
5705 }
5706 };
5710 /**
5711 *
5712 */
5713 Task *Task::createTask(Element *elem)
5714 {
5715 String tagName = elem->getName();
5716 //trace("task:%s", tagName.c_str());
5717 Task *task = NULL;
5718 if (tagName == "ar")
5719 task = new TaskAr(parent);
5720 else if (tagName == "cc")
5721 task = new TaskCC(parent);
5722 else if (tagName == "copy")
5723 task = new TaskCopy(parent);
5724 else if (tagName == "delete")
5725 task = new TaskDelete(parent);
5726 else if (tagName == "jar")
5727 task = new TaskJar(parent);
5728 else if (tagName == "javac")
5729 task = new TaskJavac(parent);
5730 else if (tagName == "link")
5731 task = new TaskLink(parent);
5732 else if (tagName == "mkdir")
5733 task = new TaskMkDir(parent);
5734 else if (tagName == "msgfmt")
5735 task = new TaskMsgFmt(parent);
5736 else if (tagName == "ranlib")
5737 task = new TaskRanlib(parent);
5738 else if (tagName == "rc")
5739 task = new TaskRC(parent);
5740 else if (tagName == "strip")
5741 task = new TaskStrip(parent);
5742 else if (tagName == "tstamp")
5743 task = new TaskTstamp(parent);
5744 else
5745 {
5746 error("Unknown task '%s'", tagName.c_str());
5747 return NULL;
5748 }
5750 if (!task->parse(elem))
5751 {
5752 delete task;
5753 return NULL;
5754 }
5755 return task;
5756 }
5760 //########################################################################
5761 //# T A R G E T
5762 //########################################################################
5764 /**
5765 *
5766 */
5767 class Target : public MakeBase
5768 {
5770 public:
5772 /**
5773 *
5774 */
5775 Target(Make &par) : parent(par)
5776 { init(); }
5778 /**
5779 *
5780 */
5781 Target(const Target &other) : parent(other.parent)
5782 { init(); assign(other); }
5784 /**
5785 *
5786 */
5787 Target &operator=(const Target &other)
5788 { init(); assign(other); return *this; }
5790 /**
5791 *
5792 */
5793 virtual ~Target()
5794 { cleanup() ; }
5797 /**
5798 *
5799 */
5800 virtual Make &getParent()
5801 { return parent; }
5803 /**
5804 *
5805 */
5806 virtual String getName()
5807 { return name; }
5809 /**
5810 *
5811 */
5812 virtual void setName(const String &val)
5813 { name = val; }
5815 /**
5816 *
5817 */
5818 virtual String getDescription()
5819 { return description; }
5821 /**
5822 *
5823 */
5824 virtual void setDescription(const String &val)
5825 { description = val; }
5827 /**
5828 *
5829 */
5830 virtual void addDependency(const String &val)
5831 { deps.push_back(val); }
5833 /**
5834 *
5835 */
5836 virtual void parseDependencies(const String &val)
5837 { deps = tokenize(val, ", "); }
5839 /**
5840 *
5841 */
5842 virtual std::vector<String> &getDependencies()
5843 { return deps; }
5845 /**
5846 *
5847 */
5848 virtual String getIf()
5849 { return ifVar; }
5851 /**
5852 *
5853 */
5854 virtual void setIf(const String &val)
5855 { ifVar = val; }
5857 /**
5858 *
5859 */
5860 virtual String getUnless()
5861 { return unlessVar; }
5863 /**
5864 *
5865 */
5866 virtual void setUnless(const String &val)
5867 { unlessVar = val; }
5869 /**
5870 *
5871 */
5872 virtual void addTask(Task *val)
5873 { tasks.push_back(val); }
5875 /**
5876 *
5877 */
5878 virtual std::vector<Task *> &getTasks()
5879 { return tasks; }
5881 private:
5883 void init()
5884 {
5885 }
5887 void cleanup()
5888 {
5889 tasks.clear();
5890 }
5892 void assign(const Target &other)
5893 {
5894 //parent = other.parent;
5895 name = other.name;
5896 description = other.description;
5897 ifVar = other.ifVar;
5898 unlessVar = other.unlessVar;
5899 deps = other.deps;
5900 tasks = other.tasks;
5901 }
5903 Make &parent;
5905 String name;
5907 String description;
5909 String ifVar;
5911 String unlessVar;
5913 std::vector<String> deps;
5915 std::vector<Task *> tasks;
5917 };
5926 //########################################################################
5927 //# M A K E
5928 //########################################################################
5931 /**
5932 *
5933 */
5934 class Make : public MakeBase
5935 {
5937 public:
5939 /**
5940 *
5941 */
5942 Make()
5943 { init(); }
5945 /**
5946 *
5947 */
5948 Make(const Make &other)
5949 { assign(other); }
5951 /**
5952 *
5953 */
5954 Make &operator=(const Make &other)
5955 { assign(other); return *this; }
5957 /**
5958 *
5959 */
5960 virtual ~Make()
5961 { cleanup(); }
5963 /**
5964 *
5965 */
5966 virtual std::map<String, Target> &getTargets()
5967 { return targets; }
5970 /**
5971 *
5972 */
5973 bool run();
5975 /**
5976 *
5977 */
5978 bool run(const String &target);
5982 private:
5984 /**
5985 *
5986 */
5987 void init();
5989 /**
5990 *
5991 */
5992 void cleanup();
5994 /**
5995 *
5996 */
5997 void assign(const Make &other);
5999 /**
6000 *
6001 */
6002 bool executeTask(Task &task);
6005 /**
6006 *
6007 */
6008 bool executeTarget(Target &target,
6009 std::set<String> &targetsCompleted);
6012 /**
6013 *
6014 */
6015 bool execute();
6017 /**
6018 *
6019 */
6020 bool checkTargetDependencies(Target &prop,
6021 std::vector<String> &depList);
6023 /**
6024 *
6025 */
6026 bool parsePropertyFile(const String &fileName,
6027 const String &prefix);
6029 /**
6030 *
6031 */
6032 bool parseProperty(Element *elem);
6034 /**
6035 *
6036 */
6037 bool parseTask(Task &task, Element *elem);
6039 /**
6040 *
6041 */
6042 bool parseFile();
6044 /**
6045 *
6046 */
6047 std::vector<String> glob(const String &pattern);
6050 //###############
6051 //# Fields
6052 //###############
6054 String projectName;
6056 String currentTarget;
6058 String defaultTarget;
6060 String specifiedTarget;
6062 String baseDir;
6064 String description;
6066 String envAlias;
6068 //std::vector<Property> properties;
6070 std::map<String, Target> targets;
6072 std::vector<Task *> allTasks;
6075 };
6078 //########################################################################
6079 //# C L A S S M A I N T E N A N C E
6080 //########################################################################
6082 /**
6083 *
6084 */
6085 void Make::init()
6086 {
6087 uri = "build.xml";
6088 projectName = "";
6089 currentTarget = "";
6090 defaultTarget = "";
6091 specifiedTarget = "";
6092 baseDir = "";
6093 description = "";
6094 envAlias = "";
6095 properties.clear();
6096 for (unsigned int i = 0 ; i < allTasks.size() ; i++)
6097 delete allTasks[i];
6098 allTasks.clear();
6099 }
6103 /**
6104 *
6105 */
6106 void Make::cleanup()
6107 {
6108 for (unsigned int i = 0 ; i < allTasks.size() ; i++)
6109 delete allTasks[i];
6110 allTasks.clear();
6111 }
6115 /**
6116 *
6117 */
6118 void Make::assign(const Make &other)
6119 {
6120 uri = other.uri;
6121 projectName = other.projectName;
6122 currentTarget = other.currentTarget;
6123 defaultTarget = other.defaultTarget;
6124 specifiedTarget = other.specifiedTarget;
6125 baseDir = other.baseDir;
6126 description = other.description;
6127 properties = other.properties;
6128 }
6132 //########################################################################
6133 //# U T I L I T Y T A S K S
6134 //########################################################################
6136 /**
6137 * Perform a file globbing
6138 */
6139 std::vector<String> Make::glob(const String &pattern)
6140 {
6141 std::vector<String> res;
6142 return res;
6143 }
6146 //########################################################################
6147 //# P U B L I C A P I
6148 //########################################################################
6152 /**
6153 *
6154 */
6155 bool Make::executeTarget(Target &target,
6156 std::set<String> &targetsCompleted)
6157 {
6159 String name = target.getName();
6161 //First get any dependencies for this target
6162 std::vector<String> deps = target.getDependencies();
6163 for (unsigned int i=0 ; i<deps.size() ; i++)
6164 {
6165 String dep = deps[i];
6166 //Did we do it already? Skip
6167 if (targetsCompleted.find(dep)!=targetsCompleted.end())
6168 continue;
6170 std::map<String, Target> &tgts =
6171 target.getParent().getTargets();
6172 std::map<String, Target>::iterator iter =
6173 tgts.find(dep);
6174 if (iter == tgts.end())
6175 {
6176 error("Target '%s' dependency '%s' not found",
6177 name.c_str(), dep.c_str());
6178 return false;
6179 }
6180 Target depTarget = iter->second;
6181 if (!executeTarget(depTarget, targetsCompleted))
6182 {
6183 return false;
6184 }
6185 }
6187 status("## Target : %s", name.c_str());
6189 //Now let's do the tasks
6190 std::vector<Task *> &tasks = target.getTasks();
6191 for (unsigned int i=0 ; i<tasks.size() ; i++)
6192 {
6193 Task *task = tasks[i];
6194 status("---- task : %s", task->getName().c_str());
6195 if (!task->execute())
6196 {
6197 return false;
6198 }
6199 }
6201 targetsCompleted.insert(name);
6203 return true;
6204 }
6208 /**
6209 * Main execute() method. Start here and work
6210 * up the dependency tree
6211 */
6212 bool Make::execute()
6213 {
6214 status("######## EXECUTE");
6216 //Determine initial target
6217 if (specifiedTarget.size()>0)
6218 {
6219 currentTarget = specifiedTarget;
6220 }
6221 else if (defaultTarget.size()>0)
6222 {
6223 currentTarget = defaultTarget;
6224 }
6225 else
6226 {
6227 error("execute: no specified or default target requested");
6228 return false;
6229 }
6231 std::map<String, Target>::iterator iter =
6232 targets.find(currentTarget);
6233 if (iter == targets.end())
6234 {
6235 error("Initial target '%s' not found",
6236 currentTarget.c_str());
6237 return false;
6238 }
6240 //Now run
6241 Target target = iter->second;
6242 std::set<String> targetsCompleted;
6243 if (!executeTarget(target, targetsCompleted))
6244 {
6245 return false;
6246 }
6248 status("######## EXECUTE COMPLETE");
6249 return true;
6250 }
6255 /**
6256 *
6257 */
6258 bool Make::checkTargetDependencies(Target &target,
6259 std::vector<String> &depList)
6260 {
6261 String tgtName = target.getName().c_str();
6262 depList.push_back(tgtName);
6264 std::vector<String> deps = target.getDependencies();
6265 for (unsigned int i=0 ; i<deps.size() ; i++)
6266 {
6267 String dep = deps[i];
6268 //First thing entered was the starting Target
6269 if (dep == depList[0])
6270 {
6271 error("Circular dependency '%s' found at '%s'",
6272 dep.c_str(), tgtName.c_str());
6273 std::vector<String>::iterator diter;
6274 for (diter=depList.begin() ; diter!=depList.end() ; diter++)
6275 {
6276 error(" %s", diter->c_str());
6277 }
6278 return false;
6279 }
6281 std::map<String, Target> &tgts =
6282 target.getParent().getTargets();
6283 std::map<String, Target>::iterator titer = tgts.find(dep);
6284 if (titer == tgts.end())
6285 {
6286 error("Target '%s' dependency '%s' not found",
6287 tgtName.c_str(), dep.c_str());
6288 return false;
6289 }
6290 if (!checkTargetDependencies(titer->second, depList))
6291 {
6292 return false;
6293 }
6294 }
6295 return true;
6296 }
6302 static int getword(int pos, const String &inbuf, String &result)
6303 {
6304 int p = pos;
6305 int len = (int)inbuf.size();
6306 String val;
6307 while (p < len)
6308 {
6309 char ch = inbuf[p];
6310 if (!isalnum(ch) && ch!='.' && ch!='_')
6311 break;
6312 val.push_back(ch);
6313 p++;
6314 }
6315 result = val;
6316 return p;
6317 }
6322 /**
6323 *
6324 */
6325 bool Make::parsePropertyFile(const String &fileName,
6326 const String &prefix)
6327 {
6328 FILE *f = fopen(fileName.c_str(), "r");
6329 if (!f)
6330 {
6331 error("could not open property file %s", fileName.c_str());
6332 return false;
6333 }
6334 int linenr = 0;
6335 while (!feof(f))
6336 {
6337 char buf[256];
6338 if (!fgets(buf, 255, f))
6339 break;
6340 linenr++;
6341 String s = buf;
6342 s = trim(s);
6343 int len = s.size();
6344 if (len == 0)
6345 continue;
6346 if (s[0] == '#')
6347 continue;
6348 String key;
6349 String val;
6350 int p = 0;
6351 int p2 = getword(p, s, key);
6352 if (p2 <= p)
6353 {
6354 error("property file %s, line %d: expected keyword",
6355 fileName.c_str(), linenr);
6356 return false;
6357 }
6358 if (prefix.size() > 0)
6359 {
6360 key.insert(0, prefix);
6361 }
6363 //skip whitespace
6364 for (p=p2 ; p<len ; p++)
6365 if (!isspace(s[p]))
6366 break;
6368 if (p>=len || s[p]!='=')
6369 {
6370 error("property file %s, line %d: expected '='",
6371 fileName.c_str(), linenr);
6372 return false;
6373 }
6374 p++;
6376 //skip whitespace
6377 for ( ; p<len ; p++)
6378 if (!isspace(s[p]))
6379 break;
6381 /* This way expects a word after the =
6382 p2 = getword(p, s, val);
6383 if (p2 <= p)
6384 {
6385 error("property file %s, line %d: expected value",
6386 fileName.c_str(), linenr);
6387 return false;
6388 }
6389 */
6390 // This way gets the rest of the line after the =
6391 if (p>=len)
6392 {
6393 error("property file %s, line %d: expected value",
6394 fileName.c_str(), linenr);
6395 return false;
6396 }
6397 val = s.substr(p);
6398 if (key.size()==0 || val.size()==0)
6399 continue;
6401 //trace("key:'%s' val:'%s'", key.c_str(), val.c_str());
6402 properties[key] = val;
6403 }
6404 fclose(f);
6405 return true;
6406 }
6411 /**
6412 *
6413 */
6414 bool Make::parseProperty(Element *elem)
6415 {
6416 std::vector<Attribute> &attrs = elem->getAttributes();
6417 for (unsigned int i=0 ; i<attrs.size() ; i++)
6418 {
6419 String attrName = attrs[i].getName();
6420 String attrVal = attrs[i].getValue();
6422 if (attrName == "name")
6423 {
6424 String val;
6425 if (!getAttribute(elem, "value", val))
6426 return false;
6427 if (val.size() > 0)
6428 {
6429 properties[attrVal] = val;
6430 continue;
6431 }
6432 if (!getAttribute(elem, "location", val))
6433 return false;
6434 if (val.size() > 0)
6435 {
6436 //TODO: process a path relative to build.xml
6437 properties[attrVal] = val;
6438 continue;
6439 }
6440 }
6441 else if (attrName == "file")
6442 {
6443 String prefix;
6444 if (!getAttribute(elem, "prefix", prefix))
6445 return false;
6446 if (prefix.size() > 0)
6447 {
6448 if (prefix[prefix.size()-1] != '.')
6449 prefix.push_back('.');
6450 }
6451 if (!parsePropertyFile(attrName, prefix))
6452 return false;
6453 }
6454 else if (attrName == "environment")
6455 {
6456 if (envAlias.size() > 0)
6457 {
6458 error("environment property can only be set once");
6459 return false;
6460 }
6461 envAlias = attrVal;
6462 }
6463 }
6465 return true;
6466 }
6471 /**
6472 *
6473 */
6474 bool Make::parseFile()
6475 {
6476 status("######## PARSE");
6478 Parser parser;
6479 Element *root = parser.parseFile(uri.getNativePath());
6480 if (!root)
6481 {
6482 error("Could not open %s for reading",
6483 uri.getNativePath().c_str());
6484 return false;
6485 }
6487 if (root->getChildren().size()==0 ||
6488 root->getChildren()[0]->getName()!="project")
6489 {
6490 error("Main xml element should be <project>");
6491 delete root;
6492 return false;
6493 }
6495 //########## Project attributes
6496 Element *project = root->getChildren()[0];
6497 String s = project->getAttribute("name");
6498 if (s.size() > 0)
6499 projectName = s;
6500 s = project->getAttribute("default");
6501 if (s.size() > 0)
6502 defaultTarget = s;
6503 s = project->getAttribute("basedir");
6504 if (s.size() > 0)
6505 baseDir = s;
6507 //######### PARSE MEMBERS
6508 std::vector<Element *> children = project->getChildren();
6509 for (unsigned int i=0 ; i<children.size() ; i++)
6510 {
6511 Element *elem = children[i];
6512 String tagName = elem->getName();
6514 //########## DESCRIPTION
6515 if (tagName == "description")
6516 {
6517 description = parser.trim(elem->getValue());
6518 }
6520 //######### PROPERTY
6521 else if (tagName == "property")
6522 {
6523 if (!parseProperty(elem))
6524 return false;
6525 }
6527 //######### TARGET
6528 else if (tagName == "target")
6529 {
6530 String tname = elem->getAttribute("name");
6531 String tdesc = elem->getAttribute("description");
6532 String tdeps = elem->getAttribute("depends");
6533 String tif = elem->getAttribute("if");
6534 String tunless = elem->getAttribute("unless");
6535 Target target(*this);
6536 target.setName(tname);
6537 target.setDescription(tdesc);
6538 target.parseDependencies(tdeps);
6539 target.setIf(tif);
6540 target.setUnless(tunless);
6541 std::vector<Element *> telems = elem->getChildren();
6542 for (unsigned int i=0 ; i<telems.size() ; i++)
6543 {
6544 Element *telem = telems[i];
6545 Task breeder(*this);
6546 Task *task = breeder.createTask(telem);
6547 if (!task)
6548 return false;
6549 allTasks.push_back(task);
6550 target.addTask(task);
6551 }
6553 //Check name
6554 if (tname.size() == 0)
6555 {
6556 error("no name for target");
6557 return false;
6558 }
6559 //Check for duplicate name
6560 if (targets.find(tname) != targets.end())
6561 {
6562 error("target '%s' already defined", tname.c_str());
6563 return false;
6564 }
6565 //more work than targets[tname]=target, but avoids default allocator
6566 targets.insert(std::make_pair<String, Target>(tname, target));
6567 }
6569 }
6571 std::map<String, Target>::iterator iter;
6572 for (iter = targets.begin() ; iter!= targets.end() ; iter++)
6573 {
6574 Target tgt = iter->second;
6575 std::vector<String> depList;
6576 if (!checkTargetDependencies(tgt, depList))
6577 {
6578 return false;
6579 }
6580 }
6583 delete root;
6584 status("######## PARSE COMPLETE");
6585 return true;
6586 }
6589 /**
6590 *
6591 */
6592 bool Make::run()
6593 {
6594 if (!parseFile())
6595 return false;
6596 if (!execute())
6597 return false;
6598 return true;
6599 }
6602 /**
6603 *
6604 */
6605 bool Make::run(const String &target)
6606 {
6607 status("##################################");
6608 status("# BuildTool");
6609 status("# version 0.2");
6610 status("##################################");
6611 specifiedTarget = target;
6612 if (!run())
6613 return false;
6614 status("##################################");
6615 status("# BuildTool Completed");
6616 status("##################################");
6617 return true;
6618 }
6626 }// namespace buildtool
6627 //########################################################################
6628 //# M A I N
6629 //########################################################################
6631 /**
6632 * Format an error message in printf() style
6633 */
6634 static void error(char *fmt, ...)
6635 {
6636 va_list ap;
6637 va_start(ap, fmt);
6638 fprintf(stderr, "BuildTool error: ");
6639 vfprintf(stderr, fmt, ap);
6640 fprintf(stderr, "\n");
6641 va_end(ap);
6642 }
6645 /**
6646 * Compare a buffer with a key, for the length of the key
6647 */
6648 static bool sequ(const buildtool::String &buf, char *key)
6649 {
6650 for (int i=0 ; key[i] ; i++)
6651 {
6652 if (key[i] != buf[i])
6653 return false;
6654 }
6655 return true;
6656 }
6658 /**
6659 * Parse the command-line args, get our options,
6660 * and run this thing
6661 */
6662 static bool parseOptions(int argc, char **argv)
6663 {
6664 if (argc < 1)
6665 {
6666 error("Cannot parse arguments");
6667 return false;
6668 }
6670 buildtool::String buildFile;
6671 buildtool::String target;
6673 //char *progName = argv[0];
6674 for (int i=1 ; i<argc ; i++)
6675 {
6676 buildtool::String arg = argv[i];
6677 if (sequ(arg, "--"))
6678 {
6679 if (sequ(arg, "--file=") && arg.size()>7)
6680 {
6681 buildFile = arg.substr(7, arg.size()-7);
6682 }
6683 else
6684 {
6685 error("Unknown option:%s", arg.c_str());
6686 return false;
6687 }
6688 }
6689 else if (sequ(arg, "-"))
6690 {
6691 for (unsigned int p=1 ; p<arg.size() ; p++)
6692 {
6693 int ch = arg[p];
6694 if (0)//put options here
6695 {
6696 }
6697 else
6698 {
6699 error("Unknown option '%c'", ch);
6700 return false;
6701 }
6702 }
6703 }
6704 else
6705 {
6706 target = arg;
6707 }
6708 }
6710 //We have the options. Now execute them
6711 buildtool::Make make;
6712 if (buildFile.size() > 0)
6713 {
6714 make.setURI(buildFile);
6715 }
6716 if (!make.run(target))
6717 return false;
6719 return true;
6720 }
6725 /*
6726 static bool runMake()
6727 {
6728 buildtool::Make make;
6729 if (!make.run())
6730 return false;
6731 return true;
6732 }
6735 static bool pkgConfigTest()
6736 {
6737 buildtool::PkgConfig pkgConfig;
6738 if (!pkgConfig.readFile("gtk+-2.0.pc"))
6739 return false;
6740 return true;
6741 }
6745 static bool depTest()
6746 {
6747 buildtool::DepTool deptool;
6748 deptool.setSourceDirectory("/dev/ink/inkscape/src");
6749 if (!deptool.generateDependencies("build.dep"))
6750 return false;
6751 std::vector<buildtool::DepRec> res =
6752 deptool.loadDepFile("build.dep");
6753 if (res.size() == 0)
6754 return false;
6755 return true;
6756 }
6758 static bool popenTest()
6759 {
6760 buildtool::Make make;
6761 buildtool::String out, err;
6762 bool ret = make.executeCommand("gcc xx.cpp", "", out, err);
6763 printf("Popen test:%d '%s' '%s'\n", ret, out.c_str(), err.c_str());
6764 return true;
6765 }
6768 static bool propFileTest()
6769 {
6770 buildtool::Make make;
6771 make.parsePropertyFile("test.prop", "test.");
6772 return true;
6773 }
6774 */
6776 int main(int argc, char **argv)
6777 {
6779 if (!parseOptions(argc, argv))
6780 return 1;
6781 /*
6782 if (!popenTest())
6783 return 1;
6785 if (!depTest())
6786 return 1;
6787 if (!propFileTest())
6788 return 1;
6789 if (runMake())
6790 return 1;
6791 */
6792 return 0;
6793 }
6796 //########################################################################
6797 //# E N D
6798 //########################################################################