Skip to content

Commit 27bcc3e

Browse files
committed
[tool][eclipse]:Add project-name support and enhance folder linking
- Add project-name support for Eclipse target, allowing users to specify custom project names via the --project-name option - Enhance .project file update mechanism to properly handle PROJECT_SOURCE_FOLDERS defined in rtconfig.py - Support linking external folders located at different physical locations - Improve path handling with Eclipse standard path variables (PARENT-* and PROJECT_LOC) for better project portability
1 parent 0fc5f25 commit 27bcc3e

File tree

1 file changed

+136
-37
lines changed

1 file changed

+136
-37
lines changed

tools/targets/eclipse.py

Lines changed: 136 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
# Date Author Notes
88
# 2019-03-21 Bernard the first version
99
# 2019-04-15 armink fix project update error
10+
# 2025-09-01 wdfk-prog Add project-name support for Eclipse target and enhance folder linking
1011
#
1112

1213
import glob
@@ -355,32 +356,73 @@ def HandleToolOption(tools, env, project, reset):
355356

356357
return
357358

358-
359359
def UpdateProjectStructure(env, prj_name):
360-
bsp_root = env['BSP_ROOT']
361-
rtt_root = env['RTT_ROOT']
362-
363-
project = etree.parse('.project')
364-
root = project.getroot()
365-
366-
if rtt_root.startswith(bsp_root):
367-
linkedResources = root.find('linkedResources')
368-
if linkedResources == None:
369-
linkedResources = SubElement(root, 'linkedResources')
370-
371-
links = linkedResources.findall('link')
372-
# delete all RT-Thread folder links
373-
for link in links:
374-
if link.find('name').text.startswith('rt-thread'):
375-
linkedResources.remove(link)
376-
377-
if prj_name:
378-
name = root.find('name')
379-
if name == None:
380-
name = SubElement(root, 'name')
381-
name.text = prj_name
360+
"""
361+
Updates the .project file to link any external/specified folders from
362+
PROJECT_SOURCE_FOLDERS in rtconfig.py. This version correctly handles
363+
all specified paths, regardless of their physical location.
364+
"""
365+
bsp_root = os.path.abspath(env['BSP_ROOT'])
366+
367+
# --- 1. Read the list of folders to be linked from rtconfig.py ---
368+
folders_to_link = []
369+
try:
370+
import rtconfig
371+
if hasattr(rtconfig, 'PROJECT_SOURCE_FOLDERS') and rtconfig.PROJECT_SOURCE_FOLDERS:
372+
folders_to_link = rtconfig.PROJECT_SOURCE_FOLDERS
373+
except ImportError:
374+
pass # It's okay if the file or variable doesn't exist.
382375

383-
out = open('.project', 'w')
376+
if not os.path.exists('.project'):
377+
print("Error: .project file not found. Cannot update.")
378+
return
379+
380+
project_xml = etree.parse('.project')
381+
root = project_xml.getroot()
382+
383+
# --- 2. Ensure the <linkedResources> node exists ---
384+
linkedResources = root.find('linkedResources')
385+
if linkedResources is None:
386+
linkedResources = SubElement(root, 'linkedResources')
387+
388+
# --- 3. Clean up previously managed links to prevent duplicates ---
389+
managed_link_names = [os.path.basename(p) for p in folders_to_link]
390+
for link in list(linkedResources.findall('link')): # Use list() to safely remove items while iterating
391+
name_element = link.find('name')
392+
if name_element is not None and name_element.text in managed_link_names:
393+
linkedResources.remove(link)
394+
print(f"Removed existing linked resource '{name_element.text}' to regenerate it.")
395+
396+
# --- 4. Create new links for each folder specified by the user ---
397+
for folder_path in folders_to_link:
398+
# The link name in the IDE will be the directory's base name.
399+
link_name = os.path.basename(folder_path)
400+
401+
# Resolve the absolute path of the folder to be linked.
402+
abs_folder_path = os.path.abspath(os.path.join(bsp_root, folder_path))
403+
404+
print(f"Creating linked resource for '{link_name}' pointing to '{abs_folder_path}'...")
405+
406+
# Calculate the URI relative to the Eclipse ${PROJECT_LOC} variable.
407+
# This works for both internal and external folders.
408+
relative_link_path = os.path.relpath(abs_folder_path, bsp_root).replace('\\', '/')
409+
410+
# Use Eclipse path variables for robustness. PARENT_LOC is more standard for external folders.
411+
if relative_link_path.startswith('../'):
412+
# Count how many levels up
413+
levels_up = relative_link_path.count('../')
414+
clean_path = relative_link_path.replace('../', '')
415+
location_uri = f'PARENT-{levels_up}-PROJECT_LOC/{clean_path}'
416+
else:
417+
location_uri = f'PROJECT_LOC/{relative_link_path}'
418+
419+
link_element = SubElement(linkedResources, 'link')
420+
SubElement(link_element, 'name').text = link_name
421+
SubElement(link_element, 'type').text = '2' # Type 2 means a folder link.
422+
SubElement(link_element, 'locationURI').text = location_uri
423+
424+
# --- 5. Write the updated content back to the .project file ---
425+
out = open('.project', 'w', encoding='utf-8')
384426
out.write('<?xml version="1.0" encoding="UTF-8"?>\n')
385427
xml_indent(root)
386428
out.write(etree.tostring(root, encoding='utf-8').decode('utf-8'))
@@ -492,6 +534,57 @@ def HandleExcludingOption(entry, sourceEntries, excluding):
492534

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

537+
def UpdateProjectName(prj_name):
538+
"""
539+
Regardless of whether the .project file exists, make sure its name is correct.
540+
"""
541+
if not prj_name:
542+
return
543+
544+
try:
545+
if not os.path.exists('.project'):
546+
if rt_studio.gen_project_file(os.path.abspath(".project"), prj_name) is False:
547+
print('Fail!')
548+
return
549+
print("Generated .project file with name:", prj_name)
550+
551+
project_tree = etree.parse('.project')
552+
root = project_tree.getroot()
553+
name_element = root.find('name')
554+
555+
if name_element is not None and name_element.text != prj_name:
556+
print(f"Updating project name from '{name_element.text}' to '{prj_name}'...")
557+
name_element.text = prj_name
558+
559+
project_tree.write('.project', encoding='UTF-8', xml_declaration=True)
560+
xml_indent(root)
561+
with open('.project', 'w', encoding='utf-8') as f:
562+
f.write('<?xml version="1.0" encoding="UTF-8"?>\n')
563+
f.write(etree.tostring(root, encoding='utf-8').decode('utf-8'))
564+
565+
except Exception as e:
566+
print("Error updating .project file:", e)
567+
568+
def HandleSourceEntries_Global(sourceEntries, excluding):
569+
"""
570+
Configure the project to include the root folder ("") and exclude all
571+
files/folders in the 'excluding' list. This makes all project folders
572+
visible in the IDE.
573+
"""
574+
# To keep the configuration clean, first remove all existing entries
575+
for entry in sourceEntries.findall('entry'):
576+
sourceEntries.remove(entry)
577+
578+
# Join the exclusion list into a single string with the '|' separator
579+
excluding_str = '|'.join(sorted(excluding))
580+
581+
# Create a new, single entry for the project root directory
582+
SubElement(sourceEntries, 'entry', {
583+
'flags': 'VALUE_WORKSPACE_PATH|RESOLVED',
584+
'kind': 'sourcePath',
585+
'name': "", # An empty string "" represents the project root
586+
'excluding': excluding_str
587+
})
495588

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

600+
if prj_name:
601+
config_element = cconfiguration.find('storageModule/configuration')
602+
if config_element is not None:
603+
config_element.set('artifactName', prj_name)
604+
507605
sourceEntries = cconfiguration.find('storageModule/configuration/sourceEntries')
508-
if sourceEntries != None:
509-
entry = sourceEntries.find('entry')
510-
HandleExcludingOption(entry, sourceEntries, excluding)
511-
# update refreshScope
606+
if sourceEntries is not None:
607+
# Call the new global handler function for source entries
608+
HandleSourceEntries_Global(sourceEntries, excluding)
609+
610+
# update refreshScope to ensure the project refreshes correctly
512611
if prj_name:
513-
prj_name = '/' + prj_name
612+
prj_name_for_path = '/' + prj_name
514613
configurations = root.findall('storageModule/configuration')
515614
for configuration in configurations:
516615
resource = configuration.find('resource')
517-
configuration.remove(resource)
518-
SubElement(configuration, 'resource', {'resourceType': "PROJECT", 'workspacePath': prj_name})
616+
if resource is not None:
617+
configuration.remove(resource)
618+
SubElement(configuration, 'resource', {'resourceType': "PROJECT", 'workspacePath': prj_name_for_path})
519619

520-
# write back to .cproject
620+
# write back to .cproject file
521621
out = open('.cproject', 'w')
522622
out.write('<?xml version="1.0" encoding="UTF-8" standalone="no"?>\n')
523623
out.write('<?fileVersion 4.0.0?>')
524624
xml_indent(root)
525625
out.write(etree.tostring(root, encoding='utf-8').decode('utf-8'))
526626
out.close()
527627

528-
529-
def TargetEclipse(env, reset=False, prj_name=None):
628+
def TargetEclipse(env, project, reset=False, prj_name=None):
530629
global source_pattern
531630

631+
UpdateProjectName(prj_name)
632+
532633
print('Update eclipse setting...')
533634

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

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

574-
project = ProjectInfo(env)
575-
576675
# update the project file structure info on '.project' file
577676
UpdateProjectStructure(env, prj_name)
578677

0 commit comments

Comments
 (0)