Introduce new barectf configuration API and YAML configuration schema
[deliverable/barectf.git] / barectf / cli.py
1 # The MIT License (MIT)
2 #
3 # Copyright (c) 2014-2020 Philippe Proulx <pproulx@efficios.com>
4 #
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:
12 #
13 # The above copyright notice and this permission notice shall be
14 # included in all copies or substantial portions of the Software.
15 #
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.
23
24 import pkg_resources
25 import termcolor
26 import argparse
27 import os.path
28 import barectf
29 import barectf.config_parse_common as barectf_config_parse_common
30 import sys
31 import os
32
33
34 # Colors and prints the error message `msg` and exits with status code
35 # 1.
36 def _print_error(msg):
37 termcolor.cprint('Error: ', 'red', end='', file=sys.stderr)
38 termcolor.cprint(msg, 'red', attrs=['bold'], file=sys.stderr)
39 sys.exit(1)
40
41
42 # Pretty-prints the barectf configuration error `exc` and exits with
43 # status code 1.
44 def _print_config_error(exc):
45 # reverse: most precise message comes last
46 for ctx in reversed(exc.context):
47 msg = ''
48
49 if ctx.message is not None:
50 msg = f' {ctx.message}'
51
52 color = 'red'
53 termcolor.cprint(f'{ctx.name}', color, attrs=['bold'], file=sys.stderr, end='')
54 termcolor.cprint(':', color, file=sys.stderr, end='')
55 termcolor.cprint(msg, color, file=sys.stderr)
56
57 sys.exit(1)
58
59
60 # Pretty-prints the unknown exception `exc`.
61 def _print_unknown_exc(exc):
62 import traceback
63
64 traceback.print_exc()
65 _print_error(f'Unknown exception: {exc}')
66
67
68 def _parse_args():
69 ap = argparse.ArgumentParser()
70
71 ap.add_argument('-c', '--code-dir', metavar='DIR', action='store', default=os.getcwd(),
72 help='output directory of C source file')
73 ap.add_argument('--dump-config', action='store_true',
74 help='also dump the effective YAML configuration file used for generation')
75 ap.add_argument('-H', '--headers-dir', metavar='DIR', action='store', default=os.getcwd(),
76 help='output directory of C header files')
77 ap.add_argument('-I', '--include-dir', metavar='DIR', action='append', default=[],
78 help='add directory DIR to the list of directories to be searched for include files')
79 ap.add_argument('--ignore-include-not-found', action='store_true',
80 help='continue to process the configuration file when included files are not found')
81 ap.add_argument('-m', '--metadata-dir', metavar='DIR', action='store', default=os.getcwd(),
82 help='output directory of CTF metadata')
83 ap.add_argument('-p', '--prefix', metavar='PREFIX', action='store',
84 help='override configuration\'s prefixes')
85 ap.add_argument('-V', '--version', action='version',
86 version='%(prog)s {}'.format(barectf.__version__))
87 ap.add_argument('config', metavar='CONFIG', action='store',
88 help='barectf YAML configuration file')
89
90 # parse args
91 args = ap.parse_args()
92
93 # validate output directories
94 for dir in [args.code_dir, args.headers_dir, args.metadata_dir] + args.include_dir:
95 if not os.path.isdir(dir):
96 _print_error(f'`{dir}` is not an existing directory')
97
98 # validate that configuration file exists
99 if not os.path.isfile(args.config):
100 _print_error(f'`{args.config}` is not an existing, regular file')
101
102 # Load configuration file to get its major version in order to
103 # append the correct implicit inclusion directory.
104 try:
105 with open(args.config) as f:
106 config_major_version = barectf.configuration_file_major_version(f)
107 except barectf._ConfigurationParseError as exc:
108 _print_config_error(exc)
109 except Exception as exc:
110 _print_unknown_exc(exc)
111
112 # append current working directory and implicit inclusion directory
113 args.include_dir += [
114 os.getcwd(),
115 pkg_resources.resource_filename(__name__, f'include/{config_major_version}')
116 ]
117
118 return args
119
120
121 def run():
122 # parse arguments
123 args = _parse_args()
124
125 # create configuration
126 try:
127 with open(args.config) as f:
128 if args.dump_config:
129 # print effective configuration file
130 print(barectf.effective_configuration_file(f, args.include_dir,
131 args.ignore_include_not_found))
132
133 # barectf.configuration_from_file() reads the file again
134 # below: rewind.
135 f.seek(0)
136
137 config = barectf.configuration_from_file(f, args.include_dir,
138 args.ignore_include_not_found)
139 except barectf._ConfigurationParseError as exc:
140 _print_config_error(exc)
141 except Exception as exc:
142 _print_unknown_exc(exc)
143
144 if args.prefix:
145 # Override prefixes.
146 #
147 # For historical reasons, the `--prefix` option applies the
148 # barectf 2 configuration prefix rules. Therefore, get the
149 # equivalent barectf 3 prefixes first.
150 v3_prefixes = barectf_config_parse_common._v3_prefixes_from_v2_prefix(args.prefix)
151 cg_opts = config.options.code_generation_options
152 cg_opts = barectf.ConfigurationCodeGenerationOptions(v3_prefixes.identifier,
153 v3_prefixes.file_name,
154 cg_opts.default_stream_type,
155 cg_opts.header_options,
156 cg_opts.clock_type_c_types)
157 config = barectf.Configuration(config.trace, barectf.ConfigurationOptions(cg_opts))
158
159 # create a barectf code generator
160 code_gen = barectf.CodeGenerator(config)
161
162 def write_file(dir, file):
163 with open(os.path.join(dir, file.name), 'w') as f:
164 f.write(file.contents)
165
166 def write_files(dir, files):
167 for file in files:
168 write_file(dir, file)
169
170 try:
171 # generate and write metadata stream file
172 write_file(args.metadata_dir, code_gen.generate_metadata_stream())
173
174 # generate and write C header files
175 write_files(args.headers_dir, code_gen.generate_c_headers())
176
177 # generate and write C source files
178 write_files(args.code_dir, code_gen.generate_c_sources())
179 except Exception as exc:
180 # We know `config` is valid, therefore the code generator cannot
181 # fail for a reason known to barectf.
182 _print_unknown_exc(exc)
This page took 0.035608 seconds and 5 git commands to generate.