Skip to content

Commit 6e5eaa7

Browse files
authored
Rust feature debugging (#7)
1 parent 96b48f8 commit 6e5eaa7

File tree

8 files changed

+285
-1
lines changed

8 files changed

+285
-1
lines changed
Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
+++
2+
title = "Rust crate feature debugging"
3+
date = 2024-12-12
4+
[extra]
5+
tags=["rust","bevy","cargo"]
6+
hidden = true
7+
custom_summary = "Figure out what enabled a feature in a crate in your dependencies breaking your build."
8+
+++
9+
10+
In this short post we look at how to debug a recent build breakage we encountered due to a *feature* being enabled on one of our dependencies that is not compatible with our build target: **wasm**.
11+
12+
# What happened
13+
14+
After porting our bevy based game [tinytakeoff](https://tinytakeoff.com) to the newest Bevy release: [0.15](https://bevyengine.org/news/bevy-0-15/) our build broke with the following error on **wasm**:
15+
16+
```sh
17+
cargo:warning=In file included from vendor/basis_universal/encoder/pvpngreader.cpp:14:
18+
cargo:warning=vendor/basis_universal/encoder/../transcoder/basisu.h:53:10: fatal error: 'stdlib.h' file not found
19+
cargo:warning= 53 | #include <stdlib.h>
20+
cargo:warning= | ^~~~~~~~~~
21+
cargo:warning=1 error generated.
22+
```
23+
24+
So that looks like some crate tries to build some c code under the hood. That is not a problem on native build targets but wont fly on **wasm**.
25+
26+
This happens in the `basis-universal` crate, what could that be good for? Reading up on it's [crates.io page](https://crates.io/crates/basis-universal) we find out that it is:
27+
28+
> Bindings for Binomial LLC's basis-universal Supercompressed GPU Texture Codec
29+
30+
Looking into the [Bevy Migration Guide for 0.14 to 0.15](https://bevyengine.org/learn/migration-guides/0-14-to-0-15) we find exactly one [place](https://bevyengine.org/learn/migration-guides/0-14-to-0-15/#add-feature-requirement-info-to-image-loading-docs) of it being mentioned:
31+
32+
<img src="screen1.png" alt="changelog screenshot" class="centered" style="max-width: 70%"/>
33+
34+
So this dependency is for sure nothing we need as we did not start making use of said image format. How did it get introduced?
35+
36+
Let's find the cause for this.
37+
38+
# How to find the cause
39+
40+
We first want to find out where in our tree of dependencies this one is used. `cargo tree` is the tool to help you analyze your dependencies as the graph structure they make up.
41+
42+
When running `cargo tree` we get get over 1.000 lines of output where we can search for `basis-universal`:
43+
44+
```sh
45+
│ │ ├── bevy_image v0.15.0
46+
│ │ │ ├── basis-universal v0.3.1
47+
│ │ │ │ ├── basis-universal-sys v0.3.1
48+
│ │ │ │ │ [build-dependencies]
49+
│ │ │ │ │ └── cc v1.2.3 (*)
50+
```
51+
52+
We got a winner. It is used by `bevy_image`. The problem is that we do not know why. Based on the changelog linked above we know it is supposed to be behind a `feature` flag called `basis_universal`, looking at our `Cargo.toml` we do not enable it though.
53+
54+
> Cargo will enable the minimum subset of `features` needed so that every dependency using `bevy` get the features they ask for.
55+
56+
The question therefore is: Which crate asks for this feature?
57+
58+
# Playing Cargo Feature Detective
59+
60+
There is a little known feature in `cargo tree` that allows us to not only see our dependency tree but also the features that are enabled in each crate.
61+
62+
Running `cargo tree -e features` in our repository root we get over 3.000 lines of this:
63+
64+
```sh
65+
├── winit v0.30.5
66+
│ ├── tracing v0.1.41
67+
│ │ ├── tracing-core v0.1.33
68+
│ │ │ └── once_cell feature "default"
69+
│ │ │ ├── once_cell v1.20.2
70+
│ │ │ └── once_cell feature "std"
71+
│ │ │ ├── once_cell v1.20.2
72+
│ │ │ └── once_cell feature "alloc"
73+
│ │ │ ├── once_cell v1.20.2
74+
│ │ │ └── once_cell feature "race"
75+
│ │ │ └── once_cell v1.20.2
76+
│ │ ├── pin-project-lite feature "default"
77+
│ │ │ └── pin-project-lite v0.2.15
78+
│ │ └── tracing-attributes feature "default"
79+
│ │ └── tracing-attributes v0.1.28 (proc-macro)
80+
```
81+
82+
Luckily we know know already what feature we are looking for: `basis-universal`, so lets search for `bevy feature "basis-universal"`:
83+
84+
```sh
85+
├── bevy_libgdx_atlas feature "default"
86+
│ └── bevy_libgdx_atlas v0.3.0
87+
│ ├── bevy feature "basis-universal"
88+
│ │ ├── bevy v0.15.0 (*)
89+
```
90+
91+
Here we go. Our own crate `bevy_libgdx_atlas` enables the feature `basis-universal` which in turn enables the dependency `basis-universal` which breaks our build on **wasm**. That makes it easier to fix. Funny enough it was used to enable `bevy_image` while trying to depend on the smallest subset of features of `bevy`. This is a known in Bevy 0.15, see [issue #16563](https://github.com/bevyengine/bevy/issues/16563). But there is a cleaner workaround by just enabling the `bevy_image` feature.
92+
93+
## Improving ergonomics
94+
95+
In case you run into multiple crates doing this and depending on said feature it is more ergonomic to invert the tree using: `cargo tree -e features -p bevy --invert`.
96+
With this we limit our root to `bevy` and we will find *one* entry for the feature and a subtree of dependencies using it:
97+
98+
```sh
99+
├── bevy feature "basis-universal"
100+
│ └── bevy_libgdx_atlas v0.3.0
101+
│ └── bevy_libgdx_atlas feature "default"
102+
│ └── tinytakeoff v0.1.1
103+
```
104+
105+
# Conclusion
106+
107+
The feature option in `cargo tree` is a very powerful tool in fighting against the subtle way dependencies and features in them can creep into your codebase.
108+
109+
Since it is close to christmas I want to make a whishlist to improve the situation:
110+
111+
1. A `cargo deny` like tool that allows me to white/blacklist features in dependencies.
112+
2. `cargo tree` should generate a computer readable format (ron/json whatever) to facilitate point 1.
113+
3. In a perfect world there would be a `cargo tree-tui` allowing to interactively inspect dependencies, their features and fan in (who uses it) and fan out (what it is using).
114+
115+
That being said `cargo tree` seems underutilized, so go and run it on your bevy project to figure out what features of dependencies like Bevy you actually compile. This can have a huge impact on your wasm binary size!
116+
117+
---
118+
119+
Do you need support building your Bevy or Rust project? Our team of experts can support you! [Contact us.](@/contact.md)
94.4 KB
Loading
Lines changed: 150 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,150 @@
1+
<!DOCTYPE html>
2+
<html lang="en">
3+
4+
<head>
5+
<meta charset="UTF-8">
6+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
7+
8+
<title>rustunit</title>
9+
<meta name="description" content="Rustunit offers software development consulting with a focus on rust, game-development and large scale distributed backend services.">
10+
11+
<link rel="stylesheet" href="https://rustunit.com/fonts.css">
12+
<link rel="stylesheet" href="https://rustunit.com/style.css">
13+
14+
<script src="https://cdn.usefathom.com/script.js" data-site="TSUIPVAW" defer></script>
15+
</head>
16+
17+
<body>
18+
<header>
19+
<a href="https://rustunit.com/">HOME</a>
20+
<a href="https://rustunit.com/#games">GAMES</a>
21+
<a href="https://rustunit.com/blog/">BLOG</a>
22+
<a href="https://rustunit.com/contact/">CONTACT</a>
23+
</header>
24+
25+
<div id="blogpage">
26+
<div class="date">2024-12-12</div>
27+
28+
<div class="hidden">hidden</div>
29+
30+
<h1 class="title">
31+
Rust crate feature debugging
32+
</h1>
33+
<div class="content">
34+
<p>In this short post we look at how to debug a recent build breakage we encountered due to a <em>feature</em> being enabled on one of our dependencies that is not compatible with our build target: <strong>wasm</strong>.</p>
35+
<h1 id="what-happened">What happened</h1>
36+
<p>After porting our bevy based game <a href="https://tinytakeoff.com">tinytakeoff</a> to the newest Bevy release: <a href="https://bevyengine.org/news/bevy-0-15/">0.15</a> our build broke with the following error on <strong>wasm</strong>:</p>
37+
<pre data-lang="sh" style="background-color:#212121;color:#eeffff;" class="language-sh "><code class="language-sh" data-lang="sh"><span>cargo:warning</span><span style="color:#89ddff;">=</span><span style="color:#c3e88d;">In </span><span style="color:#82aaff;">file included from vendor/basis_universal/encoder/pvpngreader.cpp:14:
38+
</span><span>cargo:warning</span><span style="color:#89ddff;">=</span><span style="color:#c3e88d;">vendor/basis_universal/encoder/../transcoder/basisu.h:53:10: </span><span style="color:#82aaff;">fatal error: </span><span style="color:#89ddff;">&#39;</span><span style="color:#c3e88d;">stdlib.h</span><span style="color:#89ddff;">&#39;</span><span style="color:#82aaff;"> file not found
39+
</span><span>cargo:warning</span><span style="color:#89ddff;">= </span><span style="color:#82aaff;">53 </span><span style="color:#89ddff;">| </span><span style="font-style:italic;color:#4a4a4a;">#include &lt;stdlib.h&gt;
40+
</span><span>cargo:warning</span><span style="color:#89ddff;">= | </span><span style="color:#82aaff;">^</span><span style="font-style:italic;color:#ff5370;">~~~~~~~~~
41+
</span><span>cargo:warning</span><span style="color:#89ddff;">=</span><span style="color:#c3e88d;">1 </span><span style="color:#82aaff;">error generated.
42+
</span></code></pre>
43+
<p>So that looks like some crate tries to build some c code under the hood. That is not a problem on native build targets but wont fly on <strong>wasm</strong>.</p>
44+
<p>This happens in the <code>basis-universal</code> crate, what could that be good for? Reading up on it's <a href="https://crates.io/crates/basis-universal">crates.io page</a> we find out that it is:</p>
45+
<blockquote>
46+
<p>Bindings for Binomial LLC's basis-universal Supercompressed GPU Texture Codec</p>
47+
</blockquote>
48+
<p>Looking into the <a href="https://bevyengine.org/learn/migration-guides/0-14-to-0-15">Bevy Migration Guide for 0.14 to 0.15</a> we find exactly one <a href="https://bevyengine.org/learn/migration-guides/0-14-to-0-15/#add-feature-requirement-info-to-image-loading-docs">place</a> of it being mentioned:</p>
49+
<img src="screen1.png" alt="changelog screenshot" class="centered" style="max-width: 70%"/>
50+
<p>So this dependency is for sure nothing we need as we did not start making use of said image format. How did it get introduced?</p>
51+
<p>Let's find the cause for this.</p>
52+
<h1 id="how-to-find-the-cause">How to find the cause</h1>
53+
<p>We first want to find out where in our tree of dependencies this one is used. <code>cargo tree</code> is the tool to help you analyze your dependencies as the graph structure they make up.</p>
54+
<p>When running <code>cargo tree</code> we get get over 1.000 lines of output where we can search for <code>basis-universal</code>:</p>
55+
<pre data-lang="sh" style="background-color:#212121;color:#eeffff;" class="language-sh "><code class="language-sh" data-lang="sh"><span style="color:#82aaff;">│ │ ├── bevy_image v0.15.0
56+
</span><span style="color:#82aaff;">│ │ │ ├── basis-universal v0.3.1
57+
</span><span style="color:#82aaff;">│ │ │ │ ├── basis-universal-sys v0.3.1
58+
</span><span style="color:#82aaff;">│ │ │ │ │ </span><span style="font-style:italic;color:#c792ea;">[</span><span style="color:#82aaff;">build</span><span style="color:#89ddff;">-</span><span style="color:#82aaff;">dependencies</span><span style="font-style:italic;color:#c792ea;">]
59+
</span><span style="color:#82aaff;">│ │ │ │ │ └── cc v1.2.3 (</span><span style="color:#89ddff;">*</span><span>)
60+
</span></code></pre>
61+
<p>We got a winner. It is used by <code>bevy_image</code>. The problem is that we do not know why. Based on the changelog linked above we know it is supposed to be behind a <code>feature</code> flag called <code>basis_universal</code>, looking at our <code>Cargo.toml</code> we do not enable it though.</p>
62+
<blockquote>
63+
<p>Cargo will enable the minimum subset of <code>features</code> needed so that every dependency using <code>bevy</code> get the features they ask for.</p>
64+
</blockquote>
65+
<p>The question therefore is: Which crate asks for this feature?</p>
66+
<h1 id="playing-cargo-feature-detective">Playing Cargo Feature Detective</h1>
67+
<p>There is a little known feature in <code>cargo tree</code> that allows us to not only see our dependency tree but also the features that are enabled in each crate.</p>
68+
<p>Running <code>cargo tree -e features</code> in our repository root we get over 3.000 lines of this:</p>
69+
<pre data-lang="sh" style="background-color:#212121;color:#eeffff;" class="language-sh "><code class="language-sh" data-lang="sh"><span style="color:#82aaff;">├── winit v0.30.5
70+
</span><span style="color:#82aaff;">│ ├── tracing v0.1.41
71+
</span><span style="color:#82aaff;">│ │ ├── tracing-core v0.1.33
72+
</span><span style="color:#82aaff;">│ │ │ └── once_cell feature </span><span style="color:#89ddff;">&quot;</span><span style="color:#c3e88d;">default</span><span style="color:#89ddff;">&quot;
73+
</span><span style="color:#82aaff;">│ │ │ ├── once_cell v1.20.2
74+
</span><span style="color:#82aaff;">│ │ │ └── once_cell feature </span><span style="color:#89ddff;">&quot;</span><span style="color:#c3e88d;">std</span><span style="color:#89ddff;">&quot;
75+
</span><span style="color:#82aaff;">│ │ │ ├── once_cell v1.20.2
76+
</span><span style="color:#82aaff;">│ │ │ └── once_cell feature </span><span style="color:#89ddff;">&quot;</span><span style="color:#c3e88d;">alloc</span><span style="color:#89ddff;">&quot;
77+
</span><span style="color:#82aaff;">│ │ │ ├── once_cell v1.20.2
78+
</span><span style="color:#82aaff;">│ │ │ └── once_cell feature </span><span style="color:#89ddff;">&quot;</span><span style="color:#c3e88d;">race</span><span style="color:#89ddff;">&quot;
79+
</span><span style="color:#82aaff;">│ │ │ └── once_cell v1.20.2
80+
</span><span style="color:#82aaff;">│ │ ├── pin-project-lite feature </span><span style="color:#89ddff;">&quot;</span><span style="color:#c3e88d;">default</span><span style="color:#89ddff;">&quot;
81+
</span><span style="color:#82aaff;">│ │ │ └── pin-project-lite v0.2.15
82+
</span><span style="color:#82aaff;">│ │ └── tracing-attributes feature </span><span style="color:#89ddff;">&quot;</span><span style="color:#c3e88d;">default</span><span style="color:#89ddff;">&quot;
83+
</span><span style="color:#82aaff;">│ │ └── tracing-attributes v0.1.28 (proc-macro</span><span>)
84+
</span></code></pre>
85+
<p>Luckily we know know already what feature we are looking for: <code>basis-universal</code>, so lets search for <code>bevy feature "basis-universal"</code>:</p>
86+
<pre data-lang="sh" style="background-color:#212121;color:#eeffff;" class="language-sh "><code class="language-sh" data-lang="sh"><span style="color:#82aaff;">├── bevy_libgdx_atlas feature </span><span style="color:#89ddff;">&quot;</span><span style="color:#c3e88d;">default</span><span style="color:#89ddff;">&quot;
87+
</span><span style="color:#82aaff;">│ └── bevy_libgdx_atlas v0.3.0
88+
</span><span style="color:#82aaff;">│ ├── bevy feature </span><span style="color:#89ddff;">&quot;</span><span style="color:#c3e88d;">basis-universal</span><span style="color:#89ddff;">&quot;
89+
</span><span style="color:#82aaff;">│ │ ├── bevy v0.15.0 (</span><span style="color:#89ddff;">*</span><span>)
90+
</span></code></pre>
91+
<p>Here we go. Our own crate <code>bevy_libgdx_atlas</code> enables the feature <code>basis-universal</code> which in turn enables the dependency <code>basis-universal</code> which breaks our build on <strong>wasm</strong>. That makes it easier to fix. Funny enough it was used to enable <code>bevy_image</code> while trying to depend on the smallest subset of features of <code>bevy</code>. This is a known in Bevy 0.15, see <a href="https://github.com/bevyengine/bevy/issues/16563">issue #16563</a>. But there is a cleaner workaround by just enabling the <code>bevy_image</code> feature.</p>
92+
<h2 id="improving-ergonomics">Improving ergonomics</h2>
93+
<p>In case you run into multiple crates doing this and depending on said feature it is more ergonomic to invert the tree using: <code>cargo tree -e features -p bevy --invert</code>.
94+
With this we limit our root to <code>bevy</code> and we will find <em>one</em> entry for the feature and a subtree of dependencies using it:</p>
95+
<pre data-lang="sh" style="background-color:#212121;color:#eeffff;" class="language-sh "><code class="language-sh" data-lang="sh"><span style="color:#82aaff;">├── bevy feature </span><span style="color:#89ddff;">&quot;</span><span style="color:#c3e88d;">basis-universal</span><span style="color:#89ddff;">&quot;
96+
</span><span style="color:#82aaff;">│ └── bevy_libgdx_atlas v0.3.0
97+
</span><span style="color:#82aaff;">│ └── bevy_libgdx_atlas feature </span><span style="color:#89ddff;">&quot;</span><span style="color:#c3e88d;">default</span><span style="color:#89ddff;">&quot;
98+
</span><span style="color:#82aaff;">│ └── tinytakeoff v0.1.1
99+
</span></code></pre>
100+
<h1 id="conclusion">Conclusion</h1>
101+
<p>The feature option in <code>cargo tree</code> is a very powerful tool in fighting against the subtle way dependencies and features in them can creep into your codebase.</p>
102+
<p>Since it is close to christmas I want to make a whishlist to improve the situation:</p>
103+
<ol>
104+
<li>A <code>cargo deny</code> like tool that allows me to white/blacklist features in dependencies.</li>
105+
<li><code>cargo tree</code> should generate a computer readable format (ron/json whatever) to facilitate point 1.</li>
106+
<li>In a perfect world there would be a <code>cargo tree-tui</code> allowing to interactively inspect dependencies, their features and fan in (who uses it) and fan out (what it is using).</li>
107+
</ol>
108+
<p>That being said <code>cargo tree</code> seems underutilized, so go and run it on your bevy project to figure out what features of dependencies like Bevy you actually compile. This can have a huge impact on your wasm binary size!</p>
109+
<hr />
110+
<p>Do you need support building your Bevy or Rust project? Our team of experts can support you! <a href="https://rustunit.com/contact/">Contact us.</a></p>
111+
112+
</div>
113+
</div>
114+
115+
<footer>
116+
117+
<div class="links">
118+
119+
<a rel="me" href="https:&#x2F;&#x2F;www.linkedin.com&#x2F;company&#x2F;rustunit&#x2F;" title="LinkedIn">
120+
<img alt="LinkedIn" class="icon" src="https://rustunit.com/icons/linkedin.svg" />
121+
</a>
122+
123+
<a rel="me" href="&#x2F;contact" title="Contact">
124+
<img alt="Contact" class="icon" src="https://rustunit.com/icons/mail.svg" />
125+
</a>
126+
127+
<a rel="me" href="https:&#x2F;&#x2F;github.com&#x2F;rustunit" title="GitHub">
128+
<img alt="GitHub" class="icon" src="https://rustunit.com/icons/github.svg" />
129+
</a>
130+
131+
<a rel="me" href="https:&#x2F;&#x2F;www.youtube.com&#x2F;@rustunit_com" title="YouTube">
132+
<img alt="YouTube" class="icon" src="https://rustunit.com/icons/youtube.svg" />
133+
</a>
134+
135+
<a rel="me" href="https:&#x2F;&#x2F;mastodon.social&#x2F;@rustunit" title="Mastodon">
136+
<img alt="Mastodon" class="icon" src="https://rustunit.com/icons/mastodon.svg" />
137+
</a>
138+
139+
<a rel="me" href="https:&#x2F;&#x2F;discord.gg&#x2F;rZv4uxSQx3" title="Discord">
140+
<img alt="Discord" class="icon" src="https://rustunit.com/icons/discord.svg" />
141+
</a>
142+
143+
</div>
144+
145+
146+
<div>Copyright © 2024 Rustunit B.V.</div>
147+
</footer>
148+
</body>
149+
150+
</html>
94.4 KB
Loading

0 commit comments

Comments
 (0)