2

I'm running a makefile using mingw on windows. I've seen a lot of SO links about this topic, but they all seem to be for C or c++. I'm not sure if the same rules apply, and since I'm using windows, syntax seems to be a bit different too. Here are some of the other references:

How to place object files in separate subdirectory (most promising, I think)

Using a make file to compile files in separate directories

Flat object file directory structure output with GNU Make

What I currently have is (verbatim)

VPATH =\
    user \
    static \
    computations \
    solvers\\steadyState \
    solvers\\transient \
    solvers\\transient\\momentum \
    solvers\\transient\\induction

FC      = gfortran
TOP_DIR = .
MOD_DIR = $(TOP_DIR)\\mod
OBJ_DIR = $(TOP_DIR)\\obj

FCFLAGS = -g
FCFLAGS += -J$(MOD_DIR) -fopenmp -fimplicit-none -Wuninitialized

TARGET = parametricStudy

SRCS_F =\
    user\\constants.f90 \
    static\\myExceptions.f90 \
    static\\myDebug.f90 \
    static\\scalarField.f90 \
    static\\vectorField.f90 \
    static\\myIO.f90 \
    user\\simParams.f90 \
    static\\solverSettings.f90 \
    static\\myTime.f90 \
    computations\\myError.f90 \
    static\\coordinates.f90 \
    user\\griddata.f90 \
    static\\myAllocate.f90 \
    static\\BCs.f90 \
    user\\rundata.f90 \
    computations\\myDel.f90 \
    computations\\vectorOps.f90 \
    static\\myExport.f90 \
    computations\\applyBCs.f90 \
    solvers\\steadyState\\mySOR.f90 \
    solvers\\steadyState\\myPoisson.f90 \
    solvers\\transient\\induction\\initializeBBCs.f90 \
    solvers\\transient\\induction\\initializeBfield.f90 \
    solvers\\transient\\induction\\initializeSigmaMu.f90 \
    solvers\\transient\\momentum\\initializeUBCs.f90 \
    solvers\\transient\\momentum\\initializeUfield.f90 \
    solvers\\transient\\inductionSolver.f90 \
    solvers\\transient\\momentumSolver.f90 \
    solvers\\transient\\MHDSolver.f90 \
    user\\MOONS.f90 \
    parametricStudy.f90

OBJS_F = $(patsubst %.f90,$(OBJ_DIR)\\%.o,$(notdir $(SRCS_F)))

all: $(TARGET)

$(TARGET): $(OBJS_F)
    $(FC) -o $@ $(FCFLAGS) $(OBJS_F)

$(OBJ_DIR)\\%.o: %.f90
    $(FC) $(FCFLAGS) -c -o $@ $<
clean:
    del $(OBJ_DIR)\\*.o $(MOD_DIR)\\*.mod parametricStudy.exe
list:;  @echo " "
    @echo " "
    @echo "Source files:"
    @echo $(SRCS_F)
    @echo " "
    @echo "Object files:"
    @echo $(OBJS_F)
    @echo " "
    @echo "Compiler          : $(FC)"
    @echo "Include directory : $(INC_DIR)"
    @echo "Root directory    : $(ROOT_DIR)"
    @echo "Bin directory     : $(BIN_DIR)"
    @echo "Modules directory : $(MOD_DIR)"
    @echo "Modules directory : $(MOD_DIR)"
    @echo "Object directory  : $(OBJ_DIR)"
    @echo " "

Using this, I can execute the following (again, verbatim)

C:\Users\Charlie\Desktop\development\FORTRAN_LIB>gmake
gfortran -g -J.\\mod -fopenmp -fimplicit-none -Wuninitialized -c -o .\\obj\const
ants.o user/constants.f90
gfortran -g -J.\\mod -fopenmp -fimplicit-none -Wuninitialized -c -o .\\obj\myExc
eptions.o static/myExceptions.f90
gfortran -g -J.\\mod -fopenmp -fimplicit-none -Wuninitialized -c -o .\\obj\myDeb
ug.o static/myDebug.f90
gfortran -g -J.\\mod -fopenmp -fimplicit-none -Wuninitialized -c -o .\\obj\scala
rField.o static/scalarField.f90
gfortran -g -J.\\mod -fopenmp -fimplicit-none -Wuninitialized -c -o .\\obj\vecto
rField.o static/vectorField.f90
gfortran -g -J.\\mod -fopenmp -fimplicit-none -Wuninitialized -c -o .\\obj\myIO.
o static/myIO.f90
gfortran -g -J.\\mod -fopenmp -fimplicit-none -Wuninitialized -c -o .\\obj\simPa
rams.o user/simParams.f90
gfortran -g -J.\\mod -fopenmp -fimplicit-none -Wuninitialized -c -o .\\obj\solve
rSettings.o static/solverSettings.f90
gfortran -g -J.\\mod -fopenmp -fimplicit-none -Wuninitialized -c -o .\\obj\myTim
e.o static/myTime.f90
gfortran -g -J.\\mod -fopenmp -fimplicit-none -Wuninitialized -c -o .\\obj\myErr
or.o computations/myError.f90
gfortran -g -J.\\mod -fopenmp -fimplicit-none -Wuninitialized -c -o .\\obj\coord
inates.o static/coordinates.f90
gfortran -g -J.\\mod -fopenmp -fimplicit-none -Wuninitialized -c -o .\\obj\gridd
ata.o user/griddata.f90
gfortran -g -J.\\mod -fopenmp -fimplicit-none -Wuninitialized -c -o .\\obj\myAll
ocate.o static/myAllocate.f90
gfortran -g -J.\\mod -fopenmp -fimplicit-none -Wuninitialized -c -o .\\obj\BCs.o
 static/BCs.f90
gfortran -g -J.\\mod -fopenmp -fimplicit-none -Wuninitialized -c -o .\\obj\runda
ta.o user/rundata.f90
gfortran -g -J.\\mod -fopenmp -fimplicit-none -Wuninitialized -c -o .\\obj\myDel
.o computations/myDel.f90
gfortran -g -J.\\mod -fopenmp -fimplicit-none -Wuninitialized -c -o .\\obj\vecto
rOps.o computations/vectorOps.f90
gfortran -g -J.\\mod -fopenmp -fimplicit-none -Wuninitialized -c -o .\\obj\myExp
ort.o static/myExport.f90
gfortran -g -J.\\mod -fopenmp -fimplicit-none -Wuninitialized -c -o .\\obj\apply
BCs.o computations/applyBCs.f90
gfortran -g -J.\\mod -fopenmp -fimplicit-none -Wuninitialized -c -o .\\obj\mySOR
.o solvers\\steadyState/mySOR.f90
gfortran -g -J.\\mod -fopenmp -fimplicit-none -Wuninitialized -c -o .\\obj\myPoi
sson.o solvers\\steadyState/myPoisson.f90
gfortran -g -J.\\mod -fopenmp -fimplicit-none -Wuninitialized -c -o .\\obj\initi
alizeBBCs.o solvers\\transient\\induction/initializeBBCs.f90
gfortran -g -J.\\mod -fopenmp -fimplicit-none -Wuninitialized -c -o .\\obj\initi
alizeBfield.o solvers\\transient\\induction/initializeBfield.f90
gfortran -g -J.\\mod -fopenmp -fimplicit-none -Wuninitialized -c -o .\\obj\initi
alizeSigmaMu.o solvers\\transient\\induction/initializeSigmaMu.f90
gfortran -g -J.\\mod -fopenmp -fimplicit-none -Wuninitialized -c -o .\\obj\initi
alizeUBCs.o solvers\\transient\\momentum/initializeUBCs.f90
gfortran -g -J.\\mod -fopenmp -fimplicit-none -Wuninitialized -c -o .\\obj\initi
alizeUfield.o solvers\\transient\\momentum/initializeUfield.f90
gfortran -g -J.\\mod -fopenmp -fimplicit-none -Wuninitialized -c -o .\\obj\induc
tionSolver.o solvers\\transient\\induction/inductionSolver.f90
gfortran -g -J.\\mod -fopenmp -fimplicit-none -Wuninitialized -c -o .\\obj\momen
tumSolver.o solvers\\transient\\momentum/momentumSolver.f90
gfortran -g -J.\\mod -fopenmp -fimplicit-none -Wuninitialized -c -o .\\obj\MHDSo
lver.o solvers\\transient/MHDSolver.f90
gfortran -g -J.\\mod -fopenmp -fimplicit-none -Wuninitialized -c -o .\\obj\MOONS
.o user/MOONS.f90
gmake: *** No rule to make target `.\\obj\parametricStudy.o', needed by `paramet
ricStudy'.  Stop.

C:\Users\Charlie\Desktop\development\FORTRAN_LIB>gfortran -g -J.\\mod -fopenmp -
c -o .\\obj\parametricStudy.o parametricStudy.f90

C:\Users\Charlie\Desktop\development\FORTRAN_LIB>gmake
gfortran -o parametricStudy -g -J.\\mod -fopenmp -fimplicit-none -Wuninitialized
 .\\obj\constants.o .\\obj\myExceptions.o .\\obj\myDebug.o .\\obj\scalarField.o
.\\obj\vectorField.o .\\obj\myIO.o .\\obj\simParams.o .\\obj\solverSettings.o .\
\obj\myTime.o .\\obj\myError.o .\\obj\coordinates.o .\\obj\griddata.o .\\obj\myA
llocate.o .\\obj\BCs.o .\\obj\rundata.o .\\obj\myDel.o .\\obj\vectorOps.o .\\obj
\myExport.o .\\obj\applyBCs.o .\\obj\mySOR.o .\\obj\myPoisson.o .\\obj\initializ
eBBCs.o .\\obj\initializeBfield.o .\\obj\initializeSigmaMu.o .\\obj\initializeUB
Cs.o .\\obj\initializeUfield.o .\\obj\inductionSolver.o .\\obj\momentumSolver.o
.\\obj\MHDSolver.o .\\obj\MOONS.o .\\obj\parametricStudy.o

Note that after I receive the error, I can explicitly compile the parametricStudy.f90 after the error with:

 gfortran -g -J.\\mod -fopenmp -c -o .\\obj\parametricStudy.o parametricStudy.f90

and then type

 gmake

Again, which results in no errors. I'm very puzzled.

Here is a screenshotenter image description here of my directory:

Maybe it has something to do with TARGET? It seems like the path of the last file is somehow wrong. Any help is greatly appreciated!

Community
  • 1
  • 1
Charles
  • 947
  • 1
  • 15
  • 39

2 Answers2

3

The problem you're facing is that with

ROOT_DIR = "C:\Users\Charlie\"
OBJ_DIR = $(ROOT_DIR)\obj

the rule

$(OBJ_DIR)/%.o: %.f90

expands to

"C:\Users\Charlie\"obj/%.o: %.f90

which is parsed as the static pattern rule

"C: \Users\Charlie\"obj/%.o: %.f90

That is to say, with target "C, target pattern \Users\Charlie\"obj/%.o and prerequisite pattern %.f90. make complains that "C does not match the pattern \Users\Charlie\"obj/%.o.

There is some hacky code in GNU make (at least in MinGW; I think Cygwin behaves differently because it expects you to work with its unix-ish directory structure) to recognize absolute Windows paths, but it does not handle quoting. As long as your OBJ_DIR does not contain spaces, using

ROOT_DIR = C:\Users\Charlie\

should make the Windows path recognition kick in.

However...it is rather unusual to see absolute paths in handcrafted Makefiles. Are you sure you want to do it this way? A more common approach would be to work with relative paths so that the Makefile does not have to be changed if the source code is copied to a different directory. Assuming the Makefile is in the root directory, that would be

ROOT_DIR = .

or just nixing the ROOT_DIR variable altogether and saying

MOD_DIR = mod
OBJ_DIR = obj

Oh, and to answer the next question that's going to crop up: In order to make make use the

$(OBJ_DIR)/%.o: %.f90

rule, you'll need to make $(TARGET) have prerequisites that match the $(OBJ_DIR)/%.o pattern. That could be

OBJS_F = $(patsubst %.f90,$(OBJ_DIR)/%.o,$(notdir $(SRCS_F)))
Wintermute
  • 42,983
  • 5
  • 77
  • 80
  • When I try what you suggested, I get the error "gmake: *** No rule to make target `obj/constants.o', needed by `parametricStudy' . Stop." – Charles Jan 24 '15 at 05:56
  • I think what I'm not understanding is what your saying about $(TARGET) having prerequisites that match the $(OBJ_DIR)%.o pattern.. Does that mean that the main program needs to be in the .o directory?.. – Charles Jan 24 '15 at 06:08
  • 1
    Where the main program lies does not matter, but its prerequisites have to match the pattern for a rule that knows how to make them. Hmmm...I think I see the problem. With `(OBJ_DIR)/%.o : %.f90`, the `.f90` files would have to lie directly in the working directory, which is not the case. And it's difficult to reconstruct the `%.f90` on which a particular `%.o` depends. Your original makefile doesn't do that either, though, so you should at least be able to reproduce the old behavior (that everything is recompiled when one .f90 changes) with `$(OBJ_DIR)/%.o: $(SRCS_F)` – Wintermute Jan 24 '15 at 11:35
  • Im really new to using make files.. I would like if only the files that need to be compiled are compiled when changes are made to .f90 files.. I may re-post this question with a different, more appropriate title, and go through the errors and issues with each form of the make files that I've tried.. – Charles Jan 24 '15 at 17:30
3

I think the following style of makefile solves the problems. I tested it with a specimen C project, not fortran, but that is immaterial to the make problem and solution.

# VPATH: Tell `make` to look for in `user` for prerequisites it can't find here
VPATH = user
# If e.g. you also want `make` to look for for prerequisites in `../include`, then:
# VPATH = user:../include
FC      = gfortran
TOP_DIR = .
MOD_DIR = $(TOP_DIR)\\mod
OBJ_DIR = $(TOP_DIR)\\obj

FCFLAGS = -g
FCFLAGS += -J$(MOD_DIR) -fopenmp -fimplicit-none -Wuninitialized

TARGET = parametricStudy

SRCS_F =\
    user\\constants.f90 \
    ...
    parametricStudy.f90

OBJS_T1 = $(patsubst %.f90,%.o,$(SRCS_F))
OBJS_T2 = $(notdir $(OBJS_T1))
# The object files are all to be obj\<name>.o
OBJS_F = $(patsubst %.o,$(OBJ_DIR)\\%.o,$(OBJS_T2))

all: $(TARGET)

$(TARGET): $(OBJS_F)
    $(FC) -o $@ $(FCFLAGS) $(OBJS_F)

# How to make an obj\*.o from the matching *.f90. `make` considers the VPATH
$(OBJ_DIR)\\%.o: %.f90
    $(FC) $(FCFLAGS) -c -o $@ $<
clean:
    del $(OBJ_DIR)\\*.o $(MOD_DIR)\\*.mod parametricStudy.exe

Using a relative path TOP_DIR = . rather than an absolute ROOT_DIR is good advice already offered by @Wintermute.

To enable the required pattern rule:

$(OBJ_DIR)\\%.o: %.f90

to kick in, you must make it appear to make that any prerequisite *.f90 is right here, as per the pattern, not in some other directory like, e.g. user\constants.f90 That is what the VPATH achieves.

Continued for later developments

Not yet having seen a listing of the directory where the makefile resides I can only venture a hypothesis, but the hypothesis suggested by what I do see is:

The directory does not actually contain a file called parametricStudy.f90, but a file called parametricStudy.F90, and if it is renamed to parametricStudy.f90, then the makefile will find and compile it.

Is that right?

How this explains the facts: The pattern rule:

$(OBJ_DIR)\\%.o: %.f90

is failing to match any parametricStudy.f90, so there is no such file. You say however that:

gfortran -g -J.\\mod -fopenmp -c -o .\\obj\parametricStudy.o parametricStudy.f90

successfully compiles.

You are building on Windows, so the toolchain subscribes to Windows' file-handling protocols. Filenames are case-insentive: parametricStudy.f90 will identify parametricStudy.F90, if it exists, and .F90 will be interpreted by gfortran (on Windows or anywhere else) as denoting a Fortran 90 source file. Thus the successful commandline compile.

But the pattern rule is indifferent to file-handling protocols. It is just a pattern matching rule, and the invariant .f90 is not matched by .F90.

Mike Kinghan
  • 55,740
  • 12
  • 153
  • 182
  • This is so close to fully working! All of the files, except the last one (parametricStudy.f90) are compiled to .object files. It would be really nice for that last .f90 file to be located in an absolute path (I want to separate my code and simulation results). Is there a way that I can make VPATH include the root directory? (I tried ".", but that didn't work). It would be even nicer if I could make VPATH include an absolute path. Also, the executable is not being made since the last file is not successfully made. – Charles Jan 25 '15 at 17:26
  • parametricStudy.f90 does not get compiled? There must be still be a mistake in the makefile. Suggest you post it exactly as it is now. – Mike Kinghan Jan 25 '15 at 17:45
  • P.S. Am I right in assuming that `parametricStudy.f90` *is in* the directory where you are running `make`? – Mike Kinghan Jan 25 '15 at 17:52
  • And just to be sure, `parametricStudy.f90` *is in* the directory where you are running make? – Mike Kinghan Jan 25 '15 at 22:28
  • Yeah, I've got makefile, parametricStudy, user (folder), etc. – Charles Jan 25 '15 at 22:32
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/69572/discussion-between-charlie-and-mike-kinghan). – Charles Jan 25 '15 at 22:33
  • Would, but it's my bedtime. Work tomorrow. Back on the case tomorrow night. Cheers. – Mike Kinghan Jan 25 '15 at 22:40
  • no, the file parametricStudy.f90 exists in the same directory as the makefile before compile time – Charles Jan 28 '15 at 22:52
  • @Charlie The issue isn't whether the file exists in the correct directory, but whether it has a correct extension. An extension of `f9O` or `F90` might look correct, but this will confuse `make. – Ross Ridge Jan 28 '15 at 22:55
  • @MikeKinghan You are absolutely right. That one file, was the only one that had the extension .F90, all others, for some reason were .f90. THANK YOU! – Charles Jan 28 '15 at 22:58
  • @Charlie You're welcome. That was a workout. There are some lose ends to tie off in my answer but it's bedtime again. Do it tomorrow. – Mike Kinghan Jan 28 '15 at 23:03