Potion
Potion

Reputation: 946

Makefile Mark Dependency, Compile and Link

I have a modular implementation of barriers done in openmp. I have a test harness that I would want to link. The directory looks like so:

Project/
  -Tests
     -openmp_tournament
        -test_openmp_tournament.cpp
  -OpenMP
     -tournament.cpp .   //barrier implementation
     -tournament.h

Now both .cpp files have a header included for tournament.h in Project/OpenMp/. The idea is to compile both then link them together in a modular manner (because I have various implementations I want to test). My makefile looks like this:

PATH_TO_OPENMP = ../../OpenMP/

#OpenMP Flags Etc.
OMPFLAGS = -fopenmp -DLEVEL1_DCACHE_LINESIZE=`getconf LEVEL1_DCACHE_LINESIZE`
OMPLIBS = -lgomp

CC = g++
CPPFLAGS = -MMD -MP # enables automatic dependency tracking
CFLAGS = -Wall -Wextra -I$(PATH_TO_OPENMP) $(OMPFLAGS) -std=c++11
LDLIBS = $(OMPLIBS)

COMPILE = $(CC) $(CPPFLAGS) $(CFLAGS) $^ $(LDLIBS) -o $@
COMPILE_NOLINK = $(CC) -c $(CPPFLAGS) $(CFLAGS) $< $(LDLIBS) -o $@

all: tournament_openmp.a

# link all files into one binary
tournament_openmp.a: tournament.o test_openmp_tournament.o 
    $(COMPILE)

# compile only each source file and mark as dependent
%.o: $(PATH_TO_OPENMP)%.cpp %.d
    $(COMPILE_NOLINK)

# Empty pattern rule to match dependency (*.d) files (i.e. makefiles),
# so make won't fail if dependency doesn't exist
%.d: ;

# Mark dependency files as precious so they won't be deleted as intermediates
.PRECIOUS: %.d

# The list of all source files I want to track dependencies for
SOURCES=$(wildcard *.cpp)

# Include any dependency files that exist, and
# suppress error message for ones that don't yet (via hyphen)
-include $(SOURCES:.cpp=.d)

.PHONY: clean
clean:
    rm -rf *.o *.d *.a *_openmp *_mpi

When I run Make, I get the following error and I am unsure how to resolve it. It cannot find tournament.h for the test_openmp_tournament.cpp file:

a@ubuntu:~/Documents/Project/Tests/openmp_tournament$ make all
g++ -c -MMD -MP  -Wall -Wextra -I../../OpenMP/ -fopenmp -DLEVEL1_DCACHE_LINESIZE=`getconf LEVEL1_DCACHE_LINESIZE` -std=c++11 ../../OpenMP/tournament.cpp -lgomp -o tournament.o
g++  -MMD -MP   -c -o test_openmp_tournament.o test_openmp_tournament.cpp
test_openmp_tournament.cpp:51:10: fatal error: tournament.h: No such file or directory
   51 | #include "tournament.h"

Upvotes: 0

Views: 224

Answers (1)

MadScientist
MadScientist

Reputation: 101081

Why can't the compiler find your header file? Well, we should always start from the assumption that the compiler is right, and you're doing something wrong :). So, take a look at the compile line that works:

g++ -c -MMD -MP  -Wall -Wextra -I../../OpenMP/ -fopenmp ...

then the one that fails:

g++  -MMD -MP   -c ...

It's pretty obvious why the compiler can't find your headers, right? Because, you didn't give it the correct compiler flags.

So the question becomes, why isn't make running the compile command we expect? The only reason make would do that is because it's using two different rules to build the two targets. So let's take a look at your rule:

%.o: $(PATH_TO_OPENMP)%.cpp %.d
        $(COMPILE_NOLINK)

Well, this tells make how to build a file xxx.o from a file ../../OpenMP/xxx.cpp for any xxx. That works great for the source file in the ../../OpenMP directory, but of course this rule cannot be used to build any object file where there is no source file in ../../OpenMP.

So, what will make do if your rule doesn't match? It will look through its own built-in rules and sure enough, there's a built-in rule that knows how to build an object file from a source file in the current directory.

Why does this fail? It fails because you are using non-standard variables, and so the built-in rule that make uses is not using your custom variables.

In particular, the CPPFLAGS variable is intended to hold C/C++ preprocessor flags, so technically the -I option (which is a preprocessor option) should appear in that variable. Second, the standard variables for the C++ compiler are CXX for the compiler (not CC, which is the C compiler) and CXXFLAGS for the flags (not CFLAGS, which are for the C compiler).

So, you should do at least one, and possibly both, of the following:

Fix your variable names to be standard:

CXX = g++
CPPFLAGS = -MMD -MP -I$(PATH_TO_OPENMP)
CXXFLAGS = -Wall -Wextra $(OMPFLAGS) -std=c++11

Create a rule to build local objects instead of the default rule:

%.o: %.cpp %.d
        $(COMPILE_NOLINK)

(the astute will notice that OMPFLAGS contains a preprocessor flag in it as well: the -D option... it's up to you whether you want to try to tease that out or not).

Upvotes: 2

Related Questions