OpenHarmony开发者论坛

标题: 游戏三方库编译适配之C/C++篇(附:部分适配SDK列表) [打印本页]

作者: yanglei60    时间: 2024-7-30 02:13
标题: 游戏三方库编译适配之C/C++篇(附:部分适配SDK列表)
[md]## 1 编译适配工具介绍

### 1\.1 CMake构建系统

OpenHarmony提供的编译构建系统为CMake。提供了一系列的脚本文件。在ndk目录(openharmony/native)的build目录中放置预定义的toolchain脚本文件ohos.toolchain.cmake。

![1.1-1.png](https://forums-obs.openharmony.c ... nf5nfps9o35aorb.png "1.1-1.png")

CMake编译时需要读取该文件中的默认值,比如编译器架构、C++库链接方式等,因此在编译时会通过CMAKE\_TOOLCHAIN\_FILE指出该文件的路径,便于CMake在编译时定位到该文件。

build-tools文件夹:放置NDK提供的编译工具。

### 1\.2 编译器

OpenHarmony工具链提供的编译器为Clang+LLVM,在ndk目录的llvm目录中。

![1.2-1.png](https://forums-obs.openharmony.c ... li94ien5yyye4xv.png "1.2-1.png")

Clang编译main.c到可执行文件的过程:

![1.2-2.png](https://forums-obs.openharmony.c ... att5xllppl6llz4.png "1.2-2.png")

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 -o main\_compile.s

6\.目标程序:clang -fmodules -c main.c -o main\_obj.o

7\.生成可执行文件:clang main\_obj.o -o mainExec

### 1\.3 使用工具链进行编译

工具链的使用基本上可以理解为两个脚本cmake\_exec.sh和ninja\_exec.sh。

```
# 脚本1 cmake_exec.sh
# ===============================
#    需调整OpenHarmony 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文件。

```
# 脚本2 ninja_exec.sh
# ===============================
#    需调整OpenHarmony 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文件较为相似。

以下为CMakeLists.txt重写Android.mk示例:

```
\  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)
```

```
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
    )

```

### 2\.2 使用原生构建工具

使用原生的构建工具,需要针对环境进行配置,一般建议在linux环境下使用。
注意:linux环境下需要linux版本sdk,且解压时注意使用linux环境下的解压工具解压,避免问题。

环境配置如下:

```
export OHOS\_SDK=/home/ohos/tools/OH\_SDK/ohos-sdk/linux # 此处替换解压目录
export AS=${OHOS\_SDK}/native/llvm/bin/llvm-as
export CC="${OHOS\_SDK}/native/llvm/bin/clang --target=aarch64-linux-ohos"
export CXX="${OHOS\_SDK}/native/llvm/bin/clang++ --target=aarch64-linux-ohos"
export LD=${OHOS\_SDK}/native/llvm/bin/ld.lld
export STRIP=${OHOS\_SDK}/native/llvm/bin/llvm-strip
export RANLIB=${OHOS\_SDK}/native/llvm/bin/llvm-ranlib
export OBJDUMP=${OHOS\_SDK}/native/llvm/bin/llvm-objdump
export OBJCOPY=${OHOS\_SDK}/native/llvm/bin/llvm-objcopy
export NM=${OHOS\_SDK}/native/llvm/bin/llvm-nm
export AR=${OHOS\_SDK}/native/llvm/bin/llvm-ar
export CFLAGS="-fPIC -D\_\_MUSL\_\_=1"export CXXFLAGS="-fPIC -D\_\_MUSL\_\_=1"
```

完成环境配置之后,可参照三方库提供文档(readme文件或是其他)按照教程进行编译。

编译过程中可能会涉及一些平台相关信息调整和修改。

## 3 编译适配示例

通过对openal三方库,来加深大家对编译的理解。

openal说明:跨平台音频库,里面包含多种音频处理方式,本示例中仅对opensles进行适配。

本示例中使用的版本:1.22.0 源码地址:[https://github.com/kcat/openal-soft](https://github.com/kcat/openal-soft)。

### 3\.1 分析

该三方库提供CMakeLists.txt脚本,分析脚本中ANDROID相关部分。

```
# ......
# 省略部分代码
# ......
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()

# ......
# 省略部分代码
# ......
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这个模块在OpenHarmony sdk中也是提供的,也可以尝试替换;

我们先对比一下AndroidNDK中该模块的头文件和OpenHarmony sdk中有何区别:

* androidNDK版本:25.1.8937393

  ![3.1-1.png](https://forums-obs.openharmony.c ... n4et464geomg4jn.png "3.1-1.png")
* OpenHarmony sdk版本:API 11

  ![3.1-2.png](https://forums-obs.openharmony.c ... fkxfy6kuhlupukf.png "3.1-2.png")

对比内容后发现有OpenHarmony SDK与Android NDK有较大差别的。但仍然可以尝试调整。

### 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的系统文件,但是OpenHarmoy中并没有提供类似文件。

针对OpenSLES\_AndroidConfiguration.h分析之后发现,该文件中主要时提供一些变量和接口体,并没有接口相关代码,所以尝试参照OpenSLES\_AndroidConfiguration.h编写一个OpenSLES\_OHOS.h供OpenHarmony使用。
由于篇幅关系,此处不展示具体源码调整相关代码,可直接访问[openAL-soft: version 1.22.0 support OHOS (gitee.com)](https://gitee.com/WayneHalak/open-al-soft)获取。

### 3\.4 编译

执行以下脚本进行编译即可

```
# ===============================
# 需调整OpenHarmony 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

# ===============================
# 需调整OpenHarmony sdk路径及工具链路径
# ===============================
/home/chenghui/ohos_sdk/10/native/build-tools/cmake/bin/ninja -C build/

```

## 4 编译总结和常见问题

### 4\.1 三方库编译总结

- 分析三方库提供文档分析编译步骤,使用的工具有哪些;
- 分析编译脚本,是否涉及平台相关内容(ABI),是否有其他三方库依赖;
- 分析源码,是否涉及平台相关内容(宏);
- 对比源码中编译Android平台使用的头文件,函数、变量等是否在OpenHarmony sdk中是否存在;
- 如存在:直接使用同名文件、函数、变量替换;
- 不存在:先了解其功能,分析OpenHarmony中是否存在相同功能函数,存在则替换;不存在则可以咨询sdk相关同事如何处理(先注释,或参考Android自行添加);
- 创建deveco项目进行验证或直接带入项目中验证;

### 4\.2 常见问题

* Q1.编译过程中找不到符号,找不到文件或是.链接过程中报错符号未定义问题

A:一般是编译脚本中存在某个判断影响到了编译,仔细排查编译脚本或是修改源码处的相关逻辑,避免由于宏相关的判断导致问题。

* Q2.Cmake版本是否可以升级

A:可以升级,但是不建议。

* Q3.Neon优化相关问题

在HarmonyOS系统中,arm64-v8a ABI下默认已经开启了对Neon扩展的支持

如出现以下报错:

![4.2-1.png](https://forums-obs.openharmony.c ... kjmkwzw878ermk3.png "4.2-1.png")

A:处理方法:

可以通过-fno-vectorize关闭,具体参考《Auto-Vectorization in LLVM》。

使用Neon intrinsics库,方便开发者直接操作低阶Neon指令。

详细参考:[使用](https://developer.huawei.com/con ... de-0000001820880473)[Neon](https://developer.huawei.com/con ... de-0000001820880473)[指令扩展](https://developer.huawei.com/con ... de-0000001820880473)[-](https://developer.huawei.com/con ... de-0000001820880473)[硬件兼容性](https://developer.huawei.com/con ... de-0000001820880473)[-NDK](https://developer.huawei.com/con ... de-0000001820880473)[开发](https://developer.huawei.com/con ... de-0000001820880473)[-](https://developer.huawei.com/con ... de-0000001820880473)[开发 ](https://developer.huawei.com/con ... de-0000001820880473)[| ](https://developer.huawei.com/con ... de-0000001820880473)[华为开发者联盟 ](https://developer.huawei.com/con ... de-0000001820880473)[(huawei.com)](https://developer.huawei.com/con ... de-0000001820880473)

* Q4.\_POSIX\_VERSION问题

A: \_POSIX\_VERSION 是一个宏定义,表示当前操作系统的 POSIX 标准版本。POSIX(Portable Operating System Interface,便携式操作系统接口)是一个由 IEEE 定义的标准,旨在提供操作系统的可移植性和兼容性。目前OpenHarmony NDK(API9-API11)中\_POSIX\_VERSION=200809L,有些旧版本三方库中对\_POSIX\_VERSION的判断比较低,使用了低版本的系统函数导致错误。

* Q5.atomic不存在问题

A: 三方库适配过程中,有些库用到了libatomic.a但是OpenHarmony 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(需商业授权获取)

FMOD:[https://fmod.com/](https://fmod.com/)

### 5\.2 开源SDK(可通过开源代码仓获取)

HybridCLR:[https://github.com/focus-creative-games/hybridclr](https://github.com/focus-creative-games/hybridclr)

Solar2D:[https://gitee.com/fengozl/corona/](https://gitee.com/fengozl/corona/)

V8 Engine:[https://github.com/dumganhar/v8]()

PuerTs:[https://github.com/Tencent/puerts]()
[/md]




欢迎光临 OpenHarmony开发者论坛 (https://forums.openharmony.cn/) Powered by Discuz! X3.5