I have a C++ project with the following structure:
/Project
Makefile
/src (.cpp source files)
...
/include (.h header files)
...
/libs
...
/build (.o object files)
...
/tests (target .cpp files I want to compile)
test1.cpp
test2.cpp
test3.cpp
...
/bin (output directory for compiled files)
...
For the tests inside my test file, I would like to be able to
Compile them individually, e.g. "make test1", "make test2"
Compile them all at once
But I would like to be able to do this without needing to define new variables (e.g. TARGET1, TARGET2,...) for each new test file, nor add a bunch of new lines to my makefile for each new test file.
For example, right now I have something like:
CXX = g++
SRC_DIR = ./src
BUILD_DIR = ./build
LIB = -I libs
INC = -I include
SRCS = $(shell find $(SRC_DIR) -type f -name *.cpp)
OBJS = $(patsubst $(SRC_DIR)/%, $(BUILD_DIR)/%, $(SRCS:.cpp=.o))
TARGET1 ?= test1.cpp
TARGET2 ?= test2.cpp
TARGET3 ?= test3.cpp
all: $(OBJS)
$(CXX) ./tests/$(TARGET1).cpp $(LIB) $(INC) $^ -o ./bin/$(TARGET1)
$(CXX) ./tests/$(TARGET2).cpp $(LIB) $(INC) $^ -o ./bin/$(TARGET2)
$(CXX) ./tests/$(TARGET3).cpp $(LIB) $(INC) $^ -o ./bin/$(TARGET3)
$(TARGET1): $(OBJS)
$(CXX) ./tests/$(TARGET1).cpp $(LIB) $(INC) $^ -o ./bin/$(TARGET1)
$(TARGET2): $(OBJS)
$(CXX) ./tests/$(TARGET2).cpp $(LIB) $(INC) $^ -o ./bin/$(TARGET2)
$(TARGET3): $(OBJS)
$(CXX) ./tests/$(TARGET3).cpp $(LIB) $(INC) $^ -o ./bin/$(TARGET3)
$(BUILD_DIR)/%.o: $(SRC_DIR)/%.cpp
$(CXX) $(INC) -c -o $# $<
which does the job, but isn't very scalable. How could I do this scalably?
Make has some more tricks that you can use (not tested):
CXX = g++
SRC_DIR = src
BUILD_DIR = build
TEST_DIR = tests
BIN_DIR = bin
LIB = -I libs
INC = -I include
SRCS = $(wildcard $(SRC_DIR)/*.cpp)
OBJS = $(patsubst $(SRC_DIR)/%.cpp,$(BUILD_DIR)/%.o,$(SRCS))
TESTS = $(wildcard $(TEST_DIR)/*.cpp)
TARGETS = $(patsubst $(TEST_DIR)/%.cpp,$(BIN_DIR)/%,$(TESTS))
all: $(TARGETS)
$(TARGETS): $(BIN_DIR)/%: $(OBJS)
$(CXX) $(TEST_DIR)/$*.cpp $(LIB) $(INC) $^ -o $#
$(BUILD_DIR)/%.o: $(SRC_DIR)/%.cpp
$(CXX) $(INC) -c -o $# $<
The main trick here is the static pattern rule for $(TARGETS): in the recipe $* expands as the stem of the pattern. The other tricks are a simpler use of patsubst and the use of wildcard instead of the less efficient shell find. Note that this last one works only if your source files are flat in src, not if they are organized in a hierarchy of sub-directories.
But this does not answer your most tricky request: a way to invoke make testX instead of make bin/testX. So, here is the most tricky part:
SHORTERTARGETS = $(patsubst $(TEST_DIR)/%.cpp,%,$(TESTS))
.PHONY: $(SHORTERTARGETS)
# $(1): short target
define TARGETS_rule
$(1): $(BIN_DIR)/$(1)
endef
$(foreach t,$(SHORTERTARGETS),$(eval $(call TARGETS_rule,$(t))))
You can even use this foreach-eval-call to factorize other parts of your Makefile:
CXX = g++
SRC_DIR = src
BUILD_DIR = build
TEST_DIR = tests
BIN_DIR = bin
LIB = -I libs
INC = -I include
SRCS = $(wildcard $(SRC_DIR)/*.cpp)
OBJS = $(patsubst $(SRC_DIR)/%.cpp,$(BUILD_DIR)/%.o,$(SRCS))
TESTS = $(wildcard $(TEST_DIR)/*.cpp)
TARGETS = $(patsubst $(TEST_DIR)/%.cpp,$(BIN_DIR)/%,$(TESTS))
SHORTERTARGETS = $(patsubst $(TEST_DIR)/%.cpp,%,$(TESTS))
.PHONY: all $(SHORTERTARGETS)
all: $(TARGETS)
$(BUILD_DIR)/%.o: $(SRC_DIR)/%.cpp
$(CXX) $(INC) -c -o $# $<
# $(1): short target
define TARGETS_rule
$(1): $(BIN_DIR)/$(1)
$(BIN_DIR)/$(1): $(OBJS)
$(CXX) $(TEST_DIR)/$(1).cpp $(LIB) $(INC) $$^ -o $$#
endef
$(foreach t,$(SHORTERTARGETS),$(eval $(call TARGETS_rule,$(t))))
The most difficult to understand in this last version is the need of $$in the recipe (double expansion). But here the GNU make manual is your friend.
Related
I'm currently using MinGW64, G++, and a makefile to compile my c++ project on VSCode. I have two src directories src and src/vendor/imGui containing .cpp files that I compile. As of now, the makefile is able to compile both src folders. However, the .o files are outputted in their respective src folders. How can I make it so that every .o file generated gets sent to one specific folder/directory?
Current Makefile:
CXX = g++
CXXFLAGS := -std=c++17 -Wall -Wextra -g
LFLAGS += -LC:/mingw64/x86_64-w64-mingw32/bin
LFLAGS += -LC:/mingw64/x86_64-w64-mingw32/lib
OUTPUT := output
SRC := src
SRC += src/vendor/imGui
INCLUDE := include
LIB := lib
ifeq ($(OS),Windows_NT)
MAIN := main.exe
SOURCEDIRS := $(SRC)
INCLUDEDIRS := $(INCLUDE)
LIBDIRS := $(LIB)
FIXPATH = $(subst /,\,$1)
RM := del /q /f
MD := mkdir
else
MAIN := main
SOURCEDIRS := $(shell find $(SRC) -type d)
INCLUDEDIRS := $(shell find $(INCLUDE) -type d)
LIBDIRS := $(shell find $(LIB) -type d)
FIXPATH = $1
RM = rm -f
MD := mkdir -p
endif
INCLUDES := $(patsubst %,-I%, $(INCLUDEDIRS:%/=%))
INCLUDES += -IC:/Users/kimda/Desktop/Projects/C++/Libraries/glm
INCLUDES += -IC:/mingw64/x86_64-w64-mingw32/include
INCLUDES += -IC:/Users/kimda/Desktop/Projects/C++/OpenGL/src/vendor/imGui
LIBS := $(patsubst %,-L%, $(LIBDIRS:%/=%))
LIBS += -lglew32
LIBS += -lopengl32
LIBS += -lglfw3
LIBS += -lgdi32
LIBS += -lglu32
SOURCES := $(wildcard $(patsubst %,%/*.cpp, $(SOURCEDIRS)))
OBJECTS := $(SOURCES:.cpp=.o)
OUTPUTMAIN := $(call FIXPATH,$(OUTPUT)/$(MAIN))
all: $(OUTPUT) $(MAIN)
#echo Building complete!
$(OUTPUT):
#$(MD) $(OUTPUT)
$(MAIN): $(OBJECTS)
#$(CXX) $(CXXFLAGS) $(INCLUDES) -o $(OUTPUTMAIN) $(OBJECTS) $(LFLAGS) $(LIBS)
.cpp.o:
#$(CXX) $(CXXFLAGS) $(INCLUDES) -c $< -o $#
.PHONY: clean
clean:
#$(RM) $(OUTPUTMAIN)
#$(RM) $(call FIXPATH,$(OBJECTS))
#echo Cleanup complete!
run: clean all
#./$(OUTPUTMAIN)
#echo Executing complete!
If I understand you correctly you want to send all object files to some 'build' directory.
I usually do something like this
BUILDDIR := build
[...]
OBJECTS := $(addprefix $(BUILDDIR)/,$(SOURCES:.cpp=.o))
[...]
and then change your
.cpp.o:
#$(CXX) $(CXXFLAGS) $(INCLUDES) -c $< -o $#
to
$(BUILDDIR)/%.o: %.cpp
#$(MD) $(dir $#)
#$(CXX) $(CXXFLAGS) $(INCLUDES) -c $< -o $#
When I run make, I get the following error:
make: *** No rule to make target Menu.h', needed by Menu.o'. Stop.
Here is my Makefile:
PROG = sim
CURR_PATH = ~/Projects/restaurant/cpp/
CC = g++
CPPFLAGS = -g -v -Wall $(LOCAL_INCLUDES) -I$(BOOST_ROOT)
ODIR = ./bin
SDIR = ./src
LOCAL_INCLUDES = $(patsubst %,-I$(CURR_PATH)src/%,$(PKG_DIRS))
PKG_DIRS = $(shell ls $(SDIR))
FIND_SRC_FILES = $(notdir $(wildcard $(SDIR)/$(pkg)/*.cpp))
SRC_FILES = $(foreach pkg,$(PKG_DIRS),$(FIND_SRC_FILES))
OBJ_FILES = $(patsubst %.cpp,%.o,$(SRC_FILES))
MAIN_OBJ = main.o
.PHONY : prog
prog : $(PROG)
all : ; $(info $$CPPFLAGS is [${CPPFLAGS}])#echo Hello world
$(PROG) : $(OBJ_FILES)
$(CC) $(CPPFLAGS) -o $(PROG) $(MAIN_OBJ)
%.o : %.cpp
$(CC) $(CPPFLAGS) -c $< -o $#
$(OBJ_FILES) : %.o : %.h
$(CC) $(CPPFLAGS) -c $(patsubst %.h,%.cpp,$<) -o $#
BTW, in case you're wondering what LOCAL_INCLUDES looks like, the output for the 'all' recipe is the following:
$CPPFLAGS is [-g -v -Wall -I~/Projects/restaurant/cpp/src/concurrent -I~/Projects/restaurant/cpp/src/containers -I~/Projects/restaurant/cpp/src/data -I~/Projects/restaurant/cpp/src/loader -I~/Projects/restaurant/cpp/src/main -I~/Projects/restaurant/cpp/src/people -I~/Projects/restaurant/cpp/src/sim -I/usr/local/boost_1_72_0]
Hello world
Sorry for the single line output, I am unaware of how to format in a more readable fashion. But as you can see, the directory data, which contains Menu.h, is being properly included. But for some reason, make is unable to find it. What could possibly be going wrong here?
Let me know if you need more information.
Cheers
The compiler knows how to find that header file. Make does not know how to find it, and Make is the one producing that error message.
I suggest you make this modification:
INCLUDE_DIRS = $(addprefix $(CURR_PATH)src/,$(PKG_DIRS))
LOCAL_INCLUDES = $(addprefix -I,$(INCLUDE_DIRS))
vpath %.h $(INCLUDE_DIRS)
(P.S. Your use of CURR_PATH and . is confusing, and data/ is a terrible place to put header files.)
EDIT:
All right, let's take this in stages. Step 1, try this makefile:
OBJ_FILES = Menu.o
INCLUDE_DIRS = ~/Projects/restaurant/cpp/src/data
vpath %.h $(INCLUDE_DIRS)
Menu.o:
$(OBJ_FILES) : %.o : %.cpp %.h
#echo building $# from $^
and tell us what happens. (If it doesn't work, tell us what happens.)
As aforementioned, make's vpath did not like the absolute path that it was being given. See below for my modified Makefile:
PROG = sim
CURR_PATH = ~/Projects/restaurant/cpp/
CC = g++
CPPFLAGS = -g -v -Wall $(LOCAL_INCLUDES) -I$(BOOST_ROOT)
ODIR = bin
# vpath only needs SDIR and PKG_DIRS!
SDIR = src
PKG_DIRS = $(shell ls $(SDIR))
INCLUDE_DIRS = $(addprefix $(CURR_PATH)src/,$(PKG_DIRS))
LOCAL_INCLUDES = $(addprefix -I,$(INCLUDE_DIRS))
FIND_SRC_FILES = $(notdir $(wildcard $(SDIR)/$(pkg)/*.cpp))
SRC_FILES = $(foreach pkg,$(PKG_DIRS),$(FIND_SRC_FILES))
OBJ_FILES = $(patsubst %.cpp,%.o,$(SRC_FILES))
MAIN_OBJ = main.o
vpath %.h $(addprefix $(SDIR)/,$(PKG_DIRS))
.PHONY : all
prog : $(PROG)
all : ; $(info $$CPPFLAGS is [${CPPFLAGS}])#echo Hello world
$(PROG) : $(OBJ_FILES)
$(CC) $(CPPFLAGS) -o $(PROG) $(MAIN_OBJ)
%.o : %.cpp
$(CC) $(CPPFLAGS) -c $< -o $#
$(OBJ_FILES) : %.o : %.h
$(CC) $(CPPFLAGS) -c $(patsubst %.h,%.cpp,$<) -o $#
I hope this helps other people that come across similar issues.
I have the following makefile and would like to also use the GSL library. Do you know how I should proceed?
I cannot find the right way to make my code compile for now.
INCLDIR := include
OBJDIR := obj
SRCDIR := src
BINDIR := bin
CC := g++
VPATH :=
LDFLAGS := -L/home/path/gsl/lib
LIBRARY :=
CFLAGS := -g -Wall -I $(INCLDIR)
#Source and object files (automatic)
SRCS = $(wildcard $(SRCDIR)/*.cpp)
OBJS = $(subst $(SRCDIR)/,$(OBJDIR)/, $(subst .cpp,.o, $(SRCS)))
# Define here your main source files separated by spaces (without suffix!)
EXEC = main
#Phony = do not represent a file
#.PHONY: all
all : makedir $(EXEC)
# For multiple binaries
$(EXEC) : %: %.cpp $(OBJS)
$(CC) $(CFLAGS) -o $(BINDIR)/$# $^
$(OBJDIR)/%.o : $(SRCDIR)/%.cpp
$(CC) $(CFLAGS) -c -o $# $<
#Clean: delete every binaries and object files
.PHONY: clean
clean :
rm -rf $(OBJDIR)/*
rm -rf $(BINDIR)/*
#Building folders (-p : no error if folder do not exist)
.PHONY: makedir
makedir :
mkdir -p $(BINDIR)
mkdir -p $(OBJDIR)
Your LDFLAGS is missing -lgsl. Thus far you have only told the linker, where to search in addition to the default directories.
I have seen some examples of using makefile with multiple source directories but still could not implement it to my makefile.
Currently I have all source files at /src/ folder and I would like to be able compile subfolders in /src/ such as /src/modules.
I am using:
NAME = 'AI'
SRC = src
TGT = obj
PRG = application
INCLUDES = -Iinclude ...
LIBRARIES = -L ...
CXXFLAGS = -O2 -std=c++0x $(INCLUDES) $(LIBRARIES)
SOURCES = $(wildcard $(SRC)/*.cpp)
ENDFLAGS = -ljvm -lcurl
NOWARNINGS = -Wno-unused-variable -Wno-unused-result
OBJS = $(addprefix $(TGT)/, $(notdir $(SOURCES:.cpp=.o)))
$(TGT)/%.o: $(SRC)/%.cpp
$(CXX) $(CXXFLAGS) -c $< -o $# $(NOWARNINGS)
$(PRG)/$(NAME): $(OBJS)
$(CXX) $(LIBRARIES) $(OBJS) -o $# $(ENDFLAGS) $(NOWARNINGS)
I am trying to build excutables for multiple files which are built in the same way. When i run make all the excutables should be generated. I am getting error at prerequisites part of the macro.
CXX = g++
CXX_FLAGS = -g -Wall
LD_FLAGS =
INC_DIR = -I/my/path/include
SRC_DIR = .
LIB_DIR = -L$/my/path/lib
OBJ_DIR = obj
EXE_DIR = exe
SRCS := $(foreach s_dir, $(SRC_DIR), $(wildcard $(s_dir)/*.cpp))
OBJS := $(patsubst $(SRC_DIR)/%.cpp, $(OBJ_DIR)/%.o, $(SRCS))
EXES := $(patsubst $(SRC_DIR)/%.cpp, $(EXE_DIR)/%.out, $(SRCS))
all: create_directories create_objects create_exes
create_directories:
#echo "Creating $(OBJ_DIR) and $(EXE_DIR)..."
#mkdir -p obj
#mkdir -p exe
create_objects:
$(foreach b_dir, $(OBJ_DIR), $(eval $(call build-objects, $(b_dir))))
create_exes:
$(foreach ot, $(EXE_DIR), $(eval $(call build-exes, $(ot))))
define build-objects
$1/%.o: %.cpp
$(CXX) $(CXX_FLAGS) $(INC_DIR) -MMD -MP -c $$< -o $$#
endef
define build-exes
$1/%.out:obj/%.o
$(CXX) $(LD_FLAGS) -o $# $(OBJS) $(LIB_DIR) -lmylib
endef
Is this a right way to do generate multiple exes or any other simple way?
If I'm reading this makefile right, then it's much too complicated.
First let's have a rule to build object files:
$(OBJ_DIR)/%.o: %.cpp
$(CXX) $(CXX_FLAGS) $(INC_DIR) -MMD -MP -c $< -o $#
Now if we're not sure about the existence of obj/, we could add a rule to create it, but for the moment let's just put in a failsafe (we'll come back to this later):
$(OBJ_DIR)/%.o: %.cpp
#mkdir -p $(OBJ_DIR)
$(CXX) $(CXX_FLAGS) $(INC_DIR) -MMD -MP -c $< -o $#
And a similar rule to build the executables:
$(EXE_DIR)/%.out: $(OBJ_DIR)/%.o
#mkdir -p $(EXE_DIR)
$(CXX) $(LD_FLAGS) -o $# $^ $(LIB_DIR) -lmylib
And finally (at the top) some variables, the lists of files, and the all rule:
CXX = g++
CXX_FLAGS = -g -Wall
LD_FLAGS =
INC_DIR = -I/my/path/include
SRC_DIR = .
LIB_DIR = -L$/my/path/lib
OBJ_DIR = obj
EXE_DIR = exe
SRCS := $(wildcard $(SRC_DIR)/*.cpp)
EXES := $(patsubst $(SRC_DIR)/%.cpp, $(EXE_DIR)/%.out, $(SRCS))
# Let the object files take care of themselves
all: $(EXES)
That's all you need. Once this is working perfectly, we can discuss refinements like rules for building directories.