diff --git a/Cargo.toml b/Cargo.toml index b4863e7439..55429ef87c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,5 +1,5 @@ [package] -build = "build/cargo.rs" +build = "build/main.rs" categories = ["cryptography", "no-std"] description = "An experiment." edition = "2024" @@ -41,9 +41,7 @@ include = [ "benches/*.rs", - "build/build.rs", - "build/build/path.rs", - "build/cargo.rs", + "build/**/*.rs", "crypto/chacha/asm/chacha-armv4.pl", "crypto/chacha/asm/chacha-armv8.pl", diff --git a/build/build/c.rs b/build/build/c.rs new file mode 100644 index 0000000000..a02e3f4d89 --- /dev/null +++ b/build/build/c.rs @@ -0,0 +1,178 @@ +// Copyright 2015-2026 Brian Smith. +// +// Permission to use, copy, modify, and/or distribute this software for any +// purpose with or without fee is hereby granted, provided that the above +// copyright notice and this permission notice appear in all copies. +// +// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY +// SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION +// OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN +// CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +//! A wrapper around cc-rs. + +// Avoid `std::env` here. All configuration should be done through `Target`, +// `Profile`, and `Tools`. +use super::target::*; +use std::path::{Path, PathBuf}; + +fn cpp_flags(compiler: &cc::Tool) -> &'static [&'static str] { + if !compiler.is_like_msvc() { + static NON_MSVC_FLAGS: &[&str] = &[ + "-fvisibility=hidden", + "-std=c1x", // GCC 4.6 requires "c1x" instead of "c11" + "-Wall", + "-Wbad-function-cast", + "-Wcast-align", + "-Wcast-qual", + "-Wconversion", + "-Wmissing-field-initializers", + "-Wmissing-include-dirs", + "-Wnested-externs", + "-Wredundant-decls", + "-Wshadow", + "-Wsign-compare", + "-Wsign-conversion", + "-Wstrict-prototypes", + "-Wundef", + "-Wuninitialized", + ]; + NON_MSVC_FLAGS + } else { + static MSVC_FLAGS: &[&str] = &[ + "/Gy", // Enable function-level linking. + "/Zc:wchar_t", + "/Zc:forScope", + "/Zc:inline", + // Warnings. + "/W4", + "/wd4127", // C4127: conditional expression is constant + "/wd4464", // C4464: relative include path contains '..' + "/wd5045", /* C5045: Compiler will insert Spectre mitigation for memory load if + * /Qspectre switch specified */ + ]; + MSVC_FLAGS + } +} + +pub struct Profile { + /// Is this a debug build? This affects whether assertions might be enabled + /// in the C code. For packaged builds, this should always be `false`. + pub is_debug: bool, + + /// true: Force warnings to be treated as errors. + /// false: Use the default behavior (perhaps determined by `$CFLAGS`, etc.) + pub force_warnings_into_errors: bool, + + pub is_git: bool, +} + +fn new_build( + target: &Target, + profile: &Profile, + c_root_dir: &Path, + include_dir: &Path, +) -> cc::Build { + let mut b = cc::Build::new(); + configure_cc(&mut b, target, profile, c_root_dir, include_dir); + b +} + +pub fn build_library<'a>( + target: &Target, + profile: &Profile, + c_root_dir: &Path, + lib_name: &str, + srcs: impl Iterator, + include_dir: &Path, + preassembled_objs: &[PathBuf], +) { + let mut c = new_build(target, profile, c_root_dir, include_dir); + + // Compile all the (dirty) source files into object files. + srcs.for_each(|src| { + c.file(c_root_dir.join(src)); + }); + + preassembled_objs.iter().for_each(|obj| { + c.object(obj); + }); + + // Rebuild the library if necessary. + let lib_path = target.out_dir.join(format!("lib{lib_name}.a")); + + // Handled below. + let _ = c.cargo_metadata(false); + + c.compile( + lib_path + .file_name() + .and_then(|f| f.to_str()) + .expect("No filename"), + ); + + // Link the library. This works even when the library doesn't need to be + // rebuilt. + println!("cargo:rustc-link-lib=static={lib_name}"); + println!( + "cargo:rustc-link-search=native={}", + target.out_dir.to_str().expect("Invalid path") + ); +} + +fn configure_cc( + c: &mut cc::Build, + target: &Target, + profile: &Profile, + c_root_dir: &Path, + include_dir: &Path, +) { + // FIXME: On Windows AArch64 we currently must use Clang to compile C code. + // clang-cl emulates the cl.exe command line, `$CFLAGS`, etc. + if target.os == WINDOWS && target.arch == AARCH64 { + let _: &_ = c.prefer_clang_cl_over_msvc(true); + }; + let compiler = c.get_compiler(); + + let _ = c.include(c_root_dir.join("include")); + let _ = c.include(include_dir); + for f in cpp_flags(&compiler) { + let _ = c.flag(f); + } + + if APPLE_ABI.contains(&target.os.as_str()) { + // ``-gfull`` is required for Darwin's |-dead_strip|. + let _ = c.flag("-gfull"); + } else if !compiler.is_like_msvc() { + let _ = c.flag("-g3"); + }; + + if !profile.is_debug { + let _ = c.define("NDEBUG", None); + } + + if target.arch == X86 { + let is_msvc_not_clang_cl = compiler.is_like_msvc() && !compiler.is_like_clang_cl(); + if !is_msvc_not_clang_cl { + let _ = c.flag("-msse2"); + } + } + + // Allow cross-compiling without a target sysroot for these targets. + if (target.arch == WASM32) + || (target.os == "linux" && target.env == "musl" && target.arch != X86_64) + { + // TODO: Expand this to non-clang compilers in 0.17.0 if practical. + if compiler.is_like_clang() { + let _ = c.flag("-nostdlibinc"); + let _ = c.define("RING_CORE_NOSTDLIBINC", "1"); + } + } + + if profile.force_warnings_into_errors { + c.warnings_into_errors(true); + } +} diff --git a/build/build/cargo.rs b/build/build/cargo.rs new file mode 100644 index 0000000000..ca850378f3 --- /dev/null +++ b/build/build/cargo.rs @@ -0,0 +1,162 @@ +// Copyright 2015-2016 Brian Smith. +// +// Permission to use, copy, modify, and/or distribute this software for any +// purpose with or without fee is hereby granted, provided that the above +// copyright notice and this permission notice appear in all copies. +// +// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY +// SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION +// OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN +// CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +//! Cargo-specific build logic that isn't specific to this project. + +use super::{Profile, Target, target::Endian}; +// Avoid `std::env` here; use `self::env` instead. +use std::{ + fs::{self}, + path::{Path, PathBuf}, +}; + +pub mod env { + use std::ffi::OsString; + + macro_rules! define_env { + { $vis:vis $NAME:ident : $ty:ident } => { + $vis const $NAME: EnvVar = EnvVar { + name: stringify!($NAME), + ty: EnvVarTy::$ty, + }; + }; + } + + enum EnvVarTy { + RerunIfChanged, + SetByCargo, + } + + pub struct EnvVar { + pub name: &'static str, + ty: EnvVarTy, + } + + /// Read an environment variable and optionally tell Cargo that we depend on it. + /// + /// The env var is static since we intend to only read a static set of environment + /// variables. + pub fn var_os(env_var: &'static EnvVar) -> Option { + match env_var.ty { + EnvVarTy::RerunIfChanged => { + println!("cargo:rerun-if-env-changed={}", env_var.name); + } + EnvVarTy::SetByCargo => {} + } + std::env::var_os(env_var.name) + } + + pub fn var(env_var: &'static EnvVar) -> Option { + var_os(env_var).and_then(|value| value.into_string().ok()) + } + + // In alphabetical order + define_env! { pub(super) CARGO_CFG_TARGET_ARCH: SetByCargo } + define_env! { pub(super) CARGO_CFG_TARGET_ENDIAN: SetByCargo } + define_env! { pub(super) CARGO_CFG_TARGET_ENV: SetByCargo } + define_env! { pub(super) CARGO_CFG_TARGET_OS: SetByCargo } + define_env! { pub(super) CARGO_MANIFEST_DIR: SetByCargo } + define_env! { pub(super) CARGO_MANIFEST_LINKS: SetByCargo } + define_env! { pub(super) CARGO_PKG_NAME: SetByCargo } + define_env! { pub(super) CARGO_PKG_VERSION_MAJOR: SetByCargo } + define_env! { pub(super) CARGO_PKG_VERSION_MINOR: SetByCargo } + define_env! { pub(super) CARGO_PKG_VERSION_PATCH: SetByCargo } + define_env! { pub(super) CARGO_PKG_VERSION_PRE: SetByCargo } + define_env! { pub(super) DEBUG: SetByCargo } + define_env! { pub(super) OUT_DIR: SetByCargo } + + // XXX: These don't belong here. + define_env! { pub PERL_EXECUTABLE: RerunIfChanged } + define_env! { pub RING_PREGENERATE_ASM: RerunIfChanged } +} + +pub fn root_dir() -> PathBuf { + // Avoid assuming the working directory is the same is the $CARGO_MANIFEST_DIR so that toolchains + // which may assume other working directories can still build this code. + PathBuf::from( + env::var_os(&env::CARGO_MANIFEST_DIR).expect("CARGO_MANIFEST_DIR should always be set"), + ) +} + +pub fn extern_c_prefix(component: &str) -> String { + // Keep in sync with `core_name_and_version!` in prefixed.rs. + let core_name_and_version = [ + &env::var(&env::CARGO_PKG_NAME).unwrap(), + component, + &env::var(&env::CARGO_PKG_VERSION_MAJOR).unwrap(), + &env::var(&env::CARGO_PKG_VERSION_MINOR).unwrap(), + &env::var(&env::CARGO_PKG_VERSION_PATCH).unwrap(), + &env::var(&env::CARGO_PKG_VERSION_PRE).unwrap(), // Often empty + ] + .join("_"); + // Ensure `links` in Cargo.toml is consistent with the version. + assert_eq!( + &env::var(&env::CARGO_MANIFEST_LINKS).unwrap(), + &core_name_and_version + ); + core_name_and_version +} + +impl Target { + pub fn new_from_env() -> Self { + let arch = env::var(&env::CARGO_CFG_TARGET_ARCH).unwrap(); + let os = env::var(&env::CARGO_CFG_TARGET_OS).unwrap(); + let env = env::var(&env::CARGO_CFG_TARGET_ENV).unwrap(); + let endian = env::var(&env::CARGO_CFG_TARGET_ENDIAN).unwrap(); + let endian = if endian == "little" { + Endian::Little + } else { + Endian::Other + }; + + let out_dir = PathBuf::from(env::var_os(&env::OUT_DIR).unwrap()); + + Self { + arch, + os, + env, + endian, + out_dir, + } + } +} + +impl Profile { + pub fn new_from_env(root_dir: &Path) -> Self { + let is_git = fs::metadata(root_dir.join(".git")).is_ok(); + + // Published builds are always built in release mode. + let is_debug = is_git && env::var(&env::DEBUG).unwrap() != "false"; + + // During local development, force warnings in non-Rust code to be treated + // as errors. Since warnings are highly compiler-dependent and compilers + // don't maintain backward compatibility w.r.t. which warnings they issue, + // don't do this for packaged builds. + let force_warnings_into_errors = is_git; + + Self { + is_git, + is_debug, + force_warnings_into_errors, + } + } +} + +// TODO: We should emit `cargo:rerun-if-changed-env` for the various +// environment variables that affect the build. +pub fn emit_rerun_if_changed<'a>(paths: impl Iterator) { + paths.for_each(|path| { + println!("cargo:rerun-if-changed={}", path.to_str().unwrap()); + }) +} diff --git a/build/build.rs b/build/build/core.rs similarity index 51% rename from build/build.rs rename to build/build/core.rs index fa6cdacdea..30f854cbe3 100644 --- a/build/build.rs +++ b/build/build/core.rs @@ -13,29 +13,25 @@ // CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. //! Build the non-Rust components. - -#![allow(clippy::too_many_arguments)] +//! +//! This module doesn't depend on any Cargo-set environment variables, other than +//! what cc-rs depends on. // Avoid `std::env` here. All configuration should be done through `Target`, -// `Profile`, and `Tools`. - -use self::path::{join_components_with_forward_slashes_if_windows, walk_dir}; +// `Profile`, and `Tools`. Avoid `cargo`. +use super::{ + Profile, Target, + c::build_library, + path::{join_components_with_forward_slashes_if_windows, walk_dir}, + prefixed::generate_prefix_symbols_headers, + target::*, +}; use std::{ ffi::{OsStr, OsString}, - fs, - io::Write, path::{Path, PathBuf}, process::{Command, Stdio}, }; -mod path; - -const X86: &str = "x86"; -const X86_64: &str = "x86_64"; -const AARCH64: &str = "aarch64"; -const ARM: &str = "arm"; -const WASM32: &str = "wasm32"; - #[rustfmt::skip] const RING_SRCS: &[(&[&str], &str)] = &[ (&[], "crypto/curve25519/curve25519.c"), @@ -104,45 +100,6 @@ const RING_TEST_SRCS: &[&str] = &[("crypto/constant_time_test.c")]; pub const PREGENERATED: &str = "pregenerated"; -fn cpp_flags(compiler: &cc::Tool) -> &'static [&'static str] { - if !compiler.is_like_msvc() { - static NON_MSVC_FLAGS: &[&str] = &[ - "-fvisibility=hidden", - "-std=c1x", // GCC 4.6 requires "c1x" instead of "c11" - "-Wall", - "-Wbad-function-cast", - "-Wcast-align", - "-Wcast-qual", - "-Wconversion", - "-Wmissing-field-initializers", - "-Wmissing-include-dirs", - "-Wnested-externs", - "-Wredundant-decls", - "-Wshadow", - "-Wsign-compare", - "-Wsign-conversion", - "-Wstrict-prototypes", - "-Wundef", - "-Wuninitialized", - ]; - NON_MSVC_FLAGS - } else { - static MSVC_FLAGS: &[&str] = &[ - "/Gy", // Enable function-level linking. - "/Zc:wchar_t", - "/Zc:forScope", - "/Zc:inline", - // Warnings. - "/W4", - "/wd4127", // C4127: conditional expression is constant - "/wd4464", // C4464: relative include path contains '..' - "/wd5045", /* C5045: Compiler will insert Spectre mitigation for memory load if - * /Qspectre switch specified */ - ]; - MSVC_FLAGS - } -} - // None means "any OS" or "any target". The first match in sequence order is // taken. const ASM_TARGETS: &[AsmTarget] = &[ @@ -229,32 +186,9 @@ impl AsmTarget { } } -const ANDROID: &str = "android"; -const DRAGONFLY: &str = "dragonfly"; -const FREEBSD: &str = "freebsd"; -const FUCHSIA: &str = "fuchsia"; -const HAIKU: &str = "haiku"; -const HORIZON: &str = "horizon"; -const HURD: &str = "hurd"; -const ILLUMOS: &str = "illumos"; -const LINUX: &str = "linux"; -const NETBSD: &str = "netbsd"; -const NTO: &str = "nto"; -const OPENBSD: &str = "openbsd"; -const REDOX: &str = "redox"; -const SOLARIS: &str = "solaris"; -const VITA: &str = "vita"; - const WIN32N: &str = "win32n"; const NASM: &str = "nasm"; -/// Operating systems that have the same ABI as macOS on every architecture -/// mentioned in `ASM_TARGETS`. -const APPLE_ABI: &[&str] = &["ios", "macos", "tvos", "visionos", "watchos"]; - -const WINDOWS: &str = "windows"; -const CYGWIN: &str = "cygwin"; - pub fn generate_sources_and_preassemble<'a>( tools: &Tools, out_dir: &Path, @@ -262,7 +196,13 @@ pub fn generate_sources_and_preassemble<'a>( c_root_dir: &Path, core_name_and_version: &str, ) { - generate_prefix_symbols_headers(out_dir, core_name_and_version).unwrap(); + generate_prefix_symbols_headers( + out_dir, + core_name_and_version, + SYMBOLS_TO_RENAME, + SYMBOLS_TO_PREFIX, + ) + .unwrap(); for asm_target in asm_targets { let perlasm_src_dsts = perlasm_src_dsts(out_dir, asm_target); @@ -280,40 +220,12 @@ pub fn generate_sources_and_preassemble<'a>( } } -pub struct Target { - pub arch: String, - pub os: String, - pub env: String, - pub endian: Endian, -} - -pub enum Endian { - Little, - Other, -} - -pub struct Profile { - /// Is this a debug build? This affects whether assertions might be enabled - /// in the C code. For packaged builds, this should always be `false`. - pub is_debug: bool, - - /// true: Force warnings to be treated as errors. - /// false: Use the default behavior (perhaps determined by `$CFLAGS`, etc.) - pub force_warnings_into_errors: bool, -} - -pub struct Tools<'a> { - pub perl_exe: &'a Path, - pub nasm_exe: &'a OsStr, -} - pub fn build_c_code( asm_target: Option<&AsmTarget>, target: &Target, profile: &Profile, generated_dir: &Path, c_root_dir: &Path, - out_dir: &Path, core_name_and_version: &str, ) { let (asm_srcs, obj_srcs) = if let Some(asm_target) = asm_target { @@ -380,7 +292,6 @@ pub fn build_c_code( target, profile, c_root_dir, - out_dir, lib_name, srcs, generated_dir, @@ -390,58 +301,8 @@ pub fn build_c_code( println!( "cargo:rustc-link-search=native={}", - out_dir.to_str().expect("Invalid path") - ); -} - -fn new_build( - target: &Target, - profile: &Profile, - c_root_dir: &Path, - include_dir: &Path, -) -> cc::Build { - let mut b = cc::Build::new(); - configure_cc(&mut b, target, profile, c_root_dir, include_dir); - b -} - -fn build_library<'a>( - target: &Target, - profile: &Profile, - c_root_dir: &Path, - out_dir: &Path, - lib_name: &str, - srcs: impl Iterator, - include_dir: &Path, - preassembled_objs: &[PathBuf], -) { - let mut c = new_build(target, profile, c_root_dir, include_dir); - - // Compile all the (dirty) source files into object files. - srcs.for_each(|src| { - c.file(c_root_dir.join(src)); - }); - - preassembled_objs.iter().for_each(|obj| { - c.object(obj); - }); - - // Rebuild the library if necessary. - let lib_path = PathBuf::from(out_dir).join(format!("lib{lib_name}.a")); - - // Handled below. - let _ = c.cargo_metadata(false); - - c.compile( - lib_path - .file_name() - .and_then(|f| f.to_str()) - .expect("No filename"), + target.out_dir.to_str().expect("Invalid path") ); - - // Link the library. This works even when the library doesn't need to be - // rebuilt. - println!("cargo:rustc-link-lib=static={lib_name}"); } fn obj_path(out_dir: &Path, src: &Path) -> PathBuf { @@ -453,58 +314,9 @@ fn obj_path(out_dir: &Path, src: &Path) -> PathBuf { out_path } -fn configure_cc( - c: &mut cc::Build, - target: &Target, - profile: &Profile, - c_root_dir: &Path, - include_dir: &Path, -) { - // FIXME: On Windows AArch64 we currently must use Clang to compile C code. - // clang-cl emulates the cl.exe command line, `$CFLAGS`, etc. - if target.os == WINDOWS && target.arch == AARCH64 { - let _: &_ = c.prefer_clang_cl_over_msvc(true); - }; - let compiler = c.get_compiler(); - - let _ = c.include(c_root_dir.join("include")); - let _ = c.include(include_dir); - for f in cpp_flags(&compiler) { - let _ = c.flag(f); - } - - if APPLE_ABI.contains(&target.os.as_str()) { - // ``-gfull`` is required for Darwin's |-dead_strip|. - let _ = c.flag("-gfull"); - } else if !compiler.is_like_msvc() { - let _ = c.flag("-g3"); - }; - - if !profile.is_debug { - let _ = c.define("NDEBUG", None); - } - - if target.arch == X86 { - let is_msvc_not_clang_cl = compiler.is_like_msvc() && !compiler.is_like_clang_cl(); - if !is_msvc_not_clang_cl { - let _ = c.flag("-msse2"); - } - } - - // Allow cross-compiling without a target sysroot for these targets. - if (target.arch == WASM32) - || (target.os == "linux" && target.env == "musl" && target.arch != X86_64) - { - // TODO: Expand this to non-clang compilers in 0.17.0 if practical. - if compiler.is_like_clang() { - let _ = c.flag("-nostdlibinc"); - let _ = c.define("RING_CORE_NOSTDLIBINC", "1"); - } - } - - if profile.force_warnings_into_errors { - c.warnings_into_errors(true); - } +pub struct Tools<'a> { + pub perl_exe: &'a Path, + pub nasm_exe: &'a OsStr, } impl Tools<'_> { @@ -645,252 +457,168 @@ pub fn walk_non_root_sources(f: impl Fn(&Path)) { } } -/// Creates the necessary header files for symbol renaming. -/// -/// For simplicity, both non-Nasm- and Nasm- style headers are always -/// generated, even though local non-packaged builds need only one of them. -fn generate_prefix_symbols_headers( - out_dir: &Path, - core_name_and_version: &str, -) -> Result<(), std::io::Error> { - let prefix = &(String::from(core_name_and_version) + "_"); - - generate_prefix_symbols_header(out_dir, "prefix_symbols.h", '#', None, prefix)?; - - generate_prefix_symbols_header( - out_dir, - "prefix_symbols_asm.h", - '#', - Some("#if defined(__APPLE__)"), - prefix, - )?; - - generate_prefix_symbols_header( - out_dir, - "prefix_symbols_nasm.inc", - '%', - Some("%ifidn __OUTPUT_FORMAT__,win32"), - prefix, - )?; - - Ok(()) -} - -fn generate_prefix_symbols_header( - out_dir: &Path, - filename: &str, - pp: char, - prefix_condition: Option<&str>, - prefix: &str, -) -> Result<(), std::io::Error> { - let dir = out_dir.join("ring_core_generated"); - fs::create_dir_all(&dir)?; - - let path = dir.join(filename); - let mut file = fs::File::create(path)?; - - let filename_ident = filename.replace('.', "_").to_uppercase(); - writeln!( - file, - r#" -{pp}ifndef ring_core_generated_{filename_ident} -{pp}define ring_core_generated_{filename_ident} -"# - )?; - - if let Some(prefix_condition) = prefix_condition { - writeln!(file, "{prefix_condition}")?; - writeln!(file, "{}", prefix_all_symbols(pp, "_", prefix))?; - writeln!(file, "{pp}else")?; - }; - writeln!(file, "{}", prefix_all_symbols(pp, "", prefix))?; - if prefix_condition.is_some() { - writeln!(file, "{pp}endif")? - } - - writeln!(file, "{pp}endif")?; - - Ok(()) -} - -fn prefix_all_symbols(pp: char, prefix_prefix: &str, prefix: &str) -> String { - // Rename some nistz256 assembly functions to match the names of their - // polyfills. - static SYMBOLS_TO_RENAME: &[(&str, &str)] = &[ - ("ecp_nistz256_point_double", "p256_point_double"), - ("ecp_nistz256_point_add", "p256_point_add"), - ("ecp_nistz256_point_add_affine", "p256_point_add_affine"), - ("ecp_nistz256_ord_mul_mont", "p256_scalar_mul_mont"), - ("ecp_nistz256_ord_sqr_mont", "p256_scalar_sqr_rep_mont"), - ("ecp_nistz256_mul_mont", "p256_mul_mont"), - ("ecp_nistz256_sqr_mont", "p256_sqr_mont"), - ]; - - static SYMBOLS_TO_PREFIX: &[&str] = &[ - "adx_bmi2_available", - "avx2_available", - "ChaCha20_ctr32", - "ChaCha20_ctr32_avx2", - "ChaCha20_ctr32_neon", - "ChaCha20_ctr32_nohw", - "ChaCha20_ctr32_ssse3", - "ChaCha20_ctr32_ssse3_4x", - "LIMB_is_zero", - "LIMBS_add_mod", - "LIMBS_are_zero", - "LIMBS_equal", - "LIMBS_less_than", - "LIMBS_reduce_once", - "LIMBS_select_512_32", - "LIMBS_shl_mod", - "LIMBS_sub_mod", - "LIMBS_window5_split_window", - "LIMBS_window5_unsplit_window", - "aes_gcm_dec_kernel", - "aes_gcm_dec_update_vaes_avx2", - "aes_gcm_enc_kernel", - "aes_gcm_enc_update_vaes_avx2", - "aes_hw_ctr32_encrypt_blocks", - "aes_hw_encrypt_xor_block", - "aes_hw_set_encrypt_key", - "aes_hw_set_encrypt_key_128", - "aes_hw_set_encrypt_key_256", - "aes_hw_set_encrypt_key_alt", - "aes_hw_set_encrypt_key_base", - "aesni_gcm_decrypt", - "aesni_gcm_encrypt", - "bn_from_montgomery_in_place", - "bn_gather5", - "bn_mul_mont_fallback", - "bn_mul_mont_nohw", - "bn_mul_mont_sse2", - "bn_mul4x_mont", - "bn_mulx4x_mont", - "bn_mul8x_mont_neon", - "bn_mul4x_mont_gather5", - "bn_mulx4x_mont_gather5", - "bn_neg_inv_mod_r_u64", - "bn_power5_nohw", - "bn_powerx5", - "bn_sqr8x_internal", - "bn_sqr8x_mont", - "bn_sqrx8x_internal", - "bsaes_ctr32_encrypt_blocks", - "bssl_constant_time_test_conditional_memcpy", - "bssl_constant_time_test_conditional_memxor", - "bssl_constant_time_test_main", - "chacha20_poly1305_open", - "chacha20_poly1305_open_avx2", - "chacha20_poly1305_open_sse41", - "chacha20_poly1305_seal", - "chacha20_poly1305_seal_avx2", - "chacha20_poly1305_seal_sse41", - "ecp_nistz256_mul_mont_adx", - "ecp_nistz256_mul_mont_nohw", - "ecp_nistz256_ord_mul_mont_adx", - "ecp_nistz256_ord_mul_mont_nohw", - "ecp_nistz256_ord_sqr_mont_adx", - "ecp_nistz256_ord_sqr_mont_nohw", - "ecp_nistz256_point_add_adx", - "ecp_nistz256_point_add_nohw", - "ecp_nistz256_point_add_affine_adx", - "ecp_nistz256_point_add_affine_nohw", - "ecp_nistz256_point_double_adx", - "ecp_nistz256_point_double_nohw", - "ecp_nistz256_select_w5_avx2", - "ecp_nistz256_select_w5_nohw", - "ecp_nistz256_select_w7_avx2", - "ecp_nistz256_select_w7_nohw", - "ecp_nistz256_sqr_mont_adx", - "ecp_nistz256_sqr_mont_nohw", - "fiat_curve25519_adx_mul", - "fiat_curve25519_adx_square", - "gcm_ghash_avx", - "gcm_ghash_clmul", - "gcm_ghash_neon", - "gcm_ghash_vpclmulqdq_avx2_16", - "gcm_gmult_clmul", - "gcm_gmult_neon", - "gcm_gmult_v8", - "gcm_init_avx", - "gcm_init_clmul", - "gcm_init_neon", - "gcm_init_v8", - "gcm_init_vpclmulqdq_avx2", - "k25519Precomp", - "limbs_mul_add_limb", - "little_endian_bytes_from_scalar", - "ecp_nistz256_neg", - "ecp_nistz256_select_w5", - "ecp_nistz256_select_w7", - "neon_available", - "p256_mul_mont", - "p256_point_add", - "p256_point_add_affine", - "p256_point_double", - "p256_point_mul", - "p256_point_mul_base", - "p256_point_mul_base_vartime", - "p256_scalar_mul_mont", - "p256_scalar_sqr_rep_mont", - "p256_sqr_mont", - "p384_elem_div_by_2", - "p384_elem_mul_mont", - "p384_elem_neg", - "p384_elem_sub", - "p384_point_add", - "p384_point_double", - "p384_point_mul", - "p384_scalar_mul_mont", - "openssl_poly1305_neon2_addmulmod", - "openssl_poly1305_neon2_blocks", - "sha256_block_data_order", - "sha256_block_data_order_avx", - "sha256_block_data_order_ssse3", - "sha256_block_data_order_hw", - "sha256_block_data_order_neon", - "sha256_block_data_order_nohw", - "sha512_block_data_order", - "sha512_block_data_order_avx", - "sha512_block_data_order_hw", - "sha512_block_data_order_neon", - "sha512_block_data_order_nohw", - "vpaes_ctr32_encrypt_blocks", - "vpaes_encrypt", - "vpaes_encrypt_key_to_bsaes", - "vpaes_set_encrypt_key", - "vpaes_set_encrypt_key", - "x25519_NEON", - "x25519_fe_invert", - "x25519_fe_isnegative", - "x25519_fe_mul_assign_tt", - "x25519_fe_neg", - "x25519_fe_tobytes", - "x25519_ge_double_scalarmult_vartime", - "x25519_ge_frombytes_vartime", - "x25519_ge_scalarmult_base", - "x25519_ge_scalarmult_base_adx", - "x25519_ge_scalarmult_base_adx_from_bytes", - "x25519_public_from_private_generic_masked", - "x25519_sc_mask", - "x25519_sc_muladd", - "x25519_sc_reduce", - "x25519_scalar_mult_adx", - "x25519_scalar_mult_generic_masked", - "x25519_u_coordinate", - ]; - - let mut out = String::new(); - - for (old, new) in SYMBOLS_TO_RENAME { - let line = format!("{pp}define {prefix_prefix}{old} {prefix_prefix}{new}\n"); - out += &line; - } - - for symbol in SYMBOLS_TO_PREFIX { - let line = format!("{pp}define {prefix_prefix}{symbol} {prefix_prefix}{prefix}{symbol}\n"); - out += &line; - } +// Rename some nistz256 assembly functions to match the names of their +// polyfills. +static SYMBOLS_TO_RENAME: &[(&str, &str)] = &[ + ("ecp_nistz256_point_double", "p256_point_double"), + ("ecp_nistz256_point_add", "p256_point_add"), + ("ecp_nistz256_point_add_affine", "p256_point_add_affine"), + ("ecp_nistz256_ord_mul_mont", "p256_scalar_mul_mont"), + ("ecp_nistz256_ord_sqr_mont", "p256_scalar_sqr_rep_mont"), + ("ecp_nistz256_mul_mont", "p256_mul_mont"), + ("ecp_nistz256_sqr_mont", "p256_sqr_mont"), +]; - out -} +static SYMBOLS_TO_PREFIX: &[&str] = &[ + "adx_bmi2_available", + "avx2_available", + "ChaCha20_ctr32", + "ChaCha20_ctr32_avx2", + "ChaCha20_ctr32_neon", + "ChaCha20_ctr32_nohw", + "ChaCha20_ctr32_ssse3", + "ChaCha20_ctr32_ssse3_4x", + "LIMB_is_zero", + "LIMBS_add_mod", + "LIMBS_are_zero", + "LIMBS_equal", + "LIMBS_less_than", + "LIMBS_reduce_once", + "LIMBS_select_512_32", + "LIMBS_shl_mod", + "LIMBS_sub_mod", + "LIMBS_window5_split_window", + "LIMBS_window5_unsplit_window", + "aes_gcm_dec_kernel", + "aes_gcm_dec_update_vaes_avx2", + "aes_gcm_enc_kernel", + "aes_gcm_enc_update_vaes_avx2", + "aes_hw_ctr32_encrypt_blocks", + "aes_hw_encrypt_xor_block", + "aes_hw_set_encrypt_key", + "aes_hw_set_encrypt_key_128", + "aes_hw_set_encrypt_key_256", + "aes_hw_set_encrypt_key_alt", + "aes_hw_set_encrypt_key_base", + "aesni_gcm_decrypt", + "aesni_gcm_encrypt", + "bn_from_montgomery_in_place", + "bn_gather5", + "bn_mul_mont_fallback", + "bn_mul_mont_nohw", + "bn_mul_mont_sse2", + "bn_mul4x_mont", + "bn_mulx4x_mont", + "bn_mul8x_mont_neon", + "bn_mul4x_mont_gather5", + "bn_mulx4x_mont_gather5", + "bn_neg_inv_mod_r_u64", + "bn_power5_nohw", + "bn_powerx5", + "bn_sqr8x_internal", + "bn_sqr8x_mont", + "bn_sqrx8x_internal", + "bsaes_ctr32_encrypt_blocks", + "bssl_constant_time_test_conditional_memcpy", + "bssl_constant_time_test_conditional_memxor", + "bssl_constant_time_test_main", + "chacha20_poly1305_open", + "chacha20_poly1305_open_avx2", + "chacha20_poly1305_open_sse41", + "chacha20_poly1305_seal", + "chacha20_poly1305_seal_avx2", + "chacha20_poly1305_seal_sse41", + "ecp_nistz256_mul_mont_adx", + "ecp_nistz256_mul_mont_nohw", + "ecp_nistz256_ord_mul_mont_adx", + "ecp_nistz256_ord_mul_mont_nohw", + "ecp_nistz256_ord_sqr_mont_adx", + "ecp_nistz256_ord_sqr_mont_nohw", + "ecp_nistz256_point_add_adx", + "ecp_nistz256_point_add_nohw", + "ecp_nistz256_point_add_affine_adx", + "ecp_nistz256_point_add_affine_nohw", + "ecp_nistz256_point_double_adx", + "ecp_nistz256_point_double_nohw", + "ecp_nistz256_select_w5_avx2", + "ecp_nistz256_select_w5_nohw", + "ecp_nistz256_select_w7_avx2", + "ecp_nistz256_select_w7_nohw", + "ecp_nistz256_sqr_mont_adx", + "ecp_nistz256_sqr_mont_nohw", + "fiat_curve25519_adx_mul", + "fiat_curve25519_adx_square", + "gcm_ghash_avx", + "gcm_ghash_clmul", + "gcm_ghash_neon", + "gcm_ghash_vpclmulqdq_avx2_16", + "gcm_gmult_clmul", + "gcm_gmult_neon", + "gcm_gmult_v8", + "gcm_init_avx", + "gcm_init_clmul", + "gcm_init_neon", + "gcm_init_v8", + "gcm_init_vpclmulqdq_avx2", + "k25519Precomp", + "limbs_mul_add_limb", + "little_endian_bytes_from_scalar", + "ecp_nistz256_neg", + "ecp_nistz256_select_w5", + "ecp_nistz256_select_w7", + "neon_available", + "p256_mul_mont", + "p256_point_add", + "p256_point_add_affine", + "p256_point_double", + "p256_point_mul", + "p256_point_mul_base", + "p256_point_mul_base_vartime", + "p256_scalar_mul_mont", + "p256_scalar_sqr_rep_mont", + "p256_sqr_mont", + "p384_elem_div_by_2", + "p384_elem_mul_mont", + "p384_elem_neg", + "p384_elem_sub", + "p384_point_add", + "p384_point_double", + "p384_point_mul", + "p384_scalar_mul_mont", + "openssl_poly1305_neon2_addmulmod", + "openssl_poly1305_neon2_blocks", + "sha256_block_data_order", + "sha256_block_data_order_avx", + "sha256_block_data_order_ssse3", + "sha256_block_data_order_hw", + "sha256_block_data_order_neon", + "sha256_block_data_order_nohw", + "sha512_block_data_order", + "sha512_block_data_order_avx", + "sha512_block_data_order_hw", + "sha512_block_data_order_neon", + "sha512_block_data_order_nohw", + "vpaes_ctr32_encrypt_blocks", + "vpaes_encrypt", + "vpaes_encrypt_key_to_bsaes", + "vpaes_set_encrypt_key", + "vpaes_set_encrypt_key", + "x25519_NEON", + "x25519_fe_invert", + "x25519_fe_isnegative", + "x25519_fe_mul_assign_tt", + "x25519_fe_neg", + "x25519_fe_tobytes", + "x25519_ge_double_scalarmult_vartime", + "x25519_ge_frombytes_vartime", + "x25519_ge_scalarmult_base", + "x25519_ge_scalarmult_base_adx", + "x25519_ge_scalarmult_base_adx_from_bytes", + "x25519_public_from_private_generic_masked", + "x25519_sc_mask", + "x25519_sc_muladd", + "x25519_sc_reduce", + "x25519_scalar_mult_adx", + "x25519_scalar_mult_generic_masked", + "x25519_u_coordinate", +]; diff --git a/build/build/mod.rs b/build/build/mod.rs new file mode 100644 index 0000000000..fadecb1222 --- /dev/null +++ b/build/build/mod.rs @@ -0,0 +1,8 @@ +mod c; +pub mod cargo; +pub mod core; +mod path; +mod prefixed; +mod target; + +pub use self::{c::Profile, target::Target}; diff --git a/build/build/path.rs b/build/build/path.rs index 24bc8b8a00..380521a492 100644 --- a/build/build/path.rs +++ b/build/build/path.rs @@ -12,6 +12,8 @@ // OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN // CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +//! Path manipulation. + use std::{ ffi::OsString, fs::{self, DirEntry}, diff --git a/build/build/prefixed.rs b/build/build/prefixed.rs new file mode 100644 index 0000000000..c7d6b9c112 --- /dev/null +++ b/build/build/prefixed.rs @@ -0,0 +1,133 @@ +// Copyright 2015-2026 Brian Smith. +// +// Permission to use, copy, modify, and/or distribute this software for any +// purpose with or without fee is hereby granted, provided that the above +// copyright notice and this permission notice appear in all copies. +// +// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY +// SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION +// OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN +// CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +//! Symbol prefixing to prevent linking collisions with OpenSSL(-derivative) +//! libraries and with other versions of *ring*. + +// Avoid `std::env` here. All configuration should be done through parameters. +use std::{fs, io::Write, path::Path}; + +/// Creates the necessary header files for symbol renaming. +/// +/// For simplicity, both non-Nasm- and Nasm- style headers are always +/// generated, even though local non-packaged builds need only one of them. +pub fn generate_prefix_symbols_headers( + out_dir: &Path, + core_name_and_version: &str, + symbols_to_rename: &[(&str, &str)], + symbols_to_prefix: &[&str], +) -> Result<(), std::io::Error> { + let prefix = &(String::from(core_name_and_version) + "_"); + + generate_prefix_symbols_header( + out_dir, + "prefix_symbols.h", + '#', + None, + prefix, + symbols_to_rename, + symbols_to_prefix, + )?; + + generate_prefix_symbols_header( + out_dir, + "prefix_symbols_asm.h", + '#', + Some("#if defined(__APPLE__)"), + prefix, + symbols_to_rename, + symbols_to_prefix, + )?; + + generate_prefix_symbols_header( + out_dir, + "prefix_symbols_nasm.inc", + '%', + Some("%ifidn __OUTPUT_FORMAT__,win32"), + prefix, + symbols_to_rename, + symbols_to_prefix, + )?; + + Ok(()) +} + +fn generate_prefix_symbols_header( + out_dir: &Path, + filename: &str, + pp: char, + prefix_condition: Option<&str>, + prefix: &str, + symbols_to_rename: &[(&str, &str)], + symbols_to_prefix: &[&str], +) -> Result<(), std::io::Error> { + let dir = out_dir.join("ring_core_generated"); + fs::create_dir_all(&dir)?; + + let path = dir.join(filename); + let mut file = fs::File::create(path)?; + + let filename_ident = filename.replace('.', "_").to_uppercase(); + writeln!( + file, + r#" +{pp}ifndef ring_core_generated_{filename_ident} +{pp}define ring_core_generated_{filename_ident} +"# + )?; + + if let Some(prefix_condition) = prefix_condition { + writeln!(file, "{prefix_condition}")?; + writeln!( + file, + "{}", + prefix_all_symbols(pp, "_", prefix, symbols_to_rename, symbols_to_prefix) + )?; + writeln!(file, "{pp}else")?; + }; + writeln!( + file, + "{}", + prefix_all_symbols(pp, "", prefix, symbols_to_rename, symbols_to_prefix) + )?; + if prefix_condition.is_some() { + writeln!(file, "{pp}endif")? + } + + writeln!(file, "{pp}endif")?; + + Ok(()) +} + +fn prefix_all_symbols( + pp: char, + prefix_prefix: &str, + prefix: &str, + symbols_to_rename: &[(&str, &str)], + symbols_to_prefix: &[&str], +) -> String { + let mut out = String::new(); + + for (old, new) in symbols_to_rename { + let line = format!("{pp}define {prefix_prefix}{old} {prefix_prefix}{new}\n"); + out += &line; + } + + for symbol in symbols_to_prefix { + let line = format!("{pp}define {prefix_prefix}{symbol} {prefix_prefix}{prefix}{symbol}\n"); + out += &line; + } + + out +} diff --git a/build/build/target.rs b/build/build/target.rs new file mode 100644 index 0000000000..806f802e1f --- /dev/null +++ b/build/build/target.rs @@ -0,0 +1,59 @@ +// Copyright 2015-2026 Brian Smith. +// +// Permission to use, copy, modify, and/or distribute this software for any +// purpose with or without fee is hereby granted, provided that the above +// copyright notice and this permission notice appear in all copies. +// +// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY +// SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION +// OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN +// CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +// Avoid `std::env` here. All configuration should be done through `Target`, +// `Profile`, and `Tools`. +use std::path::PathBuf; + +pub const X86: &str = "x86"; +pub const X86_64: &str = "x86_64"; +pub const AARCH64: &str = "aarch64"; +pub const ARM: &str = "arm"; +pub const WASM32: &str = "wasm32"; + +pub const ANDROID: &str = "android"; +pub const DRAGONFLY: &str = "dragonfly"; +pub const FREEBSD: &str = "freebsd"; +pub const FUCHSIA: &str = "fuchsia"; +pub const HAIKU: &str = "haiku"; +pub const HORIZON: &str = "horizon"; +pub const HURD: &str = "hurd"; +pub const ILLUMOS: &str = "illumos"; +pub const LINUX: &str = "linux"; +pub const NETBSD: &str = "netbsd"; +pub const NTO: &str = "nto"; +pub const OPENBSD: &str = "openbsd"; +pub const REDOX: &str = "redox"; +pub const SOLARIS: &str = "solaris"; +pub const VITA: &str = "vita"; + +/// Operating systems that have the same ABI as macOS on every architecture +/// mentioned in `ASM_TARGETS`. +pub const APPLE_ABI: &[&str] = &["ios", "macos", "tvos", "visionos", "watchos"]; + +pub const WINDOWS: &str = "windows"; +pub const CYGWIN: &str = "cygwin"; + +pub struct Target { + pub arch: String, + pub os: String, + pub env: String, + pub endian: Endian, + pub out_dir: PathBuf, +} + +pub enum Endian { + Little, + Other, +} diff --git a/build/cargo.rs b/build/cargo.rs deleted file mode 100644 index b7b2a51110..0000000000 --- a/build/cargo.rs +++ /dev/null @@ -1,229 +0,0 @@ -// Copyright 2015-2016 Brian Smith. -// -// Permission to use, copy, modify, and/or distribute this software for any -// purpose with or without fee is hereby granted, provided that the above -// copyright notice and this permission notice appear in all copies. -// -// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES -// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY -// SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION -// OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN -// CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -//! Build the non-Rust components. - -#![allow(clippy::too_many_arguments)] - -use self::build::{ - AsmTarget, Endian, PREGENERATED, Profile, Target, Tools, build_c_code, - generate_sources_and_preassemble, walk_non_root_sources, -}; -// Avoid `std::env` here; use `self::env` instead. -use std::{ - ffi::OsStr, - fs::{self}, - path::{Path, PathBuf}, -}; - -mod build; - -mod env { - use std::ffi::OsString; - - macro_rules! define_env { - { $vis:vis $NAME:ident : $ty:ident } => { - $vis const $NAME: EnvVar = EnvVar { - name: stringify!($NAME), - ty: EnvVarTy::$ty, - }; - }; - } - - enum EnvVarTy { - RerunIfChanged, - SetByCargo, - } - - pub struct EnvVar { - pub name: &'static str, - ty: EnvVarTy, - } - - /// Read an environment variable and optionally tell Cargo that we depend on it. - /// - /// The env var is static since we intend to only read a static set of environment - /// variables. - pub fn var_os(env_var: &'static EnvVar) -> Option { - match env_var.ty { - EnvVarTy::RerunIfChanged => { - println!("cargo:rerun-if-env-changed={}", env_var.name); - } - EnvVarTy::SetByCargo => {} - } - std::env::var_os(env_var.name) - } - - pub fn var(env_var: &'static EnvVar) -> Option { - var_os(env_var).and_then(|value| value.into_string().ok()) - } - - // In alphabetical order - define_env! { pub CARGO_CFG_TARGET_ARCH: SetByCargo } - define_env! { pub CARGO_CFG_TARGET_ENDIAN: SetByCargo } - define_env! { pub CARGO_CFG_TARGET_ENV: SetByCargo } - define_env! { pub CARGO_CFG_TARGET_OS: SetByCargo } - define_env! { pub CARGO_MANIFEST_DIR: SetByCargo } - define_env! { pub CARGO_MANIFEST_LINKS: SetByCargo } - define_env! { pub CARGO_PKG_NAME: SetByCargo } - define_env! { pub CARGO_PKG_VERSION_MAJOR: SetByCargo } - define_env! { pub CARGO_PKG_VERSION_MINOR: SetByCargo } - define_env! { pub CARGO_PKG_VERSION_PATCH: SetByCargo } - define_env! { pub CARGO_PKG_VERSION_PRE: SetByCargo } - define_env! { pub DEBUG: SetByCargo } - define_env! { pub OUT_DIR: SetByCargo } - define_env! { pub PERL_EXECUTABLE: RerunIfChanged } - define_env! { pub RING_PREGENERATE_ASM: RerunIfChanged } -} - -fn main() { - // Avoid assuming the working directory is the same is the $CARGO_MANIFEST_DIR so that toolchains - // which may assume other working directories can still build this code. - let c_root_dir = PathBuf::from( - env::var_os(&env::CARGO_MANIFEST_DIR).expect("CARGO_MANIFEST_DIR should always be set"), - ); - - // Keep in sync with `core_name_and_version!` in prefixed.rs. - let core_name_and_version = [ - &env::var(&env::CARGO_PKG_NAME).unwrap(), - "core", - &env::var(&env::CARGO_PKG_VERSION_MAJOR).unwrap(), - &env::var(&env::CARGO_PKG_VERSION_MINOR).unwrap(), - &env::var(&env::CARGO_PKG_VERSION_PATCH).unwrap(), - &env::var(&env::CARGO_PKG_VERSION_PRE).unwrap(), // Often empty - ] - .join("_"); - // Ensure `links` in Cargo.toml is consistent with the version. - assert_eq!( - &env::var(&env::CARGO_MANIFEST_LINKS).unwrap(), - &core_name_and_version - ); - - let perl_exe = get_perl_exe(); - let nasm_exe: &OsStr = "./target/tools/windows/nasm/nasm".as_ref(); - - let tools = Tools { - perl_exe: &perl_exe, - nasm_exe, - }; - - match env::var_os(&env::RING_PREGENERATE_ASM).as_deref() { - Some(s) if s == "1" => { - pregenerate_asm_main(&tools, &c_root_dir, &core_name_and_version); - } - None => ring_build_rs_main(&tools, &c_root_dir, &core_name_and_version), - _ => { - panic!("${} has an invalid value", &env::RING_PREGENERATE_ASM.name); - } - } -} - -fn ring_build_rs_main(tools: &Tools, c_root_dir: &Path, core_name_and_version: &str) { - let out_dir = env::var_os(&env::OUT_DIR).unwrap(); - let out_dir = PathBuf::from(out_dir); - - let arch = env::var(&env::CARGO_CFG_TARGET_ARCH).unwrap(); - let os = env::var(&env::CARGO_CFG_TARGET_OS).unwrap(); - let env = env::var(&env::CARGO_CFG_TARGET_ENV).unwrap(); - let endian = env::var(&env::CARGO_CFG_TARGET_ENDIAN).unwrap(); - let endian = if endian == "little" { - Endian::Little - } else { - Endian::Other - }; - - let is_git = fs::metadata(c_root_dir.join(".git")).is_ok(); - - // Published builds are always built in release mode. - let is_debug = is_git && env::var(&env::DEBUG).unwrap() != "false"; - - // During local development, force warnings in non-Rust code to be treated - // as errors. Since warnings are highly compiler-dependent and compilers - // don't maintain backward compatibility w.r.t. which warnings they issue, - // don't do this for packaged builds. - let force_warnings_into_errors = is_git; - - let target = Target { - arch, - os, - env, - endian, - }; - let profile = Profile { - is_debug, - force_warnings_into_errors, - }; - - let asm_target = AsmTarget::for_target(&target); - - // If `.git` exists then assume this is the "local hacking" case where - // we want to make it easy to build *ring* using `cargo build`/`cargo test` - // without a prerequisite `package` step, at the cost of needing additional - // tools like `Perl` and/or `nasm`. - // - // If `.git` doesn't exist then assume that this is a packaged build where - // we want to optimize for minimizing the build tools required: No Perl, - // no nasm, etc. - let generated_dir = if !is_git { - c_root_dir.join(PREGENERATED) - } else { - generate_sources_and_preassemble( - tools, - &out_dir, - asm_target.into_iter(), - c_root_dir, - core_name_and_version, - ); - out_dir.clone() - }; - - build_c_code( - asm_target, - &target, - &profile, - &generated_dir, - c_root_dir, - &out_dir, - core_name_and_version, - ); - emit_rerun_if_changed() -} - -fn pregenerate_asm_main(tools: &Tools, c_root_dir: &Path, core_name_and_version: &str) { - let pregenerated = c_root_dir.join(PREGENERATED); - fs::create_dir(&pregenerated).unwrap(); - generate_sources_and_preassemble( - tools, - &pregenerated, - AsmTarget::all(), - c_root_dir, - core_name_and_version, - ); -} - -// TODO: We should emit `cargo:rerun-if-changed-env` for the various -// environment variables that affect the build. -fn emit_rerun_if_changed() { - walk_non_root_sources(|path| { - println!("cargo:rerun-if-changed={}", path.to_str().unwrap()); - }) -} - -fn get_perl_exe() -> PathBuf { - get_command(&env::PERL_EXECUTABLE, "perl") -} - -fn get_command(var: &'static env::EnvVar, default: &str) -> PathBuf { - PathBuf::from(env::var_os(var).unwrap_or_else(|| default.into())) -} diff --git a/build/main.rs b/build/main.rs new file mode 100644 index 0000000000..8041bd2b0a --- /dev/null +++ b/build/main.rs @@ -0,0 +1,128 @@ +// Copyright 2015-2016 Brian Smith. +// +// Permission to use, copy, modify, and/or distribute this software for any +// purpose with or without fee is hereby granted, provided that the above +// copyright notice and this permission notice appear in all copies. +// +// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY +// SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION +// OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN +// CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +//! Build the non-Rust components. + +use build::{ + Profile, Target, cargo, + core::{ + AsmTarget, PREGENERATED, Tools, build_c_code, generate_sources_and_preassemble, + walk_non_root_sources, + }, +}; +// Avoid `std::env` here; use `cargo::env` instead. +use std::{ + ffi::OsStr, + fs::{self}, + iter, + path::{Path, PathBuf}, +}; + +mod build; + +fn main() { + // Avoid assuming the working directory is the same is the $CARGO_MANIFEST_DIR so that toolchains + // which may assume other working directories can still build this code. + let c_root_dir = cargo::root_dir(); + + // Keep in sync with `core_name_and_version!` in prefixed.rs. + let core_name_and_version = cargo::extern_c_prefix("core"); + + let perl_exe = get_perl_exe(); + let nasm_exe: &OsStr = "./target/tools/windows/nasm/nasm".as_ref(); + + let tools = Tools { + perl_exe: &perl_exe, + nasm_exe, + }; + + match cargo::env::var_os(&cargo::env::RING_PREGENERATE_ASM).as_deref() { + Some(s) if s == "1" => { + pregenerate_asm_main(&tools, &c_root_dir, &core_name_and_version); + } + None => ring_build_rs_main(&tools, &c_root_dir, &core_name_and_version), + _ => { + panic!( + "${} has an invalid value", + &cargo::env::RING_PREGENERATE_ASM.name + ); + } + } +} + +fn ring_build_rs_main(tools: &Tools, c_root_dir: &Path, core_name_and_version: &str) { + let target = Target::new_from_env(); + let profile = Profile::new_from_env(c_root_dir); + + let asm_target = AsmTarget::for_target(&target); + + // If `.git` exists then assume this is the "local hacking" case where + // we want to make it easy to build *ring* using `cargo build`/`cargo test` + // without a prerequisite `package` step, at the cost of needing additional + // tools like `Perl` and/or `nasm`. + // + // If `.git` doesn't exist then assume that this is a packaged build where + // we want to optimize for minimizing the build tools required: No Perl, + // no nasm, etc. + let generated_dir = if !profile.is_git { + &c_root_dir.join(PREGENERATED) + } else { + generate_sources_and_preassemble( + tools, + &target.out_dir, + asm_target.into_iter(), + c_root_dir, + core_name_and_version, + ); + &target.out_dir + }; + + build_c_code( + asm_target, + &target, + &profile, + generated_dir, + c_root_dir, + core_name_and_version, + ); + emit_rerun_if_changed() +} + +fn pregenerate_asm_main(tools: &Tools, c_root_dir: &Path, core_name_and_version: &str) { + let pregenerated = c_root_dir.join(PREGENERATED); + fs::create_dir(&pregenerated).unwrap(); + generate_sources_and_preassemble( + tools, + &pregenerated, + AsmTarget::all(), + c_root_dir, + core_name_and_version, + ); +} + +// TODO: We should emit `cargo:rerun-if-changed-env` for the various +// environment variables that affect the build. +fn emit_rerun_if_changed() { + let build_non_rust: &Path = &PathBuf::from("build_non_rust"); + cargo::emit_rerun_if_changed(iter::once(build_non_rust)); + walk_non_root_sources(|path| cargo::emit_rerun_if_changed(iter::once(path))) +} + +fn get_perl_exe() -> PathBuf { + get_command(&cargo::env::PERL_EXECUTABLE, "perl") +} + +fn get_command(var: &'static cargo::env::EnvVar, default: &str) -> PathBuf { + PathBuf::from(cargo::env::var_os(var).unwrap_or_else(|| default.into())) +}