Summary: dkml-c-probe is a new package for maintainers who compile or link C code. Install it with opam install dkml-c-probe
. Full docs are at https://github.com/diskuv/dkml-c-probe#readme
Problem
You are creating an OCaml package that has foreign C code. Perhaps you need special C headers or libraries when you are targeting Apple users, or perhaps you need to execute custom OCaml code for Android users. More generally you need a way to determine whether your OCaml or C code is compiling for a Linux AMD/Intel 64-bit, Android ARM 32-bit, or any other ABI target.
Solution
A user of your OCaml package may, for example, be on a 64-bit AMD/Intel Linux machine using a 32-bit OCaml system compiled with gcc -m32
; additionally they have a 32-bit Android ARM cross-compiler toolchain. dkml-c-probe
will tell you the target operating system is Linux
and the target ABI is Linux_x86
except when the cross-compiler toolchain is invoked. With the cross-compiler toolchain dkml-c-probe
will tell you the target operating system is Android
and the target ABI is Android_arm32v7a
.
How it works
dkml-c-probe
uses C preprocessor definitions (ex. #if TARGET_CPU_X86_64
, #if __ANDROID__
, etc.) to determine which ABI the C compiler (ex. ocamlopt -config | grep native_c_compiler
) is targeting.
This isn’t a new idea. The pattern is used in Esy and Mirage code as well. dkml-c-probe
just codifies the pattern for use in your own code.
Usage
In OCaml code you can use the versioned module:
module V2 :
sig
type t_os = Android | IOS | Linux | OSX | Windows
type t_abi =
Android_arm64v8a
| Android_arm32v7a
| Android_x86
| Android_x86_64
| Darwin_arm64
| Darwin_x86_64
| Linux_arm64
| Linux_arm32v6
| Linux_arm32v7
| Linux_x86_64
| Linux_x86
| Windows_x86_64
| Windows_x86
| Windows_arm64
| Windows_arm32
val get_os : (t_os, string) result Lazy.t
val get_abi : (t_abi, string) result Lazy.t
val get_abi_name : (string, string) result Lazy.t
end
Edit: The docs wrongly showed (*, Rresult.R.msg) result Lazy.t
. The (*, string) result Lazy.t
type is actually used in the API
In C code you can use the provided dkml_compiler_probe.h
header from within Dune or Opam. Here is a snippet that handles part of the Linux introspection:
#elif __linux__
# if __ANDROID__
# ...
# else
# define DKML_OS_NAME "Linux"
# define DKML_OS_Linux
# if __aarch64__
# define DKML_ABI "linux_arm64"
# define DKML_ABI_linux_arm64
# elif __arm__
# if defined(__ARM_ARCH_6__) || defined(__ARM_ARCH_6J__) || defined(__ARM_ARCH_6K__) || defined(__ARM_ARCH_6Z__) || defined(__ARM_ARCH_6ZK__) || defined(__ARM_ARCH_6T2__)
# define DKML_ABI "linux_arm32v6"
# define DKML_ABI_linux_arm32v6
# elif defined(__ARM_ARCH_7__) || defined(__ARM_ARCH_7A__) || defined(__ARM_ARCH_7R__) || defined(__ARM_ARCH_7M__) || defined(__ARM_ARCH_7S__)
# define DKML_ABI "linux_arm32v7"
# define DKML_ABI_linux_arm32v7
# endif /* __ARM_ARCH_6__ || ..., __ARM_ARCH_7__ || ... */
# elif __x86_64__
# define DKML_ABI "linux_x86_64"
# define DKML_ABI_linux_x86_64
# elif __i386__
# define DKML_ABI "linux_x86"
# define DKML_ABI_linux_x86
# elif defined(__ppc64__) || defined(__PPC64__)
# define DKML_ABI "linux_ppc64"
# define DKML_ABI_linux_ppc64
# elif __s390x__
# define DKML_ABI "linux_s390x"
# define DKML_ABI_linux_s390x
# endif /* __aarch64__, __arm__, __x86_64__, __i386__, __ppc64__ || __PPC64__, __s390x__ */
Versioning and Contributing
Whenever a new ABI is added, it goes into a new version (ex. module V3
). Your existing code that uses module V2
will be unaffected.
But each new ABI needs to have its own maintainer because I don’t have access to every hardware platform on the planet!
For example, PowerPC (ppc64
) and Linux on IBM Z (s390x
) are supported in the C Header but not the OCaml module because there are no PowerPC and S390x maintainers.
Please consider contributing, especially if you want others to have an easier compilation story for your favorite hardware platform.