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.
Files changed (132) hide show
  1. powertrain_build/__init__.py +40 -40
  2. powertrain_build/__main__.py +6 -6
  3. powertrain_build/a2l.py +582 -582
  4. powertrain_build/a2l_merge.py +650 -650
  5. powertrain_build/a2l_templates.py +717 -717
  6. powertrain_build/build.py +985 -985
  7. powertrain_build/build_defs.py +309 -309
  8. powertrain_build/build_proj_config.py +690 -690
  9. powertrain_build/check_interface.py +575 -575
  10. powertrain_build/cli.py +141 -141
  11. powertrain_build/config.py +542 -542
  12. powertrain_build/core.py +395 -395
  13. powertrain_build/core_dummy.py +343 -343
  14. powertrain_build/create_conversion_table.py +73 -73
  15. powertrain_build/dids.py +916 -916
  16. powertrain_build/dummy.py +157 -157
  17. powertrain_build/dummy_spm.py +252 -252
  18. powertrain_build/environmentcheck.py +52 -52
  19. powertrain_build/ext_dbg.py +255 -255
  20. powertrain_build/ext_var.py +327 -327
  21. powertrain_build/feature_configs.py +301 -301
  22. powertrain_build/gen_allsysteminfo.py +227 -227
  23. powertrain_build/gen_label_split.py +449 -449
  24. powertrain_build/handcode_replacer.py +124 -124
  25. powertrain_build/html_report.py +133 -133
  26. powertrain_build/interface/__init__.py +4 -4
  27. powertrain_build/interface/application.py +511 -511
  28. powertrain_build/interface/base.py +500 -500
  29. powertrain_build/interface/csp_api.py +490 -490
  30. powertrain_build/interface/device_proxy.py +677 -677
  31. powertrain_build/interface/ems.py +67 -67
  32. powertrain_build/interface/export_global_vars.py +121 -121
  33. powertrain_build/interface/generate_adapters.py +132 -132
  34. powertrain_build/interface/generate_hi_interface.py +87 -87
  35. powertrain_build/interface/generate_service.py +69 -69
  36. powertrain_build/interface/generate_wrappers.py +147 -147
  37. powertrain_build/interface/generation_utils.py +142 -142
  38. powertrain_build/interface/hal.py +194 -194
  39. powertrain_build/interface/model_yaml_verification.py +348 -348
  40. powertrain_build/interface/service.py +296 -296
  41. powertrain_build/interface/simulink.py +249 -249
  42. powertrain_build/interface/update_call_sources.py +180 -180
  43. powertrain_build/interface/update_model_yaml.py +186 -186
  44. powertrain_build/interface/zone_controller.py +362 -362
  45. powertrain_build/lib/__init__.py +4 -4
  46. powertrain_build/lib/helper_functions.py +127 -127
  47. powertrain_build/lib/logger.py +55 -55
  48. powertrain_build/matlab_scripts/CodeGen/BuildAutomationPyBuild.m +78 -78
  49. powertrain_build/matlab_scripts/CodeGen/Generate_A2L.m +154 -154
  50. powertrain_build/matlab_scripts/CodeGen/generateTLUnit.m +239 -239
  51. powertrain_build/matlab_scripts/CodeGen/getAsilClassification.m +28 -28
  52. powertrain_build/matlab_scripts/CodeGen/modelConfiguredForTL.m +28 -28
  53. powertrain_build/matlab_scripts/CodeGen/moveDefOutports.m +88 -88
  54. powertrain_build/matlab_scripts/CodeGen/parseCalMeasData.m +410 -410
  55. powertrain_build/matlab_scripts/CodeGen/parseCoreIdentifiers.m +139 -139
  56. powertrain_build/matlab_scripts/CodeGen/parseDIDs.m +141 -141
  57. powertrain_build/matlab_scripts/CodeGen/parseInPorts.m +106 -106
  58. powertrain_build/matlab_scripts/CodeGen/parseIncludeConfigs.m +25 -25
  59. powertrain_build/matlab_scripts/CodeGen/parseModelInfo.m +38 -38
  60. powertrain_build/matlab_scripts/CodeGen/parseNVM.m +81 -81
  61. powertrain_build/matlab_scripts/CodeGen/parseOutPorts.m +120 -120
  62. powertrain_build/matlab_scripts/CodeGen/parsePreProcBlks.m +23 -23
  63. powertrain_build/matlab_scripts/CodeGen/struct2JSON.m +128 -128
  64. powertrain_build/matlab_scripts/CodeGen/updateCodeSwConfig.m +31 -31
  65. powertrain_build/matlab_scripts/Init_PyBuild.m +91 -91
  66. powertrain_build/matlab_scripts/__init__.py +2 -2
  67. powertrain_build/matlab_scripts/helperFunctions/Get_Full_Name.m +46 -46
  68. powertrain_build/matlab_scripts/helperFunctions/Get_SrcLines.m +12 -12
  69. powertrain_build/matlab_scripts/helperFunctions/Init_Models.m +78 -78
  70. powertrain_build/matlab_scripts/helperFunctions/Init_Projects.m +67 -67
  71. powertrain_build/matlab_scripts/helperFunctions/Read_Units.m +34 -34
  72. powertrain_build/matlab_scripts/helperFunctions/SetProjectTimeSamples.m +26 -26
  73. powertrain_build/matlab_scripts/helperFunctions/Strip_Suffix.m +16 -16
  74. powertrain_build/matlab_scripts/helperFunctions/followLink.m +118 -118
  75. powertrain_build/matlab_scripts/helperFunctions/getCodeSwitches.m +50 -50
  76. powertrain_build/matlab_scripts/helperFunctions/getConsumerBlocks.m +30 -30
  77. powertrain_build/matlab_scripts/helperFunctions/getDefBlock.m +39 -39
  78. powertrain_build/matlab_scripts/helperFunctions/getDefOutport.m +58 -58
  79. powertrain_build/matlab_scripts/helperFunctions/getDstBlocks.m +19 -19
  80. powertrain_build/matlab_scripts/helperFunctions/getDstLines.m +13 -13
  81. powertrain_build/matlab_scripts/helperFunctions/getInterfaceSignals.m +37 -37
  82. powertrain_build/matlab_scripts/helperFunctions/getName.m +37 -37
  83. powertrain_build/matlab_scripts/helperFunctions/getPath.m +6 -6
  84. powertrain_build/matlab_scripts/helperFunctions/getProperValue.m +21 -21
  85. powertrain_build/matlab_scripts/helperFunctions/getSrcBlocks.m +19 -19
  86. powertrain_build/matlab_scripts/helperFunctions/getSrcLines.m +13 -13
  87. powertrain_build/matlab_scripts/helperFunctions/loadLibraries.m +10 -10
  88. powertrain_build/matlab_scripts/helperFunctions/loadjson.m +6 -6
  89. powertrain_build/matlab_scripts/helperFunctions/modifyEnumStructField.m +21 -21
  90. powertrain_build/matlab_scripts/helperFunctions/removeConfigDuplicates.m +31 -31
  91. powertrain_build/matlab_scripts/helperFunctions/sortSystemByClass.m +26 -26
  92. powertrain_build/matlab_scripts/helperFunctions/tl_getfast.m +89 -89
  93. powertrain_build/matlab_scripts/helperFunctions/topLevelSystem.m +20 -20
  94. powertrain_build/matlab_scripts/helperFunctions/updateModels.m +131 -131
  95. powertrain_build/memory_section.py +224 -224
  96. powertrain_build/nvm_def.py +729 -729
  97. powertrain_build/problem_logger.py +86 -86
  98. powertrain_build/pt_matlab.py +430 -430
  99. powertrain_build/pt_win32.py +144 -144
  100. powertrain_build/replace_compu_tab_ref.py +105 -105
  101. powertrain_build/rte_dummy.py +254 -254
  102. powertrain_build/sched_funcs.py +209 -207
  103. powertrain_build/signal.py +7 -7
  104. powertrain_build/signal_if_html_rep.py +221 -221
  105. powertrain_build/signal_if_html_rep_all.py +302 -302
  106. powertrain_build/signal_incons_html_rep.py +180 -180
  107. powertrain_build/signal_incons_html_rep_all.py +366 -366
  108. powertrain_build/signal_incons_html_rep_base.py +168 -168
  109. powertrain_build/signal_inconsistency_check.py +641 -641
  110. powertrain_build/signal_interfaces.py +864 -864
  111. powertrain_build/templates/Index_SigCheck_All.html +22 -22
  112. powertrain_build/templates/Index_SigIf_All.html +19 -19
  113. powertrain_build/types.py +218 -218
  114. powertrain_build/unit_configs.py +419 -419
  115. powertrain_build/user_defined_types.py +660 -660
  116. powertrain_build/versioncheck.py +66 -66
  117. powertrain_build/wrapper.py +512 -512
  118. powertrain_build/xlrd_csv.py +87 -87
  119. powertrain_build/zone_controller/__init__.py +4 -4
  120. powertrain_build/zone_controller/calibration.py +176 -176
  121. powertrain_build/zone_controller/composition_yaml.py +880 -878
  122. {powertrain_build-1.13.1.dist-info → powertrain_build-1.13.3.dev3.dist-info}/METADATA +100 -100
  123. powertrain_build-1.13.3.dev3.dist-info/RECORD +130 -0
  124. {powertrain_build-1.13.1.dist-info → powertrain_build-1.13.3.dev3.dist-info}/WHEEL +1 -1
  125. {powertrain_build-1.13.1.dist-info → powertrain_build-1.13.3.dev3.dist-info}/licenses/LICENSE +202 -202
  126. powertrain_build-1.13.3.dev3.dist-info/pbr.json +1 -0
  127. powertrain_build-1.13.1.dist-info/RECORD +0 -130
  128. powertrain_build-1.13.1.dist-info/pbr.json +0 -1
  129. {powertrain_build-1.13.1.dist-info → powertrain_build-1.13.3.dev3.dist-info}/entry_points.txt +0 -0
  130. {powertrain_build-1.13.1.dist-info → powertrain_build-1.13.3.dev3.dist-info}/licenses/AUTHORS +0 -0
  131. {powertrain_build-1.13.1.dist-info → powertrain_build-1.13.3.dev3.dist-info}/licenses/NOTICE +0 -0
  132. {powertrain_build-1.13.1.dist-info → powertrain_build-1.13.3.dev3.dist-info}/top_level.txt +0 -0
@@ -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:])