-
-
Notifications
You must be signed in to change notification settings - Fork 117
/
Copy pathinstall.R
598 lines (558 loc) · 24 KB
/
install.R
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
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
#' Install/Uninstall TinyTeX
#'
#' The function \code{install_tinytex()} downloads and installs TinyTeX, a
#' custom LaTeX distribution based on TeX Live. The function
#' \code{uninstall_tinytex()} removes TinyTeX; \code{reinstall_tinytex()}
#' reinstalls TinyTeX as well as previously installed LaTeX packages by default;
#' \code{tinytex_root()} returns the root directory of TinyTeX if found.
#' @param force Whether to force to install or uninstall TinyTeX. For
#' \code{install_tinytex()}, \code{force = FALSE} will stop this function from
#' installing TinyTeX if another LaTeX distribution is detected, or the
#' directory specified via the \code{dir} argument exists.
#' @param dir The directory to install (should not exist unless \code{force =
#' TRUE}) or uninstall TinyTeX.
#' @param version The version of TinyTeX, e.g., \code{"2020.09"} (see all
#' available versions at \url{https://github.com/rstudio/tinytex-releases}, or
#' via \code{xfun::github_releases('rstudio/tinytex-releases')}). By default,
#' it installs the latest daily build of TinyTeX. If \code{version =
#' 'latest'}, it installs the latest monthly Github release of TinyTeX.
#' @param bundle The bundle name of TinyTeX (which determines the collection of
#' LaTeX packages to install). See
#' \url{https://github.com/rstudio/tinytex-releases#releases} for all possible
#' bundles and their meanings.
#' @param repository The CTAN repository to set. By default, it is the
#' repository automatically chosen by \code{https://mirror.ctan.org} (which is
#' usually the fastest one to your location). You can find available
#' repositories at \code{https://ctan.org/mirrors}), e.g.,
#' \code{'http://mirrors.tuna.tsinghua.edu.cn/CTAN/'}, or
#' \code{'https://mirror.las.iastate.edu/tex-archive/'}. In theory, this
#' argument should end with the path \file{/systems/texlive/tlnet}, and if it
#' does not, the path will be automatically appended.
#' @param extra_packages A character vector of extra LaTeX packages to be
#' installed. By default, a vector of all currently installed LaTeX packages
#' if an existing installation of TinyTeX is found. If you want a fresh
#' installation, you may use \code{extra_packages = NULL}.
#' @param add_path Whether to run the command \command{tlmgr path add} to add
#' the bin path of TeX Live to the system environment variable \var{PATH}.
#' @references See the TinyTeX documentation (\url{https://yihui.org/tinytex/})
#' for the default installation directories on different platforms.
#' @note If you really want to disable the installation, you may set the
#' environment variable \var{TINYTEX_PREVENT_INSTALL} to \code{true}. Then
#' \code{install_tinytex()} will fail immediately. This can be useful to
#' sysadmins who want to prevent the accidental installation of TinyTeX.
#'
#' Installing TinyTeX requires perl (on Linux, perl-base is insufficient).
#' @export
install_tinytex = function(
force = FALSE, dir = 'auto', version = 'daily', bundle = 'TinyTeX-1', repository = 'auto',
extra_packages = if (is_tinytex()) tl_pkgs(), add_path = TRUE
) {
if (tolower(Sys.getenv('TINYTEX_PREVENT_INSTALL')) == 'true') stop(
"The environment variable 'TINYTEX_PREVENT_INSTALL' was set to 'true', so ",
"the installation is aborted."
)
if (!is.logical(force)) stop('The argument "force" must take a logical value.')
continue_inst = function() {
tolower(substr(readline('Continue the installation anyway? (Y/N) '), 1, 1)) == 'y'
}
# if tlmgr is detected in the system, ask in interactive mode whether to
# continue the installation, and stop in non-interactive() mode
p = which_bin(c('tlmgr', 'pdftex', 'xetex', 'luatex'))
p = p[p != '']
if (!force && length(p)) {
message("Found '", p[1], "', which indicates a LaTeX distribution may have existed in the system.")
if (interactive()) {
if (!continue_inst()) return(invisible(''))
} else stop(
'If you want to force installing TinyTeX anyway, use tinytex::install_tinytex(force = TRUE).'
)
}
force(extra_packages) # evaluate it before TinyTeX is removed or reinstalled next
check_dir = function(dir) {
if (dir_exists(dir) && !force) stop(
'The directory "', dir, '" exists. Please either delete it, ',
'or use tinytex::install_tinytex(force = TRUE).'
)
}
if (missing(dir)) dir = ''
user_dir = ''
if (dir != '') {
dir = gsub('[/\\]+$', '', dir) # remove trailing slashes
check_dir(dir)
dir = xfun::normalize_path(dir)
if (is_windows() && !valid_path(dir)) {
warning(
"The directory path '", dir, "' contains spaces or non-ASCII characters, ",
"and TinyTeX may not work. Please use a path with pure ASCII characters and no spaces.",
immediate. = TRUE
)
if (!force && !(interactive() && continue_inst())) return(invisible(dir))
}
unlink(dir, recursive = TRUE)
user_dir = dir
}
repository = normalize_repo(repository)
not_ctan = repository != 'ctan'
https = grepl('^https://', repository)
if (!grepl('TinyTeX', bundle)) message(
"The bundle name '", bundle, "' has been automatically corrected to '",
bundle <- gsub('tinytex', 'TinyTeX', bundle, ignore.case = TRUE), "'."
)
owd = setwd(tempdir()); on.exit(setwd(owd), add = TRUE)
if ((texinput <- Sys.getenv('TEXINPUT')) != '') message(
'Your environment variable TEXINPUT is "', texinput,
'". Normally you should not set this variable, because it may lead to issues like ',
'https://github.com/rstudio/tinytex/issues/92.'
)
switch(
os,
'unix' = {
check_local_bin()
if (os_index != 3 && !any(dir_exists(c('~/bin', '~/.local/bin')))) on.exit(message(
'You may have to restart your system after installing TinyTeX to make sure ',
'~/bin appears in your PATH variable (https://github.com/rstudio/tinytex/issues/16).'
), add = TRUE)
},
'windows' = {},
stop('Sorry, but tinytex::install_tinytex() does not support this platform: ', os)
)
src_install = getOption('tinytex.source.install', need_source_install())
# if needs to install from source, set `extra_packages` according to `bundle`
if (src_install && missing(extra_packages)) {
extra_packages = switch(
bundle,
'TinyTeX-2' = 'scheme-full',
'TinyTeX' = read_lines('https://yihui.org/gh/tinytex/tools/pkgs-custom.txt'),
'TinyTeX-0' = {
warning("bundle = 'TinyTeX-0' is not supported for your system"); NULL
}
)
}
install = function(...) {
if (src_install) {
install_tinytex_source(repository, ...)
} else {
install_prebuilt(bundle, ..., repo = repository)
}
}
if (version == 'daily') {
version = ''
# test if https://yihui.org or github.com is accessible because the daily
# version is downloaded from there
determine_version = function() {
if (xfun::url_accessible('https://yihui.org')) return('')
if (xfun::url_accessible('https://github.com')) return('daily-github')
warning(
"The daily version of TinyTeX does not appear to be accessible. ",
"Switching to version = 'latest' instead. If you are sure to install ",
"the daily version, call tinytex::install_tinytex(version = 'daily') ",
"(which may fail)."
)
'latest'
}
if (missing(version) && !src_install) version = determine_version()
}
user_dir = install(user_dir, version, add_path, extra_packages)
opts = options(tinytex.tlmgr.path = find_tlmgr(user_dir))
on.exit(options(opts), add = TRUE)
if (not_ctan) {
# install tlgpg for Windows and macOS users if an HTTPS repo is preferred
if (os_index %in% c(1, 3) && https) {
tlmgr(c('--repository', 'http://www.preining.info/tlgpg/', 'install', 'tlgpg'))
}
tlmgr_repo(repository)
if (tlmgr(c('update', '--list')) != 0) {
warning('The repository ', repository, ' does not seem to be accessible. Reverting to the default CTAN mirror.')
tlmgr(c('option', 'repository', 'ctan'))
}
}
invisible(user_dir)
}
# TinyTeX has to be installed from source for OSes that are not Linux or
# non-x86_64 Linux machines
need_source_install = function() {
os_index == 0 || (os_index == 2 && !identical(Sys.info()[['machine']], 'x86_64'))
}
# append /systems/texlive/tlnet to the repo url if necessary
normalize_repo = function(url) {
# don't normalize the url if users passes I(url) or 'ctan' or NULL
if (is.null(url) || url == 'ctan' || inherits(url, 'AsIs')) return(url)
if (url == 'auto') return(auto_repo())
if (url == 'illinois') return('https://ctan.math.illinois.edu/systems/texlive/tlnet')
url = sub('/+$', '', url)
if (!grepl('/tlnet$', url)) {
url2 = paste0(url, '/systems/texlive/tlnet')
# return the amended url if it works
if (xfun::url_accessible(url2)) return(url2)
}
url
}
# get the automatic CTAN mirror returned from mirror.ctan.org
auto_repo = function() {
# curlGetHeaders() may time out, hence tryCatch() here
x = tryCatch(
curlGetHeaders('https://mirror.ctan.org/systems/texlive/tlnet'),
error = function(e) character()
)
x = xfun::grep_sub('^location: ([^[:space:]]+)\\s*$', '\\1', x)
x = tail(x, 1)
if (length(x) == 1) x else 'ctan'
}
# use %APPDATA%/TinyTeX if it exists or doesn't contain spaces or non-ASCII
# chars, otherwise use %ProgramData%, because TeX Live doesn't work when the
# installation path contains non-ASCII chars
win_app_dir = function(s) {
d = Sys.getenv('TINYTEX_DIR')
if (d != '') return(file.path(d, s))
d = Sys.getenv('APPDATA')
if (d != '') {
d2 = file.path(d, s)
if (dir_exists(d2)) {
if (getOption('tinytex.warn.appdata', TRUE) && !xfun::is_ascii(d2)) warning(
"You are recommended to move TinyTeX to another location via\n\n",
" tinytex::copy_tinytex(to = Sys.getenv('ProgramData'), move = TRUE)\n\n",
"otherwise TinyTeX will not work because its current installation path '",
normalizePath(d2), "' contains non-ASCII characters.", call. = FALSE
)
return(d2)
}
if (valid_path(d)) return(d2)
}
d = Sys.getenv('ProgramData')
if (d == '') stop("The environment variable 'ProgramData' is not set.")
file.path(d, s)
}
# test if path is pure ASCII and has no spaces
valid_path = function(x) grepl('^[!-~]+$', x)
# check if /usr/local/bin on macOS is writable
check_local_bin = function() {
if (os_index != 3 || is_writable(p <- '/usr/local/bin')) return()
message(
'The directory ', p, ' is not writable. I recommend that you make it writable. ',
'See https://github.com/rstudio/tinytex/issues/24 for more info.'
)
if (!dir_exists(p)) osascript(paste('mkdir -p', p))
user = system2('whoami', stdout = TRUE)
osascript(sprintf('chown -R %s:admin %s', user, p))
}
osascript = function(cmd) {
if (system(sprintf(
"/usr/bin/osascript -e 'do shell script \"%s\" with administrator privileges'", cmd
)) != 0) warning(
"Please run this command in your Terminal (password required):\n sudo ",
cmd, call. = FALSE
)
}
install_tinytex_source = function(repo = '', dir, version, add_path, extra_packages) {
if (version != '') stop(
'tinytex::install_tinytex() does not support installing a specific version of ',
'TinyTeX for your platform. Please use the argument version = "".'
)
if (repo != 'ctan') {
Sys.setenv(CTAN_REPO = repo)
on.exit(Sys.unsetenv('CTAN_REPO'), add = TRUE)
}
download_file('https://yihui.org/gh/tinytex/tools/install-unx.sh')
res = system2('sh', c(
'install-unx.sh', if (repo != 'ctan') c('--no-admin', '--path', shQuote(repo))
))
if (res != 0) stop('Failed to install TinyTeX', call. = FALSE)
target = normalizePath(default_inst())
if (!dir_exists(target)) stop('Failed to install TinyTeX.')
if (!dir %in% c('', target)) {
dir.create(dirname(dir), showWarnings = FALSE, recursive = TRUE)
dir_rename(target, dir)
target = dir
}
opts = options(tinytex.tlmgr.path = find_tlmgr(target))
on.exit(options(opts), add = TRUE)
post_install_config(add_path, extra_packages, repo)
unlink(c('install-unx.sh', 'install-tl.zip', 'pkgs-custom.txt', 'tinytex.profile'))
target
}
os_index = if (is_windows()) 1 else if (is_linux()) 2 else if (is_macos()) 3 else 0
default_inst = function() switch(
os_index, win_app_dir('TinyTeX'), '~/.TinyTeX', '~/Library/TinyTeX'
)
find_tlmgr = function(dir = default_inst()) {
bin = file.path(list.files(file.path(dir, 'bin'), full.names = TRUE), 'tlmgr')
if (is_windows()) bin = paste0(bin, '.bat')
bin[file_test('-x', bin)][1]
}
#' @rdname install_tinytex
#' @export
uninstall_tinytex = function(force = FALSE, dir = tinytex_root()) {
tweak_path()
if (dir == '') stop('TinyTeX does not seem to be installed.')
if (!is_tinytex() && !force) stop(
'Detected TeX Live at "', dir, '", but it appears to be TeX Live instead of TinyTeX. ',
'To uninstall TeX Live, use the argument force = TRUE.'
)
r_texmf('remove', .quiet = TRUE)
tlmgr_path('remove')
delete_texmf_user()
unlink(dir, recursive = TRUE)
}
# delete user's texmf tree; don't delete ~/.TinyTeX if TinyTeX itself is
# installed there
delete_texmf_user = function() {
r = dir.exists(d <- path.expand('~/.TinyTeX'))
if (!r) return(FALSE)
d1 = xfun::normalize_path(tinytex_root(error = FALSE))
if (d1 == '') return() # not TinyTeX
d2 = xfun::normalize_path(d)
if (substr(d1, 1, nchar(d2)) == d2) return(FALSE)
unlink(d, recursive = TRUE)
r
}
#' @param packages Whether to reinstall all currently installed packages.
#' @param ... Other arguments to be passed to \code{install_tinytex()} (note
#' that the \code{extra_packages} argument will be set to \code{tl_pkgs()} if
#' \code{packages = TRUE}).
#' @rdname install_tinytex
#' @export
reinstall_tinytex = function(packages = TRUE, dir = tinytex_root(), ...) {
pkgs = if (packages) tl_pkgs()
if (length(pkgs)) message(
'If reinstallation fails, try install_tinytex() again. Then ',
'install the following packages:\n\ntinytex::tlmgr_install(c(',
paste('"', pkgs, '"', sep = '', collapse = ', '), '))\n'
)
# in theory, users should not touch the texmf-local dir; if they did, I'll try
# to preserve it during reinstall: https://github.com/rstudio/tinytex/issues/117
if (length(list.files(texmf <- file.path(dir, 'texmf-local'), recursive = TRUE)) > 0) {
dir.create(texmf_tmp <- tempfile(), recursive = TRUE)
message(
'The directory ', texmf, ' is not empty. It will be backed up to ',
texmf_tmp, ' and restored later.\n'
)
file.copy(texmf, texmf_tmp, recursive = TRUE)
on.exit(
file.copy(file.path(texmf_tmp, basename(texmf)), dirname(texmf), recursive = TRUE),
add = TRUE
)
}
uninstall_tinytex()
install_tinytex(extra_packages = pkgs, dir = dir, ...)
}
#' @param error Whether to signal an error if TinyTeX is not found.
#' @rdname install_tinytex
#' @export
tinytex_root = function(error = TRUE) {
path = which_bin('tlmgr')
if (path == '') return('')
root_dir = function(path, ...) {
dir = normalizePath(file.path(dirname(path), ...), mustWork = TRUE)
if (!'bin' %in% list.files(dir)) if (error) stop(
dir, ' does not seem to be the root directory of TeX Live (no "bin/" dir under it)'
) else return('')
dir
}
if (os == 'windows') return(root_dir(path, '..', '..'))
if (Sys.readlink(path) == '') if (error) stop(
'Cannot figure out the root directory of TeX Live from ', path,
' (not a symlink on ', os, ')'
) else return('')
path = symlink_root(path)
root_dir(normalizePath(path), '..', '..', '..')
}
# return paths to TinyTeX's executables even if TinyTeX was not added to PATH
which_bin = function(exec) {
tweak_path()
Sys.which(exec)
}
# trace a symlink to its final destination
symlink_root = function(path) {
path = normalizePath(path, mustWork = TRUE)
path2 = Sys.readlink(path)
if (path2 == '') return(path) # no longer a symlink; must be resolved now
# path2 may still be a _relative_ symlink
in_dir(dirname(path), symlink_root(path2))
}
# a helper function to open tlmgr.pl (on *nix)
open_tlmgr = function() {
file.edit(symlink_root(Sys.which('tlmgr')))
}
#' Check if the LaTeX installation is TinyTeX
#'
#' First find the root directory of the installation via
#' \code{\link{tinytex_root}()}. Then check if the directory name is
#' \code{"tinytex"} (case-insensitive). If not, further check if the first line
#' of the file \file{texmf-dist/web2c/fmtutil.cnf} under the directory contains
#' \code{"TinyTeX"} or \code{".TinyTeX"}. If the binary version of TinyTeX was
#' installed, \file{fmtutil.cnf} should contain a line like \samp{Generated by
#' */TinyTeX/bin/x86_64-darwin/tlmgr on Thu Sep 17 07:13:28 2020}.
#' @return A logical value indicating if the LaTeX installation is TinyTeX.
#' @export
#' @examples tinytex::is_tinytex()
is_tinytex = function() tryCatch({
root = tinytex_root()
root != '' && (
grepl('^[.]?tinytex$', tolower(basename(root))) ||
file.exists(file.path(root, '.tinytex'))
)
}, error = function(e) FALSE)
dir_rename = function(from, to) {
# cannot rename '/foo' to '/bar' because of 'Invalid cross-device link'
suppressWarnings(file.rename(from, to)) || dir_copy(from, to)
}
dir_copy = function(from, to) {
dir.create(to, showWarnings = FALSE, recursive = TRUE)
all(file.copy(list.files(from, full.names = TRUE), to, recursive = TRUE)) &&
unlink(from, recursive = TRUE) == 0
}
# LaTeX packages that I use
install_yihui_pkgs = function() {
pkgs = read_lines('https://yihui.org/gh/tinytex/tools/pkgs-yihui.txt')
tlmgr_install(pkgs)
}
# install a prebuilt version of TinyTeX
install_prebuilt = function(
pkg = '', dir = '', version = '', add_path = TRUE, extra_packages = NULL,
repo = 'ctan', hash = FALSE, cache = NA
) {
if (need_source_install()) stop(
'There is no prebuilt version of TinyTeX for this platform: ',
paste(Sys.info()[c('sysname', 'machine')], collapse = ' '), '.'
)
dir0 = default_inst(); b = basename(dir0)
dir1 = xfun::normalize_path(dir) # expected installation dir
if (dir1 == '') dir1 = dir0
# the archive is extracted to this target dir
target = dirname(dir1)
dir2 = file.path(target, b) # path to (.)TinyTeX/ after extraction
if (xfun::file_ext(pkg) == '') {
if (version == 'latest') {
version = xfun::github_releases('rstudio/tinytex-releases', version)
} else if (version == 'daily-github') {
version = ''
opts = options(tinytex.install.url = 'https://github.com/rstudio/tinytex-releases/releases/download/daily/')
on.exit(options(opts), add = TRUE)
}
version = gsub('^v', '', version)
installer = if (pkg == '') 'TinyTeX' else pkg
# e.g., TinyTeX-0.zip, TinyTeX-1-v2020.10.tar.gz, ...
pkg = paste0(
installer, if (version != '') paste0('-v', version), '.',
c('zip', 'tar.gz', 'tgz')[os_index]
)
# Full scheme is bundled as a self extracting archive on Windows
if (os_index == 1 && installer == 'TinyTeX-2') pkg = xfun::with_ext(pkg, "exe")
if (file.exists(pkg) && is.na(cache)) {
# invalidate cache (if unspecified) when the installer is more than one day old
if (as.numeric(difftime(Sys.time(), file.mtime(pkg), units = 'days')) > 1)
cache = FALSE
}
if (identical(cache, FALSE)) {
file.remove(pkg); on.exit(file.remove(pkg), add = TRUE)
}
if (!file.exists(pkg)) download_installer(pkg, version)
}
pkg = path.expand(pkg)
# installation dir shouldn't be a file but a directory
file.remove(exist_files(c(dir1, dir2)))
if (grepl('[.]exe$', pkg)) {
system2(pkg, args = c('-y', paste0('-o', path.expand(target))))
} else {
extract = if (grepl('[.]zip$', pkg)) unzip else untar
extract(pkg, exdir = path.expand(target))
}
# TinyTeX (or .TinyTeX) is extracted to the parent dir of `dir`; may need to rename
if (dir != '') {
if (basename(dir1) != b) file.rename(dir2, dir1)
opts = options(tinytex.tlmgr.path = find_tlmgr(dir1))
on.exit(options(opts), add = TRUE)
}
post_install_config(add_path, extra_packages, repo, hash)
invisible(dir1)
}
# post-install configurations
post_install_config = function(add_path = TRUE, extra_packages = NULL, repo = 'ctan', hash = FALSE) {
if (os_index == 2) {
if (!dir_exists(bin_dir <- '~/.local/bin')) dir.create(bin_dir <- '~/bin', FALSE, TRUE)
tlmgr(c('option', 'sys_bin', bin_dir))
}
# fix fonts.conf: https://github.com/rstudio/tinytex/issues/313
tlmgr(c('postaction', 'install', 'script', 'xetex'), .quiet = TRUE)
# do not wrap lines in latex log (#322)
tlmgr_conf(c('texmf', 'max_print_line', '10000'), .quiet = TRUE, stdout = FALSE)
if (add_path) tlmgr_path()
r_texmf(.quiet = TRUE)
# don't use the default random ctan mirror when installing on CI servers
if (repo != 'ctan' || tolower(Sys.getenv('CI')) != 'true')
tlmgr_repo(repo, stdout = FALSE, .quiet = TRUE)
tlmgr_install(setdiff(extra_packages, tl_pkgs()))
if (hash) {
texhash(); fmtutil(stdout = FALSE); updmap(); fc_cache()
}
}
download_installer = function(file, version) {
url = if (version != '') sprintf(
'https://github.com/rstudio/tinytex-releases/releases/download/v%s/%s', version, file
) else paste0(getOption('tinytex.install.url', 'https://yihui.org/tinytex/'), file)
download_file(url, file, mode = 'wb')
}
#' Copy TinyTeX to another location and use it in another system
#'
#' The function \code{copy_tinytex()} copies the existing TinyTeX installation
#' to another directory (e.g., a portable device like a USB stick). The function
#' \code{use_tinytex()} runs \command{tlmgr path add} to add the copy of TinyTeX
#' in an existing folder to the \code{PATH} variable of the current system, so
#' that you can use utilities such as \command{tlmgr} and \command{pdflatex},
#' etc.
#' @param from The root directory of the TinyTeX installation. For
#' \code{copy_tinytex()}, the default value \code{tinytex_root()} should be a
#' reasonable guess if you installed TinyTeX via \code{install_tinytex()}. For
#' \code{use_tinytex()}, if \code{from} is not provided, a dialog for choosing
#' the directory interactively will pop up.
#' @param to The destination directory where you want to make a copy of TinyTeX.
#' Like \code{from} in \code{use_tinytex()}, a dialog will pop up if \code{to}
#' is not provided in \code{copy_tinytex()}.
#' @param move Whether to use the new copy and delete the original copy of
#' TinyTeX after copying it.
#' @note You can only copy TinyTeX and use it in the same system, e.g., the
#' Windows version of TinyTeX only works on Windows.
#' @export
copy_tinytex = function(
from = tinytex_root(), to = select_dir('Select Destination Directory'), move = FALSE
) {
op = options(tinytex.warn.appdata = FALSE); on.exit(options(op), add = TRUE)
if (!dir_exists(from)) stop('TinyTeX does not seem to be installed.')
if (length(to) != 1 || !dir_exists(to))
stop("The destination directory '", to, "' does not exist.")
target = file.path(to, basename(from))
if (!move || !{tlmgr_path('remove'); res <- file.rename(from, target)}) {
res = file.copy(from, to, recursive = TRUE)
if (res && move) {
tlmgr_path('remove')
unlink(from, recursive = TRUE)
}
}
if (res && move) use_tinytex(target)
res
}
#' @rdname copy_tinytex
#' @export
use_tinytex = function(from = select_dir('Select TinyTeX Directory')) {
if (length(from) != 1) stop('Please provide a valid path to the TinyTeX directory.')
d = list.files(file.path(from, 'bin'), full.names = TRUE)
d = d[dir_exists(d)]
if (length(d) != 1) stop("The directory '", from, "' does not contain TinyTeX.")
p = file.path(d, 'tlmgr')
if (os == 'windows') p = paste0(p, '.bat')
if (system2(p, c('path', 'add')) != 0) stop(
"Failed to add '", d, "' to your system's environment variable PATH. You may ",
"consider the fallback approach, i.e., set options(tinytex.tlmgr.path = '", p, "')."
)
op = options(tinytex.tlmgr.path = p); on.exit(options(op), add = TRUE)
post_install_config(FALSE)
message('Restart R and your editor and check if tinytex::tinytex_root() points to ', from)
}
select_dir = function(caption = 'Select Directory') {
d = tryCatch(rstudioapi::selectDirectory(caption), error = function(e) {
if (os == 'windows') utils::choose.dir(caption = caption) else {
tcltk::tk_choose.dir(caption = caption)
}
})
if (!is.null(d) && !is.na(d)) d
}