]> begriffs open source - cmsis/blob - CMSIS/CoreValidation/Project/build.py
Core{ Rework CoreValidation
[cmsis] / CMSIS / CoreValidation / Project / build.py
1 #!/usr/bin/env python3
2 # -*- coding: utf-8 -*-
3
4 import logging
5
6 from datetime import datetime
7 from enum import Enum
8 from glob import glob, iglob
9 from pathlib import Path
10
11 from lxml.etree import XMLSyntaxError
12 from zipfile import ZipFile
13
14 from matrix_runner import main, matrix_axis, matrix_action, matrix_command, matrix_filter, \
15     ConsoleReport, CropReport, TransformReport, JUnitReport
16
17
18 @matrix_axis("device", "d", "Device(s) to be considered.")
19 class DeviceAxis(Enum):
20     CM0 = ('Cortex-M0', 'CM0')
21     CM0plus = ('Cortex-M0plus', 'CM0plus')
22     CM3 = ('Cortex-M3', 'CM3')
23     CM4 = ('Cortex-M4', 'CM4')
24     CM7 = ('Cortex-M7', 'CM7')
25     CM23 = ('Cortex-M23', 'CM23')
26     CM23S = ('Cortex-M23S', 'CM23S')
27     CM23NS = ('Cortex-M23NS', 'CM23NS')
28     CM33 = ('Cortex-M33', 'CM33')
29     CM33S = ('Cortex-M33S', 'CM33S')
30     CM33NS = ('Cortex-M33NS', 'CM33NS')
31     CM35P = ('Cortex-M35P', 'CM35P')
32     CM35PS = ('Cortex-M35PS', 'CM35PS')
33     CM35PNS = ('Cortex-M35PNS', 'CM35PNS')
34     CM55 = ('Cortex-M55', 'CM55')
35     CM55S = ('Cortex-M55S', 'CM55S')
36     CM55NS = ('Cortex-M55NS', 'CM55NS')
37     CM85 = ('Cortex-M85', 'CM85')
38     CM85S = ('Cortex-M85S', 'CM85S')
39     CM85NS = ('Cortex-M85NS', 'CM85NS')
40     CA5 = ('Cortex-A5', 'CA5')
41     CA7 = ('Cortex-A7', 'CA7')
42     CA9 = ('Cortex-A9', 'CA9')
43 #    CA5NEON = ('Cortex-A5neon', 'CA5neon')
44 #    CA7NEON = ('Cortex-A7neon', 'CA7neon')
45 #    CA9NEON = ('Cortex-A9neon', 'CA9neon')
46
47     def has_bl(self):
48         return self in [
49             DeviceAxis.CM23NS,
50             DeviceAxis.CM33NS,
51             DeviceAxis.CM35PNS,
52             DeviceAxis.CM55NS,
53             DeviceAxis.CM85NS
54         ]
55
56     @property
57     def bl_device(self):
58         bld = {
59             DeviceAxis.CM23NS: 'CM23S',
60             DeviceAxis.CM33NS: 'CM33S',
61             DeviceAxis.CM35PNS: 'CM35PS',
62             DeviceAxis.CM55NS: 'CM55S',
63             DeviceAxis.CM85NS: 'CM85S'
64         }
65         return bld[self]
66
67
68 @matrix_axis("compiler", "c", "Compiler(s) to be considered.")
69 class CompilerAxis(Enum):
70     AC6 = ('AC6')
71     GCC = ('GCC')
72     IAR = ('IAR')
73     CLANG = ('Clang')
74
75     @property
76     def image_ext(self):
77         ext = {
78             CompilerAxis.AC6: 'axf',
79             CompilerAxis.GCC: 'elf',
80             CompilerAxis.IAR: 'elf',
81             CompilerAxis.CLANG: 'elf',
82         }
83         return ext[self]
84
85     @property
86     def toolchain(self):
87         ext = {
88             CompilerAxis.AC6: 'AC6',
89             CompilerAxis.GCC: 'GCC',
90             CompilerAxis.IAR: 'IAR',
91             CompilerAxis.CLANG: 'CLANG'
92         }
93         return ext[self]
94
95
96 @matrix_axis("optimize", "o", "Optimization level(s) to be considered.")
97 class OptimizationAxis(Enum):
98     NONE = ('none')
99     BALANCED = ('balanced')
100     SPEED = ('speed')
101     SIZE = ('size')
102
103
104 MODEL_EXECUTABLE = {
105     DeviceAxis.CM0: ("FVP_MPS2_Cortex-M0", []),
106     DeviceAxis.CM0plus: ("FVP_MPS2_Cortex-M0plus", []),
107     DeviceAxis.CM3: ("FVP_MPS2_Cortex-M3", []),
108     DeviceAxis.CM4: ("FVP_MPS2_Cortex-M4", []),
109     DeviceAxis.CM7: ("FVP_MPS2_Cortex-M7", []),
110     DeviceAxis.CM23: ("FVP_MPS2_Cortex-M23", []),
111     DeviceAxis.CM23S: ("FVP_MPS2_Cortex-M23", []),
112     DeviceAxis.CM23NS: ("FVP_MPS2_Cortex-M23", []),
113     DeviceAxis.CM33: ("FVP_MPS2_Cortex-M33", []),
114     DeviceAxis.CM33S: ("FVP_MPS2_Cortex-M33", []),
115     DeviceAxis.CM33NS: ("FVP_MPS2_Cortex-M33", []),
116     DeviceAxis.CM35P: ("FVP_MPS2_Cortex-M35P", []),
117     DeviceAxis.CM35PS: ("FVP_MPS2_Cortex-M35P", []),
118     DeviceAxis.CM35PNS: ("FVP_MPS2_Cortex-M35P", []),
119     DeviceAxis.CM55: ("FVP_MPS2_Cortex-M55", []),
120     DeviceAxis.CM55S: ("FVP_MPS2_Cortex-M55", []),
121     DeviceAxis.CM55NS: ("FVP_MPS2_Cortex-M55", []),
122     DeviceAxis.CM85: ("FVP_MPS2_Cortex-M85", []),
123     DeviceAxis.CM85S: ("FVP_MPS2_Cortex-M85", []),
124     DeviceAxis.CM85NS: ("FVP_MPS2_Cortex-M85", []),
125     DeviceAxis.CA5: ("FVP_VE_Cortex-A5x1", []),
126     DeviceAxis.CA7: ("FVP_VE_Cortex-A7x1", []),
127     DeviceAxis.CA9: ("FVP_VE_Cortex-A9x1", []),
128 #    DeviceAxis.CA5NEON: ("_VE_Cortex-A5x1", []),
129 #    DeviceAxis.CA7NEON: ("_VE_Cortex-A7x1", []),
130 #    DeviceAxis.CA9NEON: ("_VE_Cortex-A9x1", [])
131 }
132
133 def config_suffix(config, timestamp=True):
134     suffix = f"{config.compiler[0]}-{config.optimize[0]}-{config.device[1]}"
135     if timestamp:
136         suffix += f"-{datetime.now().strftime('%Y%m%d%H%M%S')}"
137     return suffix
138
139
140 def project_name(config):
141     return f"Validation.{config.compiler}_{config.optimize}+{config.device[1]}"
142
143
144 def bl_project_name(config):
145     return f"Bootloader.{config.compiler}_{config.optimize}+{config.device.bl_device}"
146
147
148 def output_dir(config):
149     return f"Validation/outdir"
150
151
152 def bl_output_dir(config):
153     return f"Bootloader/outdir"
154
155
156 def model_config(config):
157     return f"../Layer/Target/{config.device[1]}/model_config.txt"
158
159
160 def build_dir(config):
161     return f"build/{config.device[1]}/{config.compiler.toolchain}/{config.optimize}"
162
163
164 @matrix_action
165 def clean(config):
166     """Build the selected configurations using CMSIS-Build."""
167     yield cbuild_clean(f"{project_name(config)}/{project_name(config)}.cprj")
168
169
170 @matrix_action
171 def build(config, results):
172     """Build the selected configurations using CMSIS-Build."""
173
174     logging.info("Compiling Tests...")
175
176     yield cbuild(config)
177
178     if not all(r.success for r in results):
179         return
180
181     file = f"build/CoreValidation-{config_suffix(config)}.zip"
182     logging.info("Archiving build output to %s...", file)
183     with ZipFile(file, "w") as archive:
184         for content in iglob(f"{build_dir(config)}/**/*", recursive=True):
185             if Path(content).is_file():
186                 archive.write(content)
187
188
189 @matrix_action
190 def extract(config):
191     """Extract the latest build archive."""
192     archives = sorted(glob(f"build/CoreValidation-{config_suffix(config, timestamp=False)}-*.zip"), reverse=True)
193     yield unzip(archives[0])
194
195
196 @matrix_action
197 def run(config, results):
198     """Run the selected configurations."""
199     logging.info("Running Core Validation on Arm model ...")
200     yield model_exec(config)
201
202     try:
203         results[0].test_report.write(f"build/CoreValidation-{config_suffix(config)}.junit")
204     except RuntimeError as ex:
205         if isinstance(ex.__cause__, XMLSyntaxError):
206             logging.error("No valid test report found in model output!")
207         else:
208             logging.exception(ex)
209
210
211 @matrix_command()
212 def cbuild_clean(project):
213     return ["cbuild", "-c", project]
214
215
216 @matrix_command()
217 def unzip(archive):
218     return ["bash", "-c", f"unzip {archive}"]
219
220
221 @matrix_command()
222 def preprocess(infile, outfile):
223     return ["arm-none-eabi-gcc", "-xc", "-E", infile, "-P", "-o", outfile]
224
225
226 @matrix_command()
227 def cbuild(config):
228     return ["cbuild", "--toolchain", config.compiler.toolchain, "--update-rte", \
229              "--context", f".{config.optimize}+{config.device[1]}", \
230              "Validation.csolution.yml" ]
231
232
233 @matrix_command(test_report=ConsoleReport() |
234                             CropReport('<\?xml version="1.0"\?>', '</report>') |
235                             TransformReport('validation.xsl') |
236                             JUnitReport(title=lambda title, result: f"{result.command.config.compiler}."
237                                                                     f"{result.command.config.optimize}."
238                                                                     f"{result.command.config.device}."
239                                                                     f"{title}"))
240 def model_exec(config):
241     cmdline = [MODEL_EXECUTABLE[config.device][0], "-q", "--simlimit", 100, "-f", model_config(config)]
242     cmdline += MODEL_EXECUTABLE[config.device][1]
243     cmdline += ["-a", f"{build_dir(config)}/{output_dir(config)}/Validation.{config.compiler.image_ext}"]
244     if config.device.has_bl():
245         cmdline += ["-a", f"{build_dir(config)}/{bl_output_dir(config)}/Bootloader.{config.compiler.image_ext}"]
246     return cmdline
247
248
249 @matrix_filter
250 def filter_iar(config):
251     return config.compiler == CompilerAxis.IAR
252
253
254 if __name__ == "__main__":
255     main()