Hacker News new | ask | show | jobs
by klodolph 1093 days ago
I’m not sure what you mean by specifying dependencies while the target is executing. The thorn here is generated headers—if they aren’t built before your compiler runs, then you are stuck.

> Not really; you can't know what compilation unit will contain a function in C and C++. Well, you could try to hack it with grep or something, but I consider that less desirable.

This is solvable and has in fact been solved. You use a function in your C++ file, the analysis system knows which header contains that function declaration, and the build system knows which library must be linked in for the header file. This is basically how it works in Go, with some extra steps to associate headers with libraries. But all the pieces are there—you do not need grep, if you want to implement a similar system yourself.

The catch here is that these systems are a bit inexact—any given function could be supplied by more than one library, and the header files may require some specific ordering to work correctly. The solution is to store the dependencies in the build scripts, rather than try and figure them out from sources each time. The general problem, of figuring out the correct headers and libraries necessary to compile a given piece of C++ code, is just too much of a pain in the ass to make it completely automatic—you want a human in the loop. It’s not just a problem with exactness, you also have multiple configurations with their own preprocessor flags, you have dependencies which are specified indirectly but which should be direct (how do you detect that?)

The ecosystem, such as it is, is a chaotic mixture of tools used interactively during development or non-interactively during the build. One of the super useful properties of Bazel build files is that you can modify them programmatically, using a tool called Buildozer. This can be used for things like automatic refactoring of your build system, and it can also be used to make automatic changes to the build system as you edit source code. Part of the “sauce” that makes it work is the way rules are rigidly defined in build scripts. As you make build scripts more complicated, it gets harder and harder for the tooling to keep up—and often, that means more manual work to keep everything set up right.

1 comments

> I’m not sure what you mean by specifying dependencies while the target is executing. The thorn here is generated headers—if they aren’t built before your compiler runs, then you are stuck.

I understand why you think this. It took me a while to understand dynamic dependencies too.

But that is actually not the case. A target that may have dynamic dependencies will run and figure out the required headers or figure out the required imports. At that point, it tells the build system that it needs those dependencies, but if those dependencies are already up-to-date, it can be considered up-to-date too.

The build system checks those dependencies, finds that they are up-to-date and marks the first target done and doesn't finish running it.

> The general problem, of figuring out the correct headers and libraries necessary to compile a given piece of C++ code, is just too much of a pain...to make it completely automatic—you want a human in the loop.

This is what I meant. Sure, you can make good assumptions (and in my monorepo, functions are arranged in specific ways in files, so I could do that), but it's not generalizable.

> One of the super useful properties of Bazel build files is that you can modify them programmatically, using a tool called Buildozer. This can be used for things like automatic refactoring of your build system, and it can also be used to make automatic changes to the build system as you edit source code.

This is really cool. It shows that Bazel is just a different model, and in the eyes of many people, better than mine. That's okay! I'm sure that plenty of people would prefer Bazel's model, including yourself. That's great! Diversity of build systems is good.