From c7197c1a1af71daeef8a17a51e3c38a0d666126f Mon Sep 17 00:00:00 2001 From: Jordan Baxter Date: Thu, 2 Nov 2023 20:08:10 -0700 Subject: [PATCH 01/21] Started messing around with submodule outputs. --- .gitmodules | 6 ++++++ test-child-repo | 1 + test_outer_directory/test-child-repo | 1 + 3 files changed, 8 insertions(+) create mode 100644 .gitmodules create mode 160000 test-child-repo create mode 160000 test_outer_directory/test-child-repo diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..f5ef52d --- /dev/null +++ b/.gitmodules @@ -0,0 +1,6 @@ +[submodule "test-child-repo"] + path = test-child-repo + url = git@github.com:baxterjo/test-child-repo.git +[submodule "test_outer_directory/test-child-repo"] + path = test_outer_directory/test-child-repo + url = git@github.com:baxterjo/test-child-repo.git diff --git a/test-child-repo b/test-child-repo new file mode 160000 index 0000000..da418ba --- /dev/null +++ b/test-child-repo @@ -0,0 +1 @@ +Subproject commit da418ba0080659fdea1e883dc192ad8a827de337 diff --git a/test_outer_directory/test-child-repo b/test_outer_directory/test-child-repo new file mode 160000 index 0000000..da418ba --- /dev/null +++ b/test_outer_directory/test-child-repo @@ -0,0 +1 @@ +Subproject commit da418ba0080659fdea1e883dc192ad8a827de337 From 761a583e4c7f027379bdffb4e08390e18d98b4c7 Mon Sep 17 00:00:00 2001 From: Jordan Baxter Date: Sun, 5 Nov 2023 15:24:55 -0800 Subject: [PATCH 02/21] Added submodule macro and fixed clippy lints. --- git-version-macro/src/describe_submodules.rs | 120 +++++++++++++++++++ git-version-macro/src/lib.rs | 90 ++++++++++---- git-version-macro/src/utils.rs | 25 ++-- src/lib.rs | 2 +- tests/version.rs | 12 +- 5 files changed, 218 insertions(+), 31 deletions(-) create mode 100644 git-version-macro/src/describe_submodules.rs diff --git a/git-version-macro/src/describe_submodules.rs b/git-version-macro/src/describe_submodules.rs new file mode 100644 index 0000000..4890232 --- /dev/null +++ b/git-version-macro/src/describe_submodules.rs @@ -0,0 +1,120 @@ +extern crate proc_macro; + +use crate::git_dependencies; +use crate::utils::describe_modules; +use proc_macro2::{Span, TokenStream as TokenStream2}; +use quote::{quote, ToTokens}; +use syn::{ + bracketed, + parse::{Parse, ParseStream}, + punctuated::Punctuated, + token::{Comma, Eq}, + Expr, Ident, LitStr, +}; + +macro_rules! error { + ($($args:tt)*) => { + syn::Error::new(Span::call_site(), format!($($args)*)) + }; +} + +#[derive(Default)] +pub(crate) struct GitModArgs { + describe_args: Option>, + foreach_args: Option>, + prefix: Option, + suffix: Option, + fallback: Option, +} + +impl Parse for GitModArgs { + fn parse(input: ParseStream) -> syn::Result { + 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() { + "describe_args" => { + check_dup(result.describe_args.is_some())?; + let content; + bracketed!(content in input); + result.describe_args = Some(Punctuated::parse_terminated(&content)?); + } + "foreach_args" => { + check_dup(result.foreach_args.is_some())?; + let content; + bracketed!(content in input); + result.foreach_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(crate) fn git_version_modules_impl(args: GitModArgs) -> syn::Result { + let git_describe_args = args.describe_args.map_or_else( + || vec!["--always".to_string(), "--dirty=-modified".to_string()], + |list| list.iter().map(|x| x.value()).collect(), + ); + + let mut git_foreach_args = args.foreach_args.map_or_else( + || vec!["--quiet".to_string(), "--recursive".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 descibe_args = format!("echo $displaypath : {prefix}`git describe {}`{suffix}", git_describe_args.join(" ")); + // let descibe_args = format!("echo $displaypath : `git describe {}`", git_describe_args.join(" ")); + + let mut git_args: Vec = vec!["submodule".to_string(), "foreach".to_string()]; + git_args.append(&mut git_foreach_args); + git_args.push(descibe_args); + + match describe_modules(&git_args) { + Ok(version) => { + let dependencies = git_dependencies()?; + Ok(quote!({ + #dependencies; + #version + })) + } + Err(_) if args.fallback.is_some() => Ok(args.fallback.to_token_stream()), + Err(e) => Err(error!("{}", e)), + } +} diff --git a/git-version-macro/src/lib.rs b/git-version-macro/src/lib.rs index 8c18378..2d529a6 100644 --- a/git-version-macro/src/lib.rs +++ b/git-version-macro/src/lib.rs @@ -12,6 +12,7 @@ use syn::{ token::{Comma, Eq}, Expr, Ident, LitStr, }; +pub(crate) mod describe_submodules; mod utils; use self::utils::{describe_cwd, git_dir_cwd}; @@ -23,25 +24,29 @@ macro_rules! error { } fn canonicalize_path(path: &Path) -> syn::Result { - Ok(path - .canonicalize() + path.canonicalize() .map_err(|e| error!("failed to canonicalize {}: {}", path.display(), e))? .into_os_string() .into_string() - .map_err(|file| error!("invalid UTF-8 in path to {}", PathBuf::from(file).display()))? - ) + .map_err(|file| error!("invalid UTF-8 in path to {}", PathBuf::from(file).display())) } /// Create a token stream representing dependencies on the git state. fn git_dependencies() -> syn::Result { let git_dir = git_dir_cwd().map_err(|e| error!("failed to determine .git directory: {}", e))?; - let deps: Vec<_> = ["logs/HEAD", "index"].iter().flat_map(|&file| { - canonicalize_path(&git_dir.join(file)).map(Some).unwrap_or_else(|e| { - eprintln!("Failed to add dependency on the git state: {}. Git state changes might not trigger a rebuild.", e); - None + let deps: Vec<_> = ["logs/HEAD", "index"] + .iter() + .flat_map(|&file| { + canonicalize_path(&git_dir.join(file)).map(Some).unwrap_or_else(|e| { + eprintln!( + "Failed to add dependency on the git state: {}. Git state changes might not trigger a rebuild.", + e + ); + None + }) }) - }).collect(); + .collect(); Ok(quote! { #( include_bytes!(#deps); )* @@ -62,7 +67,9 @@ impl Parse for Args { fn parse(input: ParseStream) -> syn::Result { let mut result = Args::default(); loop { - if input.is_empty() { break; } + if input.is_empty() { + break; + } let ident: Ident = input.parse()?; let _: Eq = input.parse()?; let check_dup = |dup: bool| { @@ -101,7 +108,9 @@ impl Parse for Args { } x => Err(error!("Unexpected argument name `{}`", x))?, } - if input.is_empty() { break; } + if input.is_empty() { + break; + } let _: Comma = input.parse()?; } Ok(result) @@ -156,7 +165,7 @@ pub fn git_version(input: TokenStream) -> TokenStream { fn git_version_impl(args: Args) -> syn::Result { let git_args = args.git_args.map_or_else( || vec!["--always".to_string(), "--dirty=-modified".to_string()], - |list| list.iter().map(|x| x.value()).collect() + |list| list.iter().map(|x| x.value()).collect(), ); let cargo_fallback = args.cargo_prefix.is_some() || args.cargo_suffix.is_some(); @@ -175,20 +184,59 @@ fn git_version_impl(args: Args) -> syn::Result { if let Ok(version) = std::env::var("CARGO_PKG_VERSION") { let prefix = args.cargo_prefix.iter(); let suffix = args.cargo_suffix; - Ok(quote!( - concat!(#(#prefix,)* #version, #suffix) - )) + Ok(quote!(concat!(#(#prefix,)* #version, #suffix))) } else if let Some(fallback) = args.fallback { Ok(fallback.to_token_stream()) } else { Err(error!("Unable to get git or cargo version")) } } - Err(_) if args.fallback.is_some() => { - Ok(args.fallback.to_token_stream()) - } - Err(e) => { - Err(error!("{}", e)) - } + Err(_) if args.fallback.is_some() => Ok(args.fallback.to_token_stream()), + Err(e) => Err(error!("{}", e)), } } + +/// Get the git version for the source code. +/// +/// The following (named) arguments can be given: +/// +/// - `args`: The arguments to call `git describe` with. +/// Default: `args = ["--always", "--dirty=-modified"]` +/// +/// - `prefix`, `suffix`: +/// The git version will be prefixed/suffexed by these strings. +/// +/// - `cargo_prefix`, `cargo_suffix`: +/// If either is given, Cargo's version (given by the CARGO_PKG_VERSION +/// environment variable) will be used if git fails instead of giving an +/// error. It will be prefixed/suffixed by the given strings. +/// +/// - `fallback`: +/// If all else fails, this string will be given instead of reporting an +/// error. +/// +/// # Examples +/// +/// ```ignore +/// const VERSION: &str = git_version!(); +/// ``` +/// +/// ```ignore +/// const VERSION: &str = git_version!(args = ["--abbrev=40", "--always"]); +/// ``` +/// +/// ``` +/// # use git_version::git_version; +/// const VERSION: &str = git_version!(prefix = "git:", cargo_prefix = "cargo:", fallback = "unknown"); +/// ``` +#[proc_macro] +pub fn git_version_modules(input: TokenStream) -> TokenStream { + let args = parse_macro_input!(input as describe_submodules::GitModArgs); + + let tokens = match describe_submodules::git_version_modules_impl(args) { + Ok(x) => x, + Err(e) => e.to_compile_error(), + }; + + TokenStream::from(tokens) +} diff --git a/git-version-macro/src/utils.rs b/git-version-macro/src/utils.rs index 476b806..fc44a9e 100644 --- a/git-version-macro/src/utils.rs +++ b/git-version-macro/src/utils.rs @@ -4,7 +4,7 @@ use std::process::Command; /// Remove a trailing newline from a byte string. fn strip_trailing_newline(mut input: Vec) -> Vec { - if input.len() > 0 && input[input.len() - 1] == b'\n' { + if !input.is_empty() && input[input.len() - 1] == b'\n' { input.pop(); } input @@ -16,10 +16,7 @@ where I: IntoIterator, S: AsRef, { - let cmd = Command::new("git") - .arg("describe") - .args(args) - .output()?; + let cmd = Command::new("git").arg("describe").args(args).output()?; let output = verbose_command_error("git describe", cmd)?; let output = strip_trailing_newline(output.stdout); @@ -27,12 +24,24 @@ where Ok(String::from_utf8_lossy(&output).to_string()) } +/// Run `git describe` for submodules in the current working directory with custom flags to get version information from git. +pub fn describe_modules(args: I) -> std::io::Result +where + I: IntoIterator, + S: AsRef, +{ + let cmd = Command::new("git").args(args).output()?; + + let output = verbose_command_error("git submodule", cmd)?; + let output = strip_trailing_newline(output.stdout); + + Ok(String::from_utf8_lossy(&output).to_string()) +} + /// Get the git directory for the current working directory. pub fn git_dir_cwd() -> std::io::Result { // Run git rev-parse --git-dir, and capture standard output. - let cmd = Command::new("git") - .args(&["rev-parse", "--git-dir"]) - .output()?; + let cmd = Command::new("git").args(["rev-parse", "--git-dir"]).output()?; let output = verbose_command_error("git rev-parse --git-dir", cmd)?; let output = strip_trailing_newline(output.stdout); diff --git a/src/lib.rs b/src/lib.rs index 310dbd4..1c1e622 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -13,7 +13,7 @@ //! These macros do not depend on libgit, but simply uses the `git` binary directly. //! So you must have `git` installed somewhere in your `PATH`. -pub use git_version_macro::git_version; +pub use git_version_macro::{git_version, git_version_modules}; /// Run `git describe` at compile time with custom flags. /// diff --git a/tests/version.rs b/tests/version.rs index f3e9b9d..509eeb4 100644 --- a/tests/version.rs +++ b/tests/version.rs @@ -1,4 +1,4 @@ -use git_version::{git_describe, git_version}; +use git_version::{git_describe, git_version, git_version_modules}; #[test] fn git_describe_is_right() { @@ -14,3 +14,13 @@ fn git_describe_is_right() { assert_eq!(git_describe!("--always", "--dirty=-modified"), name); assert_eq!(git_version!(prefix = "[", suffix = "]"), format!("[{}]", name)); } + +#[test] +fn test_modules_macro_gives_expected_output() { + let module_versions = git_version_modules!( + prefix = "pre-", + suffix = "-suff", + describe_args = ["--always", "--dirty=-modified", "--tags"] + ); + println!("{module_versions}"); +} From e7247ec1fed2f7a442983197d5464c28e1ca96f6 Mon Sep 17 00:00:00 2001 From: Jordan Baxter Date: Sun, 5 Nov 2023 15:40:51 -0800 Subject: [PATCH 03/21] Added docs. --- git-version-macro/src/lib.rs | 35 +++++++++++++++++++++-------------- 1 file changed, 21 insertions(+), 14 deletions(-) diff --git a/git-version-macro/src/lib.rs b/git-version-macro/src/lib.rs index 2d529a6..e097f67 100644 --- a/git-version-macro/src/lib.rs +++ b/git-version-macro/src/lib.rs @@ -196,20 +196,27 @@ fn git_version_impl(args: Args) -> syn::Result { } } -/// Get the git version for the source code. +/// Get the git version for submodules below the cargo project. +/// +/// This is achieved by running `git foreach` in tandem with `git describe`. +/// The arguments for both git commands are exposed as macro arguments. +/// +/// The each line of text output for this macro will be formatted as follows: +/// ```bash +/// relative/path/to/submodule : {prefix}{git_describe_output_for_submodule}{suffix} +/// ``` /// /// The following (named) arguments can be given: /// -/// - `args`: The arguments to call `git describe` with. +/// - `foreach_args`: The arguments to call `git submodule foreach` with. +/// Default: `args = ["--quiet", "--recursive"]` +/// +/// - `describe_args`: The arguments to call `git describe` with. /// Default: `args = ["--always", "--dirty=-modified"]` /// /// - `prefix`, `suffix`: -/// The git version will be prefixed/suffexed by these strings. -/// -/// - `cargo_prefix`, `cargo_suffix`: -/// If either is given, Cargo's version (given by the CARGO_PKG_VERSION -/// environment variable) will be used if git fails instead of giving an -/// error. It will be prefixed/suffixed by the given strings. +/// The git version for each submodule will be prefixed/suffixed +/// by these strings. /// /// - `fallback`: /// If all else fails, this string will be given instead of reporting an @@ -217,17 +224,17 @@ fn git_version_impl(args: Args) -> syn::Result { /// /// # Examples /// -/// ```ignore -/// const VERSION: &str = git_version!(); +/// ``` +/// const VERSION: &str = git_version_modules!(); /// ``` /// -/// ```ignore -/// const VERSION: &str = git_version!(args = ["--abbrev=40", "--always"]); +/// ``` +/// const VERSION: &str = git_version_modules!(describe_args = ["--abbrev=40", "--always"]); /// ``` /// /// ``` -/// # use git_version::git_version; -/// const VERSION: &str = git_version!(prefix = "git:", cargo_prefix = "cargo:", fallback = "unknown"); +/// # use git_version::git_version_modules; +/// const VERSION: &str = git_version_modules!(prefix = "git:", cargo_prefix = "cargo:", fallback = "unknown"); /// ``` #[proc_macro] pub fn git_version_modules(input: TokenStream) -> TokenStream { From b1d05201d2c4474f2f48364a15569d068cac5e95 Mon Sep 17 00:00:00 2001 From: Jordan Baxter Date: Thu, 2 Nov 2023 20:08:10 -0700 Subject: [PATCH 04/21] Started messing around with submodule outputs. --- .gitmodules | 6 ++++++ test-child-repo | 1 + test_outer_directory/test-child-repo | 1 + 3 files changed, 8 insertions(+) create mode 100644 .gitmodules create mode 160000 test-child-repo create mode 160000 test_outer_directory/test-child-repo diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..f5ef52d --- /dev/null +++ b/.gitmodules @@ -0,0 +1,6 @@ +[submodule "test-child-repo"] + path = test-child-repo + url = git@github.com:baxterjo/test-child-repo.git +[submodule "test_outer_directory/test-child-repo"] + path = test_outer_directory/test-child-repo + url = git@github.com:baxterjo/test-child-repo.git diff --git a/test-child-repo b/test-child-repo new file mode 160000 index 0000000..da418ba --- /dev/null +++ b/test-child-repo @@ -0,0 +1 @@ +Subproject commit da418ba0080659fdea1e883dc192ad8a827de337 diff --git a/test_outer_directory/test-child-repo b/test_outer_directory/test-child-repo new file mode 160000 index 0000000..da418ba --- /dev/null +++ b/test_outer_directory/test-child-repo @@ -0,0 +1 @@ +Subproject commit da418ba0080659fdea1e883dc192ad8a827de337 From f7ad9e41bba18da84a5182f603b0d06379583c59 Mon Sep 17 00:00:00 2001 From: Jordan Baxter Date: Sun, 5 Nov 2023 15:24:55 -0800 Subject: [PATCH 05/21] Added submodule macro and fixed clippy lints. --- git-version-macro/src/describe_submodules.rs | 120 +++++++++++++++++++ git-version-macro/src/lib.rs | 88 ++++++++++---- git-version-macro/src/utils.rs | 24 +++- src/lib.rs | 2 +- tests/version.rs | 12 +- 5 files changed, 220 insertions(+), 26 deletions(-) create mode 100644 git-version-macro/src/describe_submodules.rs diff --git a/git-version-macro/src/describe_submodules.rs b/git-version-macro/src/describe_submodules.rs new file mode 100644 index 0000000..4890232 --- /dev/null +++ b/git-version-macro/src/describe_submodules.rs @@ -0,0 +1,120 @@ +extern crate proc_macro; + +use crate::git_dependencies; +use crate::utils::describe_modules; +use proc_macro2::{Span, TokenStream as TokenStream2}; +use quote::{quote, ToTokens}; +use syn::{ + bracketed, + parse::{Parse, ParseStream}, + punctuated::Punctuated, + token::{Comma, Eq}, + Expr, Ident, LitStr, +}; + +macro_rules! error { + ($($args:tt)*) => { + syn::Error::new(Span::call_site(), format!($($args)*)) + }; +} + +#[derive(Default)] +pub(crate) struct GitModArgs { + describe_args: Option>, + foreach_args: Option>, + prefix: Option, + suffix: Option, + fallback: Option, +} + +impl Parse for GitModArgs { + fn parse(input: ParseStream) -> syn::Result { + 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() { + "describe_args" => { + check_dup(result.describe_args.is_some())?; + let content; + bracketed!(content in input); + result.describe_args = Some(Punctuated::parse_terminated(&content)?); + } + "foreach_args" => { + check_dup(result.foreach_args.is_some())?; + let content; + bracketed!(content in input); + result.foreach_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(crate) fn git_version_modules_impl(args: GitModArgs) -> syn::Result { + let git_describe_args = args.describe_args.map_or_else( + || vec!["--always".to_string(), "--dirty=-modified".to_string()], + |list| list.iter().map(|x| x.value()).collect(), + ); + + let mut git_foreach_args = args.foreach_args.map_or_else( + || vec!["--quiet".to_string(), "--recursive".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 descibe_args = format!("echo $displaypath : {prefix}`git describe {}`{suffix}", git_describe_args.join(" ")); + // let descibe_args = format!("echo $displaypath : `git describe {}`", git_describe_args.join(" ")); + + let mut git_args: Vec = vec!["submodule".to_string(), "foreach".to_string()]; + git_args.append(&mut git_foreach_args); + git_args.push(descibe_args); + + match describe_modules(&git_args) { + Ok(version) => { + let dependencies = git_dependencies()?; + Ok(quote!({ + #dependencies; + #version + })) + } + Err(_) if args.fallback.is_some() => Ok(args.fallback.to_token_stream()), + Err(e) => Err(error!("{}", e)), + } +} diff --git a/git-version-macro/src/lib.rs b/git-version-macro/src/lib.rs index 8fb5a37..265ffe5 100644 --- a/git-version-macro/src/lib.rs +++ b/git-version-macro/src/lib.rs @@ -6,7 +6,7 @@ use syn::parse::{Parse, ParseStream}; use syn::punctuated::Punctuated; use syn::token::{Comma, Eq}; use syn::{Expr, Ident, LitStr}; - +pub(crate) mod describe_submodules; mod utils; use self::utils::{describe_cwd, git_dir_cwd}; @@ -17,8 +17,7 @@ macro_rules! error { } fn canonicalize_path(path: &Path) -> syn::Result { - path - .canonicalize() + path.canonicalize() .map_err(|e| error!("failed to canonicalize {}: {}", path.display(), e))? .into_os_string() .into_string() @@ -29,12 +28,18 @@ fn canonicalize_path(path: &Path) -> syn::Result { fn git_dependencies() -> syn::Result { let git_dir = git_dir_cwd().map_err(|e| error!("failed to determine .git directory: {}", e))?; - let deps: Vec<_> = ["logs/HEAD", "index"].iter().flat_map(|&file| { - canonicalize_path(&git_dir.join(file)).map(Some).unwrap_or_else(|e| { - eprintln!("Failed to add dependency on the git state: {}. Git state changes might not trigger a rebuild.", e); - None + let deps: Vec<_> = ["logs/HEAD", "index"] + .iter() + .flat_map(|&file| { + canonicalize_path(&git_dir.join(file)).map(Some).unwrap_or_else(|e| { + eprintln!( + "Failed to add dependency on the git state: {}. Git state changes might not trigger a rebuild.", + e + ); + None + }) }) - }).collect(); + .collect(); Ok(quote! { #( include_bytes!(#deps); )* @@ -55,7 +60,9 @@ impl Parse for Args { fn parse(input: ParseStream) -> syn::Result { let mut result = Args::default(); loop { - if input.is_empty() { break; } + if input.is_empty() { + break; + } let ident: Ident = input.parse()?; let _: Eq = input.parse()?; let check_dup = |dup: bool| { @@ -94,7 +101,9 @@ impl Parse for Args { } x => Err(error!("Unexpected argument name `{}`", x))?, } - if input.is_empty() { break; } + if input.is_empty() { + break; + } let _: Comma = input.parse()?; } Ok(result) @@ -151,7 +160,7 @@ pub fn git_version(input: TokenStream) -> TokenStream { fn git_version_impl(args: Args) -> syn::Result { let git_args = args.git_args.map_or_else( || vec!["--always".to_string(), "--dirty=-modified".to_string()], - |list| list.iter().map(|x| x.value()).collect() + |list| list.iter().map(|x| x.value()).collect(), ); let cargo_fallback = args.cargo_prefix.is_some() || args.cargo_suffix.is_some(); @@ -170,20 +179,59 @@ fn git_version_impl(args: Args) -> syn::Result { if let Ok(version) = std::env::var("CARGO_PKG_VERSION") { let prefix = args.cargo_prefix.iter(); let suffix = args.cargo_suffix; - Ok(quote!( - concat!(#(#prefix,)* #version, #suffix) - )) + Ok(quote!(concat!(#(#prefix,)* #version, #suffix))) } else if let Some(fallback) = args.fallback { Ok(fallback.to_token_stream()) } else { Err(error!("Unable to get git or cargo version")) } } - Err(_) if args.fallback.is_some() => { - Ok(args.fallback.to_token_stream()) - } - Err(e) => { - Err(error!("{}", e)) - } + Err(_) if args.fallback.is_some() => Ok(args.fallback.to_token_stream()), + Err(e) => Err(error!("{}", e)), } } + +/// Get the git version for the source code. +/// +/// The following (named) arguments can be given: +/// +/// - `args`: The arguments to call `git describe` with. +/// Default: `args = ["--always", "--dirty=-modified"]` +/// +/// - `prefix`, `suffix`: +/// The git version will be prefixed/suffexed by these strings. +/// +/// - `cargo_prefix`, `cargo_suffix`: +/// If either is given, Cargo's version (given by the CARGO_PKG_VERSION +/// environment variable) will be used if git fails instead of giving an +/// error. It will be prefixed/suffixed by the given strings. +/// +/// - `fallback`: +/// If all else fails, this string will be given instead of reporting an +/// error. +/// +/// # Examples +/// +/// ```ignore +/// const VERSION: &str = git_version!(); +/// ``` +/// +/// ```ignore +/// const VERSION: &str = git_version!(args = ["--abbrev=40", "--always"]); +/// ``` +/// +/// ``` +/// # use git_version::git_version; +/// const VERSION: &str = git_version!(prefix = "git:", cargo_prefix = "cargo:", fallback = "unknown"); +/// ``` +#[proc_macro] +pub fn git_version_modules(input: TokenStream) -> TokenStream { + let args = parse_macro_input!(input as describe_submodules::GitModArgs); + + let tokens = match describe_submodules::git_version_modules_impl(args) { + Ok(x) => x, + Err(e) => e.to_compile_error(), + }; + + TokenStream::from(tokens) +} diff --git a/git-version-macro/src/utils.rs b/git-version-macro/src/utils.rs index b650ea6..1069f0a 100644 --- a/git-version-macro/src/utils.rs +++ b/git-version-macro/src/utils.rs @@ -34,8 +34,8 @@ fn run_git(program: &str, command: &mut std::process::Command) -> Result Result // If the command terminated with non-zero exit code, return an error. } else if let Some(status) = output.status.code() { // Include the first line of stderr in the error message, if it's valid UTF-8 and not empty. - let message = output.stderr.split(|c| *c == b'\n') + let message = output + .stderr + .split(|c| *c == b'\n') .next() .and_then(|x| std::str::from_utf8(x).ok()) .filter(|x| !x.is_empty()); @@ -80,10 +82,24 @@ fn strip_trailing_newline(mut input: Vec) -> Vec { input } +/// Run `git describe` for submodules in the current working directory with custom flags to get version information from git. +pub fn describe_modules(args: I) -> std::io::Result +where + I: IntoIterator, + S: AsRef, +{ + let cmd = Command::new("git").args(args).output()?; + + let output = verbose_command_error("git submodule", cmd)?; + let output = strip_trailing_newline(output.stdout); + + Ok(String::from_utf8_lossy(&output).to_string()) +} + #[test] fn test_git_dir() { - use std::path::Path; use assert2::{assert, let_assert}; + use std::path::Path; let_assert!(Ok(git_dir) = git_dir_cwd()); let_assert!(Ok(git_dir) = git_dir.canonicalize()); diff --git a/src/lib.rs b/src/lib.rs index 310dbd4..1c1e622 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -13,7 +13,7 @@ //! These macros do not depend on libgit, but simply uses the `git` binary directly. //! So you must have `git` installed somewhere in your `PATH`. -pub use git_version_macro::git_version; +pub use git_version_macro::{git_version, git_version_modules}; /// Run `git describe` at compile time with custom flags. /// diff --git a/tests/version.rs b/tests/version.rs index d81fc8a..553a8a7 100644 --- a/tests/version.rs +++ b/tests/version.rs @@ -1,4 +1,4 @@ -use git_version::{git_describe, git_version}; +use git_version::{git_describe, git_version, git_version_modules}; #[test] fn git_describe_is_right() { @@ -14,3 +14,13 @@ fn git_describe_is_right() { assert_eq!(git_describe!("--always", "--dirty=-modified"), name); assert_eq!(git_version!(prefix = "[", suffix = "]"), format!("[{}]", name)); } + +#[test] +fn test_modules_macro_gives_expected_output() { + let module_versions = git_version_modules!( + prefix = "pre-", + suffix = "-suff", + describe_args = ["--always", "--dirty=-modified", "--tags"] + ); + println!("{module_versions}"); +} From ea2884f35e807afe2d1c7aa56d11355c4f464c70 Mon Sep 17 00:00:00 2001 From: Jordan Baxter Date: Sun, 5 Nov 2023 15:40:51 -0800 Subject: [PATCH 06/21] Added docs. --- git-version-macro/src/lib.rs | 35 +++++++++++++++++++++-------------- 1 file changed, 21 insertions(+), 14 deletions(-) diff --git a/git-version-macro/src/lib.rs b/git-version-macro/src/lib.rs index 265ffe5..80d0d07 100644 --- a/git-version-macro/src/lib.rs +++ b/git-version-macro/src/lib.rs @@ -191,20 +191,27 @@ fn git_version_impl(args: Args) -> syn::Result { } } -/// Get the git version for the source code. +/// Get the git version for submodules below the cargo project. +/// +/// This is achieved by running `git foreach` in tandem with `git describe`. +/// The arguments for both git commands are exposed as macro arguments. +/// +/// The each line of text output for this macro will be formatted as follows: +/// ```bash +/// relative/path/to/submodule : {prefix}{git_describe_output_for_submodule}{suffix} +/// ``` /// /// The following (named) arguments can be given: /// -/// - `args`: The arguments to call `git describe` with. +/// - `foreach_args`: The arguments to call `git submodule foreach` with. +/// Default: `args = ["--quiet", "--recursive"]` +/// +/// - `describe_args`: The arguments to call `git describe` with. /// Default: `args = ["--always", "--dirty=-modified"]` /// /// - `prefix`, `suffix`: -/// The git version will be prefixed/suffexed by these strings. -/// -/// - `cargo_prefix`, `cargo_suffix`: -/// If either is given, Cargo's version (given by the CARGO_PKG_VERSION -/// environment variable) will be used if git fails instead of giving an -/// error. It will be prefixed/suffixed by the given strings. +/// The git version for each submodule will be prefixed/suffixed +/// by these strings. /// /// - `fallback`: /// If all else fails, this string will be given instead of reporting an @@ -212,17 +219,17 @@ fn git_version_impl(args: Args) -> syn::Result { /// /// # Examples /// -/// ```ignore -/// const VERSION: &str = git_version!(); +/// ``` +/// const VERSION: &str = git_version_modules!(); /// ``` /// -/// ```ignore -/// const VERSION: &str = git_version!(args = ["--abbrev=40", "--always"]); +/// ``` +/// const VERSION: &str = git_version_modules!(describe_args = ["--abbrev=40", "--always"]); /// ``` /// /// ``` -/// # use git_version::git_version; -/// const VERSION: &str = git_version!(prefix = "git:", cargo_prefix = "cargo:", fallback = "unknown"); +/// # use git_version::git_version_modules; +/// const VERSION: &str = git_version_modules!(prefix = "git:", cargo_prefix = "cargo:", fallback = "unknown"); /// ``` #[proc_macro] pub fn git_version_modules(input: TokenStream) -> TokenStream { From da3a7b4b2c759dc15293fe4ca24fbc653d9a8f46 Mon Sep 17 00:00:00 2001 From: Jordan Baxter Date: Sun, 19 Nov 2023 10:44:06 -0800 Subject: [PATCH 07/21] Trying to tokenize a tuple of &str. --- git-version-macro/src/describe_submodules.rs | 13 +++++++---- git-version-macro/src/utils.rs | 24 ++++++++++++++++---- 2 files changed, 28 insertions(+), 9 deletions(-) diff --git a/git-version-macro/src/describe_submodules.rs b/git-version-macro/src/describe_submodules.rs index 4890232..4b869e6 100644 --- a/git-version-macro/src/describe_submodules.rs +++ b/git-version-macro/src/describe_submodules.rs @@ -1,9 +1,9 @@ extern crate proc_macro; - use crate::git_dependencies; use crate::utils::describe_modules; use proc_macro2::{Span, TokenStream as TokenStream2}; use quote::{quote, ToTokens}; +use syn::TypeTuple; use syn::{ bracketed, parse::{Parse, ParseStream}, @@ -99,19 +99,22 @@ pub(crate) fn git_version_modules_impl(args: GitModArgs) -> syn::Result "".to_string(), }; - let descibe_args = format!("echo $displaypath : {prefix}`git describe {}`{suffix}", git_describe_args.join(" ")); - // let descibe_args = format!("echo $displaypath : `git describe {}`", git_describe_args.join(" ")); + let descibe_args = format!("echo $displaypath:`git describe {}`", git_describe_args.join(" ")); let mut git_args: Vec = vec!["submodule".to_string(), "foreach".to_string()]; git_args.append(&mut git_foreach_args); git_args.push(descibe_args); - match describe_modules(&git_args) { + match describe_modules(&git_args, prefix, suffix) { Ok(version) => { let dependencies = git_dependencies()?; + let mut version_tokenable: Vec<(&str, &str)> = vec![]; + for line in version.into_iter() { + version_tokenable.push((line.0.as_str(), line.1.as_str())); + } Ok(quote!({ #dependencies; - #version + [#(#version_tokenable),*]; })) } Err(_) if args.fallback.is_some() => Ok(args.fallback.to_token_stream()), diff --git a/git-version-macro/src/utils.rs b/git-version-macro/src/utils.rs index 1069f0a..3a532ac 100644 --- a/git-version-macro/src/utils.rs +++ b/git-version-macro/src/utils.rs @@ -1,4 +1,5 @@ use std::ffi::OsStr; +use std::io::{Error, ErrorKind}; use std::path::PathBuf; use std::process::Command; @@ -83,17 +84,32 @@ fn strip_trailing_newline(mut input: Vec) -> Vec { } /// Run `git describe` for submodules in the current working directory with custom flags to get version information from git. -pub fn describe_modules(args: I) -> std::io::Result +pub fn describe_modules(args: I, prefix: String, suffix: String) -> std::io::Result> where I: IntoIterator, S: AsRef, { let cmd = Command::new("git").args(args).output()?; - let output = verbose_command_error("git submodule", cmd)?; - let output = strip_trailing_newline(output.stdout); + let cmd_output = verbose_command_error("git submodule", cmd)?; + let cmd_output = strip_trailing_newline(cmd_output.stdout); - Ok(String::from_utf8_lossy(&output).to_string()) + let mut output: Vec<(String, String)> = vec![]; + + for line in String::from_utf8_lossy(&cmd_output).to_string().split("\n") { + let line_split: Vec<&str> = line.split(':').collect(); + + if line_split.len() != 2 { + return Err(Error::new( + ErrorKind::Other, + format!("Git command returned unexpected result: {line}"), + )); + } + output.push((line_split[0].to_string(), line_split[1].to_string())); + // output.push(line.split(":").collect()); + } + + Ok(output) } #[test] From 043f3876d354afc8587a32f72bc8a6e4aef999ba Mon Sep 17 00:00:00 2001 From: Jordan Baxter Date: Sun, 19 Nov 2023 12:17:39 -0800 Subject: [PATCH 08/21] Finished array creation. --- git-version-macro/src/describe_submodules.rs | 29 ++++++++++++++++---- git-version-macro/src/lib.rs | 21 +++++++------- git-version-macro/src/utils.rs | 25 ++--------------- tests/version.rs | 2 +- 4 files changed, 36 insertions(+), 41 deletions(-) diff --git a/git-version-macro/src/describe_submodules.rs b/git-version-macro/src/describe_submodules.rs index 4b869e6..a5aaf14 100644 --- a/git-version-macro/src/describe_submodules.rs +++ b/git-version-macro/src/describe_submodules.rs @@ -3,7 +3,6 @@ use crate::git_dependencies; use crate::utils::describe_modules; use proc_macro2::{Span, TokenStream as TokenStream2}; use quote::{quote, ToTokens}; -use syn::TypeTuple; use syn::{ bracketed, parse::{Parse, ParseStream}, @@ -90,6 +89,10 @@ pub(crate) fn git_version_modules_impl(args: GitModArgs) -> syn::Result x.value(), _ => "".to_string(), @@ -105,16 +108,30 @@ pub(crate) fn git_version_modules_impl(args: GitModArgs) -> syn::Result { let dependencies = git_dependencies()?; - let mut version_tokenable: Vec<(&str, &str)> = vec![]; - for line in version.into_iter() { - version_tokenable.push((line.0.as_str(), line.1.as_str())); + + let mut output: Vec> = vec![]; + let newline_split = version.split('\n'); + + for line in newline_split { + let line = line.to_string(); + let line_split: Vec<&str> = line.split(':').collect(); + assert!( + line_split.len() == 2, + // NOTE: I Don't love this split, but I think I have protected against weirdness + // by adding the only arbitrary text allowed (prefix, suffix) after the split happens. + // so unless people are using colons in their tag names, it should be fine. + "Found an unexpected colon ':' in git describe output - {}", + version + ); + output.push(vec![line_split[0].to_string(), format!("{}{}{}", prefix, line_split[1], suffix)]) } + Ok(quote!({ #dependencies; - [#(#version_tokenable),*]; + [#([#(#output),*]),*] })) } Err(_) if args.fallback.is_some() => Ok(args.fallback.to_token_stream()), diff --git a/git-version-macro/src/lib.rs b/git-version-macro/src/lib.rs index 80d0d07..5015fd2 100644 --- a/git-version-macro/src/lib.rs +++ b/git-version-macro/src/lib.rs @@ -196,18 +196,17 @@ fn git_version_impl(args: Args) -> syn::Result { /// This is achieved by running `git foreach` in tandem with `git describe`. /// The arguments for both git commands are exposed as macro arguments. /// -/// The each line of text output for this macro will be formatted as follows: -/// ```bash -/// relative/path/to/submodule : {prefix}{git_describe_output_for_submodule}{suffix} -/// ``` +/// This macro expands to a 2D array that looks like the following: +/// +/// `[["relative/path/to/submodule", "{prefix}{git_describe_output}{suffix}"]]` /// /// The following (named) arguments can be given: /// -/// - `foreach_args`: The arguments to call `git submodule foreach` with. -/// Default: `args = ["--quiet", "--recursive"]` +/// - `foreach_args`: The arguments to call `git submodule foreach` with. Default: `foreach_args = ["--quiet", "--recursive"]` +/// - NOTE: `"--quiet"` is a required argument. If `"--quiet"` is not in the list of provided args, it will be added automatically. /// /// - `describe_args`: The arguments to call `git describe` with. -/// Default: `args = ["--always", "--dirty=-modified"]` +/// Default: `describe_args = ["--always", "--dirty=-modified"]` /// /// - `prefix`, `suffix`: /// The git version for each submodule will be prefixed/suffixed @@ -220,20 +219,20 @@ fn git_version_impl(args: Args) -> syn::Result { /// # Examples /// /// ``` -/// const VERSION: &str = git_version_modules!(); +/// const MODULE_VERSIONS: [[&str, 2], 4] = git_version_modules!(); /// ``` /// /// ``` -/// const VERSION: &str = git_version_modules!(describe_args = ["--abbrev=40", "--always"]); +/// const MODULE_VERSIONS: [[&str, 2], 4] = git_version_modules!(describe_args = ["--abbrev=40", "--always"]); /// ``` /// /// ``` /// # use git_version::git_version_modules; -/// const VERSION: &str = git_version_modules!(prefix = "git:", cargo_prefix = "cargo:", fallback = "unknown"); +/// const MODULE_VERSIONS: [[&str, 2], 4] = git_version_modules!(prefix = "git:", fallback = "unknown"); /// ``` #[proc_macro] pub fn git_version_modules(input: TokenStream) -> TokenStream { - let args = parse_macro_input!(input as describe_submodules::GitModArgs); + let args = syn::parse_macro_input!(input as describe_submodules::GitModArgs); let tokens = match describe_submodules::git_version_modules_impl(args) { Ok(x) => x, diff --git a/git-version-macro/src/utils.rs b/git-version-macro/src/utils.rs index 3a532ac..9d74737 100644 --- a/git-version-macro/src/utils.rs +++ b/git-version-macro/src/utils.rs @@ -1,5 +1,4 @@ use std::ffi::OsStr; -use std::io::{Error, ErrorKind}; use std::path::PathBuf; use std::process::Command; @@ -84,32 +83,12 @@ fn strip_trailing_newline(mut input: Vec) -> Vec { } /// Run `git describe` for submodules in the current working directory with custom flags to get version information from git. -pub fn describe_modules(args: I, prefix: String, suffix: String) -> std::io::Result> +pub fn describe_modules(args: I) -> Result where I: IntoIterator, S: AsRef, { - let cmd = Command::new("git").args(args).output()?; - - let cmd_output = verbose_command_error("git submodule", cmd)?; - let cmd_output = strip_trailing_newline(cmd_output.stdout); - - let mut output: Vec<(String, String)> = vec![]; - - for line in String::from_utf8_lossy(&cmd_output).to_string().split("\n") { - let line_split: Vec<&str> = line.split(':').collect(); - - if line_split.len() != 2 { - return Err(Error::new( - ErrorKind::Other, - format!("Git command returned unexpected result: {line}"), - )); - } - output.push((line_split[0].to_string(), line_split[1].to_string())); - // output.push(line.split(":").collect()); - } - - Ok(output) + run_git("git submodule", Command::new("git").args(args)) } #[test] diff --git a/tests/version.rs b/tests/version.rs index 553a8a7..18a93f4 100644 --- a/tests/version.rs +++ b/tests/version.rs @@ -22,5 +22,5 @@ fn test_modules_macro_gives_expected_output() { suffix = "-suff", describe_args = ["--always", "--dirty=-modified", "--tags"] ); - println!("{module_versions}"); + println!("{module_versions:?}"); } From 0d4e1ef089eb58ff4839e0f1e77a13e8a9ba081c Mon Sep 17 00:00:00 2001 From: Jordan Baxter Date: Wed, 22 Nov 2023 12:14:23 -0800 Subject: [PATCH 09/21] Working on final quote block./ --- git-version-macro/src/describe_submodules.rs | 118 ++++++++++++------- git-version-macro/src/utils.rs | 11 +- 2 files changed, 76 insertions(+), 53 deletions(-) diff --git a/git-version-macro/src/describe_submodules.rs b/git-version-macro/src/describe_submodules.rs index a5aaf14..0ff710e 100644 --- a/git-version-macro/src/describe_submodules.rs +++ b/git-version-macro/src/describe_submodules.rs @@ -1,8 +1,12 @@ extern crate proc_macro; +use crate::canonicalize_path; use crate::git_dependencies; -use crate::utils::describe_modules; +use crate::utils::run_git; use proc_macro2::{Span, TokenStream as TokenStream2}; use quote::{quote, ToTokens}; +use std::ffi::OsStr; +use std::path::Path; +use std::process::Command; use syn::{ bracketed, parse::{Parse, ParseStream}, @@ -20,7 +24,6 @@ macro_rules! error { #[derive(Default)] pub(crate) struct GitModArgs { describe_args: Option>, - foreach_args: Option>, prefix: Option, suffix: Option, fallback: Option, @@ -49,12 +52,6 @@ impl Parse for GitModArgs { bracketed!(content in input); result.describe_args = Some(Punctuated::parse_terminated(&content)?); } - "foreach_args" => { - check_dup(result.foreach_args.is_some())?; - let content; - bracketed!(content in input); - result.foreach_args = Some(Punctuated::parse_terminated(&content)?); - } "prefix" => { check_dup(result.prefix.is_some())?; result.prefix = Some(input.parse()?); @@ -79,20 +76,24 @@ impl Parse for GitModArgs { } pub(crate) fn git_version_modules_impl(args: GitModArgs) -> syn::Result { + let modules = match get_modules() { + Ok(x) => x, + Err(err) => return Err(error!("{}", err)), + }; + + let mut describe_paths: Vec<(String, String)> = vec![]; + + for path in modules.into_iter() { + let path_obj = Path::new(&path); + let path_obj = canonicalize_path(&path_obj)?; + describe_paths.push((path, path_obj)); + } + let git_describe_args = args.describe_args.map_or_else( || vec!["--always".to_string(), "--dirty=-modified".to_string()], |list| list.iter().map(|x| x.value()).collect(), ); - let mut git_foreach_args = args.foreach_args.map_or_else( - || vec!["--quiet".to_string(), "--recursive".to_string()], - |list| list.iter().map(|x| x.value()).collect(), - ); - - if !git_foreach_args.contains(&"--quiet".to_string()) { - git_foreach_args.push("--quiet".to_string()) - } - let prefix = match args.prefix { Some(x) => x.value(), _ => "".to_string(), @@ -102,39 +103,70 @@ pub(crate) fn git_version_modules_impl(args: GitModArgs) -> syn::Result "".to_string(), }; - let descibe_args = format!("echo $displaypath:`git describe {}`", git_describe_args.join(" ")); - - let mut git_args: Vec = vec!["submodule".to_string(), "foreach".to_string()]; - git_args.append(&mut git_foreach_args); - git_args.push(descibe_args); - - match describe_modules(&git_args) { + match describe_modules(describe_paths, &git_describe_args, prefix, suffix) { Ok(version) => { let dependencies = git_dependencies()?; - - let mut output: Vec> = vec![]; - let newline_split = version.split('\n'); - - for line in newline_split { - let line = line.to_string(); - let line_split: Vec<&str> = line.split(':').collect(); - assert!( - line_split.len() == 2, - // NOTE: I Don't love this split, but I think I have protected against weirdness - // by adding the only arbitrary text allowed (prefix, suffix) after the split happens. - // so unless people are using colons in their tag names, it should be fine. - "Found an unexpected colon ':' in git describe output - {}", - version - ); - output.push(vec![line_split[0].to_string(), format!("{}{}{}", prefix, line_split[1], suffix)]) - } - + let i = (0..version.len()).map(syn::Index::from); Ok(quote!({ #dependencies; - [#([#(#output),*]),*] + + #[derive(Debug)] + struct SubmoduleVersion { + path: String, + version: String, + }; + + + [#(SubmoduleVersion{ + path: version[#i].0, + version: version[#i].1 + }),*] + })) } Err(_) if args.fallback.is_some() => Ok(args.fallback.to_token_stream()), Err(e) => Err(error!("{}", e)), } } + +/// Run `git submodule foreach` command to discover submodules in the project. +pub fn get_modules() -> Result, String> { + let mut args: Vec = "submodule foreach --quiet --recursive" + .to_string() + .split(" ") + .map(|x| x.to_string()) + .collect(); + + args.push("echo $displaypath".to_string()); + + let result = run_git("git submodule", Command::new("git").args(args))?; + + Ok(result.split("\n").map(|x| x.to_string()).collect()) +} + +/// Run `git describe` for each submodule to +pub fn describe_modules( + paths: Vec<(String, String)>, + describe_args: I, + prefix: String, + suffix: String, +) -> Result, String> +where + I: IntoIterator + Clone, + S: AsRef, +{ + let mut output: Vec<(String, String)> = vec![]; + + for (rel_path, abs_path) in paths.into_iter() { + let result = run_git( + "git describe", + Command::new("git") + .current_dir(abs_path) + .arg("describe") + .args(describe_args.clone()), + )?; + output.push((rel_path, format!("{}{}{}", prefix, result, suffix))); + } + + Ok(output) +} diff --git a/git-version-macro/src/utils.rs b/git-version-macro/src/utils.rs index 9d74737..5aa3271 100644 --- a/git-version-macro/src/utils.rs +++ b/git-version-macro/src/utils.rs @@ -17,7 +17,7 @@ pub fn git_dir_cwd() -> Result { Ok(PathBuf::from(path)) } -fn run_git(program: &str, command: &mut std::process::Command) -> Result { +pub(crate) fn run_git(program: &str, command: &mut std::process::Command) -> Result { let output = command .stdout(std::process::Stdio::piped()) .stderr(std::process::Stdio::piped()) @@ -82,15 +82,6 @@ fn strip_trailing_newline(mut input: Vec) -> Vec { input } -/// Run `git describe` for submodules in the current working directory with custom flags to get version information from git. -pub fn describe_modules(args: I) -> Result -where - I: IntoIterator, - S: AsRef, -{ - run_git("git submodule", Command::new("git").args(args)) -} - #[test] fn test_git_dir() { use assert2::{assert, let_assert}; From 4fdaa9376b41af85c305ca19b99418189742fbad Mon Sep 17 00:00:00 2001 From: Jordan Baxter Date: Wed, 22 Nov 2023 18:59:59 -0800 Subject: [PATCH 10/21] Added submodule version struct to output. --- git-version-macro/src/describe_submodules.rs | 37 ++++++++++---------- git-version-macro/src/lib.rs | 11 +++--- tests/version.rs | 4 +-- 3 files changed, 24 insertions(+), 28 deletions(-) diff --git a/git-version-macro/src/describe_submodules.rs b/git-version-macro/src/describe_submodules.rs index 0ff710e..108f2ba 100644 --- a/git-version-macro/src/describe_submodules.rs +++ b/git-version-macro/src/describe_submodules.rs @@ -23,7 +23,7 @@ macro_rules! error { #[derive(Default)] pub(crate) struct GitModArgs { - describe_args: Option>, + args: Option>, prefix: Option, suffix: Option, fallback: Option, @@ -46,11 +46,11 @@ impl Parse for GitModArgs { } }; match ident.to_string().as_str() { - "describe_args" => { - check_dup(result.describe_args.is_some())?; + "args" => { + check_dup(result.args.is_some())?; let content; bracketed!(content in input); - result.describe_args = Some(Punctuated::parse_terminated(&content)?); + result.args = Some(Punctuated::parse_terminated(&content)?); } "prefix" => { check_dup(result.prefix.is_some())?; @@ -85,11 +85,11 @@ pub(crate) fn git_version_modules_impl(args: GitModArgs) -> syn::Result syn::Result { + Ok(result) => { let dependencies = git_dependencies()?; - let i = (0..version.len()).map(syn::Index::from); + let (paths, versions) = result; + Ok(quote!({ #dependencies; @@ -116,11 +117,7 @@ pub(crate) fn git_version_modules_impl(args: GitModArgs) -> syn::Result syn::Result Result, String> { let mut args: Vec = "submodule foreach --quiet --recursive" .to_string() - .split(" ") + .split(' ') .map(|x| x.to_string()) .collect(); @@ -141,7 +138,7 @@ pub fn get_modules() -> Result, String> { let result = run_git("git submodule", Command::new("git").args(args))?; - Ok(result.split("\n").map(|x| x.to_string()).collect()) + Ok(result.split('\n').map(|x| x.to_string()).collect()) } /// Run `git describe` for each submodule to @@ -150,12 +147,13 @@ pub fn describe_modules( describe_args: I, prefix: String, suffix: String, -) -> Result, String> +) -> Result<(Vec, Vec), String> where I: IntoIterator + Clone, S: AsRef, { - let mut output: Vec<(String, String)> = vec![]; + let mut paths_out: Vec = vec![]; + let mut versions: Vec = vec![]; for (rel_path, abs_path) in paths.into_iter() { let result = run_git( @@ -165,8 +163,9 @@ where .arg("describe") .args(describe_args.clone()), )?; - output.push((rel_path, format!("{}{}{}", prefix, result, suffix))); + paths_out.push(rel_path); + versions.push(format!("{}{}{}", prefix, result, suffix)) } - Ok(output) + Ok((paths_out, versions)) } diff --git a/git-version-macro/src/lib.rs b/git-version-macro/src/lib.rs index 5015fd2..d0de6e5 100644 --- a/git-version-macro/src/lib.rs +++ b/git-version-macro/src/lib.rs @@ -194,18 +194,15 @@ fn git_version_impl(args: Args) -> syn::Result { /// Get the git version for submodules below the cargo project. /// /// This is achieved by running `git foreach` in tandem with `git describe`. -/// The arguments for both git commands are exposed as macro arguments. +/// The arguments for `git describe` are exposed as macro arguments. /// -/// This macro expands to a 2D array that looks like the following: +/// This macro expands to an array of SubmoduleVersion structs that are automatically imported where the macro is used: /// -/// `[["relative/path/to/submodule", "{prefix}{git_describe_output}{suffix}"]]` +/// `[SubmoduleVersion { path:"relative/path/to/submodule", version: "{prefix}{git_describe_output}{suffix}" }]` /// /// The following (named) arguments can be given: /// -/// - `foreach_args`: The arguments to call `git submodule foreach` with. Default: `foreach_args = ["--quiet", "--recursive"]` -/// - NOTE: `"--quiet"` is a required argument. If `"--quiet"` is not in the list of provided args, it will be added automatically. -/// -/// - `describe_args`: The arguments to call `git describe` with. +/// - `args`: The arguments to call `git describe` with. /// Default: `describe_args = ["--always", "--dirty=-modified"]` /// /// - `prefix`, `suffix`: diff --git a/tests/version.rs b/tests/version.rs index 18a93f4..72bbcf1 100644 --- a/tests/version.rs +++ b/tests/version.rs @@ -20,7 +20,7 @@ fn test_modules_macro_gives_expected_output() { let module_versions = git_version_modules!( prefix = "pre-", suffix = "-suff", - describe_args = ["--always", "--dirty=-modified", "--tags"] + args = ["--always", "--dirty=-modified", "--tags"] ); - println!("{module_versions:?}"); + println!("{module_versions:#?}"); } From 30d74d213d472d31c54e97d0d94b59a8fca84a6d Mon Sep 17 00:00:00 2001 From: Jordan Baxter Date: Wed, 22 Nov 2023 19:14:49 -0800 Subject: [PATCH 11/21] Removed submodule version struct. --- git-version-macro/src/describe_submodules.rs | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/git-version-macro/src/describe_submodules.rs b/git-version-macro/src/describe_submodules.rs index 108f2ba..9803453 100644 --- a/git-version-macro/src/describe_submodules.rs +++ b/git-version-macro/src/describe_submodules.rs @@ -111,13 +111,7 @@ pub(crate) fn git_version_modules_impl(args: GitModArgs) -> syn::Result syn::Result Result, String> { +fn get_modules() -> Result, String> { let mut args: Vec = "submodule foreach --quiet --recursive" .to_string() .split(' ') @@ -142,7 +136,7 @@ pub fn get_modules() -> Result, String> { } /// Run `git describe` for each submodule to -pub fn describe_modules( +fn describe_modules( paths: Vec<(String, String)>, describe_args: I, prefix: String, From fd0925dcc91c9314f054065fd7a3e7a14de4361c Mon Sep 17 00:00:00 2001 From: Jordan Baxter Date: Wed, 22 Nov 2023 19:20:14 -0800 Subject: [PATCH 12/21] Updated docs. --- git-version-macro/src/lib.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/git-version-macro/src/lib.rs b/git-version-macro/src/lib.rs index d0de6e5..7228687 100644 --- a/git-version-macro/src/lib.rs +++ b/git-version-macro/src/lib.rs @@ -196,14 +196,14 @@ fn git_version_impl(args: Args) -> syn::Result { /// This is achieved by running `git foreach` in tandem with `git describe`. /// The arguments for `git describe` are exposed as macro arguments. /// -/// This macro expands to an array of SubmoduleVersion structs that are automatically imported where the macro is used: +/// This macro expands to an array of `(&str, &str)` tuples that look like the following: /// -/// `[SubmoduleVersion { path:"relative/path/to/submodule", version: "{prefix}{git_describe_output}{suffix}" }]` +/// `[("relative/path/to/submodule", "{prefix}{git_describe_output}{suffix}")]` /// /// The following (named) arguments can be given: /// /// - `args`: The arguments to call `git describe` with. -/// Default: `describe_args = ["--always", "--dirty=-modified"]` +/// Default: `args = ["--always", "--dirty=-modified"]` /// /// - `prefix`, `suffix`: /// The git version for each submodule will be prefixed/suffixed @@ -216,16 +216,16 @@ fn git_version_impl(args: Args) -> syn::Result { /// # Examples /// /// ``` -/// const MODULE_VERSIONS: [[&str, 2], 4] = git_version_modules!(); +/// const MODULE_VERSIONS: [(&str, &str), N] = git_version_modules!(); /// ``` /// /// ``` -/// const MODULE_VERSIONS: [[&str, 2], 4] = git_version_modules!(describe_args = ["--abbrev=40", "--always"]); +/// const MODULE_VERSIONS: [(&str, &str), N] = git_version_modules!(args = ["--abbrev=40", "--always"]); /// ``` /// /// ``` /// # use git_version::git_version_modules; -/// const MODULE_VERSIONS: [[&str, 2], 4] = git_version_modules!(prefix = "git:", fallback = "unknown"); +/// const MODULE_VERSIONS: [(&str, &str), N] = git_version_modules!(prefix = "git:", fallback = "unknown"); /// ``` #[proc_macro] pub fn git_version_modules(input: TokenStream) -> TokenStream { From 0dd1bb51ca1b4d9d315b95880ff91573b2f4e467 Mon Sep 17 00:00:00 2001 From: Jordan Baxter Date: Mon, 27 Nov 2023 21:08:33 -0800 Subject: [PATCH 13/21] Renamed macro and added more effective test. --- git-version-macro/src/describe_submodules.rs | 4 +- git-version-macro/src/lib.rs | 4 +- src/lib.rs | 2 +- tests/version.rs | 41 ++++++++++++++++---- 4 files changed, 39 insertions(+), 12 deletions(-) diff --git a/git-version-macro/src/describe_submodules.rs b/git-version-macro/src/describe_submodules.rs index 9803453..67f8710 100644 --- a/git-version-macro/src/describe_submodules.rs +++ b/git-version-macro/src/describe_submodules.rs @@ -75,7 +75,7 @@ impl Parse for GitModArgs { } } -pub(crate) fn git_version_modules_impl(args: GitModArgs) -> syn::Result { +pub(crate) fn git_module_versions_impl(args: GitModArgs) -> syn::Result { let modules = match get_modules() { Ok(x) => x, Err(err) => return Err(error!("{}", err)), @@ -135,7 +135,7 @@ fn get_modules() -> Result, String> { Ok(result.split('\n').map(|x| x.to_string()).collect()) } -/// Run `git describe` for each submodule to +/// Run `git describe` for each submodule to get the git version with the specified args. fn describe_modules( paths: Vec<(String, String)>, describe_args: I, diff --git a/git-version-macro/src/lib.rs b/git-version-macro/src/lib.rs index 7228687..ddae1e0 100644 --- a/git-version-macro/src/lib.rs +++ b/git-version-macro/src/lib.rs @@ -228,10 +228,10 @@ fn git_version_impl(args: Args) -> syn::Result { /// const MODULE_VERSIONS: [(&str, &str), N] = git_version_modules!(prefix = "git:", fallback = "unknown"); /// ``` #[proc_macro] -pub fn git_version_modules(input: TokenStream) -> TokenStream { +pub fn git_module_versions(input: TokenStream) -> TokenStream { let args = syn::parse_macro_input!(input as describe_submodules::GitModArgs); - let tokens = match describe_submodules::git_version_modules_impl(args) { + let tokens = match describe_submodules::git_module_versions_impl(args) { Ok(x) => x, Err(e) => e.to_compile_error(), }; diff --git a/src/lib.rs b/src/lib.rs index 1c1e622..260df3a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -13,7 +13,7 @@ //! These macros do not depend on libgit, but simply uses the `git` binary directly. //! So you must have `git` installed somewhere in your `PATH`. -pub use git_version_macro::{git_version, git_version_modules}; +pub use git_version_macro::{git_module_versions, git_version}; /// Run `git describe` at compile time with custom flags. /// diff --git a/tests/version.rs b/tests/version.rs index 72bbcf1..d6d3fbd 100644 --- a/tests/version.rs +++ b/tests/version.rs @@ -1,4 +1,4 @@ -use git_version::{git_describe, git_version, git_version_modules}; +use git_version::{git_describe, git_module_versions, git_version}; #[test] fn git_describe_is_right() { @@ -17,10 +17,37 @@ fn git_describe_is_right() { #[test] fn test_modules_macro_gives_expected_output() { - let module_versions = git_version_modules!( - prefix = "pre-", - suffix = "-suff", - args = ["--always", "--dirty=-modified", "--tags"] - ); - println!("{module_versions:#?}"); + let vec = std::process::Command::new("git") + .args(["submodule", "foreach", "--quiet", "--recursive", "echo $displaypath"]) + .output() + .expect("failed to execute git") + .stdout; + let submodules: Vec = String::from_utf8(vec) + .expect("Failed to gather submodules for test") + .trim_end() + .to_string() + .split("\n") + .map(|str| str.to_string()) + .collect(); + + let mut expected_result: Vec<(String, String)> = vec![]; + for submodule in submodules.into_iter() { + let abs_path = std::fs::canonicalize(submodule.clone()).expect("Failed to canonicalize submodule path in test"); + let vec = std::process::Command::new("git") + .current_dir(abs_path) + .args(["describe", "--always", "--dirty=-modified"]) + .output() + .expect("failed to execute git") + .stdout; + let name = std::str::from_utf8(&vec[..vec.len() - 1]).expect("non-utf8 error?!"); + expected_result.push((submodule.clone(), name.to_string())) + } + + let boxed_slice: Box<[(&str, &str)]> = expected_result + .iter() + .map(|(path, version)| (path.as_str(), version.as_str())) + .collect::>() + .into_boxed_slice(); + + assert_eq!(*boxed_slice, git_module_versions!(args = ["--always", "--dirty=-modified"])); } From 111b54e363cb474d1cfc2d5f09582c1c5d00801f Mon Sep 17 00:00:00 2001 From: Jordan Baxter Date: Mon, 27 Nov 2023 21:08:39 -0800 Subject: [PATCH 14/21] Removed submodules. --- .gitmodules | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/.gitmodules b/.gitmodules index f5ef52d..8b13789 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,6 +1 @@ -[submodule "test-child-repo"] - path = test-child-repo - url = git@github.com:baxterjo/test-child-repo.git -[submodule "test_outer_directory/test-child-repo"] - path = test_outer_directory/test-child-repo - url = git@github.com:baxterjo/test-child-repo.git + From f5ba079cf537cc43119bf28dc36e647b10e56ab9 Mon Sep 17 00:00:00 2001 From: Jordan Baxter Date: Mon, 27 Nov 2023 21:09:51 -0800 Subject: [PATCH 15/21] Removed submodule. --- test-child-repo | 1 - test_outer_directory/test-child-repo | 1 - 2 files changed, 2 deletions(-) delete mode 160000 test-child-repo delete mode 160000 test_outer_directory/test-child-repo diff --git a/test-child-repo b/test-child-repo deleted file mode 160000 index da418ba..0000000 --- a/test-child-repo +++ /dev/null @@ -1 +0,0 @@ -Subproject commit da418ba0080659fdea1e883dc192ad8a827de337 diff --git a/test_outer_directory/test-child-repo b/test_outer_directory/test-child-repo deleted file mode 160000 index da418ba..0000000 --- a/test_outer_directory/test-child-repo +++ /dev/null @@ -1 +0,0 @@ -Subproject commit da418ba0080659fdea1e883dc192ad8a827de337 From 0e92ea6e7b02b29a2927dd96889164ab180a78c5 Mon Sep 17 00:00:00 2001 From: Jordan Baxter Date: Mon, 27 Nov 2023 21:41:48 -0800 Subject: [PATCH 16/21] Fixed up for projects without submodules. --- .gitmodules | 1 - git-version-macro/src/describe_submodules.rs | 9 +++++---- git-version-macro/src/lib.rs | 11 +++++++---- tests/version.rs | 10 ++++++++-- 4 files changed, 20 insertions(+), 11 deletions(-) delete mode 100644 .gitmodules diff --git a/.gitmodules b/.gitmodules deleted file mode 100644 index 8b13789..0000000 --- a/.gitmodules +++ /dev/null @@ -1 +0,0 @@ - diff --git a/git-version-macro/src/describe_submodules.rs b/git-version-macro/src/describe_submodules.rs index 67f8710..28e6253 100644 --- a/git-version-macro/src/describe_submodules.rs +++ b/git-version-macro/src/describe_submodules.rs @@ -3,7 +3,7 @@ use crate::canonicalize_path; use crate::git_dependencies; use crate::utils::run_git; use proc_macro2::{Span, TokenStream as TokenStream2}; -use quote::{quote, ToTokens}; +use quote::quote; use std::ffi::OsStr; use std::path::Path; use std::process::Command; @@ -76,11 +76,12 @@ impl Parse for GitModArgs { } pub(crate) fn git_module_versions_impl(args: GitModArgs) -> syn::Result { - let modules = match get_modules() { + let mut modules = match get_modules() { Ok(x) => x, Err(err) => return Err(error!("{}", err)), }; - + modules.retain(|path| path != ""); + println!("modules:{:?}", modules); let mut describe_paths: Vec<(String, String)> = vec![]; for path in modules.into_iter() { @@ -115,7 +116,7 @@ pub(crate) fn git_module_versions_impl(args: GitModArgs) -> syn::Result Ok(args.fallback.to_token_stream()), + Err(_) if args.fallback.is_some() => Ok(quote!([("fallback", args.fallback)])), Err(e) => Err(error!("{}", e)), } } diff --git a/git-version-macro/src/lib.rs b/git-version-macro/src/lib.rs index ddae1e0..f68f4dc 100644 --- a/git-version-macro/src/lib.rs +++ b/git-version-macro/src/lib.rs @@ -193,10 +193,12 @@ fn git_version_impl(args: Args) -> syn::Result { /// Get the git version for submodules below the cargo project. /// -/// This is achieved by running `git foreach` in tandem with `git describe`. -/// The arguments for `git describe` are exposed as macro arguments. +/// This macro will not infer type if there are no submodules in the project. /// -/// This macro expands to an array of `(&str, &str)` tuples that look like the following: +/// This macro expands to `[(&str, &str), N]` where `N` is the total number of +/// submodules below the root of the project (evaluated recursively) +/// +/// The format of the array is as follows: /// /// `[("relative/path/to/submodule", "{prefix}{git_describe_output}{suffix}")]` /// @@ -211,7 +213,8 @@ fn git_version_impl(args: Args) -> syn::Result { /// /// - `fallback`: /// If all else fails, this string will be given instead of reporting an -/// error. +/// error. This will yield the same type as if the macro was a success, but +/// format will be `[("fallback", fallback_argument)]` /// /// # Examples /// diff --git a/tests/version.rs b/tests/version.rs index d6d3fbd..34c8642 100644 --- a/tests/version.rs +++ b/tests/version.rs @@ -16,13 +16,14 @@ fn git_describe_is_right() { } #[test] +#[ignore = "Remove this ignore when there are submodules in the project"] fn test_modules_macro_gives_expected_output() { let vec = std::process::Command::new("git") .args(["submodule", "foreach", "--quiet", "--recursive", "echo $displaypath"]) .output() .expect("failed to execute git") .stdout; - let submodules: Vec = String::from_utf8(vec) + let mut submodules: Vec = String::from_utf8(vec) .expect("Failed to gather submodules for test") .trim_end() .to_string() @@ -30,6 +31,8 @@ fn test_modules_macro_gives_expected_output() { .map(|str| str.to_string()) .collect(); + submodules.retain(|path| path != ""); + let mut expected_result: Vec<(String, String)> = vec![]; for submodule in submodules.into_iter() { let abs_path = std::fs::canonicalize(submodule.clone()).expect("Failed to canonicalize submodule path in test"); @@ -49,5 +52,8 @@ fn test_modules_macro_gives_expected_output() { .collect::>() .into_boxed_slice(); - assert_eq!(*boxed_slice, git_module_versions!(args = ["--always", "--dirty=-modified"])); + assert_eq!( + *boxed_slice, + git_module_versions!(args = ["--always", "--dirty=-modified"], fallback = "no submodules") + ); } From dc059b8dda1e897302f11c74ed7db8b84a76cbf2 Mon Sep 17 00:00:00 2001 From: Jordan Baxter <73496400+baxterjo@users.noreply.github.com> Date: Thu, 7 Dec 2023 17:49:04 -0800 Subject: [PATCH 17/21] Update git-version-macro/src/describe_submodules.rs Co-authored-by: Maarten de Vries --- git-version-macro/src/describe_submodules.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/git-version-macro/src/describe_submodules.rs b/git-version-macro/src/describe_submodules.rs index 28e6253..fb7c551 100644 --- a/git-version-macro/src/describe_submodules.rs +++ b/git-version-macro/src/describe_submodules.rs @@ -81,7 +81,6 @@ pub(crate) fn git_module_versions_impl(args: GitModArgs) -> syn::Result return Err(error!("{}", err)), }; modules.retain(|path| path != ""); - println!("modules:{:?}", modules); let mut describe_paths: Vec<(String, String)> = vec![]; for path in modules.into_iter() { From b79c25b0e9cec3626a07bc39ad0145efbea4968e Mon Sep 17 00:00:00 2001 From: Jordan Baxter <73496400+baxterjo@users.noreply.github.com> Date: Thu, 7 Dec 2023 17:49:24 -0800 Subject: [PATCH 18/21] Update git-version-macro/src/describe_submodules.rs Co-authored-by: Maarten de Vries --- git-version-macro/src/describe_submodules.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/git-version-macro/src/describe_submodules.rs b/git-version-macro/src/describe_submodules.rs index fb7c551..e90169f 100644 --- a/git-version-macro/src/describe_submodules.rs +++ b/git-version-macro/src/describe_submodules.rs @@ -83,7 +83,7 @@ pub(crate) fn git_module_versions_impl(args: GitModArgs) -> syn::Result = vec![]; - for path in modules.into_iter() { + for path in modules { let path_obj = Path::new(&path); let path_obj = canonicalize_path(path_obj)?; describe_paths.push((path, path_obj)); From c6f8ba91f5c808cc8d0fa699dd25e9b161499fb9 Mon Sep 17 00:00:00 2001 From: Jordan Baxter Date: Mon, 27 Nov 2023 21:43:11 -0800 Subject: [PATCH 19/21] Removed ignore. --- tests/version.rs | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/tests/version.rs b/tests/version.rs index 34c8642..428d138 100644 --- a/tests/version.rs +++ b/tests/version.rs @@ -16,7 +16,6 @@ fn git_describe_is_right() { } #[test] -#[ignore = "Remove this ignore when there are submodules in the project"] fn test_modules_macro_gives_expected_output() { let vec = std::process::Command::new("git") .args(["submodule", "foreach", "--quiet", "--recursive", "echo $displaypath"]) @@ -52,8 +51,5 @@ fn test_modules_macro_gives_expected_output() { .collect::>() .into_boxed_slice(); - assert_eq!( - *boxed_slice, - git_module_versions!(args = ["--always", "--dirty=-modified"], fallback = "no submodules") - ); + assert_eq!(*boxed_slice, git_module_versions!(args = ["--always", "--dirty=-modified"])); } From 386a08e7b573358805b893d62c52bb7f1903ac86 Mon Sep 17 00:00:00 2001 From: Jordan Baxter Date: Fri, 8 Dec 2023 09:33:52 -0800 Subject: [PATCH 20/21] Added fallback on a per-submodule basis. --- git-version-macro/src/describe_submodules.rs | 25 ++++++++++++++------ 1 file changed, 18 insertions(+), 7 deletions(-) diff --git a/git-version-macro/src/describe_submodules.rs b/git-version-macro/src/describe_submodules.rs index e90169f..e35fe22 100644 --- a/git-version-macro/src/describe_submodules.rs +++ b/git-version-macro/src/describe_submodules.rs @@ -12,7 +12,7 @@ use syn::{ parse::{Parse, ParseStream}, punctuated::Punctuated, token::{Comma, Eq}, - Expr, Ident, LitStr, + Ident, LitStr, }; macro_rules! error { @@ -26,7 +26,7 @@ pub(crate) struct GitModArgs { args: Option>, prefix: Option, suffix: Option, - fallback: Option, + fallback: Option, } impl Parse for GitModArgs { @@ -80,7 +80,9 @@ pub(crate) fn git_module_versions_impl(args: GitModArgs) -> syn::Result x, Err(err) => return Err(error!("{}", err)), }; - modules.retain(|path| path != ""); + + modules.retain(|path| !path.is_empty()); + let mut describe_paths: Vec<(String, String)> = vec![]; for path in modules { @@ -102,8 +104,9 @@ pub(crate) fn git_module_versions_impl(args: GitModArgs) -> syn::Result x.value(), _ => "".to_string(), }; + let fallback = args.fallback.map(|x| x.value()); - match describe_modules(describe_paths, &git_describe_args, prefix, suffix) { + match describe_modules(describe_paths, &git_describe_args, prefix, suffix, fallback) { Ok(result) => { let dependencies = git_dependencies()?; let (paths, versions) = result; @@ -115,7 +118,6 @@ pub(crate) fn git_module_versions_impl(args: GitModArgs) -> syn::Result Ok(quote!([("fallback", args.fallback)])), Err(e) => Err(error!("{}", e)), } } @@ -141,6 +143,7 @@ fn describe_modules( describe_args: I, prefix: String, suffix: String, + fallback: Option, ) -> Result<(Vec, Vec), String> where I: IntoIterator + Clone, @@ -150,13 +153,21 @@ where let mut versions: Vec = vec![]; for (rel_path, abs_path) in paths.into_iter() { - let result = run_git( + // Get the submodule version or fallback. + let result = match run_git( "git describe", Command::new("git") .current_dir(abs_path) .arg("describe") .args(describe_args.clone()), - )?; + ) { + Ok(version) => version, + Err(_git_err) if fallback.is_some() => fallback.clone().unwrap(), + Err(git_err) => { + // If git error and no fallback provided, return error. + return Err(git_err); + } + }; paths_out.push(rel_path); versions.push(format!("{}{}{}", prefix, result, suffix)) } From 59f6c355745016958d0d88b62a5b073227387116 Mon Sep 17 00:00:00 2001 From: Jordan Baxter Date: Mon, 11 Dec 2023 12:03:24 -0800 Subject: [PATCH 21/21] Corrected description of "fallback" in docs. --- git-version-macro/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/git-version-macro/src/lib.rs b/git-version-macro/src/lib.rs index f68f4dc..433a242 100644 --- a/git-version-macro/src/lib.rs +++ b/git-version-macro/src/lib.rs @@ -214,7 +214,7 @@ fn git_version_impl(args: Args) -> syn::Result { /// - `fallback`: /// If all else fails, this string will be given instead of reporting an /// error. This will yield the same type as if the macro was a success, but -/// format will be `[("fallback", fallback_argument)]` +/// format will be `[("relative/path/to/submodule", {fallback})]` /// /// # Examples ///