David Kiger
David Kiger

Reputation: 1996

Different dependency locations in Makefile based on target

I'm trying to set up a Makefile to handle two different targets from one set of sources, and I'm a little out of my element. The vast majority of it works fine, but my dependency structure is hosed and thus I'm forced to do a full recompile each time. A pared down sample is as follows:

first:  OBJDIR = obj
second: OBJDIR = obj-2

SRCS = $(wildcard src/*.cc)

OBJECTS = $(patsubst %.cc,$(OBJDIR)/%.o,$(SRCS))

first:  CFLAGS = -g -Wall -Wextra -std=c++11 -MMD
second: CFLAGS = -g -Wall -Wextra -std=c++11 -MMD -DCOMPILE_FLAG

$(OBJDIR)/%.o: %.cc
    @mkdir -p $(OBJDIR)/src
    clang++ -c $(CFLAGS) -o $(OBJDIR)$@ $<

#DEPENDENCIES AREN'T WORKING PROPERLY
-include $(OBJECTS:.o=.d)

first: $(OBJECTS)
    clang++ -o gen/first $(OBJECTS)

second: $(OBJECTS)
    clang++ -o gen/second $(OBJECTS)

If I @echo $(OBJECTS:.o=.d) under my first: executable generation (or as it's used in the compilation step), it properly expands to /obj/src/filename.d. However, when it's in the include line, it shows up simply as /src/filename.d. Obviously the .d files don't exist in that location, so it doesn't find them and does a full recompile.

Makefiles are not something I'm heavily experienced with, so if there are better ways to do anything up above, I'm all ears. The key point, though, is being able to have two sets of object files and two sets of dependencies from the same sources.

--

To clarify on the eventual goals, what I have is a set of source files that are used to build two separate executables, with the differences handled via #ifdefs.

What I want to get out of the makefile structure is a single makefile with two targets. Each target generates its own .o/.d files, so that when a change is made to the source, I can run make first and make second to generate the two new executables without having to recompile everything from scratch. I've handled this previously by having two separate makefiles, but that just seems wrong.

Upvotes: 0

Views: 265

Answers (2)

David Kiger
David Kiger

Reputation: 1996

I believe the answer is simply to rethink the way I was doing it. I've rewritten the makefile to be as follows (trimming out anything unrelated), after reading a lot of Makefile documentation and taking into consideration some comments from MadScientist:

CC      = clang++
SRCS    = $(wildcard src/*.cc)
OBJECTS = $(patsubst %.cc,$(OBJDIR)/%.o,$(SRCS))
CFLAGS  = -g -Wall -Wextra -std=c++11 -MMD 

.PHONY: all clean run

all: $(EXECUTABLE)

-include $(OBJECTS:.o=.d)

$(EXECUTABLE): $(OBJECTS)
    @mkdir -p gen
    $(CC) -o gen/$(EXECUTABLE) $(OBJECTS)

$(OBJDIR)/%.o: %.cc
    @mkdir -p $(@D)
    $(CC) -c $(CFLAGS) $(CMDFLAGS) -o $@ $<

clean:
    rm -rf obj obj-2 gen

run:
    cd gen && ./$(EXECUTABLE)

From there, I made a couple aliases in my .bash_profile:

alias mfirst="make OBJDIR=obj EXECUTABLE=first"
alias msecond="make OBJDIR=obj-2 CMDFLAGS=-DCOMPILE_FLAG EXECUTABLE=second"

Because the variables are now set outside of any target specification, everything plays nicely. It keeps the object files and the dependencies separate, and the aliases still allow quick usage (including mfirst run, for example).

Feel free to point out any flaws here, but I'm fairly happy with the result.

Upvotes: 0

MadScientist
MadScientist

Reputation: 101081

You've missed a critical sentence in the GNU make manual related to target-specific variables:

As with automatic variables, these values are only available within the context of a target's recipe

This means that you can't use target-specific variables in either the target or prerequisite lists: any variables used there will ALWAYS have the global value and never the target-specific value. Similarly, include lines are parsed as the makefile is read in: there's no target context at all here either so the global value is used.

Further, any time you see ANY rule in a makefile that is creating a file which is not exactly $@ but is instead some modification of it, that's a red flag and you know you have a problem: -o $(OBJDIR)$@ is wrong.

In general, there's a lot wrong with this makefile and it's not entirely clear exactly what you're trying to do here. Maybe if you stepped back and described the goal you want to achieve we can give you some pointers.

Upvotes: 1

Related Questions