Alien-libssh
view release on metacpan or search on metacpan
.claude/skills/perl-xs/references/build-and-test.md view on Meta::CPAN
```
`make` regenerates the `.c` whenever the `.xs` is newer, so an edit to XS needs a
`make`, not just `prove`. Running `prove -lr t/` without a preceding `make` tests
whatever `blib/` last held â a stale `.so` passing its own old tests is a plausible
and very confusing green run.
## Makefile.PL
```perl
WriteMakefile(
NAME => 'Foo',
LIBS => [ '-lfoo' ], # linker flags
INC => '-I/usr/include/foolib', # compiler include flags
OBJECT => 'Foo$(OBJ_EXT)', # only when the basename differs or several
DEFINE => '-DSOMETHING',
);
```
`OBJECT` defaults to `$(BASEEXT)$(OBJ_EXT)` â the distribution's last name component.
Set it when the `.xs` basename differs from that, or when several object files link
into one extension.
Hardcoded `LIBS`/`INC` are fine for a system library that is genuinely everywhere.
Anything else resolves them at configure time â through an `Alien::*` module
(`perl-alien`) or `ExtUtils::PkgConfig`.
## ppport.h
`ppport.h` is generated by `Devel::PPPort` and backfills newer Perl API onto older
Perls. Regenerate it rather than editing it:
```bash
perl -MDevel::PPPort -e 'Devel::PPPort::WriteFile()' # write/refresh ppport.h
perl ppport.h # audit the .xs files against it
```
The audit run reads the XS sources and reports which API needs a `NEED_` define,
which constructs limit the minimum supported Perl, and what can be simplified.
Each `NEED_foo` must appear **before** `#include "ppport.h"` and exactly once per
compilation unit â it is what makes ppport.h emit a static implementation of that
function:
```c
#define NEED_mg_findext /* mg_findext is 5.14+; this covers older Perls */
#include "ppport.h"
```
## Toolchain versions
xsubpp is `ExtUtils::ParseXS`, versioned independently of Perl and upgradable from
CPAN â so an XS feature gated on an xsubpp version is a *toolchain* requirement, not
a Perl one:
```bash
perl -MExtUtils::ParseXS -e 'print "$ExtUtils::ParseXS::VERSION\n"'
```
A typemap that needs a minimum states `REQUIRE: 3.60` on its first line. Without
that line, a construct like `${type}` expanding `::` to `__` silently produces
uncompilable C on older toolchains.
## Reading the generated C
When a compile error points into generated code, generate it directly and read it:
```bash
perl -MExtUtils::ParseXS -e \
'ExtUtils::ParseXS->new->process_file(filename=>"Foo.xs", output=>"/tmp/gen.c",
typemap=>"typemap")'
```
The `#line` directives map every block back to its source line in the `.xs`, and the
argument-conversion code the typemap produced is right there to compare against what
it was supposed to produce. Keep the file out of the distribution â the `.c` beside
the `.xs` is a build artifact and belongs in `.gitignore`.
## Testing what only XS gets wrong
Perl-level tests cover the API. Three failure modes need their own shape:
**Crashes.** A refcount or lifetime bug segfaults, and a segfault takes `prove` down
instead of failing a test. Run each such scenario in a forked child and assert on
what the child reports:
```perl
use POSIX ();
sub run_in_child {
my ($name, $code, $expect) = @_;
pipe(my $rd, my $wr) or die "pipe: $!";
my $pid = fork() // die "fork: $!";
if (!$pid) { print {$wr} eval { $code->() } // "ERROR|$@"; close $wr; POSIX::_exit(0) }
close $wr;
chomp(my $got = <$rd> // '');
waitpid $pid, 0;
is $got, $expect, $name; # a child killed by SIGSEGV reports nothing at all
}
```
Cover every way of losing a variable, not just the one that reads naturally:
out of scope, `undef $x`, and `$x = something_else` fail differently.
**Leaks.** `Test::LeakTrace`'s `no_leaks_ok { ⦠}` runs the block twice and compares
the SV count. It sees Perl-level leaks; C-level ones (a `malloc` never freed behind
a still-collected SV) need valgrind:
```bash
PERL_DESTRUCT_LEVEL=2 valgrind --leak-check=full perl -Mblib t/03-whatever.t
```
`PERL_DESTRUCT_LEVEL=2` makes Perl free its own arenas at exit, so what valgrind
still reports is yours.
**Blocking.** A C call that hangs turns a failing test into a hanging suite. Guard
any call that can block on the network with `alarm`, so the failure mode is a
reported failure rather than a CI job that runs until the timeout.
For debugging symbols, rebuild with `perl Makefile.PL OPTIMIZE='-g -O0'`.
( run in 0.741 second using v1.01-cache-2.11-cpan-302cb4679cc )