Single Makefile for sources in multiple sub directories - c++

In my C++ project the sources are organized inside a src directory. Inside the src directory are sub directories which all contain headers and source files, e.g.
project
├── Makefile
│
├── MyBinary
│
├── src
│ │
│ ├── main.cpp
│ │
│ ├── Application
│ │ │
│ │ ├── Application.h
│ │ └── Application.cpp
│ │
│ │
│ └── Tasks
│ ├── BackgroundWorker.h
│ └── BackgroundWorker.cpp
│
└── obj
├── Application.o
└── BackgroungWorker.o
I'm trying to create a Makefile so that all object files are created inside obj directory and the executable MyBinary is created above the src directory at the same level as the Makefile is.
It hasn't to be too complicated or automated. I don't mind manually specifying each .cpp and .h file in the Makefile.
But I'm new to Makefiles and unfortunately I'm struggling with this attempt:
CXX=c++
CXXFLAGS=-Wall -Os -g0
# Name of the output binary
APPNAME=MyBinary
# Source root directory
SRC_ROOT=src
# Object file directory
OBJ_DIR=obj
DEPS=$(SRC_ROOT)/Application/Application.h \
$(SRC_ROOT)/Tasks/BackgroundWorker.h
_OBJ=$(SRC_ROOT)/Application/Application.o \
$(SRC_ROOT)/Tasks/BackgroundWorker.o\
$(SRC_ROOT)/main.o
OBJ=$(patsubst %,$(OBJ_DIR)/%,$(_OBJ))
# This rule says that the .o file depends upon the .c version of the
# file and the .h files included in the DEPS macro.
$(OBJ_DIR)/%.o: %.cpp $(DEPS)
$(CXX) -c -o $# $< $(CXXFLAGS)
# Build the application.
# NOTE: The $# represents the left side of the colon, here $(APPNAME)
# The $^ represents the right side of the colon, here $(OBJ)
$(APPNAME): $(OBJ)
$(CXX) -o $# $^ $(CXXFLAGS)
clean:
rm -f $(OBJ_DIR)/*.o $(APPNAME)
The error when calling make is: Fatal error: can't create obj/src/Application.o: File or directory not found.
Can anyone help?

OBJ=$(patsubst %,$(OBJ_DIR)/%,$(_OBJ)) prepends obj/ to the words of _OBJ. You want to replace the src by obj something which you can do with
OBJ=$(patsubst $(SRC_ROOT)/%,$(OBJ_DIR)/%,$(_OBJ))
Note that the directory structure you get expect subdirectories Application and Tasks to obj you either have to create them manually before calling make or updating the Makefile to create them.
Here is something which behaves as I expect when the directory structure is pre-created.
APPNAME=MyBinary
SRC_ROOT=src
OBJ_DIR=obj
DEPS=$(SRC_ROOT)/Application/Application.h \
$(SRC_ROOT)/Tasks/BackgroundWorker.h
_OBJ=$(SRC_ROOT)/Application/Application.o \
$(SRC_ROOT)/Tasks/BackgroundWorker.o\
$(SRC_ROOT)/main.o
OBJ=$(patsubst $(SRC_ROOT)/%,$(OBJ_DIR)/%,$(_OBJ))
$(OBJ_DIR)/%.o: $(SRC_ROOT)/%.cpp $(DEPS)
echo Making $# from $<
touch $#
$(APPNAME): $(OBJ)
echo Making $# from $^
touch $#
Note that in practice you have to be finer with the dependencies and probably have them to be generated by the compiler (see -MM and similar options for g++), here you recompile everything when you change an header.

Related

Is there a better way to write my makefile?

I have this makefile
# Compiler and flags
CC = g++
CFLAGS = -Iinclude
# Source and object files
SOURCES = $(wildcard src/*.cpp)
OBJS = $(patsubst src/%.cpp,obj/%.o,$(SOURCES))
# Dependency files
DEPS = $(patsubst src/%.cpp,obj/%.d,$(SOURCES))
# Include dependency files
-include $(DEPS)
# Build object files from source files
obj/%.o: src/%.cpp
$(CC) -c -o $# $< $(CFLAGS)
$(CC) -MM -MT $# -MF $(patsubst %.o,%.d,$#) $<
# Build library from object files
libui.a: $(OBJS)
ar rcs $# $^
ranlib $#
copy vui.hpp include/vui.hpp
# Clean object files and library
.PHONY: clean
clean:
del obj\*.o obj\*.d libui.a
and this is my workspace:
workspace
├── include
│ ├── UI_Button.hpp
│ ├── UI_CheckBox.hpp
│ ├── UI_Console.hpp
│ ├── UI_ScrollBar.hpp
│ ├── UI_Slider.hpp
│ ├── UI_TextBox.hpp
│ └── vui.hpp
├── src
│ ├── UI_Button.cpp
│ ├── UI_CheckBox.cpp
│ ├── UI_Console.cpp
│ ├── UI_ScrollBar.cpp
│ ├── UI_Slider.cpp
│ └── UI_TextBox.cpp
├── obj
│ ├── UI_Button.o
│ ├── UI_CheckBox.o
│ ├── UI_console.o
│ ├── UI_ScrollBar.o
│ ├── UI_Slider.o
│ └── UI_TextBox.o
├── libui.a
└── Makefile
Now that makefile has built everything I got an error
copy vui.hpp include/vui.hpp
The system cannot find the file specified.
make: *** [Makefile:24: libui.a] Error 1
and when I try to remake I need to delete everything because it only checks UI_Button.o and says it is up to date even if I change any other .hpp or .cpp file it doesn't want to rebuild. Is there a a way I can fix these two errors? the vui.hpp just includes all other headers.
Not sure what copy or del is, but I assume copy a b copies a file a to b. However, this recipe:
# Build library from object files
libui.a: $(OBJS)
ar rcs $# $^
ranlib $#
copy vui.hpp include/vui.hpp
does not depend on vui.hpp, nor do I see any rule to create vui.hpp in your source-root, nor do I see it listed in the files you state you have in your source tree. So, there just is no such file.
If you want to hang on to that command, don't have it running in a completely unrelated recipe. Have an extra recipe for it:
include/vui.hpp: vui.hpp
copy vui.hpp include/vui.hpp

GNU Make wildcard to compile each file separately into a build directory

I am trying to create the following directory structure for a C++ project.
├── Project
├── Makefile
│ ├── build/
│ ├── src
│ │ ├── include/
│ │ ├── cpp/
│ │ loader.s
│ │ linker.ld
I also want to compile each .cpp file separately into the build directory and link in a different target.
I am having trouble with the wildcard to do this:
BUILD = ./build/
OBJS = $(patsubst src/cpp/%.cpp,build/%.o,$(wildcard src/cpp/*.cpp))
CPPFLAGS = -I ./src/include -Wfatal-errors -fno-use-cxa-atexit -nostdlib -fno-builtin -fno-rtti -fno-exceptions -fno-leading-underscore
$(BUILD)kernel.elf: $(BUILD)loader.o $(OBJS)
$(ARMGNU)-ld -T ./src/linker.ld -o $# $^
$(ARMGNU)-objdump -D $(BUILD)kernel.elf > $(BUILD)kernel.list
$(BUILD)%.o: ./src/%.s
$(ARMGNU)-as $(AFLAGS) -c -o $# $^
$(BUILD)%.o: ./src/cpp/%.cpp
$(ARMGNU)-gcc $(CPPFLAGS) -c -o $# $^
The build/%.o's are never built so the link step gets an empty $(OBJS). What's the correct syntax for this?
Your BUILD variable is set to ./build/, while object files in OBJ start with build/. After expansion rules look like:
./build/kernel.elf: ./build/loader.o build/...
./build/%.o: ./src/%.s
That additional ./ prefix affects rule matching by make, because paths aren't normalized.
The fix is as simple as making prefixes in target goals and prerequisites match by setting:
BUILD = build/

Makefile for many subfolders

I have the following folder structure:
├── build
├── external
│   ├── build
│   ├── lib1
│   │   ├── lib1Classes.cc
│   │   └── lib1Classes.h
│   ├── lib2
│   │   ├── lib2Classes.cc
│   │   └── lib2Classes.h
│   ├── lib3
│   │   ├── lib3Classes.cc
│   │   └── lib3Classes.h
│   └── Makefile
├── include
│   ├── SomeClass.h
│   └── SomeOtherClass.h
├── Makefile
└── src
├── main.cpp
├── SomeClass.cc
└── SomeOtherClass.cc
I am trying to first compile libXClasses.cc and libXClasses.h into ./external/build/libX.o, then combine all libX.o into an ./external/build/external.so.
In step 2 I'd like to compile SomeClass.h and SomeClass.cc into ./build/SomeClass.o and SomeOtherClass.h, SomeOtherClass.cc into ./build/SomeOtherClass.o. Finally I want to link ./external/build/external.o and ./build/*.o into a binary in ./build.
I am however already failing in step 1. My idea for ./external/Makefile was something like this:
CXX = g++ -std=c++11
TARGET ?= external.so
BUILD_DIR ?= ./build
# get all source files
SRCS = $(shell find . -name *.cc -or -name *.cpp -or -name *.h)
# get the name of all directories that contain at least one source file
SRC_DIRS ?= $(foreach src,$(SRCS),$(shell dirname $(src)))
# remove duplicate directories
SRC_DIRS := $(shell echo $(SRC_DIRS) | xargs -n1 | sort -u | xargs)
# define one target object file foreach .cc source file
OBJS := $(foreach srcd,$(SRC_DIRS),$(BUILD_DIR)/$(patsubst %.cc,%.o,$(shell find $(srcd) -name *.cc -printf "%f\n")))
# rule to build ./external/build/external.so
$(BUILD_DIR)/$(TARGET): $(OBJS)
$(CXX) -shared $(OBJS) -o $#
# no idea how to build libX.o in one rule
$(BUILD_DIR)/%.o: %.cc %.h
$(CXX) -c -fpic $< -o $#
This does not work as in the last rule I am not specifying where the correct .cc and .h come from. But I don't know how I could do that at all. Do I have to write a rule for each libX directory separately? What if I have 10 or 20 libX directories?
Cheers!
Step 1: create a list of the libX directories (because no one wants to maintain a list like that by hand):
LIBS := $(wildcard external/lib*)
Step 2: create a list of the source files:
LIB_SRCS := $(notdir $(wildcard $(addsuffix /*.cc, $(LIBS))))
(I've removed the paths because they're a pain; there's more than one way to do this.)
Step 3: create a list of desired object files:
OBJS := $(patsubst lib%Classes.cc, external/build/lib%.o, $(LIB_SRCS))
Step 4: write a rule to build these files:
vpath %.cc $(LIBS)
$(OBJS): external/build/lib%.o: lib%Classes.cc
$(CXX) -c -fPIC -I$(dir $<) $< -o $#
(Step 5: write a rule to build the library, but you already have that.)
One could do more. This makefile does not detect the fact that libN.o depends on libNClasses.h. It is possible to set that up, but it's a little complicated, and this is enough for one day.

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

Execute a program opening a file in parent directory from parent directory in C++

My project structure looks like this:
├── Makefile
├── data
├── src
│   ├── ...other code
│   └── main.cpp
└── bin
└── out
The program will look into the parent directory and open a file put in there, the code opening the file looks like:
f.open("../data");
and it will generate some temporary files in bin(where the binary goes) with code like this:
f.open("temp");
My makefile looks like this:
SOURCES=src/main.cpp src/foo.cpp
OBJECTS=$(SOURCES:.cpp=.o)
EXECUTABLE=bin/out
all: $(SOURCES) $(EXECUTABLE)
$(EXECUTABLE): $(OBJECTS)
$(CC) $(OBJECTS) -o $#
.cpp.o:
$(CC) $(CFLAGS) $< -o $#
clean:
rm src/*.o $(EXECUTABLE)
test:
bin/out arg1 arg2
load:
bin/out arg1 arg2
.PHONY: test load
Now, I can make from the parent directory, then go into the bin, execute the program. But the program cannot open the data in the parent directory if I just run make load or make test from the parent directory.
You can put cd on a make recipe command :
test:
cd bin; ./out arg1 arg2
Don't forget to cd in the same line where you are executing