How to write a Makefile for structured file system - c++

I'm trying to learn how to write makefiles for my projects. Given that I'm using a text editor like sublime-text and the terminal.
I know how to write a makefile to compile a single file and it's relevant header files IF all files are in the same directory.
Imagine that we have a directory and some sub-directories which are structured like this:
the Headers contains all the .h files Objects is a directory to hold the .o files and Sources is a directory that has the source codes .cpp The Makefile is in the top level directory.
.
├── Headers
│   └── function.h
├── Makefile
├── Objects
│   └── main.o
└── Sources
├── function.cpp
└── main.cpp
I have tried to put together a Makefile by following the GNU make and the answer to this post.
At the moment my Makefile looks like this:
INC_DIR = Headers
SRC_DIR = Sources
OBJ_DIR = Objects
CXXFLAGS = -c -Wall -I.
CC = g++
SRCS = $(SRC_DIR)/*.cpp
OBJS = $(OBJ_DIR)/*.o
DEPS = $(INC_DIR)/*.h
output: $(OBJ_DIR)/main.o $(OBJ_DIR)/function.o
$(CC) $^ -o $#
$(OBJ_DIR)/%.o: $(SRC_DIR)/%.cpp $(DEPS)
$(CC) $(CXXFLAGS) $< -o $#
clean:
rm $(OBJ_DIR)/*.o output
Which will generate the following error when make is executed
g++ -c -Wall -I. Sources/function.cpp -o Objects/function.o
Sources/function.cpp:2:22: fatal error: function.h: No such file or directory
compilation terminated.
Makefile:30: recipe for target 'Objects/function.o' failed
make: *** [Objects/function.o] Error 1
I know that I'm doing something wrong but can't figure out what and how to fix it.
I'd appreciate any help and guidance.

Add Headers to your CXXFLAGS:
CXXFLAGS = -c -Wall -I. -IHeaders
# ^^^^^^^^^
Also you rather might want to generate header dependencies using GCC's -M<x> option family and include these into your Makefile.
Here's a pretty good explanation for the technique from GNU make's documentation:
4.14 Generating Prerequisites Automatically
more info can be also found here:
Makefile (Auto-Dependency Generation)

Related

GNU make uses same source file for each object file when part of dependency list is removed

I have a somewhat complicated source directory, and have written a makefile to compile it:
├── include
│   ├── subinc
│   │   ├── test_y.h
│   │   └── test_z.h
│   ├── test_w.h
│   └── test_x.h
├── makefile
├── src
│   ├── test_w.cpp
│   └── test_x.cpp
├── src2
│   ├── test_y.cpp
│   └── test_z.cpp
└── test.cpp
The makefile as shown below is working. But, I'm a bit confused why. It doesn't seem to be using $(DEPS), since it gives paths like ./include/./include/subinc/test_y.h when I echo it in the rule. That's obvious because of the patsubst line, but changing that to patsubst %,%,$(INCLUDES) breaks it too... (maybe that's the root of the whole problem!)
But, something funky happens when I remove that constant from the dependency list of the rule %.o, so the rule is just %.o: $(SOURCES). Upon running make, it uses the first item in $(SOURCES) as the target for every call to g++ creating object files:
$ make
g++ -c -o test.o test.cpp -I./include -I./include/subinc
g++ -c -o src/test_x.o test.cpp -I./include -I./include/subinc
g++ -c -o src/test_w.o test.cpp -I./include -I./include/subinc
g++ -c -o src2/test_z.o test.cpp -I./include -I./include/subinc
g++ -c -o src2/test_y.o test.cpp -I./include -I./include/subinc
I figured that makes sense, because $< is used.
But why does this only print the first in the dependency list when I take the second constant (of header files -- some even with bad format) out of the list?
My thought is that somehow make is intelligently matching the .cpp files in the list to the corresponding .h files in the list, and then removing them from the list each time it runs the rule....
Thanks
Makefile (working version, maybe full of bad practices...)
INCDIR=./include
SRCDIR=./src
CXX=g++
CXXFLAGS=-I$(INCDIR) -I$(INCDIR)/subinc
INCLUDES=$(shell find . -name "*.h" -o -name "*.hpp")
DEPS = $(patsubst %,$(INCDIR)/%,$(INCLUDES))
EXE=testexe
SOURCES=$(wildcard *.cpp) $(wildcard **/*.cpp)
OBJ=$(SOURCES:.cpp=.o)
####RULES
%.o: $(SOURCES) $(DEPS)
$(CXX) -c -o $# $< $(CXXFLAGS)
$(EXE): $(OBJ)
g++ -o $# $^ $(CXXFLAGS)
rm $(OBJ)
EDIT
If you'd like an MCVE for this, each test_*.h defines an empty class like
class T*{
T*(); //defined in test_*.cpp to print "T* created"
~T*(); //defined in test_*.cpp to print "T* destroyed"
};
And the main test.cpp file just creates a pointer to each of the classes, and then deletes it.
There are many issues here. First:
SOURCES=$(wildcard *.cpp) $(wildcard **/*.cpp)
GNU make uses simple globbing, which does not understand **. This behaves the same as *. Since you only have one level of subdirectory this works for you but if you add another (sub-subdirectory) level, this won't match it.
Second, this is wrong:
DEPS = $(patsubst %,$(INCDIR)/%,$(INCLUDES))
As you point out this changes the dependency paths from the (correct) ./include/xy/z.h to the (incorrect) ./include/./include/xy/z.h. I'm not sure why you're trying to change anything at all here: why not just use the INCLUDES variable content directly? Using $(patsubst %,%,$(INCLUDES)) is a no-op; it has no effect.
Third, you should be using simple expansion (:=) for these types of assignments, so that they are not rerun every time the variable is used.
Next, this is wrong:
%.o: $(SOURCES) $(DEPS)
$(CXX) -c -o $# $< $(CXXFLAGS)
This will expand to something like this after SOURCES are resolved:
%.o: test.cpp src/test_w.cpp src/test_x.cpp src2/test_y.cpp src2/test_z.cpp ./include/include/subinc/test_y.h $(DEPS)
$(CXX) -c -o $# $< $(CXXFLAGS)
What this means is that every object file depends on all source files (and all $(DEPS)). So if any source or header file ever changes ALL the object files will be rebuilt. Clearly that's not what you want.
Also, the reason it always compiles the same file is that the recipe uses $<, which represents the first prerequisite, and the first prerequisite here is test.cpp, so that's what's always compiled.
When you create a pattern rule, (at least) the first source file should (almost always) be a pattern as well, so that it changes along with the target to build each object file (in this case).
So, you want your pattern rule to look like this:
%.o: %.cpp $(INCLUDES)
$(CXX) -c -o $# $< $(CXXFLAGS)
Of course this does mean that every object file depends on all header files, so if you change any header file all your object files are rebuilt. Maybe that's OK; if not you'll need to do something more advanced.
Lastly, you asked why it your makefile seems to work properly if you create bogus paths in the $(DEPS) variable. Here's why: because those paths don't exist, make decides that your pattern rule doesn't apply (since not all the prerequisites can be created) and so it ignores your pattern rule completely.
Once that happens, make's default pattern rule for how to build an object file takes over, and that default rule builds things for you properly. However, you may notice that if you modify any of the header files, make won't rebuild your object files (because it doesn't know about that prerequisite relationship).

Generate gcc dependencies with full path

I have a simple project that looks something like this
.
├── build
│   ├── file1.o
│   └── one
│   ├── file1.o
│   └── file2.o
├── .depend
├── Makefile
└── src
   ├── file1.cpp
   └── one
   ├── file1.cpp
   └── file2.cpp
The Makefile is something like this:
# Get all of the source files
SRC = $(shell find src/ -name "*.cpp")
# Get all of the object files
OBJ = $(subst src,build,$(SRC:.cpp=.o))
$(OBJ):
#mkdir -p $(shell dirname $#)
g++ -g -c $(subst build,src,$(subst .o,.cpp,$#)) -o $#
all: depend build
build: $(OBJ)
gcc -o project $^
depend:
g++ -MM $(SRC) > .depend
sed -i 's/.\/src/.\/build\//g' .depend
sinclude .depend
I am attempting to generate makefile dependencies by running g++ -MM src/file1.cpp src/one/file1.cpp src/one/file2.cpp > .depend, and it generates the following directives:
file1.o: src/file1.cpp <other headers>
file1.o: src/one/file1.cpp <other headers>
file2.o: src/one/file2.cpp <other headers>
The problem with this, is that build/file1.o does not match file1.o, and as a result, changing src/file1.cpp or any of the headers it depends on does not cause the object file to be rebuilt. At first I thought it might have been an issue where sinclude .depend was run before the .depend file was generated, but the problem persists even if I run make depend followed by make build. From everything I've read, there are no g++ arguments or options that would preserve the path of the name.
Is it possible to generate a dependency file this way, or is this a fundamentally incorrect approach to building a project?
I took a look at the answers for the question this question was marked as a possible duplicate of, but it seems that question is asking how to create a complete makefile for a project, whereas my issue is not with the creation of a Makefile, but rather an issue with gcc -MM dependency generation. The answers to that question do not address my problems.
What about:
# Get all of the source files
SRC = $(shell find src/ -name "*.cpp")
# Get all of the object files
OBJ = $(patsubst src/%.cpp,build/%.o,$(SRC))
.PHONY: all
all: project
project: $(OBJ)
gcc -o $# $^
$(OBJ): build/%.o: src/%.cpp
#mkdir -p $(dir $#)
g++ -g -c $< -o $#
.depend: $(SRC)
g++ -MM $^ > $# && \
sed -Ei 's#^(.*\.o: *)src/(.*/)?(.*\.cpp)#build/\2\1src/\2\3#' $#
include .depend
Dependencies computation
The sed command substitutes any:
file.o: src/file.cpp ...
by:
build/file.o: src/file.cpp ...
and any:
file.o: src/X/Y/Z/file.cpp ...
by:
build/X/Y/Z/file.o: src/X/Y/Z/file.cpp ...
The target is directly .depend and it has all source files as dependencies such that it is automatically rebuilt if missing or older than any source file. No need to use the depend phony target or to add it as a pre-requisite of all (make automatically tries to rebuild files included with include, if needed).
Note
I added some GNU make features (patsubst, static pattern rule, systematic use of automatic variables...) Rework the non-supported ones if you use another make.
Here are three approaches.
One, modify the output with sed (similar to Renaud Pacalet's answer):
depend:
g++ -MM $(SRC) | sed 's/.*: src\([^ ]*\)cpp/build\1o: src\1cpp/' > .depend
Two, use a shell loop:
STEMS := file1 one/file1 one/file2
depend:
rm -f .depend
for x in $(STEMS); do g++ -MM -MT build/$$x.o src/$$x.cpp >> .depend; done
Three, a Make approach:
DEPENDENCIES := $(addsuffix -depend,$(STEMS))
clear-depend:
rm -f .depend
depend: $(DEPENDENCIES)
%-depend: clear-depend
g++ -MM -MT build/$*.o src/$*.cpp >> .depend
(My favorite approach is to have a separate dependency file for each object file, instead of one big .depend file. It has several advantages, but it takes some time to explain, and it's also tricky if there are name collisions in your source tree, such as file1.cpp and file1.cpp.)
I use the following sequence for my build:
define req
$(subst ..,__,$(dir build-$(TARGET)$(build_dir_ext)/$(1)))%.o: $(dir $1)%.cpp
mkdir -p $$(dir $$(subst ..,__,$$#))
$$(CXX) -MM $$(CXXFLAGS) $$< -MT $$(subst ..,__,$$#) > $$(patsubst %.o,%.d,$$(subst ..,__,$$#))
$$(CXX) $$(CXXFLAGS) $$< -c -o $$(subst ..,__,$$#)
endef
$(eval $(foreach x,$(OBJ),$(call req,$(x))))
As a result make is now able to handle a path which can be "outside" the source tree, simply by using '__' instead of '..' and the build dir is set accordingly to the found patterns, so there is no problem with src and build anymore. I need the "outside" files to use a source pool where the local build dir is under the root of source pool and project directory.
In hope that helps...
EDIT 1: Why replacing '..'
Think of the following source tree:
./sourcepool/lib1/src/one.cpp
./sourcepool/project/build
If your Makefile is in the ./sourcepool/project path and one of the OBJ is "../lib1/src/one.o" the Makefile should create a equivalent path in the build directory. That is, if '..' is used, not possible, because the path is then not longer in build but one depth higher. If replacing .. with __ the result is as following:
./sourcepool/project/build/__/lib1/src/one.o
This makes it possible to not copy or link all used dirs to the local project and build file tree.

How do I separate Makefile build outputs for different configurations?

Given I have a project with a straightforward Makefile like so:
all: foobar
foobar: foo.o bar.o
that I can build to target different architectures:
$ CC=clang make # or
$ CC=x86_64-w64-mingw32-gcc make # or
$ CC=arm-linux-gnueabihf-gcc make
This works but I want to be able to maintain outputs for multiple configurations at the same time, for example on a build server.
What would be a good, clean way to go about this? I've considered the following:
Use autotools or another build tool, but I want to see what's possible without
Create build directories with Makefiles in them that set VPATH and include the root Makefile
Write a script that moves the output after building each architecture
Modify the Makefile to build multiple configurations. I don't like this solution because you end up with a sort of meta-Makefile that's complex and tightly coupled to your specific build environment
Add a variable to the Makefile to set the output directory. This could work but it means I can't use implicit Makefile rules. Pattern rules would get messy too
I would go with something like that:
# User configuration
BINARY := hello
SOURCES := main.c
# Create the output paths
out ?= out
outdir := $(out)/$(CC)
outbin := $(outdir)/$(BINARY)
objects := $(outdir)/$(SOURCES:.c=.o)
# Default target
all: $(outbin)
# Binary target
$(outbin): $(objects)
$(CC) -o $# $^
# Objects target
$(objects): $(outdir)/%.o: %.c
mkdir -p $(#D)
$(CC) -o $# -c $<
# The cleanning targets
clean:
$(RM) -r $(outdir)
mrproper:
$(RM) -r $(out)
# Declare phony targets
.PHONY: all clean mrproper
Note that the objects target is using static pattern to be able to get the source files in the current directory and the object files in the output directory.
It is also as easy to use as a basic Makefile:
$ make
mkdir -p out/cc
cc -o out/cc/main.o -c main.c
cc -o out/cc/hello out/cc/main.o
$ make
make: Nothing to be done for 'all'.
$ tree
.
├── main.c
├── Makefile
└── out
└── cc
├── hello
└── main.o
2 directories, 4 files
$ CC=gcc make
mkdir -p out/gcc
gcc -o out/gcc/main.o -c main.c
gcc -o out/gcc/hello out/gcc/main.o
$ tree
.
├── main.c
├── Makefile
└── out
├── cc
│   ├── hello
│   └── main.o
└── gcc
├── hello
└── main.o
3 directories, 6 files

Makefile doesn't find .hpp

I have 2 directories in my project, one called Builds, who has the Makefile and a test program ( test-P0-consola.cpp ) and other one directory called P0 who constains the classes I use, cadena (string) and fecha (date).
test-P0-consola.cpp includes both of them, but Make doesn't find them.
CPP = g++
CPPFLAGS = -std=c++14 -g -Wall -pedantic
VPATH = ../P0:.:..
test-consola: test-P0-consola.o fecha.o cadena.o
${CPP} ${CPPFLAGS} -o $#.ex $^
test-P0-consola.o: test-P0-consola.cpp fecha.hpp cadena.hpp
${CPP} -c ${CPPFLAGS} $< -o $#
fecha.o: fecha.hpp
cadena.o: cadena.hpp
It throws the fatal error "cadena.hpp doesn't exist the file or directory" when it tries to compile test-P0-consola.o, but It find them out when I force it to compile cadena or fecha. I'm using GCC and Ubuntu.
..
├── Builds
│   ├── makefile.mak
│   └── test-P0-consola.cpp
├── P0
│   ├── cadena.cpp
│   ├── cadena.hpp
│   ├── fecha.cpp
│   └── fecha.hpp
EDIT
Error:
g++ -std=c++14 -g -Wall -pedantic -c test-P0-consola.cpp
test-P0-consola.cpp:7:21: fatal error: fecha.hpp: There is no file or directory
compilation terminated.
makefile.mak:9: Failure in the instructions for the objective 'test-P0-consola.o'
make: *** [test-P0-consola.o] Error 1
Look closely at your error:
test-P0-consola.cpp:7:21: fatal error: fecha.hpp: There is no file or directory
You probably have something like:
// test-P0-consola.cpp
#include "fetcha.hpp"
But fetcha.hpp is not in that directory, so it can't find it. You either need to change the way you include the file directly (via #include "../P0/fetcha.hpp") or to change the build rule to pass in an additional include path (via -I../P0).
Note: I'm not sure there's a reason to add . to VPATH. That's kind of implicit.
Note 2: this is a bad idea:
test-consola: test-P0-consola.o fecha.o cadena.o
${CPP} ${CPPFLAGS} -o $#.ex $^
~~~~~
Don't lie to Make. The result of running a recipe should be the target file, except for PHONY targets. The recipe here should be -o $#. If you want the .ex suffix, you should change the target to be test-consola.ex. If you still want the rule to be named test-consola, you'll want:
test-consola : test-consola.ex
test-consola : .PHONY
You should put in the makefile the include path of the .hpp files you need the compiler to use. You should use the -Ipath compiler directive, where path is the path of your include files.
See `Makefile: How to correctly include header file and its directory?
and
How to define several include path in Makefile
Something like:
CPP = g++
CPPFLAGS = -std=c++14 -g -Wall -pedantic
INC = -Iyourincludebasepath/P0
VPATH = ../P0:.:..
test-consola: test-P0-consola.o fecha.o cadena.o
${CPP} ${CPPFLAGS} ${INC} -o $#.ex $^
test-P0-consola.o: test-P0-consola.cpp fecha.hpp cadena.hpp
${CPP} -c ${CPPFLAGS} ${INC} $< -o $#
fecha.o: fecha.hpp
cadena.o: cadena.hpp

How do I write a smart makefile?

I'm new to linux and new to makefiles so I'm a little stumped at this point. I've been staring the make manual for an hour now and figured I would just ask:
I want to create a makefile for a relatively small project, whose source files are in proj/src/, and within subdirectories of that location. I have written a makefile at proj/makefile and want to use it to gather up all the source files, find their dependencies, and compile the result into proj/build/. Here is what I've written:
# here are my files and directories (there are also header files that are not
# listed here, but these are referred to from within the .cpp files with
# respect to the proj/src directory)
# proj/makefile
# proj/src/
# proj/src/main.cpp
# proj/src/dir1/
# proj/src/dir1/source1.cpp
# proj/src/dir2/
# proj/src/dir2/source2.cpp
# proj/build/
srcDir = src/
buildDir = build/
# This is a list of all the object files (can I get this programatically?)
objects = main.o source1.o source2.o
all: prog
# Here I want to compile all source (.cpp) files from src/ and all of its subdirectories, and to find the dependencies I want to call g++ -MM to automatically generate the list:
%.o: %.cpp
$(CXX) $(CXXFLAGS) -MM -I $(srcDir)
clean:
rm -rf *o $(buildDir)/prog
This is far from functional but I wanted to give it ago before asking. Running this gives
make: * No rule to make target main.o', needed byprog'. Stop.
So presumably my attempt at automatically writing the object files failed. If someone could give me some direction that would be great, I'm hoping that between the comments and code you can make out the intention.
EDIT: I have now tried the following:
SHELL := /bin/bash
srcDir = src
buildDir = bin
sources := $(shell find $(srcDir) -name *.cpp)
objects := $(sources:%.cpp=%.o)
-include $(sources:%.cpp=%.d)
# This is a list of all the object files (can I get this programatically?)
all: prog
prog: $(objects)
g++ $(objects) -o $(buildDir)/prog
# Here I want to compile all source (.cpp) files from src/ and all of its subdirectories, and to find the dependencies I want to call g++ -MM to automatically generate the list:
%.o: %.cpp
$(CXX) $(CXXFLAGS) -MMD -MP -c -I $(srcDir)
clean:
rm -rf *o $(buildDir)/hello
But when I run this I get the following error:
g++ -MMD -MP -I src
g++: fatal error: no input files
compilation terminated.
make: *** [src/core/Cell.o] Error 1
It wants to build main.o from main.cpp, but there is no main.cpp at the top level. You could specify the full paths of the object files, if they are to be built alongside the source files:
objects = $(srcDir)/main.o $(srcDir)/dir1/source1.o $(srcDir)/dir2/source2.o
can I get this programatically?
I do it using gnumake's shell function:
SHELL := /bin/bash
sources := $(shell find $(srcDir) -name *.cpp)
objects := $(sources:%.cpp=%.o)
to find the dependencies I want to call g++ -MM to automatically generate the list
Use -MMD to generate dependency files as well as compiling. It's also a good idea to specify -MP here - that makes sure the dependencies are correctly updated when you remove a header. Given the variables defined above, you can include the dependency files with
-include $(sources:%.cpp=%.d)