Selaa lähdekoodia

cmBuildDatabase: add initial structures

This class represents a build database as introduced by P2977R0. It
includes support for reading, writing, and merging.

See: http://wg21.link/p2977r0
Ben Boeckel 2 vuotta sitten
vanhempi
sitoutus
4df5a6fbb4
4 muutettua tiedostoa jossa 426 lisäystä ja 0 poistoa
  1. 2 0
      Source/CMakeLists.txt
  2. 374 0
      Source/cmBuildDatabase.cxx
  3. 49 0
      Source/cmBuildDatabase.h
  4. 1 0
      bootstrap

+ 2 - 0
Source/CMakeLists.txt

@@ -121,6 +121,8 @@ add_library(
   cmBinUtilsWindowsPELinker.h
   cmBinUtilsWindowsPEObjdumpGetRuntimeDependenciesTool.cxx
   cmBinUtilsWindowsPEObjdumpGetRuntimeDependenciesTool.h
+  cmBuildDatabase.cxx
+  cmBuildDatabase.h
   cmBuildOptions.h
   cmCacheManager.cxx
   cmCacheManager.h

+ 374 - 0
Source/cmBuildDatabase.cxx

@@ -0,0 +1,374 @@
+/* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
+   file Copyright.txt or https://cmake.org/licensing for details.  */
+#include "cmBuildDatabase.h"
+
+#include <utility>
+
+#include <cm/memory>
+#include <cm/string_view>
+#include <cmext/string_view>
+
+#include <cm3p/json/reader.h>
+#include <cm3p/json/value.h>
+#include <cm3p/json/writer.h>
+
+#include "cmsys/FStream.hxx"
+
+#include "cmGeneratedFileStream.h"
+#include "cmStringAlgorithms.h"
+#include "cmSystemTools.h"
+
+cmBuildDatabase::cmBuildDatabase() = default;
+cmBuildDatabase::cmBuildDatabase(cmBuildDatabase const&) = default;
+cmBuildDatabase::~cmBuildDatabase() = default;
+
+void cmBuildDatabase::Write(std::string const& path) const
+{
+  Json::Value mcdb = Json::objectValue;
+
+  mcdb["version"] = 1;
+  mcdb["revision"] = 0;
+
+  Json::Value& sets = mcdb["sets"] = Json::arrayValue;
+
+  for (auto const& Set_ : this->Sets) {
+    Json::Value set = Json::objectValue;
+
+    set["name"] = Set_.Name;
+    set["family-name"] = Set_.FamilyName;
+
+    Json::Value& visible_sets = set["visible-sets"] = Json::arrayValue;
+    for (auto const& VisibleSet : Set_.VisibleSets) {
+      visible_sets.append(VisibleSet);
+    }
+
+    Json::Value& tus = set["translation-units"] = Json::arrayValue;
+    for (auto const& TranslationUnit_ : Set_.TranslationUnits) {
+      Json::Value tu = Json::objectValue;
+
+      if (!TranslationUnit_.WorkDirectory.empty()) {
+        tu["work-directory"] = TranslationUnit_.WorkDirectory;
+      }
+      tu["source"] = TranslationUnit_.Source;
+      if (TranslationUnit_.Object) {
+        tu["object"] = *TranslationUnit_.Object;
+      }
+      tu["private"] = TranslationUnit_.Private;
+
+      Json::Value& reqs = tu["requires"] = Json::arrayValue;
+      for (auto const& Require : TranslationUnit_.Requires) {
+        reqs.append(Require);
+      }
+
+      Json::Value& provides = tu["provides"] = Json::objectValue;
+      for (auto const& Provide : TranslationUnit_.Provides) {
+        provides[Provide.first] = Provide.second;
+      }
+
+      Json::Value& baseline_arguments = tu["baseline-arguments"] =
+        Json::arrayValue;
+      for (auto const& BaselineArgument : TranslationUnit_.BaselineArguments) {
+        baseline_arguments.append(BaselineArgument);
+      }
+
+      Json::Value& local_arguments = tu["local-arguments"] = Json::arrayValue;
+      for (auto const& LocalArgument : TranslationUnit_.LocalArguments) {
+        local_arguments.append(LocalArgument);
+      }
+
+      Json::Value& arguments = tu["arguments"] = Json::arrayValue;
+      for (auto const& Argument : TranslationUnit_.Arguments) {
+        arguments.append(Argument);
+      }
+
+      tus.append(tu);
+    }
+
+    sets.append(set);
+  }
+
+  cmGeneratedFileStream mcdbf(path);
+  mcdbf << mcdb;
+}
+
+static bool ParseFilename(Json::Value const& val, std::string& result)
+{
+  if (val.isString()) {
+    result = val.asString();
+  } else {
+    return false;
+  }
+
+  return true;
+}
+
+#define PARSE_BLOB(val, res)                                                  \
+  do {                                                                        \
+    if (!ParseFilename(val, res)) {                                           \
+      cmSystemTools::Error(cmStrCat("-E cmake_module_compile_db failed to ",  \
+                                    "parse ", path, ": invalid blob"));       \
+      return {};                                                              \
+    }                                                                         \
+  } while (0)
+
+#define PARSE_FILENAME(val, res, make_full)                                   \
+  do {                                                                        \
+    if (!ParseFilename(val, res)) {                                           \
+      cmSystemTools::Error(cmStrCat("-E cmake_module_compile_db failed to ",  \
+                                    "parse ", path, ": invalid filename"));   \
+      return {};                                                              \
+    }                                                                         \
+                                                                              \
+    if (make_full && work_directory && !work_directory->empty() &&            \
+        !cmSystemTools::FileIsFullPath(res)) {                                \
+      res = cmStrCat(*work_directory, '/', res);                              \
+    }                                                                         \
+  } while (0)
+
+std::unique_ptr<cmBuildDatabase> cmBuildDatabase::Load(std::string const& path)
+{
+  Json::Value mcdb;
+  {
+    cmsys::ifstream mcdbf(path.c_str(), std::ios::in | std::ios::binary);
+    Json::Reader reader;
+    if (!reader.parse(mcdbf, mcdb, false)) {
+      cmSystemTools::Error(
+        cmStrCat("-E cmake_module_compile_db failed to parse ", path,
+                 reader.getFormattedErrorMessages()));
+      return {};
+    }
+  }
+
+  Json::Value const& version = mcdb["version"];
+  if (version.asUInt() > 1) {
+    cmSystemTools::Error(
+      cmStrCat("-E cmake_module_compile_db failed to parse ", path,
+               ": version ", version.asString()));
+    return {};
+  }
+
+  auto db = cm::make_unique<cmBuildDatabase>();
+
+  Json::Value const& sets = mcdb["sets"];
+  if (sets.isArray()) {
+    for (auto const& set : sets) {
+      Set Set_;
+
+      Json::Value const& name = set["name"];
+      if (!name.isString()) {
+        cmSystemTools::Error(
+          cmStrCat("-E cmake_module_compile_db failed to parse ", path,
+                   ": name is not a string"));
+        return {};
+      }
+      Set_.Name = name.asString();
+
+      Json::Value const& family_name = set["family-name"];
+      if (!family_name.isString()) {
+        cmSystemTools::Error(
+          cmStrCat("-E cmake_module_compile_db failed to parse ", path,
+                   ": family-name is not a string"));
+        return {};
+      }
+      Set_.FamilyName = family_name.asString();
+
+      Json::Value const& visible_sets = set["visible-sets"];
+      if (!visible_sets.isArray()) {
+        cmSystemTools::Error(
+          cmStrCat("-E cmake_module_compile_db failed to parse ", path,
+                   ": visible-sets is not an array"));
+        return {};
+      }
+      for (auto const& visible_set : visible_sets) {
+        if (!visible_set.isString()) {
+          cmSystemTools::Error(
+            cmStrCat("-E cmake_module_compile_db failed to parse ", path,
+                     ": a visible-sets item is not a string"));
+          return {};
+        }
+
+        Set_.VisibleSets.emplace_back(visible_set.asString());
+      }
+
+      Json::Value const& translation_units = set["translation-units"];
+      if (!translation_units.isArray()) {
+        cmSystemTools::Error(
+          cmStrCat("-E cmake_module_compile_db failed to parse ", path,
+                   ": translation-units is not an array"));
+        return {};
+      }
+      for (auto const& translation_unit : translation_units) {
+        if (!translation_unit.isObject()) {
+          cmSystemTools::Error(
+            cmStrCat("-E cmake_module_compile_db failed to parse ", path,
+                     ": a translation-units item is not an object"));
+          return {};
+        }
+
+        TranslationUnit TranslationUnit_;
+
+        cm::optional<std::string> work_directory;
+        Json::Value const& workdir = translation_unit["work-directory"];
+        if (workdir.isString()) {
+          PARSE_BLOB(workdir, TranslationUnit_.WorkDirectory);
+          work_directory = TranslationUnit_.WorkDirectory;
+        } else if (!workdir.isNull()) {
+          cmSystemTools::Error(
+            cmStrCat("-E cmake_module_compile_db failed to parse ", path,
+                     ": work-directory is not a string"));
+          return {};
+        }
+
+        Json::Value const& source = translation_unit["source"];
+        PARSE_FILENAME(source, TranslationUnit_.Source, true);
+
+        if (translation_unit.isMember("object")) {
+          Json::Value const& object = translation_unit["object"];
+          if (!object.isNull()) {
+            TranslationUnit_.Object = "";
+            PARSE_FILENAME(object, *TranslationUnit_.Object, false);
+          }
+        }
+
+        if (translation_unit.isMember("private")) {
+          Json::Value const& priv = translation_unit["private"];
+          if (!priv.isBool()) {
+            cmSystemTools::Error(
+              cmStrCat("-E cmake_module_compile_db failed to parse ", path,
+                       ": private is not a boolean"));
+            return {};
+          }
+          TranslationUnit_.Private = priv.asBool();
+        }
+
+        if (translation_unit.isMember("requires")) {
+          Json::Value const& reqs = translation_unit["requires"];
+          if (!reqs.isArray()) {
+            cmSystemTools::Error(
+              cmStrCat("-E cmake_module_compile_db failed to parse ", path,
+                       ": requires is not an array"));
+            return {};
+          }
+
+          for (auto const& require : reqs) {
+            if (!require.isString()) {
+              cmSystemTools::Error(
+                cmStrCat("-E cmake_module_compile_db failed to parse ", path,
+                         ": a requires item is not a string"));
+              return {};
+            }
+
+            TranslationUnit_.Requires.emplace_back(require.asString());
+          }
+        }
+
+        if (translation_unit.isMember("provides")) {
+          Json::Value const& provides = translation_unit["provides"];
+          if (!provides.isObject()) {
+            cmSystemTools::Error(
+              cmStrCat("-E cmake_module_compile_db failed to parse ", path,
+                       ": provides is not an object"));
+            return {};
+          }
+
+          for (auto i = provides.begin(); i != provides.end(); ++i) {
+            if (!i->isString()) {
+              cmSystemTools::Error(
+                cmStrCat("-E cmake_module_compile_db failed to parse ", path,
+                         ": a provides value is not a string"));
+              return {};
+            }
+
+            TranslationUnit_.Provides[i.key().asString()] = i->asString();
+          }
+        }
+
+        if (translation_unit.isMember("baseline-arguments")) {
+          Json::Value const& baseline_arguments =
+            translation_unit["baseline-arguments"];
+          if (!baseline_arguments.isArray()) {
+            cmSystemTools::Error(
+              cmStrCat("-E cmake_module_compile_db failed to parse ", path,
+                       ": baseline_arguments is not an array"));
+            return {};
+          }
+
+          for (auto const& baseline_argument : baseline_arguments) {
+            if (baseline_argument.isString()) {
+              TranslationUnit_.BaselineArguments.emplace_back(
+                baseline_argument.asString());
+            } else {
+              cmSystemTools::Error(
+                cmStrCat("-E cmake_module_compile_db failed to parse ", path,
+                         ": a baseline argument is not a string"));
+              return {};
+            }
+          }
+        }
+
+        if (translation_unit.isMember("local-arguments")) {
+          Json::Value const& local_arguments =
+            translation_unit["local-arguments"];
+          if (!local_arguments.isArray()) {
+            cmSystemTools::Error(
+              cmStrCat("-E cmake_module_compile_db failed to parse ", path,
+                       ": local_arguments is not an array"));
+            return {};
+          }
+
+          for (auto const& local_argument : local_arguments) {
+            if (local_argument.isString()) {
+              TranslationUnit_.LocalArguments.emplace_back(
+                local_argument.asString());
+            } else {
+              cmSystemTools::Error(
+                cmStrCat("-E cmake_module_compile_db failed to parse ", path,
+                         ": a local argument is not a string"));
+              return {};
+            }
+          }
+        }
+
+        if (translation_unit.isMember("arguments")) {
+          Json::Value const& arguments = translation_unit["arguments"];
+          if (!arguments.isArray()) {
+            cmSystemTools::Error(
+              cmStrCat("-E cmake_module_compile_db failed to parse ", path,
+                       ": arguments is not an array"));
+            return {};
+          }
+
+          for (auto const& argument : arguments) {
+            if (argument.isString()) {
+              TranslationUnit_.Arguments.emplace_back(argument.asString());
+            } else {
+              cmSystemTools::Error(
+                cmStrCat("-E cmake_module_compile_db failed to parse ", path,
+                         ": an argument is not a string"));
+              return {};
+            }
+          }
+        }
+
+        Set_.TranslationUnits.emplace_back(std::move(TranslationUnit_));
+      }
+
+      db->Sets.emplace_back(std::move(Set_));
+    }
+  }
+
+  return db;
+}
+
+cmBuildDatabase cmBuildDatabase::Merge(
+  std::vector<cmBuildDatabase> const& components)
+{
+  cmBuildDatabase db;
+
+  for (auto const& component : components) {
+    db.Sets.insert(db.Sets.end(), component.Sets.begin(),
+                   component.Sets.end());
+  }
+
+  return db;
+}

+ 49 - 0
Source/cmBuildDatabase.h

@@ -0,0 +1,49 @@
+/* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
+   file Copyright.txt or https://cmake.org/licensing for details.  */
+#pragma once
+
+#include "cmConfigure.h" // IWYU pragma: keep
+
+#include <map>
+#include <memory>
+#include <string>
+#include <vector>
+
+#include <cm/optional>
+
+class cmBuildDatabase
+{
+public:
+  struct TranslationUnit
+  {
+    std::string WorkDirectory;
+    std::string Source;
+    cm::optional<std::string> Object;
+    std::vector<std::string> Requires;
+    std::map<std::string, std::string> Provides;
+    std::vector<std::string> BaselineArguments;
+    std::vector<std::string> LocalArguments;
+    std::vector<std::string> Arguments;
+    bool Private = false;
+  };
+
+  struct Set
+  {
+    std::string Name;
+    std::string FamilyName;
+    std::vector<std::string> VisibleSets;
+    std::vector<TranslationUnit> TranslationUnits;
+  };
+
+  cmBuildDatabase();
+  cmBuildDatabase(cmBuildDatabase const&);
+  ~cmBuildDatabase();
+
+  void Write(std::string const& path) const;
+
+  static std::unique_ptr<cmBuildDatabase> Load(std::string const& path);
+  static cmBuildDatabase Merge(std::vector<cmBuildDatabase> const& components);
+
+private:
+  std::vector<Set> Sets;
+};

+ 1 - 0
bootstrap

@@ -306,6 +306,7 @@ CMAKE_CXX_SOURCES="\
   cmBlockCommand \
   cmBreakCommand \
   cmBuildCommand \
+  cmBuildDatabase \
   cmCMakeLanguageCommand \
   cmCMakeMinimumRequired \
   cmList \