esp-rs / esp-idf-sys

Bindings for ESP-IDF (Espressif's IoT Development Framework)
Apache License 2.0
254 stars 118 forks source link

Building sys crates with internal cc builder and bindgen #321

Open Nyrmburk opened 3 weeks ago

Nyrmburk commented 3 weeks ago
  1. Do setup from docs.
  2. Generate esp-idf template.
  3. Compile successfully.
  4. Add a crate with generated Rust bindings for a wrapped C library such as ring or littlefs2.
  5. Observe a build failure (fish and bash have the same result).

Is there a mechanism to forward the environment that is compiling the esp-idf to these other sys dependencies?

Template generate config:

cargo generate esp-rs/esp-idf-template cargo
⚠️   Favorite `esp-rs/esp-idf-template` not found in config, using it as a git repository: https://github.com/esp-rs/esp-idf-template.git
🤷   Project Name: buildnative-espidf
🔧   Destination: /home/nyrmburk/documents/dev/buildnative-espidf ...
🔧   project-name: buildnative-espidf ...
🔧   Generating template ...
✔ 🤷   Which MCU to target? · esp32c3
✔ 🤷   Configure advanced template options? · true
✔ 🤷   Enable STD support? · true
✔ 🤷   ESP-IDF version (master = UNSTABLE) · v5.2
✔ 🤷   Configure project to use Dev Containers (VS Code and GitHub Codespaces)? · false
✔ 🤷   Configure project to support Wokwi simulation with Wokwi VS Code extension? · false
✔ 🤷   Add CI files for GitHub Action? · false
🔧   Moving generated files into: `/home/nyrmburk/documents/dev/buildnative-espidf`...
🔧   Initializing a fresh Git repository
✨   Done! New project created /home/nyrmburk/documents/dev/buildnative-espid

Build failure with littlefs2: Crate: https://docs.rs/littlefs2/latest/littlefs2/ Repo: https://github.com/trussed-dev/littlefs2

This crate generates bindings and builds the C littlefs library with the littlefs2-sys crate: Crate: https://docs.rs/littlefs2-sys/latest/littlefs2_sys/ Repo: https://github.com/trussed-dev/littlefs2-sys

The build.rs file is very simple: https://github.com/trussed-dev/littlefs2-sys/blob/main/build.rs

use std::env;
use std::path::PathBuf;

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let mut builder = cc::Build::new();
    let target = env::var("TARGET")?;
    let builder = builder
        .flag("-std=c11")
        .flag("-DLFS_NO_DEBUG")
        .flag("-DLFS_NO_WARN")
        .flag("-DLFS_NO_ERROR")
        .file("littlefs/lfs.c")
        .file("littlefs/lfs_util.c")
        .file("string.c");

    #[cfg(feature = "software-intrinsics")]
    let builder = builder.flag("-DLFS_NO_INTRINSICS");

    #[cfg(not(feature = "assertions"))]
    let builder = builder.flag("-DLFS_NO_ASSERT");

    #[cfg(feature = "trace")]
    let builder = builder.flag("-DLFS_YES_TRACE");

    #[cfg(not(feature = "malloc"))]
    builder.flag("-DLFS_NO_MALLOC");

    builder.compile("lfs-sys");

    let bindings = bindgen::Builder::default()
        .header("littlefs/lfs.h")
        .clang_arg(format!("--target={}", target))
        .use_core()
        .allowlist_item("lfs_.*")
        .allowlist_item("LFS_.*")
        .generate()
        .expect("Unable to generate bindings");

    let out_path = PathBuf::from(env::var("OUT_DIR").unwrap());
    bindings
        .write_to_file(out_path.join("bindings.rs"))
        .expect("Couldn't write bindings!");

    Ok(())
}

cargo build results:

 $ cargo add littlefs2
 ...
 $ cargo build
 ...
 The following warnings were emitted during compilation:

warning: littlefs2-sys@0.1.7: Compiler family detection failed due to error: ToolNotFound: Failed to find tool. Is `riscv32-esp-elf-gcc` installed?

error: failed to run custom build command for `littlefs2-sys v0.1.7`

Caused by:
  process didn't exit successfully: `/home/nyrmburk/documents/dev/buildnative-espidf/target/debug/build/littlefs2-sys-78b36b8617a86ec1/build-script-build` (exit status: 1)
  --- stdout
  OUT_DIR = Some(/home/nyrmburk/documents/dev/buildnative-espidf/target/riscv32imc-esp-espidf/debug/build/littlefs2-sys-0283119dc2df45e7/out)
  TARGET = Some(riscv32imc-esp-espidf)
  OPT_LEVEL = Some(z)
  HOST = Some(x86_64-unknown-linux-gnu)
  cargo:rerun-if-env-changed=CC_riscv32imc-esp-espidf
  CC_riscv32imc-esp-espidf = None
  cargo:rerun-if-env-changed=CC_riscv32imc_esp_espidf
  CC_riscv32imc_esp_espidf = None
  cargo:rerun-if-env-changed=TARGET_CC
  TARGET_CC = None
  cargo:rerun-if-env-changed=CC
  CC = None
  cargo:rerun-if-env-changed=CROSS_COMPILE
  CROSS_COMPILE = None
  RUSTC_LINKER = Some(ldproxy)
  cargo:rerun-if-env-changed=CC_ENABLE_DEBUG_OUTPUT
  cargo:warning=Compiler family detection failed due to error: ToolNotFound: Failed to find tool. Is `riscv32-esp-elf-gcc` installed?
  RUSTC_WRAPPER = None
  cargo:rerun-if-env-changed=CRATE_CC_NO_DEFAULTS
  CRATE_CC_NO_DEFAULTS = Some(1)
  cargo:rerun-if-env-changed=CFLAGS_riscv32imc-esp-espidf
  CFLAGS_riscv32imc-esp-espidf = None
  cargo:rerun-if-env-changed=CFLAGS_riscv32imc_esp_espidf
  CFLAGS_riscv32imc_esp_espidf = None
  cargo:rerun-if-env-changed=TARGET_CFLAGS
  TARGET_CFLAGS = None
  cargo:rerun-if-env-changed=CFLAGS
  CFLAGS = None

  --- stderr

  error occurred: Failed to find tool. Is `riscv32-esp-elf-gcc` installed?

warning: build failed, waiting for other jobs to finish...

Adding the compiler bins to my path manually (fish shell):

$ export PATH="$(pwd)/.embuild/espressif/tools/riscv32-esp-elf/esp-13.2.0_20230928/riscv32-esp-elf/bin:$PATH"
$ cargo build
...
error: failed to run custom build command for `littlefs2-sys v0.1.7`

Caused by:
  process didn't exit successfully: `/home/nyrmburk/documents/dev/buildnative-espidf/target/debug/build/littlefs2-sys-78b36b8617a86ec1/build-script-build` (exit status: 101)
  --- stdout
  OUT_DIR = Some(/home/nyrmburk/documents/dev/buildnative-espidf/target/riscv32imc-esp-espidf/debug/build/littlefs2-sys-0283119dc2df45e7/out)
  TARGET = Some(riscv32imc-esp-espidf)
  OPT_LEVEL = Some(z)
  HOST = Some(x86_64-unknown-linux-gnu)
  cargo:rerun-if-env-changed=CC_riscv32imc-esp-espidf
  CC_riscv32imc-esp-espidf = None
  cargo:rerun-if-env-changed=CC_riscv32imc_esp_espidf
  CC_riscv32imc_esp_espidf = None
  cargo:rerun-if-env-changed=TARGET_CC
  TARGET_CC = None
  cargo:rerun-if-env-changed=CC
  CC = None
  cargo:rerun-if-env-changed=CROSS_COMPILE
  CROSS_COMPILE = None
  RUSTC_LINKER = Some(ldproxy)
  cargo:rerun-if-env-changed=CC_ENABLE_DEBUG_OUTPUT
  RUSTC_WRAPPER = None
  cargo:rerun-if-env-changed=CRATE_CC_NO_DEFAULTS
  CRATE_CC_NO_DEFAULTS = Some(1)
  cargo:rerun-if-env-changed=CFLAGS_riscv32imc-esp-espidf
  CFLAGS_riscv32imc-esp-espidf = None
  cargo:rerun-if-env-changed=CFLAGS_riscv32imc_esp_espidf
  CFLAGS_riscv32imc_esp_espidf = None
  cargo:rerun-if-env-changed=TARGET_CFLAGS
  TARGET_CFLAGS = None
  cargo:rerun-if-env-changed=CFLAGS
  CFLAGS = None
  cargo:rerun-if-env-changed=AR_riscv32imc-esp-espidf
  AR_riscv32imc-esp-espidf = None
  cargo:rerun-if-env-changed=AR_riscv32imc_esp_espidf
  AR_riscv32imc_esp_espidf = None
  cargo:rerun-if-env-changed=TARGET_AR
  TARGET_AR = None
  cargo:rerun-if-env-changed=AR
  AR = None
  cargo:rerun-if-env-changed=ARFLAGS_riscv32imc-esp-espidf
  ARFLAGS_riscv32imc-esp-espidf = None
  cargo:rerun-if-env-changed=ARFLAGS_riscv32imc_esp_espidf
  ARFLAGS_riscv32imc_esp_espidf = None
  cargo:rerun-if-env-changed=TARGET_ARFLAGS
  TARGET_ARFLAGS = None
  cargo:rerun-if-env-changed=ARFLAGS
  ARFLAGS = None
  cargo:rustc-link-lib=static=lfs-sys
  cargo:rustc-link-search=native=/home/nyrmburk/documents/dev/buildnative-espidf/target/riscv32imc-esp-espidf/debug/build/littlefs2-sys-0283119dc2df45e7/out

  --- stderr
  error: unknown target triple 'riscv32imc-esp-espidf'
  thread 'main' panicked at /home/nyrmburk/.cargo/registry/src/index.crates.io-6f17d22bba15001f/bindgen-0.56.0/src/ir/context.rs:531:15:
  libclang error; possible causes include:
  - Invalid flag syntax
  - Unrecognized flags
  - Invalid flag arguments
  - File I/O errors
  - Host vs. target architecture mismatch
  If you encounter an error missing from this list, please file an issue or a PR!
  note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
warning: build failed, waiting for other jobs to finish...

From some other bugs I read here, it seems like bindgen might be trying to use some host resources instead of the cross compile ones?

Trying the same for the ring crate is extremely siimilar: Crate: https://lib.rs/crates/ring Repo:https://github.com/briansmith/ring

*new terminal with default env variables*
$ cargo remove littlefs2
...
$ cargo add ring
...
$ cargo build
The following warnings were emitted during compilation:

warning: ring@0.17.8: Compiler family detection failed due to error: ToolNotFound: Failed to find tool. Is `riscv32-esp-elf-gcc` installed?
warning: ring@0.17.8: Compiler family detection failed due to error: ToolNotFound: Failed to find tool. Is `riscv32-esp-elf-gcc` installed?

error: failed to run custom build command for `ring v0.17.8`

Caused by:
  process didn't exit successfully: `/home/nyrmburk/documents/dev/buildnative-espidf/target/debug/build/ring-6ae3434f56ff74da/build-script-build` (exit status: 1)
  --- stdout
  cargo:rerun-if-env-changed=RING_PREGENERATE_ASM
  cargo:rustc-env=RING_CORE_PREFIX=ring_core_0_17_8_
  OPT_LEVEL = Some(z)
  TARGET = Some(riscv32imc-esp-espidf)
  OUT_DIR = Some(/home/nyrmburk/documents/dev/buildnative-espidf/target/riscv32imc-esp-espidf/debug/build/ring-513392959b65266a/out)
  HOST = Some(x86_64-unknown-linux-gnu)
  cargo:rerun-if-env-changed=CC_riscv32imc-esp-espidf
  CC_riscv32imc-esp-espidf = None
  cargo:rerun-if-env-changed=CC_riscv32imc_esp_espidf
  CC_riscv32imc_esp_espidf = None
  cargo:rerun-if-env-changed=TARGET_CC
  TARGET_CC = None
  cargo:rerun-if-env-changed=CC
  CC = None
  cargo:rerun-if-env-changed=CROSS_COMPILE
  CROSS_COMPILE = None
  RUSTC_LINKER = Some(ldproxy)
  cargo:rerun-if-env-changed=CC_ENABLE_DEBUG_OUTPUT
  cargo:warning=Compiler family detection failed due to error: ToolNotFound: Failed to find tool. Is `riscv32-esp-elf-gcc` installed?
  RUSTC_WRAPPER = None
  cargo:rerun-if-env-changed=CRATE_CC_NO_DEFAULTS
  CRATE_CC_NO_DEFAULTS = Some(1)
  cargo:rerun-if-env-changed=CFLAGS_riscv32imc-esp-espidf
  CFLAGS_riscv32imc-esp-espidf = None
  cargo:rerun-if-env-changed=CFLAGS_riscv32imc_esp_espidf
  CFLAGS_riscv32imc_esp_espidf = None
  cargo:rerun-if-env-changed=TARGET_CFLAGS
  TARGET_CFLAGS = None
  cargo:rerun-if-env-changed=CFLAGS
  CFLAGS = None
  cargo:warning=Compiler family detection failed due to error: ToolNotFound: Failed to find tool. Is `riscv32-esp-elf-gcc` installed?

  --- stderr

  error occurred: Failed to find tool. Is `riscv32-esp-elf-gcc` installed?

warning: build failed, waiting for other jobs to finish...

Adding the compiler path for ring hit some compilation failures that are outside the scope of this issue. However if it succeeded, I think the following bindgen would fail for the same reason as littlefs2.

Vollbrecht commented 2 weeks ago

In most situation the "simplest" way is let esp-idf-sys handle the bindgen creation for you. In case of your littlefs example, its probably also good to use a forked version that is known to work on esp-idf. It already exist on the esp-idf component registry, so you could add it to the build process described here. That could give you a little-fs sys module inside the esp-idf-sys. You would than go and instead of using the -sys module from littlefs2 rust crate, directly use the generated bindgens from us. In other words the littlefs2 crate would not use its own -sys module but the one you now build.

Other than that we propagate a bunch of information in the build script inside esp-idf-sys that gets produced by the embuild crate for consumption in downstream crates. This can be found here and you see it used in esp-idf-hal/svc buildscript.

Overall the creation of FFI bindings and the building of a C library into a static lib and including it are two separate things.

While not exactly the same but a similar case can be made about the rust bindings for the lvgl crate. I didn't look recently into it but i know that it can work with our system, without even knowing that we exist by essentially doing the heavy lifting by itself. They also generate a static C library and generate bindings off of that.

AlixANNERAUD commented 2 weeks ago

The same issue occurs with wamr-rust-sdk.

I'm not sure if this is related, but overriding LIBCLANG_PATH by sourcing export-esp.sh seems to interfere with the compilation of the regular x86_64-unknown-linux-gnu target when bindgen is used by crates:

  thread 'main' panicked at /home/alix_anneraud/.cargo/registry/src/index.crates.io-6f17d22bba15001f/bindgen-0.69.4/lib.rs:899:13:
  assertion `left == right` failed: "x86_64-unknown-linux-gnu" "x86_64-unknown-linux-gnu"
    left: 4
   right: 8

This issue did not occur before I updated to version 1.79.0 of the esp Rust toolchain.