# Running build.rs in rust-analyzer

**URL:** https://internals.rust-lang.org/t/running-build-rs-in-rust-analyzer/14154
**Category:** tools and infrastructure
**Created:** [March 2, 2021, 4:20pm UTC](https://internals.rust-lang.org/t/running-build-rs-in-rust-analyzer/14154 "2021-03-02T16:20:29Z")
**Posts on this page:** 14
**Page:** 1

<div class="post-metadata">

### Author: ![matklad](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/matklad/32/12266_2.png) [@matklad](https://internals.rust-lang.org/u/matklad)
#### Post date: [March 2, 2021, 4:20pm UTC](https://internals.rust-lang.org/t/running-build-rs-in-rust-analyzer/14154/1 "2021-03-02T16:20:29Z")

</div>

Right now, rust-analyzer doesn't run build scripts by default, for mostly historical reasons. Running build scripts (and proc macros) is sadly required to get decent IDE support, as those can generate rust code and affect the semantic model of code.

I am wondering if folks here have ideas about what would be the best user experience here. I'll refrain from publishing my own thoughts for now to avoid spoiling creativity 🙂

---

<div class="post-metadata">

### Author: ![Nokel81](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/nokel81/32/3966_2.png) [@Nokel81](https://internals.rust-lang.org/u/Nokel81)
#### Post date: [March 2, 2021, 4:43pm UTC](https://internals.rust-lang.org/t/running-build-rs-in-rust-analyzer/14154/2 "2021-03-02T16:43:43Z")

</div>

I guess because `build.rs` is essentially a script file it is hard to tell which files it touches. Though it might be able to provide it with a mock of the file system crate and then track which files it reads from and only run it again if itself or any of those are changed.

---

<div class="post-metadata">

### Author: ![kornel](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/kornel/32/2711_2.png) [@kornel](https://internals.rust-lang.org/u/kornel)
#### Post date: [March 2, 2021, 4:52pm UTC](https://internals.rust-lang.org/t/running-build-rs-in-rust-analyzer/14154/3 "2021-03-02T16:52:54Z")

</div>

`cargo check` already runs them, so I don't think it would be too terrible if rust-analyzer did too. Just don't re-run it `build.rs` on every keystroke 🙂

But you may need to re-run proc macros, even on every keystroke, when the code they touch is changed (e.g. editing a struct definition with `derive`).

Would it be possible to run these asynchronously? present best-guess based on source without them, run them in the background, update results once they finish. This way slow build scripts/proc-macros wouldn't hurt latency of IDE experience.

---

<div class="post-metadata">

### Author: ![cuviper](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/cuviper/32/1897_2.png) [@cuviper](https://internals.rust-lang.org/u/cuviper)
#### Post date: [March 2, 2021, 5:04pm UTC](https://internals.rust-lang.org/t/running-build-rs-in-rust-analyzer/14154/4 "2021-03-02T17:04:19Z")

</div>

> [@kornel](#):
>
> `cargo check` already runs them,

For that reason, I think it would be ok after there has been a check, like `checkOnSave`. But I don't think arbitrary execution should be part of startup, just having opened a file.

---

<div class="post-metadata">

### Author: ![kornel](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/kornel/32/2711_2.png) [@kornel](https://internals.rust-lang.org/u/kornel)
#### Post date: [March 2, 2021, 6:31pm UTC](https://internals.rust-lang.org/t/running-build-rs-in-rust-analyzer/14154/5 "2021-03-02T18:31:26Z")

</div>

Yeah, running arbitrary code from just looking at a Cargo project is risky. But because of `cargo check` the cat is out of the bag — I can't rely on it not happening. In [cargo-crev I hide `Cargo.toml` to itentionally break IDE integrations](https://github.com/crev-dev/cargo-crev/issues/381) to mitigate this risk.

---

<div class="post-metadata">

### Author: ![gbutler](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/gbutler/32/3670_2.png) [@gbutler](https://internals.rust-lang.org/u/gbutler)
#### Post date: [March 2, 2021, 9:19pm UTC](https://internals.rust-lang.org/t/running-build-rs-in-rust-analyzer/14154/6 "2021-03-02T21:19:14Z")

</div>

I believe an initial implementation could simply re-run the build.rs whenever it is changed/saved and then re-index everything it writes to (by monitoring which files it opens etc.). Re-running the builid.rs when something it reads changes, may, in the initial implementation be too much work. If so, having to re-save the build.rs to have any changes take effect from its inputs would not be the worst user experience.

If instead of attempting to monitor what build.rs does to figure out its inputs and outputs, would it be possible to add a new file that declares them? Something like this:

Filename: build.decl (contains declarations for build.rs, it not present, build.rs must be run manually by the user re-saving it) Contents:

```rust
[directories.input]
path1=/some/dir
path2=/someother/dirs/*.build.decl
path3=...potentiall other kind of paths, maybe with regex...

[directories.output]
...same...

[files.input]
...same but files rather than directories...

[files.output]
...same...

[other.input]
name1 = "description of other kind of input 1"
name2 = "..."
...
namen = "..."

[other.output]
name1 = "description of other kind of output 1"
name2 = "..."
...
namen = "..."

```

Maintainers could/should be encouraged to add this file. Perhaps cargo could require it if there is a build.rs. Rust analyzer (and similar tools, including potentially cargo) could leverage this file to know when to re-run build.rs. In addition, if this file were incorrect for what the build.rs does, it would get noticed by a lot of people who were maintaining the crate as analyzer would constantly be getting things wrong if this file were not correct.

The "other.\*" options are things that are outside the scope of files/directories and that analyzer (at least initially) cannot monitor and so cannot 100% reliably re-build the build.rs. Analyzer could warn the user that they may need to manually invoke the build or build.rs if any of those resources change.

---

<div class="post-metadata">

### Author: ![Aloso](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/aloso/32/5039_2.png) [@Aloso](https://internals.rust-lang.org/u/Aloso)
#### Post date: [March 3, 2021, 12:27am UTC](https://internals.rust-lang.org/t/running-build-rs-in-rust-analyzer/14154/7 "2021-03-03T00:27:49Z")

</div>

I think the best solution would be to compile the build script to WASM. Then it could be executed with read access to the current workspace and write access only to the target dir. More permissions could be given if necessary. The WASM runtime could also record which files/directories are read when the build script is executed for the first time, and inform rust-analyzer to watch these locations.

But I guess this would require some changes to Cargo and/or rustc, and we don't want to wait until these changes are implemented and land in stable Rust 😐

---

<div class="post-metadata">

### Author: ![bjorn3](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/bjorn3/32/2736_2.png) [@bjorn3](https://internals.rust-lang.org/u/bjorn3)
#### Post date: [March 3, 2021, 6:57am UTC](https://internals.rust-lang.org/t/running-build-rs-in-rust-analyzer/14154/8 "2021-03-03T06:57:40Z")

</div>

Compiling build scripts to wasm would make it impossible to for example use bindgen, as it depends on libclang, which is compiled for the host and not for wasm.

---

<div class="post-metadata">

### Author: ![kornel](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/kornel/32/2711_2.png) [@kornel](https://internals.rust-lang.org/u/kornel)
#### Post date: [March 3, 2021, 11:57am UTC](https://internals.rust-lang.org/t/running-build-rs-in-rust-analyzer/14154/9 "2021-03-03T11:57:30Z")

</div>

Cargo has `rerun-if-changed`, but [it has a flaw that makes it unusable for build dependencies](https://github.com/rust-lang/cargo/issues/4587), so I would be very happy to see some mechanism that replaces it.

But it needs to be compatible with delegating changes to build-time dependencies, because the build script may discover these files and their relationships at run time (e.g. searching for a package, scanning for HTML templates to compile, etc.).

---

<div class="post-metadata">

### Author: ![matklad](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/matklad/32/12266_2.png) [@matklad](https://internals.rust-lang.org/u/matklad)
#### Post date: [March 4, 2021, 11:32am UTC](https://internals.rust-lang.org/t/running-build-rs-in-rust-analyzer/14154/10 "2021-03-04T11:32:44Z")

</div>

Let me dump my thoughts on the issue right now.

The fundamental problem here is security. build.rs and proc macros can do arbitrary things. It is not good that just opening some random Cargo project in an IDE for reading you might get pwned.

On the other hand, not running these things by default would result in a poor user experience (or rather, it already results in poor user experience today, and we want to fix it). Tools really should be zero config and as helpful as possible out of the box. Naturally, the first thought here is to ask the user "do you want to run build scripts?" on startup. I however, strongly believe that _first use_ dialogs are an anti-pattren. They are an annoyance for experience user, and confusion and distraction for novices ("what's a build script?").

Performance and reliability are big problems, but are not as fundamental: asynchrony, proper error reporing, and fallbacks are a good enough solution here.

It's also true that we already `cargo check` on save by default, and may editors have auto-save, so, in a sense.

For these reasons, the current plan is to just enable running `cargo check` at startup by default (with an option to disable it).

---

<div class="post-metadata">

### Author: ![kornel](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/kornel/32/2711_2.png) [@kornel](https://internals.rust-lang.org/u/kornel)
#### Post date: [March 4, 2021, 1:18pm UTC](https://internals.rust-lang.org/t/running-build-rs-in-rust-analyzer/14154/11 "2021-03-04T13:18:56Z")

</div>

I don't think rust-analyzer can do much here in terms of security. It's an underlying problem, bigger than just rust-analyzer's.

There was a prototype of WASM-based proc-macros. It'd be cool if Rust made it official:

> **[dtolnay/watt](https://github.com/dtolnay/watt)**
>
> Runtime for executing procedural macros as WebAssembly

but for build scripts I don't even have a solution. If you sandbox them, then lots of them won't be able to do their job (like searching the OS for packages or running arbitrary C build systems). And even if you sandbox them for Rust analyzer, a user working on a project is likely to run `cargo test` or run the binary anyway, so all that effort would only delay pwnage for 5 minutes.

---

<div class="post-metadata">

### Author: ![djc](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/djc/32/1592_2.png) [@djc](https://internals.rust-lang.org/u/djc)
#### Post date: [March 5, 2021, 10:09am UTC](https://internals.rust-lang.org/t/running-build-rs-in-rust-analyzer/14154/12 "2021-03-05T10:09:03Z")

</div>

I wonder if we should build out a more declarative system that targets specific use cases. Presumably this can be done in a backwards-compatible way.

---

<div class="post-metadata">

### Author: ![kornel](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/kornel/32/2711_2.png) [@kornel](https://internals.rust-lang.org/u/kornel)
#### Post date: [March 5, 2021, 1:07pm UTC](https://internals.rust-lang.org/t/running-build-rs-in-rust-analyzer/14154/13 "2021-03-05T13:07:27Z")

</div>

`build.rs` is mainly used for getting C dependencies, and I do not think it is possible to have a declarative system for C dependencies that isn't a pain to use. The C ecosystem has tried and tried many times, and hasn't found a satisfactory solution that works across different Linux distros, on Windows, macOS, iOS, Android and WASM.

It's a problem that is deceptively _almost_ doable, but breaks at edge cases, and having C dependencies work well is all about handling the edge cases. Because `build.rs` scripts are able to have complex fallback logic and try many workarounds, specific for each dependency, they are paradoxically more reliable for getting C dependencies work than native C tools! e.g. LLVM has its own `llvm-config` replacement for `pkg-config`. [OpenSSL build script](https://github.com/sfackler/rust-openssl/tree/32e215751e01eb8f583a124479a9c3f630dccae1/openssl-sys/build) needs a lot of custom logic. [OpenMP needs compiler-specific hacks](https://gitlab.com/kornelski/openmp-rs/-/blob/master/build.rs). libjpeg [needs different assemblers and has 3 ABIs](https://github.com/kornelski/mozjpeg-sys/blob/master/src/build.rs).

You'd have to invent a new build system that is a superset of all these custom build.rs scripts full of weird one-off hacks. These hacks are there for a reason — if you try to simplify and don't replicate all of the weird hacks, you'll get build errors. It would be amount of work comparable to making a Linux distro that unifies all Linux distros, and also works for all platforms from Windows to WASM at the same time.

---

<div class="post-metadata">

### Author: ![system](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/system/32/14092_2.png) [@system](https://internals.rust-lang.org/u/system)
#### Post date: [June 3, 2021, 1:07pm UTC](https://internals.rust-lang.org/t/running-build-rs-in-rust-analyzer/14154/14 "2021-06-03T13:07:35Z")

</div>

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.
