This is an automated email from the ASF dual-hosted git repository.

tqchen pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/tvm-ffi.git


The following commit(s) were added to refs/heads/main by this push:
     new 303f4703 [FIX][Rust] Fail loudly when tvm-ffi-config is missing (#825)
303f4703 is described below

commit 303f4703fcc088be03a18fc2bd4eeea582afbe7a
Author: Gabriel Wu <[email protected]>
AuthorDate: Mon Sep 28 22:23:26 2026 +0800

    [FIX][Rust] Fail loudly when tvm-ffi-config is missing (#825)
    
    ## Why
    
    `tvm-ffi-sys/build.rs` decides whether it is building documentation with
    `env::var("RUSTDOC").is_ok()`. Cargo sets `RUSTDOC` for every build
    script ([Cargo: environment variables set for build
    
scripts](https://doc.rust-lang.org/cargo/reference/environment-variables.html#environment-variables-cargo-sets-for-build-scripts)),
    so the check is always true. It was meant for the Rust docs build in
    `docs/conf.py` (#160).
    
    As a result, a normal `cargo build` without a working `tvm-ffi-config`
    doesn't fail. It prints "tvm-ffi-config not available, skipping for
    documentation build" and emits no `rustc-link-*` directives. The first
    sign of trouble is a link error in a dependent crate:
    
    ```
    rust-lld: error: undefined symbol: TVMFFIEnvGetStream
    ```
    
    Cargo also caches the empty build-script output. The build script
    declares no `rerun-if-*`, so it reruns only when the package's own files
    change, and the build keeps failing after tvm-ffi is installed until
    `cargo clean -p tvm-ffi-sys`.
    
    ## What
    
    - **Documentation builds:** detected by `DOCS_RS`, which docs.rs sets.
    `docs/conf.py` now sets it too for its `cargo doc`, so docs still build
    without tvm-ffi installed. In that case the build script emits a
    `cargo:warning` naming the reason and doesn't link.
    - **Every other build:** fails in the build script, with the reason and
    the fix, when `tvm-ffi-config` cannot be run, exits non-zero (its stderr
    is included), or prints no library directory:
    
      ```
    could not run `tvm-ffi-config`: No such file or directory (os error 2).
    tvm-ffi-sys links libtvm_ffi from the directory that `tvm-ffi-config
    --libdir` prints: install tvm-ffi (e.g. `pip install apache-tvm-ffi`)
    and put tvm-ffi-config on PATH. To build documentation only, set
    DOCS_RS=1.
      ```
    
    - **Rerunning:** the build script declares its inputs with
    `cargo:rerun-if-env-changed` (`DOCS_RS`, `PATH`, and the library-path
    variable it reads) plus `rerun-if-changed=build.rs`, so output from one
    environment isn't reused in another.
---
 docs/conf.py              |  3 ++-
 rust/tvm-ffi-sys/build.rs | 64 ++++++++++++++++++++++++++++-------------------
 2 files changed, 40 insertions(+), 27 deletions(-)

diff --git a/docs/conf.py b/docs/conf.py
index 07befa12..5e2de3d3 100644
--- a/docs/conf.py
+++ b/docs/conf.py
@@ -210,7 +210,8 @@ def _build_rust_docs() -> None:
             ["cargo", "doc", "--no-deps", "--workspace", "--target-dir", 
"target"],
             check=True,
             cwd=_RUST_DIR,
-            env={**os.environ, "RUSTDOCFLAGS": "--cfg docsrs"},
+            # DOCS_RS lets tvm-ffi-sys build without tvm-ffi installed, as on 
docs.rs.
+            env={**os.environ, "RUSTDOCFLAGS": "--cfg docsrs", "DOCS_RS": "1"},
         )
 
         print(f"Rust documentation built successfully at {target_doc}")
diff --git a/rust/tvm-ffi-sys/build.rs b/rust/tvm-ffi-sys/build.rs
index fe787589..d7c9dca2 100644
--- a/rust/tvm-ffi-sys/build.rs
+++ b/rust/tvm-ffi-sys/build.rs
@@ -34,6 +34,7 @@ fn update_ld_library_path(lib_dir: &str) {
         return;
     }
     // Get the current value of the environment variable at build time (if any)
+    println!("cargo:rerun-if-env-changed={}", os_env_var);
     let current_val = env::var(os_env_var).unwrap_or_else(|_| String::new());
     // Use platform-specific separator
     let separator = if os_env_var == "PATH" { ";" } else { ":" };
@@ -47,37 +48,48 @@ fn update_ld_library_path(lib_dir: &str) {
 }
 
 fn main() {
-    // When building documentation, we may not need actual linking
-    // Check if this is a rustdoc build
-    let is_rustdoc = env::var("RUSTDOC").is_ok() || 
env::var("CARGO_CFG_DOC").is_ok();
+    // docs.rs builds the documentation without tvm-ffi installed, and so does
+    // docs/conf.py, which sets DOCS_RS as docs.rs does. Cargo sets RUSTDOC for
+    // every build script, so it cannot tell a documentation build apart.
+    let docs_only = env::var_os("DOCS_RS").is_some();
+    println!("cargo:rerun-if-env-changed=DOCS_RS");
+    // The library directory comes from the tvm-ffi-config found on PATH.
+    println!("cargo:rerun-if-env-changed=PATH");
+    println!("cargo:rerun-if-changed=build.rs");
 
-    // Run `mylib-config --libdir` to get the library path
-    let config_result = 
Command::new("tvm-ffi-config").arg("--libdir").output();
-
-    let lib_dir = match config_result {
-        Ok(output) if output.status.success() => 
String::from_utf8(output.stdout)
-            .unwrap_or_default()
-            .trim()
-            .to_string(),
-        _ if is_rustdoc => {
-            // For rustdoc builds, we can proceed without the library
-            eprintln!("Warning: tvm-ffi-config not available, skipping for 
documentation build");
-            return;
-        }
-        _ => {
-            panic!("Failed to run tvm-ffi-config. Make sure tvm-ffi is 
installed.");
+    // Run `tvm-ffi-config --libdir` to get the library path
+    let found = match Command::new("tvm-ffi-config").arg("--libdir").output() {
+        Ok(output) if output.status.success() => {
+            let lib_dir = String::from_utf8(output.stdout)
+                .unwrap_or_default()
+                .trim()
+                .to_string();
+            if lib_dir.is_empty() {
+                Err("`tvm-ffi-config --libdir` printed no library 
directory".to_string())
+            } else {
+                Ok(lib_dir)
+            }
         }
+        Ok(output) => Err(format!(
+            "`tvm-ffi-config --libdir` failed ({}): {}",
+            output.status,
+            String::from_utf8_lossy(&output.stderr).trim()
+        )),
+        Err(err) => Err(format!("could not run `tvm-ffi-config`: {err}")),
     };
-
-    if lib_dir.is_empty() {
-        if is_rustdoc {
-            eprintln!(
-                "Warning: Empty lib_dir from tvm-ffi-config, skipping for 
documentation build"
-            );
+    let lib_dir = match found {
+        Ok(lib_dir) => lib_dir,
+        Err(reason) if docs_only => {
+            println!("cargo:warning={reason}; not linking libtvm_ffi for a 
documentation build");
             return;
         }
-        panic!("tvm-ffi-config returned empty library path");
-    }
+        Err(reason) => panic!(
+            "{reason}. tvm-ffi-sys links libtvm_ffi from the directory that \
+             `tvm-ffi-config --libdir` prints: install tvm-ffi (e.g. `pip 
install \
+             apache-tvm-ffi`) and put tvm-ffi-config on PATH. To build 
documentation \
+             only, set DOCS_RS=1."
+        ),
+    };
 
     // add the library directory to the linker search path
     println!("cargo:rustc-link-search=native={}", lib_dir);

Reply via email to