Skip to content

Commit

Permalink
Experimental esp-idf native cmake build (espressif#7)
Browse files Browse the repository at this point in the history
* Implement native esp-idf cmake build

Move `build.rs` contents to `build_pio.rs`
Add feature `native`
Add optional dependency `strum`

* Remove main wrapper

* Fix patches not getting applied, use correct patches

* Clarify some `Chip` methods, fix python virtualenv path

Use correct target name for `riscv32`.

* Fix pio build, tidy code

Fix potentially using `:` to split PATH on windows.

* Add documentation to README

* Change DEFAULT_SDK_DIR to `.espressif`

Set min python version to 3.7.
Set default build feature to pio.
Make `native` feature usable.

* Track `sdkconfig` and `sdkconfig_defaults` files.
  • Loading branch information
N3xed authored Sep 9, 2021
1 parent 6d08b4f commit b6174b5
Show file tree
Hide file tree
Showing 7 changed files with 652 additions and 145 deletions.
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

0 comments on commit b6174b5

Please sign in to comment.