File: ada-lib-info-source-date-epoch.diff

package info (click to toggle)
gprbuild 2025.0.0-4
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 5,752 kB
  • sloc: ada: 72,651; sh: 429; makefile: 422; python: 242; ansic: 108; cpp: 89; fortran: 62; xml: 13
file content (168 lines) | stat: -rw-r--r-- 6,611 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
Description: set ALI timestamps from SOURCE_DATE_EPOCH if available.
 When the SOURCE_DATE_EPOCH environment variable is set,
 replace timestamps more recent than its value with its value
 when writing Ada Library Information (ALI) files.
 This allow reproducible builds from generated or patched Ada sources.
 https://reproducible-builds.org/specs/source-date-epoch/
 .
 Let gprbuild recognize this situation instead of always detecting the
 file as obsolete.
 .
 The patch should be kept in sync with the gcc-BV patch with the same name
 (hence Getenv instead of Ada.Environment_Variable.Value).
Forwarded: no
Author: Nicolas Boulenguez <nicolas@debian.org>

--- a/gpr/src/gpr-osint.adb
+++ b/gpr/src/gpr-osint.adb
@@ -22,11 +22,14 @@
 --                                                                          --
 ------------------------------------------------------------------------------
 
+with Ada.Calendar.Conversions;
 with Ada.Command_Line; use Ada.Command_Line;
 with Ada.Directories;  use Ada.Directories;
 
 with Ada.Unchecked_Conversion;
 
+with Interfaces.C;
+
 with System.CRTL;
 with System.OS_Constants;
 
@@ -39,6 +42,11 @@
 
    Current_Full_Lib_Name : File_Name_Type  := No_File;
 
+   Source_Date_Epoch : OS_Time := GNAT.OS_Lib.Invalid_Time;
+   Source_Date_Time  : Ada.Calendar.Time := Invalid_Time;
+   --  Set at startup by the Initialize procedure.
+   --  See the specification of the File_Time_Stamp functions.
+
    function File_Length
      (Name : C_File_Name;
       Attr : access File_Attributes) return Long_Integer;
@@ -227,13 +235,20 @@
    ----------------
 
    function File_Stamp (Name : String) return Time_Stamp_Type is
+      T : OS_Time := File_Time_Stamp (Name);
    begin
       --  File_Time_Stamp will always return Invalid_Time if the file does
       --  not exist, and OS_Time_To_GNAT_Time will convert this value to
       --  Empty_Time_Stamp. Therefore we do not need to first test whether
       --  the file actually exists, which saves a system call.
 
-      return OS_Time_To_GNAT_Time (File_Time_Stamp (Name));
+      if Source_Date_Epoch /= GNAT.OS_Lib.Invalid_Time
+        and then T /= GNAT.OS_Lib.Invalid_Time
+        and then Source_Date_Epoch < T
+      then
+         T := Source_Date_Epoch;
+      end if;
+      return OS_Time_To_GNAT_Time (T);
    end File_Stamp;
 
    function File_Stamp (Name : File_Name_Type) return Time_Stamp_Type is
@@ -260,8 +275,15 @@
    is
       function Internal (N : C_File_Name; A : System.Address) return OS_Time;
       pragma Import (C, Internal, "__gnat_file_time_name_attr");
+      T : OS_Time := Internal (Name, Attr.all'Address);
    begin
-      return Internal (Name, Attr.all'Address);
+      if Source_Date_Epoch /= GNAT.OS_Lib.Invalid_Time
+        and then T /= GNAT.OS_Lib.Invalid_Time
+        and then Source_Date_Epoch < T
+      then
+         T := Source_Date_Epoch;
+      end if;
+      return T;
    end File_Time_Stamp;
 
    function File_Time_Stamp
@@ -285,11 +307,19 @@
 
    function File_Time_Stamp (Name : String) return Ada.Calendar.Time is
       FN : aliased constant String := Name & ASCII.NUL;
+      T : Ada.Calendar.Time := File_Time_Stamp (FN'Address);
+      use type Ada.Calendar.Time;
    begin
       --  Do not use Ada.Directories.Modification_Time directly because it
       --  raises an exception on an absent file.
 
-      return File_Time_Stamp (FN'Address);
+      if Source_Date_Time /= Invalid_Time
+        and then T /= Invalid_Time
+        and then Source_Date_Time < T
+      then
+         T := Source_Date_Time;
+      end if;
+      return T;
    end File_Time_Stamp;
 
    ---------------
@@ -582,4 +612,39 @@
 
 begin
    Reset_File_Attributes (Unknown_Attributes'Address);
+
+   --  When the SOURCE_DATE_EPOCH the environment variable is not empty,
+   --  only contains decimal digits and represents a value lower than 2**63,
+   --  set Source_Date_Epoch to this value.
+   Set_Source_Date_Epoch : declare
+      Env_Var : String_Access := Getenv ("SOURCE_DATE_EPOCH");
+      Epoch   : Long_Long_Integer range 0 .. Long_Long_Integer'Last := 0;
+      Index   : Integer range Env_Var.all'First .. Env_Var.all'Last + 1
+                := Env_Var.all'First;
+      Digit   : Long_Long_Integer;
+   begin
+      if Index <= Env_Var.all'Last then
+         --  Calling System.Val_LLI breaks the bootstrap sequence.
+         loop
+            Digit := Character'Pos (Env_Var.all (Index)) - Character'Pos ('0');
+            exit when Digit not in 0 .. 9;
+            exit when Long_Long_Integer'Last - Digit < Epoch;
+            Epoch := Epoch + Digit;
+            if Index = Env_Var.all'Last then
+               Source_Date_Epoch := To_Ada (Epoch);
+               --  Note that Interfaces.C.long *will* overflow in 2038.
+               --  When they will be available (GCC-15), next line should
+               --  use the 64 bits conversions.
+               Source_Date_Time := Ada.Calendar.Conversions.To_Ada_Time
+                 (Interfaces.C.long (Epoch));
+               exit;
+            end if;
+            Index := Index + 1;
+            --  The division is computed once at build time.
+            exit when Long_Long_Integer'Last / 10 < Epoch;
+            Epoch := Epoch * 10;
+         end loop;
+      end if;
+      Free (Env_Var);
+   end Set_Source_Date_Epoch;
 end GPR.Osint;
--- a/gpr/src/gpr-osint.ads
+++ b/gpr/src/gpr-osint.ads
@@ -205,11 +205,21 @@
    --  Returns file last modification time with nanoseconds precision.
    --  Returns Invalid_Time on error.
 
+   --  If the SOURCE_DATE_EPOCH environment variable only contains
+   --  decimal digits that, interpreted as a number of seconds since
+   --  1970-01-01 UTC, define a date representable by the underlying
+   --  operating system, then files modified after this date will be
+   --  reported as modified at this date.
+   --  This ensures that gnat1 writes deterministic .ali files even in
+   --  the presence of patched or generated sources.  See
+   --  https://reproducible-builds.org/specs/source-date-epoch.
+
    function File_Stamp (Name : File_Name_Type) return Time_Stamp_Type;
    --  Returns the time stamp of file Name. Name should include relative
    --  path information in order to locate it. If the source file cannot be
    --  opened, or Name = No_File, and all blank time stamp is returned (this
    --  is not an error situation).
+   --  The result is truncated to SOURCE_DATE_EPOCH as described above.
 
    function File_Stamp (Name : Path_Name_Type) return Time_Stamp_Type;
    --  Same as above for a path name