mirror of
https://https.git.savannah.gnu.org/git/gettext.git
synced 2026-01-26 15:39:11 +00:00
xgettext: Implement a "reproducible" POT-Creation-Date value.
* autogen.sh (GNULIB_MODULES_TOOLS_FOR_SRC): Add vc-mtime. * gettext-tools/src/xgettext.c: Include vc-mtime.h, msgl-header.h. (has_some_mtimes, max_of_mtimes, some_mtimes_failed): New variables. (long_options): Add --reference option. (main): Implement --reference option. Invoke consider_vc_mtime for the --files-from option. (usage): Document the --reference option. (consider_vc_mtime): New function. (read_exclusion_file, extract_from_file, extract_from_xml_file: Invoke consider_vc_mtime. (construct_header): Don't compute the value of POT-Creation-Date here. (finalize_header): Compute it here. * gettext-tools/doc/xgettext.texi: Document the --reference option. * NEWS: Mention the change.
This commit is contained in:
parent
748c4bfe2f
commit
2406f51927
4
NEWS
4
NEWS
@ -15,6 +15,10 @@ Version 0.24 - February 2025
|
||||
- A new example 'hello-c++-gnome3' has been added.
|
||||
* Ruby:
|
||||
- A new example 'hello-ruby' has been added.
|
||||
# Improvements for maintainers:
|
||||
* When xgettext creates the POT file of a package under Git version control,
|
||||
the 'POT-Creation-Date' in the POT file usually no longer changes
|
||||
gratuitously each time the POT file is regenerated.
|
||||
# Caveat maintainers:
|
||||
* Building the po/ directory now requires GNU make on specific platforms:
|
||||
macOS, Solaris, AIX.
|
||||
|
||||
@ -274,6 +274,7 @@ if ! $skip_gnulib; then
|
||||
unlocked-io
|
||||
unsetenv
|
||||
vasprintf
|
||||
vc-mtime
|
||||
wait-process
|
||||
write
|
||||
xalloc
|
||||
|
||||
@ -744,6 +744,23 @@ which the translators can contact you.
|
||||
The default value is empty, which means that translators will be clueless!
|
||||
Don't forget to specify this option.
|
||||
|
||||
@item --reference=@var{file}
|
||||
@opindex --reference@r{, @code{xgettext} option}
|
||||
|
||||
Declares that the output depends on the contents of the given FILE.
|
||||
This has an influence on the @samp{POT-Creation-Date} field in the output.
|
||||
|
||||
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
|
||||
the output depends also on some other files.
|
||||
For example, use this option when
|
||||
some of the input files is not under version control
|
||||
but instead is generated from one or more files that are under version control.
|
||||
|
||||
By ``version control'', here we mean the @code{Git} version control system.
|
||||
|
||||
@item -m[@var{string}]
|
||||
@itemx --msgstr-prefix[=@var{string}]
|
||||
@opindex -m@r{, @code{xgettext} option}
|
||||
|
||||
@ -80,7 +80,9 @@
|
||||
#include "msgl-ascii.h"
|
||||
#include "msgl-ofn.h"
|
||||
#include "xg-check.h"
|
||||
#include "vc-mtime.h"
|
||||
#include "po-time.h"
|
||||
#include "msgl-header.h"
|
||||
#include "write-catalog.h"
|
||||
#include "write-po.h"
|
||||
#include "write-properties.h"
|
||||
@ -232,6 +234,11 @@ static locating_rule_list_ty *its_locating_rules;
|
||||
/* If nonzero add comments used by itstool. */
|
||||
static bool add_itstool_comments = false;
|
||||
|
||||
/* Accumulating the version-controlled modification times of file names. */
|
||||
static bool has_some_mtimes = false;
|
||||
static struct timespec max_of_mtimes;
|
||||
static bool some_mtimes_failed = false;
|
||||
|
||||
/* Long options. */
|
||||
static const struct option long_options[] =
|
||||
{
|
||||
@ -274,6 +281,7 @@ static const struct option long_options[] =
|
||||
{ "package-version", required_argument, NULL, CHAR_MAX + 13 },
|
||||
{ "properties-output", no_argument, NULL, CHAR_MAX + 6 },
|
||||
{ "qt", no_argument, NULL, CHAR_MAX + 9 },
|
||||
{ "reference", required_argument, NULL, CHAR_MAX + 22 },
|
||||
{ "sentence-end", required_argument, NULL, CHAR_MAX + 18 },
|
||||
{ "sort-by-file", no_argument, NULL, 'F' },
|
||||
{ "sort-output", no_argument, NULL, 's' },
|
||||
@ -319,6 +327,7 @@ struct extractor_ty
|
||||
/* Forward declaration of local functions. */
|
||||
_GL_NORETURN_FUNC static void usage (int status);
|
||||
static void read_exclusion_file (char *file_name);
|
||||
static void consider_vc_mtime (const char *file_name);
|
||||
static void extract_from_file (const char *file_name, extractor_ty extractor,
|
||||
msgdomain_list_ty *mdlp);
|
||||
static void extract_from_xml_file (const char *file_name,
|
||||
@ -694,6 +703,10 @@ main (int argc, char *argv[])
|
||||
x_javascript_tag (optarg);
|
||||
break;
|
||||
|
||||
case CHAR_MAX + 22: /* --reference */
|
||||
consider_vc_mtime (optarg);
|
||||
break;
|
||||
|
||||
default:
|
||||
usage (EXIT_FAILURE);
|
||||
/* NOTREACHED */
|
||||
@ -808,7 +821,11 @@ xgettext cannot work without keywords to look for"));
|
||||
|
||||
/* Determine list of files we have to process. */
|
||||
if (files_from != NULL)
|
||||
file_list = read_names_from_file (files_from);
|
||||
{
|
||||
if (strcmp (files_from, "-") != 0)
|
||||
consider_vc_mtime (files_from);
|
||||
file_list = read_names_from_file (files_from);
|
||||
}
|
||||
else
|
||||
file_list = string_list_alloc ();
|
||||
/* Append names from command line. */
|
||||
@ -1257,6 +1274,10 @@ Output details:\n"));
|
||||
printf (_("\
|
||||
--msgid-bugs-address=EMAIL@ADDRESS set report address for msgid bugs\n"));
|
||||
printf (_("\
|
||||
--reference=FILE Declares that the output depends on the contents\n\
|
||||
of the given FILE. This has an influence on the\n\
|
||||
'POT-Creation-Date' field in the output.\n"));
|
||||
printf (_("\
|
||||
-m[STRING], --msgstr-prefix[=STRING] use STRING or \"\" as prefix for msgstr\n\
|
||||
values\n"));
|
||||
printf (_("\
|
||||
@ -1288,6 +1309,29 @@ or by email to <%s>.\n"),
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
consider_vc_mtime (const char *file_name)
|
||||
{
|
||||
struct timespec mtime;
|
||||
if (vc_mtime (&mtime, file_name) >= 0)
|
||||
{
|
||||
if (has_some_mtimes)
|
||||
{
|
||||
/* Compute the maximum of max_of_mtimes and mtime. */
|
||||
if (max_of_mtimes.tv_sec < mtime.tv_sec
|
||||
|| (max_of_mtimes.tv_sec == mtime.tv_sec
|
||||
&& max_of_mtimes.tv_nsec < mtime.tv_nsec))
|
||||
max_of_mtimes = mtime;
|
||||
}
|
||||
else
|
||||
max_of_mtimes = mtime;
|
||||
}
|
||||
else
|
||||
some_mtimes_failed = true;
|
||||
has_some_mtimes = true;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
exclude_directive_domain (abstract_catalog_reader_ty *catr,
|
||||
char *name, lex_pos_ty *name_pos)
|
||||
@ -1359,9 +1403,11 @@ read_exclusion_file (char *filename)
|
||||
{
|
||||
char *real_filename;
|
||||
FILE *fp = open_catalog_file (filename, &real_filename, true);
|
||||
abstract_catalog_reader_ty *catr;
|
||||
|
||||
catr = catalog_reader_alloc (&exclude_methods, textmode_xerror_handler);
|
||||
consider_vc_mtime (real_filename);
|
||||
|
||||
abstract_catalog_reader_ty *catr =
|
||||
catalog_reader_alloc (&exclude_methods, textmode_xerror_handler);
|
||||
catalog_reader_parse (catr, fp, real_filename, filename, true, &input_format_po);
|
||||
catalog_reader_free (catr);
|
||||
|
||||
@ -1971,6 +2017,8 @@ extract_from_file (const char *file_name, extractor_ty extractor,
|
||||
if (extractor.extract_from_stream)
|
||||
{
|
||||
FILE *fp = xgettext_open (file_name, &logical_file_name, &real_file_name);
|
||||
if (fp != stdin)
|
||||
consider_vc_mtime (real_file_name);
|
||||
|
||||
/* Set the default for the source file encoding. May be overridden by
|
||||
the extractor function. */
|
||||
@ -1992,6 +2040,7 @@ extract_from_file (const char *file_name, extractor_ty extractor,
|
||||
const char *found_in_dir;
|
||||
xgettext_find_file (file_name, &logical_file_name,
|
||||
&found_in_dir, &real_file_name);
|
||||
consider_vc_mtime (real_file_name);
|
||||
|
||||
extractor.extract_from_file (found_in_dir, real_file_name,
|
||||
logical_file_name,
|
||||
@ -2043,6 +2092,8 @@ extract_from_xml_file (const char *file_name,
|
||||
char *logical_file_name;
|
||||
char *real_file_name;
|
||||
FILE *fp = xgettext_open (file_name, &logical_file_name, &real_file_name);
|
||||
if (fp != stdin)
|
||||
consider_vc_mtime (real_file_name);
|
||||
|
||||
/* The default encoding for XML is UTF-8. It can be overridden by
|
||||
an XML declaration in the XML file itself, not through the
|
||||
@ -2059,6 +2110,7 @@ extract_from_xml_file (const char *file_name,
|
||||
|
||||
if (fp != stdin)
|
||||
fclose (fp);
|
||||
consider_vc_mtime (real_file_name);
|
||||
free (logical_file_name);
|
||||
free (real_file_name);
|
||||
}
|
||||
@ -2076,8 +2128,6 @@ static message_ty *
|
||||
construct_header ()
|
||||
{
|
||||
char *project_id_version;
|
||||
time_t now;
|
||||
char *timestring;
|
||||
message_ty *mp;
|
||||
char *msgstr;
|
||||
char *comment;
|
||||
@ -2102,13 +2152,10 @@ the MSGID_BUGS_ADDRESS variable there; otherwise please\n\
|
||||
specify an --msgid-bugs-address command line option.\n\
|
||||
")));
|
||||
|
||||
time (&now);
|
||||
timestring = po_strftime (&now);
|
||||
|
||||
msgstr = xasprintf ("\
|
||||
Project-Id-Version: %s\n\
|
||||
Report-Msgid-Bugs-To: %s\n\
|
||||
POT-Creation-Date: %s\n\
|
||||
POT-Creation-Date: \n\
|
||||
PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n\
|
||||
Last-Translator: FULL NAME <EMAIL@ADDRESS>\n\
|
||||
Language-Team: LANGUAGE <LL@li.org>\n\
|
||||
@ -2117,10 +2164,8 @@ MIME-Version: 1.0\n\
|
||||
Content-Type: text/plain; charset=CHARSET\n\
|
||||
Content-Transfer-Encoding: 8bit\n",
|
||||
project_id_version,
|
||||
msgid_bugs_address != NULL ? msgid_bugs_address : "",
|
||||
timestring);
|
||||
msgid_bugs_address != NULL ? msgid_bugs_address : "");
|
||||
assume (msgstr != NULL);
|
||||
free (timestring);
|
||||
free (project_id_version);
|
||||
|
||||
mp = message_alloc (NULL, "", NULL, msgstr, strlen (msgstr) + 1, &pos);
|
||||
@ -2149,6 +2194,20 @@ FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.\n");
|
||||
static void
|
||||
finalize_header (msgdomain_list_ty *mdlp)
|
||||
{
|
||||
/* Set the POT-Creation-Date field. */
|
||||
{
|
||||
time_t stamp;
|
||||
if (has_some_mtimes && !some_mtimes_failed)
|
||||
/* Use the maximum of the encountered mtimes. */
|
||||
stamp = max_of_mtimes.tv_sec;
|
||||
else
|
||||
/* Use the current time. */
|
||||
time (&stamp);
|
||||
char *timestring = po_strftime (&stamp);
|
||||
msgdomain_list_set_header_field (mdlp, "POT-Creation-Date:", timestring);
|
||||
free (timestring);
|
||||
}
|
||||
|
||||
/* If the generated PO file has plural forms, add a Plural-Forms template
|
||||
to the constructed header. */
|
||||
{
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user