Building hierarchical Makefile with GNU Make - makefile

I have a project divided in modules, each hosted in a directory, say:
root
|_module_A
|_module.cpp
|_Makefile
|_module_B
|_Makefile
|_main.c
|_Makefile
main.c depends on targets defined in Makefiles related to module_A and module_B.
I want to write my root/Makefile with respect to targets defined in Makefiles of both modules.
Now, I know that I could use the include directive, but the problem here is that targets and filenames in module_A and module_B aren't prepended with their directory, so I get something like this:
make: *** No rule to make target `module.o', needed by `main.c'. Stop.
There is a good way to solve this?
Thanks.

There are a couple of ways to do this, none of them perfect. The basic problem is that Make is good at using things there to make things here, but not the other way around.
You haven't said what the targets in module_B are; I'll be pessimistic and suppose that module_A and module_B both have targets called module (different source files, different recipes), so you really can't use include.
The biggest choice you have to make is whether to use recursive Make:
If you don't, then root/Makefile must know how to build module_A/module and module_B/module, so you'll simply have to put those rules in. Then you must either leave the redundant rules in the subdir makefiles (and run the risk that they'll drift out of agreement with the master makefile), or eliminate them, or have them call the master makefile recursively (which you wouldn't have to do very often, but it sure would look silly).
If you do, then root/Makefile will look something like this:
main: main.o module_A/module.o Module_B/module.o
...
main.o: main.c
...
%/module.o:
$(MAKE) -C $(#D) $(#F)
This will work well enough, but it will know nothing about dependencies within the subdirectories, so it will sometimes fail to rebuild an object that is out of date. You can make clean (recursively) beforehand every time, just to be on the safe side, crude but effective. Or force the %/module.o rule, which is less wasteful but a little more complicated. Or duplicate the dependency information in root/Makefile, which is tedious and untidy.
It's just a question of your priorities.

Can't you write the makefile in a non-recursive way?
Recursive Make Considered Harmful

Related

Makefile - does using .PHONY for running commands was intended when creating make?

I know we can set up commands such as all, clean, install etc in makefile and use .PHONY to tell make they're not associated with files.
But I was wondering - when creating make and makefile - was this kind of use (to run such commands) combined with .PHONY designed for that purpose? Or maybe .PHONY was added later to easily extend make to support those kind of commands?
I also read this but there wasn't anything else there except the regular known usage.
Thanks!
I do not know the history of GNU make.
The use of .PHONY is exactly what you suspect: have targets (which can thus be goals, or commands, if you wish) that are not files, and that work even if, by accident, a file with the same name exists. It is one single and clearly defined purpose.
In certain cases you want to force a target file to be re-built even if it is up-to-date, and you can declare it a prerequisite of .PHONY for this purpose, but it is frequently the sign that your makefile is not what it should be.
Another frequent situation is the grouping of several targets (real or phony) as prerequisites of one single other phony target.
But in both cases, we can say that the resulting phony target is a kind of command. In the first case it is a command that forces the build of a file. In the second it is a kind of alias for a series of actions.

Makefile: how to detect changes within the makefile itself?

I'm aware of the idea of using recursive makefiles. Will the subsequent makefiles such as the following be called be updated solely on any changes to the subsequent makefiles themselves?
e.g.:
#parent makefile. no changes here.
subsystem:
cd subdir && $(MAKE)
If the makefile within subdir was changed such that the following does not hold (e.g. only a gcc flag was changed), then will the object files be updated?
The recompilation must be done if the source file, or any of the
header files named as dependencies, is more recent than the object
file, or if the object file does not exist.
The only reason that, as written, make even runs that rule at all is because subsystem and subdir do not match.
If a subsystem file or directory were ever to be created in that directory that rule would cease to function.
If .PHONY: subsystem1 were added that problem would be fixed and that rule would always be run when listed on the command line (i.e. make subsystem). (As indicated in the comments .PHONY is a GNU Make extension. The section following the linked section discusses a portable alternative. Though it is worth noting that they are not completely identical in that .PHONY has some extra benefits and some extra limitations.)
In neither of those cases is the subsystem target paying any attention to modification dates of anything (as it lists no prerequisites).
To have a target depend on changes to a makefile you need to list the makefile(s) as prerequisites like anything else (i.e. subsystem: subdir/Makefile). Listing it as .PHONY is likely more correct and more what you want.
No, nothing in make itself tracks non-prerequisites. So flag changes/etc. do not trigger rebuilds. There are ways to make that work for make however (they involve storing the used flags in files that themselves are prerequisites of the targets that use those flags, etc.). There are questions and answers on SO about doing that (I don't have them ready offhand though).
Other tools do handle flag changes automatically however. I believe Electric Cloud's tools do this. I believe CMake does as well. There might also be others.
Recursive makefiles are executed whether or not anything changed. This is exactly one of the objections pointed out by Paul Miller in his Recursive make considered harmful paper from almost 20 years ago.
With that said, a makefile is just like any other dependency and can be added to a production rule to trigger that rule if the makefile is altered.
You can include the makefile as a dependency, the same as any other file:
mytarget.o: mytarget.c Makefile

Writing a Makefile to be includable by other Makefiles

Background
I have a (large) project A and a (large) project B, such that A depends on B.
I would like to have two separate makefiles -- one for project A and one for project B -- for performance and maintainability.
Based on the comments to an earlier question, I have decided to entirely rewrite B's makefile such that A's makefile can include it. This will avoid the evils of recursive make: allow parallelism, not remake unnecessarily, improve performance, etc.
Current solution
I can find the directory of the currently executing makefile by including at the top (before any other includes).
TOP := $(dir $(lastword $(MAKEFILE_LIST)))
I am writing each target as
$(TOP)/some-target: $(TOP)/some-src
and making changes to any necessary shell commands, e.g. find dir to find $(TOP)/dir.
While this solves the problems it has a couple disadvantages:
Targets and rules are longer and a little less readable. (This is likely unavoidable. Modularity has a price).
Using gcc -M to auto-generate dependencies requires post-processing to add $(TOP) everywhere.
Is this the usual way to write makefiles that can be included by others?
If by "usual" you mean, "most common", then the answer is "no". The most common thing people do, is to improvise some changes to the includee so the names do not clash with the includer.
What you did, however, is "good design".
In fact, I take your design even futher.
I compute a stack of directories, if the inclusion is recursive, you need to keep the current directories on a stack as you parse the makefile tree. $D is the current directory - shorter for people to type than $(TOP)/,
and I prepend everything in the includee, with $D/, so you have variables:
$D/FOOBAR :=
and phony targets:
$D/phony:

Makefile include makefile from different directory

I have two makefiles, directoryA/Makefile and directoryB/Makefile.
directoryA/Makefile depends on targets in a rather large and complex directoryB/Makefile.
I could do a recursive make
$(MAKE) -C directoryB
But that is undesirable for several reasons. Two big reasons: I make have to execute the makefile several times, and make can't correctly know when rebuilding a target is necessary.
I would like to use the include directive. The problem is twofold:
The targets in directoryB/Makefile are all defined relative to that Makefile.
Many commands depend on the working directory being directoryB.
Recursive make solves both of these problems, but with big disadvantages (mentioned earlier). Is there a way to solve both problems when using include?
It's hard to say without seeing directoryA/Makefile, but another alternative is to have it include directoryB/Makefile, then
cd directoryB
make -f ../directoryA/Makefile

Compilation order in make rule

I have a compilation rule as follows,
$(compiled_objs) : $(obj_dir)/%.o: $(src_base)/%.cpp
It creates .o dso objects from specific .cpp files in src_base and works fine.
Question:
My question is that is there a way in gnu Makefiles to sort the order in which %.cpp files are processed. For example, in each src_base, i have a file called xxxLast.cpp and i want to create the object for *Last.cpp after all other .cpp files from $src_dir directory has already been processed.
Use-Case:
My use case is not common but i want to embedd the md5sum of all other .o objects in xxxLast.cpp file for which i can add additional handling in my rule.
I haven't done much work on makefiles. Help will be highly appreciated and thanks in advance.
Somewhere else in your makefile you'll have a target that depends on $(compiled_objs):
all_objects: $(compiled_objs)
Make will build the prerequisites of any target in the order they are listed. So if you want a particular object to be built last, then you just put it at the end of the compiled_objs macro.
Be aware that during parallel builds make will still walk the commands to run in the same order, BUT due to parallelization effects they may actually run in a different order, or at least you can't know that the last one will be started after all the previous ones have completed.
For your situation I really don't recommend just stucking the last object at the end and hoping. You should define this relationship explicitly using make rules, so:
all_objects: xxxLast.o
xxxLast.o: $(compiled_objs-but-not-xxxLast.o)
...generate md5sums for $^...
xxxLast.o $(compiled_objs-but-not-xxxLast.o): $(obj_dir)/%.o: $(src_base)/%.cpp

Resources