From c8e304244bc841b392684dbd5eba33f6dda04ea7 Mon Sep 17 00:00:00 2001 From: Bas Zalmstra Date: Fri, 1 Nov 2019 15:13:15 +0100 Subject: [PATCH 1/2] feat: include liblld instead of spawning as process --- .github/actions/install-llvm/dist/index.js | 3 +- .github/actions/install-llvm/index.js | 4 +- README.md | 4 +- crates/mun_codegen/Cargo.toml | 3 +- crates/mun_codegen/build.rs | 211 ----------- crates/mun_codegen/src/code_gen.rs | 20 +- crates/mun_codegen/src/code_gen/linker.rs | 144 +++---- crates/mun_compiler/src/lib.rs | 1 - crates/mun_lld/Cargo.toml | 20 + crates/mun_lld/build.rs | 415 +++++++++++++++++++++ crates/mun_lld/src/lib.rs | 82 ++++ crates/mun_lld/wrapper/lld-c.cpp | 80 ++++ 12 files changed, 660 insertions(+), 327 deletions(-) delete mode 100644 crates/mun_codegen/build.rs create mode 100644 crates/mun_lld/Cargo.toml create mode 100644 crates/mun_lld/build.rs create mode 100644 crates/mun_lld/src/lib.rs create mode 100644 crates/mun_lld/wrapper/lld-c.cpp diff --git a/.github/actions/install-llvm/dist/index.js b/.github/actions/install-llvm/dist/index.js index f78fcc268..f99c17dce 100644 --- a/.github/actions/install-llvm/dist/index.js +++ b/.github/actions/install-llvm/dist/index.js @@ -975,7 +975,7 @@ async function execute(cmd) { (async () => { try { if(isLinux) { - await exec.exec("sudo apt install llvm-7 llvm-7-* lld-7"); + await exec.exec("sudo apt install llvm-7 llvm-7-* lld-7 liblld-7*"); } else if(isMacOS) { await exec.exec("brew install llvm@7") let llvmPath = await execute("brew --prefix llvm@7"); @@ -997,6 +997,7 @@ async function execute(cmd) { } })(); + /***/ }), /***/ 129: diff --git a/.github/actions/install-llvm/index.js b/.github/actions/install-llvm/index.js index 2a12534fa..21edcf51d 100644 --- a/.github/actions/install-llvm/index.js +++ b/.github/actions/install-llvm/index.js @@ -30,7 +30,7 @@ export async function execute(cmd) { (async () => { try { if(isLinux) { - await exec.exec("sudo apt install llvm-7 llvm-7-* lld-7"); + await exec.exec("sudo apt install llvm-7 llvm-7-* lld-7 liblld-7*"); } else if(isMacOS) { await exec.exec("brew install llvm@7") let llvmPath = await execute("brew --prefix llvm@7"); @@ -50,4 +50,4 @@ export async function execute(cmd) { } catch(error) { core.setFailed(error.message); } -})(); \ No newline at end of file +})(); diff --git a/README.md b/README.md index 87f04584b..bf611b4be 100644 --- a/README.md +++ b/README.md @@ -83,12 +83,12 @@ rustup](https://www.rust-lang.org/tools/install). Mun targets LLVM 7.1.0. Installing LLVM is platform dependant and as such can be a pain. The following steps are how we install LLVM on [our CI -runners](https://github.com/mun-lang/mun/blob/master/ci/azure-install-llvm.yml): +runners](.github/actions/install-llvm/index.js): * ***nix**: Package managers of recent *nix distros can install binary versions of LLVM, e.g.: ```bash # Ubuntu 18.04 - sudo apt install llvm-7 llvm-7-* lld-7 + sudo apt install llvm-7 llvm-7-* lld-7 liblld-7* ``` * **macOS**: [Brew](https://brew.sh/) contains a binary distribution of LLVM 7.1.0. However, as it's not the latest version, it won't be added to the path. We are using diff --git a/crates/mun_codegen/Cargo.toml b/crates/mun_codegen/Cargo.toml index 8d6a106f9..e4f147042 100644 --- a/crates/mun_codegen/Cargo.toml +++ b/crates/mun_codegen/Cargo.toml @@ -8,6 +8,7 @@ edition = "2018" inkwell = { git = "https://github.com/TheDan64/inkwell", branch = "llvm7-0" } mun_hir = { path = "../mun_hir" } mun_target = { path = "../mun_target" } +mun_lld = { path = "../mun_lld" } failure = "0.1.5" salsa="0.12" md5="0.6.1" @@ -21,4 +22,4 @@ insta = "0.12.0" [build-dependencies] lazy_static = "1.4.0" semver = "0.9.0" -regex = "1.3.1" \ No newline at end of file +regex = "1.3.1" diff --git a/crates/mun_codegen/build.rs b/crates/mun_codegen/build.rs deleted file mode 100644 index ce3fc018a..000000000 --- a/crates/mun_codegen/build.rs +++ /dev/null @@ -1,211 +0,0 @@ -use regex::Regex; -use semver::Version; -use std::{env, ffi::OsStr, fs, io, io::ErrorKind, path::Path, path::PathBuf, process::Command}; - -#[macro_use] -extern crate lazy_static; - -const LLVM_VERSION_MAJOR: u32 = 7; -const LLVM_VERSION_MINOR: u32 = 0; - -// Environment variables that can guide compilation -// -// When adding new ones, they should also be added to main() to force a -// rebuild if they are changed. - -lazy_static! { - /// A single path to search for LLVM in (containing bin/llvm-config) - static ref ENV_LLVM_PREFIX: String = - format!("LLVM_SYS_{}_PREFIX", LLVM_VERSION_MAJOR*10); - - /// If exactly "YES", ignore the version blacklist - static ref ENV_IGNORE_BLACKLIST: String = - format!("LLVM_SYS_{}_IGNORE_BLACKLIST", LLVM_VERSION_MAJOR*10); - - /// If set, enforce precise correspondence between crate and binary versions. - static ref ENV_STRICT_VERSIONING: String = - format!("LLVM_SYS_{}_STRICT_VERSIONING", LLVM_VERSION_MAJOR*10); - - /// If set, do not attempt to strip irrelevant options for llvm-config --cflags - static ref ENV_NO_CLEAN_CFLAGS: String = - format!("LLVM_SYS_{}_NO_CLEAN_CFLAGS", LLVM_VERSION_MAJOR*10); - - /// If set and targeting MSVC, force the debug runtime library - static ref ENV_USE_DEBUG_MSVCRT: String = - format!("LLVM_SYS_{}_USE_DEBUG_MSVCRT", LLVM_VERSION_MAJOR*10); - - /// If set, always link against libffi - static ref ENV_FORCE_FFI: String = - format!("LLVM_SYS_{}_FFI_WORKAROUND", LLVM_VERSION_MAJOR*10); -} - -lazy_static! { - /// LLVM version used by this version of the crate. - static ref CRATE_VERSION: Version = { - Version::parse(&format!("{}.{}.0",LLVM_VERSION_MAJOR, LLVM_VERSION_MINOR)) - .expect("Crate version is somehow not valid semver") - }; - - static ref LLVM_CONFIG_BINARY_NAMES: Vec = { - vec![ - format!("llvm-config{}", std::env::consts::EXE_SUFFIX), - format!("llvm-config-{}{}", LLVM_VERSION_MAJOR, std::env::consts::EXE_SUFFIX), - format!("llvm-config-{}.{}{}", LLVM_VERSION_MAJOR, LLVM_VERSION_MINOR, std::env::consts::EXE_SUFFIX), - ] - }; - - /// Filesystem path to an llvm-config binary for the correct version. - static ref LLVM_CONFIG_PATH: PathBuf = { - // Try llvm-config via PATH first. - if let Some(name) = locate_system_llvm_config() { - return name.into(); - } else { - println!("Didn't find usable system-wide LLVM."); - } - - // Did the user give us a binary path to use? If yes, try - // to use that and fail if it doesn't work. - if let Some(path) = env::var_os(&*ENV_LLVM_PREFIX) { - for binary_name in LLVM_CONFIG_BINARY_NAMES.iter() { - let mut pb: PathBuf = path.clone().into(); - pb.push("bin"); - pb.push(binary_name); - - let ver = llvm_version(&pb) - .expect(&format!("Failed to execute {:?}", &pb)); - if is_compatible_llvm(&ver) { - return pb; - } else { - println!("LLVM binaries specified by {} are the wrong version. - (Found {}, need {}.)", *ENV_LLVM_PREFIX, ver, *CRATE_VERSION); - } - } - } - - println!("No suitable version of LLVM was found system-wide or pointed - to by {}. - - Consider using `llvmenv` to compile an appropriate copy of LLVM, and - refer to the llvm-sys documentation for more information. - - llvm-sys: https://crates.io/crates/llvm-sys - llvmenv: https://crates.io/crates/llvmenv", *ENV_LLVM_PREFIX); - panic!("Could not find a compatible version of LLVM"); - }; -} - -/// Get the output from running `llvm-config` with the given argument. -/// -/// Lazily searches for or compiles LLVM as configured by the environment -/// variables. -fn llvm_config(arg: &str) -> String { - llvm_config_ex(&*LLVM_CONFIG_PATH, arg).expect("Surprising failure from llvm-config") -} - -/// Try to find a system-wide version of llvm-config that is compatible with -/// this crate. -/// -/// Returns None on failure. -fn locate_system_llvm_config() -> Option<&'static str> { - for binary_name in LLVM_CONFIG_BINARY_NAMES.iter() { - match llvm_version(binary_name) { - Ok(ref version) if is_compatible_llvm(version) => { - // Compatible version found. Nice. - return Some(binary_name); - } - Ok(version) => { - // Version mismatch. Will try further searches, but warn that - // we're not using the system one. - println!( - "Found LLVM version {} on PATH, but need {}", - version, *CRATE_VERSION - ); - } - Err(ref e) if e.kind() == ErrorKind::NotFound => { - // Looks like we failed to execute any llvm-config. Keep - // searching. - } - // Some other error, probably a weird failure. Give up. - Err(e) => panic!("Failed to search PATH for llvm-config: {}", e), - } - } - - None -} - -/// Check whether the given LLVM version is compatible with this version of -/// the crate. -fn is_compatible_llvm(llvm_version: &Version) -> bool { - // if let Some(reason) = is_blacklisted_llvm(llvm_version) { - // println!( - // "Found LLVM {}, which is blacklisted: {}", - // llvm_version, reason - // ); - // return false; - // } - - let strict = - env::var_os(&*ENV_STRICT_VERSIONING).is_some() || cfg!(feature = "strict-versioning"); - if strict { - llvm_version.major == CRATE_VERSION.major && llvm_version.minor == CRATE_VERSION.minor - } else { - llvm_version.major >= CRATE_VERSION.major - || (llvm_version.major == CRATE_VERSION.major - && llvm_version.minor >= CRATE_VERSION.minor) - } -} - -/// Invoke the specified binary as llvm-config. -/// -/// Explicit version of the `llvm_config` function that bubbles errors -/// up. -fn llvm_config_ex>(binary: S, arg: &str) -> io::Result { - Command::new(binary) - .arg(arg) - .arg("--link-static") // Don't use dylib for >= 3.9 - .output() - .map(|output| { - String::from_utf8(output.stdout).expect("Output from llvm-config was not valid UTF-8") - }) -} - -/// Get the LLVM version using llvm-config. -fn llvm_version>(binary: S) -> io::Result { - let version_str = llvm_config_ex(binary.as_ref(), "--version")?; - - // LLVM isn't really semver and uses version suffixes to build - // version strings like '3.8.0svn', so limit what we try to parse - // to only the numeric bits. - let re = Regex::new(r"^(?P\d+)\.(?P\d+)(?:\.(?P\d+))??").unwrap(); - let c = re - .captures(&version_str) - .expect("Could not determine LLVM version from llvm-config."); - - // some systems don't have a patch number but Version wants it so we just append .0 if it isn't - // there - let s = match c.name("patch") { - None => format!("{}.0", &c[0]), - Some(_) => c[0].to_string(), - }; - Ok(Version::parse(&s).unwrap()) -} - -fn main() { - let out_dir = env::var("OUT_DIR").unwrap(); - let bindir = llvm_config("--bindir").trim().to_string(); - - let lld_path = Path::new(&bindir) - .join(format!("lld{}", std::env::consts::EXE_SUFFIX)) - .canonicalize() - .expect(&format!( - "unable to locate 'lld{}' in {}", - std::env::consts::EXE_SUFFIX, - bindir - )); - let lld_output_path = Path::new(&out_dir) - .join("../../..") - .canonicalize() - .unwrap() - .join(format!("lld{}", std::env::consts::EXE_SUFFIX)); - fs::copy(lld_path, lld_output_path).unwrap(); -} diff --git a/crates/mun_codegen/src/code_gen.rs b/crates/mun_codegen/src/code_gen.rs index f6ce0e343..e4cd60796 100644 --- a/crates/mun_codegen/src/code_gen.rs +++ b/crates/mun_codegen/src/code_gen.rs @@ -7,7 +7,6 @@ use inkwell::OptimizationLevel; use mun_hir::FileId; use std::io::{self, Write}; use std::path::Path; -use std::process::{Child, Stdio}; mod abi_types; mod linker; @@ -16,8 +15,6 @@ mod linker; enum CodeGenerationError { #[fail(display = "linker error: {}", 0)] LinkerError(String), - #[fail(display = "linker error: {}", 0)] - SpawningLinkerError(io::Error), #[fail(display = "unknown target triple: {}", 0)] UnknownTargetTriple(String), #[fail(display = "error creating target machine")] @@ -105,20 +102,9 @@ pub fn write_module_shared_object( // Link the object linker.build_shared_object(&output_file_path); - let mut cmd = linker.finalize(); - let result = cmd - .stderr(Stdio::piped()) - .spawn() - .and_then(Child::wait_with_output) - .map_err(CodeGenerationError::SpawningLinkerError)?; - - if !result.status.success() { - let error = String::from_utf8(result.stderr) - .unwrap_or_else(|_| "".to_owned()); - Err(CodeGenerationError::LinkerError(error).into()) - } else { - Ok(()) - } + linker + .finalize() + .map_err(|e| CodeGenerationError::LinkerError(e).into()) } /// Optimizes the specified LLVM `Module` using the default passes for the given diff --git a/crates/mun_codegen/src/code_gen/linker.rs b/crates/mun_codegen/src/code_gen/linker.rs index 3862c1ca9..58b7e4bc6 100644 --- a/crates/mun_codegen/src/code_gen/linker.rs +++ b/crates/mun_codegen/src/code_gen/linker.rs @@ -1,10 +1,6 @@ use mun_target::spec; use mun_target::spec::LinkerFlavor; -use std::ffi::OsString; use std::path::Path; -use std::path::PathBuf; -use std::process; -use std::process::Command; pub fn create_with_target(target: &spec::Target) -> Box { match target.linker_flavor { @@ -17,137 +13,101 @@ pub fn create_with_target(target: &spec::Target) -> Box { pub trait Linker { fn add_object(&mut self, path: &Path); fn build_shared_object(&mut self, path: &Path); - fn finalize(&mut self) -> process::Command; + fn finalize(&mut self) -> Result<(), String>; } struct LdLinker { - cmd: process::Command, -} - -struct Ld64Linker { - cmd: process::Command, -} - -struct MsvcLinker { - cmd: process::Command, + args: Vec, } impl LdLinker { fn new(_target: &spec::Target) -> Self { - let mut cmd = process::Command::new(LLD_PATH.as_os_str()); - cmd.arg("-flavor"); - cmd.arg("ld"); - - LdLinker { cmd } - } -} - -impl Ld64Linker { - fn new(target: &spec::Target) -> Self { - let mut cmd = process::Command::new(LLD_PATH.as_os_str()); - cmd.arg("-flavor"); - cmd.arg("ld64"); - - cmd.arg("-arch"); - cmd.arg(&target.arch); - - Ld64Linker { cmd } - } -} - -lazy_static::lazy_static! { - static ref LLD_PATH: PathBuf = { - let path = std::env::current_exe() - .unwrap() - .parent() - .unwrap() - .to_path_buf(); - - let mut binary_dir:&Path = &path; - let mut binary_path; - loop { - binary_path = binary_dir.to_path_buf(); - binary_path.push(format!("lld{}", std::env::consts::EXE_SUFFIX)); - if binary_path.exists() { - break; - } - binary_dir = binary_dir.parent().expect("could not find lld"); + LdLinker { + args: Vec::default(), } - binary_path - }; -} - -impl MsvcLinker { - fn new(_target: &spec::Target) -> Self { - let mut cmd = process::Command::new(LLD_PATH.as_os_str()); - cmd.arg("-flavor"); - cmd.arg("link"); - - MsvcLinker { cmd } } } impl Linker for LdLinker { fn add_object(&mut self, path: &Path) { - self.cmd.arg(path); + self.args.push(path.to_string_lossy().to_string()); } fn build_shared_object(&mut self, path: &Path) { // Link as dynamic library - self.cmd.arg("--shared"); - // self.cmd.arg("--apply-dynamic-relocs"); - // self.cmd.arg("--pie"); + self.args.push("--shared".to_string()); // Specify output path - self.cmd.arg("-o"); - self.cmd.arg(path); + self.args.push("-o".to_string()); + self.args.push(path.to_str().unwrap().to_string()); } - fn finalize(&mut self) -> process::Command { - ::std::mem::replace(&mut self.cmd, Command::new("")) + fn finalize(&mut self) -> Result<(), String> { + mun_lld::link(mun_lld::LldFlavor::Elf, &self.args).ok() + } +} + +struct Ld64Linker { + args: Vec, +} + +impl Ld64Linker { + fn new(target: &spec::Target) -> Self { + Ld64Linker { + args: vec![format!("-arch {}", &target.arch)], + } } } impl Linker for Ld64Linker { fn add_object(&mut self, path: &Path) { - self.cmd.arg(path); + self.args.push(path.to_string_lossy().to_string()); } fn build_shared_object(&mut self, path: &Path) { // Link as dynamic library - self.cmd.arg("-dylib"); - self.cmd.arg("-lsystem"); + self.args.push("-dylib".to_string()); + self.args.push("-lsystem".to_string()); // Specify output path - self.cmd.arg("-o"); - self.cmd.arg(path); + self.args.push("-o".to_string()); + self.args.push(path.to_str().unwrap().to_string()); } - fn finalize(&mut self) -> process::Command { - ::std::mem::replace(&mut self.cmd, Command::new("")) + fn finalize(&mut self) -> Result<(), String> { + mun_lld::link(mun_lld::LldFlavor::MachO, &self.args).ok() + } +} + +struct MsvcLinker { + args: Vec, +} + +impl MsvcLinker { + fn new(_target: &spec::Target) -> Self { + MsvcLinker { + args: Vec::default(), + } } } impl Linker for MsvcLinker { fn add_object(&mut self, path: &Path) { - self.cmd.arg(path); + self.args.push(path.to_string_lossy().to_string()); } fn build_shared_object(&mut self, path: &Path) { - self.cmd.arg("/DLL"); - self.cmd.arg("/NOENTRY"); - self.cmd.arg("/EXPORT:get_info"); - - let mut arg = OsString::from("/IMPLIB:"); - arg.push(path.with_extension("dll.lib")); - self.cmd.arg(arg); - - let mut arg = OsString::from("/OUT:"); - arg.push(path); - self.cmd.arg(arg); + self.args.push("/DLL".to_string()); + self.args.push("/NOENTRY".to_string()); + self.args.push("/EXPORT:get_info".to_string()); + self.args.push(format!( + "/IMPLIB:{}", + path.with_extension("dll.lib").to_string_lossy() + )); + self.args.push(format!("/OUT:{}", path.to_string_lossy())); } - fn finalize(&mut self) -> process::Command { - ::std::mem::replace(&mut self.cmd, Command::new("")) + fn finalize(&mut self) -> Result<(), String> { + mun_lld::link(mun_lld::LldFlavor::Coff, &self.args).ok() } } diff --git a/crates/mun_compiler/src/lib.rs b/crates/mun_compiler/src/lib.rs index 994e247b2..42f828fe8 100644 --- a/crates/mun_compiler/src/lib.rs +++ b/crates/mun_compiler/src/lib.rs @@ -249,7 +249,6 @@ pub fn main(options: &CompilerOptions) -> Result, failure::Error } let diagnostics = diagnostics(&db, file_id); - dbg!(&diagnostics); if !diagnostics.is_empty() { let mut writer = StandardStream::stderr(ColorChoice::Auto); for diagnostic in diagnostics { diff --git a/crates/mun_lld/Cargo.toml b/crates/mun_lld/Cargo.toml new file mode 100644 index 000000000..bf41168e7 --- /dev/null +++ b/crates/mun_lld/Cargo.toml @@ -0,0 +1,20 @@ +[package] +name = "mun_lld" +version = "70.2.0" +authors = ["Bas Zalmstra "] +edition = "2018" + +[dependencies] +libc = "0.2.65" + +[build-dependencies.cc] +version = "1.0" + +[build-dependencies.lazy_static] +version = "1.0" + +[build-dependencies.regex] +version = "1.0" + +[build-dependencies.semver] +version = "0.9" diff --git a/crates/mun_lld/build.rs b/crates/mun_lld/build.rs new file mode 100644 index 000000000..4f0766f5b --- /dev/null +++ b/crates/mun_lld/build.rs @@ -0,0 +1,415 @@ +extern crate cc; +#[macro_use] +extern crate lazy_static; +extern crate regex; +extern crate semver; + +use regex::Regex; +use semver::Version; +use std::env; +use std::ffi::OsStr; +use std::io::{self, ErrorKind}; +use std::path::PathBuf; +use std::process::Command; + +lazy_static! { + /// LLVM version used by this version of the crate. + static ref CRATE_VERSION: Version = { + let crate_version = Version::parse(env!("CARGO_PKG_VERSION")) + .expect("Crate version is somehow not valid semver"); + Version { + major: crate_version.major / 10, + minor: crate_version.major % 10, + .. crate_version + } + }; + + static ref LLVM_CONFIG_BINARY_NAMES: Vec = { + vec![ + "llvm-config".into(), + format!("llvm-config-{}", CRATE_VERSION.major), + format!("llvm-config-{}.{}", CRATE_VERSION.major, CRATE_VERSION.minor), + ] + }; + + /// Filesystem path to an llvm-config binary for the correct version. + static ref LLVM_CONFIG_PATH: PathBuf = { + // Try llvm-config via PATH first. + if let Some(name) = locate_system_llvm_config() { + return name.into(); + } else { + println!("Didn't find usable system-wide LLVM."); + } + + // Did the user give us a binary path to use? If yes, try + // to use that and fail if it doesn't work. + let binary_prefix_var = format!("LLVM_SYS_{}_PREFIX", + env!("CARGO_PKG_VERSION_MAJOR")); + if let Some(path) = env::var_os(&binary_prefix_var) { + for binary_name in LLVM_CONFIG_BINARY_NAMES.iter() { + let mut pb: PathBuf = path.clone().into(); + pb.push("bin"); + pb.push(binary_name); + + let ver = llvm_version(&pb) + .expect(&format!("Failed to execute {:?}", &pb)); + if is_compatible_llvm(&ver) { + return pb; + } else { + println!("LLVM binaries specified by {} are the wrong version. + (Found {}, need {}.)", binary_prefix_var, ver, *CRATE_VERSION); + } + } + } + + println!("No suitable version of LLVM was found system-wide or pointed + to by {}. + + Consider using `llvmenv` to compile an appropriate copy of LLVM, and + refer to the llvm-sys documentation for more information. + + llvm-sys: https://crates.io/crates/llvm-sys + llvmenv: https://crates.io/crates/llvmenv", binary_prefix_var); + panic!("Could not find a compatible version of LLVM"); + }; +} + +/// Try to find a system-wide version of llvm-config that is compatible with +/// this crate. +/// +/// Returns None on failure. +fn locate_system_llvm_config() -> Option<&'static str> { + for binary_name in LLVM_CONFIG_BINARY_NAMES.iter() { + match llvm_version(binary_name) { + Ok(ref version) if is_compatible_llvm(version) => { + // Compatible version found. Nice. + return Some(binary_name); + } + Ok(version) => { + // Version mismatch. Will try further searches, but warn that + // we're not using the system one. + println!( + "Found LLVM version {} on PATH, but need {}.", + version, *CRATE_VERSION + ); + } + Err(ref e) if e.kind() == ErrorKind::NotFound => { + // Looks like we failed to execute any llvm-config. Keep + // searching. + } + // Some other error, probably a weird failure. Give up. + Err(e) => panic!("Failed to search PATH for llvm-config: {}", e), + } + } + + None +} + +/// Check whether the given version of LLVM is blacklisted, +/// returning `Some(reason)` if it is. +fn is_blacklisted_llvm(llvm_version: &Version) -> Option<&'static str> { + static BLACKLIST: &'static [(u64, u64, u64, &'static str)] = &[]; + + let blacklist_var = format!( + "LLVM_SYS_{}_IGNORE_BLACKLIST", + env!("CARGO_PKG_VERSION_MAJOR") + ); + if let Some(x) = env::var_os(&blacklist_var) { + if &x == "YES" { + println!( + "cargo:warning=Ignoring blacklist entry for LLVM {}", + llvm_version + ); + return None; + } else { + println!( + "cargo:warning={} is set but not exactly \"YES\"; blacklist is still honored.", + &blacklist_var + ); + } + } + + for &(major, minor, patch, reason) in BLACKLIST.iter() { + let bad_version = Version { + major: major, + minor: minor, + patch: patch, + pre: vec![], + build: vec![], + }; + + if &bad_version == llvm_version { + return Some(reason); + } + } + None +} + +/// Check whether the given LLVM version is compatible with this version of +/// the crate. +fn is_compatible_llvm(llvm_version: &Version) -> bool { + if let Some(reason) = is_blacklisted_llvm(llvm_version) { + println!( + "Found LLVM {}, which is blacklisted: {}", + llvm_version, reason + ); + return false; + } + + let strict = env::var_os(format!( + "LLVM_SYS_{}_STRICT_VERSIONING", + env!("CARGO_PKG_VERSION_MAJOR") + )) + .is_some() + || cfg!(feature = "strict-versioning"); + if strict { + llvm_version.major == CRATE_VERSION.major && llvm_version.minor == CRATE_VERSION.minor + } else { + llvm_version.major >= CRATE_VERSION.major + || (llvm_version.major == CRATE_VERSION.major + && llvm_version.minor >= CRATE_VERSION.minor) + } +} + +/// Get the output from running `llvm-config` with the given argument. +/// +/// Lazily searches for or compiles LLVM as configured by the environment +/// variables. +fn llvm_config(arg: &str) -> String { + llvm_config_ex(&*LLVM_CONFIG_PATH, arg).expect("Surprising failure from llvm-config") +} + +/// Invoke the specified binary as llvm-config. +/// +/// Explicit version of the `llvm_config` function that bubbles errors +/// up. +fn llvm_config_ex>(binary: S, arg: &str) -> io::Result { + Command::new(binary) + .arg(arg) + .arg("--link-static") // Don't use dylib for >= 3.9 + .output() + .map(|output| { + String::from_utf8(output.stdout).expect("Output from llvm-config was not valid UTF-8") + }) +} + +/// Get the LLVM version using llvm-config. +fn llvm_version>(binary: S) -> io::Result { + let version_str = llvm_config_ex(binary.as_ref(), "--version")?; + + // LLVM isn't really semver and uses version suffixes to build + // version strings like '3.8.0svn', so limit what we try to parse + // to only the numeric bits. + let re = Regex::new(r"^(?P\d+)\.(?P\d+)(?:\.(?P\d+))??").unwrap(); + let c = re + .captures(&version_str) + .expect("Could not determine LLVM version from llvm-config."); + + // some systems don't have a patch number but Version wants it so we just append .0 if it isn't + // there + let s = match c.name("patch") { + None => format!("{}.0", &c[0]), + Some(_) => c[0].to_string(), + }; + Ok(Version::parse(&s).unwrap()) +} + +/// Get the names of the dylibs required by LLVM, including the C++ standard +/// library. +fn get_system_libraries() -> Vec { + llvm_config("--system-libs") + .split(&[' ', '\n'] as &[char]) + .filter(|s| !s.is_empty()) + .map(|flag| { + if cfg!(target_env = "msvc") { + // Same as --libnames, foo.lib + assert!(flag.ends_with(".lib")); + &flag[..flag.len() - 4] + } else { + // Linker flags style, -lfoo + assert!(flag.starts_with("-l")); + &flag[2..] + } + }) + .chain(get_system_libcpp()) + .map(str::to_owned) + .collect::>() +} + +/// Get the library that must be linked for C++, if any. +fn get_system_libcpp() -> Option<&'static str> { + if cfg!(target_env = "msvc") { + // MSVC doesn't need an explicit one. + None + } else if cfg!(target_os = "macos") { + // On OS X 10.9 and later, LLVM's libc++ is the default. On earlier + // releases GCC's libstdc++ is default. Unfortunately we can't + // reasonably detect which one we need (on older ones libc++ is + // available and can be selected with -stdlib=lib++), so assume the + // latest, at the cost of breaking the build on older OS releases + // when LLVM was built against libstdc++. + Some("c++") + } else if cfg!(target_os = "freebsd") { + Some("c++") + } else { + // Otherwise assume GCC's libstdc++. + // This assumption is probably wrong on some platforms, but would need + // testing on them. + Some("stdc++") + } +} + +/// Get the names of libraries to link against. +fn get_link_libraries() -> Vec { + // Using --libnames in conjunction with --libdir is particularly important + // for MSVC when LLVM is in a path with spaces, but it is generally less of + // a hack than parsing linker flags output from --libs and --ldflags. + llvm_config("--libnames") + .split(&[' ', '\n'] as &[char]) + .filter(|s| !s.is_empty()) + .map(|name| { + // --libnames gives library filenames. Extract only the name that + // we need to pass to the linker. + if cfg!(target_env = "msvc") { + // LLVMfoo.lib + assert!(name.ends_with(".lib")); + &name[..name.len() - 4] + } else { + // libLLVMfoo.a + assert!(name.starts_with("lib") && name.ends_with(".a")); + &name[3..name.len() - 2] + } + }) + .map(str::to_owned) + .collect::>() +} + +// fn get_llvm_cflags() -> String { +// let output = llvm_config("--cflags"); + +// // llvm-config includes cflags from its own compilation with --cflags that +// // may not be relevant to us. In particularly annoying cases, these might +// // include flags that aren't understood by the default compiler we're +// // using. Unless requested otherwise, clean CFLAGS of options that are +// // known to be possibly-harmful. +// let no_clean = env::var_os(format!( +// "LLVM_SYS_{}_NO_CLEAN_CFLAGS", +// env!("CARGO_PKG_VERSION_MAJOR") +// )) +// .is_some(); +// if no_clean || cfg!(target_env = "msvc") { +// // MSVC doesn't accept -W... options, so don't try to strip them and +// // possibly strip something that should be retained. Also do nothing if +// // the user requests it. +// return output; +// } + +// llvm_config("--cflags") +// .split(&[' ', '\n'][..]) +// .filter(|word| !word.starts_with("-W")) +// .collect::>() +// .join(" ") +// } + +fn get_llvm_cxxflags() -> String { + let output = llvm_config("--cxxflags"); + + // llvm-config includes cflags from its own compilation with --cflags that + // may not be relevant to us. In particularly annoying cases, these might + // include flags that aren't understood by the default compiler we're + // using. Unless requested otherwise, clean CFLAGS of options that are + // known to be possibly-harmful. + let no_clean = env::var_os(format!( + "LLVM_SYS_{}_NO_CLEAN_CFLAGS", + env!("CARGO_PKG_VERSION_MAJOR") + )) + .is_some(); + if no_clean || cfg!(target_env = "msvc") { + // MSVC doesn't accept -W... options, so don't try to strip them and + // possibly strip something that should be retained. Also do nothing if + // the user requests it. + return output; + } + + llvm_config("--cxxflags") + .split(&[' ', '\n'][..]) + .filter(|word| !word.starts_with("-W")) + .collect::>() + .join(" ") +} + +fn is_llvm_debug() -> bool { + // Has to be either Debug or Release + llvm_config("--build-mode").contains("Debug") +} + +fn main() { + // Build the extra wrapper functions. + std::env::set_var("CXXFLAGS", get_llvm_cxxflags()); + cc::Build::new() + .cpp(true) + .file("wrapper/lld-c.cpp") + .compile("lldwrapper"); + + if cfg!(feature = "no-llvm-linking") { + return; + } + + let libdir = llvm_config("--libdir"); + + // Export information to other crates + println!("cargo:config_path={}", LLVM_CONFIG_PATH.display()); // will be DEP_LLVM_CONFIG_PATH + println!("cargo:libdir={}", libdir); // DEP_LLVM_LIBDIR + + // Link LLVM libraries + println!("cargo:rustc-link-search=native={}", libdir); + let blacklist = vec!["LLVMLineEditor"]; + for name in get_link_libraries().iter().filter(|n| { + blacklist + .iter() + .find(|blacklisted| n.contains(**blacklisted)) + .is_none() + }) { + println!("cargo:rustc-link-lib=static={}", name); + } + + // Link system libraries + for name in get_system_libraries() { + println!("cargo:rustc-link-lib=dylib={}", name); + } + + let use_debug_msvcrt = env::var_os(format!( + "LLVM_SYS_{}_USE_DEBUG_MSVCRT", + env!("CARGO_PKG_VERSION_MAJOR") + )) + .is_some(); + if cfg!(target_env = "msvc") && (use_debug_msvcrt || is_llvm_debug()) { + println!("cargo:rustc-link-lib={}", "msvcrtd"); + } + + // Link libffi if the user requested this workaround. + // See https://bitbucket.org/tari/llvm-sys.rs/issues/12/ + let force_ffi = env::var_os(format!( + "LLVM_SYS_{}_FFI_WORKAROUND", + env!("CARGO_PKG_VERSION_MAJOR") + )) + .is_some(); + if force_ffi { + println!("cargo:rustc-link-lib=dylib={}", "ffi"); + } + + println!("cargo:rustc-link-lib=static={}", "lldCOFF"); + println!("cargo:rustc-link-lib=static={}", "lldCommon"); + println!("cargo:rustc-link-lib=static={}", "lldCore"); + println!("cargo:rustc-link-lib=static={}", "lldDriver"); + println!("cargo:rustc-link-lib=static={}", "lldELF"); + println!("cargo:rustc-link-lib=static={}", "lldMachO"); + println!("cargo:rustc-link-lib=static={}", "lldMinGW"); + println!("cargo:rustc-link-lib=static={}", "lldReaderWriter"); + println!("cargo:rustc-link-lib=static={}", "lldWasm"); + println!("cargo:rustc-link-lib=static={}", "lldYAML"); + + if cfg!(not(target_os = "windows")) { + println!("cargo:rustc-link-lib=dylib=ffi"); + } +} diff --git a/crates/mun_lld/src/lib.rs b/crates/mun_lld/src/lib.rs new file mode 100644 index 000000000..131cd4637 --- /dev/null +++ b/crates/mun_lld/src/lib.rs @@ -0,0 +1,82 @@ +use std::ffi::CStr; +use std::{ + ffi::CString, + iter::FromIterator, + os::raw::{c_char, c_int}, +}; + +#[repr(C)] +struct LldInvokeResult { + success: bool, + messages: *const c_char, +} + +#[repr(C)] +pub enum LldFlavor { + Elf = 0, + Wasm = 1, + MachO = 2, + Coff = 3, +} + +extern "C" { + fn mun_lld_link(flavor: LldFlavor, argc: c_int, argv: *const *const c_char) -> LldInvokeResult; + fn mun_link_free_result(result: *mut LldInvokeResult); +} + +pub enum LldError { + StringConversionError, +} + +pub struct LldResult { + success: bool, + messages: String, +} + +impl LldResult { + pub fn ok(self) -> Result<(), String> { + if self.success { + Ok(()) + } else { + Err(self.messages) + } + } +} + +/// Invokes LLD of the given flavor with the specified arguments. +pub fn link(target: LldFlavor, args: &[String]) -> LldResult { + // Prepare arguments + let c_args = args + .iter() + .map(|arg| CString::new(arg.as_bytes()).unwrap()) + .collect::>(); + let args: Vec<*const c_char> = Vec::from_iter(c_args.iter().map(|arg| arg.as_ptr())); + + dbg!(&c_args); + + // Invoke LLD + let mut lld_result = unsafe { mun_lld_link(target, args.len() as c_int, args.as_ptr()) }; + + // Get the messages from the invocation + let messages = if !lld_result.messages.is_null() { + unsafe { + CStr::from_ptr(lld_result.messages) + .to_string_lossy() + .to_string() + } + } else { + String::new() + }; + + // Construct the result + let result = LldResult { + success: lld_result.success, + messages, + }; + + // Release the result + unsafe { mun_link_free_result(&mut lld_result as *mut LldInvokeResult) }; + drop(lld_result); + + result +} diff --git a/crates/mun_lld/wrapper/lld-c.cpp b/crates/mun_lld/wrapper/lld-c.cpp new file mode 100644 index 000000000..b44b82177 --- /dev/null +++ b/crates/mun_lld/wrapper/lld-c.cpp @@ -0,0 +1,80 @@ +#include +#include +#include +#include + +const char* mun_alloc_str(const std::string& str) +{ + size_t size = str.length(); + if(size > 0) + { + char *strPtr = reinterpret_cast(malloc(size + 1)); + memcpy(strPtr, str.c_str(), size + 1); + return strPtr; + } + return nullptr; +} + +// The COFF driver seems not to be thread safe. This is terrible. We basically only allow single threaded access +// to the driver using a mutex. +std::mutex _coffMutex; +std::mutex _elfMutex; + +extern "C" { + +enum LldFlavor { + Elf = 0, + Wasm = 1, + MachO = 2, + Coff = 3, +}; + +struct LldInvokeResult { + bool success; + const char* messages; +}; + +void mun_link_free_result(LldInvokeResult* result) +{ + if(result->messages) + { + free(reinterpret_cast(const_cast(result->messages))); + } +} + +LldInvokeResult mun_lld_link(LldFlavor flavor, int argc, const char *const *argv) { + std::string errorString; + llvm::raw_string_ostream errorStream(errorString); + std::vector args(argv, argv + argc); + LldInvokeResult result; + switch(flavor) + { + case Elf: + { + args.insert(args.begin(), "lld"); // Issue #1: The first argument MUST be the executable name.. + std::unique_lock lock(_elfMutex); // Issue #2: The ELF driver is not thread safe.. + result.success = lld::elf::link(args, false, errorStream); + break; + } + case Wasm: + result.success = lld::wasm::link(args, false, errorStream); + break; + case MachO: + result.success = lld::mach_o::link(args, false, errorStream); + break; + case Coff: + { + args.insert(args.begin(), "lld.exe"); // Issue #1: The first argument MUST be the executable name.. + std::unique_lock lock(_coffMutex); // Issue #2: The COFF driver is not thread safe.. + result.success = lld::coff::link(args, false, errorStream); + break; + } + default: + result.success = false; + break; + } + result.messages = mun_alloc_str(errorStream.str()); + return result; +} + +} From 1093d41463606bb661baf7ac0905cfa16902e77d Mon Sep 17 00:00:00 2001 From: Bas Zalmstra Date: Mon, 4 Nov 2019 14:31:30 +0100 Subject: [PATCH 2/2] refactor: pass path conversion errors up the stack --- .github/actions/install-llvm/dist/index.js | 2 +- .github/actions/install-llvm/index.js | 2 +- README.md | 2 +- crates/mun_codegen/src/code_gen.rs | 24 ++-- crates/mun_codegen/src/code_gen/linker.rs | 131 +++++++++++++++------ crates/mun_lld/Cargo.toml | 16 +-- crates/mun_lld/build.rs | 27 ----- 7 files changed, 121 insertions(+), 83 deletions(-) diff --git a/.github/actions/install-llvm/dist/index.js b/.github/actions/install-llvm/dist/index.js index f99c17dce..8cf0cfb8a 100644 --- a/.github/actions/install-llvm/dist/index.js +++ b/.github/actions/install-llvm/dist/index.js @@ -975,7 +975,7 @@ async function execute(cmd) { (async () => { try { if(isLinux) { - await exec.exec("sudo apt install llvm-7 llvm-7-* lld-7 liblld-7*"); + await exec.exec("sudo apt install llvm-7 llvm-7-* liblld-7*"); } else if(isMacOS) { await exec.exec("brew install llvm@7") let llvmPath = await execute("brew --prefix llvm@7"); diff --git a/.github/actions/install-llvm/index.js b/.github/actions/install-llvm/index.js index 21edcf51d..ed2ee0621 100644 --- a/.github/actions/install-llvm/index.js +++ b/.github/actions/install-llvm/index.js @@ -30,7 +30,7 @@ export async function execute(cmd) { (async () => { try { if(isLinux) { - await exec.exec("sudo apt install llvm-7 llvm-7-* lld-7 liblld-7*"); + await exec.exec("sudo apt install llvm-7 llvm-7-* liblld-7*"); } else if(isMacOS) { await exec.exec("brew install llvm@7") let llvmPath = await execute("brew --prefix llvm@7"); diff --git a/README.md b/README.md index bf611b4be..b670239c9 100644 --- a/README.md +++ b/README.md @@ -88,7 +88,7 @@ runners](.github/actions/install-llvm/index.js): * ***nix**: Package managers of recent *nix distros can install binary versions of LLVM, e.g.: ```bash # Ubuntu 18.04 - sudo apt install llvm-7 llvm-7-* lld-7 liblld-7* + sudo apt install llvm-7 llvm-7-* liblld-7* ``` * **macOS**: [Brew](https://brew.sh/) contains a binary distribution of LLVM 7.1.0. However, as it's not the latest version, it won't be added to the path. We are using diff --git a/crates/mun_codegen/src/code_gen.rs b/crates/mun_codegen/src/code_gen.rs index e4cd60796..28fa1e2f9 100644 --- a/crates/mun_codegen/src/code_gen.rs +++ b/crates/mun_codegen/src/code_gen.rs @@ -1,3 +1,4 @@ +use crate::code_gen::linker::LinkerError; use crate::IrDatabase; use failure::Fail; use inkwell::module::Module; @@ -13,8 +14,10 @@ mod linker; #[derive(Debug, Fail)] enum CodeGenerationError { - #[fail(display = "linker error: {}", 0)] - LinkerError(String), + #[fail(display = "{}", 0)] + LinkerError(#[fail(cause)] LinkerError), + #[fail(display = "error linking modules: {}", 0)] + ModuleLinkerError(String), #[fail(display = "unknown target triple: {}", 0)] UnknownTargetTriple(String), #[fail(display = "error creating target machine")] @@ -25,6 +28,12 @@ enum CodeGenerationError { CodeGenerationError(String), } +impl From for CodeGenerationError { + fn from(e: LinkerError) -> Self { + CodeGenerationError::LinkerError(e) + } +} + /// Construct a shared object for the given `hir::FileId` at the specified output file location. pub fn write_module_shared_object( db: &impl IrDatabase, @@ -46,7 +55,7 @@ pub fn write_module_shared_object( let module = db.module_ir(file_id); assembly_module .link_in_module(module.llvm_module.clone()) - .map_err(|e| CodeGenerationError::LinkerError(e.to_string()))?; + .map_err(|e| CodeGenerationError::ModuleLinkerError(e.to_string()))?; // Generate the `get_info` method. symbols::gen_reflection_ir( @@ -97,14 +106,13 @@ pub fn write_module_shared_object( // Construct a linker for the target let mut linker = linker::create_with_target(&target); - linker.add_object(obj_file.path()); + linker.add_object(obj_file.path())?; // Link the object - linker.build_shared_object(&output_file_path); + linker.build_shared_object(&output_file_path)?; + linker.finalize()?; - linker - .finalize() - .map_err(|e| CodeGenerationError::LinkerError(e).into()) + Ok(()) } /// Optimizes the specified LLVM `Module` using the default passes for the given diff --git a/crates/mun_codegen/src/code_gen/linker.rs b/crates/mun_codegen/src/code_gen/linker.rs index 58b7e4bc6..d829584c9 100644 --- a/crates/mun_codegen/src/code_gen/linker.rs +++ b/crates/mun_codegen/src/code_gen/linker.rs @@ -1,6 +1,30 @@ +use failure::Fail; use mun_target::spec; use mun_target::spec::LinkerFlavor; -use std::path::Path; +use std::fmt; +use std::path::{Path, PathBuf}; + +#[derive(Fail, Debug)] +pub enum LinkerError { + /// Error emitted by the linker + LinkError(String), + + /// Error in path conversion + PathError(PathBuf), +} + +impl fmt::Display for LinkerError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { + match self { + LinkerError::LinkError(e) => write!(f, "{}", e), + LinkerError::PathError(path) => write!( + f, + "path contains invalid UTF-8 characters: {}", + path.display() + ), + } + } +} pub fn create_with_target(target: &spec::Target) -> Box { match target.linker_flavor { @@ -11,9 +35,9 @@ pub fn create_with_target(target: &spec::Target) -> Box { } pub trait Linker { - fn add_object(&mut self, path: &Path); - fn build_shared_object(&mut self, path: &Path); - fn finalize(&mut self) -> Result<(), String>; + fn add_object(&mut self, path: &Path) -> Result<(), LinkerError>; + fn build_shared_object(&mut self, path: &Path) -> Result<(), LinkerError>; + fn finalize(&mut self) -> Result<(), LinkerError>; } struct LdLinker { @@ -29,21 +53,34 @@ impl LdLinker { } impl Linker for LdLinker { - fn add_object(&mut self, path: &Path) { - self.args.push(path.to_string_lossy().to_string()); + fn add_object(&mut self, path: &Path) -> Result<(), LinkerError> { + let path_str = path + .to_str() + .ok_or_else(|| LinkerError::PathError(path.to_owned()))? + .to_owned(); + self.args.push(path_str); + Ok(()) } - fn build_shared_object(&mut self, path: &Path) { + fn build_shared_object(&mut self, path: &Path) -> Result<(), LinkerError> { + let path_str = path + .to_str() + .ok_or_else(|| LinkerError::PathError(path.to_owned()))?; + // Link as dynamic library - self.args.push("--shared".to_string()); + self.args.push("--shared".to_owned()); // Specify output path - self.args.push("-o".to_string()); - self.args.push(path.to_str().unwrap().to_string()); + self.args.push("-o".to_owned()); + self.args.push(path_str.to_owned()); + + Ok(()) } - fn finalize(&mut self) -> Result<(), String> { - mun_lld::link(mun_lld::LldFlavor::Elf, &self.args).ok() + fn finalize(&mut self) -> Result<(), LinkerError> { + mun_lld::link(mun_lld::LldFlavor::Elf, &self.args) + .ok() + .map_err(LinkerError::LinkError) } } @@ -60,22 +97,35 @@ impl Ld64Linker { } impl Linker for Ld64Linker { - fn add_object(&mut self, path: &Path) { - self.args.push(path.to_string_lossy().to_string()); + fn add_object(&mut self, path: &Path) -> Result<(), LinkerError> { + let path_str = path + .to_str() + .ok_or_else(|| LinkerError::PathError(path.to_owned()))? + .to_owned(); + self.args.push(path_str); + Ok(()) } - fn build_shared_object(&mut self, path: &Path) { + fn build_shared_object(&mut self, path: &Path) -> Result<(), LinkerError> { + let path_str = path + .to_str() + .ok_or_else(|| LinkerError::PathError(path.to_owned()))?; + // Link as dynamic library - self.args.push("-dylib".to_string()); - self.args.push("-lsystem".to_string()); + self.args.push("-dylib".to_owned()); + self.args.push("-lsystem".to_owned()); // Specify output path - self.args.push("-o".to_string()); - self.args.push(path.to_str().unwrap().to_string()); + self.args.push("-o".to_owned()); + self.args.push(path_str.to_owned()); + + Ok(()) } - fn finalize(&mut self) -> Result<(), String> { - mun_lld::link(mun_lld::LldFlavor::MachO, &self.args).ok() + fn finalize(&mut self) -> Result<(), LinkerError> { + mun_lld::link(mun_lld::LldFlavor::MachO, &self.args) + .ok() + .map_err(LinkerError::LinkError) } } @@ -92,22 +142,35 @@ impl MsvcLinker { } impl Linker for MsvcLinker { - fn add_object(&mut self, path: &Path) { - self.args.push(path.to_string_lossy().to_string()); + fn add_object(&mut self, path: &Path) -> Result<(), LinkerError> { + let path_str = path + .to_str() + .ok_or_else(|| LinkerError::PathError(path.to_owned()))? + .to_owned(); + self.args.push(path_str); + Ok(()) } - fn build_shared_object(&mut self, path: &Path) { - self.args.push("/DLL".to_string()); - self.args.push("/NOENTRY".to_string()); - self.args.push("/EXPORT:get_info".to_string()); - self.args.push(format!( - "/IMPLIB:{}", - path.with_extension("dll.lib").to_string_lossy() - )); - self.args.push(format!("/OUT:{}", path.to_string_lossy())); + fn build_shared_object(&mut self, path: &Path) -> Result<(), LinkerError> { + let dll_path_str = path + .to_str() + .ok_or_else(|| LinkerError::PathError(path.to_owned()))?; + + let dll_lib_path_str = path + .to_str() + .ok_or_else(|| LinkerError::PathError(path.to_owned()))?; + + self.args.push("/DLL".to_owned()); + self.args.push("/NOENTRY".to_owned()); + self.args.push("/EXPORT:get_info".to_owned()); + self.args.push(format!("/IMPLIB:{}", dll_lib_path_str)); + self.args.push(format!("/OUT:{}", dll_path_str)); + Ok(()) } - fn finalize(&mut self) -> Result<(), String> { - mun_lld::link(mun_lld::LldFlavor::Coff, &self.args).ok() + fn finalize(&mut self) -> Result<(), LinkerError> { + mun_lld::link(mun_lld::LldFlavor::Coff, &self.args) + .ok() + .map_err(LinkerError::LinkError) } } diff --git a/crates/mun_lld/Cargo.toml b/crates/mun_lld/Cargo.toml index bf41168e7..c68a45e91 100644 --- a/crates/mun_lld/Cargo.toml +++ b/crates/mun_lld/Cargo.toml @@ -7,14 +7,8 @@ edition = "2018" [dependencies] libc = "0.2.65" -[build-dependencies.cc] -version = "1.0" - -[build-dependencies.lazy_static] -version = "1.0" - -[build-dependencies.regex] -version = "1.0" - -[build-dependencies.semver] -version = "0.9" +[build-dependencies] +cc = "1.0" +lazy_static = "1.4" +regex = "1.3" +semver = "0.9" diff --git a/crates/mun_lld/build.rs b/crates/mun_lld/build.rs index 4f0766f5b..97eec2119 100644 --- a/crates/mun_lld/build.rs +++ b/crates/mun_lld/build.rs @@ -284,33 +284,6 @@ fn get_link_libraries() -> Vec { .collect::>() } -// fn get_llvm_cflags() -> String { -// let output = llvm_config("--cflags"); - -// // llvm-config includes cflags from its own compilation with --cflags that -// // may not be relevant to us. In particularly annoying cases, these might -// // include flags that aren't understood by the default compiler we're -// // using. Unless requested otherwise, clean CFLAGS of options that are -// // known to be possibly-harmful. -// let no_clean = env::var_os(format!( -// "LLVM_SYS_{}_NO_CLEAN_CFLAGS", -// env!("CARGO_PKG_VERSION_MAJOR") -// )) -// .is_some(); -// if no_clean || cfg!(target_env = "msvc") { -// // MSVC doesn't accept -W... options, so don't try to strip them and -// // possibly strip something that should be retained. Also do nothing if -// // the user requests it. -// return output; -// } - -// llvm_config("--cflags") -// .split(&[' ', '\n'][..]) -// .filter(|word| !word.starts_with("-W")) -// .collect::>() -// .join(" ") -// } - fn get_llvm_cxxflags() -> String { let output = llvm_config("--cxxflags");