跨平台工程构建

前言

最近由于项目需求,需要采用 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 git@github.com: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
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,602评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,442评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,878评论 0 344
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,306评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,330评论 5 373
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,071评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,382评论 3 400
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,006评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,512评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,965评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,094评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,732评论 4 323
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,283评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,286评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,512评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,536评论 2 354
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,828评论 2 345

推荐阅读更多精彩内容

  • CMake学习 本篇分享一下有关CMake的一些学习心得以及相关使用。 本文目录如下: [1、CMake介绍] [...
    AlphaGL阅读 12,220评论 11 79
  • 本篇文章旨在简介 Android 中 NDK 是什么以及重点讲解最新 Android Studio 编译工具 CM...
    Tsy远阅读 63,176评论 20 213
  • 本文不介绍cmake命令使用方法,也不讲CMakeLists.txt的语法,有需要的读者可以看我另外相关的文章即可...
    konishi5202阅读 1,101评论 0 5
  • 向您的项目添加 C 和 C++ 代码 本文内容 下载 NDK 和构建工具 创建支持 C/C++ 的新项目 构建和运...
    会飞的大象_阅读 3,750评论 0 3
  • 注:首发地址 1. 前言 当在做 Android NDK 开发时,如果不熟悉用 CMake 来构建,读不懂 CMa...
    cfanr阅读 24,261评论 1 53