jarvis-ai-assistant 0.7.16__py3-none-any.whl → 1.0.2__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 +567 -222
- jarvis/jarvis_agent/agent_manager.py +19 -12
- jarvis/jarvis_agent/builtin_input_handler.py +79 -11
- jarvis/jarvis_agent/config_editor.py +7 -2
- jarvis/jarvis_agent/event_bus.py +24 -13
- jarvis/jarvis_agent/events.py +19 -1
- jarvis/jarvis_agent/file_context_handler.py +67 -64
- jarvis/jarvis_agent/file_methodology_manager.py +38 -24
- jarvis/jarvis_agent/jarvis.py +186 -114
- jarvis/jarvis_agent/language_extractors/__init__.py +8 -1
- jarvis/jarvis_agent/language_extractors/c_extractor.py +7 -4
- jarvis/jarvis_agent/language_extractors/cpp_extractor.py +9 -4
- jarvis/jarvis_agent/language_extractors/go_extractor.py +7 -4
- jarvis/jarvis_agent/language_extractors/java_extractor.py +27 -20
- jarvis/jarvis_agent/language_extractors/javascript_extractor.py +22 -17
- jarvis/jarvis_agent/language_extractors/python_extractor.py +7 -4
- jarvis/jarvis_agent/language_extractors/rust_extractor.py +7 -4
- jarvis/jarvis_agent/language_extractors/typescript_extractor.py +22 -17
- jarvis/jarvis_agent/language_support_info.py +250 -219
- jarvis/jarvis_agent/main.py +19 -23
- jarvis/jarvis_agent/memory_manager.py +9 -6
- jarvis/jarvis_agent/methodology_share_manager.py +21 -15
- jarvis/jarvis_agent/output_handler.py +4 -2
- jarvis/jarvis_agent/prompt_builder.py +7 -6
- jarvis/jarvis_agent/prompt_manager.py +113 -8
- jarvis/jarvis_agent/prompts.py +317 -85
- jarvis/jarvis_agent/protocols.py +5 -2
- jarvis/jarvis_agent/run_loop.py +192 -32
- jarvis/jarvis_agent/session_manager.py +7 -3
- jarvis/jarvis_agent/share_manager.py +23 -13
- jarvis/jarvis_agent/shell_input_handler.py +12 -8
- jarvis/jarvis_agent/stdio_redirect.py +25 -26
- jarvis/jarvis_agent/task_analyzer.py +29 -23
- jarvis/jarvis_agent/task_list.py +869 -0
- jarvis/jarvis_agent/task_manager.py +26 -23
- jarvis/jarvis_agent/tool_executor.py +6 -5
- jarvis/jarvis_agent/tool_share_manager.py +24 -14
- jarvis/jarvis_agent/user_interaction.py +3 -3
- jarvis/jarvis_agent/utils.py +9 -1
- jarvis/jarvis_agent/web_bridge.py +37 -17
- jarvis/jarvis_agent/web_output_sink.py +5 -2
- jarvis/jarvis_agent/web_server.py +165 -36
- jarvis/jarvis_c2rust/__init__.py +1 -1
- jarvis/jarvis_c2rust/cli.py +260 -141
- jarvis/jarvis_c2rust/collector.py +37 -18
- jarvis/jarvis_c2rust/constants.py +60 -0
- jarvis/jarvis_c2rust/library_replacer.py +242 -1010
- jarvis/jarvis_c2rust/library_replacer_checkpoint.py +133 -0
- jarvis/jarvis_c2rust/library_replacer_llm.py +287 -0
- jarvis/jarvis_c2rust/library_replacer_loader.py +191 -0
- jarvis/jarvis_c2rust/library_replacer_output.py +134 -0
- jarvis/jarvis_c2rust/library_replacer_prompts.py +124 -0
- jarvis/jarvis_c2rust/library_replacer_utils.py +188 -0
- jarvis/jarvis_c2rust/llm_module_agent.py +98 -1044
- jarvis/jarvis_c2rust/llm_module_agent_apply.py +170 -0
- jarvis/jarvis_c2rust/llm_module_agent_executor.py +288 -0
- jarvis/jarvis_c2rust/llm_module_agent_loader.py +170 -0
- jarvis/jarvis_c2rust/llm_module_agent_prompts.py +268 -0
- jarvis/jarvis_c2rust/llm_module_agent_types.py +57 -0
- jarvis/jarvis_c2rust/llm_module_agent_utils.py +150 -0
- jarvis/jarvis_c2rust/llm_module_agent_validator.py +119 -0
- jarvis/jarvis_c2rust/loaders.py +28 -10
- jarvis/jarvis_c2rust/models.py +5 -2
- jarvis/jarvis_c2rust/optimizer.py +192 -1974
- jarvis/jarvis_c2rust/optimizer_build_fix.py +286 -0
- jarvis/jarvis_c2rust/optimizer_clippy.py +766 -0
- jarvis/jarvis_c2rust/optimizer_config.py +49 -0
- jarvis/jarvis_c2rust/optimizer_docs.py +183 -0
- jarvis/jarvis_c2rust/optimizer_options.py +48 -0
- jarvis/jarvis_c2rust/optimizer_progress.py +469 -0
- jarvis/jarvis_c2rust/optimizer_report.py +52 -0
- jarvis/jarvis_c2rust/optimizer_unsafe.py +309 -0
- jarvis/jarvis_c2rust/optimizer_utils.py +469 -0
- jarvis/jarvis_c2rust/optimizer_visibility.py +185 -0
- jarvis/jarvis_c2rust/scanner.py +229 -166
- jarvis/jarvis_c2rust/transpiler.py +531 -2732
- jarvis/jarvis_c2rust/transpiler_agents.py +503 -0
- jarvis/jarvis_c2rust/transpiler_build.py +1294 -0
- jarvis/jarvis_c2rust/transpiler_codegen.py +204 -0
- jarvis/jarvis_c2rust/transpiler_compile.py +146 -0
- jarvis/jarvis_c2rust/transpiler_config.py +178 -0
- jarvis/jarvis_c2rust/transpiler_context.py +122 -0
- jarvis/jarvis_c2rust/transpiler_executor.py +516 -0
- jarvis/jarvis_c2rust/transpiler_generation.py +278 -0
- jarvis/jarvis_c2rust/transpiler_git.py +163 -0
- jarvis/jarvis_c2rust/transpiler_mod_utils.py +225 -0
- jarvis/jarvis_c2rust/transpiler_modules.py +336 -0
- jarvis/jarvis_c2rust/transpiler_planning.py +394 -0
- jarvis/jarvis_c2rust/transpiler_review.py +1196 -0
- jarvis/jarvis_c2rust/transpiler_symbols.py +176 -0
- jarvis/jarvis_c2rust/utils.py +269 -79
- jarvis/jarvis_code_agent/after_change.py +233 -0
- jarvis/jarvis_code_agent/build_validation_config.py +37 -30
- jarvis/jarvis_code_agent/builtin_rules.py +68 -0
- jarvis/jarvis_code_agent/code_agent.py +976 -1517
- jarvis/jarvis_code_agent/code_agent_build.py +227 -0
- jarvis/jarvis_code_agent/code_agent_diff.py +246 -0
- jarvis/jarvis_code_agent/code_agent_git.py +525 -0
- jarvis/jarvis_code_agent/code_agent_impact.py +177 -0
- jarvis/jarvis_code_agent/code_agent_lint.py +283 -0
- jarvis/jarvis_code_agent/code_agent_llm.py +159 -0
- jarvis/jarvis_code_agent/code_agent_postprocess.py +105 -0
- jarvis/jarvis_code_agent/code_agent_prompts.py +46 -0
- jarvis/jarvis_code_agent/code_agent_rules.py +305 -0
- jarvis/jarvis_code_agent/code_analyzer/__init__.py +52 -48
- jarvis/jarvis_code_agent/code_analyzer/base_language.py +12 -10
- jarvis/jarvis_code_agent/code_analyzer/build_validator/__init__.py +12 -11
- jarvis/jarvis_code_agent/code_analyzer/build_validator/base.py +16 -12
- jarvis/jarvis_code_agent/code_analyzer/build_validator/cmake.py +26 -17
- jarvis/jarvis_code_agent/code_analyzer/build_validator/detector.py +558 -104
- jarvis/jarvis_code_agent/code_analyzer/build_validator/fallback.py +27 -16
- jarvis/jarvis_code_agent/code_analyzer/build_validator/go.py +22 -18
- jarvis/jarvis_code_agent/code_analyzer/build_validator/java_gradle.py +21 -16
- jarvis/jarvis_code_agent/code_analyzer/build_validator/java_maven.py +20 -16
- jarvis/jarvis_code_agent/code_analyzer/build_validator/makefile.py +27 -16
- jarvis/jarvis_code_agent/code_analyzer/build_validator/nodejs.py +47 -23
- jarvis/jarvis_code_agent/code_analyzer/build_validator/python.py +71 -37
- jarvis/jarvis_code_agent/code_analyzer/build_validator/rust.py +162 -35
- jarvis/jarvis_code_agent/code_analyzer/build_validator/validator.py +111 -57
- jarvis/jarvis_code_agent/code_analyzer/build_validator.py +18 -12
- jarvis/jarvis_code_agent/code_analyzer/context_manager.py +185 -183
- jarvis/jarvis_code_agent/code_analyzer/context_recommender.py +2 -1
- jarvis/jarvis_code_agent/code_analyzer/dependency_analyzer.py +24 -15
- jarvis/jarvis_code_agent/code_analyzer/file_ignore.py +227 -141
- jarvis/jarvis_code_agent/code_analyzer/impact_analyzer.py +321 -247
- jarvis/jarvis_code_agent/code_analyzer/language_registry.py +37 -29
- jarvis/jarvis_code_agent/code_analyzer/language_support.py +21 -13
- jarvis/jarvis_code_agent/code_analyzer/languages/__init__.py +15 -9
- jarvis/jarvis_code_agent/code_analyzer/languages/c_cpp_language.py +75 -45
- jarvis/jarvis_code_agent/code_analyzer/languages/go_language.py +87 -52
- jarvis/jarvis_code_agent/code_analyzer/languages/java_language.py +84 -51
- jarvis/jarvis_code_agent/code_analyzer/languages/javascript_language.py +94 -64
- jarvis/jarvis_code_agent/code_analyzer/languages/python_language.py +109 -71
- jarvis/jarvis_code_agent/code_analyzer/languages/rust_language.py +97 -63
- jarvis/jarvis_code_agent/code_analyzer/languages/typescript_language.py +103 -69
- jarvis/jarvis_code_agent/code_analyzer/llm_context_recommender.py +271 -268
- jarvis/jarvis_code_agent/code_analyzer/symbol_extractor.py +76 -64
- jarvis/jarvis_code_agent/code_analyzer/tree_sitter_extractor.py +92 -19
- jarvis/jarvis_code_agent/diff_visualizer.py +998 -0
- jarvis/jarvis_code_agent/lint.py +223 -524
- jarvis/jarvis_code_agent/rule_share_manager.py +158 -0
- jarvis/jarvis_code_agent/rules/clean_code.md +144 -0
- jarvis/jarvis_code_agent/rules/code_review.md +115 -0
- jarvis/jarvis_code_agent/rules/documentation.md +165 -0
- jarvis/jarvis_code_agent/rules/generate_rules.md +52 -0
- jarvis/jarvis_code_agent/rules/performance.md +158 -0
- jarvis/jarvis_code_agent/rules/refactoring.md +139 -0
- jarvis/jarvis_code_agent/rules/security.md +160 -0
- jarvis/jarvis_code_agent/rules/tdd.md +78 -0
- jarvis/jarvis_code_agent/test_rules/cpp_test.md +118 -0
- jarvis/jarvis_code_agent/test_rules/go_test.md +98 -0
- jarvis/jarvis_code_agent/test_rules/java_test.md +99 -0
- jarvis/jarvis_code_agent/test_rules/javascript_test.md +113 -0
- jarvis/jarvis_code_agent/test_rules/php_test.md +117 -0
- jarvis/jarvis_code_agent/test_rules/python_test.md +91 -0
- jarvis/jarvis_code_agent/test_rules/ruby_test.md +102 -0
- jarvis/jarvis_code_agent/test_rules/rust_test.md +86 -0
- jarvis/jarvis_code_agent/utils.py +36 -26
- jarvis/jarvis_code_analysis/checklists/loader.py +21 -21
- jarvis/jarvis_code_analysis/code_review.py +64 -33
- jarvis/jarvis_data/config_schema.json +285 -192
- jarvis/jarvis_git_squash/main.py +8 -6
- jarvis/jarvis_git_utils/git_commiter.py +53 -76
- jarvis/jarvis_mcp/__init__.py +5 -2
- jarvis/jarvis_mcp/sse_mcp_client.py +40 -30
- jarvis/jarvis_mcp/stdio_mcp_client.py +27 -19
- jarvis/jarvis_mcp/streamable_mcp_client.py +35 -26
- jarvis/jarvis_memory_organizer/memory_organizer.py +78 -55
- jarvis/jarvis_methodology/main.py +48 -39
- jarvis/jarvis_multi_agent/__init__.py +56 -23
- jarvis/jarvis_multi_agent/main.py +15 -18
- jarvis/jarvis_platform/base.py +179 -111
- jarvis/jarvis_platform/human.py +27 -16
- jarvis/jarvis_platform/kimi.py +52 -45
- jarvis/jarvis_platform/openai.py +101 -40
- jarvis/jarvis_platform/registry.py +51 -33
- jarvis/jarvis_platform/tongyi.py +68 -38
- jarvis/jarvis_platform/yuanbao.py +59 -43
- jarvis/jarvis_platform_manager/main.py +68 -76
- jarvis/jarvis_platform_manager/service.py +24 -14
- jarvis/jarvis_rag/README_CONFIG.md +314 -0
- jarvis/jarvis_rag/README_DYNAMIC_LOADING.md +311 -0
- jarvis/jarvis_rag/README_ONLINE_MODELS.md +230 -0
- jarvis/jarvis_rag/__init__.py +57 -4
- jarvis/jarvis_rag/cache.py +3 -1
- jarvis/jarvis_rag/cli.py +48 -68
- jarvis/jarvis_rag/embedding_interface.py +39 -0
- jarvis/jarvis_rag/embedding_manager.py +7 -230
- jarvis/jarvis_rag/embeddings/__init__.py +41 -0
- jarvis/jarvis_rag/embeddings/base.py +114 -0
- jarvis/jarvis_rag/embeddings/cohere.py +66 -0
- jarvis/jarvis_rag/embeddings/edgefn.py +117 -0
- jarvis/jarvis_rag/embeddings/local.py +260 -0
- jarvis/jarvis_rag/embeddings/openai.py +62 -0
- jarvis/jarvis_rag/embeddings/registry.py +293 -0
- jarvis/jarvis_rag/llm_interface.py +8 -6
- jarvis/jarvis_rag/query_rewriter.py +8 -9
- jarvis/jarvis_rag/rag_pipeline.py +61 -52
- jarvis/jarvis_rag/reranker.py +7 -75
- jarvis/jarvis_rag/reranker_interface.py +32 -0
- jarvis/jarvis_rag/rerankers/__init__.py +41 -0
- jarvis/jarvis_rag/rerankers/base.py +109 -0
- jarvis/jarvis_rag/rerankers/cohere.py +67 -0
- jarvis/jarvis_rag/rerankers/edgefn.py +140 -0
- jarvis/jarvis_rag/rerankers/jina.py +79 -0
- jarvis/jarvis_rag/rerankers/local.py +89 -0
- jarvis/jarvis_rag/rerankers/registry.py +293 -0
- jarvis/jarvis_rag/retriever.py +58 -43
- jarvis/jarvis_sec/__init__.py +66 -141
- jarvis/jarvis_sec/agents.py +21 -17
- jarvis/jarvis_sec/analysis.py +80 -33
- jarvis/jarvis_sec/checkers/__init__.py +7 -13
- jarvis/jarvis_sec/checkers/c_checker.py +356 -164
- jarvis/jarvis_sec/checkers/rust_checker.py +47 -29
- jarvis/jarvis_sec/cli.py +43 -21
- jarvis/jarvis_sec/clustering.py +430 -272
- jarvis/jarvis_sec/file_manager.py +99 -55
- jarvis/jarvis_sec/parsers.py +9 -6
- jarvis/jarvis_sec/prompts.py +4 -3
- jarvis/jarvis_sec/report.py +44 -22
- jarvis/jarvis_sec/review.py +180 -107
- jarvis/jarvis_sec/status.py +50 -41
- jarvis/jarvis_sec/types.py +3 -0
- jarvis/jarvis_sec/utils.py +160 -83
- jarvis/jarvis_sec/verification.py +411 -181
- jarvis/jarvis_sec/workflow.py +132 -21
- jarvis/jarvis_smart_shell/main.py +28 -41
- jarvis/jarvis_stats/cli.py +14 -12
- jarvis/jarvis_stats/stats.py +28 -19
- jarvis/jarvis_stats/storage.py +14 -8
- jarvis/jarvis_stats/visualizer.py +12 -7
- jarvis/jarvis_tools/base.py +5 -2
- jarvis/jarvis_tools/clear_memory.py +13 -9
- jarvis/jarvis_tools/cli/main.py +23 -18
- jarvis/jarvis_tools/edit_file.py +572 -873
- jarvis/jarvis_tools/execute_script.py +10 -7
- jarvis/jarvis_tools/file_analyzer.py +7 -8
- jarvis/jarvis_tools/meta_agent.py +287 -0
- jarvis/jarvis_tools/methodology.py +5 -3
- jarvis/jarvis_tools/read_code.py +305 -1438
- jarvis/jarvis_tools/read_symbols.py +50 -17
- jarvis/jarvis_tools/read_webpage.py +19 -18
- jarvis/jarvis_tools/registry.py +435 -156
- jarvis/jarvis_tools/retrieve_memory.py +16 -11
- jarvis/jarvis_tools/save_memory.py +8 -6
- jarvis/jarvis_tools/search_web.py +31 -31
- jarvis/jarvis_tools/sub_agent.py +32 -28
- jarvis/jarvis_tools/sub_code_agent.py +44 -60
- jarvis/jarvis_tools/task_list_manager.py +1811 -0
- jarvis/jarvis_tools/virtual_tty.py +29 -19
- jarvis/jarvis_utils/__init__.py +4 -0
- jarvis/jarvis_utils/builtin_replace_map.py +2 -1
- jarvis/jarvis_utils/clipboard.py +9 -8
- jarvis/jarvis_utils/collections.py +331 -0
- jarvis/jarvis_utils/config.py +699 -194
- jarvis/jarvis_utils/dialogue_recorder.py +294 -0
- jarvis/jarvis_utils/embedding.py +6 -3
- jarvis/jarvis_utils/file_processors.py +7 -1
- jarvis/jarvis_utils/fzf.py +9 -3
- jarvis/jarvis_utils/git_utils.py +71 -42
- jarvis/jarvis_utils/globals.py +116 -32
- jarvis/jarvis_utils/http.py +6 -2
- jarvis/jarvis_utils/input.py +318 -83
- jarvis/jarvis_utils/jsonnet_compat.py +119 -104
- jarvis/jarvis_utils/methodology.py +37 -28
- jarvis/jarvis_utils/output.py +201 -44
- jarvis/jarvis_utils/utils.py +986 -628
- {jarvis_ai_assistant-0.7.16.dist-info → jarvis_ai_assistant-1.0.2.dist-info}/METADATA +49 -33
- jarvis_ai_assistant-1.0.2.dist-info/RECORD +304 -0
- jarvis/jarvis_code_agent/code_analyzer/structured_code.py +0 -556
- jarvis/jarvis_tools/generate_new_tool.py +0 -205
- jarvis/jarvis_tools/lsp_client.py +0 -1552
- jarvis/jarvis_tools/rewrite_file.py +0 -105
- jarvis_ai_assistant-0.7.16.dist-info/RECORD +0 -218
- {jarvis_ai_assistant-0.7.16.dist-info → jarvis_ai_assistant-1.0.2.dist-info}/WHEEL +0 -0
- {jarvis_ai_assistant-0.7.16.dist-info → jarvis_ai_assistant-1.0.2.dist-info}/entry_points.txt +0 -0
- {jarvis_ai_assistant-0.7.16.dist-info → jarvis_ai_assistant-1.0.2.dist-info}/licenses/LICENSE +0 -0
- {jarvis_ai_assistant-0.7.16.dist-info → jarvis_ai_assistant-1.0.2.dist-info}/top_level.txt +0 -0
|
@@ -17,50 +17,42 @@ C2Rust 转译器模块
|
|
|
17
17
|
- 本模块提供 run_transpile(...) 作为对外入口,后续在 cli.py 中挂载为子命令
|
|
18
18
|
- 尽量复用现有 Agent/CodeAgent 能力,保持最小侵入与稳定性
|
|
19
19
|
"""
|
|
20
|
+
|
|
20
21
|
from __future__ import annotations
|
|
21
22
|
|
|
22
|
-
import json
|
|
23
|
-
import os
|
|
24
|
-
import re
|
|
25
|
-
import subprocess
|
|
26
|
-
import time
|
|
27
23
|
from pathlib import Path
|
|
28
|
-
from typing import Any
|
|
29
|
-
|
|
30
|
-
import
|
|
31
|
-
|
|
32
|
-
from
|
|
33
|
-
from
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
from jarvis.
|
|
37
|
-
|
|
38
|
-
from jarvis.jarvis_c2rust.constants import
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
DEFAULT_REVIEW_MAX_ITERATIONS,
|
|
46
|
-
DEFAULT_TEST_MAX_RETRIES,
|
|
47
|
-
ERROR_SUMMARY_MAX_LENGTH,
|
|
48
|
-
MAX_FUNCTION_RETRIES,
|
|
49
|
-
PROGRESS_JSON,
|
|
50
|
-
SYMBOL_MAP_JSONL,
|
|
51
|
-
)
|
|
24
|
+
from typing import Any
|
|
25
|
+
from typing import Dict
|
|
26
|
+
from typing import List
|
|
27
|
+
from typing import Optional
|
|
28
|
+
from typing import Tuple
|
|
29
|
+
from typing import Union
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
from jarvis.jarvis_c2rust.constants import C2RUST_DIRNAME
|
|
33
|
+
from jarvis.jarvis_c2rust.constants import CONFIG_JSON
|
|
34
|
+
from jarvis.jarvis_c2rust.constants import DEFAULT_CHECK_MAX_RETRIES
|
|
35
|
+
from jarvis.jarvis_c2rust.constants import DEFAULT_PLAN_MAX_RETRIES
|
|
36
|
+
from jarvis.jarvis_c2rust.constants import DEFAULT_PLAN_MAX_RETRIES_ENTRY
|
|
37
|
+
from jarvis.jarvis_c2rust.constants import DEFAULT_REVIEW_MAX_ITERATIONS
|
|
38
|
+
from jarvis.jarvis_c2rust.constants import DEFAULT_TEST_MAX_RETRIES
|
|
39
|
+
from jarvis.jarvis_c2rust.constants import PROGRESS_JSON
|
|
40
|
+
from jarvis.jarvis_c2rust.constants import SYMBOL_MAP_JSONL
|
|
52
41
|
from jarvis.jarvis_c2rust.loaders import _SymbolMapJsonl
|
|
53
42
|
from jarvis.jarvis_c2rust.models import FnRecord
|
|
54
|
-
from jarvis.jarvis_c2rust.
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
43
|
+
from jarvis.jarvis_c2rust.transpiler_agents import AgentManager
|
|
44
|
+
from jarvis.jarvis_c2rust.transpiler_compile import CompileCommandsManager
|
|
45
|
+
from jarvis.jarvis_c2rust.transpiler_config import ConfigManager
|
|
46
|
+
from jarvis.jarvis_c2rust.transpiler_context import ContextCollector
|
|
47
|
+
from jarvis.jarvis_c2rust.transpiler_executor import TranspilerExecutor
|
|
48
|
+
from jarvis.jarvis_c2rust.transpiler_generation import GenerationManager
|
|
49
|
+
from jarvis.jarvis_c2rust.transpiler_git import GitManager
|
|
50
|
+
from jarvis.jarvis_c2rust.transpiler_modules import ModuleManager
|
|
51
|
+
from jarvis.jarvis_c2rust.transpiler_planning import PlanningManager
|
|
52
|
+
from jarvis.jarvis_c2rust.transpiler_symbols import SymbolMapper
|
|
53
|
+
from jarvis.jarvis_c2rust.utils import check_and_handle_test_deletion
|
|
54
|
+
from jarvis.jarvis_c2rust.utils import default_crate_dir
|
|
55
|
+
from jarvis.jarvis_code_agent.code_agent import CodeAgent
|
|
64
56
|
|
|
65
57
|
|
|
66
58
|
class Transpiler:
|
|
@@ -71,11 +63,19 @@ class Transpiler:
|
|
|
71
63
|
llm_group: Optional[str] = None,
|
|
72
64
|
plan_max_retries: int = DEFAULT_PLAN_MAX_RETRIES, # 规划阶段最大重试次数(0表示无限重试)
|
|
73
65
|
max_retries: int = 0, # 兼容旧接口,如未设置则使用 check_max_retries 和 test_max_retries
|
|
74
|
-
check_max_retries: Optional[
|
|
75
|
-
|
|
66
|
+
check_max_retries: Optional[
|
|
67
|
+
int
|
|
68
|
+
] = None, # cargo check 阶段最大重试次数(0表示无限重试)
|
|
69
|
+
test_max_retries: Optional[
|
|
70
|
+
int
|
|
71
|
+
] = None, # cargo test 阶段最大重试次数(0表示无限重试)
|
|
76
72
|
review_max_iterations: int = DEFAULT_REVIEW_MAX_ITERATIONS, # 审查阶段最大迭代次数(0表示无限重试)
|
|
77
|
-
disabled_libraries: Optional[
|
|
78
|
-
|
|
73
|
+
disabled_libraries: Optional[
|
|
74
|
+
List[str]
|
|
75
|
+
] = None, # 禁用库列表(在实现时禁止使用这些库)
|
|
76
|
+
root_symbols: Optional[
|
|
77
|
+
List[str]
|
|
78
|
+
] = None, # 根符号列表(这些符号对应的接口实现时要求对外暴露,main除外)
|
|
79
79
|
non_interactive: bool = True,
|
|
80
80
|
) -> None:
|
|
81
81
|
self.project_root = Path(project_root).resolve()
|
|
@@ -91,27 +91,47 @@ class Transpiler:
|
|
|
91
91
|
self.check_max_retries = max_retries
|
|
92
92
|
self.test_max_retries = max_retries
|
|
93
93
|
else:
|
|
94
|
-
self.check_max_retries =
|
|
95
|
-
|
|
96
|
-
|
|
94
|
+
self.check_max_retries = (
|
|
95
|
+
check_max_retries
|
|
96
|
+
if check_max_retries is not None
|
|
97
|
+
else DEFAULT_CHECK_MAX_RETRIES
|
|
98
|
+
)
|
|
99
|
+
self.test_max_retries = (
|
|
100
|
+
test_max_retries
|
|
101
|
+
if test_max_retries is not None
|
|
102
|
+
else DEFAULT_TEST_MAX_RETRIES
|
|
103
|
+
)
|
|
104
|
+
self.max_retries = max(
|
|
105
|
+
self.check_max_retries, self.test_max_retries
|
|
106
|
+
) # 保持兼容性
|
|
97
107
|
self.review_max_iterations = review_max_iterations
|
|
98
108
|
self.non_interactive = non_interactive
|
|
99
109
|
|
|
100
|
-
self.crate_dir =
|
|
110
|
+
self.crate_dir = (
|
|
111
|
+
Path(crate_dir) if crate_dir else default_crate_dir(self.project_root)
|
|
112
|
+
)
|
|
101
113
|
# 使用自包含的 order.jsonl 记录构建索引,避免依赖 symbols.jsonl
|
|
102
114
|
self.fn_index_by_id: Dict[int, FnRecord] = {}
|
|
103
115
|
self.fn_name_to_id: Dict[str, int] = {}
|
|
104
116
|
|
|
105
117
|
# 断点续跑功能默认始终启用
|
|
106
118
|
self.resume = True
|
|
107
|
-
|
|
108
|
-
#
|
|
109
|
-
|
|
110
|
-
self.
|
|
111
|
-
|
|
119
|
+
|
|
120
|
+
# 初始化状态变量(需要在初始化管理器之前)
|
|
121
|
+
# 当前函数开始时的 commit id(用于失败回退)
|
|
122
|
+
self._current_function_start_commit: Optional[str] = None
|
|
123
|
+
# 连续修复失败的次数(用于判断是否需要回退)
|
|
124
|
+
self._consecutive_fix_failures: int = 0
|
|
125
|
+
# 每个 Agent 对应的工具调用前的 commit id(用于细粒度检测)
|
|
126
|
+
self._agent_before_commits: Dict[str, Optional[str]] = {}
|
|
127
|
+
|
|
128
|
+
# 初始化各个功能模块
|
|
129
|
+
self.config_manager = ConfigManager(self.data_dir, self.progress_path)
|
|
130
|
+
self.progress = self.config_manager.progress
|
|
131
|
+
|
|
112
132
|
# 从独立的配置文件加载配置(支持从 progress.json 向后兼容迁移)
|
|
113
|
-
config = self.
|
|
114
|
-
|
|
133
|
+
config = self.config_manager.load_config()
|
|
134
|
+
|
|
115
135
|
# 如果提供了新的根符号或禁用库,更新配置;否则从配置文件中恢复
|
|
116
136
|
# 优先使用传入的参数,如果为 None 则从配置文件恢复
|
|
117
137
|
if root_symbols is not None:
|
|
@@ -121,7 +141,7 @@ class Transpiler:
|
|
|
121
141
|
# 传入的参数为 None,从配置文件恢复
|
|
122
142
|
# 如果配置文件中有配置则使用,否则使用空列表
|
|
123
143
|
self.root_symbols = config.get("root_symbols", [])
|
|
124
|
-
|
|
144
|
+
|
|
125
145
|
if disabled_libraries is not None:
|
|
126
146
|
# 传入的参数不为 None,使用传入的值并保存
|
|
127
147
|
self.disabled_libraries = disabled_libraries
|
|
@@ -129,372 +149,170 @@ class Transpiler:
|
|
|
129
149
|
# 传入的参数为 None,从配置文件恢复
|
|
130
150
|
# 如果配置文件中有配置则使用,否则使用空列表
|
|
131
151
|
self.disabled_libraries = config.get("disabled_libraries", [])
|
|
132
|
-
|
|
152
|
+
|
|
133
153
|
# 从配置文件读取附加说明(不支持通过参数传入,只能通过配置文件设置)
|
|
134
154
|
self.additional_notes = config.get("additional_notes", "")
|
|
135
|
-
|
|
155
|
+
|
|
136
156
|
# 保存配置到独立的配置文件
|
|
137
|
-
self.
|
|
138
|
-
|
|
157
|
+
self.config_manager.save_config(
|
|
158
|
+
self.root_symbols, self.disabled_libraries, self.additional_notes
|
|
159
|
+
)
|
|
160
|
+
|
|
161
|
+
# 初始化其他模块
|
|
162
|
+
self.compile_commands_manager = CompileCommandsManager(self.project_root)
|
|
163
|
+
self.git_manager = GitManager(str(self.crate_dir))
|
|
164
|
+
self.module_manager = ModuleManager(self.crate_dir)
|
|
165
|
+
|
|
166
|
+
# 初始化 Agent 管理器
|
|
167
|
+
self.agent_manager = AgentManager(
|
|
168
|
+
self.crate_dir,
|
|
169
|
+
self.project_root,
|
|
170
|
+
self.llm_group,
|
|
171
|
+
self.non_interactive,
|
|
172
|
+
self.fn_index_by_id,
|
|
173
|
+
self._get_crate_commit_hash,
|
|
174
|
+
self._agent_before_commits,
|
|
175
|
+
)
|
|
176
|
+
self.agent_manager.set_reset_to_commit_func(self._reset_to_commit)
|
|
177
|
+
|
|
178
|
+
# 初始化规划管理器(需要在 AgentManager 之后,因为需要访问 agent_manager 的方法)
|
|
179
|
+
self.planning_manager = PlanningManager(
|
|
180
|
+
self.project_root,
|
|
181
|
+
self.crate_dir,
|
|
182
|
+
self.data_dir,
|
|
183
|
+
self.llm_group,
|
|
184
|
+
self.plan_max_retries,
|
|
185
|
+
self.non_interactive,
|
|
186
|
+
self.disabled_libraries,
|
|
187
|
+
self.root_symbols,
|
|
188
|
+
self._extract_compile_flags,
|
|
189
|
+
self._collect_callees_context,
|
|
190
|
+
self._append_additional_notes,
|
|
191
|
+
self._is_root_symbol,
|
|
192
|
+
self._get_crate_commit_hash,
|
|
193
|
+
self.agent_manager.on_before_tool_call,
|
|
194
|
+
self.agent_manager.on_after_tool_call,
|
|
195
|
+
self._agent_before_commits,
|
|
196
|
+
)
|
|
197
|
+
|
|
198
|
+
# 初始化代码生成管理器
|
|
199
|
+
self.generation_manager = GenerationManager(
|
|
200
|
+
self.project_root,
|
|
201
|
+
self.crate_dir,
|
|
202
|
+
self.data_dir,
|
|
203
|
+
self.disabled_libraries,
|
|
204
|
+
self._extract_compile_flags,
|
|
205
|
+
self._append_additional_notes,
|
|
206
|
+
self._is_root_symbol,
|
|
207
|
+
self._get_generation_agent,
|
|
208
|
+
self._compose_prompt_with_context,
|
|
209
|
+
self._check_and_handle_test_deletion,
|
|
210
|
+
self._get_crate_commit_hash,
|
|
211
|
+
self._ensure_top_level_pub_mod,
|
|
212
|
+
)
|
|
213
|
+
|
|
214
|
+
# 构建管理器将在需要时延迟初始化(因为需要访问其他管理器的方法)
|
|
215
|
+
self.build_manager: Optional[Any] = None
|
|
216
|
+
self._build_loop_has_fixes = False # 标记构建循环中是否进行了修复
|
|
217
|
+
|
|
218
|
+
# 初始化审查管理器(需要在其他管理器之后,因为需要访问它们的方法)
|
|
219
|
+
from jarvis.jarvis_c2rust.transpiler_review import ReviewManager
|
|
220
|
+
|
|
221
|
+
self.review_manager = ReviewManager(
|
|
222
|
+
self.crate_dir,
|
|
223
|
+
self.data_dir,
|
|
224
|
+
self.llm_group,
|
|
225
|
+
self.non_interactive,
|
|
226
|
+
self.review_max_iterations,
|
|
227
|
+
self.disabled_libraries,
|
|
228
|
+
self.progress,
|
|
229
|
+
self._save_progress,
|
|
230
|
+
self._read_source_span,
|
|
231
|
+
self._collect_callees_context,
|
|
232
|
+
self._extract_compile_flags,
|
|
233
|
+
self._is_root_symbol,
|
|
234
|
+
self._get_crate_commit_hash,
|
|
235
|
+
lambda: self._current_function_start_commit,
|
|
236
|
+
self._compose_prompt_with_context,
|
|
237
|
+
self._get_fix_agent,
|
|
238
|
+
self._check_and_handle_test_deletion,
|
|
239
|
+
self._append_additional_notes,
|
|
240
|
+
self._cargo_build_loop,
|
|
241
|
+
self._get_build_loop_has_fixes,
|
|
242
|
+
self._on_before_tool_call,
|
|
243
|
+
self._on_after_tool_call,
|
|
244
|
+
self._agent_before_commits,
|
|
245
|
+
self._get_git_diff,
|
|
246
|
+
)
|
|
247
|
+
|
|
139
248
|
# 在初始化完成后打印日志
|
|
140
|
-
|
|
249
|
+
from jarvis.jarvis_utils.output import PrettyOutput
|
|
250
|
+
|
|
251
|
+
PrettyOutput.auto_print(
|
|
252
|
+
f"📋 [c2rust-transpiler][init] 初始化参数: project_root={self.project_root} crate_dir={self.crate_dir} llm_group={self.llm_group} plan_max_retries={self.plan_max_retries} check_max_retries={self.check_max_retries} test_max_retries={self.test_max_retries} review_max_iterations={self.review_max_iterations} disabled_libraries={self.disabled_libraries} root_symbols={self.root_symbols} non_interactive={self.non_interactive}"
|
|
253
|
+
)
|
|
141
254
|
# 使用 JSONL 存储的符号映射
|
|
142
255
|
self.symbol_map = _SymbolMapJsonl(self.symbol_map_path)
|
|
143
256
|
|
|
144
|
-
#
|
|
145
|
-
self.
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
# 当前函数开始时的 commit id(用于失败回退)
|
|
158
|
-
self._current_function_start_commit: Optional[str] = None
|
|
159
|
-
# 连续修复失败的次数(用于判断是否需要回退)
|
|
160
|
-
self._consecutive_fix_failures: int = 0
|
|
161
|
-
# 每个 Agent 对应的工具调用前的 commit id(用于细粒度检测)
|
|
162
|
-
self._agent_before_commits: Dict[str, Optional[str]] = {}
|
|
163
|
-
|
|
164
|
-
def _find_compile_commands(self) -> Optional[Path]:
|
|
165
|
-
"""
|
|
166
|
-
查找 compile_commands.json 文件。
|
|
167
|
-
搜索顺序:
|
|
168
|
-
1. project_root / compile_commands.json
|
|
169
|
-
2. project_root / build / compile_commands.json
|
|
170
|
-
3. project_root 的父目录及向上查找(最多向上3层)
|
|
171
|
-
"""
|
|
172
|
-
# 首先在 project_root 下查找
|
|
173
|
-
candidates = [
|
|
174
|
-
self.project_root / "compile_commands.json",
|
|
175
|
-
self.project_root / "build" / "compile_commands.json",
|
|
176
|
-
]
|
|
177
|
-
# 向上查找(最多3层)
|
|
178
|
-
current = self.project_root.parent
|
|
179
|
-
for _ in range(3):
|
|
180
|
-
if current and current.exists():
|
|
181
|
-
candidates.append(current / "compile_commands.json")
|
|
182
|
-
current = current.parent
|
|
183
|
-
else:
|
|
184
|
-
break
|
|
185
|
-
|
|
186
|
-
for path in candidates:
|
|
187
|
-
if path.exists() and path.is_file():
|
|
188
|
-
return path.resolve()
|
|
189
|
-
return None
|
|
190
|
-
|
|
191
|
-
def _load_compile_commands(self) -> Optional[List[Dict[str, Any]]]:
|
|
192
|
-
"""
|
|
193
|
-
加载 compile_commands.json 文件。
|
|
194
|
-
如果已缓存,直接返回缓存结果。
|
|
195
|
-
"""
|
|
196
|
-
if self._compile_commands_cache is not None:
|
|
197
|
-
return self._compile_commands_cache
|
|
198
|
-
|
|
199
|
-
compile_commands_path = self._find_compile_commands()
|
|
200
|
-
if compile_commands_path is None:
|
|
201
|
-
self._compile_commands_cache = []
|
|
202
|
-
self._compile_commands_path = None
|
|
203
|
-
return None
|
|
204
|
-
|
|
205
|
-
try:
|
|
206
|
-
with compile_commands_path.open("r", encoding="utf-8") as f:
|
|
207
|
-
data = json.load(f)
|
|
208
|
-
if isinstance(data, list):
|
|
209
|
-
self._compile_commands_cache = data
|
|
210
|
-
self._compile_commands_path = compile_commands_path
|
|
211
|
-
typer.secho(f"[c2rust-transpiler][compile_commands] 已加载: {compile_commands_path} ({len(data)} 条记录)", fg=typer.colors.BLUE)
|
|
212
|
-
return data
|
|
213
|
-
except Exception as e:
|
|
214
|
-
typer.secho(f"[c2rust-transpiler][compile_commands] 加载失败: {compile_commands_path}: {e}", fg=typer.colors.YELLOW)
|
|
215
|
-
self._compile_commands_cache = []
|
|
216
|
-
self._compile_commands_path = None
|
|
217
|
-
return None
|
|
218
|
-
|
|
219
|
-
self._compile_commands_cache = []
|
|
220
|
-
self._compile_commands_path = None
|
|
221
|
-
return None
|
|
257
|
+
# 初始化上下文收集器和符号映射器
|
|
258
|
+
self.context_collector = ContextCollector(
|
|
259
|
+
self.project_root,
|
|
260
|
+
self.fn_index_by_id,
|
|
261
|
+
self.fn_name_to_id,
|
|
262
|
+
self.symbol_map,
|
|
263
|
+
)
|
|
264
|
+
self.symbol_mapper = SymbolMapper(
|
|
265
|
+
self.symbol_map,
|
|
266
|
+
self.progress,
|
|
267
|
+
self.config_manager,
|
|
268
|
+
self.git_manager,
|
|
269
|
+
)
|
|
222
270
|
|
|
223
271
|
def _extract_compile_flags(self, c_file_path: Union[str, Path]) -> Optional[str]:
|
|
224
|
-
"""
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
如果 compile_commands.json 中存在 arguments 字段,则用空格连接该数组并返回。
|
|
228
|
-
如果只有 command 字段,则直接返回 command 字符串。
|
|
229
|
-
|
|
230
|
-
返回格式:
|
|
231
|
-
- 如果存在 arguments:用空格连接的参数字符串,例如 "-I/usr/include -DDEBUG"
|
|
232
|
-
- 如果只有 command:完整的编译命令字符串,例如 "gcc -I/usr/include -DDEBUG file.c"
|
|
233
|
-
|
|
234
|
-
如果未找到或解析失败,返回 None。
|
|
235
|
-
"""
|
|
236
|
-
compile_commands = self._load_compile_commands()
|
|
237
|
-
if not compile_commands:
|
|
238
|
-
return None
|
|
239
|
-
|
|
240
|
-
# 规范化目标文件路径
|
|
241
|
-
try:
|
|
242
|
-
target_path = Path(c_file_path)
|
|
243
|
-
if not target_path.is_absolute():
|
|
244
|
-
target_path = (self.project_root / target_path).resolve()
|
|
245
|
-
target_path = target_path.resolve()
|
|
246
|
-
except Exception:
|
|
247
|
-
return None
|
|
248
|
-
|
|
249
|
-
# 查找匹配的编译命令
|
|
250
|
-
for entry in compile_commands:
|
|
251
|
-
if not isinstance(entry, dict):
|
|
252
|
-
continue
|
|
253
|
-
|
|
254
|
-
entry_file = entry.get("file")
|
|
255
|
-
if not entry_file:
|
|
256
|
-
continue
|
|
257
|
-
|
|
258
|
-
try:
|
|
259
|
-
entry_path = Path(entry_file)
|
|
260
|
-
if not entry_path.is_absolute() and entry.get("directory"):
|
|
261
|
-
entry_path = (Path(entry.get("directory")) / entry_path).resolve()
|
|
262
|
-
entry_path = entry_path.resolve()
|
|
263
|
-
|
|
264
|
-
# 路径匹配(支持相对路径和绝对路径)
|
|
265
|
-
if entry_path == target_path:
|
|
266
|
-
# 如果存在 arguments,用空格连接并返回
|
|
267
|
-
arguments = entry.get("arguments")
|
|
268
|
-
if isinstance(arguments, list):
|
|
269
|
-
# 过滤掉空字符串,然后用空格连接
|
|
270
|
-
args = [str(arg) for arg in arguments if arg]
|
|
271
|
-
return " ".join(args) if args else None
|
|
272
|
-
# 如果只有 command,直接返回 command 字符串
|
|
273
|
-
elif entry.get("command"):
|
|
274
|
-
command = entry.get("command", "")
|
|
275
|
-
return command if command else None
|
|
276
|
-
except Exception:
|
|
277
|
-
continue
|
|
278
|
-
|
|
279
|
-
return None
|
|
272
|
+
"""从 compile_commands.json 中提取指定 C 文件的编译参数(委托给 CompileCommandsManager)"""
|
|
273
|
+
return self.compile_commands_manager.extract_compile_flags(c_file_path)
|
|
280
274
|
|
|
281
275
|
def _save_progress(self) -> None:
|
|
282
276
|
"""保存进度,使用原子性写入"""
|
|
283
|
-
|
|
277
|
+
self.config_manager.save_progress()
|
|
284
278
|
|
|
285
279
|
def _load_config(self) -> Dict[str, Any]:
|
|
286
|
-
"""
|
|
287
|
-
|
|
288
|
-
如果配置文件不存在,尝试从 progress.json 迁移配置(向后兼容)。
|
|
289
|
-
"""
|
|
290
|
-
config_path = self.data_dir / CONFIG_JSON
|
|
291
|
-
default_config = {"root_symbols": [], "disabled_libraries": [], "additional_notes": ""}
|
|
292
|
-
|
|
293
|
-
# 尝试从配置文件读取
|
|
294
|
-
if config_path.exists():
|
|
295
|
-
config = read_json(config_path, default_config)
|
|
296
|
-
if isinstance(config, dict):
|
|
297
|
-
# 确保包含所有必需的键(向后兼容)
|
|
298
|
-
if "additional_notes" not in config:
|
|
299
|
-
config["additional_notes"] = ""
|
|
300
|
-
return config
|
|
301
|
-
|
|
302
|
-
# 向后兼容:如果配置文件不存在,尝试从 progress.json 迁移
|
|
303
|
-
progress_config = self.progress.get("config", {})
|
|
304
|
-
if progress_config:
|
|
305
|
-
# 迁移配置到独立文件
|
|
306
|
-
migrated_config = {
|
|
307
|
-
"root_symbols": progress_config.get("root_symbols", []),
|
|
308
|
-
"disabled_libraries": progress_config.get("disabled_libraries", []),
|
|
309
|
-
"additional_notes": progress_config.get("additional_notes", ""),
|
|
310
|
-
}
|
|
311
|
-
write_json(config_path, migrated_config)
|
|
312
|
-
typer.secho(f"[c2rust-transpiler][config] 已从 progress.json 迁移配置到 {config_path}", fg=typer.colors.YELLOW)
|
|
313
|
-
# 从 progress.json 中移除 config(可选,保持兼容性)
|
|
314
|
-
# if "config" in self.progress:
|
|
315
|
-
# del self.progress["config"]
|
|
316
|
-
# self._save_progress()
|
|
317
|
-
return migrated_config
|
|
318
|
-
|
|
319
|
-
return default_config
|
|
280
|
+
"""从独立的配置文件加载配置(委托给 ConfigManager)"""
|
|
281
|
+
return self.config_manager.load_config()
|
|
320
282
|
|
|
321
283
|
def _save_config(self) -> None:
|
|
322
|
-
"""
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
"disabled_libraries": self.disabled_libraries,
|
|
327
|
-
"additional_notes": getattr(self, "additional_notes", ""),
|
|
328
|
-
}
|
|
329
|
-
write_json(config_path, config)
|
|
330
|
-
|
|
284
|
+
"""保存配置到独立的配置文件(委托给 ConfigManager)"""
|
|
285
|
+
self.config_manager.save_config(
|
|
286
|
+
self.root_symbols, self.disabled_libraries, self.additional_notes
|
|
287
|
+
)
|
|
331
288
|
|
|
332
289
|
def _read_source_span(self, rec: FnRecord) -> str:
|
|
333
290
|
"""按起止行读取源码片段(忽略列边界,尽量完整)"""
|
|
334
|
-
|
|
335
|
-
p = Path(rec.file)
|
|
336
|
-
if not p.is_absolute():
|
|
337
|
-
p = (self.project_root / p).resolve()
|
|
338
|
-
if not p.exists():
|
|
339
|
-
return ""
|
|
340
|
-
lines = p.read_text(encoding="utf-8", errors="replace").splitlines()
|
|
341
|
-
s = max(1, int(rec.start_line or 1))
|
|
342
|
-
e = min(len(lines), max(int(rec.end_line or s), s))
|
|
343
|
-
chunk = "\n".join(lines[s - 1 : e])
|
|
344
|
-
return chunk
|
|
345
|
-
except Exception:
|
|
346
|
-
return ""
|
|
291
|
+
return self.context_collector.read_source_span(rec)
|
|
347
292
|
|
|
348
293
|
def _load_order_index(self, order_jsonl: Path) -> None:
|
|
349
|
-
"""
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
若同一 id 多次出现,首次记录为准。
|
|
354
|
-
"""
|
|
355
|
-
self.fn_index_by_id.clear()
|
|
356
|
-
self.fn_name_to_id.clear()
|
|
357
|
-
typer.secho(f"[c2rust-transpiler][index] 正在加载翻译顺序索引: {order_jsonl}", fg=typer.colors.BLUE)
|
|
358
|
-
try:
|
|
359
|
-
with order_jsonl.open("r", encoding="utf-8") as f:
|
|
360
|
-
for ln in f:
|
|
361
|
-
ln = ln.strip()
|
|
362
|
-
if not ln:
|
|
363
|
-
continue
|
|
364
|
-
try:
|
|
365
|
-
obj = json.loads(ln)
|
|
366
|
-
except Exception:
|
|
367
|
-
continue
|
|
368
|
-
# 仅支持新格式:items
|
|
369
|
-
recs = obj.get("items")
|
|
370
|
-
if not isinstance(recs, list):
|
|
371
|
-
continue
|
|
372
|
-
for r in recs:
|
|
373
|
-
if not isinstance(r, dict):
|
|
374
|
-
continue
|
|
375
|
-
# 构建 FnRecord
|
|
376
|
-
try:
|
|
377
|
-
fid = int(r.get("id"))
|
|
378
|
-
except Exception:
|
|
379
|
-
continue
|
|
380
|
-
if fid in self.fn_index_by_id:
|
|
381
|
-
# 已收录
|
|
382
|
-
continue
|
|
383
|
-
nm = r.get("name") or ""
|
|
384
|
-
qn = r.get("qualified_name") or ""
|
|
385
|
-
fp = r.get("file") or ""
|
|
386
|
-
refs = r.get("ref")
|
|
387
|
-
if not isinstance(refs, list):
|
|
388
|
-
refs = []
|
|
389
|
-
refs = [c for c in refs if isinstance(c, str) and c]
|
|
390
|
-
sr = int(r.get("start_line") or 0)
|
|
391
|
-
sc = int(r.get("start_col") or 0)
|
|
392
|
-
er = int(r.get("end_line") or 0)
|
|
393
|
-
ec = int(r.get("end_col") or 0)
|
|
394
|
-
sg = r.get("signature") or ""
|
|
395
|
-
rt = r.get("return_type") or ""
|
|
396
|
-
pr = r.get("params") if isinstance(r.get("params"), list) else None
|
|
397
|
-
lr = r.get("lib_replacement") if isinstance(r.get("lib_replacement"), dict) else None
|
|
398
|
-
rec = FnRecord(
|
|
399
|
-
id=fid,
|
|
400
|
-
name=nm,
|
|
401
|
-
qname=qn,
|
|
402
|
-
file=fp,
|
|
403
|
-
start_line=sr,
|
|
404
|
-
start_col=sc,
|
|
405
|
-
end_line=er,
|
|
406
|
-
end_col=ec,
|
|
407
|
-
refs=refs,
|
|
408
|
-
signature=str(sg or ""),
|
|
409
|
-
return_type=str(rt or ""),
|
|
410
|
-
params=pr,
|
|
411
|
-
lib_replacement=lr,
|
|
412
|
-
)
|
|
413
|
-
self.fn_index_by_id[fid] = rec
|
|
414
|
-
if nm:
|
|
415
|
-
self.fn_name_to_id.setdefault(nm, fid)
|
|
416
|
-
if qn:
|
|
417
|
-
self.fn_name_to_id.setdefault(qn, fid)
|
|
418
|
-
except Exception:
|
|
419
|
-
# 若索引构建失败,保持为空,后续流程将跳过
|
|
420
|
-
pass
|
|
421
|
-
typer.secho(f"[c2rust-transpiler][index] 索引构建完成: ids={len(self.fn_index_by_id)} names={len(self.fn_name_to_id)}", fg=typer.colors.BLUE)
|
|
294
|
+
"""从自包含的 order.jsonl 中加载所有 records(委托给 ConfigManager)"""
|
|
295
|
+
self.config_manager.load_order_index(
|
|
296
|
+
order_jsonl, self.fn_index_by_id, self.fn_name_to_id
|
|
297
|
+
)
|
|
422
298
|
|
|
423
299
|
def _should_skip(self, rec: FnRecord) -> bool:
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
return True
|
|
427
|
-
return False
|
|
300
|
+
"""判断是否应该跳过该函数(委托给 SymbolMapper)"""
|
|
301
|
+
return self.symbol_mapper.should_skip(rec)
|
|
428
302
|
|
|
429
303
|
def _collect_callees_context(self, rec: FnRecord) -> List[Dict[str, Any]]:
|
|
430
|
-
"""
|
|
431
|
-
|
|
432
|
-
- 若已转译:提供 {name, qname, translated: true, rust_module, rust_symbol, ambiguous?}
|
|
433
|
-
- 若未转译但存在扫描记录:提供 {name, qname, translated: false, file, start_line, end_line}
|
|
434
|
-
- 若仅名称:提供 {name, qname, translated: false}
|
|
435
|
-
注:若存在同名映射多条记录(重载/同名符号),此处标记 ambiguous=true,并选择最近一条作为提示。
|
|
436
|
-
"""
|
|
437
|
-
ctx: List[Dict[str, Any]] = []
|
|
438
|
-
for callee in rec.refs or []:
|
|
439
|
-
entry: Dict[str, Any] = {"name": callee, "qname": callee}
|
|
440
|
-
# 已转译映射
|
|
441
|
-
if self.symbol_map.has_symbol(callee):
|
|
442
|
-
recs = self.symbol_map.get(callee)
|
|
443
|
-
m = recs[-1] if recs else None
|
|
444
|
-
entry.update({
|
|
445
|
-
"translated": True,
|
|
446
|
-
"rust_module": (m or {}).get("module"),
|
|
447
|
-
"rust_symbol": (m or {}).get("rust_symbol"),
|
|
448
|
-
})
|
|
449
|
-
if len(recs) > 1:
|
|
450
|
-
entry["ambiguous"] = True
|
|
451
|
-
ctx.append(entry)
|
|
452
|
-
continue
|
|
453
|
-
# 使用 order 索引按名称解析ID(函数或类型)
|
|
454
|
-
cid = self.fn_name_to_id.get(callee)
|
|
455
|
-
if cid:
|
|
456
|
-
crec = self.fn_index_by_id.get(cid)
|
|
457
|
-
if crec:
|
|
458
|
-
entry.update({
|
|
459
|
-
"translated": False,
|
|
460
|
-
"file": crec.file,
|
|
461
|
-
"start_line": crec.start_line,
|
|
462
|
-
"end_line": crec.end_line,
|
|
463
|
-
})
|
|
464
|
-
else:
|
|
465
|
-
entry.update({"translated": False})
|
|
466
|
-
ctx.append(entry)
|
|
467
|
-
return ctx
|
|
304
|
+
"""生成被引用符号上下文列表(委托给 ContextCollector)"""
|
|
305
|
+
return self.context_collector.collect_callees_context(rec)
|
|
468
306
|
|
|
469
307
|
def _untranslated_callee_symbols(self, rec: FnRecord) -> List[str]:
|
|
470
|
-
"""
|
|
471
|
-
|
|
472
|
-
"""
|
|
473
|
-
syms: List[str] = []
|
|
474
|
-
for callee in rec.refs or []:
|
|
475
|
-
if not self.symbol_map.has_symbol(callee):
|
|
476
|
-
syms.append(callee)
|
|
477
|
-
# 去重
|
|
478
|
-
try:
|
|
479
|
-
syms = list(dict.fromkeys(syms))
|
|
480
|
-
except Exception:
|
|
481
|
-
syms = sorted(list(set(syms)))
|
|
482
|
-
return syms
|
|
308
|
+
"""返回尚未转换的被调函数符号(委托给 ContextCollector)"""
|
|
309
|
+
return self.context_collector.untranslated_callee_symbols(rec)
|
|
483
310
|
|
|
484
311
|
def _append_additional_notes(self, prompt: str) -> str:
|
|
485
|
-
"""
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
prompt: 原始提示词
|
|
490
|
-
|
|
491
|
-
Returns:
|
|
492
|
-
追加了附加说明的提示词
|
|
493
|
-
"""
|
|
494
|
-
additional_notes = getattr(self, "additional_notes", "")
|
|
495
|
-
if additional_notes and additional_notes.strip():
|
|
496
|
-
return prompt + "\n\n" + "【附加说明(用户自定义)】\n" + additional_notes.strip()
|
|
497
|
-
return prompt
|
|
312
|
+
"""在提示词末尾追加附加说明(委托给 ContextCollector)"""
|
|
313
|
+
return self.context_collector.append_additional_notes(
|
|
314
|
+
prompt, self.additional_notes
|
|
315
|
+
)
|
|
498
316
|
|
|
499
317
|
def _build_module_selection_prompts(
|
|
500
318
|
self,
|
|
@@ -503,527 +321,80 @@ class Transpiler:
|
|
|
503
321
|
callees_ctx: List[Dict[str, Any]],
|
|
504
322
|
crate_tree: str,
|
|
505
323
|
) -> Tuple[str, str, str]:
|
|
506
|
-
"""
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
{
|
|
510
|
-
"module": "src/<path>.rs or module path (e.g., src/foo/mod.rs or src/foo/bar.rs)",
|
|
511
|
-
"rust_signature": "pub fn ...",
|
|
512
|
-
"notes": "optional"
|
|
513
|
-
}
|
|
514
|
-
"""
|
|
515
|
-
is_root = self._is_root_symbol(rec)
|
|
516
|
-
system_prompt = (
|
|
517
|
-
"你是资深Rust工程师,擅长为C/C++函数选择合适的Rust模块位置并产出对应的Rust函数签名。\n"
|
|
518
|
-
"目标:根据提供的C源码、调用者上下文与crate目录结构,为该函数选择合适的Rust模块文件并给出Rust函数签名(不实现)。\n"
|
|
519
|
-
"原则:\n"
|
|
520
|
-
"- 按功能内聚与依赖方向选择模块,避免循环依赖;\n"
|
|
521
|
-
"- 模块路径必须落在 crate 的 src/ 下,优先放置到已存在的模块中;必要时可建议创建新的子模块文件;\n"
|
|
522
|
-
"- 函数接口设计应遵循 Rust 最佳实践,不需要兼容 C 的数据类型;优先使用 Rust 原生类型(如 i32/u32/usize、&[T]/&mut [T]、String、Result<T, E> 等),而不是 C 风格类型(如 core::ffi::c_*、libc::c_*);\n"
|
|
523
|
-
"- 禁止使用 extern \"C\";函数应使用标准的 Rust 调用约定,不需要 C ABI;\n"
|
|
524
|
-
"- 参数个数与顺序可以保持与 C 一致,但类型设计应优先考虑 Rust 的惯用法和安全性;\n"
|
|
525
|
-
+ ("- **根符号要求**:此函数是根符号,必须使用 `pub` 关键字对外暴露,确保可以从 crate 外部访问。同时,该函数所在的模块必须在 src/lib.rs 中被导出(使用 `pub mod <模块名>;`)。\n" if is_root else "")
|
|
526
|
-
+ "- **评估是否需要实现**:在规划阶段,请评估此函数是否真的需要实现。以下情况可以跳过实现(设置 skip_implementation 为 true):\n"
|
|
527
|
-
+ " * **已实现的函数**:如果函数已经在目标模块(module)中实现,可以使用 read_code 工具检查目标文件,确认函数已存在且实现正确,则无需重复实现\n"
|
|
528
|
-
+ " * **资源释放类函数**:如文件关闭 fclose、内存释放 free、句柄释放、锁释放等,在 Rust 中通常通过 RAII(Drop trait)自动管理,无需显式实现\n"
|
|
529
|
-
+ " * **已被库替代**:如果函数已被标准库或第三方 crate 替代(lib_replacement 字段已设置),且不需要兼容层,可以跳过实现\n"
|
|
530
|
-
+ " * **空实现或无意义函数**:如果 C 函数本身是空实现、简单返回常量、或仅用于兼容性占位,在 Rust 中可能不需要实现\n"
|
|
531
|
-
+ " * **内联函数或宏**:如果函数在 C 中是内联函数或宏,在 Rust 中可能不需要单独实现\n"
|
|
532
|
-
+ " * **其他不需要实现的情况**:根据具体情况判断,如果函数在 Rust 转译中确实不需要实现,可以跳过\n"
|
|
533
|
-
+ " * 如果设置 skip_implementation 为 true,请在 notes 字段中详细说明原因\n"
|
|
534
|
-
+ "- 仅输出必要信息,避免冗余解释。"
|
|
324
|
+
"""构建模块选择提示词(委托给 PlanningManager)"""
|
|
325
|
+
return self.planning_manager.build_module_selection_prompts(
|
|
326
|
+
rec, c_code, callees_ctx, crate_tree
|
|
535
327
|
)
|
|
536
|
-
# 提取编译参数
|
|
537
|
-
compile_flags = self._extract_compile_flags(rec.file)
|
|
538
|
-
compile_flags_section = ""
|
|
539
|
-
if compile_flags:
|
|
540
|
-
compile_flags_section = "\n".join([
|
|
541
|
-
"",
|
|
542
|
-
"C文件编译参数(来自 compile_commands.json):",
|
|
543
|
-
compile_flags,
|
|
544
|
-
])
|
|
545
|
-
|
|
546
|
-
user_prompt = "\n".join([
|
|
547
|
-
"请阅读以下上下文并准备总结:",
|
|
548
|
-
f"- 函数标识: id={rec.id}, name={rec.name}, qualified={rec.qname}",
|
|
549
|
-
f"- 源文件位置: {rec.file}:{rec.start_line}-{rec.end_line}",
|
|
550
|
-
f"- crate 根目录路径: {self.crate_dir.resolve()}",
|
|
551
|
-
"",
|
|
552
|
-
"C函数源码片段:",
|
|
553
|
-
"<C_SOURCE>",
|
|
554
|
-
c_code,
|
|
555
|
-
"</C_SOURCE>",
|
|
556
|
-
"",
|
|
557
|
-
"符号表签名与参数(只读参考):",
|
|
558
|
-
json.dumps({"signature": getattr(rec, "signature", ""), "params": getattr(rec, "params", None)}, ensure_ascii=False, indent=2),
|
|
559
|
-
"",
|
|
560
|
-
"被引用符号上下文(如已转译则包含Rust模块信息):",
|
|
561
|
-
json.dumps(callees_ctx, ensure_ascii=False, indent=2),
|
|
562
|
-
"",
|
|
563
|
-
"库替代上下文(若存在):",
|
|
564
|
-
json.dumps(getattr(rec, "lib_replacement", None), ensure_ascii=False, indent=2),
|
|
565
|
-
compile_flags_section,
|
|
566
|
-
"",
|
|
567
|
-
*([f"禁用库列表(禁止在实现中使用这些库):{', '.join(self.disabled_libraries)}"] if self.disabled_libraries else []),
|
|
568
|
-
*([""] if self.disabled_libraries else []),
|
|
569
|
-
"当前crate目录结构(部分):",
|
|
570
|
-
"<CRATE_TREE>",
|
|
571
|
-
crate_tree,
|
|
572
|
-
"</CRATE_TREE>",
|
|
573
|
-
"",
|
|
574
|
-
"为避免完整读取体积较大的符号表,你也可以使用工具 read_symbols 按需获取指定符号记录:",
|
|
575
|
-
"- 工具: read_symbols",
|
|
576
|
-
"- 参数示例(JSON):",
|
|
577
|
-
f" {{\"symbols_file\": \"{(self.data_dir / 'symbols.jsonl').resolve()}\", \"symbols\": [\"符号1\", \"符号2\"]}}",
|
|
578
|
-
"",
|
|
579
|
-
"**重要:检查函数是否已实现**",
|
|
580
|
-
"在确定目标模块(module)后,请使用 read_code 工具检查该模块文件,确认函数是否已经实现:",
|
|
581
|
-
"- 工具: read_code",
|
|
582
|
-
"- 参数示例(JSON):",
|
|
583
|
-
" {\"file_path\": \"<目标模块路径>\"}",
|
|
584
|
-
"- 如果函数已经在目标模块中实现,且实现正确,可以设置 skip_implementation 为 true,并在 notes 中说明 \"函数已在目标模块中实现\"",
|
|
585
|
-
"",
|
|
586
|
-
"如果理解完毕,请进入总结阶段。",
|
|
587
|
-
])
|
|
588
|
-
summary_prompt = (
|
|
589
|
-
"请仅输出一个 <SUMMARY> 块,块内必须且只包含一个 JSON 对象,不得包含其它内容。\n"
|
|
590
|
-
"允许字段(JSON 对象):\n"
|
|
591
|
-
'- "module": "<绝对路径>/src/xxx.rs 或 <绝对路径>/src/xxx/mod.rs;或相对路径 src/xxx.rs / src/xxx/mod.rs"\n'
|
|
592
|
-
'- "rust_signature": "pub fn xxx(...)->..."\n'
|
|
593
|
-
'- "skip_implementation": bool // 可选,如果为 true,表示此函数不需要实现,可以直接标记为已实现\n'
|
|
594
|
-
'- "notes": "可选说明(若有上下文缺失或风险点,请在此列出;如果 skip_implementation 为 true,必须在此说明原因)"\n'
|
|
595
|
-
"注意:\n"
|
|
596
|
-
"- module 必须位于 crate 的 src/ 目录下,接受绝对路径或以 src/ 开头的相对路径;尽量选择已有文件;如需新建文件,给出合理路径;\n"
|
|
597
|
-
"- rust_signature 应遵循 Rust 最佳实践,不需要兼容 C 的数据类型;优先使用 Rust 原生类型和惯用法,而不是 C 风格类型。\n"
|
|
598
|
-
"- **评估是否需要实现**:请仔细评估此函数是否真的需要实现。以下情况可以设置 skip_implementation 为 true:\n"
|
|
599
|
-
+ " * **已实现的函数**:如果函数已经在目标模块(module)中实现,可以使用 read_code 工具检查目标文件,确认函数已存在且实现正确,则无需重复实现\n"
|
|
600
|
-
+ " * **资源释放类函数**:如文件关闭 fclose、内存释放 free、句柄释放、锁释放等,在 Rust 中通常通过 RAII(Drop trait)自动管理,无需显式实现\n"
|
|
601
|
-
+ " * **已被库替代**:如果函数已被标准库或第三方 crate 替代(lib_replacement 字段已设置),且不需要兼容层,可以跳过实现\n"
|
|
602
|
-
+ " * **空实现或无意义函数**:如果 C 函数本身是空实现、简单返回常量、或仅用于兼容性占位,在 Rust 中可能不需要实现\n"
|
|
603
|
-
+ " * **内联函数或宏**:如果函数在 C 中是内联函数或宏,在 Rust 中可能不需要单独实现\n"
|
|
604
|
-
+ " * **其他不需要实现的情况**:根据具体情况判断,如果函数在 Rust 转译中确实不需要实现,可以跳过\n"
|
|
605
|
-
+ " * **重要**:如果设置 skip_implementation 为 true,必须在 notes 字段中详细说明原因,例如:\n"
|
|
606
|
-
+ " - \"函数已在目标模块中实现\"\n"
|
|
607
|
-
+ " - \"通过 RAII 自动管理,无需显式实现\"\n"
|
|
608
|
-
+ " - \"已被标准库 std::xxx 替代,无需实现\"\n"
|
|
609
|
-
+ " - \"空实现函数,在 Rust 中不需要\"\n"
|
|
610
|
-
+ " - \"内联函数,已在调用处展开,无需单独实现\"\n"
|
|
611
|
-
+ "- 如果函数确实需要实现,则不要设置 skip_implementation 或设置为 false\n"
|
|
612
|
-
"- 类型设计原则:\n"
|
|
613
|
-
" * 基本类型:优先使用 i32/u32/i64/u64/isize/usize/f32/f64 等原生 Rust 类型,而不是 core::ffi::c_* 或 libc::c_*;\n"
|
|
614
|
-
" * 指针/引用:优先使用引用 &T/&mut T 或切片 &[T]/&mut [T],而非原始指针 *const T/*mut T;仅在必要时使用原始指针;\n"
|
|
615
|
-
" * 字符串:优先使用 String、&str 而非 *const c_char/*mut c_char;\n"
|
|
616
|
-
" * 错误处理:考虑使用 Result<T, E> 而非 C 风格的错误码;\n"
|
|
617
|
-
" * 参数个数与顺序可以保持与 C 一致,但类型应优先考虑 Rust 的惯用法、安全性和可读性;\n"
|
|
618
|
-
+ ("- **根符号要求**:此函数是根符号,rust_signature 必须包含 `pub` 关键字,确保可以从 crate 外部访问。同时,该函数所在的模块必须在 src/lib.rs 中被导出(使用 `pub mod <模块名>;`)。\n" if is_root else "")
|
|
619
|
-
+ "- 函数签名应包含可见性修饰(pub)与函数名;类型应为 Rust 最佳实践的选择,而非简单映射 C 类型。\n"
|
|
620
|
-
"- 禁止使用 extern \"C\";函数应使用标准的 Rust 调用约定,不需要 C ABI。\n"
|
|
621
|
-
"请严格按以下格式输出(JSON格式,支持jsonnet语法如尾随逗号、注释、|||分隔符多行字符串等):\n"
|
|
622
|
-
"示例1(正常函数):\n"
|
|
623
|
-
"<SUMMARY>\n{\n \"module\": \"...\",\n \"rust_signature\": \"...\",\n \"notes\": \"...\"\n}\n</SUMMARY>\n"
|
|
624
|
-
"示例2(已实现的函数,可跳过实现):\n"
|
|
625
|
-
"<SUMMARY>\n{\n \"module\": \"...\",\n \"rust_signature\": \"...\",\n \"skip_implementation\": true,\n \"notes\": \"函数已在目标模块中实现\"\n}\n</SUMMARY>\n"
|
|
626
|
-
"示例3(不需要实现的函数,可跳过实现):\n"
|
|
627
|
-
"<SUMMARY>\n{\n \"module\": \"...\",\n \"rust_signature\": \"...\",\n \"skip_implementation\": true,\n \"notes\": \"通过 RAII 自动管理,无需显式实现\"\n}\n</SUMMARY>\n"
|
|
628
|
-
"示例4(已被库替代,可跳过实现):\n"
|
|
629
|
-
"<SUMMARY>\n{\n \"module\": \"...\",\n \"rust_signature\": \"...\",\n \"skip_implementation\": true,\n \"notes\": \"已被标准库 std::xxx 替代,无需实现\"\n}\n</SUMMARY>\n"
|
|
630
|
-
"示例5(空实现函数,可跳过实现):\n"
|
|
631
|
-
"<SUMMARY>\n{\n \"module\": \"...\",\n \"rust_signature\": \"...\",\n \"skip_implementation\": true,\n \"notes\": \"C 函数为空实现,在 Rust 中不需要\"\n}\n</SUMMARY>"
|
|
632
|
-
)
|
|
633
|
-
# 在 user_prompt 和 summary_prompt 中追加附加说明(system_prompt 通常不需要)
|
|
634
|
-
user_prompt = self._append_additional_notes(user_prompt)
|
|
635
|
-
summary_prompt = self._append_additional_notes(summary_prompt)
|
|
636
|
-
return system_prompt, user_prompt, summary_prompt
|
|
637
|
-
|
|
638
|
-
def _plan_module_and_signature(self, rec: FnRecord, c_code: str) -> Tuple[str, str, bool]:
|
|
639
|
-
"""调用 Agent 选择模块与签名,返回 (module_path, rust_signature, skip_implementation),若格式不满足将自动重试直到满足"""
|
|
640
|
-
crate_tree = dir_tree(self.crate_dir)
|
|
641
|
-
callees_ctx = self._collect_callees_context(rec)
|
|
642
|
-
sys_p, usr_p, base_sum_p = self._build_module_selection_prompts(rec, c_code, callees_ctx, crate_tree)
|
|
643
|
-
|
|
644
|
-
def _validate(meta: Any) -> Tuple[bool, str]:
|
|
645
|
-
"""基本格式检查,仅验证字段存在性,不做硬编码规则校验"""
|
|
646
|
-
if not isinstance(meta, dict) or not meta:
|
|
647
|
-
return False, "未解析到有效的 <SUMMARY> 中的 JSON 对象"
|
|
648
|
-
module = meta.get("module")
|
|
649
|
-
rust_sig = meta.get("rust_signature")
|
|
650
|
-
if not isinstance(module, str) or not module.strip():
|
|
651
|
-
return False, "缺少必填字段 module"
|
|
652
|
-
if not isinstance(rust_sig, str) or not rust_sig.strip():
|
|
653
|
-
return False, "缺少必填字段 rust_signature"
|
|
654
|
-
# 路径归一化:容忍相对/简略路径,最终归一为 crate_dir 下的绝对路径(不做硬编码校验)
|
|
655
|
-
try:
|
|
656
|
-
raw = str(module).strip().replace("\\", "/")
|
|
657
|
-
crate_root = self.crate_dir.resolve()
|
|
658
|
-
mp: Path
|
|
659
|
-
p = Path(raw)
|
|
660
|
-
if p.is_absolute():
|
|
661
|
-
mp = p.resolve()
|
|
662
|
-
else:
|
|
663
|
-
# 规范化相对路径:若不以 src/ 开头,自动补全为 src/<raw>
|
|
664
|
-
if raw.startswith("./"):
|
|
665
|
-
raw = raw[2:]
|
|
666
|
-
if not raw.startswith("src/"):
|
|
667
|
-
raw = f"src/{raw}"
|
|
668
|
-
mp = (crate_root / raw).resolve()
|
|
669
|
-
# 将归一化后的绝对路径回写到 meta,避免后续流程二次解析歧义
|
|
670
|
-
meta["module"] = str(mp)
|
|
671
|
-
except Exception:
|
|
672
|
-
# 路径归一化失败不影响,保留原始值
|
|
673
|
-
pass
|
|
674
|
-
return True, ""
|
|
675
|
-
|
|
676
|
-
def _retry_sum_prompt(reason: str) -> str:
|
|
677
|
-
return (
|
|
678
|
-
base_sum_p
|
|
679
|
-
+ "\n\n[格式检查失败,必须重试]\n"
|
|
680
|
-
+ f"- 失败原因:{reason}\n"
|
|
681
|
-
+ "- 仅输出一个 <SUMMARY> 块;块内直接包含 JSON 对象(不需要额外的标签);\n"
|
|
682
|
-
+ '- JSON 对象必须包含字段:module、rust_signature。\n'
|
|
683
|
-
)
|
|
684
|
-
|
|
685
|
-
attempt = 0
|
|
686
|
-
last_reason = "未知错误"
|
|
687
|
-
plan_max_retries_val = getattr(self, "plan_max_retries", 0)
|
|
688
|
-
# 如果 plan_max_retries 为 0,表示无限重试
|
|
689
|
-
use_direct_model = False # 标记是否使用直接模型调用
|
|
690
|
-
agent = None # 在循环外声明,以便重试时复用
|
|
691
|
-
|
|
692
|
-
# 注意:Agent 必须在 crate 根目录下创建,以确保工具(如 read_symbols)能正确获取符号表
|
|
693
|
-
# 由于 transpile() 开始时已切换到 crate 目录,此处无需再次切换
|
|
694
|
-
while plan_max_retries_val == 0 or attempt < plan_max_retries_val:
|
|
695
|
-
attempt += 1
|
|
696
|
-
sum_p = base_sum_p if attempt == 1 else _retry_sum_prompt(last_reason)
|
|
697
|
-
|
|
698
|
-
# 第一次创建 Agent,后续重试时复用(如果使用直接模型调用)
|
|
699
|
-
# 注意:Agent 必须在 crate 根目录下创建,以确保工具(如 read_symbols)能正确获取符号表
|
|
700
|
-
if agent is None or not use_direct_model:
|
|
701
|
-
# 获取函数信息用于 Agent name
|
|
702
|
-
fn_name = rec.qname or rec.name or f"fn_{rec.id}"
|
|
703
|
-
agent_name = f"C2Rust-Function-Planner({fn_name})"
|
|
704
|
-
agent = Agent(
|
|
705
|
-
system_prompt=sys_p,
|
|
706
|
-
name=agent_name,
|
|
707
|
-
model_group=self.llm_group,
|
|
708
|
-
summary_prompt=sum_p,
|
|
709
|
-
need_summary=True,
|
|
710
|
-
auto_complete=True,
|
|
711
|
-
use_tools=["execute_script", "read_code", "read_symbols"],
|
|
712
|
-
non_interactive=self.non_interactive,
|
|
713
|
-
use_methodology=False,
|
|
714
|
-
use_analysis=False,
|
|
715
|
-
)
|
|
716
|
-
# 订阅 BEFORE_TOOL_CALL 和 AFTER_TOOL_CALL 事件,用于细粒度检测测试代码删除
|
|
717
|
-
agent.event_bus.subscribe(BEFORE_TOOL_CALL, self._on_before_tool_call)
|
|
718
|
-
agent.event_bus.subscribe(AFTER_TOOL_CALL, self._on_after_tool_call)
|
|
719
|
-
# 记录 Agent 创建时的 commit id(作为初始值)
|
|
720
|
-
agent_id = id(agent)
|
|
721
|
-
agent_key = f"agent_{agent_id}"
|
|
722
|
-
initial_commit = self._get_crate_commit_hash()
|
|
723
|
-
if initial_commit:
|
|
724
|
-
self._agent_before_commits[agent_key] = initial_commit
|
|
725
|
-
|
|
726
|
-
if use_direct_model:
|
|
727
|
-
# 格式校验失败后,直接调用模型接口
|
|
728
|
-
# 构造包含摘要提示词和具体错误信息的完整提示
|
|
729
|
-
error_guidance = ""
|
|
730
|
-
if last_reason and last_reason != "未知错误":
|
|
731
|
-
if "JSON解析失败" in last_reason:
|
|
732
|
-
error_guidance = f"\n\n**格式错误详情(请根据以下错误修复输出格式):**\n- {last_reason}\n\n请确保输出的JSON格式正确,包括正确的引号、逗号、大括号等。JSON 对象必须包含字段:module(字符串)、rust_signature(字符串)。支持jsonnet语法(如尾随逗号、注释、||| 或 ``` 分隔符多行字符串等)。"
|
|
733
|
-
else:
|
|
734
|
-
error_guidance = f"\n\n**格式错误详情(请根据以下错误修复输出格式):**\n- {last_reason}\n\n请确保输出格式正确:仅输出一个 <SUMMARY> 块,块内直接包含 JSON 对象(不需要额外的标签);JSON 对象必须包含字段:module(字符串)、rust_signature(字符串)。支持jsonnet语法(如尾随逗号、注释、||| 或 ``` 分隔符多行字符串等)。"
|
|
735
|
-
|
|
736
|
-
full_prompt = f"{usr_p}{error_guidance}\n\n{sum_p}"
|
|
737
|
-
try:
|
|
738
|
-
response = agent.model.chat_until_success(full_prompt) # type: ignore
|
|
739
|
-
summary = response
|
|
740
|
-
except Exception as e:
|
|
741
|
-
typer.secho(f"[c2rust-transpiler][plan] 直接模型调用失败: {e},回退到 run()", fg=typer.colors.YELLOW)
|
|
742
|
-
summary = agent.run(usr_p)
|
|
743
|
-
else:
|
|
744
|
-
# 第一次使用 run(),让 Agent 完整运行(可能使用工具)
|
|
745
|
-
summary = agent.run(usr_p)
|
|
746
|
-
|
|
747
|
-
meta, parse_error = extract_json_from_summary(str(summary or ""))
|
|
748
|
-
if parse_error:
|
|
749
|
-
# JSON解析失败,将错误信息反馈给模型
|
|
750
|
-
typer.secho(f"[c2rust-transpiler][plan] JSON解析失败: {parse_error}", fg=typer.colors.YELLOW)
|
|
751
|
-
last_reason = f"JSON解析失败: {parse_error}"
|
|
752
|
-
use_direct_model = True
|
|
753
|
-
# 解析失败,继续重试
|
|
754
|
-
continue
|
|
755
|
-
else:
|
|
756
|
-
ok, reason = _validate(meta)
|
|
757
|
-
if ok:
|
|
758
|
-
module = str(meta.get("module") or "").strip()
|
|
759
|
-
rust_sig = str(meta.get("rust_signature") or "").strip()
|
|
760
|
-
skip_impl = bool(meta.get("skip_implementation") is True)
|
|
761
|
-
if skip_impl:
|
|
762
|
-
notes = str(meta.get("notes") or "")
|
|
763
|
-
typer.secho(f"[c2rust-transpiler][plan] 第 {attempt} 次尝试成功: 模块={module}, 签名={rust_sig}, 跳过实现={skip_impl}", fg=typer.colors.GREEN)
|
|
764
|
-
if notes:
|
|
765
|
-
typer.secho(f"[c2rust-transpiler][plan] 跳过实现原因: {notes}", fg=typer.colors.CYAN)
|
|
766
|
-
else:
|
|
767
|
-
typer.secho(f"[c2rust-transpiler][plan] 第 {attempt} 次尝试成功: 模块={module}, 签名={rust_sig}", fg=typer.colors.GREEN)
|
|
768
|
-
return module, rust_sig, skip_impl
|
|
769
|
-
else:
|
|
770
|
-
typer.secho(f"[c2rust-transpiler][plan] 第 {attempt} 次尝试失败: {reason}", fg=typer.colors.YELLOW)
|
|
771
|
-
last_reason = reason
|
|
772
|
-
# 格式校验失败,后续重试使用直接模型调用
|
|
773
|
-
use_direct_model = True
|
|
774
|
-
|
|
775
|
-
# 规划超出重试上限:回退到兜底方案(默认模块 src/ffi.rs + 简单占位签名)
|
|
776
|
-
# 注意:如果 plan_max_retries_val == 0(无限重试),理论上不应该到达这里
|
|
777
|
-
try:
|
|
778
|
-
crate_root = self.crate_dir.resolve()
|
|
779
|
-
fallback_module = str((crate_root / "src" / "ffi.rs").resolve())
|
|
780
|
-
except Exception:
|
|
781
|
-
fallback_module = "src/ffi.rs"
|
|
782
|
-
fallback_sig = f"pub fn {rec.name or ('fn_' + str(rec.id))}()"
|
|
783
|
-
typer.secho(f"[c2rust-transpiler][plan] 超出规划重试上限({plan_max_retries_val if plan_max_retries_val > 0 else '无限'}),回退到兜底: module={fallback_module}, signature={fallback_sig}", fg=typer.colors.YELLOW)
|
|
784
|
-
return fallback_module, fallback_sig, False
|
|
785
|
-
|
|
786
|
-
def _update_progress_current(self, rec: FnRecord, module: str, rust_sig: str) -> None:
|
|
787
|
-
self.progress["current"] = {
|
|
788
|
-
"time": time.strftime("%Y-%m-%dT%H:%M:%S", time.localtime()),
|
|
789
|
-
"id": rec.id,
|
|
790
|
-
"name": rec.name,
|
|
791
|
-
"qualified_name": rec.qname,
|
|
792
|
-
"file": rec.file,
|
|
793
|
-
"start_line": rec.start_line,
|
|
794
|
-
"end_line": rec.end_line,
|
|
795
|
-
"module": module,
|
|
796
|
-
"rust_signature": rust_sig,
|
|
797
|
-
}
|
|
798
|
-
self._save_progress()
|
|
799
328
|
|
|
329
|
+
def _plan_module_and_signature(
|
|
330
|
+
self, rec: FnRecord, c_code: str
|
|
331
|
+
) -> Tuple[str, str, bool]:
|
|
332
|
+
"""调用 Agent 选择模块与签名(委托给 PlanningManager)"""
|
|
333
|
+
return self.planning_manager.plan_module_and_signature(rec, c_code)
|
|
800
334
|
|
|
335
|
+
def _update_progress_current(
|
|
336
|
+
self, rec: FnRecord, module: str, rust_sig: str
|
|
337
|
+
) -> None:
|
|
338
|
+
"""更新当前进度(委托给 AgentManager)"""
|
|
339
|
+
self.agent_manager.update_progress_current(
|
|
340
|
+
rec, module, rust_sig, self.progress, self._save_progress
|
|
341
|
+
)
|
|
801
342
|
|
|
802
343
|
# ========= Agent 复用与上下文拼接辅助 =========
|
|
803
344
|
|
|
804
|
-
def _compose_prompt_with_context(self, prompt: str) -> str:
|
|
805
|
-
"""
|
|
806
|
-
|
|
807
|
-
策略:
|
|
808
|
-
- 每个函数生命周期内,首次调用拼接“全量头部”;
|
|
809
|
-
- 后续调用仅拼接“精简头部”;
|
|
810
|
-
- 如头部缺失则直接返回原提示。
|
|
811
|
-
"""
|
|
812
|
-
# 首次发送全量上下文
|
|
813
|
-
if (not getattr(self, "_current_context_full_sent", False)) and getattr(self, "_current_context_full_header", ""):
|
|
814
|
-
self._current_context_full_sent = True
|
|
815
|
-
return self._current_context_full_header + "\n\n" + prompt
|
|
816
|
-
# 后续拼接精简上下文
|
|
817
|
-
compact = getattr(self, "_current_context_compact_header", "")
|
|
818
|
-
if compact:
|
|
819
|
-
return compact + "\n\n" + prompt
|
|
820
|
-
return prompt
|
|
821
|
-
|
|
822
|
-
def _reset_function_context(self, rec: FnRecord, module: str, rust_sig: str, c_code: str) -> None:
|
|
823
|
-
"""
|
|
824
|
-
初始化当前函数的上下文与复用Agent缓存。
|
|
825
|
-
在单个函数实现开始时调用一次,之后复用代码编写与修复Agent/Review等Agent。
|
|
826
|
-
"""
|
|
827
|
-
self._current_agents = {}
|
|
828
|
-
self._current_function_id = rec.id
|
|
829
|
-
|
|
830
|
-
# 汇总上下文头部,供后续复用时拼接
|
|
831
|
-
callees_ctx = self._collect_callees_context(rec)
|
|
832
|
-
crate_tree = dir_tree(self.crate_dir)
|
|
833
|
-
librep_ctx = rec.lib_replacement if isinstance(rec.lib_replacement, dict) else None
|
|
834
|
-
# 提取编译参数
|
|
835
|
-
compile_flags = self._extract_compile_flags(rec.file)
|
|
836
|
-
|
|
837
|
-
header_lines = [
|
|
838
|
-
"【当前函数上下文(复用Agent专用)】",
|
|
839
|
-
f"- 函数: {rec.qname or rec.name} (id={rec.id})",
|
|
840
|
-
f"- 源位置: {rec.file}:{rec.start_line}-{rec.end_line}",
|
|
841
|
-
f"- 原 C 工程目录: {self.project_root.resolve()}",
|
|
842
|
-
f"- 目标模块: {module}",
|
|
843
|
-
f"- 建议/当前签名: {rust_sig}",
|
|
844
|
-
f"- crate 根目录: {self.crate_dir.resolve()}",
|
|
845
|
-
"",
|
|
846
|
-
"原始C函数源码片段(只读参考):",
|
|
847
|
-
"<C_SOURCE>",
|
|
848
|
-
c_code or "",
|
|
849
|
-
"</C_SOURCE>",
|
|
850
|
-
"",
|
|
851
|
-
"被引用符号上下文:",
|
|
852
|
-
json.dumps(callees_ctx, ensure_ascii=False, indent=2),
|
|
853
|
-
"",
|
|
854
|
-
"库替代上下文(若有):",
|
|
855
|
-
json.dumps(librep_ctx, ensure_ascii=False, indent=2),
|
|
856
|
-
]
|
|
857
|
-
# 添加编译参数(如果存在)
|
|
858
|
-
if compile_flags:
|
|
859
|
-
header_lines.extend([
|
|
860
|
-
"",
|
|
861
|
-
"C文件编译参数(来自 compile_commands.json):",
|
|
862
|
-
compile_flags,
|
|
863
|
-
])
|
|
864
|
-
header_lines.extend([
|
|
865
|
-
"",
|
|
866
|
-
"crate 目录结构(部分):",
|
|
867
|
-
"<CRATE_TREE>",
|
|
868
|
-
crate_tree,
|
|
869
|
-
"</CRATE_TREE>",
|
|
870
|
-
])
|
|
871
|
-
# 精简头部(后续复用)
|
|
872
|
-
compact_lines = [
|
|
873
|
-
"【函数上下文简要(复用)】",
|
|
874
|
-
f"- 函数: {rec.qname or rec.name} (id={rec.id})",
|
|
875
|
-
f"- 原 C 工程目录: {self.project_root.resolve()}",
|
|
876
|
-
f"- 模块: {module}",
|
|
877
|
-
f"- 签名: {rust_sig}",
|
|
878
|
-
f"- crate: {self.crate_dir.resolve()}",
|
|
879
|
-
]
|
|
880
|
-
self._current_context_full_header = "\n".join(header_lines)
|
|
881
|
-
self._current_context_compact_header = "\n".join(compact_lines)
|
|
882
|
-
self._current_context_full_sent = False
|
|
345
|
+
def _compose_prompt_with_context(self, prompt: str, for_fix: bool = False) -> str:
|
|
346
|
+
"""在复用Agent时,将此前构建的函数上下文头部拼接到当前提示词前(委托给 AgentManager)"""
|
|
347
|
+
return self.agent_manager.compose_prompt_with_context(prompt, for_fix)
|
|
883
348
|
|
|
349
|
+
def _reset_function_context(
|
|
350
|
+
self, rec: FnRecord, module: str, rust_sig: str, c_code: str
|
|
351
|
+
) -> None:
|
|
352
|
+
"""初始化当前函数的上下文与复用Agent缓存(委托给 AgentManager)"""
|
|
353
|
+
# 设置当前函数 ID,以便 AgentManager 可以访问
|
|
354
|
+
self.agent_manager._current_function_id = rec.id
|
|
355
|
+
self.agent_manager.reset_function_context(
|
|
356
|
+
rec,
|
|
357
|
+
module,
|
|
358
|
+
rust_sig,
|
|
359
|
+
c_code,
|
|
360
|
+
self._collect_callees_context,
|
|
361
|
+
self._extract_compile_flags,
|
|
362
|
+
)
|
|
884
363
|
|
|
885
364
|
def _on_before_tool_call(self, agent: Any, current_response=None, **kwargs) -> None:
|
|
886
|
-
"""
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
self._agent_before_commits[agent_key] = current_commit
|
|
902
|
-
except Exception as e:
|
|
903
|
-
# 事件处理器异常不应影响主流程
|
|
904
|
-
typer.secho(f"[c2rust-transpiler][test-detection] BEFORE_TOOL_CALL 事件处理器异常: {e}", fg=typer.colors.YELLOW)
|
|
905
|
-
|
|
906
|
-
def _on_after_tool_call(self, agent: Any, current_response=None, need_return=None, tool_prompt=None, **kwargs) -> None:
|
|
907
|
-
"""
|
|
908
|
-
工具调用后的事件处理器,用于细粒度检测测试代码删除。
|
|
909
|
-
|
|
910
|
-
在每次工具调用后立即检测,如果检测到测试代码被错误删除,立即回退。
|
|
911
|
-
"""
|
|
912
|
-
try:
|
|
913
|
-
# 只检测编辑文件的工具调用
|
|
914
|
-
last_tool = agent.get_user_data("__last_executed_tool__") if hasattr(agent, "get_user_data") else None
|
|
915
|
-
if not last_tool:
|
|
916
|
-
return
|
|
917
|
-
|
|
918
|
-
# 只关注可能修改代码的工具
|
|
919
|
-
edit_tools = {"edit_file", "rewrite_file", "apply_patch"}
|
|
920
|
-
if last_tool not in edit_tools:
|
|
921
|
-
return
|
|
922
|
-
|
|
923
|
-
# 获取该 Agent 对应的工具调用前的 commit id
|
|
924
|
-
agent_id = id(agent)
|
|
925
|
-
agent_key = f"agent_{agent_id}"
|
|
926
|
-
before_commit = self._agent_before_commits.get(agent_key)
|
|
927
|
-
|
|
928
|
-
# 如果没有 commit 信息,无法检测
|
|
929
|
-
if not before_commit:
|
|
930
|
-
return
|
|
931
|
-
|
|
932
|
-
# 检测测试代码删除
|
|
933
|
-
from jarvis.jarvis_c2rust.utils import detect_test_deletion, ask_llm_about_test_deletion
|
|
934
|
-
|
|
935
|
-
detection_result = detect_test_deletion("[c2rust-transpiler]")
|
|
936
|
-
if not detection_result:
|
|
937
|
-
# 没有检测到删除,更新 commit 记录
|
|
938
|
-
current_commit = self._get_crate_commit_hash()
|
|
939
|
-
if current_commit and current_commit != before_commit:
|
|
940
|
-
self._agent_before_commits[agent_key] = current_commit
|
|
941
|
-
return
|
|
942
|
-
|
|
943
|
-
typer.secho("[c2rust-transpiler][test-detection] 检测到可能错误删除了测试代码标记(工具调用后检测)", fg=typer.colors.YELLOW)
|
|
944
|
-
|
|
945
|
-
# 询问 LLM 是否合理
|
|
946
|
-
need_reset = ask_llm_about_test_deletion(detection_result, agent, "[c2rust-transpiler]")
|
|
947
|
-
|
|
948
|
-
if need_reset:
|
|
949
|
-
typer.secho(f"[c2rust-transpiler][test-detection] LLM 确认删除不合理,正在回退到 commit: {before_commit}", fg=typer.colors.RED)
|
|
950
|
-
if self._reset_to_commit(before_commit):
|
|
951
|
-
typer.secho("[c2rust-transpiler][test-detection] 已回退到之前的 commit(工具调用后检测)", fg=typer.colors.GREEN)
|
|
952
|
-
# 回退后,保持之前的 commit 记录
|
|
953
|
-
self._agent_before_commits[agent_key] = before_commit
|
|
954
|
-
# 在 agent 的 session 中添加提示,告知修改被撤销
|
|
955
|
-
if hasattr(agent, "session") and hasattr(agent.session, "prompt"):
|
|
956
|
-
agent.session.prompt += "\n\n⚠️ 修改被撤销:检测到测试代码被错误删除,已回退到之前的版本。\n"
|
|
957
|
-
else:
|
|
958
|
-
typer.secho("[c2rust-transpiler][test-detection] 回退失败", fg=typer.colors.RED)
|
|
959
|
-
else:
|
|
960
|
-
# LLM 认为删除合理,更新 commit 记录
|
|
961
|
-
current_commit = self._get_crate_commit_hash()
|
|
962
|
-
if current_commit and current_commit != before_commit:
|
|
963
|
-
self._agent_before_commits[agent_key] = current_commit
|
|
964
|
-
except Exception as e:
|
|
965
|
-
# 事件处理器异常不应影响主流程
|
|
966
|
-
typer.secho(f"[c2rust-transpiler][test-detection] AFTER_TOOL_CALL 事件处理器异常: {e}", fg=typer.colors.YELLOW)
|
|
365
|
+
"""工具调用前的事件处理器(委托给 AgentManager)"""
|
|
366
|
+
return self.agent_manager.on_before_tool_call(agent, current_response, **kwargs)
|
|
367
|
+
|
|
368
|
+
def _on_after_tool_call(
|
|
369
|
+
self,
|
|
370
|
+
agent: Any,
|
|
371
|
+
current_response=None,
|
|
372
|
+
need_return=None,
|
|
373
|
+
tool_prompt=None,
|
|
374
|
+
**kwargs,
|
|
375
|
+
) -> None:
|
|
376
|
+
"""工具调用后的事件处理器(委托给 AgentManager)"""
|
|
377
|
+
return self.agent_manager.on_after_tool_call(
|
|
378
|
+
agent, current_response, need_return, tool_prompt, **kwargs
|
|
379
|
+
)
|
|
967
380
|
|
|
968
381
|
def _get_code_agent(self) -> CodeAgent:
|
|
969
|
-
"""
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
rec = self.fn_index_by_id.get(fid)
|
|
986
|
-
if rec:
|
|
987
|
-
fn_name = rec.qname or rec.name or f"fn_{fid}"
|
|
988
|
-
agent_name = "C2Rust-CodeAgent" + (f"({fn_name})" if fn_name else "")
|
|
989
|
-
agent = CodeAgent(
|
|
990
|
-
name=agent_name,
|
|
991
|
-
need_summary=False,
|
|
992
|
-
non_interactive=self.non_interactive,
|
|
993
|
-
model_group=self.llm_group,
|
|
994
|
-
append_tools="read_symbols,methodology",
|
|
995
|
-
use_methodology=True,
|
|
996
|
-
use_analysis=True,
|
|
997
|
-
force_save_memory=False,
|
|
998
|
-
)
|
|
999
|
-
# 订阅 BEFORE_TOOL_CALL 和 AFTER_TOOL_CALL 事件,用于细粒度检测测试代码删除
|
|
1000
|
-
agent.event_bus.subscribe(BEFORE_TOOL_CALL, self._on_before_tool_call)
|
|
1001
|
-
agent.event_bus.subscribe(AFTER_TOOL_CALL, self._on_after_tool_call)
|
|
1002
|
-
# 记录 Agent 创建时的 commit id(作为初始值)
|
|
1003
|
-
agent_id = id(agent)
|
|
1004
|
-
agent_key = f"agent_{agent_id}"
|
|
1005
|
-
initial_commit = self._get_crate_commit_hash()
|
|
1006
|
-
if initial_commit:
|
|
1007
|
-
self._agent_before_commits[agent_key] = initial_commit
|
|
1008
|
-
self._current_agents[key] = agent
|
|
1009
|
-
return agent
|
|
1010
|
-
|
|
1011
|
-
def _refresh_compact_context(self, rec: FnRecord, module: str, rust_sig: str) -> None:
|
|
1012
|
-
"""
|
|
1013
|
-
刷新精简上下文头部(在 sig-fix/ensure-impl 后调用,保证后续提示一致)。
|
|
1014
|
-
仅更新精简头部,不影响已发送的全量头部。
|
|
1015
|
-
"""
|
|
1016
|
-
try:
|
|
1017
|
-
compact_lines = [
|
|
1018
|
-
"【函数上下文简要(复用)】",
|
|
1019
|
-
f"- 函数: {rec.qname or rec.name} (id={rec.id})",
|
|
1020
|
-
f"- 模块: {module}",
|
|
1021
|
-
f"- 签名: {rust_sig}",
|
|
1022
|
-
f"- crate: {self.crate_dir.resolve()}",
|
|
1023
|
-
]
|
|
1024
|
-
self._current_context_compact_header = "\n".join(compact_lines)
|
|
1025
|
-
except Exception:
|
|
1026
|
-
pass
|
|
382
|
+
"""获取代码生成/修复Agent(委托给 AgentManager,保持向后兼容)"""
|
|
383
|
+
return self.agent_manager.get_code_agent()
|
|
384
|
+
|
|
385
|
+
def _get_generation_agent(self) -> CodeAgent:
|
|
386
|
+
"""获取代码生成Agent(委托给 AgentManager)"""
|
|
387
|
+
return self.agent_manager.get_generation_agent()
|
|
388
|
+
|
|
389
|
+
def _get_fix_agent(self, c_code: Optional[str] = None) -> CodeAgent:
|
|
390
|
+
"""获取修复Agent(委托给 AgentManager,每次重新创建)"""
|
|
391
|
+
return self.agent_manager.get_fix_agent(c_code)
|
|
392
|
+
|
|
393
|
+
def _refresh_compact_context(
|
|
394
|
+
self, rec: FnRecord, module: str, rust_sig: str
|
|
395
|
+
) -> None:
|
|
396
|
+
"""刷新精简上下文头部(委托给 AgentManager)"""
|
|
397
|
+
self.agent_manager.refresh_compact_context(rec, module, rust_sig)
|
|
1027
398
|
|
|
1028
399
|
# ========= 代码生成与修复 =========
|
|
1029
400
|
|
|
@@ -1035,459 +406,110 @@ class Transpiler:
|
|
|
1035
406
|
return (rec.name in self.root_symbols) or (rec.qname in self.root_symbols)
|
|
1036
407
|
|
|
1037
408
|
def _build_generate_impl_prompt(
|
|
1038
|
-
self,
|
|
409
|
+
self,
|
|
410
|
+
rec: FnRecord,
|
|
411
|
+
c_code: str,
|
|
412
|
+
module: str,
|
|
413
|
+
rust_sig: str,
|
|
414
|
+
unresolved: List[str],
|
|
1039
415
|
) -> str:
|
|
1040
|
-
"""
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
"3. Refactor:优化代码,保持测试通过",
|
|
1058
|
-
"",
|
|
1059
|
-
"【核心要求】",
|
|
1060
|
-
"- 先写测试再写实现,测试必须可编译通过",
|
|
1061
|
-
"- 禁止使用 todo!/unimplemented!,必须实现完整功能",
|
|
1062
|
-
"- 使用 Rust 原生类型(i32/u32、&str/String、&[T]/&mut [T]、Result<T,E>),避免 C 风格类型",
|
|
1063
|
-
"- 禁止使用 extern \"C\",使用标准 Rust 调用约定",
|
|
1064
|
-
"- 保持最小变更,避免无关重构",
|
|
1065
|
-
"- 注释使用中文,禁止 use ...::* 通配导入",
|
|
1066
|
-
"- 资源释放类函数(fclose/free 等)可通过 RAII 自动管理,提供空实现并在文档中说明",
|
|
1067
|
-
*([f"- 禁用库:{', '.join(self.disabled_libraries)}"] if self.disabled_libraries else []),
|
|
1068
|
-
"",
|
|
1069
|
-
"【依赖处理】",
|
|
1070
|
-
"- 检查依赖函数是否已实现,未实现的需一并补齐(遵循 TDD:先测试后实现)",
|
|
1071
|
-
"- 使用 read_symbols/read_code 获取 C 源码",
|
|
1072
|
-
"- 优先处理底层依赖,确保所有测试通过",
|
|
1073
|
-
"",
|
|
1074
|
-
"【工具】",
|
|
1075
|
-
f"- read_symbols: {{\"symbols_file\": \"{symbols_path}\", \"symbols\": [...]}}",
|
|
1076
|
-
"- read_code: 读取 C 源码或 Rust 模块",
|
|
1077
|
-
"",
|
|
1078
|
-
*([f"未转换符号:{', '.join(unresolved)}"] if unresolved else []),
|
|
1079
|
-
"",
|
|
1080
|
-
"C 源码:",
|
|
1081
|
-
"<C_SOURCE>",
|
|
1082
|
-
c_code,
|
|
1083
|
-
"</C_SOURCE>",
|
|
1084
|
-
"",
|
|
1085
|
-
"签名参考:",
|
|
1086
|
-
json.dumps({"signature": getattr(rec, "signature", ""), "params": getattr(rec, "params", None)}, ensure_ascii=False, indent=2),
|
|
1087
|
-
"",
|
|
1088
|
-
"仅输出补丁,不要解释。",
|
|
1089
|
-
]
|
|
1090
|
-
# 若存在库替代上下文,则附加到实现提示中,便于生成器参考(多库组合、参考API、备注等)
|
|
1091
|
-
librep_ctx = None
|
|
1092
|
-
try:
|
|
1093
|
-
librep_ctx = getattr(rec, "lib_replacement", None)
|
|
1094
|
-
except Exception:
|
|
1095
|
-
librep_ctx = None
|
|
1096
|
-
if isinstance(librep_ctx, dict) and librep_ctx:
|
|
1097
|
-
requirement_lines.extend([
|
|
1098
|
-
"",
|
|
1099
|
-
"库替代上下文(若存在):",
|
|
1100
|
-
json.dumps(librep_ctx, ensure_ascii=False, indent=2),
|
|
1101
|
-
"",
|
|
1102
|
-
])
|
|
1103
|
-
# 添加编译参数(如果存在)
|
|
1104
|
-
compile_flags = self._extract_compile_flags(rec.file)
|
|
1105
|
-
if compile_flags:
|
|
1106
|
-
requirement_lines.extend([
|
|
1107
|
-
"",
|
|
1108
|
-
"C文件编译参数(来自 compile_commands.json):",
|
|
1109
|
-
compile_flags,
|
|
1110
|
-
"",
|
|
1111
|
-
])
|
|
1112
|
-
prompt = "\n".join(requirement_lines)
|
|
1113
|
-
return self._append_additional_notes(prompt)
|
|
1114
|
-
|
|
1115
|
-
def _codeagent_generate_impl(self, rec: FnRecord, c_code: str, module: str, rust_sig: str, unresolved: List[str]) -> None:
|
|
1116
|
-
"""
|
|
1117
|
-
使用 CodeAgent 生成/更新目标模块中的函数实现。
|
|
1118
|
-
约束:最小变更,生成可编译的占位实现,尽可能保留后续细化空间。
|
|
1119
|
-
"""
|
|
1120
|
-
# 构建提示词
|
|
1121
|
-
prompt = self._build_generate_impl_prompt(rec, c_code, module, rust_sig, unresolved)
|
|
1122
|
-
|
|
1123
|
-
# 确保目标模块文件存在(提高补丁应用与实现落盘的确定性)
|
|
1124
|
-
try:
|
|
1125
|
-
mp = Path(module)
|
|
1126
|
-
if not mp.is_absolute():
|
|
1127
|
-
mp = (self.crate_dir / module).resolve()
|
|
1128
|
-
mp.parent.mkdir(parents=True, exist_ok=True)
|
|
1129
|
-
if not mp.exists():
|
|
1130
|
-
try:
|
|
1131
|
-
mp.write_text("// Auto-created by c2rust transpiler\n", encoding="utf-8")
|
|
1132
|
-
typer.secho(f"[c2rust-transpiler][gen] auto-created module file: {mp}", fg=typer.colors.GREEN)
|
|
1133
|
-
except Exception:
|
|
1134
|
-
pass
|
|
1135
|
-
except Exception:
|
|
1136
|
-
pass
|
|
1137
|
-
|
|
1138
|
-
# 由于 transpile() 开始时已切换到 crate 目录,此处无需再次切换
|
|
1139
|
-
# 记录运行前的 commit
|
|
1140
|
-
before_commit = self._get_crate_commit_hash()
|
|
1141
|
-
agent = self._get_code_agent()
|
|
1142
|
-
agent.run(self._compose_prompt_with_context(prompt), prefix="[c2rust-transpiler][gen]", suffix="")
|
|
1143
|
-
|
|
1144
|
-
# 检测并处理测试代码删除
|
|
1145
|
-
if self._check_and_handle_test_deletion(before_commit, agent):
|
|
1146
|
-
# 如果回退了,需要重新运行 agent
|
|
1147
|
-
typer.secho("[c2rust-transpiler][gen] 检测到测试代码删除问题,已回退,重新运行 agent", fg=typer.colors.YELLOW)
|
|
1148
|
-
before_commit = self._get_crate_commit_hash()
|
|
1149
|
-
agent.run(self._compose_prompt_with_context(prompt), prefix="[c2rust-transpiler][gen][retry]", suffix="")
|
|
1150
|
-
# 再次检测
|
|
1151
|
-
if self._check_and_handle_test_deletion(before_commit, agent):
|
|
1152
|
-
typer.secho("[c2rust-transpiler][gen] 再次检测到测试代码删除问题,已回退", fg=typer.colors.RED)
|
|
1153
|
-
|
|
1154
|
-
# 如果是根符号,确保其模块在 lib.rs 中被暴露
|
|
1155
|
-
if self._is_root_symbol(rec):
|
|
1156
|
-
try:
|
|
1157
|
-
mp = Path(module)
|
|
1158
|
-
crate_root = self.crate_dir.resolve()
|
|
1159
|
-
rel = mp.resolve().relative_to(crate_root) if mp.is_absolute() else Path(module)
|
|
1160
|
-
rel_s = str(rel).replace("\\", "/")
|
|
1161
|
-
if rel_s.startswith("./"):
|
|
1162
|
-
rel_s = rel_s[2:]
|
|
1163
|
-
if rel_s.startswith("src/"):
|
|
1164
|
-
parts = rel_s[len("src/"):].strip("/").split("/")
|
|
1165
|
-
if parts and parts[0]:
|
|
1166
|
-
top_mod = parts[0]
|
|
1167
|
-
# 过滤掉 "mod" 关键字和 .rs 文件
|
|
1168
|
-
if top_mod != "mod" and not top_mod.endswith(".rs"):
|
|
1169
|
-
self._ensure_top_level_pub_mod(top_mod)
|
|
1170
|
-
typer.secho(f"[c2rust-transpiler][gen] 根符号 {rec.qname or rec.name} 的模块 {top_mod} 已在 lib.rs 中暴露", fg=typer.colors.GREEN)
|
|
1171
|
-
except Exception:
|
|
1172
|
-
pass
|
|
416
|
+
"""构建代码生成提示词(委托给 GenerationManager)"""
|
|
417
|
+
return self.generation_manager.build_generate_impl_prompt(
|
|
418
|
+
rec, c_code, module, rust_sig, unresolved
|
|
419
|
+
)
|
|
420
|
+
|
|
421
|
+
def _codeagent_generate_impl(
|
|
422
|
+
self,
|
|
423
|
+
rec: FnRecord,
|
|
424
|
+
c_code: str,
|
|
425
|
+
module: str,
|
|
426
|
+
rust_sig: str,
|
|
427
|
+
unresolved: List[str],
|
|
428
|
+
) -> None:
|
|
429
|
+
"""使用 CodeAgent 生成/更新目标模块中的函数实现(委托给 GenerationManager)"""
|
|
430
|
+
return self.generation_manager.codeagent_generate_impl(
|
|
431
|
+
rec, c_code, module, rust_sig, unresolved
|
|
432
|
+
)
|
|
1173
433
|
|
|
1174
434
|
def _extract_rust_fn_name_from_sig(self, rust_sig: str) -> str:
|
|
1175
|
-
"""
|
|
1176
|
-
|
|
1177
|
-
例如: 'pub fn foo(a: i32) -> i32 { ... }' -> 'foo'
|
|
1178
|
-
例如: 'pub fn foo<'a>(bzf: &'a mut BzFile) -> Result<&'a [u8], BzError>' -> 'foo'
|
|
1179
|
-
"""
|
|
1180
|
-
# 支持生命周期参数和泛型参数:fn name<'a, T>(...)
|
|
1181
|
-
m = re.search(r"\bfn\s+([A-Za-z_][A-Za-z0-9_]*)\s*(?:<[^>]+>)?\s*\(", rust_sig or "")
|
|
1182
|
-
return m.group(1) if m else ""
|
|
435
|
+
"""从 rust 签名中提取函数名(委托给 GenerationManager)"""
|
|
436
|
+
return self.generation_manager.extract_rust_fn_name_from_sig(rust_sig)
|
|
1183
437
|
|
|
1184
438
|
def _ensure_top_level_pub_mod(self, mod_name: str) -> None:
|
|
1185
|
-
"""
|
|
1186
|
-
|
|
1187
|
-
- 如已存在 `pub mod`,不做改动
|
|
1188
|
-
- 如存在 `mod <mod_name>;`,升级为 `pub mod <mod_name>;`
|
|
1189
|
-
- 如都不存在,则在文件末尾追加一行 `pub mod <mod_name>;`
|
|
1190
|
-
- 最小改动,不覆盖其他内容
|
|
1191
|
-
"""
|
|
1192
|
-
try:
|
|
1193
|
-
if not mod_name or mod_name in ("lib", "main", "mod"):
|
|
1194
|
-
return
|
|
1195
|
-
lib_rs = (self.crate_dir / "src" / "lib.rs").resolve()
|
|
1196
|
-
lib_rs.parent.mkdir(parents=True, exist_ok=True)
|
|
1197
|
-
if not lib_rs.exists():
|
|
1198
|
-
try:
|
|
1199
|
-
lib_rs.write_text("// Auto-generated by c2rust transpiler\n", encoding="utf-8")
|
|
1200
|
-
typer.secho(f"[c2rust-transpiler][mod] 已创建 src/lib.rs: {lib_rs}", fg=typer.colors.GREEN)
|
|
1201
|
-
except Exception:
|
|
1202
|
-
return
|
|
1203
|
-
txt = lib_rs.read_text(encoding="utf-8", errors="replace")
|
|
1204
|
-
pub_pat = re.compile(rf'(?m)^\s*pub\s+mod\s+{re.escape(mod_name)}\s*;\s*$')
|
|
1205
|
-
mod_pat = re.compile(rf'(?m)^\s*mod\s+{re.escape(mod_name)}\s*;\s*$')
|
|
1206
|
-
if pub_pat.search(txt):
|
|
1207
|
-
return
|
|
1208
|
-
if mod_pat.search(txt):
|
|
1209
|
-
# 升级为 pub mod(保留原缩进)
|
|
1210
|
-
def _repl(m):
|
|
1211
|
-
line = m.group(0)
|
|
1212
|
-
ws = re.match(r'^(\s*)', line).group(1) if re.match(r'^(\s*)', line) else ""
|
|
1213
|
-
return f"{ws}pub mod {mod_name};"
|
|
1214
|
-
new_txt = mod_pat.sub(_repl, txt, count=1)
|
|
1215
|
-
else:
|
|
1216
|
-
new_txt = (txt.rstrip() + f"\npub mod {mod_name};\n")
|
|
1217
|
-
lib_rs.write_text(new_txt, encoding="utf-8")
|
|
1218
|
-
typer.secho(f"[c2rust-transpiler][mod] updated src/lib.rs: ensured pub mod {mod_name}", fg=typer.colors.GREEN)
|
|
1219
|
-
except Exception:
|
|
1220
|
-
# 保持稳健,失败不阻塞主流程
|
|
1221
|
-
pass
|
|
439
|
+
"""在 src/lib.rs 中确保存在 `pub mod <mod_name>;`(委托给 ModuleManager)"""
|
|
440
|
+
self.module_manager.ensure_top_level_pub_mod(mod_name)
|
|
1222
441
|
|
|
1223
442
|
def _ensure_mod_rs_decl(self, dir_path: Path, child_mod: str) -> None:
|
|
1224
|
-
"""
|
|
1225
|
-
|
|
1226
|
-
- 如存在 `mod <child_mod>;` 则升级为 `pub mod <child_mod>;`
|
|
1227
|
-
- 如均不存在则在文件末尾追加 `pub mod <child_mod>;`
|
|
1228
|
-
- 最小改动,不覆盖其他内容
|
|
1229
|
-
"""
|
|
1230
|
-
try:
|
|
1231
|
-
if not child_mod or child_mod in ("lib", "main", "mod"):
|
|
1232
|
-
return
|
|
1233
|
-
mod_rs = (dir_path / "mod.rs").resolve()
|
|
1234
|
-
mod_rs.parent.mkdir(parents=True, exist_ok=True)
|
|
1235
|
-
if not mod_rs.exists():
|
|
1236
|
-
try:
|
|
1237
|
-
mod_rs.write_text("// Auto-generated by c2rust transpiler\n", encoding="utf-8")
|
|
1238
|
-
typer.secho(f"[c2rust-transpiler][mod] 已创建 {mod_rs}", fg=typer.colors.GREEN)
|
|
1239
|
-
except Exception:
|
|
1240
|
-
return
|
|
1241
|
-
txt = mod_rs.read_text(encoding="utf-8", errors="replace")
|
|
1242
|
-
pub_pat = re.compile(rf'(?m)^\s*pub\s+mod\s+{re.escape(child_mod)}\s*;\s*$')
|
|
1243
|
-
mod_pat = re.compile(rf'(?m)^\s*mod\s+{re.escape(child_mod)}\s*;\s*$')
|
|
1244
|
-
if pub_pat.search(txt):
|
|
1245
|
-
return
|
|
1246
|
-
if mod_pat.search(txt):
|
|
1247
|
-
# 升级为 pub mod(保留原缩进)
|
|
1248
|
-
def _repl(m):
|
|
1249
|
-
line = m.group(0)
|
|
1250
|
-
ws = re.match(r'^(\s*)', line).group(1) if re.match(r'^(\s*)', line) else ""
|
|
1251
|
-
return f"{ws}pub mod {child_mod};"
|
|
1252
|
-
new_txt = mod_pat.sub(_repl, txt, count=1)
|
|
1253
|
-
else:
|
|
1254
|
-
new_txt = (txt.rstrip() + f"\npub mod {child_mod};\n")
|
|
1255
|
-
mod_rs.write_text(new_txt, encoding="utf-8")
|
|
1256
|
-
typer.secho(f"[c2rust-transpiler][mod] updated {mod_rs}: ensured pub mod {child_mod}", fg=typer.colors.GREEN)
|
|
1257
|
-
except Exception:
|
|
1258
|
-
pass
|
|
443
|
+
"""在 dir_path/mod.rs 中确保存在 `pub mod <child_mod>;`(委托给 ModuleManager)"""
|
|
444
|
+
self.module_manager.ensure_mod_rs_decl(dir_path, child_mod)
|
|
1259
445
|
|
|
1260
446
|
def _ensure_mod_chain_for_module(self, module: str) -> None:
|
|
1261
|
-
"""
|
|
1262
|
-
|
|
1263
|
-
- 对于 src/foo/bar.rs:在 src/foo/mod.rs 确保 `pub mod bar;`
|
|
1264
|
-
并在上层 src/mod.rs(不修改)改为在 src/lib.rs 确保 `pub mod foo;`(已由顶层函数处理)
|
|
1265
|
-
- 对于 src/foo/bar/mod.rs:在 src/foo/mod.rs 确保 `pub mod bar;`
|
|
1266
|
-
- 对多级目录,逐级在上层 mod.rs 确保对子目录的 `pub mod <child>;`
|
|
1267
|
-
"""
|
|
1268
|
-
try:
|
|
1269
|
-
mp = Path(module)
|
|
1270
|
-
base = mp
|
|
1271
|
-
if not mp.is_absolute():
|
|
1272
|
-
base = (self.crate_dir / module).resolve()
|
|
1273
|
-
crate_root = self.crate_dir.resolve()
|
|
1274
|
-
# 必须在 crate/src 下
|
|
1275
|
-
rel = base.relative_to(crate_root)
|
|
1276
|
-
rel_s = str(rel).replace("\\", "/")
|
|
1277
|
-
if not rel_s.startswith("src/"):
|
|
1278
|
-
return
|
|
1279
|
-
# 计算起始目录与首个子模块名
|
|
1280
|
-
inside = rel_s[len("src/"):].strip("/")
|
|
1281
|
-
if not inside:
|
|
1282
|
-
return
|
|
1283
|
-
parts = [p for p in inside.split("/") if p] # 过滤空字符串
|
|
1284
|
-
if parts[-1].endswith(".rs"):
|
|
1285
|
-
if parts[-1] in ("lib.rs", "main.rs"):
|
|
1286
|
-
return
|
|
1287
|
-
child = parts[-1][:-3] # 去掉 .rs
|
|
1288
|
-
# 过滤掉 "mod" 关键字
|
|
1289
|
-
if child == "mod":
|
|
1290
|
-
return
|
|
1291
|
-
if len(parts) > 1:
|
|
1292
|
-
start_dir = crate_root / "src" / "/".join(parts[:-1])
|
|
1293
|
-
else:
|
|
1294
|
-
start_dir = crate_root / "src"
|
|
1295
|
-
# 确保 start_dir 在 crate/src 下
|
|
1296
|
-
try:
|
|
1297
|
-
start_dir_rel = start_dir.relative_to(crate_root)
|
|
1298
|
-
if not str(start_dir_rel).replace("\\", "/").startswith("src/"):
|
|
1299
|
-
return
|
|
1300
|
-
except ValueError:
|
|
1301
|
-
return
|
|
1302
|
-
# 在当前目录的 mod.rs 确保 pub mod <child>
|
|
1303
|
-
if start_dir.name != "src":
|
|
1304
|
-
self._ensure_mod_rs_decl(start_dir, child)
|
|
1305
|
-
# 向上逐级确保父目录对当前目录的 pub mod 声明
|
|
1306
|
-
cur_dir = start_dir
|
|
1307
|
-
else:
|
|
1308
|
-
# 末尾为目录(mod.rs 情况):确保父目录对该目录 pub mod
|
|
1309
|
-
if parts:
|
|
1310
|
-
cur_dir = crate_root / "src" / "/".join(parts)
|
|
1311
|
-
# 确保 cur_dir 在 crate/src 下
|
|
1312
|
-
try:
|
|
1313
|
-
cur_dir_rel = cur_dir.relative_to(crate_root)
|
|
1314
|
-
if not str(cur_dir_rel).replace("\\", "/").startswith("src/"):
|
|
1315
|
-
return
|
|
1316
|
-
except ValueError:
|
|
1317
|
-
return
|
|
1318
|
-
else:
|
|
1319
|
-
return
|
|
1320
|
-
# 逐级向上到 src 根(不修改 src/mod.rs,顶层由 lib.rs 公开)
|
|
1321
|
-
while True:
|
|
1322
|
-
parent = cur_dir.parent
|
|
1323
|
-
if not parent.exists():
|
|
1324
|
-
break
|
|
1325
|
-
# 确保不超过 crate 根目录
|
|
1326
|
-
try:
|
|
1327
|
-
parent.relative_to(crate_root)
|
|
1328
|
-
except ValueError:
|
|
1329
|
-
# parent 不在 crate_root 下,停止向上遍历
|
|
1330
|
-
break
|
|
1331
|
-
if parent.name == "src":
|
|
1332
|
-
# 顶层由 _ensure_top_level_pub_mod 负责
|
|
1333
|
-
break
|
|
1334
|
-
# 在 parent/mod.rs 确保 pub mod <cur_dir.name>
|
|
1335
|
-
# 确保 parent 在 crate/src 下
|
|
1336
|
-
# 过滤掉 "mod" 关键字
|
|
1337
|
-
if cur_dir.name == "mod":
|
|
1338
|
-
cur_dir = parent
|
|
1339
|
-
continue
|
|
1340
|
-
try:
|
|
1341
|
-
parent_rel = parent.relative_to(crate_root)
|
|
1342
|
-
if str(parent_rel).replace("\\", "/").startswith("src/"):
|
|
1343
|
-
self._ensure_mod_rs_decl(parent, cur_dir.name)
|
|
1344
|
-
except (ValueError, Exception):
|
|
1345
|
-
# parent 不在 crate/src 下,跳过
|
|
1346
|
-
break
|
|
1347
|
-
cur_dir = parent
|
|
1348
|
-
except Exception:
|
|
1349
|
-
pass
|
|
447
|
+
"""根据目标模块文件,补齐从该文件所在目录向上的 mod.rs 声明链(委托给 ModuleManager)"""
|
|
448
|
+
self.module_manager.ensure_mod_chain_for_module(module)
|
|
1350
449
|
|
|
1351
450
|
def _module_file_to_crate_path(self, module: str) -> str:
|
|
1352
|
-
"""
|
|
1353
|
-
|
|
1354
|
-
- src/lib.rs -> crate
|
|
1355
|
-
- src/foo/mod.rs -> crate::foo
|
|
1356
|
-
- src/foo/bar.rs -> crate::foo::bar
|
|
1357
|
-
支持绝对路径:若 module 为绝对路径且位于 crate 根目录下,会自动转换为相对路径再解析;
|
|
1358
|
-
其它(无法解析为 crate/src 下的路径)统一返回 'crate'
|
|
1359
|
-
"""
|
|
1360
|
-
mod = str(module).strip()
|
|
1361
|
-
# 若传入绝对路径且在 crate_dir 下,转换为相对路径以便后续按 src/ 前缀解析
|
|
1362
|
-
try:
|
|
1363
|
-
mp = Path(mod)
|
|
1364
|
-
if mp.is_absolute():
|
|
1365
|
-
try:
|
|
1366
|
-
rel = mp.resolve().relative_to(self.crate_dir.resolve())
|
|
1367
|
-
mod = str(rel).replace("\\", "/")
|
|
1368
|
-
except Exception:
|
|
1369
|
-
# 绝对路径不在 crate_dir 下,保持原样
|
|
1370
|
-
pass
|
|
1371
|
-
except Exception:
|
|
1372
|
-
pass
|
|
1373
|
-
# 规范化 ./ 前缀
|
|
1374
|
-
if mod.startswith("./"):
|
|
1375
|
-
mod = mod[2:]
|
|
1376
|
-
# 仅处理位于 src/ 下的模块文件
|
|
1377
|
-
if not mod.startswith("src/"):
|
|
1378
|
-
return "crate"
|
|
1379
|
-
p = mod[len("src/"):]
|
|
1380
|
-
if p.endswith("mod.rs"):
|
|
1381
|
-
p = p[: -len("mod.rs")]
|
|
1382
|
-
elif p.endswith(".rs"):
|
|
1383
|
-
p = p[: -len(".rs")]
|
|
1384
|
-
p = p.strip("/")
|
|
1385
|
-
return "crate" if not p else "crate::" + p.replace("/", "::")
|
|
1386
|
-
|
|
1387
|
-
def _resolve_pending_todos_for_symbol(self, symbol: str, callee_module: str, callee_rust_fn: str, callee_rust_sig: str) -> None:
|
|
1388
|
-
"""
|
|
1389
|
-
当某个 C 符号对应的函数已转换为 Rust 后:
|
|
1390
|
-
- 扫描整个 crate(优先 src/ 目录)中所有 .rs 文件,查找占位:todo!("符号名") 或 unimplemented!("符号名")
|
|
1391
|
-
- 对每个命中的文件,创建 CodeAgent 将占位替换为对已转换函数的真实调用(可使用 crate::... 完全限定路径或 use 引入)
|
|
1392
|
-
- 最小化修改,避免无关重构
|
|
451
|
+
"""将模块文件路径转换为 crate 路径前缀(委托给 ModuleManager)"""
|
|
452
|
+
return self.module_manager.module_file_to_crate_path(module)
|
|
1393
453
|
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
|
|
1397
|
-
|
|
1398
|
-
|
|
1399
|
-
|
|
1400
|
-
|
|
1401
|
-
|
|
1402
|
-
|
|
1403
|
-
|
|
1404
|
-
|
|
1405
|
-
|
|
1406
|
-
|
|
1407
|
-
|
|
1408
|
-
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
|
|
1412
|
-
|
|
1413
|
-
|
|
1414
|
-
|
|
1415
|
-
|
|
1416
|
-
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
|
|
1428
|
-
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
|
|
1436
|
-
|
|
1437
|
-
|
|
1438
|
-
|
|
1439
|
-
|
|
1440
|
-
|
|
1441
|
-
|
|
1442
|
-
|
|
1443
|
-
])
|
|
1444
|
-
# 记录运行前的 commit
|
|
1445
|
-
before_commit = self._get_crate_commit_hash()
|
|
1446
|
-
agent = self._get_code_agent()
|
|
1447
|
-
agent.run(self._compose_prompt_with_context(prompt), prefix=f"[c2rust-transpiler][todo-fix:{symbol}]", suffix="")
|
|
1448
|
-
|
|
1449
|
-
# 检测并处理测试代码删除
|
|
1450
|
-
if self._check_and_handle_test_deletion(before_commit, agent):
|
|
1451
|
-
# 如果回退了,需要重新运行 agent
|
|
1452
|
-
typer.secho(f"[c2rust-transpiler][todo-fix] 检测到测试代码删除问题,已回退,重新运行 agent (symbol={symbol})", fg=typer.colors.YELLOW)
|
|
1453
|
-
before_commit = self._get_crate_commit_hash()
|
|
1454
|
-
agent.run(self._compose_prompt_with_context(prompt), prefix=f"[c2rust-transpiler][todo-fix:{symbol}][retry]", suffix="")
|
|
1455
|
-
# 再次检测
|
|
1456
|
-
if self._check_and_handle_test_deletion(before_commit, agent):
|
|
1457
|
-
typer.secho(f"[c2rust-transpiler][todo-fix] 再次检测到测试代码删除问题,已回退 (symbol={symbol})", fg=typer.colors.RED)
|
|
454
|
+
def _ensure_cargo_toml_bin(
|
|
455
|
+
self, bin_path: str, bin_name: Optional[str] = None
|
|
456
|
+
) -> None:
|
|
457
|
+
"""在 Cargo.toml 中确保存在 [[bin]] 配置(委托给 ModuleManager)"""
|
|
458
|
+
self.module_manager.ensure_cargo_toml_bin(bin_path, bin_name)
|
|
459
|
+
|
|
460
|
+
def _resolve_pending_todos_for_symbol(
|
|
461
|
+
self, symbol: str, callee_module: str, callee_rust_fn: str, callee_rust_sig: str
|
|
462
|
+
) -> None:
|
|
463
|
+
"""解析待处理的 todo 占位(委托给 SymbolMapper)"""
|
|
464
|
+
# 使用修复 Agent(传入 None,因为该方法不需要 C 代码)
|
|
465
|
+
self.symbol_mapper.resolve_pending_todos_for_symbol(
|
|
466
|
+
symbol,
|
|
467
|
+
callee_module,
|
|
468
|
+
callee_rust_fn,
|
|
469
|
+
callee_rust_sig,
|
|
470
|
+
self.crate_dir,
|
|
471
|
+
lambda: self._get_fix_agent(None),
|
|
472
|
+
lambda prompt: self._compose_prompt_with_context(prompt, for_fix=True),
|
|
473
|
+
self._check_and_handle_test_deletion,
|
|
474
|
+
)
|
|
475
|
+
|
|
476
|
+
def _init_build_manager(self) -> None:
|
|
477
|
+
"""初始化构建管理器"""
|
|
478
|
+
if self.build_manager is None:
|
|
479
|
+
from jarvis.jarvis_c2rust.transpiler_build import BuildManager
|
|
480
|
+
|
|
481
|
+
self.build_manager = BuildManager(
|
|
482
|
+
self.crate_dir,
|
|
483
|
+
self.project_root,
|
|
484
|
+
self.data_dir,
|
|
485
|
+
self.test_max_retries,
|
|
486
|
+
self.disabled_libraries,
|
|
487
|
+
self.root_symbols,
|
|
488
|
+
self.progress,
|
|
489
|
+
self._save_progress,
|
|
490
|
+
self._extract_compile_flags,
|
|
491
|
+
self._get_current_function_context,
|
|
492
|
+
self._get_fix_agent,
|
|
493
|
+
self._compose_prompt_with_context,
|
|
494
|
+
self._check_and_handle_test_deletion,
|
|
495
|
+
self._get_crate_commit_hash,
|
|
496
|
+
self._reset_to_commit,
|
|
497
|
+
self._append_additional_notes,
|
|
498
|
+
lambda: self._consecutive_fix_failures,
|
|
499
|
+
lambda v: setattr(self, "_consecutive_fix_failures", v),
|
|
500
|
+
lambda: self._current_function_start_commit,
|
|
501
|
+
self._get_git_diff,
|
|
502
|
+
)
|
|
1458
503
|
|
|
1459
504
|
def _classify_rust_error(self, text: str) -> List[str]:
|
|
1460
|
-
"""
|
|
1461
|
-
|
|
1462
|
-
|
|
1463
|
-
|
|
1464
|
-
|
|
1465
|
-
|
|
1466
|
-
|
|
1467
|
-
|
|
1468
|
-
"""
|
|
1469
|
-
tags: List[str] = []
|
|
1470
|
-
t = (text or "").lower()
|
|
1471
|
-
def has(s: str) -> bool:
|
|
1472
|
-
return s in t
|
|
1473
|
-
if ("unresolved import" in t) or ("not found in this scope" in t) or ("cannot find" in t) or ("use of undeclared crate or module" in t):
|
|
1474
|
-
tags.append("missing_import")
|
|
1475
|
-
if ("mismatched types" in t) or ("expected" in t and "found" in t):
|
|
1476
|
-
tags.append("type_mismatch")
|
|
1477
|
-
if ("private" in t and "module" in t) or ("private" in t and "field" in t) or ("private" in t and "function" in t):
|
|
1478
|
-
tags.append("visibility")
|
|
1479
|
-
if ("does not live long enough" in t) or ("borrowed data escapes" in t) or ("cannot borrow" in t):
|
|
1480
|
-
tags.append("borrow_checker")
|
|
1481
|
-
if ("failed to select a version" in t) or ("could not find crate" in t) or ("no matching package named" in t):
|
|
1482
|
-
tags.append("dependency_missing")
|
|
1483
|
-
if ("file not found for module" in t) or ("unresolved module" in t):
|
|
1484
|
-
tags.append("module_not_found")
|
|
1485
|
-
# 去重
|
|
1486
|
-
try:
|
|
1487
|
-
tags = list(dict.fromkeys(tags))
|
|
1488
|
-
except Exception:
|
|
1489
|
-
tags = list(set(tags))
|
|
1490
|
-
return tags
|
|
505
|
+
"""朴素错误分类(委托给 BuildManager)"""
|
|
506
|
+
if self.build_manager is None:
|
|
507
|
+
self._init_build_manager()
|
|
508
|
+
return (
|
|
509
|
+
self.build_manager.classify_rust_error(text)
|
|
510
|
+
if self.build_manager is not None
|
|
511
|
+
else []
|
|
512
|
+
)
|
|
1491
513
|
|
|
1492
514
|
def _get_current_function_context(self) -> Tuple[Dict[str, Any], str, str, str]:
|
|
1493
515
|
"""
|
|
@@ -1499,7 +521,11 @@ class Transpiler:
|
|
|
1499
521
|
except Exception:
|
|
1500
522
|
curr = {}
|
|
1501
523
|
sym_name = str(curr.get("qualified_name") or curr.get("name") or "")
|
|
1502
|
-
src_loc =
|
|
524
|
+
src_loc = (
|
|
525
|
+
f"{curr.get('file')}:{curr.get('start_line')}-{curr.get('end_line')}"
|
|
526
|
+
if curr
|
|
527
|
+
else ""
|
|
528
|
+
)
|
|
1503
529
|
c_code = ""
|
|
1504
530
|
try:
|
|
1505
531
|
cf = curr.get("file")
|
|
@@ -1519,1431 +545,204 @@ class Transpiler:
|
|
|
1519
545
|
return curr, sym_name, src_loc, c_code
|
|
1520
546
|
|
|
1521
547
|
def _build_repair_prompt_base(
|
|
1522
|
-
self,
|
|
1523
|
-
|
|
548
|
+
self,
|
|
549
|
+
stage: str,
|
|
550
|
+
tags: List[str],
|
|
551
|
+
sym_name: str,
|
|
552
|
+
src_loc: str,
|
|
553
|
+
c_code: str,
|
|
554
|
+
curr: Dict[str, Any],
|
|
555
|
+
symbols_path: str,
|
|
556
|
+
include_output_patch_hint: bool = False,
|
|
1524
557
|
) -> List[str]:
|
|
1525
|
-
"""
|
|
1526
|
-
|
|
1527
|
-
|
|
1528
|
-
|
|
1529
|
-
|
|
1530
|
-
|
|
1531
|
-
|
|
1532
|
-
|
|
1533
|
-
|
|
1534
|
-
|
|
1535
|
-
|
|
1536
|
-
|
|
1537
|
-
|
|
1538
|
-
|
|
1539
|
-
|
|
1540
|
-
|
|
1541
|
-
|
|
1542
|
-
" * 添加临时调试输出:使用 `println!()` 或 `dbg!()` 宏输出关键变量的值、函数调用路径、中间状态等",
|
|
1543
|
-
" * 检查函数调用链:使用 `read_code` 工具读取相关函数的实现,确认调用关系是否正确",
|
|
1544
|
-
" * 验证数据流:在关键位置添加调试输出,检查数据在函数间的传递是否正确",
|
|
1545
|
-
" * 对比 C 实现:使用 `read_symbols` 和 `read_code` 工具读取 C 源码,对比 Rust 实现与 C 实现的差异",
|
|
1546
|
-
" * 检查类型转换:确认 Rust 类型与 C 类型的对应关系是否正确,特别是指针、数组、结构体等",
|
|
1547
|
-
" * 验证边界条件:检查数组边界、空值处理、溢出处理等边界情况",
|
|
1548
|
-
" * 运行单个测试:如果测试套件很大,可以使用 `cargo test -- --nocapture <test_name>` 运行特定测试,加快调试速度",
|
|
1549
|
-
" * 查看完整错误信息:确保阅读完整的错误输出,包括堆栈跟踪、类型信息、位置信息等",
|
|
1550
|
-
" * 注意:调试输出可以在修复后移除,但建议保留关键位置的调试信息直到问题完全解决",
|
|
1551
|
-
"- **⚠️ 重要:修复后必须验证** - 修复完成后,必须使用 `execute_script` 工具执行验证命令:",
|
|
1552
|
-
" * 执行 `cargo test -- --nocapture` 验证编译和测试是否通过",
|
|
1553
|
-
" * 命令必须成功(返回码为 0),才说明修复成功",
|
|
1554
|
-
" * **不要假设修复成功,必须实际执行命令验证**",
|
|
1555
|
-
" * **cargo test 会自动编译,无需单独执行 cargo check**",
|
|
1556
|
-
"- 注释规范:所有代码注释(包括文档注释、行内注释、块注释等)必须使用中文;",
|
|
1557
|
-
f"- 依赖管理:如修复中引入新的外部 crate 或需要启用 feature,请同步更新 Cargo.toml 的 [dependencies]/[dev-dependencies]/[features]{(',避免未声明依赖导致构建失败;版本号可使用兼容范围(如 ^x.y)或默认值' if stage == 'cargo test' else '')};",
|
|
1558
|
-
*([f"- **禁用库约束**:禁止在修复中使用以下库:{', '.join(self.disabled_libraries)}。如果这些库在 Cargo.toml 中已存在,请移除相关依赖;如果修复需要使用这些库的功能,请使用标准库或其他允许的库替代。"] if self.disabled_libraries else []),
|
|
1559
|
-
*([f"- **根符号要求**:此函数是根符号({sym_name}),必须使用 `pub` 关键字对外暴露,确保可以从 crate 外部访问。同时,该函数所在的模块必须在 src/lib.rs 中被导出(使用 `pub mod <模块名>;`)。"] if is_root else []),
|
|
1560
|
-
"",
|
|
1561
|
-
"【重要:依赖检查与实现要求】",
|
|
1562
|
-
"在修复问题之前,请务必检查以下内容:",
|
|
1563
|
-
"1. 检查当前函数是否已完整实现:",
|
|
1564
|
-
f" - 在目标模块中查找函数 {sym_name} 的实现",
|
|
1565
|
-
" - 如果已存在实现,检查其是否完整且正确",
|
|
1566
|
-
"2. 检查所有依赖函数是否已实现:",
|
|
1567
|
-
" - 分析构建错误,识别所有缺失或未实现的被调函数",
|
|
1568
|
-
" - 遍历当前函数调用的所有被调函数(包括直接调用和间接调用)",
|
|
1569
|
-
" - 对于每个被调函数,检查其在 Rust crate 中是否已有完整实现",
|
|
1570
|
-
" - 可以使用 read_code 工具读取相关模块文件进行检查",
|
|
1571
|
-
"3. 对于未实现的依赖函数:",
|
|
1572
|
-
" - 使用 read_symbols 工具获取其 C 源码和符号信息",
|
|
1573
|
-
" - 使用 read_code 工具读取其 C 源码实现",
|
|
1574
|
-
" - 在本次修复中一并补齐这些依赖函数的 Rust 实现",
|
|
1575
|
-
" - 根据依赖关系选择合适的模块位置(可在同一模块或合理的新模块中)",
|
|
1576
|
-
" - 确保所有依赖函数都有完整实现,禁止使用 todo!/unimplemented! 占位",
|
|
1577
|
-
"4. 实现顺序:",
|
|
1578
|
-
" - 优先实现最底层的依赖函数(不依赖其他未实现函数的函数)",
|
|
1579
|
-
" - 然后实现依赖这些底层函数的函数",
|
|
1580
|
-
" - 最后修复当前目标函数",
|
|
1581
|
-
"5. 验证:",
|
|
1582
|
-
" - 确保当前函数及其所有依赖函数都已完整实现",
|
|
1583
|
-
" - 确保没有遗留的 todo!/unimplemented! 占位",
|
|
1584
|
-
" - 确保所有函数调用都能正确解析",
|
|
1585
|
-
]
|
|
1586
|
-
if include_output_patch_hint:
|
|
1587
|
-
base_lines.append("- 请仅输出补丁,不要输出解释或多余文本。")
|
|
1588
|
-
base_lines.extend([
|
|
1589
|
-
"",
|
|
1590
|
-
"最近处理的函数上下文(供参考,优先修复构建错误):",
|
|
1591
|
-
f"- 函数:{sym_name}",
|
|
1592
|
-
f"- 源位置:{src_loc}",
|
|
1593
|
-
f"- 目标模块(progress):{curr.get('module') or ''}",
|
|
1594
|
-
f"- 建议签名(progress):{curr.get('rust_signature') or ''}",
|
|
1595
|
-
"",
|
|
1596
|
-
"原始C函数源码片段(只读参考):",
|
|
1597
|
-
"<C_SOURCE>",
|
|
1598
|
-
c_code,
|
|
1599
|
-
"</C_SOURCE>",
|
|
1600
|
-
])
|
|
1601
|
-
# 添加编译参数(如果存在)
|
|
1602
|
-
c_file_path = curr.get("file") or ""
|
|
1603
|
-
if c_file_path:
|
|
1604
|
-
compile_flags = self._extract_compile_flags(c_file_path)
|
|
1605
|
-
if compile_flags:
|
|
1606
|
-
base_lines.extend([
|
|
1607
|
-
"",
|
|
1608
|
-
"C文件编译参数(来自 compile_commands.json):",
|
|
1609
|
-
compile_flags,
|
|
1610
|
-
])
|
|
1611
|
-
base_lines.extend([
|
|
1612
|
-
"",
|
|
1613
|
-
"【工具使用建议】",
|
|
1614
|
-
"1. 符号表检索:",
|
|
1615
|
-
" - 工具: read_symbols",
|
|
1616
|
-
" - 用途: 定位或交叉验证 C 符号位置",
|
|
1617
|
-
" - 参数示例(JSON):",
|
|
1618
|
-
f" {{\"symbols_file\": \"{symbols_path}\", \"symbols\": [\"{sym_name}\"]}}",
|
|
1619
|
-
"",
|
|
1620
|
-
"2. 代码读取:",
|
|
1621
|
-
" - 工具: read_code",
|
|
1622
|
-
" - 用途: 读取 C 源码实现或 Rust 模块文件",
|
|
1623
|
-
" - 调试用途: 当遇到问题时,可以读取相关文件检查实现是否正确",
|
|
1624
|
-
"",
|
|
1625
|
-
"3. 脚本执行(调试辅助):",
|
|
1626
|
-
" - 工具: execute_script",
|
|
1627
|
-
" - 调试用途:",
|
|
1628
|
-
" * 执行 `cargo test -- --nocapture <test_name>` 运行特定测试,加快调试速度",
|
|
1629
|
-
" * 执行 `cargo test --message-format=short --no-run` 只检查编译,不运行测试",
|
|
1630
|
-
" * 执行 `cargo check` 快速检查编译错误(如果测试太慢)",
|
|
1631
|
-
" * 执行 `cargo test --lib` 只运行库测试,跳过集成测试",
|
|
1632
|
-
" * 执行 `cargo test --test <test_file>` 运行特定的测试文件",
|
|
1633
|
-
"",
|
|
1634
|
-
"上下文:",
|
|
1635
|
-
f"- crate 根目录路径: {self.crate_dir.resolve()}",
|
|
1636
|
-
f"- 包名称(用于 cargo -p): {self.crate_dir.name}",
|
|
1637
|
-
])
|
|
1638
|
-
return base_lines
|
|
558
|
+
"""构建修复提示词的基础部分(委托给 BuildManager)"""
|
|
559
|
+
if self.build_manager is None:
|
|
560
|
+
self._init_build_manager()
|
|
561
|
+
return (
|
|
562
|
+
self.build_manager.build_repair_prompt_base(
|
|
563
|
+
stage,
|
|
564
|
+
tags,
|
|
565
|
+
sym_name,
|
|
566
|
+
src_loc,
|
|
567
|
+
c_code,
|
|
568
|
+
curr,
|
|
569
|
+
symbols_path,
|
|
570
|
+
include_output_patch_hint,
|
|
571
|
+
)
|
|
572
|
+
if self.build_manager is not None
|
|
573
|
+
else []
|
|
574
|
+
)
|
|
1639
575
|
|
|
1640
576
|
def _build_repair_prompt_stage_section(
|
|
1641
577
|
self, stage: str, output: str, command: Optional[str] = None
|
|
1642
578
|
) -> List[str]:
|
|
1643
|
-
"""
|
|
1644
|
-
|
|
1645
|
-
|
|
1646
|
-
|
|
1647
|
-
|
|
1648
|
-
|
|
1649
|
-
|
|
1650
|
-
|
|
1651
|
-
|
|
1652
|
-
|
|
1653
|
-
|
|
1654
|
-
|
|
1655
|
-
|
|
1656
|
-
|
|
1657
|
-
|
|
1658
|
-
|
|
1659
|
-
|
|
1660
|
-
|
|
1661
|
-
|
|
1662
|
-
|
|
1663
|
-
|
|
1664
|
-
|
|
1665
|
-
|
|
1666
|
-
|
|
1667
|
-
|
|
1668
|
-
|
|
1669
|
-
|
|
1670
|
-
|
|
1671
|
-
section_lines.append(f"执行的命令:{command}")
|
|
1672
|
-
section_lines.append("提示:如果不相信上述命令执行结果,可以使用 execute_script 工具自己执行一次该命令进行验证。")
|
|
1673
|
-
section_lines.extend([
|
|
1674
|
-
"",
|
|
1675
|
-
"【测试失败详细信息 - 必须仔细阅读并修复】",
|
|
1676
|
-
"以下是从 `cargo test` 命令获取的完整输出,包含测试失败的具体信息:",
|
|
1677
|
-
"<TEST_FAILURE>",
|
|
1678
|
-
output,
|
|
1679
|
-
"</TEST_FAILURE>",
|
|
1680
|
-
"",
|
|
1681
|
-
"**修复要求:**",
|
|
1682
|
-
"1. 仔细分析上述测试失败信息,找出失败的根本原因",
|
|
1683
|
-
"2. 定位到具体的代码位置(文件路径和行号)",
|
|
1684
|
-
"3. **如果问题难以定位,添加调试信息辅助定位**:",
|
|
1685
|
-
" - 在关键位置添加 `println!()` 或 `dbg!()` 输出变量值、函数调用路径、中间状态",
|
|
1686
|
-
" - 检查函数参数和返回值是否正确传递",
|
|
1687
|
-
" - 验证数据结构和类型转换是否正确",
|
|
1688
|
-
" - 对比 C 实现与 Rust 实现的差异,找出可能导致问题的点",
|
|
1689
|
-
" - 使用 `read_code` 工具读取相关函数的实现,确认逻辑是否正确",
|
|
1690
|
-
" - 如果测试输出信息不足,可以添加更详细的调试输出来定位问题",
|
|
1691
|
-
"4. 修复导致测试失败的代码逻辑",
|
|
1692
|
-
"5. 确保修复后测试能够通过(不要只修复编译错误)",
|
|
1693
|
-
"6. 如果测试用例本身有问题,可以修改测试用例,但必须确保测试能够正确验证函数行为",
|
|
1694
|
-
"",
|
|
1695
|
-
"**⚠️ 重要:修复后必须验证**",
|
|
1696
|
-
"- 修复完成后,**必须使用 `execute_script` 工具执行以下命令验证修复效果**:",
|
|
1697
|
-
f" - 命令:`{command or 'cargo test -- --nocapture'}`",
|
|
1698
|
-
"- 验证要求:",
|
|
1699
|
-
" * 如果命令执行成功(返回码为 0),说明修复成功",
|
|
1700
|
-
" * 如果命令执行失败(返回码非 0),说明修复未成功,需要继续修复",
|
|
1701
|
-
" * **不要假设修复成功,必须实际执行命令验证**",
|
|
1702
|
-
"- 如果验证失败,请分析失败原因并继续修复,直到验证通过",
|
|
1703
|
-
"",
|
|
1704
|
-
"修复后请再次执行 `cargo test` 进行验证。",
|
|
1705
|
-
])
|
|
1706
|
-
else:
|
|
1707
|
-
section_lines.extend([
|
|
1708
|
-
"",
|
|
1709
|
-
"请阅读以下构建错误并进行必要修复:",
|
|
1710
|
-
])
|
|
1711
|
-
if command:
|
|
1712
|
-
section_lines.append(f"执行的命令:{command}")
|
|
1713
|
-
section_lines.append("提示:如果不相信上述命令执行结果,可以使用 execute_script 工具自己执行一次该命令进行验证。")
|
|
1714
|
-
section_lines.extend([
|
|
1715
|
-
"",
|
|
1716
|
-
"<BUILD_ERROR>",
|
|
579
|
+
"""构建修复提示词的阶段特定部分(委托给 BuildManager)"""
|
|
580
|
+
if self.build_manager is None:
|
|
581
|
+
self._init_build_manager()
|
|
582
|
+
return (
|
|
583
|
+
self.build_manager.build_repair_prompt_stage_section(stage, output, command)
|
|
584
|
+
if self.build_manager is not None
|
|
585
|
+
else []
|
|
586
|
+
)
|
|
587
|
+
|
|
588
|
+
def _build_repair_prompt(
|
|
589
|
+
self,
|
|
590
|
+
stage: str,
|
|
591
|
+
output: str,
|
|
592
|
+
tags: List[str],
|
|
593
|
+
sym_name: str,
|
|
594
|
+
src_loc: str,
|
|
595
|
+
c_code: str,
|
|
596
|
+
curr: Dict[str, Any],
|
|
597
|
+
symbols_path: str,
|
|
598
|
+
include_output_patch_hint: bool = False,
|
|
599
|
+
command: Optional[str] = None,
|
|
600
|
+
) -> str:
|
|
601
|
+
"""构建修复提示词(委托给 BuildManager)"""
|
|
602
|
+
if self.build_manager is None:
|
|
603
|
+
self._init_build_manager()
|
|
604
|
+
return (
|
|
605
|
+
self.build_manager.build_repair_prompt(
|
|
606
|
+
stage,
|
|
1717
607
|
output,
|
|
1718
|
-
|
|
1719
|
-
|
|
1720
|
-
|
|
1721
|
-
|
|
1722
|
-
|
|
1723
|
-
|
|
1724
|
-
|
|
1725
|
-
|
|
1726
|
-
|
|
1727
|
-
|
|
1728
|
-
|
|
1729
|
-
"4. 修复导致构建错误的代码",
|
|
1730
|
-
"5. 确保修复后代码能够编译通过",
|
|
1731
|
-
"",
|
|
1732
|
-
"**⚠️ 重要:修复后必须验证**",
|
|
1733
|
-
"- 修复完成后,**必须使用 `execute_script` 工具执行以下命令验证修复效果**:",
|
|
1734
|
-
" - 命令:`cargo test -- --nocapture`",
|
|
1735
|
-
"- 验证要求:",
|
|
1736
|
-
" * 命令必须执行成功(返回码为 0),才说明修复成功",
|
|
1737
|
-
" * 如果命令执行失败(返回码非 0),说明修复未成功,需要继续修复",
|
|
1738
|
-
" * **不要假设修复成功,必须实际执行命令验证**",
|
|
1739
|
-
"- 如果验证失败,请分析失败原因并继续修复,直到验证通过",
|
|
1740
|
-
"",
|
|
1741
|
-
"修复后请执行 `cargo test -- --nocapture` 进行验证。",
|
|
1742
|
-
])
|
|
1743
|
-
return section_lines
|
|
1744
|
-
|
|
1745
|
-
def _build_repair_prompt(self, stage: str, output: str, tags: List[str], sym_name: str, src_loc: str, c_code: str, curr: Dict[str, Any], symbols_path: str, include_output_patch_hint: bool = False, command: Optional[str] = None) -> str:
|
|
1746
|
-
"""
|
|
1747
|
-
构建修复提示词。
|
|
1748
|
-
|
|
1749
|
-
Args:
|
|
1750
|
-
stage: 阶段名称("cargo test")
|
|
1751
|
-
output: 构建错误输出
|
|
1752
|
-
tags: 错误分类标签
|
|
1753
|
-
sym_name: 符号名称
|
|
1754
|
-
src_loc: 源文件位置
|
|
1755
|
-
c_code: C 源码片段
|
|
1756
|
-
curr: 当前进度信息
|
|
1757
|
-
symbols_path: 符号表文件路径
|
|
1758
|
-
include_output_patch_hint: 是否包含"仅输出补丁"提示(test阶段需要)
|
|
1759
|
-
command: 执行的命令(可选)
|
|
1760
|
-
"""
|
|
1761
|
-
base_lines = self._build_repair_prompt_base(
|
|
1762
|
-
stage, tags, sym_name, src_loc, c_code, curr, symbols_path, include_output_patch_hint
|
|
608
|
+
tags,
|
|
609
|
+
sym_name,
|
|
610
|
+
src_loc,
|
|
611
|
+
c_code,
|
|
612
|
+
curr,
|
|
613
|
+
symbols_path,
|
|
614
|
+
include_output_patch_hint,
|
|
615
|
+
command,
|
|
616
|
+
)
|
|
617
|
+
if self.build_manager is not None
|
|
618
|
+
else ""
|
|
1763
619
|
)
|
|
1764
|
-
stage_lines = self._build_repair_prompt_stage_section(stage, output, command)
|
|
1765
|
-
prompt = "\n".join(base_lines + stage_lines)
|
|
1766
|
-
return self._append_additional_notes(prompt)
|
|
1767
620
|
|
|
1768
621
|
def _detect_crate_kind(self) -> str:
|
|
1769
|
-
"""
|
|
1770
|
-
|
|
1771
|
-
|
|
1772
|
-
|
|
1773
|
-
|
|
1774
|
-
|
|
1775
|
-
|
|
1776
|
-
|
|
1777
|
-
try:
|
|
1778
|
-
cargo_path = (self.crate_dir / "Cargo.toml").resolve()
|
|
1779
|
-
txt = ""
|
|
1780
|
-
if cargo_path.exists():
|
|
1781
|
-
try:
|
|
1782
|
-
txt = cargo_path.read_text(encoding="utf-8", errors="ignore")
|
|
1783
|
-
except Exception:
|
|
1784
|
-
txt = ""
|
|
1785
|
-
txt_lower = txt.lower()
|
|
1786
|
-
has_lib = (self.crate_dir / "src" / "lib.rs").exists() or bool(re.search(r"(?m)^\s*\[lib\]\s*$", txt_lower))
|
|
1787
|
-
# 兼容:[[bin]] 为数组表,极少数项目也会写成 [bin]
|
|
1788
|
-
has_bin = (self.crate_dir / "src" / "main.rs").exists() or bool(re.search(r"(?m)^\s*\[\[bin\]\]\s*$", txt_lower) or re.search(r"(?m)^\s*\[bin\]\s*$", txt_lower))
|
|
1789
|
-
if has_lib and has_bin:
|
|
1790
|
-
return "mixed"
|
|
1791
|
-
if has_bin:
|
|
1792
|
-
return "bin"
|
|
1793
|
-
if has_lib:
|
|
1794
|
-
return "lib"
|
|
1795
|
-
except Exception:
|
|
1796
|
-
pass
|
|
1797
|
-
# 默认假设为 lib
|
|
1798
|
-
return "lib"
|
|
622
|
+
"""检测 crate 类型(委托给 BuildManager)"""
|
|
623
|
+
if self.build_manager is None:
|
|
624
|
+
self._init_build_manager()
|
|
625
|
+
return (
|
|
626
|
+
self.build_manager.detect_crate_kind()
|
|
627
|
+
if self.build_manager is not None
|
|
628
|
+
else ""
|
|
629
|
+
)
|
|
1799
630
|
|
|
1800
631
|
def _run_cargo_fmt(self, workspace_root: str) -> None:
|
|
1801
|
-
"""执行 cargo fmt
|
|
1802
|
-
|
|
1803
|
-
|
|
1804
|
-
|
|
1805
|
-
|
|
1806
|
-
text=True,
|
|
1807
|
-
check=False,
|
|
1808
|
-
cwd=workspace_root,
|
|
1809
|
-
)
|
|
1810
|
-
if res.returncode == 0:
|
|
1811
|
-
typer.secho("[c2rust-transpiler][fmt] 代码格式化完成", fg=typer.colors.CYAN)
|
|
1812
|
-
else:
|
|
1813
|
-
# fmt 失败不影响主流程,只记录警告
|
|
1814
|
-
typer.secho(f"[c2rust-transpiler][fmt] 代码格式化失败(非致命): {res.stderr or res.stdout}", fg=typer.colors.YELLOW)
|
|
1815
|
-
except Exception as e:
|
|
1816
|
-
# fmt 失败不影响主流程,只记录警告
|
|
1817
|
-
typer.secho(f"[c2rust-transpiler][fmt] 代码格式化异常(非致命): {e}", fg=typer.colors.YELLOW)
|
|
632
|
+
"""执行 cargo fmt 格式化代码(委托给 BuildManager)"""
|
|
633
|
+
if self.build_manager is None:
|
|
634
|
+
self._init_build_manager()
|
|
635
|
+
if self.build_manager is not None:
|
|
636
|
+
self.build_manager.run_cargo_fmt(workspace_root)
|
|
1818
637
|
|
|
1819
638
|
def _get_crate_commit_hash(self) -> Optional[str]:
|
|
1820
|
-
"""获取 crate 目录的当前 commit id"""
|
|
1821
|
-
|
|
1822
|
-
|
|
1823
|
-
|
|
1824
|
-
|
|
1825
|
-
|
|
1826
|
-
return None
|
|
639
|
+
"""获取 crate 目录的当前 commit id(委托给 GitManager)"""
|
|
640
|
+
return self.git_manager.get_crate_commit_hash()
|
|
641
|
+
|
|
642
|
+
def _get_git_diff(self, base_commit: Optional[str] = None) -> str:
|
|
643
|
+
"""获取 git diff,显示从 base_commit 到当前工作区的变更(委托给 GitManager)"""
|
|
644
|
+
return self.git_manager.get_git_diff(base_commit)
|
|
1827
645
|
|
|
1828
646
|
def _reset_to_commit(self, commit_hash: str) -> bool:
|
|
1829
|
-
"""回退 crate 目录到指定的 commit"""
|
|
1830
|
-
|
|
1831
|
-
# 由于 transpile() 开始时已切换到 crate 目录,此处无需再次切换
|
|
1832
|
-
# 检查是否是 git 仓库
|
|
1833
|
-
result = subprocess.run(
|
|
1834
|
-
["git", "rev-parse", "--git-dir"],
|
|
1835
|
-
capture_output=True,
|
|
1836
|
-
text=True,
|
|
1837
|
-
check=False,
|
|
1838
|
-
)
|
|
1839
|
-
if result.returncode != 0:
|
|
1840
|
-
# 不是 git 仓库,无法回退
|
|
1841
|
-
return False
|
|
1842
|
-
|
|
1843
|
-
# 执行硬重置
|
|
1844
|
-
result = subprocess.run(
|
|
1845
|
-
["git", "reset", "--hard", commit_hash],
|
|
1846
|
-
capture_output=True,
|
|
1847
|
-
text=True,
|
|
1848
|
-
check=False,
|
|
1849
|
-
)
|
|
1850
|
-
if result.returncode == 0:
|
|
1851
|
-
# 清理未跟踪的文件
|
|
1852
|
-
subprocess.run(
|
|
1853
|
-
["git", "clean", "-fd"],
|
|
1854
|
-
capture_output=True,
|
|
1855
|
-
text=True,
|
|
1856
|
-
check=False,
|
|
1857
|
-
)
|
|
1858
|
-
return True
|
|
1859
|
-
return False
|
|
1860
|
-
except Exception:
|
|
1861
|
-
return False
|
|
647
|
+
"""回退 crate 目录到指定的 commit(委托给 GitManager)"""
|
|
648
|
+
return self.git_manager.reset_to_commit(commit_hash)
|
|
1862
649
|
|
|
1863
|
-
def _check_and_handle_test_deletion(
|
|
650
|
+
def _check_and_handle_test_deletion(
|
|
651
|
+
self, before_commit: Optional[str], agent: Any
|
|
652
|
+
) -> bool:
|
|
1864
653
|
"""
|
|
1865
654
|
检测并处理测试代码删除。
|
|
1866
|
-
|
|
655
|
+
|
|
1867
656
|
参数:
|
|
1868
657
|
before_commit: agent 运行前的 commit hash
|
|
1869
658
|
agent: 代码生成或修复的 agent 实例,使用其 model 进行询问
|
|
1870
|
-
|
|
659
|
+
|
|
1871
660
|
返回:
|
|
1872
661
|
bool: 如果检测到问题且已回退,返回 True;否则返回 False
|
|
1873
662
|
"""
|
|
1874
663
|
return check_and_handle_test_deletion(
|
|
1875
|
-
before_commit,
|
|
1876
|
-
agent,
|
|
1877
|
-
self._reset_to_commit,
|
|
1878
|
-
"[c2rust-transpiler]"
|
|
664
|
+
before_commit, agent, self._reset_to_commit, "[c2rust-transpiler]"
|
|
1879
665
|
)
|
|
1880
666
|
|
|
1881
|
-
def _run_cargo_test_and_fix(
|
|
1882
|
-
|
|
1883
|
-
|
|
1884
|
-
|
|
1885
|
-
|
|
1886
|
-
(
|
|
1887
|
-
|
|
1888
|
-
|
|
1889
|
-
|
|
1890
|
-
|
|
1891
|
-
["cargo", "test", "--", "--nocapture"],
|
|
1892
|
-
capture_output=True,
|
|
1893
|
-
text=True,
|
|
1894
|
-
timeout=30,
|
|
1895
|
-
check=False,
|
|
1896
|
-
cwd=workspace_root,
|
|
1897
|
-
)
|
|
1898
|
-
returncode = res_test.returncode
|
|
1899
|
-
stdout = res_test.stdout or ""
|
|
1900
|
-
stderr = res_test.stderr or ""
|
|
1901
|
-
except subprocess.TimeoutExpired as e:
|
|
1902
|
-
# 超时视为测试失败,继续修复流程
|
|
1903
|
-
returncode = -1
|
|
1904
|
-
stdout = e.stdout.decode("utf-8", errors="replace") if e.stdout else ""
|
|
1905
|
-
stderr = "命令执行超时(30秒)\n" + (e.stderr.decode("utf-8", errors="replace") if e.stderr else "")
|
|
1906
|
-
typer.secho("[c2rust-transpiler][build] Cargo 测试超时(30秒),视为失败并继续修复流程", fg=typer.colors.YELLOW)
|
|
1907
|
-
except Exception as e:
|
|
1908
|
-
# 其他异常也视为测试失败
|
|
1909
|
-
returncode = -1
|
|
1910
|
-
stdout = ""
|
|
1911
|
-
stderr = f"执行 cargo test 时发生异常: {str(e)}"
|
|
1912
|
-
typer.secho(f"[c2rust-transpiler][build] Cargo 测试执行异常: {e},视为失败并继续修复流程", fg=typer.colors.YELLOW)
|
|
1913
|
-
|
|
1914
|
-
if returncode == 0:
|
|
1915
|
-
typer.secho("[c2rust-transpiler][build] Cargo 测试通过。", fg=typer.colors.GREEN)
|
|
1916
|
-
# 测试通过,重置连续失败计数
|
|
1917
|
-
self._consecutive_fix_failures = 0
|
|
1918
|
-
try:
|
|
1919
|
-
cur = self.progress.get("current") or {}
|
|
1920
|
-
metrics = cur.get("metrics") or {}
|
|
1921
|
-
metrics["test_attempts"] = int(test_iter)
|
|
1922
|
-
cur["metrics"] = metrics
|
|
1923
|
-
cur["impl_verified"] = True
|
|
1924
|
-
cur["failed_stage"] = None
|
|
1925
|
-
self.progress["current"] = cur
|
|
1926
|
-
self._save_progress()
|
|
1927
|
-
except Exception:
|
|
1928
|
-
pass
|
|
1929
|
-
return (True, False)
|
|
1930
|
-
|
|
1931
|
-
# 测试失败
|
|
1932
|
-
output = stdout + "\n" + stderr
|
|
1933
|
-
limit_info = f" (上限: {self.test_max_retries if self.test_max_retries > 0 else '无限'})" if test_iter % 10 == 0 or test_iter == 1 else ""
|
|
1934
|
-
typer.secho(f"[c2rust-transpiler][build] Cargo 测试失败 (第 {test_iter} 次尝试{limit_info})。", fg=typer.colors.RED)
|
|
1935
|
-
typer.secho(output, fg=typer.colors.RED)
|
|
1936
|
-
maxr = self.test_max_retries
|
|
1937
|
-
if maxr > 0 and test_iter >= maxr:
|
|
1938
|
-
typer.secho(f"[c2rust-transpiler][build] 已达到最大重试次数上限({maxr}),停止构建修复循环。", fg=typer.colors.RED)
|
|
1939
|
-
try:
|
|
1940
|
-
cur = self.progress.get("current") or {}
|
|
1941
|
-
metrics = cur.get("metrics") or {}
|
|
1942
|
-
metrics["test_attempts"] = int(test_iter)
|
|
1943
|
-
cur["metrics"] = metrics
|
|
1944
|
-
cur["impl_verified"] = False
|
|
1945
|
-
cur["failed_stage"] = "test"
|
|
1946
|
-
err_summary = (output or "").strip()
|
|
1947
|
-
if len(err_summary) > ERROR_SUMMARY_MAX_LENGTH:
|
|
1948
|
-
err_summary = err_summary[:ERROR_SUMMARY_MAX_LENGTH] + "...(truncated)"
|
|
1949
|
-
cur["last_build_error"] = err_summary
|
|
1950
|
-
self.progress["current"] = cur
|
|
1951
|
-
self._save_progress()
|
|
1952
|
-
except Exception:
|
|
1953
|
-
pass
|
|
1954
|
-
return (False, False)
|
|
1955
|
-
|
|
1956
|
-
# 构建失败(测试阶段)修复
|
|
1957
|
-
tags = self._classify_rust_error(output)
|
|
1958
|
-
symbols_path = str((self.data_dir / "symbols.jsonl").resolve())
|
|
1959
|
-
curr, sym_name, src_loc, c_code = self._get_current_function_context()
|
|
1960
|
-
|
|
1961
|
-
# 调试输出:确认测试失败信息是否正确传递
|
|
1962
|
-
typer.secho(f"[c2rust-transpiler][debug] 测试失败信息长度: {len(output)} 字符", fg=typer.colors.CYAN)
|
|
1963
|
-
if output:
|
|
1964
|
-
# 提取关键错误信息用于调试
|
|
1965
|
-
error_lines = output.split('\n')
|
|
1966
|
-
key_errors = [line for line in error_lines if any(keyword in line.lower() for keyword in ['failed', 'error', 'panic', 'unwrap', 'sequence'])]
|
|
1967
|
-
if key_errors:
|
|
1968
|
-
typer.secho("[c2rust-transpiler][debug] 关键错误信息(前5行):", fg=typer.colors.CYAN)
|
|
1969
|
-
for i, line in enumerate(key_errors[:5], 1):
|
|
1970
|
-
typer.secho(f" {i}. {line[:100]}", fg=typer.colors.CYAN)
|
|
1971
|
-
|
|
1972
|
-
repair_prompt = self._build_repair_prompt(
|
|
1973
|
-
stage="cargo test",
|
|
1974
|
-
output=output,
|
|
1975
|
-
tags=tags,
|
|
1976
|
-
sym_name=sym_name,
|
|
1977
|
-
src_loc=src_loc,
|
|
1978
|
-
c_code=c_code,
|
|
1979
|
-
curr=curr,
|
|
1980
|
-
symbols_path=symbols_path,
|
|
1981
|
-
include_output_patch_hint=True,
|
|
1982
|
-
command="cargo test -- --nocapture",
|
|
1983
|
-
)
|
|
1984
|
-
# 由于 transpile() 开始时已切换到 crate 目录,此处无需再次切换
|
|
1985
|
-
# 记录运行前的 commit
|
|
1986
|
-
before_commit = self._get_crate_commit_hash()
|
|
1987
|
-
agent = self._get_code_agent()
|
|
1988
|
-
agent.run(self._compose_prompt_with_context(repair_prompt), prefix=f"[c2rust-transpiler][build-fix iter={test_iter}][test]", suffix="")
|
|
1989
|
-
|
|
1990
|
-
# 检测并处理测试代码删除
|
|
1991
|
-
if self._check_and_handle_test_deletion(before_commit, agent):
|
|
1992
|
-
# 如果回退了,需要重新运行 agent
|
|
1993
|
-
typer.secho(f"[c2rust-transpiler][build-fix] 检测到测试代码删除问题,已回退,重新运行 agent (iter={test_iter})", fg=typer.colors.YELLOW)
|
|
1994
|
-
before_commit = self._get_crate_commit_hash()
|
|
1995
|
-
agent.run(self._compose_prompt_with_context(repair_prompt), prefix=f"[c2rust-transpiler][build-fix iter={test_iter}][test][retry]", suffix="")
|
|
1996
|
-
# 再次检测
|
|
1997
|
-
if self._check_and_handle_test_deletion(before_commit, agent):
|
|
1998
|
-
typer.secho(f"[c2rust-transpiler][build-fix] 再次检测到测试代码删除问题,已回退 (iter={test_iter})", fg=typer.colors.RED)
|
|
1999
|
-
|
|
2000
|
-
# 修复后验证:先检查编译,再实际运行测试
|
|
2001
|
-
# 第一步:检查编译是否通过
|
|
2002
|
-
res_compile = subprocess.run(
|
|
2003
|
-
["cargo", "test", "--message-format=short", "-q", "--no-run"],
|
|
2004
|
-
capture_output=True,
|
|
2005
|
-
text=True,
|
|
2006
|
-
check=False,
|
|
2007
|
-
cwd=workspace_root,
|
|
667
|
+
def _run_cargo_test_and_fix(
|
|
668
|
+
self, workspace_root: str, test_iter: int
|
|
669
|
+
) -> Tuple[bool, Optional[bool]]:
|
|
670
|
+
"""运行 cargo test 并在失败时修复(委托给 BuildManager)"""
|
|
671
|
+
if self.build_manager is None:
|
|
672
|
+
self._init_build_manager()
|
|
673
|
+
return (
|
|
674
|
+
self.build_manager.run_cargo_test_and_fix(workspace_root, test_iter)
|
|
675
|
+
if self.build_manager is not None
|
|
676
|
+
else (False, None)
|
|
2008
677
|
)
|
|
2009
|
-
if res_compile.returncode != 0:
|
|
2010
|
-
typer.secho("[c2rust-transpiler][build] 修复后编译仍有错误,将在下一轮循环中处理", fg=typer.colors.YELLOW)
|
|
2011
|
-
# 编译失败,增加连续失败计数
|
|
2012
|
-
self._consecutive_fix_failures += 1
|
|
2013
|
-
# 检查是否需要回退
|
|
2014
|
-
if self._consecutive_fix_failures >= CONSECUTIVE_FIX_FAILURE_THRESHOLD and self._current_function_start_commit:
|
|
2015
|
-
typer.secho(f"[c2rust-transpiler][build] 连续修复失败 {self._consecutive_fix_failures} 次,回退到函数开始时的 commit: {self._current_function_start_commit}", fg=typer.colors.RED)
|
|
2016
|
-
if self._reset_to_commit(self._current_function_start_commit):
|
|
2017
|
-
typer.secho("[c2rust-transpiler][build] 已回退到函数开始时的 commit,将重新开始处理该函数", fg=typer.colors.YELLOW)
|
|
2018
|
-
# 返回特殊值,表示需要重新开始
|
|
2019
|
-
return (False, None) # type: ignore
|
|
2020
|
-
else:
|
|
2021
|
-
typer.secho("[c2rust-transpiler][build] 回退失败,继续尝试修复", fg=typer.colors.YELLOW)
|
|
2022
|
-
return (False, False) # 需要继续循环
|
|
2023
|
-
|
|
2024
|
-
# 第二步:编译通过,实际运行测试验证
|
|
2025
|
-
try:
|
|
2026
|
-
res_test_verify = subprocess.run(
|
|
2027
|
-
["cargo", "test", "--", "--nocapture"],
|
|
2028
|
-
capture_output=True,
|
|
2029
|
-
text=True,
|
|
2030
|
-
timeout=30,
|
|
2031
|
-
check=False,
|
|
2032
|
-
cwd=workspace_root,
|
|
2033
|
-
)
|
|
2034
|
-
verify_returncode = res_test_verify.returncode
|
|
2035
|
-
except subprocess.TimeoutExpired:
|
|
2036
|
-
# 超时视为测试失败
|
|
2037
|
-
verify_returncode = -1
|
|
2038
|
-
typer.secho("[c2rust-transpiler][build] 修复后验证测试超时(30秒),视为失败", fg=typer.colors.YELLOW)
|
|
2039
|
-
except Exception as e:
|
|
2040
|
-
# 其他异常也视为测试失败
|
|
2041
|
-
verify_returncode = -1
|
|
2042
|
-
typer.secho(f"[c2rust-transpiler][build] 修复后验证测试执行异常: {e},视为失败", fg=typer.colors.YELLOW)
|
|
2043
|
-
|
|
2044
|
-
if verify_returncode == 0:
|
|
2045
|
-
typer.secho("[c2rust-transpiler][build] 修复后测试通过,继续构建循环", fg=typer.colors.GREEN)
|
|
2046
|
-
# 测试真正通过,重置连续失败计数
|
|
2047
|
-
self._consecutive_fix_failures = 0
|
|
2048
|
-
return (False, False) # 需要继续循环(但下次应该会通过)
|
|
2049
|
-
else:
|
|
2050
|
-
# 编译通过但测试仍然失败,说明修复没有解决测试逻辑问题
|
|
2051
|
-
typer.secho("[c2rust-transpiler][build] 修复后编译通过,但测试仍然失败,将在下一轮循环中处理", fg=typer.colors.YELLOW)
|
|
2052
|
-
# 测试失败,增加连续失败计数(即使编译通过)
|
|
2053
|
-
self._consecutive_fix_failures += 1
|
|
2054
|
-
# 检查是否需要回退
|
|
2055
|
-
if self._consecutive_fix_failures >= CONSECUTIVE_FIX_FAILURE_THRESHOLD and self._current_function_start_commit:
|
|
2056
|
-
typer.secho(f"[c2rust-transpiler][build] 连续修复失败 {self._consecutive_fix_failures} 次(编译通过但测试失败),回退到函数开始时的 commit: {self._current_function_start_commit}", fg=typer.colors.RED)
|
|
2057
|
-
if self._reset_to_commit(self._current_function_start_commit):
|
|
2058
|
-
typer.secho("[c2rust-transpiler][build] 已回退到函数开始时的 commit,将重新开始处理该函数", fg=typer.colors.YELLOW)
|
|
2059
|
-
# 返回特殊值,表示需要重新开始
|
|
2060
|
-
return (False, None) # type: ignore
|
|
2061
|
-
else:
|
|
2062
|
-
typer.secho("[c2rust-transpiler][build] 回退失败,继续尝试修复", fg=typer.colors.YELLOW)
|
|
2063
|
-
return (False, False) # 需要继续循环
|
|
2064
678
|
|
|
2065
679
|
def _cargo_build_loop(self) -> Optional[bool]:
|
|
2066
|
-
"""在 crate
|
|
2067
|
-
|
|
2068
|
-
|
|
2069
|
-
|
|
2070
|
-
|
|
2071
|
-
|
|
2072
|
-
|
|
2073
|
-
|
|
2074
|
-
|
|
2075
|
-
|
|
2076
|
-
|
|
2077
|
-
|
|
2078
|
-
|
|
2079
|
-
|
|
680
|
+
"""在 crate 目录执行构建与测试(委托给 BuildManager)"""
|
|
681
|
+
if self.build_manager is None:
|
|
682
|
+
self._init_build_manager()
|
|
683
|
+
result = (
|
|
684
|
+
self.build_manager.cargo_build_loop()
|
|
685
|
+
if self.build_manager is not None
|
|
686
|
+
else None
|
|
687
|
+
)
|
|
688
|
+
# 保存修复标记,供调用方检查
|
|
689
|
+
self._build_loop_has_fixes = getattr(
|
|
690
|
+
self.build_manager, "_build_loop_has_fixes", False
|
|
691
|
+
)
|
|
692
|
+
return result
|
|
693
|
+
|
|
694
|
+
def _get_build_loop_has_fixes(self) -> bool:
|
|
695
|
+
"""获取构建循环中是否进行了修复"""
|
|
696
|
+
return getattr(self, "_build_loop_has_fixes", False)
|
|
2080
697
|
|
|
2081
698
|
def _review_and_optimize(self, rec: FnRecord, module: str, rust_sig: str) -> None:
|
|
2082
|
-
"""
|
|
2083
|
-
|
|
2084
|
-
合并了功能一致性审查和类型/边界严重问题审查,避免重复审查。
|
|
2085
|
-
审查只关注本次函数与相关最小上下文,避免全局重构。
|
|
2086
|
-
"""
|
|
2087
|
-
def build_review_prompts() -> Tuple[str, str, str]:
|
|
2088
|
-
sys_p = (
|
|
2089
|
-
"你是Rust代码审查专家。验收标准:Rust 实现应与原始 C 实现在功能上一致,且不应包含可能导致功能错误的严重问题。\n"
|
|
2090
|
-
"**审查优先级**:严重问题 > 破坏性变更 > 功能一致性 > 文件结构。优先处理可能导致程序崩溃或编译失败的问题。\n"
|
|
2091
|
-
"**审查范围**:主要审查当前函数的实现,相关依赖函数作为辅助参考。\n"
|
|
2092
|
-
"审查标准(合并了功能一致性和严重问题检查):\n"
|
|
2093
|
-
"1. 功能一致性检查:\n"
|
|
2094
|
-
" - **核心功能定义**:核心输入输出、主要功能逻辑是否与 C 实现一致。核心功能指函数的主要目的和预期行为(如'计算哈希值'、'解析字符串'、'压缩数据'等),不包括实现细节;\n"
|
|
2095
|
-
" - **安全改进允许行为不一致**:允许 Rust 实现修复 C 代码中的安全漏洞(如缓冲区溢出、空指针解引用、未初始化内存使用、整数溢出、格式化字符串漏洞等),这些安全改进可能导致行为与 C 实现不一致,但这是允许的,不应被视为功能不一致;\n"
|
|
2096
|
-
" - **忽略语言差异导致的行为不一致**:由于 Rust 和 C 语言的本质差异,以下行为差异是不可避免的,应被忽略:\n"
|
|
2097
|
-
" * 整数溢出处理:Rust 在 debug 模式下会 panic,release 模式下会 wrapping,而 C 是未定义行为;\n"
|
|
2098
|
-
" * 未定义行为:Rust 会避免或明确处理,而 C 可能产生未定义行为;\n"
|
|
2099
|
-
" * 空指针/空引用:Rust 使用 Option<T> 或 Result<T, E> 处理,而 C 可能直接解引用导致崩溃;\n"
|
|
2100
|
-
" * 内存安全:Rust 的借用检查器会阻止某些 C 中允许的不安全操作;\n"
|
|
2101
|
-
" * 错误处理:Rust 使用 Result<T, E> 或 Option<T>,而 C 可能使用错误码或全局 errno;\n"
|
|
2102
|
-
" - 允许 Rust 实现使用不同的类型设计、错误处理方式、资源管理方式等,只要功能一致即可;\n"
|
|
2103
|
-
"2. 严重问题检查(可能导致功能错误或程序崩溃):\n"
|
|
2104
|
-
" - 明显的空指针解引用或会导致 panic 的严重错误;\n"
|
|
2105
|
-
" - 明显的越界访问或会导致程序崩溃的问题;\n"
|
|
2106
|
-
" - 会导致程序无法正常运行的逻辑错误;\n"
|
|
2107
|
-
"3. 破坏性变更检测(对现有代码的影响):\n"
|
|
2108
|
-
" - **允许签名不一致**:允许函数签名、参数数量、参数类型、返回类型等与C实现不一致,只要功能实现了即可。这是Rust转译的正常现象,因为Rust的类型系统和设计理念与C不同;\n"
|
|
2109
|
-
" - **仅检查实际破坏性影响**:只有当函数签名变更确实导致调用方代码无法编译或运行时,才报告为破坏性变更。如果调用方代码已经适配了新签名,或可以通过简单的适配解决,则不应视为破坏性变更;\n"
|
|
2110
|
-
" - **⚠️ 重要:检查测试用例删除**:必须检查代码变更中是否错误删除了测试用例标记(#[test] 或 #[cfg(test)])。如果发现删除了测试用例标记,必须报告为破坏性变更,除非:\n"
|
|
2111
|
-
" * 测试用例被移动到其他位置(在diff中可以看到对应的添加);\n"
|
|
2112
|
-
" * 测试用例是重复的或过时的,确实需要删除;\n"
|
|
2113
|
-
" * 测试用例被重构为其他形式的测试(如集成测试、文档测试等);\n"
|
|
2114
|
-
" - 检查模块导出变更是否会影响其他模块的导入(如 pub 关键字缺失、模块路径变更);\n"
|
|
2115
|
-
" - 检查类型定义变更是否会导致依赖该类型的代码失效(如结构体字段变更、枚举变体变更);\n"
|
|
2116
|
-
" - 检查常量或静态变量变更是否会影响引用该常量的代码;\n"
|
|
2117
|
-
" - **优先使用diff信息**:如果diff中已包含调用方代码信息,优先基于diff判断;只有在diff信息不足时,才使用 read_code 工具读取调用方代码进行验证;\n"
|
|
2118
|
-
"4. 文件结构合理性检查:\n"
|
|
2119
|
-
" - 检查模块文件位置是否符合 Rust 项目约定(如 src/ 目录结构、模块层次);\n"
|
|
2120
|
-
" - 检查文件命名是否符合 Rust 命名规范(如 snake_case、模块文件命名);\n"
|
|
2121
|
-
" - 检查模块组织是否合理(如相关功能是否放在同一模块、模块拆分是否过度或不足);\n"
|
|
2122
|
-
" - 检查模块导出是否合理(如 lib.rs 中的 pub mod 声明是否正确、是否遗漏必要的导出);\n"
|
|
2123
|
-
" - 检查是否存在循环依赖或过度耦合;\n"
|
|
2124
|
-
"不检查类型匹配、指针可变性、边界检查细节、资源释放细节、内存语义等技术细节(除非会导致功能错误)。\n"
|
|
2125
|
-
"**重要要求:在总结阶段,对于发现的每个问题,必须提供:**\n"
|
|
2126
|
-
"1. 详细的问题描述:明确指出问题所在的位置(文件、函数、行号等)、问题的具体表现、为什么这是一个问题\n"
|
|
2127
|
-
"2. 具体的修复建议:提供详细的修复方案,包括需要修改的代码位置、修改方式、预期效果等\n"
|
|
2128
|
-
"3. 问题分类:使用 [function] 标记功能一致性问题,使用 [critical] 标记严重问题,使用 [breaking] 标记破坏性变更,使用 [structure] 标记文件结构问题\n"
|
|
2129
|
-
"请在总结阶段详细指出问题和修改建议,但不要尝试修复或修改任何代码,不要输出补丁。"
|
|
2130
|
-
)
|
|
2131
|
-
# 附加原始C函数源码片段,供审查作为只读参考
|
|
2132
|
-
c_code = self._read_source_span(rec) or ""
|
|
2133
|
-
# 附加被引用符号上下文与库替代上下文,以及crate目录结构,提供更完整审查背景
|
|
2134
|
-
callees_ctx = self._collect_callees_context(rec)
|
|
2135
|
-
librep_ctx = rec.lib_replacement if isinstance(rec.lib_replacement, dict) else None
|
|
2136
|
-
crate_tree = dir_tree(self.crate_dir)
|
|
2137
|
-
# 提取编译参数
|
|
2138
|
-
compile_flags = self._extract_compile_flags(rec.file)
|
|
2139
|
-
|
|
2140
|
-
# 获取从初始commit到当前commit的变更作为上下文(每次review都必须获取)
|
|
2141
|
-
commit_diff = ""
|
|
2142
|
-
diff_status = "" # 用于记录diff获取状态
|
|
2143
|
-
if self._current_function_start_commit:
|
|
2144
|
-
current_commit = self._get_crate_commit_hash()
|
|
2145
|
-
if current_commit:
|
|
2146
|
-
if current_commit == self._current_function_start_commit:
|
|
2147
|
-
# commit相同,说明没有变更
|
|
2148
|
-
commit_diff = "(无变更:当前commit与函数开始时的commit相同)"
|
|
2149
|
-
diff_status = "no_change"
|
|
2150
|
-
else:
|
|
2151
|
-
# commit不同,获取diff
|
|
2152
|
-
try:
|
|
2153
|
-
# 注意:transpile()开始时已切换到crate目录,此处无需再次切换
|
|
2154
|
-
commit_diff = get_diff_between_commits(self._current_function_start_commit, current_commit)
|
|
2155
|
-
if commit_diff and not commit_diff.startswith("获取") and not commit_diff.startswith("发生"):
|
|
2156
|
-
# 成功获取diff,限制长度避免上下文过大
|
|
2157
|
-
# 优先使用agent的剩余token数量,回退到输入窗口限制
|
|
2158
|
-
max_diff_chars = None
|
|
2159
|
-
try:
|
|
2160
|
-
# 优先尝试使用已有的agent获取剩余token(更准确,包含对话历史)
|
|
2161
|
-
review_key = f"review::{rec.id}"
|
|
2162
|
-
agent = self._current_agents.get(review_key)
|
|
2163
|
-
if agent:
|
|
2164
|
-
remaining_tokens = agent.get_remaining_token_count()
|
|
2165
|
-
# 使用剩余token的50%作为字符限制(1 token ≈ 4字符,所以 remaining_tokens * 0.5 * 4 = remaining_tokens * 2)
|
|
2166
|
-
max_diff_chars = int(remaining_tokens * 2)
|
|
2167
|
-
if max_diff_chars <= 0:
|
|
2168
|
-
max_diff_chars = None
|
|
2169
|
-
except Exception:
|
|
2170
|
-
pass
|
|
2171
|
-
|
|
2172
|
-
# 回退方案2:使用输入窗口的50%转换为字符数
|
|
2173
|
-
if max_diff_chars is None:
|
|
2174
|
-
max_input_tokens = get_max_input_token_count(self.llm_group)
|
|
2175
|
-
max_diff_chars = max_input_tokens * 2 # 最大输入token数量的一半转换为字符数
|
|
2176
|
-
|
|
2177
|
-
if len(commit_diff) > max_diff_chars:
|
|
2178
|
-
commit_diff = commit_diff[:max_diff_chars] + "\n... (差异内容过长,已截断)"
|
|
2179
|
-
diff_status = "success"
|
|
2180
|
-
else:
|
|
2181
|
-
# 获取失败,保留错误信息
|
|
2182
|
-
diff_status = "error"
|
|
2183
|
-
typer.secho(f"[c2rust-transpiler][review] 获取commit差异失败: {commit_diff}", fg=typer.colors.YELLOW)
|
|
2184
|
-
except Exception as e:
|
|
2185
|
-
commit_diff = f"获取commit差异时发生异常: {str(e)}"
|
|
2186
|
-
diff_status = "error"
|
|
2187
|
-
typer.secho(f"[c2rust-transpiler][review] 获取commit差异失败: {e}", fg=typer.colors.YELLOW)
|
|
2188
|
-
else:
|
|
2189
|
-
# 无法获取当前commit
|
|
2190
|
-
commit_diff = "(无法获取当前commit id)"
|
|
2191
|
-
diff_status = "no_current_commit"
|
|
2192
|
-
else:
|
|
2193
|
-
# 没有保存函数开始时的commit
|
|
2194
|
-
commit_diff = "(未记录函数开始时的commit id)"
|
|
2195
|
-
diff_status = "no_start_commit"
|
|
2196
|
-
|
|
2197
|
-
usr_p_lines = [
|
|
2198
|
-
f"待审查函数:{rec.qname or rec.name}",
|
|
2199
|
-
f"建议签名:{rust_sig}",
|
|
2200
|
-
f"目标模块:{module}",
|
|
2201
|
-
f"crate根目录路径:{self.crate_dir.resolve()}",
|
|
2202
|
-
f"源文件位置:{rec.file}:{rec.start_line}-{rec.end_line}",
|
|
2203
|
-
"",
|
|
2204
|
-
"原始C函数源码片段(只读参考,不要修改C代码):",
|
|
2205
|
-
"<C_SOURCE>",
|
|
2206
|
-
c_code,
|
|
2207
|
-
"</C_SOURCE>",
|
|
2208
|
-
"",
|
|
2209
|
-
"审查说明(合并审查):",
|
|
2210
|
-
"**审查优先级**:严重问题 > 破坏性变更 > 功能一致性 > 文件结构。优先处理可能导致程序崩溃或编译失败的问题。",
|
|
2211
|
-
"",
|
|
2212
|
-
"1. 功能一致性:",
|
|
2213
|
-
" - **核心功能定义**:核心输入输出、主要功能逻辑是否与 C 实现一致。核心功能指函数的主要目的和预期行为(如'计算哈希值'、'解析字符串'、'压缩数据'等),不包括实现细节;",
|
|
2214
|
-
" - **安全改进允许行为不一致**:允许Rust实现修复C代码中的安全漏洞(如缓冲区溢出、空指针解引用、未初始化内存使用、整数溢出、格式化字符串漏洞等),这些安全改进可能导致行为与 C 实现不一致,但这是允许的,不应被视为功能不一致;",
|
|
2215
|
-
" - **忽略语言差异导致的行为不一致**:由于 Rust 和 C 语言的本质差异,以下行为差异是不可避免的,应被忽略:",
|
|
2216
|
-
" * 整数溢出处理:Rust 在 debug 模式下会 panic,release 模式下会 wrapping,而 C 是未定义行为;",
|
|
2217
|
-
" * 未定义行为:Rust 会避免或明确处理,而 C 可能产生未定义行为;",
|
|
2218
|
-
" * 空指针/空引用:Rust 使用 Option<T> 或 Result<T, E> 处理,而 C 可能直接解引用导致崩溃;",
|
|
2219
|
-
" * 内存安全:Rust 的借用检查器会阻止某些 C 中允许的不安全操作;",
|
|
2220
|
-
" * 错误处理:Rust 使用 Result<T, E> 或 Option<T>,而 C 可能使用错误码或全局 errno;",
|
|
2221
|
-
" - 允许Rust实现使用不同的类型设计、错误处理方式、资源管理方式等,只要功能一致即可;",
|
|
2222
|
-
"2. 严重问题(可能导致功能错误):",
|
|
2223
|
-
" - 明显的空指针解引用或会导致 panic 的严重错误;",
|
|
2224
|
-
" - 明显的越界访问或会导致程序崩溃的问题;",
|
|
2225
|
-
"3. 破坏性变更检测(对现有代码的影响):",
|
|
2226
|
-
" - **允许签名不一致**:允许函数签名、参数数量、参数类型、返回类型等与C实现不一致,只要功能实现了即可。这是Rust转译的正常现象,因为Rust的类型系统和设计理念与C不同;",
|
|
2227
|
-
" - **仅检查实际破坏性影响**:只有当函数签名变更确实导致调用方代码无法编译或运行时,才报告为破坏性变更。如果调用方代码已经适配了新签名,或可以通过简单的适配解决,则不应视为破坏性变更;",
|
|
2228
|
-
" - **⚠️ 重要:检查测试用例删除**:必须检查代码变更中是否错误删除了测试用例标记(#[test] 或 #[cfg(test)])。如果发现删除了测试用例标记,必须报告为破坏性变更,除非:",
|
|
2229
|
-
" * 测试用例被移动到其他位置(在diff中可以看到对应的添加);",
|
|
2230
|
-
" * 测试用例是重复的或过时的,确实需要删除;",
|
|
2231
|
-
" * 测试用例被重构为其他形式的测试(如集成测试、文档测试等);",
|
|
2232
|
-
" - 检查模块导出变更是否会影响其他模块的导入(如 pub 关键字缺失、模块路径变更);",
|
|
2233
|
-
" - 检查类型定义变更是否会导致依赖该类型的代码失效(如结构体字段变更、枚举变体变更);",
|
|
2234
|
-
" - 检查常量或静态变量变更是否会影响引用该常量的代码;",
|
|
2235
|
-
" - **优先使用diff信息**:如果diff中已包含调用方代码信息,优先基于diff判断;只有在diff信息不足时,才使用 read_code 工具读取调用方代码进行验证;",
|
|
2236
|
-
" - 如果该函数是根符号或被其他已转译函数调用,检查调用方代码是否仍能正常编译和使用;如果调用方已经适配了新签名,则不应视为破坏性变更;",
|
|
2237
|
-
"4. 文件结构合理性检查:",
|
|
2238
|
-
" - 检查模块文件位置是否符合 Rust 项目约定(如 src/ 目录结构、模块层次);",
|
|
2239
|
-
" - 检查文件命名是否符合 Rust 命名规范(如 snake_case、模块文件命名);",
|
|
2240
|
-
" - 检查模块组织是否合理(如相关功能是否放在同一模块、模块拆分是否过度或不足);",
|
|
2241
|
-
" - 检查模块导出是否合理(如 lib.rs 中的 pub mod 声明是否正确、是否遗漏必要的导出);",
|
|
2242
|
-
" - 检查是否存在循环依赖或过度耦合;",
|
|
2243
|
-
" - 检查文件大小是否合理(如单个文件是否过大需要拆分,或是否过度拆分导致文件过多);",
|
|
2244
|
-
"不检查类型匹配、指针可变性、边界检查细节等技术细节(除非会导致功能错误)。",
|
|
2245
|
-
"",
|
|
2246
|
-
"**重要:问题报告要求**",
|
|
2247
|
-
"对于发现的每个问题,必须在总结中提供:",
|
|
2248
|
-
"1. 详细的问题描述:明确指出问题所在的位置(文件、函数、行号等)、问题的具体表现、为什么这是一个问题",
|
|
2249
|
-
"2. 具体的修复建议:提供详细的修复方案,包括需要修改的代码位置、修改方式、预期效果等",
|
|
2250
|
-
"3. 问题分类:使用 [function] 标记功能一致性问题,使用 [critical] 标记严重问题,使用 [breaking] 标记破坏性变更,使用 [structure] 标记文件结构问题",
|
|
2251
|
-
"示例:",
|
|
2252
|
-
' "[function] 返回值处理缺失:在函数 foo 的第 42 行,当输入参数为负数时,函数没有返回错误码,但 C 实现中会返回 -1。修复建议:在函数开始处添加参数验证,当参数为负数时返回 Result::Err(Error::InvalidInput)。"',
|
|
2253
|
-
' "[critical] 空指针解引用风险:在函数 bar 的第 58 行,直接解引用指针 ptr 而没有检查其是否为 null,可能导致 panic。修复建议:使用 if let Some(value) = ptr.as_ref() 进行空指针检查,或使用 Option<&T> 类型。"',
|
|
2254
|
-
' "[breaking] 函数签名变更导致调用方无法编译:函数 baz 的签名从 `fn baz(x: i32) -> i32` 变更为 `fn baz(x: i64) -> i64`,但调用方代码(src/other.rs:15)仍使用 i32 类型调用,且无法通过简单适配解决,会导致类型不匹配错误。修复建议:保持函数签名与调用方兼容,或同时更新所有调用方代码。注意:如果调用方已经适配了新签名,或可以通过简单的类型转换解决,则不应视为破坏性变更。"',
|
|
2255
|
-
' "[breaking] 测试用例被错误删除:在 diff 中发现删除了测试用例标记 `#[test]` 或 `#[cfg(test)]`(如 src/foo.rs:42),但没有看到对应的添加或移动,这可能导致测试无法运行。修复建议:恢复被删除的测试用例标记,或如果确实需要删除,请说明原因(如测试用例已移动到其他位置、测试用例是重复的等)。"',
|
|
2256
|
-
' "[structure] 模块导出缺失:函数 qux 所在的模块 utils 未在 src/lib.rs 中导出,导致无法从 crate 外部访问。修复建议:在 src/lib.rs 中添加 `pub mod utils;` 声明。"',
|
|
2257
|
-
"",
|
|
2258
|
-
"被引用符号上下文(如已转译则包含Rust模块信息):",
|
|
2259
|
-
json.dumps(callees_ctx, ensure_ascii=False, indent=2),
|
|
2260
|
-
"",
|
|
2261
|
-
"库替代上下文(若存在):",
|
|
2262
|
-
json.dumps(librep_ctx, ensure_ascii=False, indent=2),
|
|
2263
|
-
"",
|
|
2264
|
-
*([f"禁用库列表(禁止在实现中使用这些库):{', '.join(self.disabled_libraries)}"] if self.disabled_libraries else []),
|
|
2265
|
-
*([f"根符号要求:此函数是根符号({rec.qname or rec.name}),必须使用 `pub` 关键字对外暴露,确保可以从 crate 外部访问。同时,该函数所在的模块必须在 src/lib.rs 中被导出(使用 `pub mod <模块名>;`)。"] if self._is_root_symbol(rec) else []),
|
|
2266
|
-
]
|
|
2267
|
-
# 添加编译参数(如果存在)
|
|
2268
|
-
if compile_flags:
|
|
2269
|
-
usr_p_lines.extend([
|
|
2270
|
-
"",
|
|
2271
|
-
"C文件编译参数(来自 compile_commands.json):",
|
|
2272
|
-
compile_flags,
|
|
2273
|
-
])
|
|
2274
|
-
usr_p_lines.extend([
|
|
2275
|
-
"",
|
|
2276
|
-
"当前crate目录结构(部分):",
|
|
2277
|
-
"<CRATE_TREE>",
|
|
2278
|
-
crate_tree,
|
|
2279
|
-
"</CRATE_TREE>",
|
|
2280
|
-
])
|
|
2281
|
-
|
|
2282
|
-
# 添加commit变更上下文(每次review都必须包含)
|
|
2283
|
-
usr_p_lines.extend([
|
|
2284
|
-
"",
|
|
2285
|
-
"从函数开始到当前的commit变更(用于了解代码变更历史和上下文):",
|
|
2286
|
-
"<COMMIT_DIFF>",
|
|
2287
|
-
commit_diff,
|
|
2288
|
-
"</COMMIT_DIFF>",
|
|
2289
|
-
"",
|
|
2290
|
-
])
|
|
2291
|
-
|
|
2292
|
-
# 根据diff状态添加不同的说明
|
|
2293
|
-
if diff_status == "success":
|
|
2294
|
-
usr_p_lines.extend([
|
|
2295
|
-
"**重要:commit变更上下文说明**",
|
|
2296
|
-
"- 上述diff显示了从函数开始处理时的commit到当前commit之间的所有变更",
|
|
2297
|
-
"- 这些变更可能包括:当前函数的实现、依赖函数的实现、模块结构的调整等",
|
|
2298
|
-
"- **优先使用diff信息进行审查判断**:如果diff中已经包含了足够的信息(如函数实现、签名变更、模块结构等),可以直接基于diff进行审查,无需读取原始文件",
|
|
2299
|
-
"- 只有在diff信息不足或需要查看完整上下文时,才使用 read_code 工具读取原始文件",
|
|
2300
|
-
"- 在审查破坏性变更时,请特别关注这些变更对现有代码的影响",
|
|
2301
|
-
"- 如果发现变更中存在问题(如破坏性变更、文件结构不合理等),请在审查报告中指出",
|
|
2302
|
-
])
|
|
2303
|
-
elif diff_status == "no_change":
|
|
2304
|
-
usr_p_lines.extend([
|
|
2305
|
-
"**注意**:当前commit与函数开始时的commit相同,说明没有代码变更。请使用 read_code 工具读取目标模块文件的最新内容进行审查。",
|
|
2306
|
-
])
|
|
2307
|
-
else:
|
|
2308
|
-
# diff_status 为 "error"、"no_current_commit" 或 "no_start_commit"
|
|
2309
|
-
usr_p_lines.extend([
|
|
2310
|
-
"**注意**:由于无法获取commit差异信息,请使用 read_code 工具读取目标模块文件的最新内容进行审查。",
|
|
2311
|
-
])
|
|
2312
|
-
|
|
2313
|
-
usr_p_lines.extend([
|
|
2314
|
-
"",
|
|
2315
|
-
"如需定位或交叉验证 C 符号位置,请使用符号表检索工具:",
|
|
2316
|
-
"- 工具: read_symbols",
|
|
2317
|
-
"- 参数示例(JSON):",
|
|
2318
|
-
f" {{\"symbols_file\": \"{(self.data_dir / 'symbols.jsonl').resolve()}\", \"symbols\": [\"{rec.qname or rec.name}\"]}}",
|
|
2319
|
-
"",
|
|
2320
|
-
"**重要:审查要求**",
|
|
2321
|
-
"- **优先使用diff信息**:如果提供了commit差异(COMMIT_DIFF),优先基于diff信息进行审查判断,只有在diff信息不足时才使用 read_code 工具读取原始文件",
|
|
2322
|
-
"- 必须基于最新的代码进行审查,如果使用 read_code 工具,请读取目标模块文件的最新内容",
|
|
2323
|
-
"- 禁止依赖任何历史记忆、之前的审查结论或对话历史进行判断",
|
|
2324
|
-
"- 每次审查都必须基于最新的代码状态(通过diff或read_code获取),确保审查结果反映当前代码的真实状态",
|
|
2325
|
-
"- 结合commit变更上下文(如果提供),全面评估代码变更的影响和合理性",
|
|
2326
|
-
"",
|
|
2327
|
-
"请基于提供的diff信息(如果可用)或读取crate中该函数的当前实现进行审查,并准备总结。",
|
|
2328
|
-
])
|
|
2329
|
-
usr_p = "\n".join(usr_p_lines)
|
|
2330
|
-
sum_p = (
|
|
2331
|
-
"请仅输出一个 <SUMMARY> 块,块内直接包含 JSON 对象(不需要额外的标签),字段:\n"
|
|
2332
|
-
'"ok": bool // 若满足功能一致且无严重问题、无破坏性变更、文件结构合理,则为 true\n'
|
|
2333
|
-
'"function_issues": [string, ...] // 功能一致性问题,每项以 [function] 开头,必须包含详细的问题描述和修复建议\n'
|
|
2334
|
-
'"critical_issues": [string, ...] // 严重问题(可能导致功能错误),每项以 [critical] 开头,必须包含详细的问题描述和修复建议\n'
|
|
2335
|
-
'"breaking_issues": [string, ...] // 破坏性变更问题(对现有代码的影响),每项以 [breaking] 开头,必须包含详细的问题描述和修复建议\n'
|
|
2336
|
-
'"structure_issues": [string, ...] // 文件结构问题,每项以 [structure] 开头,必须包含详细的问题描述和修复建议\n'
|
|
2337
|
-
"注意:\n"
|
|
2338
|
-
"- 前置条件:必须在crate中找到该函数的实现(匹配函数名或签名)。若未找到,ok 必须为 false,function_issues 应包含 [function] function not found: 详细描述问题位置和如何查找函数实现\n"
|
|
2339
|
-
"- **安全改进允许行为不一致**:若Rust实现修复了C代码中的安全漏洞(如缓冲区溢出、空指针解引用、未初始化内存使用等),即使导致行为与 C 实现不一致,这也是允许的,不应被视为功能不一致;\n"
|
|
2340
|
-
"- **忽略语言差异导致的行为不一致**:由于 Rust 和 C 语言的本质差异(如内存安全、类型系统、错误处理、未定义行为处理等),某些行为差异是不可避免的,这些差异应被忽略,不应被视为功能不一致;\n"
|
|
2341
|
-
"- **允许签名不一致**:允许函数签名、参数数量、参数类型、返回类型等与C实现不一致,只要功能实现了即可。这是Rust转译的正常现象,不应被视为破坏性变更;\n"
|
|
2342
|
-
"- **破坏性变更判断标准**:只有当函数签名变更确实导致调用方代码无法编译或运行时,才报告为破坏性变更。如果调用方代码已经适配了新签名,或可以通过简单的适配解决,则不应视为破坏性变更;\n"
|
|
2343
|
-
"- **⚠️ 重要:测试用例删除检查**:必须检查代码变更中是否错误删除了测试用例标记(#[test] 或 #[cfg(test)])。如果发现删除了测试用例标记且没有合理的理由(如移动到其他位置、重复测试等),必须报告为破坏性变更;\n"
|
|
2344
|
-
"- 若Rust实现使用了不同的实现方式但保持了功能一致,且无严重问题、无破坏性变更、文件结构合理,ok 应为 true\n"
|
|
2345
|
-
"- 仅报告功能不一致、严重问题、破坏性变更和文件结构问题,不报告类型匹配、指针可变性、边界检查细节等技术细节(除非会导致功能错误)\n"
|
|
2346
|
-
"- **重要:每个问题描述必须包含以下内容:**\n"
|
|
2347
|
-
" 1. 问题的详细描述:明确指出问题所在的位置(文件、函数、行号等)、问题的具体表现、为什么这是一个问题\n"
|
|
2348
|
-
" 2. 修复建议:提供具体的修复方案,包括需要修改的代码位置、修改方式、预期效果等\n"
|
|
2349
|
-
" 3. 问题格式:[function]、[critical]、[breaking] 或 [structure] 开头,后跟详细的问题描述和修复建议\n"
|
|
2350
|
-
" 示例格式:\n"
|
|
2351
|
-
' "[function] 返回值处理缺失:在函数 foo 的第 42 行,当输入参数为负数时,函数没有返回错误码,但 C 实现中会返回 -1。修复建议:在函数开始处添加参数验证,当参数为负数时返回 Result::Err(Error::InvalidInput)。"\n'
|
|
2352
|
-
' "[critical] 空指针解引用风险:在函数 bar 的第 58 行,直接解引用指针 ptr 而没有检查其是否为 null,可能导致 panic。修复建议:使用 if let Some(value) = ptr.as_ref() 进行空指针检查,或使用 Option<&T> 类型。"\n'
|
|
2353
|
-
' "[breaking] 函数签名变更导致调用方无法编译:函数 baz 的签名从 `fn baz(x: i32) -> i32` 变更为 `fn baz(x: i64) -> i64`,但调用方代码(src/other.rs:15)仍使用 i32 类型调用,且无法通过简单适配解决,会导致类型不匹配错误。修复建议:保持函数签名与调用方兼容,或同时更新所有调用方代码。注意:如果调用方已经适配了新签名,或可以通过简单的类型转换解决,则不应视为破坏性变更。"\n'
|
|
2354
|
-
' "[structure] 模块导出缺失:函数 qux 所在的模块 utils 未在 src/lib.rs 中导出,导致无法从 crate 外部访问。修复建议:在 src/lib.rs 中添加 `pub mod utils;` 声明。"\n'
|
|
2355
|
-
"请严格按以下格式输出(JSON格式,支持jsonnet语法如尾随逗号、注释、|||分隔符多行字符串等):\n"
|
|
2356
|
-
"<SUMMARY>\n{\n \"ok\": true,\n \"function_issues\": [],\n \"critical_issues\": [],\n \"breaking_issues\": [],\n \"structure_issues\": []\n}\n</SUMMARY>"
|
|
2357
|
-
)
|
|
2358
|
-
# 在 usr_p 和 sum_p 中追加附加说明(sys_p 通常不需要)
|
|
2359
|
-
usr_p = self._append_additional_notes(usr_p)
|
|
2360
|
-
sum_p = self._append_additional_notes(sum_p)
|
|
2361
|
-
return sys_p, usr_p, sum_p
|
|
2362
|
-
|
|
2363
|
-
i = 0
|
|
2364
|
-
max_iterations = self.review_max_iterations
|
|
2365
|
-
# 复用 Review Agent(仅在本函数生命周期内构建一次)
|
|
2366
|
-
# 注意:Agent 必须在 crate 根目录下创建,以确保工具(如 read_symbols)能正确获取符号表
|
|
2367
|
-
# 由于 transpile() 开始时已切换到 crate 目录,此处无需再次切换
|
|
2368
|
-
review_key = f"review::{rec.id}"
|
|
2369
|
-
sys_p_init, _, sum_p_init = build_review_prompts() # 只获取一次 sys_p 和 sum_p,usr_p 每次重新构建
|
|
2370
|
-
|
|
2371
|
-
# 获取函数信息用于 Agent name
|
|
2372
|
-
fn_name = rec.qname or rec.name or f"fn_{rec.id}"
|
|
2373
|
-
agent_name = f"C2Rust-Review-Agent({fn_name})"
|
|
2374
|
-
|
|
2375
|
-
if self._current_agents.get(review_key) is None:
|
|
2376
|
-
review_agent = Agent(
|
|
2377
|
-
system_prompt=sys_p_init,
|
|
2378
|
-
name=agent_name,
|
|
2379
|
-
model_group=self.llm_group,
|
|
2380
|
-
summary_prompt=sum_p_init,
|
|
2381
|
-
need_summary=True,
|
|
2382
|
-
auto_complete=True,
|
|
2383
|
-
use_tools=["execute_script", "read_code", "read_symbols"],
|
|
2384
|
-
non_interactive=self.non_interactive,
|
|
2385
|
-
use_methodology=False,
|
|
2386
|
-
use_analysis=False,
|
|
2387
|
-
)
|
|
2388
|
-
# 订阅 BEFORE_TOOL_CALL 和 AFTER_TOOL_CALL 事件,用于细粒度检测测试代码删除
|
|
2389
|
-
review_agent.event_bus.subscribe(BEFORE_TOOL_CALL, self._on_before_tool_call)
|
|
2390
|
-
review_agent.event_bus.subscribe(AFTER_TOOL_CALL, self._on_after_tool_call)
|
|
2391
|
-
# 记录 Agent 创建时的 commit id(作为初始值)
|
|
2392
|
-
agent_id = id(review_agent)
|
|
2393
|
-
agent_key = f"agent_{agent_id}"
|
|
2394
|
-
initial_commit = self._get_crate_commit_hash()
|
|
2395
|
-
if initial_commit:
|
|
2396
|
-
self._agent_before_commits[agent_key] = initial_commit
|
|
2397
|
-
self._current_agents[review_key] = review_agent
|
|
2398
|
-
|
|
2399
|
-
# 0表示无限重试,否则限制迭代次数
|
|
2400
|
-
use_direct_model_review = False # 标记是否使用直接模型调用
|
|
2401
|
-
parse_failed = False # 标记上一次解析是否失败
|
|
2402
|
-
parse_error_msg: Optional[str] = None # 保存上一次的YAML解析错误信息
|
|
2403
|
-
while max_iterations == 0 or i < max_iterations:
|
|
2404
|
-
agent = self._current_agents[review_key]
|
|
2405
|
-
# 由于 transpile() 开始时已切换到 crate 目录,此处无需再次切换
|
|
2406
|
-
|
|
2407
|
-
# 每次迭代都重新获取最新的 diff(从保存的 commit 到当前的 HEAD)
|
|
2408
|
-
# 重新构建 user prompt,包含最新的 diff
|
|
2409
|
-
_, usr_p_current, _ = build_review_prompts() # 重新构建,获取最新的 diff
|
|
2410
|
-
|
|
2411
|
-
if i > 0:
|
|
2412
|
-
# 修复后的审查,添加代码已更新的提示
|
|
2413
|
-
code_changed_notice = "\n".join([
|
|
2414
|
-
"",
|
|
2415
|
-
"【重要:代码已更新】",
|
|
2416
|
-
f"在本次审查之前(第 {i} 次迭代),已根据审查意见对代码进行了修复和优化。",
|
|
2417
|
-
"目标函数的实现已经发生变化,包括但不限于:",
|
|
2418
|
-
"- 函数实现逻辑的修改",
|
|
2419
|
-
"- 类型和签名的调整",
|
|
2420
|
-
"- 依赖关系的更新",
|
|
2421
|
-
"- 错误处理的改进",
|
|
2422
|
-
"",
|
|
2423
|
-
"**审查要求:**",
|
|
2424
|
-
"- **优先使用diff信息**:如果提供了最新的commit差异(COMMIT_DIFF),优先基于diff信息进行审查判断,只有在diff信息不足时才使用 read_code 工具读取原始文件",
|
|
2425
|
-
"- 如果必须使用 read_code 工具,请读取目标模块文件的最新内容",
|
|
2426
|
-
"- **禁止基于之前的审查结果、对话历史或任何缓存信息进行判断**",
|
|
2427
|
-
"- 必须基于最新的代码状态(通过diff或read_code获取)进行审查评估",
|
|
2428
|
-
"",
|
|
2429
|
-
"如果diff信息充足,可以直接基于diff进行审查;如果diff信息不足,请使用 read_code 工具读取最新代码。",
|
|
2430
|
-
"",
|
|
2431
|
-
])
|
|
2432
|
-
usr_p_with_notice = usr_p_current + code_changed_notice
|
|
2433
|
-
composed_prompt = self._compose_prompt_with_context(usr_p_with_notice)
|
|
2434
|
-
# 修复后必须使用 Agent.run(),不能使用直接模型调用(因为需要工具调用)
|
|
2435
|
-
use_direct_model_review = False
|
|
2436
|
-
else:
|
|
2437
|
-
composed_prompt = self._compose_prompt_with_context(usr_p_current)
|
|
2438
|
-
|
|
2439
|
-
if use_direct_model_review:
|
|
2440
|
-
# 格式解析失败后,直接调用模型接口
|
|
2441
|
-
# 构造包含摘要提示词和具体错误信息的完整提示
|
|
2442
|
-
error_guidance = ""
|
|
2443
|
-
# 检查上一次的解析结果
|
|
2444
|
-
if parse_error_msg:
|
|
2445
|
-
# 如果有JSON解析错误,优先反馈
|
|
2446
|
-
error_guidance = (
|
|
2447
|
-
f"\n\n**格式错误详情(请根据以下错误修复输出格式):**\n"
|
|
2448
|
-
f"- JSON解析失败: {parse_error_msg}\n\n"
|
|
2449
|
-
f"请确保输出的JSON格式正确,包括正确的引号、逗号、大括号等。JSON 对象必须包含字段:ok(布尔值)、function_issues(字符串数组)、critical_issues(字符串数组)、breaking_issues(字符串数组)、structure_issues(字符串数组)。支持jsonnet语法(如尾随逗号、注释、||| 或 ``` 分隔符多行字符串等)。"
|
|
2450
|
-
)
|
|
2451
|
-
elif parse_failed:
|
|
2452
|
-
error_guidance = (
|
|
2453
|
-
"\n\n**格式错误详情(请根据以下错误修复输出格式):**\n"
|
|
2454
|
-
"- 无法从摘要中解析出有效的 JSON 对象\n\n"
|
|
2455
|
-
"请确保输出格式正确:仅输出一个 <SUMMARY> 块,块内直接包含 JSON 对象(不需要额外的标签),字段:ok(布尔值)、function_issues(字符串数组)、critical_issues(字符串数组)、breaking_issues(字符串数组)、structure_issues(字符串数组)。支持jsonnet语法(如尾随逗号、注释、||| 或 ``` 分隔符多行字符串等)。"
|
|
2456
|
-
)
|
|
2457
|
-
|
|
2458
|
-
full_prompt = f"{composed_prompt}{error_guidance}\n\n{sum_p_init}"
|
|
2459
|
-
typer.secho(f"[c2rust-transpiler][review] 直接调用模型接口修复格式错误(第 {i+1} 次重试)", fg=typer.colors.YELLOW)
|
|
2460
|
-
try:
|
|
2461
|
-
response = agent.model.chat_until_success(full_prompt) # type: ignore
|
|
2462
|
-
summary = str(response or "")
|
|
2463
|
-
except Exception as e:
|
|
2464
|
-
typer.secho(f"[c2rust-transpiler][review] 直接模型调用失败: {e},回退到 run()", fg=typer.colors.YELLOW)
|
|
2465
|
-
summary = str(agent.run(composed_prompt) or "")
|
|
2466
|
-
else:
|
|
2467
|
-
# 第一次使用 run(),让 Agent 完整运行(可能使用工具)
|
|
2468
|
-
summary = str(agent.run(composed_prompt) or "")
|
|
2469
|
-
|
|
2470
|
-
# 解析 JSON 格式的审查结果
|
|
2471
|
-
verdict, parse_error_review = extract_json_from_summary(summary)
|
|
2472
|
-
parse_failed = False
|
|
2473
|
-
parse_error_msg = None
|
|
2474
|
-
if parse_error_review:
|
|
2475
|
-
# JSON解析失败
|
|
2476
|
-
parse_failed = True
|
|
2477
|
-
parse_error_msg = parse_error_review
|
|
2478
|
-
typer.secho(f"[c2rust-transpiler][review] JSON解析失败: {parse_error_review}", fg=typer.colors.YELLOW)
|
|
2479
|
-
# 兼容旧格式:尝试解析纯文本 OK
|
|
2480
|
-
m = re.search(r"<SUMMARY>([\s\S]*?)</SUMMARY>", summary, flags=re.IGNORECASE)
|
|
2481
|
-
content = (m.group(1).strip() if m else summary.strip()).upper()
|
|
2482
|
-
if content == "OK":
|
|
2483
|
-
verdict = {"ok": True, "function_issues": [], "critical_issues": [], "breaking_issues": [], "structure_issues": []}
|
|
2484
|
-
parse_failed = False # 兼容格式成功,不算解析失败
|
|
2485
|
-
parse_error_msg = None
|
|
2486
|
-
else:
|
|
2487
|
-
# 无法解析,立即重试:设置标志并继续循环
|
|
2488
|
-
use_direct_model_review = True
|
|
2489
|
-
# 继续循环,立即重试
|
|
2490
|
-
continue
|
|
2491
|
-
elif not isinstance(verdict, dict):
|
|
2492
|
-
parse_failed = True
|
|
2493
|
-
# 兼容旧格式:尝试解析纯文本 OK
|
|
2494
|
-
m = re.search(r"<SUMMARY>([\s\S]*?)</SUMMARY>", summary, flags=re.IGNORECASE)
|
|
2495
|
-
content = (m.group(1).strip() if m else summary.strip()).upper()
|
|
2496
|
-
if content == "OK":
|
|
2497
|
-
verdict = {"ok": True, "function_issues": [], "critical_issues": [], "breaking_issues": [], "structure_issues": []}
|
|
2498
|
-
parse_failed = False # 兼容格式成功,不算解析失败
|
|
2499
|
-
else:
|
|
2500
|
-
# 无法解析,立即重试:设置标志并继续循环
|
|
2501
|
-
use_direct_model_review = True
|
|
2502
|
-
parse_error_msg = f"无法从摘要中解析出有效的 JSON 对象,得到的内容类型为: {type(verdict).__name__}"
|
|
2503
|
-
# 继续循环,立即重试
|
|
2504
|
-
continue
|
|
2505
|
-
|
|
2506
|
-
ok = bool(verdict.get("ok") is True)
|
|
2507
|
-
function_issues = verdict.get("function_issues") if isinstance(verdict.get("function_issues"), list) else []
|
|
2508
|
-
critical_issues = verdict.get("critical_issues") if isinstance(verdict.get("critical_issues"), list) else []
|
|
2509
|
-
breaking_issues = verdict.get("breaking_issues") if isinstance(verdict.get("breaking_issues"), list) else []
|
|
2510
|
-
structure_issues = verdict.get("structure_issues") if isinstance(verdict.get("structure_issues"), list) else []
|
|
2511
|
-
|
|
2512
|
-
typer.secho(f"[c2rust-transpiler][review][iter={i+1}] verdict ok={ok}, function_issues={len(function_issues)}, critical_issues={len(critical_issues)}, breaking_issues={len(breaking_issues)}, structure_issues={len(structure_issues)}", fg=typer.colors.CYAN)
|
|
2513
|
-
|
|
2514
|
-
# 如果 ok 为 true,表示审查通过(功能一致且无严重问题、无破坏性变更、文件结构合理),直接返回,不触发修复
|
|
2515
|
-
if ok:
|
|
2516
|
-
limit_info = f" (上限: {max_iterations if max_iterations > 0 else '无限'})"
|
|
2517
|
-
typer.secho(f"[c2rust-transpiler][review] 代码审查通过{limit_info} (共 {i+1} 次迭代)。", fg=typer.colors.GREEN)
|
|
2518
|
-
# 记录审查结果到进度
|
|
2519
|
-
try:
|
|
2520
|
-
cur = self.progress.get("current") or {}
|
|
2521
|
-
cur["review"] = {
|
|
2522
|
-
"ok": True,
|
|
2523
|
-
"function_issues": list(function_issues),
|
|
2524
|
-
"critical_issues": list(critical_issues),
|
|
2525
|
-
"breaking_issues": list(breaking_issues),
|
|
2526
|
-
"structure_issues": list(structure_issues),
|
|
2527
|
-
"iterations": i + 1,
|
|
2528
|
-
}
|
|
2529
|
-
metrics = cur.get("metrics") or {}
|
|
2530
|
-
metrics["review_iterations"] = i + 1
|
|
2531
|
-
metrics["function_issues"] = len(function_issues)
|
|
2532
|
-
metrics["type_issues"] = len(critical_issues)
|
|
2533
|
-
metrics["breaking_issues"] = len(breaking_issues)
|
|
2534
|
-
metrics["structure_issues"] = len(structure_issues)
|
|
2535
|
-
cur["metrics"] = metrics
|
|
2536
|
-
self.progress["current"] = cur
|
|
2537
|
-
self._save_progress()
|
|
2538
|
-
except Exception:
|
|
2539
|
-
pass
|
|
2540
|
-
return
|
|
2541
|
-
|
|
2542
|
-
# 需要优化:提供详细上下文背景,并明确审查意见仅针对 Rust crate,不修改 C 源码
|
|
2543
|
-
crate_tree = dir_tree(self.crate_dir)
|
|
2544
|
-
issues_text = "\n".join([
|
|
2545
|
-
"功能一致性问题:" if function_issues else "",
|
|
2546
|
-
*[f" - {issue}" for issue in function_issues],
|
|
2547
|
-
"严重问题(可能导致功能错误):" if critical_issues else "",
|
|
2548
|
-
*[f" - {issue}" for issue in critical_issues],
|
|
2549
|
-
"破坏性变更问题(对现有代码的影响):" if breaking_issues else "",
|
|
2550
|
-
*[f" - {issue}" for issue in breaking_issues],
|
|
2551
|
-
"文件结构问题:" if structure_issues else "",
|
|
2552
|
-
*[f" - {issue}" for issue in structure_issues],
|
|
2553
|
-
])
|
|
2554
|
-
fix_prompt = "\n".join([
|
|
2555
|
-
"请根据以下审查结论对目标函数进行最小优化(保留结构与意图,不进行大范围重构):",
|
|
2556
|
-
"<REVIEW>",
|
|
2557
|
-
issues_text if issues_text.strip() else "审查发现问题,但未提供具体问题描述",
|
|
2558
|
-
"</REVIEW>",
|
|
2559
|
-
"",
|
|
2560
|
-
"上下文背景信息:",
|
|
2561
|
-
f"- crate_dir: {self.crate_dir.resolve()}",
|
|
2562
|
-
f"- 目标模块文件: {module}",
|
|
2563
|
-
f"- 建议/当前 Rust 签名: {rust_sig}",
|
|
2564
|
-
"crate 目录结构(部分):",
|
|
2565
|
-
crate_tree,
|
|
2566
|
-
"",
|
|
2567
|
-
"约束与范围:",
|
|
2568
|
-
"- 本次审查意见仅针对 Rust crate 的代码与配置;不要修改任何 C/C++ 源文件(*.c、*.h 等)。",
|
|
2569
|
-
"- 仅允许在 crate_dir 下进行最小必要修改(Cargo.toml、src/**/*.rs);不要改动其他目录。",
|
|
2570
|
-
"- 保持最小改动,避免与问题无关的重构或格式化。",
|
|
2571
|
-
"- 优先修复严重问题(可能导致功能错误),然后修复功能一致性问题;",
|
|
2572
|
-
"- 如审查问题涉及缺失/未实现的被调函数或依赖,请阅读其 C 源码并在本次一并补齐等价的 Rust 实现;必要时在合理模块新增函数或引入精确 use;",
|
|
2573
|
-
"- 禁止使用 todo!/unimplemented! 作为占位;",
|
|
2574
|
-
"- 可使用工具 read_symbols/read_code 获取依赖符号的 C 源码与位置以辅助实现;仅精确导入所需符号(禁止通配);",
|
|
2575
|
-
"- 注释规范:所有代码注释(包括文档注释、行内注释、块注释等)必须使用中文;",
|
|
2576
|
-
*([f"- **禁用库约束**:禁止在优化中使用以下库:{', '.join(self.disabled_libraries)}。如果这些库在 Cargo.toml 中已存在,请移除相关依赖;如果优化需要使用这些库的功能,请使用标准库或其他允许的库替代。"] if self.disabled_libraries else []),
|
|
2577
|
-
*([f"- **根符号要求**:此函数是根符号({rec.qname or rec.name}),必须使用 `pub` 关键字对外暴露,确保可以从 crate 外部访问。同时,该函数所在的模块必须在 src/lib.rs 中被导出(使用 `pub mod <模块名>;`)。"] if self._is_root_symbol(rec) else []),
|
|
2578
|
-
"",
|
|
2579
|
-
"【重要:依赖检查与实现要求】",
|
|
2580
|
-
"在优化函数之前,请务必检查以下内容:",
|
|
2581
|
-
"1. 检查当前函数是否已完整实现:",
|
|
2582
|
-
f" - 在目标模块 {module} 中查找函数 {rec.qname or rec.name} 的实现",
|
|
2583
|
-
" - 如果已存在实现,检查其是否完整且正确",
|
|
2584
|
-
"2. 检查所有依赖函数是否已实现:",
|
|
2585
|
-
" - 遍历当前函数调用的所有被调函数(包括直接调用和间接调用)",
|
|
2586
|
-
" - 对于每个被调函数,检查其在 Rust crate 中是否已有完整实现",
|
|
2587
|
-
" - 可以使用 read_code 工具读取相关模块文件进行检查",
|
|
2588
|
-
"3. 对于未实现的依赖函数:",
|
|
2589
|
-
" - 使用 read_symbols 工具获取其 C 源码和符号信息",
|
|
2590
|
-
" - 使用 read_code 工具读取其 C 源码实现",
|
|
2591
|
-
" - 在本次优化中一并补齐这些依赖函数的 Rust 实现",
|
|
2592
|
-
" - 根据依赖关系选择合适的模块位置(可在同一模块或合理的新模块中)",
|
|
2593
|
-
" - 确保所有依赖函数都有完整实现,禁止使用 todo!/unimplemented! 占位",
|
|
2594
|
-
"4. 实现顺序:",
|
|
2595
|
-
" - 优先实现最底层的依赖函数(不依赖其他未实现函数的函数)",
|
|
2596
|
-
" - 然后实现依赖这些底层函数的函数",
|
|
2597
|
-
" - 最后优化当前目标函数",
|
|
2598
|
-
"5. 验证:",
|
|
2599
|
-
" - 确保当前函数及其所有依赖函数都已完整实现",
|
|
2600
|
-
" - 确保没有遗留的 todo!/unimplemented! 占位",
|
|
2601
|
-
" - 确保所有函数调用都能正确解析",
|
|
2602
|
-
"",
|
|
2603
|
-
"请仅以补丁形式输出修改,避免冗余解释。",
|
|
2604
|
-
])
|
|
2605
|
-
# 由于 transpile() 开始时已切换到 crate 目录,此处无需再次切换
|
|
2606
|
-
# 记录运行前的 commit
|
|
2607
|
-
before_commit = self._get_crate_commit_hash()
|
|
2608
|
-
ca = self._get_code_agent()
|
|
2609
|
-
limit_info = f"/{max_iterations}" if max_iterations > 0 else "/∞"
|
|
2610
|
-
fix_prompt_with_notes = self._append_additional_notes(fix_prompt)
|
|
2611
|
-
ca.run(self._compose_prompt_with_context(fix_prompt_with_notes), prefix=f"[c2rust-transpiler][review-fix iter={i+1}{limit_info}]", suffix="")
|
|
2612
|
-
|
|
2613
|
-
# 检测并处理测试代码删除
|
|
2614
|
-
if self._check_and_handle_test_deletion(before_commit, ca):
|
|
2615
|
-
# 如果回退了,需要重新运行 agent
|
|
2616
|
-
typer.secho(f"[c2rust-transpiler][review-fix] 检测到测试代码删除问题,已回退,重新运行 agent (iter={i+1})", fg=typer.colors.YELLOW)
|
|
2617
|
-
before_commit = self._get_crate_commit_hash()
|
|
2618
|
-
ca.run(self._compose_prompt_with_context(fix_prompt_with_notes), prefix=f"[c2rust-transpiler][review-fix iter={i+1}{limit_info}][retry]", suffix="")
|
|
2619
|
-
# 再次检测
|
|
2620
|
-
if self._check_and_handle_test_deletion(before_commit, ca):
|
|
2621
|
-
typer.secho(f"[c2rust-transpiler][review-fix] 再次检测到测试代码删除问题,已回退 (iter={i+1})", fg=typer.colors.RED)
|
|
2622
|
-
|
|
2623
|
-
# 优化后进行一次构建验证;若未通过则进入构建修复循环,直到通过为止
|
|
2624
|
-
self._cargo_build_loop()
|
|
2625
|
-
|
|
2626
|
-
# 记录本次审查结果
|
|
2627
|
-
try:
|
|
2628
|
-
cur = self.progress.get("current") or {}
|
|
2629
|
-
cur["review"] = {
|
|
2630
|
-
"ok": False,
|
|
2631
|
-
"function_issues": list(function_issues),
|
|
2632
|
-
"critical_issues": list(critical_issues),
|
|
2633
|
-
"breaking_issues": list(breaking_issues),
|
|
2634
|
-
"structure_issues": list(structure_issues),
|
|
2635
|
-
"iterations": i + 1,
|
|
2636
|
-
}
|
|
2637
|
-
metrics = cur.get("metrics") or {}
|
|
2638
|
-
metrics["function_issues"] = len(function_issues)
|
|
2639
|
-
metrics["type_issues"] = len(critical_issues)
|
|
2640
|
-
metrics["breaking_issues"] = len(breaking_issues)
|
|
2641
|
-
metrics["structure_issues"] = len(structure_issues)
|
|
2642
|
-
cur["metrics"] = metrics
|
|
2643
|
-
self.progress["current"] = cur
|
|
2644
|
-
self._save_progress()
|
|
2645
|
-
except Exception:
|
|
2646
|
-
pass
|
|
2647
|
-
|
|
2648
|
-
i += 1
|
|
2649
|
-
|
|
2650
|
-
# 达到迭代上限(仅当设置了上限时)
|
|
2651
|
-
if max_iterations > 0 and i >= max_iterations:
|
|
2652
|
-
typer.secho(f"[c2rust-transpiler][review] 已达到最大迭代次数上限({max_iterations}),停止审查优化。", fg=typer.colors.YELLOW)
|
|
2653
|
-
try:
|
|
2654
|
-
cur = self.progress.get("current") or {}
|
|
2655
|
-
cur["review_max_iterations_reached"] = True
|
|
2656
|
-
cur["review_iterations"] = i
|
|
2657
|
-
self.progress["current"] = cur
|
|
2658
|
-
self._save_progress()
|
|
2659
|
-
except Exception:
|
|
2660
|
-
pass
|
|
699
|
+
"""审查生成的实现(委托给 ReviewManager)"""
|
|
700
|
+
return self.review_manager.review_and_optimize(rec, module, rust_sig)
|
|
2661
701
|
|
|
2662
702
|
def _mark_converted(self, rec: FnRecord, module: str, rust_sig: str) -> None:
|
|
2663
|
-
"""记录映射:C 符号 -> Rust
|
|
2664
|
-
|
|
2665
|
-
# 从签名中提取函数名(支持生命周期参数和泛型参数)
|
|
2666
|
-
# 支持生命周期参数和泛型参数:fn name<'a, T>(...)
|
|
2667
|
-
m = re.search(r"\bfn\s+([A-Za-z_][A-Za-z0-9_]*)\s*(?:<[^>]+>)?\s*\(", rust_sig)
|
|
2668
|
-
if m:
|
|
2669
|
-
rust_symbol = m.group(1)
|
|
2670
|
-
# 写入 JSONL 映射(带源位置,用于区分同名符号)
|
|
2671
|
-
self.symbol_map.add(rec, module, rust_symbol or (rec.name or f"fn_{rec.id}"))
|
|
2672
|
-
|
|
2673
|
-
# 获取当前 commit id 并记录
|
|
2674
|
-
current_commit = self._get_crate_commit_hash()
|
|
2675
|
-
|
|
2676
|
-
# 更新进度:已转换集合
|
|
2677
|
-
converted = self.progress.get("converted") or []
|
|
2678
|
-
if rec.id not in converted:
|
|
2679
|
-
converted.append(rec.id)
|
|
2680
|
-
self.progress["converted"] = converted
|
|
2681
|
-
self.progress["current"] = None
|
|
2682
|
-
|
|
2683
|
-
# 记录每个已转换函数的 commit id
|
|
2684
|
-
converted_commits = self.progress.get("converted_commits") or {}
|
|
2685
|
-
if current_commit:
|
|
2686
|
-
converted_commits[str(rec.id)] = current_commit
|
|
2687
|
-
self.progress["converted_commits"] = converted_commits
|
|
2688
|
-
typer.secho(f"[c2rust-transpiler][progress] 已记录函数 {rec.id} 的 commit: {current_commit}", fg=typer.colors.CYAN)
|
|
2689
|
-
|
|
2690
|
-
self._save_progress()
|
|
703
|
+
"""记录映射:C 符号 -> Rust 符号与模块路径(委托给 SymbolMapper)"""
|
|
704
|
+
self.symbol_mapper.mark_converted(rec, module, rust_sig)
|
|
2691
705
|
|
|
2692
706
|
def transpile(self) -> None:
|
|
2693
|
-
"""
|
|
2694
|
-
|
|
2695
|
-
|
|
2696
|
-
|
|
2697
|
-
|
|
2698
|
-
|
|
2699
|
-
|
|
2700
|
-
|
|
2701
|
-
|
|
2702
|
-
|
|
2703
|
-
|
|
2704
|
-
|
|
2705
|
-
|
|
2706
|
-
|
|
2707
|
-
|
|
2708
|
-
|
|
2709
|
-
|
|
2710
|
-
|
|
2711
|
-
|
|
2712
|
-
|
|
2713
|
-
|
|
2714
|
-
|
|
2715
|
-
|
|
2716
|
-
|
|
2717
|
-
|
|
2718
|
-
|
|
2719
|
-
|
|
2720
|
-
|
|
2721
|
-
|
|
2722
|
-
|
|
2723
|
-
|
|
2724
|
-
|
|
2725
|
-
|
|
2726
|
-
|
|
2727
|
-
|
|
2728
|
-
|
|
2729
|
-
|
|
2730
|
-
|
|
2731
|
-
|
|
2732
|
-
steps = iter_order_steps(order_path)
|
|
2733
|
-
if not steps:
|
|
2734
|
-
typer.secho("[c2rust-transpiler] 未找到翻译步骤。", fg=typer.colors.YELLOW)
|
|
2735
|
-
return
|
|
2736
|
-
|
|
2737
|
-
# 构建自包含 order 索引(id -> FnRecord,name/qname -> id)
|
|
2738
|
-
self._load_order_index(order_path)
|
|
2739
|
-
|
|
2740
|
-
# 扁平化顺序,按单个函数处理(保持原有顺序)
|
|
2741
|
-
seq: List[int] = []
|
|
2742
|
-
for grp in steps:
|
|
2743
|
-
seq.extend(grp)
|
|
2744
|
-
|
|
2745
|
-
# 若支持 resume,则跳过 progress['converted'] 中已完成的
|
|
2746
|
-
done: Set[int] = set(self.progress.get("converted") or [])
|
|
2747
|
-
# 计算需要处理的函数总数(排除已完成的)
|
|
2748
|
-
total_to_process = len([fid for fid in seq if fid not in done])
|
|
2749
|
-
current_index = 0
|
|
2750
|
-
|
|
2751
|
-
# 恢复时,reset 到最后一个已转换函数的 commit id
|
|
2752
|
-
if self.resume and done:
|
|
2753
|
-
converted_commits = self.progress.get("converted_commits") or {}
|
|
2754
|
-
if converted_commits:
|
|
2755
|
-
# 找到最后一个已转换函数的 commit id
|
|
2756
|
-
last_commit = None
|
|
2757
|
-
for fid in reversed(seq):
|
|
2758
|
-
if fid in done:
|
|
2759
|
-
commit_id = converted_commits.get(str(fid))
|
|
2760
|
-
if commit_id:
|
|
2761
|
-
last_commit = commit_id
|
|
2762
|
-
break
|
|
2763
|
-
|
|
2764
|
-
if last_commit:
|
|
2765
|
-
current_commit = self._get_crate_commit_hash()
|
|
2766
|
-
if current_commit != last_commit:
|
|
2767
|
-
typer.secho(f"[c2rust-transpiler][resume] 检测到代码状态不一致,正在 reset 到最后一个已转换函数的 commit: {last_commit}", fg=typer.colors.YELLOW)
|
|
2768
|
-
if self._reset_to_commit(last_commit):
|
|
2769
|
-
typer.secho(f"[c2rust-transpiler][resume] 已 reset 到 commit: {last_commit}", fg=typer.colors.GREEN)
|
|
2770
|
-
else:
|
|
2771
|
-
typer.secho("[c2rust-transpiler][resume] reset 失败,继续使用当前代码状态", fg=typer.colors.YELLOW)
|
|
2772
|
-
else:
|
|
2773
|
-
typer.secho("[c2rust-transpiler][resume] 代码状态一致,无需 reset", fg=typer.colors.CYAN)
|
|
2774
|
-
|
|
2775
|
-
typer.secho(f"[c2rust-transpiler][order] 顺序信息: 步骤数={len(steps)} 总ID={sum(len(g) for g in steps)} 已转换={len(done)} 待处理={total_to_process}", fg=typer.colors.BLUE)
|
|
2776
|
-
|
|
2777
|
-
for fid in seq:
|
|
2778
|
-
if fid in done:
|
|
2779
|
-
continue
|
|
2780
|
-
rec = self.fn_index_by_id.get(fid)
|
|
2781
|
-
if not rec:
|
|
2782
|
-
continue
|
|
2783
|
-
if self._should_skip(rec):
|
|
2784
|
-
typer.secho(f"[c2rust-transpiler][skip] 跳过 {rec.qname or rec.name} (id={rec.id}) 位于 {rec.file}:{rec.start_line}-{rec.end_line}", fg=typer.colors.YELLOW)
|
|
2785
|
-
continue
|
|
2786
|
-
|
|
2787
|
-
# 更新进度索引
|
|
2788
|
-
current_index += 1
|
|
2789
|
-
progress_info = f"({current_index}/{total_to_process})" if total_to_process > 0 else ""
|
|
2790
|
-
|
|
2791
|
-
# 在每个函数开始转译前执行 cargo fmt
|
|
2792
|
-
workspace_root = str(self.crate_dir)
|
|
2793
|
-
self._run_cargo_fmt(workspace_root)
|
|
2794
|
-
|
|
2795
|
-
# 读取C函数源码
|
|
2796
|
-
typer.secho(f"[c2rust-transpiler][read] {progress_info} 读取 C 源码: {rec.qname or rec.name} (id={rec.id}) 来自 {rec.file}:{rec.start_line}-{rec.end_line}", fg=typer.colors.BLUE)
|
|
2797
|
-
c_code = self._read_source_span(rec)
|
|
2798
|
-
typer.secho(f"[c2rust-transpiler][read] 已加载 {len(c_code.splitlines()) if c_code else 0} 行", fg=typer.colors.BLUE)
|
|
2799
|
-
|
|
2800
|
-
# 若缺少源码片段且缺乏签名/参数信息,则跳过本函数,记录进度以便后续处理
|
|
2801
|
-
if not c_code and not (getattr(rec, "signature", "") or getattr(rec, "params", None)):
|
|
2802
|
-
skipped = self.progress.get("skipped_missing_source") or []
|
|
2803
|
-
if rec.id not in skipped:
|
|
2804
|
-
skipped.append(rec.id)
|
|
2805
|
-
self.progress["skipped_missing_source"] = skipped
|
|
2806
|
-
typer.secho(f"[c2rust-transpiler] {progress_info} 跳过:缺少源码与签名信息 -> {rec.qname or rec.name} (id={rec.id})", fg=typer.colors.YELLOW)
|
|
2807
|
-
self._save_progress()
|
|
2808
|
-
continue
|
|
2809
|
-
# 1) 规划:模块路径与Rust签名
|
|
2810
|
-
typer.secho(f"[c2rust-transpiler][plan] {progress_info} 正在规划模块与签名: {rec.qname or rec.name} (id={rec.id})", fg=typer.colors.CYAN)
|
|
2811
|
-
module, rust_sig, skip_implementation = self._plan_module_and_signature(rec, c_code)
|
|
2812
|
-
typer.secho(f"[c2rust-transpiler][plan] 已选择 模块={module}, 签名={rust_sig}", fg=typer.colors.CYAN)
|
|
2813
|
-
|
|
2814
|
-
# 记录当前进度
|
|
2815
|
-
self._update_progress_current(rec, module, rust_sig)
|
|
2816
|
-
typer.secho(f"[c2rust-transpiler][progress] 已更新当前进度记录 id={rec.id}", fg=typer.colors.CYAN)
|
|
2817
|
-
|
|
2818
|
-
# 如果标记为跳过实现,则直接标记为已转换
|
|
2819
|
-
if skip_implementation:
|
|
2820
|
-
typer.secho(f"[c2rust-transpiler][skip-impl] 函数 {rec.qname or rec.name} 评估为不需要实现,跳过实现阶段", fg=typer.colors.CYAN)
|
|
2821
|
-
# 直接标记为已转换,跳过代码生成、构建和审查阶段
|
|
2822
|
-
self._mark_converted(rec, module, rust_sig)
|
|
2823
|
-
typer.secho(f"[c2rust-transpiler][mark] 已标记并建立映射: {rec.qname or rec.name} -> {module} (跳过实现,视为已实现)", fg=typer.colors.GREEN)
|
|
2824
|
-
continue
|
|
2825
|
-
|
|
2826
|
-
# 初始化函数上下文与代码编写与修复Agent复用缓存(只在当前函数开始时执行一次)
|
|
2827
|
-
self._reset_function_context(rec, module, rust_sig, c_code)
|
|
2828
|
-
|
|
2829
|
-
# 1.5) 确保模块声明链(提前到生成实现之前,避免生成的代码无法被正确引用)
|
|
2830
|
-
try:
|
|
2831
|
-
self._ensure_mod_chain_for_module(module)
|
|
2832
|
-
typer.secho(f"[c2rust-transpiler][mod] 已补齐 {module} 的 mod.rs 声明链", fg=typer.colors.GREEN)
|
|
2833
|
-
# 确保顶层模块在 src/lib.rs 中被公开
|
|
2834
|
-
mp = Path(module)
|
|
2835
|
-
crate_root = self.crate_dir.resolve()
|
|
2836
|
-
rel = mp.resolve().relative_to(crate_root) if mp.is_absolute() else Path(module)
|
|
2837
|
-
rel_s = str(rel).replace("\\", "/")
|
|
2838
|
-
if rel_s.startswith("./"):
|
|
2839
|
-
rel_s = rel_s[2:]
|
|
2840
|
-
if rel_s.startswith("src/"):
|
|
2841
|
-
parts = rel_s[len("src/"):].strip("/").split("/")
|
|
2842
|
-
if parts and parts[0]:
|
|
2843
|
-
top_mod = parts[0]
|
|
2844
|
-
# 过滤掉 "mod" 关键字和 .rs 文件
|
|
2845
|
-
if top_mod != "mod" and not top_mod.endswith(".rs"):
|
|
2846
|
-
self._ensure_top_level_pub_mod(top_mod)
|
|
2847
|
-
typer.secho(f"[c2rust-transpiler][mod] 已在 src/lib.rs 确保顶层 pub mod {top_mod}", fg=typer.colors.GREEN)
|
|
2848
|
-
cur = self.progress.get("current") or {}
|
|
2849
|
-
cur["mod_chain_fixed"] = True
|
|
2850
|
-
cur["mod_visibility_fixed"] = True
|
|
2851
|
-
self.progress["current"] = cur
|
|
2852
|
-
self._save_progress()
|
|
2853
|
-
except Exception:
|
|
2854
|
-
pass
|
|
2855
|
-
|
|
2856
|
-
# 在处理函数前,记录当前的 commit id(用于失败回退)
|
|
2857
|
-
self._current_function_start_commit = self._get_crate_commit_hash()
|
|
2858
|
-
if self._current_function_start_commit:
|
|
2859
|
-
typer.secho(f"[c2rust-transpiler][commit] 记录函数开始时的 commit: {self._current_function_start_commit}", fg=typer.colors.BLUE)
|
|
2860
|
-
else:
|
|
2861
|
-
typer.secho("[c2rust-transpiler][commit] 警告:无法获取 commit id,将无法在失败时回退", fg=typer.colors.YELLOW)
|
|
2862
|
-
|
|
2863
|
-
# 重置连续失败计数(每个新函数开始时重置)
|
|
2864
|
-
self._consecutive_fix_failures = 0
|
|
2865
|
-
|
|
2866
|
-
# 使用循环来处理函数,支持失败回退后重新开始
|
|
2867
|
-
function_retry_count = 0
|
|
2868
|
-
max_function_retries = MAX_FUNCTION_RETRIES
|
|
2869
|
-
while function_retry_count <= max_function_retries:
|
|
2870
|
-
if function_retry_count > 0:
|
|
2871
|
-
typer.secho(f"[c2rust-transpiler][retry] 重新开始处理函数 (第 {function_retry_count} 次重试)", fg=typer.colors.YELLOW)
|
|
2872
|
-
# 重新记录 commit id(回退后的新 commit)
|
|
2873
|
-
self._current_function_start_commit = self._get_crate_commit_hash()
|
|
2874
|
-
if self._current_function_start_commit:
|
|
2875
|
-
typer.secho(f"[c2rust-transpiler][commit] 重新记录函数开始时的 commit: {self._current_function_start_commit}", fg=typer.colors.BLUE)
|
|
2876
|
-
# 重置连续失败计数(重新开始时重置)
|
|
2877
|
-
self._consecutive_fix_failures = 0
|
|
2878
|
-
|
|
2879
|
-
# 2) 生成实现
|
|
2880
|
-
unresolved = self._untranslated_callee_symbols(rec)
|
|
2881
|
-
typer.secho(f"[c2rust-transpiler][deps] {progress_info} 未解析的被调符号: {', '.join(unresolved) if unresolved else '(none)'}", fg=typer.colors.BLUE)
|
|
2882
|
-
typer.secho(f"[c2rust-transpiler][gen] {progress_info} 正在为 {rec.qname or rec.name} 生成 Rust 实现", fg=typer.colors.GREEN)
|
|
2883
|
-
self._codeagent_generate_impl(rec, c_code, module, rust_sig, unresolved)
|
|
2884
|
-
typer.secho(f"[c2rust-transpiler][gen] 已在 {module} 生成或更新实现", fg=typer.colors.GREEN)
|
|
2885
|
-
# 刷新精简上下文(防止签名/模块调整后提示不同步)
|
|
2886
|
-
try:
|
|
2887
|
-
self._refresh_compact_context(rec, module, rust_sig)
|
|
2888
|
-
except Exception:
|
|
2889
|
-
pass
|
|
2890
|
-
|
|
2891
|
-
# 3) 构建与修复
|
|
2892
|
-
typer.secho("[c2rust-transpiler][build] 开始 cargo 测试循环", fg=typer.colors.MAGENTA)
|
|
2893
|
-
ok = self._cargo_build_loop()
|
|
2894
|
-
|
|
2895
|
-
# 检查是否需要重新开始(回退后)
|
|
2896
|
-
if ok is None:
|
|
2897
|
-
# 需要重新开始
|
|
2898
|
-
function_retry_count += 1
|
|
2899
|
-
if function_retry_count > max_function_retries:
|
|
2900
|
-
typer.secho(f"[c2rust-transpiler] 函数重新开始次数已达上限({max_function_retries}),停止处理该函数", fg=typer.colors.RED)
|
|
2901
|
-
# 保留当前状态,便于下次 resume
|
|
2902
|
-
return
|
|
2903
|
-
# 重置连续失败计数
|
|
2904
|
-
self._consecutive_fix_failures = 0
|
|
2905
|
-
# 继续循环,重新开始处理
|
|
2906
|
-
continue
|
|
2907
|
-
|
|
2908
|
-
typer.secho(f"[c2rust-transpiler][build] 构建结果: {'通过' if ok else '失败'}", fg=typer.colors.MAGENTA)
|
|
2909
|
-
if not ok:
|
|
2910
|
-
typer.secho("[c2rust-transpiler] 在重试次数限制内未能成功构建,已停止。", fg=typer.colors.RED)
|
|
2911
|
-
# 保留当前状态,便于下次 resume
|
|
2912
|
-
return
|
|
2913
|
-
|
|
2914
|
-
# 构建成功,跳出循环继续后续流程
|
|
2915
|
-
break
|
|
2916
|
-
|
|
2917
|
-
# 4) 审查与优化(复用 Review Agent)
|
|
2918
|
-
typer.secho(f"[c2rust-transpiler][review] {progress_info} 开始代码审查: {rec.qname or rec.name}", fg=typer.colors.MAGENTA)
|
|
2919
|
-
self._review_and_optimize(rec, module, rust_sig)
|
|
2920
|
-
typer.secho("[c2rust-transpiler][review] 代码审查完成", fg=typer.colors.MAGENTA)
|
|
2921
|
-
|
|
2922
|
-
# 5) 标记已转换与映射记录(JSONL)
|
|
2923
|
-
self._mark_converted(rec, module, rust_sig)
|
|
2924
|
-
typer.secho(f"[c2rust-transpiler][mark] {progress_info} 已标记并建立映射: {rec.qname or rec.name} -> {module}", fg=typer.colors.GREEN)
|
|
2925
|
-
|
|
2926
|
-
# 6) 若此前有其它函数因依赖当前符号而在源码中放置了 todo!("<symbol>"),则立即回头消除(复用代码编写与修复Agent)
|
|
2927
|
-
current_rust_fn = self._extract_rust_fn_name_from_sig(rust_sig)
|
|
2928
|
-
# 收集需要处理的符号(去重,避免 qname 和 name 相同时重复处理)
|
|
2929
|
-
symbols_to_resolve = []
|
|
2930
|
-
if rec.qname:
|
|
2931
|
-
symbols_to_resolve.append(rec.qname)
|
|
2932
|
-
if rec.name and rec.name != rec.qname: # 如果 name 与 qname 不同,才添加
|
|
2933
|
-
symbols_to_resolve.append(rec.name)
|
|
2934
|
-
# 处理每个符号(去重后)
|
|
2935
|
-
for sym in symbols_to_resolve:
|
|
2936
|
-
typer.secho(f"[c2rust-transpiler][todo] 清理 todo!(\'{sym}\') 的出现位置", fg=typer.colors.BLUE)
|
|
2937
|
-
self._resolve_pending_todos_for_symbol(sym, module, current_rust_fn, rust_sig)
|
|
2938
|
-
# 如果有处理任何符号,统一运行一次 cargo test(避免重复运行)
|
|
2939
|
-
if symbols_to_resolve:
|
|
2940
|
-
typer.secho("[c2rust-transpiler][build] 处理 todo 后重新运行 cargo test", fg=typer.colors.MAGENTA)
|
|
2941
|
-
self._cargo_build_loop()
|
|
2942
|
-
|
|
2943
|
-
typer.secho("[c2rust-transpiler] 所有符合条件的函数均已处理完毕。", fg=typer.colors.GREEN)
|
|
2944
|
-
finally:
|
|
2945
|
-
os.chdir(prev_cwd)
|
|
2946
|
-
typer.secho(f"[c2rust-transpiler][end] 已恢复工作目录: {os.getcwd()}", fg=typer.colors.BLUE)
|
|
707
|
+
"""主流程(委托给 TranspilerExecutor)"""
|
|
708
|
+
executor = TranspilerExecutor(
|
|
709
|
+
project_root=self.project_root,
|
|
710
|
+
crate_dir=self.crate_dir,
|
|
711
|
+
progress=self.progress,
|
|
712
|
+
resume=self.resume,
|
|
713
|
+
fn_index_by_id=self.fn_index_by_id,
|
|
714
|
+
load_order_index_func=self._load_order_index,
|
|
715
|
+
should_skip_func=self._should_skip,
|
|
716
|
+
read_source_span_func=self._read_source_span,
|
|
717
|
+
plan_module_and_signature_func=self._plan_module_and_signature,
|
|
718
|
+
update_progress_current_func=self._update_progress_current,
|
|
719
|
+
mark_converted_func=self._mark_converted,
|
|
720
|
+
reset_function_context_func=self._reset_function_context,
|
|
721
|
+
ensure_mod_chain_for_module_func=self._ensure_mod_chain_for_module,
|
|
722
|
+
ensure_top_level_pub_mod_func=self._ensure_top_level_pub_mod,
|
|
723
|
+
get_crate_commit_hash_func=self._get_crate_commit_hash,
|
|
724
|
+
reset_to_commit_func=self._reset_to_commit,
|
|
725
|
+
run_cargo_fmt_func=self._run_cargo_fmt,
|
|
726
|
+
untranslated_callee_symbols_func=self._untranslated_callee_symbols,
|
|
727
|
+
codeagent_generate_impl_func=self._codeagent_generate_impl,
|
|
728
|
+
refresh_compact_context_func=self._refresh_compact_context,
|
|
729
|
+
cargo_build_loop_func=self._cargo_build_loop,
|
|
730
|
+
review_and_optimize_func=self._review_and_optimize,
|
|
731
|
+
extract_rust_fn_name_from_sig_func=self._extract_rust_fn_name_from_sig,
|
|
732
|
+
resolve_pending_todos_for_symbol_func=self._resolve_pending_todos_for_symbol,
|
|
733
|
+
save_progress_func=self._save_progress,
|
|
734
|
+
consecutive_fix_failures_getter=lambda: self._consecutive_fix_failures,
|
|
735
|
+
consecutive_fix_failures_setter=lambda v: setattr(
|
|
736
|
+
self, "_consecutive_fix_failures", v
|
|
737
|
+
),
|
|
738
|
+
current_function_start_commit_getter=lambda: self._current_function_start_commit,
|
|
739
|
+
current_function_start_commit_setter=lambda v: setattr(
|
|
740
|
+
self, "_current_function_start_commit", v
|
|
741
|
+
),
|
|
742
|
+
get_build_loop_has_fixes_func=self._get_build_loop_has_fixes,
|
|
743
|
+
ensure_cargo_toml_bin_func=self._ensure_cargo_toml_bin,
|
|
744
|
+
)
|
|
745
|
+
executor.execute()
|
|
2947
746
|
|
|
2948
747
|
|
|
2949
748
|
def run_transpile(
|
|
@@ -2980,4 +779,4 @@ def run_transpile(
|
|
|
2980
779
|
root_symbols=root_symbols,
|
|
2981
780
|
non_interactive=non_interactive,
|
|
2982
781
|
)
|
|
2983
|
-
t.transpile()
|
|
782
|
+
t.transpile()
|