Skip to content

Commit a9877ed

Browse files
committed
Swift: split generated C++ files by directory
1 parent b2ebf63 commit a9877ed

21 files changed

+585
-456
lines changed

swift/codegen/generators/cppgen.py

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
"""
1313

1414
import functools
15+
import pathlib
1516
from typing import Dict
1617

1718
import inflection
@@ -70,12 +71,20 @@ def _get_class(self, name: str) -> cpp.Class:
7071
)
7172

7273
def get_classes(self):
73-
inheritance_graph = {k: cls.bases for k, cls in self._classmap.items()}
74-
return [self._get_class(cls) for cls in toposort_flatten(inheritance_graph)]
74+
grouped = {pathlib.Path(): {}}
75+
for k, cls in self._classmap.items():
76+
grouped.setdefault(cls.dir, {}).update({k: cls})
77+
ret = {}
78+
for dir, map in grouped.items():
79+
inheritance_graph = {k: {b for b in cls.bases if b in map} for k, cls in map.items()}
80+
ret[dir] = [self._get_class(cls) for cls in toposort_flatten(inheritance_graph)]
81+
return ret
7582

7683

7784
def generate(opts, renderer):
7885
assert opts.cpp_output
7986
processor = Processor({cls.name: cls for cls in schema.load(opts.schema).classes})
8087
out = opts.cpp_output
81-
renderer.render(cpp.ClassList(processor.get_classes(), opts.schema), out / "TrapClasses")
88+
for dir, classes in processor.get_classes().items():
89+
include_parent = (dir != pathlib.Path())
90+
renderer.render(cpp.ClassList(classes, opts.schema, include_parent), out / dir / "TrapClasses")

swift/codegen/generators/dbschemegen.py

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ def cls_to_dbscheme(cls: schema.Class):
3535
""" Yield all dbscheme entities needed to model class `cls` """
3636
if cls.derived:
3737
yield Union(dbtype(cls.name), (dbtype(c) for c in cls.derived))
38+
dir = cls.dir if cls.dir != pathlib.Path() else None
3839
# output a table specific to a class only if it is a leaf class or it has 1-to-1 properties
3940
# Leaf classes need a table to bind the `@` ids
4041
# 1-to-1 properties are added to a class specific table
@@ -49,7 +50,8 @@ def cls_to_dbscheme(cls: schema.Class):
4950
Column("id", type=dbtype(cls.name), binding=binding),
5051
] + [
5152
Column(f.name, dbtype(f.type)) for f in cls.properties if f.is_single
52-
]
53+
],
54+
dir=dir,
5355
)
5456
# use property-specific tables for 1-to-many and 1-to-at-most-1 properties
5557
for f in cls.properties:
@@ -61,7 +63,8 @@ def cls_to_dbscheme(cls: schema.Class):
6163
Column("id", type=dbtype(cls.name)),
6264
Column("index", type="int"),
6365
Column(inflection.singularize(f.name), dbtype(f.type)),
64-
]
66+
],
67+
dir=dir,
6568
)
6669
elif f.is_optional:
6770
yield Table(
@@ -71,6 +74,7 @@ def cls_to_dbscheme(cls: schema.Class):
7174
Column("id", type=dbtype(cls.name)),
7275
Column(f.name, dbtype(f.type)),
7376
],
77+
dir=dir,
7478
)
7579
elif f.is_predicate:
7680
yield Table(
@@ -79,6 +83,7 @@ def cls_to_dbscheme(cls: schema.Class):
7983
columns=[
8084
Column("id", type=dbtype(cls.name)),
8185
],
86+
dir=dir,
8287
)
8388

8489

swift/codegen/generators/trapgen.py

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
"""
1313

1414
import logging
15+
import pathlib
1516

1617
import inflection
1718
from toposort import toposort_flatten
@@ -71,16 +72,18 @@ def generate(opts, renderer):
7172
tag_graph = {}
7273
out = opts.cpp_output
7374

74-
traps = []
75+
traps = {pathlib.Path(): []}
7576
for e in dbscheme.iterload(opts.dbscheme):
7677
if e.is_table:
77-
traps.append(get_trap(e))
78+
traps.setdefault(e.dir, []).append(get_trap(e))
7879
elif e.is_union:
7980
tag_graph.setdefault(e.lhs, set())
8081
for d in e.rhs:
8182
tag_graph.setdefault(d.type, set()).add(e.lhs)
8283

83-
renderer.render(cpp.TrapList(traps, opts.dbscheme), out / "TrapEntries")
84+
for dir, entries in traps.items():
85+
dir = dir or pathlib.Path()
86+
renderer.render(cpp.TrapList(entries, opts.dbscheme), out / dir / "TrapEntries")
8487

8588
tags = []
8689
for index, tag in enumerate(toposort_flatten(tag_graph)):

swift/codegen/lib/cpp.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -148,3 +148,4 @@ class ClassList:
148148

149149
classes: List[Class]
150150
source: str
151+
include_parent: bool = False

swift/codegen/lib/dbscheme.py

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
""" dbscheme format representation """
22

33
import logging
4+
import pathlib
45
import re
56
from dataclasses import dataclass
67
from typing import ClassVar, List
@@ -64,6 +65,7 @@ class Table(Decl):
6465
name: str
6566
columns: List[Column]
6667
keyset: KeySet = None
68+
dir: pathlib.Path = None
6769

6870
def __post_init__(self):
6971
if self.columns:
@@ -108,13 +110,15 @@ class Scheme:
108110
class Re:
109111
entity = re.compile(
110112
"(?m)"
111-
r"(?:^#keyset\[(?P<tablekeys>[\w\s,]+)\][\s\n]*)?^(?P<table>\w+)\((?P<tablebody>[^\)]*)\);?"
113+
r"(?:^#keyset\[(?P<tablekeys>[\w\s,]+)\][\s\n]*)?^(?P<table>\w+)\("
114+
r"(?:\s*//dir=(?P<tabledir>\S*))?(?P<tablebody>[^\)]*)"
115+
r"\);?"
112116
"|"
113117
r"^(?P<union>@\w+)\s*=\s*(?P<unionbody>@\w+(?:\s*\|\s*@\w+)*)\s*;?"
114118
)
115119
field = re.compile(r"(?m)[\w\s]*\s(?P<field>\w+)\s*:\s*(?P<type>@?\w+)(?P<ref>\s+ref)?")
116120
key = re.compile(r"@\w+")
117-
comment = re.compile(r"(?m)(?s)/\*.*?\*/|//[^\n]*$")
121+
comment = re.compile(r"(?m)(?s)/\*.*?\*/|//(?!dir=)[^\n]*$") # lookahead avoid ignoring metadata like //dir=foo
118122

119123

120124
def get_column(match):
@@ -133,6 +137,7 @@ def get_table(match):
133137
name=match["table"],
134138
columns=[get_column(f) for f in Re.field.finditer(match["tablebody"])],
135139
keyset=keyset,
140+
dir=pathlib.PosixPath(match["tabledir"]) if match["tabledir"] else None,
136141
)
137142

138143

swift/codegen/templates/cpp_classes_h.mustache

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,9 @@
99
#include "swift/extractor/trap/TrapLabel.h"
1010
#include "swift/extractor/trap/TrapTagTraits.h"
1111
#include "./TrapEntries.h"
12+
{{#include_parent}}
13+
#include "../TrapClasses.h"
14+
{{/include_parent}}
1215

1316
namespace codeql {
1417
{{#classes}}

swift/codegen/templates/dbscheme.mustache

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
{{#keyset}}
1919
#keyset[{{#ids}}{{^first}}, {{/first}}{{id}}{{/ids}}]
2020
{{/keyset}}
21-
{{name}}({{#columns}}{{^first}},{{/first}}
21+
{{name}}({{#dir}} //dir={{.}}{{/dir}}{{#columns}}{{^first}},{{/first}}
2222
{{lhstype}} {{name}}: {{rhstype}}{{/columns}}
2323
);
2424
{{/is_table}}

swift/codegen/templates/trap_traps_h.mustache

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77

88
#include "swift/extractor/trap/TrapLabel.h"
99
#include "swift/extractor/trap/TrapTagTraits.h"
10-
#include "./TrapTags.h"
10+
#include "swift/extractor/trap/generated/TrapTags.h"
1111

1212
namespace codeql {
1313
{{#traps}}

swift/codegen/test/test_cppgen.py

Lines changed: 36 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -8,16 +8,27 @@
88

99

1010
@pytest.fixture
11-
def generate(opts, renderer, input):
11+
def generate_grouped(opts, renderer, input):
1212
opts.cpp_output = output_dir
1313

1414
def ret(classes):
1515
input.classes = classes
1616
generated = run_generation(cppgen.generate, opts, renderer)
17-
assert set(generated) == {output_dir / "TrapClasses"}
18-
generated = generated[output_dir / "TrapClasses"]
19-
assert isinstance(generated, cpp.ClassList)
20-
return generated.classes
17+
for f, g in generated.items():
18+
assert isinstance(g, cpp.ClassList), f
19+
assert g.include_parent is (f.parent != output_dir)
20+
assert f.name == "TrapClasses", f
21+
return {str(f.parent.relative_to(output_dir)): g.classes for f, g in generated.items()}
22+
23+
return ret
24+
25+
26+
@pytest.fixture
27+
def generate(generate_grouped):
28+
def ret(classes):
29+
generated = generate_grouped(classes)
30+
assert set(generated) == {"."}
31+
return generated["."]
2132

2233
return ret
2334

@@ -88,10 +99,12 @@ def test_class_with_field(generate, type, expected, property_cls, optional, repe
8899

89100
def test_class_with_predicate(generate):
90101
assert generate([
91-
schema.Class(name="MyClass", properties=[schema.PredicateProperty("prop")]),
102+
schema.Class(name="MyClass", properties=[
103+
schema.PredicateProperty("prop")]),
92104
]) == [
93105
cpp.Class(name="MyClass",
94-
fields=[cpp.Field("prop", "bool", trap_name="MyClassProp", is_predicate=True)],
106+
fields=[
107+
cpp.Field("prop", "bool", trap_name="MyClassProp", is_predicate=True)],
95108
trap_name="MyClasses",
96109
final=True)
97110
]
@@ -136,5 +149,21 @@ def test_class_with_keyword_field(generate, name):
136149
]
137150

138151

152+
def test_classes_with_dirs(generate_grouped):
153+
cbase = cpp.Class(name="CBase")
154+
assert generate_grouped([
155+
schema.Class(name="A"),
156+
schema.Class(name="B", dir=pathlib.Path("foo")),
157+
schema.Class(name="C", bases={"CBase"}, dir=pathlib.Path("bar")),
158+
schema.Class(name="CBase", derived={"C"}, dir=pathlib.Path("bar")),
159+
schema.Class(name="D", dir=pathlib.Path("foo/bar/baz")),
160+
]) == {
161+
".": [cpp.Class(name="A", trap_name="As", final=True)],
162+
"foo": [cpp.Class(name="B", trap_name="Bs", final=True)],
163+
"bar": [cbase, cpp.Class(name="C", bases=[cbase], trap_name="Cs", final=True)],
164+
"foo/bar/baz": [cpp.Class(name="D", trap_name="Ds", final=True)],
165+
}
166+
167+
139168
if __name__ == '__main__':
140169
sys.exit(pytest.main([__file__] + sys.argv[1:]))

swift/codegen/test/test_dbscheme.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,18 @@ def test_load_table_with_multiple_columns(load):
114114
]
115115

116116

117+
def test_load_table_with_multiple_columns_and_dir(load):
118+
columns = ",\n".join(c for c, _ in expected_columns)
119+
expected = [deepcopy(e) for _, e in expected_columns]
120+
assert load(f"""
121+
foos( //dir=foo/bar/baz
122+
{columns}
123+
);
124+
""") == [
125+
dbscheme.Table(name="foos", columns=expected, dir=pathlib.Path("foo/bar/baz"))
126+
]
127+
128+
117129
def test_load_multiple_table_with_columns(load):
118130
tables = [f"table{i}({col});" for i, (col, _) in enumerate(expected_columns)]
119131
expected = [dbscheme.Table(name=f"table{i}", columns=[deepcopy(e)]) for i, (_, e) in enumerate(expected_columns)]

0 commit comments

Comments
 (0)