aws-lc-sys/builder/cmake_builder.rs (414 lines of code) (raw):

// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 OR ISC use crate::cc_builder::CcBuilder; use crate::OutputLib::{Crypto, RustWrapper, Ssl}; use crate::{ allow_prebuilt_nasm, cargo_env, effective_target, emit_warning, execute_command, get_crate_cflags, is_crt_static, is_no_asm, is_no_pregenerated_src, optional_env, optional_env_optional_crate_target, set_env, set_env_for_target, target_arch, target_env, target_os, test_nasm_command, use_prebuilt_nasm, OutputLibType, }; use std::env; use std::ffi::OsString; use std::path::PathBuf; pub(crate) struct CmakeBuilder { manifest_dir: PathBuf, out_dir: PathBuf, build_prefix: Option<String>, output_lib_type: OutputLibType, } fn test_clang_cl_command() -> bool { execute_command("clang-cl".as_ref(), &["--version".as_ref()]).status } fn find_cmake_command() -> Option<OsString> { if let Some(cmake) = optional_env_optional_crate_target("CMAKE") { emit_warning(&format!( "CMAKE environment variable set: {}", cmake.clone() )); if execute_command(cmake.as_ref(), &["--version".as_ref()]).status { Some(cmake.into()) } else { None } } else if execute_command("cmake3".as_ref(), &["--version".as_ref()]).status { Some("cmake3".into()) } else if execute_command("cmake".as_ref(), &["--version".as_ref()]).status { Some("cmake".into()) } else { None } } impl CmakeBuilder { pub(crate) fn new( manifest_dir: PathBuf, out_dir: PathBuf, build_prefix: Option<String>, output_lib_type: OutputLibType, ) -> Self { Self { manifest_dir, out_dir, build_prefix, output_lib_type, } } fn artifact_output_dir(&self) -> PathBuf { self.out_dir.join("build").join("artifacts") } fn get_cmake_config(&self) -> cmake::Config { cmake::Config::new(&self.manifest_dir) } fn apply_universal_build_options<'a>( &self, cmake_cfg: &'a mut cmake::Config, ) -> &'a cmake::Config { // Use the compiler options identified by CcBuilder let cc_builder = CcBuilder::new( self.manifest_dir.clone(), self.out_dir.clone(), self.build_prefix.clone(), self.output_lib_type, ); let cc_build = cc::Build::new(); let (is_like_msvc, build_options) = cc_builder.collect_universal_build_options(&cc_build); for option in &build_options { option.apply_cmake(cmake_cfg, is_like_msvc); } cmake_cfg } #[allow(clippy::too_many_lines)] fn prepare_cmake_build(&self) -> cmake::Config { let mut cmake_cfg = self.get_cmake_config(); if let Some(generator) = optional_env_optional_crate_target("CMAKE_GENERATOR") { set_env("CMAKE_GENERATOR", generator); } if OutputLibType::default() == OutputLibType::Dynamic { cmake_cfg.define("BUILD_SHARED_LIBS", "1"); } else { cmake_cfg.define("BUILD_SHARED_LIBS", "0"); } if let Some(prefix) = &self.build_prefix { cmake_cfg.define("BORINGSSL_PREFIX", format!("{prefix}_")); let include_path = self.manifest_dir.join("generated-include"); cmake_cfg.define( "BORINGSSL_PREFIX_HEADERS", include_path.display().to_string(), ); } // Build flags that minimize our crate size. cmake_cfg.define("BUILD_TESTING", "OFF"); cmake_cfg.define("BUILD_TOOL", "OFF"); if cfg!(feature = "ssl") { cmake_cfg.define("BUILD_LIBSSL", "ON"); } else { cmake_cfg.define("BUILD_LIBSSL", "OFF"); } if is_no_pregenerated_src() { // Go and Perl will be required. cmake_cfg.define("DISABLE_PERL", "OFF"); cmake_cfg.define("DISABLE_GO", "OFF"); } else { // Build flags that minimize our dependencies. cmake_cfg.define("DISABLE_PERL", "ON"); cmake_cfg.define("DISABLE_GO", "ON"); } if is_no_asm() { let opt_level = cargo_env("OPT_LEVEL"); if opt_level == "0" { cmake_cfg.define("OPENSSL_NO_ASM", "1"); } else { panic!("AWS_LC_SYS_NO_ASM only allowed for debug builds!") } } if cfg!(feature = "asan") { set_env_for_target("CC", "clang"); set_env_for_target("CXX", "clang++"); cmake_cfg.define("ASAN", "1"); } let cflags = get_crate_cflags(); if !cflags.is_empty() { set_env_for_target("CFLAGS", cflags); } if target_env() == "ohos" { Self::configure_open_harmony(&mut cmake_cfg); return cmake_cfg; } // cmake-rs has logic that strips Optimization/Debug options that are passed via CFLAGS: // https://github.com/rust-lang/cmake-rs/issues/240 // This breaks build configurations that generate warnings when optimizations // are disabled. Self::preserve_cflag_optimization_flags(&mut cmake_cfg); // Allow environment to specify CMake toolchain. if let Some(toolchain) = optional_env_optional_crate_target("CMAKE_TOOLCHAIN_FILE") { set_env_for_target("CMAKE_TOOLCHAIN_FILE", toolchain); return cmake_cfg; } // We only consider compiler CFLAGS when no cmake toolchain is set self.apply_universal_build_options(&mut cmake_cfg); // See issue: https://github.com/aws/aws-lc-rs/issues/453 if target_os() == "windows" { self.configure_windows(&mut cmake_cfg); } // If the build environment vendor is Apple #[cfg(target_vendor = "apple")] { if target_arch() == "aarch64" { cmake_cfg.define("CMAKE_OSX_ARCHITECTURES", "arm64"); cmake_cfg.define("CMAKE_SYSTEM_PROCESSOR", "arm64"); } if target_arch() == "x86_64" { cmake_cfg.define("CMAKE_OSX_ARCHITECTURES", "x86_64"); cmake_cfg.define("CMAKE_SYSTEM_PROCESSOR", "x86_64"); } if target_os().trim() == "ios" { cmake_cfg.define("CMAKE_SYSTEM_NAME", "iOS"); if effective_target().ends_with("-ios-sim") || target_arch() == "x86_64" { cmake_cfg.define("CMAKE_OSX_SYSROOT", "iphonesimulator"); } else { cmake_cfg.define("CMAKE_OSX_SYSROOT", "iphoneos"); } cmake_cfg.define("CMAKE_THREAD_LIBS_INIT", "-lpthread"); } if target_os().trim() == "macos" { cmake_cfg.define("CMAKE_SYSTEM_NAME", "Darwin"); cmake_cfg.define("CMAKE_OSX_SYSROOT", "macosx"); } } if target_os() == "android" { self.configure_android(&mut cmake_cfg); } cmake_cfg } fn preserve_cflag_optimization_flags(cmake_cfg: &mut cmake::Config) { if let Ok(cflags) = env::var("CFLAGS") { let split = cflags.split_whitespace(); for arg in split { if arg.starts_with("-O") || arg.starts_with("/O") { emit_warning(&format!("Preserving optimization flag: {arg}")); cmake_cfg.cflag(arg); } } } } #[allow(clippy::unused_self)] fn configure_android(&self, _cmake_cfg: &mut cmake::Config) { // If we leave CMAKE_SYSTEM_PROCESSOR unset, then cmake-rs should handle properly setting // CMAKE_SYSTEM_NAME and CMAKE_SYSTEM_PROCESSOR: // https://github.com/rust-lang/cmake-rs/blob/b689783b5448966e810d515c798465f2e0ab56fd/src/lib.rs#L450-L499 // Log relevant environment variables. if let Some(value) = optional_env_optional_crate_target("ANDROID_NDK_ROOT") { set_env("ANDROID_NDK_ROOT", value); } else { emit_warning("ANDROID_NDK_ROOT not set."); } if let Some(value) = optional_env_optional_crate_target("ANDROID_NDK") { set_env("ANDROID_NDK", value); } else { emit_warning("ANDROID_NDK not set."); } if let Some(value) = optional_env_optional_crate_target("ANDROID_STANDALONE_TOOLCHAIN") { set_env("ANDROID_STANDALONE_TOOLCHAIN", value); } else { emit_warning("ANDROID_STANDALONE_TOOLCHAIN not set."); } } fn configure_windows(&self, cmake_cfg: &mut cmake::Config) { match (target_env().as_str(), target_arch().as_str()) { ("msvc", "aarch64") => { // If CMAKE_GENERATOR is either not set or not set to "Ninja" let cmake_generator = optional_env("CMAKE_GENERATOR"); if cmake_generator.is_none() || cmake_generator.unwrap().to_lowercase() != "ninja" { // The following is not supported by the Ninja generator cmake_cfg.generator_toolset(format!( "ClangCL{}", if cfg!(target_arch = "x86_64") { ",host=x64" } else { "" } )); cmake_cfg.define("CMAKE_GENERATOR_PLATFORM", "ARM64"); } cmake_cfg.static_crt(is_crt_static()); cmake_cfg.define("CMAKE_SYSTEM_NAME", "Windows"); cmake_cfg.define("CMAKE_SYSTEM_PROCESSOR", "ARM64"); } ("msvc", "x86") => { cmake_cfg.static_crt(is_crt_static()); cmake_cfg.define("CMAKE_SYSTEM_NAME", ""); cmake_cfg.define("CMAKE_SYSTEM_PROCESSOR", ""); } ("msvc", _) => { cmake_cfg.static_crt(is_crt_static()); } ("gnu", "x86") => { cmake_cfg.define("CMAKE_SYSTEM_NAME", "Windows"); cmake_cfg.define("CMAKE_SYSTEM_PROCESSOR", "x86"); } _ => {} } if use_prebuilt_nasm() { emit_warning("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"); emit_warning("!!! Using pre-built NASM binaries !!!"); emit_warning("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"); let script_name = if cfg!(target_os = "windows") { "prebuilt-nasm.bat" } else { "prebuilt-nasm.sh" }; let script_path = self .manifest_dir .join("builder") .join(script_name) .display() .to_string(); let script_path = script_path.replace('\\', "/"); cmake_cfg.define("CMAKE_ASM_NASM_COMPILER", script_path.as_str()); // Without the following definition, the build fails with a message similar to the one // reported here: https://gitlab.kitware.com/cmake/cmake/-/issues/19453 // The variables below were found in the associated fix: // https://gitlab.kitware.com/cmake/cmake/-/merge_requests/4257/diffs cmake_cfg.define( "CMAKE_ASM_NASM_COMPILE_OPTIONS_MSVC_RUNTIME_LIBRARY_MultiThreaded", "", ); cmake_cfg.define( "CMAKE_ASM_NASM_COMPILE_OPTIONS_MSVC_RUNTIME_LIBRARY_MultiThreadedDLL", "", ); cmake_cfg.define( "CMAKE_ASM_NASM_COMPILE_OPTIONS_MSVC_RUNTIME_LIBRARY_MultiThreadedDebug", "", ); cmake_cfg.define( "CMAKE_ASM_NASM_COMPILE_OPTIONS_MSVC_RUNTIME_LIBRARY_MultiThreadedDebugDLL", "", ); cmake_cfg.define( "CMAKE_ASM_NASM_COMPILE_OPTIONS_MSVC_DEBUG_INFORMATION_FORMAT_ProgramDatabase", "", ); } } fn configure_open_harmony(cmake_cfg: &mut cmake::Config) { let mut cflags = vec!["-Wno-unused-command-line-argument"]; let mut asmflags = vec![]; // If a toolchain is not specified by the environment if optional_env_optional_crate_target("CMAKE_TOOLCHAIN_FILE").is_none() { if let Ok(ndk) = env::var("OHOS_NDK_HOME") { set_env_for_target( "CMAKE_TOOLCHAIN_FILE", format!("{ndk}/native/build/cmake/ohos.toolchain.cmake"), ); } else if let Ok(sdk) = env::var("OHOS_SDK_NATIVE") { set_env_for_target( "CMAKE_TOOLCHAIN_FILE", format!("{sdk}/build/cmake/ohos.toolchain.cmake"), ); } else { emit_warning( "Neither OHOS_NDK_HOME nor OHOS_SDK_NATIVE are set! No toolchain found.", ); } } match effective_target().as_str() { "aarch64-unknown-linux-ohos" => { cmake_cfg.define("OHOS_ARCH", "arm64-v8a"); } "armv7-unknown-linux-ohos" => { const ARM7_FLAGS: [&str; 6] = [ "-march=armv7-a", "-mfloat-abi=softfp", "-mtune=generic-armv7-a", "-mthumb", "-mfpu=neon", "-DHAVE_NEON", ]; cflags.extend(ARM7_FLAGS); asmflags.extend(ARM7_FLAGS); cmake_cfg.define("OHOS_ARCH", "armeabi-v7a"); } "x86_64-unknown-linux-ohos" => { const X86_64_FLAGS: [&str; 3] = ["-msse4.1", "-DHAVE_NEON_X86", "-DHAVE_NEON"]; cflags.extend(X86_64_FLAGS); asmflags.extend(X86_64_FLAGS); cmake_cfg.define("OHOS_ARCH", "x86_64"); } ohos_target => { emit_warning(format!("Target: {ohos_target} is not support yet!").as_str()); } } cmake_cfg .cflag(cflags.join(" ").as_str()) .cxxflag(cflags.join(" ").as_str()) .asmflag(asmflags.join(" ").as_str()); } fn build_rust_wrapper(&self) -> PathBuf { self.prepare_cmake_build() .configure_arg("--no-warn-unused-cli") .build() } } impl crate::Builder for CmakeBuilder { fn check_dependencies(&self) -> Result<(), String> { let mut missing_dependency = false; if target_os() == "windows" && target_arch() == "x86_64" { if is_no_asm() && Some(true) == allow_prebuilt_nasm() { eprintln!( "Build environment has both `AWS_LC_SYS_PREBUILT_NASM` and `AWS_LC_SYS_NO_ASM` set.\ Please remove one of these environment variables. See User Guide: https://aws.github.io/aws-lc-rs/index.html" ); } if !is_no_asm() && !test_nasm_command() && !use_prebuilt_nasm() { eprintln!( "Consider installing NASM or setting `AWS_LC_SYS_PREBUILT_NASM` in the build environment.\ See User Guide: https://aws.github.io/aws-lc-rs/index.html" ); eprintln!("Missing dependency: nasm"); missing_dependency = true; } if target_arch() == "aarch64" && target_env() == "msvc" && !test_clang_cl_command() { eprintln!("Missing dependency: clang-cl"); missing_dependency = true; } } if let Some(cmake_cmd) = find_cmake_command() { env::set_var("CMAKE", cmake_cmd); } else { eprintln!("Missing dependency: cmake"); missing_dependency = true; } if missing_dependency { return Err("Required build dependency is missing. Halting build.".to_owned()); } Ok(()) } fn build(&self) -> Result<(), String> { self.build_rust_wrapper(); println!( "cargo:rustc-link-search=native={}", self.artifact_output_dir().display() ); println!( "cargo:rustc-link-lib={}={}", self.output_lib_type.rust_lib_type(), Crypto.libname(&self.build_prefix) ); if cfg!(feature = "ssl") { println!( "cargo:rustc-link-lib={}={}", self.output_lib_type.rust_lib_type(), Ssl.libname(&self.build_prefix) ); } println!( "cargo:rustc-link-lib={}={}", self.output_lib_type.rust_lib_type(), RustWrapper.libname(&self.build_prefix) ); Ok(()) } fn name(&self) -> &'static str { "CMake" } }