Skip to content

Commit bd81f51

Browse files
authored
Merge pull request #24 from divmadan/devel/cmake-frontend
Develop CMake frontend
2 parents e60a5cc + ba2e993 commit bd81f51

File tree

2 files changed

+248
-40
lines changed

2 files changed

+248
-40
lines changed

clang_bind/cmake_frontend.py

Lines changed: 248 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,248 @@
1+
import json
2+
import clang.cindex as clang
3+
from pathlib import Path
4+
5+
6+
class CompilationDatabase:
7+
"""Class to get information from a CMake compilation database."""
8+
9+
def __init__(self, build_dir):
10+
self.compilation_database = clang.CompilationDatabase.fromDirectory(
11+
buildDir=build_dir
12+
)
13+
14+
def get_compilation_arguments(self, filename=None):
15+
"""Returns the compilation commands extracted from the compilation database
16+
17+
:param filename: Get compilation arguments of the file, defaults to None: get for all files
18+
:type filename: str, optional
19+
:return: ilenames and their compiler arguments: {filename: compiler arguments}
20+
:rtype: dict
21+
"""
22+
23+
if filename:
24+
# Get compilation commands from the compilation database for the given file
25+
compilation_commands = self.compilation_database.getCompileCommands(
26+
filename=filename
27+
)
28+
else:
29+
# Get all compilation commands from the compilation database
30+
compilation_commands = self.compilation_database.getAllCompileCommands()
31+
32+
return {
33+
command.filename: list(command.arguments)[1:-1]
34+
for command in compilation_commands
35+
}
36+
37+
38+
class Target:
39+
"""Class to get information about targets found from the CMake file API."""
40+
41+
def __init__(self, target_file):
42+
with open(target_file) as f:
43+
self.target = json.load(f)
44+
45+
def get_artifacts(self):
46+
"""Get artifacts from the target.
47+
48+
:return: Artifacts' paths
49+
:rtype: list
50+
"""
51+
return [artifact.get("path") for artifact in self.target.get("artifacts", [])]
52+
53+
def get_commands(self):
54+
"""Get commands from the target.
55+
56+
:return: Commands concerning the build system.
57+
:rtype: list
58+
"""
59+
return self.target.get("backtraceGraph", {}).get("commands")
60+
61+
def get_compile_groups(self):
62+
"""Get compile groups from the target.
63+
64+
:return: list of dictionaries of compile groups
65+
:rtype: list
66+
"""
67+
return [
68+
{
69+
"fragments": [
70+
compile_command_fragment.get("fragment")
71+
for compile_command_fragment in compile_group.get(
72+
"compileCommandFragments", []
73+
)
74+
],
75+
"defines": [
76+
define.get("define") for define in compile_group.get("defines", [])
77+
],
78+
"includes": [
79+
include.get("path") for include in compile_group.get("includes", [])
80+
],
81+
"language": compile_group.get("language"),
82+
}
83+
for compile_group in self.target.get("compileGroups", [])
84+
]
85+
86+
def get_dependencies(self):
87+
"""Get dependencies from the target.
88+
89+
:return: Dependencies ids' list
90+
:rtype: list
91+
"""
92+
return [
93+
dependency.get("id") for dependency in self.target.get("dependencies", [])
94+
]
95+
96+
def get_files(self):
97+
"""Get files from the target.
98+
99+
:return: Files concerning the build system.
100+
:rtype: list
101+
"""
102+
return self.target.get("backtraceGraph", {}).get("files")
103+
104+
def get_folder(self):
105+
"""Get folder from the target.
106+
107+
:return: Folder of the target.
108+
:rtype: str
109+
"""
110+
return self.target.get("folder", {}).get("name")
111+
112+
def get_id(self):
113+
"""Get id from the target.
114+
115+
:return: ID of the target
116+
:rtype: str
117+
"""
118+
return self.target.get("id")
119+
120+
def get_install(self):
121+
"""Get install info from the target.
122+
123+
:return: Install info
124+
:rtype: dict
125+
"""
126+
install = self.target.get("install", {})
127+
return {
128+
"destinations": [
129+
destination.get("path")
130+
for destination in install.get("destinations", [])
131+
],
132+
"prefix": install.get("prefix", {}).get("path"),
133+
}
134+
135+
def get_link(self):
136+
"""Get link info from the target.
137+
138+
:return: Link info
139+
:rtype: dict
140+
"""
141+
link = self.target.get("link", {})
142+
command_fragments = link.get("commandFragments", [])
143+
return {
144+
"flags_fragments": [
145+
command_fragment.get("fragment")
146+
for command_fragment in command_fragments
147+
if command_fragment.get("role") == "flags"
148+
],
149+
"libraries_fragments": [
150+
command_fragment.get("fragment")
151+
for command_fragment in command_fragments
152+
if command_fragment.get("role") == "libraries"
153+
],
154+
"language": link.get("language"),
155+
}
156+
157+
def get_name(self):
158+
"""Get name from the target.
159+
160+
:return: Name of the target
161+
:rtype: str
162+
"""
163+
return self.target.get("name")
164+
165+
def get_name_on_disk(self):
166+
"""Get name on disk from the target.
167+
168+
:return: Name on disk of the target
169+
:rtype: str
170+
"""
171+
return self.target.get("nameOnDisk")
172+
173+
def get_paths(self):
174+
"""Get paths from the target.
175+
176+
:return: Paths of the target.
177+
:rtype: dict
178+
"""
179+
return self.target.get("paths")
180+
181+
def get_sources(self):
182+
"""Get sources from the target.
183+
184+
:return: Sources of the target.
185+
:rtype: list
186+
"""
187+
return [sources.get("path") for sources in self.target.get("sources", [])]
188+
189+
def get_type(self):
190+
"""Get type from the target.
191+
192+
:return: Type of the target.
193+
:rtype: str
194+
"""
195+
return self.target.get("type")
196+
197+
198+
class CMakeFileAPI:
199+
"""CMake File API front end."""
200+
201+
def __init__(self, build_dir):
202+
self.reply_dir = Path(build_dir, ".cmake", "api", "v1", "reply")
203+
self.targets = {}
204+
self._set_targets_from_codemodel()
205+
206+
def _set_targets_from_codemodel(self):
207+
"""Populate targets dict by accessing values in the codemodel file."""
208+
209+
for file in Path(self.reply_dir).iterdir(): # iterate for all files
210+
if file.name.startswith("codemodel"): # find the codemodel file
211+
codemodel_file = Path(self.reply_dir, file)
212+
with open(codemodel_file) as f:
213+
codemodel = json.load(f) # load the JSON codemodel
214+
215+
for configuration in codemodel.get(
216+
"configurations", []
217+
): # for each configuration
218+
for target in configuration.get("targets", []): # for each targets
219+
target_file = target["jsonFile"] # get the target file
220+
target_obj = Target(Path(self.reply_dir, target_file))
221+
self.targets[target_obj.get_name()] = target_obj
222+
223+
def get_dependencies(self, target=None):
224+
"""Get dependencies of the target(s).
225+
226+
:param target: Target to get the dependencies, defaults to None
227+
:type target: str, optional
228+
:return: Dependencies of the target(s).
229+
:rtype: dict
230+
"""
231+
targets = [self.targets.get(target)] if target else self.targets.values()
232+
return {
233+
target.get_name(): list(
234+
map(lambda x: x.split("::")[0], target.get_dependencies())
235+
)
236+
for target in targets
237+
}
238+
239+
def get_sources(self, target=None):
240+
"""Get sources of the target(s).
241+
242+
:param target: Target to get the dependencies, defaults to None
243+
:type target: str, optional
244+
:return: Sources of the target(s).
245+
:rtype: dict
246+
"""
247+
targets = [self.targets.get(target)] if target else self.targets.values()
248+
return {target.get_name(): target.get_sources() for target in targets}

clang_bind/compilation_database.py

Lines changed: 0 additions & 40 deletions
This file was deleted.

0 commit comments

Comments
 (0)