LinuxのCプログラミングにちょっと慣れてきたら、自分のライブラリが欲しくなります。
静的ライブラリを活用することで、複数の実行ファイルに共通する関数の管理がやりやすくなります。
makeを使って、ターミナルのCUIでやります。
ここでは、getch()を例に取り、getch()と、kbhit()で構成されるlibgetch.aという
静的ライブラリを開発することを目的とします。
kbhit()はキーボードが押されたかどうかを判定する関数で、getch()は、コンソールからのエコーバックなしの1文字入力です。
環境
Ubuntu22.04LTS
GNU Make 4.3
gcc (Ubuntu 11.3.0-1ubuntu1~22.04) 11.3.0
LinuxのCの開発環境がインストールされているものとしておいて下さい。
ディレクトリ構成
以下のディレクトリ構成の開発環境だとします。
/home/user/
├── getch # カレントディレクトリ
│ ├── getch.c
│ ├── getch.h
│ ├── kbhit.c
│ ├── main.c
│ └── makefile # libgetch.aと実行ファイルを作るためのmakefile
├── include # ユーザー用インクルードディレクトリ
├── lib # ユーザー用ライブラリディレクトリ
:
:
必要ファイル
getch.h
extern int getch();
extern int kbhit();
getch.c
#include <termios.h>
#include <unistd.h>
#include <stdio.h>
#include <fcntl.h>
#include <getch.h>
int getch(){
struct termios oldt, newt;
int c;
tcgetattr( STDIN_FILENO, &oldt );
newt = oldt;
newt.c_lflag &= ~ICANON;
newt.c_lflag &= ~ECHO;
tcsetattr( STDIN_FILENO, TCSANOW, &newt );
c = getchar();
tcsetattr( STDIN_FILENO, TCSANOW, &oldt );
return c;
}
kbhit.c
#include <termios.h>
#include <unistd.h>
#include <stdio.h>
#include <fcntl.h>
#include <getch.h>
int kbhit(){
struct termios oldt, newt;
int c;
int oldf;
tcgetattr(STDIN_FILENO, &oldt);
newt = oldt;
newt.c_lflag &= ~(ICANON | ECHO);
tcsetattr(STDIN_FILENO, TCSANOW, &newt);
oldf = fcntl(STDIN_FILENO, F_GETFL, 0);
fcntl(STDIN_FILENO, F_SETFL, oldf | O_NONBLOCK);
c = getchar();
tcsetattr(STDIN_FILENO, TCSANOW, &oldt);
fcntl(STDIN_FILENO, F_SETFL, oldf);
if(c != EOF) { ungetc(c, stdin); return 1; }
return 0;
}
main.c
#include <stdio.h>
#include <getch.h>
int main() {
while(1) {
if (kbhit()) {
int key=getch();
printf("%c",key);
}
}
}
makefile
makefile のテンプレートとしても使えます
# makefileのレシピ行の最初のキャラクタをスペースにします
.RECIPEPREFIX :=$(RECIPEPREFIX) <space>
# ユーザーネーム
USER = user
CC = gcc # コンパイラはgccを使うものとします
RM = rm
CP = cp
CSUFFIX = .c
OSUFFIX = .o
LSUFFIX = .a
HSUFFIX = .h
# 静的ライブラリの名前
TARGET = getch
# 実行ファイル名
MAIN = main
SRCS = getch.c kbhit.c main.c # ソースファイルの列挙
OBJS = $(SRCS:$(CSUFFIX)=.o) # オブジェクトファイルの名前定義
LIBNAME = lib$(TARGET)$(LSUFFIX) # 静的ライブラリのファイルネーム
HEADER = $(TARGET)$(HSUFFIX) # ヘッダファイルのファイルネーム
# ライブラリディレクトリ
LIBDIRNAME = /home/$(USER)/lib/
# インクルードディレクトリ)
INCDIR = /home/$(USER)/include/
# コンパイラに渡すオプション(インクルードディレクトリ)
INCLUDE = -I$(INCDIR)
# コンパイラに渡すオプション(ライブラリディレクトリ)
LDFLAGS = -L$(LIBDIRNAME)
# コンパイラ渡すオプション(静的ライブラリのファイル指定)
LIBS = -l$(TARGET)
# コンパイラに渡すオプション
CFLAGS = -Wall -O2 $(INCLUDE)
# 一つづつオブジェクトファイルを生成
$(CSUFFIX).$(OSUFFIX):
$(CC) -c $<
# make とすると静的ライブラリを作成する
$(LIBNAME): inc $(filter-out $(MAIN)$(OSUFFIX),$(OBJS))
ar rcs $(LIBNAME) $(filter-out inc,$^)
# make incとすると、ヘッダフィルを当該ディレクトリにインストール
# ディレクトリのアクセス権をセット
inc: $(HEADER)
$(CP) $(HEADER) $(INCDIR)
# make main とすると実行ファイルを作成
$(MAIN): inc $(MAIN)$(OSUFFIX) $(LIBNAME) install
$(CC) -o $(MAIN) $(MAIN)$(OSUFFIX) $(CFLAGS) $(LIBS) $(LDFLAGS)
# make setとすると、静的ライブラリとヘッダファイルを当該ディレクトリにインストール
install: $(LIBNAME) inc
$(CP) $(LIBNAME) $(LIBDIRNAME)
# make clean とすると .o ファイル削除
clean:
$(RM) -rf $(OBJS) $(LIBNAME)
# make all とすると最初からやる
all: clean inc $(MAIN)
ライブラリ作成
これで、makeとすると、ヘッダファイルgetch.hが、/home/user/includeにインストールされ、静的ライブラリlibgetch.aが、/home/user/libにインストールされます。
make main とするとテストプログラムのmainという実行ファイルが作成されます。
makefileのmake のコマンド
$ make # 静的ライブラリlibgetch.aを作成する
$ make inc # ヘッダファイルをインクルードディレクトリにインストール
$ make main # 実行ファイルを作成
$ make install # 静的ライブラリとヘッダファイルを当該ディレクトリにインストール
$ make clean # オブジェクトファイル削除、静的ライブラリlibgetch.aを削除
$ make all # オブジェクをファイルを削除、静的ライブラリ削除、
# また作成、実行ファイル作成、静的ライブラリとヘッダファイルを
# 当該ディレクトリにインストール
注意点
・コンパイラが静的ライブラリ(.a)をリンクする場合は、例えばA.oがB.aに依存しているなら、A.oをB.aの前に引数で与えなくてはなりません。
・静的ライブラリ(.a)は、オブジェクトファイル(.o)をarで束ねただけのものです。
・-lで与える名前は、libgetch.aでもいいですが、省略してgetchとします。
make allの実行をした結果のディレクトリ構造
/home/user/
├── getch # カレントディレクトリ
│ ├── getch.c
│ ├── getch.h
│ ├── getch.o
│ ├── kbhit.c
│ ├── kbhit.o
│ ├── libgetch.a
│ ├── main
│ ├── main.c
│ ├── main.o
│ └── makefile
├── include
│ └── getch.h
├── lib
: └── libgetch.a
:
環境変数
最後に、環境変数にPATHをセットして終わりです。
export C_INCLUDE_PATH="/home/user/include:$C_INCLUDE_PATH"
export LIBRARY_PATH="$LIBRARY_PATH:/home/user/lib"
これで、gcc main.c -lgetch
でコンパイルできるようになります。