HarmonyOS 鸿蒙PC三方库移植:vcpkg方式的 Port 脚本编写简明教程

本文面向鸿蒙三方库移植适配,需要在 vcpkg 中维护或新增端口的开发者,结合通用约定与本仓库中 ports/libpngports/curlports/opensslports/libmediainfoportfile.cmake 里的常见写法,说明如何组织端口脚本、与 vcpkg.json 配合,以及处理特性、平台差异与安装收尾工作。


1. Port 是什么

一个 port 描述「如何从上游源码构建并安装某个库/工具到 vcpkg 的安装树」。每个端口通常包含:

文件 作用
vcpkg.json 端口元数据:名称、版本、描述、依赖、可选 features、平台约束等
portfile.cmake 构建脚本:下载源码、打补丁、调用构建系统、把产物放到 CURRENT_PACKAGES_DIR
补丁、辅助 .cmakeusage 与端口同目录,由 portfile.cmake 引用

portfile.cmakeCMake 脚本模式 下由 vcpkg 执行,可使用 CMake 语法以及 vcpkg 提供的函数(如 vcpkg_from_githubvcpkg_cmake_configure 等)。


2. 执行时可依赖的上下文变量

编写 portfile.cmake 时最常接触的内置变量包括:

  • PORT :当前端口名(与 vcpkg.jsonname 一致)。
  • VERSION :当前要构建的版本字符串(来自 vcpkg.jsonversion / version-date 等)。
  • FEATURES :用户启用的特性列表;可用 "foo" IN_LIST FEATURES 判断。
  • VCPKG_LIBRARY_LINKAGEstaticdynamic,与 triplet 一致。
  • VCPKG_TARGET_IS_WINDOWSVCPKG_TARGET_IS_UWPVCPKG_TARGET_IS_ANDROIDVCPKG_TARGET_IS_OHOS 等:目标平台布尔变量,用于分支逻辑。
  • VCPKG_TARGET_ARCHITECTURE :如 x64arm64arm
  • CURRENT_PACKAGES_DIR :本端口本次安装的目标根目录(installed/<triplet>/ 下对应包目录)。
  • CURRENT_BUILDTREES_DIR:构建树路径,适合放下载的补丁解压物等中间文件。
  • CMAKE_CURRENT_LIST_DIR / CURRENT_PORT_DIR :当前 portfile.cmake 所在端口目录,用于 file(INSTALL ...) 引用同目录下的补丁、usagevcpkg-cmake-wrapper.cmake 等。

libpng 中,会根据 VCPKG_LIBRARY_LINKAGE 设置 PNG_STATIC / PNG_SHARED;在 curl 中会根据 FEATURES 与 Windows 条件追加 Schannel 等选项------这些都是典型的上下文用法。


3. 声明依赖与特性:vcpkg.json

脚本里的逻辑应与 vcpkg.json 一致

  • 普通依赖 列在 dependencies;需要「仅在宿主机上参与配置」的工具链端口可加 "host": true(例如 vcpkg-cmake)。
  • 可选能力 放在 features 里;portfile 里用 vcpkg_check_featuresIN_LIST FEATURES 与 CMake 选项对齐。

libpngapngtoolscurl 的大量 SSL/协议相关 features 都是范例:JSON 声明依赖与描述,portfile.cmake 把 feature 名映射到上游的 -D... 开关。


4. 获取源码

4.1 vcpkg_from_github(最常见)

本仓库中 libpngcurlopenssllibmediainfo 均使用 vcpkg_from_github

  • OUT_SOURCE_PATH SOURCE_PATH :输出源码根路径变量名(惯例写 SOURCE_PATH)。
  • REPO / REF / SHA512:仓库与固定提交/标签及校验和,保证可复现构建。
  • HEAD_REF :供 --head 模式使用。
  • PATCHES :相对于端口目录的补丁列表(.patch / .diff)。

版本字符串处理 :上游标签与 VERSION 不完全一致时,在 portfile 里用 string(REGEX REPLACE ...) 等先算出 REF 再传给 vcpkg_from_githublibmediainfoVERSION 规整为 MEDIAINFO_VERSION 再拼 v${MEDIAINFO_VERSION}curlstring(REPLACE "." "_" ...) 生成 curl-${VERSION} 形式的 ref。

4.2 额外下载与解压(libpng 的 apng)

当某个 feature 需要额外资源时,可:

  1. 使用 vcpkg_download_distfile 下载文件并校验 SHA512
  2. vcpkg_execute_required_process 调用外部命令(如 gzip -d)解压到 CURRENT_BUILDTREES_DIR
  3. vcpkg_from_githubPATCHES 里加入该补丁路径(可为空字符串时需注意条件,本仓库通过变量在 feature 关闭时为空补丁路径的处理方式依端口而定)。

若 Windows 上需要 Unix 工具链,可配合 vcpkg_acquire_msysvcpkg_add_to_pathlibpng 的 apng 流程)。


5. 特性映射:vcpkg_check_features

vcpkg.json 中的 feature 名映射为 CMake 配置选项,推荐统一使用:

cmake 复制代码
vcpkg_check_features(OUT_FEATURE_OPTIONS FEATURE_OPTIONS
    FEATURES
        http2       USE_NGHTTP2
        openssl     CURL_USE_OPENSSL
        openssl     CURL_CA_FALLBACK
    INVERTED_FEATURES
        ldap        CURL_DISABLE_LDAP
)
  • FEATURES :启用某 feature 时,向列表追加 -D<OPTION>=ON(或等价行为,依实现而定)。
  • INVERTED_FEATURES:未启用某 feature 时追加对应选项(如禁用 LDAP)。

OPTIONS 里展开 ${FEATURE_OPTIONS} 传给 vcpkg_cmake_configure(见 curl)。

互斥与组合校验 :复杂端口用 if(...) + message(FATAL_ERROR "...") 明确禁止不支持的组合。curlhttp3 与部分 TLS backend 的组合即如此;openssl 在开头检测已安装的 libressl/boringssl 并直接失败,避免链接冲突。


6. CMake 端口的标准流水线

libmediainfocurllibpng 为代表,典型顺序为:

  1. vcpkg_find_acquire_program (如 PKGCONFIG),必要时 set(ENV{PKG_CONFIG} ...)
  2. vcpkg_cmake_configure
    • SOURCE_PATH 指向上游 含顶层或子目录 CMakeLists.txt 的路径(libmediainfo 使用 "${SOURCE_PATH}/Project/CMake")。
    • OPTIONS / OPTIONS_DEBUG / OPTIONS_RELEASE 传入 -D 变量;可加入 MAYBE_UNUSED_VARIABLES 避免上游未使用某变量时产生警告(libpng)。
  3. vcpkg_cmake_install
  4. vcpkg_cmake_config_fixup :修正 *Config.cmake 安装路径或包名(PACKAGE_NAMECONFIG_PATH)。
  5. vcpkg_fixup_pkgconfig :修正 .pc 文件中的前缀等。
  6. 按需 vcpkg_copy_pdbs (MSVC)、vcpkg_copy_tools (安装可执行文件到 tools/<port>)。
  7. file(REMOVE_RECURSE ...) 删除不需要安装的目录(如重复的 debug/includedebug/share)。
  8. vcpkg_install_copyright :安装许可证文件到 share/<port>/copyright
  9. 可选:安装 usagevcpkg-cmake-wrapper.cmake 以改善 find_package 体验(curllibpng)。

注入额外 CMake 逻辑curl 通过 -DCMAKE_PROJECT_INCLUDE=... 在工程配置阶段包含自定义片段,用于与 vcpkg 环境对齐。


7. 非 CMake 或拆分式 portfileopenssl

openssl 说明了一种常见模式:顶层 portfile.cmake 只做共性逻辑vcpkg_from_github、组装 CONFIGURE_OPTIONS、按 feature 追加 OpenSSL 的 Configure 参数),再通过

cmake 复制代码
include("${CMAKE_CURRENT_LIST_DIR}/windows/portfile.cmake")
# 或
include("${CMAKE_CURRENT_LIST_DIR}/unix/portfile.cmake")

把平台相关的大段步骤拆到子文件,便于维护。

此类端口还会使用 vcpkg_cmake_get_vars + include("${cmake_vars_file}") 读取探测到的编译器信息(如 VCPKG_DETECTED_CMAKE_C_COMPILER_ID),用于决定是否启用某些优化或 OpenSSL 目标三元组(见 openssllibpng 中 ARM/编译器相关分支)。


8. 平台与链接类型分支

典型模式:

  • 静态库使用方式curlVCPKG_LIBRARY_LINKAGE STREQUAL "static" 时改写 curl.h 中的宏,使消费者默认按静态链接语义包含头文件。
  • Windows 与 .pc / 库名libpngcurl 在 Windows 上对 pkgconfig 中的 -l 名称做 vcpkg_replace_string ,区分 debugrelease、以及是否定义 VCPKG_BUILD_TYPE(单一构建类型 triplet)。
  • UWP / Android / OHOS :通过 VCPKG_TARGET_IS_* 关闭不支持的选项或调整编译参数;libpng 在 OHOS 上为 VCPKG_C_FLAGS 追加 --target=... 以配合交叉 sysroot,这是「工具链绕过 CMake 的 execute_process 调用编译器」类问题的典型处理思路。

9. 安装后修补:vcpkg_replace_string 与路径重写

上游生成的脚本或 .pc 常带有绝对路径 ,不利于包可移植性。curlcurl-config 的替换与移动到 tools/${PORT}/bin 是范例:

  • 把安装目录占位符改为 ${prefix} 或基于脚本位置的相对推导;
  • 区分 debugrelease 两套文件;
  • IGNORE_UNCHANGED 避免在路径已替换时失败。

10. 链接方式约束与工具依赖

  • vcpkg_check_linkage(ONLY_STATIC_LIBRARY) :在无法支持动态库的平台上强制静态库(openssl 对 Emscripten)。
  • vcpkg_find_acquire_program(PERL)NASMPKGCONFIG 等:声明构建期可执行文件,由 vcpkg 获取或定位;必要时 vcpkg_add_to_path

11. 调试与质量检查建议

  1. 先最小化 :默认关闭非必要 features,保证基线 vcpkg install <port> 可通过。
  2. 对齐 vcpkg.jsonfeaturesdependenciessupportsportfile 中的 FATAL_ERROR 条件应一致,否则用户会在解析阶段或构建阶段才看到错误。
  3. 补丁 :尽量小、注释清楚 issue/upstream 链接;命名放在端口目录,在 vcpkg_from_github 或等价获取函数中列出。
  4. 版权 :始终 vcpkg_install_copyright ;若项目多许可证(如 curl 额外写入从源文件提取的声明),可组合多个 FILE_LIST 条目。

12. 实战:新增 mediainfo CLI 端口(依赖 libmediainfo

你当前仓库已经有 libmediainfo(库),但没有 mediainfo(命令行工具)。这类"CLI 与库分仓、CLI 依赖库 "是 vcpkg 中很常见的建模场景,推荐单独新建一个 mediainfo port。

12.1 目标与设计

  • 端口拆分原则libmediainfo 负责 SDK/链接库;mediainfo 负责最终可执行程序。
  • 依赖关系mediainfovcpkg.json 依赖 libmediainfo(以及其上游链条,如 libzen)。
  • 安装形态 :CLI 可执行文件应安装到 tools/mediainfo(由 vcpkg_copy_tools 或上游安装路径配合移动完成)。
  • 链接策略 :通常允许 triplet 决定静/动态,不在端口里硬编码;若上游对纯静态有问题,再用 vcpkg_check_linkage 限制。

12.2 新建目录结构

ports 下创建:

text 复制代码
ports/
  mediainfo/
    vcpkg.json
    portfile.cmake
    usage

如果后续需要修补上游构建逻辑,再补充 *.patch / *.diff 文件。

12.3 vcpkg.json 示例(CLI 端口)

下面是一个可作为起点的声明示例(版本号与哈希需按你实际选用的上游 release 调整):

json 复制代码
{
  "name": "mediainfo",
  "version": "24.12",
  "description": "Unified display of technical and tag data for video and audio files",
  "homepage": "https://mediaarea.net/en/MediaInfo",
  "license": "BSD-2-Clause",
  "dependencies": [
    {
      "name": "vcpkg-cmake",
      "host": true
    },
    {
      "name": "vcpkg-cmake-config",
      "host": true
    },
    "libmediainfo"
  ]
}

编写要点:

  • mediainfo 不直接重复声明 libzen,因为它已在 libmediainfo 依赖链里。
  • 若 CLI 仅支持部分平台,可在 supports 字段提前约束(例如排除 uwp)。

12.4 portfile.cmake 示例(重点)

mediainfo CLI 的上游仓库是 MediaArea/MediaInfo(与 libmediainfoMediaInfoLib 不同)。可采用和现有端口一致的 CMake 流水线:

cmake 复制代码
vcpkg_from_github(
    OUT_SOURCE_PATH SOURCE_PATH
    REPO MediaArea/MediaInfo
    REF "v${MEDIAINFO_VERSION}"
    SHA512 <填写实际SHA512>
    HEAD_REF master
    PATCHES
        find-mediainfolib-names.patch
)

vcpkg_cmake_configure(
    SOURCE_PATH "${SOURCE_PATH}/Project/CMake/CLI"
    OPTIONS
        -DMEDIAINFO_CLI_STATIC=OFF
)

vcpkg_cmake_install()
vcpkg_copy_pdbs()
vcpkg_copy_tools(TOOL_NAMES mediainfo AUTO_CLEAN)

file(REMOVE_RECURSE "${CURRENT_PACKAGES_DIR}/debug/include")
file(REMOVE_RECURSE "${CURRENT_PACKAGES_DIR}/debug/share")

file(INSTALL "${CMAKE_CURRENT_LIST_DIR}/usage" DESTINATION "${CURRENT_PACKAGES_DIR}/share/${PORT}")
vcpkg_install_copyright(FILE_LIST "${SOURCE_PATH}/LICENSE")

这段示例体现了几个关键点:

  1. 分仓拉取REPO 必须指向 CLI 仓库而不是 MediaInfoLib
  2. 入口目录正确MediaInfo CLI 的 CMake 入口是 Project/CMake/CLI,不是 Project/CMake
  3. 关闭"源码子工程"静态路径-DMEDIAINFO_CLI_STATIC=OFF,否则会要求 MediaInfoLibMediaInfo 在同一父目录下并排克隆,触发 add_subdirectory ... MediaInfoLib/Project/CMake 不存在。
  4. CMake 包名与文件名不一致 :上游安装的是 MediaInfoLibConfig.cmake ,而 CLI 里写的是 find_package(mediainfolib) ,在区分大小写系统上 CONFIG 查找会失败。本仓库 find-mediainfolib-names.patch 将其改为 find_package(mediainfolib NAMES MediaInfoLib REQUIRED)不要ZenLib 使用 NAMES zenliblibzen 安装目录里仍是 ZenLibConfig.cmakevcpkg_cmake_config_fixup(PACKAGE_NAME zenlib) 主要修正路径与合并 debug/release,并不等于生成 zenlib-config.cmake ),写成 NAMES zenlib 反而会找不到包。ZenLib 保持上游的 find_package(ZenLib REQUIRED) 即可。
  5. 不要在 mediainfoportfile 里向 CURRENT_PACKAGES_DIR 复制 libmediainfo 的 CMake 文件 :配置 mediainfo 时,CURRENT_PACKAGES_DIR 指向 packages/mediainfo_* ,此时还没有、也不应去写 libmediainfo share/mediainfolib/;把复制逻辑放在 ports/libmediainfo/portfile.cmake(或仅用上述补丁)才对。
  6. 慎加 CMAKE_REQUIRE_FIND_PACKAGE_mediainfolib :若与上游 find_package(... REQUIRED) 叠加,vcpkg 会提示 "already called with REQUIRED, thus ... has no effect",且对解决 "找不到 Config" 无帮助。
  7. CLI 安装规范vcpkg_copy_tools(TOOL_NAMES mediainfo ...) 把可执行文件收敛到 tools/${PORT}
  8. 纯工具端口 :一般不需要 vcpkg_cmake_config_fixup()(除非上游安装了需修正的 CMake package)。

许可证文件名以仓库为准(常见为根目录 LICENSELicense.html)。

12.5 usage 示例

ports/mediainfo/usage 可以写成:

txt 复制代码
The package mediainfo provides the command line tool:

    mediainfo <media-file>

Example:
    mediainfo sample.mp4

12.6 验证流程(建议按顺序)

  1. 安装并构建端口
    • vcpkg install mediainfo
  2. 验证可执行文件位置
    • 检查 installed/<triplet>/tools/mediainfo/mediainfo(.exe) 是否存在
  3. 运行命令验证
    • installed/<triplet>/tools/mediainfo/mediainfo --Version
  4. 验证依赖解析
    • 如果配置失败,先看 CMake 日志是否在 find_package(MediaInfoLib) 处报错,再检查 vcpkg.json 依赖声明

12.7 常见坑位与处理

  • add_subdirectory ... MediaInfoLib ... not an existing directory :几乎总是 MEDIAINFO_CLI_STATIC 仍为默认 ON 。在 vcpkg_cmake_configure 里加上 -DMEDIAINFO_CLI_STATIC=OFF ,让工程使用 find_package(mediainfolib)(vcpkg 安装的 libmediainfo),而不是在 buildtree 旁再克隆一份 MediaInfoLib
  • CMAKE_REQUIRE_FIND_PACKAGE_* 与 "already called with REQUIRED"**:对已在 find_package(... REQUIRED)的包再写CMAKE_REQUIRE_FIND_PACKAGE_* 往往只会报警、不能修复缺失的 Config;优先用补丁或 **NAMES` 修正查找名。
  • Could not find a package configuration file provided by "mediainfolib"MediaInfoLib 工程安装的是 MediaInfoLibConfig.cmake ,与 CLI 里 find_package(mediainfolib) 在大小写敏感系统上不匹配。处理:(A) libmediainfo 端口用 configure_file(... COPYONLY) 生成 mediainfolib-config.cmake (本仓库 ports/libmediainfo/portfile.cmake);(B) mediainfo 补丁 find_package(mediainfolib NAMES MediaInfoLib REQUIRED) (本仓库 ports/mediainfo/find-mediainfolib-names.patch)。切勿mediainfoportfile 里向 packages/mediainfo_* 下的 share/mediainfolib 复制------那是错误目录,配置阶段永远找不到 libmediainfo 已安装的文件。
  • Could not find ... zenlibConfig.cmake(包名却写 ZenLib) :多为把 find_package(ZenLib ...) 改成了 NAMES zenliblibzen 仍提供 ZenLibConfig.cmake ,应保留上游 find_package(ZenLib REQUIRED) ,只对 mediainfolib 使用 NAMES MediaInfoLib
  • MediaInfoDLL.hunknown type name 'size_t'(OHOS / musl) :动态链接路径下会包含该头文件;在部分 libc 上 dlfcn.h 不保证 已间接定义 size_t 。本仓库在 libmediainfo 中通过补丁 mediainfodll-include-stddef.patchextern "C" 前加入 #include <stddef.h>
  • 版本标签不匹配 :若上游 tag 不是 v${VERSION},参考 curl / libmediainfo 的做法先做字符串变换再传 REF
  • 二进制名差异 :某些平台产物可能不是 mediainfo,需按实际产物名修改 vcpkg_copy_tools(TOOL_NAMES ...)
  • Windows 调试后缀 :若上游生成 mediainfo_d.exe 等命名,建议在安装后统一命名,减少下游脚本复杂度。
  • 仅工具端口场景 :若端口只提供 CLI,不提供库,可不强制 vcpkg_cmake_config_fixup(),但保留也通常无害(取决于是否安装了 config 文件)。
  • OHOS SDK 的 CMake Deprecation Warning :来自 ohos.toolchain.cmake 里偏旧的 cmake_minimum_required,与 mediainfo 本身无关;升级 Harmony/OpenHarmony NDK/SDK 中的 toolchain 或本地忽略该警告即可。

13. 从本仓库四个端口可归纳的「检查清单」

步骤 libpng curl openssl libmediainfo
获取源码 GitHub + 条件额外下载 GitHub GitHub GitHub
版本/ref 变换 直接使用 v${VERSION} 下划线替换 openssl-${VERSION} 正则修正次版本号
特性 vcpkg_check_features + IN_LIST 大量 feature + 冲突检测 fips / tools 少量 + FIND_PACKAGE 锁定
构建系统 CMake CMake Configure/NMake 等(分平台) CMake(子目录)
收尾 pkgconfig、PDB、可选 tools curl-config、静态头宏 拆分子 portfile + wrapper 模板 pkgconfig debug 后缀
版权 LICENSE COPYING + 额外片段 LICENSE.txt LICENSE

gitcode地址: https://gitcode.com/qq8864/vcpkg

14. 小结

编写 vcpkg 端口脚本的核心是:vcpkg.json 中诚实声明元数据与特性 ,在 portfile.cmake 中把「版本/平台/特性」翻译成上游构建系统能理解的选项 ,并在安装阶段 修正 CMake 配置、pkg-config、脚本路径与版权文件 ,使安装树对下游 CMake/pkg-config 用户一致且可维护。遇到复杂平台或互斥依赖时,尽早 message(FATAL_ERROR)拆分 include() 子文件vcpkg_cmake_get_vars 读取探测结果,与本仓库中成熟端口的写法保持一致,可显著降低维护成本。

最后,欢迎加入开源鸿蒙开发者社区交流:https://harmonypc.csdn.net/

相关推荐
GLAB-Mary2 小时前
华为职业认证新版全景图介绍及重认证规则变更预通知
运维·服务器·华为·华为认证
Ww.xh2 小时前
鸿蒙Flutter混合开发实战:跨平台UI无缝集成
flutter·华为·harmonyos
chenbin___2 小时前
鸿蒙RN position: ‘absolute‘ 和 zIndex 的兼容性问题(转自千问)
前端·javascript·react native·harmonyos
苏杰豪4 小时前
DevEco Studio 启动鸿蒙模拟器提示未开启 Hyper-V,怎么处理?
华为·harmonyos
chenjixue4 小时前
记录下我理解的安卓,鸿蒙,ios, rn , fullter, Jetpack Compose,react 的相似与不同
android·华为·harmonyos
想你依然心痛4 小时前
HarmonyOS 6(API 23)悬浮导航与沉浸光感实战:打造下一代玻璃拟态UI体验
ui·华为·harmonyos·悬浮导航·沉浸光感
yumgpkpm4 小时前
Qwen3.6正式开源,华为昇腾910B实现高效适配
华为·ai作画·stable diffusion·开源·ai写作·llama·gpu算力
廖松洋(Alina)14 小时前
【果一下】鸿蒙APP上架了
华为·harmonyos
Lanren的编程日记15 小时前
Flutter鸿蒙应用开发:生物识别(指纹/面容)功能集成实战
flutter·华为·harmonyos