From e58957d6491a08ec212958457c29a14eec787279 Mon Sep 17 00:00:00 2001 From: Boris Kolpackov Date: Fri, 11 Mar 2016 16:24:37 +0200 Subject: Implement --config-{guess,sub} options --- build2/b-options | 16 ++++++ build2/b-options.cxx | 24 ++++++++ build2/b-options.ixx | 24 ++++++++ build2/b.cli | 17 ++++++ build2/b.cxx | 1 - build2/buildfile | 114 ++++++++++++++++++------------------- build2/cli/module.cxx | 3 - build2/cli/rule.cxx | 2 - build2/context | 2 + build2/context.cxx | 53 ++++++++++-------- build2/cxx/compile.cxx | 2 - build2/cxx/guess.cxx | 139 ++++++++-------------------------------------- build2/cxx/link.cxx | 3 - build2/cxx/module.cxx | 17 +++++- build2/dist/operation.cxx | 3 - build2/install/rule.cxx | 1 - build2/test/rule.cxx | 2 - build2/types | 11 ++++ build2/types-parsers | 38 +++++++++++++ build2/types-parsers.cxx | 51 +++++++++++++++++ build2/utility | 56 +++++++++++++++++++ build2/utility.cxx | 58 ++++++++++++++++++- build2/utility.txx | 36 ++++++++++++ 23 files changed, 458 insertions(+), 215 deletions(-) create mode 100644 build2/types-parsers create mode 100644 build2/types-parsers.cxx create mode 100644 build2/utility.txx 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 // // 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 + { + "", + "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 + { + "", + "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. { "", 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 " \ +--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 -#include -#include - #include #include #include 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 -#include - #include #include #include diff --git a/build2/context b/build2/context index d5b55d3..b99ff73 100644 --- a/build2/context +++ b/build2/context @@ -11,6 +11,7 @@ #include #include +#include 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 (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 // numeric_limits #include // exit() -#include -#include #include #include 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 #include // strlen() -#include // cerr - -#include -#include #include @@ -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 - 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 (args, f, &cs); + // Suppress all the compiler errors because we may be trying an + // unsupported option. + // + r = run (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 (args, f); + r = run (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 (args, f); + r = run (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 (targs.data (), f)); + string t (run (args.data (), f, false)); if (t.empty ()) { l5 ([&]{trace << cxx << " doesn's support -print-multiarch, " << "falling back to -dumpmachine";}); - targs[1] = "-dumpmachine"; - t = run (targs.data (), f); + args[1] = "-dumpmachine"; + t = run (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 (targs.data (), f)); + string t (run (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 (vargs, f); + s = run (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 (targs.data (), f)); + string t (run (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 (vargs, [] (string& l) {return string (move (l));}); + t = run (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 // exit() -#include -#include -#include #include #include 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 -#include #include -#include #include +#include #include #include @@ -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 (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 -#include -#include - #include #include #include 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 -#include #include #include 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 -#include - #include #include #include diff --git a/build2/types b/build2/types index 7c803a1..54056ba 100644 --- a/build2/types +++ b/build2/types @@ -21,6 +21,8 @@ #include #include +#include +#include #include #include @@ -99,6 +101,15 @@ namespace build2 // using butl::sha256; + // + // + // + using butl::process; + using butl::process_error; + + using butl::ifdstream; + using butl::ofdstream; + // // } 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 + +namespace build2 +{ + namespace cl + { + class scanner; + + template + struct parser; + + template <> + struct parser + { + static void + parse (path&, bool&, scanner&); + }; + + template <> + struct parser + { + 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 + +#include // build2::cl namespace + +namespace build2 +{ + namespace cl + { + template + 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:: + parse (path& x, bool& xs, scanner& s) + { + xs = true; + parse_path (x, s); + } + + void parser:: + 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 + T + run (const char* const* args, + T (*) (string&), + bool error = true, + sha256* checksum = nullptr); + + template + 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 (args, f, error, checksum); + } + + template + 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 (args, f, error, checksum); + } + // Empty string and path. // extern const std::string empty_string; @@ -197,5 +252,6 @@ namespace build2 } #include +#include #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 -#include // strtol() +#include // strtol() +#include // cerr #include #include @@ -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 + 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; + } +} -- cgit v1.1