3

I am trying to write a wrapper code for a simple "Hello World" program on Fortran, but I keep getting linking conflicts whenever I call a subroutine from another file.

These are the codes I used:

file name: helloworld.f90

module hellomod
public
contains
    subroutine printing
        print *, 'Hello World!'
        return
    end subroutine
end module hellomod

program main
    use hellomod, only: printing
    call printing
end program main

file name: hellowrap.f90

subroutine wrapping
    use hellomod, only: printing
    call printing
end subroutine wrapping

program hellowrap
    implicit none
    call wrapping
end program hellowrap

The error I got:

/usr/bin/ld: /tmp/cc5p8TIE.o: in function `wrapping_':
hellowrap.f90:(.text+0x5): undefined reference to `__hellomod_MOD_printing'
collect2: error: ld returned 1 exit status

I am a newbie in Fortran programming, so it would be great if I could know what exactly I might be doing wrong.

P.S: I did go through the other questions which were similar to mine. I tried those fixes, but they simply didn't work. One of the questions I explored was about linking a module, but when I tried to compile them together, I got an error as there are essentially two 'main' functions.

I want to compile these files separately, so this does not work. I do not exactly know if this is exclusively a linking issue.

Senku02
  • 83
  • 6
  • 4
    Hi ad welcome to Stackoverflow. Can you inform us how you compiled the code, and more importantly, how do you deal with two program-blocks in your code base. How does the compiler know which program to use? – kvantour Dec 11 '22 at 11:20
  • It must be about "how did you compile/How does the compiler know which program to use", because you code runs when everything is put in 1 file (and I do not even have experience in Fortran), see: https://www.onlinegdb.com/AWAaKOAY4 – Luuk Dec 11 '22 at 13:30
  • These are two separate programs you want to have, or one program with two modules? – John Alexiou Dec 11 '22 at 16:55
  • This smells like the problem questioned here ([gfortran multiple definition of main](https://stackoverflow.com/a/33602325/724039)), but that question is about a call to Fortran from C++. Loosing the `main` in the file `helloworld.f90` seems needed? (But I am nowhere a Fortran, nor a C++ programmer...) – Luuk Dec 11 '22 at 17:32
  • I am really sorry for the late response, but here goes: 1. I want to have two files, one which prints "Hello World", which is the first code I have written using a module. The second file has the wrapper code, which calls the module `hellomod` and utilises the `printing` function. 2. If I compile both of them together, then losing one of the main programs (the one in `helloworld.f90`) helps. – Senku02 Dec 13 '22 at 14:15

4 Answers4

2

Yes, to compile properly, at least on a command line compiler, you include both files. But then, since your helloworld.f90 file has a main program as well, the compiler sees two main programs, one in helloworld.f90 and hellowrap.f90. This is not allowed in Fortran.

You say you want them compiled separately anyway. Start by deleting the main program in helloworld.f90 so it only contains the subroutine. Compile it with the -c option since it does not have a main program in it: gfortran -c helloworld.f90 This makes the .mod file

Now you can compile the wrapping program as long as you include both file names: gfortran hellowrap.f90 helloworld.f90

Dan Sp.
  • 1,419
  • 1
  • 14
  • 21
  • Thanks! This helped. A quick question, if I may; while compiling `helloworld.f90` as you showed, the compiled file had a .o extension. However, when I compiled the wrapping program, I did not get any such extensions. Is there any reason for this? Will it affect further codes somehow? – Senku02 Dec 14 '22 at 04:55
  • @Senku02 .o file is an object file. Please check my answer regarding the compilation and linking steps. When you do `gfortran a.f90 b.f90` the object files are created and stored in temporary directory. It does not affect the program as the process is same but the object files are just hidden from you. – Shriraj Hegde Dec 19 '22 at 13:00
1

In order to call a fortran subroutine/function in another file, you simply need to compile the files together:

file1.f90:

program main
   call callme()
end program main

file2.f90:

subroutine callme
   print *, "hello world"
end subroutine callme

compile:

gfortran file1.f90 file2.f90

The above is the simplest and longest standing way to call an external function in fortran, however, there is an objectivly better way to access a subroutine/function, namely with modules:

file1.f90

use file2
program main
   call callme()
end program main

file2.f90

module file2
contains
   subroutine callme
      print *, "hello world"
   end subroutine callme
end module file2

compile:

gfortran file1.f90

this is the new standard as it limits the function's visibility to whoever uses the module, thus removing the function from the global scope

Shriraj Hegde
  • 829
  • 5
  • 18
Marty Farce
  • 127
  • 7
1

Compiled languages generate executables in two steps, 1.Compilation and 2.linking

Compilation step just compiles each block of the program to object files as long as they are syntactically correct. The final executable has to be linked to libraries like standard and external libraries, In this step, the program is put together as a whole. When you directly compile a program, both steps are done one after the other.

What you are getting is a linker error. The linker is not able to find the object file (compiled machine code but not linked) containing the function wrapping. (Some fortran compilers add an underscore in the binary name for historical reasons to hint it is a fortran function. This can be ignored if you stay within fortran)

You can approach this in two ways. You can compile both .f90 files separately and then link the resulting binaries or compile them in a single step so both steps are handled as once. The second way is just doing gfortran helloworld.f90 hellowrap.f90 -o programname

Now the regarding the problem of two main functions, all programs should have an "entrypoint" which is the start of execution. This is the function automatically called on start of a program. There can only be one of this. For binaries on linux this is _start but usually compilers implement a wrapper function like main in C/C++ and the program in fortran. The program can have only one entry point. In terms of binary, the program block is just another function like main and it should be unique.

TLDR: You can include all the files in the gfortran command but you can have only one program block in all the files you provide. You should remove program wrapping or program main as there can be only one entrypoint for the program.

Shriraj Hegde
  • 829
  • 5
  • 18
-1

I did a test using Intel's oneAPI Fortran Compiler with Visual Studio

Created a static library project with the module code

module hello_mod
use, intrinsic :: iso_fortran_env
implicit none
public

contains

subroutine printing
    print *, 'Hello World!'
    return
end subroutine

end module hello_mod

fig1

and then a separate .exe project with the program code

program HelloConsole
use hello_mod
implicit none

! Variables

! Body of HelloConsole
call printing

end program HelloConsole

but I added the first project as a dependency

scr2

and by default, Intel Fortran links the dependencies and so it works without any other changes.

To confirm here is the option to do that

scr3

with the successful program output of

Hello World!
John Alexiou
  • 28,472
  • 11
  • 77
  • 133
  • Thank you for the input! I see that you have linked the dependency via Intel's oneAPI, which I was not aware of. Is there an alternative approach to linking the dependencies just via code, instead of using this? – Senku02 Dec 13 '22 at 14:26
  • @Senku02 - it would not be programmatically, as the linker needs to be given access to the library. Check documentation for different compilers. This is the same mechanism used to link BLAS and LAPACK via the MKL libraries. You could use an `include` statement to just pull in the source code from a different project. – John Alexiou Dec 13 '22 at 14:32
  • 1
    This is unnecessarily complicated and offtopic for the question regarding linking and program entrypoint. Visual Code abstracts a lot of things which is necessary knowledge to learn when beginning to program using compiled languages. – Shriraj Hegde Dec 19 '22 at 12:54