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/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..9a7b3fb09aa2 100644 --- a/tools/targets/iar.py +++ b/tools/targets/iar.py @@ -40,14 +40,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 +70,47 @@ 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): + # create a minimal wsdt if it does not exist yet + os.makedirs(os.path.dirname(wsdt_path), exist_ok=True) + content = '\n\n \n \n %s\n \n \n\n' % config_str + with open(wsdt_path, 'w') as f: + f.write(content) + return + + try: + tree = etree.parse(wsdt_path) + root = tree.getroot() + proj_elem = root.find('ConfigDictionary/CurrentConfigs/Project') + if proj_elem is not None: + proj_elem.text = config_str + else: + # create the elements if missing + 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 = SubElement(cur_cfgs, 'Project') + proj_elem.text = config_str + tree.write(wsdt_path, encoding='unicode', xml_declaration=True) + except Exception as e: + print('Warning: could not update %s: %s' % (wsdt_path, e)) + +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() @@ -87,6 +124,7 @@ def IARProject(env, target, script): CPPPATH = [] CPPDEFINES = env.get('CPPDEFINES', []) + LOCAL_CPPDEFINES = [] LINKFLAGS = '' CFLAGS = '' @@ -160,6 +198,13 @@ def searchLib(group): state = SubElement(option, 'state') state.text = define + if name.text == 'IlinkConfigDefines': + # write bare symbol=value tokens from LINKFLAGS as IAR linker defines + import re + for token in re.findall(r'\S+', LINKFLAGS): + state = SubElement(option, 'state') + state.text = token + if name.text == 'IlinkAdditionalLibs': for path in Libs: state = SubElement(option, 'state') @@ -173,7 +218,46 @@ def searchLib(group): out.write(etree.tostring(root, encoding='utf-8').decode()) out.close() - IARWorkspace(target) + # 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 452f273dffd4..823146d29a54 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,23 +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') - IncludePath.text = ';'.join([_make_path_relative(project_path, os.path.normpath(i)) for i in set(CPPPATH)]) + # write include path, definitions and link flags for all targets in the template + include_path_text = ';'.join([_make_path_relative(project_path, os.path.normpath(i)) for i in set(CPPPATH)]) + define_text = ', '.join(set(CPPDEFINES)) + + for target_node in tree.findall('Targets/Target'): + # copy groups from the first target to all other targets so they share the same source file list + 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 - Define = tree.find('Targets/Target/TargetOption/TargetArmAds/Cads/VariousControls/Define') - Define.text = ', '.join(set(CPPDEFINES)) + 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 = tree.find('Targets/Target/TargetOption/TargetArmAds/Cads/uC99') - uC99.text = '1' + 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 = tree.find('Targets/Target/TargetOption/TargetArmAds/Cads/uGnu') - uGnu.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 = tree.find('Targets/Target/TargetOption/TargetArmAds/LDads/Misc') - Misc.text = LINKFLAGS + 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()) @@ -362,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)