1#!/usr/bin/env python3 2# coding=utf-8 3 4# Copyright (c) 2020 HiSilicon (Shanghai) Technologies CO., LIMITED. 5# Licensed under the Apache License, Version 2.0 (the "License"); 6# you may not use this file except in compliance with the License. 7# You may obtain a copy of the License at 8# 9# http://www.apache.org/licenses/LICENSE-2.0 10# 11# Unless required by applicable law or agreed to in writing, software 12# distributed under the License is distributed on an "AS IS" BASIS, 13# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14# See the License for the specific language governing permissions and 15# limitations under the License. 16 17import os 18import shutil 19import sys 20import time 21 22from utils.build_utils import exec_shell, root_path, output_root, sdk_output_path, pkg_tools_path 23from utils.build_utils import compare_bin 24from enviroment import TargetEnvironment, BuildEnvironment 25from pack_tool import packTool 26from sdk_generator.sdk_generator import SdkGenerator 27from rom_ram_callback.gen_rom_ram_callback import gen_rom_ram_callback 28from rom_ram_callback.strip_undef_symbols import strip_undefined_symbols 29from usr_config import mconfig 30from custom_cmd import run_custom_cmd 31from target_config.common_config import CommonConfig 32 33class CMakeBuilder(BuildEnvironment): 34 """ cmake builder, 接收并解析参数,启动构建 35 """ 36 def __init__(self, param_list): 37 super(CMakeBuilder, self).__init__(param_list) 38 self.cmake_cmd = [] 39 self.sdk = None 40 self.pack_tool = None 41 42 def get_component(self, env): 43 com_config = CommonConfig(env.get('arch')) 44 components = [] 45 if env.get('just_build_components', False): 46 just_build_components = env.get('just_build_components', False) 47 else: 48 just_build_components = self.component 49 for component in just_build_components: 50 if component in env.get('ram_component', False): 51 components.append(component) 52 continue 53 if component not in env.get('ram_component_set', False): 54 continue 55 for comm in com_config.get_component_set(component): 56 if comm in env.get('ram_component', False): 57 components.append(comm) 58 return components 59 60 def get_build_cmd(self, env): 61 ext_cmd = [] 62 components = self.get_component(env) 63 ext_cmd.extend(components) 64 if self.generator == 'Ninja': 65 return ['ninja'] + ext_cmd + ['-j%d' % self.thread] 66 else: 67 return ['make'] + ext_cmd + ['-j%d' % self.thread] 68 69 def build(self): 70 """ 71 """ 72 built_targets = [] 73 for group in self.group_names: 74 need_pack = False 75 if group.startswith('pack'): 76 need_pack = True 77 chip = self.get_chip_name(group) 78 if os.path.exists(os.path.join(output_root, 'package', chip, group)): 79 shutil.rmtree(os.path.join(output_root, 'package', chip, group)) 80 81 for target in self.get_target_names_by_group_name(group): 82 self.pack_tool = packTool(group, target) 83 if self.is_group(target): 84 self.group_names.append(target) 85 continue 86 87 if self.is_copy_target(target): 88 self.pack_tool.pack() 89 continue 90 91 if target == 'fwpkg' and need_pack: 92 self.pack_fwpkg(chip, group) 93 continue 94 95 if not self.is_target(target): 96 print("Invalid target %s" % target) 97 raise 98 if not target in built_targets: 99 self.build_target(target) 100 built_targets.append(target) 101 else: 102 print("%s has built, skip" % target) 103 target_env = TargetEnvironment(target, self.extr_defines) 104 if target_env.get('build_type') == 'SDK': 105 continue 106 if need_pack: 107 self.pack_tool.pack() 108 109 if len(self.target_names) == 1 and self.open_kconfig == True: 110 self.menuconfig_to_build(self.target_names[0]) 111 return 112 113 for target in self.target_names: 114 self.build_target(target) 115 116 def menuconfig_to_build(self, target): 117 env = TargetEnvironment(target) 118 mconfig(self.kconfig_param, env.get("chip"), env.get("core"), target, None) 119 120 def build_target(self, target): 121 env = TargetEnvironment(target, self.extr_defines) 122 if env.get('build_type') == 'SDK': 123 self.build_sdk(env) 124 return 125 if not run_custom_cmd(env, target, 'build_pre'): 126 self.print_build_result(target, 1) 127 sys.exit(1) 128 129 self.compile_target(target, env) 130 131 if env.get('just_build_components'): 132 return 133 134 if env.get("fs_image"): 135 fs_image_path = os.path.join(root_path, 'build', 'config', 'target_config', env.get('chip'), 'mk_fs_image') 136 fs_script_path = os.path.join(fs_image_path, 'mkyaffs2tool.py') 137 output_path = env.get_output_path() 138 print(output_path) 139 errcode = exec_shell([self.python_path, fs_script_path, output_path], None, True) 140 if errcode != 0: 141 print("creat fs image error!") 142 self.print_build_result(target, errcode) 143 sys.exit(1) 144 print("fs image success!") 145 146 if env.get('upg_pkg'): 147 self.pack_fota(env.get('chip'), target, env.get('upg_pkg')) 148 149 if not run_custom_cmd(env, target, 'build_post'): 150 self.print_build_result(target, 1) 151 sys.exit(1) 152 153 if env.get('packet') : 154 self.pack_fwpkg(env.get('chip'), target) 155 156 def build_sdk(self, env): 157 self.sdk = SdkGenerator(env, sdk_output_path) 158 if os.path.exists(sdk_output_path): 159 print("Cleaning SDK output path") 160 shutil.rmtree(sdk_output_path) 161 sdk_pkg_target_name = env.get('pkg_target_name', cmake_type=False) 162 163 for sdk_target in sdk_pkg_target_name: 164 sdk_target_env = TargetEnvironment(sdk_target, self.extr_defines) 165 output_path = sdk_target_env.get_output_path() 166 self.compile_target(sdk_target, sdk_target_env) 167 self.sdk.copy_depends('%s/cmake_trace.txt' % output_path) 168 if self.sdk.env.get('reload_kconfig'): 169 cwd = os.getcwd() 170 os.chdir(sdk_output_path) 171 mconfig('reloadconfig', sdk_target_env.get("chip"), sdk_target_env.get("core"), sdk_target, None, root=sdk_output_path) 172 os.chdir(cwd) 173 174 if env.get('config') and 'lib_gen' in env.get('config'): 175 lib_gen_tasks = env.get('config')['lib_gen'] 176 print(lib_gen_tasks) 177 for libs in lib_gen_tasks: 178 task = lib_gen_tasks[libs] 179 target_name = task['base_target_name'] 180 defines = task.get("defines", []) 181 self.component = task['components'] 182 target_env = TargetEnvironment(target_name, extra_defines=defines) 183 target_env.add("LIB_GEN_NAME", libs) 184 target_env.extend("SDK_LIBGEN_COMPONENTS", self.component) 185 self.compile_target(target_name, target_env) 186 chip = env.get('chip') 187 self.sdk.genarate_efuse_cfg_bin(chip) 188 self.sdk.sdk_build(self.build_time, self.no_hso, self.build_level) 189 self.sdk = None 190 self.component = [] 191 192 def compile_target(self, target_name, env): 193 start_time = time.time() 194 self.deal_symbol_link(env) 195 env.add('build_level', self.build_level) 196 if self.build_level == "debug": 197 env.extend("ccflags", ['-funwind-tables', '-fasynchronous-unwind-tables']) 198 env.extend("defines", "NO_TIMEOUT") 199 if self.build_as_lib: 200 env.add("GEN_ONLY_LIB_PATH") 201 self.cmake_cmd = ['cmake', '-G', self.generator, '-Wno-dev', '--no-warn-unused-cli', '-DCMAKE_C_COMPILER_WORKS=TRUE', '-DCMAKE_CXX_COMPILER_WORKS=TRUE'] 202 if env.get('fp_enable'): 203 env.append('defines', 'SUPPORT_CALLSTACK') 204 env.append('ccflags', '-fno-omit-frame-pointer') 205 self.add_build_param(env) 206 207 output_path = env.get_output_path() 208 self.pre_sdk(output_path, env) 209 if env.get('libstd_option'): 210 self.add_cmake_def(env, 'std_libs') 211 self.cmake_cmd.append(root_path) 212 213 if env.get('product_type'): 214 self.cmake_cmd.append('-DPRODUCT_TYPE={0}'.format(env.get('product_type'))) 215 else: 216 self.cmake_cmd.append('-DPRODUCT_TYPE=default') 217 218 if self.dump: 219 env.dump() 220 221 if self.rom_callback(env, target_name, output_path): 222 if not os.path.exists(output_path): 223 os.makedirs(output_path) 224 env.set('build_rom_callback', False) 225 env.append('defines', '_PRE_FEATURE_VENEER_ROM') 226 env.append('ram_component', 'rom_callback') 227 self.redef_cmake_def(env, 'build_rom_callback') 228 self.redef_cmake_def(env, 'defines') 229 self.redef_cmake_def(env, 'ram_component') 230 self.add_cmake_param("-DROM_CHECK=False") 231 self.start(env, target_name, output_path, clean=self.need_clean, nhso=self.no_hso) 232 self.rom_check(env, target_name, output_path) 233 234 end_time = time.time() 235 print("%s takes %f s" % (target_name, end_time - start_time)) 236 237 def deal_symbol_link(self, env): 238 if "rom_sym_path" not in env.config: 239 return 240 if self.no_symbol_link and not self.sdk: 241 env.config["rom_sym_path"] = "" 242 else: 243 env.config["rom_sym_path"] = env.config["rom_sym_path"].replace("<root>", root_path) 244 if os.path.exists(env.config["rom_sym_path"]): 245 env.config["linkflags"].append("-Wl,--just-symbols=" + env.config["rom_sym_path"]) 246 env.config["defines"].append("ROM_SYMBOL_LINK") 247 else: 248 print("ERROR: rom_sym_path %s is not exists" % env.config["rom_sym_path"]) 249 if "bootrom_sym_path" not in env.config: 250 return 251 if env.config["bootrom_sym_path"] != "": 252 env.config["bootrom_sym_path"] = env.config["bootrom_sym_path"].replace("<root>", root_path) 253 if os.path.exists(env.config["bootrom_sym_path"]): 254 env.config["linkflags"].append("-Wl,--just-symbols=" + env.config["bootrom_sym_path"]) 255 env.config["defines"].append("BOOTROM_SYMBOL_LINK") 256 else: 257 print("ERROR: bootrom_sym_path %s is not exists" % env.config["bootrom_sym_path"]) 258 259 def add_build_param(self, env): 260 for item in env.config: 261 self.add_cmake_def(env, item) 262 self.add_cmake_param('-DCMAKE_TOOLCHAIN_FILE=%s' % env.get_tool_chain()) 263 self.add_cmake_param('-DPY_PATH=%s' % self.python_path) 264 if self.build_time != '': 265 self.add_cmake_param('-DBUILD_TIME=%s' % self.build_time) 266 267 def pre_sdk(self, output_path, env): 268 if not self.sdk: 269 return 270 271 self.sdk.register_org_target_path(output_path) 272 self.add_cmake_param('-DSDK_OUTPUT_PATH=%s' % sdk_output_path) 273 self.add_cmake_def(self.sdk.env, 'sdk_type') 274 # closed_components takes precedence over open_components 275 closed_components = self.sdk.env.get('closed_components', cmake_type=False) 276 open_components = self.sdk.env.get('open_components', cmake_type=False) 277 if None not in (closed_components, open_components): 278 raise Exception(f'ERROR! closed_components and open_components cannot be set together!') 279 self.add_cmake_def(self.sdk.env, 'closed_components') 280 if not closed_components: 281 self.add_cmake_def(self.sdk.env, 'open_components') 282 283 self.add_cmake_def(env, 'main_component') 284 285 # export trace of cmake invocation 286 cmake_trace_file = os.path.join(output_path, 'cmake_trace.txt') 287 self.cmake_cmd.append('--trace-format=json-v1') 288 self.cmake_cmd.append('--trace-redirect=%s' % cmake_trace_file) 289 290 def print_build_result(self, target_name, fail): 291 print("######### Build target:%s %s" % (target_name, "failed" if fail else "success")) 292 if self.sdk: 293 print("######### Build sdk %s!!" % ("failed" if fail else "success")) 294 295 def rom_callback(self, env, target_name, output_path): 296 if not env.get('build_rom_callback'): 297 return False 298 if env.get('fixed_rom'): 299 return True 300 self.start(env, target_name, output_path, nhso=True, clean=self.need_clean) 301 print("GENERATING AUTO ROM BIN FILE!!!") 302 chip = env.get("chip") 303 core = env.get("core") 304 board = env.get('board') 305 application = env.get('application') 306 arch = env.get('arch') 307 rom_config = os.path.join(root_path, 'drivers', 'chips', chip, 'rom_config', core) 308 rom_output = os.path.join(rom_config, 'output') 309 if not os.path.exists(rom_output): 310 os.makedirs(rom_output) 311 312 shutil.copy(os.path.join(output_path, "rom_bin_raw.undef"), rom_output) 313 shutil.copy(os.path.join(output_path, "rom_symbol.list"), rom_output) 314 shutil.copy(os.path.join(output_path, "rom_bin.rel"), rom_output) 315 shutil.copy(os.path.join(output_path, "image_symbol.list"), rom_output) 316 shutil.copy(os.path.join(rom_config, "undef_rom_filter.list"), rom_output) 317 shutil.copy(os.path.join(rom_config, "region.list"), rom_output) 318 319 strip_undefined_symbols(rom_output) 320 if arch[:5] == "riscv": 321 target = "riscv32" 322 elif arch[:3] == "arm": 323 target = "arm32" 324 gen_rom_ram_callback(target, rom_output) 325 print("ROM CALLBACK BUILD SUCCESSFULLY!!!") 326 shutil.rmtree(output_path) 327 return True 328 329 def start(self, env, target_name, output_path, nhso=None, clean=None): 330 # remember the root folder 331 def _excute(cmd, log_file, is_dump): 332 errcode = exec_shell(cmd, log_file, is_dump) 333 if errcode != 0: 334 self.print_build_result(target_name, errcode) 335 sys.exit(1) 336 org_work_path = os.getcwd() 337 self.cmake_cmd.append('-DPKG_TARGET_NAME=%s' % target_name) 338 target_name = target_name.replace('-', "_") 339 if clean and os.path.exists(output_path): 340 shutil.rmtree(output_path) 341 if not os.path.exists(output_path): 342 os.makedirs(output_path) 343 self.cmake_cmd.append('-DBUILD_TARGET_NAME=%s' % (target_name)) 344 self.cmake_cmd.append('-DNHSO=%s' %nhso) 345 chip = env.get('chip') 346 core = env.get('core') 347 348 # Generate the menuconfig header. 349 mconfig_file_path = os.path.join(root_path, 'build', 'config', 'target_config', chip, 'menuconfig', core, f"{target_name}.config") 350 if os.path.exists(mconfig_file_path): 351 if env.get('reload_kconfig'): 352 print("build reload %s" %mconfig_file_path) 353 mconfig("reloadconfig", chip, core, target_name, output_path) 354 mconfig("savemenuconfig", chip, core, target_name, output_path) 355 356 os.chdir(output_path) 357 log_file_name = "build_%s.log" % target_name 358 log_file = os.path.join(output_root, chip, log_file_name) 359 360 # run 'make' 361 if self.is_command_refresh(output_path): 362 self.dump_cmake_command(output_path) 363 _excute(self.cmake_cmd, log_file, self.dump) 364 _excute(self.get_build_cmd(env), log_file, self.dump) 365 else: 366 ext_cmd = [] 367 components_target = self.get_component(env) 368 print(components_target) 369 if components_target: 370 ext_cmd = ['--target'] + components_target 371 _excute(["cmake", "--build", output_path, '-j%d' % self.thread] + ext_cmd, log_file, self.dump) 372 373 if len(self.get_component(env)) > 0: 374 os.chdir(org_work_path) 375 print("######### Build target:%s, component:[%s] success" % (target_name, ' '.join(self.get_component(env)))) 376 return 0 377 378 if env.is_enable_hso() and not nhso: 379 cmd = self.get_build_cmd(env) 380 cmd.append("HSO_DB") 381 _excute(cmd, log_file, self.dump) 382 383 # switch the work folder back 384 os.chdir(org_work_path) 385 self.print_build_result(target_name, 0) 386 return 0 387 388 def add_cmake_param(self, param): 389 """ 390 accept string only 391 """ 392 self.cmake_cmd.append(param) 393 394 def add_cmake_def(self, env, item): 395 if env.get(item) is None or env.get(item) == '': 396 return 397 self.cmake_cmd.append('-D%s=%s' % (item.upper(), env.get(item))) 398 399 def redef_cmake_def(self, env, item): 400 if env.get(item) is None or env.get(item) == '': 401 return 402 val = env.get(item) 403 item = item.upper() 404 for i, para in enumerate(self.cmake_cmd): 405 if not para.startswith('-D%s=' % item): 406 continue 407 self.cmake_cmd[i] = '-D%s=%s' % (item, val) 408 break 409 410 def rom_check(self, env, target_name, output_path): 411 if env.get('rom_sym_path'): 412 return True 413 414 if env.get('fixed_rom'): 415 fix_path = env.get('fixed_rom_path').replace('<root>', root_path) 416 bin1 = os.path.join(output_path, '%s_rom.bin' % env.get('bin_name')) 417 bin2 = fix_path 418 if not compare_bin(bin1, bin2): 419 print(f"ERROR! :{bin1} is not same with {bin2}") 420 sys.exit(1) 421 return True 422 423 if env.get('rom_ram_check'): 424 self.add_cmake_param("-DROM_CHECK=True") 425 self.start(env, target_name, output_path, clean=False, nhso=True) 426 if not env.get('rom_ram_compare'): 427 return True 428 bin1 = os.path.join(output_path, '%s_rom.bin' % env.get('bin_name')) 429 bin2 = os.path.join(output_path, '%s_romcheck_rom.bin' % env.get('bin_name')) 430 if not compare_bin(bin1, bin2): 431 print(f"ERROR! :{bin1} is not same with {bin2}") 432 sys.exit(1) 433 434 def pack_fwpkg(self, chip, target_name): 435 # bin packet all in one 436 packet_script_path = os.path.join(pkg_tools_path, 'packet.py') 437 print("packet ing...") 438 errcode = exec_shell([self.python_path, packet_script_path, chip, target_name, " ".join(self.extr_defines)], None, True) 439 if errcode != 0: 440 print("packet error!") 441 self.print_build_result(target_name, errcode) 442 sys.exit(1) 443 print("packet success!") 444 445 def pack_fota(self, chip, target_name, option): 446 fota_script_path = os.path.join(root_path, 'build', 'config', 'target_config', chip, 'build_' + chip + '_update.py') 447 print("fota packet generating...") 448 errcode = exec_shell([self.python_path, fota_script_path, target_name, option], None, True) 449 if errcode != 0: 450 print("fota packet error!") 451 self.print_build_result(target_name, errcode) 452 sys.exit(1) 453 print("fota packet success!") 454 455 def dump_cmake_command(self, output_path): 456 with open(os.path.join(output_path, 'cmake_command.txt'), "w") as f: 457 f.write("\n".join(self.cmake_cmd)) 458 459 def is_command_refresh(self, output_path): 460 cmd_path = os.path.join(output_path, 'cmake_command.txt') 461 if not os.path.exists(cmd_path): 462 return True 463 with open(cmd_path, "r") as f: 464 text = f.read() 465 return text != "\n".join(self.cmake_cmd) 466