junocc 0.1.4

Compiler for JunoLang
use std::hash::{ DefaultHasher, Hash, Hasher };
use std::path::Path;
use std::process::Command;

use libjuno::inkwell::OptimizationLevel;
use libjuno::inkwell::targets::{
    CodeModel,
    InitializationConfig,
    RelocMode,
    Target,
    TargetMachine,
};
use libjuno::{ compile_file, inkwell::module::Module };
use clap::Parser;

mod optimizer;

#[derive(Parser, Debug, Clone)]
#[command(version, about, long_about = None)]
struct Cli {
    files: Vec<String>,

    #[arg(short, long)]
    output: Option<String>,

    #[arg(long)]
    bc: bool,
}

struct JunoObject<'a> {
    module: Module<'a>,
    filename: String,
}

fn main() {
    let args = Cli::parse();
    let output = args.output.unwrap_or("out.junoc".to_string());
    let linker = std::env::var("JUNO_LD").unwrap_or("clang".to_string());
    let _out_ext: Vec<&str> = output.split(".").collect();
    let mut out_ext = *_out_ext.last().unwrap();
    if _out_ext.len() < 2 {
        out_ext = "elf";
    }
    let mut objects: Vec<JunoObject> = vec![];
    let target_machine = get_target_machine();
    for file in args.files {
        let ext = file.split(".").last().unwrap();

        let mut o = match ext {
            "juno" =>
                JunoObject {
                    module: compile_file(Path::new(&file)),
                    filename: file,
                },
            _ => panic!("Unknown input filetyp: {}", ext),
        };
        optimizer::optimize(&mut o.module);
        objects.push(o);
    }
    if args.bc {
        for o in &objects {
            let mut s = DefaultHasher::new();

            o.module.to_string().hash(&mut s);
            let hash = s.finish();

            o.module.write_bitcode_to_path(
                Path::new(&format!("./{}-{:x}.bc", o.filename, hash).to_string())
            );
        }
    }
    match out_ext {
        "junoc" => {}
        "junobj" => {}
        "elf" => {
            let mut object_paths: Vec<String> = vec![];
            for o in &objects {
                let mut s = DefaultHasher::new();

                o.module.to_string().hash(&mut s);
                let hash = s.finish();
                let path = &format!("./{}-{:x}.o", o.filename, hash).to_string();
                let _ = target_machine.write_to_file(
                    &o.module,
                    libjuno::inkwell::targets::FileType::Object,
                    Path::new(path)
                );
                object_paths.push(path.clone());
            }
            let linker_args: Vec<String> = vec!["-o".to_string(), output, "-no-pie".to_string()];
            object_paths.extend(linker_args);
            let _status = Command::new(&linker)
                .args(&object_paths)
                .status().unwrap();
        }
        "lib" => {}
        "bc" => {}
        _ => panic!("Unknown output filetyp: {}", out_ext),
    }
}

pub fn get_target_machine() -> TargetMachine {
    Target::initialize_native(&InitializationConfig::default()).unwrap();

    let triple = TargetMachine::get_default_triple();

    let target = Target::from_triple(&triple).unwrap();

    target
        .create_target_machine(
            &triple,
            "generic",
            "",
            OptimizationLevel::Default,
            RelocMode::Default,
            CodeModel::Default
        )
        .unwrap()
}