Posted by cbrake 5 days ago
Like the article points out, the software stacks people use for embedded devices are the same as people use non-embedded use; Rust, Go, NodeJS, and sure still C++. The only real difference with embedded devices is non-OS components like the bootloader (u-boot, EDK2) and customizing the device tree. (And of course firmware flashing). Fundamentally those are all just packages that I can describe in Nix. I don't need a separate tool just because the board is small.
IMO the embedded space, especially in the US, is already pretty Niche. Most companies either just ship the vendors BSP example (Ubuntu/Debian/Yocto) and pay very little attention to the detail or re-useablity. Once you vendor declares the BSP EOL you are stuck unless you re-implement it yourself.
Using Nix (or Guix) has the massive advantage of a large and active community that isn't fractured like the Yoctoo/Buildroot community. (By fractured I mean there may by many, many people using those tools at $DAYJOB but due to vendor BSP customization they likely share much less with the upstream community maintained sources).
Because embedded usually means dealing with complete functionality provided by the soc, bare minimum for the init system to get quick boot times.
If you just want to boot the CPU and run some IoT app, device tree + kernel + uboot should be enough.
But for anything with a GUI, You'd probably need the vendor provided binary blobs for GPU accelerated UI, proper gstreamer packages with all the patches to make it work with the binary blobs... properly packaged Qt etc ... Not to mention read only rootfs and A/B partitions for upgrades to deal with power loss headaches.
I haven't used Nix for any of these but most of these things are available ready to use with the vendor provided buildroot or yocto setups. And it would've been just more work to get this all working with yet another build system/package manager
However, reading through the docs, the author clearly knows about Nix/Nixpks and has factored some of its properties into the design: https://docs.yoebuild.org/nix.html
I’d still probably just implement this in Nix/Nixpkgs so I could leverage the rest of the ecosystem but that’s not trivial at all.
I wouldn’t even say Nix as a language is better than Starlark. The author is certainly well informed in the choices made.
[1] https://search.nixos.org/options?channel=26.05&query=boot.bi...
Because historically, and I would argue should still, refer to very limited hardware, in terms of memory, processing power, and energy.
Adding new targets is deceptively easy, just copy an existing template and substitute your values.
https://codeberg.org/guix/guix/src/branch/master/gnu/system/...
https://codeberg.org/guix/guix/src/branch/master/gnu/bootloa...
$ my-arm-system = nixpkgs.lib.nixosSystem { system = "aarch64-linux"; modules = [ ./configuration.nix ]; };
$ :b arm-system.config.system.build.images.iso
nixos-rebuild build-vm (--flake DIR#HOSTNAME / configuration.nix)
./result/bin/SCRIPT
to build and launch a nixos vm with qemu.
build-vm is the same as building the configs “config.system.build.vm” attribute.
nixos-rebuild is fine for getting started but you end up needing to use the attribute path to do anything more complicated like CI caching or inspecting the drvPath.
Architecture emulation with qemu-userspace is 5-10x slower than running a native build.
The only 'proper' no-friction cross-compilation system I know is Debian/Ubuntu, because it allows to install foreign architecture libraries into your native system, and has all the wrappers for cross-compilation in dpkg-buildpackage.
This way you don't have to maintain cross-compiler or toolchain/sysroot, you just install whatever dependencies you need with the regular `apt install` of another architecture.
I'd like to have more friendly tooling around that instead of a slow compilation of architecture emulation.
- You're building on the same native system as GNU and Linux packages, you install them globally in the same places that servers and desktops use
- Your C, C++ compiler and entire toolchain and other binary utilities with the kernel is a one single unit that you can only change one part at a time
- You use the same up to date headers with glibc, gcc and Linux kernel
- You're building software in the same universe of all the other packages, especially gcc libraries (libgcc_s, libstdc++), glibc (especially bad since ld-linux.so is part of it)
- The build system only uses standard paths
The reason Yocto is so complicated is that developing in a Linux environment actually sucks when you're not writing web-oriented or server / VM software. Yocto fixes it. It introduces a good set of abstractions that work around terrible design decisions that were made in overall Linux ecosystem. There are a lot because the OS design is fundamentally broken, especially with C-based toolchains which is 99.999% of the ecosystem. Current C toolchains including MSVC strongly ties OS with the C's internal types and bad decisions of 70s.
As always all articles whose title asks a question are answered with NO, 99% time. By taking away the cross-compiling abilities and the workarounds doesn't fix the brokenness of Linux and overall FOSS ecosystem.
If you're looking for how a better embedded environment looks like, look at Rust toolchains. For Linux take a look at musl-libc based ones (you 100% need a systemd distro to get away from nss complexities that musl introduces). Or even better take a look at relibc. There are barely any assumptions about the target filesystem and tooling in Rust toolchains, unlike C/C++/Make toolchains. There is redox OS but it is still in slow development and they stuck with Make, which I think was a bad decision. Android uses its own build tooling but cannot run away from C/C++ tooling unless Google revives Fuschia.
Yes, skipping builds from source can be faster, and, I don't know that you need to throw away yocto to do that https://rootcommit.com/pub/conferences/2024/elce/yocto-binar...
From the statement about "Caches builds so no piece of software is built twice." I'm guessing that the author has not enabled sstate caching https://docs.yoctoproject.org/dev/overview-manual/concepts.h...
I'm a little doubtful on the post's assertion that yocto is only needed for "deeply embedded regulated products." For products that have to follow the https://en.wikipedia.org/wiki/Cyber_Resilience_Act coming up, they will almost certainly need:
* SBOM management https://docs.yoctoproject.org/next/dev-manual/sbom.html
* CVE (or its successor) tracking https://docs.yoctoproject.org/dev/security-manual/vulnerabil...
* License management https://docs.yoctoproject.org/next/dev-manual/licenses.html
And I'll echo what some other folks have said here: if you don't need features like that, bundle your custom bootloaders and kernel with a binary distribution like Debian and call it a day.
Reversing and reimplementing the closed bits are what I want my Buildroot pipelines to do.
That’s my use case though; targeting COTS devices with (mainline) Linux support that don’t do all they could.
I have done it the ugly way (in production) and I want elegance in my old age.