Commit 9019c01f authored by Lorenz Hüdepohl's avatar Lorenz Hüdepohl Committed by Lorenz Huedepohl
Browse files

Added support for libraries

parent f9c72191
......@@ -11,7 +11,7 @@ builds work.
Usage
-----
Put this project as a directory "fdep" in your sourcecode, place the two
Put this project as a directory "fdep" in your source code, place the two
lines
m4_include([fdep/fortran_dependencies.m4])
......@@ -21,16 +21,14 @@ Usage
@FORTRAN_MODULE_DEPS@
in your Makefile.am. All .F90 files of all programs in bin_PROGRAMS will now
be scanned for modules and the resulting dependencies will be honoured.
in your Makefile.am. All .F90 files of all programs in bin_PROGRAMS and all
libraries in lib_LTLIBRARIES will now be scanned for modules and the
resulting dependencies will be honoured.
What is the problem with Fortran 90 modules and make dependencies?
------------------------------------------------------------------
This set of three files can be used to tell a GNU make running an automake
produced Makefile about dependencies of Fortran 90 files with modules.
In Fortran 90 source files one can define any number of "modules", containing
variable and function definitions. The names of the modules defined in a file
can be arbitrary.
......@@ -48,14 +46,14 @@ What is the problem with Fortran 90 modules and make dependencies?
encounters a "use" statement during the compilation of another file, it
confers to this file to import the definitions of the module.
That means, you cannot compile files using modules defined in yet uncompiled
That means, you cannot compile files using modules defined in yet un-compiled
files, one has to tell make about this dependency.
(A primitive solution to this problem is listing the file in a pre-sorted
order, so that files defining modules are compiled first.
However, the dependency-graph make knows about is incomplete and parallel
builds will fail with a high probability)
However, that way the dependency-graph make knows about is incomplete and
parallel builds will fail with a high probability)
How does fdep solve this problem technically?
......@@ -71,20 +69,22 @@ How does fdep solve this problem technically?
More specifically, the perl-script fortran_dependencies.pl is run by make to
create a file .fortran_modules/dependencies.mk, which is then included. To do
this, first every source file (for every program defined in bin_PROGRAMS) is
scanned for lines with "module" or "use" statements, which are saved in two
files (.use_mods and .def_mods) containing a list of defined and required
modules. The perl script then reads these in and produces the appropriate
rules.
create a file .fortran_dependencies/dependencies.mk, which is then included.
To do this, first every source file (for every defined program and library)
is scanned for lines with "module" or "use" statements. These are saved in
two additional files (.use_mods and .def_mods) per source file and contain
lists of defined and required modules. The perl script then reads these in
and produces the appropriate rules.
Drawbacks
---------
GNU make is required. The detailed dependency graph due to "module" and "use"
statements is only available after pre-processing, when autoconf and even
configure is long over. To still get proper depdendencies, fdep uses the GNU
make feature to include a generated sub-Makefile during a make invocation.
configure is long over. To still get proper dependencies, fdep uses GNU
make's feature to include generated sub-Makefiles during a running make
invocation.
License
......
_f90_verbose = $(_f90_verbose_$(V))
_f90_verbose_ = $(_f90_verbose_$(AM_DEFAULT_VERBOSITY))
_f90_verbose_0 = @echo " $1";
_f90_targets = $(subst .,_,$(bin_PROGRAMS) $(lib_LTLIBRARIES))
FORTRAN_CPP ?= cpp -P -traditional -Wall -Werror
# $1 source files
#
# returns: file without any .F90 .f90 .F .f extension
define strip_fortran_ext
$(patsubst %.F90,%,$(patsubst %.f90,%,$(patsubst %.F,%,$(patsubst %.f,%,$1))))
endef
# $1 program
#
# returns:
# '1' if object files for target $1 are prefixed due to 'per-target' flags,
# '' (the empty string) otherwise. See the automake manual for 'per-target'
# compilation
#
define is_per_target
$(if $(filter $(patsubst %.F90,%.o,$(firstword $($1_SOURCES))),$($1_OBJECTS)),,1)
$(if $(filter $(call strip_fortran_ext,$(firstword $(call fortran_sources,$1))),$(patsubst %.o,%,$(patsubst %.lo,%,$($1_OBJECTS)))),,1)
endef
# $1 top-level target name (i.e. an entry of _f90_targets)
#
# returns: all target source files matching *.F90 *.f90 *.F *.f
define fortran_sources
$(filter %.F90 %.f90 %.F %.f,$($1_SOURCES))
endef
# $1 top-level target name
#
# returns: the appropriate extension (i.e. 'o' for normal programs, '.lo' for libraries)
define object_extension
$(if $(filter $2,$(bin_PROGRAMS)),o,lo)
endef
# $1 source_file
# $2 stem
# $3 program
define module_targets
$(eval _$(3)_use_mods += $(dir $1)$(2)$(patsubst %.F90,%,$(notdir $1)).use_mods)
$(dir $1)$(2)$(patsubst %.F90,%,$(notdir $1)).use_mods: $1 $(dir $1)$(am__dirstamp)
$(call _f90_verbose,F90 USE [$3] $$<)$(CPP) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $($p_CPPFLAGS) $(CPPFLAGS) -o /dev/stdout $$< | grep -i -o '^ *use [^ ,!:]*' | sort -u > $$@
$(info called module_targets($1,$2,$3))
$(eval _$(3)_use_mods += $(dir $1)$(2)$(call strip_fortran_ext,$(notdir $1)).use_mods.$(call object_extension,$3))
$(dir $1)$(2)$(call strip_fortran_ext,$(notdir $1)).use_mods.$(call object_extension,$3): $1 $(dir $1)$(am__dirstamp)
$(call _f90_verbose,F90 USE [$3] $$<)$(FORTRAN_CPP) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $($p_CPPFLAGS) $(CPPFLAGS) -o /dev/stdout $$< | grep -i -o '^ *use [^ ,!:]*' | sort -u > $$@
$(eval _$(3)_def_mods += $(dir $1)$(2)$(call strip_fortran_ext,$(notdir $1)).def_mods.$(call object_extension,$3))
$(dir $1)$(2)$(call strip_fortran_ext,$(notdir $1)).def_mods.$(call object_extension,$3): $1 $(dir $1)$(am__dirstamp)
$(call _f90_verbose,F90 MOD [$3] $$<)$(FORTRAN_CPP) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $($p_CPPFLAGS) $(CPPFLAGS) -o /dev/stdout $$< | grep -i -o '^ *module [^!]*' > $$@ || true
$(eval _$(3)_def_mods += $(dir $1)$(2)$(patsubst %.F90,%,$(notdir $1)).def_mods)
$(dir $1)$(2)$(patsubst %.F90,%,$(notdir $1)).def_mods: $1 $(dir $1)$(am__dirstamp)
$(call _f90_verbose,F90 MOD [$3] $$<)$(CPP) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $($p_CPPFLAGS) $(CPPFLAGS) -o /dev/stdout $$< | grep -i -o '^ *module [^!]*' > $$@ || true
endef
$(foreach p,$(bin_PROGRAMS),$(if $(call is_per_target,$p),$(foreach s,$($p_SOURCES),$(eval $(call module_targets,$s,$p-,$p))),$(foreach s,$($p_SOURCES),$(eval $(call module_targets,$s,,$p)))))
$(foreach p,$(_f90_targets),$(if $(call is_per_target,$p),$(foreach s,$(call fortran_sources,$p),$(eval $(call module_targets,$s,$p-,$p))),$(foreach s,$(call fortran_sources,$p),$(eval $(call module_targets,$s,,$p)))))
_f90_moddir=$(abs_builddir)/.fortran_modules
_f90_depfile = $(_f90_moddir)/dependencies.mk
_f90_depdir=$(abs_builddir)/.fortran_dependencies
_f90_depfile = $(_f90_depdir)/dependencies.mk
define is_clean
$(if $(filter-out mostlyclean clean distclean maintainer-clean,$(MAKECMDGOALS)),0,1)
......@@ -31,15 +63,15 @@ endef
ifneq ($(call is_clean),1)
include $(_f90_depfile)
endif
$(_f90_depfile): $(top_srcdir)/fdep/fortran_dependencies.pl $(foreach p,$(bin_PROGRAMS),$(_$p_use_mods) $(_$p_def_mods)) | $(foreach p,$(bin_PROGRAMS),$(_f90_moddir)/$p)
$(call _f90_verbose,F90 DEPS $@)echo > $@; $(foreach p,$(bin_PROGRAMS),$(top_srcdir)/fdep/fortran_dependencies.pl $(_$p_use_mods) $(_$p_def_mods) >> $@; )
$(_f90_depfile): $(top_srcdir)/fdep/fortran_dependencies.pl $(foreach p,$(_f90_targets),$(_$p_use_mods) $(_$p_def_mods)) | $(foreach p,$(_f90_targets),$(_f90_depdir)/$p)
$(call _f90_verbose,F90 DEPS $@)echo > $@; $(foreach p,$(_f90_targets),$(top_srcdir)/fdep/fortran_dependencies.pl $(_$p_use_mods) $(_$p_def_mods) >> $@; )
$(_f90_moddir):
$(_f90_depdir):
@mkdir $@
$(foreach p,$(bin_PROGRAMS),$(_f90_moddir)/$p): | $(_f90_moddir)
$(foreach p,$(_f90_targets),$(_f90_depdir)/$p): | $(_f90_depdir)
@mkdir $@
CLEANFILES += $(foreach p,$(bin_PROGRAMS),$(_$p_def_mods) $(_$p_use_mods))
CLEANFILES += $(foreach p,$(bin_PROGRAMS),$(_f90_moddir)/$p/*)
CLEANFILES += $(foreach p,$(_f90_targets),$(_$p_def_mods) $(_$p_use_mods))
CLEANFILES += $(foreach p,$(_f90_targets),$(_f90_depdir)/$p/*)
CLEANFILES += $(_f90_depfile)
......@@ -37,14 +37,14 @@ foreach my $file (@ARGV) {
my $re;
my $add;
my $object;
if ($file =~ /(.*).def_mods$/) {
if ($file =~ /^(.*)\.def_mods(\..*)$/) {
$re = $def_re;
$add = \&add_def;
$object = $1 . ".o";
} elsif ($file =~ /^(.*).use_mods$/) {
$object = $1 . $2;
} elsif ($file =~ /^(.*)\.use_mods(\..*)$/) {
$re = $use_re;
$add = \&add_use;
$object = $1 . ".o";
$object = $1 . $2;
} else {
die "Unrecognized file extension for '$file'";
}
......@@ -62,5 +62,11 @@ foreach my $file (@ARGV) {
}
foreach my $object (sort keys %uses) {
print "$object: ", join(" ", sort (map {$defs{$_}} keys $uses{$object})), "\n";
for my $m (keys $uses{$object}) {
if (defined $defs{$m}) {
print "$object: ", $defs{$m}, "\n";
} elsif (defined($ENV{V}) && $ENV{V} eq "1") {
print STDERR "Warning: Cannot find definition of module $m in files for current program, might be external\n";
}
}
}
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment