diff options
-rw-r--r-- | Help/index.rst | 1 | ||||
-rw-r--r-- | Help/manual/cmake-configure-log.7.rst | 254 | ||||
-rw-r--r-- | Help/release/dev/configure-log.rst | 5 | ||||
-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 | ||||
-rw-r--r-- | Tests/RunCMake/RunCMake.cmake | 66 | ||||
-rw-r--r-- | Tests/RunCMake/try_compile/Inspect-config.txt | 34 | ||||
-rw-r--r-- | Tests/RunCMake/try_compile/SourceFromBadName-config.txt | 1 | ||||
-rw-r--r-- | Tests/RunCMake/try_run/ConfigureLog-bad.c | 1 | ||||
-rw-r--r-- | Tests/RunCMake/try_run/ConfigureLog-config.txt | 96 | ||||
-rw-r--r-- | Tests/RunCMake/try_run/ConfigureLog-test.c | 9 | ||||
-rw-r--r-- | Tests/RunCMake/try_run/ConfigureLog.cmake | 18 | ||||
-rw-r--r-- | Tests/RunCMake/try_run/RunCMakeTest.cmake | 1 |
20 files changed, 995 insertions, 79 deletions
diff --git a/Help/index.rst b/Help/index.rst index fdbf847..16c8f25 100644 --- a/Help/index.rst +++ b/Help/index.rst @@ -57,6 +57,7 @@ Reference Manuals /manual/cmake-buildsystem.7 /manual/cmake-commands.7 /manual/cmake-compile-features.7 + /manual/cmake-configure-log.7 /manual/cmake-developer.7 /manual/cmake-env-variables.7 /manual/cmake-file-api.7 diff --git a/Help/manual/cmake-configure-log.7.rst b/Help/manual/cmake-configure-log.7.rst new file mode 100644 index 0000000..7f395f5 --- /dev/null +++ b/Help/manual/cmake-configure-log.7.rst @@ -0,0 +1,254 @@ +.. cmake-manual-description: CMake Configure Log + +cmake-configure-log(7) +********************** + +.. versionadded:: 3.26 + +.. only:: html + + .. contents:: + +Introduction +============ + +CMake writes a running log, known as the configure log, +of certain events that occur during the "configure" step. +The log file is located at:: + + ${CMAKE_BINARY_DIR}/CMakeFiles/CMakeConfigureLog.yaml + +The configure log does *not* contain a log of all output, errors, +or messages printed while configuring a project. It is a log of +detailed information about specific events, such as toolchain inspection +by :command:`try_compile`, meant for use in debugging the configuration +of a build tree. + +Log Structure +============= + +The configure log is designed to be both machine- and human-readable. + +The log file is a YAML document stream containing zero or more YAML +documents separated by document markers. Each document begins +with a ``---`` document marker line, contains a single YAML mapping +that logs events from one CMake "configure" step, and, if the configure +step finished normally, ends with a ``...`` document marker line: + +.. code-block:: yaml + + --- + events: + - + kind: "try_compile-v1" + # (other fields omitted) + - + kind: "try_compile-v1" + # (other fields omitted) + ... + +A new document is appended to the log every time CMake configures +the build tree and logs new events. + +The keys of the each document root mapping are: + +``events`` + A YAML block sequence of nodes corresponding to events logged during + one CMake "configure" step. Each event is a YAML node containing one + of the `Event Kinds`_ documented below. + +Log Versioning +-------------- + +Each of the `Event Kinds`_ is versioned independently. The set of +keys an event's log entry provides is specific to its major version. +When an event is logged, the latest version of its event kind that is +known to the running version of CMake is always written to the log. + +Tools reading the configure log must ignore event kinds and versions +they do not understand: + +* A future version of CMake may introduce a new event kind or version. + +* If an existing build tree is re-configured with a different version of + CMake, the log may contain different versions of the same event kind. + +Text Block Encoding +------------------- + +In order to make the log human-readable, text blocks are always +represented using YAML literal block scalars (``|``). +Since literal block scalars do not support escaping, backslashes +and non-printable characters are encoded at the application layer: + +* ``\\`` encodes a backslash. +* ``\xXX`` encodes a byte using two hexadecimal digits, ``XX``. + +.. _`configure-log event kinds`: + +Event Kinds +=========== + +Every event kind is represented by a YAML mapping of the form: + +.. code-block:: yaml + + kind: "<kind>-v<major>" + backtrace: + - "<file>:<line> (<function>)" + #...event-specific keys... + +The keys common to all events are: + +``kind`` + A string identifying the event kind and major version. + +``backtrace`` + A YAML block sequence reporting the call stack of CMake source + locations at which the event occurred. Each node is a string + specifying one location formatted as ``<file>:<line> (<function>)``. + +Additional mapping keys are specific to each (versioned) event kind, +described below. + +Event Kind ``try_compile`` +-------------------------- + +The :command:`try_compile` command logs ``try_compile`` events. + +There is only one ``try_compile`` event major version, version 1. + +.. _`try_compile-v1 event`: + +``try_compile-v1`` Event +^^^^^^^^^^^^^^^^^^^^^^^^ + +A ``try_compile-v1`` event is a YAML mapping: + +.. code-block:: yaml + + kind: "try_compile-v1" + backtrace: + - "CMakeLists.txt:123 (try_compile)" + directories: + source: "/path/to/.../TryCompile-01234" + binary: "/path/to/.../TryCompile-01234" + buildResult: + variable: "COMPILE_RESULT" + cached: true + stdout: | + # ... + exitCode: 0 + +The keys specific to ``try_compile-v1`` mappings are: + +``directories`` + A mapping describing the directories associated with the + compilation attempt. It has the following keys: + + ``source`` + String specifying the source directory of the + :command:`try_compile` project. + + ``binary`` + String specifying the binary directory of the + :command:`try_compile` project. + For non-project invocations, this is often the same as + the source directory. + +``buildResult`` + A mapping describing the result of compiling the test code. + It has the following keys: + + ``variable`` + A string specifying the name of the CMake variable + storing the result of trying to build the test project. + + ``cached`` + A boolean indicating whether the above result ``variable`` + is stored in the CMake cache. + + ``stdout`` + A YAML literal block scalar containing the output from building + the test project, represented using our `Text Block Encoding`_. + This contains build output from both stdout and stderr. + + ``exitCode`` + An integer specifying the build tool exit code from trying + to build the test project. + +Event Kind ``try_run`` +---------------------- + +The :command:`try_run` command logs ``try_run`` events. + +There is only one ``try_run`` event major version, version 1. + +.. _`try_run-v1 event`: + +``try_run-v1`` Event +^^^^^^^^^^^^^^^^^^^^ + +A ``try_run-v1`` event is a YAML mapping: + +.. code-block:: yaml + + kind: "try_run-v1" + backtrace: + - "CMakeLists.txt:456 (try_run)" + directories: + source: "/path/to/.../TryCompile-56789" + binary: "/path/to/.../TryCompile-56789" + buildResult: + variable: "COMPILE_RESULT" + cached: true + stdout: | + # ... + exitCode: 0 + runResult: + variable: "RUN_RESULT" + cached: true + stdout: | + # ... + stderr: | + # ... + exitCode: 0 + +The keys specific to ``try_run-v1`` mappings include those +documented by the `try_compile-v1 event`_, plus: + +``runResult`` + A mapping describing the result of running the test code. + It has the following keys: + + ``variable`` + A string specifying the name of the CMake variable + storing the result of trying to run the test executable. + + ``cached`` + A boolean indicating whether the above result ``variable`` + is stored in the CMake cache. + + ``stdout`` + An optional key that is present when the test project built successfully. + Its value is a YAML literal block scalar containing output from running + the test executable, represented using our `Text Block Encoding`_. + + If ``RUN_OUTPUT_VARIABLE`` was used, stdout and stderr are captured + together, so this will contain both. Otherwise, this will contain + only the stdout output. + + ``stderr`` + An optional key that is present when the test project built successfully + and the ``RUN_OUTPUT_VARIABLE`` option was not used. + Its value is a YAML literal block scalar containing output from running + the test executable, represented using our `Text Block Encoding`_. + + If ``RUN_OUTPUT_VARIABLE`` was used, stdout and stderr are captured + together in the ``stdout`` key, and this key will not be present. + Otherwise, this will contain the stderr output. + + ``exitCode`` + An optional key that is present when the test project built successfully. + Its value is an integer specifying the exit code, or a string containing + an error message, from trying to run the test executable. diff --git a/Help/release/dev/configure-log.rst b/Help/release/dev/configure-log.rst new file mode 100644 index 0000000..8518b21 --- /dev/null +++ b/Help/release/dev/configure-log.rst @@ -0,0 +1,5 @@ +Configure Log +------------- + +* CMake now writes a YAML log of configure-time checks. + See the :manual:`cmake-configure-log(7)` manual. 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; diff --git a/Tests/RunCMake/RunCMake.cmake b/Tests/RunCMake/RunCMake.cmake index cc754e8..54d7eb5 100644 --- a/Tests/RunCMake/RunCMake.cmake +++ b/Tests/RunCMake/RunCMake.cmake @@ -33,18 +33,18 @@ function(run_cmake test) set(platform_name msys) endif() - foreach(o IN ITEMS out err) - if(RunCMake-std${o}-file AND EXISTS ${top_src}/${RunCMake-std${o}-file}) - file(READ ${top_src}/${RunCMake-std${o}-file} expect_std${o}) - string(REGEX REPLACE "\n+$" "" expect_std${o} "${expect_std${o}}") - elseif(EXISTS ${top_src}/${test}-std${o}-${platform_name}.txt) - file(READ ${top_src}/${test}-std${o}-${platform_name}.txt expect_std${o}) - string(REGEX REPLACE "\n+$" "" expect_std${o} "${expect_std${o}}") - elseif(EXISTS ${top_src}/${test}-std${o}.txt) - file(READ ${top_src}/${test}-std${o}.txt expect_std${o}) - string(REGEX REPLACE "\n+$" "" expect_std${o} "${expect_std${o}}") + foreach(o IN ITEMS stdout stderr config) + if(RunCMake-${o}-file AND EXISTS ${top_src}/${RunCMake-${o}-file}) + file(READ ${top_src}/${RunCMake-${o}-file} expect_${o}) + string(REGEX REPLACE "\n+$" "" expect_${o} "${expect_${o}}") + elseif(EXISTS ${top_src}/${test}-${o}-${platform_name}.txt) + file(READ ${top_src}/${test}-${o}-${platform_name}.txt expect_${o}) + string(REGEX REPLACE "\n+$" "" expect_${o} "${expect_${o}}") + elseif(EXISTS ${top_src}/${test}-${o}.txt) + file(READ ${top_src}/${test}-${o}.txt expect_${o}) + string(REGEX REPLACE "\n+$" "" expect_${o} "${expect_${o}}") else() - unset(expect_std${o}) + unset(expect_${o}) endif() endforeach() if (NOT expect_stderr) @@ -144,6 +144,12 @@ function(run_cmake test) if(NOT "${actual_result}" MATCHES "${expect_result}") string(APPEND msg "Result is [${actual_result}], not [${expect_result}].\n") endif() + set(config_file "${RunCMake_TEST_COMMAND_WORKING_DIRECTORY}/CMakeFiles/CMakeConfigureLog.yaml") + if(EXISTS "${config_file}") + file(READ "${config_file}" actual_config) + else() + set(actual_config "") + endif() # Special case: remove ninja no-op line from stderr, but not stdout. # Test cases that look for it should use RunCMake_TEST_OUTPUT_MERGE. @@ -180,17 +186,13 @@ function(run_cmake test) "|[^\n]*Bullseye Testing Technology" ")[^\n]*\n)+" ) - foreach(o IN ITEMS out err) - string(REGEX REPLACE "\r\n" "\n" actual_std${o} "${actual_std${o}}") - string(REGEX REPLACE "${ignore_line_regex}" "\\1" actual_std${o} "${actual_std${o}}") - string(REGEX REPLACE "\n+$" "" actual_std${o} "${actual_std${o}}") - set(expect_${o} "") - if(DEFINED expect_std${o}) - if(NOT "${actual_std${o}}" MATCHES "${expect_std${o}}") - string(REGEX REPLACE "\n" "\n expect-${o}> " expect_${o} - " expect-${o}> ${expect_std${o}}") - set(expect_${o} "Expected std${o} to match:\n${expect_${o}}\n") - string(APPEND msg "std${o} does not match that expected.\n") + foreach(o IN ITEMS stdout stderr config) + string(REGEX REPLACE "\r\n" "\n" actual_${o} "${actual_${o}}") + string(REGEX REPLACE "${ignore_line_regex}" "\\1" actual_${o} "${actual_${o}}") + string(REGEX REPLACE "\n+$" "" actual_${o} "${actual_${o}}") + if(DEFINED expect_${o}) + if(NOT "${actual_${o}}" MATCHES "${expect_${o}}") + string(APPEND msg "${o} does not match that expected.\n") endif() endif() endforeach() @@ -215,15 +217,17 @@ function(run_cmake test) string(APPEND msg "Command was:\n command> ${command}\n") endif() if(msg) - string(REGEX REPLACE "\n" "\n actual-out> " actual_out " actual-out> ${actual_stdout}") - string(REGEX REPLACE "\n" "\n actual-err> " actual_err " actual-err> ${actual_stderr}") - message(SEND_ERROR "${test}${RunCMake_TEST_VARIANT_DESCRIPTION} - FAILED:\n" - "${msg}" - "${expect_out}" - "Actual stdout:\n${actual_out}\n" - "${expect_err}" - "Actual stderr:\n${actual_err}\n" - ) + foreach(o IN ITEMS stdout stderr config) + if(DEFINED expect_${o}) + string(REGEX REPLACE "\n" "\n expect-${o}> " expect_${o} " expect-${o}> ${expect_${o}}") + string(APPEND msg "Expected ${o} to match:\n${expect_${o}}\n") + endif() + if(NOT o STREQUAL "config" OR DEFINED expect_${o}) + string(REGEX REPLACE "\n" "\n actual-${o}> " actual_${o} " actual-${o}> ${actual_${o}}") + string(APPEND msg "Actual ${o}:\n${actual_${o}}\n") + endif() + endforeach() + message(SEND_ERROR "${test}${RunCMake_TEST_VARIANT_DESCRIPTION} - FAILED:\n${msg}") else() message(STATUS "${test}${RunCMake_TEST_VARIANT_DESCRIPTION} - PASSED") endif() diff --git a/Tests/RunCMake/try_compile/Inspect-config.txt b/Tests/RunCMake/try_compile/Inspect-config.txt new file mode 100644 index 0000000..47169cf --- /dev/null +++ b/Tests/RunCMake/try_compile/Inspect-config.txt @@ -0,0 +1,34 @@ +^ +--- +events: + - + kind: "try_compile-v1" + backtrace: + - "[^"]*/Modules/CMakeDetermineCompilerABI.cmake:[0-9]+ \(try_compile\)" + - "[^"]*/Modules/CMakeTestCCompiler.cmake:[0-9]+ \(CMAKE_DETERMINE_COMPILER_ABI\)" + - "Inspect.cmake:[0-9]+ \(enable_language\)" + - "CMakeLists.txt:[0-9]+ \(include\)" + directories: + source: "[^"]*/Tests/RunCMake/try_compile/Inspect-build/CMakeFiles/CMakeScratch/TryCompile-[^/]+" + binary: "[^"]*/Tests/RunCMake/try_compile/Inspect-build/CMakeFiles/CMakeScratch/TryCompile-[^/]+" + buildResult: + variable: "CMAKE_C_ABI_COMPILED" + cached: true + stdout: \|.* + exitCode: 0 + - + kind: "try_compile-v1" + backtrace: + - "[^"]*/Modules/CMakeDetermineCompilerABI.cmake:[0-9]+ \(try_compile\)" + - "[^"]*/Modules/CMakeTestCXXCompiler.cmake:[0-9]+ \(CMAKE_DETERMINE_COMPILER_ABI\)" + - "Inspect.cmake:[0-9]+ \(enable_language\)" + - "CMakeLists.txt:[0-9]+ \(include\)" + directories: + source: "[^"]*/Tests/RunCMake/try_compile/Inspect-build/CMakeFiles/CMakeScratch/TryCompile-[^/]+" + binary: "[^"]*/Tests/RunCMake/try_compile/Inspect-build/CMakeFiles/CMakeScratch/TryCompile-[^/]+" + buildResult: + variable: "CMAKE_CXX_ABI_COMPILED" + cached: true + stdout: \|.* + exitCode: 0 +\.\.\.$ diff --git a/Tests/RunCMake/try_compile/SourceFromBadName-config.txt b/Tests/RunCMake/try_compile/SourceFromBadName-config.txt new file mode 100644 index 0000000..10f3293 --- /dev/null +++ b/Tests/RunCMake/try_compile/SourceFromBadName-config.txt @@ -0,0 +1 @@ +^$ diff --git a/Tests/RunCMake/try_run/ConfigureLog-bad.c b/Tests/RunCMake/try_run/ConfigureLog-bad.c new file mode 100644 index 0000000..6508ead --- /dev/null +++ b/Tests/RunCMake/try_run/ConfigureLog-bad.c @@ -0,0 +1 @@ +#error "This does not compile!" diff --git a/Tests/RunCMake/try_run/ConfigureLog-config.txt b/Tests/RunCMake/try_run/ConfigureLog-config.txt new file mode 100644 index 0000000..602437e --- /dev/null +++ b/Tests/RunCMake/try_run/ConfigureLog-config.txt @@ -0,0 +1,96 @@ +^ +--- +events: + - + kind: "try_compile-v1" + backtrace: + - "[^"]*/Modules/CMakeDetermineCompilerABI.cmake:[0-9]+ \(try_compile\)" + - "[^"]*/Modules/CMakeTestCCompiler.cmake:[0-9]+ \(CMAKE_DETERMINE_COMPILER_ABI\)" + - "CMakeLists.txt:[0-9]+ \(project\)" + directories: + source: "[^"]*/Tests/RunCMake/try_run/ConfigureLog-build/CMakeFiles/CMakeScratch/TryCompile-[^/]+" + binary: "[^"]*/Tests/RunCMake/try_run/ConfigureLog-build/CMakeFiles/CMakeScratch/TryCompile-[^/]+" + buildResult: + variable: "CMAKE_C_ABI_COMPILED" + cached: true + stdout: \|.* + exitCode: 0 + - + kind: "try_run-v1" + backtrace: + - "ConfigureLog.cmake:[0-9]+ \(try_run\)" + - "CMakeLists.txt:[0-9]+ \(include\)" + directories: + source: "[^"]*/Tests/RunCMake/try_run/ConfigureLog-build/CMakeFiles/CMakeScratch/TryCompile-[^/]+" + binary: "[^"]*/Tests/RunCMake/try_run/ConfigureLog-build/CMakeFiles/CMakeScratch/TryCompile-[^/]+" + buildResult: + variable: "COMPILE_RESULT" + cached: true + stdout: \|.* + exitCode: [1-9][0-9]* + runResult: + variable: "RUN_RESULT" + cached: true + - + kind: "try_run-v1" + backtrace: + - "ConfigureLog.cmake:[0-9]+ \(try_run\)" + - "CMakeLists.txt:[0-9]+ \(include\)" + directories: + source: "[^"]*/Tests/RunCMake/try_run/ConfigureLog-build/CMakeFiles/CMakeScratch/TryCompile-[^/]+" + binary: "[^"]*/Tests/RunCMake/try_run/ConfigureLog-build/CMakeFiles/CMakeScratch/TryCompile-[^/]+" + buildResult: + variable: "COMPILE_RESULT" + cached: true + stdout: \|.* + exitCode: 0 + runResult: + variable: "RUN_RESULT" + cached: true + stdout: \| + Output on stdout! + stderr: \| + Output, with backslash '\\\\', on stderr! + exitCode: 12 + - + kind: "try_run-v1" + backtrace: + - "ConfigureLog.cmake:[0-9]+ \(try_run\)" + - "CMakeLists.txt:[0-9]+ \(include\)" + directories: + source: "[^"]*/Tests/RunCMake/try_run/ConfigureLog-build/CMakeFiles/CMakeScratch/TryCompile-[^/]+" + binary: "[^"]*/Tests/RunCMake/try_run/ConfigureLog-build/CMakeFiles/CMakeScratch/TryCompile-[^/]+" + buildResult: + variable: "COMPILE_RESULT" + cached: true + stdout: \|.* + exitCode: 0 + runResult: + variable: "RUN_RESULT" + cached: true + stdout: \| + Output, with backslash '\\\\', on stderr! + Output on stdout! + exitCode: 12 + - + kind: "try_run-v1" + backtrace: + - "ConfigureLog.cmake:[0-9]+ \(try_run\)" + - "CMakeLists.txt:[0-9]+ \(include\)" + directories: + source: "[^"]*/Tests/RunCMake/try_run/ConfigureLog-build/CMakeFiles/CMakeScratch/TryCompile-[^/]+" + binary: "[^"]*/Tests/RunCMake/try_run/ConfigureLog-build/CMakeFiles/CMakeScratch/TryCompile-[^/]+" + buildResult: + variable: "COMPILE_RESULT" + cached: true + stdout: \|.* + exitCode: 0 + runResult: + variable: "RUN_RESULT" + cached: true + stdout: \| + Output on stdout! + stderr: \| + Output, with backslash '\\\\', on stderr! + exitCode: 12 +\.\.\.$ diff --git a/Tests/RunCMake/try_run/ConfigureLog-test.c b/Tests/RunCMake/try_run/ConfigureLog-test.c new file mode 100644 index 0000000..6a8f125 --- /dev/null +++ b/Tests/RunCMake/try_run/ConfigureLog-test.c @@ -0,0 +1,9 @@ +#include <stdio.h> + +int main() +{ + fprintf(stderr, "Output, with backslash '\\', on stderr!\n"); + fflush(stderr); /* make output deterministic even if stderr is buffered */ + fprintf(stdout, "Output on stdout!\n"); + return 12; +} diff --git a/Tests/RunCMake/try_run/ConfigureLog.cmake b/Tests/RunCMake/try_run/ConfigureLog.cmake new file mode 100644 index 0000000..4b5c7cb --- /dev/null +++ b/Tests/RunCMake/try_run/ConfigureLog.cmake @@ -0,0 +1,18 @@ +try_run(RUN_RESULT COMPILE_RESULT + SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/ConfigureLog-bad.c + ) + +try_run(RUN_RESULT COMPILE_RESULT + SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/ConfigureLog-test.c + ) + +try_run(RUN_RESULT COMPILE_RESULT + SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/ConfigureLog-test.c + RUN_OUTPUT_VARIABLE RUN_OUTPUT + ) + +try_run(RUN_RESULT COMPILE_RESULT + SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/ConfigureLog-test.c + RUN_OUTPUT_STDOUT_VARIABLE RUN_STDOUT + RUN_OUTPUT_STDERR_VARIABLE RUN_STDERR + ) diff --git a/Tests/RunCMake/try_run/RunCMakeTest.cmake b/Tests/RunCMake/try_run/RunCMakeTest.cmake index dbea089..62e3caf 100644 --- a/Tests/RunCMake/try_run/RunCMakeTest.cmake +++ b/Tests/RunCMake/try_run/RunCMakeTest.cmake @@ -3,6 +3,7 @@ include(RunCMake) run_cmake(BinDirEmpty) run_cmake(BinDirRelative) run_cmake(NoOutputVariable) +run_cmake(ConfigureLog) set(RunCMake_TEST_OPTIONS -Dtry_compile_DEFS=old_signature.cmake) include(${RunCMake_SOURCE_DIR}/old_and_new_signature_tests.cmake) |