use mun_target::spec;
use mun_target::spec::LinkerFlavor;
use std::fmt;
use std::path::{Path, PathBuf};
use thiserror::Error;
#[derive(Error, Debug)]
pub enum LinkerError {
LinkError(String),
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<dyn Linker> {
match target.linker_flavor {
LinkerFlavor::Ld => Box::new(LdLinker::new(target)),
LinkerFlavor::Ld64 => Box::new(Ld64Linker::new(target)),
LinkerFlavor::Msvc => Box::new(MsvcLinker::new(target)),
}
}
pub trait Linker {
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 {
args: Vec<String>,
}
impl LdLinker {
fn new(_target: &spec::Target) -> Self {
LdLinker {
args: Vec::default(),
}
}
}
impl Linker for LdLinker {
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) -> Result<(), LinkerError> {
let path_str = path
.to_str()
.ok_or_else(|| LinkerError::PathError(path.to_owned()))?;
self.args.push("--shared".to_owned());
self.args.push("-o".to_owned());
self.args.push(path_str.to_owned());
Ok(())
}
fn finalize(&mut self) -> Result<(), LinkerError> {
mun_lld::link(mun_lld::LldFlavor::Elf, &self.args)
.ok()
.map_err(LinkerError::LinkError)
}
}
struct Ld64Linker {
args: Vec<String>,
}
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) -> 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) -> Result<(), LinkerError> {
let path_str = path
.to_str()
.ok_or_else(|| LinkerError::PathError(path.to_owned()))?;
let filename_str = path
.file_name()
.expect("path must have a filename")
.to_str()
.ok_or_else(|| LinkerError::PathError(path.to_owned()))?;
self.args.push("-dylib".to_owned());
self.args.push("-lsystem".to_owned());
self.args.push("-o".to_owned());
self.args.push(path_str.to_owned());
self.args.push("-install_name".to_owned());
self.args.push(filename_str.to_owned());
Ok(())
}
fn finalize(&mut self) -> Result<(), LinkerError> {
mun_lld::link(mun_lld::LldFlavor::MachO, &self.args)
.ok()
.map_err(LinkerError::LinkError)
}
}
struct MsvcLinker {
args: Vec<String>,
}
impl MsvcLinker {
fn new(_target: &spec::Target) -> Self {
MsvcLinker {
args: Vec::default(),
}
}
}
impl Linker for MsvcLinker {
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) -> 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(format!("/EXPORT:{}", abi::GET_INFO_FN_NAME));
self.args
.push(format!("/EXPORT:{}", abi::GET_VERSION_FN_NAME));
self.args
.push(format!("/EXPORT:{}", abi::SET_ALLOCATOR_HANDLE_FN_NAME));
self.args.push(format!("/IMPLIB:{}", dll_lib_path_str));
self.args.push(format!("/OUT:{}", dll_path_str));
Ok(())
}
fn finalize(&mut self) -> Result<(), LinkerError> {
mun_lld::link(mun_lld::LldFlavor::Coff, &self.args)
.ok()
.map_err(LinkerError::LinkError)
}
}