blob: fbfa0c867f2193cc6ac30016e3f7c4baee2c17a2 [file] [log] [blame]
Minos Galanakisf4ca6ac2017-12-11 02:39:21 +01001#!/usr/bin/env python3
2
3""" tfm_builder.py:
4
5 Build wrapping class that builds a specific tfm configuration """
6
7from __future__ import print_function
8
9__copyright__ = """
10/*
11 * Copyright (c) 2018-2019, Arm Limited. All rights reserved.
12 *
13 * SPDX-License-Identifier: BSD-3-Clause
14 *
15 */
16 """
17__author__ = "Minos Galanakis"
18__email__ = "minos.galanakis@linaro.org"
19__project__ = "Trusted Firmware-M Open CI"
20__status__ = "stable"
Minos Galanakisea421232019-06-20 17:11:28 +010021__version__ = "1.1"
Minos Galanakisf4ca6ac2017-12-11 02:39:21 +010022
23import os
Minos Galanakisea421232019-06-20 17:11:28 +010024import re
Karl Zhangaff558a2020-05-15 14:28:23 +010025import time
Minos Galanakisf4ca6ac2017-12-11 02:39:21 +010026import shutil
Minos Galanakisea421232019-06-20 17:11:28 +010027from .utils import *
Minos Galanakisf4ca6ac2017-12-11 02:39:21 +010028from .structured_task import structuredTask
29
30
31class TFM_Builder(structuredTask):
32 """ Wrap around tfm cmake system and spawn a thread to build the project.
33 """
Minos Galanakisf4ca6ac2017-12-11 02:39:21 +010034 def __init__(self,
35 name, # Proccess name
Minos Galanakisf4ca6ac2017-12-11 02:39:21 +010036 work_dir, # Current working directory(ie logs)
37 cfg_dict, # Input config dictionary of the following form
38 # input_dict = {"PROJ_CONFIG": "ConfigRegression",
39 # "TARGET_PLATFORM": "MUSCA_A",
40 # "COMPILER": "ARMCLANG",
41 # "CMAKE_BUILD_TYPE": "Debug"}
Minos Galanakisea421232019-06-20 17:11:28 +010042 build_threads=4, # Number of CPU thrads used in build
43 silent=False, # Silence stdout ouptut
44 img_sizes=False, # Use arm-none-eabi-size for size info
45 relative_paths=False): # Store relative paths in report
Minos Galanakisf4ca6ac2017-12-11 02:39:21 +010046
47 self._tfb_cfg = cfg_dict
48 self._tfb_build_threads = build_threads
Minos Galanakisf4ca6ac2017-12-11 02:39:21 +010049 self._tfb_silent = silent
Minos Galanakisea421232019-06-20 17:11:28 +010050 self._tfb_img_sizes = img_sizes
51 self._tfb_relative_paths = relative_paths
Minos Galanakisf4ca6ac2017-12-11 02:39:21 +010052 self._tfb_binaries = []
53
54 # Required by other methods, always set working directory first
55 self._tfb_work_dir = os.path.abspath(os.path.expanduser(work_dir))
56
Minos Galanakisea421232019-06-20 17:11:28 +010057 # Override code_base_dir with abspath
58 _code_dir = self._tfb_cfg["codebase_root_dir"]
59 self._tfb_code_dir = os.path.abspath(os.path.expanduser(_code_dir))
Minos Galanakisf4ca6ac2017-12-11 02:39:21 +010060 # Entries will be filled after sanity test on cfg_dict dring pre_exec
61 self._tfb_build_dir = None
62 self._tfb_log_f = None
Minos Galanakisea421232019-06-20 17:11:28 +010063
Minos Galanakisf4ca6ac2017-12-11 02:39:21 +010064 super(TFM_Builder, self).__init__(name=name)
65
66 def mute(self):
67 self._tfb_silent = True
68
69 def log(self):
70 """ Print and return the contents of log file """
Minos Galanakisea421232019-06-20 17:11:28 +010071 try:
72 with open(self._tfb_log_f, "r") as F:
73 log = F.read()
74 print(log)
75 return log
76 except FileNotFoundError:
77 print("Log %s not found" % self._tfb_log_f)
78 return ""
Minos Galanakisf4ca6ac2017-12-11 02:39:21 +010079
80 def report(self):
81 """Return the report on the job """
82 return self.unstash("Build Report")
83
84 def pre_eval(self):
85 """ Tests that need to be run in set-up state """
86
Minos Galanakisea421232019-06-20 17:11:28 +010087 if not os.path.isdir(self._tfb_code_dir):
88 print("Missing code-base directory:", self._tfb_code_dir)
Minos Galanakisf4ca6ac2017-12-11 02:39:21 +010089 return False
Minos Galanakisea421232019-06-20 17:11:28 +010090
Minos Galanakisf4ca6ac2017-12-11 02:39:21 +010091 return True
92
93 def pre_exec(self, eval_ret):
94 """ Create all required directories, files if they do not exist """
95
96 self._tfb_build_dir = os.path.join(self._tfb_work_dir,
97 self.get_name())
98 # Ensure we have a clean build directory
99 shutil.rmtree(self._tfb_build_dir, ignore_errors=True)
100
Minos Galanakisf4ca6ac2017-12-11 02:39:21 +0100101 # Log will be placed in work directory, named as the build dir
102 self._tfb_log_f = "%s.log" % self._tfb_build_dir
103
104 # Confirm that the work/build directory exists
105 for p in [self._tfb_work_dir, self._tfb_build_dir]:
106 if not os.path.exists(p):
107 os.makedirs(p)
108
Karl Zhangaff558a2020-05-15 14:28:23 +0100109 def pre_build(self):
110 print("builder start %s \r\nself._tfb_cfg %s\r\n" %
111 (self, self._tfb_cfg))
112
113 try:
114 self._lock.acquire()
115 if self._tfb_code_base_updated:
116 print("Code base has been updated")
117 return True
118
119 self._tfb_code_base_updated = True
120 self._lock.release()
121
122 if "build_psa_api" in self._tfb_cfg:
123 # FF IPC build needs repo manifest update for TFM and PSA arch test
124 if "build_ff_ipc" in self._tfb_cfg:
125 print("Checkout to FF IPC code base")
126 os.chdir(self._tfb_cfg["codebase_root_dir"] + "/../psa-arch-tests/api-tests")
127 _api_test_manifest = "git checkout . ; python3 tools/scripts/manifest_update.py"
128 if subprocess_log(_api_test_manifest,
129 self._tfb_log_f,
130 append=True,
131 prefix=_api_test_manifest,
132 silent=self._tfb_silent):
133
134 raise Exception("Python Failed please check log: %s" %
135 self._tfb_log_f)
136
137 _api_test_manifest_tfm = "python3 tools/tfm_parse_manifest_list.py -m tools/tfm_psa_ff_test_manifest_list.yaml append"
138 os.chdir(self._tfb_cfg["codebase_root_dir"])
139 if subprocess_log(_api_test_manifest_tfm,
140 self._tfb_log_f,
141 append=True,
142 prefix=_api_test_manifest_tfm,
143 silent=self._tfb_silent):
144
145 raise Exception("Python TFM Failed please check log: %s" %
146 self._tfb_log_f)
147 else:
148 print("Checkout to default code base")
149 os.chdir(self._tfb_cfg["codebase_root_dir"] + "/../psa-arch-tests/api-tests")
150 _api_test_manifest = "git checkout ."
151 if subprocess_log(_api_test_manifest,
152 self._tfb_log_f,
153 append=True,
154 prefix=_api_test_manifest,
155 silent=self._tfb_silent):
156
157 raise Exception("Python Failed please check log: %s" %
158 self._tfb_log_f)
159
160 _api_test_manifest_tfm = "python3 tools/tfm_parse_manifest_list.py"
161 os.chdir(self._tfb_cfg["codebase_root_dir"])
162 if subprocess_log(_api_test_manifest_tfm,
163 self._tfb_log_f,
164 append=True,
165 prefix=_api_test_manifest_tfm,
166 silent=self._tfb_silent):
167
168 raise Exception("Python TFM Failed please check log: %s" %
169 self._tfb_log_f)
170 finally:
171 print("python pass after builder prepare")
172
173 p = self._tfb_build_dir + "/BUILD"
174 if not os.path.exists(p):
175 os.makedirs(p)
176
177 os.chdir(p)
178 if subprocess_log(self._tfb_cfg["build_psa_api"],
179 self._tfb_log_f,
180 append=True,
181 prefix=self._tfb_cfg["build_psa_api"],
182 silent=self._tfb_silent):
183
184 raise Exception("Build Failed please check log: %s" %
185 self._tfb_log_f)
186 def copy_tfm(self):
187 """ Copy a new TFM for crypto compile """
188
189 cp_cmd = "cp -r " + self._tfb_cfg["codebase_root_dir"] + " " + \
190 self._tfb_build_dir
191 if subprocess_log(cp_cmd,
192 self._tfb_log_f,
193 append=True,
194 prefix=cp_cmd,
195 silent=self._tfb_silent):
196
197 raise Exception("Build Failed please check log: %s" %
198 self._tfb_log_f)
199
200 cp_cmd = "cp -r " + self._tfb_cfg["codebase_root_dir"] + "/../mbed-crypto " + \
201 self._tfb_build_dir
202 if subprocess_log(cp_cmd,
203 self._tfb_log_f,
204 append=True,
205 prefix=cp_cmd,
206 silent=self._tfb_silent):
207
208 raise Exception("Build Failed please check log: %s" %
209 self._tfb_log_f)
210
211 cp_cmd = "cp -r " + self._tfb_cfg["codebase_root_dir"] + "/../psa-arch-tests " + \
212 self._tfb_build_dir
213 if subprocess_log(cp_cmd,
214 self._tfb_log_f,
215 append=True,
216 prefix=cp_cmd,
217 silent=self._tfb_silent):
218
219 raise Exception("Build Failed please check log: %s" %
220 self._tfb_log_f)
221
Karl Zhangaff558a2020-05-15 14:28:23 +0100222 self._tfb_cfg["build_cmds"][0] = \
223 self._tfb_cfg["build_cmds"][0].replace(self._tfb_cfg["codebase_root_dir"],
224 self._tfb_build_dir + "/tf-m")
225
Minos Galanakisf4ca6ac2017-12-11 02:39:21 +0100226 def task_exec(self):
227 """ Main tasks """
228
229 # Mark proccess running as status
230 self.set_status(-1)
Karl Zhangaff558a2020-05-15 14:28:23 +0100231 print("builder _tfb_cfg %s" % self._tfb_cfg)
232
233 if "build_psa_api" in self._tfb_cfg:
234 p = self._tfb_build_dir + "/BUILD"
235 if not os.path.exists(p):
236 os.makedirs(p)
237 os.chdir(p)
238 if subprocess_log(self._tfb_cfg["build_psa_api"],
239 self._tfb_log_f,
240 append=True,
241 prefix=self._tfb_cfg["build_psa_api"],
242 silent=self._tfb_silent):
243 raise Exception("Build Failed please check log: %s" %
244 self._tfb_log_f)
245
246 if self._tfb_cfg["build_cmds"].__str__().find( \
247 "CRYPTO_HW_ACCELERATOR_OTP_STATE=ENABLED") > 0:
248 self.copy_tfm()
249 time.sleep(15)
250 os.sync()
251 print("new build cmd ", self._tfb_cfg["build_cmds"][0])
252
253 print("Go to build directory")
Minos Galanakisf4ca6ac2017-12-11 02:39:21 +0100254 # Go to build directory
255 os.chdir(self._tfb_build_dir)
Minos Galanakisea421232019-06-20 17:11:28 +0100256
257 build_cmds = self._tfb_cfg["build_cmds"]
258
259 threads_no_rex = re.compile(r'.*(-j\s?(\d+))')
Minos Galanakisf4ca6ac2017-12-11 02:39:21 +0100260
261 # Pass the report to later stages
Minos Galanakisea421232019-06-20 17:11:28 +0100262 rep = {"build_cmd": "%s" % ",".join(build_cmds)}
Minos Galanakisf4ca6ac2017-12-11 02:39:21 +0100263 self.stash("Build Report", rep)
264
Minos Galanakisea421232019-06-20 17:11:28 +0100265 # Calll cmake to configure the project
266 for build_cmd in build_cmds:
267 # if a -j parameter is passed as user argument
268 user_set_threads_match = threads_no_rex.findall(build_cmd)
269
270 if user_set_threads_match:
271 # Unpack the regex groups (fullmatch, decimal match)
272 user_jtxt, user_set_threads = user_set_threads_match[0]
273 if int(user_set_threads) > self._tfb_build_threads:
274 print("Ignoring user requested n=%s threads because it"
275 " exceeds the maximum thread set ( %d )" %
276 (user_set_threads, self._tfb_build_threads))
277 thread_no = self._tfb_build_threads
278 else:
279 print("Using %s build threads" % user_set_threads)
280 thread_no = user_set_threads
281 build_cmd = build_cmd.replace(user_jtxt,
282 "-j %s " % thread_no)
283
Minos Galanakisf4ca6ac2017-12-11 02:39:21 +0100284 # Build it
Karl Zhangaff558a2020-05-15 14:28:23 +0100285 print("~builder build_cmd %s\r\n" % build_cmd)
Minos Galanakisf4ca6ac2017-12-11 02:39:21 +0100286 if subprocess_log(build_cmd,
287 self._tfb_log_f,
288 append=True,
289 prefix=build_cmd,
290 silent=self._tfb_silent):
Minos Galanakisea421232019-06-20 17:11:28 +0100291
Minos Galanakisf4ca6ac2017-12-11 02:39:21 +0100292 raise Exception("Build Failed please check log: %s" %
293 self._tfb_log_f)
Minos Galanakisf4ca6ac2017-12-11 02:39:21 +0100294
Minos Galanakisf4ca6ac2017-12-11 02:39:21 +0100295 self._t_stop()
296
Minos Galanakisf4ca6ac2017-12-11 02:39:21 +0100297 def post_eval(self):
298 """ Verify that the artefacts exist """
299 print("%s Post eval" % self.get_name())
300
301 ret_eval = False
302 rep = self.unstash("Build Report")
Minos Galanakisf4ca6ac2017-12-11 02:39:21 +0100303
Minos Galanakisea421232019-06-20 17:11:28 +0100304 artefacts = list_filtered_tree(self._tfb_work_dir, r'%s' %
305 self._tfb_cfg["artifact_capture_rex"])
Minos Galanakisf4ca6ac2017-12-11 02:39:21 +0100306
307 # Add artefact related information to report
308 rep["log"] = self._tfb_log_f
Minos Galanakisea421232019-06-20 17:11:28 +0100309
310 if not len(artefacts):
311 print("ERROR: Could not capture any binaries:")
312
313 # TODO update self._tfb_binaries
314 ret_eval = False
315 else:
316 print("SUCCESS: Produced the following binaries:")
317 print("\n\t".join(artefacts))
318 ret_eval = True
319
320 rep["artefacts"] = artefacts
321
322 # Proccess the artifacts into file structures
323 art_files = {}
324 for art_item in artefacts:
325 art_f = {"pl_source": 1,
326 "resource": art_item if not self._tfb_relative_paths
327 else resolve_rel_path(art_item),
328 "size": {"bytes": str(os.path.getsize(art_item))}
329 }
330 if self._tfb_img_sizes and ".axf" in art_item:
331 eabi_size, _ = arm_non_eabi_size(art_item)
332 art_f["size"]["text"] = eabi_size["text"]
333 art_f["size"]["data"] = eabi_size["data"]
334 art_f["size"]["bss"] = eabi_size["bss"]
335 # filename is used as key for artfacts
336 art_files[os.path.split(art_item)[-1]] = art_f
337 rep["artefacts"] = art_files
338
339 if "required_artefacts" in self._tfb_cfg.keys():
340 if len(self._tfb_cfg["required_artefacts"]):
341 print("Searching for required binaries")
342 missing_binaries = list(filter(lambda x: not os.path.isfile(x),
343 self._tfb_cfg["required_artefacts"]))
344 if len(missing_binaries):
345 rep["missing_artefacts"] = missing_binaries
346 print("ERROR: Missing required artefacts:")
347 print("\n".join(missing_binaries))
348 ret_eval = False
349 else:
350 ret_eval = True
Minos Galanakisf4ca6ac2017-12-11 02:39:21 +0100351
352 rep["status"] = "Success" if ret_eval else "Failed"
353 self.stash("Build Report", rep)
354 return ret_eval
355
356 def post_exec(self, eval_ret):
357 """ """
358
359 if eval_ret:
360 print("TFM Builder %s was Successful" % self.get_name())
361 else:
362 print("TFM Builder %s was UnSuccessful" % self.get_name())
Minos Galanakisea421232019-06-20 17:11:28 +0100363
364
365if __name__ == "__main__":
366 pass