'Create a Makefile to compile all .c files in a folder/subfolder and ship the binaries elsewhere

I am currently studying K&R and I would like to create a Makefile in order to automate the compiling process.

I have a directory structured as follows :

k-r/
    chapter-1/
        exe/
        1-1/
            program1.c
            program2.c
            ...
        1-2/
            program3.c
            program4.c
            ...
    ch1.c
    ch1.h

Most of the programs are self-contained but some require functions which are defined in k-r/chapter-1/ch1.c and k-r/chapter-1/ch1.h.

I would like to compile all the programs in k-r/chapter-1/1-1, k-r/chapter-1/1-2 and so on, and ship the executable binaries to k-r/chapter-1/exe.

Below is what I have so far and it works, but I would like to know if there is a more efficient way to do it, for example without having to list the filenames ?

CC := clang
CFLAGS := -Wall
LIB := ../ch1.c
BIN := ../exe/

.PHONY: all clean 1-1 1-2

all: 1-1 1-2

1-1:
    cd 1-1 && \
    $(CC) $(CFLAGS) program1.c -o $(BIN)program1.exe && \
    $(CC) $(CFLAGS) program2.c -o $(BIN)program2.exe && \

1-2:
    cd 1-2 && \
    $(CC) $(CFLAGS) program3.c $(LIB) -o $(BIN)program3.exe && \
    $(CC) $(CFLAGS) program4.c $(LIB) -o $(BIN)program4.exe && \

clean:
    rm exe/*



Solution 1:[1]

I am assuming that by "efficient" you mean "more generic/automated". Here is the most "efficient" solution I could think of:

CC := gcc
CFLAGS := -Wall
BIN_DIR := exe
SRC_DIRS := $(wildcard *-*)

.PHONY: all clean $(SRC_DIRS)
all: $(SRC_DIRS)

clean:
    del /s /q $(BIN_DIR)\*

define SETUP_TARGETS
$(1): $$(patsubst $(1)/%.c,$(BIN_DIR)/%.exe,$$(wildcard $(1)/*.c))
$(BIN_DIR)/%.exe: $(1)/%.c $$(wildcard *.c)
    $(CC) $(CFLAGS) $$(addprefix -I ,$$(patsubst %/,%,$$(dir $$^))) $$^ -o $$@
endef

$(foreach target,$(SRC_DIRS),$(eval $(call SETUP_TARGETS,$(target))))

Observations about the makefile:

  • Since you are using the file extension .exe, I am assuming that you are running GNU Make on MS-Windows. Hence, I used Command Prompt syntax and .exe extension.
  • You have to put the makefile in the root directory of the chapter (chapter-1/ in this case).
  • When you copy the code to your makefile, please pay attention to the indented parts (recipe of each rule). Each command line must start with a TAB character, not spaces.
  • I am assuming the directory exe/ already exists and always will be there.
  • The makefile will detect every subdirectory that matches the pattern *-* (e.g. 1-1, 1-2, ...) and will create a target for each one.
  • The command line make target (target is a subdirectory, e.g. 1-1, 1-2, ...) will create one executable in exe/ for each source file in the target subdirectory. I am assuming that each source file has a function main().
  • Each target build will also include every source file *.c from the root directory (ch1.c in this case). I am assuming that these sources do not contain a function main().
  • The files involved in the builds do not have to be named in any particular way. All files will be detected.
  • The include paths used in the compilations are the same directories that contain the sources involved, which are the target subdirectory and the root directory. Hence, you can #include header files from them to your programs.
  • The command lines make and make all will build all the targets.
  • The command line make clean will delete every file in exe/.
  • This makefile was created to minimize build operations so that only executables that are out of date with respect to one of their dependent sources will be built. If you try to rebuild a target without modifying any source, you are going to get a warning like make: Nothing to be done for '1-1'.
  • You can use this makefile for other chapters as well, as long as they have the same directory structure.

Enjoy :)

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1