Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Experimental esp-idf native cmake build #7

Merged
merged 8 commits into from
Sep 9, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 9 additions & 3 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,15 +16,21 @@ links = "esp_idf"
default-target = "x86_64-unknown-linux-gnu"

[features]
default = ["std", "embedded-svc/std"]
default = ["std", "embedded-svc/std", "pio"]

std = []

# Use `platformio` to build the `esp-idf`
pio = []
# Experimental: Use native `esp-idf` tooling to build it
native = ["strum"]

[dependencies]
mutex-trait = "0.2"
embedded-svc = "0.8.3"
paste = "1"

[build-dependencies]
embuild = "0.23"
anyhow = "1"
embuild = "0.23.5"
anyhow = { version = "1" }
strum = { version = "0.21", optional = true, features = ["derive"] }
62 changes: 56 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,20 @@ The ESP-IDF API in Rust, with support for each ESP chip (ESP32, ESP32S2, ESP32C3

## Build

- The build requires the [Rust ESP32 STD compiler fork](https://github.com/ivmarkov/rust) to be configured and installed as per the instructions there.
- The relevant Espressif toolchain, as well as the ESP-IDF itself are all automatically downloaded during the build by utilizing the [cargo-pio](https://github.com/ivmarkov/cargo-pio) library crate.
- The build requires the [Rust ESP32 STD compiler fork](https://github.com/esp-rs/rust) to be configured and installed as per the instructions there.
- The relevant Espressif toolchain, as well as the `esp-idf` itself are all automatically
downloaded during the build by
- with the feature `pio` (default): utilizing [platformio](https://platformio.org/) (via
the [embuild](https://github.com/ivmarkov/embuild) crate) or
- with the feature `native` (*experimental*): utilizing native `esp-idf` tooling.
- Check the ["Hello, World" demo](https://github.com/ivmarkov/rust-esp32-std-hello) for how to use and build this crate

## Bluetooth Support
## Feature `platformio`
This is currently the default for installing all build tools and building the `esp-idf` C
library. It uses [platformio](https://platformio.org/) via the
[embuild](https://github.com/ivmarkov/embuild) crate.

### Bluetooth Support

In order to enable Bluetooth support with either Bluedroid or NimBLE, there is some additional work:
* Go to the root of your **binary crate** project (e.g., the ["Hello, World" demo](https://github.com/ivmarkov/rust-esp32-std-hello))
Expand All @@ -32,14 +41,55 @@ CONFIG_BTDM_CTRL_MODE_BTDM=n
//CONFIG_BT_NIMBLE_ENABLED=y
```

## Using cargo-pio to interactively modify ESP-IDF's `sdkconfig` file
### Using cargo-pio to interactively modify ESP-IDF's `sdkconfig` file

To enable Bluetooth, or do other configurations to the ESP-IDF sdkconfig you might take advantage of the cargo-pio Cargo subcommand:
* To install it, issue `cargo install cargo-pio --git https://github.com/ivmarkov/cargo-pio`
* To open the ESP-IDF interactive menuconfig system, issue `cargo pio espidf menuconfig` in the root of your **binary crate** project
* To use the generated/updated `sdkconfig` file, follow the steps described in the "Bluetooth Support" section

## More info
### More info

If you are interested how it all works under the hood, check the [build_pio.rs](build.rs)
or script of this crate.


## Experimental feature `native`

Download all tools and build the `esp-idf` using its own tooling.

**Warning**: This is an experimental feature and subject to changes.

Currently, this build script installs all needed tools to compile the `esp-idf` as well as the `esp-idf` itself
under `<workspace-dir>/.espressif`, this is subject to change in the future.

### Requirements
- If using chips other than `esp32c3`:
- [Rust ESP32 compiler fork](https://github.com/esp-rs/rust)
- [libclang of the xtensa LLVM fork](https://github.com/espressif/llvm-project/releases)
- `python >= 3.7`
- `cmake >= 3.20`

### Configuration
Environment variables are used to configure how the `esp-idf` is compiled.
The following environment variables are used by the build script:

If you are interested how it all works under the hood, check the [build.rs](https://github.com/ivmarkov/esp-idf-sys/blob/master/build.rs) script of this crate.
- `SDK_DIR`: The path to the directory where all esp-idf tools are installed,
defaults to `<workspace-dir>/.espressif`.
- `ESP_IDF_VERSION`:
The version used for the `esp-idf` can be one of the following:
- `commit:<hash>`: Uses the commit `<hash>` of the `esp-idf` repository.
Note that this will clone the whole `esp-idf` not just one commit.
- `tag:<tag>`: Uses the tag `<tag>` of the `esp-idf` repository.
- `branch:<branch>`: Uses the branch `<branch>` of the `esp-idf` repository.
- `v<major>.<minor>` or `<major>.<minor>`: Uses the tag `v<major>.<minor>` of the `esp-idf` repository.
- `<branch>`: Uses the branch `<branch>` of the `esp-idf` repository.

It defaults to `v4.3`.
- `ESP_IDF_REPOSITORY`: The URL to the git repository of the `esp-idf`, defaults to <https://github.com/espressif/esp-idf.git>.
- `ESP_IDF_SDKCONFIG_DEFAULTS`: A `;`-seperated list of paths to `sdkconfig.default` files to be used as base
values for the `sdkconfig`.
- `ESP_IDF_SDKCONFIG`: A path (absolute or relative) to the esp-idf `sdkconfig` file.
- `ESP_IDF_EXTRA_TOOLS`: A `;`-seperated list of additional tools to install with `idf_tools.py`.
- `MCU`: The mcu name (e.g. `esp32` or `esp32c3`). If not set this will be automatically detected from the
cargo target.
152 changes: 16 additions & 136 deletions build.rs
Original file line number Diff line number Diff line change
@@ -1,136 +1,16 @@
use std::convert::TryFrom;
use std::{env, path::PathBuf};

use anyhow::*;

use embuild::bindgen;
use embuild::build;
use embuild::cargo;
use embuild::kconfig;
use embuild::pio;
use embuild::pio::project;

fn main() -> Result<()> {
let pio_scons_vars = if let Some(pio_scons_vars) = project::SconsVariables::from_piofirst() {
println!("cargo:info=PIO->Cargo build detected: generating bindings only");

pio_scons_vars
} else {
let pio = pio::Pio::install_default()?;

let resolution = pio::Resolver::new(pio.clone())
.params(pio::ResolutionParams {
platform: Some("espressif32".into()),
frameworks: vec!["espidf".into()],
target: Some(env::var("TARGET")?),
..Default::default()
})
.resolve(true)?;

let mut builder =
project::Builder::new(PathBuf::from(env::var("OUT_DIR")?).join("esp-idf"));

builder
.enable_scons_dump()
.enable_c_entry_points()
.options(build::env_options_iter("ESP_IDF_SYS_PIO_CONF")?)
.files(build::tracked_globs_iter(
PathBuf::from("."),
&["patches/**"],
)?)
.files(build::tracked_env_globs_iter("ESP_IDF_SYS_GLOB")?);

#[cfg(feature = "espidf_master")]
builder
.platform_package(
"framework-espidf",
"https://github.com/ivmarkov/esp-idf.git#master",
)
.platform_package_patch(
PathBuf::from("patches").join("master_missing_xtensa_atomics_fix.diff"),
PathBuf::from("framework-espidf"),
)
.platform_package_patch(
PathBuf::from("patches").join("ping_setsockopt_fix.diff"),
PathBuf::from("framework-espidf"),
);

#[cfg(feature = "espidf_master")]
env::set_var("IDF_MAINTAINER", "1");

#[cfg(not(feature = "espidf_master"))]
builder
.platform_package_patch(
PathBuf::from("patches").join("pthread_destructor_fix.diff"),
PathBuf::from("framework-espidf"),
)
.platform_package_patch(
PathBuf::from("patches").join("ping_setsockopt_fix.diff"),
PathBuf::from("framework-espidf"),
)
.platform_package_patch(
PathBuf::from("patches").join("missing_xtensa_atomics_fix.diff"),
PathBuf::from("framework-espidf"),
);

let project_path = builder.generate(&resolution)?;

pio.build(&project_path, env::var("PROFILE")? == "release")?;

let pio_scons_vars = project::SconsVariables::from_dump(&project_path)?;

build::LinkArgsBuilder::try_from(&pio_scons_vars)?
.build()
.propagate();

pio_scons_vars
};

// In case other SYS crates need to have access to the ESP-IDF C headers
build::CInclArgs::try_from(&pio_scons_vars)?.propagate();

let cfg_args = kconfig::CfgArgs::try_from(
pio_scons_vars
.project_dir
.join(if pio_scons_vars.release_build {
"sdkconfig.release"
} else {
"sdkconfig.debug"
})
.as_path(),
)?;

cfg_args.propagate("ESP_IDF");
cfg_args.output("ESP_IDF");

let mcu = pio_scons_vars.mcu.as_str();

// Output the exact ESP32 MCU, so that we and crates depending directly on us can branch using e.g. #[cfg(esp32xxx)]
println!("cargo:rustc-cfg={}", mcu);
println!("cargo:MCU={}", mcu);

let header = PathBuf::from("src")
.join("include")
.join(if mcu == "esp8266" {
"esp-8266-rtos-sdk"
} else {
"esp-idf"
})
.join("bindings.h");

cargo::track_file(&header);

bindgen::run(
bindgen::Factory::from_scons_vars(&pio_scons_vars)?
.builder()?
.ctypes_prefix("c_types")
.header(header.to_string_lossy())
.blacklist_function("strtold")
.blacklist_function("_strtold_r")
.clang_args(if mcu == "esp32c3" {
vec!["-target", "riscv32"]
} else {
vec![]
}),
)
}
#[cfg(not(any(feature = "pio", feature = "native")))]
compile_error!("One of the features `pio` or `native` must be selected.");

// Note that the feature `native` must come before `pio`. These features are really
// mutually exclusive but that would require that all dependencies specify the same
// feature so instead we prefer the `native` feature over `pio` so that if one package
// specifies it, this overrides the `pio` feature for all other dependencies too.
// See https://doc.rust-lang.org/cargo/reference/features.html#mutually-exclusive-features.
#[cfg(any(feature = "pio", feature = "native"))]
#[cfg_attr(feature = "native", path = "build_native.rs")]
#[cfg_attr(all(feature = "pio", not(feature = "native")), path = "build_pio.rs")]
mod build_impl;

fn main() -> anyhow::Result<()> {
build_impl::main()
}
Loading