Can I remove LinkingTo dependencies after installation of an R package?
Asked Answered
N

2

6

Junior R developer here. I'm not really completely clear on the meaning of the LinkingTo field in an R package's dependencies.

What I would like to do is remove the package BH after the installation of dplyr. It's listed as a LinkingTo dependency of dplyr. The package takes up 154MB and my use case is very space sensitive. I've tried removing it and haven't seen any negative consequences (at least for the functions of dplyr that I'm using) but I'd like to understand the implications more thoroughly.

I know that Writing R Extensions says

Specifying a package in ‘LinkingTo’ suffices if these are C++ headers containing source code or static linking is done at installation: the packages do not need to be (and usually should not be) listed in the ‘Depends’ or ‘Imports’ fields.

However, this seems to say multiple conflicting things to me. If it's static linking done at install, that suggests to me that LinkingTo package is really only needed while the dependent package is compiling and thereafter any necessary capabilities will be packaged into dependent shared library. The other statement "if these are C++ header containing source code" seems more ambiguous and suggests possible runtime dependencies. Meanwhile, "the packages do not need to be... listed in the ‘Depends’ or ‘Imports’ fields" suggests, given the meaning of Depends and Imports, that these dependencies are not needed at runtime.

So, are the LinkingTo dependencies needed at runtime or only at compile time? Once the dependent package is installed, can I simply remove them to save space?

In well over my head here and hoping someone can provide some advice.

Nipha answered 23/1, 2019 at 20:43 Comment(2)
I suspect the answer is that "it depends". Do you know that dplyr uses static linking for whatever it gets from BH?Divorce
Hmm, I do not. And I'm not sure how to figure that out, either. Digging into what's going on behind the scenes in dplyr feels like it's probably a little beyond my abilities or at least a really impractical effort.Nipha
P
2

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:

  1. 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

  2. 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.

Pohl answered 20/9 at 20:44 Comment(0)
F
0

There is a github issue from around when this question was made that says that in the case of dplyr specifically, you could delete the BH dependency after install, because BH was only used for the headers. (Though it has been removed as a dependency since the time of this original question)

For the more general case, as mentioned by @user2554330, it really would depend on the package.

Freightage answered 20/9 at 19:44 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.