From c397402cba2f8bccaa9b8e63f7ae95f3540f54cd Mon Sep 17 00:00:00 2001 From: Boris Kolpackov Date: Mon, 21 Feb 2022 09:16:20 +0200 Subject: Factor process-wide initialization to init_process() function --- build2/b.cxx | 83 ++------------------------------ libbuild2/build/script/parser.test.cxx | 2 +- libbuild2/function.test.cxx | 2 +- libbuild2/make-parser.test.cxx | 2 +- libbuild2/test/script/parser.test.cxx | 2 +- libbuild2/utility.cxx | 87 ++++++++++++++++++++++++++++++++++ libbuild2/utility.hxx | 14 ++++++ tests/libbuild2/driver.cxx | 2 +- 8 files changed, 109 insertions(+), 85 deletions(-) diff --git a/build2/b.cxx b/build2/b.cxx index 304b58e..e636a9d 100644 --- a/build2/b.cxx +++ b/build2/b.cxx @@ -1,16 +1,6 @@ // file : build2/b.cxx -*- C++ -*- // license : MIT; see accompanying LICENSE file -#ifndef _WIN32 -# include // signal() -#else -# include -#endif - -#ifdef __GLIBCXX__ -# include -#endif - #include #include #include // cout @@ -171,59 +161,9 @@ main (int argc, char* argv[]) tracer trace ("main"); - int r (0); - - // This is a little hack to make out baseutils for Windows work when called - // with absolute path. In a nutshell, MSYS2's exec*p() doesn't search in the - // parent's executable directory, only in PATH. And since we are running - // without a shell (that would read /etc/profile which sets PATH to some - // sensible values), we are only getting Win32 PATH values. And MSYS2 /bin - // is not one of them. So what we are going to do is add /bin at the end of - // PATH (which will be passed as is by the MSYS2 machinery). This will make - // MSYS2 search in /bin (where our baseutils live). And for everyone else - // this should be harmless since it is not a valid Win32 path. - // -#ifdef _WIN32 - { - string mp; - if (optional p = getenv ("PATH")) - { - mp = move (*p); - mp += ';'; - } - mp += "/bin"; - - setenv ("PATH", mp); - } -#endif - - // A data race happens in the libstdc++ (as of GCC 7.2) implementation of - // the ctype::narrow() function (bug #77704). The issue is easily - // triggered by the testscript runner that indirectly (via regex) uses - // ctype facet of the global locale (and can potentially be triggered - // by other locale-aware code). We work around this by pre-initializing the - // global locale facet internal cache. - // -#ifdef __GLIBCXX__ - { - const ctype& ct (use_facet> (locale ())); - - for (size_t i (0); i != 256; ++i) - ct.narrow (static_cast (i), '\0'); - } -#endif - - // On POSIX ignore SIGPIPE which is signaled to a pipe-writing process if - // the pipe reading end is closed. Note that by default this signal - // terminates a process. Also note that there is no way to disable this - // behavior on a file descriptor basis or for the write() function call. - // -#ifndef _WIN32 - if (signal (SIGPIPE, SIG_IGN) == SIG_ERR) - fail << "unable to ignore broken pipe (SIGPIPE) signal: " - << system_error (errno, generic_category ()); // Sanitize. -#endif + init_process (); + int r (0); options ops; scheduler sched; @@ -314,32 +254,15 @@ main (int argc, char* argv[]) } } - // Initialize time conversion data that is used by localtime_r(). - // -#ifndef _WIN32 - tzset (); -#else - _tzset (); -#endif - // Initialize the global state. // init (&::terminate, argv[0], + ops.serial_stop (), cmdl.mtime_check, cmdl.config_sub, cmdl.config_guess); -#ifdef _WIN32 - // On Windows disable displaying error reporting dialog box for the - // current and child processes unless we are in the stop mode. Failed that - // we may have multiple dialog boxes popping up. - // - if (!ops.serial_stop ()) - SetErrorMode (SetErrorMode (0) | // Returns the current mode. - SEM_FAILCRITICALERRORS | SEM_NOGPFAULTERRORBOX); -#endif - // Load builtin modules. // load_builtin_module (&config::build2_config_load); diff --git a/libbuild2/build/script/parser.test.cxx b/libbuild2/build/script/parser.test.cxx index 63d41ff..5808015 100644 --- a/libbuild2/build/script/parser.test.cxx +++ b/libbuild2/build/script/parser.test.cxx @@ -176,7 +176,7 @@ namespace build2 // Fake build system driver, default verbosity. // init_diag (1); - init (nullptr, argv[0]); + init (nullptr, argv[0], true); // Serial execution. // diff --git a/libbuild2/function.test.cxx b/libbuild2/function.test.cxx index 0b3c922..f711d2f 100644 --- a/libbuild2/function.test.cxx +++ b/libbuild2/function.test.cxx @@ -44,7 +44,7 @@ namespace build2 // Fake build system driver, default verbosity. // init_diag (1); - init (nullptr, argv[0]); + init (nullptr, argv[0], true); // Serial execution. // diff --git a/libbuild2/make-parser.test.cxx b/libbuild2/make-parser.test.cxx index 5c57978..00a265a 100644 --- a/libbuild2/make-parser.test.cxx +++ b/libbuild2/make-parser.test.cxx @@ -22,7 +22,7 @@ namespace build2 // Fake build system driver, default verbosity. // init_diag (1); - init (nullptr, argv[0]); + init (nullptr, argv[0], true); path_name in (""); diff --git a/libbuild2/test/script/parser.test.cxx b/libbuild2/test/script/parser.test.cxx index eb4a59b..e0dd3d2 100644 --- a/libbuild2/test/script/parser.test.cxx +++ b/libbuild2/test/script/parser.test.cxx @@ -162,7 +162,7 @@ namespace build2 // Fake build system driver, default verbosity. // init_diag (1); - init (nullptr, argv[0]); + init (nullptr, argv[0], true); // Serial execution. // diff --git a/libbuild2/utility.cxx b/libbuild2/utility.cxx index 3f89def..31be3aa 100644 --- a/libbuild2/utility.cxx +++ b/libbuild2/utility.cxx @@ -3,8 +3,18 @@ #include +#ifndef _WIN32 +# include // signal() +#else +# include +#endif + #include // tzset() (POSIX), _tzset() (Windows) +#ifdef __GLIBCXX__ +# include +#endif + #include // ENOENT #include // strlen(), str[n]cmp() #include // cerr @@ -551,8 +561,73 @@ namespace build2 } void + init_process () + { + // This is a little hack to make out baseutils for Windows work when + // called with absolute path. In a nutshell, MSYS2's exec*p() doesn't + // search in the parent's executable directory, only in PATH. And since we + // are running without a shell (that would read /etc/profile which sets + // PATH to some sensible values), we are only getting Win32 PATH values. + // And MSYS2 /bin is not one of them. So what we are going to do is add + // /bin at the end of PATH (which will be passed as is by the MSYS2 + // machinery). This will make MSYS2 search in /bin (where our baseutils + // live). And for everyone else this should be harmless since it is not a + // valid Win32 path. + // +#ifdef _WIN32 + { + string mp; + if (optional p = getenv ("PATH")) + { + mp = move (*p); + mp += ';'; + } + mp += "/bin"; + + setenv ("PATH", mp); + } +#endif + + // On POSIX ignore SIGPIPE which is signaled to a pipe-writing process if + // the pipe reading end is closed. Note that by default this signal + // terminates a process. Also note that there is no way to disable this + // behavior on a file descriptor basis or for the write() function call. + // +#ifndef _WIN32 + if (signal (SIGPIPE, SIG_IGN) == SIG_ERR) + fail << "unable to ignore broken pipe (SIGPIPE) signal: " + << system_error (errno, generic_category ()); // Sanitize. +#endif + + // Initialize time conversion data that is used by localtime_r(). + // +#ifndef _WIN32 + tzset (); +#else + _tzset (); +#endif + + // A data race happens in the libstdc++ (as of GCC 7.2) implementation of + // the ctype::narrow() function (bug #77704). The issue is easily + // triggered by the testscript runner that indirectly (via regex) uses + // ctype facet of the global locale (and can potentially be + // triggered by other locale-aware code). We work around this by + // pre-initializing the global locale facet internal cache. + // +#ifdef __GLIBCXX__ + { + const ctype& ct (use_facet> (locale ())); + + for (size_t i (0); i != 256; ++i) + ct.narrow (static_cast (i), '\0'); + } +#endif + } + + void init (void (*t) (bool), const char* a0, + bool ss, optional mc, optional cs, optional cg) @@ -587,6 +662,18 @@ namespace build2 } script::regex::init (); + + if (!ss) + { +#ifdef _WIN32 + // On Windows disable displaying error reporting dialog box for the + // current and child processes unless we are in the stop mode. Failed + // that we may have multiple dialog boxes popping up. + // + SetErrorMode (SetErrorMode (0) | // Returns the current mode. + SEM_FAILCRITICALERRORS | SEM_NOGPFAULTERRORBOX); +#endif + } } optional diff --git a/libbuild2/utility.hxx b/libbuild2/utility.hxx index b62d2ab..c82dcc2 100644 --- a/libbuild2/utility.hxx +++ b/libbuild2/utility.hxx @@ -97,6 +97,19 @@ namespace build2 // using butl::path_pattern; + // Perform process-wide initializations/adjustments/workarounds. Should be + // called once early in main(). In particular, besides other things, this + // functions does the following: + // + // - Sets PATH to include baseutils /bin on Windows. + // + // - Ignores SIGPIPE. + // + // - Calls tzset(). + // + LIBBUILD2_SYMEXPORT void + init_process (); + // Diagnostics state (verbosity level, etc; see ). // // Note on naming of values (here and in the global state below) that come @@ -138,6 +151,7 @@ namespace build2 LIBBUILD2_SYMEXPORT void init (void (*terminate) (bool), const char* argv0, + bool serial_stop, optional mtime_check = nullopt, optional config_sub = nullopt, optional config_guess = nullopt); diff --git a/tests/libbuild2/driver.cxx b/tests/libbuild2/driver.cxx index dafb38e..6201a6c 100644 --- a/tests/libbuild2/driver.cxx +++ b/tests/libbuild2/driver.cxx @@ -32,7 +32,7 @@ main (int, char* argv[]) // Fake build system driver, default verbosity. // init_diag (1); - init (nullptr, argv[0]); + init (nullptr, argv[0], true); load_builtin_module (&config::build2_config_load); load_builtin_module (&dist::build2_dist_load); -- cgit v1.1