cargo / zerocopy / audit
cargo : zerocopy @ 0.8.50
PE Patrick Elsen signed 2026-06-02 published 2026-06-02

build.rs

260 lines · rust

// Copyright 2024 The Fuchsia Authors//// Licensed under the 2-Clause BSD License <LICENSE-BSD or// https://opensource.org/license/bsd-2-clause>, Apache License, Version 2.0// <LICENSE-APACHE or https://www.apache.org/licenses/LICENSE-2.0>, or the MIT// license <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your option.// This file may not be copied, modified, or distributed except according to// those terms.// Sometimes we want to use lints which were added after our MSRV.// `unknown_lints` is `warn` by default and we deny warnings in CI, so without// this attribute, any unknown lint would cause a CI failure when testing with// our MSRV.#![allow(unknown_lints)]#![deny(renamed_and_removed_lints)]#![deny(    anonymous_parameters,    deprecated_in_future,    late_bound_lifetime_arguments,    missing_copy_implementations,    missing_debug_implementations,    path_statements,    patterns_in_fns_without_body,    rust_2018_idioms,    trivial_numeric_casts,    unreachable_pub,    unsafe_op_in_unsafe_fn,    unused_extern_crates,    variant_size_differences)]#![deny(    clippy::all,    clippy::alloc_instead_of_core,    clippy::arithmetic_side_effects,    clippy::as_underscore,    clippy::assertions_on_result_states,    clippy::as_conversions,    clippy::correctness,    clippy::dbg_macro,    clippy::decimal_literal_representation,    clippy::get_unwrap,    clippy::indexing_slicing,    clippy::missing_inline_in_public_items,    clippy::missing_safety_doc,    clippy::obfuscated_if_else,    clippy::perf,    clippy::print_stdout,    clippy::style,    clippy::suspicious,    clippy::todo,    clippy::undocumented_unsafe_blocks,    clippy::unimplemented,    clippy::unnested_or_patterns,    clippy::unwrap_used,    clippy::use_debug)]use std::{env, fs, process::Command, str};fn main() {    // Avoid unnecessary re-building.    println!("cargo:rerun-if-changed=build.rs");    // This is necessary because changes to the list of detected Rust toolchain    // versions will affect what `--cfg`s this script emits. Without this,    // changes to that list have no effect on the build without running `cargo    // clean` or similar.    println!("cargo:rerun-if-changed=Cargo.toml");    let version_cfgs = parse_version_cfgs_from_cargo_toml();    let rustc_version = rustc_version();    if rustc_version >= (Version { major: 1, minor: 77, patch: 0 }) {        for version_cfg in &version_cfgs {            // This tells the `unexpected_cfgs` lint to expect to see all of            // these `cfg`s. The `cargo::` syntax was only added in 1.77, so we            // don't emit these on earlier toolchain versions.            println!("cargo:rustc-check-cfg=cfg({})", version_cfg.cfg_name);            // This tells the `unexpected_cfgs` lint to expect to see `cfg`s of            // the form `rust = "1.2.3"`. These aren't real `cfg`s, but we use            // them in `cfg_attr(doc_cfg, doc(cfg(rust = "1.2.3")))` on items            // that are version-gated so that the rendered Rustdoc shows which            // Rust toolchain versions those items are available on.            let Version { major, minor, patch } = version_cfg.version;            println!("cargo:rustc-check-cfg=cfg(rust, values(\"{}.{}.{}\"))", major, minor, patch);        }        // FIXME(https://github.com/rust-lang/rust/issues/124816): Remove these        // once they're no longer needed.        println!("cargo:rustc-check-cfg=cfg(doc_cfg)");        println!("cargo:rustc-check-cfg=cfg(kani)");        println!(            "cargo:rustc-check-cfg=cfg(__ZEROCOPY_INTERNAL_USE_ONLY_NIGHTLY_FEATURES_IN_TESTS)"        );        println!("cargo:rustc-check-cfg=cfg(__ZEROCOPY_INTERNAL_USE_ONLY_DEV_MODE)");        println!("cargo:rustc-check-cfg=cfg(coverage_nightly)");        println!("cargo:rustc-check-cfg=cfg(zerocopy_inline_always)");        println!("cargo:rustc-check-cfg=cfg(zerocopy_unstable_ptr)");    }    for version_cfg in version_cfgs {        if rustc_version < version_cfg.version {            println!("cargo:rustc-cfg={}", version_cfg.cfg_name);        }    }}#[derive(Debug, Ord, PartialEq, PartialOrd, Eq)]struct Version {    major: usize,    minor: usize,    patch: usize,}#[derive(Debug)]struct VersionCfg {    version: Version,    cfg_name: String,}const ITER_FIRST_NEXT_EXPECT_MSG: &str = "unreachable: a string split cannot produce 0 items";fn parse_version_cfgs_from_cargo_toml() -> Vec<VersionCfg> {    let cargo_toml = fs::read_to_string("Cargo.toml").expect("failed to read Cargo.toml");    // Expect a Cargo.toml with the following format:    //    //   ...    //    //   [package.metadata.build-rs]    //   # Comments...    //   zerocopy-panic-in-const-fn = "1.57.0"    //    //   ...    //    //   [...]    //    // In other words, the following sections, in order:    // - Arbitrary content    // - The literal header `[package.metadata.build-rs]`    // - Any number of:    //   - Comments    //   - Key/value pairs    // - A TOML table, indicating the end of the section we care about    const TABLE_HEADER: &str = "[package.metadata.build-rs]";    if !cargo_toml.contains(TABLE_HEADER) {        panic!("{}", format!("Cargo.toml does not contain `{}`", TABLE_HEADER));    }    // Now that we know there's at least one instance of `TABLE_HEADER`, we    // consume the iterator until we find the text following that first    // instance. This isn't terribly bullet-proof, but we also authored    // `Cargo.toml`, and we'd have to mess up pretty badly to accidentally put    // two copies of the same table header in that file.    let mut iter = cargo_toml.split(TABLE_HEADER);    let _prefix = iter.next().expect(ITER_FIRST_NEXT_EXPECT_MSG);    let rest = iter.next().expect("unreachable: we already confirmed that there's a table header");    // Scan until we find the next table section, which should start with a `[`    // character at the beginning of a line.    let mut iter = rest.split("\n[");    let section = iter.next().expect("unreachable: a string split cannot produce 0 items");    section        .lines()        .filter_map(|line| {            // Parse lines of one of the following forms:            //            //   # Comment            //            //   name-of-key = "1.2.3" # Comment            //            // Comments on their own line are ignored, and comments after a            // key/value pair will be stripped before further processing.            // We don't need to handle the case where the `#` character isn't a            // comment (which can happen if it's inside a string) since we authored            // `Cargo.toml` and, in this section, we only put Rust version numbers            // in strings.            let before_comment = line.split('#').next().expect(ITER_FIRST_NEXT_EXPECT_MSG);            let before_comment_without_whitespace = before_comment.trim_start();            if before_comment_without_whitespace.is_empty() {                return None;            }            // At this point, assuming Cargo.toml is correctly formatted according            // to the format expected by this function, we know that            // `before_comment_without_whitespace` is of the form:            //            //   name-of-key = "1.2.3" # Comment            //            // ...with no leading whitespace, and where the trailing comment is            // optional.            let mut iter = before_comment_without_whitespace.split_whitespace();            let name = iter.next().expect(ITER_FIRST_NEXT_EXPECT_MSG);            const EXPECT_MSG: &str =                "expected lines of the format `name-of-key = \"1.2.3\" # Comment`";            let equals_sign = iter.next().expect(EXPECT_MSG);            let value = iter.next().expect(EXPECT_MSG);            assert_eq!(equals_sign, "=", "{}", EXPECT_MSG);            // Replace dashes with underscores.            let name = name.replace('-', "_");            // Strip the quotation marks.            let value = value.trim_start_matches('"').trim_end_matches('"');            let mut iter = value.split('.');            let major = iter.next().expect(ITER_FIRST_NEXT_EXPECT_MSG);            let minor = iter.next().expect(EXPECT_MSG);            let patch = iter.next().expect(EXPECT_MSG);            assert_eq!(iter.next(), None, "{}", EXPECT_MSG);            let major: usize = major.parse().expect(EXPECT_MSG);            let minor: usize = minor.parse().expect(EXPECT_MSG);            let patch: usize = patch.parse().expect(EXPECT_MSG);            Some(VersionCfg { version: Version { major, minor, patch }, cfg_name: name })        })        .collect()}fn rustc_version() -> Version {    let rustc_cmd_name = env::var_os("RUSTC").expect("could not get rustc command name");    let version =        Command::new(rustc_cmd_name).arg("--version").output().expect("could not invoke rustc");    if !version.status.success() {        panic!(            "rustc failed with status: {}\nrustc output: {}",            version.status,            String::from_utf8_lossy(version.stderr.as_slice())        );    }    const RUSTC_EXPECT_MSG: &str = "could not parse rustc version output";    let version = str::from_utf8(version.stdout.as_slice()).expect(RUSTC_EXPECT_MSG);    let version = version.trim_start_matches("rustc ");    // The version string is sometimes followed by other information such as the    // string `-nightly` or other build information. We don't care about any of    // that.    let version = version        .split(|c: char| c != '.' && !c.is_ascii_digit())        .next()        .expect(ITER_FIRST_NEXT_EXPECT_MSG);    let mut iter = version.split('.');    let major = iter.next().expect(ITER_FIRST_NEXT_EXPECT_MSG);    let minor = iter.next().expect(RUSTC_EXPECT_MSG);    let patch = iter.next().expect(RUSTC_EXPECT_MSG);    let major: usize = major.parse().expect(RUSTC_EXPECT_MSG);    let minor: usize = minor.parse().expect(RUSTC_EXPECT_MSG);    let patch: usize = patch.parse().expect(RUSTC_EXPECT_MSG);    Version { major, minor, patch }}