diff options
| author | kamalpreetgrewal <grewalkamal005@gmail.com> | 2016-08-15 03:59:18 +0000 |
|---|---|---|
| committer | kamalpreetgrewal <grewalkamal005@gmail.com> | 2016-08-15 03:59:18 +0000 |
| commit | 25a9ed4f38121eeb59cf15dbf19391aaef45bba3 (patch) | |
| tree | e123aeae50d98a52e1ad4575b29bf3c199619e33 /buildtool.cpp | |
| parent | Solve crash when deleting CSS property (diff) | |
| parent | inkview: Convert to ApplicationWindow (diff) | |
| download | inkscape-25a9ed4f38121eeb59cf15dbf19391aaef45bba3.tar.gz inkscape-25a9ed4f38121eeb59cf15dbf19391aaef45bba3.zip | |
Merge changes from trunk
(bzr r14949.1.64)
Diffstat (limited to 'buildtool.cpp')
| -rw-r--r-- | buildtool.cpp | 10333 |
1 files changed, 0 insertions, 10333 deletions
diff --git a/buildtool.cpp b/buildtool.cpp deleted file mode 100644 index cb70a25c2..000000000 --- a/buildtool.cpp +++ /dev/null @@ -1,10333 +0,0 @@ -/** - * Simple build automation tool. - * - * Authors: - * Bob Jamison - * Jasper van de Gronde - * Johan Engelen - * - * Copyright (C) 2006-2008 Bob Jamison - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -/** - * To use this file, compile with: - * <pre> - * g++ -O3 buildtool.cpp -o btool.exe -fopenmp - * (or whatever your compiler might be) - * Then - * btool - * or - * btool {target} - * - * Note: if you are using MinGW, and a not very recent version of it, - * gettimeofday() might be missing. If so, just build this file with - * this command: - * g++ -O3 -DNEED_GETTIMEOFDAY buildtool.cpp -o btool.exe -fopenmp - * - */ - -#define BUILDTOOL_VERSION "BuildTool v0.9.9multi" - -#include <stdio.h> -#include <fcntl.h> -#include <unistd.h> -#include <stdarg.h> -#include <sys/stat.h> -#include <time.h> -#include <sys/time.h> -#include <utime.h> -#include <dirent.h> - -#include <iostream> -#include <list> -#include <string> -#include <map> -#include <set> -#include <vector> -#include <algorithm> - - -#ifdef __WIN32__ -#define WIN32_LEAN_AND_MEAN -#define NOGDI -#include <windows.h> -#endif - -#include <errno.h> - - -//######################################################################## -//# Definition of gettimeofday() for those who don't have it -//######################################################################## -#ifdef NEED_GETTIMEOFDAY -#include <sys/timeb.h> - -struct timezone { - int tz_minuteswest; /* minutes west of Greenwich */ - int tz_dsttime; /* type of dst correction */ - }; - -static int gettimeofday (struct timeval *tv, struct timezone *tz) -{ - struct _timeb tb; - - if (!tv) - return (-1); - - _ftime (&tb); - tv->tv_sec = tb.time; - tv->tv_usec = tb.millitm * 1000 + 500; - if (tz) - { - tz->tz_minuteswest = -60 * _timezone; - tz->tz_dsttime = _daylight; - } - return 0; -} - -#endif - - - - - - - -namespace buildtool -{ - - - - -//######################################################################## -//######################################################################## -//## R E G E X P -//######################################################################## -//######################################################################## - -/** - * This is the SLRE (Super Light Regular Expression library) - * SLRE is an ISO C library that implements a subset of Perl - * regular expression syntax. - * - * See https://github.com/cesanta/slre for details - * - * It's clean code and small size allow us to - * embed it in BuildTool without adding a dependency - * - */ - -//begin slre.h - -/* - * Copyright (c) 2004-2013 Sergey Lyubka <valenok@gmail.com> - * Copyright (c) 2013 Cesanta Software Limited - * All rights reserved - * - * This library is dual-licensed: you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. For the terms of this - * license, see <http://www.gnu.org/licenses/>. - * - * You are free to use this library under the terms of the GNU General - * Public License, but WITHOUT ANY WARRANTY; without even the implied - * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU General Public License for more details. - * - * Alternatively, you can license this library under a commercial - * license, as set out in <http://cesanta.com/products.html>. - */ - -/* - * This is a regular expression library that implements a subset of Perl RE. - * Please refer to README.md for a detailed reference. - */ - -#ifndef SLRE_HEADER_DEFINED -#define SLRE_HEADER_DEFINED - -#ifdef __cplusplus -extern "C" { -#endif - -struct slre_cap { - const char *ptr; - int len; -}; - - -int slre_match(const char *regexp, const char *buf, int buf_len, - struct slre_cap *caps, int num_caps, int flags); - -/* Possible flags for slre_match() */ -enum { SLRE_IGNORE_CASE = 1 }; - - -/* slre_match() failure codes */ -#define SLRE_NO_MATCH -1 -#define SLRE_UNEXPECTED_QUANTIFIER -2 -#define SLRE_UNBALANCED_BRACKETS -3 -#define SLRE_INTERNAL_ERROR -4 -#define SLRE_INVALID_CHARACTER_SET -5 -#define SLRE_INVALID_METACHARACTER -6 -#define SLRE_CAPS_ARRAY_TOO_SMALL -7 -#define SLRE_TOO_MANY_BRANCHES -8 -#define SLRE_TOO_MANY_BRACKETS -9 - -#ifdef __cplusplus -} -#endif - -#endif /* SLRE_HEADER_DEFINED */ - -//end slre.h - -//start slre.c - -/* - * Copyright (c) 2004-2013 Sergey Lyubka <valenok@gmail.com> - * Copyright (c) 2013 Cesanta Software Limited - * All rights reserved - * - * This library is dual-licensed: you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. For the terms of this - * license, see <http://www.gnu.org/licenses/>. - * - * You are free to use this library under the terms of the GNU General - * Public License, but WITHOUT ANY WARRANTY; without even the implied - * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU General Public License for more details. - * - * Alternatively, you can license this library under a commercial - * license, as set out in <http://cesanta.com/products.html>. - */ - -#include <stdio.h> -#include <ctype.h> -#include <string.h> - -//#include "slre.h" - -#define MAX_BRANCHES 100 -#define MAX_BRACKETS 100 -#define FAIL_IF(condition, error_code) if (condition) return (error_code) - -#ifndef ARRAY_SIZE -#define ARRAY_SIZE(ar) (sizeof(ar) / sizeof((ar)[0])) -#endif - -#ifdef SLRE_DEBUG -#define DBG(x) printf x -#else -#define DBG(x) -#endif - -struct bracket_pair { - const char *ptr; /* Points to the first char after '(' in regex */ - int len; /* Length of the text between '(' and ')' */ - int branches; /* Index in the branches array for this pair */ - int num_branches; /* Number of '|' in this bracket pair */ -}; - -struct branch { - int bracket_index; /* index for 'struct bracket_pair brackets' */ - /* array defined below */ - const char *schlong; /* points to the '|' character in the regex */ -}; - -struct regex_info { - /* - * Describes all bracket pairs in the regular expression. - * First entry is always present, and grabs the whole regex. - */ - struct bracket_pair brackets[MAX_BRACKETS]; - int num_brackets; - - /* - * Describes alternations ('|' operators) in the regular expression. - * Each branch falls into a specific branch pair. - */ - struct branch branches[MAX_BRANCHES]; - int num_branches; - - /* Array of captures provided by the user */ - struct slre_cap *caps; - int num_caps; - - /* E.g. SLRE_IGNORE_CASE. See enum below */ - int flags; -}; - -static int is_metacharacter(const unsigned char *s) { - static const char *metacharacters = "^$().[]*+?|\\Ssdbfnrtv"; - return strchr(metacharacters, *s) != NULL; -} - -static int op_len(const char *re) { - return re[0] == '\\' && re[1] == 'x' ? 4 : re[0] == '\\' ? 2 : 1; -} - -static int set_len(const char *re, int re_len) { - int len = 0; - - while (len < re_len && re[len] != ']') { - len += op_len(re + len); - } - - return len <= re_len ? len + 1 : -1; -} - -static int get_op_len(const char *re, int re_len) { - return re[0] == '[' ? set_len(re + 1, re_len - 1) + 1 : op_len(re); -} - -static int is_quantifier(const char *re) { - return re[0] == '*' || re[0] == '+' || re[0] == '?'; -} - -static int toi(int x) { - return isdigit(x) ? x - '0' : x - 'W'; -} - -static int hextoi(const unsigned char *s) { - return (toi(tolower(s[0])) << 4) | toi(tolower(s[1])); -} - -static int match_op(const unsigned char *re, const unsigned char *s, - struct regex_info *info) { - int result = 0; - switch (*re) { - case '\\': - /* Metacharacters */ - switch (re[1]) { - case 'S': FAIL_IF(isspace(*s), SLRE_NO_MATCH); result++; break; - case 's': FAIL_IF(!isspace(*s), SLRE_NO_MATCH); result++; break; - case 'd': FAIL_IF(!isdigit(*s), SLRE_NO_MATCH); result++; break; - case 'b': FAIL_IF(*s != '\b', SLRE_NO_MATCH); result++; break; - case 'f': FAIL_IF(*s != '\f', SLRE_NO_MATCH); result++; break; - case 'n': FAIL_IF(*s != '\n', SLRE_NO_MATCH); result++; break; - case 'r': FAIL_IF(*s != '\r', SLRE_NO_MATCH); result++; break; - case 't': FAIL_IF(*s != '\t', SLRE_NO_MATCH); result++; break; - case 'v': FAIL_IF(*s != '\v', SLRE_NO_MATCH); result++; break; - - case 'x': - /* Match byte, \xHH where HH is hexadecimal byte representaion */ - FAIL_IF(hextoi(re + 2) != *s, SLRE_NO_MATCH); - result++; - break; - - default: - /* Valid metacharacter check is done in bar() */ - FAIL_IF(re[1] != s[0], SLRE_NO_MATCH); - result++; - break; - } - break; - - case '|': FAIL_IF(1, SLRE_INTERNAL_ERROR); break; - case '$': FAIL_IF(1, SLRE_NO_MATCH); break; - case '.': result++; break; - - default: - if (info->flags & SLRE_IGNORE_CASE) { - FAIL_IF(tolower(*re) != tolower(*s), SLRE_NO_MATCH); - } else { - FAIL_IF(*re != *s, SLRE_NO_MATCH); - } - result++; - break; - } - - return result; -} - -static int match_set(const char *re, int re_len, const char *s, - struct regex_info *info) { - int len = 0, result = -1, invert = re[0] == '^'; - - if (invert) re++, re_len--; - - while (len <= re_len && re[len] != ']' && result <= 0) { - /* Support character range */ - if (re[len] != '-' && re[len + 1] == '-' && re[len + 2] != ']' && - re[len + 2] != '\0') { - result = info->flags & SLRE_IGNORE_CASE ? - tolower(*s) >= tolower(re[len]) && tolower(*s) <= tolower(re[len + 2]) : - *s >= re[len] && *s <= re[len + 2]; - len += 3; - } else { - result = match_op((unsigned char *) re + len, (unsigned char *) s, info); - len += op_len(re + len); - } - } - return (!invert && result > 0) || (invert && result <= 0) ? 1 : -1; -} - -static int doh(const char *s, int s_len, struct regex_info *info, int bi); - -static int bar(const char *re, int re_len, const char *s, int s_len, - struct regex_info *info, int bi) { - /* i is offset in re, j is offset in s, bi is brackets index */ - int i, j, n, step; - - for (i = j = 0; i < re_len && j <= s_len; i += step) { - - /* Handle quantifiers. Get the length of the chunk. */ - step = re[i] == '(' ? info->brackets[bi + 1].len + 2 : - get_op_len(re + i, re_len - i); - - DBG(("%s [%.*s] [%.*s] re_len=%d step=%d i=%d j=%d\n", __func__, - re_len - i, re + i, s_len - j, s + j, re_len, step, i, j)); - - FAIL_IF(is_quantifier(&re[i]), SLRE_UNEXPECTED_QUANTIFIER); - FAIL_IF(step <= 0, SLRE_INVALID_CHARACTER_SET); - - if (i + step < re_len && is_quantifier(re + i + step)) { - DBG(("QUANTIFIER: [%.*s]%c [%.*s]\n", step, re + i, - re[i + step], s_len - j, s + j)); - if (re[i + step] == '?') { - int result = bar(re + i, step, s + j, s_len - j, info, bi); - j += result > 0 ? result : 0; - i++; - } else if (re[i + step] == '+' || re[i + step] == '*') { - int j2 = j, nj = j, n1, n2 = -1, ni, non_greedy = 0; - - /* Points to the regexp code after the quantifier */ - ni = i + step + 1; - if (ni < re_len && re[ni] == '?') { - non_greedy = 1; - ni++; - } - - do { - if ((n1 = bar(re + i, step, s + j2, s_len - j2, info, bi)) > 0) { - j2 += n1; - } - if (re[i + step] == '+' && n1 < 0) break; - - if (ni >= re_len) { - /* After quantifier, there is nothing */ - nj = j2; - } else if ((n2 = bar(re + ni, re_len - ni, s + j2, - s_len - j2, info, bi)) >= 0) { - /* Regex after quantifier matched */ - nj = j2 + n2; - } - if (nj > j && non_greedy) break; - } while (n1 > 0); - - /* - * Even if we found one or more pattern, this branch will be executed, - * changing the next captures. - */ - if (n1 < 0 && n2 < 0 && re[i + step] == '*' && - (n2 = bar(re + ni, re_len - ni, s + j, s_len - j, info, bi)) > 0) { - nj = j + n2; - } - - DBG(("STAR/PLUS END: %d %d %d %d %d\n", j, nj, re_len - ni, n1, n2)); - FAIL_IF(re[i + step] == '+' && nj == j, SLRE_NO_MATCH); - - /* If while loop body above was not executed for the * quantifier, */ - /* make sure the rest of the regex matches */ - FAIL_IF(nj == j && ni < re_len && n2 < 0, SLRE_NO_MATCH); - - /* Returning here cause we've matched the rest of RE already */ - return nj; - } - continue; - } - - if (re[i] == '[') { - n = match_set(re + i + 1, re_len - (i + 2), s + j, info); - DBG(("SET %.*s [%.*s] -> %d\n", step, re + i, s_len - j, s + j, n)); - FAIL_IF(n <= 0, SLRE_NO_MATCH); - j += n; - } else if (re[i] == '(') { - n = SLRE_NO_MATCH; - bi++; - FAIL_IF(bi >= info->num_brackets, SLRE_INTERNAL_ERROR); - DBG(("CAPTURING [%.*s] [%.*s] [%s]\n", - step, re + i, s_len - j, s + j, re + i + step)); - - if (re_len - (i + step) <= 0) { - /* Nothing follows brackets */ - n = doh(s + j, s_len - j, info, bi); - } else { - int j2; - for (j2 = 0; j2 <= s_len - j; j2++) { - if ((n = doh(s + j, s_len - (j + j2), info, bi)) >= 0 && - bar(re + i + step, re_len - (i + step), - s + j + n, s_len - (j + n), info, bi) >= 0) break; - } - } - - DBG(("CAPTURED [%.*s] [%.*s]:%d\n", step, re + i, s_len - j, s + j, n)); - FAIL_IF(n < 0, n); - if (info->caps != NULL && n > 0) { - info->caps[bi - 1].ptr = s + j; - info->caps[bi - 1].len = n; - } - j += n; - } else if (re[i] == '^') { - FAIL_IF(j != 0, SLRE_NO_MATCH); - } else if (re[i] == '$') { - FAIL_IF(j != s_len, SLRE_NO_MATCH); - } else { - FAIL_IF(j >= s_len, SLRE_NO_MATCH); - n = match_op((unsigned char *) (re + i), (unsigned char *) (s + j), info); - FAIL_IF(n <= 0, n); - j += n; - } - } - - return j; -} - -/* Process branch points */ -static int doh(const char *s, int s_len, struct regex_info *info, int bi) { - const struct bracket_pair *b = &info->brackets[bi]; - int i = 0, len, result; - const char *p; - - do { - p = i == 0 ? b->ptr : info->branches[b->branches + i - 1].schlong + 1; - len = b->num_branches == 0 ? b->len : - i == b->num_branches ? (int) (b->ptr + b->len - p) : - (int) (info->branches[b->branches + i].schlong - p); - DBG(("%s %d %d [%.*s] [%.*s]\n", __func__, bi, i, len, p, s_len, s)); - result = bar(p, len, s, s_len, info, bi); - DBG(("%s <- %d\n", __func__, result)); - } while (result <= 0 && i++ < b->num_branches); /* At least 1 iteration */ - - return result; -} - -static int baz(const char *s, int s_len, struct regex_info *info) { - int i, result = -1, is_anchored = info->brackets[0].ptr[0] == '^'; - - for (i = 0; i <= s_len; i++) { - result = doh(s + i, s_len - i, info, 0); - if (result >= 0) { - result += i; - break; - } - if (is_anchored) break; - } - - return result; -} - -static void setup_branch_points(struct regex_info *info) { - int i, j; - struct branch tmp; - - /* First, sort branches. Must be stable, no qsort. Use bubble algo. */ - for (i = 0; i < info->num_branches; i++) { - for (j = i + 1; j < info->num_branches; j++) { - if (info->branches[i].bracket_index > info->branches[j].bracket_index) { - tmp = info->branches[i]; - info->branches[i] = info->branches[j]; - info->branches[j] = tmp; - } - } - } - - /* - * For each bracket, set their branch points. This way, for every bracket - * (i.e. every chunk of regex) we know all branch points before matching. - */ - for (i = j = 0; i < info->num_brackets; i++) { - info->brackets[i].num_branches = 0; - info->brackets[i].branches = j; - while (j < info->num_branches && info->branches[j].bracket_index == i) { - info->brackets[i].num_branches++; - j++; - } - } -} - -static int foo(const char *re, int re_len, const char *s, int s_len, - struct regex_info *info) { - int i, step, depth = 0; - - /* First bracket captures everything */ - info->brackets[0].ptr = re; - info->brackets[0].len = re_len; - info->num_brackets = 1; - - /* Make a single pass over regex string, memorize brackets and branches */ - for (i = 0; i < re_len; i += step) { - step = get_op_len(re + i, re_len - i); - - if (re[i] == '|') { - FAIL_IF(info->num_branches >= (int) ARRAY_SIZE(info->branches), - SLRE_TOO_MANY_BRANCHES); - info->branches[info->num_branches].bracket_index = - info->brackets[info->num_brackets - 1].len == -1 ? - info->num_brackets - 1 : depth; - info->branches[info->num_branches].schlong = &re[i]; - info->num_branches++; - } else if (re[i] == '\\') { - FAIL_IF(i >= re_len - 1, SLRE_INVALID_METACHARACTER); - if (re[i + 1] == 'x') { - /* Hex digit specification must follow */ - FAIL_IF(re[i + 1] == 'x' && i >= re_len - 3, - SLRE_INVALID_METACHARACTER); - FAIL_IF(re[i + 1] == 'x' && !(isxdigit(re[i + 2]) && - isxdigit(re[i + 3])), SLRE_INVALID_METACHARACTER); - } else { - FAIL_IF(!is_metacharacter((unsigned char *) re + i + 1), - SLRE_INVALID_METACHARACTER); - } - } else if (re[i] == '(') { - FAIL_IF(info->num_brackets >= (int) ARRAY_SIZE(info->brackets), - SLRE_TOO_MANY_BRACKETS); - depth++; /* Order is important here. Depth increments first. */ - info->brackets[info->num_brackets].ptr = re + i + 1; - info->brackets[info->num_brackets].len = -1; - info->num_brackets++; - FAIL_IF(info->num_caps > 0 && info->num_brackets - 1 > info->num_caps, - SLRE_CAPS_ARRAY_TOO_SMALL); - } else if (re[i] == ')') { - int ind = info->brackets[info->num_brackets - 1].len == -1 ? - info->num_brackets - 1 : depth; - info->brackets[ind].len = (int) (&re[i] - info->brackets[ind].ptr); - DBG(("SETTING BRACKET %d [%.*s]\n", - ind, info->brackets[ind].len, info->brackets[ind].ptr)); - depth--; - FAIL_IF(depth < 0, SLRE_UNBALANCED_BRACKETS); - FAIL_IF(i > 0 && re[i - 1] == '(', SLRE_NO_MATCH); - } - } - - FAIL_IF(depth != 0, SLRE_UNBALANCED_BRACKETS); - setup_branch_points(info); - - return baz(s, s_len, info); -} - -int slre_match(const char *regexp, const char *s, int s_len, - struct slre_cap *caps, int num_caps, int flags) { - struct regex_info info; - - /* Initialize info structure */ - info.flags = flags; - info.num_brackets = info.num_branches = 0; - info.num_caps = num_caps; - info.caps = caps; - - DBG(("========================> [%s] [%.*s]\n", regexp, s_len, s)); - return foo(regexp, (int) strlen(regexp), s, s_len, &info); -} - -//end slre.c - -//######################################################################## -//######################################################################## -//## E N D R E G E X P -//######################################################################## -//######################################################################## - - - - - -//######################################################################## -//######################################################################## -//## X M L -//######################################################################## -//######################################################################## - -// Note: This mini-dom library comes from Pedro, another little project -// of mine. - -typedef std::string String; -typedef unsigned int XMLCh; - - -class Namespace -{ -public: - Namespace() - {} - - Namespace(const String &prefixArg, const String &namespaceURIArg) - { - prefix = prefixArg; - namespaceURI = namespaceURIArg; - } - - Namespace(const Namespace &other) - { - assign(other); - } - - Namespace &operator=(const Namespace &other) - { - assign(other); - return *this; - } - - virtual ~Namespace() - {} - - virtual String getPrefix() - { return prefix; } - - virtual String getNamespaceURI() - { return namespaceURI; } - -protected: - - void assign(const Namespace &other) - { - prefix = other.prefix; - namespaceURI = other.namespaceURI; - } - - String prefix; - String namespaceURI; - -}; - -class Attribute -{ -public: - Attribute() - {} - - Attribute(const String &nameArg, const String &valueArg) - { - name = nameArg; - value = valueArg; - } - - Attribute(const Attribute &other) - { - assign(other); - } - - Attribute &operator=(const Attribute &other) - { - assign(other); - return *this; - } - - virtual ~Attribute() - {} - - virtual String getName() - { return name; } - - virtual String getValue() - { return value; } - -protected: - - void assign(const Attribute &other) - { - name = other.name; - value = other.value; - } - - String name; - String value; - -}; - - -class Element -{ -friend class Parser; - -public: - Element() - { - init(); - } - - Element(const String &nameArg) - { - init(); - name = nameArg; - } - - Element(const String &nameArg, const String &valueArg) - { - init(); - name = nameArg; - value = valueArg; - } - - Element(const Element &other) - { - assign(other); - } - - Element &operator=(const Element &other) - { - assign(other); - return *this; - } - - virtual Element *clone(); - - virtual ~Element() - { - for (std::size_t i=0 ; i<children.size() ; i++) - delete children[i]; - } - - virtual String getName() - { return name; } - - virtual String getValue() - { return value; } - - Element *getParent() - { return parent; } - - std::vector<Element *> getChildren() - { return children; } - - std::vector<Element *> findElements(const String &name); - - String getAttribute(const String &name); - - std::vector<Attribute> &getAttributes() - { return attributes; } - - String getTagAttribute(const String &tagName, const String &attrName); - - String getTagValue(const String &tagName); - - void addChild(Element *child); - - void addAttribute(const String &name, const String &value); - - void addNamespace(const String &prefix, const String &namespaceURI); - - - /** - * Prettyprint an XML tree to an output stream. Elements are indented - * according to element hierarchy. - * @param f a stream to receive the output - * @param elem the element to output - */ - void writeIndented(FILE *f); - - /** - * Prettyprint an XML tree to standard output. This is the equivalent of - * writeIndented(stdout). - * @param elem the element to output - */ - void print(); - - int getLine() - { return line; } - -protected: - - void init() - { - parent = NULL; - line = 0; - } - - void assign(const Element &other) - { - parent = other.parent; - children = other.children; - attributes = other.attributes; - namespaces = other.namespaces; - name = other.name; - value = other.value; - line = other.line; - } - - void findElementsRecursive(std::vector<Element *>&res, const String &name); - - void writeIndentedRecursive(FILE *f, int indent); - - Element *parent; - - std::vector<Element *>children; - - std::vector<Attribute> attributes; - std::vector<Namespace> namespaces; - - String name; - String value; - - int line; -}; - - - - - -class Parser -{ -public: - /** - * Constructor - */ - Parser() - { init(); } - - virtual ~Parser() - {} - - /** - * Parse XML in a char buffer. - * @param buf a character buffer to parse - * @param pos position to start parsing - * @param len number of chars, from pos, to parse. - * @return a pointer to the root of the XML document; - */ - Element *parse(const char *buf,int pos,int len); - - /** - * Parse XML in a char buffer. - * @param buf a character buffer to parse - * @param pos position to start parsing - * @param len number of chars, from pos, to parse. - * @return a pointer to the root of the XML document; - */ - Element *parse(const String &buf); - - /** - * Parse a named XML file. The file is loaded like a data file; - * the original format is not preserved. - * @param fileName the name of the file to read - * @return a pointer to the root of the XML document; - */ - Element *parseFile(const String &fileName); - - /** - * Utility method to preprocess a string for XML - * output, escaping its entities. - * @param str the string to encode - */ - static String encode(const String &str); - - /** - * Removes whitespace from beginning and end of a string - */ - String trim(const String &s); - -private: - - void init() - { - keepGoing = true; - currentNode = NULL; - parselen = 0; - parsebuf = NULL; - currentPosition = 0; - } - - int countLines(int begin, int end); - - void getLineAndColumn(int pos, int *lineNr, int *colNr); - - void error(const char *fmt, ...); - - int peek(int pos); - - int match(int pos, const char *text); - - int skipwhite(int p); - - int getWord(int p0, String &buf); - - int getQuoted(int p0, String &buf, int do_i_parse); - - int parseVersion(int p0); - - int parseDoctype(int p0); - - int parseElement(int p0, Element *par,int depth); - - Element *parse(XMLCh *buf,int pos,int len); - - bool keepGoing; - Element *currentNode; - int parselen; - XMLCh *parsebuf; - String cdatabuf; - int currentPosition; -}; - - - - -//######################################################################## -//# E L E M E N T -//######################################################################## - -Element *Element::clone() -{ - Element *elem = new Element(name, value); - elem->parent = parent; - elem->attributes = attributes; - elem->namespaces = namespaces; - elem->line = line; - - std::vector<Element *>::iterator iter; - for (iter = children.begin(); iter != children.end() ; iter++) - { - elem->addChild((*iter)->clone()); - } - return elem; -} - - -void Element::findElementsRecursive(std::vector<Element *>&res, const String &name) -{ - if (getName() == name) - { - res.push_back(this); - } - for (std::size_t i=0; i<children.size() ; i++) - children[i]->findElementsRecursive(res, name); -} - -std::vector<Element *> Element::findElements(const String &name) -{ - std::vector<Element *> res; - findElementsRecursive(res, name); - return res; -} - -String Element::getAttribute(const String &name) -{ - for (std::size_t i=0 ; i<attributes.size() ; i++) - if (attributes[i].getName() ==name) - return attributes[i].getValue(); - return ""; -} - -String Element::getTagAttribute(const String &tagName, const String &attrName) -{ - std::vector<Element *>elems = findElements(tagName); - if (elems.size() <1) - return ""; - String res = elems[0]->getAttribute(attrName); - return res; -} - -String Element::getTagValue(const String &tagName) -{ - std::vector<Element *>elems = findElements(tagName); - if (elems.size() <1) - return ""; - String res = elems[0]->getValue(); - return res; -} - -void Element::addChild(Element *child) -{ - if (!child) - return; - child->parent = this; - children.push_back(child); -} - - -void Element::addAttribute(const String &name, const String &value) -{ - Attribute attr(name, value); - attributes.push_back(attr); -} - -void Element::addNamespace(const String &prefix, const String &namespaceURI) -{ - Namespace ns(prefix, namespaceURI); - namespaces.push_back(ns); -} - -void Element::writeIndentedRecursive(FILE *f, int indent) -{ - int i; - if (!f) - return; - //Opening tag, and attributes - for (i=0;i<indent;i++) - fputc(' ',f); - fprintf(f,"<%s",name.c_str()); - for (std::size_t i=0 ; i<attributes.size() ; i++) - { - fprintf(f," %s=\"%s\"", - attributes[i].getName().c_str(), - attributes[i].getValue().c_str()); - } - for (std::size_t i=0 ; i<namespaces.size() ; i++) - { - fprintf(f," xmlns:%s=\"%s\"", - namespaces[i].getPrefix().c_str(), - namespaces[i].getNamespaceURI().c_str()); - } - fprintf(f,">\n"); - - //Between the tags - if (value.size() > 0) - { - for (int i=0;i<indent;i++) - fputc(' ', f); - fprintf(f," %s\n", value.c_str()); - } - - for (std::size_t i=0 ; i<children.size() ; i++) - children[i]->writeIndentedRecursive(f, indent+2); - - //Closing tag - for (int i=0; i<indent; i++) - fputc(' ',f); - fprintf(f,"</%s>\n", name.c_str()); -} - -void Element::writeIndented(FILE *f) -{ - writeIndentedRecursive(f, 0); -} - -void Element::print() -{ - writeIndented(stdout); -} - - -//######################################################################## -//# P A R S E R -//######################################################################## - - - -typedef struct - { - const char *escaped; - char value; - } EntityEntry; - -static EntityEntry entities[] = -{ - { "&" , '&' }, - { "<" , '<' }, - { ">" , '>' }, - { "'", '\'' }, - { """, '"' }, - { NULL , '\0' } -}; - - - -/** - * Removes whitespace from beginning and end of a string - */ -String Parser::trim(const String &s) -{ - if (s.size() < 1) - return s; - - //Find first non-ws char - std::size_t begin = 0; - for ( ; begin < s.size() ; begin++) - { - if (!isspace(s[begin])) - break; - } - - //Find first non-ws char, going in reverse - std::size_t end = s.size() - 1; - for ( ; end > begin ; end--) - { - if (!isspace(s[end])) - break; - } - //trace("begin:%d end:%d", begin, end); - - String res = s.substr(begin, end-begin+1); - return res; -} - - -int Parser::countLines(int begin, int end) -{ - int count = 0; - for (int i=begin ; i<end ; i++) - { - XMLCh ch = parsebuf[i]; - if (ch == '\n' || ch == '\r') - count++; - } - return count; -} - - -void Parser::getLineAndColumn(int pos, int *lineNr, int *colNr) -{ - int line = 1; - int col = 1; - for (long i=0 ; i<pos ; i++) - { - XMLCh ch = parsebuf[i]; - if (ch == '\n' || ch == '\r') - { - col = 0; - line ++; - } - else - col++; - } - *lineNr = line; - *colNr = col; - -} - - -void Parser::error(const char *fmt, ...) -{ - int lineNr; - int colNr; - getLineAndColumn(currentPosition, &lineNr, &colNr); - va_list args; - fprintf(stderr, "xml error at line %d, column %d:", lineNr, colNr); - va_start(args,fmt); - vfprintf(stderr,fmt,args); - va_end(args) ; - fprintf(stderr, "\n"); -} - - - -int Parser::peek(int pos) -{ - if (pos >= parselen) - return -1; - currentPosition = pos; - int ch = parsebuf[pos]; - //printf("ch:%c\n", ch); - return ch; -} - - - -String Parser::encode(const String &str) -{ - String ret; - for (std::size_t i=0 ; i<str.size() ; i++) - { - XMLCh ch = (XMLCh)str[i]; - if (ch == '&') - ret.append("&"); - else if (ch == '<') - ret.append("<"); - else if (ch == '>') - ret.append(">"); - else if (ch == '\'') - ret.append("'"); - else if (ch == '"') - ret.append("""); - else - ret.push_back(ch); - - } - return ret; -} - - -int Parser::match(int p0, const char *text) -{ - int p = p0; - while (*text) - { - if (peek(p) != *text) - return p0; - p++; text++; - } - return p; -} - - - -int Parser::skipwhite(int p) -{ - - while (p<parselen) - { - int p2 = match(p, "<!--"); - if (p2 > p) - { - p = p2; - while (p<parselen) - { - p2 = match(p, "-->"); - if (p2 > p) - { - p = p2; - break; - } - p++; - } - } - XMLCh b = peek(p); - if (!isspace(b)) - break; - p++; - } - return p; -} - -/* modify this to allow all chars for an element or attribute name*/ -int Parser::getWord(int p0, String &buf) -{ - int p = p0; - while (p<parselen) - { - XMLCh b = peek(p); - if (b<=' ' || b=='/' || b=='>' || b=='=') - break; - buf.push_back(b); - p++; - } - return p; -} - -int Parser::getQuoted(int p0, String &buf, int do_i_parse) -{ - - int p = p0; - if (peek(p) != '"' && peek(p) != '\'') - return p0; - p++; - - while ( p<parselen ) - { - XMLCh b = peek(p); - if (b=='"' || b=='\'') - break; - if (b=='&' && do_i_parse) - { - bool found = false; - for (EntityEntry *ee = entities ; ee->value ; ee++) - { - int p2 = match(p, ee->escaped); - if (p2>p) - { - buf.push_back(ee->value); - p = p2; - found = true; - break; - } - } - if (!found) - { - error("unterminated entity"); - return false; - } - } - else - { - buf.push_back(b); - p++; - } - } - return p; -} - -int Parser::parseVersion(int p0) -{ - //printf("### parseVersion: %d\n", p0); - - int p = p0; - - p = skipwhite(p0); - - if (peek(p) != '<') - return p0; - - p++; - if (p>=parselen || peek(p)!='?') - return p0; - - p++; - - String buf; - - while (p<parselen) - { - XMLCh ch = peek(p); - if (ch=='?') - { - p++; - break; - } - buf.push_back(ch); - p++; - } - - if (peek(p) != '>') - return p0; - p++; - - //printf("Got version:%s\n",buf.c_str()); - return p; -} - -int Parser::parseDoctype(int p0) -{ - //printf("### parseDoctype: %d\n", p0); - - int p = p0; - p = skipwhite(p); - - if (p>=parselen || peek(p)!='<') - return p0; - - p++; - - if (peek(p)!='!' || peek(p+1)=='-') - return p0; - p++; - - String buf; - while (p<parselen) - { - XMLCh ch = peek(p); - if (ch=='>') - { - p++; - break; - } - buf.push_back(ch); - p++; - } - - //printf("Got doctype:%s\n",buf.c_str()); - return p; -} - - - -int Parser::parseElement(int p0, Element *par,int lineNr) -{ - - int p = p0; - - int p2 = p; - - p = skipwhite(p); - - //## Get open tag - XMLCh ch = peek(p); - if (ch!='<') - return p0; - - //int line, col; - //getLineAndColumn(p, &line, &col); - - p++; - - String openTagName; - p = skipwhite(p); - p = getWord(p, openTagName); - //printf("####tag :%s\n", openTagName.c_str()); - p = skipwhite(p); - - //Add element to tree - Element *n = new Element(openTagName); - n->line = lineNr + countLines(p0, p); - n->parent = par; - par->addChild(n); - - // Get attributes - if (peek(p) != '>') - { - while (p<parselen) - { - p = skipwhite(p); - ch = peek(p); - //printf("ch:%c\n",ch); - if (ch=='>') - break; - else if (ch=='/' && p<parselen+1) - { - p++; - p = skipwhite(p); - ch = peek(p); - if (ch=='>') - { - p++; - //printf("quick close\n"); - return p; - } - } - String attrName; - p2 = getWord(p, attrName); - if (p2==p) - break; - //printf("name:%s",buf); - p=p2; - p = skipwhite(p); - ch = peek(p); - //printf("ch:%c\n",ch); - if (ch!='=') - break; - p++; - p = skipwhite(p); - // ch = parsebuf[p]; - // printf("ch:%c\n",ch); - String attrVal; - p2 = getQuoted(p, attrVal, true); - p=p2+1; - //printf("name:'%s' value:'%s'\n",attrName.c_str(),attrVal.c_str()); - char *namestr = (char *)attrName.c_str(); - if (strncmp(namestr, "xmlns:", 6)==0) - n->addNamespace(attrName, attrVal); - else - n->addAttribute(attrName, attrVal); - } - } - - bool cdata = false; - - p++; - // ### Get intervening data ### */ - String data; - while (p<parselen) - { - //# COMMENT - p2 = match(p, "<!--"); - if (!cdata && p2>p) - { - p = p2; - while (p<parselen) - { - p2 = match(p, "-->"); - if (p2 > p) - { - p = p2; - break; - } - p++; - } - } - - ch = peek(p); - //# END TAG - if (ch=='<' && !cdata && peek(p+1)=='/') - { - break; - } - //# CDATA - p2 = match(p, "<![CDATA["); - if (p2 > p) - { - cdata = true; - p = p2; - continue; - } - - //# CHILD ELEMENT - if (ch == '<') - { - p2 = parseElement(p, n, lineNr + countLines(p0, p)); - if (p2 == p) - { - /* - printf("problem on element:%s. p2:%d p:%d\n", - openTagName.c_str(), p2, p); - */ - return p0; - } - p = p2; - continue; - } - //# ENTITY - if (ch=='&' && !cdata) - { - bool found = false; - for (EntityEntry *ee = entities ; ee->value ; ee++) - { - int p2 = match(p, ee->escaped); - if (p2>p) - { - data.push_back(ee->value); - p = p2; - found = true; - break; - } - } - if (!found) - { - error("unterminated entity"); - return -1; - } - continue; - } - - //# NONE OF THE ABOVE - data.push_back(ch); - p++; - }/*while*/ - - - n->value = data; - //printf("%d : data:%s\n",p,data.c_str()); - - //## Get close tag - p = skipwhite(p); - ch = peek(p); - if (ch != '<') - { - error("no < for end tag\n"); - return p0; - } - p++; - ch = peek(p); - if (ch != '/') - { - error("no / on end tag"); - return p0; - } - p++; - ch = peek(p); - p = skipwhite(p); - String closeTagName; - p = getWord(p, closeTagName); - if (openTagName != closeTagName) - { - error("Mismatched closing tag. Expected </%S>. Got '%S'.", - openTagName.c_str(), closeTagName.c_str()); - return p0; - } - p = skipwhite(p); - if (peek(p) != '>') - { - error("no > on end tag for '%s'", closeTagName.c_str()); - return p0; - } - p++; - // printf("close element:%s\n",closeTagName.c_str()); - p = skipwhite(p); - return p; -} - - - - -Element *Parser::parse(XMLCh *buf,int pos,int len) -{ - parselen = len; - parsebuf = buf; - Element *rootNode = new Element("root"); - pos = parseVersion(pos); - pos = parseDoctype(pos); - pos = parseElement(pos, rootNode, 1); - return rootNode; -} - - -Element *Parser::parse(const char *buf, int pos, int len) -{ - XMLCh *charbuf = new XMLCh[len + 1]; - long i = 0; - for ( ; i < len ; i++) - charbuf[i] = (XMLCh)buf[i]; - charbuf[i] = '\0'; - - Element *n = parse(charbuf, pos, len); - delete[] charbuf; - return n; -} - -Element *Parser::parse(const String &buf) -{ - long len = (long)buf.size(); - XMLCh *charbuf = new XMLCh[len + 1]; - long i = 0; - for ( ; i < len ; i++) - charbuf[i] = (XMLCh)buf[i]; - charbuf[i] = '\0'; - - Element *n = parse(charbuf, 0, len); - delete[] charbuf; - return n; -} - -Element *Parser::parseFile(const String &fileName) -{ - - //##### LOAD INTO A CHAR BUF, THEN CONVERT TO XMLCh - FILE *f = fopen(fileName.c_str(), "rb"); - if (!f) - return NULL; - - struct stat statBuf; - if (fstat(fileno(f),&statBuf)<0) - { - fclose(f); - return NULL; - } - long filelen = statBuf.st_size; - - //printf("length:%d\n",filelen); - XMLCh *charbuf = new XMLCh[filelen + 1]; - for (XMLCh *p=charbuf ; !feof(f) ; p++) - { - *p = (XMLCh)fgetc(f); - } - fclose(f); - charbuf[filelen] = '\0'; - - - /* - printf("nrbytes:%d\n",wc_count); - printf("buf:%ls\n======\n",charbuf); - */ - Element *n = parse(charbuf, 0, filelen); - delete[] charbuf; - return n; -} - -//######################################################################## -//######################################################################## -//## E N D X M L -//######################################################################## -//######################################################################## - - - - - - -//######################################################################## -//######################################################################## -//## U R I -//######################################################################## -//######################################################################## - -//This would normally be a call to a UNICODE function -#define isLetter(x) isalpha(x) - -/** - * A class that implements the W3C URI resource reference. - */ -class URI -{ -public: - - typedef enum - { - SCHEME_NONE =0, - SCHEME_DATA, - SCHEME_HTTP, - SCHEME_HTTPS, - SCHEME_FTP, - SCHEME_FILE, - SCHEME_LDAP, - SCHEME_MAILTO, - SCHEME_NEWS, - SCHEME_TELNET - } SchemeTypes; - - /** - * - */ - URI() - { - init(); - } - - /** - * - */ - URI(const String &str) - { - init(); - parse(str); - } - - - /** - * - */ - URI(const char *str) - { - init(); - String domStr = str; - parse(domStr); - } - - - /** - * - */ - URI(const URI &other) - { - init(); - assign(other); - } - - - /** - * - */ - URI &operator=(const URI &other) - { - init(); - assign(other); - return *this; - } - - - /** - * - */ - virtual ~URI() - {} - - - - /** - * - */ - virtual bool parse(const String &str); - - /** - * - */ - virtual String toString() const; - - /** - * - */ - virtual int getScheme() const; - - /** - * - */ - virtual String getSchemeStr() const; - - /** - * - */ - virtual String getAuthority() const; - - /** - * Same as getAuthority, but if the port has been specified - * as host:port , the port will not be included - */ - virtual String getHost() const; - - /** - * - */ - virtual int getPort() const; - - /** - * - */ - virtual String getPath() const; - - /** - * - */ - virtual String getNativePath() const; - - /** - * - */ - virtual bool isAbsolute() const; - - /** - * - */ - virtual bool isOpaque() const; - - /** - * - */ - virtual String getQuery() const; - - /** - * - */ - virtual String getFragment() const; - - /** - * - */ - virtual URI resolve(const URI &other) const; - - /** - * - */ - virtual void normalize(); - -private: - - /** - * - */ - void init() - { - parsebuf = NULL; - parselen = 0; - scheme = SCHEME_NONE; - schemeStr = ""; - port = 0; - authority = ""; - path = ""; - absolute = false; - opaque = false; - query = ""; - fragment = ""; - } - - - /** - * - */ - void assign(const URI &other) - { - scheme = other.scheme; - schemeStr = other.schemeStr; - authority = other.authority; - port = other.port; - path = other.path; - absolute = other.absolute; - opaque = other.opaque; - query = other.query; - fragment = other.fragment; - } - - int scheme; - - String schemeStr; - - String authority; - - bool portSpecified; - - int port; - - String path; - - bool absolute; - - bool opaque; - - String query; - - String fragment; - - void error(const char *fmt, ...); - - void trace(const char *fmt, ...); - - - int peek(int p); - - int match(int p, const char *key); - - int parseScheme(int p); - - int parseHierarchicalPart(int p0); - - int parseQuery(int p0); - - int parseFragment(int p0); - - int parse(int p); - - char *parsebuf; - - int parselen; - -}; - - - -typedef struct -{ - int ival; - const char *sval; - int port; -} LookupEntry; - -LookupEntry schemes[] = -{ - { URI::SCHEME_DATA, "data:", 0 }, - { URI::SCHEME_HTTP, "http:", 80 }, - { URI::SCHEME_HTTPS, "https:", 443 }, - { URI::SCHEME_FTP, "ftp", 12 }, - { URI::SCHEME_FILE, "file:", 0 }, - { URI::SCHEME_LDAP, "ldap:", 123 }, - { URI::SCHEME_MAILTO, "mailto:", 25 }, - { URI::SCHEME_NEWS, "news:", 117 }, - { URI::SCHEME_TELNET, "telnet:", 23 }, - { 0, NULL, 0 } -}; - - -String URI::toString() const -{ - String str = schemeStr; - if (authority.size() > 0) - { - str.append("//"); - str.append(authority); - } - str.append(path); - if (query.size() > 0) - { - str.append("?"); - str.append(query); - } - if (fragment.size() > 0) - { - str.append("#"); - str.append(fragment); - } - return str; -} - - -int URI::getScheme() const -{ - return scheme; -} - -String URI::getSchemeStr() const -{ - return schemeStr; -} - - -String URI::getAuthority() const -{ - String ret = authority; - if (portSpecified && port>=0) - { - char buf[7]; - snprintf(buf, 6, ":%6d", port); - ret.append(buf); - } - return ret; -} - -String URI::getHost() const -{ - return authority; -} - -int URI::getPort() const -{ - return port; -} - - -String URI::getPath() const -{ - return path; -} - -String URI::getNativePath() const -{ - String npath; -#ifdef __WIN32__ - std::size_t firstChar = 0; - if (path.size() >= 3) - { - if (path[0] == '/' && - isLetter(path[1]) && - path[2] == ':') - firstChar++; - } - for (std::size_t i=firstChar ; i<path.size() ; i++) - { - XMLCh ch = (XMLCh) path[i]; - if (ch == '/') - npath.push_back((XMLCh)'\\'); - else - npath.push_back(ch); - } -#else - npath = path; -#endif - return npath; -} - - -bool URI::isAbsolute() const -{ - return absolute; -} - -bool URI::isOpaque() const -{ - return opaque; -} - - -String URI::getQuery() const -{ - return query; -} - - -String URI::getFragment() const -{ - return fragment; -} - - -URI URI::resolve(const URI &other) const -{ - //### According to w3c, this is handled in 3 cases - - //## 1 - if (opaque || other.isAbsolute()) - return other; - - //## 2 - if (other.fragment.size() > 0 && - other.path.size() == 0 && - other.scheme == SCHEME_NONE && - other.authority.size() == 0 && - other.query.size() == 0 ) - { - URI fragUri = *this; - fragUri.fragment = other.fragment; - return fragUri; - } - - //## 3 http://www.ietf.org/rfc/rfc2396.txt, section 5.2 - URI newUri; - //# 3.1 - newUri.scheme = scheme; - newUri.schemeStr = schemeStr; - newUri.query = other.query; - newUri.fragment = other.fragment; - if (other.authority.size() > 0) - { - //# 3.2 - if (absolute || other.absolute) - newUri.absolute = true; - newUri.authority = other.authority; - newUri.port = other.port;//part of authority - newUri.path = other.path; - } - else - { - //# 3.3 - if (other.absolute) - { - newUri.absolute = true; - newUri.path = other.path; - } - else - { - std::size_t pos = path.find_last_of('/'); - if (pos != path.npos) - { - String tpath = path.substr(0, pos+1); - tpath.append(other.path); - newUri.path = tpath; - } - else - newUri.path = other.path; - } - } - - newUri.normalize(); - return newUri; -} - - - -/** - * This follows the Java URI algorithm: - * 1. All "." segments are removed. - * 2. If a ".." segment is preceded by a non-".." segment - * then both of these segments are removed. This step - * is repeated until it is no longer applicable. - * 3. If the path is relative, and if its first segment - * contains a colon character (':'), then a "." segment - * is prepended. This prevents a relative URI with a path - * such as "a:b/c/d" from later being re-parsed as an - * opaque URI with a scheme of "a" and a scheme-specific - * part of "b/c/d". (Deviation from RFC 2396) - */ -void URI::normalize() -{ - std::vector<String> segments; - - //## Collect segments - if (path.size()<2) - return; - bool abs = false; - std::size_t pos=0; - if (path[0]=='/') - { - abs = true; - pos++; - } - while (pos < path.size()) - { - std::size_t pos2 = path.find('/', pos); - if (pos2==path.npos) - { - String seg = path.substr(pos); - //printf("last segment:%s\n", seg.c_str()); - segments.push_back(seg); - break; - } - if (pos2>pos) - { - String seg = path.substr(pos, pos2-pos); - //printf("segment:%s\n", seg.c_str()); - segments.push_back(seg); - } - pos = pos2; - pos++; - } - - //## Clean up (normalize) segments - bool edited = false; - std::vector<String>::iterator iter; - for (iter=segments.begin() ; iter!=segments.end() ; ) - { - String s = *iter; - if (s == ".") - { - iter = segments.erase(iter); - edited = true; - } - else if (s == ".." && - iter != segments.begin() && - *(iter-1) != "..") - { - iter--; //back up, then erase two entries - iter = segments.erase(iter); - iter = segments.erase(iter); - edited = true; - } - else - iter++; - } - - //## Rebuild path, if necessary - if (edited) - { - path.clear(); - if (abs) - { - path.append("/"); - } - std::vector<String>::iterator iter; - for (iter=segments.begin() ; iter!=segments.end() ; iter++) - { - if (iter != segments.begin()) - path.append("/"); - path.append(*iter); - } - } - -} - - - -//######################################################################### -//# M E S S A G E S -//######################################################################### - -void URI::error(const char *fmt, ...) -{ - va_list args; - fprintf(stderr, "URI error: "); - va_start(args, fmt); - vfprintf(stderr, fmt, args); - va_end(args); - fprintf(stderr, "\n"); -} - -void URI::trace(const char *fmt, ...) -{ - va_list args; - fprintf(stdout, "URI: "); - va_start(args, fmt); - vfprintf(stdout, fmt, args); - va_end(args); - fprintf(stdout, "\n"); -} - - - - -//######################################################################### -//# P A R S I N G -//######################################################################### - - - -int URI::peek(int p) -{ - if (p<0 || p>=parselen) - return -1; - return parsebuf[p]; -} - - - -int URI::match(int p0, const char *key) -{ - int p = p0; - while (p < parselen) - { - if (*key == '\0') - return p; - else if (*key != parsebuf[p]) - break; - p++; key++; - } - return p0; -} - -//######################################################################### -//# Parsing is performed according to: -//# http://www.gbiv.com/protocols/uri/rfc/rfc3986.html#components -//######################################################################### - -int URI::parseScheme(int p0) -{ - int p = p0; - for (LookupEntry *entry = schemes; entry->sval ; entry++) - { - int p2 = match(p, entry->sval); - if (p2 > p) - { - schemeStr = entry->sval; - scheme = entry->ival; - port = entry->port; - p = p2; - return p; - } - } - - return p; -} - - -int URI::parseHierarchicalPart(int p0) -{ - int p = p0; - int ch; - - //# Authority field (host and port, for example) - int p2 = match(p, "//"); - if (p2 > p) - { - p = p2; - portSpecified = false; - String portStr; - while (p < parselen) - { - ch = peek(p); - if (ch == '/') - break; - else if (ch == ':') - portSpecified = true; - else if (portSpecified) - portStr.push_back((XMLCh)ch); - else - authority.push_back((XMLCh)ch); - p++; - } - if (portStr.size() > 0) - { - char *pstr = (char *)portStr.c_str(); - char *endStr; - long val = strtol(pstr, &endStr, 10); - if (endStr > pstr) //successful parse? - port = val; - } - } - - //# Are we absolute? - ch = peek(p); - if (isLetter(ch) && peek(p+1)==':') - { - absolute = true; - path.push_back((XMLCh)'/'); - } - else if (ch == '/') - { - absolute = true; - if (p>p0) //in other words, if '/' is not the first char - opaque = true; - path.push_back((XMLCh)ch); - p++; - } - - while (p < parselen) - { - ch = peek(p); - if (ch == '?' || ch == '#') - break; - path.push_back((XMLCh)ch); - p++; - } - - return p; -} - -int URI::parseQuery(int p0) -{ - int p = p0; - int ch = peek(p); - if (ch != '?') - return p0; - - p++; - while (p < parselen) - { - ch = peek(p); - if (ch == '#') - break; - query.push_back((XMLCh)ch); - p++; - } - - - return p; -} - -int URI::parseFragment(int p0) -{ - - int p = p0; - int ch = peek(p); - if (ch != '#') - return p0; - - p++; - while (p < parselen) - { - ch = peek(p); - if (ch == '?') - break; - fragment.push_back((XMLCh)ch); - p++; - } - - - return p; -} - - -int URI::parse(int p0) -{ - - int p = p0; - - int p2 = parseScheme(p); - if (p2 < 0) - { - error("Scheme"); - return -1; - } - p = p2; - - - p2 = parseHierarchicalPart(p); - if (p2 < 0) - { - error("Hierarchical part"); - return -1; - } - p = p2; - - p2 = parseQuery(p); - if (p2 < 0) - { - error("Query"); - return -1; - } - p = p2; - - - p2 = parseFragment(p); - if (p2 < 0) - { - error("Fragment"); - return -1; - } - p = p2; - - return p; - -} - - - -bool URI::parse(const String &str) -{ - init(); - - parselen = str.size(); - - String tmp; - for (std::size_t i=0 ; i<str.size() ; i++) - { - XMLCh ch = (XMLCh) str[i]; - if (ch == '\\') - tmp.push_back((XMLCh)'/'); - else - tmp.push_back(ch); - } - parsebuf = (char *) tmp.c_str(); - - - int p = parse(0); - normalize(); - - if (p < 0) - { - error("Syntax error"); - return false; - } - - //printf("uri:%s\n", toString().c_str()); - //printf("path:%s\n", path.c_str()); - - return true; - -} - - - - - - - - -//######################################################################## -//######################################################################## -//## M A K E -//######################################################################## -//######################################################################## - -//######################################################################## -//# Stat cache to speed up stat requests -//######################################################################## -struct StatResult { - int result; - struct stat statInfo; -}; -typedef std::map<String, StatResult> statCacheType; -static statCacheType statCache; -static int cachedStat(const String &f, struct stat *s) { - //printf("Stat path: %s\n", f.c_str()); - std::pair<statCacheType::iterator, bool> result = statCache.insert(statCacheType::value_type(f, StatResult())); - if (result.second) { - result.first->second.result = stat(f.c_str(), &(result.first->second.statInfo)); - } - *s = result.first->second.statInfo; - return result.first->second.result; -} -static void removeFromStatCache(const String f) { - //printf("Removing from cache: %s\n", f.c_str()); - statCache.erase(f); -} - -//######################################################################## -//# Dir cache to speed up dir requests -//######################################################################## -/*struct DirListing { - bool available; - std::vector<String> files; - std::vector<String> dirs; -}; -typedef std::map<String, DirListing > dirCacheType; -static dirCacheType dirCache; -static const DirListing &cachedDir(String fullDir) -{ - String dirNative = getNativePath(fullDir); - std::pair<dirCacheType::iterator,bool> result = dirCache.insert(dirCacheType::value_type(dirNative, DirListing())); - if (result.second) { - DIR *dir = opendir(dirNative.c_str()); - if (!dir) - { - error("Could not open directory %s : %s", - dirNative.c_str(), strerror(errno)); - result.first->second.available = false; - } - else - { - result.first->second.available = true; - while (true) - { - struct dirent *de = readdir(dir); - if (!de) - break; - - //Get the directory member name - String s = de->d_name; - if (s.size() == 0 || s[0] == '.') - continue; - String childName; - if (dirName.size()>0) - { - childName.append(dirName); - childName.append("/"); - } - childName.append(s); - String fullChild = baseDir; - fullChild.append("/"); - fullChild.append(childName); - - if (isDirectory(fullChild)) - { - //trace("directory: %s", childName.c_str()); - if (!listFiles(baseDir, childName, res)) - return false; - continue; - } - else if (!isRegularFile(fullChild)) - { - error("unknown file:%s", childName.c_str()); - return false; - } - - //all done! - res.push_back(childName); - - } - closedir(dir); - } - } - return result.first->second; -}*/ - -//######################################################################## -//# F I L E S E T -//######################################################################## -/** - * This is the descriptor for a <fileset> item - */ -class FileSet -{ -public: - - /** - * - */ - FileSet() - {} - - /** - * - */ - FileSet(const FileSet &other) - { assign(other); } - - /** - * - */ - FileSet &operator=(const FileSet &other) - { assign(other); return *this; } - - /** - * - */ - virtual ~FileSet() - {} - - /** - * - */ - String getDirectory() const - { return directory; } - - /** - * - */ - void setDirectory(const String &val) - { directory = val; } - - /** - * - */ - void setFiles(const std::vector<String> &val) - { files = val; } - - /** - * - */ - std::vector<String> getFiles() const - { return files; } - - /** - * - */ - void setIncludes(const std::vector<String> &val) - { includes = val; } - - /** - * - */ - std::vector<String> getIncludes() const - { return includes; } - - /** - * - */ - void setExcludes(const std::vector<String> &val) - { excludes = val; } - - /** - * - */ - std::vector<String> getExcludes() const - { return excludes; } - - /** - * - */ - std::size_t size() const - { return files.size(); } - - /** - * - */ - String operator[](int index) const - { return files[index]; } - - /** - * - */ - void clear() - { - directory = ""; - files.clear(); - includes.clear(); - excludes.clear(); - } - - -private: - - void assign(const FileSet &other) - { - directory = other.directory; - files = other.files; - includes = other.includes; - excludes = other.excludes; - } - - String directory; - std::vector<String> files; - std::vector<String> includes; - std::vector<String> excludes; -}; - - -//######################################################################## -//# F I L E L I S T -//######################################################################## -/** - * This is a simpler, explicitly-named list of files - */ -class FileList -{ -public: - - /** - * - */ - FileList() - {} - - /** - * - */ - FileList(const FileList &other) - { assign(other); } - - /** - * - */ - FileList &operator=(const FileList &other) - { assign(other); return *this; } - - /** - * - */ - virtual ~FileList() - {} - - /** - * - */ - String getDirectory() - { return directory; } - - /** - * - */ - void setDirectory(const String &val) - { directory = val; } - - /** - * - */ - void setFiles(const std::vector<String> &val) - { files = val; } - - /** - * - */ - std::vector<String> getFiles() - { return files; } - - /** - * - */ - std::size_t size() - { return files.size(); } - - /** - * - */ - String operator[](int index) - { return files[index]; } - - /** - * - */ - void clear() - { - directory = ""; - files.clear(); - } - - -private: - - void assign(const FileList &other) - { - directory = other.directory; - files = other.files; - } - - String directory; - std::vector<String> files; -}; - - - - -//######################################################################## -//# M A K E B A S E -//######################################################################## -/** - * Base class for all classes in this file - */ -class MakeBase -{ -public: - - MakeBase() - { line = 0; } - virtual ~MakeBase() - {} - - /** - * Return the URI of the file associated with this object - */ - URI getURI() - { return uri; } - - /** - * Set the uri to the given string - */ - void setURI(const String &uristr) - { uri.parse(uristr); } - - /** - * Set the number of threads that can be used - */ - void setNumThreads(const int num) - { numThreads = num; } - - /** - * Resolve another path relative to this one - */ - String resolve(const String &otherPath); - - /** - * replace variable refs like ${a} with their values - * Assume that the string has already been syntax validated - */ - String eval(const String &s, const String &defaultVal); - - /** - * replace variable refs like ${a} with their values - * return true or false - * Assume that the string has already been syntax validated - */ - bool evalBool(const String &s, bool defaultVal); - - /** - * replace variable refs like ${a} with their values - * return the value parsed as an integer - * Assume that the string has already been syntax validated - */ - int evalInt(const String &s, int defaultVal); - - /** - * Get an element attribute, performing substitutions if necessary - */ - bool getAttribute(Element *elem, const String &name, String &result); - - /** - * Get an element value, performing substitutions if necessary - */ - bool getValue(Element *elem, String &result); - - /** - * Set the current line number in the file - */ - void setLine(int val) - { line = val; } - - /** - * Get the current line number in the file - */ - int getLine() - { return line; } - - - /** - * Set a property to a given value - */ - virtual void setProperty(const String &name, const String &val) - { - properties[name] = val; - } - - /** - * Return a named property is found, else a null string - */ - virtual String getProperty(const String &name) - { - String val; - std::map<String, String>::iterator iter = properties.find(name); - if (iter != properties.end()) - val = iter->second; - String sval; - if (!getSubstitutions(val, sval)) - return String(); - return sval; - } - - /** - * Return true if a named property is found, else false - */ - virtual bool hasProperty(const String &name) - { - std::map<String, String>::iterator iter = properties.find(name); - if (iter == properties.end()) - return false; - return true; - } - - -protected: - - /** - * The path to the file associated with this object - */ - URI uri; - - /** - * The number of threads that can be used - */ - static int numThreads; - - /** - * If this prefix is seen in a substitution, use an environment - * variable. - * example: <property environment="env"/> - * ${env.JAVA_HOME} - */ - String envPrefix; - - /** - * If this prefix is seen in a substitution, use as a - * pkg-config 'all' query - * example: <property pkg-config="pc"/> - * ${pc.gtkmm} - */ - String pcPrefix; - - /** - * If this prefix is seen in a substitution, use as a - * pkg-config 'cflags' query - * example: <property pkg-config="pcc"/> - * ${pcc.gtkmm} - */ - String pccPrefix; - - /** - * If this prefix is seen in a substitution, use as a - * pkg-config 'libs' query - * example: <property pkg-config-libs="pcl"/> - * ${pcl.gtkmm} - */ - String pclPrefix; - - /** - * If this prefix is seen in a substitution, use as a - * Bazaar "bzr revno" query - * example: <property subversion="svn"/> ??? - * ${bzr.Revision} - */ - String bzrPrefix; - - - - - - /** - * Print a printf()-like formatted error message - */ - void error(const char *fmt, ...); - - /** - * Print a printf()-like formatted trace message - */ - void status(const char *fmt, ...); - - /** - * Show target status - */ - void targetstatus(const char *fmt, ...); - - /** - * Print a printf()-like formatted trace message - */ - void trace(const char *fmt, ...); - - /** - * Check if a given string matches a given regex pattern - */ - bool regexMatch(const String &str, const String &pattern); - - /** - * - */ - String getSuffix(const String &fname); - - /** - * Break up a string into substrings delimited the characters - * in delimiters. Null-length substrings are ignored - */ - std::vector<String> tokenize(const String &val, - const String &delimiters); - - /** - * replace runs of whitespace with a space - */ - String strip(const String &s); - - /** - * remove leading whitespace from each line - */ - String leftJustify(const String &s); - - /** - * remove leading and trailing whitespace from string - */ - String trim(const String &s); - - /** - * Return a lower case version of the given string - */ - String toLower(const String &s); - - /** - * Return the native format of the canonical - * path which we store - */ - String getNativePath(const String &path); - - /** - * Execute a shell command. Outbuf is a ref to a string - * to catch the result. - */ - bool executeCommand(const String &call, - const String &inbuf, - String &outbuf, - String &errbuf); - /** - * List all directories in a given base and starting directory - * It is usually called like: - * bool ret = listDirectories("src", "", result); - */ - bool listDirectories(const String &baseName, - const String &dirname, - std::vector<String> &res); - - /** - * Find all files in the named directory - */ - bool listFiles(const String &baseName, - const String &dirname, - std::vector<String> &result); - - /** - * Perform a listing for a fileset - */ - bool listFiles(MakeBase &propRef, FileSet &fileSet); - - /** - * Parse a <patternset> - */ - bool parsePatternSet(Element *elem, - MakeBase &propRef, - std::vector<String> &includes, - std::vector<String> &excludes); - - /** - * Parse a <fileset> entry, and determine which files - * should be included - */ - bool parseFileSet(Element *elem, - MakeBase &propRef, - FileSet &fileSet); - /** - * Parse a <filelist> entry - */ - bool parseFileList(Element *elem, - MakeBase &propRef, - FileList &fileList); - - /** - * Return this object's property list - */ - virtual std::map<String, String> &getProperties() - { return properties; } - - - std::map<String, String> properties; - - /** - * Create a directory, making intermediate dirs - * if necessary - */ - bool createDirectory(const String &dirname); - - /** - * Delete a directory and its children if desired - */ - bool removeDirectory(const String &dirName); - - /** - * Copy a file from one name to another. Perform only if needed - */ - bool copyFile(const String &srcFile, const String &destFile); - - /** - * Delete a file - */ - bool removeFile(const String &file); - - /** - * Tests if the file exists - */ - bool fileExists(const String &fileName); - - /** - * Tests if the file exists and is a regular file - */ - bool isRegularFile(const String &fileName); - - /** - * Tests if the file exists and is a directory - */ - bool isDirectory(const String &fileName); - - /** - * Tests is the modification date of fileA is newer than fileB - */ - bool isNewerThan(const String &fileA, const String &fileB); - -private: - - bool pkgConfigRecursive(const String packageName, - const String &path, - const String &prefix, - int query, - String &result, - std::set<String> &deplist); - - /** - * utility method to query for "all", "cflags", or "libs" for this package and its - * dependencies. 0, 1, 2 - */ - bool pkgConfigQuery(const String &packageName, int query, String &result); - - /** - * replace a variable ref like ${a} with a value - */ - bool lookupProperty(const String &s, String &result); - - /** - * called by getSubstitutions(). This is in case a looked-up string - * has substitutions also. - */ - bool getSubstitutionsRecursive(const String &s, String &result, int depth); - - /** - * replace variable refs in a string like ${a} with their values - */ - bool getSubstitutions(const String &s, String &result); - - int line; - - -}; - -int MakeBase::numThreads = 1; - -/** - * Define the pkg-config class here, since it will be used in MakeBase method - * implementations. - */ -class PkgConfig : public MakeBase -{ - -public: - - /** - * - */ - PkgConfig() - { - path = "."; - prefix = "/target"; - init(); - } - - /** - * - */ - PkgConfig(const PkgConfig &other) - { assign(other); } - - /** - * - */ - PkgConfig &operator=(const PkgConfig &other) - { assign(other); return *this; } - - /** - * - */ - virtual ~PkgConfig() - { } - - /** - * - */ - virtual String getName() - { return name; } - - /** - * - */ - virtual String getPath() - { return path; } - - /** - * - */ - virtual void setPath(const String &val) - { path = val; } - - /** - * - */ - virtual String getPrefix() - { return prefix; } - - /** - * Allow the user to override the prefix in the file - */ - virtual void setPrefix(const String &val) - { prefix = val; } - - /** - * - */ - virtual String getDescription() - { return description; } - - /** - * - */ - virtual String getCflags() - { return cflags; } - - /** - * - */ - virtual String getLibs() - { return libs; } - - /** - * - */ - virtual String getAll() - { - String ret = cflags; - ret.append(" "); - ret.append(libs); - return ret; - } - - /** - * - */ - virtual String getVersion() - { return version; } - - /** - * - */ - virtual int getMajorVersion() - { return majorVersion; } - - /** - * - */ - virtual int getMinorVersion() - { return minorVersion; } - - /** - * - */ - virtual int getMicroVersion() - { return microVersion; } - - /** - * - */ - virtual std::map<String, String> &getAttributes() - { return attrs; } - - /** - * - */ - virtual std::vector<String> &getRequireList() - { return requireList; } - - /** - * Read a file for its details - */ - virtual bool readFile(const String &fileName); - - /** - * Read a file for its details - */ - virtual bool query(const String &name); - -private: - - void init() - { - //do not set path and prefix here - name = ""; - description = ""; - cflags = ""; - libs = ""; - requires = ""; - version = ""; - majorVersion = 0; - minorVersion = 0; - microVersion = 0; - fileName = ""; - attrs.clear(); - requireList.clear(); - } - - void assign(const PkgConfig &other) - { - name = other.name; - path = other.path; - prefix = other.prefix; - description = other.description; - cflags = other.cflags; - libs = other.libs; - requires = other.requires; - version = other.version; - majorVersion = other.majorVersion; - minorVersion = other.minorVersion; - microVersion = other.microVersion; - fileName = other.fileName; - attrs = other.attrs; - requireList = other.requireList; - } - - - - int get(int pos); - - int skipwhite(int pos); - - int getword(int pos, String &ret); - - /** - * Very important - */ - bool parseRequires(); - - void parseVersion(); - - bool parseLine(const String &lineBuf); - - bool parse(const String &buf); - - void dumpAttrs(); - - String name; - - String path; - - String prefix; - - String description; - - String cflags; - - String libs; - - String requires; - - String version; - - int majorVersion; - - int minorVersion; - - int microVersion; - - String fileName; - - std::map<String, String> attrs; - - std::vector<String> requireList; - - char *parsebuf; - int parselen; -}; - -/** - * Execute the "bzr revno" command and return the result. - * This is a simple, small class. - */ -class BzrRevno : public MakeBase -{ -public: - - /** - * Safe way. Execute "bzr revno" and return the result. - * Safe from changes in format. - */ - bool query(String &res) - { - String cmd = "bzr revno"; - - String outString, errString; - bool ret = executeCommand(cmd.c_str(), "", outString, errString); - if (!ret) - { - error("error executing '%s': %s", cmd.c_str(), errString.c_str()); - return false; - } - res = outString; - return true; - } -}; - -/** - * Execute the "svn info" command and parse the result. - * This is a simple, small class. Define here, because it - * is used by MakeBase implementation methods. - */ -class SvnInfo : public MakeBase -{ -public: - -#if 0 - /** - * Safe way. Execute "svn info --xml" and parse the result. Search for - * elements/attributes. Safe from changes in format. - */ - bool query(const String &name, String &res) - { - String cmd = "svn info --xml"; - - String outString, errString; - bool ret = executeCommand(cmd.c_str(), "", outString, errString); - if (!ret) - { - error("error executing '%s': %s", cmd.c_str(), errString.c_str()); - return false; - } - Parser parser; - Element *elem = parser.parse(outString); - if (!elem) - { - error("error parsing 'svn info' xml result: %s", outString.c_str()); - return false; - } - - res = elem->getTagValue(name); - if (res.size()==0) - { - res = elem->getTagAttribute("entry", name); - } - return true; - } -#else - - - /** - * Universal way. Parse the file directly. Not so safe from - * changes in format. - */ - bool query(const String &name, String &res) - { - String fileName = resolve(".svn/entries"); - String nFileName = getNativePath(fileName); - - std::map<String, String> properties; - - FILE *f = fopen(nFileName.c_str(), "r"); - if (!f) - { - error("could not open SVN 'entries' file"); - return false; - } - - const char *fieldNames[] = - { - "format-nbr", - "name", - "kind", - "revision", - "url", - "repos", - "schedule", - "text-time", - "checksum", - "committed-date", - "committed-rev", - "last-author", - "has-props", - "has-prop-mods", - "cachable-props", - }; - - for (int i=0 ; i<15 ; i++) - { - inbuf[0] = '\0'; - if (feof(f) || !fgets(inbuf, 255, f)) - break; - properties[fieldNames[i]] = trim(inbuf); - } - fclose(f); - - res = properties[name]; - - return true; - } - -private: - - char inbuf[256]; - -#endif - -}; - - - - - - -/** - * Print a printf()-like formatted error message - */ -void MakeBase::error(const char *fmt, ...) -{ - va_list args; - va_start(args,fmt); - fprintf(stderr, "Make error line %d: ", line); - vfprintf(stderr, fmt, args); - fprintf(stderr, "\n"); - va_end(args) ; -} - - - -/** - * Print a printf()-like formatted trace message - */ -void MakeBase::status(const char *fmt, ...) -{ - va_list args; - //fprintf(stdout, " "); - va_start(args,fmt); - vfprintf(stdout, fmt, args); - va_end(args); - fprintf(stdout, "\n"); - fflush(stdout); -} - - -/** - * Print a printf()-like formatted trace message - */ -void MakeBase::trace(const char *fmt, ...) -{ - va_list args; - fprintf(stdout, "Make: "); - va_start(args,fmt); - vfprintf(stdout, fmt, args); - va_end(args) ; - fprintf(stdout, "\n"); - fflush(stdout); -} - - - -/** - * Resolve another path relative to this one - */ -String MakeBase::resolve(const String &otherPath) -{ - URI otherURI(otherPath); - URI fullURI = uri.resolve(otherURI); - String ret = fullURI.toString(); - return ret; -} - - - -/** - * Check if a given string matches a given regex pattern - */ -bool MakeBase::regexMatch(const String &str, const String &pattern) -{ - int res = slre_match(pattern.c_str(), str.c_str(), str.length(), NULL, 0, SLRE_IGNORE_CASE); - - bool ret = true; - if (res < 0) - { - ret = false; - - // error cases - if (res < -1) - { - String err; - switch(res) - { - case SLRE_UNEXPECTED_QUANTIFIER: - err = "unexpected quantifier"; break; - case SLRE_UNBALANCED_BRACKETS: - err = "unbalanced brackets"; break; - case SLRE_INTERNAL_ERROR: - err = "internal error"; break; - case SLRE_INVALID_CHARACTER_SET: - err = "invald character set"; break; - case SLRE_INVALID_METACHARACTER: - err = "invalid meta character"; break; - default: - err = "unknown error"; break; - } - error("regex failure (%s) while parsing [%s]!\n", err.c_str(), pattern.c_str()); - } - } - - return ret; -} - -/** - * Return the suffix, if any, of a file name - */ -String MakeBase::getSuffix(const String &fname) -{ - if (fname.size() < 2) - return ""; - std::size_t pos = fname.find_last_of('.'); - if (pos == fname.npos) - return ""; - pos++; - String res = fname.substr(pos, fname.size()-pos); - //trace("suffix:%s", res.c_str()); - return res; -} - - - -/** - * Break up a string into substrings delimited the characters - * in delimiters. Null-length substrings are ignored - */ -std::vector<String> MakeBase::tokenize(const String &str, - const String &delimiters) -{ - - std::vector<String> res; - char *del = (char *)delimiters.c_str(); - String dmp; - for (std::size_t i=0 ; i<str.size() ; i++) - { - char ch = str[i]; - char *p = (char *)0; - for (p=del ; *p ; p++) - if (*p == ch) - break; - if (*p) - { - if (dmp.size() > 0) - { - res.push_back(dmp); - dmp.clear(); - } - } - else - { - dmp.push_back(ch); - } - } - //Add tail - if (dmp.size() > 0) - { - res.push_back(dmp); - dmp.clear(); - } - - return res; -} - - - -/** - * replace runs of whitespace with a single space - */ -String MakeBase::strip(const String &s) -{ - int len = s.size(); - String stripped; - for (int i = 0 ; i<len ; i++) - { - char ch = s[i]; - if (isspace(ch)) - { - stripped.push_back(' '); - for ( ; i<len ; i++) - { - ch = s[i]; - if (!isspace(ch)) - { - stripped.push_back(ch); - break; - } - } - } - else - { - stripped.push_back(ch); - } - } - return stripped; -} - -/** - * remove leading whitespace from each line - */ -String MakeBase::leftJustify(const String &s) -{ - String out; - int len = s.size(); - for (int i = 0 ; i<len ; ) - { - char ch; - //Skip to first visible character - while (i<len) - { - ch = s[i]; - if (ch == '\n' || ch == '\r' - || !isspace(ch)) - break; - i++; - } - //Copy the rest of the line - while (i<len) - { - ch = s[i]; - if (ch == '\n' || ch == '\r') - { - if (ch != '\r') - out.push_back('\n'); - i++; - break; - } - else - { - out.push_back(ch); - } - i++; - } - } - return out; -} - - -/** - * Removes whitespace from beginning and end of a string - */ -String MakeBase::trim(const String &s) -{ - if (s.size() < 1) - return s; - - //Find first non-ws char - std::size_t begin = 0; - for ( ; begin < s.size() ; begin++) - { - if (!isspace(s[begin])) - break; - } - - //Find first non-ws char, going in reverse - std::size_t end = s.size() - 1; - for ( ; end > begin ; end--) - { - if (!isspace(s[end])) - break; - } - //trace("begin:%d end:%d", begin, end); - - String res = s.substr(begin, end-begin+1); - return res; -} - - -/** - * Return a lower case version of the given string - */ -String MakeBase::toLower(const String &s) -{ - if (s.size()==0) - return s; - - String ret; - for(std::size_t i=0; i<s.size() ; i++) - { - ret.push_back(tolower(s[i])); - } - return ret; -} - - -/** - * Return the native format of the canonical - * path which we store - */ -String MakeBase::getNativePath(const String &path) -{ -#ifdef __WIN32__ - String npath; - std::size_t firstChar = 0; - if (path.size() >= 3) - { - if (path[0] == '/' && - isalpha(path[1]) && - path[2] == ':') - firstChar++; - } - for (std::size_t i=firstChar ; i<path.size() ; i++) - { - char ch = path[i]; - if (ch == '/') - npath.push_back('\\'); - else - npath.push_back(ch); - } - return npath; -#else - return path; -#endif -} - - -#ifdef __WIN32__ -#include <tchar.h> - -static String win32LastError() -{ - - DWORD dw = GetLastError(); - - LPVOID str; - FormatMessage( - FORMAT_MESSAGE_ALLOCATE_BUFFER | - FORMAT_MESSAGE_FROM_SYSTEM, - NULL, - dw, - 0, - (LPTSTR) &str, - 0, NULL ); - LPTSTR p = _tcschr((const char *)str, _T('\r')); - if(p != NULL) - { // lose CRLF - *p = _T('\0'); - } - String ret = (char *)str; - LocalFree(str); - - return ret; -} -#endif - - - - -#ifdef __WIN32__ - -/** - * Execute a system call, using pipes to send data to the - * program's stdin, and reading stdout and stderr. - */ -bool MakeBase::executeCommand(const String &command, - const String &inbuf, - String &outbuf, - String &errbuf) -{ - -// status("============ cmd ============\n%s\n=============================", -// command.c_str()); - - outbuf.clear(); - errbuf.clear(); - - - /* - I really hate having win32 code in this program, but the - read buffer in command.com and cmd.exe are just too small - for the large commands we need for compiling and linking. - */ - - bool ret = true; - - //# Allocate a separate buffer for safety - char *paramBuf = new char[command.size() + 1]; - if (!paramBuf) - { - error("executeCommand cannot allocate command buffer"); - return false; - } - strcpy(paramBuf, (char *)command.c_str()); - - //# Go to http://msdn2.microsoft.com/en-us/library/ms682499.aspx - //# to see how Win32 pipes work - - //# Create pipes - SECURITY_ATTRIBUTES saAttr; - saAttr.nLength = sizeof(SECURITY_ATTRIBUTES); - saAttr.bInheritHandle = TRUE; - saAttr.lpSecurityDescriptor = NULL; - HANDLE stdinRead, stdinWrite; - HANDLE stdoutRead, stdoutWrite; - HANDLE stderrRead, stderrWrite; - if (!CreatePipe(&stdinRead, &stdinWrite, &saAttr, 0)) - { - error("executeProgram: could not create pipe"); - delete[] paramBuf; - return false; - } - SetHandleInformation(stdinWrite, HANDLE_FLAG_INHERIT, 0); - if (!CreatePipe(&stdoutRead, &stdoutWrite, &saAttr, 0)) - { - error("executeProgram: could not create pipe"); - delete[] paramBuf; - return false; - } - SetHandleInformation(stdoutRead, HANDLE_FLAG_INHERIT, 0); - if (&outbuf != &errbuf) { - if (!CreatePipe(&stderrRead, &stderrWrite, &saAttr, 0)) - { - error("executeProgram: could not create pipe"); - delete[] paramBuf; - return false; - } - SetHandleInformation(stderrRead, HANDLE_FLAG_INHERIT, 0); - } else { - stderrRead = stdoutRead; - stderrWrite = stdoutWrite; - } - - // Create the process - STARTUPINFO siStartupInfo; - PROCESS_INFORMATION piProcessInfo; - memset(&siStartupInfo, 0, sizeof(siStartupInfo)); - memset(&piProcessInfo, 0, sizeof(piProcessInfo)); - siStartupInfo.cb = sizeof(siStartupInfo); - siStartupInfo.hStdError = stderrWrite; - siStartupInfo.hStdOutput = stdoutWrite; - siStartupInfo.hStdInput = stdinRead; - siStartupInfo.dwFlags |= STARTF_USESTDHANDLES; - - if (!CreateProcess(NULL, paramBuf, NULL, NULL, true, - 0, NULL, NULL, &siStartupInfo, - &piProcessInfo)) - { - error("executeCommand : could not create process : %s", - win32LastError().c_str()); - ret = false; - } - - delete[] paramBuf; - - DWORD bytesWritten; - if (inbuf.size()>0 && - !WriteFile(stdinWrite, inbuf.c_str(), inbuf.size(), - &bytesWritten, NULL)) - { - error("executeCommand: could not write to pipe"); - return false; - } - if (!CloseHandle(stdinWrite)) - { - error("executeCommand: could not close write pipe"); - return false; - } - if (!CloseHandle(stdoutWrite)) - { - error("executeCommand: could not close read pipe"); - return false; - } - if (stdoutWrite != stderrWrite && !CloseHandle(stderrWrite)) - { - error("executeCommand: could not close read pipe"); - return false; - } - - bool lastLoop = false; - while (true) - { - DWORD avail; - DWORD bytesRead; - char readBuf[4096]; - - //trace("## stderr"); - PeekNamedPipe(stderrRead, NULL, 0, NULL, &avail, NULL); - if (avail > 0) - { - bytesRead = 0; - if (avail>4096) avail = 4096; - ReadFile(stderrRead, readBuf, avail, &bytesRead, NULL); - if (bytesRead > 0) - { - for (unsigned int i=0 ; i<bytesRead ; i++) - errbuf.push_back(readBuf[i]); - } - } - - //trace("## stdout"); - PeekNamedPipe(stdoutRead, NULL, 0, NULL, &avail, NULL); - if (avail > 0) - { - bytesRead = 0; - if (avail>4096) avail = 4096; - ReadFile(stdoutRead, readBuf, avail, &bytesRead, NULL); - if (bytesRead > 0) - { - for (unsigned int i=0 ; i<bytesRead ; i++) - outbuf.push_back(readBuf[i]); - } - } - - //Was this the final check after program done? - if (lastLoop) - break; - - DWORD exitCode; - GetExitCodeProcess(piProcessInfo.hProcess, &exitCode); - if (exitCode != STILL_ACTIVE) - lastLoop = true; - - Sleep(10); - } - //trace("outbuf:%s", outbuf.c_str()); - if (!CloseHandle(stdoutRead)) - { - error("executeCommand: could not close read pipe"); - return false; - } - if (stdoutRead != stderrRead && !CloseHandle(stderrRead)) - { - error("executeCommand: could not close read pipe"); - return false; - } - - DWORD exitCode; - GetExitCodeProcess(piProcessInfo.hProcess, &exitCode); - //trace("exit code:%d", exitCode); - if (exitCode != 0) - { - ret = false; - } - - CloseHandle(piProcessInfo.hProcess); - CloseHandle(piProcessInfo.hThread); - - return ret; - -} - -#else /*do it unix style*/ - -#include <sys/wait.h> - - - -/** - * Execute a system call, using pipes to send data to the - * program's stdin, and reading stdout and stderr. - */ -bool MakeBase::executeCommand(const String &command, - const String &inbuf, - String &outbuf, - String &errbuf) -{ - - status("============ cmd ============\n%s\n=============================", - command.c_str()); - - outbuf.clear(); - errbuf.clear(); - - - int outfds[2]; - if (pipe(outfds) < 0) - return false; - int errfds[2]; - if (pipe(errfds) < 0) - return false; - int pid = fork(); - if (pid < 0) - { - close(outfds[0]); - close(outfds[1]); - close(errfds[0]); - close(errfds[1]); - error("launch of command '%s' failed : %s", - command.c_str(), strerror(errno)); - return false; - } - else if (pid > 0) // parent - { - close(outfds[1]); - close(errfds[1]); - } - else // == 0, child - { - close(outfds[0]); - dup2(outfds[1], STDOUT_FILENO); - close(outfds[1]); - close(errfds[0]); - dup2(errfds[1], STDERR_FILENO); - close(errfds[1]); - - char *args[4]; - args[0] = (char *)"sh"; - args[1] = (char *)"-c"; - args[2] = (char *)command.c_str(); - args[3] = NULL; - execv("/bin/sh", args); - exit(EXIT_FAILURE); - } - - String outb; - String errb; - - int outRead = outfds[0]; - int errRead = errfds[0]; - int max = outRead; - if (errRead > max) - max = errRead; - - bool outOpen = true; - bool errOpen = true; - - while (outOpen || errOpen) - { - char ch; - fd_set fdset; - FD_ZERO(&fdset); - if (outOpen) - FD_SET(outRead, &fdset); - if (errOpen) - FD_SET(errRead, &fdset); - int ret = select(max+1, &fdset, NULL, NULL, NULL); - if (ret < 0) - break; - if (FD_ISSET(outRead, &fdset)) - { - if (read(outRead, &ch, 1) <= 0) - { outOpen = false; } - else if (ch <= 0) - { /* outOpen = false; */ } - else - { outb.push_back(ch); } - } - if (FD_ISSET(errRead, &fdset)) - { - if (read(errRead, &ch, 1) <= 0) - { errOpen = false; } - else if (ch <= 0) - { /* errOpen = false; */ } - else - { errb.push_back(ch); } - } - } - - int childReturnValue; - wait(&childReturnValue); - - close(outRead); - close(errRead); - - outbuf = outb; - errbuf = errb; - - if (childReturnValue != 0) - { - error("exec of command '%s' failed : %s", - command.c_str(), strerror(childReturnValue)); - return false; - } - - return true; -} - -#endif - - - - -bool MakeBase::listDirectories(const String &baseName, - const String &dirName, - std::vector<String> &res) -{ - res.push_back(dirName); - String fullPath = baseName; - if (dirName.size()>0) - { - if (dirName[0]!='/') fullPath.append("/"); - fullPath.append(dirName); - } - DIR *dir = opendir(fullPath.c_str()); - while (true) - { - struct dirent *de = readdir(dir); - if (!de) - break; - - //Get the directory member name - String s = de->d_name; - if (s.size() == 0 || s[0] == '.') - continue; - String childName = dirName; - childName.append("/"); - childName.append(s); - - String fullChildPath = baseName; - fullChildPath.append("/"); - fullChildPath.append(childName); - struct stat finfo; - String childNative = getNativePath(fullChildPath); - if (cachedStat(childNative, &finfo)<0) - { - error("cannot stat file:%s", childNative.c_str()); - } - else if (S_ISDIR(finfo.st_mode)) - { - //trace("directory: %s", childName.c_str()); - if (!listDirectories(baseName, childName, res)) - return false; - } - } - closedir(dir); - - return true; -} - - -bool MakeBase::listFiles(const String &baseDir, - const String &dirName, - std::vector<String> &res) -{ - String fullDir = baseDir; - if (dirName.size()>0) - { - fullDir.append("/"); - fullDir.append(dirName); - } - String dirNative = getNativePath(fullDir); - - std::vector<String> subdirs; - DIR *dir = opendir(dirNative.c_str()); - if (!dir) - { - error("Could not open directory %s : %s", - dirNative.c_str(), strerror(errno)); - return false; - } - while (true) - { - struct dirent *de = readdir(dir); - if (!de) - break; - - //Get the directory member name - String s = de->d_name; - if (s.size() == 0 || s[0] == '.') - continue; - String childName; - if (dirName.size()>0) - { - childName.append(dirName); - childName.append("/"); - } - childName.append(s); - String fullChild = baseDir; - fullChild.append("/"); - fullChild.append(childName); - - if (isDirectory(fullChild)) - { - //trace("directory: %s", childName.c_str()); - if (!listFiles(baseDir, childName, res)) - return false; - continue; - } - else if (!isRegularFile(fullChild)) - { - error("unknown file:%s", childName.c_str()); - return false; - } - - //all done! - res.push_back(childName); - - } - closedir(dir); - - return true; -} - - -/** - * Several different classes extend MakeBase. By "propRef", we mean - * the one holding the properties. Likely "Make" itself - */ -bool MakeBase::listFiles(MakeBase &propRef, FileSet &fileSet) -{ - //before doing the list, resolve any property references - //that might have been specified in the directory name, such as ${src} - String fsDir = fileSet.getDirectory(); - String dir; - if (!propRef.getSubstitutions(fsDir, dir)) - return false; - String baseDir = propRef.resolve(dir); - std::vector<String> fileList; - if (!listFiles(baseDir, "", fileList)) - return false; - - std::vector<String> includes = fileSet.getIncludes(); - std::vector<String> excludes = fileSet.getExcludes(); - - std::vector<String> incs; - std::vector<String>::iterator iter; - - std::sort(fileList.begin(), fileList.end()); - - //If there are <includes>, then add files to the output - //in the order of the include list - if (includes.size()==0) - incs = fileList; - else - { - for (iter = includes.begin() ; iter != includes.end() ; iter++) - { - String &pattern = *iter; - std::vector<String>::iterator siter; - for (siter = fileList.begin() ; siter != fileList.end() ; siter++) - { - String s = *siter; - if (regexMatch(s, pattern)) - { - //trace("INCLUDED:%s", s.c_str()); - incs.push_back(s); - } - } - } - } - - //Now trim off the <excludes> - std::vector<String> res; - for (iter = incs.begin() ; iter != incs.end() ; iter++) - { - String s = *iter; - bool skipme = false; - std::vector<String>::iterator siter; - for (siter = excludes.begin() ; siter != excludes.end() ; siter++) - { - String &pattern = *siter; - if (regexMatch(s, pattern)) - { - //trace("EXCLUDED:%s", s.c_str()); - skipme = true; - break; - } - } - if (!skipme) - res.push_back(s); - } - - fileSet.setFiles(res); - - return true; -} - - -/** - * 0 == all, 1 = cflags, 2 = libs - */ -bool MakeBase::pkgConfigRecursive(const String packageName, - const String &path, - const String &prefix, - int query, - String &result, - std::set<String> &deplist) -{ - PkgConfig pkgConfig; - if (path.size() > 0) - pkgConfig.setPath(path); - if (prefix.size() > 0) - pkgConfig.setPrefix(prefix); - if (!pkgConfig.query(packageName)) - return false; - if (query == 0) - result = pkgConfig.getAll(); - else if (query == 1) - result = pkgConfig.getCflags(); - else - result = pkgConfig.getLibs(); - deplist.insert(packageName); - std::vector<String> list = pkgConfig.getRequireList(); - for (std::size_t i = 0 ; i<list.size() ; i++) - { - String depPkgName = list[i]; - if (deplist.find(depPkgName) != deplist.end()) - continue; - String val; - if (!pkgConfigRecursive(depPkgName, path, prefix, query, val, deplist)) - { - error("Based on 'requires' attribute of package '%s'", packageName.c_str()); - return false; - } - result.append(" "); - result.append(val); - } - - return true; -} - -bool MakeBase::pkgConfigQuery(const String &packageName, int query, String &result) -{ - std::set<String> deplist; - String path = getProperty("pkg-config-path"); - if (path.size()>0) - path = resolve(path); - String prefix = getProperty("pkg-config-prefix"); - String val; - if (!pkgConfigRecursive(packageName, path, prefix, query, val, deplist)) - return false; - result = val; - return true; -} - - - -/** - * replace a variable ref like ${a} with a value - */ -bool MakeBase::lookupProperty(const String &propertyName, String &result) -{ - String varname = propertyName; - if (envPrefix.size() > 0 && - varname.compare(0, envPrefix.size(), envPrefix) == 0) - { - varname = varname.substr(envPrefix.size()); - char *envstr = getenv(varname.c_str()); - if (!envstr) - { - error("environment variable '%s' not defined", varname.c_str()); - return false; - } - result = envstr; - } - else if (pcPrefix.size() > 0 && - varname.compare(0, pcPrefix.size(), pcPrefix) == 0) - { - varname = varname.substr(pcPrefix.size()); - String val; - if (!pkgConfigQuery(varname, 0, val)) - return false; - result = val; - } - else if (pccPrefix.size() > 0 && - varname.compare(0, pccPrefix.size(), pccPrefix) == 0) - { - varname = varname.substr(pccPrefix.size()); - String val; - if (!pkgConfigQuery(varname, 1, val)) - return false; - result = val; - } - else if (pclPrefix.size() > 0 && - varname.compare(0, pclPrefix.size(), pclPrefix) == 0) - { - varname = varname.substr(pclPrefix.size()); - String val; - if (!pkgConfigQuery(varname, 2, val)) - return false; - result = val; - } - else if (bzrPrefix.size() > 0 && - varname.compare(0, bzrPrefix.size(), bzrPrefix) == 0) - { - varname = varname.substr(bzrPrefix.size()); - String val; - //SvnInfo svnInfo; - BzrRevno bzrRevno; - if (varname == "revision") - { - if (!bzrRevno.query(val)) - return ""; - result = "r"+val; - } - /*if (!svnInfo.query(varname, val)) - return false; - result = val;*/ - } - else - { - std::map<String, String>::iterator iter; - iter = properties.find(varname); - if (iter != properties.end()) - { - result = iter->second; - } - else - { - error("property '%s' not found", varname.c_str()); - return false; - } - } - return true; -} - - - - -/** - * Analyse a string, looking for any substitutions or other - * things that need resolution - */ -bool MakeBase::getSubstitutionsRecursive(const String &str, - String &result, int depth) -{ - if (depth > 10) - { - error("nesting of substitutions too deep (>10) for '%s'", - str.c_str()); - return false; - } - String s = trim(str); - int len = (int)s.size(); - String val; - for (int i=0 ; i<len ; i++) - { - char ch = s[i]; - if (ch == '$' && s[i+1] == '{') - { - String varname; - int j = i+2; - for ( ; j<len ; j++) - { - ch = s[j]; - if (ch == '$' && s[j+1] == '{') - { - error("attribute %s cannot have nested variable references", - s.c_str()); - return false; - } - else if (ch == '}') - { - varname = trim(varname); - String varval; - if (!lookupProperty(varname, varval)) - return false; - String varval2; - //Now see if the answer has ${} in it, too - if (!getSubstitutionsRecursive(varval, varval2, depth + 1)) - return false; - val.append(varval2); - break; - } - else - { - varname.push_back(ch); - } - } - i = j; - } - else - { - val.push_back(ch); - } - } - result = val; - return true; -} - -/** - * Analyse a string, looking for any substitutions or other - * things that need resilution - */ -bool MakeBase::getSubstitutions(const String &str, String &result) -{ - return getSubstitutionsRecursive(str, result, 0); -} - - - -/** - * replace variable refs like ${a} with their values - * Assume that the string has already been syntax validated - */ -String MakeBase::eval(const String &s, const String &defaultVal) -{ - if (s.size()==0) - return defaultVal; - String ret; - if (getSubstitutions(s, ret)) - return ret; - else - return defaultVal; -} - - -/** - * replace variable refs like ${a} with their values - * return true or false - * Assume that the string has already been syntax validated - */ -bool MakeBase::evalBool(const String &s, bool defaultVal) -{ - if (s.size()==0) - return defaultVal; - String val = eval(s, "false"); - if (val.size()==0) - return defaultVal; - if (val == "true" || val == "TRUE") - return true; - else - return false; -} - -int MakeBase::evalInt(const String &s, int defaultVal) -{ - if (s.size()==0) { - return defaultVal; - } - int val = atoi(s.c_str()); - // perhaps some error checking, but... bah! waste of time - return val; -} - -/** - * Get a string attribute, testing it for proper syntax and - * property names. - */ -bool MakeBase::getAttribute(Element *elem, const String &name, - String &result) -{ - String s = elem->getAttribute(name); - String tmp; - bool ret = getSubstitutions(s, tmp); - if (ret) - result = s; //assign -if- ok - return ret; -} - - -/** - * Get a string value, testing it for proper syntax and - * property names. - */ -bool MakeBase::getValue(Element *elem, String &result) -{ - String s = elem->getValue(); - String tmp; - bool ret = getSubstitutions(s, tmp); - if (ret) - result = s; //assign -if- ok - return ret; -} - - - - -/** - * Parse a <patternset> entry - */ -bool MakeBase::parsePatternSet(Element *elem, - MakeBase &propRef, - std::vector<String> &includes, - std::vector<String> &excludes - ) -{ - std::vector<Element *> children = elem->getChildren(); - for (std::size_t i=0 ; i<children.size() ; i++) - { - Element *child = children[i]; - String tagName = child->getName(); - if (tagName == "exclude") - { - String fname; - if (!propRef.getAttribute(child, "name", fname)) - return false; - //trace("EXCLUDE: %s", fname.c_str()); - excludes.push_back(fname); - } - else if (tagName == "include") - { - String fname; - if (!propRef.getAttribute(child, "name", fname)) - return false; - //trace("INCLUDE: %s", fname.c_str()); - includes.push_back(fname); - } - } - - return true; -} - - - - -/** - * Parse a <fileset> entry, and determine which files - * should be included - */ -bool MakeBase::parseFileSet(Element *elem, - MakeBase &propRef, - FileSet &fileSet) -{ - String name = elem->getName(); - if (name != "fileset") - { - error("expected <fileset>"); - return false; - } - - - std::vector<String> includes; - std::vector<String> excludes; - - //A fileset has one implied patternset - if (!parsePatternSet(elem, propRef, includes, excludes)) - { - return false; - } - //Look for child tags, including more patternsets - std::vector<Element *> children = elem->getChildren(); - for (std::size_t i=0 ; i<children.size() ; i++) - { - Element *child = children[i]; - String tagName = child->getName(); - if (tagName == "patternset") - { - if (!parsePatternSet(child, propRef, includes, excludes)) - { - return false; - } - } - } - - String dir; - //Now do the stuff - //Get the base directory for reading file names - if (!propRef.getAttribute(elem, "dir", dir)) - return false; - - fileSet.setDirectory(dir); - fileSet.setIncludes(includes); - fileSet.setExcludes(excludes); - - /* - std::vector<String> fileList; - if (dir.size() > 0) - { - String baseDir = propRef.resolve(dir); - if (!listFiles(baseDir, "", includes, excludes, fileList)) - return false; - } - std::sort(fileList.begin(), fileList.end()); - result = fileList; - */ - - - /* - for (std::size_t i=0 ; i<result.size() ; i++) - { - trace("RES:%s", result[i].c_str()); - } - */ - - - return true; -} - -/** - * Parse a <filelist> entry. This is far simpler than FileSet, - * since no directory scanning is needed. The file names are listed - * explicitly. - */ -bool MakeBase::parseFileList(Element *elem, - MakeBase &propRef, - FileList &fileList) -{ - std::vector<String> fnames; - //Look for child tags, namely "file" - std::vector<Element *> children = elem->getChildren(); - for (std::size_t i=0 ; i<children.size() ; i++) - { - Element *child = children[i]; - String tagName = child->getName(); - if (tagName == "file") - { - String fname = child->getAttribute("name"); - if (fname.size()==0) - { - error("<file> element requires name="" attribute"); - return false; - } - fnames.push_back(fname); - } - else - { - error("tag <%s> not allowed in <fileset>", tagName.c_str()); - return false; - } - } - - String dir; - //Get the base directory for reading file names - if (!propRef.getAttribute(elem, "dir", dir)) - return false; - fileList.setDirectory(dir); - fileList.setFiles(fnames); - - return true; -} - - - -/** - * Create a directory, making intermediate dirs - * if necessary - */ -bool MakeBase::createDirectory(const String &dirname) -{ - //trace("## createDirectory: %s", dirname.c_str()); - //## first check if it exists - struct stat finfo; - String nativeDir = getNativePath(dirname); - char *cnative = (char *) nativeDir.c_str(); -#ifdef __WIN32__ - if (strlen(cnative)==2 && cnative[1]==':') - return true; -#endif - if (cachedStat(nativeDir, &finfo)==0) - { - if (!S_ISDIR(finfo.st_mode)) - { - error("mkdir: file %s exists but is not a directory", - cnative); - return false; - } - else //exists - { - return true; - } - } - - //## 2: pull off the last path segment, if any, - //## to make the dir 'above' this one, if necessary - std::size_t pos = dirname.find_last_of('/'); - if (pos>0 && pos != dirname.npos) - { - String subpath = dirname.substr(0, pos); - //A letter root (c:) ? - if (!createDirectory(subpath)) - return false; - } - - //## 3: now make -#ifdef __WIN32__ - if (mkdir(cnative)<0) -#else - if (mkdir(cnative, S_IRWXU | S_IRWXG | S_IRWXO)<0) -#endif - { - error("cannot make directory '%s' : %s", - cnative, strerror(errno)); - return false; - } - - removeFromStatCache(nativeDir); - - return true; -} - - -/** - * Remove a directory recursively - */ -bool MakeBase::removeDirectory(const String &dirName) -{ - char *dname = (char *)dirName.c_str(); - - DIR *dir = opendir(dname); - if (!dir) - { - //# Let this fail nicely. - return true; - //error("error opening directory %s : %s", dname, strerror(errno)); - //return false; - } - - while (true) - { - struct dirent *de = readdir(dir); - if (!de) - break; - - //Get the directory member name - String s = de->d_name; - if (s.size() == 0 || s[0] == '.') - continue; - String childName; - if (dirName.size() > 0) - { - childName.append(dirName); - childName.append("/"); - } - childName.append(s); - - - struct stat finfo; - String childNative = getNativePath(childName); - char *cnative = (char *)childNative.c_str(); - if (cachedStat(childNative, &finfo)<0) - { - error("cannot stat file:%s", cnative); - } - else if (S_ISDIR(finfo.st_mode)) - { - //trace("DEL dir: %s", childName.c_str()); - if (!removeDirectory(childName)) - { - return false; - } - } - else if (!S_ISREG(finfo.st_mode)) - { - //trace("not regular: %s", cnative); - } - else - { - //trace("DEL file: %s", childName.c_str()); - if (!removeFile(childName)) - { - return false; - } - } - } - closedir(dir); - - //Now delete the directory - String native = getNativePath(dirName); - if (rmdir(native.c_str())<0) - { - error("could not delete directory %s : %s", - native.c_str() , strerror(errno)); - return false; - } - - removeFromStatCache(native); - - return true; - -} - - -/** - * Copy a file from one name to another. Perform only if needed - */ -bool MakeBase::copyFile(const String &srcFile, const String &destFile) -{ - //# 1 Check up-to-date times - String srcNative = getNativePath(srcFile); - struct stat srcinfo; - if (cachedStat(srcNative, &srcinfo)<0) - { - error("source file %s for copy does not exist", - srcNative.c_str()); - return false; - } - - String destNative = getNativePath(destFile); - struct stat destinfo; - if (cachedStat(destNative, &destinfo)==0) - { - if (destinfo.st_mtime >= srcinfo.st_mtime) - return true; - } - - //# 2 prepare a destination directory if necessary - std::size_t pos = destFile.find_last_of('/'); - if (pos != destFile.npos) - { - String subpath = destFile.substr(0, pos); - if (!createDirectory(subpath)) - return false; - } - - //# 3 do the data copy -#ifndef __WIN32__ - - FILE *srcf = fopen(srcNative.c_str(), "rb"); - if (!srcf) - { - error("copyFile cannot open '%s' for reading", srcNative.c_str()); - return false; - } - FILE *destf = fopen(destNative.c_str(), "wb"); - if (!destf) - { - fclose(srcf); - error("copyFile cannot open %s for writing", srcNative.c_str()); - return false; - } - - while (!feof(srcf)) - { - int ch = fgetc(srcf); - if (ch<0) - break; - fputc(ch, destf); - } - - fclose(destf); - fclose(srcf); - -#else - - if (!CopyFile(srcNative.c_str(), destNative.c_str(), false)) - { - error("copyFile from %s to %s failed", - srcNative.c_str(), destNative.c_str()); - return false; - } - -#endif /* __WIN32__ */ - - removeFromStatCache(destNative); - - return true; -} - - -/** - * Delete a file - */ -bool MakeBase::removeFile(const String &file) -{ - String native = getNativePath(file); - - if (!fileExists(native)) - { - return true; - } - -#ifdef WIN32 - // On Windows 'remove' will only delete files - - if (remove(native.c_str())<0) - { - if (errno==EACCES) - { - error("File %s is read-only", native.c_str()); - } - else if (errno==ENOENT) - { - error("File %s does not exist or is a directory", native.c_str()); - } - else - { - error("Failed to delete file %s: %s", native.c_str(), strerror(errno)); - } - return false; - } - -#else - - if (!isRegularFile(native)) - { - error("File %s does not exist or is not a regular file", native.c_str()); - return false; - } - - if (remove(native.c_str())<0) - { - if (errno==EACCES) - { - error("File %s is read-only", native.c_str()); - } - else - { - error( - errno==EACCES ? "File %s is read-only" : - errno==ENOENT ? "File %s does not exist or is a directory" : - "Failed to delete file %s: %s", native.c_str()); - } - return false; - } - -#endif - - removeFromStatCache(native); - - return true; -} - - -/** - * Tests if the file exists - */ -bool MakeBase::fileExists(const String &fileName) -{ - String native = getNativePath(fileName); - struct stat finfo; - - //Exists? - if (cachedStat(native, &finfo)<0) - return false; - - return true; -} - - -/** - * Tests if the file exists and is a regular file - */ -bool MakeBase::isRegularFile(const String &fileName) -{ - String native = getNativePath(fileName); - struct stat finfo; - - //Exists? - if (cachedStat(native, &finfo)<0) - return false; - - - //check the file mode - if (!S_ISREG(finfo.st_mode)) - return false; - - return true; -} - -/** - * Tests if the file exists and is a directory - */ -bool MakeBase::isDirectory(const String &fileName) -{ - String native = getNativePath(fileName); - struct stat finfo; - - //Exists? - if (cachedStat(native, &finfo)<0) - return false; - - - //check the file mode - if (!S_ISDIR(finfo.st_mode)) - return false; - - return true; -} - - - -/** - * Tests is the modification of fileA is newer than fileB - */ -bool MakeBase::isNewerThan(const String &fileA, const String &fileB) -{ - //trace("isNewerThan:'%s' , '%s'", fileA.c_str(), fileB.c_str()); - String nativeA = getNativePath(fileA); - struct stat infoA; - //IF source does not exist, NOT newer - if (cachedStat(nativeA, &infoA)<0) - { - return false; - } - - String nativeB = getNativePath(fileB); - struct stat infoB; - //IF dest does not exist, YES, newer - if (cachedStat(nativeB, &infoB)<0) - { - return true; - } - - //check the actual times - if (infoA.st_mtime > infoB.st_mtime) - { - return true; - } - - return false; -} - - -//######################################################################## -//# P K G C O N F I G -//######################################################################## - - -/** - * Get a character from the buffer at pos. If out of range, - * return -1 for safety - */ -int PkgConfig::get(int pos) -{ - if (pos>parselen) - return -1; - return parsebuf[pos]; -} - - - -/** - * Skip over all whitespace characters beginning at pos. Return - * the position of the first non-whitespace character. - * Pkg-config is line-oriented, so check for newline - */ -int PkgConfig::skipwhite(int pos) -{ - while (pos < parselen) - { - int ch = get(pos); - if (ch < 0) - break; - if (!isspace(ch)) - break; - pos++; - } - return pos; -} - - -/** - * Parse the buffer beginning at pos, for a word. Fill - * 'ret' with the result. Return the position after the - * word. - */ -int PkgConfig::getword(int pos, String &ret) -{ - while (pos < parselen) - { - int ch = get(pos); - if (ch < 0) - break; - if (!isalnum(ch) && ch != '_' && ch != '-' && ch != '+' && ch != '.') - break; - ret.push_back((char)ch); - pos++; - } - return pos; -} - -bool PkgConfig::parseRequires() -{ - if (requires.size() == 0) - return true; - parsebuf = (char *)requires.c_str(); - parselen = requires.size(); - int pos = 0; - while (pos < parselen) - { - pos = skipwhite(pos); - String val; - int pos2 = getword(pos, val); - if (pos2 == pos) - break; - pos = pos2; - //trace("val %s", val.c_str()); - requireList.push_back(val); - } - return true; -} - - -static int getint(const String str) -{ - char *s = (char *)str.c_str(); - char *ends = NULL; - long val = strtol(s, &ends, 10); - if (ends == s) - return 0L; - else - return val; -} - -void PkgConfig::parseVersion() -{ - if (version.size() == 0) - return; - String s1, s2, s3; - std::size_t pos = 0; - std::size_t pos2 = version.find('.', pos); - if (pos2 == version.npos) - { - s1 = version; - } - else - { - s1 = version.substr(pos, pos2-pos); - pos = pos2; - pos++; - if (pos < version.size()) - { - pos2 = version.find('.', pos); - if (pos2 == version.npos) - { - s2 = version.substr(pos, version.size()-pos); - } - else - { - s2 = version.substr(pos, pos2-pos); - pos = pos2; - pos++; - if (pos < version.size()) - s3 = version.substr(pos, pos2-pos); - } - } - } - - majorVersion = getint(s1); - minorVersion = getint(s2); - microVersion = getint(s3); - //trace("version:%d.%d.%d", majorVersion, - // minorVersion, microVersion ); -} - - -bool PkgConfig::parseLine(const String &lineBuf) -{ - parsebuf = (char *)lineBuf.c_str(); - parselen = lineBuf.size(); - int pos = 0; - - while (pos < parselen) - { - String attrName; - pos = skipwhite(pos); - int ch = get(pos); - if (ch == '#') - { - //comment. eat the rest of the line - while (pos < parselen) - { - ch = get(pos); - if (ch == '\n' || ch < 0) - break; - pos++; - } - continue; - } - pos = getword(pos, attrName); - if (attrName.size() == 0) - continue; - - pos = skipwhite(pos); - ch = get(pos); - if (ch != ':' && ch != '=') - { - error("expected ':' or '='"); - return false; - } - pos++; - pos = skipwhite(pos); - String attrVal; - while (pos < parselen) - { - ch = get(pos); - if (ch == '\n' || ch < 0) - break; - else if (ch == '$' && get(pos+1) == '{') - { - //# this is a ${substitution} - pos += 2; - String subName; - while (pos < parselen) - { - ch = get(pos); - if (ch < 0) - { - error("unterminated substitution"); - return false; - } - else if (ch == '}') - break; - else - subName.push_back((char)ch); - pos++; - } - //trace("subName:%s %s", subName.c_str(), prefix.c_str()); - if (subName == "prefix" && prefix.size()>0) - { - attrVal.append(prefix); - //trace("prefix override:%s", prefix.c_str()); - } - else - { - String subVal = attrs[subName]; - //trace("subVal:%s", subVal.c_str()); - attrVal.append(subVal); - } - } - else - attrVal.push_back((char)ch); - pos++; - } - - attrVal = trim(attrVal); - attrs[attrName] = attrVal; - - String attrNameL = toLower(attrName); - - if (attrNameL == "name") - name = attrVal; - else if (attrNameL == "description") - description = attrVal; - else if (attrNameL == "cflags") - cflags = attrVal; - else if (attrNameL == "libs") - libs = attrVal; - else if (attrNameL == "requires") - requires = attrVal; - else if (attrNameL == "version") - version = attrVal; - - //trace("name:'%s' value:'%s'", - // attrName.c_str(), attrVal.c_str()); - } - - return true; -} - - -bool PkgConfig::parse(const String &buf) -{ - init(); - - String line; - int lineNr = 0; - for (std::size_t p=0 ; p<buf.size() ; p++) - { - int ch = buf[p]; - if (ch == '\n' || ch == '\r') - { - if (!parseLine(line)) - return false; - line.clear(); - lineNr++; - } - else - { - line.push_back(ch); - } - } - if (line.size()>0) - { - if (!parseLine(line)) - return false; - } - - parseRequires(); - parseVersion(); - - return true; -} - - - - -void PkgConfig::dumpAttrs() -{ - //trace("### PkgConfig attributes for %s", fileName.c_str()); - std::map<String, String>::iterator iter; - for (iter=attrs.begin() ; iter!=attrs.end() ; iter++) - { - trace(" %s = %s", iter->first.c_str(), iter->second.c_str()); - } -} - - -bool PkgConfig::readFile(const String &fname) -{ - fileName = getNativePath(fname); - - FILE *f = fopen(fileName.c_str(), "r"); - if (!f) - { - error("cannot open file '%s' for reading", fileName.c_str()); - return false; - } - String buf; - while (true) - { - int ch = fgetc(f); - if (ch < 0) - break; - buf.push_back((char)ch); - } - fclose(f); - - //trace("####### File:\n%s", buf.c_str()); - if (!parse(buf)) - { - return false; - } - - //dumpAttrs(); - - return true; -} - - - -bool PkgConfig::query(const String &pkgName) -{ - name = pkgName; - - String fname = path; - fname.append("/"); - fname.append(name); - fname.append(".pc"); - - if (!readFile(fname)) - { - error("Cannot find package '%s'. Do you have it installed?", - pkgName.c_str()); - return false; - } - - return true; -} - - -//######################################################################## -//# D E P T O O L -//######################################################################## - - - -/** - * Class which holds information for each file. - */ -class FileRec -{ -public: - - typedef enum - { - UNKNOWN, - CFILE, - HFILE, - OFILE - } FileType; - - /** - * Constructor - */ - FileRec() - { init(); type = UNKNOWN; } - - /** - * Copy constructor - */ - FileRec(const FileRec &other) - { init(); assign(other); } - /** - * Constructor - */ - FileRec(int typeVal) - { init(); type = typeVal; } - /** - * Assignment operator - */ - FileRec &operator=(const FileRec &other) - { init(); assign(other); return *this; } - - - /** - * Destructor - */ - ~FileRec() - {} - - /** - * Directory part of the file name - */ - String path; - - /** - * Base name, sans directory and suffix - */ - String baseName; - - /** - * File extension, such as cpp or h - */ - String suffix; - - /** - * Type of file: CFILE, HFILE, OFILE - */ - int type; - - /** - * Used to list files ref'd by this one - */ - std::map<String, FileRec *> files; - - -private: - - void init() - { - } - - void assign(const FileRec &other) - { - type = other.type; - baseName = other.baseName; - suffix = other.suffix; - files = other.files; - } - -}; - - - -/** - * Simpler dependency record - */ -class DepRec -{ -public: - - /** - * Constructor - */ - DepRec() - {init();} - - /** - * Copy constructor - */ - DepRec(const DepRec &other) - {init(); assign(other);} - /** - * Constructor - */ - DepRec(const String &fname) - {init(); name = fname; } - /** - * Assignment operator - */ - DepRec &operator=(const DepRec &other) - {init(); assign(other); return *this;} - - - /** - * Destructor - */ - ~DepRec() - {} - - /** - * Directory part of the file name - */ - String path; - - /** - * Base name, without the path and suffix - */ - String name; - - /** - * Suffix of the source - */ - String suffix; - - - /** - * Used to list files ref'd by this one - */ - std::vector<String> files; - - -private: - - void init() - { - } - - void assign(const DepRec &other) - { - path = other.path; - name = other.name; - suffix = other.suffix; - files = other.files; //avoid recursion - } - -}; - - -class DepTool : public MakeBase -{ -public: - - /** - * Constructor - */ - DepTool() - { init(); } - - /** - * Copy constructor - */ - DepTool(const DepTool &other) - { init(); assign(other); } - - /** - * Assignment operator - */ - DepTool &operator=(const DepTool &other) - { init(); assign(other); return *this; } - - - /** - * Destructor - */ - ~DepTool() - {} - - - /** - * Reset this section of code - */ - virtual void init(); - - /** - * Reset this section of code - */ - virtual void assign(const DepTool &other) - { - } - - /** - * Sets the source directory which will be scanned - */ - virtual void setSourceDirectory(const String &val) - { sourceDir = val; } - - /** - * Returns the source directory which will be scanned - */ - virtual String getSourceDirectory() - { return sourceDir; } - - /** - * Sets the list of files within the directory to analyze - */ - virtual void setFileList(const std::vector<String> &list) - { fileList = list; } - - /** - * Creates the list of all file names which will be - * candidates for further processing. Reads make.exclude - * to see which files for directories to leave out. - */ - virtual bool createFileList(); - - - /** - * Generates the forward dependency list - */ - virtual bool generateDependencies(); - - - /** - * Generates the forward dependency list, saving the file - */ - virtual bool generateDependencies(const String &); - - - /** - * Load a dependency file - */ - std::vector<DepRec> loadDepFile(const String &fileName); - - /** - * Load a dependency file, generating one if necessary - */ - std::vector<DepRec> getDepFile(const String &fileName, - bool forceRefresh); - - /** - * Save a dependency file - */ - bool saveDepFile(const String &fileName); - - -private: - - - /** - * - */ - void parseName(const String &fullname, - String &path, - String &basename, - String &suffix); - - /** - * - */ - int get(int pos); - - /** - * - */ - int skipwhite(int pos); - - /** - * - */ - int getword(int pos, String &ret); - - /** - * - */ - bool sequ(int pos, const char *key); - - /** - * - */ - bool addIncludeFile(FileRec *frec, const String &fname); - - /** - * - */ - bool scanFile(const String &fname, FileRec *frec); - - /** - * - */ - bool processDependency(FileRec *ofile, FileRec *include); - - /** - * - */ - String sourceDir; - - /** - * - */ - std::vector<String> fileList; - - /** - * - */ - std::vector<String> directories; - - /** - * A list of all files which will be processed for - * dependencies. - */ - std::map<String, FileRec *> allFiles; - - /** - * The list of .o files, and the - * dependencies upon them. - */ - std::map<String, FileRec *> oFiles; - - int depFileSize; - char *depFileBuf; - - static const int readBufSize = 8192; - char readBuf[8193];//byte larger - -}; - - - - - -/** - * Clean up after processing. Called by the destructor, but should - * also be called before the object is reused. - */ -void DepTool::init() -{ - sourceDir = "."; - - fileList.clear(); - directories.clear(); - - //clear output file list - std::map<String, FileRec *>::iterator iter; - for (iter=oFiles.begin(); iter!=oFiles.end() ; iter++) - delete iter->second; - oFiles.clear(); - - //allFiles actually contains the master copies. delete them - for (iter= allFiles.begin(); iter!=allFiles.end() ; iter++) - delete iter->second; - allFiles.clear(); - -} - - - - -/** - * Parse a full path name into path, base name, and suffix - */ -void DepTool::parseName(const String &fullname, - String &path, - String &basename, - String &suffix) -{ - if (fullname.size() < 2) - return; - - std::size_t pos = fullname.find_last_of('/'); - if (pos != fullname.npos && pos<fullname.size()-1) - { - path = fullname.substr(0, pos); - pos++; - basename = fullname.substr(pos, fullname.size()-pos); - } - else - { - path = ""; - basename = fullname; - } - - pos = basename.find_last_of('.'); - if (pos != basename.npos && pos<basename.size()-1) - { - suffix = basename.substr(pos+1, basename.size()-pos-1); - basename = basename.substr(0, pos); - } - - //trace("parsename:%s %s %s", path.c_str(), - // basename.c_str(), suffix.c_str()); -} - - - -/** - * Generate our internal file list. - */ -bool DepTool::createFileList() -{ - - for (std::size_t i=0 ; i<fileList.size() ; i++) - { - String fileName = fileList[i]; - //trace("## FileName:%s", fileName.c_str()); - String path; - String basename; - String sfx; - parseName(fileName, path, basename, sfx); - if (sfx == "cpp" || sfx == "c" || sfx == "cxx" || - sfx == "cc" || sfx == "CC") - { - FileRec *fe = new FileRec(FileRec::CFILE); - fe->path = path; - fe->baseName = basename; - fe->suffix = sfx; - allFiles[fileName] = fe; - } - else if (sfx == "h" || sfx == "hh" || - sfx == "hpp" || sfx == "hxx") - { - FileRec *fe = new FileRec(FileRec::HFILE); - fe->path = path; - fe->baseName = basename; - fe->suffix = sfx; - allFiles[fileName] = fe; - } - } - - if (!listDirectories(sourceDir, "", directories)) - return false; - - return true; -} - - - - - -/** - * Get a character from the buffer at pos. If out of range, - * return -1 for safety - */ -int DepTool::get(int pos) -{ - if (pos>depFileSize) - return -1; - return depFileBuf[pos]; -} - - - -/** - * Skip over all whitespace characters beginning at pos. Return - * the position of the first non-whitespace character. - */ -int DepTool::skipwhite(int pos) -{ - while (pos < depFileSize) - { - int ch = get(pos); - if (ch < 0) - break; - if (!isspace(ch)) - break; - pos++; - } - return pos; -} - - -/** - * Parse the buffer beginning at pos, for a word. Fill - * 'ret' with the result. Return the position after the - * word. - */ -int DepTool::getword(int pos, String &ret) -{ - while (pos < depFileSize) - { - int ch = get(pos); - if (ch < 0) - break; - if (isspace(ch)) - break; - ret.push_back((char)ch); - pos++; - } - return pos; -} - -/** - * Return whether the sequence of characters in the buffer - * beginning at pos match the key, for the length of the key - */ -bool DepTool::sequ(int pos, const char *key) -{ - while (*key) - { - if (*key != get(pos)) - return false; - key++; pos++; - } - return true; -} - - - -/** - * Add an include file name to a file record. If the name - * is not found in allFiles explicitly, try prepending include - * directory names to it and try again. - */ -bool DepTool::addIncludeFile(FileRec *frec, const String &iname) -{ - //# if the name is an exact match to a path name - //# in allFiles, like "myinc.h" - std::map<String, FileRec *>::iterator iter = - allFiles.find(iname); - if (iter != allFiles.end()) //already exists - { - //h file in same dir - FileRec *other = iter->second; - //trace("local: '%s'", iname.c_str()); - frec->files[iname] = other; - return true; - } - else - { - //## Ok, it was not found directly - //look in other dirs - std::vector<String>::iterator diter; - for (diter=directories.begin() ; - diter!=directories.end() ; diter++) - { - String dfname = *diter; - dfname.append("/"); - dfname.append(iname); - URI fullPathURI(dfname); //normalize path name - String fullPath = fullPathURI.getPath(); - if (fullPath[0] == '/') - fullPath = fullPath.substr(1); - //trace("Normalized %s to %s", dfname.c_str(), fullPath.c_str()); - iter = allFiles.find(fullPath); - if (iter != allFiles.end()) - { - FileRec *other = iter->second; - //trace("other: '%s'", iname.c_str()); - frec->files[fullPath] = other; - return true; - } - } - } - return true; -} - - - -/** - * Lightly parse a file to find the #include directives. Do - * a bit of state machine stuff to make sure that the directive - * is valid. (Like not in a comment). - */ -bool DepTool::scanFile(const String &fname, FileRec *frec) -{ - String fileName; - if (sourceDir.size() > 0) - { - fileName.append(sourceDir); - fileName.append("/"); - } - fileName.append(fname); - String nativeName = getNativePath(fileName); - FILE *f = fopen(nativeName.c_str(), "r"); - if (!f) - { - error("Could not open '%s' for reading", fname.c_str()); - return false; - } - String buf; - while (!feof(f)) - { - int nrbytes = fread(readBuf, 1, readBufSize, f); - readBuf[nrbytes] = '\0'; - buf.append(readBuf); - } - fclose(f); - - depFileSize = buf.size(); - depFileBuf = (char *)buf.c_str(); - int pos = 0; - - - while (pos < depFileSize) - { - //trace("p:%c", get(pos)); - - //# Block comment - if (get(pos) == '/' && get(pos+1) == '*') - { - pos += 2; - while (pos < depFileSize) - { - if (get(pos) == '*' && get(pos+1) == '/') - { - pos += 2; - break; - } - else - pos++; - } - } - //# Line comment - else if (get(pos) == '/' && get(pos+1) == '/') - { - pos += 2; - while (pos < depFileSize) - { - if (get(pos) == '\n') - { - pos++; - break; - } - else - pos++; - } - } - //# #include! yaay - else if (sequ(pos, "#include")) - { - pos += 8; - pos = skipwhite(pos); - String iname; - pos = getword(pos, iname); - if (iname.size()>2) - { - iname = iname.substr(1, iname.size()-2); - addIncludeFile(frec, iname); - } - } - else - { - pos++; - } - } - - return true; -} - - - -/** - * Recursively check include lists to find all files in allFiles to which - * a given file is dependent. - */ -bool DepTool::processDependency(FileRec *ofile, FileRec *include) -{ - std::map<String, FileRec *>::iterator iter; - for (iter=include->files.begin() ; iter!=include->files.end() ; iter++) - { - String fname = iter->first; - if (ofile->files.find(fname) != ofile->files.end()) - { - //trace("file '%s' already seen", fname.c_str()); - continue; - } - FileRec *child = iter->second; - ofile->files[fname] = child; - - processDependency(ofile, child); - } - - - return true; -} - - - - - -/** - * Generate the file dependency list. - */ -bool DepTool::generateDependencies() -{ - std::map<String, FileRec *>::iterator iter; - //# First pass. Scan for all includes - for (iter=allFiles.begin() ; iter!=allFiles.end() ; iter++) - { - FileRec *frec = iter->second; - if (!scanFile(iter->first, frec)) - { - //quit? - } - } - - //# Second pass. Scan for all includes - for (iter=allFiles.begin() ; iter!=allFiles.end() ; iter++) - { - FileRec *include = iter->second; - if (include->type == FileRec::CFILE) - { - //String cFileName = iter->first; - FileRec *ofile = new FileRec(FileRec::OFILE); - ofile->path = include->path; - ofile->baseName = include->baseName; - ofile->suffix = include->suffix; - String fname = include->path; - if (fname.size()>0) - fname.append("/"); - fname.append(include->baseName); - fname.append(".o"); - oFiles[fname] = ofile; - //add the .c file first? no, don't - //ofile->files[cFileName] = include; - - //trace("ofile:%s", fname.c_str()); - - processDependency(ofile, include); - } - } - - - return true; -} - - - -/** - * High-level call to generate deps and optionally save them - */ -bool DepTool::generateDependencies(const String &fileName) -{ - if (!createFileList()) - return false; - if (!generateDependencies()) - return false; - if (!saveDepFile(fileName)) - return false; - return true; -} - - -/** - * This saves the dependency cache. - */ -bool DepTool::saveDepFile(const String &fileName) -{ - time_t tim; - time(&tim); - - FILE *f = fopen(fileName.c_str(), "w"); - if (!f) - { - trace("cannot open '%s' for writing", fileName.c_str()); - } - fprintf(f, "<?xml version='1.0'?>\n"); - fprintf(f, "<!--\n"); - fprintf(f, "########################################################\n"); - fprintf(f, "## File: build.dep\n"); - fprintf(f, "## Generated by BuildTool at :%s", ctime(&tim)); - fprintf(f, "########################################################\n"); - fprintf(f, "-->\n"); - - fprintf(f, "<dependencies source='%s'>\n\n", sourceDir.c_str()); - std::map<String, FileRec *>::iterator iter; - for (iter=oFiles.begin() ; iter!=oFiles.end() ; iter++) - { - FileRec *frec = iter->second; - if (frec->type == FileRec::OFILE) - { - fprintf(f, "<object path='%s' name='%s' suffix='%s'>\n", - frec->path.c_str(), frec->baseName.c_str(), frec->suffix.c_str()); - std::map<String, FileRec *>::iterator citer; - for (citer=frec->files.begin() ; citer!=frec->files.end() ; citer++) - { - String cfname = citer->first; - fprintf(f, " <dep name='%s'/>\n", cfname.c_str()); - } - fprintf(f, "</object>\n\n"); - } - } - - fprintf(f, "</dependencies>\n"); - fprintf(f, "\n"); - fprintf(f, "<!--\n"); - fprintf(f, "########################################################\n"); - fprintf(f, "## E N D\n"); - fprintf(f, "########################################################\n"); - fprintf(f, "-->\n"); - - fclose(f); - - return true; -} - - - - -/** - * This loads the dependency cache. - */ -std::vector<DepRec> DepTool::loadDepFile(const String &depFile) -{ - std::vector<DepRec> result; - - Parser parser; - Element *root = parser.parseFile(depFile.c_str()); - if (!root) - { - //error("Could not open %s for reading", depFile.c_str()); - return result; - } - - if (root->getChildren().size()==0 || - root->getChildren()[0]->getName()!="dependencies") - { - error("loadDepFile: main xml element should be <dependencies>"); - delete root; - return result; - } - - //########## Start parsing - Element *depList = root->getChildren()[0]; - - std::vector<Element *> objects = depList->getChildren(); - for (std::size_t i=0 ; i<objects.size() ; i++) - { - Element *objectElem = objects[i]; - String tagName = objectElem->getName(); - if (tagName != "object") - { - error("loadDepFile: <dependencies> should have only <object> children"); - return result; - } - - String objName = objectElem->getAttribute("name"); - //trace("object:%s", objName.c_str()); - DepRec depObject(objName); - depObject.path = objectElem->getAttribute("path"); - depObject.suffix = objectElem->getAttribute("suffix"); - //########## DESCRIPTION - std::vector<Element *> depElems = objectElem->getChildren(); - for (std::size_t i=0 ; i<depElems.size() ; i++) - { - Element *depElem = depElems[i]; - tagName = depElem->getName(); - if (tagName != "dep") - { - error("loadDepFile: <object> should have only <dep> children"); - return result; - } - String depName = depElem->getAttribute("name"); - //trace(" dep:%s", depName.c_str()); - depObject.files.push_back(depName); - } - - //Insert into the result list, in a sorted manner - bool inserted = false; - std::vector<DepRec>::iterator iter; - for (iter = result.begin() ; iter != result.end() ; iter++) - { - String vpath = iter->path; - vpath.append("/"); - vpath.append(iter->name); - String opath = depObject.path; - opath.append("/"); - opath.append(depObject.name); - if (vpath > opath) - { - inserted = true; - iter = result.insert(iter, depObject); - break; - } - } - if (!inserted) - result.push_back(depObject); - } - - delete root; - - return result; -} - - -/** - * This loads the dependency cache. - */ -std::vector<DepRec> DepTool::getDepFile(const String &depFile, - bool forceRefresh) -{ - std::vector<DepRec> result; - if (forceRefresh) - { - generateDependencies(depFile); - result = loadDepFile(depFile); - } - else - { - //try once - result = loadDepFile(depFile); - if (result.size() == 0) - { - //fail? try again - generateDependencies(depFile); - result = loadDepFile(depFile); - } - } - return result; -} - - - - -//######################################################################## -//# T A S K -//######################################################################## -//forward decl -class Target; -class Make; - -/** - * - */ -class Task : public MakeBase -{ - -public: - - typedef enum - { - TASK_NONE, - TASK_CC, - TASK_COPY, - TASK_CXXTEST_PART, - TASK_CXXTEST_ROOT, - TASK_CXXTEST_RUN, - TASK_DELETE, - TASK_ECHO, - TASK_JAR, - TASK_JAVAC, - TASK_LINK, - TASK_MAKEFILE, - TASK_MKDIR, - TASK_MSGFMT, - TASK_PKG_CONFIG, - TASK_RANLIB, - TASK_RC, - TASK_SHAREDLIB, - TASK_STATICLIB, - TASK_STRIP, - TASK_TOUCH, - TASK_TSTAMP - } TaskType; - - - /** - * - */ - Task(MakeBase &par) : parent(par) - { init(); } - - /** - * - */ - Task(const Task &other) : parent(other.parent) - { init(); assign(other); } - - /** - * - */ - Task &operator=(const Task &other) - { assign(other); return *this; } - - /** - * - */ - virtual ~Task() - { } - - - /** - * - */ - virtual MakeBase &getParent() - { return parent; } - - /** - * - */ - virtual int getType() - { return type; } - - /** - * - */ - virtual void setType(int val) - { type = val; } - - /** - * - */ - virtual String getName() - { return name; } - - /** - * - */ - virtual bool execute() - { return true; } - - /** - * - */ - virtual bool parse(Element *elem) - { return true; } - - /** - * - */ - Task *createTask(Element *elem, int lineNr); - - -protected: - - void init() - { - type = TASK_NONE; - name = "none"; - } - - void assign(const Task &other) - { - type = other.type; - name = other.name; - } - - /** - * Show task status - */ - void taskstatus(const char *fmt, ...) - { - va_list args; - va_start(args,fmt); - fprintf(stdout, " %s : ", name.c_str()); - vfprintf(stdout, fmt, args); - fprintf(stdout, "\n"); - va_end(args) ; - } - - String getAttribute(Element *elem, const String &attrName) - { - String str; - return str; - } - - MakeBase &parent; - - int type; - - String name; -}; - - - -/** - * This task runs the C/C++ compiler. The compiler is invoked - * for all .c or .cpp files which are newer than their correcsponding - * .o files. - */ -class TaskCC : public Task -{ -public: - - TaskCC(MakeBase &par) : Task(par) - { - type = TASK_CC; - name = "cc"; - } - - virtual ~TaskCC() - {} - - virtual bool isExcludedInc(const String &dirname) - { - for (std::size_t i=0 ; i<excludeInc.size() ; i++) - { - String fname = excludeInc[i]; - if (fname == dirname) - return true; - } - return false; - } - - virtual bool execute() - { - //evaluate our parameters - String command = parent.eval(commandOpt, "gcc"); - String ccCommand = parent.eval(ccCommandOpt, "gcc"); - String cxxCommand = parent.eval(cxxCommandOpt, "g++"); - String source = parent.eval(sourceOpt, "."); - String dest = parent.eval(destOpt, "."); - String ccflags = parent.eval(flagsOpt, ""); - String cxxflags = parent.eval(cxxflagsOpt, ""); - String defines = parent.eval(definesOpt, ""); - String includes = parent.eval(includesOpt, ""); - bool continueOnError = parent.evalBool(continueOnErrorOpt, true); - bool refreshCache = parent.evalBool(refreshCacheOpt, false); - - if (!listFiles(parent, fileSet)) - return false; - - FILE *f = NULL; - f = fopen("compile.lst", "w"); - - //refreshCache is probably false here, unless specified otherwise - String fullName = parent.resolve("build.dep"); - if (refreshCache || isNewerThan(parent.getURI().getPath(), fullName)) - { - taskstatus("regenerating C/C++ dependency cache"); - refreshCache = true; - } - - DepTool depTool; - depTool.setSourceDirectory(source); - depTool.setFileList(fileSet.getFiles()); - std::vector<DepRec> deps = - depTool.getDepFile("build.dep", refreshCache); - - String incs; - incs.append("-I"); - incs.append(parent.resolve(".")); - incs.append(" "); - if (includes.size()>0) - { - incs.append(includes); - incs.append(" "); - } - std::set<String> paths; - std::vector<DepRec>::iterator viter; - for (viter=deps.begin() ; viter!=deps.end() ; viter++) - { - DepRec dep = *viter; - if (dep.path.size()>0) - paths.insert(dep.path); - } - if (source.size()>0) - { - incs.append(" -I"); - incs.append(parent.resolve(source)); - incs.append(" "); - } - std::set<String>::iterator setIter; - for (setIter=paths.begin() ; setIter!=paths.end() ; setIter++) - { - String dirName = *setIter; - //check excludeInc to see if we dont want to include this dir - if (isExcludedInc(dirName)) - continue; - incs.append(" -I"); - String dname; - if (source.size()>0) - { - dname.append(source); - dname.append("/"); - } - dname.append(dirName); - incs.append(parent.resolve(dname)); - } - -// First create all directories, fails if done in OpenMP parallel loop below... goes superfast anyway, so don't optimize - for (std::size_t fi = 0; fi < deps.size() ; ++fi) - { - DepRec dep = deps[fi]; - - //## Make paths - String destPath = dest; - if (dep.path.size()>0) - { - destPath.append("/"); - destPath.append(dep.path); - } - //## Make sure destination directory exists - if (!createDirectory(destPath)) - { - taskstatus("problem creating folder: %s", destPath.c_str()); - if (f) { - fclose(f); - } - return false; - } - } - - /** - * Compile each of the C files that need it - */ - bool errorOccurred = false; - -#ifdef _OPENMP - taskstatus("compile with %d threads in parallel", numThreads); -# pragma omp parallel for num_threads(numThreads) -#endif - - for (std::size_t fi = 0; fi < deps.size() ; ++fi) - { - DepRec dep = deps[fi]; - - //## Select command - String sfx = dep.suffix; - String command = ccCommand; - String flags = ccflags; - if (sfx == "cpp" || sfx == "cxx" || sfx == "c++" || - sfx == "cc" || sfx == "CC") - { - command = cxxCommand; - flags += " " + cxxflags; - } - - //## Make paths - String destPath = dest; - String srcPath = source; - if (dep.path.size()>0) - { - destPath.append("/"); - destPath.append(dep.path); - srcPath.append("/"); - srcPath.append(dep.path); - } - - //## Check whether it needs to be done - String destName; - if (destPath.size()>0) - { - destName.append(destPath); - destName.append("/"); - } - destName.append(dep.name); - destName.append(".o"); - String destFullName = parent.resolve(destName); - String srcName; - if (srcPath.size()>0) - { - srcName.append(srcPath); - srcName.append("/"); - } - srcName.append(dep.name); - srcName.append("."); - srcName.append(dep.suffix); - String srcFullName = parent.resolve(srcName); - bool compileMe = false; - //# First we check if the source is newer than the .o - if (isNewerThan(srcFullName, destFullName)) - { -// taskstatus("compile of %s (req. by: %s)", -// destFullName.c_str(), srcFullName.c_str()); - fprintf(stdout, "compile %s\n", srcFullName.c_str()); - compileMe = true; - } - else - { - //# secondly, we check if any of the included dependencies - //# of the .c/.cpp is newer than the .o - for (std::size_t i=0 ; i<dep.files.size() ; i++) - { - String depName; - if (source.size()>0) - { - depName.append(source); - depName.append("/"); - } - depName.append(dep.files[i]); - String depFullName = parent.resolve(depName); - bool depRequires = isNewerThan(depFullName, destFullName); - //trace("%d %s %s\n", depRequires, - // destFullName.c_str(), depFullName.c_str()); - if (depRequires) - { - taskstatus("compile %s (%s modified)", - srcFullName.c_str(), depFullName.c_str()); - compileMe = true; - break; - } - } - } - if (!compileMe) - { - continue; - } - - //## Assemble the command - String cmd = command; - cmd.append(" -c "); - cmd.append(flags); - cmd.append(" "); - cmd.append(defines); - cmd.append(" "); - cmd.append(incs); - cmd.append(" "); - cmd.append(srcFullName); - cmd.append(" -o "); - cmd.append(destFullName); - - //## Execute the command - - String outString, errString; - bool ret = executeCommand(cmd.c_str(), "", outString, errString); - - if (f) - { - fprintf(f, "########################### File : %s\n", - srcFullName.c_str()); - fprintf(f, "#### COMMAND ###\n"); - int col = 0; - for (std::size_t i = 0 ; i < cmd.size() ; i++) - { - char ch = cmd[i]; - if (isspace(ch) && col > 63) - { - fputc('\n', f); - col = 0; - } - else - { - fputc(ch, f); - col++; - } - if (col > 76) - { - fputc('\n', f); - col = 0; - } - } - fprintf(f, "\n"); - fprintf(f, "#### STDOUT ###\n%s\n", outString.c_str()); - fprintf(f, "#### STDERR ###\n%s\n\n", errString.c_str()); - fflush(f); - } - if (!ret) { - error("problem compiling: %s", errString.c_str()); - errorOccurred = true; - } else if (!errString.empty()) { - fprintf(stdout, "STDERR: \n%s\n", errString.c_str()); - } - - - if (errorOccurred && !continueOnError) { -#ifndef _OPENMP // figure out a way to break the loop here with OpenMP - break; -#endif - } - - removeFromStatCache(getNativePath(destFullName)); - } - - if (f) - { - fclose(f); - } - - return !errorOccurred; - } - - - virtual bool parse(Element *elem) - { - String s; - if (!parent.getAttribute(elem, "command", commandOpt)) - return false; - if (commandOpt.size()>0) - { cxxCommandOpt = ccCommandOpt = commandOpt; } - if (!parent.getAttribute(elem, "cc", ccCommandOpt)) - return false; - if (!parent.getAttribute(elem, "cxx", cxxCommandOpt)) - return false; - if (!parent.getAttribute(elem, "destdir", destOpt)) - return false; - if (!parent.getAttribute(elem, "continueOnError", continueOnErrorOpt)) - return false; - if (!parent.getAttribute(elem, "refreshCache", refreshCacheOpt)) - return false; - - std::vector<Element *> children = elem->getChildren(); - for (std::size_t i=0 ; i<children.size() ; i++) - { - Element *child = children[i]; - String tagName = child->getName(); - if (tagName == "flags") - { - if (!parent.getValue(child, flagsOpt)) - return false; - flagsOpt = strip(flagsOpt); - } - else if (tagName == "cxxflags") - { - if (!parent.getValue(child, cxxflagsOpt)) - return false; - cxxflagsOpt = strip(cxxflagsOpt); - } - else if (tagName == "includes") - { - if (!parent.getValue(child, includesOpt)) - return false; - includesOpt = strip(includesOpt); - } - else if (tagName == "defines") - { - if (!parent.getValue(child, definesOpt)) - return false; - definesOpt = strip(definesOpt); - } - else if (tagName == "fileset") - { - if (!parseFileSet(child, parent, fileSet)) - return false; - sourceOpt = fileSet.getDirectory(); - } - else if (tagName == "excludeinc") - { - if (!parseFileList(child, parent, excludeInc)) - return false; - } - } - - return true; - } - -protected: - - String commandOpt; - String ccCommandOpt; - String cxxCommandOpt; - String sourceOpt; - String destOpt; - String flagsOpt; - String cxxflagsOpt; - String definesOpt; - String includesOpt; - String continueOnErrorOpt; - String refreshCacheOpt; - FileSet fileSet; - FileList excludeInc; - -}; - - - -/** - * - */ -class TaskCopy : public Task -{ -public: - - typedef enum - { - CP_NONE, - CP_TOFILE, - CP_TODIR - } CopyType; - - TaskCopy(MakeBase &par) : Task(par) - { - type = TASK_COPY; - name = "copy"; - cptype = CP_NONE; - haveFileSet = false; - } - - virtual ~TaskCopy() - {} - - virtual bool execute() - { - String fileName = parent.eval(fileNameOpt , "."); - String toFileName = parent.eval(toFileNameOpt , "."); - String toDirName = parent.eval(toDirNameOpt , "."); - bool verbose = parent.evalBool(verboseOpt, false); - switch (cptype) - { - case CP_TOFILE: - { - if (fileName.size()>0) - { - taskstatus("%s to %s", - fileName.c_str(), toFileName.c_str()); - String fullSource = parent.resolve(fileName); - String fullDest = parent.resolve(toFileName); - if (verbose) - taskstatus("copy %s to file %s", fullSource.c_str(), - fullDest.c_str()); - if (!isRegularFile(fullSource)) - { - error("copy : file %s does not exist", fullSource.c_str()); - return false; - } - if (!isNewerThan(fullSource, fullDest)) - { - taskstatus("skipped"); - return true; - } - if (!copyFile(fullSource, fullDest)) - return false; - taskstatus("1 file copied"); - } - return true; - } - case CP_TODIR: - { - if (haveFileSet) - { - if (!listFiles(parent, fileSet)) - return false; - String fileSetDir = parent.eval(fileSet.getDirectory(), "."); - - taskstatus("%s to %s", - fileSetDir.c_str(), toDirName.c_str()); - - int nrFiles = 0; - for (std::size_t i=0 ; i<fileSet.size() ; i++) - { - String fileName = fileSet[i]; - - String sourcePath; - if (fileSetDir.size()>0) - { - sourcePath.append(fileSetDir); - sourcePath.append("/"); - } - sourcePath.append(fileName); - String fullSource = parent.resolve(sourcePath); - - //Get the immediate parent directory's base name - String baseFileSetDir = fileSetDir; - std::size_t pos = baseFileSetDir.find_last_of('/'); - if (pos!=baseFileSetDir.npos && - pos < baseFileSetDir.size()-1) - baseFileSetDir = - baseFileSetDir.substr(pos+1, - baseFileSetDir.size()); - //Now make the new path - String destPath; - if (toDirName.size()>0) - { - destPath.append(toDirName); - destPath.append("/"); - } - if (baseFileSetDir.size()>0) - { - destPath.append(baseFileSetDir); - destPath.append("/"); - } - destPath.append(fileName); - String fullDest = parent.resolve(destPath); - //trace("fileName:%s", fileName.c_str()); - if (verbose) - taskstatus("copy %s to new dir : %s", - fullSource.c_str(), fullDest.c_str()); - if (!isNewerThan(fullSource, fullDest)) - { - if (verbose) - taskstatus("copy skipping %s", fullSource.c_str()); - continue; - } - if (!copyFile(fullSource, fullDest)) - return false; - nrFiles++; - } - taskstatus("%d file(s) copied", nrFiles); - } - else //file source - { - //For file->dir we want only the basename of - //the source appended to the dest dir - taskstatus("%s to %s", - fileName.c_str(), toDirName.c_str()); - String baseName = fileName; - std::size_t pos = baseName.find_last_of('/'); - if (pos!=baseName.npos && pos<baseName.size()-1) - baseName = baseName.substr(pos+1, baseName.size()); - String fullSource = parent.resolve(fileName); - String destPath; - if (toDirName.size()>0) - { - destPath.append(toDirName); - destPath.append("/"); - } - destPath.append(baseName); - String fullDest = parent.resolve(destPath); - if (verbose) - taskstatus("file %s to new dir : %s", fullSource.c_str(), - fullDest.c_str()); - if (!isRegularFile(fullSource)) - { - error("copy : file %s does not exist", fullSource.c_str()); - return false; - } - if (!isNewerThan(fullSource, fullDest)) - { - taskstatus("skipped"); - return true; - } - if (!copyFile(fullSource, fullDest)) - return false; - taskstatus("1 file copied"); - } - return true; - } - } - return true; - } - - - virtual bool parse(Element *elem) - { - if (!parent.getAttribute(elem, "file", fileNameOpt)) - return false; - if (!parent.getAttribute(elem, "tofile", toFileNameOpt)) - return false; - if (toFileNameOpt.size() > 0) - cptype = CP_TOFILE; - if (!parent.getAttribute(elem, "todir", toDirNameOpt)) - return false; - if (toDirNameOpt.size() > 0) - cptype = CP_TODIR; - if (!parent.getAttribute(elem, "verbose", verboseOpt)) - return false; - - haveFileSet = false; - - std::vector<Element *> children = elem->getChildren(); - for (std::size_t i=0 ; i<children.size() ; i++) - { - Element *child = children[i]; - String tagName = child->getName(); - if (tagName == "fileset") - { - if (!parseFileSet(child, parent, fileSet)) - { - error("problem getting fileset"); - return false; - } - haveFileSet = true; - } - } - - //Perform validity checks - if (fileNameOpt.size()>0 && fileSet.size()>0) - { - error("<copy> can only have one of : file= and <fileset>"); - return false; - } - if (toFileNameOpt.size()>0 && toDirNameOpt.size()>0) - { - error("<copy> can only have one of : tofile= or todir="); - return false; - } - if (haveFileSet && toDirNameOpt.size()==0) - { - error("a <copy> task with a <fileset> must have : todir="); - return false; - } - if (cptype == CP_TOFILE && fileNameOpt.size()==0) - { - error("<copy> tofile= must be associated with : file="); - return false; - } - if (cptype == CP_TODIR && fileNameOpt.size()==0 && !haveFileSet) - { - error("<copy> todir= must be associated with : file= or <fileset>"); - return false; - } - - return true; - } - -private: - - int cptype; - bool haveFileSet; - - FileSet fileSet; - String fileNameOpt; - String toFileNameOpt; - String toDirNameOpt; - String verboseOpt; -}; - - -/** - * Generate CxxTest files - */ -class TaskCxxTestPart: public Task -{ -public: - - TaskCxxTestPart(MakeBase &par) : Task(par) - { - type = TASK_CXXTEST_PART; - name = "cxxtestpart"; - } - - virtual ~TaskCxxTestPart() - {} - - virtual bool execute() - { - if (!listFiles(parent, fileSet)) - return false; - String fileSetDir = parent.eval(fileSet.getDirectory(), "."); - - String fullDest = parent.resolve(parent.eval(destPathOpt, ".")); - String cmd = parent.eval(commandOpt, "cxxtestgen.py"); - cmd.append(" --part -o "); - cmd.append(fullDest); - - unsigned int newFiles = 0; - for (std::size_t i=0 ; i<fileSet.size() ; i++) - { - String fileName = fileSet[i]; - if (getSuffix(fileName) != "h") - continue; - String sourcePath; - if (fileSetDir.size()>0) - { - sourcePath.append(fileSetDir); - sourcePath.append("/"); - } - sourcePath.append(fileName); - String fullSource = parent.resolve(sourcePath); - - cmd.append(" "); - cmd.append(fullSource); - if (isNewerThan(fullSource, fullDest)) newFiles++; - } - - if (newFiles>0) { - size_t const lastSlash = fullDest.find_last_of('/'); - if (lastSlash != fullDest.npos) { - String directory(fullDest, 0, lastSlash); - if (!createDirectory(directory)) - return false; - } - - String outString, errString; - if (!executeCommand(cmd.c_str(), "", outString, errString)) - { - error("<cxxtestpart> problem: %s", errString.c_str()); - return false; - } - removeFromStatCache(getNativePath(fullDest)); - } - - return true; - } - - virtual bool parse(Element *elem) - { - if (!parent.getAttribute(elem, "command", commandOpt)) - return false; - if (!parent.getAttribute(elem, "out", destPathOpt)) - return false; - - std::vector<Element *> children = elem->getChildren(); - for (std::size_t i=0 ; i<children.size() ; i++) - { - Element *child = children[i]; - String tagName = child->getName(); - if (tagName == "fileset") - { - if (!parseFileSet(child, parent, fileSet)) - return false; - } - } - return true; - } - -private: - - String commandOpt; - String destPathOpt; - FileSet fileSet; - -}; - - -/** - * Generate the CxxTest root file - */ -class TaskCxxTestRoot: public Task -{ -public: - - TaskCxxTestRoot(MakeBase &par) : Task(par) - { - type = TASK_CXXTEST_ROOT; - name = "cxxtestroot"; - } - - virtual ~TaskCxxTestRoot() - {} - - virtual bool execute() - { - if (!listFiles(parent, fileSet)) - return false; - String fileSetDir = parent.eval(fileSet.getDirectory(), "."); - unsigned int newFiles = 0; - - String fullDest = parent.resolve(parent.eval(destPathOpt, ".")); - String cmd = parent.eval(commandOpt, "cxxtestgen.py"); - cmd.append(" --root -o "); - cmd.append(fullDest); - String templateFile = parent.eval(templateFileOpt, ""); - if (templateFile.size()>0) { - String fullTemplate = parent.resolve(templateFile); - cmd.append(" --template="); - cmd.append(fullTemplate); - if (isNewerThan(fullTemplate, fullDest)) newFiles++; - } - - for (std::size_t i=0 ; i<fileSet.size() ; i++) - { - String fileName = fileSet[i]; - if (getSuffix(fileName) != "h") - continue; - String sourcePath; - if (fileSetDir.size()>0) - { - sourcePath.append(fileSetDir); - sourcePath.append("/"); - } - sourcePath.append(fileName); - String fullSource = parent.resolve(sourcePath); - - cmd.append(" "); - cmd.append(fullSource); - if (isNewerThan(fullSource, fullDest)) newFiles++; - } - - if (newFiles>0) { - size_t const lastSlash = fullDest.find_last_of('/'); - if (lastSlash != fullDest.npos) { - String directory(fullDest, 0, lastSlash); - if (!createDirectory(directory)) - return false; - } - - String outString, errString; - if (!executeCommand(cmd.c_str(), "", outString, errString)) - { - error("<cxxtestroot> problem: %s", errString.c_str()); - return false; - } - removeFromStatCache(getNativePath(fullDest)); - } - - return true; - } - - virtual bool parse(Element *elem) - { - if (!parent.getAttribute(elem, "command", commandOpt)) - return false; - if (!parent.getAttribute(elem, "template", templateFileOpt)) - return false; - if (!parent.getAttribute(elem, "out", destPathOpt)) - return false; - - std::vector<Element *> children = elem->getChildren(); - for (std::size_t i=0 ; i<children.size() ; i++) - { - Element *child = children[i]; - String tagName = child->getName(); - if (tagName == "fileset") - { - if (!parseFileSet(child, parent, fileSet)) - return false; - } - } - return true; - } - -private: - - String commandOpt; - String templateFileOpt; - String destPathOpt; - FileSet fileSet; - -}; - - -/** - * Execute the CxxTest test executable - */ -class TaskCxxTestRun: public Task -{ -public: - - TaskCxxTestRun(MakeBase &par) : Task(par) - { - type = TASK_CXXTEST_RUN; - name = "cxxtestrun"; - } - - virtual ~TaskCxxTestRun() - {} - - virtual bool execute() - { - unsigned int newFiles = 0; - - String workingDir = parent.resolve(parent.eval(workingDirOpt, "inkscape")); - String rawCmd = parent.eval(commandOpt, "build/cxxtests"); - - String cmdExe; - if (fileExists(rawCmd)) { - cmdExe = rawCmd; - } else if (fileExists(rawCmd + ".exe")) { - cmdExe = rawCmd + ".exe"; - } else { - error("<cxxtestrun> problem: cxxtests executable not found! (command=\"%s\")", rawCmd.c_str()); - } - // Note that the log file names are based on the exact name used to call cxxtests (it uses argv[0] + ".log"/".xml") - if (isNewerThan(cmdExe, rawCmd + ".log") || isNewerThan(cmdExe, rawCmd + ".xml")) newFiles++; - - // Prepend the necessary ../'s - String cmd = rawCmd; - unsigned int workingDirDepth = 0; - bool wasSlash = true; - for(size_t i=0; i<workingDir.size(); i++) { - // This assumes no . and .. parts - if (wasSlash && workingDir[i]!='/') workingDirDepth++; - wasSlash = workingDir[i] == '/'; - } - for(size_t i=0; i<workingDirDepth; i++) { - cmd = "../" + cmd; - } - - if (newFiles>0) { - char olddir[1024]; - if (workingDir.size()>0) { - // TODO: Double-check usage of getcwd and handle chdir errors - getcwd(olddir, 1024); - chdir(workingDir.c_str()); - } - - String outString; - if (!executeCommand(cmd.c_str(), "", outString, outString)) - { - error("<cxxtestrun> problem: %s", outString.c_str()); - return false; - } - - if (workingDir.size()>0) { - // TODO: Handle errors? - chdir(olddir); - } - - removeFromStatCache(getNativePath(cmd + ".log")); - removeFromStatCache(getNativePath(cmd + ".xml")); - } - - return true; - } - - virtual bool parse(Element *elem) - { - if (!parent.getAttribute(elem, "command", commandOpt)) - return false; - if (!parent.getAttribute(elem, "workingdir", workingDirOpt)) - return false; - return true; - } - -private: - - String commandOpt; - String workingDirOpt; - -}; - - -/** - * - */ -class TaskDelete : public Task -{ -public: - - typedef enum - { - DEL_FILE, - DEL_DIR, - DEL_FILESET - } DeleteType; - - TaskDelete(MakeBase &par) : Task(par) - { - type = TASK_DELETE; - name = "delete"; - delType = DEL_FILE; - } - - virtual ~TaskDelete() - {} - - virtual bool execute() - { - String dirName = parent.eval(dirNameOpt, "."); - String fileName = parent.eval(fileNameOpt, "."); - bool verbose = parent.evalBool(verboseOpt, false); - bool quiet = parent.evalBool(quietOpt, false); - bool failOnError = parent.evalBool(failOnErrorOpt, true); - switch (delType) - { - case DEL_FILE: - { - taskstatus("file: %s", fileName.c_str()); - String fullName = parent.resolve(fileName); - char *fname = (char *)fullName.c_str(); - if (!quiet && verbose) - taskstatus("path: %s", fname); - if (failOnError && !removeFile(fullName)) - { - //error("Could not delete file '%s'", fullName.c_str()); - return false; - } - return true; - } - case DEL_DIR: - { - taskstatus("dir: %s", dirName.c_str()); - String fullDir = parent.resolve(dirName); - if (!quiet && verbose) - taskstatus("path: %s", fullDir.c_str()); - if (failOnError && !removeDirectory(fullDir)) - { - //error("Could not delete directory '%s'", fullDir.c_str()); - return false; - } - return true; - } - } - return true; - } - - virtual bool parse(Element *elem) - { - if (!parent.getAttribute(elem, "file", fileNameOpt)) - return false; - if (fileNameOpt.size() > 0) - delType = DEL_FILE; - if (!parent.getAttribute(elem, "dir", dirNameOpt)) - return false; - if (dirNameOpt.size() > 0) - delType = DEL_DIR; - if (fileNameOpt.size()>0 && dirNameOpt.size()>0) - { - error("<delete> can have one attribute of file= or dir="); - return false; - } - if (fileNameOpt.size()==0 && dirNameOpt.size()==0) - { - error("<delete> must have one attribute of file= or dir="); - return false; - } - if (!parent.getAttribute(elem, "verbose", verboseOpt)) - return false; - if (!parent.getAttribute(elem, "quiet", quietOpt)) - return false; - if (!parent.getAttribute(elem, "failonerror", failOnErrorOpt)) - return false; - return true; - } - -private: - - int delType; - String dirNameOpt; - String fileNameOpt; - String verboseOpt; - String quietOpt; - String failOnErrorOpt; -}; - - -/** - * Send a message to stdout - */ -class TaskEcho : public Task -{ -public: - - TaskEcho(MakeBase &par) : Task(par) - { type = TASK_ECHO; name = "echo"; } - - virtual ~TaskEcho() - {} - - virtual bool execute() - { - //let message have priority over text - String message = parent.eval(messageOpt, ""); - String text = parent.eval(textOpt, ""); - if (message.size() > 0) - { - fprintf(stdout, "%s\n", message.c_str()); - } - else if (text.size() > 0) - { - fprintf(stdout, "%s\n", text.c_str()); - } - return true; - } - - virtual bool parse(Element *elem) - { - if (!parent.getValue(elem, textOpt)) - return false; - textOpt = leftJustify(textOpt); - if (!parent.getAttribute(elem, "message", messageOpt)) - return false; - return true; - } - -private: - - String messageOpt; - String textOpt; -}; - - - -/** - * - */ -class TaskJar : public Task -{ -public: - - TaskJar(MakeBase &par) : Task(par) - { type = TASK_JAR; name = "jar"; } - - virtual ~TaskJar() - {} - - virtual bool execute() - { - String command = parent.eval(commandOpt, "jar"); - String basedir = parent.eval(basedirOpt, "."); - String destfile = parent.eval(destfileOpt, "."); - - String cmd = command; - cmd.append(" -cf "); - cmd.append(destfile); - cmd.append(" -C "); - cmd.append(basedir); - cmd.append(" ."); - - String execCmd = cmd; - - String outString, errString; - bool ret = executeCommand(execCmd.c_str(), "", outString, errString); - if (!ret) - { - error("<jar> command '%s' failed :\n %s", - execCmd.c_str(), errString.c_str()); - return false; - } - removeFromStatCache(getNativePath(destfile)); - return true; - } - - virtual bool parse(Element *elem) - { - if (!parent.getAttribute(elem, "command", commandOpt)) - return false; - if (!parent.getAttribute(elem, "basedir", basedirOpt)) - return false; - if (!parent.getAttribute(elem, "destfile", destfileOpt)) - return false; - if (basedirOpt.size() == 0 || destfileOpt.size() == 0) - { - error("<jar> required both basedir and destfile attributes to be set"); - return false; - } - return true; - } - -private: - - String commandOpt; - String basedirOpt; - String destfileOpt; -}; - - -/** - * - */ -class TaskJavac : public Task -{ -public: - - TaskJavac(MakeBase &par) : Task(par) - { - type = TASK_JAVAC; name = "javac"; - } - - virtual ~TaskJavac() - {} - - virtual bool execute() - { - String command = parent.eval(commandOpt, "javac"); - String srcdir = parent.eval(srcdirOpt, "."); - String destdir = parent.eval(destdirOpt, "."); - String target = parent.eval(targetOpt, ""); - - std::vector<String> fileList; - if (!listFiles(srcdir, "", fileList)) - { - return false; - } - String cmd = command; - cmd.append(" -d "); - cmd.append(destdir); - cmd.append(" -classpath "); - cmd.append(destdir); - cmd.append(" -sourcepath "); - cmd.append(srcdir); - cmd.append(" "); - if (target.size()>0) - { - cmd.append(" -target "); - cmd.append(target); - cmd.append(" "); - } - String fname = "javalist.btool"; - FILE *f = fopen(fname.c_str(), "w"); - int count = 0; - for (std::size_t i=0 ; i<fileList.size() ; i++) - { - String fname = fileList[i]; - String srcName = fname; - if (fname.size()<6) //x.java - continue; - if (fname.compare(fname.size()-5, 5, ".java") != 0) - continue; - String baseName = fname.substr(0, fname.size()-5); - String destName = baseName; - destName.append(".class"); - - String fullSrc = srcdir; - fullSrc.append("/"); - fullSrc.append(fname); - String fullDest = destdir; - fullDest.append("/"); - fullDest.append(destName); - //trace("fullsrc:%s fulldest:%s", fullSrc.c_str(), fullDest.c_str()); - if (!isNewerThan(fullSrc, fullDest)) - continue; - - count++; - fprintf(f, "%s\n", fullSrc.c_str()); - } - fclose(f); - if (!count) - { - taskstatus("nothing to do"); - return true; - } - - taskstatus("compiling %d files", count); - - String execCmd = cmd; - execCmd.append("@"); - execCmd.append(fname); - - String outString, errString; - bool ret = executeCommand(execCmd.c_str(), "", outString, errString); - if (!ret) - { - error("<javac> command '%s' failed :\n %s", - execCmd.c_str(), errString.c_str()); - return false; - } - // TODO: - //removeFromStatCache(getNativePath(........)); - return true; - } - - virtual bool parse(Element *elem) - { - if (!parent.getAttribute(elem, "command", commandOpt)) - return false; - if (!parent.getAttribute(elem, "srcdir", srcdirOpt)) - return false; - if (!parent.getAttribute(elem, "destdir", destdirOpt)) - return false; - if (srcdirOpt.size() == 0 || destdirOpt.size() == 0) - { - error("<javac> required both srcdir and destdir attributes to be set"); - return false; - } - if (!parent.getAttribute(elem, "target", targetOpt)) - return false; - return true; - } - -private: - - String commandOpt; - String srcdirOpt; - String destdirOpt; - String targetOpt; - -}; - - -/** - * - */ -class TaskLink : public Task -{ -public: - - TaskLink(MakeBase &par) : Task(par) - { - type = TASK_LINK; name = "link"; - } - - virtual ~TaskLink() - {} - - virtual void UniqueParams(std::string& source) { - size_t prev = 0; - size_t next = 0; - std::list<std::string> thelist; - std::list<std::string>::iterator it; - std::string tstring=" "; - source +=std::string(" "); // else the last token may be lost - while ((next = source.find_first_of(" ", prev)) != std::string::npos){ - if (next - prev != 0){ - thelist.push_back(source.substr(prev, next - prev)); - } - prev = next + 1; - } - thelist.sort(); - source.clear(); - source +=std::string(" "); - for(it=thelist.begin(); it!=thelist.end();it++){ - if(*it != tstring){ - tstring = *it; - source +=tstring; - source +=std::string(" "); - } - } - } - - virtual bool execute() - { - String command = parent.eval(commandOpt, "g++"); - String fileName = parent.eval(fileNameOpt, ""); - String flags = parent.eval(flagsOpt, ""); - String libs = parent.eval(libsOpt, ""); - bool doStrip = parent.evalBool(doStripOpt, false); - String symFileName = parent.eval(symFileNameOpt, ""); - String stripCommand = parent.eval(stripCommandOpt, "strip"); - String objcopyCommand = parent.eval(objcopyCommandOpt, "objcopy"); - - if (!listFiles(parent, fileSet)) - return false; - String fileSetDir = parent.eval(fileSet.getDirectory(), "."); - //trace("%d files in %s", fileSet.size(), fileSetDir.c_str()); - bool doit = false; - String fullTarget = parent.resolve(fileName); - String cmd = command; - cmd.append(" -o "); - cmd.append(fullTarget); - cmd.append(" "); - cmd.append(flags); - for (std::size_t i=0 ; i<fileSet.size() ; i++) - { - cmd.append(" "); - String obj; - if (fileSetDir.size()>0) - { - obj.append(fileSetDir); - obj.append("/"); - } - obj.append(fileSet[i]); - String fullObj = parent.resolve(obj); - String nativeFullObj = getNativePath(fullObj); - cmd.append(nativeFullObj); - //trace("link: tgt:%s obj:%s", fullTarget.c_str(), - // fullObj.c_str()); - if (isNewerThan(fullObj, fullTarget)) - doit = true; - } - cmd.append(" "); - // trim it down to unique elements, reduce command line size - UniqueParams(libs); - cmd.append(libs); - if (!doit) - { - //trace("link not needed"); - return true; - } - //trace("LINK cmd:%s", cmd.c_str()); - - - String outbuf, errbuf; - // std::cout << "DEBUG command = " << cmd << std::endl; - if (!executeCommand(cmd.c_str(), "", outbuf, errbuf)) - { - error("LINK problem: %s", errbuf.c_str()); - return false; - } - removeFromStatCache(getNativePath(fullTarget)); - - if (symFileName.size()>0) - { - String symFullName = parent.resolve(symFileName); - cmd = objcopyCommand; - cmd.append(" --only-keep-debug "); - cmd.append(getNativePath(fullTarget)); - cmd.append(" "); - cmd.append(getNativePath(symFullName)); - if (!executeCommand(cmd, "", outbuf, errbuf)) - { - error("<strip> symbol file failed : %s", errbuf.c_str()); - return false; - } - removeFromStatCache(getNativePath(symFullName)); - } - - if (doStrip) - { - cmd = stripCommand; - cmd.append(" "); - cmd.append(getNativePath(fullTarget)); - if (!executeCommand(cmd, "", outbuf, errbuf)) - { - error("<strip> failed : %s", errbuf.c_str()); - return false; - } - removeFromStatCache(getNativePath(fullTarget)); - } - - return true; - } - - virtual bool parse(Element *elem) - { - if (!parent.getAttribute(elem, "command", commandOpt)) - return false; - if (!parent.getAttribute(elem, "objcopycommand", objcopyCommandOpt)) - return false; - if (!parent.getAttribute(elem, "stripcommand", stripCommandOpt)) - return false; - if (!parent.getAttribute(elem, "out", fileNameOpt)) - return false; - if (!parent.getAttribute(elem, "strip", doStripOpt)) - return false; - if (!parent.getAttribute(elem, "symfile", symFileNameOpt)) - return false; - - std::vector<Element *> children = elem->getChildren(); - for (std::size_t i=0 ; i<children.size() ; i++) - { - Element *child = children[i]; - String tagName = child->getName(); - if (tagName == "fileset") - { - if (!parseFileSet(child, parent, fileSet)) - return false; - } - else if (tagName == "flags") - { - if (!parent.getValue(child, flagsOpt)) - return false; - flagsOpt = strip(flagsOpt); - } - else if (tagName == "libs") - { - if (!parent.getValue(child, libsOpt)) - return false; - libsOpt = strip(libsOpt); - } - } - return true; - } - -private: - - FileSet fileSet; - - String commandOpt; - String fileNameOpt; - String flagsOpt; - String libsOpt; - String doStripOpt; - String symFileNameOpt; - String stripCommandOpt; - String objcopyCommandOpt; - -}; - - - -/** - * Create a named file - */ -class TaskMakeFile : public Task -{ -public: - - TaskMakeFile(MakeBase &par) : Task(par) - { type = TASK_MAKEFILE; name = "makefile"; } - - virtual ~TaskMakeFile() - {} - - virtual bool execute() - { - String fileName = parent.eval(fileNameOpt, ""); - bool force = parent.evalBool(forceOpt, false); - String text = parent.eval(textOpt, ""); - - taskstatus("%s", fileName.c_str()); - String fullName = parent.resolve(fileName); - if (!force && !isNewerThan(parent.getURI().getPath(), fullName)) - { - taskstatus("skipped"); - return true; - } - String fullNative = getNativePath(fullName); - //trace("fullName:%s", fullName.c_str()); - FILE *f = fopen(fullNative.c_str(), "w"); - if (!f) - { - error("<makefile> could not open %s for writing : %s", - fullName.c_str(), strerror(errno)); - return false; - } - for (std::size_t i=0 ; i<text.size() ; i++) - fputc(text[i], f); - fputc('\n', f); - fclose(f); - removeFromStatCache(fullNative); - return true; - } - - virtual bool parse(Element *elem) - { - if (!parent.getAttribute(elem, "file", fileNameOpt)) - return false; - if (!parent.getAttribute(elem, "force", forceOpt)) - return false; - if (fileNameOpt.size() == 0) - { - error("<makefile> requires 'file=\"filename\"' attribute"); - return false; - } - if (!parent.getValue(elem, textOpt)) - return false; - textOpt = leftJustify(textOpt); - //trace("dirname:%s", dirName.c_str()); - return true; - } - -private: - - String fileNameOpt; - String forceOpt; - String textOpt; -}; - - - -/** - * Create a named directory - */ -class TaskMkDir : public Task -{ -public: - - TaskMkDir(MakeBase &par) : Task(par) - { type = TASK_MKDIR; name = "mkdir"; } - - virtual ~TaskMkDir() - {} - - virtual bool execute() - { - String dirName = parent.eval(dirNameOpt, "."); - - taskstatus("%s", dirName.c_str()); - String fullDir = parent.resolve(dirName); - //trace("fullDir:%s", fullDir.c_str()); - if (!createDirectory(fullDir)) - return false; - return true; - } - - virtual bool parse(Element *elem) - { - if (!parent.getAttribute(elem, "dir", dirNameOpt)) - return false; - if (dirNameOpt.size() == 0) - { - error("<mkdir> requires 'dir=\"dirname\"' attribute"); - return false; - } - return true; - } - -private: - - String dirNameOpt; -}; - - - -/** - * Create a named directory - */ -class TaskMsgFmt: public Task -{ -public: - - TaskMsgFmt(MakeBase &par) : Task(par) - { type = TASK_MSGFMT; name = "msgfmt"; } - - virtual ~TaskMsgFmt() - {} - - virtual bool execute() - { - String command = parent.eval(commandOpt, "msgfmt"); - String toDirName = parent.eval(toDirNameOpt, "."); - String outName = parent.eval(outNameOpt, ""); - bool owndir = parent.evalBool(owndirOpt, false); - - if (!listFiles(parent, fileSet)) - return false; - String fileSetDir = fileSet.getDirectory(); - - //trace("msgfmt: %d", fileSet.size()); - for (std::size_t i=0 ; i<fileSet.size() ; i++) - { - String fileName = fileSet[i]; - if (getSuffix(fileName) != "po") - continue; - String sourcePath; - if (fileSetDir.size()>0) - { - sourcePath.append(fileSetDir); - sourcePath.append("/"); - } - sourcePath.append(fileName); - String fullSource = parent.resolve(sourcePath); - - String destPath; - if (toDirName.size()>0) - { - destPath.append(toDirName); - destPath.append("/"); - } - if (owndir) - { - String subdir = fileName; - std::size_t pos = subdir.find_last_of('.'); - if (pos != subdir.npos) - subdir = subdir.substr(0, pos); - destPath.append(subdir); - destPath.append("/"); - } - //Pick the output file name - if (outName.size() > 0) - { - destPath.append(outName); - } - else - { - destPath.append(fileName); - destPath[destPath.size()-2] = 'm'; - } - - String fullDest = parent.resolve(destPath); - - if (!isNewerThan(fullSource, fullDest)) - { - //trace("skip %s", fullSource.c_str()); - continue; - } - - String cmd = command; - cmd.append(" "); - cmd.append(fullSource); - cmd.append(" -o "); - cmd.append(fullDest); - - int pos = fullDest.find_last_of('/'); - if (pos>0) - { - String fullDestPath = fullDest.substr(0, pos); - if (!createDirectory(fullDestPath)) - return false; - } - - - - String outString, errString; - if (!executeCommand(cmd.c_str(), "", outString, errString)) - { - error("<msgfmt> problem: %s", errString.c_str()); - return false; - } - removeFromStatCache(getNativePath(fullDest)); - } - - return true; - } - - virtual bool parse(Element *elem) - { - if (!parent.getAttribute(elem, "command", commandOpt)) - return false; - if (!parent.getAttribute(elem, "todir", toDirNameOpt)) - return false; - if (!parent.getAttribute(elem, "out", outNameOpt)) - return false; - if (!parent.getAttribute(elem, "owndir", owndirOpt)) - return false; - - std::vector<Element *> children = elem->getChildren(); - for (std::size_t i=0 ; i<children.size() ; i++) - { - Element *child = children[i]; - String tagName = child->getName(); - if (tagName == "fileset") - { - if (!parseFileSet(child, parent, fileSet)) - return false; - } - } - return true; - } - -private: - - FileSet fileSet; - - String commandOpt; - String toDirNameOpt; - String outNameOpt; - String owndirOpt; - -}; - - - -/** - * Perform a Package-Config query similar to pkg-config - */ -class TaskPkgConfig : public Task -{ -public: - - typedef enum - { - PKG_CONFIG_QUERY_CFLAGS, - PKG_CONFIG_QUERY_LIBS, - PKG_CONFIG_QUERY_ALL - } QueryTypes; - - TaskPkgConfig(MakeBase &par) : Task(par) - { - type = TASK_PKG_CONFIG; - name = "pkg-config"; - } - - virtual ~TaskPkgConfig() - {} - - virtual bool execute() - { - String pkgName = parent.eval(pkgNameOpt, ""); - String prefix = parent.eval(prefixOpt, ""); - String propName = parent.eval(propNameOpt, ""); - String pkgConfigPath = parent.eval(pkgConfigPathOpt,""); - String query = parent.eval(queryOpt, "all"); - - String path = parent.resolve(pkgConfigPath); - PkgConfig pkgconfig; - pkgconfig.setPath(path); - pkgconfig.setPrefix(prefix); - if (!pkgconfig.query(pkgName)) - { - error("<pkg-config> query failed for '%s", name.c_str()); - return false; - } - - String val = ""; - if (query == "cflags") - val = pkgconfig.getCflags(); - else if (query == "libs") - val =pkgconfig.getLibs(); - else if (query == "all") - val = pkgconfig.getAll(); - else - { - error("<pkg-config> unhandled query : %s", query.c_str()); - return false; - } - taskstatus("property %s = '%s'", propName.c_str(), val.c_str()); - parent.setProperty(propName, val); - return true; - } - - virtual bool parse(Element *elem) - { - //# NAME - if (!parent.getAttribute(elem, "name", pkgNameOpt)) - return false; - if (pkgNameOpt.size()==0) - { - error("<pkg-config> requires 'name=\"package\"' attribute"); - return false; - } - - //# PROPERTY - if (!parent.getAttribute(elem, "property", propNameOpt)) - return false; - if (propNameOpt.size()==0) - { - error("<pkg-config> requires 'property=\"name\"' attribute"); - return false; - } - //# PATH - if (!parent.getAttribute(elem, "path", pkgConfigPathOpt)) - return false; - //# PREFIX - if (!parent.getAttribute(elem, "prefix", prefixOpt)) - return false; - //# QUERY - if (!parent.getAttribute(elem, "query", queryOpt)) - return false; - - return true; - } - -private: - - String queryOpt; - String pkgNameOpt; - String prefixOpt; - String propNameOpt; - String pkgConfigPathOpt; - -}; - - - - - - -/** - * Process an archive to allow random access - */ -class TaskRanlib : public Task -{ -public: - - TaskRanlib(MakeBase &par) : Task(par) - { type = TASK_RANLIB; name = "ranlib"; } - - virtual ~TaskRanlib() - {} - - virtual bool execute() - { - String fileName = parent.eval(fileNameOpt, ""); - String command = parent.eval(commandOpt, "ranlib"); - - String fullName = parent.resolve(fileName); - //trace("fullDir:%s", fullDir.c_str()); - String cmd = command; - cmd.append(" "); - cmd.append(fullName); - String outbuf, errbuf; - if (!executeCommand(cmd, "", outbuf, errbuf)) - return false; - // TODO: - //removeFromStatCache(getNativePath(fullDest)); - return true; - } - - virtual bool parse(Element *elem) - { - if (!parent.getAttribute(elem, "command", commandOpt)) - return false; - if (!parent.getAttribute(elem, "file", fileNameOpt)) - return false; - if (fileNameOpt.size() == 0) - { - error("<ranlib> requires 'file=\"fileNname\"' attribute"); - return false; - } - return true; - } - -private: - - String fileNameOpt; - String commandOpt; -}; - - - -/** - * Compile a resource file into a binary object - */ -class TaskRC : public Task -{ -public: - - TaskRC(MakeBase &par) : Task(par) - { type = TASK_RC; name = "rc"; } - - virtual ~TaskRC() - {} - - virtual bool execute() - { - String command = parent.eval(commandOpt, "windres"); - String flags = parent.eval(flagsOpt, ""); - String fileName = parent.eval(fileNameOpt, ""); - String outName = parent.eval(outNameOpt, ""); - - String fullFile = parent.resolve(fileName); - String fullOut = parent.resolve(outName); - if (!isNewerThan(fullFile, fullOut)) - return true; - String cmd = command; - cmd.append(" -o "); - cmd.append(fullOut); - cmd.append(" "); - cmd.append(flags); - cmd.append(" "); - cmd.append(fullFile); - - String outString, errString; - if (!executeCommand(cmd.c_str(), "", outString, errString)) - { - error("RC problem: %s", errString.c_str()); - return false; - } - removeFromStatCache(getNativePath(fullOut)); - return true; - } - - virtual bool parse(Element *elem) - { - if (!parent.getAttribute(elem, "command", commandOpt)) - return false; - if (!parent.getAttribute(elem, "file", fileNameOpt)) - return false; - if (!parent.getAttribute(elem, "out", outNameOpt)) - return false; - std::vector<Element *> children = elem->getChildren(); - for (std::size_t i=0 ; i<children.size() ; i++) - { - Element *child = children[i]; - String tagName = child->getName(); - if (tagName == "flags") - { - if (!parent.getValue(child, flagsOpt)) - return false; - } - } - return true; - } - -private: - - String commandOpt; - String flagsOpt; - String fileNameOpt; - String outNameOpt; - -}; - - - -/** - * Collect .o's into a .so or DLL - */ -class TaskSharedLib : public Task -{ -public: - - TaskSharedLib(MakeBase &par) : Task(par) - { type = TASK_SHAREDLIB; name = "dll"; } - - virtual ~TaskSharedLib() - {} - - virtual bool execute() - { - String command = parent.eval(commandOpt, "dllwrap"); - String fileName = parent.eval(fileNameOpt, ""); - String defFileName = parent.eval(defFileNameOpt, ""); - String impFileName = parent.eval(impFileNameOpt, ""); - String libs = parent.eval(libsOpt, ""); - - //trace("###########HERE %d", fileSet.size()); - bool doit = false; - - String fullOut = parent.resolve(fileName); - //trace("ar fullout: %s", fullOut.c_str()); - - if (!listFiles(parent, fileSet)) - return false; - String fileSetDir = parent.eval(fileSet.getDirectory(), "."); - - for (std::size_t i=0 ; i<fileSet.size() ; i++) - { - String fname; - if (fileSetDir.size()>0) - { - fname.append(fileSetDir); - fname.append("/"); - } - fname.append(fileSet[i]); - String fullName = parent.resolve(fname); - //trace("ar : %s/%s", fullOut.c_str(), fullName.c_str()); - if (isNewerThan(fullName, fullOut)) - doit = true; - } - //trace("Needs it:%d", doit); - if (!doit) - { - return true; - } - - String cmd = "dllwrap"; - cmd.append(" -o "); - cmd.append(fullOut); - if (defFileName.size()>0) - { - cmd.append(" --def "); - cmd.append(defFileName); - cmd.append(" "); - } - if (impFileName.size()>0) - { - cmd.append(" --implib "); - cmd.append(impFileName); - cmd.append(" "); - } - for (std::size_t i=0 ; i<fileSet.size() ; i++) - { - String fname; - if (fileSetDir.size()>0) - { - fname.append(fileSetDir); - fname.append("/"); - } - fname.append(fileSet[i]); - String fullName = parent.resolve(fname); - - cmd.append(" "); - cmd.append(fullName); - } - cmd.append(" "); - cmd.append(libs); - - String outString, errString; - if (!executeCommand(cmd.c_str(), "", outString, errString)) - { - error("<sharedlib> problem: %s", errString.c_str()); - return false; - } - removeFromStatCache(getNativePath(fullOut)); - return true; - } - - virtual bool parse(Element *elem) - { - if (!parent.getAttribute(elem, "command", commandOpt)) - return false; - if (!parent.getAttribute(elem, "file", fileNameOpt)) - return false; - if (!parent.getAttribute(elem, "import", impFileNameOpt)) - return false; - if (!parent.getAttribute(elem, "def", defFileNameOpt)) - return false; - - std::vector<Element *> children = elem->getChildren(); - for (std::size_t i=0 ; i<children.size() ; i++) - { - Element *child = children[i]; - String tagName = child->getName(); - if (tagName == "fileset") - { - if (!parseFileSet(child, parent, fileSet)) - return false; - } - else if (tagName == "libs") - { - if (!parent.getValue(child, libsOpt)) - return false; - libsOpt = strip(libsOpt); - } - } - return true; - } - -private: - - FileSet fileSet; - - String commandOpt; - String fileNameOpt; - String defFileNameOpt; - String impFileNameOpt; - String libsOpt; - -}; - - - -/** - * Run the "ar" command to archive .o's into a .a - */ -class TaskStaticLib : public Task -{ -public: - - TaskStaticLib(MakeBase &par) : Task(par) - { type = TASK_STATICLIB; name = "staticlib"; } - - virtual ~TaskStaticLib() - {} - - virtual bool execute() - { - String command = parent.eval(commandOpt, "ar crv"); - String fileName = parent.eval(fileNameOpt, ""); - - bool doit = false; - - String fullOut = parent.resolve(fileName); - //trace("ar fullout: %s", fullOut.c_str()); - - if (!listFiles(parent, fileSet)) - return false; - String fileSetDir = parent.eval(fileSet.getDirectory(), "."); - //trace("###########HERE %s", fileSetDir.c_str()); - - for (std::size_t i=0 ; i<fileSet.size() ; i++) - { - String fname; - if (fileSetDir.size()>0) - { - fname.append(fileSetDir); - fname.append("/"); - } - fname.append(fileSet[i]); - String fullName = parent.resolve(fname); - //trace("ar : %s/%s", fullOut.c_str(), fullName.c_str()); - if (isNewerThan(fullName, fullOut)) - doit = true; - } - //trace("Needs it:%d", doit); - if (!doit) - { - return true; - } - - String cmd = command; - cmd.append(" "); - cmd.append(fullOut); - for (std::size_t i=0 ; i<fileSet.size() ; i++) - { - String fname; - if (fileSetDir.size()>0) - { - fname.append(fileSetDir); - fname.append("/"); - } - fname.append(fileSet[i]); - String fullName = parent.resolve(fname); - - cmd.append(" "); - cmd.append(fullName); - } - - String outString, errString; - if (!executeCommand(cmd.c_str(), "", outString, errString)) - { - error("<staticlib> problem: %s", errString.c_str()); - return false; - } - removeFromStatCache(getNativePath(fullOut)); - return true; - } - - - virtual bool parse(Element *elem) - { - if (!parent.getAttribute(elem, "command", commandOpt)) - return false; - if (!parent.getAttribute(elem, "file", fileNameOpt)) - return false; - - std::vector<Element *> children = elem->getChildren(); - for (std::size_t i=0 ; i<children.size() ; i++) - { - Element *child = children[i]; - String tagName = child->getName(); - if (tagName == "fileset") - { - if (!parseFileSet(child, parent, fileSet)) - return false; - } - } - return true; - } - -private: - - FileSet fileSet; - - String commandOpt; - String fileNameOpt; - -}; - - - - -/** - * Strip an executable - */ -class TaskStrip : public Task -{ -public: - - TaskStrip(MakeBase &par) : Task(par) - { type = TASK_STRIP; name = "strip"; } - - virtual ~TaskStrip() - {} - - virtual bool execute() - { - String command = parent.eval(commandOpt, "strip"); - String fileName = parent.eval(fileNameOpt, ""); - String symFileName = parent.eval(symFileNameOpt, ""); - - String fullName = parent.resolve(fileName); - //trace("fullDir:%s", fullDir.c_str()); - String cmd; - String outbuf, errbuf; - - if (symFileName.size()>0) - { - String symFullName = parent.resolve(symFileName); - cmd = "objcopy --only-keep-debug "; - cmd.append(getNativePath(fullName)); - cmd.append(" "); - cmd.append(getNativePath(symFullName)); - if (!executeCommand(cmd, "", outbuf, errbuf)) - { - error("<strip> symbol file failed : %s", errbuf.c_str()); - return false; - } - } - - cmd = command; - cmd.append(getNativePath(fullName)); - if (!executeCommand(cmd, "", outbuf, errbuf)) - { - error("<strip> failed : %s", errbuf.c_str()); - return false; - } - removeFromStatCache(getNativePath(fullName)); - return true; - } - - virtual bool parse(Element *elem) - { - if (!parent.getAttribute(elem, "command", commandOpt)) - return false; - if (!parent.getAttribute(elem, "file", fileNameOpt)) - return false; - if (!parent.getAttribute(elem, "symfile", symFileNameOpt)) - return false; - if (fileNameOpt.size() == 0) - { - error("<strip> requires 'file=\"fileName\"' attribute"); - return false; - } - return true; - } - -private: - - String commandOpt; - String fileNameOpt; - String symFileNameOpt; -}; - - -/** - * - */ -class TaskTouch : public Task -{ -public: - - TaskTouch(MakeBase &par) : Task(par) - { type = TASK_TOUCH; name = "touch"; } - - virtual ~TaskTouch() - {} - - virtual bool execute() - { - String fileName = parent.eval(fileNameOpt, ""); - - String fullName = parent.resolve(fileName); - String nativeFile = getNativePath(fullName); - if (!isRegularFile(fullName) && !isDirectory(fullName)) - { - // S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH - int ret = creat(nativeFile.c_str(), 0666); - if (ret != 0) - { - error("<touch> could not create '%s' : %s", - nativeFile.c_str(), strerror(ret)); - return false; - } - return true; - } - int ret = utime(nativeFile.c_str(), (struct utimbuf *)0); - if (ret != 0) - { - error("<touch> could not update the modification time for '%s' : %s", - nativeFile.c_str(), strerror(ret)); - return false; - } - removeFromStatCache(nativeFile); - return true; - } - - virtual bool parse(Element *elem) - { - //trace("touch parse"); - if (!parent.getAttribute(elem, "file", fileNameOpt)) - return false; - if (fileNameOpt.size() == 0) - { - error("<touch> requires 'file=\"fileName\"' attribute"); - return false; - } - return true; - } - - String fileNameOpt; -}; - - -/** - * - */ -class TaskTstamp : public Task -{ -public: - - TaskTstamp(MakeBase &par) : Task(par) - { type = TASK_TSTAMP; name = "tstamp"; } - - virtual ~TaskTstamp() - {} - - virtual bool execute() - { - return true; - } - - virtual bool parse(Element *elem) - { - //trace("tstamp parse"); - return true; - } -}; - - - -/** - * - */ -Task *Task::createTask(Element *elem, int lineNr) -{ - String tagName = elem->getName(); - //trace("task:%s", tagName.c_str()); - Task *task = NULL; - if (tagName == "cc") - task = new TaskCC(parent); - else if (tagName == "copy") - task = new TaskCopy(parent); - else if (tagName == "cxxtestpart") - task = new TaskCxxTestPart(parent); - else if (tagName == "cxxtestroot") - task = new TaskCxxTestRoot(parent); - else if (tagName == "cxxtestrun") - task = new TaskCxxTestRun(parent); - else if (tagName == "delete") - task = new TaskDelete(parent); - else if (tagName == "echo") - task = new TaskEcho(parent); - else if (tagName == "jar") - task = new TaskJar(parent); - else if (tagName == "javac") - task = new TaskJavac(parent); - else if (tagName == "link") - task = new TaskLink(parent); - else if (tagName == "makefile") - task = new TaskMakeFile(parent); - else if (tagName == "mkdir") - task = new TaskMkDir(parent); - else if (tagName == "msgfmt") - task = new TaskMsgFmt(parent); - else if (tagName == "pkg-config") - task = new TaskPkgConfig(parent); - else if (tagName == "ranlib") - task = new TaskRanlib(parent); - else if (tagName == "rc") - task = new TaskRC(parent); - else if (tagName == "sharedlib") - task = new TaskSharedLib(parent); - else if (tagName == "staticlib") - task = new TaskStaticLib(parent); - else if (tagName == "strip") - task = new TaskStrip(parent); - else if (tagName == "touch") - task = new TaskTouch(parent); - else if (tagName == "tstamp") - task = new TaskTstamp(parent); - else - { - error("Unknown task '%s'", tagName.c_str()); - return NULL; - } - - task->setLine(lineNr); - - if (!task->parse(elem)) - { - delete task; - return NULL; - } - return task; -} - - - -//######################################################################## -//# T A R G E T -//######################################################################## - -/** - * - */ -class Target : public MakeBase -{ - -public: - - /** - * - */ - Target(Make &par) : parent(par) - { init(); } - - /** - * - */ - Target(const Target &other) : parent(other.parent) - { init(); assign(other); } - - /** - * - */ - Target &operator=(const Target &other) - { init(); assign(other); return *this; } - - /** - * - */ - virtual ~Target() - { cleanup() ; } - - - /** - * - */ - virtual Make &getParent() - { return parent; } - - /** - * - */ - virtual String getName() - { return name; } - - /** - * - */ - virtual void setName(const String &val) - { name = val; } - - /** - * - */ - virtual String getDescription() - { return description; } - - /** - * - */ - virtual void setDescription(const String &val) - { description = val; } - - /** - * - */ - virtual void addDependency(const String &val) - { deps.push_back(val); } - - /** - * - */ - virtual void parseDependencies(const String &val) - { deps = tokenize(val, ", "); } - - /** - * - */ - virtual std::vector<String> &getDependencies() - { return deps; } - - /** - * - */ - virtual String getIf() - { return ifVar; } - - /** - * - */ - virtual void setIf(const String &val) - { ifVar = val; } - - /** - * - */ - virtual String getUnless() - { return unlessVar; } - - /** - * - */ - virtual void setUnless(const String &val) - { unlessVar = val; } - - /** - * - */ - virtual void addTask(Task *val) - { tasks.push_back(val); } - - /** - * - */ - virtual std::vector<Task *> &getTasks() - { return tasks; } - -private: - - void init() - { - } - - void cleanup() - { - tasks.clear(); - } - - void assign(const Target &other) - { - //parent = other.parent; - name = other.name; - description = other.description; - ifVar = other.ifVar; - unlessVar = other.unlessVar; - deps = other.deps; - tasks = other.tasks; - } - - Make &parent; - - String name; - - String description; - - String ifVar; - - String unlessVar; - - std::vector<String> deps; - - std::vector<Task *> tasks; - -}; - - - - - - - - -//######################################################################## -//# M A K E -//######################################################################## - - -/** - * - */ -class Make : public MakeBase -{ - -public: - - /** - * - */ - Make() - { init(); } - - /** - * - */ - Make(const Make &other) - { assign(other); } - - /** - * - */ - Make &operator=(const Make &other) - { assign(other); return *this; } - - /** - * - */ - virtual ~Make() - { cleanup(); } - - /** - * - */ - virtual std::map<String, Target> &getTargets() - { return targets; } - - - /** - * - */ - virtual String version() - { return BUILDTOOL_VERSION; } - - /** - * Overload a <property> - */ - virtual bool specifyProperty(const String &name, - const String &value); - - /** - * - */ - virtual bool run(); - - /** - * - */ - virtual bool run(const String &target); - - - -private: - - /** - * - */ - void init(); - - /** - * - */ - void cleanup(); - - /** - * - */ - void assign(const Make &other); - - /** - * - */ - bool executeTask(Task &task); - - - /** - * - */ - bool executeTarget(Target &target, - std::set<String> &targetsCompleted); - - - /** - * - */ - bool execute(); - - /** - * - */ - bool checkTargetDependencies(Target &prop, - std::vector<String> &depList); - - /** - * - */ - bool parsePropertyFile(const String &fileName, - const String &prefix); - - /** - * - */ - bool parseProperty(Element *elem); - - /** - * - */ - bool parseFile(); - - /** - * - */ - std::vector<String> glob(const String &pattern); - - - //############### - //# Fields - //############### - - String projectName; - - String currentTarget; - - String defaultTarget; - - String specifiedTarget; - - String baseDir; - - String description; - - //std::vector<Property> properties; - - std::map<String, Target> targets; - - std::vector<Task *> allTasks; - - std::map<String, String> specifiedProperties; - -}; - - -//######################################################################## -//# C L A S S M A I N T E N A N C E -//######################################################################## - -/** - * - */ -void Make::init() -{ - uri = "build.xml"; - projectName = ""; - currentTarget = ""; - defaultTarget = ""; - specifiedTarget = ""; - baseDir = ""; - description = ""; - envPrefix = "env."; - pcPrefix = "pc."; - pccPrefix = "pcc."; - pclPrefix = "pcl."; - bzrPrefix = "bzr."; - properties.clear(); - for (std::size_t i = 0 ; i < allTasks.size() ; i++) - delete allTasks[i]; - allTasks.clear(); -} - - - -/** - * - */ -void Make::cleanup() -{ - for (std::size_t i = 0 ; i < allTasks.size() ; i++) - delete allTasks[i]; - allTasks.clear(); -} - - - -/** - * - */ -void Make::assign(const Make &other) -{ - uri = other.uri; - projectName = other.projectName; - currentTarget = other.currentTarget; - defaultTarget = other.defaultTarget; - specifiedTarget = other.specifiedTarget; - baseDir = other.baseDir; - description = other.description; - properties = other.properties; -} - - - -//######################################################################## -//# U T I L I T Y T A S K S -//######################################################################## - -/** - * Perform a file globbing - */ -std::vector<String> Make::glob(const String &pattern) -{ - std::vector<String> res; - return res; -} - - -//######################################################################## -//# P U B L I C A P I -//######################################################################## - - - -/** - * - */ -bool Make::executeTarget(Target &target, - std::set<String> &targetsCompleted) -{ - - String name = target.getName(); - - //First get any dependencies for this target - std::vector<String> deps = target.getDependencies(); - for (std::size_t i=0 ; i<deps.size() ; i++) - { - String dep = deps[i]; - //Did we do it already? Skip - if (targetsCompleted.find(dep)!=targetsCompleted.end()) - continue; - - std::map<String, Target> &tgts = - target.getParent().getTargets(); - std::map<String, Target>::iterator iter = - tgts.find(dep); - if (iter == tgts.end()) - { - error("Target '%s' dependency '%s' not found", - name.c_str(), dep.c_str()); - return false; - } - Target depTarget = iter->second; - if (!executeTarget(depTarget, targetsCompleted)) - { - return false; - } - } - - status("##### Target : %s\n##### %s", name.c_str(), - target.getDescription().c_str()); - - //Now let's do the tasks - std::vector<Task *> &tasks = target.getTasks(); - for (std::size_t i=0 ; i<tasks.size() ; i++) - { - Task *task = tasks[i]; - status("--- %s / %s", name.c_str(), task->getName().c_str()); - if (!task->execute()) - { - return false; - } - } - - targetsCompleted.insert(name); - - return true; -} - - - -/** - * Main execute() method. Start here and work - * up the dependency tree - */ -bool Make::execute() -{ - status("######## EXECUTE"); - - //Determine initial target - if (specifiedTarget.size()>0) - { - currentTarget = specifiedTarget; - } - else if (defaultTarget.size()>0) - { - currentTarget = defaultTarget; - } - else - { - error("execute: no specified or default target requested"); - return false; - } - - std::map<String, Target>::iterator iter = - targets.find(currentTarget); - if (iter == targets.end()) - { - error("Initial target '%s' not found", - currentTarget.c_str()); - return false; - } - - //Now run - Target target = iter->second; - std::set<String> targetsCompleted; - if (!executeTarget(target, targetsCompleted)) - { - return false; - } - - status("######## EXECUTE COMPLETE"); - return true; -} - - - - -/** - * - */ -bool Make::checkTargetDependencies(Target &target, - std::vector<String> &depList) -{ - String tgtName = target.getName().c_str(); - depList.push_back(tgtName); - - std::vector<String> deps = target.getDependencies(); - for (std::size_t i=0 ; i<deps.size() ; i++) - { - String dep = deps[i]; - //First thing entered was the starting Target - if (dep == depList[0]) - { - error("Circular dependency '%s' found at '%s'", - dep.c_str(), tgtName.c_str()); - std::vector<String>::iterator diter; - for (diter=depList.begin() ; diter!=depList.end() ; diter++) - { - error(" %s", diter->c_str()); - } - return false; - } - - std::map<String, Target> &tgts = - target.getParent().getTargets(); - std::map<String, Target>::iterator titer = tgts.find(dep); - if (titer == tgts.end()) - { - error("Target '%s' dependency '%s' not found", - tgtName.c_str(), dep.c_str()); - return false; - } - if (!checkTargetDependencies(titer->second, depList)) - { - return false; - } - } - return true; -} - - - - - -static int getword(int pos, const String &inbuf, String &result) -{ - int p = pos; - int len = (int)inbuf.size(); - String val; - while (p < len) - { - char ch = inbuf[p]; - if (!isalnum(ch) && ch!='.' && ch!='_') - break; - val.push_back(ch); - p++; - } - result = val; - return p; -} - - - - -/** - * - */ -bool Make::parsePropertyFile(const String &fileName, - const String &prefix) -{ - FILE *f = fopen(fileName.c_str(), "r"); - if (!f) - { - error("could not open property file %s", fileName.c_str()); - return false; - } - int linenr = 0; - while (!feof(f)) - { - char buf[256]; - if (!fgets(buf, 255, f)) - break; - linenr++; - String s = buf; - s = trim(s); - int len = s.size(); - if (len == 0) - continue; - if (s[0] == '#') - continue; - String key; - String val; - int p = 0; - int p2 = getword(p, s, key); - if (p2 <= p) - { - error("property file %s, line %d: expected keyword", - fileName.c_str(), linenr); - fclose(f); - return false; - } - if (prefix.size() > 0) - { - key.insert(0, prefix); - } - - //skip whitespace - for (p=p2 ; p<len ; p++) - if (!isspace(s[p])) - break; - - if (p>=len || s[p]!='=') - { - error("property file %s, line %d: expected '='", - fileName.c_str(), linenr); - return false; - } - p++; - - //skip whitespace - for ( ; p<len ; p++) - if (!isspace(s[p])) - break; - - /* This way expects a word after the = - p2 = getword(p, s, val); - if (p2 <= p) - { - error("property file %s, line %d: expected value", - fileName.c_str(), linenr); - return false; - } - */ - // This way gets the rest of the line after the = - if (p>=len) - { - error("property file %s, line %d: expected value", - fileName.c_str(), linenr); - return false; - } - val = s.substr(p); - if (key.size()==0) - continue; - //allow property to be set, even if val="" - - //trace("key:'%s' val:'%s'", key.c_str(), val.c_str()); - //See if we wanted to overload this property - std::map<String, String>::iterator iter = - specifiedProperties.find(key); - if (iter!=specifiedProperties.end()) - { - val = iter->second; - status("overloading property '%s' = '%s'", - key.c_str(), val.c_str()); - } - properties[key] = val; - } - fclose(f); - return true; -} - - - - -/** - * - */ -bool Make::parseProperty(Element *elem) -{ - std::vector<Attribute> &attrs = elem->getAttributes(); - for (std::size_t i=0 ; i<attrs.size() ; i++) - { - String attrName = attrs[i].getName(); - String attrVal = attrs[i].getValue(); - - if (attrName == "name") - { - String val; - if (!getAttribute(elem, "value", val)) - return false; - if (val.size() > 0) - { - properties[attrVal] = val; - } - else - { - if (!getAttribute(elem, "location", val)) - return false; - //let the property exist, even if not defined - properties[attrVal] = val; - } - //See if we wanted to overload this property - std::map<String, String>::iterator iter = - specifiedProperties.find(attrVal); - if (iter != specifiedProperties.end()) - { - val = iter->second; - status("overloading property '%s' = '%s'", - attrVal.c_str(), val.c_str()); - properties[attrVal] = val; - } - } - else if (attrName == "file") - { - String prefix; - if (!getAttribute(elem, "prefix", prefix)) - return false; - if (prefix.size() > 0) - { - if (prefix[prefix.size()-1] != '.') - prefix.push_back('.'); - } - if (!parsePropertyFile(attrName, prefix)) - return false; - } - else if (attrName == "environment") - { - if (attrVal.find('.') != attrVal.npos) - { - error("environment prefix cannot have a '.' in it"); - return false; - } - envPrefix = attrVal; - envPrefix.push_back('.'); - } - else if (attrName == "pkg-config") - { - if (attrVal.find('.') != attrVal.npos) - { - error("pkg-config prefix cannot have a '.' in it"); - return false; - } - pcPrefix = attrVal; - pcPrefix.push_back('.'); - } - else if (attrName == "pkg-config-cflags") - { - if (attrVal.find('.') != attrVal.npos) - { - error("pkg-config-cflags prefix cannot have a '.' in it"); - return false; - } - pccPrefix = attrVal; - pccPrefix.push_back('.'); - } - else if (attrName == "pkg-config-libs") - { - if (attrVal.find('.') != attrVal.npos) - { - error("pkg-config-libs prefix cannot have a '.' in it"); - return false; - } - pclPrefix = attrVal; - pclPrefix.push_back('.'); - } - else if (attrName == "subversion") - { - if (attrVal.find('.') != attrVal.npos) - { - error("bzr prefix cannot have a '.' in it"); - return false; - } - bzrPrefix = attrVal; - bzrPrefix.push_back('.'); - } - } - - return true; -} - - - - -/** - * - */ -bool Make::parseFile() -{ - status("######## PARSE : %s", uri.getPath().c_str()); - - setLine(0); - - Parser parser; - Element *root = parser.parseFile(uri.getNativePath()); - if (!root) - { - error("Could not open %s for reading", - uri.getNativePath().c_str()); - return false; - } - - setLine(root->getLine()); - - if (root->getChildren().size()==0 || - root->getChildren()[0]->getName()!="project") - { - error("Main xml element should be <project>"); - delete root; - return false; - } - - //########## Project attributes - Element *project = root->getChildren()[0]; - String s = project->getAttribute("name"); - if (s.size() > 0) - projectName = s; - s = project->getAttribute("default"); - if (s.size() > 0) - defaultTarget = s; - s = project->getAttribute("basedir"); - if (s.size() > 0) - baseDir = s; - - //######### PARSE MEMBERS - std::vector<Element *> children = project->getChildren(); - for (std::size_t i=0 ; i<children.size() ; i++) - { - Element *elem = children[i]; - setLine(elem->getLine()); - String tagName = elem->getName(); - - //########## DESCRIPTION - if (tagName == "description") - { - description = parser.trim(elem->getValue()); - } - - //######### PROPERTY - else if (tagName == "property") - { - if (!parseProperty(elem)) - return false; - } - - //######### TARGET - else if (tagName == "target") - { - String tname = elem->getAttribute("name"); - String tdesc = elem->getAttribute("description"); - String tdeps = elem->getAttribute("depends"); - String tif = elem->getAttribute("if"); - String tunless = elem->getAttribute("unless"); - Target target(*this); - target.setName(tname); - target.setDescription(tdesc); - target.parseDependencies(tdeps); - target.setIf(tif); - target.setUnless(tunless); - std::vector<Element *> telems = elem->getChildren(); - for (std::size_t i=0 ; i<telems.size() ; i++) - { - Element *telem = telems[i]; - Task breeder(*this); - Task *task = breeder.createTask(telem, telem->getLine()); - if (!task) - return false; - allTasks.push_back(task); - target.addTask(task); - } - - //Check name - if (tname.size() == 0) - { - error("no name for target"); - return false; - } - //Check for duplicate name - if (targets.find(tname) != targets.end()) - { - error("target '%s' already defined", tname.c_str()); - return false; - } - //more work than targets[tname]=target, but avoids default allocator - targets.insert(std::make_pair<String, Target>(tname, target)); - } - //######### none of the above - else - { - error("unknown toplevel tag: <%s>", tagName.c_str()); - return false; - } - - } - - std::map<String, Target>::iterator iter; - for (iter = targets.begin() ; iter!= targets.end() ; iter++) - { - Target tgt = iter->second; - std::vector<String> depList; - if (!checkTargetDependencies(tgt, depList)) - { - return false; - } - } - - - delete root; - status("######## PARSE COMPLETE"); - return true; -} - - -/** - * Overload a <property> - */ -bool Make::specifyProperty(const String &name, const String &value) -{ - if (specifiedProperties.find(name) != specifiedProperties.end()) - { - error("Property %s already specified", name.c_str()); - return false; - } - specifiedProperties[name] = value; - return true; -} - - - -/** - * - */ -bool Make::run() -{ - if (!parseFile()) - return false; - - if (!execute()) - return false; - - return true; -} - - - - -/** - * Get a formatted MM:SS.sss time elapsed string - */ -static String -timeDiffString(struct timeval &x, struct timeval &y) -{ - long microsX = x.tv_usec; - long secondsX = x.tv_sec; - long microsY = y.tv_usec; - long secondsY = y.tv_sec; - if (microsX < microsY) - { - microsX += 1000000; - secondsX -= 1; - } - - int seconds = (int)(secondsX - secondsY); - int millis = (int)((microsX - microsY)/1000); - - int minutes = seconds/60; - seconds -= minutes*60; - char buf[80]; - snprintf(buf, 79, "%dm %d.%03ds", minutes, seconds, millis); - String ret = buf; - return ret; - -} - -/** - * - */ -bool Make::run(const String &target) -{ - status("####################################################"); - status("# %s", version().c_str()); - status("####################################################"); - struct timeval timeStart, timeEnd; - ::gettimeofday(&timeStart, NULL); - specifiedTarget = target; - if (!run()) - return false; - ::gettimeofday(&timeEnd, NULL); - String timeStr = timeDiffString(timeEnd, timeStart); - status("####################################################"); - status("# BuildTool Completed : %s", timeStr.c_str()); - status("####################################################"); - return true; -} - - - - - - - -}// namespace buildtool -//######################################################################## -//# M A I N -//######################################################################## - -typedef buildtool::String String; - -/** - * Format an error message in printf() style - */ -static void error(const char *fmt, ...) -{ - va_list ap; - va_start(ap, fmt); - fprintf(stderr, "BuildTool error: "); - vfprintf(stderr, fmt, ap); - fprintf(stderr, "\n"); - va_end(ap); -} - - -static bool parseProperty(const String &s, String &name, String &val) -{ - int len = s.size(); - int i; - for (i=0 ; i<len ; i++) - { - char ch = s[i]; - if (ch == '=') - break; - name.push_back(ch); - } - if (i>=len || s[i]!='=') - { - error("property requires -Dname=value"); - return false; - } - i++; - for ( ; i<len ; i++) - { - char ch = s[i]; - val.push_back(ch); - } - return true; -} - - -/** - * Compare a buffer with a key, for the length of the key - */ -static bool sequ(const String &buf, const char *key) -{ - int len = buf.size(); - for (int i=0 ; key[i] && i<len ; i++) - { - if (key[i] != buf[i]) - return false; - } - return true; -} - -static void usage(int argc, char **argv) -{ - printf("usage:\n"); - printf(" %s [options] [target]\n", argv[0]); - printf("Options:\n"); - printf(" -help, -h print this message\n"); - printf(" -version print the version information and exit\n"); - printf(" -file <file> use given buildfile\n"); - printf(" -f <file> ''\n"); - printf(" -D<property>=<value> use value for given property\n"); - printf(" -j [N] build using N threads or infinite number of threads if no argument\n"); -} - - - - -/** - * Parse the command-line args, get our options, - * and run this thing - */ -static bool parseOptions(int argc, char **argv) -{ - if (argc < 1) - { - error("Cannot parse arguments"); - return false; - } - - buildtool::Make make; - - String target; - - //char *progName = argv[0]; - for (int i=1 ; i<argc ; i++) - { - String arg = argv[i]; - if (arg.size()>1 && arg[0]=='-') - { - if (arg == "-h" || arg == "-help") - { - usage(argc,argv); - return true; - } - else if (arg == "-version") - { - printf("%s", make.version().c_str()); - return true; - } - else if (arg == "-f" || arg == "-file") - { - if (i>=argc-1) - { - usage(argc, argv); - return false; - } - i++; //eat option - make.setURI(argv[i]); - } - else if (arg == "-j") - { - if (i>=argc-1) { // if -j is given as last argument - make.setNumThreads(20); // default to some high value - } else { - i++; //eat option - if (argv[i] && (*argv[i] == '-')) { // if -j is followed by another '-...' option - make.setNumThreads(20); // default to some high value - } else { - make.setNumThreads(atoi(argv[i])); - } - } - } - else if (arg.size()>2 && sequ(arg, "-D")) - { - String s = arg.substr(2, arg.size()); - String name, value; - if (!parseProperty(s, name, value)) - { - usage(argc, argv); - return false; - } - if (!make.specifyProperty(name, value)) - return false; - } - else - { - error("Unknown option:%s", arg.c_str()); - return false; - } - } - else - { - if (target.size()>0) - { - error("only one initial target"); - usage(argc, argv); - return false; - } - target = arg; - } - } - - //We have the options. Now execute them - if (!make.run(target)) - return false; - - return true; -} - - - - -/* -static bool runMake() -{ - buildtool::Make make; - if (!make.run()) - return false; - return true; -} - - -static bool pkgConfigTest() -{ - buildtool::PkgConfig pkgConfig; - if (!pkgConfig.readFile("gtk+-2.0.pc")) - return false; - return true; -} - - - -static bool depTest() -{ - buildtool::DepTool deptool; - deptool.setSourceDirectory("/dev/ink/inkscape/src"); - if (!deptool.generateDependencies("build.dep")) - return false; - std::vector<buildtool::FileRec> res = - deptool.loadDepFile("build.dep"); - if (res.size() == 0) - return false; - return true; -} - -static bool popenTest() -{ - buildtool::Make make; - buildtool::String out, err; - bool ret = make.executeCommand("gcc xx.cpp", "", out, err); - printf("Popen test:%d '%s' '%s'\n", ret, out.c_str(), err.c_str()); - return true; -} - - -static bool propFileTest() -{ - buildtool::Make make; - make.parsePropertyFile("test.prop", "test."); - return true; -} -*/ - -int main(int argc, char **argv) -{ - - if (!parseOptions(argc, argv)) - return 1; - /* - if (!popenTest()) - return 1; - - if (!depTest()) - return 1; - if (!propFileTest()) - return 1; - if (runMake()) - return 1; - */ - return 0; -} - - -//######################################################################## -//# E N D -//######################################################################## - - |
