PerlAlien / Alien-Build

Build external dependencies for use in CPAN
16 stars 25 forks source link

RFE: decouple probe & gather in PkgConfig plugin; install gather hook based on invocation in sys or share block #409

Open djerius opened 5 months ago

djerius commented 5 months ago

I'm running into some issues with using the PkgConfig plugin with qhull. The root of this is that qhull uses separate pc files for static and dynamic linking, and because of how some Linux distributions (e.g. Debian 12) link their "system" static libraries, they are unable to be linked against the Perl .so file for an XS module. So, system installs need to use dynamic libraries and share installs should use static.

Unfortunately, the use of distinct pc files (qhull_r for dynamic, qhullstatic_r for static) breaks the assumptions of PkgConfig, which assumes it can get both static and dynamic flags from the same pc file.

Here's the gist of the workflow:

  1. Probe for system qhull of minimum version;
  2. if 1. succeeds, choose qhull_r.pc
  3. if 1. fails, choose qhull_static_r.pc

so, conceptually, this is

(probe)  PkgConfig { pkg_name => 'qhull_r', atleast_version $VERSION }
sys {
  (gather) PkgConfig { pkg_name => 'qhull_r' }
}
share {
  (gather) PkgConfig { pkg_name => 'qhullstatic_r' }
}

But this doesn't work because PkgConfig

  1. Both probes and gathers
  2. Inserts gather commands for both system and share installs (regardless of whether it is called in a system or share block)
  3. Can't be overridden because only the first set of gather commands passed to the gather_* hooks is run.

An alternative approach might be to write it all out explicitly:

probe [ "pkg-config --exists qhull_r --atleast-version=$VERSION" ];

sys {
  gather [
    [ "pkg-config --modversion qhull_r", \'%{.runtime.version}' ],
    [ "pkg-config --cflags     qhull_r", \'%{.runtime.cflags}' ],
    [ "pkg-config --libs       qhull_r", \'%{.runtime.libs}' ],
  ];
}
share {
  gather [
    [ "pkg-config --modversion qhullstatic_r", \'%{.runtime.version}' ],
    [ "pkg-config --cflags     qhullstatic_r", \'%{.runtime.cflags}' ],
    [ "pkg-config --libs       qhullstatic_r", \'%{.runtime.libs}' ],
    [ "pkg-config --modversion qhullstatic_r", \'%{.runtime.version_static}' ],
    [ "pkg-config --cflags     qhullstatic_r", \'%{.runtime.cflags_static}' ],
    [ "pkg-config --libs       qhullstatic_r", \'%{.runtime.libs_static}' ],
  ];
}

But now the code can no longer take advantage of Plugin::PkgConfig::Negotiate[^1], and I'm also replicating all of the code in the PkgConfig plugin.

I think it would be worth considering

  1. having separate probe and gather modes for the PkgConfig plugin;
  2. gather only for the install the plugin is specified for (or both if it is specified outside of a sys or share block)

The first could be accomplished by an additional parameter to the plugin, and each plugin's init would react accordingly. The second could be accomplished by paying attention to the $meta->{phase} attribute (at least if the plugin is instantiated from an alienfile). I don't foresee a downside to this being the default behavior.

[^1]: I note that PkgConfig::Negotiate explicitly refuses to use PkgConfig::CommandLine on Solaris & Windows

djerius commented 5 months ago

Here's my horrible work around. Force a share static install by using the PkgConfig plugin and an impossible exact version:

plugin PkgConfig => ( pkg_name => 'qhullstatic_r', exact_version => 0 );

The probe always fails(🤞), but PkgConfig is now set up to use the proper .pc file for a static share install and I don't have to reimplement its guts. This avoids dealing with the separate .pc files at the cost of not being able to take advantage of a system install.