[scons] code cleanup for scons script. (#10429)

* [scons] move project_generation to targets; code clean for building.py.
This commit is contained in:
Bernard Xiong 2025-06-25 15:06:45 +08:00 committed by GitHub
parent 8e9872a554
commit 5a2352eb64
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
38 changed files with 888 additions and 342 deletions

View File

@ -426,9 +426,6 @@ def main():
# Print A Nice Message With Each Function and the WCS
print_all_fxns(call_graph)
def ThreadStackStaticAnalysis(env):
print('Start thread stack static analysis...')

View File

@ -26,6 +26,7 @@
# 2024-04-21 Bernard Add toolchain detection in sdk packages
# 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
import os
import sys
@ -39,90 +40,14 @@ from SCons.Script import *
from utils import _make_path_relative
from mkdist import do_copy_file
from options import AddOptions
from preprocessor import create_preprocessor_instance
from win32spawn import Win32Spawn
BuildOptions = {}
Projects = []
Rtt_Root = ''
Env = None
# SCons PreProcessor patch
def start_handling_includes(self, t=None):
"""
Causes the PreProcessor object to start processing #import,
#include and #include_next lines.
This method will be called when a #if, #ifdef, #ifndef or #elif
evaluates True, or when we reach the #else in a #if, #ifdef,
#ifndef or #elif block where a condition already evaluated
False.
"""
d = self.dispatch_table
p = self.stack[-1] if self.stack else self.default_table
for k in ('import', 'include', 'include_next', 'define'):
d[k] = p[k]
def stop_handling_includes(self, t=None):
"""
Causes the PreProcessor object to stop processing #import,
#include and #include_next lines.
This method will be called when a #if, #ifdef, #ifndef or #elif
evaluates False, or when we reach the #else in a #if, #ifdef,
#ifndef or #elif block where a condition already evaluated True.
"""
d = self.dispatch_table
d['import'] = self.do_nothing
d['include'] = self.do_nothing
d['include_next'] = self.do_nothing
d['define'] = self.do_nothing
PatchedPreProcessor = SCons.cpp.PreProcessor
PatchedPreProcessor.start_handling_includes = start_handling_includes
PatchedPreProcessor.stop_handling_includes = stop_handling_includes
class Win32Spawn:
def spawn(self, sh, escape, cmd, args, env):
# deal with the cmd build-in commands which cannot be used in
# subprocess.Popen
if cmd == 'del':
for f in args[1:]:
try:
os.remove(f)
except Exception as e:
print('Error removing file: ' + e)
return -1
return 0
import subprocess
newargs = ' '.join(args[1:])
cmdline = cmd + " " + newargs
# Make sure the env is constructed by strings
_e = dict([(k, str(v)) for k, v in env.items()])
# Windows(tm) CreateProcess does not use the env passed to it to find
# the executables. So we have to modify our own PATH to make Popen
# work.
old_path = os.environ['PATH']
os.environ['PATH'] = _e['PATH']
try:
proc = subprocess.Popen(cmdline, env=_e, shell=False)
except Exception as e:
print('Error in calling command:' + cmdline.split(' ')[0])
print('Exception: ' + os.strerror(e.errno))
if (os.strerror(e.errno) == "No such file or directory"):
print ("\nPlease check Toolchains PATH setting.\n")
return e.errno
finally:
os.environ['PATH'] = old_path
return proc.wait()
def PrepareBuilding(env, root_directory, has_libcpu=False, remove_components = []):
global BuildOptions
@ -294,7 +219,7 @@ def PrepareBuilding(env, root_directory, has_libcpu=False, remove_components = [
Env.Append(BUILDERS = {'BuildLib': bld})
# parse rtconfig.h to get used component
PreProcessor = PatchedPreProcessor()
PreProcessor = create_preprocessor_instance()
f = open('rtconfig.h', 'r')
contents = f.read()
f.close()
@ -433,7 +358,7 @@ def PrepareModuleBuilding(env, root_directory, bsp_directory):
Rtt_Root = root_directory
# parse bsp rtconfig.h to get used component
PreProcessor = PatchedPreProcessor()
PreProcessor = create_preprocessor_instance()
f = open(bsp_directory + '/rtconfig.h', 'r')
contents = f.read()
f.close()
@ -877,7 +802,7 @@ def DoBuilding(target, objects):
def GenTargetProject(program = None):
if GetOption('target') in ['mdk', 'mdk4', 'mdk5']:
from keil import MDK2Project, MDK4Project, MDK5Project, ARMCC_Version
from targets.keil import MDK2Project, MDK4Project, MDK5Project, ARMCC_Version
if os.path.isfile('template.uvprojx') and GetOption('target') not in ['mdk4']: # Keil5
MDK5Project(GetOption('project-name') + '.uvprojx', Projects)
@ -895,68 +820,68 @@ def GenTargetProject(program = None):
print("Keil-MDK project has generated successfully!")
if GetOption('target') == 'iar':
from iar import IARProject, IARVersion
from targets.iar import IARProject, IARVersion
print("IAR Version: " + IARVersion())
IARProject(GetOption('project-name') + '.ewp', Projects)
print("IAR project has generated successfully!")
if GetOption('target') == 'vs':
from vs import VSProject
from targets.vs import VSProject
VSProject(GetOption('project-name') + '.vcproj', Projects, program)
if GetOption('target') == 'vs2012':
from vs2012 import VS2012Project
from targets.vs2012 import VS2012Project
VS2012Project(GetOption('project-name') + '.vcxproj', Projects, program)
if GetOption('target') == 'cb':
from codeblocks import CBProject
from targets.codeblocks import CBProject
CBProject(GetOption('project-name') + '.cbp', Projects, program)
if GetOption('target') == 'ua':
from ua import PrepareUA
from targets.ua import PrepareUA
PrepareUA(Projects, Rtt_Root, str(Dir('#')))
if GetOption('target') == 'vsc':
from vsc import GenerateVSCode
from targets.vsc import GenerateVSCode
GenerateVSCode(Env)
if GetOption('cmsispack'):
from vscpyocd import GenerateVSCodePyocdConfig
GenerateVSCodePyocdConfig(GetOption('cmsispack'))
if GetOption('target') == 'cdk':
from cdk import CDKProject
from targets.cdk import CDKProject
CDKProject(GetOption('project-name') + '.cdkproj', Projects)
if GetOption('target') == 'ses':
from ses import SESProject
from targets.ses import SESProject
SESProject(Env)
if GetOption('target') == 'makefile':
from makefile import TargetMakefile
from targets.makefile import TargetMakefile
TargetMakefile(Env)
if GetOption('target') == 'eclipse':
from eclipse import TargetEclipse
from targets.eclipse import TargetEclipse
TargetEclipse(Env, GetOption('reset-project-config'), GetOption('project-name'))
if GetOption('target') == 'codelite':
from codelite import TargetCodelite
from targets.codelite import TargetCodelite
TargetCodelite(Projects, program)
if GetOption('target') == 'cmake' or GetOption('target') == 'cmake-armclang':
from cmake import CMakeProject
from targets.cmake import CMakeProject
CMakeProject(Env, Projects, GetOption('project-name'))
if GetOption('target') == 'xmake':
from xmake import XMakeProject
from targets.xmake import XMakeProject
XMakeProject(Env, Projects)
if GetOption('target') == 'esp-idf':
from esp_idf import ESPIDFProject
from targets.esp_idf import ESPIDFProject
ESPIDFProject(Env, Projects)
if GetOption('target') == 'zig':
from zigbuild import ZigBuildProject
from targets.zigbuild import ZigBuildProject
ZigBuildProject(Env, Projects)
def EndBuilding(target, program = None):
@ -1069,7 +994,7 @@ def GetVersion():
rtdef = os.path.join(Rtt_Root, 'include', 'rtdef.h')
# parse rtdef.h to get RT-Thread version
prepcessor = PatchedPreProcessor()
prepcessor = create_preprocessor_instance()
f = open(rtdef, 'r')
contents = f.read()
f.close()
@ -1109,4 +1034,3 @@ def PackageSConscript(package):
from package import BuildPackage
return BuildPackage(package)

49
tools/hello/README.md Normal file
View File

@ -0,0 +1,49 @@
# Hello Component
这是一个使用package.json配置的RT-Thread组件示例展示了如何使用package.json来替代传统的SConscript中DefineGroup的方式。
## 文件结构
```
hello/
├── hello.h # 头文件
├── hello.c # 源文件
├── package.json # 组件配置文件
├── SConscript # 构建脚本
└── README.md # 说明文档
```
## package.json配置说明
package.json文件包含了组件的所有构建信息
- `name`: 组件名称
- `version`: 版本号
- `description`: 组件描述
- `author`: 作者信息
- `license`: 许可证
- `source_files`: 源文件列表
- `CPPPATH`: 头文件搜索路径
- `CPPDEFINES`: 预定义宏
- `depends`: 依赖的组件
## 使用方法
1. 将hello文件夹复制到你的RT-Thread项目的components目录下
2. 在应用代码中包含头文件:
```c
#include "hello.h"
```
3. 调用hello_world函数
```c
hello_world(); // 输出: Hello World!
```
## 构建过程
SConscript文件会
1. 导入package.py模块
2. 调用BuildPackage函数处理package.json
3. 自动创建DefineGroup并返回构建对象
这种方式比传统的SConscript更加简洁和易于维护。

4
tools/hello/SConscript Normal file
View File

@ -0,0 +1,4 @@
from package import *
objs = BuildPackage()
Return('objs')

22
tools/hello/hello.c Normal file
View File

@ -0,0 +1,22 @@
/*
* Copyright (c) 2025 RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
* 2025-06-21 Bernard First version
*/
#include <rtthread.h>
#include "hello.h"
/**
* @brief Hello world function implementation
*
* This function prints "Hello World!" to the console using rt_kprintf
*/
void hello_world(void)
{
rt_kprintf("Hello World!\n");
}

29
tools/hello/hello.h Normal file
View File

@ -0,0 +1,29 @@
/*
* Copyright (c) 2025 RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
* 2025-06-21 Bernard First version
*/
#ifndef __HELLO_H__
#define __HELLO_H__
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief Hello world function
*
* This function prints "Hello World!" to the console
*/
void hello_world(void);
#ifdef __cplusplus
}
#endif
#endif /* __HELLO_H__ */

20
tools/hello/package.json Normal file
View File

@ -0,0 +1,20 @@
{
"name": "hello",
"version": "1.0.0",
"description": "Hello World component for RT-Thread",
"author": "RT-Thread Development Team",
"license": "Apache-2.0",
"type": "rt-package",
"source_files": [
"hello.c"
],
"CPPPATH": [
"."
],
"CPPDEFINES": [
"HELLO"
],
"depends": [
""
]
}

View File

@ -24,6 +24,8 @@
# this script is used to build group with package.json instead of SConscript
import os
import json
from building import *
def ExtendPackageVar(package, var):
@ -36,8 +38,14 @@ def ExtendPackageVar(package, var):
return v
def BuildPackage(package):
import json
def BuildPackage(package = None):
if package is None:
package = os.path.join(GetCurrentDir(), 'package.json')
if not os.path.isfile(package):
print("%s/package.json not found" % GetCurrentDir())
return []
f = open(package)
package_json = f.read()
@ -47,7 +55,7 @@ def BuildPackage(package):
package = json.loads(package_json)
# check package name
if 'name' not in package:
if 'name' not in package or 'type' not in package or package['type'] != 'rt-package':
return []
# get depends

89
tools/preprocessor.py Normal file
View File

@ -0,0 +1,89 @@
# -*- coding: utf-8 -*-
#
# File : preprocessor.py
# This file is part of RT-Thread RTOS
# COPYRIGHT (C) 2006 - 2025, RT-Thread Development Team
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License along
# with this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
#
# Change Logs:
# Date Author Notes
# 2025-01-05 Assistant Extract SCons PreProcessor patch to independent class
from SCons.Script import *
class SConsPreProcessorPatch:
"""
SCons PreProcessor patch class
This class provides methods to patch the SCons PreProcessor
to handle conditional compilation directives properly.
"""
def __init__(self):
"""Initialize the PreProcessor patch"""
self._patched_preprocessor = None
self._apply_patch()
def _apply_patch(self):
"""
Apply the patch to SCons PreProcessor
This method patches the SCons.cpp.PreProcessor class with
custom methods for handling includes during conditional compilation.
"""
from SCons import cpp
# Store reference to original PreProcessor
self._patched_preprocessor = cpp.PreProcessor
# Create bound methods for the patch
def start_handling_includes(preprocessor_self, t=None):
d = preprocessor_self.dispatch_table
p = preprocessor_self.stack[-1] if preprocessor_self.stack else preprocessor_self.default_table
for k in ('import', 'include', 'include_next', 'define'):
d[k] = p[k]
def stop_handling_includes(preprocessor_self, t=None):
d = preprocessor_self.dispatch_table
d['import'] = preprocessor_self.do_nothing
d['include'] = preprocessor_self.do_nothing
d['include_next'] = preprocessor_self.do_nothing
d['define'] = preprocessor_self.do_nothing
# Apply the patch methods
self._patched_preprocessor.start_handling_includes = start_handling_includes
self._patched_preprocessor.stop_handling_includes = stop_handling_includes
def get_patched_preprocessor(self):
return self._patched_preprocessor
def create_preprocessor_instance(self):
return self._patched_preprocessor()
# Global instance for easy access
_preprocessor_patch = None
def get_patched_preprocessor():
global _preprocessor_patch
if _preprocessor_patch is None:
_preprocessor_patch = SConsPreProcessorPatch()
return _preprocessor_patch.get_patched_preprocessor()
def create_preprocessor_instance():
global _preprocessor_patch
if _preprocessor_patch is None:
_preprocessor_patch = SConsPreProcessorPatch()
return _preprocessor_patch.create_preprocessor_instance()

View File

@ -33,7 +33,6 @@ def update_project_file(project_dir):
command = ' --target=iar -s'
os.system('scons --directory=' + project_dir + command + ' > 1.txt')
def update_all_project_files(root_path):
# current path is dir
if os.path.isdir(root_path):

79
tools/targets/__init__.py Normal file
View File

@ -0,0 +1,79 @@
# -*- coding: utf-8 -*-
#
# File : __init__.py
# This file is part of RT-Thread RTOS
# COPYRIGHT (C) 2006 - 2015, RT-Thread Development Team
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License along
# with this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
#
# Change Logs:
# Date Author Notes
# 2025-01-XX Bernard Create targets module for IDE project generators
# Import all target generators
from . import keil
from . import iar
from . import vs
from . import vs2012
from . import codeblocks
from . import ua
from . import vsc
from . import cdk
from . import ses
from . import eclipse
from . import codelite
from . import cmake
from . import xmake
from . import esp_idf
from . import zigbuild
from . import makefile
from . import rt_studio
# Export all target generator functions
__all__ = [
# Keil MDK
'keil',
# IAR
'iar',
# Visual Studio
'vs',
'vs2012',
# Code::Blocks
'codeblocks',
# Universal ARM
'ua',
# VSCode
'vsc',
# CDK
'cdk',
# SEGGER Embedded Studio
'ses',
# Eclipse
'eclipse',
# CodeLite
'codelite',
# CMake
'cmake',
# XMake
'xmake',
# ESP-IDF
'esp_idf',
# Zig
'zigbuild',
# Make
'makefile',
# RT-Studio
'rt_studio'
]

View File

@ -25,14 +25,17 @@
import os
import sys
import string
import building
import xml.etree.ElementTree as etree
import uuid
import utils
from xml.etree.ElementTree import SubElement
from utils import _make_path_relative
from utils import xml_indent
import utils
# Add parent directory to path to import building
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
import building
import xml.etree.ElementTree as etree
fs_encoding = sys.getfilesystemencoding()

View File

@ -25,15 +25,17 @@
import os
import sys
import string
import building
import rtconfig
import xml.etree.ElementTree as etree
import uuid
import utils
from xml.etree.ElementTree import SubElement
from utils import _make_path_relative
from utils import xml_indent
import utils
# Add parent directory to path to import building
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
import building
import xml.etree.ElementTree as etree
fs_encoding = sys.getfilesystemencoding()

View File

@ -13,7 +13,12 @@ import glob
import xml.etree.ElementTree as etree
from xml.etree.ElementTree import SubElement
import rt_studio
from . import rt_studio
import sys
import os
# Add parent directory to path to import building and utils
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
from building import *
from utils import *
from utils import _make_path_relative

View File

@ -22,8 +22,8 @@ def GenerateCFiles(env,project):
cm_file.write( "\t" + path.replace("\\", "/") + "\n" )
src = open(f.rfile().abspath, 'r')
for line in src.readlines():
if re.match('INIT_(BOARD|PREV|DEVICE|COMPONENT|ENV|APP)_EXPORT\(.+\)', line):
init_export.append(re.search('\(.+\)', line).group(0)[1:-1])
if re.match(r'INIT_(BOARD|PREV|DEVICE|COMPONENT|ENV|APP)_EXPORT\(.+\)', line):
init_export.append(re.search(r'\(.+\)', line).group(0)[1:-1])
src.close()
cm_file.write("\n")

View File

@ -36,7 +36,7 @@ from utils import xml_indent
fs_encoding = sys.getfilesystemencoding()
iar_workspace = '''<?xml version="1.0" encoding="iso-8859-1"?>
iar_workspace = r'''<?xml version="1.0" encoding="iso-8859-1"?>
<workspace>
<project>
@ -208,5 +208,5 @@ def IARVersion():
if not isinstance(stdout, str):
stdout = str(stdout, 'utf8') # Patch for Python 3
# example stdout: IAR ANSI C/C++ Compiler V8.20.1.14183/W32 for ARM
iar_version = re.search('[\d\.]+', stdout).group(0)
iar_version = re.search(r'[\d\.]+', stdout).group(0)
return iar_version

View File

@ -283,19 +283,13 @@ def MDK45Project(tree, target, script):
# get each group's LIBS flags
if 'LIBS' in group and group['LIBS']:
for item in group['LIBS']:
lib_path = ''
for path_item in group['LIBPATH']:
full_path = os.path.join(path_item, item + '.lib')
if os.path.isfile(full_path): # has this library
lib_path = full_path
break
if lib_path != '':
for item in group['LIBPATH']:
full_path = os.path.join(item, group['name'] + '.lib')
if os.path.isfile(full_path): # has this library
if group_tree != None:
MDK4AddLibToGroup(ProjectFiles, group_tree, group['name'], lib_path, project_path)
MDK4AddLibToGroup(ProjectFiles, group_tree, group['name'], full_path, project_path)
else:
group_tree = MDK4AddGroupForFN(ProjectFiles, groups, group['name'], lib_path, project_path)
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')
@ -394,7 +388,7 @@ def MDK5Project(target, script):
print('UV4.exe is not available, please check your keil installation')
def MDK2Project(target, script):
template = open('template.Uv2', "r")
template = open(os.path.join(os.path.dirname(__file__), 'template.Uv2'), 'r')
lines = template.readlines()
project = open(target, "w")

View File

@ -1,6 +1,31 @@
#
# File : makefile.py
# This file is part of RT-Thread RTOS
# COPYRIGHT (C) 2006 - 2015, RT-Thread Development Team
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License along
# with this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
#
# Change Logs:
# Date Author Notes
# 2015-01-20 Bernard Add copyright information
import os
import sys
# Add parent directory to path to import utils
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
from utils import *
from utils import _make_path_relative
import rtconfig

View File

@ -2,7 +2,14 @@ import os
import re
from string import Template
import rtconfig
try:
import rtconfig
except ImportError:
# Mock rtconfig for testing
class MockRtconfig:
pass
rtconfig = MockRtconfig()
import shutil
import time
@ -275,7 +282,7 @@ def gen_org_eclipse_core_runtime_prefs(output_file_path):
def gen_cproject_file(output_file_path):
template_file_path = os.path.join(os.path.dirname(output_file_path), "template.cproject")
template_file_path = os.path.join(os.path.dirname(__file__), 'template.cproject')
if os.path.exists(template_file_path):
try:
shutil.copy(template_file_path, output_file_path)

View File

@ -25,13 +25,17 @@
import os
import sys
import string
import building
import uuid
import utils
import xml.etree.ElementTree as etree
from xml.etree.ElementTree import SubElement
from utils import _make_path_relative
from utils import xml_indent
# Add parent directory to path to import building
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
import building
import xml.etree.ElementTree as etree
fs_encoding = sys.getfilesystemencoding()
def VS_AddGroup(ProjectFiles, parent, name, files, libs, project_path):

View File

@ -25,14 +25,17 @@
import os
import sys
import string
import building
import uuid
import xml.etree.ElementTree as etree
import utils
from xml.etree.ElementTree import SubElement
from utils import _make_path_relative
from utils import xml_indent
import utils
# Add parent directory to path to import building
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
import building
import xml.etree.ElementTree as etree
fs_encoding = sys.getfilesystemencoding()

145
tools/targets/xmake.lua Normal file
View File

@ -0,0 +1,145 @@
add_rules("mode.debug", "mode.release")
toolchain("arm-none-eabi")
set_kind("standalone")
set_sdkdir("/home/bernard/.env/tools/scripts/packages/arm-none-eabi-gcc-v13.2.rel1")
toolchain_end()
target("rt-thread")
set_kind("binary")
set_toolchains("arm-none-eabi")
add_files(
"applications/main.c",
"../../../components/libc/compilers/common/cctype.c",
"../../../components/libc/compilers/common/cstdlib.c",
"../../../components/libc/compilers/common/cstring.c",
"../../../components/libc/compilers/common/ctime.c",
"../../../components/libc/compilers/common/cunistd.c",
"../../../components/libc/compilers/common/cwchar.c",
"../../../components/libc/compilers/newlib/syscalls.c",
"../../../components/drivers/core/device.c",
"../../../components/drivers/ipc/completion_comm.c",
"../../../components/drivers/ipc/completion_up.c",
"../../../components/drivers/ipc/condvar.c",
"../../../components/drivers/ipc/dataqueue.c",
"../../../components/drivers/ipc/pipe.c",
"../../../components/drivers/ipc/ringblk_buf.c",
"../../../components/drivers/ipc/ringbuffer.c",
"../../../components/drivers/ipc/waitqueue.c",
"../../../components/drivers/ipc/workqueue.c",
"../../../components/drivers/pin/dev_pin.c",
"../../../components/drivers/serial/dev_serial.c",
"../libraries/HAL_Drivers/drivers/drv_gpio.c",
"../libraries/HAL_Drivers/drivers/drv_usart.c",
"../libraries/HAL_Drivers/drv_common.c",
"board/CubeMX_Config/Src/stm32f4xx_hal_msp.c",
"board/board.c",
"../../../components/finsh/shell.c",
"../../../components/finsh/msh.c",
"../../../components/finsh/msh_parse.c",
"../../../components/finsh/cmd.c",
"../../../src/clock.c",
"../../../src/components.c",
"../../../src/cpu_up.c",
"../../../src/defunct.c",
"../../../src/idle.c",
"../../../src/ipc.c",
"../../../src/irq.c",
"../../../src/kservice.c",
"../../../src/mem.c",
"../../../src/mempool.c",
"../../../src/object.c",
"../../../src/scheduler_comm.c",
"../../../src/scheduler_up.c",
"../../../src/thread.c",
"../../../src/timer.c",
"../../../src/klibc/kstring.c",
"../../../src/klibc/rt_vsscanf.c",
"../../../src/klibc/kstdio.c",
"../../../src/klibc/rt_vsnprintf_tiny.c",
"../../../src/klibc/kerrno.c",
"../../../libcpu/arm/common/atomic_arm.c",
"../../../libcpu/arm/common/div0.c",
"../../../libcpu/arm/common/showmem.c",
"../../../libcpu/arm/cortex-m4/context_gcc.S",
"../../../libcpu/arm/cortex-m4/cpuport.c",
"packages/stm32f4_cmsis_driver-latest/Source/Templates/gcc/startup_stm32f412zx.s",
"packages/stm32f4_cmsis_driver-latest/Source/Templates/system_stm32f4xx.c",
"packages/stm32f4_hal_driver-latest/Src/stm32f4xx_hal_dma_ex.c",
"packages/stm32f4_hal_driver-latest/Src/stm32f4xx_hal_usart.c",
"packages/stm32f4_hal_driver-latest/Src/stm32f4xx_hal_pwr_ex.c",
"packages/stm32f4_hal_driver-latest/Src/stm32f4xx_hal_cryp.c",
"packages/stm32f4_hal_driver-latest/Src/stm32f4xx_hal_gpio.c",
"packages/stm32f4_hal_driver-latest/Src/stm32f4xx_hal_rcc.c",
"packages/stm32f4_hal_driver-latest/Src/stm32f4xx_hal.c",
"packages/stm32f4_hal_driver-latest/Src/stm32f4xx_hal_cortex.c",
"packages/stm32f4_hal_driver-latest/Src/stm32f4xx_hal_pwr.c",
"packages/stm32f4_hal_driver-latest/Src/stm32f4xx_hal_cec.c",
"packages/stm32f4_hal_driver-latest/Src/stm32f4xx_hal_cryp_ex.c",
"packages/stm32f4_hal_driver-latest/Src/stm32f4xx_hal_dma.c",
"packages/stm32f4_hal_driver-latest/Src/stm32f4xx_hal_uart.c",
"packages/stm32f4_hal_driver-latest/Src/stm32f4xx_hal_rcc_ex.c",
"packages/stm32f4_hal_driver-latest/Src/stm32f4xx_hal_crc.c",
"packages/stm32f4_hal_driver-latest/Src/stm32f4xx_hal_rng.c"
)
add_includedirs(
"applications",
"packages/CMSIS-Core-latest/Include",
"../../../components/libc/compilers/newlib",
"../../../components/libc/compilers/common/include",
"../../../components/drivers/include",
"../../../components/drivers/smp_call",
"../../../components/drivers/phy",
"board",
"board/CubeMX_Config/Inc",
"../libraries/HAL_Drivers/drivers",
"../libraries/HAL_Drivers/drivers/config",
"../libraries/HAL_Drivers",
"../../../components/finsh",
".",
"../../../include",
"../../../libcpu/arm/common",
"../../../libcpu/arm/cortex-m4",
"../../../components/libc/posix/ipc",
"../../../components/libc/posix/io/poll",
"../../../components/libc/posix/io/eventfd",
"../../../components/libc/posix/io/epoll",
"packages/stm32f4_cmsis_driver-latest/Include",
"packages/stm32f4_hal_driver-latest/Inc",
"packages/stm32f4_hal_driver-latest/Inc/Legacy"
)
add_defines(
"RT_USING_LIBC",
"RT_USING_NEWLIBC",
"STM32F412Zx",
"USE_HAL_DRIVER",
"_POSIX_C_SOURCE=1",
"__RTTHREAD__"
)
add_cflags(
" -mcpu=cortex-m4 -mthumb -mfpu=fpv4-sp-d16 -mfloat-abi=hard -ffunction-sections -fdata-sections -Dgcc -O0 -gdwarf-2 -g" ,{force = true}
)
add_cxxflags(
" -mcpu=cortex-m4 -mthumb -mfpu=fpv4-sp-d16 -mfloat-abi=hard -ffunction-sections -fdata-sections -Dgcc -O0 -gdwarf-2 -g" ,{force = true}
)
add_asflags(
" -c -mcpu=cortex-m4 -mthumb -mfpu=fpv4-sp-d16 -mfloat-abi=hard -ffunction-sections -fdata-sections -x assembler-with-cpp -Wa,-mimplicit-it=thumb -gdwarf-2" ,{force = true}
)
add_ldflags(
" -mcpu=cortex-m4 -mthumb -mfpu=fpv4-sp-d16 -mfloat-abi=hard -ffunction-sections -fdata-sections -Wl,--gc-sections,-Map=rt-thread.map,-cref,-u,Reset_Handler -T board/linker_scripts/link.lds" ,{force = true}
)
set_targetdir("./")
set_filename("rtthread.elf")
after_build(function(target)
os.exec("/home/bernard/.env/tools/scripts/packages/arm-none-eabi-gcc-v13.2.rel1/bin/arm-none-eabi-objcopy -O ihex rtthread.elf rtthread.hex")
os.exec("/home/bernard/.env/tools/scripts/packages/arm-none-eabi-gcc-v13.2.rel1/bin/arm-none-eabi-objcopy -O binary rtthread.elf rtthread.bin")
os.exec("/home/bernard/.env/tools/scripts/packages/arm-none-eabi-gcc-v13.2.rel1/bin/arm-none-eabi-size rtthread.elf")
end)

View File

@ -69,14 +69,14 @@ class XmakeProject:
else:
RTT_ROOT = os.path.normpath(os.getcwd() + '/../../..')
template_path = os.path.join(RTT_ROOT, "tools", "xmake.lua")
template_path = os.path.join(RTT_ROOT, "tools", "targets", "xmake.lua")
with open(template_path, "r") as f:
data = f.read()
data = Template(data)
data = data.safe_substitute(toolchain=self.toolchain, sdkdir=self.sdkdir, bindir=self.bindir, src_path=self.src_path, inc_path=self.inc_path,
define=self.define, cflags=self.cflags, cxxflags=self.cxxflags, asflags=self.asflags,
ldflags=self.ldflags, target="rt-thread")
with open("xmake.lua", "w") as f:
with open(os.path.join(os.path.dirname(__file__), "xmake.lua"), "w") as f:
f.write(data)

32
tools/testcases/README.md Normal file
View File

@ -0,0 +1,32 @@
# 测试用例目录
本目录包含 RT-Thread 工具的测试脚本。
## 测试脚本
### test_preprocessor.py
SCons PreProcessor 补丁功能测试脚本。测试与 building.py 的集成,验证预处理器补丁是否正常工作。
### test_refactor.py
验证目标模块重构是否成功的测试脚本。测试内容包括:
- 目标模块导入
- Building.py 导入
- 目标函数调用
### mock_rtconfig.py
用于测试的模拟 rtconfig 模块。在实际 rtconfig 不可用的测试场景中提供模拟的 rtconfig 模块。
## 使用方法
要运行测试,请导航到此目录并执行:
```bash
python test_preprocessor.py
python test_refactor.py
```
## 说明
- 这些测试脚本用于验证 RT-Thread 工具的功能
- 可以独立运行或作为测试套件的一部分
- mock_rtconfig.py 文件被其他测试脚本用来模拟 rtconfig 模块

View File

@ -0,0 +1,35 @@
# -*- coding: utf-8 -*-
#
# Mock rtconfig module for testing purposes
#
# Mock configuration variables
CROSS_TOOL = 'gcc'
PLATFORM = 'gcc'
CC = 'gcc'
CXX = 'g++'
AS = 'as'
AR = 'ar'
LINK = 'gcc'
EXEC_PATH = '/usr/bin'
# Mock functions
def GetDepend(depend):
return True
# Mock environment
class MockEnv:
def __init__(self):
self.CPPPATH = []
self.CPPDEFINES = []
self.LIBS = []
self.LIBPATH = []
self.CFLAGS = []
self.CXXFLAGS = []
self.LINKFLAGS = []
self.ASFLAGS = []
# Global variables
Env = MockEnv()
Rtt_Root = '/mock/rt-thread'
Projects = []

View File

@ -0,0 +1,116 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
#
# File : test_preprocessor_patch.py
# This file is part of RT-Thread RTOS
# COPYRIGHT (C) 2006 - 2025, RT-Thread Development Team
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License along
# with this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
#
# Change Logs:
# Date Author Notes
# 2025-01-05 Assistant Test file for SCons PreProcessor patch
import sys
import os
# Add current directory to path for imports
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
def test_preprocessor_patch():
"""Test the SCons PreProcessor patch functionality"""
try:
from scons_preprocessor_patch import SConsPreProcessorPatch, create_preprocessor_instance
print("Testing SCons PreProcessor patch...")
# Test creating patch instance
patch = SConsPreProcessorPatch()
print("✓ SConsPreProcessorPatch instance created successfully")
# Test getting patched preprocessor
patched_class = patch.get_patched_preprocessor()
print("✓ Patched PreProcessor class retrieved successfully")
# Test creating preprocessor instance
preprocessor = create_preprocessor_instance()
print("✓ PreProcessor instance created successfully")
# Test basic functionality
test_content = """
#define TEST_MACRO 1
#ifdef TEST_MACRO
#define ENABLED_FEATURE 1
#else
#define DISABLED_FEATURE 1
#endif
"""
preprocessor.process_contents(test_content)
namespace = preprocessor.cpp_namespace
print("✓ PreProcessor processed test content successfully")
print(f" - TEST_MACRO: {namespace.get('TEST_MACRO', 'Not found')}")
print(f" - ENABLED_FEATURE: {namespace.get('ENABLED_FEATURE', 'Not found')}")
print(f" - DISABLED_FEATURE: {namespace.get('DISABLED_FEATURE', 'Not found')}")
print("\n✓ All tests passed! SCons PreProcessor patch is working correctly.")
return True
except ImportError as e:
print(f"✗ Import error: {e}")
print("Make sure SCons is available in the environment")
return False
except Exception as e:
print(f"✗ Test failed: {e}")
return False
def test_building_integration():
"""Test integration with building.py"""
try:
# Test that the function is available from the patch module
from scons_preprocessor_patch import create_preprocessor_instance
print("\nTesting scons_preprocessor_patch integration...")
# Test that the function is available
preprocessor = create_preprocessor_instance()
print("✓ create_preprocessor_instance function works from scons_preprocessor_patch")
# Test basic processing
test_content = "#define BUILD_TEST 1"
preprocessor.process_contents(test_content)
namespace = preprocessor.cpp_namespace
print(f"✓ Integration test passed: BUILD_TEST = {namespace.get('BUILD_TEST', 'Not found')}")
return True
except Exception as e:
print(f"✗ Integration test failed: {e}")
return False
if __name__ == "__main__":
print("SCons PreProcessor Patch Test Suite")
print("=" * 40)
success1 = test_preprocessor_patch()
success2 = test_building_integration()
if success1 and success2:
print("\n🎉 All tests passed! The refactoring was successful.")
sys.exit(0)
else:
print("\n❌ Some tests failed. Please check the implementation.")
sys.exit(1)

View File

@ -0,0 +1,121 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
# Test script to verify the refactoring is successful
import sys
import os
# Add current directory to path
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
# Mock rtconfig module for testing
import mock_rtconfig
sys.modules['rtconfig'] = mock_rtconfig
def test_targets_import():
"""Test if all target modules can be imported successfully"""
print("Testing targets module imports...")
try:
# Test importing targets module
import targets
print("✓ targets module imported successfully")
# Test importing individual target modules
target_modules = [
'keil', 'iar', 'vs', 'vs2012', 'codeblocks', 'ua',
'vsc', 'cdk', 'ses', 'eclipse', 'codelite',
'cmake', 'xmake', 'esp_idf', 'zigbuild', 'makefile', 'rt_studio'
]
for module_name in target_modules:
try:
module = getattr(targets, module_name)
print(f"{module_name} module imported successfully")
except AttributeError as e:
print(f"✗ Failed to import {module_name}: {e}")
return False
return True
except ImportError as e:
print(f"✗ Failed to import targets module: {e}")
return False
def test_building_import():
"""Test if building.py can import target modules"""
print("\nTesting building.py imports...")
try:
# Test importing building module
import building
print("✓ building module imported successfully")
# Test if GenTargetProject function exists
if hasattr(building, 'GenTargetProject'):
print("✓ GenTargetProject function found")
else:
print("✗ GenTargetProject function not found")
return False
return True
except ImportError as e:
print(f"✗ Failed to import building module: {e}")
return False
def test_target_functions():
"""Test if target functions can be called"""
print("\nTesting target function calls...")
try:
# Test importing specific target functions
from targets.keil import MDK4Project, MDK5Project
print("✓ Keil target functions imported successfully")
from targets.iar import IARProject
print("✓ IAR target functions imported successfully")
from targets.eclipse import TargetEclipse
print("✓ Eclipse target functions imported successfully")
from targets.cmake import CMakeProject
print("✓ CMake target functions imported successfully")
import targets.rt_studio
print("✓ RT-Studio target functions imported successfully")
return True
except ImportError as e:
print(f"✗ Failed to import target functions: {e}")
return False
def main():
"""Main test function"""
print("RT-Thread Tools Refactoring Test")
print("=" * 40)
success = True
# Run all tests
if not test_targets_import():
success = False
if not test_building_import():
success = False
if not test_target_functions():
success = False
print("\n" + "=" * 40)
if success:
print("✓ All tests passed! Refactoring is successful.")
return 0
else:
print("✗ Some tests failed. Please check the errors above.")
return 1
if __name__ == '__main__':
sys.exit(main())

View File

@ -1,7 +1,7 @@
#
# File : win32spawn.py
# This file is part of RT-Thread RTOS
# COPYRIGHT (C) 2006 - 2015, RT-Thread Development Team
# COPYRIGHT (C) 2006 - 2025, RT-Thread Development Team
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@ -23,163 +23,43 @@
#
import os
import threading
import sys
import subprocess
_PY2 = sys.version_info[0] < 3
if _PY2:
import Queue
else:
import queue as Queue
class Win32Spawn:
def spawn(self, sh, escape, cmd, args, env):
# deal with the cmd build-in commands which cannot be used in
# subprocess.Popen
if cmd == 'del':
for f in args[1:]:
try:
os.remove(f)
except Exception as e:
print('Error removing file: ' + e)
return -1
return 0
# Windows import
import win32file
import win32pipe
import win32api
import win32con
import win32security
import win32process
import win32event
newargs = ' '.join(args[1:])
cmdline = cmd + " " + newargs
class Win32Spawn(object):
def __init__(self, cmd, shell=False):
self.queue = Queue.Queue()
self.is_terminated = False
self.wake_up_event = win32event.CreateEvent(None, 0, 0, None)
# Make sure the env is constructed by strings
_e = dict([(k, str(v)) for k, v in env.items()])
exec_dir = os.getcwd()
comspec = os.environ.get("COMSPEC", "cmd.exe")
cmd = comspec + ' /c ' + cmd
# Windows(tm) CreateProcess does not use the env passed to it to find
# the executables. So we have to modify our own PATH to make Popen
# work.
old_path = os.environ['PATH']
os.environ['PATH'] = _e['PATH']
win32event.ResetEvent(self.wake_up_event)
try:
proc = subprocess.Popen(cmdline, env=_e, shell=False)
except Exception as e:
print('Error in calling command:' + cmdline.split(' ')[0])
print('Exception: ' + os.strerror(e.errno))
if (os.strerror(e.errno) == "No such file or directory"):
print ("\nPlease check Toolchains PATH setting.\n")
currproc = win32api.GetCurrentProcess()
return e.errno
finally:
os.environ['PATH'] = old_path
sa = win32security.SECURITY_ATTRIBUTES()
sa.bInheritHandle = 1
child_stdout_rd, child_stdout_wr = win32pipe.CreatePipe(sa, 0)
child_stdout_rd_dup = win32api.DuplicateHandle(currproc, child_stdout_rd, currproc, 0, 0, win32con.DUPLICATE_SAME_ACCESS)
win32file.CloseHandle(child_stdout_rd)
child_stderr_rd, child_stderr_wr = win32pipe.CreatePipe(sa, 0)
child_stderr_rd_dup = win32api.DuplicateHandle(currproc, child_stderr_rd, currproc, 0, 0, win32con.DUPLICATE_SAME_ACCESS)
win32file.CloseHandle(child_stderr_rd)
child_stdin_rd, child_stdin_wr = win32pipe.CreatePipe(sa, 0)
child_stdin_wr_dup = win32api.DuplicateHandle(currproc, child_stdin_wr, currproc, 0, 0, win32con.DUPLICATE_SAME_ACCESS)
win32file.CloseHandle(child_stdin_wr)
startup_info = win32process.STARTUPINFO()
startup_info.hStdInput = child_stdin_rd
startup_info.hStdOutput = child_stdout_wr
startup_info.hStdError = child_stderr_wr
startup_info.dwFlags = win32process.STARTF_USESTDHANDLES
cr_flags = 0
cr_flags = win32process.CREATE_NEW_PROCESS_GROUP
env = os.environ.copy()
self.h_process, h_thread, dw_pid, dw_tid = win32process.CreateProcess(None, cmd, None, None, 1,
cr_flags, env, os.path.abspath(exec_dir),
startup_info)
win32api.CloseHandle(h_thread)
win32file.CloseHandle(child_stdin_rd)
win32file.CloseHandle(child_stdout_wr)
win32file.CloseHandle(child_stderr_wr)
self.__child_stdout = child_stdout_rd_dup
self.__child_stderr = child_stderr_rd_dup
self.__child_stdin = child_stdin_wr_dup
self.exit_code = -1
def close(self):
win32file.CloseHandle(self.__child_stdout)
win32file.CloseHandle(self.__child_stderr)
win32file.CloseHandle(self.__child_stdin)
win32api.CloseHandle(self.h_process)
win32api.CloseHandle(self.wake_up_event)
def kill_subprocess():
win32event.SetEvent(self.wake_up_event)
def sleep(secs):
win32event.ResetEvent(self.wake_up_event)
timeout = int(1000 * secs)
val = win32event.WaitForSingleObject(self.wake_up_event, timeout)
if val == win32event.WAIT_TIMEOUT:
return True
else:
# The wake_up_event must have been signalled
return False
def get(self, block=True, timeout=None):
return self.queue.get(block=block, timeout=timeout)
def qsize(self):
return self.queue.qsize()
def __wait_for_child(self):
# kick off threads to read from stdout and stderr of the child process
threading.Thread(target=self.__do_read, args=(self.__child_stdout, )).start()
threading.Thread(target=self.__do_read, args=(self.__child_stderr, )).start()
while True:
# block waiting for the process to finish or the interrupt to happen
handles = (self.wake_up_event, self.h_process)
val = win32event.WaitForMultipleObjects(handles, 0, win32event.INFINITE)
if val >= win32event.WAIT_OBJECT_0 and val < win32event.WAIT_OBJECT_0 + len(handles):
handle = handles[val - win32event.WAIT_OBJECT_0]
if handle == self.wake_up_event:
win32api.TerminateProcess(self.h_process, 1)
win32event.ResetEvent(self.wake_up_event)
return False
elif handle == self.h_process:
# the process has ended naturally
return True
else:
assert False, "Unknown handle fired"
else:
assert False, "Unexpected return from WaitForMultipleObjects"
# Wait for job to finish. Since this method blocks, it can to be called from another thread.
# If the application wants to kill the process, it should call kill_subprocess().
def wait(self):
if not self.__wait_for_child():
# it's been killed
result = False
else:
# normal termination
self.exit_code = win32process.GetExitCodeProcess(self.h_process)
result = self.exit_code == 0
self.close()
self.is_terminated = True
return result
# This method gets called on a worker thread to read from either a stderr
# or stdout thread from the child process.
def __do_read(self, handle):
bytesToRead = 1024
while 1:
try:
finished = 0
hr, data = win32file.ReadFile(handle, bytesToRead, None)
if data:
self.queue.put_nowait(data)
except win32api.error:
finished = 1
if finished:
return
def start_pipe(self):
def worker(pipe):
return pipe.wait()
thrd = threading.Thread(target=worker, args=(self, ))
thrd.start()
return proc.wait()

View File

@ -1,46 +0,0 @@
add_rules("mode.debug", "mode.release")
toolchain("${toolchain}")
set_kind("standalone")
set_sdkdir("${sdkdir}")
toolchain_end()
target("${target}")
set_kind("binary")
set_toolchains("${toolchain}")
add_files(
${src_path}
)
add_includedirs(
${inc_path}
)
add_defines(
${define}
)
add_cflags(
"${cflags}" ,{force = true}
)
add_cxxflags(
"${cxxflags}" ,{force = true}
)
add_asflags(
"${asflags}" ,{force = true}
)
add_ldflags(
"${ldflags}" ,{force = true}
)
set_targetdir("./")
set_filename("rtthread.elf")
after_build(function(target)
os.exec("${bindir}/${toolchain}-objcopy -O ihex rtthread.elf rtthread.hex")
os.exec("${bindir}/${toolchain}-objcopy -O binary rtthread.elf rtthread.bin")
os.exec("${bindir}/${toolchain}-size rtthread.elf")
end)