Skip to content
FutureBlitz

    Install

    C interop, 6 of 7

    Native libraries in project.toml

    Name a package's C libraries in [native], static, system, or dynamic, and point Extern blocks at them with Lib.

    What you’ll build

    A package, roots, that calls cbrt and hypot from C’s math library, which it names in project.toml. Then the other two kinds of C library a package can bring: a static archive in its folder, linked into the executable, and a dynamic library, shipped beside it.

    A library in [native]

    A package’s C libraries are listed in its project.toml’s [native] table, each under a name. An Extern "C" Lib "name" block says its functions are in that library. A system library is one the system’s linker finds by its name, as -lm finds the math library.

    project.toml
    [project]
    name = "roots"
    version = "0.1.0"
    
    [native]
    m = { system = "m" }
    main.fb
    Import std.core
    
    Extern "C" Lib "m"
        Function cbrt(x: Float64): Float64
        Function hypot(x: Float64, y: Float64): Float64
    End Extern
    
    Print(cbrt(27.0))
    Print(hypot(3.0, 4.0))
    Output
    3
    5

    Lib names a library of the package whose file declares the functions. A name that isn’t in [native] is an error:

    project.toml
    [project]
    name = "roots"
    version = "0.1.0"
    
    [native]
    m = { system = "m" }
    main.fb
    Import std.core
    
    Extern "C" Lib "zlib"
        Function crc32(crc: CULong, bytes: Ptr<UInt8>, count: UInt32): CULong
    End Extern
    
    Print("never built")
    $ fb run
       Compiling roots v0.1.0
    main.fb:3:16: error: the roots package's project.toml has no [native] library "zlib"
      |
    3 | Extern "C" Lib "zlib"
      |                ^^^^^^
    error: couldn't build roots v0.1.0 because of 1 error

    A function that isn’t in the library is an error when the program links, at the line that declares it, saying where it should have been:

    project.toml
    [project]
    name = "roots"
    version = "0.1.0"
    
    [native]
    m = { system = "m" }
    main.fb
    Import std.core
    
    Extern "C" Lib "m"
        Function cbrt(x: Float64): Float64
        Function no_such_function(x: Float64): Float64
    End Extern
    
    Print(cbrt(27.0), no_such_function(1.0))
    $ fb run
       Compiling roots v0.1.0
    main.fb:5:14: error: the C symbol 'no_such_function' isn't in m, or any library the program links
      |
    5 |     Function no_such_function(x: Float64): Float64
      |              ^^^^^^^^^^^^^^^^
    error: couldn't link roots v0.1.0 because of 1 error

    A static library

    A static library is an archive in the package’s folder, by its path without its lib prefix and extension: "libs/sqlite3" finds libs/libsqlite3.a, libs/sqlite3.a, or libs/sqlite3.lib. It’s the default, so a plain string means the same:

    [native]
    sqlite3 = { static = "libs/sqlite3" }
    zstd = "libs/zstd"

    A static library is linked into the executable, so the program is still one file, with nothing to install beside it. On Windows, build the archive for the UCRT C runtime, which programs link, as gcc’s -mcrtdll=ucrt does. A path that names no archive is an error at its line in project.toml.

    A dynamic library

    A dynamic library is loaded as the program starts: a DLL on Windows, a .so on Linux, and a .dylib on macOS. A package ships a build of it for each target it supports, and {target} in the path is the target being built for: windows-x64, linux-x64, linux-arm64, macos-arm64, or macos-x64.

    [native]
    codec = { dynamic = "native/{target}/codec" }

    On Windows that finds native/windows-x64/codec.dll and its import library beside it, and on Linux native/linux-x64/libcodec.so. fb build copies each dynamic library the program needs beside the executable, which finds it there, so the build folder is what you ship. Building for a target the package has no binary for is an error that names the package.

    FutureBlitz calls C, not C++, so a C++ library, static or dynamic, is used through its C API, functions declared extern "C", which bindgen turns into an Extern block.

    What you learned

    • [native] lists a package’s C libraries, each by a name that Extern "C" Lib "name" uses.
    • system libraries are found by the linker, static ones are archives linked into the executable, and dynamic ones are copied beside it.
    • {target} in a path lets a package ship a binary for each target.
    • A missing library or symbol is an error that says where it should have been.

    Report an issue with this page or with FutureBlitz on GitHub.