commit ad31929f7b024ad0ba1fdee6bdcbb547a615860c Author: Able <abl3theabove@gmail.com> Date: Wed Mar 19 17:29:08 2025 -0500 Initial rlbuild commit. diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..892b5c0 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +target/* +out/* +cache/* \ No newline at end of file diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..ee31720 --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,33 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "foldhash" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" + +[[package]] +name = "hashbrown" +version = "0.15.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289" +dependencies = [ + "foldhash", +] + +[[package]] +name = "rlbuild" +version = "0.1.0" +dependencies = [ + "rlisp_library", +] + +[[package]] +name = "rlisp_library" +version = "0.1.0" +source = "git+https://git.ablecorp.us/able/rlisp.git#a1cef24e513a89f8d4de8a1cb3ede546bc72aeca" +dependencies = [ + "hashbrown", +] diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..6de2e02 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,7 @@ +[package] +name = "rlbuild" +version = "0.1.0" +edition = "2024" + +[dependencies] +rlisp_library = { git="https://git.ablecorp.us/able/rlisp.git"} \ No newline at end of file diff --git a/assets/pkg.rl b/assets/pkg.rl new file mode 100644 index 0000000..b81b6a2 --- /dev/null +++ b/assets/pkg.rl @@ -0,0 +1,38 @@ +;; ! hbpkg.rli ! ;; +;; package name & version, non-optional. +(package + "example" + ;; Major Minor Patch + (version 1 0 1) + :authors ("AbleTheAbove") + + :tags ("a" "b" "c") + + (deps + ;; first item after name is always source. + (lily "https://git.ablecorp.us/lily-org/lily.git" + ;; semantic version. min & max both optional. + ;; if version is string, exact match will be used. + ;; if version is "0.x", then the latest of "0.x.y" will be used + :version (:min "0.0.2" :max "0.0.5") + ) + (libexample "./src/lib.hb") + (third-dep + ;; Repo lookup refers to the rlrepo project + (repo :core "third-dep") + + ) + + + + + + + (some_clib "https://example.org/clib.git" + :commit "ABCDEFGH" ;; optional + :branch "trunk" ;; optional + ;; optional, probably not required if specifying commit. + :hash "abcdefghijklmnopqrstuvwxyz123456" + ) +) + ) \ No newline at end of file diff --git a/assets/system.rl b/assets/system.rl new file mode 100644 index 0000000..e69de29 diff --git a/readme.md b/readme.md new file mode 100644 index 0000000..d8aae08 --- /dev/null +++ b/readme.md @@ -0,0 +1,9 @@ +# RLBuild + + +## Repos +rlbuild was built with the ability to point to multiple repos to fetch packages + +repos have a url in the format + +repos.ablecorp.us/<repo_name>/<?sub_repo_name>/<pkg_name> \ No newline at end of file diff --git a/src/environ/ext.rs b/src/environ/ext.rs new file mode 100644 index 0000000..7b09964 --- /dev/null +++ b/src/environ/ext.rs @@ -0,0 +1,25 @@ +use rlisp_library::*; + + +pub fn extend_environ<'a>(mut env: Environ<'a>) -> Environ<'a> { + env.data.insert( + "system-install".to_string(), + Expr::Func(|_args: &[Expr]| -> Result<Expr, RispError> { + println!("probably the kernel is installed by now"); + Ok(Expr::Bool(true)) + }), + ); + + + env.data.insert( + "use-repo-path".to_string(), + Expr::Func(|_args: &[Expr]| -> Result<Expr, RispError> { + let repo_name = _args.first().unwrap(); + + println!("{:?}", _args); + Ok(Expr::Bool(true)) + }), + ); + + env +} \ No newline at end of file diff --git a/src/environ/mod.rs b/src/environ/mod.rs new file mode 100644 index 0000000..f5eeb3d --- /dev/null +++ b/src/environ/mod.rs @@ -0,0 +1,14 @@ +use rlisp_library::*; + +mod ext; +mod old; + +pub fn extend_environ<'a>(mut env: Environ<'a>) -> Environ<'a> { + let mut env = ext::extend_environ(env); + let mut env = old::extend_environ(env); + + + + + env +} \ No newline at end of file diff --git a/src/environ/old.rs b/src/environ/old.rs new file mode 100644 index 0000000..74a5337 --- /dev/null +++ b/src/environ/old.rs @@ -0,0 +1,324 @@ +use std::{ + fs::{self, File}, + io::Write, +}; + +use rlisp_library::{ + Environ, + Expr::{self, Bool}, + RispError, default_env, parse_eval, +}; + + + +pub +fn extend_environ<'a>(mut env: Environ<'a>) -> Environ<'a> { + env.data.insert( + "quit".to_string(), + Expr::Func(|_args: &[Expr]| -> Result<Expr, RispError> { + // TODO: let this function take in arguments + println!("Exiting."); + std::process::exit(0); + #[allow(unreachable_code)] + Err(RispError::Reason("Cannot exit process".to_string())) + }), + ); + + env.data.insert( + "print".to_string(), + Expr::Func(|args: &[Expr]| -> Result<Expr, RispError> { + let mut stri = String::new(); + for arg in args { + let fmted = match arg { + Expr::Str(string) => { + let string = string.clone(); + // string.pop(); + // string.remove(0); + + format!("{}", string) + } + _ => { + format!("{}", arg) + } + }; + + stri.push_str(&fmted); + if args.len() > 1 { + stri.push_str(" ") + } + } + print!("{}", stri); + + Ok(Expr::Bool(true)) + }), + ); + + env.data.insert( + "println".to_string(), + Expr::Func(|args: &[Expr]| -> Result<Expr, RispError> { + let mut stri = String::new(); + for arg in args { + let fmted = match arg { + Expr::Str(string) => { + let string = string.clone(); + // string.pop(); + // string.remove(0); + + format!("{}", string) + } + _ => { + format!("{}", arg) + } + }; + + stri.push_str(&fmted); + if args.len() > 1 { + stri.push_str(" ") + } + } + println!("{}", stri); + + Ok(Expr::Bool(true)) + }), + ); + + env.data.insert( + "use-repo".to_string(), + Expr::Func(|args: &[Expr]| -> Result<Expr, RispError> { + let repo_name = &args[0].clone(); + let mut repo_name_str = repo_name.to_string(); + repo_name_str.remove(0); + + let repo_url = &args[1].clone(); + let use_sources = &args[2].clone(); + + let msg = match use_sources { + Bool(b) => match b { + true => "build_from_src = true", + false => "build_from_src = false", + }, + _ => { + panic!("AHHH"); + } + }; + + let path = format!("out/system/repos"); + fs::create_dir_all(path).unwrap(); + + let path = format!("out/system/repos/{}.repo", repo_name_str); + println!("repo name {} repo url {} {}", repo_name, repo_url, msg); + + let mut file = File::create(path).unwrap(); + let msg = format!("url = {}\n{}", repo_url, msg); + let _ = file.write(msg.as_bytes()); + + Ok(Expr::Bool(true)) + }), + ); + env.data.insert( + "bootloader-install".to_string(), + Expr::Func(|_args: &[Expr]| -> Result<Expr, RispError> { + // let loader_name = &args[1].clone(); + // let mut loader_name_str = loader_name.to_string(); + // loader_name_str.remove(0); + + let path = format!("out/boot/limine"); + fs::create_dir_all(path).unwrap(); + + // let path = format!("out/boot/limine/config.rl"); + // let mut file = File::create(path).unwrap(); + // let _ = file.write_all(b"()"); + + let path = format!("out/boot/limine/limine.conf"); + let mut file = File::create(path).unwrap(); + let _ = file.write_all(b""); + + let path = format!("out/boot/limine/BOOTX64.EFI"); + let mut x64_file = File::create(&path).unwrap(); + std::fs::copy("limine/BOOTX64.EFI", &path).unwrap(); // Copy foo.txt to bar.txt + + let path = format!("out/boot/limine/BOOTAA64.EFI"); + let mut x64_file = File::create(&path).unwrap(); + std::fs::copy("limine/BOOTAA64.EFI", &path).unwrap(); // Copy foo.txt to bar.txt + + Ok(Expr::Bool(true)) + }), + ); + + env.data.insert( + "pkg-install".to_string(), + Expr::Func(|args: &[Expr]| -> Result<Expr, RispError> { + let repo_name = &args[0].clone(); + let pkg_name = &args[1].clone(); + let mut pkg_name_str = pkg_name.to_string(); + pkg_name_str.remove(0); + + println!( + "installing package {} from repo {}", + pkg_name_str, repo_name + ); + let path = format!("out/programs/{}", pkg_name_str); + fs::create_dir_all(path).unwrap(); + + let path = format!("out/programs/{}/src", pkg_name_str); + fs::create_dir_all(path).unwrap(); + + // If package is from :src repo, copy source files + if repo_name.to_string() == ":src" { + // Try original name first + let src_path = format!("sysdata/programs/{}", pkg_name_str); + println!("Checking source path: {}", src_path); + + // If not found, try with dashes converted to underscores + let underscored_name = pkg_name_str.replace('-', "_"); + let src_path_alt = format!("sysdata/programs/{}", underscored_name); + + let (found_path, found_name) = if fs::metadata(&src_path).is_ok() { + (src_path, pkg_name_str.clone()) + } else if fs::metadata(&src_path_alt).is_ok() { + println!("Found source directory with underscored name at {}", src_path_alt); + (src_path_alt, underscored_name) + } else { + return Err(RispError::Reason(format!( + "Source directory not found for package {} at {} or {}", + pkg_name_str, src_path, src_path_alt + ))); + }; + + println!("Found source directory at {}", found_path); + // Copy all files from src directory + for entry in fs::read_dir(&found_path).unwrap() { + let entry = entry.unwrap(); + let path = entry.path(); + if path.is_file() { + let file_name = path.file_name().unwrap().to_str().unwrap(); + let dest_path = format!("out/programs/{}/{}", pkg_name_str, file_name); + println!("Copying {} to {}", path.display(), dest_path); + fs::copy(&path, &dest_path).unwrap(); + } + } + // Copy contents of src directory if it exists + let src_src_path = format!("sysdata/programs/{}/src", found_name); + println!("Checking src directory at {}", src_src_path); + if fs::metadata(&src_src_path).is_ok() { + println!("Found src directory at {}", src_src_path); + for entry in fs::read_dir(&src_src_path).unwrap() { + let entry = entry.unwrap(); + let path = entry.path(); + if path.is_file() { + let file_name = path.file_name().unwrap().to_str().unwrap(); + let dest_path = format!("out/programs/{}/src/{}", pkg_name_str, file_name); + println!("Copying {} to {}", path.display(), dest_path); + fs::copy(&path, &dest_path).unwrap(); + } + } + } + + // Check if meta.rl exists in source, if not generate one + let meta_src_path = format!("sysdata/programs/{}/meta.rl", found_name); + let meta_dest_path = format!("out/programs/{}/meta.rl", pkg_name_str); + + if !fs::metadata(&meta_src_path).is_ok() { + println!("Generating meta.rl for {}", pkg_name_str); + let mut file = File::create(&meta_dest_path).unwrap(); + let meta_content = format!( + "(package \"{}\"\n (version 1 0 0)\n :authors (\"\")\n :tags ()\n (deps))\n", + pkg_name_str + ); + file.write_all(meta_content.as_bytes()).unwrap(); + } + } + + let path = format!("out/programs/{}/app.axe", pkg_name_str); + let mut file = File::create(path).unwrap(); + let _ = file.write_all(b""); + + Ok(Expr::Bool(true)) + }), + ); + + env.data.insert( + "pkg-configure".to_string(), + Expr::Func(|args: &[Expr]| -> Result<Expr, RispError> { + let pkg_name = &args[0].clone(); + let mut pkg_name_str = pkg_name.to_string(); + pkg_name_str.remove(0); + + println!("installing package {}.", pkg_name_str); + let path = format!("out/programs/{}/config.rl", pkg_name_str); + + println!("configuring package {}", pkg_name_str); + // TODO: build the code with the hblang compiler. + // TODO: use the meta.rli to map dependencies. + let mut file = File::create(path).unwrap(); + + + let _ = file.write_all(b"()"); + + Ok(Expr::Bool(true)) + }), + ); + + env.data.insert( + "drivers".to_string(), + Expr::Func(|args: &[Expr]| -> Result<Expr, RispError> { + let mut stri = String::new(); + for arg in args { + let fmted = match arg { + Expr::Str(string) => { + let string = string.clone(); + format!("{}", string) + } + _ => { + format!("{}", arg) + } + }; + + stri.push_str(&fmted); + if args.len() > 1 { + stri.push_str(" ") + } + } + println!("Drivers {}", stri); + + Ok(Expr::Bool(true)) + }), + ); + env.data.insert( + "services".to_string(), + Expr::Func(|args: &[Expr]| -> Result<Expr, RispError> { + let mut stri = String::new(); + for arg in args { + let fmted = match arg { + Expr::Str(string) => { + let string = string.clone(); + format!("{}", string) + } + _ => { + format!("{}", arg) + } + }; + + stri.push_str(&fmted); + if args.len() > 1 { + stri.push_str(" ") + } + } + println!("Services {}", stri); + + Ok(Expr::Bool(true)) + }), + ); + + env.data.insert( + "reincarnation-server".to_string(), + Expr::Func(|args: &[Expr]| -> Result<Expr, RispError> { + let reinc = &args[0].clone(); + println!("Reincarnation Server {}", reinc); + + Ok(Expr::Bool(true)) + }), + ); + + env +} diff --git a/src/environ/pkg_environ.rs b/src/environ/pkg_environ.rs new file mode 100644 index 0000000..87f7395 --- /dev/null +++ b/src/environ/pkg_environ.rs @@ -0,0 +1,182 @@ +use std::{fs, File}; +use rlisp_library::{Environ, Expr, RispError, parse_eval}; +use crate::packages::{Package, Dependency}; + +pub fn extend_environ<'a>(mut env: Environ<'a>) -> Environ<'a> { + // Function to create a package + env.data.insert( + "package".to_string(), + Expr::Func(|args: &[Expr]| -> Result<Expr, RispError> { + if args.len() < 5 { + return Err(RispError::Reason("package requires name, authors, version, dependencies, and build command".to_string())); + } + + let name = match &args[0] { + Expr::Str(s) => s.clone(), + _ => return Err(RispError::Reason("package name must be a string".to_string())), + }; + + let authors = match &args[1] { + Expr::List(l) => l.iter() + .map(|a| match a { + Expr::Str(s) => s.clone(), + _ => return Err(RispError::Reason("authors must be strings".to_string())), + }) + .collect(), + _ => return Err(RispError::Reason("authors must be a list".to_string())), + }; + + let version = match &args[2] { + Expr::Str(s) => s.clone(), + _ => return Err(RispError::Reason("version must be a string".to_string())), + }; + + let dependencies = match &args[3] { + Expr::List(l) => l.iter() + .map(|d| match d { + Expr::List(dep_list) => { + if dep_list.len() != 2 { + return Err(RispError::Reason("dependency must have type and source".to_string())); + } + let dep_type = match &dep_list[0] { + Expr::Symbol(s) => s.clone(), + _ => return Err(RispError::Reason("dependency type must be a symbol".to_string())), + }; + let source = match &dep_list[1] { + Expr::Str(s) => s.clone(), + _ => return Err(RispError::Reason("dependency source must be a string".to_string())), + }; + match dep_type.as_str() { + "library" => Ok(Dependency::Library { source }), + "binary" => Ok(Dependency::Binary { source }), + "source" => Ok(Dependency::Source { source }), + _ => Err(RispError::Reason(format!("Unknown dependency type: {}", dep_type))), + } + } + _ => Err(RispError::Reason("dependency must be a list".to_string())), + }) + .collect::<Result<Vec<Dependency>, RispError>>()?, + _ => return Err(RispError::Reason("dependencies must be a list".to_string())), + }; + + let build_command = match &args[4] { + Expr::Str(s) => s.clone(), + _ => return Err(RispError::Reason("build command must be a string".to_string())), + }; + + let package = Package::new(name, authors, version, dependencies, build_command); + + // Store the package in the environment + env.data.insert( + "current_package".to_string(), + Expr::Custom(Box::new(package)) + ); + + Ok(Expr::Bool(true)) + }), + ); + + // Function to get the current package + env.data.insert( + "get-package".to_string(), + Expr::Func(|_args: &[Expr]| -> Result<Expr, RispError> { + match env.data.get("current_package") { + Some(pkg) => Ok(pkg.clone()), + None => Err(RispError::Reason("No package is currently defined".to_string())), + } + }), + ); + + // Package installation function + env.data.insert( + "pkg-install".to_string(), + Expr::Func(|args: &[Expr]| -> Result<Expr, RispError> { + if args.len() != 2 { + return Err(RispError::Reason("pkg-install requires repo name and package name".to_string())); + } + + let repo_name = match &args[0] { + Expr::Str(s) => s.clone(), + _ => return Err(RispError::Reason("repo name must be a string".to_string())), + }; + + let pkg_name = match &args[1] { + Expr::Str(s) => s.clone(), + _ => return Err(RispError::Reason("package name must be a string".to_string())), + }; + + println!("Installing package {} from repo {}", pkg_name, repo_name); + + // Create package directory structure + let pkg_dir = format!("out/programs/{}", pkg_name); + fs::create_dir_all(&pkg_dir).unwrap(); + fs::create_dir_all(format!("{}/src", pkg_dir)).unwrap(); + + // If package is from :src repo, copy source files + if repo_name == ":src" { + // Try original name first + let src_path = format!("sysdata/programs/{}", pkg_name); + let underscored_name = pkg_name.replace('-', "_"); + let src_path_alt = format!("sysdata/programs/{}", underscored_name); + + let (found_path, found_name) = if fs::metadata(&src_path).is_ok() { + (src_path, pkg_name.clone()) + } else if fs::metadata(&src_path_alt).is_ok() { + println!("Found source directory with underscored name at {}", src_path_alt); + (src_path_alt, underscored_name) + } else { + return Err(RispError::Reason(format!( + "Source directory not found for package {} at {} or {}", + pkg_name, src_path, src_path_alt + ))); + }; + + // Copy all files from source directory + for entry in fs::read_dir(&found_path).unwrap() { + let entry = entry.unwrap(); + let path = entry.path(); + if path.is_file() { + let file_name = path.file_name().unwrap().to_str().unwrap(); + let dest_path = format!("{}/{}", pkg_dir, file_name); + println!("Copying {} to {}", path.display(), dest_path); + fs::copy(&path, &dest_path).unwrap(); + } + } + + // Copy contents of src directory if it exists + let src_src_path = format!("sysdata/programs/{}/src", found_name); + if fs::metadata(&src_src_path).is_ok() { + for entry in fs::read_dir(&src_src_path).unwrap() { + let entry = entry.unwrap(); + let path = entry.path(); + if path.is_file() { + let file_name = path.file_name().unwrap().to_str().unwrap(); + let dest_path = format!("{}/src/{}", pkg_dir, file_name); + println!("Copying {} to {}", path.display(), dest_path); + fs::copy(&path, &dest_path).unwrap(); + } + } + } + + // Read and evaluate meta.rl directly + let meta_path = format!("sysdata/programs/{}/meta.rl", found_name); + if let Ok(meta_content) = fs::read_to_string(&meta_path) { + println!("Evaluating meta.rl for package {}", pkg_name); + if let Err(e) = parse_eval(&meta_content, &mut env.clone()) { + println!("Warning: Failed to evaluate meta.rl: {}", e); + } + } else { + println!("Warning: No meta.rl found for package {}", pkg_name); + } + + // Create empty app.axe file + let app_path = format!("{}/app.axe", pkg_dir); + File::create(app_path).unwrap(); + } + + Ok(Expr::Bool(true)) + }), + ); + + env +} diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..94a7ceb --- /dev/null +++ b/src/main.rs @@ -0,0 +1,71 @@ +#![feature(slice_take)] +#![allow(special_module_name)] + +use std::{ + fs::{self, File}, + io::Write, +}; + +use rlisp_library::{ + Environ, + Expr::{self, Bool}, + RispError, default_env, parse_eval, +}; + +mod packages; +mod environ; +use environ::extend_environ; + +fn main() { + let env = &mut default_env(); + let env = &mut extend_environ(env.clone()); + // let env = &mut + + let cfg = include_str!("../assets/system.rl"); + + let mut complete_exprs: Vec<String> = vec![]; + let mut left_parens = 0; + let mut idx_of_first_left_paran = 0; + + for (i, character) in cfg.chars().enumerate() { + if character == '(' { + if left_parens == 0 { + idx_of_first_left_paran = i; + } + left_parens += 1 + } + + if character == ')' { + left_parens -= 1; + if left_parens == 0 { + let idx_of_last_right_paran = i + 1; + + complete_exprs + .push(cfg[idx_of_first_left_paran..idx_of_last_right_paran].to_string()); + } + } + } + + if left_parens != 0 { + panic!("unmatched parens. Good luck finding them!"); + } + + // TODO: Mount the disk image here. + + for expr in complete_exprs { + match parse_eval(expr, env) { + Ok(_res) => {} + Err(e) => { + panic!("{:?}", e) + } + } + } + + let path = format!("out/system/config.rl"); + + let mut file = File::create(path).unwrap(); + + let _ = file.write_all(cfg.as_bytes()); + + // TODO: unmount the disk image here. +} diff --git a/src/packages.rs b/src/packages.rs new file mode 100644 index 0000000..77fefab --- /dev/null +++ b/src/packages.rs @@ -0,0 +1,27 @@ +#[derive(Debug, Clone)] +pub struct Package { + pub name: String, + pub authors: Vec<String>, + pub version: String, + pub dependencies: Vec<Dependency>, + pub build_command: String, +} + +#[derive(Debug, Clone)] +pub enum Dependency { + Library { source: String }, + Binary { source: String }, + Source { source: String }, +} + +impl Package { + pub fn new(name: String, authors: Vec<String>, version: String, dependencies: Vec<Dependency>, build_command: String) -> Self { + Self { + name, + authors, + version, + dependencies, + build_command, + } + } +}