From: Joel Rosdahl Date: Wed, 23 Jul 2025 12:46:39 +0000 (+0200) Subject: chore: Simplify Windows-specific hacks for shell scripts in test suite X-Git-Tag: v4.12~70 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=ffef23a9fa2f02749fa83030bdbaf0041b9801dd;p=thirdparty%2Fccache.git chore: Simplify Windows-specific hacks for shell scripts in test suite --- diff --git a/src/ccache/execute.cpp b/src/ccache/execute.cpp index 96a5f60c..b0bf6f31 100644 --- a/src/ccache/execute.cpp +++ b/src/ccache/execute.cpp @@ -87,34 +87,6 @@ execute_noreturn(const char* const* argv, const fs::path& temp_dir) win32execute(argv, 0, -1, -1, util::pstr(temp_dir).c_str()); } -std::string -win32getshell(const std::string& path) -{ - auto path_list = util::getenv_path_list("PATH"); - if (path_list.empty()) { - return {}; - } - - std::string sh; - if (util::to_lowercase(util::pstr(fs::path(path).extension()).str()) - == ".sh") { - sh = util::pstr(find_executable_in_path("sh.exe", path_list)); - } - if (sh.empty() && getenv("CCACHE_DETECT_SHEBANG")) { - // Detect shebang. - util::FileStream fp(path, "r"); - if (fp) { - char buf[10] = {0}; - fgets(buf, sizeof(buf) - 1, fp.get()); - if (std::string(buf) == "#!/bin/sh") { - sh = util::pstr(find_executable_in_path("sh.exe", path_list)); - } - } - } - - return sh; -} - int win32execute(const char* const* argv, int doreturn, @@ -240,10 +212,6 @@ win32execute(const char* const* argv, tmp_file_path = tmp_file.path; } - std::string sh = win32getshell(argv[0]); - if (!sh.empty()) { - commandline = FMT(R"("{}" {})", sh, commandline); - } BOOL ret = CreateProcess(nullptr, const_cast(commandline.c_str()), nullptr, diff --git a/src/ccache/execute.hpp b/src/ccache/execute.hpp index 92940692..18daac2c 100644 --- a/src/ccache/execute.hpp +++ b/src/ccache/execute.hpp @@ -1,4 +1,4 @@ -// Copyright (C) 2020-2024 Joel Rosdahl and other contributors +// Copyright (C) 2020-2025 Joel Rosdahl and other contributors // // See doc/AUTHORS.adoc for a complete list of contributors. // @@ -45,7 +45,3 @@ std::filesystem::path find_executable_in_path( const std::string& name, const std::vector& path_list, const std::optional& exclude_path = std::nullopt); - -#ifdef _WIN32 -std::string win32getshell(const std::string& path); -#endif diff --git a/src/ccache/hashutil.cpp b/src/ccache/hashutil.cpp index ca822247..7b56f388 100644 --- a/src/ccache/hashutil.cpp +++ b/src/ccache/hashutil.cpp @@ -341,27 +341,14 @@ hash_command_output(Hash& hash, const std::string& command, const std::string& compiler) { + util::Args args = util::Args::from_string(command); #ifdef _WIN32 - std::string adjusted_command = util::strip_whitespace(command); - - // Add "echo" command. - bool using_cmd_exe; - if (util::starts_with(adjusted_command, "echo")) { - adjusted_command = FMT("cmd.exe /c \"{}\"", adjusted_command); - using_cmd_exe = true; - } else { - using_cmd_exe = false; - } - util::Args args = util::Args::from_string(adjusted_command); - { - auto full_path = - find_executable_in_path(args[0], util::getenv_path_list("PATH")).string(); - if (!full_path.empty()) { - args[0] = full_path; - } + // CreateProcess does not search PATH. + auto full_path = + find_executable_in_path(args[0], util::getenv_path_list("PATH")).string(); + if (!full_path.empty()) { + args[0] = full_path; } -#else - util::Args args = util::Args::from_string(command); #endif for (size_t i = 0; i < args.size(); i++) { @@ -393,15 +380,7 @@ hash_command_output(Hash& hash, si.dwFlags = STARTF_USESTDHANDLES; std::string commandline; - if (using_cmd_exe) { - commandline = adjusted_command; // quoted - } else { - commandline = util::format_argv_as_win32_command_string(argv); - std::string sh = win32getshell(args[0]); - if (!sh.empty()) { - commandline = FMT(R"("{}" {})", sh, commandline); - } - } + commandline = util::format_argv_as_win32_command_string(argv); BOOL ret = CreateProcess(nullptr, const_cast(commandline.c_str()), nullptr, diff --git a/src/ccache/util/string.cpp b/src/ccache/util/string.cpp index 1cefb126..0095c7c0 100644 --- a/src/ccache/util/string.cpp +++ b/src/ccache/util/string.cpp @@ -55,6 +55,9 @@ format_argv_as_win32_command_string(const char* const* argv, bool escape_backslashes) { std::string result; + if (getenv("_CCACHE_TEST") && argv[0] && util::ends_with(argv[0], ".sh")) { + result += "sh.exe "; + } for (size_t i = 0; argv[i]; ++i) { const char* arg = argv[i]; diff --git a/test/run b/test/run index e098875a..4b69ee11 100755 --- a/test/run +++ b/test/run @@ -3,7 +3,7 @@ # A simple test suite for ccache. # # Copyright (C) 2002-2007 Andrew Tridgell -# Copyright (C) 2009-2024 Joel Rosdahl and other contributors +# Copyright (C) 2009-2025 Joel Rosdahl and other contributors # # See doc/AUTHORS.adoc for a complete list of contributors. # @@ -406,7 +406,7 @@ reset_environment() { unset XDG_CONFIG_HOME export PWD=$(pwd) - export CCACHE_DETECT_SHEBANG=1 + export _CCACHE_TEST=1 export CCACHE_DIR=$ABS_TESTDIR/.ccache export CCACHE_CONFIGPATH=$CCACHE_DIR/ccache.conf # skip system config export CCACHE_LOGFILE=$ABS_TESTDIR/ccache.log diff --git a/unittest/main.cpp b/unittest/main.cpp index 0baf62ae..fb298d26 100644 --- a/unittest/main.cpp +++ b/unittest/main.cpp @@ -35,7 +35,7 @@ int main(int argc, char** argv) { #ifdef _WIN32 - util::setenv("CCACHE_DETECT_SHEBANG", "1"); + util::setenv("_CCACHE_TEST", "1"); #endif util::unsetenv("GCC_COLORS"); // Don't confuse argument processing tests. diff --git a/unittest/test_hashutil.cpp b/unittest/test_hashutil.cpp index 7416c8f7..16406070 100644 --- a/unittest/test_hashutil.cpp +++ b/unittest/test_hashutil.cpp @@ -1,4 +1,4 @@ -// Copyright (C) 2010-2024 Joel Rosdahl and other contributors +// Copyright (C) 2010-2025 Joel Rosdahl and other contributors // // See doc/AUTHORS.adoc for a complete list of contributors. // @@ -21,6 +21,7 @@ #include #include #include +#include #include @@ -28,45 +29,71 @@ using TestUtil::TestContext; +static bool +hco(Hash& hash, const std::string& command, const std::string& compiler) +{ +#ifdef _WIN32 + util::write_file("command.bat", FMT("@echo off\r\n{}\r\n", command)); + return hash_command_output(hash, "command.bat", compiler); +#else + util::write_file("command.sh", FMT("#!/bin/sh\n{}\n", command)); + chmod("command.sh", 0555); + return hash_command_output(hash, "./command.sh", compiler); +#endif +} + TEST_SUITE_BEGIN("hashutil"); TEST_CASE("hash_command_output_simple") { + TestContext test_context; + Hash h1; Hash h2; - CHECK(hash_command_output(h1, "echo", "not used")); - CHECK(hash_command_output(h2, "echo", "not used")); + CHECK(hco(h1, "echo", "not used")); + CHECK(hco(h2, "echo", "not used")); CHECK(h1.digest() == h2.digest()); } TEST_CASE("hash_command_output_space_removal") { + TestContext test_context; + Hash h1; Hash h2; - CHECK(hash_command_output(h1, "echo", "not used")); - CHECK(hash_command_output(h2, " echo ", "not used")); + CHECK(hco(h1, "echo", "not used")); + CHECK(hco(h2, " echo ", "not used")); CHECK(h1.digest() == h2.digest()); } TEST_CASE("hash_command_output_hash_inequality") { + TestContext test_context; + Hash h1; Hash h2; - CHECK(hash_command_output(h1, "echo foo", "not used")); - CHECK(hash_command_output(h2, "echo bar", "not used")); + CHECK(hco(h1, "echo foo", "not used")); + CHECK(hco(h2, "echo bar", "not used")); CHECK(h1.digest() != h2.digest()); } TEST_CASE("hash_command_output_compiler_substitution") { + TestContext test_context; + Hash h1; Hash h2; - CHECK(hash_command_output(h1, "echo foo", "not used")); + CHECK(hco(h1, "echo foo", "not used")); +#ifdef _WIN32 + util::write_file("command.bat", "@echo off\r\necho foo\r\n"); + CHECK(hash_command_output(h2, "%compiler%", "command.bat")); +#else CHECK(hash_command_output(h2, "%compiler% foo", "echo")); +#endif CHECK(h1.digest() == h2.digest()); } @@ -77,39 +104,40 @@ TEST_CASE("hash_command_output_stdout_versus_stderr") Hash h1; Hash h2; -#ifndef _WIN32 - util::write_file("stderr.sh", "#!/bin/sh\necho foo >&2\n"); - chmod("stderr.sh", 0555); - CHECK(hash_command_output(h1, "echo foo", "not used")); - CHECK(hash_command_output(h2, "./stderr.sh", "not used")); -#else +#ifdef _WIN32 util::write_file("stderr.bat", "@echo off\r\necho foo>&2\r\n"); - CHECK(hash_command_output(h1, "echo foo", "not used")); - CHECK(hash_command_output(h2, "stderr.bat", "not used")); + CHECK(hco(h1, "echo foo", "not used")); + CHECK(hco(h2, "stderr.bat", "not used")); +#else + CHECK(hco(h1, "echo foo", "not used")); + CHECK(hco(h2, "echo foo >&2", "not used")); #endif CHECK(h1.digest() == h2.digest()); } TEST_CASE("hash_multicommand_output") { + TestContext test_context; + Hash h1; Hash h2; -#ifndef _WIN32 - util::write_file("foo.sh", "#!/bin/sh\necho foo\necho bar\n"); - chmod("foo.sh", 0555); - CHECK(hash_multicommand_output(h2, "echo foo; echo bar", "not used")); - CHECK(hash_multicommand_output(h1, "./foo.sh", "not used")); +#ifdef _WIN32 + h2.hash("foo\r\nbar\r\n"); + util::write_file("foo.bat", "@echo off\r\necho foo\r\n"); + util::write_file("bar.bat", "@echo off\r\necho bar\r\n"); + CHECK(hash_multicommand_output(h1, "foo.bat; bar.bat", "not used")); #else - util::write_file("foo.bat", "@echo off\r\necho foo\r\necho bar\r\n"); - CHECK(hash_multicommand_output(h2, "echo foo; echo bar", "not used")); - CHECK(hash_multicommand_output(h1, "foo.bat", "not used")); -#endif + h2.hash("foo\nbar\n"); + CHECK(hash_multicommand_output(h1, "echo foo; echo bar", "not used")); CHECK(h1.digest() == h2.digest()); +#endif } TEST_CASE("hash_multicommand_output_error_handling") { + TestContext test_context; + Hash h1; Hash h2; @@ -118,6 +146,8 @@ TEST_CASE("hash_multicommand_output_error_handling") TEST_CASE("check_for_temporal_macros") { + TestContext test_context; + const std::string_view time_start = "__TIME__\n" "int a;\n";