1use std::path::PathBuf;
4use std::ffi::{OsStr, OsString};
5use std::process::{Command, ExitStatus};
6use crate::env_paths::{self, PathExt};
7
8#[derive(Clone, Debug, Default)]
17pub struct JavaBuild {
18 java_home: Option<PathBuf>,
21 debug_info: Option<DebugInfo>,
23 nowarn: bool,
25 verbose: bool,
27 warnings_as_errors: bool,
29 deprecation: bool,
33 enable_preview_features: bool,
35 class_paths: Vec<OsString>,
38 source_paths: Vec<OsString>,
41 boot_class_paths: Vec<OsString>,
43 extension_dirs: Vec<OsString>,
45 endorsed_dirs: Vec<OsString>,
47 annotation_processors: Vec<OsString>,
50 annotation_processor_paths: Vec<OsString>,
53 method_paramater_metadata: bool,
56 #[doc(alias = "-d")]
60 classes_out_dir: Option<OsString>,
61 #[doc(alias = "-s")]
63 sources_out_dir: Option<OsString>,
64 #[doc(alias = "-h")]
66 headers_out_dir: Option<OsString>,
67 #[doc(alias = "-A")]
69 annotation_parameters: Vec<(String, String)>,
70 files: Vec<OsString>,
72}
73
74#[derive(Clone, Debug)]
80pub struct DebugInfo {
81 pub line_numbers: bool,
82 pub variables: bool,
83 pub source_files: bool,
84}
85impl Default for DebugInfo {
86 fn default() -> Self {
87 Self {
88 line_numbers: true,
89 variables: true,
90 source_files: true,
91 }
92 }
93}
94impl DebugInfo {
95 fn add_as_args_to<'c>(&self, cmd: &'c mut Command) -> &'c mut Command {
96 if self.line_numbers {
97 cmd.arg("-g:lines");
98 }
99 if self.variables {
100 cmd.arg("-g:vars");
101 }
102 if self.source_files {
103 cmd.arg("-g:source");
104 }
105 if !self.line_numbers && !self.variables && !self.source_files {
106 cmd.arg("-g:none");
107 }
108 cmd
109 }
110}
111
112impl JavaBuild {
113 pub fn new() -> Self {
116 Default::default()
117 }
118
119 pub fn compile(&self) -> std::io::Result<ExitStatus> {
121 self.command()?.status()
122 }
123
124 pub fn command(&self) -> std::io::Result<Command> {
127 let jh_clone = self.java_home.clone();
128 let java_home = jh_clone
129 .and_then(PathExt::path_if_exists)
130 .or_else(env_paths::java_home)
131 .ok_or_else(|| std::io::Error::other(
132 "JAVA_HOME not provided, and could not be auto-discovered."
133 ))?;
134
135 let mut cmd = Command::new(java_home.join("bin").join("javac"));
136 if let Some(d) = self.debug_info.as_ref() {
137 d.add_as_args_to(&mut cmd);
138 }
139
140 self.class_paths .iter().for_each(|p| { cmd.arg("-cp").arg(p); });
141 self.source_paths .iter().for_each(|p| { cmd.arg("-sourcepath").arg(p); });
142 self.boot_class_paths.iter().for_each(|p| { cmd.arg("-bootclasspath").arg(p); });
143 self.extension_dirs .iter().for_each(|p| { cmd.arg("-extdirs").arg(p); });
144
145 let processors = self.annotation_processors.join(OsStr::new(","));
146 if processors.len() != 0 {
147 cmd.arg("-processor").arg(processors);
148 }
149
150 self.annotation_processor_paths.iter()
151 .for_each(|p| { cmd.arg("-processorpath").arg(p); });
152
153 for (flag, dir) in [
154 ("-d", self.classes_out_dir.as_ref()),
155 ("-s", self.sources_out_dir.as_ref()),
156 ("-h", self.headers_out_dir.as_ref()),
157 ].iter() {
158 if let Some(dir) = dir {
159 cmd.arg(flag).arg(dir);
160 }
161 }
162
163 for (flag, cond) in [
164 ("-nowarn", self.nowarn),
165 ("-verbose", self.verbose),
166 ("-deprecation", self.deprecation),
167 ("-parameters", self.method_paramater_metadata),
168 ("-Werror", self.warnings_as_errors),
169 ("--enable-preview", self.enable_preview_features)
170 ].into_iter() {
171 if cond { cmd.arg(flag); }
172 }
173
174 self.annotation_parameters.iter()
175 .for_each(|(k,v)| { cmd.arg(format!("-A{}={}", k, v)); });
176 self.files.iter().for_each(|f| { cmd.arg(f); });
177
178 Ok(cmd)
179 }
180
181 pub fn java_home<P: Into<PathBuf>>(&mut self, java_home: P) -> &mut Self {
189 self.java_home = Some(java_home.into());
190 self
191 }
192
193 #[doc(alias("-g"))]
195 pub fn debug_info(&mut self, debug_info: DebugInfo) -> &mut Self {
196 self.debug_info = Some(debug_info);
197 self
198 }
199
200 pub fn nowarn(&mut self, nowarn: bool) -> &mut Self {
202 self.nowarn = nowarn;
203 self
204 }
205
206 pub fn verbose(&mut self, verbose: bool) -> &mut Self {
208 self.verbose = verbose;
209 self
210 }
211
212 pub fn deprecation(&mut self, deprecation: bool) -> &mut Self {
218 self.deprecation = deprecation;
219 self
220 }
221
222 pub fn enable_preview_features(&mut self, enable_preview_features: bool) -> &mut Self {
224 self.enable_preview_features = enable_preview_features;
225 self
226 }
227
228 pub fn class_path<P: AsRef<OsStr>>(&mut self, class_path: P) -> &mut Self {
232 self.class_paths.push(class_path.as_ref().into());
233 self
234 }
235
236 pub fn source_path<P: AsRef<OsStr>>(&mut self, source_path: P) -> &mut Self {
240 self.source_paths.push(source_path.as_ref().into());
241 self
242 }
243
244 pub fn boot_class_path<P: AsRef<OsStr>>(&mut self, boot_class_path: P) -> &mut Self {
248 self.boot_class_paths.push(boot_class_path.as_ref().into());
249 self
250 }
251
252 pub fn extension_dir<P: AsRef<OsStr>>(&mut self, extension_dir: P) -> &mut Self {
256 self.extension_dirs.push(extension_dir.as_ref().into());
257 self
258 }
259
260 pub fn endorsed_dir<P: AsRef<OsStr>>(&mut self, endorsed_dir: P) -> &mut Self {
264 self.endorsed_dirs.push(endorsed_dir.as_ref().into());
265 self
266 }
267
268 pub fn annotation_processor<S: AsRef<OsStr>>(&mut self, annotation_processor: S) -> &mut Self {
272 self.annotation_processors.push(annotation_processor.as_ref().into());
273 self
274 }
275
276 pub fn annotation_processor_path<P: AsRef<OsStr>>(&mut self, annotation_processor_path: P) -> &mut Self {
280 self.annotation_processor_paths.push(annotation_processor_path.as_ref().into());
281 self
282 }
283
284 pub fn method_paramater_metadata(&mut self, method_paramater_metadata: bool) -> &mut Self {
287 self.method_paramater_metadata = method_paramater_metadata;
288 self
289 }
290
291 #[doc(alias("-d"))]
296 pub fn classes_out_dir<P: AsRef<OsStr>>(&mut self, classes_out_dir: P) -> &mut Self {
297 self.classes_out_dir = Some(classes_out_dir.as_ref().into());
298 self
299 }
300
301 #[doc(alias("-s"))]
303 pub fn sources_out_dir<P: AsRef<OsStr>>(&mut self, sources_out_dir: P) -> &mut Self {
304 self.sources_out_dir = Some(sources_out_dir.as_ref().into());
305 self
306 }
307
308 #[doc(alias("-h"))]
310 pub fn headers_out_dir<P: AsRef<OsStr>>(&mut self, headers_out_dir: P) -> &mut Self {
311 self.headers_out_dir = Some(headers_out_dir.as_ref().into());
312 self
313 }
314
315 #[doc(alias("-A"))]
317 pub fn annotation_parameter<K, V>(&mut self, key: K, value: V) -> &mut Self
318 where
319 K: Into<String>,
320 V: Into<String>,
321 {
322 self.annotation_parameters.push((key.into(), value.into()));
323 self
324 }
325
326 pub fn warnings_as_errors(&mut self, warnings_as_errors: bool) -> &mut Self {
328 self.warnings_as_errors = warnings_as_errors;
329 self
330 }
331
332 #[doc(alias("source file"))]
334 pub fn file<P: AsRef<OsStr>>(&mut self, file: P) -> &mut Self {
335 self.files.push(file.as_ref().into());
336 self
337 }
338
339 #[doc(alias("source files"))]
343 pub fn files<P>(&mut self, files: P) -> &mut Self
344 where
345 P: IntoIterator,
346 P::Item: AsRef<OsStr>,
347 {
348 self.files.extend(files.into_iter().map(|f| f.as_ref().into()));
349 self
350 }
351}