-
Notifications
You must be signed in to change notification settings - Fork 46
Expand file tree
/
Copy pathzzz-pkgdepends-config.R
More file actions
528 lines (479 loc) · 19.6 KB
/
Copy pathzzz-pkgdepends-config.R
File metadata and controls
528 lines (479 loc) · 19.6 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
windows_archs <- function() c("prefer-x64", "both")
default_windows_archs <- function() {
if (getRversion() < "4.2.0") "both" else "prefer-x64"
}
default_update_after <- function() as.difftime(24, units = "hours")
env_decode_dependencies <- function(x, name, ...) {
if (tolower(x) %in% c("yes", "true", "1", "on")) {
return(TRUE)
}
if (tolower(x) %in% c("no", "false", "0", "off")) {
return(FALSE)
}
if (tolower(x) == "na") {
return(NA)
}
strsplit(x, ";", fixed = TRUE)[[1]]
}
is_configure_opts <- function(x) {
is.null(x) ||
(is.character(x) && !anyNA(x)) ||
(is.list(x) &&
all(vapply(
x,
function(e) is.character(e) && !anyNA(e),
logical(1)
)))
}
env_decode_configure_opts <- function(x, name, ...) {
if (!nzchar(x)) {
return(character())
}
parts <- strsplit(x, ";", fixed = TRUE)[[1]]
# Named form: every `;`-separated part looks like `<pkgname>=<value>`.
named <- regexpr("^[a-zA-Z][a-zA-Z0-9._]*=", parts) == 1L
if (length(parts) && all(named)) {
nms <- sub("=.*$", "", parts)
vals <- sub("^[^=]*=", "", parts)
stats::setNames(vals, nms)
} else {
x
}
}
configure_flag <- function(val, pkg, flag) {
if (length(val) == 0) {
return(character())
}
nms <- names(val)
if (is.null(nms) || all(!nzchar(nms))) {
# unnamed: applies to every package built from source
vals <- unlist(val, use.names = FALSE)
} else if (pkg %in% nms) {
vals <- val[[pkg]]
} else {
return(character())
}
vals <- vals[nzchar(vals)]
if (length(vals) == 0) {
return(character())
}
paste0(flag, "=", shQuote(paste(vals, collapse = " ")))
}
env_decode_difftime <- function(x, name, ...) {
if (nchar(x) >= 2) {
unit <- substr(x, nchar(x), nchar(x))
unit <- c(s = "secs", m = "mins", h = "hours", d = "days")[unit]
qty <- suppressWarnings(as.numeric(substr(x, 1, nchar(x) - 1)))
if (!is.na(unit) && !is.na(qty)) {
return(as.difftime(qty, units = unit))
}
}
throw(pkg_error(
"Invalid time interval specification in {.envvar {name}} environment
variable: {.str {x}}",
i = "It must have the form {.code <number><unit>}.",
i = "The unit must be a single letter: {.code s} (seconds),
{.code m} (minutes), {.code h} (hours) or {.code d} (days).",
i = "Examples: {.emph 60s}, {.emph 2h}, {.emph 1d}."
))
}
default_library <- function(config) {
lp <- .libPaths()
if (config$get("ignore_dev_library")) {
lp <- lp[basename(lp) != "__dev_lib__"]
}
lp[1L]
}
default_sysreqs_platform <- function() {
pkgcache::current_r_platform()
}
default_sysreqs <- function(config) {
plt <- config$get("sysreqs_platform")
sysreqs_is_supported(plt) &&
(is_root() || can_sudo_without_pw())
}
default_sysreqs_sudo <- function() {
if (os_type() != "unix") {
FALSE
} else {
euid <- get_euid()
is.na(euid) || euid != 0
}
}
default_sysreqs_update <- function() {
Sys.getenv("CI") == "true"
}
default_sysreqs_verbose <- function() {
Sys.getenv("CI") != ""
}
default_sysreqs_rspm_url <- function() {
Sys.getenv("RSPM_ROOT", "https://packagemanager.posit.co")
}
default_sysreqs_rspm_repo_id <- function() {
Sys.getenv("RSPM_REPO_ID", "1")
}
pkgdepends_config <- sort_by_name(list(
# -----------------------------------------------------------------------
build_vignettes = list(
type = "flag",
default = FALSE,
docs = "Whether to build vignettes for package trees.
This is only used if the package is obtained from a package tree,
and not from a source (or binary) package archive. By default
vignettes are not built in this case. If you set this to `TRUE`,
then you need to make sure that the vignette builder packages are
available, as these are not installed by default currently."
),
# -----------------------------------------------------------------------
cache_dir = list(
type = "string",
default = function() detect_download_cache_dir(),
docs = "Directory to download the packages to. Defaults to a temporary
directory within the R session temporary directory, see
[base::tempdir()]."
),
# -----------------------------------------------------------------------
configure_args = list(
type = "configure_opts",
default = function() getOption("configure.args") %||% character(),
docs = "Extra `--configure-args` to pass to `R CMD INSTALL` when
building packages from source. It can be a single string, which is
then used for all packages built from source. It can also be a named
character vector (or a named list of character vectors) to set
configure arguments for individual packages: the names are package
names and the values are the corresponding configure arguments.
Defaults to the `configure.args` option (see [base::options()]), for
compatibility with [utils::install.packages()]. The
`PKG_CONFIGURE_ARGS` environment variable may be either a single
string (used for all packages), or a semicolon separated list of
`<package>=<args>` entries to set arguments for individual packages."
),
# -----------------------------------------------------------------------
configure_vars = list(
type = "configure_opts",
default = function() getOption("configure.vars") %||% character(),
docs = "Extra `--configure-vars` to pass to `R CMD INSTALL` when
building packages from source. It can be a single string, which is
then used for all packages built from source. It can also be a named
character vector (or a named list of character vectors) to set
configure variables for individual packages: the names are package
names and the values are the corresponding configure variables.
Defaults to the `configure.vars` option (see [base::options()]), for
compatibility with [utils::install.packages()]. The
`PKG_CONFIGURE_VARS` environment variable may be either a single
string (used for all packages), or a semicolon separated list of
`<package>=<vars>` entries to set variables for individual packages."
),
# -----------------------------------------------------------------------
cran_mirror = list(
type = "string",
default = function() default_cran_mirror(),
docs = "CRAN mirror to use. Defaults to the `repos` option
(see [base::options()]), if that's not set then
`https://cran.rstudio.com`.",
docs_pak = "CRAN mirror to use. Defaults to the `repos` option
(see [base::options()]), if that's not set then
`https://cran.rstudio.com`. See also [pak::repo_add()] and
[pak::repo_get()]"
),
# -----------------------------------------------------------------------
dependencies = list(
type = "dependencies",
default = function() pkg_dep_types_hard(),
# pak functions take this as an argument, so we do not need it, for now
pak = FALSE,
docs = "Dependencies to consider or download or install.
Defaults to the hard dependencies, see
[pkgdepends::pkg_dep_types_hard()]. The following values are
supported in the `PKG_DEPENDENCIES` environment variable:
`\"TRUE\"`, `\"FALSE\"`, `\"NA\"`, or a semicolon separated list of
dependency types. See [pkgdepends::as_pkg_dependencies()] for
details."
),
# -----------------------------------------------------------------------
git_submodules = list(
type = "flag",
default = FALSE,
docs = "Whether or not to update submodules in git repositories. This
affects `git::` and `gitlab::` package sources only.
If the R package is in a subdirectory then only the submodules
within that directory are updated. If a submodule appears in
`.Rbuildignore`, then it is skipped."
),
# -----------------------------------------------------------------------
http_retry = list(
forwarded = TRUE,
option = "pkg_http_retry",
envvar = "PKG_HTTP_RETRY",
docs = "Whether to retry failed HTTP requests. It can be `TRUE` to retry
with the default settings, or `FALSE` to never retry. Defaults to
`TRUE`. This entry is handled by pkgcache, which performs all HTTP
requests."
),
# -----------------------------------------------------------------------
ignore_dev_library = list(
type = "flag",
default = TRUE,
docs = "Whether to ignore library directories called `__dev_lib__`."
),
# -----------------------------------------------------------------------
include_linkingto = list(
type = "flag",
default = FALSE,
docs = "Whether to always include `LinkingTo` dependencies in the solution
of and installation, even if they are not needed because the packages
are installed from binaries. This is sometimes useful, see e.g.
<https://github.com/r-lib/pak/issues/485> for an example use case.
It is also needed if a repository (Posit Package Manager typically)
might serve a source package instead of a binary, so that the source
package can be built, see
<https://github.com/r-lib/pak/issues/891>."
),
# -----------------------------------------------------------------------
library = list(
type = "character_or_null",
default = function(config) default_library(config),
docs = "Package library to install packages to. It is also used for
already installed packages when considering dependencies in
[dependency lookup][pkg_deps] or
[package installation][pkg_installation_proposal]. Defaults to the
first path in [.libPaths()].",
docs_pak = "Package library to install packages to. It is also used for
already installed packages when considering dependencies."
),
# -----------------------------------------------------------------------
metadata_cache_dir = list(
type = "string",
default = function() tempfile(),
# TODO: do not link to pkgcache docs from pak
docs = "Location of metadata replica of
[`pkgcache::cranlike_metadata_cache`]. Defaults to a temporary
directory within the R session temporary directory, see
[base::tempdir()]."
),
# -----------------------------------------------------------------------
metadata_update_after = list(
type = "difftime",
default = function() default_update_after(),
docs = "A time interval as a [difftime] object. pkgdepends will update the
metadata cache if it is older than this. The default is one day.
The `PKG_METADATA_UPDATE_AFTER` environment variable may be set
in seconds (`s` suffix), minutes (`m` suffix), hours (`h` suffix),
or days (`d` suffix). E.g: `1d` means one day.",
docs_pak = "A time interval as a [difftime] object. pak will update the
metadata cache if it is older than this. The default is one day.
The `PKG_METADATA_UPDATE_AFTER` environment variable may be set
in seconds (`s` suffix), minutes (`m` suffix), hours (`h` suffix),
or days (`d` suffix). E.g: `1d` means one day."
),
# -----------------------------------------------------------------------
package_cache_dir = list(
type = "string",
docs = "Package cache location of [`pkgcache::package_cache`]. The default
is the pkgcache default.",
docs_pak = "Location of the package cache on the disk. See
[pak::cache_summary()]. Default is selected by pkgcache."
),
# -----------------------------------------------------------------------
platforms = list(
type = "character",
default = function() default_platforms(),
# TODO: do not link to pkgdepends docs from pak
docs = "Character vector of platforms to _download_ or _install_ packages
for. See [pkgdepends::default_platforms()] for possible platform
names. Defaults to the platform of the current R session, plus
`\"source\"`."
),
# -----------------------------------------------------------------------
r_versions = list(
type = "character",
default = function() current_r_version(),
check = is_r_version_list,
docs = "Character vector, R versions to download or install
packages for. It defaults to the current R version."
),
# -----------------------------------------------------------------------
sysreqs = list(
type = "flag",
default = function(config) default_sysreqs(config),
docs = "Whether to automatically look up and install system requirements.
If `TRUE`, then `r pak_or_pkgdepends()` will try to install required
system packages. If `FALSE`, then system requirements are still
printed (including OS packages on supported platforms), but they
are not installed.
By default it is `TRUE` on supported platforms,
if the current user is the root user or password-less `sudo` is
configured for the current user."
),
# -----------------------------------------------------------------------
sysreqs_db_update = list(
type = "flag",
default = TRUE,
docs = "Whether to try to update the system requirements database from
GitHub. If the update fails, then the cached or the build-in
database if used. Defaults to TRUE."
),
# -----------------------------------------------------------------------
sysreqs_db_update_timeout = list(
type = "difftime",
default = function() {
as.difftime(if (Sys.getenv("CI") == "") 5 else 60, units = "secs")
},
docs = "Timeout for the system requirements database update.
Defaults to five seconds, except if the `CI` environment variable
is set, then it is one minute."
),
# -----------------------------------------------------------------------
sysreqs_dry_run = list(
type = "flag",
default = FALSE,
docs = "If `TRUE`, then pkgdepends only prints the system commands to
install system requirements, but does not execute them.",
docs_pak = "If `TRUE`, then pak only prints the system commands to
install system requirements, but does not execute them."
),
# -----------------------------------------------------------------------
sysreqs_platform = list(
type = "string",
default = function() default_sysreqs_platform(),
docs = "The platform to use for system requirements lookup. On Linux, where
system requirements are currently supported, it must be a string
containing the distribution name and release, separated by a dash.
E.g.: `\"ubuntu-22.04\"`, or `\"rhel-9\"`."
),
# -----------------------------------------------------------------------
sysreqs_rspm_repo_id = list(
type = "string",
default = function() default_sysreqs_rspm_repo_id(),
docs = "Posit Package Manager (formerly RStudio Package Manager) repository
id to use for CRAN system requirements lookup. Defaults to the
`RSPM_REPO_ID` environment variable, if set. If not set, then it
defaults to `1`."
),
# -----------------------------------------------------------------------
sysreqs_rspm_url = list(
type = "string",
default = function() default_sysreqs_rspm_url(),
docs = "Root URL of Posit Package Manager (formerly RStudio Package
Manager) for system requirements lookup. By default the `RSPM_ROOT`
environment variable is used, if set. If not set,
it defaults to `https://packagemanager.posit.co`."
),
# -----------------------------------------------------------------------
sysreqs_sudo = list(
type = "flag",
default = function() default_sysreqs_sudo(),
docs = "Whether to use `sudo` to install system requirements,
on Unix. By default it is `TRUE` on Linux if the effective user id
of the current process is not the `root` user."
),
# -----------------------------------------------------------------------
sysreqs_update = list(
type = "flag",
default = function() default_sysreqs_update(),
docs = "Whether to try to update system packages that are already installed.
It defaults to `TRUE` on CI systems: if the `CI` environment
variable is set to `true`."
),
# -----------------------------------------------------------------------
sysreqs_verbose = list(
type = "flag",
default = function() default_sysreqs_verbose(),
docs = "Whether to echo the output of system requirements installation.
Defaults to `TRUE` if the `CI` environment variable is set."
),
# -----------------------------------------------------------------------
use_bioconductor = list(
type = "flag",
default = TRUE,
docs = "Whether to automatically use the Bioconductor repositories.
Defaults to `TRUE`."
),
# -----------------------------------------------------------------------
windows_archs = list(
type = "string",
default = function() default_windows_archs(),
# we can't indent this "correctly" because markdown will take it as code
docs = "Character scalar specifying which architectures
to download/install for on Windows. Its possible values are:
- `\"prefer-x64\"`: Generally prefer x64 binaries. If the current R
session is `x64`, then we download/install x64 packages.
(These packages might still be multi-architecture binaries!)
If the current R session is `i386`, then we download/install
packages for both architectures. This might mean compiling
packages from source if the binary packages are for `x64` only,
like the CRAN Windows binaries for R 4.2.x currently.
`\"prefer-x64\"` is the default for R 4.2.0 and later.
- `\"both\"`: Always download/install packages for both `i386` and
`x64` architectures. This might need compilation from source
if the available binaries are for `x64` only, like the CRAN
Windows binaries for R 4.2.x currently. `\"both\"` is the default
for R 4.2.0 and earlier."
),
# -----------------------------------------------------------------------
# Internal
goal = list(
type = "string",
default = "unknown"
),
sysreqs_lookup_system = list(
type = "flag",
default = TRUE
)
))
#' pkgdepends configuration
#' @name pkg_config
#' @aliases pkgdepends-config pkgdepends_config
#'
#' @description
#' Configuration entries for several pkgdepends classes.
#'
#' @export
#' @details
#' pkgdepends configuration is set from several source. They are, in the
#' order of preference:
#' * Function arguments, e.g. the `config` argument of
#' [new_pkg_installation_proposal()].
#' * Global options, set via [options()]. The name of the global option
#' is the `pkg.` prefix plus the name of the pkgdepends configuration
#' entry. E.g. `pkg.platforms`.
#' * Environment variables. The name of the environment variable is the
#' `PKG_` prefix, plus the name of the pkgdepends configuration entry, in
#' uppercase. E.g. `PKG_PLATFORMS`.
#' * Default values.
#'
#' Not all classes use all entries. E.g. a [`pkg_download_proposal`] is not
#' concerned about package libraries, so it'll ignore the `library`
#' configuration entry.
#'
#' Call `current_config()` to print the current configuration.
#'
#' # Configuration entries
#'
#' `r generate_config_docs()`
current_config <- function() {
conf <- config$new("pkg")
conf$add_type("dependencies", is_dependencies, env_decode_dependencies)
conf$add_type("difftime", is_difftime, env_decode_difftime)
conf$add_type(
"configure_opts",
is_configure_opts,
env_decode_configure_opts
)
map_named(pkgdepends_config, function(name, entry) {
if (isTRUE(entry$forwarded)) {
conf$add_forwarded(name, option = entry$option, envvar = entry$envvar)
} else {
type <- entry$type %||% "character"
conf$add(
name,
type,
default = entry$default,
check = entry$check %||% type,
env_decode = entry$env_decode %||% type
)
}
})
conf$lock()
conf
}