Affix

 view release on metacpan or  search on metacpan

MANIFEST  view on Meta::CPAN

infix/src/jit/trampoline.c
lib/Affix.c
lib/Affix.h
lib/Affix.pm
lib/Affix.pod
lib/Affix/Build.pm
lib/Affix/Build.pod
lib/Affix/Platform/BSD.pm
lib/Affix/Platform/MacOS.pm
lib/Affix/Platform/Solaris.pm
lib/Affix/Platform/Unix.pm
lib/Affix/Platform/Windows.pm
lib/Affix/Wrap.pm
lib/Affix/Wrap.pod
lib/Test2/Tools/Affix.pm
t/001_affix.t
t/002_synopsis.t
t/003_pin.t
t/004_typedef.t
t/005_varargs.t
t/006_out_params.t

README.md  view on Meta::CPAN


Loading dynamic libraries across different operating systems (Windows, macOS, Linux, BSD) can be a nightmare of varying
extensions, prefixes, and search paths. Affix abstracts this complexity away with a smart library discovery engine.

## Library Discovery

When you provide a bare library name (e.g., `'z'`, `'ssl'`, `'user32'`) rather than an absolute path, Affix
automatically formats the name for the current platform (e.g., `libz.so`, `libz.dylib`, `z.dll`) and searches the
following locations in order:

- 1. **Standard System Paths:** Windows `System32`/`SysWOW64`; Unix `/usr/local/lib`, `/usr/lib`, `/lib`, `/usr/lib/system`.
- 2. **Environment Variables:** Paths defined in `LD_LIBRARY_PATH`, `DYLD_LIBRARY_PATH`, `DYLD_FALLBACK_LIBRARY_PATH`, or `PATH`.
- 3. **Local Paths:** The current working directory (`.`) and its `lib/` subdirectory.

## Functions

### `load_library( $path_or_name )`

Locates and loads a dynamic library into memory, returning an opaque `Affix::Lib` handle.

```perl

README.md  view on Meta::CPAN


# ERROR HANDLING & DEBUGGING

Bridging two entirely different runtimes can lead to spectacular crashes if types or memory boundaries are mismatched.
Affix provides built-in tools to help you identify what went wrong.

## Error Handling

### `errno()`

Accesses the system error code from the most recent FFI or standard library call (reads `errno` on Unix and
`GetLastError` on Windows).

This function returns a **dualvar**. It behaves as an integer in numeric context, and magically resolves to the
human-readable system error message (via `strerror` or `FormatMessage`) in string context.

```perl
# Suppose a C file-open function fails
my $fd = c_open("/does/not/exist");
if (!$fd) {
    my $err = errno();

infix/src/common/double_tap.h  view on Meta::CPAN

 */
#pragma once
#ifdef DBLTAP_ENABLE
#define TAP_VERSION 13
#include <stdarg.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#if defined(__unix__) || defined(__APPLE__) || defined(__OpenBSD__)
#include <unistd.h>
#endif
#if defined(_WIN32) || defined(__CYGWIN__)
#include <windows.h>
#elif (defined(__unix__) || defined(__APPLE__)) && !defined(__OpenBSD__)
// Do not include pthread.h on OpenBSD to prevent linking/cleanup issues if -pthread is not used.
#include <pthread.h>
#endif

// C++ Headers must be included BEFORE extern "C"
#if defined(__cplusplus)
#include <atomic>
#endif

#ifdef __cplusplus

infix/src/common/double_tap.h  view on Meta::CPAN

#define TAP_THREAD_LOCAL
#elif defined(__cplusplus)
#define TAP_THREAD_LOCAL thread_local
#elif defined(_MSC_VER)
// Microsoft Visual C++
#define TAP_THREAD_LOCAL __declspec(thread)
#elif defined(_WIN32) && defined(__clang__)
// Clang on Windows
#define TAP_THREAD_LOCAL __declspec(thread)
#elif defined(__GNUC__) || defined(__clang__)
// GCC (including MinGW) and Clang on *nix
#define TAP_THREAD_LOCAL __thread
#elif defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L && !defined(__STDC_NO_THREADS__)
#define TAP_THREAD_LOCAL _Thread_local
#else
#define TAP_THREAD_LOCAL
#if !defined(_MSC_VER)
#warning "Compiler does not support thread-local storage; tests will not be thread-safe."
#endif
#endif

infix/src/common/double_tap.h  view on Meta::CPAN

#if defined(_WIN32) || defined(__CYGWIN__)
static INIT_ONCE g_tap_init_once = INIT_ONCE_STATIC_INIT;
static BOOL CALLBACK _tap_init_routine(PINIT_ONCE initOnce, PVOID param, PVOID * context) {
    (void)initOnce;
    (void)param;
    (void)context;
    printf("TAP version %d\n", TAP_VERSION);
    fflush(stdout);
    return TRUE;
}
#elif (defined(__unix__) || defined(__APPLE__)) && !defined(__OpenBSD__)
static pthread_once_t g_tap_init_once = PTHREAD_ONCE_INIT;
static void _tap_init_routine(void) {
    printf("TAP version %d\n", TAP_VERSION);
    fflush(stdout);
}
#else  // OpenBSD or other platforms without robust pthread_once support in this context
static bool g_tap_initialized = false;
#endif

/**
 * @internal
 * @brief Ensures the TAP header has been printed and thread-local state is initialized.
 * Uses `pthread_once` or `InitOnceExecuteOnce` to guarantee the TAP version header
 * is printed exactly once per process, even with multiple threads. It also initializes
 * the thread-local state for the current thread if it's the first test call on that thread.
 */
static void _tap_ensure_initialized(void) {
#if defined(_WIN32) || defined(__CYGWIN__)
    InitOnceExecuteOnce(&g_tap_init_once, _tap_init_routine, NULL, NULL);
#elif (defined(__unix__) || defined(__APPLE__)) && !defined(__OpenBSD__)
    pthread_once(&g_tap_init_once, _tap_init_routine);
#else
    // Fallback for OpenBSD/single-threaded builds
    if (!g_tap_initialized) {
        printf("TAP version %d\n", TAP_VERSION);
        fflush(stdout);
        g_tap_initialized = true;
    }
#endif
    if (!current_state) {

infix/src/core/error.c  view on Meta::CPAN

// Disable TLS entirely on this platform to ensure stability, at the cost of thread-safety.
#define INFIX_TLS
#elif defined(INFIX_COMPILER_MSVC)
// Microsoft Visual C++
#define INFIX_TLS __declspec(thread)
#elif defined(INFIX_OS_WINDOWS) && defined(INFIX_COMPILER_CLANG)
// Clang on Windows: check if behaving like MSVC or GCC.
// If using MSVC codegen/headers, use declspec.
#define INFIX_TLS __declspec(thread)
#elif defined(INFIX_COMPILER_GCC)
// MinGW (GCC on Windows) and standard GCC/Clang on *nix.
// MinGW prefers __thread or _Thread_local over __declspec(thread).
#define INFIX_TLS __thread
#elif defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L && !defined(__STDC_NO_THREADS__)
// Fallback to C11 standard
#define INFIX_TLS _Thread_local
#else
// Fallback for compilers that do not support TLS. This is not thread-safe.
#warning "Compiler does not support thread-local storage; error handling will not be thread-safe."
#define INFIX_TLS
#endif

lib/Affix.pm  view on Meta::CPAN


    BEGIN {
        use XSLoader;
        $DynaLoad::dl_debug = $DynaLoad::dl_debug = 1;
        $okay               = XSLoader::load();
        my $platform
            = 'Affix::Platform::' .
            ( ( $^O eq 'MSWin32' ) ? 'Windows' :
                $^O eq 'darwin'                                                                   ? 'MacOS' :
                ( $^O eq 'freebsd' || $^O eq 'openbsd' || $^O eq 'netbsd' || $^O eq 'dragonfly' ) ? 'BSD' :
                'Unix' );

        #~ warn $platform;
        #~ use base $platform;
        eval 'use ' . $platform . ' qw[:all];';
        $@ && die $@;
        our @ISA = ($platform);
    }
    push @{ $EXPORT_TAGS{lib} }, qw[libm libc];
    $EXPORT_TAGS{types} = [
        qw[ typedef

lib/Affix.pod  view on Meta::CPAN

extensions, prefixes, and search paths. Affix abstracts this complexity away with a smart library discovery engine.

=head2 Library Discovery

When you provide a bare library name (e.g., C<'z'>, C<'ssl'>, C<'user32'>) rather than an absolute path, Affix
automatically formats the name for the current platform (e.g., C<libz.so>, C<libz.dylib>, C<z.dll>) and searches the
following locations in order:

=over

=item 1. B<Standard System Paths:> Windows C<System32>/C<SysWOW64>; Unix C</usr/local/lib>, C</usr/lib>, C</lib>, C</usr/lib/system>.

=item 2. B<Environment Variables:> Paths defined in C<LD_LIBRARY_PATH>, C<DYLD_LIBRARY_PATH>, C<DYLD_FALLBACK_LIBRARY_PATH>, or C<PATH>.

=item 3. B<Local Paths:> The current working directory (C<.>) and its C<lib/> subdirectory.

=back

=head2 Functions

=head3 C<load_library( $path_or_name )>

lib/Affix.pod  view on Meta::CPAN


=head1 ERROR HANDLING & DEBUGGING

Bridging two entirely different runtimes can lead to spectacular crashes if types or memory boundaries are mismatched.
Affix provides built-in tools to help you identify what went wrong.

=head2 Error Handling

=head3 C<errno()>

Accesses the system error code from the most recent FFI or standard library call (reads C<errno> on Unix and
C<GetLastError> on Windows).

This function returns a B<dualvar>. It behaves as an integer in numeric context, and magically resolves to the
human-readable system error message (via C<strerror> or C<FormatMessage>) in string context.

    # Suppose a C file-open function fails
    my $fd = c_open("/does/not/exist");
    if (!$fd) {
        my $err = errno();

lib/Affix/Build.pm  view on Meta::CPAN

        # Cached Flag Arrays
        field @cflags;
        field @cxxflags;
        field @ldflags;
        field $_lib;
        #
        ADJUST {
            my $so_ext = $Config{so} // 'so';
            $build_dir = Path::Tiny->new($build_dir) unless builtin::blessed $build_dir;

            # Standard convention: Windows DLLs don't need 'lib' prefix, Unix SOs do.
            my $prefix = ( $os eq 'MSWin32' || $name =~ /^lib/ ) ? ''          : 'lib';
            my $suffix = defined $version                        ? ".$version" : '';
            $libname = $build_dir->child("$prefix$name.$so_ext$suffix")->absolute;

            # We prefer C++ drivers (g++, clang++) to handle standard libraries for mixed code (C+Rust, C+C++)
            $linker = $self->_can_run(qw[g++ clang++ c++ icpx]) || $self->_can_run(qw[cc gcc clang icx cl]) || 'c++';

            # Parse global flags...
            @cflags   = map { chomp; $_ } grep { defined && length } Text::ParseWords::parse_line( q/ /, 1, $flags->{cflags}   // '' );
            @cxxflags = map { chomp; $_ } grep { defined && length } Text::ParseWords::parse_line( q/ /, 1, $flags->{cxxflags} // '' );

lib/Affix/Platform/BSD.pm  view on Meta::CPAN

package Affix::Platform::BSD v0.12.0 {
    use v5.40;
    use parent 'Affix::Platform::Unix';
    use parent 'Exporter';
    our @EXPORT_OK   = qw[find_library];
    our %EXPORT_TAGS = ( all => \@EXPORT_OK );

    sub find_library ( $name, $version //= '' ) {    # TODO: actually feed version to diff methods
        if ( -f $name ) {
            $name = readlink $name if -l $name;      # Handle symbolic links
            return $name                             # if is_elf($name);
        }
        CORE::state $cache;

lib/Affix/Platform/MacOS.pm  view on Meta::CPAN

package Affix::Platform::MacOS v0.12.0 {
    use v5.40;
    use DynaLoader;
    use parent 'Affix::Platform::Unix';
    use parent 'Exporter';
    our @EXPORT_OK   = qw[find_library];
    our %EXPORT_TAGS = ( all => \@EXPORT_OK );

    sub find_library ($name) {
        return $name if -f $name;
        for my $file ( "lib$name.dylib", "$name.dylib", "$name.framework/$name" ) {
            my $path = DynaLoader::dl_findfile($file);
            return $path if $path;
        }

lib/Affix/Platform/Solaris.pm  view on Meta::CPAN

package Affix::Platform::Solaris v0.12.0 {
    use v5.40;
    use parent 'Affix::Platform::Unix';
    use parent 'Exporter';
    our @EXPORT_OK   = qw[find_library];
    our %EXPORT_TAGS = ( all => \@EXPORT_OK );
};
1;

lib/Affix/Platform/Unix.pm  view on Meta::CPAN

package Affix::Platform::Unix v0.12.0 {
    use v5.40;
    use Path::Tiny qw[path];
    use Config     qw[%Config];
    use DynaLoader;
    use parent 'Exporter';
    our @EXPORT_OK   = qw[find_library];
    our %EXPORT_TAGS = ( all => \@EXPORT_OK );
    my $so = $Config{so};

    sub is_elf ($filename) {



( run in 0.717 second using v1.01-cache-2.11-cpan-df04353d9ac )