Dear All, I maintain a package ('ergm') that exports a C API via /inst/include/ that includes header files with data structures, macros, and inline functions, as well as a number of functions exported in a stubs file (that use R_FindSymbol() and/or R_GetCCallable() to locate the functions in 'ergm') that the linking package #includes to access them.
I've recently had an update stall because a source-compatible change in one of the structs changed the ABI---that is, packages LinkingTo 'ergm' needs to be rebuilt, and one of them was Suggested by 'ergm' precisely in order to test the exported C API. As https://cloud.r-project.org/doc/manuals/r-devel/R-exts.html#Linking-to-native-routines-in-other-packages suggests, LinkingTo packages should either depend on an exact version (which is impractical) or test whether the version matches at runtime. Thus, the questions are: 1. What is the best way to save this information? I have a rudimentary implementation [1], but R already has a mechanism to store the build-time R version since it warns about that; can it be used for packages as well? 2. How to best test the C API without making updates impossible? Obviously, I have my own continuous integration setups, so I could simply disable the tests on CRAN, but there is some benefit to having tests on CRAN as well [2]. That is, should the testing be conditional ABI versions not mismatching, or is this more trouble than it's worth? 3. What should be done in case of a mismatch? 1. Is there a way for 'ergm' to detect when a package LinkingTo it is being loaded? Checking and handling can be put into .onLoad() of the LinkingTo packages, but there is some benefit to putting as little code into the LinkingTo package. However, as far as I can tell, hooks can only be set for specific packages. Is this correct? 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? Thanks in advance, Pavel [1] This can be implemented by defining macros with the ABI version in 'ergm', e.g., ergm_version.h: #define ERGM_API_MAJOR 4 #define ERGM_API_MINOR 8 typedef struct {unsigned int major, minor;} APIVersion; then ergm_stubs.c (#included from every LinkingTo package into a C file): #include "ergm_version.h" APIVersion GetBuiltErgmAPIVersion(void){ return (APIVersion) {ERGM_API_MAJOR, ERGM_API_MINOR}; } Then, at compile time, the macros will be resolved so GetBuiltErgmAPIVersion() will return the build-time 'ergm' version. R_FindSymbol() could be used to obtain a pointer to GetBuiltErgmAPIVersion() from a given package and hence its 'ergm' build version, which can then be returned to R. [2] For example (and this actually happened several versions ago) the package might do something with computational cost that depends on the number of installed packages. ______________________________________________ R-package-devel@r-project.org mailing list https://stat.ethz.ch/mailman/listinfo/r-package-devel