makefilegnu-make

Makefile: Generate rules from a pair of variables


I have a Makefile which has two variables:

SOURCE_DIR := ./source
BUILD_DIR := ./build
C_FILES := $(shell find $(SOURCE_DIR) -name '*.c')
BIN_FILES := $(patsubst $(SOURCE_DIR)%.c,$(BUILD_DIR)%, $(C_FILES))

Both $(C_FILES) and $(BIN_FILES) are two strings. One with the paths of all the existing .c files separated by SPACE and another is a string of all corresponding executable files (which have no extension and are yet to be generated)

My next intended step is to write a rule that builds a binary for each "element" in BIN_FILES using its corresponding "element" in C_FILES. However, I am very new to make and I still cannot figure out how to write a rule that can iterate over a pair of variables.

I tried:

$(BIN_FILES): %:%.c
    cc $< -o $@
all: $(BIN_FILES)

But when I ran make all it kept insisting that build/ch01/hello.c which makes sense since it is looking for ./build/... like patterns. How do I make make look for s./ource/... patterns for prerequisites, and at the same time build the executable in ./build/..

Any help on generating and calling the rules using the variables $(BIN_FILES) and $(C_FILES) would be much appreciated.


Solution

  • In situations like this, we're looking for a 'pattern' rule.

    The pattern is:

    To make ./build/<file> we depend on ./source/<file.c>

    So the pattern rule would look like:

    $(BUILD_DIR)/%: $(SOURCE_DIR)/%.c
    

    if the two % signs match up in terms of source and destination, then it will be able to build.

    So if we have ./source/a.c we want to end up with ./build/a

    The entirety of the makefile for this would look something like:

    SOURCE_DIR := ./source
    BUILD_DIR := ./build
    C_FILES := $(shell find $(SOURCE_DIR) -name '*.c')
    BIN_FILES := $(patsubst $(SOURCE_DIR)%.c,$(BUILD_DIR)%, $(C_FILES))
    
    all: $(BIN_FILES)
    
    $(BUILD_DIR)/%: $(SOURCE_DIR)/%.c
        $(CC) $< -o $@