Skip to content

Commit

Permalink
build: support user-provided esp-idf and activated environment (#42)
Browse files Browse the repository at this point in the history
Allows the user to specify their own esp-idf and makes it possible to use an activated esp-idf environment. For the pio builder, allows re-using an already available `platformio` from $PATH.

For the native builder:
- If an activated esp-idf is available and $ESP_IDF_TOOLS_INSTALL_DIR == "fromenv" or unset, use it.
- If $IDF_PATH is valid, use it as a user-provided esp-idf repository (but installer manages tools).
- Otherwise, the installer manages everything and respects $ESP_IDF_REPOSITORY and $ESP_IDF_VERSION.
For the pio builder:
- If the `platformio` executable is in $PATH and $ESP_IDF_TOOLS_INSTALL_DIR == "fromenv" or unset, use it.
- Otherwise, the installer manages platformio as before.

* Preconfigured env and custom esp-idf support
* Fix cargo fmt
* Improve logging
* Implement pio, update README
* Fix cargo fmt
* Upgrade embuild and strum
* Fix typos in README

Co-authored-by: N3xed <[email protected]>
  • Loading branch information
ivmarkov and N3xed authored Apr 26, 2022
1 parent 64b7fc9 commit f41277b
Show file tree
Hide file tree
Showing 5 changed files with 304 additions and 117 deletions.
4 changes: 2 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,8 @@ pio = ["embuild/pio"]
paste = "1"

[build-dependencies]
embuild = { version = "0.28.4", features = ["glob"] }
embuild = { version = "0.29.0", features = ["glob"] }
anyhow = "1"
strum = { version = "0.23", features = ["derive"], optional = true }
strum = { version = "0.24", features = ["derive"], optional = true }
regex = "1.5"
bindgen = "0.59"
37 changes: 25 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -106,27 +106,40 @@ The following environment variables are used by the build script:

- `ESP_IDF_TOOLS_INSTALL_DIR`:

The location where the ESP-IDF framework tooling is assumed to be/will be installed.
The framework tooling is either PlatformIO (when the `pio` builder is used), or the ESP-IDF native toolset (when the `native` builder is used).
The location where the ESP-IDF framework tooling is assumed to be/will be installed. The
framework tooling is either PlatformIO (when the `pio` builder is used), or the ESP-IDF
native toolset (when the `native` builder is used).

This variable can take one of the following values:
This variable can take one of the following values:
- `workspace` (default) - the tooling will be installed/used in
`<crate-workspace-dir>/.embuild/platformio` for `pio`, and `<crate-workspace-dir>/.embuild/espressif` for the `native` builder;
- `out` - the tooling will be installed/used inside the crate's build output directory, and will be deleted when `cargo clean` is invoked;
- `global` - the tooling will be installed/used in its standard directory (`~/.platformio` for PlatformIO, and `~./espressif` for the native ESP-IDF toolset);
- `custom:<dir>` - the tooling will be installed/used in the directory specified by `<dir>`. If this directory is a relative location, it is assumed to be
relative to the crate's workspace dir.

**ATTENTION**: Please be extra careful with the `custom:<dir>` setting when switching from `pio` to `native` and the other way around, because
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.


Note that both builders (`native` and `pio`) clone the ESP-IDF GIT repository *inside* the tooling directory as well. This restriction might be lifted soon for the `native` builder, whereas the user would be able to point the build to a custom ESP-IDF repository location.
relative to the crate's workspace dir;
- `fromenv` - use the build framework from the environment
- *native* builder: use activated esp-idf environment (see esp-idf docs
[unix](https://docs.espressif.com/projects/esp-idf/en/latest/esp32/get-started/linux-macos-setup.html#step-4-set-up-the-environment-variables)
/
[windows](https://docs.espressif.com/projects/esp-idf/en/latest/esp32/get-started/windows-setup.html#using-the-command-prompt))
- *pio* builder: use `platformio` from the environment (i.e. `$PATH`)

and error if this is not possible.

**ATTENTION**: Please be extra careful with the `custom:<dir>` setting when switching from `pio` to `native` and the other way around, because
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.


Note that both builders (`native` and `pio`) clone the ESP-IDF GIT repository *inside* the tooling directory as well. This restriction might be lifted soon for the `native` builder, whereas the user would be able to point the build to a custom ESP-IDF repository location.

- `IDF_PATH` (*native* builder only):
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` (*native* builder only):

The version used for the `esp-idf` can be one of the following:
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.
Expand Down
103 changes: 77 additions & 26 deletions build/common.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
use std::collections::HashSet;
use std::fmt::Display;
use std::iter::once;
use std::path::{Path, PathBuf};
use std::str::FromStr;
use std::{env, error, fs};

use anyhow::*;
use anyhow::{anyhow, bail, Result};
use embuild::cargo::{self, IntoWarning};
use embuild::utils::{OsStrExt, PathExt};
use embuild::{bindgen, build, kconfig};
Expand All @@ -24,9 +25,6 @@ pub const V_4_3_2_PATCHES: &[&str] = &[
"patches/pthread_destructor_fix.diff",
];

#[allow(unused)]
pub const NO_PATCHES: &[&str] = &[];

const TOOLS_WORKSPACE_INSTALL_DIR: &str = ".embuild";

const ALL_COMPONENTS: &[&str] = &[
Expand Down Expand Up @@ -207,32 +205,85 @@ pub fn list_specific_sdkconfigs(
})
}

pub fn get_install_dir(builder_name: impl AsRef<str>) -> Result<Option<PathBuf>> {
let location = match env::var(ESP_IDF_TOOLS_INSTALL_DIR_VAR) {
Err(env::VarError::NotPresent) => None,
var => Some(var?.to_lowercase()),
};

let dir = match location.as_deref() {
None | Some("workspace") => Some(
workspace_dir()?
.join(TOOLS_WORKSPACE_INSTALL_DIR)
.join(builder_name.as_ref()),
),
Some("global") => None,
Some("out") => Some(cargo::out_dir().join(builder_name.as_ref())),
Some(custom) => {
if let Some(suffix) = custom.strip_prefix("custom:") {
Some(PathBuf::from(suffix).abspath_relative_to(&workspace_dir()?))
} else {
bail!("Invalid installation directory format. Should be one of `global`, `workspace`, `out` or `custom:<dir>`");
#[derive(Clone, Debug)]
pub enum InstallDir {
Global,
Workspace(PathBuf),
Out(PathBuf),
Custom(PathBuf),
FromEnv,
}

impl InstallDir {
/// Get the install directory from the [`ESP_IDF_TOOLS_INSTALL_DIR_VAR`] env variable.
///
/// If this env variable is unset or empty uses `default_install_dir` instead.
/// On success returns `(install_dir as InstallDir, is_default as bool)`.
pub fn from_env_or(
default_install_dir: &str,
builder_name: &str,
) -> Result<(InstallDir, bool)> {
let location = env::var_os(ESP_IDF_TOOLS_INSTALL_DIR_VAR);
let (location, is_default) = match &location {
None => (default_install_dir, true),
Some(val) => {
let val = val.try_to_str()?.trim();
if val.is_empty() {
(default_install_dir, true)
} else {
(val, false)
}
}
};
let install_dir = match location.to_lowercase().as_str() {
"global" => Self::Global,
"workspace" => Self::Workspace(
workspace_dir()?
.join(TOOLS_WORKSPACE_INSTALL_DIR)
.join(builder_name),
),
"out" => Self::Out(cargo::out_dir().join(builder_name)),
"fromenv" => Self::FromEnv,
_ => Self::Custom({
if let Some(suffix) = location.strip_prefix("custom:") {
Path::new(suffix).abspath_relative_to(&workspace_dir()?)
} else {
bail!(
"Invalid installation directory format. \
Should be one of `global`, `workspace`, `out`, `fromenv` or `custom:<dir>`."
);
}
}),
};
Ok((install_dir, is_default))
}

pub fn is_from_env(&self) -> bool {
matches!(self, Self::FromEnv)
}

pub fn path(&self) -> Option<&Path> {
match self {
Self::Global | Self::FromEnv => None,
Self::Workspace(ref path) => Some(path.as_ref()),
Self::Out(ref path) => Some(path.as_ref()),
Self::Custom(ref path) => Some(path.as_ref()),
}
};
}
}

Ok(dir)
impl Display for InstallDir {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::Global => write!(f, "global"),
Self::Workspace(ref path) => write!(f, "workspace ({})", path.display()),
Self::Out(ref path) => write!(f, "out ({})", path.display()),
Self::Custom(ref path) => write!(f, "custom ({})", path.display()),
Self::FromEnv => write!(f, "fromenv"),
}
}
}

pub fn workspace_dir() -> Result<PathBuf> {
Ok(cargo::workspace_dir().ok_or_else(|| anyhow!("Cannot fetch crate's workspace dir"))?)
cargo::workspace_dir().ok_or_else(|| anyhow!("Cannot fetch crate's workspace dir"))
}
Loading

0 comments on commit f41277b

Please sign in to comment.