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

Feature/specify built components #115

Merged
merged 2 commits into from
Aug 2, 2022
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
140 changes: 110 additions & 30 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,6 @@
[![CI](https://github.com/esp-rs/esp-idf-sys/actions/workflows/ci.yml/badge.svg)](https://github.com/esp-rs/esp-idf-sys/actions/workflows/ci.yml)
[![Documentation](https://img.shields.io/badge/docs-esp--rs-brightgreen)](https://esp-rs.github.io/esp-idf-sys/esp_idf_sys/index.html)

## Background

The ESP-IDF API in Rust, with support for each ESP chip (ESP32, ESP32S2, ESP32S3, ESP32C3, etc.) based on the Rust target.

For more information, check out:
Expand All @@ -16,6 +14,15 @@ For more information, check out:
* The [Rust for Xtensa toolchain](https://github.com/esp-rs/rust-build)
* The [Rust-with-STD demo](https://github.com/ivmarkov/rust-esp32-std-demo) project

**Table of contents**
- [Build](#build)
- [Features](#features)
- [sdkconfig](#sdkconfig)
- [Build configuration](#build-configuration)
- [Extra esp-idf components](#extra-esp-idf-components)
- [Conditional compilation](#conditional-compilation)
- [More info](#more-info)

## Build

- To build this crate, please follow all the build requirements specified in the [ESP-IDF Rust Hello World template crate](https://github.com/esp-rs/esp-idf-template)
Expand All @@ -26,34 +33,43 @@ For more information, check out:
- Check the [ESP-IDF Rust Hello World template crate](https://github.com/esp-rs/esp-idf-template) for a "Hello, world!" Rust template demonstrating how to use and build this crate.
- Check the [demo](https://github.com/ivmarkov/rust-esp32-std-demo) crate for a more comprehensive example in terms of capabilities.

## Feature `native`
This is the default feature for downloading all tools and building the ESP-IDF framework using the framework's "native" (own) tooling.
It relies on build and installation utilities available in the [embuild](https://github.com/ivmarkov/embuild) crate.
## Features
- ### `native`
This is the default feature for downloading all tools and building the ESP-IDF framework using the framework's "native" (own) tooling.
It relies on build and installation utilities available in the [embuild](https://github.com/ivmarkov/embuild) crate.

The `native` builder installs all needed tools to compile this crate as well as the ESP-IDF framework itself.
The `native` builder installs all needed tools to compile this crate as well as the ESP-IDF framework itself.

### (Native builder only) Using cargo-idf to interactively modify ESP-IDF's `sdkconfig` file
- ### `pio`

TBD: Upcoming
This is a backup feature for installing all build tools and building the ESP-IDF framework. It uses [PlatformIO](https://platformio.org/) via the
[embuild](https://github.com/ivmarkov/embuild) crate.

## Feature `pio`
This is a backup feature for installing all build tools and building the ESP-IDF framework. It uses [PlatformIO](https://platformio.org/) via the
[embuild](https://github.com/ivmarkov/embuild) crate.
Similarly to the `native` builder, the `pio` builder also automatically installs all needed tools (PlatformIO packages and frameworks in this case) to compile this crate as well as the ESP-IDF framework itself.

Similarly to the `native` builder, the `pio` builder also automatically installs all needed tools (PlatformIO packages and frameworks in this case) to compile this crate as well as the ESP-IDF framework itself.
> ⚠️ The `pio` builder is less flexible than the default `native` builder in that it can work with only **one, specific** version of ESP-IDF. At the time of writing, this is V4.3.2.

**NOTE:** The `pio` builder is less flexible than the default `native` builder in that it can work with only **one, specific** version of ESP-IDF. At the time of writing, this is V4.3.2.
## sdkconfig

### (PIO builder only) Using cargo-pio to interactively modify ESP-IDF's `sdkconfig` file
The esp-idf makes use of an [sdkconfig](#espidfsdkconfig-espidfsdkconfig) file for its
compile-time component configuration (see the [esp-idf
docs](https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/kconfig.html#project-configuration)
for more information). This config is separate from the [build configuration](#build-configuration).

### (*native* builder only) Using cargo-idf to interactively modify ESP-IDF's `sdkconfig` file

TBD: Upcoming

### (*pio* builder only) 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

## Configuration
## Build configuration

There are two ways to configure how the ESP-IDF framework is compiled.
There are two ways to configure how the ESP-IDF framework is compiled:
1. Environment variables, denoted by `$VARIABLE`;

> The environment variables can be passed on the command line, or put into the `[env]`
Expand All @@ -70,11 +86,15 @@ There are two ways to configure how the ESP-IDF framework is compiled.

> ⚠️ Environment variables always take precedence over `Cargo.toml` metadata.

> 🛈 **Note**: *workspace directory*
> 🛈 **Note**: *workspace directory*
> The workspace directory mentioned here is always the directory containing the
> `Cargo.lock` file and the `target` directory (where the build artifacts are stored). It
> can be overridden with the `CARGO_WORKSPACE_DIR` environment variable, should this not
> be the right directory.
> (See
> [`embuild::cargo::workspace_dir`](https://docs.rs/embuild/latest/embuild/cargo/fn.workspace_dir.html)
> for more information).
>
> The workspace directory mentioned here is always the
> directory containing the `Cargo.lock` file and the `target` directory (where the build
> artifacts are stored).
> There is no need to explicitly add a
> [`[workspace]`](https://doc.rust-lang.org/cargo/reference/workspaces.html#the-workspace-section)
> section to the `Cargo.toml` of the workspace directory.
Expand All @@ -90,6 +110,8 @@ The following configuration options are available:

Defaults to `sdkconfig.defaults`.

In case of the environment variable, multiple elements should be `;`-separated.

> 🛈 **Note**
> For each defaults file in this list, a more specific file will also be searched and
> used. This happens with the following patterns and order (least to most specific):
Expand Down Expand Up @@ -131,7 +153,7 @@ The following configuration options are available:
> determine the compiler optimizations of the `esp-idf`, **however** if the compiler
> optimization options are already set in the `sdkconfig` **they will be used instead.**

- ### *`esp_idf_tools_install_dir`*, `$ESP_IDF_TOOLS_INSTALL_DIR`:
- ### *`esp_idf_tools_install_dir`*, `$ESP_IDF_TOOLS_INSTALL_DIR`

The install location for the ESP-IDF framework tooling.

Expand All @@ -147,7 +169,7 @@ The following configuration options are available:
directory](https://doc.rust-lang.org/cargo/reference/build-scripts.html#outputs-of-the-build-script),
and will be deleted when `cargo clean` is invoked;
- `global` - the tooling will be installed or used in its standard directory
(`~/.platformio` for PlatformIO, and `~./espressif` for the native ESP-IDF toolset);
(`~/.platformio` for PlatformIO, and `~/.espressif` for the native ESP-IDF toolset);
- `custom:<dir>` - the tooling will be installed or used in the directory specified by
`<dir>`. If this directory is a relative location, it is assumed to be relative to
the *workspace directory*;
Expand All @@ -164,7 +186,7 @@ The following configuration options are available:
> the builder will install the tooling in `<dir>` without using any additional `platformio` or `espressif` subdirectories, so if you are not careful, you might end up with
> both PlatformIO, as well as the ESP-IDF native tooling intermingled together in a single folder.

> &#128712; **Note**
> &#128712; **Note**
> The [ESP-IDF git repository](https://github.com/espressif/esp-idf) will be cloned
> *inside* the tooling directory. The *native* builder will use the esp-idf at
> [*`idf_path`*](#idfpath-idfpath-native-builder-only) of available.
Expand All @@ -173,7 +195,7 @@ The following configuration options are available:
A path to a user-provided local clone of the [esp-idf](https://github.com/espressif/esp-idf),
that will be used instead of the one downloaded by the build script.

- ### *`esp_idf_version`*, `$ESP_IDF_VERSION` (*native* builder only):
- ### *`esp_idf_version`*, `$ESP_IDF_VERSION` (*native* builder only)

The version used for the `esp-idf`, can be one of the following:
- `commit:<hash>`: Uses the commit `<hash>` of the `esp-idf` repository.
Expand All @@ -185,7 +207,7 @@ The following configuration options are available:

Defaults to `v4.4.1`.

- ### *`esp_idf_repository`*, `$ESP_IDF_REPOSITORY` (*native* builder only):
- ### *`esp_idf_repository`*, `$ESP_IDF_REPOSITORY` (*native* builder only)

The URL to the git repository of the `esp-idf`, defaults to <https://github.com/espressif/esp-idf.git>.

Expand All @@ -201,7 +223,7 @@ The following configuration options are available:
> its own ESP-IDF distribution, so the user-provided ESP-IDF branch may or may not compile. The current
> PlatformIO tooling is suitable for compiling ESP-IDF branches derived from versions 4.3.X and 4.4.X.

- ### `$ESP_IDF_GLOB[_XXX]_BASE` and `$ESP_IDF_GLOB[_XXX]_YYY`:
- ### `$ESP_IDF_GLOB[_XXX]_BASE` and `$ESP_IDF_GLOB[_XXX]_YYY`

A pair of environment variable prefixes that enable copying files and directory trees that match a certain glob mask into the native C project used for building the ESP-IDF framework:
- `ESP_IDF_GLOB[_XXX]_BASE` specifies the base directory which will be glob-ed for resources to be copied
Expand All @@ -214,7 +236,7 @@ The following configuration options are available:
Note also that `_HOMEDIR` in the above example is optional, and is just a mechanism allowing the user to specify more than one base directory and its glob patterns.


- ### `$ESP_IDF_PIO_CONF_XXX` (*pio* builder only):
- ### `$ESP_IDF_PIO_CONF_XXX` (*pio* builder only)

A PlatformIO setting (or multiple settings separated by a newline) that will be passed
as-is to the `platformio.ini` file of the C project that compiles the ESP-IDF.
Expand All @@ -228,7 +250,7 @@ The following configuration options are available:
> starting with `ESP_IDF_PIO_CONF_`. For example, passing `ESP_IDF_PIO_CONF_1` as well as
> `ESP_IDF_PIO_CONF_FOO` is valid and all such variables will be honored.

- ### *`esp_idf_cmake_generator`*, `$ESP_IDF_CMAKE_GENERATOR` (*native* builder only):
- ### *`esp_idf_cmake_generator`*, `$ESP_IDF_CMAKE_GENERATOR` (*native* builder only)

The CMake generator to be used when building the ESP-IDF.

Expand All @@ -241,14 +263,39 @@ The following configuration options are available:
supports](https://cmake.org/cmake/help/latest/manual/cmake-generators.7.html#cmake-generators)
with **spaces and hyphens removed**.

- ### *`mcu`*, `$MCU`:
- ### *`mcu`*, `$MCU`

The MCU name (i.e. `esp32`, `esp32s2`, `esp32s3` `esp32c3` and `esp32h2`).

If not set this will be automatically detected from the cargo target.

> ⚠️
> [Older ESP-IDF versions might not support all MCUs from above](https://github.com/espressif/esp-idf#esp-idf-release-and-soc-compatibility).
> [Older ESP-IDF versions might not support all MCUs from above.](https://github.com/espressif/esp-idf#esp-idf-release-and-soc-compatibility)

- ### *`esp_idf_components`*, `$ESP_IDF_COMPONENTS` (*native* builder only)

The list of esp-idf components (names) that should be built. This list is used to
trim the esp-idf build. Any component that is a dependency of a component in this
list will also automatically be built.

Defaults to all components being built.

> &#128712; **Note**
> Some components must be explicitly enabled in the sdkconfig.
> [Extra components](#extra-esp-idf-components) must also be added to this list if
> they are to be built.

### Example

An example of the `[package.metadata.esp-idf-sys]` section of the `Cargo.toml`.
```toml
[package.metadata.esp-idf-sys]
esp_idf_tools_install_dir = "global"
esp_idf_sdkconfig = "sdkconfig"
esp_idf_sdkconfig_defaults = ["sdkconfig.defaults", "sdkconfig.defaults.ble"]
# native builder only
esp_idf_version = "branch:release/v4.4"
```

## Extra esp-idf components

Expand Down Expand Up @@ -310,6 +357,39 @@ extra_components = [
]
```

## Conditional compilation

The *esp-idf-sys* build script will set [rustc *cfg*s](https://doc.rust-lang.org/reference/conditional-compilation.html)
available for its sources.

> ⚠️ If an upstream crate also wants to have access to the *cfg*s it must:
> - have `esp-idf-sys` as a dependency, and
> - propagate the *cfg*s in its [build
> script](https://doc.rust-lang.org/cargo/reference/build-scripts.html) with
>
> ```rust
> embuild::build::CfgArgs::output_propagated("ESP_IDF").expect("no esp-idf-sys cfgs");
> ```
> using the [embuild](https://crates.io/crates/embuild) crate.

The list of available *cfg*s:
- `esp_idf_comp_{component}_enabled` for each [component](#espidfcomponents-espidfcomponents-native-builder-only)
- `esp_idf_version="{major}.{minor}"`
- `esp_idf_version_full="{major}.{minor}.{patch}"`
- `esp_idf_version_major="{major}"`
- `esp_idf_version_minor="{minor}"`
- `esp_idf_version_patch="{patch}"`
- `esp_idf_{sdkconfig_option}`

Each [sdkconfig
setting](https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/kconfig.html#configuration-options-reference)
where `{sdkconfig_option}` corresponds to the option set in the sdkconfig **lowercased**
and **without** the `CONFIG_` prefix. Only options set to `y` will get a *cfg*.

- `{mcu}`

Corresponds to the [mcu](#mcu-mcu) for which the esp-idf is compiled for.

## More info

If you are interested in how it all works under the hood, check the [build.rs](build/build.rs)
Expand Down
19 changes: 10 additions & 9 deletions build/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ pub struct BuildConfig {
esp_idf_sdkconfig: Option<PathBuf>,

/// One or more paths to sdkconfig.defaults files used by the esp-idf.
#[serde(deserialize_with = "parse::sdkconfig_defaults")]
#[serde(deserialize_with = "parse::list")]
esp_idf_sdkconfig_defaults: Option<Vec<PathBuf>>,

/// The MCU (esp32, esp32s2, esp32s3, esp32c3, ...) to compile for if unset will be
Expand Down Expand Up @@ -173,21 +173,22 @@ where
}
}

mod parse {
use std::path::PathBuf;

pub mod parse {
use serde::{Deserialize, Deserializer};

use super::utils::ValueOrVec;

pub fn sdkconfig_defaults<'d, D: Deserializer<'d>>(
de: D,
) -> Result<Option<Vec<PathBuf>>, D::Error> {
Option::<ValueOrVec<String, PathBuf>>::deserialize(de).map(|val| match val {
/// Parse a string into a `;`-separated list of `T`s or parse a list of `T`s directly.
pub fn list<'d, T, D>(de: D) -> Result<Option<Vec<T>>, D::Error>
where
D: Deserializer<'d>,
T: for<'s> From<&'s str> + Deserialize<'d>,
{
Option::<ValueOrVec<String, T>>::deserialize(de).map(|val| match val {
Some(ValueOrVec::Val(s)) => Some(
s.split(';')
.filter(|s| !s.is_empty())
.map(PathBuf::from)
.map(Into::into)
.collect(),
),
Some(ValueOrVec::Vec(v)) => Some(v),
Expand Down
7 changes: 7 additions & 0 deletions build/native/cargo_driver.rs
Original file line number Diff line number Diff line change
Expand Up @@ -317,6 +317,11 @@ pub fn build() -> Result<EspIdfBuildOutput> {
cmake_config.env("IDF_TOOLS_PATH", install_dir);
}

// specify the components that should be built
if let Some(components) = &config.native.esp_idf_components {
cmake_config.env("COMPONENTS", components.join(";"));
}

// Build the esp-idf.
cmake_config.build();

Expand Down Expand Up @@ -373,6 +378,8 @@ pub fn build() -> Result<EspIdfBuildOutput> {
})
.collect::<Vec<_>>();

eprintln!("Built components: {}", components.join(", "));

let sdkconfig_json = path_buf![&cmake_build_dir, "config", "sdkconfig.json"];
let build_output = EspIdfBuildOutput {
cincl_args: build::CInclArgs::try_from(&target.compile_groups[0])?,
Expand Down
14 changes: 14 additions & 0 deletions build/native/cargo_driver/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -48,8 +48,19 @@ pub struct NativeConfig {
///
/// Can be specified in the root crate's `package.metadata.esp-idf-sys` and all direct
/// dependencies'.
///
/// This option is not available as an environment variable.
#[serde(alias = "extra-components")]
pub extra_components: Vec<ExtraComponent>,

/// A list of esp-idf components (names) that should be built. This list is used to
/// trim the esp-idf build. Any component that is a dependency of a component in this
/// list will also automatically be built.
///
/// If this option is not specified, all components will be built. Note though that
/// some components must be explicitly enabled in the sdkconfig.
#[serde(default, deserialize_with = "parse::list")]
pub esp_idf_components: Option<Vec<String>>,
}

impl NativeConfig {
Expand Down Expand Up @@ -181,13 +192,15 @@ impl NativeConfig {
esp_idf_cmake_generator,
idf_path,
extra_components,
esp_idf_components,
},
} = EspIdfSys::deserialize(&root.metadata)?;

set_when_none(&mut self.esp_idf_version, esp_idf_version);
set_when_none(&mut self.esp_idf_repository, esp_idf_repository);
set_when_none(&mut self.esp_idf_cmake_generator, esp_idf_cmake_generator);
set_when_none(&mut self.idf_path, idf_path);
set_when_none(&mut self.esp_idf_components, esp_idf_components);

fn make_processor(
package: &Package,
Expand Down Expand Up @@ -317,6 +330,7 @@ mod parse {
use strum::IntoEnumIterator;

use super::*;
pub use crate::config::parse::*;
use crate::config::utils::ValueOrVec;

/// Parse a cmake generator, either `default` or one of [`cmake::Generator`].
Expand Down
7 changes: 6 additions & 1 deletion resources/cmake_project/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,12 @@ foreach(component_dir IN ITEMS $ENV{EXTRA_COMPONENT_DIRS})
idf_build_component(${component_dir})
endforeach()

idf_build_process($ENV{IDF_TARGET} SDKCONFIG $ENV{SDKCONFIG} SDKCONFIG_DEFAULTS $ENV{SDKCONFIG_DEFAULTS})
if(DEFINED ENV{COMPONENTS})
idf_build_process($ENV{IDF_TARGET} SDKCONFIG $ENV{SDKCONFIG} SDKCONFIG_DEFAULTS $ENV{SDKCONFIG_DEFAULTS} COMPONENTS $ENV{COMPONENTS})
else()
idf_build_process($ENV{IDF_TARGET} SDKCONFIG $ENV{SDKCONFIG} SDKCONFIG_DEFAULTS $ENV{SDKCONFIG_DEFAULTS})
endif()

idf_build_get_property(aliases BUILD_COMPONENT_ALIASES)

add_executable(libespidf.elf main.c)
Expand Down