diff --git a/cli/src/build.rs b/cli/src/build.rs index edee82c6ce..4907da2068 100644 --- a/cli/src/build.rs +++ b/cli/src/build.rs @@ -1,5 +1,8 @@ use bumpalo::Bump; -use roc_build::{link::link, program}; +use roc_build::{ + link::{link, LinkType}, + program, +}; use roc_collections::all::MutMap; use roc_gen::llvm::build::OptLevel; use roc_load::file::LoadingProblem; @@ -96,6 +99,7 @@ pub fn build_file( binary_path.as_path(), host_input_path.as_path(), dest_filename.as_path(), + LinkType::Executable, ) .map_err(|_| { todo!("gracefully handle `rustc` failing to spawn."); diff --git a/compiler/build/src/link.rs b/compiler/build/src/link.rs index 8e2b98c5b9..4fdb913f54 100644 --- a/compiler/build/src/link.rs +++ b/compiler/build/src/link.rs @@ -4,11 +4,18 @@ use std::path::Path; use std::process::{Child, Command}; use target_lexicon::{Architecture, OperatingSystem, Triple}; +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +pub enum LinkType { + Executable, + Dylib, +} + pub fn link( target: &Triple, binary_path: &Path, host_input_path: &Path, dest_filename: &Path, + link_type: LinkType, ) -> io::Result { // TODO we should no longer need to do this once we have platforms on // a package repository, as we can then get precompiled hosts from there. @@ -19,12 +26,24 @@ pub fn link( architecture: Architecture::X86_64, operating_system: OperatingSystem::Linux, .. - } => link_linux(target, binary_path, host_input_path, dest_filename), + } => link_linux( + target, + binary_path, + host_input_path, + dest_filename, + link_type, + ), Triple { architecture: Architecture::X86_64, operating_system: OperatingSystem::Darwin, .. - } => link_macos(target, binary_path, host_input_path, dest_filename), + } => link_macos( + target, + binary_path, + host_input_path, + dest_filename, + link_type, + ), _ => panic!("TODO gracefully handle unsupported target: {:?}", target), } } @@ -121,12 +140,26 @@ fn link_linux( binary_path: &Path, host_input_path: &Path, dest_filename: &Path, + link_type: LinkType, ) -> io::Result { + let base_args = match link_type { + LinkType::Executable => Vec::new(), + // TODO: find a way to avoid using a vec! here - should theoretically be + // able to do this somehow using &[] but the borrow checker isn't having it. + // + // TODO: do we need to add a version number on to this? e.g. ".1" + // + // See https://software.intel.com/content/www/us/en/develop/articles/create-a-unix-including-linux-shared-library.html + // TODO: do we even need the -soname argument? + LinkType::Dylib => vec!["-shared", "-soname", binary_path.to_str().unwrap()], + }; + let libcrt_path = if Path::new("/usr/lib/x86_64-linux-gnu").exists() { Path::new("/usr/lib/x86_64-linux-gnu") } else { Path::new("/usr/lib") }; + let libgcc_path = if Path::new("/lib/x86_64-linux-gnu/libgcc_s.so.1").exists() { Path::new("/lib/x86_64-linux-gnu/libgcc_s.so.1") } else if Path::new("/usr/lib/x86_64-linux-gnu/libgcc_s.so.1").exists() { @@ -134,11 +167,13 @@ fn link_linux( } else { Path::new("/usr/lib/libgcc_s.so.1") }; + // NOTE: order of arguments to `ld` matters here! // The `-l` flags should go after the `.o` arguments Command::new("ld") // Don't allow LD_ env vars to affect this .env_clear() + .args(&base_args) .args(&[ "-arch", arch_str(target), @@ -174,13 +209,20 @@ fn link_macos( binary_path: &Path, host_input_path: &Path, dest_filename: &Path, + link_type: LinkType, ) -> io::Result { + let link_type_arg = match link_type { + LinkType::Executable => "-execute", + LinkType::Dylib => "-dylib", + }; + // NOTE: order of arguments to `ld` matters here! // The `-l` flags should go after the `.o` arguments Command::new("ld") // Don't allow LD_ env vars to affect this .env_clear() .args(&[ + link_type_arg, "-arch", target.architecture.to_string().as_str(), // Inputs