xtask/commands/
r_cmd_check.rs
1use std::error::Error;
2use std::path::{Path, PathBuf};
3
4use xshell::Shell;
5
6use crate::extendrtests::path_helper::RCompatiblePath;
7use crate::extendrtests::with_absolute_path::{swap_extendr_api_path, R_FOLDER_PATH};
8
9#[derive(Debug, Clone, Copy, Eq, PartialEq)]
10pub(crate) enum RCmdCheckErrorOn {
11 Never,
12 Note,
13 Warning,
14 Error,
15}
16
17impl RCmdCheckErrorOn {
18 fn get_error_on(&self) -> &'static str {
19 match self {
20 RCmdCheckErrorOn::Never => "'never'",
21 RCmdCheckErrorOn::Note => "'note'",
22 RCmdCheckErrorOn::Warning => "'warning'",
23 RCmdCheckErrorOn::Error => "'error'",
24 }
25 }
26}
27
28pub(crate) fn run<P: AsRef<Path>>(
29 shell: &Shell,
30 no_build_vignettes: bool,
31 error_on: RCmdCheckErrorOn,
32 check_dir: Option<String>,
33 initial_path: P,
34) -> Result<(), Box<dyn Error>> {
35 let check_dir = match check_dir {
36 Some(cd) => Some(construct_check_dir_path(cd, initial_path)?),
37 _ => None,
38 };
39
40 let _document_handle = swap_extendr_api_path(shell)?;
41
42 run_r_cmd_check(shell, no_build_vignettes, error_on, check_dir)
43}
44
45fn construct_check_dir_path<S: AsRef<str>, P: AsRef<Path>>(
46 check_dir: S,
47 initial_path: P,
48) -> Result<String, Box<dyn Error>> {
49 let mut path = PathBuf::from(check_dir.as_ref());
50 if !path.is_absolute() {
51 let str_rep = path.to_string_lossy();
52 if str_rep.starts_with("./") {
53 path = PathBuf::from(str_rep.trim_start_matches("./"));
54 } else if str_rep.starts_with(r".\\") {
55 path = PathBuf::from(str_rep.trim_start_matches(r".\\"));
56 }
57 path = initial_path.as_ref().canonicalize()?.join(path);
58 }
59 Ok(path.adjust_for_r())
60}
61
62#[derive(Debug)]
63enum RCmdCheckError {
64 MissingRPackages,
65}
66impl std::fmt::Display for RCmdCheckError {
67 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
68 match self {
69 RCmdCheckError::MissingRPackages => {
70 write!(f, "Missing required R-packages, please install them.")
71 }
72 }
73 }
74}
75impl Error for RCmdCheckError {}
76
77fn run_r_cmd_check(
78 shell: &Shell,
79 no_build_vignettes: bool,
80 error_on: RCmdCheckErrorOn,
81 check_dir: Option<String>,
82) -> Result<(), Box<dyn Error>> {
83 let _r_path = shell.push_dir(R_FOLDER_PATH);
84 let mut args = vec!["'--as-cran'", "'--no-manual'"];
85 if no_build_vignettes {
86 args.push("'--no-build-vignettes'");
87 }
88
89 let args = format!("c({0})", args.join(", "));
90
91 let error_on = error_on.get_error_on();
92
93 let check_dir = match check_dir {
94 Some(cd) => format!("'{}'", cd),
95 _ => "NULL".to_string(),
96 };
97
98 let has_prerequisites = shell
99 .cmd("Rscript")
100 .args([
101 "-e",
102 r#"requireNamespace("devtools");
103 requireNamespace("rcmdcheck");
104 requireNamespace("patrick");
105 requireNamespace("lobstr");
106 requireNamespace("rextendr")"#,
107 ])
108 .run()
109 .is_ok();
110
111 if !has_prerequisites {
112 println!(
113 r#"R installation is missing necessary packages.
114RScript -e 'options(repos = list(CRAN="http://cran.rstudio.com/"))'
115 -e 'install.packages("devtools")'
116 -e 'install.packages("rcmdcheck")'
117 -e 'install.packages("patrick")'
118 -e 'install.packages("lobstr")'
119 -e 'install.packages("rextendr")'
120
121Alternatively, install development version on rextendr
122Rscript -e 'options(repos = list(CRAN="http://cran.rstudio.com/"))'
123 -e 'remotes::install_github("extendr/rextendr")'"#
124 );
125 return Err(RCmdCheckError::MissingRPackages.into());
126 }
127
128 shell
129 .cmd("Rscript")
130 .arg("-e")
131 .arg(format!(
132 "rcmdcheck::rcmdcheck(args = {args}, error_on = {error_on}, check_dir = {check_dir})"
133 ))
134 .run()?;
135
136 Ok(())
137}