Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| b20a77c9a5 |
@@ -9,6 +9,11 @@ from the command line with sanity.
|
||||
It takes inspiration from Cargo, and essentially serves as a sanity-preserving
|
||||
wrapper for `javac` and `java`.
|
||||
|
||||
Because of the Rust-originating concepts, Raven *is opinionated*. If you have a
|
||||
major problem with Rust, look away. If you genuinely intend on using Raven,
|
||||
probably still look away. This likely won't enter any form of mainstream or
|
||||
active long-term support.
|
||||
|
||||
## Installing
|
||||
|
||||
Installing with `cargo`:
|
||||
@@ -22,7 +27,7 @@ worked out over time.
|
||||
|
||||
`raven` is only tested under linux. NT and Darwin support is **not** guaranteed.
|
||||
|
||||
WSL *may* affect the behaviour of raven. Quirks to be worked out.
|
||||
WSL *may* affect the behaviour of Raven. Quirks to be worked out.
|
||||
|
||||
## Getting Started
|
||||
|
||||
@@ -61,3 +66,136 @@ Options:
|
||||
will be implemented should there arise a need.
|
||||
- [ ] Wrap errors properly, instead of hap-hazardly using `anyhow`
|
||||
- [ ] Maybe proper logging?
|
||||
|
||||
## Project Structure
|
||||
|
||||
It helps if you're starting from an understanding of a Cargo Workspace.
|
||||
|
||||
- **Workspace:** The top level of a project. A workspace contains a series of
|
||||
*packages*.
|
||||
- **Package:** An individual library or binary which possesses its own
|
||||
`Nest.toml` or `pom.xml`. This should be equivalent to Maven's concept of a
|
||||
module.
|
||||
- **Module:** Any organization of classes and modules which don't possess a
|
||||
`Nest.toml` or `pom.xml`.
|
||||
|
||||
If you're unfamiliar with a cargo workspace manifest,
|
||||
An example workspace Nest:
|
||||
|
||||
```toml
|
||||
[workspace]
|
||||
default_package = "app"
|
||||
members = [
|
||||
"app",
|
||||
"cli",
|
||||
"core",
|
||||
"installer",
|
||||
"io",
|
||||
]
|
||||
|
||||
[meta]
|
||||
name = "myProject"
|
||||
version = "0.1.0"
|
||||
|
||||
[dependencies]
|
||||
cli = { path = "cli" }
|
||||
core = { path = "core" }
|
||||
io = { path = "io" }
|
||||
```
|
||||
|
||||
### Inheritance
|
||||
|
||||
Again, this takes from cargo's functionality.
|
||||
|
||||
To inherit from the workspace Nest (given the workspace example above):
|
||||
|
||||
```toml
|
||||
[package]
|
||||
entry_point = "Main.java"
|
||||
|
||||
[meta]
|
||||
name = "app"
|
||||
version = "0.1.0"
|
||||
|
||||
[dependnencies]
|
||||
cli.workspace = true
|
||||
io.workspace = true
|
||||
```
|
||||
|
||||
### Reverse Domain Name Notation
|
||||
|
||||
Raven prefers this be trashed, burned, and ground into the earth with your boot.
|
||||
|
||||
It is possible to work around this with workspace members and default entry
|
||||
points.
|
||||
|
||||
Raven feels little need to respect Java's standards.
|
||||
|
||||
### Sub-modules
|
||||
|
||||
Given the structure:
|
||||
|
||||
`WORKSPACE/packageA/packageB`
|
||||
|
||||
If you want to have subpackages like maven permits (via submodules), it is
|
||||
technically possible by listing the submodule like so in the workspace members:
|
||||
|
||||
```toml
|
||||
[workspace]
|
||||
members = [
|
||||
"packageA",
|
||||
"packageA/packageB",
|
||||
]
|
||||
|
||||
[dependencies]
|
||||
packageA = { path = "packageA" }
|
||||
|
||||
[dependencies.packageA]
|
||||
path = "packageA"
|
||||
dependencies = { packageB = "packageA/packageB" }
|
||||
```
|
||||
|
||||
Obviously, this is a total disaster, and is considered bad practise under Raven.
|
||||
|
||||
The project should be restructured to either:
|
||||
|
||||
1. Have all packages immediately within the workspace (remove all nesting), or
|
||||
2. Move the complex package with subpackages out of tree into its own workspace
|
||||
and add it as a dependency with a relative path in the workspace Nest. The
|
||||
reason being, that packageA is so complex that it should be maintained as
|
||||
its own thing, and is realistically a fairly universal library.
|
||||
|
||||
#### For Option 1
|
||||
|
||||
Example workspace Nest:
|
||||
|
||||
```toml
|
||||
[workspace]
|
||||
members = [
|
||||
"packageA",
|
||||
"packageB",
|
||||
]
|
||||
|
||||
[dependencies]
|
||||
packageA = { path = "packageA" }
|
||||
packageB = { path = "packageB" }
|
||||
```
|
||||
|
||||
#### For Option 2
|
||||
|
||||
Example workspace Nest:
|
||||
|
||||
```toml
|
||||
[dependencies]
|
||||
packageA = { path = "external/packageA" }
|
||||
```
|
||||
|
||||
Where packageA's workspace Nest:
|
||||
|
||||
```toml
|
||||
[workspace]
|
||||
members = [
|
||||
"core",
|
||||
"packageB"
|
||||
]
|
||||
```
|
||||
|
||||
@@ -5,7 +5,7 @@ use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::{Error, Result};
|
||||
|
||||
/// [`Class`] represents a source file. It is a handler.
|
||||
/// Data struct
|
||||
#[derive(Debug, Clone, Deserialize, Serialize, Hash, PartialEq, Eq, PartialOrd, Ord)]
|
||||
pub struct Class {
|
||||
/// Path relative to PACKAGE/java, without file extension.
|
||||
@@ -14,13 +14,6 @@ pub struct Class {
|
||||
}
|
||||
|
||||
impl Class {
|
||||
/// Creates a new [`Class`].
|
||||
///
|
||||
/// # Error
|
||||
///
|
||||
/// * [`Error::MismatchedPackage`] if the provided `file_path` is absolute, and cannot be
|
||||
/// subtracted to just its relative path within its parent package.
|
||||
/// * [`Error::Io`] if the source file cannot be loaded and digested.
|
||||
pub fn new<P: AsRef<Path>>(package_src_root: P, file_path: P) -> Result<Self> {
|
||||
let mut file_path = file_path.as_ref().to_path_buf();
|
||||
if file_path.is_absolute() {
|
||||
@@ -34,17 +27,6 @@ impl Class {
|
||||
})
|
||||
}
|
||||
|
||||
/// Returns a boolean representing if the class is up to date.
|
||||
///
|
||||
/// # Criteria
|
||||
///
|
||||
/// * The class path is local.
|
||||
/// * The class has been compiled to `target/`
|
||||
/// * The class' source file has not been updated.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// * [`Error::Io`]
|
||||
pub fn is_updated<P: AsRef<Path>>(&self, class_path: P) -> Result<bool> {
|
||||
// If the path is local and that file has not been updated.
|
||||
Ok(class_path
|
||||
@@ -55,13 +37,6 @@ impl Class {
|
||||
&& self.checksum == sha256::try_digest(self.path.clone())?)
|
||||
}
|
||||
|
||||
/// Updates the class' checksum.
|
||||
///
|
||||
/// Only call this if you know that the class has been compiled successfully.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// * [`Error::Io`] if the source file cannot be loaded and digested.
|
||||
pub fn update<P: AsRef<Path>>(&mut self, package_src_root: P) -> Result<()> {
|
||||
self.checksum = sha256::try_digest(dbg!(
|
||||
package_src_root
|
||||
|
||||
@@ -14,10 +14,6 @@ use crate::package::PackageHandler;
|
||||
use crate::prey::{F_PREY_LOCK, F_PREY_TOML, Prey};
|
||||
use crate::{Error, package};
|
||||
|
||||
/// Represents a raven workspace.
|
||||
///
|
||||
/// [`WorkspaceHandler`] manages project compilation, cache writing,
|
||||
/// and tracking of all packages that make up the workspace.
|
||||
#[derive(Debug)]
|
||||
pub struct WorkspaceHandler {
|
||||
nest: Nest,
|
||||
@@ -30,13 +26,6 @@ impl WorkspaceHandler {
|
||||
const DIR_SRC: &str = "src/";
|
||||
const DIR_TARGET: &str = "target/";
|
||||
|
||||
/// Creates a new [`WorkspaceHandler`] instance at the provided path.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// * [`Error::Io`] if the `project_root` cannot be canonicalized.
|
||||
/// * [`Error::MissingFileName`] if a name for the workspace cannot be derived
|
||||
/// from the `project_root`
|
||||
pub fn new<P: AsRef<Path>>(project_root: P) -> crate::Result<Self> {
|
||||
let project_root = project_root.as_ref().canonicalize()?;
|
||||
|
||||
@@ -53,14 +42,6 @@ impl WorkspaceHandler {
|
||||
})
|
||||
}
|
||||
|
||||
/// Loads an existing workspace from the provided path.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// * [`Error::Io`] for any number of IO-related errors.
|
||||
/// Unwrap the [`std::io::Error`] for further information.
|
||||
/// * [`Error::MissingFileName`] if a name for the workspace cannot be derived
|
||||
/// from the `project_root`
|
||||
pub fn load<P: AsRef<Path>>(project_root: P) -> crate::Result<Self> {
|
||||
let project_root = project_root.as_ref().canonicalize()?;
|
||||
|
||||
@@ -76,7 +57,6 @@ impl WorkspaceHandler {
|
||||
Ok(workspace_manager)
|
||||
}
|
||||
|
||||
/// Writes all lock files (Nest.toml, src/**/Prey.toml) to the disk.
|
||||
pub fn write_locks(&self) -> crate::Result<()> {
|
||||
if let Option::Some(lock) = self.nest_lock.clone() {
|
||||
lock.write(self.project_root.join(F_NEST_LOCK))?;
|
||||
@@ -89,26 +69,6 @@ impl WorkspaceHandler {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Initializes a new workspace.
|
||||
///
|
||||
/// # Behaviour
|
||||
///
|
||||
/// Init will avoid overwriting or adding an example project if raven is being integrated into
|
||||
/// an existing workspace codebase.
|
||||
///
|
||||
/// Init will always write:
|
||||
///
|
||||
/// * Nest.toml
|
||||
/// * .java-version
|
||||
/// * Attempt to append to .gitignore
|
||||
///
|
||||
/// If this is an empty workspace, init will include:
|
||||
///
|
||||
/// * Calling `git init`
|
||||
/// * Writing an example project.
|
||||
/// * Automatic package discovery.
|
||||
///
|
||||
/// # Errors
|
||||
pub fn init(&mut self) -> crate::Result<&mut Self> {
|
||||
// ORDER MATTERS.
|
||||
let is_empty = read_dir(self.project_root.as_path()).is_ok_and(|tree| tree.count() == 0);
|
||||
@@ -154,18 +114,7 @@ impl WorkspaceHandler {
|
||||
Ok(self)
|
||||
}
|
||||
|
||||
/// This is a naive build; it attempts to build every source file individually.
|
||||
///
|
||||
/// It calls [`PackageHandler::get_outdated()`] on all known packages to determine
|
||||
/// compile targets.
|
||||
///
|
||||
/// It will then attempt to write all locks to update the caches and avoid attempting to
|
||||
/// needlessly recompile targets.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// * [`Error::AbsentPrimeError`]
|
||||
/// * [`Error::Io`]
|
||||
// This is the naive build
|
||||
pub fn build(&mut self) -> crate::Result<&mut Self> {
|
||||
let compiler = java::compiler::CompilerBuilder::new()
|
||||
.class_path(Self::DIR_TARGET)
|
||||
@@ -203,20 +152,6 @@ impl WorkspaceHandler {
|
||||
Ok(self)
|
||||
}
|
||||
|
||||
/// Attempts to call the JVM to run, in order of precedence:
|
||||
///
|
||||
/// 1. Provided entry point which includes a specific source file.
|
||||
/// 2. Provided entry point to a package, using that package's default entry point
|
||||
/// defined in it's `Prey.toml`
|
||||
/// 3. The workspace's default package defined in `Nest.toml`, and that package's default
|
||||
/// entry point as defined in it's `Prey.toml`.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// * [`Error::UnknownPackage`] if the package cannot be found.
|
||||
/// * [`Error::UndefinedEntryPoint`] if no fallback entry point can be found.
|
||||
/// * [`Error::Io`]
|
||||
/// * [`Error::Java`]
|
||||
pub fn run<P: AsRef<Path>>(
|
||||
&mut self,
|
||||
entry_point: Option<P>,
|
||||
@@ -252,11 +187,6 @@ impl WorkspaceHandler {
|
||||
Ok(self)
|
||||
}
|
||||
|
||||
/// Cleans the workspace of it's caches:
|
||||
///
|
||||
/// * `target/`
|
||||
/// * Nest.lock
|
||||
/// * Prey.lock
|
||||
pub fn clean(&mut self) -> crate::Result<&mut Self> {
|
||||
// Clear Nest.lock
|
||||
if let Err(err) = std::fs::remove_file(self.project_root.join(F_NEST_LOCK)) {
|
||||
@@ -285,14 +215,7 @@ impl WorkspaceHandler {
|
||||
Ok(self)
|
||||
}
|
||||
|
||||
/// Scan for newly created packages, wrapping them in [`PackageHandler`]s
|
||||
/// and adding them to the [`WorkspaceHandler`]'s package map.
|
||||
///
|
||||
/// Packages are identified by their `Prey.toml` manifests.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// * [`Error::Io`]
|
||||
/// Add any newly created packages.
|
||||
fn discover_packages(&mut self) -> crate::Result<()> {
|
||||
// Scan the src/ directory for entries,
|
||||
// filter out the files,
|
||||
@@ -357,18 +280,10 @@ impl WorkspaceHandler {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Writes the [`WorkspaceHandler`]'s `Nest.toml` to disk.
|
||||
fn write_nest(&self) -> crate::Result<()> {
|
||||
Ok(self.nest.write(self.project_root.clone())?)
|
||||
}
|
||||
|
||||
/// Writes a `.java-version` file for `jenv` to the disk with an automatically parsed
|
||||
/// java version from [`java::get_javac_version()`].
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// * [`Error::Io`]
|
||||
/// * [`Error::Java`]
|
||||
fn write_java_version(&self) -> crate::Result<()> {
|
||||
if let Result::Ok(mut f) = OpenOptions::new()
|
||||
.write(true)
|
||||
@@ -381,7 +296,6 @@ impl WorkspaceHandler {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Writes an example project tree to disk.
|
||||
fn write_dir_tree(&self) -> std::io::Result<()> {
|
||||
for dir in [
|
||||
format!("{}main/java", Self::DIR_SRC),
|
||||
@@ -394,7 +308,6 @@ impl WorkspaceHandler {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Writes an example project to disk.
|
||||
fn write_example_project(&self) -> crate::Result<()> {
|
||||
let main: PathBuf = PathBuf::from(Self::DIR_SRC).join("main/");
|
||||
let test: PathBuf = PathBuf::from(Self::DIR_SRC).join("test/");
|
||||
|
||||
@@ -5,11 +5,6 @@ use cli::{CLI_ARGS, Command};
|
||||
|
||||
fn main() -> anyhow::Result<()> {
|
||||
// type_ is yet unused.
|
||||
|
||||
// `raven new` requires special behaviour.
|
||||
// This will call the "new" part of the command,
|
||||
// changing the cwd, before calling WorkspaceHandler.init()
|
||||
// on the actual workspace directory.
|
||||
if let Command::New { project_name, .. } = &CLI_ARGS.command {
|
||||
new(project_name.to_owned())?;
|
||||
}
|
||||
@@ -40,10 +35,7 @@ fn main() -> anyhow::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Creates a new project directory and enters it.
|
||||
fn new(project_name: String) -> anyhow::Result<()> {
|
||||
// Because this messes around with the CWD, this should live external to any
|
||||
// instance of a `WorkspaceHandler`.
|
||||
let cwd = std::env::current_dir()?.join(project_name);
|
||||
|
||||
std::fs::create_dir(&cwd)?;
|
||||
|
||||
Reference in New Issue
Block a user