compile commands functionality added
This commit is contained in:
parent
d76e080841
commit
19be0d2a6a
2 changed files with 131 additions and 25 deletions
|
@ -6,6 +6,7 @@ use toml::{Table, Value};
|
|||
pub struct Config {
|
||||
pub build_dir: String,
|
||||
pub compile_commands: bool,
|
||||
pub compile_commands_target: String,
|
||||
}
|
||||
|
||||
impl Config {
|
||||
|
@ -13,6 +14,7 @@ impl Config {
|
|||
Config {
|
||||
build_dir: String::from("build"),
|
||||
compile_commands: false,
|
||||
compile_commands_target: String::from(""),
|
||||
}
|
||||
}
|
||||
pub fn set(&mut self, table: &Table) {
|
||||
|
@ -27,12 +29,19 @@ impl Config {
|
|||
}
|
||||
"compile_commands" => {
|
||||
if let Value::Boolean(b) = table.get(k).unwrap() {
|
||||
eprintln!("warn: config.compile_commands has no functionality (yet)");
|
||||
//eprintln!("warn: config.compile_commands has no functionality (yet)");
|
||||
self.compile_commands = *b;
|
||||
} else {
|
||||
panic!("fatal: {k} invalid type, must be a boolean.");
|
||||
}
|
||||
}
|
||||
"compile_commands_target" => {
|
||||
if let Value::String(s) = table.get(k).unwrap() {
|
||||
self.compile_commands_target = s.to_owned();
|
||||
} else {
|
||||
panic!("fatal: {k} invalid type, must be a string.");
|
||||
}
|
||||
}
|
||||
_ => panic!("fatal: unrecognized key {k}."),
|
||||
}
|
||||
}
|
||||
|
|
145
src/main.rs
145
src/main.rs
|
@ -1,6 +1,9 @@
|
|||
use clap::Parser;
|
||||
use core::panic;
|
||||
use std::fs::{read, write};
|
||||
use std::{
|
||||
fs::{read, write},
|
||||
path::Path,
|
||||
};
|
||||
use toml::{Table, Value};
|
||||
|
||||
mod target;
|
||||
|
@ -16,19 +19,23 @@ struct Args {
|
|||
|
||||
#[arg(short, long, default_value = "build.ninja")]
|
||||
output_file: String,
|
||||
|
||||
#[arg(long)]
|
||||
write_compile_commands: Option<String>,
|
||||
}
|
||||
|
||||
fn gen_ninja_code(config: &Target) -> String {
|
||||
fn gen_ninja_code(target: &Target) -> String {
|
||||
let mut ret = String::from("\n");
|
||||
|
||||
let name = &config.name;
|
||||
let outfile = &config.outfile;
|
||||
let compiler = &config.compiler;
|
||||
let compiler_flags = &config.compiler_flags.join(" ");
|
||||
let linker = &config.linker;
|
||||
let linker_flags = config.linker_flags.join(" ");
|
||||
let linker_libs = config.linker_libs.join(" ");
|
||||
let sources = &config.sources;
|
||||
// necessary because you cannot access struct members in a format string :(
|
||||
let name = &target.name;
|
||||
let outfile = &target.outfile;
|
||||
let compiler = &target.compiler;
|
||||
let compiler_flags = &target.compiler_flags.join(" ");
|
||||
let linker = &target.linker;
|
||||
let linker_flags = &target.linker_flags.join(" ");
|
||||
let linker_libs = &target.linker_libs.join(" ");
|
||||
let sources = &target.sources;
|
||||
|
||||
ret.push_str(&format!(
|
||||
"\
|
||||
|
@ -65,7 +72,7 @@ build $builddir/{name}/dep: mkdir
|
|||
ret.push_str(&format!(
|
||||
" || $builddir/{name}/obj $builddir/{name}/dep\nbuild {name}: phony $builddir/{name}/{outfile}\n"
|
||||
));
|
||||
if config.is_default {
|
||||
if target.is_default {
|
||||
ret.push_str(&format!("\ndefault {name}\n"));
|
||||
}
|
||||
ret.push_str(&format!("# END TARGET {name}\n"));
|
||||
|
@ -73,6 +80,41 @@ build $builddir/{name}/dep: mkdir
|
|||
ret
|
||||
}
|
||||
|
||||
fn gen_compile_commands(target: &Target, build_dir: &str) -> String {
|
||||
let mut ret = String::from("[\n");
|
||||
|
||||
let name = &target.name;
|
||||
let compiler = &target.compiler;
|
||||
let compiler_flags = &target.compiler_flags.join(" ");
|
||||
|
||||
let pwd = Path::new(".");
|
||||
let pwd = pwd
|
||||
.canonicalize()
|
||||
.expect("cwd is non canonical (something is very wrong)");
|
||||
let pwd = pwd.to_str().unwrap();
|
||||
|
||||
for (i, s) in target.sources.iter().enumerate() {
|
||||
let new_file = s.replace("/", "-");
|
||||
let obj_name = format!("{build_dir}/{name}/obj/{new_file}.o");
|
||||
|
||||
ret.push_str(&format!(
|
||||
" {{
|
||||
\"directory\": \"{pwd}\",
|
||||
\"command\": \"{compiler} {compiler_flags} -o {obj_name} -c {s}\",
|
||||
\"file\": \"{s}\"
|
||||
}}"
|
||||
));
|
||||
if i != target.sources.len() - 1 {
|
||||
ret.push(',');
|
||||
}
|
||||
ret.push('\n');
|
||||
}
|
||||
|
||||
ret.push_str("]\n");
|
||||
|
||||
ret
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let args = Args::parse();
|
||||
let output_file = &args.output_file;
|
||||
|
@ -84,23 +126,61 @@ fn main() {
|
|||
let parsed_config = toml::from_str::<Table>(&config_file_string)
|
||||
.unwrap_or_else(|e| panic!("fatal: could not parse config file: {e}"));
|
||||
|
||||
// get config
|
||||
let mut config = Config::get_default();
|
||||
|
||||
let mut targets: Vec<Target> = Vec::new();
|
||||
targets.push(Target::new(&parsed_config, "main", None));
|
||||
for k in parsed_config.keys() {
|
||||
if let Value::Table(t) = parsed_config.get(k).unwrap() {
|
||||
if k == "config" {
|
||||
config.set(&t);
|
||||
} else {
|
||||
targets.push(Target::new(&t, k, Some(&targets[0])));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
//----
|
||||
|
||||
// parse the main target first
|
||||
let mut targets: Vec<Target> = Vec::new();
|
||||
let main_target = Target::new(&parsed_config, "main", None);
|
||||
targets.push(main_target.clone());
|
||||
let mut target_for_comp_cmds = main_target;
|
||||
let mut target_for_comp_cmds_changed = false;
|
||||
|
||||
// parse other targets
|
||||
for k in parsed_config.keys() {
|
||||
if let Value::Table(t) = parsed_config.get(k).unwrap() {
|
||||
if k == "config" {
|
||||
continue; // ignore config
|
||||
}
|
||||
let target = Target::new(&t, k, Some(&targets[0]));
|
||||
targets.push(target);
|
||||
if let Some(_) = args.write_compile_commands {
|
||||
if *k == config.compile_commands_target {
|
||||
target_for_comp_cmds = targets[targets.len() - 1].clone();
|
||||
target_for_comp_cmds_changed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let build_dir = config.build_dir;
|
||||
let _compile_commands = config.compile_commands;
|
||||
if let Some(file) = args.write_compile_commands {
|
||||
if config.compile_commands_target == "" {
|
||||
eprintln!(
|
||||
"ngen: warn: no compile_commands target found, using 'main' target by default."
|
||||
);
|
||||
} else if !target_for_comp_cmds_changed && config.compile_commands_target != "main" {
|
||||
let c = &config.compile_commands_target;
|
||||
eprintln!(
|
||||
"ngen: warn: compile_commands target {c} not found, using 'main' target instead."
|
||||
);
|
||||
}
|
||||
write(
|
||||
file,
|
||||
&gen_compile_commands(&target_for_comp_cmds, &config.build_dir),
|
||||
)
|
||||
.expect("fatal: could not write {file}");
|
||||
}
|
||||
|
||||
let build_dir = &config.build_dir;
|
||||
let mut ninjafile = format!(
|
||||
"\
|
||||
# Generated by ngen. Do not modify by hand.
|
||||
|
@ -111,7 +191,7 @@ rule mkdir
|
|||
command = mkdir -p $out
|
||||
description = Creating directory $out
|
||||
|
||||
rule regen
|
||||
rule regen_ninjafile
|
||||
command = ngen -c $in -o $out
|
||||
generator = 1
|
||||
description = Regenerating $out
|
||||
|
@ -119,14 +199,31 @@ rule regen
|
|||
",
|
||||
);
|
||||
|
||||
ninjafile.push_str(&format!(
|
||||
"build {output_file}: regen {config_file_name}\n pool = console\n"
|
||||
));
|
||||
if config.compile_commands {
|
||||
ninjafile.push_str(&format!(
|
||||
"\
|
||||
rule regen_compile_commands
|
||||
command = ngen -c $in --write-compile-commands $out
|
||||
description = Regenerating $out
|
||||
|
||||
build $builddir: mkdir
|
||||
|
||||
build $builddir/compile_commands.json: regen_compile_commands {config_file_name} | $builddir
|
||||
pool = console
|
||||
|
||||
build {output_file}: regen_ninjafile {config_file_name} || $builddir/compile_commands.json
|
||||
pool = console
|
||||
"
|
||||
));
|
||||
} else {
|
||||
ninjafile.push_str(&format!(
|
||||
"build {output_file}: regen_ninjafile {config_file_name}\n pool = console\n"
|
||||
));
|
||||
}
|
||||
|
||||
for t in targets {
|
||||
ninjafile.push_str(&gen_ninja_code(&t));
|
||||
}
|
||||
|
||||
write(output_file, ninjafile)
|
||||
.unwrap_or_else(|e| panic!("fatal: could not write ninja file: {e}"));
|
||||
write(output_file, ninjafile).expect("fatal: could not write ninja file");
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue