Makefile to support 2 executables - makefile

I am trying to update my Makefile to support building a binary of my project, and a binary of some Unit Tests.
My directory structure is the following
|-code/
|--|--src/
|--|--inc/
|
|-tests/
|--|--src/
|--|--inc/
My makefile compiles the binary from code well, but is having some issues with the test. The test folder contains some unit test that tests some classes in code/src/. I have a file main.cpp in code/src/ that contains the main() function, and also another file, called test.cpp in tests/src that contains its own main() function.
This led me to this complicated Makefile:
CC = g++
FLAGS = -g -c -Wall
INCLUDEDIR = -Icode/inc -Itests/inc
BUILDDIR = build
SOURCEDIR = code/src
SOURCES = $(wildcard $(addsuffix /*.cpp,$(SOURCEDIR)))
TEMP_OBJ = $(SOURCES:%.cpp=%.o)
NOT_DIR = $(notdir $(TEMP_OBJ))
OBJECTS = $(addprefix $(BUILDDIR)/, $(NOT_DIR))
TEST_DIR = tests/src
TEST_SOURCES = $(wildcard $(addsuffix /*.cpp,$(TEST_DIR)))
TEST_TEMP_OBJ = $(TEST_SOURCES:%.cpp=%.o)
TEST_NOT_DIR = $(notdir $(TEST_TEMP_OBJ))
TEST_OBJECTS = $(addprefix $(BUILDDIR)/, $(TEST_NOT_DIR))
EXECUTABLE = Client
TEST_EXECUTABLE = TestUnit
all: $(BUILDDIR) $(BUILDDIR)/$(EXECUTABLE) $(BUILDDIR)/$(TEST_EXECUTABLE)
$(BUILDDIR):
mkdir -p $#
$(BUILDDIR)/$(EXECUTABLE): $(OBJECTS)
$(CC) $^ -o $#
$(BUILDDIR)/%.o : code/src/%.cpp
$(CC) $(FLAGS) $< $(INCLUDEDIR) -o $#
$(BUILDDIR)/$(TEST_EXECUTABLE): $(OBJECTS) $(TEST_OBJECTS)
#rm -f $(BUILDDIR)/main.o
$(CC) $^ -o $#
$(BUILDDIR)/%.o : tests/src/%.cpp
$(CC) $(FLAGS) $< $(INCLUDEDIR) -o $#
clean:
rm -rf $(BUILDDIR)
It fails with the error:
g++: error: build/main.o: No such file or directory
make: *** [build/TestUnit] Error 1
Which is because I have the line:
#rm -f $(BUILDDIR)/main.o
but otherwise I would get the error (there is main in main.cpp and test.cpp in code/src/ and tests/code/ respectively):
/tests/src/test.cpp:7: multiple definition of `main'
code/src/main.cpp:6: first defined here
collect2: error: ld returned 1 exit status
There is a lot of duplication in my Makefile, and I would love to get something more succinct that achieves the purpose of building 2 binaries from those 2 folders, although code is shared.
Any help would please be appreciated. Thank you very much!

There are a number of problems with this makefile.
First, there is no rule to build test object files, such as test.o. The only rule for building objects requires that the source be in code/src/; I don't know how you even get far enough to see a linker error.
Let's change the object rule to a static pattern rule:
$(OBJECTS) : $(BUILDDIR)/%.o : code/src/%.cpp
$(CC) $(FLAGS) $< $(INCLUDEDIR) -o $#
Then we can add an additional rule for the test objects:
$(TEST_OBJECTS) : $(BUILDDIR)/%.o : tests/src/%.cpp
$(CC) $(FLAGS) $< $(INCLUDEDIR) -o $#
(Never mind the redundancy for now-- we have to get it working first.)
Now we should see a linker error in this rule:
$(BUILDDIR)/$(TEST_EXECUTABLE): $(OBJECTS) $(TEST_OBJECTS)
...
In that list of prerequisites are two files, main.o and test.o fighting over who gets to define main(). We want test.o, so main.o must go:
$(BUILDDIR)/$(TEST_EXECUTABLE): $(filter-out build/main.o,$(OBJECTS)) $(TEST_OBJECTS)
...
Try this much and tell us the result. Once it's working we can slim it down.

Related

How can my makefile include subdirectories?

(updated for clarity) (solution added at bottom)
I found a makefile online which builds all the cpp files in that directory and compiles them.
But I can't work out how I can include files inside a subdirectory.
Here's a breakdown of what happens:
I create the files test.cpp & test.hpp and place them inside the sub-directory '/gui' which is contained within my working directory, they contain the function testFunction().
Without including test.hpp, I type "make" into terminal and I receive the error:
:
g++ -c -o main.o main.cpp
main.cpp: In function 'int main(int, char**)':
main.cpp:6:2: error: 'testFunction' was not declared in this scope
testFunction();
^~~~~~~~~~~~
make: *** [<builtin>: main.o] Error 1
If I include (#include "gui/test.hpp"), I then receive a different error:
:
g++ -c -o main.o main.cpp
g++ main.o -Wall -o testfile
/usr/bin/ld: main.o: in function `main':
main.cpp:(.text+0x14): undefined reference to `testFunction()'
collect2: error: ld returned 1 exit status
make: *** [makefile:34: testfile] Error 1
But if I then add "-I/gui" or (at a guess) "-I./gui" to CFLAGS, I get the exact same error message.
Here's the makefile for reference:
TARGET = testfile
LIBS =
CC = g++
CFLAGS = -g -Wall
.PHONY: default all clean
default: $(TARGET)
all: default
OBJECTS = $(patsubst %.cpp, %.o, $(wildcard *.cpp))
HEADERS = $(wildcard *.hpp)
%.o: %.c $(HEADERS)
$(CC) $(CFLAGS) -c $< -o $#
.PRECIOUS: $(TARGET) $(OBJECTS)
$(TARGET): $(OBJECTS)
$(CC) $(OBJECTS) -Wall $(LIBS) -o $#
clean:
-rm -f *.o
-rm -f $(TARGET)
Thanks in advance!
Updated makefile since accepted answer:
(Changes were to include directories, CC replaced with CXX, and %.c replaced with %.cpp)
TARGET = testfile
DIRS =
LDLIBS =
CXX = g++
CXXFLAGS= -g -Wall
# this ensures that if there is a file called default, all or clean, it will still be compiled
.PHONY: default all clean
default: $(TARGET)
all: default
# substitute '.cpp' with '.o' in any *.cpp
OBJECTS = $(patsubst %.cpp, %.o, $(wildcard *.cpp $(addsuffix /*.cpp, $(DIRS))))
HEADERS = $(wildcard *.h)
# build the executable
%.o: %.cpp $(HEADERS)
$(CXX) $(CXXFLAGS) -c $< -o $#
# if make is interupted, dont delete any object file
.PRECIOUS: $(TARGET) $(OBJECTS)
# build the objects
$(TARGET): $(OBJECTS)
$(CXX) $(OBJECTS) -Wall $(LDLIBS) -o $#
clean:
-rm -f *.o $(addsuffix /*.o, $(DIRS))
-rm -f $(TARGET)
To understand what's happening here you have to look up the definitions of declaration versus definition in C++ (and other languages). You should definitely do that.
A declaration (typically put into a header file) is like the address of your house. If someone wants to send you a letter, they need your address. If your main function wants to call another function like testFunction(), it needs the declaration of the function.
The first error happens because you don't have the header file included, so the compiler doesn't have the declaration of the function you want to call, which means it won't compile your calling function.
But for the letter to actually arrive, you need your actual house. The address is the declaration and your house is the definition... in this case the actual function implementation. That lives in test.cpp file. When you link your code together, the linker (in this scenario I guess the linker is like the postal service :p :) ) will try to link up the call to the definition.
However, you can see that you are not compiling the test.cpp file nor are you linking the object file:
g++ main.o -Wall -o testfile
here we see main.o, but not gui/test.o.
Why not? This line:
OBJECTS = $(patsubst %.cpp, %.o, $(wildcard *.cpp))
Matches all *.cpp files and converts them into .o files. But *.cpp matches only files in the current directory, like main.cpp. If you want to put files in a different directory you have to tell make where they are; for example:
OBJECTS = $(patsubst %.cpp, %.o, $(wildcard *.cpp gui/*.cpp))

No rule to make target *, needed by *

I can't get what's wrong with my makefile:
DIST_PATH = ../dist/libs
BUILD_PATH = ../build
MKDIR_P = mkdir -p
.PHONY: all
SHELL = /bin/sh
CC = gcc
FLAGS = -std=gnu99
CFLAGS = -fPIC -pedantic -Wall -Werror
LDFLAGS = -shared
LOG_SRCS = $(shell echo log/*.c)
LOG_HEADERS = $(shell echo log/*.h)
LOG_OBJS = $(addprefix $(BUILD_PATH)/, $(notdir $(LOG_SRCS:.c=.o)))
LOG_TARGET = $(DIST_PATH)/liblog.so
all: dirs $(LOG_TARGET)
dirs :
$(MKDIR_P) $(DIST_PATH)
$(MKDIR_P) $(BUILD_PATH)
$(LOG_TARGET) : $(LOG_OBJS)
$(CC) $(FLAGS) $(CFLAGS) -o $# $(LDFLAGS)
I need to build a shared library from sources in log/ folder to ../dist/libs and put obj file in ../build but I am getting the error:
make: *** No rule to make target '../build/log.o', needed by '../dist/libs/liblog.so'. Stop.
P.S. I know there are many similar questions but I couldn't get from these questions how to resolve my problem.
The problem is that the source and object files are supposed to be placed in different directories, and there's no implicit rule for that.
You need to add a rule for how to translate a source file to an object file:
$(BUILD_PATH)/%.o: log/%.c
Now make knows how to create the object files from the source files.
There's another problem though:
$(LOG_TARGET) : $(LOG_OBJS)
$(CC) $(FLAGS) $(CFLAGS) -o $# $(LDFLAGS)
The command doesn't list any input files, you need to add all the object files to be linked:
$(LOG_TARGET) : $(LOG_OBJS)
$(CC) $(FLAGS) $(CFLAGS) -o $# $^ $(LDFLAGS)
# ^^
# List of all "prerequisites" (object files) to be linked

Attempt to link objects makes them recompile even if up-to-date

I have a recipe in my makefile that relies on several object files. I would like it to simply link the them, but they are always recompiling.
I've googled around and found information I did not know(marked with #) and changed it a bit, but the problem persisted.
I am led to believe make expects the name of the recipe be the name of the file, and I am failing to accomplish that. The problem is I do not what else to try and fix this. I would appreciate any help
CC = g++
#.PHONY: sfml-app
LIBS = -lsfml-graphics -lsfml-window -lsfml-system
APPLICATION = sfml-app
INCLUDE_DIR = -I include/
SOURCE_DIR = source
OUTPUT_DIR = bin
SOURCES = $(wildcard $(SOURCE_DIR)/*.cpp)
OBJECTS = $(notdir $(patsubst %.cpp, %.o, $(SOURCES)))
#$(OUTPUT_DIR)/$(APPLICATION): $(OBJECTS)
#bin/sfml-app: $(OBJECTS)
#sfml-app: $(OBJECTS)
#$(APPLICATION): $(OBJECTS)
$(CC) $(OUTPUT_DIR)/*.o $(LIBS) -o $(OUTPUT_DIR)/$(APPLICATION)
%.o: $(SOURCE_DIR)/%.cpp
$(CC) -c $< $(INCLUDE_DIR) -o $(OUTPUT_DIR)/$#
clean:
rm $(OUTPUT_DIR)/*
print-% : ; #echo $* = $($*)
This rule doesn't create the file it promises to:
%.o: $(SOURCE_DIR)/%.cpp
$(CC) -c $< $(INCLUDE_DIR) -o $(OUTPUT_DIR)/$#
See that -o $(OUTPUT_DIR)/$#? That's instructing the compiler to create a file in $(OUTPUT_DIR) instead of in the working directory.
If you really want your object files to go in $(OUTPUT_DIR), you need to make sure that your rule indicates that:
$(OUTPUT_DIR)/%.o: $(SOURCE_DIR)/%.cpp
$(CC) -c $< $(INCLUDE_DIR) -o $#
Or better, to act like the standard %.o: %.c rule (which will include CFLAGS etc):
$(OUTPUT_DIR)/%.o: $(SOURCE_DIR)/%.cpp
$(COMPILE.c) $(OUTPUT_OPTION) $<
I note your input files are named *.cpp - usually, that convention is for C++ files (i.e. to be compiled with $(COMPILE.cc), which will invoke $(CXX) rather than $(CC)). Check that you've not mixed up your C and C++ sources!

Makefile percent sign in rule not being recognised

I just began learning about GNU make yesterday. As the title says, it appears that the % sign in my Makefile is not being recognised. Or perhaps I am using it the wrong way. Can someone enlighten me?
CXX = clang++
EXE = Invaders
SDL = -F./lib
LDFLAGS = $(SDL)
SRCDIR = src
OBJDIR = obj
SRC = $(SRCDIR)/main.cpp $(wildcard $(SRCDIR)/util/*.cpp) $(wildcard $(SRCDIR)/misc/*.cpp)
OBJ = $(patsubst $(SRCDIR)/%.cpp,$(OBJDIR)/%.o,$(SRC))
all: $(EXE)
$(EXE): $(OBJ)
$(CXX) $(LDFLAGS) -o bin/$# $^
$(OBJDIR)/%.o: $(SRCDIR)/%.cpp $(SRCDIR)/%.h
$(CXX) -c -o $# $<
Console output:
make: *** No rule to make target `obj/main.o', needed by `Invaders'. Stop.
Tree view of my entire project with directories /obj and /src expanded
Thanks!
This rule:
$(OBJDIR)/%.o: $(SRCDIR)/%.cpp $(SRCDIR)/%.h
$(CXX) -c -o $# $<
does not fit obj/main.o, because there is no src/main.h.
(Also, please put the necessary information in the body of the question, don't rely on a link to an image.)

Makefile does not work properly

I've this folder structure
project
|_src
| |_test
| |_main.cpp
|_Makefile
This is my makefile (trying to adapt from this link):
CC = g++
RM = rm
WFLAGS = -c -Wall -W
LDFLAGS =
SRCTESTD = src/test
EXECUTABLE = test
OBJD = .obj
DEPD = .dep
SRCSTEST = $(SRCTESTD)/main.cpp
OBJECTSTEST = $(patsubst %.cpp, $(OBJD)/test/%.o, $(notdir $(SRCSTEST)))
DEPDSTEST = $(patsubst %.cpp, $(DEPD)/test/%.d, $(notdir $(SRCSTEST)))
all: $(SRCSTEST) $(EXECUTABLE)
$(EXECUTABLE): $(OBJECTSTEST)
$(CC) $(LDFLAGS) $(OBJECTSTEST) -o $#
.cpp.o:
$(CC) $(WFLAGS) $< -o $#
It does not work, and I've this error
make: *** No rule to make target `.obj/test/main.o', needed by `test'. Stop.
What I'm doing wrong? Sorry for trivial question, but I'm a make newbie.
The link shows outdated methods, such as suffix rules. Making dependencies can also be done during compilation by gcc/g++.
As for the rest, here is it :
EXE := test
SRCDIR := src
OBJDIR := .obj
SRC := $(shell find $(SRCDIR) -name "*.cpp")
OBJ := $(SRC:$(SRCDIR)/%.cpp=$(OBJDIR)/%.o)
DEP := $(OBJ:.o=.d)
LDLIBS := # -l flags
LDFLAGS := # -L flags
CPPFLAGS := -MMD -MP # -I flags also
CXXFLAGS := -W -Wall # no -c flag here
.PHONY: all clean fclean re
all: $(EXE)
clean:
$(RM) -r $(OBJDIR)
fclean: clean
$(RM) $(EXE)
re: fclean all
-include $(DEP)
$(EXE): $(OBJ)
$(CXX) $(LDFLAGS) $^ $(LDLIBS) -o $#
$(OBJDIR)/%.o: $(SRCDIR)/%.cpp
#mkdir -p $(#D)
$(CXX) $(CPPFLAGS) $(CXXFLAGS) -o $# -c $<
No redefinition of internally defined variables, no suffix rules, correct linking step and dependencies generation.
Update: To avoid calling mkdir for every source file, one should use order-only prerequisites and the special target .SECONDEXPANSION.
Change this block:
$(OBJDIR)/%.o: $(SRCDIR)/%.cpp
#mkdir -p $(#D)
$(CXX) $(CPPFLAGS) $(CXXFLAGS) -o $# -c $<
To this:
.SECONDEXPANSION:
$(OBJDIR)/%.o: $(SRCDIR)/%.cpp | $$(#D)/
$(CXX) $(CPPFLAGS) $(CXXFLAGS) -o $# -c $<
%/:
mkdir $*
The error means make can't find a correct rule to build your object files. Your tree structure lacks some informations: only one file ? Where are the others ? Anyway, here are some hints:
In the last two lines, you are using an obsolete feature of make: suffix rules. I suggest you switch to a pattern rule, which is functionaly equivalent.
Say something like:
%.o: %.cpp
$(CXX) $(CXXFLAGS) $< -o $#
Another thing (that shouldn't be a problem here): you are using the variable CC which is internally defined as the default C compiler. It's okay because you redefine it, but as your sources seem to be C++ files, why not use the variable CXX, that is internally defined as the C++ compiler ?
Lastly, to make sure your set of files are correctly defined, you can print them with a dummy show target, see here.
show:
#echo "OBJECTSTEST=$(OBJECTSTEST)"
...

Resources