yu quan
yu quan

Reputation: 161

makefile avoid recompilation when source is unchanged

I'm new to makefile stuff, I cook up this makefile from online examples to build my project and it works. Turns out that when I run it again without changing any source code, all the object files are re-compiled. I think make should only recompile those targets which has changed input files. Why is this happening? =.=

#makefile
ROOTDIR = $(dir $(CURDIR))

icc iccclean: PLAT = icc
gcc gccclean: PLAT = gcc
clang clangclean: PLAT = clang

icc: CC = icpc
gcc: CC = g++
clang: CC = clang-omp++

#these are not important and I removed the details
LIBS = 
INC = 
FLAG = 
DEF = 

icc gcc clang:  run_de

SRCDIR = $(ROOTDIR)source   #path to all .cpp .h files
OBJDIR = $(ROOTDIR)build/$(PLAT)   #path of output .o files
TARGET = $(ROOTDIR)run_de.$(PLAT)  #final executable product

#the final product depends on object files core.o eval.o file_processing.o

run_de: $(OBJDIR)/core.o $(OBJDIR)/eval.o $(OBJDIR)/file_processing.o
    cd $(OBJDIR); $(CC) $(LIBS) $(FLAG) $(INC) $(DEF) -o $(TARGET) core.o eval.o file_processing.o

#rules of making the object files

$(OBJDIR)/core.o:   $(SRCDIR)/core.cpp
    $(CC) -c $(FLAG) $(LIBS) $(INC) $(DEF) $(SRCDIR)/core.cpp -o $(OBJDIR)/core.o
$(OBJDIR)/eval.o:   $(SRCDIR)/eval.cpp
    $(CC) -c $(FLAG) $(LIBS) $(INC) $(DEF) $(SRCDIR)/eval.cpp -o $(OBJDIR)/eval.o
$(OBJDIR)/file_processing.o:    $(SRCDIR)/file_processing.cpp
    $(CC) -c $(FLAG) $(LIBS) $(INC) $(DEF) $(SRCDIR)/file_processing.cpp -o $(OBJDIR)/file_processing.o

gccclean iccclean clangclean:
    $(RM) $(OBJDIR)/*.o $(TARGET)

.Phony: icc gcc clang iccclean gccclean clangclean

Upvotes: 0

Views: 616

Answers (2)

yu quan
yu quan

Reputation: 161

I spent many hours to come out with a functioning-as-expected Makefile... The thing is there are many details about makefile that cause bugs that are very difficult to notice.

Not very easy to put things in different directories. Better just keep everything in one folder, unless there are very strong reasons.

#makefile
#One lesson learnt is that the makefile scope and shell command scope are different variable workspaces.
#Commands that are after a rule name is within shell command scope.
#variables in makefile scope are transparenet in the shell command scope. But not vice-versa.
#variables defined or modified in shell command scope cannot be correctly retrived in the makefile scope......
#This is a bloody source of bugs which is v difficult to debug.

icc iccclean: PLAT = icc
gcc gccclean: PLAT = gcc
clang clangclean: PLAT = clang

icc: CC = icpc
gcc: CC = g++
clang: CC = clang-omp++

TARGET = ../run_de.$(MAKECOMGOALS)
SOURCE = $(wildcard ../source/*.cpp)

OBJECT_temp = $(subst ../source/,$(MAKECMDGOALS)/,$(SOURCE))
OBJECT = $(subst .cpp,.o,$(OBJECT_temp))

LIBS = -lgsl -lgslcblas

gcc clang: LIBS += -lm

INC = 
clang: LIBS += -L/usr/local/Cellar/gsl/1.16/lib
clang: INC += -I/usr/local/Cellar/boost/1.58.0/include
clang: INC += -I/usr/local/Cellar/gsl/1.16/include -Wall -Wno-format-extra-args

FLAG = -std=c++11
clang gcc: FLAG += -fopenmp
icc: FLAG += -openmp

DEF = 
gcc icc: DEF += -DLenovoDebian
clang: DEF += -DyuMac
icc gcc clang:  $(TARGET)

$(TARGET): $(OBJECT)
    $(CC) -o $(TARGET) $(OBJECT) $(LIBS) $(INC) $(FLAG) $(DEF)

$(MAKECMDGOALS)/%.o: ../source/%.cpp
    $(CC) -c $< -o $@ $(LIBS) $(INC) $(FLAG) $(DEF)

gccclean iccclean clangclean:
$(RM) $(PLAT)/*.o $(TARGET)

.PHONY: icc gcc clang iccclean gccclean clangclean

Upvotes: 0

Jonathan Leffler
Jonathan Leffler

Reputation: 753695

As noted in comments, I probably wouldn't bother with the sub-directory for the object files. Using it makes the makefile harder to write, and I'm lazy and don't find any major benefit to working harder than necessary.

Note that CC is the macro for the C compiler; CXX is usually the macro for the C++ compiler.

I'd have the object files and source files in the current directory. Then I'd use a makefile similar to:

icc   iccclean:   PLAT = icc
gcc   gccclean:   PLAT = gcc
clang clangclean: PLAT = clang

icc:   CXX = icpc
gcc:   CXX = g++
clang: CXX = clang-omp++

LDLIBS = 
LDFLAGS = 

SOURCE = eval.cpp core.cpp file_processing.cpp
OBJECT = ${SOURCE:.cpp=.o}
TARGET = run_de.${PLAT}

icc gcc clang:  ${TARGET}

${TARGET}: ${OBJECT}
    ${CXX} -o $@ ${OBJECT} ${LDFLAGS} ${LDLIBS}

gccclean iccclean clangclean:
    $(RM) ${OBJECT}

.PHONY: icc gcc clang iccclean gccclean clangclean

When you've got that working, you can start refining it in whatever ways you want. You'll need to review what the relevant flags are for compiling C++ source to object files. You can place object files in other directories if you wish. You can even put the source files in a different directory. But learn to walk before you start running.

And do use macros for lists of files, etc, so you don't have to repeat yourself.

Upvotes: 2

Related Questions