//! The revive LLVM `wasm32_unknown_emscripten` builder. //! //! Cross-compiling LLVM for Emscripten requires llvm-tblgen, clang-tblgen and llvm-config. use std::{collections::HashSet, path::Path, process::Command}; /// The building sequence. #[allow(clippy::too_many_arguments)] pub fn build( build_type: crate::BuildType, targets: HashSet, llvm_projects: HashSet, enable_rtti: bool, default_target: Option, enable_tests: bool, enable_coverage: bool, extra_args: &[String], ccache_variant: Option, enable_assertions: bool, sanitizer: Option, enable_valgrind: bool, ) -> anyhow::Result<()> { crate::utils::check_presence("cmake")?; crate::utils::check_presence("ninja")?; crate::utils::check_presence("emsdk")?; crate::utils::check_presence("clang")?; crate::utils::check_presence("clang++")?; if cfg!(target_os = "linux") { crate::utils::check_presence("lld")?; } let llvm_module_llvm = crate::LLVMPath::llvm_module_llvm()?; let llvm_host_module_llvm = crate::LLVMPath::llvm_host_module_llvm()?; let llvm_build_host = crate::LLVMPath::llvm_build_host()?; let llvm_target_host = crate::LLVMPath::llvm_target_host()?; let llvm_build_final = crate::LLVMPath::llvm_build_final()?; let llvm_target_final = crate::LLVMPath::llvm_target_final()?; build_host( llvm_host_module_llvm.as_path(), llvm_build_host.as_path(), llvm_target_host.as_path(), ccache_variant, )?; build_target( build_type, targets, llvm_projects, enable_rtti, default_target, llvm_module_llvm.as_path(), llvm_build_final.as_path(), llvm_target_final.as_path(), llvm_target_host.as_path(), enable_tests, enable_coverage, extra_args, ccache_variant, enable_assertions, sanitizer, enable_valgrind, )?; Ok(()) } /// The host toolchain building sequence. fn build_host( source_directory: &Path, build_directory: &Path, target_directory: &Path, ccache_variant: Option, ) -> anyhow::Result<()> { log::info!("building the LLVM Emscripten host utilities"); crate::utils::command( Command::new("cmake") .args([ "-S", source_directory.to_string_lossy().as_ref(), "-B", build_directory.to_string_lossy().as_ref(), "-G", "Ninja", "-DLINKER_SUPPORTS_COLOR_DIAGNOSTICS=0", &format!( "-DCMAKE_INSTALL_PREFIX='{}'", target_directory.to_string_lossy() ), "-DLLVM_BUILD_SHARED_LIBS='Off'", "-DCMAKE_BUILD_TYPE='Release'", &format!( "-DLLVM_TARGETS_TO_BUILD='WebAssembly;{}'", crate::Platform::PolkaVM ), "-DLLVM_ENABLE_PROJECTS='clang;lld'", ]) .args(crate::platforms::shared::SHARED_BUILD_OPTS) .args(crate::platforms::shared::SHARED_BUILD_OPTS_NOT_MUSL) .args(crate::platforms::shared::shared_build_opts_ccache( ccache_variant, )), "LLVM host building cmake config", )?; crate::utils::ninja(build_directory)?; Ok(()) } /// The target toolchain building sequence. #[allow(clippy::too_many_arguments)] fn build_target( build_type: crate::BuildType, targets: HashSet, llvm_projects: HashSet, enable_rtti: bool, default_target: Option, source_directory: &Path, build_directory: &Path, target_directory: &Path, host_target_directory: &Path, enable_tests: bool, enable_coverage: bool, extra_args: &[String], ccache_variant: Option, enable_assertions: bool, sanitizer: Option, enable_valgrind: bool, ) -> anyhow::Result<()> { let mut llvm_tblgen_path = host_target_directory.to_path_buf(); llvm_tblgen_path.push("bin/llvm-tblgen"); let mut clang_tblgen_path = host_target_directory.to_path_buf(); clang_tblgen_path.push("bin/clang-tblgen"); crate::utils::command( Command::new("emcmake") .env("EMCC_DEBUG", "2") .env("CXXFLAGS", "-Dwait4=__syscall_wait4") .env("LDFLAGS", "-lnodefs.js -s NO_INVOKE_RUN=1 -s EXIT_RUNTIME=1 -s ALLOW_MEMORY_GROWTH=1 -s EXPORTED_RUNTIME_METHODS=FS,callMain,NODEFS -s MODULARIZE=1 -s WASM_BIGINT=1 -s ALLOW_TABLE_GROWTH=1 -s NODEJS_CATCH_EXIT=0 -sDYNAMIC_EXECUTION=0") .arg("cmake") .args([ "-S", source_directory.to_string_lossy().as_ref(), "-B", build_directory.to_string_lossy().as_ref(), "-G", "Ninja", "-DLINKER_SUPPORTS_COLOR_DIAGNOSTICS=0", "-DCMAKE_BUILD_WITH_INSTALL_RPATH=1", // Enable thin LTO but emscripten has various issues with it. // FIXME: https://github.com/paritytech/revive/issues/148 //"-DLLVM_ENABLE_LTO='Thin'", //"-DCMAKE_EXE_LINKER_FLAGS='-Wl,-u,htons -Wl,-u,htonl -Wl,-u,fileno -Wl,-u,ntohs'", &format!( "-DCMAKE_INSTALL_PREFIX='{}'", target_directory.to_string_lossy() ), &format!("-DCMAKE_BUILD_TYPE='{build_type}'"), &format!( "-DLLVM_TARGETS_TO_BUILD='{}'", targets .into_iter() .map(|platform| platform.to_string()) .collect::>() .join(";") ), &format!( "-DLLVM_ENABLE_PROJECTS='{}'", llvm_projects .into_iter() .map(|project| project.to_string()) .collect::>() .join(";") ), "-DLLVM_BUILD_SHARED_LIBS='Off'", "-DLLVM_ENABLE_DUMP='Off'", "-DLLVM_ENABLE_EXPENSIVE_CHECKS='Off'", "-DLLVM_ENABLE_BACKTRACES='Off'", "-DLLVM_ENABLE_BACKTRACES='Off'", "-DLLVM_ENABLE_THREADS='Off'", "-DLLVM_BUILD_TOOLS='Off'", &format!("-DLLVM_TABLEGEN='{}'", llvm_tblgen_path.to_string_lossy()), &format!("-DCLANG_TABLEGEN='{}'", clang_tblgen_path.to_string_lossy()), ]) .args(crate::platforms::shared::shared_build_opts_default_target( default_target, )) .args(crate::platforms::shared::SHARED_BUILD_OPTS) .args(crate::platforms::shared::SHARED_BUILD_OPTS_NOT_MUSL) .args(crate::platforms::shared::shared_build_opts_werror(crate::target_env::TargetEnv::Emscripten)) .args(crate::platforms::shared::shared_build_opts_tests( enable_tests, )) .args(crate::platforms::shared::shared_build_opts_coverage( enable_coverage, )) .args(extra_args) .args(crate::platforms::shared::shared_build_opts_ccache( ccache_variant, )) .args(crate::platforms::shared::shared_build_opts_assertions( enable_assertions, )) .args(crate::platforms::shared::shared_build_opts_rtti( enable_rtti, )) .args(crate::platforms::shared::shared_build_opts_sanitizers( sanitizer, )) .args(crate::platforms::shared::shared_build_opts_valgrind( enable_valgrind, )), "LLVM target building cmake", )?; crate::utils::ninja(build_directory)?; Ok(()) }