Bug#856042: gnat: please use SOURCE_DATE_EPOCH for reproducible ALI timestamps
Package: gnat-7
Followup-For: Bug #856042
Control: tags 856042 + patch
This new source-date-epoch.diff applies to 7-20170226-1 and builds
with DEB_BUILD_OPTIONS="lang=ada nocheck noopt nostrap nolang=...".
The test is more extensive, but I have not been able to run it on the
build result (for unrelated reasons).
--- a/debian/rules.patch
+++ b/debian/rules.patch
@@ -145,6 +145,7 @@ debian_patches += ada-arm
#endif
debian_patches += ada-link-shlib
+ debian_patches += ada-lib-info-source-date-epoch
#endif
--- /dev/null
+++ b/debian/patches/ada-lib-info-source-date-epoch.diff
@@ -0,0 +1,174 @@
+# DP: When the SOURCE_DATE_EPOCH environment variable is set,
+# DP: replace timestamps more recent than its value with its value
+# DP: when writing Ada Library Information (ALI) files.
+# DP: This allow reproducible builds from generated or patched Ada sources.
+# DP: https://reproducible-builds.org/specs/source-date-epoch/
+
+--- a/src/gcc/ada/lib-writ.adb
++++ b/src/gcc/ada/lib-writ.adb
+@@ -54,6 +54,7 @@
+ with Uname; use Uname;
+
+ with System.Case_Util; use System.Case_Util;
++with System.OS_Lib;
+ with System.WCh_Con; use System.WCh_Con;
+
+ package body Lib.Writ is
+@@ -62,6 +63,15 @@
+ -- Local Subprograms --
+ -----------------------
+
++ procedure Truncate_To_Source_Date_Epoch (T : in out Time_Stamp_Type);
++ pragma Inline (Truncate_To_Source_Date_Epoch);
++ -- If SOURCE_DATE_EPOCH is defined in the environment and
++ -- represents an UNIX epoch, T is truncated to this date.
++
++ -- This allows reproducible ALI contents even for sources patched
++ -- or generated at build time.
++ -- https://reproducible-builds.org/specs/source-date-epoch/
++
+ procedure Write_Unit_Name (N : Node_Id);
+ -- Used to write out the unit name for R (pragma Restriction) lines
+ -- for uses of Restriction (No_Dependence => unit-name).
+@@ -175,6 +185,65 @@
+ end;
+ end Ensure_System_Dependency;
+
++ -----------------------------------
++ -- Truncate_To_Source_Date_Epoch --
++ -----------------------------------
++
++ -- An internal state caches the result of the getenv() system call
++ -- during first execution. The Source_Date_Unset case could be
++ -- replaced with a Source_Date far in the future, but we want to
++ -- avoid Time_Stamp_Type comparisons in the most common case.
++
++ type A_Source_Date_State is
++ (Source_Date_Unknown, Source_Date_Unset, Source_Date_Set);
++ Source_Date_State : A_Source_Date_State := Source_Date_Unknown;
++ Source_Date : Time_Stamp_Type;
++
++ procedure Truncate_To_Source_Date_Epoch (T : in out Time_Stamp_Type) is
++ begin
++ case Source_Date_State is
++ when Source_Date_Unset =>
++ null;
++ when Source_Date_Set =>
++ if Source_Date < T then
++ T := Source_Date;
++ end if;
++ when Source_Date_Unknown =>
++ declare
++ use System.OS_Lib;
++ Env_Var : String_Access;
++ Get_OK : Boolean;
++ Epoch : OS_Time;
++ Y : Year_Type;
++ Mo : Month_Type;
++ D : Day_Type;
++ H : Hour_Type;
++ Mn : Minute_Type;
++ S : Second_Type;
++ 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
++ GM_Split (Epoch, Y, Mo, D, H, Mn, S);
++ Make_Time_Stamp (Year => Nat (Y),
++ Month => Nat (Mo),
++ Day => Nat (D),
++ Hour => Nat (H),
++ Minutes => Nat (Mn),
++ Seconds => Nat (S),
++ TS => Source_Date);
++ Source_Date_State := Source_Date_Set;
++ if Source_Date < T then
++ T := Source_Date;
++ end if;
++ else
++ Source_Date_State := Source_Date_Unset;
++ end if;
++ end;
++ end case;
++ end Truncate_To_Source_Date_Epoch;
++
+ ---------------
+ -- Write_ALI --
+ ---------------
+@@ -1471,7 +1540,14 @@
+
+ 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
++ Truncate_To_Source_Date_Epoch (T);
++ Write_Info_Str (String (T));
++ end;
++
+ Write_Info_Char (' ');
+ Write_Info_Str (Get_Hex_String (Source_Checksum (Sind)));
+
+--- a/src/gcc/ada/s-os_lib.adb
++++ b/src/gcc/ada/s-os_lib.adb
+@@ -1153,6 +1153,41 @@
+ 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/s-os_lib.ads
++++ b/src/gcc/ada/s-os_lib.ads
+@@ -164,6 +164,13 @@
+ -- 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 --
+ ----------------
#!/bin/sh
set -C -e -f -u
cat > p.ads <<EOF
package P is
N : constant := 42;
end P;
EOF
mtime=20
touch --date=@$mtime p.ads
to_gnat () {
date --utc --date=@$1 +%Y%m%d%H%M%S
}
check () {
expected=`to_gnat $1`
gnatgcc -c p.ads
found=`sed -n '/^D p\.ads\t\+\([0-9]\{14\}\) .*/{s//\1/;p;q}' p.ali`
rm -f p.ali p.o
if test "$found" != $expected ; then
echo "Test failed"
echo " SOURCE_DATE_EPOCH: '${SOURCE_DATE_EPOCH:-}'"
echo " Source file mtime:`to_gnat $mtime` ($mtime)"
echo " Expected in ALI :$expected ($1)"
echo " Found in ALI :$found"
rm -f p.ads
exit 1
fi
}
# SOURCE_DATE_EPOCH is not defined
check $mtime
# SOURCE_DATE_EPOCH is invalid or more recent than mtime.
for d in "" a " 1" -0 -1 30 030 ; do
SOURCE_DATE_EPOCH=$d check $mtime
done
# SOURCE_DATE_EPOCH is valid and older than mtime.
for d in 0 10 010 ; do
SOURCE_DATE_EPOCH=$d check $d
done
rm -f p.ads
echo "SOURCE_DATE_EPOCH tests OK"
Reply to: