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

"Production-Ready" Template Project #770

Open
phip1611 opened this issue Apr 24, 2023 · 5 comments
Open

"Production-Ready" Template Project #770

phip1611 opened this issue Apr 24, 2023 · 5 comments

Comments

@phip1611
Copy link
Contributor

phip1611 commented Apr 24, 2023

Compiling a minimal hello-word UEFI application is easy. Setting up a productive development environment with a "run in QEMU" command and unit-tests is hard, however. Especially the last part is very challenging, as one has to mix the UEFI target with a standard target for test execution - rust runtime problems (panic handlers, global allocators), different compilation targets... I worked on something (https://github.com/phip1611/rust-uefi-template) that supports the simultaneous building and unit testing inside the same Cargo project.

(Either I'm totally dumb or this is indeed very hard).

Can you (@nicholasbishop, @GabrielMajeri but also the community) please tell me what you think? And if yes, could or should we integrate the template into the rust-osdev namespace? The README and the code comments should describe the relevant problems that I'm solving there..

Summary of the Problems

  • "cargo test" needs the host platform's target, such as x86_64-unknown-linux-gnu, as libtest is pre-compiled and working on this target. Unit tests can't be compiled and executed for the x86_64-unknown-uefi target - that would not work.
  • "cargo build" needs the x86_64-unknown-uefi target
  • however, a application may use a custom #[panic_handler]. This won't work for unit tests in a cargo test setup
  • the same as above applies to the #[global_allocator]
  • a cargo workspace also shouldn't help here / doesn't change the root problem

As a consequence, it takes a few quirks to cope with that - and they are not very intuitive, I think.

https://github.com/phip1611/rust-uefi-template

PS: I just found that there is an open issue in cargo because of this problem: rust-lang/cargo#6784

@phip1611 phip1611 changed the title Production-Ready Template Project "Production-Ready" Template Project Apr 24, 2023
@phip1611
Copy link
Contributor Author

phip1611 commented Apr 24, 2023

@blitz If you have time, I'd also love to hear your thoughts - I think you might also have experience with that in the lanzaboote project.

@phip1611
Copy link
Contributor Author

phip1611 commented Apr 24, 2023

For example, the bootloader crate also refrains from writing unit tests. Probably for the reason described above. https://github.com/rust-osdev/bootloader/blob/main/uefi/src/main.rs

@blitz
Copy link
Contributor

blitz commented Apr 24, 2023

We can chat about it this week in person. :)

@phil-opp
Copy link
Member

My opinion is that unit tests should be self-contained and independent of the target system. So for target-specific crates such as UEFI applications, I would move all the platform-independent stuff to a separate crate, which can then be tested as usual without requiring any conditional compilation.

For target-specific integration tests, you typically want to test them on an (emulated) target system directly. Rust doesn't really provide much support for this yet, unfortunately. I think the best solution is to build a custom test framework, e.g. using a crate such as inventory to collect a set of test functions.

I'm not a fan of using cfg-gates for testing since this makes it less obvious what you're really testing. For example, I think your example test at https://github.com/phip1611/rust-uefi-template/blob/2fde9ebfd9f1d43c5a90f7d0054efa050d1d55a8/src/lib.rs#L26-L29 effectively only verifies that there is a global allocator in Rust's standard library.

@nicholasbishop
Copy link
Contributor

(Either I'm totally dumb or this is indeed very hard).

I think you are correct that it's hard. I think there are two directions to attack the problem from:

  1. Improve the template project and documentation to help guide the user through the complexity
  2. Improve the ecosystem to make things easier

And these are not mutually exclusive of course.

I think the idea of a standalone template UEFI app repo is good, and better than our current template subdirectory in uefi-rs. Keeping it in a separate repo makes it easier to include things like basic github CI and potentially a simple workspace. I agree with phil-opp that using separate packages is good for testing; keep the testable-on-the-host stuff in one package and the depends-on-uefi stuff in another package. And I'm a proponent of the xtask pattern rather than a Makefile; Makefiles and shell scripts get hard to read very quickly IMO, and easy to make non-obvious mistakes when writing too.

From the direction of improving the ecosystem, perhaps we should look at librarification of some of the code in uefi-rs used to run QEMU. Pretty much every UEFI project has some local code for finding OVMF (often just a hardcoded path, but we can do better), figuring out the args for QEMU, etc. uefi-run exists, but it's fairly restrictive even in the library. A new library with flexible code for launching QEMU could be very helpful for getting a new project up and running.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants