cmake_preset/
lib.rs

1//! A tool library for building C++ code using CMake Presets in Rust projects
2//!
3//! This library provides a simple way to call CMake Presets from Rust's build.rs script.
4
5use std::marker::PhantomData;
6use std::path::{Path, PathBuf};
7use std::process::Command;
8
9/// Type status: Set the project directory
10pub struct StatusSetProjectDir;
11
12/// Type status: Set the preset name
13pub struct StatusSetConfigPreset;
14
15/// Type status: Set the library name
16pub struct StatusSetLibraryName;
17
18/// Type status: Configuration
19pub struct StatusConfig;
20
21/// Type status: Construction
22pub struct StatusBuild;
23
24/// Builder for CMake presets
25#[derive(Debug)]
26pub struct CMakePresetBuilder<S> {
27    output_dir: PathBuf,
28    project_dir: PathBuf,
29    config_preset: String,
30    library_name: String,
31    _phantom: PhantomData<S>,
32}
33
34impl CMakePresetBuilder<StatusSetProjectDir> {
35    /// Create a new CMakePresetBuilder
36    pub fn new() -> CMakePresetBuilder<StatusSetProjectDir> {
37        CMakePresetBuilder {
38            output_dir: std::env::var("OUT_DIR")
39                .unwrap_or_else(|_| ".".to_string())
40                .into(),
41            project_dir: PathBuf::new(),
42            config_preset: "".to_string(),
43            library_name: "".to_string(),
44            _phantom: PhantomData,
45        }
46    }
47
48    /// Set the project directory
49    /// The project directory is the directory where the CMakeLists.txt file is located
50    /// # Arguments
51    /// * `project_dir` - The project directory
52    pub fn set_project_dir<P: AsRef<Path>>(
53        self,
54        project_dir: P,
55    ) -> CMakePresetBuilder<StatusSetConfigPreset> {
56        if !project_dir.as_ref().exists() {
57            panic!(
58                "Project directory:{:?} does not exist",
59                project_dir.as_ref()
60            );
61        } else if !project_dir.as_ref().is_dir() {
62            panic!(
63                "Project directory:{:?} is not a directory",
64                project_dir.as_ref()
65            );
66        }
67
68        println!(
69            "cargo:rerun-if-changed={}/",
70            project_dir.as_ref().to_str().unwrap()
71        );
72        CMakePresetBuilder {
73            output_dir: self.output_dir,
74            project_dir: project_dir.as_ref().to_path_buf(),
75            config_preset: self.config_preset,
76            library_name: self.library_name,
77            _phantom: PhantomData,
78        }
79    }
80}
81
82impl CMakePresetBuilder<StatusSetConfigPreset> {
83    /// Set the preset to use for configuring the project.
84    /// # Arguments
85    /// * `preset_name` - The name of the preset to use for configuring the project.
86    pub fn set_config_preset(
87        self,
88        preset_name: impl AsRef<str>,
89    ) -> CMakePresetBuilder<StatusSetLibraryName> {
90        CMakePresetBuilder {
91            output_dir: self.output_dir,
92            project_dir: self.project_dir,
93            config_preset: preset_name.as_ref().into(),
94            library_name: self.library_name,
95            _phantom: PhantomData,
96        }
97    }
98}
99
100impl CMakePresetBuilder<StatusSetLibraryName> {
101    /// Set the name of the generated static library
102    /// # Arguments
103    /// * `library_name` - The name of the generated static library
104    pub fn set_library_name(
105        self,
106        library_name: impl AsRef<str>,
107    ) -> CMakePresetBuilder<StatusConfig> {
108        println!(
109            "cargo:rustc-link-search=native={}",
110            self.output_dir.to_str().unwrap()
111        );
112        println!("cargo:rustc-link-lib=static={}", library_name.as_ref());
113        CMakePresetBuilder {
114            output_dir: self.output_dir,
115            project_dir: self.project_dir,
116            config_preset: self.config_preset,
117            library_name: library_name.as_ref().to_string(),
118            _phantom: PhantomData,
119        }
120    }
121}
122
123impl CMakePresetBuilder<StatusConfig> {
124    /// Run cmake --preset <preset_name>
125    pub fn config(self) -> CMakePresetBuilder<StatusBuild> {
126        let output_dir = std::env::var("OUT_DIR").unwrap_or_else(|_| ".".to_string());
127
128        if !self.project_dir.exists() {
129            panic!("Project directory does not exist: {:?}", self.project_dir);
130        }
131
132        let output = Command::new("cmake")
133            .arg("--preset")
134            .arg(&self.config_preset)
135            .arg("-B")
136            .arg(&output_dir)
137            .current_dir(&self.project_dir)
138            .output()
139            .unwrap();
140
141        if !output.status.success() {
142            panic!(
143                "CMake configure failed: {}",
144                String::from_utf8_lossy(&output.stderr)
145            );
146        }
147
148        CMakePresetBuilder {
149            output_dir: self.output_dir,
150            project_dir: self.project_dir,
151            config_preset: self.config_preset,
152            library_name: self.library_name,
153            _phantom: PhantomData,
154        }
155    }
156}
157
158impl CMakePresetBuilder<StatusBuild> {
159    /// Run cmake --build
160    pub fn build(self) {
161        let output = Command::new("cmake")
162            .arg("--build")
163            .arg(&self.output_dir)
164            .arg("--parallel")
165            .current_dir(&self.project_dir)
166            .output()
167            .unwrap();
168
169        if !output.status.success() {
170            panic!(
171                "CMake build failed: {}",
172                String::from_utf8_lossy(&output.stdout)
173            );
174        }
175    }
176}