跨平台工程构建

前言

最近由于项目需求,需要采用 C++ 编译一个库,供给 iOS 和 android 使用。本人是第一次接触到跨平台项目,之前也几乎没有接触过相关的内容,因此刚开始的时候一脸懵逼。回顾整个过程,最困难的地方是在整个项目架子的搭建过程。为了方便自己后续的 C++ 库的开发,就花了点时间编写了一个简单的 python 脚本,可以较为简单、快速的搭建一个 C++ 跨平台工程的架子。

下面的内容,主要从 4 个方面进行介绍:

  1. 环境配置
  2. 项目脚手架脚本使用
  3. 项目整体结构介绍
  4. 实例演示

环境配置

由于本人是在 mac 上面进行编程,所以以下所有的内容都是以 mac osx 系统为基础进行的。相关的环境配置分成 2 部分。「项目脚手架运行环境配置 」和「跨平台项目编译环境配置」

项目脚手架运行环境配置

  • python 3

跨平台项目编译环境配置

  • python(>=2.7.11)
  • cmake(3.8.x)
  • conan(>=0.30.x) 添加内部仓库
  • NDK(android-ndk-r13b)
  • 配置 ANDROID_NDK_HOME 环境变量指向 NDK 目录,编译 android 需要

Tip
Xcode 11 与 cmake 不兼容问题

项目脚手架脚本使用

  1. 从 GitHub 上拉取脚手架代码

git clone [email protected]:zhshijie/CrossPlatformCreator.git

  1. 进入到 crossplatformcreator 文件夹

cd crossplatformcreator

  1. 运行 createProject.py 脚本

python3 createProject.py

  1. 按照脚本提示输入项目名称、项目路径、iOS 的 bundle ID 和安卓的包名,如下图所示:
图1.4-脚手架使用图示.png

如果一切顺利,就可以在指定的项目路径中找到新创建的跨平台项目。

Tip
python3 使用时,Xcdoe 11 和 Xcode 10 冲突问题

项目整体结构

项目的整体目录结构如下图:

图1.5-项目整体目录结构.png

按照文件的功能,可以目录归为 3 部分:

  1. 脚本文件
  2. 配置文件
  3. 源文件

脚本文件

bootstrap.pybuild.py 属于脚本文件。

bootstrap.py

bootstrap.py 是对 CMake 命令行的封装,使用 bootstrap.py,可以快速的创建不同平台的项目。例如:调用 python bootstrap iOS,会在项目的根目录创建一个 build 文件夹,里面存放着一个 iOS 项目。如下图所示:

图1.6-iOS项目目录结构.png

各个平台对应项目的创建方法,可以在项目根目录中的 README.md 进行了解

build.py

调用 build.py 时,会对各个平台的项目进行编译,编译的结果存储在各个平台根目录的 lib 的文件夹中。例如:调用 python build.py iOS,会编译出 framwork,存储在 lib中,编译结果如下图所示:

图1.7-iOS编译目录结果.png

一般来说,脚本文件都是不需要修改的,所以直接使用就可以了。

配置文件

CMakeLists.txt, conanfile.txt, buildtool文件夹cmake文件夹 都是和 cmake 相关的配置文件。下面通过几个例子,来解析下各个配置项的作用。

buildtool文件夹

buildtool,顾名思义,里面存储的是一些编译相关的工具文件或配置,默认该文件夹里面会有个 conan.cmake 文件,conan 是 C++ 中包管理工具,因此该文件主要功能是 cmake 在编译时,使用 conan 拉取依赖库。拉取的库存储在用户本地的/Users/{用户名}/.conan/data路径下(mac)。一般来说,不需要改动该文件。

cmake文件夹

由于不同平台需要用到的编译参数不一样,所以会将各个平台的参数,统一存储到.camke配置脚本中,然后在编译不同目标系统时,为 cmake 指定不同的编译配置脚本,我们可以打开项目根目录中的 bootstarp.py 文件,然后全局搜索 args["system"] == "ios", 如下图所示

图1.8-iOStoolchainFile配置.png

其中,从上往下第二个红框中拼接了ios.toolchain.cmake的路径,在第三和第四个红框的位置,将路径设置给了DCMAKE_TOOLCHAIN_FILE变量。进入到 ios.toolchain.cmake 文件中查看,我们主要可以看到以下 3 种语法

execute_process(...)
message(...)
set()

execute_process 的作用用是执行 shell 命令,在 ios.toolchain.cmake 中有以下代码

图1.9-iOSToolChain的内容.png

这段代码的功能主要是执行 xcrun 指令

message 的功能主要是输出信息到控制台

set 的功能主要是设置变量,在 ios.toolchain.cmake 中存在大量的变量设置,如下图所示

图1.10-iOSToolChain变量设置.png

例如,对于 iOS 来说,50,51,52 3 行分别设置了项目的签名、bitcode 和 最低支持版本 3 个相关配置。

正常来说,这些配置都是通用配置,不用进行修改。

如果需要修改配置,但是又不清楚自己的平台使用了哪一个 .cmake 脚本进行配置,可以到 bootstarp.py 中找到目标平台,查看该平台的 DCMAKE_TOOLCHAIN_FILE 设置的路径。以 andorid 为例,如下图所示,

图1.11-AndroidCmake设置.png

android 是通过系统环境变量ANDROID_NDK_HOME来获取到系统NDK 中的.cmake配置。

conanfile.txt

由于我们是使用conan来进行包管理,conanfile中设置了项目中依赖到的第三方库,下面例子展示了如何引入一个 boringssl 库:

[requires]
boringssl/1.1.0@1602/stable

[generators]
cmake_multi

[options]
boringssl:shared=False
#第三方依赖库的配置,shared=False 通过静态库的方式引入

CMakeLists.txt

cmake 是一个项目软件构建管理工具,它允许开发者用一种简单的文本格式进行构建参数的指定。前面我们讨论到的camke文件夹中的 .cmake 脚本主要是用于未编译时环境的配置,而正式编译时,由 CMakeLists 文件中的内容,设置了编译的源码路径,各个子模块源码直接的依赖关系,生成工程的类型等等。

具体的 cmake 语法在这里就不细讲了。一些项目的配置项,会在下面的源文件模块中提及到。

源文件

项目根目录的sdk中存储的项目的源码文件,由于 CMakeLists.txt 和源码文件关联性较大,所以会合并在一起讲。

项目源文件目录初始目录结构如下:

- HelloWorld
  - sdk
    - src
      - CMakeLists.txt
      - Commons
      - header
      - src
      - jni 

    - cocoa
    - android
    
  - CMakeLists.txt

首先在根目录的 sdk 目录中,有 3 个子目录 src, cocoa, android,如下所示

- HelloWorld
  - sdk
    - src
    - cocoa
    - android

src 主要存放 C++ 相关的源码

cocoa 主要存放 Objective-C 对于 C++ 的封装源码

android 主要存放 Android 的 java 文件

也就是说,在 src 目录中存储了核心的源码信息,而在 cocoaandroid 中主要存储的是根据不同的平台,对 C++ 封装的源码

详细看下 src 子目录中的内容,其内容如下:

- src
  - CMakeLists.txt
  - Commons
  - header
  - src
  - jni

src 目录中,有一个 CMakeLists 文件,说明该目录应该是一个子模块,这时候打开下根目录的 CMakeLists.txt 文件,直接拉到文件最下面,可以看到以下代码

# 编译 sdk/src 目录下文件,本质上是调用 sdk/src 目录中的 CMakeLists.txt
add_subdirectory(sdk/src)

在使用 cmake 编译时,sdk/src会作为一个子模块加入主项目中。

Commons是一个存储版本信息的文件,如果不需要版本控制,可以不需要理会。

header 存储的是 C++ 对外暴露的头文件

src 存储的是 C++ 的源文件,包含了 .h.cc 文件

jni 存储的是 androidjni 层的源文件

了解目录的整体结构后,接下来需要简单了解下CMakeLists中的相关配置。

下面图片主要展示了 CMakeLists 中文件加载的代码

图1.13-项目中源文件目录.png

通过 file 指令加载目录中的指定类型的问题,并存储到变量中,如

file(GLOB objc_public_header "${CMAKE_SOURCE_DIR}/sdk/cocoa/src/*.h*")

上面的直接就是加载{CMAKE_SOURCE_DIR}/sdk/cocoa/src/中所有的 .h 文件,存储在 objc_public_header 变量中。

成功获取到源文件后,就需要将源文件编译生成库或项目,下图所示:

图1.14-库的生成.png

通过 add_library,可以生成一个静态库或动态库。如果是 iOS 需要引入 cocoa 目录中的封装代码和封装代码中使用到的系统基础库,如果是安卓,需要引入 jni 目录中的封装代码。add_library 中的第二参数是指定生成动态库或静态库,不设置默认是静态库,SHARED 是动态库。

如果项目中使用到了依赖库,那么还需要将依赖库和项目的target链接起来,如下图所示

target 就是通过 add_library 或 add_executable 创建的内容

图1.15-链接第三方库.png

所以在 cmake 中,我们是使用 conan 来拉取依赖库,然后通过 target_link_libraries 将依赖库链接到指定的target中。

我们梳理下 CMakeLists 文件中整体的思路

  1. 通过 file 获取到源文件的路径集合
  2. 通过 add_libraryfile 获取到的源文件路径集合编译出一个target(库或可执行文件)
  3. 通过 target_link_libraries 将编译出来的target和依赖库进行链接

实例演示

如果你是 iOS 或 Cocoa 的开发者,下面为你展示下在如何通过 Xcode 来进行库的编码和调试

  1. 相互环境配置
  2. 通过 crossplatformcreator 创建一个跨平台工程,helloWorld
  3. 在项目根目录运行运行 python bootstrap.py ios,创建 iOS 工程
  4. 打开 build 目录中的 iOS 工程,如下图
图1.16-iOS工程目录.png

如何编写 C++ 源码

直接在xxxStaticSDKxxxSDK中的 headersrc 中直接编写源码就可以了,如下图所示,添加一个 helloWorld 方法

图1.17-编写C++源码.png

如何封装 C++ 源码

直接在xxxStaticSDKxxxSDK中的 Header FilesSource Files 中直接编写封装源码,如下图所示,添加一个 helloWorld 方法

图1.18-ObjcectiveC封装.png

如何调试

HelloWorldDemo 中直接引入封装后的Objective-C对象,调用相应的方法。然后直接运行 HelloWorldDemo target 就可以进行调试。

图1.19-如何调试.png

你可能感兴趣的:(跨平台工程构建)