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
jarvis/jarvis_c2rust/scanner.py
CHANGED
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
#!/usr/bin/env python3
|
|
2
|
-
# -*- coding: utf-8 -*-
|
|
3
1
|
"""
|
|
4
2
|
使用 libclang 的 C/C++ 函数扫描器和调用图提取器。
|
|
5
3
|
|
|
@@ -47,17 +45,23 @@ JSONL 文件
|
|
|
47
45
|
|
|
48
46
|
from __future__ import annotations
|
|
49
47
|
|
|
50
|
-
|
|
51
48
|
import json
|
|
52
|
-
import os
|
|
53
49
|
|
|
54
|
-
import
|
|
50
|
+
from jarvis.jarvis_utils.output import PrettyOutput
|
|
51
|
+
|
|
52
|
+
#!/usr/bin/env python3
|
|
53
|
+
# -*- coding: utf-8 -*-
|
|
54
|
+
import os
|
|
55
|
+
import shutil
|
|
55
56
|
import time
|
|
56
57
|
from dataclasses import dataclass
|
|
57
58
|
from pathlib import Path
|
|
58
59
|
from typing import Any, Dict, Iterable, List, Optional, Set
|
|
60
|
+
|
|
59
61
|
import typer
|
|
60
|
-
|
|
62
|
+
|
|
63
|
+
from jarvis.jarvis_c2rust.constants import SOURCE_EXTS, TYPE_KINDS
|
|
64
|
+
|
|
61
65
|
|
|
62
66
|
# ---------------------------
|
|
63
67
|
# libclang loader
|
|
@@ -88,8 +92,10 @@ def _try_import_libclang() -> Any:
|
|
|
88
92
|
# Verify Python clang bindings major version (if available)
|
|
89
93
|
py_major: Optional[int] = None
|
|
90
94
|
try:
|
|
91
|
-
import clang as _clang
|
|
92
95
|
import re as _re
|
|
96
|
+
|
|
97
|
+
import clang as _clang
|
|
98
|
+
|
|
93
99
|
v = getattr(_clang, "__version__", None)
|
|
94
100
|
if v:
|
|
95
101
|
m = _re.match(r"(\\d+)", str(v))
|
|
@@ -111,8 +117,10 @@ def _try_import_libclang() -> Any:
|
|
|
111
117
|
try:
|
|
112
118
|
import ctypes
|
|
113
119
|
import re as _re
|
|
120
|
+
|
|
114
121
|
class CXString(ctypes.Structure):
|
|
115
122
|
_fields_ = [("data", ctypes.c_void_p), ("private_flags", ctypes.c_uint)]
|
|
123
|
+
|
|
116
124
|
lib = ctypes.CDLL(path)
|
|
117
125
|
# Ensure correct ctypes signatures to avoid mis-parsing strings
|
|
118
126
|
lib.clang_getClangVersion.restype = CXString
|
|
@@ -175,11 +183,13 @@ def _try_import_libclang() -> Any:
|
|
|
175
183
|
for maj in (21, 20, 19, 18, 17, 16):
|
|
176
184
|
candidates.append(base / f"libclang.so.{maj}")
|
|
177
185
|
# Generic names
|
|
178
|
-
candidates.extend(
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
186
|
+
candidates.extend(
|
|
187
|
+
[
|
|
188
|
+
base / "libclang.so", # Linux
|
|
189
|
+
base / "libclang.dylib", # macOS
|
|
190
|
+
base / "libclang.dll", # Windows
|
|
191
|
+
]
|
|
192
|
+
)
|
|
183
193
|
for cand in candidates:
|
|
184
194
|
if cand.exists() and _ensure_supported_and_set(str(cand)):
|
|
185
195
|
return cindex
|
|
@@ -196,52 +206,65 @@ def _try_import_libclang() -> Any:
|
|
|
196
206
|
candidates_llvm: List[Path] = []
|
|
197
207
|
for maj in (21, 20, 19, 18, 17, 16):
|
|
198
208
|
candidates_llvm.append(p / f"libclang.so.{maj}")
|
|
199
|
-
candidates_llvm.extend(
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
209
|
+
candidates_llvm.extend(
|
|
210
|
+
[
|
|
211
|
+
p / "libclang.so",
|
|
212
|
+
p / "libclang.dylib",
|
|
213
|
+
p / "libclang.dll",
|
|
214
|
+
]
|
|
215
|
+
)
|
|
204
216
|
for cand in candidates_llvm:
|
|
205
217
|
if cand.exists() and _ensure_supported_and_set(str(cand)):
|
|
206
218
|
return cindex
|
|
207
219
|
|
|
208
220
|
# 4) Common locations for versions 16-21
|
|
209
221
|
import platform as _platform
|
|
222
|
+
|
|
210
223
|
sys_name = _platform.system()
|
|
211
224
|
path_candidates: List[Path] = []
|
|
212
225
|
if sys_name == "Linux":
|
|
213
226
|
for maj in (21, 20, 19, 18, 17, 16):
|
|
214
|
-
path_candidates.extend(
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
227
|
+
path_candidates.extend(
|
|
228
|
+
[
|
|
229
|
+
Path(f"/usr/lib/llvm-{maj}/lib/libclang.so.{maj}"),
|
|
230
|
+
Path(f"/usr/lib/llvm-{maj}/lib/libclang.so"),
|
|
231
|
+
]
|
|
232
|
+
)
|
|
218
233
|
# Generic fallbacks
|
|
219
|
-
path_candidates.extend(
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
234
|
+
path_candidates.extend(
|
|
235
|
+
[
|
|
236
|
+
Path("/usr/local/lib/libclang.so.21"),
|
|
237
|
+
Path("/usr/local/lib/libclang.so.20"),
|
|
238
|
+
Path("/usr/local/lib/libclang.so.19"),
|
|
239
|
+
Path("/usr/local/lib/libclang.so.18"),
|
|
240
|
+
Path("/usr/local/lib/libclang.so.17"),
|
|
241
|
+
Path("/usr/local/lib/libclang.so.16"),
|
|
242
|
+
Path("/usr/local/lib/libclang.so"),
|
|
243
|
+
Path("/usr/lib/libclang.so.21"),
|
|
244
|
+
Path("/usr/lib/libclang.so.20"),
|
|
245
|
+
Path("/usr/lib/libclang.so.19"),
|
|
246
|
+
Path("/usr/lib/libclang.so.18"),
|
|
247
|
+
Path("/usr/lib/libclang.so.17"),
|
|
248
|
+
Path("/usr/lib/libclang.so.16"),
|
|
249
|
+
Path("/usr/lib/libclang.so"),
|
|
250
|
+
]
|
|
251
|
+
)
|
|
235
252
|
elif sys_name == "Darwin":
|
|
236
253
|
# Homebrew llvm@N formulas
|
|
237
254
|
for maj in (21, 20, 19, 18, 17, 16):
|
|
238
|
-
path_candidates.append(
|
|
239
|
-
|
|
255
|
+
path_candidates.append(
|
|
256
|
+
Path(f"/opt/homebrew/opt/llvm@{maj}/lib/libclang.dylib")
|
|
257
|
+
)
|
|
258
|
+
path_candidates.append(
|
|
259
|
+
Path(f"/usr/local/opt/llvm@{maj}/lib/libclang.dylib")
|
|
260
|
+
)
|
|
240
261
|
# Generic llvm formula path (may be symlinked to a specific version)
|
|
241
|
-
path_candidates.extend(
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
262
|
+
path_candidates.extend(
|
|
263
|
+
[
|
|
264
|
+
Path("/opt/homebrew/opt/llvm/lib/libclang.dylib"),
|
|
265
|
+
Path("/usr/local/opt/llvm/lib/libclang.dylib"),
|
|
266
|
+
]
|
|
267
|
+
)
|
|
245
268
|
else:
|
|
246
269
|
# Best-effort on other systems (Windows)
|
|
247
270
|
path_candidates = [
|
|
@@ -298,6 +321,8 @@ def _try_import_libclang() -> Any:
|
|
|
298
321
|
" export CLANG_LIBRARY_FILE=/usr/lib/llvm-21/lib/libclang.so # Linux (请调整版本)\n"
|
|
299
322
|
" export CLANG_LIBRARY_FILE=/opt/homebrew/opt/llvm@21/lib/libclang.dylib # macOS (请调整版本)\n"
|
|
300
323
|
)
|
|
324
|
+
|
|
325
|
+
|
|
301
326
|
# ---------------------------
|
|
302
327
|
# Data structures
|
|
303
328
|
# ---------------------------
|
|
@@ -317,10 +342,6 @@ class FunctionInfo:
|
|
|
317
342
|
language: str
|
|
318
343
|
|
|
319
344
|
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
345
|
# ---------------------------
|
|
325
346
|
# Compile commands loader
|
|
326
347
|
# ---------------------------
|
|
@@ -350,7 +371,7 @@ def load_compile_commands(cc_path: Path) -> Dict[str, List[str]]:
|
|
|
350
371
|
except Exception:
|
|
351
372
|
return {}
|
|
352
373
|
|
|
353
|
-
|
|
374
|
+
result: Dict[str, List[str]] = {}
|
|
354
375
|
for entry in data:
|
|
355
376
|
file_path = Path(entry.get("file", "")).resolve()
|
|
356
377
|
if not file_path:
|
|
@@ -358,38 +379,16 @@ def load_compile_commands(cc_path: Path) -> Dict[str, List[str]]:
|
|
|
358
379
|
if "arguments" in entry and isinstance(entry["arguments"], list):
|
|
359
380
|
# arguments usually includes the compiler as argv[0]
|
|
360
381
|
args = entry["arguments"][1:] if entry["arguments"] else []
|
|
382
|
+
result[str(file_path)] = args
|
|
361
383
|
else:
|
|
362
384
|
# fallback to split command string
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
cleaned: List[str] = []
|
|
370
|
-
skip_next = False
|
|
371
|
-
for a in args:
|
|
372
|
-
if skip_next:
|
|
373
|
-
skip_next = False
|
|
374
|
-
continue
|
|
375
|
-
if a in ("-c",):
|
|
376
|
-
continue
|
|
377
|
-
if a in ("-o", "-MF"):
|
|
378
|
-
skip_next = True
|
|
379
|
-
continue
|
|
380
|
-
if a.startswith("-o"):
|
|
381
|
-
continue
|
|
382
|
-
cleaned.append(a)
|
|
383
|
-
mapping[str(file_path)] = cleaned
|
|
384
|
-
return mapping
|
|
385
|
+
command = entry.get("command", "")
|
|
386
|
+
if command:
|
|
387
|
+
result[str(file_path)] = (
|
|
388
|
+
command.split()[1:] if len(command.split()) > 1 else []
|
|
389
|
+
)
|
|
390
|
+
return result
|
|
385
391
|
|
|
386
|
-
# ---------------------------
|
|
387
|
-
# File discovery
|
|
388
|
-
# ---------------------------
|
|
389
|
-
SOURCE_EXTS: Set[str] = {
|
|
390
|
-
".c", ".cc", ".cpp", ".cxx", ".C",
|
|
391
|
-
".h", ".hh", ".hpp", ".hxx",
|
|
392
|
-
}
|
|
393
392
|
|
|
394
393
|
def iter_source_files(root: Path) -> Iterable[Path]:
|
|
395
394
|
for p in root.rglob("*"):
|
|
@@ -501,13 +500,18 @@ def scan_file(cindex, file_path: Path, args: List[str]) -> List[FunctionInfo]:
|
|
|
501
500
|
# Only consider functions with definitions in this file
|
|
502
501
|
if is_function_like(node) and node.is_definition():
|
|
503
502
|
loc_file = node.location.file
|
|
504
|
-
if
|
|
503
|
+
if (
|
|
504
|
+
loc_file is not None
|
|
505
|
+
and Path(loc_file.name).resolve() == file_path.resolve()
|
|
506
|
+
):
|
|
505
507
|
try:
|
|
506
508
|
name = node.spelling or ""
|
|
507
509
|
qualified_name = get_qualified_name(node)
|
|
508
510
|
signature = node.displayname or name
|
|
509
511
|
try:
|
|
510
|
-
return_type =
|
|
512
|
+
return_type = (
|
|
513
|
+
node.result_type.spelling
|
|
514
|
+
) # not available for constructors/destructors
|
|
511
515
|
except Exception:
|
|
512
516
|
return_type = ""
|
|
513
517
|
params = collect_params(node)
|
|
@@ -568,6 +572,7 @@ def scan_directory(scan_root: Path, db_path: Optional[Path] = None) -> Path:
|
|
|
568
572
|
if cindex is None:
|
|
569
573
|
try:
|
|
570
574
|
from clang import cindex as _ci
|
|
575
|
+
|
|
571
576
|
cindex = _ci
|
|
572
577
|
except Exception as e:
|
|
573
578
|
raise RuntimeError(f"Failed to load libclang bindings: {e}")
|
|
@@ -582,6 +587,7 @@ def scan_directory(scan_root: Path, db_path: Optional[Path] = None) -> Path:
|
|
|
582
587
|
def _has_symbol(lib_path: str, symbol: str) -> bool:
|
|
583
588
|
try:
|
|
584
589
|
import ctypes
|
|
590
|
+
|
|
585
591
|
lib = ctypes.CDLL(lib_path)
|
|
586
592
|
getattr(lib, symbol)
|
|
587
593
|
return True
|
|
@@ -590,6 +596,7 @@ def scan_directory(scan_root: Path, db_path: Optional[Path] = None) -> Path:
|
|
|
590
596
|
|
|
591
597
|
# Build candidate search dirs (Linux/macOS)
|
|
592
598
|
import platform as _platform
|
|
599
|
+
|
|
593
600
|
sys_name = _platform.system()
|
|
594
601
|
lib_candidates = []
|
|
595
602
|
if sys_name == "Linux":
|
|
@@ -610,12 +617,16 @@ def scan_directory(scan_root: Path, db_path: Optional[Path] = None) -> Path:
|
|
|
610
617
|
"/usr/local/opt/llvm/lib/libclang.dylib",
|
|
611
618
|
]
|
|
612
619
|
|
|
613
|
-
good = [
|
|
620
|
+
good = [
|
|
621
|
+
p
|
|
622
|
+
for p in lib_candidates
|
|
623
|
+
if Path(p).exists() and _has_symbol(p, "clang_getOffsetOfBase")
|
|
624
|
+
]
|
|
614
625
|
hint = ""
|
|
615
626
|
if good:
|
|
616
627
|
hint = f"\n建议的包含所需符号的库:\n export CLANG_LIBRARY_FILE={good[0]}\n然后重新运行: jarvis-c2rust scan -r {scan_root}"
|
|
617
628
|
|
|
618
|
-
|
|
629
|
+
PrettyOutput.auto_print(
|
|
619
630
|
"[c2rust-scanner] 检测到 libclang/python 绑定不匹配 (未定义符号)。"
|
|
620
631
|
f"\n详情: {msg}"
|
|
621
632
|
"\n这通常意味着您的 Python 'clang' 绑定版本高于已安装的 libclang。"
|
|
@@ -623,14 +634,12 @@ def scan_directory(scan_root: Path, db_path: Optional[Path] = None) -> Path:
|
|
|
623
634
|
"- 安装/更新 libclang 以匹配您 Python 'clang' 的主版本 (例如 16-21)。\n"
|
|
624
635
|
"- 或将 Python 'clang' 版本固定为与系统 libclang 匹配 (例如 pip install 'clang>=16,<22')。\n"
|
|
625
636
|
"- 或设置 CLANG_LIBRARY_FILE 指向匹配的 libclang 共享库。\n"
|
|
626
|
-
f"{hint}"
|
|
627
|
-
fg=typer.colors.RED,
|
|
628
|
-
err=True,
|
|
637
|
+
f"{hint}"
|
|
629
638
|
)
|
|
630
639
|
raise typer.Exit(code=2)
|
|
631
640
|
else:
|
|
632
641
|
# Other initialization errors: surface and exit
|
|
633
|
-
|
|
642
|
+
PrettyOutput.auto_print(f"❌ [c2rust-scanner] libclang 初始化失败: {e}")
|
|
634
643
|
raise typer.Exit(code=2)
|
|
635
644
|
|
|
636
645
|
# compile_commands
|
|
@@ -644,7 +653,9 @@ def scan_directory(scan_root: Path, db_path: Optional[Path] = None) -> Path:
|
|
|
644
653
|
|
|
645
654
|
files = list(iter_source_files(scan_root))
|
|
646
655
|
total_files = len(files)
|
|
647
|
-
|
|
656
|
+
PrettyOutput.auto_print(
|
|
657
|
+
f"📋 [c2rust-scanner] 正在扫描 {scan_root} 目录下的 {total_files} 个文件"
|
|
658
|
+
)
|
|
648
659
|
|
|
649
660
|
scanned = 0
|
|
650
661
|
total_functions = 0
|
|
@@ -760,9 +771,11 @@ def scan_directory(scan_root: Path, db_path: Optional[Path] = None) -> Path:
|
|
|
760
771
|
# If we hit undefined symbol, it's a libclang/python bindings mismatch; abort with guidance
|
|
761
772
|
msg = str(e)
|
|
762
773
|
if "undefined symbol" in msg:
|
|
774
|
+
|
|
763
775
|
def _has_symbol(lib_path: str, symbol: str) -> bool:
|
|
764
776
|
try:
|
|
765
777
|
import ctypes
|
|
778
|
+
|
|
766
779
|
lib = ctypes.CDLL(lib_path)
|
|
767
780
|
getattr(lib, symbol)
|
|
768
781
|
return True
|
|
@@ -770,6 +783,7 @@ def scan_directory(scan_root: Path, db_path: Optional[Path] = None) -> Path:
|
|
|
770
783
|
return False
|
|
771
784
|
|
|
772
785
|
import platform as _platform
|
|
786
|
+
|
|
773
787
|
sys_name = _platform.system()
|
|
774
788
|
lib_candidates2: List[str] = []
|
|
775
789
|
if sys_name == "Linux":
|
|
@@ -786,12 +800,17 @@ def scan_directory(scan_root: Path, db_path: Optional[Path] = None) -> Path:
|
|
|
786
800
|
"/usr/local/opt/llvm/lib/libclang.dylib",
|
|
787
801
|
]
|
|
788
802
|
|
|
789
|
-
good = [
|
|
803
|
+
good = [
|
|
804
|
+
lp
|
|
805
|
+
for lp in lib_candidates2
|
|
806
|
+
if Path(lp).exists()
|
|
807
|
+
and _has_symbol(lp, "clang_getOffsetOfBase")
|
|
808
|
+
]
|
|
790
809
|
hint = ""
|
|
791
810
|
if good:
|
|
792
811
|
hint = f"\n建议的包含所需符号的库:\n export CLANG_LIBRARY_FILE={good[0]}\n然后重新运行: jarvis-c2rust scan -r {scan_root}"
|
|
793
812
|
|
|
794
|
-
|
|
813
|
+
PrettyOutput.auto_print(
|
|
795
814
|
"[c2rust-scanner] 解析期间检测到 libclang/python 绑定不匹配 (未定义符号)。"
|
|
796
815
|
f"\n详情: {msg}"
|
|
797
816
|
"\n这通常意味着您的 Python 'clang' 绑定版本高于已安装的 libclang。"
|
|
@@ -799,9 +818,7 @@ def scan_directory(scan_root: Path, db_path: Optional[Path] = None) -> Path:
|
|
|
799
818
|
"- 安装/更新 libclang 以匹配您 Python 'clang' 的主版本 (例如 19/20)。\n"
|
|
800
819
|
"- 或将 Python 'clang' 版本固定为与系统 libclang 匹配 (例如 pip install 'clang==18.*')。\n"
|
|
801
820
|
"- 或设置 CLANG_LIBRARY_FILE 指向匹配的 libclang 共享库。\n"
|
|
802
|
-
f"{hint}"
|
|
803
|
-
fg=typer.colors.RED,
|
|
804
|
-
err=True,
|
|
821
|
+
f"{hint}"
|
|
805
822
|
)
|
|
806
823
|
raise typer.Exit(code=2)
|
|
807
824
|
|
|
@@ -809,7 +826,7 @@ def scan_directory(scan_root: Path, db_path: Optional[Path] = None) -> Path:
|
|
|
809
826
|
try:
|
|
810
827
|
funcs = scan_file(cindex, p, [])
|
|
811
828
|
except Exception:
|
|
812
|
-
|
|
829
|
+
PrettyOutput.auto_print(f"❌ [c2rust-scanner] 解析 {p} 失败: {e}")
|
|
813
830
|
continue
|
|
814
831
|
|
|
815
832
|
# Write JSONL
|
|
@@ -840,7 +857,9 @@ def scan_directory(scan_root: Path, db_path: Optional[Path] = None) -> Path:
|
|
|
840
857
|
|
|
841
858
|
scanned += 1
|
|
842
859
|
if scanned % 20 == 0 or scanned == total_files:
|
|
843
|
-
|
|
860
|
+
PrettyOutput.auto_print(
|
|
861
|
+
f"📊 [c2rust-scanner] 进度: {scanned}/{total_files} 个文件, {total_functions} 个函数, {total_types} 个类型"
|
|
862
|
+
)
|
|
844
863
|
finally:
|
|
845
864
|
try:
|
|
846
865
|
f_sym.close()
|
|
@@ -857,22 +876,31 @@ def scan_directory(scan_root: Path, db_path: Optional[Path] = None) -> Path:
|
|
|
857
876
|
"source_root": str(scan_root),
|
|
858
877
|
}
|
|
859
878
|
try:
|
|
860
|
-
meta_json.write_text(
|
|
879
|
+
meta_json.write_text(
|
|
880
|
+
json.dumps(meta, ensure_ascii=False, indent=2), encoding="utf-8"
|
|
881
|
+
)
|
|
861
882
|
except Exception:
|
|
862
883
|
pass
|
|
863
884
|
|
|
864
|
-
|
|
865
|
-
|
|
885
|
+
PrettyOutput.auto_print(
|
|
886
|
+
f"✅ [c2rust-scanner] 完成。收集到的函数: {total_functions}, 类型: {total_types}, 符号: {total_functions + total_types}"
|
|
887
|
+
)
|
|
888
|
+
PrettyOutput.auto_print(
|
|
889
|
+
f"📊 [c2rust-scanner] JSONL 已写入: {symbols_raw_jsonl} (原始符号)"
|
|
890
|
+
)
|
|
866
891
|
# 同步生成基线 symbols.jsonl(与 raw 等价),便于后续流程仅基于 symbols.jsonl 运行
|
|
867
892
|
try:
|
|
868
893
|
shutil.copy2(symbols_raw_jsonl, symbols_curated_jsonl)
|
|
869
|
-
|
|
894
|
+
PrettyOutput.auto_print(
|
|
895
|
+
f"📊 [c2rust-scanner] JSONL 基线已写入: {symbols_curated_jsonl} (用于后续流程)"
|
|
896
|
+
)
|
|
870
897
|
except Exception as _e:
|
|
871
|
-
|
|
898
|
+
PrettyOutput.auto_print(f"❌ [c2rust-scanner] 生成 symbols.jsonl 失败: {_e}")
|
|
872
899
|
raise
|
|
873
|
-
|
|
900
|
+
PrettyOutput.auto_print(f"📋 [c2rust-scanner] 元数据已写入: {meta_json}")
|
|
874
901
|
return symbols_raw_jsonl
|
|
875
902
|
|
|
903
|
+
|
|
876
904
|
# ---------------------------
|
|
877
905
|
# Type scanning
|
|
878
906
|
# ---------------------------
|
|
@@ -890,18 +918,6 @@ class TypeInfo:
|
|
|
890
918
|
language: str
|
|
891
919
|
|
|
892
920
|
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
TYPE_KINDS: Set[str] = {
|
|
896
|
-
"STRUCT_DECL",
|
|
897
|
-
"UNION_DECL",
|
|
898
|
-
"ENUM_DECL",
|
|
899
|
-
"CXX_RECORD_DECL", # C++ class/struct/union
|
|
900
|
-
"TYPEDEF_DECL",
|
|
901
|
-
"TYPE_ALIAS_DECL",
|
|
902
|
-
}
|
|
903
|
-
|
|
904
|
-
|
|
905
921
|
def scan_types_file(cindex, file_path: Path, args: List[str]) -> List[TypeInfo]:
|
|
906
922
|
index = cindex.Index.create()
|
|
907
923
|
tu = index.parse(
|
|
@@ -922,7 +938,12 @@ def scan_types_file(cindex, file_path: Path, args: List[str]) -> List[TypeInfo]:
|
|
|
922
938
|
|
|
923
939
|
if kind in TYPE_KINDS:
|
|
924
940
|
# Accept full definitions for record/enum; typedef/alias are inherently definitions
|
|
925
|
-
need_def = kind in {
|
|
941
|
+
need_def = kind in {
|
|
942
|
+
"STRUCT_DECL",
|
|
943
|
+
"UNION_DECL",
|
|
944
|
+
"ENUM_DECL",
|
|
945
|
+
"CXX_RECORD_DECL",
|
|
946
|
+
}
|
|
926
947
|
if (not need_def) or node.is_definition():
|
|
927
948
|
try:
|
|
928
949
|
name = node.spelling or ""
|
|
@@ -1040,8 +1061,6 @@ def generate_dot_from_db(db_path: Path, out_path: Path) -> Path:
|
|
|
1040
1061
|
return base
|
|
1041
1062
|
|
|
1042
1063
|
# Prepare output path
|
|
1043
|
-
if out_path is None:
|
|
1044
|
-
out_path = sjsonl.parent / "global_refgraph.dot"
|
|
1045
1064
|
out_path = Path(out_path)
|
|
1046
1065
|
out_path.parent.mkdir(parents=True, exist_ok=True)
|
|
1047
1066
|
|
|
@@ -1075,6 +1094,7 @@ def find_root_function_ids(db_path: Path) -> List[int]:
|
|
|
1075
1094
|
- 严格使用 ref 字段
|
|
1076
1095
|
- 函数与类型统一处理(不区分)
|
|
1077
1096
|
"""
|
|
1097
|
+
|
|
1078
1098
|
def _resolve_symbols_jsonl_path(hint: Path) -> Path:
|
|
1079
1099
|
p = Path(hint)
|
|
1080
1100
|
if p.is_file() and p.suffix.lower() == ".jsonl":
|
|
@@ -1131,7 +1151,9 @@ def find_root_function_ids(db_path: Path) -> List[int]:
|
|
|
1131
1151
|
return root_ids
|
|
1132
1152
|
|
|
1133
1153
|
|
|
1134
|
-
def compute_translation_order_jsonl(
|
|
1154
|
+
def compute_translation_order_jsonl(
|
|
1155
|
+
db_path: Path, out_path: Optional[Path] = None
|
|
1156
|
+
) -> Path:
|
|
1135
1157
|
"""
|
|
1136
1158
|
Compute translation order on reference graph and write order to JSONL.
|
|
1137
1159
|
Data source: symbols.jsonl (or provided .jsonl path), strictly using ref field and including all symbols.
|
|
@@ -1145,6 +1167,7 @@ def compute_translation_order_jsonl(db_path: Path, out_path: Optional[Path] = No
|
|
|
1145
1167
|
"created_at": "YYYY-MM-DDTHH:MM:SS"
|
|
1146
1168
|
}
|
|
1147
1169
|
"""
|
|
1170
|
+
|
|
1148
1171
|
def _resolve_symbols_jsonl_path(hint: Path) -> Path:
|
|
1149
1172
|
p = Path(hint)
|
|
1150
1173
|
if p.is_file() and p.suffix.lower() == ".jsonl":
|
|
@@ -1278,6 +1301,7 @@ def compute_translation_order_jsonl(db_path: Path, out_path: Optional[Path] = No
|
|
|
1278
1301
|
|
|
1279
1302
|
# Kahn on reversed DAG
|
|
1280
1303
|
from collections import deque
|
|
1304
|
+
|
|
1281
1305
|
q = deque(sorted([i for i in range(comp_count) if indeg[i] == 0]))
|
|
1282
1306
|
comp_order: List[int] = []
|
|
1283
1307
|
while q:
|
|
@@ -1322,19 +1346,25 @@ def compute_translation_order_jsonl(db_path: Path, out_path: Optional[Path] = No
|
|
|
1322
1346
|
nm = str(meta.get("name") or "").lower()
|
|
1323
1347
|
qn = str(meta.get("qname") or "").lower()
|
|
1324
1348
|
# Configurable delayed entry symbols via env:
|
|
1325
|
-
# -
|
|
1326
|
-
# -
|
|
1349
|
+
# - c2rust_delay_entry_symbols
|
|
1350
|
+
# - c2rust_delay_entries
|
|
1327
1351
|
# - C2RUST_DELAY_ENTRIES
|
|
1328
|
-
entries_env =
|
|
1329
|
-
|
|
1330
|
-
|
|
1352
|
+
entries_env = (
|
|
1353
|
+
os.environ.get("c2rust_delay_entry_symbols")
|
|
1354
|
+
or os.environ.get("c2rust_delay_entries")
|
|
1355
|
+
or os.environ.get("C2RUST_DELAY_ENTRIES")
|
|
1356
|
+
or ""
|
|
1357
|
+
)
|
|
1331
1358
|
entries_set = set()
|
|
1332
1359
|
if entries_env:
|
|
1333
1360
|
try:
|
|
1334
1361
|
import re as _re
|
|
1362
|
+
|
|
1335
1363
|
parts = _re.split(r"[,\s;]+", entries_env.strip())
|
|
1336
1364
|
except Exception:
|
|
1337
|
-
parts = [
|
|
1365
|
+
parts = [
|
|
1366
|
+
p.strip() for p in entries_env.replace(";", ",").split(",")
|
|
1367
|
+
]
|
|
1338
1368
|
entries_set = {p.strip().lower() for p in parts if p and p.strip()}
|
|
1339
1369
|
# If configured, use the provided entries; otherwise fallback to default 'main'
|
|
1340
1370
|
if entries_set:
|
|
@@ -1373,17 +1403,25 @@ def compute_translation_order_jsonl(db_path: Path, out_path: Optional[Path] = No
|
|
|
1373
1403
|
roots_labels = []
|
|
1374
1404
|
if root_id is not None:
|
|
1375
1405
|
meta_r = by_id.get(root_id, {})
|
|
1376
|
-
rlabel =
|
|
1406
|
+
rlabel = (
|
|
1407
|
+
meta_r.get("qname") or meta_r.get("name") or f"sym_{root_id}"
|
|
1408
|
+
)
|
|
1377
1409
|
roots_labels = [rlabel]
|
|
1378
|
-
steps.append(
|
|
1379
|
-
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
|
|
1384
|
-
|
|
1385
|
-
|
|
1386
|
-
|
|
1410
|
+
steps.append(
|
|
1411
|
+
{
|
|
1412
|
+
"step": len(steps) + 1,
|
|
1413
|
+
"ids": sorted(selected),
|
|
1414
|
+
"items": [
|
|
1415
|
+
by_id.get(nid, {}).get("record")
|
|
1416
|
+
for nid in sorted(selected)
|
|
1417
|
+
if isinstance(by_id.get(nid, {}).get("record"), dict)
|
|
1418
|
+
],
|
|
1419
|
+
"symbols": syms,
|
|
1420
|
+
"group": len(syms) > 1,
|
|
1421
|
+
"roots": roots_labels,
|
|
1422
|
+
"created_at": now_ts,
|
|
1423
|
+
}
|
|
1424
|
+
)
|
|
1387
1425
|
|
|
1388
1426
|
# Emit delayed entry functions as the final step for this root
|
|
1389
1427
|
if delayed_entries:
|
|
@@ -1399,15 +1437,21 @@ def compute_translation_order_jsonl(db_path: Path, out_path: Optional[Path] = No
|
|
|
1399
1437
|
meta_r = by_id.get(root_id, {})
|
|
1400
1438
|
rlabel = meta_r.get("qname") or meta_r.get("name") or f"sym_{root_id}"
|
|
1401
1439
|
roots_labels = [rlabel]
|
|
1402
|
-
steps.append(
|
|
1403
|
-
|
|
1404
|
-
|
|
1405
|
-
|
|
1406
|
-
|
|
1407
|
-
|
|
1408
|
-
|
|
1409
|
-
|
|
1410
|
-
|
|
1440
|
+
steps.append(
|
|
1441
|
+
{
|
|
1442
|
+
"step": len(steps) + 1,
|
|
1443
|
+
"ids": sorted(delayed_entries),
|
|
1444
|
+
"items": [
|
|
1445
|
+
by_id.get(nid, {}).get("record")
|
|
1446
|
+
for nid in sorted(delayed_entries)
|
|
1447
|
+
if isinstance(by_id.get(nid, {}).get("record"), dict)
|
|
1448
|
+
],
|
|
1449
|
+
"symbols": syms,
|
|
1450
|
+
"group": len(syms) > 1,
|
|
1451
|
+
"roots": roots_labels,
|
|
1452
|
+
"created_at": now_ts,
|
|
1453
|
+
}
|
|
1454
|
+
)
|
|
1411
1455
|
|
|
1412
1456
|
for rid in sorted(roots, key=lambda r: len(root_reach.get(r, set())), reverse=True):
|
|
1413
1457
|
_emit_for_root(rid)
|
|
@@ -1430,7 +1474,9 @@ def compute_translation_order_jsonl(db_path: Path, out_path: Optional[Path] = No
|
|
|
1430
1474
|
# Purge redundant fields before writing (keep ids and records; drop symbols/items)
|
|
1431
1475
|
try:
|
|
1432
1476
|
# 保留 items(包含完整符号记录及替换信息),仅移除冗余的 symbols 文本标签
|
|
1433
|
-
steps = [
|
|
1477
|
+
steps = [
|
|
1478
|
+
dict((k, v) for k, v in st.items() if k not in ("symbols",)) for st in steps
|
|
1479
|
+
]
|
|
1434
1480
|
except Exception:
|
|
1435
1481
|
pass
|
|
1436
1482
|
with open(out_path, "w", encoding="utf-8") as fo:
|
|
@@ -1502,7 +1548,9 @@ def export_root_subgraphs_to_dir(db_path: Path, out_dir: Path) -> List[Path]:
|
|
|
1502
1548
|
if not s:
|
|
1503
1549
|
return "root"
|
|
1504
1550
|
s = s.replace("::", "__")
|
|
1505
|
-
return "".join(ch if ch.isalnum() or ch in ("_", "-") else "_" for ch in s)[
|
|
1551
|
+
return "".join(ch if ch.isalnum() or ch in ("_", "-") else "_" for ch in s)[
|
|
1552
|
+
:120
|
|
1553
|
+
]
|
|
1506
1554
|
|
|
1507
1555
|
generated: List[Path] = []
|
|
1508
1556
|
root_ids = find_root_function_ids(db_path)
|
|
@@ -1549,7 +1597,11 @@ def export_root_subgraphs_to_dir(db_path: Path, out_dir: Path) -> List[Path]:
|
|
|
1549
1597
|
edges.add((src_node, dst))
|
|
1550
1598
|
|
|
1551
1599
|
# Write DOT
|
|
1552
|
-
root_base =
|
|
1600
|
+
root_base = (
|
|
1601
|
+
by_id.get(rid, {}).get("qname")
|
|
1602
|
+
or by_id.get(rid, {}).get("name")
|
|
1603
|
+
or f"sym_{rid}"
|
|
1604
|
+
)
|
|
1553
1605
|
fname = f"subgraph_root_{rid}_{sanitize_filename(root_base)}.dot"
|
|
1554
1606
|
out_path = out_dir / fname
|
|
1555
1607
|
with open(out_path, "w", encoding="utf-8") as f:
|
|
@@ -1563,7 +1615,9 @@ def export_root_subgraphs_to_dir(db_path: Path, out_dir: Path) -> List[Path]:
|
|
|
1563
1615
|
for nid, lbl in node_labels.items():
|
|
1564
1616
|
safe_label = lbl.replace("\\", "\\\\").replace('"', '\\"')
|
|
1565
1617
|
if nid.startswith("ext"):
|
|
1566
|
-
f.write(
|
|
1618
|
+
f.write(
|
|
1619
|
+
f' {nid} [label="{safe_label}", shape=ellipse, style=dashed, color=gray50, fontcolor=gray30];\n'
|
|
1620
|
+
)
|
|
1567
1621
|
else:
|
|
1568
1622
|
f.write(f' {nid} [label="{safe_label}", shape=box];\n')
|
|
1569
1623
|
|
|
@@ -1582,6 +1636,7 @@ def export_root_subgraphs_to_dir(db_path: Path, out_dir: Path) -> List[Path]:
|
|
|
1582
1636
|
# Third-party replacement evaluation
|
|
1583
1637
|
# ---------------------------
|
|
1584
1638
|
|
|
1639
|
+
|
|
1585
1640
|
def run_scan(
|
|
1586
1641
|
dot: Optional[Path] = None,
|
|
1587
1642
|
only_dot: bool = False,
|
|
@@ -1592,20 +1647,22 @@ def run_scan(
|
|
|
1592
1647
|
) -> None:
|
|
1593
1648
|
# Scan for C/C++ functions and persist results to JSONL; optionally generate DOT.
|
|
1594
1649
|
# Determine data path
|
|
1595
|
-
root = Path(
|
|
1596
|
-
Path(
|
|
1597
|
-
data_path_curated = Path(
|
|
1650
|
+
root = Path(".")
|
|
1651
|
+
Path(".") / ".jarvis" / "c2rust" / "symbols_raw.jsonl"
|
|
1652
|
+
data_path_curated = Path(".") / ".jarvis" / "c2rust" / "symbols.jsonl"
|
|
1598
1653
|
|
|
1599
1654
|
# Helper: render a DOT file to PNG using Graphviz 'dot'
|
|
1600
1655
|
def _render_dot_to_png(dot_file: Path, png_out: Optional[Path] = None) -> Path:
|
|
1601
1656
|
try:
|
|
1602
|
-
from shutil import which
|
|
1603
1657
|
import subprocess
|
|
1658
|
+
from shutil import which
|
|
1604
1659
|
except Exception as _e:
|
|
1605
1660
|
raise RuntimeError(f"准备 PNG 渲染时出现环境问题: {_e}")
|
|
1606
1661
|
exe = which("dot")
|
|
1607
1662
|
if not exe:
|
|
1608
|
-
raise RuntimeError(
|
|
1663
|
+
raise RuntimeError(
|
|
1664
|
+
"在 PATH 中未找到 Graphviz 'dot'。请安装 graphviz 并确保 'dot' 可用。"
|
|
1665
|
+
)
|
|
1609
1666
|
dot_file = Path(dot_file)
|
|
1610
1667
|
if png_out is None:
|
|
1611
1668
|
png_out = dot_file.with_suffix(".png")
|
|
@@ -1613,7 +1670,9 @@ def run_scan(
|
|
|
1613
1670
|
png_out = Path(png_out)
|
|
1614
1671
|
png_out.parent.mkdir(parents=True, exist_ok=True)
|
|
1615
1672
|
try:
|
|
1616
|
-
subprocess.run(
|
|
1673
|
+
subprocess.run(
|
|
1674
|
+
[exe, "-Tpng", str(dot_file), "-o", str(png_out)], check=True
|
|
1675
|
+
)
|
|
1617
1676
|
except FileNotFoundError:
|
|
1618
1677
|
raise RuntimeError("未找到 Graphviz 'dot' 可执行文件。")
|
|
1619
1678
|
except subprocess.CalledProcessError as e:
|
|
@@ -1624,18 +1683,24 @@ def run_scan(
|
|
|
1624
1683
|
try:
|
|
1625
1684
|
scan_directory(root)
|
|
1626
1685
|
except Exception as e:
|
|
1627
|
-
|
|
1686
|
+
PrettyOutput.auto_print(f"❌ [c2rust-scanner] 错误: {e}")
|
|
1628
1687
|
raise typer.Exit(code=1)
|
|
1629
1688
|
else:
|
|
1630
1689
|
# Only-generate mode (no rescan). 验证输入,仅基于既有 symbols.jsonl 进行可选的 DOT/子图输出;此处不计算翻译顺序。
|
|
1631
1690
|
if not data_path_curated.exists():
|
|
1632
|
-
|
|
1691
|
+
PrettyOutput.auto_print(
|
|
1692
|
+
f"⚠️ [c2rust-scanner] 未找到数据: {data_path_curated}"
|
|
1693
|
+
)
|
|
1633
1694
|
raise typer.Exit(code=2)
|
|
1634
1695
|
if only_dot and dot is None:
|
|
1635
|
-
|
|
1696
|
+
PrettyOutput.auto_print(
|
|
1697
|
+
"⚠️ [c2rust-scanner] --only-dot 需要 --dot 来指定输出文件"
|
|
1698
|
+
)
|
|
1636
1699
|
raise typer.Exit(code=2)
|
|
1637
1700
|
if only_subgraphs and subgraphs_dir is None:
|
|
1638
|
-
|
|
1701
|
+
PrettyOutput.auto_print(
|
|
1702
|
+
"⚠️ [c2rust-scanner] --only-subgraphs 需要 --subgraphs-dir 来指定输出目录"
|
|
1703
|
+
)
|
|
1639
1704
|
raise typer.Exit(code=2)
|
|
1640
1705
|
|
|
1641
1706
|
# Generate DOT (global) if requested
|
|
@@ -1643,12 +1708,14 @@ def run_scan(
|
|
|
1643
1708
|
try:
|
|
1644
1709
|
# 使用正式符号表生成可视化
|
|
1645
1710
|
generate_dot_from_db(data_path_curated, dot)
|
|
1646
|
-
|
|
1711
|
+
PrettyOutput.auto_print(f"📊 [c2rust-scanner] DOT 文件已写入: {dot}")
|
|
1647
1712
|
if png:
|
|
1648
1713
|
png_path = _render_dot_to_png(dot)
|
|
1649
|
-
|
|
1714
|
+
PrettyOutput.auto_print(
|
|
1715
|
+
f"📊 [c2rust-scanner] PNG 文件已写入: {png_path}"
|
|
1716
|
+
)
|
|
1650
1717
|
except Exception as e:
|
|
1651
|
-
|
|
1718
|
+
PrettyOutput.auto_print(f"❌ [c2rust-scanner] 写入 DOT/PNG 失败: {e}")
|
|
1652
1719
|
raise typer.Exit(code=1)
|
|
1653
1720
|
|
|
1654
1721
|
# Generate per-root subgraphs if requested
|
|
@@ -1665,17 +1732,13 @@ def run_scan(
|
|
|
1665
1732
|
except Exception as _e:
|
|
1666
1733
|
# Fail fast on PNG generation error for subgraphs to make issues visible
|
|
1667
1734
|
raise
|
|
1668
|
-
|
|
1669
|
-
f"[c2rust-scanner] 根节点子图已写入: {len(files)} 个 DOT 文件和 {png_count} 个 PNG 文件 -> {subgraphs_dir}"
|
|
1670
|
-
fg=typer.colors.GREEN,
|
|
1735
|
+
PrettyOutput.auto_print(
|
|
1736
|
+
f"📊 [c2rust-scanner] 根节点子图已写入: {len(files)} 个 DOT 文件和 {png_count} 个 PNG 文件 -> {subgraphs_dir}"
|
|
1671
1737
|
)
|
|
1672
1738
|
else:
|
|
1673
|
-
|
|
1674
|
-
f"[c2rust-scanner] 根节点子图已写入: {len(files)} 个文件 -> {subgraphs_dir}"
|
|
1675
|
-
fg=typer.colors.GREEN,
|
|
1739
|
+
PrettyOutput.auto_print(
|
|
1740
|
+
f"📊 [c2rust-scanner] 根节点子图已写入: {len(files)} 个文件 -> {subgraphs_dir}"
|
|
1676
1741
|
)
|
|
1677
1742
|
except Exception as e:
|
|
1678
|
-
|
|
1743
|
+
PrettyOutput.auto_print(f"❌ [c2rust-scanner] 写入子图 DOT/PNG 失败: {e}")
|
|
1679
1744
|
raise typer.Exit(code=1)
|
|
1680
|
-
|
|
1681
|
-
|