NAME

Alien::Xrepo::Runtime - Consumer class for xrepo-based Alien modules

SYNOPSIS

package Alien::Zstd;
use v5.40;
use experimental 'class';
use Alien::Xrepo::Runtime;

class Alien::Zstd : isa(Alien::Xrepo::Runtime) {
    method recipe {
        return {
            name        => 'Alien-Zstd',
            packages    => [ { name => 'zstd', kind => 'shared' } ],
            local_repos => ['vendor/recipes'],   # optional, used by the build engine
        };
    }
}

1;

# A dist with no extra recipe fields may declare only pkg_name instead:

package Alien::Zstd;
use v5.40;
use experimental 'class';
use Alien::Xrepo::Runtime;

class Alien::Zstd : isa(Alien::Xrepo::Runtime) {
    method pkg_name {
        return [ { name => 'zstd', kind => 'shared' } ];
    }
}

1;

DESCRIPTION

Alien::Xrepo::Runtime is the post-build accessor layer for an xrepo-based Alien. It deliberately avoids the Alien::Base model of generating a per-subclass ::ConfigData module at build time. Instead, package paths are resolved by querying xrepo itself lazily, once per package, cached for the process. This means a fresh checkout of an Alien using this class works perfectly with no build step at all.

Optionally, an xrepo.json recipe instead of an inline pkg_name, alongside a snapshot written by Alien::Xrepo::Build (auto-detected from the dist's share directory, see autodetect_snapshot), makes resolution fully hermetic. The accessors then serve recorded paths and never spawn xrepo. When the build installs packages into the dist's share directory (share_dir), the snapshot records share-relative paths. These are dynamically resolved against the snapshot file's own directory, ensuring the same snapshot serves a source-tree blib build and the installed File::ShareDir tree alike.

Describing the dist once: the recipe()

A distribution that ships extra recipe fields (local_repos, pkg_roots, hooks, defaults) benefits from a single declaration that serves both phases. A subclass may instead declare a recipe method, honored when no recipe/pkg_name constructor argument is supplied (and taking precedence over a pkg_name method). It may return:

The build engine, through Alien::Xrepo::Build, consumes this very same hashref (it may be passed directly as the recipe => ... argument). This guarantees the distribution's description lives in exactly one place and no separate xrepo.json is strictly necessary. A recipe's defaults are treated as ambient install options for every package during dynamic resolution. Because this matches the install profile the build engine derives, dynamic and hermetic resolution always agree.

METHODS

The accessor surface strictly follows Alien::Base conventions. Each accessor takes an optional package name and defaults to the primary (first declared) package.

cflags( [$pkg] )

Include flags (-I...) for the package. Returns an empty string when the package cannot be resolved.

cflags_static( [$pkg] )

Include flags for static linking. Currently acts as an alias of cflags( ) in this release.

libs( [$pkg] )

Link flags (-L... -l...) for the package. Returns an empty string when the package cannot be resolved.

libs_static( [$pkg] )

Link flags for static linking. Currently acts as an alias of libs( ) in this release.

libpath( [$pkg] )

Absolute path to the package's runtime library (DLL/SO/dylib). Returns undef for a header-only package or when resolution fails.

ffi_lib( [$pkg] )

An alias of libpath( ) under the name that Alien::Base build_ffi consumers call.

dynamic_libs( [$pkg] )

Absolute paths to every library file xrepo reported for the package.

bin_dir( [$pkg] )

Executable directories (a tool package's bin). Returns an empty list when the package has none.

prepend_to_path( [$pkg] )

Prepends the package's bin_dir directories to $ENV{PATH} (joined with the platform's path separator, functioning properly on both POSIX and Windows). This lets a consumer spawn the shipped tool (e.g., Exotic::Ninja) without knowing where xrepo installed it. Returns the prepended directories, or an empty list (leaving PATH untouched) when the package has none:

my $ninja = Exotic::Ninja->new;
$ninja->prepend_to_path;
system('ninja', '--version') == 0 or die 'ninja did not run';
version( [$pkg] )

The installed version as reported by xrepo, or undef before resolution.

kind( [$pkg] )

The package kind (library for libraries, binary for tools) as reported by xrepo.

dist_dir( [$pkg] )

The package's install root (where its files live), or undef before resolution.

install_type( [$pkg] )

'share' once the package has resolved, 'system' otherwise. In hermetic snapshot mode, the value recorded in the snapshot is returned unchanged.

alt( $name )

Returns an accessor bound to a non-primary package; calling alt on the primary returns $self. Example: Exotic::SDL3->alt('libsdl3_ttf')->cflags.

find_header( $file, [$pkg] )

Absolute path to a header within the package include directories, or undef if it is not found.

runtime_deps( [$pkg] )

The classified direct runtime dependencies of the package's libpath, as an arrayref of hashrefs with name, path, dir, and scope (store, host, system, or unresolved). Cached per package, and degrades to an empty arrayref when the engine cannot scan (spy engines, failures). This is the diagnostic that turns an otherwise opaque dlopen failure into an explanation: the loader resolves these before the package's own symbols are usable, so a vendored provider that is not on the default search path has to be made findable first.

foreign_runtime_deps( [$pkg] )

The subset of runtime_deps the loader would not find on its own (store, host, and unresolved; system is filtered out). An empty list means every dependency is standard.

runtime_dep_dirs( [$pkg] )

The deduplicated directories from foreign_runtime_deps, i.e. what to append to the loader search path so they resolve.

loader_env_var( [$pkg] )

The environment variable this platform's loader consults for extra directories (PATH, DYLD_LIBRARY_PATH, or LD_LIBRARY_PATH). Delegated to the engine when it can answer.

runtime_dep_report( [$pkg] )

A short human-readable summary of foreign_runtime_deps and the directories to add to loader_env_var, or an empty string when there is nothing to report. Intended for a post-install warning or a test message.

package_info( [$pkg] )

The underlying Alien::Xrepo::PackageInfo, or undef before resolution. Under dynamic resolution it carries the runtime dependency diagnostic (see runtime_deps( )); a loaded snapshot's recorded value is served unchanged and never triggers a fresh scan. Opt out of the dynamic scan with check_runtime_deps => 0.

package_names()

The recipe's package names in declaration order (the first is the primary package).

package_defs()

The recipe's normalized per-package definition hashref.

has_snapshot( )

True when a snapshot was supplied or auto-detected, regardless of whether every package it names resolved. Lets a consumer's tests distinguish a genuinely cold checkout (no snapshot: resolution stays lazy and may legitimately be empty) from a distribution whose snapshot was written but is incomplete.

snapshot_path( )

The path of the snapshot that was loaded, or undef when resolution is dynamic.

snapshot_errors( )

The per-package error map recorded in the loaded snapshot ({ <package> => <message> }), or an empty hashref when there is no snapshot or it recorded no failures. A non-empty map means the build that produced the snapshot failed to install those packages; test suites use it to report why a snapshot is unusable rather than just that a package is missing.

split_flags( $flags, [$pkg] )

Splits a flag string on whitespace and returns the individual flags in a Windows-safe list form. The package argument is accepted for compatibility with other accessors, but the split logic itself does not strictly require it.

CONSTRUCTOR

Alien::Zstd->new(
    pkg_name     => 'zstd',                  # or an arrayref of names / hashref defs
    recipe       => $path_or_recipe,         # alternative to inline pkg_name
    install_opts => { kind => 'shared' },    # ambient flags under every package
    root         => $store_root,
    repo         => $alien_xrepo,            # injectable engine
    cache        => 0,                       # bypass Alien::Xrepo's warm-start cache
    check_runtime_deps => 1,                 # attach discovered runtime deps to package_info
    snapshot     => $file,                   # hermetic data; wins over dynamic fetch
    autodetect_snapshot => 1,                # look in the installed dist's share dir
    verbose      => 1
)

recipe is undef by default; when set, it may be an Alien::Xrepo::Build::Recipe object, a file or directory path to an xrepo.json, or a hashref of recipe fields (name, packages, defaults, pkg_roots, local_repos, hooks).

check_runtime_deps (default 1) controls whether package_info( ... ) attaches the discovered runtime dependencies (see runtime_deps( )). A loaded snapshot is always authoritative and never triggers a fresh scan, so setting it to 0 only affects dynamic resolution.

Subclasses may also declare recipe, pkg_name, and install_opts as methods, which the constructor honors when no constructor argument is supplied. A subclass's recipe method takes precedence over pkg_name.

cache (default 1) is forwarded to the engine created by this class, so passing cache => 0 disables "Cache System" in Alien::Xrepo's on-disk resolution cache for a fully live, one-spawn-per-package consumer. When repo => ... injects an engine directly, that engine's own cache setting is respected instead.

SEE ALSO

Alien::Xrepo::Build, Alien::Xrepo::Build::Recipe, Alien::Xrepo, Alien::Base

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.

AUTHOR

Sanko Robinson https://github.com/sanko