Affix

 view release on metacpan or  search on metacpan

Changes.md  view on Meta::CPAN

- SIMD Vector Improvements:
 - Added M512, M512d, and M512i type helpers.
 - Ensured compatibility with infix's refined vector alignment and passing rules.
- [infix] Added support for half-precision floating-point (`float16`).
- [infix] Implemented C++ exception propagation through JIT frames on Linux (x86-64 and ARM64) using manual DWARF `.eh_frame` generation and `__register_frame`.
- [infix] Implemented Structured Exception Handling (SEH) for Windows x64 and ARM64 for C++ exception propagation through trampolines.
- [infix] Added `infix_forward_create_safe` API to establish an exception boundary that catches native exceptions and returns a dedicated error code (`INFIX_CODE_NATIVE_EXCEPTION`).
- [infix] Added support for 256-bit (AVX) and 512-bit (AVX-512) vectors in the System V ABI.
- [infix] Added support for receiving bitfield structs in reverse call trampolines.
- [infix] Added trampoline caching. Identical signatures and targets now share the same JIT-compiled code and metadata via internal reference counting, significantly reducing memory overhead and initialization time.
- [infix] Added a new opt-in build mode (`--sanity`) that emits extra JIT instructions to verify stack pointer consistency around user-provided marshaller calls, making it easier to debug corrupting language bindings.

### Changed

- Pull infix v0.1.6.
- [infix] Explicitly enabled 16-byte stack alignment in Windows x64 trampolines to ensure SIMD compatibility.
- [infix] Updated `infix_type_create_vector` to use the vector's full size for its natural alignment (e.g., 32-byte alignment for `__m256`).
- [infix] Refined the Windows x64 ABI to pass all vector types by reference (pointer in GPR). This ensures compatibility with MSVC which expects even 128-bit vectors to be passed via pointer in many scenarios, while still returning them by value in `...
- [infix] Move to a pre-calculated hash field in `_infix_registry_entry_t`. Lookups and rehashing now use this stored hash, significantly reducing string hashing overhead during type resolution and registry scaling.
- [infix] Optimized Type Registry memory management: Internal hash table buckets are now heap-allocated and freed during rehashes, preventing memory "leaks" within the registry's arena.

Changes.md  view on Meta::CPAN

  - Correctly implemented array decay for function arguments on ARM and Win64. `Array[...]` types are now marshalled into temporary C arrays and passed as pointers, matching standard C behavior. Previously, they were incorrectly passed by value, caus...
  - Fixed binary safety for `Array[Char/UChar]`. Reading these arrays now respects the explicit length rather than stopping at the first null byte.
  - The write-back mechanism no longer attempts to overwrite the read-only ArrayRef scalar with the pointer address.
  - `Pointer[SV]` is now handled properly as args, return values, and in callbacks. Reference counting is automatic to prevent premature garbage collection of passed scalars.
  - Shared libs written in Go spin up background threads (for GC and scheduling) that do not shut down cleanly when a shared library is unloaded. This often causes access violations on Windows during program exit. We attempt to work around this by de...

## [v1.0.2] - 2025-12-14

### Changed

  - In an attempt to debug mystery failures in SDL3.pm, Affix.pm will warn and return `undef` instead of `croak`ing.
  - Improved error reporting: if the internal error message is empty, the numeric error code is now included in the warning.

### Fixed

  - [[infix]] Fixed a critical file descriptor leak on POSIX platforms (Linux/FreeBSD) where the file descriptor returned by `shm_open` was kept open for the lifetime of the trampoline, eventually hitting the process file descriptor limit (EMFILE). T...
  - Fixed memory leaks that occurred when trampoline creation failed midway (cleaning up partial arenas, strings, and backend structures).

## [v1.0.1] - 2025-12-13

### Changed

README.md  view on Meta::CPAN


# 2. Read the memory immediately as an integer value
my $val = cast($void_ptr, Int); # Returns 99
```

# POINTER UTILITIES

### `address( $ptr )`

Returns the virtual memory address of the pointer as a Perl Unsigned Integer (`UInt64`). Useful for passing addresses
to other FFI libraries or debugging.

```
say sprintf("Address: 0x%X", address($ptr));
```

### `ptr_add( $ptr, $offset_bytes )`

Returns a new **unmanaged alias Pin** offset by `$offset_bytes`.

```perl

builder/Affix/Builder.pm  view on Meta::CPAN

    use JSON::PP 2            qw[encode_json decode_json];
    use File::Temp            qw[tempfile];

    # Not in CORE
    use Path::Tiny qw[path cwd];
    use ExtUtils::Helpers 0.028 qw[make_executable split_like_shell detildefy];

    # infix and Affix stuff
    use Config qw[%Config];
    field $force : param //= 0;
    field $debug : param = 0;
    field $libver;
    field $cflags;
    field $ldflags;
    field $cppver = 'c++17';    # https://en.wikipedia.org/wiki/C%2B%2B20#Compiler_support
    field $cver   = 'c17';      # https://en.wikipedia.org/wiki/C17_(C_standard_revision)
    field $make : param //= $Config{make};
    #
    field $action : param //= 'build';
    field $meta : reader = CPAN::Meta->load_file('META.json');

builder/Affix/Builder.pm  view on Meta::CPAN

    field $prefix        : param    //= '';
    #
    ADJUST {
        -e 'META.json' or die "No META information provided\n";

        # Configure Flags
        my $is_bsd = $^O =~ /bsd/i;
        my $is_win = $^O =~ /MSWin32/i;
        $cflags  = $is_bsd ? '' : '-fPIC ';
        $ldflags = $is_bsd ? '' : ' -flto=auto ';
        if ( $debug > 0 ) {
            $cflags
                .= '-DDEBUG=' .
                $debug .
                ' -g3 -gdwarf-4 ' .
                ' -Wno-deprecated -pipe ' .
                ' -Wall -Wextra -Wpedantic -Wvla -Wnull-dereference ' .
                ' -Wswitch-enum  -Wduplicated-cond ' .
                ' -Wduplicated-branches';
            $cflags .= ' -fvar-tracking-assignments' unless $Config{osname} eq 'darwin';
        }
        elsif ( !$is_win ) {
            $cflags
                .= ' -DNDEBUG -DBOOST_DISABLE_ASSERTS -Ofast -ftree-vectorize -ffast-math -fno-align-functions -fno-align-loops -fno-omit-frame-pointer -flto=auto';

builder/Affix/Builder.pm  view on Meta::CPAN

                quiet        => 0,
                'C++'        => $cxx,
                source       => $source->stringify,
                defines      => { VERSION => qq/"$version"/, XS_VERSION => qq/"$version"/ },
                include_dirs => [
                    cwd->stringify,                                             cwd->child('infix')->realpath->stringify,
                    cwd->child('infix')->child('include')->realpath->stringify, cwd->child('infix')->child('src')->realpath->stringify,
                    $source->dirname,                                           $pre->child( $meta->name, 'include' )->stringify
                ],
                extra_compiler_flags =>
                    ( '-fPIC -std=' . ( $cxx ? $cppver : $cver ) . ' ' . $cflags . ( $debug ? ' -ggdb3 -g -Wall -Wextra -pedantic' : '' ) )
                ) :
                $obj;
        }

        # Point to the Architecture-specific build lib
        my $infix_build_lib = cwd->absolute->child('infix')->child( 'build_lib', $Config{archname} )->stringify;

        # Check for -lrt requirement
        my $lrt_flag = $self->check_for_lrt();
        my $data     = {

infix/include/infix/infix.h  view on Meta::CPAN

 */
typedef struct infix_registry_iterator_t {
    const infix_registry_t * registry; /**< The registry being iterated. */
    size_t _bucket_index;              /**< Internal: current hash bucket. */
    void * _current_entry;             /**< Internal: opaque pointer to current entry. */
} infix_registry_iterator_t;
/**
 * @brief Serializes all defined types within a registry into a single, human-readable string.
 *
 * The output format is a sequence of definitions (e.g., `@Name = { ... };`) separated
 * by newlines, suitable for logging, debugging, or saving to a file. This function
 * will not print forward declarations that have not been fully defined.
 *
 * @param[out] buffer The output buffer to write the string into.
 * @param[in] buffer_size The size of the output buffer.
 * @param[in] registry The registry to serialize.
 * @return `INFIX_SUCCESS` on success, or `INFIX_ERROR_INVALID_ARGUMENT` if the buffer is too small.
 */
INFIX_API INFIX_NODISCARD infix_status infix_registry_print(char *, size_t, const infix_registry_t *);
/**
 * @brief Initializes an iterator for traversing the types in a registry.

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

 *
 * SPDX-License-Identifier: (Artistic-2.0 OR MIT)
 *
 * The documentation blocks within this file are licensed under the
 * Creative Commons Attribution 4.0 International License (CC BY 4.0).
 *
 * SPDX-License-Identifier: CC-BY-4.0
 */
/**
 * @file utility.h
 * @brief A header for conditionally compiled debugging utilities.
 * @ingroup internal_utils
 *
 * @internal
 * This header is the central point for the library's internal debugging infrastructure.
 * Its primary feature is that it behaves differently based on the `INFIX_DEBUG_ENABLED`
 * preprocessor macro. This allows debugging code to be seamlessly integrated
 * during development without affecting the performance or size of the final
 * production binary.
 *
 * - **When `INFIX_DEBUG_ENABLED` is defined and non-zero (Debug Mode):**
 *   - It declares the `infix_dump_hex` function for detailed memory inspection.
 *   - It defines the `INFIX_DEBUG_PRINTF` macro, which integrates with the `double_tap`
 *     test harness's logging system (`note()`) if available, or falls back to a
 *     standard `printf`. This allows debug messages from the core library to appear
 *     cleanly within the test output.
 *
 * - **When `INFIX_DEBUG_ENABLED` is not defined or is zero (Release Mode):**
 *   - All debugging macros are defined as no-ops (`((void)0)`).
 *   - The `infix_dump_hex` function is defined as an empty `static inline` function.
 *   - This design ensures that all debugging code and calls are completely compiled
 *     out by the optimizer, resulting in zero overhead in release builds.
 * @endinternal
 */
#include "common/compat_c23.h"
#include <stddef.h>
// Check if INFIX_DEBUG_ENABLED is defined and set to a non-zero value.
#if defined(INFIX_DEBUG_ENABLED) && INFIX_DEBUG_ENABLED
#include <stdio.h>
/**
 * @internal
 * @def INFIX_DEBUG_PRINTF(...)
 * @brief A macro for printing formatted debug messages during a debug build.
 * @details This macro falls back to a standard `printf`, ensuring that debug messages are still visible.
 *          We use a '#' prefix to ensure these messages are treated as comments by TAP consumers.
 */
#define INFIX_DEBUG_PRINTF(...)                \
    do {                                       \
        printf("# INFIX_DEBUG: " __VA_ARGS__); \
        printf("\n");                          \
        fflush(stdout);                        \
    } while (0)

/**
 * @internal
 * @brief Declares the function prototype for `infix_dump_hex` for use in debug builds.
 * @details This function is an invaluable tool for inspecting the raw machine code generated
 *          by the JIT compiler or examining the memory layout of complex structs. It prints
 *          a detailed hexadecimal and ASCII dump of a memory region to the standard
 *          output, formatted for readability.
 *
 * @param data A pointer to the start of the memory block to dump.
 * @param size The number of bytes to dump.
 * @param title A descriptive title to print before and after the hex dump.
 */
void infix_dump_hex(const void * data, size_t size, const char * title);
/**
 * @internal
 * @brief Declares the function prototype for `infix_dump_state` for use in debug builds.
 * @details This function is an invaluable tool for inspecting the processor state.
 *
 * @param file The file name where the function is being called.
 * @param title The line number.
 */
void infix_dump_state(const char * file, int line);
/**
 * @internal
 * @def INFIX_DUMP_STATE(...)
 * @brief Automatically pass on the current file and line to `infix_dump_state`.
 */
#define INFIX_DUMP_STATE() infix_dump_state(__FILE__, __LINE__)
#else  // INFIX_DEBUG_ENABLED is NOT defined or is zero (Release Mode)
/**
 * @internal
 * @def INFIX_DEBUG_PRINTF(...)
 * @brief A no-op macro for printing debug messages in release builds.
 * @details In release builds, this macro is defined as `((void)0)`, a standard C idiom
 *          for creating a statement that does nothing and has no side effects. The
 *          compiler will completely remove any calls to it, ensuring zero performance impact.
 */
#define INFIX_DEBUG_PRINTF(...) ((void)0)
/**
 * @internal
 * @brief A no-op version of `infix_dump_hex` for use in release builds.
 * @details This function is defined as an empty `static inline` function.
 *          - `static`: Prevents linker errors if this header is included in multiple files.

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

 * @details This module is responsible for two key functionalities that form the
 * user-facing API of the library:
 *
 * 1.  **Parsing:** It contains a hand-written recursive descent parser that transforms a
 *     human-readable signature string (e.g., `"({int, *char}) -> void"`) into an
 *     unresolved `infix_type` object graph. This is the **"Parse"** stage of the core
 *     data pipeline. The internal entry point for the "Parse" stage is `_infix_parse_type_internal`.
 *
 * 2.  **Printing:** It provides functions to serialize a fully resolved `infix_type`
 *     graph back into a canonical signature string. This is crucial for introspection,
 *     debugging, and verifying the library's understanding of a type.
 *
 * The public functions `infix_type_from_signature` and `infix_signature_parse`
 * are high-level orchestrators. They manage the entire **"Parse -> Copy -> Resolve -> Layout"**
 * pipeline, providing the user with a fully validated, self-contained, and ready-to-use
 * type object that is safe to use for the lifetime of its returned arena.
 */
#include "common/infix_internals.h"
#include <ctype.h>
#include <stdarg.h>
#include <stdbool.h>

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

        }
    }
    else if (buffer_size > 0)
        buffer[buffer_size - 1] = '\0';
    return state.status;
}
/**
 * @brief Serializes all defined types within a registry into a single, human-readable string.
 *
 * @details The output format is a sequence of definitions (e.g., `@Name = { ... };`) separated
 * by newlines, suitable for logging, debugging, or saving to a file. This function
 * will not print forward declarations that have not been fully defined. The order of
 * definitions in the output string is not guaranteed.
 *
 * @param[out] buffer The output buffer to write the string into.
 * @param[in] buffer_size The size of the output buffer.
 * @param[in] registry The registry to serialize.
 * @return `INFIX_SUCCESS` on success, or `INFIX_ERROR_INVALID_ARGUMENT` if the buffer is too small
 *         or another error occurs.
 */
c23_nodiscard infix_status infix_registry_print(char * buffer, size_t buffer_size, const infix_registry_t * registry) {

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

 *
 * SPDX-License-Identifier: (Artistic-2.0 OR MIT)
 *
 * The documentation blocks within this file are licensed under the
 * Creative Commons Attribution 4.0 International License (CC BY 4.0).
 *
 * SPDX-License-Identifier: CC-BY-4.0
 */
/**
 * @file utility.c
 * @brief Implements internal debugging utilities.
 * @ingroup internal_core
 *
 * @details This file provides the implementation for debugging functions that are
 * conditionally compiled only when the `INFIX_DEBUG_ENABLED` preprocessor macro
 * is defined and set to a non-zero value.
 *
 * In release builds, this entire translation unit is effectively empty, ensuring
 * that debugging code has no impact on the final binary's size or performance.
 * The corresponding function declarations in `utility.h` become empty inline
 * stubs, which are optimized away by the compiler.
 */
// This file is only compiled if debugging is enabled.
#if defined(INFIX_DEBUG_ENABLED) && INFIX_DEBUG_ENABLED
// Use the double-tap test harness's `note` macro for debug printing if available.
// This integrates the debug output seamlessly into the TAP test logs.
#if defined(DBLTAP_ENABLE) && defined(DBLTAP_IMPLEMENTATION)
#include "common/double_tap.h"
#else
// If not building as part of a test, fall back to a standard printf implementation.
#include <stdio.h>
#ifndef note
#define note(...)                 \
    do {                          \
        printf("# " __VA_ARGS__); \
        printf("\n");             \
    } while (0)
#endif
#endif  // DBLTAP_ENABLE
#include "common/utility.h"
#include <inttypes.h>
/**
 * @internal
 * @brief Dumps a block of memory to standard output in a standard hexadecimal format.
 *
 * @details This is an essential debugging tool for inspecting the machine code generated
 * by the JIT engine or for examining the memory layout of complex data structures.
 * The output is formatted similarly to common hex dump utilities, with a 16-byte
 * width, address offsets, hexadecimal bytes, and an ASCII representation.
 *
 * This function is only compiled in debug builds.
 *
 * @param data A pointer to the memory to dump.
 * @param size The number of bytes to dump.
 * @param title A descriptive title to print before the hex dump.
 */
void infix_dump_hex(const void * data, size_t size, const char * title) {
    const uint8_t * byte = (const uint8_t *)data;
    char line_buf[256];
    char * buf_ptr;
    size_t remaining_len;

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

                remaining_len -= written;
            }
        }
print_line:
        note("  %s", line_buf);
    }
    note("End of %s", title);
}
/**
 * @internal
 * @brief Declares the function prototype for `infix_dump_state` for use in debug builds.
 * @details This function is an invaluable tool for inspecting the processor state.
 *
 * This function is only compiled in debug builds.
 *
 * @param file The file name where the function is being called.
 * @param title The line number.
 */
void infix_dump_state(const char * file, int line) {
#if defined(INFIX_ARCH_X64)
    printf("# Dumping x64 Register State at %s:%d\n", file, line);
    volatile unsigned long long stack_dump[16];
    register long long rsp __asm__("rsp");

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

 * rest of the source tree. It is the entry point for the build system.
 * @endinternal
 */
// 1. Error Handling: Provides the thread-local error reporting system.
//    (No dependencies on other infix modules).
#include "core/error.c"
// 2. Arena Allocator: The fundamental memory management component.
//    (Depends only on malloc/free).
#include "core/arena.c"
// 3. OS Executor: Handles OS-level memory management for executable code.
//    (Depends on error handling, debugging utilities).
#include "jit/executor.c"
// 4. Type Registry: Manages named types.
//    (Depends on arena for storage and signature parser for definitions).
#include "core/type_registry.c"
// 5. Signature Parser: Implements the high-level string-based API.
//    (Depends on types, arena, and registry).
#include "core/signature.c"
// 6. Dynamic Library Loader: Implements cross-platform `dlopen`/`dlsym`.
//    (Depends on error handling, types, and arena).
#include "core/loader.c"

infix/src/jit/executor.c  view on Meta::CPAN

#endif

/**
 * @internal
 * @brief Frees a block of executable memory with use-after-free hardening.
 *
 * @details Before freeing the memory, this function first attempts to change the
 * memory protection to be inaccessible (`PROT_NONE` or `PAGE_NOACCESS`). This
 * creates a "guard page" that will cause an immediate, safe crash if a dangling
 * pointer to the freed trampoline is ever used, making use-after-free bugs
 * much easier to detect and debug.
 *
 * @param exec The executable memory block to free.
 */
void infix_executable_free(infix_executable_t exec) {
    if (exec.size == 0)
        return;
#if defined(INFIX_OS_WINDOWS)
#if defined(INFIX_ARCH_X64) || defined(INFIX_ARCH_AARCH64)
    if (exec.seh_registration)
        RtlDeleteFunctionTable((PRUNTIME_FUNCTION)exec.seh_registration);

lib/Affix.pm  view on Meta::CPAN

    use Carp                  qw[];
    use Config                qw[%Config];
    use File::Spec::Functions qw[rel2abs canonpath curdir path catdir];
    use File::Basename        qw[basename dirname];
    use File::Find            qw[find];
    use File::Temp            qw[tempdir];
    my $okay = 0;

    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;

lib/Affix.pod  view on Meta::CPAN

    $int_ptr->[0] = 99;

    # 2. Read the memory immediately as an integer value
    my $val = cast($void_ptr, Int); # Returns 99

=head1 POINTER UTILITIES

=head3 C<address( $ptr )>

Returns the virtual memory address of the pointer as a Perl Unsigned Integer (C<UInt64>). Useful for passing addresses
to other FFI libraries or debugging.

    say sprintf("Address: 0x%X", address($ptr));

=head3 C<ptr_add( $ptr, $offset_bytes )>

Returns a new B<unmanaged alias Pin> offset by C<$offset_bytes>.

    my $int_arr   = calloc(10, Int);
    my $next_elem = ptr_add($int_arr, sizeof(Int));

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

    use ExtUtils::MakeMaker;
    use Text::ParseWords;

    class Affix::Build {

        # Public Parameters
        field $os        : param : reader //= $^O;
        field $clean     : param : reader //= 0;
        field $build_dir : param : reader //= Path::Tiny->tempdir( CLEANUP => $clean );
        field $name      : param : reader //= 'affix_lib';
        field $debug     : param : reader //= 0;
        field $version   : param : reader //= ();

        # Global flags applied to all compilations of that type
        # cflags, cxxflags, ldflags, rustflags, etc.
        field $flags : param : reader //= {};

        # Internal State
        field @sources;
        field $libname : reader;
        field $linker  : reader;

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

            elsif ( $l =~ /^(nim)$/ )                   { return '_build_nim'; }
            elsif ( $l =~ /^(d|dlang)$/ )               { return '_build_d'; }
            elsif ( $l =~ /^(swift)$/ )                 { return '_build_swift'; }
            elsif ( $l =~ /^(v|vlang)$/ )               { return '_build_v'; }

            # Fallback to C
            return '_build_c';
        }

        method _run (@cmd) {
            print STDERR "[Affix] Exec: @cmd\n" if $debug;

            # use Data::Dump;
            # ddx \@cmd;
            my ( $stdout, $stderr, $exit ) = capture {
                system @cmd;
            };
            if ( !!$exit ) {
                warn $stdout if $stdout;
                warn $stderr if $stderr;
                my $rc = $exit >> 8;

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

        #[no_mangle]
        pub extern "C" fn add(a: i32, b: i32) -> i32 { a + b }
    RUST

    my $rust_dll = $rust->link();

    # Example 2: Polyglot (C and Go)
    my $mix = Affix::Build->new(
        name      => 'hybrid_lib',
        flags     => { cflags => '-O3', ldflags => '-L/opt/lib' },
        debug     => 1
    );

    # Add file with per-file compiler overrides
    $mix->add('src/wrapper.c', flags => '-DDEBUG');
    $mix->add('src/core.go');

    my $poly_dll = $mix->link();

=head1 DESCRIPTION

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

=over

=item * B<C<name>>: The base name of the resulting library (default: C<'affix_lib'>). The compiler will automatically append the OS-specific extension (C<.dll> on Windows, C<.dylib> on macOS, C<.so> on Linux).

=item * B<C<version>>: Optional version string to append to the library name.

=item * B<C<build_dir>>: The directory where intermediate artifacts and the final library will be written. If not provided, a temporary directory is created via L<Path::Tiny>.

=item * B<C<clean>>: If true, the generated C<build_dir> and all its contents will be deleted when the object is destroyed (default: C<0>).

=item * B<C<debug>>: If true, the exact system commands executed by the compiler will be printed to C<STDERR> (default: C<0>).

=item * B<C<flags>>: A HashRef of global flags applied across all files. Keys can be C<cflags>, C<cxxflags>, and C<ldflags>.

=item * B<C<os>>: Override the detected operating system (defaults to C<$^O>).

=back

=head1 METHODS

=head2 C<add( $input, %args )>

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

            $clibname = 'msvcrt';
        }
        elsif ( $version <= 13 ) {
            $clibname = sprintf( 'msvcr%d', $version * 10 );
        }
        else {
            # CRT not directly loadable (see python/cpython#23606)
            return undef;
        }

        # Check for debug build
        my $debug_suffix = '_d';    # Assuming debug suffix is '_d'
        my $suffixes     = join '|', map quotemeta, @DynaLoader::dl_extensions;
        if ( $debug_suffix =~ /$suffixes/ ) {
            $clibname .= $debug_suffix;
        }
        return "$clibname.dll";
    }

    sub get_msvcrt_version() {
        open( my $pipe, '-|', 'dumpbin /headers msvcrt.dll', 'r' ) or return;
        my $dumpbin_output;
        $dumpbin_output .= $_ while <$pipe>;
        close $pipe;
        return $1 if $dumpbin_output && $dumpbin_output =~ /FileVersion\s+(\d+\.\d+\.\d+\.\d+)/;

lib/Test2/Tools/Affix.pm  view on Meta::CPAN

            $line++;
            $filename =~ s[\\][\\\\]g;    # Windows...
            $opt->spew_utf8(qq[#line $line "$filename"\r\n$name]);
        }
        if ( !$opt ) {
            $c->fail('Failed to locate test source');
            $c->release;
            return ();
        }
        $aggs->{cflags} .= ' -I' . $Inc;
        my $compiler = Affix::Build->new( debug => 0, name => 'testing', version => '1.0', flags => $aggs );
        $compiler->add( $opt->canonpath );
        $compiler->link;
        push @cleanup, $opt->canonpath, $compiler->link unless $keep;
        $c->ok( 1, 'build lib: ' . $compiler->link );
        $c->release;
        $compiler->link;
    }

    sub affix_ok ( $lib, $name, $args, $ret ) {
        my $c = context;



( run in 1.067 second using v1.01-cache-2.11-cpan-39bf76dae61 )