|
| 1 | +# Extract source code from an installed R package using srcref metadata. |
| 2 | +# |
| 3 | +# See also `lobstr::src()`: |
| 4 | +# https://github.com/r-lib/lobstr/blob/85dd18d43e4612f98b05bb93019a7b12fb0bbf6b/R/src.R#L205 |
| 5 | +# |
| 6 | +# Arguments (via commandArgs): |
| 7 | +# 1. package - Package name |
| 8 | +# 2. version - Expected package version |
| 9 | +# |
| 10 | +# Library paths have been set ahead of time via the `R_LIBS` environment |
| 11 | +# variable. |
| 12 | +# |
| 13 | +# On success: prints the concatenated source lines (with `#line` directives) to |
| 14 | +# stdout. |
| 15 | +# |
| 16 | +# On failure: exits with code 1 (package unexpectedly not installed, version |
| 17 | +# mismatch) or 2 (no srcrefs). |
| 18 | + |
| 19 | +args <- commandArgs(trailingOnly = TRUE) |
| 20 | + |
| 21 | +if (length(args) != 2L) { |
| 22 | + message("'package' and 'version' are required arguments.") |
| 23 | + quit(status = 1L) |
| 24 | +} |
| 25 | + |
| 26 | +package <- args[[1L]] |
| 27 | +version <- args[[2L]] |
| 28 | + |
| 29 | +# Make sure package is installed |
| 30 | +path <- find.package(package, quiet = TRUE) |
| 31 | +if (length(path) == 0L) { |
| 32 | + message(paste0("Package '", package, "' is not installed")) |
| 33 | + quit(status = 1L) |
| 34 | +} |
| 35 | + |
| 36 | +# Make sure installed version matches the requested version. |
| 37 | +# Normalize both to `package_version`s to handle versions like sf's 1.1-1. |
| 38 | +version <- as.character(package_version(version)) |
| 39 | +installed_version <- as.character(packageVersion(package)) |
| 40 | +if (installed_version != version) { |
| 41 | + message(paste0( |
| 42 | + "Version mismatch for '", |
| 43 | + package, |
| 44 | + "': ", |
| 45 | + "installed ", |
| 46 | + installed_version, |
| 47 | + ", requested ", |
| 48 | + version |
| 49 | + )) |
| 50 | + quit(status = 1L) |
| 51 | +} |
| 52 | + |
| 53 | +# Get the package namespace and find a function object. |
| 54 | +# Functions will contain the srcrefs if srcrefs have been kept, and are what we |
| 55 | +# can back out the full file structure from. |
| 56 | +ns <- asNamespace(package) |
| 57 | +exports <- getNamespaceExports(ns) |
| 58 | + |
| 59 | +fn <- NULL |
| 60 | +for (name in exports) { |
| 61 | + candidate <- get0(name, envir = ns, mode = "function") |
| 62 | + if (!is.null(candidate)) { |
| 63 | + fn <- candidate |
| 64 | + break |
| 65 | + } |
| 66 | +} |
| 67 | + |
| 68 | +if (is.null(fn)) { |
| 69 | + message(paste0("No functions found for package '", package, "'")) |
| 70 | + quit(status = 2L) |
| 71 | +} |
| 72 | + |
| 73 | +extract_lines <- function(fn) { |
| 74 | + srcref <- attr(fn, "srcref") |
| 75 | + if (is.null(srcref)) { |
| 76 | + return(NULL) |
| 77 | + } |
| 78 | + |
| 79 | + srcfile <- attr(srcref, "srcfile") |
| 80 | + if (is.null(srcfile)) { |
| 81 | + return(NULL) |
| 82 | + } |
| 83 | + |
| 84 | + original <- srcfile$original |
| 85 | + if (is.null(original)) { |
| 86 | + return(NULL) |
| 87 | + } |
| 88 | + |
| 89 | + lines <- original$lines |
| 90 | + if (is.null(lines)) { |
| 91 | + return(NULL) |
| 92 | + } |
| 93 | + |
| 94 | + lines |
| 95 | +} |
| 96 | + |
| 97 | +lines <- extract_lines(fn) |
| 98 | + |
| 99 | +if (is.null(lines)) { |
| 100 | + message(paste0("No srcrefs found for package '", package, "'")) |
| 101 | + quit(status = 2L) |
| 102 | +} |
| 103 | + |
| 104 | +cat(lines, sep = "\n") |
0 commit comments