• Lv0
    粉丝2

积分736 / 贡献0

提问27答案被采纳0文章32

  • 热心答主

    月回答量前10用户
  • 黄金

    累计积分达到3000分
  • 白银

    累计积分达到1500分
  • 答主大咖

    月回答采纳数前10用户
  • 月度专家

    负责专区当月TOP10

[经验分享] 鸿蒙游戏三方库编译适配之C/C++篇

yeyao1 企业号 显示全部楼层 发表于 2024-8-7 17:12:37
鸿蒙游戏三方库编译适配之C/C++
1.   编译适配工具介绍
1.1 CMake构建系统
鸿蒙中提供的编译构建系统为CMake。提供了一系列的脚本文件。在ndk目录(/HarmonyOS-NEXT-DB1/openharmony/native)的build目录中放置预定义的toolchain脚本文件ohos.toolchain.cmake。
                              
CMake编译时需要读取该文件中的默认值,比如编译器架构、C++库链接方式等,因此在编译时会通过CMAKE_TOOLCHAIN_FILE指出该文件的路径,便于CMake在编译时定位到该文件。
build-tools文件夹:放置NDK提供的编译工具。
1.2 编译器
鸿蒙中提供的编译器为Clang+LLVM,在ndk目录的llvm目录中。
Clang和LLVM关系
Clang编译main.c到可执行文件的过程:
   
1.预编译:clang –E main.c –o main_precompile.i
2.词法分析:clang -fmodules -E -Xclang-dump-tokens main.c
3.语法分析:clang -fmodules-fsyntax-only -Xclang -ast-dump main.c
4.中间代码:clang -S -fobjc-arc-emit-llvm main.c -o main.ll
5.汇编:clang -S main.c -omain_compile.s
6.目标程序:clang -fmodules -c main.c -omain_obj.o
7.生成可执行文件:clang main_obj.o -o mainExec
1.3 使用工具链进行编译
工具链的使用基本上可以理解为两个脚本cmake_exec.sh和ninja_exec.sh。
```shell
# 脚本1 cmake_exec.sh
#===============================
#   需调整鸿蒙sdk路径及工具链路径
#===============================
/home/chenghui/ohos_sdk/native/build-tools/cmake/bin/cmake-GNinja \
-DCMAKE_BUILD_TYPE=Release-DCMAKE_INSTALL_PREFIX=./ \
-DCMAKE_TOOLCHAIN_FILE="/home/chenghui/ohos_sdk/native/build/cmake/ohos.toolchain.cmake"-B build
```
cmake_exec.sh编译会生成一些“中间”文件,如调整了CMakeLists.txt文件需要将build文件夹清空,重新执行该脚本,避免文件缓存。若此脚本执行出现错误,一般情况下都是编译脚本的相关问题,主要调整CMakeLists.txt文件。
```shell
# 脚本2 ninja_exec.sh
#===============================
#   需调整鸿蒙sdk路径及工具链路径
#===============================
/home/chenghui/ohos_sdk/native/build-tools/cmake/bin/ninja-C build/
```
ninja_exec.sh需要在cmake_exec.sh脚本正常的前提下运行。如此脚本中执行出现错误,一般需要涉及源码调整。
使用上述两个脚本,基本上可以满足全部的CMakeList.txt编译,然后再针对编译过程中问题进行调整。
注意:Linux环境下,若是在cmake_exec.sh脚本中不指定-G,默认可以使用make工具进行编译。

2.   非CMAKE工程编译适配
C/C++三方库中还有许多的三方库可能需要使用除cmake之外的其他方式进行构建。构建此类三方库一般使用Clang+llvm编译器直接构建,其中可能还存在一些其他脚本,但本质上都是使用编译器,其他脚本作为辅助。
构建此类三方库一般两种方式:使用CMakeLists.txt重写编译脚本或是使用原生构建工具。
2.1 使用CMakeLists.txt重写编译脚本
此过程使用于相对简单的三方库,对于依赖简单,且使用工具单一(较为复杂的库可能还需要使用到python、rust等)的情况可以使用。
如三方库提供Android平台版本,可以参考Android.mk文件进行编写,内容与CMakeLists.txt文件较为相似。
      
   
   
cmake_minimum_required(VERSION    3.6)
   
project(pomelo2)
   
set(POMELO_OBJECT
   
       src/pc_lib.c
   
       src/pc_pomelo.c
   
       src/pc_trans.c
   
    ……
   
       src/tr/uv/pr_msg_pb.c
   
       src/tr/uv/tr_uv_tcp_i.c
   
    ……
   
    )
   
   
set(UV_OBJECT
   
       deps/uv/src/fs-poll.c
   
       deps/uv/src/inet.c
   
       deps/uv/src/threadpool.c
   
    ……
   
)
   
   
add_library(${PROJECT_NAME}    STATIC ${POMELO_OBJECT} ${UV_OBJECT})
   
target_compile_options(${PROJECT_NAME}   
   
       PRIVATE -fPIC  
   
       PRIVATE -Wall
   
       PRIVATE -DPC_NO_UV_TLS_TRANS
   
       PRIVATE -D_GNU_SOURCE
   
    )
   
target_include_directories(${PROJECT_NAME}   
   
       PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/include
   
       PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/src
   
       PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/deps/uv/src
   
       PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/deps/uv/src/unix
   
    )
   
   
   
   
        
   
   
LOCAL_PATH :=    $(call my-dir)
   
include    $(CLEAR_VARS)
   
   
LOCAL_MODULE :=    pomelo_static
   
LOCAL_MODULE_FILENAME    := libpomelo
   
LOCAL_SRC_FILES :=    \
   
src/pc_lib.c \
   
src/pc_pomelo.c \
   
src/pc_trans.c \
   
src/pc_trans_repo.c    \
   
src/tr/dummy/tr_dummy.c    \
   
……
   
src/tr/uv/pr_msg_pb.c    \
   
src/tr/uv/pr_pkg.c    \
   
src/tr/uv/tr_uv_tcp.c    \
   
src/tr/uv/tr_uv_tcp_aux.c    \
   
src/tr/uv/tr_uv_tcp_i.c
   

    LOCAL_CFLAGS := -DPC_NO_UV_TLS_TRANS
   
LOCAL_EXPORT_C_INCLUDES    :=$(LOCAL_PATH)/include
   
LOCAL_C_INCLUDES :=    $(LOCAL_PATH)/include \
   
                       $(LOCAL_PATH)/src \
   
                          $(LOCAL_PATH)/src/tr/dummy \
   
                          $(LOCAL_PATH)/src/tr/uv
   
LOCAL_WHOLE_STATIC_LIBRARIES    := uv_static jansson_static
   
include    $(BUILD_STATIC_LIBRARY)
   
LOCAL_CFLAGS       := -D__ANDROID__
   
$(call    import-module,libpomelo2/deps/uv) \
   
$(call    import-module,libpomelo2/deps/jansson)
   
   
   以下为CMakeLists.txt重写Android.mk示例:2.2 使用原生构建工具
使用原生的构建工具,需要针对环境进行配置,一般建议在linux环境下使用。
注意:linux环境下需要linux版本sdk,且解压时注意使用linux环境下的解压工具解压,避免问题。
环境配置如下:
```shell
exportOHOS_SDK=/home/ohos/tools/OH_SDK/ohos-sdk/linux # 此处替换解压目录
exportAS=${OHOS_SDK}/native/llvm/bin/llvm-as
exportCC="${OHOS_SDK}/native/llvm/bin/clang --target=aarch64-linux-ohos"
exportCXX="${OHOS_SDK}/native/llvm/bin/clang++ --target=aarch64-linux-ohos"
exportLD=${OHOS_SDK}/native/llvm/bin/ld.lld
exportSTRIP=${OHOS_SDK}/native/llvm/bin/llvm-strip
exportRANLIB=${OHOS_SDK}/native/llvm/bin/llvm-ranlib
exportOBJDUMP=${OHOS_SDK}/native/llvm/bin/llvm-objdump
exportOBJCOPY=${OHOS_SDK}/native/llvm/bin/llvm-objcopy
exportNM=${OHOS_SDK}/native/llvm/bin/llvm-nm
exportAR=${OHOS_SDK}/native/llvm/bin/llvm-ar
exportCFLAGS="-fPIC -D__MUSL__=1"
exportCXXFLAGS="-fPIC -D__MUSL__=1"
```
完成环境配置之后,可参照三方库提供文档(readme文件或是其他)按照教程进行编译。
编译过程中可能会涉及一些平台相关信息调整和修改。

3.   编译适配示例
通过对openal三方库鸿蒙化,来加深大家对编译的理解。
openal说明:跨平台音频库,里面包含多种音频处理方式,本示例中仅对opensles进行鸿蒙化处理。
本示例中使用的版本:1.22.0源码地址:https://github.com/kcat/openal-soft
3.1 分析
     
   
   
# ......
   
# 省略部分代码
   
# ......
   
if(ANDROID)
   
# 此处使用日志相关功能,可以替换为鸿蒙hilog
   
# Include liblog for Android logging
   
check_library_exists(log    __android_log_print "" HAVE_LIBLOG)
   
if(HAVE_LIBLOG)
   
set(EXTRA_LIBS log ${EXTRA_LIBS})
   
set(CMAKE_REQUIRED_LIBRARIES    ${CMAKE_REQUIRED_LIBRARIES} log)
   
endif()
   
endif()
   
# ......
   
# 省略部分代码
   
# ......
   
option(ALSOFT_REQUIRE_OPENSL    "Require OpenSL backend" OFF)
   
if(CMAKE_SYSTEM_NAME MATCHES    "ANDROID")
   
# 检查opensles头文件
   
CHECK_INCLUDE_FILES("SLES/OpenSLES.h;SLES/OpenSLES_Android.h"    HAVE_SLES_OPENSLES_H
   
)
   
SET(HAVE_OPENSL_ANDROID 1)
   
endif()
   
   
   该三方库提供CMakeLists.txt脚本,分析脚本中ANDROID相关部分。
     
   
   
# ......
   
# 省略部分代码
   
# ......
   
IF(HAVE_SLES_OPENSLES_H)
   
#    CHECK_SHARED_FUNCTION_EXISTS(slCreateEngine "SLES/OpenSLES.h"    OpenSLES "" HAVE_L
   
IBOPENSLES)
   
# IF(HAVE_LIBOPENSLES)
   
OPTION(ALSOFT_BACKEND_OPENSL    "Enable OpenSL backend" ON)
   
IF(ALSOFT_BACKEND_OPENSL)
   
SET(HAVE_OPENSL 1)
   
IF(HAVE_OPENSL_ANDROID)
   
# 调用opensles代码在Alc/backends/opensl.cpp中
   
SET(ALC_OBJS ${ALC_OBJS}    Alc/backends/opensl.cpp Alc/backends/opensl.
   
h)
   
ENDIF()
   
SET(BACKENDS "${BACKENDS}    OpenSL,")
   
SET(EXTRA_LIBS OpenSLES ${EXTRA_LIBS})
   
ENDIF()
   
# ENDIF()
   
ENDIF()
   
# ......
   
# 省略部分代码
   
# ......
        
   
   
通过源码中提供的CMakeLists.txt中看出,ANDROID平台编译时,主要使用的是opensles和log。 log模块我们可以使用hilog进行替换; opensles这个模块在鸿蒙sdk中也是提供的,也可以尝试替换;
我们先对比一下AndroidNDK中该模块的头文件和鸿蒙sdk中有啥区别 androidNDK版本:25.1.8937393
鸿蒙sdk版本API11
对比内容后发现有鸿蒙上面的版本和androidNDK版本还是有较大差别的。但仍然可以尝试调整。
3.2 编译脚本调整
调整CMakeLists.txt脚本,将ANDROID相关部分使用鸿蒙中能力进行替。
     
   
   
# ......
   
# 省略部分代码
   
# ......
   
if(ANDROID)
   
# Include liblog for Android logging
   
check_library_exists(log    __android_log_print "" HAVE_LIBLOG)
   
if(HAVE_LIBLOG)
   
set(EXTRA_LIBS log ${EXTRA_LIBS})
   
set(CMAKE_REQUIRED_LIBRARIES    ${CMAKE_REQUIRED_LIBRARIES} log)
   
endif()
   
endif()
   
# 新增代码,鸿蒙hilog
   
if(CMAKE_SYSTEM_NAME MATCHES    "OHOS")
   
SET(EXTRA_LIBS libhilog_ndk.z.so    ${EXTRA_LIBS})
   
SET(CMAKE_REQUIRED_LIBRARIES    ${CMAKE_REQUIRED_LIBRARIES} libhilog_ndk.z.so)
   
endif()
   
   
   
     
   
   
# ......
   
# 省略部分代码
   
# ......
   
option(ALSOFT_REQUIRE_OPENSL    "Require OpenSL backend" OFF)
   
if(CMAKE_SYSTEM_NAME MATCHES    "OHOS")
   
CHECK_INCLUDE_FILES("SLES/OpenSLES.h;SLES/OpenSLES_OpenHarmony.h"    HAVE_SLES_OPENSLES
   
_H)
   
SET(HAVE_OPENSL_OHOS 1)
   
else()
   
CHECK_INCLUDE_FILES("SLES/OpenSLES.h;SLES/OpenSLES_Android.h"    HAVE_SLES_OPENSLES_H)
   
SET(HAVE_OPENSL_ANDROID 1)
   
endif()
   
# ......
   
# 省略部分代码
   
# ......
   
IF(HAVE_SLES_OPENSLES_H)
   
OPTION(ALSOFT_BACKEND_OPENSL    "Enable OpenSL backend" ON)
   
IF(ALSOFT_BACKEND_OPENSL)
   
SET(HAVE_OPENSL 1)
   
IF(HAVE_OPENSL_ANDROID)
   
SET(ALC_OBJS ${ALC_OBJS}    Alc/backends/opensl.cpp Alc/backends/opensl.h)
   
ENDIF()
   
IF(HAVE_OPENSL_OHOS)
   
SET(ALC_OBJS ${ALC_OBJS}    Alc/backends/opensl_ohos.cpp Alc/backends/opensl.h)
   
ENDIF()
   
SET(BACKENDS "${BACKENDS}    OpenSL,")
   
SET(EXTRA_LIBS OpenSLES ${EXTRA_LIBS})
   
ENDIF()
   
ENDIF()
        
   
    3.3 源码调整
c++相关代码调整,主要增加了两个文件opensl_ohos.cpp和OpenSLES_OHOS.h。
其中opensl_ohos.cpp是参照opensl.cpp修改调整。基本上是类名上的调整和小部分的逻辑调整。
可查看适配完的源码自行对比。
OpenSLES_OHOS.h文件是参考opensl.cpp时,发现Android中使用到了
OpenSLES_AndroidConfiguration.h的系统文件,但是鸿蒙中并没有提供类似文件。
针对OpenSLES_AndroidConfiguration.h分析之后发现,该文件中主要时提供一些变量和接口体,并没有接口相关代码,所以尝试参照OpenSLES_AndroidConfiguration.h编写一个OpenSLES_OHOS.h供鸿蒙使用。
由于篇幅关系,此处不展示具体源码调整相关代码,可直接访问openAL-soft:version 1.22.0 support OHOS (gitee.com)获取。
3.4 编译
执行以下两个脚本进行编译即可
     
   
   
# ===============================
   
# 需调整鸿蒙sdk路径及工具链路径
   
# ===============================
   
/home/chenghui/ohos_sdk/10/native/build-tools/cmake/bin/cmake    -GNinja \
   
-DCMAKE_BUILD_TYPE=Release    -DCMAKE_INSTALL_PREFIX=./ \
   
-DCMAKE_TOOLCHAIN_FILE="/home/chenghui/ohos_sdk/10/native/build/cmake/ohos.toolchain.cmake"    -B build
   
   
# ===============================
   
# 需调整鸿蒙sdk路径及工具链路径
   
# ===============================
   
/home/chenghui/ohos_sdk/10/native/build-tools/cmake/bin/ninja    -C build/
   
   
   
4.   编译总结和常见问题
4.1 三方库编译总结
•          分析三方库提供文档分析编译步骤,使用的工具有哪些;
•       分析编译脚本,是否涉及平台相关内容(ABI),是否有其他三方库依赖;
•        分析源码,是否涉及平台相关内容(宏);
•       对比源码中编译Android平台使用的头文件,函数、变量等是否在鸿蒙sdk中是否存在;
•       存在:直接使用同名文件、函数、变量替换;
•       不存在:先了解其功能,分析鸿蒙中是否存在相同功能函数,存在则替换;不存在则可以咨询sdk相关同事如何处理(先注释,或参考Android自行添加);
•       创建deveco项目进行验证或直接带入项目中验证;
4.2 常见问题
Q1.编译过程中找不到符号,找不到文件或是.链接过程中报错符号未定义问题
A:一般是编译脚本中存在某个判断影响到了编译,仔细排查编译脚本或是修改源码处的相关逻辑,避免由于宏相关的判断导致问题。
Q2.Cmake版本是否可以升级
A:可以升级,但是不建议。
Q3.Neon优化相关问题
在HarmonyOS系统中,arm64-v8a ABI下默认已经开启了对Neon扩展的支持
如出现以下报错:
A:处理方法:
可以通过-fno-vectorize关闭,具体参考《Auto-Vectorization in LLVM》。
使用Neon intrinsics库,方便开发者直接操作低阶Neon指令。
Q4._POSIX_VERSION问题
A:_POSIX_VERSION 是一个宏定义,表示当前操作系统的 POSIX 标准版本。POSIX(PortableOperating System Interface,便携式操作系统接口)是一个由 IEEE 定义的标准,旨在提供操作系统的可移植性和兼容性。目前鸿蒙NDK(API9-API12)中_POSIX_VERSION=200809L,有些旧版本三方库中对_POSIX_VERSION的判断比较低,使用了低版本的系统函数导致错误。
Q5.atomic不存在问题
A: 三方库鸿蒙化过程中,有些库用到了libatomic.a但是鸿蒙sdk中并未提供该.a文件。编译时出现以下报错:
ld.lld: error: undefined reference due to--no-allow-shlib-undefined: __atomic_is_lock_free
>>> referenced by ./libcrypto.so
ld.lld: error: undefined reference due to --no-allow-shlib-undefined:__atomic_fetch_add_4
>>> referenced by ./libcrypto.so
查看https://github.com/nodejs/node/issues/28231,尝试连接atomic会出现以下问题
ld.lld: error:unable to find library -latomic
发现找不到libatomic.a或鸿蒙游戏三方库编译适配之C/C++篇
1.   编译适配工具介绍
1.1 CMake构建系统
鸿蒙中提供的编译构建系统为CMake。提供了一系列的脚本文件。在ndk目录(/HarmonyOS-NEXT-DB1/openharmony/native)的build目录中放置预定义的toolchain脚本文件ohos.toolchain.cmake。
                              
CMake编译时需要读取该文件中的默认值,比如编译器架构、C++库链接方式等,因此在编译时会通过CMAKE_TOOLCHAIN_FILE指出该文件的路径,便于CMake在编译时定位到该文件。
build-tools文件夹:放置NDK提供的编译工具。
1.2 编译器
鸿蒙中提供的编译器为Clang+LLVM,在ndk目录的llvm目录中。
Clang和LLVM关系
Clang编译main.c到可执行文件的过程:
   
1.预编译:clang –E main.c –o main_precompile.i
2.词法分析:clang -fmodules -E -Xclang-dump-tokens main.c
3.语法分析:clang -fmodules-fsyntax-only -Xclang -ast-dump main.c
4.中间代码:clang -S -fobjc-arc-emit-llvm main.c -o main.ll
5.汇编:clang -S main.c -omain_compile.s
6.目标程序:clang -fmodules -c main.c -omain_obj.o
7.生成可执行文件:clang main_obj.o -o mainExec
1.3 使用工具链进行编译
工具链的使用基本上可以理解为两个脚本cmake_exec.sh和ninja_exec.sh。
```shell
# 脚本1 cmake_exec.sh
#===============================
#   需调整鸿蒙sdk路径及工具链路径
#===============================
/home/chenghui/ohos_sdk/native/build-tools/cmake/bin/cmake-GNinja \
-DCMAKE_BUILD_TYPE=Release-DCMAKE_INSTALL_PREFIX=./ \
-DCMAKE_TOOLCHAIN_FILE="/home/chenghui/ohos_sdk/native/build/cmake/ohos.toolchain.cmake"-B build
```
cmake_exec.sh编译会生成一些“中间”文件,如调整了CMakeLists.txt文件需要将build文件夹清空,重新执行该脚本,避免文件缓存。若此脚本执行出现错误,一般情况下都是编译脚本的相关问题,主要调整CMakeLists.txt文件。
```shell
# 脚本2 ninja_exec.sh
#===============================
#   需调整鸿蒙sdk路径及工具链路径
#===============================
/home/chenghui/ohos_sdk/native/build-tools/cmake/bin/ninja-C build/
```
ninja_exec.sh需要在cmake_exec.sh脚本正常的前提下运行。如此脚本中执行出现错误,一般需要涉及源码调整。
使用上述两个脚本,基本上可以满足全部的CMakeList.txt编译,然后再针对编译过程中问题进行调整。
注意:Linux环境下,若是在cmake_exec.sh脚本中不指定-G,默认可以使用make工具进行编译。

2.   非CMAKE工程编译适配
C/C++三方库中还有许多的三方库可能需要使用除cmake之外的其他方式进行构建。构建此类三方库一般使用Clang+llvm编译器直接构建,其中可能还存在一些其他脚本,但本质上都是使用编译器,其他脚本作为辅助。
构建此类三方库一般两种方式:使用CMakeLists.txt重写编译脚本或是使用原生构建工具。
2.1 使用CMakeLists.txt重写编译脚本
此过程使用于相对简单的三方库,对于依赖简单,且使用工具单一(较为复杂的库可能还需要使用到python、rust等)的情况可以使用。
如三方库提供Android平台版本,可以参考Android.mk文件进行编写,内容与CMakeLists.txt文件较为相似。
      
   
   
cmake_minimum_required(VERSION    3.6)
   
project(pomelo2)
   
set(POMELO_OBJECT
   
       src/pc_lib.c
   
       src/pc_pomelo.c
   
       src/pc_trans.c
   
    ……
   
       src/tr/uv/pr_msg_pb.c
   
       src/tr/uv/tr_uv_tcp_i.c
   
    ……
   
    )
   
   
set(UV_OBJECT
   
       deps/uv/src/fs-poll.c
   
       deps/uv/src/inet.c
   
       deps/uv/src/threadpool.c
   
    ……
   
)
   
   
add_library(${PROJECT_NAME}    STATIC ${POMELO_OBJECT} ${UV_OBJECT})
   
target_compile_options(${PROJECT_NAME}   
   
       PRIVATE -fPIC  
   
       PRIVATE -Wall
   
       PRIVATE -DPC_NO_UV_TLS_TRANS
   
       PRIVATE -D_GNU_SOURCE
   
    )
   
target_include_directories(${PROJECT_NAME}   
   
       PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/include
   
       PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/src
   
       PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/deps/uv/src
   
       PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/deps/uv/src/unix
   
    )
   
   
   
   
        
   
   
LOCAL_PATH :=    $(call my-dir)
   
include    $(CLEAR_VARS)
   
   
LOCAL_MODULE :=    pomelo_static
   
LOCAL_MODULE_FILENAME    := libpomelo
   
LOCAL_SRC_FILES :=    \
   
src/pc_lib.c \
   
src/pc_pomelo.c \
   
src/pc_trans.c \
   
src/pc_trans_repo.c    \
   
src/tr/dummy/tr_dummy.c    \
   
……
   
src/tr/uv/pr_msg_pb.c    \
   
src/tr/uv/pr_pkg.c    \
   
src/tr/uv/tr_uv_tcp.c    \
   
src/tr/uv/tr_uv_tcp_aux.c    \
   
src/tr/uv/tr_uv_tcp_i.c
   

    LOCAL_CFLAGS := -DPC_NO_UV_TLS_TRANS
   
LOCAL_EXPORT_C_INCLUDES    :=$(LOCAL_PATH)/include
   
LOCAL_C_INCLUDES :=    $(LOCAL_PATH)/include \
   
                       $(LOCAL_PATH)/src \
   
                          $(LOCAL_PATH)/src/tr/dummy \
   
                          $(LOCAL_PATH)/src/tr/uv
   
LOCAL_WHOLE_STATIC_LIBRARIES    := uv_static jansson_static
   
include    $(BUILD_STATIC_LIBRARY)
   
LOCAL_CFLAGS       := -D__ANDROID__
   
$(call    import-module,libpomelo2/deps/uv) \
   
$(call    import-module,libpomelo2/deps/jansson)
   
   
   以下为CMakeLists.txt重写Android.mk示例:2.2 使用原生构建工具
使用原生的构建工具,需要针对环境进行配置,一般建议在linux环境下使用。
注意:linux环境下需要linux版本sdk,且解压时注意使用linux环境下的解压工具解压,避免问题。
环境配置如下:
```shell
exportOHOS_SDK=/home/ohos/tools/OH_SDK/ohos-sdk/linux # 此处替换解压目录
exportAS=${OHOS_SDK}/native/llvm/bin/llvm-as
exportCC="${OHOS_SDK}/native/llvm/bin/clang --target=aarch64-linux-ohos"
exportCXX="${OHOS_SDK}/native/llvm/bin/clang++ --target=aarch64-linux-ohos"
exportLD=${OHOS_SDK}/native/llvm/bin/ld.lld
exportSTRIP=${OHOS_SDK}/native/llvm/bin/llvm-strip
exportRANLIB=${OHOS_SDK}/native/llvm/bin/llvm-ranlib
exportOBJDUMP=${OHOS_SDK}/native/llvm/bin/llvm-objdump
exportOBJCOPY=${OHOS_SDK}/native/llvm/bin/llvm-objcopy
exportNM=${OHOS_SDK}/native/llvm/bin/llvm-nm
exportAR=${OHOS_SDK}/native/llvm/bin/llvm-ar
exportCFLAGS="-fPIC -D__MUSL__=1"
exportCXXFLAGS="-fPIC -D__MUSL__=1"
```
完成环境配置之后,可参照三方库提供文档(readme文件或是其他)按照教程进行编译。
编译过程中可能会涉及一些平台相关信息调整和修改。

3.   编译适配示例
通过对openal三方库鸿蒙化,来加深大家对编译的理解。
openal说明:跨平台音频库,里面包含多种音频处理方式,本示例中仅对opensles进行鸿蒙化处理。
本示例中使用的版本:1.22.0源码地址:https://github.com/kcat/openal-soft
3.1 分析
     
   
   
# ......
   
# 省略部分代码
   
# ......
   
if(ANDROID)
   
# 此处使用日志相关功能,可以替换为鸿蒙hilog
   
# Include liblog for Android logging
   
check_library_exists(log    __android_log_print "" HAVE_LIBLOG)
   
if(HAVE_LIBLOG)
   
set(EXTRA_LIBS log ${EXTRA_LIBS})
   
set(CMAKE_REQUIRED_LIBRARIES    ${CMAKE_REQUIRED_LIBRARIES} log)
   
endif()
   
endif()
   
# ......
   
# 省略部分代码
   
# ......
   
option(ALSOFT_REQUIRE_OPENSL    "Require OpenSL backend" OFF)
   
if(CMAKE_SYSTEM_NAME MATCHES    "ANDROID")
   
# 检查opensles头文件
   
CHECK_INCLUDE_FILES("SLES/OpenSLES.h;SLES/OpenSLES_Android.h"    HAVE_SLES_OPENSLES_H
   
)
   
SET(HAVE_OPENSL_ANDROID 1)
   
endif()
   
   
   该三方库提供CMakeLists.txt脚本,分析脚本中ANDROID相关部分。
     
   
   
# ......
   
# 省略部分代码
   
# ......
   
IF(HAVE_SLES_OPENSLES_H)
   
#    CHECK_SHARED_FUNCTION_EXISTS(slCreateEngine "SLES/OpenSLES.h"    OpenSLES "" HAVE_L
   
IBOPENSLES)
   
# IF(HAVE_LIBOPENSLES)
   
OPTION(ALSOFT_BACKEND_OPENSL    "Enable OpenSL backend" ON)
   
IF(ALSOFT_BACKEND_OPENSL)
   
SET(HAVE_OPENSL 1)
   
IF(HAVE_OPENSL_ANDROID)
   
# 调用opensles代码在Alc/backends/opensl.cpp中
   
SET(ALC_OBJS ${ALC_OBJS}    Alc/backends/opensl.cpp Alc/backends/opensl.
   
h)
   
ENDIF()
   
SET(BACKENDS "${BACKENDS}    OpenSL,")
   
SET(EXTRA_LIBS OpenSLES ${EXTRA_LIBS})
   
ENDIF()
   
# ENDIF()
   
ENDIF()
   
# ......
   
# 省略部分代码
   
# ......
        
   
   
通过源码中提供的CMakeLists.txt中看出,ANDROID平台编译时,主要使用的是opensles和log。 log模块我们可以使用hilog进行替换; opensles这个模块在鸿蒙sdk中也是提供的,也可以尝试替换;
我们先对比一下AndroidNDK中该模块的头文件和鸿蒙sdk中有啥区别 androidNDK版本:25.1.8937393
鸿蒙sdk版本API11
对比内容后发现有鸿蒙上面的版本和androidNDK版本还是有较大差别的。但仍然可以尝试调整。
3.2 编译脚本调整
调整CMakeLists.txt脚本,将ANDROID相关部分使用鸿蒙中能力进行替。
     
   
   
# ......
   
# 省略部分代码
   
# ......
   
if(ANDROID)
   
# Include liblog for Android logging
   
check_library_exists(log    __android_log_print "" HAVE_LIBLOG)
   
if(HAVE_LIBLOG)
   
set(EXTRA_LIBS log ${EXTRA_LIBS})
   
set(CMAKE_REQUIRED_LIBRARIES    ${CMAKE_REQUIRED_LIBRARIES} log)
   
endif()
   
endif()
   
# 新增代码,鸿蒙hilog
   
if(CMAKE_SYSTEM_NAME MATCHES    "OHOS")
   
SET(EXTRA_LIBS libhilog_ndk.z.so    ${EXTRA_LIBS})
   
SET(CMAKE_REQUIRED_LIBRARIES    ${CMAKE_REQUIRED_LIBRARIES} libhilog_ndk.z.so)
   
endif()
   
   
   
     
   
   
# ......
   
# 省略部分代码
   
# ......
   
option(ALSOFT_REQUIRE_OPENSL    "Require OpenSL backend" OFF)
   
if(CMAKE_SYSTEM_NAME MATCHES    "OHOS")
   
CHECK_INCLUDE_FILES("SLES/OpenSLES.h;SLES/OpenSLES_OpenHarmony.h"    HAVE_SLES_OPENSLES
   
_H)
   
SET(HAVE_OPENSL_OHOS 1)
   
else()
   
CHECK_INCLUDE_FILES("SLES/OpenSLES.h;SLES/OpenSLES_Android.h"    HAVE_SLES_OPENSLES_H)
   
SET(HAVE_OPENSL_ANDROID 1)
   
endif()
   
# ......
   
# 省略部分代码
   
# ......
   
IF(HAVE_SLES_OPENSLES_H)
   
OPTION(ALSOFT_BACKEND_OPENSL    "Enable OpenSL backend" ON)
   
IF(ALSOFT_BACKEND_OPENSL)
   
SET(HAVE_OPENSL 1)
   
IF(HAVE_OPENSL_ANDROID)
   
SET(ALC_OBJS ${ALC_OBJS}    Alc/backends/opensl.cpp Alc/backends/opensl.h)
   
ENDIF()
   
IF(HAVE_OPENSL_OHOS)
   
SET(ALC_OBJS ${ALC_OBJS}    Alc/backends/opensl_ohos.cpp Alc/backends/opensl.h)
   
ENDIF()
   
SET(BACKENDS "${BACKENDS}    OpenSL,")
   
SET(EXTRA_LIBS OpenSLES ${EXTRA_LIBS})
   
ENDIF()
   
ENDIF()
        
   
    3.3 源码调整
c++相关代码调整,主要增加了两个文件opensl_ohos.cpp和OpenSLES_OHOS.h。
其中opensl_ohos.cpp是参照opensl.cpp修改调整。基本上是类名上的调整和小部分的逻辑调整。
可查看适配完的源码自行对比。
OpenSLES_OHOS.h文件是参考opensl.cpp时,发现Android中使用到了
OpenSLES_AndroidConfiguration.h的系统文件,但是鸿蒙中并没有提供类似文件。
针对OpenSLES_AndroidConfiguration.h分析之后发现,该文件中主要时提供一些变量和接口体,并没有接口相关代码,所以尝试参照OpenSLES_AndroidConfiguration.h编写一个OpenSLES_OHOS.h供鸿蒙使用。
由于篇幅关系,此处不展示具体源码调整相关代码,可直接访问openAL-soft:version 1.22.0 support OHOS (gitee.com)获取。
3.4 编译
执行以下两个脚本进行编译即可
     
   
   
# ===============================
   
# 需调整鸿蒙sdk路径及工具链路径
   
# ===============================
   
/home/chenghui/ohos_sdk/10/native/build-tools/cmake/bin/cmake    -GNinja \
   
-DCMAKE_BUILD_TYPE=Release    -DCMAKE_INSTALL_PREFIX=./ \
   
-DCMAKE_TOOLCHAIN_FILE="/home/chenghui/ohos_sdk/10/native/build/cmake/ohos.toolchain.cmake"    -B build
   
   
# ===============================
   
# 需调整鸿蒙sdk路径及工具链路径
   
# ===============================
   
/home/chenghui/ohos_sdk/10/native/build-tools/cmake/bin/ninja    -C build/
   
   
   
4.   编译总结和常见问题
4.1 三方库编译总结
•          分析三方库提供文档分析编译步骤,使用的工具有哪些;
•       分析编译脚本,是否涉及平台相关内容(ABI),是否有其他三方库依赖;
•        分析源码,是否涉及平台相关内容(宏);
•       对比源码中编译Android平台使用的头文件,函数、变量等是否在鸿蒙sdk中是否存在;
•       存在:直接使用同名文件、函数、变量替换;
•       不存在:先了解其功能,分析鸿蒙中是否存在相同功能函数,存在则替换;不存在则可以咨询sdk相关同事如何处理(先注释,或参考Android自行添加);
•       创建deveco项目进行验证或直接带入项目中验证;
4.2 常见问题
Q1.编译过程中找不到符号,找不到文件或是.链接过程中报错符号未定义问题
A:一般是编译脚本中存在某个判断影响到了编译,仔细排查编译脚本或是修改源码处的相关逻辑,避免由于宏相关的判断导致问题。
Q2.Cmake版本是否可以升级
A:可以升级,但是不建议。
Q3.Neon优化相关问题
在HarmonyOS系统中,arm64-v8a ABI下默认已经开启了对Neon扩展的支持
如出现以下报错:
A:处理方法:
可以通过-fno-vectorize关闭,具体参考《Auto-Vectorization in LLVM》。
使用Neon intrinsics库,方便开发者直接操作低阶Neon指令。
Q4._POSIX_VERSION问题
A:_POSIX_VERSION 是一个宏定义,表示当前操作系统的 POSIX 标准版本。POSIX(PortableOperating System Interface,便携式操作系统接口)是一个由 IEEE 定义的标准,旨在提供操作系统的可移植性和兼容性。目前鸿蒙NDK(API9-API12)中_POSIX_VERSION=200809L,有些旧版本三方库中对_POSIX_VERSION的判断比较低,使用了低版本的系统函数导致错误。
Q5.atomic不存在问题
A: 三方库鸿蒙化过程中,有些库用到了libatomic.a但是鸿蒙sdk中并未提供该.a文件。编译时出现以下报错:
ld.lld: error: undefined reference due to--no-allow-shlib-undefined: __atomic_is_lock_free
>>> referenced by ./libcrypto.so
ld.lld: error: undefined reference due to --no-allow-shlib-undefined:__atomic_fetch_add_4
>>> referenced by ./libcrypto.so
查看https://github.com/nodejs/node/issues/28231,尝试连接atomic会出现以下问题
ld.lld: error:unable to find library -latomic
发现找不到libatomic.a或是libatomic.so,搜索了一下sdk中确实没有两个库,对比了Android是存在libatomic.a的。
出现上述情况需要替换libclang_rt.builtins.a

5.   已支持鸿蒙系统部分SDK列表
5.1 已支持的部分闭源SDK(需商业授权获取)5.2 已支持的部分开源SDK(可通过开源代码仓获取)5.3 更多支持的SDK列表(持续更新中)
Wwise,CRIWare(ADX/Sofdec),乐变,U8SDK,迅游等是libatomic.so,搜索了一下sdk中确实没有两个库,对比了Android是存在libatomic.a的。
出现上述情况需要替换libclang_rt.builtins.a

5.   已支持鸿蒙系统部分SDK列表
5.1 已支持的部分闭源SDK(需商业授权获取)5.2 已支持的部分开源SDK(可通过开源代码仓获取)5.3 更多支持的SDK列表(持续更新中)
WwiseCRIWareADX/Sofdec),乐变,U8SDK,迅游等

©著作权归作者所有,转载或内容合作请联系作者

您尚未登录,无法参与评论,登录后可以:
参与开源共建问题交流
认同或收藏高质量问答
获取积分成为开源共建先驱

Copyright   ©2023  开发者论坛   |技术支持 Discuz!

返回顶部