Search code examples
bashmakefilecompilationrulessubdirectory

Makefile - Compile all sub directories and output .o files in a separate directory


I have this command from my bash script

find . -type f -name "*.c" -execdir bash -c "f={}; kos-cc $KOS_CFLAGS -c {} -o $PWD/\${f%.c}.o" \;

Its job it to recursively search the current directory ($PWD) for .c files, compile them with my "kos-cc" compiler then output all the .o files into the current working directory.

I want to move parts of my bash script into a makefile and this line is the last one that stumps me. I know how to make rules that compiles c files from directory A and outputs the .o files into directory B, but here directory A isn't always the same (Since I need to handle sub directories too). How do I do the equivalent task in a Makefile either with a rule or a command?


Solution

  • It is not trivial because you have your sources in different directories and simple make pattern rules cannot easily handle this. But using slightly more advanced make features can make it:

    SRC := $(shell find . -type f -name "*.c")
    OBJ := $(patsubst %.c,%.o,$(notdir $(SRC)))
    
    .PHONY: all    
    all: $(OBJ)
    
    define COMPILE_rule
    $$(patsubst %.c,%.o,$$(notdir $(1))): $(1)
        kos-cc $$(KOS_CFLAGS) -c -o $$@ $$<
    endef
    $(foreach s,$(SRC),$(eval $(call COMPILE_rule,$(s))))
    
    .PHONY: clean
    clean:
        rm -f $(OBJ)
    

    Beware: you could have several source files with the same base name in different directories. And if this happens, you will end up with a fatal conflict of object file names...

    EDIT (add conflicts detection):

    The following detects conflicts situations and issues an error (replace error by warning or info depending on the severity level you assign to these conflicts):

    SRC := $(shell find . -type f -name "*.c")
    OBJ := $(patsubst %.c,%.o,$(notdir $(SRC)))
    
    ifneq ($(words $(OBJ)),$(words $(sort $(OBJ))))
    $(error object file name conflicts detected)
    endif
    

    (sort sorts and also removes duplicates and words returns the number of space-separated words in its string parameter).