Linking problem in compiling my binding of Allegro 5

Hello. I am working on a binding of Allegro 5, a C library similar to SDL, aimed at video games.

It compiles well with my default switch which uses OCaml 5.2, but I can’t have it to compile with OCaml 4 compilers. I discovered this problem because I want to publish my package in opam-repository. You can see here the compilation errors. There is also a failure for OCaml 5.3 on Fedora. I require ocaml>=4.12 because previous compilers apparently don’t have Val_none. The repository of my binding is here.

I am able to reproduce the problem locally with an opam switch with OCaml 4.14.2. The error message is:

File "lib/dune", lines 19-26, characters 0-277:
19 | (library
20 |  (name al5)
21 |  (public_name allegro5)
22 |  (foreign_stubs
23 |   (language c)
24 |   (names al5 display events graphics keyboard mouse system time timer font image primitives)
25 |   (flags -std=c11 -Wall -O2 -I -fPIC (:include c_flags.sexp)))
26 |  (c_library_flags (:include lib_flags.sexp)))
/usr/bin/ld: lib/display.o: warning: relocation against `Caml_state' in read-only section `.text'
/usr/bin/ld: lib/timer.o: relocation R_X86_64_PC32 against undefined symbol `Caml_state' can not be used when making a shared object; recompile with -fPIC
/usr/bin/ld: final link failed: bad value
collect2: error: ld returned 1 exit status

This is not particularly related to the display and timer modules, because if I remove them, I get the same error messages on the remaining modules.

What should I do?

1 Like

I don’t have anything concrete to suggest, but did you try adding -fPIC to the linking flags, as the error message suggests?

Cheers,
Nicolas

Is this as simple as adding -fPIC in the c_library_flags field before the :include?

#if OCAML_VERSION < 41200

#define Val_none Val_int(0)
#define Some_val(v) Field(v,0)

static value
Val_some(value v)
{
    CAMLparam1(v);
    CAMLlocal1(some);
    some = caml_alloc(1, 0);
    Store_field(some, 0, v);
    CAMLreturn(some);
}

#else

#define Val_some caml_alloc_some

#endif

For my xss binding, yes -fPIC did the trick.


For ocaml4, there is a binding for an old version of Allegro.
Maybe doing your binding for ocaml5 is enough.

Yes, I think I already tried it. I’ve just tried again and it changes nothing.

I think the error message talks about compiling the .o, not the C library, actually. But they are already compiled with -fPIC (I require it to use stderr in my source files).

1 Like

Yes, I could restrict this package to OCaml 5, but unfortunately there’s also a problem with OCaml 5.3 with Fedora. The message is not exactly the same; it is:

File "lib/dune", lines 19-26, characters 0-277:
19 | (library
20 |  (name al5)
21 |  (public_name allegro5)
22 |  (foreign_stubs
23 |   (language c)
24 |   (names al5 display events graphics keyboard mouse system time timer font image primitives)
25 |   (flags -std=c11 -Wall -O2 -I -fPIC (:include c_flags.sexp)))
26 |  (c_library_flags (:include lib_flags.sexp)))
(cd _build/default && /home/opam/.opam/5.3/bin/ocamlmklib -g -o lib/al5_stubs lib/timer.o lib/time.o lib/system.o lib/primitives.o lib/mouse.o lib/keyboard.o lib/image.o lib/graphics.o lib/font.o lib/events.o lib/display.o lib/al5.o -ldopt -lallegro_image -ldopt -lallegro_primitives -ldopt -lallegro_ttf -ldopt -lallegro_font -ldopt -lallegro)
/usr/bin/ld: lib/timer.o: relocation R_X86_64_32 against `.rodata.str1.1' can not be used when making a shared object; recompile with -fPIC
/usr/bin/ld: failed to set dynamic section sizes: bad value
collect2: error: ld returned 1 exit status

Does it help if you include :standard at the beginning of the flags field of the foreign_stubs stanza? (As it happens I don’t normally bother with specifying -fPIC in the flags field because I have assumed that the standard flags will deal with things like that.) The documentation says “[The flags] field is specified using the Ordered Set Language, where the :standard value comes from the environment settings c_flags and cxx_flags, respectively.”

Does that make any difference? It is rather perplexing, but it looks as if there is some incompatible use of flags here, either in your code or in the libraries you are linking to.

Edlt: Also try adding or removinig “use_standard_c_and_cxx_flags” in your dune-project file. That might change the way ocamlc_cflags and ocamlc_cppflags are added to the compiler command line. (I don’t understand the documentation on this option.)

Oh, shame on me, I’ve just figured out what the problem was: I left -I just before -fPIC (I used the -I flag before I decided to use pkg-config), so -fPIC was taken as an include directory instead of a flag.

Everything is fine once I’ve removed -I. Build works. Using :standard instead of -fPIC works too, so I’m using :standard at the end.

Thank you for your help.

I also encountered a problem when installing packages on my opam switch, I will open another post for it.

1 Like

How can I see which flags are hidden behind this :standard please?

The documentation I have traced is in two places, first for the flags field of the foreign stubs dune stanza and secondly for dune-project’s use standard c and cxx flags option, which are difficult to reconcile.

The first says

… the :standard value comes from the environment settings c_flags and cxx_flags, respectively. Note that, for C stubs, Dune unconditionally adds the flags present in the OCaml config fields ocamlc_cflags and ocamlc_cppflags to the compiler command line. This behavior can be disabled since Dune 2.8 via the dune-project option use_standard_c_and_cxx_flags

The second says about that option

Control how flags coming from ocamlc -config are passed to the C compiler command line.

Historically, they have been systematically prepended without a way to override them.

If the following is passed, the mechanism is slightly altered:

(use_standard_c_and_cxx_flags)

In this mode, Dune will populate the :standard set of C flags with the content of ocamlc_cflags and ocamlc_cppflags. These flags can be completed or overridden using the Ordered Set Language.

This is the default in the language version 3.0.

You can examine ocamlc_cflags and ocamlc_cppflags by greping the output of ocamlc -config. One possible reading of the somewhat obscure and contradictory wording above is that prior to dune 2.8, ocamlc_cflags and ocamlc_cppflags were always passed to the C compiler, from 2.8 to 3.0 dune optionally does this, and from 3.0 dune adds them to :standard flags instead.

2 Likes

I compiled and tested your bindings with ocaml 4.11.1 / allegro 5.2.8 under debian 12.

Debian 12 provides:

liballegro-acodec5-dev
liballegro-acodec5.2
liballegro-audio5-dev
liballegro-audio5.2
liballegro-dialog5-dev
liballegro-dialog5.2
liballegro-image5-dev
liballegro-image5.2
liballegro-physfs5-dev
liballegro-physfs5.2
liballegro-ttf5-dev
liballegro-ttf5.2
liballegro-video5-dev
liballegro-video5.2
liballegro5-dev
liballegro5.2

but I only had to install liballegro5-dev and liballegro5.2

Debian 12 also provides dune but its version is not compatible with your bindings, so I had to write my own Makefile in order to compile your project.

$ dune build
File "dune-project", line 1, characters 11-15:
1 | (lang dune 3.11)
               ^^^^
Error: Version 3.11 of the dune language is not supported.
Supported versions of this extension in version 3.11 of the dune language:
- 1.0 to 1.12
- 2.0 to 2.9

here is my patch for ocaml 4.11.1:

diff --git a/lib/events.c b/lib/events.c
index 020525a..64b92f2 100644
--- a/lib/events.c
+++ b/lib/events.c
@@ -1,5 +1,17 @@
 #include "keyboard.h"
 
+#if OCAML_VERSION < 41200
+#define Val_none Val_int(0)
+static value
+caml_alloc_some(value v)
+{
+    CAMLparam1(v);
+    CAMLlocal1(some);
+    some = caml_alloc(1, 0);
+    Store_field(some, 0, v);
+    CAMLreturn(some);
+}
+#endif
 
 CAMLprim value ml_al_create_event_queue(value unit)
 {
diff --git a/lib/font.c b/lib/font.c
index cd6cce9..cffd192 100644
--- a/lib/font.c
+++ b/lib/font.c
@@ -3,6 +3,11 @@
 #include <allegro5/allegro_font.h>
 #include <allegro5/allegro_ttf.h>
 
+#if OCAML_VERSION < 41200
+#define Some_val(v) Field(v,0)
+#define Val_none Val_int(0)
+#define Is_none(v) (v == Val_none)
+#endif
 
 CAMLprim value ml_al_init_font_addon(value unit)
 {
diff --git a/lib/graphics.c b/lib/graphics.c
index 12beffa..deb5a9f 100644
--- a/lib/graphics.c
+++ b/lib/graphics.c
@@ -1,5 +1,10 @@
 #include "graphics.h"
 
+#if OCAML_VERSION < 41200
+#define Val_none Val_int(0)
+#define Is_none(v) (v == Val_none)
+#define Some_val(v) Field(v,0)
+#endif
 
 static struct custom_operations allegro_color_ops = {
   "org.allegro5.color",
diff --git a/test/test_ocaml_allegro.ml b/test/test_ocaml_allegro.ml
index 04ff709..c2421a3 100644
--- a/test/test_ocaml_allegro.ml
+++ b/test/test_ocaml_allegro.ml
@@ -205,8 +205,8 @@ let () =
     | Failure _ -> ()
   in
 
-  let rsrc_folder = List.hd Sites.Sites.ocaml_allegro5 in
-  let alleg_img = Al5.load_bitmap (Filename.concat rsrc_folder "allegator.png") in
+  let dirname = Filename.dirname Sys.argv.(0) in
+  let alleg_img = Al5.load_bitmap (Filename.concat dirname "allegator.png") in
   MouseLine.alleg_img := Some alleg_img;
   let font = Al5.create_builtin_font () in
 

here is my Makefile:

LINK = -lallegro -lallegro_primitives -lallegro_font -lallegro_ttf -lallegro_image

al5.cma: al5.cmo dllalleg_stubs.so
	ocamlc -a -o $@ $< -dllib -lalleg_stubs -cclib '$(LINK)'

dllalleg_stubs.so: al5.o display.o events.o font.o graphics.o image.o keyboard.o mouse.o primitives.o system.o time.o timer.o 
	ocamlmklib -o alleg_stubs $^ $(LINK)

al5.cmo: al5.ml al5.cmi
	ocamlc -c $<

al5.cmi: al5.mli
	ocamlc -c $<

%.o: %.c
	ocamlopt -c $<

clean:
	$(RM) *.so *.cmxa *.cm[ioxa] *.o *.a

Then the test file works fine, but I suspect that the joystick problem that I have with SDL2 in crostini is also there with alleg5.

1 Like

Thank you @deca3, but I prefer using a recent Dune version rather than a Makefile, and I don’t care about supporting ocaml<4.12.

Allegro Image and TTF are used by my library so they should be required?!

Finally, I simply moved my test to another repository so that it is independent from the library. This should work fine.

1 Like

Would you mind if i do ?
And if i do, would you prefer that i do it with a tutorial, or with a fork ?

It was only to let you know what people with a stable Debian have.
Stable debians are well known for not providing the latest versions.

if I remember correctly yes, I added them one by one, so I finally got the same than your pkgconfig command.

There is a typo in the link:
ocaml/allegro-5-test in stead of: ocaml-allegro-5-test


Your bindings look like a very good one, thanks a lot for this gift !
I hope everyone will be happy with it !

2 Likes

I do mind if you do. A tutorial is fine. I’m going to add support for OCaml 4.11 then.

But even on stable Debian, you have opam with the last package versions, don’t you?

That said, when my package will be mature, stable Debian will probably have a sufficiently recent Dune version…

Indeed, thank you.

Yes, I hope this will help someone, even though there is already a similar package for SDL2.

1 Like

I was not thinking an “aggressive fork” with another home page and everything. I was just thinking clicking the fork button on github, and commiting these stuff to keep it somewhere in case my computer crashes. (thank you that you included it for me)

By a tutorial, I was thinking a “copy-paste” from the previous message.
I see from the git log that you already included the code.

You don’t have to, it you want to keep your code clean from details.

Yes, that’s true, but I was just lacking space.

It seems that it provides 2, but I haven’t tried both yet.
And I have only read some tutorials about dune, I have to read the official manual now.

There are 2, and one of these 2 is even one of professional quality.
And now sdl3 is already arrived.

Yes there are other similar packages, there are also other similar programming languages. People here are talking about haskell, and rust. I discovered Nim yesterday. It also features a statically type system, and a c/js export.