]> git.ipfire.org Git - thirdparty/ccache.git/commitdiff
chore: Simplify Windows-specific hacks for shell scripts in test suite
authorJoel Rosdahl <joel@rosdahl.net>
Wed, 23 Jul 2025 12:46:39 +0000 (14:46 +0200)
committerJoel Rosdahl <joel@rosdahl.net>
Thu, 24 Jul 2025 09:06:51 +0000 (11:06 +0200)
src/ccache/execute.cpp
src/ccache/execute.hpp
src/ccache/hashutil.cpp
src/ccache/util/string.cpp
test/run
unittest/main.cpp
unittest/test_hashutil.cpp

index 96a5f60c5c0ec0de8f940184266cc587c47638ae..b0bf6f31e84f6ae5e1420171e09f7b97c6ac2885 100644 (file)
@@ -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<char*>(commandline.c_str()),
                            nullptr,
index 92940692eeca2253fe0afd7c536df2cfb23eb502..18daac2c31236af6ea9ec9481f273d5054c4dc0e 100644 (file)
@@ -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<std::filesystem::path>& path_list,
   const std::optional<std::filesystem::path>& exclude_path = std::nullopt);
-
-#ifdef _WIN32
-std::string win32getshell(const std::string& path);
-#endif
index ca822247d4e483fee3b9cc590c48e504f3c4cd74..7b56f388f9ea5eebf91a0d308c4a7b1e52b3cf4c 100644 (file)
@@ -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<char*>(commandline.c_str()),
                            nullptr,
index 1cefb126eb9167240cf6fc793201fd69c1604390..0095c7c0d3cf9e5802958780f5808962cd526dc3 100644 (file)
@@ -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];
index e098875a2322bf6e5998c7a4d65491b8ef0167e7..4b69ee11f3e8db2c5568cb6aed50a6c583f8970e 100755 (executable)
--- 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
index 0baf62aebd8a3f60dba4ae46be09e5af4145039f..fb298d267067cb36e2df523237486b5176ef3168 100644 (file)
@@ -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.
 
index 7416c8f7208fad9ca29c85b28b35f27968ece2ab..16406070f69010a329ac782de63c1fd018cc7966 100644 (file)
@@ -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 <ccache/hash.hpp>
 #include <ccache/hashutil.hpp>
 #include <ccache/util/file.hpp>
+#include <ccache/util/format.hpp>
 
 #include <doctest/doctest.h>
 
 
 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";