aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--build2/b-options16
-rw-r--r--build2/b-options.cxx24
-rw-r--r--build2/b-options.ixx24
-rw-r--r--build2/b.cli17
-rw-r--r--build2/b.cxx1
-rw-r--r--build2/buildfile114
-rw-r--r--build2/cli/module.cxx3
-rw-r--r--build2/cli/rule.cxx2
-rw-r--r--build2/context2
-rw-r--r--build2/context.cxx53
-rw-r--r--build2/cxx/compile.cxx2
-rw-r--r--build2/cxx/guess.cxx139
-rw-r--r--build2/cxx/link.cxx3
-rw-r--r--build2/cxx/module.cxx17
-rw-r--r--build2/dist/operation.cxx3
-rw-r--r--build2/install/rule.cxx1
-rw-r--r--build2/test/rule.cxx2
-rw-r--r--build2/types11
-rw-r--r--build2/types-parsers38
-rw-r--r--build2/types-parsers.cxx51
-rw-r--r--build2/utility56
-rw-r--r--build2/utility.cxx58
-rw-r--r--build2/utility.txx36
23 files changed, 458 insertions, 215 deletions
diff --git a/build2/b-options b/build2/b-options
index e2bd37f..602ea64 100644
--- a/build2/b-options
+++ b/build2/b-options
@@ -407,6 +407,18 @@ namespace build2
bool
verbose_specified () const;
+ const path&
+ config_guess () const;
+
+ bool
+ config_guess_specified () const;
+
+ const path&
+ config_sub () const;
+
+ bool
+ config_sub_specified () const;
+
const string&
pager () const;
@@ -448,6 +460,10 @@ namespace build2
bool q_;
uint16_t verbose_;
bool verbose_specified_;
+ path config_guess_;
+ bool config_guess_specified_;
+ path config_sub_;
+ bool config_sub_specified_;
string pager_;
bool pager_specified_;
strings pager_option_;
diff --git a/build2/b-options.cxx b/build2/b-options.cxx
index 5ad43f7..bbdbde9 100644
--- a/build2/b-options.cxx
+++ b/build2/b-options.cxx
@@ -6,6 +6,7 @@
// Begin prologue.
//
+#include <build2/types-parsers>
//
// End prologue.
@@ -572,6 +573,10 @@ namespace build2
q_ (),
verbose_ (1),
verbose_specified_ (false),
+ config_guess_ (),
+ config_guess_specified_ (false),
+ config_sub_ (),
+ config_sub_specified_ (false),
pager_ (),
pager_specified_ (false),
pager_option_ (),
@@ -672,6 +677,19 @@ namespace build2
<< " 6. Even more detailed information, including state dumps." << ::std::endl;
os << std::endl
+ << "\033[1m--config-guess\033[0m \033[4mpath\033[0m The path to the \033[1mconfig.guess(1)\033[0m script that should be used" << ::std::endl
+ << " to guess the host machine triplet. If this option is not" << ::std::endl
+ << " specified, then \033[1mb\033[0m will fall back on to using the target it" << ::std::endl
+ << " was built for as host." << ::std::endl;
+
+ os << std::endl
+ << "\033[1m--config-sub\033[0m \033[4mpath\033[0m The path to the \033[1mconfig.sub(1)\033[0m script that should be used" << ::std::endl
+ << " to canonicalize machine triplets. If this option is not" << ::std::endl
+ << " specified, then \033[1mb\033[0m will use its built-in canonicalization" << ::std::endl
+ << " support which should be sufficient for commonly-used" << ::std::endl
+ << " platforms." << ::std::endl;
+
+ os << std::endl
<< "\033[1m--pager\033[0m \033[4mpath\033[0m The pager program to be used to show long text. Commonly" << ::std::endl
<< " used pager programs are \033[1mless\033[0m and \033[1mmore\033[0m. You can also" << ::std::endl
<< " specify additional options that should be passed to the" << ::std::endl
@@ -714,6 +732,12 @@ namespace build2
_cli_options_map_["--verbose"] =
&::build2::cl::thunk< options, uint16_t, &options::verbose_,
&options::verbose_specified_ >;
+ _cli_options_map_["--config-guess"] =
+ &::build2::cl::thunk< options, path, &options::config_guess_,
+ &options::config_guess_specified_ >;
+ _cli_options_map_["--config-sub"] =
+ &::build2::cl::thunk< options, path, &options::config_sub_,
+ &options::config_sub_specified_ >;
_cli_options_map_["--pager"] =
&::build2::cl::thunk< options, string, &options::pager_,
&options::pager_specified_ >;
diff --git a/build2/b-options.ixx b/build2/b-options.ixx
index fb7df02..ed538c9 100644
--- a/build2/b-options.ixx
+++ b/build2/b-options.ixx
@@ -240,6 +240,30 @@ namespace build2
return this->verbose_specified_;
}
+ inline const path& options::
+ config_guess () const
+ {
+ return this->config_guess_;
+ }
+
+ inline bool options::
+ config_guess_specified () const
+ {
+ return this->config_guess_specified_;
+ }
+
+ inline const path& options::
+ config_sub () const
+ {
+ return this->config_sub_;
+ }
+
+ inline bool options::
+ config_sub_specified () const
+ {
+ return this->config_sub_specified_;
+ }
+
inline const string& options::
pager () const
{
diff --git a/build2/b.cli b/build2/b.cli
index 0fba654..9144acf 100644
--- a/build2/b.cli
+++ b/build2/b.cli
@@ -220,6 +220,23 @@ namespace build2
\li|Even more detailed information, including state dumps.||"
}
+ path --config-guess
+ {
+ "<path>",
+ "The path to the \cb{config.guess(1)} script that should be used to
+ guess the host machine triplet. If this option is not specified, then
+ \cb{b} will fall back on to using the target it was built for as host."
+ }
+
+ path --config-sub
+ {
+ "<path>",
+ "The path to the \cb{config.sub(1)} script that should be used to
+ canonicalize machine triplets. If this option is not specified, then
+ \cb{b} will use its built-in canonicalization support which should
+ be sufficient for commonly-used platforms."
+ }
+
string --pager // String to allow empty value.
{
"<path>",
diff --git a/build2/b.cxx b/build2/b.cxx
index 999fd44..50398a2 100644
--- a/build2/b.cxx
+++ b/build2/b.cxx
@@ -65,7 +65,6 @@ main (int argc, char* argv[])
// and buildspecs in any order (it is really handy to just add -v at the
// end of the command line).
//
- options ops;
strings vars;
string args;
try
diff --git a/build2/buildfile b/build2/buildfile
index 6c5f773..3081e61 100644
--- a/build2/buildfile
+++ b/build2/buildfile
@@ -4,60 +4,61 @@
import libs = libbutl%lib{butl}
-exe{b}: \
- {hxx ixx txx cxx}{ algorithm } \
- { cxx}{ b } \
- {hxx ixx cxx}{ b-options } \
- {hxx txx cxx}{ context } \
- {hxx cxx}{ depdb } \
- {hxx cxx}{ diagnostics } \
- {hxx cxx}{ dump } \
- {hxx ixx cxx}{ file } \
- {hxx cxx}{ lexer } \
- {hxx cxx}{ module } \
- {hxx cxx}{ name } \
- {hxx cxx}{ operation } \
- {hxx cxx}{ parser } \
- {hxx cxx}{ prerequisite } \
- {hxx cxx}{ rule } \
- {hxx }{ rule-map } \
- {hxx cxx}{ scope } \
- {hxx cxx}{ search } \
- {hxx cxx}{ spec } \
- {hxx ixx txx cxx}{ target } \
- {hxx }{ target-key } \
- {hxx }{ target-type } \
- {hxx cxx}{ token } \
- {hxx }{ types } \
- {hxx ixx cxx}{ utility } \
- {hxx ixx txx cxx}{ variable } \
- {hxx }{ version } \
- bin/{hxx cxx}{ module } \
- bin/{hxx cxx}{ rule } \
- bin/{hxx cxx}{ target } \
- cli/{hxx cxx}{ module } \
- cli/{hxx cxx}{ rule } \
- cli/{hxx cxx}{ target } \
- config/{hxx cxx}{ module } \
- config/{hxx cxx}{ operation } \
- config/{hxx txx cxx}{ utility } \
- cxx/{hxx cxx}{ compile } \
- cxx/{hxx cxx}{ guess } \
- cxx/{hxx cxx}{ install } \
- cxx/{hxx cxx}{ link } \
- cxx/{hxx cxx}{ module } \
- cxx/{hxx cxx}{ target } \
- cxx/{hxx txx cxx}{ utility } \
- dist/{hxx cxx}{ module } \
- dist/{hxx cxx}{ operation } \
- dist/{hxx cxx}{ rule } \
-install/{hxx cxx}{ module } \
-install/{hxx cxx}{ operation } \
-install/{hxx cxx}{ rule } \
-install/{hxx }{ utility } \
- test/{hxx cxx}{ module } \
- test/{hxx cxx}{ operation } \
- test/{hxx cxx}{ rule } \
+exe{b}: \
+ {hxx ixx txx cxx}{ algorithm } \
+ { cxx}{ b } \
+ {hxx ixx cxx}{ b-options } \
+ {hxx txx cxx}{ context } \
+ {hxx cxx}{ depdb } \
+ {hxx cxx}{ diagnostics } \
+ {hxx cxx}{ dump } \
+ {hxx ixx cxx}{ file } \
+ {hxx cxx}{ lexer } \
+ {hxx cxx}{ module } \
+ {hxx cxx}{ name } \
+ {hxx cxx}{ operation } \
+ {hxx cxx}{ parser } \
+ {hxx cxx}{ prerequisite } \
+ {hxx cxx}{ rule } \
+ {hxx }{ rule-map } \
+ {hxx cxx}{ scope } \
+ {hxx cxx}{ search } \
+ {hxx cxx}{ spec } \
+ {hxx ixx txx cxx}{ target } \
+ {hxx }{ target-key } \
+ {hxx }{ target-type } \
+ {hxx cxx}{ token } \
+ {hxx }{ types } \
+ {hxx cxx}{ types-parsers } \
+ {hxx ixx txx cxx}{ utility } \
+ {hxx ixx txx cxx}{ variable } \
+ {hxx }{ version } \
+ bin/{hxx cxx}{ module } \
+ bin/{hxx cxx}{ rule } \
+ bin/{hxx cxx}{ target } \
+ cli/{hxx cxx}{ module } \
+ cli/{hxx cxx}{ rule } \
+ cli/{hxx cxx}{ target } \
+ config/{hxx cxx}{ module } \
+ config/{hxx cxx}{ operation } \
+ config/{hxx txx cxx}{ utility } \
+ cxx/{hxx cxx}{ compile } \
+ cxx/{hxx cxx}{ guess } \
+ cxx/{hxx cxx}{ install } \
+ cxx/{hxx cxx}{ link } \
+ cxx/{hxx cxx}{ module } \
+ cxx/{hxx cxx}{ target } \
+ cxx/{hxx txx cxx}{ utility } \
+ dist/{hxx cxx}{ module } \
+ dist/{hxx cxx}{ operation } \
+ dist/{hxx cxx}{ rule } \
+install/{hxx cxx}{ module } \
+install/{hxx cxx}{ operation } \
+install/{hxx cxx}{ rule } \
+install/{hxx }{ utility } \
+ test/{hxx cxx}{ module } \
+ test/{hxx cxx}{ operation } \
+ test/{hxx cxx}{ rule } \
$libs
# Pass our compiler target to be used as build2 host.
@@ -81,8 +82,9 @@ if! $cli.loaded
{hxx ixx cxx}{b-options}: cli{b}
cli.options += -I $src_root --include-with-brackets --include-prefix build2 \
---guard-prefix BUILD2 --cli-namespace build2::cl --generate-file-scanner \
---generate-parse --generate-specifier
+--guard-prefix BUILD2 --cxx-prologue "#include <build2/types-parsers>" \
+--cli-namespace build2::cl --generate-file-scanner --generate-parse \
+--generate-specifier
# Usage options.
#
diff --git a/build2/cli/module.cxx b/build2/cli/module.cxx
index 018947a..8ec2db9 100644
--- a/build2/cli/module.cxx
+++ b/build2/cli/module.cxx
@@ -4,9 +4,6 @@
#include <build2/cli/module>
-#include <butl/process>
-#include <butl/fdstream>
-
#include <build2/scope>
#include <build2/target>
#include <build2/variable>
diff --git a/build2/cli/rule.cxx b/build2/cli/rule.cxx
index 82217c7..bf52993 100644
--- a/build2/cli/rule.cxx
+++ b/build2/cli/rule.cxx
@@ -4,8 +4,6 @@
#include <build2/cli/rule>
-#include <butl/process>
-
#include <build2/scope>
#include <build2/target>
#include <build2/context>
diff --git a/build2/context b/build2/context
index d5b55d3..b99ff73 100644
--- a/build2/context
+++ b/build2/context
@@ -11,6 +11,7 @@
#include <build2/utility>
#include <build2/operation>
+#include <build2/b-options>
namespace build2
{
@@ -19,6 +20,7 @@ namespace build2
extern dir_path work;
extern dir_path home;
+ extern options ops;
extern string_pool extension_pool;
extern string_pool project_name_pool;
diff --git a/build2/context.cxx b/build2/context.cxx
index 5b2b761..0fc3bd6 100644
--- a/build2/context.cxx
+++ b/build2/context.cxx
@@ -18,6 +18,7 @@ namespace build2
{
dir_path work;
dir_path home;
+ options ops;
string_pool extension_pool;
string_pool project_name_pool;
@@ -95,33 +96,41 @@ namespace build2
// approximation/fallback since most of the time we are interested in just
// the target class (e.g., linux, windows, macosx).
//
+ {
#ifndef BUILD2_HOST_TRIPLET
#error BUILD2_HOST_TRIPLET is not defined
#endif
- try
- {
- string canon;
- triplet t (BUILD2_HOST_TRIPLET, canon);
+ // Did the user ask us to use config.guess?
+ //
+ string orig (
+ ops.config_guess_specified ()
+ ? run<string> (ops.config_guess (), [] (string& l) {return move (l);})
+ : BUILD2_HOST_TRIPLET);
- l5 ([&]{trace << "canonical host: '" << canon << "'; "
- << "class: " << t.class_;});
+ l5 ([&]{trace << "original host: '" << orig << "'";});
- // Enter as build.host.{cpu,vendor,system,version,class}.
- //
- gs.assign ("build.host", string_type) = move (canon);
- gs.assign ("build.host.cpu", string_type) = move (t.cpu);
- gs.assign ("build.host.vendor", string_type) = move (t.vendor);
- gs.assign ("build.host.system", string_type) = move (t.system);
- gs.assign ("build.host.version", string_type) = move (t.version);
- gs.assign ("build.host.class", string_type) = move (t.class_);
- }
- catch (const invalid_argument& e)
- {
- // This is where we could suggest that the user specifies --config-guess
- // to help us out.
- //
- fail << "unable to parse build host '" << BUILD2_HOST_TRIPLET << "': "
- << e.what ();
+ try
+ {
+ string canon;
+ triplet t (orig, canon);
+
+ l5 ([&]{trace << "canonical host: '" << canon << "'; "
+ << "class: " << t.class_;});
+
+ // Enter as build.host.{cpu,vendor,system,version,class}.
+ //
+ gs.assign ("build.host", string_type) = move (canon);
+ gs.assign ("build.host.cpu", string_type) = move (t.cpu);
+ gs.assign ("build.host.vendor", string_type) = move (t.vendor);
+ gs.assign ("build.host.system", string_type) = move (t.system);
+ gs.assign ("build.host.version", string_type) = move (t.version);
+ gs.assign ("build.host.class", string_type) = move (t.class_);
+ }
+ catch (const invalid_argument& e)
+ {
+ fail << "unable to parse build host '" << orig << "': " << e.what () <<
+ info << "consider using the --config-guess option";
+ }
}
// Register builtin target types.
diff --git a/build2/cxx/compile.cxx b/build2/cxx/compile.cxx
index 3cd4b9d..b15944c 100644
--- a/build2/cxx/compile.cxx
+++ b/build2/cxx/compile.cxx
@@ -8,8 +8,6 @@
#include <limits> // numeric_limits
#include <cstdlib> // exit()
-#include <butl/process>
-#include <butl/fdstream>
#include <butl/path-map>
#include <build2/depdb>
diff --git a/build2/cxx/guess.cxx b/build2/cxx/guess.cxx
index 1f5fc62..90bf693 100644
--- a/build2/cxx/guess.cxx
+++ b/build2/cxx/guess.cxx
@@ -5,10 +5,6 @@
#include <build2/cxx/guess>
#include <cstring> // strlen()
-#include <iostream> // cerr
-
-#include <butl/process>
-#include <butl/fdstream>
#include <build2/diagnostics>
@@ -85,90 +81,6 @@ namespace build2
return "";
}
- // Start a process redirecting STDOUT and STDERR to a pipe.
- //
- static process
- start (const char* const* args)
- {
- if (verb >= 3)
- print_process (args);
-
- try
- {
- return process (args, 0, -1, 1);
- }
- catch (const process_error& e)
- {
- if (e.child ())
- {
- // Note: run() below relies on this exact message.
- //
- cerr << "unable to execute " << args[0] << ": " << e.what () << endl;
- exit (1);
- }
- else
- error << "unable to execute " << args[0] << ": " << e.what ();
-
- throw failed ();
- }
- };
-
- // Run the compiler with the specified option and then call the predicate
- // function on each line of the output until it returns a non-empty object
- // T (tested with T::empty()) which is then returned to the caller.
- //
- // The predicate can move the value out of the passed string but only in
- // case of a match (so that any diagnostics lines are left intact).
- //
- // If checksum is not NULL, then feed it the content of each line.
- //
- template <typename T>
- static T
- run (const char* const* args, T (*f) (string&), sha256* checksum = nullptr)
- try
- {
- process pr (start (args));
- ifdstream is (pr.in_ofd);
-
- T r;
-
- string l; // Last line of output.
- while (is.peek () != ifdstream::traits_type::eof () && getline (is, l))
- {
- trim (l);
-
- if (checksum != nullptr)
- checksum->append (l);
-
- if (r.empty ())
- r = f (l);
- }
-
- is.close (); // Don't block.
-
- if (!pr.wait ())
- {
- // While we want to suppress all the compiler errors because we may be
- // trying unsupported options, one error that we want to let through
- // is the inability to execute the compiler 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() above.
- //
- if (l.compare (0, 18, "unable to execute ") == 0)
- fail << l;
-
- r = T ();
- }
-
- return r;
- }
- catch (const process_error& e)
- {
- error << "unable to execute " << args[0] << ": " << e.what ();
- throw failed ();
- }
-
// Guess the compiler type and variant by running it. If the pre argument
// is not empty, then only "confirm" the pre-guess. Return empty result if
// unable to guess.
@@ -190,7 +102,6 @@ namespace build2
tracer trace ("cxx::guess");
guess_result r;
- const char* args[] = {cxx.string ().c_str (), nullptr, nullptr};
// Start with -v. This will cover gcc and clang.
//
@@ -282,8 +193,10 @@ namespace build2
//
sha256 cs;
- args[1] = "-v";
- r = run<guess_result> (args, f, &cs);
+ // Suppress all the compiler errors because we may be trying an
+ // unsupported option.
+ //
+ r = run<guess_result> (cxx, "-v", f, false, &cs);
if (!r.empty ())
r.checksum = cs.string ();
@@ -310,8 +223,7 @@ namespace build2
return guess_result ();
};
- args[1] = "--version";
- r = run<guess_result> (args, f);
+ r = run<guess_result> (cxx, "--version", f, false);
}
// Finally try to run it without any options to detect msvc.
@@ -343,8 +255,7 @@ namespace build2
return guess_result ();
};
- args[1] = nullptr;
- r = run<guess_result> (args, f);
+ r = run<guess_result> (cxx, f, false);
}
if (!r.empty ())
@@ -446,24 +357,24 @@ namespace build2
// multi-arch support), then use the result. Otherwise, fallback to
// -dumpmachine (older gcc or not multi-arch).
//
- cstrings targs {cxx.string ().c_str (), "-print-multiarch"};
- append_options (targs, coptions);
- targs.push_back (nullptr);
+ cstrings args {cxx.string ().c_str (), "-print-multiarch"};
+ append_options (args, coptions);
+ args.push_back (nullptr);
// The output of both -print-multiarch and -dumpmachine is a single line
// containing just the target triplet.
//
- auto f = [] (string& l) {return string (move (l));};
+ auto f = [] (string& l) {return move (l);};
- string t (run<string> (targs.data (), f));
+ string t (run<string> (args.data (), f, false));
if (t.empty ())
{
l5 ([&]{trace << cxx << " doesn's support -print-multiarch, "
<< "falling back to -dumpmachine";});
- targs[1] = "-dumpmachine";
- t = run<string> (targs.data (), f);
+ args[1] = "-dumpmachine";
+ t = run<string> (args.data (), f);
}
if (t.empty ())
@@ -549,16 +460,14 @@ namespace build2
// Unlike gcc, clang doesn't have -print-multiarch. Its -dumpmachine,
// however, respects the compile options (e.g., -m32).
//
- cstrings targs {cxx.string ().c_str (), "-dumpmachine"};
- append_options (targs, coptions);
- targs.push_back (nullptr);
+ cstrings args {cxx.string ().c_str (), "-dumpmachine"};
+ append_options (args, coptions);
+ args.push_back (nullptr);
// The output of -dumpmachine is a single line containing just the
// target triplet.
//
- auto f = [] (string& l) {return string (move (l));};
-
- string t (run<string> (targs.data (), f));
+ string t (run<string> (args.data (), [] (string& l) {return move (l);}));
if (t.empty ())
fail << "unable to extract target architecture from " << cxx
@@ -611,8 +520,7 @@ namespace build2
: string ();
};
- const char* vargs[] = {cxx.string ().c_str (), "-V", nullptr};
- s = run<string> (vargs, f);
+ s = run<string> (cxx, "-V", f);
if (s.empty ())
fail << "unable to extract signature from " << cxx << " -V output";
@@ -695,11 +603,11 @@ namespace build2
// "Intel(R)" "64"
// "Intel(R)" "MIC" (-dumpmachine says: x86_64-k1om-linux)
//
- cstrings targs {cxx.string ().c_str (), "-V"};
- append_options (targs, coptions);
- targs.push_back (nullptr);
+ cstrings args {cxx.string ().c_str (), "-V"};
+ append_options (args, coptions);
+ args.push_back (nullptr);
- string t (run<string> (targs.data (), f));
+ string t (run<string> (args.data (), f));
if (t.empty ())
fail << "unable to extract target architecture from " << cxx
@@ -741,8 +649,7 @@ namespace build2
// on which we are running), who knows what will happen in the future.
// So instead we are going to use -dumpmachine and substitute the CPU.
//
- vargs[1] = "-dumpmachine";
- t = run<string> (vargs, [] (string& l) {return string (move (l));});
+ t = run<string> (cxx, "-dumpmachine", [] (string& l) {return move (l);});
if (t.empty ())
fail << "unable to extract target architecture from " << cxx
diff --git a/build2/cxx/link.cxx b/build2/cxx/link.cxx
index 16948b2..fb93cbb 100644
--- a/build2/cxx/link.cxx
+++ b/build2/cxx/link.cxx
@@ -6,9 +6,6 @@
#include <cstdlib> // exit()
-#include <butl/process>
-#include <butl/fdstream>
-#include <butl/optional>
#include <butl/path-map>
#include <butl/filesystem>
diff --git a/build2/cxx/module.cxx b/build2/cxx/module.cxx
index 9ebcc52..8e83a9b 100644
--- a/build2/cxx/module.cxx
+++ b/build2/cxx/module.cxx
@@ -4,11 +4,10 @@
#include <build2/cxx/module>
-#include <butl/process>
#include <butl/triplet>
-#include <butl/fdstream>
#include <build2/scope>
+#include <build2/context>
#include <build2/diagnostics>
#include <build2/config/utility>
@@ -213,6 +212,17 @@ namespace build2
// Split/canonicalize the target.
//
+
+ // Did the user ask us to use config.sub?
+ //
+ if (ops.config_sub_specified ())
+ {
+ ci.target = run<string> (ops.config_sub (),
+ ci.target.c_str (),
+ [] (string& l) {return move (l);});
+ l5 ([&]{trace << "config.sub target: '" << ci.target << "'";});
+ }
+
try
{
string canon;
@@ -236,7 +246,8 @@ namespace build2
// --config-sub to help us out.
//
fail << "unable to parse compiler target '" << ci.target << "': "
- << e.what ();
+ << e.what () <<
+ info << "consider using the --config-sub option";
}
}
diff --git a/build2/dist/operation.cxx b/build2/dist/operation.cxx
index 4105110..d1c2266 100644
--- a/build2/dist/operation.cxx
+++ b/build2/dist/operation.cxx
@@ -4,9 +4,6 @@
#include <build2/dist/operation>
-#include <butl/process>
-#include <butl/filesystem>
-
#include <build2/file>
#include <build2/dump>
#include <build2/scope>
diff --git a/build2/install/rule.cxx b/build2/install/rule.cxx
index f4fe164..6933828 100644
--- a/build2/install/rule.cxx
+++ b/build2/install/rule.cxx
@@ -4,7 +4,6 @@
#include <build2/install/rule>
-#include <butl/process>
#include <butl/filesystem>
#include <build2/scope>
diff --git a/build2/test/rule.cxx b/build2/test/rule.cxx
index 403499f..39b7d01 100644
--- a/build2/test/rule.cxx
+++ b/build2/test/rule.cxx
@@ -4,8 +4,6 @@
#include <build2/test/rule>
-#include <butl/process>
-
#include <build2/scope>
#include <build2/target>
#include <build2/algorithm>
diff --git a/build2/types b/build2/types
index 7c803a1..54056ba 100644
--- a/build2/types
+++ b/build2/types
@@ -21,6 +21,8 @@
#include <butl/path>
#include <butl/sha256>
+#include <butl/process>
+#include <butl/fdstream>
#include <butl/optional>
#include <butl/timestamp>
@@ -99,6 +101,15 @@ namespace build2
//
using butl::sha256;
+ // <butl/process>
+ // <butl/fdstream>
+ //
+ using butl::process;
+ using butl::process_error;
+
+ using butl::ifdstream;
+ using butl::ofdstream;
+
// <build2/name>
//
}
diff --git a/build2/types-parsers b/build2/types-parsers
new file mode 100644
index 0000000..735b39a
--- /dev/null
+++ b/build2/types-parsers
@@ -0,0 +1,38 @@
+// file : build2/types-parsers -*- C++ -*-
+// copyright : Copyright (c) 2014-2016 Code Synthesis Ltd
+// license : MIT; see accompanying LICENSE file
+
+// CLI parsers, included into the generated source files.
+//
+
+#ifndef BUILD2_TYPES_PARSERS
+#define BUILD2_TYPES_PARSERS
+
+#include <build2/types>
+
+namespace build2
+{
+ namespace cl
+ {
+ class scanner;
+
+ template <typename T>
+ struct parser;
+
+ template <>
+ struct parser<path>
+ {
+ static void
+ parse (path&, bool&, scanner&);
+ };
+
+ template <>
+ struct parser<dir_path>
+ {
+ static void
+ parse (dir_path&, bool&, scanner&);
+ };
+ }
+}
+
+#endif // BUILD2_TYPES_PARSERS
diff --git a/build2/types-parsers.cxx b/build2/types-parsers.cxx
new file mode 100644
index 0000000..46fb8a1
--- /dev/null
+++ b/build2/types-parsers.cxx
@@ -0,0 +1,51 @@
+// file : build2/types-parsers.cxx -*- C++ -*-
+// copyright : Copyright (c) 2014-2016 Code Synthesis Ltd
+// license : MIT; see accompanying LICENSE file
+
+#include <build2/types-parsers>
+
+#include <build2/b-options> // build2::cl namespace
+
+namespace build2
+{
+ namespace cl
+ {
+ template <typename T>
+ static void
+ parse_path (T& x, scanner& s)
+ {
+ const char* o (s.next ());
+
+ if (!s.more ())
+ throw missing_value (o);
+
+ const char* v (s.next ());
+
+ try
+ {
+ x = T (v);
+
+ if (x.empty ())
+ throw invalid_value (o, v);
+ }
+ catch (const invalid_path&)
+ {
+ throw invalid_value (o, v);
+ }
+ }
+
+ void parser<path>::
+ parse (path& x, bool& xs, scanner& s)
+ {
+ xs = true;
+ parse_path (x, s);
+ }
+
+ void parser<dir_path>::
+ parse (dir_path& x, bool& xs, scanner& s)
+ {
+ xs = true;
+ parse_path (x, s);
+ }
+ }
+}
diff --git a/build2/utility b/build2/utility
index 47eaf7d..0c442b0 100644
--- a/build2/utility
+++ b/build2/utility
@@ -67,6 +67,61 @@ namespace build2
next_word (const string&, size_t n, size_t& b, size_t& e,
char d1 = ' ', char d2 = '\0');
+ // Basic process utilities.
+ //
+
+ // Start a process with the specified arguments printing the command at
+ // verbosity level 3 and higher. Redirect STDOUT to a pipe. If error is
+ // false, then redirecting STDERR to STDOUT (this can used to suppress
+ // diagnostics from the child process). Issue diagnostics and throw failed
+ // in case of an error.
+ //
+ process
+ start_run (const char* const* args, bool error);
+
+ bool
+ finish_run (const char* const* 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
+ // with T::empty()) which is then returned to the caller.
+ //
+ // The predicate can move the value out of the passed string but, if error
+ // is false, only in case of a "content match" (so that any diagnostics
+ // lines are left intact).
+ //
+ // If checksum is not NULL, then feed it the content of each line.
+ //
+ template <typename T>
+ T
+ run (const char* const* args,
+ T (*) (string&),
+ bool error = true,
+ sha256* checksum = nullptr);
+
+ template <typename T>
+ inline T
+ run (const path& prog,
+ T (*f) (string&),
+ bool error = true,
+ sha256* checksum = nullptr)
+ {
+ const char* args[] = {prog.string ().c_str (), nullptr};
+ return run<T> (args, f, error, checksum);
+ }
+
+ template <typename T>
+ inline T
+ run (const path& prog,
+ const char* arg,
+ T (*f) (string&),
+ bool error = true,
+ sha256* checksum = nullptr)
+ {
+ const char* args[] = {prog.string ().c_str (), arg, nullptr};
+ return run<T> (args, f, error, checksum);
+ }
+
// Empty string and path.
//
extern const std::string empty_string;
@@ -197,5 +252,6 @@ namespace build2
}
#include <build2/utility.ixx>
+#include <build2/utility.txx>
#endif // BUILD2_UTILITY
diff --git a/build2/utility.cxx b/build2/utility.cxx
index 1b8be65..4c1acd3 100644
--- a/build2/utility.cxx
+++ b/build2/utility.cxx
@@ -4,7 +4,8 @@
#include <build2/utility>
-#include <cstdlib> // strtol()
+#include <cstdlib> // strtol()
+#include <iostream> // cerr
#include <build2/context>
#include <build2/variable>
@@ -83,6 +84,61 @@ namespace build2
return l;
}
+ process
+ start_run (const char* const* args, bool err)
+ {
+ if (verb >= 3)
+ print_process (args);
+
+ try
+ {
+ return process (args, 0, -1, (err ? 2 : 1));
+ }
+ catch (const process_error& e)
+ {
+ if (e.child ())
+ {
+ // Note: finish_run() expects this exact message.
+ //
+ cerr << "unable to execute " << args[0] << ": " << e.what () << endl;
+ exit (1);
+ }
+ else
+ error << "unable to execute " << args[0] << ": " << e.what ();
+
+ throw failed ();
+ }
+ };
+
+ bool
+ finish_run (const char* const* args, bool err, process& pr, const string& l)
+ try
+ {
+ if (pr.wait ())
+ return true;
+
+ if (err)
+ // Assuming diagnostics has already been issued (to STDERR).
+ //
+ throw failed ();
+
+ // Even if the user asked to suppress diagnostiscs, one error that we
+ // 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 run_start() above.
+ //
+ if (l.compare (0, 18, "unable to execute ") == 0)
+ fail << l;
+
+ return false;
+ }
+ catch (const process_error& e)
+ {
+ error << "unable to execute " << args[0] << ": " << e.what ();
+ throw failed ();
+ }
+
const string empty_string;
const path empty_path;
const dir_path empty_dir_path;
diff --git a/build2/utility.txx b/build2/utility.txx
new file mode 100644
index 0000000..7848296
--- /dev/null
+++ b/build2/utility.txx
@@ -0,0 +1,36 @@
+// file : build2/utility.txx -*- C++ -*-
+// copyright : Copyright (c) 2014-2016 Code Synthesis Ltd
+// license : MIT; see accompanying LICENSE file
+
+namespace build2
+{
+ template <typename T>
+ T
+ run (const char* const* args, T (*f) (string&), bool err, sha256* checksum)
+ {
+ process pr (start_run (args, err));
+ ifdstream is (pr.in_ofd);
+
+ T r;
+
+ string l; // Last line of output.
+ while (is.peek () != ifdstream::traits_type::eof () && // Keep last line.
+ getline (is, l))
+ {
+ trim (l);
+
+ if (checksum != nullptr)
+ checksum->append (l);
+
+ if (r.empty ())
+ r = f (l);
+ }
+
+ is.close (); // Don't block.
+
+ if (!finish_run (args, err, pr, l))
+ r = T ();
+
+ return r;
+ }
+}