How to include clean target in Makefile? - unix

I have a Makefile that looks like this
CXX = g++ -O2 -Wall
all: code1 code2
code1: code1.cc utilities.cc
$(CXX) $^ -o $#
code2: code2.cc utilities.cc
$(CXX) $^ -o $#
What I want to do next is to include clean target so that every time
I run make it will automatically delete the existing binary files of code1 and code2 before creating the new ones.
I tried to put these lines at the very end of the makefile, but it doesn't work
clean:
rm -f $#
echo Clean done
What's the right way to do it?

The best thing is probably to create a variable that holds your binaries:
binaries=code1 code2
Then use that in the all-target, to avoid repeating:
all: clean $(binaries)
Now, you can use this with the clean-target, too, and just add some globs to catch object files and stuff:
.PHONY: clean
clean:
rm -f $(binaries) *.o
Note use of the .PHONY to make clean a pseudo-target. This is a GNU make feature, so if you need to be portable to other make implementations, don't use it.

In makefile language $# means "name of the target", so rm -f $# translates to rm -f clean.
You need to specify to rm what exactly you want to delete, like rm -f *.o code1 code2

By the way it is written, clean rule is invoked only if it is explicitly called:
make clean
I think it is better, than make clean every time. If you want to do this by your way, try this:
CXX = g++ -O2 -Wall
all: clean code1 code2
code1: code1.cc utilities.cc
$(CXX) $^ -o $#
code2: code2.cc utilities.cc
$(CXX) $^ -o $#
clean:
rm ...
echo Clean done

Related

Makefile | How to check only once whether an output dir already exists?

Currently the shell script checks for the existence of intended object directory right before each compiler call. How do I modify my Makefile so that the code checks only once before it moves on to compiling all the prerequisites?
Here is my Makefile:
#########################################################
## BUILD TASKS ##
#########################################################
HDIR := hdr
SDIR := src
ODIR := obj
EXET := a
OBJS := main.o mainhdr.o testcode.o
OSRC := $(addprefix $(ODIR)/, $(OBJS))
CXX := g++
CXXFLAGS := -I$(HDIR) -g -Wall -std=c++17
## BUILD DIRECTIVE:
all: $(EXET)
$(EXET): $(OSRC)
$(CXX) $(CXXFLAGS) $^ -o $#
$(ODIR)/%.o: $(SDIR)/%.cpp
if [ ! -d "$(ODIR)" ]; then mkdir $(ODIR); fi
$(CXX) -c $(CXXFLAGS) $^ -o $#
## foo.bak: foo.bar
## if [ ! -d "$(ODIR)" ]; then mkdir $(ODIR); fi
#########################################################
## CLEAN TASK ##
#########################################################
.PHONY: clean
clean:
rm -r $(EXET) $(ODIR)
I have tried putting:
foo.bak: foo.bar
if [ ! -d "$(ODIR)" ]; then mkdir $(ODIR); fi
as suggested here right before the clean task, but that doesn't seem to work. I could be understanding it wrong, but isn't that the Makefile is executed recursively so by putting that block of code at the end it should be at the tip of recursion and thus executed before everything else?
On newer gnu-make, you can use 'order-only-prerequisites`. This eliminate the timestamp checking, and only required that the prerequisite will exists. This works well to ensure that directories will be created before files are stored into them, and can significantly speedup build jobs
$(ODIR):
mkdir $(ODIR)
# Note pipe '|' to separate order only prereq.
$(ODIR)/%.o: $(SDIR)/%.cpp | $(ODIR)
$(CXX) -c $(CXXFLAGS) $^ -o $#
See: https://www.gnu.org/software/make/manual/make.html#Prerequisite-Types

Using a shell variable within Makefile

I have a line line in make file for compiling a c program, which goes like this
$(CC) -c -o $# $< $(CFLAGS). I have to modify a particular line in code every time and compile again. Modification is just an argument to a function. I have the argument in one file and i use sed utility to modify my c source and then compile. I want to see which of the arguements leads to successful compilation. I tried to use this $(CC) -c -o $# $< $(CFLAGS) ; echo $? >status where I was hoping if compilation was successful status file would have an entry 0. But i see the source file name in status file. I came to know that $? is also a make automatic variable. How can i read the shell variable $? within makefile ? I have tried using $(CC) -c -o $# $< $(CFLAGS) ; echo $$? >status and $(CC) -c -o $# $< $(CFLAGS) ; echo $(shell echo $?) >status without getting correct results.
The version
echo $$? > status
definitely works for me. What OS are you using? What are the incorrect results when using $$??
If you are on Windows, there is no $?, you might want ot use %errorlevel% instead.

gnu make multiple targets one object each

I have an old project for which I'm trying to create a multiple binaries, one for each object in the directory. I cannot for the life of me figure out how to deal with multiple targets in this manner. The following works, but it seems to me I should be able to have one rule to link them all, so to speak,
# compile objects, no problem
%.o: %.c
$(CC) -c $(CFLAGS) $(CPPFLAGS) $< -o $#
bin: bin.o
$(CC) -o $# $< ../lib/libfoo.a -lm $(ARCH)
bar: bar.o
$(CC) -o $# $< ../lib/libfoo.a -lm $(ARCH)
One approach that I did get to work is to strip off the suffix from the target name like this, and compile and link in one step, but it feels a little hackish,
%.o: %.c
$(CC) $(CFLAGS) $(CPPFLAGS) $< ../lib/libfoo.a -lm -o $(*F)
Disclaimer: I despise make
UPDATE 1: this is what I ended up with
EXECS = bin bar ...
all: $(EXECS)
%: %.c
#echo "Building $# from $<"
$(CC) -c $(CFLAGS) $(CPPFLAGS) $< -o $#.o
$(CC) $(CFLAGS) $(CPPFLAGS) $#.o ../lib/libfoo.a -lm -o $#
The convention is to have something like this at the top of your Makefile:
.PHONY: all
all: bin bar
Thus make all will make bin and bar, and by putting it at the top it's the default target for make without arguments. The .PHONY: documents this as a "metatarget", but also instructs Make to run it even if there happens to be a file called all with a fresh datestamp.
%: %.o
$(CC) -o $# $< ../lib/libfoo.a -lm $(ARCH)
This tells Make how to make them both.

Makefile errors when I change a directory name

I have a Makefile that compiles, but I want to change the name of one of the directories from "release" to "objects". This is the original Makefile -
# This makefile compiles ....
INCLUDE = -I/usr/include/X11 -I/usr/local/include -I/usr/local/include/FL/images -I/usr/include/freetype2
CC=g++
CFLAGS=-w -D LINUX -O3 -fpermissive
OBJDIR=release # HERE IS THE DIRECTORY I WANT TO CHANGE
SRCDIR=src
LDFLAGS= -L/usr/X11R6/lib$(LIBSELECT) -lpthread -lfltk -lXext -lXft -lfontconfig -lXinerama -lpthread -ldl -lm -lX11
SOURCES_RAW= robot_driver_agent.cpp robot_driver_position.cpp robot_driver_priorityqueue.cpp main.cpp robot_driver_tree.cpp robot_driver_stack.cpp robot_driver_grid.cpp robot_driver_path.cpp grid_analyzer.cpp tcpserver.cpp tcpclient.cpp servercontrol.cpp clientcontrol.cpp robot.cpp udpserver.cpp udpclient.cpp owncontrol.cpp guiwindow.cpp rs232.cpp
TARGET:= go
TARGETD:= go_d
OBJECTS:=$(SOURCES_RAW:.cpp=.o)
OBJECTS:=$(patsubst %.o, $(OBJDIR)/%.o, $(OBJECTS))
SOURCES:=$(SOURCES_RAW)
SOURCES:=$(patsubst %.cpp, $(SRCDIR)/%.cpp, $(SOURCES))
all: $(TARGET)
$(TARGET): $(OBJECTS)
$(CC) -w -D LINUX $(INCLUDE) $^ -o $# $(LDFLAGS)
release/%.o: src/%.cpp
test -d $(OBJDIR) || mkdir $(OBJDIR)
$(CC) -g -c $< $(CFLAGS) -o $#
debug: $(TARGETD)
$(TARGETD): $(OBJECTS)
$(CC) -w -D LINUX $(INCLUDE) $^ -o $# $(LDFLAGS)
%.o: $(SRCDIR)/%.cpp
$(CC) -c -g $< $(CFLAGS)-o $#
.PHONY : clean
clean:
rm -f $(OBJDIR)/*.o
rm -f $(TARGET) $(TARGETD)
All I do is change the OBJDIR symbol to "objects" so it would just be -
OBJDIR=objects
But when I do that, I get the error -
make: *** No rule to make target `objects/robot_driver_agent.o', needed by `go'.
What am I missing? Is "objects" a word reserved for something in make so I can't use it for directories? Is it something in the make file that I need to change? Honestly, I don't know that much about makefiles so any help at all would be great. Thanks.
You have a rule:
release/%.o: src/%.cpp
...
So that when OBJDIR=release and Make wants to build release/robot_driver_agent.o, it knows just what to do. Then you try OBJDIR=objects, it wants to build objects/robot_driver_agent.o, and it doesn't know how because there's no rule that fits. Try changing the rule to:
$(OBJDIR)/%.o: src/%.cpp
...

Recursive Make issue

I'm trying my hand at using recursive make to eliminate things like having multiple CFLAGS variables, one for each target. There is only one level of recursion, there is no crazy directory tree traversal happening here. I just want to dump my object files into their target specific folders.
So far I have come up with something that is really elegant (edit: alright it's more elegant than the single-makefile solution i had from before. Which had way too much repetition!) but unfortunately does not work.
I think that by posting the format here it will be apparent what it is that I am trying to do.
# ./makefile
.PHONY: all clean
export CC = g++
export INCLUDE = -I ../include/
export SRC = Main.cpp Graphics.cpp Thread.cpp Net.cpp Otherstuff.cpp
export LINKEROPT = -lglew32 -lopengl32 -lsdl -lws2_32 -lglu32 -lmorelibraries
test:
$(MAKE) -f make.unittest
all:
$(MAKE) -f make.unittest
$(MAKE) -f make.debug
$(MAKE) -f make.release
clean:
-rm -rf build_* *.exe
# I am on windows so the targets are .exe's
Here is the file make.debug:
### sub-makefile for the debug build target. Contains target specific build settings.
DIRNAME = build_debug
TARGETNAME = program_debug
TARGETDESCR = DEBUG
CFLAGS = -Wextra -Wall -O0 -g3 -DDEBUG
### EVERYTHING AFTER THIS POINT IS A TEMPLATE
# my goal is to have as much of my makefile code being "reusable" as possible
# so that I can easily add targets.
OBJ = $(patsubst %.cpp,$(DIRNAME)/%.o,$(SRC))
DEPS = $(patsubst %.cpp,$(DIRNAME)/%.d,$(SRC))
-include $(DEPS)
# default behavior. Set up the build directory. Then build the debug target.
all: $(DIRNAME) $(TARGETNAME)
# this is the build dir
$(DIRNAME):
mkdir $(DIRNAME)
$(DIRNAME)/%.o: %.cpp
#echo -e "Compiling for $(TARGETDESCR): $< --> $#"
$(CC) $(CFLAGS) $(INCLUDE) -c $< -o $#
#echo -e "Generating dependencies: $< --> $(patsubst %.o,%.d,$#)"
$(CC) $(CFLAGS) $(INCLUDE) -MM -MT $# -MF $(patsubst %.o,%.d,$#) $<
# I realize there is a way to generate the deps while compiling in one pass
# but I'll figure it out later
$(TARGETNAME): $(OBJ)
#echo -e "Linking $(TARGETDESCR): $#.exe"
$(CC) -L ../lib/win32/ -o $# $(OBJ) $(LINKEROPT)
As you can see, I can very quickly add a new build target with its own set of CFLAGS by copying over the sub-makefile and lightly modifying it, and then adding a few entries to the main makefile.
So the problem here is that it doesn't recognize changes in files. Only when I edit Main.cpp will it recompile build_debug/Main.o. I'm really unsure about where I can start to figure out what's incorrect.
I've created a monster.
When I suggested recursive make in that other post it was to deal with a specific -- and very strange -- problem of inclusion. I do not subscribe to the dogma that recursive make is pure evil, but it does have disadvantages, and should not be used as a cure-all.
First, let's eliminate a bad (and ongoing) source of redundancy. In you main makefile, instead of
$(MAKE) -f make.debug
use
$(MAKE) -f makefile.sub DIRNAME = build_debug TARGETNAME = program_debug TARGETDESCR = DEBUG CFLAGS = -Wextra -Wall -O0 -g3 -DDEBUG
where makefile.sub is the "template" part of your various sub-makefiles. I know that doesn't look like an improvement, but this way you have only one sub-makefile, not N. (I also think you're using too many target-specific variables, but we can discuss that later.)
Once you're comfortable with this, you can use target-specific variables within makefile.sub, so that in the main makefile you can replace
all:
$(MAKE) -f makefile.sub DIRNAME=build_unittest ...
$(MAKE) -f makefile.sub DIRNAME=build_debug ...
$(MAKE) -f makefile.sub DIRNAME=build_release ...
with
all:
$(MAKE) -f makefile.sub unittest debug release
It's not clear to me what problem you're having with recognizing changes in files (when should Make rebuild build_debug/Main.o, if Main.cc hasn't changed?), but it's probably a direct consequence of using make recursively (see "disadvantages", above) and we can probably fix it without too much grief.
EDIT:
In your sub-makefile, put the all rule before the -include line, so that it will be the default rule. When you call $(MAKE), you don't specify a target, so Make chooses the default target, which is (usually) whichever one comes first. The contents of the %.d files have the form of rules, so if you include them first, one of them (i.e. build_debug/Main.o: Main.cpp) will win.

Resources