/* 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 #include #include #include #include #include #include #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::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(); 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 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 const& components) { cmBuildDatabase db; for (auto const& component : components) { db.Sets.insert(db.Sets.end(), component.Sets.begin(), component.Sets.end()); } return db; }