Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
7 changes: 7 additions & 0 deletions tools/building.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,8 @@ def PrepareBuilding(env, root_directory, has_libcpu=False, remove_components = [
AddOptions()

Env = env
# export the default environment
Export('env')

# prepare logging and set log
logging.basicConfig(level=logging.INFO, format="%(message)s")
Expand Down Expand Up @@ -89,6 +91,7 @@ def PrepareBuilding(env, root_directory, has_libcpu=False, remove_components = [
'vs':('msvc', 'cl'),
'vs2012':('msvc', 'cl'),
'vsc' : ('gcc', 'gcc'),
'vsc_workspace':('gcc', 'gcc'),
'cb':('keil', 'armcc'),
'ua':('gcc', 'gcc'),
'cdk':('gcc', 'gcc'),
Expand Down Expand Up @@ -848,6 +851,10 @@ def GenTargetProject(program = None):
from vscpyocd import GenerateVSCodePyocdConfig
GenerateVSCodePyocdConfig(GetOption('cmsispack'))

if GetOption('target') == 'vsc_workspace':
from targets.vsc import GenerateVSCodeWorkspace
GenerateVSCodeWorkspace(Env)

if GetOption('target') == 'cdk':
from targets.cdk import CDKProject
CDKProject(GetOption('project-name') + '.cdkproj', Projects)
Expand Down
2 changes: 1 addition & 1 deletion tools/options.py
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ def AddOptions():
AddOption('--target',
dest = 'target',
type = 'string',
help = 'set target project: mdk/mdk4/mdk5/iar/vs/vsc/ua/cdk/ses/makefile/eclipse/codelite/cmake')
help = 'set target project: mdk/mdk4/mdk5/iar/vs/vsc/ua/cdk/ses/makefile/eclipse/codelite/cmake/vsc_workspace')
AddOption('--cmsispack',
dest = 'cmsispack',
type = 'string',
Expand Down
184 changes: 161 additions & 23 deletions tools/targets/vsc.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@
# 2018-05-30 Bernard The first version
# 2023-03-03 Supperthomas Add the vscode workspace config file
# 2024-12-13 Supperthomas covert compile_commands.json to vscode workspace file
# 2025-07-05 Bernard Add support for generating .vscode/c_cpp_properties.json
# and .vscode/settings.json files
"""
Utils for VSCode
"""
Expand All @@ -30,6 +32,7 @@
import json
import utils
import rtconfig
from SCons.Script import GetLaunchDir

from utils import _make_path_relative
def find_first_node_with_two_children(tree):
Expand Down Expand Up @@ -62,7 +65,7 @@ def build_tree(paths):
tree = {}
current_working_directory = os.getcwd()
current_folder_name = os.path.basename(current_working_directory)
#过滤异常和不存在的路径
# Filter out invalid and non-existent paths
relative_dirs = []
for path in paths:
normalized_path = os.path.normpath(path)
Expand All @@ -79,7 +82,6 @@ def print_tree(tree, indent=''):
print(indent + key)
print_tree(subtree, indent + ' ')


def extract_source_dirs(compile_commands):
source_dirs = set()

Expand All @@ -89,7 +91,7 @@ def extract_source_dirs(compile_commands):
if file_path.endswith('.c'):
dir_path = os.path.dirname(file_path)
source_dirs.add(dir_path)
# command 或者arguments
# command or arguments
command = entry.get('command') or entry.get('arguments')

if isinstance(command, str):
Expand All @@ -104,7 +106,7 @@ def extract_source_dirs(compile_commands):
elif part.startswith('/I'):
include_dir = part[2:] if len(part) > 2 else parts[i + 1]
source_dirs.add(os.path.abspath(include_dir))
#print(f"Source Directories: {source_dirs}")

return sorted(source_dirs)


Expand All @@ -113,8 +115,7 @@ def is_path_in_tree(path, tree):
current_level = tree
found_first_node = False
root_key = list(tree.keys())[0]
#print(root_key)
#print(path)

index_start = parts.index(root_key)
length = len(parts)
try:
Expand Down Expand Up @@ -154,7 +155,6 @@ def generate_code_workspace_file(source_dirs,command_json_path,root_path):
}
}
workspace_filename = f'{current_folder_name}.code-workspace'
# print(workspace_data)
with open(workspace_filename, 'w') as f:
json.dump(workspace_data, f, indent=4)

Expand Down Expand Up @@ -192,8 +192,6 @@ def command_json_to_workspace(root_path,command_json_path):
if not is_path_in_tree(dir_path, filtered_tree):
exclude_fold.add(dir_path)

#print("Excluded Folders:")
#print(exclude_fold)
generate_code_workspace_file(exclude_fold,command_json_path,root_path)

def delete_repeatelist(data):
Expand All @@ -208,22 +206,21 @@ def GenerateCFiles(env):
if not os.path.exists('.vscode'):
os.mkdir('.vscode')

vsc_file = open('.vscode/c_cpp_properties.json', 'w')
if vsc_file:
with open('.vscode/c_cpp_properties.json', 'w') as vsc_file:
info = utils.ProjectInfo(env)

cc = os.path.join(rtconfig.EXEC_PATH, rtconfig.CC)
cc = os.path.abspath(cc).replace('\\', '/')

config_obj = {}
config_obj['name'] = 'rt-thread'
config_obj['name'] = 'Linux'
config_obj['defines'] = info['CPPDEFINES']

intelliSenseMode = 'gcc-arm'
intelliSenseMode = 'linux-gcc-arm'
if cc.find('aarch64') != -1:
intelliSenseMode = 'gcc-arm64'
intelliSenseMode = 'linux-gcc-arm64'
elif cc.find('arm') != -1:
intelliSenseMode = 'gcc-arm'
intelliSenseMode = 'linux-gcc-arm'
config_obj['intelliSenseMode'] = intelliSenseMode
config_obj['compilerPath'] = cc
config_obj['cStandard'] = "c99"
Expand All @@ -243,7 +240,6 @@ def GenerateCFiles(env):
json_obj['configurations'] = [config_obj]

vsc_file.write(json.dumps(json_obj, ensure_ascii=False, indent=4))
vsc_file.close()

"""
Generate vscode.code-workspace files by build/compile_commands.json
Expand All @@ -255,8 +251,7 @@ def GenerateCFiles(env):
"""
Generate vscode.code-workspace files
"""
vsc_space_file = open('vscode.code-workspace', 'w')
if vsc_space_file:
with open('vscode.code-workspace', 'w') as vsc_space_file:
info = utils.ProjectInfo(env)
path_list = []
for i in info['CPPPATH']:
Expand All @@ -275,7 +270,6 @@ def GenerateCFiles(env):
json_obj = {}
path_list = delete_repeatelist(path_list)
path_list = sorted(path_list, key=lambda x: x["path"])
target_path_list = []
for path in path_list:
if path['path'] != '.':
normalized_path = path['path'].replace('\\', os.path.sep)
Expand All @@ -290,7 +284,7 @@ def GenerateCFiles(env):
]
}
vsc_space_file.write(json.dumps(json_obj, ensure_ascii=False, indent=4))
vsc_space_file.close()

return

def GenerateProjectFiles(env):
Expand All @@ -301,8 +295,7 @@ def GenerateProjectFiles(env):
os.mkdir('.vscode')

project = env['project']
vsc_file = open('.vscode/project.json', 'w')
if vsc_file:
with open('.vscode/project.json', 'w') as vsc_file:
groups = []
for group in project:
if len(group['src']) > 0:
Expand All @@ -326,7 +319,6 @@ def GenerateProjectFiles(env):

# write groups to project.json
vsc_file.write(json.dumps(json_dict, ensure_ascii=False, indent=4))
vsc_file.close()

return

Expand All @@ -338,3 +330,149 @@ def GenerateVSCode(env):
print('Done!')

return

import os

def find_rtconfig_dirs(bsp_dir, project_dir):
"""
Search for subdirectories containing 'rtconfig.h' under 'bsp_dir' (up to 4 levels deep), excluding 'project_dir'.

Args:
bsp_dir (str): The root directory to search (absolute path).
project_dir (str): The subdirectory to exclude from the search (absolute path).

Returns
list: A list of absolute paths to subdirectories containing 'rtconfig.h'.
"""

result = []
project_dir = os.path.normpath(project_dir)

# list the bsp_dir to add result
list = os.listdir(bsp_dir)
for item in list:
Comment on lines +352 to +353
Copy link

Copilot AI Jul 6, 2025

Choose a reason for hiding this comment

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

[nitpick] Variable name 'list' shadows the built-in type. Rename it (e.g., to 'entries' or 'items') to avoid confusion.

Suggested change
list = os.listdir(bsp_dir)
for item in list:
entries = os.listdir(bsp_dir)
for item in entries:

Copilot uses AI. Check for mistakes.
item = os.path.join(bsp_dir, item)

# if item is a directory
if not os.path.isdir(item):
continue

# print(item, project_dir)
if not project_dir.startswith(item):
result.append(os.path.abspath(item))

parent_dir = os.path.dirname(project_dir)

if parent_dir != bsp_dir:
list = os.listdir(parent_dir)
for item in list:
item = os.path.join(parent_dir, item)
rtconfig_path = os.path.join(item, 'rtconfig.h')
if os.path.isfile(rtconfig_path):
abs_path = os.path.abspath(item)
if abs_path != project_dir:
result.append(abs_path)

# print(result)
return result

def GenerateVSCodeWorkspace(env):
"""
Generate vscode.code files
"""
print('Update workspace files for VSCode...')

# get the launch directory
cwd = GetLaunchDir()

# get .vscode/workspace.json file
workspace_file = os.path.join(cwd, '.vscode', 'workspace.json')
if not os.path.exists(workspace_file):
print('Workspace file not found, skip generating.')
return

try:
# read the workspace file
with open(workspace_file, 'r') as f:
workspace_data = json.load(f)

# get the bsp directories from the workspace data, bsps/folder
bsp_dir = os.path.join(cwd, workspace_data.get('bsps', {}).get('folder', ''))
if not bsp_dir:
print('No BSP directories found in the workspace file, skip generating.')
return
Comment on lines +400 to +403
Copy link

Copilot AI Jul 6, 2025

Choose a reason for hiding this comment

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

This check never triggers because os.path.join(cwd, '') returns cwd, not an empty string. Consider verifying the 'folder' key exists or use os.path.exists to detect missing BSP directory.

Suggested change
bsp_dir = os.path.join(cwd, workspace_data.get('bsps', {}).get('folder', ''))
if not bsp_dir:
print('No BSP directories found in the workspace file, skip generating.')
return
folder = workspace_data.get('bsps', {}).get('folder')
if not folder:
print('No BSP folder specified in the workspace file, skip generating.')
return
bsp_dir = os.path.join(cwd, folder)
if not os.path.exists(bsp_dir):
print(f'BSP directory "{bsp_dir}" does not exist, skip generating.')
return

Copilot uses AI. Check for mistakes.
except Exception as e:
print('Error reading workspace file, skip generating.')
return
Comment on lines +404 to +406
Copy link

Copilot AI Jul 6, 2025

Choose a reason for hiding this comment

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

Catching the base Exception silently may mask errors. Catch more specific exceptions (e.g., IOError, json.JSONDecodeError) or log the exception details for easier debugging.

Suggested change
except Exception as e:
print('Error reading workspace file, skip generating.')
return
except FileNotFoundError as e:
print(f'Workspace file not found: {e}, skip generating.')
return
except json.JSONDecodeError as e:
print(f'Error decoding JSON from workspace file: {e}, skip generating.')
return

Copilot uses AI. Check for mistakes.

# check if .vscode folder exists, if not, create it
if not os.path.exists(os.path.join(cwd, '.vscode')):
os.mkdir(os.path.join(cwd, '.vscode'))

with open(os.path.join(cwd, '.vscode/c_cpp_properties.json'), 'w') as vsc_file:
info = utils.ProjectInfo(env)

cc = os.path.join(rtconfig.EXEC_PATH, rtconfig.CC)
cc = os.path.abspath(cc).replace('\\', '/')

config_obj = {}
config_obj['name'] = 'Linux'
config_obj['defines'] = info['CPPDEFINES']

intelliSenseMode = 'linux-gcc-arm'
if cc.find('aarch64') != -1:
intelliSenseMode = 'linux-gcc-arm64'
elif cc.find('arm') != -1:
intelliSenseMode = 'linux-gcc-arm'
config_obj['intelliSenseMode'] = intelliSenseMode
config_obj['compilerPath'] = cc
config_obj['cStandard'] = "c99"
config_obj['cppStandard'] = "c++11"

# format "a/b," to a/b. remove first quotation mark("),and remove end (",)
includePath = []
for i in info['CPPPATH']:
if i[0] == '\"' and i[len(i) - 2:len(i)] == '\",':
includePath.append(_make_path_relative(cwd, i[1:len(i) - 2]))
else:
includePath.append(_make_path_relative(cwd, i))
# make sort for includePath
includePath = sorted(includePath, key=lambda x: x.lower())
config_obj['includePath'] = includePath

json_obj = {}
json_obj['configurations'] = [config_obj]

vsc_file.write(json.dumps(json_obj, ensure_ascii=False, indent=4))

# generate .vscode/settings.json
vsc_settings = {}
settings_path = os.path.join(cwd, '.vscode/settings.json')
if os.path.exists(settings_path):
with open(settings_path, 'r') as f:
# read the existing settings file and load to vsc_settings
vsc_settings = json.load(f)

with open(settings_path, 'w') as vsc_file:
vsc_settings['files.exclude'] = {
"**/__pycache__": True,
"tools/kconfig-frontends": True,
}

result = find_rtconfig_dirs(bsp_dir, os.getcwd())
if result:
# sort the result
result = sorted(result, key=lambda x: x.lower())
for item in result:
# make the path relative to the current working directory
rel_path = os.path.relpath(item, cwd)
# add the path to files.exclude
vsc_settings['files.exclude'][rel_path] = True

vsc_settings['search.exclude'] = vsc_settings['files.exclude']
# write the settings to the file
vsc_file.write(json.dumps(vsc_settings, ensure_ascii=False, indent=4))

print('Done!')

return