1

I've been struggling with makefiles for a while now, since I don't really get how they work...

My directory structure is as followed:

--bin/
--build/
--includes/
--src/
----classes/
------somefiles.hpp
------somefiles.cpp
----states/
------somestates.hpp
----main.cpp
----few-other-files.cpp
--Makefile
--.gitignore

Which worked fine with my makefile, until I started adding those subdirs, classes and states

I can't seem to find how to include those subdirs in my makefile which is:

CC := g++
SRCDIR := src
BUILDDIR := build
TARGET := bin/game

SRCEXT := cpp
SOURCES := $(shell find $(SRCDIR) -type f -name *.$(SRCEXT))
OBJECTS := $(patsubst $(SRCDIR)/%,$(BUILDDIR)/%,$(SOURCES:.$(SRCEXT)=.o))
CFLAGS := -g -Wall
LIB := -lsfml-audio -lsfml-graphics -lsfml-network -lsfml-system -lsfml-window
INC := -I includes -I /usr/local/include -L /usr/local/lib

$(TARGET): $(OBJECTS)
    @echo "Linking..."
    @echo "$(CC) $^ -o $(TARGET) $(LIB)"; $(CC) $^ -o $(TARGET) $(LIB)

$(BUILDDIR)/%.o: $(SRCDIR)/%.$(SRCEXT)
    @mkdir -p $(BUILDDIR)
    @echo "$(CC) $(CFLAGS) $(INC) -c -o $@ $<"; $(CC) $(CFLAGS) $(INC) -c -o $@ $<

clean:
    @echo "Cleaning..."
    @echo "$(RM) -r $(BUILDDIR) $(TARGET)"; $(RM) -r $(BUILDDIR) $(TARGET)

.PHONY: clean

Problem: Fatal error: can't create build/classes/stateManager.o: No such file or directory

Normally, the problem is that it can't build the directory. But I thought the mkdir -p would solve that.

Any help is greatly appreciated.

nepp95
  • 131
  • 1
  • 13
  • But you only do `mkdir -p $(BUILDDIR)` which is equal to `mkdir -p build`. The subdirectories (like `classes`) are not created. You might be interested in the `dir` function (see e.g. [this `make` file-function manual](https://www.gnu.org/software/make/manual/make.html#File-Name-Functions)). – Some programmer dude Jul 10 '17 at 13:44
  • Also a little nitpicking on the term "recursive makefile". A recursive makefile is a makefile that invokes itself, usually in subdirectories. Your makefile is *not* recursive. – Some programmer dude Jul 10 '17 at 13:51
  • Which ended up as: `@mkdir -p $(shell dirname $@)`, If I'm correct? – nepp95 Jul 10 '17 at 14:00
  • You may want to have a look at `VPATH` and `vpath`. – grek40 Jul 10 '17 at 14:04
  • Or `@mkdir -p $(dir $@)` – Some programmer dude Jul 10 '17 at 14:04
  • With which a new problem arises. The object files are successfully made and put in the `build` directory. But, the linker throws out an `undefined reference to main`. This usually is because of a missing `-c` flag, but it is not in this case. – nepp95 Jul 10 '17 at 14:47
  • With which a new question arises, hopefully showing the actual specific error and the linker command that reproduces it. – Useless Jul 10 '17 at 15:11
  • BTW, most of your `@echo` statements could be removed, since running make with debugging enabled will print the expanded command anyway. That saves having to keep two copies of the same rule in sync. – Useless Jul 10 '17 at 15:14
  • I prefer just running `make` and getting to choose what I echo and what not. – nepp95 Jul 10 '17 at 15:28
  • If you remove the `@` at the front of the line (along with the echo), it will echo the exact command it's running, which I believe is your intent.. Alternatively you could use something like [this]. – blackghost Jul 10 '17 at 16:42

1 Answers1

1

Replace:

mkdir -p $(BUILDDIR)

with

mkdir -p $(@:D)

Another way is to let make treat directories as targets and have them automatically created for you, so that you do not have to clutter your recipes with mkdir -p invocations:

.SECONDEXPANSION:
# Make the object file depend on its directory using order-only dependency.
$(BUILDDIR)/%.o: $(SRCDIR)/%.$(SRCEXT) | $$(dir $$@)
    $(CC) $(CFLAGS) $(INC) -c -o $@ $<

# Let make create the directories for your targets.
%/:
    mkdir -p $@

# Do not remove the directories because they are "intermediate" targets.
.PRECIOUS : %/
Maxim Egorushkin
  • 131,725
  • 17
  • 180
  • 271