Jorge Leitao
Jorge Leitao

Reputation: 20173

proper usage of makefile

So, I'm trying to understand makefiles. At this moment I have a project with some modules, which are more or less non-dependent on each others.

My directory tree is like this:

root/
- Source/ <-- referenced as $(INPUTPATH)
- Build/ <-- referenced as $(BUILDPATH)
- Release/ <-- referenced as $(OUTPUTPATH)
- Makefile

I want to have the source (.cpp/.h) on the Source/, the objects on the Build/, and the executable on the Release/*.

So, my idea was to have several different targets, and use them like this:

all: maps auxiliars methods
    @echo "linking...";\
    $(CC) $(CFLAGS) $(OBJS) $(CLIBS) -o ${OUTPUTPATH}/MainProgram

dependency1: $(INPUTPATH)/foo.cpp
    @echo "compiling ArbPrecision...";\
    cd ${BUILDPATH};\
    $(CC) $(CFLAGS) $(CINCLUDE) -c ../$?

dependency2: dependency1 $(INPUTPATH)/bar.cpp
    @echo "compiling saddleConstructor...";\
    cd ${BUILDPATH};\
    $(CC) $(CFLAGS) $(CINCLUDE) -c ../$(INPUTPATH)/bar.cpp

maps: dependency2 $(INPUTPATH)/*Map.cpp
    @echo "compiling maps...";\
    cd ${BUILDPATH};\
    $(CC) $(CFLAGS) $(CINCLUDE) -c ../$(INPUTPATH)/*Map.cpp
... (auxiliars and methods)

The $CINCLUDE and CFLAGS are just compiler stuff like headers and external libs.

Everything works almost perfect: on every target, it asks for other target's dependencies, and for the *cpp on the source. If this does not exist, it calls the target and compiles the *cpp. The executable is created without errors.

However, if I call the "make" twice, the time it takes to compile is the same for the first and second time, even if I don't make any changes on the source. So, apparently, I'm not putting the dependencies right.

Can anyone point me the right direction to perform this compilation? I'm doing something wrong and I'm not getting what it is.

Thanks, Jorge

Upvotes: 2

Views: 404

Answers (1)

Josh Kelley
Josh Kelley

Reputation: 58442

Make expects to operate off of files. For each rule, it checks to see if the target file named by that rule (1) exists and (2) is up to date (i.e., modified after any of its dependencies). If the file is not up to date, then it executes the rule to bring it up to date.

Therefore, as your makefile is currently written, when you run make, Make does the following:

  • all is the first rule, so if you don't specify any arguments, make processes the all rule.
  • No file named all exists, so the all rule needs to be executed.
  • all depends on the dependency1 rule. No file named dependency1 exists, so the dependency1 rule needs to be executed.
  • dependency1 depends on the dependency2 rule. No file named dependency2 exists, so the dependency2 rule needs to be executed.

What you instead want is something similar to the following:

OBJS := foo.o bar.o Map.o

all: $(OUTPUTPATH)/MainProgram

$(OUTPUTPATH)/MainProgram: $(OBJS)
    $(CC) $(CFLAGS) $(OBJS) $(CLIBS) -o $(OUTPUTPATH)/MainProgram

%.o: %.cpp
    $(CC) $(CFLAGS) $(CINCLUDE) $< -c $@

.PHONY: all

Note the following:

  • Since Make operates on files, rules' targets and dependencies are listed by filename wherever possible.
  • Rather than listing the .cpp files individually, an pattern rule is used to build each .o file. Within the pattern rule, the $< and $@ automatic variables are used to specify the first prerequisite (file) and the target (file), respectively.
  • Make echoes its commands by default, so there's no need to echo yourself.
  • For GNU Make, all is listed as a phony target, to keep Make from looking for a file named all.

Upvotes: 3

Related Questions