how do makefile dependencies work? - makefile

I am currently confused as to how makefile targets work. I have a current understanding, and I don't know if it is correct because the tutorials I've been reading aren't very clear to me. Here is my current understanding
when you run 'make' in the terminal, the makefile utility finds the first target in the makefile and tries to run it, but before doing so it looks at all of the dependencies in the file
(this is where I start getting confused): If the dependency is a target in the makefile, but does not exist as a file in the makefile's directory, make simply runs the target. If the dependency is a file name, but not a target in the makefile, the utility checks for the existance of the file, and if the file doesn't exist, the utility yells at you. If the dependency is a file that exists in the directory AND a target, the target is run provided that any of the files that the file-target depend on are newer than the associated file.
Do I have it down right? Is it simpler than I'm making it out to be?

You have it right, more or less, but it can be stated a little more clearly. You're right about how make chooses the initial target, except of course if the user specifies a specific target on the make command line then that one is used instead of the first one.
Then make basically implements a recursive algorithm for each target, that works like this:
Find a rule to build that target. If there is no rule to build the target, make fails.
For each prerequisite of the target, run this algorithm with that prerequisite as the target.
If either the target does not exist, or if any prerequisite's modification time is newer than the target's modification time, run the recipe associated with the target. If the recipe fails, (usually) make fails.
That's it! Of course, this hides a number of complex issues: in particular item #1 (finding a rule) can be complex in situations where you have no implicit rule for the target. Also behaviors such as what to do when a rule fails can be modified.
But that's the basic algorithm!

for the question you asked your understanding is correct !!
If you are still confused have a look at this :: http://www.jfranken.de/homepages/johannes/vortraege/make_inhalt.en.html
once comfortable move to other more complete manuals like the GNU manual for make.

Related

How does sconstruct receive input?

I want to use Sconstruct instead of Makefile. But I found that many situations in the Makefile are difficult to implement in Sconstruct.
For example,
I have three .c files, a.c, b.c, and c.c. I want to decide which file to compile into the final file based on the input. In Makefile I can use make a, make b, make c to achieve. But in sconstruct, scons e decides which statement to execute based on the final target file. Is there a way to decide which statement to execute based on the source file or a lable?
Sometimes in the Makefile, I want to compile multiple files at once, but not all files. In the Makefile, I can write the compilation of multiple files under one label. Is there such a method in Sconstruct?
I found a lot of documents about sconstruct on the Internet, but basically every document introduces the most basic commands.
Possibly not understanding all of the question, but SCons, builds the targets you ask it to on the command line, like Make does. If you don't give it any, it builds the default targets, which you yourself can define through Default() calls. If neither, then it builds ".", which means all of the targets discovered underneath the directory of the SConstruct.
Targets don't have to be the name of a file to build, you can use the Alias() function to assign a name that will work as a build target. An alias can refer to several targets if you wish, which seems to be your second question.
Feel free to hop onto the SCons Discord channel if you want to chat more interactively (see https://scons.org/contact.html for links)

Using Makefile variable before definition in included file

I've encountered a little problem during my Makefile learning adventure.
I have prepared two scripts. The first one is the main Makefile:
all:
#echo $(COMPONENTNAME)
include ~/Projects/tests/mk
And here's the mk file
COMPONENTNAME:=ISeeYou
Now, when I run
make all
I get
ISeeYou
I am not sure why... I was convinced that a variable must be defined before it is used. And the include is after COMPONENTNAME is being used. My suspicion is that all of the includes are done first, and then the target creation is started. Is that the case?
My suspicion is that all of the includes are done first, and then the target creation is started.
Yes, you are right.
All global sections in Makefile should be completely parsed before make is able to decide, which targets(and in which order) should be built. Target's recipes are evaluated only when it is needed to build target. This is explicitely stated in make manual:
The other way in which make processes recipes is by expanding any variable references in them. This occurs after make has finished reading all the makefiles and the target is determined to be out of date; so, the recipes for targets which are not rebuilt are never expanded.

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

Makefile generator creates two files

I have a generator program that creates two version files, say ver.h and ver.cpp. My ultimate build target depends on both of these files, and the rule for building both is that one program. If I did this:
build : ver.h ver.cpp
ver.h ver.cpp :
./gen/version/program
then a parallel build could run program twice, which, while not bad is just excessive. I figure I could have them both depend on a phony target:
ver.h ver.cpp : do-version-impl
do-version-impl:
./gen/version/program
.PHONY : do-version-impl
Is that the best way to do this? It smells a little funny to have to introduce a phony rule to do this.
Using the phony target as the prerequisite is a bad idea. program will be run even if ver.* files exist, which is a false positive error.
More subtly, GNU Make is only guaranteed to update its file timestamp, if that file is a target of a rule with a recipe. So here, even though program is always run, anything that in turn depends on ver.* files might not get updated at all!
In my opinion it is best to not make up unnatural patterns for each target, but instead, go explicit:
There is a "main" file that you are generating, that is ver.cpp. Use the "no-op" recipe ; for the other one, which can be put on the same line like this:
ver.h: ver.cpp ;
ver.cpp: Makefile
./gen/version/program
This method starts with what you wrote, but adds the very important ;.
If you did not have a natural candidate for the "main" file, then in my opinion it is best to use a "sentinel":
ver.h ver.cpp: sentinel ;
sentinel: Makefile
./gen/version/program
touch $#
Again, this method is similar to one of your methods, but very importantly, does not use a phony, but a real file.
See 10.5.1 Introduction to Pattern Rules specifically the last paragraph:
10.5.1 Introduction to Pattern Rules
...
A pattern rule need not have any prerequisites that contain ‘%’, or in fact any prerequisites at all. Such a rule is effectively a general wildcard. It provides a way to make any file that matches the target pattern. See Last Resort.
...
Pattern rules may have more than one target. Unlike normal rules, this does not act as many different rules with the same prerequisites and recipe. If a pattern rule has multiple targets, make knows that the rule’s recipe is responsible for making all of the targets. The recipe is executed only once to make all the targets. When searching for a pattern rule to match a target, the target patterns of a rule other than the one that matches the target in need of a rule are incidental: make worries only about giving a recipe and prerequisites to the file presently in question. However, when this file’s recipe is run, the other targets are marked as having been updated themselves.
So you could use something like this:
v%r.h v%r.cpp:
./gen/version/program
I believe you need that odd patterning to have make consider the pattern to match (I don't believe it will match % against an empty string as ver%.h ver%.cpp would need). (I can't find reference to this in the manual at the moment though.)

Multiple instances of the same target for make

I am working on a Makefile written by someone else in my lab and I see the following:
include /path/to/Makefile.inc
TARGET_A: pre_req_1 pre_req_2
cmd_1;
cmd_2;
...
When I look at /path/to/Makefile.inc I see that it also includes a target TARGET_A
with other pre-requisites and recipes.
Is this a normal practice? (and would it work?) Would make treat the two rules separately? Can we safely make any assumptions about which one is considered first?
It is a dangerous practice since it is confusing to know which one is applied.
The "include" will act has if the included file content was in the Makefile, and the targets will be overrided while reading the Makefile. So, the last target will be honored, and the first one (in the included file) will be ignored.

Resources