Commit | Line | Data |
---|---|---|
f5c77416 | 1 | #!/usr/bin/env python |
b25c5b37 | 2 | # |
c0c0989a | 3 | # SPDX-License-Identifier: GPL-2.0-only |
b25c5b37 | 4 | # |
c0c0989a | 5 | # Copyright (C) 2012 Yannick Brosseau <yannick.brosseau@gmail.com> |
b25c5b37 | 6 | |
2d9c7df1 | 7 | from __future__ import print_function |
b25c5b37 YB |
8 | import sys |
9 | import getopt | |
10 | import re | |
db06a0a2 YB |
11 | import os |
12 | import subprocess | |
b25c5b37 | 13 | |
2982a614 | 14 | |
b25c5b37 YB |
15 | class Usage(Exception): |
16 | def __init__(self, msg): | |
17 | self.msg = msg | |
18 | ||
2982a614 | 19 | |
b25c5b37 | 20 | class HeaderFile: |
2982a614 | 21 | HEADER_TPL = """ |
b25c5b37 YB |
22 | #undef TRACEPOINT_PROVIDER |
23 | #define TRACEPOINT_PROVIDER {providerName} | |
24 | ||
45f399e8 MD |
25 | #undef TRACEPOINT_INCLUDE |
26 | #define TRACEPOINT_INCLUDE "./{headerFilename}" | |
b25c5b37 | 27 | |
b25c5b37 YB |
28 | #if !defined({includeGuard}) || defined(TRACEPOINT_HEADER_MULTI_READ) |
29 | #define {includeGuard} | |
30 | ||
31 | #include <lttng/tracepoint.h> | |
32 | ||
33 | """ | |
2982a614 | 34 | FOOTER_TPL = """ |
b25c5b37 YB |
35 | #endif /* {includeGuard} */ |
36 | ||
37 | #include <lttng/tracepoint-event.h> | |
b25c5b37 | 38 | """ |
2982a614 | 39 | |
b25c5b37 YB |
40 | def __init__(self, filename, template): |
41 | self.outputFilename = filename | |
42 | self.template = template | |
43 | ||
44 | def write(self): | |
2982a614 | 45 | outputFile = open(self.outputFilename, "w") |
8ed68685 YB |
46 | # Include guard macro will be created by uppercasing the filename and |
47 | # replacing all non alphanumeric characters with '_' | |
48 | includeGuard = re.sub('[^0-9a-zA-Z]', '_', self.outputFilename.upper()) | |
b25c5b37 YB |
49 | |
50 | outputFile.write(HeaderFile.HEADER_TPL.format(providerName=self.template.domain, | |
2982a614 JR |
51 | includeGuard=includeGuard, |
52 | headerFilename=self.outputFilename)) | |
b25c5b37 | 53 | outputFile.write(self.template.text) |
2982a614 | 54 | outputFile.write(HeaderFile.FOOTER_TPL.format(includeGuard=includeGuard)) |
b25c5b37 YB |
55 | outputFile.close() |
56 | ||
2982a614 | 57 | |
b25c5b37 | 58 | class CFile: |
2982a614 | 59 | FILE_TPL = """ |
b25c5b37 YB |
60 | #define TRACEPOINT_CREATE_PROBES |
61 | /* | |
7f2f82c3 | 62 | * The header containing our LTTNG_UST_TRACEPOINT_EVENTs. |
b25c5b37 | 63 | */ |
88c7c4ea | 64 | #define LTTNG_UST_TRACEPOINT_DEFINE |
b25c5b37 YB |
65 | #include "{headerFilename}" |
66 | """ | |
2982a614 | 67 | |
b25c5b37 YB |
68 | def __init__(self, filename, template): |
69 | self.outputFilename = filename | |
70 | self.template = template | |
71 | ||
72 | def write(self): | |
2982a614 | 73 | outputFile = open(self.outputFilename, "w") |
b25c5b37 | 74 | |
b1714423 JR |
75 | headerFilename = self.outputFilename |
76 | if headerFilename.endswith(".c"): | |
77 | headerFilename = headerFilename[:-2] + ".h" | |
b25c5b37 YB |
78 | |
79 | outputFile.write(CFile.FILE_TPL.format( | |
2982a614 | 80 | headerFilename=headerFilename)) |
b25c5b37 YB |
81 | outputFile.close() |
82 | ||
2982a614 | 83 | |
db06a0a2 YB |
84 | class ObjFile: |
85 | def __init__(self, filename, template): | |
86 | self.outputFilename = filename | |
87 | self.template = template | |
2982a614 | 88 | |
db06a0a2 YB |
89 | def _detectCC(self): |
90 | cc = "" | |
2d9c7df1 | 91 | if 'CC' in os.environ: |
db06a0a2 YB |
92 | cc = os.environ['CC'] |
93 | try: | |
7996e006 | 94 | subprocess.call(cc.split(), |
db06a0a2 YB |
95 | stdout=subprocess.PIPE, |
96 | stderr=subprocess.PIPE) | |
2d9c7df1 ZT |
97 | except OSError as msg: |
98 | print("Invalid CC environment variable") | |
db06a0a2 YB |
99 | cc = "" |
100 | ||
101 | else: | |
102 | # Try c first, if that fails try gcc | |
103 | try: | |
104 | useCC = True | |
105 | subprocess.call("cc", | |
106 | stdout=subprocess.PIPE, | |
107 | stderr=subprocess.PIPE) | |
2d9c7df1 | 108 | except OSError as msg: |
db06a0a2 YB |
109 | useCC = False |
110 | if useCC: | |
111 | cc = "cc" | |
112 | ||
113 | else: | |
114 | try: | |
115 | useGCC = True | |
116 | subprocess.call("gcc", | |
117 | stdout=subprocess.PIPE, | |
118 | stderr=subprocess.PIPE) | |
2d9c7df1 | 119 | except OSError as msg: |
db06a0a2 YB |
120 | useGCC = False |
121 | if useGCC: | |
122 | cc = "gcc" | |
123 | return cc | |
124 | ||
125 | def write(self): | |
b1714423 JR |
126 | cFilename = self.outputFilename |
127 | if cFilename.endswith(".o"): | |
128 | cFilename = cFilename[:-2] + ".c" | |
129 | ||
db06a0a2 YB |
130 | cc = self._detectCC() |
131 | if cc == "": | |
132 | raise RuntimeError("No C Compiler detected") | |
170423b0 | 133 | if 'CPPFLAGS' in os.environ: |
aa4e204a | 134 | cppflags = " " + os.environ['CPPFLAGS'] |
170423b0 MD |
135 | else: |
136 | cppflags = "" | |
2d9c7df1 | 137 | if 'CFLAGS' in os.environ: |
aa4e204a | 138 | cflags = " " + os.environ['CFLAGS'] |
db06a0a2 YB |
139 | else: |
140 | cflags = "" | |
141 | ||
d619cda8 | 142 | command = cc + " -c" + cppflags + cflags + " -I. -o " + self.outputFilename + " " + cFilename |
0794b3f6 YB |
143 | if verbose: |
144 | print("Compile command: " + command) | |
db06a0a2 YB |
145 | subprocess.call(command.split()) |
146 | ||
2982a614 | 147 | |
b25c5b37 YB |
148 | class TemplateFile: |
149 | def __init__(self, filename): | |
150 | self.domain = "" | |
151 | self.inputFilename = filename | |
152 | self.parseTemplate() | |
153 | ||
b25c5b37 | 154 | def parseTemplate(self): |
2982a614 | 155 | f = open(self.inputFilename, "r") |
b25c5b37 YB |
156 | |
157 | self.text = f.read() | |
158 | ||
2982a614 | 159 | # Remove # comments (from input and output file) but keep |
0794b3f6 | 160 | # #include in the output file |
2982a614 JR |
161 | removeComments = re.compile("#[^include].*$", flags=re.MULTILINE) |
162 | self.text = removeComments.sub("", self.text) | |
0794b3f6 | 163 | # Remove #include directive from the parsed text |
2982a614 | 164 | removePreprocess = re.compile("#.*$", flags=re.MULTILINE) |
0794b3f6 | 165 | noPreprocess = removePreprocess.sub("", self.text) |
2982a614 JR |
166 | # Remove // comments |
167 | removeLineComment = re.compile("\/\/.*$", flags=re.MULTILINE) | |
0794b3f6 | 168 | nolinecomment = removeLineComment.sub("", noPreprocess) |
2982a614 JR |
169 | # Remove all spaces and lines |
170 | cleantext = re.sub("\s*", "", nolinecomment) | |
171 | # Remove multine C style comments | |
172 | nocomment = re.sub("/\*.*?\*/", "", cleantext) | |
612e9ce4 | 173 | entries = re.split("^LTTNG_UST_TRACEPOINT_.*?", nocomment) |
b25c5b37 YB |
174 | |
175 | for entry in entries: | |
176 | if entry != '': | |
177 | decomp = re.findall("(\w*?)\((\w*?),(\w*?),", entry) | |
178 | typea = decomp[0][0] | |
179 | domain = decomp[0][1] | |
180 | name = decomp[0][2] | |
181 | ||
182 | if self.domain == "": | |
183 | self.domain = domain | |
184 | else: | |
185 | if self.domain != domain: | |
2d9c7df1 | 186 | print("Warning: different domain provided (%s,%s)" % (self.domain, domain)) |
b25c5b37 | 187 | |
0794b3f6 | 188 | |
2982a614 JR |
189 | verbose = False |
190 | ||
191 | usage = """ | |
b25c5b37 YB |
192 | lttng-gen-tp - Generate the LTTng-UST header and source based on a simple template |
193 | ||
194 | usage: lttng-gen-tp TEMPLATE_FILE [-o OUTPUT_FILE][-o OUTPUT_FILE] | |
195 | ||
196 | If no OUTPUT_FILE is given, the .h and .c file will be generated. | |
197 | (The basename of the template file with be used for the generated file. | |
db06a0a2 | 198 | for example sample.tp will generate sample.h, sample.c and sample.o) |
b25c5b37 | 199 | |
db06a0a2 | 200 | When using the -o option, the OUTPUT_FILE must end with either .h, .c or .o |
b25c5b37 YB |
201 | The -o option can be repeated multiple times. |
202 | ||
612e9ce4 | 203 | The template file must contains LTTNG_UST_TRACEPOINT_EVENT and LTTNG_UST_TRACEPOINT_LOGLEVEL |
b25c5b37 YB |
204 | as per defined in the lttng/tracepoint.h file. |
205 | See the lttng-ust(3) man page for more details on the format. | |
206 | """ | |
2982a614 JR |
207 | |
208 | ||
b25c5b37 YB |
209 | def main(argv=None): |
210 | if argv is None: | |
211 | argv = sys.argv | |
212 | ||
213 | try: | |
214 | try: | |
2982a614 | 215 | opts, args = getopt.gnu_getopt(argv[1:], "ho:av", ["help", "verbose"]) |
2d9c7df1 | 216 | except getopt.error as msg: |
2982a614 | 217 | raise Usage(msg) |
b25c5b37 | 218 | |
2d9c7df1 ZT |
219 | except Usage as err: |
220 | print(err.msg, file=sys.stderr) | |
221 | print("for help use --help", file=sys.stderr) | |
b25c5b37 YB |
222 | return 2 |
223 | ||
224 | outputNames = [] | |
225 | for o, a in opts: | |
226 | if o in ("-h", "--help"): | |
2d9c7df1 | 227 | print(usage) |
b25c5b37 | 228 | return(0) |
2982a614 | 229 | if o in ("-o", ""): |
b25c5b37 | 230 | outputNames.append(a) |
2982a614 | 231 | if o in ("-a", ""): |
b25c5b37 | 232 | all = True |
0794b3f6 YB |
233 | if o in ("-v", "--verbose"): |
234 | global verbose | |
235 | verbose = True | |
a719be64 CB |
236 | try: |
237 | if len(args) == 0: | |
238 | raise Usage("No template file given") | |
239 | ||
2d9c7df1 ZT |
240 | except Usage as err: |
241 | print(err.msg, file=sys.stderr) | |
242 | print("for help use --help", file=sys.stderr) | |
a719be64 | 243 | return 2 |
b25c5b37 YB |
244 | |
245 | doCFile = None | |
246 | doHeader = None | |
db06a0a2 | 247 | doObj = None |
b25c5b37 YB |
248 | headerFilename = None |
249 | cFilename = None | |
db06a0a2 | 250 | objFilename = None |
b25c5b37 YB |
251 | |
252 | if len(outputNames) > 0: | |
253 | if len(args) > 1: | |
2d9c7df1 | 254 | print("Cannot process more than one input if you specify an output") |
b25c5b37 YB |
255 | return(3) |
256 | ||
257 | for outputName in outputNames: | |
258 | if outputName[-2:] == ".h": | |
259 | doHeader = True | |
260 | headerFilename = outputName | |
261 | elif outputName[-2:] == ".c": | |
262 | doCFile = True | |
263 | cFilename = outputName | |
264 | elif outputName[-2:] == ".o": | |
db06a0a2 YB |
265 | doObj = True |
266 | objFilename = outputName | |
b25c5b37 | 267 | else: |
2d9c7df1 | 268 | print("output file type unsupported") |
b25c5b37 YB |
269 | return(4) |
270 | else: | |
271 | doHeader = True | |
272 | doCFile = True | |
db06a0a2 | 273 | doObj = True |
b25c5b37 YB |
274 | |
275 | # process arguments | |
276 | for arg in args: | |
7cd5a840 | 277 | if arg[-3:] != ".tp": |
2d9c7df1 | 278 | print(arg + " does not end in .tp. Skipping.") |
7cd5a840 | 279 | continue |
b25c5b37 | 280 | |
44745fc1 YB |
281 | tpl = None |
282 | try: | |
283 | tpl = TemplateFile(arg) | |
284 | except IOError as args: | |
2d9c7df1 | 285 | print("Cannot read input file " + args.filename + " " + args.strerror) |
44745fc1 YB |
286 | return -1 |
287 | try: | |
288 | if doHeader: | |
289 | if headerFilename: | |
290 | curFilename = headerFilename | |
291 | else: | |
2982a614 | 292 | curFilename = re.sub("\.tp$", ".h", arg) |
44745fc1 YB |
293 | doth = HeaderFile(curFilename, tpl) |
294 | doth.write() | |
295 | if doCFile: | |
296 | if cFilename: | |
297 | curFilename = cFilename | |
298 | else: | |
2982a614 | 299 | curFilename = re.sub("\.tp$", ".c", arg) |
44745fc1 YB |
300 | dotc = CFile(curFilename, tpl) |
301 | dotc.write() | |
302 | if doObj: | |
303 | if objFilename: | |
304 | curFilename = objFilename | |
305 | else: | |
2982a614 | 306 | curFilename = re.sub("\.tp$", ".o", arg) |
44745fc1 YB |
307 | dotobj = ObjFile(curFilename, tpl) |
308 | dotobj.write() | |
309 | except IOError as args: | |
2d9c7df1 | 310 | print("Cannot write output file " + args.filename + " " + args.strerror) |
44745fc1 | 311 | return -1 |
2d9c7df1 | 312 | |
2982a614 | 313 | |
b25c5b37 YB |
314 | if __name__ == "__main__": |
315 | sys.exit(main()) |