instrumentation: support long output list of files

Fixes: #27942
This commit is contained in:
AJIOB
2026-07-26 23:04:22 +03:00
parent bbe9cd4f0b
commit dba12b5e82
12 changed files with 146 additions and 2 deletions
+17
View File
@@ -6,6 +6,7 @@
#include <cstring>
#include <functional>
#include <iostream>
#include <iterator>
#include <map>
#include <memory>
@@ -63,6 +64,7 @@ bool cmCTestLaunch::ParseArguments(int argc, char const* const* argv)
{
DoingNone,
DoingOutput,
DoingOutputAsFileName,
DoingSource,
DoingLanguage,
DoingTargetLabels,
@@ -87,6 +89,8 @@ bool cmCTestLaunch::ParseArguments(int argc, char const* const* argv)
doing = DoingCommandType;
} else if (strcmp(arg, "--output") == 0) {
doing = DoingOutput;
} else if (strcmp(arg, "--output-as-file-name") == 0) {
doing = DoingOutputAsFileName;
} else if (strcmp(arg, "--source") == 0) {
doing = DoingSource;
} else if (strcmp(arg, "--language") == 0) {
@@ -112,6 +116,19 @@ bool cmCTestLaunch::ParseArguments(int argc, char const* const* argv)
} else if (doing == DoingOutput) {
this->Reporter.OptionOutput = arg;
doing = DoingNone;
} else if (doing == DoingOutputAsFileName) {
// Read all data from the file name
cmsys::ifstream file(arg, std::ios::in);
if (!file) {
std::cerr << "failed to open file '" << arg << "' for reading ("
<< cmSystemTools::GetLastSystemError() << "):\n";
return false;
}
this->Reporter.OptionOutput =
std::string(std::istreambuf_iterator<char>(file),
std::istreambuf_iterator<char>());
doing = DoingNone;
} else if (doing == DoingSource) {
this->Reporter.OptionSource = arg;
doing = DoingNone;
+23
View File
@@ -11,6 +11,8 @@
#include <cmext/algorithm>
#include <cmext/string_view>
#include "cmsys/FStream.hxx"
#include "cmCryptoHash.h"
#include "cmCustomCommand.h"
#include "cmCustomCommandLines.h"
@@ -520,3 +522,24 @@ cmCustomCommandGenerator::GetUtilities() const
{
return this->Utilities;
}
std::string cmCustomCommandGenerator::StoreContentToFile(
std::string const& content) const
{
cmCryptoHash hash(cmCryptoHash::AlgoSHA256);
std::string fileDir =
cmStrCat(this->LG->GetBinaryDirectory(), "/CMakeFiles/c");
if (!cmSystemTools::MakeDirectory(fileDir)) {
return "";
}
std::string fileName = cmStrCat(fileDir, "/", hash.HashString(content));
cmsys::ofstream file(fileName.c_str(), std::ios::out);
if (!file) {
return "";
}
file.write(content.data(), content.size());
return fileName;
}
+2
View File
@@ -72,4 +72,6 @@ public:
std::string const& GetOutputConfig() const { return this->OutputConfig; }
std::string const& GetCommandConfig() const { return this->CommandConfig; }
std::string StoreContentToFile(std::string const& content) const;
};
+4
View File
@@ -716,6 +716,10 @@ std::string cmFastbuildTargetGenerator::MakeCustomLauncher(
}
}
vars.Output = output.c_str();
std::string filePathWithOutput = ccg.StoreContentToFile(output);
vars.FilePathWithOutput = filePathWithOutput.c_str();
vars.Role = ccg.GetCC().GetRole().c_str();
vars.CMTargetName = ccg.GetCC().GetTarget().c_str();
vars.Config = ccg.GetOutputConfig().c_str();
+4
View File
@@ -913,6 +913,10 @@ std::string cmLocalNinjaGenerator::MakeCustomLauncher(
}
}
vars.Output = output.c_str();
std::string filePathWithOutput = ccg.StoreContentToFile(output);
vars.FilePathWithOutput = filePathWithOutput.c_str();
vars.Role = ccg.GetCC().GetRole().c_str();
vars.CMTargetName = ccg.GetCC().GetTarget().c_str();
vars.Config = ccg.GetOutputConfig().c_str();
+4
View File
@@ -1071,6 +1071,10 @@ void cmLocalUnixMakefileGenerator3::AppendCustomCommand(
}
}
vars.Output = output.c_str();
std::string filePathWithOutput = ccg.StoreContentToFile(output);
vars.FilePathWithOutput = filePathWithOutput.c_str();
vars.Role = ccg.GetCC().GetRole().c_str();
vars.CMTargetName = ccg.GetCC().GetTarget().c_str();
vars.Config = ccg.GetOutputConfig().c_str();
+5
View File
@@ -304,6 +304,11 @@ std::string cmRulePlaceholderExpander::ExpandVariable(
return this->ReplaceValues->Output;
}
}
if (this->ReplaceValues->FilePathWithOutput) {
if (variable == "OUTPUT_STORE_TO_FILE") {
return this->ReplaceValues->FilePathWithOutput;
}
}
if (variable == "CMAKE_COMMAND") {
return this->OutputConverter->ConvertToOutputFormat(
cmSystemTools::GetCMakeCommand(), cmOutputConverter::SHELL);
+1
View File
@@ -56,6 +56,7 @@ public:
char const* PreprocessedSource = nullptr;
char const* DynDepFile = nullptr;
char const* Output = nullptr;
char const* FilePathWithOutput = nullptr;
char const* Object = nullptr;
char const* TargetSupportDir = nullptr;
char const* ObjectDir = nullptr;
+3 -2
View File
@@ -2882,8 +2882,9 @@ int cmake::ActualConfigure()
"--output <TARGET> --config <CONFIG> --language <LANGUAGE> -- "));
this->State->SetGlobalProperty(
"RULE_LAUNCH_CUSTOM",
cmStrCat(launcher, "--command-type custom", common_args,
"--output \"<OUTPUT>\" --role <ROLE> -- "));
cmStrCat(
launcher, "--command-type custom", common_args,
"--output-as-file-name \"<OUTPUT_STORE_TO_FILE>\" --role <ROLE> -- "));
}
#endif
@@ -617,6 +617,16 @@ instrument(cmake-command-trace
CHECK_SCRIPT check-trace-removed.cmake
)
# FIXME(#27984): FASTBuild long output test failure
# FIXME(#279846): Watcom WMake unsupported file names
if(NOT RunCMake_GENERATOR MATCHES "FASTBuild|Watcom WMake")
instrument(cmake-command-long-output
BUILD
CONFIGURE_ARGS "-DLONG_CUSTOM_COMMAND_OUTPUT=ON"
CHECK_SCRIPT check-long-output.cmake
)
endif()
# Test capture output
instrument(cmake-command-capture-output
BUILD CAPTURE_OUTPUT_QUERY
@@ -0,0 +1,53 @@
include(${CMAKE_CURRENT_LIST_DIR}/json.cmake)
if(NOT EXISTS "${RunCMake_TEST_BINARY_DIR}/Destination-Modules/check-done.txt")
string(APPEND RunCMake_TEST_FAILED
"long custom command with instrumentation did not run (Destination-Modules missing)\n")
endif()
# Reconstruct expected outputs as in project/CMakeLists.txt
file(GLOB_RECURSE input_files
RELATIVE "${CMAKE_ROOT}"
"${CMAKE_ROOT}/Modules/*")
set(expected_outs)
foreach(f IN LISTS input_files)
list(APPEND expected_outs "Destination-${f}")
endforeach()
list(LENGTH expected_outs expected_count)
list(SORT expected_outs)
# Find the custom snippet for the long-output command and verify its outputs
file(GLOB snippets LIST_DIRECTORIES false "${v1}/data/custom-*")
set(found_long_snippet 0)
foreach(snippet IN LISTS snippets)
read_json("${snippet}" contents)
string(JSON outputs ERROR_VARIABLE noOutputs GET "${contents}" outputs)
if(NOT outputs MATCHES "Destination-")
continue()
endif()
set(found_long_snippet 1)
string(JSON outputs_len LENGTH "${contents}" outputs)
if(NOT outputs_len EQUAL expected_count)
json_error("${snippet}"
"Expected ${expected_count} outputs, got ${outputs_len}")
continue()
endif()
set(actual_outs)
math(EXPR last "${outputs_len} - 1")
foreach(i RANGE ${last})
string(JSON out GET "${contents}" outputs ${i})
string(REPLACE "\\" "/" out "${out}")
list(APPEND actual_outs "${out}")
endforeach()
list(SORT actual_outs)
if(NOT expected_outs STREQUAL actual_outs)
json_error("${snippet}"
"outputs field does not match expected Destination-* list")
endif()
endforeach()
if(NOT found_long_snippet)
add_error("No custom snippet with Destination- outputs was found")
endif()
@@ -62,6 +62,26 @@ install(TARGETS main)
set_target_properties(main PROPERTIES LABELS "label1;label2")
set_target_properties(lib PROPERTIES LABELS "label3")
if(LONG_CUSTOM_COMMAND_OUTPUT)
file(GLOB_RECURSE LONG_CUSTOM_FILES
RELATIVE "${CMAKE_ROOT}"
"${CMAKE_ROOT}/Modules/*")
set(long_dst "${CMAKE_CURRENT_BINARY_DIR}/Destination-Modules")
set(long_outs)
foreach(f IN LISTS LONG_CUSTOM_FILES)
# Generate outputs without spaces to correct expansion
list(APPEND long_outs "Destination-${f}")
endforeach()
add_custom_command(
OUTPUT ${long_outs}
COMMAND "${CMAKE_COMMAND}" -E copy_directory
"${CMAKE_ROOT}/Modules" "${long_dst}"
COMMAND "${CMAKE_COMMAND}" -E touch "${long_dst}/check-done.txt"
COMMENT "Copying Modules to Destination-Modules"
)
add_custom_target(longCustomCommand ALL DEPENDS ${long_outs})
endif()
if (FAIL)
file(WRITE "${CMAKE_BINARY_DIR}/dummy.c"
"#error \"something which will not compile\"\n"