android_build/java_run.rs
1//! Builder for customizing and invoking a `java` command.
2
3use std::ffi::{OsStr, OsString};
4use std::path::PathBuf;
5use std::process::{Command, ExitStatus};
6use crate::env_paths::{self, PathExt};
7
8/// A builder for a `java` command that can be invoked.
9///
10/// If you need to customize the `java` command beyond what is provided here,
11/// you can use the [`JavaRun::command()`] method to get a [`Command`]
12/// that can be further customized with additional arguments.
13///
14/// Documentation on `java` options are based on
15/// <https://dev.java/learn/jvm/tools/core/java/>.
16#[derive(Clone, Debug, Default)]
17pub struct JavaRun {
18 /// Override the default `JAVA_HOME` path.
19 /// Otherwise, the default path is found using the `JAVA_HOME` env var.
20 java_home: Option<PathBuf>,
21
22 /// Specify where to find user class files and annotation processors.
23 /// If not provided, the current directory will be used.
24 class_paths: Vec<OsString>,
25
26 /// Specify which main class to run.
27 main_class: Option<OsString>,
28
29 /// Specify a JAR file to run instead of a main class.
30 jar_file: Option<OsString>,
31
32 /// Arguments to be passed to the main class being run by `java`.
33 args: Vec<OsString>,
34
35 /// If `true`, enable preview language features.
36 enable_preview_features: bool,
37}
38
39impl JavaRun {
40 /// Creates a new `JavaRun` instance with default values,
41 /// which can be further customized using the builder methods.
42 pub fn new() -> Self {
43 Default::default()
44 }
45
46 /// Executes the `java` command based on this `JavaRun` instance.
47 pub fn run(&self) -> std::io::Result<ExitStatus> {
48 self.command()?.status()
49 }
50
51 /// Returns a [`Command`] based on this `JavaRun` instance
52 /// that can be inspected or customized before being executed.
53 pub fn command(&self) -> std::io::Result<Command> {
54 let jh_clone = self.java_home.clone();
55 let java_home = jh_clone
56 .and_then(PathExt::path_if_exists)
57 .or_else(env_paths::java_home)
58 .ok_or_else(|| std::io::Error::other(
59 "JAVA_HOME not provided, and could not be auto-discovered."
60 ))?;
61
62 let mut cmd = Command::new(java_home.join("bin").join("java"));
63
64 if self.enable_preview_features {
65 cmd.arg("--enable-preview");
66 }
67 if !self.class_paths.is_empty() {
68 cmd.arg("-cp").arg(self.class_paths.join(OsStr::new(";")));
69 }
70 match (self.main_class.as_ref(), self.jar_file.as_ref()) {
71 (Some(main_class), None) => { cmd.arg(main_class); }
72 (None, Some(jar_file)) => { cmd.arg("-jar").arg(jar_file); }
73 (Some(_), Some(_)) => {
74 return Err(std::io::Error::other(
75 "Cannot provide both a main class AND a JAR file."
76 ));
77 },
78 _ => { }
79 }
80
81
82 self.args.iter().for_each(|f| { cmd.arg(f); });
83
84 Ok(cmd)
85 }
86
87 ///////////////////////////////////////////////////////////////////////////
88 //////////////////////// Builder methods below ////////////////////////////
89 ///////////////////////////////////////////////////////////////////////////
90
91 /// Override the default `JAVA_HOME` path.
92 ///
93 /// If not set, the default path is found using the `JAVA_HOME` env var.
94 pub fn java_home<P: AsRef<OsStr>>(&mut self, java_home: P) -> &mut Self {
95 self.java_home = Some(java_home.as_ref().into());
96 self
97 }
98
99 /// Specify where to find user class files.
100 ///
101 /// If no class paths are provided, the current directory will be used.
102 pub fn class_path<S: AsRef<OsStr>>(&mut self, class_path: S) -> &mut Self {
103 self.class_paths.push(class_path.as_ref().into());
104 self
105 }
106
107 /// Enable or disable preview language features.
108 pub fn enable_preview_features(&mut self, enable_preview_features: bool) -> &mut Self {
109 self.enable_preview_features = enable_preview_features;
110 self
111 }
112
113 /// Specify the main class to launch when running the `java` command.
114 ///
115 /// Note that this and the `jar_file` are mutually exclusive;
116 /// only one can be chosen at a time.
117 pub fn main_class<S: AsRef<OsStr>>(&mut self, class: S) -> &mut Self {
118 self.main_class = Some(class.as_ref().into());
119 self
120 }
121
122 /// Specify the JAR file to run with the `java` command.
123 ///
124 /// Note that this and the `main_class` are mutually exclusive;
125 /// only one can be chosen at a time.
126 pub fn jar_file<P: AsRef<OsStr>>(&mut self, jar_file: P) -> &mut Self {
127 self.jar_file = Some(jar_file.as_ref().into());
128 self
129 }
130
131 /// Add an argument to be passed to the main class being run by `java`.
132 pub fn arg<S: AsRef<OsStr>>(&mut self, arg: S) -> &mut Self {
133 self.args.push(arg.as_ref().into());
134 self
135 }
136
137 /// Adds multiple arguments to be passed to the main class being run by `java`.
138 pub fn args<I>(&mut self, args: I) -> &mut Self
139 where
140 I: IntoIterator,
141 I::Item: AsRef<OsStr>,
142 {
143 self.args.extend(args.into_iter().map(|a| a.as_ref().into()));
144 self
145 }
146}