forked from Cutieguwu/raven
Most of the refactor. Need to switch machines.
This commit is contained in:
21
crates/java/Cargo.toml
Normal file
21
crates/java/Cargo.toml
Normal file
@@ -0,0 +1,21 @@
|
||||
# May want to find a better name, more reflective of the JDK part
|
||||
# than the entire Java language.
|
||||
|
||||
[package]
|
||||
name = "java"
|
||||
version = "0.1.0"
|
||||
edition.workspace = true
|
||||
license.workspace = true
|
||||
|
||||
description = "Tools for interfacing with the Java Development Kit"
|
||||
repository.workspace = true
|
||||
|
||||
publish.workspace = true
|
||||
test.workspace = true
|
||||
|
||||
[dependencies]
|
||||
bytesize.workspace = true
|
||||
derive_more.workspace = true
|
||||
fs.workspace = true
|
||||
io.workspace = true
|
||||
semver.workspace = true
|
||||
80
crates/java/src/compiler.rs
Normal file
80
crates/java/src/compiler.rs
Normal file
@@ -0,0 +1,80 @@
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
use crate::JAVA_BIN_COMPILER;
|
||||
use crate::Result;
|
||||
|
||||
#[derive(Debug, Default, Clone)]
|
||||
pub struct CompilerBuilder {
|
||||
class_path: Option<PathBuf>,
|
||||
destination: Option<PathBuf>,
|
||||
}
|
||||
|
||||
impl CompilerBuilder {
|
||||
pub fn new() -> Self {
|
||||
Self::default()
|
||||
}
|
||||
|
||||
pub fn class_path<S: AsRef<Path>>(&mut self, class_path: S) -> &mut Self {
|
||||
self.class_path = Some(class_path.as_ref().to_path_buf());
|
||||
self
|
||||
}
|
||||
|
||||
pub fn destination<S: AsRef<Path>>(&mut self, destination: S) -> &mut Self {
|
||||
self.destination = Some(destination.as_ref().to_path_buf());
|
||||
self
|
||||
}
|
||||
|
||||
pub fn build(&self) -> Compiler {
|
||||
let mut flags = vec![];
|
||||
|
||||
if let Option::Some(path) = self.destination.to_owned() {
|
||||
flags.push(CompilerFlag::Destination { path });
|
||||
}
|
||||
|
||||
if let Option::Some(path) = self.class_path.to_owned() {
|
||||
flags.push(CompilerFlag::Classpath { path });
|
||||
}
|
||||
|
||||
Compiler { flags }
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Compiler {
|
||||
flags: Vec<CompilerFlag>,
|
||||
}
|
||||
|
||||
impl Compiler {
|
||||
pub fn compile<P: AsRef<Path>>(self, path: P) -> Result<(Option<String>, Option<String>)> {
|
||||
let mut cmd: Vec<String> = vec![JAVA_BIN_COMPILER.to_string()];
|
||||
|
||||
cmd.extend(
|
||||
self.flags
|
||||
.clone()
|
||||
.into_iter()
|
||||
.flat_map(|f| Into::<Vec<String>>::into(f)),
|
||||
);
|
||||
cmd.extend(
|
||||
fs::expand_files(path)?
|
||||
.into_iter()
|
||||
.filter_map(|f| Some(f.to_str()?.to_string())),
|
||||
);
|
||||
|
||||
Ok(io::run_process(cmd.as_slice())?)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum CompilerFlag {
|
||||
Classpath { path: PathBuf },
|
||||
Destination { path: PathBuf },
|
||||
}
|
||||
|
||||
impl Into<Vec<String>> for CompilerFlag {
|
||||
fn into(self) -> Vec<String> {
|
||||
match self {
|
||||
Self::Classpath { path } => vec!["-classpath".to_string(), path.display().to_string()],
|
||||
Self::Destination { path } => vec!["-d".to_string(), path.display().to_string()],
|
||||
}
|
||||
}
|
||||
}
|
||||
19
crates/java/src/error.rs
Normal file
19
crates/java/src/error.rs
Normal file
@@ -0,0 +1,19 @@
|
||||
use derive_more::{Display, From};
|
||||
|
||||
pub type Result<T> = core::result::Result<T, Error>;
|
||||
|
||||
#[derive(Debug, From, Display)]
|
||||
pub enum Error {
|
||||
EmptyStdout,
|
||||
|
||||
#[from]
|
||||
Io(io::Error),
|
||||
|
||||
NthOutOfBounds,
|
||||
|
||||
#[from]
|
||||
Semver(semver::Error),
|
||||
|
||||
#[from]
|
||||
StdIo(std::io::Error),
|
||||
}
|
||||
49
crates/java/src/lib.rs
Normal file
49
crates/java/src/lib.rs
Normal file
@@ -0,0 +1,49 @@
|
||||
pub mod compiler;
|
||||
pub mod error;
|
||||
pub mod runtime;
|
||||
|
||||
use std::str::FromStr;
|
||||
|
||||
pub use error::{Error, Result};
|
||||
use runtime::VMFlag;
|
||||
|
||||
pub const JAVA_BIN_VM: &str = "java";
|
||||
pub const JAVA_BIN_COMPILER: &str = "javac";
|
||||
|
||||
pub const JAVA_EXT_SOURCE: &str = "java";
|
||||
pub const JAVA_EXT_CLASS: &str = "class";
|
||||
|
||||
pub const F_JAVA_VERSION: &str = ".java-version";
|
||||
|
||||
/// Uses the java binary to parse its stdout for version information.
|
||||
///
|
||||
/// This is non-caching.
|
||||
pub fn get_javac_ver() -> Result<semver::Version> {
|
||||
// TODO: Consider making this pull the version info from javac instead?
|
||||
|
||||
/*
|
||||
* $ java --version
|
||||
* openjdk 21.0.9 2025-10-21
|
||||
* OpenJDK Runtime Environment (build 21.0.9+10)
|
||||
* OpenJDK 64-Bit Server VM (build 21.0.9+10, mixed mode, sharing)
|
||||
*/
|
||||
|
||||
Ok(semver::Version::from_str(
|
||||
get_java_version_info()?
|
||||
.lines()
|
||||
.nth(0)
|
||||
.ok_or(Error::EmptyStdout)?
|
||||
.split_ascii_whitespace()
|
||||
.nth(1)
|
||||
.ok_or(Error::NthOutOfBounds)?,
|
||||
)?)
|
||||
}
|
||||
|
||||
/// Calls the java binary, returning the complete stdout version information.
|
||||
fn get_java_version_info() -> Result<String> {
|
||||
Ok(
|
||||
io::run_process(&[JAVA_BIN_VM, VMFlag::Version.to_string().as_str()])?
|
||||
.0
|
||||
.ok_or(Error::EmptyStdout)?,
|
||||
)
|
||||
}
|
||||
144
crates/java/src/runtime.rs
Normal file
144
crates/java/src/runtime.rs
Normal file
@@ -0,0 +1,144 @@
|
||||
use std::fmt;
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
use crate::JAVA_BIN_VM;
|
||||
use crate::Result;
|
||||
|
||||
use bytesize::ByteSize;
|
||||
|
||||
#[derive(Debug, Default, Clone)]
|
||||
pub struct JVMBuilder {
|
||||
assertions: bool,
|
||||
monitor: bool,
|
||||
ram_min: Option<ByteSize>,
|
||||
ram_max: Option<ByteSize>,
|
||||
class_path: PathBuf,
|
||||
}
|
||||
|
||||
impl JVMBuilder {
|
||||
pub fn new<P: AsRef<Path>>(class_path: P) -> Self {
|
||||
Self {
|
||||
class_path: class_path.as_ref().to_path_buf(),
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn assertions(&mut self, assertions: bool) -> &mut Self {
|
||||
self.assertions = assertions;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn ram_min(&mut self, ram_min: ByteSize) -> &mut Self {
|
||||
self.ram_min = Some(ram_min);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn ram_max(&mut self, ram_max: ByteSize) -> &mut Self {
|
||||
self.ram_max = Some(ram_max);
|
||||
self
|
||||
}
|
||||
|
||||
/// Monitor stdout and stderr in raven's process.
|
||||
pub fn monitor(&mut self, monitor: bool) -> &mut Self {
|
||||
self.monitor = monitor;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn build(&self) -> JVM {
|
||||
let mut flags = vec![VMFlag::Classpath {
|
||||
path: self.class_path.to_owned(),
|
||||
}];
|
||||
|
||||
if self.assertions {
|
||||
flags.push(VMFlag::EnableAssert);
|
||||
}
|
||||
|
||||
if let Option::Some(size) = self.ram_min {
|
||||
flags.push(VMFlag::HeapMin { size });
|
||||
}
|
||||
|
||||
if let Option::Some(size) = self.ram_max {
|
||||
flags.push(VMFlag::HeapMax { size });
|
||||
}
|
||||
|
||||
JVM {
|
||||
monitor: self.monitor,
|
||||
flags,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct JVM {
|
||||
monitor: bool,
|
||||
flags: Vec<VMFlag>,
|
||||
}
|
||||
|
||||
impl JVM {
|
||||
pub fn run<P: AsRef<Path>>(self, entry_point: P) -> Result<(Option<String>, Option<String>)> {
|
||||
let mut cmd = vec![JAVA_BIN_VM.to_string()];
|
||||
|
||||
cmd.extend(
|
||||
self.flags
|
||||
.clone()
|
||||
.into_iter()
|
||||
.flat_map(|f| Into::<Vec<String>>::into(f)),
|
||||
);
|
||||
|
||||
cmd.push(entry_point.as_ref().to_path_buf().display().to_string());
|
||||
|
||||
let result = io::run_process(cmd.as_slice())?;
|
||||
|
||||
if self.monitor {
|
||||
let (stdout, stderr) = &result;
|
||||
|
||||
if let Option::Some(stdout) = stdout
|
||||
&& stdout.len() > 0
|
||||
{
|
||||
print!("{stdout}");
|
||||
}
|
||||
if let Option::Some(stderr) = stderr
|
||||
&& stderr.len() > 0
|
||||
{
|
||||
eprintln!("{stderr}");
|
||||
}
|
||||
}
|
||||
|
||||
Ok(result)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum VMFlag {
|
||||
Classpath { path: PathBuf },
|
||||
EnableAssert,
|
||||
HeapMax { size: ByteSize },
|
||||
HeapMin { size: ByteSize },
|
||||
Version,
|
||||
}
|
||||
|
||||
impl Into<Vec<String>> for VMFlag {
|
||||
fn into(self) -> Vec<String> {
|
||||
self.to_string()
|
||||
.split_ascii_whitespace()
|
||||
.map(|s| s.to_string())
|
||||
.collect()
|
||||
}
|
||||
}
|
||||
|
||||
// Currently being kept around because it's fine for the current branches,
|
||||
// and is currently serving a to_string() method for the Java version fetch.
|
||||
impl fmt::Display for VMFlag {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(
|
||||
f,
|
||||
"-{}",
|
||||
match self {
|
||||
Self::Classpath { path } => format!("classpath {}", path.display()),
|
||||
Self::EnableAssert => String::from("ea"),
|
||||
Self::HeapMax { size } => format!("Xmx{}", size.as_u64()),
|
||||
Self::HeapMin { size } => format!("Xms{}", size.as_u64()),
|
||||
Self::Version => String::from("-version"), // --version
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user