/src/CMake/Source/cmBuildDatabase.cxx
Line | Count | Source |
1 | | /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying |
2 | | file LICENSE.rst or https://cmake.org/licensing for details. */ |
3 | | #include "cmBuildDatabase.h" |
4 | | |
5 | | #include <cstdlib> |
6 | | #include <set> |
7 | | #include <utility> |
8 | | |
9 | | #include <cm/memory> |
10 | | #include <cm/string_view> |
11 | | #include <cmext/string_view> |
12 | | |
13 | | #include <cm3p/json/reader.h> |
14 | | #include <cm3p/json/value.h> |
15 | | #include <cm3p/json/writer.h> |
16 | | |
17 | | #include "cmsys/FStream.hxx" |
18 | | |
19 | | #include "cmComputeLinkInformation.h" |
20 | | #include "cmFileSetMetadata.h" |
21 | | #include "cmGeneratedFileStream.h" |
22 | | #include "cmGeneratorFileSet.h" |
23 | | #include "cmGeneratorTarget.h" |
24 | | #include "cmGlobalGenerator.h" |
25 | | #include "cmListFileCache.h" |
26 | | #include "cmLocalGenerator.h" |
27 | | #include "cmSourceFile.h" |
28 | | #include "cmStringAlgorithms.h" |
29 | | #include "cmSystemTools.h" |
30 | | |
31 | | namespace { |
32 | | |
33 | | std::string PlaceholderName = "<__CMAKE_UNKNOWN>"; |
34 | | |
35 | | } |
36 | | |
37 | 0 | cmBuildDatabase::cmBuildDatabase() = default; |
38 | 0 | cmBuildDatabase::cmBuildDatabase(cmBuildDatabase const&) = default; |
39 | 0 | cmBuildDatabase::~cmBuildDatabase() = default; |
40 | | |
41 | | cmBuildDatabase::LookupTable cmBuildDatabase::GenerateLookupTable() |
42 | 0 | { |
43 | 0 | LookupTable lut; |
44 | |
|
45 | 0 | for (auto& Set_ : this->Sets) { |
46 | 0 | for (auto& TranslationUnit_ : Set_.TranslationUnits) { |
47 | | // This table is from source path to TU instance. This is fine because a |
48 | | // single target (where this is used) cannot contain the same source file |
49 | | // multiple times. |
50 | 0 | lut[TranslationUnit_.Source] = &TranslationUnit_; |
51 | 0 | } |
52 | 0 | } |
53 | |
|
54 | 0 | return lut; |
55 | 0 | } |
56 | | |
57 | | bool cmBuildDatabase::HasPlaceholderNames() const |
58 | 0 | { |
59 | 0 | for (auto const& Set_ : this->Sets) { |
60 | 0 | for (auto const& TranslationUnit_ : Set_.TranslationUnits) { |
61 | 0 | for (auto const& provide : TranslationUnit_.Provides) { |
62 | 0 | if (provide.first == PlaceholderName) { |
63 | 0 | return true; |
64 | 0 | } |
65 | 0 | if (provide.second == PlaceholderName) { |
66 | 0 | return true; |
67 | 0 | } |
68 | 0 | } |
69 | 0 | } |
70 | 0 | } |
71 | | |
72 | 0 | return false; |
73 | 0 | } |
74 | | |
75 | | void cmBuildDatabase::Write(std::string const& path) const |
76 | 0 | { |
77 | 0 | Json::Value mcdb = Json::objectValue; |
78 | |
|
79 | 0 | mcdb["version"] = 1; |
80 | 0 | mcdb["revision"] = 0; |
81 | |
|
82 | 0 | Json::Value& sets = mcdb["sets"] = Json::arrayValue; |
83 | |
|
84 | 0 | for (auto const& Set_ : this->Sets) { |
85 | 0 | Json::Value set = Json::objectValue; |
86 | |
|
87 | 0 | set["name"] = Set_.Name; |
88 | 0 | set["family-name"] = Set_.FamilyName; |
89 | |
|
90 | 0 | Json::Value& visible_sets = set["visible-sets"] = Json::arrayValue; |
91 | 0 | for (auto const& VisibleSet : Set_.VisibleSets) { |
92 | 0 | visible_sets.append(VisibleSet); |
93 | 0 | } |
94 | |
|
95 | 0 | Json::Value& tus = set["translation-units"] = Json::arrayValue; |
96 | 0 | for (auto const& TranslationUnit_ : Set_.TranslationUnits) { |
97 | 0 | Json::Value tu = Json::objectValue; |
98 | |
|
99 | 0 | if (!TranslationUnit_.WorkDirectory.empty()) { |
100 | 0 | tu["work-directory"] = TranslationUnit_.WorkDirectory; |
101 | 0 | } |
102 | 0 | tu["source"] = TranslationUnit_.Source; |
103 | 0 | if (TranslationUnit_.Object) { |
104 | 0 | tu["object"] = *TranslationUnit_.Object; |
105 | 0 | } |
106 | 0 | tu["private"] = TranslationUnit_.Private; |
107 | |
|
108 | 0 | Json::Value& reqs = tu["requires"] = Json::arrayValue; |
109 | 0 | for (auto const& Require : TranslationUnit_.Requires) { |
110 | 0 | reqs.append(Require); |
111 | 0 | } |
112 | |
|
113 | 0 | Json::Value& provides = tu["provides"] = Json::objectValue; |
114 | 0 | for (auto const& Provide : TranslationUnit_.Provides) { |
115 | 0 | provides[Provide.first] = Provide.second; |
116 | 0 | } |
117 | |
|
118 | 0 | Json::Value& baseline_arguments = tu["baseline-arguments"] = |
119 | 0 | Json::arrayValue; |
120 | 0 | for (auto const& BaselineArgument : TranslationUnit_.BaselineArguments) { |
121 | 0 | baseline_arguments.append(BaselineArgument); |
122 | 0 | } |
123 | |
|
124 | 0 | Json::Value& local_arguments = tu["local-arguments"] = Json::arrayValue; |
125 | 0 | for (auto const& LocalArgument : TranslationUnit_.LocalArguments) { |
126 | 0 | local_arguments.append(LocalArgument); |
127 | 0 | } |
128 | |
|
129 | 0 | Json::Value& arguments = tu["arguments"] = Json::arrayValue; |
130 | 0 | for (auto const& Argument : TranslationUnit_.Arguments) { |
131 | 0 | arguments.append(Argument); |
132 | 0 | } |
133 | |
|
134 | 0 | tus.append(tu); |
135 | 0 | } |
136 | |
|
137 | 0 | sets.append(set); |
138 | 0 | } |
139 | |
|
140 | 0 | cmGeneratedFileStream mcdbf(path); |
141 | 0 | mcdbf << mcdb; |
142 | 0 | } |
143 | | |
144 | | static bool ParseFilename(Json::Value const& val, std::string& result) |
145 | 0 | { |
146 | 0 | if (val.isString()) { |
147 | 0 | result = val.asString(); |
148 | 0 | } else { |
149 | 0 | return false; |
150 | 0 | } |
151 | | |
152 | 0 | return true; |
153 | 0 | } |
154 | | |
155 | | #define PARSE_BLOB(val, res) \ |
156 | 0 | do { \ |
157 | 0 | if (!ParseFilename(val, res)) { \ |
158 | 0 | cmSystemTools::Error(cmStrCat("-E cmake_module_compile_db failed to ", \ |
159 | 0 | "parse ", path, ": invalid blob")); \ |
160 | 0 | return {}; \ |
161 | 0 | } \ |
162 | 0 | } while (0) |
163 | | |
164 | | #define PARSE_FILENAME(val, res, make_full) \ |
165 | 0 | do { \ |
166 | 0 | if (!ParseFilename(val, res)) { \ |
167 | 0 | cmSystemTools::Error(cmStrCat("-E cmake_module_compile_db failed to ", \ |
168 | 0 | "parse ", path, ": invalid filename")); \ |
169 | 0 | return {}; \ |
170 | 0 | } \ |
171 | 0 | \ |
172 | 0 | if (make_full && work_directory && !work_directory->empty() && \ |
173 | 0 | !cmSystemTools::FileIsFullPath(res)) { \ |
174 | 0 | res = cmStrCat(*work_directory, '/', res); \ |
175 | 0 | } \ |
176 | 0 | } while (0) |
177 | | |
178 | | std::unique_ptr<cmBuildDatabase> cmBuildDatabase::Load(std::string const& path) |
179 | 0 | { |
180 | 0 | Json::Value mcdb; |
181 | 0 | { |
182 | 0 | cmsys::ifstream mcdbf(path.c_str(), std::ios::in | std::ios::binary); |
183 | 0 | Json::Reader reader; |
184 | 0 | if (!reader.parse(mcdbf, mcdb, false)) { |
185 | 0 | cmSystemTools::Error( |
186 | 0 | cmStrCat("-E cmake_module_compile_db failed to parse ", path, |
187 | 0 | reader.getFormattedErrorMessages())); |
188 | 0 | return {}; |
189 | 0 | } |
190 | 0 | } |
191 | | |
192 | 0 | Json::Value const& version = mcdb["version"]; |
193 | 0 | if (version.asUInt() > 1) { |
194 | 0 | cmSystemTools::Error( |
195 | 0 | cmStrCat("-E cmake_module_compile_db failed to parse ", path, |
196 | 0 | ": version ", version.asString())); |
197 | 0 | return {}; |
198 | 0 | } |
199 | | |
200 | 0 | auto db = cm::make_unique<cmBuildDatabase>(); |
201 | |
|
202 | 0 | Json::Value const& sets = mcdb["sets"]; |
203 | 0 | if (sets.isArray()) { |
204 | 0 | for (auto const& set : sets) { |
205 | 0 | Set Set_; |
206 | |
|
207 | 0 | Json::Value const& name = set["name"]; |
208 | 0 | if (!name.isString()) { |
209 | 0 | cmSystemTools::Error( |
210 | 0 | cmStrCat("-E cmake_module_compile_db failed to parse ", path, |
211 | 0 | ": name is not a string")); |
212 | 0 | return {}; |
213 | 0 | } |
214 | 0 | Set_.Name = name.asString(); |
215 | |
|
216 | 0 | Json::Value const& family_name = set["family-name"]; |
217 | 0 | if (!family_name.isString()) { |
218 | 0 | cmSystemTools::Error( |
219 | 0 | cmStrCat("-E cmake_module_compile_db failed to parse ", path, |
220 | 0 | ": family-name is not a string")); |
221 | 0 | return {}; |
222 | 0 | } |
223 | 0 | Set_.FamilyName = family_name.asString(); |
224 | |
|
225 | 0 | Json::Value const& visible_sets = set["visible-sets"]; |
226 | 0 | if (!visible_sets.isArray()) { |
227 | 0 | cmSystemTools::Error( |
228 | 0 | cmStrCat("-E cmake_module_compile_db failed to parse ", path, |
229 | 0 | ": visible-sets is not an array")); |
230 | 0 | return {}; |
231 | 0 | } |
232 | 0 | for (auto const& visible_set : visible_sets) { |
233 | 0 | if (!visible_set.isString()) { |
234 | 0 | cmSystemTools::Error( |
235 | 0 | cmStrCat("-E cmake_module_compile_db failed to parse ", path, |
236 | 0 | ": a visible-sets item is not a string")); |
237 | 0 | return {}; |
238 | 0 | } |
239 | | |
240 | 0 | Set_.VisibleSets.emplace_back(visible_set.asString()); |
241 | 0 | } |
242 | | |
243 | 0 | Json::Value const& translation_units = set["translation-units"]; |
244 | 0 | if (!translation_units.isArray()) { |
245 | 0 | cmSystemTools::Error( |
246 | 0 | cmStrCat("-E cmake_module_compile_db failed to parse ", path, |
247 | 0 | ": translation-units is not an array")); |
248 | 0 | return {}; |
249 | 0 | } |
250 | 0 | for (auto const& translation_unit : translation_units) { |
251 | 0 | if (!translation_unit.isObject()) { |
252 | 0 | cmSystemTools::Error( |
253 | 0 | cmStrCat("-E cmake_module_compile_db failed to parse ", path, |
254 | 0 | ": a translation-units item is not an object")); |
255 | 0 | return {}; |
256 | 0 | } |
257 | | |
258 | 0 | TranslationUnit TranslationUnit_; |
259 | |
|
260 | 0 | cm::optional<std::string> work_directory; |
261 | 0 | Json::Value const& workdir = translation_unit["work-directory"]; |
262 | 0 | if (workdir.isString()) { |
263 | 0 | PARSE_BLOB(workdir, TranslationUnit_.WorkDirectory); |
264 | 0 | work_directory = TranslationUnit_.WorkDirectory; |
265 | 0 | } else if (!workdir.isNull()) { |
266 | 0 | cmSystemTools::Error( |
267 | 0 | cmStrCat("-E cmake_module_compile_db failed to parse ", path, |
268 | 0 | ": work-directory is not a string")); |
269 | 0 | return {}; |
270 | 0 | } |
271 | | |
272 | 0 | Json::Value const& source = translation_unit["source"]; |
273 | 0 | PARSE_FILENAME(source, TranslationUnit_.Source, true); |
274 | | |
275 | 0 | if (translation_unit.isMember("object")) { |
276 | 0 | Json::Value const& object = translation_unit["object"]; |
277 | 0 | if (!object.isNull()) { |
278 | 0 | TranslationUnit_.Object = ""; |
279 | 0 | PARSE_FILENAME(object, *TranslationUnit_.Object, false); |
280 | 0 | } |
281 | 0 | } |
282 | | |
283 | 0 | if (translation_unit.isMember("private")) { |
284 | 0 | Json::Value const& priv = translation_unit["private"]; |
285 | 0 | if (!priv.isBool()) { |
286 | 0 | cmSystemTools::Error( |
287 | 0 | cmStrCat("-E cmake_module_compile_db failed to parse ", path, |
288 | 0 | ": private is not a boolean")); |
289 | 0 | return {}; |
290 | 0 | } |
291 | 0 | TranslationUnit_.Private = priv.asBool(); |
292 | 0 | } |
293 | | |
294 | 0 | if (translation_unit.isMember("requires")) { |
295 | 0 | Json::Value const& reqs = translation_unit["requires"]; |
296 | 0 | if (!reqs.isArray()) { |
297 | 0 | cmSystemTools::Error( |
298 | 0 | cmStrCat("-E cmake_module_compile_db failed to parse ", path, |
299 | 0 | ": requires is not an array")); |
300 | 0 | return {}; |
301 | 0 | } |
302 | | |
303 | 0 | for (auto const& require : reqs) { |
304 | 0 | if (!require.isString()) { |
305 | 0 | cmSystemTools::Error( |
306 | 0 | cmStrCat("-E cmake_module_compile_db failed to parse ", path, |
307 | 0 | ": a requires item is not a string")); |
308 | 0 | return {}; |
309 | 0 | } |
310 | | |
311 | 0 | TranslationUnit_.Requires.emplace_back(require.asString()); |
312 | 0 | } |
313 | 0 | } |
314 | | |
315 | 0 | if (translation_unit.isMember("provides")) { |
316 | 0 | Json::Value const& provides = translation_unit["provides"]; |
317 | 0 | if (!provides.isObject()) { |
318 | 0 | cmSystemTools::Error( |
319 | 0 | cmStrCat("-E cmake_module_compile_db failed to parse ", path, |
320 | 0 | ": provides is not an object")); |
321 | 0 | return {}; |
322 | 0 | } |
323 | | |
324 | 0 | for (auto i = provides.begin(); i != provides.end(); ++i) { |
325 | 0 | if (!i->isString()) { |
326 | 0 | cmSystemTools::Error( |
327 | 0 | cmStrCat("-E cmake_module_compile_db failed to parse ", path, |
328 | 0 | ": a provides value is not a string")); |
329 | 0 | return {}; |
330 | 0 | } |
331 | | |
332 | 0 | TranslationUnit_.Provides[i.key().asString()] = i->asString(); |
333 | 0 | } |
334 | 0 | } |
335 | | |
336 | 0 | if (translation_unit.isMember("baseline-arguments")) { |
337 | 0 | Json::Value const& baseline_arguments = |
338 | 0 | translation_unit["baseline-arguments"]; |
339 | 0 | if (!baseline_arguments.isArray()) { |
340 | 0 | cmSystemTools::Error( |
341 | 0 | cmStrCat("-E cmake_module_compile_db failed to parse ", path, |
342 | 0 | ": baseline_arguments is not an array")); |
343 | 0 | return {}; |
344 | 0 | } |
345 | | |
346 | 0 | for (auto const& baseline_argument : baseline_arguments) { |
347 | 0 | if (baseline_argument.isString()) { |
348 | 0 | TranslationUnit_.BaselineArguments.emplace_back( |
349 | 0 | baseline_argument.asString()); |
350 | 0 | } else { |
351 | 0 | cmSystemTools::Error( |
352 | 0 | cmStrCat("-E cmake_module_compile_db failed to parse ", path, |
353 | 0 | ": a baseline argument is not a string")); |
354 | 0 | return {}; |
355 | 0 | } |
356 | 0 | } |
357 | 0 | } |
358 | | |
359 | 0 | if (translation_unit.isMember("local-arguments")) { |
360 | 0 | Json::Value const& local_arguments = |
361 | 0 | translation_unit["local-arguments"]; |
362 | 0 | if (!local_arguments.isArray()) { |
363 | 0 | cmSystemTools::Error( |
364 | 0 | cmStrCat("-E cmake_module_compile_db failed to parse ", path, |
365 | 0 | ": local_arguments is not an array")); |
366 | 0 | return {}; |
367 | 0 | } |
368 | | |
369 | 0 | for (auto const& local_argument : local_arguments) { |
370 | 0 | if (local_argument.isString()) { |
371 | 0 | TranslationUnit_.LocalArguments.emplace_back( |
372 | 0 | local_argument.asString()); |
373 | 0 | } else { |
374 | 0 | cmSystemTools::Error( |
375 | 0 | cmStrCat("-E cmake_module_compile_db failed to parse ", path, |
376 | 0 | ": a local argument is not a string")); |
377 | 0 | return {}; |
378 | 0 | } |
379 | 0 | } |
380 | 0 | } |
381 | | |
382 | 0 | if (translation_unit.isMember("arguments")) { |
383 | 0 | Json::Value const& arguments = translation_unit["arguments"]; |
384 | 0 | if (!arguments.isArray()) { |
385 | 0 | cmSystemTools::Error( |
386 | 0 | cmStrCat("-E cmake_module_compile_db failed to parse ", path, |
387 | 0 | ": arguments is not an array")); |
388 | 0 | return {}; |
389 | 0 | } |
390 | | |
391 | 0 | for (auto const& argument : arguments) { |
392 | 0 | if (argument.isString()) { |
393 | 0 | TranslationUnit_.Arguments.emplace_back(argument.asString()); |
394 | 0 | } else { |
395 | 0 | cmSystemTools::Error( |
396 | 0 | cmStrCat("-E cmake_module_compile_db failed to parse ", path, |
397 | 0 | ": an argument is not a string")); |
398 | 0 | return {}; |
399 | 0 | } |
400 | 0 | } |
401 | 0 | } |
402 | | |
403 | 0 | Set_.TranslationUnits.emplace_back(std::move(TranslationUnit_)); |
404 | 0 | } |
405 | | |
406 | 0 | db->Sets.emplace_back(std::move(Set_)); |
407 | 0 | } |
408 | 0 | } |
409 | | |
410 | 0 | return db; |
411 | 0 | } |
412 | | |
413 | | cmBuildDatabase cmBuildDatabase::Merge( |
414 | | std::vector<cmBuildDatabase> const& components) |
415 | 0 | { |
416 | 0 | cmBuildDatabase db; |
417 | |
|
418 | 0 | for (auto const& component : components) { |
419 | 0 | db.Sets.insert(db.Sets.end(), component.Sets.begin(), |
420 | 0 | component.Sets.end()); |
421 | 0 | } |
422 | |
|
423 | 0 | return db; |
424 | 0 | } |
425 | | |
426 | | cmBuildDatabase cmBuildDatabase::ForTarget(cmGeneratorTarget* gt, |
427 | | std::string const& config) |
428 | 0 | { |
429 | 0 | cmBuildDatabase db; |
430 | |
|
431 | 0 | Set set; |
432 | 0 | set.Name = cmStrCat(gt->GetName(), '@', config); |
433 | 0 | set.FamilyName = gt->GetFamilyName(); |
434 | 0 | if (auto* cli = gt->GetLinkInformation(config)) { |
435 | 0 | std::set<cmGeneratorTarget const*> emitted; |
436 | 0 | std::vector<cmGeneratorTarget const*> targets; |
437 | 0 | for (auto const& item : cli->GetItems()) { |
438 | 0 | auto const* linkee = item.Target; |
439 | 0 | if (linkee && linkee->HaveCxx20ModuleSources() && |
440 | 0 | !linkee->IsImported() && emitted.insert(linkee).second) { |
441 | 0 | set.VisibleSets.push_back(cmStrCat(linkee->GetName(), '@', config)); |
442 | 0 | } |
443 | 0 | } |
444 | 0 | } |
445 | |
|
446 | 0 | for (auto const& sfbt : gt->GetSourceFiles(config)) { |
447 | 0 | auto const* sf = sfbt.Value; |
448 | |
|
449 | 0 | bool isCXXModule = false; |
450 | 0 | bool isPrivate = true; |
451 | 0 | if (sf->GetLanguage() == "CXX"_s) { |
452 | 0 | auto const* fs = gt->GetFileSetForSource(config, sf); |
453 | 0 | if (fs && fs->GetType() == cm::FileSetMetadata::CXX_MODULES) { |
454 | 0 | isCXXModule = true; |
455 | 0 | isPrivate = !fs->IsForInterface(); |
456 | 0 | } |
457 | 0 | } |
458 | |
|
459 | 0 | TranslationUnit tu; |
460 | | |
461 | | // FIXME: Makefiles will want this to be the current working directory. |
462 | 0 | tu.WorkDirectory = gt->GetLocalGenerator()->GetBinaryDirectory(); |
463 | 0 | tu.Source = sf->GetFullPath(); |
464 | 0 | if (!gt->IsSynthetic()) { |
465 | 0 | auto* gg = gt->GetGlobalGenerator(); |
466 | 0 | std::string const objectDir = gg->ConvertToOutputPath( |
467 | 0 | cmStrCat(gt->GetSupportDirectory(), gg->GetConfigDirectory(config))); |
468 | 0 | std::string const objectFileName = gt->GetObjectName(sf); |
469 | 0 | tu.Object = cmStrCat(objectDir, '/', objectFileName); |
470 | 0 | } |
471 | 0 | if (isCXXModule) { |
472 | 0 | tu.Provides[PlaceholderName] = PlaceholderName; |
473 | 0 | } |
474 | |
|
475 | 0 | cmGeneratorTarget::ClassifiedFlags classifiedFlags = |
476 | 0 | gt->GetClassifiedFlagsForSource(sf, config); |
477 | 0 | for (auto const& classifiedFlag : classifiedFlags) { |
478 | 0 | if (classifiedFlag.Classification == |
479 | 0 | cmGeneratorTarget::FlagClassification::BaselineFlag) { |
480 | 0 | tu.BaselineArguments.push_back(classifiedFlag.Flag); |
481 | 0 | tu.LocalArguments.push_back(classifiedFlag.Flag); |
482 | 0 | } else if (classifiedFlag.Classification == |
483 | 0 | cmGeneratorTarget::FlagClassification::PrivateFlag) { |
484 | 0 | tu.LocalArguments.push_back(classifiedFlag.Flag); |
485 | 0 | } |
486 | 0 | tu.Arguments.push_back(classifiedFlag.Flag); |
487 | 0 | } |
488 | 0 | tu.Private = isPrivate; |
489 | |
|
490 | 0 | set.TranslationUnits.emplace_back(std::move(tu)); |
491 | 0 | } |
492 | |
|
493 | 0 | db.Sets.emplace_back(std::move(set)); |
494 | |
|
495 | 0 | return db; |
496 | 0 | } |
497 | | |
498 | | int cmcmd_cmake_module_compile_db( |
499 | | std::vector<std::string>::const_iterator argBeg, |
500 | | std::vector<std::string>::const_iterator argEnd) |
501 | 0 | { |
502 | 0 | std::string const* command = nullptr; |
503 | 0 | std::string const* output = nullptr; |
504 | 0 | std::vector<std::string const*> inputs; |
505 | |
|
506 | 0 | bool next_is_output = false; |
507 | 0 | for (auto i = argBeg; i != argEnd; ++i) { |
508 | | // The first argument is always the command. |
509 | 0 | if (!command) { |
510 | 0 | command = &(*i); |
511 | 0 | continue; |
512 | 0 | } |
513 | | |
514 | 0 | if (*i == "-o"_s) { |
515 | 0 | next_is_output = true; |
516 | 0 | continue; |
517 | 0 | } |
518 | 0 | if (next_is_output) { |
519 | 0 | if (output) { |
520 | 0 | cmSystemTools::Error( |
521 | 0 | "-E cmake_module_compile_db only supports one output file"); |
522 | 0 | return EXIT_FAILURE; |
523 | 0 | } |
524 | | |
525 | 0 | output = &(*i); |
526 | 0 | next_is_output = false; |
527 | 0 | continue; |
528 | 0 | } |
529 | | |
530 | 0 | inputs.emplace_back(&(*i)); |
531 | 0 | } |
532 | | |
533 | 0 | if (!command) { |
534 | 0 | cmSystemTools::Error("-E cmake_module_compile_db requires a subcommand"); |
535 | 0 | return EXIT_FAILURE; |
536 | 0 | } |
537 | | |
538 | 0 | int ret = EXIT_SUCCESS; |
539 | |
|
540 | 0 | if (*command == "verify"_s) { |
541 | 0 | if (output) { |
542 | 0 | cmSystemTools::Error( |
543 | 0 | "-E cmake_module_compile_db verify does not support an output"); |
544 | 0 | return EXIT_FAILURE; |
545 | 0 | } |
546 | | |
547 | 0 | for (auto const* i : inputs) { |
548 | 0 | auto db = cmBuildDatabase::Load(*i); |
549 | 0 | if (!db) { |
550 | 0 | cmSystemTools::Error(cmStrCat("failed to verify ", *i)); |
551 | 0 | ret = EXIT_FAILURE; |
552 | 0 | } |
553 | 0 | } |
554 | 0 | } else if (*command == "merge"_s) { |
555 | 0 | if (!output) { |
556 | 0 | cmSystemTools::Error( |
557 | 0 | "-E cmake_module_compile_db verify requires an output"); |
558 | 0 | return EXIT_FAILURE; |
559 | 0 | } |
560 | | |
561 | 0 | std::vector<cmBuildDatabase> dbs; |
562 | |
|
563 | 0 | for (auto const* i : inputs) { |
564 | 0 | auto db = cmBuildDatabase::Load(*i); |
565 | 0 | if (!db) { |
566 | 0 | cmSystemTools::Error(cmStrCat("failed to read ", *i)); |
567 | 0 | return EXIT_FAILURE; |
568 | 0 | } |
569 | | |
570 | 0 | dbs.emplace_back(std::move(*db)); |
571 | 0 | } |
572 | | |
573 | 0 | auto db = cmBuildDatabase::Merge(dbs); |
574 | 0 | db.Write(*output); |
575 | 0 | } else { |
576 | 0 | cmSystemTools::Error( |
577 | 0 | cmStrCat("-E cmake_module_compile_db unknown subcommand ", *command)); |
578 | 0 | return EXIT_FAILURE; |
579 | 0 | } |
580 | | |
581 | 0 | return ret; |
582 | 0 | } |