Is there any way to avoid error `File "topfind.ml", line 55, characters 12-41: # Error: Unbound module Toploop`?

I am porting my OCaml docker containers from Ubuntu to Alpine. And met this error:

<><> Creating initial switch (ocaml-system>=4.02.3) <><><><><><><><><><><><><><>

<><> Gathering sources ><><><><><><><><><><><><><><><><><><><><><><><><><><><><>

<><> Processing actions <><><><><><><><><><><><><><><><><><><><><><><><><><><><>
-> installed base-bigarray.base
-> installed base-threads.base
-> installed base-unix.base
-> installed ocaml-system.4.07.1
-> installed ocaml-config.1
-> installed ocaml.4.07.1
Done.
# Run eval $(opam env) to update the current shell environment
The following actions will be performed:
  - install conf-m4   1     [required by ocamlfind]
  - install ocamlfind 1.8.0
===== 2 to install =====

<><> Gathering sources ><><><><><><><><><><><><><><><><><><><><><><><><><><><><>
[ocamlfind.1.8.0] downloaded from cache at https://opam.ocaml.org/cache

<><> Processing actions <><><><><><><><><><><><><><><><><><><><><><><><><><><><>
-> installed conf-m4.1
[ERROR] The compilation of ocamlfind failed at "/usr/bin/make all".

#=== ERROR while compiling ocamlfind.1.8.0 ====================================#
# context              2.0.1 | linux/x86_64 | ocaml-system.4.07.1 | https://opam.ocaml.org#fa55d97e
# path                 ~/.opam/default/.opam-switch/build/ocamlfind.1.8.0
# command              /usr/bin/make all
# exit-code            2
# env-file             ~/.opam/log/ocamlfind-66-2c871f.env
# output-file          ~/.opam/log/ocamlfind-66-2c871f.out
### output ###
# [...]
# File "frontend.ml", line 1823, characters 16-29:
# Warning 3: deprecated: Stdlib.String.create
# Use Bytes.create instead.
# ocamlc -I +compiler-libs  -o ocamlfind -g findlib.cma unix.cma \
#            ocaml_args.cmo frontend.cmo
# ocamlc -I +compiler-libs -opaque -c topfind.mli
# ocamlc -I +compiler-libs -opaque -g -c topfind.ml
# File "topfind.ml", line 55, characters 12-41:
# Error: Unbound module Toploop
# make[1]: *** [Makefile:165: topfind.cmo] Error 2
# make[1]: Leaving directory '/home/myuser/.opam/default/.opam-switch/build/ocamlfind.1.8.0/src/findlib'
# make: *** [Makefile:13: all] Error 2



<><> Error report <><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><>
+- The following actions failed
| - build ocamlfind 1.8.0
+- 
+- The following changes have been performed
| - install conf-m4 1
+- 

Here is the piece of Dockerfile I used:

FROM alpine:edge
RUN apk add --no-cache make m4 which patch git sudo wget unzip gcc pcre \
	pcre libffi ocaml ocaml-ocamldoc opam ocaml-findlib perl python3 \
	lz4 lzo p7zip zstd gmp

RUN chmod +s `which bwrap`

RUN addgroup -g 1000 -S myuser && adduser -u 1000 -D -S myuser -G myuser
RUN sed -i.bkp -e \
	's/%wheel\s\+ALL=(ALL\(:ALL\)\?)\s\+ALL/%wheel ALL=NOPASSWD:ALL/g' \
	/etc/sudoers
COPY _etc_hosts /etc/hosts
COPY ocamlinit /home/myuser/.ocamlinit
USER myuser
WORKDIR /home/myuser
ENV OCAML_TOPLEVEL_PATH /home/myuser/.opam/default/lib/toplevel
RUN opam init --auto-setup --disable-sandboxing --yes && \
	# FIXME: Ugly hack
	ln -s /home/myuser/.opam/default/lib/toplevel /home/myuser/.opam/default/lib/topfind && \
	eval `opam env` && opam install --yes ocamlfind && \
	opam update --yes && export CHECK_IF_PREINSTALLED=false; \
	opam install --yes opam-devel

RUN eval `opam env` && \
	opam switch create 4.07.1; \
	opam switch 4.07.1

RUN eval `opam env` && \
	opam install --yes core lwt lwt_log ocaml-migrate-parsetree sexplib dune \
	ppx_tools ppx_tools_versioned async_kernel async_unix checkseum

Seems there is an issue (#11453) for that, but is it possible to workaround it somehow?

As suggested in the issue, you should add ocaml-compiler-libs to the RUN apk add --no-cache ... line, or install ocamlfind after creating and moving to the new switch.

You could also use the alpine images from ocaml/opam2 if you want to avoid the intermediate step to install opam2: https://github.com/ocaml/infrastructure/wiki/Containers
Or you could use the binary installation:

sh <(curl -sL https://raw.githubusercontent.com/ocaml/opam/master/shell/install.sh)
1 Like

Thanks! That worked, now I met another issue:

# ocamlopt -I +compiler-libs -g -a -o findlib.cmxa findlib_config.cmx fl_split.cmx fl_metatoken.cmx fl_meta.cmx fl_metascanner.cmx fl_topo.cmx fl_package_base.cmx findlib.cmx fl_args.cmx fl_lint.cmx
# if [ 1 -gt 0 ]; then \
#     ocamlopt -I +compiler-libs -g -shared -o findlib.cmxs findlib_config.cmx fl_split.cmx fl_metatoken.cmx fl_meta.cmx fl_metascanner.cmx fl_topo.cmx fl_package_base.cmx findlib.cmx fl_args.cmx fl_lint.cmx; \
# fi
# /usr/lib/gcc/x86_64-alpine-linux-musl/8.3.0/../../../../x86_64-alpine-linux-musl/bin/ld: cannot find crti.o: No such file or directory
# /usr/lib/gcc/x86_64-alpine-linux-musl/8.3.0/../../../../x86_64-alpine-linux-musl/bin/ld: cannot find -lssp_nonshared
# collect2: error: ld returned 1 exit status
# File "caml_startup", line 1:
# Error: Error during linking
# make[1]: *** [Makefile:68: findlib.cmxa] Error 2

Found the reason, it is missing apk add --no-cache musl-dev in the Dockerfile.

1 Like

Just for the reference, here is the resulting Dockerfile (a piece of it) that actually work:

FROM alpine:edge
MAINTAINER Your Name <youremail@domain.com>
RUN apk add --no-cache make m4 which patch pkgconfig sudo wget git \
	gcc g++ musl-dev linux-headers libffi pcre perl python3 \
	gmp-dev pcre-dev libressl-dev zlib-dev \
	ocaml ocaml-compiler-libs ocaml-ocamldoc opam ocaml-findlib

RUN chmod +s `which bwrap`

RUN addgroup -g 1000 -S myuser && adduser -u 1000 -D -S myuser -G myuser
RUN sed -i.bkp -e \
	's/%myuser\s\+ALL=(ALL\(:ALL\)\?)\s\+ALL/%myuser ALL=NOPASSWD:ALL/g' \
	/etc/sudoers

COPY ocamlinit /home/myuser/.ocamlinit
USER myuser
WORKDIR /home/myuser
ENV OCAML_TOPLEVEL_PATH /home/myuser/.opam/default/lib/toplevel
RUN opam init --auto-setup --disable-sandboxing --yes && \
	# Note: this is temporary workaround due to some OCaml and OPAM problems
	ln -s /home/myuser/.opam/default/lib/toplevel /home/myuser/.opam/default/lib/topfind && \
	eval `opam env` && opam install --yes ocamlfind && \
	opam update --yes && export CHECK_IF_PREINSTALLED=false; \
	opam install --yes opam-devel

# No need, since Alpine has the latest 4.07.1 as system switch
# But you can uncomment and change into the values you need
#RUN eval `opam env` && \
#	opam switch create 4.07.1 && \
#	opam switch 4.07.1

RUN eval `opam env` && \
	opam install --yes core lwt lwt_log ocaml-migrate-parsetree sexplib dune \
	ppx_tools ppx_tools_versioned

Why do you need ln -s /home/myuser/.opam/default/lib/toplevel /home/myuser/.opam/default/lib/topfind ?

I don’t remember to be honest, it was added a long time ago, will retry without it.