shadow_build_common/
lib.rs

1// https://github.com/rust-lang/rfcs/blob/master/text/2585-unsafe-block-in-unsafe-fn.md
2#![deny(unsafe_op_in_unsafe_fn)]
3
4use std::path::Path;
5
6pub struct ShadowBuildCommon {
7    deps: Option<system_deps::Dependencies>,
8    build_src_root: Box<Path>,
9    src_root: Box<Path>,
10}
11
12impl ShadowBuildCommon {
13    pub fn new(repo_root: &Path, system_deps: Option<system_deps::Dependencies>) -> Self {
14        let src_root = {
15            let mut p = repo_root.to_path_buf();
16            p.push("src");
17            p.into_boxed_path()
18        };
19
20        let build_src_root = {
21            let mut p = repo_root.to_path_buf();
22            p.push("build");
23            p.push("src");
24            p.into_boxed_path()
25        };
26
27        // Conservatively re-run build scripts if anything in their package directory
28        // changes.
29        println!("cargo:rerun-if-changed=.");
30
31        Self {
32            deps: system_deps,
33            build_src_root,
34            src_root,
35        }
36    }
37
38    pub fn cc_build(&self) -> cc::Build {
39        let mut b = cc::Build::new();
40        println!("cargo:rerun-if-env-changed=CC");
41        println!("cargo:rerun-if-env-changed=CXX");
42        println!("cargo:rerun-if-env-changed=CFLAGS");
43        println!("cargo:rerun-if-env-changed=CXXFLAGS");
44
45        // When adding flags here, consider using `add_compile_options`
46        // in the root CMakeLists.txt instead, where they will be picked
47        // up both here and in our remaining pure C targets.
48        b.define("_GNU_SOURCE", None)
49            .include(&*self.build_src_root)
50            .include(&*self.src_root)
51            // Disable extra warnings (-Wall, -Wextra) until if and when they're
52            // fixed in our C code.
53            .warnings(false)
54            // By default, *don't* convert any remaining warnings into errors (-Werror).
55            // -Werror is currently enabled here via CFLAGS, which
56            // cmake sets depending on the option SHADOW_WERROR.
57            .warnings_into_errors(false);
58
59        if let Some(deps) = &self.deps {
60            b.includes(deps.all_include_paths());
61        }
62
63        if let Some("true") = std::env::var("DEBUG").ok().as_deref() {
64            b.flag("-DDEBUG")
65                // we only check for unused functions when builing in debug mode since some
66                // functions are only called when logging, which can be #ifdef'd out in
67                // release mode
68                .flag("-Wunused-function");
69        } else {
70            b.flag("-DNDEBUG");
71        }
72
73        b
74    }
75
76    #[cfg(feature = "bindgen")]
77    pub fn bindgen_builder(&self) -> bindgen::Builder {
78        let mut builder = bindgen::Builder::default()
79            // Tell cargo to invalidate the built crate whenever any of the
80            // included header files changed.
81            .parse_callbacks(Box::new(bindgen::CargoCallbacks::new()))
82            .clang_args(&[
83                &format!("-I{}", self.build_src_root.to_str().unwrap()),
84                &format!("-I{}", self.src_root.to_str().unwrap()),
85                "-D_GNU_SOURCE",
86            ])
87            //# used to generate #[must_use] annotations)
88            .enable_function_attribute_detection();
89
90        if let Some(deps) = &self.deps {
91            for path in deps.all_include_paths() {
92                builder = builder.clang_args(&[format!("-I{}", path.to_str().unwrap())]);
93            }
94        }
95        builder
96    }
97
98    #[cfg(feature = "cbindgen")]
99    pub fn cbindgen_base_config(&self) -> cbindgen::Config {
100        let header = "
101/*
102 * The Shadow Simulator
103 * See LICENSE for licensing information
104 */
105// clang-format off";
106
107        cbindgen::Config {
108            language: cbindgen::Language::C,
109            line_length: 100,
110            documentation_style: cbindgen::DocumentationStyle::C99,
111            macro_expansion: cbindgen::MacroExpansionConfig {
112                bitflags: true,
113            },
114            header: Some(header.into()),
115            autogen_warning: Some(
116                "/* Warning, this file is autogenerated by cbindgen. Don't modify this manually. */"
117                    .into(),
118            ),
119            enumeration: cbindgen::EnumConfig {
120                prefix_with_name: true,
121                rename_variants: cbindgen::RenameRule::ScreamingSnakeCase,
122                ..cbindgen::EnumConfig::default()
123            },
124            function: cbindgen::FunctionConfig {
125                must_use: Some("__attribute__((warn_unused_result))".into()),
126                no_return: Some("__attribute__((noreturn))".into()),
127                ..cbindgen::FunctionConfig::default()
128            },
129            export: cbindgen::ExportConfig {
130                rename: std::collections::HashMap::from([
131                    ("timeval".into(), "struct timeval".into()),
132                    ("timespec".into(), "struct timespec".into()),
133                ]),
134                // All types.
135                item_types: vec![
136                    cbindgen::ItemType::Enums,
137                    cbindgen::ItemType::Constants,
138                    cbindgen::ItemType::Globals,
139                    cbindgen::ItemType::Structs,
140                    cbindgen::ItemType::Unions,
141                    cbindgen::ItemType::Typedefs,
142                    cbindgen::ItemType::OpaqueItems,
143                    cbindgen::ItemType::Functions,
144                ],
145                ..cbindgen::ExportConfig::default()
146            },
147            ..Default::default()
148        }
149    }
150}
151
152#[cfg(feature = "cbindgen")]
153pub trait CBindgenExt {
154    fn get_mut(&mut self) -> &mut cbindgen::Config;
155
156    // Export the given types opaquely.
157    //
158    // This overrides cbindgen's behavior of making any `repr(C)` type
159    // non-opaque.
160    // https://github.com/eqrion/cbindgen/issues/104
161    fn add_opaque_types(&mut self, types: &[&str]) {
162        let c = self.get_mut();
163        if types.is_empty() {
164            return;
165        }
166        if c.after_includes.is_none() {
167            c.after_includes.replace("".into());
168        }
169        for t in types {
170            c.after_includes
171                .as_mut()
172                .unwrap()
173                .push_str(&format!("typedef struct {t} {t};\n"));
174            c.export.exclude.push((*t).into());
175        }
176    }
177}
178
179#[cfg(feature = "cbindgen")]
180impl CBindgenExt for cbindgen::Config {
181    fn get_mut(&mut self) -> &mut cbindgen::Config {
182        self
183    }
184}