Skip to content

Commit efedd60

Browse files
committed
major internal rework, sdkman
1 parent cbc4a78 commit efedd60

95 files changed

Lines changed: 4377 additions & 640 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

DESCRIPTION

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -49,11 +49,13 @@ Imports:
4949
checkmate,
5050
cli,
5151
curl,
52-
jsonlite,
52+
digest,
5353
memoise,
54+
RcppSimdJson,
5455
rlang,
5556
utils,
56-
withr
57+
withr,
58+
yaml
5759
Suggests:
5860
callr,
5961
quarto,

NAMESPACE

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,9 @@ export(java_check_compatibility)
66
export(java_check_version_cmd)
77
export(java_check_version_rjava)
88
export(java_clear)
9+
export(java_clear_distrib)
10+
export(java_clear_installed)
11+
export(java_clear_project)
912
export(java_download)
1013
export(java_ensure)
1114
export(java_env_set)
@@ -14,6 +17,10 @@ export(java_find_system)
1417
export(java_get_home)
1518
export(java_install)
1619
export(java_list)
20+
export(java_list_available)
21+
export(java_list_distrib)
22+
export(java_list_installed)
23+
export(java_list_project)
1724
export(java_quick_install)
1825
export(java_resolve)
1926
export(java_unpack)

R/global_params.R

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,3 +72,46 @@ rjava_path_locking_note <- function() {
7272
global_use_cache_param <- function(.use_cache) {
7373
# this is just a placeholder for global .use_cache parameter
7474
}
75+
76+
#' @title Global Version Parameter
77+
#'
78+
#' @description
79+
#' Documentation for the `version` parameter, used for specifying Java versions.
80+
#'
81+
#' @param version Java version specification. Accepts:
82+
#' \itemize{
83+
#' \item **Major version** (e.g., `21`, `17`): Downloads the latest release for that major version.
84+
#' \item **Specific version** (e.g., `"21.0.9"`, `"11.0.29"`): Downloads the exact version.
85+
#' \item **SDKMAN identifier** (e.g., `"25.0.1-amzn"`, `"24.0.2-open"`): Uses the SDKMAN
86+
#' backend automatically. When an identifier is detected, the `distribution` and `backend`
87+
#' arguments are **ignored** and derived from the identifier. Find available identifiers
88+
#' in the `identifier` column of \code{\link{java_list_available}(backend = "sdkman")}.
89+
#' }
90+
#' @keywords internal
91+
global_version_param <- function(version) {
92+
# this is just a placeholder for global version parameter
93+
}
94+
95+
#' @title SDKMAN References
96+
#'
97+
#' @description
98+
#' Standard references for SDKMAN attribution.
99+
#'
100+
#' @references
101+
#' SDKMAN! - The Software Development Kit Manager: \url{https://github.com/sdkman}
102+
#' @keywords internal
103+
global_sdkman_references <- function() {
104+
# this is just a placeholder for SDKMAN references
105+
}
106+
107+
#' @title Global Backend Parameter
108+
#'
109+
#' @description
110+
#' Documentation for the `backend` parameter, used for specifying the download source.
111+
#'
112+
#' @param backend Download backend to use. One of "native" (vendor APIs) or "sdkman".
113+
#' Defaults to "native". Can also be set globally via `options(rJavaEnv.backend = "sdkman")`.
114+
#' @keywords internal
115+
global_backend_param <- function(backend) {
116+
# this is just a placeholder for global backend parameter
117+
}

R/internal_utilities.R

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,33 @@ java_urls_load <- function() {
5757
if (json_file == "") {
5858
cli::cli_abort("Configuration file not found")
5959
}
60-
jsonlite::fromJSON(json_file, simplifyVector = FALSE)
60+
RcppSimdJson::fload(json_file, max_simplify_lvl = "list")
61+
}
62+
63+
#' Read JSON from a URL
64+
#'
65+
#' Helper function to read JSON from a URL using RcppSimdJson for fast parsing
66+
#'
67+
#' @param url URL to read JSON from
68+
#' @param max_simplify_lvl Simplification level (default: "data_frame")
69+
#' @return Parsed JSON object
70+
#' @keywords internal
71+
read_json_url <- function(url, max_simplify_lvl = "data_frame") {
72+
content <- rawToChar(curl::curl_fetch_memory(url)$content)
73+
RcppSimdJson::fparse(content, max_simplify_lvl = max_simplify_lvl)
74+
}
75+
76+
#' Read lines from a URL or file
77+
#'
78+
#' Helper function to read lines, mainly for testability.
79+
#'
80+
#' @param path Path or URL
81+
#' @param warn Logical. Whether to warn.
82+
#' @return Character vector of lines
83+
#' @keywords internal
84+
#' @noRd
85+
rje_read_lines <- function(path, warn = FALSE) {
86+
readLines(path, warn = warn)
6187
}
6288

6389
#' Test all Java URLs

R/java_download.R

Lines changed: 99 additions & 100 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,15 @@
11
#' Download a Java distribution
22
#'
3-
#' @param version `Integer` or `character` vector of length 1 for major version of Java to download or install. If not specified, defaults to the latest LTS version. Can be "8", and "11" to "24" (or the same version numbers in `integer`) or any newer version if it is available for the selected distribution. For `macOS` on `aarch64` architecture (Apple Silicon) certain `Java` versions are not available.
4-
#' @param distribution The Java distribution to download. If not specified, defaults to "Amazon Corretto". Currently only \href{https://aws.amazon.com/corretto/}{"Amazon Corretto"} is supported.
3+
#' @inheritParams global_version_param
4+
#' @param distribution The Java distribution to download. One of "Corretto", "Temurin", or "Zulu". Defaults to "Corretto". Ignored if `version` is a SDKMAN identifier.
5+
#' @inheritParams global_backend_param
56
#' @param cache_path The destination directory to download the Java distribution to. Defaults to a user-specific data directory.
67
#' @param platform The platform for which to download the Java distribution. Defaults to the current platform.
78
#' @param arch The architecture for which to download the Java distribution. Defaults to the current architecture.
89
#' @param force A logical. Whether the distribution file should be overwritten or not. Defaults to `FALSE`.
910
#' @param temp_dir A logical. Whether the file should be saved in a temporary directory. Defaults to `FALSE`.
1011
#' @inheritParams global_quiet_param
12+
#' @inheritParams global_sdkman_references
1113
#'
1214
#' @return The path to the downloaded Java distribution file.
1315
#' @export
@@ -20,18 +22,22 @@
2022
#'
2123
#' # download default Java distribution (version 21)
2224
#' java_download(temp_dir = TRUE)
25+
#'
26+
#' # download using SDKMAN backend
27+
#' java_download(version = "21", backend = "sdkman", temp_dir = TRUE)
2328
#' }
2429
java_download <- function(
2530
version = 21,
2631
distribution = "Corretto",
32+
backend = getOption("rJavaEnv.backend", "native"),
2733
cache_path = getOption("rJavaEnv.cache_path"),
2834
platform = platform_detect()$os,
2935
arch = platform_detect()$arch,
3036
quiet = FALSE,
3137
force = FALSE,
3238
temp_dir = FALSE
3339
) {
34-
# override cache_path if temp_dir is set to TRUE
40+
# Override cache_path if temp_dir is set to TRUE
3541
if (temp_dir) {
3642
temp_dir <- tempdir()
3743
setwd(temp_dir)
@@ -41,45 +47,35 @@ java_download <- function(
4147
cache_path <- file.path(temp_dir, "rJavaEnv_cache")
4248
}
4349

44-
# rje_consent_check() # disabling consent check for now
45-
java_urls <- java_urls_load()
46-
47-
valid_distributions <- names(java_urls)
48-
valid_platforms <- names(java_urls[[distribution]])
49-
valid_architectures <- names(java_urls[[distribution]][[platform]])
50-
51-
# Checks for the parameters
52-
checkmate::check_vector(version, len = 1)
50+
# Validate version
5351
version <- as.character(version)
52+
checkmate::check_vector(version, len = 1)
5453

55-
# Optimized validation: check fast list first, only hit network if needed
56-
if (!version %in% java_valid_versions_fast()) {
57-
# Version not in fast/offline list, check if it's a new version online
58-
checkmate::assert_choice(
59-
version,
60-
java_valid_versions(
61-
distribution = distribution,
62-
platform = platform,
63-
arch = arch
54+
# Auto-detect SDKMAN identifier
55+
if (is_sdkman_identifier(version)) {
56+
if (!quiet) {
57+
cli::cli_alert_info(
58+
"Detected SDKMAN identifier {.val {version}}. Using sdkman backend."
6459
)
65-
)
60+
}
61+
backend <- "sdkman"
62+
distribution <- sdkman_vendor_to_distribution(sdkman_vendor_code(version))
63+
if (!quiet) {
64+
cli::cli_alert_info("Distribution: {.val {distribution}}")
65+
}
6666
}
6767

68-
checkmate::assert_choice(distribution, valid_distributions)
69-
70-
# Create the distrib subfolder within the destination directory
71-
cache_path <- file.path(cache_path, "distrib")
72-
if (!dir.exists(cache_path)) {
73-
dir.create(cache_path, recursive = TRUE)
68+
# Validate distribution (only for native backend)
69+
if (backend == "native") {
70+
valid_distributions <- c("Corretto", "Temurin", "Zulu")
71+
checkmate::assert_choice(distribution, valid_distributions)
7472
}
75-
checkmate::assert_directory_exists(cache_path, access = "rw", add = TRUE)
7673

77-
checkmate::assert_choice(platform, valid_platforms)
78-
checkmate::assert_choice(arch, valid_architectures)
79-
checkmate::assert_flag(quiet)
80-
checkmate::assert_flag(force)
74+
# Validate backend
75+
valid_backends <- c("native", "sdkman")
76+
checkmate::assert_choice(backend, valid_backends)
8177

82-
# Print out the detected platform and architecture
78+
# Print detected platform and architecture
8379
if (!quiet) {
8480
cli::cli_inform(c(
8581
"Detected platform: {.strong {platform}}",
@@ -88,91 +84,94 @@ java_download <- function(
8884
))
8985
}
9086

91-
if (!distribution %in% names(java_urls)) {
92-
cli::cli_abort(
93-
"Unsupported distribution: {.val {distribution}}",
94-
.envir = environment()
95-
)
96-
}
97-
98-
if (!platform %in% names(java_urls[[distribution]])) {
99-
cli::cli_abort(
100-
"Unsupported platform: {.val {platform}}",
101-
.envir = environment()
102-
)
103-
}
87+
# Resolve metadata
88+
build <- resolve_java_metadata(version, distribution, platform, arch, backend)
10489

105-
if (!arch %in% names(java_urls[[distribution]][[platform]])) {
106-
cli::cli_abort(
107-
"Unsupported architecture: {.val {arch}}",
108-
.envir = environment()
109-
)
90+
# Prepare destination directory
91+
dest_dir <- file.path(cache_path, "distrib")
92+
if (!dir.exists(dest_dir)) {
93+
dir.create(dest_dir, recursive = TRUE)
11094
}
95+
dest <- file.path(dest_dir, build$filename)
11196

112-
url_template <- java_urls[[distribution]][[platform]][[arch]]
113-
url <- gsub("\\{version\\}", version, url_template)
114-
url_md5 <- gsub("latest/", "latest_checksum/", url)
97+
# Download and verify
98+
result_path <- download_java_with_checksum(build, dest, quiet, force)
11599

116-
dest_file <- file.path(cache_path, basename(url))
117-
dest_file_md5 <- paste0(file.path(cache_path, basename(url_md5)), ".md5")
100+
# Attach metadata attributes for downstream functions
101+
attr(result_path, "distribution") <- distribution
102+
attr(result_path, "backend") <- backend
103+
attr(result_path, "version") <- version
104+
attr(result_path, "platform") <- platform
105+
attr(result_path, "arch") <- arch
118106

119-
if (!quiet) {
120-
cli::cli_inform(
121-
"Downloading Java {version} ({distribution}) for {platform} {arch} to {dest_file}",
122-
.envir = environment()
123-
)
124-
}
107+
result_path
108+
}
125109

126-
if (file.exists(dest_file) & !force) {
110+
#' Download and verify checksum
111+
#'
112+
#' Internal function to download a Java distribution and verify its checksum
113+
#'
114+
#' @param build A java_build object containing download metadata
115+
#' @param dest Destination file path
116+
#' @param quiet Logical, suppress messages
117+
#' @param force Logical, overwrite existing files
118+
#'
119+
#' @return Path to downloaded file
120+
#' @keywords internal
121+
download_java_with_checksum <- function(build, dest, quiet, force) {
122+
# Check if file already exists
123+
if (file.exists(dest) && !force) {
127124
if (!quiet) {
128-
cli::cli_inform(
129-
"File already exists. Skipping download.",
130-
.envir = environment()
131-
)
125+
cli::cli_inform("File already cached: {basename(dest)}")
132126
}
133-
return(dest_file)
127+
return(dest)
134128
}
135129

136-
if (file.exists(dest_file) & force) {
130+
# Remove existing file if force=TRUE
131+
if (file.exists(dest) && force) {
137132
if (!quiet) {
138-
cli::cli_inform("Removing existing installation.", .envir = environment())
133+
cli::cli_inform("Removing existing file...")
139134
}
140-
file.remove(dest_file)
135+
unlink(dest)
141136
}
142137

143-
download_dist_with_md5_check(url, dest_file, url_md5, dest_file_md5, quiet)
144-
145-
return(dest_file)
146-
}
147-
148-
# Helper function to download a distribution and verify its MD5 checksum
149-
download_dist_with_md5_check <- function(
150-
url,
151-
dest_file,
152-
url_md5,
153-
dest_file_md5,
154-
quiet
155-
) {
156-
# Perform downloads
157-
curl::curl_download(url, dest_file, quiet = quiet)
158-
curl::curl_download(url_md5, dest_file_md5, quiet = quiet)
159-
138+
# Download
160139
if (!quiet) {
161-
cli::cli_inform("Download completed.")
140+
cli::cli_inform("Downloading {build$vendor} Java {build$major}...")
162141
}
142+
curl::curl_download(build$download_url, dest, quiet = quiet)
163143

164-
# Verify checksum
165-
md5sum_actual <- tools::md5sum(dest_file)
166-
md5sum_expected <- readLines(dest_file_md5, warn = FALSE)
144+
# Verify checksum if available
145+
if (!is.null(build$checksum) && !is.null(build$checksum_type)) {
146+
if (!quiet) {
147+
cli::cli_inform("Verifying {build$checksum_type} checksum...")
148+
}
167149

168-
if (md5sum_actual != md5sum_expected) {
169-
unlink(dest_file) # Clean up failed download
170-
cli::cli_abort(
171-
"MD5 checksum mismatch. Please try downloading the file again."
150+
actual <- switch(
151+
build$checksum_type,
152+
"md5" = tools::md5sum(dest),
153+
"sha256" = digest::digest(dest, algo = "sha256", file = TRUE),
154+
"sha512" = digest::digest(dest, algo = "sha512", file = TRUE),
155+
NULL
172156
)
173-
}
174157

175-
if (!quiet) {
176-
cli::cli_inform("MD5 checksum verified.")
158+
if (!is.null(actual) && actual != build$checksum) {
159+
unlink(dest)
160+
cli::cli_abort("{build$checksum_type} checksum mismatch! File deleted.")
161+
}
162+
163+
if (!quiet) cli::cli_inform("Checksum verified.")
164+
} else if (build$backend == "sdkman") {
165+
if (!quiet) {
166+
cli::cli_alert_warning("Skipping checksum (unavailable for SDKMAN)")
167+
}
177168
}
169+
170+
dest
171+
}
172+
173+
#' Get extension for platform
174+
#' @keywords internal
175+
get_java_archive_extension <- function(platform) {
176+
if (platform == "windows") "zip" else "tar.gz"
178177
}

0 commit comments

Comments
 (0)