// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 use std::{fs::read_dir, io::Error, path::Path, process::Command}; fn main() -> Result<(), Error> { // allow overriding the detected features with an env variable if let Some(features) = option_env("S2N_QUIC_PLATFORM_FEATURES_OVERRIDE") { for feature in features.split(',') { supports(feature.trim()); } return Ok(()); } let env = Env::new(); for feature in read_dir("features")? { let path = feature?.path(); if let Some(name) = path.file_stem() { println!("cargo:rerun-if-changed={}", path.display()); if env.check(&path)? { supports(name.to_str().expect("valid feature name")); } } } let is_miri = std::env::var("CARGO_CFG_MIRI").is_ok(); match env.target_os.as_str() { "linux" => { supports("gso"); supports("gro"); supports("mtu_disc"); supports("pktinfo"); supports("tos"); // miri doesn't support the way we detect syscall support so override it if is_miri { supports("socket_msg"); supports("socket_mmsg"); } } "macos" => { supports("pktinfo"); supports("tos"); // miri doesn't support the way we detect syscall support so override it if is_miri { supports("socket_msg"); } } "android" => { supports("mtu_disc"); supports("pktinfo"); supports("tos"); } _ => { // TODO others } } Ok(()) } fn supports(name: &str) { println!("cargo:rustc-cfg=s2n_quic_platform_{name}"); } struct Env { rustc: String, out_dir: String, target: String, target_os: String, } impl Env { fn new() -> Self { // See https://doc.rust-lang.org/cargo/reference/environment-variables.html#environment-variables-cargo-sets-for-build-scripts Self { rustc: env("RUSTC"), out_dir: env("OUT_DIR"), target: env("TARGET"), target_os: env("CARGO_CFG_TARGET_OS"), } } // Tries to compile the program and returns if it was successful fn check(&self, path: &Path) -> Result<bool, Error> { let mut command = Command::new(&self.rustc); command .arg("--out-dir") .arg(&self.out_dir) .arg("--target") .arg(&self.target) .arg("--crate-type") .arg("bin") .arg("--codegen") .arg("opt-level=0") .arg(path); for (key, _) in std::env::vars() { const CARGO_FEATURE: &str = "CARGO_FEATURE_"; if key.starts_with(CARGO_FEATURE) { command.arg("--cfg").arg(format!( "feature=\"{}\"", key.trim_start_matches(CARGO_FEATURE) .to_lowercase() .replace('_', "-") )); } } Ok(command.spawn()?.wait()?.success()) } } fn env(name: &str) -> String { option_env(name).unwrap_or_else(|| panic!("build script missing {name:?} environment variable")) } fn option_env(name: &str) -> Option<String> { println!("cargo:rerun-if-env-changed={name}"); std::env::var(name).ok() }