背景与动机
在鸿蒙(OpenHarmony / HarmonyOS NEXT)生态持续扩张的背景下,越来越多的开发者需要将现有的 C/C++ 开源库移植到鸿蒙平台。然而鸿蒙的 NDK 工具链与传统 Linux / Android 存在若干关键差异,其中最容易踩坑的就是 共享库 SONAME 格式。
本文基于 Opus 音频编解码库 的鸿蒙适配实践,参考官方文档,系统梳理从工具链配置、CMake 工程改造到最终产物验证的完整流程,帮助开发者少走弯路。
环境准备
1. 安装 OpenHarmony SDK / Native SDK
前往 华为鸿蒙开发者下载中心下载对应版本的 SDK(已配置的请忽略),解压后得到如下结构:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| ohos-sdk/
└── openharmony/
└── native/
├── build/
│ └── cmake/
│ └── ohos.toolchain.cmake # ⭐ 工具链文件
├── build-tools/
│ └── cmake/
│ └── bin/
│ └── cmake # ⭐ 配套 CMake
├── llvm/ # Clang/LLVM 编译器
└── sysroot/ # 目标系统根目录
|
2. 配置环境变量
1 2 3 4 5
|
export OHOS_SDK_PATH="$HOME/ohos-sdk/openharmony"
export PATH="$OHOS_SDK_PATH/native/build-tools/cmake/bin:$PATH"
|
3. 验证工具链
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
|
cmake --version
ls "$OHOS_SDK_PATH/native/build/cmake/ohos.toolchain.cmake"
ls "$OHOS_SDK_PATH/native/llvm/bin/clang"
|
4. 支持的目标架构
| 架构标识 |
适用场景 |
说明 |
arm64-v8a |
真机(主流) |
64-bit ARMv8,当前主力架构 |
armeabi-v7a |
真机(旧设备) |
32-bit ARM,兼容老款设备 |
x86_64 |
模拟器 |
DevEco Studio 模拟器调试用 |
核心概念:SONAME 规范差异
这是移植过程中最关键、最容易踩坑的地方,务必理解。
什么是 SONAME?
SONAME(Shared Object NAME)是 ELF 共享库的一个元数据字段,动态链接器在运行时通过它查找并加载库文件。
Linux 标准行为 vs OpenHarmony 要求
| 平台 |
SONAME 格式 |
示例 |
| 标准 Linux |
带主版本号 |
libopus.so.0 |
| Android |
不带版本号 |
libopus.so |
| OpenHarmony |
不带版本号 |
libopus.so |
为什么不一致会出问题?
1 2 3 4 5 6 7 8 9
| # 错误情形:SONAME 为 libopus.so.0
应用运行 → dlopen("libopus.so") → 动态链接器查找 SONAME=libopus.so.0 的库 → 失败!
# 正确情形:SONAME 为 libopus.so
应用运行 → dlopen("libopus.so") → 动态链接器查找 SONAME=libopus.so 的库 → 成功✓
|
结论: 为 OpenHarmony 构建的共享库,SONAME 必须设置为不带版本号的形式,即 libxxx.so。
CMakeLists.txt 配置详解
完整配置模板
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145
| cmake_minimum_required(VERSION 3.16)
project(MyLib VERSION 1.2.3 LANGUAGES C CXX)
option(BUILD_SHARED_LIBS "Build shared libraries" ON)
option(MYLIB_BUILD_SHARED "Build MyLib as shared library" ON)
if(MYLIB_BUILD_SHARED OR BUILD_SHARED_LIBS)
set(BUILD_SHARED_LIBS ON)
set(MYLIB_BUILD_SHARED ON)
endif()
add_library(mylib
src/core.c
src/encoder.c
src/decoder.c
)
add_library(MyLib::mylib ALIAS mylib)
target_include_directories(mylib
PUBLIC
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}/mylib>
PRIVATE
${CMAKE_CURRENT_BINARY_DIR}
${CMAKE_CURRENT_SOURCE_DIR}/src
)
set_target_properties(mylib PROPERTIES
VERSION ${PROJECT_VERSION}
SOVERSION ${PROJECT_VERSION_MAJOR}
)
target_link_options(mylib PRIVATE "SHELL:-Wl,-soname,libmylib.so")
if(BUILD_SHARED_LIBS)
set_target_properties(mylib PROPERTIES
C_VISIBILITY_PRESET hidden
CXX_VISIBILITY_PRESET hidden
VISIBILITY_INLINES_HIDDEN YES
)
if(WIN32)
target_compile_definitions(mylib PRIVATE MYLIB_DLL_EXPORT)
endif()
endif()
target_compile_definitions(mylib PRIVATE MYLIB_BUILD)
target_link_libraries(mylib PRIVATE m)
include(GNUInstallDirs)
install(TARGETS mylib
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/mylib
)
install(DIRECTORY include/mylib
DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}
)
|
关键配置项说明
target_link_options 与 SONAME
这是整个移植过程的核心,单独拆解说明:
1 2 3 4 5 6 7
|
target_link_options(mylib PRIVATE "SHELL:-Wl,-soname,libmylib.so")
|
⚠️ 注意: 如果项目使用旧式 set_target_properties 加 LINK_FLAGS,需改为 target_link_options 以获得更好的 CMake 现代语法支持。
符号导出宏(推荐在公共头文件中定义)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
|
#pragma once
#ifdef _WIN32
# ifdef MYLIB_BUILD
# define MYLIB_API __declspec(dllexport)
# else
# define MYLIB_API __declspec(dllimport)
# endif
#else
# define MYLIB_API __attribute__((visibility("default")))
#endif
|
在需要导出的函数声明前使用:
1 2 3
| MYLIB_API int mylib_encode(const float* pcm, int frame_size, unsigned char* data);
MYLIB_API int mylib_decode(const unsigned char* data, int len, float* pcm);
|
构建脚本编写
完整构建脚本:build/build_ohos.sh
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343
| #!/bin/bash
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PROJECT_ROOT="$(dirname "$SCRIPT_DIR")"
if [[ -n "${OHOS_SDK_PATH:-}" ]]; then
echo "[INFO] 使用环境变量 OHOS_SDK_PATH: $OHOS_SDK_PATH"
elif [[ $# -eq 1 ]]; then
OHOS_SDK_PATH="$1"
echo "[INFO] 使用命令行参数: $OHOS_SDK_PATH"
else
echo "[ERROR] 未提供 OpenHarmony SDK 路径"
echo " 用法 1: export OHOS_SDK_PATH=/path/to/sdk && ./build_ohos.sh"
echo " 用法 2: ./build_ohos.sh /path/to/sdk"
exit 1
fi
CMAKE_BIN="$OHOS_SDK_PATH/native/build-tools/cmake/bin/cmake"
TOOLCHAIN_FILE="$OHOS_SDK_PATH/native/build/cmake/ohos.toolchain.cmake"
check_prerequisites() {
local missing=0
if [[ ! -f "$CMAKE_BIN" ]]; then
echo "[ERROR] CMake 未找到: $CMAKE_BIN"
missing=1
fi
if [[ ! -f "$TOOLCHAIN_FILE" ]]; then
echo "[ERROR] 工具链文件未找到: $TOOLCHAIN_FILE"
missing=1
fi
if [[ $missing -ne 0 ]]; then
echo "[ERROR] 请检查 SDK 路径是否正确,或重新下载 SDK"
exit 1
fi
echo "[INFO] 工具链检查通过 ✓"
}
build_arch() {
local arch="$1"
local build_dir="$SCRIPT_DIR/${arch}_harmony_build"
echo ""
echo "════════════════════════════════════════"
echo " 编译架构: $arch"
echo "════════════════════════════════════════"
rm -rf "$build_dir"
mkdir -p "$build_dir"
"$CMAKE_BIN" "$PROJECT_ROOT" \
-B "$build_dir" \
-DCMAKE_TOOLCHAIN_FILE="$TOOLCHAIN_FILE" \
-DCMAKE_BUILD_TYPE=Release \
-DBUILD_SHARED_LIBS=ON \
-DMYLIB_BUILD_SHARED=ON \
-DOHOS_ARCH="$arch" \
-DCMAKE_INSTALL_PREFIX="$SCRIPT_DIR/${arch}_standard_libs"
local jobs
jobs=$(sysctl -n hw.logicalcpu 2>/dev/null || nproc 2>/dev/null || echo 4)
cmake --build "$build_dir" --config Release --parallel "$jobs"
echo "[INFO] $arch 编译完成 ✓"
}
organize_output() {
local arch="$1"
local build_dir="$SCRIPT_DIR/${arch}_harmony_build"
local output_dir="$SCRIPT_DIR/${arch}_standard_libs"
echo ""
echo "[INFO] 整理 $arch 输出文件..."
rm -rf "$output_dir"
mkdir -p "$output_dir"/{lib,include}
cmake --install "$build_dir" --prefix "$output_dir"
local so_file
so_file=$(find "$output_dir" -name "*.so" | head -1)
if [[ -z "$so_file" ]]; then
echo "[WARN] 未在 $output_dir 中找到 .so 文件,尝试手动复制"
find "$build_dir" -name "*.so" -exec cp {} "$output_dir/lib/" \;
fi
echo "[INFO] $arch 输出目录结构:"
find "$output_dir" -type f | sort | sed 's|^| |'
echo "[INFO] 整理完成 ✓"
}
verify_soname() {
local arch="$1"
local output_dir="$SCRIPT_DIR/${arch}_standard_libs"
echo ""
echo "[INFO] 验证 $arch SONAME..."
local so_files
mapfile -t so_files < <(find "$output_dir" -name "*.so")
if [[ ${#so_files[@]} -eq 0 ]]; then
echo "[WARN] 未找到 .so 文件,跳过验证"
return
fi
for so in "${so_files[@]}"; do
local soname
if command -v readelf &>/dev/null; then
soname=$(readelf -d "$so" 2>/dev/null | grep SONAME | awk -F'[][]' '{print $2}')
elif command -v objdump &>/dev/null; then
soname=$(objdump -p "$so" 2>/dev/null | awk '/SONAME/{print $2}')
else
echo "[WARN] 未找到 readelf/objdump,跳过 SONAME 验证"
return
fi
if [[ -z "$soname" ]]; then
echo "[WARN] $so → SONAME 字段为空"
elif [[ "$soname" =~ \.so\.[0-9] ]]; then
echo "[ERROR] $so → SONAME=$soname ← 带版本号,鸿蒙不兼容!"
else
echo "[OK] $so → SONAME=$soname ✓"
fi
done
}
main() {
check_prerequisites
local archs=("arm64-v8a" "x86_64")
for arch in "${archs[@]}"; do
build_arch "$arch"
organize_output "$arch"
verify_soname "$arch"
done
echo ""
echo "════════════════════════════════════════"
echo " 全部架构构建完成!"
echo "════════════════════════════════════════"
echo " arm64-v8a (真机): $SCRIPT_DIR/arm64-v8a_standard_libs/"
echo " x86_64 (模拟器): $SCRIPT_DIR/x86_64_standard_libs/"
}
main "$@"
|
完整示例:从零适配一个 C++ 库
以一个假设的音频处理库 libmycodec 为例,演示完整适配步骤。
步骤 1:确认项目结构
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| mycodec/
├── CMakeLists.txt
├── include/
│ └── mycodec/
│ ├── mycodec.h
│ └── export.h
├── src/
│ ├── encoder.c
│ └── decoder.c
└── build/
└── build_ohos.sh
|
步骤 2:修改 CMakeLists.txt
在已有 CMakeLists.txt 的基础上,只需添加下面两处改动:
1 2 3 4 5 6 7 8 9 10 11
|
option(BUILD_SHARED_LIBS "Build shared libraries" ON)
target_link_options(mycodec PRIVATE "SHELL:-Wl,-soname,libmycodec.so")
|
步骤 3:创建构建脚本
将上方构建脚本保存为 build/build_ohos.sh,并将脚本内的 MYLIB_BUILD_SHARED 替换为你的项目选项名。
1
| chmod +x build/build_ohos.sh
|
步骤 4:执行构建
1 2 3 4 5 6 7 8 9 10 11
|
export OHOS_SDK_PATH="/Users/yourname/ohos-sdk/openharmony"
./build/build_ohos.sh
./build/build_ohos.sh /Users/yourname/ohos-sdk/openharmony
|
构建日志示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| [INFO] 工具链检查通过 ✓
════════════════════════════════════════
编译架构: arm64-v8a
════════════════════════════════════════
-- The C compiler identification is Clang 15.0.4
-- Detecting C compiler ABI info
-- Check for working C compiler: .../llvm/bin/clang - works
...
[INFO] arm64-v8a 编译完成 ✓
[INFO] 整理 arm64-v8a 输出文件...
[INFO] arm64-v8a 输出目录结构:
build/arm64-v8a_standard_libs/lib/libmycodec.so
build/arm64-v8a_standard_libs/include/mycodec/mycodec.h
[OK] .../libmycodec.so → SONAME=libmycodec.so ✓
|
步骤 5:检查输出产物
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| build/
├── arm64-v8a_harmony_build/ # 编译中间产物
├── arm64-v8a_standard_libs/ # ⭐ 真机用交付物
│ ├── lib/
│ │ └── libmycodec.so
│ └── include/
│ └── mycodec/
│ └── mycodec.h
├── x86_64_harmony_build/
└── x86_64_standard_libs/ # ⭐ 模拟器用交付物
├── lib/
│ └── libmycodec.so
└── include/
└── mycodec/
|
验证与调试
1. 验证 SONAME(最重要)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
|
readelf -d build/arm64-v8a_standard_libs/lib/libmycodec.so | grep SONAME
objdump -p build/arm64-v8a_standard_libs/lib/libmycodec.so | grep SONAME
|
2. 查看导出符号表
1 2 3 4 5 6 7 8 9 10 11 12 13
|
nm -D build/arm64-v8a_standard_libs/lib/libmycodec.so | grep " T "
|
3. 检查依赖关系
1 2 3 4 5 6 7 8 9 10 11
|
readelf -d libmycodec.so | grep NEEDED
|
4. 检查目标架构匹配
1 2 3 4 5 6 7 8 9
| file build/arm64-v8a_standard_libs/lib/libmycodec.so
file build/x86_64_standard_libs/lib/libmycodec.so
|
项目集成
将编译好的 .so 文件集成到鸿蒙应用工程(DevEco Studio)。
目录结构
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| MyHarmonyApp/
└── entry/
└── src/
└── main/
├── cpp/
│ ├── CMakeLists.txt # 应用层 CMake
│ ├── napi_init.cpp # NAPI 胶水代码
│ └── libs/
│ ├── arm64-v8a/
│ │ └── libmycodec.so
│ └── x86_64/
│ └── libmycodec.so
└── ets/
└── pages/
|
应用层 CMakeLists.txt
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47
| cmake_minimum_required(VERSION 3.16)
project(MyHarmonyApp)
set(LIB_DIR ${CMAKE_CURRENT_SOURCE_DIR}/libs/${OHOS_ARCH})
add_library(mycodec SHARED IMPORTED)
set_target_properties(mycodec PROPERTIES
IMPORTED_LOCATION "${LIB_DIR}/libmycodec.so"
)
add_library(myapp SHARED napi_init.cpp)
target_include_directories(myapp PRIVATE
${CMAKE_CURRENT_SOURCE_DIR}/../../libs/include
)
target_link_libraries(myapp PRIVATE
mycodec
libace_napi.z.so
libhilog_ndk.z.so
)
|
module.json5 配置
1 2 3 4 5 6 7 8 9 10 11 12 13
| {
"module": {
"requestPermissions": [],
"deviceTypes": ["default", "tablet"],
"abilities": [...]
}
}
|
build-profile.json5 配置(确保 .so 打包入 hap)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| {
"buildOption": {
"externalNativeOptions": {
"path": "./src/main/cpp/CMakeLists.txt",
"arguments": "",
"cppFlags": ""
}
}
}
|
常见问题与排查
Q1:运行时找不到库,报 dlopen failed
症状: 应用崩溃,日志显示 cannot locate symbol 或 No such file
排查步骤:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
|
readelf -d libmycodec.so | grep SONAME
unzip MyApp.hap -d hap_contents
ls hap_contents/libs/arm64-v8a/
|
Q2:编译报 undefined reference to 'xxx'
原因: 缺少必要的链接依赖
1 2 3 4 5 6 7 8 9 10 11
|
target_link_libraries(mycodec PRIVATE
m
pthread
dl
)
|
Q3:头文件找不到
1 2 3 4 5 6 7 8 9
|
target_include_directories(mycodec
PUBLIC
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
)
|
Q4:工具链找不到 OHOS SDK
1 2 3 4 5 6 7 8 9 10 11 12 13
|
ls "$OHOS_SDK_PATH/native/build/cmake/ohos.toolchain.cmake"
ls "$OHOS_SDK_PATH/native/build-tools/cmake/bin/cmake"
find "$OHOS_SDK_PATH" -name "ohos.toolchain.cmake" 2>/dev/null
|
Q5:arm64-v8a 和 x86_64 的库是否可以混用?
不可以。 架构完全不同,必须分别编译、分别打包。DevEco Studio 会根据运行环境自动选择正确的架构库。
Q6:如何支持 armeabi-v7a(兼容旧设备)?
在构建脚本中增加该架构:
1 2 3 4 5
| build_arch "armeabi-v7a"
organize_output "armeabi-v7a"
verify_soname "armeabi-v7a"
|
同时在应用工程的 libs/ 目录下新增 armeabi-v7a/ 子目录。
最佳实践
1. 最小化公共头文件暴露
只在公共 API 头文件中使用 MYLIB_API 宏标记需要导出的符号,配合 C_VISIBILITY_PRESET hidden 默认隐藏内部符号:
1 2 3 4 5 6 7 8 9
| set_target_properties(mylib PROPERTIES
C_VISIBILITY_PRESET hidden
CXX_VISIBILITY_PRESET hidden
VISIBILITY_INLINES_HIDDEN YES
)
|
好处: 减少库体积、降低符号冲突风险、保护内部实现。
2. Release 构建优化
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| if(CMAKE_BUILD_TYPE STREQUAL "Release")
target_compile_options(mylib PRIVATE
-O3
-ffast-math
-fomit-frame-pointer
)
set_target_properties(mylib PROPERTIES
INTERPROCEDURAL_OPTIMIZATION TRUE
)
endif()
|
3. 版本管理:生成版本头文件
1 2 3 4 5 6 7 8 9
|
configure_file(
${CMAKE_CURRENT_SOURCE_DIR}/include/mylib/version.h.in
${CMAKE_CURRENT_BINARY_DIR}/include/mylib/version.h
)
|
1 2 3 4 5 6 7 8 9
|
#define MYLIB_VERSION_MAJOR @PROJECT_VERSION_MAJOR@
#define MYLIB_VERSION_MINOR @PROJECT_VERSION_MINOR@
#define MYLIB_VERSION_PATCH @PROJECT_VERSION_PATCH@
#define MYLIB_VERSION_STRING "@PROJECT_VERSION@"
|
4. CI/CD 自动化配置(GitHub Actions 示例)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59
|
name: Build for OpenHarmony
on: [push, pull_request]
jobs:
build-ohos:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Download OHOS SDK
run: |
wget -q https://your-sdk-mirror/ohos-sdk.tar.gz
tar -xzf ohos-sdk.tar.gz -C $HOME
- name: Build
env:
OHOS_SDK_PATH: ${{ github.workspace }}/ohos-sdk/openharmony
run: |
chmod +x build/build_ohos.sh
./build/build_ohos.sh
- name: Upload artifacts
uses: actions/upload-artifact@v4
with:
name: ohos-libs
path: |
build/arm64-v8a_standard_libs/
build/x86_64_standard_libs/
|
5. 静态库 + 共享库同时输出
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
|
add_library(mylib_static STATIC ${SOURCES})
set_target_properties(mylib_static PROPERTIES OUTPUT_NAME mylib)
add_library(mylib_shared SHARED ${SOURCES})
set_target_properties(mylib_shared PROPERTIES
OUTPUT_NAME mylib
VERSION ${PROJECT_VERSION}
SOVERSION ${PROJECT_VERSION_MAJOR}
)
target_link_options(mylib_shared PRIVATE "SHELL:-Wl,-soname,libmylib.so")
|
参考资料
许可证: 本文档采用 CC BY 4.0 协议授权,转载请注明来源。