espressif/rust-esp32-example

Allow using esp-idf-svc with the CMake build

maelp opened this issue · 31 comments

maelp commented

Hi,

Thanks for this really nice repository! We're a French company using ESP32 for our products, and we are really looking forward to being able to use embedded Rust.

We saw both rust-esp32-example (this repo, CMake-based build) and https://github.com/ivmarkov/rust-esp32-std-hello (a nice "cargo-first" build from @ivmarkov which also allows to use the nice https://github.com/esp-rs/esp-idf-svc which gives access to nice Rust wrappers for most of ESP-IDF libraries).

For now, because our codebase is C/C++, we'd be happy to use this CMake-based build rather than the "cargo-first" build (as we want to incrementally modify modules of our code by replacing them with Rust code), but it seems we can't use esp-idf-svc with it out-of-the-box.

Would it be possible to update this code, or add an example of how to do a CMake-based build that also allows to use esp-idf-svc?

@maelp With the changes to the esp-idf-sys crate now upstreamed, and with the 4 environment variables defined in this demo-crate, you should in fact be able to use esp-idf-sys and esp-idf-svc in CMake-based builds.

To get you a head-start, I've (temporarily) forked this repo here and I've basically enhanced it with all of the demo code that is available in the rust-esp32-std-hello crate.

So just

  • Pull my fork above
  • Change ssid & pass here
  • idf.py set-target esp32
  • idf.py build

Flash as per the build instructions and you should be good to go.

Oh: you need the esp Rust build of course, as well as the Xtensa clang fork, as usual.

Let me know if you face issues.

maelp commented

Thanks @ivmarkov that's awesome, I'm going to try it right now and let you know if it works as expected!

maelp commented

For now I have a linking error with

  = note: ld: library not found for -liconv
          collect2: error: ld returned 1 exit status

(although I installed libiconv)

Perhaps I have to add something to the CMake file or add a LD_LIBRARY_PATH ?

Can you provide some extra context:

  • Does the build work for you for the plain (un-forked) rust-esp32-example?
  • Are you using ESP-IDF master, or V4.3? Honestly, I only tried with master (sorry!)
  • Can you provide some extra logs exactly when during the build this happens? When linking the app elf image, or during the bootloader linking?... Or much earlier? Installing "libiconv" would likely NOT help, because I can't imagine how an OS library compiled for X86_64 can be linked into the ESP-IDF Xtensa code...
maelp commented

Hey @ivmarkov, the original rust example used to compile on my machine, but it seems that the most recent commit fails with the same error, I'll try to build a few commits or git bisect to try to find which commit introduced the error,

as a reference, I had built the example successfully on commit ad2066c

maelp commented

@ivmarkov I reinstalled the latest master branch of esp-idf locally, and ran again the compilation on the commit that worked before, and still have the same error (although I know for sure that a while ago I managed to build it).

Any idea how I could debug why it requires this library?

maelp commented

I'm not sure whether libiconv is really needed for the compilation though, when I grep the esp-idf repo for iconv, I find it in one example

examples/peripherals/secure_element/atecc608_ecdsa/components/esp-cryptoauthlib/cryptoauthlib/third_party/hidapi/libusb/hid.c
49:#include <iconv.h>

and then mostly in the Doxyfile for documentation

examples/build_system/cmake/import_lib/main/lib/tinyxml2/dox
22:# before the first occurrence of this tag. Doxygen uses libiconv (or the iconv
23:# built into libc) for the transcoding. See http://www.gnu.org/software/libiconv
793:# libiconv (or the iconv built into libc) for the transcoding. See the libiconv
794:# documentation (see: http://www.gnu.org/software/libiconv) for the list of

maelp commented

This is the build command that fails

"cc" \
"-m64" \
"-arch" "x86_64" \
"[redacted_path]/rust-esp32-example-espressif/build/esp-idf/rustlib/target/release/build/proc-macro2-3ec969f85280340b/build_script_build-3ec969f85280340b.build_script_build.ca656uf2-cgu.0.rcgu.o" \
"[redacted_path]/rust-esp32-example-espressif/build/esp-idf/rustlib/target/release/build/proc-macro2-3ec969f85280340b/build_script_build-3ec969f85280340b.build_script_build.ca656uf2-cgu.1.rcgu.o" \
"[redacted_path]/rust-esp32-example-espressif/build/esp-idf/rustlib/target/release/build/proc-macro2-3ec969f85280340b/build_script_build-3ec969f85280340b.build_script_build.ca656uf2-cgu.10.rcgu.o" \
"[redacted_path]/rust-esp32-example-espressif/build/esp-idf/rustlib/target/release/build/proc-macro2-3ec969f85280340b/build_script_build-3ec969f85280340b.build_script_build.ca656uf2-cgu.11.rcgu.o" \
"[redacted_path]/rust-esp32-example-espressif/build/esp-idf/rustlib/target/release/build/proc-macro2-3ec969f85280340b/build_script_build-3ec969f85280340b.build_script_build.ca656uf2-cgu.12.rcgu.o" \
"[redacted_path]/rust-esp32-example-espressif/build/esp-idf/rustlib/target/release/build/proc-macro2-3ec969f85280340b/build_script_build-3ec969f85280340b.build_script_build.ca656uf2-cgu.13.rcgu.o" \
"[redacted_path]/rust-esp32-example-espressif/build/esp-idf/rustlib/target/release/build/proc-macro2-3ec969f85280340b/build_script_build-3ec969f85280340b.build_script_build.ca656uf2-cgu.14.rcgu.o" \
"[redacted_path]/rust-esp32-example-espressif/build/esp-idf/rustlib/target/release/build/proc-macro2-3ec969f85280340b/build_script_build-3ec969f85280340b.build_script_build.ca656uf2-cgu.15.rcgu.o" \
"[redacted_path]/rust-esp32-example-espressif/build/esp-idf/rustlib/target/release/build/proc-macro2-3ec969f85280340b/build_script_build-3ec969f85280340b.build_script_build.ca656uf2-cgu.2.rcgu.o" \
"[redacted_path]/rust-esp32-example-espressif/build/esp-idf/rustlib/target/release/build/proc-macro2-3ec969f85280340b/build_script_build-3ec969f85280340b.build_script_build.ca656uf2-cgu.3.rcgu.o" \
"[redacted_path]/rust-esp32-example-espressif/build/esp-idf/rustlib/target/release/build/proc-macro2-3ec969f85280340b/build_script_build-3ec969f85280340b.build_script_build.ca656uf2-cgu.4.rcgu.o" \
"[redacted_path]/rust-esp32-example-espressif/build/esp-idf/rustlib/target/release/build/proc-macro2-3ec969f85280340b/build_script_build-3ec969f85280340b.build_script_build.ca656uf2-cgu.5.rcgu.o" \
"[redacted_path]/rust-esp32-example-espressif/build/esp-idf/rustlib/target/release/build/proc-macro2-3ec969f85280340b/build_script_build-3ec969f85280340b.build_script_build.ca656uf2-cgu.6.rcgu.o" \
"[redacted_path]/rust-esp32-example-espressif/build/esp-idf/rustlib/target/release/build/proc-macro2-3ec969f85280340b/build_script_build-3ec969f85280340b.build_script_build.ca656uf2-cgu.7.rcgu.o" \
"[redacted_path]/rust-esp32-example-espressif/build/esp-idf/rustlib/target/release/build/proc-macro2-3ec969f85280340b/build_script_build-3ec969f85280340b.build_script_build.ca656uf2-cgu.8.rcgu.o" \
"[redacted_path]/rust-esp32-example-espressif/build/esp-idf/rustlib/target/release/build/proc-macro2-3ec969f85280340b/build_script_build-3ec969f85280340b.build_script_build.ca656uf2-cgu.9.rcgu.o" \
"[redacted_path]/rust-esp32-example-espressif/build/esp-idf/rustlib/target/release/build/proc-macro2-3ec969f85280340b/build_script_build-3ec969f85280340b.1za4lfu810eqk51f.rcgu.o" \
"-L" "[redacted_path]/rust-esp32-example-espressif/build/esp-idf/rustlib/target/release/deps" \
"-L" "/Users/primet/.rustup/toolchains/esp/lib/rustlib/x86_64-apple-darwin/lib" "/Users/primet/.rustup/toolchains/esp/lib/rustlib/x86_64-apple-darwin/lib/libstd-5391a9c2677cf581.rlib" "/Users/primet/.rustup/toolchains/esp/lib/rustlib/x86_64-apple-darwin/lib/libpanic_unwind-3bbc2df779345b87.rlib" "/Users/primet/.rustup/toolchains/esp/lib/rustlib/x86_64-apple-darwin/lib/libobject-ba2085ff30a375ca.rlib" "/Users/primet/.rustup/toolchains/esp/lib/rustlib/x86_64-apple-darwin/lib/libaddr2line-60eb3f1dc0a88ccf.rlib" "/Users/primet/.rustup/toolchains/esp/lib/rustlib/x86_64-apple-darwin/lib/libgimli-3c8cee53c8082ecf.rlib" "/Users/primet/.rustup/toolchains/esp/lib/rustlib/x86_64-apple-darwin/lib/libstd_detect-4659320ab27ece1f.rlib" "/Users/primet/.rustup/toolchains/esp/lib/rustlib/x86_64-apple-darwin/lib/librustc_demangle-38c209974c1e0383.rlib" "/Users/primet/.rustup/toolchains/esp/lib/rustlib/x86_64-apple-darwin/lib/libhashbrown-732c4590e3658e3f.rlib" "/Users/primet/.rustup/toolchains/esp/lib/rustlib/x86_64-apple-darwin/lib/librustc_std_workspace_alloc-fd3e472dbd6e7b52.rlib" "/Users/primet/.rustup/toolchains/esp/lib/rustlib/x86_64-apple-darwin/lib/libunwind-50f6ed39e54308d3.rlib" "/Users/primet/.rustup/toolchains/esp/lib/rustlib/x86_64-apple-darwin/lib/libcfg_if-7203489bd3ec4028.rlib" "/Users/primet/.rustup/toolchains/esp/lib/rustlib/x86_64-apple-darwin/lib/liblibc-3d6f48823f80dcaa.rlib" "/Users/primet/.rustup/toolchains/esp/lib/rustlib/x86_64-apple-darwin/lib/liballoc-fd99f8a81c04f070.rlib" "/Users/primet/.rustup/toolchains/esp/lib/rustlib/x86_64-apple-darwin/lib/librustc_std_workspace_core-41d8a5c3c9ceb90b.rlib" "/Users/primet/.rustup/toolchains/esp/lib/rustlib/x86_64-apple-darwin/lib/libcore-f043409408606858.rlib" "/Users/primet/.rustup/toolchains/esp/lib/rustlib/x86_64-apple-darwin/lib/libcompiler_builtins-ed99a4cb6d3f51aa.rlib" "-lSystem" "-lresolv" "-lc" "-lm" "-liconv" \
"-L" "/Users/primet/.rustup/toolchains/esp/lib/rustlib/x86_64-apple-darwin/lib" "-o" \
"[redacted_path]/rust-esp32-example-espressif/build/esp-idf/rustlib/target/release/build/proc-macro2-3ec969f85280340b/build_script_build-3ec969f85280340b" "-Wl,-dead_strip" "-nodefaultlibs"
  = note: ld: library not found for -liconv
          collect2: error: ld returned 1 exit status
maelp commented

As a reference, this is how I install the toolchain

rustup toolchain install nightly

VERSION="1.54.0-dev"
ARCH="x86_64-apple-darwin"
RUST_DIST="rust-${VERSION}-${ARCH}"
RUST_SRC_DIST="rust-src-${VERSION}"
TOOLCHAIN_DESTINATION_DIR="~/.rustup/toolchains/esp"

mkdir -p ${TOOLCHAIN_DESTINATION_DIR}

curl -O "https://dl.espressif.com/dl/idf-rust/dist/${ARCH}/${RUST_DIST}.tar.xz"
tar xvf ${RUST_DIST}.tar.xz
./${RUST_DIST}/install.sh --destdir=${TOOLCHAIN_DESTINATION_DIR} --prefix="" --without=rust-docs

curl -O "https://dl.espressif.com/dl/idf-rust/dist/noarch/${RUST_SRC_DIST}.tar.xz"
tar xvf ${RUST_SRC_DIST}.tar.xz
./${RUST_SRC_DIST}/install.sh --destdir=${TOOLCHAIN_DESTINATION_DIR} --prefix="" --without=rust-docs

rustup default esp

curl -O "https://dl.espressif.com/dl/idf-rust/dist/${ARCH}/xtensa-esp32-elf-llvm11_0_0-x86_64-apple-darwin.tar.xz"
tar xf xtensa-esp32-elf-llvm11_0_0-x86_64-apple-darwin.tar.xz
export PATH="`pwd`/xtensa-esp32-elf-clang/bin/:$PATH"

cd rust-esp32-example && get_idf && idf.py set-target esp32 && idf.py clean
maelp commented

The error seems to happen at that step:

[781/1015] Performing build step for 'rustlib_project'
FAILED: esp-idf/rustlib/stamp/rustlib_project-build esp-idf/rustlib/target/RustApi.h esp-idf/rustlib/target/xtensa-esp32-espidf/release/librustlib.a /Users/primet/work/gouach/embedded-nobackup/rust-esp32-example-espressif/build/esp-idf/rustlib/stamp/rustlib_project-build /Users/primet/work/gouach/embedded-nobackup/rust-esp32-example-espressif/build/esp-idf/rustlib/target/RustApi.h /Users/primet/work/gouach/embedded-nobackup/rust-esp32-example-espressif/build/esp-idf/rustlib/target/xtensa-esp32-espidf/release/librustlib.a

OK got it.

So where it is failing, is at a compilation stage which is NOT really building the esp-idf-* crates themselves, but is building a Rust script that will itself do a customized build of the esp-idf-* crates.

Background: since cargo is a declarative build tool, the way how you do very-special-customized build tasks is by providing - next to the Cargo.toml file - a build.rs file which Cargo is compiling and executing on your machine as part of the build itself.

So what I am trying to say is that installing libiconv - as a x86_64 library - on your own operating system - using the OS means to do this installation (be it apt-get or brew) is the way forward.

Now why it did not work for you even though you said you installed libiconv is the issue to be solved. I guess you tried something like this already? Perhaps you need to reboot your Mac or something?

maelp commented

Ha interesting, indeed if I do a docker run -it espressif/idf-rust-examples and build your code in it it works, so it might be something with libiconv

maelp commented

I tried with brew and with nix, and in both cases the libiconv is not detected, I tried adding the path to LD_LIBRARY_PATH before building, or to CFLAGS and LDFLAGS, but it does not seem to work

This stackoverflow thread might be relevant. Unfortunately no solution there. Do you happen to run an M1 Mac as well???

maelp commented

I'm on a regular intel mac :) are there some cargo flags that I can use to tell the build about other library paths?

maelp commented

If I copy paste the line of the code which does not compile, and simply remove the -liconv it compiles correctly, so perhaps that's something I should change somewhere in the script that tries to build build.rs?

maelp commented

And if I install libiconv with brew and add

LDFLAGS="-L/usr/local/opt/libiconv/lib" CCFLAGS="-I/usr/local/opt/libiconv/include" 

in front of the cc command it still does not find the lib, although

ls /usr/local/opt/libiconv/lib
libcharset.1.dylib  libcharset.dylib  libiconv.a
libcharset.a	    libiconv.2.dylib  libiconv.dylib
maelp commented

But if I include the flags "-L/usr/local/opt/libiconv/lib -I/usr/local/opt/libiconv/include" directly in front of the "-liconv" on the build command, it compiles, so I guess the issue is that for some reason the build does not take into account the LDFLAGS and CCFLAGS

For now perhaps I will use the Docker build, do you know if there's an easy way to do a build in docker and retrieve the built binary, so that I can flash it from my laptop on the ESP (since it's complicated to bind the USB ports to Docker containers on a mac)?

The fact that CCFLAGS is not considered is fine. This is Rust after all, not C, so these includes cannot be used by Rust anyway. (And I think CC is used only as a linker, and most likely only on Mac)

Now, instead of LDFLAGS, it seems you need RUSTFLAGS. Check here.

If I copy paste the line of the code which does not compile, and simply remove the -liconv it compiles correctly, so perhaps that's something I should change somewhere in the script that tries to build build.rs?

There is no "script" that tries to build the build.rs script. This is just cargo, building build.rs using the [build-dependencies] section in Cargo.toml.

Now, the fact, that removing -liconv does not result in a linkage error problably means that the -liconv flag is not coming from dependencies of build.rs (in [build-dependencies]), but somehow from the combination of the Rust compiler itself + your OS env.

Hence why I pointed you at the stackoverflow thread above where they try to compile a supersimple "Hello, world" app on Mac, and it still does not work.
So, can you actually build even a simple "Hello world" Rust app with the esp rust toolchain? And ditto for the nightly/stable toolchains?

  • Like, cloning this one
  • Then rustup default esp
  • Then, cargo build?
  • Then repeat the same but with rustup default nightly and then rustup default stable?

For now perhaps I will use the Docker build, do you know if there's an easy way to do a build in docker and retrieve the built binary, so that I can flash it from my laptop on the ESP (since it's complicated to bind the USB ports to Docker containers on a mac)?

Not really, @georgik might be able to help.

maelp commented

Compiling the hello-world:

with esp: fails with the iconv error

with nightly: fails with the iconv error

with stable: fails with the iconv error

maelp commented

The error seems to be a Nix thing, I can make the hello-world example compile using nix-shell -p cargo rustc libiconv

maelp commented

I guess the easiest for now is to build using a Docker container :)

OK since the error is really somehow with your Rust installation (basic hello-world fails to build too), please report if after flashing the thing works, and if it does, I think we can close this one. :)

P.S. RUSTFLAGS should work, but you might have to wrap the -L argument in something. Maybe -c; not sure.

maelp commented

Well, when building the code in the docker container it seems to work but it stalls when doing this

[1/15] Performing build step for 'bootloader'
[1/1] cd /opt/rust/rust-esp32-example/build/bootloader/esp-idf/esptool_py && /opt/esp/python_env/idf4.4_py3.8_env/bin/python /opt/esp/idf/components/partition_table/check_sizes.py --offset 0x8000 bootloader 0x1000 /opt/rust/rust-esp32-example/build/bootloader/bootloader.bin
Bootloader binary size 0x6350 bytes. 0xcb0 bytes (11%) free.
[1000/1012] Linking C static library esp-idf/clib/libclib.a

and then nothing is displayed and it seems to hang, does it do the same for you?

It does not stall. It just takes a lot of time (not sure why).

maelp commented

Weird, it seems to be perhaps 1h that I left it like that, do you know how long it takes on your laptop?

maelp commented

The build completed (after perhaps one hour or so), and I could successfully flash and run it :) although I will need to have a way to build faster for this to be useable haha

On my laptop it takes a couple of minutes at most. If not less than a minute.

I suggest you open a separate issue, so that Espressif & myself can look into what is causing the slowness later.
In the meantime, ideally you should somehow fix your native Rust environment, because there the build time would be much more tolerable, as per above.

maelp commented

Perfect :)

maelp commented

I've added #40 and I'm closing this issue