-
Notifications
You must be signed in to change notification settings - Fork 53
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
Support building for native targets #168
Comments
The problem with what you are suggesting is that your code base will be littered with I wonder if you are not better off by just isolating your cross platform code in a separate library crate which is then consumed by the esp32-specific binary crate? That's what I practice e.g. here. Otherwise I don't mind I think putting all ESP dependencies behind a |
Some small corrections / clarifications in your assessment of the status quo wihich might be helpful to you when driving the PR:
Nothing special in the version of Rust, aside from it supporting the
The special linker (
The problem when coding for an MCU is that you need a lot of additional stuff beyond what is available in the Rust standard libraries, and which happens to be MCU specific. Sure, there is ...which does not mean we cannot / should not try to do the
Indeed.
Big deal. You can continue depending on
Easiest is to just look at the value of |
Admittedly, part of the issue was due to me misreading things. I thought the
I think that most of what I'd need can be made common between desktop and embedded. Perhaps some explanation is in order. I'm working on a Rust port of the Blackmagic project. Previously I used In terms of MCU-specific features, I can think of:
The majority of the project will be the same for both -- namely the web terminal interface, handling the debugging requests, the serial interface, and all of the UI stuff. Websockets and the web server would also be common, unless I decide to reuse those present in Having said that, your approach seems very viable, and embodies the "Alternatives" approach.
Unfortunately that doesn't work because you can only check for environment variables through normal calls in
The workaround is to modify [build-dependencies]
embuild = { version = "0.31.3", features = ["espidf"] } |
Just two small nits:
Scratch that. What you suggest is even better, I admit! :)
That's exactly what I was trying to suggest: nothing wrong with the fact that (a) |
Would it make sense to enable the My modified main looks like this: fn main() {
// It is necessary to call this function once. Otherwise some patches to the runtime
// implemented by esp-idf-sys might not link properly. See https://github.com/esp-rs/esp-idf-template/issues/71
#[cfg(target_os = "espidf")]
{
esp_idf_svc::sys::link_patches();
// Bind the log crate to the ESP Logging facilities
esp_idf_svc::log::EspLogger::initialize_default();
}
#[cfg(not(target_os = "espidf"))]
simple_logger::SimpleLogger::new().env().init().unwrap();
log::info!("Hello, world!");
} Which is nice in that it works on both targets. You're right in that a lot of stuff will go into that guarded section, but that seems no more different from what e.g. libstd does with conditional |
Yes, why not? The @N3xed did it as a feature (that one and a bunch of others) only to get faster builds for the cases where
Yeaaah... except from the point of view of the libstd users, it is a one single API. They don't see the But then, I agree - this is not black and white. Using trait-based abstractions ( |
Closing this as the discussion reached an end point, feel free to reopen if you think there is still stuff related to this template to discuss. |
Also and BTW the use case described here (support building for native targets) IS supported after all. You just need to cfg-out all esp-idf specific APIs in your crate when building for the host. And touch the Mentioning because one of my private binary crates has precisely such a hybrid configuration now. Which allows for very easy host-based running for manual testing. |
Great! Happy to hear it's now documented. Thanks for the help. |
No it is not documented, sorry if I confused you. But is completely possible, by just following the steps described in this thread. I.e.
That's all there is to it! |
The issue was originally created as a suggestion for incorporating it into the default template. I'm guessing it's been decided that Espressif doesn't want to do this, which is why this issue was closed. In which case this thread will serve as documentation to anyone in the future who wants to do that after they have the template project cloned locally. |
Espressif does not have any strong feelings on that as the So it is between you, me and @Vollbrecht to decide. We certainly can incorporate that in the template (perhaps, under the "advanced" settings) though I wonder if even this is necessary? With all due respect, this use case is a very niche one:
But if you still want to do it... just open a PR? As long as this is not what is generated by default (i.e., it is triggered only when the user selects "advanced configuration", and then selects an option which says something like "Support cross-compilation for the host target") y/n (with default = n), I think we can merge that. |
Got it. I appreciate that it's a niche usecase, given how most users will use this repo. In that case, I think this Issue (and the helpful discussion that follows!) serves as enough documentation for people to find a workable solution. |
Fwiw, here's an excerpt from my (private / commercial) binary crate that has such a hybrid setup: Cargo.toml...
[target.'cfg(target_os = "espidf")'.dependencies]
esp-idf-svc = { version = "0.48", features = ["embassy-sync", "embassy-time-driver", "critical-section"] }
esp-idf-matter = { git = "https://github.com/ivmarkov/esp-idf-matter", branch = "mdns" }
[target.'cfg(not(target_os = "espidf"))'.dependencies]
futures-lite = "1"
libc = "0.2"
rs-matter = { version = "0.1", default-features = false, features = ["os"] }
rs-matter-stack = { git = "https://github.com/ivmarkov/rs-matter-stack", branch = "mdns", features = ["os", "nix", "edge-mdns"] }
materialize = { git = "https://github.com/ivmarkov/materialize", features = ["std", "edge-mqtt"] }
async-compat = "0.2"
env_logger = "0.11"
[build-dependencies]
embuild = { version = "0.31.3", features = ["espidf"] } build.rsfn main() {
let os = std::env::var_os("CARGO_CFG_TARGET_OS");
if matches!(
os.as_deref().and_then(std::ffi::OsStr::to_str),
Some("espidf")
) {
embuild::espidf::sysenv::output();
}
} main.rs...
mod led;
mod matter;
mod mdns;
mod mqtt;
#[cfg(target_os = "espidf")]
#[path = "runesp.rs"]
mod run;
#[cfg(not(target_os = "espidf"))]
#[path = "runhost.rs"]
mod run;
mod service;
mod spiram;
mod stack;
mod web;
fn main() -> Result<(), StackError> {
run::initialize();
info!("Starting...");
// Run in a higher-prio thread to avoid issues with `async-io` getting
// confused by the low priority of the ESP IDF main task
// Also allocate a very large stack (for now) as `rs-matter` futures do occupy quite some space
let thread = std::thread::Builder::new()
.stack_size(40 * 1024)
.spawn(|| {
run_wrapper()
})
.unwrap();
thread.join().unwrap()
}
#[inline(never)]
#[cold]
fn run_wrapper() -> Result<(), StackError> {
let result = run::run();
if let Err(e) = &result {
error!("LB-Mini aborted execution with error: {:?}", e);
} else {
info!("LB-Mini finished execution successfully");
}
result
} .cargo/config.tomlHere I'm cheating a bit as I currently have two different [build]
# If the user wants to build for e.g. the host, she needs to type `cargo build ---target x86_64-unnown-linux-gnu`
# ... or just make it the default target below
# ... or just comment ALL targets below; then cargo would pick up the host one
#target = "x86_64-unknown-linux-gnu"
target = "xtensa-esp32-espidf"
#target = "riscv32imc-esp-espidf"
[target.xtensa-esp32-espidf]
linker = "ldproxy"
rustflags = ["--cfg", "espidf_time64"]
[target.xtensa-esp32s2-espidf]
linker = "ldproxy"
rustflags = ["--cfg", "espidf_time64"]
[target.xtensa-esp32s3-espidf]
linker = "ldproxy"
rustflags = ["--cfg", "espidf_time64"]
[target.riscv32imc-esp-espidf]
linker = "ldproxy"
rustflags = ["--cfg", "espidf_time64"]
[target.riscv32imac-esp-espidf]
linker = "ldproxy"
rustflags = ["--cfg", "espidf_time64"]
[net]
git-fetch-with-cli = true
[env]
ESP_IDF_SDKCONFIG_DEFAULTS = "./sdkconfig.defaults"
ESP_IDF_VERSION = "v5.1.2"
# TODO: This is a bit annoying, as we cannot have it per-target. What that means is that STD would be re-built
# when building against the host target too.
# Maybe not such a big deal?
[unstable]
build-std = ["std", "panic_abort"] |
Motivations
The default project builds for a special version of Rust with a set of special packages to support Espressif targets. These packages mostly wrap
esp-idf
, but also include features such as linking using a special linker.While this supports targeting embedded projects well, it does not support running software on the user's host machine. For example, a simple "Hello, world!" program fails to compile when built for the user's target system.
Projects should be cross-compatible, and should work both with Espressif's custom toolchains as well as standard Rust toolchains. Without supporting both native- and cross-builds, Rust projects that target Espressif chips will never be compatible with anything else, making them less attractive.
Solution
Patches can be added to move the dependency on
esp-idf-svc
to only targets that aretarget_os = "espidf"
. Additionally, the main loop can be modified to gate patch installation behind#[cfg(target_os = "espidf")]
.A more complex issue is that
embuild
needs to explicitly have theespidf
feature enabled. Alternately, thesysenv
path could be exposed even on non-ESP targets, because code cannot be turned off inbuild.rs
with#[cfg()]
macros.Alternatives
An alternative approach would be to state that
esp-rs
projects will never be cross-compatible, and encourage users to build their projects as libraries. Rather than having one frontend that supports all targets,esp-rs
projects will only ever target a single chip, and multiple chips require multiple projects. Furthermore, "native" and standard Rust builds require other frontends.Additional context
It may be desirable to modify
embuild
as described in "Solution" in order to avoid needing to manually set feature flags insidebuild-dependencies
.The text was updated successfully, but these errors were encountered: