% Generated by roxygen2: do not edit by hand
% Please edit documentation in R/pkgSnapshot.R
\name{pkgSnapshot}
\alias{pkgSnapshot}
\alias{pkgSnapshot2}
\title{Take a snapshot of all the packages and version numbers}
\usage{
pkgSnapshot(
  packageVersionFile = getOption("Require.packageVersionFile"),
  libPaths = .libPaths(),
  standAlone = FALSE,
  purge = getOption("Require.purge", FALSE),
  exact = TRUE,
  includeBase = FALSE,
  verbose = getOption("Require.verbose")
)

pkgSnapshot2(
  packageVersionFile = getOption("Require.packageVersionFile"),
  libPaths,
  standAlone = FALSE,
  purge = getOption("Require.purge", FALSE),
  exact = TRUE,
  includeBase = FALSE,
  verbose = getOption("Require.verbose")
)
}
\arguments{
\item{packageVersionFile}{A filename to save the packages and their currently
installed version numbers. Defaults to \code{"packageVersions.txt"}.
If this is specified to be \code{NULL}, the function will return the exact
\code{Require} call needed to install all the packages at their current
versions. This can be useful to add to a script to allow for reproducibility of
a script.}

\item{libPaths}{The path to the local library where packages are installed.
Defaults to the \code{.libPaths()[1]}.}

\item{standAlone}{Logical. If \code{TRUE}, all packages will be installed to and
loaded from the \code{libPaths} only. NOTE: If \code{TRUE}, THIS WILL CHANGE THE
USER'S \code{.libPaths()}, similar to e.g., the \code{checkpoint} package. If
\code{FALSE}, then \code{libPath} will be prepended to \code{.libPaths()} during the
\code{Require} call, resulting in shared packages, i.e., it will include the
user's default package folder(s). This can be create dramatically faster
installs if the user has a substantial number of the packages already in
their personal library. Default \code{FALSE} to minimize package installing.}

\item{purge}{Logical. Should all caches be purged? Default is
\code{getOption("Require.purge", FALSE)}. There is a lot of internal caching of
results throughout the \code{Require} package. These help with speed and reduce
calls to internet sources. However, sometimes these caches must be purged.
The cached values are renewed when found to be too old, with the age limit.
This maximum age can be set in seconds with the environment variable
\code{R_AVAILABLE_PACKAGES_CACHE_CONTROL_MAX_AGE}, or if unset, defaults to 3600
(one hour -- see \code{\link[utils:available.packages]{utils::available.packages}}).

Internally, there are calls to \code{available.packages}.}

\item{exact}{Logical. If \code{TRUE}, the default, then for GitHub packages, it
will install the exact SHA, rather than the head of the \code{account/repo@branch}. For
CRAN packages, it will install the exact version. If \code{FALSE}, then GitHub
packages will identify their branch if that had been specified upon installation,
not a SHA. If the package had been installed with reference to a SHA, then it
will return the SHA as it does not know what branch it came from.
Similarly, CRAN packages will report their version and specify with a \code{>=},
allowing a subsequent user
to install with a minimum version number, as opposed to an exact version number.}

\item{includeBase}{Logical. Should R base packages be included, specifically,
those in \code{tail(.libPaths(), 1)}}

\item{verbose}{Numeric or logical indicating how verbose should the function
be. If -1 or -2, then as little verbosity as possible. If 0 or FALSE,
then minimal outputs; if \code{1} or TRUE, more outputs; \code{2} even more. NOTE: in
\code{Require} function, when \code{verbose >= 2}, also returns details as if
\code{returnDetails = TRUE} (for backwards compatibility).}
}
\value{
Will both write a file, and (invisibly) return a vector of packages with the
version numbers. This vector can be used directly in \code{Require}, though it should likely
be used with \code{require = FALSE} to prevent attaching all the packages.
}
\description{
This can be used later by \code{Require} to install or re-install the correct versions. See examples.
}
\details{
A file is written with the package names and versions of all packages within \code{libPaths}.
This can later be passed to \code{Require}.

\code{pkgSnapshot2} returns a vector of package names and versions, with no file output. See
examples.
}
\section{Installing from a snapshot file}{

Pass the snapshot file to \code{Require()} via \code{packageVersionFile = "snapshot.txt"}.
By default this routes through a multi-stage installer (gated by
\code{options(Require.snapshotInstaller = "install.packages")}) that:
\enumerate{
\item \strong{Skips already-installed-at-target-version refs.}
\item \strong{Cache pre-filter} via \code{pkgcache::pkg_cache_list()}. Source tarballs feed
\code{pak::pkg_install(local::...)}; binaries are reserved for step (4). Rotten
cache rows (missing fullpath, gzip-corrupt, DESCRIPTION mismatch) are
auto-evicted via \code{pkgcache::pkg_cache_delete_files()} so future runs
don't keep tripping on them.
\item \strong{Parallel libcurl-multi download} for refs not in cache, chunked at 50
URLs per call (macOS file-descriptor limit). Walks priority URLs: row's
\code{Repository} -> PPM -> CRAN -> CRAN/Archive. Up to 4 retries with
exponential backoff (\code{Require.snapshotDownloadAttempts}).
\item \strong{Hybrid binary-first install} via \code{install.packages(type = "binary")}
for any ref that has a cache binary matching this R session
(\code{R.version$platform}, \verb{<major>.<minor>}). Skips compilation, reduces
pak's parallel-build workload. Disable via
\code{options(Require.snapshotInstallerHybrid = FALSE)}.
\item \strong{\code{pak::pkg_install(local::...)} for the rest} with \code{dependencies = NA},
\code{upgrade = FALSE}. Refs already-installed-at-target-version are excluded
upfront so pak doesn't reinstall-to-self.
\item \strong{\verb{install.packages(repos = file://...)} fallback} if pak refuses (its
solver is strict; \code{install.packages} is best-effort and tolerates
per-package compile failures).
\item \strong{Bump-and-retry}: for any ref still missing, walks newer-than-pin
versions from CRAN/PPM/Archive ascending and tries each until one
installs (capped at 20 candidates). Disable via
\code{options(Require.snapshotInstallerBumpOnFail = FALSE)} for strict
reproducibility (no drift, fail loudly).
\item \strong{Diagnostic report} classifying each gap with a concrete \verb{fix:} line.
}

Built binaries from this run are added back to \code{pkgcache} via
\code{cacheBuiltBinaries()} (registered on \code{on.exit}), so a subsequent run hits
step (4) instead of recompiling.
}

\section{Common snapshot pitfalls}{


\describe{
\item{Version-coherence (the snapshot's own pins disagree)}{
A ref's \code{DESCRIPTION} declares \verb{Imports: X (>= V)} but the snapshot
pins \code{X} at a version that doesn't satisfy it. \code{pak}'s strict solver
refuses to install. The installer runs a coherence pre-check before
handing off to pak and prints any unsatisfied constraint with a fix
suggestion (e.g., \verb{servr 0.30 requires xfun (>= 0.42); snapshot pins xfun = 0.40 -> bump xfun}).
}
\item{\code{R} pseudo-package}{
\code{pkgSnapshot()} writes a row recording the running R version
(e.g., \verb{R,4.4,...}). The installer skips this row alongside base
packages.
}
\item{System library mismatches (compile failures)}{
Source builds need the host's system libs to match what the package
expects. The installer's \code{classifyCompileFailure()} recognises
missing-header errors (jpeglib.h, gdal.h, geos_c.h, glpk.h,
ft2build.h, sodium.h, ...) and prints the corresponding
\verb{brew install ...} (or apt) suggestion. R 4.5's removal of
\code{Calloc/Free}, GDAL >= 3.10's \verb{const OGRSpatialReference*} ABI
change, and Rcpp's \verb{class_::constructor<>} template-arity limit are
each pattern-matched and reported with a "bump \verb{<pkg>}" suggestion.
}
\item{Mac toolchain -- \verb{~/.R/Makevars}}{
R's default compile flags only search \verb{/opt/R/arm64/include}. To pick
up Homebrew headers (libjpeg, glpk, freetype, etc.), add to
\verb{~/.R/Makevars}:
\preformatted{
      CPPFLAGS += -I/opt/homebrew/include
      LDFLAGS  += -L/opt/homebrew/lib
    }
}
\item{Stale \code{pkgcache} index}{
\code{pkgcache} shares state across R versions and architectures. Cache
rows tagged with platform/rversion that don't match this session
are filtered out (e.g., R-4.5 binaries when running R-4.4); validation
also catches index rows whose file content disagrees with the index
(a known historical bug-class). Both kinds get auto-evicted, so
\code{pak::cache_clean()} is rarely needed.
}
\item{Pak's \verb{local::} is source-only}{
Confirmed empirically that pak's \code{pkg_install("local::<file>")}
rejects binary tarballs (\code{.tgz} / \code{.zip} content) with
"Platform mismatch" -- even when the binary is for the current
platform. The hybrid stage installs binaries via
\code{install.packages(type = "binary")} BEFORE pak runs, so pak only
sees source refs.
}
\item{Pak strict-aborts on first build failure}{
\code{install.packages} continues past per-package compile failures;
\code{pak::pkg_install} does not. The fallback to \code{install.packages} and
the bump-and-retry stage together provide a best-effort completion
guarantee even when individual refs are environment-fragile.
}
}
}

\section{Snapshot-installer options}{

\describe{
\item{\code{Require.snapshotInstaller}}{\code{"install.packages"} to use the
pipeline above; \code{"pak"} for the legacy direct-pak path.}
\item{\code{Require.snapshotInstallerUsePPM}}{TRUE (default) to prepend a PPM
binary repo. PPM serves Mac binaries by content-negotiating the
\verb{R/<version>} User-Agent.}
\item{\code{Require.snapshotInstallerHybrid}}{TRUE (default) -- pre-install
cache binaries via \code{install.packages(type = "binary")} before pak.}
\item{\code{Require.snapshotInstallerBumpOnFail}}{TRUE (default) -- walk newer
versions for refs that fail at the pin. FALSE for strict
reproducibility.}
\item{\code{Require.snapshotInstallerKnownFails}}{character vector of pkg
names to skip in bump-retry (e.g. environment-dependent refs whose
newer versions also won't help).}
\item{\code{Require.snapshotInstallerPakSilent}}{FALSE (default) -- pak's
resolver output reaches the user.}
\item{\code{Require.snapshotDownloadAttempts}}{Retry count for libcurl-multi
downloads. Default 4.}
\item{\code{Require.snapshotDownloadChunk}}{URLs per \code{download.file()} call.
Default 50 (stays under macOS's ~256 file-descriptor ulimit).}
}
}

\examples{
\donttest{
if (Require:::.runLongExamples()) {
  opts <- Require:::.setupExample()

  # install one archived version so that below does something interesting
  libForThisEx <- tempdir2("Example")
  Require("crayon (==1.5.1)", libPaths = libForThisEx, require = FALSE)
  # Normal use -- using the libForThisEx for example;
  #    normally libPaths would be omitted to get all
  #    packages in user or project library
  tf <- tempfile()

  # writes to getOption("Require.packageVersionFile")
  # within project; also returns a vector
  # of packages with version
  pkgs <- pkgSnapshot(
    packageVersionFile = tf,
    libPaths = libForThisEx, standAlone = TRUE # only this library
  )

  # Now move this file to another computer e.g. by committing in git,
  #   emailing, googledrive
  #   on next computer/project
  Require(packageVersionFile = tf, libPaths = libForThisEx)

  # Using pkgSnapshot2 to get the vector of packages and versions
  pkgs <- pkgSnapshot2(
    libPaths = libForThisEx, standAlone = TRUE
  )
  Install(pkgs) # will install packages from previous line

  Require:::.cleanup(opts)
  unlink(getOption("Require.packageVersionFile"))
}
}

}
