From: Bruno Haible Date: Fri, 2 May 2025 14:06:45 +0000 (+0200) Subject: xgettext: Add an option to opt-out of the use of git for the POT-Creation-Date. X-Git-Tag: v0.25~30 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=26c8529d0ee30d79579024a43da2c77275ee586e;p=thirdparty%2Fgettext.git xgettext: Add an option to opt-out of the use of git for the POT-Creation-Date. Reported by Serhii Tereshchenko at . * autogen.sh (GNULIB_MODULES_TOOLS_FOR_SRC): Add stat-time. * gettext-tools/src/xgettext.c: Include stat-time.h. (xgettext_no_git): New variable. (long_options): Add option '--no-git'. (main): Handle the option --no-git. (usage): Document the option --no-git. (struct accumulator): New type, copied from gnulib/lib/vc-mtime.c. (accumulate): New function, copied from gnulib/lib/vc-mtime.c. (max_mtime_without_git): New function, based on gnulib/lib/vc-mtime.c. (finalize_header): Conditionally invoke max_mtime_without_git instead of max_vc_mtime. * gettext-tools/doc/xgettext.texi: Document the option --no-git. * NEWS: Mention the change. --- diff --git a/NEWS b/NEWS index 236f65db2..5c96e0024 100644 --- a/NEWS +++ b/NEWS @@ -29,7 +29,8 @@ Version 0.24.1 - May 2025 * Bug fixes: - Fix bad interactions between autoreconf and autopoint. - xgettext: Creating the POT file of a package under Git version control - is now faster. + is now faster. Also, the use of Git can be turned off by specifying + the option '--no-git'. Version 0.24 - February 2025 diff --git a/autogen.sh b/autogen.sh index df68aedef..4bcf4bc2b 100755 --- a/autogen.sh +++ b/autogen.sh @@ -246,6 +246,7 @@ if ! $skip_gnulib; then sigpipe sigprocmask spawn-pipe + stat-time stdio-h stdlib-h stpcpy diff --git a/gettext-tools/doc/xgettext.texi b/gettext-tools/doc/xgettext.texi index ccb1f42e8..59d593204 100644 --- a/gettext-tools/doc/xgettext.texi +++ b/gettext-tools/doc/xgettext.texi @@ -710,6 +710,25 @@ options at different times are guaranteed to produce the same results. Note that using this option will lead to an error if the resulting file would not entirely be in ASCII. +@item --no-git +@opindex --no-git@r{, @code{xgettext} option} +Don't use the @code{git} program +to produce a reproducible @samp{POT-Creation-Date} field in the output. + +Use this option, for speed, if +your project has a very long @code{Git} history +(hundreds of thousands of commits) +or you are specifying thousands of input files. + +By default, @code{xgettext} determines the @samp{POT-Creation-Date} as +the maximum version-controlled modification time +among all the given input files. +With this option, you can specify that it should instead use +the maximum modification time (time stamp on disk) +among all the given input files. + +By ``version control'', here we mean the @code{Git} version control system. + @item --copyright-holder=@var{string} @opindex --copyright-holder@r{, @code{xgettext} option} Set the copyright holder in the output. @var{string} should be the diff --git a/gettext-tools/src/xgettext.c b/gettext-tools/src/xgettext.c index 0c1471f8d..81daa18eb 100644 --- a/gettext-tools/src/xgettext.c +++ b/gettext-tools/src/xgettext.c @@ -80,6 +80,7 @@ #include "msgl-ascii.h" #include "msgl-ofn.h" #include "xg-check.h" +#include "stat-time.h" #include "vc-mtime.h" #include "po-time.h" #include "msgl-header.h" @@ -184,6 +185,9 @@ static catalog_output_format_ty output_syntax = &output_format_po; /* If nonzero omit header with information about this run. */ int xgettext_omit_header; +/* If nonzero, don't use 'git' to compute a reproducible POT-Creation-Date. */ +static int xgettext_no_git; + /* Be more verbose. */ int verbose = 0; @@ -281,6 +285,7 @@ static const struct option long_options[] = { "msgstr-prefix", optional_argument, NULL, 'm' }, { "msgstr-suffix", optional_argument, NULL, 'M' }, { "no-escape", no_argument, NULL, 'e' }, + { "no-git", no_argument, NULL, CHAR_MAX + 23 }, { "no-location", no_argument, NULL, CHAR_MAX + 16 }, { "no-wrap", no_argument, NULL, CHAR_MAX + 4 }, { "omit-header", no_argument, &xgettext_omit_header, 1 }, @@ -731,6 +736,10 @@ main (int argc, char *argv[]) string_list_append (&files_for_vc_mtime, optarg); break; + case CHAR_MAX + 23: /* --no-git */ + xgettext_no_git = true; + break; + default: usage (EXIT_FAILURE); /* NOTREACHED */ @@ -1291,6 +1300,10 @@ Output details:\n")); printf (_("\ --omit-header don't write header with 'msgid \"\"' entry\n")); printf (_("\ + --no-git don't use the git program to produce a\n\ + reproducible 'POT-Creation-Date' field in the\n\ + output.\n")); + printf (_("\ --copyright-holder=STRING set copyright holder in output\n")); printf (_("\ --foreign-user omit FSF copyright in output for foreign user\n")); @@ -2222,6 +2235,59 @@ FIRST AUTHOR , YEAR.\n"); return mp; } +/* Accumulating mtimes. */ +struct accumulator +{ + bool has_some_mtimes; + struct timespec max_of_mtimes; +}; + +static void +accumulate (struct accumulator *accu, struct timespec mtime) +{ + if (accu->has_some_mtimes) + { + /* Compute the maximum of accu->max_of_mtimes and mtime. */ + if (accu->max_of_mtimes.tv_sec < mtime.tv_sec + || (accu->max_of_mtimes.tv_sec == mtime.tv_sec + && accu->max_of_mtimes.tv_nsec < mtime.tv_nsec)) + accu->max_of_mtimes = mtime; + } + else + { + accu->max_of_mtimes = mtime; + accu->has_some_mtimes = true; + } +} + +static int +max_mtime_without_git (struct timespec *max_of_mtimes, + size_t nfiles, const char * const *filenames) +{ + if (nfiles == 0) + /* Invalid argument. */ + abort (); + + struct accumulator accu = { false }; + + /* Always use the file's time stamp. */ + for (size_t n = 0; n < nfiles; n++) + { + struct stat statbuf; + if (stat (filenames[n], &statbuf) < 0) + return -1; + + struct timespec mtime = get_stat_mtime (&statbuf); + accumulate (&accu, mtime); + } + + /* Since nfiles > 0, we must have accumulated at least one mtime. */ + if (!accu.has_some_mtimes) + abort (); + *max_of_mtimes = accu.max_of_mtimes; + return 0; +} + static void finalize_header (msgdomain_list_ty *mdlp) { @@ -2230,8 +2296,8 @@ finalize_header (msgdomain_list_ty *mdlp) time_t stamp; struct timespec max_of_mtimes; if (files_for_vc_mtime.nitems > 0 - && max_vc_mtime (&max_of_mtimes, - files_for_vc_mtime.nitems, files_for_vc_mtime.item) + && (xgettext_no_git ? max_mtime_without_git : max_vc_mtime) + (&max_of_mtimes, files_for_vc_mtime.nitems, files_for_vc_mtime.item) == 0) /* Use the maximum of the encountered mtimes. */ stamp = max_of_mtimes.tv_sec;