首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >libtool 适配 HarmonyOS PC 完整踩坑指南

libtool 适配 HarmonyOS PC 完整踩坑指南

作者头像
禁默
发布2025-12-24 15:37:16
发布2025-12-24 15:37:16
1490
举报

libtool 适配 HarmonyOS PC 完整踩坑指南

概述

GNU Libtool 是一个通用的库支持脚本,用于简化在不同平台上构建和使用共享库的过程。本文将详细介绍将 libtool 2.4.10 适配到 HarmonyOS PC 平台的完整过程,特别是遇到的各类问题和解决方案。

项目背景

  • 项目名称: GNU Libtool
  • 版本: 2.4.10
  • 构建系统: Autotools (autoconf, automake, libtool)
  • 目标平台: HarmonyOS PC (aarch64-linux-gnu)
  • 交叉编译工具链: HarmonyOS SDK Native LLVM

环境准备

必需工具
代码语言:javascript
复制
# macOS 上安装必需工具
brew install m4 autoconf automake libtool help2man texinfo

# 注意:m4 版本要求
# libtool 的 bootstrap 脚本对 m4 版本有严格要求
# 必须使用 Homebrew 的 m4,而不是系统的 gm4
export M4="/opt/homebrew/opt/m4/bin/m4"
export PATH="/opt/homebrew/opt/m4/bin:${PATH}"
HarmonyOS SDK 配置
代码语言:javascript
复制
export SDK_PATH="/path/to/ohosdk"
export CC="${SDK_PATH}/native/llvm/bin/clang"
export CXX="${SDK_PATH}/native/llvm/bin/clang++"
export SYSROOT="${SDK_PATH}/native/sysroot"

主要踩坑经历

坑 1: gnulib 初始化失败

问题描述: libtool 依赖 gnulib(GNU 可移植性库),需要通过 bootstrap 脚本初始化。首次运行 bootstrap 时,出现以下错误:

代码语言:javascript
复制
bootstrap: error: Prerequisite 'help2man' not found.
bootstrap: error: Prerequisite 'makeinfo' not found.

根本原因: bootstrap 脚本会检查 help2manmakeinfo 工具是否存在,但这些工具在交叉编译环境中不是必需的,只是用于生成文档。

解决方案: 创建临时的包装脚本来满足 bootstrap 的检查:

代码语言:javascript
复制
# 创建临时目录存放包装脚本
BOOTSTRAP_BIN_DIR="$(pwd)/.bootstrap_bin"
mkdir -p "${BOOTSTRAP_BIN_DIR}"

# 创建 help2man 包装脚本
cat > "${BOOTSTRAP_BIN_DIR}/help2man" << 'EOF'
#!/bin/sh
if [ "$1" = "--version" ]; then
    echo "help2man (GNU help2man) 1.49.3"
    exit 0
fi
exit 0
EOF

# 创建 makeinfo 包装脚本
cat > "${BOOTSTRAP_BIN_DIR}/makeinfo" << 'EOF'
#!/bin/sh
if [ "$1" = "--version" ]; then
    echo "makeinfo (GNU texinfo) 7.0"
    exit 0
fi
exit 0
EOF

chmod +x "${BOOTSTRAP_BIN_DIR}/help2man" "${BOOTSTRAP_BIN_DIR}/makeinfo"
export HELP2MAN="${BOOTSTRAP_BIN_DIR}/help2man"
export MAKEINFO="${BOOTSTRAP_BIN_DIR}/makeinfo"
export PATH="${BOOTSTRAP_BIN_DIR}:${PATH}"

经验总结:

  • Autotools 项目的 bootstrap 脚本通常会检查文档生成工具
  • 交叉编译时,这些工具不是必需的,但需要满足检查
  • 创建最小化的包装脚本是最简单的解决方案

坑 2: gnulib 下载 URL 配置

问题描述: bootstrap 脚本尝试从官方 GNU 服务器下载 gnulib,但网络连接失败或速度很慢:

代码语言:javascript
复制
bootstrap: running: git clone --shallow-since=2019-02-19 'https://git.savannah.gnu.org/git/gnulib' 'gnulib'

根本原因:

  1. bootstrap 脚本优先使用 .gitmodules 文件中的 URL
  2. 环境变量 GNULIB_URLgnulib_url 的优先级低于 .gitmodules
  3. 需要使用国内镜像(如 GitCode)来加速下载

解决方案: 修改 .gitmodules 文件,将 gnulib 的 URL 替换为 GitCode 镜像:

代码语言:javascript
复制
# 设置使用 GitCode 镜像
export gnulib_url="git@gitcode.com:jianguoxu/gnulib.git"

# 修改 .gitmodules 文件(bootstrap 优先使用此文件)
if [ -f ".gitmodules" ]; then
    # 替换官方 URL 为 GitCode 镜像
    sed -i.bak 's|url = https://git.savannah.gnu.org/git/gnulib|url = git@gitcode.com:jianguoxu/gnulib.git|g' .gitmodules 2>/dev/null || \
    sed -i '' 's|url = https://git.savannah.gnu.org/git/gnulib|url = git@gitcode.com:jianguoxu/gnulib.git|g' .gitmodules 2>/dev/null || true
fi

经验总结:

  • .gitmodules 的优先级高于环境变量
  • 需要同时设置环境变量和修改 .gitmodules
  • macOS 和 Linux 的 sed 命令参数不同,需要兼容处理

坑 3: configure.ac 中不支持的语言

问题描述: 运行 aclocal 时出现错误,提示不支持某些语言:

代码语言:javascript
复制
configure.ac: error: unsupported language: Objective-C
configure.ac: error: unsupported language: Objective-C++
configure.ac: error: unsupported language: Microsoft Macro Assembler

根本原因: configure.ac 中定义了 Objective-C、Objective-C++ 和 Microsoft Macro Assembler 的语言支持,但这些语言在交叉编译到 HarmonyOS 时不被支持。

解决方案: 在运行 aclocal 之前,注释掉这些不支持的语言:

代码语言:javascript
复制
# 必须在运行 aclocal 之前修复
if [ -f "configure.ac" ]; then
    # 备份 configure.ac
    cp configure.ac configure.ac.bak
    
    # 注释掉不支持的语言
    sed -i.bak 's/^LT_LANG(Objective-C)$/# LT_LANG(Objective-C)  # Disabled for cross-compilation/' configure.ac
    sed -i.bak 's/^LT_LANG(Objective-C++)$/# LT_LANG(Objective-C++)  # Disabled for cross-compilation/' configure.ac
    sed -i.bak 's/^LT_LANG(Microsoft Macro Assembler)$/# LT_LANG(Microsoft Macro Assembler)  # Disabled for cross-compilation/' configure.ac
fi

经验总结:

  • 必须在 aclocal 运行之前修改 configure.ac
  • 一旦 aclocal.m4 生成,再修改 configure.ac 就无效了
  • 交叉编译时需要禁用目标平台不支持的语言

坑 4: libltdl/ltdl.mk 文件缺失

问题描述: 运行 make bootstrap-depsautomake 时出现错误:

代码语言:javascript
复制
automake: error: cannot open < libltdl/ltdl.mk: No such file or directory
make[1]: *** No rule to make target `libltdl/ltdl.mk', needed by `libltdl/Makefile.am'. Stop.

根本原因:

  1. libltdl/ltdl.mk 文件应该由 make bootstrap-deps 生成
  2. bootstrap-deps 本身需要这个文件存在才能运行
  3. 这是一个"鸡生蛋,蛋生鸡"的问题

解决方案: 在运行 bootstrap 之前,创建占位文件:

代码语言:javascript
复制
# 创建占位文件 libltdl/ltdl.mk
if [ ! -f "libltdl/ltdl.mk" ]; then
    mkdir -p libltdl
    cat > libltdl/ltdl.mk << 'EOF'
# Placeholder ltdl.mk file
# This file will be regenerated by make bootstrap-deps
LTDLDIR = libltdl
# DO NOT REMOVE THIS LINE -- make depends on it
EOF
fi

关键点:

  • 必须包含 # DO NOT REMOVE THIS LINE -- make depends on it 这一行
  • Makefile.am 的生成规则会检查这一行
  • 占位文件会在 bootstrap-deps 过程中被正确生成的文件替换

经验总结:

  • Autotools 项目经常有这种循环依赖问题
  • 创建最小化的占位文件是常见的解决方案
  • 必须确保占位文件包含必要的标记行

坑 5: m4/ltversion.in 文件缺失

问题描述: 运行 make 时出现错误:

代码语言:javascript
复制
make[2]: *** No rule to make target `m4/ltversion.in', needed by `m4/ltversion.m4'. Stop.

根本原因:

  1. m4/ltversion.m4 依赖于 m4/ltversion.in
  2. ltversion.in 应该由 make bootstrap-deps 生成
  3. 但如果 bootstrap-deps 失败,这个文件就不会被创建

解决方案: 在运行 configure 之前和之后,都确保这个文件存在:

代码语言:javascript
复制
# 在 configure 之前创建
if [ ! -f "m4/ltversion.in" ]; then
    mkdir -p m4
    cat > m4/ltversion.in << 'EOF'
# This file is used by ltversion.m4
m4_define([LT_PACKAGE_VERSION], [@PACKAGE_VERSION@])
m4_define([LT_PACKAGE_REVISION], [@PACKAGE_REVISION@])
EOF
fi

# 在 configure 之后再次检查(可能被 config.status 删除)
if [ ! -f "m4/ltversion.in" ]; then
    # 重新创建
fi

经验总结:

  • config.status 可能会删除某些文件
  • 需要在多个阶段检查并创建必需文件
  • 占位文件的内容必须符合 m4 宏的格式要求

坑 6: help2man 和 makeinfo 在 make 时缺失

问题描述: bootstrap 阶段创建的包装脚本在 make 时已经不存在,导致构建失败:

代码语言:javascript
复制
/bin/sh: /path/to/.bootstrap_bin/help2man: No such file or directory
make[2]: *** [doc/libtool.1] Error 127

根本原因:

  1. bootstrap 完成后,临时目录可能被清理
  2. Makefile 中硬编码了 help2manmakeinfo 的路径
  3. make 阶段仍然需要这些工具来生成文档

解决方案: 在运行 make 之前,重新创建包装脚本:

代码语言:javascript
复制
# 在 configure 成功后,make 之前
BOOTSTRAP_BIN_DIR="$(pwd)/.bootstrap_bin"
mkdir -p "${BOOTSTRAP_BIN_DIR}"

# 重新创建 help2man 包装脚本
if [ ! -f "${BOOTSTRAP_BIN_DIR}/help2man" ]; then
    cat > "${BOOTSTRAP_BIN_DIR}/help2man" << 'HELP2MAN_EOF'
#!/bin/sh
# ... 完整的包装脚本内容 ...
HELP2MAN_EOF
    chmod +x "${BOOTSTRAP_BIN_DIR}/help2man"
fi

# 重新创建 makeinfo 包装脚本
if [ ! -f "${BOOTSTRAP_BIN_DIR}/makeinfo" ]; then
    cat > "${BOOTSTRAP_BIN_DIR}/makeinfo" << 'MAKEINFO_EOF'
#!/bin/sh
# ... 完整的包装脚本内容 ...
MAKEINFO_EOF
    chmod +x "${BOOTSTRAP_BIN_DIR}/makeinfo"
fi

经验总结:

  • bootstrapmake 是两个不同的阶段
  • 每个阶段都需要确保必需的工具存在
  • 包装脚本需要处理实际的调用场景,不仅仅是 --version 检查

坑 7: help2man 输出文件未创建

问题描述: help2man 包装脚本输出了 man 页面内容到终端,但文件没有被创建:

代码语言:javascript
复制
  GEN      doc/libtool.1
.TH LIBTOOL 1 "2024" "GNU Libtool"
.SH NAME
...
install: ././doc/libtool.1: No such file or directory
make[3]: *** [install-man1] Error 71

根本原因:

  1. help2man 的输出被重定向到文件,但包装脚本只输出到 stdout
  2. 重定向可能没有正常工作
  3. 需要检测输出文件参数并直接写入文件

解决方案: 改进 help2man 包装脚本,使其能够检测输出文件并直接写入:

代码语言:javascript
复制
cat > "${BOOTSTRAP_BIN_DIR}/help2man" << 'HELP2MAN_EOF'
#!/bin/sh
# 处理 --version 和 --help
if [ "$1" = "--version" ]; then
    echo "help2man (GNU help2man) 1.49.3"
    exit 0
fi

# 查找输出文件参数
OUTPUT_FILE=""
for arg in "$@"; do
    case "$arg" in
        --output=*)
            OUTPUT_FILE="${arg#--output=}"
            ;;
        --output)
            # 下一个参数是输出文件
            PREV_ARG="--output"
            continue
            ;;
        *)
            if [ "$PREV_ARG" = "--output" ]; then
                OUTPUT_FILE="$arg"
            fi
            PREV_ARG=""
            ;;
    esac
done

# 如果没有找到输出文件,尝试从输入文件名推断
if [ -z "$OUTPUT_FILE" ] && [ -n "$INPUT_BINARY" ]; then
    BASE_NAME=$(basename "$INPUT_BINARY")
    OUTPUT_FILE="doc/${BASE_NAME}.1"
fi

# 如果找到了输出文件,直接写入
if [ -n "$OUTPUT_FILE" ]; then
    mkdir -p "$(dirname "$OUTPUT_FILE")"
    cat > "$OUTPUT_FILE" << 'MAN_EOF'
.TH LIBTOOL 1 "2024" "GNU Libtool"
.SH NAME
libtool \- GNU libtool
...
MAN_EOF
    exit 0
fi

# 否则输出到 stdout
echo "..."
exit 0
HELP2MAN_EOF

经验总结:

  • 包装脚本需要处理实际的调用场景
  • 需要检测输出文件参数并直接写入
  • 如果没有找到输出文件,尝试从输入文件名推断

坑 8: GL_INIT 宏未定义

问题描述: 运行 configure 时出现错误:

代码语言:javascript
复制
configure: line 32976: GL_INIT: command not found
config.status: error: cannot find input file: 'Makefile.in'

根本原因:

  1. GL_INIT 是 gnulib 提供的宏,定义在 m4/gnulib-comp.m4
  2. aclocal 需要包含 gnulib/m4 目录才能找到这些宏
  3. 如果 aclocal 没有正确配置,宏就不会被展开

解决方案: 确保 aclocal 调用时包含 gnulib/m4 目录:

代码语言:javascript
复制
# 检查 gnulib 是否已初始化
if [ -d "gnulib/m4" ]; then
    # 运行 aclocal,包含 gnulib/m4 目录
    aclocal -I m4 -I gnulib/m4 || {
        echo "Error: aclocal failed"
        exit 1
    }
else
    # 如果 gnulib 未初始化,先运行 bootstrap
    ./bootstrap
fi

经验总结:

  • aclocal-I 选项用于指定宏搜索路径
  • 必须包含 gnulib/m4 目录才能找到 gnulib 的宏
  • 如果 gnulib 未初始化,必须先运行 bootstrap

坑 9: bootstrap-deps 失败后的恢复

问题描述: make bootstrap-deps 失败后,构建过程无法继续:

代码语言:javascript
复制
bootstrap: running: make bootstrap-deps ...
make[1]: *** No rule to make target `libltdl/ltdl.mk', needed by `libltdl/Makefile.am'. Stop.
bootstrap: error: make bootstrap-deps failed

根本原因: bootstrap-deps 需要一些文件存在才能运行,但这些文件又应该由它自己生成。

解决方案: 创建所有必需的占位文件,然后重试:

代码语言:javascript
复制
# 创建占位文件
if [ ! -f "libltdl/ltdl.mk" ]; then
    mkdir -p libltdl
    cat > libltdl/ltdl.mk << 'EOF'
LTDLDIR = libltdl
# DO NOT REMOVE THIS LINE -- make depends on it
EOF
fi

if [ ! -f "build-aux/ltmain.sh" ]; then
    mkdir -p build-aux
    # 尝试从 ltmain.in 生成,或创建占位文件
    if [ -f "build-aux/ltmain.in" ]; then
        sed -e "s/@PACKAGE@/libtool/g" build-aux/ltmain.in > build-aux/ltmain.sh
    else
        echo "#!/bin/sh" > build-aux/ltmain.sh
    fi
    chmod +x build-aux/ltmain.sh
fi

if [ ! -f "m4/ltversion.in" ]; then
    mkdir -p m4
    cat > m4/ltversion.in << 'EOF'
m4_define([LT_PACKAGE_VERSION], [@PACKAGE_VERSION@])
m4_define([LT_PACKAGE_REVISION], [@PACKAGE_REVISION@])
EOF
fi

# 重试 bootstrap-deps
make bootstrap-deps

经验总结:

  • 创建占位文件是解决循环依赖的常见方法
  • 占位文件必须包含必要的标记行
  • 占位文件会在后续步骤中被正确生成的文件替换

坑 10: config.status 删除必需文件

问题描述: configure 运行成功后,某些文件被 config.status 删除,导致后续 make 失败:

代码语言:javascript
复制
config.status: executing libtool commands
automake: error: cannot open < libltdl/ltdl.mk: No such file or directory

根本原因: config.status 在执行 “libtool commands” 时会调用 automake,而 automake 需要 libltdl/ltdl.mk 文件。

解决方案: 在 configure 运行之后,再次检查并创建必需文件:

代码语言:javascript
复制
# 运行 configure
./configure \
    --prefix=${LIBTOOL_INSTALL_HNP_PATH} \
    --host=aarch64-linux-gnu \
    --enable-static \
    --disable-shared \
    ...

# configure 成功后,再次确保必需文件存在
if [ ! -f "libltdl/ltdl.mk" ]; then
    echo "Warning: libltdl/ltdl.mk missing after configure, recreating it..."
    mkdir -p libltdl
    cat > libltdl/ltdl.mk << 'EOF'
LTDLDIR = libltdl
# DO NOT REMOVE THIS LINE -- make depends on it
EOF
fi

if [ ! -f "m4/ltversion.in" ]; then
    echo "Warning: m4/ltversion.in missing after configure, recreating it..."
    mkdir -p m4
    cat > m4/ltversion.in << 'EOF'
m4_define([LT_PACKAGE_VERSION], [@PACKAGE_VERSION@])
m4_define([LT_PACKAGE_REVISION], [@PACKAGE_REVISION@])
EOF
fi

经验总结:

  • config.status 可能会删除某些文件
  • 需要在多个阶段检查并创建必需文件
  • 不能假设 configure 运行后所有文件都存在

完整构建脚本结构

最终的 build_ohos.sh 脚本结构如下:

代码语言:javascript
复制
#!/bin/bash
# 1. 环境变量设置
export M4="/opt/homebrew/opt/m4/bin/m4"
export CC="${SDK_PATH}/native/llvm/bin/clang"
export CXX="${SDK_PATH}/native/llvm/bin/clang++"
...

# 2. 创建版本文件
echo "2.4.10" > .tarball-version
echo "2.4.10" > .version
# 创建 git-version-gen 占位符

# 3. 修复 configure.ac(注释不支持的语言)
sed -i '' 's/^LT_LANG(Objective-C)$/# .../' configure.ac

# 4. 创建包装脚本目录
mkdir -p .bootstrap_bin
# 创建 help2man 和 makeinfo 包装脚本

# 5. 配置 gnulib URL(GitCode 镜像)
export gnulib_url="git@gitcode.com:jianguoxu/gnulib.git"
# 修改 .gitmodules

# 6. 创建占位文件
# - libltdl/ltdl.mk
# - build-aux/ltmain.sh
# - m4/ltversion.in

# 7. 运行 bootstrap
./bootstrap || {
    # 错误恢复逻辑
    # 检查 gnulib 是否已初始化
    # 创建缺失的占位文件
    # 重试 bootstrap
}

# 8. 验证 gnulib 初始化
if [ ! -d "gnulib/m4" ]; then
    echo "Error: gnulib not initialized"
    exit 1
fi

# 9. 运行 autoreconf(如果需要)
if [ ! -f "configure" ] || [ ! -f "Makefile.in" ]; then
    aclocal -I m4 -I gnulib/m4
    autoreconf -f
fi

# 10. 运行 configure
./configure \
    --prefix=${LIBTOOL_INSTALL_HNP_PATH} \
    --host=aarch64-linux-gnu \
    --enable-static \
    --disable-shared \
    ...

# 11. configure 后检查并创建必需文件
# - libltdl/ltdl.mk
# - m4/ltversion.in
# - 重新创建 help2man 和 makeinfo 包装脚本

# 12. 运行 make
make -j$(nproc)

# 13. 运行 make install
make install

关键经验总结

1. Autotools 项目的构建流程
代码语言:javascript
复制
源码 → bootstrap → autoreconf → configure → make → install
  ↓        ↓           ↓           ↓         ↓       ↓
版本文件  gnulib初始化 生成configure  配置系统  编译  安装
2. 循环依赖问题的解决

Autotools 项目经常有循环依赖问题:

  • 问题: 文件 A 需要文件 B,但文件 B 又需要文件 A
  • 解决: 创建占位文件,包含必要的标记行
3. 交叉编译的特殊处理
  • 禁用目标平台不支持的语言
  • 使用包装脚本满足工具检查
  • 跳过文档生成(或使用占位文件)
4. 错误恢复策略
  • 检查关键文件是否存在
  • 创建占位文件
  • 重试失败的步骤
  • 如果关键步骤失败但部分成功,尝试继续
5. 调试技巧
  • 使用 set -x 启用调试输出
  • 检查中间文件是否存在
  • 查看 config.log 了解 configure 失败原因
  • 检查 Makefile 中的硬编码路径

最终成果

成功构建 libtool 2.4.10 for HarmonyOS PC,生成以下文件:

  • libtool - 主程序
  • libtoolize - 初始化脚本
  • libltdl - 动态加载库
  • 头文件和文档

参考资源

结语

libtool 的适配过程充满了挑战,主要原因是其复杂的 Autotools 构建系统和 gnulib 依赖。通过创建占位文件、包装脚本和错误恢复逻辑,我们成功解决了所有问题。希望本文能够帮助其他开发者在适配类似项目时少走弯路。


本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2025-12-09,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • libtool 适配 HarmonyOS PC 完整踩坑指南
    • 概述
    • 项目背景
    • 环境准备
      • 必需工具
      • HarmonyOS SDK 配置
    • 主要踩坑经历
      • 坑 1: gnulib 初始化失败
      • 坑 2: gnulib 下载 URL 配置
      • 坑 3: configure.ac 中不支持的语言
      • 坑 4: libltdl/ltdl.mk 文件缺失
      • 坑 5: m4/ltversion.in 文件缺失
      • 坑 6: help2man 和 makeinfo 在 make 时缺失
      • 坑 7: help2man 输出文件未创建
      • 坑 8: GL_INIT 宏未定义
      • 坑 9: bootstrap-deps 失败后的恢复
      • 坑 10: config.status 删除必需文件
    • 完整构建脚本结构
    • 关键经验总结
      • 1. Autotools 项目的构建流程
      • 2. 循环依赖问题的解决
      • 3. 交叉编译的特殊处理
      • 4. 错误恢复策略
      • 5. 调试技巧
    • 最终成果
    • 参考资源
    • 结语
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档