Repository: WasmEdge/wasmedge-rust-sdk (language: rust)

Issue:
Standalone mode support for reproducible builds
The runwasi project (https://github.com/containerd/runwasi/pull/187) is using `wasmedge-rust-sdk` standalone mode to get the correct version of the Wasmedge library automatically. However, there is some concern about reproducible builds -- how can we be sure which exact version of the library we are getting, for example in the case when there is a security issue in Wasmedge? Would it be possible to get a checksum or similar of the expected file? Also, how are things like HTTPs proxy config propagated to the download -- can this be documented? Thanks!

(Btw, it seems to me from the code that `wasmedge-rust-sdk` doesn't validate the downloaded file checksum; this could be something that could be improved.)


Discussion / hints:
Hi @ipuustin, thanks for your report. The `standalone` mode has not been the recommended deployment choice for installing the `WasmEdge` library, so we just put very limited energy on it before. Now we see the solid requirements for the mode, we'll catch up and improve it asap. 

*Q1: how can we be sure which exact version of the library we are getting*

In current design, we maintain a [versioning table](https://github.com/WasmEdge/wasmedge-rust-sdk#get-started) to help users get to know  which pair of `WasmEdge Rust SDK` and  `WasmEdge Runtime` they can use.

*Q2: Would it be possible to get a checksum or similar of the expected file?*

We opened a new issue ([#2678](https://github.com/WasmEdge/WasmEdge/issues/2678)) in `WasmEdge` as a new requirement. Once it is satisfied, we'll support it in `wasmedge-rust-sdk`.

*Q3 how are things like HTTPs proxy config propagated to the download -- can this be documented?*

You mean the HTTPs config used in the download script or something else?  Could you please explain this question a bit more? I failed to get your point, sorry about it.

Fix patch:
diff --git a/crates/wasmedge-sys/Cargo.toml b/crates/wasmedge-sys/Cargo.toml
index da2bf7351..c1211b285 100644
--- a/crates/wasmedge-sys/Cargo.toml
+++ b/crates/wasmedge-sys/Cargo.toml
@@ -32,6 +32,12 @@ async-wasi = {workspace = true, optional = true}
 [build-dependencies]
 bindgen = {version = "0.65", default-features = false, features = ["runtime"]}
 cmake = "0.1"
+reqwest = { version = "0.11", default-features = false, features = ["blocking", "rustls-tls"] }
+flate2 = "1"
+tar = "0.4"
+sha256 = "1"
+lazy_static = "1.4.0"
+phf = { version = "0.11", features = ["macros"] }
 
 [dev-dependencies]
 anyhow = "1"
diff --git a/crates/wasmedge-sys/build.rs b/crates/wasmedge-sys/build.rs
index e24ad4af8..2f8e34779 100644
--- a/crates/wasmedge-sys/build.rs
+++ b/crates/wasmedge-sys/build.rs
@@ -1,462 +1,127 @@
-use std::path::PathBuf;
-#[cfg(all(feature = "standalone", target_family = "unix",))]
-use std::{env, process::Command};
+use lazy_static::lazy_static;
+use phf::phf_map;
 
-const WASMEDGE_H: &str = "wasmedge.h";
-#[cfg(all(feature = "standalone", target_family = "unix",))]
-const WASMEDGE_RELEASE_VERSION: &str = "0.13.3";
-
-macro_rules! env_path {
-    ($env_var:literal) => {
-        std::env::var_os($env_var).map(PathBuf::from)
-    };
-}
+mod build_paths;
+use build_paths::{Env, LibWasmEdgePaths};
 
-#[derive(Debug)]
-struct Paths {
-    header: PathBuf,
-    lib_dir: PathBuf,
-    inc_dir: PathBuf,
-}
+mod build_standalone;
+use build_standalone::*;
 
-fn main() -> Result<(), Box<dyn std::error::Error>> {
-    find_wasmedge()
-}
-
-#[cfg(not(feature = "static"))]
-fn find_wasmedge() -> Result<(), Box<dyn std::error::Error>> {
-    #[cfg(all(feature = "standalone", target_family = "unix"))]
-    install_libwasmedge();
-
-    let Paths {
-        header,
-        lib_dir,
-        inc_dir,
-    } = find_libwasmedge().expect(
-        "[wasmedge-sys] Failed to locate the required header and/or library file. Please reference the link: https://wasmedge.org/book/en/embed/rust.html",
-    );
-
-    let out_file = PathBuf::from(std::env::var("OUT_DIR").unwrap()).join("wasmedge.rs");
-    bindgen::builder()
-        .header(
-            header
-                .to_str()
-                .unwrap_or_else(|| panic!("`{}` must be a utf-8 path", header.display())),
-        )
-        .clang_arg(format!("-I{}", inc_dir.as_path().display()))
-        .prepend_enum_name(false) // The API already prepends the name.
-        .dynamic_link_require_all(true)
-        .parse_callbacks(Box::new(bindgen::CargoCallbacks))
-        .generate()
-        .expect("failed to generate bindings")
-        .write_to_file(out_file)
-        .expect("failed to write bindings");
+const WASMEDGE_RELEASE_VERSION: &str = "0.13.3";
+const REMOTE_ARCHIVES: phf::Map<&'static str, (&'static str, &'static str)> = phf_map! {
+    "macos/aarch64"         => ("db03037e2678e197f9cd5f01392ee088df07e7726849626190f2d4ec1dc693c9", "darwin_arm64"),
+    "macos/x86_64"          => ("f913ba51c69603d0c3409bbbbef1cd2a525ffccb9643fe2aea313a47c70cbea7", "darwin_x86_64"),
+    "linux/aarch64"         => ("82a6d5c686b34b15abab4f28f33099bcded11446563bb2bf0d8a22811304efa1", "manylinux2014_aarch64"),
+    "linux/x86_64"          => ("e3634df41375856fbf374f65e91a925c15821f78a4c93852bce5df053d1f2b2b", "manylinux2014_x86_64"),
+    "linux/aarch64/static"  => ("3fb21a5dace3dd46a3f28e0a0e4473c8e22526b083aba15d875afc28b1976cc9", "debian11_aarch64_static"),
+    "linux/x86_64/static"   => ("488d47c43653c0a079c034aa73e525dd60873af15967d6c1d82386862ba31baa", "debian11_x86_64_static"),
+};
+
+lazy_static! {
+
+static ref SEARCH_LOCATIONS: [Option<LibWasmEdgePaths>; 11] = [
+    // search in the env variables: WASMEDGE_INCLUDE_DIR, WASMEDGE_LIB_DIR
+    LibWasmEdgePaths::try_from("", Env("WASMEDGE_INCLUDE_DIR"), Env("WASMEDGE_LIB_DIR")),
+    // search in the env variable: WASMEDGE_DIR
+    LibWasmEdgePaths::try_from(Env("WASMEDGE_DIR"), "include", "lib64"),
+    LibWasmEdgePaths::try_from(Env("WASMEDGE_DIR"), "include", "lib"),
+    // search in the env variable: WASMEDGE_BUILD_DIR
+    LibWasmEdgePaths::try_from(Env("WASMEDGE_BUILD_DIR"), "include/api", "lib64/api"),
+    LibWasmEdgePaths::try_from(Env("WASMEDGE_BUILD_DIR"), "include/api", "lib/api"),
+    // search in the official docker container
+    LibWasmEdgePaths::try_from(Env("HOME"), ".wasmedge/include", ".wasmedge/lib64"),
+    LibWasmEdgePaths::try_from(Env("HOME"), ".wasmedge/include", ".wasmedge/lib"),
+    // search in /usr/local/
+    LibWasmEdgePaths::try_from("/usr/local", "include", "lib64"),
+    LibWasmEdgePaths::try_from("/usr/local", "include", "lib"),
+    // search in xdg
+    LibWasmEdgePaths::try_from(Env("HOME"), ".local/include", ".local/lib64"),
+    LibWasmEdgePaths::try_from(Env("HOME"), ".local/include", ".local/lib"),
+];
 
-    println!("cargo:rustc-env=LD_LIBRARY_PATH={}", lib_dir.display());
-    println!("cargo:rustc-link-search={}", lib_dir.display());
-    println!("cargo:rustc-link-lib=dylib=wasmedge");
+static ref OUT_DIR: std::path::PathBuf = Env("OUT_DIR").expect("failed to get OUT_DIR");
+static ref STANDALONE_DIR: std::path::PathBuf = OUT_DIR.join("standalone");
 
-    Ok(())
 }
 
-#[cfg(all(feature = "static", target_os = "linux"))]
-fn find_wasmedge() -> Result<(), Box<dyn std::error::Error>> {
-    #[cfg(all(
-        feature = "standalone",
-        any(target_arch = "x86_64", target_arch = "aarch64")
-    ))]
-    install_libwasmedge();
+fn find_libwasmedge<'a, L: IntoIterator<Item = &'a Option<LibWasmEdgePaths>>>(
+    locations: L,
+) -> Option<LibWasmEdgePaths> {
+    locations
+        .into_iter()
+        .flatten()
+        .find(|paths| paths.is_wasmedge_dir())
+        .cloned()
+}
 
-    let Paths {
-        header,
-        lib_dir,
-        inc_dir,
-    } = find_libwasmedge().expect(
-        "[wasmedge-sys] Failed to locate the required header and/or library file. Please reference the link: https://wasmedge.org/book/en/embed/rust.html",
-    );
+fn main() {
+    // rerun if the other build sources change
+    println!("cargo:rerun-if-changed=build_paths.rs");
+    println!("cargo:rerun-if-changed=build_install.rs");
+
+    // find the location of the libwasmedge
+    let paths = if cfg!(feature = "standalone") {
+        // use a standalone library from an extracted archive
+        let standalone_dir = get_standalone_libwasmedge();
+        debug!("using standalone extraction at {standalone_dir:?}");
+        let locations = [
+            LibWasmEdgePaths::try_from(&standalone_dir, "include", "lib64"),
+            LibWasmEdgePaths::try_from(&standalone_dir, "include", "lib"),
+        ];
+        find_libwasmedge(&locations)
+    } else {
+        // find the library in the system
+        debug!("searching for existing libwasmedge install");
+        find_libwasmedge(&*SEARCH_LOCATIONS)
+    };
 
-    let libdir_path_str = lib_dir
-        .to_str()
-        .unwrap_or_else(|| panic!("`{}` must be a utf-8 path", lib_dir.display()));
-    println!(
-        "cargo:warning=[wasmedge-sys] libdir_path_str: {:?}",
-        libdir_path_str
-    );
+    let paths = paths.expect("Failed to locate the required header and/or library file. Please reference the link: https://wasmedge.org/book/en/embed/rust.html");
+    debug!("found libwasmedge at {paths:?}");
 
-    let incdir_path_str = inc_dir
-        .to_str()
-        .unwrap_or_else(|| panic!("`{}` must be a utf-8 path", inc_dir.display()));
-    println!(
-        "cargo:warning=[wasmedge-sys] incdir_path_str: {:?}",
-        incdir_path_str
-    );
+    let lib_dir = paths.lib_dir.to_string_lossy().to_string();
 
-    let header_path_str = header
-        .to_str()
-        .unwrap_or_else(|| panic!("`{}` must be a utf-8 path", header.display()));
-    println!(
-        "cargo:warning=[wasmedge-sys] header_path_str: {:?}",
-        header_path_str
-    );
+    if cfg!(feature = "static") {
+        // Tell cargo to look for static libraries in the specified directory
+        println!("cargo:rustc-link-search=native={lib_dir}");
 
-    // Tell cargo to look for shared libraries in the specified directory
-    println!("cargo:rustc-link-search=native={}", libdir_path_str);
-    println!("cargo:rustc-link-search=/lib/x86_64-linux-gnu/");
+        // Tell cargo to tell rustc to link our `wasmedge` library. Cargo will
+        // automatically know it must look for a `libwasmedge.a` file.
+        println!("cargo:rustc-link-lib=static=wasmedge");
+        println!("cargo:rustc-link-lib=rt");
+        println!("cargo:rustc-link-lib=dl");
+        println!("cargo:rustc-link-lib=pthread");
+        println!("cargo:rustc-link-lib=m");
+        println!("cargo:rustc-link-lib=stdc++");
+    } else {
+        println!("cargo:rustc-env=LD_LIBRARY_PATH={lib_dir}");
+        println!("cargo:rustc-link-search={lib_dir}");
+        println!("cargo:rustc-link-lib=dylib=wasmedge");
+    }
 
-    // Tell cargo to tell rustc to link our `wasmedge` library. Cargo will
-    // automatically know it must look for a `libwasmedge.a` file.
-    println!("cargo:rustc-link-lib=static=wasmedge");
-    println!("cargo:rustc-link-lib=rt");
-    println!("cargo:rustc-link-lib=dl");
-    println!("cargo:rustc-link-lib=pthread");
-    println!("cargo:rustc-link-lib=m");
-    println!("cargo:rustc-link-lib=stdc++");
+    let inc_dir = paths.inc_dir.to_string_lossy().to_string();
+    let header = paths.header().to_string_lossy().to_string();
 
     // Tell cargo to invalidate the built crate whenever the header changes.
-    println!("cargo:rerun-if-changed={}", header_path_str);
+    println!("cargo:rerun-if-changed={}", &header);
 
-    let out_file = PathBuf::from(std::env::var("OUT_DIR").unwrap()).join("wasmedge.rs");
+    let out_file = OUT_DIR.join("wasmedge.rs");
+
+    debug!("generating bindgen header {out_file:?}");
     bindgen::builder()
-        .header(header_path_str)
-        .clang_arg(format!("-I{}", incdir_path_str))
-        .clang_arg(format!("-L{}", libdir_path_str))
+        .header(header)
+        .clang_arg(format!("-I{inc_dir}"))
         .prepend_enum_name(false) // The API already prepends the name.
+        .dynamic_link_require_all(true)
         .parse_callbacks(Box::new(bindgen::CargoCallbacks))
         .generate()
         .expect("failed to generate bindings")
         .write_to_file(out_file)
         .expect("failed to write bindings");
-
-    Ok(())
-}
-
-/// Check header and Returns the location of wasmedge.h and libwasmedge.a
-#[cfg(all(feature = "static", target_os = "linux"))]
-fn find_libwasmedge() -> Option<Paths> {
-    // search in the env variables: WASMEDGE_INCLUDE_DIR, WASMEDGE_LIB_DIR
-    let inc_dir = env_path!("WASMEDGE_INCLUDE_DIR");
-    let lib_dir = env_path!("WASMEDGE_LIB_DIR");
-    if let Some(inc_dir) = inc_dir {
-        if let Some(lib_dir) = lib_dir {
-            let header = inc_dir.join("wasmedge");
-            let header = header.join(WASMEDGE_H);
-            if inc_dir.exists() && lib_dir.exists() && header.exists() {
-                println!(
-                    "cargo:warning=[wasmedge-sys] libwasmedge found in {}",
-                    lib_dir.to_str().unwrap()
-                );
-                return Some(Paths {
-                    header,
-                    lib_dir,
-                    inc_dir,
-                });
-            }
-        }
-    }
-
-    // search in the env variable: WASMEDGE_BUILD_DIR
-    let build_dir = env_path!("WASMEDGE_BUILD_DIR");
-    if let Some(build_dir) = build_dir {
-        // WASMEDGE_INCLUDE_DIR
-        let inc_dir = build_dir.join("include");
-        let inc_dir = inc_dir.join("api");
-        // header
-        let header = inc_dir.join("wasmedge");
-        let header = header.join(WASMEDGE_H);
-        // WASMEDGE_LIB_DIR
-        let lib_dir = if build_dir.join("lib64").exists() {
-            build_dir.join("lib64")
-        } else {
-            build_dir.join("lib")
-        };
-        let lib_dir = lib_dir.join("api");
-
-        if build_dir.exists() && inc_dir.exists() && lib_dir.exists() && header.exists() {
-            println!(
-                "cargo:warning=[wasmedge-sys] libwasmedge found in {}",
-                lib_dir.to_str().unwrap()
-            );
-            return Some(Paths {
-                header,
-                lib_dir,
-                inc_dir,
-            });
-        }
-    }
-
-    // search in /usr/local/
-    let inc_dir = PathBuf::from("/usr/local/include");
-    let lib_dir = if PathBuf::from("/usr/local/lib64").exists() {
-        PathBuf::from("/usr/local/lib64")
-    } else {
-        PathBuf::from("/usr/local/lib")
-    };
-    let header = inc_dir.join("wasmedge").join(WASMEDGE_H);
-    if inc_dir.join("wasmedge").exists() && lib_dir.exists() && header.exists() {
-        println!(
-            "cargo:warning=[wasmedge-sys] libwasmedge found in {}",
-            lib_dir.to_str().unwrap()
-        );
-        return Some(Paths {
-            header,
-            inc_dir,
-            lib_dir,
-        });
-    }
-
-    // search in the official docker container
-    let default_dir = env_path!("HOME").map(|d| d.join(".wasmedge"));
-    if let Some(default_dir) = default_dir {
-        // WASMEDGE_INCLUDE_DIR
-        let inc_dir = default_dir.join("include");
-        // header
-        let header = inc_dir.join("wasmedge");
-        let header = header.join(WASMEDGE_H);
-
-        // WASMEDGE_LIB_DIR
-        let lib_dir = if default_dir.join("lib64").exists() {
-            default_dir.join("lib64")
-        } else {
-            default_dir.join("lib")
-        };
-
-        if default_dir.exists() && inc_dir.exists() && lib_dir.exists() && header.exists() {
-            println!(
-                "cargo:warning=[wasmedge-sys] libwasmedge found in {}",
-                lib_dir.to_str().unwrap(),
-            );
-            return Some(Paths {
-                header,
-                lib_dir,
-                inc_dir,
-            });
-        }
-    }
-
-    // search in xdg
-    let xdg_dir = env_path!("HOME").map(|d| d.join(".local"));
-    if let Some(xdg_dir) = xdg_dir {
-        let inc_dir = xdg_dir.join("include");
-        let header = inc_dir.join(WASMEDGE_H);
-        let lib_dir = if xdg_dir.join("lib64").exists() {
-            xdg_dir.join("lib64")
-        } else {
-            xdg_dir.join("lib")
-        };
-
-        if xdg_dir.exists() && inc_dir.exists() && lib_dir.exists() && header.exists() {
-            println!(
-                "cargo:warning=[wasmedge-sys] libwasmedge found in {}",
-                lib_dir.to_str().unwrap()
-            );
-            return Some(Paths {
-                header,
-                lib_dir,
-                inc_dir,
-            });
-        }
-    }
-
-    println!("cargo:warning=[wasmedge-sys] Failed to locate lib_dir, include_dir, or header.",);
-
-    None
 }
 
-/// Check header and Returns the location of wasmedge.h and libwasmedge_c.(dylib|so)
-#[cfg(not(feature = "static"))]
-fn find_libwasmedge() -> Option<Paths> {
-    // search in the env variables: WASMEDGE_INCLUDE_DIR, WASMEDGE_LIB_DIR
-    let inc_dir = env_path!("WASMEDGE_INCLUDE_DIR");
-    let lib_dir = env_path!("WASMEDGE_LIB_DIR");
-    if let Some(inc_dir) = inc_dir {
-        if let Some(lib_dir) = lib_dir {
-            let header = inc_dir.join("wasmedge");
-            let header = header.join(WASMEDGE_H);
-            if inc_dir.exists() && lib_dir.exists() && header.exists() {
-                println!(
-                    "cargo:warning=[wasmedge-sys] libwasmedge found in {}",
-                    lib_dir.to_str().unwrap()
-                );
-                return Some(Paths {
-                    header,
-                    lib_dir,
-                    inc_dir,
-                });
-            }
-        }
-    }
-
-    // search in the env variable: WASMEDGE_BUILD_DIR
-    let build_dir = env_path!("WASMEDGE_BUILD_DIR");
-    if let Some(build_dir) = build_dir {
-        // WASMEDGE_INCLUDE_DIR
-        let inc_dir = build_dir.join("include");
-        let inc_dir = inc_dir.join("api");
-        // header
-        let header = inc_dir.join("wasmedge");
-        let header = header.join(WASMEDGE_H);
-        // WASMEDGE_LIB_DIR
-        let lib_dir = if build_dir.join("lib64").exists() {
-            build_dir.join("lib64")
-        } else {
-            build_dir.join("lib")
-        };
-        let lib_dir = lib_dir.join("api");
-
-        if build_dir.exists() && inc_dir.exists() && lib_dir.exists() && header.exists() {
-            println!(
-                "cargo:warning=[wasmedge-sys] libwasmedge found in {}",
-                lib_dir.to_str().unwrap()
-            );
-            return Some(Paths {
-                header,
-                lib_dir,
-                inc_dir,
-            });
-        }
-    }
-
-    // search in the official docker container
-    let default_dir = env_path!("HOME").map(|d| d.join(".wasmedge"));
-    if let Some(default_dir) = default_dir {
-        // WASMEDGE_INCLUDE_DIR
-        let inc_dir = default_dir.join("include");
-        // header
-        let header = inc_dir.join("wasmedge");
-        let header = header.join(WASMEDGE_H);
-
-        // WASMEDGE_LIB_DIR
-        let lib_dir = if default_dir.join("lib64").exists() {
-            default_dir.join("lib64")
-        } else {
-            default_dir.join("lib")
-        };
-
-        if default_dir.exists() && inc_dir.exists() && lib_dir.exists() && header.exists() {
-            println!(
-                "cargo:warning=[wasmedge-sys] libwasmedge found in {}",
-                lib_dir.to_str().unwrap(),
-            );
-            return Some(Paths {
-                header,
-                lib_dir,
-                inc_dir,
-            });
-        }
-    }
-
-    // search in /usr/local/
-    let inc_dir = PathBuf::from("/usr/local/include");
-    let lib_dir = if PathBuf::from("/usr/local/lib64").exists() {
-        PathBuf::from("/usr/local/lib64")
-    } else {
-        PathBuf::from("/usr/local/lib")
+#[macro_export]
+macro_rules! debug {
+    ($($args:expr),+) => {
+        println!("cargo:warning=[wasmedge-sys] {}", format!($($args),+))
     };
-    let header = inc_dir.join("wasmedge").join(WASMEDGE_H);
-    if inc_dir.join("wasmedge").exists() && lib_dir.exists() && header.exists() {
-        println!(
-            "cargo:warning=[wasmedge-sys] libwasmedge found in {}",
-            lib_dir.to_str().unwrap()
-        );
-        return Some(Paths {
-            header,
-            inc_dir,
-            lib_dir,
-        });
-    }
-
-    // search in xdg
-    let xdg_dir = env_path!("HOME").map(|d| d.join(".local"));
-    if let Some(xdg_dir) = xdg_dir {
-        let inc_dir = xdg_dir.join("include");
-        let header = inc_dir.join(WASMEDGE_H);
-        let lib_dir = if xdg_dir.join("lib64").exists() {
-            xdg_dir.join("lib64")
-        } else {
-            xdg_dir.join("lib")
-        };
-
-        if xdg_dir.exists() && inc_dir.exists() && lib_dir.exists() && header.exists() {
-            println!(
-                "cargo:warning=[wasmedge-sys] libwasmedge found in {}",
-                lib_dir.to_str().unwrap()
-            );
-            return Some(Paths {
-                header,
-                lib_dir,
-                inc_dir,
-            });
-        }
-    }
-
-    println!("cargo:warning=[wasmedge-sys] Failed to locate lib_dir, include_dir, or header.",);
-
-    None
-}
-
-#[cfg(all(
-    feature = "standalone",
-    not(feature = "static"),
-    target_family = "unix"
-))]
-fn install_libwasmedge() {
-    let out_dir = env::var("OUT_DIR").expect("[wasmedge-sys] Failed to get OUT_DIR");
-    println!("cargo:warning=[wasmedge-sys] OUT_DIR: {}", &out_dir);
-
-    let output = Command::new("wget")
-        .current_dir(&out_dir)
-        .arg("https://raw.githubusercontent.com/WasmEdge/WasmEdge/master/utils/install.sh")
-        .output()
-        .expect("[wasmedge-sys] Failed to download libwasmedge installation script");
-    println!(
-        "cargo:warning=[wasmedge-sys] Download libwasmedge installation script: {:?}",
-        output
-    );
-
-    let output = Command::new("/bin/bash")
-        .current_dir(&out_dir)
-        .args(["install.sh", "-v", WASMEDGE_RELEASE_VERSION])
-        .output()
-        .expect("[wasmedge-sys] Failed to run libwasmedge installation script");
-    println!(
-        "cargo:warning=[wasmedge-sys] Run libwasmedge installation script: {:?}",
-        output
-    );
-
-    let output = Command::new("/bin/bash")
-        .arg("-c")
-        .arg("source $HOME/.wasmedge/env")
-        .output()
-        .expect("[wasmedge-sys] Failed to source the env");
-    println!("cargo:warning=[wasmedge-sys] source the env: {:?}", output);
-}
-
-#[cfg(all(
-    feature = "standalone",
-    feature = "static",
-    target_os = "linux",
-    any(target_arch = "x86_64", target_arch = "aarch64")
-))]
-fn install_libwasmedge() {
-    let arch = env::var("CARGO_CFG_TARGET_ARCH")
-        .expect("[wasmedge-sys] Failed to get CARGO_CFG_TARGET_ARCH");
-    println!("cargo:warning=[wasmedge-sys] CARGO_CFG_TARGET_ARCH: {arch}");
-
-    let out_dir = env::var("OUT_DIR").expect("[wasmedge-sys] Failed to get OUT_DIR");
-    println!("cargo:warning=[wasmedge-sys] OUT_DIR: {out_dir}");
-
-    let url = format!("https://github.com/WasmEdge/WasmEdge/releases/download/{WASMEDGE_RELEASE_VERSION}/WasmEdge-{WASMEDGE_RELEASE_VERSION}-debian11_{arch}_static.tar.gz");
-    println!("cargo:warning=[wasmedge-sys] Url for libwasmedge archive: {url}");
-
-    let cmd = format!("wget -qO- {url} | tar xz --strip-components=1");
-
-    let output = Command::new("/bin/bash")
-        .current_dir(&out_dir)
-        .args(["-c", &cmd])
-        .output()
-        .expect("[wasmedge-sys] Failed to download libwasmedge archive");
-    println!("cargo:warning=[wasmedge-sys] Download libwasmedge archive: {output:?}");
-
-    env::set_var("WASMEDGE_INCLUDE_DIR", format!("{out_dir}/include"));
-    env::set_var("WASMEDGE_LIB_DIR", format!("{out_dir}/lib"));
 }
diff --git a/crates/wasmedge-sys/build_paths.rs b/crates/wasmedge-sys/build_paths.rs
new file mode 100644
index 000000000..16f1b37a9
--- /dev/null
+++ b/crates/wasmedge-sys/build_paths.rs
@@ -0,0 +1,83 @@
+use crate::debug;
+
+pub struct Env<'a>(pub &'a str);
+
+impl Env<'_> {
+    pub fn read<T: From<std::ffi::OsString>>(&self) -> Option<T> {
+        std::env::var_os(self.0).map(T::from)
+    }
+
+    pub fn expect<T: TryFrom<std::ffi::OsString>>(&self, msg: &str) -> T
+    where
+        <T as TryFrom<std::ffi::OsString>>::Error: std::fmt::Debug,
+    {
+        self.read::<std::ffi::OsString>()
+            .map(T::try_from)
+            .expect(msg)
+            .expect(msg)
+    }
+
+    pub fn lossy(&self) -> Option<String> {
+        self.read::<std::ffi::OsString>()
+            .map(|v| v.to_string_lossy().to_string())
+    }
+
+    pub fn expect_lossy(&self, msg: &str) -> String {
+        self.lossy().expect(msg)
+    }
+}
+
+pub trait AsPath {
+    fn as_path(&self) -> Option<std::path::PathBuf>;
+}
+
+impl AsPath for Env<'_> {
+    fn as_path(&self) -> Option<std::path::PathBuf> {
+        println!("cargo:rerun-if-env-changed={}", self.0);
+        self.read()
+    }
+}
+
+impl AsPath for &str {
+    fn as_path(&self) -> Option<std::path::PathBuf> {
+        Some(std::path::PathBuf::from(self))
+    }
+}
+
+impl AsPath for &std::path::PathBuf {
+    fn as_path(&self) -> Option<std::path::PathBuf> {
+        Some((*self).clone())
+    }
+}
+
+#[derive(Debug, Clone)]
+pub struct LibWasmEdgePaths {
+    pub lib_dir: std::path::PathBuf,
+    pub inc_dir: std::path::PathBuf,
+}
+
+impl LibWasmEdgePaths {
+    pub fn header(&self) -> std::path::PathBuf {
+        self.inc_dir.join("wasmedge").join("wasmedge.h")
+    }
+
+    pub fn is_wasmedge_dir(&self) -> bool {
+        debug!("searching for libwasmedge at {self:?}");
+        self.header().exists() && self.lib_dir.exists()
+    }
+
+    pub fn try_from(
+        base_dir: impl AsPath,
+        inc_dir: impl AsPath,
+        lib_dir: impl AsPath,
+    ) -> Option<Self> {
+        let pwd = std::env::current_dir().unwrap_or_default();
+        match (base_dir.as_path(), inc_dir.as_path(), lib_dir.as_path()) {
+            (Some(base_dir), Some(inc_dir), Some(lib_dir)) => Some(LibWasmEdgePaths {
+                inc_dir: pwd.join(&base_dir).join(inc_dir),
+                lib_dir: pwd.join(&base_dir).join(lib_dir),
+            }),
+            _ => None,
+        }
+    }
+}
diff --git a/crates/wasmedge-sys/build_standalone.rs b/crates/wasmedge-sys/build_standalone.rs
new file mode 100644
index 000000000..e7de7b574
--- /dev/null
+++ b/crates/wasmedge-sys/build_standalone.rs
@@ -0,0 +1,132 @@
+use super::{REMOTE_ARCHIVES, STANDALONE_DIR, WASMEDGE_RELEASE_VERSION};
+use crate::{
+    build_paths::{AsPath, Env},
+    debug,
+};
+
+#[derive(Debug)]
+enum Archive {
+    Local { path: std::path::PathBuf },
+    Remote { url: String, checksum: String },
+}
+
+impl Archive {
+    fn hash(&self) -> String {
+        match self {
+            Archive::Local { path } => sha256::try_digest(path).expect("failed to read archive"),
+            Archive::Remote { checksum, .. } => checksum.clone(),
+        }
+    }
+
+    fn get(&self) -> std::path::PathBuf {
+        match self {
+            Archive::Local { path } => path.clone(),
+            Archive::Remote { url, checksum } => {
+                debug!("downloading archive");
+                let dst = STANDALONE_DIR.join("archive.tar.gz");
+                let mut request = do_http_request(url);
+                let mut file = std::fs::File::create(&dst).expect("failed to create archive");
+                std::io::copy(&mut request, &mut file).expect("failed to download archive");
+                let sha = sha256::try_digest(&dst).expect("failed to read archive");
+                if &sha != checksum {
+                    panic!("wrong archive checksum, expected {checksum}, received {sha}");
+                }
+                dst
+            }
+        }
+    }
+
+    fn cleanup(&self) {
+        if let Archive::Remote { .. } = self {
+            let _ = std::fs::remove_file(STANDALONE_DIR.join("archive.tar.gz"));
+        }
+    }
+}
+
+pub fn get_standalone_libwasmedge() -> std::path::PathBuf {
+    let archive = match Env("WASMEDGE_STANDALONE_ARCHIVE").as_path() {
+        Some(path) => Archive::Local { path },
+        None => get_remote_archive(),
+    };
+    debug!("using archive {archive:?}");
+
+    let hash = archive.hash();
+    if hash == std::fs::read_to_string(STANDALONE_DIR.join(".stamp")).unwrap_or_default() {
+        debug!("skipping extraction, archive is already extracted");
+    } else {
+        if STANDALONE_DIR.exists() {
+            debug!("deleting previous extraction");
+            std::fs::remove_dir_all(STANDALONE_DIR.as_path()).expect("failed to cleanup directory")
+        }
+        std::fs::create_dir_all(STANDALONE_DIR.as_path()).expect("failed to create archive dir");
+
+        let file = archive.get();
+        let readable = std::fs::File::open(file).expect("failed to open archive");
+        let ungzipped = flate2::read::GzDecoder::new(readable);
+
+        debug!("extracting archive");
+        tar::Archive::new(ungzipped)
+            .unpack(STANDALONE_DIR.as_path())
+            .expect("failed to extract archive");
+
+        archive.cleanup();
+
+        std::fs::write(STANDALONE_DIR.join(".stamp"), hash).expect("failed to write archive stamp");
+    }
+
+    std::fs::read_dir(STANDALONE_DIR.as_path())
+        .expect("failed to read archive directory")
+        .filter_map(|entry| entry.ok())
+        .find(|entry| match entry.file_type() {
+            Ok(ty) => ty.is_dir(),
+            _ => false,
+        })
+        .expect("failed to find WasmEdge in archive directory")
+        .path()
+}
+
+fn get_remote_archive() -> Archive {
+    let os = Env("CARGO_CFG_TARGET_OS").expect_lossy("failed to read CARGO_CFG_TARGET_OS");
+    let arch = Env("CARGO_CFG_TARGET_ARCH").expect_lossy("failed to read CARGO_CFG_TARGET_ARCH");
+    let target = if cfg!(feature = "static") {
+        format!("{os}/{arch}/static")
+    } else {
+        format!("{os}/{arch}")
+    };
+
+    debug!("building archive url for target {target}");
+    let (sha, slug) = REMOTE_ARCHIVES
+        .get(target.as_str())
+        .expect("target not supported with features `standalone` and `static`")
+        .to_owned();
+
+    let url = format!("https://github.com/WasmEdge/WasmEdge/releases/download/{WASMEDGE_RELEASE_VERSION}/WasmEdge-{WASMEDGE_RELEASE_VERSION}-{slug}.tar.gz");
+
+    let checksum = sha.to_string();
+    Archive::Remote { url, checksum }
+}
+
+fn do_http_request(url: &str) -> impl std::io::Read {
+    let builder = reqwest::blocking::Client::builder();
+    let builder = match Env("STANDALONE_PROXY").lossy() {
+        Some(proxy) => {
+            debug!("using proxy to download archive: {proxy}");
+            let proxy = reqwest::Proxy::all(proxy).expect("failed to parse proxy");
+            let user = Env("WASMEDGE_STANDALONE_PROXY_USER").lossy();
+            let pass = Env("WASMEDGE_STANDALONE_PROXY_PASS").lossy();
+            let proxy = match user.zip(pass) {
+                Some((user, pass)) => proxy.basic_auth(&user, &pass),
+                _ => proxy,
+            };
+            builder.proxy(proxy)
+        }
+        None => builder,
+    };
+    builder
+        .build()
+        .expect("failed to create http request")
+        .get(url)
+        .timeout(std::time::Duration::from_secs(3600 * 24))
+        .send()
+        .expect("failed to download archive")
+}


Test patch: