Skip to content

Commit 633a40c

Browse files
committed
Port to dune
Motivation ========== With a port to dune, the ctypes library can be embedded in larger dune projects simply by including it in the directory tree of the bigger project. This in turn will allow MirageOS unikernels to use Ctypes seamlessly as part of embedded compilation, using the standard cross-compilation and library variants support built into Dune. The goal is to make Ctypes the default FFI interface to MirageOS, and have it work out of the box on all of the backends. All existing OS platforms should be supported as well before merge, including Windows. Porting Approach ================ This PR still installs ocamlfind libraries in the same way as the previous Makefile infrastructure, so backwards compat is preserved. However, the findlib schema has been slightly modified to separate out the foreign library from the core ctypes library. We now install: - ctypes that contains ctypes.top and ctypes.stubs as before. There are aliases to ctypes.foreign that redirect existing uses of those. - ctypes-foreign contains the foreign library. All of the configuration logic for libffi is now in src/ctypes-foreign-base, so deleting these directories will remove the ffi build logic without touching the core library. Since dune by default has stricter warnings enabled (the default --profile=dev mode), the PR currently sprinkles files with [@@@warning tags. The warnings can be fixed as well if desired, but that would muddy this PR so not done yet. Layout ====== The previous packaging would install ctypes and ctypes.stubs in the same library. Depending on the build system, it means that it's possible to specify a dependency on ctypes and use ctypes.stubs succesfully. This is not the case with Dune since it installs these in different directories. This means that it is necessary to patch these reverse dependencies, though it can be argued that they were relying on a bug. Followup work ============= A test uses OCaml syntax to skip test on windows. Once it's acceptable to use Dune 3.9, it should be updated to use build_if. Test and dynamic libraries ========================== Due to a different linking model, there are some changes in the test suite regarding dynamic library loading. Dynamic symbols in ocaml <4.06 ------------------------------ The setup for tests is that each test executable tests some properties using both "stubs" and "foreign" strategies to refer to some symbols define in `tests/clib/` (the `test_functions` library). The symbols are accessed through both strategies: - as a linked symbol (through an `external` via stubs) - through the dynamic loader (through `dlopen` via foreign) Dune links the stubs statically, so by default the symbols would not be visible to the dynamic loader. OCaml sets `-Wl,-E` in `LDFLAGS` to make these symbols visible at runtime, but until ocaml/ocaml@edbba02 (between 4.05 and 4.06), this was ignored when linking executables. So this commit adds these flags when building tests for older versions. An alternative would be to use `(link_flag)` in an `(env)` stanza but this requires dune 3. This issue does not affect the original make-based build because it assembles the test executables a bit differently: the `test_functions` library is linked dynamically to the test executable. So the symbols are already visible to the dynamic loader, even when `-Wl,-E` is not set. clib loading ------------ This change is specific to windows (but does not change the behavior on Linux) It modifies the test setup a bit so that the contents of `clib` are dynamically loaded at the beginning of each test. This ensures that later foreign calls (that use the default handle) will succeed. It was not necessary befor because `clib` was linked dynamically. Now that it is linked statically, dynamic symbols do not have access to the symbols in the main executable. This is a problem on windows which does not have the concept of `-Wl,--export-dynamic`. Also, on windows the stubs DLL can not be loaded directly so we recompile the library using plain `%{cc}` instead of going through ocamlopt and flexlink. Instrumentation =============== This uses dune instrumentation for coverage The instructions are now: opam install bisect_ppx dune runtest --instrument-with bisect_ppx --force bisect-ppx-report html See <https://github.com/aantron/bisect_ppx#Dune> Depext handling =============== This port uses conf-libffi instead of hardcoding depexts in ctypes-foreign.
1 parent 46cc3ff commit 633a40c

File tree

230 files changed

+2431
-2851
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

230 files changed

+2431
-2851
lines changed

.depend

-175
This file was deleted.

.github/workflows/test.yml

+2-5
Original file line numberDiff line numberDiff line change
@@ -63,10 +63,7 @@ jobs:
6363
opam install -t --deps-only .
6464
6565
- name: Build
66-
run: opam exec -- make
66+
run: opam exec -- dune build
6767

6868
- name: Test
69-
run: opam exec -- make test
70-
71-
- name: Test inverted stubs
72-
run: opam pin add ctypes-inverted-stubs-example https://github.com/yallop/ocaml-ctypes-inverted-stubs-example.git
69+
run: opam exec -- dune runtest

.gitignore

+3-28
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,5 @@
1+
.*.swp
12
_build
23
_opam
3-
libffi.config
4-
asneeded.config
5-
discover
6-
gen_c_primitives
7-
gen_c_primitives.log
8-
gen_libffi_abi
9-
gen_libffi_abi.log
10-
src/ctypes/ctypes_primitives.ml
11-
src/ctypes_config.h
12-
src/ctypes_config.ml
13-
src/ctypes-foreign/dl_stubs.c
14-
src/ctypes-foreign/dl.ml
15-
src/discover/commands.cm*
16-
src/discover/discover.cm*
17-
src/configure/extract_from_c.cm*
18-
src/configure/gen_c_primitives.cm*
19-
src/configure/gen_libffi_abi.cm*
20-
*~
21-
generated_stubs.c
22-
generated_bindings.ml
23-
generated_struct_bindings.ml
24-
ncurses_generated.ml
25-
ncurses_stubs.c
26-
date_generated.ml
27-
date_stubs.c
28-
fts_generated.ml
29-
fts_stubs.c
30-
libffi_abi.ml
4+
*.install
5+
.merlin

.merlin

-19
This file was deleted.

META

-43
This file was deleted.

Makefile

+7-197
Original file line numberDiff line numberDiff line change
@@ -1,200 +1,10 @@
1-
.SECONDEXPANSION:
1+
.PHONY: build clean test
22

3-
BEST:=$(shell if ocamlopt > /dev/null 2>&1; then echo native; else echo byte; fi)
4-
OPAQUE:=$(shell if ocamlopt -opaque 2>/dev/null; then echo -opaque; fi)
5-
NO_KEEP_LOCS:=$(shell if ocamlopt -no-keep-locs 2>/dev/null; then echo -no-keep-locs; fi)
6-
DEBUG=true
7-
COVERAGE=false
8-
OCAML=ocaml
9-
OCAMLFIND=ocamlfind $(OCAMLFINDFLAGS)
10-
HOSTOCAMLFIND=$(OCAMLFIND)
11-
OCAMLDEP=$(OCAMLFIND) ocamldep
12-
OCAMLMKLIB=$(OCAMLFIND) ocamlmklib
13-
VPATH=src examples
14-
BUILDDIR=_build
15-
BASE_PROJECTS=configure libffi-abigen configured ctypes ctypes-top
16-
FOREIGN_PROJECTS=test-libffi ctypes-foreign
17-
STUB_PROJECTS=cstubs
18-
PROJECTS=$(BASE_PROJECTS) $(FOREIGN_PROJECTS) $(STUB_PROJECTS)
19-
DEP_DIRS=$(foreach project,$(PROJECTS),$($(project).dir))
20-
GENERATED=src/ctypes/ctypes_primitives.ml \
21-
src/ctypes-foreign/libffi_abi.ml \
22-
src/ctypes-foreign/dl.ml \
23-
src/ctypes-foreign/dl_stubs.c \
24-
libffi.config \
25-
asneeded.config \
26-
discover \
27-
gen_c_primitives \
28-
gen_c_primitives.log \
29-
gen_libffi_abi \
30-
src/configure/extract_from_c.cmi \
31-
src/configure/extract_from_c.cmo \
32-
src/configure/gen_c_primitives.cmi \
33-
src/configure/gen_c_primitives.cmo \
34-
src/configure/gen_libffi_abi.cmi \
35-
src/configure/gen_libffi_abi.cmo \
36-
src/discover/commands.cmi \
37-
src/discover/commands.cmo \
38-
src/discover/discover.cmi \
39-
src/discover/discover.cmo
3+
build:
4+
dune build
405

41-
OCAML_FFI_INCOPTS=$(libffi_opt)
42-
export CFLAGS DEBUG
6+
test:
7+
dune runtest
438

44-
EXTDLL:=$(shell $(OCAMLFIND) ocamlc -config | awk '/^ext_dll:/{print $$2}')
45-
OSYSTEM:=$(shell $(OCAMLFIND) ocamlc -config | awk '/^system:/{print $$2}')
46-
47-
ifneq (,$(filter mingw%,$(OSYSTEM)))
48-
OS_ALT_SUFFIX=.win
49-
else
50-
OS_ALT_SUFFIX=.unix
51-
endif
52-
53-
# public targets
54-
all: libffi.config $(PROJECTS)
55-
56-
ctypes-base: $(BASE_PROJECTS)
57-
ctypes-foreign: ctypes-base test-libffi
58-
ctypes-stubs: ctypes-base $(STUB_PROJECTS)
59-
60-
clean: clean-examples clean-tests
61-
rm -fr _build
62-
rm -f $(GENERATED)
63-
64-
# ctypes subproject
65-
ctypes.cmi_only = ctypes_static ctypes_primitive_types ctypes_structs cstubs_internals
66-
ctypes.public = lDouble complexL ctypes posixTypes ctypes_types
67-
ctypes.dir = src/ctypes
68-
ctypes.extra_mls = ctypes_primitives.ml
69-
ctypes.deps = bigarray-compat integers
70-
ctypes.linkdeps = integers_stubs
71-
ctypes.install = yes
72-
ctypes.install_native_objects = yes
73-
ifeq ($(XEN),enable)
74-
ctypes.xen = yes
75-
endif
76-
77-
ctypes: PROJECT=ctypes
78-
ctypes: $(ctypes.dir)/$(ctypes.extra_mls) $$(LIB_TARGETS)
79-
80-
# cstubs subproject
81-
cstubs.public = cstubs_structs cstubs cstubs_inverted
82-
cstubs.dir = src/cstubs
83-
cstubs.subproject_deps = ctypes
84-
cstubs.deps = str integers
85-
cstubs.install = yes
86-
cstubs.install_native_objects = yes
87-
cstubs.extra_hs = $(package_integers_path)/ocaml_integers.h
88-
89-
cstubs: PROJECT=cstubs
90-
cstubs: $(cstubs.dir)/$(cstubs.extra_mls) $$(LIB_TARGETS)
91-
92-
# ctypes-foreign subproject
93-
ctypes-foreign.public = dl libffi_abi foreign
94-
ctypes-foreign.dir = src/ctypes-foreign
95-
ctypes-foreign.subproject_deps = ctypes
96-
ctypes-foreign.deps = integers
97-
ctypes-foreign.install = yes
98-
ctypes-foreign.install_native_objects = yes
99-
ctypes-foreign.extra_cs = dl_stubs.c
100-
ctypes-foreign.extra_mls = libffi_abi.ml dl.ml
101-
ctypes-foreign.cmi_opts = $(OPAQUE) $(NO_KEEP_LOCS)
102-
ctypes-foreign.cmo_opts = $(OCAML_FFI_INCOPTS:%=-ccopt %)
103-
ctypes-foreign.cmx_opts = $(OCAML_FFI_INCOPTS:%=-ccopt %)
104-
ctypes-foreign.link_flags = $(libffi_lib) $(lib_process)
105-
ctypes-foreign.threads = yes
106-
107-
ctypes-foreign: PROJECT=ctypes-foreign
108-
ctypes-foreign: $$(LIB_TARGETS)
109-
110-
# ctypes-top subproject
111-
ctypes-top.public = ctypes_printers
112-
ctypes-top.dir = src/ctypes-top
113-
ctypes-top.install = yes
114-
ctypes-top.deps = compiler-libs integers
115-
ctypes-top.subproject_deps = ctypes
116-
ctypes-top.install_native_objects = yes
117-
118-
ctypes-top: PROJECT=ctypes-top
119-
ctypes-top: $$(LIB_TARGETS)
120-
121-
# configuration
122-
configured: src/ctypes/ctypes_primitives.ml src/ctypes-foreign/libffi_abi.ml src/ctypes-foreign/dl.ml src/ctypes-foreign/dl_stubs.c
123-
124-
src/ctypes-foreign/dl.ml: src/ctypes-foreign/dl.ml$(OS_ALT_SUFFIX)
125-
cp $< $@
126-
src/ctypes-foreign/dl_stubs.c: src/ctypes-foreign/dl_stubs.c$(OS_ALT_SUFFIX)
127-
cp $< $@
128-
129-
src/ctypes/ctypes_primitives.ml: src/configure/extract_from_c.ml src/configure/gen_c_primitives.ml
130-
$(HOSTOCAMLFIND) ocamlc -o gen_c_primitives -package str -strict-sequence -linkpkg $^ -I src/configure
131-
./gen_c_primitives > $@ 2> gen_c_primitives.log || (rm $@ && cat gen_c_primitives.log && false)
132-
133-
src/ctypes-foreign/libffi_abi.ml: src/configure/extract_from_c.ml src/configure/gen_libffi_abi.ml
134-
$(HOSTOCAMLFIND) ocamlc -o gen_libffi_abi -package str -strict-sequence -linkpkg $^ -I src/configure
135-
./gen_libffi_abi > $@ 2> gen_c_primitives.log || (rm $@ && cat gen_c_primitives.log && false)
136-
137-
libffi.config: src/discover/commands.mli src/discover/commands.ml src/discover/discover.ml
138-
$(HOSTOCAMLFIND) ocamlc -o discover -package str -strict-sequence -linkpkg $^ -I src/discover
139-
./discover -ocamlc "$(OCAMLFIND) ocamlc" > $@ || (rm $@ && false)
140-
141-
asneeded.config:
142-
./src/discover/determine_as_needed_flags.sh >> $@
143-
144-
# dependencies
145-
depend: configure
146-
$(OCAMLDEP) -one-line $(foreach dir,$(DEP_DIRS),-I $(dir)) \
147-
$(shell find src examples -name '*.mli' -o -name '*.ml') \
148-
| sed "s!src/!_build/src/!g; s!examples/!_build/examples/!g" | sort > .depend
149-
150-
#installation
151-
META-install:
152-
$(OCAMLFIND) install ctypes META CHANGES.md
153-
154-
install-%: PROJECT=$*
155-
install-%:
156-
$(if $(filter yes,$($(PROJECT).install)),\
157-
$(OCAMLFIND) install -add ctypes -optional $^ \
158-
$(LIB_TARGETS) $(LIB_TARGET_EXTRAS) \
159-
$(INSTALL_MLIS) $(INSTALL_CMIS) \
160-
$(INSTALL_CMTS) $(INSTALL_CMTIS) \
161-
$(INSTALL_HEADERS) \
162-
$(if $(filter yes,$($(PROJECT).install_native_objects)),$(NATIVE_OBJECTS)))
163-
164-
$(PROJECTS:%=install-%): META-install
165-
166-
install: META-install $(PROJECTS:%=install-%)
167-
168-
uninstall:
169-
$(OCAMLFIND) remove ctypes
170-
171-
DOCFILES=$(foreach project,$(PROJECTS),\
172-
$(foreach mli,$($(project).public),\
173-
$($(project).dir)/$(mli).mli))
174-
DOCFLAGS=-I $(shell ocamlfind query integers) $(foreach project,$(PROJECTS),-I $(BUILDDIR)/$($(project).dir))
175-
176-
doc:
177-
ocamldoc -html $(DOCFLAGS) $(DOCFILES)
178-
179-
180-
.PHONY: depend clean configure all install doc $(PROJECTS)
181-
182-
include .depend Makefile.rules Makefile.examples Makefile.tests
183-
-include libffi.config
184-
-include asneeded.config
185-
186-
ifeq ($(libffi_available),false)
187-
test-libffi:
188-
@echo "The following required C libraries are missing: libffi."
189-
@echo "Please install them and retry. If they are installed in a non-standard location"
190-
@echo "or need special flags, set the environment variables <LIB>_CFLAGS and <LIB>_LIBS"
191-
@echo "accordingly and retry."
192-
@echo
193-
@echo " For example, if libffi is installed in /opt/local, you can type:"
194-
@echo
195-
@echo " export LIBFFI_CFLAGS=-I/opt/local/include"
196-
@echo " export LIBFFI_LIBS=\"-L/opt/local/lib -lffi\""
197-
@exit 1
198-
else:
199-
test-libffi:
200-
endif
9+
clean:
10+
dune clean

0 commit comments

Comments
 (0)