jarvis-ai-assistant 0.7.0__py3-none-any.whl → 0.7.6__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- jarvis/__init__.py +1 -1
- jarvis/jarvis_agent/__init__.py +243 -139
- jarvis/jarvis_agent/agent_manager.py +5 -10
- jarvis/jarvis_agent/builtin_input_handler.py +2 -6
- jarvis/jarvis_agent/config_editor.py +2 -7
- jarvis/jarvis_agent/event_bus.py +82 -12
- jarvis/jarvis_agent/file_context_handler.py +265 -15
- jarvis/jarvis_agent/file_methodology_manager.py +3 -4
- jarvis/jarvis_agent/jarvis.py +113 -98
- jarvis/jarvis_agent/language_extractors/__init__.py +57 -0
- jarvis/jarvis_agent/language_extractors/c_extractor.py +21 -0
- jarvis/jarvis_agent/language_extractors/cpp_extractor.py +21 -0
- jarvis/jarvis_agent/language_extractors/go_extractor.py +21 -0
- jarvis/jarvis_agent/language_extractors/java_extractor.py +84 -0
- jarvis/jarvis_agent/language_extractors/javascript_extractor.py +79 -0
- jarvis/jarvis_agent/language_extractors/python_extractor.py +21 -0
- jarvis/jarvis_agent/language_extractors/rust_extractor.py +21 -0
- jarvis/jarvis_agent/language_extractors/typescript_extractor.py +84 -0
- jarvis/jarvis_agent/language_support_info.py +486 -0
- jarvis/jarvis_agent/main.py +6 -12
- jarvis/jarvis_agent/memory_manager.py +7 -16
- jarvis/jarvis_agent/methodology_share_manager.py +10 -16
- jarvis/jarvis_agent/prompt_manager.py +1 -1
- jarvis/jarvis_agent/prompts.py +193 -171
- jarvis/jarvis_agent/protocols.py +8 -12
- jarvis/jarvis_agent/run_loop.py +77 -14
- jarvis/jarvis_agent/session_manager.py +2 -3
- jarvis/jarvis_agent/share_manager.py +12 -21
- jarvis/jarvis_agent/shell_input_handler.py +1 -2
- jarvis/jarvis_agent/task_analyzer.py +26 -4
- jarvis/jarvis_agent/task_manager.py +11 -27
- jarvis/jarvis_agent/tool_executor.py +2 -3
- jarvis/jarvis_agent/tool_share_manager.py +12 -24
- jarvis/jarvis_agent/web_server.py +55 -20
- jarvis/jarvis_c2rust/__init__.py +5 -5
- jarvis/jarvis_c2rust/cli.py +461 -499
- jarvis/jarvis_c2rust/collector.py +45 -53
- jarvis/jarvis_c2rust/constants.py +26 -0
- jarvis/jarvis_c2rust/library_replacer.py +264 -132
- jarvis/jarvis_c2rust/llm_module_agent.py +162 -190
- jarvis/jarvis_c2rust/loaders.py +207 -0
- jarvis/jarvis_c2rust/models.py +28 -0
- jarvis/jarvis_c2rust/optimizer.py +1592 -395
- jarvis/jarvis_c2rust/transpiler.py +1722 -1064
- jarvis/jarvis_c2rust/utils.py +385 -0
- jarvis/jarvis_code_agent/build_validation_config.py +2 -3
- jarvis/jarvis_code_agent/code_agent.py +394 -320
- jarvis/jarvis_code_agent/code_analyzer/__init__.py +3 -0
- jarvis/jarvis_code_agent/code_analyzer/build_validator/base.py +4 -0
- jarvis/jarvis_code_agent/code_analyzer/build_validator/cmake.py +17 -2
- jarvis/jarvis_code_agent/code_analyzer/build_validator/fallback.py +3 -0
- jarvis/jarvis_code_agent/code_analyzer/build_validator/go.py +36 -4
- jarvis/jarvis_code_agent/code_analyzer/build_validator/java_gradle.py +9 -0
- jarvis/jarvis_code_agent/code_analyzer/build_validator/java_maven.py +9 -0
- jarvis/jarvis_code_agent/code_analyzer/build_validator/makefile.py +12 -1
- jarvis/jarvis_code_agent/code_analyzer/build_validator/nodejs.py +22 -5
- jarvis/jarvis_code_agent/code_analyzer/build_validator/python.py +57 -32
- jarvis/jarvis_code_agent/code_analyzer/build_validator/rust.py +62 -6
- jarvis/jarvis_code_agent/code_analyzer/build_validator/validator.py +8 -9
- jarvis/jarvis_code_agent/code_analyzer/context_manager.py +290 -5
- jarvis/jarvis_code_agent/code_analyzer/language_support.py +21 -0
- jarvis/jarvis_code_agent/code_analyzer/languages/__init__.py +21 -3
- jarvis/jarvis_code_agent/code_analyzer/languages/c_cpp_language.py +72 -4
- jarvis/jarvis_code_agent/code_analyzer/languages/go_language.py +35 -3
- jarvis/jarvis_code_agent/code_analyzer/languages/java_language.py +212 -0
- jarvis/jarvis_code_agent/code_analyzer/languages/javascript_language.py +254 -0
- jarvis/jarvis_code_agent/code_analyzer/languages/python_language.py +52 -2
- jarvis/jarvis_code_agent/code_analyzer/languages/rust_language.py +73 -1
- jarvis/jarvis_code_agent/code_analyzer/languages/typescript_language.py +280 -0
- jarvis/jarvis_code_agent/code_analyzer/llm_context_recommender.py +306 -152
- jarvis/jarvis_code_agent/code_analyzer/structured_code.py +556 -0
- jarvis/jarvis_code_agent/code_analyzer/symbol_extractor.py +193 -18
- jarvis/jarvis_code_agent/code_analyzer/tree_sitter_extractor.py +18 -8
- jarvis/jarvis_code_agent/lint.py +258 -27
- jarvis/jarvis_code_agent/utils.py +0 -1
- jarvis/jarvis_code_analysis/code_review.py +19 -24
- jarvis/jarvis_data/config_schema.json +53 -26
- jarvis/jarvis_git_squash/main.py +4 -5
- jarvis/jarvis_git_utils/git_commiter.py +44 -49
- jarvis/jarvis_mcp/sse_mcp_client.py +20 -27
- jarvis/jarvis_mcp/stdio_mcp_client.py +11 -12
- jarvis/jarvis_mcp/streamable_mcp_client.py +15 -14
- jarvis/jarvis_memory_organizer/memory_organizer.py +55 -74
- jarvis/jarvis_methodology/main.py +32 -48
- jarvis/jarvis_multi_agent/__init__.py +79 -61
- jarvis/jarvis_multi_agent/main.py +3 -7
- jarvis/jarvis_platform/base.py +469 -199
- jarvis/jarvis_platform/human.py +7 -8
- jarvis/jarvis_platform/kimi.py +30 -36
- jarvis/jarvis_platform/openai.py +65 -27
- jarvis/jarvis_platform/registry.py +26 -10
- jarvis/jarvis_platform/tongyi.py +24 -25
- jarvis/jarvis_platform/yuanbao.py +31 -42
- jarvis/jarvis_platform_manager/main.py +66 -77
- jarvis/jarvis_platform_manager/service.py +8 -13
- jarvis/jarvis_rag/cli.py +49 -51
- jarvis/jarvis_rag/embedding_manager.py +13 -18
- jarvis/jarvis_rag/llm_interface.py +8 -9
- jarvis/jarvis_rag/query_rewriter.py +10 -21
- jarvis/jarvis_rag/rag_pipeline.py +24 -27
- jarvis/jarvis_rag/reranker.py +4 -5
- jarvis/jarvis_rag/retriever.py +28 -30
- jarvis/jarvis_sec/__init__.py +220 -3520
- jarvis/jarvis_sec/agents.py +143 -0
- jarvis/jarvis_sec/analysis.py +276 -0
- jarvis/jarvis_sec/cli.py +29 -6
- jarvis/jarvis_sec/clustering.py +1439 -0
- jarvis/jarvis_sec/file_manager.py +427 -0
- jarvis/jarvis_sec/parsers.py +73 -0
- jarvis/jarvis_sec/prompts.py +268 -0
- jarvis/jarvis_sec/report.py +83 -4
- jarvis/jarvis_sec/review.py +453 -0
- jarvis/jarvis_sec/utils.py +499 -0
- jarvis/jarvis_sec/verification.py +848 -0
- jarvis/jarvis_sec/workflow.py +7 -0
- jarvis/jarvis_smart_shell/main.py +38 -87
- jarvis/jarvis_stats/cli.py +1 -1
- jarvis/jarvis_stats/stats.py +7 -7
- jarvis/jarvis_stats/storage.py +15 -21
- jarvis/jarvis_tools/clear_memory.py +3 -20
- jarvis/jarvis_tools/cli/main.py +20 -23
- jarvis/jarvis_tools/edit_file.py +1066 -0
- jarvis/jarvis_tools/execute_script.py +42 -21
- jarvis/jarvis_tools/file_analyzer.py +6 -9
- jarvis/jarvis_tools/generate_new_tool.py +11 -20
- jarvis/jarvis_tools/lsp_client.py +1552 -0
- jarvis/jarvis_tools/methodology.py +2 -3
- jarvis/jarvis_tools/read_code.py +1525 -87
- jarvis/jarvis_tools/read_symbols.py +2 -3
- jarvis/jarvis_tools/read_webpage.py +7 -10
- jarvis/jarvis_tools/registry.py +370 -181
- jarvis/jarvis_tools/retrieve_memory.py +20 -19
- jarvis/jarvis_tools/rewrite_file.py +105 -0
- jarvis/jarvis_tools/save_memory.py +3 -15
- jarvis/jarvis_tools/search_web.py +3 -7
- jarvis/jarvis_tools/sub_agent.py +17 -6
- jarvis/jarvis_tools/sub_code_agent.py +14 -16
- jarvis/jarvis_tools/virtual_tty.py +54 -32
- jarvis/jarvis_utils/clipboard.py +7 -10
- jarvis/jarvis_utils/config.py +98 -63
- jarvis/jarvis_utils/embedding.py +5 -5
- jarvis/jarvis_utils/fzf.py +8 -8
- jarvis/jarvis_utils/git_utils.py +81 -67
- jarvis/jarvis_utils/input.py +24 -49
- jarvis/jarvis_utils/jsonnet_compat.py +465 -0
- jarvis/jarvis_utils/methodology.py +33 -35
- jarvis/jarvis_utils/utils.py +245 -202
- {jarvis_ai_assistant-0.7.0.dist-info → jarvis_ai_assistant-0.7.6.dist-info}/METADATA +205 -70
- jarvis_ai_assistant-0.7.6.dist-info/RECORD +218 -0
- jarvis/jarvis_agent/edit_file_handler.py +0 -584
- jarvis/jarvis_agent/rewrite_file_handler.py +0 -141
- jarvis/jarvis_agent/task_planner.py +0 -496
- jarvis/jarvis_platform/ai8.py +0 -332
- jarvis/jarvis_tools/ask_user.py +0 -54
- jarvis_ai_assistant-0.7.0.dist-info/RECORD +0 -192
- {jarvis_ai_assistant-0.7.0.dist-info → jarvis_ai_assistant-0.7.6.dist-info}/WHEEL +0 -0
- {jarvis_ai_assistant-0.7.0.dist-info → jarvis_ai_assistant-0.7.6.dist-info}/entry_points.txt +0 -0
- {jarvis_ai_assistant-0.7.0.dist-info → jarvis_ai_assistant-0.7.6.dist-info}/licenses/LICENSE +0 -0
- {jarvis_ai_assistant-0.7.0.dist-info → jarvis_ai_assistant-0.7.6.dist-info}/top_level.txt +0 -0
jarvis/jarvis_c2rust/cli.py
CHANGED
|
@@ -2,12 +2,13 @@
|
|
|
2
2
|
"""
|
|
3
3
|
C2Rust 独立命令行入口。
|
|
4
4
|
|
|
5
|
-
提供分组式 CLI
|
|
6
|
-
- jarvis-c2rust scan
|
|
5
|
+
提供分组式 CLI:
|
|
6
|
+
- jarvis-c2rust run: 执行完整的转译流水线(scan -> lib-replace -> prepare -> transpile -> optimize),支持断点续跑
|
|
7
|
+
- jarvis-c2rust config: 管理转译配置文件(根符号列表、禁用库列表、附加说明等)
|
|
7
8
|
|
|
8
9
|
实现策略:
|
|
9
|
-
- 复用 scanner.cli 的核心逻辑,避免重复代码。
|
|
10
10
|
- 使用 Typer 分组式结构,便于后续扩展更多子命令(如 analyze/export 等)。
|
|
11
|
+
- run 命令支持断点续跑,根据状态文件自动跳过已完成的阶段。
|
|
11
12
|
"""
|
|
12
13
|
|
|
13
14
|
from __future__ import annotations
|
|
@@ -26,6 +27,62 @@ from jarvis.jarvis_c2rust.llm_module_agent import (
|
|
|
26
27
|
execute_llm_plan as _execute_llm_plan,
|
|
27
28
|
)
|
|
28
29
|
|
|
30
|
+
|
|
31
|
+
def _check_optimize_completed(crate_dir: Path) -> bool:
|
|
32
|
+
"""
|
|
33
|
+
检查优化是否真正完成。
|
|
34
|
+
需要检查 optimize_progress.json 中所有必要的步骤是否都完成了。
|
|
35
|
+
特别是 clippy_elimination:如果有告警,必须完成;如果没有告警,可以跳过。
|
|
36
|
+
"""
|
|
37
|
+
import json
|
|
38
|
+
from jarvis.jarvis_c2rust.constants import C2RUST_DIRNAME
|
|
39
|
+
|
|
40
|
+
progress_path = crate_dir / C2RUST_DIRNAME / "optimize_progress.json"
|
|
41
|
+
if not progress_path.exists():
|
|
42
|
+
# 如果没有进度文件,说明还没开始,不算完成
|
|
43
|
+
return False
|
|
44
|
+
|
|
45
|
+
try:
|
|
46
|
+
with progress_path.open("r", encoding="utf-8") as f:
|
|
47
|
+
progress = json.load(f)
|
|
48
|
+
|
|
49
|
+
steps_completed = set(progress.get("steps_completed", []))
|
|
50
|
+
|
|
51
|
+
# 检查是否有 clippy 告警
|
|
52
|
+
# 直接调用 optimizer 模块中的函数(虽然是私有函数,但我们需要它来检查)
|
|
53
|
+
import subprocess
|
|
54
|
+
try:
|
|
55
|
+
res = subprocess.run(
|
|
56
|
+
["cargo", "clippy", "--", "-W", "clippy::all"],
|
|
57
|
+
capture_output=True,
|
|
58
|
+
text=True,
|
|
59
|
+
check=False,
|
|
60
|
+
cwd=str(crate_dir),
|
|
61
|
+
)
|
|
62
|
+
stderr_output = (res.stderr or "").strip()
|
|
63
|
+
stdout_output = (res.stdout or "").strip()
|
|
64
|
+
output = (stderr_output + "\n" + stdout_output).strip() if (stderr_output and stdout_output) else (stderr_output or stdout_output or "").strip()
|
|
65
|
+
output_lower = output.lower()
|
|
66
|
+
has_warnings = "warning:" in output_lower or "warn(" in output_lower or ("clippy::" in output_lower and res.returncode != 0)
|
|
67
|
+
except Exception:
|
|
68
|
+
# 如果检查失败,保守地认为有告警(需要完成)
|
|
69
|
+
has_warnings = True
|
|
70
|
+
|
|
71
|
+
# 如果有告警,clippy_elimination 必须在 steps_completed 中
|
|
72
|
+
if has_warnings:
|
|
73
|
+
if "clippy_elimination" not in steps_completed:
|
|
74
|
+
return False
|
|
75
|
+
|
|
76
|
+
# 检查其他必要的步骤(根据 enable_* 选项,但这里我们假设都启用了)
|
|
77
|
+
# 注意:这里我们只检查 clippy_elimination,其他步骤(unsafe_cleanup, visibility_opt, doc_opt)
|
|
78
|
+
# 可能因为 enable_* 选项而未执行,所以不强制要求
|
|
79
|
+
|
|
80
|
+
return True
|
|
81
|
+
except Exception:
|
|
82
|
+
# 如果读取失败,保守地认为未完成
|
|
83
|
+
return False
|
|
84
|
+
|
|
85
|
+
|
|
29
86
|
app = typer.Typer(help="C2Rust 命令行工具")
|
|
30
87
|
|
|
31
88
|
# 显式定义根回调,确保为命令组而非单函数入口
|
|
@@ -37,572 +94,477 @@ def _root():
|
|
|
37
94
|
# 设置环境变量,标识当前运行在 c2rust 环境中
|
|
38
95
|
os.environ["JARVIS_C2RUST_ENABLED"] = "1"
|
|
39
96
|
# 不做任何处理,仅作为命令组的占位,使 'scan' 作为子命令出现
|
|
40
|
-
init_env("
|
|
97
|
+
init_env("")
|
|
41
98
|
pass
|
|
42
99
|
|
|
43
100
|
|
|
44
|
-
|
|
45
|
-
def scan(
|
|
46
|
-
dot: Optional[Path] = typer.Option(
|
|
47
|
-
None,
|
|
48
|
-
"--dot",
|
|
49
|
-
help="扫描后将引用依赖图写入 DOT 文件(或与 --only-dot 一起使用)",
|
|
50
|
-
),
|
|
51
|
-
only_dot: bool = typer.Option(
|
|
52
|
-
False,
|
|
53
|
-
"--only-dot",
|
|
54
|
-
help="不重新扫描。读取现有数据 (JSONL) 并仅生成 DOT(需要 --dot)",
|
|
55
|
-
),
|
|
56
|
-
subgraphs_dir: Optional[Path] = typer.Option(
|
|
57
|
-
None,
|
|
58
|
-
"--subgraphs-dir",
|
|
59
|
-
help="用于写入每个根函数引用子图 DOT 文件的目录(每个根函数一个文件)",
|
|
60
|
-
),
|
|
61
|
-
only_subgraphs: bool = typer.Option(
|
|
62
|
-
False,
|
|
63
|
-
"--only-subgraphs",
|
|
64
|
-
help="不重新扫描。仅生成每个根函数的引用子图 DOT 文件(需要 --subgraphs-dir)",
|
|
65
|
-
),
|
|
66
|
-
interactive: bool = typer.Option(
|
|
67
|
-
False,
|
|
68
|
-
"--interactive",
|
|
69
|
-
help="启用交互模式(默认非交互模式)",
|
|
70
|
-
),
|
|
71
|
-
) -> None:
|
|
72
|
-
"""
|
|
73
|
-
进行 C/C++ 函数扫描并生成引用关系 DOT 图;PNG 渲染默认启用(无需参数)。
|
|
74
|
-
"""
|
|
75
|
-
_run_scan(
|
|
76
|
-
dot=dot,
|
|
77
|
-
only_dot=only_dot,
|
|
78
|
-
subgraphs_dir=subgraphs_dir,
|
|
79
|
-
only_subgraphs=only_subgraphs,
|
|
80
|
-
png=True,
|
|
81
|
-
non_interactive=not interactive,
|
|
82
|
-
)
|
|
83
|
-
|
|
84
|
-
@app.command("prepare")
|
|
85
|
-
def prepare(
|
|
86
|
-
llm_group: Optional[str] = typer.Option(
|
|
87
|
-
None, "-g", "--llm-group", help="指定用于规划的 LLM 模型组(仅影响本次运行)"
|
|
88
|
-
),
|
|
89
|
-
interactive: bool = typer.Option(
|
|
90
|
-
False,
|
|
91
|
-
"--interactive",
|
|
92
|
-
help="启用交互模式(默认非交互模式)",
|
|
93
|
-
),
|
|
94
|
-
) -> None:
|
|
101
|
+
def _load_config() -> dict:
|
|
95
102
|
"""
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
默认使用当前目录作为项目根,并从 <root>/.jarvis/c2rust/symbols.jsonl 读取数据
|
|
103
|
+
从配置文件加载配置。
|
|
104
|
+
返回包含 root_symbols、disabled_libraries 和 additional_notes 的字典。
|
|
99
105
|
"""
|
|
106
|
+
import json
|
|
107
|
+
from jarvis.jarvis_c2rust.constants import CONFIG_JSON, C2RUST_DIRNAME
|
|
108
|
+
|
|
109
|
+
data_dir = Path(".") / C2RUST_DIRNAME
|
|
110
|
+
config_path = data_dir / CONFIG_JSON
|
|
111
|
+
default_config = {"root_symbols": [], "disabled_libraries": [], "additional_notes": ""}
|
|
112
|
+
|
|
113
|
+
if not config_path.exists():
|
|
114
|
+
return default_config
|
|
115
|
+
|
|
100
116
|
try:
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
117
|
+
with config_path.open("r", encoding="utf-8") as f:
|
|
118
|
+
config = json.load(f)
|
|
119
|
+
if not isinstance(config, dict):
|
|
120
|
+
return default_config
|
|
121
|
+
# 确保包含所有必需的键
|
|
122
|
+
return {
|
|
123
|
+
"root_symbols": config.get("root_symbols", []),
|
|
124
|
+
"disabled_libraries": config.get("disabled_libraries", []),
|
|
125
|
+
"additional_notes": config.get("additional_notes", ""),
|
|
126
|
+
}
|
|
127
|
+
except Exception:
|
|
128
|
+
return default_config
|
|
105
129
|
|
|
106
130
|
|
|
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
|
-
|
|
131
|
+
RUN_STATE_JSON = "run_state.json"
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
def _get_run_state_path() -> Path:
|
|
135
|
+
"""获取 run 状态文件路径"""
|
|
136
|
+
from jarvis.jarvis_c2rust.constants import C2RUST_DIRNAME
|
|
137
|
+
data_dir = Path(".") / C2RUST_DIRNAME
|
|
138
|
+
return data_dir / RUN_STATE_JSON
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
def _load_run_state() -> dict:
|
|
142
|
+
"""加载 run 状态文件"""
|
|
143
|
+
import json
|
|
144
|
+
|
|
145
|
+
state_path = _get_run_state_path()
|
|
146
|
+
default_state = {
|
|
147
|
+
"scan": {"completed": False, "timestamp": None},
|
|
148
|
+
"lib_replace": {"completed": False, "timestamp": None},
|
|
149
|
+
"prepare": {"completed": False, "timestamp": None},
|
|
150
|
+
"transpile": {"completed": False, "timestamp": None},
|
|
151
|
+
"optimize": {"completed": False, "timestamp": None},
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
if not state_path.exists():
|
|
155
|
+
return default_state
|
|
156
|
+
|
|
157
|
+
try:
|
|
158
|
+
with state_path.open("r", encoding="utf-8") as f:
|
|
159
|
+
state = json.load(f)
|
|
160
|
+
if not isinstance(state, dict):
|
|
161
|
+
return default_state
|
|
162
|
+
# 确保包含所有必需的阶段
|
|
163
|
+
for stage in ["scan", "lib_replace", "prepare", "transpile", "optimize"]:
|
|
164
|
+
if stage not in state:
|
|
165
|
+
state[stage] = {"completed": False, "timestamp": None}
|
|
166
|
+
return state
|
|
167
|
+
except Exception:
|
|
168
|
+
return default_state
|
|
169
|
+
|
|
170
|
+
|
|
171
|
+
def _save_run_state(stage: str, completed: bool = True) -> None:
|
|
172
|
+
"""保存 run 状态文件"""
|
|
173
|
+
import json
|
|
174
|
+
import time
|
|
175
|
+
|
|
176
|
+
state_path = _get_run_state_path()
|
|
177
|
+
state = _load_run_state()
|
|
178
|
+
|
|
179
|
+
state[stage] = {
|
|
180
|
+
"completed": completed,
|
|
181
|
+
"timestamp": time.strftime("%Y-%m-%dT%H:%M:%S", time.localtime()) if completed else None,
|
|
182
|
+
}
|
|
183
|
+
|
|
139
184
|
try:
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
_run_transpile(
|
|
144
|
-
project_root=Path("."),
|
|
145
|
-
crate_dir=None,
|
|
146
|
-
llm_group=llm_group,
|
|
147
|
-
max_retries=max_retries,
|
|
148
|
-
resume=resume,
|
|
149
|
-
only=only_list,
|
|
150
|
-
non_interactive=not interactive,
|
|
151
|
-
)
|
|
185
|
+
state_path.parent.mkdir(parents=True, exist_ok=True)
|
|
186
|
+
with state_path.open("w", encoding="utf-8") as f:
|
|
187
|
+
json.dump(state, f, ensure_ascii=False, indent=2)
|
|
152
188
|
except Exception as e:
|
|
153
|
-
typer.secho(f"[c2rust-
|
|
154
|
-
raise typer.Exit(code=1)
|
|
189
|
+
typer.secho(f"[c2rust-run] 保存状态文件失败: {e}", fg=typer.colors.YELLOW, err=True)
|
|
155
190
|
|
|
156
191
|
|
|
157
|
-
@app.command("
|
|
158
|
-
def
|
|
159
|
-
|
|
160
|
-
None, "
|
|
161
|
-
),
|
|
162
|
-
root_list_file: Optional[Path] = typer.Option(
|
|
163
|
-
None, "--root-list-file", help="根列表文件:按行列出要参与评估的根符号名称或限定名(忽略空行与以#开头的注释)"
|
|
192
|
+
@app.command("config")
|
|
193
|
+
def config(
|
|
194
|
+
files: Optional[List[Path]] = typer.Option(
|
|
195
|
+
None, "--files", help="头文件(.h/.hh/.hpp/.hxx)或函数名列表文件(每行一个函数名,忽略空行与以#开头的注释)"
|
|
164
196
|
),
|
|
165
197
|
root_list_syms: Optional[str] = typer.Option(
|
|
166
|
-
None, "--root-list-syms", help="
|
|
198
|
+
None, "--root-list-syms", help="根符号列表内联(逗号分隔)"
|
|
167
199
|
),
|
|
168
200
|
disabled_libs: Optional[str] = typer.Option(
|
|
169
|
-
None, "--disabled-libs", help="
|
|
201
|
+
None, "--disabled-libs", help="禁用库列表(逗号分隔)"
|
|
170
202
|
),
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
203
|
+
additional_notes: Optional[str] = typer.Option(
|
|
204
|
+
None, "--additional-notes", help="附加说明(将在所有 agent 的提示词中追加)"
|
|
205
|
+
),
|
|
206
|
+
show: bool = typer.Option(
|
|
207
|
+
False, "--show", help="显示当前配置内容"
|
|
208
|
+
),
|
|
209
|
+
clear: bool = typer.Option(
|
|
210
|
+
False, "--clear", help="清空配置(重置为默认值)"
|
|
175
211
|
),
|
|
176
212
|
) -> None:
|
|
177
213
|
"""
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
214
|
+
管理转译配置文件(.jarvis/c2rust/config.json)。
|
|
215
|
+
|
|
216
|
+
可以设置根符号列表(root_symbols)、禁用库列表(disabled_libraries)和附加说明(additional_notes)。
|
|
217
|
+
这些配置会被 transpile 命令自动读取和使用。
|
|
218
|
+
|
|
219
|
+
示例:
|
|
220
|
+
# 从头文件自动提取函数名并设置根符号列表
|
|
221
|
+
jarvis-c2rust config --files bzlib.h
|
|
222
|
+
|
|
223
|
+
# 从多个头文件提取函数名
|
|
224
|
+
jarvis-c2rust config --files a.h b.hpp c.hxx
|
|
225
|
+
|
|
226
|
+
# 从函数名列表文件设置根符号列表
|
|
227
|
+
jarvis-c2rust config --files roots.txt
|
|
228
|
+
|
|
229
|
+
# 从命令行设置根符号列表
|
|
230
|
+
jarvis-c2rust config --root-list-syms "func1,func2,func3"
|
|
231
|
+
|
|
232
|
+
# 设置禁用库列表
|
|
233
|
+
jarvis-c2rust config --disabled-libs "libc,libm"
|
|
234
|
+
|
|
235
|
+
# 设置附加说明(将在所有 agent 的提示词中追加)
|
|
236
|
+
jarvis-c2rust config --additional-notes "注意:所有函数必须处理错误情况,避免 panic"
|
|
237
|
+
|
|
238
|
+
# 同时设置多个参数
|
|
239
|
+
jarvis-c2rust config --files bzlib.h --disabled-libs "libc" --additional-notes "特殊要求说明"
|
|
240
|
+
|
|
241
|
+
# 查看当前配置
|
|
242
|
+
jarvis-c2rust config --show
|
|
243
|
+
|
|
244
|
+
# 清空配置
|
|
245
|
+
jarvis-c2rust config --clear
|
|
185
246
|
"""
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
247
|
+
import json
|
|
248
|
+
from jarvis.jarvis_c2rust.constants import CONFIG_JSON, C2RUST_DIRNAME
|
|
249
|
+
|
|
250
|
+
data_dir = Path(".") / C2RUST_DIRNAME
|
|
251
|
+
config_path = data_dir / CONFIG_JSON
|
|
252
|
+
data_dir.mkdir(parents=True, exist_ok=True)
|
|
253
|
+
|
|
254
|
+
# 读取现有配置
|
|
255
|
+
default_config = {"root_symbols": [], "disabled_libraries": [], "additional_notes": ""}
|
|
256
|
+
current_config = default_config.copy()
|
|
257
|
+
|
|
258
|
+
if config_path.exists():
|
|
259
|
+
try:
|
|
260
|
+
with config_path.open("r", encoding="utf-8") as f:
|
|
261
|
+
current_config = json.load(f)
|
|
262
|
+
if not isinstance(current_config, dict):
|
|
263
|
+
current_config = default_config.copy()
|
|
264
|
+
except Exception as e:
|
|
265
|
+
typer.secho(f"[c2rust-config] 读取现有配置失败: {e},将使用默认值", fg=typer.colors.YELLOW)
|
|
266
|
+
current_config = default_config.copy()
|
|
267
|
+
|
|
268
|
+
# 如果只是查看配置
|
|
269
|
+
if show:
|
|
270
|
+
typer.secho(f"[c2rust-config] 当前配置文件: {config_path}", fg=typer.colors.BLUE)
|
|
271
|
+
typer.secho(json.dumps(current_config, ensure_ascii=False, indent=2), fg=typer.colors.CYAN)
|
|
272
|
+
return
|
|
273
|
+
|
|
274
|
+
# 如果清空配置
|
|
275
|
+
if clear:
|
|
276
|
+
current_config = default_config.copy()
|
|
277
|
+
with config_path.open("w", encoding="utf-8") as f:
|
|
278
|
+
json.dump(current_config, f, ensure_ascii=False, indent=2)
|
|
279
|
+
typer.secho(f"[c2rust-config] 配置已清空: {config_path}", fg=typer.colors.GREEN)
|
|
280
|
+
return
|
|
281
|
+
|
|
282
|
+
# 读取根符号列表(从现有配置开始,以便追加而不是替换)
|
|
283
|
+
root_symbols: List[str] = list(current_config.get("root_symbols", []))
|
|
284
|
+
header_exts = {".h", ".hh", ".hpp", ".hxx"}
|
|
285
|
+
|
|
286
|
+
if files:
|
|
287
|
+
for file_path in files:
|
|
203
288
|
try:
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
289
|
+
file_path = Path(file_path).resolve()
|
|
290
|
+
if not file_path.exists():
|
|
291
|
+
typer.secho(f"[c2rust-config] 警告: 文件不存在,跳过: {file_path}", fg=typer.colors.YELLOW)
|
|
292
|
+
continue
|
|
293
|
+
|
|
294
|
+
# 检查是否是头文件
|
|
295
|
+
if file_path.suffix.lower() in header_exts:
|
|
296
|
+
# 从头文件提取函数名
|
|
297
|
+
typer.secho(f"[c2rust-config] 从头文件提取函数名: {file_path}", fg=typer.colors.BLUE)
|
|
298
|
+
try:
|
|
299
|
+
from jarvis.jarvis_c2rust.collector import collect_function_names as _collect_fn_names
|
|
300
|
+
# 使用临时文件存储提取的函数名
|
|
301
|
+
import tempfile
|
|
302
|
+
with tempfile.NamedTemporaryFile(mode='w', suffix='.txt', delete=False, encoding='utf-8') as tmp:
|
|
303
|
+
tmp_path = Path(tmp.name)
|
|
304
|
+
_collect_fn_names(files=[file_path], out_path=tmp_path, compile_commands_root=None)
|
|
305
|
+
# 读取提取的函数名
|
|
306
|
+
txt = tmp_path.read_text(encoding="utf-8")
|
|
307
|
+
collected = [ln.strip() for ln in txt.splitlines() if ln.strip()]
|
|
308
|
+
root_symbols.extend(collected)
|
|
309
|
+
typer.secho(f"[c2rust-config] 从头文件 {file_path.name} 提取了 {len(collected)} 个函数名", fg=typer.colors.GREEN)
|
|
310
|
+
# 清理临时文件
|
|
311
|
+
try:
|
|
312
|
+
tmp_path.unlink()
|
|
313
|
+
except Exception:
|
|
314
|
+
pass
|
|
315
|
+
except Exception as e:
|
|
316
|
+
typer.secho(f"[c2rust-config] 从头文件提取函数名失败: {file_path}: {e}", fg=typer.colors.RED, err=True)
|
|
317
|
+
raise typer.Exit(code=1)
|
|
318
|
+
else:
|
|
319
|
+
# 读取函数名列表文件(每行一个函数名)
|
|
320
|
+
txt = file_path.read_text(encoding="utf-8")
|
|
321
|
+
collected = [ln.strip() for ln in txt.splitlines() if ln.strip() and not ln.strip().startswith("#")]
|
|
322
|
+
root_symbols.extend(collected)
|
|
323
|
+
typer.secho(f"[c2rust-config] 从文件 {file_path.name} 读取了 {len(collected)} 个根符号", fg=typer.colors.BLUE)
|
|
324
|
+
except typer.Exit:
|
|
325
|
+
raise
|
|
326
|
+
except Exception as e:
|
|
327
|
+
typer.secho(f"[c2rust-config] 处理文件失败: {file_path}: {e}", fg=typer.colors.RED, err=True)
|
|
208
328
|
raise typer.Exit(code=1)
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
329
|
+
|
|
330
|
+
# 标记是否处理了 root_list_syms,以便即使结果为空也更新配置
|
|
331
|
+
processed_root_list_syms = False
|
|
332
|
+
if isinstance(root_list_syms, str) and root_list_syms.strip():
|
|
333
|
+
parts = [s.strip() for s in root_list_syms.replace("\n", ",").split(",") if s.strip()]
|
|
334
|
+
root_symbols.extend(parts)
|
|
335
|
+
processed_root_list_syms = True
|
|
336
|
+
typer.secho(f"[c2rust-config] 从命令行读取根符号: {len(parts)} 个", fg=typer.colors.BLUE)
|
|
337
|
+
|
|
338
|
+
# 去重根符号列表(如果处理了 files 或 root_list_syms,或者 root_symbols 非空,则更新配置)
|
|
339
|
+
if files or processed_root_list_syms or root_symbols:
|
|
214
340
|
try:
|
|
215
|
-
|
|
341
|
+
root_symbols = list(dict.fromkeys(root_symbols))
|
|
216
342
|
except Exception:
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
disabled_list
|
|
224
|
-
if
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
try:
|
|
243
|
-
order_msg = f"\n[c2rust-lib-replace] 转译顺序: {ret['order']}" if 'order' in ret else ""
|
|
244
|
-
typer.secho(
|
|
245
|
-
f"[c2rust-lib-replace] 替代映射: {ret['mapping']}\n"
|
|
246
|
-
f"[c2rust-lib-replace] 新符号表: {ret['symbols']}"
|
|
247
|
-
+ order_msg,
|
|
248
|
-
fg=typer.colors.GREEN,
|
|
249
|
-
)
|
|
250
|
-
except Exception as _e:
|
|
251
|
-
typer.secho(f"[c2rust-lib-replace] 结果输出时发生非致命错误: {_e}", fg=typer.colors.YELLOW, err=True)
|
|
252
|
-
except Exception as e:
|
|
253
|
-
typer.secho(f"[c2rust-lib-replace] 错误: {e}", fg=typer.colors.RED, err=True)
|
|
254
|
-
raise typer.Exit(code=1)
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
@app.command("collect")
|
|
259
|
-
def collect(
|
|
260
|
-
files: List[Path] = typer.Argument(..., help="一个或多个 C/C++ 头文件路径(.h/.hh/.hpp/.hxx)"),
|
|
261
|
-
out: Path = typer.Option(..., "-o", "--out", help="输出文件路径(写入唯一函数名,每行一个)"),
|
|
262
|
-
cc_root: Optional[Path] = typer.Option(
|
|
263
|
-
None, "--cc-root", help="compile_commands.json 根目录(可选,用于提升解析准确性)"
|
|
264
|
-
),
|
|
265
|
-
interactive: bool = typer.Option(
|
|
266
|
-
False,
|
|
267
|
-
"--interactive",
|
|
268
|
-
help="启用交互模式(默认非交互模式)",
|
|
269
|
-
),
|
|
270
|
-
) -> None:
|
|
271
|
-
"""
|
|
272
|
-
收集指定头文件中的函数名(使用 libclang 解析),并写入指定输出文件(每行一个)。
|
|
273
|
-
示例:
|
|
274
|
-
jarvis-c2rust collect a.h b.hpp -o funcs.txt
|
|
275
|
-
说明:
|
|
276
|
-
非头文件会被跳过(仅支持 .h/.hh/.hpp/.hxx)。
|
|
277
|
-
"""
|
|
343
|
+
root_symbols = sorted(list(set(root_symbols)))
|
|
344
|
+
current_config["root_symbols"] = root_symbols
|
|
345
|
+
typer.secho(f"[c2rust-config] 已设置根符号列表: {len(root_symbols)} 个", fg=typer.colors.GREEN)
|
|
346
|
+
|
|
347
|
+
# 读取禁用库列表
|
|
348
|
+
if isinstance(disabled_libs, str) and disabled_libs.strip():
|
|
349
|
+
disabled_list = [s.strip() for s in disabled_libs.replace("\n", ",").split(",") if s.strip()]
|
|
350
|
+
if disabled_list:
|
|
351
|
+
current_config["disabled_libraries"] = disabled_list
|
|
352
|
+
typer.secho(f"[c2rust-config] 已设置禁用库列表: {', '.join(disabled_list)}", fg=typer.colors.GREEN)
|
|
353
|
+
|
|
354
|
+
# 读取附加说明
|
|
355
|
+
if isinstance(additional_notes, str):
|
|
356
|
+
current_config["additional_notes"] = additional_notes.strip()
|
|
357
|
+
if additional_notes.strip():
|
|
358
|
+
typer.secho(f"[c2rust-config] 已设置附加说明: {len(additional_notes.strip())} 字符", fg=typer.colors.GREEN)
|
|
359
|
+
else:
|
|
360
|
+
typer.secho("[c2rust-config] 已清空附加说明", fg=typer.colors.GREEN)
|
|
361
|
+
|
|
362
|
+
# 如果没有提供任何参数,提示用户
|
|
363
|
+
if not files and not root_list_syms and not disabled_libs and additional_notes is None:
|
|
364
|
+
typer.secho("[c2rust-config] 未提供任何参数,使用 --show 查看当前配置,或使用 --help 查看帮助", fg=typer.colors.YELLOW)
|
|
365
|
+
return
|
|
366
|
+
|
|
367
|
+
# 保存配置
|
|
278
368
|
try:
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
typer.secho(f"[c2rust-
|
|
369
|
+
with config_path.open("w", encoding="utf-8") as f:
|
|
370
|
+
json.dump(current_config, f, ensure_ascii=False, indent=2)
|
|
371
|
+
typer.secho(f"[c2rust-config] 配置已保存: {config_path}", fg=typer.colors.GREEN)
|
|
372
|
+
typer.secho(json.dumps(current_config, ensure_ascii=False, indent=2), fg=typer.colors.CYAN)
|
|
282
373
|
except Exception as e:
|
|
283
|
-
typer.secho(f"[c2rust-
|
|
374
|
+
typer.secho(f"[c2rust-config] 保存配置失败: {e}", fg=typer.colors.RED, err=True)
|
|
284
375
|
raise typer.Exit(code=1)
|
|
285
376
|
|
|
377
|
+
|
|
286
378
|
@app.command("run")
|
|
287
379
|
def run(
|
|
288
|
-
files: Optional[List[Path]] = typer.Option(
|
|
289
|
-
None,
|
|
290
|
-
"--files",
|
|
291
|
-
help="用于 collect 阶段的头文件列表(.h/.hh/.hpp/.hxx);提供则先执行 collect",
|
|
292
|
-
),
|
|
293
|
-
out: Optional[Path] = typer.Option(
|
|
294
|
-
None,
|
|
295
|
-
"-o",
|
|
296
|
-
"--out",
|
|
297
|
-
help="collect 输出函数名文件;若未提供且指定 --files 则默认为 <root>/.jarvis/c2rust/roots.txt",
|
|
298
|
-
),
|
|
299
380
|
llm_group: Optional[str] = typer.Option(
|
|
300
381
|
None,
|
|
301
382
|
"-g",
|
|
302
383
|
"--llm-group",
|
|
303
384
|
help="用于 LLM 相关阶段(lib-replace/prepare/transpile/optimize)的模型组",
|
|
304
385
|
),
|
|
305
|
-
root_list_file: Optional[Path] = typer.Option(
|
|
306
|
-
None,
|
|
307
|
-
"--root-list-file",
|
|
308
|
-
help="兼容占位:run 会使用 collect 的 --out 作为 lib-replace 的输入;当提供 --files 时本参数将被忽略;未提供 --files 时,本命令要求使用 --root-list-syms",
|
|
309
|
-
),
|
|
310
|
-
root_list_syms: Optional[str] = typer.Option(
|
|
311
|
-
None,
|
|
312
|
-
"--root-list-syms",
|
|
313
|
-
help="lib-replace 的根列表内联(逗号分隔)。未提供 --files 时该参数为必填",
|
|
314
|
-
),
|
|
315
|
-
disabled_libs: Optional[str] = typer.Option(
|
|
316
|
-
None,
|
|
317
|
-
"--disabled-libs",
|
|
318
|
-
help="lib-replace 禁用库列表(逗号分隔)",
|
|
319
|
-
),
|
|
320
386
|
max_retries: int = typer.Option(
|
|
321
387
|
0, "-m", "--max-retries", help="transpile 构建/修复与审查的最大重试次数(0 表示不限制)"
|
|
322
388
|
),
|
|
323
|
-
resume: bool = typer.Option(
|
|
324
|
-
True, "--resume/--no-resume", help="transpile 是否启用断点续跑(默认启用)"
|
|
325
|
-
),
|
|
326
389
|
interactive: bool = typer.Option(
|
|
327
390
|
False,
|
|
328
391
|
"--interactive",
|
|
329
392
|
help="启用交互模式(默认非交互模式)",
|
|
330
393
|
),
|
|
394
|
+
reset: bool = typer.Option(
|
|
395
|
+
False,
|
|
396
|
+
"--reset",
|
|
397
|
+
help="重置状态,从头开始执行所有阶段",
|
|
398
|
+
),
|
|
331
399
|
) -> None:
|
|
332
400
|
"""
|
|
333
|
-
依次执行流水线:
|
|
401
|
+
依次执行流水线:scan -> lib-replace -> prepare -> transpile -> optimize
|
|
402
|
+
|
|
403
|
+
支持断点续跑:根据状态文件(.jarvis/c2rust/run_state.json)自动跳过已完成的阶段。
|
|
334
404
|
|
|
335
405
|
约束:
|
|
336
406
|
|
|
337
|
-
-
|
|
338
|
-
|
|
407
|
+
- 根符号列表和禁用库列表从配置文件(.jarvis/c2rust/config.json)读取
|
|
408
|
+
使用 jarvis-c2rust config 命令设置这些配置(例如:jarvis-c2rust config --files bzlib.h)
|
|
339
409
|
|
|
340
|
-
-
|
|
341
|
-
|
|
342
|
-
- scan 始终执行以确保数据完整
|
|
410
|
+
- 使用 --reset 可以重置状态,从头开始执行所有阶段
|
|
343
411
|
|
|
344
412
|
- prepare/transpile 会使用 --llm-group 指定的模型组
|
|
345
413
|
|
|
346
414
|
- optimize 阶段采用默认优化配置,自动检测 crate 根目录并进行保守优化(unsafe 清理、结构优化、可见性优化、文档补充)
|
|
347
|
-
|
|
348
|
-
补充:
|
|
349
|
-
|
|
350
|
-
- 如需精细化控制,可独立调用子命令:collect、scan、lib-replace、prepare、transpile、optimize
|
|
351
415
|
"""
|
|
416
|
+
|
|
352
417
|
try:
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
418
|
+
# 加载状态文件
|
|
419
|
+
if reset:
|
|
420
|
+
# 重置状态
|
|
421
|
+
state_path = _get_run_state_path()
|
|
422
|
+
if state_path.exists():
|
|
423
|
+
state_path.unlink()
|
|
424
|
+
typer.secho("[c2rust-run] 已重置状态,将从头开始执行", fg=typer.colors.YELLOW)
|
|
425
|
+
state = _load_run_state()
|
|
426
|
+
else:
|
|
427
|
+
state = _load_run_state()
|
|
428
|
+
# 显示当前状态
|
|
429
|
+
completed_stages = [s for s, info in state.items() if info.get("completed", False)]
|
|
430
|
+
if completed_stages:
|
|
431
|
+
typer.secho(f"[c2rust-run] 检测到已完成阶段: {', '.join(completed_stages)},将从断点继续", fg=typer.colors.CYAN)
|
|
432
|
+
|
|
433
|
+
# Step 1: scan
|
|
434
|
+
if not state.get("scan", {}).get("completed", False):
|
|
435
|
+
typer.secho("[c2rust-run] scan: 开始", fg=typer.colors.BLUE)
|
|
436
|
+
_run_scan(dot=None, only_dot=False, subgraphs_dir=None, only_subgraphs=False, png=False, non_interactive=True)
|
|
437
|
+
typer.secho("[c2rust-run] scan: 完成", fg=typer.colors.GREEN)
|
|
438
|
+
# 保存状态(因为直接调用 _run_scan 函数,需要手动保存状态)
|
|
439
|
+
_save_run_state("scan", completed=True)
|
|
440
|
+
else:
|
|
441
|
+
typer.secho("[c2rust-run] scan: 已完成,跳过", fg=typer.colors.CYAN)
|
|
442
|
+
|
|
443
|
+
# Step 2: lib-replace(从配置文件读取根列表和禁用库列表)
|
|
444
|
+
if not state.get("lib_replace", {}).get("completed", False):
|
|
445
|
+
# 从配置文件读取基础配置
|
|
446
|
+
config = _load_config()
|
|
447
|
+
root_names: List[str] = list(config.get("root_symbols", []))
|
|
448
|
+
disabled_list: Optional[List[str]] = config.get("disabled_libraries", []) or None
|
|
449
|
+
|
|
450
|
+
# 去重并校验(允许为空时回退为自动根集)
|
|
451
|
+
if root_names:
|
|
452
|
+
try:
|
|
453
|
+
root_names = list(dict.fromkeys(root_names))
|
|
454
|
+
except Exception:
|
|
455
|
+
root_names = sorted(list(set(root_names)))
|
|
456
|
+
|
|
457
|
+
candidates_list: Optional[List[str]] = root_names if root_names else None
|
|
458
|
+
if not candidates_list:
|
|
459
|
+
typer.secho("[c2rust-run] lib-replace: 根列表为空,将回退为自动检测的根集合(基于扫描结果)", fg=typer.colors.YELLOW)
|
|
460
|
+
|
|
461
|
+
if disabled_list:
|
|
462
|
+
typer.secho(f"[c2rust-run] lib-replace: 从配置文件读取禁用库: {', '.join(disabled_list)}", fg=typer.colors.BLUE)
|
|
463
|
+
|
|
464
|
+
# 执行 lib-replace(默认库 std)
|
|
465
|
+
library = "std"
|
|
466
|
+
root_count_str = str(len(candidates_list)) if candidates_list is not None else "auto"
|
|
467
|
+
typer.secho(f"[c2rust-run] lib-replace: 开始(库: {library},根数: {root_count_str})", fg=typer.colors.BLUE)
|
|
468
|
+
ret = _apply_library_replacement(
|
|
469
|
+
db_path=Path("."),
|
|
470
|
+
library_name=library,
|
|
471
|
+
llm_group=llm_group,
|
|
472
|
+
candidates=candidates_list, # None 表示自动检测全部根
|
|
473
|
+
out_symbols_path=None,
|
|
474
|
+
out_mapping_path=None,
|
|
475
|
+
max_funcs=None,
|
|
476
|
+
disabled_libraries=disabled_list,
|
|
477
|
+
non_interactive=not interactive,
|
|
478
|
+
)
|
|
359
479
|
try:
|
|
360
|
-
if
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
480
|
+
order_msg = f"\n[c2rust-run] lib-replace: 转译顺序: {ret['order']}" if 'order' in ret else ""
|
|
481
|
+
typer.secho(
|
|
482
|
+
f"[c2rust-run] lib-replace: 替代映射: {ret['mapping']}\n"
|
|
483
|
+
f"[c2rust-run] lib-replace: 新符号表: {ret['symbols']}"
|
|
484
|
+
+ order_msg,
|
|
485
|
+
fg=typer.colors.GREEN,
|
|
365
486
|
)
|
|
366
|
-
_collect_fn_names(files=files, out_path=out)
|
|
367
|
-
typer.secho(f"[c2rust-run] collect: 函数名已写入: {out}", fg=typer.colors.GREEN)
|
|
368
|
-
roots_path = out
|
|
369
487
|
except Exception as _e:
|
|
370
|
-
typer.secho(f"[c2rust-run]
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
488
|
+
typer.secho(f"[c2rust-run] lib-replace: 结果输出时发生非致命错误: {_e}", fg=typer.colors.YELLOW, err=True)
|
|
489
|
+
# 保存状态(因为直接调用 _apply_library_replacement 函数,需要手动保存状态)
|
|
490
|
+
_save_run_state("lib_replace", completed=True)
|
|
491
|
+
else:
|
|
492
|
+
typer.secho("[c2rust-run] lib-replace: 已完成,跳过", fg=typer.colors.CYAN)
|
|
493
|
+
|
|
494
|
+
# Step 3: prepare
|
|
495
|
+
if not state.get("prepare", {}).get("completed", False):
|
|
496
|
+
typer.secho("[c2rust-run] prepare: 开始", fg=typer.colors.BLUE)
|
|
497
|
+
_execute_llm_plan(apply=True, llm_group=llm_group, non_interactive=not interactive)
|
|
498
|
+
typer.secho("[c2rust-run] prepare: 完成", fg=typer.colors.GREEN)
|
|
499
|
+
# 保存状态(因为直接调用 _execute_llm_plan 函数,需要手动保存状态)
|
|
500
|
+
_save_run_state("prepare", completed=True)
|
|
501
|
+
else:
|
|
502
|
+
typer.secho("[c2rust-run] prepare: 已完成,跳过", fg=typer.colors.CYAN)
|
|
503
|
+
|
|
504
|
+
# Step 4: transpile
|
|
505
|
+
if not state.get("transpile", {}).get("completed", False):
|
|
506
|
+
typer.secho("[c2rust-run] transpile: 开始", fg=typer.colors.BLUE)
|
|
507
|
+
from jarvis.jarvis_c2rust.transpiler import run_transpile as _run_transpile
|
|
508
|
+
# 从配置文件读取配置(transpile 内部会自动读取)
|
|
509
|
+
_run_transpile(
|
|
510
|
+
project_root=Path("."),
|
|
511
|
+
crate_dir=None,
|
|
512
|
+
llm_group=llm_group,
|
|
513
|
+
max_retries=max_retries,
|
|
514
|
+
disabled_libraries=None, # 从配置文件恢复
|
|
515
|
+
root_symbols=None, # 从配置文件恢复
|
|
516
|
+
non_interactive=not interactive,
|
|
517
|
+
)
|
|
518
|
+
typer.secho("[c2rust-run] transpile: 完成", fg=typer.colors.GREEN)
|
|
519
|
+
# 保存状态(因为直接调用 _run_transpile 函数,需要手动保存状态)
|
|
520
|
+
_save_run_state("transpile", completed=True)
|
|
521
|
+
else:
|
|
522
|
+
typer.secho("[c2rust-run] transpile: 已完成,跳过", fg=typer.colors.CYAN)
|
|
380
523
|
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
if not roots_path or not roots_path.exists():
|
|
384
|
-
typer.secho("[c2rust-run] lib-replace: 未找到 collect 输出文件,无法继续", fg=typer.colors.RED, err=True)
|
|
385
|
-
raise typer.Exit(code=2)
|
|
524
|
+
# Step 5: optimize
|
|
525
|
+
if not state.get("optimize", {}).get("completed", False):
|
|
386
526
|
try:
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
527
|
+
typer.secho("[c2rust-run] optimize: 开始", fg=typer.colors.BLUE)
|
|
528
|
+
from jarvis.jarvis_c2rust.optimizer import optimize_project as _optimize_project
|
|
529
|
+
from jarvis.jarvis_c2rust.utils import default_crate_dir
|
|
530
|
+
# 使用与 transpile 相同的逻辑确定项目根目录和 crate 目录
|
|
531
|
+
project_root = Path(".")
|
|
532
|
+
crate_dir = default_crate_dir(project_root)
|
|
533
|
+
typer.secho(f"[c2rust-run] optimize: 使用项目根目录: {project_root}, crate 目录: {crate_dir}", fg=typer.colors.CYAN)
|
|
534
|
+
res = _optimize_project(project_root=project_root, crate_dir=crate_dir, llm_group=llm_group, non_interactive=not interactive)
|
|
535
|
+
summary = (
|
|
536
|
+
f"[c2rust-run] optimize: 结果摘要:\n"
|
|
537
|
+
f" files_scanned: {res.get('files_scanned')}\n"
|
|
538
|
+
f" unsafe_removed: {res.get('unsafe_removed')}\n"
|
|
539
|
+
f" unsafe_annotated: {res.get('unsafe_annotated')}\n"
|
|
540
|
+
f" visibility_downgraded: {res.get('visibility_downgraded')}\n"
|
|
541
|
+
f" docs_added: {res.get('docs_added')}\n"
|
|
542
|
+
f" cargo_checks: {res.get('cargo_checks')}\n"
|
|
543
|
+
)
|
|
544
|
+
typer.secho(summary, fg=typer.colors.GREEN)
|
|
545
|
+
|
|
546
|
+
# 检查优化是否真正完成(所有步骤都完成,包括 clippy 告警修复)
|
|
547
|
+
optimize_truly_completed = _check_optimize_completed(crate_dir)
|
|
548
|
+
if optimize_truly_completed:
|
|
549
|
+
typer.secho("[c2rust-run] optimize: 完成", fg=typer.colors.GREEN)
|
|
550
|
+
# 保存状态(因为直接调用 _optimize_project 函数,需要手动保存状态)
|
|
551
|
+
_save_run_state("optimize", completed=True)
|
|
552
|
+
else:
|
|
553
|
+
typer.secho("[c2rust-run] optimize: 部分步骤未完成(如 clippy 告警未完全修复),下次将继续", fg=typer.colors.YELLOW)
|
|
554
|
+
# 不保存状态,下次恢复时会继续执行优化
|
|
390
555
|
except Exception as _e:
|
|
391
|
-
typer.secho(f"[c2rust-run]
|
|
556
|
+
typer.secho(f"[c2rust-run] optimize: 错误: {_e}", fg=typer.colors.RED, err=True)
|
|
392
557
|
raise
|
|
393
|
-
# 兼容参数提示
|
|
394
|
-
if root_list_file is not None:
|
|
395
|
-
typer.secho("[c2rust-run] 提示: --root-list-file 已被忽略(run 会固定使用 collect 的 --out 作为输入)", fg=typer.colors.YELLOW)
|
|
396
|
-
else:
|
|
397
|
-
# 约束:未传递 files 必须提供 --root-list-syms
|
|
398
|
-
if not (isinstance(root_list_syms, str) and root_list_syms.strip()):
|
|
399
|
-
typer.secho("[c2rust-run] 错误:未提供 --files 时,必须通过 --root-list-syms 指定根列表(逗号分隔)", fg=typer.colors.RED, err=True)
|
|
400
|
-
raise typer.Exit(code=2)
|
|
401
|
-
parts = [s.strip() for s in root_list_syms.replace("\n", ",").split(",") if s.strip()]
|
|
402
|
-
root_names.extend(parts)
|
|
403
|
-
|
|
404
|
-
# 去重并校验(允许为空时回退为自动根集)
|
|
405
|
-
try:
|
|
406
|
-
root_names = list(dict.fromkeys(root_names))
|
|
407
|
-
except Exception:
|
|
408
|
-
root_names = sorted(list(set(root_names)))
|
|
409
|
-
candidates_list: Optional[List[str]] = None
|
|
410
|
-
if root_names:
|
|
411
|
-
candidates_list = root_names
|
|
412
558
|
else:
|
|
413
|
-
typer.secho("[c2rust-run]
|
|
414
|
-
|
|
415
|
-
#
|
|
416
|
-
|
|
417
|
-
if isinstance(disabled_libs, str) and disabled_libs.strip():
|
|
418
|
-
disabled_list = [s.strip() for s in disabled_libs.replace("\n", ",").split(",") if s.strip()]
|
|
419
|
-
if disabled_list:
|
|
420
|
-
typer.secho(f"[c2rust-run] lib-replace: 禁用库: {', '.join(disabled_list)}", fg=typer.colors.YELLOW)
|
|
421
|
-
|
|
422
|
-
# 执行 lib-replace(默认库 std)
|
|
423
|
-
library = "std"
|
|
424
|
-
root_count_str = str(len(candidates_list)) if candidates_list is not None else "auto"
|
|
425
|
-
typer.secho(f"[c2rust-run] lib-replace: 开始(库: {library},根数: {root_count_str})", fg=typer.colors.BLUE)
|
|
426
|
-
ret = _apply_library_replacement(
|
|
427
|
-
db_path=Path("."),
|
|
428
|
-
library_name=library,
|
|
429
|
-
llm_group=llm_group,
|
|
430
|
-
candidates=candidates_list, # None 表示自动检测全部根
|
|
431
|
-
out_symbols_path=None,
|
|
432
|
-
out_mapping_path=None,
|
|
433
|
-
max_funcs=None,
|
|
434
|
-
disabled_libraries=disabled_list,
|
|
435
|
-
non_interactive=not interactive,
|
|
436
|
-
)
|
|
437
|
-
try:
|
|
438
|
-
order_msg = f"\n[c2rust-run] lib-replace: 转译顺序: {ret['order']}" if 'order' in ret else ""
|
|
439
|
-
typer.secho(
|
|
440
|
-
f"[c2rust-run] lib-replace: 替代映射: {ret['mapping']}\n"
|
|
441
|
-
f"[c2rust-run] lib-replace: 新符号表: {ret['symbols']}"
|
|
442
|
-
+ order_msg,
|
|
443
|
-
fg=typer.colors.GREEN,
|
|
444
|
-
)
|
|
445
|
-
except Exception as _e:
|
|
446
|
-
typer.secho(f"[c2rust-run] lib-replace: 结果输出时发生非致命错误: {_e}", fg=typer.colors.YELLOW, err=True)
|
|
447
|
-
|
|
448
|
-
# Step 4: prepare
|
|
449
|
-
typer.secho("[c2rust-run] prepare: 开始", fg=typer.colors.BLUE)
|
|
450
|
-
_execute_llm_plan(apply=True, llm_group=llm_group, non_interactive=not interactive)
|
|
451
|
-
typer.secho("[c2rust-run] prepare: 完成", fg=typer.colors.GREEN)
|
|
452
|
-
|
|
453
|
-
# Step 5: transpile
|
|
454
|
-
typer.secho("[c2rust-run] transpile: 开始", fg=typer.colors.BLUE)
|
|
455
|
-
from jarvis.jarvis_c2rust.transpiler import run_transpile as _run_transpile
|
|
456
|
-
_run_transpile(
|
|
457
|
-
project_root=Path("."),
|
|
458
|
-
crate_dir=None,
|
|
459
|
-
llm_group=llm_group,
|
|
460
|
-
max_retries=max_retries,
|
|
461
|
-
resume=resume,
|
|
462
|
-
only=None,
|
|
463
|
-
non_interactive=not interactive,
|
|
464
|
-
)
|
|
465
|
-
typer.secho("[c2rust-run] transpile: 完成", fg=typer.colors.GREEN)
|
|
466
|
-
|
|
467
|
-
# Step 6: optimize
|
|
468
|
-
try:
|
|
469
|
-
typer.secho("[c2rust-run] optimize: 开始", fg=typer.colors.BLUE)
|
|
470
|
-
from jarvis.jarvis_c2rust.optimizer import optimize_project as _optimize_project
|
|
471
|
-
res = _optimize_project(crate_dir=None, llm_group=llm_group, non_interactive=not interactive)
|
|
472
|
-
summary = (
|
|
473
|
-
f"[c2rust-run] optimize: 结果摘要:\n"
|
|
474
|
-
f" files_scanned: {res.get('files_scanned')}\n"
|
|
475
|
-
f" unsafe_removed: {res.get('unsafe_removed')}\n"
|
|
476
|
-
f" unsafe_annotated: {res.get('unsafe_annotated')}\n"
|
|
477
|
-
f" duplicates_tagged: {res.get('duplicates_tagged')}\n"
|
|
478
|
-
f" visibility_downgraded: {res.get('visibility_downgraded')}\n"
|
|
479
|
-
f" docs_added: {res.get('docs_added')}\n"
|
|
480
|
-
f" cargo_checks: {res.get('cargo_checks')}\n"
|
|
481
|
-
)
|
|
482
|
-
typer.secho(summary, fg=typer.colors.GREEN)
|
|
483
|
-
typer.secho("[c2rust-run] optimize: 完成", fg=typer.colors.GREEN)
|
|
484
|
-
except Exception as _e:
|
|
485
|
-
typer.secho(f"[c2rust-run] optimize: 错误: {_e}", fg=typer.colors.RED, err=True)
|
|
486
|
-
raise
|
|
559
|
+
typer.secho("[c2rust-run] optimize: 已完成,跳过", fg=typer.colors.CYAN)
|
|
560
|
+
|
|
561
|
+
# 所有阶段完成
|
|
562
|
+
typer.secho("[c2rust-run] 所有阶段已完成!", fg=typer.colors.GREEN)
|
|
487
563
|
except Exception as e:
|
|
488
564
|
typer.secho(f"[c2rust-run] 错误: {e}", fg=typer.colors.RED, err=True)
|
|
489
565
|
raise typer.Exit(code=1)
|
|
490
566
|
|
|
491
567
|
|
|
492
|
-
@app.command("optimize")
|
|
493
|
-
def optimize(
|
|
494
|
-
crate_dir: Optional[Path] = typer.Option(
|
|
495
|
-
None, "--crate-dir", help="Rust crate 根目录(包含 Cargo.toml);未提供时自动检测"
|
|
496
|
-
),
|
|
497
|
-
llm_group: Optional[str] = typer.Option(
|
|
498
|
-
None,
|
|
499
|
-
"-g",
|
|
500
|
-
"--llm-group",
|
|
501
|
-
help="用于 CodeAgent 修复与整体优化的 LLM 模型组",
|
|
502
|
-
),
|
|
503
|
-
enable_unsafe_cleanup: bool = typer.Option(
|
|
504
|
-
True, "--unsafe/--no-unsafe", help="启用 unsafe 清理"
|
|
505
|
-
),
|
|
506
|
-
enable_structure_opt: bool = typer.Option(
|
|
507
|
-
True, "--structure/--no-structure", help="启用代码结构优化(重复代码标注)"
|
|
508
|
-
),
|
|
509
|
-
enable_visibility_opt: bool = typer.Option(
|
|
510
|
-
True, "--visibility/--no-visibility", help="启用可见性优化(尽可能最小可见性)"
|
|
511
|
-
),
|
|
512
|
-
enable_doc_opt: bool = typer.Option(
|
|
513
|
-
True, "--doc/--no-doc", help="启用文档补充(模块/函数占位文档)"
|
|
514
|
-
),
|
|
515
|
-
max_checks: int = typer.Option(
|
|
516
|
-
0, "-m", "--max-checks", help="cargo check 最大次数限制(0 表示不限)"
|
|
517
|
-
),
|
|
518
|
-
dry_run: bool = typer.Option(
|
|
519
|
-
False, "--dry-run", help="仅统计潜在修改,不写回文件"
|
|
520
|
-
),
|
|
521
|
-
include_patterns: Optional[str] = typer.Option(
|
|
522
|
-
None,
|
|
523
|
-
"--include",
|
|
524
|
-
help="仅处理匹配的文件(逗号分隔 glob,相对 crate 根,如: src/**/*.rs,src/foo/*.rs)",
|
|
525
|
-
),
|
|
526
|
-
exclude_patterns: Optional[str] = typer.Option(
|
|
527
|
-
None,
|
|
528
|
-
"--exclude",
|
|
529
|
-
help="排除匹配的文件(逗号分隔 glob,相对 crate 根)",
|
|
530
|
-
),
|
|
531
|
-
max_files: int = typer.Option(
|
|
532
|
-
0,
|
|
533
|
-
"-n",
|
|
534
|
-
"--max-files",
|
|
535
|
-
help="本次最多处理的文件数(0 表示不限,用于大项目分批优化)",
|
|
536
|
-
),
|
|
537
|
-
resume: bool = typer.Option(
|
|
538
|
-
True,
|
|
539
|
-
"--resume/--no-resume",
|
|
540
|
-
help="是否启用断点续跑,跳过已处理文件(默认启用)",
|
|
541
|
-
),
|
|
542
|
-
reset_progress: bool = typer.Option(
|
|
543
|
-
False,
|
|
544
|
-
"--reset-progress",
|
|
545
|
-
help="重置优化进度(清空已处理文件记录)后再执行",
|
|
546
|
-
),
|
|
547
|
-
build_fix_retries: int = typer.Option(
|
|
548
|
-
3,
|
|
549
|
-
"-r",
|
|
550
|
-
"--build-fix-retries",
|
|
551
|
-
help="构建失败后的最小修复重试次数(使用 CodeAgent 迭代修复)",
|
|
552
|
-
),
|
|
553
|
-
git_guard: bool = typer.Option(
|
|
554
|
-
True,
|
|
555
|
-
"--git-guard/--no-git-guard",
|
|
556
|
-
help="启用Git保护:每一步优化前记录当前HEAD,修复耗尽时自动git reset --hard回快照(默认启用)",
|
|
557
|
-
),
|
|
558
|
-
interactive: bool = typer.Option(
|
|
559
|
-
False,
|
|
560
|
-
"--interactive",
|
|
561
|
-
help="启用交互模式(默认非交互模式)",
|
|
562
|
-
),
|
|
563
|
-
|
|
564
|
-
) -> None:
|
|
565
|
-
"""
|
|
566
|
-
对生成的 Rust 代码执行保守优化(unsafe 清理、结构优化、可见性优化、文档补充)。
|
|
567
|
-
建议在转译完成后执行:jarvis-c2rust optimize
|
|
568
|
-
"""
|
|
569
|
-
try:
|
|
570
|
-
from jarvis.jarvis_c2rust.optimizer import optimize_project as _optimize_project
|
|
571
|
-
|
|
572
|
-
typer.secho("[c2rust-optimize] 开始", fg=typer.colors.BLUE)
|
|
573
|
-
res = _optimize_project(
|
|
574
|
-
crate_dir=crate_dir,
|
|
575
|
-
enable_unsafe_cleanup=enable_unsafe_cleanup,
|
|
576
|
-
enable_structure_opt=enable_structure_opt,
|
|
577
|
-
enable_visibility_opt=enable_visibility_opt,
|
|
578
|
-
enable_doc_opt=enable_doc_opt,
|
|
579
|
-
max_checks=max_checks,
|
|
580
|
-
dry_run=dry_run,
|
|
581
|
-
include_patterns=include_patterns,
|
|
582
|
-
exclude_patterns=exclude_patterns,
|
|
583
|
-
max_files=max_files,
|
|
584
|
-
resume=resume,
|
|
585
|
-
reset_progress=reset_progress,
|
|
586
|
-
build_fix_retries=build_fix_retries,
|
|
587
|
-
git_guard=git_guard,
|
|
588
|
-
llm_group=llm_group,
|
|
589
|
-
non_interactive=not interactive,
|
|
590
|
-
)
|
|
591
|
-
# 摘要输出
|
|
592
|
-
summary = (
|
|
593
|
-
f"[c2rust-optimize] 结果摘要:\n"
|
|
594
|
-
f" files_scanned: {res.get('files_scanned')}\n"
|
|
595
|
-
f" unsafe_removed: {res.get('unsafe_removed')}\n"
|
|
596
|
-
f" unsafe_annotated: {res.get('unsafe_annotated')}\n"
|
|
597
|
-
f" duplicates_tagged: {res.get('duplicates_tagged')}\n"
|
|
598
|
-
f" visibility_downgraded: {res.get('visibility_downgraded')}\n"
|
|
599
|
-
f" docs_added: {res.get('docs_added')}\n"
|
|
600
|
-
f" cargo_checks: {res.get('cargo_checks')}\n"
|
|
601
|
-
)
|
|
602
|
-
typer.secho(summary, fg=typer.colors.GREEN)
|
|
603
|
-
except Exception as e:
|
|
604
|
-
typer.secho(f"[c2rust-optimize] 错误: {e}", fg=typer.colors.RED, err=True)
|
|
605
|
-
raise typer.Exit(code=1)
|
|
606
568
|
|
|
607
569
|
def main() -> None:
|
|
608
570
|
"""主入口"""
|