• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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