autotools: Fortran module dependencies - fortran

I have two sources files:
! main.f90
program main
use foo
write(*,*) x
end program main
! foo.f90
module foo
real, parameter :: x = 3.14
end module foo
First build is successful. If I now set x = 5 it again builds without errors but the program still prints 3.14. Adding
%.o %.mod: %.f90
$(FC) -c $<
main.o: foo.mod
to my Makefile.am makes the problem disappear but this is probably not the correct way to do it.
Some googling reveals that the correct Fortran dependency tracking has been a known problem with Autotools, although I'm not sure if some solution has been found by now. Is there a simple way to fix the above problem?

Related

Mixed Fortran/C++ with intel compilers: undefined references to 'system_'

I am working on a large mixed-language code in Fortran 1990 and C++ 11. I recently compiled on a new platform with intel compilers (which I have successfully used before), specifically Intel Version 19.1.1.217.
I have boiled the issue down to a simple test:
f.F90
subroutine ff()
implicit none
call system('echo y')
end subroutine ff
main.cc
#include <iostream>
extern "C"
{
void ff_(void);
}
int main(void)
{
ff_();
return 0;
}
I have emulated exactly how my code is built, which boils down to this:
mpif90 -f90=ifort -c f.F90 -o f.o
mpicxx -cxx=icpc -c main.cc -o main.o
mpicxx -cxx=icpc main.o f.o -L/software/intel/2020.1/compilers_and_libraries/linux/lib/intel64 -lifcore
...after which I get the following:
f.o: In function `ff_':
f.F90:(.text+0xd): undefined reference to `system_'
I am aware that calls to system() are not part of the stardard, but I can successfully compile and run the following with no problem:
p.F90:
program ff
implicit none
call system('echo y')
end program
mpif90 -f90=ifort p.f90 -o pexe && ./pexe
y
This suggests to me that using mpif90 -f90=ifort is imlicitly telling the linker to link in some extra libraries, but running ldd on the resulting executable shows a subset of the libraries linked in when I compile a simple C++ program.
What do I need to link to stop this error?
Well, I stumbled upon the solution immediately after asking this question. By using mpif90 -f90=ifort -v (source files) I was able to see everything that was being linked, and tried each library one at a time until it worked.

Mixed programming - Including C++ header to Fortran

I am trying to use a function from library written in C++ in my program written in Fortran. The C++ library is summarized in one header file so that if you want to use it in another C++ program you only do #include functions.h I would like to find out how to do something similar in Fortran.
From my research I've created this minimal viable example:
clib/functions.h:
#ifndef ADD_H
#define ADD_H
extern "C"
{
int __stdcall add(int x, int y);
}
#endif
clib/functions.cpp:
extern "C"
{
int __stdcall add(int x, int y)
{
return x + y;
}
}
cinclude.c
#include "clib/functions.h"
cinterface.f95:
module cinterface
use,intrinsic::ISO_C_BINDING
integer(C_INT)::a,b
interface
integer(C_INT) function add(a,b) bind(C,name="add")
use,intrinsic::ISO_C_BINDING
implicit none
!GCC$ ATTRIBUTES STDCALL :: add
!DEC$ ATTRIBUTES STDCALL :: add
integer(C_INT), value ::a,b
end function add
end interface
end module cinterface
main.f90
program main
use cinterface
implicit none
integer :: c
c = add(1,2)
write(*,*) c
end program
makefile:
FC = gfortran
CC = g++
LD = gfortran
FFLAGS = -c -O2
CFLAGS = -c -O2
OBJ=main.o
DEP = \
cinterface.o cinclude.o
.SUFFIXES: .f90 .f95 .c .o
# default rule to make .o files from .f files
.f90.o : ; $(FC) $(FFLAGS) $*.f90 -o $*.o
.f95.o : ; $(FC) $(FFLAGS) $*.f95 -o $*.o
.c.o : ; $(CC) $(CFLAGS) $*.c -o $*.o
%.o: %.mod
#
main.ex: ${DEP} ${OBJ}
$(LD) ${DEP} ${OBJ} -o prog.exe
#
When I try to make this project using Cygwin I get a following error:
main.o:main.f90:(.text+0x13): undefined reference to `add'
main.o:main.f90:(.text+0x13): relocation truncated to fit: R_X86_64_PC32 against undefined symbol `add'
collect2: error: ld returned 1 exit status
make: *** [makefile:19: main.ex] Error 1
How can I make add function in Fortran work?
You are most of the way there. There are two things you need to address to make this work: linkage and argument passing conventions.
Linkage
As francescalus noted, the Fortran compiler doesn't understand how to parse a C/C++ header file. So your functions.h and cinclude.c files aren't going to be of any use in this example.
Don't throw away your functions.h yet, though. In it you declare the add function as:
extern "C"
{
int __stdcall add(int x, int y);
}
The extern "C" is the important part. This tells g++ that the symbols in the following block of code aren't subject to all of the C++ name mangling. You'll need the same surrounding the add definition in functions.cpp.
extern "C"
{
int add(int x, int y)
{
return x + y;
}
}
Once you've done that all you will need to link are functions.o, cinterface.o/mod, and main.o.
Argument passing conventions
The way add is declared the arguments x and y are passed to the function by value. That is the default behavior for C/C++ function arguments. Fortran, on the other hand, defaults to passing arguments to functions/subroutines by reference. In C++ this would look like int add(int* x, int* y). There are two ways to address this problem.
The first option is to redefine your add function with integer pointers for the arguments and dereference them inside the function.
extern "C"
{
int add(int* x, int* y)
{
return *x + *y;
}
}
The second option (IMHO the preferred option) is to declare the Fortran interface to pass the arguments by value. They aren't being modified in the add function...why pass them by reference? If you choose this option, then your cinterface.f95 will need to contain the following declaration of add:
integer(C_INT) function add(a,b) bind(C,name="add")
use,intrinsic::ISO_C_BINDING
implicit none
integer(C_INT),value::a,b
end function add
Note the additional value decoration on the variables a and b. No matter which option you go with, without it on my machine I get 8393540 printed out as the result of the add function call. After addressing the argument passing conventions I get 3 printed out as expected.
A build system can simplify this significantly (albeit at the cost of introducing a complex build system). Assuming the directory layout in your question (although without the the cinclude.c since I don't see what purpose it serves)
$ tree
.
├── cinterface.f90
├── clib
│   ├── CMakeLists.txt
│   ├── functions.cpp
│   └── functions.h
├── CMakeLists.txt
└── main.f90
The contents of the The cmake files are
$ cat CMakeLists.txt
cmake_minimum_required(VERSION 3.9)
project(cpp-add LANGUAGES C CXX Fortran)
add_subdirectory(clib)
add_executable(glue cinterface.f90 main.f90)
target_link_libraries(glue PUBLIC cpp-clib)
and
$ cat clib/CMakeLists.txt
add_library(cpp-clib functions.cpp)
The project can then be configured and built in the usual way:
$ cmake -H. -Bbuild && cmake --build build
Execution:
$ build/glue
3
I was having the same issue, can you show us the g++ compile line?
My issue was caused because my make file didn't properly include the appropriate .o file within the compilation for the .exe
i.e I had something like
Test: Test.cpp dependancy.o
g++ Test.cpp -o test.exe
and I was getting the same error returned that you did.
I solved it by ensuring the .o was actually used on the compile line.
Test: Test.cpp dependancy.o
g++ dependancy.o Test.cpp -o test.exe
I suggest this because the error "Undefined Symbol" typically means the compiler doesn't actually know where the code is for your call to the add function.

Trying to compile simple project that uses TRI DDS using cygwin gcc

I am trying to take the TRI DDS example code. It is all setup to build with MSVS2012 and comes with MSVS2012 proj/solution files.
However I want to try to build this using Cygwin and g++/gcc. I have got so far and then hit issues.
My cpp/h files are taken from their example - the user code is basic c++ but the generated files / RTI DDS files I think are causing an issue.
The basic source files are:
Hello.cpp
HelloPublisher.cpp/h
HelloSubscriber.cpp/h
HelloWorld.idl
RTI-DDS generator uses HelloWorld.idl to generate further files (.cxx/h files). I am not expecting to change any of the RTI-DDS files and the code within the 4 source files are fairly vanilla, so I can compile them if I hack out all the calls to RTI-DDS.
The area I want to focus on is the makefile / environment. Here are the pertinent parts of my makefile (note the linker parts are not complete because I have not got that far yet - the compile still fails):
note NDDSHOME = c:\Program Files\rti_connext_dds-5.3.0 as an env var.
# Setup minimal "un-polluted" paths to try to avoid any conflicts
makePaths=/usr/local/bin:/usr/bin:$(NDDSHOME)/bin
export PATH := $(makePaths)
#Include Paths
INCLUDES = -I. -I"$(NDDSHOME)/include" -I"$(NDDSHOME)/include/ndds" -I"$(NDDSHOME)/include/ndds/hpp"
#Compiler / options:
DEFINES = -DRTI_WIN32 -DRTI_STATIC -DWIN32 -D_WIN32_WINNT=_WIN32_WINNT_WIN7
CC = g++
CC_FLAGS = -std=c++11 -Wall -Wextra -lWS2_32
EXEC = run
# C++ files
SOURCES = $(wildcard *.cpp)
OBJECTS = $(SOURCES.cpp=.o)
# Main Target
$(EXEC): $(OBJECTS)
# Don't do linking yet - compile not working!
%.o: %.cpp
$(CC) $< -o $# -c $(DEFINES) $(CC_FLAGS) $(INCLUDES)
I know the INCLUDES paths are working because I get different errors when they are not (can't find...)
But the error I get at the moment is:
/usr/include/w32api/winsock2.h:1004:68: error: conflicting declaration of c function 'int gethostname(char*, int)'
:
:
/usr/include/sys/unistd.h:268:6 note: previous declration 'int gethostname(char *, size_t)'
note I have to copy this by hand from my other PC... so I have abbreviated the complete message.
I thought I was close to solving this by looking at other questions with similar errors, but I can't quite make the connection to fix this. I have tried adding other defines to specify the windows version, but that did not fix it. I know there is a linker option that we need to use for things like mingw that is what the -lws2_32 flag is set for - but I have not got to the linker stage yet :(
I guess either the unistd.h or the winsock2.h should not really both be included, but I can't quite figure out what define (or other) I need to add...
Any ideas?

Fortran unidentified reference to 'gplot_'

For the past several days I haven't been able to get some Fortran code to compile (note that I am not the author of this code, I downloaded it from the author's web page. It's open source). The main program is called multitaper.f95, and it uses modules called plot and spectra, as well as libraries called mwlib.a and gplot.a. I have multitaper.f95, mwlib.a, gplot.a, plot.mod, and spectra.mod all in the same directory for simplicity. The plot and spectra moduels are made from gplot.f90 and mtspec.f95, respectively, which are also in the same directory as everything else. However, the following command produces an error message:
gfortran multitaper.f95 mtspec.f95 gplot.f90 -o multitaper -L gplot.a mwlib.a
/tmp/ccBJzwYI.o: In function `MAIN__':
multitaper.f95:(.text+0x1111): undefined reference to `gplot_'
multitaper.f95:(.text+0x11af): undefined reference to `gplot_'
multitaper.f95:(.text+0x1246): undefined reference to `gplot_'
collect2: error: ld returned 1 exit status
The contents of gplot.f90 are
module plot
!
! Interface to allow for x or y to be a matrix, and plot each vector
! of y against x. The matrix is given by horizontal vectors, y(:,1) is
! the first vector.
! April 22 2005
! Allowing to also use real(4) and real(8) units.
!
!
! Interface
! dvv vector vs vector
! dvm vector vs matrix
! dmm matrix vs matrix
! rvv real(4) vector vs vector
! rvm real(4) vector vs matrix
! rmm real(4) matrix vs matrix
!
interface gnuplot
module procedure gnuplot_dvv, gnuplot_dvm, gnuplot_dmm, &
gnuplot_rvv, gnuplot_rvm, gnuplot_rmm, &
gnuplot_rv, gnuplot_rm
end interface gnuplot
! The subroutines
contains
!The NEW GNUPlot subroutines
include 'gnuplot.f90'
end module plot
!======================================================================
Gnuplot.f90 has 3700 lines of code, so I won't post it in here. I'm new to Fortran, so I apologize in advance if I'm simply doing something stupid. But, I've been combing the internet for days looking for a solution, but I didn't turn up much. I found this (Fortran compilation error - undefined reference), but multitaper.f95 has a use statement for both spectra and plot, and gplot and the subroutines of gnuplot.f90 are not private. According to https://gcc.gnu.org/wiki/GFortranGettingStarted and http://www.oceanographers.net/forums/showthread.php?378-How-to-make-a-FORTRAN-library my terminal incantation should work, to the best of my knowledge. Just to be sure, I went ahead and tried compiling gplot.f90 and mtspec.f95 separately and giving the object files to the gfortran command instead, but this changed nothing. I also tried changing the file extension of gplot.f90 and gnuplot.f90 to f95, since there shouldn't be anything in there that would result in a version conflict, but once again this produced the same error message. I also tried including the full path to the directory after the -L command, just in case, and got the same error message.
The Makefile doesn't work any better, and I admittedly don't know much about them and how they work. When I run the make command, I get the following error as when I try to compile it manually. The contents of the Makefile are as follows:
# Location of files
DIR = /N/u/rccaton/Quarry/EarthHum/mtspec/program
LIB = /N/u/rccaton/Quarry/EarthHum/mtspec/program
#LIB2 =
# Objects and Libraries
OBJS = $(LIB)/mwlib.a \
$(LIB)/gplot.a \
# /Applications/Absoft/lib/libU77.a
# Module locations
MODS = $(LIB)
MODS2 = $(LIB)
# Compiler
#FC = g95
#FC = f95
FC = gfortran
# Compiler flags
# none
FLAGS =
# debug
#FFLAGS = -g
# Module flag
# Sun Compiler
#MFLAG = -M
# Nag Compiler
#MFLAG = -i
MFLAG = -I
# Absoft Compiler
#MFLAG = -p
# g95 compiler
#MFLAG = -I
MODULE = $(MFLAG)$(MODS) # $(MFLAG)$(MODS2)
# Compile
all : multitaper
%: %.f95 $(OBJS)
$(FC) $(FFLAGS) $(MODULE) $< $(OBJS) -o $#
# Clean
clean :
rm multitaper
At the end of the day it makes to difference to me whether I end up compiling it with the makefile or with manual commands, I just need it to run. Sorry if this was verbose, I just want to provide as much relevant info as possible. Thank you for any assistance you can give.
I would suggest to find which symbol is exported from gplot.a
nm gplot.a | grep -i "gplot"
and change compilers flags accordingly, search for gfortran flags:
-fno-underscoring
-fsecond-underscore
-fcase-*
In case it does not help, ask the author to give you correct MakeFile.

Bizarre behavior observed in make 3.81 when attempting to use a reassigned variable as a dependancy

I'm running into a bizarre problem with a C++ makefile. I rewrote it this afternoon because the old one I was using made me want to kill myself, and I'm running into a weird problem regarding variable reassignment and dynamic dependancies.
for the purposes of this question, assume the following values:
OBJMOD_MODULENAME = obj/lib/foo.o obj/lib/bar.o obj/lib/quux.o
LIBDIR = lib/
CXX = g++
CXXLIBLINK = -shared
LIB = -lm -lpt
I have a target of the following format:
modulename: OBJ_CURRENTMOD = $(OBJMOD_MODULENAME)
modulename: $(OBJMOD_MODULENAME) lib/metrics.so
and later on, another of the following format:
$(LIBDIR)%.so: $(OBJ_CURRENTMOD)
$(CXX) $(CXXLIBLINK) -o $# $(OBJ_CURRENTMOD) $(LIB)
Lines in code blocks always appear in the order in which they are presented in the code blocks, however over the course of debugging I have changed the position of the blocks relative to each other.
The problem occurs after I change a source file and try to recompile with 'make modulename'. Building the object files works as expected, but rebuilding the library does not happen if the file already exists, i.e. the dependencies specified by $(OBJ_CURRENTMOD) are ignored. Using $(OBJMOD_MODULENAME) in the library dependencies works as expected. I have verified in a number of ways that the value of $(OBJ_CURRENTMOD) is as expected (stick echo
$(OBJ_CURRENTMOD) on the first line of the library target for example), but no matter what I try, the variable does not seem to update in time to trigger a recompile due to dependancy checking.
As I was typing this, I found a workaround:
OBJMOD_MODULENAME = obj/lib/foo.o obj/lib/bar.o obj/lib/quux.o
LIBDIR = lib/
CXX = g++
CXXLIBLINK = -shared
LIB = -lm -lpt
modulename: OBJ_CURRENTMOD = $(OBJMOD_MODULENAME)
modulename: $(OBJMOD_MODULENAME) lib/metrics.so
$(LIBDIR)%.so: herp $(OBJ_CURRENTMOD)
$(CXX) $(CXXLIBLINK) -o $# $(OBJ_CURRENTMOD) $(LIB)
herp: $(OBJ_CURRENTMOD)
This dummy target tacked in before the variable reference seems to force it to update and solves my problem. Is this a bug in make or something? make --version indicates GNU make 3.81. Can anyone else confirm this weird behavior? Am I just doing something horribly stupid? I've been staring at it for hours, I wouldn't be that surprised.
edit: turns out that wasn't really fixing it, it was just trapping it into running every time regardless of whether or not it needed it.
To verify the altered value:
$(LIBDIR)%.so: $(OBJ_CURRENTMOD)
echo $(OBJ_CURRENTMOD)
$(CXX) $(CXXLIBLINK) -o $# $(OBJ_CURRENTMOD) $(LIB)
As I said in one of my comments, I don't see how the $(LIBDIR)%.so pattern will match any pre-requisite of modulename but assuming metrics.so is meant to be lib/metrics.so then it will be used to build lib/metrics.so.
As interjay pointed out, "As with automatic variables, these values are only available within the context of a target's recipe (and in other target-specific assignments)." They can't be used in a target pattern or in a list of pre-requisites, which explains why the target doesn't get rebuilt when one of the pre-requisites in $(OBJ_MODULENAME) changes.
To make $(OBJ_CURRENTMOD) valid in the prerequisites list you'll need to use secondary expansion
.SECONDEXPANSION:
$(LIBDIR)%.so: $$(OBJ_CURRENTMOD)
$(CXX) $(CXXLIBLINK) -o $# $(OBJ_CURRENTMOD) $(LIB)
You are using the target-specific variable value OBJ_CURRENTMOD in the prerequisite list of the $(LIBDIR)%.so rule. This is not allowed: Target-specific variables may only be used in the recipe of a target.
Another issue is that you are defining the variable OBJ_MODULENAME and later accessing a different variable $(OBJMOD_MODULENAME), which hasn't been assigned to.