pkgbuild/0000755000176200001440000000000014575600302012055 5ustar liggesuserspkgbuild/NAMESPACE0000644000176200001440000000137614527147040013305 0ustar liggesusers# Generated by roxygen2: do not edit by hand export(build) export(check_build_tools) export(check_compiler) export(check_latex) export(check_rtools) export(clean_dll) export(compile_dll) export(compiler_flags) export(find_rtools) export(has_build_tools) export(has_compiler) export(has_devel) export(has_latex) export(has_rtools) export(local_build_tools) export(needs_compile) export(pkg_has_src) export(pkg_links_to_cpp11) export(pkg_links_to_rcpp) export(pkgbuild_process) export(rcmd_build_tools) export(rtools_needed) export(rtools_path) export(setup_rtools) export(with_build_tools) export(with_debug) export(with_latex) export(without_cache) export(without_compiler) export(without_latex) importFrom(R6,R6Class) importFrom(utils,head) importFrom(utils,tail) pkgbuild/LICENSE0000644000176200001440000000005614521162745013070 0ustar liggesusersYEAR: 2023 COPYRIGHT HOLDER: pkgbuild authors pkgbuild/README.md0000644000176200001440000001062214442272563013344 0ustar liggesusers# pkgbuild [![R-CMD-check](https://github.com/r-lib/pkgbuild/actions/workflows/R-CMD-check.yaml/badge.svg)](https://github.com/r-lib/pkgbuild/actions/workflows/R-CMD-check.yaml) [![Codecov test coverage](https://codecov.io/gh/r-lib/pkgbuild/branch/main/graph/badge.svg)](https://app.codecov.io/gh/r-lib/pkgbuild?branch=main) The goal of pkgbuild is to make it easy to build packages with compiled code. It provides tools to configure your R session, and check that everything is working ok. If you are using RStudio, it also helps you trigger automatic install of the build tools. ## Installation Install the released version from CRAN ```r install.packages("pkgbuild") ``` Or install the development version from GitHub: ```r # install.packages("pak") pak::pak("r-lib/pkgbuild") ``` ## Example ``` r # Check that you have the build tools installed pkgbuild::check_build_tools(debug = TRUE) # Build a package pkgbuild::build("/path/to/my/package") # Run your own code in an environment guaranteed to # have build tools available pkgbuild::with_build_tools(my_code) ``` ## Configuration ### `DESCRIPTION` entries * `Config/build/clean-inst-doc` can be set to `FALSE` to avoid cleaning up `inst/doc` when building a source package. Set it to `TRUE` to force a cleanup. See the `clean_doc` argument of `build()` as well. * `Config/build/copy-method` can be used to avoid copying large directories in `R CMD build`. It works by copying (or linking) the files of the package to a temporary directory, leaving out the (possibly large) files that are not part of the package. Possible values: - `none`: pkgbuild does not copy the package tree. This is the default. - `copy`: the package files are copied to a temporary directory before ` R CMD build`. - `link`: the package files are symbolic linked to a temporary directory before `R CMD build`. Windows does not have symbolic links, so on Windows this is equivalent to `copy`. You can also use the `pkg.build_copy_method` option or the `PKG_BUILD_COPY_METHOD` environment variable to set the copy method. The option is consulted first, then the `DESCRIPTION` entry, then the environment variable. * `Config/build/extra-sources` can be used to define extra source files for pkgbuild to decide whether a package DLL needs to be recompiled in `needs_compile()`. The syntax is a comma separated list of file names, or globs. (See `?utils::glob2rx()`.) E.g. `src/rust/src/*.rs` or `configure*`. ### Options * `pkg.build_copy_method`: use this option to avoid copying large directories when building a package. See possible values above, at the `Config/build/copy-method` `DESCRIPTION` entry. * `pkg.build_extra_flags`: set this to `FALSE` to to opt out from adding debug compiler flags in `compile_dll()`. Takes precedence over the `PKG_BUILD_EXTRA_FLAGS` environment variable. Possible values: - `TRUE`: add extra flags, - `FALSE`: do not add extra flags, - `"missing"`: add extra flags if the user does not have a `$HOME/.R/Makevars` file. * `pkg.build_stop_for_warnings`: if it is set to `TRUE`, then pkgbuild will stop for `R CMD build` errors. It takes precedence over the `PKG_BUILD_STOP_FOR_WARNINGS` environment variable. ### Environment variables * `PKG_BUILD_COLOR_DIAGNOSTICS`: set it to `false` to opt out of colored compiler diagnostics. Set it to `true` to force colored compiler diagnostics. * `PKG_BUILD_COPY_METHOD`: use this environment variable to avoid copying large directories when building a package. See possible values above, at the `Config/build/copy-method` `DESCRIPTION` entry. * `PKG_BUILD_EXTRA_FLAGS`: set this to `false` to to opt out from adding debug compiler flags in `compile_dll()`. The `pkg.build_extra_flags` option takes precedence over this environment variable. Possible values: - `"true"`: add extra flags, - `"false"`: do not add extra flags, - `"missing"`: add extra flags if the user does not have a `$HOME/.R/Makevars` file. * `PKG_BUILD_STOP_FOR_WARNINGS`: if it is set to `true`, then pkgbuild will stop for `R CMD build` errors. The `pkg.build_stop_for_warnings` option takes precedence over this environment variable. ## Code of Conduct Please note that the pkgbuild project is released with a [Contributor Code of Conduct](https://pkgbuild.r-lib.org/CODE_OF_CONDUCT.html). By contributing to this project, you agree to abide by its terms. pkgbuild/man/0000755000176200001440000000000014535333212012627 5ustar liggesuserspkgbuild/man/pkg_links_to_rcpp.Rd0000644000176200001440000000067514152340747016643 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/c-registration.R \name{pkg_links_to_rcpp} \alias{pkg_links_to_rcpp} \alias{pkg_links_to_cpp11} \title{Test if a package path is linking to Rcpp or cpp11} \usage{ pkg_links_to_rcpp(path) pkg_links_to_cpp11(path) } \arguments{ \item{path}{Path to a package, or within a package.} } \description{ Test if a package path is linking to Rcpp or cpp11 } \keyword{internal} pkgbuild/man/pkgbuild-package.Rd0000644000176200001440000000156014426741134016317 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/pkgbuild-package.R \docType{package} \name{pkgbuild-package} \alias{pkgbuild} \alias{pkgbuild-package} \title{pkgbuild: Find Tools Needed to Build R Packages} \description{ Provides functions used to build R packages. Locates compilers needed to build R packages on various platforms and ensures the PATH is configured appropriately so R can use them. } \seealso{ Useful links: \itemize{ \item \url{https://github.com/r-lib/pkgbuild} \item \url{https://pkgbuild.r-lib.org} \item Report bugs at \url{https://github.com/r-lib/pkgbuild/issues} } } \author{ \strong{Maintainer}: Gábor Csárdi \email{csardi.gabor@gmail.com} Authors: \itemize{ \item Hadley Wickham \item Jim Hester } Other contributors: \itemize{ \item Posit Software, PBC [copyright holder, funder] } } \keyword{internal} pkgbuild/man/has_build_tools.Rd0000644000176200001440000000414714426741134016303 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/build-tools.R \name{has_build_tools} \alias{has_build_tools} \alias{check_build_tools} \alias{with_build_tools} \alias{local_build_tools} \title{Are build tools are available?} \usage{ has_build_tools(debug = FALSE) check_build_tools(debug = FALSE, quiet = FALSE) with_build_tools(code, debug = FALSE, required = TRUE) local_build_tools( debug = FALSE, required = TRUE, .local_envir = parent.frame() ) } \arguments{ \item{debug}{If \code{TRUE}, will print out extra information useful for debugging. If \code{FALSE}, it will use result cached from a previous run.} \item{quiet}{if \code{TRUE} suppresses output from this function.} \item{code}{Code to rerun in environment where build tools are guaranteed to exist.} \item{required}{If \code{TRUE}, and build tools are not available, will throw an error. Otherwise will attempt to run \code{code} without them.} \item{.local_envir}{The environment to use for scoping.} } \description{ \code{has_build_tools} returns a logical, \code{check_build_tools} throws an error. \code{with_build_tools} checks that build tools are available, then runs \code{code} in an correctly staged environment. If run interactively from RStudio, and the build tools are not available these functions will trigger an automated install. } \details{ Errors like \verb{running command '"C:/PROGRA~1/R/R-34~1.2/bin/x64/R" CMD config CC' had status 127} indicate the code expected Rtools to be on the system PATH. You can then verify you have rtools installed with \code{has_build_tools()} and temporarily add Rtools to the PATH \code{with_build_tools({ code })}. It is possible to add Rtools to your system PATH manually; you can use \code{\link[=rtools_path]{rtools_path()}} to show the installed location. However because this requires manual updating when a new version of Rtools is installed and the binaries in Rtools may conflict with existing binaries elsewhere on the PATH it is better practice to use \code{with_build_tools()} as needed. } \examples{ has_build_tools(debug = TRUE) check_build_tools() } \seealso{ has_rtools } pkgbuild/man/pkg_has_src.Rd0000644000176200001440000000054214152340747015410 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/has_src.R \name{pkg_has_src} \alias{pkg_has_src} \title{Does a source package have \verb{src/} directory?} \usage{ pkg_has_src(path = ".") } \arguments{ \item{path}{Path to package (or directory within package).} } \description{ If it does, you definitely need build tools. } pkgbuild/man/without_compiler.Rd0000644000176200001440000000113614152340747016522 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/with-debug.R \name{without_compiler} \alias{without_compiler} \alias{without_cache} \alias{without_latex} \alias{with_latex} \title{Tools for testing pkgbuild} \usage{ without_compiler(code) without_cache(code) without_latex(code) with_latex(code) } \arguments{ \item{code}{Code to execute with broken compilers} } \description{ \code{with_compiler} temporarily disables code compilation by setting \code{CC}, \code{CXX}, makevars to \code{test}. \code{without_cache} resets the cache before and after running \code{code}. } pkgbuild/man/has_latex.Rd0000644000176200001440000000040714152340747015075 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/latex.R \name{has_latex} \alias{has_latex} \alias{check_latex} \title{Is latex installed?} \usage{ has_latex() check_latex() } \description{ Checks for presence of pdflatex on path. } pkgbuild/man/needs_compile.Rd0000644000176200001440000000072514152340747015736 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/compile-dll.R \name{needs_compile} \alias{needs_compile} \title{Does the package need recompiling? (i.e. is there a source or header file newer than the dll)} \usage{ needs_compile(path = ".") } \arguments{ \item{path}{Path to a package, or within a package.} } \description{ Does the package need recompiling? (i.e. is there a source or header file newer than the dll) } \keyword{internal} pkgbuild/man/compile_dll.Rd0000644000176200001440000000561714340630410015404 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/compile-dll.R \name{compile_dll} \alias{compile_dll} \title{Compile a .dll/.so from source.} \usage{ compile_dll( path = ".", force = FALSE, compile_attributes = pkg_links_to_cpp11(path) || pkg_links_to_rcpp(path), register_routines = FALSE, quiet = FALSE, debug = TRUE ) } \arguments{ \item{path}{Path to a package, or within a package.} \item{force}{If \code{TRUE}, for compilation even if \code{\link[=needs_compile]{needs_compile()}} is \code{FALSE}.} \item{compile_attributes}{if \code{TRUE} and the package uses Rcpp, call \code{\link[Rcpp:compileAttributes]{Rcpp::compileAttributes()}} before building the package. It is ignored if package does not need compilation.} \item{register_routines}{if \code{TRUE} and the package does not use Rcpp, call register routines with \code{tools::package_native_routine_registration_skeleton()} before building the package. It is ignored if package does not need compilation.} \item{quiet}{if \code{TRUE} suppresses output from this function.} \item{debug}{If \code{TRUE}, and if no user Makevars is found, then the build runs without optimisation (\code{-O0}) and with debug symbols (\code{-g}). See \code{\link[=compiler_flags]{compiler_flags()}} for details. If you have a user Makevars (e.g., \verb{~/.R/Makevars}) then this argument is ignored.} } \description{ \code{compile_dll} performs a fake R CMD install so code that works here should work with a regular install (and vice versa). During compilation, debug flags are set with \code{\link{compiler_flags}(TRUE)}. } \details{ Invisibly returns the names of the DLL. \subsection{Configuration}{ \subsection{Options}{ \itemize{ \item \code{pkg.build_extra_flags}: set this to \code{FALSE} to to opt out from adding debug compiler flags in \code{compile_dll()}. Takes precedence over the \code{PKG_BUILD_EXTRA_FLAGS} environment variable. Possible values: \itemize{ \item \code{TRUE}: add extra flags, \item \code{FALSE}: do not add extra flags, \item \code{"missing"}: add extra flags if the user does not have a \verb{$HOME/.R/Makevars} file. } } } \subsection{Environment variables}{ \itemize{ \item \code{PKG_BUILD_EXTRA_FLAGS}: set this to \code{false} to to opt out from adding debug compiler flags in \code{compile_dll()}. The \code{pkg.build_extra_flags} option takes precedence over this environment variable. Possible values: \itemize{ \item \code{"true"}: add extra flags, \item \code{"false"}: do not add extra flags, \item \code{"missing"}: add extra flags if the user does not have a \verb{$HOME/.R/Makevars} file. } } } } } \note{ If this is used to compile code that uses Rcpp, you will need to add the following line to your \code{Makevars} file so that it knows where to find the Rcpp headers: \verb{PKG_CPPFLAGS=}$(R_HOME)/bin/Rscript -e 'Rcpp:::CxxFlags()'`` } \seealso{ \code{\link[=clean_dll]{clean_dll()}} to delete the compiled files. } pkgbuild/man/has_compiler.Rd0000644000176200001440000000235614323725614015577 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/compiler.R \name{has_compiler} \alias{has_compiler} \alias{check_compiler} \alias{has_devel} \title{Is a compiler available?} \usage{ has_compiler(debug = FALSE) check_compiler(debug = FALSE) } \arguments{ \item{debug}{If \code{TRUE}, will print out extra information useful for debugging. If \code{FALSE}, it will use result cached from a previous run.} } \description{ These functions check if a small C file can be compiled, linked, loaded and executed. \code{has_compiler()} and \code{has_devel()} return \code{TRUE} or \code{FALSE}. \code{check_compiler()} and \code{check_devel()} throw an error if you don't have developer tools installed. If the \code{"pkgbuild.has_compiler"} option is set to \code{TRUE} or \code{FALSE}, no check is carried out, and the value of the option is used. The implementation is based on a suggestion by Simon Urbanek. End-users (particularly those on Windows) should generally run \code{\link[=check_build_tools]{check_build_tools()}} rather than \code{\link[=check_compiler]{check_compiler()}}. } \examples{ has_compiler() check_compiler() with_build_tools(has_compiler()) } \seealso{ \code{\link[=check_build_tools]{check_build_tools()}} } pkgbuild/man/clean_dll.Rd0000644000176200001440000000061714152340747015045 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/compile-dll.R \name{clean_dll} \alias{clean_dll} \title{Remove compiled objects from /src/ directory} \usage{ clean_dll(path = ".") } \arguments{ \item{path}{Path to a package, or within a package.} } \description{ Invisibly returns the names of the deleted files. } \seealso{ \code{\link[=compile_dll]{compile_dll()}} } pkgbuild/man/build.Rd0000644000176200001440000001330614366011511014215 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/build.R \name{build} \alias{build} \title{Build package} \usage{ build( path = ".", dest_path = NULL, binary = FALSE, vignettes = TRUE, manual = FALSE, clean_doc = NULL, args = NULL, quiet = FALSE, needs_compilation = pkg_has_src(path), compile_attributes = FALSE, register_routines = FALSE ) } \arguments{ \item{path}{Path to a package, or within a package.} \item{dest_path}{path in which to produce package. If it is an existing directory, then the output file is placed in \code{dest_path} and named according to the current R conversions (e.g. \code{.zip} for Windows binary packages, \code{.tgz} for macOS binary packages, etc). If it is an existing file, then it will be overwritten. If \code{dest_path} does not exist, then it is used as a file name. If \code{NULL}, it defaults to the parent directory of the package.} \item{binary}{Produce a binary (\code{--binary}) or source ( \code{--no-manual --no-resave-data}) version of the package.} \item{vignettes, manual}{For source packages: if \code{FALSE}, don't build PDF vignettes (\code{--no-build-vignettes}) or manual (\code{--no-manual}).} \item{clean_doc}{If \code{TRUE}, clean the files in \code{inst/doc} before building the package. If \code{NULL} and the \code{Config/build/clean-inst-doc} entry is present in \code{DESCRIPTION}, then that is used. Otherwise, if \code{NULL}, and interactive, ask to remove the files prior to cleaning. In most cases cleaning the files is the correct behavior to avoid stale vignette outputs in the built package.} \item{args}{An optional character vector of additional command line arguments to be passed to \verb{R CMD build} if \code{binary = FALSE}, or \verb{R CMD install} if \code{binary = TRUE}.} \item{quiet}{if \code{TRUE} suppresses output from this function.} \item{needs_compilation}{Usually only needed if the packages has C/C++/Fortran code. By default this is autodetected.} \item{compile_attributes}{if \code{TRUE} and the package uses Rcpp, call \code{\link[Rcpp:compileAttributes]{Rcpp::compileAttributes()}} before building the package. It is ignored if package does not need compilation.} \item{register_routines}{if \code{TRUE} and the package does not use Rcpp, call register routines with \code{tools::package_native_routine_registration_skeleton()} before building the package. It is ignored if package does not need compilation.} } \value{ a string giving the location (including file name) of the built package } \description{ Building converts a package source directory into a single bundled file. If \code{binary = FALSE} this creates a \code{tar.gz} package that can be installed on any platform, provided they have a full development environment (although packages without source code can typically be installed out of the box). If \code{binary = TRUE}, the package will have a platform specific extension (e.g. \code{.zip} for windows), and will only be installable on the current platform, but no development environment is needed. } \details{ \subsection{Configuration}{ \subsection{\code{DESCRIPTION} entries}{ \itemize{ \item \code{Config/build/clean-inst-doc} can be set to \code{FALSE} to avoid cleaning up \code{inst/doc} when building a source package. Set it to \code{TRUE} to force a cleanup. See the \code{clean_doc} argument. \item \code{Config/build/copy-method} can be used to avoid copying large directories in \verb{R CMD build}. It works by copying (or linking) the files of the package to a temporary directory, leaving out the (possibly large) files that are not part of the package. Possible values: \itemize{ \item \code{none}: pkgbuild does not copy the package tree. This is the default. \item \code{copy}: the package files are copied to a temporary directory before \verb{ R CMD build}. \item \code{link}: the package files are symbolic linked to a temporary directory before \verb{R CMD build}. Windows does not have symbolic links, so on Windows this is equivalent to \code{copy}. } You can also use the \code{pkg.build_copy_method} option or the \code{PKG_BUILD_COPY_METHOD} environment variable to set the copy method. The option is consulted first, then the \code{DESCRIPTION} entry, then the environment variable. \item \code{Config/build/extra-sources} can be used to define extra source files for pkgbuild to decide whether a package DLL needs to be recompiled in \code{needs_compile()}. The syntax is a comma separated list of file names, or globs. (See \code{\link[utils:glob2rx]{utils::glob2rx()}}.) E.g. \verb{src/rust/src/*.rs} or \verb{configure*}. \item \code{Config/build/bootstrap} can be set to \code{TRUE} to run \verb{Rscript bootstrap.R} in the source directory prior to running subsequent build steps. } } \subsection{Options}{ \itemize{ \item \code{pkg.build_copy_method}: use this option to avoid copying large directories when building a package. See possible values above, at the \code{Config/build/copy-method} \code{DESCRIPTION} entry. \item \code{pkg.build_stop_for_warnings}: if it is set to \code{TRUE}, then pkgbuild will stop for \verb{R CMD build} errors. It takes precedence over the \code{PKG_BUILD_STOP_FOR_WARNINGS} environment variable. } } \subsection{Environment variables}{ \itemize{ \item \code{PKG_BUILD_COLOR_DIAGNOSTICS}: set it to \code{false} to opt out of colored compiler diagnostics. Set it to \code{true} to force colored compiler diagnostics. \item \code{PKG_BUILD_COPY_METHOD}: use this environment variable to avoid copying large directories when building a package. See possible values above, at the \code{Config/build/copy-method} \code{DESCRIPTION} entry. } will stop for \verb{R CMD build} errors. The \code{pkg.build_stop_for_warnings} option takes precedence over this environment variable. } } } pkgbuild/man/has_rtools.Rd0000644000176200001440000000255614152340747015311 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/rtools-cache.R, R/rtools.R \name{rtools_path} \alias{rtools_path} \alias{has_rtools} \alias{find_rtools} \alias{setup_rtools} \alias{check_rtools} \title{Is Rtools installed?} \usage{ rtools_path() has_rtools(debug = FALSE) check_rtools(debug = FALSE) } \arguments{ \item{debug}{If \code{TRUE}, will print out extra information useful for debugging. If \code{FALSE}, it will use result cached from a previous run.} } \value{ Either a visible \code{TRUE} if rtools is found, or an invisible \code{FALSE} with a diagnostic \code{\link[=message]{message()}}. As a side-effect the internal package variable \code{rtools_path} is updated to the paths to rtools binaries. } \description{ To build binary packages on windows, Rtools (found at \url{https://CRAN.R-project.org/bin/windows/Rtools/}) needs to be on the path. The default installation process does not add it, so this script finds it (looking first on the path, then in the registry). It also checks that the version of rtools matches the version of R. \code{has_rtools()} determines if Rtools is installed, caching the results. Afterward, run \code{rtools_path()} to find out where it's installed. } \section{Acknowledgements}{ This code borrows heavily from RStudio's code for finding Rtools. Thanks JJ! } \examples{ has_rtools() } \keyword{internal} pkgbuild/man/compiler_flags.Rd0000644000176200001440000000170014535333212016102 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/compiler-flags.R \name{compiler_flags} \alias{compiler_flags} \title{Default compiler flags used by devtools.} \usage{ compiler_flags(debug = FALSE) } \arguments{ \item{debug}{If \code{TRUE} adds \code{-g -O0} to all flags (Adding \env{FFLAGS} and \env{FCFLAGS})} } \description{ These default flags enforce good coding practice by ensuring that \env{CFLAGS} and \env{CXXFLAGS} are set to \code{-Wall -pedantic}. These tests are run by cran and are generally considered to be good practice. } \details{ By default \code{\link[=compile_dll]{compile_dll()}} is run with \code{compiler_flags(TRUE)}, and check with \code{compiler_flags(FALSE)}. If you want to avoid the possible performance penalty from the debug flags, install the package. } \examples{ compiler_flags() compiler_flags(TRUE) } \seealso{ Other debugging flags: \code{\link{with_debug}()} } \concept{debugging flags} pkgbuild/man/rcmd_build_tools.Rd0000644000176200001440000000201014152340747016441 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/rcmd.R \name{rcmd_build_tools} \alias{rcmd_build_tools} \title{Call R CMD 'command' with build tools active} \usage{ rcmd_build_tools(..., env = character(), required = TRUE, quiet = FALSE) } \arguments{ \item{...}{Parameters passed on to \code{rcmd_safe}.} \item{env}{Additional environment variables to set. The defaults from \code{callr::rcmd_safe_env()} are always set.} \item{required}{If \code{TRUE}, and build tools are not available, will throw an error. Otherwise will attempt to run \code{code} without them.} \item{quiet}{if \code{TRUE} suppresses output from this function.} } \description{ This is a wrapper around \code{callr::rcmd_safe()} that checks that you have build tools available, and on Windows, automatically sets the path to include Rtools. } \examples{ # These env vars are always set callr::rcmd_safe_env() if (has_build_tools()) { rcmd_build_tools("CONFIG", "CC")$stdout rcmd_build_tools("CC", "--version")$stdout } } pkgbuild/man/with_debug.Rd0000644000176200001440000000162414535333212015242 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/with-debug.R \name{with_debug} \alias{with_debug} \title{Temporarily set debugging compilation flags.} \usage{ with_debug( code, CFLAGS = NULL, CXXFLAGS = NULL, FFLAGS = NULL, FCFLAGS = NULL, debug = TRUE ) } \arguments{ \item{code}{to execute.} \item{CFLAGS}{flags for compiling C code} \item{CXXFLAGS}{flags for compiling C++ code} \item{FFLAGS}{flags for compiling Fortran code.} \item{FCFLAGS}{flags for Fortran 9x code.} \item{debug}{If \code{TRUE} adds \code{-g -O0} to all flags (Adding \env{FFLAGS} and \env{FCFLAGS})} } \description{ Temporarily set debugging compilation flags. } \examples{ flags <- names(compiler_flags(TRUE)) with_debug(Sys.getenv(flags)) \dontrun{ install("mypkg") with_debug(install("mypkg")) } } \seealso{ Other debugging flags: \code{\link{compiler_flags}()} } \concept{debugging flags} pkgbuild/man/rtools_needed.Rd0000644000176200001440000000051414152340747015752 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/rtools.R \name{rtools_needed} \alias{rtools_needed} \title{Retrieve a text string with the rtools version needed} \usage{ rtools_needed(r_version = getRversion()) } \description{ Retrieve a text string with the rtools version needed } \keyword{internal} pkgbuild/man/pkgbuild_process.Rd0000644000176200001440000000256514323713030016460 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/build-bg.R \name{pkgbuild_process} \alias{pkgbuild_process} \title{Build package in the background} \description{ This R6 class is a counterpart of the \code{\link[=build]{build()}} function, and represents a background process that builds an R package. } \section{Usage}{ \if{html}{\out{
}}\preformatted{bp <- pkgbuild_process$new(path = ".", dest_path = NULL, binary = FALSE, vignettes = TRUE, manual = FALSE, args = NULL) bp$get_dest_path() }\if{html}{\out{
}} Other methods are inherited from \link[callr:rcmd_process]{callr::rcmd_process} and \code{processx::process}. } \section{Arguments}{ See the corresponding arguments of \code{\link[=build]{build()}}. } \section{Details}{ Most methods are inherited from \link[callr:rcmd_process]{callr::rcmd_process} and \code{processx::process}. \code{bp$get_dest_path()} returns the path to the built package. } \section{Examples}{ \if{html}{\out{
}}\preformatted{## Here we are just waiting, but in a more realistic example, you ## would probably run some other code instead... bp <- pkgbuild_process$new("mypackage", dest_path = tempdir()) bp$is_alive() bp$get_pid() bp$wait() bp$read_all_output_lines() bp$read_all_error_lines() bp$get_exit_status() bp$get_dest_path() }\if{html}{\out{
}} } pkgbuild/DESCRIPTION0000644000176200001440000000235314575600302013566 0ustar liggesusersPackage: pkgbuild Title: Find Tools Needed to Build R Packages Version: 1.4.4 Authors@R: c( person("Hadley", "Wickham", role = "aut"), person("Jim", "Hester", role = "aut"), person("Gábor", "Csárdi", , "csardi.gabor@gmail.com", role = c("aut", "cre")), person("Posit Software, PBC", role = c("cph", "fnd")) ) Description: Provides functions used to build R packages. Locates compilers needed to build R packages on various platforms and ensures the PATH is configured appropriately so R can use them. License: MIT + file LICENSE URL: https://github.com/r-lib/pkgbuild, https://pkgbuild.r-lib.org BugReports: https://github.com/r-lib/pkgbuild/issues Depends: R (>= 3.5) Imports: callr (>= 3.2.0), cli (>= 3.4.0), desc, processx, R6 Suggests: covr, cpp11, knitr, mockery, Rcpp, rmarkdown, testthat (>= 3.0.0), withr (>= 2.3.0) Config/Needs/website: tidyverse/tidytemplate Config/testthat/edition: 3 Encoding: UTF-8 RoxygenNote: 7.2.3 NeedsCompilation: no Packaged: 2024-03-17 14:13:01 UTC; gaborcsardi Author: Hadley Wickham [aut], Jim Hester [aut], Gábor Csárdi [aut, cre], Posit Software, PBC [cph, fnd] Maintainer: Gábor Csárdi Repository: CRAN Date/Publication: 2024-03-17 14:40:02 UTC pkgbuild/tests/0000755000176200001440000000000014526624156013230 5ustar liggesuserspkgbuild/tests/testthat/0000755000176200001440000000000014575600302015057 5ustar liggesuserspkgbuild/tests/testthat/test-withr.R0000644000176200001440000000171714442273510017322 0ustar liggesusers test_that("withr_with_makevars", { split_lines <- function(x) strsplit(x, "\n\r?")[[1]] orig <- split_lines(callr::rcmd("config", "CFLAGS")$stdout) new <- withr_with_makevars( new = c(CFLAGS = "-DFOO"), split_lines(callr::rcmd("config", "CFLAGS")$stdout) ) expect_equal(new, paste(orig, "-DFOO")) # with our own custom Makevars file tmp <- tempfile() on.exit(unlink(tmp), add = TRUE) writeLines(c( "CFLAGS=-DTHIS", "CXXFLAGS+=-DTHAT" ), tmp) orig <- split_lines(callr::rcmd("config", "CFLAGS")$stdout) new <- withr_with_makevars( new = c(CFLAGS = "-DFOO"), path = tmp, split_lines(callr::rcmd("config", "CFLAGS")$stdout) ) expect_equal(new, "-DTHIS -DFOO") orig <- split_lines(callr::rcmd("config", "CXXFLAGS")$stdout) new <- withr_with_makevars( new = c(CXXFLAGS = "-DFOO"), path = tmp, split_lines(callr::rcmd("config", "CXXFLAGS")$stdout) ) expect_equal(new, paste(orig, "-DTHAT -DFOO")) }) pkgbuild/tests/testthat/testDummy/0000755000176200001440000000000014152340747017057 5ustar liggesuserspkgbuild/tests/testthat/testDummy/NAMESPACE0000644000176200001440000000001214152340747020267 0ustar liggesusersexport(a) pkgbuild/tests/testthat/testDummy/DESCRIPTION0000644000176200001440000000027014152340747020564 0ustar liggesusersPackage: testDummy Title: Tools to make developing R code easier License: GPL-2 Description: Author: Hadley Maintainer: Hadley Version: 0.1 pkgbuild/tests/testthat/testDummy/R/0000755000176200001440000000000014521162621017251 5ustar liggesuserspkgbuild/tests/testthat/testDummy/R/a.R0000644000176200001440000000000714152340747017620 0ustar liggesusersa <- 1 pkgbuild/tests/testthat/testDummy/R/b.R0000644000176200001440000000000714152340747017621 0ustar liggesusersb <- 2 pkgbuild/tests/testthat/fixtures/0000755000176200001440000000000014152340747016735 5ustar liggesuserspkgbuild/tests/testthat/fixtures/xxx.zip0000644000176200001440000000024014152340747020304 0ustar liggesusersPK U8JgtxxxUT 0XXux xxx PK U8JgtxxxUT0Xux PKIApkgbuild/tests/testthat/fixtures/xxx.tar.gz0000644000176200001440000000021414152340747020710 0ustar liggesusers1XA 0F=ay-B87("y߄ݻT5(o"J0!ڠw˪|ϥVs.e_l?%MZpݿ{(pkgbuild/tests/testthat/fixtures/testWithSrc_0.1.tar.gz0000644000176200001440000000113514152340747022725 0ustar liggesusersXQk0^mI$JJ4t65NYMD+Hʶ2'; e[ФeB:q;sN0x'Q@8NV="kDPM)Peڅ2cSa:sjWWk6]7B86wSrV] ,fW:<\%'  õ\Z.+݅, ͯ_ldjq2YX}Bj]H b)sv#LxAY{ d"E1O0xH{k?`>?4;bƞTX3?mmG{Jԡ$RYQn}05 %?oמWJpOew[a.jqh<+Y| ( +trS·enGuTc-*l2Lj,*]Kep,JB3`yͭpr>v&nB ŭm4 '-ǎ3Ovvpkgbuild/tests/testthat/fixtures/xxx.gz0000644000176200001440000000003414152340747020123 0ustar liggesusers0Xxxxgtpkgbuild/tests/testthat/fixtures/testDummy_0.1.tar.gz0000644000176200001440000000063114152340747022435 0ustar liggesusersN0w8zkI @VFörHi֜õi/|;ְ;waA% anvDk:k419;ԆSR6*L%ۺl:uY#0?;&+Rf9$gr!˄gZpD ]zⶹX!̤%_AcV}|4g"2v(L'潨>n|!2VH6Iz !B8n]]owFfsGA)^XO]He.إzҟGh?:ZàQlCo`X-xfpkgbuild/tests/testthat/test-rtools.R0000644000176200001440000000167714410345632017514 0ustar liggesusers test_that("has_rtools finds rtools", { skip_if_not(is_windows() && !is.null(scan_path_for_rtools())) # Rtools path can be looked up by the PATH without_cache({ expect_true(has_rtools()) expect_true(!is.null(scan_path_for_rtools())) }) withr::with_path(Sys.getenv("R_HOME"), action = "replace", { expect_equal(scan_path_for_rtools(), NULL) }) skip_if_not(!is.null(scan_registry_for_rtools())) # Rtools path can be looked up from the registery expect_true(!is.null(scan_registry_for_rtools())) without_cache( withr::with_path(Sys.getenv("R_HOME"), action = "replace", { has_rtools() expect_true(rtools_path() != "") }) ) }) test_that("rtools_needed works", { skip_if_not(is_windows()) # Test only frozen versions expect_equal(rtools_needed("3.6.3"), "Rtools 3.5") expect_equal(rtools_needed("2.9"), "Rtools 3.0") expect_equal(rtools_needed("0.0.0"), "the appropriate version of Rtools") }) pkgbuild/tests/testthat/test-build.R0000644000176200001440000002304214575567076017303 0ustar liggesusers # Package build setup ---------------------------------------------------- test_that("with*_latex context fixtures force has_latex result", { expect_true(with_latex(has_latex())) expect_false(without_latex(has_latex())) # one of them should be different from default expect_true({ with_latex(has_latex()) != has_latex() || without_latex(has_latex()) != has_latex() }) }) # expect `manual=TRUE` & empty `args` not to add build --no-manual flag test_that("source build setup accept args and/or parameterized helpers", { expect_silent(res <- with_latex({ build_setup_source( file.path(testthat::test_path(), "testDummy"), file.path(tempdir(), "testDummyBuild"), vignettes = FALSE, manual = TRUE, clean_doc = FALSE, args = c(), needs_compilation = FALSE ) })) expect_true(!"--no-manual" %in% res$args) # expect `manual=FALSE` to affect build --no-manual flag expect_silent(res <- with_latex({ build_setup_source( file.path(testthat::test_path(), "testDummy"), file.path(tempdir(), "testDummyBuild"), vignettes = FALSE, manual = FALSE, clean_doc = FALSE, args = c(), needs_compilation = FALSE ) })) expect_true("--no-manual" %in% res$args) # expect `args` "--no-manual" to affect build --no-manual flag expect_silent(res <- with_latex({ build_setup_source( file.path(testthat::test_path(), "testDummy"), file.path(tempdir(), "testDummyBuild"), vignettes = FALSE, manual = TRUE, clean_doc = FALSE, args = c("--no-manual"), needs_compilation = FALSE ) })) expect_true("--no-manual" %in% res$args) expect_silent(res <- build_setup_source( file.path(testthat::test_path(), "testDummy"), file.path(tempdir(), "testDummyBuild"), vignettes = TRUE, manual = FALSE, clean_doc = FALSE, args = c(), needs_compilation = FALSE )) expect_true(!"--no-build-vignettes" %in% res$args) # expect `vignettes=FALSE` to affect build --no-build-vignettes flag expect_silent(res <- build_setup_source( file.path(testthat::test_path(), "testDummy"), file.path(tempdir(), "testDummyBuild"), vignettes = FALSE, manual = FALSE, clean_doc = FALSE, args = c(), needs_compilation = FALSE )) expect_true("--no-build-vignettes" %in% res$args) # expect `arg` `--no-build-vignettes` to produce --no-build-vignettes flag expect_silent(res <- build_setup_source( file.path(testthat::test_path(), "testDummy"), file.path(tempdir(), "testDummyBuild"), vignettes = TRUE, manual = FALSE, clean_doc = FALSE, args = c("--no-build-vignettes"), needs_compilation = FALSE )) expect_true("--no-build-vignettes" %in% res$args) }) # Package without source code -------------------------------------------- test_that("source builds return correct filenames", { path <- build("testDummy", dest_path = tempdir(), quiet = TRUE) on.exit(unlink(path)) expect_true(file.exists(path)) expect_false(is.na(desc::desc(path)$get("Packaged"))) expect_true(is.na(desc::desc(path)$get("Built"))) }) test_that("binary builds return correct filenames", { path <- build("testDummy", binary = TRUE, dest_path = tempdir(), quiet = TRUE) on.exit(unlink(path)) expect_true(file.exists(path)) }) test_that("can build package without src without compiler", { without_compiler({ path <- build("testDummy", binary = TRUE, dest_path = tempdir(), quiet = TRUE) on.exit(unlink(path)) expect_true(file.exists(path)) }) }) # Package with src code --------------------------------------------------- test_that("source builds return correct filenames", { path <- build("testWithSrc", dest_path = tempdir(), quiet = TRUE, register_routines = FALSE) on.exit(unlink(path)) expect_true(file.exists(path)) }) test_that("build package with src requires compiler", { without_compiler({ expect_error( build("testWithSrc", dest_path = tempdir(), quiet = TRUE), "Could not find tools" ) }) }) # Package files ----------------------------------------------------------- test_that("package tarball binary build", { path <- build("testDummy", dest_path = tempdir(), quiet = TRUE) on.exit(unlink(path), add = TRUE) path2 <- build(path, dest_path = tempdir(), quiet = TRUE, binary = TRUE, needs_compilation = FALSE, compile_attributes = FALSE ) on.exit(unlink(path2), add = TRUE) expect_true(file.exists(path2)) expect_false(is.na(desc::desc(path2)$get("Packaged"))) expect_false(is.na(desc::desc(path2)$get("Built"))) }) test_that("package tarball binary build errors", { path <- build("testDummy", dest_path = tempdir(), quiet = TRUE) on.exit(unlink(path), add = TRUE) expect_error( build(path, dest_path = tempdir(), quiet = TRUE), "binary" ) expect_error( build(path, dest_path = tempdir(), quiet = TRUE, binary = TRUE, needs_compilation = FALSE, compile_attributes = TRUE ), "compile_attributes" ) }) test_that("warnings can be turned into errors", { src <- withr::local_tempdir() dest <- withr::local_tempdir() file.copy(test_path("testDummy"), src, recursive = TRUE) withr::local_options(pkg.build_stop_for_warnings = TRUE) expect_silent( build(file.path(src, "testDummy"), dest_path = dest, quiet = TRUE) ) dir.create(file.path(src, "testDummy", "inst"), recursive = TRUE, showWarnings = FALSE) saveRDS(1:10, file.path(src, "testDummy", "inst", "testthat-problems.rds")) # No warning/error on R <= 3.5 if (getRversion() <= "3.6") skip("Needs R 3.5.0") # Warning looks different on older R if (getRversion() >= "4.1") { expect_snapshot( error = TRUE, build(file.path(src, "testDummy"), dest_path = dest, quiet = TRUE), transform = function(x) { x <- sub("\u2018", "'", x, fixed = TRUE) x <- sub("\u2019", "'", x, fixed = TRUE) x <- sub("checking for file '.*'", "checking for file ''", x) x } ) } else { expect_error( suppressMessages(build( file.path(src, "testDummy"), dest_path = dest, quiet = TRUE )) ) } }) test_that("Config/build/clean-inst-doc FALSE", { if (Sys.which("pandoc") == "") skip("No pandoc") dest <- withr::local_tempdir() expect_silent( build( test_path("testInstDoc"), dest_path = dest, quiet = TRUE, vignettes = TRUE, clean_doc = NULL ) ) pkg <- file.path(dest, dir(dest)) expect_true(length(pkg) == 1) expect_true(file.exists(pkg)) pkg_files <- untar(pkg, list = TRUE) expect_true("testInstDoc/inst/doc/keep.me" %in% pkg_files) expect_true("testInstDoc/inst/doc/test.html" %in% pkg_files) }) test_that("Config/build/clean-inst-doc TRUE", { if (Sys.which("pandoc") == "") skip("No pandoc") src <- withr::local_tempdir() dest <- withr::local_tempdir() file.copy(test_path("testInstDoc"), src, recursive = TRUE) desc::desc_set( "Config/build/clean-inst-doc" = "TRUE", file = file.path(src, "testInstDoc") ) expect_silent( build( file.path(src, "testInstDoc"), dest_path = dest, quiet = TRUE, vignettes = TRUE, clean_doc = NULL ) ) pkg <- file.path(dest, dir(dest)) expect_true(length(pkg) == 1) expect_true(file.exists(pkg)) pkg_files <- untar(pkg, list = TRUE) expect_false("testInstDoc/inst/doc/keep.me" %in% pkg_files) expect_true("testInstDoc/inst/doc/test.html" %in% pkg_files) }) test_that("bootstrap.R runs on build if present", { src <- withr::local_tempdir() dest <- withr::local_tempdir() file.copy(test_path("testDummy"), src, recursive = TRUE) writeLines( c( 'dir.create("inst")', 'file.create("inst/file-created-by-bootstrap.txt")' ), file.path(src, "testDummy", "bootstrap.R") ) desc::desc_set( "Config/build/bootstrap" = "TRUE", file = file.path(src, "testDummy") ) expect_silent( build( file.path(src, "testDummy"), dest_path = dest, quiet = TRUE ) ) pkg <- file.path(dest, dir(dest)) expect_true(length(pkg) == 1) expect_true(file.exists(pkg)) pkg_files <- untar(pkg, list = TRUE) expect_true("testDummy/inst/file-created-by-bootstrap.txt" %in% pkg_files) }) test_that("bootstrap.R does not run if Config/build/bootstrap is not TRUE", { src <- withr::local_tempdir() dest <- withr::local_tempdir() file.copy(test_path("testDummy"), src, recursive = TRUE) writeLines( c( 'dir.create("inst")', 'file.create("inst/file-created-by-bootstrap.txt")' ), file.path(src, "testDummy", "bootstrap.R") ) expect_silent( build( file.path(src, "testDummy"), dest_path = dest, quiet = TRUE ) ) pkg <- file.path(dest, dir(dest)) expect_true(length(pkg) == 1) expect_true(file.exists(pkg)) pkg_files <- untar(pkg, list = TRUE) expect_false("testDummy/inst/file-created-by-bootstrap.txt" %in% pkg_files) }) test_that("bootstrap.R can output stdout, stderr, and warnings when run", { src <- withr::local_tempdir() dest <- withr::local_tempdir() file.copy(test_path("testDummy"), src, recursive = TRUE) writeLines( c( 'message("output on stderr")', 'cat("output on stdout\\n")', 'warning("this is a warning")' ), file.path(src, "testDummy", "bootstrap.R") ) desc::desc_set( "Config/build/bootstrap" = "TRUE", file = file.path(src, "testDummy") ) expect_output( expect_message( build( file.path(src, "testDummy"), dest_path = dest ), "Running bootstrap.R" ), "output on stderr.*?output on stdout.*?this is a warning" ) }) pkgbuild/tests/testthat/test-c-registration.R0000644000176200001440000000435014211634664021120 0ustar liggesusers test_that("update_c_registration does nothing if an init.c file already exists", { skip_if(getRversion() < "3.4") init_file <- test_path("testWithSrc", "src", "init.c") on.exit(unlink(init_file)) writeLines(' #include #include #include // for NULL #include /* .Call calls */ extern SEXP add1(SEXP); extern SEXP mult2(SEXP); static const R_CallMethodDef CallEntries[] = { {"add1", (DL_FUNC) &add1, 1}, {"mult2", (DL_FUNC) &mult2, 1}, {NULL, NULL, 0} }; void R_init_testWithSrc(DllInfo *dll) { R_registerRoutines(dll, NULL, CallEntries, NULL, NULL); R_useDynamicSymbols(dll, FALSE); }', init_file) expect_equal( update_c_registration(test_path("testWithSrc")), character() ) }) test_that("update_c_registration works", { skip_if(getRversion() < "3.4") init_file <- test_path("testWithSrc", "src", "init.c") on.exit(unlink(init_file)) expect_false(file.exists(init_file)) # Should return with no lines if there are no routines called expect_equal(update_c_registration(test_path("testWithSrc")), character()) # Add a call and try to update again src_file <- test_path("testWithSrc", "R", "c.R") writeLines(' add1 <- function(x) { .Call("add1", x) }', src_file) on.exit(unlink(src_file), add = TRUE) init_lines <- update_c_registration(test_path("testWithSrc")) expect_true(any(grepl("generated by pkgbuild", init_lines))) expect_true(any(grepl('"add1",.*[(]DL_FUNC[)] &add1', init_lines))) # update_c_registration should be idempotent if nothing has changed expect_equal( update_c_registration(test_path("testWithSrc")), init_lines ) writeLines(' add1 <- function(x) { .Call("add1", x) } mult2 <- function(x) { .Call("mult2", x) }', src_file) # update_c_registration should be idempotent if nothing has changed update_c_registration(test_path("testWithSrc")) init_src <- readLines(init_file) expect_true(any(grepl("generated by pkgbuild", init_src))) expect_true(any(grepl('"add1",.*[(]DL_FUNC[)] &add1', init_src))) expect_true(any(grepl('"mult2",.*[(]DL_FUNC[)] &mult2', init_src))) }) test_that("check_namespace_registration", { expect_warning(check_namespace_registration(test_path("testWithSrc"))) }) pkgbuild/tests/testthat/testInstDoc/0000755000176200001440000000000014337377564017343 5ustar liggesuserspkgbuild/tests/testthat/testInstDoc/NAMESPACE0000644000176200001440000000001214337377564020553 0ustar liggesusersexport(a) pkgbuild/tests/testthat/testInstDoc/DESCRIPTION0000644000176200001440000000043314337377564021051 0ustar liggesusersPackage: testInstDoc Title: Tools to make developing R code easier License: GPL-2 Description: Author: Hadley Maintainer: Hadley Version: 0.1 Config/build/clean-inst-doc: FALSE VignetteBuilder: knitr Suggests: knitr, rmarkdown pkgbuild/tests/testthat/testInstDoc/vignettes/0000755000176200001440000000000014337377564021353 5ustar liggesuserspkgbuild/tests/testthat/testInstDoc/vignettes/test.Rmd0000644000176200001440000000054714337377564023004 0ustar liggesusers--- title: "pkgbuild test" output: rmarkdown::html_vignette description: > This is a vignette to be used in the pkgbuild tests. vignette: > %\VignetteIndexEntry{pkgbuild test} %\VignetteEngine{knitr::rmarkdown} \usepackage[utf8]{inputenc} --- ```{r, setup, include = FALSE} knitr::opts_chunk$set(collapse = TRUE, comment = "#>") ``` ```{r} 1:10 ``` pkgbuild/tests/testthat/testInstDoc/R/0000755000176200001440000000000014521162602017520 5ustar liggesuserspkgbuild/tests/testthat/testInstDoc/R/a.R0000644000176200001440000000000714337377564020104 0ustar liggesusersa <- 1 pkgbuild/tests/testthat/testInstDoc/R/b.R0000644000176200001440000000000714337377564020105 0ustar liggesusersb <- 2 pkgbuild/tests/testthat/testInstDoc/inst/0000755000176200001440000000000014337377564020320 5ustar liggesuserspkgbuild/tests/testthat/testInstDoc/inst/doc/0000755000176200001440000000000014337377564021065 5ustar liggesuserspkgbuild/tests/testthat/testInstDoc/inst/doc/keep.me0000644000176200001440000000001714337377564022332 0ustar liggesusersPretty please. pkgbuild/tests/testthat/test-compiler.R0000644000176200001440000000227414323725614020003 0ustar liggesusers describe("has_compiler", { withr::local_options(pkgbuild.has_compiler = NULL) testthat::local_reproducible_output() it("succeeds if a compiler exists", { skip_if(is_windows() && !has_rtools()) without_cache({ without_compiler({ expect_false(has_compiler()) expect_error(check_compiler(), "Failed to compile C code") }) cache_reset() expect_true(has_compiler()) expect_true(check_compiler()) }) }) it("returns the value of the has_compiler option", { skip_if(is_windows() && !has_rtools()) without_cache({ without_compiler({ withr::with_options( c(pkgbuild.has_compiler = TRUE), { expect_true(has_compiler()) expect_error(check_compiler(), NA) } ) }) cache_reset() withr::with_options( c(pkgbuild.has_compiler = FALSE), { expect_false(has_compiler()) expect_error(check_compiler(), "Failed to compile C code") } ) withr::with_options( list(pkgbuild.has_compiler = 1:2), expect_snapshot( error = TRUE, has_compiler() ) ) }) }) }) pkgbuild/tests/testthat/test-build-process.R0000644000176200001440000000674014410345632020741 0ustar liggesusers # Package without source code -------------------------------------------- test_that("source builds return correct filenames", { dir.create(tmp <- tempfile()) on.exit(unlink(tmp, recursive = TRUE), add = TRUE) pr <- pkgbuild_process$new("testDummy", dest_path = tmp) pr$wait(60000) if (pr$is_alive()) { pr$kill() skip("has not finished in one minute") } expect_true(file.exists(pr$get_dest_path())) expect_true(file.exists(pr$get_built_file())) expect_true(!is.na(desc::desc(pr$get_built_file())$get("Packaged"))) }) test_that("binary builds return correct filenames", { # building binaries also installs them to the library, so we need to skip on # CRAN. skip_on_cran() dir.create(tmp <- tempfile()) on.exit(unlink(tmp, recursive = TRUE), add = TRUE) pr <- pkgbuild_process$new("testDummy", binary = TRUE, dest_path = tmp) pr$wait(60000) if (pr$is_alive()) { pr$kill() skip("has not finished in one minute") } expect_true(file.exists(pr$get_dest_path())) expect_true(file.exists(pr$get_built_file())) expect_true(!is.na(desc::desc(pr$get_built_file())$get("Built"))) }) test_that("can build package without src without compiler", { # building binaries also installs them to the library, so we need to skip on # CRAN. skip_on_cran() dir.create(tmp <- tempfile()) on.exit(unlink(tmp, recursive = TRUE)) without_compiler({ pr <- pkgbuild_process$new("testDummy", binary = TRUE, dest_path = tmp) pr$wait(60000) if (pr$is_alive()) { pr$kill() skip("has not finished in one minute") } expect_true(file.exists(pr$get_dest_path())) expect_true(file.exists(pr$get_built_file())) expect_true(!is.na(desc::desc(pr$get_built_file())$get("Built"))) }) }) # Package with src code --------------------------------------------------- test_that("source builds return correct filenames", { dir.create(tmp <- tempfile()) on.exit(unlink(tmp, recursive = TRUE), add = TRUE) pr <- pkgbuild_process$new("testWithSrc", dest_path = tmp, register_routines = FALSE) pr$wait(60000) if (pr$is_alive()) { pr$kill() skip("has not finished in one minute") } expect_true(file.exists(pr$get_dest_path())) expect_true(file.exists(pr$get_built_file())) expect_true(!is.na(desc::desc(pr$get_built_file())$get("Packaged"))) }) test_that("build package with src requires compiler", { without_compiler({ expect_error( { pr <- pkgbuild_process$new("testWithSrc", dest_path = tempdir(), register_routines = FALSE) pr$kill() }, "Could not find tools" ) }) }) test_that("can get output, exit status, etc.", { dir.create(tmp <- tempfile()) on.exit(unlink(tmp, recursive = TRUE), add = TRUE) pr <- pkgbuild_process$new("testDummy", dest_path = tmp) pr$wait(60000) if (pr$is_alive()) { pr$kill() skip("has not finished in one minute") } out <- pr$read_all_output() expect_match(out, "* building", fixed = TRUE) expect_error(err <- pr$read_all_error(), NA) expect_equal(pr$get_exit_status(), 0) path <- pr$get_dest_path() on.exit(unlink(path)) }) test_that("can kill a build process", { dir.create(tmp <- tempfile()) on.exit(unlink(tmp, recursive = TRUE), add = TRUE) pr <- pkgbuild_process$new("testDummy", dest_path = tmp) ret <- pr$kill() if (!ret) skip("build finished before we could kill it") ex_stat <- pr$get_exit_status() if (.Platform$OS.type == "unix") { expect_equal(ex_stat, -9) } else { expect_true(ex_stat != 0) } }) pkgbuild/tests/testthat/test-exclude.R0000644000176200001440000001074614340633232017616 0ustar liggesusers test_that("copy_package_tree creates package dir", { tmp <- withr::local_tempdir("pkgbuild-test-") # link withr::local_options(pkg.build_copy_method = "link") unlink(tmp, recursive = TRUE) expect_no_warning( copy_package_tree(test_path("testDummy"), tmp) ) expect_snapshot( sort(dir(tmp, recursive = TRUE, include.dirs = TRUE)) ) # copy withr::local_options(pkg.build_copy_method = "copy") unlink(tmp, recursive = TRUE) expect_no_warning( copy_package_tree(test_path("testDummy"), tmp) ) expect_snapshot( sort(dir(tmp, recursive = TRUE, include.dirs = TRUE)) ) }) test_that("copy_package_tree errors", { tmp <- withr::local_tempdir("pkgbuild-test-") mkdirp(file.path(tmp, "testDummy")) expect_error( copy_package_tree(test_path("testDummy"), tmp), "already exists" ) }) test_that("exclusions", { pkgdir <- file.path(withr::local_tempdir("pkgbuild-test-"), "pkg") mkdirp(pkgdir) writeLines( c("Package: pkg", "Version: 1.0.0"), file.path(pkgdir, "DESCRIPTION") ) # create some structure with files to ignore and keep writeLines(c( "^docs$", "^src/.*[.]o$" ), file.path(pkgdir, ".Rbuildignore")) mkdirp(file.path(pkgdir, "src")) file.create(file.path(pkgdir, "src", "src.c")) file.create(file.path(pkgdir, "src", "src.o")) mkdirp(file.path(pkgdir, "docs")) file.create(file.path(pkgdir, "docs", "foo")) file.create(file.path(pkgdir, "src", ".DS_Store")) file.create(file.path(pkgdir, ".Rhistory")) file.create(file.path(pkgdir, ".RData")) file.create(file.path(pkgdir, "src", "foo.c~")) file.create(file.path(pkgdir, "src", "foo.c.bak")) file.create(file.path(pkgdir, "src", "foo.c.swp")) file.create(file.path(pkgdir, "src", "foo.d")) dir.create(file.path(pkgdir, ".git")) file.create(file.path(pkgdir, ".git", "foo")) files <- build_files(pkgdir) expect_snapshot(files[, c("path", "exclude", "isdir", "trimmed")]) dest <- withr::local_tempdir("pkgbuild-test-") copy_package_tree(pkgdir, dest) }) test_that("get_copy_method", { mockery::stub(get_copy_method, "is_windows", TRUE) withr::local_options(pkg.build_copy_method = "link") expect_equal(get_copy_method(), "copy") withr::local_options(pkg.build_copy_method = "foobar") expect_error(get_copy_method(), "pkg.build_copy_method") withr::local_options(pkg.build_copy_method = 1:10) expect_error(get_copy_method(), "It must be a string") withr::local_options(pkg.build_copy_method = NULL) mockery::stub(get_copy_method, "desc::desc_get", "link") expect_equal(get_copy_method(), "copy") }) test_that("Ignoring .Rbuildignore", { pkgdir <- file.path(withr::local_tempdir("pkgbuild-test-"), "pkg") mkdirp(pkgdir) writeLines( c("Package: pkg", "Version: 1.0.0"), file.path(pkgdir, "DESCRIPTION") ) # create some structure with files to ignore and keep writeLines(c( "^docs$", "^src/.*[.]o$", "^\\.Rbuildignore$" ), file.path(pkgdir, ".Rbuildignore")) mkdirp(file.path(pkgdir, "src")) file.create(file.path(pkgdir, "src", "src.c")) file.create(file.path(pkgdir, "src", "src.o")) mkdirp(file.path(pkgdir, "docs")) file.create(file.path(pkgdir, "docs", "foo")) files <- build_files(pkgdir) expect_snapshot(files[, c("path", "exclude", "isdir", "trimmed")]) }) test_that("copying on windows", { # this works on Unix as well environment(cp)$wind <- TRUE on.exit(environment(cp)$wind <- NULL, add = TRUE) tmp <- withr::local_tempdir("pkgbuild-test-") # link withr::local_options(pkg.build_copy_method = "copy") unlink(tmp, recursive = TRUE) expect_no_warning( copy_package_tree(test_path("testDummy"), tmp) ) expect_snapshot( sort(dir(tmp, recursive = TRUE, include.dirs = TRUE)) ) }) test_that("cp error", { environment(cp)$wind <- TRUE on.exit(environment(cp)$wind <- NULL, add = TRUE) withr::local_dir(withr::local_tempdir()) expect_snapshot(error = TRUE, cp("foo", "bar")) }) test_that("detect_cp_args", { mockery::stub( detect_cp_args, "processx::run", function(...) stop("nope") ) expect_snapshot(detect_cp_args()) mockery::stub( detect_cp_args, "processx::run", function(f1, f2) file.create(f2) ) expect_snapshot(detect_cp_args()) }) test_that("cp error on Unix", { skip_on_os("windows") withr::local_dir(withr::local_tempdir()) expect_snapshot( error = TRUE, cp("foo", "bar"), # do not include the cp output because it is slightly different on # macOS, Linux, etc. transform = function(x) utils::head(x, 3) ) }) pkgbuild/tests/testthat/test-build_tools.R0000644000176200001440000000046614211634664020511 0ustar liggesusers test_that("tests always run in environment with dev tools", { without_cache({ expect_true(has_build_tools()) expect_equal(has_rtools(), is_windows()) }) }) test_that("unless specifically disabled", { without_compiler({ expect_false(has_build_tools()) expect_false(has_rtools()) }) }) pkgbuild/tests/testthat/testWithSrc/0000755000176200001440000000000014152340747017347 5ustar liggesuserspkgbuild/tests/testthat/testWithSrc/NAMESPACE0000644000176200001440000000001214152340747020557 0ustar liggesusersexport(a) pkgbuild/tests/testthat/testWithSrc/DESCRIPTION0000644000176200001440000000027214152340747021056 0ustar liggesusersPackage: testWithSrc Title: Tools to make developing R code easier License: GPL-2 Description: Author: Hadley Maintainer: Hadley Version: 0.1 pkgbuild/tests/testthat/testWithSrc/src/0000755000176200001440000000000014527147120020132 5ustar liggesuserspkgbuild/tests/testthat/testWithSrc/src/add1.c0000644000176200001440000000056514152340747021121 0ustar liggesusers// In C ---------------------------------------- #include #include SEXP add1(SEXP a) { SEXP result = PROTECT(allocVector(REALSXP, 1)); REAL(result)[0] = asReal(a) + 1; UNPROTECT(1); return result; } SEXP mult2(SEXP a) { SEXP result = PROTECT(allocVector(REALSXP, 1)); REAL(result)[0] = asReal(a) * 2; UNPROTECT(1); return result; } pkgbuild/tests/testthat/testWithSrc/R/0000755000176200001440000000000014527147117017552 5ustar liggesuserspkgbuild/tests/testthat/testWithSrc/R/a.R0000644000176200001440000000000714152340747020110 0ustar liggesusersa <- 1 pkgbuild/tests/testthat/testWithSrc/R/b.R0000644000176200001440000000000714152340747020111 0ustar liggesusersb <- 2 pkgbuild/tests/testthat/_snaps/0000755000176200001440000000000014526624207016350 5ustar liggesuserspkgbuild/tests/testthat/_snaps/exclude.md0000644000176200001440000000467414527147121020332 0ustar liggesusers# copy_package_tree creates package dir Code sort(dir(tmp, recursive = TRUE, include.dirs = TRUE)) Output [1] "testDummy" "testDummy/DESCRIPTION" "testDummy/NAMESPACE" [4] "testDummy/R" "testDummy/R/a.R" "testDummy/R/b.R" --- Code sort(dir(tmp, recursive = TRUE, include.dirs = TRUE)) Output [1] "testDummy" "testDummy/DESCRIPTION" "testDummy/NAMESPACE" [4] "testDummy/R" "testDummy/R/a.R" "testDummy/R/b.R" # exclusions Code files[, c("path", "exclude", "isdir", "trimmed")] Output path exclude isdir trimmed 1 .RData TRUE FALSE FALSE 2 .Rbuildignore FALSE FALSE FALSE 3 .Rhistory TRUE FALSE FALSE 4 .git TRUE TRUE FALSE 5 DESCRIPTION FALSE FALSE FALSE 6 docs TRUE TRUE FALSE 7 src FALSE TRUE TRUE 8 src/.DS_Store TRUE FALSE FALSE 9 src/foo.c.bak TRUE FALSE FALSE 10 src/foo.c.swp TRUE FALSE FALSE 11 src/foo.c~ TRUE FALSE FALSE 12 src/foo.d TRUE FALSE FALSE 13 src/src.c FALSE FALSE FALSE 14 src/src.o TRUE FALSE FALSE # Ignoring .Rbuildignore Code files[, c("path", "exclude", "isdir", "trimmed")] Output path exclude isdir trimmed 1 .Rbuildignore FALSE FALSE FALSE 2 DESCRIPTION FALSE FALSE FALSE 3 docs TRUE TRUE FALSE 4 src FALSE TRUE TRUE 5 src/src.c FALSE FALSE FALSE 6 src/src.o TRUE FALSE FALSE # copying on windows Code sort(dir(tmp, recursive = TRUE, include.dirs = TRUE)) Output [1] "testDummy" "testDummy/DESCRIPTION" "testDummy/NAMESPACE" [4] "testDummy/R" "testDummy/R/a.R" "testDummy/R/b.R" # cp error Code cp("foo", "bar") Condition Error in `cp()`: ! Could not copy package files. i Failed to copy 'foo' to 'bar'. # detect_cp_args Code detect_cp_args() Output [1] "-pLR" --- Code detect_cp_args() Output [1] "-LR" "--preserve=timestamps" # cp error on Unix Code cp("foo", "bar") Condition Error in `cp()`: ! Could not copy package files. i Failed to copy 'foo' to 'bar'. pkgbuild/tests/testthat/_snaps/build.md0000644000176200001440000000163414527147117017776 0ustar liggesusers# warnings can be turned into errors Code build(file.path(src, "testDummy"), dest_path = dest, quiet = TRUE) Message ! Stopping as requested for a warning during `R CMD build`. ! The full output is printed below. * checking for file '' ... OK * preparing 'testDummy': * checking DESCRIPTION meta-information ... OK * checking for LF line-endings in source and make files and shell scripts * checking for empty or unneeded directories NB: this package now depends on R (>= 3.5.0) WARNING: Added dependency on R >= 3.5.0 because serialized objects in serialize/load version 3 cannot be read in older versions of R. File(s) containing such objects: 'testDummy/inst/testthat-problems.rds' * building 'testDummy_0.1.tar.gz' Condition Error in `force()`: ! converted from `R CMD build` warning. pkgbuild/tests/testthat/_snaps/utils.md0000644000176200001440000000130014527147122020021 0ustar liggesusers# should_add_compiler_flags errors Code should_add_compiler_flags() Condition Error in `should_add_compiler_flags()`: ! Invalid `pkg.build_extra_flags` option. i It must be `TRUE`, `FALSE` or "missing", not an integer vector. --- Code should_add_compiler_flags() Condition Error in `should_add_compiler_flags()`: ! Invalid `pkg_build_extra_flags` option. i It must be `TRUE`, `FALSE` or "missing", not "foobar". --- Code should_add_compiler_flags() Condition Error in `should_add_compiler_flags()`: ! Invalid `PKG_BUILD_EXTRA_FLAGS` environment variable. i Must be one of `true`, `false` or `missing`. pkgbuild/tests/testthat/_snaps/style.md0000644000176200001440000000053514527147121020031 0ustar liggesusers# style Code cat(style(ok = "OK", "\n", note = "NOTE", "\n", warn = "WARN", "\n", err = "ERROR", "\n", pale = "Not important", "\n", timing = "[1m]", "\n")) Output OK NOTE WARN ERROR Not important [1m] pkgbuild/tests/testthat/_snaps/compiler.md0000644000176200001440000000040214527147121020474 0ustar liggesusers# has_compiler: returns the value of the has_compiler option Code has_compiler() Condition Error in `has_compiler()`: ! ! Invalid `pkgbuild.has_compiler` option. i It must be `TRUE` or `FALSE`, not an integer vector. pkgbuild/tests/testthat/test-compile_dll.R0000644000176200001440000000034214211634664020446 0ustar liggesusers test_that("can compile a DLL and clean up afterwards", { expect_error(compile_dll("testWithSrc", quiet = TRUE, register_routines = FALSE), NA) clean_dll("testWithSrc") expect_equal(dir("testWithSrc/src"), "add1.c") }) pkgbuild/tests/testthat/test-style.R0000644000176200001440000000043214526624204017321 0ustar liggesuserstest_that("style", { withr::local_options(cli.num_colors = 256) expect_snapshot( cat(style( ok = "OK", "\n", note = "NOTE", "\n", warn = "WARN", "\n", err = "ERROR", "\n", pale = "Not important", "\n", timing = "[1m]", "\n" )) ) }) pkgbuild/tests/testthat/test-utils.R0000644000176200001440000000754014340630410017316 0ustar liggesusers test_that("should_stop_for_warnings", { withr::local_options(pkg.build_stop_for_warnings = NULL) withr::local_envvar(PKG_BUILD_STOP_FOR_WARNINGS = NA_character_) expect_false(should_stop_for_warnings()) withr::local_options(pkg.build_stop_for_warnings = FALSE) withr::local_envvar(PKG_BUILD_STOP_FOR_WARNINGS = "true") expect_false(should_stop_for_warnings()) withr::local_options(pkg.build_stop_for_warnings = TRUE) withr::local_envvar(PKG_BUILD_STOP_FOR_WARNINGS = "false") expect_true(should_stop_for_warnings()) withr::local_options(pkg.build_stop_for_warnings = NULL) withr::local_envvar(PKG_BUILD_STOP_FOR_WARNINGS = "true") expect_true(should_stop_for_warnings()) withr::local_options(pkg.build_stop_for_warnings = NULL) withr::local_envvar(PKG_BUILD_STOP_FOR_WARNINGS = "false") expect_false(should_stop_for_warnings()) withr::local_options(pkg.build_stop_for_warnings = 1:10) withr::local_envvar(PKG_BUILD_STOP_FOR_WARNINGS = "false") expect_error(should_stop_for_warnings(), "option must be") withr::local_options(pkg.build_stop_for_warnings = NULL) withr::local_envvar(PKG_BUILD_STOP_FOR_WARNINGS = "foobar") expect_error(should_stop_for_warnings(), "environment variable must be") }) test_that("isFALSE", { pos <- list(FALSE, structure(FALSE, class = "foo")) neg <- list(1, c(FALSE, TRUE), NA, list(FALSE)) for (p in pos) expect_true(isFALSE(p), info = p) for (n in neg) expect_false(isFALSE(n), info = n) }) test_that("should_add_compiler_flags", { # should not be called if option is set mockery::stub( should_add_compiler_flags, "makevars_user", function() stop("dont") ) # options is TRUE withr::local_options(pkg.build_extra_flags = TRUE) expect_true(should_add_compiler_flags()) # options is FALSE withr::local_options(pkg.build_extra_flags = FALSE) expect_false(should_add_compiler_flags()) # depends on whether Makevars exists withr::local_options(pkg.build_extra_flags = "missing") mockery::stub( should_add_compiler_flags, "makevars_user", function() character() ) expect_true(should_add_compiler_flags()) mockery::stub( should_add_compiler_flags, "makevars_user", function() "foobar" ) withr::local_options(pkg.build_extra_flags = "missing") expect_false(should_add_compiler_flags()) mockery::stub( should_add_compiler_flags, "makevars_user", function() stop("dont") ) withr::local_options(pkg.build_extra_flags = NULL) # env var true withr::local_envvar(PKG_BUILD_EXTRA_FLAGS = "true") expect_true(should_add_compiler_flags()) # env var false withr::local_envvar(PKG_BUILD_EXTRA_FLAGS = "false") expect_false(should_add_compiler_flags()) # depends on whether Makevars exists withr::local_envvar(PKG_BUILD_EXTRA_FLAGS = "missing") mockery::stub( should_add_compiler_flags, "makevars_user", function() character() ) expect_true(should_add_compiler_flags()) mockery::stub( should_add_compiler_flags, "makevars_user", function() "foobar" ) expect_false(should_add_compiler_flags()) # no option or env var, then TRUE mockery::stub( should_add_compiler_flags, "makevars_user", function() stop("dont") ) withr::local_options(pkg.build_extra_flags = NULL) withr::local_envvar(PKG_BUILD_EXTRA_FLAGS = NA_character_) expect_true(should_add_compiler_flags()) }) test_that("should_add_compiler_flags errors", { # invalid option type withr::local_options(pkg.build_extra_flags = 1:10) expect_snapshot(error = TRUE, should_add_compiler_flags()) # invalid option value withr::local_options(pkg.build_extra_flags = "foobar") expect_snapshot(error = TRUE, should_add_compiler_flags()) # invalid env var value withr::local_options(pkg.build_extra_flags = NULL) withr::local_envvar(PKG_BUILD_EXTRA_FLAGS = "foo") expect_snapshot(error = TRUE, should_add_compiler_flags()) }) pkgbuild/tests/testthat/test-compiler-flags.R0000644000176200001440000000100114337373664021071 0ustar liggesusers test_that("has_compiler_colored_diagnostics", { mockery::stub( has_compiler_colored_diagnostics, "cache_exists", function(...) stop("nope") ) withr::local_envvar(PKG_BUILD_COLOR_DIAGNOSTICS = "true") expect_true(has_compiler_colored_diagnostics()) withr::local_envvar(PKG_BUILD_COLOR_DIAGNOSTICS = "false") expect_false(has_compiler_colored_diagnostics()) withr::local_envvar(PKG_BUILD_COLOR_DIAGNOSTICS = NA_character_) expect_error(has_compiler_colored_diagnostics(), "nope") }) pkgbuild/tests/testthat/test-archives.R0000644000176200001440000000177614211634664020003 0ustar liggesusers test_that("is_zip_file", { expect_true(is_zip_file(file.path("fixtures", "xxx.zip"))) expect_false(is_zip_file(file.path("fixtures", "xxx.gz"))) expect_false(is_zip_file(file.path("fixtures", "xxx.tar.gz"))) }) test_that("is_gz_file", { expect_false(is_gz_file(file.path("fixtures", "xxx.zip"))) expect_true(is_gz_file(file.path("fixtures", "xxx.gz"))) expect_true(is_gz_file(file.path("fixtures", "xxx.tar.gz"))) }) test_that("is_tar_gz_file", { expect_false(is_tar_gz_file(file.path("fixtures", "xxx.zip"))) expect_false(is_tar_gz_file(file.path("fixtures", "xxx.gz"))) expect_true(is_tar_gz_file(file.path("fixtures", "xxx.tar.gz"))) }) test_that("pkg_has_src", { expect_false(pkg_has_src(file.path("fixtures", "testDummy_0.1.tar.gz"))) expect_true(pkg_has_src(file.path("fixtures", "testWithSrc_0.1.tar.gz"))) }) test_that("pkg_has_src on non-package files", { expect_error(pkg_has_src(file.path("fixtures", "xxx.zip"))) expect_error(pkg_has_src(file.path("fixtures", "xxx.tar.gz"))) }) pkgbuild/tests/testthat/test-find-package-root.R0000644000176200001440000000200414526673307021460 0ustar liggesuserstest_that("find_package_root", { tmp <- tempfile() on.exit(unlink(tmp, recursive = TRUE), add = TRUE) mkdirp(file.path(tmp, "a", "b", "c", "d")) lns <- "Package: this" writeLines(lns, file.path(tmp, "DESCRIPTION")) expect_equal( readLines(file.path(find_package_root(tmp), "DESCRIPTION")), lns ) expect_equal( readLines(file.path( find_package_root(file.path(tmp, "a")), "DESCRIPTION" )), lns ) expect_equal( readLines(file.path( find_package_root(file.path(tmp, "a", "b", "c", "d")), "DESCRIPTION" )), lns ) wd <- getwd() on.exit(setwd(wd), add = TRUE) setwd(file.path(tmp, "a", "b", "c")) expect_equal( readLines(file.path(find_package_root("."), "DESCRIPTION")), lns ) }) test_that("find_package_root errors", { expect_error( find_package_root(basename(tempfile())), "Path does not exist" ) if (!file.exists("/DESCRIPTION")) { expect_error( find_package_root("/"), "Could not find R package" ) } }) pkgbuild/tests/build-tools.R0000644000176200001440000000006314152340747015603 0ustar liggesuserslibrary(pkgbuild) check_build_tools(debug = TRUE) pkgbuild/tests/testthat.R0000644000176200001440000000007414152340747015210 0ustar liggesuserslibrary(testthat) library(pkgbuild) test_check("pkgbuild") pkgbuild/R/0000755000176200001440000000000014575566553012301 5ustar liggesuserspkgbuild/R/pkgbuild-package.R0000644000176200001440000000013514211634664015577 0ustar liggesusers#' @keywords internal "_PACKAGE" ## usethis namespace: start ## usethis namespace: end NULL pkgbuild/R/rtools-cache.R0000644000176200001440000000143314211634664014772 0ustar liggesusers#' @export #' @rdname has_rtools rtools_path <- function() { if (!is_windows()) { return(NA_character_) } if (!rtools_path_is_set()) { has_rtools() } cache_get("rtools_path") } rtools_path_is_set <- function() { cache_exists("rtools_path") } rtools_path_set <- function(rtools) { stopifnot(is.rtools(rtools)) path <- file.path(rtools$path, version_info[[rtools$version]]$path) # If using gcc49 and _without_ a valid BINPREF already set # Do NOT set BINPREF anymore for R 4.0 / rtools40 if (!is_R4() && using_gcc49() && is.null(rtools$valid_binpref)) { Sys.setenv(BINPREF = file.path(rtools$path, "mingw_$(WIN)", "bin", "/")) } cache_set("rtools_path", path) } using_gcc49 <- function() { grepl("4.9.3", Sys.getenv("R_COMPILED_BY"), fixed = TRUE) } pkgbuild/R/utils.R0000644000176200001440000001366414526627124013562 0ustar liggesusersdir.exists <- function(x) { res <- file.exists(x) & file.info(x)$isdir stats::setNames(res, x) } pkg_path <- function(path = ".") { find_package_root(path) } pkg_name <- function(path = ".") { desc::desc_get("Package", pkg_path(path))[[1]] } gcc_arch <- function() { if (Sys.getenv("R_ARCH") == "/i386") "32" else "64" } is_windows <- function() { .Platform$OS.type == "windows" } is_string <- function(x) { is.character(x) && length(x) == 1 && !is.na(x) } is_na <- function(x) { identical(x, NA) || identical(x, NA_integer_) || identical(x, NA_character_) || identical(x, NA_real_) || identical(x, NA_complex_) } is_dir <- function(x) { isTRUE(file.info(x)$isdir) } # This is tools::makevars_user, provided here for backwards compatibility with older versions of R makevars_user <- function() { m <- character() if (.Platform$OS.type == "windows") { if (!is.na(f <- Sys.getenv("R_MAKEVARS_USER", NA_character_))) { if (file.exists(f)) { m <- f } } else if ((Sys.getenv("R_ARCH") == "/x64") && file.exists(f <- path.expand("~/.R/Makevars.win64"))) { m <- f } else if (file.exists(f <- path.expand("~/.R/Makevars.win"))) { m <- f } else if (file.exists(f <- path.expand("~/.R/Makevars"))) { m <- f } } else { if (!is.na(f <- Sys.getenv("R_MAKEVARS_USER", NA_character_))) { if (file.exists(f)) { m <- f } } else if (file.exists(f <- path.expand(paste0( "~/.R/Makevars-", Sys.getenv("R_PLATFORM") )))) { m <- f } else if (file.exists(f <- path.expand("~/.R/Makevars"))) { m <- f } } m } last_char <- function(x) { l <- nchar(x) substr(x, l, l) } cat0 <- function(..., sep = "") { cat(..., sep = "") } is_flag <- function(x) { is.logical(x) && length(x) == 1 && !is.na(x) } flag_true_values <- c("true", "yes", "on", "1") flag_false_values <- c("false", "no", "off", "0") interpret_envvar_flag <- function(name, default = "false") { env <- tolower(Sys.getenv(name, default)) if (env %in% flag_true_values) { return(TRUE) } if (env %in% flag_false_values) { return(FALSE) } if (is.na(env)) { return(NA) } stop(cli::format_error( "The {.envvar {name}} environment variable must be {.code true} or {.code false}, if set." )) } get_config_flag_value <- function(name, default = FALSE) { option_name <- paste0("pkg.build_", tolower(name)) opt <- getOption(option_name, NULL) if (!is.null(opt)) { if (!is_flag(opt)) { stop(cli::format_error( "The {.code {option_name}} option must be {.code TRUE} or {.code FALSE}, if set." )) } return(opt) } envvar_name <- paste0("PKG_BUILD_", toupper(name)) interpret_envvar_flag(envvar_name, default = tolower(as.character(default))) } should_stop_for_warnings <- function() { get_config_flag_value("stop_for_warnings") } isFALSE <- function(x) { is.logical(x) && length(x) == 1L && !is.na(x) && !x } should_add_compiler_flags <- function() { val <- getOption("pkg.build_extra_flags", NULL) if (isTRUE(val)) { return(TRUE) } if (isFALSE(val)) { return(FALSE) } if (identical(val, "missing")) { return(length(makevars_user()) == 0) } if (!is.null(val)) { if (!is_string(val)) { stop(cli::format_error(c( "Invalid {.code pkg.build_extra_flags} option.", i = "It must be {.code TRUE}, {.code FALSE} or {.str missing}, not {.type {val}}." ))) } else { stop(cli::format_error(c( "Invalid {.code pkg_build_extra_flags} option.", i = "It must be {.code TRUE}, {.code FALSE} or {.str missing}, not {.str {val}}." ))) } } val <- Sys.getenv("PKG_BUILD_EXTRA_FLAGS", "true") if (val %in% flag_true_values) { return(TRUE) } if (val %in% flag_false_values) { return(FALSE) } if (val %in% "missing") { return(length(makevars_user()) == 0) } stop(cli::format_error(c( "Invalid {.envvar PKG_BUILD_EXTRA_FLAGS} environment variable.", i = "Must be one of {.code true}, {.code false} or {.code missing}." ))) } get_desc_config_flag <- function(path, name) { name <- paste0("Config/build/", name) val <- desc::desc_get(name, file = path) if (is.na(val)) { return(NULL) } lval <- tolower(val) if (lval %in% flag_true_values) { return(TRUE) } if (lval %in% flag_false_values) { return(FALSE) } stop(cli::format_error( "The {.code {name}} entry in {.path DESCRIPTION} must be {.code TRUE} or {.code FALSE}.", "i" = "It is {.val {val}}." )) } mkdirp <- function(path, mode = NULL) { if (file.exists(path)) { if (file.info(path)$isdir) { if (is.null(mode)) { return() } mode <- as.octmode(mode) emode <- as.octmode(file.info(path)$mode) if (emode == mode) { return() } ret <- Sys.chmod(path, mode, use_umask = FALSE) if (!ret) { stop(cli::format_error(c( "Path {.path {path}} exists, but could not update mode to {.code {mode}} from {.code {emode}}." ))) } return() } stop(cli::format_error(c( "Could not create directory {.path {path}}.", i = "Path already exists, but it is not a directory." ))) } if (is.null(mode)) mode <- "0777" wrg <- NULL withCallingHandlers( ret <- dir.create( path, showWarnings = TRUE, recursive = TRUE, mode = mode ), warning = function(w) { wrg <<- w if (!is.null(findRestart("muffleWarning"))) { invokeRestart("muffleWarning") } } ) if (!ret) { stop(cli::format_error(c( "Could not create directory {.path {path}}.", i = if (!is.null(wrg)) { "From {.fn dir.create}: {conditionMessage(wrg)}." } else { "For reasons unknown." } ))) } } verb_for_cli <- function(x) { x <- gsub("\n", "\f", x, fixed = TRUE) x <- gsub(" ", "\u00a0", x, fixed = TRUE) x } pkgbuild/R/build-bg.R0000644000176200001440000000763514527146742014113 0ustar liggesusers#' Build package in the background #' #' This R6 class is a counterpart of the [build()] function, and #' represents a background process that builds an R package. #' #' @section Usage: #' ``` #' bp <- pkgbuild_process$new(path = ".", dest_path = NULL, #' binary = FALSE, vignettes = TRUE, manual = FALSE, args = NULL) #' bp$get_dest_path() #' ``` #' #' Other methods are inherited from [callr::rcmd_process] and #' `processx::process`. #' #' @section Arguments: #' See the corresponding arguments of [build()]. #' #' @section Details: #' Most methods are inherited from [callr::rcmd_process] and #' `processx::process`. #' #' `bp$get_dest_path()` returns the path to the built package. #' #' @section Examples: #' ``` #' ## Here we are just waiting, but in a more realistic example, you #' ## would probably run some other code instead... #' bp <- pkgbuild_process$new("mypackage", dest_path = tempdir()) #' bp$is_alive() #' bp$get_pid() #' bp$wait() #' bp$read_all_output_lines() #' bp$read_all_error_lines() #' bp$get_exit_status() #' bp$get_dest_path() #' ``` #' #' @importFrom R6 R6Class #' @name pkgbuild_process NULL #' @export pkgbuild_process <- R6Class( "pkgbuild_process", inherit = callr::rcmd_process, public = list( initialize = function(path = ".", dest_path = NULL, binary = FALSE, vignettes = TRUE, manual = FALSE, clean_doc = NULL, args = NULL, needs_compilation = pkg_has_src(path), compile_attributes = FALSE, register_routines = FALSE, quiet = FALSE) { rcb_init( self, private, super, path, dest_path, binary, vignettes, manual, clean_doc, args, needs_compilation, compile_attributes, register_routines, quiet ) }, finalize = function() { unlink(private$makevars_file) super$kill() }, is_incomplete_error = function() FALSE, read_all_error = function() "", read_all_error_lines = function() character(), read_error = function(n = -1) "", read_error_lines = function(n = -1) character(), get_dest_path = function() private$dest_path, get_built_file = function() { if (self$is_alive()) stop("Still alive") if (self$get_exit_status() != 0) stop("Build process failed") ## Already copied? if (!is.null(private$out_file)) { return(private$out_file) } ## No, copy, and remove temp dir, order is important here! file_name <- dir(private$out_dir) tmp_file <- file.path(private$out_dir, file_name) file.copy(tmp_file, private$dest_path, overwrite = TRUE) private$out_file <- file.path(private$dest_path, file_name) unlink(private$out_dir, recursive = TRUE) private$out_file }, kill = function(...) { unlink(private$makevars_file) super$kill(...) } ), private = list( path = NULL, dest_path = NULL, out_dir = NULL, out_file = NULL, makevars_file = NULL ) ) rcb_init <- function(self, private, super, path, dest_path, binary, vignettes, manual, clean_doc, args, needs_compilation, compile_attributes, register_routines, quiet) { options <- build_setup( path, dest_path, binary, vignettes, manual, clean_doc, args, needs_compilation, compile_attributes, register_routines, quiet ) private$path <- options$path private$dest_path <- options$dest_path private$out_dir <- options$out_dir private$makevars_file <- tempfile() ## Build tools already checked in setup withr_set_makevars(compiler_flags(debug = FALSE), new_path = private$makevars_file) withr_with_envvar( c("R_MAKEVARS_USER" = private$makevars_file), { options <- callr::rcmd_process_options( cmd = options$cmd, cmdargs = c(options$path, options$args), wd = options$out_dir, stderr = "2>&1" ) super$initialize(options) invisible(self) } ) } pkgbuild/R/with-debug.R0000644000176200001440000000356514426741134014455 0ustar liggesusers#' Temporarily set debugging compilation flags. #' #' @param code to execute. #' @param CFLAGS flags for compiling C code #' @param CXXFLAGS flags for compiling C++ code #' @param FFLAGS flags for compiling Fortran code. #' @param FCFLAGS flags for Fortran 9x code. #' @inheritParams compiler_flags #' @family debugging flags #' @export #' @examples #' flags <- names(compiler_flags(TRUE)) #' with_debug(Sys.getenv(flags)) #' \dontrun{ #' install("mypkg") #' with_debug(install("mypkg")) #' } with_debug <- function(code, CFLAGS = NULL, CXXFLAGS = NULL, FFLAGS = NULL, FCFLAGS = NULL, debug = TRUE) { defaults <- compiler_flags(debug = debug) flags <- c( CFLAGS = CFLAGS, CXXFLAGS = CXXFLAGS, FFLAGS = FFLAGS, FCFLAGS = FCFLAGS ) flags <- unlist(utils::modifyList(as.list(defaults), as.list(flags))) withr_with_makevars(flags, code) } #' Tools for testing pkgbuild #' #' `with_compiler` temporarily disables code compilation by setting #' `CC`, `CXX`, makevars to `test`. `without_cache` #' resets the cache before and after running `code`. #' #' @param code Code to execute with broken compilers #' @export without_compiler <- function(code) { flags <- c( CC = "test", CXX = "test", CXX11 = "test", FC = "test" ) if (is_windows()) { without_cache({ cache_set("rtools_path", "") withr_with_makevars(flags, code) }) } else { without_cache({ withr_with_makevars(flags, code) }) } } #' @export #' @rdname without_compiler without_cache <- function(code) { cache_reset() on.exit(cache_reset()) code } #' @export #' @rdname without_compiler without_latex <- function(code) { withr_with_options(list(PKGBUILD_TEST_FIXTURE_HAS_LATEX = FALSE), code) } #' @export #' @rdname without_compiler with_latex <- function(code) { withr_with_options(list(PKGBUILD_TEST_FIXTURE_HAS_LATEX = TRUE), code) } pkgbuild/R/styles.R0000644000176200001440000000122514526623107013730 0ustar liggesusers# This is from https://github.com/r-lib/rcmdcheck/blob/7ee14764c2b17ee2c2f4131a9e19d1b56a66ed0f/R/styles.R style <- function(..., sep = "") { args <- list(...) st <- names(args) styles <- list( "ok" = cli::col_green, "note" = cli::make_ansi_style("orange"), "warn" = function(x) cli::style_bold(cli::make_ansi_style("orange")(x)), "err" = cli::col_red, "pale" = cli::make_ansi_style("darkgrey"), "timing" = cli::make_ansi_style("cyan") ) nms <- names(args) x <- lapply(seq_along(args), function(i) { if (nzchar(nms[i])) styles[[nms[i]]](args[[i]]) else args[[i]] }) paste(unlist(x), collapse = sep) } pkgbuild/R/rtools-metadata.R0000644000176200001440000000357214442114442015506 0ustar liggesusersversion_info <- list( "2.11" = list( version_min = "2.10.0", version_max = "2.11.1", path = c("bin", "perl/bin", "MinGW/bin") ), "2.12" = list( version_min = "2.12.0", version_max = "2.12.2", path = c("bin", "perl/bin", "MinGW/bin", "MinGW64/bin") ), "2.13" = list( version_min = "2.13.0", version_max = "2.13.2", path = c("bin", "MinGW/bin", "MinGW64/bin") ), "2.14" = list( version_min = "2.13.0", version_max = "2.14.2", path = c("bin", "MinGW/bin", "MinGW64/bin") ), "2.15" = list( version_min = "2.14.2", version_max = "2.15.1", path = c("bin", "gcc-4.6.3/bin") ), "2.16" = list( version_min = "2.15.2", version_max = "3.0.0", path = c("bin", "gcc-4.6.3/bin") ), "3.0" = list( version_min = "2.15.2", version_max = "3.0.99", path = c("bin", "gcc-4.6.3/bin") ), "3.1" = list( version_min = "3.0.0", version_max = "3.1.99", path = c("bin", "gcc-4.6.3/bin") ), "3.2" = list( version_min = "3.1.0", version_max = "3.2.99", path = c("bin", "gcc-4.6.3/bin") ), "3.3" = list( version_min = "3.2.0", version_max = "3.3.99", path = if (using_gcc49()) { "bin" } else { c("bin", "gcc-4.6.3/bin") } ), "3.4" = list( version_min = "3.3.0", version_max = "3.6.3", path = "bin" ), "3.5" = list( version_min = "3.3.0", version_max = "3.6.3", path = "bin" ), "4.0" = list( version_min = "4.0.0", version_max = "4.1.99", path = c("usr/bin", "ucrt64/bin") ), "4.2" = list( version_min = "4.2.0", version_max = "4.2.99", path = "usr/bin" ), "4.3" = list( version_min = "4.3.0", version_max = "99.99.99", path = "usr/bin" ), "custom" = list( version_min = "2.10.0", version_max = "99.99.99", path = if (getRversion() >= "4.0.0") "usr/bin" else "bin" ) ) pkgbuild/R/has_src.R0000644000176200001440000000321714211634664014033 0ustar liggesusers#' Does a source package have `src/` directory? #' #' If it does, you definitely need build tools. #' #' @param path Path to package (or directory within package). #' @export pkg_has_src <- function(path = ".") { if (is_dir(path)) { src_path <- file.path(pkg_path(path), "src") file.exists(src_path) } else { tryCatch( { files <- if (is_zip_file(path)) { utils::unzip(path, list = TRUE)$Name } else if (is_tar_gz_file(path)) { utils::untar(path, list = TRUE) } else { stop("not a zip or tar.gz file") } if (!any(grepl("^[^/]+/DESCRIPTION$", files))) { stop("no DESCRIPTION file") } any(grepl("^[^/]+/src/?$", files)) }, error = function(e) { e$message <- paste( path, "is not a valid package archive file,", e$message ) stop(e) } ) } } is_zip_file <- function(file) { buf <- readBin(file, what = "raw", n = 4) length(buf) == 4 && buf[1] == 0x50 && buf[2] == 0x4b && (buf[3] == 0x03 || buf[3] == 0x05 || buf[5] == 0x07) && (buf[4] == 0x04 || buf[4] == 0x06 || buf[4] == 0x08) } is_gz_file <- function(file) { buf <- readBin(file, what = "raw", n = 3) length(buf) == 3 && buf[1] == 0x1f && buf[2] == 0x8b && buf[3] == 0x08 } is_tar_gz_file <- function(file) { if (!is_gz_file(file)) { return(FALSE) } con <- gzfile(file, open = "rb") on.exit(close(con)) buf <- readBin(con, what = "raw", n = 262) length(buf) == 262 && buf[258] == 0x75 && buf[259] == 0x73 && buf[260] == 0x74 && buf[261] == 0x61 && buf[262] == 0x72 } pkgbuild/R/compile-dll.R0000644000176200001440000001512614442271546014616 0ustar liggesusers#' Compile a .dll/.so from source. #' #' `compile_dll` performs a fake R CMD install so code that #' works here should work with a regular install (and vice versa). #' During compilation, debug flags are set with #' \code{\link{compiler_flags}(TRUE)}. #' #' Invisibly returns the names of the DLL. #' #' ## Configuration #' #' ### Options #' #' * `pkg.build_extra_flags`: set this to `FALSE` to to opt out from adding #' debug compiler flags in `compile_dll()`. Takes precedence over the #' `PKG_BUILD_EXTRA_FLAGS` environment variable. Possible values: #' #' - `TRUE`: add extra flags, #' - `FALSE`: do not add extra flags, #' - `"missing"`: add extra flags if the user does not have a #' `$HOME/.R/Makevars` file. #' #' ### Environment variables #' #' * `PKG_BUILD_EXTRA_FLAGS`: set this to `false` to to opt out from adding #' debug compiler flags in `compile_dll()`. The `pkg.build_extra_flags` option #' takes precedence over this environment variable. Possible values: #' #' - `"true"`: add extra flags, #' - `"false"`: do not add extra flags, #' - `"missing"`: add extra flags if the user does not have a #' `$HOME/.R/Makevars` file. #' #' @note If this is used to compile code that uses Rcpp, you will need to #' add the following line to your `Makevars` file so that it #' knows where to find the Rcpp headers: #' `PKG_CPPFLAGS=`$(R_HOME)/bin/Rscript -e 'Rcpp:::CxxFlags()'`` #' #' @inheritParams build #' @param force If `TRUE`, for compilation even if [needs_compile()] is #' `FALSE`. #' @param debug If `TRUE`, and if no user Makevars is found, then the build #' runs without optimisation (`-O0`) and with debug symbols (`-g`). See #' [compiler_flags()] for details. If you have a user Makevars (e.g., #' `~/.R/Makevars`) then this argument is ignored. #' @seealso [clean_dll()] to delete the compiled files. #' @export compile_dll <- function(path = ".", force = FALSE, compile_attributes = pkg_links_to_cpp11(path) || pkg_links_to_rcpp(path), register_routines = FALSE, quiet = FALSE, debug = TRUE) { path <- pkg_path(path) if (!needs_compile(path) && !isTRUE(force)) { return(invisible()) } check_build_tools(quiet = TRUE) update_registration(path, compile_attributes, register_routines, quiet) # Mock install the package to generate the DLL xflags <- should_add_compiler_flags() if (!quiet) { cli::cli_alert_info(c( "Re-compiling {.pkg {pkg_name(path)}}", if (debug && xflags) " (debug build)" )) } install_dir <- tempfile("devtools_install_") dir.create(install_dir) build <- function() { install_min( path, dest = install_dir, components = "libs", args = if (needs_clean(path)) "--preclean", quiet = quiet ) invisible(dll_path(file.path(install_dir, pkg_name(path)))) } if (xflags) { withr_with_makevars(compiler_flags(debug), { if (debug) { withr_with_envvar(c(DEBUG = "true"), build()) } else { build() } }) } else { build() } } #' Remove compiled objects from /src/ directory #' #' Invisibly returns the names of the deleted files. #' #' @inheritParams build #' @seealso [compile_dll()] #' @export clean_dll <- function(path = ".") { path <- pkg_path(path) # Clean out the /src/ directory and children: # * individual object files # * overall package definition file # * symbols.rds (added when run inside R CMD check) pattern <- sprintf( "\\.(o|sl|so|dylib|a|dll)$|(%s\\.def)$|^symbols.rds$", pkg_name(path) ) files <- dir( file.path(path, "src"), pattern = pattern, full.names = TRUE, recursive = TRUE ) unlink(files) invisible(files) } # Returns the full path and name of the DLL file dll_path <- function(path = ".") { name <- paste(pkg_name(path), .Platform$dynlib.ext, sep = "") file.path(path, "src", name) } mtime <- function(x) { x <- x[file.exists(x)] if (length(x) == 0) { return(NULL) } max(file.info(x)$mtime) } globs <- function(path = ".", x) { old <- getwd() on.exit(setwd(old), add = TRUE) setwd(path) Sys.glob(x) } # List all source files in the package sources <- function(path = ".") { srcdir <- file.path(path, "src") src <- c( dir(srcdir, "\\.(c.*|f|rs)$", recursive = TRUE, full.names = TRUE), dir(srcdir, "^Cargo\\.toml$", recursive = TRUE, full.names = TRUE) ) extra <- desc::desc_get("Config/build/extra-sources", path) if (!is.na(extra)) { glb <- trimws(strsplit(extra, ",", fixed = TRUE)[[1]]) xs <- file.path(path, globs(path, glb)) xfls <- unlist(lapply( xs, dir, recursive = TRUE, full.names = TRUE )) src <- c(src, xs, xfls) } src } # List all header files in the package headers <- function(path = ".") { incldir <- file.path(path, "inst", "include") srcdir <- file.path(path, "src") c( dir(srcdir, "^Makevars.*$", recursive = TRUE, full.names = TRUE), dir(srcdir, "\\.h.*$", recursive = TRUE, full.names = TRUE), dir(incldir, "\\.h.*$", recursive = TRUE, full.names = TRUE) ) } #' Does the package need recompiling? #' (i.e. is there a source or header file newer than the dll) #' @inheritParams build #' @keywords internal #' @export needs_compile <- function(path = ".") { source <- mtime(c(sources(path), headers(path))) # no source files, so doesn't need compile if (is.null(source)) { return(FALSE) } dll <- mtime(dll_path(path)) # no dll, so needs compile if (is.null(dll)) { return(TRUE) } source > dll } # Does the package need a clean compile? # (i.e. is there a header or Makevars newer than the dll) needs_clean <- function(path = ".") { headers <- mtime(headers(path)) # no headers, so never needs clean compile if (is.null(headers)) { return(FALSE) } dll <- mtime(dll_path(path)) # no dll, so needs compile if (is.null(dll)) { return(TRUE) } headers > dll } install_min <- function(path = ".", dest, components = NULL, args = NULL, quiet = FALSE) { stopifnot(is.character(dest), length(dest) == 1, file.exists(dest)) poss <- c("R", "data", "help", "demo", "inst", "docs", "exec", "libs") if (!is.null(components)) { components <- match.arg(components, poss, several.ok = TRUE) } no <- setdiff(poss, components) no_args <- paste0("--no-", no) rcmd_build_tools( "INSTALL", c( path, paste("--library=", dest, sep = ""), no_args, "--no-multiarch", "--no-test-load", args ), fail_on_status = TRUE, quiet = quiet ) invisible(file.path(dest, pkg_name(path))) } pkgbuild/R/rtools-config.R0000644000176200001440000000143314211634664015174 0ustar liggesusers# First check if gcc set by BINPREF/CC is valid and use that if so scan_config_for_rtools <- function(debug = FALSE) { if (debug) { cat("Scanning R CMD config CC...\n") } if (!is_R4() && !using_gcc49()) { return() } cc_path <- gsub("\n", "", callr::rcmd_safe("config", "CC")$stdout) # remove '-m64' from tail if it exists cc_path <- sub("[[:space:]]+-m[[:digit:]]+$", "", cc_path) if (debug) { cat("cc_path:", cc_path, "\n") } cc_path <- find_arch_exe(cc_path, debug = debug) if (cc_path == "") { NULL } else { install_path <- dirname(dirname(dirname(cc_path))) if (debug) { cat("install_path:", install_path, "\n") } rtools(install_path, "custom", valid_binpref = TRUE) } } is_R4 <- function() { R.Version()$major >= "4" } pkgbuild/R/withr.R0000644000176200001440000001325214442271526013546 0ustar liggesusers withr_with_makevars <- function(new, code, path = makevars_user()) { makevars_file <- tempfile() on.exit(unlink(makevars_file), add = TRUE) force(path) withr_with_envvar(c(R_MAKEVARS_USER = makevars_file), { withr_set_makevars(new, path, makevars_file) force(code) }) } withr_set_makevars <- function(variables, old_path = withr_makevars_user(), new_path = tempfile()) { if (length(variables) == 0) { return() } stopifnot(withr_is_named(variables)) old <- NULL if (length(old_path) == 1 && file.exists(old_path)) { lines <- readLines(old_path) old <- lines lines <- c(old, paste(names(variables), variables, sep = " += ")) } else { lines <- paste(names(variables), variables, sep = " += ") } if (!identical(old, lines)) { writeLines(con = new_path, lines) } old } withr_makevars_user <- function() { tools::makevars_user() } withr_is_named <- function(x) { !is.null(names(x)) && all(names(x) != "") } # ------------------------------------------------------------------------- withr_get_envvar <- function(envs, action = "replace") { envs <- withr_as_envvars(envs) Sys.getenv(names(envs), names = TRUE, unset = NA) } withr_set_envvar <- function(envs, action = "replace") { envs <- withr_as_envvars(envs) stopifnot(is.character(action), length(action) == 1) action <- match.arg(action, c("replace", "prefix", "suffix")) if (length(envs) == 0) { return() } old <- Sys.getenv(names(envs), names = TRUE, unset = NA) set <- !is.na(envs) both_set <- set & !is.na(old) if (any(both_set)) { if (action == "prefix") { envs[both_set] <- paste(envs[both_set], old[both_set]) } else if (action == "suffix") { envs[both_set] <- paste(old[both_set], envs[both_set]) } } if (any(set)) do.call("Sys.setenv", as.list(envs[set])) if (any(!set)) Sys.unsetenv(names(envs)[!set]) invisible(old) } withr_with_envvar <- function(new, code, action = "replace") { old <- withr_get_envvar(envs = new, action = action) on.exit(withr_set_envvar(old)) withr_set_envvar(envs = new, action = action) force(code) } withr_as_envvars <- function(envs) { if (length(envs) == 0) { return(envs) } stopifnot(withr_is_named(envs)) envs[withr_vlapply(envs, is.null)] <- NA envs <- envs[!duplicated(names(envs), fromLast = TRUE)] envs } withr_vlapply <- function(X, FUN, ...) { vapply(X, FUN, FUN.VALUE = logical(1), ...) } withr_is_named <- function (x) { !is.null(names(x)) && all(names(x) != "") } # ------------------------------------------------------------------------- withr_with_temp_libpaths <- function (code, action = "prefix") { old <- .libPaths() on.exit(.libPaths(old)) withr_set_temp_libpaths(action = action) force(code) } withr_set_temp_libpaths <- function (action = "prefix") { paths <- tempfile("temp_libpath") dir.create(paths) withr_set_libpaths(paths, action = action) } withr_set_libpaths <- function (paths, action = "replace") { paths <- withr_as_character(paths) paths <- normalizePath(paths, mustWork = TRUE) old <- .libPaths() paths <- withr_merge_new(old, paths, action) .libPaths(paths) invisible(old) } withr_merge_new <- function(old, new, action, merge_fun = c) { action <- match.arg(action, c("replace", "prefix", "suffix")) if (action == "suffix") { new <- merge_fun(old, new) } else if (action == "prefix") { new <- merge_fun(new, old) } new } withr_as_character <- function(x) { nms <- names(x) res <- as.character(x) names(res) <- nms res } # ------------------------------------------------------------------------- withr_with_options <- function(new, code) { old <- withr_set_options(new_options = new) on.exit(withr_reset_options(old)) force(code) } withr_set_options <- function(new_options) { do.call(options, as.list(new_options)) } withr_reset_options <- function(old_options) { options(old_options) } # ------------------------------------------------------------------------- withr_with_path <- function(new, code, action = c("prefix", "suffix", "replace")) { old <- withr_get_path(path = new, action = action) on.exit((function(old) withr_set_path(old, "replace"))(old)) withr_set_path(path = new, action = action) force(code) } withr_set_path <- function(path, action = c("prefix", "suffix", "replace")) { action <- match.arg(action) path <- withr_as_character(path) path <- normalizePath(path, mustWork = FALSE) old <- withr_get_path() path <- withr_merge_new(old, path, action) path <- paste(path, collapse = .Platform$path.sep) Sys.setenv(PATH = path) invisible(old) } withr_merge_new <- function(old, new, action, merge_fun = c) { action <- match.arg(action, c("replace", "prefix", "suffix")) if (action == "suffix") { new <- merge_fun(old, new) } else if (action == "prefix") { new <- merge_fun(new, old) } new } withr_get_path <- function(...) { strsplit(Sys.getenv("PATH"), .Platform$path.sep)[[1]] } withr_local_path <- function (new = list(), action = c("prefix", "suffix", "replace"), .local_envir = parent.frame()) { old <- withr_get_path(path = new, action = action) withr_defer((function(old) withr_set_path(old, "replace"))(old), frame = .local_envir) withr_set_path(path = new, action = action) invisible(old) } # ------------------------------------------------------------------------- withr_defer <- function(expr, frame = parent.frame(), after = FALSE) { thunk <- as.call(list(function() expr)) do.call(on.exit, list(thunk, add = TRUE, after = after), envir = frame) } # ------------------------------------------------------------------------- pkgbuild/R/compiler-flags.R0000644000176200001440000000515214535333212015307 0ustar liggesusers#' Default compiler flags used by devtools. #' #' These default flags enforce good coding practice by ensuring that #' \env{CFLAGS} and \env{CXXFLAGS} are set to `-Wall -pedantic`. #' These tests are run by cran and are generally considered to be good practice. #' #' By default [compile_dll()] is run with `compiler_flags(TRUE)`, #' and check with `compiler_flags(FALSE)`. If you want to avoid the #' possible performance penalty from the debug flags, install the package. #' #' @param debug If `TRUE` adds `-g -O0` to all flags #' (Adding \env{FFLAGS} and \env{FCFLAGS}) #' @family debugging flags #' @export #' @examples #' compiler_flags() #' compiler_flags(TRUE) compiler_flags <- function(debug = FALSE) { res <- if (Sys.info()[["sysname"]] == "SunOS") { c( CFLAGS = "-g", CXXFLAGS = "-g", CXX11FLAGS = "-g", CXX14FLAGS = "-g", CXX17FLAGS = "-g", CXX20FLAGS = "-g" ) } else if (debug) { c( CFLAGS = "-UNDEBUG -Wall -pedantic -g -O0", CXXFLAGS = "-UNDEBUG -Wall -pedantic -g -O0", CXX11FLAGS = "-UNDEBUG -Wall -pedantic -g -O0", CXX14FLAGS = "-UNDEBUG -Wall -pedantic -g -O0", CXX17FLAGS = "-UNDEBUG -Wall -pedantic -g -O0", CXX20FLAGS = "-UNDEBUG -Wall -pedantic -g -O0", FFLAGS = "-g -O0", FCFLAGS = "-g -O0" ) } else { c( CFLAGS = "-Wall -pedantic", CXXFLAGS = "-Wall -pedantic", CXX11FLAGS = "-Wall -pedantic", CXX14FLAGS = "-Wall -pedantic", CXX17FLAGS = "-Wall -pedantic", CXX20FLAGS = "-Wall -pedantic" ) } if (cli::num_ansi_colors() > 1 && has_compiler_colored_diagnostics()) { flags <- c( "CFLAGS", "CXXFLAGS", "CXX11FLAGS", "CXX14FLAGS", "CXX17FLAGS", "CXX20FLAGS" ) res[flags] <- paste(res[flags], "-fdiagnostics-color=always") } res } has_compiler_colored_diagnostics <- function() { val <- interpret_envvar_flag("PKG_BUILD_COLOR_DIAGNOSTICS", NA_character_) if (!is.na(val)) { return(val) } if (cache_exists("has_compiler_colored_diagnostics")) { return(cache_get("has_compiler_colored_diagnostics")) } # We cannot use the existing has_compiler setting, because it may not have # run with -fdiagnostics-color=always if (cache_exists("has_compiler")) { old <- cache_get("has_compiler") cache_remove("has_compiler") on.exit(cache_set("has_compiler", old)) } else { on.exit(cache_remove("has_compiler")) } res <- withr_with_makevars(c(CFLAGS = "-fdiagnostics-color=always"), has_compiler()) cache_set("has_compiler_colored_diagnostics", res) res } pkgbuild/R/latex.R0000644000176200001440000000062114211634664013522 0ustar liggesusers#' Is latex installed? #' #' Checks for presence of pdflatex on path. #' #' @export has_latex <- function() { if (!is.null(fix <- getOption("PKGBUILD_TEST_FIXTURE_HAS_LATEX"))) { return(fix) } nzchar(Sys.which("pdflatex")) } #' @export #' @rdname has_latex check_latex <- function() { if (!has_latex()) { stop("LaTeX not installed (pdflatex not found)", call. = FALSE) } TRUE } pkgbuild/R/c-registration.R0000644000176200001440000000771314211634664015350 0ustar liggesusersupdate_registration <- function(path, compile_attributes, register_routines, quiet) { if (compile_attributes) { if (pkg_links_to_cpp11(path)) { cpp11::cpp_register(path, quiet = quiet) } else if (pkg_links_to_rcpp(path)) { unlink(file.path(path, c("R/RcppExports.R", "src/RcppExports.cpp"))) Rcpp::compileAttributes(path, verbose = !quiet) } } else if (register_routines) { update_c_registration(path) check_namespace_registration(path) } } update_c_registration <- function(path) { path <- pkg_path(path) pkgbuild_init_file <- file.path(path, "src", "init.c") should_update <- !file.exists(pkgbuild_init_file) || any(grepl("generated by pkgbuild", readLines(pkgbuild_init_file))) if (!should_update) { return(invisible(character())) } # package_native_routine_registration_skeleton is not available before R 3.4 if (getRversion() < "3.4.0") { return(invisible(character())) } con <- textConnection(NULL, "w") tools::package_native_routine_registration_skeleton(path, con = con, character_only = FALSE) lines <- textConnectionValue(con) close(con) if (length(lines) == 0) { return(invisible(lines)) } if (!file.exists(pkgbuild_init_file)) { lines <- remove_fixme(lines) } else { current_lines <- readLines(pkgbuild_init_file) current_range <- pkgbuild_generated_section(current_lines) new_range <- tools_generated_section(lines) lines <- c( current_lines[seq(1, min(current_range) - 1)], lines[new_range], current_lines[seq(max(current_range) + 1, length(current_lines))] ) } lines <- add_generation_message(lines) writeLines(lines, pkgbuild_init_file) invisible(lines) } remove_fixme <- function(lines) { fixme_loc <- grep("/* FIXME: ", lines, fixed = TRUE) lines <- lines[-seq(fixme_loc, fixme_loc + 2)] lines } tools_generated_section <- function(lines) { start_loc <- grep("/* .Call calls */", lines, fixed = TRUE) end_loc <- grep("};", lines, fixed = TRUE) seq(start_loc, end_loc) } pkgbuild_generated_section <- function(lines) { start_loc <- grep("/* Section generated by pkgbuild, do not edit */", lines, fixed = TRUE) end_loc <- grep("/* End section generated by pkgbuild */", lines, fixed = TRUE) seq(start_loc, end_loc) } add_generation_message <- function(lines) { start_loc <- grep("/* .Call calls */", lines, fixed = TRUE) end_loc <- grep("};", lines, fixed = TRUE) if (end_loc <= start_loc) { stop("Malformed init.c format") } lines <- append(lines, "/* Section generated by pkgbuild, do not edit */", after = start_loc - 1) lines <- append(lines, "/* End section generated by pkgbuild */", after = end_loc + 1) lines } check_namespace_registration <- function(path) { path <- pkg_path(path) namespace_file <- file.path(path, "NAMESPACE") if (!file.exists(namespace_file)) { warning("NAMESPACE file missing", immediate. = TRUE) } pkg_namespace <- readLines(namespace_file, warn = FALSE) has_registration <- any(grepl("^[[:space:]]*useDynLib.*[.]registration[[:space:]]*=[[:space:]]*TRUE", pkg_namespace)) if (!has_registration) { warning( immediate. = TRUE, call. = FALSE, sprintf( "NAMESPACE missing native routine registration: * Add `#' @useDynLib %s, .registration = TRUE` to R files. * Run `devtools::document()`", pkg_name(path) ) ) } } #' Test if a package path is linking to Rcpp or cpp11 #' #' @inheritParams build #' @export #' @keywords internal pkg_links_to_rcpp <- function(path) { path <- pkg_path(path) deps <- desc::desc_get_deps(file.path(path, "DESCRIPTION")) any(deps$type == "LinkingTo" & deps$package == "Rcpp") } #' @rdname pkg_links_to_rcpp #' @keywords internal #' @export pkg_links_to_cpp11 <- function(path) { path <- pkg_path(path) desc <- desc::desc(file = file.path(path, "DESCRIPTION")) deps <- desc$get_deps() desc$get_field("Package") == "cpp11" || any(deps$type == "LinkingTo" & deps$package == "cpp11") } pkgbuild/R/rcmd.R0000644000176200001440000000503714253066774013347 0ustar liggesusers#' Call R CMD 'command' with build tools active #' #' This is a wrapper around `callr::rcmd_safe()` that checks #' that you have build tools available, and on Windows, automatically sets #' the path to include Rtools. #' #' @param ... Parameters passed on to `rcmd_safe`. #' @param env Additional environment variables to set. The defaults from #' `callr::rcmd_safe_env()` are always set. #' @inheritParams with_build_tools #' @inheritParams build #' @export #' @examples #' # These env vars are always set #' callr::rcmd_safe_env() #' #' if (has_build_tools()) { #' rcmd_build_tools("CONFIG", "CC")$stdout #' rcmd_build_tools("CC", "--version")$stdout #' } rcmd_build_tools <- function(..., env = character(), required = TRUE, quiet = FALSE) { env <- c(callr::rcmd_safe_env(), env) if (!quiet) { cli::cat_rule(paste0("R CMD ", ..1), col = "cyan") } warn_for_potential_errors() callback <- if (cli::is_dynamic_tty()) { block_callback(quiet) } else { simple_callback(quiet) } res <- with_build_tools( { withCallingHandlers( callr::rcmd_safe(..., env = env, spinner = FALSE, show = FALSE, echo = FALSE, block_callback = callback, stderr = "2>&1" ), error = function(e) { if (!quiet) e$echo <- TRUE asNamespace("callr")$err$throw(e) } ) }, required = required ) msg_for_long_paths(res) invisible(res) } msg_for_long_paths <- function(output) { if (is_windows() && any(grepl("over-long path length", output$stdout))) { message( "\nIt seems that this package contains files with very long paths.\n", "This is not supported on most Windows versions. Please contact the\n", "package authors and tell them about this. See this GitHub issue\n", "for more details: https://github.com/r-lib/remotes/issues/84\n" ) } } warn_for_potential_errors <- function() { if (is_windows() && grepl(" ", R.home()) && getRversion() <= "3.4.2") { warning( immediate. = TRUE, "\n!!! Building will probably fail!\n", "This version of R has trouble with building packages if\n", "the R HOME directory (currently '", R.home(), "')\n", "has space characters. Possible workarounds include:\n", "- installing R to the C: drive,\n", "- installing it into a path without a space, or\n", "- creating a drive letter for R HOME via the `subst` windows command, and\n", " starting R from the new drive.\n", "See also https://github.com/r-lib/remotes/issues/98\n" ) } } pkgbuild/R/cache.R0000644000176200001440000000076114152340747013455 0ustar liggesusers# Need to check for existence so load_all doesn't override known rtools location if (!exists("cache")) { cache <- new.env(parent = emptyenv()) } cache_get <- function(name) { get(name, envir = cache) } cache_exists <- function(name) { exists(name, envir = cache) } cache_set <- function(name, value) { assign(name, value, envir = cache) } cache_remove <- function(name) { rm(list = name, envir = cache) } cache_reset <- function() { rm(list = ls(envir = cache), envir = cache) } pkgbuild/R/build.R0000644000176200001440000002412714442271526013513 0ustar liggesusers#' Build package #' #' Building converts a package source directory into a single bundled file. #' If `binary = FALSE` this creates a `tar.gz` package that can #' be installed on any platform, provided they have a full development #' environment (although packages without source code can typically be #' installed out of the box). If `binary = TRUE`, the package will have #' a platform specific extension (e.g. `.zip` for windows), and will #' only be installable on the current platform, but no development #' environment is needed. #' #' ## Configuration #' #' ### `DESCRIPTION` entries #' #' * `Config/build/clean-inst-doc` can be set to `FALSE` to avoid cleaning up #' `inst/doc` when building a source package. Set it to `TRUE` to force a #' cleanup. See the `clean_doc` argument. #' #' * `Config/build/copy-method` can be used to avoid copying large #' directories in `R CMD build`. It works by copying (or linking) the #' files of the package to a temporary directory, leaving out the #' (possibly large) files that are not part of the package. Possible #' values: #' #' - `none`: pkgbuild does not copy the package tree. This is the default. #' - `copy`: the package files are copied to a temporary directory before #' ` R CMD build`. #' - `link`: the package files are symbolic linked to a temporary #' directory before `R CMD build`. Windows does not have symbolic #' links, so on Windows this is equivalent to `copy`. #' #' You can also use the `pkg.build_copy_method` option or the #' `PKG_BUILD_COPY_METHOD` environment variable to set the copy method. #' The option is consulted first, then the `DESCRIPTION` entry, then the #' environment variable. #' #' * `Config/build/extra-sources` can be used to define extra source files #' for pkgbuild to decide whether a package DLL needs to be recompiled in #' `needs_compile()`. The syntax is a comma separated list of file names, #' or globs. (See [utils::glob2rx()].) E.g. `src/rust/src/*.rs` or #' `configure*`. #' #' * `Config/build/bootstrap` can be set to `TRUE` to run #' `Rscript bootstrap.R` in the source directory prior to running subsequent #' build steps. #' #' ### Options #' #' * `pkg.build_copy_method`: use this option to avoid copying large #' directories when building a package. See possible values above, at the #' `Config/build/copy-method` `DESCRIPTION` entry. #' #' * `pkg.build_stop_for_warnings`: if it is set to `TRUE`, then pkgbuild #' will stop for `R CMD build` errors. It takes precedence over the #' `PKG_BUILD_STOP_FOR_WARNINGS` environment variable. #' #' ### Environment variables #' #' * `PKG_BUILD_COLOR_DIAGNOSTICS`: set it to `false` to opt out of colored #' compiler diagnostics. Set it to `true` to force colored compiler #' diagnostics. #' #' * `PKG_BUILD_COPY_METHOD`: use this environment variable to avoid copying #' large directories when building a package. See possible values above, #' at the `Config/build/copy-method` `DESCRIPTION` entry. #' #' will stop for `R CMD build` errors. The `pkg.build_stop_for_warnings` #' option takes precedence over this environment variable. #' #' @param path Path to a package, or within a package. #' @param dest_path path in which to produce package. If it is an existing #' directory, then the output file is placed in `dest_path` and named #' according to the current R conversions (e.g. `.zip` for Windows binary #' packages, `.tgz` for macOS binary packages, etc). #' If it is an existing file, then it will be overwritten. #' If `dest_path` does not exist, then it is used as a file name. #' If `NULL`, it defaults to the parent directory of the package. #' @param binary Produce a binary (`--binary`) or source ( #' `--no-manual --no-resave-data`) version of the package. #' @param vignettes,manual For source packages: if `FALSE`, don't build PDF #' vignettes (`--no-build-vignettes`) or manual (`--no-manual`). #' @param args An optional character vector of additional command #' line arguments to be passed to `R CMD build` if `binary = FALSE`, #' or `R CMD install` if `binary = TRUE`. #' @param quiet if `TRUE` suppresses output from this function. #' @param needs_compilation Usually only needed if the packages has #' C/C++/Fortran code. By default this is autodetected. #' @param compile_attributes if `TRUE` and the package uses Rcpp, call #' [Rcpp::compileAttributes()] before building the package. It is ignored #' if package does not need compilation. #' @param register_routines if `TRUE` and the package does not use Rcpp, call #' register routines with #' `tools::package_native_routine_registration_skeleton()` before building #' the package. It is ignored if package does not need compilation. #' @param clean_doc If `TRUE`, clean the files in `inst/doc` before building #' the package. If `NULL` and the `Config/build/clean-inst-doc` entry is #' present in `DESCRIPTION`, then that is used. Otherwise, if `NULL`, #' and interactive, ask to remove the files prior to cleaning. In most #' cases cleaning the files is the correct behavior to avoid stale #' vignette outputs in the built package. #' @export #' @return a string giving the location (including file name) of the built #' package build <- function(path = ".", dest_path = NULL, binary = FALSE, vignettes = TRUE, manual = FALSE, clean_doc = NULL, args = NULL, quiet = FALSE, needs_compilation = pkg_has_src(path), compile_attributes = FALSE, register_routines = FALSE) { options <- build_setup( path, dest_path, binary, vignettes, manual, clean_doc, args, needs_compilation, compile_attributes, register_routines, quiet ) on.exit(unlink(options$out_dir, recursive = TRUE), add = TRUE) withr_with_makevars( compiler_flags(debug = FALSE), { output <- withr_with_temp_libpaths( rcmd_build_tools( options$cmd, c(options$path, options$args), wd = options$out_dir, fail_on_status = TRUE, required = FALSE, # already checked in setup quiet = quiet ) ) if (should_stop_for_warnings() && grepl("\n\\s*warning:", output$stdout, ignore.case = TRUE)) { cli::cli_alert_warning( "Stopping as requested for a warning during {.code R CMD build}.") if (quiet) { cli::cli_alert_warning("The full output is printed below.") cli::cli_verbatim(output$stdout) } stop("converted from `R CMD build` warning.") } out_file <- dir(options$out_dir) file.copy( file.path(options$out_dir, out_file), options$dest_path, overwrite = TRUE ) if (is_dir(options$dest_path)) { file.path(options$dest_path, out_file) } else { options$dest_path } } ) } build_setup <- function(path, dest_path, binary, vignettes, manual, clean_doc, args, needs_compilation, compile_attributes, register_routines, quiet) { if (!file.exists(path)) { stop("`path` must exist", call. = FALSE) } if (!is_dir(path)) { if (!binary) stop("`binary` must be TRUE for package files", call. = FALSE) if (compile_attributes) { stop("`compile_attributes` must be FALSE for package files", call. = FALSE) } if (register_routines) { stop("`register_routines` must be FALSE for package files", call. = FALSE) } } else { path <- pkg_path(path) } if (is.null(dest_path)) { dest_path <- dirname(path) } bootstrap_file <- file.path(path, "bootstrap.R") run_boostrap <- isTRUE(get_desc_config_flag(path, "bootstrap")) if (file.exists(bootstrap_file) && run_boostrap) { if (!quiet) message("Running bootstrap.R...") callr::rscript( bootstrap_file, wd = path, stderr = "2>&1", show = !quiet ) } if (needs_compilation) { update_registration(path, compile_attributes, register_routines, quiet) } if (binary) { build_setup_binary(path, dest_path, args, needs_compilation) } else { build_setup_source( path, dest_path, vignettes, manual, clean_doc, args, needs_compilation ) } } build_setup_binary <- function(path, dest_path, args, needs_compilation) { if (needs_compilation) { check_build_tools(quiet = TRUE) } # Build in temporary directory and then copy to final location out_dir <- tempfile() dir.create(out_dir) list( cmd = "INSTALL", path = normalizePath(path), args = c("--build", args), out_dir = out_dir, dest_path = dest_path ) } build_setup_source <- function(path, dest_path, vignettes, manual, clean_doc, args, needs_compilation) { if (!("--resave-data" %in% args)) { args <- c(args, "--no-resave-data") } if (!manual) { args <- unique(c(args, "--no-manual")) } if (!vignettes) { args <- unique(c(args, "--no-build-vignettes")) } no_manual <- "--no-manual" %in% args if (!no_manual && !has_latex()) { message("pdflatex not found! Not building PDF manual.") manual <- FALSE } if (needs_compilation && (vignettes || manual)) { check_build_tools(quiet = TRUE) } build_vignettes <- !("--no-build-vignettes" %in% args) if (is.null(clean_doc)) { clean_doc <- get_desc_config_flag(path, "clean-inst-doc") } if (build_vignettes && (is.null(clean_doc) || isTRUE(clean_doc))) { doc_dir <- file.path(path, "inst", "doc") if (dir.exists(doc_dir)) { if (is.null(clean_doc) && interactive()) { message("Building the package will delete...\n '", doc_dir, "'\nAre you sure?") res <- utils::menu(c("Yes", "No")) if (res == 2) { return() } } unlink(doc_dir, recursive = TRUE) } } # Build in temporary directory and then copy to final location out_dir <- tempfile() dir.create(out_dir) copy_method <- get_copy_method(path) if (copy_method != "none") { pkgname <- desc::desc_get("Package", path) tmppath <- tempfile("build-") copy_package_tree(path, tmppath, pkgname) path <- file.path(tmppath, pkgname) } list( cmd = "build", path = normalizePath(path), args = args, out_dir = out_dir, dest_path = dest_path ) } pkgbuild/R/find-package-root.R0000644000176200001440000000146114535333540015700 0ustar liggesusersfind_package_root <- function(path = ".") { is_root <- function(path) { identical( normalizePath(path, winslash = "/"), normalizePath(dirname(path), winslash = "/") ) } if (!file.exists(path)) { stop("Path does not exist: ", path) } cur_path <- normalizePath(path, winslash = "/") errmsg <- paste0( "Could not find R package in `", path, "` or its parent directories." ) max_depth <- 100 for (i in 1:max_depth) { dsc_path <- file.path(cur_path, "DESCRIPTION") if (file.exists(dsc_path) && any(grepl("^Package: ", readLines(dsc_path)))) { return(cur_path) } else if (is_root(cur_path)) { stop(errmsg) } else { cur_path <- dirname(cur_path) } } stop(errmsg, " Checked ", max_depth, " parent directories.") # nocov } pkgbuild/R/rtools-path.R0000644000176200001440000000344414211634664014667 0ustar liggesusersscan_path_for_rtools <- function(debug = FALSE) { if (debug) { cat("Scanning path...\n") } # Next looks for ls and gcc on path ls_path <- Sys.which("ls") if (ls_path == "") { return(NULL) } if (debug) { cat("ls:", ls_path, "\n") } # We have a candidate install_path install_path <- dirname(dirname(ls_path)) gcc_path <- Sys.which("gcc") if (debug) { cat("gcc_path:", gcc_path, "\n") } if (gcc_path != "") { # Check both candidate install paths are same install_path2 <- dirname(dirname(dirname(gcc_path))) if (tolower(install_path2) != tolower(install_path)) { return(NULL) } } else { # Maybe isn't on path, but is in default location gcc_default <- find_arch_exe( file.path(install_path, paste0("mingw_", gcc_arch()), "bin", "gcc.exe"), debug = debug ) if (gcc_default == "") { return(NULL) } } version <- installed_version(install_path, debug = debug) if (debug) { cat("Version:", version, "\n") } rtools(install_path, version) } find_arch_exe <- function(path, debug = FALSE) { # Convert unix path to Windows if (grepl("^/", path)) { path <- convert_unix_path(path) } full_path <- Sys.which(path) if (nchar(full_path) == 0) { if (debug) { cat("'", path, "' does not exist\n", sep = "") } return("") } # Then check architecture matches file_info <- file.info(full_path) if (file_info$exe != paste0("win", gcc_arch())) { if (debug) { cat(" Architecture doesn't match\n") } return("") } full_path } # This assumes cygpath is on your PATH, # but it should be if you are using /foo/bar paths in R CMD config, # so we don't need to handle this convert_unix_path <- function(path) { system2("cygpath", c("-m", path), stdout = TRUE) } pkgbuild/R/build-tools.R0000644000176200001440000000555614426741134014655 0ustar liggesusers#' Are build tools are available? #' #' `has_build_tools` returns a logical, `check_build_tools` throws #' an error. `with_build_tools` checks that build tools are available, #' then runs `code` in an correctly staged environment. #' If run interactively from RStudio, and the build tools are not #' available these functions will trigger an automated install. #' #' Errors like `running command #' '"C:/PROGRA~1/R/R-34~1.2/bin/x64/R" CMD config CC' had status 127` #' indicate the code expected Rtools to be on the system PATH. You can #' then verify you have rtools installed with `has_build_tools()` and #' temporarily add Rtools to the PATH `with_build_tools({ code })`. #' #' It is possible to add Rtools to your system PATH manually; you can use #' [rtools_path()] to show the installed location. However because this #' requires manual updating when a new version of Rtools is installed and the #' binaries in Rtools may conflict with existing binaries elsewhere on the PATH it #' is better practice to use `with_build_tools()` as needed. #' @inheritParams has_rtools #' @param quiet if `TRUE` suppresses output from this function. #' @export #' @seealso has_rtools #' @examples #' has_build_tools(debug = TRUE) #' check_build_tools() has_build_tools <- function(debug = FALSE) { check <- getOption("buildtools.check", NULL) has <- if (is_windows() && is_R4() && has_rtools(debug = debug)) { TRUE } else if (is_windows()) { has_rtools(debug = debug) } else { has_compiler(debug = debug) } if (!has && !is.null(check)) { check("Building R package from source") } else { has } } #' @export #' @rdname has_build_tools check_build_tools <- function(debug = FALSE, quiet = FALSE) { if (!has_build_tools(debug = debug)) { stop( "Could not find tools necessary to compile a package\n", "Call `pkgbuild::check_build_tools(debug = TRUE)` to diagnose the problem.", call. = FALSE ) } else if (!isTRUE(quiet)) { message("Your system is ready to build packages!") } invisible(TRUE) } #' @export #' @rdname has_build_tools #' @param code Code to rerun in environment where build tools are guaranteed to #' exist. #' @param required If `TRUE`, and build tools are not available, #' will throw an error. Otherwise will attempt to run `code` without #' them. with_build_tools <- function(code, debug = FALSE, required = TRUE) { if (required) { check_build_tools(debug = debug, quiet = TRUE) } if (has_rtools()) { withr_with_path(rtools_path(), code) } else { code } } #' @rdname has_build_tools #' @param .local_envir The environment to use for scoping. #' @export local_build_tools <- function(debug = FALSE, required = TRUE, .local_envir = parent.frame()) { if (required) { check_build_tools(debug = debug, quiet = TRUE) } if (has_rtools()) { withr_local_path(rtools_path(), .local_envir = .local_envir) } } pkgbuild/R/time.R0000644000176200001440000000375314526625066013361 0ustar liggesusersformat_time <- local({ assert_diff_time <- function(x) { stopifnot(inherits(x, "difftime")) } parse_ms <- function(ms) { stopifnot(is.numeric(ms)) data.frame( days = floor(ms / 86400000), hours = floor((ms / 3600000) %% 24), minutes = floor((ms / 60000) %% 60), seconds = round((ms / 1000) %% 60, 1) ) } first_positive <- function(x) which(x > 0)[1] trim <- function(x) gsub("^\\s+|\\s+$", "", x) pretty_ms <- function(ms, compact = FALSE) { stopifnot(is.numeric(ms)) parsed <- t(parse_ms(ms)) if (compact) { units <- c("d", "h", "m", "s") parsed2 <- parsed parsed2[] <- paste0(parsed, units) idx <- cbind( apply(parsed, 2, first_positive), seq_len(length(ms)) ) tmp <- paste0("~", parsed2[idx]) # handle NAs tmp[is.na(parsed2[idx])] <- NA_character_ tmp } else { ## Exact for small ones exact <- paste0(ceiling(ms), "ms") exact[is.na(ms)] <- NA_character_ ## Approximate for others, in seconds merge_pieces <- function(pieces) { ## handle NAs if (all(is.na(pieces))) { return(NA_character_) } ## handle non-NAs paste0( if (pieces[1]) paste0(pieces[1], "d "), if (pieces[2]) paste0(pieces[2], "h "), if (pieces[3]) paste0(pieces[3], "m "), if (pieces[4]) paste0(pieces[4], "s ") ) } approx <- trim(apply(parsed, 2, merge_pieces)) ifelse(ms < 1000, exact, approx) } } pretty_sec <- function(sec, compact = FALSE) { pretty_ms(sec * 1000, compact = compact) } pretty_dt <- function(dt, compact = FALSE) { assert_diff_time(dt) units(dt) <- "secs" pretty_sec(as.vector(dt), compact = compact) } structure( list( .internal = environment(), pretty_ms = pretty_ms, pretty_sec = pretty_sec, pretty_dt = pretty_dt ), class = c("standalone_time", "standalone") ) }) pkgbuild/R/callback.R0000644000176200001440000000530514527147033014144 0ustar liggesusers#' @importFrom utils head tail # This is adapted from https://github.com/r-lib/rcmdcheck/blob/7ee14764c2b17ee2c2f4131a9e19d1b56a66ed0f/R/callback.R block_callback <- function(quiet) { partial_line <- "" state <- "OK" should_time <- FALSE line_started <- Sys.time() now <- NULL prev_line <- "" no <- function(x, what = "") { pattern <- paste0(" \\.\\.\\. ", what, "$") sub("^\\* ", "", sub(pattern, "", x)) } time_if_long <- function() { elapsed <- now - line_started if (elapsed > as.difftime(1 / 3, units = "secs")) { style(timing = paste0(" (", format_time$pretty_dt(elapsed), ")")) } else { "" } } do_line <- function(x) { should_time <<- FALSE now <<- Sys.time() ## Test mode is special. It will change the 'state' back to 'OK', ## once it is done. xx <- if (is_new_check(x)) { do_new_check(x) } else if (grepl("^Status: ", x)) { ## We just skip the status, it is printed out anyway, as the return ## value NA_character_ } else { do_continuation(x) } prev_line <<- x ## NA_character_ can omit output if (is.na(xx)) { return() } if (should_time) xx <- style(xx, timing = time_if_long()) line_started <<- now cat(xx, "\n", sep = "") flush(stdout()) } do_new_check <- function(x) { should_time <<- TRUE if (grepl(" \\.\\.\\. OK\\s*$", x)) { state <<- "OK" style(ok = cli::symbol$tick, " ", pale = no(x, "OK")) } else if (grepl(" \\.\\.\\. NOTE\\s*$", x)) { state <<- "NOTE" style(note = c("N ", no(x, "NOTE"))) } else if (grepl(" \\.\\.\\. WARNING\\s*$", x)) { state <<- "WARNING" style(warn = c("W ", no(x, "WARNING"))) } else if (grepl(" \\.\\.\\. ERROR\\s*$", x)) { state <<- "ERROR" style(err = c("E ", no(x, "ERROR"))) } else if (grepl("^\\* checking tests \\.\\.\\.[ ]?$", x)) { state <<- "tests" style(pale = c(cli::symbol$line, " ", no(x))) } else if (grepl("^\\* DONE\\s*$", x)) { state <<- "OK" NA_character_ } else { style(pale = c(cli::symbol$line, " ", no(x))) } } do_continuation <- function(x) { paste0(" ", x) } function(x) { if (quiet) { return() } x <- paste0(partial_line, x) partial_line <<- "" lines <- strsplit(x, "\r?\n")[[1]] if (last_char(x) != "\n") { partial_line <<- tail(lines, 1) lines <- head(lines, -1) } cat(" \r") lapply(lines, do_line) cat0(sub("^[\\* ]", " ", partial_line), "\r") } } is_new_check <- function(x) { grepl("^\\* ", x) } simple_callback <- function(quiet) { function(x) { if (quiet) { return() } cat(x) } } pkgbuild/R/rtools.R0000644000176200001440000002307114575566553013751 0ustar liggesusers#' Is Rtools installed? #' #' To build binary packages on windows, Rtools (found at #' \url{https://CRAN.R-project.org/bin/windows/Rtools/}) needs to be on #' the path. The default installation process does not add it, so this #' script finds it (looking first on the path, then in the registry). #' It also checks that the version of rtools matches the version of R. #' `has_rtools()` determines if Rtools is installed, caching the results. #' Afterward, run `rtools_path()` to find out where it's installed. #' #' @section Acknowledgements: #' This code borrows heavily from RStudio's code for finding Rtools. #' Thanks JJ! #' @param debug If `TRUE`, will print out extra information useful for #' debugging. If `FALSE`, it will use result cached from a previous run. #' @return Either a visible `TRUE` if rtools is found, or an invisible #' `FALSE` with a diagnostic [message()]. #' As a side-effect the internal package variable `rtools_path` is #' updated to the paths to rtools binaries. #' @keywords internal #' @export #' @examples #' has_rtools() has_rtools <- function(debug = FALSE) { if (!debug && rtools_path_is_set()) { return(!identical(rtools_path(), "")) } if (!is_windows()) { return(FALSE) } # R 4.4.0 or later on ARM64 if (getRversion() >= "4.4.0" && grepl("aarch", R.version$platform)) { rtools44_aarch64_home <- Sys.getenv("RTOOLS44_AARCH64_HOME", "C:\rtools44-aarch64") if (file.exists(file.path(rtools44_aarch64_home, "usr", "bin"))) { if (debug) { cat("Found in Rtools 4.4 (aarch64) installation folder\n") } rtools_path_set(rtools(rtools44_aarch64_home, "4.4")) return(TRUE) } return(FALSE) } # R 4.4.0 or later if (getRversion() >= "4.4.0") { rtools44_home <- Sys.getenv("RTOOLS44_HOME", "C:\\rtools44") if (file.exists(file.path(rtools44_home, "usr", "bin"))) { if (debug) { cat("Found in Rtools 4.4 installation folder\n") } rtools_path_set(rtools(rtools44_home, "4.4")) return(TRUE) } return(FALSE) } # R 4.3.0 or later on ARM64 if (getRversion() >= "4.3.0" && grepl("aarch", R.version$platform)) { rtools43_aarch64_home <- Sys.getenv("RTOOLS43_AARCH64_HOME", "C:\rtools43-aarch64") if (file.exists(file.path(rtools43_aarch64_home, "usr", "bin"))) { if (debug) { cat("Found in Rtools 4.3 (aarch64) installation folder\n") } rtools_path_set(rtools(rtools43_aarch64_home, "4.3")) return(TRUE) } return(FALSE) } # R 4.3.0 or later if (getRversion() >= "4.3.0") { rtools43_home <- Sys.getenv("RTOOLS43_HOME", "C:\\rtools43") if (file.exists(file.path(rtools43_home, "usr", "bin"))) { if (debug) { cat("Found in Rtools 4.3 installation folder\n") } rtools_path_set(rtools(rtools43_home, "4.3")) return(TRUE) } return(FALSE) } # R 4.2.x or later and ucrt? ucrt <- is_ucrt() if (ucrt) { rtools42_home <- Sys.getenv("RTOOLS42_HOME", "C:\\rtools42") if (file.exists(file.path(rtools42_home, "usr", "bin"))) { if (debug) { cat("Found in Rtools 4.2 installation folder\n") } rtools_path_set(rtools(rtools42_home, "4.2")) return(TRUE) } } # In R 4.0 we can use RTOOLS40_HOME, recent versions of Rtools40 work fine # with ucrt as well, currently. if (is_R4()) { rtools40_home <- Sys.getenv("RTOOLS40_HOME", "C:\\rtools40") fld <- if (ucrt) "ucrt64" else "usr" if (file.exists(file.path(rtools40_home, fld, "bin"))) { if (debug) { cat("Found in Rtools 4.0 installation folder\n") } rtools_path_set(rtools(rtools40_home, "4.0")) return(TRUE) } } # First, R CMD config CC -------------------------------------------- # This does not work if 'make' is not yet on the path from_config <- scan_config_for_rtools(debug) if (is_compatible(from_config)) { if (debug) { cat("Found compatible gcc from R CMD config CC\n") } rtools_path_set(from_config) return(TRUE) } # Next, try the path ------------------------------------------------ from_path <- scan_path_for_rtools(debug) if (is_compatible(from_path)) { if (debug) { cat("Found compatible gcc on path\n") } rtools_path_set(from_path) return(TRUE) } if (!is.null(from_path)) { # Installed if (is.null(from_path$version)) { # but not from rtools if (debug) { cat("gcc and ls on path, assuming set up is correct\n") } return(TRUE) } else { # Installed, but not compatible needed <- rtools_needed() message( "WARNING: Rtools ", from_path$version, " found on the path", " at ", from_path$path, " is not compatible with R ", getRversion(), ".\n\n", "Please download and install ", needed, " from ", rtools_url(needed), ", remove the incompatible version from your PATH." ) return(invisible(FALSE)) } } # Next, try the registry -------------------------------------------------- registry_candidates <- scan_registry_for_rtools(debug) if (length(registry_candidates) == 0) { # Not on path or in registry, so not installled needed <- rtools_needed() message( "WARNING: Rtools is required to build R packages, but is not ", "currently installed.\n\n", "Please download and install ", needed, " from ", rtools_url(needed), "." ) return(invisible(FALSE)) } from_registry <- Find(is_compatible, registry_candidates, right = TRUE) if (is.null(from_registry)) { # In registry, but not compatible. versions <- vapply(registry_candidates, function(x) x$version, character(1)) needed <- rtools_needed() message( "WARNING: Rtools is required to build R packages, but no version ", "of Rtools compatible with R ", getRversion(), " was found. ", "(Only the following incompatible version(s) of Rtools were found: ", paste(versions, collapse = ", "), ")\n\n", "Please download and install ", needed, " from ", rtools_url(needed), "." ) return(invisible(FALSE)) } # On Rtools 3.x do an extra check if the installed version is accurate. # With rtools40 this is no longer needed (it doesn't have a Version.txt) if (isTRUE(from_registry$version < "4")) { installed_ver <- installed_version(from_registry$path, debug = debug) if (is.null(installed_ver)) { # Previously installed version now deleted needed <- rtools_needed() message( "WARNING: Rtools is required to build R packages, but the ", "version of Rtools previously installed in ", from_registry$path, " has been deleted.\n\n", "Please download and install ", needed, " from ", rtools_url(needed), "." ) return(invisible(FALSE)) } if (installed_ver != from_registry$version) { # Installed version doesn't match registry version needed <- rtools_needed() message( "WARNING: Rtools is required to build R packages, but no version ", "of Rtools compatible with R ", getRversion(), " was found. ", "Rtools ", from_registry$version, " was previously installed in ", from_registry$path, " but now that directory contains Rtools ", installed_ver, ".\n\n", "Please download and install ", needed, " from ", rtools_url(needed), "." ) return(invisible(FALSE)) } } # Otherwise it must be ok :) rtools_path_set(from_registry) TRUE } is_ucrt <- function() { identical(R.Version()$crt, "ucrt") } #' @rdname has_rtools #' @usage NULL #' @export find_rtools <- has_rtools #' @rdname has_rtools #' @usage NULL #' @export setup_rtools <- has_rtools #' @export #' @rdname has_rtools check_rtools <- function(debug = FALSE) { if (is_windows() && !has_rtools(debug = debug)) { stop("Rtools is not installed.", call. = FALSE) } TRUE } installed_version <- function(path, debug) { if (!file.exists(file.path(path, "Rtools.txt"))) { return(NULL) } # Find the version path version_path <- file.path(path, "VERSION.txt") if (debug) { cat("VERSION.txt\n") cat(readLines(version_path), "\n") } if (!file.exists(version_path)) { return(NULL) } # Rtools is in the path -- now crack the VERSION file contents <- NULL try(contents <- readLines(version_path), silent = TRUE) if (is.null(contents)) { return(NULL) } # Extract the version contents <- gsub("^\\s+|\\s+$", "", contents) version_re <- "Rtools version (\\d\\.\\d+)\\.[0-9.]+$" if (!grepl(version_re, contents)) { return(NULL) } m <- regexec(version_re, contents) regmatches(contents, m)[[1]][2] } is_compatible <- function(rtools) { if (is.null(rtools)) { return(FALSE) } if (is.null(rtools$version)) { return(FALSE) } stopifnot(is.rtools(rtools)) info <- version_info[[rtools$version]] if (is.null(info)) { return(FALSE) } r_version <- getRversion() r_version >= info$version_min && r_version <= info$version_max } rtools <- function(path, version, ...) { structure(list(version = version, path = path, ...), class = "rtools") } is.rtools <- function(x) inherits(x, "rtools") #' Retrieve a text string with the rtools version needed #' #' @keywords internal #' @export rtools_needed <- function(r_version = getRversion()) { vi <- version_info vi$custom <- NULL for (i in rev(seq_along(vi))) { version <- names(vi)[i] info <- vi[[i]] ok <- r_version >= info$version_min && r_version <= info$version_max if (ok) { return(paste("Rtools", version)) } } "the appropriate version of Rtools" } rtools_url <- function(needed) { "https://cran.r-project.org/bin/windows/Rtools/" } pkgbuild/R/exclude.R0000644000176200001440000002123514426741134014041 0ustar liggesusers copy_package_tree <- function( path = ".", dest, pkgname = desc::desc_get("Package", path)) { if (!file.exists(dest)) mkdirp(dest) pkgdir <- file.path(dest, pkgname) if (file.exists(pkgdir)) { stop(cli::format_error(c( "Cannot copy package tree to {.path {dest}}", i = "Directory {.path {pkgdir}} already exists, and did not want to overwrite." ))) } mkdirp(pkgdir) paths <- build_files(path, pkgname) method <- get_copy_method(path) for (i in seq_len(nrow(paths))) { # excluded, skip if (paths$exclude[i]) next if (paths$isdir[i] && paths$trimmed[i]) { # trimmed directory, only create directory # we do not try to update the mode of the directory, as it is not # very important for R packages, and it might fail on some file # systems. mkdirp(file.path(pkgdir, paths$path[i])) } else { # not a directory, or a non-trimmed directory, recurse src <- paths$realpath[i] tgt <- file.path(pkgdir, paths$path[i]) if (method == "link") { file.symlink(src, tgt) } else if (paths$isdir[i]) { cp(src, dirname(tgt), recursive = TRUE) } else { cp(src, tgt) } } } # TODO: should we return a trimming summary? invisible() } get_copy_method <- function(path = ".") { method <- getOption("pkg.build_copy_method", NA_character_) values <- c("none", "copy", "link") check_method <- function(method) { # no symlinks on Windows if (method == "link" && is_windows()) method <- "copy" if (method %in% values) return(method) stop(cli::format_error(c( "Invalid {.code pkg.build_copy_method} value: {.val {method}}.", i = "It must be one of {.str {values}}." ))) } if (!is_string(method) && !is_na(method)) { stop(cli::format_error(c( "Invalid {.code pkg.build_copy_method} value.", i = "It must be a string, but it is {.type {method}}." ))) } if (!is.na(method)) return(check_method(method)) method <- desc::desc_get("Config/build/copy-method", path) if (!is.na(method)) return(check_method(method)) method <- Sys.getenv("PKG_BUILD_COPY_METHOD", "none") check_method(method) } build_files <- function( path = ".", pkgname = desc::desc_get("Package", path)) { path <- normalizePath(path) # patterns in .Rbuildignore ign_file <- file.path(path, ".Rbuildignore") ign <- if (file.exists(ign_file)) { readLines(ign_file, warn = FALSE) } else { character() } ign <- ign[nzchar(ign)] # make it case insensitive, that's how R matches them if (length(ign)) ign <- paste0("(?i)", ign) ptrn <- c(ign, re_exclude(pkgname)) ptrn_dir <- re_exclude_dir(pkgname) # filter at the top level first, so we don't need to enumerate these top <- dir(path, include.dirs = TRUE, all.files=TRUE, no.. = TRUE) # now filter top realtop <- file.path(path, top) topfls <- data.frame( stringsAsFactors = FALSE, path = top, realpath = realtop, exclude = logical(length(top)), isdir = file.info(realtop)$isdir ) topfls <- exclude(path, topfls, ptrn, ptrn_dir) # now create the rest of the files sub <- unlist(lapply( topfls$path[topfls$isdir & !topfls$exclude], function(t) { tf <- dir(file.path(path, t), include.dirs = TRUE, all.files = TRUE, recursive = TRUE) tf <- file.path(t, tf) } )) realsub <- file.path(path, sub) subfls <- data.frame( stringsAsFactors = FALSE, path = sub, realpath = realsub, exclude = logical(length(sub)), isdir = file.info(realsub)$isdir ) subfls <- exclude(path, subfls, ptrn, ptrn_dir) allfls <- rbind(topfls, subfls) # Always keep this, so base R can do its own filtering, just in case # it changes compared to ours allfls$exclude[allfls$path == ".Rbuildignore"] <- FALSE allfls <- exclude_downstream(allfls) allfls } re_exclude <- function(pkg) { c( paste0( "(?i)", # these are case insensitive c( "(^|/)\\.DS_Store$", # by macOS finder "^\\.RData$", # .RData at / "~$", "\\.bak$", "\\.swp$", # backup files "(^|/)\\.#[^/]*$", "(^|/)#[^/]*#$", # more backup files (Emacs) "^config\\.(cache|log|status)$", # leftover by autoconf "(^|/)autom4te\\.cache$", "^src/.*\\.d$", "^src/Makedeps$", # INSTALL leftover on Windows "^inst/doc/Rplots\\.(ps|pdf)$" # Sweave leftover ) ), "(^|/)\\._[^/]*$", # macOS resource forks paste0( # hidden files "(^|/)\\.", c("Renviron", "Rprofile", "Rproj.user", "Rhistory", "Rapp.history", "tex", "log", "aux", "pdf", "png", "backups", "cvsignore", "cproject", "directory", "dropbox", "exrc", "gdb.history", "gitattributes", "github", "gitignore", "gitmodules", "hgignore", "hgtags", "htaccess", "latex2html-init", "project", "seed", "settings", "tm_properties" ), "$" ), paste0( "(^|/)", pkg, "_[0-9.-]+", "\\.(tar\\.gz|tar|tar\\.bz2|tar\\.xz|tgz|zip)", "$" ) ) } re_exclude_dir <- function(pkg) { c( "^revdep$", # revdepcheck paste0( # VC "(^|/)", c("CVS", ".svn", ".arch-ids", ".bzr", ".git", ".hg", "_darcs", ".metadata" ), "$" ), "(^|/)[^/]*[Oo]ld$", "(^|/)[^/]*\\.Rcheck", "^src.*/\\.deps$" ) } exclude <- function(root, paths, patterns, patterns_dir) { ex <- logical(nrow(paths)) for (p in patterns) { ex <- ex | grepl(p, paths$path, perl = TRUE) } # additional regexes for directories, we don't need to check 'ex' wdirs <- !ex & paths$isdir paths_dirs <- paths$path[wdirs] dex <- logical(length(paths_dirs)) for (p in patterns_dir) { dex <- dex | grepl(p, paths_dirs, perl = TRUE) } ex[wdirs][dex] <- TRUE paths$exclude <- ex paths } exclude_downstream <- function(paths) { # We don't actually need this now, but we could use it to optimize, # because we could trim only the subsequent elements after a directory. paths <- paths[order(paths$path), ] # We need to take each excluded directory, and remove all paths in them exdirs <- paste0(paths$path[paths$isdir & paths$exclude], "/") del <- logical(nrow(paths)) for (ed in exdirs) { del <- del | startsWith(paths$path, ed) } paths <- paths[!del, ] # Now we mark the subdirectories that can be copied as is, i.e. none of # their downstream contents are excluded indirs <- which(paths$isdir & !paths$exclude) trm <- logical(nrow(paths)) for (id in indirs) { trm[id] <- any( startsWith(paths$path, paste0(paths$path[id], "/")) & paths$exclude ) } paths$trimmed <- trm # We can remove the subdirectories of non-trimmed directories as well fulldirs <- paste0(paths$path[paths$isdir & !trm], "/") del2 <- logical(nrow(paths)) for (fd in fulldirs) { del2 <- del2 | startsWith(paths$path, fd) } paths <- paths[!del2, ] rownames(paths) <- NULL paths } cp <- local({ wind <- NULL cpargs <- NULL function(src, tgt, recursive = FALSE) { if (is.null(wind)) wind <<- is_windows() if (wind) { if (!file.copy(src, tgt, recursive = recursive, copy.date = TRUE)) { stop(cli::format_error(c( "Could not copy package files.", i = "Failed to copy {.path {src}} to {.path {tgt}}." ))) } } else { if (is.null(cpargs)) cpargs <<- detect_cp_args() ret <- processx::run( "cp", c(cpargs, src, tgt), stderr = "2>&1", error_on_status = FALSE ) if (ret$status != 0) { stop(cli::format_error(c( "Could not copy package files.", i = "Failed to copy {.path {src}} to {.path {tgt}}.", i = "{.code cp} output:", " " = verb_for_cli(ret$stdout) ))) } } } }) detect_cp_args <- function() { # we do this in a tempdir, because it might create a file a called # `--preserve=timestamps` dir.create(tmp <- tempfile()) old <- getwd() on.exit({ setwd(old); unlink(tmp, recursive = TRUE) }, add = TRUE) setwd(tmp) f1 <- basename(tempfile()) f2 <- basename(tempfile()) file.create(f1) tryCatch( suppressWarnings(processx::run("cp", c("--preserve=timestamps", f1, f2))), error = function(e) e ) if (file.exists(f2)) { c("-LR", "--preserve=timestamps") } else { "-pLR" } } pkgbuild/R/compiler.R0000644000176200001440000000550314323725614014223 0ustar liggesusers#' Is a compiler available? #' #' @description #' These functions check if a small C file can be compiled, linked, loaded #' and executed. #' #' `has_compiler()` and `has_devel()` return `TRUE` or `FALSE`. #' `check_compiler()` and `check_devel()` #' throw an error if you don't have developer tools installed. #' If the `"pkgbuild.has_compiler"` option is set to `TRUE` or `FALSE`, #' no check is carried out, and the value of the option is used. #' #' The implementation is based on a suggestion by Simon Urbanek. #' End-users (particularly those on Windows) should generally run #' [check_build_tools()] rather than [check_compiler()]. #' #' #' @export #' @inheritParams has_rtools #' @seealso [check_build_tools()] #' @examples #' has_compiler() #' check_compiler() #' #' with_build_tools(has_compiler()) has_compiler <- function(debug = FALSE) { res <- getOption("pkgbuild.has_compiler") if (!is.null(res)) { if (is_flag(res)) return(res) stop(cli::format_error(c( "", "!" = "Invalid {.code pkgbuild.has_compiler} option.", "i" = "It must be {.code TRUE} or {.code FALSE}, not {.type {res}}." ))) } if (!debug && cache_exists("has_compiler")) { return(cache_get("has_compiler")) } foo_path <- file.path(tempdir(), "foo.c") cat("void foo(int *bar) { *bar=1; }\n", file = foo_path) on.exit(unlink(foo_path)) res <- tryCatch( { if (debug) { message("Trying to compile a simple C file") } callr::rcmd_safe( "SHLIB", "foo.c", wd = tempdir(), show = debug, echo = debug, fail_on_status = TRUE, stderr = "2>&1" ) if (debug) { message("") } dylib <- file.path(tempdir(), paste0("foo", .Platform$dynlib.ext)) on.exit(unlink(dylib), add = TRUE) dll <- dyn.load(dylib) on.exit(dyn.unload(dylib), add = TRUE) .C(dll$foo, 0L)[[1]] == 1L }, error = function(e) { FALSE } ) cache_set("has_compiler", res) res } #' @export #' @rdname has_compiler check_compiler <- function(debug = FALSE) { if (!has_compiler(debug)) { stop("Failed to compile C code", call. = FALSE) } TRUE } #' @export #' @rdname has_compiler #' @usage NULL has_devel <- check_build_tools # The checking code looks for the objects in the package namespace, so defining # dll here removes the following NOTE # Registration problem: # Evaluating 'dll$foo' during check gives error # 'object 'dll' not found': # .C(dll$foo, 0L) # See https://github.com/wch/r-source/blob/d4e8fc9832f35f3c63f2201e7a35fbded5b5e14c/src/library/tools/R/QC.R#L1950-L1980 # Setting the class is needed to avoid a note about returning the wrong class. # The local object is found first in the actual call, so current behavior is # unchanged. dll <- list(foo = structure(list(), class = "NativeSymbolInfo")) pkgbuild/R/rtools-registry.R0000644000176200001440000000172114211634664015577 0ustar liggesusersread_registry <- function(...) { tryCatch( utils::readRegistry(...), error = function(e) NULL ) } scan_registry_for_rtools <- function(debug = FALSE) { if (debug) cat("Scanning registry...\n") keys <- c( read_registry("SOFTWARE\\R-core\\Rtools", hive = "HCU", view = "32-bit", maxdepth = 2), read_registry("SOFTWARE\\R-core\\Rtools", hive = "HLM", view = "32-bit", maxdepth = 2), read_registry("SOFTWARE\\R-core\\Rtools", hive = "HLM", view = "64-bit", maxdepth = 2) ) if (is.null(keys)) { return(NULL) } rts <- vector("list", length(keys)) for (i in seq_along(keys)) { version <- names(keys)[[i]] key <- keys[[version]] if (!is.list(key) || is.null(key$InstallPath)) next install_path <- normalizePath(key$InstallPath, mustWork = FALSE, winslash = "/" ) if (debug) cat("Found", install_path, "for", version, "\n") rts[[i]] <- rtools(install_path, version) } Filter(Negate(is.null), rts) } pkgbuild/NEWS.md0000644000176200001440000001426314575572522013175 0ustar liggesusers# pkgbuild 1.4.4 * pkgbuild now supports R 4.4.x and Rtools44 (#183). # pkgbuild 1.4.3 * pkgbuild now does not need the crayon, rprojroot and prettyunits packages. # pkgbuild 1.4.2 * Running `bootstrap.R` now works with `pkgbuild_process`, so it also works from pak (https://github.com/r-lib/pak/issues/508). # pkgbuild 1.4.1 * New `Config/build/extra-sources` `DESCRIPTION` option to make pkgbuild aware of extra source files to consider in `needs_compile()`. * New `Config/build/bootstrap` `DESCRIPTION` option. Set it to `TRUE` to run `Rscript bootstrap.R` in the package root prior to building the source package (#157, @paleolimbot). * pkgbuild now supports Rtools43. * pkgbuild now always _appends_ its extra compiler flags to the ones that already exist in the system and/or user `Makevars` files (#156). # pkgbuild 1.4.0 * pkgbuild can now avoid copying large package directories when building a source package. See the `PKG_BUILD_COPY_METHOD` environment variable in `?build` or the package README (#59). This is currently an experimental feature, and feedback is appreciated. * `R CMD build` warnings can now be turned into errors, by setting the `pkg.build_stop_for_warnings` option to `TRUE` or by setting the `PKG_BUILD_STOP_FOR_WARNINGS` environment variable to `true` (#114). * `need_compile()` now knows about Rust source code files, i.e. `Cargo.toml` and `*.rs` (#115). * Now `pkgbuild::build()` will not clean up `inst/doc` by default if the `Config/build/clean-inst-doc` entry in `DESCRIPTION` is set to `FALSE` (#128). * New `PKG_BUILD_COLOR_DIAGNOSTICS` environment variable to opt out from colored compiler output (#141). * pkgbuild now works with a full XCode installation if the XCode Command Line Tools are not installed, on macOS, in RStudio (#103). # pkgbuild 1.3.1 * Accept Rtools40 for R 4.2, it works well, as long as the PATH includes both `${RTOOLS40_HOME}/usr/bin` and `${RTOOLS40_HOME}/ucrt64/bin`. E.g. `~/.Renviron` should contain now ``` PATH="${RTOOLS40_HOME}\usr\bin;${RTOOLS40_HOME}\ucrt64\bin;${PATH}" ``` to make Rtools40 work with both R 4.2.x (devel currently) and R 4.1.x and R 4.0.x. # pkgbuild 1.3.0 * pkgbuild now supports Rtools 4.2. * pkgbuild now returns the correct path for R 3.x (#96). * `build()` now always returns the path of the built package (#108). * pkgbuild output now looks better in `.Rmd` documents and in general in non-dynamic terminals. You can also force dynamic and non-dynamic output now (#64). * pkgbuild does not build the PDF manual now if `pdflatex` is not installed, even if `manual = TRUE` (#123). # pkgbuild 1.2.1 * Gábor Csárdi is now the maintainer. * `build_setup_source` now considers both command-line build arguments, as well as parameters `vignettes` or `manual` when conditionally executing flag-dependent behaviors (@dgkf, #120) # pkgbuild 1.2.0 * pkgbuild is now licensed as MIT (#106) * `compile_dll()` gains a `debug` argument for more control over the compile options used (@richfitz, #100) * `pkgbuild_process()` and `build()` now use colored compiler diagnostics if supported (#102) * Avoid documentation link ambiguity in R 4.1 (#105) # pkgbuild 1.1.0 * `compile_dll()` now supports automatic cpp11 registration if the package links to cpp11. * `rtools_needed` returns correct version instead of "custom" (@burgerga, #97) # pkgbuild 1.0.8 * Fixes for capability RStudio 1.2. and Rtools 40, R 4.0.0 # pkgbuild 1.0.7 * Additional fixes for Rtools 40 # pkgbuild 1.0.6 * Support for RTools 40 and custom msys2 toolchains that are explicitly set using the `CC` Makevars (#40). # pkgbuild 1.0.5 * `check_build_tools()` gains a `quiet` argument, to control when the message is displayed. The message is no longer displayed when `check_build_tools()` is called internally by pkgbuild functions. (#83) # pkgbuild 1.0.4 * `build()` gains a `clean_doc` argument, to control if the `inst/doc` directory is cleaned before building. (#79, #75) * `build()` and `pkgbuild_process` now have standard output and error are correctly interleaved, by redirecting the standard error of build process to the standard output (@gaborcsardi, #78). * `check_build_tools()` now has a more helpful error message which points you towards ways to debug the issue (#68). * `pkgbuild_process` now do not set custom compiler flags, and it uses the user's `Makevars` file (@gaborcsardi, #76). * `rtools_path()` now returns `NA` on non-windows systems and also works when `has_rtools()` has not been run previously (#74). # pkgbuild 1.0.3 * Tests which wrote to the package library are now skipped on CRAN. * `build()` can now build a tar.gz file directly (#55) # pkgbuild 1.0.2 * `build()` and `compile_dll()` gain a `register_routines` argument, to automatically register C routines with `tools::package_native_routines_registration_skeleton()` (#50) * `build()` will now warn if trying to build packages on R versions <= 3.4.2 on Windows with a space in the R installation directory (#49) * `build()` will now message if a build contains long paths, which are unsupported on windows (#48) * `compile_dll()` no longer doubles output, a regression caused by the styling callback. (https://github.com/r-lib/devtools/issues/1877) * `build()` output is now styled like that in the rcmdcheck package (https://github.com/r-lib/devtools/issues/1874). * `build()` no longer sets compile flags (#46) # pkgbuild 1.0.1 * Preliminary support for rtools 4.0 (#40) * `compile_dll()` now does not supply compiler flags if there is an existing user defined Makevars file. * `local_build_tools()` function added to provide a deferred equivalent to `with_build_tools()`. So you can add rtools to the PATH until the end of a function body. # pkgbuild 1.0.0 * Add metadata to support Rtools 3.5 (#38). * `build()` only uses the `--no-resave-data` argument in `R CMD build` if the `--resave-data` argument wasn't supplied by the user (@theGreatWhiteShark, #26) * `build()` now cleans existing vignette files in `inst/doc` if they exist. (#10) * `clean_dll()` also deletes `symbols.rds` which is created when `compile_dll()` is run inside of `R CMD check`. * First argument of all functions is now `path` rather than `pkg`. pkgbuild/MD50000644000176200001440000001207314575600302012370 0ustar liggesusers2ff5058e0032a3b72a1e153a3ba9987d *DESCRIPTION faf2baf0ecd138dc63fc451973987996 *LICENSE 5fba5a95f84bcf1d2405e167c7ecd4e6 *NAMESPACE 5ffe33098f81f2f29b2bd88213189758 *NEWS.md 78b67ff42c1a26e46729aed96cf409b3 *R/build-bg.R a62051336b2cb8b37c23d0e0aae56d29 *R/build-tools.R 5332a09284986d079d102f7380c76472 *R/build.R 2b9ad84f281faa439fe10c04532d422d *R/c-registration.R 8d41d120d4955be4a6fe8607ea96c174 *R/cache.R 21454efb84eb5a18056bdf7398f36674 *R/callback.R 7b0848d2a79d737edd2d2a882d0e3715 *R/compile-dll.R da3c666884c5f7d0f9ffa01800ac311e *R/compiler-flags.R f1a406e5772d7ec1c4795c361113c006 *R/compiler.R f370d4cc2301e8e38560135eeee1d221 *R/exclude.R e2546b166c5451d3a826178fb95a958c *R/find-package-root.R 6569915ddd3954f37aa09e57bbfd2edd *R/has_src.R 44ef843fd141c767d3029cb643d6a028 *R/latex.R df6cc46bc7fae1a55b713f3d5065b35a *R/pkgbuild-package.R 7245e0871eeb4883cd9a9c1d05891c7d *R/rcmd.R f551ea3042a32ae816845561f8ed00aa *R/rtools-cache.R 949db6c5b8746b45777de518e15f16b3 *R/rtools-config.R 86d9dbab9c58327d28f8fe3be75f6ee7 *R/rtools-metadata.R 47b06e012e10449ffaeccdae7da00843 *R/rtools-path.R b1baa30e372abf4264be670be0095f9f *R/rtools-registry.R edc9edbb4ffcefc1829651c3c49df326 *R/rtools.R c92adeb11a5e67591e974c6f0d5ccdab *R/styles.R c362b7ba1d9f4e5862d85901c1e3cc14 *R/time.R ca568b62a08ace7ea4eaf33046f3a716 *R/utils.R 98bb021e6a9e06686af3830349e7b1cc *R/with-debug.R 6a1dc63e6ab3c14140f435eb672d7dd2 *R/withr.R 714cd2029e57f398bf9f9f1ff4c32702 *README.md 3280a80f4395a1db1f24ac6608cc6f70 *man/build.Rd b16255149b3caf04d128de8053594c71 *man/clean_dll.Rd 3c9fa4f2463b025b9b38c8cfcda4db51 *man/compile_dll.Rd 73f325dcf528758ff067b0603c6a048e *man/compiler_flags.Rd 2389b5cc2aa56411e3b30d0e62b7263a *man/has_build_tools.Rd b770baba0361a4388737b429b699950f *man/has_compiler.Rd 28b37e7ae90236503c5abf076ce680df *man/has_latex.Rd 483eac2edac3d166291adf95f1fc46e1 *man/has_rtools.Rd d16da465eef5bd0c68c48b643b928bee *man/needs_compile.Rd 88a5840825815f8def991da5111fda98 *man/pkg_has_src.Rd cc1e8d29bc22e105566a563c65175d38 *man/pkg_links_to_rcpp.Rd 7668f726d49928478dd83e383bcd7204 *man/pkgbuild-package.Rd cc6699fab7f21a8a34afe30d30d78e8c *man/pkgbuild_process.Rd fa69989496d2b1b4471e4c82948e599f *man/rcmd_build_tools.Rd 356852ee1b8d4d73bde41307fbfb9964 *man/rtools_needed.Rd 474083d45f34132224732b732e4164fe *man/with_debug.Rd 2e8086dd548443e4ad51c0aee9611ce1 *man/without_compiler.Rd d11922ac9eab43de2d4ff0bb06b9465e *tests/build-tools.R 2fb58861d239d4cb77f513d8657104ea *tests/testthat.R 4d8706534667ef814069cbaedd29e877 *tests/testthat/_snaps/build.md ab4886102479e59d26cc52c561b022b6 *tests/testthat/_snaps/compiler.md fc63add8ded7896d0242ca00c6f3489c *tests/testthat/_snaps/exclude.md bec9b69699853278a5eed25c29308765 *tests/testthat/_snaps/style.md bdb20063b70d63f163200f3c75bd9e92 *tests/testthat/_snaps/utils.md 335cf2b43bac47c1f492e231cb977233 *tests/testthat/fixtures/testDummy_0.1.tar.gz b08df40d59aa3c817f4d7651badc77e0 *tests/testthat/fixtures/testWithSrc_0.1.tar.gz aef8f02891aa60e66955e23356e6e3a4 *tests/testthat/fixtures/xxx.gz 6dc77ce3ba4e9b5ecb2d5e53e7a129e2 *tests/testthat/fixtures/xxx.tar.gz 267812fc7bc60f0a742afcfc33acfc62 *tests/testthat/fixtures/xxx.zip 1a298502f88bbcdd5a7256edaa57480d *tests/testthat/test-archives.R 62988514d1bc664b725c3ffc66658299 *tests/testthat/test-build-process.R 445dc7fca38b706b7e409de8f366503f *tests/testthat/test-build.R 86bcab16757e9c672f1f5263073f694b *tests/testthat/test-build_tools.R b75ef7003cf9f964bae1a982db785d43 *tests/testthat/test-c-registration.R 8aafc02de8bb99a9d2ffdb990b92375c *tests/testthat/test-compile_dll.R 7a5bac2e1c076bbfec1e94df0b8fd7e8 *tests/testthat/test-compiler-flags.R 0b2a3ea5c950818b39171c83ed165a6b *tests/testthat/test-compiler.R 292759090fd642fdf2ef5caa3b0cee09 *tests/testthat/test-exclude.R 72efc0f2f28309dee337370ac3683d36 *tests/testthat/test-find-package-root.R 68e7408b02e8085a435b44913acdc66d *tests/testthat/test-rtools.R def62e429ecb3dd1b190da179e097064 *tests/testthat/test-style.R 22c4024d36383120bc74c1f8aa66d147 *tests/testthat/test-utils.R f2ccf899dbafd628e8fae8357dfdbe63 *tests/testthat/test-withr.R 3d687f7cc510f4b045ab5379a1546cfc *tests/testthat/testDummy/DESCRIPTION 85601cb90b291bb14a88bc9e7349b653 *tests/testthat/testDummy/NAMESPACE 26813d0f7f8e272af05102662163c53b *tests/testthat/testDummy/R/a.R aa6661f9ad2794e7d2c6d8a36023e78b *tests/testthat/testDummy/R/b.R 4bcab09205448a0f7a368ad955997cd3 *tests/testthat/testInstDoc/DESCRIPTION 85601cb90b291bb14a88bc9e7349b653 *tests/testthat/testInstDoc/NAMESPACE 26813d0f7f8e272af05102662163c53b *tests/testthat/testInstDoc/R/a.R aa6661f9ad2794e7d2c6d8a36023e78b *tests/testthat/testInstDoc/R/b.R 05a9473ea4dc54ad4ee75a7d41f132cf *tests/testthat/testInstDoc/inst/doc/keep.me 2241e854ebd36660cb0e132afce33bd5 *tests/testthat/testInstDoc/vignettes/test.Rmd 77191fb130945287a52f77be7be32132 *tests/testthat/testWithSrc/DESCRIPTION 85601cb90b291bb14a88bc9e7349b653 *tests/testthat/testWithSrc/NAMESPACE 26813d0f7f8e272af05102662163c53b *tests/testthat/testWithSrc/R/a.R aa6661f9ad2794e7d2c6d8a36023e78b *tests/testthat/testWithSrc/R/b.R f25a893824d1be847ed0e681595b9f78 *tests/testthat/testWithSrc/src/add1.c