JUCE + CMake Tips

## はじめに

JUCE 6 から CMake でプロジェクトを生成できるようになったので、最近はオーディオプログラムを開発する際に CMake を触ることが増えてきたと思います。今回はそのあたりの Tips について紹介します。

この記事で紹介した内容を含んだサンプルプロジェクトを用意しました。以下の URL からアクセスできます。
https://github.com/hotwatermorning/juce-advent-calendar-2021

## 別階層にある JUCE を使いたいとき

以下のように CMakeLists.txt が置いてある場所と別のディレクトリに存在するプロジェクトがあるとします。

```
/path/to/my-project
|--- Project
|    |--- CMakeLists.txt
|    `--- Source　        ... ソースファイルディレクトリ
`--- Dependencies
     `--- JUCE           ... サブモジュールとして取り込んでいる JUCE
```

このようなプロジェクトで JUCE を使用する場合、下のようにして JUCE 参照しようとしても、 cmake コマンド実行時にエラーが発生して Configuration が失敗します。

```cmake
add_subdirectory("../dependencies/JUCE")
```

このようなディレクトリ構成で JUCE を利用するには下のように、 [add_subdirectory()](https://cmake.org/cmake/help/latest/command/add_subdirectory.html) コマンドに引数を追加して、 JUCE のビルドディレクトリを設定します。

```cmake
add_subdirectory("../dependencies/JUCE" build-juce)
```

これにより自分のプロジェクトのビルドディレクトリ内に build-juce ディレクトリが作成され、その中で JUCE がビルドされるようになります。

## ソースファイルを IDE で階層化したいとき

プロジェクトのソースファイルのうち、オーディオ関連のソースを Audio ディレクトリに、ユーティリティ関連のソースを Util フォルダに、 GUI 関連のソースを GUI ディレクトリに配置しているものとします。

このプロジェクトでソースファイルを以下のように設定した場合、

```cmake
target_sources(MyPlugin
    PRIVATE
    PluginEditor.h
    PluginEditor.cpp
    PluginProcessor.h
    PluginProcessor.cpp
    Util/MyUtil.h
    Util/MyUtil.cpp
    Audio/MyEffector.h
    Audio/MyEffector.cpp
    GUI/MyComponent.h
    GUI/MyComponent.cpp
)
```
Xcode や Visual Studio などの IDE ではソースファイルが、実際のファイルのディレクトリ階層を反映しないフラットな構造で表示されます。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/944/cbc27989-d269-e51c-11c5-452ab514dd1c.png" width="300px" alt="フラット版">

これを階層化して表示するには以下のように [source_group()](https://cmake.org/cmake/help/latest/command/source_group.html) コマンドを使用します。

```cmake
set(SOURCE_FILES
    PluginEditor.h
    PluginEditor.cpp
    PluginProcessor.h
    PluginProcessor.cpp
    Util/MyUtil.h
    Util/MyUtil.cpp
    Audio/MyEffector.h
    Audio/MyEffector.cpp
    GUI/MyComponent.h
    GUI/MyComponent.cpp
    )

source_group(TREE Source FILES ${SOURCE_FILES})
target_sources(MyPlugin PRIVATE ${SOURCE_FILES})
```

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/944/a6bf847b-ed03-d9d3-1bef-cd572a5a5328.png" width="300px" alt="階層化版">

## コミット番号をソースに埋め込みたいとき

AboutBox でバージョン番号などのアプリケーション情報を表示する際、コミット番号も表示したい場合があります。そのような場合は以下のようにします。

```cmake
# Configure 時にコミット ID を取得
execute_process(
  COMMAND "git" "rev-parse" "--short" "HEAD"
  OUTPUT_VARIABLE COMMIT_ID
  RESULT_VARIABLE RESULT_REV_PARSE
  OUTPUT_STRIP_TRAILING_WHITESPACE
  )

if(NOT (RESULT_REV_PARSE EQUAL 0))
  message(FATAL_ERROR "failed to get the current commit id: ${RESULT_REV_PARSE}")
endif()

# コミット ID をマクロとして利用できるようにする。
target_compile_definitions(MyPlugin PUBLIC MY_PLUGIN_COMMIT_ID=${COMMIT_ID})
```

これによって、C++ ソースファイル内で以下のようにしてコミット番号を利用できるようになります。

```cpp
#define TO_STR_I(x) #x
#define TO_STR(x) TO_STR_I(x)

std::string getCommitId()
{
  return TO_STR(MY_PLUGIN_COMMIT_ID);
}
```

コミット番号が変更されたときは、 cmake コマンドを実行してプロジェクトを再生成する必要があります。

## アイコンを .icns ファイルで設定したいとき（macOS）

JUCE の juce_add_plugin() コマンドでプラグインを作成する際、通常はスタンドアロン版のアイコンファイルを ICON_BIG や ICON_SMALL で指定します。

```cmake
juce_add_plugin(MyPlugin
    # VERSION ...                               # Set this if the plugin version is different to the project version
    ICON_BIG icon-big.png                     # ICON_* arguments specify a path to an image file to use as an icon for the Standalone
    ICON_SMALL icon-small.png
    PLUGIN_MANUFACTURER_CODE Juce               # A four-character manufacturer id with at least one upper-case character
    PLUGIN_CODE Dem0                            # A unique four-character plugin id with exactly one upper-case character
                                                # GarageBand 10.3 requires the first letter to be upper-case, and the remaining letters to be lower-case
    FORMATS AU VST3 Standalone                  # The formats to build. Other valid formats are: AAX Unity VST AU AUv3
    PRODUCT_NAME "MyPlugin")        # The name of the final executable, which can differ from the target name
```

ただ、この方法はアイコン画像を2種類の大きさでしか設定できないため、表示されるアイコンのサイズによっては期待しない見た目になる可能性があります。

macOS はアプリケーションのアイコンに複数サイズの画像を含んだ .icns ファイルを使用する仕組みをサポートしているため、これを利用できれば期待通りの見た目でアイコンを表示できるようになります。

JUCE を使ったプロジェクトで .icns ファイルを使用するには以下のようにします。（以下では使用するアイコンファイルの名前が icon.icns であるものとします）

1. juce_add_plugin() の PLIST_MERGE に CFBundleIconFile を指定した Plist ファイルの XML 文字列を設定する
2. .icns ファイルをアプリケーションの Bundle 内にコピーするように設定する

### juce_add_plugin() の PLIST_MERGE に CFBundleIconFile を指定した Plist ファイルの XML 文字列を設定する

```cmake
# juce_add_plugin() の PLIST_MERGE に指定するための文字列
set(PLIST_STRING_TO_MERGE "\
<?xml version=\"1.0\" encoding=\"UTF-8\"?>\
<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\
<plist version=\"1.0\">\
<dict>\
	<key>CFBundleIconFile</key>\
	<string>icon.icns</string>\
</dict>\
</plist>\
")

juce_add_plugin(MyPlugin
    PLIST_TO_MERGE ${PLIST_STRING_TO_MERGE}     # .icns ファイルの名前を含んだ Plist データの文字列を指定する。
    PLUGIN_MANUFACTURER_CODE Juce               # A four-character manufacturer id with at least one upper-case character
    PLUGIN_CODE Dem0                            # A unique four-character plugin id with exactly one upper-case character
                                                # GarageBand 10.3 requires the first letter to be upper-case, and the remaining letters to be lower-case
    FORMATS AU VST3 Standalone                  # The formats to build. Other valid formats are: AAX Unity VST AU AUv3
    PRODUCT_NAME "MyPlugin")        # The name of the final executable, which can differ from the target name
```

### .icns ファイルをアプリケーションの Bundle 内にコピーするように設定する

```cmake
if(APPLE)
    set(ICON_FILE ${CMAKE_CURRENT_LIST_DIR}/../Assets/icon.icns)
    target_sources(${TARGET} PRIVATE ${ICON_FILE})
    set_source_files_properties(${ICON_FILE} PROPERTIES MACOSX_PACKAGE_LOCATION "Resources")
endif()
```

これにより、スタンドアロンアプリケーションのアイコンが、指定した .icns ファイルになります。