JUCE 5を引き続きmacOSで使い続けるための覚え書き


本記事はJUCE Advent Calendar 2020 の12月9日向けに投稿した記事です。

JUCEって何?

JUCE (Jules' Utility Class Extensions)は、C++言語によるマルチメディア系アプリケーションの開発を支援するフレームワークです。クロスプラットフォーム設計のライブラリと、付属されているプロジェクトジェネレータ『Projucer』から各種IDE(VisualStudio, Xcode, Makefile)向けにプロジェクトファイルを出力することで、ワンソースからWindows, macOS, Linux, iOS, Android で動作するアプリケーションを作成することができます。
公式サイト

本記事の対象環境

OS: macOS
IDE: Xcode 11以降
JUCE: JUCE v5.4.7

JUCEのアップデートについて

フレームワーク/ライブラリにつきものなのが、機能追加やバグ修正に伴うメジャー・マイナーアップデートです。
JUCEでは、これまでに1→2→3→4→5→6とナンバリング変更を伴うアップデートが行われてきました。

JUCEではプロダクトナンバリングが変更されるタイミングでライセンスを改めて購入する機会が発生します。
例えば、JUCE 5のIndie/Proライセンスを所持している方が、JUCE 6をIndie/Proライセンスで利用したい場合には、JUCE 6版のIndie/Proライセンスを改めて購入する必要があります。一方で、JUCE 6のIndie/Proライセンスを購入すると、JUCE 5以前のProライセンスが付属してきます。

このような「ライセンスを改めて購入する機会」というものは、既に開発が進行中のプロジェクトにおいては悩みの種となります。
例えば、既にJUCE 5を利用したプロジェクトにおいて、開発途中にJUCE 6に切り替えることは、APIの破壊的変更に対応するためにプロジェクト側コードの修正作業が発生することがあります。
さらに、JUCEは5→6にナンバリングを変更したのを機に、ライセンス料金を以前よりも引き上げたこともあり、よほど重要な追加機能に魅力を感じない限り、JUCE 5で進行中のプロジェクトをJUCE 6に切り替える魅力も少ないと思われます。

なお、JUCEにはLong Term Support(LTS)版というラインナップは設定されておらず、「最新版に適用したコードの変更はバックポートしない」という運用を取っています。そのため、過去のナンバリングで発生したバグや問題については対応されません。

Xcode 11以降のmacOS SDKでビルドできなくなる問題の対応

JUCEはクロスプラットフォームなC++アプリケーションフレームワークであり、そのソースコードには各種プラットフォームのNative APIとリンクされたコードが多数含まれています。
そのため、Native側のAPIやSDKに変更が発生した場合、JUCE側でもその変更に対応するようにコードを変更することがあります。

最近(2019年頃)に大きな影響を受けたのが、macOS SDKのアップデートによって、公開クラスにNS接頭辞がない汎用的な名前のクラス(Componet、Point、AudioBufferなど)が露出してきたというプラットフォーム側の変更でした。

実は、Objective-Cの仕様には名前空間がないため、クラス名のスコープはグローバル名前空間に露出してしまうため、"Point"や"Component"といった汎用的な名前をライブラリ側のコードで宣言されると、アプリケーション側のコードで宣言した同名のクラス名と多重定義が発生してしまいやすくなるのです。
※そのような課題を回避するためにNS接頭辞などが付けられる慣習になっているはずなのですが…
※おそらく、Swiftでは名前空間が存在するため、Swift向けを想定してmacOS SDKのメンテナンスが進めるにあたって、Prefixを付ける必要がないという事情があるからだと思います。

それに関連した問題が、Xcode 11以降のmacOS SDKがリリースされたタイミングで頻発しました。
その問題はフォーラムでもIssueとして挙がっていました。

JUCE forumの関連スレッド
https://forum.juce.com/t/reference-to-point-is-ambiguous/24884
https://forum.juce.com/t/juce-is-busted-in-xcode-11-4/38249

同様の問題は別のクロスプラットフォームフレームワークであるCocos2d-xにおいても発生しており、Cocos2d-xにおいても後述する同様の解決方法にて対応策を示しています。
https://discuss.cocos2d-x.org/t/error-reference-to-point-is-ambiguous-in-mactypes-h/11853/17

JUCEサポートに報告されたIssueのうち、JUCE 5がラインナップ中に報告された事項については、JUCE 5で修正が適用されているものの、JUCE 5の最終版v5.4.7のリリース以降に報告されたIssueについての修正は、JUCE 6から取り込まれることとなり、JUCE 5には取り込まれませんでした。

その事情もあって、JUCE 5で引き続き最新のXcodeとmacOS SDKで開発を行うには、私が確認した限りでは、以下の作業を行って対処をする必要があります。

A. AudioUnitをビルドする際に必要な修正

Xcode 11以降でAudioUnitをビルドする場合に、次のコンパイルエラーが表示されます。

Reference to 'AudioBuffer' is ambiguous

解決方法: エラー箇所のクラス名にjuce名前空間を明示的に付与する

■ 修正前


■ 修正後

B.JUCEのクラスを混在したObjective-Cコードを実装する場合

macOSのNative APIを呼び出す処理を実装するためにObjective-Cコードを記述することがありますが、例えば次のように、JUCEのライブラリヘッダーとmacOS SDKのFoundationヘッダーを同時にinclude/importするObjective-Cコードをコンパイルしようとすると、JUCE側で宣言されたクラスとmacOS SDK側で宣言されたクラスとの間に多重定義のエラーが発生します。

■ ObjC.mm

#include <JuceHeader.h>
#import <Foundation/Foundation.h>

■ ObjC.mmのコンパイルエラーメッセージ

解決方法: using namespace juce;の宣言を無効化する

JUCE 5では、JuceHeader.h内にusing namespace juce;の宣言がdefaultで有効になっています。

macOS SDKとJUCEとのクラス名の衝突をusing namespace juce;の宣言を無効化することで解決します。
これに関して、DONT_SET_USING_JUCE_NAMESPACEプリプロセッサを有効にすることで無効化する仕組みが予め用意されています。

JuceHeader.h内のusing namespace juce;のdefault適用を無効化したことにより、をincludeするソースには、JUCEのクラスであることを明記するためにjuce::Xxxとjuce名前空間をクラス名に付加するか、もしくはusing namespace juce;をソース内に記述することで、JUCE名前空間のクラスであることを認識させるようにします。

補足. JUCE 6ではどうなっているか

JUCE 6からはusing namespace juce;の宣言がJuceHeader.hから削除されています。そのため、DONT_SET_USING_JUCE_NAMESPACEプリプロセッサは不要です。