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