# How to compile and link a Fortran library component with dune

**URL:** https://discuss.ocaml.org/t/how-to-compile-and-link-a-fortran-library-component-with-dune/6860
**Category:** Ecosystem
**Tags:** ctypes, dune, fortran
**Created:** [November 27, 2020, 12:58pm UTC](https://discuss.ocaml.org/t/how-to-compile-and-link-a-fortran-library-component-with-dune/6860 "2020-11-27T12:58:10Z")
**Posts on this page:** 13
**Page:** 1

<div class="post-metadata">

### Author: ![n4323](https://avatars.discourse-cdn.com/v4/letter/n/97f17d/32.png) [@n4323](https://discuss.ocaml.org/u/n4323)
#### Post date: [November 27, 2020, 12:58pm UTC](https://discuss.ocaml.org/t/how-to-compile-and-link-a-fortran-library-component-with-dune/6860/1 "2020-11-27T12:58:10Z")

</div>

I’m wondering if I am compiling and linking a simple one-file Fortran sublibrary correctly into my OCaml library. Ideally I would like to be able to make my library installable on linux and macos with a simple opam install.

After some fiddling and googling I have arrived at the project structure shown below, which seems to work for loading the Fortran library via ctypes like so:

integrate\_mvnd.ml

```ocaml
open Containers
open Ctypes
[...]
let mvnd =
  match
    Sys.getenv "CAML_LD_LIBRARY_PATH"
    |> String.split_on_char ':'
    |> List.cons (Sys.getcwd ())
    |> List.find_map (fun p ->
        try
          let filename = p ^ "/dllmvnd.so" in
          Some (Dl.dlopen ~flags:Dl.[RTLD_NOW;] ~filename)
        with Dl.DL_error _ -> None)
  with
  | Some l -> l
  | None -> failwith "unable to load mvnd shared library"

let ocaml_mvnd arg1 arg2 [...] = 
   Foreign.foreign ~from:mvnd "mvnormaldist" (ptr.integer @-> [...])

```

Some points I’m not content with:

- I have to manually check for the `CAML_LD_LIBRARY_PATH`
- I have to build a static library although afaict this will never work the way the library is loaded, because otherwise dune complains
- I am using hardcoded dll filenames, but when trying `@{dll_ext}` in dune that also gave `.so` on macos not `.dylib` as i had expected.

Am I using dune correctly? Is there a way to switch to either fully static linking of the fortran sublibrary or only dynamic linking?

Relevant project structure bits follow:

dune:

```auto
(data_only_dirs fortran-lib)

(library
	(name vbar)
	(public_name vbar)
	(modules vbar)
	(libraries [...]))

(rule
	(deps (source_tree fortran-lib))
	(targets dllmvnd.so libmvnd.a)
	(action
		(no-infer
			(progn
				(chdir fortran-lib (run make))
				(copy fortran-lib/dllmvnd.so dllmvnd.so)
				(copy fortran-lib/libmvnd.a libmvnd.a)))))

(library
	(name integrate_mvnd)
	(public_name vbar.integrate_mvnd)
	(modules integrate_mvnd)
	(libraries [...] ctypes ctypes.foreign)
	(foreign_archives mvnd))

[...]

```

fortran-lib/Makefile:

```auto
.PHONY: clean all

all: mvnd.dylib libmvnd.a

mvnd.dylib: 
	gfortran -shared -O3 -fPIC -fcheck=bounds -std=f2008 -w -o dllmvnd.so mvndstpack_mod.f

libmvnd.a: 
	gfortran -c -O3 -fPIC -fcheck=bounds -std=f2008 -w mvndstpack_mod.f
	ar -rcs libmvnd.a mvndstpack_mod.o 

clean:
	rm -f *.o *.mod *.a *.dylib

```

---

<div class="post-metadata">

### Author: ![nojb](https://sea2.discourse-cdn.com/flex020/user_avatar/discuss.ocaml.org/nojb/32/519_2.png) [@nojb](https://discuss.ocaml.org/u/nojb)
#### Post date: [November 27, 2020, 1:15pm UTC](https://discuss.ocaml.org/t/how-to-compile-and-link-a-fortran-library-component-with-dune/6860/2 "2020-11-27T13:15:27Z")

</div>

I may be missing something, but I don’t see why you need to load your library dynamically. If you use `foreign_archives`, the Fortran library should have been linked statically into your final executable (at least on native code).

Cheers,  
Nicolas

---

<div class="post-metadata">

### Author: ![n4323](https://avatars.discourse-cdn.com/v4/letter/n/97f17d/32.png) [@n4323](https://discuss.ocaml.org/u/n4323)
#### Post date: [November 27, 2020, 1:34pm UTC](https://discuss.ocaml.org/t/how-to-compile-and-link-a-fortran-library-component-with-dune/6860/3 "2020-11-27T13:34:26Z")

</div>

Ok, so how would this look? no ctypes and

```ocaml
external ocaml_mvnd : (int -> float -> [...] -> ()) = "mvnormaldist"

```

?  
The Fortran function takes integer pointer and double pointer arguments and writes the result into one of the double pointers. I was under the impression that I would have to write a C stubs file if I want external functions but maybe that’s wrong?  
Or can I use ctypes to build these pointers to pass to the external function and read them out later?  
(clearly I don’t grok how external functions and/or ctypes work)

---

<div class="post-metadata">

### Author: ![nojb](https://sea2.discourse-cdn.com/flex020/user_avatar/discuss.ocaml.org/nojb/32/519_2.png) [@nojb](https://discuss.ocaml.org/u/nojb)
#### Post date: [November 27, 2020, 1:46pm UTC](https://discuss.ocaml.org/t/how-to-compile-and-link-a-fortran-library-component-with-dune/6860/4 "2020-11-27T13:46:26Z")

</div>

> [@n4323](#):
>
> Ok, so how would this look? no ctypes and

You probably still want `ctypes` to bind the functions, but you shouldn’t need the `~from:...` argument.

Cheers,  
Nicolas

---

<div class="post-metadata">

### Author: ![n4323](https://avatars.discourse-cdn.com/v4/letter/n/97f17d/32.png) [@n4323](https://discuss.ocaml.org/u/n4323)
#### Post date: [November 27, 2020, 1:57pm UTC](https://discuss.ocaml.org/t/how-to-compile-and-link-a-fortran-library-component-with-dune/6860/5 "2020-11-27T13:57:16Z")

</div>

This is something I had tried. If I remove `let mvnd = [...]` and also remove the `~from` argument but leave everything else the same, I get a runtime error: `exception Dl.DL_error("dlsym(RTLD_DEFAULT, mvnormaldist): symbol not found")` . If i remove only `~from` but leave the dynamic loading in place, with unused resulting value `mvnd`, then the symbol is found – presumably due to the mere act of loading the library.

---

<div class="post-metadata">

### Author: ![nojb](https://sea2.discourse-cdn.com/flex020/user_avatar/discuss.ocaml.org/nojb/32/519_2.png) [@nojb](https://discuss.ocaml.org/u/nojb)
#### Post date: [November 27, 2020, 2:10pm UTC](https://discuss.ocaml.org/t/how-to-compile-and-link-a-fortran-library-component-with-dune/6860/6 "2020-11-27T14:10:03Z")

</div>

Are you using bytecode or native-code?

Cheers,  
Nicolas

---

<div class="post-metadata">

### Author: ![n4323](https://avatars.discourse-cdn.com/v4/letter/n/97f17d/32.png) [@n4323](https://discuss.ocaml.org/u/n4323)
#### Post date: [November 27, 2020, 2:16pm UTC](https://discuss.ocaml.org/t/how-to-compile-and-link-a-fortran-library-component-with-dune/6860/7 "2020-11-27T14:16:47Z")

</div>

I was trying to `dune exec` a native code executable (`.exe`) when this error was produced. i haven’t specified any special compilation/linking mode fields for the executables so i think native is chosen by default

---

<div class="post-metadata">

### Author: ![nojb](https://sea2.discourse-cdn.com/flex020/user_avatar/discuss.ocaml.org/nojb/32/519_2.png) [@nojb](https://discuss.ocaml.org/u/nojb)
#### Post date: [November 27, 2020, 2:25pm UTC](https://discuss.ocaml.org/t/how-to-compile-and-link-a-fortran-library-component-with-dune/6860/8 "2020-11-27T14:25:33Z")

</div>

It looks like the symbols from the Fortran lib are being dropped by the linker because they are not referenced from the main program (they are only being addressed dynamically using `dlsym`/`ctypes`). How to keep this from hapenning is rather OS-dependent (I don’t remember how to do it off the top of my head).

Cheers,  
Nicolas

---

<div class="post-metadata">

### Author: ![n4323](https://avatars.discourse-cdn.com/v4/letter/n/97f17d/32.png) [@n4323](https://discuss.ocaml.org/u/n4323)
#### Post date: [November 27, 2020, 2:47pm UTC](https://discuss.ocaml.org/t/how-to-compile-and-link-a-fortran-library-component-with-dune/6860/9 "2020-11-27T14:47:56Z")

</div>

Ah thanks that could be an explanation. this reminds me of [https://github.com/ocaml/ocaml/issues/10018](https://github.com/ocaml/ocaml/issues/10018) . Is it related?

---

<div class="post-metadata">

### Author: ![nojb](https://sea2.discourse-cdn.com/flex020/user_avatar/discuss.ocaml.org/nojb/32/519_2.png) [@nojb](https://discuss.ocaml.org/u/nojb)
#### Post date: [November 27, 2020, 2:49pm UTC](https://discuss.ocaml.org/t/how-to-compile-and-link-a-fortran-library-component-with-dune/6860/10 "2020-11-27T14:49:00Z")

</div>

> [@n4323](#):
>
> Is it related?

I think the root issue is the same in both cases.

Cheers,  
Nicolas

---

<div class="post-metadata">

### Author: ![n4323](https://avatars.discourse-cdn.com/v4/letter/n/97f17d/32.png) [@n4323](https://discuss.ocaml.org/u/n4323)
#### Post date: [November 27, 2020, 3:11pm UTC](https://discuss.ocaml.org/t/how-to-compile-and-link-a-fortran-library-component-with-dune/6860/11 "2020-11-27T15:11:08Z")

</div>

I’ve tried a little more. I remembered that `-all_load` can be used on macos to load unreferenced symbols, so changed:

```auto
(library
	(name integrate_mvnd)
	(public_name vbar.integrate_mvnd)
	(modules integrate_mvnd)
	(libraries util ctypes ctypes.foreign)
	(foreign_archives mvnd)
	(flags (:standard -cclib -all_load)))

```

and tried to compile. This time, the linker complains it cannot find `__gfortran_runtime_error_at` which seems to be a gfortran internal symbol. Also adding `-cclib -lgfortran` does not find a corresponding library. On my system I have `libgfortran.dylib` but as it appears no `libgfortran.a`. (I got this lead from [https://askubuntu.com/questions/581905/undefined-references-to-gfortran-runtime-error-at](https://askubuntu.com/questions/581905/undefined-references-to-gfortran-runtime-error-at))

Anyway, the static compilation option seems more painful than the dynamic library loading. I would be content with making the latter work without also producing a static library. Can I just leave out the `(foreign_archives)` field?

---

<div class="post-metadata">

### Author: ![nojb](https://sea2.discourse-cdn.com/flex020/user_avatar/discuss.ocaml.org/nojb/32/519_2.png) [@nojb](https://discuss.ocaml.org/u/nojb)
#### Post date: [November 27, 2020, 4:04pm UTC](https://discuss.ocaml.org/t/how-to-compile-and-link-a-fortran-library-component-with-dune/6860/12 "2020-11-27T16:04:53Z")

</div>

> [@n4323](#):
>
> Anyway, the static compilation option seems more painful than the dynamic library loading. I would be content with making the latter work without also producing a static library. Can I just leave out the `(foreign_archives)` field?

Yes, indeed.

Cheers,  
Nicolas

---

<div class="post-metadata">

### Author: ![n4323](https://avatars.discourse-cdn.com/v4/letter/n/97f17d/32.png) [@n4323](https://discuss.ocaml.org/u/n4323)
#### Post date: [December 1, 2020, 5:00pm UTC](https://discuss.ocaml.org/t/how-to-compile-and-link-a-fortran-library-component-with-dune/6860/13 "2020-12-01T17:00:10Z")

</div>

hi, this seemed to work at first but it turns out to be not quite right. i tried this:

```auto
(rule
	(deps (source_tree fortran-lib))
	(targets dllmvnd.so libmvnd.a)
	(action
		(no-infer
			(ignore-stdout 
				(progn
					(chdir fortran-lib (run make))
					(copy fortran-lib/libmvnd.a libmvnd.a)
					(copy fortran-lib/dllmvnd.so dllmvnd.so))))))

(library
	(name integrate_mvnd)
	(public_name vbar.integrate_mvnd)
	(modules integrate_mvnd)
	(foreign_archives mvnd)
	(libraries util ctypes ctypes.foreign))

```

which installs both the static and dynamic libraries. if i leave out `foreign_archives` neither get installed. if i keep `foreign_archives` but prevent building of the static library in the Makefile and in the `rule` listed first, then dune complains that the static lib is missing – apparently `foreign_archives` wants the static library.

i can’t seem to convince dune that only a dynamic library should be produced and then installed.
