.. _cffi: System libraries and C FFI ========================== Linking Hare programs with C libraries requires the following steps: 1. Forward declare necessary C symbols in your Hare sources 2. Pass the appropriate ``-l``/``-L`` flags to ``hare build`` (or ``hare run``, ``hare test``, etc) .. note:: Linking to any C libraries will also implicitly link to libc, which will use a libc-oriented Hare runtime that, for example, performs memory allocations via libc's malloc. This will also enable the :ref:`+libc build tag `. Linking to libraries -------------------- To link to a system library, pass ``-lname`` to ``hare build`` to link with libname.a (or libname.so) in the default library path. You can add to the library search path with ``-L`` (``-l`` and ``-L`` are passed directly on to ``cc`` by the build driver). It is recommended to identify the correct linker flags with `pkg-config`_. hare(1) does not interpret C compiler flags, so the user must take care to only add the compatible linker flags: .. _pkg-config: https://manned.org/pkg-config.1 .. code-block:: shell hare build $(pkg-config --libs-only-L --libs-only-l example) ... This will Probably Work™. Application Binary Interface ---------------------------- Hare uses a superset of the standard C ABI for the platform: [#abi-exceptions]_ * aarch64: `Procedure Call Standard for the ARM 64-bit Architecture `_ * riscv64: `RISC-V User-Level ISA `_ * x86_64: `System-V Application Binary Interface `_ Consult the `Hare specification`_ for representations of Hare-exclusive constructs, such as tagged unions or tuples. .. _Hare specification: https://harelang.org/specification Calling C from Hare +++++++++++++++++++ The `types::c`_ module provides type aliases for the host environment's standard C types, such as ``char`` and ``ulong``. .. _types::c: https://docs.harelang.org/types/c Any symbols or types you wish to use from Hare code in the libraries of interest must be forward-declared in your Hare module. Consider the following C symbol: .. code-block:: c FILE *fopen(const char *pathname, const char *mode); This should be defined in your Hare code like so: .. code-block:: hare use types::c; // See stdio.h export type FILE = opaque; // See stdio.h export @symbol("fopen") fn fopen( pathname: const *c::char, mode: const *c::char, ) nullable *FILE; The use of **@symbol** is requried here to ensure that the Hare symbol uses the same symbol name as the C symbol, so that if this appears, for example, in the "libc" Hare namespace, it's compiled as ``fopen`` rather than ``libc.fopen``. You can then use this function from Hare like so: .. code-block:: hare let file = fopen(c::nulstr("example.txt\0"), c::nulstr("r\0")); .. warning:: Hare strings are not NUL terminated and are not directly compatible with C's ``char *`` type. `types::c`_ provides functions to assist in converting between C strings and Hare strings. c::nulstr is useful for string literals which the programmer manually NUL-terminates; otherwise c::fromstr will heap-allocate a NUL-terminated copy of the input string. Converting from C strings to Hare strings is an O(n) operation that does not require allocation; see c::tostr. Calling Hare from C +++++++++++++++++++ Hare functions which are limited to the C ABI (using types::c or only the C-compatible subset of Hare types) may be directly called by C if you write a header with an appropriate forward declaration. For example, consider the following Hare function: .. code-block:: hare use types::c; export fn greet_user(user: const *c::char) int = { const user = c::tostr(user)!; return fmt::printfln("Hello, {}!", user)!: int; }; One may call this from C like so: .. code-block:: c int greet_user(const char *user); int main() { greet_user("Harriet"); return 0; } See :ref:`mixing-sources` for details on getting these two functions to co-exist in a binary. .. note:: Most Hare types are intuitively compatible with the C types you would assume, such as u8 being compatible with stdint.h's uint8_t. Most structs are trivially compatible if you rewrite the syntax appropriately, and tuples are syntax sugar over structs. The internal representations of slices and strings are also given by `types::slice`_ and `types::string`_ respectively. If in doubt, consult the `Hare specification`_, which defines the internal representation of all Hare types. On the other hand, if the Hare API knows that its target audience is C users, it can exclusively use types from `types::c`_ to limit itself to the trivially compatible ABI subset. .. _types::slice: https://docs.harelang.org/types#slice .. _types::string: https://docs.harelang.org/types#string Regarding Hare namespaces ~~~~~~~~~~~~~~~~~~~~~~~~~ Note that if the "greet_user" function appears in a Hare module (e.g. ``greetings/user.ha``), the symbol name as it appears to C will not be "greet_user". You must update either the Hare declaration or the C declaration accordingly. To cause Hare to emit a symbol named "greet_user", use the following code: .. code-block:: hare export @symbol("greet_user") fn greet_user(user: const *c::char) int = { // ... }; On the other hand, if it is more appropriate for the C code to include the Hare namespace, you may do this with a GCC extension: .. code-block:: c int greet_user(const char *user) asm ("greetings.greet_user"); stdarg.h and va_list ++++++++++++++++++++ Hare supports (type unsafe) C-style variadism using `stdarg.h`_-compatible semantics. To call a variadic C function from Hare, simply define it with ``...`` in the variadic parameter position. .. _stdarg.h: https://pubs.opengroup.org/onlinepubs/009695399/basedefs/stdarg.h.html .. code-block:: hare export @symbol("printf") fn printf(format: const *c::char, ...) int; You can call this function normally from Hare code. To implement a variadic function that can be called from C, or to prepare a va_list that can be passed to a C function, you can define your own Hare function with the ``...`` operator and use the **valist** type and **vastart**, **vaarg**, and **vaend** expressions. .. code-block:: hare use fmt; use strings; use types::c; // Implements a C-compatible printf function export @symbol("printf") fn printf(format: const *c::char, ...) int = { const fmt = c::tostr(format); const iter = strings::iter(fmt); const ap: valist = vastart(); defer vaend(ap); for (true) { const next = match (strings::next(&iter)) { case void => break; case let rn: rune => yield rn; }; if (rn != '%') { fmt::print(rn)!; continue; }; const ctrl = strings::next(&iter) as rune; switch (ctrl) { case 'd' => fmt::print(vaarg(ap): int)!; case 's' => const s: *c::char = vaarg(ap); fmt::print(c::tostr(s))!; // ... }; }; }; .. _mixing-sources: Mixing Hare and C sources ------------------------- There are two ways to do this, depending on which tool you want to perform the linking. Note that you can also use any other language (C++, Rust, etc) with either approach so long as the language in question can work with object files; adapt these instructions as necessary. If you want hare(1) to perform linking ++++++++++++++++++++++++++++++++++++++ Using a separate build orchestrator (e.g. make), compile C sources to object files with ``cc -c [...]`` and place these object files into the Hare module directory where they belong. The build driver will automatically link them with executables that depend on this module. If you want cc(1) or ld(1) to perform linking +++++++++++++++++++++++++++++++++++++++++++++ Using a separate build orchestrator (e.g. make), compile Hare sources to object files with ``hare build -t o [...]``. Pass all of the Hare objects to cc(1) or ld(1) when linking. .. note:: Note that ``hare build -t o`` builds **an entire Hare module**, not just one source file. The resulting object file *only* includes this module, and not any of the Hare dependencies it imports via the **use** keyword. You must enumerate dependencies yourself, build them separately, and pass all of the corresponding objects to the linker. The ``hare deps`` command is useful for enumerating the dependencies of a module or source file. .. rubric:: Footnotes .. [#abi-exceptions] Hare's ABI is *mostly* a superset of the C ABI. However, Hare does not support decimal types, complex numbers, long double, or extensions such as int128_t.