Skip to content

Commit 6230a3a

Browse files
authored
Merge pull request #9685 from github/redsun82/swift-code-reorg
Swift: split up generated C++ code
2 parents 80ffd81 + 299e1af commit 6230a3a

28 files changed

+663
-480
lines changed

.github/workflows/swift-codegen.yml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,8 @@ jobs:
2929
extra_args: swift-codegen --all-files
3030
- name: Generate C++ files
3131
run: |
32-
bazel run //swift/codegen:codegen -- --generate=trap,cpp --cpp-output=$PWD/swift-generated-headers
32+
bazel run //swift/codegen:codegen -- --generate=trap,cpp --cpp-output=$PWD/swift-generated-cpp-files
3333
- uses: actions/upload-artifact@v3
3434
with:
35-
name: swift-generated-headers
36-
path: swift-generated-headers/*.h
35+
name: swift-generated-cpp-files
36+
path: swift-generated-cpp-files/**

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 / f"TrapClasses.h")
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: 7 additions & 5 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,17 +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),
84-
out / f"TrapEntries.h")
84+
for dir, entries in traps.items():
85+
dir = dir or pathlib.Path()
86+
renderer.render(cpp.TrapList(entries, opts.dbscheme), out / dir / "TrapEntries")
8587

8688
tags = []
8789
for index, tag in enumerate(toposort_flatten(tag_graph)):
@@ -91,4 +93,4 @@ def generate(opts, renderer):
9193
index=index,
9294
id=tag,
9395
))
94-
renderer.render(cpp.TagList(tags, opts.dbscheme), out / f"TrapTags.h")
96+
renderer.render(cpp.TagList(tags, opts.dbscheme), out / "TrapTags")

swift/codegen/lib/cpp.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -99,14 +99,15 @@ def has_bases(self):
9999
@dataclass
100100
class TrapList:
101101
template: ClassVar = 'trap_traps'
102-
102+
extensions = ["h", "cpp"]
103103
traps: List[Trap]
104104
source: str
105105

106106

107107
@dataclass
108108
class TagList:
109109
template: ClassVar = 'trap_tags'
110+
extensions = ["h"]
110111

111112
tags: List[Tag]
112113
source: str
@@ -143,6 +144,8 @@ def single_fields(self):
143144
@dataclass
144145
class ClassList:
145146
template: ClassVar = "cpp_classes"
147+
extensions: ClassVar = ["h", "cpp"]
146148

147149
classes: List[Class]
148150
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/lib/render.py

Lines changed: 14 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -28,18 +28,23 @@ def render(self, data, output: pathlib.Path):
2828
2929
`data` must have a `template` attribute denoting which template to use from the template directory.
3030
31-
If the file is unchanged, then no write is performed (and `done_something` remains unchanged)
32-
33-
If `guard_base` is provided, it must be a path at the root of `output` and a header guard will be injected in
34-
the template based off of the relative path of `output` in `guard_base`
31+
Optionally, `data` can also have an `extensions` attribute denoting list of file extensions: they will all be
32+
appended to the template name with an underscore and be generated in turn.
3533
"""
3634
mnemonic = type(data).__name__
3735
output.parent.mkdir(parents=True, exist_ok=True)
38-
data = self._r.render_name(data.template, data, generator=self._generator)
39-
with open(output, "w") as out:
40-
out.write(data)
41-
log.debug(f"generated {mnemonic} {output.name}")
42-
self.written.add(output)
36+
extensions = getattr(data, "extensions", [None])
37+
for ext in extensions:
38+
output_filename = output
39+
template = data.template
40+
if ext:
41+
output_filename = output_filename.with_suffix(f".{ext}")
42+
template += f"_{ext}"
43+
contents = self._r.render_name(template, data, generator=self._generator)
44+
with open(output_filename, "w") as out:
45+
out.write(contents)
46+
log.debug(f"{mnemonic}: generated {output.name}")
47+
self.written.add(output_filename)
4348

4449
def cleanup(self, existing):
4550
""" Remove files in `existing` for which no `render` has been called """
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
// generated by {{generator}} from {{source}}
2+
// clang-format off
3+
#include "./TrapClasses.h"
4+
5+
namespace codeql {
6+
{{#classes}}
7+
8+
void {{name}}::emit({{^final}}TrapLabel<{{name}}Tag> id, {{/final}}std::ostream& out) const {
9+
{{#trap_name}}
10+
out << {{.}}Trap{id{{#single_fields}}, {{field_name}}{{/single_fields}}} << '\n';
11+
{{/trap_name}}
12+
{{#bases}}
13+
{{ref.name}}::emit(id, out);
14+
{{/bases}}
15+
{{#fields}}
16+
{{#is_predicate}}
17+
if ({{field_name}}) out << {{trap_name}}Trap{id} << '\n';
18+
{{/is_predicate}}
19+
{{#is_optional}}
20+
{{^is_repeated}}
21+
if ({{field_name}}) out << {{trap_name}}Trap{id, *{{field_name}}} << '\n';
22+
{{/is_repeated}}
23+
{{/is_optional}}
24+
{{#is_repeated}}
25+
for (auto i = 0u; i < {{field_name}}.size(); ++i) {
26+
{{^is_optional}}
27+
out << {{trap_name}}Trap{id, i, {{field_name}}[i]};
28+
{{/is_optional}}
29+
{{#is_optional}}
30+
if ({{field_name}}[i]) out << {{trap_name}}Trap{id, i, *{{field_name}}[i]};
31+
{{/is_optional}}
32+
}
33+
{{/is_repeated}}
34+
{{/fields}}
35+
}
36+
{{/classes}}
37+
}

swift/codegen/templates/cpp_classes.mustache renamed to swift/codegen/templates/cpp_classes_h.mustache

Lines changed: 4 additions & 28 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}}
@@ -31,34 +34,7 @@ struct {{name}}{{#has_bases}} : {{#bases}}{{^first}}, {{/first}}{{ref.name}}{{/b
3134
{{/final}}
3235

3336
protected:
34-
void emit({{^final}}TrapLabel<{{name}}Tag> id, {{/final}}std::ostream& out) const {
35-
{{#trap_name}}
36-
out << {{.}}Trap{id{{#single_fields}}, {{field_name}}{{/single_fields}}} << '\n';
37-
{{/trap_name}}
38-
{{#bases}}
39-
{{ref.name}}::emit(id, out);
40-
{{/bases}}
41-
{{#fields}}
42-
{{#is_predicate}}
43-
if ({{field_name}}) out << {{trap_name}}Trap{id} << '\n';
44-
{{/is_predicate}}
45-
{{#is_optional}}
46-
{{^is_repeated}}
47-
if ({{field_name}}) out << {{trap_name}}Trap{id, *{{field_name}}} << '\n';
48-
{{/is_repeated}}
49-
{{/is_optional}}
50-
{{#is_repeated}}
51-
for (auto i = 0u; i < {{field_name}}.size(); ++i) {
52-
{{^is_optional}}
53-
out << {{trap_name}}Trap{id, i, {{field_name}}[i]};
54-
{{/is_optional}}
55-
{{#is_optional}}
56-
if ({{field_name}}[i]) out << {{trap_name}}Trap{id, i, *{{field_name}}[i]};
57-
{{/is_optional}}
58-
}
59-
{{/is_repeated}}
60-
{{/fields}}
61-
}
37+
void emit({{^final}}TrapLabel<{{name}}Tag> id, {{/final}}std::ostream& out) const;
6238
};
6339

6440
template <>

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}}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
// generated by {{generator}} from {{source}}
2+
// clang-format off
3+
#include "./TrapEntries.h"
4+
5+
namespace codeql {
6+
{{#traps}}
7+
8+
// {{table_name}}
9+
std::ostream &operator<<(std::ostream &out, const {{name}}Trap &e) {
10+
out << "{{table_name}}("{{#fields}}{{^first}} << ", "{{/first}}
11+
<< {{#get_streamer}}e.{{field_name}}{{/get_streamer}}{{/fields}} << ")";
12+
return out;
13+
}
14+
{{/traps}}
15+
}

swift/codegen/templates/trap_traps.mustache renamed to swift/codegen/templates/trap_traps_h.mustache

Lines changed: 2 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -7,28 +7,19 @@
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}}
1414

1515
// {{table_name}}
1616
struct {{name}}Trap {
17-
static constexpr bool is_binding = {{#id}}true{{/id}}{{^id}}false{{/id}};
18-
{{#id}}
19-
{{type}} getBoundLabel() const { return {{field_name}}; }
20-
{{/id}}
21-
2217
{{#fields}}
2318
{{type}} {{field_name}}{};
2419
{{/fields}}
2520
};
2621

27-
inline std::ostream &operator<<(std::ostream &out, const {{name}}Trap &e) {
28-
out << "{{table_name}}("{{#fields}}{{^first}} << ", "{{/first}}
29-
<< {{#get_streamer}}e.{{field_name}}{{/get_streamer}}{{/fields}} << ")";
30-
return out;
31-
}
22+
std::ostream &operator<<(std::ostream &out, const {{name}}Trap &e);
3223
{{#id}}
3324

3425
namespace detail {

0 commit comments

Comments
 (0)