=pod

=encoding utf-8

=head1 NAME

Affix::Build - Polyglot Compiler Frontend for Building FFI Extensions in Everything from Assembly to Zig

=head1 SYNOPSIS

    use v5.40;
    use Affix::Build;
    use Affix;

    # Example 1: Single Language (Rust)
    my $rust = Affix::Build->new( name => 'my_rust_lib' );

    $rust->add( \<<~'RUST', lang => 'rust' );
        #[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

Compile source code from 20+ languages into shared libraries ready for Affix binding. C<Affix::Build> is a
cross-platform compilation utility designed to generate shared libraries (C<.dll>, C<.so>, C<.dylib>) from source code
in over 20 different programming languages.

It abstracts away the complexity of invoking various compilers, normalizing object file extensions, handling
platform-specific linker flags (such as MinGW vs. MSVC on Windows), and ensuring that language runtimes (like the Go GC
or .NET Runtime) are correctly initialized.

It serves as a powerful, polyglot alternative to C<Inline::*> modules. This allows you to write native code in your
language of choice and instantly bind to it from Perl using L<Affix>.

=head2 Build Strategies

The builder automatically picks the right approach based on your sources:

=over

=item 1. B<Native Toolchain Strategy> (Single Language)

If you provide source files for only B<one> language, C<Affix::Build> delegates the entire build process to that
language's native toolchain (e.g., C<go build -buildmode=c-shared>, C<rustc --crate-type cdylib>, C<dotnet publish>).
This ensures that the language's standard library and runtime environment are configured exactly as intended by its
maintainers.

=item 2. B<Polyglot Aggregation Strategy> (Mixed Languages)

If you provide source files from B<multiple> languages (e.g., C code calling into a Rust library), the builder switches
to an aggregation strategy:

=over

=item 1. It instructs each language's compiler to output a B<Static Library> (C<.a> / C<.lib>) or B<Object File> (C<.o> / C<.obj>).

=item 2. It aggregates these intermediate artifacts into a unified build context.

=item 3. It invokes the system C/C++ linker (usually C<cc> or C<g++>) to securely link them into a final shared library, bridging their differing ABIs.

=back

=back

=head1 CONSTRUCTOR

Create a new compiler instance:

    my $builder = Affix::Build->new( %params );

=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

Add source files, then compile and link into a shared library:

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

Adds a source file to the build manifest.

    $builder->add( 'path/to/file.c' );
    $builder->add( 'file.c', flags => ['-DDEBUG', '-Wall'] );
    $builder->add( \"int main(){}", lang => 'c' );

=over

=item * B<File Path:> If C<$input> is a string, it is treated as a file path. The compiler auto-detects the language based on the extension.

=item * B<Inline Code:> If C<$input> is a SCALAR reference, you B<must> provide the C<lang> argument (e.g., C<'c'>, C<'rust'>) so the compiler knows how to handle it.

=item * B<C<flags>>: An ArrayRef or space-separated string of compiler flags specific to this source file.

=back

=head2 C<compile_and_link( )>

Performs the build process:

=over

=item 1. Inspects added sources to determine the Build Strategy.

=item 2. Compiles all sources to their appropriate intermediate or final formats.

=item 3. Links the artifacts (if necessary) into a shared library.

=back

Returns a L<Path::Tiny> object pointing to the generated shared library. Dies with a detailed error message (including
STDOUT/STDERR of the failed command) if compilation fails.

=head2 C<link( )>

An alias for C<compile_and_link()>. Safe to call multiple times; it will return the cached library handle if the build
has already completed.

=head1 SUPPORTED LANGUAGES

20+ languages supported. The builder looks for compilers in your system C<PATH>.

=head3 C / C++

Automatically handles C<-fPIC> on non-Windows platforms.

=over

=item * B<Extensions:> C<.c>, C<.cpp>, C<.cxx>, C<.cc>

=item * B<Known Compilers:> C<cc>, C<gcc>, C<clang>, C<cl> (MSVC), C<c++>, C<g++>, C<icx>.

=back

=head3 Rust

=over

=item * B<Extensions:> C<.rs>

=item * B<Compiler:> C<rustc>

=back

B<Windows Note:> If running under Strawberry Perl (MinGW), you must have the GNU ABI target installed via C<rustup> so
the objects can link against Perl's GCC:

    rustup target add x86_64-pc-windows-gnu

=head3 C# / F#

Uses B<NativeAOT> to compile .NET code directly to unmanaged machine code. Your C# source must tag exported methods
with C<[UnmanagedCallersOnly]>.

=over

=item * B<Extensions:> C<.cs>, C<.fs>

=item * B<Compiler:> C<dotnet> (SDK 8.0 or newer required)

=back

=head3 Go

=over

=item * B<Extensions:> C<.go>

=item * B<Compiler:> C<go> or C<gccgo>

=back

B<Caveats:>

=over

=item * In Polyglot mode, requires C<gccgo> or standard C<go> with C<c-archive> support.

=item * The Go runtime spins up background threads (for GC and scheduling) that do not shut down cleanly when a shared library is unloaded. This can cause access violations on Windows during program exit. If you encounter segmentation faults during global destruction, you can force a safe exit using C<POSIX::_exit(0)> to skip global destructors.

=back

=head3 Zig

=over

=item * B<Extensions:> C<.zig>

=item * B<Compiler:> C<zig>

=back

=head3 Fortran

=over

=item * B<Extensions:> C<.f>, C<.f90>, C<.f95>, C<.for>

=item * B<Compilers:> C<gfortran>, C<ifx>, C<ifort>

=back

=head3 Assembly

Correctly handles ARM64 (AArch64) via the system compiler and x86_64 via NASM.

=over

=item * B<Extensions:> C<.asm> (Intel syntax), C<.s> (AT&T/GNU syntax)

=item * B<Compilers:> C<nasm> (for .asm), System C<cc> (for .s)

=back

=head3 Other Languages

Support is also included for:

=over

=item * B<Odin> (C<.odin>)

=item * B<D> (C<.d>) - via C<dmd>, C<ldc2>, or C<gdc>

=item * B<Nim> (C<.nim>)

=item * B<V> (C<.v>)

=item * B<Swift> (C<.swift>)

=item * B<Pascal> (C<.pas>, C<.pp>) - via C<fpc> (Free Pascal)

=item * B<Crystal> (C<.cr>)

=item * B<Haskell> (C<.hs>) - via C<ghc>

=item * B<Cobol> (C<.cob>, C<.cbl>) - via C<cobc> (GnuCOBOL)

=item * B<OCaml> (C<.ml>) - via C<ocamlopt>

=item * B<Eiffel> (C<.e>) - via C<se> (SmartEiffel)

=item * B<Futhark> (C<.fut>) - Transpiles to C automatically

=back

=head1 PLATFORM NOTES

Platform-specific quirks and workarounds. The Windows/MinGW environment deserves special attention:

=head2 Windows (MinGW / Strawberry Perl)

This environment is notoriously considered "Polyglot Hard Mode." C<Affix::Build> performs several behind-the-scenes
tricks to make it work:

=over

=item 1. B<Rust Compatibility:> Forces C<rustc> to use the C<x86_64-pc-windows-gnu> target to ensure the resulting static libraries can link against Perl's GCC.

=item 2. B<Whole Archive:> When linking static libraries (C<.a>) into a shared library, passes C<-Wl,--whole-archive> to the linker to ensure symbols aren't stripped out prematurely.

=item 3. B<Exports:> Automatically adds C<-Wl,--export-all-symbols> when linking to ensure Fortran/Assembly symbols are visible without explicit decoration.

=back

=head1 AUTHOR

Sanko Robinson - L<https://github.com/sanko>

=head1 COPYRIGHT

Copyright (C) 2023-2026 by Sanko Robinson.

This library is free software; you can redistribute it and/or modify it under the terms of the Artistic License 2.0.

=cut
