Forum: PC-Programmierung __date__ und __time__ und source_date_epoch?


von Bauform B. (bauformb)


Lesenswert?

Mahlzeit!

Der gcc¹ kann ja --date-- und --time-- entsprechend der Environment 
Variable SOURCE_DATE_EPOCH setzen. So weit so gut, solange ein Programm 
aus den Quellen komplett neu gebaut wird. Mit Makefile und mehreren *.c 
funktioniert das nur zufällig, weil die Datei mit diesen Variablen 
selten geändert und übersetzt wird. Dann funktioniert auch die 
Empfehlung² nicht, eine RTC erstmalig zu stellen. Außerdem sieht man in 
dem Beitrag, wie mega umständlich das ist. Und evt. ist nicht klar, ob 
man UTC oder localtime bekommt.

Mit CFLAGS += SOURCE_DATE_EPOCH=$(SOURCE_DATE_EPOCH) entfällt der 
Umstand, man bekommt die UNIX-Sekunden direkt als Konstante. Aber 
trotzdem muss die entsprechende Quelldatei jedes Mal übersetzt werden. 
So geht's also auch nicht.

Wie kann man so eine Konstante per Makefile dem Linker übergeben? Oder 
wie macht "man" das?

2) Beitrag "Re: Datum und Uhrzeit auf Mikrocontroller"
1) https://gcc.gnu.org/onlinedocs/cpp/Environment-Variables.html

: Bearbeitet durch User
von Harald K. (kirnbichler)


Lesenswert?

Bauform B. schrieb:
> Wie kann man so eine Konstante per Makefile dem Linker übergeben?

Pack sie in ein C-Sourcefile, und sorge dafür, daß das bei jedem 
make-Aufruf neu übersetzt wird.
1
version.o: version.c
2
3
version.o:   .FORCE
4
5
.FORCE:

(siehe 
https://stackoverflow.com/questions/7643838/how-to-force-make-to-always-rebuild-a-file)

von Bauform B. (bauformb)


Lesenswert?

Danke, das ist auch ein netter Trick. Aber für diesen Zweck ist das 
alles ein Krampf. Wenn SOURCE_DATE_EPOCH nicht definiert ist, setzt der 
gcc (vorschriftsmäßig) die aktuelle Uhrzeit ein, noch dazu die lokale. 
Dazu kommt der Umweg über mühsame Dekodierung von mühsam erzeugtem Text.

Diese beiden Präprozessor-Konstanten sind für etwas anderes gedacht. Es 
ist toll, dass der gcc auch Leute unterstützt, die diese Konstanten 
brauchen. Aber eigentlich will man doch nur eine einzige Zahl.

Mein Problem für heute war: -Wl,-defsym=SOURCE_DATE_EPOCH=$(UTC) benutze 
ich für die Cortex-M und früher ging es auch auf dem PC. AMD mit ihrem 
64-Bit Befehlssatz ist schuld, dass auf dem PC per Default PIC erzeugt 
wird. Deshalb geht -defsym nicht mehr ganz so einfach.

Jetzt schreibe ich sinngemäß:
1
SDE := $(shell stat -L -c %Y $(PROJECT)/src/*/*.c | sort | tail -1)
2
-Wl,-defsym=S_D_E_BASE=0 \
3
-Wl,-defsym=S_D_E=$(SDE)
4
5
extern char S_D_E_BASE, S_D_E;
6
time_t source_date_epoch = (time_t)(&S_D_E - &S_D_E_BASE);
OK, es gibt keinen Schönheitspreis, aber es ist doch völlig legal? Gibt 
es irgend welche Einwände?

von Rick (rick)


Lesenswert?

Bauform B. schrieb:
> Und evt. ist nicht klar, ob
> man UTC oder localtime bekommt.
Warum verwendest Du nicht date?
1
date +%d.%m.%Y_%H:%M

Und muß es der Linker sein? In solchen Fallen erstelle ich mir per 
Skript eine passende Quelltextdatei.

von Bauform B. (bauformb)


Lesenswert?

Rick schrieb:
> Warum verwendest Du nicht date?date +%d.%m.%Y_%H:%M

Wenn schon, dann date +%s. Dein Format muss ich ja auch wieder mühsam 
auseinander fieseln. Aber hier geht es eben nicht um die aktuelle Zeit, 
sondern um die Zeit der letzten Änderung der Quellen. Der entscheidende 
Unterschied: man kann das Programm später neu bauen und bekommt Bit für 
Bit das gleiche Binary.

https://reproducible-builds.org

> Und muß es der Linker sein? In solchen Fallen erstelle ich mir per
> Skript eine passende Quelltextdatei.

Ja, mit Haralds FORCE-Trick geht das auch. Aber auch in dem Fall würde 
ich nicht den Umweg über die offiziellen Macros gehen, sondern über ein 
define mit date +%s (in Wirklichkeit: stat -c %Y).

Ja, time_t als Differenz von 2 Adressen zu übergeben, ist sicher 
überraschend. Aber die Lösung mit define und FORCE finde ich auch nicht 
elegant. Ich hatte mich so an -Wl,-defsym gewöhnt und nur wegen PIC geht 
das nicht mehr so einfach.

Ich warte noch auf Alternativ-Vorschläge ;)

von Bauform B. (bauformb)


Lesenswert?

Rick schrieb:
> Und muß es der Linker sein? In solchen Fallen erstelle ich mir per
> Skript eine passende Quelltextdatei.

Theoretisch muss es der Linker machen, aber scheinbar gibt es keine 
vernünftige Möglichkeit. Praktisch lohnt es sich auch nicht. Es würde 
nur dann richtig funktionieren, wenn der gcc DATE und TIME so umbauen 
würde, dass der Linker die aktuellen Werte einsetzen könnte. Aber das 
sind nun mal String-Konstanten.

So eine Quelltextdatei sollte aber nicht vom Script erzeugt werden, ein 
touch reicht auch. "touch -d@$SOURCE_DATE_EPOCH version.c" muss ja 
sowieso gemacht werden, sonst hätte version.c den Build- und nicht den 
Source-Zeitstempel.

Nebenbei sorgt dieses touch dafür, dass version.c (nur) genau dann 
übersetzt wird, wenn das nötig ist. Mit FORCE gibt es nie die Meldung 
"make: 'foo' is up to date". Also so:
1
#!/bin/sh
2
# source_date_epoch.sh
3
# Liefert die mtime der neuesten Quelldatei.
4
#
5
# Dank "touch" wird version.c immer genau dann neu compiliert,
6
# wenn es irgendetwas neues gibt. Das Makefile erzeugt aus dem
7
# Zeitstempel mit -D ein Macro und versorgt per export den gcc.
8
#
9
# Das Macro darf nur in version.c benutzt werden und __DATE__
10
# und __TIME__ eigentlich garnicht. Die funktionieren nur dann
11
# reproduzierbar, wenn alles komplett neu gebaut wird.
12
13
SOURCE_DATE_EPOCH=$(stat -L -c %Y \
14
                    $HOME/src/makefile.mk \
15
                    $HOME/src/make-flags.mk \
16
                    $HOME/src/*/Makefile \
17
                    $HOME/src/*/*.c \
18
                    $HOME/src/*/*.h \
19
                    | sort | tail -1)
20
21
touch -d@$SOURCE_DATE_EPOCH version.c
22
echo $SOURCE_DATE_EPOCH
im Makefile dann so:
1
# die offizielle Variable, z.B. fuer den gcc
2
export SOURCE_DATE_EPOCH:=$(shell ./source_date_epoch.sh)
3
# und
4
CFLAGS+=-DSOURCE_DATE_EPOCH=$(SOURCE_DATE_EPOCH)
und schließlich:
1
// version.c
2
#include <time.h>
3
4
time_t
5
source_date_epoch (void)
6
{
7
   time_t  sde = SOURCE_DATE_EPOCH;
8
9
   return sde;
10
}
11
12
13
char *
14
version_string (void)
15
{
16
   time_t  sde = source_date_epoch ();
17
18
   if (sde <= 0) { return "--"; }
19
   return isotime (6, sde);
20
}

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.