tx5-go-pion-turn 0.0.7-alpha

Rust process wrapper around tx5-go-pion-turn executable
Documentation
use std::process::Command;

fn main() {
    println!("cargo:rerun-if-changed=go.mod");
    println!("cargo:rerun-if-changed=go.sum");
    println!("cargo:rerun-if-changed=main.go");
    println!("cargo:rerun-if-changed=vendor.zip");

    let mut bin_path = std::path::PathBuf::from(
        std::env::var("OUT_DIR").expect("failed to read env OUT_DIR"),
    );
    bin_path.push("tx5-go-pion-turn");

    go_check_version();
    go_unzip_vendor();
    go_build(&bin_path);
}

fn go_check_version() {
    let go_version = Command::new("go")
        .arg("version")
        .output()
        .expect("error checking go version");
    assert_eq!(b"go version go", &go_version.stdout[0..13]);
    let ver: f64 = String::from_utf8_lossy(&go_version.stdout[13..17])
        .parse()
        .expect("error parsing go version");
    assert!(
        ver >= 1.18,
        "go executable version must be >= 1.18, got: {ver}",
    );
}

fn go_unzip_vendor() {
    let out_dir = std::env::var("OUT_DIR")
        .map(std::path::PathBuf::from)
        .expect("error reading out dir");

    let mut vendor_path = std::env::var("CARGO_MANIFEST_DIR")
        .map(std::path::PathBuf::from)
        .expect("error reading manifest dir");
    vendor_path.push("vendor.zip");

    zip::read::ZipArchive::new(
        std::fs::File::open(vendor_path)
            .expect("failed to open vendor zip file"),
    )
    .expect("failed to open vendor zip file")
    .extract(out_dir)
    .expect("failed to extract vendor zip file");
}

fn go_build(path: &std::path::Path) {
    let out_dir = std::env::var("OUT_DIR")
        .map(std::path::PathBuf::from)
        .expect("error reading out dir");

    let mut cache = out_dir.clone();
    cache.push("go-build");

    let manifest_path = std::env::var("CARGO_MANIFEST_DIR")
        .map(std::path::PathBuf::from)
        .expect("error reading manifest dir");

    let cp = |f: &'static str| {
        let mut a = manifest_path.clone();
        a.push(f);
        let mut b = out_dir.clone();
        b.push(f);
        std::fs::copy(a, b).expect("failed to copy go file");
    };

    cp("main.go");
    cp("go.sum");
    cp("go.mod");

    let mut cmd = Command::new("go");

    // add some cross-compilation translators:
    match std::env::var("CARGO_CFG_TARGET_ARCH").unwrap().as_str() {
        "arm" => {
            cmd.env("GOARCH", "arm");
        }
        "aarch64" => {
            cmd.env("GOARCH", "arm64");
        }
        "x86_64" => {
            cmd.env("GOARCH", "amd64");
        }
        "x86" => {
            cmd.env("GOARCH", "386");
        }
        _ => (),
    }

    // and for the os
    let tgt_os = std::env::var("CARGO_CFG_TARGET_OS").unwrap();
    match tgt_os.as_str() {
        "windows" => {
            cmd.env("GOOS", "windows");
        }
        "macos" => {
            cmd.env("GOOS", "darwin");
        }
        "ios" => {
            cmd.env("GOOS", "ios");
        }
        "linux" => {
            cmd.env("GOOS", "linux");
        }
        "android" => {
            cmd.env("GOOS", "android");
        }
        "dragonfly" => {
            cmd.env("GOOS", "dragonfly");
        }
        "freebsd" => {
            cmd.env("GOOS", "freebsd");
        }
        "openbsd" => {
            cmd.env("GOOS", "openbsd");
        }
        "netbsd" => {
            cmd.env("GOOS", "netbsd");
        }
        _ => (),
    }

    if tgt_os == "android" {
        let linker = std::env::var("RUSTC_LINKER").unwrap();
        //println!("cargo:warning=LINKER: {linker:?}");
        cmd.env("CC_FOR_TARGET", &linker);
        cmd.env("CC", &linker);
        cmd.env("CGO_ENABLED", "1");
    }

    // grr, clippy, the debug symbols belong in one arg
    #[allow(clippy::suspicious_command_arg_space)]
    {
        cmd.current_dir(out_dir.clone())
            .env("GOCACHE", cache)
            .arg("build")
            .arg("-ldflags") // strip debug symbols
            .arg("-s -w") // strip debug symbols
            .arg("-buildvcs=false") // disable version control stamping binary
            .arg("-o")
            .arg(path)
            .arg("-mod=vendor");
    }

    //println!("cargo:warning=NOTE:running go build: {cmd:?}");

    assert!(
        cmd.spawn()
            .expect("error spawing go build")
            .wait()
            .expect("error running go build")
            .success(),
        "error running go build",
    );

    use sha2::Digest;
    let data = std::fs::read(path).expect("failed to read generated exe");
    let mut hasher = sha2::Sha256::new();
    hasher.update(data);
    let hash =
        base64::encode_config(hasher.finalize(), base64::URL_SAFE_NO_PAD);

    let mut exe_hash = out_dir;
    exe_hash.push("exe_hash.rs");
    std::fs::write(
        exe_hash,
        format!(
            r#"
        const EXE_HASH: &str = "{hash}";
    "#,
        ),
    )
    .expect("failed to write exe hash");
}