C++ Makefile, is it possible to factorize it even more? - c++

I have a project for school and I want to write a Makefile, I have seen some examples of using Makefile with multiple source directories and multiple executables but still could not implement it properly to my Makefile.
PS: I'm using doctest for the unit testing (and I can't change it).
Here is the project structure (and I can't change it):
.
├── bin
├── build
├── extern
│ └── doctest.h
├── include
│ ├── file1.hpp
│ └── file2.hpp
├── src
│ ├── file1.cpp
│ └── file2.cpp
├── tests
│ ├── file1-test.cpp
│ └── file2-test.cpp
└── Makefile
I have the following directories:
bin: for all the executables.
build: for all the objects (.o).
extern: for the doctest header (this is where I would have stored any other library)
include: for all the headers (.hpp).
src: for all the classes (.cpp).
tests: for all the unit tests (also .cpp)
You can see file1.cpp as a class, file1.hpp as the class header and file1-test.cpp as the unit tests for the class.
Here is my Makefile:
BIN_DIR := bin/
BUILD_DIR := build/
EXTERN_DIR := extern/
INCLUDE_DIR := include/
SOURCE_DIR := src/
TESTS_DIR := tests/
DEP_DIR := .dep/
DEPENDS := $(patsubst %.o, $(BUILD_DIR)$(DEP_DIR)%.d, $(notdir $(wildcard $(BUILD_DIR)*.o)))
EXE := $(addprefix $(BIN_DIR), file1-test file2-test)
OBJS_1 := $(addprefix $(BUILD_DIR), file1.o)
OBJS_2 := $(addprefix $(BUILD_DIR), file1.o file2.o)
CXX := clang++
CXXFLAGS := -Wall -std=c++11 -g -O3 -I$(INCLUDE_DIR) -I$(EXTERN_DIR)
vpath %.cpp $(SOURCE_DIR) $(TESTS_DIR)
.PHONY: all clean
all: $(EXE)
$(BUILD_DIR) $(BIN_DIR) $(BUILD_DIR)$(DEP_DIR):
#mkdir -p $#
$(BUILD_DIR)%.o: %.cpp | $(BUILD_DIR) $(BUILD_DIR)$(DEP_DIR)
#$(CXX) $(CXXFLAGS) -MMD -MP -MF $(BUILD_DIR)$(DEP_DIR)$(notdir $(basename $#).d) -c $< -o $#
$(BIN_DIR)%: $(BUILD_DIR)%.o | $(BIN_DIR)
#$(CXX) -o $# $^
$(BIN_DIR)file1-test: $(OBJS_1)
$(BIN_DIR)file2-test: $(OBJS_2)
.PRECIOUS: $(BUILD_DIR)%.o
-include $(DEPENDS)
clean:
-rm -rf $(BIN_DIR) $(BUILD_DIR)
So my questions are:
Is my Makefile following good practices ?
Is it optimized ? If no, how can I make it even better ?
For every new executable I've to add a OBJS_X variable and a target $(BIN_DIR)fileX-test: $(OBJS_X), can i get rid of it ? If yes can someone write me some generic rule, so I don't have to specify a variable and a target every time I want a new executable.
If I want to compile only one executable I have to use make bin/fileX-test. Is it possible to run only make fileX-test instead of make bin/fileX-test (but still building it in the bin directory) ? I tried to implement a rule like this: fileX-test: $(BIN_DIR)fileX-test but it's not working as I want, at the very end of the compilation it starts executing builtin rules and I don't know why. Can someone explain ?
Final answer:
This is what I considere a good answer, if it can help someone later:
BIN_DIR := bin/
BUILD_DIR := build/
EXTERN_DIR := extern/
INCLUDE_DIR := include/
SOURCE_DIR := src/
TESTS_DIR := tests/
DEP_DIR := $(BUILD_DIR).dep/
CXX := g++
CXXFLAGS := -Wall -std=c++11 -g -O3 -I$(INCLUDE_DIR) -I$(EXTERN_DIR)
DEPFLAGS := -MMD -MP -MF $(DEP_DIR)
vpath %.cpp $(SOURCE_DIR) $(TESTS_DIR)
file1-test_OBJECTS := $(addprefix $(BUILD_DIR), file1.o)
file2-test_OBJECTS := $(addprefix $(BUILD_DIR), file1.o file2.o)
EXE := $(patsubst %_OBJECTS, %, $(filter %_OBJECTS, $(.VARIABLES)))
.PHONY: all keep help check clean $(EXE)
all: $(EXE:%=$(BIN_DIR)%)
$(foreach E, $(EXE), $(eval $(BIN_DIR)$E: $($E_OBJECTS)))
$(foreach E, $(EXE), $(eval $E: $(BIN_DIR)$E ;))
$(BUILD_DIR) $(BIN_DIR) $(DEP_DIR):
#mkdir -p $#
$(BUILD_DIR)%.o: %.cpp | $(BUILD_DIR) $(DEP_DIR) $(BIN_DIR)
#$(CXX) $(CXXFLAGS) $(DEPFLAGS)$(#F:.o=.d) -c $< -o $#
$(BIN_DIR)%: $(BUILD_DIR)%.o
#$(CXX) -o $# $^
-include $(wildcard $(DEP_DIR)*.d)
keep: $(EXE:%=$(BUILD_DIR)%.o)
clean:
-#rm -rf $(BIN_DIR)* $(BUILD_DIR)* $(DEP_DIR)*

Mostly your makefile is pretty good. There are some simplifications you can make, but they're just syntax and not really performance etc.:
DEP_DIR := .dep/
You never use this by itself so if you change its definition to:
DEP_DIR := $(BUILD_DIR).dep/
you can simplify the references to it.
DEPENDS := $(patsubst %.o, $(BUILD_DIR)$(DEP_DIR)%.d, $(notdir $(wildcard $(BUILD_DIR)*.o)))
-include $(DEPENDS)
this seems complex. Why not get rid of DEPENDS and just write:
include $(wildcard $(DEP_DIR)*.d)
This:
#$(CXX) $(CXXFLAGS) -MMD -MP -MF $(BUILD_DIR)$(DEP_DIR)$(notdir $(basename $#).d) -c $< -o $#
is also complex. You can write it (if you simply DEP_DIR) as:
#$(CXX) $(CXXFLAGS) -MMD -MP -MF $(DEP_DIR)$(#F:.o=.d) -c $< -o $#
For:
.PRECIOUS: $(BUILD_DIR)%.o
I would definitely NOT use this. .PRECIOUS should be rarely, if ever, used. If you're trying to avoid object files being considered intermediate it's best to just list them directly as prerequisites, such as:
keep : $(EXE:$(BIN_DIR)%=$(BUILD_DIR)%.o)
But unless you have special need to look at these object files it doesn't hurt to let make delete them.
Regarding your question about shortcuts: the reason you see the behavior you do is that your target definition:
fileX-test: $(BIN_DIR)fileX-test
has no recipe attached to it, so make will try to find a recipe using an implicit rule. It finds built-in recipe for % : %.c, and because you set vpath it can find a %.c file that matches, so it uses it. To avoid this you can just give an empty recipe; replace the above with:
fileX-test: $(BIN_DIR)fileX-test ;
(note added semicolon).
Your main question is how to simplify this:
EXE := $(addprefix $(BIN_DIR), file1-test file2-test)
OBJS_1 := $(addprefix $(BUILD_DIR), file1.o)
OBJS_2 := $(addprefix $(BUILD_DIR), file1.o file2.o)
all: $(EXE)
$(BIN_DIR)file1-test: $(OBJS_1)
$(BIN_DIR)file2-test: $(OBJS_2)
You can do this automatically but doing so requires knowing the deeper parts of GNU make. You might find this set of blog posts interesting: http://make.mad-scientist.net/category/metaprogramming/ (start with the bottom / oldest and work your way up).
Replace the above with:
# Write one of these for each program you need:
file1-test_OBJECTS = file1.o
file2-test_OBJECTS = file1.o file2.o
# Now everything below here is boilerplate
EXE = $(patsubst %_OBJECTS,%,$(filter %_OBJECTS,$(.VARIABLES)))
all: $(EXE:%=$(BIN_DIR)%)
$(foreach E,$(EXE),$(eval $(BIN_DIR)$E: $($E_OBJECTS)))
$(foreach E,$(EXE),$(eval $E: $(BIN_DIR)$E ;))
.PHONY: $(EXE)

I am turning my comment into an answer to allow others to disapprove this view: I think CMake is better here for you. Look at this SO for some differences between Make and CMake and arguments for CMake.
Advantages related to your questions:
It will allow you more easily to follow good practices
It scales much better
You do not have to write so muc boilerplate for new executable added to your code
Building a single executable is possible, see this SO as a hint.

Related

Makefile target with wildcard is not working

I have a simple project, whose folder structure is something like:
ls -R
.:
build include makefile src
./build:
./include:
myfunc.h
./src:
main.cpp myfunc.cpp
I want to compile the .cpp sources into .o object files, which should end into ./build folder. Using the GNUmake documentation and other sources (e.g. Proper method for wildcard targets in GNU Make), I wrote this makefile:
CXX := g++
CXXFLAGS += -I./include
CXXFLAGS += -Wall
OBJDIR := ./build
SRCDIR := ./src
PROGRAM = release
DEPS = myfunc.h
SRC = $(wildcard $(SRCDIR)/*.cpp)
OBJ = $(patsubst $(SRCDIR)/%.cpp, $(OBJDIR)/%.o, $(SRC))
all: $(PROGRAM)
$(PROGRAM): $(OBJ)
$(CXX) $(CXXFLAGS) -o $(PROGRAM) $(OBJ)
$(OBJDIR)/%.o: $(SRCDIR)/%.cpp $(DEPS)
$(CXX) $(CXXFLAGS) -c $< -o $#
.PHONY: clean
clean:
rm $(PROGRAM) $(OBJ)
But I get the error message: make: *** No rule to make target 'build/main.o', needed by 'release'. Stop.. I tried a lot of different ways but I cannot manage to have my .o files end up in the ./build directory. Instead, everything works if I put them in the root directory of the project. I can also make it work by specifying a rule for each object file, but I'd like to avoid that. What am I missing?
(I am using GNUmake version 4.3)
The problem is here:
$(OBJDIR)/%.o: $(SRCDIR)/%.cpp $(DEPS)
$(CXX) $(CXXFLAGS) -c $< -o $#
See the $(DEPS)? That expands to myfunc.h. The compiler knows where to find that file (or would if this recipe were executed), because you've given it -I./include, but Make doesn't know where to find it (so it passes over this rule).
Add this line:
vpath %.h include
P.S. If you want to be really clean, you can add a variable:
INCDIR := ./include
CXXFLAGS += -I$(INCDIR)
vpath %.h $(INCDIR)

Many folders project Makefile

I have the following project structure:
common
|-- foo.cpp
|-- foo.h
exercise_1
|-- main.cpp
|-- bar_1.cpp
|-- bar_1.h
exercise_2
|-- main.cpp
|-- bar_2.cpp
|-- bar_2.h
...
How can one organize Makefile to build such project from the main directory e.g.:
make exercise_10
So that this command would build object files in common directory, in exercise_10 folder and link them all to executable in exercise_10. I started with the following:
COMPILER = g++
INCLUDE = -I common/
DEPS = common/*.o
OBJECTS := $(patsubst common/%.cpp, common/%.o, $(wildcard common/*.cpp))
common: $(OBJECTS)
exercise_%:
$(COMPILER) $#/main.cpp $(INCLUDE) -o $#/main $(DEPS)
But it's not working and I don't know what to do next.
Thanks!
If you use GNU make you could define a macro to build any of your exercises. Something like the following:
EXERCISES := $(wildcard exercise_*)
MAINS := $(addsuffix /main,$(EXERCISES))
.PHONY: all
all: $(MAINS)
common-objs := $(patsubst %.cpp,%.o,$(wildcard common/*.cpp))
common-headers := $(wildcard common/*.h)
%.o: %.cpp $(common-headers)
$(CXX) $(CPPFLAGS) $(CXXFLAGS) -Icommon -c $< -o $#
# $(1): exercise directory
define BUILD_EXERCISE
.PHONY: $(1)
$(1): $(1)/main
$(1)-objs := $$(patsubst %.cpp,%.o,$$(wildcard $(1)/*.cpp))
OBJS += $$($(1)-objs)
$(1)-headers := $$(wildcard $(1)/*.h)
$$($(1)-objs): $$($(1)-headers)
$(1)/main: $$($(1)-objs) $$(common-objs)
$$(CXX) $$(CXXFLAGS) $$(LDFLAGS) -o $$# $$^ $$(LDLIBS)
endef
$(foreach e,$(EXERCISES),$(eval $(call BUILD_EXERCISE,$(e))))
.PHONY: clean
clean:
rm -f $(MAINS) $(OBJS) $(common-objs)
It looks a bit complicated but it's not. The only trick is the $$ in the BUILD_EXERCISE macro. It is needed because the macro is expanded twice by make. Everything else is straightforward:
CXX, CPPFLAGS, CXXFLAGS, LDFLAGS and LDLIBS are Variables Used by Implicit Rules.
$#, $< and $^ are Automatic Variables.
wildcard, addsuffix, patsubst, foreach, eval and call are make functions.
Phony targets are declared as prerequisites of the .PHONY special target.
The foreach-eval-call construct is a way to programmatically instantiate make statements.
%.o: %.cpp... is a pattern rule.

C++ Makefile auto-depencies with multiple executables

I have a project for school and I want to write a Makefile, I have seen some examples of using Makefile with multiple source directories and multiple executables but still could not implement it properly to my Makefile.
PS: I'm using doctest for the unit testing (and I can't change it).
Here is the project structure (and I can't change it):
.
├── bin
├── build
├── extern
│ └── doctest.h
├── include
│ ├── file1.hpp
│ └── file2.hpp
├── src
│ ├── file1.cpp
│ └── file2.cpp
├── tests
│ ├── file1-test.cpp
│ └── file2-test.cpp
└── Makefile
I have the following directories:
bin: for all the executables.
build: for all the objects (.o).
extern: for the doctest header (this is where I would have stored any other library)
include: for all the headers (.hpp).
src: for all the classes (.cpp).
tests: for all the unit tests (also .cpp)
You can see file1.cpp as a class, file1.hpp as the class header and file1-test.cpp as the unit tests for the class.
In the exemple above I have 2 tests files but at the very end of the project I'll have a lot more, and for each test file I'll have an executable.
My goals:
I want to run make and compile all the units tests (all the .cpp in the tests/ directory).
And I want all the executables to be stored in the bin/ directory and all the binary files in the build/ directory.
Here is my Makefile:
BIN_DIR = ./bin/
BUILD_DIR = ./build/
EXTERN_DIR = ./extern/
INCLUDE_DIR = ./include/
SOURCE_DIR = ./src/
TESTS_DIR = ./tests/
vpath %.cpp $(SOURCE_DIR) $(TESTS_DIR)
CXX = clang++
CXXFLAGS = -Wall -std=c++11 -g -O3 -I$(INCLUDE_DIR) -I$(EXTERN_DIR)
EXEC_FILES = file1-test file2-test
BIN = $(addprefix $(BIN_DIR), $(EXEC_FILES))
all: $(BIN) | $(BIN_DIR)
$(BUILD_DIR)%.o: %.cpp | $(BUILD_DIR)
$(CXX) $(CXXFLAGS) -c -o $# $^
$(BIN_DIR) $(BUILD_DIR):
mkdir -p $#
$(BIN_DIR)file1-test: $(BUILD_DIR)file1.o $(BUILD_DIR)file1-test.o
$(CXX) -o $# $^
$(BIN_DIR)file2-test: $(BUILD_DIR)file1.o $(BUILD_DIR)file2.o $(BUILD_DIR)file2-test.o
$(CXX) -o $# $^
clean:
-rm -f $(BIN_DIR)* $(BUILD_DIR)*
It's working well but I feel like it's doing redondant stuff that i could avoid with more knownledge in the Makefile art, especially here:
$(BIN_DIR)file1-test: $(BUILD_DIR)file1.o $(BUILD_DIR)file1-test.o
$(CXX) -o $# $^
$(BIN_DIR)file2-test: $(BUILD_DIR)file1.o $(BUILD_DIR)file2.o $(BUILD_DIR)file2-test.o
$(CXX) -o $# $^
For the moment this Makefile is correct because I only have 2 executables, but I'll end up with 15+ and I dont want to have 15 times this for each executable:
$(BIN_DIR)xxx-test: $(BUILD_DIR)xxx.o etc.
$(CXX) -o $# $^
What I exactly need ...:
Basically, I need to write a generic rule that will fetch all the appropriated dependencies for a given target.
After reading multiple posts I think it's all about auto-dependencies.
I'm pretty sure the final result would look like this, but sadly I can't make it works in my case:
$(BIN_DIR)%: ???
#$(CXX) -o $# $^
I already looked at this (and many other posts about the subject): http://make.mad-scientist.net/papers/advanced-auto-dependency-generation/, but I still can't figure it out.
So how can I write an expression that will do the job, can someone give me a working exemple or something similar ?
EDIT 1:
Based on this post: Makefile (Auto-Dependency Generation).
I added these lines to my Makefile:
SRC = $(wildcard $(SOURCE_DIR)*.cpp)
SRC += $(wildcard $(TESTS_DIR)*.cpp)
The idea is to fetch all the .cpp from the source directories (src and tests). Then I added -MDD option to my CXXFLAGS variable to create a .d file for each target (atleast it's what I thought it's doing):
CXXFLAGS = -Wall -std=c++11 -g -O3 -I$(INCLUDE_DIR) -I$(EXTERN_DIR) -MMD
And finally, I added this:
$(BIN_DIR)%: $(SRC)
$(CXX) -o $# $^
-include $(SRC:.cpp=.d)
What I expect it to do:
Create a .d file with all the dependencies for each target.
Fetch the dependencies in the .d file and transform them to .o to get all the objects needed for the given target.
But it seems that it's not doing what I'm expecting.
EDIT 3:
After some changes I end up with this Makefile:
BIN_DIR := bin/
BUILD_DIR := build/
EXTERN_DIR := extern/
INCLUDE_DIR := include/
SOURCE_DIR := src/
TESTS_DIR := tests/
DEP_DIR := .dep/
DEPENDS := $(patsubst %.o, $(BUILD_DIR)$(DEP_DIR)%.d, $(notdir $(wildcard $(BUILD_DIR)*.o)))
EXE := $(addprefix $(BIN_DIR), Coord-test Fourmi-test)
OBJS_1 := $(addprefix $(BUILD_DIR), Coord.o)
OBJS_2 := $(addprefix $(BUILD_DIR), Coord.o Fourmi.o)
CXX := clang++
CXXFLAGS := -Wall -std=c++11 -g -O3 -I$(INCLUDE_DIR) -I$(EXTERN_DIR)
vpath %.cpp $(SOURCE_DIR) $(TESTS_DIR)
all: $(EXE)
$(BUILD_DIR):
mkdir -p $# $#/$(DEP_DIR)
$(BIN_DIR):
mkdir -p $#
$(BUILD_DIR)%.o: %.cpp | $(BUILD_DIR)
$(CXX) $(CXXFLAGS) -MMD -MP -MF $(BUILD_DIR)$(DEP_DIR)$(notdir $(basename $#).d) -c $< -o $#
$(BIN_DIR)%: $(BUILD_DIR)%.o | $(BIN_DIR)
$(CXX) -o $# $^
$(BIN_DIR)Coord-test: $(OBJS_1)
$(BIN_DIR)Fourmi-test: $(OBJS_2)
.PRECIOUS: $(BUILD_DIR)%.o
-include $(DEPENDS)
clean:
-rm -f $(BIN_DIR)* $(BUILD_DIR)* $(BUILD_DIR)$(DEP_DIR)*
It's working but I'll have to add OBS_X for each new executable.
I also wanted factorize this, but I don't know if it's possible ? If someone could tell me.
$(BIN_DIR)%: $(BUILD_DIR)%.o | $(BIN_DIR)
$(CXX) -o $# $^
$(BIN_DIR)Coord-test: $(OBJS_1)
$(BIN_DIR)Fourmi-test: $(OBJS_2)
Since you know that you will always have a foo-test.o to build a foo-test program, you can write your pattern rule like this:
$(BIN_DIR)%: $(BUILD_DIR)%.o
$(CXX) -o $# $^
However, there's no way make can infer what OTHER objects might be needed to build these executables. You'll just have to tell it. So for the above examples you can add this:
$(BIN_DIR)file1-test: $(BUILD_DIR)file1.o
$(BIN_DIR)file2-test: $(BUILD_DIR)file1.o $(BUILD_DIR)file2.o
You don't need to put the recipe here, this is just adding more prerequisites to these targets. You also don't have to put in the $(BUILD_DIR)file1-test.o etc. because this is inferred from the pattern rule.
But, if you do have other object files you need to use you'll have to list them explicitly, there's no way around it.

makefile compiles all files instead of the last edited ones

Note that there are similar questions on SO, however I think my situation is different, moreover my Makefile is extremely simple and straight forward. I am new to Makefile.
Suppose I need to compile a project, that looks like this --
.
├── [4.0K] bin
├── [ 517] Makefile
├── [4.0K] obj
└── [4.0K] src
├── [ 117] func1.cpp
├── [ 76] func2.cpp
├── [ 137] global.h
└── [ 97] main1.cpp
and my Makefile looks like this --
CC := g++
CFLAGS := -g -Wall -std=c++0x
LDFLAGS := -lm
NAMES := func1.cpp func2.cpp main1.cpp
SRC := $(addprefix src/,$(NAMES))
OBJ := $(addprefix obj/,$(NAMES:.cpp=.o))
DEPS := $(OBJ:.o=.d)
.PHONY: clean all debug
all: prog
debug:
$(info $$SRC: ${SRC})
$(info $$OBJ: ${OBJ})
$(info $$DEPS: ${DEPS})
prog: bin/prog
bin/prog: $(OBJ)
$(CC) $^ -o $#
$(OBJ): $(SRC)
$(CC) $(CFLAGS) -I/src/global.h -c $(addprefix src/,$(notdir $(#:.o=.cpp))) -o $#
-include $(DEPS)
clean:
rm -rf bin/*
rm -rf obj/*
Suppose I opened a file func1.cpp and made some changes. When I invoke make it compiles all files, but it was supposed to compile only one (func1.cpp).
How do I fix this ?
Note: I need prog, bin/prog for a different reason, also I can't do recipes like obj/%.o: src/%.c because I might have different target from the subset of the same objects.
When you write a rule like:
$(OBJ): $(SRC)
cmd
which in your case is
obj/func1.o obj/func2.o obj/main.o : src/func1.cpp src/func2.cpp src/main1.cpp
cmd
The prerequisites don't get zipped across. That generates one rule for each target, with all of the prerequisites. That is:
obj/func1.o : src/func1.cpp src/func2.cpp src/main1.cpp
cmd
obj/func2.o : src/func1.cpp src/func2.cpp src/main1.cpp
cmd
obj/main.o : src/func1.cpp src/func2.cpp src/main1.cpp
cmd
Since src/func1.cpp is a prereq for all of the object files, they all get recompiled.
What you want instead is to use a static pattern rule:
obj/%.o : src/%.cpp
$(CC) $(CFLAGS) -I/src -c $< -o $#
Note that -I is for include directories, not include files.

How to write a Makefile with separate source and header directories?

Following this tutorial...
I have 2 source files and 1 header file. I want to have them in separate directories like in the tutorial.
So I set this project up:
.
├── include
│   └── hellomake.h
├── Makefile
└── src
├── hellofunc.c
└── hellomake.c
Makefile:
IDIR =../include
CC=gcc
CFLAGS=-I$(IDIR)
ODIR=obj
LDIR =../lib
_DEPS = hellomake.h
DEPS = $(patsubst %,$(IDIR)/%,$(_DEPS))
_OBJ = hellomake.o hellofunc.o
OBJ = $(patsubst %,$(ODIR)/%,$(_OBJ))
$(ODIR)/%.o: %.c $(DEPS)
$(CC) -c -o $# $< $(CFLAGS)
hellomake: $(OBJ)
gcc -o $# $^ $(CFLAGS)
.PHONY: clean
clean:
rm -f $(ODIR)/*.o *~ core $(INCDIR)/*~
The error I generate says:
gcc -o hellomake -I../include
gcc: fatal error: no input files
compilation terminated.
make: *** [hellomake] Error 4
What's happening?
Your tutorial promotes old and bad practices, you should avoid it IMHO.
In your rule here:
$(ODIR)/%.o: %.c $(DEPS)
You're telling make to look for sources in the current directory while they actually reside in the src directory, thus this pattern is never used and you have no suitable one.
Make sure you organize your project directory like this :
root
├── include/
│ └── all .h files here
├── lib/
│ └── all third-party library files (.a/.so files) here
├── src/
│ └── all .c files here
└── Makefile
Then let's take the process step by step, using good practices.
Firstly, don't define anything if you don't need to. Make has a lot of predefined variables and functions that you should use before trying to do it manually. In fact, he has so many that you can compile a simple file without even having a Makefile in the directory at all!
List your source and build output directories:
SRC_DIR := src
OBJ_DIR := obj
BIN_DIR := bin # or . if you want it in the current directory
Name your final target, that is, your executable:
EXE := $(BIN_DIR)/hellomake
List your source files:
SRC := $(wildcard $(SRC_DIR)/*.c)
From the source files, list the object files:
OBJ := $(SRC:$(SRC_DIR)/%.c=$(OBJ_DIR)/%.o)
# You can also do it like that
OBJ := $(patsubst $(SRC_DIR)/%.c, $(OBJ_DIR)/%.o, $(SRC))
Now let's handle the flags
CPPFLAGS := -Iinclude -MMD -MP # -I is a preprocessor flag, not a compiler flag
CFLAGS := -Wall # some warnings about bad code
LDFLAGS := -Llib # -L is a linker flag
LDLIBS := -lm # Left empty if no libs are needed
(CPP stands for C PreProcessor here, not CPlusPlus! Use CXXFLAGS for C++ flags and CXX for C++ compiler.)
The -MMD -MP flags are used to generate the header dependencies automatically. We will use this later on to trigger a compilation when only a header changes.
Ok, time to roll some recipes now that our variables are correctly filled.
It is widely spread that the default target should be called all and that it should be the first target in your Makefile. Its prerequisites shall be the target you want to build when writing only make on the command line:
all: $(EXE)
One problem though is Make will think we want to actually create a file or folder named all, so let's tell him this is not a real target:
.PHONY: all
Now list the prerequisites for building your executable, and fill its recipe to tell make what to do with these:
$(EXE): $(OBJ)
$(CC) $(LDFLAGS) $^ $(LDLIBS) -o $#
(CC stands for C Compiler.)
Note that your $(BIN_DIR) might not exist yet so the call to the compiler might fail. Let's tell make that you want it to check for that first:
$(EXE): $(OBJ) | $(BIN_DIR)
$(CC) $(LDFLAGS) $^ $(LDLIBS) -o $#
$(BIN_DIR):
mkdir -p $#
Some quick additional notes:
$(CC) is a built-in variable already containing what you need when compiling and linking in C
To avoid linker errors, it is strongly recommended to put $(LDFLAGS) before your object files and $(LDLIBS) after
$(CPPFLAGS) and $(CFLAGS) are useless here, the compilation phase is already over, it is the linking phase here
Next step, since your source and object files don't share the same prefix, you need to tell make exactly what to do since its built-in rules don't cover your specific case:
$(OBJ_DIR)/%.o: $(SRC_DIR)/%.c
$(CC) $(CPPFLAGS) $(CFLAGS) -c $< -o $#
Same problem as before, your $(OBJ_DIR) might not exist yet so the call to the compiler might fail. Let's update the rules:
$(OBJ_DIR)/%.o: $(SRC_DIR)/%.c | $(OBJ_DIR)
$(CC) $(CPPFLAGS) $(CFLAGS) -c $< -o $#
$(BIN_DIR) $(OBJ_DIR):
mkdir -p $#
Ok, now the executable should build nicely. We want a simple rule to clean the build artifacts though:
clean:
#$(RM) -rv $(BIN_DIR) $(OBJ_DIR) # The # disables the echoing of the command
(Again, clean is not a target that needs to be created, so add it to the .PHONY special target!)
Last thing. Remember about the automatic dependency generation? GCC and Clang will create .d files corresponding to your .o files, which contains Makefile rules for us to use, so let's include that in here:
-include $(OBJ:.o=.d) # The dash silences errors when files don't exist (yet)
Final result:
SRC_DIR := src
OBJ_DIR := obj
BIN_DIR := bin
EXE := $(BIN_DIR)/hellomake
SRC := $(wildcard $(SRC_DIR)/*.c)
OBJ := $(SRC:$(SRC_DIR)/%.c=$(OBJ_DIR)/%.o)
CPPFLAGS := -Iinclude -MMD -MP
CFLAGS := -Wall
LDFLAGS := -Llib
LDLIBS := -lm
.PHONY: all clean
all: $(EXE)
$(EXE): $(OBJ) | $(BIN_DIR)
$(CC) $(LDFLAGS) $^ $(LDLIBS) -o $#
$(OBJ_DIR)/%.o: $(SRC_DIR)/%.c | $(OBJ_DIR)
$(CC) $(CPPFLAGS) $(CFLAGS) -c $< -o $#
$(BIN_DIR) $(OBJ_DIR):
mkdir -p $#
clean:
#$(RM) -rv $(BIN_DIR) $(OBJ_DIR)
-include $(OBJ:.o=.d)
the make utility, with no specific 'target' will make the first target in the file.
The first target is usually named 'all'
For the posted file, will make the object files and will not continue to make the executable when the target is not given in the command line
Suggest the following:
SHELL := /bin/sh
# following so could define executable name on command line
# using the '-D' parameter
#ifndef $(NAME)
NAME := hellomake
#endif
# use ':=' so macros only evaluated once
MAKE := /usr/bin/make
CC := /usr/bin/gcc
CFLAGS := -g -Wall -Wextra -pedantic
LFLAGS :=
ODIR := obj
IDIR := ../include
LIBS :=
LIBPATH := ../lib
DEPS := $(wildcard $(IDIR)/*.h)
SRCS := $(wildcard *.c)
OBJS := $(SRCS:.c=.o)
.PHONY: all
all: $(NAME) $(OBJS)
$(ODIR)/%.o: %.c $(DEPS)
$(CC) $(CFLAGS) -c -o $# $< -I$(DEPS)
$(NAME): $(OBJS)
$(CC) $(LFLAGS) -o $# $^ -L$(LIBPATH) -l$(LIBS)
.PHONY: clean
clean:
rm -f $(ODIR)/*.o
rm -f $(NAME)
however, in your proposed project,
not every source file needs every header file
so should use either gcc or sed to generate the dependency files
then use makefile rules similar to the following,
which may need a little 'tweaking' for your project
because the include files are not in the same directory
as the source files:
DEP := $(SRCS:.c=.d)
#
#create dependency files
#
%.d: %.c
#
# ========= START $< TO $# =========
$(CC) -M $(CPPFLAGS) $< > $#.$$$$; \
sed 's,\($*\)\.o[ :]*,\1.o $# : ,g' < $#.$$$$ > $#; \
rm -f $#.$$$$
# ========= END $< TO $# =========
#
# compile the .c files into .o files using the compiler flags
#
%.o: %.c %.d
#
# ========= START $< TO $# =========
$(CC) $(CCFLAGS) -c $< -o $# -I$(IDIR)
# ========= END $< TO $# =========
#
# include the contents of all the .d files
# note: the .d files contain:
# <filename>.o:<filename>.c plus all the dependencies for that .c file
# I.E. the #include'd header files
# wrap with ifneg... so will not rebuild *.d files when goal is 'clean'
#
ifneq "$(MAKECMDGOALS)" "clean"
-include $(DEP)
endif
The simple Makefile definitions seem OK to me as they appear in your question. Try specifying the compiler options before the file names:
$(ODIR)/%.o: %.c $(DEPS)
$(CC) $(CFLAGS) -c -o $# $<
hellomake: $(OBJ)
gcc $(CFLAGS) -o $# $^
You need to run make from the source directory.
When you got this error"
*gcc: fatal error: no input files
compilation terminated.*
", that means you do not have object files,
just check out that line "${OBJS} := " in Makefile.
Hi, bro!
If your project "helloFunc" 's architecture are just liking this:
helloFunc
|
|__Makefile
|__build
|__include
| |__hellomake.h
|__src
|__hellofunc.cpp
|__hellomake.cpp
your Makefile should be just like this:
# This is a Makefile for separated multiple sources to build with VSCode on mac
# Thanks, Job Vranish.
# (https://spin.atomicobject.com/2016/08/26/makefile-c-projects/)
# Reference: Makefile Tutorial
# (https://makefiletutorial.com/)
# Reference: #yagiyuki from Qiita
# (https://qiita.com/yagiyuki/items/ff343d381d9477e89f3b)
# Reference: simonsso from Github
# (https://github.com/simonsso/empty-cpp-project/blob/master/Makefile)
# Reference: Chinese Website blogger CDNS
# (https://blog.csdn.net/qq_22073849/article/details/88893201)
# (1)Compiler
# clang++
CXX = clang++
# (2)Compile options
# -Wall -Wextra -std=c++11 -g
CXX_FLAGS = -Wall -Wextra -std=c++11 -g
# (3)Build task directory path
# I do care about out-of-source builds
# ./build
BUILD_DIR ?= ./build
# (4)Source files directory path
# ./src
SRC_DIRS ?= ./src
# (5)Library files directory path
LIBDIR :=
# (6)Add library files
LIBS :=
# (7)Target file, excutable file.
# main
TARGET ?= main
# (8)Source files(code), to be compiled
# Find source files we want to compile
# *expression must around by single quotos
# ./src/bank.cpp ./src/main.cpp
SRCS := $(shell find $(SRC_DIRS) -name '*.cpp' -or -name '*.c' -or -name '*.s')
# (9)Object files
# String substituion for every C/C++ file
# e.g: ./src/bank.cpp turns into ./build/bank.cpp.o
# ./build/bank.cpp.o ./build/main.cpp.o
OBJS := $(patsubst %.cpp, ${BUILD_DIR}/%.cpp.o, $(notdir $(SRCS)))
# (10)Dependency files
# which will generate a .d file next to the .o file. Then to use the .d files,
# you just need to find them all:
#
DEPS := $(OBJS:.o=.d)
# (11)Include files directory path
# Every folder in ./src find include files to be passed via clang
# ./include
INC_DIRS := ./include
# (12)Include files add together a prefix, clang make sense that -I flag
INC_FLAGS := $(addprefix -I,$(INC_DIRS))
# (13)Make Makefiles output Dependency files
# That -MMD and -MP flags together to generate Makefiles
# That generated Makefiles will take .o as .d to the output
# That "-MMD" and "-MP" To generate the dependency files, all you have to do is
# add some flags to the compile command (supported by both Clang and GCC):
CPP_FLAGS ?= $(INC_FLAGS) -MMD -MP
# (14)Link: Generate executable file from object file
# make your target depend on the objects files:
${BUILD_DIR}/${TARGET} : $(OBJS)
$(CXX) $(OBJS) -o $#
# (15)Compile: Generate object files from source files
# $# := {TARGET}
# $< := THE first file
# $^ all the dependency
# C++ Sources
$(BUILD_DIR)/%.cpp.o: $(SRC_DIRS)/%.cpp
$(MKDIR_P) $(dir $#)
$(CXX) $(CPP_FLAGS) $(CXX_FLAGS) -c $< -o $#
#(16)Delete dependence files, object files, and the target file
.PHONY: all clean
all: ${BUILD_DIR}/${TARGET}
clean:
$(RM) $(DEPS) $(OBJS) ${BUILD_DIR}/${TARGET}
-include $(DEPS)
MKDIR_P ?= mkdir -p
Changing that Makefile to your needed Linux version:
# (1)Compiler
# g++
CXX = g++
# (2)Compile options
# -Wall -Wextra -std=c++11 -g
CXX_FLAGS = -Wall -Wextra -std=c++11 -g
# (3)Build task directory path
# I do care about out-of-source builds
# ./build
BUILD_DIR ?= ./build
# (4)Source files directory path
# ./src
SRC_DIRS ?= ./src
# (5)Library files directory path
LIBDIR :=
# (6)Add library files
LIBS :=
# (7)Target file, excutable file.
# main
TARGET ?= main
# (8)Source files(code), to be compiled
# Find source files we want to compile
# *expression must around by single quotos
# ./src/bank.cpp ./src/main.cpp
SRCS := $(shell find $(SRC_DIRS) -name '*.cpp' -or -name '*.c' -or -name '*.s')
# (9)Object files
# String substituion for every C/C++ file
# e.g: ./src/bank.cpp turns into ./build/bank.cpp.o
# ./build/bank.cpp.o ./build/main.cpp.o
OBJS := $(patsubst %.cpp, ${BUILD_DIR}/%.cpp.o, $(notdir $(SRCS)))
# (10)Dependency files
# which will generate a .d file next to the .o file. Then to use the .d files,
# you just need to find them all:
#
DEPS := $(OBJS:.o=.d)
# (11)Include files directory path
# Every folder in ./src find include files to be passed via clang
# ./include
INC_DIRS := ./include
# (12)Include files add together a prefix, gcc make sense that -I flag
INC_FLAGS := $(addprefix -I,$(INC_DIRS))
# (13)Make Makefiles output Dependency files
# That -MMD and -MP flags together to generate Makefiles
# That generated Makefiles will take .o as .d to the output
# That "-MMD" and "-MP" To generate the dependency files, all you have to do is
# add some flags to the compile command (supported by both Clang and GCC):
CPP_FLAGS ?= $(INC_FLAGS) -MMD -MP
# (14)Link: Generate executable file from object file
# make your target depend on the objects files:
${BUILD_DIR}/${TARGET} : $(OBJS)
$(CXX) $(OBJS) -o $#
# (15)Compile: Generate object files from source files
# $# := {TARGET}
# $< := THE first file
# $^ all the dependency
# C++ Sources
$(BUILD_DIR)/%.cpp.o: $(SRC_DIRS)/%.cpp
$(MKDIR_P) $(dir $#)
$(CXX) $(CPP_FLAGS) $(CXX_FLAGS) -c $< -o $#
#(16)Delete dependency files, object files and the target file
.PHONY: all clean
all: ${BUILD_DIR}/${TARGET}
clean:
$(RM) $(DEPS) $(OBJS) ${BUILD_DIR}/${TARGET}
-include $(DEPS)
MKDIR_P ?= mkdir -p
What you need to notice is that your "Makefile" file is the same directory of the include files and sources files,
so you need to change your "IDIR:=../include" to "IDIR:=./include" in your "Makefile".
END!
Here's what i'm using in my windows setup:
CC = g++
CFLAGS = -Wall -std=c++20
SRCDIR = src
HEADDIR = include
OBJDIR = build
BINDIR = bin
# where the executable will be stored
EXECUTABLE := $(BINDIR)/main
# list of all source files
SOURCES := $(wildcard $(SRCDIR)/*.cpp)
# list of all header files
INCLUDES := $(wildcard $(HEADDIR)/*.h)
# from the list of all source files, create a list of all object files
OBJECTS := $(SOURCES:$(SRCDIR)/%.cpp=$(OBJDIR)/%.o)
# all: clean $(EXECUTABLE)
all: $(EXECUTABLE)
# Link: Generate executable file from object file
$(EXECUTABLE): $(OBJECTS)
#echo LINKING..... $(CC) -o $# $(OBJECTS)
#$(CC) -o $# $(OBJECTS)
#echo RUNNING: $(EXECUTABLE)
#$(EXECUTABLE)
# Compile: Generate object files from source files
# $# := {EXECUTABLE}
# $< := THE first file
# $^ all the dependency
# C++ Sources
$(OBJDIR)/%.o : $(SRCDIR)/%.cpp | makedirs
#echo COMPILING... $(CC) $(CFLAGS) -c "$<" -o "$#"
#$(CC) $(CFLAGS) -c $< -o $#
# `|` is order-only-prerequisites
# https://www.gnu.org/software/make/manual/html_node/Prerequisite-Types.html
makedirs:
# check if the file exists; if not, create it
# mkdir -p $(OBJDIR) in linux
#if not exist "$(OBJDIR)" mkdir $(OBJDIR)
#if not exist "$(BINDIR)" mkdir $(BINDIR)
#Delete dependence files, object files, and the EXECUTABLE file
clean:
#echo CLEANING UP
# check if the directories exist; if so, delete them
#if exist "$(OBJDIR)" rmdir /s /q $(OBJDIR)
#if exist "$(BINDIR)" rmdir /s /q $(BINDIR)