[Date Prev][Date Next] [Thread Prev][Thread Next] [Date Index] [Thread Index]

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: