201 lines
6.4 KiB
Rust
201 lines
6.4 KiB
Rust
use std::process::{Command, Stdio};
|
||
use std::{
|
||
env, fs,
|
||
path::{Path, PathBuf},
|
||
};
|
||
|
||
use crate::templates::{Dsa, Dsc, Template};
|
||
|
||
mod templates;
|
||
|
||
/// Run a command and exit on failure.
|
||
fn run(cmd: &mut Command) {
|
||
let status = cmd.status().expect("failed to execute command");
|
||
if !status.success() {
|
||
std::process::exit(1);
|
||
}
|
||
}
|
||
|
||
fn main() {
|
||
// Very small CLI – only three sub‑commands.
|
||
let args: Vec<String> = env::args().collect();
|
||
if args.len() < 2 {
|
||
eprintln!("Usage: dsx-build <new|build|package> [options]");
|
||
std::process::exit(1);
|
||
}
|
||
match args[1].as_str() {
|
||
"new" => cmd_new(&args[2..]),
|
||
"build" => cmd_build(),
|
||
"package" => todo!("Package manager stub – not implemented yet."),
|
||
_ => {
|
||
eprintln!("Unknown command: {}", args[1]);
|
||
std::process::exit(1);
|
||
}
|
||
}
|
||
}
|
||
|
||
// ---------- new project ----------------------------------------------------
|
||
fn cmd_new(args: &[String]) {
|
||
let mut lang = "dsa";
|
||
for i in 0..args.len() {
|
||
if args[i] == "--lang" && i + 1 < args.len() {
|
||
lang = &args[i + 1];
|
||
}
|
||
}
|
||
|
||
let lib = args.contains(&"--lib".to_string());
|
||
|
||
// Determine project root: a subdirectory named after the supplied --name argument.
|
||
let mut name_opt = None;
|
||
for i in 0..args.len() {
|
||
if args[i] == "--name" && i + 1 < args.len() {
|
||
name_opt = Some(&args[i + 1]);
|
||
break;
|
||
}
|
||
}
|
||
|
||
let project_name = match name_opt {
|
||
Some(name) => name.to_string(),
|
||
None => {
|
||
eprintln!("Error: --name argument required");
|
||
std::process::exit(1);
|
||
}
|
||
};
|
||
|
||
let cwd = env::current_dir().unwrap();
|
||
let src_path = cwd.join(&project_name).join("src");
|
||
fs::create_dir_all(&src_path).expect("Failed to create project directory");
|
||
|
||
match lang {
|
||
"dsa" => {
|
||
// Minimal DSA binary template.
|
||
let path = src_path.join(format!("main.dsa"));
|
||
|
||
let template = Dsa::create(&project_name, lib);
|
||
|
||
fs::write(path, template).expect("Unable to write DSA file");
|
||
}
|
||
"dsc" => {
|
||
let path = src_path.join(format!("main.dsc"));
|
||
|
||
let template = Dsc::create(&project_name, lib);
|
||
|
||
fs::write(path, template).expect("Unable to write DSC file");
|
||
}
|
||
_ => {
|
||
eprintln!("Unsupported language: {}", lang);
|
||
std::process::exit(1);
|
||
}
|
||
}
|
||
|
||
fs::create_dir_all(src_path.join("lib")).expect("Failed to create lib directory");
|
||
fs::write(
|
||
src_path.join("lib/print.dsa"),
|
||
templates::create_print_lib(),
|
||
)
|
||
.expect("Failed to create print.dsa");
|
||
fs::write(
|
||
src_path.join("lib/maths.dsa"),
|
||
templates::create_maths_lib(),
|
||
)
|
||
.expect("Failed to create maths.dsa");
|
||
|
||
println!(
|
||
"Created new {} project in {}.",
|
||
lang,
|
||
src_path.parent().unwrap().display()
|
||
);
|
||
}
|
||
|
||
// ---------- build ----------------------------------------------------------
|
||
fn cmd_build() {
|
||
let cwd = env::current_dir().unwrap();
|
||
|
||
// Detect .dsc or .dsa files in current directory.
|
||
let mut has_dsc = false;
|
||
let mut has_dsa = false;
|
||
for entry in fs::read_dir(&cwd.join("src")).expect("unable to read dir") {
|
||
if let Ok(entry) = entry {
|
||
let path = entry.path();
|
||
if path.extension().and_then(|s| s.to_str()) == Some("dsc") {
|
||
has_dsc = true;
|
||
} else if path.extension().and_then(|s| s.to_str()) == Some("dsa") {
|
||
has_dsa = true;
|
||
}
|
||
}
|
||
}
|
||
|
||
if !has_dsc && !has_dsa {
|
||
eprintln!("No .dsc or .dsa source found in src directory.");
|
||
std::process::exit(1);
|
||
}
|
||
|
||
// Assemble main.dsa to a dsb binary.
|
||
println!("Assembling Project to a DSB binary...");
|
||
let build_dir = cwd.join("build");
|
||
fs::create_dir_all(&build_dir).expect("Failed to create build directory");
|
||
|
||
// Copy everything from `cwd/src` to the build directory.
|
||
fn copy_recursively(src: &Path, dst: &Path) {
|
||
if src.is_file() {
|
||
fs::create_dir_all(dst.parent().unwrap())
|
||
.expect("Failed to create parent directory");
|
||
fs::copy(src, dst).expect("Failed to copy file");
|
||
} else if src.is_dir() {
|
||
for entry in fs::read_dir(src).expect("Unable to read source dir") {
|
||
let entry = entry.expect("Failed to read entry");
|
||
let child_src = entry.path();
|
||
let child_dst = dst.join(entry.file_name());
|
||
copy_recursively(&child_src, &child_dst);
|
||
}
|
||
}
|
||
}
|
||
|
||
let src_dir = cwd.join("src");
|
||
if src_dir.exists() {
|
||
copy_recursively(&src_dir, &build_dir);
|
||
}
|
||
|
||
// Change current working directory to the build directory.
|
||
env::set_current_dir(&build_dir).expect("Failed to change to build directory");
|
||
|
||
if has_dsc {
|
||
println!("Compiling DSC to DSA...");
|
||
fn compile_recursive(path: &Path) {
|
||
if path.is_dir() {
|
||
for entry in fs::read_dir(path).expect("unable to read dir") {
|
||
let entry = entry.expect("failed to read entry");
|
||
compile_recursive(&entry.path());
|
||
}
|
||
} else if path.extension().and_then(|s| s.to_str()) == Some("dsc") {
|
||
let input_path = path;
|
||
let output_path = path.with_extension("dsa");
|
||
compiler::compile_file(&input_path, &output_path).unwrap_or_else(|e| {
|
||
eprintln!("Failed to compile {:?}: {}", input_path, e);
|
||
std::process::exit(1);
|
||
});
|
||
}
|
||
}
|
||
compile_recursive(&build_dir);
|
||
}
|
||
|
||
// Replace .dsc with .dsa only in include statements, recursively for each file.
|
||
let mut sed_cmd = Command::new("bash");
|
||
sed_cmd.args(&[
|
||
"-c",
|
||
&format!(
|
||
"find \"{}\" -type f -name '*.dsa' -exec sed -i '/^include/ s/\\.dsc/.dsa/g' {{}} +",
|
||
build_dir.display()
|
||
),
|
||
]);
|
||
run(&mut sed_cmd);
|
||
|
||
fs::create_dir_all(&cwd.join("artifacts")).expect("Failed to create build directory");
|
||
assembler::assemble_file("./main.dsa", "../artifacts/out.dsb").unwrap_or_else(|e| {
|
||
eprintln!("Failed to assemble {:?}: {}", "./main.dsa", e);
|
||
std::process::exit(1);
|
||
});
|
||
|
||
println!("Build finished. Binary at {}/main.dsb", build_dir.display());
|
||
}
|