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