| // 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 AUTHORS DISCLAIM ALL WARRANTIES |
| // WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF |
| // MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHORS 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. |
| |
| // It seems like it would be a good idea to use `log!` for logging, but it |
| // isn't worth having the external dependencies (one for the `log` crate, and |
| // another for the concrete logging implementation). Instead we use `eprintln!` |
| // to log everything to stderr. |
| |
| // In the `pregenerate_asm_main()` case we don't want to access (Cargo) |
| // environment variables at all, so avoid `use std::env` here. |
| |
| use std::{ |
| fs::{self, DirEntry}, |
| path::{Path, PathBuf}, |
| process::Command, |
| time::SystemTime, |
| }; |
| |
| const X86: &str = "x86"; |
| const X86_64: &str = "x86_64"; |
| const AARCH64: &str = "aarch64"; |
| const ARM: &str = "arm"; |
| |
| #[rustfmt::skip] |
| const RING_SRCS: &[(&[&str], &str)] = &[ |
| (&[], "crypto/fipsmodule/aes/aes_nohw.c"), |
| (&[], "crypto/fipsmodule/bn/montgomery.c"), |
| (&[], "crypto/fipsmodule/bn/montgomery_inv.c"), |
| (&[], "crypto/limbs/limbs.c"), |
| (&[], "crypto/mem.c"), |
| (&[], "crypto/poly1305/poly1305.c"), |
| |
| (&[AARCH64, ARM, X86_64, X86], "crypto/crypto.c"), |
| (&[AARCH64, ARM, X86_64, X86], "crypto/curve25519/curve25519.c"), |
| (&[AARCH64, ARM, X86_64, X86], "crypto/fipsmodule/ec/ecp_nistz.c"), |
| (&[AARCH64, ARM, X86_64, X86], "crypto/fipsmodule/ec/ecp_nistz256.c"), |
| (&[AARCH64, ARM, X86_64, X86], "crypto/fipsmodule/ec/gfp_p256.c"), |
| (&[AARCH64, ARM, X86_64, X86], "crypto/fipsmodule/ec/gfp_p384.c"), |
| |
| (&[X86_64, X86], "crypto/cpu-intel.c"), |
| |
| (&[X86], "crypto/fipsmodule/aes/asm/aesni-x86.pl"), |
| (&[X86], "crypto/fipsmodule/aes/asm/vpaes-x86.pl"), |
| (&[X86], "crypto/fipsmodule/bn/asm/x86-mont.pl"), |
| (&[X86], "crypto/chacha/asm/chacha-x86.pl"), |
| (&[X86], "crypto/fipsmodule/ec/asm/ecp_nistz256-x86.pl"), |
| (&[X86], "crypto/fipsmodule/modes/asm/ghash-x86.pl"), |
| |
| (&[X86_64], "crypto/fipsmodule/aes/asm/aesni-x86_64.pl"), |
| (&[X86_64], "crypto/fipsmodule/aes/asm/vpaes-x86_64.pl"), |
| (&[X86_64], "crypto/fipsmodule/bn/asm/x86_64-mont.pl"), |
| (&[X86_64], "crypto/fipsmodule/bn/asm/x86_64-mont5.pl"), |
| (&[X86_64], "crypto/chacha/asm/chacha-x86_64.pl"), |
| (&[X86_64], "crypto/fipsmodule/ec/asm/p256-x86_64-asm.pl"), |
| (&[X86_64], "crypto/fipsmodule/modes/asm/aesni-gcm-x86_64.pl"), |
| (&[X86_64], "crypto/fipsmodule/modes/asm/ghash-x86_64.pl"), |
| (&[X86_64], "crypto/poly1305/poly1305_vec.c"), |
| (&[X86_64], SHA512_X86_64), |
| (&[X86_64], "crypto/cipher_extra/asm/chacha20_poly1305_x86_64.pl"), |
| |
| (&[AARCH64, ARM], "crypto/fipsmodule/aes/asm/aesv8-armx.pl"), |
| (&[AARCH64, ARM], "crypto/fipsmodule/modes/asm/ghashv8-armx.pl"), |
| |
| (&[ARM], "crypto/fipsmodule/aes/asm/bsaes-armv7.pl"), |
| (&[ARM], "crypto/fipsmodule/aes/asm/vpaes-armv7.pl"), |
| (&[ARM], "crypto/fipsmodule/bn/asm/armv4-mont.pl"), |
| (&[ARM], "crypto/chacha/asm/chacha-armv4.pl"), |
| (&[ARM], "crypto/curve25519/asm/x25519-asm-arm.S"), |
| (&[ARM], "crypto/fipsmodule/ec/asm/ecp_nistz256-armv4.pl"), |
| (&[ARM], "crypto/fipsmodule/modes/asm/ghash-armv4.pl"), |
| (&[ARM], "crypto/poly1305/poly1305_arm.c"), |
| (&[ARM], "crypto/poly1305/poly1305_arm_asm.S"), |
| (&[ARM], "crypto/fipsmodule/sha/asm/sha256-armv4.pl"), |
| (&[ARM], "crypto/fipsmodule/sha/asm/sha512-armv4.pl"), |
| |
| (&[AARCH64], "crypto/fipsmodule/aes/asm/vpaes-armv8.pl"), |
| (&[AARCH64], "crypto/fipsmodule/bn/asm/armv8-mont.pl"), |
| (&[AARCH64], "crypto/chacha/asm/chacha-armv8.pl"), |
| (&[AARCH64], "crypto/fipsmodule/ec/asm/ecp_nistz256-armv8.pl"), |
| (&[AARCH64], "crypto/fipsmodule/modes/asm/ghash-neon-armv8.pl"), |
| (&[AARCH64], SHA512_ARMV8), |
| ]; |
| |
| const SHA256_X86_64: &str = "crypto/fipsmodule/sha/asm/sha256-x86_64.pl"; |
| const SHA512_X86_64: &str = "crypto/fipsmodule/sha/asm/sha512-x86_64.pl"; |
| |
| const SHA256_ARMV8: &str = "crypto/fipsmodule/sha/asm/sha256-armv8.pl"; |
| const SHA512_ARMV8: &str = "crypto/fipsmodule/sha/asm/sha512-armv8.pl"; |
| |
| const RING_TEST_SRCS: &[&str] = &[("crypto/constant_time_test.c")]; |
| |
| #[rustfmt::skip] |
| const RING_INCLUDES: &[&str] = |
| &[ |
| "crypto/curve25519/curve25519_tables.h", |
| "crypto/curve25519/internal.h", |
| "crypto/fipsmodule/bn/internal.h", |
| "crypto/fipsmodule/ec/ecp_nistz256_table.inl", |
| "crypto/fipsmodule/ec/ecp_nistz384.inl", |
| "crypto/fipsmodule/ec/ecp_nistz.h", |
| "crypto/fipsmodule/ec/ecp_nistz384.h", |
| "crypto/fipsmodule/ec/ecp_nistz256.h", |
| "crypto/internal.h", |
| "crypto/limbs/limbs.h", |
| "crypto/limbs/limbs.inl", |
| "crypto/poly1305/internal.h", |
| "include/GFp/aes.h", |
| "include/GFp/arm_arch.h", |
| "include/GFp/base.h", |
| "include/GFp/check.h", |
| "include/GFp/cpu.h", |
| "include/GFp/mem.h", |
| "include/GFp/poly1305.h", |
| "include/GFp/type_check.h", |
| "third_party/fiat/curve25519_32.h", |
| "third_party/fiat/curve25519_64.h", |
| ]; |
| |
| #[rustfmt::skip] |
| const RING_PERL_INCLUDES: &[&str] = |
| &["crypto/perlasm/arm-xlate.pl", |
| "crypto/perlasm/x86gas.pl", |
| "crypto/perlasm/x86nasm.pl", |
| "crypto/perlasm/x86asm.pl", |
| "crypto/perlasm/x86_64-xlate.pl"]; |
| |
| const RING_BUILD_FILE: &[&str] = &["build.rs"]; |
| |
| const PREGENERATED: &str = "pregenerated"; |
| |
| fn c_flags(target: &Target) -> &'static [&'static str] { |
| if target.env != MSVC { |
| static NON_MSVC_FLAGS: &[&str] = &[ |
| "-std=c1x", // GCC 4.6 requires "c1x" instead of "c11" |
| "-Wbad-function-cast", |
| "-Wnested-externs", |
| "-Wstrict-prototypes", |
| ]; |
| NON_MSVC_FLAGS |
| } else { |
| &[] |
| } |
| } |
| |
| fn cpp_flags(target: &Target) -> &'static [&'static str] { |
| if target.env != MSVC { |
| static NON_MSVC_FLAGS: &[&str] = &[ |
| "-pedantic", |
| "-pedantic-errors", |
| "-Wall", |
| "-Wextra", |
| "-Wcast-align", |
| "-Wcast-qual", |
| "-Wconversion", |
| "-Wenum-compare", |
| "-Wfloat-equal", |
| "-Wformat=2", |
| "-Winline", |
| "-Winvalid-pch", |
| "-Wmissing-field-initializers", |
| "-Wmissing-include-dirs", |
| "-Wredundant-decls", |
| "-Wshadow", |
| "-Wsign-compare", |
| "-Wsign-conversion", |
| "-Wundef", |
| "-Wuninitialized", |
| "-Wwrite-strings", |
| "-fno-strict-aliasing", |
| "-fvisibility=hidden", |
| ]; |
| NON_MSVC_FLAGS |
| } else { |
| static MSVC_FLAGS: &[&str] = &[ |
| "/GS", // Buffer security checks. |
| "/Gy", // Enable function-level linking. |
| "/EHsc", // C++ exceptions only, only in C++. |
| "/GR-", // Disable RTTI. |
| "/Zc:wchar_t", |
| "/Zc:forScope", |
| "/Zc:inline", |
| "/Zc:rvalueCast", |
| // Warnings. |
| "/sdl", |
| "/Wall", |
| "/wd4127", // C4127: conditional expression is constant |
| "/wd4464", // C4464: relative include path contains '..' |
| "/wd4514", // C4514: <name>: unreferenced inline function has be |
| "/wd4710", // C4710: function not inlined |
| "/wd4711", // C4711: function 'function' selected for inline expansion |
| "/wd4820", // C4820: <struct>: <n> bytes padding added after <name> |
| "/wd5045", /* C5045: Compiler will insert Spectre mitigation for memory load if |
| * /Qspectre switch specified */ |
| ]; |
| MSVC_FLAGS |
| } |
| } |
| |
| const LD_FLAGS: &[&str] = &[]; |
| |
| // None means "any OS" or "any target". The first match in sequence order is |
| // taken. |
| const ASM_TARGETS: &[(&str, Option<&str>, Option<&str>)] = &[ |
| ("x86_64", Some("ios"), Some("macosx")), |
| ("x86_64", Some("macos"), Some("macosx")), |
| ("x86_64", Some(WINDOWS), Some("nasm")), |
| ("x86_64", None, Some("elf")), |
| ("aarch64", Some("ios"), Some("ios64")), |
| ("aarch64", Some("macos"), Some("ios64")), |
| ("aarch64", None, Some("linux64")), |
| ("x86", Some(WINDOWS), Some("win32n")), |
| ("x86", Some("ios"), Some("macosx")), |
| ("x86", None, Some("elf")), |
| ("arm", Some("ios"), Some("ios32")), |
| ("arm", None, Some("linux32")), |
| ("wasm32", None, None), |
| ]; |
| |
| const WINDOWS: &str = "windows"; |
| const MSVC: &str = "msvc"; |
| const MSVC_OBJ_OPT: &str = "/Fo"; |
| const MSVC_OBJ_EXT: &str = "obj"; |
| |
| fn main() { |
| if let Ok(package_name) = std::env::var("CARGO_PKG_NAME") { |
| if package_name == "ring" { |
| ring_build_rs_main(); |
| return; |
| } |
| } |
| |
| pregenerate_asm_main(); |
| } |
| |
| fn ring_build_rs_main() { |
| use std::env; |
| |
| let out_dir = env::var("OUT_DIR").unwrap(); |
| let out_dir = PathBuf::from(out_dir); |
| |
| let arch = env::var("CARGO_CFG_TARGET_ARCH").unwrap(); |
| let os = env::var("CARGO_CFG_TARGET_OS").unwrap(); |
| let env = env::var("CARGO_CFG_TARGET_ENV").unwrap(); |
| let (obj_ext, obj_opt) = if env == MSVC { |
| (MSVC_OBJ_EXT, MSVC_OBJ_OPT) |
| } else { |
| ("o", "-o") |
| }; |
| |
| let is_git = std::fs::metadata(".git").is_ok(); |
| |
| // Published builds are always release builds. |
| let is_debug = is_git && env::var("DEBUG").unwrap() != "false"; |
| |
| let target = Target { |
| arch, |
| os, |
| env, |
| obj_ext, |
| obj_opt, |
| is_git, |
| is_debug, |
| }; |
| let pregenerated = PathBuf::from(env::var("CARGO_MANIFEST_DIR").unwrap()).join(PREGENERATED); |
| |
| build_c_code(&target, pregenerated, &out_dir); |
| check_all_files_tracked() |
| } |
| |
| fn pregenerate_asm_main() { |
| let pregenerated = PathBuf::from(PREGENERATED); |
| std::fs::create_dir(&pregenerated).unwrap(); |
| let pregenerated_tmp = pregenerated.join("tmp"); |
| std::fs::create_dir(&pregenerated_tmp).unwrap(); |
| |
| for &(target_arch, target_os, perlasm_format) in ASM_TARGETS { |
| // For Windows, package pregenerated object files instead of |
| // pregenerated assembly language source files, so that the user |
| // doesn't need to install the assembler. |
| let asm_dir = if target_os == Some(WINDOWS) { |
| &pregenerated_tmp |
| } else { |
| &pregenerated |
| }; |
| |
| if let Some(perlasm_format) = perlasm_format { |
| let perlasm_src_dsts = |
| perlasm_src_dsts(&asm_dir, target_arch, target_os, perlasm_format); |
| perlasm(&perlasm_src_dsts, target_arch, perlasm_format, None); |
| |
| if target_os == Some(WINDOWS) { |
| let srcs = asm_srcs(perlasm_src_dsts); |
| for src in srcs { |
| let obj_path = obj_path(&pregenerated, &src, MSVC_OBJ_EXT); |
| run_command(nasm(&src, target_arch, &obj_path)); |
| } |
| } |
| } |
| } |
| } |
| |
| struct Target { |
| arch: String, |
| os: String, |
| env: String, |
| obj_ext: &'static str, |
| obj_opt: &'static str, |
| is_git: bool, |
| is_debug: bool, |
| } |
| |
| fn build_c_code(target: &Target, pregenerated: PathBuf, out_dir: &Path) { |
| #[cfg(not(feature = "wasm32_c"))] |
| { |
| if &target.arch == "wasm32" { |
| return; |
| } |
| } |
| |
| let includes_modified = RING_INCLUDES |
| .iter() |
| .chain(RING_BUILD_FILE.iter()) |
| .chain(RING_PERL_INCLUDES.iter()) |
| .map(|f| file_modified(Path::new(*f))) |
| .max() |
| .unwrap(); |
| |
| fn is_none_or_equals<T>(opt: Option<T>, other: T) -> bool |
| where |
| T: PartialEq, |
| { |
| if let Some(value) = opt { |
| value == other |
| } else { |
| true |
| } |
| } |
| |
| let (_, _, perlasm_format) = ASM_TARGETS |
| .iter() |
| .find(|entry| { |
| let &(entry_arch, entry_os, _) = *entry; |
| entry_arch == target.arch && is_none_or_equals(entry_os, &target.os) |
| }) |
| .unwrap(); |
| |
| let use_pregenerated = !target.is_git; |
| let warnings_are_errors = target.is_git; |
| |
| let asm_dir = if use_pregenerated { |
| &pregenerated |
| } else { |
| out_dir |
| }; |
| |
| let asm_srcs = if let Some(perlasm_format) = perlasm_format { |
| let perlasm_src_dsts = |
| perlasm_src_dsts(asm_dir, &target.arch, Some(&target.os), perlasm_format); |
| |
| if !use_pregenerated { |
| perlasm( |
| &perlasm_src_dsts[..], |
| &target.arch, |
| perlasm_format, |
| Some(includes_modified), |
| ); |
| } |
| |
| let mut asm_srcs = asm_srcs(perlasm_src_dsts); |
| |
| // For Windows we also pregenerate the object files for non-Git builds so |
| // the user doesn't need to install the assembler. On other platforms we |
| // assume the C compiler also assembles. |
| if use_pregenerated && target.os == WINDOWS { |
| // The pregenerated object files always use ".obj" as the extension, |
| // even when the C/C++ compiler outputs files with the ".o" extension. |
| asm_srcs = asm_srcs |
| .iter() |
| .map(|src| obj_path(&pregenerated, src.as_path(), "obj")) |
| .collect::<Vec<_>>(); |
| } |
| |
| asm_srcs |
| } else { |
| Vec::new() |
| }; |
| |
| let core_srcs = sources_for_arch(&target.arch) |
| .into_iter() |
| .filter(|p| !is_perlasm(&p)) |
| .collect::<Vec<_>>(); |
| |
| let test_srcs = RING_TEST_SRCS.iter().map(PathBuf::from).collect::<Vec<_>>(); |
| |
| let libs = [ |
| ("ring-core", &core_srcs[..], &asm_srcs[..]), |
| ("ring-test", &test_srcs[..], &[]), |
| ]; |
| |
| // XXX: Ideally, ring-test would only be built for `cargo test`, but Cargo |
| // can't do that yet. |
| libs.iter().for_each(|&(lib_name, srcs, additional_srcs)| { |
| build_library( |
| &target, |
| &out_dir, |
| lib_name, |
| srcs, |
| additional_srcs, |
| warnings_are_errors, |
| includes_modified, |
| ) |
| }); |
| |
| println!( |
| "cargo:rustc-link-search=native={}", |
| out_dir.to_str().expect("Invalid path") |
| ); |
| } |
| |
| fn build_library( |
| target: &Target, |
| out_dir: &Path, |
| lib_name: &str, |
| srcs: &[PathBuf], |
| additional_srcs: &[PathBuf], |
| warnings_are_errors: bool, |
| includes_modified: SystemTime, |
| ) { |
| // Compile all the (dirty) source files into object files. |
| let objs = additional_srcs |
| .iter() |
| .chain(srcs.iter()) |
| .filter(|f| &target.env != "msvc" || f.extension().unwrap().to_str().unwrap() != "S") |
| .map(|f| compile(f, target, warnings_are_errors, out_dir, includes_modified)) |
| .collect::<Vec<_>>(); |
| |
| // Rebuild the library if necessary. |
| let lib_path = PathBuf::from(out_dir).join(format!("lib{}.a", lib_name)); |
| |
| if objs |
| .iter() |
| .map(Path::new) |
| .any(|p| need_run(&p, &lib_path, includes_modified)) |
| { |
| let mut c = cc::Build::new(); |
| |
| for f in LD_FLAGS { |
| let _ = c.flag(&f); |
| } |
| match target.os.as_str() { |
| "macos" => { |
| let _ = c.flag("-fPIC"); |
| let _ = c.flag("-Wl,-dead_strip"); |
| } |
| _ => { |
| let _ = c.flag("-Wl,--gc-sections"); |
| } |
| } |
| for o in objs { |
| let _ = c.object(o); |
| } |
| |
| // 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); |
| } |
| |
| fn compile( |
| p: &Path, |
| target: &Target, |
| warnings_are_errors: bool, |
| out_dir: &Path, |
| includes_modified: SystemTime, |
| ) -> String { |
| let ext = p.extension().unwrap().to_str().unwrap(); |
| if ext == "obj" { |
| p.to_str().expect("Invalid path").into() |
| } else { |
| let mut out_path = out_dir.join(p.file_name().unwrap()); |
| assert!(out_path.set_extension(target.obj_ext)); |
| if need_run(&p, &out_path, includes_modified) { |
| let cmd = if target.os != WINDOWS || ext != "asm" { |
| cc(p, ext, target, warnings_are_errors, &out_path) |
| } else { |
| nasm(p, &target.arch, &out_path) |
| }; |
| |
| run_command(cmd); |
| } |
| out_path.to_str().expect("Invalid path").into() |
| } |
| } |
| |
| fn obj_path(out_dir: &Path, src: &Path, obj_ext: &str) -> PathBuf { |
| let mut out_path = out_dir.join(src.file_name().unwrap()); |
| assert!(out_path.set_extension(obj_ext)); |
| out_path |
| } |
| |
| fn cc( |
| file: &Path, |
| ext: &str, |
| target: &Target, |
| warnings_are_errors: bool, |
| out_dir: &Path, |
| ) -> Command { |
| let is_musl = target.env.starts_with("musl"); |
| |
| let mut c = cc::Build::new(); |
| let _ = c.include("include"); |
| match ext { |
| "c" => { |
| for f in c_flags(target) { |
| let _ = c.flag(f); |
| } |
| } |
| "S" => (), |
| e => panic!("Unsupported file extension: {:?}", e), |
| }; |
| for f in cpp_flags(target) { |
| let _ = c.flag(&f); |
| } |
| if target.os != "none" |
| && target.os != "redox" |
| && target.os != "windows" |
| && target.arch != "wasm32" |
| { |
| let _ = c.flag("-fstack-protector"); |
| } |
| |
| match (target.os.as_str(), target.env.as_str()) { |
| // ``-gfull`` is required for Darwin's |-dead_strip|. |
| ("macos", _) => { |
| let _ = c.flag("-gfull"); |
| } |
| (_, "msvc") => (), |
| _ => { |
| let _ = c.flag("-g3"); |
| } |
| }; |
| if !target.is_debug { |
| let _ = c.define("NDEBUG", None); |
| } |
| |
| if &target.env == "msvc" { |
| if std::env::var("OPT_LEVEL").unwrap() == "0" { |
| let _ = c.flag("/Od"); // Disable optimization for debug builds. |
| // run-time checking: (s)tack frame, (u)ninitialized variables |
| let _ = c.flag("/RTCsu"); |
| } else { |
| let _ = c.flag("/Ox"); // Enable full optimization. |
| } |
| } |
| |
| // Allow cross-compiling without a target sysroot for these targets. |
| // |
| // poly1305_vec.c requires <emmintrin.h> which requires <stdlib.h>. |
| if (target.arch == "wasm32" && target.os == "unknown") |
| || (target.os == "linux" && is_musl && target.arch != "x86_64") |
| { |
| if let Ok(compiler) = c.try_get_compiler() { |
| // 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("GFp_NOSTDLIBINC", "1"); |
| } |
| } |
| } |
| |
| if warnings_are_errors { |
| let flag = if &target.env != "msvc" { |
| "-Werror" |
| } else { |
| "/WX" |
| }; |
| let _ = c.flag(flag); |
| } |
| if is_musl { |
| // Some platforms enable _FORTIFY_SOURCE by default, but musl |
| // libc doesn't support it yet. See |
| // http://wiki.musl-libc.org/wiki/Future_Ideas#Fortify |
| // http://www.openwall.com/lists/musl/2015/02/04/3 |
| // http://www.openwall.com/lists/musl/2015/06/17/1 |
| let _ = c.flag("-U_FORTIFY_SOURCE"); |
| } |
| |
| let mut c = c.get_compiler().to_command(); |
| let _ = c |
| .arg("-c") |
| .arg(format!( |
| "{}{}", |
| target.obj_opt, |
| out_dir.to_str().expect("Invalid path") |
| )) |
| .arg(file); |
| c |
| } |
| |
| fn nasm(file: &Path, arch: &str, out_file: &Path) -> Command { |
| let oformat = match arch { |
| "x86_64" => ("win64"), |
| "x86" => ("win32"), |
| _ => panic!("unsupported arch: {}", arch), |
| }; |
| let mut c = Command::new("./target/tools/nasm"); |
| let _ = c |
| .arg("-o") |
| .arg(out_file.to_str().expect("Invalid path")) |
| .arg("-f") |
| .arg(oformat) |
| .arg("-Xgnu") |
| .arg("-gcv8") |
| .arg(file); |
| c |
| } |
| |
| fn run_command_with_args<S>(command_name: S, args: &[String]) |
| where |
| S: AsRef<std::ffi::OsStr> + Copy, |
| { |
| let mut cmd = Command::new(command_name); |
| let _ = cmd.args(args); |
| run_command(cmd) |
| } |
| |
| fn run_command(mut cmd: Command) { |
| eprintln!("running {:?}", cmd); |
| let status = cmd.status().unwrap_or_else(|e| { |
| panic!("failed to execute [{:?}]: {}", cmd, e); |
| }); |
| if !status.success() { |
| panic!("execution failed"); |
| } |
| } |
| |
| fn sources_for_arch(arch: &str) -> Vec<PathBuf> { |
| RING_SRCS |
| .iter() |
| .filter(|&&(archs, _)| archs.is_empty() || archs.contains(&arch)) |
| .map(|&(_, p)| PathBuf::from(p)) |
| .collect::<Vec<_>>() |
| } |
| |
| fn perlasm_src_dsts( |
| out_dir: &Path, |
| arch: &str, |
| os: Option<&str>, |
| perlasm_format: &str, |
| ) -> Vec<(PathBuf, PathBuf)> { |
| let srcs = sources_for_arch(arch); |
| let mut src_dsts = srcs |
| .iter() |
| .filter(|p| is_perlasm(p)) |
| .map(|src| (src.clone(), asm_path(out_dir, src, os, perlasm_format))) |
| .collect::<Vec<_>>(); |
| |
| // Some PerlAsm source files need to be run multiple times with different |
| // output paths. |
| { |
| // Appease the borrow checker. |
| let mut maybe_synthesize = |concrete, synthesized| { |
| let concrete_path = PathBuf::from(concrete); |
| if srcs.contains(&concrete_path) { |
| let synthesized_path = PathBuf::from(synthesized); |
| src_dsts.push(( |
| concrete_path, |
| asm_path(out_dir, &synthesized_path, os, perlasm_format), |
| )) |
| } |
| }; |
| maybe_synthesize(SHA512_X86_64, SHA256_X86_64); |
| maybe_synthesize(SHA512_ARMV8, SHA256_ARMV8); |
| } |
| |
| src_dsts |
| } |
| |
| fn asm_srcs(perlasm_src_dsts: Vec<(PathBuf, PathBuf)>) -> Vec<PathBuf> { |
| perlasm_src_dsts |
| .into_iter() |
| .map(|(_src, dst)| dst) |
| .collect::<Vec<_>>() |
| } |
| |
| fn is_perlasm(path: &PathBuf) -> bool { |
| path.extension().unwrap().to_str().unwrap() == "pl" |
| } |
| |
| fn asm_path(out_dir: &Path, src: &Path, os: Option<&str>, perlasm_format: &str) -> PathBuf { |
| let src_stem = src.file_stem().expect("source file without basename"); |
| |
| let dst_stem = src_stem.to_str().unwrap(); |
| let dst_extension = if os == Some("windows") { "asm" } else { "S" }; |
| let dst_filename = format!("{}-{}.{}", dst_stem, perlasm_format, dst_extension); |
| out_dir.join(dst_filename) |
| } |
| |
| fn perlasm( |
| src_dst: &[(PathBuf, PathBuf)], |
| arch: &str, |
| perlasm_format: &str, |
| includes_modified: Option<SystemTime>, |
| ) { |
| for (src, dst) in src_dst { |
| if let Some(includes_modified) = includes_modified { |
| if !need_run(src, dst, includes_modified) { |
| continue; |
| } |
| } |
| |
| let mut args = Vec::<String>::new(); |
| args.push(src.to_string_lossy().into_owned()); |
| args.push(perlasm_format.to_owned()); |
| if arch == "x86" { |
| args.push("-fPIC".into()); |
| args.push("-DOPENSSL_IA32_SSE2".into()); |
| } |
| // Work around PerlAsm issue for ARM and AAarch64 targets by replacing |
| // back slashes with forward slashes. |
| let dst = dst |
| .to_str() |
| .expect("Could not convert path") |
| .replace("\\", "/"); |
| args.push(dst); |
| run_command_with_args(&get_command("PERL_EXECUTABLE", "perl"), &args); |
| } |
| } |
| |
| fn need_run(source: &Path, target: &Path, includes_modified: SystemTime) -> bool { |
| let s_modified = file_modified(source); |
| if let Ok(target_metadata) = std::fs::metadata(target) { |
| let target_modified = target_metadata.modified().unwrap(); |
| s_modified >= target_modified || includes_modified >= target_modified |
| } else { |
| // On error fetching metadata for the target file, assume the target |
| // doesn't exist. |
| true |
| } |
| } |
| |
| fn file_modified(path: &Path) -> SystemTime { |
| let path = Path::new(path); |
| let path_as_str = format!("{:?}", path); |
| std::fs::metadata(path) |
| .expect(&path_as_str) |
| .modified() |
| .expect("nah") |
| } |
| |
| fn get_command(var: &str, default: &str) -> String { |
| std::env::var(var).unwrap_or_else(|_| default.into()) |
| } |
| |
| fn check_all_files_tracked() { |
| for path in &["crypto", "include", "third_party/fiat"] { |
| walk_dir(&PathBuf::from(path), &is_tracked); |
| } |
| } |
| |
| fn is_tracked(file: &DirEntry) { |
| let p = file.path(); |
| let cmp = |f| p == PathBuf::from(f); |
| let tracked = match p.extension().and_then(|p| p.to_str()) { |
| Some("h") | Some("inl") => RING_INCLUDES.iter().any(cmp), |
| Some("c") | Some("S") | Some("asm") => { |
| RING_SRCS.iter().any(|(_, f)| cmp(f)) || RING_TEST_SRCS.iter().any(cmp) |
| } |
| Some("pl") => RING_SRCS.iter().any(|(_, f)| cmp(f)) || RING_PERL_INCLUDES.iter().any(cmp), |
| _ => true, |
| }; |
| if !tracked { |
| panic!("{:?} is not tracked in build.rs", p); |
| } |
| } |
| |
| fn walk_dir<F>(dir: &Path, cb: &F) |
| where |
| F: Fn(&DirEntry), |
| { |
| if dir.is_dir() { |
| for entry in fs::read_dir(dir).unwrap() { |
| let entry = entry.unwrap(); |
| let path = entry.path(); |
| if path.is_dir() { |
| walk_dir(&path, cb); |
| } else { |
| cb(&entry); |
| } |
| } |
| } |
| } |