# How to use default "uninstall" step, without \`rm\` system command

**URL:** <https://ziggit.dev/t/how-to-use-default-uninstall-step-without-rm-system-command/17682>\
**Category:** Help\
**Tags:** build-system\
**Created:** [September 22, 2026, 10:01am UTC](https://ziggit.dev/t/how-to-use-default-uninstall-step-without-rm-system-command/17682 "2026-09-22T10:01:49Z")\
**Posts on this page:** 7\
**Page:** 1

<div class="post-metadata">

**Author:** ![norwd](https://ziggit.dev/user_avatar/ziggit.dev/norwd/32/10204_2.png) [@norwd](https://ziggit.dev/u/norwd)\
**Post date:** [September 22, 2026, 10:01am UTC](https://ziggit.dev/t/how-to-use-default-uninstall-step-without-rm-system-command/17682/1 "2026-09-22T10:01:49Z")

</div>

We all know that a the default target for `zig build` is the `install` step, but there is also a default `uninstall` step that I don’t often see used. Its purpose seems pretty obvious, but (as far as I can see after scouring the std docs) there doesn’t appear to be a way to remove files from the installation prefix. Looking at other projects’ `build.zig` files, it looks like most of the search hits for `b.getUninstallStep` are from forks of the std library, and the rest all call some variation of `b.addSystemCommand("rm", "foo")`.

Is this really the only way to uninstall files from the prefix?

I’ve racked my brain for what the “correct” way to delete artefacts would be and the only solution that I can think of, that doesn’t rely on the end user’s system, is to write a small implementation of `rm` and build it just in time to use it to delete the artefacts and then itself. But then I’d need to either re-write `rm.zig` in every project, or have a dependency just to cleanly uninstall?

I feel like I must be missing something? There isn’t `b.addUninstallFile(path)` or something similar that I just couldn’t find?

---

<div class="post-metadata">

**Author:** ![lukeflo](https://ziggit.dev/user_avatar/ziggit.dev/lukeflo/32/7514_2.png) [@lukeflo](https://ziggit.dev/u/lukeflo)\
**Post date:** [September 22, 2026, 10:05am UTC](https://ziggit.dev/t/how-to-use-default-uninstall-step-without-rm-system-command/17682/2 "2026-09-22T10:05:30Z")

</div>

`zig build uninstall` is not properly implemented yet.

I had the same question a few months ago: [https://codeberg.org/ziglang/zig/issues/35504](https://codeberg.org/ziglang/zig/issues/35504)

---

<div class="post-metadata">

**Author:** ![nathanielketema](https://ziggit.dev/user_avatar/ziggit.dev/nathanielketema/32/8732_2.png) [@nathanielketema](https://ziggit.dev/u/nathanielketema)\
**Post date:** [September 22, 2026, 1:07pm UTC](https://ziggit.dev/t/how-to-use-default-uninstall-step-without-rm-system-command/17682/3 "2026-09-22T13:07:52Z")

</div>

To work around that, I usually do the following inside `build.zig`:

```zig
// I'm using the default prefix here
try b.build_root.handle.deleteTree(io, "zig-out");

```

This way I don’t have any dependency on `rm`

---

<div class="post-metadata">

**Author:** ![andrewrk](https://ziggit.dev/user_avatar/ziggit.dev/andrewrk/32/7011_2.png) [@andrewrk](https://ziggit.dev/u/andrewrk)\
**Post date:** [September 23, 2026, 12:13am UTC](https://ziggit.dev/t/how-to-use-default-uninstall-step-without-rm-system-command/17682/4 "2026-09-23T00:13:07Z")

</div>

The reason the feature has been such low priority is that it’s kind of a nonsense feature to begin with. By definition, unless you install into an isolated directory that would be trivial to remove all together, then you are potentially destroying information during the installation process by overwriting an existing file or configuration, and an uninstall step cannot possibly revert the system state to how it was before. This is why, when you install things by hand using `zig build`, it is indeed best practice to install them to an isolated directory in your home folder, and when using `zig build` in a system package, the system package manager will manage the installation and uninstallation for end users.

That said, maybe there’s still room to implement the feature after all, but you can see why it’s not exactly a top priority.

---

<div class="post-metadata">

**Author:** ![pasta](https://ziggit.dev/user_avatar/ziggit.dev/pasta/32/6273_2.png) [@pasta](https://ziggit.dev/u/pasta)\
**Post date:** [September 23, 2026, 11:56am UTC](https://ziggit.dev/t/how-to-use-default-uninstall-step-without-rm-system-command/17682/5 "2026-09-23T11:56:45Z")

</div>

To add one other “mitigation”, you can encourage users who might `zig build install` themselves with an external `--prefix` to use `--verbose` and perhaps a ` | tee install.log`. In the very least this’ll allow retro-active manual cleanup.

---

<div class="post-metadata">

**Author:** ![vrypan](https://ziggit.dev/user_avatar/ziggit.dev/vrypan/32/10150_2.png) [@vrypan](https://ziggit.dev/u/vrypan)\
**Post date:** [September 23, 2026, 6:46pm UTC](https://ziggit.dev/t/how-to-use-default-uninstall-step-without-rm-system-command/17682/6 "2026-09-23T18:46:57Z")

</div>

Personally, I often use uninstall (but in a Makefile tbh) a lot during development:

- I have the released version of the project binaries installed using homebrew.
- I use install to install the dev binaries in ~/.local/bin
- My PATH includes ~/.local/bin before other paths.

This way I can use install/uninstall during development and have scripts, tools, etc that use either the production or the development binaries, which is very handy.

---

<div class="post-metadata">

**Author:** ![norwd](https://ziggit.dev/user_avatar/ziggit.dev/norwd/32/10204_2.png) [@norwd](https://ziggit.dev/u/norwd)\
**Post date:** [October 2, 2026, 8:51am UTC](https://ziggit.dev/t/how-to-use-default-uninstall-step-without-rm-system-command/17682/7 "2026-10-02T08:51:57Z")

</div>

I’ve been thinking about this, is there ever a case when the `zig build uninstall` command should do anything other than the inverse of `zig build install`?

If might be reasonable for the uninstall steps to automatically remove (maybe preserving backups in the cache) all the installation files that were added by the install step. So e.g. `b.addExecutable(....)` implicitly also does `b.getUnintallStep().dependsOn(&b.deleteExecutable(....).Step)`, or something similar?
