Skip to content

Commit e4627cb

Browse files
committed
Swift: make codegen a bit more language-agnostic
1 parent f8f926a commit e4627cb

File tree

8 files changed

+114
-98
lines changed

8 files changed

+114
-98
lines changed

swift/codegen/codegen.py

Lines changed: 34 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -24,31 +24,44 @@ def _parse_args() -> argparse.Namespace:
2424
"and cpp")
2525
p.add_argument("--verbose", "-v", action="store_true", help="print more information")
2626
p.add_argument("--quiet", "-q", action="store_true", help="only print errors")
27-
p.add_argument("--swift-dir", type=_abspath, default=paths.swift_dir,
28-
help="the directory that should be regarded as the root of the swift codebase. Used to compute QL "
29-
"imports and in some comments (default %(default)s)")
30-
p.add_argument("--schema", type=_abspath, default=paths.swift_dir / "schema.py",
31-
help="input schema file (default %(default)s)")
32-
p.add_argument("--dbscheme", type=_abspath, default=paths.swift_dir / "ql/lib/swift.dbscheme",
33-
help="output file for dbscheme generation, input file for trap generation (default %(default)s)")
34-
p.add_argument("--ql-output", type=_abspath, default=paths.swift_dir / "ql/lib/codeql/swift/generated",
35-
help="output directory for generated QL files (default %(default)s)")
36-
p.add_argument("--ql-stub-output", type=_abspath, default=paths.swift_dir / "ql/lib/codeql/swift/elements",
37-
help="output directory for QL stub/customization files (default %(default)s). Defines also the "
38-
"generated qll file importing every class file")
39-
p.add_argument("--ql-test-output", type=_abspath, default=paths.swift_dir / "ql/test/extractor-tests/generated",
40-
help="output directory for QL generated extractor test files (default %(default)s)")
27+
p.add_argument("--root-dir", type=_abspath, default=paths.root_dir,
28+
help="the directory that should be regarded as the root of the language pack codebase. Used to"
29+
"compute QL imports and in some comments and as root for relative paths provided as options "
30+
"(default %(default)s)")
31+
p.add_argument("--language", default=paths.root_dir.name,
32+
help="string that should replace {language} in other provided options")
33+
path_arguments = [
34+
p.add_argument("--schema", default="schema.py",
35+
help="input schema file (default %(default)s)"),
36+
p.add_argument("--dbscheme", default="ql/lib/{language}.dbscheme",
37+
help="output file for dbscheme generation, input file for trap generation (default "
38+
"%(default)s)"),
39+
p.add_argument("--ql-output", default="ql/lib/codeql/{language}/generated",
40+
help="output directory for generated QL files (default %(default)s)"),
41+
p.add_argument("--ql-stub-output", default="ql/lib/codeql/{language}/elements",
42+
help="output directory for QL stub/customization files (default %(default)s). Defines also the "
43+
"generated qll file importing every class file"),
44+
p.add_argument("--ql-test-output", default="ql/test/extractor-tests/generated",
45+
help="output directory for QL generated extractor test files (default %(default)s)"),
46+
p.add_argument("--cpp-output",
47+
help="output directory for generated C++ files, required if trap or cpp is provided to "
48+
"--generate"),
49+
p.add_argument("--generated-registry", default="ql/.generated.list",
50+
help="registry file containing information about checked-in generated code"),
51+
]
4152
p.add_argument("--ql-format", action="store_true", default=True,
4253
help="use codeql to autoformat QL files (which is the default)")
4354
p.add_argument("--no-ql-format", action="store_false", dest="ql_format", help="do not format QL files")
4455
p.add_argument("--codeql-binary", default="codeql", help="command to use for QL formatting (default %(default)s)")
45-
p.add_argument("--cpp-output", type=_abspath,
46-
help="output directory for generated C++ files, required if trap or cpp is provided to --generate")
47-
p.add_argument("--generated-registry", type=_abspath, default=paths.swift_dir / "ql/.generated.list",
48-
help="registry file containing information about checked-in generated code")
4956
p.add_argument("--force", "-f", action="store_true",
50-
help="generate all files without skipping unchanged files and overwriting modified ones")
51-
return p.parse_args()
57+
help="generate all files without skipping unchanged files and overwriting modified ones"),
58+
opts = p.parse_args()
59+
# absolutize all paths relative to --root-dir
60+
for arg in path_arguments:
61+
path = getattr(opts, arg.dest)
62+
if path is not None:
63+
setattr(opts, arg.dest, opts.root_dir / path.format(language=opts.language))
64+
return opts
5265

5366

5467
def _abspath(x: str) -> typing.Optional[pathlib.Path]:
@@ -65,7 +78,7 @@ def run():
6578
log_level = logging.INFO
6679
logging.basicConfig(format="{levelname} {message}", style='{', level=log_level)
6780
for target in opts.generate:
68-
generate(target, opts, render.Renderer(opts.swift_dir))
81+
generate(target, opts, render.Renderer(opts.root_dir))
6982

7083

7184
if __name__ == "__main__":

swift/codegen/generators/dbschemegen.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -110,12 +110,12 @@ def get_declarations(data: schema.Schema):
110110
return declarations
111111

112112

113-
def get_includes(data: schema.Schema, include_dir: pathlib.Path, swift_dir: pathlib.Path):
113+
def get_includes(data: schema.Schema, include_dir: pathlib.Path, root_dir: pathlib.Path):
114114
includes = []
115115
for inc in data.includes:
116116
inc = include_dir / inc
117117
with open(inc) as inclusion:
118-
includes.append(SchemeInclude(src=inc.relative_to(swift_dir), data=inclusion.read()))
118+
includes.append(SchemeInclude(src=inc.relative_to(root_dir), data=inclusion.read()))
119119
return includes
120120

121121

@@ -125,8 +125,8 @@ def generate(opts, renderer):
125125

126126
data = schemaloader.load_file(input)
127127

128-
dbscheme = Scheme(src=input.relative_to(opts.swift_dir),
129-
includes=get_includes(data, include_dir=input.parent, swift_dir=opts.swift_dir),
128+
dbscheme = Scheme(src=input.relative_to(opts.root_dir),
129+
includes=get_includes(data, include_dir=input.parent, root_dir=opts.root_dir),
130130
declarations=get_declarations(data))
131131

132132
renderer.render(dbscheme, out)

swift/codegen/generators/qlgen.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -198,8 +198,8 @@ def get_ql_ipa_class(cls: schema.Class):
198198
return get_ql_ipa_class_db(cls.name)
199199

200200

201-
def get_import(file: pathlib.Path, swift_dir: pathlib.Path):
202-
stem = file.relative_to(swift_dir / "ql/lib").with_suffix("")
201+
def get_import(file: pathlib.Path, root_dir: pathlib.Path):
202+
stem = file.relative_to(root_dir / "ql/lib").with_suffix("")
203203
return str(stem).replace("/", ".")
204204

205205

@@ -344,7 +344,7 @@ def generate(opts, renderer):
344344

345345
classes_by_dir_and_name = sorted(classes.values(), key=lambda cls: (cls.dir, cls.name))
346346
for c in classes_by_dir_and_name:
347-
imports[c.name] = get_import(stub_out / c.path, opts.swift_dir)
347+
imports[c.name] = get_import(stub_out / c.path, opts.root_dir)
348348

349349
for c in classes.values():
350350
qll = out / c.path.with_suffix(".qll")
@@ -355,7 +355,7 @@ def generate(opts, renderer):
355355
path = _get_path(c)
356356
stub_file = stub_out / path
357357
if not renderer.is_customized_stub(stub_file):
358-
base_import = get_import(out / path, opts.swift_dir)
358+
base_import = get_import(out / path, opts.root_dir)
359359
renderer.render(_get_stub(c, base_import), stub_file)
360360

361361
# for example path/to/elements -> path/to/elements.qll
@@ -404,7 +404,7 @@ def generate(opts, renderer):
404404
if not renderer.is_customized_stub(stub_file):
405405
# stub rendering must be postponed as we might not have yet all subtracted ipa types in `ipa_type`
406406
stubs[stub_file] = ql.Synth.ConstructorStub(ipa_type)
407-
constructor_import = get_import(stub_file, opts.swift_dir)
407+
constructor_import = get_import(stub_file, opts.root_dir)
408408
constructor_imports.append(constructor_import)
409409
if ipa_type.is_ipa:
410410
ipa_constructor_imports.append(constructor_import)

swift/codegen/lib/paths.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,13 @@
66

77
try:
88
workspace_dir = pathlib.Path(os.environ['BUILD_WORKSPACE_DIRECTORY']).resolve() # <- means we are using bazel run
9-
swift_dir = workspace_dir / 'swift'
9+
root_dir = workspace_dir / 'swift'
1010
except KeyError:
1111
_this_file = pathlib.Path(__file__).resolve()
12-
swift_dir = _this_file.parents[2]
13-
workspace_dir = swift_dir.parent
12+
root_dir = _this_file.parents[2]
13+
workspace_dir = root_dir.parent
1414

15-
lib_dir = swift_dir / 'codegen' / 'lib'
16-
templates_dir = swift_dir / 'codegen' / 'templates'
15+
lib_dir = root_dir / 'codegen' / 'lib'
16+
templates_dir = root_dir / 'codegen' / 'templates'
1717

1818
exe_file = pathlib.Path(sys.argv[0]).resolve()

swift/codegen/lib/render.py

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -25,13 +25,16 @@ class Error(Exception):
2525
class Renderer:
2626
""" Template renderer using mustache templates in the `templates` directory """
2727

28-
def __init__(self, swift_dir: pathlib.Path):
28+
def __init__(self, root_dir: pathlib.Path):
2929
self._r = pystache.Renderer(search_dirs=str(paths.templates_dir), escape=lambda u: u)
30-
self._swift_dir = swift_dir
31-
self._generator = self._get_path(paths.exe_file)
30+
self._root_dir = root_dir
31+
try:
32+
self._generator = self._get_path(paths.exe_file)
33+
except ValueError:
34+
self._generator = paths.exe_file.name
3235

3336
def _get_path(self, file: pathlib.Path):
34-
return file.relative_to(self._swift_dir)
37+
return file.relative_to(self._root_dir)
3538

3639
def render(self, data: object, output: pathlib.Path):
3740
""" Render `data` to `output`.
@@ -60,7 +63,7 @@ def _do_write(self, mnemonic: str, contents: str, output: pathlib.Path):
6063

6164
def manage(self, generated: typing.Iterable[pathlib.Path], stubs: typing.Iterable[pathlib.Path],
6265
registry: pathlib.Path, force: bool = False) -> "RenderManager":
63-
return RenderManager(self._swift_dir, generated, stubs, registry, force)
66+
return RenderManager(self._root_dir, generated, stubs, registry, force)
6467

6568

6669
class RenderManager(Renderer):
@@ -85,10 +88,10 @@ class Hashes:
8588
pre: str
8689
post: typing.Optional[str] = None
8790

88-
def __init__(self, swift_dir: pathlib.Path, generated: typing.Iterable[pathlib.Path],
91+
def __init__(self, root_dir: pathlib.Path, generated: typing.Iterable[pathlib.Path],
8992
stubs: typing.Iterable[pathlib.Path],
9093
registry: pathlib.Path, force: bool = False):
91-
super().__init__(swift_dir)
94+
super().__init__(root_dir)
9295
self._registry_path = registry
9396
self._force = force
9497
self._hashes = {}
@@ -117,7 +120,7 @@ def __exit__(self, exc_type, exc_val, exc_tb):
117120
self._hashes.pop(self._get_path(f), None)
118121
# clean up the registry from files that do not exist any more
119122
for f in list(self._hashes):
120-
if not (self._swift_dir / f).exists():
123+
if not (self._root_dir / f).exists():
121124
self._hashes.pop(f)
122125
self._dump_registry()
123126

swift/codegen/test/test_qlgen.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -17,16 +17,16 @@ def run_mock():
1717

1818

1919
# these are lambdas so that they will use patched paths when called
20-
def stub_path(): return paths.swift_dir / "ql/lib/stub/path"
20+
def stub_path(): return paths.root_dir / "ql/lib/stub/path"
2121

2222

23-
def ql_output_path(): return paths.swift_dir / "ql/lib/other/path"
23+
def ql_output_path(): return paths.root_dir / "ql/lib/other/path"
2424

2525

26-
def ql_test_output_path(): return paths.swift_dir / "ql/test/path"
26+
def ql_test_output_path(): return paths.root_dir / "ql/test/path"
2727

2828

29-
def generated_registry_path(): return paths.swift_dir / "registry.list"
29+
def generated_registry_path(): return paths.root_dir / "registry.list"
3030

3131

3232
def import_file(): return stub_path().with_suffix(".qll")
@@ -47,7 +47,7 @@ def qlgen_opts(opts):
4747
opts.ql_test_output = ql_test_output_path()
4848
opts.generated_registry = generated_registry_path()
4949
opts.ql_format = True
50-
opts.swift_dir = paths.swift_dir
50+
opts.root_dir = paths.root_dir
5151
opts.force = False
5252
return opts
5353

0 commit comments

Comments
 (0)