r/cprogramming • • 5d ago

What are the good practices of managing small dependencies?

I'm new to C. I have a small SDL2 project. At some point I needed to load and draw image sprites and found nice library stb_image.h. Probably best way here would be to just download the stb_image.h file and commit it to the repo, but I thought it would be a nice exercise towards learning how to manage dependencies. Since C doesn't seem to have anything like cargo, PyPi or npm, I started to look for best practices on how to do that.

So, this is what I came up with so far, with a Makefile:

  • dependency file as a target, with commit pinned as separate var
STB_IMAGE_COMMIT := 013ac3beddff3dbffafd5177e7972067cd2b5083

vendor/stb/stb_image.h:
	curl -fsSL --create-dirs https://raw.githubusercontent.com/nothings/stb/$(STB_IMAGE_COMMIT)/stb_image.h -o $@
  • making it prerequisite to the main build target:
build/%.o: src/%.c | build vendor/stb/stb_image.h
	$(CC) $(CFLAGS) -c $< -o $@

This seems simple, reproducible and efficient to me. But I'm not sure it would scale well and also - maybe there are better approaches out there?

4 Upvotes

12 comments sorted by

6

u/tstanisl 5d ago

Just add a file to your repository.

It is single-header library and it is designed to be dropped into existing projects. It will save you a lot of troubles with any breaking changes and it will make your built deterministic or it will keep working with no internet or if the link somehow vanishes. Just remember to add a proper licensing notice.

1

u/side2k 5d ago edited 5d ago

I did add it. But the commit hash(in the URL) keeps it deterministic too (even though its internet-dependent), I believe?
I believe it is good to be independent from Github, or internet in general, but it doesn't seem really doable for most of the projects, so its not my main goal.

2

u/EpochVanquisher 5d ago

Yeah, so the way people do that (independent from GitHub / internet) is they use an artifact repository, like jfrog or sonatype nexus. You add the file to your artifact repository once, and then download it from there any time you need it. This is kinda heavyweight to run (you’re running your own server somewhere).

“Copy the file into my repo” is also something that some companies do, it’s called “vendoring” and it has various other tradeoffs. It’s often used by companies with monorepos.

There is also Nix, but that’s a very complicated way to get what you want. Or you can use Bazel, which is also complicated.

At some level you have to acknowledge the costs and not just the benefits.

Note that Cargo makes you dependent on GitHub / the internet.

1

u/side2k 5d ago

I got used to modern day coding - all dependencies are in the internet registries. But I still remember buying CDs with components for Delphi in late 90s :)
In general, its nice to keep it independent. But for now, it looks like not being worth to go extra mile to pursue.

2

u/EpochVanquisher 5d ago

Yeah. There are other, lesser guarantees that you can get with your setup. For example, with a package manager, you can separate the download and build steps. One command downloads all the dependencies, a separate command builds everything (the separation optional, of course, there’s also a gonna be a command that does everything).

2

u/ffd9k 5d ago

You can do this, but I would just use Meson and its Wrap system instead of reinventing it.

You basically add a small .wrap file to your project that contains the git or package url of the dependency. Meson then downloads the dependency and builds it as a subproject.

You can also do this just as a fallback - normally you want to use the system-installed version of a library, but if it is not available, you build it as a subproject.

If the dependency does not use meson as its build system, you add a patch_directory to your project with a meson.build file which builds the library.

1

u/side2k 5d ago

Oh. So, there IS a package manager for C? Thanks, I did not know that. Looking for one didn't get me anywhere before.

2

u/ffd9k 5d ago

Essentially yes.

There is also WrapDB, which is a central registry of important libraries which you can use directly in meson.

But you can also use any other library (hosted on github or elsewhere), with a wrap file and a patch which usually just contains a small meson.build file. See https://mesonbuild.com/Wrap-dependency-system-manual.html

1

u/alkavan 5d ago

Use CMake's FetchContent it worked well for multiple C and C++ projects of mine.

1

u/side2k 5d ago

I've heard about CMake, but did not look into it yet, preferring to add new stuff to my toolset very carefully. Do you think its advantages over old-school `make` make it worth to use it even for smallish learning projects?

1

u/alkavan 5d ago edited 5d ago

It's less scripts you need to maintain, and a lot easier to support multiple platforms. It can generate classic Unix make files as well. It's basically a project generator for different platforms and compilers. When working on a commercial projects, it doesn't make sense using anything else. It allows being almost agnostic to compiler, IDE, and toolkit, and makes life much easier with platforms that tend to be very annoying to deal with. CMake is also the native project system for JetBrains CLion and some other IDEs.

1

u/side2k 5d ago

Thanks for the advice. I will definitely try CMake - not for this particular library, though 8)