[R-pkg-devel] Best practices for built version checking in packages LinkingTo others?

Ivan Krylov |kry|ov @end|ng |rom d|@root@org
Fri Jan 17 14:50:57 CET 2025


В Fri, 17 Jan 2025 10:21:27 +0000
Pavel Krivitsky via R-package-devel <r-package-devel using r-project.org>
пишет:

>    1. What is the best way to save this information? I have a
>       rudimentary implementation [1]

For now, this is the best way we have. Export the ABI version as an
include-time constant and as a registered callable. The LinkingTo
reverse dependencies can give the include-time constant to the
registered callable to compare the two versions.

I agree that we could do better and introduce an ABIversion: field to
the DESCRIPTION file. R CMD INSTALL would write down the current
ABIversion of every package that the current package is LinkingTo (into
the installed package's DESCRIPTION or Meta/features.rds). Later, the
namespace loader would check the ABI versions and provide an
informative message in case a LinkingTo dependency is updated with an
incremented ABI version. A machine-readable list of ABI-level
dependencies should simplify the lives of the binary package
maintainers as well.

(Related may be a problem with S4, where packages may end up caching
parts of the classes from other packages they depend upon, which also
breaks binary installations but self-heals upon reinstalling from
source [1]. This still happens under R-devel. R could store and check
some sort of hash when caching these classes and either reload them in
full or prompt for the package to be reinstalled.)

>    2. How to best test the C API without making updates impossible?

Would abi-compliance-checker [2] have helped there?

It's not exactly designed for static functions and R_GetCCallable(),
but with abi-dumper -all or -dump-static it is possible to use it to
demonstrate incompatibilities between (for example) Matrix_1.6-1.1 and
Matrix_1.7. With either a separate build that exports the functions
destined for R_RegisterCCallable() or some sort of filter for public
symbols, this tool may help detect ABI incompatibilities early.

>    3. What should be done in case of a mismatch?

It's hard to guarantee anything when the ABI is broken. Maybe it's
completely harmless because your code will know that the new structure
member is not initialised. Maybe the wrong stack layout will crash the
process due to a return address mismatch, dooming the process from the
first function call.

>          1. Is there a way for 'ergm' to detect when a package
> LinkingTo it is being loaded?

With inline functions, you could make your every API call perform ABI
version checking, but that obviously comes with potential performance
problems:

// public header in mypackage
#define MYPACKAGE_ABI_VERSION 2
static R_INLINE SEXP mypackage_frobnicate(SEXP arg) {
 (
  (void (*)(int))R_GetCCallable("mypackage", "check_abi_version")
 )(MYPACKAGE_ABI_VERSION); // signals an error on mismatch
 return (
  (SEXP (*)(SEXP))R_GetCCallable("mypackage", "frobnicate")
 )(arg);
}

>          2. What should happen if a mismatch is detected? Should the
> loading be blocked (e.g., by having .onLoad() throw an error), or
> should it lead to a "use at your own risk" type warning?

The safest option would be to signal an error before any of the
potentially incompatible functions are called. It's probably pragmatic
to provide an "I know what I'm doing, void my warranty" emergency
override.

-- 
Best regards,
Ivan

[1] https://stat.ethz.ch/pipermail/r-devel/2023-August/082769.html

[2] https://lvc.github.io/abi-compliance-checker/



More information about the R-package-devel mailing list