The latest version of dplyr (currently 1.1.4) does not appear to depend on BH:
> any(tools::package_dependencies("dplyr", recursive = TRUE)[[1L]] == "BH")
[1] FALSE
But we can consider this problem more generally:
If packA is a dependency of packB, then what is the effect of removing packA on the functioning of packB?
There are two straightforward cases:
packA belongs to the recursive Imports
or recursive Depends
of packB. Then packB cannot be loaded if packA is removed.
packA belongs to the Suggests
of packB and not to the LinkingTo
, recursive Imports
, or recursive Depends
of packB. Then packB can be loaded if packA is removed, but functionality conditioned on requireNamespace("packA")
or similar won't work. (If you need that, then don't remove packA.)
The rest of this answer aims to demystify the remaining case:
- packA belongs to the
LinkingTo
of packB and not to the recursive Imports
or recursive Depends
of packB.
If you don't have a working understanding of compilers and linkers or if you've never maintained an R package containing compiled code, then the details might be hard to parse. Sorry ...
Let's start by discussing the difference between two statements:
packB/DESCRIPTION
contains LinkingTo: packA
.
This statement means that packA/include
is added to the preprocessor search path when packB is compiled. Then packA is an install time dependency but not necessarily a load time dependency or a run time dependency.
Source: Writing R Extensions, Package Dependencies
packB links packA.
This statement can mean either of two things:
(a) The symbol table of packB contains entry points defined in some library installed under packA/libs$(R_ARCH)
. Then packA is an install time dependency. If the entry points are defined in a shared library requiring dynamic linking, then packA is also a load time dependency, and it is therefore typical for packB to list packA in its Imports
or Depends
, though IIUC library.dynam
and library.dynam.unload
provide a mechanism for packB to avoid this.
Source: Writing R Extensions, Linking to other packages
(b) packB uses the registration interface of packA, executing R_GetCCallable("packA", "fn")
to get a pointer to a C function named fn
defined and registered by packA. Then packA is a run time dependency and not necessarily an install time dependency or a load time dependency. So packB would list packA in its Suggests
, Imports
, or Depends
and (in the Suggests
case) condition on requireNamespace("packA", ...)
.
Source: Writing R Extensions, Linking to native routines in other packages
Crucially, neither statement implies the other. 1 && !2
is typical where packA interfaces a header-only library like Boost (see BH) or Eigen (see RcppEigen), where there is no library to be linked in the first place. 2 && !1
could happen if packA makes its entry points available to other packages without providing headers containing suitable prototypes. packB would have to provide the prototypes itself and hope that packA doesn't introduce an ABI-breaking change. In practice, we hope that 2 && !1
never happens: packages designed to be linked really ought to provide headers containing correct prototypes, and packages doing the linking ought to include those headers where available.
Returning to the question at hand, i.e., the effect on packB of removing a LinkingTo
dependency packA:
If you have !2(a) && !2(b)
or if you have 2(a) && !2(b)
and the linking is static, then in principle removing packA should not break packB.
If you have 2(a) && !2(b)
and the linking is dynamic, then packB cannot be loaded if packA is removed.
If you have !2(a) && 2(b)
, then packA must belong to the Suggests
, Imports
, or Depends
of packB. This situation is one of the "straightforward cases" already discussed above.
dplyr
uses static linking for whatever it gets fromBH
? – Divorcedplyr
feels like it's probably a little beyond my abilities or at least a really impractical effort. – Nipha