Alien-libssh
view release on metacpan or search on metacpan
.claude/skills/alien-libssh-core/SKILL.md view on Meta::CPAN
---
name: alien-libssh-core
description: Use when working on the Alien::libssh distribution â its alienfile, the bundled libssh tarball, the static share build and its OpenSSL/zlib link line, or what the XS consumer Net::LibSSH depends on.
metadata:
type: project
---
# Alien::libssh â core
Force-loaded into every `alien-libssh-*` agent before its first turn; do not
restate it in an agent body. Generic Alien::Build mechanics (probe/share, the
`PkgConfig` plugin, `Test::Alien`, `install_prop` vs `runtime_prop`) live in
skill `perl-alien`; the consumer-side Perl/C boundary in `perl-xs`. This file
holds only what is true about *this* distribution.
## What the distribution is
One job: make libssh available to Perl, over two paths.
- **system** â `plugin 'PkgConfig' => (pkg_name => 'libssh')`. No
`minimum_version`: every current distro libssh is new enough for
`Net::LibSSH`.
- **share** â CMake build of `share/libssh-0.10.6.tar.xz`, no network
(`Fetch::Local` + a `file://` `start_url` built with `Path::Tiny->cwd`).
Offline install is a promise this distribution makes; never add a network
`start_url`, never a second tarball.
`lib/Alien/libssh.pm` is deliberately logic-free: `use parent 'Alien::Base'`,
`$VERSION`, POD. Everything that decides anything is in `alienfile`.
`PkgConfig` does probe *and* gather for both paths â on share it reads the
`libssh.pc` the CMake install drops into the prefix. A hand-rolled `sys` gather
covers only the system path and leaves a share install with empty `cflags`.
## The share build is static, and the .pc lies about it
Three facts an implementer keeps re-discovering:
1. **Out-of-source is mandatory.** libssh's
`MacroEnsureOutOfSourceBuild.cmake` refuses to configure inside the source
tree; `meta->prop->{out_of_source} = 1` makes `Build::CMake` honour that.
The `build` list is *one* cmake command (an array ref) followed by
`%{make}` and `%{make} install` â a flat list is N separate commands.
2. **`-DBUILD_SHARED_LIBS=OFF`.** A shared `libssh.so` in the Alien prefix is
invisible to the runtime linker, so an XS consumer compiles and then dies on
`libssh.so.4: cannot open shared object file`. Static avoids the rpath game.
3. **The generated `libssh.pc` has no `Libs.private`.** Static linking therefore
needs libssh's own dependencies appended: `-lcrypto -lz` (OpenSSL is the
crypto backend, `WITH_ZLIB` defaults on). The `after 'gather'` hook in
`alienfile` adds them to `libs` and `libs_static` â it must run *after*
PkgConfig's gather, or the bare line wins. Missing it looks like
`undefined symbol: EVP_PKEY_CTX_new` at `use` time in the consumer.
CMake switches beyond the plugin's defaults: `CMAKE_BUILD_TYPE=Release`,
`WITH_EXAMPLES=OFF`, `WITH_PCAP=OFF`, `WITH_GSSAPI=OFF`. Share-build
prerequisites on the host: cmake, a C compiler, pkg-config, OpenSSL headers,
zlib headers.
## Consumer
`Net::LibSSH` (`~/dev/p5-net-libssh`, XS) links at build time via
`Alien::libssh->cflags` / `->libs` and pins `Alien::libssh` in both `runtime`
and `configure`. `t/01-alien.t` mirrors that: `alien_ok` + `xs_ok` calling
`ssh_new`/`ssh_free`. Test XS, not FFI â the consumer is XS.
Work that belongs to `Net::LibSSH` becomes a ticket on that repo's karr board,
never an edit from here. Changing the bundled libssh version is its own release
and `Net::LibSSH` gets told.
## Upgrading the bundled libssh
1. `git rm` the old tarball, add the new one under `share/` â one tarball in
the tree.
2. `alienfile`: the `start_url` filename.
3. `Changes`: under `{{$NEXT}}`, which libssh and why.
4. Version strings in `README.md`, `CLAUDE.md` and this skill move together.
5. Run the forced share build (below) before calling it done.
## Verification â both paths, every time
```bash
dzil test # whatever the probe decides here
env ALIEN_INSTALL_TYPE=share dzil test # forced bundled build (~1 min)
env ALIEN_INSTALL_TYPE=system dzil test # forced probe; fails loudly with no system lib
```
`prove -l t` is not a test for an Alien â nothing has been built. An unforced
`dzil test` proves one path at most. Build logs and the gathered `alien.json`
land in `_alien/`; read that file first when a consumer sees flags it did not
expect.
( run in 1.183 second using v1.01-cache-2.11-cpan-5c0b1e786e0 )