aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBoris Kolpackov <boris@codesynthesis.com>2016-08-22 12:55:21 +0200
committerBoris Kolpackov <boris@codesynthesis.com>2016-08-22 12:55:21 +0200
commit48e2e4140b8e5aacdfd107a1215f21c9632c81c8 (patch)
tree7897b62300a433533318be5de14f9ba0745da4d8
parent3b6f882ce0dae1d80a330b36a63fbe65026a3278 (diff)
Cache process_path, use fallback search directory for binutils
-rw-r--r--build2/bin/guess10
-rw-r--r--build2/bin/guess.cxx81
-rw-r--r--build2/bin/init.cxx83
-rw-r--r--build2/c/init.cxx21
-rw-r--r--build2/cc/common1
-rw-r--r--build2/cc/compile.cxx14
-rw-r--r--build2/cc/guess6
-rw-r--r--build2/cc/guess.cxx49
-rw-r--r--build2/cc/link8
-rw-r--r--build2/cc/link.cxx27
-rw-r--r--build2/cc/module.cxx3
-rw-r--r--build2/cc/msvc.cxx16
-rw-r--r--build2/cli/init.cxx34
-rw-r--r--build2/cxx/init.cxx21
-rw-r--r--build2/types4
-rw-r--r--build2/utility59
-rw-r--r--build2/utility.cxx47
-rw-r--r--build2/utility.txx9
-rw-r--r--build2/variable22
-rw-r--r--build2/variable.cxx98
-rw-r--r--build2/variable.ixx29
21 files changed, 498 insertions, 144 deletions
diff --git a/build2/bin/guess b/build2/bin/guess
index 540e644..bcf9732 100644
--- a/build2/bin/guess
+++ b/build2/bin/guess
@@ -30,10 +30,12 @@ namespace build2
//
struct ar_info
{
+ process_path ar_path;
string ar_id;
string ar_signature;
string ar_checksum;
+ process_path ranlib_path;
string ranlib_id;
string ranlib_signature;
string ranlib_checksum;
@@ -43,7 +45,7 @@ namespace build2
// attemplated and the returned ranlib_* members will be left empty.
//
ar_info
- guess_ar (const path& ar, const path* ranlib);
+ guess_ar (const path& ar, const path* ranlib, const dir_path& fallback);
// ld information.
//
@@ -67,13 +69,14 @@ namespace build2
//
struct ld_info
{
+ process_path path;
string id;
string signature;
string checksum;
};
ld_info
- guess_ld (const path& ld);
+ guess_ld (const path& ld, const dir_path& fallback);
// rc information.
//
@@ -90,13 +93,14 @@ namespace build2
//
struct rc_info
{
+ process_path path;
string id;
string signature;
string checksum;
};
rc_info
- guess_rc (const path& rc);
+ guess_rc (const path& rc, const dir_path& fallback);
}
}
diff --git a/build2/bin/guess.cxx b/build2/bin/guess.cxx
index de91db3..13f3933 100644
--- a/build2/bin/guess.cxx
+++ b/build2/bin/guess.cxx
@@ -18,17 +18,26 @@ namespace build2
string signature;
string checksum;
+ guess_result () = default;
+ guess_result (string&& i, string&& s)
+ : id (move (i)), signature (move (s)) {}
+
bool
empty () const {return id.empty ();}
};
ar_info
- guess_ar (const path& ar, const path* rl)
+ guess_ar (const path& ar, const path* rl, const dir_path& fallback)
{
tracer trace ("bin::guess_ar");
guess_result arr, rlr;
+ process_path arp (run_search (ar, true, fallback));
+ process_path rlp (rl != nullptr
+ ? run_search (*rl, true, fallback)
+ : process_path ());
+
// Binutils, LLVM, and FreeBSD ar/ranlib all recognize the --version
// option. While Microsoft's lib.exe doesn't support --version, it only
// issues a warning and exits with zero status, printing its usual
@@ -43,23 +52,23 @@ namespace build2
// "GNU ar ".
//
if (l.compare (0, 7, "GNU ar ") == 0)
- return guess_result {"gnu", move (l), ""};
+ return guess_result ("gnu", move (l));
// LLVM ar --version output has a line that starts with
// "LLVM version ".
//
if (l.compare (0, 13, "LLVM version ") == 0)
- return guess_result {"llvm", move (l), ""};
+ return guess_result ("llvm", move (l));
// FreeBSD ar --verison output starts with "BSD ar ".
//
if (l.compare (0, 7, "BSD ar ") == 0)
- return guess_result {"bsd", move (l), ""};
+ return guess_result ("bsd", move (l));
// Microsoft lib.exe output starts with "Microsoft (R) ".
//
if (l.compare (0, 14, "Microsoft (R) ") == 0)
- return guess_result {"msvc", move (l), ""};
+ return guess_result ("msvc", move (l));
return guess_result ();
};
@@ -69,7 +78,7 @@ namespace build2
// (yes, it goes to stdout) but that seems harmless.
//
sha256 cs;
- arr = run<guess_result> (ar, "--version", f, false, false, &cs);
+ arr = run<guess_result> (arp, "--version", f, false, false, &cs);
if (!arr.empty ())
arr.checksum = cs.string ();
@@ -85,14 +94,14 @@ namespace build2
auto f = [] (string& l) -> guess_result
{
return l.find (" ar ") != string::npos
- ? guess_result {"generic", move (l), ""}
+ ? guess_result ("generic", move (l))
: guess_result ();
};
// Redirect STDERR to STDOUT and ignore exit status.
//
sha256 cs;
- arr = run<guess_result> (ar, f, false, true, &cs);
+ arr = run<guess_result> (arp, f, false, true, &cs);
if (!arr.empty ())
{
@@ -116,24 +125,24 @@ namespace build2
// "GNU ranlib ".
//
if (l.compare (0, 11, "GNU ranlib ") == 0)
- return guess_result {"gnu", move (l), ""};
+ return guess_result ("gnu", move (l));
// "LLVM version ".
//
if (l.compare (0, 13, "LLVM version ") == 0)
- return guess_result {"llvm", move (l), ""};
+ return guess_result ("llvm", move (l));
// On FreeBSD we get "ranlib" rather than "BSD ranlib" for some
// reason. Which means we can't really call it 'bsd' for sure.
//
//if (l.compare (0, 7, "ranlib ") == 0)
- // return guess_result {"bsd", move (l), ""};
+ // return guess_result ("bsd", move (l));
return guess_result ();
};
sha256 cs;
- rlr = run<guess_result> (*rl, "--version", f, false, false, &cs);
+ rlr = run<guess_result> (rlp, "--version", f, false, false, &cs);
if (!rlr.empty ())
rlr.checksum = cs.string ();
@@ -146,14 +155,14 @@ namespace build2
auto f = [] (string& l) -> guess_result
{
return l.find ("ranlib") != string::npos
- ? guess_result {"generic", move (l), ""}
+ ? guess_result ("generic", move (l))
: guess_result ();
};
// Redirect STDERR to STDOUT and ignore exit status.
//
sha256 cs;
- rlr = run<guess_result> (*rl, f, false, true, &cs);
+ rlr = run<guess_result> (rlp, f, false, true, &cs);
if (!rlr.empty ())
{
@@ -167,17 +176,19 @@ namespace build2
}
return ar_info {
- move (arr.id), move (arr.signature), move (arr.checksum),
- move (rlr.id), move (rlr.signature), move (rlr.checksum)};
+ move (arp), move (arr.id), move (arr.signature), move (arr.checksum),
+ move (rlp), move (rlr.id), move (rlr.signature), move (rlr.checksum)};
}
ld_info
- guess_ld (const path& ld)
+ guess_ld (const path& ld, const dir_path& fallback)
{
tracer trace ("bin::guess_ld");
guess_result r;
+ process_path pp (run_search (ld, true, fallback));
+
// Binutils ld recognizes the --version option. Microsoft's link.exe
// doesn't support --version (nor any other way to get the version
// without the error exist status) but it will still print its banner.
@@ -194,16 +205,16 @@ namespace build2
// Microsoft link.exe output starts with "Microsoft (R) ".
//
if (l.compare (0, 14, "Microsoft (R) ") == 0)
- return guess_result {"msvc", move (l), ""};
+ return guess_result ("msvc", move (l));
// Binutils ld.bfd --version output has a line that starts with
// "GNU ld " while ld.gold -- "GNU gold".
//
if (l.compare (0, 7, "GNU ld ") == 0)
- return guess_result {"gnu", move (l), ""};
+ return guess_result ("gnu", move (l));
if (l.compare (0, 9, "GNU gold ") == 0)
- return guess_result {"gold", move (l), ""};
+ return guess_result ("gold", move (l));
return guess_result ();
};
@@ -213,7 +224,7 @@ namespace build2
// but that seems harmless.
//
sha256 cs;
- r = run<guess_result> (ld, "--version", f, false, true, &cs);
+ r = run<guess_result> (pp, "--version", f, false, true, &cs);
if (!r.empty ())
r.checksum = cs.string ();
@@ -231,20 +242,20 @@ namespace build2
// @(#)PROGRAM:ld PROJECT:ld64-242.2
//
if (l.find ("PROJECT:ld64") != string::npos)
- return guess_result {"ld64", move (l), ""};
+ return guess_result ("ld64", move (l));
// Old ld has "cctools" in the first line, for example:
//
// Apple Computer, Inc. version cctools-622.9~2
//
if (l.find ("cctools") != string::npos)
- return guess_result {"cctools", move (l), ""};
+ return guess_result ("cctools", move (l));
return guess_result ();
};
sha256 cs;
- r = run<guess_result> (ld, "-v", f, false, false, &cs);
+ r = run<guess_result> (pp, "-v", f, false, false, &cs);
if (!r.empty ())
r.checksum = cs.string ();
@@ -262,7 +273,7 @@ namespace build2
// LLVM Linker Version: 3.7
//
if (l.compare (0, 19, "LLVM Linker Version") == 0)
- return guess_result {"llvm", move (l), ""};
+ return guess_result ("llvm", move (l));
return guess_result ();
};
@@ -271,7 +282,7 @@ namespace build2
// option.
//
sha256 cs;
- r = run<guess_result> (ld, "-version", f, false, false, &cs);
+ r = run<guess_result> (pp, "-version", f, false, false, &cs);
if (!r.empty ())
r.checksum = cs.string ();
@@ -280,16 +291,19 @@ namespace build2
if (r.empty ())
fail << "unable to guess " << ld << " signature";
- return ld_info {move (r.id), move (r.signature), move (r.checksum)};
+ return ld_info {
+ move (pp), move (r.id), move (r.signature), move (r.checksum)};
}
rc_info
- guess_rc (const path& rc)
+ guess_rc (const path& rc, const dir_path& fallback)
{
tracer trace ("bin::guess_rc");
guess_result r;
+ process_path pp (run_search (rc, true, fallback));
+
// Binutils windres recognizes the --version option.
//
{
@@ -299,7 +313,7 @@ namespace build2
// "GNU windres ".
//
if (l.compare (0, 12, "GNU windres ") == 0)
- return guess_result {"gnu", move (l), ""};
+ return guess_result ("gnu", move (l));
return guess_result ();
};
@@ -308,7 +322,7 @@ namespace build2
// option.
//
sha256 cs;
- r = run<guess_result> (rc, "--version", f, false, false, &cs);
+ r = run<guess_result> (pp, "--version", f, false, false, &cs);
if (!r.empty ())
r.checksum = cs.string ();
@@ -322,13 +336,13 @@ namespace build2
auto f = [] (string& l) -> guess_result
{
if (l.compare (0, 14, "Microsoft (R) ") == 0)
- return guess_result {"msvc", move (l), ""};
+ return guess_result ("msvc", move (l));
return guess_result ();
};
sha256 cs;
- r = run<guess_result> (rc, "/?", f, false, false, &cs);
+ r = run<guess_result> (pp, "/?", f, false, false, &cs);
if (!r.empty ())
r.checksum = cs.string ();
@@ -337,7 +351,8 @@ namespace build2
if (r.empty ())
fail << "unable to guess " << rc << " signature";
- return rc_info {move (r.id), move (r.signature), move (r.checksum)};
+ return rc_info {
+ move (pp), move (r.id), move (r.signature), move (r.checksum)};
}
}
}
diff --git a/build2/bin/init.cxx b/build2/bin/init.cxx
index 424a729..125bc7d 100644
--- a/build2/bin/init.cxx
+++ b/build2/bin/init.cxx
@@ -273,8 +273,12 @@ namespace build2
{
const string& s (cast<string> (*v));
- if (s.find ('*') == string::npos)
+ if (s.empty () ||
+ (!path::traits::is_separator (s.back ()) &&
+ s.find ('*') == string::npos))
+ {
fail << "missing '*' in binutils pattern '" << s << "'";
+ }
r.assign<string> ("bin.pattern") = s;
new_val = new_val || p.second; // False for a hinted value.
@@ -428,8 +432,11 @@ namespace build2
{
auto& v (var_pool);
- v.insert<path> ("config.bin.ar", true);
- v.insert<path> ("config.bin.ranlib", true);
+ v.insert<process_path> ("bin.rc.path");
+ v.insert<process_path> ("bin.ranlib.path");
+
+ v.insert<path> ("config.bin.ar", true);
+ v.insert<path> ("config.bin.ranlib", true);
}
// Configure.
@@ -456,6 +463,11 @@ namespace build2
const string& tsys (cast<string> (r["bin.target.system"]));
const char* ar_d (tsys == "win32-msvc" ? "lib" : "ar");
+ // This can be either a pattern or a fallback search directory.
+ //
+ const string* pat (cast_null<string> (r["bin.pattern"]));
+ bool fb (pat != nullptr && path::traits::is_separator (pat->back ()));
+
// Don't save the default value to config.build so that if the user
// changes, say, the C++ compiler (which hinted the pattern), then
// ar will automatically change as well.
@@ -464,7 +476,7 @@ namespace build2
config::required (
r,
"config.bin.ar",
- path (apply_pattern (ar_d, cast_null<string> (r["bin.pattern"]))),
+ path (apply_pattern (ar_d, fb ? nullptr : pat)),
false,
config::save_commented));
@@ -482,7 +494,8 @@ namespace build2
if (ranlib != nullptr && ranlib->empty ()) // @@ BC LT [null].
ranlib = nullptr;
- ar_info ari (guess_ar (ar, ranlib));
+ ar_info ari (
+ guess_ar (ar, ranlib, fb ? dir_path (*pat) : dir_path ()));
// If this is a new value (e.g., we are configuring), then print the
// report at verbosity level 2 and up (-v).
@@ -492,7 +505,7 @@ namespace build2
diag_record dr (text);
dr << "bin.ar " << project (r) << '@' << r.out_path () << '\n'
- << " ar " << ar << '\n'
+ << " ar " << ari.ar_path << '\n'
<< " id " << ari.ar_id << '\n'
<< " signature " << ari.ar_signature << '\n'
<< " checksum " << ari.ar_checksum;
@@ -500,23 +513,25 @@ namespace build2
if (ranlib != nullptr)
{
dr << '\n'
- << " ranlib " << *ranlib << '\n'
+ << " ranlib " << ari.ranlib_path << '\n'
<< " id " << ari.ranlib_id << '\n'
<< " signature " << ari.ranlib_signature << '\n'
<< " checksum " << ari.ranlib_checksum;
}
}
- r.assign<string> ("bin.ar.id") = move (ari.ar_id);
- r.assign<string> ("bin.ar.signature") = move (ari.ar_signature);
- r.assign<string> ("bin.ar.checksum") = move (ari.ar_checksum);
+ r.assign<process_path> ("bin.ar.path") = move (ari.ar_path);
+ r.assign<string> ("bin.ar.id") = move (ari.ar_id);
+ r.assign<string> ("bin.ar.signature") = move (ari.ar_signature);
+ r.assign<string> ("bin.ar.checksum") = move (ari.ar_checksum);
if (ranlib != nullptr)
{
- r.assign<string> ("bin.ranlib.id") = move (ari.ranlib_id);
- r.assign<string> ("bin.ranlib.signature") =
+ r.assign<process_path> ("bin.ranlib.path") = move (ari.ranlib_path);
+ r.assign<string> ("bin.ranlib.id") = move (ari.ranlib_id);
+ r.assign<string> ("bin.ranlib.signature") =
move (ari.ranlib_signature);
- r.assign<string> ("bin.ranlib.checksum") =
+ r.assign<string> ("bin.ranlib.checksum") =
move (ari.ranlib_checksum);
}
}
@@ -570,7 +585,8 @@ namespace build2
{
auto& v (var_pool);
- v.insert<path> ("config.bin.ld", true);
+ v.insert<process_path> ("bin.ld.path");
+ v.insert<path> ("config.bin.ld", true);
}
// Configure.
@@ -584,16 +600,21 @@ namespace build2
const string& tsys (cast<string> (r["bin.target.system"]));
const char* ld_d (tsys == "win32-msvc" ? "link" : "ld");
+ // This can be either a pattern or a fallback search directory.
+ //
+ const string* pat (cast_null<string> (r["bin.pattern"]));
+ bool fb (pat != nullptr && path::traits::is_separator (pat->back ()));
+
auto p (
config::required (
r,
"config.bin.ld",
- path (apply_pattern (ld_d, cast_null<string> (r["bin.pattern"]))),
+ path (apply_pattern (ld_d, fb ? nullptr : pat)),
false,
config::save_commented));
const path& ld (cast<path> (p.first));
- ld_info ldi (guess_ld (ld));
+ ld_info ldi (guess_ld (ld, fb ? dir_path (*pat) : dir_path ()));
// If this is a new value (e.g., we are configuring), then print the
// report at verbosity level 2 and up (-v).
@@ -601,15 +622,16 @@ namespace build2
if (verb >= (p.second ? 2 : 3))
{
text << "bin.ld " << project (r) << '@' << r.out_path () << '\n'
- << " ld " << ld << '\n'
+ << " ld " << ldi.path << '\n'
<< " id " << ldi.id << '\n'
<< " signature " << ldi.signature << '\n'
<< " checksum " << ldi.checksum;
}
- r.assign<string> ("bin.ld.id") = move (ldi.id);
- r.assign<string> ("bin.ld.signature") = move (ldi.signature);
- r.assign<string> ("bin.ld.checksum") = move (ldi.checksum);
+ r.assign<process_path> ("bin.ld.path") = move (ldi.path);
+ r.assign<string> ("bin.ld.id") = move (ldi.id);
+ r.assign<string> ("bin.ld.signature") = move (ldi.signature);
+ r.assign<string> ("bin.ld.checksum") = move (ldi.checksum);
}
return true;
@@ -674,7 +696,8 @@ namespace build2
{
auto& v (var_pool);
- v.insert<path> ("config.bin.rc", true);
+ v.insert<process_path> ("bin.rc.path");
+ v.insert<path> ("config.bin.rc", true);
}
// Configure.
@@ -688,16 +711,21 @@ namespace build2
const string& tsys (cast<string> (r["bin.target.system"]));
const char* rc_d (tsys == "win32-msvc" ? "rc" : "windres");
+ // This can be either a pattern or a fallback search directory.
+ //
+ const string* pat (cast_null<string> (r["bin.pattern"]));
+ bool fb (pat != nullptr && path::traits::is_separator (pat->back ()));
+
auto p (
config::required (
r,
"config.bin.rc",
- path (apply_pattern (rc_d, cast_null<string> (r["bin.pattern"]))),
+ path (apply_pattern (rc_d, fb ? nullptr : pat)),
false,
config::save_commented));
const path& rc (cast<path> (p.first));
- rc_info rci (guess_rc (rc));
+ rc_info rci (guess_rc (rc, fb ? dir_path (*pat) : dir_path ()));
// If this is a new value (e.g., we are configuring), then print the
// report at verbosity level 2 and up (-v).
@@ -705,15 +733,16 @@ namespace build2
if (verb >= (p.second ? 2 : 3))
{
text << "bin.rc " << project (r) << '@' << r.out_path () << '\n'
- << " rc " << rc << '\n'
+ << " rc " << rci.path << '\n'
<< " id " << rci.id << '\n'
<< " signature " << rci.signature << '\n'
<< " checksum " << rci.checksum;
}
- r.assign<string> ("bin.rc.id") = move (rci.id);
- r.assign<string> ("bin.rc.signature") = move (rci.signature);
- r.assign<string> ("bin.rc.checksum") = move (rci.checksum);
+ r.assign<process_path> ("bin.rc.path") = move (rci.path);
+ r.assign<string> ("bin.rc.id") = move (rci.id);
+ r.assign<string> ("bin.rc.signature") = move (rci.signature);
+ r.assign<string> ("bin.rc.checksum") = move (rci.checksum);
}
return true;
diff --git a/build2/c/init.cxx b/build2/c/init.cxx
index 52bf0b6..88f0b2b 100644
--- a/build2/c/init.cxx
+++ b/build2/c/init.cxx
@@ -121,16 +121,17 @@ namespace build2
// Note: some overridable, some not.
//
- v.insert<path> ("config.c", true),
- v.insert<strings> ("config.c.poptions", true),
- v.insert<strings> ("config.c.coptions", true),
- v.insert<strings> ("config.c.loptions", true),
- v.insert<strings> ("config.c.libs", true),
-
- v.insert<strings> ("c.poptions"),
- v.insert<strings> ("c.coptions"),
- v.insert<strings> ("c.loptions"),
- v.insert<strings> ("c.libs"),
+ v.insert<path> ("config.c", true),
+ v.insert<strings> ("config.c.poptions", true),
+ v.insert<strings> ("config.c.coptions", true),
+ v.insert<strings> ("config.c.loptions", true),
+ v.insert<strings> ("config.c.libs", true),
+
+ v.insert<process_path> ("c.path"),
+ v.insert<strings> ("c.poptions"),
+ v.insert<strings> ("c.coptions"),
+ v.insert<strings> ("c.loptions"),
+ v.insert<strings> ("c.libs"),
v["cc.poptions"],
v["cc.coptions"],
diff --git a/build2/cc/common b/build2/cc/common
index 2be290a..987fe7b 100644
--- a/build2/cc/common
+++ b/build2/cc/common
@@ -36,6 +36,7 @@ namespace build2
const variable& config_x_loptions;
const variable& config_x_libs;
+ const variable& x_path;
const variable& x_poptions;
const variable& x_coptions;
const variable& x_loptions;
diff --git a/build2/cc/compile.cxx b/build2/cc/compile.cxx
index 92ddf3b..33be687 100644
--- a/build2/cc/compile.cxx
+++ b/build2/cc/compile.cxx
@@ -644,12 +644,14 @@ namespace build2
// Initialize lazily, only if required.
//
+ const process_path* xc (nullptr);
cstrings args;
string std; // Storage.
- auto init_args = [&t, lo, &src, &rs, &args, &std, this] ()
+ auto init_args = [&t, lo, &src, &rs, &xc, &args, &std, this] ()
{
- args.push_back (cast<path> (rs[config_x]).string ().c_str ());
+ xc = &cast<process_path> (rs[x_path]);
+ args.push_back (xc->recall_string ());
// Add *.export.poptions from prerequisite libraries. Note that here
// we don't need to see group members (see apply()).
@@ -1034,7 +1036,8 @@ namespace build2
// For VC with /EP we need a pipe to stderr and stdout should go
// to /dev/null.
//
- process pr (args.data (),
+ process pr (*xc,
+ args.data (),
0,
cid == "msvc" ? -2 : -1,
cid == "msvc" ? -1 : 2);
@@ -1261,7 +1264,8 @@ namespace build2
scope& rs (*bs.root_scope ());
otype ct (compile_type (t));
- cstrings args {cast<path> (rs[config_x]).string ().c_str ()};
+ const process_path& xc (cast<process_path> (rs[x_path]));
+ cstrings args {xc.recall_string ()};
// Translate paths to relative (to working directory) ones. This
// results in easier to read diagnostics.
@@ -1418,7 +1422,7 @@ namespace build2
//
bool filter (cid == "msvc");
- process pr (args.data (), 0, (filter ? -1 : 2));
+ process pr (xc, args.data (), 0, (filter ? -1 : 2));
if (filter)
{
diff --git a/build2/cc/guess b/build2/cc/guess
index d852a5c..c49fb4f 100644
--- a/build2/cc/guess
+++ b/build2/cc/guess
@@ -100,10 +100,14 @@ namespace build2
// derived for some toolchains. For example, i686-w64-mingw32-*-4.9.
//
// The bin_pattern is the binutils program pattern that could sometimes be
- // derived for some toolchains. For example, i686-w64-mingw32-*.
+ // derived for some toolchains. For example, i686-w64-mingw32-*. If the
+ // pattern could not be derived, then it could contain a fallback search
+ // directory, in which case it will end with a directory separator but
+ // will not contain '*'.
//
struct compiler_info
{
+ process_path path;
compiler_id id;
compiler_version version;
string signature;
diff --git a/build2/cc/guess.cxx b/build2/cc/guess.cxx
index 5001879..c2b552a 100644
--- a/build2/cc/guess.cxx
+++ b/build2/cc/guess.cxx
@@ -105,6 +105,11 @@ namespace build2
compiler_id id;
string signature;
string checksum;
+ process_path path;
+
+ guess_result () = default;
+ guess_result (compiler_id&& i, string&& s)
+ : id (move (i)), signature (move (s)) {}
bool
empty () const {return id.empty ();}
@@ -117,6 +122,8 @@ namespace build2
guess_result r;
+ process_path pp (run_search (xc, true));
+
// Start with -v. This will cover gcc and clang.
//
// While icc also writes what may seem like something we can use to
@@ -152,7 +159,7 @@ namespace build2
// gcc version 6.0.0 20160131 (experimental) (GCC)
//
if (l.compare (0, 4, "gcc ") == 0)
- return guess_result {{"gcc", ""}, move (l), ""};
+ return guess_result (compiler_id {"gcc", ""}, move (l));
// The Apple clang/clang++ -v output will have a line (currently
// first) in the form:
@@ -182,7 +189,7 @@ namespace build2
if (l.compare (0, 6, "Apple ") == 0 &&
(l.compare (6, 5, "LLVM ") == 0 ||
l.compare (6, 6, "clang ") == 0))
- return guess_result {{"clang", "apple"}, move (l), ""};
+ return guess_result (compiler_id {"clang", "apple"}, move (l));
// The vanilla clang/clang++ -v output will have a line (currently
// first) in the form:
@@ -197,7 +204,7 @@ namespace build2
// clang version 3.7.0 (tags/RELEASE_370/final)
//
if (l.find ("clang ") != string::npos)
- return guess_result {{"clang", ""}, move (l), ""};
+ return guess_result (compiler_id {"clang", ""}, move (l));
return guess_result ();
};
@@ -216,7 +223,7 @@ namespace build2
// Suppress all the compiler errors because we may be trying an
// unsupported option.
//
- r = run<guess_result> (xc, "-v", f, false, false, &cs);
+ r = run<guess_result> (pp, "-v", f, false, false, &cs);
if (!r.empty ())
r.checksum = cs.string ();
@@ -239,12 +246,12 @@ namespace build2
// icc (ICC) 16.0.2 20160204
//
if (l.find (" (ICC) ") != string::npos)
- return guess_result {{"icc", ""}, move (l), ""};
+ return guess_result (compiler_id {"icc", ""}, move (l));
return guess_result ();
};
- r = run<guess_result> (xc, "--version", f, false);
+ r = run<guess_result> (pp, "--version", f, false);
}
// Finally try to run it without any options to detect msvc.
@@ -271,12 +278,12 @@ namespace build2
//
if (l.find ("Microsoft (R)") != string::npos &&
l.find ("C/C++") != string::npos)
- return guess_result {{"msvc", ""}, move (l), ""};
+ return guess_result (compiler_id {"msvc", ""}, move (l));
return guess_result ();
};
- r = run<guess_result> (xc, f, false);
+ r = run<guess_result> (pp, f, false);
}
if (!r.empty ())
@@ -290,8 +297,12 @@ namespace build2
r = guess_result ();
}
else
+ {
l5 ([&]{trace << xc << " is " << r.id << ": '"
<< r.signature << "'";});
+
+ r.path = move (pp);
+ }
}
else
l4 ([&]{trace << "unable to determine compiler type of " << xc;});
@@ -463,6 +474,7 @@ namespace build2
pat = pattern (xc, xl == lang::c ? "cc" : "c++");
return compiler_info {
+ move (gr.path),
move (gr.id),
move (v),
move (gr.signature),
@@ -579,6 +591,7 @@ namespace build2
pat = pattern (xc, xl == lang::c ? "cc" : "c++");
return compiler_info {
+ move (gr.path),
move (gr.id),
move (v),
move (gr.signature),
@@ -798,6 +811,7 @@ namespace build2
sha256 cs (s);
return compiler_info {
+ move (gr.path),
move (gr.id),
move (v),
move (gr.signature),
@@ -1011,6 +1025,7 @@ namespace build2
sha256 cs (s);
return compiler_info {
+ move (gr.path),
move (gr.id),
move (v),
move (gr.signature),
@@ -1075,11 +1090,12 @@ namespace build2
// Derive binutils pattern unless this has already been done by the
// compiler-specific code.
//
+
+ // When cross-compiling the whole toolchain is normally prefixed with
+ // the target triplet, e.g., x86_64-w64-mingw32-{gcc,g++,ar,ld}.
+ //
if (r.bin_pattern.empty ())
{
- // When cross-compiling the whole toolchain is normally prefixed with
- // the target triplet, e.g., x86_64-w64-mingw32-{gcc,g++,ar,ld}.
- //
// BTW, for GCC we also get gcc-{ar,ranlib} which add support for the
// LTO plugin though it seems more recent GNU binutils (2.25) are able
// to load the plugin when needed automatically. So it doesn't seem we
@@ -1103,6 +1119,17 @@ namespace build2
}
}
+ // If we could not derive the pattern, then see if we can come up with a
+ // fallback search directory.
+ //
+ if (r.bin_pattern.empty ())
+ {
+ const path& p (r.path.recall.empty () ? xc : r.path.recall);
+
+ if (!p.simple ())
+ r.bin_pattern = p.directory ().representation (); // Trailing slash.
+ }
+
return r;
}
diff --git a/build2/cc/link b/build2/cc/link
index 8be386f..f6a16c0 100644
--- a/build2/cc/link
+++ b/build2/cc/link
@@ -56,10 +56,14 @@ namespace build2
// Alternative search logic for VC (msvc.cxx).
//
bin::liba*
- msvc_search_static (const path&, const dir_path&, prerequisite&) const;
+ msvc_search_static (const process_path&,
+ const dir_path&,
+ prerequisite&) const;
bin::libs*
- msvc_search_shared (const path&, const dir_path&, prerequisite&) const;
+ msvc_search_shared (const process_path&,
+ const dir_path&,
+ prerequisite&) const;
target*
search_library (optional<dir_paths>&, prerequisite&) const;
diff --git a/build2/cc/link.cxx b/build2/cc/link.cxx
index 61dd0ad..7a6b059 100644
--- a/build2/cc/link.cxx
+++ b/build2/cc/link.cxx
@@ -420,7 +420,7 @@ namespace build2
if (cid == "msvc")
{
scope& rs (*p.scope.root_scope ());
- const path& ld (cast<path> (rs["config.bin.ld"]));
+ const process_path& ld (cast<process_path> (rs["bin.ld.path"]));
if (s == nullptr && !sn.empty ())
s = msvc_search_shared (ld, d, p);
@@ -1135,10 +1135,12 @@ namespace build2
{
path of (relative (manifest));
+ const process_path& rc (cast<process_path> (rs["bin.rc.path"]));
+
// @@ Would be good to add this to depdb (e.g,, rc changes).
//
const char* args[] = {
- cast<path> (rs["config.bin.rc"]).string ().c_str (),
+ rc.recall_string (),
"--input-format=rc",
"--output-format=coff",
"-o", of.string ().c_str (),
@@ -1149,7 +1151,7 @@ namespace build2
try
{
- process pr (args, -1);
+ process pr (rc, args, -1);
try
{
@@ -1224,7 +1226,7 @@ namespace build2
//
if (lt == otype::a)
{
- ranlib = rs["config.bin.ranlib"];
+ ranlib = rs["bin.ranlib.path"];
if (ranlib && ranlib->empty ()) // @@ BC LT [null].
ranlib = lookup ();
@@ -1481,11 +1483,12 @@ namespace build2
//
path relt (relative (t.path ()));
+ const process_path* ld (nullptr);
switch (lt)
{
case otype::a:
{
- args[0] = cast<path> (rs["config.bin.ar"]).string ().c_str ();
+ ld = &cast<process_path> (rs["bin.ar.path"]);
if (cid == "msvc")
{
@@ -1516,7 +1519,7 @@ namespace build2
{
// Using link.exe directly.
//
- args[0] = cast<path> (rs["config.bin.ld"]).string ().c_str ();
+ ld = &cast<process_path> (rs["bin.ld.path"]);
args.push_back ("/NOLOGO");
if (lt == otype::s)
@@ -1608,7 +1611,7 @@ namespace build2
}
else
{
- args[0] = cast<path> (rs[config_x]).string ().c_str ();
+ ld = &cast<process_path> (rs[x_path]);
// Add the option that triggers building a shared library and take
// care of any extras (e.g., import library).
@@ -1639,6 +1642,8 @@ namespace build2
}
}
+ args[0] = ld->recall_string ();
+
for (target* pt: t.prerequisite_targets)
{
file* f;
@@ -1709,7 +1714,7 @@ namespace build2
//
bool filter (cid == "msvc" && lt != otype::a);
- process pr (args.data (), 0, (filter ? -1 : 2));
+ process pr (*ld, args.data (), 0, (filter ? -1 : 2));
if (filter)
{
@@ -1756,8 +1761,10 @@ namespace build2
if (ranlib)
{
+ const process_path& rl (cast<process_path> (ranlib));
+
const char* args[] = {
- cast<path> (ranlib).string ().c_str (),
+ rl.recall_string (),
relt.string ().c_str (),
nullptr};
@@ -1766,7 +1773,7 @@ namespace build2
try
{
- process pr (args);
+ process pr (rl, args);
if (!pr.wait ())
throw failed ();
diff --git a/build2/cc/module.cxx b/build2/cc/module.cxx
index d3849e1..4926fac 100644
--- a/build2/cc/module.cxx
+++ b/build2/cc/module.cxx
@@ -105,7 +105,7 @@ namespace build2
{
dr << x << ' ' << project (r) << '@' << r.out_path () << '\n'
- << " " << left << setw (11) << x << xc << '\n'
+ << " " << left << setw (11) << x << ci.path << '\n'
<< " id " << ci.id << '\n'
<< " version " << ci.version.string << '\n'
<< " major " << ci.version.major << '\n'
@@ -129,6 +129,7 @@ namespace build2
}
}
+ r.assign (x_path) = move (ci.path);
r.assign (x_id) = ci.id.string ();
r.assign (x_id_type) = move (ci.id.type);
r.assign (x_id_variant) = move (ci.id.variant);
diff --git a/build2/cc/msvc.cxx b/build2/cc/msvc.cxx
index 84020d0..84f5853 100644
--- a/build2/cc/msvc.cxx
+++ b/build2/cc/msvc.cxx
@@ -105,7 +105,7 @@ namespace build2
// Return otype::e if it is neither (which we quietly ignore).
//
static otype
- library_type (const path& ld, const path& l)
+ library_type (const process_path& ld, const path& l)
{
// The are several reasonably reliable methods to tell whether it is a
// static or import library. One is lib.exe /LIST -- if there aren't any
@@ -125,7 +125,7 @@ namespace build2
// for libraries then we have bin.ld. So we will use the link.exe /DUMP
// /ARCHIVEMEMBERS.
//
- const char* args[] = {ld.string ().c_str (),
+ const char* args[] = {ld.recall_string (),
"/DUMP", // Must come first.
"/NOLOGO",
"/ARCHIVEMEMBERS",
@@ -135,7 +135,7 @@ namespace build2
// Link.exe seem to always dump everything to stdout but just in case
// redirect stderr to stdout.
//
- process pr (start_run (args, false));
+ process pr (run_start (ld, args, false));
bool obj (false), dll (false);
string s;
@@ -192,11 +192,11 @@ namespace build2
}
catch (const ifdstream::failure&)
{
- // Presumably the child process failed. Let finish_run() deal with
+ // Presumably the child process failed. Let run_finish() deal with
// that.
}
- if (!finish_run (args, false, pr, s))
+ if (!run_finish (args, false, pr, s))
return otype::e;
if (obj && dll)
@@ -217,7 +217,7 @@ namespace build2
template <typename T>
static T*
msvc_search_library (const char* mod,
- const path& ld,
+ const process_path& ld,
const dir_path& d,
prerequisite& p,
otype lt,
@@ -275,7 +275,7 @@ namespace build2
}
liba* link::
- msvc_search_static (const path& ld,
+ msvc_search_static (const process_path& ld,
const dir_path& d,
prerequisite& p) const
{
@@ -302,7 +302,7 @@ namespace build2
}
libs* link::
- msvc_search_shared (const path& ld,
+ msvc_search_shared (const process_path& ld,
const dir_path& d,
prerequisite& p) const
{
diff --git a/build2/cli/init.cxx b/build2/cli/init.cxx
index 467cf82..49947f5 100644
--- a/build2/cli/init.cxx
+++ b/build2/cli/init.cxx
@@ -58,7 +58,8 @@ namespace build2
v.insert<path> ("config.cli", true);
v.insert<strings> ("config.cli.options", true);
- v.insert<strings> ("cli.options");
+ v.insert<process_path> ("cli.path");
+ v.insert<strings> ("cli.options");
}
// Register target types.
@@ -91,20 +92,28 @@ namespace build2
{
// config.cli
//
+ process_path pp;
- // Return version or empty string if unable to execute (e.g., the cli
- // executable is not found).
+ // Return version or empty string if the cli executable is not found.
//
- auto test = [optional] (const path& cli) -> string
+ // @@ This needs some more thinking/cleanup. Specifically, what does
+ // it mean "cli not found"? Is it just not found in PATH? That plus
+ // was not able to execute (e.g., some shared libraries missing)?
+ // That plus cli that we found is something else?
+ //
+ auto test = [optional, &pp] (const path& cli) -> string
{
- const char* args[] = {cli.string ().c_str (), "--version", nullptr};
-
- if (verb >= 3)
- print_process (args);
+ const char* args[] = {nullptr, "--version", nullptr};
try
{
- process pr (args, 0, -1); // Open pipe to stdout.
+ pp = process::path_search (cli, true); // Can throw.
+ args[0] = pp.recall_string ();
+
+ if (verb >= 3)
+ print_process (args);
+
+ process pr (pp, args, 0, -1); // Open pipe to stdout.
try
{
@@ -146,7 +155,7 @@ namespace build2
// found). So it would be good to redirect child's STDERR.
//
if (!optional)
- error << "unable to execute " << cli << ": " << e.what ();
+ error << "unable to execute " << args[0] << ": " << e.what ();
if (e.child ())
exit (1);
@@ -216,9 +225,12 @@ namespace build2
if (unconf)
dr << " cli " << "not found, leaving unconfigured";
else
- dr << " cli " << cli << '\n'
+ dr << " cli " << pp << '\n'
<< " version " << ver;
}
+
+ if (!unconf)
+ rs.assign<process_path> ("cli.path") = move (pp);
}
// Nothing else to do if we are unconfigured.
diff --git a/build2/cxx/init.cxx b/build2/cxx/init.cxx
index 14134f1..7d64975 100644
--- a/build2/cxx/init.cxx
+++ b/build2/cxx/init.cxx
@@ -121,16 +121,17 @@ namespace build2
// Note: some overridable, some not.
//
- v.insert<path> ("config.cxx", true),
- v.insert<strings> ("config.cxx.poptions", true),
- v.insert<strings> ("config.cxx.coptions", true),
- v.insert<strings> ("config.cxx.loptions", true),
- v.insert<strings> ("config.cxx.libs", true),
-
- v.insert<strings> ("cxx.poptions"),
- v.insert<strings> ("cxx.coptions"),
- v.insert<strings> ("cxx.loptions"),
- v.insert<strings> ("cxx.libs"),
+ v.insert<path> ("config.cxx", true),
+ v.insert<strings> ("config.cxx.poptions", true),
+ v.insert<strings> ("config.cxx.coptions", true),
+ v.insert<strings> ("config.cxx.loptions", true),
+ v.insert<strings> ("config.cxx.libs", true),
+
+ v.insert<process_path> ("cxx.path"),
+ v.insert<strings> ("cxx.poptions"),
+ v.insert<strings> ("cxx.coptions"),
+ v.insert<strings> ("cxx.loptions"),
+ v.insert<strings> ("cxx.libs"),
v["cc.poptions"],
v["cc.coptions"],
diff --git a/build2/types b/build2/types
index e3aa1b1..df54860 100644
--- a/build2/types
+++ b/build2/types
@@ -127,8 +127,12 @@ namespace build2
// <butl/fdstream>
//
using butl::process;
+ using butl::process_path;
using butl::process_error;
+ ostream&
+ operator<< (ostream&, const process_path&); // Print as recall[@effect].
+
using butl::ifdstream;
using butl::ofdstream;
}
diff --git a/build2/utility b/build2/utility
index ee12545..3fb47b7 100644
--- a/build2/utility
+++ b/build2/utility
@@ -84,11 +84,23 @@ namespace build2
// diagnostics from the child process). Issue diagnostics and throw failed
// in case of an error.
//
+ process_path
+ run_search (const char*& args0);
+
+ process_path
+ run_search (const path&, bool init, const dir_path& fallback = dir_path ());
+
process
- start_run (const char* args[], bool error);
+ run_start (const process_path&, const char* args[], bool error);
+
+ inline process
+ run_start (const char* args[], bool error)
+ {
+ return run_start (run_search (args[0]), args, error);
+ }
bool
- finish_run (const char* args[], bool error, process&, const string&);
+ run_finish (const char* args[], bool error, process&, const string&);
// Start the process as above and then call the specified function on each
// trimmed line of the output until it returns a non-empty object T (tested
@@ -106,7 +118,8 @@ namespace build2
//
template <typename T>
T
- run (const char* args[],
+ run (const process_path&,
+ const char* args[],
T (*) (string&),
bool error = true,
bool ignore_exit = false,
@@ -114,6 +127,19 @@ namespace build2
template <typename T>
inline T
+ run (const char* args[],
+ T (*f) (string&),
+ bool error = true,
+ bool ignore_exit = false,
+ sha256* checksum = nullptr)
+ {
+ return run<T> (run_search (args[0]), args, f, error, ignore_exit, checksum);
+ }
+
+ // run <prog>
+ //
+ template <typename T>
+ inline T
run (const path& prog,
T (*f) (string&),
bool error = true,
@@ -126,6 +152,20 @@ namespace build2
template <typename T>
inline T
+ run (const process_path& pp,
+ T (*f) (string&),
+ bool error = true,
+ bool ignore_exit = false,
+ sha256* checksum = nullptr)
+ {
+ const char* args[] = {pp.recall_string (), nullptr};
+ return run<T> (pp, args, f, error, ignore_exit, checksum);
+ }
+
+ // run <prog> <arg>
+ //
+ template <typename T>
+ inline T
run (const path& prog,
const char* arg,
T (*f) (string&),
@@ -137,6 +177,19 @@ namespace build2
return run<T> (args, f, error, ignore_exit, checksum);
}
+ template <typename T>
+ inline T
+ run (const process_path& pp,
+ const char* arg,
+ T (*f) (string&),
+ bool error = true,
+ bool ignore_exit = false,
+ sha256* checksum = nullptr)
+ {
+ const char* args[] = {pp.recall_string (), arg, nullptr};
+ return run<T> (pp, args, f, error, ignore_exit, checksum);
+ }
+
// Empty string and path.
//
extern const std::string empty_string;
diff --git a/build2/utility.cxx b/build2/utility.cxx
index 8ebfe06..dc862c6 100644
--- a/build2/utility.cxx
+++ b/build2/utility.cxx
@@ -28,6 +28,17 @@ namespace build2
: p.representation ());
}
+ ostream&
+ operator<< (ostream& os, const process_path& p)
+ {
+ os << p.recall_string ();
+
+ if (!p.effect.empty ())
+ os << '@' << p.effect.string (); // Suppress relative().
+
+ return os;
+ }
+
//
// <build2/utility>
//
@@ -69,21 +80,47 @@ namespace build2
return l;
}
+ process_path
+ run_search (const char*& args0)
+ try
+ {
+ return process::path_search (args0);
+ }
+ catch (const process_error& e)
+ {
+ error << "unable to execute " << args0 << ": " << e.what ();
+ throw failed ();
+ }
+
+ process_path
+ run_search (const path& f, bool init, const dir_path& fallback)
+ try
+ {
+ return process::path_search (f, init, fallback);
+ }
+ catch (const process_error& e)
+ {
+ error << "unable execute " << f << ": " << e.what ();
+ throw failed ();
+ }
+
process
- start_run (const char* args[], bool err)
+ run_start (const process_path& pp, const char* args[], bool err)
{
+ assert (args[0] == pp.recall_string ());
+
if (verb >= 3)
print_process (args);
try
{
- return process (args, 0, -1, (err ? 2 : 1));
+ return process (pp, args, 0, -1, (err ? 2 : 1));
}
catch (const process_error& e)
{
if (e.child ())
{
- // Note: finish_run() expects this exact message.
+ // Note: run_finish() expects this exact message.
//
cerr << "unable to execute " << args[0] << ": " << e.what () << endl;
exit (1);
@@ -96,7 +133,7 @@ namespace build2
};
bool
- finish_run (const char* args[], bool err, process& pr, const string& l)
+ run_finish (const char* args[], bool err, process& pr, const string& l)
try
{
if (pr.wait ())
@@ -111,7 +148,7 @@ namespace build2
// want to let through is the inability to execute the program itself.
// We cannot reserve a special exit status to signal this so we will
// just have to compare the output. This particular situation will
- // result in a single error line printed by start_run() above.
+ // result in a single error line printed by run_start() above.
//
if (l.compare (0, 18, "unable to execute ") == 0)
fail << l;
diff --git a/build2/utility.txx b/build2/utility.txx
index 3e29db6..7256547 100644
--- a/build2/utility.txx
+++ b/build2/utility.txx
@@ -6,13 +6,14 @@ namespace build2
{
template <typename T>
T
- run (const char* args[],
+ run (const process_path& pp,
+ const char* args[],
T (*f) (string&),
bool err,
bool ignore_exit,
sha256* checksum)
{
- process pr (start_run (args, err));
+ process pr (run_start (pp, args, err));
T r;
string l; // Last line of output.
@@ -35,10 +36,10 @@ namespace build2
}
catch (const ifdstream::failure&)
{
- // Presumably the child process failed. Let finish_run() deal with that.
+ // Presumably the child process failed. Let run_finish() deal with that.
}
- if (!(finish_run (args, err, pr, l) || ignore_exit))
+ if (!(run_finish (args, err, pr, l) || ignore_exit))
r = T ();
return r;
diff --git a/build2/variable b/build2/variable
index d6be976..1248bd3 100644
--- a/build2/variable
+++ b/build2/variable
@@ -589,6 +589,28 @@ namespace build2
static const build2::value_type value_type;
};
+ // process_path
+ //
+ // Note that instances that we store always have non-empty recall and
+ // initial is its shallow copy.
+ //
+ template <>
+ struct value_traits<process_path>
+ {
+ static_assert (sizeof (process_path) <= value::size_, "insufficient space");
+
+ // This one is represented as a @-pair of names. As a result it cannot
+ // be stored in a container.
+ //
+ static process_path convert (name&&, name*);
+ static void assign (value&, process_path&&);
+ static int compare (const process_path&, const process_path&);
+ static bool empty (const process_path& x) {return x.empty ();}
+
+ static const char* const type_name;
+ static const build2::value_type value_type;
+ };
+
// vector<T>
//
template <typename T>
diff --git a/build2/variable.cxx b/build2/variable.cxx
index eec0573..54b61b7 100644
--- a/build2/variable.cxx
+++ b/build2/variable.cxx
@@ -662,6 +662,104 @@ namespace build2
&default_empty<name>
};
+ // process_path value
+ //
+ process_path value_traits<process_path>::
+ convert (name&& n, name* r)
+ {
+ path rp (move (n.dir));
+ if (rp.empty ())
+ rp = path (move (n.value));
+ else
+ rp /= n.value;
+
+ path ep;
+ if (r != nullptr)
+ {
+ ep = move (r->dir);
+ if (ep.empty ())
+ ep = path (move (r->value));
+ else
+ ep /= r->value;
+ }
+
+ process_path pp (nullptr, move (rp), move (ep));
+ pp.initial = pp.recall.string ().c_str ();
+ return pp;
+ }
+
+ void
+ process_path_copy_ctor (value& l, const value& r, bool m)
+ {
+ const auto& rhs (r.as<process_path> ());
+
+ if (m)
+ new (&l.data_) process_path (move (const_cast<process_path&> (rhs)));
+ else
+ {
+ auto& lhs (
+ *new (&l.data_) process_path (
+ nullptr, path (rhs.recall), path (rhs.effect)));
+ lhs.initial = lhs.recall.string ().c_str ();
+ }
+ }
+
+ void
+ process_path_copy_assign (value& l, const value& r, bool m)
+ {
+ auto& lhs (l.as<process_path> ());
+ const auto& rhs (r.as<process_path> ());
+
+ if (m)
+ lhs = move (const_cast<process_path&> (rhs));
+ else
+ {
+ lhs.recall = rhs.recall;
+ lhs.effect = rhs.effect;
+ lhs.initial = lhs.recall.string ().c_str ();
+ }
+ }
+
+ static names_view
+ process_path_reverse (const value& v, names& s)
+ {
+ auto& pp (v.as<process_path> ());
+ s.reserve (pp.effect.empty () ? 1 : 2);
+
+ s.push_back (name (pp.recall.directory (),
+ string (),
+ pp.recall.leaf ().string ()));
+
+ if (!pp.effect.empty ())
+ {
+ s.back ().pair = '@';
+ s.push_back (name (pp.effect.directory (),
+ string (),
+ pp.effect.leaf ().string ()));
+ }
+
+ return s;
+ }
+
+ const char* const value_traits<process_path>::type_name = "process_path";
+
+ const value_type value_traits<process_path>::value_type
+ {
+ type_name,
+ sizeof (process_path),
+ nullptr, // No base.
+ &default_dtor<process_path>,
+ &process_path_copy_ctor,
+ &process_path_copy_assign,
+ &simple_assign<process_path, true>, // Allow empty values.
+ nullptr, // Append not supported.
+ nullptr, // Prepend not supported.
+ &process_path_reverse,
+ nullptr, // No cast (cast data_ directly).
+ &simple_compare<process_path>,
+ &default_empty<process_path>
+ };
+
// variable_pool
//
const variable& variable_pool::
diff --git a/build2/variable.ixx b/build2/variable.ixx
index 4b699a6..393a796 100644
--- a/build2/variable.ixx
+++ b/build2/variable.ixx
@@ -476,6 +476,35 @@ namespace build2
return l.compare (r);
}
+ // process_path value
+ //
+ inline void value_traits<process_path>::
+ assign (value& v, process_path&& x)
+ {
+ // Convert the value to its "self-sufficient" form.
+ //
+ if (x.recall.empty ())
+ x.recall = path (x.initial);
+
+ x.initial = x.recall.string ().c_str ();
+
+ if (v)
+ v.as<process_path> () = move (x);
+ else
+ new (&v.data_) process_path (move (x));
+ }
+
+ inline int value_traits<process_path>::
+ compare (const process_path& x, const process_path& y)
+ {
+ int r (x.recall.compare (y.recall));
+
+ if (r == 0)
+ r = x.effect.compare (y.effect);
+
+ return r;
+ }
+
// vector<T> value
//
template <typename T>