- 一、相关文件
- 二、自动绑定
- #
- #
- clang 标签,其中可以添加预编译宏。
- 下面headers文件的搜索路径
- extra arguments for clang
- 要生成绑定代码的类的头文件,空格隔开。
- 绑定代码需要包含但是不需要被绑定工具扫描的头文件列表。
- 需要生成绑定代码的类,可以使用正则表达式,比如Menu,在脚本内部将会变成这样”^Menu$”
- 需要在脚本层被继承的类列表,以空格分隔。
- 指出不绑定的函数
- 格式:ClassName::[func1 func2]
- 类名可以是正则表达式,比如Menu,在脚本内部将会变成这样”^Menu$”
- 函数名可以是正则表达式,没有上面的^$情况
- ClassName::[*],跳过这个类的所有函数
- *::[update],跳过所有类的update函数。
- 需要被重命名的函数,会将 C++ 中的函数绑定为指定名字的脚本函数,
- 格式为 ClassName::[cppFunctionName=scriptFunctionName …],不同的类以逗号分隔。
- 需要被重命名的类,会将 C++ 中的类名绑定为指定的脚本类名,
- 格式为 CppClassName::ScriptClassName,以逗号分割。
- for all class names, should we remove something when registering in the target VM?
- classes for which there will be no “parent” lookup
- 没有父类的类列表,以空格分隔。
- base classes which will be skipped when their sub-classes found them.
- classes that create no constructor
- Set is special and we will use a hand-written constructor
- 没有构造函数的类列表,以空格分隔。
- Determining whether to use script object(js object) to control the lifecycle
- of native(cpp) object or the other way around.
- #
- 4、注册绑定
- 三、手动绑定
感谢
- https://zhuanlan.zhihu.com/p/20525109
- https://www.cnblogs.com/chevin/p/5954948.html
- https://blog.csdn.net/qq_25563175/article/details/112943639
- https://zilongshanren.com/post/embed-lua-tutorials-one/
- https://zilongshanren.com/post/pass-lua-to-cpp-and-vise-verse/
- https://zilongshanren.com/post/call-lua-table-from-cpp/
- https://zilongshanren.com/post/lua-call-cpp-functions/
- https://zilongshanren.com/post/cpp-operate-lua-string/
- https://zilongshanren.com/post/bind-a-simple-cpp-class-in-lua/
Lua绑定,指的是C++类暴露给Lua,在Lua中直接可使用。这需要借助脚本工具生成绑定代码,并注册。
一、相关文件
下面是Lua binding时需要用到的相关文件。
# 当前目录是cocos2d-x引擎目录
cocos/
scripting/ // 引擎的脚本部分:lua/js代码、lua/js的binding代码(C++)
js-bindings/ // js的C++绑定代码
lua-bindings/ // lua的C++绑定代码、预置的lua代码
auto/ // 这个是根据下面tolua/*.ini的预置配置,生成的cocos引擎部分的绑定代码
lua_cocos2dx_auto.hpp
lua_cocos2dx_auto.cpp
manual/ // 这个是手写的绑定代码,不是通过ini来配置了,而是从下面的C++代码中修改了。
lua_module_register.h // 手动配置,需要加载的绑定代码
lua_module_register.cpp
...
proj.***/ // 这些绑定代码在不同平台的编译工程
script/ // 这个是引擎预置的lua代码
tools/ // 预生成工具
bindings-generator/ // js/lua绑定的自动化工具
generator.py // 执行这个脚本,将生成js、lua的C++绑定代码。
README.md // 说明文件,描述该工具的Running Requirments、Usage。
test/ // 这个是简单示范例子,生成的是js绑定代码
tolua/ // 用于生成cocos2dx项目的lua绑定代码的工具,实质是调用上面bindings-generator
***.ini // 这些是预置的配置文件,用于生成cocos引擎部分的的lua绑定代码。
genbindings.py // 读取上面的配置文件,自动生成lua绑定代码。
README.md // bindings-generator的Running Requirments。
通过脚本bindings-generator自动生成绑定代码的大致过程如下:
- 1、配置好bindings-generator的Running Requirements。
- 2、编写ini配置文件
- 告诉脚本,需要生成哪些类的绑定代码。可以参考预置的ini来写。
- 3、配置并执行genbindings.py
- 设置ini配置的读取路径、绑定代码的输出路径。
- 实际上就是调用bindings-generators脚本,传入的参数就是ini中设置的。
- 4、注册绑定
- 在绑定代码的hpp文件中都有一个registerall*方法,在合适的地方调用,即可正式生效。
5、平台编译
Windows
cocos2d-x 3.17.2
1、安装Android NDK
- android-ndk-r16 or later
- 2、安装python
- python2.7.3 (32bit):http://www.python.org/ftp/python/2.7.3/python-2.7.3.msi
- 将Python根目录添加到环境变量PATH
- 3、安装pyyaml
- 4、安装Cheetah
- 5、设置环境变量NDK_ROOT、PYTHON_BIN
2、编写ini配置
```
#
cocos2dx_spine : 就是下面genbindings.py里的args[0]
#
[cocos2dx_spine]
#
prefix : 生成的绑定代码的文件名为:lua_prefix_auto.cpp
#
prefix = cocos2dx_spine
#
比如绑定了类Fuck,Bitch,Shit,在lua中就这么访问:sp.Fuck、sp.Bitch、sp.Shit
也可以为空,那就不需要前缀了。
#
1、C++中必须对应也定义在一个namespace中,如果没有,则报错conversion wasn’t set
2、需要在文件conversions.yaml的ns_map中配置好C++的namespace到Lua的namspace的映射。
文件路径:bindings-generator\targets\lua\
#
#
target_namespace = sp
androidheaders = androidflags = -target armv7-none-linux-androideabi -D_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS -DANDROID -D__ANDROID_API=14 -gcc-toolchain %(gcc_toolchain_dir)s —sysroot=%(androidndkdir)s/platforms/android-14/arch-arm -idirafter %(androidndkdir)s/sources/android/support/include -idirafter %(androidndkdir)s/sysroot/usr/include -idirafter %(androidndkdir)s/sysroot/usr/include/arm-linux-androideabi -idirafter %(clangllvmdir)s/lib64/clang/5.0/include -I%(androidndkdir)s/sources/cxx-stl/llvm-libc++/include clang_headers =
#
clang 标签,其中可以添加预编译宏。
#
clangflags = -nostdinc -x c++ -std=c++11 -fsigned-char -U_SSE
#
下面headers文件的搜索路径
#
cocos_headers = -I%(cocosdir)s/cocos -I%(cocosdir)s/cocos/editor-support -I%(cocosdir)s/cocos/platform/android -I%(cocosdir)s/external cocos_flags = -DANDROID cxxgenerator_headers =
#
extra arguments for clang
#
extra_arguments = %(android_headers)s %(clang_headers)s %(cxxgenerator_headers)s %(cocos_headers)s %(android_flags)s %(clang_flags)s %(cocos_flags)s %(extra_flags)s
#
要生成绑定代码的类的头文件,空格隔开。
#
headers = %(cocosdir)s/cocos/editor-support/spine/spine-cocos2dx.h
#
绑定代码需要包含但是不需要被绑定工具扫描的头文件列表。
#
cpp_headers =
#
需要生成绑定代码的类,可以使用正则表达式,比如Menu,在脚本内部将会变成这样”^Menu$”
#
classes = SkeletonRenderer SkeletonAnimation
#
需要在脚本层被继承的类列表,以空格分隔。
#
classes_need_extend =
#
指出不绑定的函数
格式:ClassName::[func1 func2]
类名可以是正则表达式,比如Menu,在脚本内部将会变成这样”^Menu$”
函数名可以是正则表达式,没有上面的^$情况
ClassName::[*],跳过这个类的所有函数
*::[update],跳过所有类的update函数。
#
skip = SkeletonRenderer::[findBone findSlot getAttachment setAttachment update draw createWithData], *::[update draw drawSkeleton], SkeletonAnimation::[setAnimationStateData createWithData addAnimation getCurrent setAnimation onAnimationStateEvent onTrackEntryEvent getState createWithFile]
#
需要被重命名的函数,会将 C++ 中的函数绑定为指定名字的脚本函数,
格式为 ClassName::[cppFunctionName=scriptFunctionName …],不同的类以逗号分隔。
#
rename_functions =
#
需要被重命名的类,会将 C++ 中的类名绑定为指定的脚本类名,
格式为 CppClassName::ScriptClassName,以逗号分割。
#
rename_classes =
#
for all class names, should we remove something when registering in the target VM?
#
remove_prefix =
#
classes for which there will be no “parent” lookup
没有父类的类列表,以空格分隔。
#
classes_have_no_parents =
#
base classes which will be skipped when their sub-classes found them.
#
base_classes_to_skip =
#
classes that create no constructor
Set is special and we will use a hand-written constructor
没有构造函数的类列表,以空格分隔。
#
abstract_classes = Skeleton SkeletonAnimation
#
Determining whether to use script object(js object) to control the lifecycle
of native(cpp) object or the other way around.
#
Supported values are ‘yes’ or ‘no’.
#
script_control_cpp = no
<a name="ZrSgV"></a>
## 3、配置genbindings.py
genbindings.py是对bindings-generator的再包装,用于生成cocos2d-x项目的lua绑定代码。
```python
#!/usr/bin/python
# This script is used to generate luabinding glue codes.
# Android ndk version must be ndk-r9b.
......;
def main():
......
#####################################################################################################
#####
##### 在以下目录寻找llvm toolchain
#####
##### llvm_toolchain_path = ndk_root + '/toolchains/llvm/prebuilt/' + platform + "-x86_64"
##### llvm_toolchain_path = ndk_root + '/toolchains/llvm/prebuilt/' + platform
##### llvm_toolchain_path = ndk_root + '/toolchains/llvm/prebuilt/' + platform + "-x86"
#####
##### platform = windows/linux/darwin
#####
#####################################################################################################
x86_llvm_path = ""
x64_llvm_path = os.path.abspath(os.path.join(ndk_root, 'toolchains/llvm/prebuilt', '%s-%s' % (cur_platform, 'x86_64')))
if not os.path.exists(x64_llvm_path):
x86_llvm_path = os.path.abspath(os.path.join(ndk_root, 'toolchains/llvm/prebuilt', '%s' % (cur_platform)))
if not os.path.exists(x86_llvm_path):
x86_llvm_path = os.path.abspath(os.path.join(ndk_root, 'toolchains/llvm/prebuilt', '%s-%s' % (cur_platform, 'x86')))
if os.path.isdir(x64_llvm_path):
llvm_path = x64_llvm_path
elif os.path.isdir(x86_llvm_path):
llvm_path = x86_llvm_path
else:
print 'llvm toolchain not found!'
print 'path: %s or path: %s are not valid! ' % (x86_llvm_path, x64_llvm_path)
sys.exit(1)
#####################################################################################################
#####
##### 在以下目录寻找gcc toolchain
#####
##### gcc_toolchain_path = ndk_root + '/toolchains/arm-linux-androideabi-4.9/prebuilt' + platform + "-x86_64"
##### gcc_toolchain_path = ndk_root + '/toolchains/arm-linux-androideabi-4.9/prebuilt' + platform
##### gcc_toolchain_path = ndk_root + '/toolchains/arm-linux-androideabi-4.9/prebuilt' + platform + "-x86"
#####
#####################################################################################################
x86_gcc_toolchain_path = ""
x64_gcc_toolchain_path = os.path.abspath(os.path.join(ndk_root, 'toolchains/arm-linux-androideabi-4.9/prebuilt', '%s-%s' % (cur_platform, 'x86_64')))
if not os.path.exists(x64_gcc_toolchain_path):
x86_gcc_toolchain_path = os.path.abspath(os.path.join(ndk_root, 'toolchains/arm-linux-androideabi-4.9/prebuilt', '%s' % (cur_platform)))
if not os.path.exists(x86_gcc_toolchain_path):
x86_gcc_toolchain_path = os.path.abspath(os.path.join(ndk_root, 'toolchains/arm-linux-androideabi-4.9/prebuilt', '%s-%s' % (cur_platform, 'x86')))
if os.path.isdir(x64_gcc_toolchain_path):
gcc_toolchain_path = x64_gcc_toolchain_path
elif os.path.isdir(x86_gcc_toolchain_path):
gcc_toolchain_path = x86_gcc_toolchain_path
else:
print 'gcc toolchain not found!'
print 'path: %s or path: %s are not valid! ' % (x64_gcc_toolchain_path, x86_gcc_toolchain_path)
sys.exit(1)
#####################################################################################################
#####
##### 设置项目目录
##### project_root = cocos_root = cocos引擎目录
#####
##### 设置bindings-generator的目录
##### cxx_generator_root = project_root + '/tools/bindings-generator'
#####
#####################################################################################################
project_root = os.path.abspath(os.path.join(os.path.dirname(__file__), '..', '..'))
cocos_root = os.path.abspath(os.path.join(project_root, ''))
cxx_generator_root = os.path.abspath(os.path.join(project_root, 'tools/bindings-generator'))
#####################################################################################################
#####
##### 这些目录会保存在和当前文件相同目录的userconf.ini中。对应关系如下:
##### androidndkdir : ndk_root
##### clangllvmdir : llvm_toolchain_path
##### gcc_toolchain_dir : gcc_toolchain_path
##### cocosdir : cocos_root
##### cxxgeneratordir : bindings-generator
#####
#####################################################################################################
config = ConfigParser.ConfigParser()
config.set('DEFAULT', 'androidndkdir', ndk_root)
config.set('DEFAULT', 'clangllvmdir', llvm_path)
config.set('DEFAULT', 'gcc_toolchain_dir', gcc_toolchain_path)
config.set('DEFAULT', 'cocosdir', cocos_root)
config.set('DEFAULT', 'cxxgeneratordir', cxx_generator_root)
config.set('DEFAULT', 'extra_flags', '')
conf_ini_file = os.path.abspath(os.path.join(os.path.dirname(__file__), 'userconf.ini'))
print 'generating userconf.ini...'
with open(conf_ini_file, 'w') as configfile:
config.write(configfile)
#####################################################################################################
#####
##### 将bindings-generator目录下的\libclang、\tools\win32添加到 PATH 环境变量中。
#####
#####################################################################################################
if 'linux' in platform or platform == 'darwin':
os.putenv('LD_LIBRARY_PATH', '%s/libclang' % cxx_generator_root)
if platform == 'win32':
path_env = os.environ['PATH']
os.putenv('PATH', r'%s;%s\libclang;%s\tools\win32;' % (path_env, cxx_generator_root, cxx_generator_root))
try:
#####################################################################################################
#####
##### 下面这些ini配置文件所在的目录:tolua_root = project_root + "/tools/tolua/"
##### 生成的C++绑定代码的输出目录,output_dir = project_root + "/cocos/scripting/lua-bindings/auto/"
#####
#####################################################################################################
tolua_root = '%s/tools/tolua' % project_root
output_dir = '%s/cocos/scripting/lua-bindings/auto' % project_root
#####################################################################################################
#####
##### key : 'cocos2dx.ini' : tolua_root目录下的配置文件名
##### args[0] : 'cocos2d-x' : 指定需要生成绑定代码的section,见ini配置文件中的解释
##### args[1] : 'lua_cocos2dx_auto' : 输出的绑定代码的文件名
#####
##### 成功,输出日志:Generating lua bindings succeeds.
##### 失败,输出日志:Generating lua bindings fails.
#####
#####################################################################################################
cmd_args = {'cocos2dx.ini' : ('cocos2d-x', 'lua_cocos2dx_auto'), \
'cocos2dx_extension.ini' : ('cocos2dx_extension', 'lua_cocos2dx_extension_auto'), \
'cocos2dx_ui.ini' : ('cocos2dx_ui', 'lua_cocos2dx_ui_auto'), \
'cocos2dx_studio.ini' : ('cocos2dx_studio', 'lua_cocos2dx_studio_auto'), \
'cocos2dx_spine.ini' : ('cocos2dx_spine', 'lua_cocos2dx_spine_auto'), \
'cocos2dx_physics.ini' : ('cocos2dx_physics', 'lua_cocos2dx_physics_auto'), \
'cocos2dx_experimental_video.ini' : ('cocos2dx_experimental_video', 'lua_cocos2dx_experimental_video_auto'), \
'cocos2dx_experimental.ini' : ('cocos2dx_experimental', 'lua_cocos2dx_experimental_auto'), \
'cocos2dx_controller.ini' : ('cocos2dx_controller', 'lua_cocos2dx_controller_auto'), \
'cocos2dx_cocosbuilder.ini': ('cocos2dx_cocosbuilder', 'lua_cocos2dx_cocosbuilder_auto'), \
'cocos2dx_cocosdenshion.ini': ('cocos2dx_cocosdenshion', 'lua_cocos2dx_cocosdenshion_auto'), \
'cocos2dx_3d.ini': ('cocos2dx_3d', 'lua_cocos2dx_3d_auto'), \
'cocos2dx_audioengine.ini': ('cocos2dx_audioengine', 'lua_cocos2dx_audioengine_auto'), \
'cocos2dx_csloader.ini' : ('cocos2dx_csloader', 'lua_cocos2dx_csloader_auto'), \
'cocos2dx_experimental_webview.ini' : ('cocos2dx_experimental_webview', 'lua_cocos2dx_experimental_webview_auto'), \
'cocos2dx_physics3d.ini' : ('cocos2dx_physics3d', 'lua_cocos2dx_physics3d_auto'), \
'cocos2dx_navmesh.ini' : ('cocos2dx_navmesh', 'lua_cocos2dx_navmesh_auto'), \
}
target = 'lua'
generator_py = '%s/generator.py' % cxx_generator_root
for key in cmd_args.keys():
args = cmd_args[key]
cfg = '%s/%s' % (tolua_root, key)
print 'Generating bindings for %s...' % (key[:-4])
command = '%s %s %s -s %s -t %s -o %s -n %s' % (python_bin, generator_py, cfg, args[0], target, output_dir, args[1])
_run_cmd(command)
print '---------------------------------'
print 'Generating lua bindings succeeds.'
print '---------------------------------'
except Exception as e:
if e.__class__.__name__ == 'CmdError':
print '---------------------------------'
print 'Generating lua bindings fails.'
print '---------------------------------'
sys.exit(1)
else:
raise
# -------------- main --------------
if __name__ == '__main__':
main()
4、注册绑定
// 比如生成的绑定代码文件如下:
// lua_MotherFucker_auto.hpp
// lua_MotherFucker_auto.cpp
# include "../lua_MotherFucker_auto.hpp"
register_all_MotherFucker(_state);
auto engine = LuaEngine::getInstance();
ScriptEngineManager::getInstance()->setScriptEngine( engine );
lua_State* L = engine->getLuaStack()->getLuaState();
register_all_MotherFucker( L ); // 加载绑定代码。跟引擎部分的注册放一块也可以,见CCLuaStack
// LuaStack* stack = engine->getLuaStack();
// stack->setXXTEAKeyAndSign( "2dxLua", strlen( "2dxLua" ), "XXTEA", strlen( "XXTEA" ) );
三、手动绑定
虽然 Bindings Generator 已经非常强大,但是它依然有其局限性。目前的 Bindings Generator 的局限性主要是以下的几点:
- 只能够针对类生成绑定,不可以绑定结构体,独立函数等
- 不能够生成 Delegate 类型的 API,因为脚本中的对象是无法继承 C++ 中的 Delegate 类并重写其中的 Delegate 函数的。
- 子类中重写了父类的 API 的同时,又重载了这个 API。
- 部分 API 实现内容并没有完全体现在其 API 定义中。
- 在运行时由 C++ 主动调用的 API。
前三种情况,可以手动编写绑定代码完成。
手写绑定教程见Lua知识部分。
Lua_C API(C与Lua的交互)