最近需要给 Adobe After Effects 开发一个插件,用于导出矢量图形到二进制文件。这篇文章不会涉及具体的插件开发细节,详细的文档可以直接参考 Adobe 官方的插件开发页面:http://www.adobe.com/devnet/aftereffects.html, 啃完 SDK 里的那两份 Guide 文档基本就没什么问题了。总体来说,有两种方式来开 AE 的插件:C++ 或者 ExtendScript 脚本。后者明显更容易一些,但是有一些限制,比如无法很方便的写入二进制文件,操作字节流会比较麻烦。由于我的目标是生成二进制文件,所以选择了 C++ 开发方式,并且还涉及到要运行压缩算法,C++处理上会比较有速度优势。那么问题来了,AE SDK 里的 Examples 在 Mac 平台上默认采用的是 XCode 作为开发环境[手动嫌弃], XCode 除了打包或 Profile 时候必须用一下,平时它的文本编辑器简直不能忍。所以这篇文章的内容主要讲一下如何把 AE SDK 里的 XCode 项目转换为 CLion 项目。
这里以 Examples 里的 Grabba 工程为例,文章末尾会附上转换好的完整项目压缩包。因为 CLion 用的是 CMake 编译系统,转换的过程其实就是把 XCode 项目打开,看一遍引用了哪些文件,以及需要的额外编译参数。用 CMakeLists.txt 再写一遍, 然后用 CLion 打开包含 CMakeLists.txt 的文件集即可。这是最终的 CMakeLists.txt:
cmake_minimum_required(VERSION 3.5)
project(AEM)
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_FLAGS "-stdlib=libc++ -arch x86_64")
set(CMAKE_OSX_DEPLOYMENT_TARGET 10.9)
file(GLOB_RECURSE SOURCE_FILES src/*.cpp src/*.cc src/*.c src/*.h)
list(APPEND SOURCE_FILES sdk/Util/AEGP_SuiteHandler.cpp sdk/Util/AEGP_SuiteHandler.h sdk/Util/MissingSuiteError.cpp)
include_directories(sdk/Headers sdk/Headers/SP sdk/Resources sdk/Util src)
set_source_files_properties(${SOURCE_FILES} PROPERTIES COMPILE_FLAGS "${CMAKE_CXX_FLAGS} -x objective-c++ -Wno-nonportable-include-path -include /System/Library/Frameworks/Cocoa.framework/Headers/Cocoa.h")
find_library(COCOA Cocoa REQUIRED)
list(APPEND libs ${COCOA})
add_library(AEM MODULE ${SOURCE_FILES})
set_target_properties(AEM PROPERTIES
CXX_VISIBILITY_PRESET hidden
VISIBILITY_INLINES_HIDDEN true)
target_link_libraries(AEM ${libs})
这里大部分内容相信熟悉 CMake 的人或者参考官方文档都能轻松写出来。就是简单的查找源文件,然后设置头文件的目录。但其中有两个坑点:
第一个是中间那句 set_source_files_properties
的内容。一开始我并没有加这句,然后到处都在提示类似这样的报错信息:
sdk/Headers/AE_Effect.h:1139:11: error: unknown type name 'Fixed'
typedef Fixed PF_Fixed;
说找不到 Fixed 这个类型,我查了一下这个类型是定义在 /usr/include/MacTypes.h
文件里,但是 SDK 里的代码并没有任何地方直接 #include
过这个头文件,那 XCode 是怎么识别出这个类型的呢?经过一番搜索后发现里编译器有个 -include
的参数,可以直接强制预先 include 一个头文件进来。我就试了下加了句:
set_source_files_properties(${SOURCE_FILES} PROPERTIES COMPILE_FLAGS "${CMAKE_CXX_FLAGS} -include /usr/include/MacTypes.h")
果然是不再报错找不到 Fixed
的错误了,但是还有其他的类型找不到,说明不只这个头文件需要关联。但这么一个个找也不是办法啊。最后想起来干脆直接查看 XCode 的编译日志好了。不管工程是 XCode 还是 CMake,他们最终都是调用系统的 clang 去编译的,所以最终都会执行调用 clang 的语句,从那个语句里找缺少的参数,然后再回头添加到 CMake 里就好。这里有个详细示例如何在 XCode 里查看详细编译日志的:查看XCode编译日志。最后发现了它强制预先 include 的头文件是这个: /System/Library/Frameworks/Cocoa.framework/Headers/Cocoa.h
。 试了下所有类型报错都消失了,但又提示新的报错了:
/System/Library/Frameworks/Foundation.framework/Headers/NSObjCRuntime.h:492:1: error: expected unqualified-id
@class NSString, Protocol;
^
看意思似乎是编译器不认识这个 @
符号,这是 Objective-C++ 的语法,回头再去看看 XCode 的编译日志,找出了这句:-x objective-c++
, 查了下用于指定这些文件编译的时候被当做 Objective-C++ 文件用的。加上这句之后终于完美编译通过了。
但是最终运行插件的时候 AE 提示无法加载插件,这就是第二个坑点了,我之前 add_library()
里声明的类型是 SHARED,查看了一下 CMake 的编译日志(在它的缓存目录里有个 link.txt 文件),里面调用 clang 的时候用的是 -dynamiclib
, 而 XCode 这里调用参数是 -boundle
。查了下两者区别,前者是共享库,可以被编译器用来链接到别的可执行文件的,后者是专门用来被运行时加载的。其实 Window 平台并不区分这两者,都是 DLL,但是 Mac 平台上这两者是不一样的东西。AE 的插件需要的是后者。这两个参数在 CMake 里分别对应 SHARED 和 MODULE, 所以改成 MODULE 就好了。
一顿折腾下来,最后的总结: 下次再遇到类似的问题,都直接看编译日志就行,不管什么编译系统,最终调用的语句都是类似的,比较最终的编译参数再回头去找各自编译系统里对应的设置方法即可,这是最直接有效的方式。
当然,CMake 编译出来的只有一个模块文件,而 AE 插件在 Mac 上是需要一个 boundle 的壳的,就是一个 *.plugin 的文件夹,里面包含最终编译好的模块文件,plist描述文件,还有一些相关资源。这个直接用对应的 XCode 项目生成一个模板就行了,然后写个脚本每次编译结束自动替换模板里的模块文件。这些都做完了,就可以愉快地用 CLion 进行开发调试了。 :)
最后附上完整的工程:aem-plugin.zip