cmBuildDatabase.cxx 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374
  1. /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
  2. file Copyright.txt or https://cmake.org/licensing for details. */
  3. #include "cmBuildDatabase.h"
  4. #include <utility>
  5. #include <cm/memory>
  6. #include <cm/string_view>
  7. #include <cmext/string_view>
  8. #include <cm3p/json/reader.h>
  9. #include <cm3p/json/value.h>
  10. #include <cm3p/json/writer.h>
  11. #include "cmsys/FStream.hxx"
  12. #include "cmGeneratedFileStream.h"
  13. #include "cmStringAlgorithms.h"
  14. #include "cmSystemTools.h"
  15. cmBuildDatabase::cmBuildDatabase() = default;
  16. cmBuildDatabase::cmBuildDatabase(cmBuildDatabase const&) = default;
  17. cmBuildDatabase::~cmBuildDatabase() = default;
  18. void cmBuildDatabase::Write(std::string const& path) const
  19. {
  20. Json::Value mcdb = Json::objectValue;
  21. mcdb["version"] = 1;
  22. mcdb["revision"] = 0;
  23. Json::Value& sets = mcdb["sets"] = Json::arrayValue;
  24. for (auto const& Set_ : this->Sets) {
  25. Json::Value set = Json::objectValue;
  26. set["name"] = Set_.Name;
  27. set["family-name"] = Set_.FamilyName;
  28. Json::Value& visible_sets = set["visible-sets"] = Json::arrayValue;
  29. for (auto const& VisibleSet : Set_.VisibleSets) {
  30. visible_sets.append(VisibleSet);
  31. }
  32. Json::Value& tus = set["translation-units"] = Json::arrayValue;
  33. for (auto const& TranslationUnit_ : Set_.TranslationUnits) {
  34. Json::Value tu = Json::objectValue;
  35. if (!TranslationUnit_.WorkDirectory.empty()) {
  36. tu["work-directory"] = TranslationUnit_.WorkDirectory;
  37. }
  38. tu["source"] = TranslationUnit_.Source;
  39. if (TranslationUnit_.Object) {
  40. tu["object"] = *TranslationUnit_.Object;
  41. }
  42. tu["private"] = TranslationUnit_.Private;
  43. Json::Value& reqs = tu["requires"] = Json::arrayValue;
  44. for (auto const& Require : TranslationUnit_.Requires) {
  45. reqs.append(Require);
  46. }
  47. Json::Value& provides = tu["provides"] = Json::objectValue;
  48. for (auto const& Provide : TranslationUnit_.Provides) {
  49. provides[Provide.first] = Provide.second;
  50. }
  51. Json::Value& baseline_arguments = tu["baseline-arguments"] =
  52. Json::arrayValue;
  53. for (auto const& BaselineArgument : TranslationUnit_.BaselineArguments) {
  54. baseline_arguments.append(BaselineArgument);
  55. }
  56. Json::Value& local_arguments = tu["local-arguments"] = Json::arrayValue;
  57. for (auto const& LocalArgument : TranslationUnit_.LocalArguments) {
  58. local_arguments.append(LocalArgument);
  59. }
  60. Json::Value& arguments = tu["arguments"] = Json::arrayValue;
  61. for (auto const& Argument : TranslationUnit_.Arguments) {
  62. arguments.append(Argument);
  63. }
  64. tus.append(tu);
  65. }
  66. sets.append(set);
  67. }
  68. cmGeneratedFileStream mcdbf(path);
  69. mcdbf << mcdb;
  70. }
  71. static bool ParseFilename(Json::Value const& val, std::string& result)
  72. {
  73. if (val.isString()) {
  74. result = val.asString();
  75. } else {
  76. return false;
  77. }
  78. return true;
  79. }
  80. #define PARSE_BLOB(val, res) \
  81. do { \
  82. if (!ParseFilename(val, res)) { \
  83. cmSystemTools::Error(cmStrCat("-E cmake_module_compile_db failed to ", \
  84. "parse ", path, ": invalid blob")); \
  85. return {}; \
  86. } \
  87. } while (0)
  88. #define PARSE_FILENAME(val, res, make_full) \
  89. do { \
  90. if (!ParseFilename(val, res)) { \
  91. cmSystemTools::Error(cmStrCat("-E cmake_module_compile_db failed to ", \
  92. "parse ", path, ": invalid filename")); \
  93. return {}; \
  94. } \
  95. \
  96. if (make_full && work_directory && !work_directory->empty() && \
  97. !cmSystemTools::FileIsFullPath(res)) { \
  98. res = cmStrCat(*work_directory, '/', res); \
  99. } \
  100. } while (0)
  101. std::unique_ptr<cmBuildDatabase> cmBuildDatabase::Load(std::string const& path)
  102. {
  103. Json::Value mcdb;
  104. {
  105. cmsys::ifstream mcdbf(path.c_str(), std::ios::in | std::ios::binary);
  106. Json::Reader reader;
  107. if (!reader.parse(mcdbf, mcdb, false)) {
  108. cmSystemTools::Error(
  109. cmStrCat("-E cmake_module_compile_db failed to parse ", path,
  110. reader.getFormattedErrorMessages()));
  111. return {};
  112. }
  113. }
  114. Json::Value const& version = mcdb["version"];
  115. if (version.asUInt() > 1) {
  116. cmSystemTools::Error(
  117. cmStrCat("-E cmake_module_compile_db failed to parse ", path,
  118. ": version ", version.asString()));
  119. return {};
  120. }
  121. auto db = cm::make_unique<cmBuildDatabase>();
  122. Json::Value const& sets = mcdb["sets"];
  123. if (sets.isArray()) {
  124. for (auto const& set : sets) {
  125. Set Set_;
  126. Json::Value const& name = set["name"];
  127. if (!name.isString()) {
  128. cmSystemTools::Error(
  129. cmStrCat("-E cmake_module_compile_db failed to parse ", path,
  130. ": name is not a string"));
  131. return {};
  132. }
  133. Set_.Name = name.asString();
  134. Json::Value const& family_name = set["family-name"];
  135. if (!family_name.isString()) {
  136. cmSystemTools::Error(
  137. cmStrCat("-E cmake_module_compile_db failed to parse ", path,
  138. ": family-name is not a string"));
  139. return {};
  140. }
  141. Set_.FamilyName = family_name.asString();
  142. Json::Value const& visible_sets = set["visible-sets"];
  143. if (!visible_sets.isArray()) {
  144. cmSystemTools::Error(
  145. cmStrCat("-E cmake_module_compile_db failed to parse ", path,
  146. ": visible-sets is not an array"));
  147. return {};
  148. }
  149. for (auto const& visible_set : visible_sets) {
  150. if (!visible_set.isString()) {
  151. cmSystemTools::Error(
  152. cmStrCat("-E cmake_module_compile_db failed to parse ", path,
  153. ": a visible-sets item is not a string"));
  154. return {};
  155. }
  156. Set_.VisibleSets.emplace_back(visible_set.asString());
  157. }
  158. Json::Value const& translation_units = set["translation-units"];
  159. if (!translation_units.isArray()) {
  160. cmSystemTools::Error(
  161. cmStrCat("-E cmake_module_compile_db failed to parse ", path,
  162. ": translation-units is not an array"));
  163. return {};
  164. }
  165. for (auto const& translation_unit : translation_units) {
  166. if (!translation_unit.isObject()) {
  167. cmSystemTools::Error(
  168. cmStrCat("-E cmake_module_compile_db failed to parse ", path,
  169. ": a translation-units item is not an object"));
  170. return {};
  171. }
  172. TranslationUnit TranslationUnit_;
  173. cm::optional<std::string> work_directory;
  174. Json::Value const& workdir = translation_unit["work-directory"];
  175. if (workdir.isString()) {
  176. PARSE_BLOB(workdir, TranslationUnit_.WorkDirectory);
  177. work_directory = TranslationUnit_.WorkDirectory;
  178. } else if (!workdir.isNull()) {
  179. cmSystemTools::Error(
  180. cmStrCat("-E cmake_module_compile_db failed to parse ", path,
  181. ": work-directory is not a string"));
  182. return {};
  183. }
  184. Json::Value const& source = translation_unit["source"];
  185. PARSE_FILENAME(source, TranslationUnit_.Source, true);
  186. if (translation_unit.isMember("object")) {
  187. Json::Value const& object = translation_unit["object"];
  188. if (!object.isNull()) {
  189. TranslationUnit_.Object = "";
  190. PARSE_FILENAME(object, *TranslationUnit_.Object, false);
  191. }
  192. }
  193. if (translation_unit.isMember("private")) {
  194. Json::Value const& priv = translation_unit["private"];
  195. if (!priv.isBool()) {
  196. cmSystemTools::Error(
  197. cmStrCat("-E cmake_module_compile_db failed to parse ", path,
  198. ": private is not a boolean"));
  199. return {};
  200. }
  201. TranslationUnit_.Private = priv.asBool();
  202. }
  203. if (translation_unit.isMember("requires")) {
  204. Json::Value const& reqs = translation_unit["requires"];
  205. if (!reqs.isArray()) {
  206. cmSystemTools::Error(
  207. cmStrCat("-E cmake_module_compile_db failed to parse ", path,
  208. ": requires is not an array"));
  209. return {};
  210. }
  211. for (auto const& require : reqs) {
  212. if (!require.isString()) {
  213. cmSystemTools::Error(
  214. cmStrCat("-E cmake_module_compile_db failed to parse ", path,
  215. ": a requires item is not a string"));
  216. return {};
  217. }
  218. TranslationUnit_.Requires.emplace_back(require.asString());
  219. }
  220. }
  221. if (translation_unit.isMember("provides")) {
  222. Json::Value const& provides = translation_unit["provides"];
  223. if (!provides.isObject()) {
  224. cmSystemTools::Error(
  225. cmStrCat("-E cmake_module_compile_db failed to parse ", path,
  226. ": provides is not an object"));
  227. return {};
  228. }
  229. for (auto i = provides.begin(); i != provides.end(); ++i) {
  230. if (!i->isString()) {
  231. cmSystemTools::Error(
  232. cmStrCat("-E cmake_module_compile_db failed to parse ", path,
  233. ": a provides value is not a string"));
  234. return {};
  235. }
  236. TranslationUnit_.Provides[i.key().asString()] = i->asString();
  237. }
  238. }
  239. if (translation_unit.isMember("baseline-arguments")) {
  240. Json::Value const& baseline_arguments =
  241. translation_unit["baseline-arguments"];
  242. if (!baseline_arguments.isArray()) {
  243. cmSystemTools::Error(
  244. cmStrCat("-E cmake_module_compile_db failed to parse ", path,
  245. ": baseline_arguments is not an array"));
  246. return {};
  247. }
  248. for (auto const& baseline_argument : baseline_arguments) {
  249. if (baseline_argument.isString()) {
  250. TranslationUnit_.BaselineArguments.emplace_back(
  251. baseline_argument.asString());
  252. } else {
  253. cmSystemTools::Error(
  254. cmStrCat("-E cmake_module_compile_db failed to parse ", path,
  255. ": a baseline argument is not a string"));
  256. return {};
  257. }
  258. }
  259. }
  260. if (translation_unit.isMember("local-arguments")) {
  261. Json::Value const& local_arguments =
  262. translation_unit["local-arguments"];
  263. if (!local_arguments.isArray()) {
  264. cmSystemTools::Error(
  265. cmStrCat("-E cmake_module_compile_db failed to parse ", path,
  266. ": local_arguments is not an array"));
  267. return {};
  268. }
  269. for (auto const& local_argument : local_arguments) {
  270. if (local_argument.isString()) {
  271. TranslationUnit_.LocalArguments.emplace_back(
  272. local_argument.asString());
  273. } else {
  274. cmSystemTools::Error(
  275. cmStrCat("-E cmake_module_compile_db failed to parse ", path,
  276. ": a local argument is not a string"));
  277. return {};
  278. }
  279. }
  280. }
  281. if (translation_unit.isMember("arguments")) {
  282. Json::Value const& arguments = translation_unit["arguments"];
  283. if (!arguments.isArray()) {
  284. cmSystemTools::Error(
  285. cmStrCat("-E cmake_module_compile_db failed to parse ", path,
  286. ": arguments is not an array"));
  287. return {};
  288. }
  289. for (auto const& argument : arguments) {
  290. if (argument.isString()) {
  291. TranslationUnit_.Arguments.emplace_back(argument.asString());
  292. } else {
  293. cmSystemTools::Error(
  294. cmStrCat("-E cmake_module_compile_db failed to parse ", path,
  295. ": an argument is not a string"));
  296. return {};
  297. }
  298. }
  299. }
  300. Set_.TranslationUnits.emplace_back(std::move(TranslationUnit_));
  301. }
  302. db->Sets.emplace_back(std::move(Set_));
  303. }
  304. }
  305. return db;
  306. }
  307. cmBuildDatabase cmBuildDatabase::Merge(
  308. std::vector<cmBuildDatabase> const& components)
  309. {
  310. cmBuildDatabase db;
  311. for (auto const& component : components) {
  312. db.Sets.insert(db.Sets.end(), component.Sets.begin(),
  313. component.Sets.end());
  314. }
  315. return db;
  316. }