feat(cbuild): 重构依赖解析器并新增依赖树打印功能
- 修改 `DependencyResolver.resolve` 方法返回类型为 `None`,不再返回依赖映射副本 - 新增 `print_tree` 方法用于打印格式化的依赖树结构 - 调整 `get_sorted_dependencies` 和 `get_all_contexts` 返回值类型为 `PackageConfig` 列表 - 在 `CPackageBuilder.tree` 中调用新的 `print_tree` 方法以简化逻辑 - 添加 `DummyCompiler` 类作为占位编译器实现 - 优化 argparse 命令行参数结构,将通用参数移到子命令中 - 改进编译器初始化逻辑,增加对参数存在性的检查 - 移除 clean 子命令中的 --all 参数选项
This commit is contained in:
@@ -199,10 +199,10 @@ class DependencyResolver:
|
|||||||
self.dep_map: dict[str, list[str]] = {}
|
self.dep_map: dict[str, list[str]] = {}
|
||||||
self._resolved = False
|
self._resolved = False
|
||||||
|
|
||||||
def resolve(self) -> dict[str, PackageConfig]:
|
def resolve(self) -> None:
|
||||||
"""解析所有依赖"""
|
"""解析所有依赖"""
|
||||||
if self._resolved:
|
if self._resolved:
|
||||||
return self.deps.copy()
|
return
|
||||||
|
|
||||||
# 使用广度优先搜索解析所有依赖
|
# 使用广度优先搜索解析所有依赖
|
||||||
queue = [self.root_package]
|
queue = [self.root_package]
|
||||||
@@ -239,7 +239,41 @@ class DependencyResolver:
|
|||||||
|
|
||||||
self.deps_graph.prepare()
|
self.deps_graph.prepare()
|
||||||
self._resolved = True
|
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]:
|
def get_dependencies_of(self, package_name: str) -> list[str]:
|
||||||
"""获取指定包的直接依赖列表
|
"""获取指定包的直接依赖列表
|
||||||
@@ -252,24 +286,23 @@ class DependencyResolver:
|
|||||||
"""
|
"""
|
||||||
return self.dep_map.get(package_name, [])
|
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())
|
sorted_names = list(self.deps_graph.static_order())
|
||||||
return [
|
return [
|
||||||
CBuildContext(self.deps[name])
|
self.deps[name]
|
||||||
for name in sorted_names
|
for name in sorted_names
|
||||||
if name != self.root_package.name and name in self.deps
|
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:
|
if not self._resolved:
|
||||||
self.resolve()
|
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)
|
@dataclass(frozen=True)
|
||||||
@@ -371,7 +404,7 @@ class CBuildContext:
|
|||||||
@property
|
@property
|
||||||
def includes(self) -> list[Path]:
|
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()]
|
includes = [inc.path.inc_path for inc in deps if inc.path.inc_path.exists()]
|
||||||
return self._path_collection(includes)
|
return self._path_collection(includes)
|
||||||
|
|
||||||
@@ -465,7 +498,7 @@ class CBuildContext:
|
|||||||
|
|
||||||
def get_build_objs(self) -> list[tuple[Path, Path]]:
|
def get_build_objs(self) -> list[tuple[Path, Path]]:
|
||||||
"""获取所有需要编译的源文件及目标文件(排除主文件即main.c, lib.c, bin/*.c)"""
|
"""获取所有需要编译的源文件及目标文件(排除主文件即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 = []
|
objs = []
|
||||||
for dep in deps:
|
for dep in deps:
|
||||||
objs.extend(dep.get_self_build_objs())
|
objs.extend(dep.get_self_build_objs())
|
||||||
@@ -619,6 +652,22 @@ class Compiler(ABC):
|
|||||||
raise
|
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):
|
class ClangCompiler(Compiler):
|
||||||
"""Clang编译器"""
|
"""Clang编译器"""
|
||||||
|
|
||||||
@@ -837,39 +886,7 @@ class CPackageBuilder:
|
|||||||
|
|
||||||
def tree(self):
|
def tree(self):
|
||||||
"""打印构建树"""
|
"""打印构建树"""
|
||||||
print("dependency tree:")
|
self.context.deps_resolver.print_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)
|
|
||||||
|
|
||||||
def tests(self):
|
def tests(self):
|
||||||
"""运行测试"""
|
"""运行测试"""
|
||||||
@@ -919,30 +936,9 @@ def setup_argparse():
|
|||||||
|
|
||||||
# 全局选项
|
# 全局选项
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
"--compiler",
|
"--verbose", "-v", action="store_true", help="Enable verbose logging"
|
||||||
"-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",
|
|
||||||
)
|
)
|
||||||
|
parser.add_argument("--path", "-p", default=".", help="Path to the package")
|
||||||
|
|
||||||
# 创建子命令解析器
|
# 创建子命令解析器
|
||||||
subparsers = parser.add_subparsers(
|
subparsers = parser.add_subparsers(
|
||||||
@@ -950,23 +946,59 @@ def setup_argparse():
|
|||||||
)
|
)
|
||||||
|
|
||||||
# Build 子命令
|
# 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 子命令
|
||||||
run_parser = subparsers.add_parser("run", help="Build and run the project")
|
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(
|
run_parser.add_argument(
|
||||||
"--args", nargs=argparse.REMAINDER, help="Arguments to pass to the program"
|
"--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 子命令
|
||||||
test_parser = subparsers.add_parser("test", help="Build and run tests")
|
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("--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 子命令
|
||||||
clean_parser = subparsers.add_parser("clean", help="Clean build artifacts")
|
subparsers.add_parser("clean", help="Clean build artifacts")
|
||||||
clean_parser.add_argument(
|
|
||||||
"--all", action="store_true", help="Clean all including cache"
|
|
||||||
)
|
|
||||||
|
|
||||||
# Tree 子命令
|
# Tree 子命令
|
||||||
subparsers.add_parser("tree", help="Show dependency tree")
|
subparsers.add_parser("tree", help="Show dependency tree")
|
||||||
@@ -986,19 +1018,20 @@ def main():
|
|||||||
logger.setLevel(logging.INFO)
|
logger.setLevel(logging.INFO)
|
||||||
|
|
||||||
# 初始化编译器
|
# 初始化编译器
|
||||||
match args.compiler:
|
compiler = DummyCompiler()
|
||||||
case "gcc":
|
if hasattr(args, "compiler") and args.compiler is not None:
|
||||||
|
if args.compiler == "gcc":
|
||||||
compiler = GccCompiler()
|
compiler = GccCompiler()
|
||||||
case "clang":
|
elif args.compiler == "clang":
|
||||||
compiler = ClangCompiler()
|
compiler = ClangCompiler()
|
||||||
case "smcc":
|
elif args.compiler == "smcc":
|
||||||
# TODO self compiler
|
# TODO self compiler
|
||||||
raise ValueError("Invalid compiler")
|
raise ValueError("Invalid compiler")
|
||||||
case _:
|
else:
|
||||||
raise ValueError("Invalid compiler")
|
raise ValueError("Invalid compiler")
|
||||||
|
|
||||||
# 启用命令记录
|
# 启用命令记录
|
||||||
if args.record:
|
if hasattr(args, "record") and args.record:
|
||||||
compiler.enable_recording()
|
compiler.enable_recording()
|
||||||
|
|
||||||
# 创建构建器
|
# 创建构建器
|
||||||
@@ -1035,7 +1068,7 @@ def main():
|
|||||||
logger.error("unknown command: %s", args.command)
|
logger.error("unknown command: %s", args.command)
|
||||||
|
|
||||||
# 输出记录的命令(如果有)
|
# 输出记录的命令(如果有)
|
||||||
if args.record:
|
if hasattr(args, "record") and args.record:
|
||||||
cmds = compiler.get_recorded_commands()
|
cmds = compiler.get_recorded_commands()
|
||||||
logger.info("Recorded compiler commands: [len is %s]", len(cmds))
|
logger.info("Recorded compiler commands: [len is %s]", len(cmds))
|
||||||
for cmd in cmds:
|
for cmd in cmds:
|
||||||
|
|||||||
Reference in New Issue
Block a user