From 119eecad0dee7eab21ac3a45f5af8d57f544bd69 Mon Sep 17 00:00:00 2001 From: Erica Marigold Date: Sat, 2 Mar 2024 18:49:28 +0530 Subject: [PATCH] feat: cross compilation target system --- src/cli/build.rs | 123 ++++++++++++++++++++++++++++++++++--- src/standalone/metadata.rs | 4 +- 2 files changed, 117 insertions(+), 10 deletions(-) diff --git a/src/cli/build.rs b/src/cli/build.rs index efe1738..ba75d82 100644 --- a/src/cli/build.rs +++ b/src/cli/build.rs @@ -7,11 +7,23 @@ use std::{ use anyhow::{Context, Result}; use clap::Parser; use console::style; +use directories::BaseDirs; +use once_cell::sync::Lazy; use tokio::{fs, io::AsyncWriteExt as _}; use crate::standalone::metadata::Metadata; -/// Build a standalone executable +const TARGET_BASE_DIR: Lazy = Lazy::new(|| { + BaseDirs::new() + .unwrap() + .home_dir() + .to_path_buf() + .join(".lune") + .join("target") + .join(env!("CARGO_PKG_VERSION")) +}); + +// Build a standalone executable #[derive(Debug, Clone, Parser)] pub struct BuildCommand { /// The path to the input file @@ -21,35 +33,123 @@ pub struct BuildCommand { /// input file path with an executable extension #[clap(short, long)] pub output: Option, + + #[clap(short, long)] + pub target: Option, } impl BuildCommand { pub async fn run(self) -> Result { - let output_path = self + let mut output_path = self .output .unwrap_or_else(|| self.input.with_extension(EXE_EXTENSION)); let input_path_displayed = self.input.display(); - let output_path_displayed = output_path.display(); // Try to read the input file let source_code = fs::read(&self.input) .await .context("failed to read input file")?; + let base_exe_path = if let Some(target_inner) = self.target { + let target_exe_extension = get_target_exe_extension(target_inner.as_str()); + + let path = + TARGET_BASE_DIR.join(format!("lune-{}.{}", target_inner, target_exe_extension)); + + output_path = output_path.with_extension(target_exe_extension); + + if !TARGET_BASE_DIR.exists() { + fs::create_dir_all(TARGET_BASE_DIR.to_path_buf()).await?; + } + + if !path.exists() { + println!("Requested target hasn't been downloaded yet, attempting to download"); + + let release_url = format!( + "https://github.com/lune-org/lune/releases/download/v{ver}/lune-{ver}-{target}.zip", + ver = env!("CARGO_PKG_VERSION"), + target = target_inner + ); + + let target_full_display = release_url + .split("/") + .last() + .or(Some("lune-UNKNOWN-UNKNOWN")) + .unwrap() + .replace("zip", target_exe_extension); + + println!( + "{} target {}", + style("Download").green().bold(), + target_full_display + ); + + // Maybe we should use the custom net client used in `@lune/net` + let dl_req = match reqwest::get(release_url).await { + Err(_) => { + eprintln!( + " {} Unable to download base binary found for target `{}`", + style("Download").red().bold(), + target_inner, + ); + + return Ok(ExitCode::FAILURE); + } + Ok(resp) => { + let resp_status = resp.status(); + + if resp_status != 200 && !resp_status.is_redirection() { + eprintln!( + " {} No precompiled base binary found for target `{}`", + style("Download").red().bold(), + target_inner + ); + + println!("{}: {}", style("HINT").yellow(), style("Perhaps try providing a path to self-compiled target with the `--base` flag").italic()); + + return Ok(ExitCode::FAILURE); + } + + resp + } + }; + + fs::OpenOptions::new() + .write(true) + .create(true) + .open(&path) + .await? + .write_all(&dl_req.bytes().await?) + .await?; + + println!( + " {} {}", + style("Downloaded").blue(), + style(target_full_display).underlined() + ) + } + + Some(path) + } else { + None + }; + // Read the contents of the lune interpreter as our starting point println!( - "Creating standalone binary using {}", - style(input_path_displayed).green() + "{} standalone binary using {}", + style("Compile").green().bold(), + style(input_path_displayed).underlined() ); - let patched_bin = Metadata::create_env_patched_bin(source_code.clone()) + let patched_bin = Metadata::create_env_patched_bin(base_exe_path, source_code.clone()) .await .context("failed to create patched binary")?; // And finally write the patched binary to the output file println!( - "Writing standalone binary to {}", - style(output_path_displayed).blue() + " {} standalone binary to {}", + style("Write").blue().bold(), + style(output_path.display()).underlined() ); write_executable_file_to(output_path, patched_bin).await?; @@ -71,3 +171,10 @@ async fn write_executable_file_to(path: impl AsRef, bytes: impl AsRef<[u8] Ok(()) } + +fn get_target_exe_extension(target: &str) -> &str { + match target { + "windows-x86_64" => "exe", + _ => "bin", + } +} diff --git a/src/standalone/metadata.rs b/src/standalone/metadata.rs index 65249a9..01dc154 100644 --- a/src/standalone/metadata.rs +++ b/src/standalone/metadata.rs @@ -49,8 +49,8 @@ impl Metadata { /** Creates a patched standalone binary from the given script contents. */ - pub async fn create_env_patched_bin(script_contents: impl Into>) -> Result> { - let mut patched_bin = fs::read(CURRENT_EXE.to_path_buf()).await?; + pub async fn create_env_patched_bin(base_exe_path: Option, script_contents: impl Into>) -> Result> { + let mut patched_bin = fs::read(base_exe_path.unwrap_or(CURRENT_EXE.to_path_buf())).await?; // Compile luau input into bytecode let bytecode = LuaCompiler::new()