Merge pull request #3682 from rtfeldman/windows-linking

Windows linking
This commit is contained in:
Richard Feldman 2022-08-03 15:33:22 -04:00 committed by GitHub
commit afb857d3e9
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 200 additions and 59 deletions

2
.gitignore vendored
View file

@ -8,6 +8,8 @@ zig-cache
*.obj
*.tmp
*.wasm
*.exe
*.pdb
# llvm human-readable output
*.ll

View file

@ -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

View file

@ -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")]

View file

@ -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(

View file

@ -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,
},
}
}

View file

@ -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))
}

View file

@ -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,
}
}
}

View file

@ -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)