y0o.de · GNU/Linux Nachrichten u.Ä.

🔒
❌ Über y0o.de
Es gibt neue verfügbare Artikel. Klicken Sie, um die Seite zu aktualisieren.
Ältere BeiträgeKoflers Blog

Ubuntu 21.10

14. Oktober 2021 um 16:11

Mit Ubuntu 21.10 »Impish Indri« hat Canonical das letzte Release vor der nächsten LTS-Version 22.04 fertiggestellt. Die wichtigsten Neuerungen lassen sich in zwei Punkten zusammenfassen: Ubuntu hat den Sprung auf Gnome 40 vollzogen (wenn auch nicht auf die aktuelle Version 41), und Firefox steht standardmäßig als Snap-Paket zur Verfügung. Das neue Installationsprogramm, an dem Canonical zur Zeit arbeitet, war noch nicht so weit gediehen, dass es für Version 21.10 zum Einsatz kommen konnte.

Ubuntu 21.10 verwendet Gnome 40 als Desktop — aber mit einem vertikalen Dock

Firefox und Snap

Leser meines Blogs wissen, dass ich kein ausgesprochener Fan der neuen Paketformate Snap und Flatpak bin. Ich verstehe natürlich den Nutzen distributions- und versionsunabhängiger Pakete, bin aber der Meinung, dass die Nachteile aufgrund des riesigen Overheads überwiegen. Unter Ubuntu 21.10 sind außer diversen Basispaketen nur der Paketmanager snap-store (das ist Canonicals Variante zu Gnome Software) sowie Firefox installiert:

snap list

Name               Version             Revision  Tracking         Herausgeber  Hinweise
bare               1.0                 5         latest/stable    canonical✓   base
core               16-2.51.7           11743     latest/stable    canonical✓   core
core20             20210928            1169      latest/stable    canonical✓   base
firefox            93.0-1              631       latest/stable/…  mozilla✓     -
gnome-3-38-2004    0+git.6ba6040       76        latest/stable/…  canonical✓   -
gtk-common-themes  0.1-59-g7bca6ae     1519      latest/stable/…  canonical✓   -
snap-store         3.38.0-66-gbd5b8f7  557       latest/stable/…  canonical✓   -

Der Platzbedarf für diese Pakete beträgt 674 MByte:

ls -lh /var/lib/snapd/snaps/

insgesamt 674M
-rw------- 1 root root 4.0K Oct 10 09:58 bare_5.snap
-rw------- 1 root root 100M Oct 10 09:58 core_11743.snap
-rw------- 1 root root  62M Oct 10 09:57 core20_1169.snap
-rw------- 1 root root 151M Oct 10 09:57 firefox_631.snap
-rw------- 1 root root 243M Oct 10 09:58 gnome-3-38-2004_76.snap
-rw------- 1 root root  66M Oct 10 09:58 gtk-common-themes_1519.snap
-rw------- 1 root root  55M Oct 10 09:58 snap-store_557.snap

Der Vorteil des Firefox-Snap-Pakets besteht darin, dass Canonical dieses Paket in Zukunft für sämtliche Versionen von Ubuntu warten kann. Ein nicht unerheblicher Nachteil besteht darin, dass der erste Start von Firefox Snap-bedingt spürbar langsamer als bisher erfolgt. (Ab dem zweiten Start ist der Unterschied kaum mehr wahrnehmbar.)

Das nächste Ärgernis ist die Verwaltung der Gnome Shell Extensions: Obwohl chrome-gnome-shell in Ubuntu standardmäßig installiert ist und ich auch die Firefox-Erweiterung Gnome Shell-Integration beim ersten Besuch von https://extensions.gnome.org/ installiert habe, kann Firefox die Extensions nicht verwalten. Meine Vermutung ist, dass das Paket chrome-gnome-shell im Ubuntu-Dateisystem für Firefox unzugänglich ist, weil dieser — Snap sei dank — ja quasi in seinem eigenen Betriebssystem läuft.

Firefox zickt bei der Darstellung der Gnome Shell Extensions

Ich bin der Sache nicht auf den Grund gegangen, weil es eine bequeme Alternative gibt: Ich habe Google Chrome installiert. Das Programm gibt es von Google als »richtiges« Paket samt eigener Paketquelle. (Es gibt in den offiziellen Ubuntu-Paketquellen übrigens noch immer ein DEB-Paket für Firefox. Dieses Paket soll aber in Version 22.04 verschwinden.)

Insgesamt stellt sich die Frage, ob Canonical mit der Snap-Entscheidung Firefox unter Ubuntu nicht endgültig den Todesstoß versetzt. Ein wenig gewinnt man in den letzten Jahren den Eindruck, Firefox entwickelt sich zu einem Programm für Idealisten.

Gnome 40

Über Gnome 40 habe ich schon genug geschrieben. Den aus meiner Sicht größte Mangel — das horizontale Dock — hat Canonical mit dem vorinstallierten Ubuntu Dock behoben. Dabei handelt es sich um eine Variante zu Dash to Dock (siehe auch Gnome 40 mit einem vertikalen Dock). Über das merkwürdige Aussehen des Papierkorb-Icons, das im Dock angezeigt wird, kann man streiten, aber davon abgesehen funktionieren sowohl Gnome 40 als auch das Dock wunderbar.

Die standardmäßig installierten Gnome Shell Extensions, dargestellt in Google Chrome

In den Systemeinstellungen kann zwischen einem hellen und einem dunklen Erscheinungsbild ausgewählt werden. Hier können Sie auch die Icon-Größe im Dock sowie dessen Position einstellen.

Gnome 40 im Dark Mode

Versionen

Basis             Desktop             Programmierung   Server
---------------   ------------------  --------------   --------------
Kernel     5.13   Gnome          40   bash       5.1   Apache     2.4
glibc      2.34   Firefox        93   docker   20.10   CUPS       2.3
X-Server   1.20   Gimp         2.10   gcc       11.2   MySQL      8.0
Wayland    1.19   LibreOffice   7.2   Java        11   OpenSSH    8.4
Mesa       21.2   Thunderbird    91   PHP        8.0   qemu/KVM   6.0
Systemd     248                       Python     3.9   Postfix    3.5
NetworkMan 1.32                                        Samba     4.13
GRUB       2.04 

Als Default-Java-Version gilt 11. Zur Auswahl stehen aber auch Java 16, 17 und sogar schon eine erste Testversion von Java 18. Der Umstieg auf PHP 8.0 ist willkommen; schade ist, dass Python 3.10 den Sprung in Ubuntu 21.10 nicht geschafft hat. (Zugegebenermaßen ist das Release gerade einmal vor 10 Tagen.)

Anmerkung

Für diesen Test habe ich Ubuntu 21.10 ausschließlich in einer virtuellen Maschine getestet. Grundsätzlich hat dabei alles wunschgemäß funktioniert. Einzig das Zusammenspiel mit Wayland hat Probleme verursacht (fallweise schwarzer Bildschirm, in Firefox kein Bild etc.). Ein neuerlicher Login mit X statt Wayland war die Lösung. Ob an den Problemen Wayland oder mein Virtualisierungssystem (KVM/QEMU) Schuld war, kann ich nicht sagen. Ähnliche Probleme hatte ich aber auch schon mit anderen Distributionen. Die Lösung hießt immer X.

Ich habe vor, mein Arbeits-Notebook nächste Woche auf Version 21.10 zu aktualisieren. Falls sich dabei neue Erkenntnisse ergeben (das ist anzunehmen), werde ich diesen Artikel noch einmal aktualisieren. Insbesondere möchte ich testen, wie gut Wayland mit den proprietären NVIDIA-Treibern harmoniert. (Die aktuelle Version der NVIDIA-Treiber ist erstmalig Wayland-kompatibel, aber zumindest laut Fedora-Berichten ist die Sache noch nicht richtig stabil.)

Fazit

Ich habe in den letzten Jahren Ubuntu als Standarddistribution auf meinem wichtigsten Arbeitsrechner verwendet (einem Lenovo-Notebook). Ja, ich habe mehr Rechner und noch viel mehr virtuelle Maschinen, auf denen ein buntes Sammelsurium von Distributionen läuft. Aber grundsätzlich hat sich Ubuntu in den letzten Jahren für mich gut bewährt; es läuft stabil, ich hatte selten ernsthafte Probleme (auch nicht mit Versionen außerhalb des LTS-Zyklus), selbst Microsoft Teams läuft (das brauche ich gelegentlich beruflich) und bin eigentlich zufrieden mit dem, was ich habe. Ganz pragmatisch: Es hat durchaus Vorteile, mit dem Linux-Mainstream mitzuschwimmen.

Bei Snap endet meine Liebe zu Ubuntu aber. Bisher war mein Ansatz, Snap samt allen dort mitgelieferten Paketen einfach zu deinstallieren. Noch ist das möglich: Ich kann Firefox durch ein APT-Paket oder gleich durch Google Chrome ersetzen, und statt dem snap-store verwende ich sowieso apt. Sollte ein Snap-freier Betrieb von Ubuntu irgendwann nicht mehr möglich sein, dann wird es mir sicher gelingen, mich mit einer anderen Linux-Distribution anzufreunden :-)

Quellen / Links / Andere Tests

Linux (17. Aufl.) erschienen

30. September 2021 um 15:12

Soeben ist mein Linux-Buch in der (unglaublich!) 17. Auflage beim Rheinwerk-Verlag erschienen!

Wie üblich habe ich das Buch im Sommer komplett aktualisiert, d.h., der gesamte Text wurde an die bis August 2021 verfügbaren Distributionen und Software-Versionen angepasst. Die folgende Liste nennt in Stichpunkten weitere Neuerungen:

  • Distributionen: AlmaLinux, Manjaro Linux, Oracle Linux, Rocky Linux
  • Desktop-Nutzung: Gnome 40, draw.io, Visual Studio Code
  • Shell: zsh als zunehmend attraktive bash-Alternative
  • Let’s Encrypt: Zertifikate mit acme.sh einrichten
  • SSH: Zweifaktorauthentifizierung (2FA) mit Google Authenticator und YubiKey
  • Firewalls: von iptables zu nft
  • Docker: Container ohne root-Rechte, Podman
  • Linux unter Windows: WSL2 und WLSg

Noch mehr Details zum Buch finden Sie hier.

 

Docker (3. Aufl.) erschienen

01. September 2021 um 15:50

Docker ist seit einigen Jahren die Schlüsseltechnologie abseits aller Programmiersprachen, die den Arbeitsalltag zahlloser Entwickler enorm vereinfacht. Docker ermöglicht es, Sprachen, Server, Linux-Umgebungen etc. in beliebigen Versionen parallel zueinander zu installieren und auszuführen — und das unter Linux, Windows und macOS.

Für die dritte Auflage haben wir das Buch vollständig aktualisiert und die Einführungskapitel übersichtlicher strukturiert. Wichtige inhaltliche Neuerungen sind:

  • Rootless Docker: Docker ohne root-Rechte verwenden
  • CPU-Architekturen: Docker auf Apple-Computern mit ARM-Prozessoren
  • Pull-Limit: Docker-Hub-Limits beim Image-Zugriff umgehen
  • Container automatisch starten: restart-Option und systemd
  • neue GUI-Tools: Docker Desktop, VSCode, Portainer
  • Traefik: ein Proxy-Server speziell für Container-Anwendungen

Weitere Infos finden Sie hier.

Debian 11 »Bullseye«

01. September 2021 um 11:21

Es gibt — wie immer — zwei Sichtweise auf das neue Debian: Die positive (»Das Glas ist halb voll«) Interpretation geht in die Richtung, dass Debian im Vergleich zum letzten Release deutlich moderner geworden ist, teilweise nahezu aktuelle Software-Versionen ausliefert, neue Funktionen bietet — und das für viel mehr Plattformen als bei jeder anderen Linux-Distribution.

Die nicht so euphorische Sichtweise (»Das Glas ist halb leer«) bedauert die im Vergleich zu Fedora oder Ubuntu nicht ganz so aktuelle Software-Ausstattung und das unverändert altmodische Erscheinungsbild des Installationsprogramms. Andererseits erfüllt der Installer seinen Zweck — und wer es gerne moderner hat, kann ja den Calamares-Installer der Live-Medien verwenden.

Das Erscheinungsbild des Debian-Installationsprogramms ist seit vielen Jahren unverändert geblieben.

Bei der Installation stehen diverse Desktop-Systeme zur Auswahl.
Standardmäßig verwendet Debian 11 als Desktopsystem Gnome 3.38.

Neuerungen

Abseits von Versionsnummern gibt es nur wenige grundlegende Neuerungen in Debian 11:

  • Unkomplizierte exFAT-Unterstützung (für große SD-Karten)
  • Treiberloses Drucken/Scannen mit vielen neuen Geräten (dank IPP-over-USB sowie eSCL und WSD)

  • Mit open datei kann aus dem Terminal heraus ein GUI-Programm als Hintergrundprozess geöffnet werden. open Downloads/bild.jpg startet beispielsweise den Bildbetrachter. open ist ausgesprochen praktisch und wird vor allem macOS-Umsteiger erfreuen. (Unter macOS gibt es ein entsprechendes Kommando schon seit vielen Jahren.) Intern ist open einfach ein via updates-alternatives --config open verwalteter Link auf das schon länger etablierte Script xdg-open, das die MIME-Einstellungen auswertet. (Anstelle von xdg-open kann auch run-mailcap eingestellt werden.)

  • Control Groups v2: Die Kernel Control Groups zur Überwachung/Steuerung von Prozessen liegt jetzt in Version 2 vor. Das ist wichtig u.a. für Container-Systeme wie Docker oder Podman. Diese Umstellung kann allerdings Probleme mit OpenStack verursachen (Details).

  • User Namespaces aktiv: Eine Neuerung von Kernel 5.10 besteht darin, dass normale Benutzer User Namespaces verwenden dürfen. Das ist wichtig für Containersysteme (Rootless Docker, Podman).

  • FUSE 3: Die Dateisysteme gvfs-fuse, kio-fuse und sshfs nutzen nun FUSE 3 anstelle der bisher üblichen Version 2.

  • Das von systemd stammende Logging-System Journal wird jetzt dauerhaft im Binärformat in /var/log/journal gespeichert, geht also nicht wie in früheren Debian-Versionen mit jedem Reboot verloren. (Parallel zum Journal läuft weiterhin auch der rsyslogd und erzeugt die traditionellen Text-Logging-Dateien in /var/log.)

  • Passwort-Hashes in der Datei /etc/shadow wurden von SHA-512 auf das sicherere Verfahren yescrypt umgestellt.

Ärgernisse

sudo: Ein wenig irritierend ist, dass der »gewöhnliche« Installer (also nicht der der Live-Medien) nach wie vor keine Möglichkeit bietet, den neuen Benutzer zur sudo-Gruppe hinzuzufügen. Das ist mittlerweile bei fast allen anderen Distributionen das Standardverhalten.

Abhilfe: Führen Sie mit root-Rechten usermod -a -G sudo <accountname> aus, um sudo für den betreffenden Account zu aktivieren.

Bitte legen Sie das Medium mit dem Namen ‚Debian GNU/Linux‘ ein: Der Standardinstaller hinterlässt in /etc/apt/sources.list eine Zeile mit dem Installationsmedium (USB-Stick oder DVD). Wenn Sie nach der Installation ein Paket installieren wollen (apt install <name>), will apt, dass Sie das Installationsmedium wieder einlegen, anstatt das betreffende Paket einfach herunterzuladen. Das ist (schon seit vielen Jahren) nicht mehr zeitgemäß.

Abhilfe: Öffnen Sie /etc/apt/sources.list mit einem Editor und entfernen Sie die Zeile, die mit deb cdrom beginnt.

Cannot set locale en_US.utf-8: Wie aktuell bei diversen anderen Distributionen tritt auch bei Debian ein Problem mit den Lokalisierungseinstellungen ein, wenn während der Installation die deutsche Sprache voreingestellt wird. Nach einem SSH-Login jammert Debian: bash: warning: setlocale: LC_ALL: cannot change locale (en_US.utf-8). Es wurde also die deutsche Lokalisierung installiert, nicht aber die englische (die als Backup immer zur Verfügung stehen sollte).

Abhilfe: Führen Sie mit root-Rechten dpkg-reconfigure locales aus und aktivieren Sie zusätzlich zur schon voreingestellten Lokalisierung auch en_US.utf-8.

Die fehlende Lokalisierung »en_US.UTF-8« aktivieren

Versionsnummern

Basis             Desktop             Programmierung   Server
---------------   ------------------  --------------   --------------
Kernel     5.10   Gnome        3.38   bash       5.1   Apache     2.4
glibc      2.31   Firefox ESR    78   docker   20.10   CUPS       2.3
X-Server   1.20   Gimp         2.10   gcc       10.2   MariaDB   10.5
Wayland    1.20   LibreOffice   7.0   Java     11/17   OpenSSH    8.4
Mesa       20.3   Thunderbird    78   PHP        7.4   qemu/KVM?  5.2
Systemd     247                       Python     3.9   Postfix    3.5
NetworkMan 1.30                                        Samba     4.13
GRUB       2.04 

Der Linux-Kernel ist zwar nicht ganz aktuell, dafür genießt er aber Langzeitunterstützung durch das Kernel-Entwickler-Team.

Die Unterstützung von Java 17 ist insofern bemerkenswert, als die kommende LTS-Version von Java noch gar nicht fertig ist. Indem schon jetzt Pakete mitgeliefert werden, können später (vorauss. im Okt. oder Nov. 2021) unkompliziert Updates installiert werden. Als »offizielle« Java-Version von Bullseye gilt aber Java 11. Die Release Notes weisen darauf hin, dass es für Java 17 voraussichtlich keine quartalsmäßigen Updates geben wird (was schade ist).

Plattformen (Architekturen)

Debian 11 steht für die folgenden Plattformen zur Verfügung:

  • Standard-PCs: i386 und amd64
  • ARM: arm64, armhf, armel
  • MIPS: mipsel, mips64el
  • PowerPC: ppc64el
  • S390X: s390x

Weitere Details zur Hardware-Unterstützung können Sie hier nachlesen:

Fazit

Debian hat einmal mehr ein grundsolides Release geliefert. Sensationen bleiben aus, Glanz und Charme versprüht die Distribution auch nicht. Dafür läuft Debian zuverlässig und stabil und bildet direkt (Ubuntu, Raspberry Pi OS) oder indirekt (Ubuntu Derivate) die Basis für zahlreiche weitere Distributionen. Insofern macht Debian — ohne über ein Budget wie Canonical, IBM/Red Hat oder SUSE zu verfügen — alles richtig. Wer mehr Aktualität sucht, kann den Testing-Zweig verwenden.

Quellen und Testberichte

Download-Links (jeweils für AMD/Intel 64 Bit)

Mehrfachauswahl im RecyclerView

13. August 2021 um 06:49

Kürzlich habe ich eine Leserzuschrift zu meinem Kotlin-Buch erhalten: »Wie realisiert man eine Mehrfachauswahl in einer Liste innerhalb einer Android-App?«. Konkret bezog sich die Frage auf die RecyclerView, die ich im Abschnitt 23.7 »Listen und Tabellen« recht ausführlich behandle.

In meiner Antwort beziehe ich mich auf das im Buch präsentierte Beispielprogramm, das in einer RecyclerView die die deutschen Bundesländer inklusive einiger Eckdaten anzeigt.

Mehrfachauswahl in einer »RecyclerView«

Datenmodell

Die erste Voraussetzung für eine Mehrfachauswahl besteht darin, dass es einen Ort gibt, wo Sie sich die Auswahl merken. In meinem Beispielprogramm ist die Datenquelle einfach ein Array von Country-Objekten. Also habe ich dieser Klasse eine zusätzliche Eigenschaft isSelected hinzugefügt:

class Country(val name: String,
              val area: Double,
              val population: Int,
              val capital: String,
              var isSelected: Boolean = false)  // <- neu!
{
   // Code unverändert ...
}

RecyclerView-Adapter

Im RecyclerView-Adapter ist die Methode onBindViewHolder für die grafische Darstellung der Listenelemente zuständig. Hier müssen Sie Code einbauen, um ausgewählte Einträge optisch hervorzuheben. Ich habe mich dazu entschieden, einfach die Hintergrundfarbe grau statt weiß darzustellen — aber es gibt natürlich viele andere Optionen.

class CountryAdapter(private val countries: List<Country>,
                     private val context: Context)
    : RecyclerView.Adapter<CountryVH>()
{
     ...
    override fun onBindViewHolder(holder: CountryVH,
                                  pos: Int)
    {
        holder.txtCountry.text = countries[pos].name
        // ... usw.

        // neuer Code: Hintergrundfarbe je nach Auswahl
        if (countries[pos].isSelected)
            holder.itemView.setBackgroundColor(Color.LTGRAY)
        else
            holder.itemView.setBackgroundColor(Color.WHITE)
        ...
     }
}

Reaktion auf das On-Click-Ereignis

Ebenfalls in onBindViewHolder wird ein Lambda-Ausdruck übergeben, der immer dann ausgeführt wird, wenn der Benutzer der App einen Eintrag der Liste berührt, um diesen auszuwählen bzw. um die Auswahl wieder aufzuheben. Dort sind zwei Dinge wichtig: Zum Einen wird die isSelected-Eigenschaft des Array-Elements der Datenquelle geändert. Und zum Anderen wird für die gesamte RecyclerView mit notifyDataSetChanged neu gezeichnet. (Andernfalls würde die Statusänderung nicht sofort sichtbar.)

class CountryAdapter(private val countries: List<Country>,
                     private val context: Context)
    : RecyclerView.Adapter<CountryVH>()
{
     ...
    override fun onBindViewHolder(holder: CountryVH,
                                  pos: Int)
    {
        ...
        // Reaktion auf Auswahl des Listeneintrags
        holder.itemView.setOnClickListener {
            // aktuelle Auswahl invertieren
            countries[pos].isSelected = !countries[pos].isSelected
            // ganze Liste neu zeichnen
            this.notifyDataSetChanged()
        }
    }
}

Beispielcode

Den gesamten Beispielcode finden Sie hier zum Download:

https://kofler.info/uploads/kotlin/kap23-recycler-multiselect.zip

Bitte beachten Sie, dass das Beispiel im Vergleich zu den Listings im Buch auf die neue ViewBinding-Technik umgebaut wurde (siehe auch Kotlin-Updates: Android Studio 4.2).

Kotlin-Updates: Jetpack Compose 1.0

27. Juli 2021 um 18:22

In Teil 7 der Kotlin-Updates-Serie werfe ich einen Blick auf Jetpack Compose. Als ich mein Kotlin-Buch im Herbst 2020 fertiggestellt habe, gab es erst eine Alpha-Version dieses neuen UI-Frameworks. Mittlerweile ist Jetpack Compose fast fertig. Weil die mittlerweile stattgefundenen Änderungen umfassend waren, habe ich das betroffene Kapitel 26 vollständig aktualisiert. Sie finden die PDF-Datei am Ende dieses Blog-Beitrags zum Download. Das Kapitel bietet einen guten Einstieg in die Zukunft der Android-Programmierung!

Dieser Text bezieht sich auf die folgenden Versionsnummern:

Android Studio Arctic Fox = Android Studio 2020.3.1 (veröffentlicht im Juli 2021)
Kotlin: 1.5.10
Jetpack Compose: 1.0
Gradle: 7.0

Anmerkung: Die ursprüngliche Fassung dieses Artikels wurde nach den Releases von Jetpack Compose 1.0 und Android Studio Arctic Fox nochmals aktualisiert.

Anmerkung 2 (August 2021): Mittlerweile funktioniert es auch mit Kotlin 1.5.21 und Compose 1.0.1.

Was ist Jetpack Compose?

Jetpack Compose eröffnet einen neuen Weg, um Benutzeroberflächen für Android-Apps zusammenzustellen: Anstatt die Aktivitäten und Steuerelemente wie bisher in einem grafischen Editor einzurichten und intern im XML-Format zu speichern, wird dazu nun Kotlin-Code mit der Annotation @Composable verwendet.

Der riesige Vorteil von Jetpack Compose besteht darin, dass Sie sich nicht mehr länger mit dem sperrigen Layouteditor und unübersichtlichen Layoutregeln herumärgern müssen. Vielmehr können Sie das Erscheinungsbild Ihrer App durch verhältnismäßig kompakten Code ausdrücken und es direkt in Android Studio ansehen und testen. Das geht schneller, lässt sich besser dokumentieren und rascher ändern. Interessanterweise beschreitet Apple mit SwiftUI genau denselben Weg. Ein weiterer Vertreter in der aktuell rasch wachsenden Familie von deklarativen UI-Frameworks ist Flutter (siehe https://flutter.dev). Es gibt also aktuell einen regelrechten Trend hin zu sogenannten deklarativen UI-Frameworks.

Licht und Schatten

Das Konzept von Jetpack Compose begeistert mich. Das liegt daran, dass ich lieber Code verfasse als umständlich per Maus oder Trackpad in versteckten Dialogen nach Layoutoptionen zu suchen.

Dessen ungeachtet ist das Compose-Konzept keineswegs frei von Problemen. Ein offensichtlicher Nachteil besteht im exzessiven Lambda-Einsatz, der zu einer Klammernhölle wie in JavaScript führt. Sobald Ihre App mehr als »Hello World!« am Bildschirm darstellen soll, lassen sich schier endlose Verschachtelungen von Compose-Funktionen kaum vermeiden.

Den Einstieg in Compose erschweren zudem unzählige Anleitungen in Blogs und auf Stack Overflow, die sich auf frühe Compose-Versionen beziehen und längst nicht mehr funktionieren. Ebenfalls irritierend sind die relativ langen Build-Zeiten, die einem flotten interaktiven Layoutprozess im Wege stehen.

Jetpack Compose ist übrigens eine radikale Absage an Java: Jetpack Compose erfordert ein spezielles Kotlin-Compiler-Plugin und kann nicht durch Java-Code genutzt werden.

Voraussetzungen

Damit Sie Jetpack Compose ausprobieren können, müssen Sie Android Studio Arctic Fox verwenden.

Falls Sie vorher eine Testversion von Android Studio installiert haben, kommt es beim Start der finalen Version womöglich zur Fehlermeldung Missing essential plugin: org.jetbrains.android. Abhilfe: Entfernen Sie aus .config/Google/AndroidStudio2020.3/disabled_plugins.txt die Zeile org.jetbrains.android. Hintergründe: stackoverflow

Die Kotlin-Version ist in den beiden build.gradle-Dateien (Project und Module) mit 1.5.10 festgelegt. Neuere Versionsnummern wurden im Juli 2021 nicht unterstützt und führten zum Build-Fehler Execution failed for task app:compileDebugKotlin.)

Von der Alpha-Version zu Version 1.0

Es gibt unzählige Änderungen in Jetpack Compose 1.0 im Vergleich zu der im Buch beschriebenen Alpha-Version:

  • Die Klassen und Methoden sind innerhalb des Frameworks vollkommen neu organisiert. Deswegen sind nun ganz andere Importe erforderlich.
  • Beim Erzeugen von Images und Icons muss zwingend der Parameter contentDescription angegeben werden, der das Bild oder Symbol beschreibt (zur Not mit null).

  • Viele Namen von Funktionen, Methoden, Parametern und Eigenschaften haben sich geändert. Manche Parameter sind zugunsten zusätzlicher Modifier-Eigenschaften ganz verschwunden.

AppCompatActivity            -> ComponentActivity
bodyContent                  -> content
ContextAmbient               -> LocalContext
gravity                      -> align
horizintalGravity            -> horizontalAlignment
icon                         -> content
keyboardType                 -> keyboardOptions
imageResource                -> painterResource
preferredHeight/-Size/-Width -> requiredHeight/-Size/-Width
showDecoration               -> showSystemUi
Stack                        -> Box
verticalGravity              -> verticalAlignment

Es ist nahezu unmöglich, vorhandenen Code, der eine alte Version von Jetpack Compose verwendet, auf die gerade aktuelle Version zu adaptieren — ganz einfach weil die Anzahl der Änderungen zu groß ist. Effizienter ist es in der Regel, den Code in kleinen Stücken neu zu implementieren.

Weil mir die Auflistung unzähliger Änderungen hier nicht zielführend erscheint, habe ich mich dazu entschlossen, den Text sowie alle Beispiele in Kapitel 26 vollständig zu aktualisieren. Sie finden die Download-Links am Ende dieses Blog-Beitrags.

UI-Status erhalten mit rememberSaveable

remember-Variablen und die damit verbundenen Steuerelemente bzw. Compose-Funktionen verlieren ihren Status, wenn die Oberfläche vollständig neu aufgebaut werden muss. Das ist beispielsweise der Fall, wenn der Nutzer einer App sein Smartphone ins Querformat dreht oder wenn die Nutzerin in den Dark Mode wechselt. In der Alpha-Version von Jetpack Compose war dieses Problem noch ungelöst. Das hat sich zum Glück geändert. Sie deklarieren nun die entsprechende Variable einfach mit rememberSaveable:

// erhält den Status des Klick-Zählers auch bei einer 
var cnt by rememberSaveable { mutableStateOf(0) }

Tastatur ausblenden mit dem LocalSoftwareKeyboardController

Zur Steuerung der Tastatur sieht Jetpack Compose das noch experimentelle Objekt LocalSoftwareKeyboardController vor:

@ExperimentalComposeUiApi
@Composable
fun MyComposeFunction() {
    val kbdController = LocalSoftwareKeyboardController.current
    ...
    Button(content = { Text("OK") },
           onClick = { kbdController?.hide() } )
    ...
}

Quellen und Links

Jetpack Compose

Android Studio

Downloads

Die aktualisierten Beispielprojekte zu Kapitel 26 können Sie hier herunterladen:

https://kofler.info/uploads/kotlin/kap26.zip

Beachten Sie, dass Sie Android Studio Arctic Fox installieren müssen, um die Beispiele auszuprobieren!

Das vollständig aktualisierte Kapitel 26 mit einem Umfang von 38 Seiten finden Sie hier:

https://kofler.info/uploads/kotlin/jetpack-compose-1.0.pdf

Der geänderte Text wurde nicht korrekturgelesen und wird daher den einen oder anderen Tipp- und Rechtschreibfehler enthalten.

Die Kotlin-Updates-Serie

Weitere Kotlin-Update-Artikel finden Sie hier auf meiner Website:

https://kofler.info/tag/kotlin-updates

Gnome 40 mit einem vertikalen Dock

25. Juli 2021 um 19:32

Vor ein paar Wochen habe ich hier im Blog kritisiert, dass mich Gnome 40 zu einem horizontalen Dock zwingen will, dass noch dazu zumeist ausgeblendet wird. Mag sein, dass manche dies auf einem kleinen Notebook-Monitor zweckmäßig finden, für mich ist es keine Option. Ich will ein ständig sichtbares Dock, und ich will es am linken Bildschirmrand. Und mittlerweile ist mein Wunsch auch erfüllbar, wie ich in diesem Beitrag anhand von drei Distributionen (Fedora 34, Manjaro, Ubuntu 21.10) zeige.

Das Gnome Entwickerteam vertritt zwar die Ansicht, Shell extensions are always going to be a niche thing, aber da Ubuntu und einige andere Distributionen solche Extensions per Default installieren, wird die Nische womöglich größer als das Original …

Anmerkung: Die Gnome-Nomenklatur nennt das Dock übrigens »Dash«, aber ich bleibe in diesem Artikel bei dem etablierteren Begriff »Dock«.

Ubuntu 21.10 (Entwicklerzweig)

Während Ubuntu 21.04 bekanntlich Gnome 40 ignoriert und stattdessen Version 3.38 ausgeliefert hat, enthält der Entwicklerzweig von Ubuntu 21.10 mittlerweile durchgängig Gnome-40-Pakete. Um dennoch wie in bisherigen Versionen ein vertikales Dock anbieten zu können, ist unter Ubuntu das Paket gnome-shell-extension-ubuntu-dock vorinstalliert. Es enthält eine modifizierte, zu Gnome 40 kompatible Variante der Gnome-Erweiterung Dash-to-Dock. Die Konfiguration kann bequem in den Gnome-Einstellungen erfolgen. Die wenigen Optionen sollten für die meisten User ausreichen. Anders als im originalen Gnome 40 kann das Dock links, rechts oder unten platziert werden. Außerdem kann die Breite eingestellt werden. Für mich reicht das aus.

Der Default-Desktop im Entwicklerzweig von Ubuntu 21.10

Manjaro

Auch Manjaro hat kürzlich auch Gnome 40 umgestellt. Genaugenommen bot das Update-System schon längere Zeit Gnome-40-Pakete an. Nur mit dem Update der Gnome-Shell hat man relativ lange gewartet, wohl um vorher das Manjaro-spezifische Programm Manjaro Hello Gnome-40-tauglich zu machen. Dieses Programm bietet die Möglichkeit, unkompliziert zwischen verschiedenen Desktop-Layouts zu wechseln. Intern werden die Layouts ähnlich wie bei Ubuntu durch verschiedene Gnome Shell Extensions realisiert. Für das Layout Manjaro Legacy mit vertikalem Dock kommt wie unter Ubuntu die Erweiterung Dash-to-Dock zum Einsatz. Dennoch sieht das Dock ein wenig anders aus, weil andere Default-Einstellungen zum Einsatz kommen.

Manjaro Linux mit Gnome 40 und dem »Manjaro Legacy« Layout mit Dash-to-Dock

Fedora 34

Bei meiner Fedora-34-Installation habe ich anstelle von Dash-to-Dock die mir bis dahin unbekannte Erweiterung Dash-to-Panel eingesetzt. Sie bietet ähnliche Funktionen wie Dash-to-Dock aber womöglich noch mehr Konfigurationsmöglichkeiten. Der größte Vorteil besteht darin, dass diese Erweiterung schon seit Wochen out of the box, also ohne Rückgriffe auf irgendwelche Testzweige oder Forks, unter Gnome 40 stabil funktioniert. Ich habe damit sehr gute Erfahrungen gemacht, wenngleich die Optionsfülle selbst für meine Begriffe ein wenig über das Ziel schießt. Aber egal, alles besser als gar keine Optionen …

Fedora 34 mit Gnome 40 und Dash-to-Panel

Quellen/Links

Kotlin-Updates: Android Studio 4.2

16. Juli 2021 um 15:00

Weiter geht’s mit Teil 6 der Kotlin-Updates-Serie. Diesmal gehe ich auf die Neuerungen ein, die sich mit Android Studio 4.2 ergeben. Den größten Änderungsbedarf verursacht das Gradle-Plugin kotlin-android-extensions, das jetzt deprecated ist.

Dieser Text bezieht sich auf die folgenden Versionsnummern:

Android Studio: 4.2.2
Kotlin: 1.5.21
Gradle: 6.7

Neue Projekte

Wenn Sie in Android Studio 4.2.2 mit allen Update ein neues Projekt einrichten, kommen Kotlin 1.5.21 und Gradle 6.7 zum Einsatz. build.gradle (Projekt) enthält zwar weiterhin den Verweis auf das jcenter-Repository, aber auch den Hinweis, dass diese Paketquelle demnächst nicht mehr aktiv sein wird. Am besten kommentieren Sie die Quelle aus.

Bei meinen Experimenten trat immer wieder die Fehlermeldung Path.op() not supported auf. Offenbar muss in Android Studio die neue Rendering Engine aktiviert werden: Settings/Experimental/Use new Layout Rendering Engine.

Beachten Sie, dass Android Studio 4.2 merkwürdigerweise selbst bei neuen Projekten das »View Binding« (siehe unten) nicht automatisch aktiviert und Sie diesbezüglich im Regen stehen lässt. Merkwürdig!

Sie müssen die unten beschriebenen Aktionen auch in neuen Projekten durchführen. Das gilt insbesondere für die Datei build.gradle (Module), in die Sie buildFeatures { viewBinding true } einfügen müssen, als auch für MainActivity.kt, wo Sie die Variable binding deklarieren und den Code in onCreate entsprechend ändern müssen.

Vorhandene Projekte aktualisieren

Um zu testen, wie einfach die Aktualisierung älterer Projekte gelingt, habe ich in den Gradle-Dateien des Währungsumrechner (siehe Kapitel 25 im Kotlin-Buch) sämtliche Versionsnummern auf den neuesten Stand aktualisiert. Offen gesagt war ich auf Kompatibilitätsprobleme eingestellt, aber überraschenderweise funktionierte der Code auch nach dem Umbau fehlerfrei.

kotlin-android-extensions is deprecated

So weit, so gut! Allerdings liefert das Build-System eine neue Warnung:

The kotlin-android-extensions Gradle plugin is deprecated. Please use this migration guide (https://goo.gle/kotlin-android-extensions-deprecation) to start working with View Binding (https://developer.android.com/topic/libraries/view-binding) and the kotlin-parcelize plugin.

Vorerst können Sie diese Warnung ignorieren. Auch wenn das Gradle-Plugin deprecated ist — noch funktioniert es ja. Ich habe die Vermutung, dass dies aufgrund unzähliger Projekte, die davon abhängig sind, noch eine Weile so bleiben wird. Aber längerfristig ist die Verwendung von deprecated-Komponenten selten eine gute Idee. Und bei neuen Projekten fehlt das Plugin ohnedies. Es wird Ihnen also nicht erspart bleiben, sich an neue Arbeitstechniken zu gewöhnen.

Welche Funktion hatte nun diese kotlin-android-extension? Dieses Gradle-Plugin stellte in der Vergangenheit sicher, dass Sie im Code unmittelbar auf Steuerelemente zugreifen konnten. Wenn Sie also ein TextView-Steuerelement mit mytext benannten (id-Eigenschaft), dann konnten Sie in der Folge im Code mit mytext.text = "abc" den dort angezeigten Text anzeigen. import kotlinx.android.synthetic.main.fragment_about.view.* und vom Plugin erzeugter synthetischer Code stellten sicher, dass der Steuerelementzugriff wie von Zauberhand funktionierte.

Die nunmehr empfohlene Vorgehensweise sieht wie folgt aus: Zuerst bauen Sie in build.gradle auf Modulebene die folgenden Zeilen ein. (Merkwürdigerweise fehlt dieser Code auch bei neu eingerichteten Projekten.) Es ist keine Referenz auf neue Bibliotheken oder Plugins erforderlich.

// Datei build.gradle (Module)
android {
    ...
    // die folgenden drei Zeilen hinzufügen, um den neuen 
    // View-Binding-Mechanismus zu aktivieren
    buildFeatures {
        viewBinding true
    }
}

In der Klassendatei für die Aktivität brauchen Sie eine neue binding-Variable. Deren Datentyp setzt sich aus dem Namen der Layout-Datei aus. Wenn es also bei einem Minimalprojekt die Datei activity_main.xml gibt, hat die resultierende Binding-Klasse den Namen ActivtyMainBinding.

Diese Variable initialisieren Sie in onCreate. In der Folge können Sie dann auf alle Steuerelemente in der Form binding.<name> zugreifen. Der Code zum Hello-World-Projekt aus Kapitel 21, wo nach dem Klick auf einen Button in einem Textfeld Datum und Uhrzeit angezeigt werden, sieht damit so aus:

// Datei MainActivity.kt
...
import info.kofler.test_as_42.databinding.ActivityMainBinding

class MainActivity : AppCompatActivity() {

    // layout/activity_main.xml -> ActivityMainBinding usw.
    private lateinit var binding: ActivityMainBinding

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        // Zugriff auf Steuerelemente via binding
        binding = ActivityMainBinding.inflate(layoutInflater)

        // den folgenden Code ausführen, wenn 'mybutton' angeklickt wird
        binding.mybutton.setOnClickListener {
            val loc = Locale.getDefault()
            val fmt = DateFormat.getDateTimeInstance(
                DateFormat.LONG, DateFormat.LONG, loc)
            binding.mytext.text = "Datum und Uhrzeit:\n" + fmt.format(Date())
        }

        setContentView(binding.root)
    }
}

Die zu ändernden Code-Passagen finden Sie am schnellsten, wenn Sie die Anweisung import kotlinx.android.synthetic.main.activity_main.* auskommentieren. Alle Zugriffe auf Steuerelemente ohne binding werden dann als Fehler markiert.

Für Fragmente gilt an sich die gleiche Vorgehensweise. Allerdings ist der Zugriff auf binding erst ab dem Aufruf von onCreateView() und nur bis zum Aufruf von onDestroyView() erlaubt. Die Dokumentation empfiehlt deswegen, binding als Property zu implementieren und eine zusätzliche Variable _binding einzuführen. Der Code sieht dann so aus:

class MyFragment : Fragment(), CoroutineScope by MainScope() {

    // layout/fragment_myname.xml -> FragmentMynameBinding usw.
    private var _binding: FragmentMynameBinding? = null
    // This property is only valid between onCreateView and
    // onDestroyView!
    private val binding get() = _binding!!

    override fun onCreateView(inflater: LayoutInflater,
                              container: ViewGroup?,
                              savedInstanceState: Bundle?): View?
    {
        _binding = FragmentAboutBinding.inflate(inflater, container, false)

        // Zugriff auf Steuerelemente via binding
        binding.mytext.text = "abc"
        binding.mybutton.setOnClickListener { ... }

        return binding.root
    }
}

Ein wenig diffiziler ist die Umstellung von Adapter- und ViewHolder-Klassen zur Darstellung von Listen. Das folgende, stark gekürzte Listing zeigt die prinzipielle Vorgehensweise anhand für das Layout item_currency.xml und die zugehörige Code-Datei CurrencyAdapter.kt des Währungsumrechners (Kapitel 25):

// an die ViewHolder-Klasse binding statt view übergeben
class CurrencyViewHolder(val binding: ItemCurrencyBinding)
    : RecyclerView.ViewHolder(binding.root)
{
  val txtCurrency : TextView = binding.txtCurrency
  val imgFlag : ImageView = binding.imgFlag
}

// in der Adapter-Klasse in onCreateViewHolder mit Bindings arbeiten
class CurrencyAdapter(private val cc: CurrencyCalculator,
                      private var selectedCurrency: MutableLiveData<String>,
                      private val context: Context)
  : RecyclerView.Adapter<CurrencyViewHolder>()
{
  ...

  override fun onCreateViewHolder(
    parent: ViewGroup, viewType: Int): CurrencyViewHolder
  {
    // layout/item_currency.xml -> ItemCurrencyBinding usw.
    val binding = ItemCurrencyBinding.inflate(LayoutInflater.from(parent.context), parent, false)
    binding.root.setOnClickListener {
      selectedCurrency.value = binding.txtCurrency.text.toString()
      notifyDataSetChanged()   // alles neu zeichnen
    }
    return CurrencyViewHolder(binding)
  }
}

Um ein vorhandenes Projekt auf die neue Binding-Technologie umzustellen, bauen Sie zuerst in build.gradle die Anweisung viewBinding true ein. Dann bauen Sie eine Activity- bzw. Fragment-Klasse nach der anderen um. (Ein Mischbetrieb zwischen der veralteten Kotlin-Android-Extension und Bindings ist erlaubt.) Zuletzt entfernen Sie die kotlin-android-extensions-Zeile aus build.gradle.

Ich habe den Währungsumrechner (Kapitel 25) innerhalb einer halben Stunde entsprechend umgestellt. Wenn man den neuen Mechanismus einmal verstanden hat, ist das keine Hexerei. Dennoch ist es natürlich ein mühsamer Prozess, und es ist ärgerlich, dass es für derartige Arbeiten keinen Assistenten gibt. Xcode bietet diesbezüglich viel mehr Komfort als Android Studio.

build.gradle (Module)

Zum Abschluss als Referenz noch die vollständige Datei build.gradle für das Modul des Währungsumrechners:

plugins {
    id 'com.android.application'
    id 'kotlin-android'
    id 'kotlinx-serialization'
    // id 'kotlin-android-extensions' -> ersetzt durch View Binding
}


android {
    compileSdkVersion 30
    defaultConfig {
        applicationId "info.kofler.currencyconverter"
        minSdkVersion 26
        targetSdkVersion 30
        versionCode 1
        versionName "1.0"
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
        }
    }
    buildFeatures {
        viewBinding true
    }
}

dependencies {
    implementation fileTree(dir: 'libs', include: ['*.jar'])
    implementation 'androidx.appcompat:appcompat:1.3.0'
    implementation 'androidx.core:core-ktx:1.6.0'
    implementation 'androidx.constraintlayout:constraintlayout:2.0.4'

    implementation 'androidx.legacy:legacy-support-v4:1.0.0'
    implementation 'androidx.navigation:navigation-fragment-ktx:2.3.5'
    implementation 'androidx.navigation:navigation-ui-ktx:2.3.5'
    implementation 'androidx.lifecycle:lifecycle-extensions:2.2.0'

    // Koroutinen
    implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.5.0'
    implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.6'
    implementation 'com.google.code.gson:gson:2.8.6'

    // Serialisierungs-Runtime
    implementation "org.jetbrains.kotlinx:kotlinx-serialization-core:1.2.1"
    implementation "org.jetbrains.kotlinx:kotlinx-serialization-json:1.2.1"
}

Beachten Sie, dass die in der Vergangenheit übliche Zeile implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version" nicht mehr erforderlich ist. Das Build-System kümmert sich selbst um den Import der Kotlin-Standardbibliothek in der richtigen Version.

Updates/Links

Download

Die aktualisierten und auf View-Bindung umgestellten Beispielprojekte der Kapitel 21 bis 25 können Sie hier herunterladen:

https://kofler.info/uploads/kotlin/kap21-25-mit-view-binding.zip

Ich habe auch den Evaluation-Client aus Kapitel 30 auf View-Binding umgestellt. Die Projekte finden Sie im ktor-Update-Artikel zum Download.

Die Kotlin-Updates-Serie

Weitere Kotlin-Update-Artikel finden Sie hier auf meiner Website:

https://kofler.info/tag/kotlin-updates

Docker-Container automatisch starten

15. Juli 2021 um 18:01

Beim Arbeiten mit Docker besteht oft der Wunsch, einen Container automatisch bei jedem Rechnerstart auszuführen, z.B. um einen Netzwerkdienst anzubieten. In diesem Text stelle ich Ihnen drei Wege vor, wie Sie diesen Wunsch realisieren können. Die ersten beiden Varianten setzen voraus, dass es einen systemweiten Docker-Dienst gibt (dockerd), dass Sie also mit einer »normalen« Docker-Installation arbeiten (nicht rootless oder mit mit Podman).

Variante 1: docker run --restart always

Wenn Sie beim Start eines Docker-Containers mit docker run die Option --restart always übergeben, dann wird dieser Container in Zukunft automatisch gestartet:

docker run -p 8080:80 --name apache  -v "${PWD}":/usr/local/apache2/htdocs -d --restart always httpd

Für die Option --restart gibt es vier mögliche Einstellungen:

  • --restart no gilt standardmäßig. Wenn der Container endet, egal aus welchem Grund, wird er nicht neu gestartet.
  • --restart always bewirkt, dass der Container automatisch neu gestartet wird, sobald er endet. Diese Neustartregel gilt auch für einen Reboot des Rechners! Dabei wird im Rahmen des Init-Prozesses der Docker-Dienst gestartet; dieser startet wiederum alle Container, die zuletzt mit der Option --restart always liefen. Aber Vorsicht: --restart always gilt auch für ein Programmende aufgrund eines Fehlers. Wenn der Container einen Fehler enthält, kann es passieren, dass der Container immer wieder gestartet wird. Die einzige Ausnahme ist ein manueller Stopp (docker stop): In diesem Fall wird der Container nicht unmittelbar neu gestartet. Wenn Sie aber Ihren Rechner herunterfahren, dann wird der Container beim nächsten Docker-Neustart wiederum gestartet.

  • --restart unless-stopped funktioniert ganz ähnlich wie --restart always. Der Unterschied besteht darin, dass ein mit docker stop beendeter Container beim nächsten Docker-Neustart nicht mehr automatisch gestartet wird.

  • --restart on-failure führt zu einem automatischen Neustart nach einem Fehler im Container, aber zu keinem Autostart bei einem Neustart des Docker-Systems.

Das für einen Container eingestellte Restart-Verhalten können Sie mit docker inspect ermitteln:

docker inspect <containername>
  ...
  "RestartPolicy": {
      "Name": "always",
      "MaximumRetryCount": 0
  },
  ...

Mit docker update können Sie das Update-Verhalten eines Containers verändern, während er läuft:

docker update --restart on-failure <containername>

Variante 2: docker-compose-Datei mit der restart-Option

Einen automatischen Neustart können Sie auch in der Datei docker-compose.yml festschreiben, und zwar mit dem Schlüsselwort restart:

services:
   db:
     image: mariadb:latest
     restart: always

Die vier zulässigen Einstellungen lauten no (gilt per Default), always, unless-stopped und on-failure. Die Bedeutung der Schlüsselwörter ist wie bei der vorhin erläuterten Option --restart von docker run. Die Einstellung
restart: always gilt solange, bis die durch die Datei docker-compose.yml beschriebenen Dienste explizit durch compose down wieder beendet und gelöscht werden.

Achten Sie darauf, dass Sie das Schlüsselwort restart in der richtigen Ebene angeben, also direkt bei den Einstellungen des jeweiligen Containers (im vorigen Beispiel db)! Zuletzt habe ich mich wochenlang gewundert, warum bei der folgenden Datei docker-compose.yml das Restart-Verhalten nicht funktionierte:

# Vorsicht, die restart-Einstellung ist hier fehlerhaft!
services:
   db:
     image: mariadb:latest
     volumes:
       - vol-db:/var/lib/mysql
     environment:
       MYSQL_USER: wpuser
       MYSQL_PASSWORD: geheim
       restart: always

Die Fehlerursache sollte klar sein: Die Option restart ist zu weit eingerückt und bezieht sich nicht auf den Container db, sondern auf dessen environment~~Einstellungen. Dort wird restart einfach ignoriert. Die falsch platzierte Option führt aber zu keiner Warnung oder gar Fehlermeldung.

Variante 3: systemd

Anstatt den Start von Containern durch Docker zu steuern, besteht auch die Möglichkeit, dies durch Funktionen des Betriebssystems zu erledigen. Diese Vorgehensweise ist relativ umständlich und wird in der Praxis daher nur recht selten gewählt. Sie hat aber den Vorteil, dass ein Container wie ein Dienst des Betriebssystem behandelt werden und mit den gleichen Kommandos gesteuert kann.

Ich gehe im Folgenden davon aus, dass Sie eine Linux-Distribution mit systemd verwenden. Sobald die Konfiguration einmal funktioniert, können auf den betreffende Container Kommandos wie systemctl restart oder systemctl enable --now angewendet werden.

Der Ausgangspunkt des folgenden Beispiels ist ein MySQL-Container, der zuerst testweise eingerichtet wurde und der nun dauerhaft gestartet werden soll. Das Volume mit den Datenbanken befindet sich in /home/kofler/docker-mysql-volume. Beim erstmaligen Einrichten des Containers wurde das MySQL-Root-Passwort festgelegt. Es ist in einer Datenbank im Volume-Verzeichnis gespeichert und muss deswegen nicht mehr mit -e MYSQL_ROOT_PASSWORD=... übergeben werden.

Eine eigene Servicedatei

Damit sich systemd selbstständig um den Start kümmert, muss im Verzeichnis /etc/systemd/system eine *.service-Datei eingerichtet werden. Für dieses Beispiel sieht die Datei so aus:

# Datei /etc/systemd/system/docker-mysql.service
[Unit]
Description=starts MySQL server as Docker container
After=docker.service
Requires=docker.service

[Service]
RemainAfterExit=true
ExecStartPre=-/usr/bin/docker stop mysql-db-buch
ExecStartPre=-/usr/bin/docker rm mysql-db-buch
ExecStartPre=-/usr/bin/docker pull mysql
ExecStart=/usr/bin/docker run -d --restart unless-stopped \
  -v /home/kofler/docker-mysql-volume:/var/lib/mysql \
  -p 13305:3306 --name mysql-db-buch mysql
ExecStop=/usr/bin/docker stop mysql-db-buch

[Install]
WantedBy=multi-user.target

Der Unit-Abschnitt beschreibt den Dienst. Die Schlüsselwörter Requires und After stellen sicher, dass der Dienst nicht vor Docker gestartet wird.

Der Service-Abschnitt legt fest, welche Aktionen zum Start bzw. zum Stopp des Diensts erforderlich sind. Die drei ExecStartPre-Anweisungen stellen sicher, dass der Container mit dem Namen mysql-db-buch gestoppt und gelöscht wird und dass die neueste Version des MySQL-Images heruntergeladen wird. Das Minuszeichen vor den stop– und rm-Kommandos bedeutet, dass ein Fehler beim Ausführen dieser Kommandos einfach ignoriert wird. (Wenn das Image bereits zuvor gestoppt und/oder gelöscht wurde, dann ist das kein Grund zur Sorge.)

Die über mehrere Zeilen verteilte ExecStart-Anweisung startet schließlich den Container, wobei das Volume-Verzeichnis /home/kofler/docker-mysql-volume und der Host-Port 13306 verwendet werden.

ExecStop gibt an, was zu tun ist, um den Dienst zu stoppen.

RemainAfterExit=true bedeutet, dass sich systemd den gerade aktivierten Status merkt. Ohne diese Option würde systemd nach der erfolgreichen Ausführung von docker run glauben, der Dienst sei bereits wieder beendet. Tatsächlich wird der Container aber im Hintergrund weiter ausgeführt.

WantedBy im Install-Abschnitt legt fest, dass der Dienst Teil des Multi-User-Targets sein soll. (Dieses Target beschreibt den normalen Betriebszustand eines Linux-Servers.)

Selbstverständlich können Sie in der Service-Datei anstelle von docker auch docker compose aufrufen. Das ist zweckmäßig, wenn der gewünschte Dienst nicht aus nur einem Container besteht, sondern aus einer ganze Gruppe von Containern. Denken Sie daran, dass Sie beim Aufruf von docker compose den vollständigen Ort der Datei docker-compose.yml mit der Option -f explizit angeben müssen.

Den Service starten und beenden

Damit systemd die neue Servicedatei berücksichtigt, müssen Sie einmalig das folgende Kommando ausführen:

systemctl daemon-reload

Sollten Sie in der Servicedatei einen Fehler eingebaut haben und ihn später korrigieren, vergessen Sie nicht, das obige Kommando neuerlich auszuführen!

Mit den folgenden Kommandos testen Sie, ob der manuelle Start und Stopp des Docker-Containers funktioniert:

systemctl start docker-mysql

systemctl status docker-mysql
  docker-mysql.service - starts MySQL server as Docker container
     Loaded: loaded (/etc/systemd/system/docker-mysql.service; 
                     disabled; vendor preset: enabled)
     Active: active (exited) since 19:28:20 CEST; 3min 41s ago
     ...

systemctl stop docker-mysql

Sofern alles klappt, müssen Sie den automatischen Start des Dienstes nun noch dauerhaft aktivieren:

systemctl enable --now docker-mysql

Sollten Sie den Dienst später nicht mehr brauchen, beenden Sie ihn wie folgt dauerhaft:

systemctl disable --now docker-mysql

Quellen/Links

Kotlin-Updates: ktor 1.5

10. Juli 2021 um 08:03

Teil 5 der Kotlin-Updates-Serie behandelt die Backend-Bibliothek ktor. Als ich mein Kotlin-Buch im Herbst 2020 fertiggestellt habe, war noch Version 1.3 aktuell, mittlerweile ist es 1.5.

ktor entwickelt sich rasch weiter, aber nicht alle Entwicklungen sind erfreulich: Ärgerlicherweise gilt das ktor-Plugin nun als obsolet. Zwar gibt es ein neues Plugin, dieses ist aber nur in IntelliJ Ultimate enthalten. So wird Jetbrains ktor nicht zum Erfolg verhelfen …

Davon abgesehen ist ktor zwar im Funktionsumfang gewachsen, aber — zumindest was die Beispielprogramme im Buch betrifft — nahezu kompatibel geblieben. (Die einzige Ausnahme betrifft den Umgang des Zeichens / am Ende von URLs, siehe unten.)

Dieser Text bezieht sich auf die folgenden Versionsnummern:

IntelliJ: 2021.1
Android Studio: 4.2
Kotlin: 1.5.20
ktor: 1.5.4
kotlinx.coroutines: 1.5
kotlinx-serialization: 1.2.1
Exposed: 0.32.1
Gradle: 6.7 und 6.8

Vom ktor-Plugin zum ktor-Projektgenerator

Jetbrains will das in der Vergangenheit sehr populäre ktor-Plugin nicht mehr weiterführen. Das Plugin steht zwar weiterhin zur Verfügung, wird aber keine ktor-Versionen > 1.5 unterstützen. Jetbrains empfiehlt ktor-Entwicklern den Wechsel auf die IntelliJ Ultimate-Edition, für die ein neues ktor-Plugin zur Verfügung steht. Persönlich halte ich das für eine sehr unglückliche Entwicklung: Ja, professionelle Entwickler haben ohnedies oft die Ultimate Edition. Aber das trifft sicherlich nicht für jeden zu, der ktor ausprobieren oder kennenlernen will. Wenn Jetbrains Interesse daran hat, dass sich ktor auf breiter Ebene etabliert, dann ist das eine ausnehmend stupide Entscheidung.

Auf das neue Plugin gehe ich hier gar nicht ein — sie finden hier eine Beschreibung. Stattdessen erkläre ich Ihnen hier, wie Sie neue Projekte mit dem Ktor Project Generator einrichten. Diese Website erzeugt ähnlich wie das bisherige Plugin ein Projekt, das Sie in der Folge als ZIP-Datei herunterladen, auf Ihrem Rechner auspacken und dann in IntelliJ öffnen können. Das ist nicht viel schwieriger als bisher, aber wesentlich umständlicher.

Im ktor-Generator wählen Sie die Features aus, die Sie anfänglich benötigen. Für erste Experimente reichen server-seitig die Module HTML-DSL, CSS-DSL und Routing aus. (Das spätere Hinzufügen von Features ist natürlich möglich. Allerdings müssen Sie dann build-gradle manuell erweitern, was natürlich immer ein wenig mühsam ist.)

Der ktor-Projektgenerator

Nachdem Sie die ZIP-Datei heruntergeladen und ausgepackt haben, öffnen Sie das fertige Projekt in IntelliJ. Sobald IntelliJ mit dem ersten Build-Prozess fertig ist und das neue Projekt läuft, können Sie den enthaltenen Testcode im Webbrowser unter der Adresse localhost:8080 ausprobieren.

Bei meinen Tests hat der Projektgenerator zwar eine aktuelle ktor-Version verwendet, aber eine alte Kotlin-Version. Abhilfe: Öffnen Sie gradle.properties und ändern Sie kotlin_version=1.4.32 zu kotlin_version=1.5.20.

Der Projektgenerator verwendet die Gradle-Version 6.6. Grundsätzlich gibt es keinen zwingenden Grund, eine neuere Version zu verwenden. Wenn Sie das doch möchten, öffnen Sie gradle/gradle-wrapper.properties und ersetzen dort gradle-6.6.1-all.zip durch gradle-6.8.zip. Danach schließen Sie das Projekt und öffnen es neuerlich.

Vorhandene Projekte aktualisieren (build.gradle)

Um vorhandene ktor-Projekte auf die aktuelle Version der Bibliothek zu aktualisieren, müssen Sie gradle.properties aktualisieren. Aktuell sollte die Datei so aussehen:

ktor_version=1.5.4
kotlin.code.style=official
kotlin_version=1.5.20
logback_version=1.2.1

Außerdem müssen Sie sicherstellen, dass in build.gradle die Repositories stimmen. Sie haben sich teilweise geändert. Das folgende Listing dient als Orientierungshilfe.

buildscript {
    repositories {
        mavenCentral()
    }

    dependencies {
        classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
    }
}

apply plugin: 'kotlin'
apply plugin: 'application'

group 'info.kofler'
version '0.0.1-SNAPSHOT'
mainClassName = "io.ktor.server.netty.EngineMain"

sourceSets {
    main.kotlin.srcDirs = main.java.srcDirs = ['src']
    test.kotlin.srcDirs = test.java.srcDirs = ['test']
    main.resources.srcDirs = ['resources']
    test.resources.srcDirs = ['testresources']
}

repositories {
    mavenLocal()
    mavenCentral()
    maven { url 'https://maven.pkg.jetbrains.space/kotlin/p/kotlin/kotlin-js-wrappers' }
}

dependencies {
    implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
    implementation "io.ktor:ktor-server-netty:$ktor_version"
    implementation "ch.qos.logback:logback-classic:$logback_version"
    implementation "io.ktor:ktor-server-core:$ktor_version"
    implementation "io.ktor:ktor-html-builder:$ktor_version"

    implementation "org.jetbrains:kotlin-css-jvm:1.0.0-pre.31-kotlin-1.2.41"
    // alternativ die aktuellste Version, wird aber nicht vom Generator verwendet:
    // implementation "org.jetbrains:kotlin-css-jvm:1.0.0-pre.148-kotlin-1.4.30"
    // siehe auch: https://mvnrepository.com/artifact/org.jetbrains/kotlin-css-jvm?repo=kotlin-js-wrappers

    // testImplementation "io.ktor:ktor-server-tests:$ktor_version"
}

Routing-Inkompatibilität: »hostname/path« versus »hostname/path/«

Mein größtes Problem nach dem Update auf ktor 1.5 bestand darin, dass das Routing anders verarbeitet wird. Während bisher ein GET-Request für hostname/todos funktionierte, ist neuerdings hostname/todos/ mit einem /-Zeichen am Ende erforderlich. Die Inkompatibilität hat mit der Behebung des folgenden Bugs zu tun:

https://github.com/ktorio/ktor/issues/1876

ktor kann jetzt zwischen URLs mit/ohne einem Slash am Ende differenzieren. An sich ist das schön, außer man weiß es nicht :-) Der folgende Code-Ausschnitt aus dem Projekt ktor-todo macht den Unterschied klar:

route("/todos") {
    // URL ohne / am Ende verarbeiten, also localhost:8080/todos
    get {
        println("get todos: $todos")
        call.respond(todos.sortedBy { it.priority })
    }

    // URL mit / am Ende verarbeiten, also localhost:8080/todos/
    get("/") {
        println("get todos/: $todos")
        call.respond(todos.sortedBy { it.priority })
    }  
    ...

Beim Test des Evaluation-Clients (Kapitel 30) habe ich daher relativ lange gebraucht, bis ich den Grund gefunden habe, warum der Verbindungsaufbau nicht klappte. Der Test

try {
   // 1: testet, ob Kontakt zum Server möglich ist
   val msg1 = httpClient.get<String>("$evalhost/api")
   ...

scheiterte immer wegen des fehlenden Slash. So funktioniert es (ohne Änderungen an eval-backend):

try {
   // 1: testet, ob Kontakt zum Server möglich ist
   val msg1 = httpClient.get<String>("$evalhost/api/")
   ...

Updates/Links

Plugin / Project Generator

Routing (Handling /)

Download

Die aktualisierten Beispielprojekte zu den Kapiteln 27 bis 30 können Sie hier herunterladen:

https://kofler.info/uploads/kotlin/kap27-30.zip

Beachten Sie, dass der Evaluaierungs-Client auf View Binding umgestellt ist. Was das bedeutet, erfahren Sie im Android-Studio-Update-Artikel.

Die Kotlin-Updates-Serie

Weitere Kotlin-Update-Artikel finden Sie hier auf meiner Website:

https://kofler.info/tag/kotlin-updates

Kotlin-Updates: kotlinx.coroutines 1.5

09. Juli 2021 um 09:42

Im vierten Teil der Kotlin-Updates-Serie geht es um die Bibliothek kotlinx.coroutines, die bei der Programmierung asynchronen Codes zum Einsatz kommt. In meinem Kotlin-Buch habe ich mich auf Version 1.3.9 bezogen, inzwischen gibt es Version 1.5. Soviel vorweg: Es gibt zwar viele Änderungen, diese betreffen die Einführungsbeispiele aus Kapitel 17 aber nicht und sind nur für Entwickler relevant, die fortgeschrittene Funktionen der Bibliothek nutzen.

Dieser Text bezieht sich auf die folgenden Versionsnummern:

IntelliJ: 2021.1
Kotlin: 1.5.20
kotlinx.coroutines: 1.5
Gradle: 6.8

Gradle

Eine minimale build-gradle-Datei können Sie nach dem folgenden Muster aufbauen:

plugins {
    id 'org.jetbrains.kotlin.jvm' version '1.5.20'
}
group 'org.example'
version '1.0-SNAPSHOT'
repositories {
    mavenCentral()
}
dependencies {
    implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8"
    implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.5.0'
}
compileKotlin {
    kotlinOptions.jvmTarget = "1.8"
}

GlobalScope-Warnung

Beim Beispielprojekt hello-coroutines zeigt IntelliJ bei der Verwendung von GlobalScope mehrfach die folgende Warnung an:

This is a delicate API and its use requires care. Make sure you fully read and understand documentation of the declaration that is marked as a delicate API.

Die Dokumentation zur Klasse warnt davor, dass derart gestartete asynchrone Methoden unlimitiert laufen. Daraus ergibt sich die Gefahr, dass das Programm unnötig Speicherplatz und die CPU blockiert (memory and resource leaks). Die Dokumentation empfiehlt, asynchrone Methoden mit suspend zu markieren und direkt bzw. im Kontext eines Objekts auszuführen.

All diese Warnungen treffen natürlich vollkommen zu. Im Beispielprojekt hello-coroutines geht es aber ausschließlich darum, diverse Programmiertechniken zu zeigen, ohne den Code durch eine echte Anwendung im Kontext eines Android-, Ktor-, Datenbank-Programms unnötig kompliziert zu machen. Daher können Sie die Warnungen in diesem Fall ignorieren.

Quellen/Links

Download

Die aktualisierten Beispielprojekte zu Kapitel 17 können Sie hier herunterladen:

https://kofler.info/uploads/kotlin/kap17.zip

Die Kotlin-Updates-Serie

Weitere Kotlin-Update-Artikel finden Sie hier auf meiner Website:

https://kofler.info/tag/kotlin-updates

Arch Linux und »archinstall«

07. Juli 2021 um 10:19

Vorige Woche habe ich die Installation von Arch Linux beschrieben. Ein Leser hat mich nun darauf hingewiesen, dass es zur rein manuellen Installation eine Alternative gibt: Das Script archinstall, das im Live-System von Arch Linux mittlerweile standardmäßig enthalten ist.

Beachten Sie, dass das ArchWiki archinstall als noch »experimentell« bezeichnet und darum bittet, bei Fehlerberichten auf die Verwendung von archinstall hinzuweisen.

Installation starten

Die Installation beginnt wie bei der manuellen Variante: Sie laden von https://archlinux.org/download das Live-Image herunter und booten damit Ihren Rechner bzw. Ihre virtuelle Maschine. Wenn Sie wollen, können Sie ein root-Passwort festlegen, sich dann per SSH im Live-System anmelden und die weitere Installation via SSH durchführen.

Danach gibt es zwei Varianten: Die eine besteht darin, archinstall sofort auszuführen. Das ist dann zweckmäßig, wenn Arch Linux den gesamten Datenträger nutzen darf, z.B. in virtuellen Maschinen. Bei der zweiten Variante kümmern Sie sich zuerst selbst um die Partitionierung, um das Einrichten des Dateisystems (optional samt LVM und RAID) und binden das Root-Dateisystem unter /mnt in den Verzeichnisbaum ein. Beim anschließenden Start von archinstall berücksichtigt das Script /mnt.

archinstall stellt während der Installation diverse Optionen zur Auswahl (Sprache, Tastatur, Desktop-System etc.), die Sie durch die Angabe des Namens oder der fortlaufenden Nummer auswählen. Die eigentliche Installation beginnt erst nach einer Anzeige aller Einstellungen und einer letzten Rückfrage.

Bootloader

Auf Rechnern mit EFI haben Sie beim Bootloader die Wahl zwischen GRUB und systemd-boot.

In virtuellen Maschinen mit BIOS installiert archinstall immer GRUB.

Automatische Partitionierung

Ich empfehle Ihnen, die Partitionierung nur dann archinstall zu überlassen, wenn das Script den gesamten Datenträger nutzen darf und Sie keine Rücksicht auf vorhandene Daten oder Betriebssysteme nehmen müssen. Ich habe keine klare Beschreibung gefunden, wie archinstall bei der Partitionierung vorgeht. Bei meinen Tests bin ich zu folgenden Ergebnissen gekommen:

  • BIOS: Eine Systempartition füllt den ganzen Datenträger mit Ausnahme des ersten MiB. GRUB wird in die ersten Sektoren installiert, es gibt keine eigene /boot-Partition.
  • EFI: Hier richtet das Script eine gemeinsame boot- und EFI-Partition ein (vfat-Dateisystem, 500 MB). Den Rest des Datenträgers füllt die Systempartition.

Beispiellauf

Das folgende Listing zeigt einen (etwas gekürzten) Ablauf des Frage- und Antwort-Spiels von archinstall.

archinstall

 ...
 4: de     
 ...
Select one of the above keyboard languages (by number or full name): 4

 ...
11: Germany
 ...
Select one of the above regions to download packages from (by number or full name): 11

0: /dev/loop0 (('638.7M', '/run/archiso/copytoram/airootfs.sfs', None))
1: /dev/sr0   (('774.3M', None, 'ARCH_202106'))
2: /dev/vda   (('25G', '/dev/vda', None))
Select one of the above disks (by name or number) or leave blank to use /mnt: 2

0: btrfs
1: ext4
2: xfs
3: f2fs
Select which filesystem your main partition should use (by number or name): 1

Enter disk encryption password (leave blank for no encryption):  <return>

Would you like to use GRUB as a bootloader instead of systemd-boot? [y/N] y

Desired hostname for the installation: archinstalltest

Enter root password (Recommendation: leave blank to leave root disabled):  <return>

Create a required super-user with sudo privileges: kofler
Password for user kofler: ********
And one more time for verification:  ******** 

Enter a username to create a additional user (leave blank to skip & continue): <return>

0: desktop
1: minimal
2: server
3: xorg
Enter a pre-programmed profile name if you want to install one: 0

0: awesome
1: budgie
2: cinnamon
3: deepin
4: enlightenment
5: gnome
6: i3
7: kde
8: lxqt
9: mate
10: sway
11: xfce4
Select your desired desktop environment: 5

0: AMD / ATI (open-source)
1: All open-source (default)
2: Intel (open-source)
3: Nvidia
4: VMware / VirtualBox (open-source)
Select your graphics card driver: 4

Would you like to install pipewire instead of pulseaudio as the default audio server? [Y/n] y

0: >> linux                                                                                                                                                              1: linux-hardened                                                                                                                             
2: linux-lts          
3: linux-zen          
Choose which kernels to use (leave blank for default: linux): <return>

If you desire a web browser, such as firefox or chromium, you may specify 
it in the following prompt. Write additional packages to install (space 
separated, leave blank to skip):  nano firefox 

0: Copy ISO network configuration to installation
1: Use NetworkManager to control and manage your internet connection
2: enp1s0
Select one network interface to configure (leave blank to skip): 1

Enter a valid timezone (examples: Europe/Stockholm, US/Eastern) 
or press enter to use UTC: Europe/Berlin

Would you like to use automatic time synchronization (NTP) with the 
default time servers? [Y/n]: y

This is your chosen configuration:

{
    "!root-password": "******",
    "audio": "pipewire",
    "bootloader": "grub-install",
    "filesystem": "ext4",
    "harddrive": {
        "model": null,
        "path": "/dev/vda",
        "size": "25G"
    },
    "hostname": "archinstalltest",
    "kernels": [
        "linux"
    ],
    "keyboard-language": "de",
    "mirror-region": {
        "Germany": {
            "https://appuals.com/archlinux/$repo/os/$arch": true,
            "https://arch.jensgutermuth.de/$repo/os/$arch": true,
            ...
            "https://pkg.fef.moe/archlinux/$repo/os/$arch": true
        }
    },
    "nic": {
        "NetworkManager": true,
        "nic": "Use NetworkManager to control and manage your internet connection"
    },
    "ntp": true,
    "packages": [
        "nano",
        "firefox"
    ],
    "profile": {
        "path": "/usr/lib/python3.9/site-packages/archinstall/profiles/desktop.py"
    },
    "script": "guided",
    "superusers": {
        "kofler": {
            "!password": "******"
        }
    },
    "sys-encoding": "utf-8",
    "sys-language": "en_US",
    "timezone": "Europe/Berlin",
    "users": {}
}

Praktische Erfahrungen

Bei meinen Tests, die ich allerdings ausschließlich in virtuellen Maschinen durchgeführt habe, hat archinstall durchwegs gut funktioniert. Ich habe mich allerdings nicht getraut, archinstall auf meine echten Rechner loszulassen, auf denen diverse andere Linux-Distributionen installiert sind.

Die Konfiguration des deutschen Textlayouts für die Konsole hat nur teilweise funktioniert. Die Eingabe deutscher Sonderzeichen war unmöglich. Abhilfe: Öffnen Sie im neu installierten System /etc/vconsole.conf mit einem Editor und ersetzen Sie KEYMAP=de durch KEYMAP=de-latin1.

Quellen/Links

AlmaLinux oder Oracle Linux oder Rocky Linux?

05. Juli 2021 um 19:01

Mein Linux-Freund Niki Kovacs hat mich heute kontaktiert: Soll man als CentOS-Ersatz Rocky Linux oder doch eher Oracle Linux empfehlen? Ich habe per Mail schon geantwortet, aber ich will die Frage hier ein wenig ausführlicher diskutieren und lade Sie ein, in den Kommentaren Ihre eigene Sichtweise zu präsentieren.

Prinzipiell geht es um die Frage: Welcher RHEL-Klon ist der beste, wenn kein Geld für eine RHEL-Lizenz da ist? CentOS 8 scheidet aus, der RHEL-Klon schlechthin hat ja für Ende 2021 die Einstellung von Updates angekündigt (siehe auch meinen Nachruf auf CentOS). Es gibt aber mittlerweile eine Menge Alternativen (hier in alphabetischer Reihenfolge):

  • AlmaLinux
  • CentOS Stream
  • Oracle Linux
  • Red Hat Enterprise Linux (RHEL) mit einem Developer Account kostenlos nutzen
  • Rocky Linux

Update 7.7.2021: Gerade habe ich noch einen RHEL-Klon entdeckt: Virtuozzo VzLinux

13.8.2021: Anscheinend noch ein Klon, Euro Linux.

RHEL kostenlos nutzen

Beginnen wir mit dem Original. Natürlich ist es schön, dass Open-Source-Lizenzen das Angebot von RHEL-Klonen überhaupt möglich machen; aber wozu einen Klon verwenden, wenn Red Hat jetzt 16 kostenlose RHEL-Installationen im Produktiveinsatz erlaubt (FAQs for no-cost Red Hat Enterprise Linux)?

Ich habe es ausprobiert. Ein paar Mausklicks reichen aus, um einen Red Hat Developer Account einzurichten — aber in der Folge ist von der kostenlosen Red Hat Developer Subscription for Individuals in der Red-Hat-Weboberfläche nichts zu sehen. Auch die Registrierung einer neuen RHEL-Installation scheitert mit unklaren Fehlermeldungen.

Schließlich habe ich den Red-Hat-Support per Mail kontaktiert. Sehr freundlich hat man mir dann einen halben Tag später die RHEL-Subscription freigeschaltet. Anscheinend gibt es da keinen Automatismus. Das kostenlose Angebot ist allerdings auf ein Jahr limitiert und muss dann verlängert werden. (Mangels eigener Erfahrung kann ich nicht sagen, ob dazu wieder ein E-Mail-Kontakt erforderlich ist.)

Die Weboberfläche zur Verwaltung von RHEL-Subskriptionen

Nach der manuellen Freischaltung ist die Aktivierung von RHEL-Systemen dann möglich. Die Verwaltung der Subskriptionen bleibt allerdings unübersichtlich. Die dafür vorgesehene GUI innerhalb von Red Hat habe ich schon in der Vergangenheit als recht fehleranfällig erlebt.

GUI zur Subskriptionsverwaltung innerhalb von RHEL

Immerhin lässt sich die Verwaltung auch per Kommando erledigen:

subscription-manager register --username <yourname> --password <pw>

  Das System wurde mit ID registriert: 764c...
  Der registrierte Systemname lautet: <hostname>

subscription-manager role --set="Red Hat Enterprise Linux Server"

  role set to "Red Hat Enterprise Linux Server".

subscription-manager service-level --set="Self-Support"

  service_level_agreement set to "Self-Support".

subscription-manager usage --set="Development/Test"

  usage set to "Development/Test".

subscription-manager attach

  Aktueller Status der installierten Produkte:
  Produktname: Red Hat Enterprise Linux for x86_64
  Status: Subskribiert

Fazit: Red Hat ist mittlerweile durchaus großzügig und erlaubt die kostenlose Nutzung von bis zu 16 RHEL-Installationen im Produktivbetrieb. Allerdings ist das Handling der Lizenzen mühsam und für Open-Source-verliebte Linux-Anwender ungewohnt. Ein wenig stellt sich die Frage nach dem Nutzen. Letzlich bleibt nur ein Argument: Paket- und Versions-Updates erfolgen beim Original in der Regel ein paar Tage schneller als bei den Klonen.

CentOS Stream

Red Hat bewirbt CentOS Stream als Nachfolger von CentOS. CentOS Stream ist wie CentOS kostenlos verfügbar. Ansonsten gibt es aber zwei gewichtige Unterschiede:

  • Zum einen ist CentOS Stream nicht mehr vollständig RHEL-kompatibel. Vielmehr werden für RHEL geplante Updates zuerst für CentOS Stream freigegeben. Das kann man durchaus als Vorteil sehen — vor allem, weil die lahme Update-Versorgung in der Vergangenheit ein großes CentOS-Problem war. Andererseits ist klar, dass CentOS Stream für Red Hat damit zur Testumgebung wird: Sollte bei einem Update doch ein Problem auftreten, werden die CentOS-Stream-Anwender dieses feststellen, bevor die zahlenden RHEL-Kunden damit beglückt werden.
  • Zum anderen hat CentOS Stream eine viel kürze Lebenszeit als RHEL. Während für CentOS in der Vergangenheit wie RHEL 10 Jahre Updates versprach, wird es diese für CentOS Stream nur ca. vier bis fünf Jahr geben. (Dieser Zeitraum ist nicht in Stein gemeißelt. Er hängt davon ab, wir rasch Red Hat die jeweils nächste RHEL-Version fertig stellt.)

Persönlich könnte ich mit Punkt 1 leben. In aller Regel sind die Paket-Updates absolut ausgereift (siehe auch diesen Artikel in der CentOS-Mailing-Liste), wenn sie für CentOS Stream freigegeben werden. CentOS Stream ist nicht Fedora und schon gar nicht Debian unstable. Aber aufgrund von Punkt 2 ist CentOS Stream aus meiner Sicht weitgehend uninteressant für den produktiven Server-Einsatz.

Oracle Linux

Damit kommen wir zu den Klonen. Seit über 15 Jahren am Markt etabliert ist Oracle Linux. Oracle vermarktet sein Linux primär als kommerzielles Angebot und versucht, sich mit Zusatzfunktionen von RHEL abzuheben: Dazu zählen ein neuerer Kernel (im Marketing-Jargon: »Unbreakable Enterprise Kernel«), Ksplice-Kernel-Updates im laufenden Betrieb sowie eine bessere Unterstützung des von Oracle mitentwickelten Dateisystems btrfs.

Anfänglich war Oracle Linux wie RHEL ausschließlich für zahlende Kunden zugänglich, wenn auch zu günstigeren Preisen als bei RHEL. Seit 2012 kann Oracle Linux inklusive aller Updates kostenlos bezogen werden und steht damit auf einer Ebene mit AlmaLinux und Rocky Linux. Oracle verspricht, dass Linux auch weiterhin frei zugänglich bleiben wird.

Für Oracle spricht, dass die Update-Versorgung in den letzten 15 Jahren ausgezeichnet (viel besser als bei CentOS) funktioniert hat. Dagegen spricht, dass Oracle in der Vergangenheit keine glückliche Hand mit Open-Source-Projekten hatte. Java, MySQL, OpenOffice führen die Liste der von Oracle übernommenen Projekte an, die prompt Konflikte mit der Community und Forks verursacht haben.

Eine Oracle-Linux-Installation in einer virtuellen Maschine

AlmaLinux und Rocky Linux

Wenige Tage nach der Ankündigung vom Ende von CentOS haben der ehemalige CentOS-Gründen Greg Kurtzer und die Firma CloudLinux jeweils einen eigenen RHEL-Klon angekündigt. Mittlerweile sind beide Produkte unter den Namen Rocky Linux (in Gedenken an ein weiteres CentOS-Gründermitglied) und AlmaLinux fertig und stehen jeweils für zwei Architekturen (x86 und ARM) zur Auswahl. Ich habe beide Klone kurz angetestet und keinerlei Probleme festgestellt.

Cloud Linux hat die weitere Verantwortung für AlmaLinux in die Hände der neu geschaffenen AlmaLinux OS Foundation gelegt, und verspricht außerdem, pro Jahr eine Million Dollar für das Projekt zur Verfügung zu stellen. Zumindest im ersten halben Jahr hinterließ AlmaLinux als Distribution wie als Organisation einen äußerst professionellen Eindruck.

Rocky Linux war mit der Fertigstellung etwas langsamer. Das Ende Juni 2021 vorgestellte Release unterstützt zudem noch kein UEFI Secure Boot. Dieses Feature soll aber in naher Zukunft nachgereicht werden. Rocky Linux genießt immerhin bereits die Unterstützung durch Amazon, Google und Microsoft. (Lesen Sie auch dieses Interview mit dem Greg Kurtzer.)

Machen Sie sich keine Illusionen: Eine echte Beurteilung von AlmaLinux bzw. von Rocky Linux ist aktuell schlicht unmöglich. Die Wartung einer Enterprise-Distribution verlangt einen langen Atem. Die regelmäßige Update-Versorgung über einen Zeitraum von 10 Jahren kostet richtig viel Arbeit (= Geld)! Das gilt umso mehr, wenn demnächst zwei und in ferner Zukunft womöglich drei Major-Versionen (RHEL 8 + 9 + 10) parallel laufen.

Das Überleben von Rocky Linux und AlmaLinux wird also letztlich von der langfristigen Unterstützung durch Sponsoren abhängen. Dementsprechend zeigen beide Projekte auf ihrer Website eine lange Liste von Sponsoren-Logos an :-) Aber ob bzw. welche der beiden Projekte in 10 Jahren noch aktiv sind, kann heute niemand seriös vorhersehen. Den großartigen Ruf, den das CentOS-Projekt hatte und den Red Hat vorigen Dezember quasi über Nacht verspielte, müssen sich AlmaLinux und Rocky Linux erst mühsam erarbeiten.

Das Installations-Programm von AlmaLinux ist nur an der optischen Gestaltung der Seitenleiste vom Original bzw. von den anderen RHEL-Klonen unterscheidbar.

Die Qual der Wahl

Damit sind wir bei der eingangs gestellten Frage: Welcher Klon soll es nun sein?

Selbst habe ich aktuell kein Projekt, wo ich einen RHEL-Klon produktiv einsetzen will. Dafür habe ich im Linux-Unterricht auf der FH Kapfenberg dieses Sommersemester mit Oracle Linux 8 gearbeitet. Ich habe dutzendweise virtuelle Maschinen mit Oracle Linux erzeugt und wieder gelöscht. Das hat vollkommen klaglos funktioniert.

Vielleicht liegt es daran, dass meine Empfehlung am ehesten in diese Richtung geht: Oracle hat schon bewiesen, dass es langfristig einen RHEL-Klon warten kann. Oracle hat die Ressourcen und die Erfahrung. Natürlich könnte Oracle den kostenlosen Zugang zu Oracle Linux jederzeit einstellen — aber persönlich erwarte ich das nicht. Dagegen spricht schon der Umstand, dass Oracle mit dem kostenlosen Angebot den großen Linux-Mitbewerber Red Hat (letztlich also IBM) ein wenig ärgern kann.

AlmaLinux und Rocky Linux machen aus meiner Sicht ebenfalls einen sehr vielversprechenden Eindruck. Ich denke eigentlich nicht, dass Sie mit der Wahl für eine der beiden Distributionen viel falsch machen können. Das liegt auch daran, dass es Migrations-Scripts gibt, die einen Umbau von einen RHEL-Klon in einen anderen bewerkstelligen. Diese Scripts sind vielleicht nicht frei von Problemen, aber sie sollten doch garantieren, dass Sie im Fall der Fälle nicht komplett in der Sackgasse landen.

Vielleicht haben Sie schon eigene Erfahrungen gemacht. Ich freue mich über Ihre Meinung in den Kommentaren!

Quellen/Links

Migrations-Scripts:

Kotlin-Updates: kotlinx.serialization 1.2

03. Juli 2021 um 12:58

In Teil 3 meiner Kotlin-Updates-Serie fasse ich ganz kurz die Neuerungen in der Bibliothek kotlinx.serialization zusammen, die seit dem Erscheinen meines Kotlin-Buchs erfolgt sind.

Dieser Text bezieht sich auf die folgenden Versionsnummern:

IntelliJ: 2021.1
Kotlin: 1.5.20
kotlinx.serialization: 1.2.1
Gradle: 6.8

Gradle

Der Umstieg von kotlinx.serialization 1.0 auf 1.2 gelingt weitgehend problemlos, sobald einmal build.gradle passt. Zuerst sollten Sie Gradle selbst auf eine aktuelle Version bringen (Datei graddle/wrapper/gradle-wrapper.properties). Ich habe das Testprojekt aus dem Buch (Download-Link am Ende des Artikels) auf Version 6.8 aktualisiert. IntelliJ verwendet nach der Änderung in gradle-wrapper.properties mitunter weiter die alte Gradle-Version und zeigt merkwürdige Fehler an. Abhilfe: Projekt schließen und neu laden.

In build.gradle müssen Sie nicht nur auf die richtigen Versionen achten, sondern auch, dass es nun getrennte Bibliotheken für unterschiedliche Serialisierungsformate gibt, die extra aufgezählt werden müssen. Wo bisher implementation "...-core:n.n" reichte, ist jetzt zumindest eine weitere Zeile mit implementation "...-json:n.n" erforderlich. Das jcenter-Repository ist zu entfernen, Gradle findet alle erforderlichen Bibliotheken in mavenCentral.

Ein minimales Muster mit aktuellen Versionsnummern sieht so aus:

// Datei build.gradle
plugins {
    id 'java'
    id 'org.jetbrains.kotlin.jvm' version '1.5.20'
    id 'org.jetbrains.kotlin.plugin.serialization' version "1.5.20"
}
repositories {
    mavenCentral()
}
dependencies {
    implementation "org.jetbrains.kotlinx:kotlinx-serialization-core:1.2.1"
    // die folgende Zeile muss bei vorhandenen Projekten hinzugefügt werden!
    implementation "org.jetbrains.kotlinx:kotlinx-serialization-json:1.2.1"
}
compileKotlin {
    kotlinOptions.jvmTarget = "11"
}

Neu in Version 1.2

kotlinx.serialization ist laut JetBrains bis zu doppelt so schnell als die bisherige Implementierung. Außerdem werden diverse neue Kotlin-Dateitypen, u.a. UInt oder UByte, jetzt nativ unterstützt. Bei meinen Tests habe ich keine Inkompatibilitäten zwischen Version 1.0 und 1.2 festgestellt.

Zur schon bekannten Annotation @SerialName, mit der Sie den Zusammenhang zwischen der Variable/Eigenschaft x in Ihrem Code und dem Serialisierungsfeld y herstellen konnte, gibt es nun neu auch die Annotation @JsonNames, um mehrere alternative Namen in der JSON-Datei einer Variablen/Eigenschaft in Ihrem Code zuzuordnen:

// schon bisher möglich
@Serializable
data class Class1(@JsonNames("title") val name: String)
// der JSON-Namen title ist der Eigenschaft name 
// der Klasse Class1 zugeordnet


// neu
@Serializable
data class Class2(@JsonNames("title") val name: String)
// die JSON-Namen title UND name sind gleichwertig und werden 
// beide der Eigenschaft name der Klasse Class2 zugeordnet

Schließlich wurde die Dokumentation komplett überarbeitet (siehe die Links am Ende des Artikels).

Serialisierung von LocalDateTime

Die Serialisierung von Java-Datentypen LocalDateTime bzw. von eigenen Datentypen funktioniert nur mit Ihrer Mithilfe. Die Vorgehensweise ist weiterhin experimentell. Sie hat sich in winzigen Details geändert. Zuerst müssen Sie eine Klasse mit den (De)serialisierungs-Funktionen programmieren, die die Schnittstelle KSerialize<typ> implementiert. Der Datentyp von deserialize muss jetzt explizit angegeben werden, damit für Kotlin klar ist, ob das Ergebnis auch null sein kann.

@ExperimentalSerializationApi
@Serializer(LocalDateTime::class)
object LocalDateTimeSerializer : KSerializer<LocalDateTime> {

    override fun serialize(encoder: Encoder, value: LocalDateTime) =
        encoder.encodeString(value.format(DateTimeFormatter.ISO_DATE_TIME))

    // neu: Ergebnistyp muss explizit angegeben werden
    override fun deserialize(decoder: Decoder) : LocalDateTime =
        LocalDateTime.parse(decoder.decodeString(),
            DateTimeFormatter.ISO_DATE_TIME)
}

Damit ist es aber noch nicht getan: Sie müssen Kotlin explizit darauf hinweisen, dass Ihr Serialisierer für einen bestimmten Datentyp verwendet werden soll. Dazu bestehen verschiedene Möglichkeiten. Die in den meisten Fällen einfachste Variante besteht darin, dass Sie am Beginn der Kotlin-Datei mit der Annotation @file:UseSerializers die gewünschte (De-)Serialisierungsklasse spezifizieren. Diese Einstellung gilt für die gesamte Datei und ist insbesondere dann zweckmäßig, wenn der betreffende Datentyp in vielen Klassen vorkommt.

// am Beginn der Code-Datei angeben
@file:UseSerializers(LocalDateTimeSerializer::class)

IntelliJ zeigt nun eine Warnung an, dass UseSerializers experimentell ist und mit der Annotation @ExperimentalSerializationApi gekennzeichnet werden sollte. Dazu habe ich aber keinen Weg gefunden. Sobald ich die Annotation vorangestellt habe, betrachtet der Compiler die gesamte Anweisung als Syntaxfehler.

Quellen/Links

Download

Das aktualisierte Beispielprojekt zu Abschnitt 18.4 können Sie hier herunterladen:

https://kofler.info/uploads/kotlin/kap18-json.zip

Die Kotlin-Updates-Serie

Weitere Kotlin-Update-Artikel finden Sie hier auf meiner Website:

https://kofler.info/tag/kotlin-updates

Kotlin-Updates: Exposed 0.32

03. Juli 2021 um 07:55

Sie lesen hier Teil 2 meiner Kotlin-Updates-Serie, in der ich Neuerungen und Änderungen rund um Kotlin seit dem Erscheinen meines Kotlin-Buchs zusammenfasse. Die Datenbank-Bibliothek Exposed (siehe Kapitel 19) hat sich mittlerweile von Version 0.27 zu Version 0.32 weiterentwickelt.

Dieser Text bezieht sich auf die folgenden Versionsnummern:

IntelliJ: 2021.1
Kotlin: 1.5.20
Exposed: 0.32.1
Gradle: 6.8

Wie Sie im Changelog nachlesen können, bietet Exposed zwar diverse kleine neue Features, zeichnet sich aber nicht durch grundlegenden Neuerungen aus. Die Beispiele aus dem Buch (siehe Kapitel 19) funktionieren nach einem Update von gradle-wrapper.properties und build.gradle unverändert und — soweit ich feststellen konnte — fehlerfrei.

Gradle-Updates

In build.gradle muss compile durch implementation ersetzt werden. Außerdem haben sich natürlich unzählige Versionsnummern geändert. jcenter() sollte aus der Liste der Repositories entfernt werden, weil das Repository ab 2022 nicht mehr unterstützt wird. Aktuelle Exposed-Versionen befinden sich in mavenCentral. build.gradle für ein Projekt mit der Exposed-Projekt sieht dann in etwa wie das folgende Muster aus:

plugins {
    id 'org.jetbrains.kotlin.jvm' version '1.5.20'
}
group 'org.example'
version '1.0-SNAPSHOT'
repositories {
    mavenCentral()
}
dependencies {
    implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8"
    implementation "org.jetbrains.exposed:exposed-core:0.32.1"
    implementation "org.jetbrains.exposed:exposed-dao:0.32.1"
    implementation "org.jetbrains.exposed:exposed-jdbc:0.32.1"
    implementation "org.xerial:sqlite-jdbc:3.36.0.1"
    ...
    // SQLite, H2, MySQL
    implementation "org.xerial:sqlite-jdbc:3.36.0.1"
    implementation "com.h2database:h2:1.4.200"
    implementation "mysql:mysql-connector-java:8.0.25"
    implementation "com.zaxxer:HikariCP:4.0.3"

    // Logger
    implementation 'org.slf4j:slf4j-nop:1.7.31'
}
compileKotlin {
    kotlinOptions.jvmTarget = "11"
}
compileTestKotlin {
    kotlinOptions.jvmTarget = "11"
}

Download

Aktualisierte Beispieldateien zu Kapitel 19 können Sie hier herunterladen:

https://kofler.info/uploads/kotlin/kap19.zip

Die Kotlin-Updates-Serie

Weitere Kotlin-Update-Artikel finden Sie hier auf meiner Website:

https://kofler.info/tag/kotlin-updates

Kotlin-Updates: Kotlin 1.5.20

03. Juli 2021 um 07:55

Mit diesem Artikel starte ich eine Serie von Update-Artikel zu meinem Kotlin-Buch. In diesem ersten Beitrag geht es um die Neuerungen in Kotlin von Version 1.4 bis zur aktuellen Version 1.5.20.

Dieser Text bezieht sich auf die folgenden Versionsnummern:

IntelliJ: 2021.1
Kotlin: 1.5.20
JDK: 16

Weitere Kotlin-Update-Artikel auf meiner Website finden Sie hier:

https://kofler.info/tag/kotlin-updates

Neues Release-Modell

Im Oktober 2020 hat Jetbrains ein neues Release-Modell angekündigt, demzufolge in Zukunft halbjährlich neue Versionen erscheinen sollen. Version 1.5 hat im Mai 2021 den Anfang gemacht, Ende 2021 soll 1.6 folgen, im Frühjahr 2022 Version 1.7 usw. Dazwischen gibt es kleiner Releases, 1.5.10, 1.5.20 usw., außerdem bei Bedarf Bugfix-Releases (1.5.21 etc.)

Der rasche Release-Zyklus hat zur Folge, dass es nicht bei jedem Release revolutionäre Neuerungen gibt. Tatsächlich sind die praktischen Auswirkungen der Neuerungen zwischen Version 1.4 und der aktuellen Version 1.5.20 gering. Die interessantesten Neuerungen gibt es aus meiner Sicht in der Standardbibliothek.

Standardbibliothek

Unsigned int types, also UByte, UShort, UInt und ULong gelten jetzt als stabil.

Die Methoden upper- und lowercase ersetzen toUpperCase und toLowerCase:

println("abCD".uppercase())   // ABCD

toChar, toInt: Aus nicht ganz nachvollziehbaren Gründen erscheinen den Kotlin-Entwicklern die Methoden toChar, toInt etc. zur Umwandlung von Zeichen in den zuhörigen ASCII/UTF-8-Code bzw. die Rückumwandlung aus einer Zahl in das entsprechende Zeichen verwirrend. Stattdessen erzeugen Sie neue Char-Elemente nun mit Char(n) und erhalten den Code eines Zeichens mit c.code. Also:

// bisher
val n = 65
val c = n.toChar()        // c = 'A'
val n2 = c.toByte()       // n2 = 65

// neu
val n = 65
val c = Char(n)           // c = 'A'
val n2 = c.code.toByte()  // n2 = 65

c.toInt() und n.toChar() wurden im letzten Moment doch nicht als deprecated erklärt, wohl aber c.toByte() etc.

Ganzzahlige Division und Rest: Ergänzend zu den Operatoren / und % und der entsprechenden Funktionen rem gibt es zwei neue Funktionen floordiv und mod. Bei positiven Zahlen führen beide Wege zum gleichen Ergebnis:

val n1 = 17
val n2 = 4
println(n1 / n2)           // 4
println(n1 % n2)           // 1
println(n1.floorDiv(n2))   // 4
println(n1.mod(n2))        // 1

Aber bei negativen Zahlen sucht / das Ergebnis, das näher bei 0 liegt, während floorDiv immer das nächst kleiner Resultat liefert, also gewissermaßen nach unten rundet. Das hat dementsprechende Auswirkungen auf den verbleibenden Rest.

val n1 = -17
val n2 = 4
println(n1 / n2)           // -4
println(n1 % n2)           // -1
println(n1.floorDiv(n2))   // -5
println(n1.mod(n2))        //  3

Collections: Es gibt einige Collections-Methoden, die zum einen neu sind oder die ich zum anderen ganz einfach übersehen habe, als ich Kapitel 11 »Lambda-Ausdrücke und funktionale Programmierung« verfasst habe:

  • lst.distinct() eliminiert alle Doppelgänger in einer Liste.
  • lst.distinctBy { lambda } eliminiert ebenfalls Doppelgänger, aber verwendet den Lambda-Ausdruck, um die »Gleichheit« zu testen.

  • coll.firstNotNullOf { lambda } liefert das erste Elemente, das nach Anwendung des Lambda-Ausdruck nicht null ist. Der Lambda-Ausdruck kann im einfachsten Fall einfach it sein. Die Methode löst einen Fehler aus, wenn es kein passendes Element findet.

  • coll.firstNotNullOfOrNull { lambda } funktioniert wie oben, gibt aber null zurück, wenn kein passendes Element gefunden hat. (Dementsprechend ist der Ergebnisdatentyp optional, also z.B. Int?.)

Path-Ausdrücke können nun besonders elegant mit dem neuen /-Operator zusammengesetzt werden (der aber vorher importiert werden muss):

import java.nio.file.Paths
import kotlin.io.path.div
...
val home = Paths.get(System.getProperty("user.home"))
val downloads = home / "Downloads"
println(downloads)  // "/home/kofler/Downloads"

Um ein Verzeichnis nach Dateien eines Typs zu durchsuchen, können Sie die ausgesprochen praktische Methode listDirectoryEntries verwenden:

// pdf hat den Typ Path
for (pdf in downloads.listDirectoryEntries("*.pdf"))
    println(pdf)

listDirectoryEntries arbeitet nicht rekursiv, die Groß- und Kleinschreibung muss exakt stimmen. Mehrere Suchmuster können Sie so kombinieren: listDirectoryEntries("*.{jpg,jpeg,JPG,JPEG}")

Duration-API: Die »Duration und Time Measurement API« (kotlin.time, siehe Abschnitt 7.3 im Buch) gilt weiterhin als experimentell. Allerdings haben sich sowohl die interne Darstellung der Daten (jetzt Long, bisher Double) als auch diverse Eigenschaften/Methoden geändert. Beispielsweise wird aus dem wunderbar lesbaren 100.milliseconds jetzt das viel umständlichere Duration.milliseconds(100). Schade!

// Rechnen mit Zeitspannen
val myDur = Duration.minutes(2) + Duration.seconds(3) - Duration.milliseconds(10) * 0.66
println(myDur)                       // 123s (gerundet)
println(myDur.inWholeMilliseconds)   // 122970 (exakt)

Java-Kompatibilität: Records

Nicht nur Kotlin entwickelt sich weiter, sondern auch Java. Damit Kotlin kompatibel zu Java bleibt und es weiterhin möglich ist, Kotlin- und Java-Code in einem Projekt zu kombinieren, muss Kotlin die jeweils neuesten Java-Features unterstützen. Dementsprechend ist Kotlin ab Version 1.5 mit Java-Records (das sind unveränderliche, also immutable Klassen) sowie mit geschlossenen Klassen und Schnittstellen (sealed classes and interfaces) kompatibel.

Java-Records haben eine Ähnlichkeiten mit den in Kotlin schon viel früher eingeführten Datenklassen. Um eine eigene data class kompatibel zu Java-Records zu machen, markieren Sie diese mit @JvmRecord.

@JvmRecord
data class Person(val name: String, val age: Int)

Die Annotation ist nur erlaubt, wenn sämtliche Eigenschaften unveränderlich sind (Deklaration mit val, nicht mit var). Diese Annotation setzt außerdem voraus, dass Ihr Kotlin-Projekt zumindest JDK 15 als Fundament verwendet. Bei meinen Tests mit JDK 16 gelang die Einstellung eines JVM-16-Targets nur in einem Gradle-Projekt, nicht aber in einem simplen IntelliJ-Projekt. (Dort ist die höchste erlaubte Ziel-JVM merkwürdigerweise 13. Vielleicht liegt hier noch ein IntelliJ-Problem vor?)

# in build.gradle
compileKotlin {
    kotlinOptions.jvmTarget = '16'
}

Java-Kompatibilität: Sealed Classes

Versiegelte Klassen/Schnittstellen verhindern, dass diese außerhalb des Projekts erweitert werden können. Auf den ersten Blick erscheint das im Vergleich zu den schon lange verfügbaren finale Klassen nichts Neues zu sein. Das stimmt aber nicht ganz: Finale Klassen können nie erweitert werden, versiegelte Klassen hingegen schon — allerdings nur im aktuellen Projekt (genaugenommen sogar nur im selben Paket, sofern es davon mehrere gibt), nicht außerhalb.

Als Entwickler gibt Ihnen eine versiegelte Klasse die Möglichkeit, jede Erweiterung/Vererbung außerhalb zu unterbinden, diese Grundfunktion objektorientierter Programmierung innerhalb Ihres Projekts aber sehr wohl zu nutzen.

// die Bicycle-Klasse kann nur innerhalb des Projekts erweitert werden,
// nicht außerhalb
sealed class Bicycle(val gears: Int, val weight: Double)

class MountainBike(gears: Int,
                   weight: Double,
                   val suspension: Double) 
         : Bicycle(gears, weight) 

Eine versiegelte Schnittstelle (also ein sealed interface) funktioniert analog wie eine versiegelte Klasse. Versiegelte Klassen/Schnittstellen von Kotlin 1.5 sind zum entsprechenden Feature von JDK 15 kompatibel.

Inline-Klassen

Inline-Klassen sind ganz »primitive« Klassen, die lediglich einen Datentyp unter einem anderen Namen zugänglich machen (also »Wrapper-Klassen«). Aktuelle JVMs können Daten derartigen Klassen besonders effizient verarbeiten — und Kotlin kann dies, dann der Annotation @JvmInline jetzt auch. Der durch die Wrapper-Klasse eingeführte Overhead wird also beim Kompilieren eliminiert.

@JvmInline
value class Password(private val s: String)

Im Prinzip ist Password bei dieser Deklaration einfach eine andere Bezeichnung für eine Zeichenkette. Im Unterschied zu einem typealias besteht darin, dass ein typealias einen zusätzlichen Namen für einen vorhandenen Datentyp definiert. Egal, ob der originale oder der Alias-Name verwendet wird — die Daten sind austauschbar. Eine Inline-Klasse ist hingegen ein eigener Datentyp.

val mypassword = Password("geheim")
val s1: String = mypassword  // Fehler, type mismatch

typealias PW = String
val mypw: PW = "secret"
val s2: String = mypw        // OK

Quellen/Links

Details

Download

Das aktualisierte Beispielprojekt zu Abschnitt 7.3 »Duration and Time Measurement API« können Sie hier herunterladen:

https://kofler.info/uploads/kotlin/kap07-kotlin-time.zip

Die Kotlin-Updates-Serie

Weitere Kotlin-Update-Artikel finden Sie hier auf meiner Website:

https://kofler.info/tag/kotlin-updates

Arch Linux installieren

02. Juli 2021 um 09:45

Arch Linux ist eine unter Linux-Profis sehr beliebte Rolling-Release-Distribution. Arch Linux zählt zu den wenigen Distributionen, die auf eine eigene Paketverwaltung außerhalb der Debian- und RPM-Welt setzen. Arch Linux betreibt schließlich ein großartiges Wiki, wo Sie technische Informationen zu kniffeligen Setup-Fragen finden (ganz egal, unter welchem Linux Sie arbeiten). Beim Recherchieren für mein Linux-Buch lande ich unweigerlich immer wieder im Arch Linux Wiki. Oft hört die Suche dort auf.

Arch Linux spricht explizit technisch versierte Linux-Anwender an. Wenn Sie nicht wissen, wie Sie Festplatten/SSDs manuell partitionieren, Dateisysteme selbst einrichten, mit chroot andere Verzeichnisse zum Root-Verzeichnis machen, ist Arch Linux nicht die richtige Wahl. Wenn Sie aber schon ein, zwei Jahre Linux-Erfahrung haben, ist Arch Linux für Desktop-Installationen definitiv einen Versuch wert. Einmal installiert erhalten Sie eine solide, schnelle und sehr moderne Linux-Distribution.

Arch Linux lohnt sich auch aus didaktischer Sicht: Da Sie sowohl bei der Installation als auch später im Betrieb viel mehr selbst machen müssen, lernen Sie Linux besser verstehen.

Dieser Blog-Beitrag fasst die wichtigsten Schritte zur Installation in einer virtuellen Maschine sowie auf einem echten Rechner zusammen. Natürlich gibt es im Internet noch mehr Anleitungen (siehe die Liste der Quellen am Ende dieses Artikels), aber nachdem ich selbst ein, zweimal über Details gestolpert bin, habe ich mir gedacht: Noch eine Anleitung kann nicht schaden!

Für diese Anleitung gilt wie für Arch Linux: Die Zielgruppe sind Leute, die mit Linux bereits gut umgehen können. Wenn Sie einen benutzerfreundlichen Einstieg in die Arch-Welt suchen, sollten Sie Manjaro ausprobieren. Dabei handelt es sich um eine Arch-Variante mit grafischer Installation. Und Linux-Einsteiger sind mit Ubuntu, Mint, openSUSE etc. besser bedient.

Sollten Sie noch nie Arch Linux installiert haben, empfehle ich Ihnen dringend, zuerst eine Probeinstallation in einer virtuellen Maschine durchzuführen, bevor Sie Arch Linux auf reale Hardware installieren.

Update 7.7.2021: Dieser Text beschreibt die manuelle Installation von Arch Linux. Eine Alternative dazu ist die Verwendung des noch experimentellen Scripts archinstall. Meine praktischen Erfahrungen damit habe ich hier zusammengefasst.

Installation im Live-System starten

Bevor Sie loslegen können, müssen Sie sich das ISO-Image von Arch Linux herunterladen:

https://archlinux.org/download/

Bei einer Installation in eine virtuelle Maschine verwenden Sie das Image als virtuelle DVD. Für eine »richtige« Installation übertragen Sie das Image mit Etcher oder dd auf einen USB-Stick.

Mit dem Start des Rechners bzw. der VM wird ein Arch-Linux-Live-System im Textmodus gestartet. Sie sind sofort als root eingeloggt und müssen nun die Installation manuell durch das ausführen von Linux-Kommandos ausführen.

Die Installation von Arch Linux erfolgt durch Kommandos im Textmodus

Tastaturlayout einstellen

Anfänglich gilt das US-Tastaturlayout. Wenn Sie mit einer deutschen Tastatur arbeiten, aktivieren Sie mit dem folgenden Kommando das richtige Tastaturlayout:

loadkeys de-latin1

Netzwerk- und Internetzugang

Sofern Ihr Rechner mit einem Ethernet-Kabel an das lokale Netzwerk angeschlossen ist, funktioniert der Netzwerkzugang dank DHCP auf Anhieb. Das gilt natürlich auch für virtuelle Maschinen. Testen Sie, ob ping google.com funktioniert!

Schwieriger ist die Sache bei Notebooks, deren Netzwerkzugang ausschließlich per WLAN hergestellt werden kann. In diesem Fall müssen Sie das Kommando iwctl zu Hilfe nehmen (siehe diese Anleitung).

Sie machen sich das Leben leichter, wenn Sie ein Netzwerkkabel verwenden, zur Not per USB-Adapter. Dann können Sie mit der WLAN-Konfiguration warten, bis Ihnen unter Arch Linux eine grafische Benutzeroberfläche zur Verfügung steht.

Installation via SSH

Im Live-System zur Arch-Linux-Installation läuft ein SSH-Server. Wenn Sie möchten, können Sie alle weiteren Kommandos via SSH ausführen. Das hat den Vorteil, dass Sie Kommandos per Cut&Paste in Ihr Terminal kopieren können. Allerdings müssen Sie zuerst ein root-Passwort festlegen und dann die IP-Adresse des Live-Systems ermitteln:

passwd

  New password: *********
  Retype new password: *********

ip addr

  1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue ...
     ...
  2: enp1s0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu ...
    inet 192.168.122.131/24 brd 192.168.122.255 ...

Von einem Rechner im lokalen Netzwerk führen Sie nun das folgende Kommando aus (wobei Sie natürlich Ihre IP-Adresse verwenden!):

ssh root@192.168.122.131

BIOS oder EFI?

Wenn Sie nicht wissen, ob Ihr System ein herkömmliches BIOS verwendet (viele virtuelle Maschinen) oder ein neueres EFI (nahezu alle handelsüblichen PCs), testen Sie einfach, ob das Verzeichnis /sys/firmware/efi existiert. In diesem Fall liegt ein EFI-System vor, sonst handelt es sich um BIOS.

Diese Information ist später zweimal relevant:

  • Partitionierung: Bei einem EFI-System müssen Sie eine EFI-Partition vorsehen bzw. diese, wenn sie schon vorhanden ist, in /mnt/boot/efi einbinden. Bei einem BIOS-System in Kombination mit einer GUID Partition Table (GPT) brauchen Sie hingegen eine winzige BIOS-GRUB-Partition.
  • Boot-Loader: Die Kommandos zur Installation des Boot-Loaders unterscheiden sich ein wenig, je nachdem, ob ein BIOS- oder ein EFI-System vorliegt.

Datum und Uhrzeit

Mit date können Sie überprüfen, ob Datum und Uhrzeit eingestellt wurden. In aller Regel ist das der Fall, sofern Sie über eine Netzwerkverbindung verfügen. Zur Not holt timedatectl set-ntp true die Zeiteinstellung nach. Mit der Konfiguration der Zeitzone warten Sie ab, bis Arch Linux installiert ist.

Partitionierung/Formatierung des Datenträgers

Als nächsten Schritt müssen Sie auf dem Datenträger eine oder mehrere Partitionen erzeugen und diese mit Dateisystemen initialisieren. Dazu verwenden Sie die Programme parted und mkfs, bei Bedarf auch LVM-, RAID- und CryptSetup-Tools.

Zuerst sollten Sie lsblk ausführen, damit Sie wissen, unter welchen Device-Namen Sie SSDs, Festplatten etc. ansprechen können. In einer virtuellen Maschine sieht das Ergebnis z.B. so aus:

lsblk

  loop0  ...          (Boot-System von Arch Linux)
  sr0    ...          (ISO-Image von Arch Linux)
  vda    20G   disk   (virtuelle Disk der VM)

Der einfachste Fall liegt vor, wenn Sie Arch Linux in eine VM mit BIOS installieren und auf eine Swap-Partition verzichten. Dann reicht es aus, im Datenträger der VM zwei Partition einzurichten, eine winzige BIOS-GRUB-Partition und eine Root-Partition. Ich empfehle, dort ein ext4-Dateisystem zu erzeugen, aber Sie haben natürlich die Wahl zwischen allen erdenklichen anderen Dateisystemen:

parted /dev/vda 

  (parted) mklabel gpt
  (parted) mkpart biosgrub 1mib 2mib
  (parted) set 1 bios_grub on
  (parted) mkpart rootpart 3mib -1mib
  (parted) unit mib
  (parted) print
    Partition Table: gpt
    Number  Start    End       Size      File system  Name      Flags
     1      1.00MiB  2.00MiB   1.00MiB                biosgrub  bios_grub
     2      3.00MiB  10239MiB  10236MiB               root
  (parted) quit

mkfs.ext4 /dev/vda2

Optional können Sie natürlich auch noch eine Boot- und/oder eine Swap-Partition einrichten.

Bei der Installation auf reale Hardware ist die Sache komplizierter. Wenn es schon andere Partitionen gibt, deren Inhalt erhalten bleiben soll, dürfen Sie diese nicht anrühren. Gibt es keine Partitionen, benötigen Sie zumindest eine EFI-Partition und eine Root-Partition. Auch eine Trennung von Root-, Boot-, Swap- und Home-Partitionen macht oft Sinn. Wenn jetzt noch LVM, RAID und/oder ein Crypto-Setup ins Spiel kommen, wird die Angelegenheit relativ unübersichtlich und erfordert sehr gutes Wissen über die diversen Kommandos zur Dateisystem-Administration.

Bei meiner EFI-Testinstallation war /dev/sda4 eine schon existierende EFI-Partition (keine Formatierung erforderlich) und /dev/sda6 die Root-Partition. Auf eigene Boot- und Swap-Partitionen habe ich verzichtet.

mkfs.ext4 /dev/sda6

Zieldateisystem mit /mnt verbinden

Nun verbinden Sie das Dateisystem der root-Partition mit dem bereits existierenden Verzeichnis /mnt. (Verwenden Sie anstelle von /dev/vda2 den richtigen Device-Namen Ihres Systems!)

mount /dev/vda2 /mnt

Wenn Sie weitere Dateisysteme eingerichtet haben, erzeugen Sie in /mnt entsprechende Unterverzeichnisse und binden dort die weiteren Dateisysteme ein. Die folgenden Zeilen zeigen die Vorgehensweise, um eine boot-Partition aus /dev/vda3 einzubinden:

mkdir /mnt/boot
mount /dev/vda3 /mnt/boot

Vergessen Sie bei EFI-Systemen nicht auf die EFI-Partition:

mkdir -p /mnt/boot/efi
mount /dev/sda6 /mnt/boot/efi

Basissystem installieren

Nach diesen Vorbereitungsarbeiten installieren Sie mit pacstrap das Basissystem inklusive des Linux-Kernels und der Firmware-Dateien in das /mnt-Verzeichnis. pacstrap lädt die erforderlichen Pakete aus dem Internet herunter, erfordert also eine funktionierende Netzwerkverbindung. Bei einer Installation in eine virtuelle Maschine können Sie auf die Firmware-Dateien verzichten; sie werden nicht gebraucht.

pacstrap /mnt base linux linux-firmware

/etc/fstab erzeugen

Das Kommando genfstab erzeugt /etc/fstab und wertet dabei die UUIDs der relevanten Dateisysteme aus.

genfstab -U /mnt >> /mnt/etc/fstab

Konfiguration des Basissystems

Mit arch-chroot, einer Variante zum gewöhnlichen chroot-Kommando, machen Sie /mnt zum aktiven Root-Verzeichnis. Von nun an werden alle Kommandos relativ zu diesem Verzeichnis gesucht bzw. ausgeführt.

arch-chroot /mnt

Zeitzone: Jetzt folgen diverse Konfigurationsarbeiten. Ein simples ln-Kommando stellt die gewünschte Zeitzone ein:

ln -sf /usr/share/zoneinfo/Europe/Berlin /etc/localtime

Uhrzeit: hwclock richtet die Datei /etc/adjtime ein. Das Kommando setzt voraus, dass die Hardware-Uhr des Rechners auf UTC eingestellt ist.

hwclock --systohc

Lokalisierungsdateien: Als nächstes öffnen Sie mit einem Editor die Datei /etc/locale.gen und entfernen die Kommentarzeichen bei den von Ihnen gewünschten Sprachen bzw. Ländercodes. Üblicherweise ist es sinnvoll, dass die folgenden Zeilen auskommentiert sind:

# in der Datei /etc/locale.gen
de_DE.UTF-8 UTF-8
en_US.UTF-8 UTF-8

Anfänglich steht als einziger Editor vi zur Verfügung. Wenn Sie sich damit nicht ärgern wollen, installieren Sie zuvor einen anderen Editor, z.B. nano oder emacs-nox.

pacman -S nano
nano /etc/locale.gen

Mit locale-gen erzeugen Sie schiesslich die Sprachdateien für die von Ihnen gewünschten Sprachen:

locale-gen

Sprache: Mit Ihrem Lieblingseditor erzeugen Sie nun die Datei /etc/locale.conf und tragen dort die gewünschte Default-Sprache ein. Vor und nach dem Zeichen = dürfen Sie keine Leerzeichen einfügen!

# Datei /etc/locale.conf
LANG=en_US.UTF-8

Oder:

# Datei /etc/locale.conf
LANG=de_DE.UTF-8

Tastaturlayout: Analog richten Sie die Datei /etc/vconsole.conf ein und geben passend zu Ihrer Tastatur das erforderliche Layout an:

# Datei /etc/vconsole.conf
KEYMAP=de-latin1

Hostname: Mit einem Editor tragen Sie in /etc/hostname den gewünschten Hostnamen ein.

/etc/hosts: Mit Ihrem Lieblingseditor richten Sie die Datei /etc/hosts ein, die drei Einträge entsprechend dem folgenden Muster haben sollte. (Ersetzen Sie archtest durch den tatsächlichen Hostnamen und local durch den Namen der Domäne Ihres lokalen Netzwerks.)

# Datei /etc/hosts
127.0.0.1     localhost
::1           localhost
127.0.1.1     archtest.local archtest

root-Passwort: Vergessen Sie nicht, mit passwd Ihr root-Passwort festzulegen:

passwd

Weitere Pakete: Wenn Sie Arch Linux demnächst erstmalig starten, verfügt das neue System über keine Netzwerkverbindung. Deswegen ist es eine gute Idee, schon jetzt alle erforderlichen Werkzeuge zu installieren, die Sie dann brauchen werden:

pacman -S dhclient networkmanager

Standardmäßig ist man nicht installiert. Abhilfe schafft:

pacman -S man

Wenn Sie wollen, können Sie natürlich auch andere Pakete schon jetzt installieren. In der Regel ist es aber zweckmäßiger, zuerst die Basisinstallation abzuschließen und weitere Pakete erst dann zu installieren, wenn alles funktioniert.

Boot-Konfiguration

Sofern Ihr Rechner exotische Hardware verwendet, für die schon während des Bootprozesses Kernelmodule erforderlich sind, oder wenn Sie LVM und/oder RAID und/oder Verschlüsselungsfunktionen nutzen, brauchen Sie eine initrd-Datei (genaugenommen eine initramfs-Datei). Diese erzeugen Sie wie folgt:

mkinitcpio -P

BIOS: Jetzt ist noch die Installation des Boot-Loaders ausständig. Ich gehe hier davon aus, dass Sie dazu das Programm GRUB verwenden. (Eine andere Option wäre systemd-boot.) Die GRUB-Installation ist davon abhängig, ob Sie (eine virtuelle Maschine mit) BIOS oder (einen realen Rechner mit) EFI verwenden. Beginnen wir mit BIOS, wo GRUB in die ersten Sektoren der Festplatte bzw. SSD installiert wird. (Das setzt voraus, dass Sie bei der Partitionierung das erste MiB freigelassen haben! Ersetzen Sie /dev/vda durch das Device des ersten Datenträgers laut lsblk!)

pacman -S grub
mkdir -p /boot/grub
grub-install /dev/vda
grub-mkconfig -o /boot/grub/grub.cfg

EFI: Auf einem EFI-System müssen Sie zusätzlich das Paket efibootmgr installieren. Sofern in /boot/efi die EFI-Partition zugänglich ist, funktioniert grub-install ohne weitere Parameter.

pacman -S grub efibootmgr
mkdir -p /boot/grub
grub-install
grub-mkconfig -o /boot/grub/grub.cfg

Microcode-Updates: Bei einer Installation auf echter Hardware sollten Sie entweder jetzt oder später, sobald das Grundsystem läuft, automatische Microcode-Updates für die CPU einrichten. Die Vorgehensweise ist natürlich im Arch-Wiki beschrieben.

Der erste Bootprozess

Damit ist es an der Zeit, einen erster Versuch zu machen, ob sich das frisch installierte starten lässt. (Haben Sie daran gedacht, das root-Passwort einzustellen?)

Dazu verlassen Sie die chroot-Umgebung mit Strg+D oder exit und führen reboot aus. Vergessen Sie nicht, bei virtuellen Maschinen die CD/DVD »auszuwerfen« (also das mit dem Laufwerk verbundene ISO-Image zu entfernen).

Wenn alles gut geht, erfolgt der erste Bootprozess verblüffend schnell: Knapp zwei Sekunden nach dem Verschwinden des GRUB-Menüs erwartet das System auch schon Ihren Login. Die Geschwindigkeit hat damit zu tun, dass das System noch komplett leer ist. Nicht einmal eine Netzwerkverbindung wird hergestellt.

Reparaturarbeiten

Wenn sich das frisch installierte Arch-Linux-System nicht starten lässt, müssen Sie die Installation nicht gleich komplett neu starten. Sie können auch versuchen, nochmals das Live-System des Installations-Images zu starten und dort Reparaturarbeiten durchzuführen. Im Wesentlichen stellen Sie zuerst das gewünschte Tastaturlayout ein, führen dann die für Ihr System passenden mount-Kommandos durch, wechseln mit arch-chroot in das unvollständig konfigurierte Zielsystem. Im einfachsten Fall genügen die folgenden Kommandos:

loadkeys de-latin1
mount /dev/vda1 /mnt
arch-chroot /mnt

Jetzt können Sie fehlende Pakete installieren, die GRUB-Installation wiederholen, das root-Passwort (neu) einstellen usw.

Weitere Konfigurationsarbeiten

Sie haben es sich vermutlich schon gedacht: Nun ist es Zeit für weitere Konfigurationsarbeiten.

Netzwerk aktivieren: Der erste Schritt ist natürlich die Herstellung einer Netzwerkverbindung, wobei ich weiterhin davon ausgehe, dass diese über ein Ethernet-Kabel erfolgt. In diesem Fall ist die Aktivierung des Netzwerks denkbar einfach:

systemctl enable --now NetworkManager

Dieses Kommando aktiviert den NetworkManager dauerhaft. Ein, zwei Sekunden später sollte Ihr Arch-Linux-System mit den lokalen Netzwerk und dem Internet verbunden sein. (Testen Sie dies z.B. mit ping google.de.)

Eine WLAN-Verbindung können Sie bei Bedarf mit nmtui einrichten. Alternativ warten Sie darauf, bis Sie eine Desktop-Umgebung (z.B. Gnome) installiert haben — dann stehen komfortablere Werkzeuge zur Verfügung.

SSH-Server: Damit Sie sich von außen bei Arch Linux anmelden können, brauchen Sie einen SSH-Server. Der lässt sich rasch installieren und aktivieren:

pacman -S openssh
systemctl enable --now sshd

Standardmäßig ist ein SSH-Login nur mit Key-Authentifizierung erlaubt. Wenn Sie auch einen gewöhnlichen Login akzeptieren wollen (was zumindest anfangs zweckmäßig ist), entfernen Sie in /etc/ssh/sshd_config vor der schon vorhandenen Zeile PasswordAuthentication yes das Kommentarzeichen #. Soll sich auch root direkt anmelden dürfen, bauen Sie außerdem PermitRootLogin yes in die Datei ein. Damit die Änderung wirksam wird, führen Sie systemctl reload sshd aus.

Benutzer mit sudo-Rechten: Als Nächstes sollten Sie einen zweiten Benutzer mit sudo-Rechten einrichten. Dazu führen Sie die folgenden Kommandos aus, wobei Sie kofler durch den gewünschten Benutzernamen ersetzen. Das usermod-Kommando fügt kofler zur wheel-Gruppe hinzu.

pacman -S sudo
useradd -m kofler
usermod -a -G wheel kofler
passwd kofler

Mit einem Editor müssen Sie nun in /etc/sudoers eine Zeile auskommentieren, damit alle wheel-Mitglieder sudo-Rechte erhalten:

export EDITOR=/usr/bin/nano
visudo
# Datei /etc/sudoers
# Kommentarzeichen bei der folgenden Zeile entfernen
%wheel ALL=(ALL) ALL

Gnome installieren

Sofern Sie keine Server-Installation geplant haben, sehnen Sie sich vermutlich inzwischen schon nach einer Desktop-Umgebung. Die nächsten Kommandos installieren und aktivieren das Grafiksystem samt Gnome. pacman zeigt einige Fragen an, welche optionalen Pakete es installieren soll bzw. welche Varianten von Paketen es berücksichtigen soll. Sie machen nichts verkehrt, wenn Sie sämtliche Rückfragen mit Return bestätigen. (Ein kompakteres System erhalten Sie, wenn Sie nur die wirklich erforderlichen Zusatzpakete installieren, indem Sie deren Nummern ohne Kommas aufzählen. Dabei besteht aber die Gefahr, dass Sie ein essenzielles Paket übersehen.)

pacman -S xorg gnome
systemctl enable --now gdm

Sobald Gnome läuft, können Sie in dessen Systemeinstellungen die gewünschte Sprache (Modul Region & Language) und das Tastaturlayout neuerlich einstellen — diesmal für Gnome.

Arch Linux mit Gnome 40, ausgeführt in einer virtuellen Maschine (virt-manager + KVM)

Auf einem Notebook ist jetzt der ideale Zeitpunkt, um über das Systemmenü bzw. in den Systemeinstellungen die WLAN-Konfiguration zu starten.

Software-Verwaltung

Anstelle von apt, dnf, yum oder zypper gibt es bei Arch Linux pacman. Das wichtigste Kommando haben Sie schon kennengelernt: pacman -S <name> installiert das angegebene Paket. (Die Option -S steht übrigens für synchronize. Wenn das Paket bereits installiert ist, wird es neu installiert. Das können Sie mit der Option --needed verhindern.) Die folgende Aufzählung nennt ein paar weitere Kommandos, die Sie oft brauchen:

  • pacman -y liest die Paketquellen neu ein.
  • pacman -Sy <name> aktualisiert zuerst die Paketquellen und installiert dann das gewünschte Paket.
  • pacman -Syu aktualisiert zuerst die Paketquellen und dann sämtliche installierten Pakete, führt also ein Komplett-Update durch. Beachten Sie, dass Arch Linux das Running Release-Modell realisiert. Sie erhalten also jederzeit die neueste, von den Arch-Entwicklern als ausreichend stabil betrachtete Version.
  • pacman -R <name> entfernt das installierte Paket.
  • pacman -Q listet alle installierten Pakete auf (query).
  • pacman -Ql <name> listet die Dateien des angegebenen Pakets auf.
  • pacman -Qo <filename> zeigt an, welches Paket die angegebene Datei zur Verfügung stellt.
  • pacman -Ss <pattern> sucht Pakete, die dem Suchbegriff entsprechen.

Noch mehr Informationen gibt’s wie immer im Arch Wiki.

Sofern Sie bei der Installation von Gnome nicht explizit darauf verzichtet haben, wurde auch Gnome Software installiert. Dieses Programm ist aber nicht pacman-kompatibel und daher funktionslos. (Generell bin ich der Meinung, dass dieses Programm unter der Würde jedes Arch-Linux-Nutzers ist.) Entfernen Sie es mit pacman -R gnome-software.)

Die Paketauswahl für Arch Linux ist groß, aber kleiner als z.B. bei Debian oder Ubuntu. Dafür gibt es unzählige von der Community verwaltete Installations-Scripts im Arch User Repository. Ein Beispiel für den Umgang mit AUR-Paketen folgt gleich.

Webbrowser

Standardmäßig ist unter Gnome der Webbrowser Web (Paketname epiphany). Firefox ist in der Regel eine bessere Wahl. Entfernen Sie also das eine Paket, installieren Sie das andere samt der deutschen Lokalisierung:

pacman -R epiphany
pacman -S firefox firefox-i18n-de

Statt Firefox können Sie mit pacman -S chromium auch Chromium installieren. Etwas komplizierter wird die Sache, wenn Sie Google Chrome verwenden wollen. Dafür gibt es kein fertiges binäres Paket, sondern ein Installation-Script im Arch User Repository (AUR). Bevor Sie AUR-Scripte installieren, sollten Sie sich einen AUR-Helper einrichten, also ein Kommando, das Sie bei der Verwaltung von AUR-Paketen unterstützt.

Die folgenden Kommando sind mit User-Rechten auszuführen (nicht als root). Damit installieren Sie zuerst ein paar Entwickler-Tools und laden das AUR-Script für yay herunter. mkpkg lädt dann den weiteren Code herunter, kompiliert das Tool zu einem Paket und installiert dieses. Beim letzten Schritt werden Sie nach Ihrem Passwort gefragt (für sudo). mkpkg weigert sich aber, gleich mit root-Rechten ausgeführt zu werden.

sudo pacman -S --needed base-devel git
git clone https://aur.archlinux.org/yay-git.git
cd yay-git
mkpkg -si

Ist yay einmal installiert, können Sie alle weiteren AUR-Pakete einfach mit yay -S installieren:

yay -S google-chrome

Quellen/Links

Raspberry Pi, 7. Auflage erschienen

29. Juni 2021 um 08:01

Ende Juni 2021 ist die siebte Auflage unseres Buchs erschienen!

Und das sind die wichtigsten Neuerungen:

  • aktualisiert im Hinblick auf die neuen Modelle Raspberry Pi 400 und Raspberry Pi Pico
  • der Raspberry Pi als Retro-Spielekonsole mit RetroPie
  • Ubuntu am Raspberry Pi
  • SATA-SSD statt SD-Karte
  • Raspberry Pi Imager
  • Programmiertechniken in MicroPython (Raspberry Pi Pico)
  • neue Projekte: CO2-Ampel und Ultraschall-Entfernungsmessung mit dem Raspberry Pi Pico
  • mit einem Geleitwort von Eben Upton

Umfang: 1088 Seiten, Farbdruck und Fadenbindung
ISBN: 978-3-8362-8351-9
Preis: Euro 44,90 (in D inkl. MWSt.)
Autoren: Michael Kofler, Christoph Scherbeck und Charly Kühnast

Weitere Informationen, Leseproben, Beispieldateien

https://pi-buch.info/raspberry-pi-7-auflage/

 

Meine Bücher als E-Book bei amazon kaufen?

20. Juni 2021 um 21:46

Der Rheinwerk Verlag, bei dem der Großteil meiner Bücher aus den letzten 10 Jahren erschienen sind, hat sich kürzlich dazu entschieden, einen Teil der Titel auch als Kindle-E-Books bei amazon.de anzubieten. (Schon immer konnten Sie Rheinwerk-Titel als Buch bei amazon kaufen — aber eben nicht als E-Book.) Für den Verlag gibt es gute Gründe dafür, E-Books via amazon zu verkaufen; aber es gibt auch Argumente dagegen. In diesem Text versuche ich Sie zu überzeugen, E-Books lieber direkt beim Rheinwerk Verlag zu erwerben.

Anmerkung: Dieser Blog-Beitrag gibt meine ganz persönliche Meinung zu diesem Thema wieder. Der Text hat nichts damit zu tun, was der Rheinwerk Verlag zu diesen Fragen denkt. Allerdings habe ich selbst fast 10 Jahre eigene E-Books bei amazon verkauft bzw. über meine eigene Website vermarktet (ebooks.kofler). Insofern glaube ich zu wissen, wovon ich hier spreche/schreibe.

Argument 1: Drei E-Books anstatt von einem

Wenn Sie ein E-Book bei amazon kaufen, erhalten Sie Zugriff auf die E-Book-Datei im proprietären MOBI-Format. Sie können das Buch somit nur auf den Geräten (Kindle etc.) lesen, die mit Ihrem amazon-Konto verbunden sind.

Kaufen Sie das gleiche E-Book zum gleichen Preis beim Rheinwerk Verlag, erhalten Sie es in drei Formaten: EPUB (fast alle Geräte inkl. iPad), MOBI (Kindle) und PDF (Notebook, Computer, Tablet).

Argument 2: PDF statt EPUB/MOBI

Ich bin der festen Überzeugung, dass die Formate EPUB und MOBI zur Darstellung von Fachbüchern und speziell zur Darstellung von IT-Büchern ungeeignet sind. Keine Frage, einen Roman kann man mit diesen Formaten perfekt abbilden/lesen, in beliebiger Formatgröße (vom Smartphone bis zum PC-Monitor) und in beliebiger Schriftgröße.

Die Formate MOBI/EBUP versagen aber bei der lesefreundlichen Darstellung von Listings (Zeilenumbruch) und komplexen Tabellen.

Sofern Sie nicht vor haben, IT-Bücher auf Ihrem Smartphone zu lesen (ohnedies keine gute Idee), ist PDF das bessere Format: Listings, Tabellen, Abbildungen und Fließtext sind auf die Seitengröße optimiert und mit viel Handarbeit (ich setze alle meine Bücher selbst!) lesefreundlich gestaltet. Selbstverständlich nutzen die PDFs Farben, sowohl in Abbildungen als auch bei Listings (Syntax Highlighting). Eine PDF entspricht eher dem »richtigen« Buch als EPUB/MOBI. Es ist damit leichter, eine schon einmal gelesene Seite wiederzuerkennen bzw. wiederzufinden.

Argument 3: Steuergerechtigkeit

amazon hat zuletzt beachtliche Gewinne erwirtschaftet, zahlt dafür aber kaum Steuer (und die wenigen Steuern wiederum in aller Regel nicht in Europe und schon gar nicht in D/A/CH sondern bestenfalls in Irland). Dank einer merkwürdigen internationalen Gesetzgebung ist das leider durchaus legal — aber eben nicht fair.

Der Rheinwerk Verlag ist dagegen ein deutsches Unternehmen. Es muss seine Steuern in Deutschland zahlen.

Anmerkung: Ich beziehe mich hier explizit auf die Besteuerung der Gewinne. Bei der Umsatz-/Mehrwertsteuer ist der E-Book-Verkauf von amazon dem Verkauf bei anderer europäischer Verlagen/Firmen/Händlern gleichgestellt.

Argument 4: Für einen kleinen Aufpreis das gedruckte Buch dazu

Auf der Website des Rheinwerk Verlags haben Sie die Wahl:

  • E-Book
  • gedrucktes Buch (in der Regel 5 Euro Aufpreis)
  • Kombiangebot aus gedrucktem Buch und E-Book (in der Regel 10 Euro Aufpreis)

Ich finde vor allem das Kombi-Angebot attraktiv. Es vereint die Vorteile beider Welten.

Noch eine Antwort auf die ewige Frage, warum das E-Book nicht viel billiger ist als das gedruckte Buch: Sie zahlen für den Inhalt, nicht für das Papier. Sie zahlen dafür, dass ich den Text verfasse und oft monatelang nichts anderes tue, als mich mit Linux, Kotlin oder welchem Thema auch immer zu beschäftigen. Sie zahlen dafür, dass der Verlag sich um Lektorat, Korrektur, Layout und Vermarktung kümmert. Und Sie zahlen dafür, dass Buchhandlungen bzw. amazon Bücher in Regale stellen, verkaufen und bis vor Ihre Haustüre liefern bzw. auf Ihr Gerät übertragen. (Übrigens verdient die Buchhandlung Ihrer Wahl bzw. amazon mit jedem verkauften Buch oder E-Book das Zwei- bis Dreifache dessen, was bei mir ankommt. Aber so sind nun einmal die Spielregeln.)

Warum überhaupt die Vermarktung über amazon?

Kein Verlag wird dazu gezwungen, Bücher und/oder E-Books bei amazon zu verkaufen. Aber natürlich ist amazon für viele Käufer die erste Adresse. Aus Kundensicht funktioniert amazon perfekt. Kurze Lieferzeiten, unkomplizierte Abwicklung.

Hinzu kommt: Das Ranking und die Rezensionen auf amazon entscheiden heute maßgeblich über den Erfolg von Büchern. Selbst viele Käufer, die in traditionellen Buchhandlungen kaufen, recherchieren zuerst auf amazon. Was dort nicht gefunden wird, existiert scheinbar gar nicht.

Naturgemäß bevorzugt amazon Titel, die es als E-Books verkaufen kann. Kindle-E-Books binden die Kunden langfristig innerhalb der amazon-Welt und ersparen amazon Versand- und Lagerkosten. Klar, dass amazon immer zuerst die Kindle-Angebote anzeigt und immer widerwilliger auch echte Bücher …

Schlusswort

Zuerst einmal: Bitte kaufen Sie weiterhin meine Bücher oder E-Books, ganz egal, ob in Ihrer Lieblingsbuchhandlung, auf der Rheinwerk-Website, bei amazon oder bei anderen Händlern. Vielen Dank dafür! Vielen Dank auch für alle positiven Rezensionen und für jedes Feedback per Mail! (Ich bemühe mich im Gegenzug seit über 30 Jahren, Ihnen bestmögliche Qualität zu liefern.)

Und dann: Wenn es Ihnen nicht zu kompliziert ist, erwägen Sie den Kauf direkt über Ihre Buchhandlung oder die Rheinwerk-Website. Ja, Sie müssen dazu womöglich noch ein Konto einrichten. Aber so schwierig ist das auch wieder nicht, oder?

Quellen

Farb-PDFs in Graustufen-PDFs umwandeln

09. Juni 2021 um 17:05

Kürzlich musste ich einige Tickets ausdrucken, die großflächig mit Farbe hinterlegt waren. Der Treiber meines Drucker bietet leider keine Möglichkeit, ein Dokument in Graustufen auszudrucken. Und der Farbdruck vergeudet eine Menge Tinte/Toner. Also habe ich nach einem Weg gesucht, Farb-PDFs in Graustufen-PDFs umzuwandeln. Und siehe da: Was Linux an Benutzerkomfort vermissen lässt, ersetzt es durch tolle Kommandos :-)

Variante 1: convert

Den bequemsten Weg bietet das Kommando convert aus dem Paket ImageMagick (apt/dnf install imagemagick). Mit den Optionen -quality und -density steuern Sie gleichzeitig die Qualität und Größe der resultierenden PDFs. Einen guten Kompromiss bietet:

convert -colorspace GRAY -density 300 -quality 95 in-color.pdf  out-grey.pdf

Noch schärfer wird der Ausdruck so:

convert -colorspace GRAY -density 400 -quality 100 Tickets-color.pdf  Tickets-grey.pdf

Die resultierende Datei ist dann aber auch entsprechend größer.

Bei vielen Distributionen weigert sich convert, PDFs zu verarbeiten, und liefert die folgende Fehlermeldung:

convert-im6.q16: attempt to perform an operation not allowed by the security policy `PDF' @ error/constitute.c/IsCoderAuthorized/421.
convert-im6.q16: no images defined `Tickets-grey.pdf' @ error/convert.c/ConvertImageCommand/3229.

Abhilfe schafft die Entfernung der entsprechenden Zeile aus /etc/ImageMagick-6/policy.xml. Im folgenden Listing habe ich die Zeile auskommentiert:

<policymap>
  ...
  <!-- <policy domain="coder" rights="none" pattern="PDF" /> -->
  ...
</policymap>

Variante 2: gs

Mit mehr Tippaufwand ist das zweite Kommando verbunden:

gs -sOutputFile=out-grey.pdf \
 -sDEVICE=pdfwrite \
 -sColorConversionStrategy=Gray \
 -dProcessColorModel=/DeviceGray \
 -dCompatibilityLevel=1.4 \
 -dNOPAUSE -dBATCH in-color.pdf

Die Qualität ist ähnlich wie bei convert -density 400 -quality 100. Die Rechenzeit ist deutlich höher, dafür ist die Dateigröße spürbar kleiner.

Quelle

https://superuser.com/questions/104656/convert-a-pdf-to-greyscale-on-the-command-line-in-floss

Fedora 34

28. April 2021 um 20:28

phoronix, heise.de und derstandard.at sind sich einig: Mit Version 34 ist Fedora ein großer Wurf gelungen. Tatsächlich gibt es diesmal Neuerungen, die ins Auge stechen und die zu hören sind: Als erste große Distribution liefert Fedora Gnome 40 auf und verwendet PipeWire als Audio-Framework. Wayland ist bei Fedora schon lange standardmäßig aktiv, aber erstmals gilt dies nun auch für den KDE-Zweig. Das Dateisystem btrfs wird jetzt standardmäßig komprimiert. Kurzum: Fedora gewährt wieder einmal einen Blick in die Linux-Zukunft.

Gnome 40

Mit Gnome 40, dem Nachfolger von Gnome 3.38, ändert sich nicht nur die Versionsnummer. Das Gnome-Projekt hat bei der Gelegenheit auch gleich eine Menge Neuerungen am Desktop durchgeführt, über die ich mich in Fluch und Segen von Gnome gerade ausgelassen habe. Die Kurzfassung: Ich finde die Optik großartig, vermisse aber zwei, drei Konfigurationsmöglichkeiten … Wenn ich auf einem großen Monitor arbeite, will ich das Dock ständig sehen, und zwar auf der linken Seite. Es ist nicht notwendig, dass dies wie unter Ubuntu die Default-Einstellung ist, aber ich fühle mich bevormundet, wenn ich diesbezüglich nicht wenigstens eine Wahlmöglichkeit habe.

Das ganzes Lamento hat mit Fedora natürlich wenig zu tun. Fedora liefert einfach den neuesten Gnome-Desktop so aus, wie die Gnome-Entwickler ihn gerade zur Verfügung stellen. Das ist ein einfacher, pragmatischer Ansatz, der diesbezüglich keine Zeit verschwendet.

Das Hintergrundbild von Fedora 34 ist so schön, dass man gar keine Fenster öffnen will.

PipeWire

PipeWire ist ein Server für Audio- und Video-Streams. Im Audio-Bereich soll PipeWire längerfristig PulseAudio ablösen. Ein Vorteil von PipeWire sind offensichtlich geringere Latenzen, aber aus eigener Erfahrung kann ich das nicht beurteilen. Gleichzeitig stellt PipeWire ein Framework zur Verfügung, das sicheres Screen Sharing bzw. Remote-Desktop-Anwendungen unter Wayland möglich macht. Schon alleine aus diesem Grund wird in der Zukunft wohl kein Weg an PipeWire vorbei gehen.

Fedora ist die erste große Distribution, die standardmäßig PulseAudio durch PipeWire ersetzt. Diese Vorreiterrolle nahm Fedora auch schon bei PulseAudio ein (Fedora 8, 2007). Die Begeisterung der Anwender hielt sich damals in Grenzen, PulseAudio war noch nicht ausgereift und verursachte viele Probleme. Dennoch etablierte PulseAudio später auch in anderen Distributionen als Audio-Standard.

Es ist zu hoffen, dass PipeWire nicht ähnliche Probleme verursacht. Bei meinen Tests ist mir nichts aufgefallen. Wenn ich nicht aus dem Studium der Release Notes wüsste, dass sich Fedora für ein neues Audio-Backend entschieden hat, hätte ich PipeWire gar nicht bemerkt. Das ist einer Kompatibilitätsschicht zu verdanken (Paket pipewire-pulseaudio). Echte Vorteile bietet PipeWire für Programme, die die JACK API nutzen (JACK Audio Connection Kit). Derartige Programme sollten jetzt auf Anhieb und ohne Konfigurationsarbeiten funktionieren.

Spannende Hintergrundinformationen bei der Analyse des Audio-Systems gibt pw-top. Es listet alle aktiven Audio-Komponenten samt Latenzzeiten auf.

Das Kommando fw-top gibt Informationen zum PipeWire-Status

btrfs

Fedora verwendet wie schon in Version 33 standardmäßig das Dateisystem btrfs. Dabei werden zwei Subvolumes für /root und /home eingerichtet. Neu in Version 34 ist die Aktivierung der Kompressionsfunktionen (compress=zstd). Dadurch werden Dateien, die sich leicht komprimieren lassen, automatisch komprimiert. Das bringt mehr, als man erwarten würde:

compsize -x /

Processed 147674 files, 97366 regular extents (106279 refs), 76200 inline.
Type       Perc     Disk Usage   Uncompressed Referenced  
TOTAL       57%      3.3G         5.8G         6.4G       
none       100%      2.0G         2.0G         1.9G       
zstd        35%      1.3G         3.8G         4.4G       
prealloc   100%      2.8M         2.8M          20M 

Die obigen Daten stammen von einer Standardinstallation von Fedora 34 mit wenigen Dateien in /home (sprich: von einer Testinstallation). Bei Dateien um Umfang von 2,0 GB hat btrfs auf die Komprimierung verzichtet. Weitere 3,8 GB wurden komprimiert, die Ersparnis betrug dabei fast 2,5 GB (also 3,8 minus 1,3).

Versionsnummern

Basis             Desktop             Programmierung   Server
---------------   ------------------  --------------   --------------
Kernel     5.11   Gnome          40   bash       5.1   Apache     2.4
glibc      2.33   Firefox        88   gcc         11   CUPS       2.3
X-Server   1.20   Gimp         2.10   Java     11/16   MariaDB   10.5
Wayland    1.19   LibreOffice   7.1   PHP        7.4   OpenSSH    8.5
Mesa         21   Thunderbird    78   Python     3.9   qemu/KVM   5.2
Systemd     248                                        Postfix    3.5
NetworkMan 1.30                                        Samba     4.14
GRUB       2.04 

Quellen / Links

Tests

Gnome 40

Pipewire

btrfs

Fluch und Segen von Gnome

28. April 2021 um 18:32

Mit Gnome 40, dem Nachfolger von Gnome 3.38, ändert sich nicht nur die Versionsnummer. Das Gnome-Projekt hat bei der Gelegenheit auch gleich eine Menge Neuerungen am Desktop durchgeführt. Soviel vorweg: Die Optik ist großartig! (Der Eindruck wird durch den wunderschönen Fedora-34-Hintergrund noch verstärkt.)

Aktivitäten

Genaugenommen sind die Neuerungen in Version 40 gar nicht so spektakulär:

  • Das Dock wandert von der linken Seite nach unten — also dorthin, wo es sich auch unter Windows, macOS und am iPad befindet.
  • Wenn es mehrere virtuelle Bildschirme (Workspaces) gibt, werden diese nebeneinander angeordnet (nicht mehr untereinander). Diesbezüglich steht wohl macOS Pate.

  • Auf einem Touchpad können Sie mit drei Fingern zwischen den Workspaces wechseln bzw. die Aktivitäten-Ansicht ein- oder ausblenden (wie in macOS). Noch schneller gelingt der Workspace-Wechsel mit dem Mausrad. Die selben Aufgaben erledigen schließlich die Tastenkombinationen Windows+Alt+Cursor links/rechts/oben/unten.

  • In der Grid-Ansicht können die installierten Programme (»Apps«) mühelos neu angeordnet oder in Gruppen zusammengefasst werden (wie auf vielen Tablets).

Grundsätzlich sind all diese Neuerungen durchdacht und vernünftig. Unverändert bleibt übrigens die Dock-Sichtbarkeit: Das Dock wird nur angezeigt, wenn Sie Aktivitäten anklicken oder die Windows-Taste drücken.

App-Grid

Wir wissen, was gut für dich ist!

Vermutlich liegt es in der Natur des Menschens, auf Änderungen zuerst einmal mit Skepsis und Widerwillen zu reagieren. Und vielleicht ist dieser Blog-Artikel einfach nur ein Ausdruck dieser Änderungs-Resistenz?

Mein Problem mit Gnome 40 ist (wie schon mit früheren Gnome-Versionen) die fehlende Wahlmöglichkeit. Mein Notebook ist sicher zu 90% der Nutzungszeit mit einem 28-Zoll-Monitor verbunden. Dieser bietet genug Platz, um das Dock ständig anzuzeigen und ICH WILL ES SO. Außerdem wünsche ich mir das Dock auf der linken Seite. Ubuntu ist per Default so konfiguriert, und bei anderen Distributionen installiere ich eben die Erweiterung Dash to Dock. In Gnome 40 bleibt dieser Ausweg verschlossen — Dash to Dock und Gnome 40 sind vorerst inkompatibel zueinander.

Windows und macOS verhalten sich per Default ähnlich wie Gnome. ABER: Beide Betriebssysteme geben mir die Freiheit, das Dock mit wenigen Mausklicks so zu konfigurieren, wie ich es haben will. Nur Gnome nicht. Die Gnome-Entwickler wissen, was gut für mich ist, da braucht es keine Wahlmöglichkeit.

Ich bin nicht vor mehr als 25 Jahren zu Linux gewechselt, damit mir Gnome vorschreibt, wie ich meinen Desktop organisieren soll. (Den Einwand, ich hätte ja mit KDE, XFCE usw. reichlich Auswahl, lasse ich hier nicht gelten. Ich bin mit Gnome weitestgehend zufrieden. Ich betrachte es als das beste, ausgereifteste Desktop-System, das Linux aktuell bietet, auf jeden Fall als den besten Kompromiss. Was nützt mir ein anderes Desktop-System, das mir mehr Dock-Flexibilität gibt, aber das dafür sonst voller Fehler und Mängel ist?)

Was mein Dock-Problem betrifft, kann ich natürlich darauf warten, bis Dash to Dock eine Gnome-40-kompatible Version ihrer großartigen Shell Extension fertigstellt. Oder bis es eine Alternative dazu gibt. (Meine aktuelle Notlösung ist Floating Dock.) Zur Not könnte ich versuchen, selbst eine geeignete Shell Extension zu programmieren — aber ich gebe zu, dazu fehlt mir die Zeit und das JavaScript-Know-how.

Warum sind Shell Extensions überhaupt notwendig, um vollkommen elementare Grundfunktionen zu realisieren? Funktionen, die gefühlt jedes Desktop-System selbstverständlich anbietet, nur nicht Gnome? Shell Extensions sind aus meiner Sicht eher Hacks als Lösungen. Jedes Gnome-Update führt zu neuen Problemen und Inkompatibilitäten. Viele Extensions haben nur eine recht kurze Lebensdauer, bis sich ihre Entwickler neuen Zielen zuwenden.

Und weil ich schon im Mecker-Modus bin: Wie wäre es, die Module des Programms Einstellungen alphabetisch zu ordnen? Ich kann in der vorliegenden Ordnung keine wie auch immer geartete Logik erkennen. Letztlich scrolle ich immer wieder von oben nach unten, bis ich die Audio-Einstellungen dann finde. (Tipp: Sie sind irgendwo in der Mitte, zwischen Freigabe-Einstellungen und Energiespar-Optionen …) Insofern wäre alphabetisch immer noch besser als gar keine Ordnung.

Einstellungen

Und noch ein Punkt für die Gnome-Wunschliste: Warum kann Gnome Fenster nicht unkompliziert in Bildschirm-Vierteln anzuordnen? Nein, Bildschirm-Hälften sind auf einem großen Monitor zu wenig. Ja, ich weiß, die Lösung heißt Windows. Das ist ein Satz, den ich normalerweise nicht ausspreche/schreibe … (Oder es muss wieder eine Shell Extension her: Ich kann z.B. den Tiling Assistant empfehlen.)

Die Konsequenzen

Natürlich kann niemand kann den Gnome-Entwicklern vorschreiben, in welche Richtung sie ihr Projekt weiterentwickeln. Aber die oft überraschenden, eigenwilligen Design-Entscheidungen irritieren nicht mich alleine. Sie führen dazu, dass Ubuntu das Gnome-40-Update vorerst nicht mitmacht, dass jetzt auch Pop!OS den Desktop neu erfinden will, dass es mit Elementary, Cinnamon, Mate sowieso schon ein Dutzend Varianten gibt, die in Wirklichkeit (fast) keiner haben will. Deren Daseinsberechtigung sich durch einige Details ergibt, die sich außerhalb des Gnome-Universums offenbar einfacher realisieren lassen als innerhalb. Die in einem sinnlosen Ausmaß Entwickler-Ressourcen binden. Die nicht fertig werden. Zu denen es schon bald keine Updates mehr geben wird.

Linux braucht Standards, um erfolgreich zu sein. Nicht alle wollen es wahr haben, aber Gnome ist dieser Standard im Desktop-Bereich. Natürlich gibt es Alternativen, aber die meisten Linux-Einsteiger beginnt mit Gnome. Firmen setzen auf Gnome und lassen Alternativen oft gar nicht zu.

Hinzu kommt: Gnome ist prinzipiell eine ausgezeichnete Desktop-Umgebung. Ich bin sogar mit dem Gnome-Konzept »Weniger ist mehr« einverstanden. Ich verstehe die Logik: Weniger Optionen sind übersichtlicher, einfacher zu bedienen, besser wartbar. Ich vertrete dieses Motto in Projekten, an denen ich arbeite, selbst.

Aber ich finde, Gnome übertreibt. Es gäbe sicherlich weniger Fragmentierung im Desktop-Bereich, wenn Gnome ein wenig mehr Flexibilität bieten könnte. Der Ansatz One size fits all funktioniert einfach nicht. Es braucht nicht für jede Option 1000 Einstellmöglichkeiten (-> KDE). Dennoch kann man den Anwendern ein paar Einstellmöglichkeiten lassen. Den Status Quo empfinde ich und empfinden offenbar auch andere als eine Bevormundung, die verärgert, frustriert und dazu führt, dass es unter Linux mehr Desktop-Systeme als Mail-Programme gibt.

Oder, neue Argumentationslinie: Gnome bietet seinen Nutzern mehr Audio-Optionen als Optionen, die die Gestaltung des Desktops betreffen. (Genaugenommen gibt es ja nur eine Option: Über das Hintergrundbild dürfen Sie vorerst noch selbst verfügen …) Es gibt mehr als ein Dutzend Optionen zur Barierrefreiheit. Die Benachrichtigungen können bis ins letzte Detail gesteuert werden. (Alle diese Optionen dürfen bleiben, nicht dass mir das jemand falsch auslegt …) Aber angesichts der Anzahl von Optionen in Randbereichen des Desktops wäre ein Dialogblatt »Desktop-Einstellungen« mit vier, fünf Optionen auch noch vertretbar. Es würde auch den Wartungsaufwand nicht sofort ins Unendliche wachsen lassen.

Schlusswort

Ich bin dankbar, dass es Linux gibt, dass es Gnome gibt. Ich bin jeder Entwicklerin, jedem Entwickler dankbar, die/der auf ihre/seine Weise das Open-Source-Universum vergrößert, an Projekten mitarbeitet, Gnome noch perfekter macht. Dieser Blog-Beitrag soll kein Zeichen meiner Undankbarkeit sein, er soll keinen Flame War auslösen.

Alles, was ich mir wünsche, ist ein Gnome, das noch ein kleines bisschen besser ist als bisher. Das seinen Anwendern ein klein wenig mehr Flexibilität bietet. Das mehr als nur ein Anwendungs-Szenario vorsieht (in etwa: kleiner/großer Monitor; Einsteiger/Entwickler) . Ich bitte nicht um unzählige neue Optionen, sondern um einige wenige. Und es wäre schön, wenn man die ohne Extensions oder Gnome Tweaks direkt in den Einstellungen verändern könnte. Ein guter Startpunkt wäre:

  • Dock: links/rechts/unten
  • Dock automatisch ausblenden: ja/nein
  • Fenster-Minimieren-Button anzeigen: ja/nein

Wenn ich total unverschämt sein darf, auch noch:

  • Fenster-Buttons: links/rechts

Das würde macOS- und Windows-Umsteigern gleichermaßen entgegen kommen.


PS: Noch eine Anmerkung zu Pop!_OS: System76 verspricht in seinem Blog den neuen Cosmic-Desktop, der mit Version 21.04 im Juni ausgeliefert werden soll. Laut ersten Tests einer Beta-Version (YouTube-Video) handelt es sich dabei ganz einfach um Gnome 3.38, das um diverse Shell Extensions erweitert wurde (inklusive einer Menge neuer Optionen im Einstellungsdialog). Im Prinzip ist das der gleiche Ansatz wie bei Ubuntu, aber mit mehr Änderungen. Und auch die Probleme sind die gleichen: Was tun, wenn die neue Gnome-Version inkompatibel zu den eigenen Extensions ist?

Ubuntu 21.04

22. April 2021 um 16:57

Ubuntu 21.04 (»Hirsute Hippo«) ist fertig. Auf meinem Hauptrechner läuft es schon seit zwei Wochen (nachdem ich Ubuntu 20.10 übersprungen und nur in virtuellen Maschinen ausprobiert habe). Dieser Artikel fasst die wichtigsten Neuerungen zusammen.

Update 28.4.2021: Suspend-Probleme (siehe eigenen Abschnitt), Hinweis auf Raspberry Pi

Das Wallpaper von Ubuntu 21.04

  • Ubuntu ist bei Gnome 3.38 geblieben und hat das Update auf Version 40.0 nicht nachvollzogen. Der Hauptgrund ist vermutlich das neue Gnome-Layout, das zu den Ubuntu-eigenen Shell-Erweiterungen (und insbesondere zur dauerhaft am linken Rand angezeigten Task-Leiste) inkompatibel ist. Ich muss zugeben, dass ich Ubuntu in diesem Punkt verstehe: Gnome 40 sieht auf einem kleinen Notebook-Monitor großartig aus, bringt mich aber auf meinem Arbeitsplatz (28-Zoll-Monitor mit 4k) zum Verzweifeln. Mir erscheint der Gnome-Weg einmal mehr fundamentalistisch. Wir wissen, wie es richtig ist, alle müssen es jetzt so machen … (Ich werde demnächst beim Fedora-34-Blog-Beitrag mehr dazu schreiben.)
  • Aufgrund einer neuen Shell Extension funktioniert das Ablegen von Icons am Desktop anscheinend besser als bisher. Da ich dieses Funktion nie verwende und mir auch rätselhaft ist, warum man Icons auf den Desktop platziert, die dann doch unter allen Fenstern verschwinden, kann ich dazu nicht viel sagen.

  • Ubuntu verwendet jetzt standardmäßig Wayland. Dieser Aussage müssen natürlich gleich ein paar Einschränkungen folgen: Zum einen gilt dies nur für das »Haupt-Ubuntu«, nicht aber für die meisten Derivate. Und zum anderen sind Wayland und die NVIDIA-Treiber weiterhin inkompatibel zueinander. Wer also eine NVIDIA-Grafikkarte samt proprietären Treiber verwendet, kommt weiter in den Genuss von X.

  • Die Heimatverzeichnisse sind jetzt besser geschützt. Bisher waren die Zugriffsrechte oktal mit 755 voreingestellt, d.h. prinzipiell konnte jeder in die Home-Verzeichnisse anderer Personen hineinschauen (r-x für others). Nunmehr lauten die Zugriffsrechte 750, was sicherer ist.

  • Wenn Sie sich bei der Installation von Ubuntu für die Verschlüsselung des Dateisystems entscheiden, zeigt das Setup-Programm in einem überarbeiteten Dialog einen Recovery-Key an, den Sie verwenden können, sollten Sie Ihr Verschlüsselungspasswort vergessen.

Wenn Sie das Dateisystem verschlüsseln, offeriert das Installations-Programm jetzt einen Recovery-Key.
  • Snap ist weiterhin standardmäßig aktiv. Interessanterweise sind zwar einige Snap-Bibliotheken installiert, aber außer dem Snap-Store (einer Variante des Gnome-Programms Software) kein einziges Programm. (In der Vergangenheit hat Ubuntu einige Gnome-Standardprogramme als Snaps verpackt, wobei ich den Grund nie verstanden habe. Damit ist jetzt offenbar Schluss.)
snap list

Name               Version             Revision  Tracking         Herausgeber  Hinweise
---------------------------------------------------------------------------------------
core18             20210309            1997      latest/stable    canonical✓   base
gnome-3-34-1804    0+git.3556cb3       66        latest/stable/…  canonical✓   -
gtk-common-themes  0.1-52-gb92ac40     1515      latest/stable/…  canonical✓   -
snap-store         3.38.0-59-g494f078  518       latest/stable/…  canonical✓   -
snapd              2.49.2              11588     latest/stable    canonical✓   snapd

Versionsnummern

Basis             Desktop             Programmierung   Server
---------------   ------------------  --------------   --------------
Kernel     5.11   Gnome        3.38   bash       5.1   Apache     2.4
glibc      2.33   Firefox        87   docker   20.10   CUPS       2.3
X-Server   1.20   Gimp         2.10   gcc       10.3   MySQL      8.0
Wayland    1.18   LibreOffice   7.1   Java        11   OpenSSH    8.4
Mesa       21.0   Thunderbird    78   PHP        7.4   qemu/KVM   5.2
Systemd     247                       Python     3.9   Postfix    3.5
NetworkMan 1.30                                        Samba     4.13
GRUB       2.04 

Gnome wird genaugenommen nicht konsequent in Version 3.38 ausgeliefert. Vielmehr gibt es vereinzelte Pakete, die neuer sind (gnome-calculator, gnome-font-viewer etc.) und solche, die älter sind (gnome-keyring, libgnome-bluetooth). Bei Bedarf können Sie sich mit dpkg -l | grep gnome rasch selbst einen Überblick verschaffen.

Ubuntu auf dem Raspberry Pi

Die Vollversion von Ubuntu 21.04 läuft auch auf aktuellen Raspberry-Pi-Modellen — vorausgesetzt, diese sind mit 4 GB RAM ausgestattet. Ubuntu funktioniert trotz der eingeschränkten Hardware erstaunlich gut. Einen Erfahrungsbericht finden Sie auf pi-buch.info.

Fazit

Wie gesagt, ich verwende nun seit fast zwei Wochen dauerhaft Ubuntu 21.04. Reale Unterschiede, die bei meiner täglichen Arbeit spürbar wären, habe ich nicht bemerkt. Weder optisch noch funktionell hat sich (für mich) etwas getan. Was die Stabilität betrifft, bin ich genau so zufrieden wie mit 20.04 — und das ist ein gutes Zeichen.

Von einem winzigen Ärgernis kann ich berichten: Aus irgendeinem Grund scheint die Spotify-App (ich verwende normalerweise das Debian-Paket, nicht das Snap-Paket) nicht mehr so recht zu funktionieren. Sie weigert sich, auf meinem Rechner Musik abzuspielen. Ich habe mir bisher nicht die Mühe gemacht, dem Problem auf den Grund zu gehen. Die App war nie eine Offenbarung. Spotify funktioniert im Browser ebenso gut.

Ubuntu 21.04 mit Gnome-3.38-Dekstop und einigen offenen Fenstern

Intel Undervolting führt zu Suspend-Problemen (Update 26.4.2021)

Mittlerweile ist doch ein Problem aufgetreten — aber Ubuntu ist daran unschuldig. Ich verwende intel-undervolting (siehe GitHub). Zusammen mit aktuellen Kernel-Versionen führt das zur Fehlermeldung Write to unrecognized MSR 0x150 und in weiterer Folge dazu, dass Suspend nicht mehr funktioniert: Failed to change runlevel. Transaction for reboot.target/start is destructive (intel-undervolt.service has ’start‘ job queued, but ’stop‘ is included in transaction. Ich habe jetzt in /etc/default/grub die folgenden Änderungen eingebaut:

GRUB_CMDLINE_LINUX_DEFAULT="msr.allow_writes=on"
GRUB_CMDLINE_LINUX="msr.allow_writes=on"

Einstweilen scheint das auszureichen. Längerfristig wird der Linux-Kernel aber MSR-Writes ganz unterbinden. Spätestens dann braucht es ein Update für intel-undervolt, oder das Tool wird nicht mehr funktionieren. Hintergründe:

https://github.com/kitsunyan/intel-undervolt/issues/55
https://git.kernel.org/pub/scm/linux/kernel/git/tip/tip.git/about/

Quellen / Links / Andere Tests

LaTeX-Benchmark: Mac M1 vs. Lenovo P1 bzw. macOS vs Linux

03. April 2021 um 18:24

Für viele Entwickler ist der CPU-intensivste Prozess das Kompilieren großer Projekte. Für mich als Autor ist es die Umwandlung von Markdown-Dateien zuerst in die LaTeX-Syntax (Pandoc) und dann in ein PDF-Dokument (latex, dvips, ps2pdf). Seit ein paar Tagen verfüge ich über einen Mac Mini M1 als zusätzlichen Testrechner. Natürlich war ich neugierig, wie schnell das Ding nun wirklich ist — im einzigen Real-World-Test, der für mich relevant ist: Beim Erzeugen von PDFs aus LaTeX-Dateien.

Setup

Mein voll wissenschaftliches ;-) Benchmark-Setup sah so aus:

  • Rechner 1: Lenovo P1 Notebook mit i7-8750H-CPU, 32 GB RAM, Ubuntu 20.04 LTS
  • Rechner 2: Mac Mini M1: 16 GB RAM, M1-CPU, macOS 11.2

Die zu übersetzende LaTeX-Datei ist 1,3 MByte groß (das ergibt ca. 550 Seiten Text), die einzubettenden EPS-Dateien umfassen 80 MByte, die resultierende PDF-Datei ist dann ca. 18 MByte groß. Überschaubare Datenmengen also.

Sämtliche Werkzeuge (latex, dvips, ps2pdf) sind single-threaded, profitieren also leider nicht von vielen CPU-Cores.

Linux plus Docker

Unter Linux verwende ich für Pandoc und LaTeX einen Docker-Container, der sämtliche Werkzeuge in einer unveränderlichen Version enthält. Die Verwendung von Docker hat sich sehr bewährt, erstens weil ich ohne viel Installationsarbeiten ein ganzes Buch schnell auf einen anderen Rechner »übersiedeln« kann und alles weiter funktioniert, und zweites, weil nicht irgendwelche LaTeX- oder Pandoc-Updates die Kompatibilität zu meinen handgestrickten Scripts zerstören.

Wenn mein Notebook im ‚Leise-Modus‘ läuft (CPU-Takt auf 80% = 2,2GHz limitiert, kein Turbomodus), dann dauert die PDF-Erzeugung (zuerst pandoc, dann 2x latex, dvips und ps2pdf) ca. 2:09 min, also 129 Sekunden.

Wenn ich es eilig habe und die CPU auf ‚Performance‘ stelle (volle Taktfrequenz, mit Turbomodus), verdoppelt sich die Geschwindigkeit nahezu, Laufzeit 1:14 min. Allerdings heult dann der Lüfter wie ein Düsenjäger, was ich hasse und die Konzentration mindert.

macOS plus Docker

Docker für den Mac Mini M1 hat in den letzten Wochen massive Fortschritte gemacht und läuft inzwischen ziemlich gut. Ich habe mit RC3 vom 1. April gearbeitet. Viele weit verbreitete Images stehen bereits nativ für ARM zur Verfügung. Mein LaTeX-Script läuft in 1:12 min, also im Rahmen der Messungenauigkeit gleich schnell wie auf dem Lenovo-Notebook bei voller CPU-Leistung. Allerdings ist der Mac Mini dabei absolut lautlos. (Es gibt einen Lüfter. Dieser läuft dauerhaft mit 1700 RPM, ist aber für meine Ohren unhörbar. Wenn man nicht gerade synthetische Benchmark-Tests oder das Xcode-Image auspackt ausführt, wird der Lüfter nie hörbar.)

PS: Alle (und speziell die heise.de-Autoren), die meinen, Docker sei angesichts der Container-Konkurrenz durch Podman und Co. obsolet, sollen einmal versuchen, mit Containern auf nicht Intel-kompatiblen Systemen bzw. außerhalb der Linux-Welt zu arbeiten :-)

macOS nativ mit MacTeX 2021

Die ersten Versionen von Docker für die ARM-CPU von Apple litten unter massiven Performance-Problemen, besonders bei I/O-Operationen. Massiv betroffen waren Datenbanksysteme. Mittlerweile scheinen dieses Probleme weitgehend behoben zu sein, aber ich wollte doch wissen, ob Docker in meinem Setup nicht ein Bremsklotz ist.

Also habe ich MacTeX 2021 installiert (erst seit wenigen Tagen verfügbar, unterstützt die ARM-Architektur nativ) und meinen Test damit wiederholt. Ergebnis: 1:05 min. (Pandoc habe ich auch bei diesem Test unter Docker ausgeführt, weil es die von mir benötigte Pandoc-Version nicht als nativen ARM-Download gibt. Ich habe mir deswegen die schon etwas ältere Pandoc-Version 2.7.3 aus den Quellen selbst in ein Ubuntu-Docker-Image kompiliert.)

Zusammenfassung

Laufzeiten für pandoc sowie für 2x latex, dvips, ps2pdf

                                Zeit              Geräuschkulisse
------------------------------- ----------------  --------------------
Lenovo P1 8750H@2,2 GHz         49 + 80 = 129 s    zumeist lautlos
Lenovo P1 8750H@4 GHz           29 + 45 =  74 s    störend
Mac Mini M1 (LaTeX in Docker)   28 + 44 =  72 s    lautlos
Mac Mini M1 (LaTeX nativ)       28 + 37 =  65 s    lautlos

Alles in allem ist die Performance der M1-CPU eindrucksvoll — insbesondere, wenn man nicht nur die Geschwindigkeit, sondern auch die Leistungsaufnahme berücksichtigt. Ich weiß natürlich, dass ich Äpfel mit Birnen vergleiche: Die Intel-CPU ist ca. zwei Jahre älter als die M1-CPU. Der Mac ist ein (kleiner) Desktop-Rechner, als Vergleichsrechner musste ein Notebook herhalten. Ein Desktop-System mit einer aktuellen Intel- oder AMD-CPU ist natürlich schneller als beide Testkandidaten, und bei geeigneter Kühlung für meine Aufgabenstellung ebenfalls (nahezu) lautlos.

Andererseits: So flüssig sich macOS auf einem M1-Rechner anfühlt — auch Apple kocht nur mit Wasser. Synthetische Tests (z.B. GeekBench) lassen den M1 schneller erscheinen, als er bei Real-World-Aufgaben tatsächlich ist. Nach dem Mediengetöse seit der M1-Vorstellung hatte ich eigentlich erwartet, dass der Mac Mini mein Notebook meilenweit abhängen würde. Das ist definitiv nicht der Fall. (Ich kann natürlich nicht ausschließen, dass Apple mit dem M1X oder M2 oder wie auch immer der M1-Nachfolger heißen wird, noch einmal ordentlich nachlegt. Aber die CPU-Entwicklung bleibt ja auch bei Intel und AMD nicht stehen. Speziell bei Intel erwarte ich, dass die Firma jetzt aus dem Dornröschenschlaf der letzten Jahre erwacht. Nichts motiviert besser als gute Konkurrenz.)

Links

❌