forked from Cutieguwu/raven
234 lines
5.6 KiB
Rust
234 lines
5.6 KiB
Rust
use std::fmt;
|
|
use std::path::{Path, PathBuf};
|
|
|
|
use anyhow::Context;
|
|
use bytesize::ByteSize;
|
|
|
|
pub const JAVA_VM: &str = "java";
|
|
pub const JAVA_COMPILER: &str = "javac";
|
|
|
|
pub const JAVA_EXT_SOURCE: &str = "java";
|
|
pub const JAVA_EXT_CLASS: &str = "class";
|
|
|
|
// TODO: Builder-pattern JVM wrapper.
|
|
#[derive(Debug, Default, Clone)]
|
|
pub struct JVMBuilder {
|
|
assertions: bool,
|
|
monitor: bool,
|
|
ram_min: Option<ByteSize>,
|
|
ram_max: Option<ByteSize>,
|
|
}
|
|
|
|
impl JVMBuilder {
|
|
pub fn new() -> Self {
|
|
Self::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![];
|
|
|
|
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,
|
|
) -> anyhow::Result<(Option<String>, Option<String>)> {
|
|
let mut cmd = vec![JAVA_VM.to_string()];
|
|
|
|
cmd.extend(
|
|
self.flags
|
|
.clone()
|
|
.into_iter()
|
|
.flat_map(|f| Into::<Vec<String>>::into(f)),
|
|
);
|
|
|
|
for part in &cmd {
|
|
println!("{}", part);
|
|
}
|
|
cmd.push("-cp".to_string());
|
|
cmd.push(entry_point.as_ref().display().to_string());
|
|
|
|
// Jave needs a class path and a file name to run.
|
|
cmd.push("Test".to_string());
|
|
|
|
println!("{:?}", cmd.as_slice());
|
|
|
|
|
|
let result = crate::io::run_process(cmd.as_slice());
|
|
|
|
if self.monitor
|
|
&& let Result::Ok((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}");
|
|
}
|
|
}
|
|
|
|
result
|
|
}
|
|
}
|
|
|
|
#[derive(Debug, Clone)]
|
|
pub enum VMFlag {
|
|
EnableAssert,
|
|
HeapMax { size: ByteSize },
|
|
HeapMin { size: ByteSize },
|
|
Version,
|
|
}
|
|
|
|
impl Into<Vec<String>> for VMFlag {
|
|
fn into(self) -> Vec<String> {
|
|
vec![self.to_string()]
|
|
}
|
|
}
|
|
|
|
// 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::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
|
|
}
|
|
)
|
|
}
|
|
}
|
|
|
|
#[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,
|
|
) -> anyhow::Result<(Option<String>, Option<String>)> {
|
|
let mut cmd: Vec<String> = vec![JAVA_COMPILER.to_string()];
|
|
|
|
cmd.extend(
|
|
self.flags
|
|
.clone()
|
|
.into_iter()
|
|
.flat_map(|f| Into::<Vec<String>>::into(f)),
|
|
);
|
|
cmd.extend(crate::fs::expand_files(path)?.into_iter().filter_map(|f| {
|
|
Some(
|
|
f.to_str()
|
|
.context("Failed to cast PathBuf to &str")
|
|
.ok()?
|
|
.to_string(),
|
|
)
|
|
}));
|
|
|
|
crate::io::run_process(cmd.as_slice())
|
|
}
|
|
}
|
|
|
|
#[derive(Debug, Clone)]
|
|
pub enum CompilerFlag {
|
|
Destination { path: PathBuf },
|
|
Classpath { path: PathBuf },
|
|
}
|
|
|
|
impl Into<Vec<String>> for CompilerFlag {
|
|
fn into(self) -> Vec<String> {
|
|
match self {
|
|
Self::Classpath { path } => vec!["-cp".to_string(), path.display().to_string()],
|
|
Self::Destination { path } => vec!["-d".to_string(), path.display().to_string()],
|
|
}
|
|
}
|
|
}
|