mirror of
https://github.com/roc-lang/roc.git
synced 2025-07-24 15:03:46 +00:00
Merge pull request #3682 from rtfeldman/windows-linking
Windows linking
This commit is contained in:
commit
afb857d3e9
8 changed files with 200 additions and 59 deletions
2
.gitignore
vendored
2
.gitignore
vendored
|
@ -8,6 +8,8 @@ zig-cache
|
|||
*.obj
|
||||
*.tmp
|
||||
*.wasm
|
||||
*.exe
|
||||
*.pdb
|
||||
|
||||
# llvm human-readable output
|
||||
*.ll
|
||||
|
|
|
@ -74,30 +74,27 @@ pub fn build_file<'a>(
|
|||
// > Non-Emscripten WebAssembly hasn't implemented __builtin_return_address
|
||||
//
|
||||
// and zig does not currently emit `.a` webassembly static libraries
|
||||
let host_extension = if emit_wasm {
|
||||
if matches!(opt_level, OptLevel::Development) {
|
||||
"wasm"
|
||||
} else {
|
||||
"zig"
|
||||
let (host_extension, app_extension, extension) = {
|
||||
use roc_target::OperatingSystem::*;
|
||||
|
||||
match roc_target::OperatingSystem::from(target.operating_system) {
|
||||
Wasi => {
|
||||
if matches!(opt_level, OptLevel::Development) {
|
||||
("wasm", "wasm", Some("wasm"))
|
||||
} else {
|
||||
("zig", "bc", Some("wasm"))
|
||||
}
|
||||
}
|
||||
Unix => ("o", "o", None),
|
||||
Windows => ("obj", "obj", Some("exe")),
|
||||
}
|
||||
} else {
|
||||
"o"
|
||||
};
|
||||
let app_extension = if emit_wasm {
|
||||
if matches!(opt_level, OptLevel::Development) {
|
||||
"wasm"
|
||||
} else {
|
||||
"bc"
|
||||
}
|
||||
} else {
|
||||
"o"
|
||||
};
|
||||
|
||||
let cwd = app_module_path.parent().unwrap();
|
||||
let mut binary_path = cwd.join(&*loaded.output_path); // TODO should join ".exe" on Windows
|
||||
let mut binary_path = cwd.join(&*loaded.output_path);
|
||||
|
||||
if emit_wasm {
|
||||
binary_path.set_extension("wasm");
|
||||
if let Some(extension) = extension {
|
||||
binary_path.set_extension(extension);
|
||||
}
|
||||
|
||||
let host_input_path = cwd
|
||||
|
|
|
@ -732,14 +732,18 @@ fn roc_run<'a, I: IntoIterator<Item = &'a OsStr>>(
|
|||
// since the process is about to exit anyway.
|
||||
std::mem::forget(arena);
|
||||
|
||||
if cfg!(target_family = "unix") {
|
||||
#[cfg(target_family = "unix")]
|
||||
{
|
||||
use std::os::unix::ffi::OsStrExt;
|
||||
|
||||
run_with_wasmer(
|
||||
generated_filename,
|
||||
args.into_iter().map(|os_str| os_str.as_bytes()),
|
||||
);
|
||||
} else {
|
||||
}
|
||||
|
||||
#[cfg(not(target_family = "unix"))]
|
||||
{
|
||||
run_with_wasmer(
|
||||
generated_filename,
|
||||
args.into_iter().map(|os_str| {
|
||||
|
@ -756,6 +760,7 @@ fn roc_run<'a, I: IntoIterator<Item = &'a OsStr>>(
|
|||
}
|
||||
}
|
||||
|
||||
#[cfg(target_family = "unix")]
|
||||
fn make_argv_envp<'a, I: IntoIterator<Item = S>, S: AsRef<OsStr>>(
|
||||
arena: &'a Bump,
|
||||
executable: &ExecutableFile,
|
||||
|
@ -889,11 +894,28 @@ impl ExecutableFile {
|
|||
let path_cstring = CString::new(path.as_os_str().as_bytes()).unwrap();
|
||||
libc::execve(path_cstring.as_ptr().cast(), argv.as_ptr(), envp.as_ptr())
|
||||
}
|
||||
|
||||
#[cfg(all(target_family = "windows"))]
|
||||
ExecutableFile::OnDisk(_, path) => {
|
||||
use std::process::Command;
|
||||
|
||||
let _ = argv;
|
||||
let _ = envp;
|
||||
|
||||
let mut command = Command::new(path);
|
||||
|
||||
let output = command.output().unwrap();
|
||||
|
||||
println!("{}", String::from_utf8_lossy(&output.stdout));
|
||||
|
||||
std::process::exit(0)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// with Expect
|
||||
#[cfg(target_family = "unix")]
|
||||
unsafe fn roc_run_native_debug(
|
||||
executable: ExecutableFile,
|
||||
argv: &[*const c_char],
|
||||
|
@ -1033,35 +1055,75 @@ fn roc_run_executable_file_path(binary_bytes: &mut [u8]) -> std::io::Result<Exec
|
|||
Ok(ExecutableFile::OnDisk(temp_dir, app_path_buf))
|
||||
}
|
||||
|
||||
#[cfg(all(target_family = "windows"))]
|
||||
fn roc_run_executable_file_path(binary_bytes: &mut [u8]) -> std::io::Result<ExecutableFile> {
|
||||
use std::fs::OpenOptions;
|
||||
use std::io::Write;
|
||||
|
||||
let temp_dir = tempfile::tempdir()?;
|
||||
|
||||
// We have not found a way to use a virtual file on non-Linux OSes.
|
||||
// Hence we fall back to just writing the file to the file system, and using that file.
|
||||
let app_path_buf = temp_dir.path().join("roc_app_binary.exe");
|
||||
let mut file = OpenOptions::new()
|
||||
.create(true)
|
||||
.write(true)
|
||||
//.mode(0o777) // create the file as executable
|
||||
.open(&app_path_buf)?;
|
||||
|
||||
file.write_all(binary_bytes)?;
|
||||
|
||||
// We store the TempDir in this variant alongside the path to the executable,
|
||||
// so that the TempDir doesn't get dropped until after we're done with the path.
|
||||
// If we didn't do that, then the tempdir would potentially get deleted by the
|
||||
// TempDir's Drop impl before the file had been executed.
|
||||
Ok(ExecutableFile::OnDisk(temp_dir, app_path_buf))
|
||||
}
|
||||
|
||||
/// Run on the native OS (not on wasm)
|
||||
#[cfg(not(target_family = "unix"))]
|
||||
fn roc_run_native<I: IntoIterator<Item = S>, S: AsRef<OsStr>>(
|
||||
_arena: Bump, // This should be passed an owned value, not a reference, so we can usefully mem::forget it!
|
||||
arena: Bump, // This should be passed an owned value, not a reference, so we can usefully mem::forget it!
|
||||
opt_level: OptLevel,
|
||||
_args: I,
|
||||
_binary_bytes: &mut [u8],
|
||||
binary_bytes: &mut [u8],
|
||||
_expectations: VecMap<ModuleId, Expectations>,
|
||||
_interns: Interns,
|
||||
) -> io::Result<i32> {
|
||||
todo!("TODO support running roc programs on non-UNIX targets");
|
||||
// let mut cmd = std::process::Command::new(&binary_path);
|
||||
use bumpalo::collections::CollectIn;
|
||||
|
||||
// // Run the compiled app
|
||||
// let exit_status = cmd
|
||||
// .spawn()
|
||||
// .unwrap_or_else(|err| panic!("Failed to run app after building it: {:?}", err))
|
||||
// .wait()
|
||||
// .expect("TODO gracefully handle block_on failing when `roc` spawns a subprocess for the compiled app");
|
||||
unsafe {
|
||||
let executable = roc_run_executable_file_path(binary_bytes)?;
|
||||
|
||||
// // `roc [FILE]` exits with the same status code as the app it ran.
|
||||
// //
|
||||
// // If you want to know whether there were compilation problems
|
||||
// // via status code, use either `roc build` or `roc check` instead!
|
||||
// match exit_status.code() {
|
||||
// Some(code) => Ok(code),
|
||||
// None => {
|
||||
// todo!("TODO gracefully handle the `roc [FILE]` subprocess terminating with a signal.");
|
||||
// }
|
||||
// }
|
||||
// TODO forward the arguments
|
||||
// let (argv_cstrings, envp_cstrings) = make_argv_envp(&arena, &executable, args);
|
||||
let argv_cstrings = bumpalo::vec![ in &arena; CString::default()];
|
||||
let envp_cstrings = bumpalo::vec![ in &arena; CString::default()];
|
||||
|
||||
let argv: bumpalo::collections::Vec<*const c_char> = argv_cstrings
|
||||
.iter()
|
||||
.map(|s| s.as_ptr())
|
||||
.chain([std::ptr::null()])
|
||||
.collect_in(&arena);
|
||||
|
||||
let envp: bumpalo::collections::Vec<*const c_char> = envp_cstrings
|
||||
.iter()
|
||||
.map(|s| s.as_ptr())
|
||||
.chain([std::ptr::null()])
|
||||
.collect_in(&arena);
|
||||
|
||||
match opt_level {
|
||||
OptLevel::Development => {
|
||||
// roc_run_native_debug(executable, &argv, &envp, expectations, interns)
|
||||
todo!()
|
||||
}
|
||||
OptLevel::Normal | OptLevel::Size | OptLevel::Optimize => {
|
||||
roc_run_native_fast(executable, &argv, &envp);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(1)
|
||||
}
|
||||
|
||||
#[cfg(feature = "run-wasm32")]
|
||||
|
|
|
@ -120,6 +120,7 @@ pub fn build_zig_host_native(
|
|||
.env_clear()
|
||||
.env("PATH", env_path)
|
||||
.env("HOME", env_home);
|
||||
|
||||
if let Some(shared_lib_path) = shared_lib_path {
|
||||
command.args(&[
|
||||
"build-exe",
|
||||
|
@ -130,6 +131,7 @@ pub fn build_zig_host_native(
|
|||
} else {
|
||||
command.args(&["build-obj", "-fPIC"]);
|
||||
}
|
||||
|
||||
command.args(&[
|
||||
zig_host_src,
|
||||
emit_bin,
|
||||
|
@ -160,6 +162,7 @@ pub fn build_zig_host_native(
|
|||
} else if matches!(opt_level, OptLevel::Size) {
|
||||
command.args(&["-O", "ReleaseSmall"]);
|
||||
}
|
||||
|
||||
command.output().unwrap()
|
||||
}
|
||||
|
||||
|
@ -425,7 +428,11 @@ pub fn rebuild_host(
|
|||
host_input_path.with_file_name(if shared_lib_path.is_some() {
|
||||
"dynhost"
|
||||
} else {
|
||||
"host.o"
|
||||
match roc_target::OperatingSystem::from(target.operating_system) {
|
||||
roc_target::OperatingSystem::Windows => "host.obj",
|
||||
roc_target::OperatingSystem::Unix => "host.o",
|
||||
roc_target::OperatingSystem::Wasi => "host.o",
|
||||
}
|
||||
})
|
||||
};
|
||||
|
||||
|
@ -1095,11 +1102,58 @@ fn link_wasm32(
|
|||
|
||||
fn link_windows(
|
||||
_target: &Triple,
|
||||
_output_path: PathBuf,
|
||||
_input_paths: &[&str],
|
||||
_link_type: LinkType,
|
||||
output_path: PathBuf,
|
||||
input_paths: &[&str],
|
||||
link_type: LinkType,
|
||||
) -> io::Result<(Child, PathBuf)> {
|
||||
todo!("Add windows support to the surgical linker. See issue #2608.")
|
||||
let zig_str_path = find_zig_str_path();
|
||||
|
||||
match link_type {
|
||||
LinkType::Dylib => {
|
||||
let child = Command::new(&zig_executable())
|
||||
.args(&["build-lib"])
|
||||
.args(input_paths)
|
||||
.args([
|
||||
"-lc",
|
||||
&format!("-femit-bin={}", output_path.to_str().unwrap()),
|
||||
"-target",
|
||||
"native",
|
||||
"--pkg-begin",
|
||||
"str",
|
||||
zig_str_path.to_str().unwrap(),
|
||||
"--pkg-end",
|
||||
"--strip",
|
||||
"-O",
|
||||
"Debug",
|
||||
"-dynamic",
|
||||
])
|
||||
.spawn()?;
|
||||
|
||||
Ok((child, output_path))
|
||||
}
|
||||
LinkType::Executable => {
|
||||
let child = Command::new(&zig_executable())
|
||||
.args(&["build-exe"])
|
||||
.args(input_paths)
|
||||
.args([
|
||||
"-lc",
|
||||
&format!("-femit-bin={}", output_path.to_str().unwrap()),
|
||||
"-target",
|
||||
"native",
|
||||
"--pkg-begin",
|
||||
"str",
|
||||
zig_str_path.to_str().unwrap(),
|
||||
"--pkg-end",
|
||||
"--strip",
|
||||
"-O",
|
||||
"Debug",
|
||||
])
|
||||
.spawn()?;
|
||||
|
||||
Ok((child, output_path))
|
||||
}
|
||||
LinkType::None => todo!(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn llvm_module_to_dylib(
|
||||
|
|
|
@ -64,7 +64,7 @@ impl FloatWidth {
|
|||
}
|
||||
|
||||
pub const fn alignment_bytes(&self, target_info: TargetInfo) -> u32 {
|
||||
use roc_target::Architecture;
|
||||
use roc_target::Architecture::*;
|
||||
use FloatWidth::*;
|
||||
|
||||
// NOTE: this must never use mem::align_of, because that returns the alignment
|
||||
|
@ -73,8 +73,8 @@ impl FloatWidth {
|
|||
match self {
|
||||
F32 => 4,
|
||||
F64 | F128 => match target_info.architecture {
|
||||
Architecture::X86_64 | Architecture::Aarch64 | Architecture::Wasm32 => 8,
|
||||
Architecture::X86_32 | Architecture::Aarch32 => 4,
|
||||
X86_64 | Aarch64 | Wasm32 => 8,
|
||||
X86_32 | Aarch32 => 4,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
|
|
@ -866,7 +866,7 @@ where
|
|||
// the next character should not be an identifier character
|
||||
// to prevent treating `whence` or `iffy` as keywords
|
||||
match state.bytes().get(width) {
|
||||
Some(next) if *next == b' ' || *next == b'#' || *next == b'\n' => {
|
||||
Some(next) if *next == b' ' || *next == b'#' || *next == b'\n' || *next == b'\r' => {
|
||||
state = state.advance(width);
|
||||
Ok((MadeProgress, (), state))
|
||||
}
|
||||
|
|
|
@ -4,9 +4,31 @@
|
|||
|
||||
use strum_macros::{EnumCount, EnumIter};
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
pub enum OperatingSystem {
|
||||
Windows,
|
||||
Unix,
|
||||
Wasi,
|
||||
}
|
||||
|
||||
impl From<target_lexicon::OperatingSystem> for OperatingSystem {
|
||||
fn from(target: target_lexicon::OperatingSystem) -> Self {
|
||||
match target {
|
||||
target_lexicon::OperatingSystem::Windows => OperatingSystem::Windows,
|
||||
target_lexicon::OperatingSystem::Wasi => OperatingSystem::Wasi,
|
||||
target_lexicon::OperatingSystem::Linux => OperatingSystem::Unix,
|
||||
target_lexicon::OperatingSystem::MacOSX { .. } => OperatingSystem::Unix,
|
||||
target_lexicon::OperatingSystem::Darwin => OperatingSystem::Unix,
|
||||
target_lexicon::OperatingSystem::Unknown => OperatingSystem::Unix,
|
||||
other => unreachable!("unsupported operating system {:?}", other),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
pub struct TargetInfo {
|
||||
pub architecture: Architecture,
|
||||
pub operating_system: OperatingSystem,
|
||||
}
|
||||
|
||||
impl TargetInfo {
|
||||
|
@ -28,18 +50,21 @@ impl TargetInfo {
|
|||
pub const fn default_aarch64() -> Self {
|
||||
TargetInfo {
|
||||
architecture: Architecture::Aarch64,
|
||||
operating_system: OperatingSystem::Unix,
|
||||
}
|
||||
}
|
||||
|
||||
pub const fn default_x86_64() -> Self {
|
||||
TargetInfo {
|
||||
architecture: Architecture::X86_64,
|
||||
operating_system: OperatingSystem::Unix,
|
||||
}
|
||||
}
|
||||
|
||||
pub const fn default_wasm32() -> Self {
|
||||
TargetInfo {
|
||||
architecture: Architecture::Wasm32,
|
||||
operating_system: OperatingSystem::Wasi,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -47,14 +72,12 @@ impl TargetInfo {
|
|||
impl From<&target_lexicon::Triple> for TargetInfo {
|
||||
fn from(triple: &target_lexicon::Triple) -> Self {
|
||||
let architecture = Architecture::from(triple.architecture);
|
||||
let operating_system = OperatingSystem::from(triple.operating_system);
|
||||
|
||||
Self { architecture }
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Architecture> for TargetInfo {
|
||||
fn from(architecture: Architecture) -> Self {
|
||||
Self { architecture }
|
||||
Self {
|
||||
architecture,
|
||||
operating_system,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -3,7 +3,7 @@ use crate::types::{Env, Types};
|
|||
use bumpalo::Bump;
|
||||
use roc_load::{LoadedModule, LoadingProblem, Threading};
|
||||
use roc_reporting::report::RenderTarget;
|
||||
use roc_target::{Architecture, TargetInfo};
|
||||
use roc_target::{Architecture, OperatingSystem, TargetInfo};
|
||||
use std::fs::File;
|
||||
use std::io::{self, ErrorKind, Write};
|
||||
use std::path::{Path, PathBuf};
|
||||
|
@ -135,7 +135,10 @@ pub fn load_types(
|
|||
|
||||
let types_and_targets = Architecture::iter()
|
||||
.map(|arch| {
|
||||
let target_info = arch.into();
|
||||
let target_info = TargetInfo {
|
||||
architecture: arch,
|
||||
operating_system: OperatingSystem::Unix,
|
||||
};
|
||||
let mut env = Env::new(arena, subs, &mut interns, target_info);
|
||||
|
||||
(env.vars_to_types(variables.clone()), target_info)
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue