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, compiler: Compiler) -> 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        // Build with support for C11 on platforms that default to C99 or C89.
46        match compiler {
47            Compiler::C => {
48                b.std("gnu11");
49            }
50            Compiler::CPP => {
51                // Switch to C++ library compilation.
52                b.cpp(true);
53                b.std("c++11");
54            }
55        }
56
57        // When adding flags here, consider using `add_compile_options`
58        // in the root CMakeLists.txt instead, where they will be picked
59        // up both here and in our remaining pure C targets.
60        b.define("_GNU_SOURCE", None)
61            .include(&*self.build_src_root)
62            .include(&*self.src_root)
63            // Disable extra warnings (-Wall, -Wextra) until if and when they're
64            // fixed in our C code.
65            .warnings(false)
66            // By default, *don't* convert any remaining warnings into errors (-Werror).
67            // -Werror is currently enabled here via CFLAGS, which
68            // cmake sets depending on the option SHADOW_WERROR.
69            .warnings_into_errors(false);
70
71        if let Some(deps) = &self.deps {
72            b.includes(deps.all_include_paths());
73        }
74
75        if let Some("true") = std::env::var("DEBUG").ok().as_deref() {
76            b.flag("-DDEBUG")
77                // we only check for unused functions when builing in debug mode since some
78                // functions are only called when logging, which can be #ifdef'd out in
79                // release mode
80                .flag("-Wunused-function");
81        } else {
82            b.flag("-DNDEBUG");
83        }
84
85        b
86    }
87
88    #[cfg(feature = "bindgen")]
89    pub fn bindgen_builder(&self) -> bindgen::Builder {
90        let mut builder = bindgen::Builder::default()
91            // Tell cargo to invalidate the built crate whenever any of the
92            // included header files changed.
93            .parse_callbacks(Box::new(bindgen::CargoCallbacks::new()))
94            .clang_args(&[
95                &format!("-I{}", self.build_src_root.to_str().unwrap()),
96                &format!("-I{}", self.src_root.to_str().unwrap()),
97                "-D_GNU_SOURCE",
98            ])
99            //# used to generate #[must_use] annotations)
100            .enable_function_attribute_detection();
101
102        if let Some(deps) = &self.deps {
103            for path in deps.all_include_paths() {
104                builder = builder.clang_args(&[format!("-I{}", path.to_str().unwrap())]);
105            }
106        }
107        builder
108    }
109
110    #[cfg(feature = "cbindgen")]
111    pub fn cbindgen_base_config(&self) -> cbindgen::Config {
112        let header = "
113/*
114 * The Shadow Simulator
115 * See LICENSE for licensing information
116 */
117// clang-format off";
118
119        cbindgen::Config {
120            language: cbindgen::Language::C,
121            line_length: 100,
122            documentation_style: cbindgen::DocumentationStyle::C99,
123            macro_expansion: cbindgen::MacroExpansionConfig {
124                bitflags: true,
125            },
126            header: Some(header.into()),
127            autogen_warning: Some(
128                "/* Warning, this file is autogenerated by cbindgen. Don't modify this manually. */"
129                    .into(),
130            ),
131            enumeration: cbindgen::EnumConfig {
132                prefix_with_name: true,
133                rename_variants: cbindgen::RenameRule::ScreamingSnakeCase,
134                ..cbindgen::EnumConfig::default()
135            },
136            function: cbindgen::FunctionConfig {
137                must_use: Some("__attribute__((warn_unused_result))".into()),
138                no_return: Some("__attribute__((noreturn))".into()),
139                ..cbindgen::FunctionConfig::default()
140            },
141            export: cbindgen::ExportConfig {
142                rename: std::collections::HashMap::from([
143                    ("timeval".into(), "struct timeval".into()),
144                    ("timespec".into(), "struct timespec".into()),
145                ]),
146                // All types.
147                item_types: vec![
148                    cbindgen::ItemType::Enums,
149                    cbindgen::ItemType::Constants,
150                    cbindgen::ItemType::Globals,
151                    cbindgen::ItemType::Structs,
152                    cbindgen::ItemType::Unions,
153                    cbindgen::ItemType::Typedefs,
154                    cbindgen::ItemType::OpaqueItems,
155                    cbindgen::ItemType::Functions,
156                ],
157                ..cbindgen::ExportConfig::default()
158            },
159            ..Default::default()
160        }
161    }
162}
163
164#[cfg(feature = "cbindgen")]
165pub trait CBindgenExt {
166    fn get_mut(&mut self) -> &mut cbindgen::Config;
167
168    // Export the given types opaquely.
169    //
170    // This overrides cbindgen's behavior of making any `repr(C)` type
171    // non-opaque.
172    // https://github.com/eqrion/cbindgen/issues/104
173    fn add_opaque_types(&mut self, types: &[&str]) {
174        let c = self.get_mut();
175        if types.is_empty() {
176            return;
177        }
178        if c.after_includes.is_none() {
179            c.after_includes.replace("".into());
180        }
181        for t in types {
182            c.after_includes
183                .as_mut()
184                .unwrap()
185                .push_str(&format!("typedef struct {t} {t};\n"));
186            c.export.exclude.push((*t).into());
187        }
188    }
189}
190
191#[cfg(feature = "cbindgen")]
192impl CBindgenExt for cbindgen::Config {
193    fn get_mut(&mut self) -> &mut cbindgen::Config {
194        self
195    }
196}
197
198pub enum Compiler {
199    C,
200    CPP,
201}