miyagawa / Carmel

CPAN Artifact Repository Manager
Other
138 stars 17 forks source link

"Build artifacts" may not work with modules depending on their initial installation paths #61

Open skaji opened 2 years ago

skaji commented 2 years ago

I think I talked with you a little bit about this :) Anyway I write about this in detail here.

Description

There are modules which depend on their initial installation paths, and they may not work if we change their installation paths. Especially Alien::** family save their initial installation paths in alien.json.

Real problem

Let's say we have the following cpanfile:

requires 'HTML::T5';

Please note that HTML::T5 depends on Alien::TidyHTML5, more precisely, a shared library libtidy.so provided by Alien::TidyHTML5.

Then, it turns out that HTML::T5 does not work with carmel:

❯ carmel install
...

❯ carmel rollout
...

❯ perl -Ilocal/lib/perl5 -MHTML::T5 -E 'say "OK"'
Can't load 'local/lib/perl5/x86_64-linux/auto/HTML/T5/T5.so' for module HTML::T5: libtidy.so.58: cannot open shared object file: No such file or directory at /home/skaji/env/plenv/versions/relocatable-5.34.1.1/lib/5.34.1/XSLoader.pm line 93.
 at local/lib/perl5/x86_64-linux/HTML/T5.pm line 360.
Compilation failed in require.
BEGIN failed--compilation aborted.

HTML::T5 is built with LD_RUN_PATH="/home/skaji/.carmel/5.34.1-x86_64-linux/perl5/lib/perl5/x86_64-linux/auto/share/dist/Alien-TidyHTML5/lib" saved in Alien::TidyHTML5's alien.json, so HTML::T5 only works if we have libtidy.so in /home/skaji/.carmel/5.34.1-x86_64-linux/perl5/lib/perl5/x86_64-linux/auto/share/dist/Alien-TidyHTML5/lib. You can also check this by readelf:

❯ readelf -d local/lib/perl5/x86_64-linux/auto/HTML/T5/T5.so | head -5

Dynamic section at offset 0x2e18 contains 22 entries:
  Tag        Type                         Name/Value
 0x0000000000000001 (NEEDED)             Shared library: [libtidy.so.58]
 0x000000000000001d (RUNPATH)            Library runpath: [/home/skaji/.carmel/5.34.1-x86_64-linux/perl5/lib/perl5/x86_64-linux/auto/share/dist/Alien-TidyHTML5/lib]

What to do

I believe that "Build artifacts" is a right approach for Perl ecosystem. So I think we should encourage CPAN authors to support "Build artifacts" in their modules.

For Alien::** problem described here, in order to support "Build artifacts", we may use$ORIGIN/ @loader_path for RPATH.

miyagawa commented 2 years ago

Thank you for the report!

I knew this could be a problem with loading from blib, but didn't realize this could also be an issue after the rollout.

How do you solve this in cpm? Do you skip using an artifact and rebuild from scratch every time?

For Alien::** problem described here, in order to support "Build artifacts", we may use$ORIGIN/ @loader_path for RPATH.

What does this exactly mean?

I need to pull in @plicease here as an Alien expert :)

skaji commented 2 years ago

How do you solve this in cpm? Do you skip using an artifact and rebuild from scratch every time?

cpm has the same problem, and I did not provide any solutions. For the time being, people can execute cpm with --no-prebuilt option to disable "artifacts".

For Alien::** problem described here, in order to support "Build artifacts", we may use$ORIGIN/ @loader_path for RPATH.

What does this exactly mean?

We may set relative RPATH by using $ORIGIN (linux) and @loader_path (macOS). The relative path of libtidy.so from HTML/T5/T5.so is always the same when rolling out, thus:

❯ chmod +w local/lib/perl5/x86_64-linux/auto/HTML/T5/T5.so

❯ chrpath -r \$ORIGIN/../../share/dist/Alien-TidyHTML5/lib local/lib/perl5/x86_64-linux/auto/HTML/T5/T5.so
local/lib/perl5/x86_64-linux/auto/HTML/T5/T5.so: RUNPATH=/home/skaji/.carmel/5.34.1-x86_64-linux/perl5/lib/perl5/x86_64-linux/auto/share/dist/Alien-TidyHTML5/lib
local/lib/perl5/x86_64-linux/auto/HTML/T5/T5.so: new RUNPATH: $ORIGIN/../../share/dist/Alien-TidyHTML5/lib

❯ perl -Ilocal/lib/perl5 -MHTML::T5 -E 'say "OK"'
OK

Please note that, I changed RPATH by chrpath just for proof of concept, but, actually we should set RPATH $ORIGIN/../../share/dist/Alien-TidyHTML5/lib in the compile time of HTML::T5.

Another solution is to use LD_LIBRARY_PATH environment variable:

❯ LD_LIBRARY_PATH=$PWD/local/lib/perl5/x86_64-linux/auto/share/dist/Alien-TidyHTML5/lib perl -Ilocal/lib/perl5 -MHTML::T5 -E 'say "OK"'
OK

This solution would be better because this can be used with loading modules from "blib".

plicease commented 2 years ago

For Alien::Build based Aliens the recommendation is to use static libraries when possible. When not possible Alien::Role::Dino has some smarts for handling dynamic libs. For Windows this is fine because it updates PATH at runtime, but for Linux and most other Unix it sets the rpath at build time, which won't (currently) be relocatable (this is not a problem unique to Carmel tbh; modern Perls properly built ought to be relocatable as well). Alien::patchelf might be helpful in the future @shawnlaffan I think might actually be using this in practice for some of his aliens, and maybe we can pull some of that tech into AB core?

shawnlaffan commented 2 years ago

Alien::gdal is one example. The code is a bit clunky but works: https://github.com/shawnlaffan/perl-alien-gdal/blob/4d411d3a341cde271e1277a575e73c3eb5ab9687/alienfile#L330 and https://github.com/shawnlaffan/perl-alien-gdal/blob/4d411d3a341cde271e1277a575e73c3eb5ab9687/alienfile#L421