Cross-compile build works on Windows
This commit is contained in:
4
Cargo.lock
generated
4
Cargo.lock
generated
@@ -63,7 +63,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "anvil"
|
name = "anvil"
|
||||||
version = "1.0.0"
|
version = "1.0.1-alpha1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"assert_cmd",
|
"assert_cmd",
|
||||||
@@ -1074,7 +1074,7 @@ checksum = "d7249219f66ced02969388cf2bb044a09756a083d0fab1e566056b04d9fbcaa5"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "xtask"
|
name = "xtask"
|
||||||
version = "0.1.0"
|
version = "0.1.1-alpha1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"clap",
|
"clap",
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ default-members = ["."]
|
|||||||
|
|
||||||
[package]
|
[package]
|
||||||
name = "anvil"
|
name = "anvil"
|
||||||
version = "1.0.0"
|
version = "1.0.1-alpha1"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
authors = ["Eric Ratliff <eric@nxlearn.net>"]
|
authors = ["Eric Ratliff <eric@nxlearn.net>"]
|
||||||
description = "Arduino project generator and build tool - forges clean embedded projects"
|
description = "Arduino project generator and build tool - forges clean embedded projects"
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "xtask"
|
name = "xtask"
|
||||||
version = "0.1.0"
|
version = "0.1.1-alpha1"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
publish = false
|
publish = false
|
||||||
|
|
||||||
|
|||||||
@@ -63,7 +63,13 @@ struct Target {
|
|||||||
archive_ext: &'static str,
|
archive_ext: &'static str,
|
||||||
}
|
}
|
||||||
|
|
||||||
const TARGETS: &[Target] = &[
|
const CROSS_TARGETS: &[Target] = &[
|
||||||
|
Target {
|
||||||
|
name: "FreeBSD x86_64",
|
||||||
|
triple: "x86_64-unknown-freebsd",
|
||||||
|
binary_name: "anvil",
|
||||||
|
archive_ext: "tar.gz",
|
||||||
|
},
|
||||||
Target {
|
Target {
|
||||||
name: "Linux x86_64",
|
name: "Linux x86_64",
|
||||||
triple: "x86_64-unknown-linux-gnu",
|
triple: "x86_64-unknown-linux-gnu",
|
||||||
@@ -78,6 +84,18 @@ const TARGETS: &[Target] = &[
|
|||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
|
/// Returns cross-compile targets for the current host, skipping the native target.
|
||||||
|
fn cross_targets_for_host(host: &str) -> Vec<&'static Target> {
|
||||||
|
CROSS_TARGETS.iter().filter(|t| {
|
||||||
|
match host {
|
||||||
|
"freebsd" => t.triple != "x86_64-unknown-freebsd",
|
||||||
|
"linux" => t.triple != "x86_64-unknown-linux-gnu",
|
||||||
|
"windows" => t.triple != "x86_64-pc-windows-gnu",
|
||||||
|
_ => true,
|
||||||
|
}
|
||||||
|
}).collect()
|
||||||
|
}
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
// Helpers
|
// Helpers
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
@@ -179,16 +197,12 @@ fn read_version(workspace: &Path) -> Result<String> {
|
|||||||
|
|
||||||
/// Detect host OS
|
/// Detect host OS
|
||||||
fn host_os() -> &'static str {
|
fn host_os() -> &'static str {
|
||||||
if cfg!(target_os = "freebsd") {
|
match std::env::consts::OS {
|
||||||
"freebsd"
|
"freebsd" => "freebsd",
|
||||||
} else if cfg!(target_os = "linux") {
|
"linux" => "linux",
|
||||||
"linux"
|
"macos" => "macos",
|
||||||
} else if cfg!(target_os = "macos") {
|
"windows" => "windows",
|
||||||
"macos"
|
_ => "unknown",
|
||||||
} else if cfg!(target_os = "windows") {
|
|
||||||
"windows"
|
|
||||||
} else {
|
|
||||||
"unknown"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -242,13 +256,21 @@ fn package_binary(
|
|||||||
}
|
}
|
||||||
"zip" => {
|
"zip" => {
|
||||||
let archive = artifacts.join(format!("{}.zip", archive_name));
|
let archive = artifacts.join(format!("{}.zip", archive_name));
|
||||||
// -j = junk paths (store just the filename, not the full path)
|
if cfg!(windows) {
|
||||||
|
let ps_cmd = format!(
|
||||||
|
"Compress-Archive -Path '{}' -DestinationPath '{}' -Force",
|
||||||
|
binary_path.display(),
|
||||||
|
archive.display()
|
||||||
|
);
|
||||||
|
run("powershell", &["-NoProfile", "-Command", &ps_cmd], None)?;
|
||||||
|
} else {
|
||||||
run(
|
run(
|
||||||
"zip",
|
"zip",
|
||||||
&["-q", "-j", archive.to_str().unwrap(), binary_path.to_str().unwrap()],
|
&["-q", "-j", archive.to_str().unwrap(), binary_path.to_str().unwrap()],
|
||||||
None,
|
None,
|
||||||
)?;
|
)?;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
_ => bail!("Unknown archive format: {}", archive_ext),
|
_ => bail!("Unknown archive format: {}", archive_ext),
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -342,18 +364,31 @@ fn run_check(_workspace: &Path) -> Result<bool> {
|
|||||||
ok(&format!("zig {}", ver));
|
ok(&format!("zig {}", ver));
|
||||||
} else {
|
} else {
|
||||||
warn("zig -- not found");
|
warn("zig -- not found");
|
||||||
println!(" {}", "sudo pkg install zig (FreeBSD) or apt install zig (Linux)");
|
println!(" {}", match host_os() {
|
||||||
|
"freebsd" => "sudo pkg install zig",
|
||||||
|
"linux" => "sudo apt install zig (or: sudo dnf install zig)",
|
||||||
|
"windows" => "winget install zig.zig (or: choco install zig)",
|
||||||
|
_ => "see https://ziglang.org/download/",
|
||||||
|
});
|
||||||
missing += 1;
|
missing += 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
// zip (for Windows archive)
|
// zip -- not needed on Windows (PowerShell Compress-Archive is built in)
|
||||||
|
if !cfg!(windows) {
|
||||||
if cmd_exists("zip") {
|
if cmd_exists("zip") {
|
||||||
ok("zip");
|
ok("zip");
|
||||||
} else {
|
} else {
|
||||||
warn("zip -- not found");
|
warn("zip -- not found");
|
||||||
println!(" {}", "sudo pkg install zip (FreeBSD) or apt install zip (Linux)");
|
println!(" {}", match host_os() {
|
||||||
|
"freebsd" => "sudo pkg install zip",
|
||||||
|
"linux" => "sudo apt install zip",
|
||||||
|
_ => "install zip",
|
||||||
|
});
|
||||||
missing += 1;
|
missing += 1;
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
ok("zip (PowerShell Compress-Archive -- built in)");
|
||||||
|
}
|
||||||
|
|
||||||
// cargo-zigbuild
|
// cargo-zigbuild
|
||||||
if cmd_exists("cargo-zigbuild") {
|
if cmd_exists("cargo-zigbuild") {
|
||||||
@@ -370,7 +405,7 @@ fn run_check(_workspace: &Path) -> Result<bool> {
|
|||||||
if cmd_exists("rustup") {
|
if cmd_exists("rustup") {
|
||||||
let targets = run_captured("rustup", &["target", "list", "--installed"])
|
let targets = run_captured("rustup", &["target", "list", "--installed"])
|
||||||
.unwrap_or_default();
|
.unwrap_or_default();
|
||||||
for t in TARGETS {
|
for t in cross_targets_for_host(host_os()) {
|
||||||
if targets.contains(t.triple) {
|
if targets.contains(t.triple) {
|
||||||
ok(&format!("rustup target: {}", t.triple));
|
ok(&format!("rustup target: {}", t.triple));
|
||||||
} else {
|
} else {
|
||||||
@@ -387,13 +422,13 @@ fn run_check(_workspace: &Path) -> Result<bool> {
|
|||||||
|
|
||||||
// Host platform note
|
// Host platform note
|
||||||
let host = host_os();
|
let host = host_os();
|
||||||
if host == "linux" {
|
if host == "windows" {
|
||||||
println!();
|
println!();
|
||||||
warn("Running on Linux -- FreeBSD binary cannot be produced.");
|
ok("Windows host -- cargo-zigbuild produces Linux, FreeBSD, and Windows binaries");
|
||||||
println!(" Boot into FreeBSD and run 'cargo xtask' for the FreeBSD binary.");
|
} else if host == "linux" {
|
||||||
} else if host != "freebsd" {
|
|
||||||
println!();
|
println!();
|
||||||
warn(&format!("Unsupported host OS: {}", host));
|
ok("Linux host -- cargo-zigbuild produces FreeBSD and Windows binaries");
|
||||||
|
println!(" (native Linux binary built with plain cargo)");
|
||||||
}
|
}
|
||||||
|
|
||||||
println!();
|
println!();
|
||||||
@@ -410,6 +445,38 @@ fn run_check(_workspace: &Path) -> Result<bool> {
|
|||||||
// Fix
|
// Fix
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
/// On Windows, reads the current Machine + User PATH from the registry and
|
||||||
|
/// returns a merged PATH string so the current process can find newly
|
||||||
|
/// installed tools without requiring a new terminal session.
|
||||||
|
#[cfg(windows)]
|
||||||
|
fn refresh_windows_path() -> Result<String> {
|
||||||
|
use std::process::Command;
|
||||||
|
// Read system PATH
|
||||||
|
let sys = Command::new("powershell")
|
||||||
|
.args(&[
|
||||||
|
"-NoProfile", "-Command",
|
||||||
|
"[System.Environment]::GetEnvironmentVariable('PATH','Machine')"
|
||||||
|
])
|
||||||
|
.output()
|
||||||
|
.map(|o| String::from_utf8_lossy(&o.stdout).trim().to_string())
|
||||||
|
.unwrap_or_default();
|
||||||
|
// Read user PATH
|
||||||
|
let usr = Command::new("powershell")
|
||||||
|
.args(&[
|
||||||
|
"-NoProfile", "-Command",
|
||||||
|
"[System.Environment]::GetEnvironmentVariable('PATH','User')"
|
||||||
|
])
|
||||||
|
.output()
|
||||||
|
.map(|o| String::from_utf8_lossy(&o.stdout).trim().to_string())
|
||||||
|
.unwrap_or_default();
|
||||||
|
Ok(format!("{};{}", sys, usr))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(not(windows))]
|
||||||
|
fn refresh_windows_path() -> Result<String> {
|
||||||
|
bail!("refresh_windows_path called on non-Windows platform")
|
||||||
|
}
|
||||||
|
|
||||||
fn run_fix() -> Result<()> {
|
fn run_fix() -> Result<()> {
|
||||||
header("Installing dependencies");
|
header("Installing dependencies");
|
||||||
|
|
||||||
@@ -432,12 +499,36 @@ fn run_fix() -> Result<()> {
|
|||||||
bail!("Could not install zig automatically. Install manually: https://ziglang.org/download/");
|
bail!("Could not install zig automatically. Install manually: https://ziglang.org/download/");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
"windows" => {
|
||||||
|
if cmd_exists("winget") {
|
||||||
|
run("winget", &["install", "--id", "zig.zig", "-e", "--silent"], None)?;
|
||||||
|
// Refresh PATH in the current process so zig is findable immediately.
|
||||||
|
// winget updates the registry but the current session won't see it
|
||||||
|
// without this -- otherwise the user needs to open a new terminal.
|
||||||
|
if let Ok(new_path) = refresh_windows_path() {
|
||||||
|
unsafe {
|
||||||
|
std::env::set_var("PATH", &new_path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !cmd_exists("zig") {
|
||||||
|
println!();
|
||||||
|
warn("zig was installed but is not yet on PATH.");
|
||||||
|
println!(" Open a new terminal and run: cargo xtask");
|
||||||
|
println!(" (Windows PATH updates require a new shell session)");
|
||||||
|
}
|
||||||
|
} else if cmd_exists("choco") {
|
||||||
|
run("choco", &["install", "zig", "-y"], None)?;
|
||||||
|
} else {
|
||||||
|
bail!("Install zig via winget: winget install zig.zig\n Or download from: https://ziglang.org/download/");
|
||||||
|
}
|
||||||
|
}
|
||||||
_ => bail!("Cannot auto-install zig on {}. Install manually: https://ziglang.org/download/", host),
|
_ => bail!("Cannot auto-install zig on {}. Install manually: https://ziglang.org/download/", host),
|
||||||
}
|
}
|
||||||
ok("zig installed");
|
ok("zig installed");
|
||||||
}
|
}
|
||||||
|
|
||||||
// zip (for Windows archive packaging)
|
// zip (not needed on Windows -- PowerShell Compress-Archive is built in)
|
||||||
|
if host != "windows" {
|
||||||
if cmd_exists("zip") {
|
if cmd_exists("zip") {
|
||||||
ok("zip already installed");
|
ok("zip already installed");
|
||||||
} else {
|
} else {
|
||||||
@@ -457,6 +548,9 @@ fn run_fix() -> Result<()> {
|
|||||||
}
|
}
|
||||||
ok("zip installed");
|
ok("zip installed");
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
ok("zip not needed on Windows (using PowerShell Compress-Archive)");
|
||||||
|
}
|
||||||
|
|
||||||
// cargo-zigbuild
|
// cargo-zigbuild
|
||||||
if cmd_exists("cargo-zigbuild") {
|
if cmd_exists("cargo-zigbuild") {
|
||||||
@@ -471,7 +565,7 @@ fn run_fix() -> Result<()> {
|
|||||||
if cmd_exists("rustup") {
|
if cmd_exists("rustup") {
|
||||||
let installed = run_captured("rustup", &["target", "list", "--installed"])
|
let installed = run_captured("rustup", &["target", "list", "--installed"])
|
||||||
.unwrap_or_default();
|
.unwrap_or_default();
|
||||||
for t in TARGETS {
|
for t in cross_targets_for_host(host) {
|
||||||
if installed.contains(t.triple) {
|
if installed.contains(t.triple) {
|
||||||
ok(&format!("rustup target {} already installed", t.triple));
|
ok(&format!("rustup target {} already installed", t.triple));
|
||||||
} else {
|
} else {
|
||||||
@@ -496,7 +590,7 @@ fn run_fix() -> Result<()> {
|
|||||||
fn run_clean(workspace: &Path) -> Result<()> {
|
fn run_clean(workspace: &Path) -> Result<()> {
|
||||||
header("Cleaning cross-compile artifacts");
|
header("Cleaning cross-compile artifacts");
|
||||||
|
|
||||||
for t in TARGETS {
|
for t in CROSS_TARGETS {
|
||||||
let target_dir = workspace.join("target").join(t.triple);
|
let target_dir = workspace.join("target").join(t.triple);
|
||||||
if target_dir.exists() {
|
if target_dir.exists() {
|
||||||
info(&format!("Removing target/{}/...", t.triple));
|
info(&format!("Removing target/{}/...", t.triple));
|
||||||
@@ -538,56 +632,53 @@ fn run_build(workspace: &Path, version: &str) -> Result<()> {
|
|||||||
}
|
}
|
||||||
fs::create_dir_all(&artifacts)?;
|
fs::create_dir_all(&artifacts)?;
|
||||||
|
|
||||||
// -- FreeBSD native ----------------------------------------------------
|
// -- Native binary (current host) -------------------------------------
|
||||||
if host == "freebsd" {
|
let (native_triple, native_name, native_ext) = match host {
|
||||||
header("FreeBSD x86_64");
|
"freebsd" => ("freebsd-x86_64", "anvil", "tar.gz"),
|
||||||
|
"linux" => ("linux-x86_64", "anvil", "tar.gz"),
|
||||||
|
"windows" => ("windows-x86_64", "anvil.exe", "zip"),
|
||||||
|
_ => ("unknown", "anvil", "tar.gz"),
|
||||||
|
};
|
||||||
|
header(&format!("{} (native)", native_name.to_uppercase().replace("ANVIL", &format!("{} x86_64", host.to_uppercase().replace("FREEBSD", "FreeBSD").replace("LINUX", "Linux").replace("WINDOWS", "Windows")))));
|
||||||
info("Building...");
|
info("Building...");
|
||||||
run("cargo", &["build", "--release"], Some(workspace))?;
|
run("cargo", &["build", "--release"], Some(workspace))?;
|
||||||
let archive_name = format!("anvil-{}-freebsd-x86_64", version);
|
let archive_name = format!("anvil-{}-{}", version, native_triple);
|
||||||
|
if native_ext == "zip" {
|
||||||
|
// Windows native -- package exe directly
|
||||||
|
let binary_path = workspace.join("target").join("release").join(native_name);
|
||||||
|
package_binary(workspace, "", native_name, &archive_name, "zip")?;
|
||||||
|
let _ = binary_path; // used inside package_binary
|
||||||
|
} else {
|
||||||
package_native(workspace, &archive_name)?;
|
package_native(workspace, &archive_name)?;
|
||||||
ok(&format!("{}.tar.gz", archive_name));
|
|
||||||
} else if host == "linux" {
|
|
||||||
println!();
|
|
||||||
warn("Running on Linux -- FreeBSD binary cannot be produced.");
|
|
||||||
println!(" Boot into FreeBSD and run 'cargo xtask' for the FreeBSD binary.");
|
|
||||||
println!();
|
|
||||||
}
|
}
|
||||||
|
ok(&format!("{}.{}", archive_name, native_ext));
|
||||||
|
|
||||||
// -- Linux x86_64 via cargo-zigbuild -----------------------------------
|
// -- Cross-compile targets via cargo-zigbuild --------------------------
|
||||||
header("Linux x86_64 (via cargo-zigbuild)");
|
for t in cross_targets_for_host(host) {
|
||||||
|
let label = match t.triple {
|
||||||
|
"x86_64-unknown-freebsd" => "FreeBSD x86_64",
|
||||||
|
"x86_64-unknown-linux-gnu" => "Linux x86_64",
|
||||||
|
"x86_64-pc-windows-gnu" => "Windows x86_64",
|
||||||
|
other => other,
|
||||||
|
};
|
||||||
|
header(&format!("{} (via cargo-zigbuild)", label));
|
||||||
info("Building...");
|
info("Building...");
|
||||||
run(
|
run(
|
||||||
"cargo",
|
"cargo",
|
||||||
&["zigbuild", "--release", "--target", "x86_64-unknown-linux-gnu"],
|
&["zigbuild", "--release", "--target", t.triple],
|
||||||
Some(workspace),
|
Some(workspace),
|
||||||
)?;
|
)?;
|
||||||
let archive_name = format!("anvil-{}-linux-x86_64", version);
|
let suffix = t.triple
|
||||||
package_binary(
|
.replace("x86_64-unknown-", "")
|
||||||
workspace,
|
.replace("x86_64-pc-", "")
|
||||||
"x86_64-unknown-linux-gnu",
|
.replace("-gnu", "")
|
||||||
"anvil",
|
.replace("freebsd", "freebsd-x86_64")
|
||||||
&archive_name,
|
.replace("linux", "linux-x86_64")
|
||||||
"tar.gz",
|
.replace("windows", "windows-x86_64");
|
||||||
)?;
|
let archive_name = format!("anvil-{}-{}", version, suffix);
|
||||||
ok(&format!("{}.tar.gz", archive_name));
|
package_binary(workspace, t.triple, t.binary_name, &archive_name, t.archive_ext)?;
|
||||||
|
ok(&format!("{}.{}", archive_name, t.archive_ext));
|
||||||
// -- Windows x86_64 via cargo-zigbuild ---------------------------------
|
}
|
||||||
header("Windows x86_64 (via cargo-zigbuild)");
|
|
||||||
info("Building...");
|
|
||||||
run(
|
|
||||||
"cargo",
|
|
||||||
&["zigbuild", "--release", "--target", "x86_64-pc-windows-gnu"],
|
|
||||||
Some(workspace),
|
|
||||||
)?;
|
|
||||||
let archive_name = format!("anvil-{}-windows-x86_64", version);
|
|
||||||
package_binary(
|
|
||||||
workspace,
|
|
||||||
"x86_64-pc-windows-gnu",
|
|
||||||
"anvil.exe",
|
|
||||||
&archive_name,
|
|
||||||
"zip",
|
|
||||||
)?;
|
|
||||||
ok(&format!("{}.zip", archive_name));
|
|
||||||
|
|
||||||
// -- Checksums ---------------------------------------------------------
|
// -- Checksums ---------------------------------------------------------
|
||||||
header("Checksums");
|
header("Checksums");
|
||||||
|
|||||||
Reference in New Issue
Block a user