Ingulit
Ingulit

Reputation: 1141

Makefile for shared library that only compiles changed files?

I am working on a project where I am creating a .so object as my output that contains several operators for use in another program. I was given a Makefile that works just fine, except it always recompiles every file whenever I run make. Originally this was not an issue, but now that there are upwards of five or more operators in the library, running make is prohibitively slow. The trouble is that for the life of me, I can't figure out how to change this Makefile into one that behaves the way I want since I've never dealt with Makefiles in this format before:

BOOST_LOCATION=/usr/local/boost_1_54_0

CFLAGS=-pedantic -W -Wextra -Wall -Wno-strict-aliasing -Wno-long-long -Wno-unused-parameter -fPIC -D__STDC_FORMAT_MACROS -Wno-system-headers -isystem -O2 -g -DNDEBUG -ggdb3  -D__STDC_LIMIT_MACROS
INC=-I. -DPROJECT_ROOT="\"$(IN_SOURCE_DIR)\"" -I"$(IN_SOURCE_DIR)/include" -I"$(BOOST_LOCATION)"
LIBS=-L"$(IN_SOURCE_DIR)/lib" -shared -Wl,-soname,libname.so -L. -lm

all:
    @if test ! -d "$(IN_SOURCE_DIR)"; then echo  "Error. Try:\n\nmake IN_SOURCE_DIR=<PATH TO SOURCE TRUNK>"; exit 1; fi 
    $(CXX) $(CFLAGS) $(INC) -o plugin.cpp.o -c plugin.cpp

    $(CXX) $(CFLAGS) $(INC) -o LogicalFile1.cpp.o -c File1/LogicalFile1.cpp
    $(CXX) $(CFLAGS) $(INC) -o PhysicalFile1.cpp.o -c File1/PhysicalFile1.cpp

    $(CXX) $(CFLAGS) $(INC) -o LogicalFile2.cpp.o -c File2/LogicalFile2.cpp
    $(CXX) $(CFLAGS) $(INC) -o PhysicalFile2.cpp.o -c File2/PhysicalFile2.cpp

    ### etc.

    $(CXX) $(CFLAGS) $(INC) -o libname.so \
                               plugin.cpp.o \
                               LogicalFile1.cpp.o \
                               PhysicalFile1.cpp.o \
                               LogicalFile2.cpp.o \
                               PhysicalFile2.cpp.o \
                               ### etc \
                               $(LIBS)

clean:
    rm -f *.o *.so

The biggest issue I have is that there is only one rule (all), and I cannot find an example of a Makefile that does this or how to split it into multiple rules.

Upvotes: 0

Views: 316

Answers (2)

Carl Norum
Carl Norum

Reputation: 225012

In this case, it's actually pretty straightforward. Right now, you just have an all target that's doing all the building. You need to break out the compiler invocations and link step into their own rules, and you should be off to the races:

BOOST_LOCATION=/usr/local/boost_1_54_0

CFLAGS=-pedantic -W -Wextra -Wall -Wno-strict-aliasing -Wno-long-long -Wno-unused-parameter -fPIC -D__STDC_FORMAT_MACROS -Wno-system-headers -isystem -O2 -g -DNDEBUG -ggdb3  -D__STDC_LIMIT_MACROS
INC=-I. -DPROJECT_ROOT="\"$(IN_SOURCE_DIR)\"" -I"$(IN_SOURCE_DIR)/include" -I"$(BOOST_LOCATION)"
LIBS=-L"$(IN_SOURCE_DIR)/lib" -shared -Wl,-soname,libname.so -L. -lm

all: plugin.cpp.o LogicalFile1.cpp.o PhysicalFile1.cpp.o LogicalFile2.cpp.o PhysicalFile2.cpp.o
    $(CXX) $(CFLAGS) $(INC) -o libname.so \
                               plugin.cpp.o \
                               LogicalFile1.cpp.o \
                               PhysicalFile1.cpp.o \
                               LogicalFile2.cpp.o \
                               PhysicalFile2.cpp.o \
                               ### etc \
                               $(LIBS)

plugin.cpp.o: plugin.cpp | test
    $(CXX) $(CFLAGS) $(INC) -o plugin.cpp.o -c plugin.cpp

LogicalFile1.cpp.o: File1/LogicalFile1.cpp | test
    $(CXX) $(CFLAGS) $(INC) -o LogicalFile1.cpp.o -c File1/LogicalFile1.cpp

PhysicalFile1.cpp.o: File1/PhysicalFile1.cpp | test
    $(CXX) $(CFLAGS) $(INC) -o PhysicalFile1.cpp.o -c File1/PhysicalFile1.cpp

LogicalFile2.cpp.o: File2/LogicalFile2.cpp | test
    $(CXX) $(CFLAGS) $(INC) -o LogicalFile2.cpp.o -c File2/LogicalFile2.cpp

PhysicalFile2.cpp.o: File2/PhysicalFile2.cpp | test
    $(CXX) $(CFLAGS) $(INC) -o PhysicalFile2.cpp.o -c File2/PhysicalFile2.cpp

test:
    @if test ! -d "$(IN_SOURCE_DIR)"; then echo  "Error. Try:\n\nmake IN_SOURCE_DIR=<PATH TO SOURCE TRUNK>"; exit 1; fi 

clean:
    rm -f *.o *.so

.PHONY: all clean test

From this point, you can simplify further, too. You could consolidate all of the compile lines into a single pattern rule, for example.

Upvotes: 1

MadScientist
MadScientist

Reputation: 100916

If you're willing to use the standard formats and built-in rules for make, you can write your entire makefile as easily as this:

ifeq (,$(wildcard $(IN_SOURCE_DIR)/.))
    $(error Try: make IN_SOURCE_DIR=<PATH TO SOURCE TRUNK>)
endif

OBJECTS = plugin.o LogicalFile1.o PhysicalFile1.o LogicalFile2.o PhysicalFile2.o  ### etc
BOOST_LOCATION = /usr/local/boost_1_54_0

CPPFLAGS = -DNDEBUG -D__STDC_FORMAT_MACROS -D__STDC_LIMIT_MACROS -I. -DPROJECT_ROOT="\"$(IN_SOURCE_DIR)\"" -I"$(IN_SOURCE_DIR)/include" -I"$(BOOST_LOCATION)"
CXXFLAGS = -pedantic -W -Wextra -Wall -Wno-strict-aliasing -Wno-long-long -Wno-unused-parameter -fPIC -Wno-system-headers -isystem -O2 -g -ggdb3
LDFLAGS = -L"$(IN_SOURCE_DIR)/lib" -L.
LDLIBS = -shared -Wl,-soname,libname.so -lm

all: libname.so

libname.so: $(OBJECTS)
        $(LINK.cc) $^ $(LDLIBS) -o $@

clean:
        rm -f *.o *.so

.PHONY: all clean

Upvotes: 1

Related Questions