readcon-core 0.4.0

An oxidized single and multiple CON file reader and writer with FFI bindings for ergonomic C/C++ usage.
Documentation
project(
    'readcon-core',
    ['rust', 'c'],
    version: '0.4.0',
    meson_version: '>=1.8.0',
    default_options: [
        'buildtype=debugoptimized',
        'warning_level=3',
        'c_std=c99',
        'rust_std=2024',
        'b_ndebug=if-release',
    ],
)

# Sanity checks
cc = meson.get_compiler('c')
rc = meson.get_compiler('rust')
if rc.version().version_compare('< 1.88.0')
    error('readcon-core requires Rust 1.88.0')
endif

cbindgen_prog = find_program('cbindgen', version: '>=0.29.0', required: true)

# Auxiliary variables
_mbproot = meson.project_build_root()
_msproot = meson.project_source_root()

# C Header
# This is done twice, once for installing things and once to generate the
# "in-tree" copy
readcon_header_build = custom_target(
    'cbindgen_clone',
    input: [files('cbindgen.toml'), files('src/lib.rs')],
    output: ['readcon-core.h'],
    command: [
        cbindgen_prog,
        '-q',
        '--config',
        '@INPUT0@',
        '--crate',
        'readcon',
        '--output',
        '@OUTPUT@',
        '--',
        '@INPUT1@',
    ],
    install: true,
    install_dir: 'include',
    build_by_default: true,
)

readcon_header_intree = custom_target(
    'cbindgen-readcon',
    input: [files('cbindgen.toml'), files('src/lib.rs')],
    output: ['readcon-intree'],
    command: [
        cbindgen_prog,
        '-q',
        '--config',
        '@INPUT0@',
        '--crate',
        'readcon',
        '@INPUT1@',
        '--output',
        f'@_msproot@/include/readcon-core.h',
    ],
    install: false,
    build_by_default: true,
)

cargo_prog = find_program('cargo', required: true)

# Build the Rust library via cargo to ensure all dependencies
# (memmap2, fast_float2, etc.) are properly resolved and linked.
# Meson's native library(rust_abi: 'c') bypasses Cargo, making
# external crates unavailable.
readcon_cargo = custom_target(
    'readcon-cargo',
    input: files('Cargo.toml', 'Cargo.lock', 'src/lib.rs'),
    output: 'libreadcon_core.a',
    command: [
        'sh', '-c',
        cargo_prog.full_path() + ' build --release --manifest-path @INPUT0@ --target-dir @PRIVATE_DIR@/cargo-target && cp @PRIVATE_DIR@/cargo-target/release/libreadcon_core.a @OUTPUT0@',
    ],
    build_by_default: true,
)

# System dependencies required when statically linking a Rust library
_rust_sys_deps = []
if host_machine.system() == 'linux'
    _rust_sys_deps += [
        dependency('threads'),
        cc.find_library('dl', required: true),
        cc.find_library('m', required: true),
    ]
elif host_machine.system() == 'darwin'
    _rust_sys_deps += [dependency('threads')]
endif

if get_option('with_tests')
    test('cargo-tests', cargo_prog,
        args: ['test', '--manifest-path', _msproot / 'Cargo.toml'],
        timeout: 300,
    )
endif

readcon_dep = declare_dependency(
    link_with: readcon_cargo,
    dependencies: _rust_sys_deps,
    include_directories: include_directories('include'),
)

# Examples
if get_option('with_examples')
    subdir('examples')
endif

# Pkgconf generation

pkg = import('pkgconfig')
pkg_ver = meson.project_version()
pkg_install_dir = join_paths(get_option('datadir'), 'pkgconfig')
pkg.generate(
    name: 'readcon-core',
    filebase: 'meson-readcon-core',
    description: 'CON file-reader in Rust',
    version: f'@pkg_ver@_meson',
    install_dir: pkg_install_dir,
)