热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

NativeHybridProgramming之构建初探

最近几周开始了IM项目,因此想在应用层写一套业务隔离的,跨平台的SDK,我便瞄准了libuv这个库,开始了NativeHybrid的研究——为了写一次代码可以同时在Android和

最近几周开始了 IM 项目,因此想在应用层写一套业务隔离的,跨平台的 SDK, 我便瞄准了 libuv 这个库,开始了 Native Hybrid 的研究 —— 为了写一次代码可以同时在 Android 和 iOS 平台上进行编译/运行。

libuv

一开始觉得编译 libuv 很困难,我采用了 gyp 的方案,而且之前对 Android NDK Toolchain 并不了解,导致以前对其尝试的时候,并没有获得我想要的结果,而且就算是 link 出来的包(不正确的方式),运行出错的信息,也不能读懂,所以浪费了我很多时间。总之,学习 Native 编程(此处 Native 指的是使用 C/C++ 进行平台级别的编程)或者说不管学习什么编程,第一个要务就是不能害怕,这个非常重要。

那么经过我的了解,GCC/Clang 编译链接的套路都差不多,无非是指定 源文件/头文件/链接库 进行操作,操作的时候有一些标志位,还有一些预定义,然后就是一些参数配置了。如果我们知晓了这些,只要一些库一开始做了跨平台开发的准备,那么使用 NDK/Android Toolchain 编译,一般都没有什么问题,那么废话不多说,我们主要就是指定几个环境变量。

CC:C 编译器,可以选择 Android Toolchain 下面的 GCC 或者 Clang
CXX: C++ 编译器
LINKER: 链接器
CFLAGS: CC 编译的时候,一些符号位。
CXXFLAGS: CXX 编译的时候,所需要的符号位

还有其他的环境变量,去参考 https://www.gnu.org/software/…
如果需要的话,我们还要给 GCC 指定一个 sysroot,方便我们的编译程序来寻找 .h 和库文件。
那么整个跨平台编译的流程就是

export CC=xxxxx
export CXX=xxxx
export CFLAGS=xx
export LD=xxx

然后make完事儿。

如果没有别的需求,那么整个编译流程就是这么简单,一开始我以为 libuv 这个库很复杂,后来证明 libuv 是一个很简单的库 = =。
当然,我们今天的主题是跨平台的编译,因此在这我们选择了 CMake 这个工具用来生成我们的 Makefile 和 xcodeproj。

CMake 的官网是:https://cmake.org/

编写 libuv 的 CMakeList.txt

如果我们使用 gyp 来生成 Makefile 的时候,可以看见 Android 下的 Makefile 内容并不是很多,用到的源文件和头文件其实也屈指可数。
具体可以使用 libuv 下的 android-configure 文件来调用 gyp 生成你的 Makefile 查看。
android-configure这个文件是把 NDK 中的独立工具链安装的 libuv 目录下面,然后导出有用的环境变量,像 CC/CXX/LD 之类的变量让 libuv 可以进行交叉编译。

DEFS_Debug := \
'-D_LARGEFILE_SOURCE' \
'-D_FILE_OFFSET_BITS=64' \
'-DDEBUG' \
'-D_DEBUG'
# Flags passed to all source files.
CFLAGS_Debug := \
-Wall \
-fvisibility=hidden \
-g \
--std=gnu89 \
-pedantic \
-Wall \
-Wextra \
-Wno-unused-parameter \
-Wstrict-prototypes \
-Wstrict-aliasing \
-g \
-O0 \
-fwrapv \
-fPIE
# Flags passed to only C files.
CFLAGS_C_Debug :=
# Flags passed to only C++ files.
CFLAGS_CC_Debug := \
-fno-rtti \
-fno-exceptions
INCS_Debug := \
-I$(srcdir)/include \
-I$(srcdir)/src
DEFS_Release := \
'-D_LARGEFILE_SOURCE' \
'-D_FILE_OFFSET_BITS=64' \
'-DNDEBUG'
# Flags passed to all source files.
CFLAGS_Release := \
-Wall \
-fvisibility=hidden \
-g \
--std=gnu89 \
-pedantic \
-Wall \
-Wextra \
-Wno-unused-parameter \
-Wstrict-prototypes \
-Wstrict-aliasing \
-O3 \
-fstrict-aliasing \
-fomit-frame-pointer \
-fdata-sections \
-ffunction-sections
# Flags passed to only C files.
CFLAGS_C_Release :=
# Flags passed to only C++ files.
CFLAGS_CC_Release := \
-fno-rtti \
-fno-exceptions
INCS_Release := \
-I$(srcdir)/include \
-I$(srcdir)/src
OBJS := \
$(obj).target/$(TARGET)/src/fs-poll.o \
$(obj).target/$(TARGET)/src/inet.o \
$(obj).target/$(TARGET)/src/threadpool.o \
$(obj).target/$(TARGET)/src/uv-common.o \
$(obj).target/$(TARGET)/src/version.o \
$(obj).target/$(TARGET)/src/unix/async.o \
$(obj).target/$(TARGET)/src/unix/core.o \
$(obj).target/$(TARGET)/src/unix/dl.o \
$(obj).target/$(TARGET)/src/unix/fs.o \
$(obj).target/$(TARGET)/src/unix/getaddrinfo.o \
$(obj).target/$(TARGET)/src/unix/getnameinfo.o \
$(obj).target/$(TARGET)/src/unix/loop.o \
$(obj).target/$(TARGET)/src/unix/loop-watcher.o \
$(obj).target/$(TARGET)/src/unix/pipe.o \
$(obj).target/$(TARGET)/src/unix/poll.o \
$(obj).target/$(TARGET)/src/unix/process.o \
$(obj).target/$(TARGET)/src/unix/signal.o \
$(obj).target/$(TARGET)/src/unix/stream.o \
$(obj).target/$(TARGET)/src/unix/tcp.o \
$(obj).target/$(TARGET)/src/unix/thread.o \
$(obj).target/$(TARGET)/src/unix/timer.o \
$(obj).target/$(TARGET)/src/unix/tty.o \
$(obj).target/$(TARGET)/src/unix/udp.o \
$(obj).target/$(TARGET)/src/unix/proctitle.o \
$(obj).target/$(TARGET)/src/unix/linux-core.o \
$(obj).target/$(TARGET)/src/unix/linux-inotify.o \
$(obj).target/$(TARGET)/src/unix/linux-syscalls.o \
$(obj).target/$(TARGET)/src/unix/pthread-fixes.o \
$(obj).target/$(TARGET)/src/unix/android-ifaddrs.o \
$(obj).target/$(TARGET)/src/unix/pthread-barrier.o \
$(obj).target/$(TARGET)/src/unix/procfs-exepath.o \
$(obj).target/$(TARGET)/src/unix/sysinfo-loadavg.o \
$(obj).target/$(TARGET)/src/unix/sysinfo-memory.o

截取这么多,可以看到个大概了,如果想看具体的执行,我们可以调用make V=1来查看具体执行的flags。导出以后,我们的CMakeList.txt大概如下:

cmake_minimum_required(VERSION 3.4.1)
PROJECT(uv CXX C)
INCLUDE_DIRECTORIES (${CMAKE_CURRENT_SOURCE_DIR}/include ${CMAKE_CURRENT_SOURCE_DIR}/src)
# from uv.gyp
MESSAGE(STATUS "in libuv" ${CMAKE_CURRENT_SOURCE_DIR})
SET(SRC_LIST "${CMAKE_CURRENT_SOURCE_DIR}/src/fs-poll.c"
"${CMAKE_CURRENT_SOURCE_DIR}/src/inet.c"
"${CMAKE_CURRENT_SOURCE_DIR}/src/threadpool.c"
"${CMAKE_CURRENT_SOURCE_DIR}/src/uv-common.c"
"${CMAKE_CURRENT_SOURCE_DIR}/src/version.c"
"${CMAKE_CURRENT_SOURCE_DIR}/src/unix/async.c"
"${CMAKE_CURRENT_SOURCE_DIR}/src/unix/core.c"
"${CMAKE_CURRENT_SOURCE_DIR}/src/unix/dl.c"
"${CMAKE_CURRENT_SOURCE_DIR}/src/unix/fs.c"
"${CMAKE_CURRENT_SOURCE_DIR}/src/unix/getaddrinfo.c"
"${CMAKE_CURRENT_SOURCE_DIR}/src/unix/getnameinfo.c"
"${CMAKE_CURRENT_SOURCE_DIR}/src/unix/loop.c"
"${CMAKE_CURRENT_SOURCE_DIR}/src/unix/loop-watcher.c"
"${CMAKE_CURRENT_SOURCE_DIR}/src/unix/pipe.c"
"${CMAKE_CURRENT_SOURCE_DIR}/src/unix/poll.c"
"${CMAKE_CURRENT_SOURCE_DIR}/src/unix/process.c"
"${CMAKE_CURRENT_SOURCE_DIR}/src/unix/signal.c"
"${CMAKE_CURRENT_SOURCE_DIR}/src/unix/stream.c"
"${CMAKE_CURRENT_SOURCE_DIR}/src/unix/tcp.c"
"${CMAKE_CURRENT_SOURCE_DIR}/src/unix/thread.c"
"${CMAKE_CURRENT_SOURCE_DIR}/src/unix/timer.c"
"${CMAKE_CURRENT_SOURCE_DIR}/src/unix/tty.c"
"${CMAKE_CURRENT_SOURCE_DIR}/src/unix/udp.c"
"${CMAKE_CURRENT_SOURCE_DIR}/src/unix/proctitle.c"
"${CMAKE_CURRENT_SOURCE_DIR}/src/unix/pthread-barrier.c")
IF (DEFINED ANDROID)
ADD_DEFINITIONS(-D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64)
SET(SRC_LIST ${SRC_LIST}
"${CMAKE_CURRENT_SOURCE_DIR}/src/unix/linux-core.c"
"${CMAKE_CURRENT_SOURCE_DIR}/src/unix/linux-inotify.c"
"${CMAKE_CURRENT_SOURCE_DIR}/src/unix/linux-syscalls.c"
"${CMAKE_CURRENT_SOURCE_DIR}/src/unix/pthread-fixes.c"
"${CMAKE_CURRENT_SOURCE_DIR}/src/unix/android-ifaddrs.c"
"${CMAKE_CURRENT_SOURCE_DIR}/src/unix/procfs-exepath.c"
"${CMAKE_CURRENT_SOURCE_DIR}/src/unix/sysinfo-loadavg.c"
"${CMAKE_CURRENT_SOURCE_DIR}/src/unix/sysinfo-memory.c")
IF(CMAKE_BUILD_TYPE STREQUAL "Debug")
# from common.gypi
MESSAGE(STATUS "in Debug") ADD_DEFINITIONS(-DDEBUG -D_DEBUG)
SET(COMMON_C_FLAGS "-Wall \
-fvisibility=hidden \
-g \
--std=gnu89 \
-pedantic \
-Wextra \
-Wno-unused-parameter \
-Wstrict-prototypes \
-Wstrict-aliasing \
-g \
-O0 \
-fwrapv \
-fPIE")
ELSE()
MESSAGE(STATUS "in Release")
ADD_DEFINITIONS(-DNDEBUG)
SET(COMMON_C_FLAGS "-Wall \
-fvisibility=hidden \
-g \
--std=gnu89 \
-pedantic \
-Wextra \
-Wno-unused-parameter \
-Wstrict-prototypes \
-Wstrict-aliasing \
-O3 \
-fstrict-aliasing \
-fomit-frame-pointer \
-fdata-sections \
-ffunction-sections")
ENDIF(CMAKE_BUILD_TYPE STREQUAL "Debug")
SET(CMAKE_C_FLAGS_DEBUG ${COMMON_C_FLAGS})
SET(CMAKE_CXX_FLAGS_DEBUG ${COMMON_C_FLAGS} "-fno-rtti -fno-exceptions")
SET(CMAKE_C_FLAGS_RELEASE ${COMMON_C_FLAGS})
SET(CMAKE_CXX_FLAGS_RELEASE ${COMMON_C_FLAGS} "-fno-rtti -fno-exceptions")
ELSEIF (DEFINED APPLE)
SET(SRC_LIST ${SRC_LIST}
"${CMAKE_CURRENT_SOURCE_DIR}/src/unix/bsd-ifaddrs.c"
"${CMAKE_CURRENT_SOURCE_DIR}/src/unix/darwin-proctitle.c"
"${CMAKE_CURRENT_SOURCE_DIR}/src/unix/darwin.c"
"${CMAKE_CURRENT_SOURCE_DIR}/src/unix/fsevents.c"
"${CMAKE_CURRENT_SOURCE_DIR}/src/unix/kqueue.c")
SET(CMAKE_CXX_FLAGS "-fobjc-abi-version=2 \
-fobjc-arc \
${CMAKE_CXX_FLAGS}")
# end of defined android
ENDIF(DEFINED ANDROID)
ADD_LIBRARY(uv STATIC ${SRC_LIST})

这里利用 CMake 的逻辑判断,分别加入了 iOS 和 Android 不同平台的源文件,我们去查看这些文件会发现他们都是平台相关的 API 实现,共有的一些源文件则是抽象的接口层。

如果想知道 CMake 指令如何使用,可以查看相关文档:
CMake 文档传送门:https://cmake.org/cmake/help/…

开始执行 CMake

当然,光有 CMake 还不够,CMake 提供了一套工具链系统,iOS 和 Android 都有相应的开源工具链插件:

iOS: https://github.com/cristeab/i…
Android :https://github.com/taka-no-me…

指定了 Toolchain 和我们的 CPU ABI 之后,我们就可以为指定的 CPU 架构编译出特定的二进制库了

比如,编译 Android 如下:

cmake -DCMAKE_TOOLCHAIN_FILE=./toolchain/android.toolchain.cmake \
-DCMAKE_BUILD_TYPE=Debug \
-DANDROID_ABI="armeabi-v7a" \
-DANDROID_NATIVE_API_LEVEL="android-21" \
.

编译 iOS 如下:

cmake -DCMAKE_TOOLCHAIN_FILE=./toolchain/darwin.toolchain.cmake \
-DIOS_PLATFORM=OS \
-GXcode \
.

-G是让 CMake 生成一个 Xcode 的构建系统,也就是 Xcode 项目文件。这样我们就可以启动 Xcode 进行编译了。

我们把这两段脚本写在一个bash文件里,每次执行的时候,只用调用./build_android.sh./build_ios.sh就行了。
每次执行android的时候,会生成libxxx.soMakefile,执行ios的时候,会生成xxx.xcodeproj,是不是觉得特别炫酷?


推荐阅读
  • 前言作为一个移动端初学者、爱好者,能使用前端技术开发原生游戏一直是一件渴望而不可及的事情,暂且不说游戏逻辑的复杂度,算法的健壮性ÿ ... [详细]
  • Flutter 核心技术与混合开发模式深入解析
    本文深入探讨了 Flutter 的核心技术,特别是其混合开发模式,包括统一管理模式和三端分离模式,以及混合栈原理。通过对比不同模式的优缺点,帮助开发者选择最适合项目的混合开发策略。 ... [详细]
  • 本文将介绍如何在混合开发(Hybrid)应用中实现Native与HTML5的交互,包括基本概念、学习目标以及具体的实现步骤。 ... [详细]
  • 本文详细介绍了在 React Native 开发过程中遇到的 'Could not connect to development server' 错误及其解决方法。该问题不仅影响开发效率,而且难以通过网络资源找到确切的解决方案。本文将提供详细的步骤,帮助开发者快速解决这一常见问题。 ... [详细]
  • 本文详细介绍了如何在ReactJS项目中集成Onsen-UI的ActionSheetButton组件,并通过具体示例展示了其使用方法及效果。 ... [详细]
  • 使用IntelliJ IDEA高效开发与运行Shell脚本
    本文介绍了如何利用IntelliJ IDEA中的BashSupport插件来增强Shell脚本的开发体验,包括插件的安装、配置以及脚本的运行方法。 ... [详细]
  • 本文详细介绍了PHP中的几种超全局变量,包括$GLOBAL、$_SERVER、$_POST、$_GET等,并探讨了AJAX的工作原理及其优缺点。通过具体示例,帮助读者更好地理解和应用这些技术。 ... [详细]
  • 本文概述了在GNU/Linux系统中,动态库在链接和运行阶段的搜索路径及其指定方法,包括通过编译时参数、环境变量及系统配置文件等方式来控制动态库的查找路径。 ... [详细]
  • 使用REM和媒体查询实现响应式布局
    本文介绍如何利用REM单位和媒体查询(Media Queries)来创建适应不同屏幕尺寸的网页布局。通过具体示例,展示在不同屏幕宽度下如何调整页面元素的样式。 ... [详细]
  • 本文详细介绍了JQuery Mobile框架中特有的事件和方法,帮助开发者更好地理解和应用这些特性,提升移动Web开发的效率。 ... [详细]
  • 腾讯云移动推送TPNS(Tencent Push Notification Service)为APP开发者和运营人员提供了一站式、高效、稳定的推送解决方案,帮助提升用户活跃度和运营效率。 ... [详细]
  • 在项目需要国际化处理时,即支持多种语言切换的功能,通常有两种方案:单个包和多个包。本文将重点讨论单个包的实现方法。 ... [详细]
  • 零拷贝技术是提高I/O性能的重要手段,常用于Java NIO、Netty、Kafka等框架中。本文将详细解析零拷贝技术的原理及其应用。 ... [详细]
  • 未定义的打字稿记录:探索其成因与解决方案 ... [详细]
  • 当前,众多初创企业对全栈工程师的需求日益增长,但市场中却存在大量所谓的“伪全栈工程师”,尤其是那些仅掌握了Node.js技能的前端开发人员。本文旨在深入探讨全栈工程师在现代技术生态中的真实角色与价值,澄清对这一角色的误解,并强调真正的全栈工程师应具备全面的技术栈和综合解决问题的能力。 ... [详细]
author-avatar
Carry_Jia
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有