| # File src/library/base/R/library.R |
| # Part of the R package, https://www.R-project.org |
| # |
| # Copyright (C) 1995-2018 The R Core Team |
| # |
| # This program is free software; you can redistribute it and/or modify |
| # it under the terms of the GNU General Public License as published by |
| # the Free Software Foundation; either version 2 of the License, or |
| # (at your option) any later version. |
| # |
| # This program is distributed in the hope that it will be useful, |
| # but WITHOUT ANY WARRANTY; without even the implied warranty of |
| # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| # GNU General Public License for more details. |
| # |
| # A copy of the GNU General Public License is available at |
| # https://www.R-project.org/Licenses/ |
| |
| ## Usage removed in 3.6.0 |
| ## testPlatformEquivalence <- |
| ## function(built, run) |
| ## { |
| ## ## args are "cpu-vendor-os", but os might be 'linux-gnu'! |
| ## ## remove vendor field |
| ## built <- gsub("([^-]*)-([^-]*)-(.*)", "\\1-\\3", built) |
| ## run <- gsub("([^-]*)-([^-]*)-(.*)", "\\1-\\3", run) |
| ## ## macOS supports multiple CPUs by using 'universal' binaries |
| ## if (startsWith(built, "universal-darwin") && nzchar(.Platform$r_arch)) |
| ## built <- sub("^universal", R.version$arch, built) |
| ## ## allow for small mismatches, e.g. OS version number and i686 vs i586. |
| ## length(agrep(built, run)) > 0 |
| ## } |
| |
| ## If we want this it would be better to factor out the core of checkConflicts. |
| ## searchConflicts <- function(pkg) { |
| ## vars <- getNamespaceExports(pkg) |
| ## conflicts <- function(pos) intersect(vars, ls(pos, all = TRUE)) |
| ## val <- Filter(length, sapply(search()[-1], conflicts)) |
| ## if (length(val)) val else NULL |
| ## } |
| |
| conflictRules <- |
| local({ |
| data <- new.env() |
| function(pkg, mask.ok = NULL, exclude = NULL) { |
| if ((! missing(mask.ok)) || (! missing(exclude))) |
| assign(pkg, list(mask.ok = mask.ok, exclude = exclude), |
| envir = data) |
| else if (exists(pkg, envir = data, inherits = FALSE)) |
| get(pkg, envir = data, inherits = FALSE) |
| else NULL |
| } |
| }) |
| |
| library <- |
| function(package, help, pos = 2, lib.loc = NULL, character.only = FALSE, |
| logical.return = FALSE, warn.conflicts, |
| quietly = FALSE, verbose = getOption("verbose"), |
| mask.ok, exclude, include.only, |
| attach.required = missing(include.only)) |
| { |
| conf.ctrl <- getOption("conflicts.policy") |
| if (is.character(conf.ctrl)) |
| conf.ctrl <- |
| switch(conf.ctrl, |
| strict = list(error = TRUE, warn = FALSE), |
| depends.ok = list(error = TRUE, |
| generics.ok = TRUE, |
| can.mask = c("base", "methods", "utils", |
| "grDevices", "graphics", |
| "stats"), |
| depends.ok = TRUE), |
| warning(gettextf("unknown conflict policy: %s", |
| sQuote(conf.ctrl)), |
| call. = FALSE, domain = NA)) |
| if (! is.list(conf.ctrl)) |
| conf.ctrl <- NULL |
| stopOnConflict <- isTRUE(conf.ctrl$error) |
| |
| if (missing(warn.conflicts)) |
| warn.conflicts <- if (isFALSE(conf.ctrl$warn)) FALSE else TRUE |
| if ((! missing(include.only)) && (! missing(exclude))) |
| stop(gettext("only one of 'include.only' and 'exclude' can be used"), |
| call. = FALSE, domain = NA) |
| |
| testRversion <- function(pkgInfo, pkgname, pkgpath) |
| { |
| if(is.null(built <- pkgInfo$Built)) |
| stop(gettextf("package %s has not been installed properly\n", |
| sQuote(pkgname)), |
| call. = FALSE, domain = NA) |
| |
| ## which version was this package built under? |
| R_version_built_under <- as.numeric_version(built$R) |
| if(R_version_built_under < "3.0.0") |
| stop(gettextf("package %s was built before R 3.0.0: please re-install it", |
| sQuote(pkgname)), call. = FALSE, domain = NA) |
| |
| current <- getRversion() |
| ## depends on R version? |
| ## as it was installed >= 2.7.0 it will have Rdepends2 |
| if(length(Rdeps <- pkgInfo$Rdepends2)) { |
| for(dep in Rdeps) |
| if(length(dep) > 1L) { |
| target <- dep$version |
| res <- if(is.character(target)) { |
| do.call(dep$op, # these are both strings |
| list(as.numeric(R.version[["svn rev"]]), |
| as.numeric(sub("^r", "", dep$version)))) |
| } else { |
| do.call(dep$op, |
| list(current, as.numeric_version(target))) |
| ## target <- as.numeric_version(dep$version) |
| ## eval(parse(text=paste("current", dep$op, "target"))) |
| } |
| if(!res) |
| stop(gettextf("This is R %s, package %s needs %s %s", |
| current, sQuote(pkgname), dep$op, target), |
| call. = FALSE, domain = NA) |
| } |
| } |
| ## warn if installed under a later version of R |
| if(R_version_built_under > current) |
| warning(gettextf("package %s was built under R version %s", |
| sQuote(pkgname), as.character(built$R)), |
| call. = FALSE, domain = NA) |
| platform <- built$Platform |
| r_arch <- .Platform$r_arch |
| if(.Platform$OS.type == "unix") { |
| ## allow mismatches if r_arch is in use, e.g. |
| ## i386-gnu-linux vs x86-gnu-linux depending on |
| ## build system. |
| ## if(!nzchar(r_arch) && grepl("\\w", platform) && |
| ## !testPlatformEquivalence(platform, R.version$platform)) |
| ## stop(gettextf("package %s was built for %s", |
| ## sQuote(pkgname), platform), |
| ## call. = FALSE, domain = NA) |
| } else { # Windows |
| ## a check for 'mingw' suffices, since i386 and x86_64 |
| ## have DLLs in different places. This allows binary packages |
| ## to be merged. |
| if(nzchar(platform) && !grepl("mingw", platform)) |
| stop(gettextf("package %s was built for %s", |
| sQuote(pkgname), platform), |
| call. = FALSE, domain = NA) |
| } |
| ## if using r_arch subdirs, check for presence |
| if(nzchar(r_arch) |
| && file.exists(file.path(pkgpath, "libs")) |
| && !file.exists(file.path(pkgpath, "libs", r_arch))) |
| stop(gettextf("package %s is not installed for 'arch = %s'", |
| sQuote(pkgname), r_arch), |
| call. = FALSE, domain = NA) |
| } |
| |
| checkNoGenerics <- function(env, pkg) |
| { |
| nenv <- env |
| ns <- .getNamespace(as.name(pkg)) |
| if(!is.null(ns)) nenv <- asNamespace(ns) |
| if (exists(".noGenerics", envir = nenv, inherits = FALSE)) |
| TRUE |
| else { |
| ## A package will have created a generic |
| ## only if it has created a formal method. |
| !any(startsWith(names(env), ".__T")) |
| } |
| } |
| |
| ## FIXME: ./attach.R 's attach() has *very* similar checkConflicts(), keep in sync |
| checkConflicts <- function(package, pkgname, pkgpath, nogenerics, env) |
| { |
| dont.mind <- c("last.dump", "last.warning", ".Last.value", |
| ".Random.seed", ".Last.lib", ".onDetach", |
| ".packageName", ".noGenerics", ".required", |
| ".no_S3_generics", ".Depends", ".requireCachedGenerics") |
| sp <- search() |
| lib.pos <- which(sp == pkgname) |
| ## ignore generics not defined for the package |
| ob <- names(as.environment(lib.pos)) |
| if(!nogenerics) { |
| ## Exclude generics that are consistent with implicit generic |
| ## from another package. A better test would be to move this |
| ## down into the loop and test against specific other package name |
| ## but subtle conflicts like that are likely to be found elsewhere |
| these <- ob[startsWith(ob,".__T__")] |
| gen <- gsub(".__T__(.*):([^:]+)", "\\1", these) |
| from <- gsub(".__T__(.*):([^:]+)", "\\2", these) |
| gen <- gen[from != package] |
| ob <- ob[!(ob %in% gen)] |
| } |
| |
| ipos <- seq_along(sp)[-c(lib.pos, |
| match(c("Autoloads", "CheckExEnv"), sp, 0L))] |
| cpos <- NULL |
| conflicts <- vector("list", 0) |
| for (i in ipos) { |
| obj.same <- match(names(as.environment(i)), ob, nomatch = 0L) |
| if (any(obj.same > 0)) { |
| same <- ob[obj.same] |
| same <- same[!(same %in% dont.mind)] |
| Classobjs <- which(startsWith(same,".__")) |
| if(length(Classobjs)) same <- same[-Classobjs] |
| ## report only objects which are both functions or |
| ## both non-functions. |
| same.isFn <- function(where) |
| vapply(same, exists, NA, |
| where = where, mode = "function", inherits = FALSE) |
| same <- same[same.isFn(i) == same.isFn(lib.pos)] |
| ## if a package imports and re-exports, there's no problem |
| not.Ident <- function(ch, TRAFO=identity, ...) |
| vapply(ch, function(.) |
| !identical(TRAFO(get(., i)), |
| TRAFO(get(., lib.pos)), ...), |
| NA) |
| if(length(same)) same <- same[not.Ident(same)] |
| ## if the package is 'base' it cannot be imported and re-exported, |
| ## allow a "copy": |
| if(length(same) && identical(sp[i], "package:base")) |
| same <- same[not.Ident(same, ignore.environment = TRUE)] |
| if(length(same)) { |
| conflicts[[sp[i]]] <- same |
| cpos[sp[i]] <- i |
| } |
| } |
| } |
| if (length(conflicts)) { |
| if (stopOnConflict) { |
| emsg <- "" |
| pkg <- names(conflicts) |
| notOK <- vector("list", 0) |
| for (i in seq_along(conflicts)) { |
| pkgname <- sub("^package:", "", pkg[i]) |
| if (pkgname %in% canMaskEnv$canMask) |
| next |
| same <- conflicts[[i]] |
| if (is.list(mask.ok)) |
| myMaskOK <- mask.ok[[pkgname]] |
| else myMaskOK <- mask.ok |
| |
| ## adjust 'same' for conflict resolution specifications |
| if (isTRUE(myMaskOK)) |
| same <- NULL |
| else if (is.character(myMaskOK)) |
| same <- setdiff(same, myMaskOK) |
| |
| if (length(same)) { |
| notOK[[pkg[i]]] <- same |
| msg <- .maskedMsg(sort(same), pkg = sQuote(pkg[i]), |
| by = cpos[i] < lib.pos) |
| emsg <- paste(emsg, msg, sep = "\n") |
| } |
| } |
| if (length(notOK)) { |
| msg <- gettextf("Conflicts attaching package %s:\n%s", |
| sQuote(package), |
| emsg) |
| stop(errorCondition(msg, |
| package = package, |
| conflicts = conflicts, |
| class = "packageConflictError")) |
| } |
| } |
| if (warn.conflicts) { |
| ## Use separate messages to preserve previous behavior. |
| packageStartupMessage(gettextf("\nAttaching package: %s\n", |
| sQuote(package)), domain = NA) |
| pkg <- names(conflicts) |
| for (i in seq_along(conflicts)) { |
| msg <- .maskedMsg(sort(conflicts[[i]]), |
| pkg = sQuote(pkg[i]), |
| by = cpos[i] < lib.pos) |
| packageStartupMessage(msg, domain = NA) |
| } |
| } |
| } |
| } |
| |
| if(verbose && quietly) |
| message("'verbose' and 'quietly' are both true; being verbose then ..") |
| if(!missing(package)) { |
| if (is.null(lib.loc)) lib.loc <- .libPaths() |
| ## remove any non-existent directories |
| lib.loc <- lib.loc[dir.exists(lib.loc)] |
| |
| if(!character.only) |
| package <- as.character(substitute(package)) |
| if(length(package) != 1L) |
| stop("'package' must be of length 1") |
| if(is.na(package) || (package == "")) |
| stop("invalid package name") |
| |
| pkgname <- paste0("package:", package) |
| newpackage <- is.na(match(pkgname, search())) |
| if(newpackage) { |
| ## Check for the methods package before attaching this |
| ## package. |
| ## Only if it is _already_ here do we do cacheMetaData. |
| ## The methods package caches all other pkgs when it is |
| ## attached. |
| |
| ## Too extreme (unfortunately; warning too often): |
| ## pkgpath <- find.package(package, lib.loc, quiet = TRUE, verbose = !quietly) |
| ## 'verbose' here means to warn about packages found more than once |
| pkgpath <- find.package(package, lib.loc, quiet = TRUE, |
| verbose = verbose) |
| if(length(pkgpath) == 0L) { |
| if(length(lib.loc) && !logical.return) |
| stop(packageNotFoundError(package, lib.loc, sys.call())) |
| txt <- if(length(lib.loc)) |
| gettextf("there is no package called %s", sQuote(package)) |
| else |
| gettext("no library trees found in 'lib.loc'") |
| if(logical.return) { |
| warning(txt, domain = NA) |
| return(FALSE) |
| } else stop(txt, domain = NA) |
| } |
| which.lib.loc <- normalizePath(dirname(pkgpath), "/", TRUE) |
| pfile <- system.file("Meta", "package.rds", package = package, |
| lib.loc = which.lib.loc) |
| if(!nzchar(pfile)) |
| stop(gettextf("%s is not a valid installed package", |
| sQuote(package)), domain = NA) |
| pkgInfo <- readRDS(pfile) |
| testRversion(pkgInfo, package, pkgpath) |
| |
| ## The ABI compatibility check is now in loadNamespace |
| ## The licence check is now in loadNamespace |
| ## The check for inconsistent naming is now in find.package |
| |
| if(is.character(pos)) { |
| npos <- match(pos, search()) |
| if(is.na(npos)) { |
| warning(gettextf("%s not found on search path, using pos = 2", |
| sQuote(pos)), domain = NA) |
| pos <- 2 |
| } else pos <- npos |
| } |
| |
| deps <- unique(names(pkgInfo$Depends)) |
| depsOK <- isTRUE(conf.ctrl$depends.ok) |
| if (depsOK) { |
| canMaskEnv <- dynGet("__library_can_mask__", NULL) |
| if (is.null(canMaskEnv)) { |
| canMaskEnv <- new.env() |
| canMaskEnv$canMask <- union("base", conf.ctrl$can.mask) |
| "__library_can_mask__" <- canMaskEnv |
| } |
| canMaskEnv$canMask <- unique(c(package, deps, |
| canMaskEnv$canMask)) |
| } |
| else canMaskEnv <- NULL |
| |
| if (attach.required) |
| .getRequiredPackages2(pkgInfo, quietly = quietly) |
| |
| cr <- conflictRules(package) |
| if (missing(mask.ok)) |
| mask.ok <- cr$mask.ok |
| if (missing(exclude)) |
| exclude <- cr$exclude |
| |
| ## If the namespace mechanism is available and the package |
| ## has a namespace, then the namespace loading mechanism |
| ## takes over. |
| if (packageHasNamespace(package, which.lib.loc)) { |
| if (isNamespaceLoaded(package)) { |
| ## Already loaded. Does the version match? |
| newversion <- as.numeric_version(pkgInfo$DESCRIPTION["Version"]) |
| oldversion <- as.numeric_version(getNamespaceVersion(package)) |
| if (newversion != oldversion) { |
| ## No, so try to unload the previous one |
| tryCatch(unloadNamespace(package), |
| error = function(e) { |
| P <- if(!is.null(cc <- conditionCall(e))) |
| paste("Error in", deparse(cc)[1L], ": ") |
| else "Error : " |
| stop(gettextf("Package %s version %s cannot be unloaded:\n %s", |
| sQuote(package), oldversion, |
| paste0(P, conditionMessage(e),"\n")), |
| domain=NA)}) |
| } |
| } |
| tt <- tryCatch({ |
| attr(package, "LibPath") <- which.lib.loc |
| ns <- loadNamespace(package, lib.loc) |
| env <- attachNamespace(ns, pos = pos, deps, |
| exclude, include.only) |
| }, error = function(e) { |
| P <- if(!is.null(cc <- conditionCall(e))) |
| paste(" in", deparse(cc)[1L]) else "" |
| msg <- gettextf("package or namespace load failed for %s%s:\n %s", |
| sQuote(package), P, conditionMessage(e)) |
| if(logical.return) |
| message(paste("Error:", msg), domain = NA) # returns NULL |
| else stop(msg, call. = FALSE, domain = NA) |
| }) |
| if(logical.return && is.null(tt)) |
| return(FALSE) |
| |
| attr(package, "LibPath") <- NULL |
| { |
| on.exit(detach(pos = pos)) |
| ## If there are S4 generics then the package should |
| ## depend on methods |
| nogenerics <- |
| !.isMethodsDispatchOn() || checkNoGenerics(env, package) |
| if (isFALSE(conf.ctrl$generics.ok) || |
| (stopOnConflict && ! isTRUE(conf.ctrl$generics.ok))) |
| nogenerics <- TRUE ## no silent masking for genrics |
| if(stopOnConflict || |
| (warn.conflicts && # never will with a namespace |
| !exists(".conflicts.OK", envir = env, |
| inherits = FALSE))) |
| checkConflicts(package, pkgname, pkgpath, |
| nogenerics, ns) |
| on.exit() |
| if (logical.return) |
| return(TRUE) |
| else |
| return(invisible(.packages())) |
| } |
| } else |
| stop(gettextf("package %s does not have a namespace and should be re-installed", |
| sQuote(package)), domain = NA) |
| } |
| if (verbose && !newpackage) |
| warning(gettextf("package %s already present in search()", |
| sQuote(package)), domain = NA) |
| |
| } |
| else if(!missing(help)) { |
| if(!character.only) |
| help <- as.character(substitute(help)) |
| pkgName <- help[1L] # only give help on one package |
| pkgPath <- find.package(pkgName, lib.loc, verbose = verbose) |
| docFiles <- c(file.path(pkgPath, "Meta", "package.rds"), |
| file.path(pkgPath, "INDEX")) |
| if(file.exists(vignetteIndexRDS <- |
| file.path(pkgPath, "Meta", "vignette.rds"))) |
| docFiles <- c(docFiles, vignetteIndexRDS) |
| pkgInfo <- vector("list", 3L) |
| readDocFile <- function(f) { |
| if(basename(f) %in% "package.rds") { |
| txt <- readRDS(f)$DESCRIPTION |
| if("Encoding" %in% names(txt)) { |
| to <- if(Sys.getlocale("LC_CTYPE") == "C") "ASCII//TRANSLIT"else "" |
| tmp <- try(iconv(txt, from=txt["Encoding"], to=to)) |
| if(!inherits(tmp, "try-error")) |
| txt <- tmp |
| else |
| warning("'DESCRIPTION' has an 'Encoding' field and re-encoding is not possible", |
| call. = FALSE) |
| } |
| nm <- paste0(names(txt), ":") |
| ## indent might be excessive for long field names. |
| formatDL(nm, txt, indent = max(nchar(nm, "w")) + 3L) |
| } else if(basename(f) %in% "vignette.rds") { |
| txt <- readRDS(f) |
| ## New-style vignette indices are data frames with more |
| ## info than just the base name of the PDF file and the |
| ## title. For such an index, we give the names of the |
| ## vignettes, their titles, and indicate whether PDFs |
| ## are available. |
| ## The index might have zero rows. |
| if(is.data.frame(txt) && nrow(txt)) |
| cbind(basename(gsub("\\.[[:alpha:]]+$", "", |
| txt$File)), |
| paste(txt$Title, |
| paste0(rep.int("(source", NROW(txt)), |
| ifelse(nzchar(txt$PDF), |
| ", pdf", |
| ""), |
| ")"))) |
| else NULL |
| } else |
| readLines(f) |
| } |
| for(i in which(file.exists(docFiles))) |
| pkgInfo[[i]] <- readDocFile(docFiles[i]) |
| y <- list(name = pkgName, path = pkgPath, info = pkgInfo) |
| class(y) <- "packageInfo" |
| return(y) |
| } |
| else { |
| ## library(): |
| if(is.null(lib.loc)) |
| lib.loc <- .libPaths() |
| db <- matrix(character(), nrow = 0L, ncol = 3L) |
| nopkgs <- character() |
| |
| for(lib in lib.loc) { |
| a <- .packages(all.available = TRUE, lib.loc = lib) |
| for(i in sort(a)) { |
| ## All packages installed under 2.0.0 should have |
| ## 'package.rds' but we have not checked. |
| file <- system.file("Meta", "package.rds", package = i, |
| lib.loc = lib) |
| title <- if(nzchar(file)) { |
| txt <- readRDS(file) |
| if(is.list(txt)) txt <- txt$DESCRIPTION |
| ## we may need to re-encode here. |
| if("Encoding" %in% names(txt)) { |
| to <- if(Sys.getlocale("LC_CTYPE") == "C") "ASCII//TRANSLIT" else "" |
| tmp <- try(iconv(txt, txt["Encoding"], to, "?")) |
| if(!inherits(tmp, "try-error")) |
| txt <- tmp |
| else |
| warning("'DESCRIPTION' has an 'Encoding' field and re-encoding is not possible", call.=FALSE) |
| } |
| txt["Title"] |
| } else NA |
| if(is.na(title)) |
| title <- " ** No title available ** " |
| db <- rbind(db, cbind(i, lib, title)) |
| } |
| if(length(a) == 0L) |
| nopkgs <- c(nopkgs, lib) |
| } |
| dimnames(db) <- list(NULL, c("Package", "LibPath", "Title")) |
| if(length(nopkgs) && !missing(lib.loc)) { |
| pkglist <- paste(sQuote(nopkgs), collapse = ", ") |
| msg <- sprintf(ngettext(length(nopkgs), |
| "library %s contains no packages", |
| "libraries %s contain no packages"), |
| pkglist) |
| warning(msg, domain=NA) |
| } |
| |
| y <- list(header = NULL, results = db, footer = NULL) |
| class(y) <- "libraryIQR" |
| return(y) |
| } |
| |
| if (logical.return) |
| TRUE |
| else invisible(.packages()) |
| } ## {library} |
| |
| format.libraryIQR <- |
| function(x, ...) |
| { |
| db <- x$results |
| if(!nrow(db)) return(character()) |
| ## Split according to LibPath, preserving order of libraries. |
| libs <- db[, "LibPath"] |
| libs <- factor(libs, levels = unique(libs)) |
| out <- lapply(split(1 : nrow(db), libs), |
| function(ind) db[ind, c("Package", "Title"), |
| drop = FALSE]) |
| c(unlist(Map(function(lib, sep) { |
| c(gettextf("%sPackages in library %s:\n", sep, sQuote(lib)), |
| formatDL(out[[lib]][, "Package"], |
| out[[lib]][, "Title"])) |
| }, |
| names(out), |
| c("", rep.int("\n", length(out) - 1L)))), |
| x$footer) |
| } |
| |
| print.libraryIQR <- |
| function(x, ...) |
| { |
| s <- format(x) |
| if(!length(s)) { |
| message("no packages found") |
| } else { |
| outFile <- tempfile("RlibraryIQR") |
| writeLines(s, outFile) |
| file.show(outFile, delete.file = TRUE, |
| title = gettext("R packages available")) |
| } |
| invisible(x) |
| } |
| |
| library.dynam <- |
| function(chname, package, lib.loc, verbose = getOption("verbose"), |
| file.ext = .Platform$dynlib.ext, ...) |
| { |
| dll_list <- .dynLibs() |
| |
| if(missing(chname) || !nzchar(chname)) return(dll_list) |
| |
| ## For better error messages, force these to be evaluated. |
| package |
| lib.loc |
| |
| r_arch <- .Platform$r_arch |
| chname1 <- paste0(chname, file.ext) |
| ## it is not clear we should allow this, rather require a single |
| ## package and library. |
| for(pkg in find.package(package, lib.loc, verbose = verbose)) { |
| DLLpath <- if(nzchar(r_arch)) file.path(pkg, "libs", r_arch) |
| else file.path(pkg, "libs") |
| file <- file.path(DLLpath, chname1) |
| if(file.exists(file)) break else file <- "" |
| } |
| if(file == "") |
| if(.Platform$OS.type == "windows") |
| stop(gettextf("DLL %s not found: maybe not installed for this architecture?", sQuote(chname)), domain = NA) |
| else |
| stop(gettextf("shared object %s not found", sQuote(chname1)), |
| domain = NA) |
| ## for consistency with library.dyn.unload: |
| file <- file.path(normalizePath(DLLpath, "/", TRUE), chname1) |
| ind <- vapply(dll_list, function(x) x[["path"]] == file, NA) |
| if(length(ind) && any(ind)) { |
| if(verbose) |
| if(.Platform$OS.type == "windows") |
| message(gettextf("DLL %s already loaded", sQuote(chname1)), |
| domain = NA) |
| else |
| message(gettextf("shared object '%s' already loaded", |
| sQuote(chname1)), domain = NA) |
| return(invisible(dll_list[[ seq_along(dll_list)[ind] ]])) |
| } |
| if(.Platform$OS.type == "windows") { |
| ## Make it possible to find other DLLs in the same place as |
| ## @code{file}, so that e.g. binary packages can conveniently |
| ## provide possibly missing DLL dependencies in this place |
| ## (without having to bypass the default package dynload |
| ## mechanism). Note that this only works under Windows, and a |
| ## more general solution will have to be found eventually. |
| ## |
| ## 2.7.0: there's a more general mechanism in DLLpath=, |
| ## so not clear if this is still needed. |
| PATH <- Sys.getenv("PATH") |
| Sys.setenv(PATH = paste(gsub("/", "\\\\", DLLpath), PATH, sep=";")) |
| on.exit(Sys.setenv(PATH = PATH)) |
| } |
| if(verbose) |
| message(gettextf("now dyn.load(\"%s\") ...", file), domain = NA) |
| dll <- if("DLLpath" %in% names(list(...))) dyn.load(file, ...) |
| else dyn.load(file, DLLpath = DLLpath, ...) |
| .dynLibs(c(dll_list, list(dll))) |
| invisible(dll) |
| } |
| |
| library.dynam.unload <- |
| function(chname, libpath, verbose = getOption("verbose"), |
| file.ext = .Platform$dynlib.ext) |
| { |
| dll_list <- .dynLibs() |
| |
| if(missing(chname) || nchar(chname, "c") == 0L) |
| if(.Platform$OS.type == "windows") |
| stop("no DLL was specified") |
| else |
| stop("no shared object was specified") |
| |
| ## We need an absolute path here, and separators consistent with |
| ## library.dynam |
| libpath <- normalizePath(libpath, "/", TRUE) |
| chname1 <- paste0(chname, file.ext) |
| file <- if(nzchar(.Platform$r_arch)) |
| file.path(libpath, "libs", .Platform$r_arch, chname1) |
| else file.path(libpath, "libs", chname1) |
| |
| pos <- which(vapply(dll_list, function(x) x[["path"]] == file, NA)) |
| if(!length(pos)) |
| if(.Platform$OS.type == "windows") |
| stop(gettextf("DLL %s was not loaded", sQuote(chname1)), |
| domain = NA) |
| else |
| stop(gettextf("shared object %s was not loaded", sQuote(chname1)), |
| domain = NA) |
| |
| if(!file.exists(file)) |
| if(.Platform$OS.type == "windows") |
| stop(gettextf("DLL %s not found", sQuote(chname1)), domain = NA) |
| else |
| stop(gettextf("shared object '%s' not found", sQuote(chname1)), |
| domain = NA) |
| if(verbose) |
| message(gettextf("now dyn.unload(\"%s\") ...", file), domain = NA) |
| dyn.unload(file) |
| .dynLibs(dll_list[-pos]) |
| invisible(dll_list[[pos]]) |
| } |
| |
| require <- |
| function(package, lib.loc = NULL, quietly = FALSE, warn.conflicts, |
| character.only = FALSE, mask.ok, exclude, include.only, |
| attach.required = missing(include.only)) |
| { |
| if(!character.only) |
| package <- as.character(substitute(package)) # allowing "require(eda)" |
| loaded <- paste0("package:", package) %in% search() |
| |
| if (!loaded) { |
| if (!quietly) |
| packageStartupMessage(gettextf("Loading required package: %s", |
| package), domain = NA) |
| value <- tryCatch(library(package, lib.loc = lib.loc, |
| character.only = TRUE, |
| logical.return = TRUE, |
| warn.conflicts = warn.conflicts, |
| quietly = quietly, |
| mask.ok = mask.ok, |
| exclude = exclude, |
| include.only = include.only, |
| attach.required = attach.required), |
| error = function(e) e) |
| if (inherits(value, "error")) { |
| if (!quietly) { |
| msg <- conditionMessage(value) |
| cat("Failed with error: ", |
| sQuote(msg), "\n", file = stderr(), sep = "") |
| .Internal(printDeferredWarnings()) |
| } |
| return(invisible(FALSE)) |
| } |
| if (!value) return(invisible(FALSE)) |
| } else value <- TRUE |
| invisible(value) |
| } |
| |
| .packages <- |
| function(all.available = FALSE, lib.loc = NULL) |
| { |
| if(is.null(lib.loc)) |
| lib.loc <- .libPaths() |
| if(all.available) { |
| ans <- character() |
| for(lib in lib.loc[file.exists(lib.loc)]) { |
| a <- list.files(lib, all.files = FALSE, full.names = FALSE) |
| pfile <- file.path(lib, a, "Meta", "package.rds") |
| ans <- c(ans, a[file.exists(pfile)]) |
| } |
| return(unique(ans)) |
| } ## else |
| s <- search() |
| invisible(.rmpkg(s[substr(s, 1L, 8L) == "package:"])) |
| } |
| |
| path.package <- |
| function(package = NULL, quiet = FALSE) |
| { |
| if(is.null(package)) package <- .packages() |
| if(length(package) == 0L) return(character()) |
| s <- search() |
| searchpaths <- |
| lapply(seq_along(s), function(i) attr(as.environment(i), "path")) |
| searchpaths[[length(s)]] <- system.file() |
| pkgs <- paste0("package:", package) |
| pos <- match(pkgs, s) |
| if(any(m <- is.na(pos))) { |
| if(!quiet) { |
| if(all(m)) |
| stop("none of the packages are loaded") |
| else |
| warning(sprintf(ngettext(as.integer(sum(m)), |
| "package %s is not loaded", |
| "packages %s are not loaded"), |
| paste(package[m], collapse=", ")), |
| domain = NA) |
| } |
| pos <- pos[!m] |
| } |
| unlist(searchpaths[pos], use.names = FALSE) |
| } |
| |
| ## As from 2.9.0 ignore versioned installs |
| find.package <- |
| function(package = NULL, lib.loc = NULL, quiet = FALSE, |
| verbose = getOption("verbose")) |
| { |
| if(is.null(package) && is.null(lib.loc) && !verbose) { |
| ## We only want the paths to the attached packages. |
| return(path.package()) |
| } |
| |
| ## don't waste time looking for the standard packages: |
| ## we know where they are and this can take a significant |
| ## time with 1000+ packages installed. |
| if(length(package) == 1L && |
| package %in% c("base", "tools", "utils", "grDevices", "graphics", |
| "stats", "datasets", "methods", "grid", "parallel", |
| "splines", "stats4", "tcltk", "compiler")) |
| return(file.path(.Library, package)) |
| |
| if(is.null(package)) package <- .packages() |
| if(!length(package)) return(character()) |
| if(use_loaded <- is.null(lib.loc)) |
| lib.loc <- .libPaths() |
| |
| bad <- character() |
| out <- character() |
| |
| for(pkg in package) { |
| paths <- file.path(lib.loc, pkg) |
| paths <- paths[ file.exists(file.path(paths, "DESCRIPTION")) ] |
| if(use_loaded && isNamespaceLoaded(pkg)) { |
| dir <- if (pkg == "base") system.file() |
| else .getNamespaceInfo(asNamespace(pkg), "path") |
| paths <- c(dir, paths) |
| } |
| ## trapdoor for tools:::setRlibs |
| if(length(paths) && |
| file.exists(file.path(paths[1], "dummy_for_check"))) { |
| bad <- c(bad, pkg) |
| next |
| } |
| if(length(paths)) { |
| paths <- unique(paths) |
| valid_package_version_regexp <- |
| .standard_regexps()$valid_package_version |
| db <- lapply(paths, function(p) { |
| ## Note that this is sometimes used for source |
| ## packages, e.g. by promptPackage from package.skeleton |
| pfile <- file.path(p, "Meta", "package.rds") |
| info <- if(file.exists(pfile)) |
| ## this must have these fields to get installed |
| readRDS(pfile)$DESCRIPTION[c("Package", "Version")] |
| else { |
| info <- tryCatch(read.dcf(file.path(p, "DESCRIPTION"), |
| c("Package", "Version"))[1, ], |
| error = identity) |
| if(inherits(info, "error") |
| || (length(info) != 2L) |
| || anyNA(info)) |
| c(Package = NA, Version = NA) # need dimnames below |
| else |
| info |
| } |
| }) |
| db <- do.call("rbind", db) |
| ok <- (apply(!is.na(db), 1L, all) |
| & (db[, "Package"] == pkg) |
| & (grepl(valid_package_version_regexp, db[, "Version"]))) |
| paths <- paths[ok] |
| } |
| |
| if(length(paths) == 0L) { |
| bad <- c(bad, pkg) |
| next |
| } |
| if(length(paths) > 1L) { |
| ## If a package was found more than once ... |
| if(verbose) |
| warning(gettextf("package %s found more than once, using the first from\n %s", |
| sQuote(pkg), |
| paste(dQuote(paths), collapse=",\n ")), |
| domain = NA) |
| paths <- paths[1L] |
| } |
| out <- c(out, paths) |
| } |
| |
| if(!quiet && length(bad)) { |
| if(length(out) == 0L) |
| stop(packageNotFoundError(bad, lib.loc, sys.call())) |
| for(pkg in bad) |
| warning(gettextf("there is no package called %s", sQuote(pkg)), |
| domain = NA) |
| } |
| |
| out |
| } |
| |
| packageNotFoundError <- |
| function(package, lib.loc, call = NULL) { |
| if(length(package) == 1L) |
| msg <- gettextf("there is no package called %s", sQuote(package)) |
| else |
| msg <- paste0(ngettext(length(package), |
| "there is no package called", |
| "there are no packages called"), " ", |
| paste(sQuote(package), collapse = ", ")) |
| errorCondition(msg, package = package, lib.loc = lib.loc, call = call, |
| class = "packageNotFoundError") |
| } |
| |
| format.packageInfo <- |
| function(x, ...) |
| { |
| if(!inherits(x, "packageInfo")) stop("wrong class") |
| vignetteMsg <- |
| gettextf("Further information is available in the following vignettes in directory %s:", |
| sQuote(file.path(x$path, "doc"))) |
| headers <- sprintf("\n%s\n", |
| c(gettext("Description:"), |
| gettext("Index:"), |
| paste(strwrap(vignetteMsg), collapse = "\n"))) |
| formatDocEntry <- function(entry) { |
| if(is.list(entry) || is.matrix(entry)) |
| formatDL(entry, style = "list") |
| else |
| entry |
| } |
| c(gettextf("\n\t\tInformation on package %s", sQuote(x$name)), |
| unlist(lapply(which(!vapply(x$info, is.null, NA)), |
| function(i) |
| c(headers[i], formatDocEntry(x$info[[i]]))))) |
| |
| } |
| |
| print.packageInfo <- |
| function(x, ...) |
| { |
| outFile <- tempfile("RpackageInfo") |
| writeLines(format(x), outFile) |
| file.show(outFile, delete.file = TRUE, |
| title = |
| gettextf("Documentation for package %s", sQuote(x$name))) |
| invisible(x) |
| } |
| |
| .getRequiredPackages <- |
| function(file="DESCRIPTION", lib.loc = NULL, quietly = FALSE, useImports = FALSE) |
| { |
| ## OK to call tools as only used during installation. |
| pkgInfo <- tools:::.split_description(tools:::.read_description(file)) |
| .getRequiredPackages2(pkgInfo, quietly, lib.loc, useImports) |
| invisible() |
| } |
| |
| .getRequiredPackages2 <- |
| function(pkgInfo, quietly = FALSE, lib.loc = NULL, useImports = FALSE) |
| { |
| ### FIXME: utils::packageVersion() should be pushed up here instead |
| .findVersion <- function(pkg, lib.loc = NULL) { |
| pfile <- system.file("Meta", "package.rds", |
| package = pkg, lib.loc = lib.loc) |
| if (nzchar(pfile)) |
| as.numeric_version(readRDS(pfile)$DESCRIPTION["Version"]) |
| ## else NULL |
| } |
| pkgs <- unique(names(pkgInfo$Depends)) |
| pkgname <- pkgInfo$DESCRIPTION["Package"] |
| for(pkg in setdiff(pkgs, "base")) { |
| ## allow for multiple occurrences |
| depends <- pkgInfo$Depends[names(pkgInfo$Depends) == pkg] |
| attached <- paste0("package:", pkg) %in% search() |
| current <- .findVersion(pkg, lib.loc) |
| if(is.null(current)) |
| stop(gettextf("package %s required by %s could not be found", |
| sQuote(pkg), sQuote(pkgname)), |
| call. = FALSE, domain = NA) |
| have_vers <- lengths(depends) > 1L |
| for(dep in depends[have_vers]) { |
| target <- as.numeric_version(dep$version) |
| sufficient <- do.call(dep$op, list(current, target)) |
| if (!sufficient) { |
| if (is.null(lib.loc)) |
| lib.loc <- .libPaths() |
| allV <- lapply(lib.loc, .findVersion, pkg=pkg) |
| versions <- do.call(c, allV[iV <- which(!vapply(allV, is.null, NA))]) |
| sufficient <- vapply(versions, dep$op, logical(1L), target) |
| if (any(sufficient)) { |
| warning(gettextf("version %s of %s masked by %s in %s", |
| versions[which(sufficient)[1L]], |
| sQuote(pkg), |
| current, |
| lib.loc[iV[!sufficient][1L]]), |
| call. = FALSE, domain = NA) |
| } |
| msg <- if (attached) |
| "package %s %s is loaded, but %s %s is required by %s" |
| else |
| "package %s %s was found, but %s %s is required by %s" |
| stop(gettextf(msg, sQuote(pkg), current, dep$op, |
| target, sQuote(pkgname)), |
| call. = FALSE, domain = NA) |
| } |
| } |
| |
| if (!attached) { |
| if (!quietly) |
| packageStartupMessage(gettextf("Loading required package: %s", |
| pkg), domain = NA) |
| library(pkg, character.only = TRUE, logical.return = TRUE, |
| lib.loc = lib.loc, quietly = quietly) || |
| stop(gettextf("package %s could not be loaded", sQuote(pkg)), |
| call. = FALSE, domain = NA) |
| } |
| } |
| if(useImports) { |
| nss <- names(pkgInfo$Imports) |
| for(ns in nss) loadNamespace(ns, lib.loc) |
| } |
| } |
| |
| .expand_R_libs_env_var <- |
| function(x) |
| { |
| v <- paste(R.version[c("major", "minor")], collapse = ".") |
| |
| expand <- function(x, spec, expansion) |
| gsub(paste0("(^|[^%])(%%)*%", spec), |
| sprintf("\\1\\2%s", expansion), x) |
| |
| ## %V => version x.y.z |
| x <- expand(x, "V", v) |
| ## %v => version x.y |
| x <- expand(x, "v", sub("\\.[^.]*$", "", v)) |
| ## %p => platform |
| x <- expand(x, "p", R.version$platform) |
| ## %a => arch |
| x <- expand(x, "a", R.version$arch) |
| ## %o => os |
| x <- expand(x, "o", R.version$os) |
| |
| gsub("%%", "%", x) |
| } |