cmake_file_api/objects/codemodel_v2/codemodel.rs
1#![allow(clippy::module_name_repetitions)]
2
3use crate::objects::codemodel_v2::{Directory, Target};
4use crate::objects::{MajorMinor, Object, ObjectKind};
5use serde::{Deserialize, Serialize};
6use std::path::PathBuf;
7use crate::reply;
8
9/// The codemodel object kind describes the build system structure as modeled by `CMake`.
10#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
11#[serde(rename_all = "camelCase")]
12#[non_exhaustive]
13pub struct CodeModel {
14 /// Kind of the codemodel object.
15 pub kind: ObjectKind,
16
17 /// Version of the codemodel object.
18 pub version: MajorMinor,
19
20 /// Paths of the codemodel object.
21 pub paths: CodemodelPaths,
22
23 /// Available build configurations.
24 /// On single-configuration generators there is one entry for the value of the CMAKE_BUILD_TYPE variable.
25 /// For multi-configuration generators there is an entry for each configuration listed in the CMAKE_CONFIGURATION_TYPES variable.
26 pub configurations: Vec<Configuration>,
27}
28
29#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
30#[serde(rename_all = "camelCase")]
31#[non_exhaustive]
32pub struct CodemodelPaths {
33 /// Absolute path to the top-level source directory, represented with forward slashes.
34 pub build: PathBuf,
35
36 /// Absolute path to the top-level build directory, represented with forward slashes.
37 pub source: PathBuf,
38}
39
40#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
41#[serde(rename_all = "camelCase")]
42#[non_exhaustive]
43pub struct Configuration {
44 /// A string specifying the name of the configuration, e.g. Debug.
45 pub name: String,
46
47 /// Top-level project and subprojects defined in the build system.
48 /// Each (sub-)project corresponds to a source directory whose CMakeLists.txt file calls the project() command with a project name different from its parent directory.
49 /// The first entry corresponds to the top-level project.
50 pub projects: Vec<Project>,
51
52 /// Build system directory info whose source directory contains a CMakeLists.txt file.
53 /// The first entry corresponds to the top-level directory
54 #[serde(rename = "directories")]
55 pub directory_refs: Vec<DirectoryReference>,
56
57 /// Build system targets.
58 /// Such targets are created by calls to add_executable(), add_library(), and add_custom_target(),
59 /// excluding imported targets and interface libraries (which do not generate any build rules).
60 #[serde(rename = "targets")]
61 pub target_refs: Vec<TargetReference>,
62
63 /// The following members are not part of the JSON file.
64 /// They are used to store the actual objects that the references point to.
65
66 /// Directory objects.
67 /// The position in the vector corresponds to the index in the directory_refs vector.
68 #[serde(skip)]
69 pub directories: Vec<Directory>,
70
71 /// Target objects.
72 /// The position in the vector corresponds to the index in the target_refs vector.
73 #[serde(skip)]
74 pub targets: Vec<Target>,
75}
76
77#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
78#[serde(rename_all = "camelCase")]
79#[non_exhaustive]
80pub struct DirectoryReference {
81 /// Path to the source directory, represented with forward slashes.
82 /// If the directory is inside the top-level source directory then the path is specified
83 /// relative to that directory (with . for the top-level source directory itself).
84 /// Otherwise, the path is absolute.
85 pub source: PathBuf,
86
87 /// Path to the build directory, represented with forward slashes.
88 /// If the directory is inside the top-level build directory then the path is specified
89 /// relative to that directory (with . for the top-level build directory itself).
90 /// Otherwise, the path is absolute.
91 pub build: PathBuf,
92
93 /// Optional member that is present when the directory is not top-level.
94 /// The value is an unsigned integer 0-based index of another entry in the main directories array
95 /// that corresponds to the parent directory that added this directory as a subdirectory.
96 pub parent_index: Option<usize>,
97
98 /// Optional member that is present when the directory has subdirectories.
99 /// Each entry corresponding to child directory created by the add_subdirectory() or subdirs() command.
100 /// Each entry is an unsigned integer 0-based index of another entry in the main directories array.
101 #[serde(default)]
102 pub child_indexes: Vec<usize>,
103
104 /// An unsigned integer 0-based index into the main projects array indicating the build system project to which the directory belongs.
105 pub project_index: usize,
106
107 /// Optional member that is present when the directory itself has targets, excluding those belonging to subdirectories.
108 /// Each entry corresponding to the targets.
109 /// Each entry is an unsigned integer 0-based index into the main targets array.
110 #[serde(default)]
111 pub target_indexes: Vec<usize>,
112
113 /// Optional member present when a minimum required version of CMake is known for the directory.
114 /// This is the `<min>` version given to the most local call to the cmake_minimum_required(VERSION) command in the directory itself or
115 /// one of its ancestors.
116 #[serde(rename = "minimumCMakeVersion")]
117 pub minimum_cmake_version: Option<MinimumCmakeVersion>,
118
119 /// True when the directory or one of its subdirectories contains any install() rules, i.e. whether a make install or equivalent rule is available.
120 #[serde(default)]
121 pub has_install_rule: bool,
122
123 /// Path relative to the codemodel file to another JSON file containing a "codemodel" version 2 "directory" object.
124 pub json_file: PathBuf,
125}
126
127#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
128#[serde(rename_all = "camelCase")]
129#[non_exhaustive]
130pub struct MinimumCmakeVersion {
131 /// A string specifying the minimum required version in the format
132 /// \<major\>.\<minor\>.\[\<patch\>\[.\<tweak\>]]\[\<suffix\>]
133 /// Each component is an unsigned integer and the suffix may be an arbitrary string.
134 #[serde(rename = "string")]
135 pub version: String,
136}
137
138#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
139#[serde(rename_all = "camelCase")]
140#[non_exhaustive]
141pub struct Project {
142 /// A string specifying the name given to the project() command.
143 pub name: String,
144
145 /// Optional member that is present when the project is not top-level.
146 /// The value is an unsigned integer 0-based index of another entry in the main projects array that corresponds to the parent project
147 /// that added this project as a subproject.
148 pub parent_index: Option<usize>,
149
150 /// Optional member that is present when the project has subprojects.
151 /// Entries corresponding to the subprojects.
152 /// Each entry is an unsigned integer 0-based index of another entry in the main projects array.
153 #[serde(default)]
154 pub child_indexes: Vec<usize>,
155
156 /// Entries corresponding to build system directories that are part of the project.
157 /// The first entry corresponds to the top-level directory of the project.
158 /// Each entry is an unsigned integer 0-based index into the main directories array.
159 pub directory_indexes: Vec<usize>,
160
161 /// Optional member that is present when the project itself has targets, excluding those belonging to subprojects.
162 /// Entries corresponding to the targets.
163 /// Each entry is an unsigned integer 0-based index into the main targets array.
164 #[serde(default)]
165 pub target_indexes: Vec<usize>,
166}
167
168#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
169#[serde(rename_all = "camelCase")]
170#[non_exhaustive]
171pub struct TargetReference {
172 /// A string specifying the target name.
173 pub name: String,
174
175 /// A string uniquely identifying the target.
176 /// This matches the id field in the file referenced by jsonFile.
177 pub id: String,
178
179 /// An unsigned integer 0-based index into the main directories array indicating
180 /// the build system directory in which the target is defined.
181 pub directory_index: usize,
182
183 /// An unsigned integer 0-based index into the main projects array indicating the
184 /// build system project in which the target is defined.
185 pub project_index: usize,
186
187 /// Path relative to the codemodel file to another JSON file containing a "codemodel" version 2 "target" object.
188 pub json_file: PathBuf,
189}
190
191impl Object for CodeModel {
192 fn kind() -> ObjectKind {
193 ObjectKind::CodeModel
194 }
195
196 fn major() -> u32 {
197 2
198 }
199
200 fn resolve_references(&mut self, reader: &reply::Reader) -> Result<(), reply::ReaderError> {
201 let reply_dir = reply::dir(reader.build_dir());
202
203 // resolve targets and directories references
204 for config in &mut self.configurations {
205 for target_ref in &config.target_refs {
206 config
207 .targets
208 .push(reply::Reader::parse_reply(reply_dir.join(&target_ref.json_file))?);
209 }
210
211 for directory_ref in &config.directory_refs {
212 config.directories.push(reply::Reader::parse_reply(
213 reply_dir.join(&directory_ref.json_file),
214 )?);
215 }
216 }
217
218 Ok(())
219 }
220}
221
222#[cfg(test)]
223mod tests {
224 use crate::objects;
225 use crate::objects::codemodel_v2::*;
226 use crate::objects::MajorMinor;
227 use serde_json::json;
228 use std::path::PathBuf;
229
230 #[test]
231 fn test_model() {
232 let json = json!({
233 "kind": "codemodel",
234 "version": { "major": 2, "minor": 6 },
235 "paths": {
236 "source": "/path/to/top-level-source-dir",
237 "build": "/path/to/top-level-build-dir"
238 },
239 "configurations": [
240 {
241 "name": "Debug",
242 "directories": [
243 {
244 "source": ".",
245 "build": ".",
246 "childIndexes": [ 1 ],
247 "projectIndex": 0,
248 "targetIndexes": [ 0 ],
249 "hasInstallRule": true,
250 "minimumCMakeVersion": {
251 "string": "3.14"
252 },
253 "jsonFile": "<file>"
254 },
255 {
256 "source": "sub",
257 "build": "sub",
258 "parentIndex": 0,
259 "projectIndex": 0,
260 "targetIndexes": [ 1 ],
261 "minimumCMakeVersion": {
262 "string": "3.14"
263 },
264 "jsonFile": "<file>"
265 }
266 ],
267 "projects": [
268 {
269 "name": "MyProject",
270 "directoryIndexes": [ 0, 1 ],
271 "targetIndexes": [ 0, 1 ]
272 }
273 ],
274 "targets": [
275 {
276 "name": "MyExecutable",
277 "directoryIndex": 0,
278 "projectIndex": 0,
279 "jsonFile": "<file>",
280 "id": "0"
281 },
282 {
283 "name": "MyLibrary",
284 "directoryIndex": 1,
285 "projectIndex": 0,
286 "jsonFile": "<file>",
287 "id": "1"
288 }
289 ]
290 }
291 ]
292 });
293
294 let model = serde_json::from_value::<CodeModel>(json).unwrap();
295 assert_eq!(model.kind, objects::ObjectKind::CodeModel);
296 assert_eq!(model.version, MajorMinor { major: 2, minor: 6 });
297 assert_eq!(
298 model.paths,
299 CodemodelPaths {
300 source: "/path/to/top-level-source-dir".into(),
301 build: "/path/to/top-level-build-dir".into()
302 }
303 );
304 assert_eq!(model.configurations.len(), 1);
305 assert_eq!(model.configurations[0].name, "Debug");
306 assert_eq!(model.configurations[0].directory_refs.len(), 2);
307 assert_eq!(
308 model.configurations[0].directory_refs[0].source,
309 PathBuf::from(".")
310 );
311 assert_eq!(model.configurations[0].projects.len(), 1);
312 assert_eq!(model.configurations[0].projects[0].name, "MyProject");
313 assert_eq!(model.configurations[0].target_refs.len(), 2);
314 assert_eq!(model.configurations[0].target_refs[0].name, "MyExecutable");
315 }
316}