phibel
phibel

Reputation: 171

Single Makefile for sources in multiple sub directories

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?

Upvotes: 1

Views: 1352

Answers (1)

AProgrammer
AProgrammer

Reputation: 52284

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.

Upvotes: 1

Related Questions