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/ Author: Nicolas Boulenguez --- a/src/gcc/ada/ali-util.adb +++ b/src/gcc/ada/ali-util.adb @@ -484,8 +484,10 @@ package body ALI.Util is for D in ALIs.Table (A).First_Sdep .. ALIs.Table (A).Last_Sdep loop Src := Source_Id (Get_Name_Table_Int (Sdep.Table (D).Sfile)); - if Opt.Minimal_Recompilation - and then Sdep.Table (D).Stamp /= Source.Table (Src).Stamp + if (Opt.Minimal_Recompilation + and then Sdep.Table (D).Stamp /= Source.Table (Src).Stamp) + or else (Sdep.Table (D).Stamp = Source_Date_Epoch + and then Source_Date_Epoch < Source.Table (Src).Stamp) then -- If minimal recompilation is in action, replace the stamp -- of the source file in the table if checksums match. --- a/src/gcc/ada/lib-writ.adb +++ b/src/gcc/ada/lib-writ.adb @@ -1559,7 +1559,14 @@ package body Lib.Writ is Write_Info_Name_May_Be_Quoted (Fname); Write_Info_Tab (25); - Write_Info_Str (String (Time_Stamp (Sind))); + declare + T : Time_Stamp_Type := Time_Stamp (Sind); + begin + if Source_Date_Epoch < T then + T := Source_Date_Epoch; + end if; + Write_Info_Str (String (T)); + end; Write_Info_Char (' '); Write_Info_Str (Get_Hex_String (Source_Checksum (Sind))); --- a/src/gcc/ada/osint.adb +++ b/src/gcc/ada/osint.adb @@ -1686,6 +1686,20 @@ package body Osint is Lib_Search_Directories.Set_Last (Primary_Directory); Lib_Search_Directories.Table (Primary_Directory) := new String'(""); + + -- Look for Source_Date_Epoch in the environment. + declare + Env_Var : String_Access; + Get_OK : Boolean; + Epoch : OS_Time; + begin + Env_Var := Getenv ("SOURCE_DATE_EPOCH"); + Get_OS_Time_From_String (Env_Var.all, Get_OK, Epoch); + Free (Env_Var); + if Get_OK then + Source_Date_Epoch := OS_Time_To_GNAT_Time (Epoch); + end if; + end; end Initialize; ------------------ --- a/src/gcc/ada/osint.ads +++ b/src/gcc/ada/osint.ads @@ -683,6 +683,17 @@ package Osint is function Prep_Suffix return String; -- The suffix used for preprocessed files + Source_Date_Epoch : Time_Stamp_Type := Time_Stamp_Type'("99991231235959"); + -- * gnat1 truncates to this date time stamps written to ALI files, making + -- their contents deterministic even for patched or generated sources. + -- See https://reproducible-builds.org/specs/source-date-epoch. + -- * When gnatmake reads this date from an ALI file, and the source file is + -- more recent, it ignores the dates and only considers checksums as if + -- Minimal_Recompilation was selected. Else, the source would always + -- be detected as requiring a recompilation. + -- The default value has no effect, but Initialize will assign it if + -- SOURCE_DATE_EPOCH in the environment represents a valid epoch. + private Current_Main : File_Name_Type := No_File; --- a/src/gcc/ada/libgnat/s-os_lib.adb +++ b/src/gcc/ada/libgnat/s-os_lib.adb @@ -1146,6 +1146,41 @@ package body System.OS_Lib is return Result; end Get_Object_Suffix; + ----------------------------- + -- Get_OS_Time_From_String -- + ----------------------------- + + procedure Get_OS_Time_From_String (Arg : String; + Success : out Boolean; + Result : out OS_Time) is + -- Calling System.Val_LLI breaks the bootstrap sequence. + Digit : OS_Time; + begin + Result := 0; + if Arg'Length = 0 then + Success := False; + return; + end if; + for I in Arg'Range loop + if Arg (I) not in '0' .. '9' then + Success := False; + return; + end if; + Digit := OS_Time (Character'Pos (Arg (I)) - Character'Pos ('0')); + if OS_Time'Last / 10 < Result then + Success := False; + return; + end if; + Result := Result * 10; + if OS_Time'Last - Digit < Result then + Success := False; + return; + end if; + Result := Result + Digit; + end loop; + Success := True; + end Get_OS_Time_From_String; + ---------------------------------- -- Get_Target_Debuggable_Suffix -- ---------------------------------- --- a/src/gcc/ada/libgnat/s-os_lib.ads +++ b/src/gcc/ada/libgnat/s-os_lib.ads @@ -164,6 +164,13 @@ package System.OS_Lib is -- component parts to be interpreted in the local time zone, and returns -- an OS_Time. Returns Invalid_Time if the creation fails. + procedure Get_OS_Time_From_String (Arg : String; + Success : out Boolean; + Result : out OS_Time); + -- Success is set if Arg is not empty, only contains decimal + -- digits and represents an integer within OS_Time range. Result + -- is then affected with the represented value. + ---------------- -- File Stuff -- ----------------