0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Acrobatプラグイン開発を「秘伝のタレ」から卒業する macOS編 ― CMakeで .acroplugin をビルドしてAcrobatに読み込ませる

0
Last updated at Posted at 2026-10-19

はじめに

前回の記事では、Acrobat SDKをCMakeパッケージにして、Windows向けのプラグインをCMakeでビルドするところまでを書きました。

今回はその続きとして、前回のCMakeLists.txtに設定を追加して、同じソースからmacOS用のプラグインをビルドできるようにします。

私は最近のMac用のアプリケーションをビルドするのは初めてです。かなり昔、AppleのMacintoshがSystem 7とかMac OS 8のころに、Indide Macintoshを片手にQuickDrawに挑むべく、Think CやCodeWarrierを使って画像を表示させたり、フォーマットを変換したりするアプリを作ったりしたことがありますがそれ以来です。最初はWindowsのコードとほとんど変わらないため、拡張子を .acroplugin に変えるくらいで済むだろうと軽く考えていました。実際には、Objective-C++でのコンパイル、プレフィックスヘッダ、ユニバーサルバイナリ、署名と、Windowsでは意識しなかった話が次々に出てきました。最後には、Acrobatに読み込まれているのにメニューが出てこないという状態に陥り、なかなか解決ができませんでした。この原因は、Info.plistのキーが1つ足りないことでした。しかもそのキーは、Acrobat SDKのマニュアルに載っていないものでした。

この記事では、ハマったところと、原因を突き止めるまでの手順を順番に書いていきます。

この記事の対象読者

  • 前回の記事の構成で、Windows向けのプラグインをビルドできている人
  • Acrobatプラグインを、Xcodeのサンプルプロジェクトを使わずにmacOS向けにビルドしたい人

検証環境

項目 バージョン
OS macOS 26.5.1(Apple Silicon)
Xcode 26.2
コンパイラ AppleClang 21
CMake 4.4.3(CMakeLists.txtの要求は 3.16 以上)
Acrobat Acrobat Studio 26.002.21931
Acrobat SDK 前回作った acrobat-sdk-cmake。中身はAcrobat SDK配布物の Headers/ と、それを束ねるCMakeLists.txt

本記事の内容は、2026年10月時点で上の組み合わせを使って確認した結果です。

プラグイン側のソース src/BasicPlugin.cpp と src/BasicPluginInit.cpp は、Acrobat SDK配布物に含まれるサンプル BasicPlugin のソースをコピーしたもので、前回の記事から変えていません。手元のSDKでは Acrobat_DC_SDK_Windows_2021_v3/Adobe/Acrobat DC SDK/Version 1/PluginSupport/Samples/BasicPlugin/sources に入っていました(配布物内のパスはSDKの版によって違うので、見つからない場合は BasicPlugin で探してみてください)。

ゴール

  • 前回のCMakeLists.txtに elseif(APPLE) を足すだけで、macOS用の .acroplugin を作れるようにする
  • C++のソースはWindowsと共通のままにする(Mac用の分岐をソースに足さない)
  • コマンドラインでもXcodeでもビルドできるようにする
  • 作ったプラグインをAcrobatに読み込ませ、初期化が最後まで成功するところまで確認する

macOSのAcrobatプラグインはバンドル

Windowsのプラグインは、拡張子を .api にしたDLLでした。macOSのプラグインは1つのファイルではなく、.acroplugin という拡張子のバンドル(決まった構成のフォルダ)です。手元のFinderでは1つのファイルのように表示され、右クリックの「パッケージの内容を表示」で中を開くと、次のような構成になっています。

MyCustomPlugin.acroplugin/
└── Contents/
    ├── Info.plist          ← バンドルの情報(ID、バージョン、エントリポイント名など)
    ├── MacOS/
    │   └── MyCustomPlugin  ← プラグイン本体(Mach-Oのbundle形式)
    └── _CodeSignature/     ← 署名(ビルド方法によっては作られない。後述)

_CodeSignature/ は常にあるわけではありません。手元では、Xcodeジェネレータでビルドした build-xcode/Release/MyCustomPlugin.acroplugin では作られましたが、Makefileジェネレータでビルドした build/MyCustomPlugin.acroplugin には作られませんでした。この違いは後半の「署名について」で扱います。

Acrobat本体に同梱されているプラグインも同じ形をしていて、次の場所で確認ができます。後半で、これと見比べたことが解決の糸口になりました。

ls "/Applications/Adobe Acrobat DC/Adobe Acrobat.app/Contents/Plugins"

設定

ステップ1:バンドルを作る

まずはプラグイン側のプロジェクトで、バンドルの形で出力されるようにCMakeLists.txtに以下の設定を追加します。

elseif(APPLE)
    add_library(MyCustomPlugin MODULE
        src/BasicPlugin.cpp
        src/BasicPluginInit.cpp
        ${acrobatsdk_SOURCE_DIR}/Headers/API/PIMain.c
    )
    set_target_properties(MyCustomPlugin PROPERTIES
        BUNDLE TRUE
        BUNDLE_EXTENSION "acroplugin"
        MACOSX_BUNDLE_INFO_PLIST "${CMAKE_CURRENT_SOURCE_DIR}/Info.plist.in"
    )

Windowsでは SHARED でしたが、macOSでは MODULE にします。SHARED はほかのプログラムからリンクして使うライブラリ(.dylib)で、MODULE は実行時に dlopen で読み込まれる前提のもの(Mach-Oの bundle 形式)です。Acrobatプラグインは後者です。この区別はCMakeのドキュメントにも書かれています(add_library — Normal Libraries の MODULE:A Module Library: a plugin that may not be linked by other targets, but may be dynamically loaded at runtime using dlopen-like functionality.)。

BUNDLE TRUE を付けると、CMakeが Contents/MacOS/ を含むバンドルの構成で出力してくれます。拡張子は BUNDLE_EXTENSION で指定します(既定は bundle)。

Info.plistのテンプレート

Macでは Info.plist という、システムにあらかじめ知らせておく情報やビルド時のユーザ定義情報を設定するファイルが必要です。

MACOSX_BUNDLE_INFO_PLIST には、Info.plistのテンプレートを指定します。テンプレートの中の @変数名@ は、CMakeがビルド時に値を埋めてくれます(configure_file と同じ置換です)。

Info.plist.in
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
	<key>CFBundleDevelopmentRegion</key>
	<string>English</string>
	<key>CFBundleExecutable</key>
	<string>MyCustomPlugin</string>
	<key>CFBundleGetInfoString</key>
	<string>@MACOSX_BUNDLE_INFO_STRING@</string>
	<key>CFBundleIconFile</key>
	<string></string>
	<key>CFBundleIdentifier</key>
	<string>@MACOSX_BUNDLE_GUI_IDENTIFIER@</string>
	<key>CFBundleInfoDictionaryVersion</key>
	<string>6.0</string>
	<key>CFBundleLongVersionString</key>
	<string>@MACOSX_BUNDLE_LONG_VERSION_STRING@</string>
	<key>CFBundleName</key>
	<string>@MACOSX_BUNDLE_BUNDLE_NAME@</string>
	<key>CFBundlePackageType</key>
	<string>XTND</string>
	<key>CFBundleShortVersionString</key>
	<string>@MACOSX_BUNDLE_SHORT_VERSION_STRING@</string>
	<key>CFBundleSignature</key>
	<string>CARO</string>
	<key>CFBundleVersion</key>
	<string>@MACOSX_BUNDLE_BUNDLE_VERSION@</string>
	<key>CSResourcesFileMapped</key>
	<true/>
	<key>MainEntryPointV2</key>
	<string>AcroPluginMain</string>
	<key>NSHumanReadableCopyright</key>
	<string></string>
</dict>
</plist>

CFBundlePackageType はバンドルの種類を表す4文字コードです。ここで指定している XTND と、CFBundleSignature の CARO は、Adobeのマニュアルに次のように書かれている値です。

PowerPC plugins must have creator CARO (CFBundleSignature) and type XTND (CFBundlePackageType).

PowerPC時代の記述ですが、XTND はAcrobat本体に同梱されている現行のプラグインにも入っています。CFBundleSignature のほうは、同梱プラグインは CARO ですが、今回の検証では ????でも読み込まれました。ここでは同梱プラグインに合わせて CARO にしています。

MainEntryPointV2 はこのプラグインのエントリーポイントを指定するものなのですが、最初はこれを設定していませんでした。上記のマニュアルには Each plugin exports a single main entry point, AcroPluginMain. とあり、エントリポイントをエクスポートすればAcrobatがそこへ飛ぶ、と読めます。実際に nm -gU でも _AcroPluginMain はエクスポートされていました。Info.plistにエントリポイント名を書く必要があるという記述は、このマニュアルには見つけられなかったのでそのままにしていたのですが、これが最後にハマることになりました。

    set_target_properties(MyCustomPlugin PROPERTIES
        # (前略)
        MACOSX_BUNDLE_GUI_IDENTIFIER "com.example.MyCustomPlugin"
        MACOSX_BUNDLE_BUNDLE_NAME "${PROJECT_NAME}"
        MACOSX_BUNDLE_INFO_STRING "${PROJECT_NAME} ${PROJECT_VERSION}"
        MACOSX_BUNDLE_LONG_VERSION_STRING "${PROJECT_VERSION}"
        MACOSX_BUNDLE_SHORT_VERSION_STRING "${PROJECT_VERSION}"
        MACOSX_BUNDLE_BUNDLE_VERSION "${PROJECT_VERSION}"
    )

PROJECT_VERSION は、前回の記事で project(MyCustomPlugin VERSION 1.0.0 ...) と書いておいた値です。macOSではこれがInfo.plistの4か所に入るので、バージョンを上げるときは project() の1か所を変えるだけで済みます。なお、プラグインのバイナリ本体にはバージョン情報は入りません。Windowsの .api も、バージョン情報のリソース(.rc)を用意していないので、今の構成ではどこにもバージョンが入っていません。

CFBundleIdentifier は逆ドメイン形式で、自分の組織のものに置き換えます。


ステップ2:Objective-C++としてコンパイルする

バンドルの設定ができたのでビルド(ビルドの方法は後述しています)してみると、SDKのヘッダの中で NSURL が分からないというエラーで止まります。原因は、SDKの Headers/API/WLHFT.h にある次の1行です(手元のSDKでは42〜44行目)。

Headers/API/WLHFT.h(42-44行目)
#if MAC_PLATFORM
typedef NSURL* WDAppSpecifier;
#endif

調べてみると、NSURL はObjective-Cのクラスなので普通のC++としてコンパイルしていると存在しません。SDKのほかのヘッダも、たとえば Headers/API/AVExpT.h はObjective-Cのときだけ AppKit を読み込むなど、Objective-Cとしてコンパイルされる前提の書き方になっています(同ヘッダでは NSView* を #if __OBJC__ で切り替えている箇所もあります)。

Headers/API/AVExpT.h(62-66行目)
#elif MAC_PLATFORM
#if __OBJC__
#include <AppKit/AppKit.h>
#endif
#include <CoreServices/CoreServices.h>

つまり、macOSのAcrobat SDKは、ソースをObjective-C++としてコンパイルすることを前提にしています。

最初は、ソースの先頭に class NSURL; という前方宣言を足してエラーを消す修正が入っていました。ビルドは通りますが、Mac用の分岐をソースに入れることになり、ゴールから外れます。そこでソースには手を入れず、CMake側でObjective-C++としてコンパイルするように指定しました。

    enable_language(OBJCXX)
    set_source_files_properties(
        src/BasicPlugin.cpp
        src/BasicPluginInit.cpp
        ${acrobatsdk_SOURCE_DIR}/Headers/API/PIMain.c
        PROPERTIES LANGUAGE OBJCXX
    )

OBJCXX 言語のサポートはCMake 3.16で追加されたものです(CMake 3.16 リリースノート:CMake learned to support the Objective C (OBJC) and Objective C++ (OBJCXX) languages. They may be enabled via the project() and enable_language() commands.)。コマンドの仕様は enable_language にあります。前回の記事で cmake_minimum_required(VERSION 3.16) にしていたので、そのまま使えました。拡張子が .cpp や .c のままでも、ソースファイルの LANGUAGE プロパティに OBJCXX を指定すればObjective-C++としてコンパイルされます。

PIMain.c を LANGUAGE CXX にするのではだめなのか

PIMain.c をC++としてコンパイルする方法もありますが、clangが treating 'c' input as 'c++' when in C++ mode, this behavior is deprecated という警告を出します。ほかのファイルはどのみちObjective-C++にするので、PIMain.c もまとめてObjective-C++にしておく方がすっきりします。


ステップ3:プレフィックスヘッダを用意する

BasicPluginのソースの先頭は、次のようになっています。

BasicPlugin.cpp
// Acrobat Headers.
#ifndef MAC_PLATFORM
#include "PIHeaders.h"
#endif

Windowsのときだけ PIHeaders.h を読み込み、macOSでは読み込まない書き方です。macOSではどこでSDKのヘッダを読み込むのかというと、SDKにMac用のプレフィックスヘッダ(すべてのソースの先頭で自動的に読み込まれるヘッダ)として Headers/SDK/PIHeaders++.pch が入っていました。

Headers/SDK/PIHeaders++.pch(抜粋)
#include <Carbon/Carbon.h>
...
#define Platform_Carbon					1	/* use Carbon MacOS */
...
#define PLATFORM "MacPlatform.h"
#define PRODUCT "Plugin.h"
#include "Environ.h"
#include "PIHeaders.h"

Xcodeのサンプルプロジェクトではこのプレフィックスヘッダを読み込ませていて、そのためソース側では PIHeaders.h を読み込まない作りなのだと思います(サンプルプロジェクト自体は確認していません)。

このファイルをそのまま使うこともできそうですが、ACRO_SDK_LEVEL を 0x00090000(SDKバージョン9。同ファイル35行目)に定義していたりと内容がかなり古いので、必要な部分だけをCMakeで再現しました。

    # Mac ではソースが PIHeaders.h を読み込まないので、プレフィックスヘッダとして渡す
    target_precompile_headers(MyCustomPlugin PRIVATE <Carbon/Carbon.h> <PIHeaders.h>)

    target_compile_definitions(MyCustomPlugin PRIVATE
        PLATFORM=\"MacPlatform.h\"
        PRODUCT=\"Plugin.h\"
        Platform_Carbon=1
    )

target_precompile_headers はプリコンパイル済みヘッダを作るためのコマンドです。ドキュメントには The cmake_pch.h|xx header file will be force included (-include for GCC, /FI for MSVC) to all source files とあり、指定したヘッダをまとめたヘッダが、clangでも -include で全ソースの先頭に強制インクルードされます。そのため、プレフィックスヘッダの代わりにも使えます。

生成されるプリコンパイル済みヘッダの名前

この記事の設定を最後まで入れたあとでビルドすると、次のファイルが生成されます。

$ ls build/CMakeFiles/MyCustomPlugin.dir/ | grep pch
cmake_pch_arm64.objcxx.hxx
cmake_pch_arm64.objcxx.hxx.mm
cmake_pch_arm64.objcxx.hxx.pch
cmake_pch_arm64.objcxx.hxx.pch.d
cmake_pch_x86_64.objcxx.hxx
cmake_pch_x86_64.objcxx.hxx.mm
cmake_pch_x86_64.objcxx.hxx.pch
cmake_pch_x86_64.objcxx.hxx.pch.d
cmake_pch.objcxx.hxx

.mm はCMakeが生成する中間のObjective-C++ソース、.pch.d は依存関係ファイルです。プレフィックスヘッダの実体は .hxx、プリコンパイル済みの成果物は .pch です。

.objcxx. が入っているのは、ステップ2で LANGUAGE OBJCXX を指定したからです。_arm64 と _x86_64 に分かれているのは、このあとステップ4で入れるユニバーサルバイナリの指定によって、プリコンパイル済みヘッダもアーキテクチャごとに作られるためです(この時点の設定ではまだ分割されません)。

PLATFORM と PRODUCT は必須です。SDKの Headers/API/Environ.h(29〜38行目)では、WindowsやOS/2、UNIXにはこの2つの既定値がありますが、macOSにはなく、定義していないと #error でビルドが止まります。

Headers/API/Environ.h(29-38行目、抜粋)
#ifndef PLATFORM
#ifdef WIN_ENV
#define PLATFORM "winpltfm.h"
...
#else
#error You must define the PLATFORM macro
#endif
#endif

Platform_Carbon は、手元のSDKのヘッダを検索した限りでは Headers/SDK/PIHeaders++.pch:31 と Headers/API/PIHeaders.pch:22 の2か所で定義されているだけで、参照している箇所は見つかりませんでした。なくても良さそうですが、プレフィックスヘッダに合わせて残しています。Carbon/Carbon.h も同じ理由でプレフィックスヘッダに入れていますが、こちらを外してもビルドが通るかは確認していません。Carbonは古いフレームワーク(macOS 26 SDKにはまだ Carbon.framework が存在します)なので、将来的には外せるかどうかを確かめておく価値がありそうです。

MAC_PLATFORM は前回作ったSDK側のCMakeLists.txtの elseif(APPLE) の中で、ACRO_SDK_LEVEL は共通の定義として、それぞれ定義済みなので、プラグイン側で書く必要はありません(該当部分は末尾の「付録:SDKパッケージ側のCMakeLists.txt」で確認できます)。前回「今回は使わないが、Mac用の定義もここに書いておく」と書いておいた部分が、ここで役に立ちました。

CoreFoundationをリンクする

SDKの PIMain.c は、macOSでは CFRetain や CFBundleOpenBundleResourceMap などCoreFoundationの関数を呼んでいるので、CoreFoundationフレームワークをリンクします。前回、Acrobat_SDK への target_link_libraries は名前に反して何もリンクしていないと書きましたが、こちらは本当にリンクしています。

    find_library(COREFOUNDATION_FRAMEWORK CoreFoundation REQUIRED)
    target_link_libraries(MyCustomPlugin PRIVATE "${COREFOUNDATION_FRAMEWORK}")

ステップ4(オプショナル):ユニバーサルバイナリにする場合

ビルドが通ったので出来上がったバイナリを確認すると、次のようになっていました。

$ lipo -info MyCustomPlugin.acroplugin/Contents/MacOS/MyCustomPlugin
Non-fat file: ... is architecture: arm64

$ otool -l MyCustomPlugin.acroplugin/Contents/MacOS/MyCustomPlugin | grep minos
    minos 26.0

何も指定しないと、CMakeはビルドしたMacのアーキテクチャ(Apple Siliconならarm64だけ)向けに、手元のSDKのバージョン(ここではmacOS 26.0)を最低OSとしてビルドします。このままだと、Intel MacのAcrobatや、macOS 26より前のOSでは読み込めません。指定に使う変数は CMAKE_OSX_ARCHITECTURES と CMAKE_OSX_DEPLOYMENT_TARGET です。

project() の前に2行足すと解決します。

cmake_minimum_required(VERSION 3.16)

# project() より前に書く必要がある
set(CMAKE_OSX_ARCHITECTURES "arm64;x86_64" CACHE STRING "macOS target architectures")
set(CMAKE_OSX_DEPLOYMENT_TARGET "12.0" CACHE STRING "Minimum macOS version")

project(MyCustomPlugin VERSION 1.0.0 LANGUAGES C CXX)
$ lipo -info MyCustomPlugin.acroplugin/Contents/MacOS/MyCustomPlugin
Architectures in the fat file: ... are: x86_64 arm64

最低OSの 12.0 は、今回の検証で決めた値ではありません。対象にするAcrobatがサポートしているmacOSのバージョンに合わせて決めてください。AcrobatのサポートOSは版ごとに変わるので、Adobeのシステム要件のページで都度確認するのが確実です。

手元のXcodeは最新に近いバージョンなので、古い環境でも動くのか気になりましたが、バイナリがどのmacOSで動くかを決めるのは、Xcodeのバージョンではなくこの CMAKE_OSX_DEPLOYMENT_TARGET です。なお、arm64を含むユニバーサルバイナリを作るには、Xcode 12.2以降が必要です(Building a universal macOS binary)。

既存のビルドディレクトリには反映されない

CMAKE_OSX_ARCHITECTURES は、最初のconfigureのときにCMakeが空の値でキャッシュに登録します。set(... CACHE ...) は(FORCE を付けない限り)キャッシュにある値を上書きしないので、一度configureした build/ では、この2行を足しても反映されません。build/ を削除してconfigureし直すか、-DCMAKE_OSX_ARCHITECTURES="arm64;x86_64" を明示して実行します。


ビルドする

コマンドライン

コマンドはWindowsのときと同じです。成果物は build/MyCustomPlugin.acroplugin に出力されます。

cmake -B build
cmake --build build

Xcodeのプロジェクトを生成する

-G Xcode を付けると、Xcodeのプロジェクトを生成できます。成果物は build-xcode/Release/MyCustomPlugin.acroplugin に出力され、build-xcode/MyCustomPlugin.xcodeproj を開けばXcodeからもビルドできます。

cmake -B build-xcode -G Xcode
cmake --build build-xcode --config Release

ここで、Xcodeはインストールされているのに、configureが次のエラーで失敗しました。

xcode-select: error: tool 'xcodebuild' requires Xcode, but active developer directory '/Library/Developer/CommandLineTools' is a command line tools instance

macOSでは、使う開発ツールを xcode-select で切り替えています。これがXcode本体ではなく、Command Line Tools(Xcodeなしで入れられるコマンドラインツール一式)を指していたのが原因でした。コマンドラインのビルドはCommand Line Toolsでも動くので、Xcodeのプロジェクトを生成しようとするまで気づきませんでした。

# そのコマンドの間だけXcodeを使う(まずはこちらを試すのがおすすめ)
DEVELOPER_DIR=/Applications/Xcode.app/Contents/Developer cmake -B build-xcode -G Xcode

# 恒久的にXcodeに切り替える(管理者パスワードが必要。マシン全体の設定が変わる)
sudo xcode-select -s /Applications/Xcode.app/Contents/Developer

sudo xcode-select -s はマシン全体の開発ツールの向き先を変えるので、ほかのプロジェクトのビルドにも影響します。切り替えたくない場合は、上の DEVELOPER_DIR のようにそのコマンドの環境変数だけで指定してください。現在の向き先は xcode-select -p で確認できます。


署名について

Apple SiliconのMacでは、実行するarm64のコードに少なくとも簡易的な署名(アドホック署名、証明書を使わない署名)が必要とされています。この要件を明文で書いたAppleのドキュメントは見つけられませんでした。手元では、何も指定しなくてもリンカがarm64側にアドホック署名を付けていたので、普段これを意識することはありませんでした。

$ codesign -dv --verbose=4 --arch arm64 build/MyCustomPlugin.acroplugin/Contents/MacOS/MyCustomPlugin
Identifier=MyCustomPlugin
...
CodeDirectory v=20400 size=551 flags=0x20002(adhoc,linker-signed) hashes=14+0 location=embedded
...
Signature=adhoc
...
TeamIdentifier=not set

(出力は全24行あるので、関係する行だけを抜粋しています)

flags の adhoc,linker-signed が、「アドホック署名」かつ「リンカが付けた署名」であることを示しています。手元でビルドする分には何もしなくても付きますが、ビルド方法によって付き方が違いました。

試した条件は次のとおりです。同じソース・同じCMakeLists.txtから、2つのジェネレータでビルドした成果物を比べています。

  • Xcode: cmake -B build-xcode -G Xcode + cmake --build build-xcode --config Release → build-xcode/Release/MyCustomPlugin.acroplugin
  • コマンドライン(Makefile): cmake -B build + cmake --build build → build/MyCustomPlugin.acroplugin
  • 確認コマンド: バンドル全体は codesign -v、スライスごとは codesign -dv --verbose=4 --arch arm64(および --arch x86_64)、_CodeSignature/ の有無は ls Contents/
ビルド方法 arm64 x86_64 _CodeSignature/ codesign -v
Xcode 署名あり 署名あり あり 成功(無出力)
コマンドライン(Makefile) リンカが自動で署名(adhoc,linker-signed) 署名なし(code object is not signed at all) なし 失敗(code object is not signed at all)

Apple Siliconのマシンでネイティブに動かすだけならarm64の部分の署名で読み込まれますが、気になる場合はバンドル全体に署名しておきます。--sign - の - がアドホック署名の指定です(man codesign の SIGNING IDENTITIES の節に If identity is the single letter "-" (dash), ad-hoc signing is performed とあります)。

codesign --force --sign - MyCustomPlugin.acroplugin

Acrobat本体の権限(entitlements)を見ると、com.apple.security.cs.disable-library-validation が付いています。これは、同じチームIDで署名されていない(あるいはAppleが署名していない)ライブラリやプラグインの読み込みを許可する設定なので、自分のMacで開発・テストする分にはアドホック署名のままで問題はないと思われます。

$ codesign -d --entitlements - "/Applications/Adobe Acrobat DC/Adobe Acrobat.app"
	[Key] com.apple.security.cs.disable-executable-page-protection
	[Key] com.apple.security.cs.disable-library-validation

ほかのMacに配布する場合は、ダウンロードしたファイルに付く隔離属性(quarantine)の関係で、Developer ID証明書での署名とAppleの公証(notarization)が必要になるはずです(Notarizing macOS software before distribution)。こちらは今回は試していません。


Acrobatに読み込ませる

ビルドした .acroplugin を、Acrobatのプラグインフォルダに置きます。今回は次のフォルダで確認しました。このフォルダへの書き込みには管理者権限が必要です。

/Library/Application Support/Adobe/Acrobat/DC/Plug-ins/

なお、検証環境は「Acrobat Studio 26.002.21931」ですが、アプリ本体とプラグインフォルダのパスは Adobe Acrobat DC / Acrobat/DC/ のままです(製品名が変わってもインストール先のパスは据え置かれています)。

Acrobatを終了してから、古いものを削除してコピーし直し、Acrobatを起動します。

sudo rm -rf を実行する前に対象を確認する

次の手順には、Acrobatのプラグインフォルダに対する sudo rm -rf が含まれます。パスを打ち間違えたり、末尾の MyCustomPlugin.acroplugin を落としたりすると、Acrobatに同梱されているプラグインごと消えます(その場合はAcrobatの再インストールが必要です)。削除の前に、同じパスを ls -d に渡して、消すつもりのバンドルだけが表示されることを確かめてください。

ls -d "/Library/Application Support/Adobe/Acrobat/DC/Plug-ins/MyCustomPlugin.acroplugin"
# Acrobat を終了し、プロセスが残っていないことを確認する(何も表示されなければOK)
osascript -e 'quit app "Adobe Acrobat"'
pgrep -fl "Adobe Acrobat.app/Contents/MacOS"

# 消す対象を確認してから削除する
ls -d "/Library/Application Support/Adobe/Acrobat/DC/Plug-ins/MyCustomPlugin.acroplugin"
sudo rm -rf "/Library/Application Support/Adobe/Acrobat/DC/Plug-ins/MyCustomPlugin.acroplugin"

# 配置する
sudo ditto build-xcode/Release/MyCustomPlugin.acroplugin \
  "/Library/Application Support/Adobe/Acrobat/DC/Plug-ins/MyCustomPlugin.acroplugin"

# Acrobat を起動する
open -a "/Applications/Adobe Acrobat DC/Adobe Acrobat.app"

cp -R ではなく ditto を使っているのは、バンドルの属性や署名をそのまま保ってコピーするためです。ユーザーごとのフォルダ ~/Library/Application Support/Adobe/Acrobat/DC/Plug-ins/ もありますが、こちらに置いたときの読み込みは今回確認していません。

正しく読み込まれれば、メニューに「Acrobat SDK」→「Basic Plugin」が追加されます。

image.png

image.png


トラブルシューティング:読み込まれているのにメニューが出ない

最初にこの手順で配置したとき、メニューに「Acrobat SDK」が出てきませんでした。この時点で、ビルドしたプラグインは次の点を確認済みで、問題なさそうに見えていました。

  • アーキテクチャにarm64が含まれている
  • エントリポイントの _AcroPluginMain がエクスポートされている
  • アドホック署名が有効(codesign -v が成功)
  • 隔離属性は付いていない
  • Acrobat側の「承認済みのプラグインのみを使用」の設定はオフ

Acrobatのプロセスのメモリを vmmap で見ると、プラグインは読み込まれていました。

vmmap $(pgrep -f "Adobe Acrobat.app/Contents/MacOS/AdobeAcrobat") | grep MyCustomPlugin

読み込まれているのに、何も起きていない状態です。

ログを仕込む

どこで止まっているのかを調べるために、ログを仕込むことにしました。macOSでは、os_log を使うとシステムのログに出力でき、ターミナルから log stream で見られます。

まず、ログ出力用のマクロを用意しました(src/PluginLog.h)。メッセージの先頭を MyCustomPlugin: で揃えておくと、後で絞り込みやすくなります。Windowsとソースを共通にしているので、Windowsでは何もしないようにしておきます。

src/PluginLog.h
#if defined(MAC_PLATFORM)
#include <os/log.h>
// 調査中は os_log_error に出す(既定の設定でもディスクに残り、取りこぼしにくい)
#define PLUGIN_LOG(fmt, ...) os_log_error(OS_LOG_DEFAULT, "MyCustomPlugin: " fmt, ##__VA_ARGS__)
// 恒常的に残すログは os_log_info のほうが適切
#define PLUGIN_LOG_INFO(fmt, ...) os_log_info(OS_LOG_DEFAULT, "MyCustomPlugin: " fmt, ##__VA_ARGS__)
#else
#define PLUGIN_LOG(fmt, ...) ((void)0)
#define PLUGIN_LOG_INFO(fmt, ...) ((void)0)
#endif

ログレベルは使い分けてください。Generating log messages from your code の Choose the Appropriate Log Level for Each Message に The system stores all messages in memory initially, and it writes messages with more severe log levels to disk とあり、同節の表では Info レベルが Only when collected with the log tool とされています。つまり os_log_error は既定の設定でもディスクに保存されるので、今回のように「出ているかどうか」が知りたい調査には向いていますが、成功経路まで os_log_error で出し続けるのは行儀が良くありません。恒常的に入れておくログは os_log_info(既定ではメモリ上、log stream では見える)にしておき、調査のときだけ PLUGIN_LOG に差し替えるのがおすすめです。

os_log は、プライバシー保護のため、文字列の引数を既定で <private> と伏せ字にします。文字列の中身を出したいときは、%s ではなく %{public}s と書きます(同ドキュメントの Redact Sensitive User Data from a Log Message の節。it does redact the contents of dynamic strings and complex dynamic objects とあります)。

なお、可変引数が空のときにカンマを消す , ##__VA_ARGS__ はGNU拡張で、clangとMSVCでは使えますが標準ではありません。標準に寄せるなら、C++20の __VA_OPT__ を使って fmt, __VA_OPT__(,) __VA_ARGS__ と書けます(本記事はC++17でビルドしているので、ここではGNU拡張のままにしています)。

ヘッダは src/ に置いて、target_include_directories(MyCustomPlugin PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/src") でインクルードパスに入れています(この行は最終的なCMakeLists.txtにも入っています)。

次に、プラグインが dlopen された時点でログを出すようにしました。__attribute__((constructor)) を付けた関数は、バイナリが読み込まれたときに自動で実行されます。

BasicPluginInit.cpp
#include "PluginLog.h"

#if defined(MAC_PLATFORM)
__attribute__((constructor))
static void PluginLogDlopen(void)
{
    PLUGIN_LOG("dlopen OK");
}
#endif

あわせて、PIHandshake、PluginInit など、BasicPluginInit.cppの関数の入口と戻り値にもログを入れました。戻り値はいったん変数に受けてからログに出し、その変数を返す形にすると、処理を変えずにログだけを足せます。

BasicPluginInit.cpp
ACCB1 ASBool ACCB2 PluginInit(void)
{
	PLUGIN_LOG("PluginInit enter");

	//Register for mode switch notification.
	AVAppRegisterNotification(AcroAppModeSwitchNSEL, gExtensionID,
		(void*)ASCallbackCreateNotification(AcroAppModeSwitch, AcroAppModeSwitchNotification), NULL);

	ASBool result = MyPluginSetmenu();
	PLUGIN_LOG("PluginInit return %d", (int)result);
	return result;
}

ログを見ながらAcrobatを起動する

ログは、Acrobatを起動する前から監視しておきます。ターミナルを2つ使い、片方でログを監視しながら、もう片方で配置と起動を行いました。

# ターミナル1:MyCustomPlugin: を含むログだけを表示し続ける
log stream --predicate 'eventMessage CONTAINS "MyCustomPlugin"' --style compact
# ターミナル2:Acrobat を終了 → 配置 → 起動(「Acrobatに読み込ませる」と同じ手順)
osascript -e 'quit app "Adobe Acrobat"'
# 消す対象を確認してから削除する(打ち間違いで同梱プラグインごと消えるのを防ぐ)
ls -d "/Library/Application Support/Adobe/Acrobat/DC/Plug-ins/MyCustomPlugin.acroplugin"
sudo rm -rf "/Library/Application Support/Adobe/Acrobat/DC/Plug-ins/MyCustomPlugin.acroplugin"
sudo ditto build-xcode/Release/MyCustomPlugin.acroplugin \
  "/Library/Application Support/Adobe/Acrobat/DC/Plug-ins/MyCustomPlugin.acroplugin"
open -a "/Applications/Adobe Acrobat DC/Adobe Acrobat.app"

--predicate の条件にはメッセージ本文が含まれるので、プラグインのログだけでなく、ファイルパスに MyCustomPlugin を含むシステムのログ(署名の確認など)も一緒に表示されます。結果は次のとおりでした(パスは省略し、バンドルIDは置き換えています)。

kernel: AMFI: '.../MyCustomPlugin.acroplugin/Contents/MacOS/MyCustomPlugin' is adhoc signed.
amfid: ... not valid: Error Domain=AppleMobileFileIntegrityError Code=-423 "The file is adhoc signed or signed by an unknown certificate chain"
syspolicyd: GK evaluateScanResult: 2, ... (id: com.example.MyCustomPlugin) ...
AdobeAcrobat: (MyCustomPlugin) MyCustomPlugin: dlopen OK

dlopen OK は出ていますが、その後の PIHandshake のログが1行も出ていません。

AMFIの not valid というメッセージは、署名で弾かれているように見えます。ただ、その後に dlopen OK が出ているので、読み込み自体は成功しています。Acrobatは disable-library-validation を持っているので、これはアドホック署名であることの記録で、読み込みを止めているわけではありません。

SDKの PIMain.c の中を見る

PIHandshake は、SDKの PIMain.c の処理がすべて成功した後に呼ばれます。前回の記事で、Acrobatプラグインはロード時にHFT(関数表)を受け取る仕組みだと書きましたが、その受け取りをしているのが PIMain.c です。macOSでの流れは次のようになっています。

  1. Acrobatが AcroPluginMain を呼ぶ(Windowsの PlugInMain に当たるもの)
  2. AcroPluginMain が、次に呼んでほしい関数として PISetupSDK をAcrobatに返す
  3. Acrobatが PISetupSDK を呼ぶ。ここで必要なHFTを1つずつ取得し、1つでも取れなければ失敗する
  4. 成功すれば、BasicPluginInit.cppの PIHandshake が呼ばれる

HFTの取得に失敗しているのだろうと予想して、PIMain.c にもログを入れることにしました。SDK本体のファイルは変更したくないので、src/ にコピーして、CMakeLists.txtでコピーの方をビルドするように変えます。

cp build/_deps/acrobatsdk-src/Headers/API/PIMain.c src/PIMain.c
    add_library(MyCustomPlugin MODULE
        src/BasicPlugin.cpp
        src/BasicPluginInit.cpp
        src/PIMain.c   # SDK 本体の代わりに、ログを入れたコピーをビルドする
    )

ログは、AcroPluginMain の入口、HFTを取得するたびの結果、false を返すすべての箇所に入れました。たとえば、HFTを取得する GetRequestedHFT には、次のように入れています。

src/PIMain.c(抜粋)
	if (gAcroSupportVersion >= ASCallsHFT_VERSION_6) {
		thft = ASExtensionMgrGetHFT(tablename, requiredVer);
		PLUGIN_LOG("GetRequestedHFT %{public}s: ASExtensionMgrGetHFT(ver=0x%08x) -> %{public}s",
			table, (unsigned)requiredVer, thft ? "OK" : "NULL");
		...
	PLUGIN_LOG("GetRequestedHFT %{public}s: required=0x%08x resultingVer=0x%08x -> %{public}s",
		table, (unsigned)requiredVer, (unsigned)*resultingVer, (*resultingVer != 0) ? "success" : "FAILED");
	return *resultingVer != 0;

PISetupSDK で必須のHFTが取れずに失敗する箇所には、どのHFTだったかを出すようにしました。

src/PIMain.c(抜粋)
		for (i=0;i<sizeof(hftInfo)/sizeof(hftInfo[0]);i++) {
			bSuccess = GetRequestedHFT(hftInfo[i].name,hftInfo[i].ver,hftInfo[i].retver,hftInfo[i].hft);
			if (!bSuccess && !hftInfo[i].optional) {
				PLUGIN_LOG("PISetupSDK return false: required HFT %{public}s (ver=0x%08x) not available",
					hftInfo[i].name, (unsigned)hftInfo[i].ver);
				return false;
			}
		}

ところが、同じ手順でもう一度起動しても、出てくるログは dlopen OK だけでした。AcroPluginMain の入口のログすら出ていません。Acrobatはバンドルを読み込んだものの、エントリポイントを一度も呼んでいなかったということです。

本物のプラグインと見比べる

_AcroPluginMain はエクスポートされているので、Acrobatが探せば見つかるはずです。だとすると、Acrobatが関数を探しに行っていない可能性があります。そこで、Acrobat本体に同梱されているプラグインのInfo.plistを見てみました。

plutil -p "/Applications/Adobe Acrobat DC/Adobe Acrobat.app/Contents/Plugins/Checkers.acroplugin/Contents/Info.plist"
{
  ...
  "CFBundleIdentifier" => "com.adobe.AcrobatPlugin.Checkers"
  ...
  "CFBundlePackageType" => "XTND"
  "CFBundleShortVersionString" => "26.002.21931"
  "CFBundleSignature" => "CARO"
  ...
  "MainEntryPointV2" => "AcroPluginMain"
  ...
}

MainEntryPointV2 というキーに、AcroPluginMain という関数名が入っています。ほかのプラグイン(DropboxStorageなど)も同様で、自分のInfo.plistにはこのキーがありませんでした。

Acrobatは、このキーを見て呼び出すエントリポイントの関数名を決めているようです。キーがないので、バンドルを読み込んだ後、呼ぶべき関数が分からずにそのまま止まっていた、と考えるとログの出方と辻褄が合います。Info.plist.inに次の2行を足して、ビルドし直しました。

MainEntryPointV2 について、アドビの公式ドキュメントでの記載は見つけられませんでしたが、Acrobat SDKにはMac版の中にInfo.plistがPluginSupport/Headers/SDK/Info.plistとして入っており、MainEntryPointV2 のキー定義もありました。

	<key>MainEntryPointV2</key>
	<string>AcroPluginMain</string>

結果

同じ手順で起動したときのログです(一部省略しています)。

MyCustomPlugin: dlopen OK
MyCustomPlugin: AcroPluginMain enter appHandshakeVersion=0x00020000 ...
MyCustomPlugin: AcroPluginMain return TRUE (chosen handshakeVersion=0x00020000, ...)
MyCustomPlugin: PISetupSDK enter handshakeVersion=0x00020000 ...
MyCustomPlugin: GetRequestedHFT Core: required=0x00050000 resultingVer=0x00050000 -> success
MyCustomPlugin: GetRequestedHFT Cos: required=0x00060000 resultingVer=0x00090000 -> success
(中略:PDModel、AcroView、Macintosh、AcroColorHFT など、すべて success)
MyCustomPlugin: PISetupSDK return TRUE
MyCustomPlugin: PIHandshake enter handshakeVersion=0x00020000 ...
MyCustomPlugin: GetExtensionName return atom=7271 name=ADBE:BasicPlugin
MyCustomPlugin: PIHandshake return 1
MyCustomPlugin: PluginInit enter
MyCustomPlugin: PluginMenuItem: AVMenubarAcquireMenuByName(ADBE:Acrobat_SDK) returned NULL, creating menu
MyCustomPlugin: PluginMenuItem return 1
MyCustomPlugin: PluginInit return 1

AcroPluginMain が呼ばれ、必要なHFTもすべて取得でき、PluginInit まで成功しています。「Acrobat SDK」メニューも新しく作られ、そこに「Basic Plugin」が追加されています。

原因が分かってから、仕込んだログはすべて削除し、PIMain.c もSDK本体のものをビルドする形に戻しています(src/PluginLog.h も削除しました)。同じ切り分けをやりたい場合は、本記事に載せた PluginLog.h 全文と、__attribute__((constructor)) のログ、PIMain.c をコピーしてビルド対象を差し替える手順をそのまま使えます。

ログでの切り分けの考え方

「読み込まれているのに動かない」ときは、処理の段階ごとにログを置いておくと、どこまで進んだかで原因の範囲を絞れます。

最後に出たログ 疑う場所
何も出ない 配置場所、アーキテクチャ、署名など、読み込み以前の問題
dlopen OK だけ Acrobatがエントリポイントを呼んでいない(今回はInfo.plist)
PISetupSDK の途中で失敗 必要なHFTが取得できていない(取れなかったHFT名で特定できる)
PIHandshake 以降で失敗 プラグイン側の初期化処理(メニュー作成など)

できあがったCMakeLists.txt

前回のWindows向けの設定に、ここまでのmacOS向けの設定を足すと、プラグイン側のCMakeLists.txtは次のようになります。これは実際に検証に使ったファイルの全文です。元のファイルとの違いは、GIT_REPOSITORY と ACROBAT_PLUGIN_BUNDLE_IDENTIFIER の2箇所を読者が置き換える値(プレースホルダ)にしたことと、コメントを日本語にしたことだけです。Windows側(if(WIN32) と if(MSVC))は前回から変えていません。

バンドルの識別子は、ステップ1では説明を簡単にするため直接書いていましたが、最終形では ACROBAT_PLUGIN_BUNDLE_IDENTIFIER というキャッシュ変数に切り出しています。こうすると、ソースを触らずに -DACROBAT_PLUGIN_BUNDLE_IDENTIFIER=com.yourcompany.MyCustomPlugin で上書きできます。

cmake_minimum_required(VERSION 3.16)

# macOS: ユニバーサルバイナリと最低OSの指定(project() より前に書く)
set(CMAKE_OSX_ARCHITECTURES "arm64;x86_64" CACHE STRING "macOS target architectures")
set(CMAKE_OSX_DEPLOYMENT_TARGET "12.0" CACHE STRING "Minimum macOS version")

project(MyCustomPlugin VERSION 1.0.0 LANGUAGES C CXX)

set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)

# Fetch Acrobat SDK library
include(FetchContent)
FetchContent_Declare(
    AcrobatSDK
    GIT_REPOSITORY https://github.com/<your-account>/acrobat-sdk-cmake
    GIT_TAG <tag or commit hash>
)
FetchContent_MakeAvailable(AcrobatSDK)

if(WIN32)
    add_library(MyCustomPlugin SHARED
        src/BasicPlugin.cpp
        src/BasicPluginInit.cpp
        ${acrobatsdk_SOURCE_DIR}/Headers/API/PIMain.c
    )

    # change the output file suffix for the plugin to .api
    set_target_properties(MyCustomPlugin PROPERTIES
        PREFIX ""
        SUFFIX ".api"
    )
elseif(APPLE)
    set(ACROBAT_PLUGIN_BUNDLE_IDENTIFIER "com.example.MyCustomPlugin" CACHE STRING
        "Reverse-DNS bundle identifier for the Acrobat plug-in"
    )

    # SDKのヘッダは Objective-C++ 前提(WLHFT.h の NSURL* など)
    enable_language(OBJCXX)
    set_source_files_properties(
        src/BasicPlugin.cpp
        src/BasicPluginInit.cpp
        ${acrobatsdk_SOURCE_DIR}/Headers/API/PIMain.c
        PROPERTIES LANGUAGE OBJCXX
    )
    add_library(MyCustomPlugin MODULE
        src/BasicPlugin.cpp
        src/BasicPluginInit.cpp
        ${acrobatsdk_SOURCE_DIR}/Headers/API/PIMain.c
    )

    # Mac ではソースが PIHeaders.h を読み込まないので、プレフィックスヘッダとして渡す
    target_precompile_headers(MyCustomPlugin PRIVATE <Carbon/Carbon.h> <PIHeaders.h>)

    set_target_properties(MyCustomPlugin PROPERTIES
        BUNDLE TRUE
        BUNDLE_EXTENSION "acroplugin"
        OUTPUT_NAME "${PROJECT_NAME}"
        MACOSX_BUNDLE_INFO_PLIST "${CMAKE_CURRENT_SOURCE_DIR}/Info.plist.in"
        MACOSX_BUNDLE_GUI_IDENTIFIER "${ACROBAT_PLUGIN_BUNDLE_IDENTIFIER}"
        MACOSX_BUNDLE_BUNDLE_NAME "${PROJECT_NAME}"
        MACOSX_BUNDLE_INFO_STRING "${PROJECT_NAME} ${PROJECT_VERSION}"
        MACOSX_BUNDLE_LONG_VERSION_STRING "${PROJECT_VERSION}"
        MACOSX_BUNDLE_SHORT_VERSION_STRING "${PROJECT_VERSION}"
        MACOSX_BUNDLE_BUNDLE_VERSION "${PROJECT_VERSION}"
    )

    # MAC_PLATFORM / ACRO_SDK_LEVEL は Acrobat_SDK 側で定義済み
    target_compile_definitions(MyCustomPlugin PRIVATE
        PLATFORM=\"MacPlatform.h\"
        PRODUCT=\"Plugin.h\"
        Platform_Carbon=1
    )
else()
    message(FATAL_ERROR "Unsupported platform: only Windows and macOS are supported")
endif()

# src/ は PluginLog.h などプラグイン側のヘッダ用
target_include_directories(MyCustomPlugin PRIVATE
    "${CMAKE_CURRENT_SOURCE_DIR}/src"
)
target_link_libraries(MyCustomPlugin PRIVATE Acrobat_SDK)

if(MSVC)
    # Sources are UTF-8 (without BOM)
    target_compile_options(MyCustomPlugin PRIVATE /utf-8)
    target_link_options(MyCustomPlugin PRIVATE /EXPORT:PlugInMain)
endif()

if(APPLE)
    find_library(COREFOUNDATION_FRAMEWORK CoreFoundation REQUIRED)
    target_link_libraries(MyCustomPlugin PRIVATE "${COREFOUNDATION_FRAMEWORK}")
endif()

# Acrobat_SDK が配っているインクルードパスの確認用
get_target_property(
    ACRO_INCLUDES
    Acrobat_SDK
    INTERFACE_INCLUDE_DIRECTORIES
)
message(STATUS "AcrobatSDK Includes=${ACRO_INCLUDES}")

末尾の get_target_property は、Acrobat_SDK がどのインクルードパスを配っているかをconfigure時に表示するだけのもので、ビルドには影響しません。前回の記事でSDKパッケージを作ったときの確認用に入れたまま残しています。

ステップ3では説明の都合で find_library(COREFOUNDATION_FRAMEWORK ...) を elseif(APPLE) の中に書きましたが、最終形では末尾の if(APPLE) にまとめています。どちらでも動きます。

if(WIN32) と if(MSVC) を分けているのは、/utf-8 のようなMSVC形式(/ で始まる)のオプションを、ほかのコンパイラに渡さないためです。macOS対応を足してみて、この分け方にしておいてよかったと思いました。if(WIN32) 側に入れていたら、macOSのビルドで clang が /utf-8 をファイルパスと解釈して失敗します。

なお、Windowsでプラグインのエントリポイント PlugInMain をエクスポートするには、.def ファイルや /EXPORT:PlugInMain といった指定が別途必要です。SDKの WinPltfm.h 内では ACCB1 が空に定義されていて、__declspec(dllexport) は付かないためです。Windows向けのエクスポート設定は前回の記事を参照してください。macOS側は、clangが既定で関数をエクスポートするので指定は不要です(nm -gU で _AcroPluginMain がエクスポートされていることを確認できます)。

拡張名の ADBE: は自分のものに変える

BasicPluginのサンプルでは、拡張名やメニュー名に ADBE:BasicPlugin のような ADBE: で始まる名前が使われています。ADBE はAdobeが使っているプレフィックスで、ソースにも「自分のユニークな名前に変えること」という #pragma message が残っています。今回の問題とは関係ありませんでしたが、実際に使うプラグインでは自分の名前に変えておいてください。


付録:SDKパッケージ側のCMakeLists.txt

前回の記事で作ったCMakeパッケージには、再配布できないAcrobat SDKの Headers/ が入っているので、そのリポジトリは非公開にしています。一方 CMakeLists.txt は公開して差し支えないものなので、全文を載せておきます。これと、Adobeから入手したAcrobat SDKの Headers/ があれば、同じパッケージを手元で再現できます。ヘッダを除いた同じ構成は公開リポジトリとしても用意してあるので、そちらも使えます(末尾の「公開リポジトリ」を参照)。

リポジトリの構成

acrobat-sdk-cmake/
├── CMakeLists.txt
└── Headers/           ← Acrobat SDK 配布物からコピー(再配布不可)
    ├── API/
    └── SDK/

Headers/API と Headers/SDK は、Adobe Acrobat SDK の配布物に含まれているものをそのままコピーしてください(配布物内のパスはSDKの版によって違います)。SDKはAdobeのDeveloperサイトから入手しますが、配布ページのURLは過去に何度か変わっているため、ここではリンクを張らず「Acrobat SDK」で検索して現在の配布ページを参照する形にしておきます。AdobeのSDKライセンスの範囲での利用になるので、ヘッダ類を公開リポジトリに置くことはできません。本記事で引用しているヘッダの行番号は、手元のSDK(ACRO_SDK_LEVEL を 0x000A0000 とした版)でのものです。

CMakeLists.txt の全文

acrobat-sdk-cmake/CMakeLists.txt
CMAKE_MINIMUM_REQUIRED(VERSION 3.10)
project(AcrobatSDK CXX)

# Add Interface Target
add_library(Acrobat_SDK INTERFACE)
target_include_directories(Acrobat_SDK INTERFACE include)

# Add Include Path
target_include_directories(Acrobat_SDK INTERFACE
    "${CMAKE_CURRENT_SOURCE_DIR}/Headers/API"
    "${CMAKE_CURRENT_SOURCE_DIR}/Headers/SDK"
)

# Preprocessor Definitions
# MAC_PLATFORM, WIN_PLATFORM
if(WIN32)
    target_compile_definitions(Acrobat_SDK INTERFACE
        WIN_PLATFORM
        _WINDOWS
        WIN_ENV
    )
elseif(APPLE)
    target_compile_definitions(Acrobat_SDK INTERFACE
        MAC_PLATFORM=1
    )
endif()
target_compile_definitions(Acrobat_SDK INTERFACE
    ACRO_SDK_LEVEL=0x000A0000
    PI_ACROCOLOR_VERSION=0x00060000
    PDMETADATA_HFT=1
    PLUGIN=1
)

本記事の内容と対応する部分を補足しておきます。

  • add_library(Acrobat_SDK INTERFACE) — ライブラリの実体はなく、インクルードパスとプリプロセッサ定義を配るだけのターゲットです。前回「target_link_libraries(... Acrobat_SDK) は名前に反して何もリンクしていない」と書いたのはこのためです。macOSでCoreFoundationを別途 find_library + target_link_libraries しているのは、こちらは本当にリンクが必要だからです。
  • elseif(APPLE) の MAC_PLATFORM=1 — ステップ2・ステップ3で出てきた、SDKヘッダの #if MAC_PLATFORM を有効にする定義です。プラグイン側で書く必要がないのはここで定義しているからです。
  • ACRO_SDK_LEVEL=0x000A0000 — SDKバージョン10を指定しています。ステップ3で触れた Headers/SDK/PIHeaders++.pch は 0x00090000(バージョン9)のままなので、.pch をそのまま使わずCMake側で再現したのは、この食い違いを避ける意味もあります。
  • target_include_directories(Acrobat_SDK INTERFACE include)(6行目) — これは効いていません。手元のリポジトリに include/ ディレクトリは存在せず、実際のインクルードパスはその下の Headers/API と Headers/SDK です。初期に書いたまま消し忘れた行で、消しても動作は変わりません(そのままにしてあるので、全文としてはここに残しています)。
  • PI_ACROCOLOR_VERSION / PDMETADATA_HFT / PLUGIN — Windows編で必要になった定義です。macOS側では、これらを変えずにそのまま使えました。

おわりに

macOS対応で一番時間がかかったのは慣れてない環境もあるのですが、ビルドが通った後の「読み込まれているのに動かない」状態でした。原因はInfo.plistのキーが1つ足りないだけで、Xcodeのサンプルプロジェクトを使っていれば、たぶん最初から入っていて気にすることもなかったと思います。前回の記事で書いた秘伝のタレの話は、macOSでも同じでした。

一方で、ログを仕込んで AcroPluginMain → PISetupSDK → PIHandshake と順に追っていったおかげで、プラグインがAcrobatにどういう順番で初期化されていくのかを、実際の動きとして見ることができました。前回、設定をテキストに書き下すと分からない部分を分からないまま残せなくなる、と書きましたが、今回はそれに加えて、実際の動きを1つずつ確かめていけば答えにたどり着ける、という経験になりました。

迷ったときは、Acrobat本体に同梱されているプラグインと見比べてまねをするのが早いですね。

確認していない点

本文中でそのつど書いていますが、まとめておきます。

  • Developer ID証明書での署名とAppleの公証による配布
  • Xcodeのサンプルプロジェクトの中身(プレフィックスヘッダをどう読み込ませているか)

公開リポジトリ

本記事で使ったビルド設定を、筆者が2つの公開リポジトリにまとめました。プロジェクトのテンプレートの参考にしてください。

  • bandetech/acrobat-sdk-cmake — Acrobat SDKのヘッダを Acrobat_SDK ターゲットとして配るCMakeパッケージ
  • bandetech/acrobat-sdk-sample — そのターゲットを使い、Windowsの .api とmacOSの .acroplugin を1つの CMakeLists.txt からビルドするサンプル

どちらにも Acrobat SDKのファイルは入っていません。 SDKのヘッダ(Headers/API、Headers/SDK)と、BasicPluginのソース(src/BasicPlugin.cpp、src/BasicPluginInit.cpp)はAdobeの配布物で再配布できないため、置き場所を空のディレクトリとして用意してあるだけです。各自がAdobeからSDKを入手して配置してください(手順は各リポジトリのREADMEにあります)。.gitignore で該当パスを無視しているので、配置したファイルが誤ってコミットされることもありません。

本記事に載せたCMakeLists.txtとは、2点だけ違います。

  • SDKパッケージの取り込み方 — 本記事は FetchContent ですが、公開リポジトリは ACROBAT_SDK_DIR というキャッシュ変数でローカルのクローンを指す形にしています。公開版の acrobat-sdk-cmake はヘッダを含まないので、FetchContent で取得しても Headers/ が空のままで、PIHeaders.h が見つからずビルドできないからです。configureは cmake -B build -DACROBAT_SDK_DIR=/path/to/acrobat-sdk-cmake の形になります。未指定のときやヘッダが未配置のときは、何をすればよいかを表示して止まるようにしてあります。
  • Windowsのエクスポート指定 — 公開リポジトリには target_link_options(MyCustomPlugin PRIVATE /EXPORT:PlugInMain) が入っています。SDKの WinPltfm.h では ACCB1 が空に定義されていて __declspec(dllexport) が付かないため、Windowsではエントリポイントのエクスポート指定が別途必要です。

なお、公開リポジトリのやり方を本記事のCMakeLists.txtに持ち込む場合は、PIMain.c のパスに使っている ${acrobatsdk_SOURCE_DIR} を3箇所とも書き換えてください。この変数は FetchContent が設定するもので、add_subdirectory では定義されず、空に展開されて Cannot find source file で止まります。

リファレンス

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?