-
Notifications
You must be signed in to change notification settings - Fork 18
/
generate.py
executable file
·118 lines (92 loc) · 3.54 KB
/
generate.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
#!/usr/bin/env python3
import argparse
import importlib.util
import logging
import os
from os.path import join
from shutil import rmtree
import sys
from typing import List
sys.path.insert(0, "stone")
from stone.compiler import BackendException, Compiler
from stone.frontend.exception import InvalidSpec
from stone.frontend.frontend import specs_to_ir
class CodegenFailed(Exception):
pass
def load_source(module_name, path):
"Reimplementation of the deprecated imp.load_source() function"
spec = importlib.util.spec_from_file_location(module_name, path)
module = importlib.util.module_from_spec(spec)
sys.modules[module_name] = module
spec.loader.exec_module(module)
return module
def spec_files(spec_root: str) -> List[str]:
specs = []
for dirent in os.scandir(spec_root):
if dirent.is_file() and dirent.path.endswith(".stone"):
specs.append(dirent.path)
return specs
def generate_code(spec_root: str, gen_rust: bool, gen_test: bool):
"""
This is basically stone/stone/cli.py stripped down and customized to our needs.
"""
targets = ["rust"] if gen_rust else []
targets += ["test"] if gen_test else []
print(f"Generating [{', '.join(targets)}] from {spec_root}")
specs = []
for path in spec_files(spec_root):
with open(path) as f:
specs.append((path, f.read()))
try:
api = specs_to_ir(specs)
except InvalidSpec as e:
print(f"{e.path}:{e.lineno}: error: {e.msg}", file=sys.stderr)
raise CodegenFailed
sys.path.append("generator")
for target in targets:
print(f"Running generator for {target}")
try:
backend_module = load_source(
f'{target}_backend', join('generator', f'{target}.stoneg.py'))
except Exception:
print(f"error: Importing backend \"{target}\" module raised an exception: ",
file=sys.stderr)
raise
destination = {
"rust": join("src", "generated"),
"test": join("tests", "generated"),
}[target]
rmtree(destination, ignore_errors=True)
c = Compiler(api, backend_module, [], destination)
try:
c.build()
except BackendException as e:
print(f"error: {e.backend_name} raised an exception:\n{e.traceback}",
file=sys.stderr)
raise CodegenFailed
if os.linesep != "\n":
# If this is Windows, rewrite the files to have the proper line ending.
for dirent in os.scandir(destination):
if dirent.is_file():
crlf_path = dirent.path + "_"
with open(dirent.path) as lf, open(crlf_path, "w") as crlf:
for line in lf:
crlf.write(line)
os.replace(crlf_path, dirent.path)
def main():
parser = argparse.ArgumentParser(description="generate SDK code from the Stone API spec")
parser.add_argument("--spec-path", type=str, default="dropbox-api-spec",
help="Path to the API spec submodule.")
parser.add_argument("--gen-rust", action="store_true")
parser.add_argument("--gen-test", action="store_true")
args = parser.parse_args()
if not args.gen_rust and not args.gen_test:
args.gen_rust = True
args.gen_test = True
logging.basicConfig(level=logging.INFO)
try:
generate_code(args.spec_path, args.gen_rust, args.gen_test)
except CodegenFailed:
exit(2)
if __name__ == "__main__":
main()