|
20 | 20 | import subprocess |
21 | 21 | import tempfile |
22 | 22 | from pathlib import Path |
23 | | -from typing import Any, Sequence |
| 23 | +from typing import Any, List, Sequence |
24 | 24 | import difflib |
25 | 25 | import re |
26 | 26 | import sys |
@@ -119,9 +119,10 @@ def function_name_to_cpp_path(function_name: str, base_path = Path("src")) -> tu |
119 | 119 | def compile_cpp_code_for_function(function_name: str, contents: str) -> tuple[bool, str, str]: |
120 | 120 | """ |
121 | 121 | Write and compile cpp code for function identified by fully namespaced function name. |
| 122 | + Includes the generated cpp file automatically in the cmake sources list. |
122 | 123 | |
123 | 124 | Args: |
124 | | - function_name: Name of the function to extract, fully namespaced using '::' |
| 125 | + function_name: Name of the function to compile, fully namespaced using '::' |
125 | 126 | contents: New contents of the file |
126 | 127 | |
127 | 128 | Returns: |
@@ -149,6 +150,45 @@ def compile_cpp_code_for_function(function_name: str, contents: str) -> tuple[bo |
149 | 150 | # Compile the project and return the resulting state |
150 | 151 | return compile_project() |
151 | 152 |
|
| 153 | +@mcp.tool() |
| 154 | +def exclude_function_cpp_code_from_compilation(function_name: str) -> tuple[bool, str, str]: |
| 155 | + """ |
| 156 | + Excludes the generated cpp file from the cmake sources list, avoiding its compilation. |
| 157 | + |
| 158 | + Args: |
| 159 | + function_name: Name of the function to exclude, fully namespaced using '::' |
| 160 | + |
| 161 | + Returns: |
| 162 | + Tuple of (success, stdout, stderr) |
| 163 | + """ |
| 164 | + # Translate the function name into a path |
| 165 | + rstate, rresult, rerr = function_name_to_cpp_path(function_name=function_name) |
| 166 | + if not rstate: |
| 167 | + return rstate, "", f"could not resolve function name to file path: {rerr}" |
| 168 | + path = Path(rresult) |
| 169 | + if not path.exists(): |
| 170 | + return False, "", f"could not resolve function name to file path, file does not exist: {rerr}" |
| 171 | + |
| 172 | + # Ensure the cpp file is included in the build |
| 173 | + csentry = str(path).replace("\\", "/") |
| 174 | + if not csentry.startswith("src/"): |
| 175 | + return False, "", f"invalid cmake/openshc-sources.txt entry: {csentry}" |
| 176 | + |
| 177 | + # Store a backup |
| 178 | + i = 1 |
| 179 | + pcandidate = PATH_CMAKE_OPENSHC_SOURCES.with_name(PATH_CMAKE_OPENSHC_SOURCES.name + f".{i:{0}>3}") |
| 180 | + while pcandidate.exists(): |
| 181 | + i += 1 |
| 182 | + pcandidate = PATH_CMAKE_OPENSHC_SOURCES.with_name(PATH_CMAKE_OPENSHC_SOURCES.name + f".{i:{0}>3}") |
| 183 | + pcandidate.write_text(PATH_CMAKE_OPENSHC_SOURCES.read_text()) |
| 184 | + |
| 185 | + # Produce the new file without the line |
| 186 | + lines = PATH_CMAKE_OPENSHC_SOURCES.read_text().splitlines(False) |
| 187 | + lines = [line for line in lines if not line.startswith(csentry)] |
| 188 | + PATH_CMAKE_OPENSHC_SOURCES.write_text('\n'.join(lines) + '\n', newline='\n') |
| 189 | + |
| 190 | + return True, "cpp file excluded", "" |
| 191 | + |
152 | 192 | def read_function(function_name: str, base_path: Path = Path("src")): |
153 | 193 | rstate, rresult, rerr = function_name_to_cpp_path(function_name=function_name, base_path=base_path) |
154 | 194 | if not rstate: |
@@ -232,5 +272,25 @@ def fetch_cached_ghidra_function_decompilation(function_name: str) -> tuple[bool |
232 | 272 | """ |
233 | 273 | return read_function(function_name=function_name, base_path=Path("tools") / "mcp" / "ghidra_scripts" / "decompilation") |
234 | 274 |
|
| 275 | +@mcp.tool() |
| 276 | +def find_source_file_containing_text(text: str, glob: str = "**/*") -> List[str]: |
| 277 | + """ |
| 278 | + Returns a list of files in which 'text' can be found. The default file glob pattern is '**/*'. |
| 279 | + |
| 280 | + Args: |
| 281 | + text: the text to find, no regex is supported |
| 282 | + glob: the glob to use, useful to look in hpp or cpp files specifically |
| 283 | + |
| 284 | + Returns: |
| 285 | + list of file path strings |
| 286 | + """ |
| 287 | + results: List[str] = [] |
| 288 | + src = Path("src") |
| 289 | + for f in src.glob(pattern=glob): |
| 290 | + if f.is_file(): |
| 291 | + if text in f.read_text(encoding='UTF-8'): |
| 292 | + results.append(str(f.relative_to(src))) |
| 293 | + return results |
| 294 | + |
235 | 295 | if __name__ == "__main__": |
236 | 296 | mcp.run(transport="stdio") |
0 commit comments