Search code examples
cmakefilesubdirectory

Makefile that handles subdirectories and puts object files in a object folder tree


I have a question about Makefiles with subdirectories:

I have a program with the structure

---src
    |
    |-> main.c
    |-> morestuff.c
    |-> [...]
    |-> builtins -> builtin.c
                 -> builtin2.c
---obj
---inc

Now what I want to do is: I want to run make such that I create object files in my object directory (order structure not necessarily needed) and that I (obviously) create an executable. I am able to do that without the subdirectories, but my pattern rules break, once I try to include the subdirectories...

My current approach (without subdirectories) looks something like this:

NAME = minishell
SRC_DIR = src/
OBJ_DIR = obj/
INC_DIR = inc/
LIBFT_DIR = libft/
LIBFT_EXEC = libft.a
CC = gcc
CFLAGS = -Wall -Werror -Wextra -g

# place all source files here
SRC =     $(SRC_DIR)main.c \
          $(SRC_DIR)builtin1.c \
          $(SRC_DIR)builtin2.c \
          [...]

# takes all named source files and converts them to .o files in the /obj directory
OBJ = $(SRC:$(SRC_DIR)%.c=$(OBJ_DIR)%.o)

# prevents rules from being considered as files
.PHONY: all clean fclean re

all: $(NAME)

# creates subdirectory /obj
$(OBJ_DIR):
    @mkdir $@
    @echo "Creating object directory..."

# makes sure to make a /obj dir before compiling .o files
$(OBJ): | $(OBJ_DIR)

$(OBJ): $(OBJ_DIR)%.o: $(SRC_DIR)%.c
    $(CC) $(CFLAGS) -c $< -o $@

# compiles all object files and builds executable file 'minishell' -> ADJUST READLINE FOR LINUX!
$(NAME): $(OBJ)
    @echo "Compiling libft..."
    $(MAKE) -C libft
    @echo "Compiling $(NAME)..."
    $(CC) $(CFLAGS) $^ $(LIBFT_DIR)$(LIBFT_EXEC) -L $(HOME)/goinfre/.brew/opt/readline/lib/ -lreadline -o $@
    @echo "SUCCESSFULLY CREATED MINISHELL!"

So how can I scale that up to handle subdirectories? I know I could make Makefiles in subdirectories, but this is not worth the effort since there aren't a lot of files in there...


Solution

  • Instead of just creating the object directory before compiling, you should create the object file directory tree prior to compiling each file:

    $(OBJ_DIR)%.o: $(SRC_DIR)%.c
        @mkdir -p $(dir $@)
        $(CC) $(CFLAGS) -c $< -o $@
    

    $(dir $@) is a GNU make extension. You can use @mkdir -p `dirname $@` for portability to other make flavors.