LoginSignup
1
1

More than 1 year has passed since last update.

linux C言語:ユーザーのライブラリを作る。makefile のテンプレート、getch(),kbhit()を例にとって。

Last updated at Posted at 2023-02-19

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

getch.h
extern int getch();
extern int kbhit();

getch.c

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

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

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でコンパイルできるようになります。

1
1
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1