Function to load shared libraries using a platform-portable interface.
Arguments
- libnames
vector of character strings specifying several short library names.
- auto.unload
logical: if
TRUEthen a finalizer is registered that closes the library on garbage collection. Seedynload()for details.- try.load
logical: if
TRUE, attempt candidates indynfind()order until one loads. The loaded handle is immediately closed withdynunload().
Value
dynfind() returns an external pointer (library handle), if search was
successful.
Otherwise, if no library is located, a NULL is returned.
dynfind_explain() returns a data frame with columns libname, source,
candidate, exists, loaded, and resolved_path. loaded is NA for
candidates that were not attempted because an earlier candidate loaded, or
because try.load = FALSE.
Details
dynfind() offers a platform-portable naming interface for loading a
specific shared library.
dynfind_explain() returns the candidate paths that dynfind() would try,
optionally attempts them in the same order, and records the first loadable
candidate. It is intended for diagnosing platform-specific library discovery
failures without keeping a library handle open.
The naming scheme and standard locations of shared libraries are OS-specific.
When loading a shared library dynamically at run-time across platforms via
standard interfaces such as dynload() or dyn.load(),
a platform-test is usually needed to specify the OS-dependant library file
path.
This library name problem is encountered via breaking up the library file path into several abstract components:
By permutation of values in each component and concatenation, a list of
possible file paths can be derived.
dynfind() goes through this list to try opening a library. On the first
success, the search is stopped and the function returns.
Given that the three components location, prefix and suffix are set up
properly on a per OS basis,
the unique identification of a library is given by libname - the short
library name.
For some libraries, multiple ‘short library name’ are needed to make this mechanism work across all major platforms. For example, to load the Standard C Library across major R platforms:
On Windows MSVCRT.dll would be loaded; libc.dylib on Mac OS X;
libc.so.6 on Linux and libc.so on BSD.
Here is a sample list of values for the three other components:
location:/usr/local/lib/,C:/Windows/System32/prefix:lib(common), empty - common on Windowssuffix:.dll(Windows),.so(ELF),.dylib(macOS) and empty - useful for all platforms
The vector of locations is initialized by the dynamic linker search rules,
including environment variables such as PATH on Windows and
LD_LIBRARY_PATH on Unix-flavour systems. If the dynamic linker lookup
fails, dynfind() also checks library directories that belong to the
current R runtime, such as R.home("lib") and R.home("bin"), and common
package-manager library locations such as Homebrew (HOMEBREW_PREFIX,
/opt/homebrew, /usr/local), MacPorts (MACPORTS_PREFIX, /opt/local),
Linuxbrew (/home/linuxbrew/.linuxbrew), Scoop (SCOOP, SCOOP_GLOBAL,
ProgramData/scoop), MSYS2 (MINGW_PREFIX, MSYSTEM_PREFIX,
C:/msys64), vcpkg (VCPKG_ROOT) and conda (CONDA_PREFIX).
On Windows, when dynfind() tries a full DLL path from one of these
directories, it temporarily prepends that directory to PATH for the load
attempt so that sibling transitive DLL dependencies can be resolved.
(The set of hardcoded locations might expand and change within the next minor releases).
The file extension depends on the OS: .dll (Windows), .dylib (macOS),
.so (all others).
On Mac OS X, the search for a library includes the ‘Frameworks’ folders as well. This happens before the normal library search procedure and uses a slightly different naming pattern in a separate search phase:
The frameworksLocation is a vector of locations such as /System/Library/
and /Library/.
dynfind() loads a library via dynload() passing over the parameter
auto.unload.
See also
See dynload() for details on the loader interface to the OS-specific
dynamic linker.
Examples
diag <- dynfind_explain(c("msvcrt", "m", "m.so.6"), try.load = FALSE)
head(diag)
#> dynfind candidates; load attempts were not run:
#> libname source candidate exists loaded resolved_path
#> msvcrt loader libmsvcrt.so FALSE NA <NA>
#> msvcrt loader libmsvcrt FALSE NA <NA>
#> msvcrt loader msvcrt FALSE NA <NA>
#> m loader libm.so FALSE NA <NA>
#> m loader libm FALSE NA <NA>
#> m loader m FALSE NA <NA>