Skip to content

Commit

Permalink
Reuse same Args struct, preserve span of literals in submodule versions.
Browse files Browse the repository at this point in the history
  • Loading branch information
de-vri-es committed Dec 13, 2023
1 parent c442057 commit 5a21856
Show file tree
Hide file tree
Showing 3 changed files with 53 additions and 121 deletions.
149 changes: 30 additions & 119 deletions git-version-macro/src/describe_submodules.rs
Original file line number Diff line number Diff line change
@@ -1,86 +1,29 @@
extern crate proc_macro;
use proc_macro2::{Span, TokenStream as TokenStream2};
use quote::quote;
use std::ffi::OsStr;
use std::path::Path;
use std::process::Command;
use syn::{
bracketed,
parse::{Parse, ParseStream},
punctuated::Punctuated,
token::{Comma, Eq},
Ident, LitStr,
};

use crate::utils::{git_dir, run_git};
use crate::Args;

macro_rules! error {
($($args:tt)*) => {
syn::Error::new(Span::call_site(), format!($($args)*))
};
}

#[derive(Default)]
pub(crate) struct GitModArgs {
args: Option<Punctuated<LitStr, Comma>>,
prefix: Option<LitStr>,
suffix: Option<LitStr>,
fallback: Option<LitStr>,
}

impl Parse for GitModArgs {
fn parse(input: ParseStream) -> syn::Result<Self> {
let mut result = GitModArgs::default();
loop {
if input.is_empty() {
break;
}
let ident: Ident = input.parse()?;
let _: Eq = input.parse()?;
let check_dup = |dup: bool| {
if dup {
Err(error!("`{} = ` can only appear once", ident))
} else {
Ok(())
}
};
match ident.to_string().as_str() {
"args" => {
check_dup(result.args.is_some())?;
let content;
bracketed!(content in input);
result.args = Some(Punctuated::parse_terminated(&content)?);
}
"prefix" => {
check_dup(result.prefix.is_some())?;
result.prefix = Some(input.parse()?);
}
"suffix" => {
check_dup(result.suffix.is_some())?;
result.suffix = Some(input.parse()?);
}
"fallback" => {
check_dup(result.fallback.is_some())?;
result.fallback = Some(input.parse()?);
}
x => Err(error!("Unexpected argument name `{}`", x))?,
}
if input.is_empty() {
break;
}
let _: Comma = input.parse()?;
}
Ok(result)
pub fn git_submodule_versions_impl(args: Args) -> syn::Result<TokenStream2> {
if let Some(cargo_prefix) = &args.cargo_prefix {
return Err(syn::Error::new_spanned(cargo_prefix, "invalid argument `cargo_prefix` for `git_submodule_versions!()`"));
}
if let Some(cargo_suffix) = &args.cargo_suffix {
return Err(syn::Error::new_spanned(cargo_suffix, "invalid argument `cargo_suffix` for `git_submodule_versions!()`"));
}
}

pub(crate) fn git_submodule_versions_impl(args: GitModArgs) -> syn::Result<TokenStream2> {
let manifest_dir = std::env::var_os("CARGO_MANIFEST_DIR")
.ok_or_else(|| error!("CARGO_MANIFEST_DIR is not set"))?;
let git_dir = git_dir(&manifest_dir)
let git_dir = crate::utils::git_dir(&manifest_dir)
.map_err(|e| error!("failed to determine .git directory: {}", e))?;

let modules = match get_submodules(&manifest_dir) {
let modules = match crate::utils::get_submodules(&manifest_dir) {
Ok(x) => x,
Err(err) => return Err(error!("{}", err)),
};
Expand All @@ -90,79 +33,47 @@ pub(crate) fn git_submodule_versions_impl(args: GitModArgs) -> syn::Result<Token
return Ok(quote!([("", ""); 0]));
}

let git_describe_args = args.args.map_or_else(
|| vec!["--always".to_string(), "--dirty=-modified".to_string()],
|list| list.iter().map(|x| x.value()).collect(),
);

let prefix = match args.prefix {
Some(x) => x.value(),
_ => "".to_string(),
};
let suffix = match args.suffix {
Some(x) => x.value(),
_ => "".to_string(),
};
let fallback = args.fallback.map(|x| x.value());

let versions = describe_submodules(&git_dir.join(".."), &modules, &git_describe_args, &prefix, &suffix, fallback.as_deref())
let versions = describe_submodules(&git_dir.join(".."), &modules, &args)
.map_err(|e| error!("{}", e))?;

Ok(quote!({
[#((#modules, #versions)),*]
}))
}

/// Run `git submodule foreach` command to discover submodules in the project.
fn get_submodules(dir: impl AsRef<Path>) -> Result<Vec<String>, String> {
let dir = dir.as_ref();
let result = run_git("git submodule",
Command::new("git")
.arg("-C")
.arg(dir)
.arg("submodule")
.arg("foreach")
.arg("--quiet")
.arg("--recursive")
.arg("echo $displaypath"),
)?;

Ok(result.lines()
.filter(|x| !x.is_empty())
.map(|x| x.to_owned())
.collect()
)
}

/// Run `git describe` for each submodule to get the git version with the specified args.
fn describe_submodules<I, S>(
fn describe_submodules(
root: &Path,
submodules: &[String],
describe_args: I,
prefix: &str,
suffix: &str,
fallback: Option<&str>,
) -> Result<Vec<String>, String>
where
I: IntoIterator<Item = S> + Clone,
S: AsRef<OsStr>,
{
let mut versions: Vec<String> = vec![];
args: &Args,
) -> Result<Vec<TokenStream2>, String> {
let mut versions = Vec::new();

let git_args = args.git_args.as_ref().map_or_else(
|| vec!["--always".to_string(), "--dirty=-modified".to_string()],
|list| list.iter().map(|x| x.value()).collect(),
);

for submodule in submodules {
let path = root.join(submodule);
// Get the submodule version or fallback.
let version = match crate::utils::describe(path, describe_args.clone()) {
Ok(version) => version,
let version = match crate::utils::describe(path, &git_args) {
Ok(version) => {
let prefix = args.prefix.iter();
let suffix = args.suffix.iter();
quote!{
::core::concat!(#(#prefix,)* #version #(, #suffix)*)
}
}
Err(e) => {
if let Some(fallback) = fallback {
fallback.to_owned()
if let Some(fallback) = &args.fallback {
quote!( #fallback )
} else {
return Err(e)
}
},
};
versions.push(format!("{}{}{}", prefix, version, suffix))
versions.push(version);
}

Ok(versions)
Expand Down
2 changes: 1 addition & 1 deletion git-version-macro/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -243,7 +243,7 @@ fn git_version_impl(args: Args) -> syn::Result<TokenStream2> {
/// ```
#[proc_macro]
pub fn git_submodule_versions(input: TokenStream) -> TokenStream {
let args = syn::parse_macro_input!(input as describe_submodules::GitModArgs);
let args = syn::parse_macro_input!(input as Args);

let tokens = match describe_submodules::git_submodule_versions_impl(args) {
Ok(x) => x,
Expand Down
23 changes: 22 additions & 1 deletion git-version-macro/src/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,28 @@ pub fn git_dir(dir: impl AsRef<Path>) -> Result<PathBuf, String> {
Ok(dir.join(path))
}

pub fn run_git(program: &str, command: &mut std::process::Command) -> Result<String, String> {
/// Run `git submodule foreach` command to discover submodules in the project.
pub fn get_submodules(dir: impl AsRef<Path>) -> Result<Vec<String>, String> {
let dir = dir.as_ref();
let result = run_git("git submodule",
Command::new("git")
.arg("-C")
.arg(dir)
.arg("submodule")
.arg("foreach")
.arg("--quiet")
.arg("--recursive")
.arg("echo $displaypath"),
)?;

Ok(result.lines()
.filter(|x| !x.is_empty())
.map(|x| x.to_owned())
.collect()
)
}

fn run_git(program: &str, command: &mut std::process::Command) -> Result<String, String> {
let output = command
.stdout(std::process::Stdio::piped())
.stderr(std::process::Stdio::piped())
Expand Down

0 comments on commit 5a21856

Please sign in to comment.