Idiomatische Möglichkeit, in Erlang geschriebene Befehlszeilenwerkzeuge zu versenden

9

Das Problem

Die meisten Artikel und Bücher über Erlang I könnten den Schwerpunkt auf die Erstellung lang laufender serverähnlicher Anwendungen legen, die den Prozess der Erstellung von Befehlszeilentools nicht abdecken.

Ich habe ein rebar3-Projekt mit mehreren Anwendungen, das aus drei Anwendungen besteht:

  • myweb - ein cowboy basierter Webservice;
  • mycli - ein Befehlszeilenwerkzeug zum Vorbereiten von Assets für myweb ;
  • mylib - Eine Bibliothek, die von myweb und mycli verwendet wird, hängt von einem NIF ab.

Als Ergebnis des Builds möchte ich solche Artefakte erhalten:

  1. eine ausführbare Datei für den Webpart, der HTTP-Anfragen bedienen soll;
  2. ein ausführbares Befehlszeilentool für die Anlagenvorbereitung;
  3. eine Reihe von Bibliotheken, die von den oben genannten verwendet werden.

Anforderungen

  • cli sollte sich wie ein normales, nicht-interaktives Kommandozeilenwerkzeug verhalten: handle arguments, behandle stdin / stdout, gib einen Exit-Code ungleich Null bei einem Fehler zurück, etc;
  • Sowohl Server als auch CLI sollten NIFs verwenden können;
  • Es sollte einfach sein, die Artefakte als eine Menge von deb / rpm-Paketen zu verpacken, so dass sowohl Server als auch CLI gemeinsame Abhängigkeiten wiederverwenden sollten.

Bisherige Versuche

Erstellen eines E-Skripts

Eine der Möglichkeiten, die ich in freier Wildbahn gesehen habe, ist das Erstellen einer eigenständigen Escript-Datei. Mindestens rebar und relx tun dies. Also habe ich es versucht.

Vorteile:

  • unterstützt Befehlszeilenargumente;
  • gibt im Falle von Fehlern einen Nicht-Null-Beendigungscode zurück.

Nachteile:

  • bettet alle Abhängigkeiten in eine einzige Datei ein und macht die Wiederverwendung von mylib ;
  • unmöglich
  • da *.so -Dateien in die resultierende Escript-Datei eingebettet werden, können sie zur Laufzeit nicht geladen werden, daher funktionieren NIFs nicht (siehe Erlang Rebar escriptize & amp; nifs );
  • rebar3 escriptize behandelt Abhängigkeiten nicht gut (siehe Fehler 1139 ).

Unbekannte:

  • sollte die cli-App eine richtige OTP-Anwendung werden;
  • sollte es einen Überwachungsbaum haben;
  • sollte es überhaupt angefangen werden;
  • Wenn ja, wie halte ich es an, wenn die Assets verarbeitet wurden?

Erstellen eines Releases

Eine andere Möglichkeit zum Erstellen eines Befehlszeilenwerkzeugs wurde in einem Wie starte ich: Erlang Artikel von Fred Hebert beschrieben / p>

Vorteile:

  • Jede der Abhängigkeitsanwendungen wird in ihr eigenes Verzeichnis geladen, wodurch sie einfach geteilt und verpackt werden können.

Nachteile:

  • Es gibt keinen definierten Einstiegspunkt wie escript main/1 ;
  • Folglich müssen sowohl die Befehlszeilenargumente als auch der Beendigungscode manuell behandelt werden.

Unbekannte:

  • wie man die cli OTP-App nicht interaktiv modelliert;
  • Wie kann ich die App stoppen, wenn die Assets verarbeitet wurden?

Keiner der obigen Ansätze scheint bei mir zu funktionieren.

Es wäre das Beste aus beiden Welten: Holen Sie sich die Infrastruktur, die von escript zur Verfügung gestellt wird, wie% Co_de% Einstiegspunkt, Kommandozeilenparameter und Exit-Code-Behandlung, während immer noch eine schöne Verzeichnisstruktur, die einfach zu verpacken ist behindert nicht die Verwendung von NIFs.

    
Ihor Kaharlichenko 01.04.2016, 14:34
quelle

2 Antworten

3

Unabhängig davon, ob Sie eine lange Daemon-ähnliche Anwendung in Erlang oder einen CLI-Befehl starten, benötigen Sie immer Folgendes:

  1. erts application - die VM und der Kernel in einer bestimmten Version
  2. Erlang OTP-Anwendungen
  3. Abhängigkeiten Ihrer Anwendungen
  4. CLI-Einstiegspunkt

Dann muss der CLI-Einstiegspunkt in jedem Fall die Erlang-VM starten und den Code ausführen, den er in einer bestimmten Situation ausführen soll. Dann wird es entweder beendet oder weiter ausgeführt - das spätere für eine lang laufende Anwendung.

Der CLI-Einstiegspunkt kann alles sein, was eine Erlang-VM startet, z. Ein escript script, sh , bash , usw. Der offensichtliche Vorteil von escript gegenüber generischer Shell ist, dass escript bereits im Kontext einer Erlang VM ausgeführt wird. Stoppen der VM.

Sie können Erlang VM auf zwei Arten starten:

  1. Verwenden Sie systemweite Erlang VM
  2. Verwenden Sie eine eingebettete Erlang Version

Im ersten Fall liefern Sie weder erts noch irgendeine OTP-Anwendung mit Ihrem Paket, Sie machen nur eine bestimmte Erlang-Version zu einer Abhängigkeit für Ihre Anwendung. Im zweiten Fall liefern Sie erts und alle erforderlichen OTP-Anwendungen zusammen mit den Abhängigkeiten Ihrer Anwendung in Ihrem Paket.

Im zweiten Fall müssen Sie auch den Code-Stamm richtig einstellen, wenn Sie das Programm starten VM. Aber das ist ziemlich einfach, siehe das Skript erl , mit dem Erlang die systemweite VM startet:

%Vor%

Dies kann durch Skripte gehandhabt werden, zum Beispiel das node_package Werkzeug, mit dem Basho seine Riak-Datenbank für alle wichtigen Pakete paketiert Betriebssysteme. Ich behalte meine eigene Gabel , die ich mit meinem eigenen Build-Tool namens builderl . Ich sage das nur, damit Sie wissen, dass Sie das auch gut können, wenn ich es anpassen konnte:)

Sobald die Erlang-VM gestartet wurde, sollte Ihre Anwendung in der Lage sein, alle Anwendungen zu laden und zu starten, die entweder mit Erlang oder mit Ihrer Anwendung geliefert werden (einschließlich der von Ihnen erwähnten Bibliothek mylib ). Hier einige Beispiele, wie dies erreicht werden könnte:

escript Beispiel

Siehe dieses Beispiel für builderl.esh , wie ich das Laden anderer handhabe Erlange Anwendungen von builderl . Das Skript escript setzt voraus, dass die Erlang-Installation relativ zu dem Ordner ist, aus dem sie ausgeführt wird. Wenn es Teil einer anderen Anwendung ist, wie zum Beispiel humbundee , ist das load_builderl.hrl include Datei kompiliert und lädt bld_load , was wiederum alle verbleibenden Module mit %Co_de% . Beachten Sie, wie ich Standard-OTP-Anwendungen verwenden kann, ohne anzugeben, wo sie sich befinden - bld_load:boot/3 wird von builderl ausgeführt und alle Anwendungen werden von dem Ort aus, an dem sie installiert wurden, geladen ( escript auf meinem System). Wenn von Ihrer Anwendung verwendete Bibliotheken, z. /usr/local/lib/erlang/lib/ , werden woanders installiert, alles, was Sie tun müssen, ist, diesen Ort dem Erlang-Pfad hinzuzufügen, z. mit mylib . Erlang lädt Module, die im Code verwendet werden, automatisch aus Ordnern, die der Codepfadliste hinzugefügt wurden.

eingebettetes Erlang

Dasselbe gilt jedoch, wenn es sich bei der Anwendung um eine ordnungsgemäße OTP-Version handelte, die unabhängig von der systemweiten Erlang-Installation installiert wurde. Das liegt daran, dass in diesem Fall das Skript von code:add_path ausgeführt wird, das zu dieser eingebetteten Erlang-Version gehört, und nicht zur systemweiten Version (selbst wenn es installiert ist). Daher kennt es den Speicherort aller Anwendungen, die zu dieser Version gehören (einschließlich Ihrer Anwendungen). Zum Beispiel macht escript genau das - in ihrem Paket enthalten sie eine eingebettete Erlang-Version , die ihr eigenes% co_de enthält % und alle abhängigen Erlang-Anwendungen. Auf diese Weise kann riak gestartet werden, ohne dass Erlang sogar auf dem Host-Betriebssystem installiert wird. Dies ist ein Auszug aus einem erts Paket auf FreeBSD:

%Vor%

riak / riak

Dies unterscheidet sich nicht wesentlich von dem oben Gesagten, abgesehen davon, dass Sie explizit die Funktion aufrufen müssen, die beim Starten der Erlang-VM ausgeführt werden soll (der Einstiegspunkt oder die sh -Funktion, wie Sie es genannt haben).

Betrachten Sie dieses Skript, das bash erzeugt, um eine Erlang-Anwendung zu starten, nur um eine bestimmte Aufgabe auszuführen (erzeugen Sie die Datei main ), nach der der Knoten heruntergefahren wird:

%Vor%

Dies ist ein ähnliches Skript, startet jedoch keinen spezifischen Code oder eine bestimmte Anwendung. Stattdessen wird eine korrekte OTP-Version gestartet. Welche Anwendungen gestartet werden und in welcher Reihenfolge, hängt von der Version ab (angegeben durch die Option builderl ).

%Vor%

In der Datei RELEASES können Sie bei Bedarf zusätzliche Pfade zu Ihren Anwendungen bereitstellen, z. B .:

%Vor%

In diesem Beispiel sind diese relativ, könnten aber absolut sein, wenn Ihre Anwendung an einem bekannten Standardstandort installiert wird. Dies ist auch nur erforderlich, wenn Sie die systemweite Erlang-Installation verwenden und zusätzliche Pfade hinzufügen müssen, um Ihre Erlang-Anwendungen zu lokalisieren, oder wenn sich Ihre Erlang-Anwendungen an einem nicht standardmäßigen Speicherort befinden (z. B. nicht im Ordner -boot ) , wie Erlang OTP erfordert). In einer ordnungsgemäß eingebetteten Erlang-Version, in der sich die Anwendungen im Code-Stammverzeichnis / vm.args befinden Ordner kann Erlang diese Anwendungen laden, ohne zusätzliche Pfade anzugeben.

Zusammenfassung und andere Überlegungen

Die Bereitstellung von Erlang-Anwendungen unterscheidet sich nicht wesentlich von anderen in Skriptsprachen geschriebenen Projekten, z. Ruby- oder Python-Projekte. All diese Projekte müssen sich mit ähnlichen Problemen befassen, und ich glaube, dass die Paketverwaltung jedes Betriebssystems auf die eine oder andere Weise damit zu tun hat:

  1. Erfahren Sie, wie Ihr Betriebssystem Pakete mit Laufzeitabhängigkeiten verarbeitet.

  2. Sehen Sie, wie andere Erlang-Anwendungen für Ihr Betriebssystem gepackt werden. Es gibt viele davon, die normalerweise von allen wichtigen Systemen verteilt werden: RabbitMQ, Ejabberd, Riak und andere. Laden Sie einfach das Paket herunter und entpacken Sie es in einen Ordner. Dann sehen Sie, wo alle Dateien abgelegt sind.

BEARBEITEN - Referenzieren Sie die Anforderungen

Zurück zu Ihren Anforderungen, haben Sie die folgenden Möglichkeiten:

  1. Installiere Erlang als OTP-Release systemweit, als eingebettetes Erlang oder als Tasche mit Anwendungen in einigen zufälligen Ordnern (sorry Rebar)

  2. Sie können mehrere Einstiegspunkte in Form von lib oder lib scripts haben, die eine Auswahl von Anwendungen aus der installierten Version ausführen. Beide funktionieren so lange, wie Sie den Code-Root und die Pfade zu diesen Anwendungen korrekt konfiguriert haben (wie oben beschrieben).

Dann müsste jede Ihrer Anwendungen: sh und escript in ihrem eigenen neuen Kontext ausgeführt werden, z. Starten Sie eine neue VM-Instanz und führen Sie die gewünschte Anwendung aus (aus der gleichen Erlang-Version). Im Falle von myweb kann der Einstiegspunkt ein mycli Skript sein, das einen neuen Knoten entsprechend der Version startet (ähnlich wie Riak). Im Fall von myweb kann der Einstiegspunkt ein sh sein, der die Ausführung beendet, sobald die Aufgabe abgeschlossen ist.

Es ist jedoch durchaus möglich, eine Aufgabe mit kurzer Laufzeit zu erstellen, die die VM auch dann verlässt, wenn sie von mycli gestartet wurde - siehe Beispiel oben. In diesem Fall benötigt escript separate Release-Dateien - die sh und mycli , um die VM zu booten. Und natürlich ist es auch möglich, eine langlaufende Erlang VM von script zu starten.

Ich habe ein Beispielprojekt zur Verfügung gestellt, das alle diese Methoden auf einmal verwendet: humbundee . Sobald es kompiliert ist, bietet es drei Zugriffspunkte:

  1. Die boot release.
  2. Die escript release.
  3. Die cmd humbundee .

Der erste wird verwendet, um den Knoten für die Installation zu starten und dann herunterzufahren. Die zweite wird verwendet, um eine lang laufende Erlang-Anwendung zu starten. Die dritte ist ein Build-Tool zum Installieren / Konfigurieren des Knotens. So sieht das Projekt nach der Erstellung der Freigabe aus:

%Vor%

Der Einstiegspunkt builder.esh verwendet die Programme escript und cmd sowie die Dateien deploy-0.0.1 , builderl-0.2.7 und einige OTP-Anwendungen. Der Standardeintrag cmd.boot verwendet alle Anwendungen außer cmd.script und humbundee . Dann verwendet das builderl escript die Anwendung deploy und builderl.esh . Alles aus derselben eingebetteten Erlang OTP-Installation.

    
Amiramix 01.04.2016, 17:15
quelle
0

Ein kleines Skript, das dann von "herkömmlichen" Modulen in den Code geht, könnte eine Lösung sein.

Als Beispiel wird erwartet, dass Concuerror als Befehlszeilenwerkzeug verwendet wird und ein Escript als Einstiegspunkt verwendet. Es behandelt Befehlszeilenargumente über getopt . Der Hauptcode befindet sich in regulären Erlang-Modulen, die im Pfad mit einfachen Argumenten zum Skript enthalten sind.

Soweit ich weiß, können NIFs dann mit regulären -onload -Attributen geladen werden (Concuerror verwendet keine NIFs).

    
aronisstav 01.04.2016 16:28
quelle

Tags und Links