Andrew
Andrew

Reputation: 734

Compile multiple executables from multiple source directories to single bin directory using makefile

I'm trying to create a makefile for a suite of programs that I am working on. The programs are all written in fortran and the source files are contained in different directories. I can't seem how to figure out how to get things to work. My current sumfile is

#Compiler and compiler flag variables
FCOMP=/usr/local/bin/gfortran
F_FLAGS=-O2 -fbounds-check -Wall 
F_FLAGSDB=-g -fbounds-check -Wall

#paths to libraries
COMMON_LIB=/usr/local/lib/libspc_common.a
SPICE_LIB=/usr/local/lib/spicelib.a

# Paths to directories
BIN_DIR=BIN

# Get file names of component source files
#get names of files in src1
FORT_FILES=$(wildcard ./SRC1/*.f)
#get names of files in src2 
FORTFILES+=$(wildcard ./SRC2/*.f)   
#get names of files in src3 
FORTFILES+=$(wildcard ./SRC3/*.f)   

#get file names for output
EXE_FILES=$(addprefix $(BIN_DIR),$(notdir $(patsubst %.f, % , $(FORTFILES))))

# make commands
# Set the default option to compile the library with optimization
default: all

# create all command 
all: $(EXE_FILES)
    @echo toolkit has been built with optimization

#If compiling for debugging replace the compiler flags to remove optimization and add debugging
debug: F_FLAGS=$(F_FLAGSDB)
#Run compiler with debugging flags
debug: $(EXE_FILES)
    @echo toolkit has been built with debugging

# Compile all of the source files into executables 
$(EXE_FILES): % : %.f
    $(FCOMP) $(F_FLAGS) $^ $(COMMON_LIB) $(SPICE_LIB) -o $(BIN_DIR)/$@

# install the library in /usr/local/lib
install: 
    cp -p $(BIN_DIR)* /usr/local/bin/toolkit/   

# remove executable files for a clean build
clean:
    rm $(BIN_DIR)*

The problem I am running into is that I get the following error when I try to run make:

make: *** No rule to make target `Display.f', needed by `Display'.  Stop.

which I am assuming is because I have lost the directory that the source file comes from. Can someone help me here? I am totally stuck and don't know how to proceed.

In addition (this is more a general question about make), is there a way to tell make to recompile everything if the COMMON_LIB changes?

Thanks for your help!

Upvotes: 1

Views: 539

Answers (1)

Beta
Beta

Reputation: 99104

Suppose your source files are

SRC1/alpha.f
SRC1/beta.f
SRC2/gamma.f
SRC3/delta.f

1) There is a flaw here:

EXE_FILES=$(addprefix $(BIN_DIR),$(notdir $(patsubst %.f, % , $(FORTFILES))))

This will produce

BINalpha BINbeta BINgamma BINdelta

when I think you intended

BIN/alpha BIN/beta BIN/gamma BIN/delta

A simple fix:

EXE_FILES=$(addprefix $(BIN_DIR)/,$(notdir $(patsubst %.f, % , $(FORTFILES))))

2) Now look at the static pattern rule:

$(EXE_FILES): % : %.f
    ...

So to build BIN/alpha, Make must first find BIN/alpha.f, which doesn't exist. To make it look for alpha.f, do this:

$(EXE_FILES): $(BIN_DIR)/% : %.f
    ...

3) How to find the sources?

You could do some delicate coding to help Make remember where it found alpha.f, but there's no need when we can use the vpath directive:

vpath %.f SRC1 SRC2 SRC3

4) One last look at that rule:

This command:

$(FCOMP) $(F_FLAGS) $^ $(COMMON_LIB) $(SPICE_LIB) -o $(BIN_DIR)/$@

Will produce e.g. BIN/BIN/alpha, which is silly. A non-PHONY Make rule should produce a file whose name is the target of the rule. It prevents a lot of trouble.

$(FCOMP) $(F_FLAGS) $^ $(COMMON_LIB) $(SPICE_LIB) -o $@

A few further refinements may be possible, once you have this working perfectly.

Upvotes: 2

Related Questions