From 1ab07a5815332e7d26829a69a3ebde345be73749 Mon Sep 17 00:00:00 2001 From: zzy <2450266535@qq.com> Date: Thu, 27 Nov 2025 15:25:45 +0800 Subject: [PATCH] =?UTF-8?q?feat(cbuild):=20=E9=87=8D=E6=9E=84=E4=BE=9D?= =?UTF-8?q?=E8=B5=96=E8=A7=A3=E6=9E=90=E5=99=A8=E5=B9=B6=E6=96=B0=E5=A2=9E?= =?UTF-8?q?=E4=BE=9D=E8=B5=96=E6=A0=91=E6=89=93=E5=8D=B0=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 修改 `DependencyResolver.resolve` 方法返回类型为 `None`,不再返回依赖映射副本 - 新增 `print_tree` 方法用于打印格式化的依赖树结构 - 调整 `get_sorted_dependencies` 和 `get_all_contexts` 返回值类型为 `PackageConfig` 列表 - 在 `CPackageBuilder.tree` 中调用新的 `print_tree` 方法以简化逻辑 - 添加 `DummyCompiler` 类作为占位编译器实现 - 优化 argparse 命令行参数结构,将通用参数移到子命令中 - 改进编译器初始化逻辑,增加对参数存在性的检查 - 移除 clean 子命令中的 --all 参数选项 --- tools/cbuild/cbuild.py | 191 ++++++++++++++++++++++++----------------- 1 file changed, 112 insertions(+), 79 deletions(-) diff --git a/tools/cbuild/cbuild.py b/tools/cbuild/cbuild.py index 0a892a1..9955766 100644 --- a/tools/cbuild/cbuild.py +++ b/tools/cbuild/cbuild.py @@ -199,10 +199,10 @@ class DependencyResolver: self.dep_map: dict[str, list[str]] = {} self._resolved = False - def resolve(self) -> dict[str, PackageConfig]: + def resolve(self) -> None: """解析所有依赖""" if self._resolved: - return self.deps.copy() + return # 使用广度优先搜索解析所有依赖 queue = [self.root_package] @@ -239,7 +239,41 @@ class DependencyResolver: self.deps_graph.prepare() self._resolved = True - return self.deps.copy() + + def print_tree(self) -> None: + """打印依赖树""" + self.resolve() + print("dependency tree:") + + root_pkg = self.root_package + + def print_tree(pkg_name, prefix="", is_last=True): + """递归打印依赖树""" + + # 获取包配置 + pkg_config = self.deps[pkg_name] + + # 打印当前包 + if pkg_name == root_pkg.name: + print(f"{pkg_config.name} v{pkg_config.version}") + else: + connector = "└── " if is_last else "├── " + print(f"{prefix}{connector}{pkg_config.name} v{pkg_config.version}") + + # 计算子节点的前缀 + if pkg_name == root_pkg.name: + child_prefix = "" + else: + child_prefix = prefix + (" " if is_last else "│ ") + + # 递归打印依赖 + dependencies = self.get_dependencies_of(pkg_name) + for i, dep_name in enumerate(dependencies): + is_last_child = i == len(dependencies) - 1 + print_tree(dep_name, child_prefix, is_last_child) + + # 从根包开始打印树 + print_tree(root_pkg.name) def get_dependencies_of(self, package_name: str) -> list[str]: """获取指定包的直接依赖列表 @@ -252,24 +286,23 @@ class DependencyResolver: """ return self.dep_map.get(package_name, []) - def get_sorted_dependencies(self) -> list[CBuildContext]: + def get_sorted_dependencies(self) -> list[PackageConfig]: """获取按拓扑排序的依赖列表(不包括根包)""" - if not self._resolved: - self.resolve() + self.resolve() sorted_names = list(self.deps_graph.static_order()) return [ - CBuildContext(self.deps[name]) + self.deps[name] for name in sorted_names if name != self.root_package.name and name in self.deps ] - def get_all_contexts(self) -> list[CBuildContext]: + def get_all_contexts(self) -> list[PackageConfig]: """获取所有上下文(包括根包)""" if not self._resolved: self.resolve() - return [CBuildContext(resolved_dep) for resolved_dep in self.deps.values()] + return [resolved_dep for resolved_dep in self.deps.values()] @dataclass(frozen=True) @@ -371,7 +404,7 @@ class CBuildContext: @property def includes(self) -> list[Path]: """获取包的包含路径""" - deps = self.deps_resolver.get_all_contexts() + deps = [CBuildContext(i) for i in self.deps_resolver.get_all_contexts()] includes = [inc.path.inc_path for inc in deps if inc.path.inc_path.exists()] return self._path_collection(includes) @@ -465,7 +498,7 @@ class CBuildContext: def get_build_objs(self) -> list[tuple[Path, Path]]: """获取所有需要编译的源文件及目标文件(排除主文件即main.c, lib.c, bin/*.c)""" - deps = self.deps_resolver.get_all_contexts() + deps = [CBuildContext(i) for i in self.deps_resolver.get_all_contexts()] objs = [] for dep in deps: objs.extend(dep.get_self_build_objs()) @@ -619,6 +652,22 @@ class Compiler(ABC): raise +class DummyCompiler(Compiler): + """Dummy编译器""" + + def compile( + self, sources: Path, output: Path, includes: list[Path], flags: list[str] + ): + """编译源文件""" + pass + + def link( + self, objects: list[Path], libraries: list[str], flags: list[str], output: Path + ): + """链接对象文件""" + pass + + class ClangCompiler(Compiler): """Clang编译器""" @@ -837,39 +886,7 @@ class CPackageBuilder: def tree(self): """打印构建树""" - print("dependency tree:") - - # 解析依赖 - deps = self.context.deps_resolver.resolve() # 确保依赖已解析 - root_pkg = self.context.deps_resolver.root_package - - def print_tree(pkg_name, prefix="", is_last=True): - """递归打印依赖树""" - - # 获取包配置 - pkg_config = deps[pkg_name] - - # 打印当前包 - if pkg_name == root_pkg.name: - print(f"{pkg_config.name} v{pkg_config.version}") - else: - connector = "└── " if is_last else "├── " - print(f"{prefix}{connector}{pkg_config.name} v{pkg_config.version}") - - # 计算子节点的前缀 - if pkg_name == root_pkg.name: - child_prefix = "" - else: - child_prefix = prefix + (" " if is_last else "│ ") - - # 递归打印依赖 - dependencies = self.context.deps_resolver.get_dependencies_of(pkg_name) - for i, dep_name in enumerate(dependencies): - is_last_child = i == len(dependencies) - 1 - print_tree(dep_name, child_prefix, is_last_child) - - # 从根包开始打印树 - print_tree(root_pkg.name) + self.context.deps_resolver.print_tree() def tests(self): """运行测试""" @@ -919,30 +936,9 @@ def setup_argparse(): # 全局选项 parser.add_argument( - "--compiler", - "-c", - choices=["gcc", "clang", "smcc"], - default="gcc", - help="Compiler to use (default: gcc)", - ) - parser.add_argument( - "--verbose", - "-V", - action="store_true", - help="Enable verbose logging (default: false)", - ) - parser.add_argument( - "--path", - "-p", - default=".", - help="Path to the package (default: current directory)", - ) - parser.add_argument( - "--record", - "-r", - action="store_true", - help="Record the compiler command and output to stdout", + "--verbose", "-v", action="store_true", help="Enable verbose logging" ) + parser.add_argument("--path", "-p", default=".", help="Path to the package") # 创建子命令解析器 subparsers = parser.add_subparsers( @@ -950,23 +946,59 @@ def setup_argparse(): ) # Build 子命令 - subparsers.add_parser("build", help="Build the project") + build_parser = subparsers.add_parser("build", help="Build the project") + build_parser.add_argument( + "--compiler", + "-c", + choices=["gcc", "clang", "smcc"], + default="gcc", + help="Compiler to use (default: gcc)", + ) + build_parser.add_argument( + "--record", + "-r", + action="store_true", + help="Record the compiler command and output to stdout", + ) # Run 子命令 run_parser = subparsers.add_parser("run", help="Build and run the project") + run_parser.add_argument( + "--compiler", + "-c", + choices=["gcc", "clang", "smcc"], + default="gcc", + help="Compiler to use (default: gcc)", + ) run_parser.add_argument( "--args", nargs=argparse.REMAINDER, help="Arguments to pass to the program" ) + run_parser.add_argument( + "--record", + "-r", + action="store_true", + help="Record the compiler command and output to stdout", + ) # Test 子命令 test_parser = subparsers.add_parser("test", help="Build and run tests") + test_parser.add_argument( + "--compiler", + "-c", + choices=["gcc", "clang", "smcc"], + default="gcc", + help="Compiler to use (default: gcc)", + ) test_parser.add_argument("--filter", help="Filter tests by name pattern") + test_parser.add_argument( + "--record", + "-r", + action="store_true", + help="Record the compiler command and output to stdout", + ) # Clean 子命令 - clean_parser = subparsers.add_parser("clean", help="Clean build artifacts") - clean_parser.add_argument( - "--all", action="store_true", help="Clean all including cache" - ) + subparsers.add_parser("clean", help="Clean build artifacts") # Tree 子命令 subparsers.add_parser("tree", help="Show dependency tree") @@ -986,19 +1018,20 @@ def main(): logger.setLevel(logging.INFO) # 初始化编译器 - match args.compiler: - case "gcc": + compiler = DummyCompiler() + if hasattr(args, "compiler") and args.compiler is not None: + if args.compiler == "gcc": compiler = GccCompiler() - case "clang": + elif args.compiler == "clang": compiler = ClangCompiler() - case "smcc": + elif args.compiler == "smcc": # TODO self compiler raise ValueError("Invalid compiler") - case _: + else: raise ValueError("Invalid compiler") # 启用命令记录 - if args.record: + if hasattr(args, "record") and args.record: compiler.enable_recording() # 创建构建器 @@ -1035,7 +1068,7 @@ def main(): logger.error("unknown command: %s", args.command) # 输出记录的命令(如果有) - if args.record: + if hasattr(args, "record") and args.record: cmds = compiler.get_recorded_commands() logger.info("Recorded compiler commands: [len is %s]", len(cmds)) for cmd in cmds: