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}