makefile

Single makefile to make for all targets


I have a makefile with 2 different targets - one builds for LINUX executable and the other builds for WINDOWS executable.

linux: $(LINUX_OBJS)
    @echo Linking: $@
    @mkdir -p $(BUILD_DIR)
    @mkdir -p $(BUILD_DIR)/$@
    @$(CC) $(LFLAGS) app.c $+ -o $(BUILD_DIR)/$@/app $(INCLUDES) $(LINUX_INCS)

win: $(WIN_OBJS)
    @echo Linking: $@
    @mkdir -p $(BUILD_DIR)
    @mkdir -p $(BUILD_DIR)/$@
    @$(CC) $(LFLAGS) app.c $+ -o $(BUILD_DIR)/$@/app.exe $(INCLUDES) $(WIN_INCS)

## compiling
$(BUILD_DIR)/linux/%.o $(BUILD_DIR)/win/%.o: $(ROOT_PATH)/%.c
    @echo target: $? \($@\)
    @mkdir -p $(dir $@)
    @$(CC) $(CFLAGS) $(WFLAGS) -c -o $@ $^ $(INCLUDES) $(WIN_INCS) $(LINUX_INCS)

clean:
    @rm -f -r $(BUILD_DIR)

I want to add an all option, to build both for linux and for windows, so I added:

all: clean linux win

The problem is that when I it starts building for windows, it only compiles the new files that are relevan for windows only (from $WIN_OBJS)) and therefore fails to link it all.

How can I make it build them both? (so I can keep all object files and final linked execution in corresponding build directory, without being deleted between them)?


Solution

  • You should show us what the values of the WIN_OBJS and LINUX_OBJS variables are and how they are set; without this info we may be missing something important.

    However, this rule will definitely not do what you appear to think that it will:

    $(BUILD_DIR)/linux/%.o $(BUILD_DIR)/win/%.o: $(ROOT_PATH)/%.c
            @echo target: $? \($@\)
            @mkdir -p $(dir $@)
            @$(CC) $(CFLAGS) $(WFLAGS) -c -o $@ $^ $(INCLUDES) $(WIN_INCS) $(LINUX_INCS)
    

    One thing you should absolutely do is remove the @ prefixes when you're trying to debug makefiles. Having them there is like sending your compiler output to /dev/null then trying to understand why it fails.

    You may be thinking the above is the same as writing two different pattern rules but that's not true at all. See Multiple Targets in a Rule for details, but what this tells make is that ONE invocation of the recipe will build both of the targets. That is of course not true for your recipe, it will only build one of the targets.

    You must write the pattern twice if you want make to invoke the recipe separately for Windows and Linux objects. If you want to avoid duplicating the recipe, you can put it into a variable:

    COMPILE.c = echo target: $? \($@\); \
            mkdir -p $(dir $@); \
            $(CC) $(CFLAGS) $(WFLAGS) -c -o $@ $^ $(INCLUDES) $(WIN_INCS) $(LINUX_INCS)
    
    $(BUILD_DIR)/linux/%.o: $(ROOT_PATH)/%.c
            @$(COMPILE.c)
    
    $(BUILD_DIR)/win/%.o: $(ROOT_PATH)/%.c
            @$(COMPILE.c)