diff options
author | Ben Boeckel <ben.boeckel@kitware.com> | 2023-09-24 19:35:43 (GMT) |
---|---|---|
committer | Ben Boeckel <ben.boeckel@kitware.com> | 2024-08-27 16:36:46 (GMT) |
commit | 4df5a6fbb485e631d19ff6488e0cc41ded3a8be8 (patch) | |
tree | 8971e7f06882c99c22c0913fc18e8edb93feac0b | |
parent | 7b8b751637c4a2956ef9d899cf18302da038f4ed (diff) | |
download | CMake-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.txt | 2 | ||||
-rw-r--r-- | Source/cmBuildDatabase.cxx | 374 | ||||
-rw-r--r-- | Source/cmBuildDatabase.h | 49 | ||||
-rwxr-xr-x | bootstrap | 1 |
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; +}; @@ -306,6 +306,7 @@ CMAKE_CXX_SOURCES="\ cmBlockCommand \ cmBreakCommand \ cmBuildCommand \ + cmBuildDatabase \ cmCMakeLanguageCommand \ cmCMakeMinimumRequired \ cmList \ |