--- name: What pperl already has --- ```{index} single: native module single: %INC; native module ``` # What pperl already has Several hundred modules are built into the pperl binary, in two kinds. About a hundred are compiled in: `Storable`, `Socket`, `Digest::MD5`, `Data::Dumper`, `Time::HiRes`, `POSIX`, `Encode` and friends are not files pperl loads - they are Rust code, and `require` is routed to it. The rest are the Perl modules of the core library, `Test::More`, `File::Spec`, `Getopt::Long` and so on, whose source the binary carries. For both kinds `%INC` records `./pperl-native/` (for example `./pperl-native/Storable.pm`) - it reads like a path, but there is no file behind it. That is invisible and harmless when you are writing Perl. It is a problem when an *installer* is trying to work out what you have. ```{index} single: native module; version probe single: $VERSION; installer probe ``` ## The probe Every installer answers "is this module present, and at what version?" the same way: look for the `.pm` file on the search path, read `$VERSION` out of it. A built-in module has no file. So the probe walks past pperl entirely and finds the answer in the host perl's tree - a different interpreter, with different versions. On the reference machine, five of six modules checked disagreed: | module | pperl provides | host perl says | |----------------|----------------|----------------| | `Digest::MD5` | 2.58 | 2.59 | | `Storable` | 3.41 | 3.37 | | `Socket` | 2.038 | 2.040 | | `Time::HiRes` | 1.9777 | 1.9778 | | `Data::Dumper` | 2.183 | 2.192 | Every consequence of that is wrong, and two of the three are silent. - A dependency on `Storable >= 3.40` reads as unsatisfied, because the host says 3.37. The installer fetches Storable - an XS distribution - to replace a module pperl already has at 3.41. The build then fails, and it fails for a module you never needed. - On a host with no perl at all - a minimal container, or the static build whose entire point is not needing one - every built-in module reads as missing and the same false build is attempted for each. - In the other direction, and this one is silent: a dependency on `Data::Dumper >= 2.190` reads as *satisfied* by the host's 2.192, while pperl actually runs 2.183. ```{index} single: Config::lib_file single: native module; lib_file ``` ## Asking pperl instead pperl answers the question itself. `Config::lib_file` returns the path under which pperl sees a library file, a built-in one first: ```perl use Config; print Config::lib_file("Storable.pm"), "\n"; # ./pperl-native/Storable.pm print Config::lib_file("Foo/Bar.pm"), "\n"; # /home/you/.pperl/lib/perl5/Foo/Bar.pm ``` The path can be opened, so the version can be read from it as from any installed file. A built-in Perl module reads as its source. A compiled-in module that has no Perl source reads as three lines, its package, its `$VERSION` and a true value: ```perl use Config; use ExtUtils::MakeMaker (); print MM->parse_version(Config::lib_file("Storable.pm")), "\n"; # 3.41 ``` The installer that is part of pperl asks exactly this. So do the `ExtUtils::MakeMaker` and `Module::Metadata` pperl carries, which is what a `Makefile.PL` and a `Build.PL` use to check their prerequisites. A built-in module is then simply skipped: no download, no build, no diagnostic to explain. Asked for a module that is built in, `--cpan` says so and stops: ```text pperl: Storable 3.41 is built into pperl; nothing to install ``` :::{note} The built-in path opens, but it answers no file test: `-e`, `-f` and `-r` say no. Code that looks for a module by testing `"$dir/Storable.pm"` for each directory of `@INC` does not find a built-in one. Use `Config::lib_file` there. ::: ```{index} single: native module; version mismatch ``` ## When the built-in version is too old pperl reports the version it actually has. That does not make the version acceptable. If a distribution asks for `Storable >= 3.50` and pperl carries 3.41, the dependency is genuinely unsatisfied, the installer goes after the real distribution, and - Storable being XS - the build fails. That is the honest outcome rather than a bug: the module pperl carries is not the version being asked for. It does mean a built-in module falling behind CPAN can turn into an install failure some way downstream, so a version mismatch is worth checking early when an install fails for no visible reason: ```bash pperl -MStorable -e 'print Storable->VERSION, "\n"' ```