Plus many of the implementations are closed source.
C also tries to make as few assumptions about the platform as possible. It was only recently that support for ones-complement arithmetic was dropped http://www.open-std.org/jtc1/sc22/wg14/www/docs/n2218.htm / despite never being used for decades https://stackoverflow.com/questions/12276957/are-there-any-n...
C is also somewhat unique in supporting cross-targeting, where you build a program on platform A that will run on platform B (such as a microcontroller), and the target platform may be very different (such as no filesystem, no OS, different instruction set). Most other modern languages use intermediate bytecodes and try to be "WORA" (write once run anywhere).
That sounds like a distro.
C has a terrible dependency management story.
When it became commonplace to depend on third party or system libraries, because of the special status C enjoys in the software infrastructure, OS and distro developers bend over backwards to accomodate C linkage, providing pre-packaged dynamic linking objects.
The status quo is actually rather unfortunate. Several package management tools exist to attempt to address the issue, but there is no de-facto standard and every project makes its own choice.
> The C programming language predates the time when using third party libraries was common.
The English language predates spaceflight, but has adapted. PHP predates proper security practices, but has adapted. Javascript predates the Document Object Model but has adapted.For that matter, Javascript also predates the time when using third party libraries was common.
C had such a powerful influence on computing that it didn't need to adapt to the world, the world adapted to C.
Of course, there is more going on with C than the use of new "words" introduced through libraries. Even though it is viewed as a portable language, most of that portability is between hardware architectures and operating systems of the same class. Almost everything above that is handled by vendor specific libraries so there was likely less of a drive to standardize on library management. This has negative consequences in today's environment, but it would have taken foresight to predict that outcome.
And you just install packages from your linux distro repository. If you want to distribute your own code, rhen you make a package out of it and that will handle your dependancies for you.
- you are not using Linux
- you need a newer version of the library than your distro provides
- you want to use a package that is not popular enough to be packaged by your distro
2. cmake and autoconf will both let you list dependencies and provide error messages to tell users to install them. It won't auto-install dependencies like a package.json or tell a user where to get the dependencies, but the message that something needs to be installed can be passed to the user.
Via a Dockerfile? ;)
But seriously, this is typically done in the INSTALL.txt installation instruction file for your library, and it’s up to the user to make sure any dependencies are properly installed and configured.
For dependencies that aren’t commonly installed, tools will often include their source and build them from scratch.
It’s a misnomer to say C package management is broken, because it doesn’t have any.
For executables: actual real statically linked binary. You have a single executable containing all the dependencies and all necessary runtime support.
For libraries: no standard mechanism. On Linux, you can generally make a "libfoo" package that specifies the dependencies. On Windows, this is a bit more of a problem, but people can and do just hand out a DLL.
Or you make people build the dependencies from source as well.
(As an old school C developer, I see containers as occupying the same conceptual space as statically linked binaries for distribution. It's just that very few other languages will let you make a true standalone executable.)
https://cmake.org/cmake/help/latest/command/find_package.htm...
There is a bit of learning curve but everything is there for a reason.
- Toolchain files define your target system and build toolchain.
- Hunter specifies your dependencies withing CMake (using git and hashes) and does proper incapsulation/namespacing (which isn't present in "normal" CMake) as well as setting up the proper "inheritance" of headers and stuff.
- CMake does the final linking of build artifacts and headers and does the final build command generation.
A proper setup can build both dynamic and static builds for any system with virtually no platform specific switches
For instance, what is this if not package management?
apt install libpng-dev
This is the problem that npm, pip, and whatnot solve. They work the same, (almost) everywhere.
C/C++ don’t have a proper answer for it.
And then there is specifying linking files and include files, and headers, it makes it so challenging to even run other people’s code
- a plethora of details that are implementation specific; - different compilers and platforms that differ in important details;
- as a consequence of above, most code ends up targeting some limited set is compilers and platforms;
- no standard build system to build package management on top of
- the header file system, which allows api declaration and implementation to be decoupled
By the time you’ve narrowed down enough to make these tractable, you might as well use a given operating system’s package management tools as your “C package manager”.
You're also ignoring the fact that Linux isn't the only operating system in the world. Some of us write multi-platform software, and package managers make it so much easier.
The mere fact that I could not build the project on my local machine (windows) and have to login to a Unix (IBM AIX) was baffling. Yep, Unix, not even linux. And because it was a business critical program which worked, business refused to make any drastic changes to it unless they were absolutely necessary, even if it was a bug fix to a frequently occurring issue.
And I maintained it till 2018 when I left that job. I am pretty sure it is still there in same state.
So, I understand the build tool woes, and cross platform issues. however, I understand it to mean that package manager is not feasible in current environment, not that it does not add value. Recently there was a post on zig, which aims for providing a standard build tool for C (& zig), and later, a package manager for C (and zig)
So, I think such efforts are not only beneficial, but will become reality in future
Package: tarball of code.
Manage: download tarball of code and build/install libraries using configure/make. Then tell the compiler where the headers are and the linker where the libraries are. Easy peasy!
(OK, we know it’s not quite _that_ easy in many cases.)
The elephant in the room is that people on HN and elsewhere violently nod about the need for a “proper package manager” when they are actually in disagreement about what a “proper package manager” needs to do. Meanwhile, it turns out that you can do without one, as the C community and all the “non-proper package managers” out there amply demonstrate.
C targets an incredibly broad spectrum of hardware and software platforms, compiler implementations, and OS layouts. That's both blessing and a curse. I think it would be worth at least trying to converge on a "package" system, even if it takes the next 20 years.
I believe that is changing. A quite large number of influential projects have standardized on Meson. That includes systemd, Xorg, PipeWire, GLib, libui, hexchat, pacman, and (almost) all Gnome projects. It seems fair to say that most people (a) like it, and (b) see it as the most likely path forward for build systems.
With complex software, just grabbing the latest version of every dependency will lead to broken builds (or ones that only work on x64 but are broken on other platforms)
Last I looked at GUIX (which I guess is similar) you are tied to a release "version" of the package-manager with whatever package version it comes with. But maybe I misunderstood
NixOS (the distribution) is not actually rolling: it has stable channels that are released twice a year, and an "unstable" channel that normally updates every few days (depends when all tests passed). You could call the latter "rolling", but it's a bit different compared to actually rolling distributions like Arch Linux.
> Can you designate specific package versions for all your dependencies? Like having a very new openssl and an older zlib version?
Normally only the last version is packaged, but you can change the version of the package and install multiple versions of the same library without conflicts. Some big libraries have multiple branches for backward compatibility (eg. boost175, boost174, openssl_1_1), for the others you have to override the package (it's pretty easy, but you'll likely lose the binary cache).
> With complex software, just grabbing the latest version of every dependency will lead to broken builds (or ones that only work on x64 but are broken on other platforms)
If you really need that kind of stability, a common solution is to pin the version of the whole Nixpkgs. Something a little less drastic is simply using the stable channel, which only receives security updates.
> Last I looked at GUIX (which I guess is similar) you are tied to a release "version" of the package-manager with whatever package version it comes with. But maybe I misunderstood
I'm not familiar with Guix, but I think you can do what I described above with it too.
I mixed things up a bit. Guix doesn't have a stable channel but Nix does. Twice a year sounds quite fast - but maybe that's just what I'm used to from Ubuntu LTS. I can't imagine being a package maintainer and having to deal with build issues that often (I guess it only targets x64 so it's not too bad)
> If you really need that kind of stability
I guess I don't really 'need' it .. but I just rather my system not be constantly updating and changing under me.
Looks like you have run stable and add unstable packages as needed:
https://discourse.nixos.org/t/installing-only-a-single-packa...
(like running LTS and adding PPAs)
Guix has Inferiors [0] for this nowadays.
There's also a lighter-weight but less robust approach: define a new package inheriting the package in question, changing only the version number and source URI.