You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Short version: I am writing an application for a slightly unusual embedded platform, which entails using a platform json and no-std. It worked great until I decided I wanted to write a #[test] function for one of my files. At this point I got a series of very unhelpful error messages, ending with learning that our custom platform setup excluded use of cargo test entirely.
I got a lot of help from a nice frog person on the Rust Discord who explained why this was happening; when I asked if I should file an issue they said
I think all of this is covered by the cargo build-std working group issues. But you can file a usecase report if you want
So here is my filing of "a use case I would consider reasonable but which the current combination of planned Cargo features does not allow", for the consideration of the people working on build-std. This may be too much detail but I wasn't sure what to include.
Which is a sample code/test app for a bespoke RISC-V based platform a friend is making. The platform is in one sense exotic (it is a softcore CPU for a FPGA-based video game system) and in another sense very ordinary (it is a standard RISC-V configuration, based on litex which is a common toolkit for building SoCs). I believe the problems below would apply to any program targeting a 32-bit RISC-V CPU with an FPU.
The project is set up in this way: Because our CPU arch is "tier 3" for Rust (there are other RISC-Vs which are tier 2, such as riscv32imac and riscv64imafdc [aka riscv64gc], but riscv32imafdc— in other words, riscv32imac with a FPU— is tier 3), we must describe the arch to the Rust compiler using a json file. Because we are running in an embedded configuration where there is no OS and booting the program entails just jumping to an address, we run with no_main and riscv_rt::entry on "main", and we objcopy the binary out of the ELF for raw loading as a second step after build (we have a minimal Makefile for executing the two cargo invocations). To tell the compiler about our platform json, as well as our "region" linker scripts, we use .cargo/config.toml. We use core (but not std), and in order to use core on a tier 3 arch we rely on build-std, which requires running in nightly. Everything about this configuration works, and we are not aware of a simpler way to do it (except maybe "possible solution 3" below).
My test app has a src/irect2.rs containing a miniscule set of math helpers. I wound up writing brief unit tests for these math helpers, which live inline in irect2.rs as the #[cfg(test)] mod tests { use super::*; #[test] fn… construction that the docs recommend. When I tried running this with cargo +nightly test I got an error "can't find crate" on test. (In my view, this error is "correct" and probably adequately clear, because test is in std and our .cargo/config.toml is configured only for build-std = ["core", "compiler_builtins", "alloc"].) At this point I started getting help from madfrog on Discord (can link conversation if helpful), who explained this error and showed me the way around this. This is where things get less "correct".
What I next tried was cargo +nightly test --target x86_64-unknown-linux-gnu. What I understand this to mean is that instead of building the tests for our custom riscv platform (which, in this case, I didn't want anyway— once built I'd have no way to execute them) builds them to run on the laptop where I'm executing the commands. This gave hundreds of errors, all of the form:
error[E0152]: duplicate lang item in crate `core` (which `std` depends on): `sized`.
|
= note: the lang item is first defined in crate `core` (which `minibreak` depends on)
= note: first definition in `core` loaded from /home/mcc/work/r/v/riscv-pocket-app/target/x86_64-unknown-linux-gnu/debug/deps/libcore-37638ee60b32e126.rlib
= note: second definition in `core` loaded from /home/mcc/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/x86_64-unknown-linux-gnu/lib/libcore-ec02dd343723da85.rlib
I got one of these for what seems like every symbol in core, including things like Err, fn_once and String. Frog believed that this was my use of no_main/custom entry messing up cargo build-std. As a last thing to try Frog suggested cargo +nightly test --target x86_64-unknown-linux-gnu -Zpanic-abort-tests, which did not make any difference.
I do not know what my "expected behavior" here is, exactly. I am not blocked on anything because running my unit tests through cargo is not a high priority (and I have stopgap options such as pasting the tests into the main() body). But I think my expectation is that "I want to cross-compile to this embedded, no-std platform, but have cargo test run normally on the machine that does the builds" is a reasonable use case, and there should be some way to communicate it to Cargo.
Possible Solution(s)
I was given three possible solutions to my problem on the Discord. Two of them would have actually worked, but I still suspect there is a gap in Cargo functionality here.
Other people have solved this by manually importing std for just the test submodule/ part of the file containing #[test]. I don't know if this would have worked but I am resisting it because my tests should actually not have std in scope. Part of what I am testing is that my code works when run against core only (eg I didn't accidentally rely on something from std).
My code would have worked if instead of irect2.rs being a single file I made it a lib subcrate. As a lib crate, it could have had its own platform targets and the unit tests could have been run independently of the main program. This is a very clean solution but in this case (not counting the tests, irect2.rs is under 100 lines) the overhead is enough I would probably just not bother running the unit tests if this were the only way.
The Discord consensus was everything I specified in my .cargo/config.toml probably could have been specified as command-line flags instead. So I could have thrown out the config.toml, then had one Makefile target that builds for the unusual riscv arch and one Makefile target that builds normally on the build pc. This is a solution I will probably explore at some point, because it would provide other advantages like allowing a single app to build for several platforms (say, our exotic litex platform from one makefile target, and pc or wasm from another). However, it would be nice to be able to solve as much as the problem within Cargo as possible rather than relying on external build tools (especially since Makefile is not cross-platform; the more complexity we put in our Makefile, the harder it will be to someday support building on Windows).
It may be that what I am trying to do (specify a complex custom cross-compilation platform, while also expecting manually-specified amd64 linux target to continue working) is just too weird. If this is the case, I would ask whether the error messages could be improved (getting a duplicate error for every symbol in core is confusing to diagnose).
In my testing, cargo test with this reproduces the same problem I see with my litex/riscv example, but this example is much simpler and targets a more mainstream embedded target (AVR)
Problem
Short version: I am writing an application for a slightly unusual embedded platform, which entails using a platform json and no-std. It worked great until I decided I wanted to write a
#[test]
function for one of my files. At this point I got a series of very unhelpful error messages, ending with learning that our custom platform setup excluded use ofcargo test
entirely.I got a lot of help from a nice frog person on the Rust Discord who explained why this was happening; when I asked if I should file an issue they said
So here is my filing of "a use case I would consider reasonable but which the current combination of planned Cargo features does not allow", for the consideration of the people working on build-std. This may be too much detail but I wasn't sure what to include.
Steps
I have a project: https://github.com/mcclure/pocket-riscv-rs/tree/minibreak
Which is a sample code/test app for a bespoke RISC-V based platform a friend is making. The platform is in one sense exotic (it is a softcore CPU for a FPGA-based video game system) and in another sense very ordinary (it is a standard RISC-V configuration, based on litex which is a common toolkit for building SoCs). I believe the problems below would apply to any program targeting a 32-bit RISC-V CPU with an FPU.
The project is set up in this way: Because our CPU arch is "tier 3" for Rust (there are other RISC-Vs which are tier 2, such as riscv32imac and riscv64imafdc [aka riscv64gc], but riscv32imafdc— in other words, riscv32imac with a FPU— is tier 3), we must describe the arch to the Rust compiler using a json file. Because we are running in an embedded configuration where there is no OS and booting the program entails just jumping to an address, we run with
no_main
andriscv_rt::entry
on "main", and we objcopy the binary out of the ELF for raw loading as a second step after build (we have a minimalMakefile
for executing the two cargo invocations). To tell the compiler about our platform json, as well as our "region" linker scripts, we use.cargo/config.toml
. We use core (but not std), and in order to use core on a tier 3 arch we rely on build-std, which requires running in nightly. Everything about this configuration works, and we are not aware of a simpler way to do it (except maybe "possible solution 3" below).My test app has a
src/irect2.rs
containing a miniscule set of math helpers. I wound up writing brief unit tests for these math helpers, which live inline in irect2.rs as the#[cfg(test)] mod tests { use super::*; #[test] fn…
construction that the docs recommend. When I tried running this withcargo +nightly test
I got an error "can't find crate" on test. (In my view, this error is "correct" and probably adequately clear, because test is instd
and our.cargo/config.toml
is configured only forbuild-std = ["core", "compiler_builtins", "alloc"]
.) At this point I started getting help from madfrog on Discord (can link conversation if helpful), who explained this error and showed me the way around this. This is where things get less "correct".What I next tried was
cargo +nightly test --target x86_64-unknown-linux-gnu
. What I understand this to mean is that instead of building the tests for our custom riscv platform (which, in this case, I didn't want anyway— once built I'd have no way to execute them) builds them to run on the laptop where I'm executing the commands. This gave hundreds of errors, all of the form:I got one of these for what seems like every symbol in core, including things like
Err
,fn_once
andString
. Frog believed that this was my use of no_main/custom entry messing up cargo build-std. As a last thing to try Frog suggestedcargo +nightly test --target x86_64-unknown-linux-gnu -Zpanic-abort-tests
, which did not make any difference.I do not know what my "expected behavior" here is, exactly. I am not blocked on anything because running my unit tests through cargo is not a high priority (and I have stopgap options such as pasting the tests into the
main()
body). But I think my expectation is that "I want to cross-compile to this embedded, no-std platform, but havecargo test
run normally on the machine that does the builds" is a reasonable use case, and there should be some way to communicate it to Cargo.Possible Solution(s)
I was given three possible solutions to my problem on the Discord. Two of them would have actually worked, but I still suspect there is a gap in Cargo functionality here.
.cargo/config.toml
probably could have been specified as command-line flags instead. So I could have thrown out the config.toml, then had one Makefile target that builds for the unusual riscv arch and one Makefile target that builds normally on the build pc. This is a solution I will probably explore at some point, because it would provide other advantages like allowing a single app to build for several platforms (say, our exotic litex platform from one makefile target, and pc or wasm from another). However, it would be nice to be able to solve as much as the problem within Cargo as possible rather than relying on external build tools (especially since Makefile is not cross-platform; the more complexity we put in our Makefile, the harder it will be to someday support building on Windows).It may be that what I am trying to do (specify a complex custom cross-compilation platform, while also expecting manually-specified amd64 linux target to continue working) is just too weird. If this is the case, I would ask whether the error messages could be improved (getting a duplicate error for every symbol in
core
is confusing to diagnose).Notes
No response
Version
The text was updated successfully, but these errors were encountered: