aboutsummaryrefslogtreecommitdiff
path: root/tools/gnulib/patches/689-vc-mtime.patch
diff options
context:
space:
mode:
Diffstat (limited to 'tools/gnulib/patches/689-vc-mtime.patch')
-rw-r--r--tools/gnulib/patches/689-vc-mtime.patch366
1 files changed, 366 insertions, 0 deletions
diff --git a/tools/gnulib/patches/689-vc-mtime.patch b/tools/gnulib/patches/689-vc-mtime.patch
new file mode 100644
index 0000000000..62638d7ff4
--- /dev/null
+++ b/tools/gnulib/patches/689-vc-mtime.patch
@@ -0,0 +1,366 @@
+From 701d20aaf579bb71f35209dd63a272c3d9d21096 Mon Sep 17 00:00:00 2001
+From: Bruno Haible <bruno@clisp.org>
+Date: Mon, 24 Feb 2025 19:03:17 +0100
+Subject: [PATCH] vc-mtime: New module.
+
+* lib/vc-mtime.h: New file.
+* lib/vc-mtime.c: New file.
+* modules/vc-mtime: New file.
+---
+ ChangeLog | 7 ++
+ lib/vc-mtime.c | 208 +++++++++++++++++++++++++++++++++++++++++++++++
+ lib/vc-mtime.h | 97 ++++++++++++++++++++++
+ modules/vc-mtime | 34 ++++++++
+ 4 files changed, 346 insertions(+)
+ create mode 100644 lib/vc-mtime.c
+ create mode 100644 lib/vc-mtime.h
+ create mode 100644 modules/vc-mtime
+
+--- /dev/null
++++ b/lib/vc-mtime.c
+@@ -0,0 +1,208 @@
++/* Return the version-control based modification time of a file.
++ Copyright (C) 2025 Free Software Foundation, Inc.
++
++ This program is free software: you can redistribute it and/or modify
++ it under the terms of the GNU General Public License as published by
++ the Free Software Foundation, either version 3 of the License, or
++ (at your option) any later version.
++
++ This program is distributed in the hope that it will be useful,
++ but WITHOUT ANY WARRANTY; without even the implied warranty of
++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ GNU General Public License for more details.
++
++ You should have received a copy of the GNU General Public License
++ along with this program. If not, see <https://www.gnu.org/licenses/>. */
++
++/* Written by Bruno Haible <bruno@clisp.org>, 2025. */
++
++#include <config.h>
++
++/* Specification. */
++#include "vc-mtime.h"
++
++#include <stdlib.h>
++#include <unistd.h>
++
++#include <error.h>
++#include "spawn-pipe.h"
++#include "wait-process.h"
++#include "execute.h"
++#include "safe-read.h"
++#include "xstrtol.h"
++#include "stat-time.h"
++#include "gettext.h"
++
++#define _(msgid) dgettext ("gnulib", msgid)
++
++
++/* Determines whether the specified file is under version control. */
++static bool
++git_vc_controlled (const char *filename)
++{
++ /* Run "git ls-files FILENAME" and return true if the exit code is 0
++ and the output is non-empty. */
++ const char *argv[4];
++ pid_t child;
++ int fd[1];
++
++ argv[0] = "git";
++ argv[1] = "ls-files";
++ argv[2] = filename;
++ argv[3] = NULL;
++ child = create_pipe_in ("git", "git", argv, NULL, NULL,
++ DEV_NULL, true, true, false, fd);
++ if (child == -1)
++ return false;
++
++ /* Read the subprocess output, and test whether it is non-empty. */
++ size_t count = 0;
++ char c;
++
++ while (safe_read (fd[0], &c, 1) > 0)
++ count++;
++
++ close (fd[0]);
++
++ /* Remove zombie process from process list, and retrieve exit status. */
++ int exitstatus =
++ wait_subprocess (child, "git", false, true, true, false, NULL);
++ return (exitstatus == 0 && count > 0);
++}
++
++/* Determines whether the specified file is unmodified, compared to the
++ last version in version control. */
++static bool
++git_unmodified (const char *filename)
++{
++ /* Run "git diff --quiet -- HEAD FILENAME"
++ (or "git diff --quiet HEAD FILENAME")
++ and return true if the exit code is 0.
++ The '--' option is for the case that the specified file was removed. */
++ const char *argv[7];
++ int exitstatus;
++
++ argv[0] = "git";
++ argv[1] = "diff";
++ argv[2] = "--quiet";
++ argv[3] = "--";
++ argv[4] = "HEAD";
++ argv[5] = filename;
++ argv[6] = NULL;
++ exitstatus = execute ("git", "git", argv, NULL, NULL,
++ false, false, true, true,
++ true, false, NULL);
++ return (exitstatus == 0);
++}
++
++/* Stores in *MTIME the time of last modification in version control of the
++ specified file, and returns 0.
++ Upon failure, it returns -1. */
++static int
++git_mtime (struct timespec *mtime, const char *filename)
++{
++ /* Run "git log -1 --format=%ct -- FILENAME". It prints the time of last
++ modification, as the number of seconds since the Epoch.
++ The '--' option is for the case that the specified file was removed. */
++ const char *argv[7];
++ pid_t child;
++ int fd[1];
++
++ argv[0] = "git";
++ argv[1] = "log";
++ argv[2] = "-1";
++ argv[3] = "--format=%ct";
++ argv[4] = "--";
++ argv[5] = filename;
++ argv[6] = NULL;
++ child = create_pipe_in ("git", "git", argv, NULL, NULL,
++ DEV_NULL, true, true, false, fd);
++ if (child == -1)
++ return -1;
++
++ /* Retrieve its result. */
++ FILE *fp;
++ char *line;
++ size_t linesize;
++ size_t linelen;
++
++ fp = fdopen (fd[0], "r");
++ if (fp == NULL)
++ error (EXIT_FAILURE, errno, _("fdopen() failed"));
++
++ line = NULL; linesize = 0;
++ linelen = getline (&line, &linesize, fp);
++ if (linelen == (size_t)(-1))
++ {
++ error (0, 0, _("%s subprocess I/O error"), "git");
++ fclose (fp);
++ wait_subprocess (child, "git", true, false, true, false, NULL);
++ }
++ else
++ {
++ int exitstatus;
++
++ if (linelen > 0 && line[linelen - 1] == '\n')
++ line[linelen - 1] = '\0';
++
++ fclose (fp);
++
++ /* Remove zombie process from process list, and retrieve exit status. */
++ exitstatus =
++ wait_subprocess (child, "git", true, false, true, false, NULL);
++ if (exitstatus == 0)
++ {
++ char *endptr;
++ unsigned long git_log_time;
++ if (xstrtoul (line, &endptr, 10, &git_log_time, NULL) == LONGINT_OK
++ && endptr == line + strlen (line))
++ {
++ mtime->tv_sec = git_log_time;
++ mtime->tv_nsec = 0;
++ free (line);
++ return 0;
++ }
++ }
++ }
++ free (line);
++ return -1;
++}
++
++int
++vc_mtime (struct timespec *mtime, const char *filename)
++{
++ static bool git_tested;
++ static bool git_present;
++
++ if (!git_tested)
++ {
++ /* Test for presence of git:
++ "git --version >/dev/null 2>/dev/null" */
++ const char *argv[3];
++ int exitstatus;
++
++ argv[0] = "git";
++ argv[1] = "--version";
++ argv[2] = NULL;
++ exitstatus = execute ("git", "git", argv, NULL, NULL,
++ false, false, true, true,
++ true, false, NULL);
++ git_present = (exitstatus == 0);
++ git_tested = true;
++ }
++
++ if (git_present
++ && git_vc_controlled (filename)
++ && git_unmodified (filename))
++ {
++ if (git_mtime (mtime, filename) == 0)
++ return 0;
++ }
++ struct stat statbuf;
++ if (stat (filename, &statbuf) == 0)
++ {
++ *mtime = get_stat_mtime (&statbuf);
++ return 0;
++ }
++ return -1;
++}
+--- /dev/null
++++ b/lib/vc-mtime.h
+@@ -0,0 +1,97 @@
++/* Return the version-control based modification time of a file.
++ Copyright (C) 2025 Free Software Foundation, Inc.
++
++ This program is free software: you can redistribute it and/or modify
++ it under the terms of the GNU General Public License as published by
++ the Free Software Foundation, either version 3 of the License, or
++ (at your option) any later version.
++
++ This program is distributed in the hope that it will be useful,
++ but WITHOUT ANY WARRANTY; without even the implied warranty of
++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ GNU General Public License for more details.
++
++ You should have received a copy of the GNU General Public License
++ along with this program. If not, see <https://www.gnu.org/licenses/>. */
++
++/* Written by Bruno Haible <bruno@clisp.org>, 2025. */
++
++#ifndef _VC_MTIME_H
++#define _VC_MTIME_H
++
++/* Get struct timespec. */
++#include <time.h>
++
++/* The "version-controlled modification time" vc_mtime(F) of a file F
++ is defined as:
++ - If F is under version control and not modified locally:
++ the time of the last change of F in the version control system.
++ - Otherwise: The modification time of F on disk.
++
++ For now, the only VCS supported by this module is git. (hg and svn are
++ hardly in use any more.)
++
++ This has the properties that:
++ - Different users who have checked out the same git repo on different
++ machines, at different times, and not done local modifications,
++ get the same vc_mtime(F).
++ - If a user has modified F locally, the modification time of that file
++ counts.
++ - If that user then reverts the modification, they then again get the
++ same vc_mtime(F) as everyone else.
++ - Different users who have unpacked the same tarball (without .git
++ directory) on different machines, at different times, also get the same
++ vc_mtime(F) [but possibly a different one than when the .git directory
++ was present]. (Assuming a POSIX compliant file system.)
++ - When a user commits local modifications into git, this only increases
++ (not decreases) the vc_mtime(F).
++
++ The purpose of the version-controlled modification time is to produce a
++ reproducible timestamp(Z) of a file Z that depends on files X1, ..., Xn,
++ in such a way that
++ - timestamp(Z) is reproducible, that is, different users on different
++ machines get the same value.
++ - timestamp(Z) is related to reality. It's not just a dummy, like what
++ is suggested in <https://reproducible-builds.org/docs/timestamps/>.
++ - One can arrange for timestamp(Z) to respect the modification time
++ relations of a build system.
++
++ There are two uses of such a timestamp:
++ - It can be set as the modification time of file Z in a file system, or
++ - It can be embedded in Z, with the purpose of telling a user how old
++ the file Z is. For example, in PDF files or in generated documentation,
++ such a time is embedded in a special place.
++
++ The simplest example is a file Z that depends on files X1, ..., Xn.
++ Generally one will define
++ timestamp(Z) = max (vc_mtime(X1), ..., vc_mtime(Xn))
++ for an embedded timestamp, or
++ timestamp(Z) = max (vc_mtime(X1), ..., vc_mtime(Xn)) + 1 second
++ for a time stamp in a file system. The added second
++ 1. accounts for fractional seconds in mtime(X1), ..., mtime(Xn),
++ 2. allows for 'make' implementation that attempt to rebuild Z
++ if mtime(Z) == mtime(Xi).
++
++ A more complicated example is when there are intermediate built files, not
++ under version control. For example, if the build process produces
++ X1, X2 -> Y1
++ X3, X4 -> Y2
++ Y1, Y2, X5 -> Z
++ where Y1 and Y2 are intermediate built files, you should ignore the
++ mtime(Y1), mtime(Y2), and consider only the vc_mtime(X1), ..., vc_mtime(X5).
++ */
++
++#ifdef __cplusplus
++extern "C" {
++#endif
++
++/* Determines the version-controlled modification time of FILENAME, stores it
++ in *MTIME, and returns 0.
++ Upon failure, it returns -1. */
++extern int vc_mtime (struct timespec *mtime, const char *filename);
++
++#ifdef __cplusplus
++}
++#endif
++
++#endif /* _VC_MTIME_H */
+--- /dev/null
++++ b/modules/vc-mtime
+@@ -0,0 +1,34 @@
++Description:
++Returns the version-control based modification time of a file.
++
++Files:
++lib/vc-mtime.h
++lib/vc-mtime.c
++
++Depends-on:
++time-h
++bool
++spawn-pipe
++wait-process
++execute
++safe-read
++error
++getline
++xstrtol
++stat-time
++gettext-h
++gnulib-i18n
++
++configure.ac:
++
++Makefile.am:
++lib_SOURCES += vc-mtime.c
++
++Include:
++"vm-mtime.h"
++
++License:
++GPL
++
++Maintainer:
++Bruno Haible