Paketbaurichtlinien für C und C++
Einführung
Die Programmiersprachen C und C++ sowie deren Laufzeitumgebungen gehören zu den gängigsten Entwicklungsframeworks für Pakete in Fedora. Daher gibt es große Unterschiede hinsichtlich Qualität, Stil und Konventionen in den jeweiligen Paketen. Das folgende Dokument zeigt Ihnen bewährte Vorgehensweisen für bestimmte Aspekte der Paketierung von C- und C++-Paketen.
Paketierung
BuildRequires und Requires
Wenn Ihre Anwendung in C oder C++ geschrieben ist, müssen Sie in BuildRequires die Pakete gcc, gcc-c++ oder clang angeben. Diese Pakete enthalten alles, was zum Erstellen einer standardkonformen C- oder C++-Anwendung benötigt wird.
Wenn Ihre Bibliothek Standard-C- oder C++-Header enthält, müssen Sie BuildRequires für gcc, gcc-c++ oder clang auflisten, um die benötigten standardkonformen Header zu installieren.
Wenn Sie zur Laufzeit cpp verwenden, um C- oder C++-Sprachheader zu verarbeiten, müssen Sie Requires für gcc, gcc-c++ oder clang verwenden, um die benötigten Header für eine standardkonforme C- oder C++-Anwendung zu installieren. Dies könnte sich in Zukunft ändern, falls spezielle Anbieter wie z.B. c-headers oder c++-headers einen Satz standardkonformer C- oder C++-Sprachheader bereitstellen.
Wenn Sie zur Laufzeit cpp verwenden, um C- oder C++-Sprachheader zu verarbeiten, müssen Sie BuildRequires oder Requires für gcc, gcc-c++ oder clang verwenden, um den benötigten Header für eine standardkonforme C- oder C++-Anwendung zu installieren. Dies könnte sich in Zukunft ändern, falls spezielle Anbieter wie z.B. c-headers oder c++-headers einen Satz standardkonformer C- oder C++-Sprachheader bereitstellen.
Eine Liste der unterstützten Compiler für C und C++ finden Sie in den Compiler-Richtlinien.
Fragen und Antworten zur Paketierung
-
Benötige ich
Requires: glibc, um sicherzustellen, dass die C-Laufzeitumgebung für meine Anwendung installiert ist?Nein. RPM ermittelt automatisch die benötigten ELF-Bibliotheken anhand der Binärdateien in Ihrem Paket. Dies reicht für die Installation von glibc aus.
-
Benötige ich
Requires: libgcc?Wenn Sie eine API von
libgccdirekt verwenden, benötigen Sie die ZeileRequires: libgcc. Im Allgemeinen benötigtglibcjedochlibgcc, daher ist es immer installiert.
Bibliotheken
Bibliotheken sollten eindeutige Shared-Object-Namen (SONAMEs über -Wl,-soname=libfoo.so) besitzen, die nicht mit anderen in der Distribution verwendeten Bibliotheks-SONAMEs kollidieren. Beispielsweise sollte es nur eine libfoo.so in der Distribution geben. Eine Ausnahme besteht, wenn mehrere Implementierungen derselben Bibliothek libfoo.so von verschiedenen Autoren bereitgestellt werden und diese miteinander in Konflikt stehen. In diesem Fall müssen beide libfoo.so exakt dieselbe Schnittstelle, jedoch mit unterschiedlicher Implementierung, bereitstellen. Zwei libfoo.so mit jeweils unterschiedlicher API zu verwenden, ist schlechte Praxis und erschwert das Paketieren und Verteilen.
Versionierte Symbole
Ohne versionierte Symbole generiert RPM einen Abhängigkeitsausdruck, der die Bibliothek zwar benennt, aber keine Version angibt. Dadurch wird effektiv „$Major.0.0“ als Mindestversion festgelegt. Versionierte Symbole liefern die notwendigen Informationen, um sicherzustellen, dass Bibliotheken tatsächlich aktuell genug sind, um die Software auszuführen, die mit ihnen verlinkt ist.
Prüfen Sie die vom Binär-RPM-Paket bereitgestellten Funktionen: rpm -qp --provides <Paket>. Ein Paket mit gemeinsam genutzten Bibliotheken listet die Bibliothek als libc.so.6()(64bit) auf, und wenn die Bibliothek versionierte Symbole bereitstellt, wird sie zusätzlich mit den Versionen als libm.so.6(GLIBC_2.41)(64bit) aufgeführt.
Paketbetreuer werden dazu angehalten, mit den Upstream-Projekten zusammenzuarbeiten, um versionierte Symbole zu Bibliotheken hinzuzufügen, die diese noch nicht enthalten.
Das Hinzufügen von Symbolversionen ist für die meisten Bibliotheken einfach und erfordert anfänglich nur eine Symbolzuordnung und ein zusätzliches Argument für den Linker während des Bauprozesses.
generate_initial_map.sh:
#!/bin/sh
echo "# Avoid modifying a symbol set after it has been released"
echo "# When adding features in a new release, add a new set"
echo "# Removing features is a breaking change"
echo "$2 {"
echo " global:"
objdump -T $1 | \
grep -F .text | \
awk '{print $7;}' | \
c++filt | \
awk '/[() ]/ {print " \"" $0 "\";";} \
!/[() ]/ {print " " $0 ";";}' | \
sort
echo "}"
Führen Sie generate_initial_map.sh /Pfad/zur/Bibliothek.so.1 <BIBLIOTHEK>_<VERSION> aus, um eine Zuordnungsdatei zu generieren.
Versionierungsskript zu Automake hinzufügen
Das Handbuch zur GNU Portability Library enthält Beispiele für die Verwendung von „version-script“ in Automake. In Makefile.am:
=== Versionierungsskript zu CMake hinzufügen
Das unter der BSD-3-Clause-Lizenz stehende link:https://github.com/protocolbuffers/protobuf[protobuf] enthält Beispiele für die Verwendung von version-script in CMake.
Überprüfen Sie in der Datei CMakeLists.txt, ob der Linker unterstützt wird:
file(WRITE ${CMAKE_CURRENT_BINARY_DIR}/cmaketest.map "{ global: main; local: *; };") # CheckLinkerFlag module available in CMake >=3.18. if(${CMAKE_VERSION} VERSION_GREATER_EQUAL 3.18) include(CheckLinkerFlag) check_linker_flag(CXX -Wl,--version-script=${CMAKE_CURRENT_BINARY_DIR}/cmaketest.map project_HAVE_LD_VERSION_SCRIPT) endif() file(REMOVE ${CMAKE_CURRENT_BINARY_DIR}/cmaketest.map)
Und wo die Bibliothek definiert ist:
if(project_HAVE_LD_VERSION_SCRIPT) target_link_options(libfoo PRIVATE -Wl,--version-script=${protobuf_source_dir}/src/libfoo.map) set_target_properties(libfoo PROPERTIES LINK_DEPENDS ${project_source_dir}/src/libfoo.map) endif()
=== Versionierungsskript zu Meson hinzufügen
Mesons link:https://github.com/mesonbuild/meson/blob/master/test%20cases/linuxlike/3%20linker%20script/meson.build[Testfälle] enthalten Beispiele für die Verwendung von version-script.
Solaris 11.4 ld supports --version-script only when you also specify
-z gnu-version-script-compat
if meson.get_compiler('c').get_linker_id() == 'ld.solaris' add_project_link_arguments('-Wl,-z,gnu-version-script-compat', language: 'C') endif
Static map file mapfile = 'bob.map' vflag = '-Wl,--version-script,@0@/@1@'.format(meson.current_source_dir(), mapfile)
l = shared_library('bob', 'bob.c', link_args : vflag, link_depends : mapfile) `
Want to help? Learn how to contribute to Fedora Docs ›