It feels to me like this comes from a linux-specific viewpoint. My understanding is that for a bunch of platforms, the supported platform interface is mostly defined by shared libraries (eg: macOS, Windows), and that the actual kernel interface isn’t guaranteed to be stable. For one, I recall that Go has to actually link to the C library in some cases.
So, sure, it sucks that the ABI is defined in terms of C, but it’s one of those things that’d be hugely expensive to replace with something “better”, for little benefit to anyone but language/runtime designers.
Yes, this is Linux-specific. It’s not portable to other OSes. That is fine. Many people are just targeting Linux anyway (think server workloads) so why should they drag around a bunch of deadweight complexity for a “lowest common denominator” interface?
Very true, but it’s also worth observing that afaik Linux is the odd one out here. I know Windows forces you to go through a C lib, and I think the BSD’s generally do as well – I seem to remember hearing about one that purposefully renumbered its syscalls every release to avoid people trying to invoke them directly.
IMO putting the userland/kernel ABI boundary in userland functions is honestly slightly nicer than putting it at the syscall boundary, seems like it would make it easier to implement VDSO-y things.
Pretty much every non-Unix makes the stable API (or mandates it) libc, but Windows take the other approach of not privileging any language runtime at all for the system interface. You can take your pick of libc (as many as you want), or none at all.
Tried it with two projects of mine, both compiled fine with it. One of them even worked! The other ended up segfaulting, so that is gonna be fun to debug a little, to verify that the problem isn’t in my code.
An interesting project! Will keep an eye on it, maybe I’ll find a good reason to use it somewhere.
Oh, it can! unsafe is a thing, yet, pure rust. But it can do so even without unsafe! At least, no unsafe used in the package itself, only in dependencies:
nix-shell-env ❯ cargo test -r --test config
Finished release [optimized] target(s) in 0.59s
Running tests/config.rs (target/release/deps/config-37c2a2607b5ce536)
error: test failed, to rerun pass `--test config`
Caused by:
process didn't exit successfully: `/home/algernon/src/personal/rust/ironforge/target/release/deps/config-37c2a2607b5ce536` (signal: 11, SIGSEGV: invalid memory reference)
A think I always find frustrating about projects like this is that this means surprising changes in behavior in software that I have to use. A well-known example is DNS: there have been times where Go didn’t implement exactly the same DNS lookup procedure as glibc, and so it makes debugging problems with software I don’t maintain more difficult and surprising. Wrappers around syscall numbers/args aren’t the whole interface story.
This is the thing I really hate about a lot of new languages. They come along and say ‘existing build systems suck, I will create a new build system for my language’. And then they assume that everything written in their language will be written in only their language. They build package managers for their language and ignore the fact that these packages depend on shared libraries written in other languages.
The reason that C/C++ build infrastructure is so complex is that it isn’t really C/C++ build infrastructure, it’s generic build infrastructure that happens to be mostly used for C/C++. Reinventing it by providing the subset that 90% of users of your language need will always end up missing features that are necessary.
To be fair, it’s a trade-off between usability can capability. Most people just want to build some silly little program, and don’t care about integrating it with a preexisting build system. So far, I’ve founded that cargo’s approach of allowing you to shell out in a build script is mostly good enough, even if it does have some significant downsides. And for other cases, you can integrate with nix, bazel or similar.
The reason that C/C++ build infrastructure is so complex is that it isn’t really C/C++ build infrastructure, it’s generic build infrastructure that happens to be mostly used for C/C++.
No, it’s because C/C++ has no conventions or metadata at all about how to actually build anything, so every single program and library needs to reinvent the wheel to figure out how to actually compile libfoo.so, let alone figure out which libfoo.so you might need. And they all inevitably end up doing it slightly differently. Now every C/C++ build system needs to be able to incorporate any possible combination of other C/C++ build systems, and most even-slightly-nontrivial projects have to heavily customize their own boutique toolchains out of make or CMake or whatever to just put some basic junk together.
So yeah, it’s generic, in the same way a pile of wood and nails is a generic building.
Now every C/C++ build system needs to be able to incorporate any possible combination of other C/C++ build systems
No, it need not. This may be the easiest approach with short-term advantages and long-term pain, but it’s not the only approach. A project can instead convert all third-party dependencies it needs to use the same build system as the project itself.
C/C++ build systems, both their poor quality and multitude, are the reason package management is the problem. Given a single, sane build system, package management is not that difficult to solve (well, until you get to dependency configuration support, that is).
It feels to me like this comes from a linux-specific viewpoint. My understanding is that for a bunch of platforms, the supported platform interface is mostly defined by shared libraries (eg: macOS, Windows), and that the actual kernel interface isn’t guaranteed to be stable. For one, I recall that Go has to actually link to the C library in some cases.
So, sure, it sucks that the ABI is defined in terms of C, but it’s one of those things that’d be hugely expensive to replace with something “better”, for little benefit to anyone but language/runtime designers.
Yes, this is Linux-specific. It’s not portable to other OSes. That is fine. Many people are just targeting Linux anyway (think server workloads) so why should they drag around a bunch of deadweight complexity for a “lowest common denominator” interface?
Very true, but it’s also worth observing that afaik Linux is the odd one out here. I know Windows forces you to go through a C lib, and I think the BSD’s generally do as well – I seem to remember hearing about one that purposefully renumbered its syscalls every release to avoid people trying to invoke them directly.
IMO putting the userland/kernel ABI boundary in userland functions is honestly slightly nicer than putting it at the syscall boundary, seems like it would make it easier to implement VDSO-y things.
Pretty much every non-Unix makes the stable API (or mandates it) libc, but Windows take the other approach of not privileging any language runtime at all for the system interface. You can take your pick of libc (as many as you want), or none at all.
This is actually mentioned in the article.
Also, they do fallback to a libc-based implementation on non-linux OSs.
Tried it with two projects of mine, both compiled fine with it. One of them even worked! The other ended up segfaulting, so that is gonna be fun to debug a little, to verify that the problem isn’t in my code.
An interesting project! Will keep an eye on it, maybe I’ll find a good reason to use it somewhere.
You must be mistaken. A pure rust program can’t segfault :-)
Oh, it can!
unsafe
is a thing, yet, pure rust. But it can do so even without unsafe! At least, no unsafe used in the package itself, only in dependencies:(if you wanna try this here :))
A think I always find frustrating about projects like this is that this means surprising changes in behavior in software that I have to use. A well-known example is DNS: there have been times where Go didn’t implement exactly the same DNS lookup procedure as glibc, and so it makes debugging problems with software I don’t maintain more difficult and surprising. Wrappers around syscall numbers/args aren’t the whole interface story.
seems like half the time i try to build a non-trivial rust program, it fails due to some C dependency that the build system does not handle.
i hope more rust projects will be rewritten in rust for real.
This is the thing I really hate about a lot of new languages. They come along and say ‘existing build systems suck, I will create a new build system for my language’. And then they assume that everything written in their language will be written in only their language. They build package managers for their language and ignore the fact that these packages depend on shared libraries written in other languages.
The reason that C/C++ build infrastructure is so complex is that it isn’t really C/C++ build infrastructure, it’s generic build infrastructure that happens to be mostly used for C/C++. Reinventing it by providing the subset that 90% of users of your language need will always end up missing features that are necessary.
To be fair, it’s a trade-off between usability can capability. Most people just want to build some silly little program, and don’t care about integrating it with a preexisting build system. So far, I’ve founded that cargo’s approach of allowing you to shell out in a build script is mostly good enough, even if it does have some significant downsides. And for other cases, you can integrate with nix, bazel or similar.
No, it’s because C/C++ has no conventions or metadata at all about how to actually build anything, so every single program and library needs to reinvent the wheel to figure out how to actually compile
libfoo.so
, let alone figure out whichlibfoo.so
you might need. And they all inevitably end up doing it slightly differently. Now every C/C++ build system needs to be able to incorporate any possible combination of other C/C++ build systems, and most even-slightly-nontrivial projects have to heavily customize their own boutique toolchains out of make or CMake or whatever to just put some basic junk together.So yeah, it’s generic, in the same way a pile of wood and nails is a generic building.
No, it need not. This may be the easiest approach with short-term advantages and long-term pain, but it’s not the only approach. A project can instead convert all third-party dependencies it needs to use the same build system as the project itself.
The problem isn’t builds, but package management.
C/C++ build systems, both their poor quality and multitude, are the reason package management is the problem. Given a single, sane build system, package management is not that difficult to solve (well, until you get to dependency configuration support, that is).
Package management is NP-hard, in the context of version unification (which is required for C/C++!)
Or use a better build system that supports any kind of dependencies ¯\_(ツ)_/¯ (like Nix 👀)