Sie sind auf Seite 1von 88

Entwicklung einer portablen, dynamischen Programmbibliothek zum abstrahierten Zugri auf den REST-Dienst des spirit@fhs Projektes

Bachelorarbeit
zur Erlangung des akademischen Grades Bachelor of Science Informatik

Fakultt: Erstgutachter: Zweitgutachter: vorgelegt von: Matrikelnummer:

Informatik Prof. Dr. Oliver Braun Dipl.-Math. Gerd Recknagel Robert Worgul 28 10 17

Inhaltsverzeichnis
1. Einleitung 2. Grundlagen 2.1. ANSI C . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2.1.1. berblick . . . . . . . . . . . . . . . . . . . . . . . . . . . 2.1.2. Die Standards von C . . . . . . . . . . . . . . . . . . . . 2.1.3. Die Standardbibliothek . . . . . . . . . . . . . . . . . . . 2.1.4. Modularisierung in C . . . . . . . . . . . . . . . . . . . . 2.1.5. Erzeugung eines Programmes . . . . . . . . . . . . . . . 2.2. Programmbibliotheken . . . . . . . . . . . . . . . . . . . . . . . 2.2.1. Die Programmbibliothekstypen . . . . . . . . . . . . . . 2.2.2. Struktur einer dynamischen Programmbibliothek . . . 2.2.3. Richtlinien fr portable, dynamische Programmbibliotheken . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2.3. REST . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2.3.1. berblick . . . . . . . . . . . . . . . . . . . . . . . . . . . 2.3.2. RESTful Web Services . . . . . . . . . . . . . . . . . . . 2.3.3. Ein RESTful Dienst auf dem HTTP-Protokoll . . . . . 2.3.4. Bedingungen, die ein RESTful Dienst erfllen muss . . 2.4. CMake . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2.4.1. berblick . . . . . . . . . . . . . . . . . . . . . . . . . . . 2.4.2. Die Entstehung von CMake . . . . . . . . . . . . . . . . 2.4.3. Die Charakteristika von CMake . . . . . . . . . . . . . . 2.4.4. Der Aufbau eines CMake Projektes . . . . . . . . . . . 1 4 4 4 5 5 6 7 10 10 12 12 19 19 20 21 21 25 25 26 26 30

ii

Inhaltsverzeichnis 3. Lsungsanstze 3.1. Fremdbibliotheken zur Realisierung komplexer Funktionalitt 3.2. Alternative Erstellungssysteme . . . . . . . . . . . . . . . . . . 4. Beschreibung der Lsung 4.1. Voraussetzungen, welche libspirit erfllen soll . . . . . . . . . . 4.2. berlegungen zur Optimierung fr den Endanwender . . . . . 4.2.1. Handle System . . . . . . . . . . . . . . . . . . . . . . . . 4.2.2. System zur Verwaltung von Einstellungen . . . . . . . 4.2.3. System zur Verwaltung von Fehlern . . . . . . . . . . . 4.2.4. Benennungskonventionen . . . . . . . . . . . . . . . . . . 4.3. Implementierung von libspirit . . . . . . . . . . . . . . . . . . . 4.3.1. Verwendete Entwicklungssoftware . . . . . . . . . . . . 4.3.2. Verwendete Fremdbibliotheken . . . . . . . . . . . . . . 4.3.3. Erstellen des Projektes . . . . . . . . . . . . . . . . . . . 4.3.4. Integration von YAJL und libcurl . . . . . . . . . . . . 4.3.5. Implementierung von Testfunktionen und einer Testapplikation . . . . . . . . . . . . . . . . . . . . . . . . . . . 4.3.6. Implementierung des Handle-, Einstellungs- und Fehlersystems . . . . . . . . . . . . . . . . . . . . . . . . . . . 4.3.7. Implementierung der Programmlogik . . . . . . . . . . 4.3.8. Die aktualisierte Testapplikation . . . . . . . . . . . . . 4.3.9. Installation auf verbreiteten Betriebssystemen . . . . . 5. Zusammenfassung und Ausblick Eidesstattliche Erklrung Abbildungsverzeichnis Tabellenverzeichnis Quelltextverzeichnis Glossar 31 31 32 35 35 36 37 37 38 38 40 40 41 42 46 48 49 55 57 58 61 63 64 65 66 68

iii

Inhaltsverzeichnis Literaturverzeichnis Anhang 71 74

iv

1.

Einleitung

Diese Arbeit ist ein Teil des Projektes spirit@fhs. Im Rahmen des Projektes spirit@fhs wird das Stundenplan- und Informationssystem der Fakultt Informatik an der Fachhochschule Schmalkalden von Grund auf erneuert und erweitert. Ein Groteil der Teilprojekte wird dabei von Studierenden des Fachbereichs Informatik realisiert. Den Studierenden wird die Mglichkeit geboten, Praktikums-, Bachelor- und Masterarbeiten mit den fr das Projekt spirit@fhs relevanten Aufgaben zu verknpfen. Die Aufgabe des Stundenplan- und Informationssystems der Fakultt Informatik ist es, die Studierenden, auf eine der aktuellen Zeit angemessene Art und Weise, ber ihren Stundenplan und aktuelle Neuigkeiten (z. B. den Ausfall einer Vorlesung) zu informieren. Der aktuellen Zeit angemessen bedeutet in diesem Fall, dass die Studierenden nicht mehr die Internetseite im Browser berprfen mssen, um auf dem aktuellen Stand zu sein. Stattdessen wird die Mglichkeit geboten, RSS-Feeds in ihre Newsreader einzubinden oder eine App fr ihr Smartphone zu verwenden. Die vorliegende Arbeit beschftigt sich mit der Entwicklung einer portablen, dynamischen Programmbibliothek zum Zugri auf den REST-Dienst des spirit@fhs Projektes in der Programmiersprache C (ANSI Standard). Diese Programmbibliothek soll interessierten Studierenden im Rahmen der Vorlesung Prozedurale Programmierung die Mglichkeit geben, ohne Vorkenntnisse auf den REST-Dienst zuzugreifen. Durch den gekapselten und folglich einfachen Zugri knnen schnell Programme geschrieben werden, welche aktuelle und relevante Daten ausgeben. Die Mglichkeit, Programme mit mehr Funktionalitt als Hallo Welt!-Ausgaben zu schreiben, soll das In1

1. Einleitung teresse der Studierenden wecken, selbst mehr zu programmieren und dadurch ihre Kenntnisse der Vorlesung Prozedurale Programmierung zu festigen. Besonderer Wert wurde auf die Portabilitt der Programmbibliothek gelegt. Die Studierenden verwenden verschiedene Betriebssysteme in unterschiedlichen Versionen (z. B. Windows XP, Windows 7 x64, Mac OS X oder diverse Linux-Distributionen). Dennoch soll es allen mglich sein, auf ihrem Endsystem zu arbeiten und zu programmieren. Das erfordert den Einsatz einer universellen Programmiersprache wie ANSI C und Werkzeugen, welche den Erstellungsvorgang automatisieren, um ihn den Gegebenheiten des Zielsystems (z. B. des Compiler-Typs) anzupassen. Da die vorliegende Arbeit nur ein Teil des spirit@fhs Projektes ist, sollen die anderen Teilprojekte ebenfalls genannt werden: Core: Entwicklung eines Systems zur Erzeugung der Stundenplne sowie einer Domain Specic Language zur Beschreibung der Stundenplandaten News: Erstellen einer Informationsplattform fr Studierende mit aktuellen Neuigkeiten ber Veranstaltungen und Referatsthemen StudWeb: eine neue entwickelte Informationsplattform fr Studierende, basierend auf PHP/Zend EmployeeWeb: eine Plattform fr Dozentinnen und Dozenten, um Informationen wie Neuigkeiten oder Referatsthemen an Studierende weiterzuleiten, indem diese in spirit@fhs eingestellt werden PlanningWeb: eine Webapplikation, mit der fr die Stundenplanerstellung relevanten Daten in spirit@fhs eingetragen werden knnen Data: Untersuchung und Implementierung der Datenhaltung des spirit@fhs Projektes Mobile: Erstellung von Apps fr die gngigen mobilen Betriebssysteme Android, Windows Phone 7 und iOS DistributedCalc: eine Lastenverteilung der Berechnung des Stundenplans auf mehrere Systeme 2

1. Einleitung Migrate: Entwicklung von temporren Schnittstellen, um vom alten auf das neue Informationssystem zu migrieren

2.

Grundlagen

In dem folgenden Kapitel werden die im Rahmen dieser Arbeit vorausgesetzten Grundlagen erlutert. Darunter zhlen die Programmiersprache C, Methoden zur Erstellung portabler Programmbibliotheken, der REST-Dienst und das Erstellungssystem CMake.

2.1.

ANSI C

Zur Implementierung der portablen, dynamischen Programmbibliothek libspirit soll ANSI konformes C als Programmiersprache verwendet werden.

2.1.1.

berblick

Die Programmiersprache C wurde 1972 von Dennis Ritchie entwickelt. C sollte zur Systemprogrammierung auf dem Betriebssystem Unix dienen. Die Programmiersprache C ist aktuell stark verbreitet und wird zur Erstellung von Anwendungsprogrammen bis hin zu Betriebssystem-APIs genutzt. Ein C-Compiler lsst sich durch die geringe Menge an Schlsselwrtern schneller erstellen als ein Compiler umfangreicherer Programmiersprachen. Die Grundfunktionen von C sind sehr beschrnkt, denn alle erweiterten Funktionen, wie Ein- und Ausgabe oder Dateizugrie, werden ber die Standardbibliotheken geliefert. Die Standardbibliothek ermglicht eine portable Programmierung, wobei das Programm nur fr das Endsystem neu kompiliert werden muss.

2.1. ANSI C

2.1.2.

Die Standards von C

Im Jahr 1982 verdeutlichte sich die Notwendigkeit einer Standardisierung der Programmiersprache C. Die Programmiersprache, wie sie Kernighan und Ritchie deniert haben, wurde mit unterschiedlichen Vernderungen von verschiedenen Compilern verwendet. Als die Programmiersprache C auch in Projekten aus staatlichen Auftrgen verwendet wurde, zeigte sich der Stellenwert einer Standardisierung. Daraus folgte 1983 die Grndung des X3J11-Kommitees durch ANSI und 1989 der ANSI Standard fr die Programmiersprache C welcher als C89 bezeichnet wird.1 Der internationale Standard folgte 1990 durch die ISO mit geringfgigen nderungen gegenber C89. Dieser Standard wird als C90 bezeichnet.2 Der ANSI C Standard ist zurzeit der am weitesten verbreitete C-Dialekt und die populrste kompilierte Programmiersprache der Welt.3 Neuere Standards sind die spter verentlichten C95, C99 oder der sich aktuell in Bearbeitung bendende C1X.4 Diese Standards erweitern die Programmiersprache C hauptschlich um Konstrukte wie z. B. komplexe Zahlen und geraten durch die daraus resultierende Ausdehnung der sonst schlanken Programmiersprache C (was einer der Hauptgrnde fr die Verwendung zur Mikrocontrollerprogrammierung ist) oft in Verruf.5 In der vorliegenden Arbeit wird ausschlielich der ANSI C Standard (C89/C90) verwendet, weshalb auf die aktuelleren Standards an dieser Stelle nicht weiter eingegangen wird.

2.1.3.

Die Standardbibliothek

Die C-Standardbibliothek besteht aus mehreren Header-Dateien. Die in Tabelle 2.1 aufgezhlten Header-Dateien sind als Bestandteil des ANSI C Standards deklariert. Diese Header-Dateien liefern Funktionen und Makros, die nicht
1 2

Vgl. Vgl. 3 Vgl. 4 Vgl. 5 Vgl.

[Rit93] [ISO03, S. 1] [Hoo05, S. 119] [ISO03, S. 1] [Sch05, Vorwort]

2.1. ANSI C Header Dateinamen <assert.h> <float.h> <ctype.h> <limits.h> <errno.h> <locale.h> <math.h> <stdarg.h> <stdlib.h> <setjmp.h> <stddef.h> <string.h> <signal.h> <stdio.h> <time.h>

Tabelle 2.1: Standard Header von ANSI C, Quelle: [Ker88]

Teil der Programmiersprache C sind, aber in jeder Umgebung, die standardisiertes ANSI C untersttzt, vorhanden sein mssen.6 Dies bietet ein hohes Ma an Portabilitt. Die Funktionen der Standardbibliothek sind einheitlich deklariert, aber betriebssystemspezisch implementiert. Als Beispiel sei die Funktion fopen() der Header-Datei <stdio.h> genannt, welche eine Datei zum Lesen oder Schreiben net. Deklariert ist diese Funktion in ANSI C immer durch den Prototyp:
FILE *fopen(const char *filename, const char *mode);

Die Implementierung dieser Funktion ist allerdings in Abhngigkeit von Compiler und Betriebssystem unterschiedlich. So kann die Funktion fopen() unter Windows die WINAPI-Funktion CreateFile() nutzen, um den Dateizugri zu gewhren und unter Linux mit Hilfe der Funktion open() implementiert sein. Nach auen wirkt der durch die C-Standardbibliothek gekapselte Dateizugri immer gleich und ein Programm kann direkt ohne nderungen auf einem anderen Betriebsystem mit ANSI C Untersttzung kompiliert werden.7

2.1.4.

Modularisierung in C

Die Modularisierung erfolgt in C ausschlielich ber Dateien. Header-Dateien spielen dabei eine wesentliche Rolle. Jede C-Quelltextdatei wird einzeln bersetzt. Sofern kein Prototyp einer aufzurufenden Funktion angegeben wurde, muss die aufzurufende Funktion vor der aufrufenden implementiert worden sein. Wurde vorher ein Prototyp angegeben, entfllt die Notwendigkeit der Beachtung der Reihenfolge. D. h. in diesem konkreten Fall, dass eine zu
6 7

Vgl. [Ker88, S. 198] Vgl. [Hoo05, S. 195]

2.1. ANSI C bersetzende C-Quelltextdatei an einer Stelle X nur die Funktionen kennt, welche vor X (im Quelltext) implementiert wurden oder deren Prototyp vor X (im Quelltext) deniert wurde. Projiziert man diese Tatsache auf die Mglichkeit, Prototypen in HeaderDateien zu denieren, zeigt sich die Modularisierung in C: man deniert in einer Header-Datei die Prototypen derjenigen Funktionen, welche anderen Quelltextdateien zur Verfgung stehen sollen.8 Wenn andere Quelltextdateien diese Header-Datei per #include einfgen, knnen die darin prototypisch denierten Funktionen in der Quelltextdatei A verwendet werden, obwohl sie in einer anderen Quelltextdatei B implementiert wurden. Funktionen der Quelltextdatei B, deren Prototyp nicht in der Header-Datei deniert wurden, sind in der Quelltextdatei A unsichtbar und deren Verwendung wrde zu Warnungen oder Fehlern whrend der bersetzung fhren. Das Modularisierungsprinzip ber Header-Dateien spielt auch fr die unter 2.2. beschriebene Entwicklung von Programmbibliotheken mit C eine groe Rolle.

2.1.5.

Erzeugung eines Programmes

Als Erzeugen bezeichnet man das bersetzen von Quelltext einer Programmiersprache in ausfhrbaren Code. Aktuelle Compiler bestehen aus mehreren Phasen, welche nacheinander ablaufen. Jede dieser Phasen arbeitet mit einer eigenen abstrakten Programmiersprache.9 Es gibt verschiedene Mglichkeiten, ein Programm durch Ausfhren oder Erzeugen auf einem System lauhig zu machen. Man unterscheidet zwischen Interpretation, just in time (JIT) bersetzung und ahead of time (AOT) bersetzung. Um Quelltext direkt auszufhren, ist die Interpretation des Quelltextes zur Laufzeit besonders bei Skriptsprachen eine oft verwendete Methode. Die dadurch ausgefhrten Programme sind portabel, sofern ein Interpreter der Programmiersprache auf dem Ausfhrungssystem vorhanden ist. Das
8 9

Vgl. [Ker88, S. 69 70] Vgl. [App98, S. 3]

2.1. ANSI C Hauptproblem interpretierter Programme ist, dass die Performanz unter der Tatsache leidet, dass keinerlei Codeoptimierungen mglich sind. Der Grund hierfr ist, dass kein Zwischencode, wie Bytecode oder nativer Maschinencode, erzeugt, sondern der Quelltext nur ausgefhrt wird. Zur Steigerung der Performanz durch mgliche Optimierungen prolierte sich die Methode der just in time bersetzung. Der just in time Compiler setzt darauf, dass das Programm zuerst in plattformunabhngigen Bytecode bersetzt wird. Dieser Bytecode wird zur Laufzeit in nativen Maschinencode bersetzt und ausgefhrt (z. B. von einer Virtuellen Maschine wie bei Java). Die Methode der just in time bersetzung ist eine einfache Mglichkeit, Programme plattformunabhngig zu erstellen. Die vorliegende Arbeit soll eine portable Programmbibliothek zur Untersttzung der Vorlesung Prozedurale Programmierung liefern. Da im Rahmen der Vorlesung Prozedurale Programmierung die Programmiersprachen C oder C++ verwendet werden, entfllt die Mglichkeit, die Portabilitt ber Interpretation oder just in time bersetzung zu realisieren. Das in Abbildung 2.1 beschriebene Modell entspricht einer ahead of time bersetzung, wie sie bei C durchgefhrt wird. Dabei wird der Quelltext zur bersetzungszeit in nativen Maschinencode bersetzt, welcher nicht mehr portabel ist. Die Portabilitt muss also durch portablen Quelltext erreicht werden, welcher fr mehrere Compiler und Betriebssysteme bersetzbar ist.10 Der Prprozessor Als Teil des Erzeugungsprozesses ist der Prprozessor ein mchtiges Werkzeug. Der Prprozessor stellt einen vor dem eigentlichen bersetztungsprozess aufgerufenen Textersetzer dar. Neben dem Entfernen von Kommentaren erstellt er aus der Quelldatei und allen per #include eingefgten HeaderDateien eine groe Codedatei, welche dann an den Compiler weitergegeben wird. Ein wichtiger Bestandteil sind dabei die bedingten Prprozessor-Direktiven. Diese knnen dafr genutzt werden, eine Header-Datei bei mehrfacher
10

Vgl. [Val00, S. 150 151]

2.1. ANSI C

Abbildung 2.1: Erzeugung eines lauhigen Programms, Quelle: [Nad08]

#include-Anweisung nur einmal einzufgen oder Code je nach denierter Betriebssystemvariable auszutauschen, um das Programm portabel zu machen.11 Einige Prprozessor-Makros werden durch den Compiler deniert. So gibt es Makros, welche deniert sind, wenn der Compiler gerade unter dem Betriebssystem luft (z. B. __unix__ und _WIN32). Als Beispiel sei der folgende Quelltext 2.1 angebracht, wo vom Prprozessor in Abhngigkeit des Betriebssystems entweder die Zeile 7 oder die Zeile 9 eingesetzt wird.
1 2 3 4 5 6 7 8 9 10 11

#include <stdio.h> #include <stdlib.h> int main(void) { int x = -1; #ifdef __unix__ x = unix_function_to_get_x(); #elif defined _WIN32 x = windows_function_to_get_x(); #endif

11

Vgl. [Ker88, S. 74 77]

2.2. Programmbibliotheken
12 13

return EXIT_SUCCESS; } Quelltext 2.1: Beispiel der Prprozessorersetzung

2.2.

Programmbibliotheken

Eine Programmbibliothek ist eine nicht eigenstndig lauhige Sammlung von Funktionen. Sie knnen als eine Art Hilfsmodule betrachtet werden, mit deren Hilfe die Funktionalitt eines Programmes erweitert werden kann.

2.2.1.

Die Programmbibliothekstypen

In diesem Abschnitt werden die mglichen Typen einer Programmbibliothek beschrieben. Im weiteren Verlauf werden nur noch dynamische Bibliotheken betrachtet, da das Ziel dieser Arbeit die Erstellung einer dynamischen Programmbibliothek ist. Quelltextbibliothek Ein bereits genanntes Beispiel einer Quelltextbibliothek stellt die C-Standardbibliothek dar. Quelltextbibliotheken enthalten Funktionen und Denitionen, welche als unkompilierter Quelltext vorliegen und bei Bedarf verwendet werden knnen (sie werden mit kompiliert). Statische Bibliothek Eine statische Bibliothek hnelt einer Quelltextbibliothek. Der Unterschied gegenber der Quelltextbibliothek ist, dass die statische Bibliothek als vorab kompilierter Code vorliegt. Wird ein Programm erzeugt, so wird dem Linker mitgeteilt, dass er das zu erzeugende Programm an eine statische Programmbibliothek binden soll. Der Linker fgt daraufhin die aus der statischen Programmbibliothek referenzierten Komponenten dem zu erzeugenden Programm hinzu. Die Dateigre des zu erzeugenden Programmes steigt dadurch an. 10

2.2. Programmbibliotheken Dynamische Bibliothek Als immer mehr Programme die gleichen statischen Programmbibliotheken verwendeten, zeigte sich, dass deren Code redundant in jedes einzelne Programm kopiert wurde. Deshalb wurde nach einem Ansatz gesucht, diese Bibliotheken zu teilen. Daraus entwickelten sich shared objects unter Unix oder Linux, import oder shared libraries unter Mac OS, shared libraries unter AmigaDOS, dynamic link libraries (DLLs) unter Windows und OS/2 und frameworks unter Mac OS X.12 Weitere Vorteile dynamischer Programmbibliotheken sind z. B. die Mglichkeit zur Erstellung von Plug-ins oder das theoretisch einfache Beheben von Fehlern, indem man nur die betreende Programmbibliothek erneuert. Dadurch muss nicht das gesamte Programm neu erzeugt werden.13 Bei der Verwendung einer dynamischen Programmbibliothek wird zwischen dynamic linking und dynamic loading unterschieden. Als dynamic linking bezeichnet man das Binden von verwendeten Referenzen der Programmbibliothek an das zu erzeugende Programm durch den Linker. Es kann entweder an eine Import-Bibliothek (diese enthlt die ntigen Referenzen, so dass der Link-Vorgang nicht aufgehalten wird) oder an die Programmbibliothek selbst gebunden werden. Dieser Vorgang hnelt dem Umgang mit statischen Programmbibliotheken. Wird nun das erzeugte Programm durch das Betriebssystem ausgefhrt, berprft das Betriebssystem zuerst, welche externen Referenzen geladen werden mssen. Im nchsten Schritt ldt das Betriebssystem alle ntigen dynamischen Bibliotheken (wenn sie nicht bereits geladen sind) und erstellt eine Sprungtabelle, so dass die Aufrufe des Programmes richtig ausgefhrt werden. Sollte eine dynamische Bibliothek nicht gefunden werden, meldet das Betriebssystem einen Fehler beim Programmstart.14 Dynamic loading hnelt dem dynamic linking, auer dass das zu erzeugende Programm nicht an die Programmbibliothek gebunden wird. Bei dynamic
12 13

Vgl. [Hoo05, S. 175] Vgl. [Hoo05, S. 176] 14 Vgl. [Hoo05, S. 176]

11

2.2. Programmbibliotheken loading wird die Programmbibliothek zur Laufzeit geladen und Referenzen manuell gebunden. Der grte Vorteil von dynamic loading ist, dass das erzeugte Programm selbst dann luft, wenn die Programmbibliothek nicht vorhanden ist. Das dynamic loading ist die Grundlage von Plug-in Systemen.15

2.2.2.

Struktur einer dynamischen Programmbibliothek

Eine dynamische Programmbibliothek besteht aus zwei Teilen: dem Interface und der Implementierung. Diese Unterteilung beschreibt eine einfache Kapselung. Das Interface besteht aus Funktionen und Deklarationen, auf die der Nutzer der Programmbibliothek Zugri haben soll. Das Interface wird ber Interface-Header-Dateien realisiert. Die Implementierung sind Funktionen und Deklarationen, die fr das Funktionieren der Programmbibliothek zwar notwendig sind, fr den Nutzer jedoch keine Relevanz haben und deshalb versteckt sein sollten.16 Das folgende Zitat beschreibt, wie die Balance zwischen Interface und Implementierung gehalten werden sollte: The trick, when designing a library, is to make the interface rich enough to be useful without exposing so much of the guts that the user is overwhelmed.17

2.2.3.

Richtlinien fr portable, dynamische Programmbibliotheken

Unter diesem Punkt werden Richtlinien vorgestellt, welche sich als hilfreich fr die Entwicklung von plattformunabhngigen Programmbibliotheken und Programmen herausgestellt haben. Es wurde versucht, alle genannten Richtlinien in der im Rahmen dieser Arbeit entwickelten Programmbibliotheken libspirit umzusetzen.
15 16

Vgl. [Hoo05, S. 176] Vgl. [Mic01, S. 4] 17 [Mic01, S. 4]

12

2.2. Programmbibliotheken Wahl der Programmiersprache Um grtmgliche Kompatibilitt zu erreichen, empehlt es sich, den ANSI Standard von C zu halten und proprietre Erweiterungen der Programmiersprache zu meiden. ANSI C ist die am meisten verbreitete Programmiersprache und deshalb ist die Wahrscheinlichkeit des Vorhandenseins eines ANSI C Compilers auf einem speziellen Betriebssystem hoch.18 Kompatibilitt mit C++ Die Programmiersprachen C und C++ sind verwandt, denn C++ gilt als eine evolutionre Entwicklung der Programmiersprache C.19 Der Compiler beider Programmiersprachen fhrt einen Prozess durch, den man als name decoration bezeichnet. Dabei werden Funktionsnamen in interne, fr den Kompilierungsvorgang bestimmte, Funktionsnamen umgewandelt. Als Beispiel20 sei die folgende Funktionsdeklaration genannt:
float f(int a, char b);

Der C++ Compiler wrde diese Funktion intern zu _f_int_char umbenennen, um Funktionsberladung zu untersttzen. Die Programmiersprache C hingegen untersttzt keine Funktionsberladung. Deshalb wird dies bei der Benamung der Funktion nicht bercksichtigt und der C Compiler benennt die Funktion _f. Die Folge der unterschiedlichen Namensgebung durch die Compiler wren Linkerfehler. Der Linker wre nicht in der Lage, die C++ Funktionsaufrufe von f() aufzulsen. Abhilfe verschat das alternative linkage Verfahren von C++ ber das Schlsselwort extern. Umschliet man die Funktionsdeklarationen mit einem extern "C" { }-Block, teilt man dem C++ Compiler mit, bei diesen Funktionen auf die C++ name decoration zu verzichten.21 Im Fall einer portablen dynamischen Programmbibliothek muss in den Interface-Header-Dateien eine mgliche Kompilierung durch C++ bedacht
18 19

Vgl. [Mic01, S. 14] Vgl. [Bre07, S. 19] 20 [Eck00, S. 465] 21 Vgl. [Eck00, S. 465 466]

13

2.2. Programmbibliotheken werden.22 Das selbststndige Aktivieren von aktive linkage kann folgendermaen ber den Prprozessor realisiert werden:
#ifdef __cplusplus extern "C" { #endif /* Funktionsdeklarationen */ float f(int a, char b); #ifdef __cplusplus } #endif Quelltext 2.2: Selbststndiges Aktivieren von active linkage

Codestil bei Prprozessorersetzungen bersichtlicher Code ist eine wichtige Voraussetzung, um auf mgliche Plattformabhngigkeiten aufmerksam zu werden, bevor Fehler auftreten. Wie im Quelltext 2.2 zu sehen, kann die Verwendung von Prprozessorersetzungen zu einem unbersichtlichen und aufgeblhten Quelltext fhren. Deshalb wird bei einer Funktion, welche viele betriebssystemabhngige Aufrufe hat, geraten, diese Funktion besser fr jedes Betriebssystem seperat zu implementieren und per Prprozessor nur zwischen den Implementierungen zu wechseln. Dadurch werden die Prprozessorbefehle auf ein Minimum reduziert und die bersichtlichkeit des Quelltextes wird gesteigert.23 Pfadangaben im Quelltext Die unterschiedlichen Betriebssysteme haben auch ein unterschiedliches Verhalten im Bezug auf Pfadangaben. Zum einen sind manche Betriebssysteme case sensitive bei der Pfadangabe und zum anderen ist der Pfadseparator abhngig vom Betriebssystem. Deshalb sollte mglichst auf Pfadangaben verzichtet werden.
22 23

Vgl. [Mic01, S. 18] Vgl. [Hoo05, S. 115 116]

14

2.2. Programmbibliotheken Da sich aber nicht alle Pfadangaben vermeiden lassen (z. B. #includeAnweisungen), wird angeraten, zumindest auf absolute Pfadangaben zu verzichten, sofern diese nicht garantiert auf allen Betriebssystemen verfgbar sind. Stattdessen sollten relative Pfadangaben genutzt werden. Als Pfadseparator sollte immer ein Schrgstrich (/) verwendet werden, denn der Backslash (\) ist laut C Standard unzulssig in #include-Anweisungen. Aufgrund dieser Tatsache wird der Schrgstrich von den meisten Compilern akzeptiert.24 Mgliche Typabweichungen Besondere Vorsicht ist geboten, wenn mit Sprachkonstrukten gearbeitet wird, deren elementarer Datentyp durch den Compiler bestimmt wird. Ein Beispiel hierfr ist das Aufzhlungskonstrukt enum. Der C Standard legt zwar fest, dass die Aufzhlungswerte einen ganzzahligen Typ haben, allerdings nicht dessen Gre. So knnte die Aufzhlung enum E { a, b, c }; bei manchen Compilern durch einen 8 Bit char und bei anderen Compilern durch einen 32 Bit int reprsentiert werden. Wird nun eine Struktur (struct) deniert, welche eine oder mehrere Aufzhlungen von Typ E enthlt, ist die mit sizeof() bestimmte Gre dieser Struktur unterschiedlich (in Abhngigkeit vom System).25 Es sind verschiedene Folgen der unterschiedlichen Typgre denkbar. Eine Mglichkeit wre, dass binr gespeicherte Dateien des einen Systems auf einem anderen System nicht fehlerfrei gelesen werden knnen. Speicherallokation Bekanntlich muss Speicher, welcher per z. B. malloc() reserviert wurde, auch wieder manuell freigegeben werden. Gibt eine Funktion der dynamischen Programmbibliothek eine Referenz auf den in der Funktion reservierten Speicher zurck, knnen Probleme bei der Speicherfreigabe auftreten. Bei einigen Betriebssystemen (z. B. Windows) reservieren dynamische Programmbibliotheken und deren aufrufende Programme ihren Speicher in verschiedenen
24 25

Vgl. [Hoo05, S. 113 114] Vgl. [Hoo05, S. 131 132]

15

2.2. Programmbibliotheken Speicherbereichen. Da es blicherweise verboten ist, Speicher in fremden Speicherbereichen freizugeben, muss die Programmbibliothek eine eigene Funktion zum Freigeben des von ihr reservierten Speichers anbieten. Ein empfohlener Lsungsweg ist, so wenig Speicher wie mglich in der dynamischen Programmbibliothek zu reservieren. Da es sich aber meist nicht vermeiden lsst, sollte man statt der Rckgabe von Referenzen auf den in der dynamischen Programmbibliothek reservierten Speicher den Aufrufer Speicher reservieren und diesen beim Funktionsaufruf an die Funktion weitergeben lassen.26 Threadsicherheit Die Anzahl der Kerne moderner Prozessoren steigt immer weiter an. Die Folge ist, dass Programme ihre Aufgaben auf mehrere Prozessoren aufteilen sollten, z. B. mit Kernelthreads. Auch wenn der Entwurf der dynamischen Programmbibliothek nicht direkt auf eine Verwendung in Verbindung mit Threads abzielt, sollte diese Mglichkeit immer bedacht werden.27 Gibt es im Quelltext Bereiche, die mit Ressourcen arbeiten, welche auch andere Threads des Prozesses ndern knnen, muss deren Verwendung durch den Einsatz von beispielsweise Semaphoren gesichert werden, um Seiteneekte zu vermeiden. Dies kann wieder zu Portabilittsproblemen fhren, da Threads und Semaphore nicht durch die C Standardbibliothek vereinheitlicht werden. Ein Beispiel fr die mgliche Gefhrdung der Threadsicherheit durch die Rckgabe von kumulierten Datentypen (z. B. struct oder union) wird nun anhand von Quelltext 2.3 besprochen.
struct thingy get_a_thingy_by_value( void ) { struct thingy result; /* do some stuff */ return result; /* how is this returned? }

Implementation depended */

26 27

Vgl. [Mic01, S. 15] Vgl. [Mic01, S. 12]

16

2.2. Programmbibliotheken
void get_a_thingy_by_copy( struct thingy *t ) { struct thingy s; /* do some stuff */ *t = s; } Quelltext 2.3: Thread Sicherheit bei Rckgabewerten, Quelle: [Hoo05]

Das Problem besteht in der uneinheitlichen Umsetzung der Rckgabe durch verschiedene Compiler. Manche Compiler geben einen versteckten Zeiger auf eine versteckte Stackvariable zurck. Andere Compiler geben die Adresse einer statischen Variable zurck. An dieser Stelle ist die Threadsicherheit nicht gegeben, denn die Variable knnte durch andere Threads modiziert werden.28 Eine mgliche Lsung des Problems wurde im Quelltext 2.3 bereits angegeben: der Aufrufer reicht einen Zeiger auf eine lokale Variable als Ergebnisspeicher weiter und ist somit selbst fr die Threadsicherheit zustndig. Beachtung der Bytereihenfolge Datentypen, welche mehrere Bytes gro sind, knnen grundstzlich in zwei verschiedenen Darstellungen reprsentiert werden: Little Endian und Big Endian29 . Das mgliche Problem mit der Bytereihenfolge entsteht dadurch, dass die C Standardbibliothek in Funktionen wie z. B. fwrite() und fread() immer die native Bytereihenfolge des aktuellen Prozessors verwendet. Das bedeutet, dass Daten, welche von der dynamischen Programmbibliothek auf einem Big Endian System binr geschrieben wurden, auf einem Little Endian System nicht mehr korrekt lesbar sind, denn die Bytereihenfolge ist invertiert.30 Eine mgliche Lsung wre, die Daten im Textformat zu speichern. Dies knnte z. B. durch Serialisierung und Deserialisierung via JSON geschehen.
Vgl. [Hoo05, S. 137] Bei Little Endian ist die niedrigste und bei Big Endian die hchste Adresse das niederwertigste Byte. 30 Vgl. [Hoo05, S. 81 82]
29 28

17

2.2. Programmbibliotheken Sollte es trotzdem ntig sein, die Daten binr zu speichern, muss manuell dafr gesorgt werden, dass dies in der richtigen Reihenfolge geschieht. Die kann zum einen realisiert werden, indem die Daten mit Bitverschiebungsoperationen in einzelne Bytes zerlegt und in einer festgelegten Reihenfolge (z. B. Little Endian) in die Datei geschrieben werden. Andererseits gibt es die Mglichkeit, mehrere Bytereihenfolgen zu untersttzen. Bei dieser Mglichkeit, welche z. B. bei dem Format TIFF angewendet wird, wird in den Datei-Headerinformationen erwhnt, welche Bytereihenfolge vorliegt. Der Leser der Datei muss selbst dafr sorgen, die Bytereihenfolge seiner evtl. anderen Prozessorarchitektur anzupassen.31 Fehlerbehandlung Es ist wichtig, dass die dynamische Programmbibliothek so robust wie mglich programmiert wird. Dies wird erreicht, indem jeder Rckgabewert der in der dynamischen Programmbibliothek aufgerufenen Funktionen berprft wird. Im Fehlerfall sollte versucht werden, das Problem zu beheben. Ist das nicht mglich, sollte ein aussagekrftiger Fehlercode zurckgegeben werden, anstatt das Programm mit exit(); zu terminieren. Das impliziert, dass ein Fehlercode-System fr die dynamische Programmbibliothek implementiert werden sollte. Namensgebung Die Namensgebung whrend der Entwicklung einer portablen dynamischen Programmbibliothek kann sich erheblich auf ihre Kompatibilitt zu anderen Systemen und Programmen auswirken. Die Namen der Interface-Funktionen knnen zu Namenskonikten in Programmen fhren, welche die dynamische Programmbibliothek verwenden. Deshalb sollte ein kurzes und prgnantes Prx fr alle Interface-Funktionen der dynamischen Programmbibliothek verwendet werden. Die C Standardbiblio31

Vgl. [Hoo05, S. 82 84]

18

2.3. REST thek verwendet auch Prxe, z. B. str fr Stringoperationen wie strlen() oder strcpy().32 Auch bei der Namensgebung der Quelltext-Dateien einer dynamischen Programmbibliothek sollte darauf geachtet werden, dass der Dateiname nicht lnger als 31 Zeichen ist. Das ist die maximale Dateinamenlnge unter Mac OS. Weiterhin sollte beachtet werden, dass einige Betriebssysteme (z. B. Unix) case sensitive sind. Deshalb sollte eine einheitliche Gro- und Kleinschreibung fr die Datei- und Ordnernamen gewhlt werden.33

2.3.

REST

Representational State Transfer (REST) ist eine von Roy Fielding im Rahmen einer Dissertation entwickelte Softwarearchitektur.34 Diese Softwarearchitektur soll darauf abzielen, dass Dienste auf verteilten Hypermedia-Systemen wie dem World Wide Web laufen knnen, ohne dass eine berdimensionierte Dienstarchitektur aufgebaut werden muss.

2.3.1.

berblick

Streng genommen kann jede Web Applikation im Internet als Dienst betrachtet werden. Die Suche einer Seite ber Google35 ist eine formatierte Ausgabe einer Datenbankanfrage. Fr Programmierer und Entwickler stellen Unternehmen wie Google oft Dienste in Form von Web Dienste zur Verfgung. ber diese Web Dienste kann von selbst entwickelten Programmen auf Angebote wie z. B. die Google-Suche zugegrien werden. Der Hauptunterschied zwischen der Web Applikation und dem Web Dienst besteht darin, dass ein Web Dienst die Daten in einer fr den Computer einfach zu lesenden Form ausgibt. Die fr den Menschen bestimmten Ausgaben von Web Applikationen hingegen sind schwer fr einen Computer zu interpretieren.
32 33

Vgl. [Mic01, S. 4] Vgl. [Hoo05, S. 65] 34 Vgl. [Fie00, S. 76] 35 Ein Suchdienst der Firma Google Inc..

19

2.3. REST Das Problem der meisten Web Dienste ist, dass sie nichts mit dem Web zu tun haben. Es handelt sich um schwergewichtige Softwarearchitekturen, welche die meisten Eigenschaften, die das Web so erfolgreich gemacht haben, ignorieren oder neu ernden.36 Genau an dieser Stelle kommt der Gedanke hinter REST zum Tragen. Das Web wurde durch vergleichsweise einfache Technologien wie HTTP, URIs und HTML realisiert. Da der Unterschied zwischen Web Applikationen und Web Diensten nicht gravierend ist, knnen auch Web Dienste ber einfache, mit dem Web verwandte, Technologien realisiert werden37 , ohne eine komplexe, fast abgekapselte, neue Softwarearchitektur zu erstellen.

2.3.2.

RESTful Web Services

Die Dissertation von Roy Fielding beschftigt sich grundlegend mit der Denition und der Bewertung von Designkriterien fr Dienst-Softwarearchitekturen in verteilten Hypermedia-Systemen.38 Die im Rahmen der Dissertation entwickelte Softwarearchitektur REST basiert auf den darin festgelegten Designkriterien. Deshalb hat sich spter der Begri RESTful fr Softwarearchitekturen etabliert, welche nach diesen Designkriterien erstellt wurden. Es gibt keinen Standard fr einen RESTful39 Dienst. Das unterscheidet REST von einem Protokoll. Am Beispiel von REST wurde beschrieben, wie ein zu entwickelnder Dienst fr das moderne Web entworfen werden sollte.40 Roy Fielding verwendete fr seinen REST-Entwurf das HTTP-Protokoll. Dies bedeutet aber auf keinen Fall die Notwendigkeit von HTTP zur Erstellung eines RESTful Dienstes. Die Designkriterien fr einen RESTful Dienst besagen, dass das Hinzufgen neuer, applikationsspezischer Funktionalitten zu vermeiden und stattdessen der Wortschatz des verwendeten Protokolls zu
Vgl. [Ric07, S. xiii] Vgl. [Ric07, S. xv] 38 Vgl. [Ric07, S. xvi] 39 Ab jetzt wird in der vorliegenden Arbeit das Wort RESTful als Eigenschaftswort benutzt, welches aussagt, dass ein Dienst nach den Bedingungen welche von Roy Fielding in [Fie00] aufgestellt wurden, konzipiert ist. 40 Vgl. [Fie00, S. xvii]
37 36

20

2.3. REST nutzen ist, sofern dieser umfangreich genug ist.41 Der Gedanke hinter einem RESTful Dienst ist demnach, wenn mglich, das bestehende Protokoll zu nutzen.

2.3.3.

Ein RESTful Dienst auf dem HTTP-Protokoll

Im Fall von HTTP ist der Wortschatz des Protokolls umfangreich genug, um alle CRUD-Operationen durchzufhren. HTTP bietet die Methoden GET, POST, PUT und DELETE, welche auch zur Implementierung eines RESTful Dienstes verwendet werden knnen (wie in Tabelle 2.2 angefhrt). Operation Auisten aller Benutzer Erstellen eines Benutzers Anzeigen eines Benutzers ndern eines Benutzers Lschen eines Benutzers HTTP Aktion GET /users POST /users GET /users/52 PUT /users/52 DELETE /users/52

Tabelle 2.2: Verwendung der HTTP Methoden fr REST, Quelle: Eigene Darstellung in Anlehnung an [Ric07, S. 174]

Weiterhin bietet HTTP die eindeutige Identikation von Ressourcen ber URIs und viele Antwort-Codes, was die Implementierung eines RESTful Dienstes erleichtert.

2.3.4.

Bedingungen, die ein RESTful Dienst erfllen muss

Dieser Bereich befasst sich mit den von einem RESTful Dienst zu erfllenden Bedingungen. Dabei werden ausschlielich die ursprnglichen Bedingungen betrachtet, welche Roy Fielding in [Fie00] deniert hat.
41

Vgl. [Fie00, S. 13 15]

21

2.3. REST Client-Server Eine Softwarearchitektur im Stil Client-Server ermglicht die Trennung des Benutzerinferfaces des Clients von der Datenhaltung des Servers. Diese Trennung steigert die Portabilitt der Client-Komponenten und reduziert die Komplexitt des Servers bei gleichzeitiger Steigerung seiner Skalierbarkeit. Einer der wichtigsten Aspekte im Web, welcher durch das Client-Server-Prinzip erfllt wird, ist jedoch die Mglichkeit aller Komponenten, sich unabhngig voneinander zu entwickeln.42 Zustandslosigkeit Die Zustandslosigkeit besagt, dass jede Anfrage eines Clients an den Server alle Daten enthalten muss, die zur Durchfhrung der Anfrage notwendig sind. Dies verbessert die Transparenz, Verlsslichkeit und Skalierbarkeit. Die Transparenz ist verbessert, weil ein berwachungssystem mit der Einsicht einer Anfrage die gesamte Absicht des Clients kennt. Die Softwarearchitektur wird verlsslicher, da Fehler (z. B. Verlust einer Anfrage) einfacher behoben werden knnen (erneutes Senden der Anfrage). Die Skalierbarkeit wird verbessert, da keine Informationen zwischen den Anfragen serverseitig gespeichert werden mssen. Dadurch kann eine einfache Lastenverteilung stattnden, ohne dass die Server clientspezische Daten austauschen mssen. Ein Nachteil der Zustandslosigkeit ist allerdings die erhhte Belastung des Netzwerkes durch die teilweise redundanten Daten.43 Cache Um die Ezienz des Netzwerks zu erhhen, wird ein Cache verwendet. Es ist dabei zwingend erforderlich, in der Antwort auf eine Anfrage implizit oder explizit zu kennzeichnen, ob ein Cache verwendet werden darf. Sollte ein Cache verwendet werden drfen, so steht dem Client frei, die empfangene Antwort fr sptere, quivalente Anfragen zu nutzen.
42 43

Vgl. [Fie00, S. 78] Vgl. [Fie00, S. 78 79]

22

2.3. REST Der daraus resultierende Vorteil ist die potenzielle teilweise oder vollstndige Einsparung von Interaktionen. Dadurch werden Ezienz und Skalierbarkeit gesteigert bei gleichzeitiger Verbesserung der vom Nutzer wahrgenommenen Reaktionszeit. Ein Nachteil ist hingegen die Mglichkeit, dass die Daten des Caches nicht mehr mit den Daten, welche aktuell auf dem Server vorliegen, bereinstimmen. Die Folge ist eine verminderte Verlsslichkeit und die Beeintrchtigung der vorgesehenen Funktionalitt.44 Einheitliches Interface Eine wichtige Eigenschaft eines RESTful Dienstes ist das einheitliche Interface zwischen den Komponenten. Durch die Generalisierung des KomponentenInterfaces wird die Systemarchitektur vereinfacht und die Transparenz der Interaktionen gesteigert. Ein Vorteil ist, dass sich der Dienst unabhngig entwickeln kann und nach auen weiterhin unverndert wirkt. Die Ezienz hingegen wird durch ein einheitliches Interface gesenkt. Die Ursache hierfr liegt in der bertragung von Daten in einem einheitlichen Format, weshalb die Zielapplikation diese oft zustzlich verarbeiten muss.45 Im folgenden Abschnitt werden die in [Fie00] festgelegten Prinzipien zur Erstellung von einheitlichen Interfaces kurz vorgestellt. Identikation von Ressourcen Jede Ressource muss individuell identiziert werden knnen. Das kann z. B. ber URIs realisiert werden. Die Ressource selbst ist dabei unabhngig von der Darstellung fr den Client. D. h. ein RESTful Dienst auf Basis von HTTP knnte dieselbe Ressource (URI) fr Browser benutzerfreundlich darstellen oder in Abhngigkeit des vom Client akzeptierten Inhaltstyps im HTTP-Header fr Maschinen strukturiert ausgeben.46
44 45

Vgl. [Fie00, S. 79 81] Vgl. [Fie00, S. 81 82] 46 Vgl. [Fie00, S. 86 88]

23

2.3. REST Manipulation von Ressourcen durch ihre Darstellung Hat ein Client die Reprsentation einer Ressource, besitzt er alle notwendigen Informationen, um die Ressource auf dem Server zu ndern oder zu lschen (sofern er die Berechtigung dazu besitzt).47 Selbsterklrende Nachrichten Jede Nachricht enthlt gengend Informationen darber, wie sie zu verarbeiten ist. Es knnte beispielsweise ein Internet Media Type (ehem. MIME) angegeben werden.48 Hypermedia zur Zustandsnderung Die Clients ndern ihren Zustand nur ber dynamisch vom Server festgelegte Aktionen innerhalb des Hypermedia. Es gibt einen Einstiegspunkt (z. B. /users) und von dort aus wird nur noch Weiterleitungen (z. B. Hyperlinks) gefolgt, welche vom Server in der empfangenen Darstellung der Ressource angegeben wurden (z. B. /users/52).49

Mehrschichtiges System Ein mehrschichtiges System wirkt sich positiv auf die Skalierbarkeit im Internet aus. Die Schichten bilden eine Hierarchie, bei der fr jede Komponente nur die Schicht sichtbar ist, mit der sie arbeitet. In einem mehrschichtigen System kann ein Client nicht erkennen, ob er direkt mit dem End-Server oder einem Vermittlungsserver verbunden ist. Die Verwendung von Vermittlern kann die Skalierbarkeit durch Lastverteilung oder geteilte Caches verbessern. Nachteilig ist die erhhte Reaktionszeit und der entstehende Overhead.50 Code-On-Demand Hierbei handelt es sich um die einzige optionale Bedingung fr einen RESTful Dienst. Die Client-Funktionalitt kann durch die Ausfhrung von heruntergeladenem Code, in Form von kompilierten Java Applets oder Client-seitig
47 48

Vgl. Vgl. 49 Vgl. 50 Vgl.

[Fie00, [Fie00, [Fie00, [Fie00,

S. S. S. S.

88 90] 90 92] 92 96] 82 84]

24

2.4. CMake ausfhrbaren Scripts (z. B. JavaScript), erweitert werden. Durch die Mglichkeit, spter weitere Funktionalitt herunterzuladen, wird die Erweiterbarkeit des Systems gesteigert. Diese Bedingung ist optional, da die Transparenz des Systems verringert wird.51

2.4.

CMake

CMake ist ein erweiterbares Open Source System, um den Erstellungsprozess unabhngig vom Betriebssystem und dem auf dem Betriebssystem verwendeten Compiler durchzufhren.

2.4.1.

berblick

CMake ist fr alle gngigen Betriebssysteme verfgbar. Der Unterschied zu anderen cross-platform Build Systemen ist, dass CMake den Erstellungsprozess nicht selbst durchfhrt, sondern native Erstellungsumgebungen je nach Betriebssystem und Compiler generiert. In der Abbildung 2.2 ist dieser Prozess dargestellt. Die Grundlage dafr legen Kongurationsdateien mit
CMakeLists.txt CMake Native Build System

Executables / Libraries

Native Build Tools

Abbildung 2.2: Erstellungsprozess unter Verwendung von CMake, Quelle: Eigene Darstellung in Anlehnung an [Eng07]

Namen CMakeLists.txt in jedem Quellenverzeichnis. Diese Kongurationsdateien beschreiben Abhngigkeiten und das Ziel des Erstellungsprozesses. Beim Starten von CMake mit einer Kongurationsdatei wird die native Erstellungsumgebung erzeugt (z. B. UNIX oder Linux Makeles, Windows Visual Studio Projekte/Workspaces, Apple Xcode). Mit der erzeugten nativen
51

Vgl. [Fie00, S. 84 85]

25

2.4. CMake Erstellungsumgebung kann der Erstellungsprozess anschlieend wie gewohnt durchgefhrt werden.52

2.4.2.

Die Entstehung von CMake

CMake wurde entwickelt, da ein cross-platform Build System fr das ITK bentigt wurde. Damals stand kein System zur Verfgung, welches allen Anforderungen gerecht wurde. Beeinusst wurde CMake von einem frheren System namens pcmaker, welches ebenfalls von Kitware Inc., dem Entwickler von CMake, fr ein anderes Projekt entwickelt wurde. Viele Ideen der Basis von pcmaker wurden bernommen und um Funktionalitt erweitert, welche an das Unix Werkzeug congure angelehnt ist. Mitte 2000 gab es erste Implementierungen von CMake und ab 2001 beschleunigte sich die Entwicklung. Viele der in CMake integrierten Verbesserungen haben ihren Ursprung bei anderen Firmen, welche CMake fr ihre Zwecke anpassten.53

2.4.3.

Die Charakteristika von CMake

CMake ist in der Lage, native Erstellungsumgebungen zu erzeugen, welche Programmbibliotheken (dynamisch oder statisch), Wrapper, ausfhrbare Dateien oder eine beliebige Kombination der genannten Komponenten erstellen. Um die Konguration benutzerfreundlicher zu gestalten, wird die Mglichkeit geboten, den Erstellungsprozess ber eine grasche Oberche vorzukongurieren. Ermglicht wird dies durch die sogenannten Cache-Dateien von CMake. Beim Aufruf aus der Konsole und beim Start der graschen Benutzeroberche von CMake wird zunchst die Konguration fr das aktuelle System vorbereitet. Dies beinhaltet die berprfung, ob die gewhlte native Entwicklungsumgebung vorhanden ist (z. B. Compiler Version prfen). Weiterhin werden die optional vom Ersteller des CMake Projektes angeordneten Tests durchgefhrt und alle relevanten Erstellungsoptionen auf die in
52 53

Vgl. [Kit11a, About] Vgl. [Kit11a, The Origins of CMake]

26

2.4. CMake der CMakeLists.txt gesetzten Standardeinstellungen festgelegt. Die Einstellungen der automatischen Konguration werden in einer Datei mit Namen CMakeCache.txt im Erstellungsverzeichnis gesichert, um Kongurationszeit bei der Neuerstellung einzusparen. Diese Dateien enthalten Eintrge im Format: VARIABLENNAME:TYP=WERT Die Standardeinstellungen knnen sowohl ber Kommandozeilenparameter als auch ber die grasche Benutzeroberche verndert werden.54 CMake kann komplexe Verzeichnishierarchien und Abhngigkeiten von mehreren Programmbibliotheken verwalten. Es ist dabei z. B. in der Lage, eine Anwendung zu erstellen, welche von mehreren, ebenfalls zu erstellenden, Programmbibliotheken abhngig ist. CMake erkennt diese Abhngigkeiten und lsst den Erstellungsprozess die Reihenfolge ohne weitere Arbeit fr den Ersteller einhalten. Es ist sogar mglich, dass CMake in einem Erstellungsvorgang eine Anwendung erstellt, diese Anwendung einen Quelltext generieren lsst, welcher wiederum fr eine weitere zu erstellende Anwendung verwendet wird. Diese Funktionen werden u. a. durch custom commands ermglicht. Bei custom commands handelt es sich um benutzerdenierte Befehle, um beispielsweise Dateien zu kopieren. Um dabei betriebssystemunabhngig zu bleiben, werden Standarddateioperationen wie kopieren von CMake angeboten. Pfadunabhngigkeit wird durch CMake-Pfadvariablen wie ${PROJECT_BINARY_DIR} oder ${CURRENT_SOURCE_DIR} gewhrleistet.55 Mit CMake ist es mglich, eigene Tests durchzufhren. Diese Tests knnen verwendet werden, um z. B. das Vorhandensein von bentigten oder optionalen Fremdbibliotheken wie ZLIB oder OpenSSL zu berprfen. Dafr kann die Anweisung:
find_package (<paketname>)
54 55

Vgl. [Eng07, S. 8] Vgl. [Kit11a, About] und [Eng07, S. 3, 25 27]

27

2.4. CMake verwendet werden. Fr hug genutzte Fremdbibliotheken liefert CMake schon vorgefertigte Suchskripte, welche in der Installation enthalten sind (z. B. FindZLIB.cmake). Diese Suchskripte versuchen, die Fremdbibliotheken an fr das aktuelle Betriebssystem spezischen Stellen zu nden und setzen anschlieend CMake Variablen, welche ber den Suchverlauf Auskunft geben. Es ist ebenfalls mglich, selbst ein .cmake Modul zu schreiben und durch dessen Hilfe bentigte Fremdbibliotheken zu nden.56 Eine weitere Testmglichkeit ist die Suche nach Header-Dateien. Auch dafr bietet CMake eine Methode:
check_include_files (<header> <ERGEBNIS_VARIABLE>)

Diese Funktion sucht nach der bei <header> angegebenen Header-Datei und setzt, je nach Status, die CMake Variable <ERGEBNIS_VARIABLE>. An dieser Stelle kann eine weitere ntzliche Funktion von CMake Anwendung nden. CMake ist in der Lage, eine Header-Datei zu generieren, in der benutzerdenierte Prprozessor-Makros deniert sind. ber den Befehl
configure_file (<input> <output>)

wird die Datei <input>, welche eine Rohling ist, mit den gesetzten CMake Variablen gefllt und in die Datei <output> gespeichert.57 Der folgende Abschnitt stellt dieses Verfahren anhand eines Beispiels58 dar.

CHECK_INCLUDE_FILES (malloc.h HAVE_MALLOC_H) CHECK_INCLUDE_FILES ("sys/param.h;sys/mount.h" HAVE_SYS_MOUNT_H) CONFIGURE_FILE (${CMAKE_CURRENT_SOURCE_DIR}/config.h.in ${CMAKE_CURRENT_BINARY_DIR}/config.h) Quelltext 2.4: Auszug aus CMakeLists.txt

Die Anweisungen aus Quelltext 2.4 setzen die CMake Variablen HAVE_MALLOC_H und HAVE_SYS_MOUNT_H, wenn die gesuchten Header-Dateien vorhanden sind. Durch check_include_files wird die Datei cong.h.in
56 57

Vgl. [Kit11b] Vgl. [Kit11d] 58 Quelle: Eigene Darstellung in Anlehnung an [Kit11d]

28

2.4. CMake (Quelltext 2.5) in Abhngigkeit der eben genannten CMake Variablen gebildet und als Datei cong.h gespeichert.
#cmakedefine HAVE_MALLOC_H 1 #cmakedefine HAVE_SYS_MOUNT_H Quelltext 2.5: Der Kongurationsrohling cong.h.in

Dieser in Quelltext 2.5 dargestellte Kongurationsrohling wird von CMake whrend des Kongurationsprozesses in Abhngigkeit der CMake Variablen in eine C Header-Datei umgewandelt. Sind die CMake Variablen gesetzt, entsteht Quelltext 2.6, sind sie nicht gesetzt, entsteht Quelltext 2.7.
#define HAVE_MALLOC_H 1 #define HAVE_SYS_MOUNT_H Quelltext 2.6: Die Kongurationsdatei cong.h bei gesetzten CMake Variablen

/* #undef HAVE_MALLOC_H 1 */ /* #define HAVE_SYS_MOUNT_H */ Quelltext 2.7: Die Kongurationsdatei cong.h bei nicht gesetzten CMake Variablen

CMake bietet die Mglichkeit, eigene Makros zu denieren. Mit Hilfe von Makros knnen hug ausgefhrte Funktionsablufe, wie z. B. Tests, zusammengefasst werden. Weiterhin wird seit Version 2.6.0 Cross-Kompilierung untersttzt. Als Cross-Compilierung bezeichnet man das Kompilieren von Software fr ein anderes System als das, auf dem kompiliert wird. Diese Funktion kann z. B. dafr genutzt werden, um vollautomatisch einmal tglich einen Nightly Build fr ein Projekt zu erzeugen.59
59

Vgl. [Kit11c]

29

2.4. CMake

2.4.4.

Der Aufbau eines CMake Projektes

Ein Projekt, welches mit Hilfe von CMake erstellt wird, besteht aus zwei Verzeichnisbumen: dem Quelltextverzeichnisbaum und dem Binrverzeichnisbaum. Der Quelltextverzeichnisbaum enthlt smtliche CMake Kongurationsdateien (z. B. die Dateien CMakeLists.txt, cong.h.in oder FindZLIB.cmake). Weiterhin sind die Quelltexte des zu erzeugenden Objektes in dem Quelltextverzeichnisbaum enthalten. Im Binrverzeichnisbaum sind sowohl die Ausgaben von CMake als auch die Ausgaben des Erstellungsprozesses enthalten. Dies beinhaltet die Dateien, welche fr das native Erstellungssystem erzeugt wurden (z. B. Makeles), die dynamischen Header-Dateien (z. B. cong.h.in cong.h), welche von CMake erzeugt wurden und die Ausgaben des Erstellungsprozesses selbst (Programmbibliotheken, ausfhrbare Dateien oder jegliche andere erstellbare Datei). CMake untersttzt sowohl die in-source, als auch die out-of-source Erstellung. Bei der in-source Erstellung bendet sich der Binrverzeichnisbaum im Quelltextverzeichnisbaum, bei der out-of-source Erstellung hingegen in einem separaten Verzeichnis.60

60

Vgl. [Eng07, S. 6]

30

3.

Lsungsanstze

Die Mglichkeiten eine portable, dynamische Programmbibliothek zu erstellen, mssen sich auf den Erzeugungsprozess der dynamischen Programmbibliothek selbst beschrnken. Es ist nicht mglich, eine portablere Programmiersprache (z. B. Java mit JIT Kompilierung) zu verwenden, da die vorliegende Arbeit durch die Vorlesung Prozedurale Programmierung auf die Programmiersprache C festgelegt ist. Die Programmiersprache C ist in ihren Grundelementen portabel (siehe 2.1.). Werden jedoch komplexere Funktionen bentigt, ist die Portabilitt gefhrdet. Bei libspirit sind diese komplexen Funktionen die HTTP-, HTTPS und JSON Untersttzung, um Zugri auf den REST-Service des spirit@fhs Projektes zu erhalten.

3.1.

Fremdbibliotheken zur Realisierung komplexer Funktionalitt

Es gibt mehrere Mglichkeiten, komplexe Funktionalitt in ein zu programmierendes Projekt einzubinden. Dies kann beispielsweise dadurch erfolgen, dass die bentigte komplexe Programmlogik selbst implementiert wird, auf einen Dienst zugegrien wird, welcher die bentigte Funktionalitt anbietet oder eine Fremdbibliothek verwendet wird. Es ist nicht immer vorteilhaft, jegliche Programmlogik selbst zu schreiben. Der dabei geschriebene Quelltext erhht das Fehlerrisiko fr das eigentliche 31

3.2. Alternative Erstellungssysteme Projekt. Fr hug verwendete, komplexe Funktionalitten wurden oft schon Fremdbibliotheken entwickelt, welche als Open Source oder vorkompilierte Programmbibliotheken zur Verfgung stehen. Die Verwendung von stabilen Fremdbibliotheken sollte der Neuimplementierung von erfolgreich getestetem Code in jedem Falle vorgezogen werden. Auf einen Dienst zuzugreifen ist in einer schlichten Sprache wie C61 nicht durch Standardbestandteile der Programmiersprache mglich. Folglich steht diese Option nur fr umfangreiche Programmiersprachen wie Java zur Verfgung. In C msste der Dienstzugri selbst implementiert oder durch eine Fremdbibliothek realisiert werden. Fremdbibliotheken zu verwenden ist die beste Alternative im Fall von libspirit. Hug verwendete Programmlogik, wie z. B. der Zugri auf HTTP und andere Internet-Protokolle, wird oft durch die Verwendung von Fremdbibliotheken realisiert. Diese sind durch die zahlreiche Anwendung in verschiedenen Projekten bereits erprobt, was Fehlerrisiken in dem von der Fremdbibliothek bernommenen Bereich minimiert.

3.2.

Alternative Erstellungssysteme

Um den bersetzungsvorgang des C Quelltextes, welcher auf jedem neuen Betriebssystem ntig ist, so zu gestalten, dass dieser fr den Endanwender der dynamischen Programmbibliothek einfach und ohne nderungen ermglicht wird, muss eine Abstraktion des Erstellungsprozesses stattnden. Diese Abstraktion muss in der Lage sein, betriebssystemunabhnig das Vorhandensein von Abhngigkeiten (z. B. Fremdbibliotheken) zu berprfen, benutzer- und betriebssystemspezische Programme wie Compiler und Linker ausfhren zu knnen und alle ntigen Programmaufrufe fr den Erstellungsprozess in der richtigen Reihenfolge auszufhren.
Gemeint ist der geringe Funktions- und Sprachkonstruktumfang von C, was aus der hardware- und assamblernahen Orientierung resultiert.
61

32

3.2. Alternative Erstellungssysteme Ursprnglich war geplant, libspirit mit den GNU Autotools62 zu erstellen. Diese bestehen hauptschlich aus den Projekten Autoconf63 , Automake64 und Libtool65 . Die GNU Autotools wurden fr Unix-Systeme entwickelt. Sie steigern die Portabilitt von erzeugten Programmen und vereinfachen den Erstellungsprozess ber Shell-Skripte, weshalb der Nutzer keine Kommandozeilenbefehle zum Kompilieren eingeben muss. Das bereits beschriebene Erstellungssystem CMake wurde den GNU Autotools vorgezogen, da die GNU Autotools eine starke Unix-Orientierung haben. Die GNU Autotools sind unter Windows nicht ohne Einschrnkungen zu verwenden, da die erstellten Shell-Skripte fr die Bourne-Shell erzeugt werden. Die Bourne-Shell wird unter Windows nicht nativ untersttzt. Mit Hilfe von Projekten wie MSYS oder Cygwin ist es mglich, eine Umgebung fr die GNU Autotools unter Windows zu schaen, jedoch mit verringerter Funktionalitt und Performanz. Desweiteren wird fr Windows-Nutzer mit unzureichender Unix-Erfahrung der Erstellungsprozess unntig verkompliziert.66
CMake
Grad der Abstraktion erstellt

GNU Autotools Makefile

Microsoft Visual Studio Projekt / Workspace

Apple Xcode Projekt


steuert

zu benutzender Compiler

Abbildung 3.1: Abstraktionshierarchie der Erstellungssysteme, Quelle: Eigene Darstellung

CMake hingegen ist als vollstndig betriebssystemunabhngiges Erstellungssystem geplant. Dies wird im Gegensatz zu den GNU Autotools auf einer hheren Abstraktionsebene realisiert. Die Abbildung 3.1 stellt diese Abstraktionsebenen dar. Die GNU Autotools abstrahieren die Bedienung des
GNU Build System, auch bekannt als Autotools, http://www.gnu.org/software/. Generiert ein Kongurations-Skript fr ein Projekt. 64 Vereinfacht die Erstellung von Makeles. 65 Bietet Abstraktionsmglichkeiten fr die Erstellung dynamischer Programmbibliotheken. 66 Vgl. [Cal10, S. 1 3]
63 62

33

3.2. Alternative Erstellungssysteme Compilers und die Konguration des Projektes fr das Zielsystem. CMake kann benutzt werden, um die Verwendung von Erstellungssystemen, wie den GNU Autotools, zu abstrahieren. Dadurch ist CMake abstrakter als die GNU Autotools und anpassungsfhiger an verschiedene Betriebssysteme, weshalb es fr libspirit verwendet wurde.

34

4.

Beschreibung der Lsung

In diesem Kapitel wird die Entwicklung der libspirit, einer portablen, dynamischen Programmbibliothek in der Programmiersprache C (ANSI Standard), erlutert.

4.1.

Voraussetzungen, welche libspirit erfllen soll

Im Vorfeld sollte zunchst deniert werden, welchen Voraussetzungen die entwickelte Software libspirit gerecht werden soll. Im Rahmen der Aufgabenstellung wurden die folgenden Voraussetzungen deniert: [1] Die entwickelte Software soll eine dynamische Programmbibliothek sein. Dadurch wird Studierenden ermglicht, die durch libspirit bereitgestellten Funktionen unkompliziert in ihre eigenen Anwendungen einzubinden. [2] Es soll mglich sein, die entwickelte dynamische Programmbibliothek ohne weitere Anpassungen auf unterschiedlichen Betriebssystemen zu kompilieren. Dafr ist ein Erstellungssystem zu verwenden. Es soll in Betracht gezogen werden, dass fr bestimmte Betriebssysteme fertig kompilierte Binrdateien (z. B. DLLs unter Windows) weitergegeben werden knnen, um Studierenden den Erstellungsprozess der libspirit zu ersparen. [3] Die entwickelte Software soll auf den REST-Dienst des spirit@fhs-Projektes zugreifen, um Informationen zu abzurufen. Diese Informationen 35

4.2. berlegungen zur Optimierung fr den Endanwender liegen bei der Implementierung des spirit@fhs-REST-Dienstes wahlweise im XML- oder JSON-Format vor. Der Abruf kann sowohl ber HTTP als auch ber HTTPS stattnden. Es ist deshalb eine Voraussetzung, dass mindestens eines der Informationsformate und beide Abrufprotokolle untersttzt werden. [4] Die Informationen, welche durch die entwickelte Software vom RESTDienst abgerufen werden, sollen an den Nutzer abstrahiert weitergegeben werden. Abstrahiert bedeutet in diesem Fall, dass die Verwendung des REST-Dienstes und smtliche komplizierteren Abfragen oder Informationsformate (wie JSON) durch die Software bernommen werden. Dabei muss der Nutzer keinerlei Kenntnisse ber die Funktionsweise und Verwendung dieser Techniken haben. Dennoch soll dem Nutzer der Zugri auf eine Teilmenge der angebotenen Informationen ermglicht werden. Diese Informationen werden dann in einem einfacheren Format weitergegeben (z. B. ber eine Funktion, welche alle aktuellen Neuigkeiten in der Standardausgabe ausgibt). [5] Die durch die entwickelte Software angebotenen Funktionen sollen fr Studierende ansprechend sein, um das Interesse an der libspirit und somit an der Programmierung zu wecken. Deshalb ist es wichtig, dass die angebotenen Funktionen die Studierenden persnlich betreen (Neuigkeiten fr den eigenen Studiengang, der eigene Stundenplan).

4.2.

Vorbereitende berlegungen zur Optimierung fr den Endanwender

Ziel ist es, eine unkompliziert zu verwendende Software zu entwickeln. Darum mussten im Vorhinein einige Design-Entscheidungen getroen und Konventionen deniert werden, um eine konsequent intuitive Verwendung der Software zu ermglichen.

36

4.2. berlegungen zur Optimierung fr den Endanwender

4.2.1.

Handle System

Es ist mglich, dass die zu entwickelnde Software mehrfach in demselben Programm verwendet werden soll. Sollen beispielsweise Funktionen der zu entwickelnden Software mit unterschiedlichen Einstellungen abwechselnd genutzt werden, mssten diese Einstellungen vor jedem Funktionsaufruf neu (und redundant) gesetzt werden. Ein Handle identiziert eine Sitzung67 inklusive aller Einstellungen eindeutig. Beim Aufruf einer Funktion wird zustzlich zu anderen Parametern das Handle (und damit die Sitzung) angegeben, mit dessen Einstellungen diese Funktion auszufhren ist. Folglich mssen die Einstellungen auch bei mehrfacher Verwendung lediglich beim Initialisieren des Handles und bei eventuell nachtrglichen nderungen verndert werden.

4.2.2.

System zur Verwaltung von Einstellungen

Wie in 4.2.1. bereits erlutert, ist es erforderlich, einige Einstellungsmglichkeiten zu haben. Diese frdern u. a. auch die Toleranz gegenber spteren Vernderungen. So kann sich z. B. die Basis-URL des REST-Dienstes ndern, ohne dass die Software neu kompiliert werden muss. Weiterhin knnten DebugAusgaben zur Fehlersuche aktiviert werden. Bei der Erstellung eines Systems zur Verwaltung von Einstellungen mit der Programmiersprache C muss darauf geachtet werden, eine fr den Nutzer leicht zu verwendende Schnittstelle zwischen dem Setzen einer Option und deren Speicherung zu schaen. Ursache dafr sind die im Vergleich zu objektorientierten Programmiersprachen wie C++ geringen Abstraktionsmglichkeiten. In der Programmiersprache C muss diese Abstraktion durch den Programmierer geschaen werden.
Als eine Sitzung werden in diesem Fall alle Informationen (z. B. Verbindungsinformationen fr den Zugri auf den REST-Dienst) betrachtet, welche notwendig sind, um eine beliebige Endanwender-Funktion von libspirit auszufhren.
67

37

4.2. berlegungen zur Optimierung fr den Endanwender

4.2.3.

System zur Verwaltung von Fehlern

Es besteht immer die Mglichkeit, dass whrend der Laufzeit Fehler auftreten. Deshalb ist es ntig, ber ein einheitliches System zur Behandlung von Laufzeitfehlern zu verfgen. In modernen Programmiersprachen werden dafr oft Exceptions benutzt. Exceptions werden von der Programmiersprache C nicht untersttzt. Stattdessen verwendet die in C programmierte Software Rckgabecodes. Diese Rckgabecodes werden als Ganzzahl reprsentiert. Das hat zur Folge, dass die Erkennung eines Fehlers zwar simpel ist (z. B. Rckgabewert 0), aber die Komplexitt der Auswertung des Fehlers von dem Fehlersystem des Entwicklers der verwendeten Funktion abhngt. Ein gutes Fehlersystem sollte ermglichen, Fehlercodes whrend der Programmierung ber Namen68 anstelle ihrer ganzzahligen Werte zu identizieren. Weiterhin sollte die Umwandlung von Fehlercodes in aussagekrftige Strings fr die Ausgabe mglich sein.

4.2.4.

Benennungskonventionen

Wie in Kapitel 2.2.3. beschrieben, ist es fr die maximale Portabilitt ntig, dass die zu entwickelnde Software durchdachten Benennungskonventionen fr Datei- und Funktionsnamen folgt. Quelltext- und Header-Dateien Als allgemeine Regel fr alle Quelltext- und Header-Dateien werden alle Dateinamen (inklusive ihrer Erweiterung) ausschlielich in Kleinschreibung vergeben. Bei den Dateinamen fr die Interface Header-Dateien wurde das Prx spirit gewhlt. Eine genauere Beschreibung der enthaltenen Funktionalitt kann durch einen angehngten Begri ausgedrckt werden. Als Trennzeichen dient ein Unterstrich. So heit die Haupt-Header-Datei spirit.h und eine zustzliche Funktionen bietende Header-Datei z. B. spirit_error.h.
siehe Quelltext 2.1: return EXIT_SUCCESS; EXIT_SUCCESS reprsentiert den Wert 0 und wird in der Standard-Header-Datei stdlib.h durch das Makro #define EXIT_SUCCESS 0 deniert.
68

38

4.2. berlegungen zur Optimierung fr den Endanwender Die Dateinamen der restlichen Quelltext- und Header-Dateien erhalten einen Prx libspirit_, sofern die enthaltene Funktionalitt nur in Verbindung mit libspirit verwendet werden kann. Ist nur allgemein zu verwendende Funktionalitt enthalten (z. B. eine Funktion fr das Duplizieren von Strings im Speicher), so erhlt der Dateiname kein Prx. Besteht ein Dateiname aus mehreren beschreibenden Worten, so knnen diese durch einen Unterstrich voneinander getrennt werden. Funktions- und Typnamen Die verwendeten Benennungskonventionen fr Funktionsnamen unterscheiden zwischen Interface-Funktionen und internen Funktionen. Die Interface-Funktionen werden in Kleinbuchstaben notiert. Jede Interface-Funktion erhlt das Prx spirit_, um sie eindeutig zu kennzeichnen und berschneidungen mit den vom Nutzer denierten Funktionen zu vermeiden. Besteht der Funktionsname aus mehreren Worten, kann ein Unterstrich zur Trennung eingesetzt werden69 . Die Namen interner Funktionen beginnen mit dem Prx Spirit_ und der beschreibende Funktionsname wird in lowerCamelCase angehngt70 . Die Benennungskonventionen fr Typen und Konstanten sind in Implementierung und Interface gleich, da sich ihr Einsatzgebiet berschneidet. Selbst denierte Datentypen beginnen mit dem Prx SPIRIT, gefolgt von einer Bezeichnung71 in Kleinbuchstaben ohne Trennzeichen72 . Konstanten erhalten ebenfalls den Prx SPIRIT sowie weitere bezeichnende Worte in Grobuchstaben, welche voneinander durch einen Unterstrich zu trennen sind.
z. B. spirit_init() z. B. Spirit_printNewsFromJsonString() 71 Eine Ausnahme bildet der Handle-Typ von libspirit, dessen Name nur aus dem Prx besteht: SPIRIT. 72 z. B. SPIRITcode
70 69

39

4.3. Implementierung von libspirit

4.3.

Implementierung von libspirit

Dieser Abschnitt der Arbeit befasst sich mit der schrittweisen Implementierung der zu entwickelnden Software libspirit. Jeder Unterabschnitt kann als eigener Meilenstein des Entwicklungsprozesses betrachtet werden.

4.3.1.

Verwendete Entwicklungssoftware

Der Groteil der Entwicklung fand unter Microsoft Windows 7 Professional x64 statt. Als Entwicklungsumgebung wurde die Eclipse IDE for C/C++ Developers73 in der x86 Indigo-Version mit dem CMake Editor74 Plugin, Microsoft Visual Studio 2010 Ultimate75 und Notepad++76 als universaler Texteditor verwendet. Die Versionsverwaltung wurde mit Hilfe der Open Source Software Git77 realisiert. Das entsprechende Repository fr die zu entwickelnde Software ist auf github hinterlegt und unter der folgenden URL zu erreichen: https://github.com/spirit-fhs/libspirit Die Softwaretests mssen auf mehreren Betriebssystemen ausgefhrt werden. Nur durch erfolgreiche Tests auf mehreren Betriebssystemen kann die Software als portabel bezeichnet werden. In der Tabelle 4.1 sind die im Rahmen der Arbeit verwendeten Test-Systeme mit den relevanten Spezikationen aufgelistet. Whrend der Implementierung wurde regelmig getestet, ob sich die zu entwickelnde Software auf allen Test-Systemen der Tabelle 4.1 erzeugen lsst.
73 74

http://www.eclipse.org/ http://cmakeed.sourceforge.net/ 75 http://www.microsoft.com/germany/visualstudio/ 76 http://notepad-plus-plus.org/ 77 http://git-scm.com/

40

4.3. Implementierung von libspirit Betriebssytem Gentoo 2.0.3 Gentoo 2.0.3 Mac OS X 10.6 Ubuntu 11.04 Ubuntu 11.04 Windows 7 Prof. SP1 Arch. x86 x64 x64 x86 x64 x64 CMake 2.8.4 2.8.4 2.8.5 2.8.3 2.8.3 2.8.5 Erzeugungssoftware GNU Make 3.82 & GNU Make 3.82 & Xcode 3.2 GNU Make 3.81 & GNU Make 3.81 & Visual Studio 10 GCC 4.4.5 GCC 4.4.5 GCC 4.5.2 GCC 4.5.2

Tabelle 4.1: Testumgebungen fr die zu entwickelnde Software libspirit

4.3.2.

Verwendete Fremdbibliotheken

Die zu entwickelnde Software bentigt Zugri auf den REST-Dienst des spirit@fhs-Projektes, wie in Voraussetzung [3] beschrieben. Die dafr bentigte Untersttzung der Protokolle HTTP, HTTPS und des Datenaustauschformates JSON wird ber die Verwendung von Fremdbibliotheken realisiert. Diese Fremdbibliotheken mssen die folgenden Eigenschaften untersttzen, um in die zu entwickelnde Software integriert werden zu knnen: Die Fremdbibliothek muss in Quelltext-Form (nicht vorkompiliert) und in der Programmiersprache C (ANSI Standard) vorliegen, um unabhngig vom Betriebssystem bersetzt werden zu knnen. Nur so wird grtmgliche Portabilitt geboten. Es muss mglich und im Optimalfall vorgesehen sein, dass die Fremdbibliothek mit dem Erstellungssystem CMake erstellt wird. Dadurch wird eine einfache Integration in den Erstellungsprozess ermglicht. Die Fremdbibliothek muss statisch kompilierbar sein, damit alle verwendeten Funktionen in die letztendlich erstellte dynamische Programmbibliothek integriert und ohne zustzliche Abhngigkeiten ausgefhrt werden knnen.

41

4.3. Implementierung von libspirit Die Wahl der Fremdbibliotheken el auf libcurl78 und YAJL79 . Die Programmbibliothek libcurl ist Teil des seit 1997 entwickelten cURLProjektes. Der Name cURL ist eine Abkrzung und steht fr Client for URLs. Durch die Programmbibliothek libcurl werden u. a. auch die Protokolle HTTP und HTTPS untersttzt. Die Abkrzung YAJL steht fr Yet Another JSON Library. YAJL ist eine Programmbibliothek, welche einen String, der Daten im JSON-Format enthlt, auswertet und die enthaltenen Daten fr ein in der Programmiersprache C geschriebenes Programm zugnglich macht. Beide Programmbibliotheken liegen als portabler ANSI C Quelltext vor, werden nativ mit CMake kompiliert und sind als statische Programmbibliothek erstellbar.

4.3.3.

Erstellen des Projektes

Der Grundaufbau des Projektes soll bereits erstellbar sein. Dadurch werden von Beginn an Tests in den verschiedenen Testumgebungen ermglicht. Der Grundaufbau besteht aus den Dateien CMakeLists.txt, welche den Erstellungsprozess beschreibt, der dynamischen Header-Datei libspiritcong.h.in und den Quelltext-Dateien spirit.h und libspirit.c.

1 2 3 4 5 6 7 8 9

cmake_minimum_required (VERSION 2.8) project (libspirit) set set set set set set
78 79

(libspirit_VERSION_MAJOR 1) (libspirit_VERSION_MINOR 0) (incDir ${PROJECT_BINARY_DIR}/include/libspirit) (SRCS src/libspirit.c) (HDRS ) (PUB_HDRS src/api/spirit.h) http://curl.haxx.se/ http://lloyd.github.com/yajl/

42

4.3. Implementierung von libspirit


10 11 12 13 14 15 16 17 18 19 20 21 22

set (LIB_TYPE SHARED) configure_file ("${PROJECT_SOURCE_DIR}/libspiritconfig.h.in" "${PROJECT_BINARY_DIR}/libspiritconfig.h") include_directories("${PROJECT_BINARY_DIR}") add_library(libspirit ${LIB_TYPE} ${SRCS} ${HDRS} ${PUB_HDRS}) set_target_properties(libspirit PROPERTIES PREFIX "") file (MAKE_DIRECTORY ${incDir}) foreach (header ${PUB_HDRS}) set (header ${PROJECT_SOURCE_DIR}/${header}) execute_process(COMMAND ${CMAKE_COMMAND} -E copy_if_different ${header} ${incDir}) endforeach (header ${PUB_HDRS}) include_directories (${incDir}/..) Quelltext 4.1: CMakeLists.txt

23 24

Der Quelltext 4.1 ist die Grundversion fr die Steuerung von CMake. Dieser Code ermglicht es bereits, eine dynamische Programmbibliothek auf verschiedenen Betriebssystemen erstellen zu lassen. Die Zeilen 1 und 2 sind Metainformationen fr das Projekt. Sie legen den Projektnamen auf libspirit fest und veranlassen CMake-Versionen vor 2.8 den Erstellungsprozess nicht auszufhren. In den Zeilen 4 bis 10 werden Variablen deniert und initialisiert. Die in den Zeilen 4 und 5 denierten Versionsinformationen werden im Quelltext 4.2 verwendet, um sowohl einzelne Versionsmakros als auch ein Makro im Format der User-Agent-Denition des HTTP-Headers zu erstellen.
1 2 3 4

#define libspirit_VERSION_MAJOR @libspirit_VERSION_MAJOR@ #define libspirit_VERSION_MINOR @libspirit_VERSION_MINOR@ #define libspirit_USER_AGENT "libspirit/@libspirit_VERSION_MAJOR@.@libspirit_VERSION_MINOR@" Quelltext 4.2: libspiritcong.h.in

43

4.3. Implementierung von libspirit Diese dynamisch erzeugten Werte knnen im Quelltext verwendet werden. Der Befehl configure_file aus Zeile 12 kopiert die Eingangsdatei und ersetzt CMake-Variablen im Kongurationsrohling. An dieser Stelle sei erwhnt, dass die Reihenfolge fr CMake-Befehle keine Rolle spielt, auer bei configure_file. Dort werden nur die Variablen ersetzt, welche vor dem Aufruf der Funktion deniert wurden. Die Zeilen 7 bis 9 denieren Listen aus Dateinamen, welche dem Compiler bekannt gemacht werden. Diese werden in Zeile 16 bergeben. Durch den Befehl add_library und den in der Variable LIB_TYPE gesetzten Wert SHARED wird CMake angewiesen, eine dynamische Programmbibliothek zu erstellen. Um einen einheitlichen Dateinamen der erzeugten dynamischen Programmbibliothek zu garantieren, wird die CMake Funktion in Zeile 17 deaktiviert, welche auf Unix-Systemen automatisch das Prx lib hinzufgt. In den Zeilen 19 bis 24 wird durch eine Schleife realisiert, die InterfaceHeader-Dateien von libspirit in den Binrverzeichnisbaum zu kopieren und diese dem Suchpfad des Compilers hinzuzufgen.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19

#ifndef SPIRIT_H_ #define SPIRIT_H_ #ifdef __cplusplus extern "C" { #endif #if (defined(_WIN32) || defined(WIN32)) #ifdef libspirit_EXPORTS #define LIBSPIRIT_API __declspec(dllexport) #else #define LIBSPIRIT_API __declspec(dllimport) #endif #else /* not MSVC */ #define LIBSPIRIT_API #endif #ifdef __cplusplus }

44

4.3. Implementierung von libspirit


20 21 22

#endif #endif /* SPIRIT_H_ */ Quelltext 4.3: spirit.h

Der Quelltext 4.3 ist die Standard-Interface-Header-Datei des Projektes libspirit. Die Zeilen 4 bis 6 und 18 bis 20 ermglichen die Verwendung der dynamischen Programmbibliothek mit C++ wie in 2.2.3. angeben. Die Denition des Prprozessor-Makros LIBSPIRIT_API in den Zeilen 8 bis 16 wird verwendet, um Windows kompatible Header-Dateien zu erstellen. Unter Windows mssen durch eine Programmbibliothek zu exportierende Funktionen mit __declspec(dllexport) in der Signatur gekennzeichnet werden, da sonst nichts exportiert wird. Bei der Verwendung der dynamischen Programmbibliothek mssen die bereitgestellten Funktionen mit __declspec(dllimport) in der Signatur zum Importieren gekennzeichnet werden, da sonst Linker-Fehler auftreten. Da zwei Header-Dateien (eine fr Import und eine fr Export) eine unntige Fehlerquelle sind, wird beim Kompilieren der dynamischen Programmbibliothek automatisch unterschieden, welcher Fall aktuell vorliegt. Sollte das Betriebssystem, auf dem kompiliert wird, nicht Windows sein, wird LIBSPIRIT_API als leer deniert und folglich in der Funktionssignatur ignoriert.
1 2 3 4 5 6 7

#include <libspirit/spirit.h> #include <libspiritconfig.h> LIBSPIRIT_API int fndlltest(void) { return 42; } Quelltext 4.4: libspirit.c

Der Quelltext 4.4 exportiert eine simple Funktion, um den Kompiliervorgang testen zu knnen. 45

4.3. Implementierung von libspirit

4.3.4.

Integration von YAJL und libcurl

Da beide Fremdbibliotheken nativ mit CMake erstellt werden, ist die Integration in den Erstellungsvorgang von libspirit simpel. Der Quelltext 4.5 enthlt die ntigen nderungen an der Datei CMakeLists.txt, um beide Fremdbibliotheken ebenfalls zu erstellen und anschlieend an libspirit binden zu lassen. Die Reihenfolge der Befehle spielt hier ebenfalls keine Rolle. CMake ndet automatisch die richtige Reihenfolge, um alle Abhngigkeiten zu erstellen.
1 2 3 4 5

set (YAJL_DIST_NAME yajl-2.0.3) add_subdirectory (yajl) set (EXTRA_LIBS ${EXTRA_LIBS} yajl_s) set (CURL_STATICLIB ON CACHE BOOL "(FORCED) get a static libcurl" FORCE) set (BUILD_CURL_EXE OFF CACHE BOOL "(FORCED) no exe needed" FORCE) set (BUILD_CURL_TESTS OFF CACHE BOOL "(FORCED) no tests needed" FORCE) if(MSVC) set (BUILD_RELEASE_DEBUG_DIRS ON CACHE BOOL "(FORCED) seems to be bugged when off" FORCE) endif() add_subdirectory (curl) set (EXTRA_LIBS ${EXTRA_LIBS} libcurl) include_directories ("${PROJECT_BINARY_DIR}/curl/include/curl") include_directories ("${PROJECT_BINARY_DIR}/curl/lib") include_directories ("${PROJECT_SOURCE_DIR}/curl/include") include_directories ("${PROJECT_BINARY_DIR}/yajl/${YAJL_DIST_NAME}/include") target_link_libraries (libspirit ${EXTRA_LIBS})

6 7 8 9

10 11 12 13 14 15 16 17

18 19

Quelltext 4.5: nderungen an der CMakeList.txt um YAJL und libcurl zu integrieren

Die Zeilen 1 bis 3 fgen YAJL dem Projekt libspirit hinzu. Mit dem Befehl add_subdirectory wird das Verzeichnis des YAJL-Quelltextes hinzugefgt. Dadurch wird die sich darin bendende CMakeLists.txt ebenfalls ausgewertet und YAJL erstellt. Mit der Variable YAJL_DIST_NAME wird der Name des 46

4.3. Implementierung von libspirit Verzeichnisses angegeben, in dem YAJL seine Interface-Header-Dateien generiert. In Zeile 3 wird schlielich der Name des Projektes der statischen YAJL Bibliothek an die Liste von Zusatzbibliotheken von libspirit angehngt. In den Zeilen 5 bis 12 wiederholt sich der im vorhergehenden Abschnitt beschriebene Vorgang fr libcurl. Es werden einige Cache-Variablen gesetzt, welche die Einstellungen von libcurl beeinussen, ohne dessen CMakeLists.txt zu modizieren. Das erleichtert den Austausch des Quelltextes durch eine neuere Version. Die Zeile 19 weist CMake an, die Liste von Zusatzbibliotheken an die dynamische Programmbibliothek libcurl zu binden. Es ist unvermeidlich, geringfgige Modikationen an den Dateien der Fremdbibliotheken vorzunehmen. Unter Unix muss die Compiler-Option -fPIC aktiviert werden, um positionsunabhngigen Code zu erzeugen. Es ist notwendig, dass der Code umplatziert werden kann, da bei dynamischen Programmbibliotheken der Speicherbereich, in dem sie ausgefhrt werden, im Vorfeld nicht bekannt ist.80
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17

#include #include #include #include #include

<libspirit/spirit.h> <libspiritconfig.h> <yajl/yajl_gen.h> <yajl/yajl_parse.h> <curl/curl.h>

LIBSPIRIT_API int fndlltest(void) { yajl_gen g; yajl_status st; yajl_handle h; st = yajl_parse(h, "[]", 3); return 42; } LIBSPIRIT_API int curltest(void) { CURL *curl; CURLcode res;
80

Vgl. [Hoo05, S. 182]

47

4.3. Implementierung von libspirit


18 19 20 21 22 23 24 25

curl = curl_easy_init(); if(curl) { curl_easy_setopt(curl, CURLOPT_URL, "http://example.com"); res = curl_easy_perform(curl); curl_easy_cleanup(curl); } return 0; } Quelltext 4.6: libspirit.c mit genderten Testfunktionen fr die Fremdbibliotheken

Um zu gewhrleisten, dass sich die dynamische Programmbibliothek libspirit auf allen Testumgebungen mit den integrierten nderungen erzeugen lsst, werden die Testfunktionen der libspirit.c wie im Quelltext 4.6 erweitert. Es wurden Funktionen und Datentypen der Fremdbibliotheken genutzt, um diese vom Linker in die libspirit integrieren zu lassen. Die implementierten Aufrufe knnten zwar Laufzeitfehler verursachen, jedoch sind diese fr einen Funktionstest noch vernachlssigbar. Die Tests beziehen sich lediglich darauf, ob die verwendeten Funktionen der Fremdbibliotheken statisch und ohne Compilerfehler in die libspirit integriert wurden.

4.3.5.

Implementierung von Testfunktionen und einer Testapplikation

Wird die dynamische Programmbibliothek (inklusive der Fremdbibliotheken) auf allen Betriebssystemen fehlerfrei erzeugt, kann mit der Entwicklung der in 4.1. beschriebenen Programmlogik fortgefahren werden. Als Grundlage soll erneut die Testbarkeit der Funktionen herangezogen werden. Deshalb wird eine Testapplikation (Quelltext 4.7) erstellt, welche die dynamische Programmbibliothek libspirit bindet und die von ihr angebotenen Funktionen auf Laufzeitfehler testet. Zunchst wurden simple Funktionstests von den Herstellerwebseiten der Fremdbibliotheken in die dynamische Programmbibliothek libspirit aufgenommen. Der Aufbau dieser Funktionen ist fr diesen Abschnitt nicht von 48

4.3. Implementierung von libspirit Bedeutung und kann im Anhang A-1 eingesehen werden. Nach erfolgreicher Erstellung der dynamischen Programmbibliothek inklusive der neuen Testfunktionen kann mit der Implementierung der Testapplikation begonnen werden.
1 2 3 4 5 6 7 8 9 10 11

#include <stdio.h> #include <stdlib.h> #include <libspirit/spirit.h> int main(void) { puts("-- libspirit test application --"); fndlltest(); curltest("209.85.148.106"); curltest("https://212.201.64.226:8443/fhs-spirit/news"); return EXIT_SUCCESS; } Quelltext 4.7: testapp.c

Die Testapplikation wird ebenfalls als CMake Projekt angelegt und ber den Befehl
add_subdirectory (testapp)

in die Datei CMakeLists.txt der libspirit eingefgt. Der Quelltext der CMakeProjektdatei der Testapplikation enthlt nichts unbekanntes und bendet sich im Anhang A-2. Funktioniert die im Quelltext 4.7 dargestellte Testanwendung, so kann davon ausgegangen werden, dass die Fremdbibliotheken fehlerfrei an die zu entwickelnde dynamische Programmbibliothek libspirit gebunden wurden. Tests von neu implementierter Programmlogik knnen ab jetzt mit Hilfe der Testapplikation realisiert werden.

4.3.6.

Implementierung des Handle-, Einstellungsund Fehlersystems

Bevor die fr den Endanwender vorgesehenen Funktionen implementiert werden knnen, mssen zuerst die in 4.2. beschriebenen Handle- und Fehler49

4.3. Implementierung von libspirit Systeme erstellt werden, da diese in Funktionsdeklarationen sowohl als Parameter als auch als Rckgabetyp verwendet werden. Das Einstellungssystem kann als Erweiterung des Handle-Systems betrachtet werden und wird deshalb im gleichen Schritt implementiert. Das Fehler-System Im Quelltext 4.8 werden die ganzzahligen Fehlerwerte durch den Aufzhlungstyp enum als Konstanten fr den Programmierer deniert. Auf diese Weise wird vermieden, mit unbersichtlichen Ganzzahlen arbeiten zu mssen. Wie u. a. in den Zeilen 4 und 5 zu sehen, knnen bei der Vergabe von Fehlerwerten auch Platzhalter eingesetzt werden. Sollte spter z. B. eine weitere Fehlermeldung in Verbindung mit der Speicherallokation deniert werden, so kann diese die Zeile 3 ersetzen und in der Nhe anderer Speicherallokationsfehler stehen, ohne die Wertzuweisung der anderen Konstanten zu verschieben. Der in Zeile 14 denierte Aufzhlungseintrag SPIRIT_LAST ist nicht als Fehlertyp, sondern als Begrenzung gedacht. Durch seinen Wert kann man sowohl die Anzahl der denierten Fehlercodes bestimmen als auch Fehlercodes identizieren, welche auerhalb des Wertebereiches liegen.
1 2 3 4 5 6

7 8 9 10 11 12 13 14

LIBSPIRIT_API typedef enum { SPIRITE_OK = 0, SPIRITE_OUT_OF_MEMORY, SPIRITE_OBSOLETE2, SPIRITE_OBSOLETE3, SPIRITE_JSON_PARSE_ERROR, string */ SPIRITE_JSON_NODE_NOT_FOUND, SPIRITE_OBSOLETE6, SPIRITE_OBSOLETE7, SPIRITE_OBSOLETE8, SPIRITE_BAD_FUNCTION_ARGUMENT, SPIRITE_UNKNOWN_OPTION, SPIRIT_LAST /* never use! */

/* /* /* /* /* /* /* /* /* /*

1 2 3 4 5 6 7 8 9 10

memory allocation failed */ NOT USED */ NOT USED */ error while parsing the JSON node not in JSON tree */ NOT USED */ NOT USED */ NOT USED */ bad function argument given */ unknown option given */

50

4.3. Implementierung von libspirit


15

} SPIRITcode; Quelltext 4.8: Die Fehlerwerte in einer fr den Programmierer lesbaren Form

Wie in 4.2.3. beschrieben, sollte eine Funktion zur Umwandlung von Fehlercodes in Strings existieren. Diese ist im Quelltext 4.9 teilweise dargestellt. Jeder denierte Fehlercode wird in der switch-Anweisung behandelt. Sollte der Fehlercode unbekannt sein, wird die switch-Anweisung verlassen und ein Unknown error zurckgegeben.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17

LIBSPIRIT_API const char *spirit_errstring(SPIRITcode error) { switch (error) { case SPIRITE_OK: return "No error"; case SPIRITE_OUT_OF_MEMORY: return "Out of memory"; /* [...] */ case SPIRITE_OBSOLETE7: case SPIRITE_OBSOLETE8: case SPIRIT_LAST: break; } return "Unknown error"; } Quelltext 4.9: Funktion zur Umwandlung von Fehlercodes in Strings

Das Handle-System Ein Handle des in 4.2.1. beschriebenen Handle-Systems muss Daten halten. Sicherheitshalber sollten diese Daten nicht direkt vom Endanwender eingesehen oder gendert werden knnen. Deshalb wird ein Handle der libspirit nach auen als
LIBSPIRIT_API typedef void SPIRIT;

51

4.3. Implementierung von libspirit reprsentiert. Die interne Darstellung (inklusive Datenhaltung) erfolgt durch die im Quelltext 4.10 einzusehende Struktur SpiritHandle. Weiterhin werden Endanwenderfunktionen geboten, durch deren Einsatz ein Handle genet und geschlossen werden kann. Die Implementierung ist im Anhang A-3 zu sehen und besteht fast ausschlielich aus Speicherallokation und der Freigabe von Speicher.
1 2 3 4 5 6 7 8 9 10

struct SpiritHandle { struct LibcurlSettings curl; char *base_url; }; struct LibcurlSettings { char *ssl_cipher_type; char *header_accept; char *user_agent; }; Quelltext 4.10: Struktur zur Speicherung der Sitzungsinformationen eines Handles

Das Einstellungs-System Um eine Schnittstelle fr den Endanwender zu schaen, mit der er die Daten eines Handles ndern kann, wurde das Einstellungs-System implementiert. Da in ANSI C keine Funktionsberladung mglich ist, wurde eine hnliche Umgebung mit variablen Parameterlisten geschaen. Quelltext 4.11 zeigt die Erstellung eines Aufzhlungstyps, um die Optionen, hnlich der Fehlercodes, als ganzzahlige und gleichzeitig benannte Konstante verwenden zu knnen. Die einzelnen Aufzhlungseintrge werden durch das in Zeile 10 denierte Prprozessor-Makro erstellt. Durch das Makro wird der ganzzahlige Wert der Konstante, je nach Datentyp der Option, auf einen optionsabhngigen Basiswert addiert. Folglich kann der Typ einer Option durch die Position seines Wertes im Wertebereich bestimmt werden. Weiterhin wird die Nummer der Option selbst vergeben. Dadurch knnen auch Freirume

52

4.3. Implementierung von libspirit gelassen werden, um eine Gruppierung hnlicher Aufzhlungseintrge zu ermglichen (analog zu den Fehlercodes). Das Makro SINIT in Zeile 10 wird mit dem Namen der Option, ihrem Datentyp und einer beliebigen, einzigartig vergebenen Nummer als Parameter aufgerufen. Der Aufruf:
SINIT(BASE_URL, OBJECTPOINT, 1),

wird durch den Prprozessor in den folgenden Aufzhlungseintrag umgewandelt:


SPIRITOPT_BASE_URL = 10001,

1 2 3 4 5 6 7 8 9 10

#define #define #define #define

SPIRITOPTTYPE_LONG SPIRITOPTTYPE_OBJECTPOINT SPIRITOPTTYPE_FUNCTIONPOINT SPIRITOPTTYPE_OFF_T

0 10000 20000 30000

#ifdef SINIT #undef SINIT #endif #define SINIT(name,type,number) SPIRITOPT_ ## name = SPIRITOPTTYPE_ ## type + number typedef enum { /* base url for REST request */ SINIT(BASE_URL, OBJECTPOINT, 1), /* sipher type of ssl (advanced option) */ SINIT(SSL_SIPHER_TYPE, OBJECTPOINT, 2), /* header accept, like "Accept: application/json" */ SINIT(HEADER_ACCEPT, OBJECTPOINT, 3), SPIRITOPT_LASTENTRY /* the last unused */ } SPIRIToption; Quelltext 4.11: Erstellung eines Aufzhlungstyps zur Unterscheidung von Optionen

11 12 13 14 15 16 17 18 19 20

Um die Funktion zum Setzen der Optionen spirit_setopt() sicherer zu implementieren, wurden spezielle Prprozessor-Makros verwendet. In der 53

4.3. Implementierung von libspirit Interface-Header-Datei (Quelltext 4.12) wurde direkt nach dem Funktionsprototyp ein mit der Funktion gleichnamiges Makro deniert. Vor der Implementierung (Quelltext 4.13) der Funktion wird die Makrodenition mittels
#undef spirit_setopt

wieder aufgehoben. Der Eekt dieser Verfahrensweise ist, dass der Prprozessor Funktionsaufrufe durch exakt den selben Funktionsaufruf ersetzt, da er den Makro-Aufruf durchfhrt. Solange der Funktionsaufruf exakt drei Argumente (wie das Makro) hat, wird ein Funktionsaufruf durch den selben Funktionsaufruf ersetzt. Ist die Anzahl der Argumente verschieden von drei, kann das Makro nicht ausgefhrt und das Programm nicht kompiliert werden.
1

LIBSPIRIT_API SPIRITcode spirit_setopt(SPIRIT *handle, SPIRIToption tag, ...); #define spirit_setopt(handle,opt,param) spirit_setopt(handle,opt,param) Quelltext 4.12: Fixiert auf drei Parameter, trotz variabler Parameterliste

Eine Option kann vom Endanwender durch Aufruf der Funktion spirit_setopt()81 gesetzt werden. Es muss ein Handle, eine Optionskonstante und ein Wert angegeben werden. Die Funktion spirit_setopt() wandelt lediglich das Endanwender-Handle in die dahinterstehende Datenstruktur um. Die Datenstruktur und das erste Argument der variablen Parameterliste werden an die im Quelltext 4.13 beschriebene Funktion weitergeleitet, welche die Optionen setzt. Der Kommentar im Quelltext 4.13 erlutert, wie ein einzelner Fall der switch-Anweisung je nach Datentyp der Option aussehen kann. Die Funktion setstropt() ist im Anhang A-5 deniert. Sie gibt den Speicher des alten Strings frei, erstellt eine Kopie82 des neuen Strings und lsst den Zeiger des Optionswertes auf die Adresse des kopierten Strings verweisen.
siehe Anhang A-4 Die dafr verwendete Funktion my_strdup() ist ebenfalls im Anhang A-5 einzusehen. Es war notwendig, die Funktion strdup() selbst zu implementieren, da sie nicht zum genutzten ANSI Standard gehrt.
82 81

54

4.3. Implementierung von libspirit

2 3 4 5 6 7 8 9 10 11 12 13 14

15 16 17 18 19 20 21 22 23 24

SPIRITcode Spirit_setopt(struct SpiritHandle *data, SPIRIToption option, va_list param) { SPIRITcode result = SPIRITE_OK; /* Usage exaples: * bool * long tmp = va_arg(param, long); * data.my_val = (bool) (0 != tmp); * long * data.my_val = va_arg(param, long); * string * result = setstropt(&data->my_str, va_arg(param, char *)); */ switch (option) { case SPIRITOPT_SSL_SIPHER_TYPE: result = setstropt(&data->curl.ssl_cipher_type, va_arg(param, char *)); break; /* [...] */ default: /* unknown tag and its companion, just ignore: */ result = SPIRITE_UNKNOWN_OPTION; break; } return result; } Quelltext 4.13: Implementierung des Setzens von Informationen

4.3.7.

Implementierung der Programmlogik

Die simplen Zugrie auf den REST-Dienst zum Abruf von Daten knnen in eine Funktion ausgelagert werden. Sofern nur ein GET als Befehl verwendet wird, kann die Funktion
Spirit_initCurlConnectionForUrl()

verwendet werden. Der Quelltext dieser Funktion bendet sich im Anhang A-6. 55

4.3. Implementierung von libspirit Als Parameter wird ein gltiges Handle der libspirit, ein Handle der libcurl, der Pfad zu der angesprochenen Funktion (z. B. news/allNews) unter der im libspirit Handle angegebenen Basis-URL und eine Speicherstruktur, welche einen Zeiger auf die empfangenen Daten und eine Ganzzahl mit deren Gre enthlt, angegeben. Die Funktion setzt einige libcurl Optionen selbst. Andere werden in Abhngigkeit von den ber das Einstellungs-System kongurierten Optionen gesetzt. Die Funktion liefert ein zum Einsatz bereites Handle der libcurl. Eine Beispielfunktion (Quelltext 4.14), welche der Endanwender verwenden kann, um alle Neuigkeiten auszugeben, soll demonstrieren, dass die Implementierung von Endanwenderfunktionen durch die weitere Abstraktion in wenigen Zeilen Code mglich ist. Die Zeilen 12 bis 14 des Quelltextes 4.14 gengen, um die Anfrage an den REST-Dienst zu stellen und ihr Ergebnis in chunk.memory zu speichern. Die Auswertung der JSON Daten wird in der folgenden Unterfunktion durchgefhrt:
Spirit_printNewsFromJsonString()

Der Quelltext dieser Unterfunktion ist im Anhang A-7 einzusehen. Mit der Funktion yajl_tree_parse() wird der vom REST-Dienst empfangene String eingelesen, ausgewertet und der Wurzelknoten zurckgegeben. Mit Hilfe der Funktion yajl_tree_get() kann ausgehend von einem Knoten ein angegebener Pfad (in Form einer mit einem NULL-Zeiger terminierten Liste aus Strings) zu einem tieferliegenden Knoten abgegangen werden. Mit diesen Funktionen kann das Auslesen der JSON-Daten realisiert werden.
1 2 3

LIBSPIRIT_API SPIRITcode spirit_news_print_all(SPIRIT *handle) { struct SpiritHandle *data = (struct SpiritHandle *)handle; CURL *curl;

56

4.3. Implementierung von libspirit


4 5 6 7 8 9 10 11 12

struct MemoryStruct chunk; SPIRITcode res = SPIRITE_OK; chunk.memory = malloc(1); /* will be grown */ chunk.size = 0; /* no data at this point */ if (chunk.memory == NULL) return SPIRITE_OUT_OF_MEMORY; res = Spirit_initCurlConnectionForUrl(handle, &curl, "news", &chunk); curl_easy_perform(curl); curl_easy_cleanup(curl); if (res != SPIRITE_OK) { if (chunk.memory) free(chunk.memory); return res; } res = Spirit_printNewsFromJsonString(chunk.memory); if (chunk.memory) free(chunk.memory); return res; } Quelltext 4.14: Beispiel einer Endanwenderfunktion, um alle Neuigkeiten auszugeben

13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28

4.3.8.

Die aktualisierte Testapplikation

Nach der Implementierung von Handle-, Einstellungs- und Fehler-System sowie der darauf folgenden Implementierung von Programmlogik fr den Endanwender ist eine Aktualisierung der Testapplikation ntig. Alle zuvor implementierten Funktionen werden in der neuen Testapplikation (Quelltext 4.15) getestet: Das Handle-System wird zusammen mit den Funktionen zum nen und Schlieen von Handles verwendet (Zeilen 7, 12, 14, 17 und 18). 57

4.3. Implementierung von libspirit Die Zeile 14 ist ein einfaches Beispiel fr den Aufruf einer Endanwenderfunktion. Das Fehler-System wird durch die berprfung und Umwandlung von Rckgabecodes in Strings verwendet (Zeile 16). Eine Option wird mit dem Einstellungs-System gendert (Zeile 17).

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17

#include #include #include #include

<stdio.h> <stdlib.h> <libspirit/spirit_news.h> <libspirit/spirit_error.h>

int main(void) { SPIRIT *spirit_handle; SPIRITcode res; printf("-- libspirit test application --\n"); spirit_handle = spirit_init("https://spirit.fh-schmalkalden.de/"); printf("\n==================== ALL NEWS ====================\n"); res = spirit_news_print_all(spirit_handle); printf("\n==================================================\n"); printf("\n\tSPIRIT RESULT %i: %s\n", res, spirit_errstring(res)); spirit_setopt(spirit_handle, SPIRITOPT_HEADER_ACCEPT, "Accept: application/xml"); spirit_cleanup(spirit_handle); return EXIT_SUCCESS; } Quelltext 4.15: Der aktualisierte Quelltext der Testapplikation

18 19 20 21

4.3.9.

Installation auf verbreiteten Betriebssystemen

Zum Erstellen von Installationen wird von CMake eine simple Lsung angeboten. Wird das Paket CPack inkludiert und mindestens die CPack-Variablen

58

4.3. Implementierung von libspirit der Version und des Ortes der Lizenzdatei gesetzt, so wird ein Installationspaket in Abhngigkeit des Betriebssystems erstellt. Dieses Installationspaket enthlt sowohl alle mit install angegebenen Dateien als auch eventuell zur Ausfhrung ntige Fremdbibliotheken, welche automatisch durch das Paket InstallRequiredSystemLibraries hinzugefgt werden.
1 2 3 4 5 6 7 8 9 10 11 12

# add the install targets install (TARGETS libspirit DESTINATION lib) install (FILES ${PUB_HDRS} DESTINATION include/libspirit)

# build a CPack driven installer package include (InstallRequiredSystemLibraries) set (CPACK_RESOURCE_FILE_LICENSE "${CMAKE_CURRENT_SOURCE_DIR}/License.txt") set (CPACK_PACKAGE_VERSION_MAJOR "${libspirit_VERSION_MAJOR}") set (CPACK_PACKAGE_VERSION_MINOR "${libspirit_VERSION_MINOR}") include (CPack) Quelltext 4.16: Zustzliche Zeilen, um ber CMakeLists.txt ein Installationspaket erstellen zu lassen

Der Quelltext 4.16 beinhaltet alle bentigten Zeilen, um die CMakeLists.txt um die Erstellung eines Installationspaketes zu erweitern. Die Art des Installationspaketes hngt dabei vom Betriebssystem ab. Unter Windows wird zur Erstellung eines Installers das Open Source Projekt Nullsoft Scriptable Install System83 (NSIS) bentigt. Wird der Installer erstellt, so wird eine Installationsanwendung im gewohnten Stil erzeugt. Diese kann mit dem Visual Studio Projektes PACKAGE erstellt werden. Unter Mac OS X wird eine .dmg-Datei erstellt, welche ebenfalls wie eine gewohnte Installationsanwendung gehandhabt wird. Diese kann sowohl in Xcode durch Erstellen des Targets package als auch mit den GNU Autotools ber den Befehl make package erzeugt werden (wie bei allen Unix-basierten Betriebssystemen).
83

http://nsis.sourceforge.net/

59

4.3. Implementierung von libspirit Sollte CMake kein spezielles Installationsformat fr eine Linux-Distribution untersttzen, wird ein Archiv erstellt, welches die zu installierenden Dateien enthlt.

60

5.

Zusammenfassung und Ausblick

Im Rahmen der vorliegenden Arbeit wurde eine portable, dynamische Programmbibliothek entwickelt. Diese Programmbibliothek greift auf eine zentrale Schnittstelle des spirit@fhs-Projektes zu (REST-Dienst). Da sich alle Komponenten des spirit@fhs-Projektes dauerhaft in der Entwicklung benden, musste auf die Erweiterbarkeit der entwickelten Programmbibliothek geachtet werden. Abstraktion war die Grundlage vieler Entwurfsmuster. Der Zugri auf den REST-Dienst sowie die Benutzung der entwickelten Programmbibliothek selbst wurden durch einen hohen Abstraktionsgrad im Quelltext so trivial wie mglich gestaltet. Des Weiteren konnte die plattformunabhngige Erstellung durch Verwendung des Erstellungssystems CMake abstrahiert werden, was die Erstellung vereinfacht und die Erweiterbarkeit aufrecht erhlt. Fremdbibliotheken wurden verwendet, um nicht triviale, bentigte Funktionalitt in die Programmbibliothek aufzunehmen. Die Fremdbibliotheken stellten eine Abstraktion der von ihnen angebotenen Funktionalitten dar und vereinfachten somit die Entwicklung der Programmbibliothek. Die portable Entwicklung unter der Programmiersprache C zeichnete sich mehr durch Richtlinien als durch Sprachelemente aus. Der von C gelieferte Abstraktionsgrad ist aufgrund ihrer Hardwarenhe gering. Folglich musste die Portabilitt vom Quelltext des Programmierers ausgehen.

61

5. Zusammenfassung und Ausblick Nachfolgende Arbeiten sollten sich darauf konzentrieren, die Erweiterbarkeit und die einfache Handhabung der Programmbibliothek libspirit noch zu steigern. Sollte sich der in Entwicklung bendliche REST-Dienst im Angebotsumfang vergrern, muss die libspirit an das Angebot angepasst werden. Diese Anpassung htte zur Folge, dass der komplette Quelltext neu kompiliert und die libspirit neu erzeugt werden muss. Es wre denkbar, eine Schnittstelle ber eine Skriptsprache in die libspirit einzubauen. Dadurch knnten Endanwenderfunktionen ausgelagert und separat erweitert bzw. aktualisiert werden. Um die Anwenderfreundlichkeit zu steigern, knnten Projektvorlagen fr Entwicklungsumgebungen erstellt werden. Diese Projektvorlagen knnten Endanwendern ermglichen, ein Projekt zu erstellen, welches die libspirit ohne ntiges Vorwissen zum Einfgen von dynamischen Programmbibliotheken verwendet.

62

Eidesstattliche Erklrung
Ich versichere an Eides Statt durch meine eigenhndige Unterschrift, dass ich die vorliegende Arbeit selbststndig und ohne fremde Hilfe angefertigt habe. Alle Stellen, die wrtlich oder dem Sinn nach auf Publikationen oder Vortrgen anderer Autoren beruhen, sind als solche kenntlich gemacht. Ich versichere auerdem, dass ich keine andere als die angegebene Literatur verwendet habe. Diese Versicherung bezieht sich auch auf alle in der Arbeit enthaltenen Zeichnungen, Skizzen, bildlichen Darstellungen und dergleichen. Die Arbeit wurde bisher keiner anderen Prfungsbehrde vorgelegt und auch noch nicht verentlicht.

Schmalkalden, den 27. September 2011


Ort, Datum Robert Worgul

63

Abbildungsverzeichnis
2.1 2.2 3.1 Erzeugung eines lauhigen Programms . . . . . . . . . . . . . Erstellungsprozess unter Verwendung von CMake . . . . . . . Abstraktionshierarchie der Erstellungssysteme . . . . . . . . . 9 25 33

64

Tabellenverzeichnis
2.1 2.2 4.1 Standard Header von ANSI C . . . . . . . . . . . . . . . . . . . Verwendung der HTTP Methoden fr REST . . . . . . . . . . Testumgebungen fr die zu entwickelnde Software libspirit . . 6 21 41

65

Quelltextverzeichnis
2.1 2.2 2.3 2.4 2.5 2.6 2.7 4.1 4.2 4.3 4.4 4.5 4.6 4.7 4.8 4.9 4.10 4.11 4.12 4.13 Beispiel der Prprozessorersetzung . . . . . . . . . . . . . . . . Selbststndiges Aktivieren von active linkage . . . . . . . . . . Threadsicherheit bei Rckgabewerten . . . . . . . . . . . . . . Auszug aus CMakeLists.txt . . . . . . . . . . . . . . . . . . . . . Der Kongurationsrohling cong.h.in . . . . . . . . . . . . . . . Die Kongurationsdatei cong.h bei gesetzten CMake Variablen Die Kongurationsdatei cong.h bei nicht gesetzten CMake Variablen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . CMakeLists.txt . . . . . . . . . . . . . . . . . . . . . . . . . . . . libspiritcong.h.in . . . . . . . . . . . . . . . . . . . . . . . . . . spirit.h . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . libspirit.c . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . nderungen an der CMakeList.txt um YAJL und libcurl zu integrieren . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . libspirit.c mit genderten Testfunktionen fr die Fremdbibliotheken . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . testapp.c . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Die Fehlerwerte in einer fr den Programmierer lesbaren Form Funktion zur Umwandlung von Fehlercodes in Strings . . . . . Struktur zur Speicherung der Sitzungsinformationen eines Handles . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Erstellung eines Aufzhlungstyps zur Unterscheidung von Optionen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Fixiert auf drei Parameter, trotz variabler Parameterliste . . . Implementierung des Setzens von Informationen . . . . . . . . 9 14 16 28 29 29 29 42 43 44 45 46 47 49 50 51 52 53 54 55 66

Quelltextverzeichnis 4.14 Beispiel einer Endanwenderfunktion, um alle Neuigkeiten auszugeben . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4.15 Der aktualisierte Quelltext der Testapplikation . . . . . . . . . 4.16 Zustzliche Zeilen, um ber CMakeLists.txt ein Installationspaket erstellen zu lassen . . . . . . . . . . . . . . . . . . . . . . .

56 58 59

67

Glossar
ANSI American National Standards Institute eine amerikanische Normungsinstitution. Besagt, dass zwei Zeichenfolgen nicht gleich sind, wenn sie sich lediglich in Groschreibung bzw. Kleinschreibung unterscheiden. CRUD steht fr die Operationen Create, Read, Update und Delete, welche die Standardfunktionen der persistenten Datenhaltung ausmachen. Bietet eine Kompatibilittsschicht unter Windows, welche die Unix-API zur Verfgung stellt. Eine portable Open Source Entwicklungsumgebung. Eine Open Source Software zur Versionsverwaltung. Ein freies Betriebssystem.

case sensitive

CRUD

Cygwin

Eclipse IDE

Git

GNU

68

Glossar

HTML

Hypertext Markup Language eine textbasierte Auszeichnungssprache fr Webinhalte. Hypertext Transfer Protocol ein Netzwerkprotokoll. International Organization for Standardization Insight Segmentation and Registration Toolkit ein Open Source cross-platform System zur Bildanalyse (u. a. fr medizinische Zwecke). JavaScript Object Notation ein kompaktes Datenformat in Textform. Eine Open Source Programmbibliothek zum Zugri auf verschiedene NetzwerkTransportprotokolle. Eine in der Programmierung oft verwendete Benennungskonvention, bei der ein Begri aus mehreren Worten, auf Grund des Verbotes von Leerzeichen, durch groe Anfangsbuchstaben im zusammengesetzten Wort lesbarer gestaltet wird (z. B. printAllNews).

HTTP

ISO ITK

JSON

libcurl

lowerCamelCase

Microsoft Visual Studio Eine proprietre Entwicklungsumgebung fr Windows.

69

Glossar

MIME

Multipurpose Internet Mail Extensions ein Standard um das Datenformat von E-Mails anzugeben. Minimal SYStem Windowsportierung der Unix-Shell fr MinGW In der Regel nachts und automatisch kompilierter Versionsstand eines Projektes. Ein Open Source Texteditor mit Syntaxhervorhebung. Nullsoft Scriptable Install System ein freies System zum Erstellen von Installationen unter Windows. Uniform Resource Identier eine Zeichenkette, welche eine Ressource im Internet identiziert. Eine Auszeichnungssprache zur Darstellung hierarchischer strukturierter Daten in Textform. Eine Open Source Programmbibliothek zur Handhabung von Daten im JSON-Format.

MSYS

Nightly Build

Notepad++

NSIS

URI

XML

YAJL

70

Literaturverzeichnis
[App98] Appel, A.W.: Modern compiler implementation in ML. Cambridge University Press, 1998. [Bre07] Breymann, U.: C++: Einfhrung und professionelle Programmierung. Hanser, 2007. [Cal10] Calcote, J.: Autotools: A Practioners Guide to GNU Autoconf, Automake, and Libtool. No Starch Press Series. No Starch Press, 2010. [Eck00] Eckel, B. and Allison, C.: Thinking in C++.: Introduction to standard C++. Thinking in C++. Prentice Hall, 2000. [Eng07] Engels, J.: CMake Tutorial. http://www-flc.desy.de/ ldcoptimization/documents/talks/CMake_Tutorial.pdf, Deutsches Elektronen-Synchrotron (DESY), 2007. [Online; abgerufen am 9. September 2011]. [Fie00] Fielding, R.: Architectural Styles and the Design of Network-based Software Architectures. Doktorarbeit, University of Califormia, Irvine, USA, 2000. [Hoo05] Hook, B.: Write portable code: an introduction to developing software for multiple platforms. No Starch Press Series. No Starch Press, 2005. [ISO03] ISO/IEC 9899: Rationale for International Standard Programming Languages C. Revision 5.10, 2003. 71

Literaturverzeichnis [Ker88] Kernighan, B.W. and Ritchie, D.M.: The C Programming Language. Prentice-Hall software series. Prentice Hall, 1988. [Kit11a] Kitware Inc.: CMake Cross Platform Make. http://www. cmake.org/cmake/project/about.html, 2011. [Online; abgerufen am 10. September 2011]. [Kit11b] Kitware Inc.: CMake 2.8 Documentation. http://www.cmake. org/cmake/help/cmake-2-8-docs.html, 2011. [Online; abgerufen am 10. September 2011]. [Kit11c] Kitware Inc.: CMake Wiki Cross Compiling. http: //www.cmake.org/Wiki/index.php?title=CMake_Cross_ [Online; abgerufen am 10. Compiling&oldid=37811, 2011. September 2011]. [Kit11d] Kitware Inc.: CMake Wiki How To Write Platform Checks. http://www.cmake.org/Wiki/index.php?title=CMake: How_To_Write_Platform_Checks&oldid=16288, 2011. [Online; abgerufen am 10. September 2011]. [Mic01] Michaels, D. and Mikes, S.: Building a cross-platform C library. IBM developerWorks, 2001. [Nad08] Nadobnyh, E.: Folien der Vorlesung Prozedurale Programmierung, 2008. [Ric07] Richardson, L. and Ruby, S.: RESTful web services. OReilly Series. OReilly, 2007. [Rit93] Ritchie, Dennis M.: The Development of the C Language. http: //cm.bell-labs.com/cm/cs/who/dmr/chist.html, 1993. [Online; abgerufen am 24. August 2011]. [Sch05] Schellong, H.: Moderne C-Programmierung: Kompendium und Referenz. Xpert. press Series. Springer, 2005.

72

Literaturverzeichnis [Val00] Valero, M. and Kumar, V.K.P. and Vajapeyam, S.: High performance computing - HiPC 2000: 7th International Conference, Bangalore, India, December 2000 : proceedings. Lecture notes in computer science. Springer, 2000.

73

Anhang
A-1 Simple Funktionstests der Fremdbibliotheken in der libspirit.c
#include <stdio.h> #include <string.h> #include <stddef.h> #include #include #include #include <libspiritconfig.h> <yajl/yajl_tree.h> <curl_config.h> <curl/curl.h>

#include <libspirit/spirit.h>

LIBSPIRIT_API int fndlltest(void) { static unsigned char fileData[65536]; size_t rd; yajl_val node; char errbuf[1024]; FILE *fp; /* null plug buffers */ fileData[0] = errbuf[0] = 0; /* read the entire config file */ if((fp = fopen("sample.config", "r"))==NULL) { printf("Cannot open file.\n"); return 1; }

74

A-1 Simple Funktionstests der Fremdbibliotheken in der libspirit.c


rd = fread((void *) fileData, 1, sizeof(fileData) - 1, fp); puts(fileData); /* file read error handling */ if (rd == 0 && !feof(fp)) { fprintf(stderr, "error encountered on file read\n"); return 1; } else if (rd >= sizeof(fileData) - 1) { fprintf(stderr, "config file too big\n"); return 1; } /* we have the whole config file in memory. lets parse it ... */ node = yajl_tree_parse((const char *) fileData, errbuf, sizeof(errbuf)); /* parse error handling */ if (node == NULL) { fprintf(stderr, "parse_error: "); if (strlen(errbuf)) fprintf(stderr, " %s", errbuf); else fprintf(stderr, "unknown error"); fprintf(stderr, "\n"); return 1; } /* ... and extract a nested value from the config file */ { const char * path[] = { "Logging", "timeFormat", (const char *) 0 }; yajl_val v = yajl_tree_get(node, path, yajl_t_string); if (v) printf("%s/%s: %s\n", path[0], path[1], YAJL_GET_STRING(v)); else printf("no such node: %s/%s\n", path[0], path[1]); } yajl_tree_free(node); return 0; } LIBSPIRIT_API int curltest(char* url) {

75

A-1 Simple Funktionstests der Fremdbibliotheken in der libspirit.c


CURL *curl; CURLcode res; curl = curl_easy_init(); if(curl) { curl_easy_setopt(curl, CURLOPT_URL, url); res = curl_easy_perform(curl); printf("res = %i -- %s\n", res, curl_easy_strerror(res)); /* always cleanup */ curl_easy_cleanup(curl); } return 0; }

76

A-2 CMakeLists.txt des CMake Projektes fr die Testapplikation

A-2

CMakeLists.txt des CMake Projektes fr die Testapplikation

cmake_minimum_required (VERSION 2.8) set (SRCS src/testapp.c) set (HDRS ) set (PUB_HDRS ) set (EXTRA_LIBS libspirit) # set up some paths set (incDir ${CMAKE_CURRENT_BINARY_DIR}/../include) include_directories (${incDir}) # add the executable add_executable(testapp ${SRCS} ${HDRS} ${PUB_HDRS}) target_link_libraries (testapp ${EXTRA_LIBS}) #copy our shared library to executable so it can run if (CMAKE_MAJOR_VERSION GREATER 2 OR (CMAKE_MAJOR_VERSION EQUAL 2 AND CMAKE_MINOR_VERSION GREATER 8) OR (CMAKE_MAJOR_VERSION EQUAL 2 AND CMAKE_MINOR_VERSION EQUAL 8 AND CMAKE_PATCH_VERSION GREATER 3)) add_custom_command(TARGET testapp POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy $<TARGET_FILE_DIR:libspirit>/$<TARGET_FILE_NAME:libspirit> $<TARGET_FILE_DIR:testapp>/$<TARGET_FILE_NAME:libspirit>) else() message(WARNING "Cant copy libspirit to the directory of the testapp binary file because your CMake Version is too low (less 2.8.4). If testapp wont run you should copy the library by yourself.") endif()

77

A-3 Funktionen zur Initialisierung und Schlieung eines Handles

A-3

Endanwenderfunktionen zur Initialisierung und Schlieung eines Handles

LIBSPIRIT_API SPIRIT *spirit_init(const char *base_url) { struct SpiritHandle *handle; handle = calloc(1, sizeof(struct SpiritHandle)); if (handle == NULL) return NULL; Spirit_initLibcurlSettings(&handle->curl); handle->base_url = my_strdup(base_url); return handle; } #ifndef my_free #define my_free(x) if (x != NULL) free(x); #endif LIBSPIRIT_API void spirit_cleanup(SPIRIT *handle) { struct SpiritHandle *data = (struct SpiritHandle *)handle; my_free((&data->curl)->header_accept) my_free((&data->curl)->ssl_cipher_type) my_free((&data->curl)->user_agent) my_free(data->base_url) my_free(data) }

SPIRITcode Spirit_initLibcurlSettings(struct LibcurlSettings *curl) { SPIRITcode res = SPIRITE_OK; curl->header_accept = my_strdup("Accept: application/json"); curl->ssl_cipher_type = my_strdup("DHE-RSA-AES256-SHA"); curl->user_agent = my_strdup(libspirit_USER_AGENT); /* cmake generated makro */ return res; }

78

A-4 Interface-Funktion zum Setzen einer Option

A-4

Interface-Funktion zum Setzen einer Option

#undef spirit_setopt LIBSPIRIT_API SPIRITcode spirit_setopt(SPIRIT *handle, SPIRIToption tag, ...) { va_list arg; struct SpiritHandle *data = handle; SPIRITcode ret; if(!handle) return SPIRITE_BAD_FUNCTION_ARGUMENT; va_start(arg, tag); ret = Spirit_setopt(data, tag, arg); va_end(arg); return ret; }

79

A-5 Funktion zum Kopieren von Optionen im String-Format

A-5

Funktion zum Kopieren von Optionen im String-Format

char *my_strdup(const char *s) { char *p = malloc(strlen(s) + 1); if (p) { strcpy(p, s); } return p; } static SPIRITcode setstropt(char **charp, char * s) { if (*charp) { free(*charp); *charp = (char *) NULL; } if (s) { s = my_strdup(s); if (!s) return SPIRITE_OUT_OF_MEMORY; *charp = s; } return SPIRITE_OK; }

80

A-6 Initialisierung der libcurl fr die REST-Anfrage

A-6

Initialisierungsfunktion fr eine libcurl-Abfrage an den REST-Dienst

SPIRITcode Spirit_initCurlConnectionForUrl(struct SpiritHandle *spirit, CURL **curl_handle, const char *url, struct MemoryStruct *chunk) { struct curl_slist *slist = NULL; char *request_url; SPIRITcode res = SPIRITE_OK; CURL *curl; request_url = calloc(strlen(spirit->base_url) + strlen(url) + 1, sizeof(char)); if (request_url == NULL) return SPIRITE_OUT_OF_MEMORY; strcpy(request_url, spirit->base_url); strcpy(&(request_url[strlen(request_url)]), url); curl = curl_easy_init(); if (curl) { curl_easy_setopt(curl, CURLOPT_URL, request_url); curl_easy_setopt(curl, CURLOPT_VERBOSE, 0L); curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 1); curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L); curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0L); curl_easy_setopt( curl, CURLOPT_USERAGENT, spirit->curl.user_agent); slist = curl_slist_append(slist, spirit->curl.header_accept); curl_easy_setopt(curl, CURLOPT_HTTPHEADER, slist); curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteMemoryCallback); curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)chunk); } *curl_handle = curl; return res;

81

A-6 Initialisierung der libcurl fr die REST-Anfrage


} struct MemoryStruct { char *memory; size_t size; }; static size_t WriteMemoryCallback(void *ptr, size_t size, size_t nmemb, void *data) { size_t realsize = size * nmemb; struct MemoryStruct *mem = (struct MemoryStruct *) data; mem->memory = realloc(mem->memory, mem->size + realsize + 1); if (mem->memory == NULL) { /* out of memory! */ mem->size = 0; return 0; } memcpy(&(mem->memory[mem->size]), ptr, realsize); mem->size += realsize; mem->memory[mem->size] = 0; return realsize; }

82

A-7 Unterfunktion zur Ausgabe von Neuigkeiten im JSON-Format

A-7

Unterfunktion zur Auswertung der JSON-Daten und zur Ausgabe von Neuigkeiten

static SPIRITcode Spirit_printNewsFromJsonString(const char* json) { yajl_val node; char errbuf[1024]; SPIRITcode res = SPIRITE_OK; node = yajl_tree_parse((const char *) json, errbuf, sizeof(errbuf)); if (node == NULL) return SPIRITE_JSON_PARSE_ERROR; { /* print news */ const char * path[] = { "news", (const char *) 0 }; yajl_val newsNode = yajl_tree_get(node, path, yajl_t_array); if (YAJL_IS_ARRAY(newsNode)) { yajl_val *allNews = newsNode->u.array.values; unsigned int i; for (i = 0; i < newsNode->u.array.len; ++i) { const char * pathTitle[] = { "title", (const char *) 0 }; const char * pathContent[] = { "content", (const char *) 0 }; yajl_val title = yajl_tree_get(*(allNews), pathTitle, yajl_t_string); yajl_val content = yajl_tree_get(*(allNews), pathContent, yajl_t_string); if (YAJL_IS_STRING(title) && YAJL_IS_STRING(content)) { printf("--[ %s ]", YAJL_GET_STRING(title)); fprintNChars(stdout, -, 80 strlen(YAJL_GET_STRING(title)) - 6); printf("\n%s\n", YAJL_GET_STRING(content)); fprintNChars(stdout, -, 80); printf("\n\n"); }

83

A-7 Unterfunktion zur Ausgabe von Neuigkeiten im JSON-Format


++allNews; } } else return SPIRITE_JSON_NODE_NOT_FOUND; } yajl_tree_free(node); return res; }

84