1from check_index import *
2
3import json
4import sys
5import os
6
7def read_codemodel_json_data(filename):
8    abs_filename = os.path.join(os.path.dirname(os.path.realpath(__file__)), "codemodel-v2-data", filename)
9    with open(abs_filename, "r") as f:
10        return json.load(f)
11
12def check_objects(o, g):
13    assert is_list(o)
14    assert len(o) == 1
15    check_index_object(o[0], "codemodel", 2, 3, check_object_codemodel(g))
16
17def check_backtrace(t, b, backtrace):
18    btg = t["backtraceGraph"]
19    for expected in backtrace:
20        assert is_int(b)
21        node = btg["nodes"][b]
22        expected_keys = ["file"]
23        assert matches(btg["files"][node["file"]], expected["file"])
24
25        if expected["line"] is not None:
26            expected_keys.append("line")
27            assert is_int(node["line"], expected["line"])
28
29        if expected["command"] is not None:
30            expected_keys.append("command")
31            assert is_int(node["command"])
32            assert is_string(btg["commands"][node["command"]], expected["command"])
33
34        if expected["hasParent"]:
35            expected_keys.append("parent")
36            assert is_int(node["parent"])
37            b = node["parent"]
38        else:
39            b = None
40
41        assert sorted(node.keys()) == sorted(expected_keys)
42
43    assert b is None
44
45def check_backtraces(t, actual, expected):
46    assert is_list(actual)
47    assert is_list(expected)
48    assert len(actual) == len(expected)
49
50    i = 0
51    while i < len(actual):
52        check_backtrace(t, actual[i], expected[i])
53        i += 1
54
55def check_directory(c):
56    def _check(actual, expected):
57        assert is_dict(actual)
58        expected_keys = ["build", "jsonFile", "source", "projectIndex"]
59        assert matches(actual["build"], expected["build"])
60
61        assert is_int(actual["projectIndex"])
62        assert is_string(c["projects"][actual["projectIndex"]]["name"], expected["projectName"])
63
64        if expected["parentSource"] is not None:
65            expected_keys.append("parentIndex")
66            assert is_int(actual["parentIndex"])
67            assert matches(c["directories"][actual["parentIndex"]]["source"], expected["parentSource"])
68
69        if expected["childSources"] is not None:
70            expected_keys.append("childIndexes")
71            check_list_match(lambda a, e: matches(c["directories"][a]["source"], e),
72                             actual["childIndexes"], expected["childSources"],
73                             missing_exception=lambda e: "Child source: %s" % e,
74                             extra_exception=lambda a: "Child source: %s" % a["source"])
75
76        if expected["targetIds"] is not None:
77            expected_keys.append("targetIndexes")
78            check_list_match(lambda a, e: matches(c["targets"][a]["id"], e),
79                             actual["targetIndexes"], expected["targetIds"],
80                             missing_exception=lambda e: "Target ID: %s" % e,
81                             extra_exception=lambda a: "Target ID: %s" % c["targets"][a]["id"])
82
83        if expected["minimumCMakeVersion"] is not None:
84            expected_keys.append("minimumCMakeVersion")
85            assert is_dict(actual["minimumCMakeVersion"])
86            assert sorted(actual["minimumCMakeVersion"].keys()) == ["string"]
87            assert is_string(actual["minimumCMakeVersion"]["string"], expected["minimumCMakeVersion"])
88
89        if expected["hasInstallRule"] is not None:
90            expected_keys.append("hasInstallRule")
91            assert is_bool(actual["hasInstallRule"], expected["hasInstallRule"])
92
93        assert sorted(actual.keys()) == sorted(expected_keys)
94
95        assert is_string(actual["jsonFile"])
96        filepath = os.path.join(reply_dir, actual["jsonFile"])
97        with open(filepath) as f:
98            d = json.load(f)
99
100        assert is_dict(d)
101        assert sorted(d.keys()) == ["backtraceGraph", "installers", "paths"]
102
103        assert is_string(d["paths"]["source"], actual["source"])
104        assert is_string(d["paths"]["build"], actual["build"])
105
106        check_backtrace_graph(d["backtraceGraph"])
107
108        assert is_list(d["installers"])
109        assert len(d["installers"]) == len(expected["installers"])
110        for a, e in zip(d["installers"], expected["installers"]):
111            assert is_dict(a)
112            expected_keys = ["component", "type"]
113
114            assert is_string(a["component"], e["component"])
115            assert is_string(a["type"], e["type"])
116
117            if e["destination"] is not None:
118                expected_keys.append("destination")
119                assert is_string(a["destination"], e["destination"])
120
121            if e["paths"] is not None:
122                expected_keys.append("paths")
123                assert is_list(a["paths"])
124                assert len(a["paths"]) == len(e["paths"])
125
126                for ap, ep in zip(a["paths"], e["paths"]):
127                    if is_string(ep):
128                        assert matches(ap, ep)
129                    else:
130                        assert is_dict(ap)
131                        assert sorted(ap.keys()) == ["from", "to"]
132                        assert matches(ap["from"], ep["from"])
133                        assert matches(ap["to"], ep["to"])
134
135            if e["isExcludeFromAll"] is not None:
136                expected_keys.append("isExcludeFromAll")
137                assert is_bool(a["isExcludeFromAll"], e["isExcludeFromAll"])
138
139            if e["isForAllComponents"] is not None:
140                expected_keys.append("isForAllComponents")
141                assert is_bool(a["isForAllComponents"], e["isForAllComponents"])
142
143            if e["isOptional"] is not None:
144                expected_keys.append("isOptional")
145                assert is_bool(a["isOptional"], e["isOptional"])
146
147            if e["targetId"] is not None:
148                expected_keys.append("targetId")
149                assert matches(a["targetId"], e["targetId"])
150
151            if e["targetIndex"] is not None:
152                expected_keys.append("targetIndex")
153                assert is_int(a["targetIndex"])
154                assert c["targets"][a["targetIndex"]]["name"] == e["targetIndex"]
155
156            if e["targetIsImportLibrary"] is not None:
157                expected_keys.append("targetIsImportLibrary")
158                assert is_bool(a["targetIsImportLibrary"], e["targetIsImportLibrary"])
159
160            if e["targetInstallNamelink"] is not None:
161                expected_keys.append("targetInstallNamelink")
162                assert is_string(a["targetInstallNamelink"], e["targetInstallNamelink"])
163
164            if e["exportName"] is not None:
165                expected_keys.append("exportName")
166                assert is_string(a["exportName"], e["exportName"])
167
168            if e["exportTargets"] is not None:
169                expected_keys.append("exportTargets")
170                assert is_list(a["exportTargets"])
171                assert len(a["exportTargets"]) == len(e["exportTargets"])
172                for at, et in zip(a["exportTargets"], e["exportTargets"]):
173                    assert is_dict(at)
174                    assert sorted(at.keys()) == ["id", "index"]
175                    assert matches(at["id"], et["id"])
176                    assert is_int(at["index"])
177                    assert c["targets"][at["index"]]["name"] == et["index"]
178
179            if e["scriptFile"] is not None:
180                expected_keys.append("scriptFile")
181                assert is_string(a["scriptFile"], e["scriptFile"])
182
183            if e.get("runtimeDependencySetName", None) is not None:
184                expected_keys.append("runtimeDependencySetName")
185                assert is_string(a["runtimeDependencySetName"], e["runtimeDependencySetName"])
186
187            if e.get("runtimeDependencySetType", None) is not None:
188                expected_keys.append("runtimeDependencySetType")
189                assert is_string(a["runtimeDependencySetType"], e["runtimeDependencySetType"])
190
191            if e["backtrace"] is not None:
192                expected_keys.append("backtrace")
193                check_backtrace(d, a["backtrace"], e["backtrace"])
194
195            assert sorted(a.keys()) == sorted(expected_keys)
196
197    return _check
198
199def check_backtrace_graph(btg):
200    assert is_dict(btg)
201    assert sorted(btg.keys()) == ["commands", "files", "nodes"]
202    assert is_list(btg["commands"])
203
204    for c in btg["commands"]:
205        assert is_string(c)
206
207    for f in btg["files"]:
208        assert is_string(f)
209
210    for n in btg["nodes"]:
211        expected_keys = ["file"]
212        assert is_dict(n)
213        assert is_int(n["file"])
214        assert 0 <= n["file"] < len(btg["files"])
215
216        if "line" in n:
217            expected_keys.append("line")
218            assert is_int(n["line"])
219
220        if "command" in n:
221            expected_keys.append("command")
222            assert is_int(n["command"])
223            assert 0 <= n["command"] < len(btg["commands"])
224
225        if "parent" in n:
226            expected_keys.append("parent")
227            assert is_int(n["parent"])
228            assert 0 <= n["parent"] < len(btg["nodes"])
229
230        assert sorted(n.keys()) == sorted(expected_keys)
231
232def check_target(c):
233    def _check(actual, expected):
234        assert is_dict(actual)
235        assert sorted(actual.keys()) == ["directoryIndex", "id", "jsonFile", "name", "projectIndex"]
236        assert is_int(actual["directoryIndex"])
237        assert matches(c["directories"][actual["directoryIndex"]]["source"], expected["directorySource"])
238        assert is_string(actual["name"], expected["name"])
239        assert is_string(actual["jsonFile"])
240        assert is_int(actual["projectIndex"])
241        assert is_string(c["projects"][actual["projectIndex"]]["name"], expected["projectName"])
242
243        filepath = os.path.join(reply_dir, actual["jsonFile"])
244        with open(filepath) as f:
245            obj = json.load(f)
246
247        expected_keys = ["name", "id", "type", "backtraceGraph", "paths", "sources"]
248        assert is_dict(obj)
249        assert is_string(obj["name"], expected["name"])
250        assert matches(obj["id"], expected["id"])
251        assert is_string(obj["type"], expected["type"])
252        check_backtrace_graph(obj["backtraceGraph"])
253
254        assert is_dict(obj["paths"])
255        assert sorted(obj["paths"].keys()) == ["build", "source"]
256        assert matches(obj["paths"]["build"], expected["build"])
257        assert matches(obj["paths"]["source"], expected["source"])
258
259        def check_source(actual, expected):
260            assert is_dict(actual)
261            expected_keys = ["path"]
262
263            if expected["compileGroupLanguage"] is not None:
264                expected_keys.append("compileGroupIndex")
265                assert is_string(obj["compileGroups"][actual["compileGroupIndex"]]["language"], expected["compileGroupLanguage"])
266
267            if expected["sourceGroupName"] is not None:
268                expected_keys.append("sourceGroupIndex")
269                assert is_string(obj["sourceGroups"][actual["sourceGroupIndex"]]["name"], expected["sourceGroupName"])
270
271            if expected["isGenerated"] is not None:
272                expected_keys.append("isGenerated")
273                assert is_bool(actual["isGenerated"], expected["isGenerated"])
274
275            if expected["backtrace"] is not None:
276                expected_keys.append("backtrace")
277                check_backtrace(obj, actual["backtrace"], expected["backtrace"])
278
279            assert sorted(actual.keys()) == sorted(expected_keys)
280
281        check_list_match(lambda a, e: matches(a["path"], e["path"]), obj["sources"],
282                         expected["sources"], check=check_source,
283                         check_exception=lambda a, e: "Source file: %s" % a["path"],
284                         missing_exception=lambda e: "Source file: %s" % e["path"],
285                         extra_exception=lambda a: "Source file: %s" % a["path"])
286
287        if expected["backtrace"] is not None:
288            expected_keys.append("backtrace")
289            check_backtrace(obj, obj["backtrace"], expected["backtrace"])
290
291        if expected["folder"] is not None:
292            expected_keys.append("folder")
293            assert is_dict(obj["folder"])
294            assert sorted(obj["folder"].keys()) == ["name"]
295            assert is_string(obj["folder"]["name"], expected["folder"])
296
297        if expected["nameOnDisk"] is not None:
298            expected_keys.append("nameOnDisk")
299            assert matches(obj["nameOnDisk"], expected["nameOnDisk"])
300
301        if expected["artifacts"] is not None:
302            expected_keys.append("artifacts")
303
304            def check_artifact(actual, expected):
305                assert is_dict(actual)
306                assert sorted(actual.keys()) == ["path"]
307
308            check_list_match(lambda a, e: matches(a["path"], e["path"]),
309                             obj["artifacts"], expected["artifacts"],
310                             check=check_artifact,
311                             check_exception=lambda a, e: "Artifact: %s" % a["path"],
312                             missing_exception=lambda e: "Artifact: %s" % e["path"],
313                             extra_exception=lambda a: "Artifact: %s" % a["path"])
314
315        if expected["isGeneratorProvided"] is not None:
316            expected_keys.append("isGeneratorProvided")
317            assert is_bool(obj["isGeneratorProvided"], expected["isGeneratorProvided"])
318
319        if expected["install"] is not None:
320            expected_keys.append("install")
321            assert is_dict(obj["install"])
322            assert sorted(obj["install"].keys()) == ["destinations", "prefix"]
323
324            assert is_dict(obj["install"]["prefix"])
325            assert sorted(obj["install"]["prefix"].keys()) == ["path"]
326            assert matches(obj["install"]["prefix"]["path"], expected["install"]["prefix"])
327
328            def check_install_destination(actual, expected):
329                assert is_dict(actual)
330                expected_keys = ["path"]
331
332                if expected["backtrace"] is not None:
333                    expected_keys.append("backtrace")
334                    check_backtrace(obj, actual["backtrace"], expected["backtrace"])
335
336                assert sorted(actual.keys()) == sorted(expected_keys)
337
338            check_list_match(lambda a, e: matches(a["path"], e["path"]),
339                             obj["install"]["destinations"], expected["install"]["destinations"],
340                             check=check_install_destination,
341                             check_exception=lambda a, e: "Install path: %s" % a["path"],
342                             missing_exception=lambda e: "Install path: %s" % e["path"],
343                             extra_exception=lambda a: "Install path: %s" % a["path"])
344
345        if expected["link"] is not None:
346            expected_keys.append("link")
347            assert is_dict(obj["link"])
348            link_keys = ["language"]
349
350            assert is_string(obj["link"]["language"], expected["link"]["language"])
351
352            if "commandFragments" in obj["link"]:
353                link_keys.append("commandFragments")
354                assert is_list(obj["link"]["commandFragments"])
355                for f in obj["link"]["commandFragments"]:
356                    assert is_dict(f)
357                    assert sorted(f.keys()) == ["fragment", "role"] or sorted(f.keys()) == ["backtrace", "fragment", "role"]
358                    assert is_string(f["fragment"])
359                    assert is_string(f["role"])
360                    assert f["role"] in ("flags", "libraries", "libraryPath", "frameworkPath")
361
362            if expected["link"]["commandFragments"] is not None:
363                def check_link_command_fragments(actual, expected):
364                    assert is_dict(actual)
365                    expected_keys = ["fragment", "role"]
366
367                    if expected["backtrace"] is not None:
368                        expected_keys.append("backtrace")
369                        assert matches(actual["fragment"], expected["fragment"])
370                        assert actual["role"] == expected["role"]
371                        check_backtrace(obj, actual["backtrace"], expected["backtrace"])
372
373                    assert sorted(actual.keys()) == sorted(expected_keys)
374
375                check_list_match(lambda a, e: matches(a["fragment"], e["fragment"]),
376                                 obj["link"]["commandFragments"], expected["link"]["commandFragments"],
377                                 check=check_link_command_fragments,
378                                 check_exception=lambda a, e: "Link fragment: %s" % a["fragment"],
379                                 missing_exception=lambda e: "Link fragment: %s" % e["fragment"],
380                                 extra_exception=lambda a: "Link fragment: %s" % a["fragment"],
381                                 allow_extra=True)
382
383            if expected["link"]["lto"] is not None:
384                link_keys.append("lto")
385                assert is_bool(obj["link"]["lto"], expected["link"]["lto"])
386
387            # FIXME: Properly test sysroot
388            if "sysroot" in obj["link"]:
389                link_keys.append("sysroot")
390                assert is_string(obj["link"]["sysroot"])
391
392            assert sorted(obj["link"].keys()) == sorted(link_keys)
393
394        if expected["archive"] is not None:
395            expected_keys.append("archive")
396            assert is_dict(obj["archive"])
397            archive_keys = []
398
399            # FIXME: Properly test commandFragments
400            if "commandFragments" in obj["archive"]:
401                archive_keys.append("commandFragments")
402                assert is_list(obj["archive"]["commandFragments"])
403                for f in obj["archive"]["commandFragments"]:
404                    assert is_dict(f)
405                    assert sorted(f.keys()) == ["fragment", "role"]
406                    assert is_string(f["fragment"])
407                    assert is_string(f["role"])
408                    assert f["role"] in ("flags")
409
410            if expected["archive"]["lto"] is not None:
411                archive_keys.append("lto")
412                assert is_bool(obj["archive"]["lto"], expected["archive"]["lto"])
413
414            assert sorted(obj["archive"].keys()) == sorted(archive_keys)
415
416        if expected["dependencies"] is not None:
417            expected_keys.append("dependencies")
418
419            def check_dependency(actual, expected):
420                assert is_dict(actual)
421                expected_keys = ["id"]
422
423                if expected["backtrace"] is not None:
424                    expected_keys.append("backtrace")
425                    check_backtrace(obj, actual["backtrace"], expected["backtrace"])
426
427                assert sorted(actual.keys()) == sorted(expected_keys)
428
429            check_list_match(lambda a, e: matches(a["id"], e["id"]),
430                             obj["dependencies"], expected["dependencies"],
431                             check=check_dependency,
432                             check_exception=lambda a, e: "Dependency ID: %s" % a["id"],
433                             missing_exception=lambda e: "Dependency ID: %s" % e["id"],
434                             extra_exception=lambda a: "Dependency ID: %s" % a["id"])
435
436        if expected["sourceGroups"] is not None:
437            expected_keys.append("sourceGroups")
438
439            def check_source_group(actual, expected):
440                assert is_dict(actual)
441                assert sorted(actual.keys()) == ["name", "sourceIndexes"]
442
443                check_list_match(lambda a, e: matches(obj["sources"][a]["path"], e),
444                                 actual["sourceIndexes"], expected["sourcePaths"],
445                                 missing_exception=lambda e: "Source path: %s" % e,
446                                 extra_exception=lambda a: "Source path: %s" % obj["sources"][a]["path"])
447
448            check_list_match(lambda a, e: is_string(a["name"], e["name"]),
449                             obj["sourceGroups"], expected["sourceGroups"],
450                             check=check_source_group,
451                             check_exception=lambda a, e: "Source group: %s" % a["name"],
452                             missing_exception=lambda e: "Source group: %s" % e["name"],
453                             extra_exception=lambda a: "Source group: %s" % a["name"])
454
455        if expected["compileGroups"] is not None:
456            expected_keys.append("compileGroups")
457
458            def check_compile_group(actual, expected):
459                assert is_dict(actual)
460                expected_keys = ["sourceIndexes", "language"]
461
462                check_list_match(lambda a, e: matches(obj["sources"][a]["path"], e),
463                                 actual["sourceIndexes"], expected["sourcePaths"],
464                                 missing_exception=lambda e: "Source path: %s" % e,
465                                 extra_exception=lambda a: "Source path: %s" % obj["sources"][a]["path"])
466
467                if "compileCommandFragments" in actual:
468                    expected_keys.append("compileCommandFragments")
469                    assert is_list(actual["compileCommandFragments"])
470                    for f in actual["compileCommandFragments"]:
471                        assert is_dict(f)
472                        assert is_string(f["fragment"])
473
474                if expected["compileCommandFragments"] is not None:
475                    def check_compile_command_fragments(actual, expected):
476                        assert is_dict(actual)
477                        expected_keys = ["fragment"]
478
479                        if expected["backtrace"] is not None:
480                            expected_keys.append("backtrace")
481                            assert actual["fragment"] == expected["fragment"]
482                            check_backtrace(obj, actual["backtrace"], expected["backtrace"])
483
484                        assert sorted(actual.keys()) == sorted(expected_keys)
485
486                    check_list_match(lambda a, e: is_string(a["fragment"], e["fragment"]),
487                                     actual["compileCommandFragments"], expected["compileCommandFragments"],
488                                     check=check_compile_command_fragments,
489                                     check_exception=lambda a, e: "Compile fragment: %s" % a["fragment"],
490                                     missing_exception=lambda e: "Compile fragment: %s" % e["fragment"],
491                                     extra_exception=lambda a: "Compile fragment: %s" % a["fragment"],
492                                     allow_extra=True)
493
494                if expected["includes"] is not None:
495                    expected_keys.append("includes")
496
497                    def check_include(actual, expected):
498                        assert is_dict(actual)
499                        expected_keys = ["path"]
500
501                        if expected["isSystem"] is not None:
502                            expected_keys.append("isSystem")
503                            assert is_bool(actual["isSystem"], expected["isSystem"])
504
505                        if expected["backtrace"] is not None:
506                            expected_keys.append("backtrace")
507                            check_backtrace(obj, actual["backtrace"], expected["backtrace"])
508
509                        assert sorted(actual.keys()) == sorted(expected_keys)
510
511                    check_list_match(lambda a, e: matches(a["path"], e["path"]),
512                                     actual["includes"], expected["includes"],
513                                     check=check_include,
514                                     check_exception=lambda a, e: "Include path: %s" % a["path"],
515                                     missing_exception=lambda e: "Include path: %s" % e["path"],
516                                     extra_exception=lambda a: "Include path: %s" % a["path"])
517
518                if "precompileHeaders" in expected:
519                    expected_keys.append("precompileHeaders")
520
521                    def check_precompile_header(actual, expected):
522                        assert is_dict(actual)
523                        expected_keys = ["backtrace", "header"]
524                        check_backtrace(obj, actual["backtrace"], expected["backtrace"])
525
526                        assert sorted(actual.keys()) == sorted(expected_keys)
527
528                    check_list_match(lambda a, e: matches(a["header"], e["header"]),
529                                     actual["precompileHeaders"], expected["precompileHeaders"],
530                                     check=check_precompile_header,
531                                     check_exception=lambda a, e: "Precompile header: %s" % a["header"],
532                                     missing_exception=lambda e: "Precompile header: %s" % e["header"],
533                                     extra_exception=lambda a: "Precompile header: %s" % a["header"])
534
535                if "languageStandard" in expected:
536                    expected_keys.append("languageStandard")
537
538                    def check_language_standard(actual, expected):
539                        assert is_dict(actual)
540                        expected_keys = ["backtraces", "standard"]
541                        assert actual["standard"] == expected["standard"]
542                        check_backtraces(obj, actual["backtraces"], expected["backtraces"])
543
544                        assert sorted(actual.keys()) == sorted(expected_keys)
545
546                    check_language_standard(actual["languageStandard"], expected["languageStandard"])
547
548                if expected["defines"] is not None:
549                    expected_keys.append("defines")
550
551                    def check_define(actual, expected):
552                        assert is_dict(actual)
553                        expected_keys = ["define"]
554
555                        if expected["backtrace"] is not None:
556                            expected_keys.append("backtrace")
557                            check_backtrace(obj, actual["backtrace"], expected["backtrace"])
558
559                        assert sorted(actual.keys()) == sorted(expected_keys)
560
561                    check_list_match(lambda a, e: is_string(a["define"], e["define"]),
562                                     actual["defines"], expected["defines"],
563                                     check=check_define,
564                                     check_exception=lambda a, e: "Define: %s" % a["define"],
565                                     missing_exception=lambda e: "Define: %s" % e["define"],
566                                     extra_exception=lambda a: "Define: %s" % a["define"])
567
568                # FIXME: Properly test sysroot
569                if "sysroot" in actual:
570                    expected_keys.append("sysroot")
571                    assert is_string(actual["sysroot"])
572
573                assert sorted(actual.keys()) == sorted(expected_keys)
574
575            check_list_match(lambda a, e: is_string(a["language"], e["language"]),
576                             obj["compileGroups"], expected["compileGroups"],
577                             check=check_compile_group,
578                             check_exception=lambda a, e: "Compile group: %s" % a["language"],
579                             missing_exception=lambda e: "Compile group: %s" % e["language"],
580                             extra_exception=lambda a: "Compile group: %s" % a["language"])
581
582        assert sorted(obj.keys()) == sorted(expected_keys)
583
584    return _check
585
586def check_project(c):
587    def _check(actual, expected):
588        assert is_dict(actual)
589        expected_keys = ["name", "directoryIndexes"]
590
591        check_list_match(lambda a, e: matches(c["directories"][a]["source"], e),
592                         actual["directoryIndexes"], expected["directorySources"],
593                         missing_exception=lambda e: "Directory source: %s" % e,
594                         extra_exception=lambda a: "Directory source: %s" % c["directories"][a]["source"])
595
596        if expected["parentName"] is not None:
597            expected_keys.append("parentIndex")
598            assert is_int(actual["parentIndex"])
599            assert is_string(c["projects"][actual["parentIndex"]]["name"], expected["parentName"])
600
601        if expected["childNames"] is not None:
602            expected_keys.append("childIndexes")
603            check_list_match(lambda a, e: is_string(c["projects"][a]["name"], e),
604                             actual["childIndexes"], expected["childNames"],
605                             missing_exception=lambda e: "Child name: %s" % e,
606                             extra_exception=lambda a: "Child name: %s" % c["projects"][a]["name"])
607
608        if expected["targetIds"] is not None:
609            expected_keys.append("targetIndexes")
610            check_list_match(lambda a, e: matches(c["targets"][a]["id"], e),
611                             actual["targetIndexes"], expected["targetIds"],
612                             missing_exception=lambda e: "Target ID: %s" % e,
613                             extra_exception=lambda a: "Target ID: %s" % c["targets"][a]["id"])
614
615        assert sorted(actual.keys()) == sorted(expected_keys)
616
617    return _check
618
619def gen_check_directories(c, g):
620    expected = [
621        read_codemodel_json_data("directories/top.json"),
622        read_codemodel_json_data("directories/alias.json"),
623        read_codemodel_json_data("directories/custom.json"),
624        read_codemodel_json_data("directories/cxx.json"),
625        read_codemodel_json_data("directories/imported.json"),
626        read_codemodel_json_data("directories/interface.json"),
627        read_codemodel_json_data("directories/object.json"),
628        read_codemodel_json_data("directories/dir.json"),
629        read_codemodel_json_data("directories/dir_dir.json"),
630        read_codemodel_json_data("directories/external.json"),
631    ]
632
633    if matches(g["name"], "^Visual Studio "):
634        for e in expected:
635            if e["parentSource"] is not None:
636                e["targetIds"] = filter_list(lambda t: not matches(t, "^\\^ZERO_CHECK"), e["targetIds"])
637
638    elif g["name"] == "Xcode":
639        if ';' in os.environ.get("CMAKE_OSX_ARCHITECTURES", ""):
640            for e in expected:
641                e["targetIds"] = filter_list(lambda t: not matches(t, "^\\^(link_imported_object_exe)"), e["targetIds"])
642
643    else:
644        for e in expected:
645            e["targetIds"] = filter_list(lambda t: not matches(t, "^\\^(ALL_BUILD|ZERO_CHECK)"), e["targetIds"])
646
647    if sys.platform in ("win32", "cygwin", "msys") or "aix" in sys.platform:
648        for e in expected:
649            e["installers"] = list(filter(lambda i: i["targetInstallNamelink"] is None or i["targetInstallNamelink"] == "skip", e["installers"]))
650            for i in e["installers"]:
651                i["targetInstallNamelink"] = None
652
653    if sys.platform not in ("win32", "cygwin", "msys"):
654        for e in expected:
655            e["installers"] = list(filter(lambda i: not i.get("_dllExtra", False), e["installers"]))
656            if "aix" not in sys.platform:
657                for i in e["installers"]:
658                    if "pathsNamelink" in i:
659                        i["paths"] = i["pathsNamelink"]
660
661    if sys.platform not in ("win32", "darwin") and "linux" not in sys.platform:
662        for e in expected:
663            e["installers"] = list(filter(lambda i: i["type"] != "runtimeDependencySet", e["installers"]))
664
665    if sys.platform != "darwin":
666        for e in expected:
667            e["installers"] = list(filter(lambda i: i.get("runtimeDependencySetType", None) != "framework", e["installers"]))
668
669    return expected
670
671def check_directories(c, g):
672    check_list_match(lambda a, e: matches(a["source"], e["source"]), c["directories"], gen_check_directories(c, g),
673                     check=check_directory(c),
674                     check_exception=lambda a, e: "Directory source: %s" % a["source"],
675                     missing_exception=lambda e: "Directory source: %s" % e["source"],
676                     extra_exception=lambda a: "Directory source: %s" % a["source"])
677
678def gen_check_targets(c, g, inSource):
679    expected = [
680        read_codemodel_json_data("targets/all_build_top.json"),
681        read_codemodel_json_data("targets/zero_check_top.json"),
682        read_codemodel_json_data("targets/interface_exe.json"),
683        read_codemodel_json_data("targets/c_lib.json"),
684        read_codemodel_json_data("targets/c_exe.json"),
685        read_codemodel_json_data("targets/c_shared_lib.json"),
686        read_codemodel_json_data("targets/c_shared_exe.json"),
687        read_codemodel_json_data("targets/c_static_lib.json"),
688        read_codemodel_json_data("targets/c_static_exe.json"),
689
690        read_codemodel_json_data("targets/all_build_cxx.json"),
691        read_codemodel_json_data("targets/zero_check_cxx.json"),
692        read_codemodel_json_data("targets/cxx_lib.json"),
693        read_codemodel_json_data("targets/cxx_exe.json"),
694        read_codemodel_json_data("targets/cxx_standard_compile_feature_exe.json"),
695        read_codemodel_json_data("targets/cxx_standard_exe.json"),
696        read_codemodel_json_data("targets/cxx_shared_lib.json"),
697        read_codemodel_json_data("targets/cxx_shared_exe.json"),
698        read_codemodel_json_data("targets/cxx_static_lib.json"),
699        read_codemodel_json_data("targets/cxx_static_exe.json"),
700
701        read_codemodel_json_data("targets/all_build_alias.json"),
702        read_codemodel_json_data("targets/zero_check_alias.json"),
703        read_codemodel_json_data("targets/c_alias_exe.json"),
704        read_codemodel_json_data("targets/cxx_alias_exe.json"),
705
706        read_codemodel_json_data("targets/all_build_object.json"),
707        read_codemodel_json_data("targets/zero_check_object.json"),
708        read_codemodel_json_data("targets/c_object_lib.json"),
709        read_codemodel_json_data("targets/c_object_exe.json"),
710        read_codemodel_json_data("targets/cxx_object_lib.json"),
711        read_codemodel_json_data("targets/cxx_object_exe.json"),
712
713        read_codemodel_json_data("targets/all_build_imported.json"),
714        read_codemodel_json_data("targets/zero_check_imported.json"),
715        read_codemodel_json_data("targets/link_imported_exe.json"),
716        read_codemodel_json_data("targets/link_imported_shared_exe.json"),
717        read_codemodel_json_data("targets/link_imported_static_exe.json"),
718        read_codemodel_json_data("targets/link_imported_object_exe.json"),
719        read_codemodel_json_data("targets/link_imported_interface_exe.json"),
720
721        read_codemodel_json_data("targets/all_build_interface.json"),
722        read_codemodel_json_data("targets/zero_check_interface.json"),
723        read_codemodel_json_data("targets/iface_srcs.json"),
724
725        read_codemodel_json_data("targets/all_build_custom.json"),
726        read_codemodel_json_data("targets/zero_check_custom.json"),
727        read_codemodel_json_data("targets/custom_tgt.json"),
728        read_codemodel_json_data("targets/custom_exe.json"),
729        read_codemodel_json_data("targets/all_build_external.json"),
730        read_codemodel_json_data("targets/zero_check_external.json"),
731        read_codemodel_json_data("targets/generated_exe.json"),
732    ]
733
734    if cxx_compiler_id in ['Clang', 'AppleClang', 'GNU', 'Intel', 'IntelLLVM', 'MSVC', 'Embarcadero'] and g["name"] != "Xcode":
735        for e in expected:
736            if e["name"] == "cxx_exe":
737                if matches(g["name"], "^(Visual Studio |Ninja Multi-Config)"):
738                    precompile_header_data = read_codemodel_json_data("targets/cxx_exe_precompileheader_multigen.json")
739                else:
740                    if ';' in os.environ.get("CMAKE_OSX_ARCHITECTURES", ""):
741                        precompile_header_data = read_codemodel_json_data("targets/cxx_exe_precompileheader_2arch.json")
742                    else:
743                        precompile_header_data = read_codemodel_json_data("targets/cxx_exe_precompileheader.json")
744                e["compileGroups"] = precompile_header_data["compileGroups"]
745                e["sources"] = precompile_header_data["sources"]
746                e["sourceGroups"] = precompile_header_data["sourceGroups"]
747
748    if os.path.exists(os.path.join(reply_dir, "..", "..", "..", "..", "cxx", "cxx_std_11.txt")):
749        for e in expected:
750            if e["name"] == "cxx_standard_compile_feature_exe":
751                language_standard_data = read_codemodel_json_data("targets/cxx_standard_compile_feature_exe_languagestandard.json")
752                e["compileGroups"][0]["languageStandard"] = language_standard_data["languageStandard"]
753
754    if not os.path.exists(os.path.join(reply_dir, "..", "..", "..", "..", "ipo_enabled.txt")):
755        for e in expected:
756            try:
757                e["link"]["lto"] = None
758            except TypeError:  # "link" is not a dict, no problem.
759                pass
760            try:
761                e["archive"]["lto"] = None
762            except TypeError:  # "archive" is not a dict, no problem.
763                pass
764
765    if inSource:
766        for e in expected:
767            if e["sources"] is not None:
768                for s in e["sources"]:
769                    s["path"] = s["path"].replace("^.*/Tests/RunCMake/FileAPI/", "^", 1)
770            if e["sourceGroups"] is not None:
771                for group in e["sourceGroups"]:
772                    group["sourcePaths"] = [p.replace("^.*/Tests/RunCMake/FileAPI/", "^", 1) for p in group["sourcePaths"]]
773            if e["compileGroups"] is not None:
774                for group in e["compileGroups"]:
775                    group["sourcePaths"] = [p.replace("^.*/Tests/RunCMake/FileAPI/", "^", 1) for p in group["sourcePaths"]]
776
777    if matches(g["name"], "^Visual Studio "):
778        expected = filter_list(lambda e: e["name"] not in ("ZERO_CHECK") or e["id"] == "^ZERO_CHECK::@6890427a1f51a3e7e1df$", expected)
779        for e in expected:
780            if e["type"] == "UTILITY":
781                if e["id"] == "^ZERO_CHECK::@6890427a1f51a3e7e1df$":
782                    e["sources"] = [
783                        {
784                            "path": "^.*/Tests/RunCMake/FileAPI/codemodel-v2-build/CMakeFiles/([0-9a-f]+/)?generate\\.stamp\\.rule$",
785                            "isGenerated": True,
786                            "sourceGroupName": "CMake Rules",
787                            "compileGroupLanguage": None,
788                            "backtrace": [
789                                {
790                                    "file": "^CMakeLists\\.txt$",
791                                    "line": None,
792                                    "command": None,
793                                    "hasParent": False,
794                                },
795                            ],
796                        },
797                    ]
798                    e["sourceGroups"] = [
799                        {
800                            "name": "CMake Rules",
801                            "sourcePaths": [
802                                "^.*/Tests/RunCMake/FileAPI/codemodel-v2-build/CMakeFiles/([0-9a-f]+/)?generate\\.stamp\\.rule$",
803                            ],
804                        },
805                    ]
806                elif e["name"] in ("ALL_BUILD"):
807                    e["sources"] = []
808                    e["sourceGroups"] = None
809            if e["dependencies"] is not None:
810                for d in e["dependencies"]:
811                    if matches(d["id"], "^\\^ZERO_CHECK::@"):
812                        d["id"] = "^ZERO_CHECK::@6890427a1f51a3e7e1df$"
813
814    elif g["name"] == "Xcode":
815        if ';' in os.environ.get("CMAKE_OSX_ARCHITECTURES", ""):
816            expected = filter_list(lambda e: e["name"] not in ("link_imported_object_exe"), expected)
817            for e in expected:
818                e["dependencies"] = filter_list(lambda d: not matches(d["id"], "^\\^link_imported_object_exe::@"), e["dependencies"])
819                if e["name"] in ("c_object_lib", "cxx_object_lib"):
820                    e["artifacts"] = None
821
822    else:
823        for e in expected:
824            e["dependencies"] = filter_list(lambda d: not matches(d["id"], "^\\^ZERO_CHECK::@"), e["dependencies"])
825
826        expected = filter_list(lambda t: t["name"] not in ("ALL_BUILD", "ZERO_CHECK"), expected)
827
828    if sys.platform not in ("win32", "cygwin", "msys"):
829        for e in expected:
830            e["artifacts"] = filter_list(lambda a: not a["_dllExtra"], e["artifacts"])
831            if e["install"] is not None:
832                e["install"]["destinations"] = filter_list(lambda d: "_dllExtra" not in d or not d["_dllExtra"], e["install"]["destinations"])
833
834    else:
835        for e in expected:
836            if e["install"] is not None:
837                e["install"]["destinations"] = filter_list(lambda d: "_namelink" not in d or not d["_namelink"], e["install"]["destinations"])
838
839    if "aix" not in sys.platform:
840        for e in expected:
841            e["artifacts"] = filter_list(lambda a: not a.get("_aixExtra", False), e["artifacts"])
842
843    return expected
844
845def check_targets(c, g, inSource):
846    check_list_match(lambda a, e: matches(a["id"], e["id"]),
847                     c["targets"], gen_check_targets(c, g, inSource),
848                     check=check_target(c),
849                     check_exception=lambda a, e: "Target ID: %s" % a["id"],
850                     missing_exception=lambda e: "Target ID: %s" % e["id"],
851                     extra_exception=lambda a: "Target ID: %s" % a["id"])
852
853def gen_check_projects(c, g):
854    expected = [
855        read_codemodel_json_data("projects/codemodel-v2.json"),
856        read_codemodel_json_data("projects/cxx.json"),
857        read_codemodel_json_data("projects/alias.json"),
858        read_codemodel_json_data("projects/object.json"),
859        read_codemodel_json_data("projects/imported.json"),
860        read_codemodel_json_data("projects/interface.json"),
861        read_codemodel_json_data("projects/custom.json"),
862        read_codemodel_json_data("projects/external.json"),
863    ]
864
865    if matches(g["name"], "^Visual Studio "):
866        for e in expected:
867            if e["parentName"] is not None:
868                e["targetIds"] = filter_list(lambda t: not matches(t, "^\\^ZERO_CHECK"), e["targetIds"])
869
870    elif g["name"] == "Xcode":
871        if ';' in os.environ.get("CMAKE_OSX_ARCHITECTURES", ""):
872            for e in expected:
873                e["targetIds"] = filter_list(lambda t: not matches(t, "^\\^(link_imported_object_exe)"), e["targetIds"])
874
875    else:
876        for e in expected:
877            e["targetIds"] = filter_list(lambda t: not matches(t, "^\\^(ALL_BUILD|ZERO_CHECK)"), e["targetIds"])
878
879    return expected
880
881def check_projects(c, g):
882    check_list_match(lambda a, e: is_string(a["name"], e["name"]), c["projects"], gen_check_projects(c, g),
883                     check=check_project(c),
884                     check_exception=lambda a, e: "Project name: %s" % a["name"],
885                     missing_exception=lambda e: "Project name: %s" % e["name"],
886                     extra_exception=lambda a: "Project name: %s" % a["name"])
887
888def check_object_codemodel_configuration(c, g, inSource):
889    assert sorted(c.keys()) == ["directories", "name", "projects", "targets"]
890    assert is_string(c["name"])
891    check_directories(c, g)
892    check_targets(c, g, inSource)
893    check_projects(c, g)
894
895def check_object_codemodel(g):
896    def _check(o):
897        assert sorted(o.keys()) == ["configurations", "kind", "paths", "version"]
898        # The "kind" and "version" members are handled by check_index_object.
899        assert is_dict(o["paths"])
900        assert sorted(o["paths"].keys()) == ["build", "source"]
901        assert matches(o["paths"]["build"], "^.*/Tests/RunCMake/FileAPI/codemodel-v2-build$")
902        assert matches(o["paths"]["source"], "^.*/Tests/RunCMake/FileAPI$")
903
904        inSource = os.path.dirname(o["paths"]["build"]) == o["paths"]["source"]
905
906        if g["multiConfig"]:
907            assert sorted([c["name"] for c in o["configurations"]]) == ["Debug", "MinSizeRel", "RelWithDebInfo", "Release"]
908        else:
909            assert len(o["configurations"]) == 1
910            assert o["configurations"][0]["name"] in ("", "Debug", "Release", "RelWithDebInfo", "MinSizeRel")
911
912        for c in o["configurations"]:
913            check_object_codemodel_configuration(c, g, inSource)
914    return _check
915
916cxx_compiler_id = sys.argv[2]
917assert is_dict(index)
918assert sorted(index.keys()) == ["cmake", "objects", "reply"]
919check_objects(index["objects"], index["cmake"]["generator"])
920