custom format strings

This commit is contained in:
Noah Swerhun 2024-03-15 09:27:43 -05:00
parent 9a96f4dfb6
commit 3363b23335
6 changed files with 71 additions and 11 deletions

7
Cargo.lock generated
View file

@ -86,6 +86,7 @@ version = "0.1.0"
dependencies = [ dependencies = [
"clap", "clap",
"serde", "serde",
"strfmt",
"toml", "toml",
] ]
@ -136,6 +137,12 @@ dependencies = [
"serde", "serde",
] ]
[[package]]
name = "strfmt"
version = "0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7a8348af2d9fc3258c8733b8d9d8db2e56f54b2363a4b5b81585c7875ed65e65"
[[package]] [[package]]
name = "syn" name = "syn"
version = "2.0.52" version = "2.0.52"

View file

@ -8,4 +8,5 @@ edition = "2021"
[dependencies] [dependencies]
clap = { version = "4.5.1", features = ["derive", "std", "help", "usage"], default-features = false } clap = { version = "4.5.1", features = ["derive", "std", "help", "usage"], default-features = false }
serde = { version = "1.0.197", features = ["derive"] } serde = { version = "1.0.197", features = ["derive"] }
strfmt = "0.2.4"
toml = "0.8.10" toml = "0.8.10"

View file

@ -2,6 +2,7 @@ use std::fmt::Display;
pub enum ErrorKind { pub enum ErrorKind {
NoSources, NoSources,
FormatError,
} }
pub struct Error { pub struct Error {

View file

@ -1,12 +1,14 @@
use std::collections::HashMap;
use std::fs::OpenOptions; use std::fs::OpenOptions;
use std::io::BufWriter; use std::io::BufWriter;
use std::io::Write; use std::io::Write;
use strfmt::strfmt;
use super::*; use super::*;
use crate::parse_deser::*; use crate::parse_deser::*;
use crate::Args; use crate::Args;
pub fn gen_ninja_header(config: &Config, cmdline_args: &Args) -> Result<String> { fn gen_ninja_header(config: &Config, cmdline_args: &Args) -> Result<String> {
let mut ret = "".to_string(); let mut ret = "".to_string();
ret.push_str(&format!( ret.push_str(&format!(
"## Generated by ngen "## Generated by ngen
@ -52,7 +54,7 @@ build {0}: regen_ninjafile {1} || $builddir/compile_commands.json
Ok(ret) Ok(ret)
} }
pub fn gen_ninja_target(name: &str, target: &Target) -> Result<String> { fn gen_ninja_target(name: &str, target: &Target) -> Result<String> {
if target.sources.len() == 0 { if target.sources.len() == 0 {
return Err(Error { return Err(Error {
kind: ErrorKind::NoSources, kind: ErrorKind::NoSources,
@ -66,23 +68,58 @@ pub fn gen_ninja_target(name: &str, target: &Target) -> Result<String> {
let linker_flags = target.linker_flags.join(" "); let linker_flags = target.linker_flags.join(" ");
let linker_libs = target.linker_libs.join(" "); let linker_libs = target.linker_libs.join(" ");
let mut fmt_args = HashMap::new();
fmt_args.insert("outfile".to_string(), "$out");
fmt_args.insert("compiler".to_string(), target.compiler);
fmt_args.insert("compiler_flags".to_string(), &compiler_flags);
fmt_args.insert("linker".to_string(), target.compiler);
fmt_args.insert("object".to_string(), "$out");
fmt_args.insert("objects".to_string(), "$in");
fmt_args.insert("source".to_string(), "$in");
fmt_args.insert("depfile".to_string(), "$dep");
fmt_args.insert("linker_flags".to_string(), &linker_flags);
fmt_args.insert("linker_libs".to_string(), &linker_libs);
ret.push_str(&format!( ret.push_str(&format!(
"\ "\
# BEGIN TARGET {0} # BEGIN TARGET {name}
rule cc_{0} rule cc_{name}
deps = gcc deps = gcc
depfile = $dep depfile = $dep
command = {1} {2} -MD -MF $dep -o $out -c $in command = "
));
match strfmt(target.opts.compile_cmd_fmt, &fmt_args) {
Ok(v) => ret.push_str(&v),
Err(e) => {
return Err(Error {
kind: ErrorKind::FormatError,
message: e.to_string(),
});
}
}
ret.push_str(&format!(
"
description = Building $in -> $out description = Building $in -> $out
rule link_{name} rule link_{name}
command = {3} {4} -o $out $in {5} command = "
));
match strfmt(target.opts.link_cmd_fmt, &fmt_args) {
Ok(v) => ret.push_str(&v),
Err(e) => {
return Err(Error {
kind: ErrorKind::FormatError,
message: e.to_string(),
});
}
}
ret.push_str(&format!(
"
description = Linking $out description = Linking $out
build $builddir/{0}/obj: mkdir build $builddir/{name}/obj: mkdir
build $builddir/{0}/dep: mkdir build $builddir/{name}/dep: mkdir
", "
name, target.compiler, compiler_flags, target.linker, linker_flags, linker_libs
)); ));
let mut object_list: Vec<String> = Vec::new(); let mut object_list: Vec<String> = Vec::new();
@ -127,7 +164,6 @@ pub fn create_ninja_file_content(
Ok(v) => ret.push_str(&v), Ok(v) => ret.push_str(&v),
Err(e) => match e.kind { Err(e) => match e.kind {
ErrorKind::NoSources => eprintln!("WARNING: {e}. Skipping `{}`", name), ErrorKind::NoSources => eprintln!("WARNING: {e}. Skipping `{}`", name),
#[allow(unreachable_patterns)]
_ => { _ => {
return Err(e); return Err(e);
} }

View file

@ -21,6 +21,8 @@ pub struct DeserTargetOptions {
pub inherit_from: Option<String>, pub inherit_from: Option<String>,
pub depend_on: Option<Vec<String>>, pub depend_on: Option<Vec<String>>,
pub default: Option<bool>, pub default: Option<bool>,
pub compile_cmd_fmt: Option<String>,
pub link_cmd_fmt: Option<String>,
} }
#[derive(Debug, Clone, Deserialize)] #[derive(Debug, Clone, Deserialize)]

View file

@ -6,6 +6,8 @@ pub struct TargetOptions<'a> {
pub inherit_from: &'a str, pub inherit_from: &'a str,
pub default: bool, pub default: bool,
pub depend_on: Vec<&'a str>, pub depend_on: Vec<&'a str>,
pub compile_cmd_fmt: &'a str,
pub link_cmd_fmt: &'a str,
} }
impl<'a> From<&'a DeserTargetOptions> for TargetOptions<'a> { impl<'a> From<&'a DeserTargetOptions> for TargetOptions<'a> {
@ -30,6 +32,14 @@ impl<'a> From<&'a DeserTargetOptions> for TargetOptions<'a> {
} }
} }
if let Some(v) = &value.compile_cmd_fmt {
ret.compile_cmd_fmt = v;
}
if let Some(v) = &value.link_cmd_fmt {
ret.link_cmd_fmt = v;
}
ret ret
} }
} }
@ -41,6 +51,9 @@ impl<'a> TargetOptions<'a> {
inherit_from: "main", inherit_from: "main",
default: false, default: false,
depend_on: vec![], depend_on: vec![],
compile_cmd_fmt:
"{compiler} {compiler_flags} -MD -MF {depfile} -o {object} -c {source}",
link_cmd_fmt: "{linker} {linker_flags} -o {outfile} {objects} {linker_libs}",
} }
} }
} }