From b0e481a653b01e4329bccb1d101d56e3e878e960 Mon Sep 17 00:00:00 2001 From: Karen Arutyunov Date: Fri, 3 Jun 2016 16:43:46 +0300 Subject: Port to MinGW --- build2/b.cxx | 17 +--------- build2/cli/module.cxx | 4 +-- build2/config/operation.cxx | 9 +++-- build2/context.cxx | 6 ++-- build2/context.txx | 6 ++-- build2/cxx/compile.cxx | 38 +++++++++++++++++---- build2/cxx/link.cxx | 4 +-- build2/depdb | 12 ++++++- build2/name | 29 ++++++++++++++-- build2/name.cxx | 73 +++++++++++++++++++++++++++++++++++----- build2/parser.cxx | 27 +++++++++++---- tests/amalgam/unnamed/test.sh | 2 +- tests/escaping/test.sh | 2 +- tests/eval/test.sh | 2 +- tests/function/call/test.sh | 2 +- tests/if-else/test.sh | 2 +- tests/keyword/test.sh | 2 +- tests/names/test.sh | 2 +- tests/pairs/test.sh | 2 +- tests/quote/test.sh | 2 +- tests/scope/test.sh | 7 ++-- tests/test.sh | 30 +++++++++++++++++ tests/variable/expansion/test.sh | 2 +- tests/variable/null/test.sh | 2 +- tests/variable/override/test.sh | 30 ++++++++++++++--- tests/variable/prepend/test.sh | 2 +- tests/variable/qualified/test.sh | 2 +- tests/variable/type/test.sh | 2 +- 28 files changed, 249 insertions(+), 71 deletions(-) create mode 100755 tests/test.sh 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 // getenv() #include // getuid() #include // uid_t -#include // struct passwd, getpwuid() #include #include // 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 ()) { @@ -302,7 +302,7 @@ namespace build2 // libso // path sn; - const string* se; + const string* se (nullptr); if (l || p.is_a ()) { 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; using names_view = vector_view; + // 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 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 // Note: not +#include // strchr() + +#include + #include 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 "") + 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 - -- cgit v1.1