2023.04.27

C++開発とCMake ~その3 : 外部ライブラリを自前でビルドして使う(OSC with liblo)~

前回までの記事


続いて外部ライブラリの使い方。 CMakeでは外部ライブラリを使う方法でfind_packageがあるが、CMake初心者としてはまずは外部ライブラリを自前でビルドして使う方法を試して、そのあとでfind_packageについて学ぼうと思う。

前回までのコードはこちら https://github.com/gollowars/cpp_cmake_bootstrap/tree/testlib このコードを修正して使っていく。


今回はOSCを使うためのライブラリのlibloをビルドしてリンクさせて使ってみる。

libloのインストール

プロジェクトに移動してlibloをおくディレクトリを作成しておく。

cd YOUR_PROJECT_PATH
mkdir liblo

私の場合プロジェクトルートはこの状態

.
├── CMakeLists.txt
├── README.md
├── build
├── data
├── include
├── liblo
├── main.cpp
└── src

libloをダウンロードする。 こちらのページのLatest stable releaseからダウンロードする。現在のバージョンはliblo 0.31 https://liblo.sourceforge.net/

ダウンロードしてきたファイルを展開する。

tar zxvf liblo-0.31.tar.gz

中にINSTALLというファイルがあるので、これを見て手順を確認する。 デフォルトではmake installすると/usr/local/binにインストールされるとの事だが、 今回はプロジェクトディレクトリに配置したいので、 --prefixを利用してプロジェクトのパスを指定する。

cd liblo-0.31
sh ./configure --prefix=YOUR_PROJECT_PATH # しばらく時間かかる。--prefixでインストール先を指定できる。
make
sudo make install

これでプロジェクトのlibloディレクトリにlibloのコンパイルしたデータやヘッダーファイルが配置される。 このような状態。

.
├── CMakeLists.txt
├── README.md
├── data
├── include
├── liblo
│   ├── bin
│   │   ├── oscdump
│   │   ├── oscsend
│   │   └── oscsendfile
│   ├── include
│   │   └── lo
│   │       ├── lo.h
│   │       ├── lo_cpp.h
│   │       ├── lo_endian.h
│   │       ├── lo_errors.h
│   │       ├── lo_lowlevel.h
│   │       ├── lo_macros.h
│   │       ├── lo_osc_types.h
│   │       ├── lo_serverthread.h
│   │       ├── lo_throw.h
│   │       └── lo_types.h
│   └── lib
│       ├── liblo.7.dylib
│       ├── liblo.dylib -> liblo.7.dylib
│       ├── liblo.la
│       └── pkgconfig
│           └── liblo.pc
├── main.cpp
└── src
    ├── CSVParser.cpp
    └── MyClass.cpp

CMakeLists.txtの修正

add_executableの下に以下を追加する。

# libloのインクルードディレクトリを指定
set(LIBLO_DIR ${CMAKE_CURRENT_SOURCE_DIR}/liblo)
include_directories(${LIBLO_DIR}/include)

# libloのライブラリを指定
add_library(liblo SHARED IMPORTED)
set_property(TARGET liblo PROPERTY IMPORTED_LOCATION ${LIBLO_DIR}/lib/liblo.dylib)
target_link_libraries(${PROJECT_NAME} liblo)

これでmain.cppにて

main.cpp
#include <lo/lo.h>

これで読み出せる。

ちなみにCMakeLists.txtの下の箇所を

include_directories(${LIBLO_DIR}/include)

以下に変えると

include_directories(${LIBLO_DIR}/include/lo)

main.cppで使う場合

main.cpp
#include <lo.h>

これで呼び出せるようになる。ただ、依存関係でおかしくなるとかありそうなのでincludeディレクトリを指定しておくのが良いでしょう。CMakeの指定の理解しとて勉強。


CMakeLists.txt全部見たい場合はこちら -> https://github.com/gollowars/cpp_cmake_bootstrap/blob/use-liblo/CMakeLists.txt

余談: includeの記述について

知らなかったのだが、

main.cpp
#include "test.h"
#include <test.h>

の違いは

#include "test.h": コンパイラは、現在のソースファイルと同じディレクトリから検索を始めます。もし見つからなければ、システムのインクルードディレクトリやプロジェクトで指定された追加のインクルードディレクトリを検索します。これは、主にプロジェクト固有のヘッダーファイルやライブラリのローカルコピーを参照する場合に使用されます。

#include <test.h>: コンパイラは、システムのインクルードディレクトリやプロジェクトで指定された追加のインクルードディレクトリから検索を始めます。これは、主にシステムにインストールされた共有ライブラリや標準ライブラリを参照する場合に使用されます。

とchatgptさんが言っていました。

ファイルを分離しつつ、find_packageを理解する。

find_packageはCMAKE_MODULE_PATHに指定されたディレクトリを検索し、Find<PackageName>.cmakeという名前のファイルを探す。

また、CMakeファイルの中で、Find<PackageName>.cmakeのファイルの場合find_package(<PackageName>)として指定して使う。

例えば、FindLiblo.cmakeという名前のファイルは、CMAKE_MODULE_PATHに含まれていればfind_package(Liblo)を使用して、その設定ファイルを読み込むことができる。


まずcmakeディレクトリを作りその中にFindLiblo.cmakeを作成する。

mkdir cmake
touch cmake/FindLiblo.cmake

FindLiblo.cmakeに以下を記述する。

cmake/FindLiblo.cmake
set(LIBLO_INCLUDE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/liblo/include")
if(APPLE)
    set(LIBLO_LIBRARY "${CMAKE_CURRENT_SOURCE_DIR}/liblo/lib/liblo.dylib")
elseif(WIN32)
    set(LIBLO_LIBRARY "${CMAKE_CURRENT_SOURCE_DIR}/liblo/lib/liblo.dll")
elseif(UNIX)
    set(LIBLO_LIBRARY "${CMAKE_CURRENT_SOURCE_DIR}/liblo/lib/liblo.so")
else()
    message(FATAL_ERROR "Unsupported operating system")
endif()

if (LIBLO_INCLUDE_DIR AND LIBLO_LIBRARY)
    set(Liblo_FOUND TRUE)
else()
    set(Liblo_FOUND FALSE)
endif()

if (Liblo_FOUND)
    message(STATUS "Found Liblo: ${LIBLO_LIBRARY}")

    add_library(liblo SHARED IMPORTED)
    set_target_properties(liblo PROPERTIES
        IMPORTED_LOCATION "${LIBLO_LIBRARY}"
        INTERFACE_INCLUDE_DIRECTORIES "${LIBLO_INCLUDE_DIR}"
    )
else()
    message(FATAL_ERROR "Liblo not found")
endif()

そしてCMakeLists.txtのadd_executableの下に、

CMakeLists.txt
## libloを読み込む設定を記述していく。

## CMAKE_MODULE_PATHにcmakeディレクトリを追加
list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake)

## libloを探す
find_package(Liblo REQUIRED)

# ライブラリをリンクする。
target_link_libraries(${PROJECT_NAME} 
    PRIVATE liblo
)

これで、ファイルを分離しlibloを読み込むことができる。 今回はlibloを自前でビルドしてプロジェクト配下に置いたが、CMAKE_MODULE_PATHにFind~.cmakeが追加されていれば、 find_packageで探すことができるみたいだ。

ここのPRIVATEの部分

target_link_libraries(${PROJECT_NAME} 
    PRIVATE liblo
)

target_link_libraries()のリンクタイプには、以下の3つのキーワードがある。

  • PRIVATE: ターゲット自体にのみ影響を与えるライブラリを指定します。これは、ターゲットが他のターゲットにリンクされても、そのライブラリは伝播しません。
  • PUBLIC: ターゲット自体と、そのターゲットをリンクする他のターゲットに影響を与えるライブラリを指定します。これにより、そのライブラリは自動的に伝播します。
  • INTERFACE: ターゲット自体には影響を与えず、そのターゲットをリンクする他のターゲットにのみ影響を与えるライブラリを指定します。

PRIVATEを使用することで、プロジェクトの依存関係をより明確にすることができ、ビルドやリンクの問題を防ぐことができる。 main.cppなどで使う場合は基本PRIVATEでよさそう。libraryを作る時などはPUBLICを使うなど考えた方がよさそう。


今回できたソースはこちら -> https://github.com/gollowars/cpp_cmake_bootstrap/tree/use-liblo-find

C++開発シリーズ

References