File size: 5,777 Bytes
26d5b81 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 | 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}") |