Reputation: 1307
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)
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
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
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
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