summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorBen Boeckel <ben.boeckel@kitware.com>2023-09-24 19:35:43 (GMT)
committerBen Boeckel <ben.boeckel@kitware.com>2024-08-27 16:36:46 (GMT)
commit4df5a6fbb485e631d19ff6488e0cc41ded3a8be8 (patch)
tree8971e7f06882c99c22c0913fc18e8edb93feac0b
parent7b8b751637c4a2956ef9d899cf18302da038f4ed (diff)
downloadCMake-4df5a6fbb485e631d19ff6488e0cc41ded3a8be8.zip
CMake-4df5a6fbb485e631d19ff6488e0cc41ded3a8be8.tar.gz
CMake-4df5a6fbb485e631d19ff6488e0cc41ded3a8be8.tar.bz2
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
-rw-r--r--Source/CMakeLists.txt2
-rw-r--r--Source/cmBuildDatabase.cxx374
-rw-r--r--Source/cmBuildDatabase.h49
-rwxr-xr-xbootstrap1
4 files changed, 426 insertions, 0 deletions
diff --git a/Source/CMakeLists.txt b/Source/CMakeLists.txt
index 2dc3bcc..c4cd101 100644
--- a/Source/CMakeLists.txt
+++ b/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
diff --git a/Source/cmBuildDatabase.cxx b/Source/cmBuildDatabase.cxx
new file mode 100644
index 0000000..52220f5
--- /dev/null
+++ b/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;
+}
diff --git a/Source/cmBuildDatabase.h b/Source/cmBuildDatabase.h
new file mode 100644
index 0000000..0b87562
--- /dev/null
+++ b/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;
+};
diff --git a/bootstrap b/bootstrap
index 96d331f..53358d5 100755
--- a/bootstrap
+++ b/bootstrap
@@ -306,6 +306,7 @@ CMAKE_CXX_SOURCES="\
cmBlockCommand \
cmBreakCommand \
cmBuildCommand \
+ cmBuildDatabase \
cmCMakeLanguageCommand \
cmCMakeMinimumRequired \
cmList \