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}")