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