=pod

=encoding utf-8

=head1 NAME

Alien::Xrepo::Base - Base class for Alien distributions using Xrepo

=head1 SYNOPSIS

In your subclass C<lib/Alien/Zstd.pm>:

    use v5.40;
    use experimental 'class';
    use Alien::Xrepo::Base;

    class Alien::Zstd : isa(Alien::Xrepo::Base) {
        method pkg_name { 'zstd' }

        # Optional: customize installation
        method install_opts {
            return (
                kind    => 'shared',
                configs => { legacy => 1 }
            );
        }
    }

In your C<Build.PL>:

    use strict;
    use warnings;
    use lib 'lib';
    use Alien::Xrepo::Base;

    # Pass your subclass name to the builder
    Build_PL('Alien::Zstd');

In your C<cpanfile>:

    requires 'perl', '5.40.0';
    requires 'File::ShareDir', '1.00';

    on 'configure' => sub {
        requires 'Alien::Xmake', '0.08';
        requires 'Alien::Xrepo', '0.08';
    };

    on 'build' => sub {
        requires 'ExtUtils::InstallPaths', '0.002';
        requires 'ExtUtils::Install', '2.00';
    };

=head1 DESCRIPTION

C<Alien::Xrepo::Base> is a base class designed to simplify the creation of C<Alien::*> distributions that use C<xrepo>
to manage native dependencies.

It leverages Perl 5.40's native C<class> features and provides a C<Module::Build::Tiny>-compatible build system that:

=over

=item 1. Installs the requested package via C<xrepo> into a C<share> directory during the build phase.

=item 2. Captures all relevant metadata (include dirs, lib dirs, flags, etc.).

=item 3. Generates a C<ConfigData.pm> module for runtime access.

=item 4. Handles portable path resolution both from C<blib> (during development/testing) and from the final installation directory.

=back

=head1 CONSTRUCTOR

=head2 C<new( ... )>

    my $alien = Alien::Zstd->new(
        pkg_name           => 'zstd',
        version_constraint => 'latest',
        root               => '/path/to/project-root',
        verbose            => 1
    );

Creates a new instance. If C<pkg_name> is omitted it is inherited from the subclass C<pkg_name> method.

=over

=item B<pkg_name>

The name of the package as it appears in the C<xrepo> repository (e.g., C<zstd>). If omitted, the value returned by the
subclass C<pkg_name> method is used. One of the two must be provided. May also be an array reference (or list) of
package names to install and expose C<SDL3>-style families of related libraries (see the C<Alien-SDL3> example in
C<eg/examples/>). The first name is the C<primary> package used when no package is selected.

=item B<version_constraint>

Optional semantic version constraint (e.g., C<1.6.x>, C<latest>, C<1.6.32>). Passed to C<Alien::Xrepo::install> and
appended to the C<xrepo> install spec.

=item B<root>

Optional path to an isolated, local installation root for packages and configuration. When set, C<XMAKE_CONFIGDIR> and
C<XMAKE_PKG_INSTALLDIR> point inside this directory so the package does not touch the global C<~/.xmake> environment.

=item B<verbose>

Boolean. If true, prints xrepo status output to C<STDOUT>. Defaults to C<0>.

=back

=head1 SUBCLASSING

To create a new Alien module, inherit from this class and implement the following methods:

=head2 C<pkg_name( )>

B<Required>. Returns the name of the package as it appears in the C<xrepo> repository. May return a scalar or an array
reference / list of names when the module installs and exposes more than one library (for example, a binding to a
family of libraries such as SDL3). When multiple names are given, each is installed and recorded separately; the first
name is the C<primary> package used when no package name is passed to the delegation methods. This mirrors the
C<pkg_name> keyword of C<Alien::Build::Plugin::PkgConfig>.

=head2 C<package_names( )>

Returns the list of package names this module binds to (a single-element list for single-package subclasses).

=head2 C<alt( [$pkg] )>

Returns an object bound to the named package, mirroring C<Alien::Base'>s C<alt> accessor. Without an argument (or with
the primary package name) it returns C<$self>. With a different name it returns a delegate whose C<cflags>, C<libs>,
C<libpath>, C<bin_dir>, C<kind>, C<dynamic_libs>, C<dist_dir>, C<install_type>, C<version>, and related accessors all
refer to that package:

    my $ttf = Alien::SDL3->alt('libsdl3_ttf');
    my $cflags = $ttf->cflags;
    my $libs   = $ttf->libs;

This is the recommended way to access a non-primary package.

=head2 C<package_info( [$pkg] )> and enumerating packages

C<package_names> returns the names the module binds to, and each package's metadata lives in the
L<Alien::Xrepo::PackageInfo> object returned by C<package_info( $name )> (or by the C<alt( $name )> delegate). Drive an
C<install> first so every package is fetched and recorded, then iterate the names to build a report of what was
installed, where, and at what version:

    use Alien::SDL3;

    my $alien = Alien::SDL3->new;
    $alien->install;    # populate/refresh the per-package metadata

    for my $name ( $alien->package_names ) {
        my $alt = $alien->alt($name);
        my $info = $alt->package_info;
        next unless $info;               # package could not be located/installed

        printf "%-16s v%-9s %-8s\n", $name, $info->version, $info->kind;
        printf "  installdir : %s\n", $info->installdir // '(none)';
        printf "  libpath    : %s\n", $info->libpath   // '(none)';
        printf "  dynamic    : %s\n", join( '; ', @{ $info->libfiles // [] } ) || '(none)';
        printf "  includes   : %s\n", join( '; ', @{ $info->includedirs // [] } ) || '(none)';
        printf "  cflags     : %s\n", $alt->cflags;
        printf "  libs       : %s\n", $alt->libs;
    }

B<Note>: C<package_info> (and the delegation methods) return C<undef>/empty for a package that is not installed or
could not be resolved. You can tell a specific package is missing by checking that return value, e.g.
C<Alien::SDL3->alt('libsdl3_ttf')->libpath> is C<undef> (or C<package_info> itself is C<undef>) when C<libsdl3_ttf>
wasn't present. See the C<package_info> entry under L</METHODS>.

=head2 C<install_opts( )>

Optional. Returns a hash (or list) of options honored by the builder (C<Build_PL> / C<Build>) when it installs the
package during the C<build> action. Pass these same options directly to C<install( %opts )> when installing at runtime.
Common options include:

=over 4

=item * C<kind>: 'shared' (default) or 'static'.

=item * C<configs>: A hash reference of package-specific configuration flags.

=item * C<plat>, C<arch>, C<mode>: Platform/Architecture/Mode overrides.

=back

=head1 METHODS

=head2 C<install( %opts )>

Explicitly triggers the installation of the package. This is usually handled automatically by the build system, but can
be called manually at runtime if dynamic fetching is desired.

=head2 C<upgrade( %opts )>

Updates the xrepo repositories and then calls C<install()>.

=head2 C<cflags( [$pkg] )>

Returns the compiler flags (typically C<-I> flags) required to use the library. Pass a package name to get the flags
for that package when the module binds multiple libraries, or use C<alt()>.

=head2 C<cflags_static( [$pkg] )>

Alias for C<cflags()>. Xrepo does not produce a distinct set of static compiler flags, so this is identical.

=head2 C<libs( [$pkg] )>

Returns the linker flags (typically C<-L> and C<-l> flags) required to use the library. Pass a package name to get the
flags for that package when the module binds multiple libraries.

=head2 C<libs_static( [$pkg] )>

Alias for C<libs()>, matching C<Alien::Base> naming.

=head2 C<split_flags( $flags, [$pkg] )>

Splits a C<cflags>/C<libs> string into a list of individual flags (using core C<Text::ParseWords>), mirroring
C<Alien::Base::split_flags>. The package argument is accepted for interface symmetry.

=head2 C<version( [$pkg] )>

Returns the version of the installed package.

=head2 C<libpath( [$pkg] )>

Returns the absolute path to the primary shared library.

=head2 C<ffi_lib( [$pkg] )>

An alias for C<libpath()>, provided for compatibility with C<FFI::Platypus>.

=head2 C<dynamic_libs( [$pkg] )>

Returns the absolute paths of the shared libraries / objects of the package. List context.

=head2 C<bin_dir( [$pkg] )>

Returns a list of directories containing executables.

=head2 C<kind( [$pkg] )>

Returns the type of installation: 'library' or 'binary'.

=head2 C<dist_dir( [$pkg] )>

Returns the directory where the package's files live (its xrepo install root).

=head2 C<install_type( [$pkg] )>

Returns C<'share'> once the package has been installed into the (local) xrepo root, and C<'system'> when nothing is
installed yet. This mirrors the C<install_type> contract of C<Alien::Base>.

=head2 C<find_header( $filename, [$pkg] )>

Searches the package's include directories for the specified header and returns its absolute path.

=head2 C<package_info( [$pkg] )>

Returns the raw L<Alien::Xrepo::PackageInfo> object containing all metadata.

=head1 BUILDER METHODS

These methods are intended for use in C<Build.PL> and the generated C<Build> script.

=head2 C<Build_PL( $alien_class )>

Creates the C<Build> script and stores build parameters. C<$alien_class> should be the name of your subclass.

=head2 C<Build( [$alien_class] )>

The entry point for the C<Build> script. Orchestrates the C<build>, C<test>, C<install>, and C<clean> actions.

=head1 SEE ALSO

L<Alien::Xrepo>, L<Alien::Xmake>, L<xmake|https://xmake.io>, L<xrepo|https://xrepo.xmake.io>

=head1 LICENSE

Copyright (C) Sanko Robinson.

This library is free software; you can redistribute it and/or modify it under the terms found in the Artistic License
2. Other copyrights, terms, and conditions may apply to data transmitted through this module.

=head1 AUTHOR

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

=cut
