Globale SVN-Revisionsnummer im Projekt - und zwar richtig!

Wenn man ein Projekt aufsetzt, dann hat man meist eine Versionsnummer im Format a.b.c.d, mit
  1. a = Hauptversionsnummer, ändert sich eigentlich nur bei massiven Änderungen
  2. b = zeigt an, dass neue Features hinzugekommen sind
  3. c = zeigt an, dass etwas gepatched bzw. geupdated wurde
  4. d = gibt den Revisionsstand der Software im Versionsverwaltungssystem an.
Bei SVN gibt es die Möglichkeit sich per Code-Kommentar eine Revisionsnummer direkt in den Quelltext schreiben zu lassen. Das sieht dann beispielsweise so aus:
  1. /*!
  2. *******************************************************************************
  3. * File identification: $Id:$
  4. * Revision of last commit: $Rev:$
  5. * Author of last commit: $Author:$
  6. * Date of last commit: $Date:$
  7. *******************************************************************************
  8. */
Und wird von SVN beim Commit ersetzt zu
  1. /*!
  2. *******************************************************************************
  3. * File identification: $Id: main.cpp 178 2016-02-08 14:42:36Z cypax $
  4. * Revision of last commit: $Rev: 178$
  5. * Author of last commit: $Author: cypax $
  6. * Date of last commit: $Date: 2016-02-08 15:42:36 +0100 (Mo, 08 Feb 2016) $
  7. *******************************************************************************
  8. */
Vorausgesetzt natürlich, dass die Datei die entsprechenden SVN-Keywords gesetzt hat:
SVN keywords

So weit, so gut. In dem Beispiel oben ist die Revisionsnummer 178. Wenn es jetzt aber noch eine zweite Datei gibt und Änderungen an dieser comitted werden, dann bleibt in main.cpp die 178 stehen. Warum? Weil die SVN-Revisionsnummer dateibezogen ist - nicht projektbezogen! Das ist ja jetzt nicht ganz das, was wir haben wollten. Zum Glück, gibt es aber ein Tool, welches genau für unsere Zwecke gedacht ist: svnversion! Um eine globale Revisionsnummer zu erhalten erstellt man eine leere Datei revision.h, checkt sie nicht in SVN ein und schreibt folgendes in die *.pro-Projektdatei eines Qt-Projekts:
  1. PRE_TARGETDEPS += $$PWD/code/revision.h
  2. # Obtain SVN revision
  3. SVN_REVISION = $$system(svnversion -n)
  4. # split into list by ':'
  5. REVISION_LIST = $$split(SVN_REVISION,:)
  6. # get last item of list
  7. HEAD_REVISION = $$last(REVISION_LIST)
  8. # remove M (modified working copy)
  9. HEAD_REVISION = $$replace(HEAD_REVISION,M,)
  10. # remove S (switched working copy)
  11. HEAD_REVISION = $$replace(HEAD_REVISION,S,)
  12. # remove P (partial working copy, from a sparse checkout)
  13. HEAD_REVISION = $$replace(HEAD_REVISION,P,)
  14. QMAKE_EXTRA_TARGETS += revtarget
  15. revtarget.target = $$PWD/code/revision.h
  16. unix {
  17. revtarget.commands = "$$system(echo \'/* generated file - do not edit */\' > $$revtarget.target)"
  18. revtarget.commands += "$$system(echo \'$${LITERAL_HASH}ifndef REVISION_H\' >> $$revtarget.target)"
  19. revtarget.commands += "$$system(echo \'$${LITERAL_HASH}define REVISION_H\' >> $$revtarget.target)"
  20. revtarget.commands += "$$system(echo \'$${LITERAL_HASH}define SVN_REVISION \"$$SVN_REVISION\"\' >> $$revtarget.target)"
  21. revtarget.commands += "$$system(echo \'$${LITERAL_HASH}define HEAD_REVISION $$HEAD_REVISION\' >> $$revtarget.target)"
  22. revtarget.commands += "$$system(echo \'$${LITERAL_HASH}define HEAD_REVISION_STRING \"$$HEAD_REVISION\"\' >> $$revtarget.target)"
  23. revtarget.commands += "$$system(echo \'$${LITERAL_HASH}endif // REVISION_H\' >> $$revtarget.target)"
  24. }
  25. win32 {
  26. revtarget.commands = "$$system(echo '/* generated file - do not edit */' > $$revtarget.target)"
  27. revtarget.commands += "$$system(echo '$${LITERAL_HASH}ifndef REVISION_H' >> $$revtarget.target)"
  28. revtarget.commands += "$$system(echo '$${LITERAL_HASH}define REVISION_H' >> $$revtarget.target)"
  29. revtarget.commands += "$$system(echo '$${LITERAL_HASH}define SVN_REVISION \"$$SVN_REVISION\"' >> $$revtarget.target)"
  30. revtarget.commands += "$$system(echo '$${LITERAL_HASH}define HEAD_REVISION $$HEAD_REVISION' >> $$revtarget.target)"
  31. revtarget.commands += "$$system(echo '$${LITERAL_HASH}define HEAD_REVISION_STRING \"$$HEAD_REVISION\"' >> $$revtarget.target)"
  32. revtarget.commands += "$$system(echo '$${LITERAL_HASH}endif // REVISION_H' >> $$revtarget.target)"
  33. }
  34. revtarget.depends = FORCE
  35. QMAKE_DISTCLEAN += $$revtarget.target
Zur Erklärung: Als erstes wird qmake mitgeteilt, dass es eine zusätzliche Abhängigkeit gibt (PRE_TARGETDEPS, Zeile 1) - nämlich besagte revision.h-Datei. Dann werden ein paar Variablen mit den SVN-Revisionsinformationen gefüllt. Mit dem Kommando svnversion -n (Zeile 4) erhält man die SVN-Revisionsnummer. Der Parameter -n bewirkt, dass die Ausgabe keinen Zeilenumbruch enthält. Die Variable SVN_REVISION enthält somit die Ausgabe von svnversion, ausgehend vom Pfad, in der die Projektdatei liegt. Wenn die lokale Arbeitskopie allerdings modifiziert, unvollständig ausgecheckt oder zu einer anderen Revision geswitched wurde, ist die Revisionsnummer nicht einfach eine Zahl, sondern nach dem Schema [abc:]xyz[M|S|P] aufgebaut, wobei xyz die Headrevision ist (für Details einfach mal in der Konsole svnversion -help eingeben). Aus diesem Grund teilen wir die Ausgabe anhand des Trennzeichens ":" auf (Zeile 6), nehmen das letzte Element dieser Liste (Zeile 8) und entfernen alle M-, S- und P-Zeichen (Zeilen 10 - 14). In Zeile 16 wird qmake mitgeteilt, dass es ein zusätzliches Target revtarget gibt (QMAKE_EXTRA_TARGETS), welches die, im Unterordner /code befindliche, revision.h-Datei ist und welche stets neu zu erstellen ist (Zeile 39). Das Befüllen der revision.h erfolgt in den Zeilen 19 - 37. Zu beachten ist, dass die erste echo-Ausgabe mit einem einfachen > umgeleitet wird. Dadurch wird der der Inhalt der Datei überschrieben. Anhängen weiterer Zeilen erfolgt mit >>. Die echo-Anweisungen für Unix müssen übrigens deshalb extra escaped werden, weil ein echo /* generated file - do not edit */ erst alle Dateien unter / auflisten würde, dann "generated file - do not edit" ausgibt und dann alle Dateien im aktuellen Verzeichnis auflistet.

Wer ein Hobby mit Computern und Elektronik hat weiß, dass der Umgang mit technischen Dingen beileibe nicht für die Mehrheit der Leute zu den Lieblingsbeschäftigungen gehört - insbesondere, wenn das technische Ding nicht tut was es soll. Das zeigt sich nämlich immer dann, wenn die liebe Verwandt- und Bekanntschaft wieder auf der Matte steht mit einem fahrlässig, mutwillig oder ahnungslos malträtiertem Gerät in der Hand und dem Spruch
Du kennst dich doch mit so was aus ...
Und meistens handelt es sich dann um spannende und präzise formulierte Problemstellungen wie "Mein Windows ist total komisch geworden ..." "Ich krieg das Programm nicht ins Internet installiert ..." "Das Handy geht irgendwie nicht mehr ..."
... kannst du da nicht irgendwas machen?
Super nervig, so was. Aber manchmal, ganz selten nur, gibt es dann doch mal was Interessantes. - So hatte ich jüngst dieses Gefährt bekommen, um es zu reparieren:
camper mover
Banana for scale

Dieser kleine Panzer nennt sich "Camper Trolley" und ist eine Einparkhilfe für Wohnanhänger: der Anhänger wird oben angekoppelt und per Fernsteuerung navigiert man das ungleiche Gespann zur gewünschten Stelle auf dem Campingplatz. Das klingt zwar vielleicht erst mal etwas obskur, aber der Campingpanzer hat es durchaus in sich und bringt laut Hersteller eine enorme Zugkraft von 1500kg mit sich. - Das haben wir mit Abschleppseil und Bobbycar natürlich auch umgehend ausprobiert. Und außerdem macht es irre Spaß, damit durch den Sandkasten auf dem Spielplatz zu pflügen. Abseits von Spiel&Spaß und dem eigentlichen Verwendungszweck böte sich so ein kleines Monster aber auch prima als Basisplattform für einen Roboter an. Der Preis wäre mir für eine Roboterbastelei dann allerdings doch etwas zu hoch (um die 1k €, gebraucht, in der Bucht). Und leider ist das Teil ebenso langsam wie stark. Also wird das nix mit uns - ist repariert und geht zurück an die Besitzer. PS: Und was war nun eigentlich kaputt an dem Ding? ⇒ Die Senderbatterie war leer ;-)

Neues vom Roboterclub

Der Roboterclub-Freiburg hat sich wieder getroffen und es gab ein paar neue Basteleien zu bewundern. So wie diesen fernsteuerbaren WALL-E aus Lego und Pappe.
Wall-E aus Pappe
Kann Kopf und Arme bewegen, Dinge greifen und macht auch einem Terminator alle Ehre

Und auch Roboter aus dem 3D-Drucker sind jetzt keine Seltenheit mehr im Club:
freibot
Ein Roboter, welcher vom freiBot abstammt

Heimatkunde aus dem 3D-Drucker

Nachdem ich dieses Jahr bereits eine 3D-Karte im Miniformat gedruckt hatte, habe ich nun den Maßstab etwas erhöht:
3D-Karte
Blick nach Westen über das Dreisamtal nach Freiburg und in die Rheinebene

Sechs Kacheln á 20 x 20 cm, aneinander geklebt und mit Airbrush und Pinsel bemalt:
3D-Karte Freiburg
Freiburg, Hochrhein und Südschwarzwald (60 x 40 x 4 cm)
Zur Orientierung: Freiburg liegt im Zentrum. Links der Rhein mit ein paar Baggerseen. Aus der Rheinebene ehebt sich der Kaiserstuhl. Ganz oben links befindet sich Sélestat. Links unten kann man noch die östlichen die Ausläufer von Mulhouse erkennen. Rechts auf der Karte der Südschwarzwald mit dem Schluchsee. Links vom Schluchsee der Feldberg und unterhalb von Freiburg der Schauinsland.