powertrain-build 1.13.1__py3-none-any.whl → 1.13.3.dev3__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- powertrain_build/__init__.py +40 -40
- powertrain_build/__main__.py +6 -6
- powertrain_build/a2l.py +582 -582
- powertrain_build/a2l_merge.py +650 -650
- powertrain_build/a2l_templates.py +717 -717
- powertrain_build/build.py +985 -985
- powertrain_build/build_defs.py +309 -309
- powertrain_build/build_proj_config.py +690 -690
- powertrain_build/check_interface.py +575 -575
- powertrain_build/cli.py +141 -141
- powertrain_build/config.py +542 -542
- powertrain_build/core.py +395 -395
- powertrain_build/core_dummy.py +343 -343
- powertrain_build/create_conversion_table.py +73 -73
- powertrain_build/dids.py +916 -916
- powertrain_build/dummy.py +157 -157
- powertrain_build/dummy_spm.py +252 -252
- powertrain_build/environmentcheck.py +52 -52
- powertrain_build/ext_dbg.py +255 -255
- powertrain_build/ext_var.py +327 -327
- powertrain_build/feature_configs.py +301 -301
- powertrain_build/gen_allsysteminfo.py +227 -227
- powertrain_build/gen_label_split.py +449 -449
- powertrain_build/handcode_replacer.py +124 -124
- powertrain_build/html_report.py +133 -133
- powertrain_build/interface/__init__.py +4 -4
- powertrain_build/interface/application.py +511 -511
- powertrain_build/interface/base.py +500 -500
- powertrain_build/interface/csp_api.py +490 -490
- powertrain_build/interface/device_proxy.py +677 -677
- powertrain_build/interface/ems.py +67 -67
- powertrain_build/interface/export_global_vars.py +121 -121
- powertrain_build/interface/generate_adapters.py +132 -132
- powertrain_build/interface/generate_hi_interface.py +87 -87
- powertrain_build/interface/generate_service.py +69 -69
- powertrain_build/interface/generate_wrappers.py +147 -147
- powertrain_build/interface/generation_utils.py +142 -142
- powertrain_build/interface/hal.py +194 -194
- powertrain_build/interface/model_yaml_verification.py +348 -348
- powertrain_build/interface/service.py +296 -296
- powertrain_build/interface/simulink.py +249 -249
- powertrain_build/interface/update_call_sources.py +180 -180
- powertrain_build/interface/update_model_yaml.py +186 -186
- powertrain_build/interface/zone_controller.py +362 -362
- powertrain_build/lib/__init__.py +4 -4
- powertrain_build/lib/helper_functions.py +127 -127
- powertrain_build/lib/logger.py +55 -55
- powertrain_build/matlab_scripts/CodeGen/BuildAutomationPyBuild.m +78 -78
- powertrain_build/matlab_scripts/CodeGen/Generate_A2L.m +154 -154
- powertrain_build/matlab_scripts/CodeGen/generateTLUnit.m +239 -239
- powertrain_build/matlab_scripts/CodeGen/getAsilClassification.m +28 -28
- powertrain_build/matlab_scripts/CodeGen/modelConfiguredForTL.m +28 -28
- powertrain_build/matlab_scripts/CodeGen/moveDefOutports.m +88 -88
- powertrain_build/matlab_scripts/CodeGen/parseCalMeasData.m +410 -410
- powertrain_build/matlab_scripts/CodeGen/parseCoreIdentifiers.m +139 -139
- powertrain_build/matlab_scripts/CodeGen/parseDIDs.m +141 -141
- powertrain_build/matlab_scripts/CodeGen/parseInPorts.m +106 -106
- powertrain_build/matlab_scripts/CodeGen/parseIncludeConfigs.m +25 -25
- powertrain_build/matlab_scripts/CodeGen/parseModelInfo.m +38 -38
- powertrain_build/matlab_scripts/CodeGen/parseNVM.m +81 -81
- powertrain_build/matlab_scripts/CodeGen/parseOutPorts.m +120 -120
- powertrain_build/matlab_scripts/CodeGen/parsePreProcBlks.m +23 -23
- powertrain_build/matlab_scripts/CodeGen/struct2JSON.m +128 -128
- powertrain_build/matlab_scripts/CodeGen/updateCodeSwConfig.m +31 -31
- powertrain_build/matlab_scripts/Init_PyBuild.m +91 -91
- powertrain_build/matlab_scripts/__init__.py +2 -2
- powertrain_build/matlab_scripts/helperFunctions/Get_Full_Name.m +46 -46
- powertrain_build/matlab_scripts/helperFunctions/Get_SrcLines.m +12 -12
- powertrain_build/matlab_scripts/helperFunctions/Init_Models.m +78 -78
- powertrain_build/matlab_scripts/helperFunctions/Init_Projects.m +67 -67
- powertrain_build/matlab_scripts/helperFunctions/Read_Units.m +34 -34
- powertrain_build/matlab_scripts/helperFunctions/SetProjectTimeSamples.m +26 -26
- powertrain_build/matlab_scripts/helperFunctions/Strip_Suffix.m +16 -16
- powertrain_build/matlab_scripts/helperFunctions/followLink.m +118 -118
- powertrain_build/matlab_scripts/helperFunctions/getCodeSwitches.m +50 -50
- powertrain_build/matlab_scripts/helperFunctions/getConsumerBlocks.m +30 -30
- powertrain_build/matlab_scripts/helperFunctions/getDefBlock.m +39 -39
- powertrain_build/matlab_scripts/helperFunctions/getDefOutport.m +58 -58
- powertrain_build/matlab_scripts/helperFunctions/getDstBlocks.m +19 -19
- powertrain_build/matlab_scripts/helperFunctions/getDstLines.m +13 -13
- powertrain_build/matlab_scripts/helperFunctions/getInterfaceSignals.m +37 -37
- powertrain_build/matlab_scripts/helperFunctions/getName.m +37 -37
- powertrain_build/matlab_scripts/helperFunctions/getPath.m +6 -6
- powertrain_build/matlab_scripts/helperFunctions/getProperValue.m +21 -21
- powertrain_build/matlab_scripts/helperFunctions/getSrcBlocks.m +19 -19
- powertrain_build/matlab_scripts/helperFunctions/getSrcLines.m +13 -13
- powertrain_build/matlab_scripts/helperFunctions/loadLibraries.m +10 -10
- powertrain_build/matlab_scripts/helperFunctions/loadjson.m +6 -6
- powertrain_build/matlab_scripts/helperFunctions/modifyEnumStructField.m +21 -21
- powertrain_build/matlab_scripts/helperFunctions/removeConfigDuplicates.m +31 -31
- powertrain_build/matlab_scripts/helperFunctions/sortSystemByClass.m +26 -26
- powertrain_build/matlab_scripts/helperFunctions/tl_getfast.m +89 -89
- powertrain_build/matlab_scripts/helperFunctions/topLevelSystem.m +20 -20
- powertrain_build/matlab_scripts/helperFunctions/updateModels.m +131 -131
- powertrain_build/memory_section.py +224 -224
- powertrain_build/nvm_def.py +729 -729
- powertrain_build/problem_logger.py +86 -86
- powertrain_build/pt_matlab.py +430 -430
- powertrain_build/pt_win32.py +144 -144
- powertrain_build/replace_compu_tab_ref.py +105 -105
- powertrain_build/rte_dummy.py +254 -254
- powertrain_build/sched_funcs.py +209 -207
- powertrain_build/signal.py +7 -7
- powertrain_build/signal_if_html_rep.py +221 -221
- powertrain_build/signal_if_html_rep_all.py +302 -302
- powertrain_build/signal_incons_html_rep.py +180 -180
- powertrain_build/signal_incons_html_rep_all.py +366 -366
- powertrain_build/signal_incons_html_rep_base.py +168 -168
- powertrain_build/signal_inconsistency_check.py +641 -641
- powertrain_build/signal_interfaces.py +864 -864
- powertrain_build/templates/Index_SigCheck_All.html +22 -22
- powertrain_build/templates/Index_SigIf_All.html +19 -19
- powertrain_build/types.py +218 -218
- powertrain_build/unit_configs.py +419 -419
- powertrain_build/user_defined_types.py +660 -660
- powertrain_build/versioncheck.py +66 -66
- powertrain_build/wrapper.py +512 -512
- powertrain_build/xlrd_csv.py +87 -87
- powertrain_build/zone_controller/__init__.py +4 -4
- powertrain_build/zone_controller/calibration.py +176 -176
- powertrain_build/zone_controller/composition_yaml.py +880 -878
- {powertrain_build-1.13.1.dist-info → powertrain_build-1.13.3.dev3.dist-info}/METADATA +100 -100
- powertrain_build-1.13.3.dev3.dist-info/RECORD +130 -0
- {powertrain_build-1.13.1.dist-info → powertrain_build-1.13.3.dev3.dist-info}/WHEEL +1 -1
- {powertrain_build-1.13.1.dist-info → powertrain_build-1.13.3.dev3.dist-info}/licenses/LICENSE +202 -202
- powertrain_build-1.13.3.dev3.dist-info/pbr.json +1 -0
- powertrain_build-1.13.1.dist-info/RECORD +0 -130
- powertrain_build-1.13.1.dist-info/pbr.json +0 -1
- {powertrain_build-1.13.1.dist-info → powertrain_build-1.13.3.dev3.dist-info}/entry_points.txt +0 -0
- {powertrain_build-1.13.1.dist-info → powertrain_build-1.13.3.dev3.dist-info}/licenses/AUTHORS +0 -0
- {powertrain_build-1.13.1.dist-info → powertrain_build-1.13.3.dev3.dist-info}/licenses/NOTICE +0 -0
- {powertrain_build-1.13.1.dist-info → powertrain_build-1.13.3.dev3.dist-info}/top_level.txt +0 -0
powertrain_build/config.py
CHANGED
|
@@ -1,542 +1,542 @@
|
|
|
1
|
-
# Copyright 2024 Volvo Car Corporation
|
|
2
|
-
# Licensed under Apache 2.0.
|
|
3
|
-
|
|
4
|
-
"""Script to update configs based on c-files."""
|
|
5
|
-
import argparse
|
|
6
|
-
import copy
|
|
7
|
-
import glob
|
|
8
|
-
import itertools
|
|
9
|
-
import json
|
|
10
|
-
import operator
|
|
11
|
-
import os
|
|
12
|
-
import re
|
|
13
|
-
import sys
|
|
14
|
-
from pprint import pformat
|
|
15
|
-
from typing import List, Optional
|
|
16
|
-
|
|
17
|
-
from powertrain_build.lib import logger
|
|
18
|
-
|
|
19
|
-
LOGGER = logger.create_logger('config')
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
class ConfigParserCommon:
|
|
23
|
-
"""Parser for c and h files."""
|
|
24
|
-
|
|
25
|
-
def __init__(self):
|
|
26
|
-
"""Initialize common properties."""
|
|
27
|
-
self.ifs = []
|
|
28
|
-
self.def_map = {}
|
|
29
|
-
self.configs = {}
|
|
30
|
-
self.code_regexes = [(re.compile(r'^\s*#(?P<type>if|ifdef|ifndef) (?P<condition>.*)$'),
|
|
31
|
-
self.parse_if),
|
|
32
|
-
(re.compile(r'^\s*#else.*$'), self.parse_else),
|
|
33
|
-
(re.compile(r'^\s*#define (\w*)\s?(.*)?'), self.parse_defines),
|
|
34
|
-
(re.compile(r'^\s*#endif.*$'), self.parse_endif)]
|
|
35
|
-
|
|
36
|
-
def parse_line(self, line):
|
|
37
|
-
"""Process each regex.
|
|
38
|
-
|
|
39
|
-
Arguments:
|
|
40
|
-
line (str): line of code
|
|
41
|
-
"""
|
|
42
|
-
for regex, function in self.code_regexes:
|
|
43
|
-
self.process_regex(line, regex, function)
|
|
44
|
-
|
|
45
|
-
@staticmethod
|
|
46
|
-
def process_regex(line, regex, function):
|
|
47
|
-
"""Process one regex.
|
|
48
|
-
|
|
49
|
-
Arguments:
|
|
50
|
-
line (str): line of code
|
|
51
|
-
regex (object): compiled re object
|
|
52
|
-
function (function): function to run if regex matches
|
|
53
|
-
"""
|
|
54
|
-
match = regex.match(line)
|
|
55
|
-
if match:
|
|
56
|
-
function(*match.groups())
|
|
57
|
-
|
|
58
|
-
def parse_file_content(self, file_content):
|
|
59
|
-
"""Parse each line in the file.
|
|
60
|
-
|
|
61
|
-
Arguments:
|
|
62
|
-
file_contents (list): Contents of a file
|
|
63
|
-
"""
|
|
64
|
-
for line in file_content:
|
|
65
|
-
self.parse_line(line)
|
|
66
|
-
|
|
67
|
-
def parse_if(self, if_type, condition):
|
|
68
|
-
"""Parse an if-preprocessor statement.
|
|
69
|
-
|
|
70
|
-
Arguments:
|
|
71
|
-
match (object): match object
|
|
72
|
-
"""
|
|
73
|
-
self.ifs.append((if_type, condition))
|
|
74
|
-
|
|
75
|
-
def parse_else(self):
|
|
76
|
-
"""Stub for parsing."""
|
|
77
|
-
raise NotImplementedError
|
|
78
|
-
|
|
79
|
-
def parse_defines(self, variable, definition):
|
|
80
|
-
"""Stub for parsing."""
|
|
81
|
-
raise NotImplementedError
|
|
82
|
-
|
|
83
|
-
def parse_endif(self):
|
|
84
|
-
"""Parse an endif-preprocessor statement.
|
|
85
|
-
|
|
86
|
-
Arguments:
|
|
87
|
-
match (object): match object
|
|
88
|
-
"""
|
|
89
|
-
if self.ifs:
|
|
90
|
-
c_type, condition = self.ifs.pop()
|
|
91
|
-
LOGGER.debug('Removing %s %s', c_type, condition)
|
|
92
|
-
|
|
93
|
-
@staticmethod
|
|
94
|
-
def read_file(c_file):
|
|
95
|
-
"""Read file.
|
|
96
|
-
|
|
97
|
-
Arguments:
|
|
98
|
-
c_file (str): Full path to a file
|
|
99
|
-
"""
|
|
100
|
-
file_content = ''
|
|
101
|
-
with open(c_file, encoding='latin-1') as file_handle:
|
|
102
|
-
for line in file_handle:
|
|
103
|
-
file_content += line
|
|
104
|
-
out = re.sub(r'/\*.*?\*/', '', file_content, flags=re.S).splitlines()
|
|
105
|
-
return out
|
|
106
|
-
|
|
107
|
-
@staticmethod
|
|
108
|
-
def compose_and(conditions):
|
|
109
|
-
"""Return and conditions."""
|
|
110
|
-
return f"({' && '.join(conditions)})"
|
|
111
|
-
|
|
112
|
-
@staticmethod
|
|
113
|
-
def compose_or(conditions):
|
|
114
|
-
"""Return and conditions."""
|
|
115
|
-
return f"({' || '.join(conditions)})"
|
|
116
|
-
|
|
117
|
-
@staticmethod
|
|
118
|
-
def sort_u(item):
|
|
119
|
-
"""Get a unique list of configs.
|
|
120
|
-
|
|
121
|
-
Can handle unhashable elements.
|
|
122
|
-
|
|
123
|
-
Arguments:
|
|
124
|
-
item (list): list to unique elements of.
|
|
125
|
-
"""
|
|
126
|
-
return map(
|
|
127
|
-
operator.itemgetter(0),
|
|
128
|
-
itertools.groupby(sorted(item)))
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
class CConfigParser(ConfigParserCommon):
|
|
132
|
-
"""Parser for c-files."""
|
|
133
|
-
|
|
134
|
-
def set_regexes(self, variable):
|
|
135
|
-
"""Create regexes to find configs for a single variable.
|
|
136
|
-
|
|
137
|
-
Arguments:
|
|
138
|
-
variable (str): variable
|
|
139
|
-
"""
|
|
140
|
-
self.code_regexes.append((re.compile(r'.*\b({})\b.*'.format(variable)), self.parse_code))
|
|
141
|
-
|
|
142
|
-
def parse_defines(self, variable, definition):
|
|
143
|
-
"""Parse defines in c-files."""
|
|
144
|
-
if definition:
|
|
145
|
-
LOGGER.warning('Configuration using %s might be wrong. Set to %s in the c-file', variable, definition)
|
|
146
|
-
if variable:
|
|
147
|
-
if self.ifs and variable == self.ifs[-1][-1]:
|
|
148
|
-
self.ifs.pop()
|
|
149
|
-
|
|
150
|
-
def parse_else(self):
|
|
151
|
-
"""Parse defines in c-files."""
|
|
152
|
-
if_type, condition = self.ifs.pop()
|
|
153
|
-
self.ifs.append(('else' + if_type, condition))
|
|
154
|
-
|
|
155
|
-
def parse_code(self, variable):
|
|
156
|
-
"""Parse a line with the variable we are looking for.
|
|
157
|
-
|
|
158
|
-
Arguments:
|
|
159
|
-
match (object): match object
|
|
160
|
-
"""
|
|
161
|
-
LOGGER.debug('Found %s with %s', variable, self.ifs)
|
|
162
|
-
if variable not in self.configs:
|
|
163
|
-
self.configs[variable] = []
|
|
164
|
-
# In this case, we add to a list which should be joined by 'and'
|
|
165
|
-
self.configs[variable].append(copy.deepcopy(self.ifs))
|
|
166
|
-
|
|
167
|
-
@staticmethod
|
|
168
|
-
def define_config(condition, header_map, ctype):
|
|
169
|
-
"""Get a config from the header map.
|
|
170
|
-
|
|
171
|
-
Arguments:
|
|
172
|
-
condition
|
|
173
|
-
header_map
|
|
174
|
-
ctype
|
|
175
|
-
"""
|
|
176
|
-
if ctype == 'ifdef':
|
|
177
|
-
if condition in header_map.keys():
|
|
178
|
-
config = header_map[condition]
|
|
179
|
-
else:
|
|
180
|
-
config = 'ALWAYS_ACTIVE'
|
|
181
|
-
LOGGER.error('Define not found: %s %s in %s', ctype, condition, header_map)
|
|
182
|
-
if ctype == 'ifndef':
|
|
183
|
-
if condition in header_map.keys():
|
|
184
|
-
config = '!(' + header_map[condition] + ')'
|
|
185
|
-
else:
|
|
186
|
-
config = 'NEVER_ACTIVE'
|
|
187
|
-
LOGGER.error('Define not found: %s %s in %s', ctype, condition, header_map)
|
|
188
|
-
if ctype == 'elseifdef':
|
|
189
|
-
if condition in header_map.keys():
|
|
190
|
-
config = f'!({header_map[condition]})'
|
|
191
|
-
else:
|
|
192
|
-
config = 'NEVER_ACTIVE'
|
|
193
|
-
LOGGER.error('Define not found: %s %s in %s', ctype, condition, header_map)
|
|
194
|
-
if ctype == 'elseifndef':
|
|
195
|
-
if condition in header_map.keys():
|
|
196
|
-
config = header_map[condition]
|
|
197
|
-
else:
|
|
198
|
-
config = 'ALWAYS_ACTIVE'
|
|
199
|
-
LOGGER.error('Define not found: %s %s in %s', ctype, condition, header_map)
|
|
200
|
-
return config
|
|
201
|
-
|
|
202
|
-
def get_configs(self, variable, header_map):
|
|
203
|
-
"""Get configs.
|
|
204
|
-
|
|
205
|
-
Does not remove redundant configs.
|
|
206
|
-
"""
|
|
207
|
-
configs = []
|
|
208
|
-
if variable not in self.configs:
|
|
209
|
-
LOGGER.warning('%s not found. Inport that leads to terminal suspected.', variable)
|
|
210
|
-
return '(NEVER_ACTIVE)'
|
|
211
|
-
for config in self.configs[variable]:
|
|
212
|
-
tmp_config = []
|
|
213
|
-
for ctype, condition in config:
|
|
214
|
-
if ctype == 'if':
|
|
215
|
-
if condition in header_map.keys():
|
|
216
|
-
LOGGER.debug('Redefining %s as %s', condition, header_map[condition])
|
|
217
|
-
tmp_config.append(header_map[condition])
|
|
218
|
-
else:
|
|
219
|
-
tmp_config.append(condition)
|
|
220
|
-
elif ctype == 'elseif':
|
|
221
|
-
if condition in header_map.keys():
|
|
222
|
-
LOGGER.debug('Redefining %s as !(%s)', condition, header_map[condition])
|
|
223
|
-
tmp_config.append('!(' + header_map[condition] + ')')
|
|
224
|
-
else:
|
|
225
|
-
LOGGER.debug('Negating %s to !(%s)', condition, condition)
|
|
226
|
-
tmp_config.append('!(' + condition + ')')
|
|
227
|
-
else:
|
|
228
|
-
tmp_config.append(self.define_config(condition, header_map, ctype))
|
|
229
|
-
if not tmp_config and config:
|
|
230
|
-
LOGGER.warning('Config not found: %s from %s', config, self.configs)
|
|
231
|
-
tmp_config.append('ALWAYS_ACTIVE')
|
|
232
|
-
LOGGER.info('Current config: %s', tmp_config)
|
|
233
|
-
elif not config:
|
|
234
|
-
LOGGER.debug('No config, always active')
|
|
235
|
-
tmp_config.append('ALWAYS_ACTIVE')
|
|
236
|
-
configs.append(self.compose_and(list(self.sort_u(tmp_config))))
|
|
237
|
-
return self.compose_or(list(self.sort_u(configs)))
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
class JsonConfigHandler:
|
|
241
|
-
"""Handle the json config."""
|
|
242
|
-
|
|
243
|
-
def __init__(self, cparser, header_map):
|
|
244
|
-
"""Initialize handling of one json file.
|
|
245
|
-
|
|
246
|
-
Arguments:
|
|
247
|
-
parser (obj): c-parser
|
|
248
|
-
header_map (dict): defines in the header files
|
|
249
|
-
"""
|
|
250
|
-
self.cparser = cparser
|
|
251
|
-
self.header_map = header_map
|
|
252
|
-
|
|
253
|
-
def traverse_unit(self, struct, setup=True):
|
|
254
|
-
"""Go through a data structure and look for configs to update.
|
|
255
|
-
|
|
256
|
-
Arguments:
|
|
257
|
-
struct (dict): data to go through
|
|
258
|
-
parser (obj): parsing object
|
|
259
|
-
header_map (dict): dict of defines
|
|
260
|
-
setup (bool): Set up the parser obecjt (True) or replace configs (False)
|
|
261
|
-
"""
|
|
262
|
-
for name, data in struct.items():
|
|
263
|
-
if isinstance(data, dict) and name != 'API_blk':
|
|
264
|
-
# Core data has the propety config, not configs
|
|
265
|
-
if data.get('API_blk') is not None or data.get('configs') is not None:
|
|
266
|
-
if setup:
|
|
267
|
-
self.cparser.set_regexes(name)
|
|
268
|
-
else:
|
|
269
|
-
data['configs'] = self.cparser.get_configs(name, self.header_map)
|
|
270
|
-
else:
|
|
271
|
-
self.traverse_unit(data, setup)
|
|
272
|
-
|
|
273
|
-
def update_config(self, struct, c_code, header_map=None):
|
|
274
|
-
"""Update dict.
|
|
275
|
-
|
|
276
|
-
Arguments:
|
|
277
|
-
data (dict): A configuration dict or subdict
|
|
278
|
-
c_code (list): code part of a c-file
|
|
279
|
-
"""
|
|
280
|
-
if header_map is None:
|
|
281
|
-
header_map = {}
|
|
282
|
-
# Set up regexes:
|
|
283
|
-
self.traverse_unit(struct, setup=True)
|
|
284
|
-
self.cparser.parse_file_content(c_code)
|
|
285
|
-
self.traverse_unit(struct, setup=False)
|
|
286
|
-
|
|
287
|
-
@staticmethod
|
|
288
|
-
def read_config(config_file):
|
|
289
|
-
"""Read config file.
|
|
290
|
-
|
|
291
|
-
Arguments:
|
|
292
|
-
config_file (str): Full path to config file
|
|
293
|
-
"""
|
|
294
|
-
with open(config_file, encoding='latin-1') as unit_json:
|
|
295
|
-
unit_config = json.load(unit_json)
|
|
296
|
-
return unit_config
|
|
297
|
-
|
|
298
|
-
@staticmethod
|
|
299
|
-
def write_config(config_file, unit_config):
|
|
300
|
-
"""Write config file.
|
|
301
|
-
|
|
302
|
-
Arguments:
|
|
303
|
-
config_file (str): Full path to config file
|
|
304
|
-
unit_config (dict): Unit config to write to file
|
|
305
|
-
"""
|
|
306
|
-
with open(config_file, 'w', encoding="utf-8") as unit_json:
|
|
307
|
-
unit_json.write(json.dumps(unit_config, indent=2))
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
class HeaderConfigParser(ConfigParserCommon):
|
|
311
|
-
"""Parser for c-files."""
|
|
312
|
-
|
|
313
|
-
def set_defines(self, defines):
|
|
314
|
-
"""Set already defined defines."""
|
|
315
|
-
self.def_map = defines
|
|
316
|
-
|
|
317
|
-
def parse_else(self):
|
|
318
|
-
"""Crash if this is found in a header."""
|
|
319
|
-
raise NotImplementedError
|
|
320
|
-
|
|
321
|
-
def parse_defines(self, variable, definition):
|
|
322
|
-
"""Parse defines in c-files."""
|
|
323
|
-
if self.ifs and self.ifs[-1][0] == 'ifndef' and variable == self.ifs[-1][-1]:
|
|
324
|
-
# We have encountered a case of:
|
|
325
|
-
#
|
|
326
|
-
# #ifndef a
|
|
327
|
-
# #define a
|
|
328
|
-
# #define b
|
|
329
|
-
#
|
|
330
|
-
# Then we don't want b to be dependent on a not being defined.
|
|
331
|
-
|
|
332
|
-
c_type, condition = self.ifs.pop()
|
|
333
|
-
LOGGER.debug('Removing now defined %s from ifs: %s %s', variable, c_type, condition)
|
|
334
|
-
if definition:
|
|
335
|
-
LOGGER.info('Redefining %s as %s', variable, definition)
|
|
336
|
-
# Here we ignore the potential #if statements preceding this.
|
|
337
|
-
# Have not encountered a case where that matters.
|
|
338
|
-
# This structure does not support that logic.
|
|
339
|
-
# Potential for bugs.
|
|
340
|
-
self.configs[variable] = [definition]
|
|
341
|
-
elif self.ifs:
|
|
342
|
-
config = self.get_configs(self.ifs, self.def_map)
|
|
343
|
-
LOGGER.info('Defining %s as %s', variable, config)
|
|
344
|
-
if variable not in self.configs:
|
|
345
|
-
self.configs[variable] = []
|
|
346
|
-
self.configs[variable].append(copy.deepcopy(config))
|
|
347
|
-
self.def_map.update({variable: copy.deepcopy(config)})
|
|
348
|
-
|
|
349
|
-
@staticmethod
|
|
350
|
-
def define_config(condition, header_map, ctype):
|
|
351
|
-
"""Get a config from the header map.
|
|
352
|
-
|
|
353
|
-
Arguments:
|
|
354
|
-
condition
|
|
355
|
-
header_map
|
|
356
|
-
ctype
|
|
357
|
-
"""
|
|
358
|
-
if ctype == 'ifdef':
|
|
359
|
-
if condition in header_map.keys():
|
|
360
|
-
LOGGER.debug('returning %s as %s', condition, header_map[condition])
|
|
361
|
-
config = header_map[condition]
|
|
362
|
-
else:
|
|
363
|
-
config = 'ALWAYS_ACTIVE'
|
|
364
|
-
LOGGER.warning('Not Implemented Yet: %s %s', ctype, condition)
|
|
365
|
-
if ctype == 'ifndef':
|
|
366
|
-
if condition in header_map.keys():
|
|
367
|
-
LOGGER.debug('returning %s as %s', condition, header_map[condition])
|
|
368
|
-
config = '!(' + header_map[condition] + ')'
|
|
369
|
-
else:
|
|
370
|
-
config = 'ALWAYS_ACTIVE'
|
|
371
|
-
LOGGER.warning('Not Implemented Yet: %s %s', ctype, condition)
|
|
372
|
-
return config
|
|
373
|
-
|
|
374
|
-
def process_config(self, inconfigs, header_map):
|
|
375
|
-
"""Process configs."""
|
|
376
|
-
configs = []
|
|
377
|
-
for config in inconfigs:
|
|
378
|
-
LOGGER.debug('Current config: %s', config)
|
|
379
|
-
if isinstance(config, list):
|
|
380
|
-
configs.append(self.process_config(config, header_map))
|
|
381
|
-
else:
|
|
382
|
-
ctype, condition = config
|
|
383
|
-
if ctype == 'if':
|
|
384
|
-
if condition in header_map.keys():
|
|
385
|
-
configs.append(header_map[condition])
|
|
386
|
-
else:
|
|
387
|
-
configs.append(condition)
|
|
388
|
-
elif ctype in ['ifdef', 'ifndef']:
|
|
389
|
-
configs.append(self.define_config(condition, header_map, ctype))
|
|
390
|
-
else:
|
|
391
|
-
LOGGER.error('Not Implemented: %s', ctype)
|
|
392
|
-
if not configs:
|
|
393
|
-
configs = ['ALWAYS_ACTIVE']
|
|
394
|
-
return list(self.sort_u(configs))
|
|
395
|
-
|
|
396
|
-
def get_configs(self, configs, header_map):
|
|
397
|
-
"""Get configs.
|
|
398
|
-
|
|
399
|
-
Does not remove redundant configs.
|
|
400
|
-
"""
|
|
401
|
-
configs = self.process_config(configs, header_map)
|
|
402
|
-
if len(configs) > 1:
|
|
403
|
-
return self.compose_and(list(self.sort_u(configs)))
|
|
404
|
-
return configs[0]
|
|
405
|
-
|
|
406
|
-
def get_config(self):
|
|
407
|
-
"""Get the header map."""
|
|
408
|
-
header_map = self.def_map
|
|
409
|
-
for header_def, configs in self.configs.items():
|
|
410
|
-
header_map[header_def] = self.compose_or(configs)
|
|
411
|
-
return header_map
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
class ProcessHandler:
|
|
415
|
-
"""Class to collect functions for the process."""
|
|
416
|
-
|
|
417
|
-
PARSER_HELP = "Parse configs.json and c-files, to update code switch configs"
|
|
418
|
-
|
|
419
|
-
@staticmethod
|
|
420
|
-
def configure_parser(parser: argparse.ArgumentParser):
|
|
421
|
-
"""Parse arguments."""
|
|
422
|
-
parser.description = "Parse configs.json and c-files, to update code switch configs"
|
|
423
|
-
|
|
424
|
-
subparser = parser.add_subparsers(
|
|
425
|
-
title='Operation mode',
|
|
426
|
-
dest='mode',
|
|
427
|
-
help="Run chosen files on in a number of directories",
|
|
428
|
-
)
|
|
429
|
-
dir_parser = subparser.add_parser(
|
|
430
|
-
'models',
|
|
431
|
-
help="Run for one or multiple models. Script finds files generated from the model(s).")
|
|
432
|
-
dir_parser.add_argument('models', nargs='+',
|
|
433
|
-
help="Space separated list of model directories")
|
|
434
|
-
|
|
435
|
-
file_parser = subparser.add_parser('files',
|
|
436
|
-
help="Choose specific files. Mainly for manually written configs.")
|
|
437
|
-
file_parser.add_argument('c_file',
|
|
438
|
-
help="Full path to C-file")
|
|
439
|
-
file_parser.add_argument('config_file',
|
|
440
|
-
help="Full path to config file")
|
|
441
|
-
file_parser.add_argument('--aux_file',
|
|
442
|
-
help="Full path to tl_aux file. (Optional) ")
|
|
443
|
-
file_parser.add_argument('--local_file',
|
|
444
|
-
help="Full path to OPort file. (Optional) ")
|
|
445
|
-
|
|
446
|
-
parser.set_defaults(func=ProcessHandler.main)
|
|
447
|
-
|
|
448
|
-
@staticmethod
|
|
449
|
-
def get_files(model_path):
|
|
450
|
-
"""Get file paths from model path.
|
|
451
|
-
|
|
452
|
-
Arguments:
|
|
453
|
-
model_path (str): Path to a model (.mdl)
|
|
454
|
-
|
|
455
|
-
Returns:
|
|
456
|
-
local_file (str): Path to model_OPortMvd_LocalDefs.h
|
|
457
|
-
aux_file (str): Path to tl_aux_defines_model.h
|
|
458
|
-
config_file (str): Path to config_model.json
|
|
459
|
-
c_file (str): Path to model.c
|
|
460
|
-
|
|
461
|
-
"""
|
|
462
|
-
model_dir = os.path.dirname(model_path)
|
|
463
|
-
LOGGER.info('Processing %s', model_dir)
|
|
464
|
-
model_name = os.path.basename(model_dir)
|
|
465
|
-
local_file = os.path.join(model_dir, 'pybuild_src', f'{model_name}_OPortMvd_LocalDefs.h')
|
|
466
|
-
# aux_file does not contain the whole model-name if it is too long.
|
|
467
|
-
aux_file = os.path.join(model_dir, 'pybuild_src', f'tl_aux_defines_{model_name[2:12]}.h')
|
|
468
|
-
config_file = os.path.join(model_dir, 'pybuild_cfg', f'config_{model_name}.json')
|
|
469
|
-
clean_model_name = model_name.split('__')[0]
|
|
470
|
-
c_file = os.path.join(model_dir, 'pybuild_src', f'{clean_model_name}.c')
|
|
471
|
-
return local_file, aux_file, c_file, config_file
|
|
472
|
-
|
|
473
|
-
@staticmethod
|
|
474
|
-
def update_config_file(c_file, config_file, header_map):
|
|
475
|
-
"""Update one config file.
|
|
476
|
-
|
|
477
|
-
Arguments:
|
|
478
|
-
c_file (str): Full path to c-file
|
|
479
|
-
config_file (str): Full path to config.json
|
|
480
|
-
oport_file (str): Full path to OPortMvd_LocalDefs.h (Optional)
|
|
481
|
-
"""
|
|
482
|
-
LOGGER.info('Updating %s based on %s', config_file, c_file)
|
|
483
|
-
cparser = CConfigParser()
|
|
484
|
-
c_code = cparser.read_file(c_file)
|
|
485
|
-
json_handler = JsonConfigHandler(cparser, header_map)
|
|
486
|
-
unit_config = json_handler.read_config(config_file)
|
|
487
|
-
json_handler.update_config(unit_config, c_code, header_map)
|
|
488
|
-
json_handler.write_config(config_file, unit_config)
|
|
489
|
-
|
|
490
|
-
@staticmethod
|
|
491
|
-
def get_header_config(header_file, def_map):
|
|
492
|
-
"""Get header config.
|
|
493
|
-
|
|
494
|
-
Arguments:
|
|
495
|
-
c_file (str): Full path to c-file
|
|
496
|
-
config_file (str): Full path to config.json
|
|
497
|
-
oport_file (str): Full path to OPortMvd_LocalDefs.h (Optional)
|
|
498
|
-
"""
|
|
499
|
-
if header_file is None:
|
|
500
|
-
LOGGER.info('File not found: %s', header_file)
|
|
501
|
-
return def_map
|
|
502
|
-
if not os.path.isfile(header_file):
|
|
503
|
-
LOGGER.info('File not found: %s', header_file)
|
|
504
|
-
model_dir = os.path.dirname(header_file)
|
|
505
|
-
for tl_aux_file in glob.glob(os.path.join(model_dir, 'tl_aux*')):
|
|
506
|
-
LOGGER.warning('Looking for %s?', tl_aux_file)
|
|
507
|
-
return def_map
|
|
508
|
-
LOGGER.info('Parsing %s', header_file)
|
|
509
|
-
parser = HeaderConfigParser()
|
|
510
|
-
header_code = parser.read_file(header_file)
|
|
511
|
-
parser.set_defines(def_map)
|
|
512
|
-
parser.parse_file_content(header_code)
|
|
513
|
-
LOGGER.debug('Header configs: %s', pformat(parser.configs))
|
|
514
|
-
return parser.get_config()
|
|
515
|
-
|
|
516
|
-
@classmethod
|
|
517
|
-
def main(cls, args: argparse.Namespace):
|
|
518
|
-
"""Run the main function of the script."""
|
|
519
|
-
if args.mode == 'files':
|
|
520
|
-
LOGGER.info('Using manually supplied files %s', args)
|
|
521
|
-
local_defs = cls.get_header_config(args.local_file, {})
|
|
522
|
-
aux_defs = cls.get_header_config(args.aux_file, local_defs)
|
|
523
|
-
cls.update_config_file(args.c_file, args.config_file, aux_defs)
|
|
524
|
-
else:
|
|
525
|
-
for model in args.models:
|
|
526
|
-
local_file, aux_file, c_file, config_file = cls.get_files(model)
|
|
527
|
-
local_defs = cls.get_header_config(local_file, {})
|
|
528
|
-
aux_defs = cls.get_header_config(aux_file, local_defs)
|
|
529
|
-
if os.path.isfile(c_file) and os.path.isfile(config_file):
|
|
530
|
-
cls.update_config_file(c_file, config_file, aux_defs)
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
def main(argv: Optional[List[str]] = None):
|
|
534
|
-
"""Run main function."""
|
|
535
|
-
parser = argparse.ArgumentParser(ProcessHandler.PARSER_HELP)
|
|
536
|
-
ProcessHandler.configure_parser(parser)
|
|
537
|
-
args = parser.parse_args(argv)
|
|
538
|
-
return args.func(args)
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
if __name__ == "__main__":
|
|
542
|
-
main(sys.argv[1:])
|
|
1
|
+
# Copyright 2024 Volvo Car Corporation
|
|
2
|
+
# Licensed under Apache 2.0.
|
|
3
|
+
|
|
4
|
+
"""Script to update configs based on c-files."""
|
|
5
|
+
import argparse
|
|
6
|
+
import copy
|
|
7
|
+
import glob
|
|
8
|
+
import itertools
|
|
9
|
+
import json
|
|
10
|
+
import operator
|
|
11
|
+
import os
|
|
12
|
+
import re
|
|
13
|
+
import sys
|
|
14
|
+
from pprint import pformat
|
|
15
|
+
from typing import List, Optional
|
|
16
|
+
|
|
17
|
+
from powertrain_build.lib import logger
|
|
18
|
+
|
|
19
|
+
LOGGER = logger.create_logger('config')
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class ConfigParserCommon:
|
|
23
|
+
"""Parser for c and h files."""
|
|
24
|
+
|
|
25
|
+
def __init__(self):
|
|
26
|
+
"""Initialize common properties."""
|
|
27
|
+
self.ifs = []
|
|
28
|
+
self.def_map = {}
|
|
29
|
+
self.configs = {}
|
|
30
|
+
self.code_regexes = [(re.compile(r'^\s*#(?P<type>if|ifdef|ifndef) (?P<condition>.*)$'),
|
|
31
|
+
self.parse_if),
|
|
32
|
+
(re.compile(r'^\s*#else.*$'), self.parse_else),
|
|
33
|
+
(re.compile(r'^\s*#define (\w*)\s?(.*)?'), self.parse_defines),
|
|
34
|
+
(re.compile(r'^\s*#endif.*$'), self.parse_endif)]
|
|
35
|
+
|
|
36
|
+
def parse_line(self, line):
|
|
37
|
+
"""Process each regex.
|
|
38
|
+
|
|
39
|
+
Arguments:
|
|
40
|
+
line (str): line of code
|
|
41
|
+
"""
|
|
42
|
+
for regex, function in self.code_regexes:
|
|
43
|
+
self.process_regex(line, regex, function)
|
|
44
|
+
|
|
45
|
+
@staticmethod
|
|
46
|
+
def process_regex(line, regex, function):
|
|
47
|
+
"""Process one regex.
|
|
48
|
+
|
|
49
|
+
Arguments:
|
|
50
|
+
line (str): line of code
|
|
51
|
+
regex (object): compiled re object
|
|
52
|
+
function (function): function to run if regex matches
|
|
53
|
+
"""
|
|
54
|
+
match = regex.match(line)
|
|
55
|
+
if match:
|
|
56
|
+
function(*match.groups())
|
|
57
|
+
|
|
58
|
+
def parse_file_content(self, file_content):
|
|
59
|
+
"""Parse each line in the file.
|
|
60
|
+
|
|
61
|
+
Arguments:
|
|
62
|
+
file_contents (list): Contents of a file
|
|
63
|
+
"""
|
|
64
|
+
for line in file_content:
|
|
65
|
+
self.parse_line(line)
|
|
66
|
+
|
|
67
|
+
def parse_if(self, if_type, condition):
|
|
68
|
+
"""Parse an if-preprocessor statement.
|
|
69
|
+
|
|
70
|
+
Arguments:
|
|
71
|
+
match (object): match object
|
|
72
|
+
"""
|
|
73
|
+
self.ifs.append((if_type, condition))
|
|
74
|
+
|
|
75
|
+
def parse_else(self):
|
|
76
|
+
"""Stub for parsing."""
|
|
77
|
+
raise NotImplementedError
|
|
78
|
+
|
|
79
|
+
def parse_defines(self, variable, definition):
|
|
80
|
+
"""Stub for parsing."""
|
|
81
|
+
raise NotImplementedError
|
|
82
|
+
|
|
83
|
+
def parse_endif(self):
|
|
84
|
+
"""Parse an endif-preprocessor statement.
|
|
85
|
+
|
|
86
|
+
Arguments:
|
|
87
|
+
match (object): match object
|
|
88
|
+
"""
|
|
89
|
+
if self.ifs:
|
|
90
|
+
c_type, condition = self.ifs.pop()
|
|
91
|
+
LOGGER.debug('Removing %s %s', c_type, condition)
|
|
92
|
+
|
|
93
|
+
@staticmethod
|
|
94
|
+
def read_file(c_file):
|
|
95
|
+
"""Read file.
|
|
96
|
+
|
|
97
|
+
Arguments:
|
|
98
|
+
c_file (str): Full path to a file
|
|
99
|
+
"""
|
|
100
|
+
file_content = ''
|
|
101
|
+
with open(c_file, encoding='latin-1') as file_handle:
|
|
102
|
+
for line in file_handle:
|
|
103
|
+
file_content += line
|
|
104
|
+
out = re.sub(r'/\*.*?\*/', '', file_content, flags=re.S).splitlines()
|
|
105
|
+
return out
|
|
106
|
+
|
|
107
|
+
@staticmethod
|
|
108
|
+
def compose_and(conditions):
|
|
109
|
+
"""Return and conditions."""
|
|
110
|
+
return f"({' && '.join(conditions)})"
|
|
111
|
+
|
|
112
|
+
@staticmethod
|
|
113
|
+
def compose_or(conditions):
|
|
114
|
+
"""Return and conditions."""
|
|
115
|
+
return f"({' || '.join(conditions)})"
|
|
116
|
+
|
|
117
|
+
@staticmethod
|
|
118
|
+
def sort_u(item):
|
|
119
|
+
"""Get a unique list of configs.
|
|
120
|
+
|
|
121
|
+
Can handle unhashable elements.
|
|
122
|
+
|
|
123
|
+
Arguments:
|
|
124
|
+
item (list): list to unique elements of.
|
|
125
|
+
"""
|
|
126
|
+
return map(
|
|
127
|
+
operator.itemgetter(0),
|
|
128
|
+
itertools.groupby(sorted(item)))
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
class CConfigParser(ConfigParserCommon):
|
|
132
|
+
"""Parser for c-files."""
|
|
133
|
+
|
|
134
|
+
def set_regexes(self, variable):
|
|
135
|
+
"""Create regexes to find configs for a single variable.
|
|
136
|
+
|
|
137
|
+
Arguments:
|
|
138
|
+
variable (str): variable
|
|
139
|
+
"""
|
|
140
|
+
self.code_regexes.append((re.compile(r'.*\b({})\b.*'.format(variable)), self.parse_code))
|
|
141
|
+
|
|
142
|
+
def parse_defines(self, variable, definition):
|
|
143
|
+
"""Parse defines in c-files."""
|
|
144
|
+
if definition:
|
|
145
|
+
LOGGER.warning('Configuration using %s might be wrong. Set to %s in the c-file', variable, definition)
|
|
146
|
+
if variable:
|
|
147
|
+
if self.ifs and variable == self.ifs[-1][-1]:
|
|
148
|
+
self.ifs.pop()
|
|
149
|
+
|
|
150
|
+
def parse_else(self):
|
|
151
|
+
"""Parse defines in c-files."""
|
|
152
|
+
if_type, condition = self.ifs.pop()
|
|
153
|
+
self.ifs.append(('else' + if_type, condition))
|
|
154
|
+
|
|
155
|
+
def parse_code(self, variable):
|
|
156
|
+
"""Parse a line with the variable we are looking for.
|
|
157
|
+
|
|
158
|
+
Arguments:
|
|
159
|
+
match (object): match object
|
|
160
|
+
"""
|
|
161
|
+
LOGGER.debug('Found %s with %s', variable, self.ifs)
|
|
162
|
+
if variable not in self.configs:
|
|
163
|
+
self.configs[variable] = []
|
|
164
|
+
# In this case, we add to a list which should be joined by 'and'
|
|
165
|
+
self.configs[variable].append(copy.deepcopy(self.ifs))
|
|
166
|
+
|
|
167
|
+
@staticmethod
|
|
168
|
+
def define_config(condition, header_map, ctype):
|
|
169
|
+
"""Get a config from the header map.
|
|
170
|
+
|
|
171
|
+
Arguments:
|
|
172
|
+
condition
|
|
173
|
+
header_map
|
|
174
|
+
ctype
|
|
175
|
+
"""
|
|
176
|
+
if ctype == 'ifdef':
|
|
177
|
+
if condition in header_map.keys():
|
|
178
|
+
config = header_map[condition]
|
|
179
|
+
else:
|
|
180
|
+
config = 'ALWAYS_ACTIVE'
|
|
181
|
+
LOGGER.error('Define not found: %s %s in %s', ctype, condition, header_map)
|
|
182
|
+
if ctype == 'ifndef':
|
|
183
|
+
if condition in header_map.keys():
|
|
184
|
+
config = '!(' + header_map[condition] + ')'
|
|
185
|
+
else:
|
|
186
|
+
config = 'NEVER_ACTIVE'
|
|
187
|
+
LOGGER.error('Define not found: %s %s in %s', ctype, condition, header_map)
|
|
188
|
+
if ctype == 'elseifdef':
|
|
189
|
+
if condition in header_map.keys():
|
|
190
|
+
config = f'!({header_map[condition]})'
|
|
191
|
+
else:
|
|
192
|
+
config = 'NEVER_ACTIVE'
|
|
193
|
+
LOGGER.error('Define not found: %s %s in %s', ctype, condition, header_map)
|
|
194
|
+
if ctype == 'elseifndef':
|
|
195
|
+
if condition in header_map.keys():
|
|
196
|
+
config = header_map[condition]
|
|
197
|
+
else:
|
|
198
|
+
config = 'ALWAYS_ACTIVE'
|
|
199
|
+
LOGGER.error('Define not found: %s %s in %s', ctype, condition, header_map)
|
|
200
|
+
return config
|
|
201
|
+
|
|
202
|
+
def get_configs(self, variable, header_map):
|
|
203
|
+
"""Get configs.
|
|
204
|
+
|
|
205
|
+
Does not remove redundant configs.
|
|
206
|
+
"""
|
|
207
|
+
configs = []
|
|
208
|
+
if variable not in self.configs:
|
|
209
|
+
LOGGER.warning('%s not found. Inport that leads to terminal suspected.', variable)
|
|
210
|
+
return '(NEVER_ACTIVE)'
|
|
211
|
+
for config in self.configs[variable]:
|
|
212
|
+
tmp_config = []
|
|
213
|
+
for ctype, condition in config:
|
|
214
|
+
if ctype == 'if':
|
|
215
|
+
if condition in header_map.keys():
|
|
216
|
+
LOGGER.debug('Redefining %s as %s', condition, header_map[condition])
|
|
217
|
+
tmp_config.append(header_map[condition])
|
|
218
|
+
else:
|
|
219
|
+
tmp_config.append(condition)
|
|
220
|
+
elif ctype == 'elseif':
|
|
221
|
+
if condition in header_map.keys():
|
|
222
|
+
LOGGER.debug('Redefining %s as !(%s)', condition, header_map[condition])
|
|
223
|
+
tmp_config.append('!(' + header_map[condition] + ')')
|
|
224
|
+
else:
|
|
225
|
+
LOGGER.debug('Negating %s to !(%s)', condition, condition)
|
|
226
|
+
tmp_config.append('!(' + condition + ')')
|
|
227
|
+
else:
|
|
228
|
+
tmp_config.append(self.define_config(condition, header_map, ctype))
|
|
229
|
+
if not tmp_config and config:
|
|
230
|
+
LOGGER.warning('Config not found: %s from %s', config, self.configs)
|
|
231
|
+
tmp_config.append('ALWAYS_ACTIVE')
|
|
232
|
+
LOGGER.info('Current config: %s', tmp_config)
|
|
233
|
+
elif not config:
|
|
234
|
+
LOGGER.debug('No config, always active')
|
|
235
|
+
tmp_config.append('ALWAYS_ACTIVE')
|
|
236
|
+
configs.append(self.compose_and(list(self.sort_u(tmp_config))))
|
|
237
|
+
return self.compose_or(list(self.sort_u(configs)))
|
|
238
|
+
|
|
239
|
+
|
|
240
|
+
class JsonConfigHandler:
|
|
241
|
+
"""Handle the json config."""
|
|
242
|
+
|
|
243
|
+
def __init__(self, cparser, header_map):
|
|
244
|
+
"""Initialize handling of one json file.
|
|
245
|
+
|
|
246
|
+
Arguments:
|
|
247
|
+
parser (obj): c-parser
|
|
248
|
+
header_map (dict): defines in the header files
|
|
249
|
+
"""
|
|
250
|
+
self.cparser = cparser
|
|
251
|
+
self.header_map = header_map
|
|
252
|
+
|
|
253
|
+
def traverse_unit(self, struct, setup=True):
|
|
254
|
+
"""Go through a data structure and look for configs to update.
|
|
255
|
+
|
|
256
|
+
Arguments:
|
|
257
|
+
struct (dict): data to go through
|
|
258
|
+
parser (obj): parsing object
|
|
259
|
+
header_map (dict): dict of defines
|
|
260
|
+
setup (bool): Set up the parser obecjt (True) or replace configs (False)
|
|
261
|
+
"""
|
|
262
|
+
for name, data in struct.items():
|
|
263
|
+
if isinstance(data, dict) and name != 'API_blk':
|
|
264
|
+
# Core data has the propety config, not configs
|
|
265
|
+
if data.get('API_blk') is not None or data.get('configs') is not None:
|
|
266
|
+
if setup:
|
|
267
|
+
self.cparser.set_regexes(name)
|
|
268
|
+
else:
|
|
269
|
+
data['configs'] = self.cparser.get_configs(name, self.header_map)
|
|
270
|
+
else:
|
|
271
|
+
self.traverse_unit(data, setup)
|
|
272
|
+
|
|
273
|
+
def update_config(self, struct, c_code, header_map=None):
|
|
274
|
+
"""Update dict.
|
|
275
|
+
|
|
276
|
+
Arguments:
|
|
277
|
+
data (dict): A configuration dict or subdict
|
|
278
|
+
c_code (list): code part of a c-file
|
|
279
|
+
"""
|
|
280
|
+
if header_map is None:
|
|
281
|
+
header_map = {}
|
|
282
|
+
# Set up regexes:
|
|
283
|
+
self.traverse_unit(struct, setup=True)
|
|
284
|
+
self.cparser.parse_file_content(c_code)
|
|
285
|
+
self.traverse_unit(struct, setup=False)
|
|
286
|
+
|
|
287
|
+
@staticmethod
|
|
288
|
+
def read_config(config_file):
|
|
289
|
+
"""Read config file.
|
|
290
|
+
|
|
291
|
+
Arguments:
|
|
292
|
+
config_file (str): Full path to config file
|
|
293
|
+
"""
|
|
294
|
+
with open(config_file, encoding='latin-1') as unit_json:
|
|
295
|
+
unit_config = json.load(unit_json)
|
|
296
|
+
return unit_config
|
|
297
|
+
|
|
298
|
+
@staticmethod
|
|
299
|
+
def write_config(config_file, unit_config):
|
|
300
|
+
"""Write config file.
|
|
301
|
+
|
|
302
|
+
Arguments:
|
|
303
|
+
config_file (str): Full path to config file
|
|
304
|
+
unit_config (dict): Unit config to write to file
|
|
305
|
+
"""
|
|
306
|
+
with open(config_file, 'w', encoding="utf-8") as unit_json:
|
|
307
|
+
unit_json.write(json.dumps(unit_config, indent=2))
|
|
308
|
+
|
|
309
|
+
|
|
310
|
+
class HeaderConfigParser(ConfigParserCommon):
|
|
311
|
+
"""Parser for c-files."""
|
|
312
|
+
|
|
313
|
+
def set_defines(self, defines):
|
|
314
|
+
"""Set already defined defines."""
|
|
315
|
+
self.def_map = defines
|
|
316
|
+
|
|
317
|
+
def parse_else(self):
|
|
318
|
+
"""Crash if this is found in a header."""
|
|
319
|
+
raise NotImplementedError
|
|
320
|
+
|
|
321
|
+
def parse_defines(self, variable, definition):
|
|
322
|
+
"""Parse defines in c-files."""
|
|
323
|
+
if self.ifs and self.ifs[-1][0] == 'ifndef' and variable == self.ifs[-1][-1]:
|
|
324
|
+
# We have encountered a case of:
|
|
325
|
+
#
|
|
326
|
+
# #ifndef a
|
|
327
|
+
# #define a
|
|
328
|
+
# #define b
|
|
329
|
+
#
|
|
330
|
+
# Then we don't want b to be dependent on a not being defined.
|
|
331
|
+
|
|
332
|
+
c_type, condition = self.ifs.pop()
|
|
333
|
+
LOGGER.debug('Removing now defined %s from ifs: %s %s', variable, c_type, condition)
|
|
334
|
+
if definition:
|
|
335
|
+
LOGGER.info('Redefining %s as %s', variable, definition)
|
|
336
|
+
# Here we ignore the potential #if statements preceding this.
|
|
337
|
+
# Have not encountered a case where that matters.
|
|
338
|
+
# This structure does not support that logic.
|
|
339
|
+
# Potential for bugs.
|
|
340
|
+
self.configs[variable] = [definition]
|
|
341
|
+
elif self.ifs:
|
|
342
|
+
config = self.get_configs(self.ifs, self.def_map)
|
|
343
|
+
LOGGER.info('Defining %s as %s', variable, config)
|
|
344
|
+
if variable not in self.configs:
|
|
345
|
+
self.configs[variable] = []
|
|
346
|
+
self.configs[variable].append(copy.deepcopy(config))
|
|
347
|
+
self.def_map.update({variable: copy.deepcopy(config)})
|
|
348
|
+
|
|
349
|
+
@staticmethod
|
|
350
|
+
def define_config(condition, header_map, ctype):
|
|
351
|
+
"""Get a config from the header map.
|
|
352
|
+
|
|
353
|
+
Arguments:
|
|
354
|
+
condition
|
|
355
|
+
header_map
|
|
356
|
+
ctype
|
|
357
|
+
"""
|
|
358
|
+
if ctype == 'ifdef':
|
|
359
|
+
if condition in header_map.keys():
|
|
360
|
+
LOGGER.debug('returning %s as %s', condition, header_map[condition])
|
|
361
|
+
config = header_map[condition]
|
|
362
|
+
else:
|
|
363
|
+
config = 'ALWAYS_ACTIVE'
|
|
364
|
+
LOGGER.warning('Not Implemented Yet: %s %s', ctype, condition)
|
|
365
|
+
if ctype == 'ifndef':
|
|
366
|
+
if condition in header_map.keys():
|
|
367
|
+
LOGGER.debug('returning %s as %s', condition, header_map[condition])
|
|
368
|
+
config = '!(' + header_map[condition] + ')'
|
|
369
|
+
else:
|
|
370
|
+
config = 'ALWAYS_ACTIVE'
|
|
371
|
+
LOGGER.warning('Not Implemented Yet: %s %s', ctype, condition)
|
|
372
|
+
return config
|
|
373
|
+
|
|
374
|
+
def process_config(self, inconfigs, header_map):
|
|
375
|
+
"""Process configs."""
|
|
376
|
+
configs = []
|
|
377
|
+
for config in inconfigs:
|
|
378
|
+
LOGGER.debug('Current config: %s', config)
|
|
379
|
+
if isinstance(config, list):
|
|
380
|
+
configs.append(self.process_config(config, header_map))
|
|
381
|
+
else:
|
|
382
|
+
ctype, condition = config
|
|
383
|
+
if ctype == 'if':
|
|
384
|
+
if condition in header_map.keys():
|
|
385
|
+
configs.append(header_map[condition])
|
|
386
|
+
else:
|
|
387
|
+
configs.append(condition)
|
|
388
|
+
elif ctype in ['ifdef', 'ifndef']:
|
|
389
|
+
configs.append(self.define_config(condition, header_map, ctype))
|
|
390
|
+
else:
|
|
391
|
+
LOGGER.error('Not Implemented: %s', ctype)
|
|
392
|
+
if not configs:
|
|
393
|
+
configs = ['ALWAYS_ACTIVE']
|
|
394
|
+
return list(self.sort_u(configs))
|
|
395
|
+
|
|
396
|
+
def get_configs(self, configs, header_map):
|
|
397
|
+
"""Get configs.
|
|
398
|
+
|
|
399
|
+
Does not remove redundant configs.
|
|
400
|
+
"""
|
|
401
|
+
configs = self.process_config(configs, header_map)
|
|
402
|
+
if len(configs) > 1:
|
|
403
|
+
return self.compose_and(list(self.sort_u(configs)))
|
|
404
|
+
return configs[0]
|
|
405
|
+
|
|
406
|
+
def get_config(self):
|
|
407
|
+
"""Get the header map."""
|
|
408
|
+
header_map = self.def_map
|
|
409
|
+
for header_def, configs in self.configs.items():
|
|
410
|
+
header_map[header_def] = self.compose_or(configs)
|
|
411
|
+
return header_map
|
|
412
|
+
|
|
413
|
+
|
|
414
|
+
class ProcessHandler:
|
|
415
|
+
"""Class to collect functions for the process."""
|
|
416
|
+
|
|
417
|
+
PARSER_HELP = "Parse configs.json and c-files, to update code switch configs"
|
|
418
|
+
|
|
419
|
+
@staticmethod
|
|
420
|
+
def configure_parser(parser: argparse.ArgumentParser):
|
|
421
|
+
"""Parse arguments."""
|
|
422
|
+
parser.description = "Parse configs.json and c-files, to update code switch configs"
|
|
423
|
+
|
|
424
|
+
subparser = parser.add_subparsers(
|
|
425
|
+
title='Operation mode',
|
|
426
|
+
dest='mode',
|
|
427
|
+
help="Run chosen files on in a number of directories",
|
|
428
|
+
)
|
|
429
|
+
dir_parser = subparser.add_parser(
|
|
430
|
+
'models',
|
|
431
|
+
help="Run for one or multiple models. Script finds files generated from the model(s).")
|
|
432
|
+
dir_parser.add_argument('models', nargs='+',
|
|
433
|
+
help="Space separated list of model directories")
|
|
434
|
+
|
|
435
|
+
file_parser = subparser.add_parser('files',
|
|
436
|
+
help="Choose specific files. Mainly for manually written configs.")
|
|
437
|
+
file_parser.add_argument('c_file',
|
|
438
|
+
help="Full path to C-file")
|
|
439
|
+
file_parser.add_argument('config_file',
|
|
440
|
+
help="Full path to config file")
|
|
441
|
+
file_parser.add_argument('--aux_file',
|
|
442
|
+
help="Full path to tl_aux file. (Optional) ")
|
|
443
|
+
file_parser.add_argument('--local_file',
|
|
444
|
+
help="Full path to OPort file. (Optional) ")
|
|
445
|
+
|
|
446
|
+
parser.set_defaults(func=ProcessHandler.main)
|
|
447
|
+
|
|
448
|
+
@staticmethod
|
|
449
|
+
def get_files(model_path):
|
|
450
|
+
"""Get file paths from model path.
|
|
451
|
+
|
|
452
|
+
Arguments:
|
|
453
|
+
model_path (str): Path to a model (.mdl)
|
|
454
|
+
|
|
455
|
+
Returns:
|
|
456
|
+
local_file (str): Path to model_OPortMvd_LocalDefs.h
|
|
457
|
+
aux_file (str): Path to tl_aux_defines_model.h
|
|
458
|
+
config_file (str): Path to config_model.json
|
|
459
|
+
c_file (str): Path to model.c
|
|
460
|
+
|
|
461
|
+
"""
|
|
462
|
+
model_dir = os.path.dirname(model_path)
|
|
463
|
+
LOGGER.info('Processing %s', model_dir)
|
|
464
|
+
model_name = os.path.basename(model_dir)
|
|
465
|
+
local_file = os.path.join(model_dir, 'pybuild_src', f'{model_name}_OPortMvd_LocalDefs.h')
|
|
466
|
+
# aux_file does not contain the whole model-name if it is too long.
|
|
467
|
+
aux_file = os.path.join(model_dir, 'pybuild_src', f'tl_aux_defines_{model_name[2:12]}.h')
|
|
468
|
+
config_file = os.path.join(model_dir, 'pybuild_cfg', f'config_{model_name}.json')
|
|
469
|
+
clean_model_name = model_name.split('__')[0]
|
|
470
|
+
c_file = os.path.join(model_dir, 'pybuild_src', f'{clean_model_name}.c')
|
|
471
|
+
return local_file, aux_file, c_file, config_file
|
|
472
|
+
|
|
473
|
+
@staticmethod
|
|
474
|
+
def update_config_file(c_file, config_file, header_map):
|
|
475
|
+
"""Update one config file.
|
|
476
|
+
|
|
477
|
+
Arguments:
|
|
478
|
+
c_file (str): Full path to c-file
|
|
479
|
+
config_file (str): Full path to config.json
|
|
480
|
+
oport_file (str): Full path to OPortMvd_LocalDefs.h (Optional)
|
|
481
|
+
"""
|
|
482
|
+
LOGGER.info('Updating %s based on %s', config_file, c_file)
|
|
483
|
+
cparser = CConfigParser()
|
|
484
|
+
c_code = cparser.read_file(c_file)
|
|
485
|
+
json_handler = JsonConfigHandler(cparser, header_map)
|
|
486
|
+
unit_config = json_handler.read_config(config_file)
|
|
487
|
+
json_handler.update_config(unit_config, c_code, header_map)
|
|
488
|
+
json_handler.write_config(config_file, unit_config)
|
|
489
|
+
|
|
490
|
+
@staticmethod
|
|
491
|
+
def get_header_config(header_file, def_map):
|
|
492
|
+
"""Get header config.
|
|
493
|
+
|
|
494
|
+
Arguments:
|
|
495
|
+
c_file (str): Full path to c-file
|
|
496
|
+
config_file (str): Full path to config.json
|
|
497
|
+
oport_file (str): Full path to OPortMvd_LocalDefs.h (Optional)
|
|
498
|
+
"""
|
|
499
|
+
if header_file is None:
|
|
500
|
+
LOGGER.info('File not found: %s', header_file)
|
|
501
|
+
return def_map
|
|
502
|
+
if not os.path.isfile(header_file):
|
|
503
|
+
LOGGER.info('File not found: %s', header_file)
|
|
504
|
+
model_dir = os.path.dirname(header_file)
|
|
505
|
+
for tl_aux_file in glob.glob(os.path.join(model_dir, 'tl_aux*')):
|
|
506
|
+
LOGGER.warning('Looking for %s?', tl_aux_file)
|
|
507
|
+
return def_map
|
|
508
|
+
LOGGER.info('Parsing %s', header_file)
|
|
509
|
+
parser = HeaderConfigParser()
|
|
510
|
+
header_code = parser.read_file(header_file)
|
|
511
|
+
parser.set_defines(def_map)
|
|
512
|
+
parser.parse_file_content(header_code)
|
|
513
|
+
LOGGER.debug('Header configs: %s', pformat(parser.configs))
|
|
514
|
+
return parser.get_config()
|
|
515
|
+
|
|
516
|
+
@classmethod
|
|
517
|
+
def main(cls, args: argparse.Namespace):
|
|
518
|
+
"""Run the main function of the script."""
|
|
519
|
+
if args.mode == 'files':
|
|
520
|
+
LOGGER.info('Using manually supplied files %s', args)
|
|
521
|
+
local_defs = cls.get_header_config(args.local_file, {})
|
|
522
|
+
aux_defs = cls.get_header_config(args.aux_file, local_defs)
|
|
523
|
+
cls.update_config_file(args.c_file, args.config_file, aux_defs)
|
|
524
|
+
else:
|
|
525
|
+
for model in args.models:
|
|
526
|
+
local_file, aux_file, c_file, config_file = cls.get_files(model)
|
|
527
|
+
local_defs = cls.get_header_config(local_file, {})
|
|
528
|
+
aux_defs = cls.get_header_config(aux_file, local_defs)
|
|
529
|
+
if os.path.isfile(c_file) and os.path.isfile(config_file):
|
|
530
|
+
cls.update_config_file(c_file, config_file, aux_defs)
|
|
531
|
+
|
|
532
|
+
|
|
533
|
+
def main(argv: Optional[List[str]] = None):
|
|
534
|
+
"""Run main function."""
|
|
535
|
+
parser = argparse.ArgumentParser(ProcessHandler.PARSER_HELP)
|
|
536
|
+
ProcessHandler.configure_parser(parser)
|
|
537
|
+
args = parser.parse_args(argv)
|
|
538
|
+
return args.func(args)
|
|
539
|
+
|
|
540
|
+
|
|
541
|
+
if __name__ == "__main__":
|
|
542
|
+
main(sys.argv[1:])
|