Skip to content

Commit fbdab95

Browse files
BernardXiongRbb666
authored andcommitted
[Feature][Tools] Add support for package.json, refactor BuildPackage function to handle new format.
1 parent 5c568f0 commit fbdab95

File tree

6 files changed

+304
-92
lines changed

6 files changed

+304
-92
lines changed

tools/docs/package-json-support.md

Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
# RT-Thread package.json 构建支持
2+
3+
## 概述
4+
5+
RT-Thread支持使用package.json来定义组件的构建配置,作为传统SConscript的简化替代方案。
6+
7+
## 现有支持
8+
9+
### package.json格式
10+
```json
11+
{
12+
"name": "hello",
13+
"description": "Hello World component for RT-Thread",
14+
"type": "rt-thread-component",
15+
"dependencies": ["RT_USING_HELLO"],
16+
"defines": [],
17+
"sources": [{
18+
"name": "src",
19+
"dependencies": [],
20+
"includes": ["."],
21+
"files": ["hello.c"]
22+
}]
23+
}
24+
```
25+
26+
### 字段说明
27+
- **name**: 组件名称(必需)
28+
- **type**: 必须为"rt-thread-component"(必需)
29+
- **description**: 组件描述
30+
- **dependencies**: 全局依赖,数组形式的宏定义
31+
- **defines**: 全局宏定义
32+
- **sources**: 源文件组数组,每组可包含:
33+
- **name**: 源组名称
34+
- **dependencies**: 源组特定依赖
35+
- **includes**: 头文件搜索路径
36+
- **files**: 源文件列表(支持通配符)
37+
38+
## 使用方式
39+
40+
### 1. 在SConscript中使用
41+
42+
方式一:使用PackageSConscript(推荐)
43+
```python
44+
from building import *
45+
46+
objs = PackageSConscript('package.json')
47+
Return('objs')
48+
```
49+
50+
方式二:直接调用BuildPackage
51+
```python
52+
Import('env')
53+
from package import BuildPackage
54+
55+
objs = BuildPackage('package.json')
56+
Return('objs')
57+
```
58+
59+
### 2. 目录结构示例
60+
```
61+
mycomponent/
62+
├── SConscript
63+
├── package.json
64+
├── mycomponent.c
65+
├── mycomponent.h
66+
└── src/
67+
└── helper.c
68+
```
69+
70+
### 3. 完整示例
71+
72+
package.json:
73+
```json
74+
{
75+
"name": "mycomponent",
76+
"description": "My RT-Thread component",
77+
"type": "rt-thread-component",
78+
"dependencies": ["RT_USING_MYCOMPONENT"],
79+
"defines": ["MY_VERSION=1"],
80+
"sources": [
81+
{
82+
"name": "main",
83+
"dependencies": [],
84+
"includes": ["."],
85+
"files": ["mycomponent.c"]
86+
},
87+
{
88+
"name": "helper",
89+
"dependencies": ["RT_USING_MYCOMPONENT_HELPER"],
90+
"includes": ["src"],
91+
"files": ["src/*.c"]
92+
}
93+
]
94+
}
95+
```
96+
97+
## 工作原理
98+
99+
1. **依赖检查**:首先检查全局dependencies,如果不满足则跳过整个组件
100+
2. **源组处理**:遍历sources数组,每个源组独立检查dependencies
101+
3. **路径处理**:includes相对路径基于package.json所在目录
102+
4. **文件匹配**:使用SCons的Glob函数处理文件通配符
103+
5. **构建调用**:最终调用DefineGroup创建构建组
104+
105+
## 与DefineGroup的对比
106+
107+
| 特性 | package.json | DefineGroup |
108+
|------|--------------|-------------|
109+
| 配置方式 | JSON文件 | Python代码 |
110+
| 依赖管理 | 结构化 | 函数参数 |
111+
| 源文件组织 | 分组管理 | 单一列表 |
112+
| 条件编译 | 源组级别 | 整体级别 |
113+
| 灵活性 | 中等 ||
114+
| 易用性 || 中等 |
115+
116+
## 最佳实践
117+
118+
1. **简单组件优先使用package.json**:配置清晰,易于维护
119+
2. **复杂逻辑使用SConscript**:需要动态逻辑时使用传统方式
120+
3. **混合使用**:可以在同一项目中混用两种方式
121+
122+
## 注意事项
123+
124+
1. package.json必须是有效的JSON格式
125+
2. type字段必须为"rt-thread-component"
126+
3. 文件路径相对于package.json所在目录
127+
4. 依赖不满足时会静默跳过,不会报错
128+
5. 与RT-Thread构建系统完全集成,不支持独立构建

tools/hello/package.json

Lines changed: 28 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,31 @@
11
{
2-
"name": "hello",
3-
"version": "1.0.0",
4-
"description": "Hello World component for RT-Thread",
5-
"author": "RT-Thread Development Team",
6-
"license": "Apache-2.0",
7-
"type": "rt-package",
8-
"source_files": [
9-
"hello.c"
10-
],
11-
"CPPPATH": [
2+
"name": "hello",
3+
"description": "Hello World component for RT-Thread",
4+
"type": "rt-thread-component",
5+
"dependencies": [],
6+
"defines": [
7+
"DEFINE_HELLO"
8+
],
9+
"sources": [
10+
{
11+
"dependencies": [],
12+
"includes": [
1213
"."
13-
],
14-
"CPPDEFINES": [
15-
"HELLO"
16-
],
17-
"depends": [
18-
""
19-
]
14+
],
15+
"files": [
16+
"hello.c"
17+
]
18+
},
19+
{
20+
"dependencies": [
21+
"HELLO_USING_HELPER"
22+
],
23+
"includes": [
24+
"src"
25+
],
26+
"files": [
27+
"src/helper.c"
28+
]
29+
}
30+
]
2031
}

tools/hello/src/helper.c

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
/*
2+
* Copyright (c) 2025 RT-Thread Development Team
3+
*
4+
* SPDX-License-Identifier: Apache-2.0
5+
*
6+
* Change Logs:
7+
* Date Author Notes
8+
* 2025-08-03 Bernard First version
9+
*/
10+
11+
#include "helper.h"
12+
13+
void hello_helper()
14+
{
15+
}

tools/hello/src/helper.h

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
/*
2+
* Copyright (c) 2025 RT-Thread Development Team
3+
*
4+
* SPDX-License-Identifier: Apache-2.0
5+
*
6+
* Change Logs:
7+
* Date Author Notes
8+
* 2025-08-03 Bernard First version
9+
*/
10+
11+
#ifndef __HELPER__H__
12+
#define __HELPER__H__
13+
14+
void hello_helper();
15+
16+
#endif //!__HELPER__H__

tools/ng/environment.py

Lines changed: 39 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -170,62 +170,56 @@ def BuildPackage(env, package_path: str = None) -> List:
170170
171171
Args:
172172
env: SCons Environment
173-
package_path: Path to package.json or directory containing it
173+
package_path: Path to package.json. If None, looks for package.json in current directory.
174174
175175
Returns:
176176
List of build objects
177177
"""
178-
import json
178+
# Import the existing package module
179+
import sys
180+
import os
179181

180-
# Find package.json
181-
if package_path is None:
182-
package_path = 'package.json'
183-
elif os.path.isdir(package_path):
184-
package_path = os.path.join(package_path, 'package.json')
185-
186-
if not os.path.exists(package_path):
187-
env.GetContext().logger.error(f"Package file not found: {package_path}")
188-
return []
189-
190-
# Load package definition
191-
with open(package_path, 'r') as f:
192-
package = json.load(f)
193-
194-
# Process package
195-
name = package.get('name', 'unnamed')
196-
dependencies = package.get('dependencies', {})
182+
# Get the building module path
183+
building_path = os.path.dirname(os.path.abspath(__file__))
184+
tools_path = os.path.dirname(building_path)
197185

198-
# Check main dependency
199-
if 'RT_USING_' + name.upper() not in dependencies:
200-
main_depend = 'RT_USING_' + name.upper().replace('-', '_')
201-
else:
202-
main_depend = list(dependencies.keys())[0]
186+
# Add to path if not already there
187+
if tools_path not in sys.path:
188+
sys.path.insert(0, tools_path)
189+
190+
# Import and use the existing BuildPackage
191+
try:
192+
from package import BuildPackage as build_package_func
203193

204-
if not env.GetDepend(main_depend):
205-
return []
194+
# BuildPackage uses global functions, so we need to set up the context
195+
# Save current directory
196+
current_dir = os.getcwd()
206197

207-
# Collect sources
208-
src = []
209-
include_paths = []
210-
211-
sources = package.get('sources', {})
212-
for category, config in sources.items():
213-
# Check condition
214-
condition = config.get('condition')
215-
if condition and not eval(condition, {'env': env, 'GetDepend': env.GetDepend}):
216-
continue
198+
# Change to the directory where we want to build
199+
if package_path is None:
200+
work_dir = env.GetCurrentDir()
201+
elif os.path.isdir(package_path):
202+
work_dir = package_path
203+
else:
204+
work_dir = os.path.dirname(package_path)
217205

218-
# Add source files
219-
source_files = config.get('source_files', [])
220-
for pattern in source_files:
221-
src.extend(env.Glob(pattern))
206+
os.chdir(work_dir)
207+
208+
try:
209+
# Call the original BuildPackage
210+
result = build_package_func(package_path)
211+
finally:
212+
# Restore directory
213+
os.chdir(current_dir)
222214

223-
# Add header paths
224-
header_path = config.get('header_path', [])
225-
include_paths.extend(header_path)
215+
return result
226216

227-
# Create group
228-
return env.DefineGroup(name, src, depend=main_depend, CPPPATH=include_paths)
217+
except ImportError:
218+
# Fallback if import fails
219+
context = BuildContext.get_current()
220+
if context:
221+
context.logger.error("Failed to import package module")
222+
return []
229223

230224
@staticmethod
231225
def Glob(env, pattern: str) -> List[str]:

0 commit comments

Comments
 (0)