Since rust has no standard, how can it have a standard build system?
That aside, the fact that C has no enforced/bundled/included/coupled build system is personally a major win over rust. Why would you want to be forced to do things the way cargo does it? Not everyone has the same requirements. It's also a serious misconception that cross-platform building in C is is difficult, it's not as long as simply consider it from the beginning and test it (and ignore microsoft compilers and environments since it just much easier to cross compile for windows or compile via some mingw environment).
>Now you just have to have the user tell you where it's located.
No? You use pkg-config with the fallback option of having the user use PKG_CONFIG_PATH to specify a custom location for .pc files. If the library sucks and doesn't support pkg-config, either pick a better library or add an option to fall back if -llibrary doesn't find it. Realistically users should be encouraged to use their system package manager rather than just installing libraries all over the place. I agree that this could be made smoother but at the end of the day, this has never really been an issue for projects.
>There's a lot of potential error messages you have to deal with, just to get at what ought to be a core feature of any modern programming language.
I highly disagree that programming languages should have anything to do with package management. Feel free to make a programming language, feel free to bundle with it a decoupled optional build system, and feel free to attach to that an optional and decoupled package manager (see python) but to bundle them all together is incredibly inflexible and discourages me from using that particular language.
> Maybe you might want to vendor that library in your source tree, just to be sure you always have it.
Unless this is a commercial proprietary internal project, you NEVER want this.
> Now you get the fun of having your build system invoke another build system as a subproject.
If you still DO want it, it's usually trivial to do this. Linux distributions usually publish files which automate building all packages, these files usually don't span thousands of lines. If archlinux can do this with some shell scripts then it's not that hard to do it from any build system. That being said, you DON'T want to do this, it's just the wrong way to think about the responsibilities. You document the versions of libraries that your project supports, optionally you document that your project uses pkg-config or whatnot as a way to specify unconventional locations for dependencies. The people compiling your code follow these basic instructions and they get your code built. It's actually really not that hard.
> * Screw other dependencies, let's just use a newer C standard version, since it is in C11. How do you get your C compiler to compile for C11? Oh wait, that's basically the same world of pain as it is to try to use OpenMP.
C11 optional supports a thread model which was never thought about. So it's not an option here. That being said, you are seriously misinformed if you think that compiling against a specific C standard version is particularly hard. It's nontrivial to get compilers to completely drop their own extensions, but as long as you make sure your code is compliant with a specific standard and explicitly make that clear to the compiler's you're invoking, it's really not difficult. Certainly not as difficult as making sure that the OpenMP version is correct.
To be fair, given this completely incendiary article I would doubt that getting OpenMP to work is as hard as the article makes it sound, I've certainly never had these issues with it, but at the same time I don't have the time to look at the particular problems the project had so I will have to take it on faith that at least on that one particular point there might be an issue.
> * Your build system is cargo and you specify things primarily with Cargo.toml. This is as little work as the happiest happy paths of C/C++ build systems. And note you don't really choose build system; there is one ecosystem-approved build system--for C/C++, you have at the very least hand-rolled makefiles, IDE solutions, cmake, and autotools as major candidates, and many major projects very happily choose "none of the above" (with all the pain that entails).
I once again don't understand why you think having fewer choices is better. In my build system adding a new dependency is as easy as specifying the library and version number in one file. It can be like that if you want. The fact that C doesn't couple the build system to the language means that I can pick any tool I feel like being appropriate for a project and use it. It might be nice if there was some default build tool for C which could be used for the most basic projects, but I would be severely unhappy if it was as thoroughly coupled to package management and the language as cargo is. As it stands, rust is basically unusable without cargo, that doesn't really make me want to use it as a language.
I also don't know of any projects which chose "none of the above". Unless you mean projects which use meson but I don't think you mean that since it just looks like you simply missed it off your list.
> * If you want to add a dependency in cargo, you add a line in Cargo.toml. That's it; you're done. For the aforementioned C/C++ build systems, even the happiest happy paths aren't that happy.
How do I depend on a specific C library from a cargo.toml? How do I declare a runtime dependency on grep or ps or whatever. How do I declare a compile-time dependency on python (because for example I want to run python to generate some code which gets built)? The fact that cargo.toml solves a tiny fraction of problems (at great cost) while ignoring the rest doesn't really make me particularly excited about it. In C I can pick or design a build system which suits the task at hand, if I need to depend on python, I can document it (specify it has to be in the PATH) and run it during the build. I can make this as flexible or inflexible as I need. I highly doubt that it's possible to pick any random possible executable and add in one line the information needed to document the dependency on that executable, use it in the right way and integrate that into the build process. cargo is inflexible, I agree that it's nice if your project is simple, the fact that it's so inherently tied to rust means that it's annoying at best if it is not adequate for the task at hand.
>This isn't to say that Rust's solution is perfect (it has a fair amount of warts), but to assert that Rust is no better than C is an indication to me that you've never really had to actually support the build system of a project before.
Rust's solution is adequate for a small subset of problems not completely overlapping with problems I like to solve. It is inadequate for what I want to do, aside from the number of other issues I have with rust, it's just not as flexible as what you can do with C. I have had to actually support the build system of a project before.
There is a certain amount of irony in asserting the superiority of not having a standard build system when you immediately go on to advocate simply not supporting anything that isn't compatible with your way of building things.
As a side note. Restricting yourself to rust and cargo, you're going far further than just discarding MSVC as a worthwhile target.
Because then you don't actually have to do anything at all. All the work is pushed onto the provider of the library. And they have less work too because 1. All their dependencies will "just work". 2. They only have to support one build system too!
There is ongoing work to integrate Cargo with build systems like Bazel, Buck, Meson, etc. Which is currently a weakness of Rust if you need to use those tools. However, note that this work is going into the tools themselves rather than every package author having to repeat the same work. That seems like a big win to me.
It's wishful thinking to suggest that all build related tasks can and will be doable with cargo. If cargo does not do exactly what I want, either it's impossible to do with cargo or is made more difficult by forcing me to use it.
>All the work is pushed onto the provider of the library.
What work is there to do for me as someone who depends on a C library? Ignoring weird edge cases like OpenMP, it's trivial to write in my documentation "Depends on SDL v2" and add 'sdl2' to my libs array or whatever particular build system detail there is.
As someone who isn't overly familiar with the C ecosystem: suppose I want to depend on SDL2, and I want my code to work across windows, macOS and linux. What does a minimal build config look like (in a build system of your choice)? And how (as a second developer or on a second machine) would I then use that config assuming it was committed as part of the codebase?
LDLIBS += $(shell pkg-config --libs sdl2)
CFLAGS += $(shell pkg-config --cflags sdl2)
prog: prog.o
Then you just run `make` or `gmake` (the developer should be aware of what his platform of choice prefers to call gnu make) and prog.c gets compiled to prog using the appropriate flags. I missed out some important details like setting the standard revision but I wanted to keep this short. This will work with gnu make, gcc and clang on linux, mac, any BSD, and windows (under wsl or msys or cygwin). Cross compilation is possible too. Personally I would use redo as the build system but this is shorter in make for such a simple project. More complex projects can be made to work with this basic pattern. When using autotools, cmake or meson you also have plenty of good options although I'm not entirely familiar with them as I don't use autotools, cmake or meson.Obviously this won't work if your machine is broken or if you don't have SDL2 installed in the correct place, but these are concerns for the package manager. The documentation simply needs to instruct you to install SDL2 (with development headers and the pkg-config definition). If you need a more advanced use-case such as you're trying to vendor the dependencies, you need to deal with PKG_CONFIG_PATH but I should stress that vendoring is NOT a normal use-case in the open-source-project-relying-on-sdl2 world.
On Windows/MSYS2, run pacman -S mingw-w64-SDL2. On macOS/Homebrew, run brew install sdl2. On Linux/DEB, run apt install libsdl2-dev. On Linux/RPM, run dnf install SDL2-devel.
Which is no fun, not to speak of problems when versions in system package managers are different.
Distro package managers are a far superior solution than having 5 different language package managers you have to keep in sync and then having to compile your own software.
First, you need to find out the package name. Run vcpkg search sdl2. It tells you that the package is called sdl2. It is trivial in this case, but if you wanted SDL 1.x, it could have been called sdl, or sdl1. vcpkg search will tell you it's sdl1.
Next, you install it by running vcpkg install sdl2. This will build from source. >1000 packages in the official repository should build cleanly, warning free, etc.
Installation tells you how you should use the package with CMake. It is two lines. find_package(SDL2), and target_link_libraries(main SDL2::SDL2).
To use CMake with vcpkg, copy and paste two lines to your CMakeLists.txt, and run CMake with CMAKE_TOOLCHAIN_FILE set to vcpkg.cmake.
Yes, C situation is bad, but it is not as bad as people think.
> C11 optional supports a thread model
I've been fighting for C99 support 15 years after its publication, so a merely decade-old C11 seems too new and radical. MSVC of course doesn't support C11 threads.
> How do I depend on a specific C library from a cargo.toml?
It is easier if you rewrite it in Rust ;)
But seriously, you make (or usually use an existing) Cargo package for it. Cargo calls them "-sys crates". They contain an arbitrary build script that figures out what combination of pkg-config, vcpkg, brew, or vendoring is the right one for a given snowflake C library. This may seem like overkill, but it really is helpful and often necessary if you target more than a Linux distro. pkg-config is utterly useless on Windows. Gives out brittle library paths on macOS. Non-existent on iOS. On every OS it gives invalid results for cross-compilation, unless user works around that in a platform-specific way. Some packages like libpng, llvm or macOS openssl may have non-working or incorrect pkg-config files on purpose.
Would your build script include workarounds for Homebrew? Emscripten? If you're not a user of these, would you even know you need them? In Cargo people collaborate and share their build script workarounds as dependencies too. It does look like duplicating work done by Linux distros, but it's done for all platforms, so you get the consistency you expect from a Linux distro everywhere. e.g. openssl-sys has this monster https://github.com/sfackler/rust-openssl/blob/master/openssl... or all the pains to configure ffmpeg build, e.g for cross-builds or static linking on macOS: https://github.com/meh/rust-ffmpeg-sys/blob/master/build.rs
gcc, clang, python and I imagine quite a few other programming languages are developed to this day without the ability to run unit tests against everyone's code and as far as I'm aware they're not breaking everyone's code on a regular basis so the point you make about this doesn't seem that important.
But most importantly, you can have package standards without the tight integration that cargo requires (see python setuptools and pip for an example of a decoupled package manager and a decoupled build system).
> I've been fighting for C99 support 15 years after its publication, so a merely decade-old C11 seems too new and radical. MSVC of course doesn't support C11 threads.
MSVC is not a viable C platform. C in MSVC is just an afterthought and you're at the whims of Microsoft as to whether they want to support all of C or not. Complaining that writing C code which also compiles against MSVC is hard is not productive when MSVC doesn't intend on actually supporting C further than what Microsoft feels they can absolutely get away with.
>It is easier if you rewrite it in Rust ;)
Yes, of course, let's rewrite everything in rust. Because rewriting software has zero chance of introducing bugs.
Moreover, let's rewrite our core libraries in rust so that they can get statically compiled into software in such a manner that if there's a security vulnerability (very likely given you've just rewritten it) you have to notify thousands of people to update their version pins and then fix all the resulting fallout (because everyone pins everything, nobody cares about strict backwards compatibility). This seems like hell compared to the current situation. Proponents of the highly restrictive rust/cargo model are always complaining about duplication of effort as an excuse for why any given package can have upwards of 100 dependencies, but they are perfectly content insisting that people should duplicate years of effort by rewriting everything in rust.
>pkg-config is utterly useless on Windows.
It's fine if you use msys or cygwin
>Gives out brittle library paths on macOS.
That sounds like a macOS bug.
>Non-existent on iOS.
People compile things on iOS?
>On every OS it gives invalid results for cross-compilation, unless user works around that in a platform-specific way.
There's work being put in place to make this better[0]. But I would say the current situation isn't that bad, it's just a matter of setting PKG_CONFIG_PATH. Most build systems for C can handle this properly (assuming they're correctly configured).
>Would your build script include workarounds for Homebrew?
What workarounds are required on my end?
>Emscripten?
I'm sorry but targeting javascript is not something I'm ever interested in. I am of the opinion that the less support there is for this the better.
>OpenSSL and ffmpeg.
The problem here is that I don't believe that it's worthwhile to statically compile against everything. On windows and other systems, there already exist perfectly normal ways of acquiring a copy of OpenSSL and ffmpeg. I don't need to compile either of these as part of my build process. If the end user wants to use a statically compiled version of these libraries, they're welcome to figure out how to do that on their system of choice.
In summary, I don't see how forcing everyone to statically compile everything and rewrite everything in rust is a major advantage over being able to chose where your libraries come from and chose to dynamically link against existing system libraries.
[0]: https://www.freedesktop.org/wiki/Software/pkg-config/CrossCo...
This is basically the issue he's complaining about. The author wants to reuse a wheel, but reusing the wheel in C is difficult. What are your options?
* OpenMP: Great, now you get the fun of figuring out how to cross-platform(/compiler) enable openmp in your build system (that you have to maintain yourself because there is no standard build system. Of course, you could also go with cmake or autotools to maintain the build system for you, but now you have similar issues with meta-build system stuff. Yay platform diversity!)
* Okay, let's use another library instead. Now you just have to have the user tell you where it's located. And maybe have to provide lengthy instructions on how to find it. Or you can try having the build system automatically download it on the fly. There's a lot of potential error messages you have to deal with, just to get at what ought to be a core feature of any modern programming language.
* Maybe you might want to vendor that library in your source tree, just to be sure you always have it. Now you get the fun of having your build system invoke another build system as a subproject.
* Screw other dependencies, let's just use a newer C standard version, since it is in C11. How do you get your C compiler to compile for C11? Oh wait, that's basically the same world of pain as it is to try to use OpenMP.
> Hardly any more dreadful than rust's solution to the problem.
Contrast this with Rust's solution.
* Your build system is cargo and you specify things primarily with Cargo.toml. This is as little work as the happiest happy paths of C/C++ build systems. And note you don't really choose build system; there is one ecosystem-approved build system--for C/C++, you have at the very least hand-rolled makefiles, IDE solutions, cmake, and autotools as major candidates, and many major projects very happily choose "none of the above" (with all the pain that entails).
* If you want to add a dependency in cargo, you add a line in Cargo.toml. That's it; you're done. For the aforementioned C/C++ build systems, even the happiest happy paths aren't that happy.
This isn't to say that Rust's solution is perfect (it has a fair amount of warts), but to assert that Rust is no better than C is an indication to me that you've never really had to actually support the build system of a project before.