Nordico
Nordico

Reputation: 1307

Fortran Makefile - Circular dependency dropped and m2c

The following is a working make; it is a simplified version of a more complex one, but it manages to reproduce the problems when I try some changes. Files "myobj.f" and "mymod.f" are in folders "src1" and "src2" respectively and are almost empty (mymod is a module that has a variable declared and myobj is a subroutine that initializes and prints that variable).

I tried googling for this, but all circular dependency errors I found seemed more clear than this. Could you please help me identify what is wrong with my makefile?

all : mylib.so
makefiles := Makefile
FC = gfortran
FFLAGS = -I$(obj_path) -J$(obj_path) -fPIC
LFLAGS = -shared -fPIC

obj_path := obj
src_paths = src1 src2
vpath %.o   $(obj_path)
vpath %.mod $(obj_path)
vpath %.f   $(src_paths)

objects = myobj.o mymod.o
obj/myobj.o : obj/mymod.mod

mylib.so : $(objects:%.o=$(obj_path)/%.o)
        $(FC) $(LFLAGS) $^ $(LIBS) -o $@

$(obj_path)/%.mod : %.o
        @touch $@

$(obj_path)/%.o   : %.f   $(makefiles) |  $(obj_path)
        $(FC) $(FFLAGS) -c $< -o $@

$(obj_path) :
        mkdir -p $@

.PHONY: clean
clean:
        rm -rf mylib.so $(obj_path)

(1) I can't simplify the rules

Having the "$(obj_path)" explicitly in the rules for compiling objects seems unnecessary: I know vpath does not work with files that change during compile time and was designed only for sources, but I don't see why clarifying the path when I list the objects as dependencies of the library shouldn't suffice.

mylib.so : $(objects:%.o=$(obj_path)/%.o)
        $(FC) $(LFLAGS) $^ $(LIBS) -o $@

%.mod : %.o
        @touch $@

%.o   : %.f   $(makefiles) |  $(obj_path)
        $(FC) $(FFLAGS) -c $< -o $@

But this doesn't work; I get a circular dependency dropped warning and make tries to compile the modules using m2c. Just in case, commenting the vpaths for .o and .mod does not fix this. Output of make --debug:

Reading makefiles...
Updating goal targets....
 File 'all' does not exist.
   File 'mylib.so' does not exist.
     File 'obj/myobj.o' does not exist.
make: Circular obj/myobj.mod <- obj/myobj.o dependency dropped.
       File 'obj/mymod.mod' does not exist.
         File 'obj/mymod.o' does not exist.
make: Circular obj/mymod.o <- obj/mymod.mod dependency dropped.
        Must remake target 'obj/mymod.o'.
m2c    -o obj/mymod.o 
make: m2c: Command not found
<builtin>: recipe for target 'obj/mymod.o' failed
make: *** [obj/mymod.o] Error 127

(2) I can't add a rule for f90

Now I want to be able to incorporate f90 code to my library. Adding the rule seems not to affect the process if nothing else changes, but when I change the extension of mymod.f to mymod.f90, it returns a similar error that the other case. The modified section is:

$(obj_path)/%.mod : %.o
        @touch $@

$(obj_path)/%.o   : %.f   $(makefiles) |  $(obj_path)
        $(FC) $(FFLAGS) -c $< -o $@

$(obj_path)/%.o   : %.f90   $(makefiles) |  $(obj_path)
        $(FC) $(FFLAGS) -c $< -o $@

And the output of make --debug is this:

Reading makefiles...
Updating goal targets....
 File 'all' does not exist.
   File 'mylib.so' does not exist.
     File 'obj/myobj.o' does not exist.
       File 'obj' does not exist.
      Must remake target 'obj'.
mkdir -p obj
      Successfully remade target file 'obj'.
       File 'obj/mymod.mod' does not exist.
         File 'obj/mymod.o' does not exist.
make: Circular obj/mymod.o <- obj/mymod.mod dependency dropped.
        Must remake target 'obj/mymod.o'.
m2c    -o obj/mymod.o 
make: m2c: Command not found
<builtin>: recipe for target 'obj/mymod.o' failed
make: *** [obj/mymod.o] Error 127

COMPILATION CONFIG 1:

Makefile:

all : mylib.so
makefiles := Makefile
FC = gfortran
FFLAGS = -I$(obj_path) -J$(obj_path) -fPIC
LFLAGS = -shared -fPIC

obj_path := obj
src_paths = src1 src2
#vpath %.o   $(obj_path)
#vpath %.mod $(obj_path)
vpath %.f   $(src_paths)

objects = myobj.o mymod.o
obj/myobj.o : obj/mymod.mod

mylib.so : $(objects:%.o=$(obj_path)/%.o)
        $(FC) $(LFLAGS) $^ $(LIBS) -o $@

%.mod : %.o
        @touch $@

%.o   : %.f   $(makefiles) |  $(obj_path)
        $(FC) $(FFLAGS) -c $< -o $@

$(obj_path) :
        mkdir -p $@

.PHONY: clean
clean:
        rm -rf mylib.so $(obj_path)

src1/myobj.f:

       subroutine myobj
       use mymod
       implicit none
       mymodx=1
       print*,'mymod was included', mymodx
       return
       end subroutine

src2/mymod.f:

       module mymod
       implicit none
       integer :: mymodx
       end module

Terminal output:

make --debug
GNU Make 4.1
Built for x86_64-unknown-linux-gnu
Copyright (C) 1988-2014 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
Reading makefiles...
Updating goal targets....
 File 'all' does not exist.
   File 'mylib.so' does not exist.
     File 'obj/myobj.o' does not exist.
make: Circular obj/myobj.mod <- obj/myobj.o dependency dropped.
       File 'obj/mymod.mod' does not exist.
         File 'obj/mymod.o' does not exist.
make: Circular obj/mymod.o <- obj/mymod.mod dependency dropped.
        Must remake target 'obj/mymod.o'.
m2c    -o obj/mymod.o 
make: m2c: Command not found
<builtin>: recipe for target 'obj/mymod.o' failed
make: *** [obj/mymod.o] Error 127

Upvotes: 0

Views: 3137

Answers (1)

casey
casey

Reputation: 6915

There are a few issues with your approach. The vpath searching only applies to pre-requisites and not targets, so a rule like

%.o: %.f

will search the $(src_paths) for the source but it will not attempt to match targets in $(obj_path). This leads to the next issue, that due to putting objects into an intermediate directory, your rule needs to account for this since vpath won't help. Your main target to build the library depends on things like $(obj_path)/blah.f so you want to use a rule like:

$(obj_path)/%.o: %.f

to match the objects. There may be a cleaner way to do this, but I am not aware of them.

Likewise, the rule with the %.mod target was not matching the requirement in the object directory and it looks like a default implicit rule was being called to handle them, attempting to invoke m2c.
Specifically, make tries to compile the mod file as modula-2 source and its automatic rule to do this conflicted with your %.o:%.mod rule and produced the circular dependency. By modifying that rule to properly match the mod files, this mess is avoided.

Also, dont forget to add a vpath for %.f90, so your rules can find those source files.

I can successfully build your test project with this slightly modified Makefile. I also used files named .f90 to show that it also solves your second issue.

FC = gfortran
FFLAGS = -I$(obj_path) -J$(obj_path) -fPIC
LFLAGS = -shared -fPIC

src_paths = src1 src2
obj_path := obj
vpath %.o $(obj_path)
vpath %.mod $(obj_path)
vpath %.f90 $(src_paths)
vpath %.f $(src_paths)

mylib_objects = myobj.o mymod.o
mylib_target = mylib.so

all: $(mylib_target)

$(mylib_target) : $(mylib_objects:%.o=$(obj_path)/%.o)
    $(FC) $(LFLAGS) $^ $(LIBS) -o $@

$(obj_path)/myobj.o: myobj.f90 $(obj_path)/mymod.mod

$(obj_path) :
    mkdir -p $@

$(obj_path)/%.mod : %.o $(obj_path)
    @true

$(obj_path)/%.o: %.f | $(obj_path)
    $(FC) $(FFLAGS) -c $< -o $@

$(obj_path)/%.o: %.f90 | $(obj_path)
    $(FC) $(FFLAGS) -c $< -o $@

clean:
    rm -rf $(mylib_target) $(obj_path) 

From a clean start, the output of make --debug is:

% make --debug
GNU Make 4.1
Built for x86_64-pc-linux-gnu
Copyright (C) 1988-2014 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
Reading makefiles...
Updating goal targets....
 File 'all' does not exist.
   File 'mylib.so' does not exist.
     File 'obj/myobj.o' does not exist.
       File 'obj' does not exist.
      Must remake target 'obj'.
mkdir -p obj
      Successfully remade target file 'obj'.
       File 'obj/mymod.mod' does not exist.
         File 'obj/mymod.o' does not exist.
        Must remake target 'obj/mymod.o'.
gfortran -Iobj -Jobj -fPIC -c src2/mymod.f90 -o obj/mymod.o
        Successfully remade target file 'obj/mymod.o'.
      Must remake target 'obj/mymod.mod'.
      Successfully remade target file 'obj/mymod.mod'.
    Must remake target 'obj/myobj.o'.
gfortran -Iobj -Jobj -fPIC -c src1/myobj.f90 -o obj/myobj.o
    Successfully remade target file 'obj/myobj.o'.
  Must remake target 'mylib.so'.
gfortran -shared -fPIC obj/myobj.o obj/mymod.o  -o mylib.so
  Successfully remade target file 'mylib.so'.
Must remake target 'all'.
Successfully remade target file 'all'.

Also note that you can instead handle your module dependency by stipulating the object files in an other that guarantees necessary mod files will have been generated. For example, changing

objects = myobj.o mymod.o

to

objects = mymod.o myobj.o

makes the rules and pre-requisites involving the mod files un-necessary, as this will cause mymod.o (and the byproduct mymod.mod) to be built before myobj.o is attempted. It is possible that this solution would break in a parallel build, so I have maintained your object ordering and made sure the rules worked as intended.

Upvotes: 2

Related Questions