blob: 141a74e74cea57d2ed8babdd2bb3c09e38f18be8 [file] [log] [blame]
// 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 }) {
// 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.
for version_cfg in &version_cfgs {
println!("cargo:rustc-check-cfg=cfg({})", version_cfg.cfg_name);
}
// TODO(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(coverage_nightly)");
}
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 }
}