| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383 | 
							- /* 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;
 
- }
 
 
  |