本記事は JUCE Advent Calendar 2022 の12月22日向けに投稿した記事です。
JUCEをCMakeからインテグレートする際に、通常用意されているJUCE CMake APIではすんなり出来なかったことに挑戦してみました。
注意:Windowsでのみ試したため、他のプラットフォームだと上手く動かない可能性あり。
対象バージョン
- JUCE 7.0.3
- Visual Studio 2022
- CMake 3.15以上
- Git 2.0以上
事前情報
JUCEとは
JUCE (Jules' Utility Class Extensions)は、C++言語によるマルチメディア系アプリケーションの開発を支援するフレームワークです。
ライブラリの一部モジュールとして独自のGUIシステムを実装しており、開発者はそのAPIを利用することで、クロスプラットフォームなGUIアプリケーションを統一したコードベースで実装することができます。
公式サイト
CMakeとは
CMakeは、主にC言語、C++言語などC系言語のビルドにおけるクロスプラットフォームビルド環境を提供するビルドマネジメントツールの一つです。ソフトウェアのビルドにおける一連の処理をCMakeLists.txtに記述し、CMakeプログラムを通じて各種プラットフォーム用のビルドツールに向けたプロジェクトを出力することができます。
公式サイト
JUCEライブラリ と CMake
2020年にリリースされたJUCE 6から、ビルドマネジメントツール『CMake』のサポートが追加されました。
JUCEがCMakeをサポートすることで以下のメリットを得ることができるようになりました。
- ビルド設定をCMake用のフォーマットで記述することができる
- CMakeのエコシステムの恩恵を受けることができる
- CMakeが独自に提供する機能を利用することができる
- CMakeに対応した他のライブラリとのインテグレーションが容易になる
JUCEをCMakeからインテグレートする際のお作法
JUCEのCMake APIには、CMakeのターゲットを生成するAPIとして3種類のAPIが用意されています。
- juce_add_gui_app: GUIアプリケーションのターゲットを作成する(実行可能ファイル)
- juce_add_console_app: コンソールアプリケーションのターゲットを作成する(実行可能ファイル)
- juce_add_plugin: オーディオプラグインのターゲットを作成する(動的ライブラリ)
上記のように、「メインとなるアプリケーション・モジュールコードにJUCEをリンクして、エントリポイント(main関数)はJUCE CMake APIが提供する」という考え方になっています。
これはこれでカジュアルに使いやすいというメリットはあるのですが、今回筆者が求めていた構成を実現するには利用できないという事情がありました。
JUCE内のCMake APIドキュメントの抜粋
### Functions
#### `juce_add_<target>`
juce_add_gui_app(<target> [KEY value]...)
juce_add_console_app(<target> [KEY value]...)
juce_add_plugin(<target> [KEY value]...)
`juce_add_gui_app` and `juce_add_console_app` add an executable target with name `<target>`.
`juce_add_plugin` adds a 'shared code' static library target with name `<target>`, along with extra
targets for each of the specified plugin formats. Each of these functions also takes a number of
optional arguments in the form of a `KEY` followed by one or more `value`s which can be used to set
additional attributes of the target. If these optional arguments aren't specified, their values will
fall back to sensible defaults.
以下の構成となるCMakeターゲットを作成したい!
- JUCEをリンクする中間ライブラリ(Coreライブラリ)
- 中間ライブラリをリンクするアプリケーション(App.exe)
- 中間ライブラリをリンクするアプリケーションその2(App2.exe)
- アプリケーションからJUCE APIを呼ぶことができる
今回作成したリポジトリ
上記の構成を実現したGitHubリポジトリです。
CMakeList.txt
上記リポジトリに含まれるCMakeLists.txt
ファイルの記述です。
cmake_minimum_required(VERSION 3.15)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)
project(JUCE-LAYERED-INTEGRATE VERSION 0.0.1)
if(MSVC)
# static linking in Windows
set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>:Debug>")
endif()
# Adds all the module sources so they appear correctly in the IDE
set_property(GLOBAL PROPERTY USE_FOLDERS YES)
option(JUCE_ENABLE_MODULE_SOURCE_GROUPS "Enable Module Source Groups" ON)
# Add JUCE Package
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/External/JUCE)
# Add static library target
add_library(Core STATIC)
_juce_initialise_target(Core)
_juce_set_output_name(Core $<TARGET_PROPERTY:Core,JUCE_PRODUCT_NAME>)
target_link_libraries(Core
PUBLIC
juce::juce_analytics
juce::juce_audio_basics
juce::juce_audio_devices
juce::juce_audio_formats
juce::juce_audio_plugin_client
juce::juce_audio_processors
juce::juce_audio_utils
juce::juce_box2d
juce::juce_core
juce::juce_cryptography
juce::juce_data_structures
juce::juce_dsp
juce::juce_events
juce::juce_graphics
juce::juce_gui_basics
juce::juce_gui_extra
juce::juce_opengl
juce::juce_osc
juce::juce_product_unlocking
juce::juce_video
)
target_sources(Core
PRIVATE
"Core/Source/Core.cpp"
)
# Add application target
add_executable(App)
target_sources(App
PRIVATE
"App/Source/Main.cpp"
)
target_include_directories(App
PRIVATE
"Core/Source"
)
target_link_libraries(App
PRIVATE
Core
)
# Add another application target
add_executable(App2)
target_sources(App2
PRIVATE
"App2/Source/Main.cpp"
)
target_include_directories(App2
PRIVATE
"Core/Source"
)
target_link_libraries(App2
PRIVATE
Core
)
抜粋
上記のスクリプトにおいて、JUCEをリンクする中間ライブラリを生成する処理は以下の部分になります。
# 静的ライブラリのターゲットを追加する
add_library(Core STATIC)
# JUCE CMake APIからJUCE特有のターゲット初期化処理を実行する
_juce_initialise_target(Core)
_juce_set_output_name(Core $<TARGET_PROPERTY:Core,JUCE_PRODUCT_NAME>)
# 生成する静的ライブラリにリンクするJUCEモジュールを列挙する
# このとき、"PUBLIC"オプションを選択するくことで、このCoreターゲットをリンクするAppターゲットに対してJUCEライブラリのAPIを公開することができる。
target_link_libraries(Core
PUBLIC
juce::juce_analytics
juce::juce_audio_basics
juce::juce_audio_devices
juce::juce_audio_formats
juce::juce_audio_plugin_client
juce::juce_audio_processors
juce::juce_audio_utils
juce::juce_box2d
juce::juce_core
juce::juce_cryptography
juce::juce_data_structures
juce::juce_dsp
juce::juce_events
juce::juce_graphics
juce::juce_gui_basics
juce::juce_gui_extra
juce::juce_opengl
juce::juce_osc
juce::juce_product_unlocking
juce::juce_video
)
注意事項
JUCEに限らずC++のライブラリのリンクに関することではあるのですが、以下の点に注意しましょう。
- プロジェクトごとに異なるモジュール・オプションを必要とする場合、同じJUCEオブジェクト・ファイルを複数のプロジェクトで再利用することは危険
- JUCEの静的ライブラリをターゲット間で共有する場合、依存する全てのターゲットが全く同じ JUCEモジュールとモジュール設定フラグのセットを使用することを確認する必要がある