mirror of
https://github.com/Kitware/CMake.git
synced 2026-02-17 04:31:04 -06:00
committed by
Brad King
parent
a2267f337a
commit
abbe41578d
@@ -1180,6 +1180,7 @@ syn keyword cmakeVariable contained
|
||||
\ CMAKE_EXE_LINKER_FLAGS
|
||||
\ CMAKE_EXE_LINKER_FLAGS_INIT
|
||||
\ CMAKE_EXPORT_COMPILE_COMMANDS
|
||||
\ CMAKE_EXPORT_SARIF
|
||||
\ CMAKE_EXPORT_NO_PACKAGE_REGISTRY
|
||||
\ CMAKE_EXPORT_PACKAGE_REGISTRY
|
||||
\ CMAKE_EXTRA_GENERATOR
|
||||
|
||||
@@ -203,6 +203,7 @@ Variables that Change Behavior
|
||||
/variable/CMAKE_EXECUTE_PROCESS_COMMAND_ERROR_IS_FATAL
|
||||
/variable/CMAKE_EXPORT_BUILD_DATABASE
|
||||
/variable/CMAKE_EXPORT_COMPILE_COMMANDS
|
||||
/variable/CMAKE_EXPORT_SARIF
|
||||
/variable/CMAKE_EXPORT_PACKAGE_REGISTRY
|
||||
/variable/CMAKE_EXPORT_NO_PACKAGE_REGISTRY
|
||||
/variable/CMAKE_FIND_APPBUNDLE
|
||||
|
||||
@@ -307,6 +307,16 @@ Options
|
||||
When this command line option is given, :variable:`CMAKE_MESSAGE_CONTEXT_SHOW`
|
||||
is ignored.
|
||||
|
||||
.. option:: --sarif-output=<path>
|
||||
|
||||
.. versionadded:: 4.0
|
||||
|
||||
Enable logging of diagnostic messages produced by CMake in the SARIF format.
|
||||
|
||||
Write diagnostic messages to a SARIF file at the path specified. Projects can
|
||||
also set :variable:`CMAKE_EXPORT_SARIF` to ``ON`` to enable this feature for a
|
||||
build tree.
|
||||
|
||||
.. option:: --debug-trycompile
|
||||
|
||||
Do not delete the files and directories created for
|
||||
|
||||
81
Help/variable/CMAKE_EXPORT_SARIF.rst
Normal file
81
Help/variable/CMAKE_EXPORT_SARIF.rst
Normal file
@@ -0,0 +1,81 @@
|
||||
CMAKE_EXPORT_SARIF
|
||||
------------------
|
||||
|
||||
.. versionadded:: 4.0
|
||||
|
||||
Enable or disable CMake diagnostics output in SARIF format for a project.
|
||||
|
||||
If enabled, CMake will generate a SARIF log file containing diagnostic messages
|
||||
output by CMake when running in a project. By default, the log file is written
|
||||
to `.cmake/sarif/cmake.sarif`, but the location can be changed by setting the
|
||||
command-line option :option:`cmake --sarif-output` to the desired path.
|
||||
|
||||
The Static Analysis Results Interchange Format (SARIF) is a JSON-based standard
|
||||
format for static analysis tools (including build tools like CMake) to record
|
||||
and communicate diagnostic messages. CMake generates a SARIF log entry for
|
||||
warnings and errors produced while running CMake on a project (e.g.
|
||||
:command:`message` calls). Each log entry includes the message, severity, and
|
||||
location information if available.
|
||||
|
||||
An example of CMake's SARIF output is:
|
||||
|
||||
.. code-block:: json
|
||||
|
||||
{
|
||||
"version" : "2.1.0",
|
||||
"$schema" : "https://schemastore.azurewebsites.net/schemas/json/sarif-2.1.0-rtm.4.json",
|
||||
"runs" :
|
||||
[
|
||||
{
|
||||
"tool" :
|
||||
{
|
||||
"driver" :
|
||||
{
|
||||
"name" : "CMake",
|
||||
"rules" :
|
||||
[
|
||||
{
|
||||
"id" : "CMake.Warning",
|
||||
"messageStrings" :
|
||||
{
|
||||
"default" :
|
||||
{
|
||||
"text" : "CMake Warning: {0}"
|
||||
}
|
||||
},
|
||||
"name" : "CMake Warning"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"results" :
|
||||
[
|
||||
{
|
||||
"level" : "warning",
|
||||
"locations" :
|
||||
[
|
||||
{
|
||||
"physicalLocation" :
|
||||
{
|
||||
"artifactLocation" :
|
||||
{
|
||||
"uri" : "/home/user/development/project/CMakeLists.txt"
|
||||
},
|
||||
"region" :
|
||||
{
|
||||
"startLine" : 5
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"message" :
|
||||
{
|
||||
"text" : "An example warning"
|
||||
},
|
||||
"ruleId" : "CMake.Warning",
|
||||
"ruleIndex" : 0
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -446,6 +446,8 @@ add_library(
|
||||
cmRST.h
|
||||
cmRuntimeDependencyArchive.cxx
|
||||
cmRuntimeDependencyArchive.h
|
||||
cmSarifLog.cxx
|
||||
cmSarifLog.h
|
||||
cmScriptGenerator.h
|
||||
cmScriptGenerator.cxx
|
||||
cmSourceFile.cxx
|
||||
|
||||
@@ -10,6 +10,8 @@
|
||||
|
||||
#if !defined(CMAKE_BOOTSTRAP)
|
||||
# include "cmsys/SystemInformation.hxx"
|
||||
|
||||
# include "cmSarifLog.h"
|
||||
#endif
|
||||
|
||||
#include <sstream>
|
||||
@@ -218,6 +220,11 @@ void cmMessenger::DisplayMessage(MessageType t, std::string const& text,
|
||||
|
||||
displayMessage(t, msg);
|
||||
|
||||
#ifndef CMAKE_BOOTSTRAP
|
||||
// Add message to SARIF logs
|
||||
this->SarifLog.LogMessage(t, text, backtrace);
|
||||
#endif
|
||||
|
||||
#ifdef CMake_ENABLE_DEBUGGER
|
||||
if (DebuggerAdapter) {
|
||||
DebuggerAdapter->OnMessageOutput(t, msg.str());
|
||||
|
||||
@@ -13,6 +13,10 @@
|
||||
#include "cmListFileCache.h"
|
||||
#include "cmMessageType.h" // IWYU pragma: keep
|
||||
|
||||
#ifndef CMAKE_BOOTSTRAP
|
||||
# include "cmSarifLog.h"
|
||||
#endif
|
||||
|
||||
#ifdef CMake_ENABLE_DEBUGGER
|
||||
namespace cmDebugger {
|
||||
class cmDebuggerAdapter;
|
||||
@@ -59,6 +63,10 @@ public:
|
||||
return this->DeprecatedWarningsAsErrors;
|
||||
}
|
||||
|
||||
#ifndef CMAKE_BOOTSTRAP
|
||||
cmSarif::ResultsLog const& GetSarifResultsLog() const { return SarifLog; }
|
||||
#endif
|
||||
|
||||
// Print the top of a backtrace.
|
||||
void PrintBacktraceTitle(std::ostream& out,
|
||||
cmListFileBacktrace const& bt) const;
|
||||
@@ -76,6 +84,10 @@ private:
|
||||
|
||||
cm::optional<std::string> TopSource;
|
||||
|
||||
#ifndef CMAKE_BOOTSTRAP
|
||||
cmSarif::ResultsLog SarifLog;
|
||||
#endif
|
||||
|
||||
bool SuppressDevWarnings = false;
|
||||
bool SuppressDeprecatedWarnings = false;
|
||||
bool DevWarningsAsErrors = false;
|
||||
|
||||
383
Source/cmSarifLog.cxx
Normal file
383
Source/cmSarifLog.cxx
Normal file
@@ -0,0 +1,383 @@
|
||||
/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
|
||||
file Copyright.txt or https://cmake.org/licensing for details. */
|
||||
#include "cmSarifLog.h"
|
||||
|
||||
#include <memory>
|
||||
#include <stdexcept>
|
||||
|
||||
#include <cm/filesystem>
|
||||
|
||||
#include <cm3p/json/value.h>
|
||||
#include <cm3p/json/writer.h>
|
||||
|
||||
#include "cmsys/FStream.hxx"
|
||||
|
||||
#include "cmListFileCache.h"
|
||||
#include "cmMessageType.h"
|
||||
#include "cmStringAlgorithms.h"
|
||||
#include "cmSystemTools.h"
|
||||
#include "cmValue.h"
|
||||
#include "cmVersionConfig.h"
|
||||
#include "cmake.h"
|
||||
|
||||
cmSarif::ResultsLog::ResultsLog()
|
||||
{
|
||||
// Add the known CMake rules
|
||||
this->KnownRules.emplace(RuleBuilder("CMake.AuthorWarning")
|
||||
.Name("CMake Warning (dev)")
|
||||
.DefaultMessage("CMake Warning (dev): {0}")
|
||||
.Build());
|
||||
this->KnownRules.emplace(RuleBuilder("CMake.Warning")
|
||||
.Name("CMake Warning")
|
||||
.DefaultMessage("CMake Warning: {0}")
|
||||
.Build());
|
||||
this->KnownRules.emplace(RuleBuilder("CMake.DeprecationWarning")
|
||||
.Name("CMake Deprecation Warning")
|
||||
.DefaultMessage("CMake Deprecation Warning: {0}")
|
||||
.Build());
|
||||
this->KnownRules.emplace(RuleBuilder("CMake.AuthorError")
|
||||
.Name("CMake Error (dev)")
|
||||
.DefaultMessage("CMake Error (dev): {0}")
|
||||
.Build());
|
||||
this->KnownRules.emplace(RuleBuilder("CMake.FatalError")
|
||||
.Name("CMake Error")
|
||||
.DefaultMessage("CMake Error: {0}")
|
||||
.Build());
|
||||
this->KnownRules.emplace(
|
||||
RuleBuilder("CMake.InternalError")
|
||||
.Name("CMake Internal Error")
|
||||
.DefaultMessage("CMake Internal Error (please report a bug): {0}")
|
||||
.Build());
|
||||
this->KnownRules.emplace(RuleBuilder("CMake.DeprecationError")
|
||||
.Name("CMake Deprecation Error")
|
||||
.DefaultMessage("CMake Deprecation Error: {0}")
|
||||
.Build());
|
||||
this->KnownRules.emplace(RuleBuilder("CMake.Message")
|
||||
.Name("CMake Message")
|
||||
.DefaultMessage("CMake Message: {0}")
|
||||
.Build());
|
||||
this->KnownRules.emplace(RuleBuilder("CMake.Log")
|
||||
.Name("CMake Log")
|
||||
.DefaultMessage("CMake Log: {0}")
|
||||
.Build());
|
||||
}
|
||||
|
||||
void cmSarif::ResultsLog::Log(cmSarif::Result&& result) const
|
||||
{
|
||||
// The rule ID is optional, but if it is present, enable metadata output for
|
||||
// the rule by marking it as used
|
||||
if (result.RuleId) {
|
||||
std::size_t index = this->UseRule(*result.RuleId);
|
||||
result.RuleIndex = index;
|
||||
}
|
||||
|
||||
// Add the result to the log
|
||||
this->Results.emplace_back(result);
|
||||
}
|
||||
|
||||
void cmSarif::ResultsLog::LogMessage(
|
||||
MessageType t, std::string const& text,
|
||||
cmListFileBacktrace const& backtrace) const
|
||||
{
|
||||
// Add metadata to the result object
|
||||
// The CMake SARIF rules for messages all expect 1 string argument with the
|
||||
// message text
|
||||
Json::Value additionalProperties(Json::objectValue);
|
||||
Json::Value args(Json::arrayValue);
|
||||
args.append(text);
|
||||
additionalProperties["message"]["id"] = "default";
|
||||
additionalProperties["message"]["arguments"] = args;
|
||||
|
||||
// Create and log a result object
|
||||
// Rule indices are assigned when writing the final JSON output. Right now,
|
||||
// leave it as nullopt. The other optional fields are filled if available
|
||||
this->Log(cmSarif::Result{
|
||||
text, cmSarif::SourceFileLocation::FromBacktrace(backtrace),
|
||||
cmSarif::MessageSeverityLevel(t), cmSarif::MessageRuleId(t), cm::nullopt,
|
||||
additionalProperties });
|
||||
}
|
||||
|
||||
std::size_t cmSarif::ResultsLog::UseRule(std::string const& id) const
|
||||
{
|
||||
// Check if the rule is already in the index
|
||||
auto it = this->RuleToIndex.find(id);
|
||||
if (it != this->RuleToIndex.end()) {
|
||||
// The rule is already in use. Return the known index
|
||||
return it->second;
|
||||
}
|
||||
|
||||
// This rule is not yet in the index, so check if it is recognized
|
||||
auto itKnown = this->KnownRules.find(id);
|
||||
if (itKnown == this->KnownRules.end()) {
|
||||
// The rule is not known. Add an empty rule to the known rules so that it
|
||||
// is included in the output
|
||||
this->KnownRules.emplace(RuleBuilder(id.c_str()).Build());
|
||||
}
|
||||
|
||||
// Since this is the first time the rule is used, enable it and add it to the
|
||||
// index
|
||||
std::size_t idx = this->EnabledRules.size();
|
||||
this->RuleToIndex[id] = idx;
|
||||
this->EnabledRules.emplace_back(id);
|
||||
return idx;
|
||||
}
|
||||
|
||||
cmSarif::ResultSeverityLevel cmSarif::MessageSeverityLevel(MessageType t)
|
||||
{
|
||||
switch (t) {
|
||||
case MessageType::AUTHOR_WARNING:
|
||||
case MessageType::WARNING:
|
||||
case MessageType::DEPRECATION_WARNING:
|
||||
return ResultSeverityLevel::SARIF_WARNING;
|
||||
case MessageType::AUTHOR_ERROR:
|
||||
case MessageType::FATAL_ERROR:
|
||||
case MessageType::INTERNAL_ERROR:
|
||||
case MessageType::DEPRECATION_ERROR:
|
||||
return ResultSeverityLevel::SARIF_ERROR;
|
||||
case MessageType::MESSAGE:
|
||||
case MessageType::LOG:
|
||||
return ResultSeverityLevel::SARIF_NOTE;
|
||||
default:
|
||||
return ResultSeverityLevel::SARIF_NONE;
|
||||
}
|
||||
}
|
||||
|
||||
cm::optional<std::string> cmSarif::MessageRuleId(MessageType t)
|
||||
{
|
||||
switch (t) {
|
||||
case MessageType::AUTHOR_WARNING:
|
||||
return "CMake.AuthorWarning";
|
||||
case MessageType::WARNING:
|
||||
return "CMake.Warning";
|
||||
case MessageType::DEPRECATION_WARNING:
|
||||
return "CMake.DeprecationWarning";
|
||||
case MessageType::AUTHOR_ERROR:
|
||||
return "CMake.AuthorError";
|
||||
case MessageType::FATAL_ERROR:
|
||||
return "CMake.FatalError";
|
||||
case MessageType::INTERNAL_ERROR:
|
||||
return "CMake.InternalError";
|
||||
case MessageType::DEPRECATION_ERROR:
|
||||
return "CMake.DeprecationError";
|
||||
case MessageType::MESSAGE:
|
||||
return "CMake.Message";
|
||||
case MessageType::LOG:
|
||||
return "CMake.Log";
|
||||
default:
|
||||
return cm::nullopt;
|
||||
}
|
||||
}
|
||||
|
||||
Json::Value cmSarif::Rule::GetJson() const
|
||||
{
|
||||
Json::Value rule(Json::objectValue);
|
||||
rule["id"] = this->Id;
|
||||
|
||||
if (this->Name) {
|
||||
rule["name"] = *this->Name;
|
||||
}
|
||||
if (this->FullDescription) {
|
||||
rule["fullDescription"]["text"] = *this->FullDescription;
|
||||
}
|
||||
if (this->DefaultMessage) {
|
||||
rule["messageStrings"]["default"]["text"] = *this->DefaultMessage;
|
||||
}
|
||||
|
||||
return rule;
|
||||
}
|
||||
|
||||
cmSarif::SourceFileLocation::SourceFileLocation(
|
||||
cmListFileBacktrace const& backtrace)
|
||||
{
|
||||
if (backtrace.Empty()) {
|
||||
throw std::runtime_error("Empty source file location");
|
||||
}
|
||||
|
||||
cmListFileContext const& lfc = backtrace.Top();
|
||||
this->Uri = lfc.FilePath;
|
||||
this->Line = lfc.Line;
|
||||
}
|
||||
|
||||
cm::optional<cmSarif::SourceFileLocation>
|
||||
cmSarif::SourceFileLocation::FromBacktrace(
|
||||
cmListFileBacktrace const& backtrace)
|
||||
{
|
||||
if (backtrace.Empty()) {
|
||||
return cm::nullopt;
|
||||
}
|
||||
cmListFileContext const& lfc = backtrace.Top();
|
||||
if (lfc.Line <= 0 || lfc.FilePath.empty()) {
|
||||
return cm::nullopt;
|
||||
}
|
||||
|
||||
return cm::make_optional<cmSarif::SourceFileLocation>(backtrace);
|
||||
}
|
||||
|
||||
void cmSarif::ResultsLog::WriteJson(Json::Value& root) const
|
||||
{
|
||||
// Add SARIF metadata
|
||||
root["version"] = "2.1.0";
|
||||
root["$schema"] = "https://schemastore.azurewebsites.net/schemas/json/"
|
||||
"sarif-2.1.0-rtm.4.json";
|
||||
|
||||
// JSON object for the SARIF runs array
|
||||
Json::Value runs(Json::arrayValue);
|
||||
|
||||
// JSON object for the current (only) run
|
||||
Json::Value currentRun(Json::objectValue);
|
||||
|
||||
// Accumulate info about the reported rules
|
||||
Json::Value jsonRules(Json::arrayValue);
|
||||
for (auto const& ruleId : this->EnabledRules) {
|
||||
jsonRules.append(KnownRules.at(ruleId).GetJson());
|
||||
}
|
||||
|
||||
// Add info the driver for the current run (CMake)
|
||||
Json::Value driverTool(Json::objectValue);
|
||||
driverTool["name"] = "CMake";
|
||||
driverTool["version"] = CMake_VERSION;
|
||||
driverTool["rules"] = jsonRules;
|
||||
currentRun["tool"]["driver"] = driverTool;
|
||||
|
||||
runs.append(currentRun);
|
||||
|
||||
// Add all results
|
||||
Json::Value jsonResults(Json::arrayValue);
|
||||
for (auto const& res : this->Results) {
|
||||
Json::Value jsonResult(Json::objectValue);
|
||||
|
||||
if (res.Message) {
|
||||
jsonResult["message"]["text"] = *(res.Message);
|
||||
}
|
||||
|
||||
// If the result has a level, add it to the result
|
||||
if (res.Level) {
|
||||
switch (*res.Level) {
|
||||
case ResultSeverityLevel::SARIF_WARNING:
|
||||
jsonResult["level"] = "warning";
|
||||
break;
|
||||
case ResultSeverityLevel::SARIF_ERROR:
|
||||
jsonResult["level"] = "error";
|
||||
break;
|
||||
case ResultSeverityLevel::SARIF_NOTE:
|
||||
jsonResult["level"] = "note";
|
||||
break;
|
||||
case ResultSeverityLevel::SARIF_NONE:
|
||||
jsonResult["level"] = "none";
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// If the result has a rule ID or index, add it to the result
|
||||
if (res.RuleId) {
|
||||
jsonResult["ruleId"] = *res.RuleId;
|
||||
}
|
||||
if (res.RuleIndex) {
|
||||
jsonResult["ruleIndex"] = Json::UInt64(*res.RuleIndex);
|
||||
}
|
||||
|
||||
if (res.Location) {
|
||||
jsonResult["locations"][0]["physicalLocation"]["artifactLocation"]
|
||||
["uri"] = (res.Location)->Uri;
|
||||
jsonResult["locations"][0]["physicalLocation"]["region"]["startLine"] =
|
||||
Json::Int64((res.Location)->Line);
|
||||
}
|
||||
|
||||
jsonResults.append(jsonResult);
|
||||
}
|
||||
|
||||
currentRun["results"] = jsonResults;
|
||||
runs[0] = currentRun;
|
||||
root["runs"] = runs;
|
||||
}
|
||||
|
||||
cmSarif::LogFileWriter::~LogFileWriter()
|
||||
{
|
||||
// If the file has not been written yet, try to finalize it
|
||||
if (!this->FileWritten) {
|
||||
// Try to write and check the result
|
||||
if (this->TryWrite() == WriteResult::FAILURE) {
|
||||
// If the result is `FAILURE`, it means the write condition is true but
|
||||
// the file still wasn't written. This is an error.
|
||||
cmSystemTools::Error("Failed to write SARIF log to " +
|
||||
this->FilePath.generic_string());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool cmSarif::LogFileWriter::EnsureFileValid()
|
||||
{
|
||||
// First, ensure directory exists
|
||||
cm::filesystem::path dir = this->FilePath.parent_path();
|
||||
if (!cmSystemTools::FileIsDirectory(dir.generic_string())) {
|
||||
if (!this->CreateDirectories ||
|
||||
!cmSystemTools::MakeDirectory(dir.generic_string()).IsSuccess()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Open the file for writing
|
||||
cmsys::ofstream outputFile(this->FilePath.generic_string().c_str());
|
||||
if (!outputFile.good()) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
cmSarif::LogFileWriter::WriteResult cmSarif::LogFileWriter::TryWrite()
|
||||
{
|
||||
// Check that SARIF logging is enabled
|
||||
if (!this->WriteCondition || !this->WriteCondition()) {
|
||||
return WriteResult::SKIPPED;
|
||||
}
|
||||
|
||||
// Open the file
|
||||
if (!this->EnsureFileValid()) {
|
||||
return WriteResult::FAILURE;
|
||||
}
|
||||
cmsys::ofstream outputFile(this->FilePath.generic_string().c_str());
|
||||
|
||||
// The file is available, so proceed to write the log
|
||||
|
||||
// Assemble the SARIF JSON from the results in the log
|
||||
Json::Value root(Json::objectValue);
|
||||
this->Log.WriteJson(root);
|
||||
|
||||
// Serialize the JSON to the file
|
||||
Json::StreamWriterBuilder builder;
|
||||
std::unique_ptr<Json::StreamWriter> writer(builder.newStreamWriter());
|
||||
|
||||
writer->write(root, &outputFile);
|
||||
outputFile.close();
|
||||
|
||||
this->FileWritten = true;
|
||||
return WriteResult::SUCCESS;
|
||||
}
|
||||
|
||||
bool cmSarif::LogFileWriter::ConfigureForCMakeRun(cmake& cm)
|
||||
{
|
||||
// If an explicit SARIF output path has been provided, set and check it
|
||||
cm::optional<std::string> sarifFilePath = cm.GetSarifFilePath();
|
||||
if (sarifFilePath) {
|
||||
this->SetPath(cm::filesystem::path(*sarifFilePath));
|
||||
if (!this->EnsureFileValid()) {
|
||||
cmSystemTools::Error(
|
||||
cmStrCat("Invalid SARIF output file path: ", *sarifFilePath));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// The write condition is checked immediately before writing the file, which
|
||||
// allows projects to enable SARIF diagnostics by setting a cache variable
|
||||
// and have it take effect for the current run.
|
||||
this->SetWriteCondition([&cm]() {
|
||||
// The command-line option can be used to set an explicit path, but in
|
||||
// normal mode, the project variable `CMAKE_EXPORT_SARIF` can also enable
|
||||
// SARIF logging.
|
||||
return cm.GetSarifFilePath().has_value() ||
|
||||
(cm.GetWorkingMode() == cmake::NORMAL_MODE &&
|
||||
cm.GetCacheDefinition(cmSarif::PROJECT_SARIF_FILE_VARIABLE).IsOn());
|
||||
});
|
||||
|
||||
return true;
|
||||
}
|
||||
287
Source/cmSarifLog.h
Normal file
287
Source/cmSarifLog.h
Normal file
@@ -0,0 +1,287 @@
|
||||
/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
|
||||
file Copyright.txt or https://cmake.org/licensing for details. */
|
||||
#pragma once
|
||||
|
||||
#include <cstddef>
|
||||
#include <functional>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include <cm/filesystem>
|
||||
#include <cm/optional>
|
||||
|
||||
#include <cm3p/json/value.h>
|
||||
|
||||
class cmake;
|
||||
class cmListFileBacktrace;
|
||||
enum class MessageType;
|
||||
|
||||
/// @brief CMake support for SARIF logging
|
||||
namespace cmSarif {
|
||||
|
||||
constexpr char const* PROJECT_SARIF_FILE_VARIABLE = "CMAKE_EXPORT_SARIF";
|
||||
|
||||
constexpr char const* PROJECT_DEFAULT_SARIF_FILE = ".cmake/sarif/cmake.sarif";
|
||||
|
||||
/// @brief The severity level of a result in SARIF
|
||||
///
|
||||
/// The SARIF specification section 3.27.10 defines four levels of severity
|
||||
/// for results.
|
||||
enum class ResultSeverityLevel
|
||||
{
|
||||
SARIF_WARNING,
|
||||
SARIF_ERROR,
|
||||
SARIF_NOTE,
|
||||
SARIF_NONE,
|
||||
};
|
||||
|
||||
/// @brief A location in a source file logged with a SARIF result
|
||||
struct SourceFileLocation
|
||||
{
|
||||
std::string Uri;
|
||||
long Line = 0;
|
||||
|
||||
/// @brief Construct a SourceFileLocation at the top of the call stack
|
||||
SourceFileLocation(cmListFileBacktrace const& backtrace);
|
||||
|
||||
/// @brief Get the SourceFileLocation from the top of a call stack, if any
|
||||
/// @return The location or nullopt if the call stack is empty or is missing
|
||||
/// location information
|
||||
static cm::optional<SourceFileLocation> FromBacktrace(
|
||||
cmListFileBacktrace const& backtrace);
|
||||
};
|
||||
|
||||
/// @brief A result defined by SARIF reported by a CMake run
|
||||
///
|
||||
/// This is the data model for results in a SARIF log. Typically, a result only
|
||||
/// requires either a message or a rule index. The most common properties are
|
||||
/// named in this struct, but arbitrary metadata can be added to the result
|
||||
/// using the additionalProperties field.
|
||||
struct Result
|
||||
{
|
||||
/// @brief The message text of the result (required if no rule index)
|
||||
cm::optional<std::string> Message;
|
||||
|
||||
/// @brief The location of the result (optional)
|
||||
cm::optional<cmSarif::SourceFileLocation> Location;
|
||||
|
||||
/// @brief The severity level of the result (optional)
|
||||
cm::optional<cmSarif::ResultSeverityLevel> Level;
|
||||
|
||||
/// @brief The rule ID of the result (optional)
|
||||
cm::optional<std::string> RuleId;
|
||||
|
||||
/// @brief The index of the rule in the log's rule array (optional)
|
||||
cm::optional<std::size_t> RuleIndex;
|
||||
|
||||
/// @brief Additional JSON properties for the result (optional)
|
||||
///
|
||||
/// The additional properties should be merged into the result object when it
|
||||
/// is written to the SARIF log.
|
||||
Json::Value AdditionalProperties;
|
||||
};
|
||||
|
||||
/// @brief A SARIF reporting rule
|
||||
///
|
||||
/// A rule in SARIF is described by a reportingDescriptor object (SARIF
|
||||
/// specification section 3.49). The only property required for a rule is the
|
||||
/// ID property. The ID is normally an opaque string that identifies a rule
|
||||
/// applicable to a class of results. The other included properties are
|
||||
/// optional but recommended for rules reported by CMake.
|
||||
struct Rule
|
||||
{
|
||||
/// @brief The ID of the rule. Required by SARIF
|
||||
std::string Id;
|
||||
|
||||
/// @brief The end-user name of the rule (optional)
|
||||
cm::optional<std::string> Name;
|
||||
|
||||
/// @brief The extended description of the rule (optional)
|
||||
cm::optional<std::string> FullDescription;
|
||||
|
||||
/// @brief The default message for the rule (optional)
|
||||
cm::optional<std::string> DefaultMessage;
|
||||
|
||||
/// @brief Get the JSON representation of this rule
|
||||
Json::Value GetJson() const;
|
||||
};
|
||||
|
||||
/// @brief A builder for SARIF rules
|
||||
///
|
||||
/// `Rule` is a data model for SARIF rules. Known rules are usually initialized
|
||||
/// manually by field. Using a builder makes initialization more readable and
|
||||
/// prevents issues with reordering and optional fields.
|
||||
class RuleBuilder
|
||||
{
|
||||
public:
|
||||
/// @brief Construct a new rule builder for a rule with the given ID
|
||||
RuleBuilder(char const* id) { this->NewRule.Id = id; }
|
||||
|
||||
/// @brief Set the name of the rule
|
||||
RuleBuilder& Name(std::string name)
|
||||
{
|
||||
this->NewRule.Name = std::move(name);
|
||||
return *this;
|
||||
}
|
||||
|
||||
/// @brief Set the full description of the rule
|
||||
RuleBuilder& FullDescription(std::string fullDescription)
|
||||
{
|
||||
this->NewRule.FullDescription = std::move(fullDescription);
|
||||
return *this;
|
||||
}
|
||||
|
||||
/// @brief Set the default message for the rule
|
||||
RuleBuilder& DefaultMessage(std::string defaultMessage)
|
||||
{
|
||||
this->NewRule.DefaultMessage = std::move(defaultMessage);
|
||||
return *this;
|
||||
}
|
||||
|
||||
/// @brief Build the rule
|
||||
std::pair<std::string, Rule> Build() const
|
||||
{
|
||||
return std::make_pair(this->NewRule.Id, this->NewRule);
|
||||
}
|
||||
|
||||
private:
|
||||
Rule NewRule;
|
||||
};
|
||||
|
||||
/// @brief Get the SARIF severity level of a CMake message type
|
||||
ResultSeverityLevel MessageSeverityLevel(MessageType t);
|
||||
|
||||
/// @brief Get the SARIF rule ID of a CMake message type
|
||||
/// @return The rule ID or nullopt if the message type is unrecognized
|
||||
///
|
||||
/// The rule ID is a string assigned to SARIF results to identify the category
|
||||
/// of the result. CMake maps messages to rules based on the message type.
|
||||
/// CMake's rules are of the form "CMake.<MessageType>".
|
||||
cm::optional<std::string> MessageRuleId(MessageType t);
|
||||
|
||||
/// @brief A log for reporting results in the SARIF format
|
||||
class ResultsLog
|
||||
{
|
||||
public:
|
||||
ResultsLog();
|
||||
|
||||
/// @brief Log a result of this run to the SARIF output
|
||||
void Log(cmSarif::Result&& result) const;
|
||||
|
||||
/// @brief Log a result from a CMake message with a source file location
|
||||
/// @param t The type of the message, which corresponds to the level and rule
|
||||
/// of the result
|
||||
/// @param text The contents of the message
|
||||
/// @param backtrace The call stack where the message originated (may be
|
||||
/// empty)
|
||||
void LogMessage(MessageType t, std::string const& text,
|
||||
cmListFileBacktrace const& backtrace) const;
|
||||
|
||||
/// @brief Write this SARIF log to an empty JSON object
|
||||
/// @param[out] root The JSON object to write to
|
||||
void WriteJson(Json::Value& root) const;
|
||||
|
||||
private:
|
||||
// Private methods
|
||||
|
||||
// Log that a rule was used and should be included in the output. Returns the
|
||||
// index of the rule in the log
|
||||
std::size_t UseRule(std::string const& id) const;
|
||||
|
||||
// Private data
|
||||
// All data is mutable since log results are often added in const methods
|
||||
|
||||
// All results added chronologically
|
||||
mutable std::vector<cmSarif::Result> Results;
|
||||
|
||||
// Mapping of rule IDs to rule indices in the log.
|
||||
// In SARIF, rule metadata is typically only included if the rule is
|
||||
// referenced. The indices are unique to one log output and and vary
|
||||
// depending on when the rule was first encountered.
|
||||
mutable std::unordered_map<std::string, std::size_t> RuleToIndex;
|
||||
|
||||
// Rules that will be added to the log in order of appearance
|
||||
mutable std::vector<std::string> EnabledRules;
|
||||
|
||||
// All known rules that could be included in a log
|
||||
mutable std::unordered_map<std::string, Rule> KnownRules;
|
||||
};
|
||||
|
||||
/// @brief Writes contents of a `cmSarif::ResultsLog` to a file
|
||||
///
|
||||
/// The log file writer is a helper class that writes the contents of a
|
||||
/// `cmSarif::ResultsLog` upon destruction if a condition (e.g. project
|
||||
/// variable is enabled) is met.
|
||||
class LogFileWriter
|
||||
{
|
||||
public:
|
||||
/// @brief Create a new, disabled log file writer
|
||||
///
|
||||
/// The returned writer will not write anything until the path generator
|
||||
/// and write condition are set. If the log has not been written when the
|
||||
/// object is being destroyed, the destructor will write the log if the
|
||||
/// condition is met and a valid path is available.
|
||||
LogFileWriter(ResultsLog const& log)
|
||||
: Log(log)
|
||||
{
|
||||
}
|
||||
|
||||
/// @brief Configure a log file writer for a CMake run
|
||||
///
|
||||
/// CMake should write a SARIF log if the project variable
|
||||
/// `CMAKE_EXPORT_SARIF` is `ON` or if the `--sarif-output=<path>` command
|
||||
/// line option is set. The writer will be configured to respond to these
|
||||
/// conditions.
|
||||
///
|
||||
/// This does not configure a default path, so one must be set once it is
|
||||
/// known that we're in normal mode if none was explicitly provided.
|
||||
bool ConfigureForCMakeRun(cmake& cm);
|
||||
|
||||
~LogFileWriter();
|
||||
|
||||
/// @brief Check if a valid path is set by opening the output file
|
||||
/// @return True if the file can be opened for writing
|
||||
bool EnsureFileValid();
|
||||
|
||||
/// @brief The possible outcomes of trying to write the log file
|
||||
enum class WriteResult
|
||||
{
|
||||
SUCCESS, ///< File written with no issues
|
||||
FAILURE, ///< Error encountered while writing the file
|
||||
SKIPPED, ///< Writing was skipped due to false write condition
|
||||
};
|
||||
|
||||
/// @brief Try to write the log file and return `true` if it was written
|
||||
///
|
||||
/// Check the write condition and path generator to determine if the log
|
||||
/// file should be written.
|
||||
WriteResult TryWrite();
|
||||
|
||||
/// @brief Set a lambda to check if the log file should be written
|
||||
void SetWriteCondition(std::function<bool()> const& checkConditionCallback)
|
||||
{
|
||||
this->WriteCondition = checkConditionCallback;
|
||||
}
|
||||
|
||||
/// @brief Set the output file path, optionally creating parent directories
|
||||
///
|
||||
/// The settings will apply when the log file is written. If the output
|
||||
/// file should be checked earlier, use `CheckFileValidity`.
|
||||
void SetPath(cm::filesystem::path const& path,
|
||||
bool createParentDirectories = false)
|
||||
{
|
||||
this->FilePath = path;
|
||||
this->CreateDirectories = createParentDirectories;
|
||||
}
|
||||
|
||||
private:
|
||||
ResultsLog const& Log;
|
||||
std::function<bool()> WriteCondition;
|
||||
cm::filesystem::path FilePath;
|
||||
bool CreateDirectories = false;
|
||||
bool FileWritten = false;
|
||||
};
|
||||
|
||||
} // namespace cmSarif
|
||||
@@ -14,6 +14,7 @@
|
||||
#include <stdexcept>
|
||||
#include <utility>
|
||||
|
||||
#include <cm/filesystem>
|
||||
#include <cm/memory>
|
||||
#include <cm/optional>
|
||||
#include <cm/string_view>
|
||||
@@ -61,6 +62,9 @@
|
||||
#include "cmJSONState.h"
|
||||
#include "cmList.h"
|
||||
#include "cmMessenger.h"
|
||||
#ifndef CMAKE_BOOTSTRAP
|
||||
# include "cmSarifLog.h"
|
||||
#endif
|
||||
#include "cmState.h"
|
||||
#include "cmStateDirectory.h"
|
||||
#include "cmStringAlgorithms.h"
|
||||
@@ -1272,6 +1276,16 @@ void cmake::SetArgs(std::vector<std::string> const& args)
|
||||
state->SetIgnoreLinkWarningAsError(true);
|
||||
return true;
|
||||
} },
|
||||
#ifndef CMAKE_BOOTSTRAP
|
||||
CommandArgument{ "--sarif-output", "No file specified for --sarif-output",
|
||||
CommandArgument::Values::One,
|
||||
[](std::string const& value, cmake* state) -> bool {
|
||||
state->SarifFilePath =
|
||||
cmSystemTools::ToNormalizedPathOnDisk(value);
|
||||
state->SarifFileOutput = true;
|
||||
return true;
|
||||
} },
|
||||
#endif
|
||||
CommandArgument{ "--debugger", CommandArgument::Values::Zero,
|
||||
[](std::string const&, cmake* state) -> bool {
|
||||
#ifdef CMake_ENABLE_DEBUGGER
|
||||
@@ -2853,6 +2867,15 @@ int cmake::Run(std::vector<std::string> const& args, bool noconfigure)
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifndef CMAKE_BOOTSTRAP
|
||||
// Configure the SARIF log for the current run
|
||||
cmSarif::LogFileWriter sarifLogFileWriter(
|
||||
this->GetMessenger()->GetSarifResultsLog());
|
||||
if (!sarifLogFileWriter.ConfigureForCMakeRun(*this)) {
|
||||
return -1;
|
||||
}
|
||||
#endif
|
||||
|
||||
// Log the trace format version to the desired output
|
||||
if (this->GetTrace()) {
|
||||
this->PrintTraceFormatVersion();
|
||||
@@ -2879,6 +2902,17 @@ int cmake::Run(std::vector<std::string> const& args, bool noconfigure)
|
||||
cmSystemTools::Error("Error executing cmake::LoadCache(). Aborting.\n");
|
||||
return -1;
|
||||
}
|
||||
#ifndef CMAKE_BOOTSTRAP
|
||||
// If no SARIF file has been explicitly specified, use the default path
|
||||
if (!this->SarifFileOutput) {
|
||||
// If no output file is specified, use the default path
|
||||
// Enable parent directory creation for the default path
|
||||
sarifLogFileWriter.SetPath(
|
||||
cm::filesystem::path(this->GetHomeOutputDirectory()) /
|
||||
std::string(cmSarif::PROJECT_DEFAULT_SARIF_FILE),
|
||||
true);
|
||||
}
|
||||
#endif
|
||||
} else {
|
||||
if (this->FreshCache) {
|
||||
cmSystemTools::Error("--fresh allowed only when configuring a project");
|
||||
@@ -2909,6 +2943,11 @@ int cmake::Run(std::vector<std::string> const& args, bool noconfigure)
|
||||
return this->HasScriptModeExitCode() ? this->GetScriptModeExitCode() : 0;
|
||||
}
|
||||
|
||||
#ifndef CMAKE_BOOTSTRAP
|
||||
// CMake only responds to the SARIF variable in normal mode
|
||||
this->MarkCliAsUsed(cmSarif::PROJECT_SARIF_FILE_VARIABLE);
|
||||
#endif
|
||||
|
||||
// If MAKEFLAGS are given in the environment, remove the environment
|
||||
// variable. This will prevent try-compile from succeeding when it
|
||||
// should fail (if "-i" is an option). We cannot simply test
|
||||
|
||||
@@ -29,6 +29,8 @@
|
||||
#include "cmValue.h"
|
||||
|
||||
#if !defined(CMAKE_BOOTSTRAP)
|
||||
# include <type_traits>
|
||||
|
||||
# include <cm/optional>
|
||||
|
||||
# include <cm3p/json/value.h>
|
||||
@@ -575,6 +577,15 @@ public:
|
||||
|
||||
cmMessenger* GetMessenger() const { return this->Messenger.get(); }
|
||||
|
||||
#ifndef CMAKE_BOOTSTRAP
|
||||
/// Get the SARIF file path if set manually for this run
|
||||
cm::optional<std::string> GetSarifFilePath() const
|
||||
{
|
||||
return (this->SarifFileOutput ? cm::make_optional(this->SarifFilePath)
|
||||
: cm::nullopt);
|
||||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Get the state of the suppression of developer (author) warnings.
|
||||
* Returns false, by default, if developer warnings should be shown, true
|
||||
@@ -811,6 +822,11 @@ private:
|
||||
cmStateSnapshot CurrentSnapshot;
|
||||
std::unique_ptr<cmMessenger> Messenger;
|
||||
|
||||
#ifndef CMAKE_BOOTSTRAP
|
||||
bool SarifFileOutput = false;
|
||||
std::string SarifFilePath;
|
||||
#endif
|
||||
|
||||
std::vector<std::string> TraceOnlyThisSources;
|
||||
|
||||
std::set<std::string> DebugFindPkgs;
|
||||
|
||||
@@ -650,6 +650,7 @@ add_RunCMake_test(project_injected)
|
||||
add_RunCMake_test(property_init)
|
||||
add_RunCMake_test(DependencyProviders)
|
||||
add_RunCMake_test(return)
|
||||
add_RunCMake_test(SarifOutput)
|
||||
add_RunCMake_test(separate_arguments)
|
||||
add_RunCMake_test(set_property)
|
||||
add_RunCMake_test(string)
|
||||
|
||||
3
Tests/RunCMake/SarifOutput/CMakeLists.txt
Normal file
3
Tests/RunCMake/SarifOutput/CMakeLists.txt
Normal file
@@ -0,0 +1,3 @@
|
||||
cmake_minimum_required(VERSION 3.10)
|
||||
project(${RunCMake_TEST} NONE)
|
||||
include(${RunCMake_TEST}.cmake)
|
||||
@@ -0,0 +1,4 @@
|
||||
# By default, no SARIF file should be generated
|
||||
if (EXISTS "${RunCMake_TEST_BINARY_DIR}/.cmake/sarif/cmake.sarif")
|
||||
message(FATAL_ERROR "SARIF file should not have been generated by default")
|
||||
endif()
|
||||
4
Tests/RunCMake/SarifOutput/DefaultSarifOutput-stderr.txt
Normal file
4
Tests/RunCMake/SarifOutput/DefaultSarifOutput-stderr.txt
Normal file
@@ -0,0 +1,4 @@
|
||||
^CMake Warning at DefaultSarifOutput\.cmake:1 \(message\):
|
||||
Example warning message
|
||||
Call Stack \(most recent call first\):
|
||||
CMakeLists\.txt:[0-9]+ \(include\)$
|
||||
1
Tests/RunCMake/SarifOutput/DefaultSarifOutput.cmake
Normal file
1
Tests/RunCMake/SarifOutput/DefaultSarifOutput.cmake
Normal file
@@ -0,0 +1 @@
|
||||
message(WARNING "Example warning message")
|
||||
@@ -0,0 +1,4 @@
|
||||
include("${CMAKE_CURRENT_LIST_DIR}/check-sarif.cmake")
|
||||
|
||||
check_sarif_output("${RunCMake_TEST_BINARY_DIR}/.cmake/sarif/cmake.sarif"
|
||||
"${CMAKE_CURRENT_LIST_DIR}/GenerateSarifResults-expected.sarif")
|
||||
@@ -0,0 +1,67 @@
|
||||
{
|
||||
"$schema": "https://schemastore.azurewebsites.net/schemas/json/sarif-2.1.0-rtm.4.json",
|
||||
"runs": [
|
||||
{
|
||||
"results": [
|
||||
{
|
||||
"level": "warning",
|
||||
"locations": [
|
||||
{
|
||||
"physicalLocation": {
|
||||
"artifactLocation": {
|
||||
"uri": "PATH:<SOURCE_DIR>/GenerateSarifResults.cmake"
|
||||
},
|
||||
"region": {
|
||||
"startLine": 2
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"message": {
|
||||
"text": "Example warning message"
|
||||
},
|
||||
"ruleId": "CMake.Warning",
|
||||
"ruleIndex": 0
|
||||
},
|
||||
{
|
||||
"level": "warning",
|
||||
"locations": [
|
||||
{
|
||||
"physicalLocation": {
|
||||
"artifactLocation": {
|
||||
"uri": "PATH:<SOURCE_DIR>/GenerateSarifResults.cmake"
|
||||
},
|
||||
"region": {
|
||||
"startLine": 5
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"message": {
|
||||
"text": "A second example warning message"
|
||||
},
|
||||
"ruleId": "CMake.Warning",
|
||||
"ruleIndex": 0
|
||||
}
|
||||
],
|
||||
"tool": {
|
||||
"driver": {
|
||||
"name": "CMake",
|
||||
"rules": [
|
||||
{
|
||||
"id": "CMake.Warning",
|
||||
"messageStrings": {
|
||||
"default": {
|
||||
"text": "CMake Warning: {0}"
|
||||
}
|
||||
},
|
||||
"name": "CMake Warning"
|
||||
}
|
||||
],
|
||||
"version": "<IGNORE>"
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"version": "2.1.0"
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
^CMake Warning at GenerateSarifResults\.cmake:2 \(message\):
|
||||
Example warning message
|
||||
Call Stack \(most recent call first\):
|
||||
CMakeLists\.txt:[0-9]+ \(include\)
|
||||
+
|
||||
CMake Warning at GenerateSarifResults\.cmake:5 \(message\):
|
||||
A second example warning message
|
||||
Call Stack \(most recent call first\):
|
||||
CMakeLists\.txt:[0-9]+ \(include\)$
|
||||
8
Tests/RunCMake/SarifOutput/GenerateSarifResults.cmake
Normal file
8
Tests/RunCMake/SarifOutput/GenerateSarifResults.cmake
Normal file
@@ -0,0 +1,8 @@
|
||||
# Write some user messages to produce SARIF results
|
||||
message(WARNING "Example warning message")
|
||||
|
||||
# The second warning should be logged, but the rule should not be duplicated
|
||||
message(WARNING "A second example warning message")
|
||||
|
||||
# Status message should not be logged
|
||||
message(STATUS "Example status message")
|
||||
4
Tests/RunCMake/SarifOutput/ProjectFatalError-check.cmake
Normal file
4
Tests/RunCMake/SarifOutput/ProjectFatalError-check.cmake
Normal file
@@ -0,0 +1,4 @@
|
||||
include("${CMAKE_CURRENT_LIST_DIR}/check-sarif.cmake")
|
||||
|
||||
check_sarif_output("${RunCMake_TEST_BINARY_DIR}/.cmake/sarif/cmake.sarif"
|
||||
"${CMAKE_CURRENT_LIST_DIR}/ProjectFatalError-expected.sarif")
|
||||
47
Tests/RunCMake/SarifOutput/ProjectFatalError-expected.sarif
Normal file
47
Tests/RunCMake/SarifOutput/ProjectFatalError-expected.sarif
Normal file
@@ -0,0 +1,47 @@
|
||||
{
|
||||
"$schema": "https://schemastore.azurewebsites.net/schemas/json/sarif-2.1.0-rtm.4.json",
|
||||
"runs": [
|
||||
{
|
||||
"results": [
|
||||
{
|
||||
"level": "error",
|
||||
"locations": [
|
||||
{
|
||||
"physicalLocation": {
|
||||
"artifactLocation": {
|
||||
"uri": "PATH:<SOURCE_DIR>/ProjectFatalError.cmake"
|
||||
},
|
||||
"region": {
|
||||
"startLine": 1
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"message": {
|
||||
"text": "Example error"
|
||||
},
|
||||
"ruleId": "CMake.FatalError",
|
||||
"ruleIndex": 0
|
||||
}
|
||||
],
|
||||
"tool": {
|
||||
"driver": {
|
||||
"name": "CMake",
|
||||
"rules": [
|
||||
{
|
||||
"id": "CMake.FatalError",
|
||||
"messageStrings": {
|
||||
"default": {
|
||||
"text": "CMake Error: {0}"
|
||||
}
|
||||
},
|
||||
"name": "CMake Error"
|
||||
}
|
||||
],
|
||||
"version": "<IGNORE>"
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"version": "2.1.0"
|
||||
}
|
||||
1
Tests/RunCMake/SarifOutput/ProjectFatalError-result.txt
Normal file
1
Tests/RunCMake/SarifOutput/ProjectFatalError-result.txt
Normal file
@@ -0,0 +1 @@
|
||||
1
|
||||
4
Tests/RunCMake/SarifOutput/ProjectFatalError-stderr.txt
Normal file
4
Tests/RunCMake/SarifOutput/ProjectFatalError-stderr.txt
Normal file
@@ -0,0 +1,4 @@
|
||||
^CMake Error at ProjectFatalError\.cmake:1 \(message\):
|
||||
Example error
|
||||
Call Stack \(most recent call first\):
|
||||
CMakeLists\.txt:[0-9]+ \(include\)$
|
||||
1
Tests/RunCMake/SarifOutput/ProjectFatalError.cmake
Normal file
1
Tests/RunCMake/SarifOutput/ProjectFatalError.cmake
Normal file
@@ -0,0 +1 @@
|
||||
message(FATAL_ERROR "Example error")
|
||||
25
Tests/RunCMake/SarifOutput/RunCMakeTest.cmake
Normal file
25
Tests/RunCMake/SarifOutput/RunCMakeTest.cmake
Normal file
@@ -0,0 +1,25 @@
|
||||
include(RunCMake)
|
||||
include("${CMAKE_CURRENT_LIST_DIR}/check-sarif.cmake")
|
||||
|
||||
# Default case: the SARIF file should not be generated
|
||||
run_cmake(DefaultSarifOutput)
|
||||
|
||||
# Ensure the expected messages are present in the SARIF output
|
||||
run_cmake_with_options(GenerateSarifResults -DCMAKE_EXPORT_SARIF=ON)
|
||||
|
||||
# Activate SARIF output using the `CMAKE_EXPORT_SARIF` variable
|
||||
run_cmake(ToggleExportSarifVariable)
|
||||
|
||||
# If CMake stops with a fatal error, it should still generate a SARIF file if
|
||||
# requested (and the fatal error should be in the log)
|
||||
run_cmake_with_options(ProjectFatalError -DCMAKE_EXPORT_SARIF=ON)
|
||||
|
||||
# ScriptModeSarifVariable Test: Script mode must ignore the
|
||||
# `CMAKE_EXPORT_SARIF`variable
|
||||
run_cmake_script(ScriptModeSarifVariable -DCMAKE_EXPORT_SARIF=ON)
|
||||
|
||||
# Check that the command-line option can be used to set the file output path
|
||||
run_cmake_with_options(SarifFileArgument --sarif-output=test_cmake_run.sarif)
|
||||
|
||||
# Test the command-line option in script mode as well
|
||||
run_cmake_script(SarifFileArgumentScript --sarif-output=test_cmake_run.sarif)
|
||||
4
Tests/RunCMake/SarifOutput/SarifFileArgument-check.cmake
Normal file
4
Tests/RunCMake/SarifOutput/SarifFileArgument-check.cmake
Normal file
@@ -0,0 +1,4 @@
|
||||
# Make sure the output exists
|
||||
if (NOT EXISTS "${RunCMake_TEST_BINARY_DIR}/test_cmake_run.sarif")
|
||||
message(FATAL_ERROR "SARIF file not generated in the expected location")
|
||||
endif()
|
||||
4
Tests/RunCMake/SarifOutput/SarifFileArgument-stderr.txt
Normal file
4
Tests/RunCMake/SarifOutput/SarifFileArgument-stderr.txt
Normal file
@@ -0,0 +1,4 @@
|
||||
^CMake Warning at SarifFileArgument\.cmake:1 \(message\):
|
||||
SARIF file test
|
||||
Call Stack \(most recent call first\):
|
||||
CMakeLists\.txt:[0-9]+ \(include\)$
|
||||
1
Tests/RunCMake/SarifOutput/SarifFileArgument.cmake
Normal file
1
Tests/RunCMake/SarifOutput/SarifFileArgument.cmake
Normal file
@@ -0,0 +1 @@
|
||||
message(WARNING "SARIF file test")
|
||||
@@ -0,0 +1,4 @@
|
||||
# Make sure the output exists
|
||||
if (NOT EXISTS "${RunCMake_TEST_BINARY_DIR}/test_cmake_run.sarif")
|
||||
message(FATAL_ERROR "SARIF file not generated in the expected location")
|
||||
endif()
|
||||
2
Tests/RunCMake/SarifOutput/SarifFileArgumentScript.cmake
Normal file
2
Tests/RunCMake/SarifOutput/SarifFileArgumentScript.cmake
Normal file
@@ -0,0 +1,2 @@
|
||||
# This won't appear in the SARIF log, but it gives the script something to do.
|
||||
message(STATUS "SARIF file test")
|
||||
@@ -0,0 +1,4 @@
|
||||
# Script mode should ignore the SARIF project variable and export nothing
|
||||
if (EXISTS "${RunCMake_TEST_BINARY_DIR}/.cmake/sarif/cmake.sarif")
|
||||
message(FATAL_ERROR "SARIF file should not have been generated in script mode")
|
||||
endif()
|
||||
3
Tests/RunCMake/SarifOutput/ScriptModeSarifVariable.cmake
Normal file
3
Tests/RunCMake/SarifOutput/ScriptModeSarifVariable.cmake
Normal file
@@ -0,0 +1,3 @@
|
||||
# Try enabling SARIF output in script mode
|
||||
# No file should be generated since script mode ignores the variable
|
||||
set(CMAKE_EXPORT_SARIF ON CACHE BOOL "Export SARIF results" FORCE)
|
||||
@@ -0,0 +1,5 @@
|
||||
include("${CMAKE_CURRENT_LIST_DIR}/check-sarif.cmake")
|
||||
|
||||
# This test should produce the same output as GenerateSarifResults
|
||||
check_sarif_output("${RunCMake_TEST_BINARY_DIR}/.cmake/sarif/cmake.sarif"
|
||||
"${CMAKE_CURRENT_LIST_DIR}/GenerateSarifResults-expected.sarif")
|
||||
@@ -0,0 +1,11 @@
|
||||
^CMake Warning at GenerateSarifResults\.cmake:2 \(message\):
|
||||
Example warning message
|
||||
Call Stack \(most recent call first\):
|
||||
ToggleExportSarifVariable\.cmake:[0-9]+ \(include\)
|
||||
CMakeLists\.txt:[0-9]+ \(include\)
|
||||
+
|
||||
CMake Warning at GenerateSarifResults.cmake:5 \(message\):
|
||||
A second example warning message
|
||||
Call Stack \(most recent call first\):
|
||||
ToggleExportSarifVariable\.cmake:[0-9]+ \(include\)
|
||||
CMakeLists\.txt:[0-9]+ \(include\)$
|
||||
@@ -0,0 +1,6 @@
|
||||
# Generate potential SARIF results
|
||||
include("${CMAKE_CURRENT_LIST_DIR}/GenerateSarifResults.cmake")
|
||||
|
||||
# Enable SARIF logging at the end for the most behavior coverage
|
||||
# All results should be captured regardless of when enabled
|
||||
set(CMAKE_EXPORT_SARIF ON CACHE BOOL "Export SARIF results" FORCE)
|
||||
19
Tests/RunCMake/SarifOutput/check-sarif.cmake
Normal file
19
Tests/RunCMake/SarifOutput/check-sarif.cmake
Normal file
@@ -0,0 +1,19 @@
|
||||
include("${CMAKE_CURRENT_LIST_DIR}/../CXXModules/check-json.cmake")
|
||||
|
||||
# Check that the SARIF results from a test match the expected results
|
||||
macro(check_sarif_output sarif_output_file expected_sarif_output_file)
|
||||
# Make sure the output file exists before reading it
|
||||
if (NOT EXISTS "${sarif_output_file}")
|
||||
message(FATAL_ERROR "SARIF output file not found: ${sarif_output_file}")
|
||||
endif()
|
||||
file(READ "${sarif_output_file}" actual_output)
|
||||
|
||||
# Make sure the expected output file exists before reading it
|
||||
if (NOT EXISTS "${expected_sarif_output_file}")
|
||||
message(FATAL_ERROR "Expected SARIF output file not found: ${expected_sarif_output_file}")
|
||||
endif()
|
||||
file(READ "${expected_sarif_output_file}" expected_output)
|
||||
|
||||
# Check the actual output against the expected output
|
||||
check_json("${actual_output}" "${expected_output}")
|
||||
endmacro()
|
||||
Reference in New Issue
Block a user