diff options
| author | Enji Cooper <ngie@FreeBSD.org> | 2026-09-21 02:29:32 +0000 |
|---|---|---|
| committer | Enji Cooper <ngie@FreeBSD.org> | 2026-09-21 02:29:32 +0000 |
| commit | f6ae5b18a6ed3fd628c48723e608fa3a9f5b1906 (patch) | |
| tree | ba23129aaf87f9b2a1ea30d573f114a1b499caf4 | |
| parent | 5cb96993447fc7bd26aba808fd6961652effa4bc (diff) | |
kyua: import kyua-0.15.0-rc1vendor/kyua/kyua-0.15.0-rc1vendor/kyua
This change adds kyua kyua-0.15.0-rc1 from [upstream][1].
The kyua-0.15.0-rc1 artifact was been verified by [SHA256 checksum][3].
This release incorporates items upstreamed from FreeBSD src and uses C++
attributes (`[[maybe_unused]]` and `[[noreturn]]`) instead of ad hoc
equivalents.
More information about the release (from a high level) can be found in
the [release notes][4].
Updated via [`update_kyua.sh`][4] `update_kyua.sh 0.15.0-rc1 kyua-0.15.0`.
1: https://github.com/freebsd/kyua/releases/download/kyua-0.15.0-rc1/kyua-0.15.0-rc1.tar.gz
2: https://github.com/freebsd/kyua/releases/download/kyua-0.15.0-rc1/kyua-0.15.0-rc1.tar.gz.sha256
3: https://github.com/freebsd/kyua/blob/kyua-0.15.0-rc1/NEWS.md
4: https://codeberg.org/ngie/freebsd-powertools:shell/update_kyua.sh@10a04edb
41 files changed, 325 insertions, 340 deletions
diff --git a/Doxyfile.in b/Doxyfile.in index e28d82f8999a..ac1dbf3398b6 100644 --- a/Doxyfile.in +++ b/Doxyfile.in @@ -45,7 +45,6 @@ JAVADOC_AUTOBRIEF = YES MACRO_EXPANSION = YES OUTPUT_DIRECTORY = @top_builddir@/api-docs OUTPUT_LANGUAGE = English -PREDEFINED += "KYUA_DEFS_NORETURN=" PREDEFINED += "KYUA_DEFS_FORMAT_PRINTF(x, y)=" PROJECT_NAME = "@PACKAGE_NAME@" PROJECT_NUMBER = @VERSION@ diff --git a/Makefile.in b/Makefile.in index cb8356fe8380..2dad43957ea9 100644 --- a/Makefile.in +++ b/Makefile.in @@ -3229,9 +3229,7 @@ AR = @AR@ ATF_CXX_CFLAGS = @ATF_CXX_CFLAGS@ ATF_CXX_LIBS = @ATF_CXX_LIBS@ ATF_SH = @ATF_SH@ -ATTRIBUTE_NORETURN = @ATTRIBUTE_NORETURN@ ATTRIBUTE_PURE = @ATTRIBUTE_PURE@ -ATTRIBUTE_UNUSED = @ATTRIBUTE_UNUSED@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ diff --git a/cli/cmd_debug.cpp b/cli/cmd_debug.cpp index fdf37abfedc7..f5e48572407e 100644 --- a/cli/cmd_debug.cpp +++ b/cli/cmd_debug.cpp @@ -28,7 +28,12 @@ #include "cli/cmd_debug.hpp" +extern "C" { +#include <unistd.h> +} + #include <cstdlib> +#include <cstring> #include <iostream> #include "cli/common.ipp" @@ -39,13 +44,20 @@ #include "utils/cmdline/parser.ipp" #include "utils/cmdline/ui.hpp" #include "utils/format/macros.hpp" +#include "utils/fs/path.hpp" +#include "utils/process/child.ipp" #include "utils/process/executor.hpp" +#include "utils/process/operations.hpp" +#include "utils/process/status.hpp" namespace cmdline = utils::cmdline; namespace config = utils::config; namespace executor = utils::process::executor; +namespace process = utils::process; using cli::cmd_debug; +using utils::process::args_vector; +using utils::process::child; namespace { @@ -63,6 +75,57 @@ const cmdline::bool_option pause_before_cleanup_option( "Pauses right before the test cleanup"); +static const char* DEFAULT_CMD = "$SHELL"; +const cmdline::string_option execute_option( + 'x', "execute", + "A command to run within the given execenv upon test failure", + "cmd", DEFAULT_CMD, true); + + +/// Functor to execute a program. +class execute { + const std::string& _cmd; + executor::exit_handle& _eh; + +public: + /// Constructor. + /// + /// \param program Program binary absolute path. + /// \param args Program arguments. + execute( + const std::string& cmd_, + executor::exit_handle& eh_) : + _cmd(cmd_), + _eh(eh_) + { + } + + /// Body of the subprocess. + void + operator()(void) + { + if (::chdir(_eh.work_directory().c_str()) == -1) { + std::cerr << "execute: chdir() errors: " + << std::strerror(errno) << ".\n"; + std::exit(EXIT_FAILURE); + } + + std::string program_path = "/bin/sh"; + const char* shell = std::getenv("SHELL"); + if (shell) + program_path = shell; + + args_vector av; + if (!(_cmd.empty() || _cmd == DEFAULT_CMD)) { + av.push_back("-c"); + av.push_back(_cmd); + } + + process::exec(utils::fs::path(program_path), av); + } +}; + + /// The debugger interface implementation. class dbg : public engine::debugger { /// Object to interact with the I/O of the program. @@ -104,6 +167,21 @@ public: } }; + void upon_test_failure( + const model::test_program_ptr&, + const model::test_case&, + optional< model::test_result >&, + executor::exit_handle& eh) const + { + if (!_cmdline.has_option(execute_option.long_name())) + return; + const std::string& cmd = _cmdline.get_option<cmdline::string_option>( + execute_option.long_name()); + std::unique_ptr< process::child > child = child::fork_interactive( + execute(cmd, eh)); + (void) child->wait(); + }; + }; @@ -128,6 +206,8 @@ cmd_debug::cmd_debug(void) : cli_command( add_option(cmdline::path_option( "stderr", "Where to direct the standard error of the test case", "path", "/dev/stderr")); + + add_option(execute_option); } @@ -152,7 +232,8 @@ cmd_debug::run(cmdline::ui* ui, const cmdline::parsed_cmdline& cmdline, engine::debugger_ptr debugger = nullptr; if (cmdline.has_option(pause_before_cleanup_upon_fail_option.long_name()) - || cmdline.has_option(pause_before_cleanup_option.long_name())) { + || cmdline.has_option(pause_before_cleanup_option.long_name()) + || cmdline.has_option(execute_option.long_name())) { debugger = std::shared_ptr< engine::debugger >(new dbg(ui, cmdline)); } diff --git a/configure b/configure index c60596311d2b..c0add152ddc6 100755 --- a/configure +++ b/configure @@ -711,9 +711,7 @@ build_cpu build LIBTOOL UMOUNT -ATTRIBUTE_UNUSED ATTRIBUTE_PURE -ATTRIBUTE_NORETURN HAVE_CXX20 ac_ct_AR AR @@ -1823,50 +1821,6 @@ fi } # ac_fn_cxx_try_link -# ac_fn_c_try_run LINENO -# ---------------------- -# Try to run conftest.$ac_ext, and return whether this succeeded. Assumes that -# executables *can* be run. -ac_fn_c_try_run () -{ - as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack - if { { ac_try="$ac_link" -case "(($ac_try" in - *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; - *) ac_try_echo=$ac_try;; -esac -eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" -printf '%s\n' "$ac_try_echo"; } >&5 - (eval "$ac_link") 2>&5 - ac_status=$? - printf '%s\n' "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 - test $ac_status = 0; } && { ac_try='./conftest$ac_exeext' - { { case "(($ac_try" in - *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; - *) ac_try_echo=$ac_try;; -esac -eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" -printf '%s\n' "$ac_try_echo"; } >&5 - (eval "$ac_try") 2>&5 - ac_status=$? - printf '%s\n' "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 - test $ac_status = 0; }; } -then : - ac_retval=0 -else case e in #( - e) printf '%s\n' "$as_me: program exited with status $ac_status" >&5 - printf '%s\n' "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 - - ac_retval=$ac_status ;; -esac -fi - rm -rf conftest.dSYM conftest_ipa8_conftest.oo - eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno - as_fn_set_status $ac_retval - -} # ac_fn_c_try_run - # ac_fn_c_check_header_compile LINENO HEADER VAR INCLUDES # ------------------------------------------------------- # Tests whether HEADER exists and can be compiled using the include files in @@ -2014,6 +1968,50 @@ printf '%s\n' "$ac_res" >&6; } } # ac_fn_c_check_func +# ac_fn_c_try_run LINENO +# ---------------------- +# Try to run conftest.$ac_ext, and return whether this succeeded. Assumes that +# executables *can* be run. +ac_fn_c_try_run () +{ + as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + if { { ac_try="$ac_link" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +printf '%s\n' "$ac_try_echo"; } >&5 + (eval "$ac_link") 2>&5 + ac_status=$? + printf '%s\n' "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } && { ac_try='./conftest$ac_exeext' + { { case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +printf '%s\n' "$ac_try_echo"; } >&5 + (eval "$ac_try") 2>&5 + ac_status=$? + printf '%s\n' "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; } +then : + ac_retval=0 +else case e in #( + e) printf '%s\n' "$as_me: program exited with status $ac_status" >&5 + printf '%s\n' "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_retval=$ac_status ;; +esac +fi + rm -rf conftest.dSYM conftest_ipa8_conftest.oo + eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno + as_fn_set_status $ac_retval + +} # ac_fn_c_try_run + # ac_fn_cxx_try_cpp LINENO # ------------------------ # Try to preprocess conftest.$ac_ext, and return whether this succeeded. @@ -7229,63 +7227,6 @@ ac_compiler_gnu=$ac_cv_c_compiler_gnu - { printf '%s\n' "$as_me:${as_lineno-$LINENO}: checking whether __attribute__((noreturn)) is supported" >&5 -printf %s "checking whether __attribute__((noreturn)) is supported... " >&6; } -if test ${kyua_cv_attribute_noreturn+y} -then : - printf %s "(cached) " >&6 -else case e in #( - e) - if test "$cross_compiling" = yes -then : - { { printf '%s\n' "$as_me:${as_lineno-$LINENO}: error: in '$ac_pwd':" >&5 -printf '%s\n' "$as_me: error: in '$ac_pwd':" >&2;} -as_fn_error $? "cannot run test program while cross compiling -See 'config.log' for more details" "$LINENO" 5; } -else case e in #( - e) cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ - -int -main (void) -{ - -#if ((__GNUC__ == 2 && __GNUC_MINOR__ >= 5) || __GNUC__ > 2) - return 0; -#else - return 1; -#endif - - ; - return 0; -} -_ACEOF -if ac_fn_c_try_run "$LINENO" -then : - kyua_cv_attribute_noreturn=yes -else case e in #( - e) kyua_cv_attribute_noreturn=no ;; -esac -fi -rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ - conftest.$ac_objext conftest.beam conftest.$ac_ext ;; -esac -fi - - ;; -esac -fi -{ printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: $kyua_cv_attribute_noreturn" >&5 -printf '%s\n' "$kyua_cv_attribute_noreturn" >&6; } - if test "${kyua_cv_attribute_noreturn}" = yes; then - attribute_value="__attribute__((noreturn))" - else - attribute_value="" - fi - ATTRIBUTE_NORETURN=${attribute_value} - - - { printf '%s\n' "$as_me:${as_lineno-$LINENO}: checking whether __attribute__((__pure__)) is supported" >&5 printf %s "checking whether __attribute__((__pure__)) is supported... " >&6; } if test ${kyua_cv_attribute_pure+y} @@ -7334,53 +7275,6 @@ printf '%s\n' "$kyua_cv_attribute_pure" >&6; } ATTRIBUTE_PURE=${attribute_value} - - { printf '%s\n' "$as_me:${as_lineno-$LINENO}: checking whether __attribute__((__unused__)) is supported" >&5 -printf %s "checking whether __attribute__((__unused__)) is supported... " >&6; } -if test ${kyua_cv_attribute_unused+y} -then : - printf %s "(cached) " >&6 -else case e in #( - e) - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ - -static void -function(int a __attribute__((__unused__))) -{ -} -int -main (void) -{ - - function(3); - return 0; - - ; - return 0; -} -_ACEOF -if ac_fn_c_try_compile "$LINENO" -then : - kyua_cv_attribute_unused=yes -else case e in #( - e) kyua_cv_attribute_unused=no ;; -esac -fi -rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext - ;; -esac -fi -{ printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: $kyua_cv_attribute_unused" >&5 -printf '%s\n' "$kyua_cv_attribute_unused" >&6; } - if test "${kyua_cv_attribute_unused}" = yes; then - attribute_value="__attribute__((__unused__))" - else - attribute_value="" - fi - ATTRIBUTE_UNUSED=${attribute_value} - - ac_header= ac_cache= for ac_item in $ac_header_c_list do diff --git a/configure.ac b/configure.ac index 220423cd9a94..f53955ecea19 100644 --- a/configure.ac +++ b/configure.ac @@ -113,9 +113,7 @@ AC_PROG_CXX AM_PROG_AR AX_CXX_COMPILE_STDCXX(20, noext, mandatory) KYUA_DEVELOPER_MODE([C++]) -KYUA_ATTRIBUTE_NORETURN KYUA_ATTRIBUTE_PURE -KYUA_ATTRIBUTE_UNUSED KYUA_FS_MODULE KYUA_GETOPT KYUA_LAST_SIGNO diff --git a/doc/kyuafile.5.in b/doc/kyuafile.5.in index 04480ecfe759..ae4b8ebfa3fe 100644 --- a/doc/kyuafile.5.in +++ b/doc/kyuafile.5.in @@ -499,7 +499,7 @@ plain_test_program{name='the_test', .Ss FreeBSD jail execution environment The following example configures the test to be run within a temporary jail with -.Xr vnet 9 +.Xr VNET 9 support and the permission to create raw sockets: .Bd -literal -offset indent syntax(2) diff --git a/engine/atf.hpp b/engine/atf.hpp index 34ddc2413235..b9821e5fb7a7 100644 --- a/engine/atf.hpp +++ b/engine/atf.hpp @@ -40,23 +40,24 @@ namespace engine { /// Implementation of the scheduler interface for atf test programs. class atf_interface : public engine::scheduler::interface { public: - void exec_list(const model::test_program&, - const utils::config::properties_map&) const UTILS_NORETURN; + void exec_list [[noreturn]] ( + const model::test_program&, + const utils::config::properties_map&) const; model::test_cases_map parse_list( const utils::optional< utils::process::status >&, const utils::fs::path&, const utils::fs::path&) const; - void exec_test(const model::test_program&, const std::string&, - const utils::config::properties_map&, - const utils::fs::path&) const - UTILS_NORETURN; + void exec_test [[noreturn]] ( + const model::test_program&, const std::string&, + const utils::config::properties_map&, + const utils::fs::path&) const; - void exec_cleanup(const model::test_program&, const std::string&, - const utils::config::properties_map&, - const utils::fs::path&) const - UTILS_NORETURN; + void exec_cleanup [[noreturn]]( + const model::test_program&, const std::string&, + const utils::config::properties_map&, + const utils::fs::path&) const; model::test_result compute_result( const utils::optional< utils::process::status >&, diff --git a/engine/debugger.hpp b/engine/debugger.hpp index 3c4d087f8ad0..ce87d41ed94d 100644 --- a/engine/debugger.hpp +++ b/engine/debugger.hpp @@ -58,6 +58,13 @@ public: const model::test_case&, optional< model::test_result >&, executor::exit_handle&) const = 0; + + /// Called upon test failure. + virtual void upon_test_failure( + const model::test_program_ptr&, + const model::test_case&, + optional< model::test_result >&, + executor::exit_handle&) const = 0; }; diff --git a/engine/execenv/execenv.hpp b/engine/execenv/execenv.hpp index a0e5bf043e53..d167196c7e98 100644 --- a/engine/execenv/execenv.hpp +++ b/engine/execenv/execenv.hpp @@ -83,7 +83,7 @@ public: /// scheduler::interface::exec_test() or exec_cleanup(). /// /// \param args The arguments to pass to the binary. - virtual void exec(const args_vector& args) const UTILS_NORETURN = 0; + virtual void exec [[noreturn]] (const args_vector& args) const = 0; }; diff --git a/engine/execenv/execenv_host.hpp b/engine/execenv/execenv_host.hpp index 2742366cfd6f..3276ad210574 100644 --- a/engine/execenv/execenv_host.hpp +++ b/engine/execenv/execenv_host.hpp @@ -53,7 +53,7 @@ public: void init() const; void cleanup() const; - void exec(const args_vector& args) const UTILS_NORETURN; + void exec [[noreturn]] (const args_vector& args) const; }; diff --git a/engine/googletest.cpp b/engine/googletest.cpp index d03a7bd82de7..e050410ba1f5 100644 --- a/engine/googletest.cpp +++ b/engine/googletest.cpp @@ -191,6 +191,7 @@ engine::googletest_interface::exec_test( F("--gtest_filter=%s") % (test_case_name) }; process::exec(test_program.absolute_path(), args); + __builtin_unreachable(); } diff --git a/engine/googletest.hpp b/engine/googletest.hpp index fb9a7e97adf2..9628f994a066 100644 --- a/engine/googletest.hpp +++ b/engine/googletest.hpp @@ -40,18 +40,19 @@ namespace engine { /// Implementation of the scheduler interface for googletest test programs. class googletest_interface : public engine::scheduler::interface { public: - void exec_list(const model::test_program&, - const utils::config::properties_map&) const UTILS_NORETURN; + void exec_list [[noreturn]] ( + const model::test_program&, + const utils::config::properties_map&) const; model::test_cases_map parse_list( const utils::optional< utils::process::status >&, const utils::fs::path&, const utils::fs::path&) const; - void exec_test(const model::test_program&, const std::string&, - const utils::config::properties_map&, - const utils::fs::path&) const - UTILS_NORETURN; + void exec_test [[noreturn]] ( + const model::test_program&, const std::string&, + const utils::config::properties_map&, + const utils::fs::path&) const; model::test_result compute_result( const utils::optional< utils::process::status >&, diff --git a/engine/plain.hpp b/engine/plain.hpp index ee5f3e746781..60bffec9bf04 100644 --- a/engine/plain.hpp +++ b/engine/plain.hpp @@ -40,18 +40,19 @@ namespace engine { /// Implementation of the scheduler interface for plain test programs. class plain_interface : public engine::scheduler::interface { public: - void exec_list(const model::test_program&, - const utils::config::properties_map&) const UTILS_NORETURN; + void exec_list [[noreturn]] ( + const model::test_program&, + const utils::config::properties_map&) const; model::test_cases_map parse_list( const utils::optional< utils::process::status >&, const utils::fs::path&, const utils::fs::path&) const; - void exec_test(const model::test_program&, const std::string&, - const utils::config::properties_map&, - const utils::fs::path&) const - UTILS_NORETURN; + void exec_test [[noreturn]] ( + const model::test_program&, const std::string&, + const utils::config::properties_map&, + const utils::fs::path&) const; model::test_result compute_result( const utils::optional< utils::process::status >&, diff --git a/engine/scheduler.cpp b/engine/scheduler.cpp index 2077ca16df6c..6e19a1598f8c 100644 --- a/engine/scheduler.cpp +++ b/engine/scheduler.cpp @@ -87,11 +87,11 @@ using utils::optional; /// /// TODO(jmmv): This is here only for testing purposes. Maybe we should expose /// this setting as part of the user_config. -datetime::delta scheduler::cleanup_timeout(60, 0); +datetime::delta scheduler::cleanup_timeout(300, 0); /// Timeout for the test case execenv cleanup operation. -datetime::delta scheduler::execenv_cleanup_timeout(60, 0); +datetime::delta scheduler::execenv_cleanup_timeout(300, 0); /// Timeout for the test case listing operation. @@ -1403,6 +1403,9 @@ scheduler::scheduler_handle::wait_any(void) if (debugger) { debugger->before_cleanup(test_data->test_program, test_case, result, handle); + if (!result.get().good()) + debugger->upon_test_failure(test_data->test_program, test_case, + result, handle); } if (test_data->needs_cleanup) { diff --git a/engine/scheduler.hpp b/engine/scheduler.hpp index 508a0c0cbfd9..0e241172f1e7 100644 --- a/engine/scheduler.hpp +++ b/engine/scheduler.hpp @@ -98,9 +98,9 @@ public: /// /// \param test_program The test program to execute. /// \param vars User-provided variables to pass to the test program. - virtual void exec_list(const model::test_program& test_program, - const utils::config::properties_map& vars) - const UTILS_NORETURN = 0; + virtual void exec_list [[noreturn]] ( + const model::test_program& test_program, + const utils::config::properties_map& vars) const = 0; /// Computes the test cases list of a test program. /// @@ -126,11 +126,11 @@ public: /// \param vars User-provided variables to pass to the test program. /// \param control_directory Directory where the interface may place control /// files. - virtual void exec_test(const model::test_program& test_program, - const std::string& test_case_name, - const utils::config::properties_map& vars, - const utils::fs::path& control_directory) - const UTILS_NORETURN = 0; + virtual void exec_test [[noreturn]] ( + const model::test_program& test_program, + const std::string& test_case_name, + const utils::config::properties_map& vars, + const utils::fs::path& control_directory) const = 0; /// Executes a test cleanup routine of the test program. /// @@ -143,11 +143,11 @@ public: /// \param vars User-provided variables to pass to the test program. /// \param control_directory Directory where the interface may place control /// files. - virtual void exec_cleanup(const model::test_program& test_program, - const std::string& test_case_name, - const utils::config::properties_map& vars, - const utils::fs::path& control_directory) - const UTILS_NORETURN; + virtual void exec_cleanup [[noreturn]]( + const model::test_program& test_program, + const std::string& test_case_name, + const utils::config::properties_map& vars, + const utils::fs::path& control_directory) const; /// Computes the result of a test case based on its termination status. /// diff --git a/engine/scheduler_test.cpp b/engine/scheduler_test.cpp index d91c448f2e5e..93e7627f291a 100644 --- a/engine/scheduler_test.cpp +++ b/engine/scheduler_test.cpp @@ -131,7 +131,7 @@ class mock_interface : public scheduler::interface { /// /// \param exit_code Exit code. void - do_exit(const int exit_code) const UTILS_NORETURN + do_exit [[noreturn]] (const int exit_code) const { std::cout.flush(); std::cerr.flush(); @@ -140,7 +140,7 @@ class mock_interface : public scheduler::interface { /// Executes a test case that creates various files and then fails. void - exec_create_files_and_fail(void) const UTILS_NORETURN + exec_create_files_and_fail [[noreturn]] (void) const { std::cerr << "This should not be clobbered\n"; atf::utils::create_file("first file", ""); @@ -155,7 +155,7 @@ class mock_interface : public scheduler::interface { /// This is intended to validate that the test runs in an empty directory, /// separate from any control files that the scheduler may have created. void - exec_delete_all(void) const UTILS_NORETURN + exec_delete_all [[noreturn]] (void) const { const int exit_code = ::system("rm *") == -1 ? EXIT_FAILURE : EXIT_SUCCESS; @@ -170,14 +170,14 @@ class mock_interface : public scheduler::interface { /// /// \param exit_code Exit status to terminate the program with. void - exec_exit(const int exit_code) const UTILS_NORETURN + exec_exit [[noreturn]] (const int exit_code) const { do_exit(exit_code); } /// Executes a test case that just fails. void - exec_fail(void) const UTILS_NORETURN + exec_fail [[noreturn]] (void) const { std::cerr << "This should not be clobbered\n"; ::kill(::getpid(), SIGTERM); @@ -191,10 +191,10 @@ class mock_interface : public scheduler::interface { /// number. /// \param vars User-provided variables to pass to the test program. void - exec_print_params(const model::test_program& test_program, - const std::string& test_case_name, - const config::properties_map& vars) const - UTILS_NORETURN + exec_print_params [[noreturn]] ( + const model::test_program& test_program, + const std::string& test_case_name, + const config::properties_map& vars) const { std::cout << F("Test program: %s\n") % test_program.relative_path(); std::cout << F("Test case: %s\n") % test_case_name; @@ -218,9 +218,9 @@ public: /// \param test_program The test program to execute. /// \param vars User-provided variables to pass to the test program. void - exec_list(const model::test_program& test_program, - const config::properties_map& vars) - const UTILS_NORETURN + exec_list [[noreturn]] ( + const model::test_program& test_program, + const config::properties_map& vars) const { const std::string name = test_program.absolute_path().leaf_name(); diff --git a/engine/tap.hpp b/engine/tap.hpp index b46bf28f0240..51ad76231eaf 100644 --- a/engine/tap.hpp +++ b/engine/tap.hpp @@ -40,18 +40,19 @@ namespace engine { /// Implementation of the scheduler interface for tap test programs. class tap_interface : public engine::scheduler::interface { public: - void exec_list(const model::test_program&, - const utils::config::properties_map&) const UTILS_NORETURN; + void exec_list [[noreturn]] ( + const model::test_program&, + const utils::config::properties_map&) const; model::test_cases_map parse_list( const utils::optional< utils::process::status >&, const utils::fs::path&, const utils::fs::path&) const; - void exec_test(const model::test_program&, const std::string&, - const utils::config::properties_map&, - const utils::fs::path&) const - UTILS_NORETURN; + void exec_test [[noreturn]] ( + const model::test_program&, const std::string&, + const utils::config::properties_map&, + const utils::fs::path&) const; model::test_result compute_result( const utils::optional< utils::process::status >&, diff --git a/m4/compiler-features.m4 b/m4/compiler-features.m4 index 840f292383d5..5d2bc92229b1 100644 --- a/m4/compiler-features.m4 +++ b/m4/compiler-features.m4 @@ -27,39 +27,6 @@ dnl (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE dnl OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. dnl -dnl KYUA_ATTRIBUTE_NORETURN -dnl -dnl Checks if the current compiler has a way to mark functions that do not -dnl return and defines ATTRIBUTE_NORETURN to the appropriate string. -dnl -AC_DEFUN([KYUA_ATTRIBUTE_NORETURN], [ - dnl This check is overly simple and should be fixed. For example, - dnl Sun's cc does support the noreturn attribute but CC (the C++ - dnl compiler) does not. And in that case, CC just raises a warning - dnl during compilation, not an error. - AC_CACHE_CHECK( - [whether __attribute__((noreturn)) is supported], - [kyua_cv_attribute_noreturn], [ - AC_RUN_IFELSE([AC_LANG_PROGRAM([], [ -#if ((__GNUC__ == 2 && __GNUC_MINOR__ >= 5) || __GNUC__ > 2) - return 0; -#else - return 1; -#endif - ])], - [kyua_cv_attribute_noreturn=yes], - [kyua_cv_attribute_noreturn=no]) - ]) - if test "${kyua_cv_attribute_noreturn}" = yes; then - attribute_value="__attribute__((noreturn))" - else - attribute_value="" - fi - AC_SUBST([ATTRIBUTE_NORETURN], [${attribute_value}]) -]) - - -dnl dnl KYUA_ATTRIBUTE_PURE dnl dnl Checks if the current compiler has a way to mark functions as pure. @@ -89,34 +56,3 @@ function(int a, int b) fi AC_SUBST([ATTRIBUTE_PURE], [${attribute_value}]) ]) - - -dnl -dnl KYUA_ATTRIBUTE_UNUSED -dnl -dnl Checks if the current compiler has a way to mark parameters as unused -dnl so that the -Wunused-parameter warning can be avoided. -dnl -AC_DEFUN([KYUA_ATTRIBUTE_UNUSED], [ - AC_CACHE_CHECK( - [whether __attribute__((__unused__)) is supported], - [kyua_cv_attribute_unused], [ - AC_COMPILE_IFELSE( - [AC_LANG_PROGRAM([ -static void -function(int a __attribute__((__unused__))) -{ -}], [ - function(3); - return 0; -])], - [kyua_cv_attribute_unused=yes], - [kyua_cv_attribute_unused=no]) - ]) - if test "${kyua_cv_attribute_unused}" = yes; then - attribute_value="__attribute__((__unused__))" - else - attribute_value="" - fi - AC_SUBST([ATTRIBUTE_UNUSED], [${attribute_value}]) -]) diff --git a/os/freebsd/execenv_jail.hpp b/os/freebsd/execenv_jail.hpp index e6d2c2e42497..73365316dae1 100644 --- a/os/freebsd/execenv_jail.hpp +++ b/os/freebsd/execenv_jail.hpp @@ -56,7 +56,7 @@ public: void init() const; void cleanup() const; - void exec(const args_vector& args) const UTILS_NORETURN; + void exec [[noreturn]] (const args_vector& args) const; }; diff --git a/os/freebsd/execenv_jail_stub.cpp b/os/freebsd/execenv_jail_stub.cpp index 9425618e2b5a..4634f4ef80c9 100644 --- a/os/freebsd/execenv_jail_stub.cpp +++ b/os/freebsd/execenv_jail_stub.cpp @@ -35,7 +35,7 @@ using utils::process::args_vector; -static inline void requires_freebsd(void) UTILS_NORETURN; +static inline void requires_freebsd [[noreturn]] (void); static inline void requires_freebsd(void) diff --git a/os/freebsd/reqs_checker_kmods.cpp b/os/freebsd/reqs_checker_kmods.cpp index 3ae3446a7815..ce17caeaeb8d 100644 --- a/os/freebsd/reqs_checker_kmods.cpp +++ b/os/freebsd/reqs_checker_kmods.cpp @@ -31,7 +31,7 @@ #include "model/metadata.hpp" extern "C" { -#include "libutil.h" +#include <libutil.h> } std::string diff --git a/os/freebsd/utils/jail.hpp b/os/freebsd/utils/jail.hpp index 5b972155cd25..44730493a7d0 100644 --- a/os/freebsd/utils/jail.hpp +++ b/os/freebsd/utils/jail.hpp @@ -51,9 +51,10 @@ public: const std::string& test_case_name); void create(const std::string& jail_name, const std::string& jail_params); - void exec(const std::string& jail_name, - const fs::path& program, - const args_vector& args) throw() UTILS_NORETURN; + void exec [[noreturn]] ( + const std::string& jail_name, + const fs::path& program, + const args_vector& args) throw(); void remove(const std::string& jail_name); }; diff --git a/utils/cmdline/options.cpp b/utils/cmdline/options.cpp index 61736e31c11e..9d448503e3f9 100644 --- a/utils/cmdline/options.cpp +++ b/utils/cmdline/options.cpp @@ -53,15 +53,18 @@ namespace text = utils::text; /// purposes. /// \param default_value_ If not NULL, specifies that the option has a default /// value for the mandatory argument. +/// \param arg_is_optional_ Specifies if a value must be provided or not. cmdline::base_option::base_option(const char short_name_, const char* long_name_, const char* description_, const char* arg_name_, - const char* default_value_) : + const char* default_value_, + bool arg_is_optional_) : _short_name(short_name_), _long_name(long_name_), _description(description_), _arg_name(arg_name_ == NULL ? "" : arg_name_), + _arg_is_optional(arg_is_optional_), _has_default_value(default_value_ != NULL), _default_value(default_value_ == NULL ? "" : default_value_) { @@ -164,6 +167,16 @@ cmdline::base_option::arg_name(void) const } +/// Returns optionality of the argument. +/// +/// \return The optionality. +bool +cmdline::base_option::arg_is_optional(void) const +{ + return _arg_is_optional; +} + + /// Checks whether the option has a default value for its argument. /// /// \pre needs_arg() must be true. @@ -558,9 +571,10 @@ cmdline::string_option::string_option(const char short_name_, const char* long_name_, const char* description_, const char* arg_name_, - const char* default_value_) : + const char* default_value_, + bool arg_is_optional_) : base_option(short_name_, long_name_, description_, arg_name_, - default_value_) + default_value_, arg_is_optional_) { } diff --git a/utils/cmdline/options.hpp b/utils/cmdline/options.hpp index f3a83889e491..d11de14af514 100644 --- a/utils/cmdline/options.hpp +++ b/utils/cmdline/options.hpp @@ -91,6 +91,9 @@ class base_option { /// Descriptive name of the required argument; empty if not allowed. std::string _arg_name; + /// If the option can be used without an explicit argument provided. + bool _arg_is_optional = false; + /// Whether the option has a default value or not. /// /// \todo We should probably be using the optional class here. @@ -101,7 +104,7 @@ class base_option { public: base_option(const char, const char*, const char*, const char* = NULL, - const char* = NULL); + const char* = NULL, bool = false); base_option(const char*, const char*, const char* = NULL, const char* = NULL); virtual ~base_option(void); @@ -113,6 +116,7 @@ public: bool needs_arg(void) const; const std::string& arg_name(void) const; + bool arg_is_optional(void) const; bool has_default_value(void) const; const std::string& default_value(void) const; @@ -219,7 +223,7 @@ public: class string_option : public base_option { public: string_option(const char, const char*, const char*, const char*, - const char* = NULL); + const char* = NULL, bool = false); string_option(const char*, const char*, const char*, const char* = NULL); virtual ~string_option(void) {} diff --git a/utils/cmdline/parser.cpp b/utils/cmdline/parser.cpp index 5c83f6d69cc4..29dd4612f6ad 100644 --- a/utils/cmdline/parser.cpp +++ b/utils/cmdline/parser.cpp @@ -88,7 +88,10 @@ options_to_getopt_data(const cmdline::options_vector& options, long_option.name = option->long_name().c_str(); if (option->needs_arg()) - long_option.has_arg = required_argument; + if (option->arg_is_optional()) + long_option.has_arg = optional_argument; + else + long_option.has_arg = required_argument; else long_option.has_arg = no_argument; @@ -96,7 +99,7 @@ options_to_getopt_data(const cmdline::options_vector& options, if (option->has_short_name()) { data.short_options += option->short_name(); if (option->needs_arg()) - data.short_options += ':'; + data.short_options += option->arg_is_optional() ? "::" : ":"; id = option->short_name(); } else { id = cur_id++; @@ -320,9 +323,11 @@ cmdline::parse(const int argc, const char* const* argv, for (cmdline::options_vector::const_iterator iter = options.begin(); iter != options.end(); iter++) { const cmdline::base_option* option = *iter; - if (option->needs_arg() && option->has_default_value()) + if (option->needs_arg() && option->has_default_value() && + !option->arg_is_optional()) { option_values[option->long_name()].push_back( option->default_value()); + } } args_vector args; @@ -357,8 +362,13 @@ cmdline::parse(const int argc, const char* const* argv, if (::optarg != NULL) { option->validate(::optarg); option_values[option->long_name()].push_back(::optarg); - } else - INV(option->has_default_value()); + } else { + if (option->arg_is_optional()) + option_values[option->long_name()].push_back( + option->default_value()); + else + INV(option->has_default_value()); + } } else { option_values[option->long_name()].push_back(""); } diff --git a/utils/defs.hpp.in b/utils/defs.hpp.in index 7290980a6f6f..88207c90aa58 100644 --- a/utils/defs.hpp.in +++ b/utils/defs.hpp.in @@ -38,23 +38,13 @@ extern "C" { #include <inttypes.h> } -/// Attribute to mark a function as non-returning. -#define UTILS_NORETURN @ATTRIBUTE_NORETURN@ - - /// Attribute to mark a function as pure. #define UTILS_PURE @ATTRIBUTE_PURE@ - -/// Attribute to mark an entity as unused. -#define UTILS_UNUSED @ATTRIBUTE_UNUSED@ - - /// Unconstifies a pointer. /// /// \param type The target type of the conversion. /// \param ptr The pointer to be unconstified. #define UTILS_UNCONST(type, ptr) ((type*)(uintptr_t)(const void*)(ptr)) - #endif // !defined(UTILS_DEFS_HPP) diff --git a/utils/fs/operations.cpp b/utils/fs/operations.cpp index 185d164b88d7..4e1bb294a228 100644 --- a/utils/fs/operations.cpp +++ b/utils/fs/operations.cpp @@ -148,7 +148,7 @@ unmount(const char* /* path */, const int exit_known_error = 123; -static void run_mount_tmpfs(const fs::path&, const uint64_t) UTILS_NORETURN; +static void run_mount_tmpfs [[noreturn]] (const fs::path&, const uint64_t); /// Executes 'mount -t tmpfs' (or a similar variant). diff --git a/utils/process/child.cpp b/utils/process/child.cpp index 36b6b6b3e51f..c51c39e6d1ff 100644 --- a/utils/process/child.cpp +++ b/utils/process/child.cpp @@ -235,6 +235,30 @@ process::child::fork_capture_aux(void) } +std::unique_ptr< process::child > +process::child::fork_interactive(void) +{ + std::cout.flush(); + std::cerr.flush(); + + std::unique_ptr< signals::interrupts_inhibiter > inhibiter( + new signals::interrupts_inhibiter); + pid_t pid = detail::syscall_fork(); + if (pid == -1) { + inhibiter.reset(); // Unblock signals. + throw process::system_error("fork(2) failed", errno); + } else if (pid == 0) { + inhibiter.reset(); // Unblock signals. + return {}; + } else { + signals::add_pid_to_kill(pid); + inhibiter.reset(NULL); // Unblock signals. + return std::unique_ptr< process::child >( + new process::child(new impl(pid, NULL))); + } +} + + /// Helper function for fork(). /// /// Please note: if you update this function to change the return type or to diff --git a/utils/process/child.hpp b/utils/process/child.hpp index 3e00cea8752c..bddf4b67fe0b 100644 --- a/utils/process/child.hpp +++ b/utils/process/child.hpp @@ -64,8 +64,8 @@ namespace process { namespace detail { -void report_error_and_abort(void) UTILS_NORETURN; -void report_error_and_abort(const std::runtime_error&) UTILS_NORETURN; +void report_error_and_abort [[noreturn]] (void); +void report_error_and_abort [[noreturn]] (const std::runtime_error&); } // namespace detail @@ -80,6 +80,8 @@ class child : noncopyable { static std::unique_ptr< child > fork_capture_aux(void); + static std::unique_ptr< child > fork_interactive(void); + static std::unique_ptr< child > fork_files_aux(const fs::path&, const fs::path&); @@ -93,6 +95,9 @@ public: std::istream& output(void); template< typename Hook > + static std::unique_ptr< child > fork_interactive(Hook); + + template< typename Hook > static std::unique_ptr< child > fork_files(Hook, const fs::path&, const fs::path&); diff --git a/utils/process/child.ipp b/utils/process/child.ipp index beb2ea3b0b0a..86bc01fc0d6e 100644 --- a/utils/process/child.ipp +++ b/utils/process/child.ipp @@ -104,6 +104,26 @@ child::fork_capture(Hook hook) } +template< typename Hook > +std::unique_ptr< child > +child::fork_interactive(Hook hook) +{ + std::unique_ptr< child > child = fork_interactive(); + if (child.get() == NULL) { + try { + hook(); + std::abort(); + } catch (const std::runtime_error& e) { + detail::report_error_and_abort(e); + } catch (...) { + detail::report_error_and_abort(); + } + } + + return child; +} + + } // namespace process } // namespace utils diff --git a/utils/process/executor_test.cpp b/utils/process/executor_test.cpp index 13ae69bd44ed..b52730b5c71c 100644 --- a/utils/process/executor_test.cpp +++ b/utils/process/executor_test.cpp @@ -80,7 +80,7 @@ using utils::optional; static const datetime::delta infinite_timeout(1000000, 0); -static void do_exit(const int) UTILS_NORETURN; +static void do_exit [[noreturn]] (const int); /// Terminates a subprocess without invoking destructors. @@ -118,8 +118,7 @@ public: /// Runs the subprocess. void - operator()(const fs::path& /* control_directory */) - UTILS_NORETURN + operator() [[noreturn]] (const fs::path& /* control_directory */) { std::cout << "Creating cookie: " << _cookie_name << " (stdout)\n"; std::cerr << "Creating cookie: " << _cookie_name << " (stderr)\n"; @@ -129,7 +128,7 @@ public: }; -static void child_delete_all(const fs::path&) UTILS_NORETURN; +static void child_delete_all [[noreturn]] (const fs::path&); /// Subprocess that deletes all files in the current directory. @@ -155,7 +154,7 @@ child_delete_all(const fs::path& control_directory) } -static void child_dump_unprivileged_user(const fs::path&) UTILS_NORETURN; +static void child_dump_unprivileged_user [[noreturn]] (const fs::path&); /// Subprocess that dumps user configuration. @@ -183,15 +182,14 @@ public: /// Runs the subprocess. void - operator()(const fs::path& /* control_directory */) - UTILS_NORETURN + operator() [[noreturn]] (const fs::path& /* control_directory */) { do_exit(_exit_code); } }; -static void child_pause(const fs::path&) UTILS_NORETURN; +static void child_pause [[noreturn]] (const fs::path&); /// Subprocess that just blocks. @@ -207,7 +205,7 @@ child_pause(const fs::path& /* control_directory */) } -static void child_print(const fs::path&) UTILS_NORETURN; +static void child_print [[noreturn]] (const fs::path&); /// Subprocess that writes to stdout and stderr. @@ -236,8 +234,7 @@ public: /// Runs the subprocess. void - operator()(const fs::path& /* control_directory */) - UTILS_NORETURN + operator() [[noreturn]] (const fs::path& /* control_directory */) { ::sleep(_seconds); do_exit(EXIT_SUCCESS); @@ -245,7 +242,7 @@ public: }; -static void child_spawn_blocking_child(const fs::path&) UTILS_NORETURN; +static void child_spawn_blocking_child [[noreturn]] (const fs::path&); /// Subprocess that spawns a subchild that gets stuck. @@ -278,7 +275,7 @@ child_spawn_blocking_child( } -static void child_validate_isolation(const fs::path&) UTILS_NORETURN; +static void child_validate_isolation [[noreturn]] (const fs::path&); /// Subprocess that checks if isolate_child() has been called. diff --git a/utils/process/isolation.cpp b/utils/process/isolation.cpp index 90dd08d5772d..3b2ecaa3e992 100644 --- a/utils/process/isolation.cpp +++ b/utils/process/isolation.cpp @@ -67,7 +67,7 @@ const int process::exit_isolation_failure = 124; namespace { -static void fail(const std::string&, const int) UTILS_NORETURN; +static void fail [[noreturn]] (const std::string&, const int); /// Fails the process with an errno-based error message. diff --git a/utils/process/operations.hpp b/utils/process/operations.hpp index 773f9d38bb74..781d7d84af8e 100644 --- a/utils/process/operations.hpp +++ b/utils/process/operations.hpp @@ -42,10 +42,10 @@ namespace utils { namespace process { -void exec(const utils::fs::path&, const args_vector&) throw() UTILS_NORETURN; -void exec_unsafe(const utils::fs::path&, const args_vector&) UTILS_NORETURN; +void exec [[noreturn]] (const utils::fs::path&, const args_vector&) throw(); +void exec_unsafe [[noreturn]] (const utils::fs::path&, const args_vector&); void terminate_group(const int); -void terminate_self_with(const status&) UTILS_NORETURN; +void terminate_self_with [[noreturn]] (const status&); status wait(const int); status wait_any(void); diff --git a/utils/process/operations_test.cpp b/utils/process/operations_test.cpp index d30dc890abd2..863e0937fe0a 100644 --- a/utils/process/operations_test.cpp +++ b/utils/process/operations_test.cpp @@ -117,7 +117,7 @@ child_exit(void) } -static void suspend(void) UTILS_NORETURN; +static void suspend [[noreturn]] (void); /// Blocks a subprocess from running indefinitely. @@ -132,7 +132,7 @@ suspend(void) } -static void write_loop(const int) UTILS_NORETURN; +static void write_loop [[noreturn]] (const int); /// Provides an infinite stream of data in a subprocess. diff --git a/utils/sanity.hpp b/utils/sanity.hpp index 6b126f984999..d4bde12e8060 100644 --- a/utils/sanity.hpp +++ b/utils/sanity.hpp @@ -49,8 +49,8 @@ namespace utils { -void sanity_failure(const assert_type, const char*, const size_t, - const std::string&) UTILS_NORETURN; +void sanity_failure [[noreturn]]( + const assert_type, const char*, const size_t, const std::string&); void install_crash_handlers(const std::string&); diff --git a/utils/signals/misc_test.cpp b/utils/signals/misc_test.cpp index 3b6d57325ee1..398cf50c4c2b 100644 --- a/utils/signals/misc_test.cpp +++ b/utils/signals/misc_test.cpp @@ -51,7 +51,7 @@ namespace signals = utils::signals; namespace { -static void program_reset_raise(void) UTILS_NORETURN; +static void program_reset_raise [[noreturn]] (void); /// Body of a subprocess that tests the signals::reset function. diff --git a/utils/sqlite/test_utils.hpp b/utils/sqlite/test_utils.hpp index bf35d209a164..ba6b720b57ed 100644 --- a/utils/sqlite/test_utils.hpp +++ b/utils/sqlite/test_utils.hpp @@ -97,7 +97,7 @@ static const char* create_test_table_sql = "INSERT INTO test (prime) VALUES (3);\n"; -static void create_test_table(::sqlite3*) UTILS_UNUSED; +[[maybe_unused]] void create_test_table(::sqlite3*); /// Creates a 'test' table in a database. @@ -118,7 +118,7 @@ create_test_table(::sqlite3* db) } -static void verify_test_table(::sqlite3*) UTILS_UNUSED; +[[maybe_unused]] void verify_test_table(::sqlite3*); /// Verifies that the specified database contains the 'test' table. diff --git a/utils/stacktrace.cpp b/utils/stacktrace.cpp index 11636b31959f..30d8459c98c3 100644 --- a/utils/stacktrace.cpp +++ b/utils/stacktrace.cpp @@ -77,7 +77,7 @@ const char* utils::builtin_gdb = GDB; /// Maximum time the external GDB process is allowed to run for. -datetime::delta utils::gdb_timeout(60, 0); +datetime::delta utils::gdb_timeout(300, 0); namespace { diff --git a/utils/stacktrace_test.cpp b/utils/stacktrace_test.cpp index ca87e7087f5a..e3b170755764 100644 --- a/utils/stacktrace_test.cpp +++ b/utils/stacktrace_test.cpp @@ -94,7 +94,7 @@ public: /// Runs the binary. void - operator()(void) const UTILS_NORETURN + operator() [[noreturn]] (void) const { atf::utils::copy_file(_binary.str(), _copy_name.str()); @@ -106,15 +106,14 @@ public: /// /// This interface is exposed to support passing crash_me to the executor. void - operator()(const fs::path& /* control_directory */) const - UTILS_NORETURN + operator() [[noreturn]] (const fs::path& /* control_directory */) const { (*this)(); // Delegate to ensure the two entry points remain in sync. } }; -static void child_exit(const fs::path&) UTILS_NORETURN; +static void child_exit [[noreturn]] (const fs::path&); /// Subprocess that exits cleanly. @@ -125,7 +124,7 @@ child_exit(const fs::path& /* control_directory */) } -static void child_pause(const fs::path&) UTILS_NORETURN; +static void child_pause [[noreturn]] (const fs::path&); /// Subprocess that just blocks. diff --git a/utils/test_utils.ipp b/utils/test_utils.ipp index f21d0f4cc172..60b92adf5463 100644 --- a/utils/test_utils.ipp +++ b/utils/test_utils.ipp @@ -67,7 +67,7 @@ avoid_coredump_on_crash(void) } -inline void abort_without_coredump(void) UTILS_NORETURN; +inline void abort_without_coredump [[noreturn]] (void); /// Aborts execution and tries to not dump core. diff --git a/utils/text/regex.cpp b/utils/text/regex.cpp index b078ba88f6b4..86b41cc872aa 100644 --- a/utils/text/regex.cpp +++ b/utils/text/regex.cpp @@ -47,8 +47,8 @@ namespace text = utils::text; namespace { -static void throw_regex_error(const int, const ::regex_t*, const std::string&) - UTILS_NORETURN; +static void throw_regex_error [[noreturn]] ( + const int, const ::regex_t*, const std::string&); /// Constructs and raises a regex_error. |
