forked from AIDASoft/podio
-
Notifications
You must be signed in to change notification settings - Fork 0
/
podio_class_generator.py
executable file
·188 lines (164 loc) · 5.9 KB
/
podio_class_generator.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
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""Podio class generator script"""
import os
import subprocess
from podio_gen.podio_config_reader import PodioConfigReader
from podio_gen.generator_utils import DefinitionError
from podio_gen.cpp_generator import CPPClassGenerator
from podio_gen.julia_generator import JuliaClassGenerator
def has_clang_format():
"""Check if clang format is available"""
try:
# This one can raise if -fallback-style is not found
out = subprocess.check_output(
["clang-format", "-style=file", "-fallback-style=llvm", "--help"],
stderr=subprocess.STDOUT,
)
# This one doesn't raise
out = subprocess.check_output(
"echo | clang-format -style=file ", stderr=subprocess.STDOUT, shell=True
)
if b".clang-format" in out:
return False
return True
except FileNotFoundError:
print("ERROR: Cannot find clang-format executable")
print(" Please make sure it is in the PATH.")
return False
except subprocess.CalledProcessError:
print("ERROR: At least one argument was not recognized by clang-format")
print(" Most likely the version you are using is old")
return False
def clang_format_file(content, name):
"""Formatter function to run clang-format on generate c++ files"""
if name.endswith(".jl"):
return content
clang_format = ["clang-format", "-style=file", "-fallback-style=llvm"]
with subprocess.Popen(clang_format, stdin=subprocess.PIPE, stdout=subprocess.PIPE) as cfproc:
return cfproc.communicate(input=content.encode())[0].decode()
def read_upstream_edm(name_path):
"""Read an upstream EDM yaml definition file to make the types that are defined
in that available to the current EDM"""
if name_path is None:
return None
try:
name, path = name_path.split(":")
except ValueError as err:
raise argparse.ArgumentTypeError(
"upstream-edm argument needs to be the upstream package "
"name and the upstream edm yaml file separated by a colon"
) from err
if not os.path.isfile(path):
raise argparse.ArgumentTypeError(f"{path} needs to be an EDM yaml file")
try:
return PodioConfigReader.read(path, name)
except DefinitionError as err:
raise argparse.ArgumentTypeError(
f"{path} does not contain a valid datamodel definition"
) from err
if __name__ == "__main__":
import argparse
# pylint: disable=invalid-name # before 2.5.0 pylint is too strict with the naming here
parser = argparse.ArgumentParser(
description="Given a description yaml file this script generates "
"the necessary c++ or julia files in the target directory"
)
parser.add_argument("description", help="yaml file describing the datamodel")
parser.add_argument(
"targetdir",
help="Target directory where the generated data classes will be put. "
"Header files will be put under <targetdir>/<packagename>/*.h. "
"Source files will be put under <targetdir>/src/*.cc. "
"Julia files will be put under <targetdir>/<packagename>/*.jl.",
)
parser.add_argument("packagename", help="Name of the package.")
parser.add_argument(
"iohandlers",
choices=["ROOT", "SIO"],
nargs="*",
help="The IO backend specific code that should be generated",
default="ROOT",
)
parser.add_argument(
"-l",
"--lang",
choices=["cpp", "julia"],
default="cpp",
help="Specify the programming language (default: cpp)",
)
parser.add_argument(
"-q",
"--quiet",
dest="verbose",
action="store_false",
default=True,
help="Don't write a report to screen",
)
parser.add_argument(
"-d",
"--dryrun",
action="store_true",
default=False,
help="Do not actually write datamodel files",
)
parser.add_argument(
"-c",
"--clangformat",
action="store_true",
default=False,
help="Apply clang-format when generating code (with -style=file)",
)
parser.add_argument(
"--upstream-edm",
help="Make datatypes of this upstream EDM available to the current"
" EDM. Format is '<upstream-name>:<upstream.yaml>'. "
"Note that only the code for the current EDM will be generated",
default=None,
type=read_upstream_edm,
)
parser.add_argument(
"--old-description",
help="Provide schema evolution relative to the old yaml file.",
default=None,
action="store",
)
parser.add_argument(
"-e",
"--evolution_file",
help="yaml file clarifying schema evolutions",
default=None,
action="store",
)
args = parser.parse_args()
install_path = args.targetdir
project = args.packagename
for sub_dir in ("src", project):
directory = os.path.join(install_path, sub_dir)
if not os.path.exists(directory):
os.makedirs(directory)
if args.lang == "julia":
gen = JuliaClassGenerator(
args.description,
args.targetdir,
args.packagename,
verbose=args.verbose,
dryrun=args.dryrun,
upstream_edm=args.upstream_edm,
)
if args.lang == "cpp":
gen = CPPClassGenerator(
args.description,
args.targetdir,
args.packagename,
args.iohandlers,
verbose=args.verbose,
dryrun=args.dryrun,
upstream_edm=args.upstream_edm,
old_description=args.old_description,
evolution_file=args.evolution_file,
)
if args.clangformat and has_clang_format():
gen.formatter_func = clang_format_file
gen.process()
# pylint: enable=invalid-name