compiles main classes (todo dependencies)
This commit is contained in:
parent
4ce945450c
commit
caa440a27a
11 changed files with 240 additions and 62 deletions
11
Cargo.lock
generated
11
Cargo.lock
generated
|
|
@ -98,6 +98,16 @@ version = "1.0.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||
|
||||
[[package]]
|
||||
name = "colored"
|
||||
version = "2.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cbf2150cce219b664a8a70df7a1f933836724b503f8a413af9365b4dcc4d90b8"
|
||||
dependencies = [
|
||||
"lazy_static",
|
||||
"windows-sys 0.48.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "core-foundation"
|
||||
version = "0.9.4"
|
||||
|
|
@ -423,6 +433,7 @@ version = "0.1.0"
|
|||
dependencies = [
|
||||
"anyhow",
|
||||
"bytes",
|
||||
"colored",
|
||||
"hex",
|
||||
"home",
|
||||
"reqwest",
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ edition = "2021"
|
|||
toml = "0.8"
|
||||
anyhow = "1.0"
|
||||
strong-xml = "0.6"
|
||||
colored = "2.0"
|
||||
reqwest = {version = "0.11", features = ["blocking"]}
|
||||
tokio = { version = "1", features = ["full"] }
|
||||
bytes = "1.5"
|
||||
|
|
|
|||
15
README.md
15
README.md
|
|
@ -1,10 +1,8 @@
|
|||
**Jargo**
|
||||
|
||||
Exploring the need/possibility to move from maven/gradle to cargo.
|
||||
An experimental build tool for Java taking inspiration from Cargo.
|
||||
|
||||
That is, build tool for java taking inspiration from Cargo
|
||||
|
||||
And it's called Jargo. I do not wish to put a J in front of anything, as is the java tradition,
|
||||
And it's called *Jargo*. I do not wish to put a J in front of anything, as is the java tradition,
|
||||
but 'jargo' actually sounds kinda nice and it conveys pretty much what it is.
|
||||
|
||||
It is NOT a new maven (not yet at least). That's the reason it's not called 'raven.'
|
||||
|
|
@ -12,8 +10,8 @@ It is NOT a new maven (not yet at least). That's the reason it's not called 'rav
|
|||
Basic premisses:
|
||||
1. written in Rust
|
||||
2. does NOT copy anything from the maven philosophy (phases, goals etc). Instead find out on the go what would be
|
||||
a good design
|
||||
3. uses TOML
|
||||
a good design. _That said, some things are just good to keep using, such as the default project structure._
|
||||
3. configured in TOML. ie. no XML, **yay!**, AND no Turing-completeness (groovy/kotlin in gradle), **yay2!!**
|
||||
|
||||
see [tests/sample_project/Jargo.toml](https://github.com/shautvast/jargo/blob/main/tests/sample_project/Jargo.toml) to get an impression of what that looks like.
|
||||
|
||||
|
|
@ -38,7 +36,8 @@ _Every tool is currently being rewritten in rust._ And for good reason!
|
|||
|
||||
2. Why not create a drop-in replacement for maven written in rust?
|
||||
|
||||
_While that would make migration a no-brainer, it seems too ambitious based on what I've seen of the maven
|
||||
codebase_
|
||||
_While that would (in theory) make migration a no-brainer, it seems too ambitious based on what I've seen of the maven
|
||||
codebase. Other than that you will most likely run into onforeseen issues while migrating this way, because this or
|
||||
that is subtly different here and there. Better avoid the promise of easy migration altogether._
|
||||
|
||||
|
||||
|
|
|
|||
107
src/compile/mod.rs
Normal file
107
src/compile/mod.rs
Normal file
|
|
@ -0,0 +1,107 @@
|
|||
use std::fs;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::process::Command;
|
||||
|
||||
use anyhow::Error;
|
||||
use colored::Colorize;
|
||||
use crate::compile::PathNode::*;
|
||||
|
||||
use crate::Project;
|
||||
|
||||
const SOURCES: &str = "src/main/java";
|
||||
const TESTSOURCES: &str = "src/test/java";
|
||||
const RESOURCES: &str = "src/main/resources";
|
||||
const TESTRESOURCES: &str = "src/main/java";
|
||||
|
||||
const TARGET_MAIN: &str = "target/classes";
|
||||
const TARGET_TEST: &str = "target/test-classes";
|
||||
|
||||
|
||||
#[derive(Debug)]
|
||||
enum PathNode {
|
||||
DirNode(PathBuf, Vec<PathNode>, Vec<PathNode>),
|
||||
FileNode(PathBuf),
|
||||
}
|
||||
|
||||
pub fn run(project: &Project) -> Result<(), Error> {
|
||||
println!("{} {}.{}-{}", "Compiling".green(), project.group, project.name, project.version);
|
||||
|
||||
let root = PathBuf::from(&project.project_root).join(SOURCES);
|
||||
|
||||
let mut src_tree = DirNode(root.clone(), Vec::new(), Vec::new());
|
||||
determine_src_tree(root, &mut src_tree)?;
|
||||
println!("{:?}", src_tree);
|
||||
|
||||
compile_sourcedir(project, &mut src_tree)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn compile_sourcedir(project: &Project, src_tree: &mut PathNode) -> Result<(), Error> {
|
||||
if let DirNode(dir_name, subdirs, contents) = src_tree {
|
||||
if !contents.is_empty() {
|
||||
let mut javac = if cfg!(target_os = "windows") {
|
||||
vec!["/C"]
|
||||
} else {
|
||||
vec![]
|
||||
};
|
||||
let classes = PathBuf::from(&project.project_root).join(TARGET_MAIN);
|
||||
let classes = classes.to_str().unwrap();
|
||||
|
||||
javac.append(&mut vec!["javac", "-d", classes, "-sourcepath"]);
|
||||
javac.push(dir_name.to_str().unwrap().into());
|
||||
for source in contents {
|
||||
if let FileNode(source_name) = source {
|
||||
let name = source_name.to_str().unwrap();
|
||||
javac.push(name);
|
||||
}
|
||||
}
|
||||
|
||||
let output = if cfg!(target_os = "windows") {
|
||||
Command::new("cmd")
|
||||
.args(javac)
|
||||
.output()
|
||||
.expect("failed to execute process")
|
||||
} else {
|
||||
Command::new("sh")
|
||||
.arg("-c")
|
||||
.arg(javac.join(" "))
|
||||
.output()
|
||||
.expect("failed to execute process")
|
||||
};
|
||||
if !output.stderr.is_empty() {
|
||||
println!("{}", String::from_utf8(output.stderr)?.red());
|
||||
}
|
||||
println!("{}", String::from_utf8(output.stdout)?);
|
||||
}
|
||||
for subdir in subdirs {
|
||||
compile_sourcedir(project, subdir)?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn determine_src_tree(parent: PathBuf, parent_node: &mut PathNode) -> Result<(), Error> {
|
||||
let paths = fs::read_dir(&parent)?;
|
||||
|
||||
for path in paths {
|
||||
let path = path?;
|
||||
|
||||
if path.metadata()?.is_dir() {
|
||||
let mut subdir = DirNode(path.path(), Vec::new(), Vec::new());
|
||||
determine_src_tree(path.path(), &mut subdir)?;
|
||||
if let DirNode(_, subdirs, _) = parent_node {
|
||||
subdirs.push(subdir);
|
||||
}
|
||||
} else {
|
||||
let name = path.file_name();
|
||||
let name = name.to_str().unwrap().to_owned();
|
||||
if name.ends_with(".java") {
|
||||
if let DirNode(_, _, contents) = parent_node {
|
||||
contents.push(FileNode(path.path()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
|
@ -9,11 +9,11 @@ pub struct Config {
|
|||
|
||||
pub static CONFIG: OnceLock<Config> = OnceLock::new();
|
||||
|
||||
pub fn get_config() -> &'static Config {
|
||||
pub fn config() -> &'static Config {
|
||||
CONFIG.get_or_init(|| {
|
||||
let user_home = home::home_dir().unwrap();
|
||||
Config {
|
||||
cache_location: format!("{}/jargo/repo", user_home.to_str().unwrap()).into(),
|
||||
cache_location: format!("{}/jargo/repo", user_home.to_str().unwrap()).into(),//TODO make '.jargo'
|
||||
user_home,
|
||||
maven_central: "https://repo.maven.apache.org/maven2".into()
|
||||
}
|
||||
|
|
|
|||
137
src/deploader.rs
137
src/deploader.rs
|
|
@ -10,8 +10,9 @@ use sha1::{Digest, Sha1};
|
|||
use strong_xml::XmlRead;
|
||||
|
||||
use crate::Artifact;
|
||||
use crate::config::get_config;
|
||||
use crate::config::config;
|
||||
use crate::pom::model::Pom;
|
||||
use colored::Colorize;
|
||||
|
||||
pub fn load_artifacts(artifacts: &[Artifact]) -> Result<(), Error> {
|
||||
for art in artifacts {
|
||||
|
|
@ -21,37 +22,22 @@ pub fn load_artifacts(artifacts: &[Artifact]) -> Result<(), Error> {
|
|||
}
|
||||
|
||||
pub fn load_artifact(art: &Artifact) -> Result<(), Error> {
|
||||
let artifact_path = format!("{}/{}/{}",
|
||||
art.group.replace(".", "/"),
|
||||
art.name,
|
||||
art.version,
|
||||
);
|
||||
let local_artifact_loc = format!("{}/{}", get_config().cache_location, artifact_path);
|
||||
let artifact_path = format!("{}/{}/{}", art.group.replace(".", "/"), art.name, art.version);
|
||||
|
||||
// check/create artifact directory
|
||||
let local_artifact_loc = format!("{}/{}", config().cache_location, artifact_path);
|
||||
if !exists(&local_artifact_loc) {
|
||||
fs::create_dir_all(&local_artifact_loc)?;
|
||||
}
|
||||
|
||||
// download remote jar if not in cache and check its SHA-1 checksum
|
||||
let local_artifact_jar_path = format!("{}/{}-{}.jar", local_artifact_loc, art.name, art.version);
|
||||
if !exists(&local_artifact_jar_path) {
|
||||
load_verify_artifact_jar(art, &artifact_path, &local_artifact_jar_path)?;
|
||||
download_verify_artifact_jar(art, &artifact_path, &local_artifact_jar_path)?;
|
||||
}
|
||||
|
||||
let local_artifact_pom_path = format!("{}/{}-{}.pom", local_artifact_loc, art.name, art.version);
|
||||
let pom_xml = if !exists(&local_artifact_pom_path) {
|
||||
// download pom
|
||||
let remote_artifact_pom_url = format!("{}/{}/{}-{}.pom", get_config().maven_central, artifact_path, art.name, art.version);
|
||||
println!("Downloading {}", remote_artifact_pom_url);
|
||||
let body = reqwest::blocking::get(&remote_artifact_pom_url)?.text().unwrap();
|
||||
println!("Downloaded {}", remote_artifact_pom_url);
|
||||
write_text(&local_artifact_pom_path, &body)?;
|
||||
body
|
||||
} else {
|
||||
// read local pom file
|
||||
let mut file = File::open(local_artifact_pom_path)?;
|
||||
let mut contents = String::new();
|
||||
file.read_to_string(&mut contents)?;
|
||||
contents
|
||||
};
|
||||
// download remote pom if not in cache //TODO check its SHA-1 checksum
|
||||
let pom_xml = download_verify_pom(art, artifact_path, local_artifact_loc)?;
|
||||
|
||||
// parse pom file
|
||||
let pom = Pom::from_str(&pom_xml).unwrap();
|
||||
|
|
@ -61,50 +47,91 @@ pub fn load_artifact(art: &Artifact) -> Result<(), Error> {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
fn load_verify_artifact_jar(art: &Artifact, artifact_path: &str, local_artifact_jar_path: &str) -> Result<(), Error> {
|
||||
let remote_artifact_jar_url = format!("{}/{}/{}-{}.jar", get_config().maven_central, artifact_path, art.name, art.version);
|
||||
fn download_verify_pom(art: &Artifact, artifact_path: String, local_artifact_loc: String) -> Result<String, Error> {
|
||||
let local_artifact_pom_path = &format!("{}/{}-{}.pom", local_artifact_loc, art.name, art.version);
|
||||
let remote_artifact_pom_url = format!("{}/{}/{}-{}.pom", config().maven_central, artifact_path, art.name, art.version);
|
||||
let pom_xml = if !exists(local_artifact_pom_path) {
|
||||
println!("{} {}", "Downloading".green(), remote_artifact_pom_url);
|
||||
let body = reqwest::blocking::get(&remote_artifact_pom_url)?.text().unwrap();
|
||||
println!("{} {}", "Downloaded".green(), remote_artifact_pom_url);
|
||||
write_text(local_artifact_pom_path, &body)?;
|
||||
body
|
||||
} else {
|
||||
read_file_to_string(local_artifact_pom_path)?
|
||||
};
|
||||
|
||||
println!("Downloading {}", remote_artifact_jar_url);
|
||||
let local_artifact_pom_sha1_path = format!("{}.sha1", local_artifact_pom_path);
|
||||
|
||||
// verify jarfile with SHA1 checksum (which is hex encoded)
|
||||
let checksum = hex::decode(
|
||||
if !exists(&local_artifact_pom_sha1_path) {
|
||||
download_checksum(&remote_artifact_pom_url, &local_artifact_pom_sha1_path)?
|
||||
} else {
|
||||
read_file_to_bytes(local_artifact_pom_sha1_path)?
|
||||
})?;
|
||||
let validated = validate_checksum_text(&pom_xml, checksum);
|
||||
if !validated {
|
||||
Err(anyhow!("SHA1 checksum for {} is not valid", remote_artifact_pom_url))
|
||||
} else { Ok(pom_xml) }
|
||||
}
|
||||
|
||||
|
||||
/// Download jar from remote repo and check its signature
|
||||
/// For now it's a blocking call, because async and recursion add unwanted complexity/I don't understand that
|
||||
/// TODO add progress bar
|
||||
fn download_verify_artifact_jar(art: &Artifact, artifact_path: &str, local_artifact_jar_path: &str) -> Result<(), Error> {
|
||||
let remote_artifact_jar_url = format!("{}/{}/{}-{}.jar", config().maven_central, artifact_path, art.name, art.version);
|
||||
|
||||
println!("{} {}", "Downloading".green(), remote_artifact_jar_url);
|
||||
let jar = reqwest::blocking::get(&remote_artifact_jar_url)?.bytes().unwrap();
|
||||
println!("Downloaded {}", remote_artifact_jar_url);
|
||||
write_bytes(&local_artifact_jar_path, jar.clone())?;
|
||||
println!("{} {}", "Downloaded".green(), remote_artifact_jar_url);
|
||||
write_bytes_to_file(&local_artifact_jar_path, &jar)?;
|
||||
|
||||
let local_artifact_jar_sha1_path = format!("{}.sha1", local_artifact_jar_path);
|
||||
|
||||
|
||||
// verify jarfile with SHA1 checksum
|
||||
// verify jarfile with SHA1 checksum (which is hex encoded)
|
||||
let checksum = hex::decode(
|
||||
if !exists(&local_artifact_jar_sha1_path) {
|
||||
let remote_artifact_jar_sha1_url = format!("{}.sha1", remote_artifact_jar_url);
|
||||
println!("{}", remote_artifact_jar_sha1_url);
|
||||
let jar_checksum = reqwest::blocking::get(&remote_artifact_jar_sha1_url)?.bytes().unwrap();
|
||||
write_bytes(&local_artifact_jar_sha1_path, jar_checksum.clone())?;
|
||||
jar_checksum.to_vec()
|
||||
download_checksum(&remote_artifact_jar_url, &local_artifact_jar_sha1_path)?
|
||||
} else {
|
||||
let mut file = File::open(local_artifact_jar_sha1_path)?;
|
||||
let mut contents = Vec::new();
|
||||
file.read_to_end(&mut contents)?;
|
||||
contents
|
||||
read_file_to_bytes(local_artifact_jar_sha1_path)?
|
||||
})?;
|
||||
|
||||
let mut hasher = Sha1::new();
|
||||
hasher.update(&jar.to_vec());
|
||||
let result = hasher.finalize();
|
||||
let validated = result[..] == checksum;
|
||||
|
||||
let validated = validate_checksum_bytes(&jar, checksum);
|
||||
if !validated {
|
||||
Err(anyhow!("SHA1 checksum for {} is not valid", remote_artifact_jar_url))
|
||||
} else { Ok(()) }
|
||||
}
|
||||
|
||||
|
||||
fn download_checksum(remote_artifact_url: &String, local_artifact_jar_sha1_path: &String) -> Result<Vec<u8>, Error> {
|
||||
let remote_artifact_jar_sha1_url = format!("{}.sha1", remote_artifact_url);
|
||||
let jar_checksum = reqwest::blocking::get(&remote_artifact_jar_sha1_url)?.bytes().unwrap();
|
||||
write_bytes_to_file(&local_artifact_jar_sha1_path, &jar_checksum)?;
|
||||
Ok(jar_checksum.to_vec())
|
||||
}
|
||||
|
||||
fn validate_checksum_bytes(jar: &Bytes, checksum: Vec<u8>) -> bool {
|
||||
let mut hasher = Sha1::new();
|
||||
hasher.update(&jar.to_vec());
|
||||
let result = hasher.finalize();
|
||||
result[..] == checksum
|
||||
}
|
||||
|
||||
fn validate_checksum_text(text: &str, checksum: Vec<u8>) -> bool {
|
||||
let mut hasher = Sha1::new();
|
||||
hasher.update(text.as_bytes());
|
||||
let result = hasher.finalize();
|
||||
result[..] == checksum
|
||||
}
|
||||
|
||||
fn exists(path: &str) -> bool {
|
||||
Path::new(path).exists()
|
||||
}
|
||||
|
||||
|
||||
fn write_bytes(jar_path: &str, bytes: Bytes) -> Result<(), Error> {
|
||||
fn write_bytes_to_file(jar_path: &str, bytes: &Bytes) -> Result<(), Error> {
|
||||
let mut file = File::create(jar_path)?;
|
||||
file.write_all(&bytes)?;
|
||||
file.write_all(bytes)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
|
@ -112,4 +139,18 @@ fn write_text(path: &str, contents: &String) -> Result<(), Error> {
|
|||
let mut file = File::create(path)?;
|
||||
file.write(contents.as_bytes())?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn read_file_to_string(local_artifact_pom_path: &str) -> Result<String, Error> {
|
||||
let mut file = File::open(local_artifact_pom_path)?;
|
||||
let mut contents = String::new();
|
||||
file.read_to_string(&mut contents)?;
|
||||
Ok(contents)
|
||||
}
|
||||
|
||||
fn read_file_to_bytes(local_artifact_jar_sha1_path: String) -> Result<Vec<u8>, Error> {
|
||||
let mut file = File::open(local_artifact_jar_sha1_path)?;
|
||||
let mut contents = Vec::new();
|
||||
file.read_to_end(&mut contents)?;
|
||||
Ok(contents)
|
||||
}
|
||||
19
src/lib.rs
19
src/lib.rs
|
|
@ -1,4 +1,5 @@
|
|||
use std::fs;
|
||||
use std::path::Path;
|
||||
|
||||
use anyhow::{anyhow, Error};
|
||||
use toml::{Table, Value};
|
||||
|
|
@ -6,6 +7,7 @@ use toml::{Table, Value};
|
|||
pub mod pom;
|
||||
mod config;
|
||||
pub mod deploader;
|
||||
pub mod compile;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Project {
|
||||
|
|
@ -14,6 +16,7 @@ pub struct Project {
|
|||
pub version: String,
|
||||
pub dependencies: Vec<Artifact>,
|
||||
pub test_dependencies: Vec<Artifact>,
|
||||
pub project_root: String,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
|
|
@ -51,19 +54,23 @@ impl Artifact {
|
|||
}
|
||||
|
||||
pub fn load_project(jargo_file: Option<&str>) -> Result<Project, Error> {
|
||||
let project_table = fs::read_to_string(jargo_file.unwrap_or("./Jargo.toml"))?.parse::<Table>()?;
|
||||
let jargo = Path::new(jargo_file.unwrap_or("./Jargo.toml"));
|
||||
|
||||
let project_table = fs::read_to_string(jargo)?.parse::<Table>()?;
|
||||
let package = project_table.get("package").expect("package info missing");
|
||||
|
||||
let dependencies = get_dependencies(project_table.get("dependencies"))?;
|
||||
|
||||
let test_dependencies = get_dependencies(project_table.get("test-dependencies"))?;
|
||||
|
||||
|
||||
Ok(Project {
|
||||
group: package.get("group").unwrap().to_string(),
|
||||
name: package.get("name").unwrap().to_string(),
|
||||
version: package.get("version").unwrap().to_string(),
|
||||
group: strip_first_last(package.get("group").unwrap().to_string()),
|
||||
name: strip_first_last(package.get("name").unwrap().to_string()),
|
||||
version: strip_first_last(package.get("version").unwrap().to_string()),
|
||||
dependencies,
|
||||
test_dependencies,
|
||||
project_root: jargo.parent().map(Path::to_str).unwrap().expect(&format!("projectroot {:?} not usable", jargo)).into(),
|
||||
})
|
||||
}
|
||||
|
||||
|
|
@ -80,4 +87,8 @@ fn get_dependencies(table: Option<&Value>) -> Result<Vec<Artifact>, Error> {
|
|||
}
|
||||
}
|
||||
Ok(dependencies)
|
||||
}
|
||||
|
||||
fn strip_first_last(text: String) -> String{
|
||||
text[1..text.len()-1].into()
|
||||
}
|
||||
|
|
@ -3,5 +3,6 @@ use anyhow::Error;
|
|||
fn main() -> anyhow::Result<(), Error> {
|
||||
let project = jargo::load_project(Some("tests/sample_project/Jargo.toml"))?;
|
||||
jargo::deploader::load_artifacts(&project.test_dependencies)?;
|
||||
jargo::compile::run(&project)?;
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,7 @@
|
|||
package nl.sander.sample;
|
||||
|
||||
public class SampleMain {
|
||||
public static void main(String[] args) {
|
||||
System.out.println(Sample.getTheNumber());
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Reference in a new issue