libcsp_cargo_build/
lib.rs

1//! This crate provides a library to allow building the [`libcsp`](https://github.com/libcsp/libcsp)
2//! C library with cargo. You can find some more high-level information and examples in the
3//! [main repository](https://egit.irs.uni-stuttgart.de/rust/libcsp-rust).
4use std::{
5    io::{self, Write},
6    path::{Path, PathBuf},
7};
8
9pub mod cfg_keys {
10    pub const POSIX: &str = "CSP_POSIX";
11    pub const ZEPHYR: &str = "CSP_ZEPHYR";
12
13    pub const HAVE_STDIO: &str = "CSP_HAVE_STDIO";
14    pub const ENABLE_CSP_PRINT: &str = "CSP_ENABLE_CSP_PRINT";
15    pub const PRINT_STDIO: &str = "CSP_PRINT_STDIO";
16
17    pub const REPRODUCIBLE_BUILDS: &str = "CSP_REPRODUCIBLE_BUILDS";
18
19    pub const QFIFO_LEN: &str = "CSP_QFIFO_LEN";
20    pub const PORT_MAX_BIND: &str = "CSP_PORT_MAX_BIND";
21    pub const CONN_RXQUEUE_LEN: &str = "CSP_CONN_RXQUEUE_LEN";
22    pub const CONN_MAX: &str = "CSP_CONN_MAX";
23    pub const BUFFER_SIZE: &str = "CSP_BUFFER_SIZE";
24    pub const BUFFER_COUNT: &str = "CSP_BUFFER_COUNT";
25    pub const RDP_MAX_WINDOW: &str = "CSP_RDP_MAX_WINDOW";
26    pub const RTABLE_SIZE: &str = "CSP_RTABLE_SIZE";
27
28    pub const USE_RDP: &str = "CSP_USE_RDP";
29    pub const USE_HMAC: &str = "CSP_USE_HMAC";
30    pub const USE_PROMISC: &str = "CSP_USE_PROMISC";
31    pub const USE_RTABLE: &str = "CSP_USE_RTABLE";
32    pub const HAVE_LIBSOCKETCAN: &str = "CSP_HAVE_LIBSOCKETCAN";
33    pub const HAVE_LIBZMQ: &str = "CSP_HAVE_LIBZMQ";
34}
35
36const SRCS_LIST: &[&str] = &[
37    "csp_bridge.c",
38    "csp_buffer.c",
39    "csp_crc32.c",
40    "csp_debug.c",
41    "csp_id.c",
42    "csp_iflist.c",
43    "csp_conn.c",
44    "csp_init.c",
45    "csp_io.c",
46    "csp_port.c",
47    "csp_promisc.c",
48    "csp_qfifo.c",
49    "csp_port.c",
50    "csp_route.c",
51    "csp_dedup.c",
52    "csp_services.c",
53    "csp_service_handler.c",
54    "interfaces/csp_if_lo.c",
55    "interfaces/csp_if_kiss.c",
56    "interfaces/csp_if_tun.c",
57    "interfaces/csp_if_udp.c",
58    "crypto/csp_hmac.c",
59    "crypto/csp_sha1.c",
60];
61
62const ARCH_SRCS_UNIX: &[&str] = &[
63    "arch/posix/csp_clock.c",
64    "arch/posix/csp_semaphore.c",
65    "arch/posix/csp_system.c",
66    "arch/posix/csp_time.c",
67    "arch/posix/csp_queue.c",
68    "arch/posix/pthread_queue.c",
69];
70
71pub struct Config {
72    pub have_stdio: bool,
73    pub print_stdio: bool,
74    pub reproducible_builds: bool,
75    pub qfifo_len: u32,
76    pub port_max_bind: u32,
77    pub conn_rx_queue_len: u32,
78    pub conn_max: u32,
79    pub buffer_size: u32,
80    pub buffer_count: u32,
81    pub rdp_max_window: u32,
82    pub rtable_size: u32,
83    pub hmac: bool,
84    pub rtable: bool,
85    pub csp_print: bool,
86    pub promisc: bool,
87    pub rdp: bool,
88    pub yaml: bool,
89}
90
91impl Default for Config {
92    fn default() -> Self {
93        Self {
94            have_stdio: true,
95            print_stdio: true,
96            reproducible_builds: false,
97            qfifo_len: 16,
98            port_max_bind: 16,
99            conn_rx_queue_len: 16,
100            conn_max: 8,
101            buffer_size: 256,
102            buffer_count: 15,
103            rdp_max_window: 5,
104            rtable_size: 10,
105            hmac: true,
106            rtable: false,
107            csp_print: true,
108            promisc: true,
109            rdp: true,
110            yaml: false,
111        }
112    }
113}
114
115/// Primary builder structure used to compile the `libcsp` C library.
116///
117/// The [Self::cfg] field can be used to configure the library build. The will also take care
118/// of generating the autoconfig.h file required for the library configuration automatically based
119/// on the build [Config]. An API is also provided to generate the autoconfig.rs file required
120/// for compiling the [`libcsp-sys`](https://crates.io/crates/libcp-sys) Rust FFI bindings crate.
121///
122/// ## Example
123///
124/// The [example buildscript](https://egit.irs.uni-stuttgart.de/rust/libcsp-rust/src/branch/main/examples/build.rs)
125/// uses this builder structure to compile the library.
126pub struct Builder {
127    generate_autoconf_file: bool,
128    libcsp_path: PathBuf,
129    libcsp_src_path_base: PathBuf,
130    out_dir: PathBuf,
131    pub cfg: Config,
132    pub compiler_warnings: bool,
133    build: cc::Build,
134}
135
136#[derive(Debug, Copy, Clone, PartialEq, Eq, thiserror::Error)]
137pub enum BuildCreationError {
138    #[error("The specified libcsp path does not exist")]
139    PathDoesNotExist,
140    #[error("The specified libcsp path does not have the expected format of the library")]
141    InvalidLibcspFormat,
142}
143
144impl Builder {
145    /// Create a new builder instance. Returns [BuildCreationError] if the specified libcsp path
146    /// does not exist or does not have the expected format.
147    pub fn new(libcsp_path: PathBuf, out_dir: PathBuf) -> Result<Self, BuildCreationError> {
148        // Basic sanity checks for the passed libcsp path.
149        if !libcsp_path.exists() {
150            return Err(BuildCreationError::PathDoesNotExist);
151        }
152        if !libcsp_path
153            .join("include")
154            .join("csp")
155            .join("csp.h")
156            .exists()
157        {
158            return Err(BuildCreationError::InvalidLibcspFormat);
159        }
160
161        let mut libcsp_src_path_base = libcsp_path.clone();
162        libcsp_src_path_base.push("src");
163        Ok(Self {
164            generate_autoconf_file: true,
165            libcsp_path,
166            libcsp_src_path_base,
167            out_dir,
168            cfg: Default::default(),
169            compiler_warnings: true,
170            build: Default::default(),
171        })
172    }
173
174    /// Access to the underlying [cc::Build] builder object.
175    pub fn cc(&mut self) -> &cc::Build {
176        &self.build
177    }
178
179    /// Mutable access to the underlying [cc::Build] builder object.
180    pub fn cc_mut(&mut self) -> &mut cc::Build {
181        &mut self.build
182    }
183
184    pub fn compile(&mut self) -> io::Result<()> {
185        if self.generate_autoconf_file {
186            self.generate_autoconf_header_file_default_location()?;
187        }
188        for src in SRCS_LIST {
189            let mut next_file = self.libcsp_src_path_base.clone();
190            next_file.push(src);
191            self.build.file(next_file);
192        }
193        if self.cfg.rdp {
194            let mut next_file = self.libcsp_src_path_base.clone();
195            next_file.push("csp_rdp.c");
196            self.build.file(next_file);
197            let mut next_file = self.libcsp_src_path_base.clone();
198            next_file.push("csp_rdp_queue.c");
199            self.build.file(next_file);
200        }
201        if self.cfg.promisc {
202            let mut next_file = self.libcsp_src_path_base.clone();
203            next_file.push("csp_promisc.c");
204            self.build.file(next_file);
205        }
206        if self.cfg.csp_print {
207            let mut next_file = self.libcsp_src_path_base.clone();
208            next_file.push("csp_hex_dump.c");
209            self.build.file(next_file);
210        }
211        if self.cfg.yaml {
212            let mut next_file = self.libcsp_src_path_base.clone();
213            next_file.push("csp_yaml.c");
214            self.build.file(next_file);
215        }
216        if self.cfg.rtable {
217            let mut next_file = self.libcsp_src_path_base.clone();
218            next_file.push("csp_rtable_cidr.c");
219            self.build.file(next_file);
220        }
221
222        // TODO: UNIX does not necesarilly mean POSIX? Details to deal with later..
223        #[cfg(unix)]
224        self.posix_arch_files();
225
226        let mut inc_path = self.libcsp_path.clone();
227        inc_path.push("include");
228        self.build.include(inc_path);
229        self.build.include(&self.libcsp_src_path_base);
230        self.build.cargo_warnings(self.compiler_warnings);
231
232        self.build.compile("csp");
233        // TODO: We could generate some sort of information file which contains the
234        // compilation information and the version of hte library.
235        Ok(())
236    }
237
238    #[cfg(unix)]
239    fn posix_arch_files(&mut self) {
240        for src in ARCH_SRCS_UNIX {
241            let mut next_file = self.libcsp_src_path_base.clone();
242            next_file.push(src);
243            self.build.file(next_file);
244        }
245    }
246
247    pub fn generate_autoconf_header_file_default_location(&mut self) -> io::Result<()> {
248        let mut autoconf_dir = self.out_dir.join("cfg");
249        self.build.include(&autoconf_dir);
250        autoconf_dir.push("csp");
251        std::fs::create_dir_all(&autoconf_dir)?;
252        generate_autoconf_header_file(&autoconf_dir, &self.cfg)
253    }
254
255    pub fn generate_autoconf_header_file(&mut self, dir: impl AsRef<Path>) -> io::Result<()> {
256        generate_autoconf_header_file(dir, &self.cfg)
257    }
258
259    pub fn generate_autoconf_rust_file(&self, dir: impl AsRef<Path>) -> io::Result<()> {
260        generate_autoconf_rust_file(dir, &self.cfg)
261    }
262}
263
264/// Generate the autoconfig.h file which is required to build the C library. This file contains
265/// important configuration defines.
266pub fn generate_autoconf_header_file(out_dir: impl AsRef<Path>, cfg: &Config) -> io::Result<()> {
267    let out_dir = out_dir.as_ref();
268    let mut autoconf_file_string = String::new();
269    let version = env!("CARGO_PKG_VERSION");
270    let name = env!("CARGO_PKG_NAME");
271    autoconf_file_string.push_str(&format!(
272        "// This file was auto-generated by {} v{}\n",
273        name, version
274    ));
275    #[cfg(unix)]
276    autoconf_file_string.push_str("#define CSP_POSIX 1\n");
277    #[cfg(not(unix))]
278    autoconf_file_string.push_str("#define CSP_POSIX 0\n");
279    autoconf_file_string.push_str("#define CSP_ZEPHYR 0\n");
280    autoconf_file_string.push('\n');
281    autoconf_file_string.push_str(&format!(
282        "#define {} {}\n",
283        cfg_keys::HAVE_STDIO,
284        cfg.have_stdio as u32
285    ));
286    autoconf_file_string.push_str(&format!(
287        "#define {} {}\n",
288        cfg_keys::ENABLE_CSP_PRINT,
289        cfg.csp_print as u32
290    ));
291    autoconf_file_string.push_str(&format!(
292        "#define {} {}\n",
293        cfg_keys::PRINT_STDIO,
294        cfg.print_stdio as u32
295    ));
296    autoconf_file_string.push_str(&format!(
297        "#define {} {}\n",
298        cfg_keys::REPRODUCIBLE_BUILDS,
299        cfg.reproducible_builds as u32
300    ));
301    autoconf_file_string.push('\n');
302
303    autoconf_file_string.push_str(&format!(
304        "#define {} {}\n",
305        cfg_keys::QFIFO_LEN,
306        cfg.qfifo_len
307    ));
308    autoconf_file_string.push_str(&format!(
309        "#define {} {}\n",
310        cfg_keys::PORT_MAX_BIND,
311        cfg.port_max_bind
312    ));
313    autoconf_file_string.push_str(&format!(
314        "#define {} {}\n",
315        cfg_keys::CONN_RXQUEUE_LEN,
316        cfg.conn_rx_queue_len
317    ));
318    autoconf_file_string.push_str(&format!(
319        "#define {} {}\n",
320        cfg_keys::CONN_MAX,
321        cfg.conn_max
322    ));
323    autoconf_file_string.push_str(&format!(
324        "#define {} {}\n",
325        cfg_keys::BUFFER_SIZE,
326        cfg.buffer_size
327    ));
328    autoconf_file_string.push_str(&format!(
329        "#define {} {}\n",
330        cfg_keys::BUFFER_COUNT,
331        cfg.buffer_count
332    ));
333    autoconf_file_string.push_str(&format!(
334        "#define {} {}\n",
335        cfg_keys::RDP_MAX_WINDOW,
336        cfg.rdp_max_window
337    ));
338    autoconf_file_string.push_str(&format!(
339        "#define {} {}\n",
340        cfg_keys::RTABLE_SIZE,
341        cfg.rtable_size
342    ));
343
344    autoconf_file_string.push('\n');
345    autoconf_file_string.push_str(&format!(
346        "#define {} {}\n",
347        cfg_keys::USE_RDP,
348        cfg.rdp as u32
349    ));
350    autoconf_file_string.push_str(&format!(
351        "#define {} {}\n",
352        cfg_keys::USE_HMAC,
353        cfg.hmac as u32
354    ));
355    autoconf_file_string.push_str(&format!(
356        "#define {} {}\n",
357        cfg_keys::USE_PROMISC,
358        cfg.promisc as u32
359    ));
360    autoconf_file_string.push_str(&format!(
361        "#define {} {}\n",
362        cfg_keys::USE_RTABLE,
363        cfg.rtable as u32
364    ));
365
366    autoconf_file_string.push('\n');
367    // TODO: Maybe those will be added at some point.. For now, they are hardcoded to 0.
368    autoconf_file_string.push_str(&format!("#define {} {}\n", cfg_keys::HAVE_LIBSOCKETCAN, 0));
369    autoconf_file_string.push_str(&format!("#define {} {}\n", cfg_keys::HAVE_LIBZMQ, 0));
370    let out_file = out_dir.join("autoconfig.h");
371    let mut file = std::fs::File::create(out_file)?;
372    file.write_all(autoconf_file_string.as_bytes())?;
373    Ok(())
374}
375
376/// Generate the autoconfig.rs file which is required by the
377/// [`libcsp-sys`](https://crates.io/crates/libcsp-sys) Rust bindings crate.
378pub fn generate_autoconf_rust_file(out_dir: impl AsRef<Path>, cfg: &Config) -> io::Result<()> {
379    let out_dir = out_dir.as_ref();
380    let mut autoconf_file_string = String::new();
381    let version = env!("CARGO_PKG_VERSION");
382    let name = env!("CARGO_PKG_NAME");
383    autoconf_file_string.push_str(&format!(
384        "// This file was auto-generated by {} v{}\n",
385        name, version
386    ));
387    autoconf_file_string.push_str(&format!(
388        "pub const {}: usize = {};\n",
389        cfg_keys::CONN_RXQUEUE_LEN,
390        cfg.conn_rx_queue_len
391    ));
392    autoconf_file_string.push_str(&format!(
393        "pub const {}: usize = {};\n",
394        cfg_keys::QFIFO_LEN,
395        cfg.qfifo_len
396    ));
397    autoconf_file_string.push_str(&format!(
398        "pub const {}: usize = {};\n",
399        cfg_keys::PORT_MAX_BIND,
400        cfg.port_max_bind
401    ));
402    autoconf_file_string.push_str(&format!(
403        "pub const {}: usize = {};\n",
404        cfg_keys::CONN_MAX,
405        cfg.conn_max
406    ));
407    autoconf_file_string.push_str(&format!(
408        "pub const {}: usize = {};\n",
409        cfg_keys::BUFFER_SIZE,
410        cfg.buffer_size
411    ));
412    autoconf_file_string.push_str(&format!(
413        "pub const {}: usize = {};\n",
414        cfg_keys::RDP_MAX_WINDOW,
415        cfg.rdp_max_window
416    ));
417    autoconf_file_string.push_str(&format!(
418        "pub const {}: usize = {};\n",
419        cfg_keys::RTABLE_SIZE,
420        cfg.rtable_size
421    ));
422    let out_file = out_dir.join("autoconfig.rs");
423    let mut file = std::fs::File::create(out_file)?;
424    file.write_all(autoconf_file_string.as_bytes())?;
425    Ok(())
426}
427
428#[cfg(test)]
429mod tests {
430    // TODO: Unittest autoconf generators.
431}