• Unity3D跨平台动态库编译---记kcp基于CMake的各平台构建实践


    一  为什么需要动态库

      1)提供原生代码(native code)的支持,也叫原生插件,但是我实践的是c/cpp跨平台动态库,这里不具体涉及安卓平台java库和ios平台的objectc库构建。

      2)某些开源库是c/cpp编写,没有对应c#版本

      3)或者有c#版本实现,但是效率或者gc达不到期望效果,特别是GC,一般的开源库c#版本的作者,对gc优化得大多不好

      4)追求效率,比如A*寻路等开销比较大的算法,想做下优化

      5)某些模块,如网络模块,需要Unity客户端和后端跑一份相同的逻辑代码,而不想维护两份语言的实现

    二  示例demo选择

      前阵子由于项目需要,编译了一下kcp库,这里拿kcp编译到Unity的各平台动态库来做一次总结,kcp的github地址:https://github.com/skywind3000/kcp

      有关这个库的其它信息这里不做介绍,它本质就是一个可靠UDP的网络库,我们项目是用在了一款多人实时射击类游戏中。

    三  编译工具选择

      这里使用的是CMake,官网地址为:https://cmake.org/。有关CMake的使用资料自行网络获取,我这里只说一点,这是一个跨平台的构建工具,针对不同的平台,只需要一份描述文件,很方便,不需要每个平台去写makefile。

    四  示例demo选择

      

      其中:

        1)cmke目录:包含CMake在安卓、iOS平台进行构建时需要要到的两个文件:android.toolchain.cmake、iOS.cmake

        2)Plugins目录:各平台构建的输出目录,构建完成后可以直接放置到Unity项目Assdets目录下使用

        3)CMakeLists.txt文件:主要要由我们自己编写的一个文件,cmake根据CMakeLists生成各个平台编译的中间文件以及makefile文件

        4)目标库源代码:kcp.h、kcp.c

        5)make_xxx:各平台执行构建脚本文件,这些文件基本是固定的,不需要做什么修改

    五  项目简要说明

      1、kcp项目需要做的调整

        kcp.c不需要动,kcp.h修改部分如下:

     1 #ifdef DLL_EXPORTS
     2 #define KCPDLL _declspec(dllexport)
     3 #else
     4 #define KCPDLL
     5 #endif
     6 
     7 #ifdef __cplusplus
     8 extern "C" {
     9 #endif
    10 
    11     //---------------------------------------------------------------------
    12     // interface
    13     //---------------------------------------------------------------------
    14 
    15     // create a new kcp control object, 'conv' must equal in two endpoint
    16     // from the same connection. 'user' will be passed to the output callback
    17     // output callback can be setup like this: 'kcp->output = my_udp_output'
    18     KCPDLL ikcpcb* ikcp_create(IUINT32 conv, void *user);
    19     //其它导出接口省略
    20 
    21 #ifdef __cplusplus
    22 }
    23 #endif

      2、CMakeLists文件编写

     1 cmake_minimum_required(VERSION 2.8)
     2 
     3 if ( WIN32 AND NOT CYGWIN AND NOT ( CMAKE_SYSTEM_NAME STREQUAL "WindowsStore" ) )
     4     set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} /MT" CACHE STRING "")
     5     set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} /MTd" CACHE STRING "")
     6     set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} /MT" CACHE STRING "")
     7     set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} /MTd" CACHE STRING "")
     8 endif ()
     9 
    10 project(kcp)
    11 
    12 if ( IOS )
    13         set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fembed-bitcode")
    14         set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fembed-bitcode")
    15 endif ()
    16 
    17 find_path(KCP_PROJECT_DIR NAMES SConstruct
    18     PATHS 
    19     ${CMAKE_SOURCE_DIR}
    20     NO_DEFAULT_PATH
    21     )
    22 
    23 MARK_AS_ADVANCED(KCP_PROJECT_DIR)
    24 
    25 set ( KCP_CORE
    26     kcp.c
    27 )
    28 
    29 macro(source_group_by_dir proj_dir source_files)
    30     if(MSVC OR APPLE)
    31         get_filename_component(sgbd_cur_dir ${proj_dir} ABSOLUTE)
    32         foreach(sgbd_file ${${source_files}})
    33             get_filename_component(sgbd_abs_file ${sgbd_file} ABSOLUTE)
    34             file(RELATIVE_PATH sgbd_fpath ${sgbd_cur_dir} ${sgbd_abs_file})
    35             string(REGEX REPLACE "(.*)/.*" \1 sgbd_group_name ${sgbd_fpath})
    36             string(COMPARE EQUAL ${sgbd_fpath} ${sgbd_group_name} sgbd_nogroup)
    37             string(REPLACE "/" "\" sgbd_group_name ${sgbd_group_name})
    38             if(sgbd_nogroup)
    39                 set(sgbd_group_name "\")
    40             endif(sgbd_nogroup)
    41             source_group(${sgbd_group_name} FILES ${sgbd_file})
    42         endforeach(sgbd_file)
    43     endif(MSVC OR APPLE)
    44 endmacro(source_group_by_dir)
    45 
    46 source_group_by_dir(${CMAKE_CURRENT_SOURCE_DIR} KCP_CORE)
    47 
    48 if (APPLE)
    49     if (IOS)
    50         set(CMAKE_OSX_ARCHITECTURES "$(ARCHS_STANDARD)")
    51         add_library(kcp STATIC
    52            ${KCP_CORE}
    53         )
    54     else ()
    55         set(CMAKE_OSX_ARCHITECTURES "$(ARCHS_STANDARD_32_64_BIT)")
    56         add_library(kcp MODULE
    57             ${KCP_CORE}
    58         )
    59         set_target_properties ( kcp PROPERTIES BUNDLE TRUE )
    60         #set_target_properties ( kcp PROPERTIES FRAMEWORK TRUE )
    61         #set_target_properties ( kcp PROPERTIES MACOSX_RPATH TRUE )
    62     endif ()
    63 else ( )
    64     add_library(kcp SHARED
    65         ${KCP_CORE}
    66     )
    67 endif ( )
    68 
    69 if ( WIN32 AND NOT CYGWIN )
    70     target_compile_definitions (kcp PRIVATE DLL_EXPORTS)
    71 endif ( )

        1)第10行:指定项目名称为kcp

        2)第26行:指定要编译的c/cpp文件

        3)第70行:指定预定义宏DLL_EXPORTS,这个宏只有在window平台编译dll动态库才需要,其它平台不需要

        4)其它没什么好说的,在构建你自己的项目时,只需要注意的带有“kcp”字样的地方替换为你自己对应的项目名称即可。

      3、各平台构建脚本

        1)window32位系统:make_win32.bat

    1 mkdir build32 & pushd build32
    2 cmake -G "Visual Studio 14 2015" ..
    3 popd
    4 cmake --build build32 --config Release
    5 md Pluginsx86
    6 copy /Y build32Releasekcp.dll Pluginsx86kcp.dll
    7 rmdir /S /Q build32
    8 pause

        2)windows64位系统:make_win64.bat

    1 mkdir build64 & pushd build64
    2 cmake -G "Visual Studio 14 2015 Win64" ..
    3 popd
    4 cmake --build build64 --config Release
    5 md Pluginsx86_64
    6 copy /Y build64Releasekcp.dll Pluginsx86_64kcp.dll
    7 rmdir /S /Q build64
    8 pause

        3)linux32位系统:make_linux32.sh

    1 mkdir -p build_linux32 && cd build_linux32
    2 cmake -DCMAKE_C_FLAGS=-m32 -DCMAKE_CXX_FLAGS=-m32 -DCMAKE_SHARED_LINKER_FLAGS=-m32 ../
    3 cd ..
    4 cmake --build build_linux32 --config Release
    5 cp build_linux32/kcp.so Plugins/x86/kcp.so
    6 rm -rf build_linux32

        4)linux64位系统:make_linux64.sh

    1 mkdir -p build_linux64 && cd build_linux64
    2 cmake ../
    3 cd ..
    4 cmake --build build_linux64 --config Release
    5 cp build_linux64/kcp.so Plugins/x86_64/kcp.so
    6 rm -rf build_linux64

        5)Mac系统:make_osx.sh

    1 mkdir -p build_osx && cd build_osx
    2 cmake -GXcode ../
    3 cd ..
    4 cmake --build build_osx --config Release
    5 mkdir -p Plugins/kcp.bundle/Contents/MacOS/
    6 cp build_osx/Release/kcp.bundle/Contents/MacOS/kcp Plugins/kcp.bundle/Contents/MacOS/kcp
    7 rm -rf build_osx

        6)android系统:make_android.sh

     1 if [ -z "$ANDROID_NDK" ]; then
     2     export ANDROID_NDK=~/android-ndk-r10e
     3 fi
     4 
     5 mkdir -p build_v7a && cd build_v7a
     6 cmake -DANDROID_ABI=armeabi-v7a -DCMAKE_TOOLCHAIN_FILE=../cmake/android.toolchain.cmake -DANDROID_TOOLCHAIN_NAME=arm-linux-androideabi-clang3.6 -DANDROID_NATIVE_API_LEVEL=android-9 ../
     7 cd ..
     8 cmake --build build_v7a --config Release
     9 mkdir -p Plugins/Android/libs/armeabi-v7a/
    10 cp build_v7a/libkcp.so Plugins/Android/libs/armeabi-v7a/libkcp.so
    11 rm -rf build_v7a
    12 
    13 mkdir -p build_x86 && cd build_x86
    14 cmake -DANDROID_ABI=x86 -DCMAKE_TOOLCHAIN_FILE=../cmake/android.toolchain.cmake -DANDROID_TOOLCHAIN_NAME=x86-clang3.5 -DANDROID_NATIVE_API_LEVEL=android-9 ../
    15 cd ..
    16 cmake --build build_x86 --config Release
    17 mkdir -p Plugins/Android/libs/x86/
    18 cp build_x86/libkcp.so Plugins/Android/libs/x86/libkcp.so
    19 rm -rf build_x86

        7)iOS系统:make_ios.sh

     1 mkdir -p build_ios && cd build_ios
     2 cmake -DCMAKE_TOOLCHAIN_FILE=../cmake/iOS.cmake  -GXcode ../
     3 cd ..
     4 cmake --build build_ios --config Release
     5 mkdir -p Plugins/iOS/
     6 exist_armv7=`lipo -info build_ios/Release-iphoneos/libkcp.a | grep armv7 | wc -l`
     7 exist_arm64=`lipo -info build_ios/Release-iphoneos/libkcp.a | grep arm64 | wc -l`
     8 if [ $[exist_armv7] -eq 0 ]; then
     9     echo "** ERROR **: No support for armv7, maybe XCode version is to high, use manual_build_ios instead!"
    10 elif [ $[exist_arm64] -eq 0 ]; then
    11     echo "** ERROR ** : No support for arm64, maybe XCode version is to high, use manual_build_ios instead!"
    12 else
    13     cp build_ios/Release-iphoneos/libkcp.a Plugins/iOS/libkcp.a
    14     rm -rf build_io
    15 fi

        8)Windows Phone系统:make_uwp.bat

     1 mkdir build_uwp & pushd build_uwp
     2 cmake -G "Visual Studio 14 2015" -DCMAKE_SYSTEM_NAME=WindowsStore -DCMAKE_SYSTEM_VERSION=10.0 ..
     3 popd
     4 cmake --build build_uwp --config Release
     5 md PluginsWSAx86
     6 copy /Y build_uwpReleasekcp.dll PluginsWSAx86kcp.dll
     7 rmdir /S /Q build_uwp
     8 
     9 mkdir build_uwp64 & pushd build_uwp64
    10 cmake -G "Visual Studio 14 2015 Win64" -DCMAKE_SYSTEM_NAME=WindowsStore -DCMAKE_SYSTEM_VERSION=10.0 ..
    11 popd
    12 cmake --build build_uwp64 --config Release
    13 md PluginsWSAx64
    14 copy /Y build_uwp64Releasekcp.dll PluginsWSAx64kcp.dll
    15 rmdir /S /Q build_uwp64
    16 
    17 mkdir build_uwp_arm & pushd build_uwp_arm
    18 cmake -G "Visual Studio 14 2015 ARM" -DCMAKE_SYSTEM_NAME=WindowsStore -DCMAKE_SYSTEM_VERSION=10.0 ..
    19 popd
    20 cmake --build build_uwp_arm --config Release
    21 md PluginsWSAARM
    22 copy /Y build_uwp_armReleasekcp.dll PluginsWSAARMkcp.dll
    23 rmdir /S /Q build_uwp_arm
    24 
    25 pause

        9)这些脚本都很简单明了,像需要的VS版本、安卓的NDK版本等等,一看便知。要移植到你自己的构建项目也很简单,基本只需要修改项目名称即可。

    六  各平台构建环境简要说明

      1)以下各平台,均需要安装CMake,安装教程,包括各平台环境变量等的配置自行谷歌,很多相关帖子。

      2)window系统和windows phone系统:在window系统中执行对应的.bat脚本即可,windows phone的编译需要相关SDK。本帖使用VS2015,windows phone暂时不需要,所以没有构建测试过。

      3)linux系统:在linux系统中执行对应的.sh脚本即可

      4)mac系统:在mac系统中执行对应的.sh脚本即可

      5)iOS系统:在mack系统中执行对应的.sh脚本即可。需要xcode版本8.1,版本太高会有问题,可以进行手动构建,参考博文:iOS代码封装成.a文件(封装SDK)。真机上的.a动态库需要同时支持armv7,ram64架构,查看你编译出来的.a是否是正确架构可使用lipo -info kcp.a

      6)android系统:安卓系统可以在各个平台编译,我这里是在linux系统中编译的,执行对应的.sh脚本即可,andorid SDK版本为:android-ndk-r10e。关于SDK安装以及环境变量配置自行谷歌。

      7)可能根据各位自己的情况,编译过程会遇到各种小问题,但是这些问题几乎都是出在环境上,如环境变量、SDK版本、xcode版本等,我这里已经对环境信息给了足够的说明,如果你实践当中问题解决不了,可以参考我这里列举的环境。

      8).sh文件在window下编辑可能会存在文件结束符问题,使用doc2unix命令转换就好,遇到权限问题使用chmod命令就好,其它可能遇到的细节问题自己耐心多多摸索,问题都不大。

    七  Unity项目动态库配置简要说明

      1、输出目录结构

        由于我们公司全部在window下开发,所以没有编译linux下的动态库,如果你们需要,将在x86、x86_64目录下存在libkcp.so文件。同样,window phone平台也没实测。

      2、window平台

        x86、x86_64下的.dll文件:

      3、Mac平台

        kcp.bundle,配置时选择osx系统,任意cpu

      4、linux平台

        x86、x86_64下的.so文件,相应配置

      5、andorid平台

        Android子目录下的.so文件,选择Android Platform,cpu架构做对应勾选,很简单,不再截图

      6、iOS平台

        选择iOS Platform

    八  动态库项目使用

      编写对应kcp.cs文件,声明外部函数,脚本如下:

     1 namespace KCP
     2 {
     3     using System;
     4     using System.Runtime.InteropServices;
     5 
     6     public class kcp
     7     {
     8 #if UNITY_IPHONE && !UNITY_EDITOR
     9         const string KcpDLL = "__Internal";
    10 #else
    11         const string KcpDLL = "kcp";
    12 #endif
    13 
    14         [DllImport(KcpDLL, CallingConvention=CallingConvention.Cdecl)]
    15         public static extern uint ikcp_check(IntPtr kcp, uint current);、
    16         //其它接口省略
    17     }
    18 }

      1)函数调用约定指明为cdecl

      2)所有接口以静态外部函数导出

      3)DllIport在iOS上和其它平台导出使用的参数不一样

      4)在构建你自己的动态库时依葫芦画瓢就好,没啥太多要说的

    九  kcp_build完整构建工程

      github地址:https://github.com/smilehao/kcp_bulild

    博客:http://www.cnblogs.com/SChivas/

    仓库:https://github.com/smilehao/

    邮箱:703016035@qq.com

    感谢您的阅读,如果您觉得本文对您有所帮助,请点一波推荐。

    欢迎各位点评转载,但是转载文章之后务必在文章页面中给出作者和原文链接,谢谢。

  • 相关阅读:
    14.Java基础_函数/函数重载/参数传递
    98. 验证二叉搜索树(深搜)
    13.Java基础_数组内存图
    12Java基础_数组定义格式/动态初始化/静态初始化
    计算几何基础
    11.Java基础_IDEA常用快捷键
    Add Two Numbers
    Two Sum
    登录界面id属性的使用
    系统查看
  • 原文地址:https://www.cnblogs.com/SChivas/p/7854100.html
Copyright © 2020-2023  润新知