diff --git a/bsp/nxp/imx/imxrt/imxrt1180-nxp-evk/cm33/board/SConscript b/bsp/nxp/imx/imxrt/imxrt1180-nxp-evk/cm33/board/SConscript index b5ab58e802d3..3b9d91d0d1a9 100644 --- a/bsp/nxp/imx/imxrt/imxrt1180-nxp-evk/cm33/board/SConscript +++ b/bsp/nxp/imx/imxrt/imxrt1180-nxp-evk/cm33/board/SConscript @@ -58,8 +58,8 @@ if GetDepend(['BSP_USE_MULTICORE']): LINKFLAGS += ' --predefine="-D__use_shmem__=1"' LINKFLAGS += ' --predefine="-D__multicore__=1"' if rtconfig.PLATFORM in ['iccarm']: - LINKFLAGS += ' __use_shmem__=1' - LINKFLAGS += ' __multicore__=1' + LINKFLAGS += ' --config_def __use_shmem__=1' + LINKFLAGS += ' --config_def __multicore__=1' if GetDepend(['BSP_USING_ETH']): CPPDEFINES += ['SDK_NETC_USED=1', 'FSL_ETH_ENABLE_CACHE_CONTROL=1'] diff --git a/bsp/nxp/imx/imxrt/imxrt1180-nxp-evk/cm33/rtconfig.py b/bsp/nxp/imx/imxrt/imxrt1180-nxp-evk/cm33/rtconfig.py index ab1be162d20f..d38365c0d1e6 100644 --- a/bsp/nxp/imx/imxrt/imxrt1180-nxp-evk/cm33/rtconfig.py +++ b/bsp/nxp/imx/imxrt/imxrt1180-nxp-evk/cm33/rtconfig.py @@ -61,7 +61,6 @@ 'FLEXSPI_NOR_HYPERRAM': 'MIMXRT1189xxxxx_cm33_flexspi_nor_hyperram', }[_LINKER_SCRIPT_TYPE] - if PLATFORM == 'gcc': PREFIX = 'arm-none-eabi-' CC = PREFIX + 'gcc' @@ -229,25 +228,31 @@ EXEC_PATH = EXEC_PATH + '/arm/bin/' POST_ACTION = 'ielftool --bin $TARGET rtthread.bin' -# Map from linker script type to the matching Keil target name. -_LINKER_SCRIPT_TO_KEIL_TARGET = { +# Map from linker script type to the matching Keil target / IAR configuration name. +# Keil targets and IAR configurations share the same names, so one table serves both. +_LINKER_SCRIPT_TO_PROJECT_TARGET = { 'RAM': 'rtthread_ram', 'FLEXSPI_NOR': 'rtthread_flexspi_nor', 'FLEXSPI_NOR_HYPERRAM': 'rtthread_flexspi_nor_hyperram', } +def _get_active_project_target(): + """Return the Keil target / IAR config name matching the selected linker script.""" + return _LINKER_SCRIPT_TO_PROJECT_TARGET.get(_LINKER_SCRIPT_TYPE, 'rtthread_ram') + def update_keil_active_target(uvoptx_path='project.uvoptx'): """Set in project.uvoptx to match the selected linker script.""" import xml.etree.ElementTree as etree - active = _LINKER_SCRIPT_TO_KEIL_TARGET.get(_LINKER_SCRIPT_TYPE, 'rtthread_ram') + active = _get_active_project_target() if not os.path.exists(uvoptx_path): - return + return active tree = etree.parse(uvoptx_path) root = tree.getroot() + for tgt in tree.findall('Target'): tname = tgt.find('TargetName') is_current = tgt.find('TargetOption/OPTFL/IsCurrentTarget') @@ -261,7 +266,22 @@ def update_keil_active_target(uvoptx_path='project.uvoptx'): print('Keil active target set to: ' + active) + # Return the active target name so the caller (tools/targets/keil.py) can + # build the matching target with UV4.exe instead of the template's first one. + return active + +def iar_get_active_config(): + + """Return the IAR configuration name matching the selected linker script. + + This hook is called by tools/targets/iar.py when generating the IAR + project so that the active configuration follows the Kconfig linker + script selection. It is board-specific and only defined here. + """ + return _get_active_project_target() + def dist_handle(BSP_ROOT, dist_dir): + import sys cwd_path = os.getcwd() # sys.path.append(os.path.join(os.path.dirname(BSP_ROOT), 'tools')) diff --git a/bsp/nxp/imx/imxrt/imxrt1180-nxp-evk/cm7/board/SConscript b/bsp/nxp/imx/imxrt/imxrt1180-nxp-evk/cm7/board/SConscript index f9fa157f1ada..282a90491d97 100644 --- a/bsp/nxp/imx/imxrt/imxrt1180-nxp-evk/cm7/board/SConscript +++ b/bsp/nxp/imx/imxrt/imxrt1180-nxp-evk/cm7/board/SConscript @@ -61,8 +61,8 @@ if GetDepend(['BSP_USE_MULTICORE']): LINKFLAGS += ' --predefine="-D__use_shmem__=1"' LINKFLAGS += ' --predefine="-D__multicore__=1"' if rtconfig.PLATFORM in ['iccarm']: - LINKFLAGS += ' __use_shmem__=1' - LINKFLAGS += ' __multicore__=1' + LINKFLAGS += ' --config_def __use_shmem__=1' + LINKFLAGS += ' --config_def __multicore__=1' if GetDepend(['BSP_USING_ETH']): CPPDEFINES += ['SDK_NETC_USED=1', 'FSL_ETH_ENABLE_CACHE_CONTROL=1'] diff --git a/bsp/nxp/imx/imxrt/imxrt1180-nxp-evk/cm7/rtconfig.py b/bsp/nxp/imx/imxrt/imxrt1180-nxp-evk/cm7/rtconfig.py index 5cd0142118f8..d27c92f50d43 100644 --- a/bsp/nxp/imx/imxrt/imxrt1180-nxp-evk/cm7/rtconfig.py +++ b/bsp/nxp/imx/imxrt/imxrt1180-nxp-evk/cm7/rtconfig.py @@ -242,22 +242,27 @@ EXEC_PATH = EXEC_PATH + '/arm/bin/' POST_ACTION = 'ielftool --bin $TARGET rtthread.bin' -# Map from linker script type to the matching Keil target name. -_LINKER_SCRIPT_TO_KEIL_TARGET = { +# Map from linker script type to the matching Keil target / IAR configuration name. +# Keil targets and IAR configurations share the same names, so one table serves both. +_LINKER_SCRIPT_TO_PROJECT_TARGET = { 'RAM': 'rtthread_ram', 'HYPERRAM': 'rtthread_hyperram', 'FLEXSPI_NOR': 'rtthread_flexspi_nor', 'FLEXSPI_NOR_HYPERRAM': 'rtthread_flexspi_nor_hyperram', } +def _get_active_project_target(): + """Return the Keil target / IAR config name matching the selected linker script.""" + return _LINKER_SCRIPT_TO_PROJECT_TARGET.get(_LINKER_SCRIPT_TYPE, 'rtthread_ram') + def update_keil_active_target(uvoptx_path='project.uvoptx'): """Set in project.uvoptx to match the selected linker script.""" import xml.etree.ElementTree as etree - active = _LINKER_SCRIPT_TO_KEIL_TARGET.get(_LINKER_SCRIPT_TYPE, 'rtthread_ram') + active = _get_active_project_target() if not os.path.exists(uvoptx_path): - return + return active tree = etree.parse(uvoptx_path) root = tree.getroot() @@ -275,6 +280,20 @@ def update_keil_active_target(uvoptx_path='project.uvoptx'): print('Keil active target set to: ' + active) + # Return the active target name so the caller (tools/targets/keil.py) can + # build the matching target with UV4.exe instead of the template's first one. + return active + + +def iar_get_active_config(): + """Return the IAR configuration name matching the selected linker script. + + This hook is called by tools/targets/iar.py when generating the IAR + project so that the active configuration follows the Kconfig linker + script selection. It is board-specific and only defined here. + """ + return _get_active_project_target() + def dist_handle(BSP_ROOT, dist_dir): import sys cwd_path = os.getcwd() diff --git a/tools/targets/iar.py b/tools/targets/iar.py index ee53ad2e0010..9170b038087b 100644 --- a/tools/targets/iar.py +++ b/tools/targets/iar.py @@ -23,6 +23,7 @@ # import os +import re import sys import string import utils @@ -40,14 +41,15 @@ - $WS_DIR$\%s - + $WS_DIR$\%(project)s + %(active_config)s ''' + def IARAddGroup(parent, name, files, project_path): group = SubElement(parent, 'group') group_name = SubElement(group, 'name') @@ -69,11 +71,69 @@ def IARAddGroup(parent, name, files, project_path): else: file_name.text = '$PROJ_DIR$\\' + path # ('$PROJ_DIR$\\' + path).decode(fs_encoding) -def IARWorkspace(target): - # make an workspace +def _update_iar_wsdt(wsdt_path, project_name, active_config): + """Update in the IAR session file to set the active configuration.""" + config_str = '%s/%s' % (project_name, active_config) + + if not os.path.exists(wsdt_path): + os.makedirs(os.path.dirname(wsdt_path), exist_ok=True) + tree = etree.ElementTree(etree.Element('Workspace')) + else: + tree = etree.parse(wsdt_path) + + root = tree.getroot() + cfg_dict = root.find('ConfigDictionary') + if cfg_dict is None: + cfg_dict = SubElement(root, 'ConfigDictionary') + cur_cfgs = cfg_dict.find('CurrentConfigs') + if cur_cfgs is None: + cur_cfgs = SubElement(cfg_dict, 'CurrentConfigs') + proj_elem = cur_cfgs.find('Project') + if proj_elem is None: + proj_elem = SubElement(cur_cfgs, 'Project') + proj_elem.text = config_str + tree.write(wsdt_path, encoding='utf-8', xml_declaration=True) + +def _split_iar_linkflags(linkflags): + tokens = re.findall(r'(?:[^\s"]+|"[^"]*")+', linkflags) + if re.sub(r'\s+', '', ''.join(tokens)) != re.sub(r'\s+', '', linkflags): + raise ValueError('Invalid quoting in IAR linker flags: %s' % linkflags) + + defines = [] + extra = [] + index = 0 + while index < len(tokens): + token = tokens[index] + if token == '--config_def': + index += 1 + if index == len(tokens): + raise ValueError('Missing value for --config_def') + define = tokens[index] + elif token.startswith('--config_def='): + define = token[len('--config_def='):] + else: + extra.append(token) + index += 1 + continue + + if define.startswith('"') and define.endswith('"'): + define = define[1:-1] + if not re.fullmatch(r'[A-Za-z_][A-Za-z0-9_]*=.+', define): + raise ValueError('Invalid IAR configuration definition: %s' % define) + defines.append(define) + index += 1 + + return defines, ' '.join(extra) + +def IARWorkspace(target, active_config=None): + # make an workspace, optionally setting the active configuration workspace = target.replace('.ewp', '.eww') + project_name = os.path.splitext(os.path.basename(target))[0] + active_elem = '' + if active_config: + active_elem = '\n \n %s/%s\n ' % (project_name, active_config) out = open(workspace, 'w') - xml = iar_workspace % target + xml = iar_workspace % {'project': target, 'active_config': active_elem} out.write(xml) out.close() @@ -83,10 +143,9 @@ def IARProject(env, target, script): tree = etree.parse('template.ewp') root = tree.getroot() - out = open(target, 'w') - CPPPATH = [] CPPDEFINES = env.get('CPPDEFINES', []) + LOCAL_CPPDEFINES = [] LINKFLAGS = '' CFLAGS = '' @@ -118,7 +177,7 @@ def searchLib(group): # get each group's link flags if 'LINKFLAGS' in group and group['LINKFLAGS']: - LINKFLAGS += group['LINKFLAGS'] + LINKFLAGS += ' ' + group['LINKFLAGS'] if 'LIBS' in group and group['LIBS']: for item in group['LIBS']: @@ -137,6 +196,16 @@ def searchLib(group): paths[inc] = None # 使用 dict 去重并保持插入顺序 paths = list(paths.keys()) + config_defines, extra_linkflags = _split_iar_linkflags(LINKFLAGS) + + if config_defines or extra_linkflags: + for config in tree.findall('configuration'): + names = [option.findtext('name') for option in config.findall('settings/data/option')] + if config_defines and 'IlinkConfigDefines' not in names: + raise ValueError('IAR configuration has no IlinkConfigDefines option') + if extra_linkflags and ('IlinkUseExtraOptions' not in names or 'IlinkExtraOptions' not in names): + raise ValueError('IAR configuration has no linker extra options') + # setting options options = tree.findall('configuration/settings/data/option') for option in options: @@ -160,6 +229,23 @@ def searchLib(group): state = SubElement(option, 'state') state.text = define + if name.text == 'IlinkConfigDefines': + for define in config_defines: + state = SubElement(option, 'state') + state.text = define + + if name.text == 'IlinkUseExtraOptions' and extra_linkflags: + state = option.find('state') + if state is None: + state = SubElement(option, 'state') + state.text = '1' + + if name.text == 'IlinkExtraOptions' and extra_linkflags: + state = option.find('state') + if state is None: + state = SubElement(option, 'state') + state.text = ' '.join(filter(None, [state.text, extra_linkflags])) + if name.text == 'IlinkAdditionalLibs': for path in Libs: state = SubElement(option, 'state') @@ -170,10 +256,49 @@ def searchLib(group): state.text = path xml_indent(root) - out.write(etree.tostring(root, encoding='utf-8').decode()) - out.close() - - IARWorkspace(target) + with open(target, 'w') as out: + out.write(etree.tostring(root, encoding='utf-8').decode()) + + # Determine the active configuration from the BSP via an optional board-specific hook. + active_config = None + try: + import rtconfig + if hasattr(rtconfig, 'iar_get_active_config'): + active_config = rtconfig.iar_get_active_config() + except Exception as e: + print('Warning: could not get IAR active config: %s' % e) + + IARWorkspace(target, active_config) + + # update settings/project.wsdt to set the active configuration + if active_config: + wsdt_path = os.path.join('settings', os.path.splitext(os.path.basename(target))[0] + '.wsdt') + + project_name = os.path.splitext(os.path.basename(target))[0] + _update_iar_wsdt(wsdt_path, project_name, active_config) + + # The IAR IDE keeps the workspace/session state in memory and writes it + # back on close, which can overwrite the active configuration we just + # generated. Remind the user to close the workspace before regenerating. + print('IAR active configuration set to: %s' % active_config) + print('Note: if the IAR workspace (.eww) is currently open, close it ' + 'before regenerating the project, otherwise the active target ' + 'will not be updated.') + + # copy template.ewd (debugger settings) and template.ewt (build settings) to project files. + # The template sources are always template.ewd / template.ewt in the project directory; + # do not derive them from the output project name, otherwise a custom --project-name + # would make source and destination identical and trigger shutil.SameFileError. + import shutil + ewd_template = os.path.join(project_path, 'template.ewd') + ewd_target = target.replace('.ewp', '.ewd') + if os.path.exists(ewd_template) and os.path.abspath(ewd_template) != os.path.abspath(ewd_target): + shutil.copy2(ewd_template, ewd_target) + + ewt_template = os.path.join(project_path, 'template.ewt') + ewt_target = target.replace('.ewp', '.ewt') + if os.path.exists(ewt_template) and os.path.abspath(ewt_template) != os.path.abspath(ewt_target): + shutil.copy2(ewt_template, ewt_target) def IARPath(): import rtconfig diff --git a/tools/targets/keil.py b/tools/targets/keil.py index 5dcd8959fadb..033fc988b820 100644 --- a/tools/targets/keil.py +++ b/tools/targets/keil.py @@ -231,11 +231,15 @@ def MDK45Project(env, tree, target, script): CFLAGS = '' ProjectFiles = [] - # add group - groups = tree.find('Targets/Target/Groups') + import copy + + # add groups to the first target; they will be copied to all other targets afterward + first_target = tree.find('Targets/Target') + groups = first_target.find('Groups') if groups is None: - groups = SubElement(tree.find('Targets/Target'), 'Groups') + groups = SubElement(first_target, 'Groups') groups.clear() # clean old groups + for group in script: group_tree = MDK4AddGroup(ProjectFiles, groups, group['name'], group['src'], project_path, group) @@ -284,25 +288,39 @@ def MDK45Project(env, tree, target, script): else: group_tree = MDK4AddGroupForFN(ProjectFiles, groups, group['name'], full_path, project_path) - # write include path, definitions and link flags - IncludePath = tree.find('Targets/Target/TargetOption/TargetArmAds/Cads/VariousControls/IncludePath') # Keep the same include path precedence as the SCons build environment. paths = [_make_path_relative(project_path, os.path.normpath(i)) for i in CPPPATH] - IncludePath.text = ';'.join(dict.fromkeys(paths)) - - Define = tree.find('Targets/Target/TargetOption/TargetArmAds/Cads/VariousControls/Define') - Define.text = ', '.join(set(CPPDEFINES)) - - if 'c99' in CXXFLAGS or 'c99' in CCFLAGS or 'c99' in CFLAGS: - uC99 = tree.find('Targets/Target/TargetOption/TargetArmAds/Cads/uC99') - uC99.text = '1' - - if 'gnu' in CXXFLAGS or 'gnu' in CCFLAGS or 'gnu' in CFLAGS: - uGnu = tree.find('Targets/Target/TargetOption/TargetArmAds/Cads/uGnu') - uGnu.text = '1' - - Misc = tree.find('Targets/Target/TargetOption/TargetArmAds/LDads/Misc') - Misc.text = LINKFLAGS + include_path_text = ';'.join(dict.fromkeys(paths)) + define_text = ', '.join(set(CPPDEFINES)) + + for target_node in tree.findall('Targets/Target'): + if target_node is not first_target: + existing_groups = target_node.find('Groups') + if existing_groups is not None: + target_node.remove(existing_groups) + target_node.append(copy.deepcopy(groups)) + + inc = target_node.find('TargetOption/TargetArmAds/Cads/VariousControls/IncludePath') + if inc is not None: + inc.text = include_path_text + + dfn = target_node.find('TargetOption/TargetArmAds/Cads/VariousControls/Define') + if dfn is not None: + dfn.text = define_text + + if 'c99' in CXXFLAGS or 'c99' in CCFLAGS or 'c99' in CFLAGS: + uC99 = target_node.find('TargetOption/TargetArmAds/Cads/uC99') + if uC99 is not None: + uC99.text = '1' + + if 'gnu' in CXXFLAGS or 'gnu' in CCFLAGS or 'gnu' in CFLAGS: + uGnu = target_node.find('TargetOption/TargetArmAds/Cads/uGnu') + if uGnu is not None: + uGnu.text = '1' + + misc = target_node.find('TargetOption/TargetArmAds/LDads/Misc') + if misc is not None: + misc.text = LINKFLAGS xml_indent(root) out.write(etree.tostring(root, encoding='utf-8').decode()) @@ -364,18 +382,37 @@ def MDK5Project(env, target, script): # copy uvopt file if os.path.exists('template.uvoptx'): import shutil - shutil.copy2('template.uvoptx', '{}.uvoptx'.format(os.path.splitext(target)[0])) - # build with UV4.exe + project_uvoptx = '{}.uvoptx'.format(os.path.splitext(target)[0]) + shutil.copy2('template.uvoptx', project_uvoptx) + + # Set the active target from the BSP via an optional board-specific hook. + # The hook returns the active target name so we build the matching target. + active_target_name = None + try: + import rtconfig + if hasattr(rtconfig, 'update_keil_active_target'): + active_target_name = rtconfig.update_keil_active_target(project_uvoptx) + except Exception as e: + print('Warning: could not set Keil active target: %s' % e) + # build with UV4.exe if shutil.which('UV4.exe') is not None: - target_name = template_tree.find('Targets/Target/TargetName') - print('target_name:', target_name.text) + # Prefer the active target reported by the BSP hook; fall back to the + # template's first target only when no hook is available. + if not active_target_name: + target_name = template_tree.find('Targets/Target/TargetName') + active_target_name = target_name.text + print('target_name:', active_target_name) + + # Use the actual generated project file name (honors --project-name). + uvprojx_name = os.path.basename(target) + log_file_path = 'keil.log' if os.path.exists(log_file_path): os.remove(log_file_path) log_thread = threading.Thread(target=monitor_log_file, args=(log_file_path,)) log_thread.start() - cmd = 'UV4.exe -b project.uvprojx -q -j0 -t '+ target_name.text +' -o '+log_file_path + cmd = 'UV4.exe -b '+ uvprojx_name +' -q -j0 -t '+ active_target_name +' -o '+log_file_path print('Start to build keil project') print(cmd) os.system(cmd)