Paktebaurichtlinien für OCaml

Dieses Dokument beschreibt die Konventionen und Gepflogenheiten für die korrekte Paketierung von OCaml-Modulen in Fedora. Es erhebt keinen Anspruch auf Vollständigkeit, sondern soll jene Praktiken festhalten, die sich in der Fedora-OCaml-Community bewährt haben.

Benennung

Der Basis-OCaml-Compiler heißt ocaml.

OCaml-Module, -Bibliotheken und -Syntaxerweiterungen sollten nach dem Schema ocaml-foo benannt werden. Beispiele hierfür sind: ocaml-extlib, ocaml-ssl.

Diese Namenskonvention gilt nicht für in OCaml geschriebene Anwendungen, die ihren normalen Namen behalten können. Beispiele hierfür sind: coccinelle, frama-c, virt-top.

Begründung: So werden sie in anderen Distributionen (Debian, PLD) benannt, und dies entspricht der Namensgebung von Perl / PHP / Python.

Paketierung von Bibliotheken

Hauptpaket

Damit OCaml-Skripte und die oberste Ebene eine Bibliothek verwenden können, sollte das Hauptpaket nur Dateien enthalten, die folgenden Kriterien entsprechen:

  • *.cma (enthält den Bytecode)

  • *.cmi (enthält die kompilierte Signatur)

  • *.so (falls vorhanden, enthält OCaml <→ C-Stubs)

  • META (die findlib-Beschreibung)

  • *.so.owner (falls vorhanden, wird von findlib verwendet)

  • eine Lizenzdatei (falls vorhanden), mit %license gekennzeichnet

  • .cmo-Dateien werden normalerweise nicht eingebunden. Es gibt eine Ausnahme: Wenn die .cmo-Datei zum Linken benötigt wird, muss sie eingebunden werden, damit die Bibliothek korrekt verlinkt werden kann.

Wenn das Paket *.so-Dateien enthält, sollten diese gemäß den Fedora-Paketbaurichtlinien keine RPATHs haben.

Der Paketierer sollte die Fußnote der META-Datei lesen: [findlib Reference Manual - META files.]. Falls keine META-Datei vorhanden ist, sollte der Paketierer eine erstellen, sie dem Paket beifügen und sie an die Upstream-Entwickler weiterleiten.

Begründung: OCaml unterstützt kein dynamisches Linken von Binärdateien, und selbst wenn es dies mit dem aktuellen Modul-Hash-System zur Formulierung strenger Typisierungsanforderungen tun würde, müsste bei nahezu jeder denkbaren Änderung an einer Bibliothek die Binärdatei neu kompiliert werden. OCaml-Skripte kommen dem dynamischen Linken am nächsten, da sie in der Regel nicht von einer bestimmten Version einer Bibliothek abhängen (dies funktioniert jedoch nur, weil die Skripte bei jeder Ausführung neu kompiliert werden).

-devel-Teilpaket

Das -devel-Teilpaket einer Bibliothek sollte alle weiteren Dateien enthalten, die für die Entwicklung mit der Bibliothek erforderlich sind. Normalerweise wären dies:

  • *.a (enthält den kompilierten Maschinencode)

  • *.cmxa (beschreibt den kompilierten Maschinencode)

  • *.cmx (falls vorhanden, ermöglicht es modulübergreifende Optimierungen)

  • *.mli (enthält die Signatur der Bibliothek)

  • *.cmt (enthält Typanmerkungen für kompilierten Code)

  • *.cmti (enthält Typanmerkungen für Schnittstellendateien)

  • .o-Dateien werden normalerweise nicht eingebunden. Es gibt eine Ausnahme: Wenn die Datei zum Verlinken benötigt wird (wie std_exit.cmx und std_exit.o in OCaml selbst), dann sollte sie eingebunden werden.

  • .ml-Dateien werden normalerweise nicht eingebunden. Eine Ausnahme besteht, wenn die Datei eine Modulsignatur beschreibt und keine entsprechende .mli-Datei existiert; in diesem Fall sollte die .ml-Datei eingebunden werden (beachten Sie, dass Debian hier toleranter ist und häufig *.ml-Dateien verteilt, die es Programmierern ermöglichen, einen Blick auf die Implementierung eines Moduls zu werfen).

Dokumentation, Beispiele und weitere für Entwickler nützliche Artikel können im -devel-Teilpaket enthalten sein. Die Lizenzdatei (die sich im Hauptpaket befindet) muss nicht erneut im -devel-Teilpaket enthalten sein.

Wenn das -devel-Teilpaket ausschließlich Dokumentationsdateien enthalten würde, kann der Paketierer nach eigenem Ermessen die Dokumentationsdateien im Hauptpaket ablegen und auf ein -devel-Teilpaket ganz verzichten.

Das Unterpaket -devel sollte die exakte Namens-Versions-Release-Kette des Hauptpakets (gemäß Fedora-Richtlinien) als Requires einbinden. Es sollte außerdem alle für die Entwicklung benötigten C-Bibliotheken einbinden, was mitunter ein explizites Requires erfordert. Beispielsweise benötigt ocaml-pcre-devel ein explizites Requires: pcre-devel, um für die Entwicklung verwendet werden zu können.

Der Grund für die Einbindung aller .cmx-Dateien ist, das diese Dateien auch für Module benötigt werden, die in .cmxa-Bibliotheken eingebunden sind, um modulübergreifende Optimierungen (Inlining, Konstantenweitergabe und direkte Funktionsaufrufe) zu ermöglichen. Die .o-Dateien werden nicht benötigt. [Aus einer privaten E-Mail von Alain Frisch]

-doc-Teilpaket

Sind die Dokumentationsdateien sehr groß, können sie gemäß den üblichen Fedora-Richtlinien in einem separaten -doc-Teilpaket abgelegt werden.

-data-Teilpaket

Falls das Paket übermäßig große Datendateien enthält, können diese gemäß den üblichen Fedora-Richtlinien in einem separaten -data-Teilpaket abgelegt werden.

Requires und Provides

Für jedes Modul, das Bibliothek A von einer anderen Bibliothek B verwendet, muss Bibliothek A ein Requires der Form ocaml(Modulname) = MD5hash enthalten

Analog dazu muss für jedes Modul, das Bibliothek A anderen Bibliotheken bereitstellen kann, Bibliothek A ein Provides derselben Form haben.

Eine Bibliothek muss von der genauen Version des OCaml-Compilers abhängen, zum Beispiel: ocaml(runtime) = 3.10.0

Die korrekten Requires und Provides sollten automatisch generiert werden.

Begründung: OCaml bietet keine Binärkompatibilität zwischen verschiedenen Versionen des Compilers (auch nicht zwischen Bugfixes). Darüber hinaus verwendet das Modulsystem einen Hashwert über die Schnittstelle und einige interne Modulmerkmale. Das bedeutet, dass eine Bibliothek oder ein Programm mit den identischen Modulen verknüpft werden muss, mit denen es kompiliert wurde. Die Zeilen Requires und Provides geben den Modulnamen und den Hashwert an, sodass RPM dieselben Anforderungen wie der OCaml-Linker selbst erzwingt. Weitere Details finden Sie in den weiterführenden Informationen am Ende dieser Seite.

Paketieren von Binärdateien

Die Regeln für das Paketieren von OCaml-Binärdateien unterscheiden sich nicht wesentlich von denen für das Paketieren gewöhnlicher Programme (siehe Paketbaurichtlinien).

Wenn das OCaml-Paket jedoch auch eine Bibliothek enthält, sollten Sie auch die vorstehend beschriebenen Regeln für das Paketieren von Bibliotheken befolgen.

Strippen von Binärdateien

Die Binärdateien sollten gemäß den üblichen Fedora-Paketbaurichtlinien gestrippt werden.

Es gibt eine Ausnahme, bei der eine Binärdatei nicht gestrippt werden sollte. Wenn das Paket mit ocamlc -custom kompiliert wurde, enthält es Bytecode, der durch das Strippen gelöscht wird, wodurch die Binärdatei unbrauchbar wird. Dies lässt sich leicht überprüfen: Wenn nach dem Entfernen von Bytecode jeder Versuch, die Binärdatei auszuführen, die Meldung No bytecode file specified erzeugt, wurde die Binärdatei so kompiliert und sollte nicht entfernt werden.

Bereitstellung bestmöglicher Binärdateien

Der Paketierer sollte, sofern möglich, native, kompilierte Binärdateien gegenüber Bytecode-kompilierten Binärdateien bevorzugen.

Nur-Bytecode-Architekturen

Der native OCaml-Code-Compiler (ocamlopt) enthält Codegeneratoren für gängige Architekturen, jedoch nicht für jede Architektur, die Fedora möglicherweise unterstützt. Auf solchen Architekturen sollte die Spec-Datei dennoch Bytecode-Bibliotheken und Binärdateien erzeugen.

Um zu prüfen, ob der native Compiler vorhanden ist, verwenden Sie das Makro %{ocaml_native_compiler}. Definieren Sie gegebenenfalls bedingte Abschnitte in %build, %install und %files. Zum Beispiel:

%build
make byte
%ifarch %{ocaml_native_compiler}
make opt
%endif

Um zu testen, ob Ihre Spec-Datei auf einer solchen Architektur funktioniert, entfernen Sie vorübergehend die Dateien /usr/bin/ocamlopt und /usr/bin/ocamlopt.opt während des Kompilierungsprozesses oder benennen sie sie um.

Grund: Im Abschnitt 2.3 der Debian-Paketbaurichtlinien wird dasselbe empfohlen.

Unnötige Dateien

Die folgenden Dateien sollten normalerweise nicht verteilt werden:

  • *.cmo-Objektdateien. Ausnahme: siehe oben.

  • .o für korrespondierendes .cmx. Ausnahme: siehe oben.

  • *.ml-Quellen. Ausnahme: siehe oben.

Sicherheitsprobleme in OCaml-Bibliotheken

Wenn in einer OCaml-Bibliothek ein Sicherheitsproblem auftritt, müssen alle Bibliotheken und Binärdateien, die von ihr abhängen, neu kompiliert werden.

OCaml-Skripte müssen nicht geändert werden (es sei denn, die Behebung des Sicherheitsproblems erfordert eine Änderung der öffentlichen Schnittstelle zur Bibliothek und das Skript wird durch die Änderung beschädigt). Dies liegt daran, dass OCaml-Skripte bei jeder Ausführung neu kompiliert werden.

RPM-Makros

Die folgenden Makros stehen für die Verwendung in Spec-Dateien zur Verfügung:

  • %{ocaml_native_compiler}: die Architekturen, für die native Kompilierung möglich ist

  • %{ocaml_natdynlink}: die Architekturen, für die natives dynamisches Linken möglich ist

  • %{ocamldir}: Installationsverzeichnis der obersten Ebene für OCaml-Pakete, derzeit gleichbedeutend mit %{_libdir}/ocaml

  • %{ocaml_files}: Erzeugt eine Liste der installierten Dateien in Dateien mit den Namen`.ofiles` (für das Hauptpaket) und .ofiles-devel (für das Unterpaket devel ), sofern nicht -s oder -n angegeben ist. Dieses Makro erfordert, dass python3 im Buildroot verfügbar ist. Flags:

    • -n: es gibt kein -devel-Teilpaket. Alle Dateien werden in .ofiles aufgelistet.

    • -s: separate Installation; jedes Unterverzeichnis von %{ocamldir} ist ein separates RPM-Paket. Für jedes Unterverzeichnis werden .ofiles-<Unterverzeichnis> und .ofiles-<Unterverzeichnis>-devel generiert (sofern nicht auch -n angegeben ist).

Beispiele

Dieser Abschnitt enthält Beispiel-Spec-Dateien, die veranschaulichen, wie man OCaml-Bibliotheken und Binärpakete mit verschiedenen Bauwerkzeugen erstellt.

Dune

Dune ist ein beliebtes Bauwerkzeug für OCaml-Pakete. RPM-Makros sind verfügbar, um das Kompilieren mit Dune zu vereinfachen.

  • %dune_build: Ruft Dune auf, um alle installierbaren Artefakte im Release-Modus zu erstellen.Flags:

    • -j <Zahl>: Anzahl der Jobs, die parallel ausgeführt werden können. Dieser Wert wird automatisch auf %{?_smp_mflags} gesetzt und wird daher typischerweise nur verwendet, um die Parallelverarbeitung mit -j 1 zu deaktivieren.

    • -p <modules>: Weist Dune an, nur die durch Kommas getrennte Liste der Module zu erstellen,anstatt jedes installierbare Artefakt.

    • --: trennt die Flags für dieses Makro von den Flags, die an Dune übergeben werden sollen

  • %dune_install: Ruft Dune auf, um alle installierbaren Artefakte zu installieren. Flags:

    • -n: es gibt kein -devel-Teilpaket. Alle Dateien gehören zum Hauptpaket.

    • -s: separate Installation; jedes Unterverzeichnis von %{ocamldir} ist ein separates RPM-Paket. Anderenfalls sind alle Dateien einem einzigen Hauptpaket zugeordnet.

    • --: trennt die Flags für dieses Makro von den Flags, die an Dune übergeben werden sollen

  • %dune_check: Ruft Dune auf, um Tests für alle installierbaren Artefakte auszuführen.Flags:

    • -j <Zahl>: Anzahl der Jobs, die parallel ausgeführt werden können. Dieser Wert wird automatisch auf %{?_smp_mflags} gesetzt und wird daher typischerweise nur verwendet, um die Parallelverarbeitung mit -j 1 zu deaktivieren.

    • -p <modules>: Weist Dune an, nur die durch Kommas getrennte Liste der Module zu erstellen,anstatt jedes installierbare Artefakt.

    • --: trennt die Flags für dieses Makro von den Flags, die an Dune übergeben werden sollen

  • %odoc_package: Deklariert ein Teilpaket, das die von odoc generierte Dokumentation enthält. Flags:

    • -L <Lizenzdateiname>: gibt den Namen einer Datei an, die als Lizenzdatei in das Teilpaket aufgenommen werden soll.

Nachfolgend finden Sie eine Beispiel-Spec-Datei für eine fiktive OCaml-Bibliothek namens_foolib_, die mit dune erstellt wird.

ocaml-dune-example.spec
%ifnarch %{ocaml_native_compiler}
%global debug_package %{nil}
%endif

Name:           ocaml-foolib
Version:        1.2.3
Release:        %autorelease
Summary:        OCaml library for fooing bars

License:        LGPL-2.1-or-later
URL:            https://www.example.com/foolib
Source:         https://www.example.com/foolib-%{version}.tar.gz

BuildRequires:  ocaml
BuildRequires:  ocaml-dune

%description
OCaml library for fooing bars.  This library can also foo bazes.


%package        devel
Summary:        Development files for %{name}
Requires:       %{name}%{?_isa} = %{version}-%{release}


%description    devel
The %{name}-devel package contains libraries and signature files for
developing applications that use %{name}.


%prep
%autosetup -n foolib-%{version}


%build
# Build all installable targets
%dune_build
# Build a specific set of targets
%dune_build -p bazzer,boffer
# Build non-default targets
%dune_build @install @doc


%install
# Install all installable targets
%dune_install
# Install a specific set of targets
%dune_install bazzer boffer


%check
# Check all installable targets
%dune_check
# Check a specific set of targets
%dune_check -p bazzer,boffer


%files -f .ofiles
%doc README
%license LICENSE


%files devel -f .ofiles-devel


%changelog
%autochangelog

Topkg

topkg, der „transitory OCaml software packager“ (temporärer OCaml-Software-Paketierer), generiert Skripte, dieausgeführt werden, um verschiedene Paketierungs- und Bauaufgaben durchzuführen.

Nachfolgend finden Sie eine Beispiel-Spec-Datei für eine fiktive OCaml-Bibliothek namens_foolib_, die mit topkg erstellt wird.

ocaml-topkg-example.spec
%ifnarch %{ocaml_native_compiler}
%global debug_package %{nil}
%endif

Name:           ocaml-foolib
Version:        1.2.3
Release:        %autorelease
Summary:        OCaml library for fooing bars

License:        LGPL-2.1-or-later
URL:            https://www.example.com/foolib
Source:         https://www.example.com/foolib-%{version}.tar.gz

BuildRequires:  ocaml
BuildRequires:  ocaml-findlib
BuildRequires:  ocaml-topkg-devel
BuildRequires:  python3

%description
OCaml library for fooing bars.  This library can also foo bazes.


%package        devel
Summary:        Development files for %{name}
Requires:       %{name}%{?_isa} = %{version}-%{release}


%description    devel
The %{name}-devel package contains libraries and signature files for
developing applications that use %{name}.


%prep
%autosetup -n foolib-%{version}

# Enable debuginfo if upstream does not
echo true: debug >> _tags


%build
ocaml pkg/pkg.ml build --tests true


%install
mkdir -p %{buildroot}%{ocamldir}/foolib
cp -p _build/{opam,pkg/META} %{buildroot}%{ocamldir}/foolib
%ifarch %{ocaml_native_compiler}
cp -a _build/src/*.{a,cma,cmi,cmt,cmti,cmx,cmxa,cmxs,mli} \
  %{buildroot}%{ocamldir}/foolib
%else
cp -a _build/src/*.{cma,cmi,cmt,cmti,mli} %{buildroot}%{ocamldir}/foolib
%endif

# This macro requires python3 in the buildroot
%ocaml_files


%check
ocaml pkg/pkg.ml test


%files -f .ofiles
%doc README
%license LICENSE


%files devel -f .ofiles-devel
%ifarch %{ocaml_native_compiler}


%changelog
%autochangelog

Weitere Erstellungswerkzeuge

Nachfolgend finden Sie eine Beispiel-Spec-Datei für eine fiktive OCaml-Bibliothek namens foolib.

ocaml-example.spec
%ifnarch %{ocaml_native_compiler}
%global debug_package %{nil}
%endif

Name:           ocaml-foolib
Version:        1.2.3
Release:        %autorelease
Summary:        OCaml library for fooing bars

License:        LGPL-2.1-or-later
URL:            https://www.example.com/foolib
Source:         https://www.example.com/foolib-%{version}.tar.gz

BuildRequires:  ocaml
BuildRequires:  ocaml-findlib

%description
OCaml library for fooing bars.  This library can also foo bazes.


%package        devel
Summary:        Development files for %{name}
Requires:       %{name}%{?_isa} = %{version}-%{release}


%description    devel
The %{name}-devel package contains libraries and signature files for
developing applications that use %{name}.


%prep
%autosetup -n foolib-%{version}


%build
# You may need a ./configure step here.
make byte
%ifarch %{ocaml_native_compiler}
make opt
%endif


%install
# These rules work if the library uses 'ocamlfind install' to install itself.
export OCAMLFIND_DESTDIR=%{buildroot}%{ocamldir}
mkdir -p $OCAMLFIND_DESTDIR/stublibs
%make_install


%files
%doc README
%license LICENSE
%{ocamldir}/foolib
%ifarch %{ocaml_native_compiler}
%exclude %{ocamldir}/foolib/*.a
%exclude %{ocamldir}/foolib/*.cmxa
%exclude %{ocamldir}/foolib/*.cmx
%endif
%exclude %{ocamldir}/foolib/*.mli
%{ocamldir}/stublibs/*.so
%{ocamldir}/stublibs/*.so.owner


%files devel
%ifarch %{ocaml_native_compiler}
%{ocamldir}/foolib/*.a
%{ocamldir}/foolib/*.cmxa
%{ocamldir}/foolib/*.cmx
%endif
%{ocamldir}/foolib/*.mli


%changelog
%autochangelog

Weitere Informationen