Bazel is a fully reproducible and hermetic build system. A lot of painstaking work goes into it producing the exact same artifacts build after build. And that provides some interesting properties that you can leverage for artifact caching, deployments, and CICD.
We very happily runny a polyglot monorepo w/ 5+ languages, multiple architectures, with fully reproducible artifacts and deployment manifests, all deployed to almost all AWS regions on every build. We update tens of thousands of resources in every environment for every build. The fact that Bazel is creating reproducible artifacts allow us to manage this seamlessly and reliably. Clean builds take an hour+, but our GH self-hosted runners often complete commit to green build for our devs in less than a minute.
The core concept of Bazel is very simple: explicitly declare the input you pass to a rule/tool and explicitly declare the output it creates. If that can click, you're half way there.
> Bazel is a fully reproducible and hermetic build system.
Yes, and it's very important to note that Bazel does nothing to solve the problem about having a reproducible and hermetic runtime. Even if you think you aren't linking against anything dynamically, you are probably linking against several system libraries which must be present in the same versions to get reproducible and hermetic behavior.
This is solvable with Docker or exceptionally arcane Linux hackery, but it's completely missing from the Bazel messaging and it often leaves people thinking it provides more than it really does.
Bazel is perfectly capable of producing static binaries, if that's what you want. It's not fair to say that does nothing to solve this problem, it simply does not mandate that the artifacts it produces are static.
Most static binaries are not completely statically linked. The system libstdc++ and libc as well as a few other things are almost always linked dynamically.
In particular, it’s almost impossible to get glibc to link statically. You need to switch to musl or another libc that supports it.
Static linking means no dependencies whatsoever, so it only needs a syscall interface in the kernel. If your binary requires libstdc++, it's not static, period.
Go creates static binaries. I was a bit bummed out to learn that Rust doesn't by default since it requires glibc (and copying a binary to an older distribution fails because glibc is too old).
it's pretty trivial to create static binaries in Rust, but things like openssl won't compile since openssl depends on glibc.
Two commands to get you running:
1) rustup target add x86_64-unknown-linux-musl
2) cargo build --target=x86_64-unknown-linux-musl
Yes and no, it's trivial unless you use some of the most common libraries that depend on, i.e. OpenSSL, like pretty much anything that talks with the outside world. Then it's painful. (You mentioned OpenSSL in your comment but somehow I missed that part when I replied. My bad.)
One thing Go has going for it is that they have a more complete standard library so you can do without the pain of cross-compiling—which is what you're doing if most of your libs assume you're in a glibc environment but you really want musl.
I know because I recently tried compiling a Rust application that had to run on an air-gapped older Debian machine with an old glibc, and the easiest solution was to set up a debian container, download Rust and compile there, instead of fixing all the cargo deps that didn't like to be built with musl.
So I just migrated everything over to that (which consisted of enabling the `rusttls` feature for every crate) and made another build with musl. Everything worked perfectly fine, and since it's not a performance sensitive application, there was basically no drawbacks. The binary became a bit bigger, but still under 4MB (with other assets baked into it) so wasn't a big issue.