pkgbuild/0000755000176200001440000000000013547363742012071 5ustar liggesuserspkgbuild/NAMESPACE0000644000176200001440000000161113522347771013304 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_rcpp) export(pkgbuild_process) export(rcmd_build_tools) export(rtools_path) export(setup_rtools) export(with_build_tools) export(with_debug) export(without_cache) export(without_compiler) importFrom(R6,R6Class) importFrom(callr,rcmd_process) importFrom(callr,rcmd_process_options) importFrom(cli,symbol) importFrom(crayon,bold) importFrom(crayon,green) importFrom(crayon,make_style) importFrom(crayon,red) importFrom(prettyunits,pretty_dt) importFrom(utils,head) importFrom(utils,tail) pkgbuild/README.md0000644000176200001440000000216213346243032013333 0ustar liggesusers# pkgbuild [![Travis-CI Build Status](https://travis-ci.org/r-lib/pkgbuild.svg?branch=master)](https://travis-ci.org/r-lib/pkgbuild) [![AppVeyor Build Status](https://ci.appveyor.com/api/projects/status/github/r-lib/pkgbuild?branch=master&svg=true)](https://ci.appveyor.com/project/hadley/pkgbuild) [![Coverage status](https://codecov.io/gh/r-lib/pkgbuild/branch/master/graph/badge.svg)](https://codecov.io/github/r-lib/pkgbuild?branch=master) 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 You can install pkgbuild from github with: ``` r # install.packages("devtools") devtools::install_github("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) ``` pkgbuild/man/0000755000176200001440000000000013361444775012644 5ustar liggesuserspkgbuild/man/pkg_links_to_rcpp.Rd0000644000176200001440000000056713361362706016643 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/rcpp-attributes.R \name{pkg_links_to_rcpp} \alias{pkg_links_to_rcpp} \title{Test if a package path is linking to Rcpp} \usage{ pkg_links_to_rcpp(path) } \arguments{ \item{path}{Path to a package, or within a package.} } \description{ Test if a package path is linking to Rcpp } \keyword{internal} pkgbuild/man/has_build_tools.Rd0000644000176200001440000000417113522347771016305 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}{\code{[environment]}\cr 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 \code{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.Rd0000644000176200001440000000054213171400122015370 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 \code{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.Rd0000644000176200001440000000101613171400122016477 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} \title{Tools for testing pkgbuild} \usage{ without_compiler(code) without_cache(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.Rd0000644000176200001440000000040713171400122015055 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.Rd0000644000176200001440000000072513361362706015736 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.Rd0000644000176200001440000000313513361444775015420 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_rcpp(path), register_routines = FALSE, quiet = FALSE) } \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.} } \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. } \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: \code{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.Rd0000644000176200001440000000170413522037444015570 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{ \code{has_devel} returns \code{TRUE} or \code{FALSE}. \code{check_devel} throws an error if you don't have developer tools installed. Implementation 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.Rd0000644000176200001440000000061713253022562015036 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.Rd0000644000176200001440000000502413522042146014214 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 \code{NULL}, 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 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 \code{R CMD build} if \code{binary = FALSE}, or \code{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. } pkgbuild/man/has_rtools.Rd0000644000176200001440000000255613253022562015302 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.Rd0000644000176200001440000000167413350240703016110 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.Rd0000644000176200001440000000207613352707530016453 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 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{\link[callr:rcmd_safe_env]{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{\link[callr:rcmd_safe]{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.Rd0000644000176200001440000000160513350240703015235 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/pkgbuild_process.Rd0000644000176200001440000000242513252501550016455 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}{ \preformatted{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 \link[callr:rcmd_process]{callr::rcmd_process} and \link[processx:process]{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 \link[processx:process]{processx::process}. \code{bp$get_dest_path()} returns the path to the built package. } \section{Examples}{ \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() } } pkgbuild/DESCRIPTION0000644000176200001440000000175513547363742013607 0ustar liggesusersPackage: pkgbuild Title: Find Tools Needed to Build R Packages Version: 1.0.6 Authors@R: c( person("Hadley", "Wickham", role = "aut"), person("Jim", "Hester", , "jim.hester@rstudio.com", role = c("aut", "cre")), person("RStudio", role = "cph") ) 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. Imports: callr (>= 3.2.0), cli, crayon, desc, prettyunits, R6, rprojroot, withr (>= 2.1.2) Suggests: Rcpp, testthat, covr License: GPL-3 Encoding: UTF-8 LazyData: true RoxygenNote: 6.1.1 URL: https://github.com/r-lib/pkgbuild BugReports: https://github.com/r-lib/pkgbuild/issues Depends: R (>= 3.1) NeedsCompilation: no Packaged: 2019-10-09 13:10:49 UTC; jhester Author: Hadley Wickham [aut], Jim Hester [aut, cre], RStudio [cph] Maintainer: Jim Hester Repository: CRAN Date/Publication: 2019-10-09 14:00:02 UTC pkgbuild/tests/0000755000176200001440000000000013254006616013220 5ustar liggesuserspkgbuild/tests/testthat/0000755000176200001440000000000013547363742015073 5ustar liggesuserspkgbuild/tests/testthat/testDummy/0000755000176200001440000000000013057275664017067 5ustar liggesuserspkgbuild/tests/testthat/testDummy/NAMESPACE0000644000176200001440000000001213057275664020277 0ustar liggesusersexport(a) pkgbuild/tests/testthat/testDummy/DESCRIPTION0000644000176200001440000000027013057275664020574 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/0000755000176200001440000000000013057275664017270 5ustar liggesuserspkgbuild/tests/testthat/testDummy/R/a.r0000644000176200001440000000000713057275664017670 0ustar liggesusersa <- 1 pkgbuild/tests/testthat/testDummy/R/b.r0000644000176200001440000000000713057275664017671 0ustar liggesusersb <- 2 pkgbuild/tests/testthat/fixtures/0000755000176200001440000000000013441730077016734 5ustar liggesuserspkgbuild/tests/testthat/fixtures/xxx.zip0000644000176200001440000000024013441730077020303 0ustar liggesusersPK U8JgtxxxUT 0XXux xxx PK U8JgtxxxUT0Xux PKIApkgbuild/tests/testthat/fixtures/xxx.tar.gz0000644000176200001440000000021413441730077020707 0ustar liggesusers1XA 0F=ay-B87("y߄ݻT5(o"J0!ڠw˪|ϥVs.e_l?%MZpݿ{(pkgbuild/tests/testthat/fixtures/testWithSrc_0.1.tar.gz0000644000176200001440000000113513441730077022724 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.gz0000644000176200001440000000003413441730077020122 0ustar liggesusers0Xxxxgtpkgbuild/tests/testthat/fixtures/testDummy_0.1.tar.gz0000644000176200001440000000063113441730077022434 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-c-registration.R0000644000176200001440000000440113361362706021115 0ustar liggesuserscontext("c-registration.R") test_that("update_c_registration does nothig 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/test-compiler.R0000644000176200001440000000062113254006645017773 0ustar liggesuserscontext("test-compiler.R") describe("has_compiler", { 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()) }) }) }) pkgbuild/tests/testthat/test-build_tools.R0000644000176200001440000000051513254006645020502 0ustar liggesuserscontext("build_tools") 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/0000755000176200001440000000000013057275664017357 5ustar liggesuserspkgbuild/tests/testthat/testWithSrc/NAMESPACE0000644000176200001440000000001213057275664020567 0ustar liggesusersexport(a) pkgbuild/tests/testthat/testWithSrc/DESCRIPTION0000644000176200001440000000027213057275664021066 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/0000755000176200001440000000000013522350117020126 5ustar liggesuserspkgbuild/tests/testthat/testWithSrc/src/add1.c0000644000176200001440000000056513361362706021121 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/0000755000176200001440000000000013522350117017540 5ustar liggesuserspkgbuild/tests/testthat/testWithSrc/R/a.r0000644000176200001440000000000713057275664020160 0ustar liggesusersa <- 1 pkgbuild/tests/testthat/testWithSrc/R/b.r0000644000176200001440000000000713361151230020135 0ustar liggesusersb <- 2 pkgbuild/tests/testthat/test-compile_dll.R0000644000176200001440000000040013361362706020441 0ustar liggesuserscontext("test-compile_dll.R") 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-rtools.r0000644000176200001440000000127613254006626017551 0ustar liggesuserscontext("test-rtools.R") 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() != "") })) }) pkgbuild/tests/testthat/test-build.r0000644000176200001440000000437513361362706017334 0ustar liggesuserscontext("build") # 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") }) pkgbuild/tests/testthat/test-archives.R0000644000176200001440000000203313441730077017765 0ustar liggesusers context("Package archives") 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-build-process.r0000644000176200001440000000674413522035663021010 0ustar liggesuserscontext("pkgbuild_process") # 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/build-tools.R0000644000176200001440000000006313254006616015577 0ustar liggesuserslibrary(pkgbuild) check_build_tools(debug = TRUE) pkgbuild/tests/testthat.R0000644000176200001440000000007413254006616015204 0ustar liggesuserslibrary(testthat) library(pkgbuild) test_check("pkgbuild") pkgbuild/R/0000755000176200001440000000000013546672122012264 5ustar liggesuserspkgbuild/R/rtools-cache.R0000644000176200001440000000135613522037162014770 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 if (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() { isTRUE(sub("^gcc[^[:digit:]]+", "", Sys.getenv("R_COMPILED_BY")) >= "4.9.3") } pkgbuild/R/utils.R0000644000176200001440000000273513406511163013546 0ustar liggesusersdir.exists <- function(x) { res <- file.exists(x) & file.info(x)$isdir stats::setNames(res, x) } pkg_path <- function(path = ".") { rprojroot::find_root("DESCRIPTION", 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_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 = "") } pkgbuild/R/build-bg.R0000644000176200001440000000711413522041667014075 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) rcb_init(self, private, super, path, dest_path, binary, vignettes, manual, clean_doc, args, needs_compilation, compile_attributes, register_routines), finalize = function() 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(...) super$kill(...) ), private = list( path = NULL, dest_path = NULL, out_dir = NULL, out_file = NULL ) ) #' @importFrom callr rcmd_process rcmd_process_options rcb_init <- function(self, private, super, path, dest_path, binary, vignettes, manual, clean_doc, args, needs_compilation, compile_attributes, register_routines) { options <- build_setup(path, dest_path, binary, vignettes, manual, clean_doc, args, needs_compilation, compile_attributes, register_routines) private$path <- options$path private$dest_path <- options$dest_path private$out_dir <- options$out_dir ## Build tools already checked in setup options <- 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.R0000644000176200001440000000316713254006722014446 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 withr::with_envvar #' @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", CXX1X = "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 } pkgbuild/R/styles.R0000644000176200001440000000116413352707530013731 0ustar liggesusers#' @importFrom crayon red green make_style bold # 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" = green, "note" = make_style("orange"), "warn" = make_style("orange") $ bold, "err" = red, "pale" = make_style("darkgrey"), "timing" = make_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.R0000644000176200001440000000317413546672122015514 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 = "99.99.99", path = "bin" ), "3.5" = list( version_min = "3.3.0", version_max = "99.99.99", path = "bin" ), "4.0" = list( version_min = "3.3.0", version_max = "99.99.99", path = "bin" ), "custom" = list( version_min = "2.10.0", version_max = "99.99.99", path = "bin" ) ) pkgbuild/R/has_src.R0000644000176200001440000000312113441730110014010 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.R0000644000176200001440000001156313522347663014621 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. #' #' @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`. #' @seealso [clean_dll()] to delete the compiled files. #' @export compile_dll <- function(path = ".", force = FALSE, compile_attributes = pkg_links_to_rcpp(path), register_routines = FALSE, quiet = FALSE) { path <- pkg_path(path) if (!needs_compile(path) && !isTRUE(force)) { return(invisible()) } check_build_tools(quiet = TRUE) update_registration(path, compile_attributes, register_routines) # Mock install the package to generate the DLL if (!quiet) message("Re-compiling ", pkg_name(path)) install_dir <- tempfile("devtools_install_") dir.create(install_dir) # If the user has a makevars file just use that if (length(makevars_user()) > 0) { install_min( path, dest = install_dir, components = "libs", args = if (needs_clean(path)) "--preclean", quiet = quiet ) } else { # Otherwise set makevars for fast development / debugging withr::with_makevars(compiler_flags(TRUE), assignment = "+=", { 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)))) } #' 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) } # List all source files in the package sources <- function(path = ".") { srcdir <- file.path(path, "src") dir(srcdir, "\\.(c.*|f)$", recursive = TRUE, full.names = TRUE) } # 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.R0000644000176200001440000000130113546672122015167 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 (!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) } } pkgbuild/R/compiler-flags.R0000644000176200001440000000406313352713656015321 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" ) } else if (debug) { c( CFLAGS = "-UNDEBUG -Wall -pedantic -g -O0", CXXFLAGS = "-UNDEBUG -Wall -pedantic -g -O0", CXX11FLAGS = "-UNDEBUG -Wall -pedantic -g -O0", FFLAGS = "-g -O0", FCFLAGS = "-g -O0" ) } else { c( CFLAGS = "-Wall -pedantic", CXXFLAGS = "-Wall -pedantic", CXX11FLAGS = "-Wall -pedantic" ) } if (crayon::has_color() && has_compiler_colored_diagnostics()) { res[c("CFLAGS", "CXXFLAGS", "CXX11FLAGS")] <- paste(res[c("CFLAGS", "CXXFLAGS", "CXX11FLAGS")], "-fdiagnostics-color=always") } res } has_compiler_colored_diagnostics <- function() { 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.R0000644000176200001440000000045713254006722013523 0ustar liggesusers#' Is latex installed? #' #' Checks for presence of pdflatex on path. #' #' @export has_latex <- function() { 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.R0000644000176200001440000000615713361362706015351 0ustar liggesusersupdate_registration <- function(path, compile_attributes, register_routines) { if (compile_attributes) { compile_rcpp_attributes(path) } 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) ) ) } } pkgbuild/R/rcmd.R0000644000176200001440000000423713522035663013337 0ustar liggesusers#' Call R CMD 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) warn_for_potential_errors() res <- with_build_tools( callr::rcmd_safe(..., env = env, spinner = FALSE, show = FALSE, echo = FALSE, block_callback = block_callback(quiet), stderr = "2>&1"), 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.R0000644000176200001440000000076113254006722013447 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.R0000644000176200001440000001317713522347630013514 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. #' #' @param path Path to a package, or within a package. #' @param dest_path path in which to produce package. If `NULL`, 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 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) on.exit(unlink(options$out_dir, recursive = TRUE), add = TRUE) 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 ) ) out_file <- dir(options$out_dir) file.copy( file.path(options$out_dir, out_file), options$dest_path, overwrite = TRUE) file.path(options$dest_path, out_file) } build_setup <- function(path, dest_path, binary, vignettes, manual, clean_doc, args, needs_compilation, compile_attributes, register_routines) { 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) } if (needs_compilation) { update_registration(path, compile_attributes, register_routines) } 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 && !has_latex()) { message("pdflatex not found! Not building PDF manual.") manual <- FALSE } if (needs_compilation && (vignettes || manual)) { check_build_tools(quiet = TRUE) } if (!manual) { args <- c(args, "--no-manual") } if (!vignettes) { args <- c(args, "--no-build-vignettes") } else if (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) list( cmd = "build", path = normalizePath(path), args = args, out_dir = out_dir, dest_path = dest_path ) } pkgbuild/R/rtools-path.R0000644000176200001440000000335313546672122014667 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.R0000644000176200001440000000532413522350057014642 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) if (!is.null(check)) { check("Building R package from source") } else if (is_windows()) { has_rtools(debug = debug) } else { has_compiler(debug = debug) } } #' @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 #' @inheritParams withr::local_path #' @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/callback.R0000644000176200001440000000513713352707530014146 0ustar liggesusers#' @importFrom cli symbol #' @importFrom utils head tail #' @importFrom prettyunits pretty_dt # 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(" (", 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 = 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(symbol$line, " ", no(x))) } else if (grepl("^\\* DONE\\s*$", x)) { state <<- "OK" NA_character_ } else { style(pale = c(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) } pkgbuild/R/rtools.R0000644000176200001440000001451613406446056013740 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) # First, R CMD config CC -------------------------------------------- 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 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 ", rtools_needed(), " from ", rtools_url, ", 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 message("WARNING: Rtools is required to build R packages, but is not ", "currently installed.\n\n", "Please download and install ", rtools_needed(), " from ", rtools_url, ".") 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)) 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 ", rtools_needed(), " from ", rtools_url, ".") return(invisible(FALSE)) } installed_ver <- installed_version(from_registry$path, debug = debug) if (is.null(installed_ver)) { # Previously installed version now deleted 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 ", rtools_needed(), " from ", rtools_url, ".") return(invisible(FALSE)) } if (installed_ver != from_registry$version) { # Installed version doesn't match registry version 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 ", rtools_needed(), " from ", rtools_url, ".") return(invisible(FALSE)) } # Otherwise it must be ok :) rtools_path_set(from_registry) TRUE } #' @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") rtools_needed <- function() { r_version <- getRversion() for (i in rev(seq_along(version_info))) { version <- names(version_info)[i] info <- version_info[[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 <- "http://cran.r-project.org/bin/windows/Rtools/" pkgbuild/R/rcpp-attributes.R0000644000176200001440000000113413361362706015535 0ustar liggesusers# Call Rcpp::compileAttributes() for packages that link to Rcpp. # Assumes Rcpp is installed compile_rcpp_attributes <- function(path = ".") { path <- pkg_path(path) if (pkg_links_to_rcpp(path)) { unlink(file.path(path, c("R/RcppExports.R", "src/RcppExports.cpp"))) Rcpp::compileAttributes(path) } } #' Test if a package path is linking to Rcpp #' @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") } pkgbuild/R/compiler.R0000644000176200001440000000423213522035663014217 0ustar liggesusers#' Is a compiler available? #' #' `has_devel` returns `TRUE` or `FALSE`. `check_devel` #' throws an error if you don't have developer tools installed. Implementation #' 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) { 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.R0000644000176200001440000000170313546672122015600 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.md0000644000176200001440000000535213547354652013174 0ustar liggesusers# 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/MD50000644000176200001440000000713113547363742012403 0ustar liggesusersffa15337c522a0e38b62cb0593888764 *DESCRIPTION 5322d4fe4bd99f9b09f113749440eb56 *NAMESPACE fd3a11cc4751de1416c24bef2fddb0df *NEWS.md a12516d8c68e725848aeb582c164caa3 *R/build-bg.R 33300800588fe78e9a1b04f014417a19 *R/build-tools.R 129dfad978aee4d9b6b3cd207b2061c3 *R/build.R ca0283e0428d2eadd2a62cc40f849510 *R/c-registration.R 8d41d120d4955be4a6fe8607ea96c174 *R/cache.R 147e1846a39d0d14f73cbc14aa13d136 *R/callback.R ff3ae7239990c6766b6ba55f4173989d *R/compile-dll.R 5350323d38eff4cbf7fe9c529a9aea28 *R/compiler-flags.R b110372f3b80823b810d4641c3af81b1 *R/compiler.R fd0876609b6b761c36cd165da7b7f146 *R/has_src.R 649c3819a0e41c6ff6f32c845c435097 *R/latex.R 85d179643b86dab17fcafab256e557f9 *R/rcmd.R a408aaa989410ef000caaa678c287d0a *R/rcpp-attributes.R 62a3a909b8c0ca6480dd53bf6557c326 *R/rtools-cache.R 84e08ca5c936c91acc0ae3344636e853 *R/rtools-config.R 117a59f38b4cfd3b073f7ee1fd4887bf *R/rtools-metadata.R f24838e0333dc2bc3a7ca6e8f27883d9 *R/rtools-path.R a47b642b208c96cd9886a023d1aca291 *R/rtools-registry.R d9d0d74a4fb5e49e5b34ac2622da691f *R/rtools.R f005f154dcfde9d9b32708bd9eb8c76b *R/styles.R 260b980b205829677986ac73ef43211c *R/utils.R 969b0c7e558c9098e93a7a099428d993 *R/with-debug.R 95c503ae376857f9c541e43b8c0adf9f *README.md ad076683a1781ebff76486aeeac602ff *man/build.Rd b16255149b3caf04d128de8053594c71 *man/clean_dll.Rd 4784301196c26138ddd09243309d5708 *man/compile_dll.Rd 33023e6647eb9409f92043a7fdb44ccd *man/compiler_flags.Rd ccf05433d231c281d0c5f818f67b7912 *man/has_build_tools.Rd c00a4ac4cd7833f4e562133337c74ca1 *man/has_compiler.Rd 28b37e7ae90236503c5abf076ce680df *man/has_latex.Rd 483eac2edac3d166291adf95f1fc46e1 *man/has_rtools.Rd d16da465eef5bd0c68c48b643b928bee *man/needs_compile.Rd 0ff57be8e6093cefc2a4fddc9b306dbf *man/pkg_has_src.Rd 7c4b955dfeb79ec0c4020ef049a97bb3 *man/pkg_links_to_rcpp.Rd 6cc4e3826423040966c724227e4f4489 *man/pkgbuild_process.Rd 532f8238d420fbf0fea9dff1efdf85d1 *man/rcmd_build_tools.Rd 7bd78747cfe9c17faf4c2e824bae06fe *man/with_debug.Rd d75062f8d4ff71e0cca79bf5c042be48 *man/without_compiler.Rd d11922ac9eab43de2d4ff0bb06b9465e *tests/build-tools.R 2fb58861d239d4cb77f513d8657104ea *tests/testthat.R 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 c4bd7c279a480670f6ed045720f69e9c *tests/testthat/test-archives.R 3b2fbd3fd4b2a1d84bca24ea96945c75 *tests/testthat/test-build-process.r ee4895dbaad648238db6f220ee47dac9 *tests/testthat/test-build.r d1b687fc83746b673c905f50e955a5d9 *tests/testthat/test-build_tools.R f62ac607e714ae6245ebb61469586880 *tests/testthat/test-c-registration.R 6a367497c505998a782524a57579e3da *tests/testthat/test-compile_dll.R 4536bffa7124f108937eab3ad2ffbaf2 *tests/testthat/test-compiler.R 17f841b5cfbdfdca81e951945ff7da4b *tests/testthat/test-rtools.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 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