summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorbernard <[email protected]>2025-08-03 03:57:54 +0800
committerR b b666 <[email protected]>2025-08-07 09:32:45 +0800
commitfbdab95299f0de68079978f73fac33c1de8a81ab (patch)
treef077b70dfb9adb00f6c911bf3e78d1ecd46f05ca
parent5c568f0280179253d52c41cd23bf7f1ff9925746 (diff)
[Feature][Tools] Add support for package.json, refactor BuildPackage function to handle new format.
-rw-r--r--tools/docs/package-json-support.md128
-rw-r--r--tools/hello/package.json45
-rw-r--r--tools/hello/src/helper.c15
-rw-r--r--tools/hello/src/helper.h16
-rw-r--r--tools/ng/environment.py84
-rw-r--r--tools/package.py96
6 files changed, 298 insertions, 86 deletions
diff --git a/tools/docs/package-json-support.md b/tools/docs/package-json-support.md
new file mode 100644
index 0000000000..c27aed3648
--- /dev/null
+++ b/tools/docs/package-json-support.md
@@ -0,0 +1,128 @@
+# RT-Thread package.json 构建支持
+
+## 概述
+
+RT-Thread支持使用package.json来定义组件的构建配置,作为传统SConscript的简化替代方案。
+
+## 现有支持
+
+### package.json格式
+```json
+{
+ "name": "hello",
+ "description": "Hello World component for RT-Thread",
+ "type": "rt-thread-component",
+ "dependencies": ["RT_USING_HELLO"],
+ "defines": [],
+ "sources": [{
+ "name": "src",
+ "dependencies": [],
+ "includes": ["."],
+ "files": ["hello.c"]
+ }]
+}
+```
+
+### 字段说明
+- **name**: 组件名称(必需)
+- **type**: 必须为"rt-thread-component"(必需)
+- **description**: 组件描述
+- **dependencies**: 全局依赖,数组形式的宏定义
+- **defines**: 全局宏定义
+- **sources**: 源文件组数组,每组可包含:
+ - **name**: 源组名称
+ - **dependencies**: 源组特定依赖
+ - **includes**: 头文件搜索路径
+ - **files**: 源文件列表(支持通配符)
+
+## 使用方式
+
+### 1. 在SConscript中使用
+
+方式一:使用PackageSConscript(推荐)
+```python
+from building import *
+
+objs = PackageSConscript('package.json')
+Return('objs')
+```
+
+方式二:直接调用BuildPackage
+```python
+Import('env')
+from package import BuildPackage
+
+objs = BuildPackage('package.json')
+Return('objs')
+```
+
+### 2. 目录结构示例
+```
+mycomponent/
+├── SConscript
+├── package.json
+├── mycomponent.c
+├── mycomponent.h
+└── src/
+ └── helper.c
+```
+
+### 3. 完整示例
+
+package.json:
+```json
+{
+ "name": "mycomponent",
+ "description": "My RT-Thread component",
+ "type": "rt-thread-component",
+ "dependencies": ["RT_USING_MYCOMPONENT"],
+ "defines": ["MY_VERSION=1"],
+ "sources": [
+ {
+ "name": "main",
+ "dependencies": [],
+ "includes": ["."],
+ "files": ["mycomponent.c"]
+ },
+ {
+ "name": "helper",
+ "dependencies": ["RT_USING_MYCOMPONENT_HELPER"],
+ "includes": ["src"],
+ "files": ["src/*.c"]
+ }
+ ]
+}
+```
+
+## 工作原理
+
+1. **依赖检查**:首先检查全局dependencies,如果不满足则跳过整个组件
+2. **源组处理**:遍历sources数组,每个源组独立检查dependencies
+3. **路径处理**:includes相对路径基于package.json所在目录
+4. **文件匹配**:使用SCons的Glob函数处理文件通配符
+5. **构建调用**:最终调用DefineGroup创建构建组
+
+## 与DefineGroup的对比
+
+| 特性 | package.json | DefineGroup |
+|------|--------------|-------------|
+| 配置方式 | JSON文件 | Python代码 |
+| 依赖管理 | 结构化 | 函数参数 |
+| 源文件组织 | 分组管理 | 单一列表 |
+| 条件编译 | 源组级别 | 整体级别 |
+| 灵活性 | 中等 | 高 |
+| 易用性 | 高 | 中等 |
+
+## 最佳实践
+
+1. **简单组件优先使用package.json**:配置清晰,易于维护
+2. **复杂逻辑使用SConscript**:需要动态逻辑时使用传统方式
+3. **混合使用**:可以在同一项目中混用两种方式
+
+## 注意事项
+
+1. package.json必须是有效的JSON格式
+2. type字段必须为"rt-thread-component"
+3. 文件路径相对于package.json所在目录
+4. 依赖不满足时会静默跳过,不会报错
+5. 与RT-Thread构建系统完全集成,不支持独立构建 \ No newline at end of file
diff --git a/tools/hello/package.json b/tools/hello/package.json
index 1e390882b1..6fce0b9b00 100644
--- a/tools/hello/package.json
+++ b/tools/hello/package.json
@@ -1,20 +1,31 @@
{
- "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": [
+ "name": "hello",
+ "description": "Hello World component for RT-Thread",
+ "type": "rt-thread-component",
+ "dependencies": [],
+ "defines": [
+ "DEFINE_HELLO"
+ ],
+ "sources": [
+ {
+ "dependencies": [],
+ "includes": [
"."
- ],
- "CPPDEFINES": [
- "HELLO"
- ],
- "depends": [
- ""
- ]
+ ],
+ "files": [
+ "hello.c"
+ ]
+ },
+ {
+ "dependencies": [
+ "HELLO_USING_HELPER"
+ ],
+ "includes": [
+ "src"
+ ],
+ "files": [
+ "src/helper.c"
+ ]
+ }
+ ]
} \ No newline at end of file
diff --git a/tools/hello/src/helper.c b/tools/hello/src/helper.c
new file mode 100644
index 0000000000..4367d076a2
--- /dev/null
+++ b/tools/hello/src/helper.c
@@ -0,0 +1,15 @@
+/*
+ * Copyright (c) 2025 RT-Thread Development Team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Change Logs:
+ * Date Author Notes
+ * 2025-08-03 Bernard First version
+ */
+
+#include "helper.h"
+
+void hello_helper()
+{
+}
diff --git a/tools/hello/src/helper.h b/tools/hello/src/helper.h
new file mode 100644
index 0000000000..bf8934217d
--- /dev/null
+++ b/tools/hello/src/helper.h
@@ -0,0 +1,16 @@
+/*
+ * Copyright (c) 2025 RT-Thread Development Team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Change Logs:
+ * Date Author Notes
+ * 2025-08-03 Bernard First version
+ */
+
+#ifndef __HELPER__H__
+#define __HELPER__H__
+
+void hello_helper();
+
+#endif //!__HELPER__H__
diff --git a/tools/ng/environment.py b/tools/ng/environment.py
index 3646cfad7d..3423263683 100644
--- a/tools/ng/environment.py
+++ b/tools/ng/environment.py
@@ -170,62 +170,56 @@ class RTEnv:
Args:
env: SCons Environment
- package_path: Path to package.json or directory containing it
+ package_path: Path to package.json. If None, looks for package.json in current directory.
Returns:
List of build objects
"""
- import json
+ # Import the existing package module
+ import sys
+ import os
- # Find package.json
- if package_path is None:
- package_path = 'package.json'
- elif os.path.isdir(package_path):
- package_path = os.path.join(package_path, 'package.json')
-
- if not os.path.exists(package_path):
- env.GetContext().logger.error(f"Package file not found: {package_path}")
- return []
-
- # Load package definition
- with open(package_path, 'r') as f:
- package = json.load(f)
-
- # Process package
- name = package.get('name', 'unnamed')
- dependencies = package.get('dependencies', {})
+ # Get the building module path
+ building_path = os.path.dirname(os.path.abspath(__file__))
+ tools_path = os.path.dirname(building_path)
- # Check main dependency
- if 'RT_USING_' + name.upper() not in dependencies:
- main_depend = 'RT_USING_' + name.upper().replace('-', '_')
- else:
- main_depend = list(dependencies.keys())[0]
+ # Add to path if not already there
+ if tools_path not in sys.path:
+ sys.path.insert(0, tools_path)
+
+ # Import and use the existing BuildPackage
+ try:
+ from package import BuildPackage as build_package_func
- if not env.GetDepend(main_depend):
- return []
+ # BuildPackage uses global functions, so we need to set up the context
+ # Save current directory
+ current_dir = os.getcwd()
- # Collect sources
- src = []
- include_paths = []
-
- sources = package.get('sources', {})
- for category, config in sources.items():
- # Check condition
- condition = config.get('condition')
- if condition and not eval(condition, {'env': env, 'GetDepend': env.GetDepend}):
- continue
+ # Change to the directory where we want to build
+ if package_path is None:
+ work_dir = env.GetCurrentDir()
+ elif os.path.isdir(package_path):
+ work_dir = package_path
+ else:
+ work_dir = os.path.dirname(package_path)
- # Add source files
- source_files = config.get('source_files', [])
- for pattern in source_files:
- src.extend(env.Glob(pattern))
+ os.chdir(work_dir)
+
+ try:
+ # Call the original BuildPackage
+ result = build_package_func(package_path)
+ finally:
+ # Restore directory
+ os.chdir(current_dir)
- # Add header paths
- header_path = config.get('header_path', [])
- include_paths.extend(header_path)
+ return result
- # Create group
- return env.DefineGroup(name, src, depend=main_depend, CPPPATH=include_paths)
+ except ImportError:
+ # Fallback if import fails
+ context = BuildContext.get_current()
+ if context:
+ context.logger.error("Failed to import package module")
+ return []
@staticmethod
def Glob(env, pattern: str) -> List[str]:
diff --git a/tools/package.py b/tools/package.py
index f7a2ffa745..37a53f47bd 100644
--- a/tools/package.py
+++ b/tools/package.py
@@ -41,41 +41,89 @@ def ExtendPackageVar(package, var):
def BuildPackage(package = None):
if package is None:
package = os.path.join(GetCurrentDir(), 'package.json')
+ elif os.path.isdir(package):
+ # support directory path
+ package = os.path.join(package, 'package.json')
+
+ # get package.json path
+ cwd = os.path.dirname(os.path.abspath(package))
if not os.path.isfile(package):
- print("%s/package.json not found" % GetCurrentDir())
+ # silent return for conditional usage
return []
- f = open(package)
- package_json = f.read()
+ with open(package, 'r') as f:
+ package_json = f.read()
+ package = json.loads(package_json)
- # get package.json path
- cwd = os.path.dirname(package)
+ # check package name
+ if 'name' not in package or 'type' not in package or package['type'] != 'rt-thread-component':
+ return []
- package = json.loads(package_json)
+ # get depends
+ depend = []
+ if 'dependencies' in package:
+ depend = ExtendPackageVar(package, 'dependencies')
- # check package name
- if 'name' not in package or 'type' not in package or package['type'] != 'rt-package':
- return []
+ # check dependencies
+ if depend:
+ group_enable = False
+ for item in depend:
+ if GetDepend(item):
+ group_enable = True
+ break
+ if not group_enable:
+ return []
+
+ CPPDEFINES = []
+ if 'defines' in package:
+ CPPDEFINES = ExtendPackageVar(package, 'defines')
+
+ src = []
+ CPPPATH = []
+ if 'sources' in package:
+ src_depend = []
+ src_CPPPATH = []
+ for item in package['sources']:
+ if 'includes' in item:
+ includes = item['includes']
+ for include in includes:
+ if include.startswith('/') and os.path.isdir(include):
+ src_CPPPATH = src_CPPPATH + [include]
+ else:
+ path = os.path.abspath(os.path.join(cwd, include))
+ src_CPPPATH = src_CPPPATH + [path]
+
+ if 'dependencies' in item:
+ src_depend = src_depend + ExtendPackageVar(item, 'dependencies')
+
+ src_enable = False
+ if src_depend == []:
+ src_enable = True
+ else:
+ for d in src_depend:
+ if GetDepend(d):
+ src_enable = True
+ break
- # get depends
- depend = ExtendPackageVar(package, 'depends')
+ if src_enable:
+ files = []
+ src_files = []
+ if 'files' in item:
+ files += ExtendPackageVar(item, 'files')
- src = []
- if 'source_files' in package:
- for src_file in package['source_files']:
- src_file = os.path.join(cwd, src_file)
- src += Glob(src_file)
+ for item in files:
+ # handle glob patterns relative to package.json directory
+ old_dir = os.getcwd()
+ os.chdir(cwd)
+ try:
+ src_files += Glob(item)
+ finally:
+ os.chdir(old_dir)
- CPPPATH = []
- if 'CPPPATH' in package:
- for path in package['CPPPATH']:
- if path.startswith('/') and os.path.isdir(path):
- CPPPATH = CPPPATH + [path]
- else:
- CPPPATH = CPPPATH + [os.path.join(cwd, path)]
+ src += src_files
- CPPDEFINES = ExtendPackageVar(package, 'CPPDEFINES')
+ CPPPATH += src_CPPPATH
objs = DefineGroup(package['name'], src, depend = depend, CPPPATH = CPPPATH, CPPDEFINES = CPPDEFINES)