import os import re import json import argparse import logging logger = logging.getLogger(__name__) HEADER_ONLY_PROS = [ "无需编译.cpp文件,包含即可用", "模板代码可内联优化", "分发简单(单头文件)", "适合小型模板库", "避免链接顺序问题", ] HEADER_ONLY_CONS = [ "编译时间随包含次数线性增长", "二进制体积膨胀(重复实例化)", "循环依赖风险高", "调试困难(模板展开复杂)", "IDE代码补全和跳转受限", "修改头文件触发全量重编译", ] COMPILED_SEP_PROS = [ "编译时间可控(修改cpp仅重编译单文件)", "二进制体积小(单次实例化)", "可隐藏实现细节(Pimpl模式)", "循环依赖易解(前向声明)", "调试友好(独立编译单元)", "增量编译高效", ] COMPILED_SEP_CONS = [ "需维护hpp/cpp文件对", "模板代码仍需在头文件", "构建系统更复杂", "分发需同时提供头文件和库文件", "链接顺序可能出错", ] def analyze_stub_file(file_path): with open(file_path, "r", encoding="utf-8", errors="ignore") as f: lines = f.readlines() total = len(lines) effective = sum(1 for l in lines if l.strip() and not l.strip().startswith("//")) has_include = any("#include" in l for l in lines) has_namespace = any("namespace" in l for l in lines) has_impl = any(re.search(r'\w+::\w+', l) for l in lines if not l.strip().startswith("//")) is_stub = effective <= 5 and not has_impl return { "path": file_path, "total_lines": total, "effective_lines": effective, "is_stub": is_stub, "has_include": has_include, "has_namespace": has_namespace, "has_implementation": has_impl, } def analyze_hpp_template_ratio(hpp_dir): reports = {} for fname in os.listdir(hpp_dir): if not fname.endswith(".hpp"): continue fpath = os.path.join(hpp_dir, fname) with open(fpath, "r", encoding="utf-8", errors="ignore") as f: lines = f.readlines() total = len(lines) template_lines = sum(1 for l in lines if "template" in l) reports[fname] = {"total_lines": total, "template_lines": template_lines, "ratio": template_lines / total if total > 0 else 0} return reports def make_mode_decision(stub_reports, template_reports): decision = {"mode": "MIXED", "details": []} for fname, info in template_reports.items(): if info["ratio"] > 0.05: decision["details"].append((fname, "HEADER_ONLY", "模板占比高,保持Header-Only")) else: decision["details"].append((fname, "COMPILED_SEP", "模板占比低,迁移到编译分离")) return decision def generate_cmake_patch(current_cmake_path): patch_lines = [ "# === NeuroFlow 文件完整性补丁 ===", "# 在neuroflow_core源文件列表中添加:", "# src/weight_io.cpp", "# 新增训练可执行目标:", "# add_executable(neuroflow_train_v2 src/train_v2.cpp)", "# target_link_libraries(neuroflow_train_v2 PRIVATE neuroflow_core OpenMP::OpenMP_CXX)", ] return "\n".join(patch_lines) def generate_evaluation_report(stub_reports, template_reports, decision, cmake_patch): lines = ["# NeuroFlow 空壳源文件评估报告\n"] lines.append("## 空壳文件分析\n") for r in stub_reports: status = "✅ 空壳" if r["is_stub"] else "❌ 非空壳" lines.append(f"- `{r['path']}`: {r['total_lines']}行, 有效{r['effective_lines']}行, {status}") lines.append("\n## Header-Only vs 编译分离\n") lines.append("### Header-Only 优点\n") for p in HEADER_ONLY_PROS: lines.append(f"- {p}") lines.append("\n### Header-Only 缺点\n") for c in HEADER_ONLY_CONS: lines.append(f"- {c}") lines.append("\n### 编译分离 优点\n") for p in COMPILED_SEP_PROS: lines.append(f"- {p}") lines.append("\n### 编译分离 缺点\n") for c in COMPILED_SEP_CONS: lines.append(f"- {c}") lines.append(f"\n## 混合模式决策: **{decision['mode']}**\n") for fname, mode, reason in decision["details"]: lines.append(f"- `{fname}`: **{mode}** — {reason}") lines.append("\n## CMakeLists.txt 修改方案\n") lines.append(f"```cmake\n{cmake_patch}\n```") return "\n".join(lines) if __name__ == "__main__": logging.basicConfig(level=logging.INFO) parser = argparse.ArgumentParser(description="NeuroFlow空壳源文件评估器") parser.add_argument("--source-dir", type=str, default="src", help="源文件目录") parser.add_argument("--include-dir", type=str, default="include/neuroflow", help="头文件目录") parser.add_argument("--cmake", type=str, default="CMakeLists.txt", help="CMakeLists.txt路径") parser.add_argument("--output", type=str, default="report_stub_evaluation.md", help="输出报告路径") args = parser.parse_args() stub_reports = [] for f in os.listdir(args.source_dir): if f.endswith(".cpp"): stub_reports.append(analyze_stub_file(os.path.join(args.source_dir, f))) template_reports = analyze_hpp_template_ratio(args.include_dir) decision = make_mode_decision(stub_reports, template_reports) cmake_patch = generate_cmake_patch(args.cmake) report = generate_evaluation_report(stub_reports, template_reports, decision, cmake_patch) with open(args.output, "w", encoding="utf-8") as f: f.write(report) logger.info(f"评估报告已保存到 {args.output}")