diff options
author | Brad King <brad.king@kitware.com> | 2022-12-17 13:44:36 (GMT) |
---|---|---|
committer | Kitware Robot <kwrobot@kitware.com> | 2022-12-17 13:44:50 (GMT) |
commit | a1b71112d10b371610ef03b908ce9f2ab8eaeaf9 (patch) | |
tree | e1280527bde42f2207a373ddaa0923a90557ee3f /Source | |
parent | 20a77bdf170d22072b4161693b06322559d0b3e9 (diff) | |
parent | 6c40e0b25ec8475d73779ffbaa63a4230aa5e28d (diff) | |
download | CMake-a1b71112d10b371610ef03b908ce9f2ab8eaeaf9.zip CMake-a1b71112d10b371610ef03b908ce9f2ab8eaeaf9.tar.gz CMake-a1b71112d10b371610ef03b908ce9f2ab8eaeaf9.tar.bz2 |
Merge topic 'configure-log'
6c40e0b25e ConfigureLog: Version individual events instead of the whole log
048a02d5bb ConfigureLog: Log try_compile and try_run checks
746c776caf ConfigureLog: Add infrastructure for structured configure event logging
e8b8d82cbf Tests: Generalize RunCMake expectation component names
8d29a0bda6 cmTryRunCommand: Factor out stdout/stderr capture conditions
fdda4095a3 cmCoreTryCompile: Return more semantic information from compile step
Acked-by: Kitware Robot <kwrobot@kitware.com>
Tested-by: buildbot <buildbot@kitware.com>
Merge-request: !8017
Diffstat (limited to 'Source')
-rw-r--r-- | Source/CMakeLists.txt | 2 | ||||
-rw-r--r-- | Source/cmConfigureLog.cxx | 261 | ||||
-rw-r--r-- | Source/cmConfigureLog.h | 68 | ||||
-rw-r--r-- | Source/cmCoreTryCompile.cxx | 78 | ||||
-rw-r--r-- | Source/cmCoreTryCompile.h | 20 | ||||
-rw-r--r-- | Source/cmTryCompileCommand.cxx | 30 | ||||
-rw-r--r-- | Source/cmTryRunCommand.cxx | 108 | ||||
-rw-r--r-- | Source/cmake.cxx | 13 | ||||
-rw-r--r-- | Source/cmake.h | 8 |
9 files changed, 540 insertions, 48 deletions
diff --git a/Source/CMakeLists.txt b/Source/CMakeLists.txt index 41a901a..db928fc 100644 --- a/Source/CMakeLists.txt +++ b/Source/CMakeLists.txt @@ -160,6 +160,8 @@ add_library( cmComputeLinkInformation.h cmComputeTargetDepends.h cmComputeTargetDepends.cxx + cmConfigureLog.h + cmConfigureLog.cxx cmConsoleBuf.h cmConsoleBuf.cxx cmConstStack.h diff --git a/Source/cmConfigureLog.cxx b/Source/cmConfigureLog.cxx new file mode 100644 index 0000000..c2a5b5e --- /dev/null +++ b/Source/cmConfigureLog.cxx @@ -0,0 +1,261 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing for details. */ +#include "cmConfigureLog.h" + +#include <cassert> +#include <cstdio> +#include <iterator> +#include <sstream> +#include <utility> + +#include <cmext/algorithm> +#include <cmext/string_view> + +#include <cm3p/json/writer.h> + +#include "cm_utf8.h" + +#include "cmListFileCache.h" +#include "cmMakefile.h" +#include "cmStringAlgorithms.h" +#include "cmSystemTools.h" +#include "cmake.h" + +cmConfigureLog::cmConfigureLog(std::string logDir, + std::vector<unsigned long> logVersions) + : LogDir(std::move(logDir)) + , LogVersions(std::move(logVersions)) +{ + // Always emit events for the latest log version. + static const unsigned long LatestLogVersion = 1; + if (!cm::contains(this->LogVersions, LatestLogVersion)) { + this->LogVersions.emplace_back(LatestLogVersion); + } + + Json::StreamWriterBuilder builder; + this->Encoder.reset(builder.newStreamWriter()); +} + +cmConfigureLog::~cmConfigureLog() +{ + if (this->Opened) { + this->EndObject(); + this->Stream << "...\n"; + } +} + +bool cmConfigureLog::IsAnyLogVersionEnabled( + std::vector<unsigned long> const& v) const +{ + // Both input lists are sorted. Look for a matching element. + auto i1 = v.cbegin(); + auto i2 = this->LogVersions.cbegin(); + while (i1 != v.cend() && i2 != this->LogVersions.cend()) { + if (*i1 < *i2) { + ++i1; + } else if (*i2 < *i1) { + ++i2; + } else { + return true; + } + } + return false; +} + +void cmConfigureLog::WriteBacktrace(cmMakefile const& mf) +{ + std::vector<std::string> backtrace; + auto root = mf.GetCMakeInstance()->GetHomeDirectory(); + for (auto bt = mf.GetBacktrace(); !bt.Empty(); bt = bt.Pop()) { + auto t = bt.Top(); + if (!t.Name.empty() || t.Line == cmListFileContext::DeferPlaceholderLine) { + t.FilePath = cmSystemTools::RelativeIfUnder(root, t.FilePath); + std::ostringstream s; + s << t; + backtrace.emplace_back(s.str()); + } + } + this->WriteValue("backtrace"_s, backtrace); +} + +void cmConfigureLog::EnsureInit() +{ + if (this->Opened) { + return; + } + assert(!this->Stream.is_open()); + + std::string name = cmStrCat(this->LogDir, "/CMakeConfigureLog.yaml"); + this->Stream.open(name.c_str(), std::ios::out | std::ios::app); + + this->Opened = true; + + this->Stream << "\n---\n"; + this->BeginObject("events"_s); +} + +cmsys::ofstream& cmConfigureLog::BeginLine() +{ + for (unsigned i = 0; i < this->Indent; ++i) { + this->Stream << " "; + } + return this->Stream; +} + +void cmConfigureLog::EndLine() +{ + this->Stream << std::endl; +} + +void cmConfigureLog::BeginObject(cm::string_view key) +{ + this->BeginLine() << key << ':'; + this->EndLine(); + ++this->Indent; +} + +void cmConfigureLog::EndObject() +{ + assert(this->Indent); + --this->Indent; +} + +void cmConfigureLog::BeginEvent(std::string const& kind) +{ + this->EnsureInit(); + + this->BeginLine() << '-'; + this->EndLine(); + + ++this->Indent; + + this->WriteValue("kind"_s, kind); +} + +void cmConfigureLog::EndEvent() +{ + assert(this->Indent); + --this->Indent; +} + +void cmConfigureLog::WriteValue(cm::string_view key, std::nullptr_t) +{ + this->BeginLine() << key << ": null"; + this->EndLine(); +} + +void cmConfigureLog::WriteValue(cm::string_view key, bool value) +{ + this->BeginLine() << key << ": " << (value ? "true" : "false"); + this->EndLine(); +} + +void cmConfigureLog::WriteValue(cm::string_view key, int value) +{ + this->BeginLine() << key << ": " << value; + this->EndLine(); +} + +void cmConfigureLog::WriteValue(cm::string_view key, std::string const& value) +{ + this->BeginLine() << key << ": "; + this->Encoder->write(value, &this->Stream); + this->EndLine(); +} + +void cmConfigureLog::WriteValue(cm::string_view key, + std::vector<std::string> const& list) +{ + this->BeginObject(key); + for (auto const& value : list) { + this->BeginLine() << "- "; + this->Encoder->write(value, &this->Stream); + this->EndLine(); + } + this->EndObject(); +} + +void cmConfigureLog::WriteLiteralTextBlock(cm::string_view key, + cm::string_view text) +{ + this->BeginLine() << key << ": |"; + this->EndLine(); + + auto const l = text.length(); + if (l) { + ++this->Indent; + this->BeginLine(); + + auto i = decltype(l){ 0 }; + while (i < l) { + // YAML allows ' ', '\t' and "printable characters", but NOT other + // ASCII whitespace; those must be escaped, as must the upper UNICODE + // control characters (U+0080 - U+009F) + static constexpr unsigned int C1_LAST = 0x9F; + auto const c = static_cast<unsigned char>(text[i]); + switch (c) { + case '\r': + // Print a carriage return only if it is not followed by a line feed. + ++i; + if (i == l || text[i] != '\n') { + this->WriteEscape(c); + } + break; + case '\n': + // Print any line feeds except the very last one + if (i + 1 < l) { + this->EndLine(); + this->BeginLine(); + } + ++i; + break; + case '\t': + // Print horizontal tab verbatim + this->Stream.put('\t'); + ++i; + break; + case '\\': + // Escape backslash for disambiguation + this->Stream << "\\\\"; + ++i; + break; + default: + if (c >= 32 && c < 127) { + // Print ascii byte. + this->Stream.put(text[i]); + ++i; + break; + } else if (c > 127) { + // Decode a UTF-8 sequence. + unsigned int c32; + auto const* const s = text.data() + i; + auto const* const e = text.data() + l; + auto const* const n = cm_utf8_decode_character(s, e, &c32); + if (n > s && c32 > C1_LAST) { + auto const k = std::distance(s, n); + this->Stream.write(s, static_cast<std::streamsize>(k)); + i += static_cast<unsigned>(k); + break; + } + } + + // Escape non-printable byte. + this->WriteEscape(c); + ++i; + break; + } + } + + this->EndLine(); + --this->Indent; + } +} + +void cmConfigureLog::WriteEscape(unsigned char c) +{ + char buffer[6]; + int n = snprintf(buffer, sizeof(buffer), "\\x%02x", c); + if (n > 0) { + this->Stream.write(buffer, n); + } +} diff --git a/Source/cmConfigureLog.h b/Source/cmConfigureLog.h new file mode 100644 index 0000000..9caac66 --- /dev/null +++ b/Source/cmConfigureLog.h @@ -0,0 +1,68 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing for details. */ +#pragma once + +#include <memory> +#include <string> +#include <vector> + +#include <cm/string_view> + +#include "cmsys/FStream.hxx" + +namespace Json { +class StreamWriter; +} + +class cmMakefile; + +class cmConfigureLog +{ +public: + /** Construct with the log directory and a sorted list of enabled log + versions. The latest log version will be enabled regardless. */ + cmConfigureLog(std::string logDir, std::vector<unsigned long> logVersions); + ~cmConfigureLog(); + + /** Return true if at least one of the log versions in the given sorted + list is enabled. */ + bool IsAnyLogVersionEnabled(std::vector<unsigned long> const& v) const; + + void WriteBacktrace(cmMakefile const& mf); + + void EnsureInit(); + + void BeginEvent(std::string const& kind); + void EndEvent(); + + void BeginObject(cm::string_view key); + void EndObject(); + + // TODO other value types + void WriteValue(cm::string_view key, std::nullptr_t); + void WriteValue(cm::string_view key, bool value); + void WriteValue(cm::string_view key, int value); + void WriteValue(cm::string_view key, std::string const& value); + void WriteValue(cm::string_view key, std::vector<std::string> const& list); + + void WriteTextBlock(cm::string_view key, cm::string_view text); + void WriteLiteralTextBlock(cm::string_view key, cm::string_view text); + + void WriteLiteralTextBlock(cm::string_view key, std::string const& text) + { + this->WriteLiteralTextBlock(key, cm::string_view{ text }); + } + +private: + std::string LogDir; + std::vector<unsigned long> LogVersions; + cmsys::ofstream Stream; + unsigned Indent = 0; + bool Opened = false; + + std::unique_ptr<Json::StreamWriter> Encoder; + + cmsys::ofstream& BeginLine(); + void EndLine(); + void WriteEscape(unsigned char c); +}; diff --git a/Source/cmCoreTryCompile.cxx b/Source/cmCoreTryCompile.cxx index b44111d..25a0e2d 100644 --- a/Source/cmCoreTryCompile.cxx +++ b/Source/cmCoreTryCompile.cxx @@ -16,6 +16,7 @@ #include "cmsys/FStream.hxx" #include "cmArgumentParser.h" +#include "cmConfigureLog.h" #include "cmExportTryCompileFileGenerator.h" #include "cmGlobalGenerator.h" #include "cmMakefile.h" @@ -285,8 +286,8 @@ Arguments cmCoreTryCompile::ParseArgs( return arguments; } -bool cmCoreTryCompile::TryCompileCode(Arguments& arguments, - cmStateEnums::TargetType targetType) +cm::optional<cmTryCompileResult> cmCoreTryCompile::TryCompileCode( + Arguments& arguments, cmStateEnums::TargetType targetType) { this->OutputFile.clear(); // which signature were we called with ? @@ -302,7 +303,7 @@ bool cmCoreTryCompile::TryCompileCode(Arguments& arguments, arguments.SourceDirectoryOrFile->empty()) { this->Makefile->IssueMessage(MessageType::FATAL_ERROR, "No <srcdir> specified."); - return false; + return cm::nullopt; } sourceDirectory = *arguments.SourceDirectoryOrFile; projectName = *arguments.ProjectName; @@ -322,7 +323,7 @@ bool cmCoreTryCompile::TryCompileCode(Arguments& arguments, if (!arguments.BinaryDirectory || arguments.BinaryDirectory->empty()) { this->Makefile->IssueMessage(MessageType::FATAL_ERROR, "No <bindir> specified."); - return false; + return cm::nullopt; } if (*arguments.BinaryDirectory == unique_binary_directory) { // leave empty until we're ready to create it, so we don't try to remove @@ -335,7 +336,7 @@ bool cmCoreTryCompile::TryCompileCode(Arguments& arguments, MessageType::FATAL_ERROR, cmStrCat("<bindir> is not an absolute path:\n '", *arguments.BinaryDirectory, "'")); - return false; + return cm::nullopt; } this->BinaryDirectory = *arguments.BinaryDirectory; // compute the binary dir when TRY_COMPILE is called with a src file @@ -367,7 +368,7 @@ bool cmCoreTryCompile::TryCompileCode(Arguments& arguments, "IMPORTED LINK_LIBRARIES. Got ", tgt->GetName(), " of type ", cmState::GetTargetTypeName(tgt->GetType()), ".")); - return false; + return cm::nullopt; } if (tgt->IsImported()) { targets.emplace_back(i); @@ -379,28 +380,28 @@ bool cmCoreTryCompile::TryCompileCode(Arguments& arguments, if (arguments.CopyFileTo && arguments.CopyFileTo->empty()) { this->Makefile->IssueMessage(MessageType::FATAL_ERROR, "COPY_FILE must be followed by a file path"); - return false; + return cm::nullopt; } if (arguments.CopyFileError && arguments.CopyFileError->empty()) { this->Makefile->IssueMessage( MessageType::FATAL_ERROR, "COPY_FILE_ERROR must be followed by a variable name"); - return false; + return cm::nullopt; } if (arguments.CopyFileError && !arguments.CopyFileTo) { this->Makefile->IssueMessage( MessageType::FATAL_ERROR, "COPY_FILE_ERROR may be used only with COPY_FILE"); - return false; + return cm::nullopt; } if (arguments.Sources && arguments.Sources->empty()) { this->Makefile->IssueMessage( MessageType::FATAL_ERROR, "SOURCES must be followed by at least one source file"); - return false; + return cm::nullopt; } if (this->SrcFileSignature) { @@ -409,19 +410,19 @@ bool cmCoreTryCompile::TryCompileCode(Arguments& arguments, this->Makefile->IssueMessage( MessageType::FATAL_ERROR, "SOURCE_FROM_CONTENT requires exactly two arguments"); - return false; + return cm::nullopt; } if (arguments.SourceFromVar && arguments.SourceFromVar->size() % 2) { this->Makefile->IssueMessage( MessageType::FATAL_ERROR, "SOURCE_FROM_VAR requires exactly two arguments"); - return false; + return cm::nullopt; } if (arguments.SourceFromFile && arguments.SourceFromFile->size() % 2) { this->Makefile->IssueMessage( MessageType::FATAL_ERROR, "SOURCE_FROM_FILE requires exactly two arguments"); - return false; + return cm::nullopt; } } else { // only valid for srcfile signatures @@ -430,19 +431,19 @@ bool cmCoreTryCompile::TryCompileCode(Arguments& arguments, MessageType::FATAL_ERROR, cmStrCat(arguments.LangProps.begin()->first, " allowed only in source file signature")); - return false; + return cm::nullopt; } if (!arguments.CompileDefs.empty()) { this->Makefile->IssueMessage( MessageType::FATAL_ERROR, "COMPILE_DEFINITIONS allowed only in source file signature"); - return false; + return cm::nullopt; } if (arguments.CopyFileTo) { this->Makefile->IssueMessage( MessageType::FATAL_ERROR, "COPY_FILE allowed only in source file signature"); - return false; + return cm::nullopt; } } @@ -462,7 +463,7 @@ bool cmCoreTryCompile::TryCompileCode(Arguments& arguments, e << "Attempt at a recursive or nested TRY_COMPILE in directory\n" << " " << this->BinaryDirectory << "\n"; this->Makefile->IssueMessage(MessageType::FATAL_ERROR, e.str()); - return false; + return cm::nullopt; } std::string outFileName = this->BinaryDirectory + "/CMakeLists.txt"; @@ -486,7 +487,7 @@ bool cmCoreTryCompile::TryCompileCode(Arguments& arguments, const auto& content = (*arguments.SourceFromContent)[i + 1]; auto out = this->WriteSource(name, content, "SOURCE_FROM_CONTENT"); if (out.empty()) { - return false; + return cm::nullopt; } sources.emplace_back(std::move(out)); } @@ -499,7 +500,7 @@ bool cmCoreTryCompile::TryCompileCode(Arguments& arguments, const auto& content = this->Makefile->GetDefinition(var); auto out = this->WriteSource(name, content, "SOURCE_FROM_VAR"); if (out.empty()) { - return false; + return cm::nullopt; } sources.emplace_back(std::move(out)); } @@ -514,7 +515,7 @@ bool cmCoreTryCompile::TryCompileCode(Arguments& arguments, const auto& msg = cmStrCat("SOURCE_FROM_FILE given invalid filename \"", dst, "\""); this->Makefile->IssueMessage(MessageType::FATAL_ERROR, msg); - return false; + return cm::nullopt; } auto dstPath = cmStrCat(this->BinaryDirectory, "/", dst); @@ -523,7 +524,7 @@ bool cmCoreTryCompile::TryCompileCode(Arguments& arguments, const auto& msg = cmStrCat("SOURCE_FROM_FILE failed to copy \"", src, "\": ", result.GetString()); this->Makefile->IssueMessage(MessageType::FATAL_ERROR, msg); - return false; + return cm::nullopt; } sources.emplace_back(std::move(dstPath)); @@ -550,7 +551,7 @@ bool cmCoreTryCompile::TryCompileCode(Arguments& arguments, err << cmJoin(langs, " "); err << "\nSee project() command to enable other languages."; this->Makefile->IssueMessage(MessageType::FATAL_ERROR, err.str()); - return false; + return cm::nullopt; } } @@ -577,7 +578,7 @@ bool cmCoreTryCompile::TryCompileCode(Arguments& arguments, << cmSystemTools::GetLastSystemError(); /* clang-format on */ this->Makefile->IssueMessage(MessageType::FATAL_ERROR, e.str()); - return false; + return cm::nullopt; } cmValue def = this->Makefile->GetDefinition("CMAKE_MODULE_PATH"); @@ -778,7 +779,7 @@ bool cmCoreTryCompile::TryCompileCode(Arguments& arguments, this->Makefile->IssueMessage(MessageType::FATAL_ERROR, "could not write export file."); fclose(fout); - return false; + return cm::nullopt; } fprintf(fout, "\ninclude(\"${CMAKE_CURRENT_LIST_DIR}/%s\")\n\n", fname.c_str()); @@ -1111,7 +1112,7 @@ bool cmCoreTryCompile::TryCompileCode(Arguments& arguments, } if (!arguments.CopyFileError) { this->Makefile->IssueMessage(MessageType::FATAL_ERROR, emsg.str()); - return false; + return cm::nullopt; } copyFileErrorMessage = emsg.str(); } @@ -1122,7 +1123,15 @@ bool cmCoreTryCompile::TryCompileCode(Arguments& arguments, this->Makefile->AddDefinition(copyFileError, copyFileErrorMessage); } } - return res == 0; + + cmTryCompileResult result; + result.SourceDirectory = sourceDirectory; + result.BinaryDirectory = this->BinaryDirectory; + result.Variable = *arguments.CompileResultVariable; + result.VariableCached = !arguments.NoCache; + result.Output = std::move(output); + result.ExitCode = res; + return result; } bool cmCoreTryCompile::IsTemporary(std::string const& path) @@ -1263,3 +1272,20 @@ std::string cmCoreTryCompile::WriteSource(std::string const& filename, file.close(); return filepath; } + +void cmCoreTryCompile::WriteTryCompileEventFields( + cmConfigureLog& log, cmTryCompileResult const& compileResult) +{ +#ifndef CMAKE_BOOTSTRAP + log.BeginObject("directories"_s); + log.WriteValue("source"_s, compileResult.SourceDirectory); + log.WriteValue("binary"_s, compileResult.BinaryDirectory); + log.EndObject(); + log.BeginObject("buildResult"_s); + log.WriteValue("variable"_s, compileResult.Variable); + log.WriteValue("cached"_s, compileResult.VariableCached); + log.WriteLiteralTextBlock("stdout"_s, compileResult.Output); + log.WriteValue("exitCode"_s, compileResult.ExitCode); + log.EndObject(); +#endif +} diff --git a/Source/cmCoreTryCompile.h b/Source/cmCoreTryCompile.h index 3e1e12c..6d29586 100644 --- a/Source/cmCoreTryCompile.h +++ b/Source/cmCoreTryCompile.h @@ -14,10 +14,23 @@ #include "cmArgumentParserTypes.h" #include "cmStateTypes.h" +class cmConfigureLog; class cmMakefile; template <typename Iter> class cmRange; +struct cmTryCompileResult +{ + std::string SourceDirectory; + std::string BinaryDirectory; + + bool VariableCached = true; + std::string Variable; + + std::string Output; + int ExitCode = 1; +}; + /** \class cmCoreTryCompile * \brief Base class for cmTryCompileCommand and cmTryRunCommand * @@ -80,8 +93,8 @@ public: * This function requires at least two \p arguments and will crash if given * fewer. */ - bool TryCompileCode(Arguments& arguments, - cmStateEnums::TargetType targetType); + cm::optional<cmTryCompileResult> TryCompileCode( + Arguments& arguments, cmStateEnums::TargetType targetType); /** * Returns \c true if \p path resides within a CMake temporary directory, @@ -103,6 +116,9 @@ public: */ void FindOutputFile(const std::string& targetName); + static void WriteTryCompileEventFields( + cmConfigureLog& log, cmTryCompileResult const& compileResult); + std::string BinaryDirectory; std::string OutputFile; std::string FindErrorMessage; diff --git a/Source/cmTryCompileCommand.cxx b/Source/cmTryCompileCommand.cxx index a2c4ce1..eff21cc 100644 --- a/Source/cmTryCompileCommand.cxx +++ b/Source/cmTryCompileCommand.cxx @@ -2,6 +2,9 @@ file Copyright.txt or https://cmake.org/licensing for details. */ #include "cmTryCompileCommand.h" +#include <cm/optional> + +#include "cmConfigureLog.h" #include "cmCoreTryCompile.h" #include "cmExecutionStatus.h" #include "cmMakefile.h" @@ -13,6 +16,23 @@ #include "cmValue.h" #include "cmake.h" +namespace { +#ifndef CMAKE_BOOTSTRAP +void WriteTryCompileEvent(cmConfigureLog& log, cmMakefile const& mf, + cmTryCompileResult const& compileResult) +{ + static const std::vector<unsigned long> LogVersionsWithTryCompileV1{ 1 }; + + if (log.IsAnyLogVersionEnabled(LogVersionsWithTryCompileV1)) { + log.BeginEvent("try_compile-v1"); + log.WriteBacktrace(mf); + cmCoreTryCompile::WriteTryCompileEventFields(log, compileResult); + log.EndEvent(); + } +} +#endif +} + bool cmTryCompileCommand(std::vector<std::string> const& args, cmExecutionStatus& status) { @@ -59,7 +79,15 @@ bool cmTryCompileCommand(std::vector<std::string> const& args, if (!arguments) { return true; } - tc.TryCompileCode(arguments, targetType); + + if (cm::optional<cmTryCompileResult> compileResult = + tc.TryCompileCode(arguments, targetType)) { +#ifndef CMAKE_BOOTSTRAP + if (cmConfigureLog* log = mf.GetCMakeInstance()->GetConfigureLog()) { + WriteTryCompileEvent(*log, mf, *compileResult); + } +#endif + } // if They specified clean then we clean up what we can if (tc.SrcFileSignature) { diff --git a/Source/cmTryRunCommand.cxx b/Source/cmTryRunCommand.cxx index 1e81195..63e4478 100644 --- a/Source/cmTryRunCommand.cxx +++ b/Source/cmTryRunCommand.cxx @@ -3,12 +3,15 @@ #include "cmTryRunCommand.h" #include <cstdio> +#include <stdexcept> #include <cm/optional> +#include <cmext/string_view> #include "cmsys/FStream.hxx" #include "cmArgumentParserTypes.h" +#include "cmConfigureLog.h" #include "cmCoreTryCompile.h" #include "cmDuration.h" #include "cmExecutionStatus.h" @@ -23,6 +26,48 @@ #include "cmake.h" namespace { +struct cmTryRunResult +{ + bool VariableCached = true; + std::string Variable; + cm::optional<std::string> Stdout; + cm::optional<std::string> Stderr; + cm::optional<std::string> ExitCode; +}; + +#ifndef CMAKE_BOOTSTRAP +void WriteTryRunEvent(cmConfigureLog& log, cmMakefile const& mf, + cmTryCompileResult const& compileResult, + cmTryRunResult const& runResult) +{ + static const std::vector<unsigned long> LogVersionsWithTryRunV1{ 1 }; + + if (log.IsAnyLogVersionEnabled(LogVersionsWithTryRunV1)) { + log.BeginEvent("try_run-v1"); + log.WriteBacktrace(mf); + cmCoreTryCompile::WriteTryCompileEventFields(log, compileResult); + + log.BeginObject("runResult"_s); + log.WriteValue("variable"_s, runResult.Variable); + log.WriteValue("cached"_s, runResult.VariableCached); + if (runResult.Stdout) { + log.WriteLiteralTextBlock("stdout"_s, *runResult.Stdout); + } + if (runResult.Stderr) { + log.WriteLiteralTextBlock("stderr"_s, *runResult.Stderr); + } + if (runResult.ExitCode) { + try { + log.WriteValue("exitCode"_s, std::stoi(*runResult.ExitCode)); + } catch (std::invalid_argument const&) { + log.WriteValue("exitCode"_s, *runResult.ExitCode); + } + } + log.EndObject(); + log.EndEvent(); + } +} +#endif class TryRunCommandImpl : public cmCoreTryCompile { @@ -96,23 +141,35 @@ bool TryRunCommandImpl::TryRunCode(std::vector<std::string> const& argv) } bool captureRunOutput = false; - bool captureRunOutputStdOutErr = false; if (arguments.OutputVariable) { captureRunOutput = true; } else if (arguments.CompileOutputVariable) { arguments.OutputVariable = arguments.CompileOutputVariable; } - if (arguments.RunOutputStdOutVariable || arguments.RunOutputStdErrVariable) { - captureRunOutputStdOutErr = true; - } else if (arguments.RunOutputVariable) { - captureRunOutput = true; + + // Capture the split output for the configure log unless the caller + // requests combined output to be captured by a variable. + bool captureRunOutputStdOutErr = true; + if (!arguments.RunOutputStdOutVariable && + !arguments.RunOutputStdErrVariable) { + if (arguments.RunOutputVariable) { + captureRunOutput = true; + captureRunOutputStdOutErr = false; + } else if (arguments.OutputVariable) { + captureRunOutputStdOutErr = false; + } } // do the try compile - bool compiled = this->TryCompileCode(arguments, cmStateEnums::EXECUTABLE); + cm::optional<cmTryCompileResult> compileResult = + this->TryCompileCode(arguments, cmStateEnums::EXECUTABLE); + + cmTryRunResult runResult; + runResult.Variable = this->RunResultVariable; + runResult.VariableCached = !arguments.NoCache; // now try running the command if it compiled - if (compiled) { + if (compileResult && compileResult->ExitCode == 0) { if (this->OutputFile.empty()) { cmSystemTools::Error(this->FindErrorMessage); } else { @@ -131,22 +188,26 @@ bool TryRunCommandImpl::TryRunCode(std::vector<std::string> const& argv) runArgs, *arguments.SourceDirectoryOrFile, *arguments.CompileResultVariable, captureRunOutput ? &runOutputContents : nullptr, - captureRunOutputStdOutErr && arguments.RunOutputStdOutVariable - ? &runOutputStdOutContents - : nullptr, - captureRunOutputStdOutErr && arguments.RunOutputStdErrVariable - ? &runOutputStdErrContents - : nullptr); + captureRunOutputStdOutErr ? &runOutputStdOutContents : nullptr, + captureRunOutputStdOutErr ? &runOutputStdErrContents : nullptr); } else { this->RunExecutable( runArgs, arguments.RunWorkingDirectory, captureRunOutput ? &runOutputContents : nullptr, - captureRunOutputStdOutErr && arguments.RunOutputStdOutVariable - ? &runOutputStdOutContents - : nullptr, - captureRunOutputStdOutErr && arguments.RunOutputStdErrVariable - ? &runOutputStdErrContents - : nullptr); + captureRunOutputStdOutErr ? &runOutputStdOutContents : nullptr, + captureRunOutputStdOutErr ? &runOutputStdErrContents : nullptr); + } + + if (captureRunOutputStdOutErr) { + runResult.Stdout = runOutputStdOutContents; + runResult.Stderr = runOutputStdErrContents; + } else { + runResult.Stdout = runOutputContents; + } + + if (cmValue ec = + this->Makefile->GetDefinition(this->RunResultVariable)) { + runResult.ExitCode = *ec; } // now put the output into the variables @@ -177,6 +238,15 @@ bool TryRunCommandImpl::TryRunCode(std::vector<std::string> const& argv) } } +#ifndef CMAKE_BOOTSTRAP + if (compileResult) { + cmMakefile const& mf = *(this->Makefile); + if (cmConfigureLog* log = mf.GetCMakeInstance()->GetConfigureLog()) { + WriteTryRunEvent(*log, mf, *compileResult, runResult); + } + } +#endif + // if we created a directory etc, then cleanup after ourselves if (!this->Makefile->GetCMakeInstance()->GetDebugTryCompile()) { this->CleanupFiles(this->BinaryDirectory); diff --git a/Source/cmake.cxx b/Source/cmake.cxx index befcb55..ee63909 100644 --- a/Source/cmake.cxx +++ b/Source/cmake.cxx @@ -70,6 +70,7 @@ # include <cm3p/curl/curl.h> # include <cm3p/json/writer.h> +# include "cmConfigureLog.h" # include "cmFileAPI.h" # include "cmGraphVizWriter.h" # include "cmVariableWatch.h" @@ -2423,10 +2424,22 @@ int cmake::ActualConfigure() #if !defined(CMAKE_BOOTSTRAP) this->FileAPI = cm::make_unique<cmFileAPI>(this); this->FileAPI->ReadQueries(); + + if (!this->GetIsInTryCompile()) { + this->TruncateOutputLog("CMakeConfigureLog.yaml"); + this->ConfigureLog = cm::make_unique<cmConfigureLog>( + cmStrCat(this->GetHomeOutputDirectory(), "/CMakeFiles"_s), + std::vector<unsigned long>()); + } #endif // actually do the configure this->GlobalGenerator->Configure(); + +#if !defined(CMAKE_BOOTSTRAP) + this->ConfigureLog.reset(); +#endif + // Before saving the cache // if the project did not define one of the entries below, add them now // so users can edit the values in the cache: diff --git a/Source/cmake.h b/Source/cmake.h index 325c0c5..10db87d 100644 --- a/Source/cmake.h +++ b/Source/cmake.h @@ -37,6 +37,7 @@ # include "cmMakefileProfilingData.h" #endif +class cmConfigureLog; class cmExternalMakefileProjectGeneratorFactory; class cmFileAPI; class cmFileTimeCache; @@ -521,6 +522,10 @@ public: void SetTraceFile(std::string const& file); void PrintTraceFormatVersion(); +#ifndef CMAKE_BOOTSTRAP + cmConfigureLog* GetConfigureLog() const { return this->ConfigureLog.get(); } +#endif + //! Use trace from another ::cmake instance. void SetTraceRedirect(cmake* other); @@ -714,6 +719,9 @@ private: TraceFormat TraceFormatVar = TRACE_HUMAN; cmGeneratedFileStream TraceFile; cmake* TraceRedirect = nullptr; +#ifndef CMAKE_BOOTSTRAP + std::unique_ptr<cmConfigureLog> ConfigureLog; +#endif bool WarnUninitialized = false; bool WarnUnusedCli = true; bool CheckSystemVars = false; |