user3273814
user3273814

Reputation: 218

Why is my makefile not working as expected?

Here is my makefile:

SHELL = /bin/sh
CC=g++
CFLAGS=-I.
DEPS = settings.h
OBJ = settings.o tomato.o
EXDIR = $(ROOT_TOMATO)/bin
OBJDIR = $(ROOT_TOMATO)/obj

$(OBJDIR)/%.o: %.cpp $(DEPS)
    $(CC) -c -o $@ $<

$(EXDIR)/tomato: $(OBJ)
    $(CC) -o $@ $^ $(CFLAGS)

clean:
    rm -f a.out *.o

all: tomato

On line 9 and 10, I have attempted to get it to create object files and put them in the OBJDIR, but it instead places the obj files in the current directory, ROOT_TOMATO/src:

$(OBJDIR)/%.o: %.cpp $(DEPS)
    $(CC) -c -o $@ $<

I can't figure out why it is not working. Perhaps there are better ways, but I also like to know why my code doesn't work in this particular case.

As a side note, the makefile is called by another makefile:

#Main makefile for project

#Get root compile directory
ROOT_TOMATO = $(shell pwd)
export ROOT_TOMATO

All:
   $(MAKE) -C src

Upvotes: 1

Views: 1401

Answers (2)

Norman Gray
Norman Gray

Reputation: 12514

Make is a language where ‘keep it simple’ is great advice. It's a language where simple things are simple; hard things look like line-noise.

Remember that Make is a sort of executable build-process documentation – readability is a Good Thing.

I'd rewrite your Makefile as something like

CXX=g++
OBJ=settings.o tomato.o
TARGET=tomato

%.o: %.cpp
    $(CXX) -c -o $@ $<


# default target
$(TARGET): $(OBJ)
    $(CXX) -o $(TARGET) $(OBJ) $(CFLAGS)

tomato.o: settings.h
settings.o: settings.h

clean:
    rm $(TARGET) *.o

That is:

  1. It's conventional to use CXX as the macro for the C++ compiler, and leave CC for the C compiler – that prompts anyone reading the rules to jump to the right conclusion.
  2. Keep the %.o: %cpp rule simple. So, you end up with lots of *.o files in the current directory - big deal! Put them in your Mercurial/Git ignore file and move on. You can put them into another directory, and @Wintermute shows how to do that, but it needs more punctuation, and doesn't really gain much.
  3. Notice how I haven't needed to use any $(VAR:sub=result) constructions. They're useful, but if you use too many of them, the result quickly becomes unreadable.
  4. Be reluctant to put dependencies in % rules. Expressing those separately in action-less rules (as with the dependencies for tomato.o and settings.o) documents that ‘there's nothing fancy about these modules’ and keeps everything looking cleaner.

A complicated makefile which does everything automatically is a great way of procrastinating (take it from me, oh brother...).

Upvotes: 0

Wintermute
Wintermute

Reputation: 44073

Where you say

$(EXDIR)/tomato: $(OBJ)

The prerequisites for $(EXDIR)/tomato are not $(OBJDIR)/settings.o and $(OBJDIR)/tomato.o but just plain settings.o and tomato.o, as you defined in OBJ. Therefore, the pattern rule is not used to build them, and make falls back on implicit rules to build settings.o and tomato.o.

You could instead use

$(EXDIR)/tomato: $(OBJ:%=$(OBJDIR)/%)

...or set OBJ so that it contains these paths from the get-go.

Note that your clean rule has a similar problem, so having a variable that contains the actual object paths would be sensible. You could use it both in the $(EXDIR)/tomato prerequisites and in the clean recipe.

Also note that the default rule for the inner Makefile is not all but $(EXDIR)/tomato, because it is the first for a specific target. It's sort of a good thing here; the all rule would not work as intended because its prerequisite is tomato, for which there is no rule, rather than $(EXDIR)/tomato. I suspect you'll want to fix that and move the all rule to the top at some point, though.

Upvotes: 3

Related Questions