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
|