Skip to content
Open
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 25 additions & 13 deletions tools/building.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
# 2025-01-05 Bernard Add logging as Env['log']
# 2025-03-02 ZhaoCake Add MkDist_Strip
# 2025-01-05 Assistant Refactor SCons PreProcessor patch to independent class
# 2025-09-01 wdfk-prog Add project-name option for custom project naming in IDE targets

import os
import sys
Expand Down Expand Up @@ -802,19 +803,21 @@ def local_group(group, objects):

EndBuilding(target, program)

def GenTargetProject(program = None):
def GenTargetProject(program = None, project_name = None):
if project_name is None:
project_name = GetOption('project-name')

if GetOption('target') in ['mdk', 'mdk4', 'mdk5']:
from targets.keil import MDK2Project, MDK4Project, MDK5Project, ARMCC_Version

if os.path.isfile('template.uvprojx') and GetOption('target') not in ['mdk4']: # Keil5
MDK5Project(Env, GetOption('project-name') + '.uvprojx', Projects)
MDK5Project(Env, project_name + '.uvprojx', Projects)
print("Keil5 project is generating...")
elif os.path.isfile('template.uvproj') and GetOption('target') not in ['mdk5']: # Keil4
MDK4Project(Env, GetOption('project-name') + '.uvproj', Projects)
MDK4Project(Env, project_name + '.uvproj', Projects)
print("Keil4 project is generating...")
elif os.path.isfile('template.Uv2') and GetOption('target') not in ['mdk4', 'mdk5']: # Keil2
MDK2Project(Env, GetOption('project-name') + '.Uv2', Projects)
MDK2Project(Env, project_name + '.Uv2', Projects)
print("Keil2 project is generating...")
else:
print ('No template project file found.')
Expand All @@ -825,20 +828,20 @@ def GenTargetProject(program = None):
if GetOption('target') == 'iar':
from targets.iar import IARProject, IARVersion
print("IAR Version: " + IARVersion())
IARProject(Env, GetOption('project-name') + '.ewp', Projects)
IARProject(Env, project_name + '.ewp', Projects)
print("IAR project has generated successfully!")

if GetOption('target') == 'vs':
from targets.vs import VSProject
VSProject(GetOption('project-name') + '.vcproj', Projects, program)
VSProject(project_name + '.vcproj', Projects, program)

if GetOption('target') == 'vs2012':
from targets.vs2012 import VS2012Project
VS2012Project(GetOption('project-name') + '.vcxproj', Projects, program)
VS2012Project(project_name + '.vcxproj', Projects, program)

if GetOption('target') == 'cb':
from targets.codeblocks import CBProject
CBProject(GetOption('project-name') + '.cbp', Projects, program)
CBProject(project_name + '.cbp', Projects, program)

if GetOption('target') == 'ua':
from targets.ua import PrepareUA
Expand All @@ -857,7 +860,7 @@ def GenTargetProject(program = None):

if GetOption('target') == 'cdk':
from targets.cdk import CDKProject
CDKProject(GetOption('project-name') + '.cdkproj', Projects)
CDKProject(project_name + '.cdkproj', Projects)

if GetOption('target') == 'ses':
from targets.ses import SESProject
Expand All @@ -869,15 +872,17 @@ def GenTargetProject(program = None):

if GetOption('target') == 'eclipse':
from targets.eclipse import TargetEclipse
TargetEclipse(Env, GetOption('reset-project-config'), GetOption('project-name'))
from utils import ProjectInfo
project = ProjectInfo(Env)
TargetEclipse(Env, project, GetOption('reset-project-config'), project_name)

if GetOption('target') == 'codelite':
from targets.codelite import TargetCodelite
TargetCodelite(Projects, program)

if GetOption('target') == 'cmake' or GetOption('target') == 'cmake-armclang':
from targets.cmake import CMakeProject
CMakeProject(Env, Projects, GetOption('project-name'))
CMakeProject(Env, Projects, project_name)

if GetOption('target') == 'xmake':
from targets.xmake import XMakeProject
Expand Down Expand Up @@ -912,13 +917,20 @@ def EndBuilding(target, program = None):
Clean(target, 'rtua.pyc')
Clean(target, '.sconsign.dblite')

# Priority: 1. PROJECT_NAME in rtconfig.py ->
# 2. --project-name command line option ->
# 3. SCons default value ('project')
Comment on lines +920 to +922
Copy link
Preview

Copilot AI Sep 1, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

English: The comment describes priority logic that doesn't match the implementation. The code sets project_name from GetOption first, then overwrites it if rtconfig.PROJECT_NAME exists, which makes rtconfig.PROJECT_NAME higher priority, not GetOption. Chinese: 注释描述的优先级逻辑与实现不匹配。代码首先从GetOption设置project_name,然后如果rtconfig.PROJECT_NAME存在就覆盖它,这使得rtconfig.PROJECT_NAME具有更高的优先级,而不是GetOption。

Copilot generated this review using guidance from repository custom instructions.

project_name = GetOption('project-name')
if hasattr(rtconfig, 'PROJECT_NAME') and rtconfig.PROJECT_NAME:
project_name = rtconfig.PROJECT_NAME


if GetOption('target'):
Comment on lines +924 to 928
Copy link
Preview

Copilot AI Sep 1, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

English: There are unnecessary blank lines (lines 926-927) that should be reduced to a single blank line for better code formatting. Chinese: 有不必要的空行(第926-927行),应该减少为单个空行以获得更好的代码格式。

Copilot generated this review using guidance from repository custom instructions.

GenTargetProject(program)
GenTargetProject(program, project_name)
need_exit = True

BSP_ROOT = Dir('#').abspath

project_name = GetOption('project-name')
project_path = GetOption('project-path')

# 合并处理打包相关选项
Expand Down
173 changes: 136 additions & 37 deletions tools/targets/eclipse.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
# Date Author Notes
# 2019-03-21 Bernard the first version
# 2019-04-15 armink fix project update error
# 2025-09-01 wdfk-prog Add project-name support for Eclipse target and enhance folder linking
#

import glob
Expand Down Expand Up @@ -355,32 +356,73 @@ def HandleToolOption(tools, env, project, reset):

return


def UpdateProjectStructure(env, prj_name):
bsp_root = env['BSP_ROOT']
rtt_root = env['RTT_ROOT']

project = etree.parse('.project')
root = project.getroot()

if rtt_root.startswith(bsp_root):
linkedResources = root.find('linkedResources')
if linkedResources == None:
linkedResources = SubElement(root, 'linkedResources')

links = linkedResources.findall('link')
# delete all RT-Thread folder links
for link in links:
if link.find('name').text.startswith('rt-thread'):
linkedResources.remove(link)

if prj_name:
name = root.find('name')
if name == None:
name = SubElement(root, 'name')
name.text = prj_name
"""
Updates the .project file to link any external/specified folders from
PROJECT_SOURCE_FOLDERS in rtconfig.py. This version correctly handles
all specified paths, regardless of their physical location.
"""
bsp_root = os.path.abspath(env['BSP_ROOT'])

# --- 1. Read the list of folders to be linked from rtconfig.py ---
folders_to_link = []
try:
import rtconfig
if hasattr(rtconfig, 'PROJECT_SOURCE_FOLDERS') and rtconfig.PROJECT_SOURCE_FOLDERS:
folders_to_link = rtconfig.PROJECT_SOURCE_FOLDERS
except ImportError:
pass # It's okay if the file or variable doesn't exist.

out = open('.project', 'w')
if not os.path.exists('.project'):
print("Error: .project file not found. Cannot update.")
return

project_xml = etree.parse('.project')
root = project_xml.getroot()

# --- 2. Ensure the <linkedResources> node exists ---
linkedResources = root.find('linkedResources')
if linkedResources is None:
linkedResources = SubElement(root, 'linkedResources')

# --- 3. Clean up previously managed links to prevent duplicates ---
managed_link_names = [os.path.basename(p) for p in folders_to_link]
for link in list(linkedResources.findall('link')): # Use list() to safely remove items while iterating
name_element = link.find('name')
if name_element is not None and name_element.text in managed_link_names:
linkedResources.remove(link)
print(f"Removed existing linked resource '{name_element.text}' to regenerate it.")

# --- 4. Create new links for each folder specified by the user ---
for folder_path in folders_to_link:
# The link name in the IDE will be the directory's base name.
link_name = os.path.basename(folder_path)

# Resolve the absolute path of the folder to be linked.
abs_folder_path = os.path.abspath(os.path.join(bsp_root, folder_path))

print(f"Creating linked resource for '{link_name}' pointing to '{abs_folder_path}'...")

# Calculate the URI relative to the Eclipse ${PROJECT_LOC} variable.
# This works for both internal and external folders.
relative_link_path = os.path.relpath(abs_folder_path, bsp_root).replace('\\', '/')

# Use Eclipse path variables for robustness. PARENT_LOC is more standard for external folders.
if relative_link_path.startswith('../'):
# Count how many levels up
levels_up = relative_link_path.count('../')
clean_path = relative_link_path.replace('../', '')
location_uri = f'PARENT-{levels_up}-PROJECT_LOC/{clean_path}'
Comment on lines +412 to +415
Copy link
Preview

Copilot AI Sep 1, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

English: The indentation is inconsistent. Line 413 has extra spaces before the comment, while line 415 has proper indentation. Chinese: 缩进不一致。第413行注释前有额外的空格,而第415行有正确的缩进。

Copilot generated this review using guidance from repository custom instructions.

else:
location_uri = f'PROJECT_LOC/{relative_link_path}'

link_element = SubElement(linkedResources, 'link')
SubElement(link_element, 'name').text = link_name
SubElement(link_element, 'type').text = '2' # Type 2 means a folder link.
SubElement(link_element, 'locationURI').text = location_uri

# --- 5. Write the updated content back to the .project file ---
out = open('.project', 'w', encoding='utf-8')
out.write('<?xml version="1.0" encoding="UTF-8"?>\n')
xml_indent(root)
out.write(etree.tostring(root, encoding='utf-8').decode('utf-8'))
Expand Down Expand Up @@ -492,6 +534,57 @@ def HandleExcludingOption(entry, sourceEntries, excluding):

SubElement(sourceEntries, 'entry', {'excluding': value, 'flags': 'VALUE_WORKSPACE_PATH|RESOLVED', 'kind':'sourcePath', 'name':""})

def UpdateProjectName(prj_name):
"""
Regardless of whether the .project file exists, make sure its name is correct.
"""
if not prj_name:
return

try:
if not os.path.exists('.project'):
if rt_studio.gen_project_file(os.path.abspath(".project"), prj_name) is False:
print('Fail!')
return
print("Generated .project file with name:", prj_name)

project_tree = etree.parse('.project')
root = project_tree.getroot()
name_element = root.find('name')

if name_element is not None and name_element.text != prj_name:
print(f"Updating project name from '{name_element.text}' to '{prj_name}'...")
name_element.text = prj_name

project_tree.write('.project', encoding='UTF-8', xml_declaration=True)
xml_indent(root)
with open('.project', 'w', encoding='utf-8') as f:
f.write('<?xml version="1.0" encoding="UTF-8"?>\n')
f.write(etree.tostring(root, encoding='utf-8').decode('utf-8'))
Comment on lines +595 to +598
Copy link
Preview

Copilot AI Sep 1, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

English: Line 562 has extra spaces before the f.write call, creating inconsistent indentation within the with block. Chinese: 第562行在f.write调用前有额外的空格,在with块内创建了不一致的缩进。

Copilot generated this review using guidance from repository custom instructions.


except Exception as e:
print("Error updating .project file:", e)

def HandleSourceEntries_Global(sourceEntries, excluding):
"""
Configure the project to include the root folder ("") and exclude all
files/folders in the 'excluding' list. This makes all project folders
visible in the IDE.
"""
# To keep the configuration clean, first remove all existing entries
for entry in sourceEntries.findall('entry'):
sourceEntries.remove(entry)

# Join the exclusion list into a single string with the '|' separator
excluding_str = '|'.join(sorted(excluding))

# Create a new, single entry for the project root directory
SubElement(sourceEntries, 'entry', {
'flags': 'VALUE_WORKSPACE_PATH|RESOLVED',
'kind': 'sourcePath',
'name': "", # An empty string "" represents the project root
'excluding': excluding_str
})

def UpdateCproject(env, project, excluding, reset, prj_name):
excluding = sorted(excluding)
Expand All @@ -504,31 +597,39 @@ def UpdateCproject(env, project, excluding, reset, prj_name):
tools = cconfiguration.findall('storageModule/configuration/folderInfo/toolChain/tool')
HandleToolOption(tools, env, project, reset)

if prj_name:
config_element = cconfiguration.find('storageModule/configuration')
if config_element is not None:
config_element.set('artifactName', prj_name)

sourceEntries = cconfiguration.find('storageModule/configuration/sourceEntries')
if sourceEntries != None:
entry = sourceEntries.find('entry')
HandleExcludingOption(entry, sourceEntries, excluding)
# update refreshScope
if sourceEntries is not None:
# Call the new global handler function for source entries
HandleSourceEntries_Global(sourceEntries, excluding)

# update refreshScope to ensure the project refreshes correctly
if prj_name:
prj_name = '/' + prj_name
prj_name_for_path = '/' + prj_name
configurations = root.findall('storageModule/configuration')
for configuration in configurations:
resource = configuration.find('resource')
configuration.remove(resource)
SubElement(configuration, 'resource', {'resourceType': "PROJECT", 'workspacePath': prj_name})
if resource is not None:
configuration.remove(resource)
SubElement(configuration, 'resource', {'resourceType': "PROJECT", 'workspacePath': prj_name_for_path})

# write back to .cproject
# write back to .cproject file
out = open('.cproject', 'w')
out.write('<?xml version="1.0" encoding="UTF-8" standalone="no"?>\n')
out.write('<?fileVersion 4.0.0?>')
xml_indent(root)
out.write(etree.tostring(root, encoding='utf-8').decode('utf-8'))
out.close()


def TargetEclipse(env, reset=False, prj_name=None):
def TargetEclipse(env, project, reset=False, prj_name=None):
global source_pattern

UpdateProjectName(prj_name)

print('Update eclipse setting...')

# generate cproject file
Expand Down Expand Up @@ -561,7 +662,7 @@ def TargetEclipse(env, reset=False, prj_name=None):
# enable lowwer .s file compiled in eclipse cdt
if not os.path.exists('.settings/org.eclipse.core.runtime.prefs'):
if rt_studio.gen_org_eclipse_core_runtime_prefs(
os.path.abspath(".settings/org.eclipse.core.runtime.prefs")) is False:
os.path.abspath(".settings/org.eclipse.core.runtime.prefs")) is False:
print('Fail!')
return

Expand All @@ -571,8 +672,6 @@ def TargetEclipse(env, reset=False, prj_name=None):
print('Fail!')
return

project = ProjectInfo(env)

# update the project file structure info on '.project' file
UpdateProjectStructure(env, prj_name)

Expand Down
6 changes: 4 additions & 2 deletions tools/targets/rt_studio.py
Original file line number Diff line number Diff line change
Expand Up @@ -328,16 +328,18 @@ def gen_cproject_file(output_file_path):
return False


def gen_project_file(output_file_path):
def gen_project_file(output_file_path, project_name):
try:
w_str = project_temp
w_str = project_temp.replace('__project_name_flag__', project_name)

dir_name = os.path.dirname(output_file_path)
if not os.path.exists(dir_name):
os.makedirs(dir_name)
with open(output_file_path, 'w') as f:
f.write(w_str)
return True
except Exception as e:
print(e)
return False


Expand Down