1 # The MIT License (MIT)
3 # Copyright (c) 2014-2020 Philippe Proulx <pproulx@efficios.com>
5 # Permission is hereby granted, free of charge, to any person obtaining
6 # a copy of this software and associated documentation files (the
7 # "Software"), to deal in the Software without restriction, including
8 # without limitation the rights to use, copy, modify, merge, publish,
9 # distribute, sublicense, and/or sell copies of the Software, and to
10 # permit persons to whom the Software is furnished to do so, subject to
11 # the following conditions:
13 # The above copyright notice and this permission notice shall be
14 # included in all copies or substantial portions of the Software.
16 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17 # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18 # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
19 # IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
20 # CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
21 # TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
22 # SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
29 import barectf
.config_parse_common
as barectf_config_parse_common
30 import barectf
.argpar
as barectf_argpar
35 # Colors and prints the error message `msg` and exits with status code
37 def _print_error(msg
):
38 termcolor
.cprint('Error: ', 'red', end
='', file=sys
.stderr
)
39 termcolor
.cprint(msg
, 'red', attrs
=['bold'], file=sys
.stderr
)
43 # Pretty-prints the barectf configuration error `exc` and exits with
45 def _print_config_error(exc
):
46 # reverse: most precise message comes last
47 for ctx
in reversed(exc
.context
):
50 if ctx
.message
is not None:
51 msg
= f
' {ctx.message}'
54 termcolor
.cprint(f
'{ctx.name}', color
, attrs
=['bold'], file=sys
.stderr
, end
='')
55 termcolor
.cprint(':', color
, file=sys
.stderr
, end
='')
56 termcolor
.cprint(msg
, color
, file=sys
.stderr
)
61 # Pretty-prints the unknown exception `exc`.
62 def _print_unknown_exc(exc
):
66 _print_error(f
'Unknown exception: {exc}')
69 # Finds and returns all the option items in `items` having the long name
71 def _find_opt_items(items
, long_name
):
75 if type(item
) is barectf_argpar
._OptItem
and item
.descr
.long_name
== long_name
:
76 ret_items
.append(item
)
83 # For an option item without an argument:
86 # For an option item with an argument:
89 # Uses the last option item having the long name `long_name` found in
92 # Returns `default` if there's no such option item.
93 def _opt_item_val(items
, long_name
, default
=None):
94 opt_items
= _find_opt_items(items
, long_name
)
96 if len(opt_items
) == 0:
99 opt_item
= opt_items
[-1]
101 if opt_item
.descr
.has_arg
:
102 return opt_item
.arg_text
111 class _CliGenCmdCfg(_CliCfg
):
112 def __init__(self
, config_file_path
, c_source_dir
, c_header_dir
, metadata_stream_dir
,
113 inclusion_dirs
, ignore_inclusion_not_found
, dump_config
, v2_prefix
):
114 self
._config
_file
_path
= config_file_path
115 self
._c
_source
_dir
= c_source_dir
116 self
._c
_header
_dir
= c_header_dir
117 self
._metadata
_stream
_dir
= metadata_stream_dir
118 self
._inclusion
_dirs
= inclusion_dirs
119 self
._ignore
_inclusion
_not
_found
= ignore_inclusion_not_found
120 self
._dump
_config
= dump_config
121 self
._v
2_prefix
= v2_prefix
124 def config_file_path(self
):
125 return self
._config
_file
_path
128 def c_source_dir(self
):
129 return self
._c
_source
_dir
132 def c_header_dir(self
):
133 return self
._c
_header
_dir
136 def metadata_stream_dir(self
):
137 return self
._metadata
_stream
_dir
140 def inclusion_dirs(self
):
141 return self
._inclusion
_dirs
144 def ignore_inclusion_not_found(self
):
145 return self
._ignore
_inclusion
_not
_found
148 def dump_config(self
):
149 return self
._dump
_config
153 return self
._v
2_prefix
156 def _print_gen_cmd_usage():
157 print('''Usage: barectf generate [--code-dir=DIR] [--headers-dir=DIR]
158 [--metadata-dir=DIR] [--prefix=PREFIX]
159 [--include-dir=DIR]... [--ignore-include-not-found]
160 [--dump-config] CONFIG-FILE-PATH
163 -c DIR, --code-dir=DIR Write C source files to DIR
164 --dump-config Print the effective configuration file
165 -H DIR, --headers-dir=DIR Write C header files to DIR
166 --ignore-include-not-found Continue to process the configuration file when
167 included files are not found
168 -I DIR, --include-dir=DIR Add DIR to the list of directories to be
169 searched for inclusion files
170 -m DIR, --metadata-dir=DIR Write the metadata stream file to DIR
171 -p PREFIX, --prefix=PREFIX Set the configuration prefix to PREFIX''')
174 class _CliError(Exception):
178 def _cli_gen_cfg_from_args(orig_args
):
179 # parse original arguments
181 barectf_argpar
.OptDescr('h', 'help'),
182 barectf_argpar
.OptDescr('c', 'code-dir', True),
183 barectf_argpar
.OptDescr('H', 'headers-dir', True),
184 barectf_argpar
.OptDescr('I', 'include-dir', True),
185 barectf_argpar
.OptDescr('m', 'metadata-dir', True),
186 barectf_argpar
.OptDescr('p', 'prefix', True),
187 barectf_argpar
.OptDescr(long_name
='dump-config'),
188 barectf_argpar
.OptDescr(long_name
='ignore-include-not-found'),
190 res
= barectf_argpar
.parse(orig_args
, opt_descrs
)
191 assert len(res
.ingested_orig_args
) == len(orig_args
)
194 if len(_find_opt_items(res
.items
, 'help')) > 0:
195 _print_gen_cmd_usage()
198 # check configuration file path
199 config_file_path
= None
201 for item
in res
.items
:
202 if type(item
) is barectf_argpar
._NonOptItem
:
203 if config_file_path
is not None:
204 raise _CliError('Multiple configuration file paths provided')
206 config_file_path
= item
.text
208 if config_file_path
is None:
209 raise _CliError('Missing configuration file path')
211 if not os
.path
.isfile(config_file_path
):
212 raise _CliError(f
'`{config_file_path}` is not an existing, regular file')
215 c_source_dir
= _opt_item_val(res
.items
, 'code-dir', os
.getcwd())
216 c_header_dir
= _opt_item_val(res
.items
, 'headers-dir', os
.getcwd())
217 metadata_stream_dir
= _opt_item_val(res
.items
, 'metadata-dir', os
.getcwd())
218 inclusion_dirs
= [item
.arg_text
for item
in _find_opt_items(res
.items
, 'include-dir')]
220 for dir in [c_source_dir
, c_header_dir
, metadata_stream_dir
] + inclusion_dirs
:
221 if not os
.path
.isdir(dir):
222 raise _CliError(f
'`{dir}` is not an existing directory')
224 inclusion_dirs
.append(os
.getcwd())
227 ignore_inclusion_not_found
= _opt_item_val(res
.items
, 'ignore-include-not-found', False)
228 dump_config
= _opt_item_val(res
.items
, 'dump-config', False)
229 v2_prefix
= _opt_item_val(res
.items
, 'prefix')
231 return _CliGenCmdCfg(config_file_path
, c_source_dir
, c_header_dir
, metadata_stream_dir
,
232 inclusion_dirs
, ignore_inclusion_not_found
, dump_config
, v2_prefix
)
235 def _print_general_usage():
236 print('''Usage: barectf COMMAND COMMAND-ARGS
241 -h, --help Show this help and quit
242 -V, --version Show version and quit
245 gen, generate Generate the C source and CTF metadata files of a tracer
246 from a configuration file
248 Run `barectf COMMAND --help` to show the help of COMMAND.''')
251 def _cli_cfg_from_args():
252 # We use our `argpar` module here instead of Python's `argparse`
253 # because we need to support the two following use cases:
255 # $ barectf config.yaml
256 # $ barectf generate config.yaml
258 # In other words, the default command is `generate` (for backward
259 # compatibility reasons). The argument parser must not consider
260 # `config.yaml` as being a command name.
261 general_opt_descrs
= [
262 barectf_argpar
.OptDescr('V', 'version'),
263 barectf_argpar
.OptDescr('h', 'help'),
265 orig_args
= sys
.argv
[1:]
266 res
= barectf_argpar
.parse(orig_args
, general_opt_descrs
, False)
268 # find command name, collecting preceding (common) option items
269 general_opt_items
= []
270 cmd_first_orig_arg_index
= None
273 for item
in res
.items
:
274 if type(item
) is barectf_argpar
._NonOptItem
:
275 if item
.text
in ['gen', 'generate']:
276 cmd_name
= 'generate'
277 cmd_first_orig_arg_index
= item
.orig_arg_index
+ 1
279 cmd_first_orig_arg_index
= item
.orig_arg_index
283 assert type(item
) is barectf_argpar
._OptItem
284 general_opt_items
.append(item
)
287 if len(_find_opt_items(general_opt_items
, 'help')) > 0:
288 _print_general_usage()
292 if len(_find_opt_items(general_opt_items
, 'version')) > 0:
293 print(f
'barectf {barectf.__version__}')
297 cmd_orig_args
= orig_args
[cmd_first_orig_arg_index
:]
300 # default `generate` command
301 return _cli_gen_cfg_from_args(cmd_orig_args
)
303 assert cmd_name
== 'generate'
304 return _cli_gen_cfg_from_args(cmd_orig_args
)
310 cli_cfg
= _cli_cfg_from_args()
311 except barectf_argpar
._Error
as exc
:
312 _print_error(f
'Command-line: For argument `{exc.orig_arg}`: {exc.msg}')
313 except _CliError
as exc
:
314 _print_error(f
'Command-line: {exc}')
316 assert type(cli_cfg
) is _CliGenCmdCfg
318 # create configuration
320 with
open(cli_cfg
.config_file_path
) as f
:
321 if cli_cfg
.dump_config
:
322 # print effective configuration file
323 print(barectf
.effective_configuration_file(f
, True, cli_cfg
.inclusion_dirs
,
324 cli_cfg
.ignore_inclusion_not_found
))
326 # barectf.configuration_from_file() reads the file again
330 config
= barectf
.configuration_from_file(f
, True, cli_cfg
.inclusion_dirs
,
331 cli_cfg
.ignore_inclusion_not_found
)
332 except barectf
._ConfigurationParseError
as exc
:
333 _print_config_error(exc
)
334 except Exception as exc
:
335 _print_unknown_exc(exc
)
337 if cli_cfg
.v2_prefix
is not None:
340 # For historical reasons, the `--prefix` option applies the
341 # barectf 2 configuration prefix rules. Therefore, get the
342 # equivalent barectf 3 prefixes first.
343 v3_prefixes
= barectf_config_parse_common
._v
3_prefixes
_from
_v
2_prefix
(cli_cfg
.v2_prefix
)
344 cg_opts
= config
.options
.code_generation_options
345 cg_opts
= barectf
.ConfigurationCodeGenerationOptions(v3_prefixes
.identifier
,
346 v3_prefixes
.file_name
,
347 cg_opts
.default_stream_type
,
348 cg_opts
.header_options
,
349 cg_opts
.clock_type_c_types
)
350 config
= barectf
.Configuration(config
.trace
, barectf
.ConfigurationOptions(cg_opts
))
352 # create a barectf code generator
353 code_gen
= barectf
.CodeGenerator(config
)
355 def write_file(dir, file):
356 with
open(os
.path
.join(dir, file.name
), 'w') as f
:
357 f
.write(file.contents
)
359 def write_files(dir, files
):
361 write_file(dir, file)
364 # generate and write metadata stream file
365 write_file(cli_cfg
.metadata_stream_dir
, code_gen
.generate_metadata_stream())
367 # generate and write C header files
368 write_files(cli_cfg
.c_header_dir
, code_gen
.generate_c_headers())
370 # generate and write C source files
371 write_files(cli_cfg
.c_source_dir
, code_gen
.generate_c_sources())
372 except Exception as exc
:
373 # We know `config` is valid, therefore the code generator cannot
374 # fail for a reason known to barectf.
375 _print_unknown_exc(exc
)