Hacker News new | ask | show | jobs
by claudius 2632 days ago
What I miss about these tools is some "relatively" straightforward dependency detection and generation.

That is, I have a bunch of .cpp files which need to be compiled into individual executables in a folder bin/. I also have a folder inc/ which contains some headers (.h) and those headers possibly also have some associated TU (.cpp).

Now g++ can already generate a dependency graph of headers for an executable. It is then (with a standard Makefile and some supporting bash scripts) quite straightforward to mangle that graph into a list of translation units (namely those files whose name matches a depended-on header) which must be compiled and linked into the executable.

That is, I can simply create a new "executable file" .cpp file in bin/, include some headers therein and when I say make, the Makefile automagically figures out which (internal) dependencies it needs to link in when linking the executable.

Now that I have these "relatively straightforward" scripts and the corresponding Makefile, the incentive to move to another (nicer) build system which would require me to rebuild this infrastructure to fit into this other build system's view of the world is quite low – unless there is some way to do this directly?

Xmake as shown here (and also Meson linked in a sister comment) appear to still require manual selection of dependencies.

5 comments

> Now g++ can already generate a dependency graph of headers for an executable.

Actually, it cannot; and this should be well known. It emits in practice less than half of the information that it knows from path lookup, that a build system really needs to know.

* https://news.ycombinator.com/item?id=15060146

* https://news.ycombinator.com/item?id=15044438

Xmake can only simplify the maintenance management of dependencies, improve the usability and maintainability, but can not fully realize the way you say
Your workflow is one way to build and link, but not the only way. I might want to build several of those TUs into a static library, link against it, and ship it alongside a few executables.

And when it comes to creating a library, it's difficult to infer which TUs should be pulled in or left out since you'd need to see at least representative samples of the use of that library to be able to infer that.

How would a tool know from a header dependency, in which source file the implementation for the header lives? C or C++ don't require any relationship between a declaration file and implementation file. The implementation could be in an entirely differently named source file, or spread over various files, mixed with implementation code from other headers, or included right in the header.
In the common 2 step generation model of C and C++, this information is not needed. When generating object files it is not relevant which .c/.cpp file corresponds to which header files, because they are no inputs to that. Linkong has to happen when any object file changed.
Generating automatically the dependencies is trivial with gcc and GNU make, if you just take care to group adequately in directories and subdirectories.

I.e. you just have to put all the source files from which you generate object files that will go in the same libray in a set of directories which does not contain files that will not go there.

Similarly, all the source files for the object files required for an executable, except those that are in libraries, should be in a set of directories.

The sets of directories need not be disjoint, just a given set must not contain files that must be excluded for linking a certain target, as that will make the building process more complex.

Given this constraints, it is possible to write a universal GNU makefile usable for any project, which will generate automatically all dependencies.

For any executable or library you want to build, it is enough to write an extremely small makefile, containing 4 lists (of defined symbols, of source files, of directories with header files and of libraries) and the name of the generated file and its type (excutable, shared library, static library).

At the end you need to include a makefile that is good for any project targetting a certain CPU + operating system combination.

The makefiles per CPU/OS must define a few things, e.g. the compiler used and other utilities, option flags for all, locations of the tools and so on, then you include a unique makefile for all architectures and operating systems.

I have started using this method more than twenty years ago and I have never ever needed to write manually any dependency information.

Whenever I see the huge and intricate and impossible to maintain makefiles that are too frequently encountered in many software projects, I wonder how one is willing to waste so much time with a non-essential part of the project.

From my point of view, building easily any large software project is a problem solved a long time ago by gcc & GNU make, but for reasons that I cannot understand most people choose to not do it in the right way.

Of course having to use in 2019 a programming language which does not implement modules by any better method than including header files is something even more difficult to understand, but I still must use C/C++ in my work, as there is no alternative for most embedded computers.

Sorry, there are several typos in my message above. For most of them it is obvious which was the correct intended word.

However, one typo can lead to a confusion because an entire word is missing. In the 4 lists that must be written in the makefile, the most important list, as the other lists can be omitted, is the list of directories with source files (not a list of source files).

For simple projects the list will be reduced to a single source directory. Whenever you add, delete or rename source files, there is no need to edit the makefile of the project.

All changes can be taken automatically into account by GNU make, which can be instructed to scan the source directories for source files for all the programming languages that you use.

There's no way to achieve that with today's standard C++ as it requires metadata to access/infer package version numbers.

This will hopefully change with the introduction of C++ modules in C++20 but until them the best option available to C++ programmers is either manually managing third-party libraries or employing dependency management tools such as Conan.

This is for internal dependencies of a project and indeed outside the scope of the standard which does not say that if a function is declared in file abc.h then it is defined in file abc.cpp and that this file is compiled into an object file abc.o which then must be linked during linking of any file which includes abc.h.

However, just because it is (like most build system questions) outside the scope of the standard does not mean that it isn’t possible to define some project-internal rules about what gets compiled and linked into what and that the build system cannot apply those rules to take work away from users.

The few external dependencies my project has are installed semiautomatically before any compilation starts.