UNIXライクシステム用のプログラムおよびライブラリでパッケージを作成、アセンブル、インストール、および使用する方法

ソースコード(tarballを含む)の形で配布されるUNIXライクシステム用のプログラムとライブラリについて話します。通常、CとC ++で記述されています(ただし、同じ言語のソフトウェアにも同じ手順を適用できます)。 この記事の多くの内容はGNU / Linux専用に書かれていますが、記事の多くは他のUNIXライクなオペレーティングシステムに一般化できます。

この記事の「パッケージ」という言葉は、特定のGNU / Linuxディストリビューションのパッケージではなく、ソースパッケージを意味しますが、ソフトウェアの元の作者によるパッケージのみを意味します(2017-02-09からのUPD :文脈から明らかな場合を除きます) 「パッケージ」という言葉が別の意味で使用されていること)。

この記事では、次の質問を分析します。


非常に基本的なことだけを分析します。 UNIXに似たシステム用のCおよびC ++でプログラミングされたフリーソフトウェアコミュニティの典型的なメンバーは、通常すでに知っています。 tarballの作成方法(「裸の」makeの例を使用)および他の人のtarballの設定方法。 「良い」パッケージを作成するための高度なヒントは提供しません。 Debianのすばらしい記事「アップストリームガイド」で、ビルドシステムのドキュメントの「高度な」ことを読んでください(最後に「良い」パッケージを作成するためのリンクがまだたくさんあります)。 私の目標であるこの記事では、多くのことを異なる方法で行うことができます。広大さを把握しようとするのではなく、少なくとも1つの方法を与えることです。

私はあなたに警告します:始まりは非常にシンプルになりますが、終わりに近づくともっと面白くなるでしょう。

そしてもう一つの警告。 私は一人のために急いでこの記事を書きました。 そして、書いた後、私は書いたので、消えないようにHabrに投稿するだろうと彼らは言うと思いました。 したがって、記事のあいまいな構造化、「 包含自体が実行しない 」などのフレーズなど、記事に欠陥があります。これを修正しません。 たぶん、次の人生のいつか、手が届きます。 選択は、投稿するかまったく投稿しないかでした。

そのため、Hello、worldプログラムでパッケージを作成することから始めます。 まず、ビルドシステムを決定しましょう。

実際には、パッケージ自体は通常makeプログラムを使用してビルドされます(ただし、これが唯一の方法ではありません)。 makeの構成は通常、Makefileというファイルにあります。

いくつかのオプションがあります。単にmakeを使用するか、makeと一緒に高レベルのビルドシステム(通常はautotoolsまたはcmake)を使用します。これらは実際にmakeの構成を生成します。

この例では、簡単にするためにmakeのみを使用します。 したがって、hello.cを作成します。

#include <stdio.h> int main (void) { printf ("Hello, world!\n"); return 0; } 

さて、Makefile:

注意! GNU / Linuxを搭載。 macOSでの動作は保証されていません! このMakefileは、すべてのUNIXライクシステムでまったく移植性がありません! 次はさらに続きます。

ここで<------>はタブ文字を意味します。

 PREFIX=/usr/local CC=cc CFLAGS= all: hello hello: hello.c <------>$(CC) $(CFLAGS) -o hello hello.c install: all <------>mkdir -p $(PREFIX)/bin <------>install hello $(PREFIX)/bin/ 

これは、このMakefileを書く唯一の方法からはほど遠いです。 一般に、私の記事全体の目的は、何らかの基礎を提供することです。 このMakefileで他に変更できるものは、他のソースから学ぶことができます。

また、この記事では、makeに関する非常に基本的なことを知っていることを前提としています。 つまり、Makefileは依存関係ツリーの説明であり、ビルドする必要のあるファイルを正確に収集するために必要であることを知っています。 この記事では、パッケージの作成時にmakeを使用すること、インストールなどの「仮想」makeの目標について説明します。 また、コマンドラインからコンパイラを起動する方法について、非常に基本的なこと、つまりcc -o hello hello.c何であるかを既に知っていると想定しています。

それでは、Makefileを解析しましょう。

まず、次のように言います。 ユーザーがパッケージをダウンロードするとします。 彼は最初にそれを収集する必要があります。つまり、パッケージ自体と一緒にディレクトリ内のバイナリを取得します(またはツリー構築の場合は、本質を変更しない他のディレクトリにそれらを取得します)、次にインストール、つまり結果のバイナリをコピーしますシステム内の最終ストレージの場所。

つまり、見てください。 ユーザーuserとしてログインしています。 ホームディレクトリは/ home / userです。 / home / user / Desktop / fooのように、パッケージをダウンロードして解凍しました。 次に、それを収集しました。 同じフォルダ/ home / user / Desktop / fooに収集されます。 または、ツリービルド以外の場合は、別のフォルダー/ home / user / Desktop / foo-buildを作成してそこに配置します。 さて、ビルド後、/ usr / localにインストールすることにしました。 ここでは、すでにルート権限が必要です。 sudoを使用して、このプログラムを/ usr / localにインストールします。

システム内のディレクトリの配置に関するいくつかの言葉。 いくつかのディレクトリのみを分析します。 詳細については、Filesystem Hierarchy Standard(多くのGNU / Linuxディストリビューションでサポートされています)およびGNU / Linuxおよびその他のオペレーティングシステムのコマンド「man 7 hier」を参照してください。 いずれにせよ、それらがどのように配置されたか、たとえば約5年前(つまり、2012年のどこか)、最近のFedoraなどのあらゆる種類の新しいトレンドを「すべてを/ usrに移動しましょう」と言います私は考慮しません。


プレフィックスについて説明します。 パッケージをインストールするとき、いわゆるプレフィックス、つまり、すべてがインストールされるディレクトリを指定する必要があります。 このディレクトリにbinサブディレクトリが作成され、そこにバイナリがインストールされ、libサブディレクトリが作成され、ライブラリのバイナリがそこにインストールされます。

つまり、たとえば、接頭辞/ usr / localを指定してパッケージをインストールします。 次に、バイナリは/ usr / local / binに移動し、ライブラリのバイナリは/ usr / local / libに移動します。

ここで、Makefileの解析に戻ります。 PREFIX-これは先ほどお話ししたプレフィックスです。 デフォルトのプレフィックス(ユーザーが再定義できます)として、/ usr / localが指定されています-デフォルトの適切な選択肢です。 通常、パッケージを作成するときは常にデフォルトのプレフィックスとして示されます(残念ながら、一部のパッケージはまだこれを行っていませんが、後でさらに行われます)。 次はCCです。 これは、コンパイラが配置されるmake変数の標準名であり、この場合はccです。 ccは、特定のシステムでデフォルトのコンパイラを起動するために一般的に使用されるコマンドです。 gccまたはclangにすることができます。 一部のシステムでは、ccコマンドが使用できない場合があります。 CFLAGSは、コンパイルフラグを持つ変数の標準名です。

makeと入力するだけで、Makefileの最初に来る目標が達成されます。 一般的な方法は、すべてを呼び出すことです。 そして、そのような目標は通常、プロジェクト全体を収集しますが、何も確立しません。 通常、このターゲットは「仮想」です。つまり、allというファイルはありません。 タスクはhelloバイナリを1つだけ収集することなので、すべてをhelloに依存させるだけです。 以下は、helloアセンブリの目的の説明です。 原則として、hello.cのアセンブリhello.oとhello.oのアセンブリhelloの2つの段階に分けることができます。 簡単にするためにこれをしませんでした。

次はインストール、インストールです。 これも仮想目標です。 それはすべてに依存します。 これは、ユーザーが「make」なしで「make install」とすぐに入力した場合に行われます。 この目的のために、最初にインストールするフォルダー、つまり、予想どおり$(PREFIX)/ binを作成し、その中にインストールユーティリティをインストールします。

インストールは何をしますか? これはcpとほぼ同じです。 正確な違いはわかりません。 プログラムをインストールするには、cpではなくinstallを使用する必要があります。

バイナリは$(PREFIX)/ binに設定されます。これがまさにあなたがする必要があることだからです。 バイナリはプレフィックスのbinサブディレクトリに移動し、バイナリはPREFIXのlibのライブラリに移動します。

ここでは、システムにいわゆるBSDインストールがインストールされていると仮定します。 GNU / Linuxでは、そうです。 一部のシステムでは、そうでない場合があります。 この状況では機能しない他のインストールが存在する可能性があります。 これは、異なるOSでの動作が保証されないと言ったとき、私が念頭に置いていたものです。

ここでは、DESTDIRは考慮しませんでした。DESTDIRは、Makefileでの使用を強くお勧めします。 きれいにするという目標すら考えていませんでした。

さて、最後のtarball、拡張子.tar.gz、.tar.xzなどのいわゆるファイルを作成しましょう。 hello.cおよびMakefileファイルをhello-1.0フォルダーに配置します(tarballの作成時にバージョン番号を指定するのが一般的です)。 次に、現在のディレクトリをhello-1.0を含むディレクトリに設定し、次のように入力します。

 tar --xz -cf hello-1.0.tar.xz hello-1.0 

これにより、hello.cとMakefileを含むhello-1.0ディレクトリがあるアーカイブが作成されます。 これはパッケージを配布する方法です。

C ++はパッケージのバリアントです。 ソースは同じで、hello.cppと呼ばれる必要があります。 メイクファイルは次のようになります。

 PREFIX=/usr/local CXX=c++ CXXFLAGS= all: hello hello: hello.cpp <------>$(CXX) $(CXXFLAGS) -o hello hello.cpp install: all <------>mkdir -p $(PREFIX)/bin <------>install hello $(PREFIX)/bin/ 

C ++コンパイラのフラグの標準変数名は、CPPFLAGSではなくCXXFLAGSであることに注意してください。 CPPFLAGSは、Cプリプロセッサへのフラグの変数の名前です。

次に、ソート(任意、プログラム、ライブラリ)からパッケージをインストールする方法を考えてみましょう。 ビルドシステムに関係なく。 このアルゴリズムは、作成したtarballにも適しています。 ビルドしたいパッケージがhelloとも呼ばれているとします。

最初のステップ:ダウンロードして、ソートされたフォルダーに移動します。 2つのオプションがあります:バージョン管理システムからのダウンロード(たとえばgitを分析します)またはtarballのダウンロード。

最初のオプション。 git。 クローンを作成:

 git clone <> 

これにより、helloフォルダーが作成されます。 バージョン管理システムからダウンロードしたため、バージョン番号なし。 cdを作成されたフォルダー、つまりcd helloます。

2番目のオプション。 ターボール。 tarballをダウンロードします。 次に、コマンドtar -xf hello-1.0.tar.xzます。 これにより、たとえばhello-1.0などのフォルダーが作成されます。 通常、パッケージの作成者がすべてを正しく行った場合、フォルダー名にはバージョン番号が含まれます。 次に、結果のフォルダーにcdを作成します(例: cd hello-1.0

今、あなたは収集する必要があります。 簡単にするために、ツリービルドを使用しないことを前提とします(パッケージの作成者がツリービルドを使用しない場合、通常、その方法についての説明がそこに書かれています)。 そのため、並べ替える場所と同じフォルダに収集します。 つまり、cdを作成したばかりのこのフォルダー。

以降のアクションは、プロジェクトで選択されたビルドシステムによって異なります。 ただし、ビルドシステムに関係なく、ビルドプロセス中にプレフィックスを指定する必要があります。 また、通常、プレフィックスはバイナリ内にハードコーディングされるため、インストール段階ではなく、アセンブリ段階で指定する必要があります。 そしてこれは、特定の場所にプログラムをインストールした後、あなたはそれをただ動かして移動することができないことを意味します。

ここでは、ほとんどの場合に機能するサンプルの手順を示します。 プロジェクトの作者からの正確な指示が表示されますが、微妙な違いがあるかもしれません。

作成時に必ずプレフィックスを指定してください(作成者が指示に記載するものは何でも)。 指定しない場合、デフォルトが選択されます。 これは通常/ usr / localであり、これはかなり良い選択です。 そうでない場合は? パッケージの作成者が他のデフォルトプレフィックスを指定した場合はどうなりますか? どこにインストールするかは明確ではありません。 特に、libqglviewerはデフォルトのプレフィックスとして/ usrを使用しますが、これは完全に間違っています(バグレポートを作成者に送信しました)。 そのため、絶対に常にプレフィックスを指定します。 著者が自分のサイトで指し示している指示を読み、接頭辞を置く場所を見つけます。

それで、どんなビルドシステムができるのか。 まず、それはただのメイクかもしれません。 ベアメイクのこのオプションはまれです。 裸のmakeを備えた数少ないパッケージの1つはbzip( www.bzip.org )です。 作成したhello-1.0.tar.xzの場合、まさにそのようなオプションがあります。

したがって、ベアメイクの場合にビルドするには、これが必要です:

 make PREFIX=/--- 

(具体的には、bzipの場合、ビルド段階でPREFIXを指定する必要はありません。しかし、理論的には、バイナリ内でPREFIXをハードコードするパッケージを想像できます。したがって、一般にPREFIXが必要です。)

次のオプションはautotoolsです。 この場合、次のように組み立てます。

 ./configure --prefix=/--- make 

次のオプションはcmakeです。 このように配置します(cmakeコマンドの最後のポイントに注意してください):

 cmake -DCMAKE_INSTALL_PREFIX=/--- . make 

最後のポイントはどこですか? 実際のところ、cmakeはパスを酒に渡す必要があるということです。 また、ツリービルドを使用していないため、ここで収集します。 私たちがいるのは魔術師です。 したがって、ポイント、つまり現在のディレクトリ。

autotoolsおよびcmakeの場合、Makefileを生成するコマンド(つまり./configureまたはcmake)はmake configにプレフィックスを書き込むため、makef(および後述するmake installコマンド)はプレフィックスを指定する必要がありません。

したがって、これらのメソッドの1つを収集しました。 次は? ここでインストールする必要があります。

ベアメイクの場合、これは次のように行われます。

 make PREFIX=/--- install 

アセンブリ中に指定したものと同じプレフィックスを指定する必要があります。

このようなautotoolsとcmakeの場合:

 make install 

プレフィックスに書き込むためにsudoが必要な場合は、sudoでインストールするためにこのコマンドを入力する必要があります。 一般に、アセンブリは常に通常の権限で実行されますが、インストールはプレフィックスに書き込むために必要な権限で実行されます。

さて、今度は何をしたか見てみましょう。 何もインストールせず、前に作成したhello-1.0.tar.xzをインストールしたとします。 また、指定したプレフィックスが/ fooであると仮定します。 次に、フォルダー/ foo、/ foo / bin(以前にフォルダーがなかった場合)およびファイル/ foo / bin / helloがシステムに表示されます。 どうした アセンブリ中にコマンドラインで指定されたPREFIX = / foo変数は、Makefileで指定されたPREFIX = / usr / localを再定義しました。 その結果、Makefileで指定されたmkdir -pおよびインストールコマンドは次のようになりました。

 mkdir -p /foo/bin install hello /foo/bin/ 

その結果、バイナリは/ foo / binに依存していました。

ここで、プレフィックスについてもう少し説明します。 どんなプレフィックスがありますか?

プレフィックス。 それを選択する必要があることはまずありません。 OSのロードの初期段階に重要なプログラムに使用されます(つまり、ロードの重要な要素は/ bin、/ libなどにあります)(ただし、プログラムを/にインストールする必要がある場合でも、最初に/にインストールされます) usr、つまり、/ usrプレフィックスを使用してアセンブルおよびインストールされ、必要なものが/に移動されます(つまり、/ usr / binから/ binに移動されます)。いずれにしても、Linux From Scratch 7.10の作成者はこれを行います。パッケージ、bashなど)。

プレフィックス/ usr。 パッケージマネージャーを通じてインストールされるプログラムに一般的に使用される標準プレフィックス。 つまり、パッケージマネージャーを使用してプログラムをインストールした場合、/ usrプレフィックスを付けてシステムにコンパイルおよびインストールされたかのように動作します。 / usrプレフィックスが付いたパッケージを自分でインストールすることはできません。

プレフィックス/ usr / local。 自分でプログラムをインストールするための優れたプレフィックス。 良いことは、/ usr / local / binがデフォルトのPATHにあることです(少なくともdebianでは)。 つまり、プログラムをインストールした直後に、名前でプログラムを簡単に実行できます。 バイナリは/ usr / local / binにあり、/ usr / local / binはPATHにあるためです。 悪いことは、すべてのプログラムが混在していることです。 fooライブラリをインストールしてから、barライブラリをインストールしたとします。 このプレフィックスの両方。 ツリーは次のようになります(完全に簡略化された形式)。

 /usr/local/include/foo.h /usr/local/include/bar.h /usr/local/lib/libfoo.so /usr/local/lib/libbar.so 

ほら すべてが混在しています。 「fooに関連するすべて」を含む単一のフォルダーや、「barに関連するすべて」を含む他のフォルダーはありません。 (これはあなたのビジネスですが、本当に悪いかどうかを考慮してください)。 同じ問題が1つのプレフィックスの異なるパッケージのインストールで存在することを明確にします。 つまり、接頭辞/ usrには同じ問題があります。パッケージはシステム全体に「広がり」ます(ここでは、パッケージマネージャーを介して配信されるパッケージ、つまり実際にシステムを構成するパッケージについて説明しています)。 実際、これはほとんどのUNIXライクシステムとWindowsとの顕著な違いの1つです。 Windowsでは、各プログラムはProgram Filesの独自のフォルダーにあります。 ほとんどのUNIXライクシステムでは、システム全体に「広がり」ます。 (2017-02-09からのUPD :Windowsでは、レジストリによって実際にプログラムが「スミア」されます。これはそれほど顕著ではありません。)この問題を「解決」するGNU / Linuxディストリビューションがあります。たとえば、GoboLinuxです。 そこでは、Windowsのように、各パッケージが独自のディレクトリにあります。

/ opt / XXXの形式のプレフィックス。 / optフォルダーは次のように使用することになっています。サブディレクトリを作成し、パッケージ名を呼び出して、これらのサブディレクトリをプレフィックスとして使用する必要があります。 このアプローチを使用すると、上記の問題/ usr / local(問題と思われる場合)はなくなります。 各パッケージは、独自のディレクトリにインストールされます。 fooとbarを使用した上記の例は次のようになります(/ optのサブディレクトリの名前にバージョン番号を指定することをお勧めします)。

 /opt/foo-1.0/include/foo.h /opt/foo-1.0/lib/libfoo.so /opt/bar-2.0/include/bar.h /opt/bar-2.0/lib/libbar.so 

このソリューションにも欠点があります。 これらの無数の/opt/foo-1.0/binディレクトリを(パッケージごとに)PATHに自分で追加する必要があります。

ホームディレクトリに一致するプレフィックス。 つまり、/ home / userなどです。 「自分だけ」、つまり1人のユーザーのみを置きたい場合にアドバイスします。 または、ルート権限がない場合。 おそらく、OSに付属する設定は、〜/ binをPATHに配置するように既に構成されています(そのようなディレクトリが存在する場合)。 したがって、PATHは必要に応じて構成されます。

各プレフィックスには、独自のbin、sbin、lib、includeなどを含めることができます。

それで、何から選択するのですか? システム全体にインストールする必要がある場合は、/ opt / XXXをお勧めします。 私は通常、このように置きます。

次に、アセンブリ、ライブラリのインストール、およびその使用について説明します。 ライブラリは、他のパッケージと同じ方法でアセンブルおよびインストールされます。これについては、すでに上記で説明しました。 それでは、すぐに使用に移りましょう。 そのため、ライブラリを特定の接頭辞、たとえば/ fooにインストールしました。 現在、このライブラリのヘッダーは/ foo / includeにあり、ライブラリのバイナリは/ foo / libにあります(.soは動的ライブラリ、または.aは静的、または両方)。

このライブラリでacファイルを収集する必要があるとしましょう。 どうやってやるの?

まず、ファイルの先頭に、接続されたヘッダーに対応する#includeを記述する必要があります。 さて、次のように収集する必要があります。

 cc -c -I/foo/include ac cc -L/foo/lib -oa ao -lfoo 

正しくしましょう。 まず、I(大きな英語a)とl(小さな英語エール)は2つの異なる文字であると言います。 上記のコマンドでそれらを混同しないでください。

最初のコマンドはコンパイル、つまりacに基づいてaoを作成します2番目のコマンド-リンク、つまりaoに基づいた最終バイナリ

最初のコマンドでは、-I / foo / includeを指定しました。 これは、ヘッダーを検索するフォルダーを示しています。 このオプションからのパスは、#includeで指定されたファイルに接続します。 つまり、コマンドラインで-I / foo / includeが指定され、ファイルに#include <foo.h>が書き込まれている場合、/ foo / include / foo.hが取得され、非アクティブになります。

ここで、-I / foo / includeオプションは、それ自体を含めません。 検索するフォルダーのみを示しているため、#includeも必要です。 つまり、-I / foo / includeと#includeが必要です。そのうちの1つでは不十分です。

リンク。 -L / foo / libは、バイナリライブラリファイル、つまり.soおよび.aファイルを検索するフォルダーを示します。 -lfooは、このライブラリを結果のバイナリに実際にリンクする必要があることを示しています。 -lfooオプションで指定されたライブラリ名は、-L / foo / libで指定されたフォルダーに接続し、get / foo / lib / libfoo(「lib」という単語がファイル名の先頭に挿入されます)、. so(または.a )オプションで、バージョン番号は/foo/lib/libfoo.soまたは/foo/lib/libfoo.so.1になります。 これは、検索する.soファイルの名前になります。

コンパイル(acのao)と同様に、-L / foo / libと-lfooの両方が必要です。 -L / foo / libはどこを見るかを示します。 -lfooは最終的なリンクコマンドを提供します。

-lfooの代わりに、リンクするライブラリファイルへのパス全体を直接書き込むことができます(例:/foo/lib/libfoo.so.1)。 この場合、-L / foo / libオプションは不要です。 次のようになります。

 cc -oa ao /foo/lib/libfoo.so.1 

ライブラリ(ライブラリへのフルパスまたは-lfooタイプのオプション)は、「your」オブジェクトファイルの後に指定する必要があります。この場合はaoです(たぶんそうではありませんが、念のため、これを行う方が良いでしょう)。

2つのチームを1つにまとめると、次のようになります。

 cc -I/foo/include -L/foo/lib -oa ac -lfoo 

ライブラリが/ usrプレフィックスでインストールされている場合(つまり、パッケージマネージャを介して単にインストールされている場合)、-Iおよび-Lオプションは不要です。-I/ usr / includeおよび-L / usr / libがあることを考慮してくださいすでに持っています。 同じことが/ usr / localにも当てはまる場合があります。

foo, libfoo ( libfoo1, libfoo2) libfoo-dev. libfoo .so, libfoo-dev — . . .

libfoo libfoo-dev, foo /usr. , , :

 /usr/include/foo.h (  libfoo-dev) /usr/lib/libfoo.so.1 (  libfoo) 

, , , dpkg -L libfoo . dpkg -S /usr/include/foo.h .

Source: https://habr.com/ru/post/J321468/


All Articles