1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256
|
// 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(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 }
}
|