1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351
//! Builder for customizing and invoking a `javac` command.
use std::path::PathBuf;
use std::ffi::{OsStr, OsString};
use std::process::{Command, ExitStatus};
use crate::env_paths::{self, PathExt};
/// A builder for a `javac` command that can be invoked.
///
/// If you need to customize the `javac` command beyond what is provided here,
/// you can use the [`JavaBuild::command()`] method to get a [`Command`]
/// that can be further customized with additional arguments.
///
/// Documentation on `javac` options are based on
/// <https://dev.java/learn/jvm/tools/core/javac/>.
#[derive(Clone, Debug, Default)]
pub struct JavaBuild {
/// Override the default `JAVA_HOME` path.
/// Otherwise, the default path is found using the `JAVA_HOME` env var.
java_home: Option<PathBuf>,
/// Debug info to include in the output ("-g" flag).
debug_info: Option<DebugInfo>,
/// If `true`, all warnings are disabled.
nowarn: bool,
/// Enable verbose output.
verbose: bool,
/// If `true`, warnings are treated as compilation errors.
warnings_as_errors: bool,
/// If `true`, show full descriptions of all places where
/// deprecated members/classes are used or overridden.
/// If `false`, is to show only a summary on a per-source file basis).
deprecation: bool,
/// If `true`, enable preview language features.
enable_preview_features: bool,
/// Specify where to find user class files and annotation processors.
/// If not provided, the current directory will be used.
class_paths: Vec<OsString>,
/// Specify where to find input source files.
/// If not specified, `class_paths` will be searched for source files.
source_paths: Vec<OsString>,
/// Override the location of bootstrap class files.
boot_class_paths: Vec<OsString>,
/// Override the location of installed extensions.
extension_dirs: Vec<OsString>,
/// Override the location of endorsed standards path.
endorsed_dirs: Vec<OsString>,
/// Specify names of the annotation processors to run.
/// Setting this will bypass the default discovery process.
annotation_processors: Vec<OsString>,
/// Specify where to find annotation processors.
/// If not provided, the `class_paths` will be searched.
annotation_processor_paths: Vec<OsString>,
/// Enable generation of metadata on method parameters
/// such that the reflection API can be used to retrieve parameter info.
method_paramater_metadata: bool,
/// Specify where to place generated class files.
/// If not provided, class files will be placed
/// in the same directory as the source files.
#[doc(alias = "-d")]
classes_out_dir: Option<OsString>,
/// Specify where to place generated source files.
#[doc(alias = "-s")]
sources_out_dir: Option<OsString>,
/// Specify where to place generated native header files.
#[doc(alias = "-h")]
headers_out_dir: Option<OsString>,
/// Pass an option to an annotation processor.
#[doc(alias = "-A")]
annotation_parameters: Vec<(String, String)>,
/// Paths to the java source files to be compiled.
files: Vec<OsString>,
}
/// Debug information to include in the output of a `javac` build.
///
/// The default value for this struct is for everything to be `true`,
/// meaning all debug information is included.
/// This is only relevant *if* you set the `debug_info` field in [`JavaBuild`].
#[derive(Clone, Debug)]
pub struct DebugInfo {
pub line_numbers: bool,
pub variables: bool,
pub source_files: bool,
}
impl Default for DebugInfo {
fn default() -> Self {
Self {
line_numbers: true,
variables: true,
source_files: true,
}
}
}
impl DebugInfo {
fn add_as_args_to<'c>(&self, cmd: &'c mut Command) -> &'c mut Command {
if self.line_numbers {
cmd.arg("-g:lines");
}
if self.variables {
cmd.arg("-g:vars");
}
if self.source_files {
cmd.arg("-g:source");
}
if !self.line_numbers && !self.variables && !self.source_files {
cmd.arg("-g:none");
}
cmd
}
}
impl JavaBuild {
/// Creates a new `JavaBuild` instance with default values,
/// which can be further customized using the builder methods.
pub fn new() -> Self {
Default::default()
}
/// Executes the `javac` command based on this `JavaBuild` instance.
pub fn compile(&self) -> std::io::Result<ExitStatus> {
self.command()?.status()
}
/// Returns a [`Command`] based on this `JavaBuild` instance
/// that can be inspected or customized before being executed.
pub fn command(&self) -> std::io::Result<Command> {
let jh_clone = self.java_home.clone();
let java_home = jh_clone
.and_then(PathExt::path_if_exists)
.or_else(env_paths::java_home)
.ok_or_else(|| std::io::Error::other(
"JAVA_HOME not provided, and could not be auto-discovered."
))?;
let mut cmd = Command::new(java_home.join("bin").join("javac"));
if let Some(d) = self.debug_info.as_ref() {
d.add_as_args_to(&mut cmd);
}
self.class_paths .iter().for_each(|p| { cmd.arg("-cp").arg(p); });
self.source_paths .iter().for_each(|p| { cmd.arg("-sourcepath").arg(p); });
self.boot_class_paths.iter().for_each(|p| { cmd.arg("-bootclasspath").arg(p); });
self.extension_dirs .iter().for_each(|p| { cmd.arg("-extdirs").arg(p); });
let processors = self.annotation_processors.join(OsStr::new(","));
if processors.len() != 0 {
cmd.arg("-processor").arg(processors);
}
self.annotation_processor_paths.iter()
.for_each(|p| { cmd.arg("-processorpath").arg(p); });
for (flag, dir) in [
("-d", self.classes_out_dir.as_ref()),
("-s", self.sources_out_dir.as_ref()),
("-h", self.headers_out_dir.as_ref()),
].iter() {
if let Some(dir) = dir {
cmd.arg(flag).arg(dir);
}
}
for (flag, cond) in [
("-nowarn", self.nowarn),
("-verbose", self.verbose),
("-deprecation", self.deprecation),
("-parameters", self.method_paramater_metadata),
("-Werror", self.warnings_as_errors),
("--enable-preview", self.enable_preview_features)
].into_iter() {
if cond { cmd.arg(flag); }
}
self.annotation_parameters.iter()
.for_each(|(k,v)| { cmd.arg(format!("-A{}={}", k, v)); });
self.files.iter().for_each(|f| { cmd.arg(f); });
Ok(cmd)
}
///////////////////////////////////////////////////////////////////////////
//////////////////////// Builder methods below ////////////////////////////
///////////////////////////////////////////////////////////////////////////
/// Override the default `JAVA_HOME` path.
///
/// If not set, the default path is found using the `JAVA_HOME` env var.
pub fn java_home<P: Into<PathBuf>>(&mut self, java_home: P) -> &mut Self {
self.java_home = Some(java_home.into());
self
}
/// Set which debug info should be included in the generated class files
#[doc(alias("-g"))]
pub fn debug_info(&mut self, debug_info: DebugInfo) -> &mut Self {
self.debug_info = Some(debug_info);
self
}
/// If set to `true`, all warnings are disabled.
pub fn nowarn(&mut self, nowarn: bool) -> &mut Self {
self.nowarn = nowarn;
self
}
/// Enable verbose output.
pub fn verbose(&mut self, verbose: bool) -> &mut Self {
self.verbose = verbose;
self
}
/// Configure the output about `deprecation` usage.
///
/// * If `true`, javac will output full descriptions of all places
/// where deprecated members/classes are used or overridden.
/// * If `false`, javac will output only a summary on a per-source file basis.
pub fn deprecation(&mut self, deprecation: bool) -> &mut Self {
self.deprecation = deprecation;
self
}
/// Enable or disable preview language features.
pub fn enable_preview_features(&mut self, enable_preview_features: bool) -> &mut Self {
self.enable_preview_features = enable_preview_features;
self
}
/// Specify where to find user class files and annotation processors.
///
/// If no class paths are provided, the current directory will be used.
pub fn class_path<P: AsRef<OsStr>>(&mut self, class_path: P) -> &mut Self {
self.class_paths.push(class_path.as_ref().into());
self
}
/// Specify where to find input source files.
///
/// If not specified, `class_paths` will be searched for source files.
pub fn source_path<P: AsRef<OsStr>>(&mut self, source_path: P) -> &mut Self {
self.source_paths.push(source_path.as_ref().into());
self
}
/// Specify where to find bootstrap class files.
///
/// If set, this will override the default search locations.
pub fn boot_class_path<P: AsRef<OsStr>>(&mut self, boot_class_path: P) -> &mut Self {
self.boot_class_paths.push(boot_class_path.as_ref().into());
self
}
/// Specify where to find installed extensions.
///
/// If set, this will override the default search locations.
pub fn extension_dir<P: AsRef<OsStr>>(&mut self, extension_dir: P) -> &mut Self {
self.extension_dirs.push(extension_dir.as_ref().into());
self
}
/// Specify where to find endorsed standards.
///
/// If set, this will override the default endorsed standards path.
pub fn endorsed_dir<P: AsRef<OsStr>>(&mut self, endorsed_dir: P) -> &mut Self {
self.endorsed_dirs.push(endorsed_dir.as_ref().into());
self
}
/// Add an annotation processor to be run during compilation.
///
/// Setting this will bypass the default discovery process.
pub fn annotation_processor<S: AsRef<OsStr>>(&mut self, annotation_processor: S) -> &mut Self {
self.annotation_processors.push(annotation_processor.as_ref().into());
self
}
/// Add a path to search for annotation processors.
///
/// If not provided, the class paths will be searched by default.
pub fn annotation_processor_path<P: AsRef<OsStr>>(&mut self, annotation_processor_path: P) -> &mut Self {
self.annotation_processor_paths.push(annotation_processor_path.as_ref().into());
self
}
/// Enable generation of metadata on method parameters
/// such that the reflection API can be used to retrieve parameter info.
pub fn method_paramater_metadata(&mut self, method_paramater_metadata: bool) -> &mut Self {
self.method_paramater_metadata = method_paramater_metadata;
self
}
/// Specify where to place generated class files.
///
/// If not provided, class files will be placed
/// in the same directory as the source files.
#[doc(alias("-d"))]
pub fn classes_out_dir<P: AsRef<OsStr>>(&mut self, classes_out_dir: P) -> &mut Self {
self.classes_out_dir = Some(classes_out_dir.as_ref().into());
self
}
/// Specify where to place generated source files.
#[doc(alias("-s"))]
pub fn sources_out_dir<P: AsRef<OsStr>>(&mut self, sources_out_dir: P) -> &mut Self {
self.sources_out_dir = Some(sources_out_dir.as_ref().into());
self
}
/// Specify where to place generated native header files.
#[doc(alias("-h"))]
pub fn headers_out_dir<P: AsRef<OsStr>>(&mut self, headers_out_dir: P) -> &mut Self {
self.headers_out_dir = Some(headers_out_dir.as_ref().into());
self
}
/// Add a key-value pair to be passed as an option to an annotation processor.
#[doc(alias("-A"))]
pub fn annotation_parameter<K, V>(&mut self, key: K, value: V) -> &mut Self
where
K: Into<String>,
V: Into<String>,
{
self.annotation_parameters.push((key.into(), value.into()));
self
}
/// If set to `true`, warnings are treated as compilation errors.
pub fn warnings_as_errors(&mut self, warnings_as_errors: bool) -> &mut Self {
self.warnings_as_errors = warnings_as_errors;
self
}
/// Adds a Java source file to be compiled by javac.
#[doc(alias("source file"))]
pub fn file<P: AsRef<OsStr>>(&mut self, file: P) -> &mut Self {
self.files.push(file.as_ref().into());
self
}
/// Adds multiple Java source files to be compiled by javac.
///
/// This is the same as calling [`JavaBuild::file()`] multiple times.
#[doc(alias("source files"))]
pub fn files<P>(&mut self, files: P) -> &mut Self
where
P: IntoIterator,
P::Item: AsRef<OsStr>,
{
self.files.extend(files.into_iter().map(|f| f.as_ref().into()));
self
}
}