1
votes

I have a C project, which has the following file structure:

Makefile
src
 |-utils
 |   |--vic.c
 |   |--vic.h
 |-mod
 |  |--type.c
 |  |--type.h
 |-bb.c
 |-bb.h
 |-main.c

So, at the root directory I have the actual Makefile and the src directory, which includes the source files. Inside the src directory there are multiple .c and .h files along with the main.c file, and there are other directories which also contain other .c and .h files. Please note that the above shown file structure is kept short for brevity. I want to create a Makefile that will automatically compile everything and generate an executable main program, and also delete the object files generated during the compilation. Currently, I have something like this:

CC=gcc
CFLAGS=
RM=rm -rf
OUT=main
SRC=src
OBJ=obj

SOURCES=$(wildcard $(SRC)/*.c)
OBJECTS=$(patsubst $(SRC)/%.c, $(OBJ)/%.o, $(SOURCES))

all: build

build: $(OBJECTS)
    $(CC) $(CFLAGS) $^ -o $@
    $(RM) $(OBJ)

$(OBJ)/%.o: $(SRC)/%.c
    $(CC) $(CFLAGS) -I$(SRC) -c $< -o $@

debug: CFLAGS+=-DDEBUG
debug: build

.PHONY: clean

clean:
    $(RM) $(OBJ) $(OUT)

But that doesn't seem to work as expected, as when I issue make command, it returns the following error message:

Assembler messages: Fatal error: can't create obj/main.o: No such file or directory

Any ideas how to achieve what I want?

1
You are making trouble for yourself by distributing your sources in a directory tree, instead of all in the same directory, and at the same time trying to write a single Makefile that dynamically discovers and builds all the sources. This is not beyond GNU make's capabilities, but the effort you will spend getting it to work would probably be better spent on solving the problem a different way.John Bollinger
Alternatives include hand-writing all the sources into the Makefile (possibly with the help of piping in the output of an ls command), or at least making a static list of all the subdirectories, or setting up a separate Makefile in each subdirectory and performing a good old recursive make.John Bollinger
There are also better alternatives for keeping the source directory clean, such as setting up for and performing VPATH builds.John Bollinger

1 Answers

0
votes

It seems you rm -rf obj/ after each step, but there's no mkdir -p obj/ to replace it.

Since GNU make will, itself, remove intermediate targets, I don't see why you would do $(RM) $(OBJ), but your %.o target could have a line like mkdir -p "$(shell dirname "$@")" … to ensure that the destination directory exists.

PS: your output is named build rather than main because that's the name you gave it …