GNU Make: Automatically Prerequisites can't work if rename header files - gnu-make

A common Makefile for automatically prereq, looks like:
SRCS := $(wildcard *.c)
OBJS := $(SRCS:%.c=%.o)
DEPS := $(OBJS:%.o=%.d)
$(OBJS): %.o: %.c
$(CC) $(CFLAGS) -c -o $# $<
include $(DEPS)
$(DEPS): %.d: %.c
xxx
the first time, build ok, the generated .d file like this:
config.o config.d: config.c config.h
then I rename config.h to config2.h, and modify config.c:
-#include "config.h"
+#include "config2.h"
make again, Makefile generate error:
make[1]: *** No rule to make target 'config.h', needed by 'config.d'
because config.d depends config.h, How can I modify my Makefile to fix this rename problem.

Pretty simple really. Your .d file needs this additional line:
config.h:
Now when make discovers config.h doesn't exist,
it will run the non-existent recipe and happily believe it has created config.h. Then it carries on.
The manual says:
If a rule has no prerequisites or recipe, and the target of the rule is a nonexistent file, then make imagines this target to have been updated whenever its rule is run.
How to we get this extra line?
Back in the day you would run a perl one-liner over the newly created .d file. Nowadays, for modern gcc variants, just add -MP to the compiler command-line.
-MP This option instructs CPP to add a phony target for each dependency other than the main file, causing each to depend on nothing. These dummy rules work around errors make gives if you remove header files without updating the Makefile to match.
Job's a good 'un.

Related

How would I create a Makefile that remotely updates itself?

I have a makefile that I've changed up a bit here to look more generalized
.PHONY:
bash hash.sh
all: .PHONY lab tests.zip
lab: .PHONY lab.cpp
g++ -o lab lab.cpp -g -Wall -std=c++11
tests.zip:
curl -L -O https://url/tests.zip
unzip tests.zip
tests: .PHONY lab tests.zip
bash scripts/test.bash lab.cpp
clean:
rm -rf scripts tests bitset-tests.zip
I am a TA for an entry level computer science course at my university, and I've created a makefile here for my students to use to compile and test their code seamlessly.
One thing I want to do though is to have the makefile update itself every time the remote repository has a new version of it. I know I could just have them update the file themselves, but my job is to make the students focus less on setting things up and more on just coding for now, since it's entry level. So for the purposes of this, I'm sticking with the idea I have.
Currently, I'm achieving this with a script hash.sh which fetches a hash of the makefile from the repo, and compares it to a hash of the makefile in the student's directory. If the hashes don't match, then the updated makefile is fetched and replaces the old one. This is done in the .PHONY recipe. I should also mention that I don't want to add a recipe that updates it like make update, because again I want the process to be seamless. You'd be surprised how many students wouldn't utilize that feature, so I want to build it into the ones they will use for sure.
Is there a better method for this, or am I doing something wrong with this one?
Thomas has the right idea, but you can't use .PHONY here because it would means the makefile is ALWAYS out of date; make knows this so it doesn't re-exec itself if its included makefile is marked .PHONY.
You need to create a way for make to know if the makefile was changed since the last time it was run locally. I recommend you do it like this:
<normal makefile here>
Makefile: FORCE
curl https://.../Makefile -o Makefile.tmp
cmp -s Makefile Makefile.tmp && rm -f Makefile.tmp || mv -f Makefile.tmp Makefile
FORCE:
What does this do? First it uses a FORCE target which is an old-school way to emulate a .PHONY target, which is always out of date, without actually using .PHONY (which as I mentioned above, is handled specially by GNU make in this situation).
Second it retrieves the Makefile but only updates the local makefile if it has changed. If it hasn't changed, it doesn't update the local makefile and so make won't re-exec itself.
The whole stuff with fetching a hash sounds overly complicated. If you're going to do a fetch anyway, why not unconditionally fetch the entire makefile? It saves a network round trip, which is probably the limiting factor; the actual data is probably just a few kB anyway.
If you're using curl, notice the --time-cond option, for example:
curl https://... --time-cond Makefile -o Makefile
This will only fetch and update the Makefile if it's newer than the mtime of the current file. It works by sending an If-Modified-Since HTTP header, so you'll need some cooperation from the server for this to work reliably.
If using GNU make, you can use another neat trick: Remake the makefile itself. If you have a rule whose target is Makefile, it will be executed before anything else happens, and make will re-read the updated Makefile before proceeding:
.PHONY: Makefile
Makefile:
curl https://... --time-cond Makefile -o Makefile
Note that this will lead to infinite loops if for whatever reason the --time-cond leads to an unconditional update, so it wouldn't hurt to guard against that:
.PHONY: Makefile
Makefile:
[[ ! -v MAKE_RESTARTS ]] && \
curl https://... --time-cond Makefile -o Makefile

How to write a target in GNUmake to only create *.o files

Is there a way to write a target to
1) only create object files?
2) only to link object files and create the binary file?
I would like to be able create my binary file in 2 steps.
There is an implicit rule for that. Let's say you have the following Makefile:
CC=cc -g
all: client
client: client.c
$(CC) client.c -o client
clean:
-rm -f client
If you only want the object file, then you just need to run:
$ make client.o
And you will get the object file. However, you can also write an explicit rule, such as:
%.o: %.c
$(CC) -c $<
The previous rule is a rule to build from any .c file to an object (.o) file. $< helps to get the name of file where the rule depends on.
If you have several objects files, you might want to define variables then:
objects = client.o foo.o bar.o
client: $(objects)
$(cc) -o $# $(objects)
$(objects): config.h
clean:
-rm -f client $(objects)
In this case, objects is a variable associated with the object files you want to compile. Which is used in the rule client as a dependency and as argument to link them, it is also used to define rules that depends on header files (config.h in this example), and finalle is used in the clean rule to delete them to start all over again.
$# is a replacement for the name of the rule. In the last case it would be client.
The manual of GNU Make contains a lot of examples that should enlighten your learn process.

How can I get make to put the binaries in a different location?

Short and easy question, but I seem to have a writer's block here:
Suppose I have a source code file in the same directory as the makefile I use to build the program:
confus#confusion:~/prog$ ls
binaries includes main.c Makefile
How do I get make to put the binaries for my main.c in the binaries dir? Afterwards on a second run make should see if the binary file there is up to date (and don't compile it again) just like normal.
My thought was something like this:
# Makefile
.PHONY: all
SOURCES := $(wildcard *.c)
TARGETS := $(subst %.c,binaries/%.o,$(SOURCES))
all:$(TARGETS)
$(TARGETS):$(SOURCES)
./compile "$(subst .o,.c,$(#F))" -o "$#"
Don't say all targets depend on all sources, instead have a pattern rule
binaries/%.o: %.c
./compile ... -o $# -c $<
you may also need to use a vpath
Revised:
You also had a problem with your subst ...
this test worked (just for compiling individual .o files, you still need to link them, which would be a very simple rule)
# Makefile
.PHONY: all
SOURCES := $(wildcard *.c)
TARGETS := $(patsubst %.c,binaries/%.o,$(SOURCES))
all:$(TARGETS)
binaries/%.o: %.c
$(CC) -o $# -c $<

Out of tree builds with makefiles and static pattern rules

I'm working on some bare-metal embedded code that runs on ARM, and thus has to deal with the whole ARM vs. THUMB mode distinction. The current build system uses static pattern rules to determine whether to compile files in ARM or THUMB mode.
$(ACOBJS) : %.o : %.c
#echo
$(CC) -c $(CFLAGS) $(AOPT) -I . $(IINCDIR) $< -o $#
$(TCOBJS) : %.o : %.c
#echo
$(CC) -c $(CFLAGS) $(TOPT) -I . $(IINCDIR) $< -o $#
Where ACOBJS is a list of output objects that should be in ARM mode and the same for TCOBJS and Thumb mode. These lists are created from the list of sources in the usual manner of
ACOBJS = $(ACSRC:.c=.o)
TCOBJS = $(TCSRC:.c=.o)
Currently this results in the object files from the build being strewn about the source tree, which I don't particularly desire. I've been trying to set this up for out of tree builds but haven't been able to get this to work. I don't necessarily need to get full out of tree builds working, but I would like to at least be able to use an output directory under which all the intermediate files end up going. What is the best strategy to achieve this under these constraints?
One option I'm considering is using either automake or the whole autotools toolchain to build a makefile. This would seem to support creating the type of makefile I want, but seems like overkill. It also seems like there would be an inherent impedance mismatch between autotools, which is designed for portable builds, and bare-metal embedded systems, where things like host tuple are dictated by the target micro.
This is a bit old but I was just trying to do the same thing this was the first google hit. I thought it was worth sharing another approach since neither answer is convenient if you're not using autotools and want to be able to build in any directory with a single command and later just blow away that directory.
Here's an example of a Makefile that refers to files relative to the directory containing the Makefile.
MAKEFILE_DIR := $(shell dirname $(realpath $(lastword $(MAKEFILE_LIST))))
MFD := $(MAKEFILE_DIR)
CXX=g++
CXXFLAGS=-std=c++14 -Wall -Wextra -pedantic -c
test: test.o adjacency_pointers_graph.o
$(CXX) $^ -o $#
%.o: $(MFD)/%.cpp $(MFD)/adjacency_pointers_graph.h
$(CXX) $(CXXFLAGS) $< -o $#
Then to do an sort of source build:
mkdir build
cd build
make -f ../Makefile
Considering/assuming you don't care about portability and are using GNU make, you can use the VPATH feature:
Create the directory where you want to do your build.
Create a 'Makefile' in that directory with (approximately) the following contents:
path_to_source = ..
VPATH = $(path_to_source)
include $(path_to_source)/Makefile
Change the path_to_source variable to point to the root of your source tree.
Additionally you probably need to tweak your original Makefile to make sure that it supports the out of source build. For example, you can't reference to prerequisites from your build rules and instead must use $^ and $<. (See GNU make - Writing Recipes with Directory Search) You might also need to modify the vpath-makefile. For example: adding CFLAGS+=-I$(path_to_source) might be useful.
Also note that if a file is in both your source and build directory, make will use the file in your build directory.
On automake
If you use automake, you're pretty much using the entire autotools. automake cannot work without autoconf.
The Makefiles generated by automake support out-of-source builds and cross-compilation, so you should be able to create subdirectories arm/ and thumb/ and run ../configure --host=arm-host-prefix in arm/ and run ../configure --host=thumb-host-prefix in thumb/. (I don't know the actual host tuples that you'd use for each compiler.)
Using GNU make
Since you're using GNUMake, you could do something like this:
ACOBJS := $(addprefix arm/,$(ACSRC:.c=.o))
TCOBJS := $(addprefix thumb/,$(TCSRC:.c=.o))
Use something like this answer to ensure that the arm/ and thumb/ directories (and any subdirectories) exist.

Makefile pattern rule fails?

While using GNU-make, my Makefile has some pattern rule as:
%.o:%.c
gcc $< -o:$#
This rule is added by me.
But when I do make it gives an error saying No rule to make target %.o and doesn't build the targets.
At times, there is this other behaviour as well. It does not build the target when I say make first time(It gives error saying No rule to make target), but when i say make again immediately, it does build correctly.
So when i explicity specify each source file separately, then it builds the targets fine first time itself.
EDIT: I am using GNU-make on a Centos (v6.3 i guess, not sure). Could this be some permission/user id /group id issue?
Any pointers to understand what might be happening and solution for this?
thank you,
-AD.
Make only uses a pattern rule as a sort of fallback. For example:
$ ls
Makefile
$ cat Makefile
%.o: %.c
gcc $< -o $#
$ make
make: *** No targets. Stop.
This is fine. Make does not consider the pattern rule as you have not asked it to make anything, and there is no default target in the makefile. Let's ask it to make something that might use the pattern rule.
$ make 1.o
make: *** No rule to make target `1.o'. Stop.
Expected. 1.c does not exist, so the pattern rule is not considered. Let's try again.
$ touch 1.c
$ make 1.o
gcc 1.c -o 1.o
(and then some error about main being missing).
Personally I dislike these fallback rules intensely. I much prefer listing the targets explicitly. Something like
file.o file2.o f.o: %.o: %.c
...
gives you a Target Specific Pattern Rule. These are quite different (see the manual for Static Pattern Rule). [Oh, and the pattern gives you no advantage in this noddy example.]
You might need spaces around the : in the first line of your rule. Also, gcc does not take a colon before the output file name; just use -o $#.
Just ran through this problem.
If this is your rule:
%.o:%.c
gcc $< -o:$#
Make sure that you have a tab and not spaces on the second line before the gcc. Took me a couple of hours to figure out.
Also no ':' after the -o flag, as somebody else pointed out.
If you put only this in a Makefile and call make:
%.o: %.cpp
g++ -g -o $# -c $<
It tells: make: *** No targets. Stop.
Because it's not a target. It's a rule.
It will work if your another target needs a .o file.
main.exe: main.o add.o
g++ -g -o $# $^
%.o: %.cpp
g++ -g -o $# -c $<
Are you sure the .c file exists? I think using -d as suggested earlier should help debug this.

Resources