install.R

function(...) {
  
  ## This is the code of the package, put in here by brew
  
  
  bioc_version <- function() {
    bver <- get(
      ".BioC_version_associated_with_R_version",
      envir = asNamespace("tools"),
      inherits = FALSE
    )
    
    if (is.function(bver)) bver() else bver
  }
  
  ## This is mostly from https://bioconductor.org/biocLite.R
  
  #' Deduce the URLs of the BioConductor repositories
  #'
  #' @return A named character vector of the URLs of the
  #' BioConductor repositories, appropriate for the current
  #' R version.
  #'
  #' @export
  
  bioc_install_repos <- function() {
    
    vers <- getRversion()
    biocVers <- bioc_version()
    
    a <- NULL
    
    p <- file.path(Sys.getenv("HOME"), ".R", "repositories")
    if (file.exists(p)) {
      a <- ("tools" %:::% ".read_repositories")(p)
      if (!"BioCsoft" %in% rownames(a)) a <- NULL
    }
    
    if (is.null(a)) {
      p <- file.path(R.home("etc"), "repositories")
      a <- ("tools" %:::% ".read_repositories")(p)
    }
    
    ## add a conditional for Bioc releases occuring WITHIN
    ## a single R minor version. This is so that a user with a
    ## version of R (whose etc/repositories file references the
    ## no-longer-latest URL) and without BiocInstaller
    ## will be pointed to the most recent repository suitable
    ## for their version of R
    if (vers >= "3.2.2" && vers < "3.3.0") {
      ## transitioning to https support; check availability
      con <- file(fl <- tempfile(), "w")
      sink(con, type = "message")
      tryCatch(
        { xx <- close(file("https://bioconductor.org")) },
        error = function(e) { message(conditionMessage(e)) }
      )
      sink(type = "message")
      close(con)
      
      if (!length(readLines(fl))) {
        a[, "URL"] <- sub("^http:", "https:", a[, "URL"])
      }
    }
    if (vers >= "3.5") {
      "3.6"
    } else if (vers >= "3.4") {
      a[, "URL"] <- sub(as.character(biocVers), "3.5", a[, "URL"])
      
    } else if (vers >= "3.3.0") {
      a[, "URL"] <- sub(as.character(biocVers), "3.4", a[, "URL"])
      
    } else if (vers >= "3.2") {
      a[, "URL"] <- sub(as.character(biocVers), "3.2", a[, "URL"])
      
    } else if (vers == "3.1.1") {
      ## R-3.1.1's etc/repositories file at the time of the release
      ## of Bioc 3.0 pointed to the 2.14 repository, but we want
      ## new installations to access the 3.0 repository
      a[, "URL"] <- sub(as.character(biocVers), "3.0", a[, "URL"])
      
    } else if (vers == "3.1.0") {
      ## R-devel points to 2.14 repository
      a[, "URL"] <- sub(as.character(biocVers), "2.14", a[, "URL"])
      
    } else if (vers >= "2.15" && vers < "2.16") {
      a[, "URL"] <- sub(as.character(biocVers), "2.11", a[, "URL"])
      biocVers <- numeric_version("2.11")
    }
    
    repos <- intersect(
      rownames(a),
      c("BioCsoft", "BioCann", "BioCexp", "BioCextra")
    )
    
    structure(a[repos, "URL"], names = repos)
  }
  
  ## A environment to hold which packages are being installed so packages
  ## with circular dependencies can be skipped the second time.
  
  installing <- new.env(parent = emptyenv())
  
  is_root_install <- function() is.null(installing$packages)
  
  exit_from_root_install <- function() installing$packages <- NULL
  
  check_for_circular_dependencies <- function(pkgdir, quiet) {
    pkgdir <- normalizePath(pkgdir)
    pkg <- get_desc_field(file.path(pkgdir, "DESCRIPTION"), "Package")
    
    if (pkg %in% installing$packages) {
      if (!quiet) message("Skipping ", pkg, ", it is already being installed")
      TRUE
      
    } else {
      installing$packages <- c(installing$packages, pkg)
      FALSE
    }
  }
  
  available_packages <- function(repos, type) {
    suppressWarnings(utils::available.packages(utils::contrib.url(repos, type), type = type))
  }
  read_dcf <- function(path) {
    fields <- colnames(read.dcf(path))
    as.list(read.dcf(path, keep.white = fields)[1, ])
  }
  
  write_dcf <- function(path, desc) {
    write.dcf(
      rbind(unlist(desc)),
      file = path,
      keep.white = names(desc),
      indent = 0
    )
  }
  
  get_desc_field <- function(path, field) {
    dcf <- read_dcf(path)
    dcf[[field]]
  }
  # Decompress pkg, if needed
  source_pkg <- function(path, subdir = NULL, before_install = NULL) {
    if (!file.info(path)$isdir) {
      bundle <- path
      outdir <- tempfile(pattern = "remotes")
      dir.create(outdir)
      
      path <- decompress(path, outdir)
    } else {
      bundle <- NULL
    }
    
    pkg_path <- if (is.null(subdir)) path else file.path(path, subdir)
    
    # Check it's an R package
    if (!file.exists(file.path(pkg_path, "DESCRIPTION"))) {
      stop("Does not appear to be an R package (no DESCRIPTION)", call. = FALSE)
    }
    
    # Check configure is executable if present
    config_path <- file.path(pkg_path, "configure")
    if (file.exists(config_path)) {
      Sys.chmod(config_path, "777")
    }
    
    # Call before_install for bundles (if provided)
    if (!is.null(bundle) && !is.null(before_install))
      before_install(bundle, pkg_path)
    
    pkg_path
  }
  
  
  decompress <- function(src, target) {
    stopifnot(file.exists(src))
    
    if (grepl("\\.zip$", src)) {
      my_unzip(src, target)
      outdir <- getrootdir(as.vector(utils::unzip(src, list = TRUE)$Name))
      
    } else if (grepl("\\.tar$", src)) {
      untar(src, exdir = target)
      outdir <- getrootdir(untar(src, list = TRUE))
      
    } else if (grepl("\\.(tar\\.gz|tgz)$", src)) {
      untar(src, exdir = target, compressed = "gzip")
      outdir <- getrootdir(untar(src, compressed = "gzip", list = TRUE))
      
    } else if (grepl("\\.(tar\\.bz2|tbz)$", src)) {
      untar(src, exdir = target, compressed = "bzip2")
      outdir <- getrootdir(untar(src, compressed = "bzip2", list = TRUE))
      
    } else {
      ext <- gsub("^[^.]*\\.", "", src)
      stop("Don't know how to decompress files with extension ", ext,
           call. = FALSE)
    }
    
    file.path(target, outdir)
  }
  
  
  # Returns everything before the last slash in a filename
  # getdir("path/to/file") returns "path/to"
  # getdir("path/to/dir/") returns "path/to/dir"
  getdir <- function(path)  sub("/[^/]*$", "", path)
  
  # Given a list of files, returns the root (the topmost folder)
  # getrootdir(c("path/to/file", "path/to/other/thing")) returns "path/to"
  getrootdir <- function(file_list) {
    slashes <- nchar(gsub("[^/]", "", file_list))
    if (min(slashes) == 0) return("")
    
    getdir(file_list[which.min(slashes)])
  }
  
  my_unzip <- function(src, target, unzip = getOption("unzip")) {
    if (unzip %in% c("internal", "")) {
      return(utils::unzip(src, exdir = target))
    }
    
    args <- paste(
      "-oq", shQuote(src),
      "-d", shQuote(target)
    )
    
    system_check(unzip, args)
  }
  
  #' Find all dependencies of a CRAN or dev package.
  #'
  #' Find all the dependencies of a package and determine whether they are ahead
  #' or behind CRAN. A \code{print()} method identifies mismatches (if any)
  #' between local and CRAN versions of each dependent package; an
  #' \code{update()} method installs outdated or missing packages from CRAN.
  #'
  #' @param packages A character vector of package names.
  #' @param pkgdir path to a package directory, or to a package tarball.
  #' @param dependencies Which dependencies do you want to check?
  #'   Can be a character vector (selecting from "Depends", "Imports",
  #'    "LinkingTo", "Suggests", or "Enhances"), or a logical vector.
  #'
  #'   \code{TRUE} is shorthand for "Depends", "Imports", "LinkingTo" and
  #'   "Suggests". \code{NA} is shorthand for "Depends", "Imports" and "LinkingTo"
  #'   and is the default. \code{FALSE} is shorthand for no dependencies (i.e.
  #'   just check this package, not its dependencies).
  #' @param quiet If \code{TRUE}, suppress output.
  #' @param upgrade If \code{TRUE}, also upgrade any of out date dependencies.
  #' @param repos A character vector giving repositories to use.
  #' @param type Type of package to \code{update}.  If "both", will switch
  #'   automatically to "binary" to avoid interactive prompts during package
  #'   installation.
  #'
  #' @param object A \code{package_deps} object.
  #' @param ... Additional arguments passed to \code{install_packages}.
  #'
  #' @return
  #'
  #' A \code{data.frame} with columns:
  #'
  #' \tabular{ll}{
  #' \code{package} \tab The dependent package's name,\cr
  #' \code{installed} \tab The currently installed version,\cr
  #' \code{available} \tab The version available on CRAN,\cr
  #' \code{diff} \tab An integer denoting whether the locally installed version
  #'   of the package is newer (1), the same (0) or older (-1) than the version
  #'   currently available on CRAN.\cr
  #' }
  #'
  #' @export
  #' @examples
  #' \dontrun{
  #' package_deps("devtools")
  #' # Use update to update any out-of-date dependencies
  #' update(package_deps("devtools"))
  #' }
  
  package_deps <- function(packages, dependencies = NA,
                           repos = getOption("repos"),
                           type = getOption("pkgType")) {
    
    if (identical(type, "both")) {
      type <- "binary"
    }
    
    repos <- fix_repositories(repos)
    cran <- available_packages(repos, type)
    
    deps <- sort(find_deps(packages, cran, top_dep = dependencies))
    
    # Remove base packages
    inst <- utils::installed.packages()
    base <- unname(inst[inst[, "Priority"] %in% c("base", "recommended"), "Package"])
    deps <- setdiff(deps, base)
    
    inst_ver <- unname(inst[, "Version"][match(deps, rownames(inst))])
    cran_ver <- unname(cran[, "Version"][match(deps, rownames(cran))])
    diff <- compare_versions(inst_ver, cran_ver)
    
    structure(
      data.frame(
        package = deps,
        installed = inst_ver,
        available = cran_ver,
        diff = diff,
        stringsAsFactors = FALSE
      ),
      class = c("package_deps", "data.frame"),
      repos = repos,
      type = type
    )
  }
  
  #' \code{local_package_deps} extracts dependencies from a
  #' local DESCRIPTION file.
  #'
  #' @export
  #' @rdname package_deps
  
  local_package_deps <- function(pkgdir = ".", dependencies = NA) {
    pkg <- load_pkg_description(pkgdir)
    
    dependencies <- tolower(standardise_dep(dependencies))
    dependencies <- intersect(dependencies, names(pkg))
    
    parsed <- lapply(pkg[tolower(dependencies)], parse_deps)
    unlist(lapply(parsed, `[[`, "name"), use.names = FALSE)
  }
  
  #' \code{dev_package_deps} lists the status of the dependencies
  #' of a local package.
  #'
  #' @export
  #' @rdname package_deps
  
  dev_package_deps <- function(pkgdir = ".", dependencies = NA,
                               repos = getOption("repos"),
                               type = getOption("pkgType")) {
    
    pkg <- load_pkg_description(pkgdir)
    install_dev_remotes(pkgdir)
    repos <- c(repos, parse_additional_repositories(pkg))
    
    deps <- local_package_deps(pkgdir = pkgdir, dependencies = dependencies)
    
    if (is_bioconductor(pkg)) {
      bioc_repos <- bioc_install_repos()
      
      missing_repos <- setdiff(names(bioc_repos), names(repos))
      
      if (length(missing_repos) > 0)
        repos[missing_repos] <- bioc_repos[missing_repos]
    }
    
    package_deps(deps, repos = repos, type = type)
  }
  
  ## -2 = not installed, but available on CRAN
  ## -1 = installed, but out of date
  ##  0 = installed, most recent version
  ##  1 = installed, version ahead of CRAN
  ##  2 = package not on CRAN
  
  compare_versions <- function(installed, cran) {
    stopifnot(length(installed) == length(cran))
    
    compare_var <- function(i, c) {
      if (is.na(c)) return(c(notcran = 2L))
      if (is.na(i)) return(c(notinst = -2L))
      
      i <- package_version(i)
      c <- package_version(c)
      
      if (i < c) {
        c(outofdate = -1L)
      } else if (i > c) {
        c(aheadofcran = 1L)
      } else {
        c(equal = 0L)
      }
    }
    
    vapply(
      seq_along(installed),
      function(i) compare_var(installed[[i]], cran[[i]]),
      integer(1)
    )
  }
  
  install_dev_remotes <- function(pkgdir = ".", ...) {
    
    pkg <- load_pkg_description(pkgdir)
    if (!has_dev_remotes(pkg)) {
      return()
    }
    
    types <- dev_remote_type(pkg[["remotes"]])
    
    lapply(types, function(type) type$fun(type$repository, ...))
  }
  
  # Parse the remotes field split into pieces and get install_ functions for each
  # remote type
  dev_remote_type <- function(remotes = "") {
    
    if (!nchar(remotes)) {
      return()
    }
    
    dev_packages <- trim_ws(unlist(strsplit(remotes, ",[[:space:]]*")))
    
    parse_one <- function(x) {
      pieces <- strsplit(x, "::", fixed = TRUE)[[1]]
      
      if (length(pieces) == 1) {
        type <- "github"
        repo <- pieces
      } else if (length(pieces) == 2) {
        type <- pieces[1]
        repo <- pieces[2]
      } else {
        stop("Malformed remote specification '", x, "'", call. = FALSE)
      }
      tryCatch(
        fun <- get(x = paste0("install_", tolower(type)), mode = "function"),
        error = function(e) {
          stop(
            "Malformed remote specification '", x, "'",
            ", error: ", conditionMessage(e),
            call. = FALSE
          )
        })
      list(repository = repo, type = type, fun = fun)
    }
    
    lapply(dev_packages, parse_one)
  }
  
  has_dev_remotes <- function(pkg) {
    !is.null(pkg[["remotes"]])
  }
  
  #' @export
  print.package_deps <- function(x, show_ok = FALSE, ...) {
    class(x) <- "data.frame"
    
    ahead <- x$diff > 0L
    behind <- x$diff < 0L
    same_ver <- x$diff == 0L
    
    x$diff <- NULL
    x[] <- lapply(x, format)
    
    if (any(behind)) {
      cat("Needs update -----------------------------\n")
      print(x[behind, , drop = FALSE], row.names = FALSE, right = FALSE)
    }
    
    if (any(ahead)) {
      cat("Not on CRAN ----------------------------\n")
      print(x[ahead, , drop = FALSE], row.names = FALSE, right = FALSE)
    }
    
    if (show_ok && any(same_ver)) {
      cat("OK ---------------------------------------\n")
      print(x[same_ver, , drop = FALSE], row.names = FALSE, right = FALSE)
    }
  }
  
  ## -2 = not installed, but available on CRAN
  ## -1 = installed, but out of date
  ##  0 = installed, most recent version
  ##  1 = installed, version ahead of CRAN
  ##  2 = package not on CRAN
  
  #' @export
  #' @rdname package_deps
  #' @importFrom stats update
  
  update.package_deps <- function(object, ..., quiet = FALSE, upgrade = TRUE) {
    ahead <- object$package[object$diff == 2L]
    if (length(ahead) > 0 && !quiet) {
      message("Skipping ", length(ahead), " packages not available: ",
              paste(ahead, collapse = ", "))
    }
    
    missing <- object$package[object$diff == 1L]
    if (length(missing) > 0 && !quiet) {
      message("Skipping ", length(missing), " packages ahead of CRAN: ",
              paste(missing, collapse = ", "))
    }
    
    if (upgrade) {
      behind <- object$package[object$diff < 0L]
    } else {
      behind <- object$package[is.na(object$installed)]
    }
    if (length(behind) > 0L) {
      install_packages(behind, repos = attr(object, "repos"),
                       type = attr(object, "type"), ...)
    }
    
  }
  
  install_packages <- function(packages, repos = getOption("repos"),
                               type = getOption("pkgType"), ...,
                               dependencies = FALSE, quiet = NULL) {
    if (identical(type, "both"))
      type <- "binary"
    if (is.null(quiet))
      quiet <- !identical(type, "source")
    
    message("Installing ", length(packages), " packages: ",
            paste(packages, collapse = ", "))
    
    safe_install_packages(
      packages,
      repos = repos,
      type = type,
      ...,
      dependencies = dependencies,
      quiet = quiet
    )
  }
  
  find_deps <- function(packages, available = utils::available.packages(),
                        top_dep = TRUE, rec_dep = NA, include_pkgs = TRUE) {
    if (length(packages) == 0 || identical(top_dep, FALSE))
      return(character())
    
    top_dep <- standardise_dep(top_dep)
    rec_dep <- standardise_dep(rec_dep)
    
    top <- tools::package_dependencies(packages, db = available, which = top_dep)
    top_flat <- unlist(top, use.names = FALSE)
    
    if (length(rec_dep) != 0 && length(top_flat) > 0) {
      rec <- tools::package_dependencies(top_flat, db = available, which = rec_dep,
                                         recursive = TRUE)
      rec_flat <- unlist(rec, use.names = FALSE)
    } else {
      rec_flat <- character()
    }
    
    unique(c(if (include_pkgs) packages, top_flat, rec_flat))
  }
  
  standardise_dep <- function(x) {
    if (identical(x, NA)) {
      c("Depends", "Imports", "LinkingTo")
    } else if (isTRUE(x)) {
      c("Depends", "Imports", "LinkingTo", "Suggests")
    } else if (identical(x, FALSE)) {
      character(0)
    } else if (is.character(x)) {
      x
    } else {
      stop("Dependencies must be a boolean or a character vector", call. = FALSE)
    }
  }
  
  #' Update packages that are missing or out-of-date.
  #'
  #' Works similarly to \code{\link[utils]{install.packages}} but doesn't install packages
  #' that are already installed, and also upgrades out dated dependencies.
  #'
  #' @param packages Character vector of packages to update.
  #' @inheritParams package_deps
  #' @seealso \code{\link{package_deps}} to see which packages are out of date/
  #'   missing.
  #' @export
  #' @examples
  #' \dontrun{
  #' update_packages("ggplot2")
  #' update_packages(c("plyr", "ggplot2"))
  #' }
  
  update_packages <- function(packages, dependencies = NA,
                              repos = getOption("repos"),
                              type = getOption("pkgType")) {
    pkgs <- package_deps(packages, repos = repos, type = type)
    update(pkgs)
  }
  
  has_additional_repositories <- function(pkg) {
    "additional_repositories" %in% names(pkg)
  }
  
  parse_additional_repositories <- function(pkg) {
    if (has_additional_repositories(pkg)) {
      strsplit(pkg[["additional_repositories"]], "[,[:space:]]+")[[1]]
    }
  }
  
  fix_repositories <- function(repos) {
    if (length(repos) == 0)
      repos <- character()
    
    # Override any existing default values with the cloud mirror
    # Reason: A "@CRAN@" value would open a GUI for choosing a mirror
    repos[repos == "@CRAN@"] <- "http://cloud.r-project.org"
    repos
  }
  
  has_devel <- function() {
    tryCatch(
      has_devel2(),
      error = function(e) FALSE
    )
  }
  
  ## This is similar to devtools:::has_devel(), with some
  ## very minor differences.
  
  has_devel2 <- function() {
    foo_path <- file.path(tempfile(fileext = ".c"))
    
    cat("void foo(int *bar) { *bar=1; }\n", file = foo_path)
    on.exit(unlink(foo_path))
    
    R(c("CMD", "SHLIB", basename(foo_path)), dirname(foo_path))
    dylib <- sub("\\.c$", .Platform$dynlib.ext, foo_path)
    on.exit(unlink(dylib), add = TRUE)
    
    dll <- dyn.load(dylib)
    on.exit(dyn.unload(dylib), add = TRUE)
    
    stopifnot(.C(dll$foo, 0L)[[1]] == 1L)
    TRUE
  }
  
  missing_devel_warning <- function(pkgdir) {
    pkgname <- tryCatch(
      get_desc_field(file.path(pkgdir, "DESCRIPTION"), "Package"),
      error = function(e) NULL
    ) %||% "<unknown>"
    
    sys <- sys_type()
    
    warning(
      "Package ",
      pkgname,
      " has compiled code, but no suitable ",
      "compiler(s) were found. Installation will likely fail.\n  ",
      if (sys == "windows") "Install Rtools and make sure it is in the PATH.",
      if (sys == "macos") "Install XCode and make sure it works.",
      if (sys == "linux") "Install compilers via your Linux package manager."
    )
  }
  
  R <- function(args, path = tempdir()) {
    
    r <- file.path(R.home("bin"), "R")
    
    args <- c(
      "--no-site-file", "--no-environ", "--no-save",
      "--no-restore", "--quiet",
      args
    )
    
    system_check(r, args, path = path)
  }
  
  #' @importFrom utils compareVersion
  
  download <- function(path, url, auth_token = NULL, basic_auth = NULL,
                       quiet = TRUE) {
    
    real_url <- url
    
    if (!is.null(basic_auth)) {
      str <- paste0("://", basic_auth$user, ":", basic_auth$password, "@")
      real_url <- sub("://", str, url)
    }
    
    if (!is.null(auth_token)) {
      sep <- if (grepl("?", url, fixed = TRUE)) "&" else "?"
      real_url <- paste0(url, sep, "access_token=", auth_token)
    }
    
    if (compareVersion(get_r_version(), "3.2.0") == -1) {
      curl_download(real_url, path, quiet)
      
    } else {
      
      base_download(real_url, path, quiet)
    }
    
    path
  }
  
  base_download <- function(url, path, quiet) {
    
    suppressWarnings(
      status <- utils::download.file(
        url,
        path,
        method = download_method(),
        quiet = quiet,
        mode = "wb"
      )
    )
    
    if (status != 0)  stop("Cannot download file from ", url, call. = FALSE)
    
    path
  }
  
  download_method <- function() {
    
    # R versions newer than 3.3.0 have correct default methods
    if (compareVersion(get_r_version(), "3.3") == -1) {
      
      if (os_type() == "windows") {
        "wininet"
        
      } else if (isTRUE(unname(capabilities("libcurl")))) {
        "libcurl"
        
      } else {
        "auto"
      }
      
    } else {
      "auto"
    }
  }
  
  curl_download <- function(url, path, quiet) {
    
    if (!pkg_installed("curl")) {
      stop("The 'curl' package is required if R is older than 3.2.0")
    }
    
    curl::curl_download(url, path, quiet = quiet, mode = "wb")
  }
  
  # Extract the commit hash from a git archive. Git archives include the SHA1
  # hash as the comment field of the zip central directory record
  # (see https://www.kernel.org/pub/software/scm/git/docs/git-archive.html)
  # Since we know it's 40 characters long we seek that many bytes minus 2
  # (to confirm the comment is exactly 40 bytes long)
  git_extract_sha1 <- function(bundle) {
    
    # open the bundle for reading
    conn <- file(bundle, open = "rb", raw = TRUE)
    on.exit(close(conn))
    
    # seek to where the comment length field should be recorded
    seek(conn, where = -0x2a, origin = "end")
    
    # verify the comment is length 0x28
    len <- readBin(conn, "raw", n = 2)
    if (len[1] == 0x28 && len[2] == 0x00) {
      # read and return the SHA1
      rawToChar(readBin(conn, "raw", n = 0x28))
    } else {
      NULL
    }
  }
  
  git <- function(args, quiet = TRUE, path = ".") {
    full <- paste0(shQuote(check_git_path()), " ", paste(args, collapse = ""))
    if (!quiet) {
      message(full)
    }
    
    result <- in_dir(path, system(full, intern = TRUE, ignore.stderr = quiet))
    
    status <- attr(result, "status") %||% 0
    if (!identical(as.character(status), "0")) {
      stop("Command failed (", status, ")", call. = FALSE)
    }
    
    result
  }
  
  # Retrieve the current running path of the git binary.
  # @param git_binary_name The name of the binary depending on the OS.
  git_path <- function(git_binary_name = NULL) {
    # Use user supplied path
    if (!is.null(git_binary_name)) {
      if (!file.exists(git_binary_name)) {
        stop("Path ", git_binary_name, " does not exist", .call = FALSE)
      }
      return(git_binary_name)
    }
    
    # Look on path
    git_path <- Sys.which("git")[[1]]
    if (git_path != "") return(git_path)
    
    # On Windows, look in common locations
    if (os_type() == "windows") {
      look_in <- c(
        "C:/Program Files/Git/bin/git.exe",
        "C:/Program Files (x86)/Git/bin/git.exe"
      )
      found <- file.exists(look_in)
      if (any(found)) return(look_in[found][1])
    }
    
    NULL
  }
  
  check_git_path <- function(git_binary_name = NULL) {
    
    path <- git_path(git_binary_name)
    
    if (is.null(path)) {
      stop("Git does not seem to be installed on your system.", call. = FALSE)
    }
    
    path
  }
  
  github_GET <- function(path, ..., pat = github_pat()) {
    
    url <- paste0("https://api.github.com/", path)
    
    tmp <- tempfile()
    download(tmp, url, auth_token = pat)
    
    fromJSONFile(tmp)
  }
  
  github_commit <- function(username, repo, ref = "master") {
    
    url <- file.path("https://api.github.com",
                     "repos", username, repo, "commits", ref)
    
    tmp <- tempfile()
    download(tmp, url, auth_token = github_pat())
    
    fromJSONFile(tmp)
  }
  
  #' Retrieve Github personal access token.
  #'
  #' A github personal access token
  #' Looks in env var \code{GITHUB_PAT}
  #'
  #' @keywords internal
  #' @noRd
  github_pat <- function() {
    pat <- Sys.getenv('GITHUB_PAT')
    if (identical(pat, "")) return(NULL)
    
    message("Using github PAT from envvar GITHUB_PAT")
    pat
  }
  
  #' Install a package directly from bitbucket
  #'
  #' This function is vectorised so you can install multiple packages in
  #' a single command.
  #'
  #' @inheritParams install_github
  #' @param auth_user your account username if you're attempting to install
  #'   a package hosted in a private repository (and your username is different
  #'   to \code{username})
  #' @param password your password
  #' @param ref Desired git reference; could be a commit, tag, or branch name.
  #'   Defaults to master.
  #' @seealso Bitbucket API docs:
  #'   \url{https://confluence.atlassian.com/bitbucket/use-the-bitbucket-cloud-rest-apis-222724129.html}
  #'
  #' @export
  #' @examples
  #' \dontrun{
  #' install_bitbucket("sulab/mygene.r@@default")
  #' install_bitbucket("dannavarro/lsr-package")
  #' }
  install_bitbucket <- function(repo, ref = "master", subdir = NULL,
                                auth_user = NULL, password = NULL, ...) {
    
    remotes <- lapply(repo, bitbucket_remote, ref = ref,
                      subdir = subdir, auth_user = auth_user, password = password)
    
    install_remotes(remotes, ...)
  }
  
  bitbucket_remote <- function(repo, ref = NULL, subdir = NULL,
                               auth_user = NULL, password = NULL, sha = NULL) {
    
    meta <- parse_git_repo(repo)
    
    remote("bitbucket",
           repo = meta$repo,
           subdir = meta$subdir %||% subdir,
           username = meta$username,
           ref = meta$ref %||% ref,
           sha = sha,
           auth_user = auth_user,
           password = password
    )
  }
  
  #' @export
  remote_download.bitbucket_remote <- function(x, quiet = FALSE) {
    if (!quiet) {
      message("Downloading bitbucket repo ", x$username, "/", x$repo, "@", x$ref)
    }
    
    dest <- tempfile(fileext = paste0(".zip"))
    src <- paste("https://bitbucket.org/", x$username, "/", tolower(x$repo), "/get/",
                 x$ref, ".zip", sep = "")
    
    if (!is.null(x$password)) {
      auth <- list(
        user = x$auth_user %||% x$username,
        password = x$password
      )
    } else {
      auth <- NULL
    }
    
    download(dest, src, basic_auth = auth)
  }
  
  #' @export
  remote_metadata.bitbucket_remote <- function(x, bundle = NULL, source = NULL) {
    # Determine sha as efficiently as possible
    if (!is.null(x$sha)) {
      # Might be cached already (because re-installing)
      sha <- x$sha
    } else if (!is.null(bundle)) {
      # Might be able to get from zip archive
      sha <- git_extract_sha1(bundle)
    } else {
      # Don't know
      sha <- NULL
    }
    
    list(
      RemoteType = "bitbucket",
      RemoteRepo = x$repo,
      RemoteUsername = x$username,
      RemoteRef = x$ref,
      RemoteSha = sha,
      RemoteSubdir = x$subdir
    )
  }
  
  #' Install a package from a git repository
  #'
  #' It is vectorised so you can install multiple packages with
  #' a single command. You do not need to have the \code{git2r} package,
  #' or an external git client installed.
  #'
  #' @param url Location of package. The url should point to a public or
  #'   private repository.
  #' @param branch Name of branch or tag to use, if not master.
  #' @param subdir A sub-directory within a git repository that may
  #'   contain the package we are interested in installing.
  #' @param git Whether to use the \code{git2r} package, or an external
  #'   git client via system. Default is \code{git2r} if it is installed,
  #'   otherwise an external git installation.
  #' @param ... passed on to \code{\link[utils]{install.packages}}
  #' @export
  #' @examples
  #' \dontrun{
  #' install_git("git://github.com/hadley/stringr.git")
  #' install_git("git://github.com/hadley/stringr.git", branch = "stringr-0.2")
  #'}
  install_git <- function(url, subdir = NULL, branch = NULL,
                          git = c("auto", "git2r", "external"), ...) {
    
    git_remote <- select_git_remote(match.arg(git))
    remotes <- lapply(url, git_remote, subdir = subdir, branch = branch)
    install_remotes(remotes, ...)
  }
  
  
  select_git_remote <- function(git) {
    if (git == "auto") {
      git <- if (pkg_installed("git2r")) "git2r" else "external"
    }
    
    list(git2r = git_remote_git2r, external = git_remote_xgit)[[git]]
  }
  
  
  git_remote_git2r <- function(url, subdir = NULL, branch = NULL) {
    remote("git2r",
           url = url,
           subdir = subdir,
           branch = branch
    )
  }
  
  
  git_remote_xgit <- function(url, subdir = NULL, branch = NULL) {
    remote("xgit",
           url = url,
           subdir = subdir,
           branch = branch
    )
  }
  
  #' @export
  remote_download.git2r_remote <- function(x, quiet = FALSE) {
    if (!quiet) {
      message("Downloading git repo ", x$url)
    }
    
    bundle <- tempfile()
    git2r::clone(x$url, bundle, progress = FALSE)
    
    if (!is.null(x$branch)) {
      r <- git2r::repository(bundle)
      git2r::checkout(r, x$branch)
    }
    
    bundle
  }
  
  #' @export
  remote_metadata.git2r_remote <- function(x, bundle = NULL, source = NULL) {
    if (!is.null(bundle)) {
      r <- git2r::repository(bundle)
      sha <- git2r::commits(r)[[1]]@sha
    } else {
      sha <- NULL
    }
    
    list(
      RemoteType = "git",
      RemoteUrl = x$url,
      RemoteSubdir = x$subdir,
      RemoteRef = x$ref,
      RemoteSha = sha
    )
  }
  
  
  #' @export
  remote_download.xgit_remote <- function(x, quiet = FALSE) {
    if (!quiet) {
      message("Downloading git repo ", x$url)
    }
    
    bundle <- tempfile()
    
    args <- c('clone', '--depth', '1', '--no-hardlinks')
    if (!is.null(x$branch)) args <- c(args, "--branch", x$branch)
    args <- c(args, x$args, x$url, bundle)
    git(paste0(args, collapse = " "), quiet = quiet)
    
    bundle
  }
  
  #' @export
  remote_metadata.xgit_remote <- function(x, bundle = NULL, source = NULL) {
    list(
      RemoteType = "git",
      RemoteUrl = x$url,
      RemoteSubdir = x$subdir,
      RemoteRef = x$ref,
      RemoteSha = xgit_remote_sha1(x$url),
      RemoteArgs = if (length(x$args) > 0) paste0(deparse(x$args), collapse = " ")
    )
  }
  
  #' @importFrom utils read.delim
  
  xgit_remote_sha1 <- function(url, ref = "master") {
    refs <- git(paste("ls-remote", url, ref))
    
    refs_df <- read.delim(text = refs, stringsAsFactors = FALSE, sep = "\t",
                          header = FALSE)
    names(refs_df) <- c("sha", "ref")
    
    refs_df$sha[1]
  }
  #' Attempts to install a package directly from GitHub.
  #'
  #' This function is vectorised on \code{repo} so you can install multiple
  #' packages in a single command.
  #'
  #' @param repo Repository address in the format
  #'   \code{username/repo[/subdir][@@ref|#pull]}. Alternatively, you can
  #'   specify \code{subdir} and/or \code{ref} using the respective parameters
  #'   (see below); if both is specified, the values in \code{repo} take
  #'   precedence.
  #' @param username User name. Deprecated: please include username in the
  #'   \code{repo}
  #' @param ref Desired git reference. Could be a commit, tag, or branch
  #'   name, or a call to \code{\link{github_pull}}. Defaults to \code{"master"}.
  #' @param subdir subdirectory within repo that contains the R package.
  #' @param auth_token To install from a private repo, generate a personal
  #'   access token (PAT) in \url{https://github.com/settings/applications} and
  #'   supply to this argument. This is safer than using a password because
  #'   you can easily delete a PAT without affecting any others. Defaults to
  #'   the \code{GITHUB_PAT} environment variable.
  #' @param host GitHub API host to use. Override with your GitHub enterprise
  #'   hostname, for example, \code{"github.hostname.com/api/v3"}.
  #' @param ... Other arguments passed on to \code{\link[utils]{install.packages}}.
  #' @details
  #' Attempting to install from a source repository that uses submodules
  #' raises a warning. Because the zipped sources provided by GitHub do not
  #' include submodules, this may lead to unexpected behaviour or compilation
  #' failure in source packages. In this case, cloning the repository manually
  #' may yield better results.
  #' @export
  #' @seealso \code{\link{github_pull}}
  #' @examples
  #' \dontrun{
  #' install_github("klutometis/roxygen")
  #' install_github("wch/ggplot2")
  #' install_github(c("rstudio/httpuv", "rstudio/shiny"))
  #' install_github(c("hadley/httr@@v0.4", "klutometis/roxygen#142",
  #'   "mfrasca/r-logging/pkg"))
  #'
  #' # To install from a private repo, use auth_token with a token
  #' # from https://github.com/settings/applications. You only need the
  #' # repo scope. Best practice is to save your PAT in env var called
  #' # GITHUB_PAT.
  #' install_github("hadley/private", auth_token = "abc")
  #'
  #' }
  install_github <- function(repo, username = NULL,
                             ref = "master", subdir = NULL,
                             auth_token = github_pat(),
                             host = "api.github.com", ...) {
    
    remotes <- lapply(repo, github_remote, username = username, ref = ref,
                      subdir = subdir, auth_token = auth_token, host = host)
    
    install_remotes(remotes, ...)
  }
  
  github_remote <- function(repo, username = NULL, ref = NULL, subdir = NULL,
                            auth_token = github_pat(), sha = NULL,
                            host = "api.github.com") {
    
    meta <- parse_git_repo(repo)
    meta <- github_resolve_ref(meta$ref %||% ref, meta)
    
    if (is.null(meta$username)) {
      meta$username <- username %||% getOption("github.user") %||%
        stop("Unknown username.")
      warning("Username parameter is deprecated. Please use ",
              username, "/", repo, call. = FALSE)
    }
    
    remote("github",
           host = host,
           repo = meta$repo,
           subdir = meta$subdir %||% subdir,
           username = meta$username,
           ref = meta$ref,
           sha = sha,
           auth_token = auth_token
    )
  }
  
  #' @export
  remote_download.github_remote <- function(x, quiet = FALSE) {
    if (!quiet) {
      message("Downloading GitHub repo ", x$username, "/", x$repo, "@", x$ref)
    }
    
    dest <- tempfile(fileext = paste0(".zip"))
    src_root <- paste0("https://", x$host, "/repos/", x$username, "/", x$repo)
    src <- paste0(src_root, "/zipball/", utils::URLencode(x$ref, reserved = TRUE))
    
    if (github_has_submodules(x)) {
      warning("GitHub repo contains submodules, may not function as expected!",
              call. = FALSE)
    }
    
    download(dest, src, auth_token = x$auth_token)
  }
  
  github_has_submodules <- function(x) {
    src_root <- paste0("https://", x$host, "/repos/", x$username, "/", x$repo)
    src_submodules <- paste0(src_root, "/contents/.gitmodules?ref=", x$ref)
    
    tmp <- tempfile()
    res <- tryCatch(
      download(tmp, src_submodules, auth_token = x$auth_token),
      error = function(e) e
    )
    if (methods::is(res, "error")) return(FALSE)
    
    ## download() sometimes just downloads the error page, because
    ## the libcurl backend in download.file() is broken
    ## If the request was successful (=submodules exist), then it has an
    ## 'sha' field.
    sha <- tryCatch(
      fromJSONFile(tmp)$sha,
      error = function(e) e
    )
    ! methods::is(sha, "error") && ! is.null(sha)
  }
  
  #' @export
  remote_metadata.github_remote <- function(x, bundle = NULL, source = NULL) {
    # Determine sha as efficiently as possible
    if (!is.null(x$sha)) {
      # Might be cached already (because re-installing)
      sha <- x$sha
    } else if (!is.null(bundle)) {
      # Might be able to get from zip archive
      sha <- git_extract_sha1(bundle)
    } else {
      # Otherwise can use github api
      sha <- github_commit(x$username, x$repo, x$ref)$sha
    }
    
    list(
      RemoteType = "github",
      RemoteHost = x$host,
      RemoteRepo = x$repo,
      RemoteUsername = x$username,
      RemoteRef = x$ref,
      RemoteSha = sha,
      RemoteSubdir = x$subdir,
      # Backward compatibility for packrat etc.
      GithubRepo = x$repo,
      GithubUsername = x$username,
      GithubRef = x$ref,
      GithubSHA1 = sha,
      GithubSubdir = x$subdir
    )
  }
  
  #' GitHub references
  #'
  #' Use as \code{ref} parameter to \code{\link{install_github}}.
  #' Allows installing a specific pull request or the latest release.
  #'
  #' @param pull The pull request to install
  #' @seealso \code{\link{install_github}}
  #' @rdname github_refs
  #' @export
  github_pull <- function(pull) structure(pull, class = "github_pull")
  
  #' @rdname github_refs
  #' @export
  github_release <- function() structure(NA_integer_, class = "github_release")
  
  github_resolve_ref <- function(x, params) UseMethod("github_resolve_ref")
  
  #' @export
  github_resolve_ref.default <- function(x, params) {
    params$ref <- x
    params
  }
  
  #' @export
  github_resolve_ref.NULL <- function(x, params) {
    params$ref <- "master"
    params
  }
  
  #' @export
  github_resolve_ref.github_pull <- function(x, params) {
    # GET /repos/:user/:repo/pulls/:number
    path <- file.path("repos", params$username, params$repo, "pulls", x)
    response <- tryCatch(
      github_GET(path),
      error = function(e) e
    )
    
    ## Just because libcurl might download the error page...
    if (methods::is(response, "error") || is.null(response$head)) {
      stop("Cannot find GitHub pull request ", params$username, "/",
           params$repo, "#", x)
    }
    
    params$username <- response$head$user$login
    params$ref <- response$head$ref
    params
  }
  
  # Retrieve the ref for the latest release
  #' @export
  github_resolve_ref.github_release <- function(x, params) {
    # GET /repos/:user/:repo/releases
    path <- paste("repos", params$username, params$repo, "releases", sep = "/")
    response <- tryCatch(
      github_GET(path),
      error = function(e) e
    )
    
    if (methods::is(response, "error") || !is.null(response$message)) {
      stop("Cannot find repo ", params$username, "/", params$repo, ".")
    }
    
    if (length(response) == 0L)
      stop("No releases found for repo ", params$username, "/", params$repo, ".")
    
    params$ref <- response[[1L]]$tag_name
    params
  }
  
  #' Parse a remote git repo specification
  #'
  #' A remote repo can be specified in two ways:
  #' \describe{
  #' \item{as a URL}{\code{parse_github_url()} handles HTTPS and SSH remote URLs
  #' and various GitHub browser URLs}
  #' \item{via a shorthand}{\code{parse_repo_spec()} handles this concise form:
  #' \code{[username/]repo[/subdir][#pull|@ref|@*release]}}
  #' }
  #'
  #' @param repo Character scalar, the repo specification.
  #' @return List with members: \code{username}, \code{repo}, \code{subdir}
  #'   \code{ref}, \code{pull}, \code{release}, some which will be empty.
  #'
  #' @name parse-git-repo
  #' @examples
  #' parse_repo_spec("metacran/crandb")
  #' parse_repo_spec("jimhester/covr#47")        ## pull request
  #' parse_repo_spec("jeroen/curl@v0.9.3")       ## specific tag
  #' parse_repo_spec("tidyverse/dplyr@*release") ## shorthand for latest release
  #' parse_repo_spec("r-lib/remotes@550a3c7d3f9e1493a2ba") ## commit SHA
  #'
  #' parse_github_url("https://github.com/jeroen/curl.git")
  #' parse_github_url("git@github.com:metacran/crandb.git")
  #' parse_github_url("https://github.com/jimhester/covr")
  #' parse_github_url("https://github.example.com/user/repo.git")
  #' parse_github_url("git@github.example.com:user/repo.git")
  #'
  #' parse_github_url("https://github.com/r-lib/remotes/pull/108")
  #' parse_github_url("https://github.com/r-lib/remotes/tree/name-of-branch")
  #' parse_github_url("https://github.com/r-lib/remotes/commit/1234567")
  #' parse_github_url("https://github.com/r-lib/remotes/releases/latest")
  #' parse_github_url("https://github.com/r-lib/remotes/releases/tag/1.0.0")
  NULL
  
  #' @export
  #' @rdname parse-git-repo
  parse_repo_spec <- function(repo) {
    username_rx <- "(?:(?<username>[^/]+)/)?"
    repo_rx     <- "(?<repo>[^/@#]+)"
    subdir_rx   <- "(?:/(?<subdir>[^@#]*[^@#/])/?)?"
    ref_rx      <- "(?:@(?<ref>[^*].*))"
    pull_rx     <- "(?:#(?<pull>[0-9]+))"
    release_rx  <- "(?:@(?<release>[*]release))"
    ref_or_pull_or_release_rx <- sprintf(
      "(?:%s|%s|%s)?", ref_rx, pull_rx, release_rx
    )
    spec_rx  <- sprintf(
      "^%s%s%s%s$", username_rx, repo_rx, subdir_rx, ref_or_pull_or_release_rx
    )
    params <- as.list(re_match(text = repo, pattern = spec_rx))
    
    if (is.na(params$.match)) {
      stop(sprintf("Invalid git repo specification: '%s'", repo))
    }
    
    params[grepl("^[^\\.]", names(params))]
  }
  
  #' @export
  #' @rdname parse-git-repo
  parse_github_repo_spec <- parse_repo_spec
  
  #' @export
  #' @rdname parse-git-repo
  parse_github_url <- function(repo) {
    prefix_rx <- "(?:github[^/:]+[/:])"
    username_rx <- "(?:(?<username>[^/]+)/)"
    repo_rx     <- "(?<repo>[^/@#]+)"
    ref_rx <- "(?:(?:tree|commit|releases/tag)/(?<ref>.+$))"
    pull_rx <- "(?:pull/(?<pull>.+$))"
    release_rx <- "(?:releases/)(?<release>.+$)"
    ref_or_pull_or_release_rx <- sprintf(
      "(?:/(%s|%s|%s))?", ref_rx, pull_rx, release_rx
    )
    url_rx  <- sprintf(
      "%s%s%s%s",
      prefix_rx, username_rx, repo_rx, ref_or_pull_or_release_rx
    )
    params <- as.list(re_match(text = repo, pattern = url_rx))
    
    if (is.na(params$.match)) {
      stop(sprintf("Invalid GitHub URL: '%s'", repo))
    }
    if (params$ref == "" && params$pull == "" && params$release == "") {
      params$repo <- gsub("\\.git$", "", params$repo)
    }
    if (params$release == "latest") {
      params$release <- "*release"
    }
    
    params[grepl("^[^\\.]", names(params))]
  }
  
  parse_git_repo <- function(repo) {
    
    if (grepl("^https://github|^git@github", repo)) {
      params <- parse_github_url(repo)
    } else {
      params <- parse_repo_spec(repo)
    }
    params <- params[viapply(params, nchar) > 0]
    
    if (!is.null(params$pull)) {
      params$ref <- github_pull(params$pull)
      params$pull <- NULL
    }
    
    if (!is.null(params$release)) {
      params$ref <- github_release()
      params$release <- NULL
    }
    
    params
  }
  
  #' Install a package from a local file
  #'
  #' This function is vectorised so you can install multiple packages in
  #' a single command.
  #'
  #' @param path path to local directory, or compressed file (tar, zip, tar.gz
  #'   tar.bz2, tgz2 or tbz)
  #' @inheritParams install_url
  #' @export
  #' @examples
  #' \dontrun{
  #' dir <- tempfile()
  #' dir.create(dir)
  #' pkg <- download.packages("testthat", dir, type = "source")
  #' install_local(pkg[, 2])
  #' }
  
  install_local <- function(path, subdir = NULL, ...) {
    remotes <- lapply(path, local_remote, subdir = subdir)
    install_remotes(remotes, ...)
  }
  
  local_remote <- function(path, subdir = NULL, branch = NULL, args = character(0)) {
    remote("local",
           path = normalizePath(path),
           subdir = subdir
    )
  }
  
  #' @export
  remote_download.local_remote <- function(x, quiet = FALSE) {
    # Already downloaded - just need to copy to tempdir()
    bundle <- tempfile()
    dir.create(bundle)
    file.copy(x$path, bundle, recursive = TRUE)
    
    # file.copy() creates directory inside of bundle
    dir(bundle, full.names = TRUE)[1]
  }
  
  #' @export
  remote_metadata.local_remote <- function(x, bundle = NULL, source = NULL) {
    list(
      RemoteType = "local",
      RemoteUrl = x$path,
      RemoteSubdir = x$subdir
    )
  }
  #' Install a remote package.
  #'
  #' This:
  #' \enumerate{
  #'   \item downloads source bundle
  #'   \item decompresses & checks that it's a package
  #'   \item adds metadata to DESCRIPTION
  #'   \item calls install
  #' }
  #' @noRd
  install_remote <- function(remote, ..., quiet = FALSE) {
    stopifnot(is.remote(remote))
    
    bundle <- remote_download(remote, quiet = quiet)
    on.exit(unlink(bundle), add = TRUE)
    
    source <- source_pkg(bundle, subdir = remote$subdir)
    on.exit(unlink(source, recursive = TRUE), add = TRUE)
    
    add_metadata(source, remote_metadata(remote, bundle, source))
    
    # Because we've modified DESCRIPTION, its original MD5 value is wrong
    clear_description_md5(source)
    
    install(source, ..., quiet = quiet)
  }
  
  install_remotes <- function(remotes, ...) {
    invisible(vapply(remotes, install_remote, ..., FUN.VALUE = logical(1)))
  }
  
  # Add metadata
  add_metadata <- function(pkg_path, meta) {
    path <- file.path(pkg_path, "DESCRIPTION")
    desc <- read_dcf(path)
    
    desc <- utils::modifyList(desc, meta)
    
    write_dcf(path, desc)
  }
  
  # Modify the MD5 file - remove the line for DESCRIPTION
  clear_description_md5 <- function(pkg_path) {
    path <- file.path(pkg_path, "MD5")
    
    if (file.exists(path)) {
      text <- readLines(path)
      text <- text[!grepl(".*\\*DESCRIPTION$", text)]
      
      writeLines(text, path)
    }
  }
  
  remote <- function(type, ...) {
    structure(list(...), class = c(paste0(type, "_remote"), "remote"))
  }
  is.remote <- function(x) inherits(x, "remote")
  
  remote_download <- function(x, quiet = FALSE) UseMethod("remote_download")
  remote_metadata <- function(x, bundle = NULL, source = NULL) UseMethod("remote_metadata")
  
  #' Install a package from a SVN repository
  #'
  #' This function requires \command{svn} to be installed on your system in order to
  #' be used.
  #'
  #' It is vectorised so you can install multiple packages with
  #' a single command.
  #'
  #' @inheritParams install_git
  #' @param subdir A sub-directory withing a svn repository that contains the
  #'   package we are interested in installing.
  #' @param args A character vector providing extra options to pass on to
  #'   \command{svn}.
  #' @param revision svn revision, if omitted updates to latest
  #' @param ... Other arguments passed on to \code{\link[utils]{install.packages}}.
  #' @export
  #'
  #' @examples
  #' \dontrun{
  #' install_svn("svn://github.com/hadley/stringr/trunk")
  #' install_svn("svn://github.com/hadley/httr/branches/oauth")
  #'}
  install_svn <- function(url, subdir = NULL, args = character(0),
                          ..., revision = NULL) {
    
    remotes <- lapply(url, svn_remote, svn_subdir = subdir,
                      revision = revision, args = args)
    
    install_remotes(remotes, ...)
  }
  
  svn_remote <- function(url, svn_subdir = NULL, revision = revision,
                         args = character(0)) {
    remote("svn",
           url = url,
           svn_subdir = svn_subdir,
           revision = revision,
           args = args
    )
  }
  
  #' @export
  remote_download.svn_remote <- function(x, quiet = FALSE) {
    if (!quiet) {
      message("Downloading svn repo ", x$url)
    }
    
    bundle <- tempfile()
    svn_binary_path <- svn_path()
    url <- x$url
    args <- "export"
    if (!is.null(x$revision))
      args <- paste("-r", x$revision, args)
    if (!is.null(x$svn_subdir)) {
      url <- file.path(url, x$svn_subdir);
    }
    args <- c(x$args, args, url, bundle)
    
    message(shQuote(svn_binary_path), " ", paste0(args, collapse = " "))
    request <- system2(svn_binary_path, args, stdout = FALSE, stderr = FALSE)
    
    # This is only looking for an error code above 0-success
    if (request > 0) {
      stop("There seems to be a problem retrieving this SVN-URL.", call. = FALSE)
    }
    
    bundle
  }
  
  #' @export
  remote_metadata.svn_remote <- function(x, bundle = NULL, source = NULL) {
    list(
      RemoteType = "svn",
      RemoteUrl = x$url,
      RemoteSubdir = x$subdir,
      RemoteArgs = if (length(x$args) > 0) paste0(deparse(x$args), collapse = " ")
    )
  }
  
  svn_path <- function(svn_binary_name = NULL) {
    # Use user supplied path
    if (!is.null(svn_binary_name)) {
      if (!file.exists(svn_binary_name)) {
        stop("Path ", svn_binary_name, " does not exist", .call = FALSE)
      }
      return(svn_binary_name)
    }
    
    # Look on path
    svn_path <- Sys.which("svn")[[1]]
    if (svn_path != "") return(svn_path)
    
    # On Windows, look in common locations
    if (os_type() == "windows") {
      look_in <- c(
        "C:/Program Files/Svn/bin/svn.exe",
        "C:/Program Files (x86)/Svn/bin/svn.exe"
      )
      found <- file.exists(look_in)
      if (any(found)) return(look_in[found][1])
    }
    
    stop("SVN does not seem to be installed on your system.", call. = FALSE)
  }
  
  #' Install a package from a url
  #'
  #' This function is vectorised so you can install multiple packages in
  #' a single command.
  #'
  #' @param url location of package on internet. The url should point to a
  #'   zip file, a tar file or a bzipped/gzipped tar file.
  #' @param subdir subdirectory within url bundle that contains the R package.
  #' @param ... Other arguments passed on to \code{\link[utils]{install.packages}}.
  #' @export
  #'
  #' @examples
  #' \dontrun{
  #' install_url("https://github.com/hadley/stringr/archive/master.zip")
  #' }
  
  install_url <- function(url, subdir = NULL, ...) {
    remotes <- lapply(url, url_remote, subdir = subdir)
    install_remotes(remotes, ...)
  }
  
  url_remote <- function(url, subdir = NULL) {
    remote("url",
           url = url,
           subdir = subdir
    )
  }
  
  #' @importFrom tools file_ext
  #' @export
  remote_download.url_remote <- function(x, quiet = FALSE) {
    if (!quiet) {
      message("Downloading package from url: ", x$url)
    }
    
    ext <- if (grepl("\\.tar\\.gz$", x$url)) "tar.gz" else file_ext(x$url)
    
    bundle <- tempfile(fileext = paste0(".", ext))
    download(bundle, x$url)
  }
  
  #' @export
  remote_metadata.url_remote <- function(x, bundle = NULL, source = NULL) {
    list(
      RemoteType = "url",
      RemoteUrl = x$url,
      RemoteSubdir = x$subdir
    )
  }
  
  #' Install specified version of a CRAN package.
  #'
  #' If you are installing an package that contains compiled code, you will
  #' need to have an R development environment installed.  You can check
  #' if you do by running \code{devtools::has_devel} (you need the
  #' \code{devtools} package for this).
  #'
  #' @export
  #' @family package installation
  #' @param package package name
  #' @param version If the specified version is NULL or the same as the most
  #'   recent version of the package, this function simply calls
  #'   \code{\link[utils]{install.packages}}. Otherwise, it looks at the list of
  #'   archived source tarballs and tries to install an older version instead.
  #' @param ... Other arguments passed on to \code{\link[utils]{install.packages}}.
  #' @inheritParams utils::install.packages
  #' @author Jeremy Stephens
  #' @importFrom utils available.packages contrib.url install.packages
  
  install_version <- function(package, version = NULL, repos = getOption("repos"), type = getOption("pkgType"), ...) {
    
    url <- download_version_url(package, version, repos, type)
    install_url(url, ...)
  }
  
  package_find_repo <- function(package, repos) {
    for (repo in repos) {
      if (length(repos) > 1)
        message("Trying ", repo)
      
      archive <-
        tryCatch({
          con <- gzcon(url(sprintf("%s/src/contrib/Meta/archive.rds", repo), "rb"))
          on.exit(close(con))
          readRDS(con)
        },
        warning = function(e) list(),
        error = function(e) list())
      
      info <- archive[[package]]
      if (!is.null(info)) {
        info$repo <- repo
        return(info)
      }
    }
    
    stop(sprintf("couldn't find package '%s'", package))
  }
  
  
  #' Download a specified version of a CRAN package
  #'
  #' It downloads the package to a temporary file, and
  #' returns the name of the file.
  #'
  #' @inheritParams install_version
  #' @return Name of the downloaded file.
  #'
  #' @export
  
  download_version <- function(package, version = NULL,
                               repos = getOption("repos"),
                               type = getOption("pkgType"), ...) {
    
    url <- download_version_url(package, version, repos, type)
    download(path = tempfile(), url = url)
  }
  
  download_version_url <- function(package, version, repos, type) {
    
    contriburl <- contrib.url(repos, type)
    available <- available.packages(contriburl)
    
    if (package %in% row.names(available)) {
      current.version <- available[package, 'Version']
      if (is.null(version) || version == current.version) {
        row <- available[which(rownames(available) == package)[1], ]
        return(paste0(
          row[["Repository"]],
          "/",
          row[["Package"]],
          "_",
          row[["Version"]],
          ".tar.gz"
        ))
      }
    }
    
    info <- package_find_repo(package, repos)
    
    if (is.null(version)) {
      # Grab the latest one: only happens if pulled from CRAN
      package.path <- row.names(info)[nrow(info)]
    } else {
      package.path <- paste(package, "/", package, "_", version, ".tar.gz",
                            sep = "")
      if (!(package.path %in% row.names(info))) {
        stop(sprintf("version '%s' is invalid for package '%s'", version,
                     package))
      }
    }
    
    paste(info$repo[1L], "/src/contrib/Archive/", package.path, sep = "")
  }
  
  install <- function(pkgdir = ".", dependencies = NA, quiet = TRUE, ...) {
    
    if (file.exists(file.path(pkgdir, "src")) && ! has_devel()) {
      missing_devel_warning(pkgdir)
    }
    
    ## Check for circular dependencies. We need to know about the root
    ## of the install process.
    if (is_root_install()) on.exit(exit_from_root_install(), add = TRUE)
    if (check_for_circular_dependencies(pkgdir, quiet)) {
      return(invisible(FALSE))
    }
    
    install_deps(pkgdir, dependencies = dependencies, quiet = quiet, ...)
    
    safe_install_packages(
      pkgdir,
      repos = NULL,
      quiet = quiet,
      type = "source",
      ...
    )
    
    invisible(TRUE)
  }
  
  safe_install_packages <- function(...) {
    
    lib <- paste(.libPaths(), collapse = ":")
    
    if (has_package("crancache") && has_package("callr")) {
      i.p <- "crancache" %::% "install_packages"
    } else {
      i.p <- utils::install.packages
    }
    
    with_envvar(
      c(R_LIBS = lib,
        R_LIBS_USER = lib,
        R_LIBS_SITE = lib,
        R_PROFILE_USER = tempfile()),
      i.p(...)
    )
  }
  
  #' Install package dependencies if needed.
  #'
  #' @inheritParams package_deps
  #' @param threads Number of threads to start, passed to
  #'   \code{\link[utils]{install.packages}} as \code{Ncpus}.
  #' @param ... additional arguments passed to \code{\link[utils]{install.packages}}.
  #' @export
  #' @examples
  #' \dontrun{install_deps(".")}
  
  install_deps <- function(pkgdir = ".", dependencies = NA,
                           threads = getOption("Ncpus", 1),
                           repos = getOption("repos"),
                           type = getOption("pkgType"),
                           ...,
                           upgrade = TRUE,
                           quiet = FALSE) {
    
    packages <- dev_package_deps(
      pkgdir,
      repos = repos,
      dependencies = dependencies,
      type = type
    )
    
    dep_deps <- if (isTRUE(dependencies)) NA else dependencies
    
    update(
      packages,
      dependencies = dep_deps,
      ...,
      Ncpus = threads,
      quiet = quiet,
      upgrade = upgrade
    )
  }
  
  tokenize_json <- function(text) {
    text <- paste(text, collapse = "\n")
    
    ESCAPE <- '(\\\\[^u[:cntrl:]]|\\\\u[0-9a-fA-F]{4})'
    CHAR <- '[^[:cntrl:]"\\\\]'
    
    STRING <- paste0('"', CHAR, '*(', ESCAPE, CHAR, '*)*"')
    NUMBER <- "-?(0|[1-9][0-9]*)([.][0-9]*)?([eE][+-]?[0-9]*)?"
    KEYWORD <- 'null|false|true'
    SPACE <- '[[:space:]]+'
    
    match <- gregexpr(
      pattern = paste0(
        STRING, "|", NUMBER, "|", KEYWORD, "|", SPACE, "|", "."
      ),
      text = text,
      perl = TRUE
    )
    
    grep("^\\s+$", regmatches(text, match)[[1]], value = TRUE, invert = TRUE)
  }
  
  throw <- function(...) {
    stop("JSON: ", ..., call. = FALSE)
  }
  
  fromJSONFile <- function(filename) {
    fromJSON(readLines(filename, warn = FALSE))
  }
  
  fromJSON <- function(text) {
    
    tokens <- tokenize_json(text)
    token <- NULL
    ptr <- 1
    
    read_token <- function() {
      if (ptr <= length(tokens)) {
        token <<- tokens[ptr]
        ptr <<- ptr + 1
      } else {
        token <<- 'EOF'
      }
    }
    
    parse_value <- function(name = "") {
      if (token == "{") {
        parse_object()
      } else if (token == "[") {
        parse_array()
      } else if (token == "EOF" || (nchar(token) == 1 && ! token %in% 0:9)) {
        throw("EXPECTED value GOT ", token)
      } else {
        j2r(token)
      }
    }
    
    parse_object <- function() {
      res <- structure(list(), names = character())
      
      read_token()
      
      ## Invariant: we are at the beginning of an element
      while (token != "}") {
        
        ## "key"
        if (grepl('^".*"$', token)) {
          key <- j2r(token)
        } else {
          throw("EXPECTED string GOT ", token)
        }
        
        ## :
        read_token()
        if (token != ":") { throw("EXPECTED : GOT ", token) }
        
        ## value
        read_token()
        res[key] <- list(parse_value())
        
        ## } or ,
        read_token()
        if (token == "}") {
          break
        } else if (token != ",") {
          throw("EXPECTED , or } GOT ", token)
        }
        read_token()
      }
      
      res
    }
    
    parse_array <- function() {
      res <- list()
      
      read_token()
      
      ## Invariant: we are at the beginning of an element
      while (token != "]") {
        ## value
        res <- c(res, list(parse_value()))
        
        ## ] or ,
        read_token()
        if (token == "]") {
          break
        } else if (token != ",") {
          throw("EXPECTED , GOT ", token)
        }
        read_token()
      }
      
      res
    }
    
    read_token()
    parse_value(tokens)
  }
  
  j2r <- function(token) {
    if (token == "null") {
      NULL
    } else if (token == "true") {
      TRUE
    } else if (token == "false") {
      FALSE
    } else if (grepl('^".*"$', token)) {
      trimq(token)
    } else {
      as.numeric(token)
    }
  }
  
  trimq <- function(x) {
    sub('^"(.*)"$', "\\1", x)
  }
  
  parse_deps <- function(string) {
    if (is.null(string)) return()
    stopifnot(is.character(string), length(string) == 1)
    if (grepl("^\\s*$", string)) return()
    
    pieces <- strsplit(string, ",")[[1]]
    
    # Get the names
    names <- gsub("\\s*\\(.*?\\)", "", pieces)
    names <- gsub("^\\s+|\\s+$", "", names)
    
    # Get the versions and comparison operators
    versions_str <- pieces
    have_version <- grepl("\\(.*\\)", versions_str)
    versions_str[!have_version] <- NA
    
    compare  <- sub(".*\\((\\S+)\\s+.*\\)", "\\1", versions_str)
    versions <- sub(".*\\(\\S+\\s+(.*)\\)", "\\1", versions_str)
    
    # Check that non-NA comparison operators are valid
    compare_nna   <- compare[!is.na(compare)]
    compare_valid <- compare_nna %in% c(">", ">=", "==", "<=", "<")
    if(!all(compare_valid)) {
      stop("Invalid comparison operator in dependency: ",
           paste(compare_nna[!compare_valid], collapse = ", "))
    }
    
    deps <- data.frame(name = names, compare = compare,
                       version = versions, stringsAsFactors = FALSE)
    
    # Remove R dependency
    deps[names != "R", ]
  }
  
  load_pkg_description <- function(path) {
    
    path <- normalizePath(path)
    
    if (!is_dir(path)) {
      dir <- tempfile()
      path_desc <- untar_description(path, dir = dir)
      on.exit(unlink(dir, recursive = TRUE))
      
    } else {
      path_desc <- file.path(path, "DESCRIPTION")
    }
    
    desc <- read_dcf(path_desc)
    names(desc) <- tolower(names(desc))
    desc$path <- path
    
    desc
  }
  
  system_check <- function(command, args = character(), quiet = TRUE,
                           error = TRUE, path = ".") {
    
    out <- tempfile()
    err <- tempfile()
    on.exit(unlink(out), add = TRUE)
    on.exit(unlink(err), add = TRUE)
    
    ## We suppress warnings, they are given if the command
    ## exits with a non-zero status
    res <- in_dir(
      path,
      suppressWarnings(
        system2(command, args = args, stdout = out, stderr = err)
      )
    )
    
    res <- list(
      stdout = tryCatch(
        suppressWarnings(win2unix(read_char(out))),
        error = function(e) ""
      ),
      stderr = tryCatch(
        suppressWarnings(win2unix(read_char(err))),
        error = function(e) ""
      ),
      status = res
    )
    
    if (error && res$status != 0) {
      stop("Command ", command, " failed ", res$stderr)
    }
    
    if (! quiet) {
      if (! identical(res$stdout, NA_character_)) cat(res$stdout)
      if (! identical(res$stderr, NA_character_)) cat(res$stderr)
    }
    
    res
  }
  
  win2unix <- function(str) {
    gsub("\r\n", "\n", str, fixed = TRUE)
  }
  
  read_char <- function(path, ...) {
    readChar(path, nchars = file.info(path)$size, ...)
  }
  
  `%||%` <- function (a, b) if (!is.null(a)) a else b
  
  `%:::%` <- function (p, f) get(f, envir = asNamespace(p))
  
  `%::%` <- function (p, f) get(f, envir = asNamespace(p))
  
  viapply <- function(X, FUN, ..., USE.NAMES = TRUE) {
    vapply(X, FUN, integer(1L), ..., USE.NAMES = USE.NAMES)
  }
  
  is_bioconductor <- function(x) {
    !is.null(x$biocviews)
  }
  
  trim_ws <- function(x) {
    gsub("^[[:space:]]+|[[:space:]]+$", "", x)
  }
  
  set_envvar <- function(envs) {
    if (length(envs) == 0) return()
    
    stopifnot(is.named(envs))
    
    old <- Sys.getenv(names(envs), names = TRUE, unset = NA)
    set <- !is.na(envs)
    
    both_set <- set & !is.na(old)
    
    if (any(set))  do.call("Sys.setenv", as.list(envs[set]))
    if (any(!set)) Sys.unsetenv(names(envs)[!set])
    
    invisible(old)
  }
  
  with_envvar <- function(new, code) {
    old <- set_envvar(new)
    on.exit(set_envvar(old))
    force(code)
  }
  
  is.named <- function(x) {
    !is.null(names(x)) && all(names(x) != "")
  }
  
  pkg_installed <- function(pkg) {
    
    if (pkg %in% loadedNamespaces()) {
      TRUE
    } else if (requireNamespace(pkg, quietly = TRUE)) {
      try(unloadNamespace(pkg))
      TRUE
    } else {
      FALSE
    }
  }
  
  has_package <- function(pkg) {
    if (pkg %in% loadedNamespaces()) {
      TRUE
    } else {
      requireNamespace(pkg, quietly = TRUE)
    }
  }
  
  with_something <- function(set, reset = set) {
    function(new, code) {
      old <- set(new)
      on.exit(reset(old))
      force(code)
    }
  }
  
  in_dir <- with_something(setwd)
  
  get_r_version <- function() {
    paste(R.version$major, sep = ".", R.version$minor)
  }
  
  set_libpaths <- function(paths) {
    old <- .libPaths()
    .libPaths(paths)
    invisible(old)
  }
  
  with_libpaths <- with_something(set_libpaths, .libPaths)
  
  ## There are two kinds of tar on windows, one needs --force-local
  ## not to interpret : characters, the other does not. We try both ways.
  
  untar <- function(tarfile, ...) {
    if (os_type() == "windows") {
      tryCatch(
        utils::untar(tarfile, extras = "--force-local", ...),
        error = function(e) utils::untar(tarfile, ...)
      )
      
    } else {
      utils::untar(tarfile, ...)
    }
  }
  
  os_type <- function() {
    .Platform$OS.type
  }
  
  sys_type <- function() {
    if (.Platform$OS.type == "windows") {
      "windows"
    } else if (Sys.info()["sysname"] == "Darwin") {
      "macos"
    } else if (Sys.info()["sysname"] == "Linux") {
      "linux"
    } else if (.Platform$OS.type == "unix") {
      "unix"
    } else {
      stop("Unknown OS")
    }
  }
  
  is_dir <- function(path) {
    file.info(path)$isdir
  }
  
  untar_description <- function(tarball, dir = tempfile()) {
    files <- untar(tarball, list = TRUE)
    desc <- grep("^[^/]+/DESCRIPTION$", files, value = TRUE)
    if (length(desc) < 1) stop("No 'DESCRIPTION' file in package")
    untar(tarball, desc, exdir = dir)
    file.path(dir, desc)
  }
  
  ## copied from rematch2@180fb61
  re_match <- function(text, pattern, perl = TRUE, ...) {
    
    stopifnot(is.character(pattern), length(pattern) == 1, !is.na(pattern))
    text <- as.character(text)
    
    match <- regexpr(pattern, text, perl = perl, ...)
    
    start  <- as.vector(match)
    length <- attr(match, "match.length")
    end    <- start + length - 1L
    
    matchstr <- substring(text, start, end)
    matchstr[ start == -1 ] <- NA_character_
    
    res <- data.frame(
      stringsAsFactors = FALSE,
      .text = text,
      .match = matchstr
    )
    
    if (!is.null(attr(match, "capture.start"))) {
      
      gstart  <- attr(match, "capture.start")
      glength <- attr(match, "capture.length")
      gend    <- gstart + glength - 1L
      
      groupstr <- substring(text, gstart, gend)
      groupstr[ gstart == -1 ] <- NA_character_
      dim(groupstr) <- dim(gstart)
      
      res <- cbind(groupstr, res, stringsAsFactors = FALSE)
    }
    
    names(res) <- c(attr(match, "capture.names"), ".text", ".match")
    class(res) <- c("tbl_df", "tbl", class(res))
    res
  }
  
  
  install_github(...)
  
}
ttraboue/FrissNotie documentation built on May 17, 2019, 8:17 a.m.