A little while ago, I received the good news that I had gotten two (TWO 🎉) of my talk proposals accepted by NDC Oslo this year: one on profiling performance issues and one on building Rust games for the browser 🥳 While this is great, it also means that I need to start doing some serious work, because these talks don't exist yet.
For the talk on building games with Rust, I knew immediately that I wanted to use the Bevy game engine. However, I've never used Bevy before, so I thought I'd start with the Bevy Book and its /Getting Started/ section. For most cases, the docs should be sufficient, but if you run NixOS (I do 🙋), then you might run into some cases not covered by the guide. This post details all the extra steps I had to take to get through the tutorial. At the end, I'll put the whole shell.nix
file that I ended up with as well as any other changes I had to make.
This was for Bevy 0.5 running on NixOS 21.05 on a Dell XPS 9570 using the NVIDIA graphics card. If your system is different, the steps in this post may or may not work for you. But if you try and follow the steps in this post, do let me know how it went (@ me on Twitter!)!
Oh, and before we get started: I'd like to extend a big thank you to everyone who helped me out in the Bevy discord as well as to the people behind the engine itself and the docs ❤️
Initial setup
Let's get a dev environment going with the dependencies we need. The Bevy GitHub repo has a linux setup file (with a specific section for NixOS!) that tells you what you need. As mentioned in the text, add this to a build.rs
file (in your project root):
fn main() {
if cfg!(target_os = "linux") {
println!("cargo:rustc-link-lib=vulkan");
}
}
It also gives you a Nix shell that you can use. It contains most dependencies, but as we'll see, we need to modify it slightly (or at least I had to). The version that's there is:
{ pkgs ? import <nixpkgs> { } }:
with pkgs;
mkShell {
buildInputs = [
cargo
pkgconfig udev alsaLib lutris
x11 xorg.libXcursor xorg.libXrandr xorg.libXi
vulkan-tools vulkan-headers vulkan-loader vulkan-validation-layers
];
}
Problem 1: fast compilation
The first issues I ran into were in the /Setup/ section of the guide. Specifically around fast compilation.
The guide says to copy this Cargo config file into YOUR_WORKSPACE/.cargo/config.toml
. This config file enables fast compilation by configuring the linker and specific Rust flags. The configuration for building on Linux looks like this:
[target.x86_64-unknown-linux-gnu]
linker = "/usr/bin/clang"
rustflags = ["-Clink-arg=-fuse-ld=lld", "-Zshare-generics=y"]
If you've used NixOS for a bit, you'll probably realize why this won't work: there's no /usr/bin
directory on NixOS. Also, if you look at the shell.nix
file from before, there's no clang
or lld
included.
We'll fix this by changing the linker to just clang
and by adding clang
and lld
to the shell.nix
build inputs. That means the .cargo/config.toml
file should look like this:
[target.x86_64-unknown-linux-gnu]
linker = "clang"
rustflags = ["-Clink-arg=-fuse-ld=lld", "-Zshare-generics=y"]
Problem 2: Apps
The next issue I ran into was in the next section, /Apps/. The basic "Hello World" program you get when starting a new project via Cargo built and ran just fine. However, it suddenly stopped working when I tried to import and use Bevy.
The error I got was:
target/debug/<project>: error while loading shared libraries: libudev.so.1: cannot open shared object file: No such file or directory
The solution was to modify the environment's LD_LIBRARY_PATH
by adding udev
. This took some searching to find out, but thanks to this GitHub comment and @LegendOfMiracles post in the Bevy Discord's #help channel, I got there in the end.
In addition to udev
we also need to add alsaLib
to the LD_LIBRARY_PATH
. To achieve this, add this to your shell.nix
:
shellHook = ''export LD_LIBRARY_PATH="$LD_LIBRARY_PATH:${pkgs.lib.makeLibraryPath [
pkgs.alsaLib
pkgs.udev
]}"'';
Problem 3: Plugins
The third and final problem I ran into was with the section on /Plugins/. When I added DefaultPlugins
, the program immediately panicked with this message:
Finished dev [unoptimized + debuginfo] target(s) in 0.08s Running `target/debug/bevy-tutorial` thread 'main' panicked at 'Unable to find a GPU! Make sure you have installed required drivers!', /home/thomas/.cargo/registry/src/github.com-1ecc6299db9ec823/bevy_wgpu-0.5.0/src/wgpu_renderer.rs:47:14 note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
Turns out the solution to this was to modify the LD_LIBRARY_PATH
further and add vulkan-loader
too. When I added the vulkan-loader
everything ran and worked as expected.
Summary and full files
It took some extra work, but I got there in the end. Below are the files that I ended up with. In addition to these, remember to also add the build.rs
file as specified above.
shell.nix
Here's what I use for the dev environment. I use fenix for managing the Rust toolchain. I've also added cargo-edit
and cargo-watch
because they're handy tools to have. Everything else is Bevy-related.
{ pkgs ? import <nixpkgs> {} }:
let
fenix = import "${
fetchTarball "https://github.com/nix-community/fenix/archive/main.tar.gz"
}/packages.nix";
in
pkgs.mkShell {
shellHook = ''export LD_LIBRARY_PATH="$LD_LIBRARY_PATH:${pkgs.lib.makeLibraryPath [
pkgs.alsaLib
pkgs.udev
pkgs.vulkan-loader
]}"'';
buildInputs = with pkgs; [
(
with fenix;
combine (
with default; [
cargo
clippy-preview
latest.rust-src
rust-analyzer
rust-std
rustc
rustfmt-preview
]
)
)
cargo-edit
cargo-watch
lld
clang
# # bevy-specific deps (from https://github.com/bevyengine/bevy/blob/main/docs/linux_dependencies.md)
pkgconfig
udev
alsaLib
lutris
x11
xorg.libXcursor
xorg.libXrandr
xorg.libXi
vulkan-tools
vulkan-headers
vulkan-loader
vulkan-validation-layers
];
}
.cargo/config.toml
This is what the full config.toml
file looks like after adjusting the Linux config for NixOS.
# Add the contents of this file to `config.toml` to enable "fast build" configuration. Please read the notes below.
# NOTE: For maximum performance, build using a nightly compiler
# If you are using rust stable, remove the "-Zshare-generics=y" below.
[target.x86_64-unknown-linux-gnu]
linker = "clang"
rustflags = ["-Clink-arg=-fuse-ld=lld", "-Zshare-generics=y"]
# NOTE: you must manually install https://github.com/michaeleisel/zld on mac. you can easily do this with the "brew" package manager:
# `brew install michaeleisel/zld/zld`
[target.x86_64-apple-darwin]
rustflags = ["-C", "link-arg=-fuse-ld=/usr/local/bin/zld", "-Zshare-generics=y"]
[target.x86_64-pc-windows-msvc]
linker = "rust-lld.exe"
rustflags = ["-Zshare-generics=y"]
# Optional: Uncommenting the following improves compile times, but reduces the amount of debug info to 'line number tables only'
# In most cases the gains are negligible, but if you are on macos and have slow compile times you should see significant gains.
#[profile.dev]
#debug = 1
That's it for now. I hope this is useful to someone.
Until next time! 👋