diff --git a/runtime/libcore/include/core_vec.h b/runtime/libcore/include/core_vec.h index e4de0ff..7aa5844 100644 --- a/runtime/libcore/include/core_vec.h +++ b/runtime/libcore/include/core_vec.h @@ -61,9 +61,9 @@ */ #define vec_push(vec, value) \ do { \ - if (vec.size >= vec.cap) { \ - int cap = vec.cap ? vec.cap * 2 : 4; \ - void *data = __vec_realloc(vec.data, cap * sizeof(*vec.data)); \ + if ((vec).size >= (vec).cap) { \ + int cap = (vec).cap ? (vec).cap * 2 : 4; \ + void *data = __vec_realloc((vec).data, cap * sizeof(*(vec).data)); \ if (!data) { \ LOG_FATAL("vector_push: realloc failed\n"); \ } \ diff --git a/tools/cbuild/cbuild.py b/tools/cbuild/cbuild.py index 66c97ad..0a892a1 100644 --- a/tools/cbuild/cbuild.py +++ b/tools/cbuild/cbuild.py @@ -194,14 +194,15 @@ class DependencyResolver: def __init__(self, root_package: PackageConfig): self.root_package = root_package - self.resolved_deps: dict[str, PackageConfig] = {} + self.deps: dict[str, PackageConfig] = {} self.deps_graph: TopologicalSorter = TopologicalSorter() + self.dep_map: dict[str, list[str]] = {} self._resolved = False def resolve(self) -> dict[str, PackageConfig]: """解析所有依赖""" if self._resolved: - return self.resolved_deps + return self.deps.copy() # 使用广度优先搜索解析所有依赖 queue = [self.root_package] @@ -216,8 +217,11 @@ class DependencyResolver: visited.add(pkg_name) # 创建构建上下文 - if pkg_name not in self.resolved_deps: - self.resolved_deps[pkg_name] = pkg_config + if pkg_name not in self.deps: + self.deps[pkg_name] = pkg_config + + if pkg_name not in self.dep_map: + self.dep_map[pkg_name] = [] # 解析直接依赖 for dep in pkg_config.dependencies: @@ -227,14 +231,26 @@ class DependencyResolver: # 添加依赖图关系 self.deps_graph.add(pkg_name, dep_name) + self.dep_map[pkg_name].append(dep_name) # 如果是新依赖,加入队列继续解析 - if dep_name not in visited and dep_name not in self.resolved_deps: + if dep_name not in visited and dep_name not in self.deps: queue.append(dep_config) self.deps_graph.prepare() self._resolved = True - return self.resolved_deps + return self.deps.copy() + + def get_dependencies_of(self, package_name: str) -> list[str]: + """获取指定包的直接依赖列表 + + Args: + package_name (str): 包名称 + + Returns: + list[str]: 直接依赖的包名列表 + """ + return self.dep_map.get(package_name, []) def get_sorted_dependencies(self) -> list[CBuildContext]: """获取按拓扑排序的依赖列表(不包括根包)""" @@ -243,9 +259,9 @@ class DependencyResolver: sorted_names = list(self.deps_graph.static_order()) return [ - CBuildContext(self.resolved_deps[name]) + CBuildContext(self.deps[name]) for name in sorted_names - if name != self.root_package.name and name in self.resolved_deps + if name != self.root_package.name and name in self.deps ] def get_all_contexts(self) -> list[CBuildContext]: @@ -253,9 +269,7 @@ class DependencyResolver: if not self._resolved: self.resolve() - return [ - CBuildContext(resolved_dep) for resolved_dep in self.resolved_deps.values() - ] + return [CBuildContext(resolved_dep) for resolved_dep in self.deps.values()] @dataclass(frozen=True) @@ -556,6 +570,20 @@ class CompilerBuildMode(Enum): class Compiler(ABC): """编译器抽象类""" + def __init__(self): + self.recorded_commands: list[str] = [] + self.should_record = False + + def enable_recording(self, enable=True): + """启用或禁用命令记录""" + self.should_record = enable + if enable: + self.recorded_commands.clear() + + def get_recorded_commands(self): + """获取记录的命令列表""" + return self.recorded_commands.copy() + def get_default_flags(self, mode: CompilerBuildMode) -> list[str]: """获取指定模式的默认标志""" logger.debug("get default flags for mode: %s is not supported", mode.name) @@ -575,6 +603,10 @@ class Compiler(ABC): def cmd(self, cmd: str | list[str]): """执行命令并处理错误输出""" + if self.should_record: + self.recorded_commands.append( + " ".join(cmd) if isinstance(cmd, list) else cmd + ) try: logger.debug( "command: `%s`", " ".join(cmd) if isinstance(cmd, list) else cmd @@ -683,6 +715,7 @@ class CPackageBuilder: self.compiler: Compiler = compiler # FIXME hack context self.cache = BuildCache(self.context.path.output_path) if need_cache else None + self.mode = mode self.global_flags = self.compiler.get_default_flags(mode) def _compile( @@ -767,9 +800,7 @@ class CPackageBuilder: f"{elapsed_time:.2f}s" if elapsed_time < 60 else f"{elapsed_time / 60:.1f}m" ) # 显示完成信息 - logger.info( - "\tFinished dev [unoptimized + debuginfo] target(s) in %s", time_str - ) + logger.info("Finished %s target(s) in %s", self.mode.value, time_str) def run(self): """运行项目""" @@ -805,28 +836,40 @@ class CPackageBuilder: logger.info("没有找到构建目录,无需清理") def tree(self): - """打印构建树 - 仿照 cargo 的风格""" - # TODO 递归显示 + """打印构建树""" print("dependency tree:") # 解析依赖 - resolver = self.context.deps_resolver - resolver.resolve() + deps = self.context.deps_resolver.resolve() # 确保依赖已解析 + root_pkg = self.context.deps_resolver.root_package - # 获取根包 - root_pkg = resolver.root_package - print(f"{root_pkg.name} v{root_pkg.version}") + def print_tree(pkg_name, prefix="", is_last=True): + """递归打印依赖树""" - # 获取依赖图的节点和边 - # 这里我们简化处理,只显示直接依赖 - for pkg_config in resolver.resolved_deps.values(): - if pkg_config.name != root_pkg.name: # 跳过根包 - indent = ( - "├── " - if pkg_config.name != list(resolver.resolved_deps.keys())[-1] - else "└── " - ) - print(f"{indent}{pkg_config.name} v{pkg_config.version}") + # 获取包配置 + 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): """运行测试""" @@ -867,47 +910,14 @@ class CPackageBuilder: failed += 1 print(f"\n测试结果: {passed} 通过, {failed} 失败") - def cmd(self, cmd: str): - """执行命令""" - match cmd: - case "build": - self.build( - [ - TargetType.MAIN_EXECUTABLE, - TargetType.TEST_EXECUTABLE, - TargetType.STATIC_LIBRARY, - TargetType.SHARED_LIBRARY, - TargetType.EXECUTABLE, - ] - ) - case "run": - bin_path = self.context.path.default_bin_path - if not bin_path.exists(): - logger.error("%s not exist", bin_path) - return - self.build([TargetType.MAIN_EXECUTABLE]) - self.run() - case "test": - self.build([TargetType.TEST_EXECUTABLE]) - self.tests() - case "clean": - self.clean() - case "tree": - self.tree() - case _: - logger.error("unknown command: %s", cmd) - -def main(): - """main""" +def setup_argparse(): + """设置分级命令行参数解析器""" parser = argparse.ArgumentParser( description="Simple C Package Manager", prog="cbuild" ) - parser.add_argument( - "command", - choices=["build", "run", "test", "clean", "tree"], - help="Command to execute", - ) + + # 全局选项 parser.add_argument( "--compiler", "-c", @@ -919,7 +929,7 @@ def main(): "--verbose", "-V", action="store_true", - help="enable the logging to debug (default: false)", + help="Enable verbose logging (default: false)", ) parser.add_argument( "--path", @@ -927,15 +937,55 @@ def main(): 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", + ) + # 创建子命令解析器 + subparsers = parser.add_subparsers( + dest="command", help="Available commands", metavar="COMMAND" + ) + + # Build 子命令 + subparsers.add_parser("build", help="Build the project") + + # Run 子命令 + run_parser = subparsers.add_parser("run", help="Build and run the project") + run_parser.add_argument( + "--args", nargs=argparse.REMAINDER, help="Arguments to pass to the program" + ) + + # Test 子命令 + test_parser = subparsers.add_parser("test", help="Build and run tests") + test_parser.add_argument("--filter", help="Filter tests by name pattern") + + # Clean 子命令 + clean_parser = subparsers.add_parser("clean", help="Clean build artifacts") + clean_parser.add_argument( + "--all", action="store_true", help="Clean all including cache" + ) + + # Tree 子命令 + subparsers.add_parser("tree", help="Show dependency tree") + + return parser + + +def main(): + """main function with hierarchical command processing""" + parser = setup_argparse() args = parser.parse_args() - package_path = Path.cwd() / Path(args.path) + # 设置日志级别 if args.verbose: logger.setLevel(logging.DEBUG) else: logger.setLevel(logging.INFO) + # 初始化编译器 match args.compiler: case "gcc": compiler = GccCompiler() @@ -946,8 +996,50 @@ def main(): raise ValueError("Invalid compiler") case _: raise ValueError("Invalid compiler") + + # 启用命令记录 + if args.record: + compiler.enable_recording() + + # 创建构建器 + package_path = Path.cwd() / Path(args.path) builder = CPackageBuilder(package_path, compiler) - builder.cmd(args.command) + + # 处理子命令 + match args.command: + case "build": + builder.build( + [ + TargetType.MAIN_EXECUTABLE, + TargetType.TEST_EXECUTABLE, + TargetType.STATIC_LIBRARY, + TargetType.SHARED_LIBRARY, + TargetType.EXECUTABLE, + ] + ) + case "run": + bin_path = builder.context.path.default_bin_path + if not bin_path.exists(): + logger.error("%s not exist", bin_path) + return + builder.build([TargetType.MAIN_EXECUTABLE]) + builder.run() + case "test": + builder.build([TargetType.TEST_EXECUTABLE]) + builder.tests() + case "clean": + builder.clean() + case "tree": + builder.tree() + case _: + logger.error("unknown command: %s", args.command) + + # 输出记录的命令(如果有) + if args.record: + cmds = compiler.get_recorded_commands() + logger.info("Recorded compiler commands: [len is %s]", len(cmds)) + for cmd in cmds: + print(cmd) if __name__ == "__main__":