上級者のための Hello World! 2 ~C/C++ プログラミング~

[前:上級者のための Hello World! ~C/C++ プログラミング~](https://qiita.com/KaoruHosokawa00/items/77122f99988e2f793b46)

# はじめに

これは C/C++ に関する記事の二つ目である。今回はライブラリの作成者が作るべきツールについて書く。今回はそれぞれのツールについては深入りしない。

# Make

C/C++ のコンパイル方法は前回みたように長かった。また、ファイルやライブラリも増えていくとコンパイルに時間がかかり大変だ。そこで、コンパイルを自動化するために Make がうまれた。我々が Makefile というテキストファイルにコンパイル方法を書き込んでおくと、勝手にコンパイルがおこなわれるのである。

## 書き方

前回作ったバイナリファイルを全て消しておこう。

```bash: ターミナル
$ rm lib/* ./hello*
```

`Makefile` の基本的な書き方は下の通りである。`<コマンド>` の左は**必ず Tab** でなくてはならない。空白だと動かない。`<ターゲット>` とは通常、生成されるファイルのことである。

```makefile: 基本的な Makefile
<ターゲット>: <依存ファイル>
	<コマンド>
```

`<依存ファイル>` と `<コマンド>` は 0 個でも 100 個でもいい。

実行するときは下のようにする。

```bash: ターミナル
$ make <ターゲット>
```

Make ではまず `<ターゲット>` のところが呼ばれる。そして依存ファイルを再帰的にたどっていって、もし前回の Make と違っていればコマンドを実行する。下にフローチャートを書いた。

![make-ページ2 (1).jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2986389/c961633e-4b72-c5a6-bf00-2d096de6c778.jpeg)


## ライブラリの作成

前回のライブラリを生成する `Makefile` を下に書く。静的ライブラリと共有ライブラリをどちらも作る。先に言っておくが、これはスマートな書き方ではない。これを `lib` ディレクトリに入ってそこに置いておく。

```bash: ターミナル
$ cd lib
$ touch Makefile
````

```makefile: lib/Makefile
.PHONY: all
all: libhello.a libhello.so

libhello.a: hello.o
	ar r libhello.a hello.o

libhello.so: hello.o
	gcc hello.o -o libhello.so -shared

hello.o: ../src/hello.c ../include/hello.h
	gcc -c ../src/hello.c -I../include
```

ここで 1 行目の `.PHONY: all` をみてほしい。これは「`all` というのがファイルではない」という意味だ。これで下のように打つと、`all` の依存関係のファイルがすべて作られ、コマンドが実行される（この例だとコマンドはない）。

```bash: ターミナル
$ make all
```

次に、`lib/libhello.so` を生成するところを見てほしい。前回と違い `lib/hello.o` から共有ライブラリを作っている。Makefile では、`.c` などのソースコードからまず `.o` ファイルを作成する。こうすることでソースコードが変更されても、そのファイルだけをコンパイルしなおすことができる。

もう一度 `make all` を打ってみよう。

```:ターミナル
$ make all
make: Nothing to be done for 'all'.
```

これは依存ファイルがすべて編集されていないため、なにも起こらなかったのである。例えば `include/hello.h` の編集時刻を更新してみよう。

```bash: ターミナル
$ touch hello.h
$ make all
```

コマンドが再び実行されているのがわかるだろう。

## Makefile から Makefile を呼ぶ

別のディレクトリから `Makefile` を呼ぶには、`-C` オプションを使う。

```bash: ターミナル
$ cd .. && touch include/hello.h
$ make all -C lib
```

`hello_ws` 直下に `Makefile` を書くと統合できる。

```bash: ターミナル
$ touch Makefile include/hello.h
```

```make: Makefile
.PHONY: all
all:
	make all -C lib
```

```bash: ターミナル
$ make all
```

---

`Makefile` の書き方は以下に詳しい。

[makeコマンドを使ってみよう #1 — MIRACLE LINUX サポート&amp;テクノロジー | サイバートラスト株式会社](https://www.miraclelinux.com/tech-blog/0icygs)

[ついにッ！最強のMakefileが完成したぞッッッ！！！ - Qiita](https://qiita.com/harinez2/items/0d25eabdc6dae66e7bee)

# pkg-config

ライブラリの作成は自動化できたが、コンパイルの引数の問題は解決していない。例えば画像処理に使われる`OpenCV` では現在、ライブラリが50個以上ある。コンパイルのたびにライブラリを50個以上指定するさまを想像してほしい。 `pkg-config` はライブラリを使うときにコンパイラへ引数を渡す役割を負う。ライブラリの製作者が `pc` という拡張子のファイルを作成することで使用できる。

## 書き方

`<ライブラリ>.pc` ファイルの基本的な書き方は下のとおりである。

```:<ライブラリ>.pc
<オプション>: <出力>
```

また、`.pc` ファイルでは変数も使える（`Makefile` でも使える）。

```:<ライブラリ>.pc
<変数名>=<値>
```

変数を使いたいところに `${<変数名>}` と書けば、そこが `<値>` で置き換わって使うことができる。

オプションは自由に決められないが、変数は自由に決められる。

## .pc ファイルの作成

ではライブラリを使うための `.pc` ファイルを作ろう。 `lib/pkgconfig` の配下に `hello.pc` ファイルを作る。

```bash: ターミナル
$ mkdir lib/pkgconfig
$ touch lib/pkgconfig/hello.pc
```

1行目の `<hello_wsへのパス>` には `pwd` コマンドの出力をコピペして**書き換えて**ほしい。

```:hello.pc
prefix=<hello_wsへのパス>

Name: HelloLib
Version: 1.0
Description: Hello World! を出力するライブラリ

Libs: -L${prefix}/lib -lhello

Cflags: -I${prefix}/include
```

`Name`, `Version`, `Description` がないとエラーが出る。適当に書いておこう。

このままでは `pkg-config` が `.pc` ファイルを探せないので、環境変数 `PKG_CONFIG_PATH` に `lib/pkgconfig` を指定しておこう。

```bash: ターミナル
$  export PKG_CONFIG_PATH=$PWD/lib/pkgconfig:$PKG_CONFIG_PATH
```

`pkg-config` はオプションに応じて文字列を出力するだけのコマンドである。下のコマンドをそれぞれ実行してみよう。オプションの最初の文字が小文字になっていることに注意してほしい。

```bash: ターミナル
$ pkg-config --libs hello
$ pkg-config --cflags hello
```

オプションはまとめることができる。コンパイルしてみよう。`$()` は先に `()` 内のコマンドを実行して文字列を置換する。

```bash: ターミナル
$ gcc main.c -o hello $(pkg-config --cflags --libs hello)
$ ./hello
```

下のエラーが出るときはライブラリへのパスを指定していないためなので、 `export LD_LIBRARY_PATH=$PWD/lib:$LD_LIBRARY_PATH` を実行してほしい。（[ここを参照](https://qiita.com/KaoruHosokawa00/items/77122f99988e2f793b46#:~:text=%E7%92%B0%E5%A2%83%E5%A4%89%E6%95%B0%20LD_LIBRARY_PATH%20%E3%82%92%E6%8C%87%E5%AE%9A%E3%81%97%E3%81%AA%E3%81%84%E3%81%A8%E3%80%81%E5%AE%9F%E8%A1%8C%E5%8F%AF%E8%83%BD%E3%83%95%E3%82%A1%E3%82%A4%E3%83%AB%E3%81%AE%E5%AE%9F%E8%A1%8C%E6%99%82%E3%81%AB%E3%80%81%E8%87%AA%E4%BD%9C%E3%81%AE%E5%85%B1%E6%9C%89%E3%83%A9%E3%82%A4%E3%83%96%E3%83%A9%E3%83%AA%E3%82%92%E6%8E%A2%E3%81%99%E3%81%93%E3%81%A8%E3%81%8C%E3%81%A7%E3%81%8D%E3%81%AA%E3%81%84%E3%80%82)）

```:ターミナル
./hello: error while loading shared libraries: libhello.so: cannot open shared object file: No such file or directory
```

<!---

# CMake

ライブラリの作成とコンパイルのツールを見てきたが、最後にCMakeを紹介する。これは、ライブラリの作成とコンパイルのツールの両方を提供できる。

```cmake:
cmake_minimum_required(VERSION 3.22)
project(hello C)

set(WS_DIR /home/user/c_ws/hello_ws)

include_directories(include)

add_library(foo STATIC ${WS_DIR}/src/hello.c)
add_library(bar SHARED ${WS_DIR}/src/hello.c)

install(TARGETS foo bar DESTINATION ${WS_DIR}/lib_)
```


```lib/cmake/HelloConfig.cmake
#[[

あなたの CMakeLists.txt に以下の行を追加してください

include_directories(${Hello_INCLUDE_DIRS})
link_directories(${Hello_LIBRARY_DIRS})
target_link_libraries(${PROJECT_NAME} ${Hello_LIBRARIES})

]]

set(WS_DIR <hello_ws への絶対パス>)

set(Hello_INCLUDE_DIRS ${WS_DIR}/include)
set(Hello_LIBRARY_DIRS ${WS_DIR}/lib)
set(Hello_LIBRARIES libhello.so)
```

`<hello_ws への絶対パス>` は `hello_ws` への絶対パスに書き換えてほしい。

```cmake: CMakeLists.txt
cmake_minimum_required(VERSION 3.22)
project(hello C)

find_package(Hello REQUIRED)

include_directories(${Hello_INCLUDE_DIRS})
link_directories(${Hello_LIBRARY_DIRS})

add_executable(${PROJECT_NAME} main.c)

target_link_libraries(${PROJECT_NAME} ${Hello_LIBRARIES})
```

https://mickey-happygolucky.hatenablog.com/entry/2020/11/19/203903

https://theolizer.com/cpp-school3/cpp-school3-10/

https://qiita.com/osamu0329/items/bd3d1e50edf37c277fa9#config-%E3%83%A2%E3%83%BC%E3%83%89

https://qiita.com/shohirose/items/d9bda00a39a113965c5c

https://qiita.com/osamu0329/items/134de918c0ffa7f0557b

https://zenn.dev/turing_motors/articles/daf3e8829f0967

--->