aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--build2/b.cxx17
-rw-r--r--build2/cli/module.cxx4
-rw-r--r--build2/config/operation.cxx9
-rw-r--r--build2/context.cxx6
-rw-r--r--build2/context.txx6
-rw-r--r--build2/cxx/compile.cxx38
-rw-r--r--build2/cxx/link.cxx4
-rw-r--r--build2/depdb12
-rw-r--r--build2/name29
-rw-r--r--build2/name.cxx73
-rw-r--r--build2/parser.cxx27
-rwxr-xr-xtests/amalgam/unnamed/test.sh2
-rwxr-xr-xtests/escaping/test.sh2
-rwxr-xr-xtests/eval/test.sh2
-rwxr-xr-xtests/function/call/test.sh2
-rwxr-xr-xtests/if-else/test.sh2
-rwxr-xr-xtests/keyword/test.sh2
-rwxr-xr-xtests/names/test.sh2
-rwxr-xr-xtests/pairs/test.sh2
-rwxr-xr-xtests/quote/test.sh2
-rwxr-xr-xtests/scope/test.sh7
-rwxr-xr-xtests/test.sh30
-rwxr-xr-xtests/variable/expansion/test.sh2
-rwxr-xr-xtests/variable/null/test.sh2
-rwxr-xr-xtests/variable/override/test.sh30
-rwxr-xr-xtests/variable/prepend/test.sh2
-rwxr-xr-xtests/variable/qualified/test.sh2
-rwxr-xr-xtests/variable/type/test.sh2
28 files changed, 249 insertions, 71 deletions
diff --git a/build2/b.cxx b/build2/b.cxx
index 1377d5b..633d583 100644
--- a/build2/b.cxx
+++ b/build2/b.cxx
@@ -8,7 +8,6 @@
#include <stdlib.h> // getenv()
#include <unistd.h> // getuid()
#include <sys/types.h> // uid_t
-#include <pwd.h> // struct passwd, getpwuid()
#include <sstream>
#include <cstring> // strcmp(), strchr()
@@ -202,21 +201,7 @@ main (int argc, char* argv[])
// Figure out work and home directories.
//
work = dir_path::current ();
-
- if (const char* h = getenv ("HOME"))
- home = dir_path (h);
- else
- {
- struct passwd* pw (getpwuid (getuid ()));
-
- if (pw == nullptr)
- {
- const char* msg (strerror (errno));
- fail << "unable to determine home directory: " << msg;
- }
-
- home = dir_path (pw->pw_dir);
- }
+ home = dir_path::home ();
if (verb >= 5)
{
diff --git a/build2/cli/module.cxx b/build2/cli/module.cxx
index bc6e13f..44b36a1 100644
--- a/build2/cli/module.cxx
+++ b/build2/cli/module.cxx
@@ -132,7 +132,7 @@ namespace build2
{
// In some cases this is not enough (e.g., the runtime linker
// will print scary errors if some shared libraries are not
- // found. So it would be good to redirect child's STDERR.
+ // found). So it would be good to redirect child's STDERR.
//
if (!optional)
error << "unable to execute " << cli << ": " << e.what ();
@@ -140,7 +140,7 @@ namespace build2
if (e.child ())
exit (1);
- throw failed ();
+ return string (); // Not found.
}
};
diff --git a/build2/config/operation.cxx b/build2/config/operation.cxx
index fe36034..20425ea 100644
--- a/build2/config/operation.cxx
+++ b/build2/config/operation.cxx
@@ -56,7 +56,9 @@ namespace build2
ofs << "# Created automatically by the config module." << endl
<< "#" << endl
- << "src_root = " << src_root << endl;
+ << "src_root = ";
+ to_stream (ofs, name (src_root), true, '@'); // Quote.
+ ofs << endl;
}
catch (const ofstream::failure&)
{
@@ -251,7 +253,10 @@ namespace build2
if (v)
{
storage.clear ();
- ofs << n << " = " << reverse (v, storage) << endl;
+
+ ofs << n << " = ";
+ to_stream (ofs, reverse (v, storage), true, '@'); // Quote.
+ ofs << endl;
}
else
ofs << n << " = [null]" << endl;
diff --git a/build2/context.cxx b/build2/context.cxx
index 000d3b2..3e9fe4a 100644
--- a/build2/context.cxx
+++ b/build2/context.cxx
@@ -339,7 +339,8 @@ namespace build2
if (verb)
text << "mkdir " << d;
- fail << "unable to create directory " << d << ": " << e.what ();
+ error << "unable to create directory " << d << ": " << e.what ();
+ throw failed ();
}
if (ms == mkdir_status::success)
@@ -368,7 +369,8 @@ namespace build2
if (verb)
text << "mkdir -p " << d;
- fail << "unable to create directory " << d << ": " << e.what ();
+ error << "unable to create directory " << d << ": " << e.what ();
+ throw failed ();
}
if (ms == mkdir_status::success)
diff --git a/build2/context.txx b/build2/context.txx
index 3233092..1f3fce9 100644
--- a/build2/context.txx
+++ b/build2/context.txx
@@ -35,7 +35,8 @@ namespace build2
else if (verb >= l2)
text << "rm " << t;
- fail << "unable to remove file " << f << ": " << e.what ();
+ error << "unable to remove file " << f << ": " << e.what ();
+ throw failed ();
}
if (rs == rmfile_status::success)
@@ -74,7 +75,8 @@ namespace build2
else if (verb)
text << "rmdir " << t;
- fail << "unable to remove directory " << d << ": " << e.what ();
+ error << "unable to remove directory " << d << ": " << e.what ();
+ throw failed ();
}
switch (rs)
diff --git a/build2/cxx/compile.cxx b/build2/cxx/compile.cxx
index b11a919..e0fabb4 100644
--- a/build2/cxx/compile.cxx
+++ b/build2/cxx/compile.cxx
@@ -424,8 +424,28 @@ namespace build2
{
char c (l[p]);
- if (c == '\\')
- c = l[++p];
+ if (p + 1 != n)
+ {
+ if (c == '$')
+ {
+ // Got to be another (escaped) '$'.
+ //
+ if (l[p + 1] == '$')
+ ++p;
+ }
+ else if (c == '\\')
+ {
+ // This may or may not be an escape sequence depending on whether
+ // what follows is "escapable".
+ //
+ switch (c = l[++p])
+ {
+ case '\\': break;
+ case ' ': break;
+ default: c = '\\'; --p; // Restore.
+ }
+ }
+ }
r += c;
}
@@ -627,8 +647,14 @@ namespace build2
{
args.push_back ("-M"); // Note: -MM -MG skips missing <>-included.
args.push_back ("-MG"); // Treat missing headers as generated.
+
+ // Previously we used '*' as a target name but it gets expanded to
+ // the current directory file names by GCC (4.9) that comes with
+ // MSYS2 (2.4). Yes, this is the (bizarre) behavior of GCC being
+ // executed in the shell with -MQ '*' option and not just -MQ *.
+ //
args.push_back ("-MQ"); // Quoted target name.
- args.push_back ("*"); // Old versions can't do empty target name.
+ args.push_back ("^"); // Old versions can't do empty target name.
}
// We are using absolute source file path in order to get absolute
@@ -1051,19 +1077,19 @@ namespace build2
break;
}
- assert (l[0] == '*' && l[1] == ':' && l[2] == ' ');
+ assert (l[0] == '^' && l[1] == ':' && l[2] == ' ');
first = false;
second = true;
// While normally we would have the source file on the first
// line, if too long, it will be moved to the next line and
- // all we will have on this line is "*: \".
+ // all we will have on this line is "^: \".
//
if (l.size () == 4 && l[3] == '\\')
continue;
else
- pos = 3; // Skip "*: ".
+ pos = 3; // Skip "^: ".
// Fall through to the 'second' block.
}
diff --git a/build2/cxx/link.cxx b/build2/cxx/link.cxx
index bbb49ca..d085a1d 100644
--- a/build2/cxx/link.cxx
+++ b/build2/cxx/link.cxx
@@ -266,7 +266,7 @@ namespace build2
// liba
//
path an;
- const string* ae;
+ const string* ae (nullptr);
if (l || p.is_a<liba> ())
{
@@ -302,7 +302,7 @@ namespace build2
// libso
//
path sn;
- const string* se;
+ const string* se (nullptr);
if (l || p.is_a<libso> ())
{
diff --git a/build2/depdb b/build2/depdb
index 74b6678..6dec29e 100644
--- a/build2/depdb
+++ b/build2/depdb
@@ -154,7 +154,17 @@ namespace build2
}
string*
- expect (const path& v) {return expect (v.string ());}
+ expect (const path& v)
+ {
+ string* l (read ());
+ if (l == nullptr || path::traits::compare (*l, v.string ()) != 0)
+ {
+ write (v);
+ return l;
+ }
+
+ return nullptr;
+ }
string*
expect (const char* v)
diff --git a/build2/name b/build2/name
index 68fe9c7..84e334a 100644
--- a/build2/name
+++ b/build2/name
@@ -106,19 +106,42 @@ namespace build2
inline bool
operator< (const name& x, const name& y) {return x.compare (y) < 0;}
+ // Serialize the name to the stream. If requested, the name components
+ // containing special characters are quoted. The special characters are:
+ //
+ // {}[]$() \t\n#\"'%
+ //
+ // If the pair argument is not '\0', then it is added to the above special
+ // characters set. If the quote character is present in the component then
+ // it is double quoted rather than single quoted. In this case the following
+ // characters are escaped:
+ //
+ // \$("
+ //
ostream&
- operator<< (ostream&, const name&);
+ to_stream (ostream&, const name&, bool quote, char pair);
+
+ inline ostream&
+ operator<< (ostream& os, const name& n) {
+ return to_stream (os, n, false, '\0');}
+
// Vector of names.
//
using names = vector<name>;
using names_view = vector_view<const name>;
+ // The same semantics as to_stream(name).
+ //
ostream&
- operator<< (ostream&, const names_view&);
+ to_stream (ostream&, const names_view&, bool quote, char pair);
+
+ inline ostream&
+ operator<< (ostream& os, const names_view& ns) {
+ return to_stream (os, ns, false, '\0');}
inline ostream&
- operator<< (ostream& os, const names& n) {return os << names_view (n);}
+ operator<< (ostream& os, const names& ns) {return os << names_view (ns);}
}
#include <build2/name.ixx>
diff --git a/build2/name.cxx b/build2/name.cxx
index f3e4c0f..de1ee51 100644
--- a/build2/name.cxx
+++ b/build2/name.cxx
@@ -4,15 +4,69 @@
#include <build2/types> // Note: not <build2/names>
+#include <string.h> // strchr()
+
+#include <sstream>
+
#include <build2/diagnostics>
namespace build2
{
ostream&
- operator<< (ostream& os, const name& n)
+ to_stream (ostream& os, const name& n, bool quote, char pair)
{
+ auto write_string = [quote, pair, &os](const string& v)
+ {
+ char sc[] = {
+ '{', '}', '[', ']', '$', '(', ')', // Token endings.
+ ' ', '\t', '\n', '#', // Spaces.
+ '\\', '"', // Escaping and quoting.
+ '%', // Project name separator.
+ pair, // Pair separator, if any.
+ '\0'};
+
+ if (quote && v.find ('\'') != string::npos)
+ {
+ // Quote the string with the double quotes rather than with the single
+ // one. Escape some of the special characters.
+ //
+ os << '"';
+
+ for (auto c: v)
+ {
+ if (strchr ("\\$(\"", c) != nullptr) // Special inside double quotes.
+ os << '\\';
+
+ os << c;
+ }
+
+ os << '"';
+ }
+ else if (quote && v.find_first_of (sc) != string::npos)
+ os << "'" << v << "'";
+ else
+ os << v;
+ };
+
+ auto write_dir = [quote, &os, &write_string](const dir_path& d)
+ {
+ if (quote)
+ {
+ std::ostringstream s;
+ stream_verb (s, stream_verb (os));
+ s << d;
+
+ write_string (s.str ());
+ }
+ else
+ os << d;
+ };
+
if (n.proj != nullptr)
- os << *n.proj << '%';
+ {
+ write_string (*n.proj);
+ os << '%';
+ }
// If the value is empty, then we want to print the directory
// inside {}, e.g., dir{bar/}, not bar/dir{}. We also want to
@@ -23,15 +77,18 @@ namespace build2
bool t (!n.type.empty () || (!d && !v));
if (v)
- os << n.dir;
+ write_dir (n.dir);
if (t)
- os << n.type << '{';
+ {
+ write_string (n.type);
+ os << '{';
+ }
if (v)
- os << n.value;
+ write_string (n.value);
else
- os << n.dir;
+ write_dir (n.dir);
if (t)
os << '}';
@@ -40,13 +97,13 @@ namespace build2
}
ostream&
- operator<< (ostream& os, const names_view& ns)
+ to_stream (ostream& os, const names_view& ns, bool quote, char pair)
{
for (auto i (ns.begin ()), e (ns.end ()); i != e; )
{
const name& n (*i);
++i;
- os << n;
+ to_stream (os, n, quote, pair);
if (n.pair)
os << n.pair;
diff --git a/build2/parser.cxx b/build2/parser.cxx
index 6351cae..9d11d74 100644
--- a/build2/parser.cxx
+++ b/build2/parser.cxx
@@ -31,15 +31,26 @@ namespace build2
class parser::enter_scope
{
public:
- enter_scope (): p_ (nullptr) {}
+ enter_scope (): p_ (nullptr), r_ (nullptr), s_ (nullptr) {}
+
enter_scope (parser& p, dir_path&& d): p_ (&p), r_ (p.root_), s_ (p.scope_)
{
- // Relative scopes are opened relative to out, not src.
+ // Check for the global scope as a special case. While on POSIX the
+ // check is redundant, on Windows the path completion/normalization
+ // would otherwise transform it to the out path of the current scope
+ // since "/" is a relative path on Windows (and we use "/" even on
+ // Windows for that gloabl scope).
//
- if (d.relative ())
- d = p.scope_->out_path () / d;
+ if (d != dir_path ("/"))
+ {
+ // Relative scopes are opened relative to out, not src.
+ //
+ if (d.relative ())
+ d = p.scope_->out_path () / d;
+
+ d.normalize ();
+ }
- d.normalize ();
p.switch_scope (d);
}
@@ -70,7 +81,8 @@ namespace build2
class parser::enter_target
{
public:
- enter_target (): p_ (nullptr) {}
+ enter_target (): p_ (nullptr), t_ (nullptr) {}
+
enter_target (parser& p, name&& n, const location& loc, tracer& tr)
: p_ (&p), t_ (p.target_)
{
@@ -1074,7 +1086,8 @@ namespace build2
try {iv = to_version (v);}
catch (const invalid_argument& e)
{
- fail (l) << "invalid version '" << v << "': " << e.what ();
+ error (l) << "invalid version '" << v << "': " << e.what ();
+ throw failed ();
}
if (iv > BUILD2_VERSION)
diff --git a/tests/amalgam/unnamed/test.sh b/tests/amalgam/unnamed/test.sh
index afcb3bd..c745b76 100755
--- a/tests/amalgam/unnamed/test.sh
+++ b/tests/amalgam/unnamed/test.sh
@@ -1,3 +1,3 @@
#!/bin/sh
-b -q | diff -u test.out -
+b -q | diff --strip-trailing-cr -u test.out -
diff --git a/tests/escaping/test.sh b/tests/escaping/test.sh
index afcb3bd..c745b76 100755
--- a/tests/escaping/test.sh
+++ b/tests/escaping/test.sh
@@ -1,3 +1,3 @@
#!/bin/sh
-b -q | diff -u test.out -
+b -q | diff --strip-trailing-cr -u test.out -
diff --git a/tests/eval/test.sh b/tests/eval/test.sh
index afcb3bd..c745b76 100755
--- a/tests/eval/test.sh
+++ b/tests/eval/test.sh
@@ -1,3 +1,3 @@
#!/bin/sh
-b -q | diff -u test.out -
+b -q | diff --strip-trailing-cr -u test.out -
diff --git a/tests/function/call/test.sh b/tests/function/call/test.sh
index afcb3bd..c745b76 100755
--- a/tests/function/call/test.sh
+++ b/tests/function/call/test.sh
@@ -1,3 +1,3 @@
#!/bin/sh
-b -q | diff -u test.out -
+b -q | diff --strip-trailing-cr -u test.out -
diff --git a/tests/if-else/test.sh b/tests/if-else/test.sh
index afcb3bd..c745b76 100755
--- a/tests/if-else/test.sh
+++ b/tests/if-else/test.sh
@@ -1,3 +1,3 @@
#!/bin/sh
-b -q | diff -u test.out -
+b -q | diff --strip-trailing-cr -u test.out -
diff --git a/tests/keyword/test.sh b/tests/keyword/test.sh
index afcb3bd..c745b76 100755
--- a/tests/keyword/test.sh
+++ b/tests/keyword/test.sh
@@ -1,3 +1,3 @@
#!/bin/sh
-b -q | diff -u test.out -
+b -q | diff --strip-trailing-cr -u test.out -
diff --git a/tests/names/test.sh b/tests/names/test.sh
index afcb3bd..c745b76 100755
--- a/tests/names/test.sh
+++ b/tests/names/test.sh
@@ -1,3 +1,3 @@
#!/bin/sh
-b -q | diff -u test.out -
+b -q | diff --strip-trailing-cr -u test.out -
diff --git a/tests/pairs/test.sh b/tests/pairs/test.sh
index afcb3bd..c745b76 100755
--- a/tests/pairs/test.sh
+++ b/tests/pairs/test.sh
@@ -1,3 +1,3 @@
#!/bin/sh
-b -q | diff -u test.out -
+b -q | diff --strip-trailing-cr -u test.out -
diff --git a/tests/quote/test.sh b/tests/quote/test.sh
index afcb3bd..c745b76 100755
--- a/tests/quote/test.sh
+++ b/tests/quote/test.sh
@@ -1,3 +1,3 @@
#!/bin/sh
-b -q | diff -u test.out -
+b -q | diff --strip-trailing-cr -u test.out -
diff --git a/tests/scope/test.sh b/tests/scope/test.sh
index 61194f6..2728a9d 100755
--- a/tests/scope/test.sh
+++ b/tests/scope/test.sh
@@ -2,11 +2,14 @@
# In-tree.
#
-b amalgamation/l1/ 2>/dev/null | diff -u test-1.out -
+b amalgamation/l1/ 2>/dev/null | diff --strip-trailing-cr -u test-1.out -
# Out-of-tree.
#
rm -rf a-out/
b 'configure(amalgamation/@a-out/)' 2>/dev/null
-b amalgamation/l1/@a-out/l1/ 2>/dev/null | diff -u test-2.out -
+
+b amalgamation/l1/@a-out/l1/ 2>/dev/null | \
+ diff --strip-trailing-cr -u test-2.out -
+
rm -rf a-out/
diff --git a/tests/test.sh b/tests/test.sh
new file mode 100755
index 0000000..ce115d5
--- /dev/null
+++ b/tests/test.sh
@@ -0,0 +1,30 @@
+#! /usr/bin/env bash
+
+cur_dir="`pwd`"
+trap 'cd "$cur_dir"' EXIT
+
+export PATH=$cur_dir/../build2:$PATH
+
+function test ()
+{
+ echo "Testing $1"
+ cd "$cur_dir/$1"
+ ./test.sh
+}
+
+test "amalgam/unnamed"
+test "escaping"
+test "eval"
+test "function/call"
+test "if-else"
+test "keyword"
+test "names"
+test "pairs"
+test "quote"
+test "scope"
+test "variable/expansion"
+test "variable/null"
+test "variable/override"
+test "variable/prepend"
+test "variable/qualified"
+test "variable/type"
diff --git a/tests/variable/expansion/test.sh b/tests/variable/expansion/test.sh
index afcb3bd..c745b76 100755
--- a/tests/variable/expansion/test.sh
+++ b/tests/variable/expansion/test.sh
@@ -1,3 +1,3 @@
#!/bin/sh
-b -q | diff -u test.out -
+b -q | diff --strip-trailing-cr -u test.out -
diff --git a/tests/variable/null/test.sh b/tests/variable/null/test.sh
index afcb3bd..c745b76 100755
--- a/tests/variable/null/test.sh
+++ b/tests/variable/null/test.sh
@@ -1,3 +1,3 @@
#!/bin/sh
-b -q | diff -u test.out -
+b -q | diff --strip-trailing-cr -u test.out -
diff --git a/tests/variable/override/test.sh b/tests/variable/override/test.sh
index 63a792f..e960929 100755
--- a/tests/variable/override/test.sh
+++ b/tests/variable/override/test.sh
@@ -2,6 +2,25 @@
verbose=n
+# By default when MSYS2 executable (bash.exe in particular) runs another
+# executable it converts arguments that look like POSIX paths to Windows
+# representations. More about it at:
+#
+# http://www.mingw.org/wiki/Posix_path_conversion
+#
+# So when you run b /v=X, build2 gets 'C:/msys64/v=X' argument instead of
+# '/v=X'. To disable this behavior set MSYS2_ARG_CONV_EXCL environment
+# variable, so all arguments starting with / will not be converted. You can
+# list more prefixes using ';' as a separator.
+#
+export MSYS2_ARG_CONV_EXCL=/
+
+tmp_file=`mktemp`
+
+# Remove temporary file on exit. Cover the case when exit due to an error.
+#
+trap 'rm -f $tmp_file' EXIT
+
function error () { echo "$*" 1>&2; exit 1; }
function fail ()
@@ -21,10 +40,13 @@ function fail ()
function test ()
{
- # There is no way to get the exit code in process substitution
- # so ruin the output.
- #
- diff -u - <(b -q $* || echo "<invalid output>")
+ b -q $* >$tmp_file
+
+ if [ $? -ne 0 ]; then
+ error "failed: b -q $* >$tmp_file"
+ fi
+
+ diff --strip-trailing-cr -u - $tmp_file
if [ $? -ne 0 ]; then
error "failed: b $*"
diff --git a/tests/variable/prepend/test.sh b/tests/variable/prepend/test.sh
index afcb3bd..c745b76 100755
--- a/tests/variable/prepend/test.sh
+++ b/tests/variable/prepend/test.sh
@@ -1,3 +1,3 @@
#!/bin/sh
-b -q | diff -u test.out -
+b -q | diff --strip-trailing-cr -u test.out -
diff --git a/tests/variable/qualified/test.sh b/tests/variable/qualified/test.sh
index afcb3bd..c745b76 100755
--- a/tests/variable/qualified/test.sh
+++ b/tests/variable/qualified/test.sh
@@ -1,3 +1,3 @@
#!/bin/sh
-b -q | diff -u test.out -
+b -q | diff --strip-trailing-cr -u test.out -
diff --git a/tests/variable/type/test.sh b/tests/variable/type/test.sh
index afcb3bd..c745b76 100755
--- a/tests/variable/type/test.sh
+++ b/tests/variable/type/test.sh
@@ -1,3 +1,3 @@
#!/bin/sh
-b -q | diff -u test.out -
+b -q | diff --strip-trailing-cr -u test.out -