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,
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,
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++) {
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,
-// 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.
//
#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());
}
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;
TEST_CASE("check_for_temporal_macros")
{
+ TestContext test_context;
+
const std::string_view time_start =
"__TIME__\n"
"int a;\n";