Alien-Xrepo

 view release on metacpan or  search on metacpan

README.md  view on Meta::CPAN

Takes fetch or closure entries (each with an optional `artifacts.installdir` plus `libfiles`, `linkdirs`, and
`bindirs`) and returns the pair `( \@search_dirs, \@store_dirs )` a caller should offer the loader and use to
recognize "our" paths. [Alien::Xrepo::Runtime](https://metacpan.org/pod/Alien%3A%3AXrepo%3A%3ARuntime) and [Alien::Xrepo::Build](https://metacpan.org/pod/Alien%3A%3AXrepo%3A%3ABuild) use it to turn a dependency closure into
scan arguments.

## `info( ... )`

```perl
my $json = $repo->info( 'zlib', format => 'json' );
my $text = $repo->info( 'libpng' );
my $dot  = $repo->info( 'libpng', depgraph => 1, format => 'dot' );   # dependency graph (Graphviz)
```

Shows package information. Pass `format => 'json'` to receive the decoded data structure (array of hashes),
`format => 'dot'` for a Graphviz DOT dependency graph, or `depgraph => 1` to include the package dependency
tree. A DOT graph can be rendered with `dot -Tpng dep.dot -o dep.png`.

## `scan( [ ... ] )`

```perl
my @installed = $repo->scan( 'libpng' );
my @all       = $repo->scan();
```

Lists installed packages (optionally filtered by a Lua pattern). Returns the output lines as a list.

## `download( ... )`

```perl
$repo->download( 'zlib', undef, outputdir => './dl', shallow => 1 ); # Downloads the latest version
```

Only downloads the package source archives without building them. `outputdir` selects the destination directory
(default `packages`). Supports `force`, `shallow`, and the standard `%options`.

## `import_pkg( ... )`

```perl
$repo->import_pkg( 'zlib', undef, packagedir => './packages' ); # Latest zlib version
$repo->import_pkg( 'libfake', '1.0.x' ); # A particular version of this fake lib
```

Imports pre-downloaded package archives into the local cache. `packagedir` selects the source directory.

## `export( ... )`

```perl
$repo->export( 'zlib', undef, packagedir => './packages', shallow => 1 ); # Export the latest version
```

Exports installed package files for offline use. `packagedir` selects the destination directory.

## `env( [ ..., [ ... ] ] )`

```perl
$repo->env( 'bash', bind => 'zlib' );   # run a program inside the package env
$repo->env( undef, show => 1 );         # only print the environment
```

Sets up the package environment and either prints it (`show`) or executes `$program` (default `shell`) inside it.
`bind` selects which environment config or package to bind, `list` lists global configs, and `add`/`remove` manage
global environment config files.

## `list_repo()`

```perl
my @repos = $repo->list_repo();
```

Lists all configured remote repositories (as output lines).

## `uninstall( ..., [ ... ] )`

```perl
$repo->uninstall( 'zlib' );
$repo->uninstall( 'zl*', all => 1 );
```

Removes the specified package from the local cache. Accepts the same `%options` as `install( ... )`. `all` removes
all matching packages (ignoring extra configs, Lua patterns allowed) and `force` removes addon packages even when they
are still depended upon.

## `search( ..., [ ... ] )`

```perl
$repo->search( $query );
$repo->search( $query, addon => 1 );
```

Runs `xrepo search` and returns the matching packages as a list of `name` or `name-version` tokens (in list context;
the match count in scalar context). Name and version are returned together because a package name may itself contain
`-`, so the boundary cannot be reliably recovered. You can pass a token directly back to `install()` since it is a
valid (possibly versioned) spec. Whole tokens also work with `grep`, so callers can filter on plain names.

The output is captured, so nothing is printed to `STDOUT` by this method. On a failed run (nonzero exit, e.g., a
missing `vcpkg::` namespace) it `warn`s and returns an empty list. `addon` searches the `addons/` sub-repository.

## `clean( [ ... ] )`

```perl
$repo->clean();
$repo->clean( installdir => './store' );   # clean an isolated store
```

Cleans the cached packages and downloads. Pass `installdir` and/or `cachedir` to target an isolated store.

## `add_repo( ... )`

```
$repo->add_repo( $name, $git_url, $branch );
```

Adds a custom `xmake` repository.

## `remove_repo( ... )`

```
$repo->remove_repo( $name );
```

Removes a custom repository.

## `update_repo( [...] )`

```
$repo->update_repo();        # Update all
$repo->update_repo( 'main' ); # Update specific repo
```

Updates the local package lists from the remote repositories.

# Cache System

`install` avoids paying `xmake`'s process-startup cost on every call. Each successful resolution is memorized as a
small JSON record and replayed on the next launch, so a long-lived demo (e.g., ["webui.pl" in eg](https://metacpan.org/pod/eg#webui.pl)) or a build loop running
against the same store starts instantly once a package is resolved.

- **What is stored**

    The raw `fetch --json` output plus the resolved install directory, keyed by a SHA-1 fingerprint of the package spec
    and every option that can move the installed layout (`kind`, `plat`, `arch`, `mode`, `configs`). Config values run
    through the same boolean stringifier as the CLI, so `configs => { shared => true }` and `configs =>
    { shared => 'true' }` share a key.

- **Where it lives**

    A `cache.json` under `<store>/.alien-xmake/` when an instance or per-call store is set (`root` / `installdir`),
    otherwise under `<xmake global dir>/.alien-xmake/` (normally `~/.xmake`), honoring `XMAKE_GLOBALDIR`.

- **Validation**

    A hit is only trusted while its recorded install directory still exists on disk. An entry whose package was
    uninstalled, expired, or otherwise removed disqualifies itself and is pruned on the next save, after which the package
    is reinstalled normally.

- **Bounded**

    Entries are kept Least-Recently-Used with a fixed cap (64), so the file stays a few hundred KB no matter how many
    package/option combinations you touch.

- **Disabling**

    Pass `cache => 0` to the ["new( \[...\] )"](#new) constructor, or as a per-call option to ["`install( ... )`"](#install), to
    force live resolution. [Alien::Xrepo::Runtime](https://metacpan.org/pod/Alien%3A%3AXrepo%3A%3ARuntime) and [Alien::Xrepo::Build](https://metacpan.org/pod/Alien%3A%3AXrepo%3A%3ABuild) accept the same constructor flag and forward
    it to the engine they create.

# Package Info

`install( ... )` and its friends return [Alien::Xrepo::PackageInfo](https://metacpan.org/pod/Alien%3A%3AXrepo%3A%3APackageInfo) objects that carry the results of dependency
resolution. See [Alien::Xrepo::PackageInfo](https://metacpan.org/pod/Alien%3A%3AXrepo%3A%3APackageInfo) for the complete list of attributes and methods.

# Examples

Documentation can be dense; let's look at some real-world demonstrations.

## Full Distributions

Complete, installable `Alien-*`-like examples are found in `eg/examples/`.

Each is a real dist and includes its `Build.PL` ([Alien::Xrepo::MB](https://metacpan.org/pod/Alien%3A%3AXrepo%3A%3AMB)) or `Makefile.PL` ([Alien::Xrepo::MM](https://metacpan.org/pod/Alien%3A%3AXrepo%3A%3AMM)), a
complete recipe in the main module, unit tests, and examples.

### `Exotic::SDL3`

Demonstrates using `Build.PL` ([Module::Build](https://metacpan.org/pod/Module%3A%3ABuild)) to install multiple packages as a single family: the `libsdl3` core
plus the `libsdl3_image`, `libsdl3_ttf`, and `libsdl3_mixer` extensions. Each one is built as a shared library for
quick wrapping with `Affix` or [FFI::Platypus](https://metacpan.org/pod/FFI%3A%3APlatypus).

This dist also carries a local `recipes/` mini `xmake` repo (the `libsdl3_ttf` override) which is registered through
`local_repos` to demonstrate how you can modify how prerequisites are handled; in this instance, we force
`libfreetype` to be built as a shared lib for `libsdl3_ttf`.

### `Exotic::Ninja`

Another [Module::Build](https://metacpan.org/pod/Module%3A%3ABuild)-based example, but this time we install a binary tool: [ninja](https://ninja-build.org/).

### `Exotic::Zlib`

Also uses [Module::Build](https://metacpan.org/pod/Module%3A%3ABuild), but this time we build a **static** `zlib` to demonstrate setting up a toolchain for
[Inline::C](https://metacpan.org/pod/Inline%3A%3AC) or even your XS-based modules.

### `Exotic::SQLite3`

This example demonstrates how you'd define per-package build options.

In this demo, we request a specific toolchain (in this case 'mingw') in the recipe.

### `Exotic::Zstandard`

README.md  view on Meta::CPAN


my $cflags  = $repo->fetch( 'libpng', undef, cflags  => 1 );   # "-Ic:/.../include ..."
my $ldflags = $repo->fetch( 'libpng', undef, ldflags => 1 );   # "-Lc:/.../lib -l... .."
say "CFLAGS:  $cflags";
say "LDFLAGS: $ldflags";
```

### Remove a package from the cache

`uninstall` removes a package from the local store (the same one `install`/`fetch`/`scan` use, or an isolated
`root` you installed into). It accepts the same options as `install`; `all` removes every stored variant, and
`force` removes a package even when others still depend on it. It returns the underlying exit status, so check `$?`:

```perl
use v5.40;
use Alien::Xrepo;

my $repo = Alien::Xrepo->new;

$repo->uninstall('freetype');               # remove one package
$repo->uninstall('zl*', all => 1);          # every variant matching the pattern
$repo->uninstall('fontconfig', force => 1); # remove even if still depended upon
```

Note that this edits the shared store. Unlike a builder's prune, which slims the `share` directory a distribution
ships, `uninstall` frees space in the store you manage yourself.

# Third-party Package Managers

`xrepo` can install from external package managers instead of (or alongside) the official `xmake-repo`. You select
the manager with a package-spec namespace and every `Alien::Xrepo` method takes it verbatim:

```perl
# Vcpkg, Homebrew/Linuxbrew, Conan
my $zlib = $repo->install( 'vcpkg::zlib' );
my $zlib = $repo->install( 'brew::zlib'  );
my $zlib = $repo->install( 'conan::zlib/1.2.11' );

# Pacman (archlinux/msys2), Clib, Dub, Cargo, Conda, apt
$repo->install( 'pacman::libcurl' );
$repo->install( 'dub::log 0.4.3' );
```

Searching and flag fetching work against them too:

```perl
$repo->search( 'vcpkg::pcre' );          # search the vcpkg namespace
my $flags = $repo->fetch( 'conan::zlib/1.2.11', undef, cflags => 1, ldflags => 1 );
```

The installed results (`libpath`, `includedirs`, `links`, ...) are decoded into an [Alien::Xrepo::PackageInfo](https://metacpan.org/pod/Alien%3A%3AXrepo%3A%3APackageInfo)
exactly like any `xmake-repo` package, so your wrapper code does not care where the library came from.

See the `xmake-repo` [integration notes](https://github.com/xmake-io/xrepo-docs/blob/master/getting_started.md) for
the corresponding `add_requires` syntax inside an `xmake` project.

## How third-party namespaces are installed

A package in a third-party namespace (`vcpkg::zlib`) is installed by the _external_ manager, and `xmake`'s
integration does not bootstrap that manager for you: e.g., the `vcpkg` integration raises **`vcpkg not found!`** when
it cannot locate the tool (`find_vcpkgdir` checks the `xmake g --vcpkg` global config, `$VCPKG_ROOT`,
`$VCPKG_INSTALLATION_ROOT`, Homebrew, and the Windows `vcpkg.path.txt` lookup in that order). A recipe that installs
from a third-party namespace therefore lists the **bare tool package** that owns the namespace as well:

```perl
packages => [ { name => 'vcpkg::zlib' }, { name => 'vcpkg' } ]
```

These two names mean different things, and their order in the recipe is not their install order:

- **Recipe order**: (`vcpkg::zlib` then `vcpkg`) is the _consumer_ order. [Alien::Xrepo::Runtime](https://metacpan.org/pod/Alien%3A%3AXrepo%3A%3ARuntime) resolves `libpath`, `find_header`, `cflags`, and `libs` against the _first_ package, so the libr...
- **Install order**: (`vcpkg` then `vcpkg::zlib`) is computed by [Alien::Xrepo::Build](https://metacpan.org/pod/Alien%3A%3AXrepo%3A%3ABuild)'s `_install_order()`: any bare package whose name is the namespace of a later `ns::pkg` entry is installed fi...

Once the bare tool is installed, the engine copies its install dir into the environment of the next namespaced spawn:
for `vcpkg` it sets `VCPKG_ROOT` (and prepends the tool dir to `PATH`), so `xmake`'s `vcpkg` integration finds the
freshly built binary. `vcpkg` then installs the package into its own tree under the tool's install dir (`<vcpkg
root>/installed/<triplet>/`), and the engine rebases those paths so the snapshot stays hermetic under the
distribution's `share` directory exactly like any normal `xmake-repo` package.

Two implementation details make that work and are easy to get wrong (see [Alien::Xrepo::Build](https://metacpan.org/pod/Alien%3A%3AXrepo%3A%3ABuild)):

- `_pkg_installdir` refuses _namespaced_ (`::`) packages. The external manager owns the on-disk layout, so a per-package store path such as `<share>/vcpkg::zlib` is meaningless and the store guard would reject a perfectly valid install. Leaving `inst...
- Environment propagation uses a hash-slice `local`:

    ```
    local @ENV{ keys %env } = values %env if %env;
    ```

    The otherwise-obvious form `local $ENV{$_} = $env{$_} for keys %env` silently binds the _global_ `$_` (not the
    loop variable) in some builds of Perl, so the values never reach the child process.

Also note that the decoded `fetch --json` output for third-party packages is not always a tidy list: `libfiles`,
`bindirs`, `includedirs`, and `linkdirs` may each arrive as a plain string when there is exactly one file or
directory. The engine normalizes those scalars into one-element lists before use.

# SEE ALSO

[https://xrepo.xmake.io](https://xrepo.xmake.io), [https://packages.xmake.io/](https://packages.xmake.io/)

[Alien::Xmake](https://metacpan.org/pod/Alien%3A%3AXmake), [Alien::Xrepo::Build](https://metacpan.org/pod/Alien%3A%3AXrepo%3A%3ABuild), [Alien::Xrepo::Runtime](https://metacpan.org/pod/Alien%3A%3AXrepo%3A%3ARuntime)

[Alien::Xrepo::MB](https://metacpan.org/pod/Alien%3A%3AXrepo%3A%3AMB), [Alien::Xrepo::MM](https://metacpan.org/pod/Alien%3A%3AXrepo%3A%3AMM), [Alien::Xrepo::Build::Dist](https://metacpan.org/pod/Alien%3A%3AXrepo%3A%3ABuild%3A%3ADist)

[Affix](https://metacpan.org/pod/Affix), [Affix::Wrap](https://metacpan.org/pod/Affix%3A%3AWrap), [FFI::Platypus](https://metacpan.org/pod/FFI%3A%3APlatypus), [Inline::C](https://metacpan.org/pod/Inline%3A%3AC)

# 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](https://github.com/sanko)



( run in 1.002 second using v1.01-cache-2.11-cpan-1236ad508bc )