Whois 工具在 HarmonyOS PC 上的交叉编译实践

摘要

本文记录了将 Whois 工具(版本 5.5.10)成功交叉编译到 HarmonyOS PC 平台的完整过程。Whois 是一个用于查询域名注册信息的命令行工具,在交叉编译过程中遇到了版本信息生成、依赖库检测、系统函数缺失、翻译文件构建等多个挑战。通过系统性的问题排查和解决,最终成功构建并打包为 HNP(HarmonyOS Native Package)格式。

1. 背景

1.1 项目需求

  • 目标平台: HarmonyOS PC(ARM64 架构)
  • 构建平台: macOS(ARM64)
  • Whois 版本: 5.5.10
  • 构建系统: Makefile(基于 Autotools)
  • 输出格式: HNP 包

1.2 技术挑战

Whois 使用传统的 Makefile 构建系统,在交叉编译场景下,主要挑战包括:

  1. 版本信息生成 : 需要从 debian/changelog 生成 version.h
  2. 依赖库检测 : pkg-config 工具缺失导致依赖检测失败
  3. 系统函数缺失 : HarmonyOS SDK 可能没有提供 getpass 函数
  4. 翻译文件构建 : msgfmt 工具可能出现段错误
  5. 安装路径配置 : 需要正确设置 DESTDIRprefix

2. 环境准备

2.1 工具链信息

HarmonyOS SDK 提供的交叉编译工具链位于:

复制代码
/Users/jianguo/Desktop/ohosdk/native/llvm/bin/

关键工具:

  • clang: C 编译器
  • ld.lld: 链接器
  • llvm-ar: 归档工具
  • llvm-ranlib: 索引工具

2.2 编译标志

bash 复制代码
CFLAGS="-fPIC -D__MUSL__=1 -D__OHOS__ -fstack-protector-strong \
        --target=aarch64-linux-ohos \
        --sysroot=/path/to/sysroot"

LDFLAGS="-fuse-ld=${LD} --target=aarch64-linux-ohos \
         --sysroot=/path/to/sysroot"

3. 问题排查与解决

3.1 问题一:IDSTRING 未定义错误

错误信息:

复制代码
whois.c:81:26: error: use of undeclared identifier 'IDSTRING'
const char *client_tag = IDSTRING;
                         ^

原因分析 :
whois.c 中使用了 IDSTRING 宏,该宏定义在 version.h 文件中。但 version.h 文件不存在,需要通过 make_version_h.pl 脚本从 debian/changelog 生成。

解决方案 :

在构建前生成 version.h 文件:

bash 复制代码
# 生成 version.h(make clean 可能会删除它)
if [ ! -f "version.h" ] && [ -f "debian/changelog" ] && [ -f "make_version_h.pl" ]; then
    echo "Generating version.h from debian/changelog..."
    perl make_version_h.pl debian/changelog > version.h || {
        echo "Error: Failed to generate version.h"
        exit 1
    }
fi

# 检查 version.h 是否存在
if [ ! -f "version.h" ]; then
    echo "Error: version.h not found and could not be generated"
    exit 1
fi

生成的 version.h 文件内容:

c 复制代码
#define VERSION "5.6.5"
#define IDSTRING "Md5.6.5"

3.2 问题二:pkg-config 缺失

错误信息:

复制代码
/bin/sh: pkg-config: command not found

原因分析 :
pkg-config 工具不存在,导致 Makefile 中的依赖库检测失败。虽然这些检测不是致命的,但会产生大量警告。

解决方案 :

创建一个临时的 pkg-config 包装脚本:

bash 复制代码
# 设置 PKG_CONFIG 为空命令(如果不存在),避免构建失败
if ! command -v pkg-config >/dev/null 2>&1; then
    echo "Warning: pkg-config not found, using false command"
    # 创建一个临时的 pkg-config 包装脚本
    PKG_CONFIG_WRAPPER=$(mktemp)
    cat > "${PKG_CONFIG_WRAPPER}" << 'EOF'
#!/bin/sh
exit 1
EOF
    chmod +x "${PKG_CONFIG_WRAPPER}"
    export PKG_CONFIG="${PKG_CONFIG_WRAPPER}"
fi

这个包装脚本返回失败状态,让 Makefile 的依赖检测失败但不影响构建。

3.3 问题三:getpass 函数未实现

错误信息:

复制代码
mkpasswd.c:402:13: warning: call to undeclared function 'getpass'
mkpasswd.c:402:11: error: incompatible integer to pointer conversion
ld.lld: error: undefined symbol: getpass

原因分析 :
mkpasswd.c 中使用了 getpass 函数来读取密码,但 HarmonyOS SDK 可能没有提供该函数的实现。getpass 是一个 POSIX 函数,用于从终端读取密码(不回显)。

解决方案 :

实现一个自定义的 getpass 函数:

c 复制代码
/* Ensure getpass is declared and implemented */
#if !defined(HAVE_READPASSPHRASE)
/* HarmonyOS may not have getpass, provide a simple implementation */
#if !defined(getpass)
#include <termios.h>
#include <signal.h>

static char *getpass_buffer = NULL;

static void getpass_sigint(int sig) {
    (void)sig;
    if (getpass_buffer) {
        free(getpass_buffer);
        getpass_buffer = NULL;
    }
    signal(SIGINT, SIG_DFL);
    raise(SIGINT);
}

char *getpass(const char *prompt) {
    struct termios old_termios, new_termios;
    FILE *tty;
    int c;
    size_t len = 0;
    size_t size = 128;
    
    /* Free previous buffer if exists */
    if (getpass_buffer) {
        free(getpass_buffer);
        getpass_buffer = NULL;
    }
    
    /* Open controlling terminal */
    tty = fopen("/dev/tty", "r+");
    if (!tty) {
        tty = stdin;
    }
    
    /* Save terminal settings */
    if (tcgetattr(fileno(tty), &old_termios) == 0) {
        new_termios = old_termios;
        new_termios.c_lflag &= ~(ECHO | ECHOE | ECHOK | ECHONL);
        tcsetattr(fileno(tty), TCSAFLUSH, &new_termios);
    }
    
    /* Set up signal handler */
    signal(SIGINT, getpass_sigint);
    
    /* Print prompt */
    fprintf(tty, "%s", prompt);
    fflush(tty);
    
    /* Allocate buffer */
    getpass_buffer = malloc(size);
    if (!getpass_buffer) {
        if (tty != stdin) fclose(tty);
        return NULL;
    }
    
    /* Read password */
    while ((c = fgetc(tty)) != EOF && c != '\n' && c != '\r') {
        if (len + 1 >= size) {
            size *= 2;
            char *new_buf = realloc(getpass_buffer, size);
            if (!new_buf) {
                free(getpass_buffer);
                getpass_buffer = NULL;
                if (tty != stdin) fclose(tty);
                return NULL;
            }
            getpass_buffer = new_buf;
        }
        getpass_buffer[len++] = c;
    }
    getpass_buffer[len] = '\0';
    
    /* Print newline */
    fprintf(tty, "\n");
    fflush(tty);
    
    /* Restore terminal settings */
    if (tcgetattr(fileno(tty), &old_termios) == 0) {
        tcsetattr(fileno(tty), TCSAFLUSH, &old_termios);
    }
    
    /* Restore signal handler */
    signal(SIGINT, SIG_DFL);
    
    if (tty != stdin) {
        fclose(tty);
    }
    
    return getpass_buffer;
}
#endif
#endif

实现特点

  • 使用 termios API 禁用终端回显
  • 从控制终端读取密码
  • 处理 SIGINT 信号,确保中断时清理资源
  • 使用静态缓冲区存储密码(符合 POSIX getpass 规范)
  • 支持动态扩展缓冲区大小

3.4 问题四:安装路径错误

错误信息:

复制代码
install: chmod 755 /usr/bin/: Operation not permitted
install: /usr/bin/whois: Operation not permitted

原因分析 :

Makefile 默认安装到 /usr/bin/,这是系统目录,需要 root 权限。在交叉编译场景下,应该安装到指定的安装目录。

解决方案 :

设置 DESTDIRprefix 环境变量:

bash 复制代码
# 设置安装路径
export DESTDIR=${TREE_INSTALL_HNP_PATH}
export prefix=/usr

这样文件会安装到 ${TREE_INSTALL_HNP_PATH}/usr/bin/,而不是系统的 /usr/bin/

3.5 问题五:翻译文件构建失败

错误信息:

复制代码
msgfmt --statistics --check --verbose --output-file=cs.mo cs.po
make[1]: *** [cs.mo] Segmentation fault: 11

原因分析 :
msgfmt 工具在构建翻译文件时出现段错误。这可能是工具本身的问题,或者是交叉编译环境导致的。

解决方案 :

将翻译文件构建设为可选,失败不影响整体构建:

bash 复制代码
# 构建主程序(跳过翻译文件,因为 msgfmt 可能有问题)
make VERBOSE=1 whois mkpasswd || {
    echo "Error: Failed to build whois or mkpasswd"
    exit 1
}

# 安装主程序(跳过翻译文件安装)
make install-whois install-mkpasswd install-bashcomp || {
    echo "Error: Failed to install whois or mkpasswd"
    exit 1
}

# 尝试安装翻译文件(如果失败不影响整体构建)
make install-pos || {
    echo "Warning: Failed to install translation files, continuing..."
}

3.6 问题六:构建结果路径输出

需求 :

构建成功后,需要打印生成的 HNP 包和 tar 包的路径,方便用户查找和使用。

解决方案 :

在构建脚本末尾添加结果输出:

bash 复制代码
# 打包 HNP
HNP_FILE="${ARCHIVE_PATH}/whois.hnp"
${HNP_TOOL} pack -i ${TREE_INSTALL_HNP_PATH} -o ${ARCHIVE_PATH}/ || {
    echo "Error: Failed to pack HNP file"
    exit 1
}

# 打包 tar.gz
TAR_FILE="${ARCHIVE_PATH}/ohos_whois_5.5.10.tar.gz"
tar -zvcf ${TAR_FILE} whois_5.5.10/ || {
    echo "Error: Failed to create tar archive"
    exit 1
}

# 打印构建结果
echo ""
echo "=========================================="
echo "Build completed successfully!"
echo "=========================================="
echo "HNP Package: ${HNP_FILE}"
echo "Tar Archive: ${TAR_FILE}"
echo "Installation Path: ${TREE_INSTALL_HNP_PATH}"
echo "=========================================="
echo ""

4. 最终构建脚本

完整的构建脚本 build_ohos.sh 包含以下关键步骤:

4.1 版本信息生成

bash 复制代码
make clean

# 生成 version.h(make clean 可能会删除它)
if [ ! -f "version.h" ] && [ -f "debian/changelog" ] && [ -f "make_version_h.pl" ]; then
    echo "Generating version.h from debian/changelog..."
    perl make_version_h.pl debian/changelog > version.h || {
        echo "Error: Failed to generate version.h"
        exit 1
    }
fi

4.2 pkg-config 处理

bash 复制代码
# 设置 PKG_CONFIG 为空命令(如果不存在),避免构建失败
if ! command -v pkg-config >/dev/null 2>&1; then
    echo "Warning: pkg-config not found, using false command"
    PKG_CONFIG_WRAPPER=$(mktemp)
    cat > "${PKG_CONFIG_WRAPPER}" << 'EOF'
#!/bin/sh
exit 1
EOF
    chmod +x "${PKG_CONFIG_WRAPPER}"
    export PKG_CONFIG="${PKG_CONFIG_WRAPPER}"
fi

4.3 构建和安装

bash 复制代码
# 设置安装路径
export DESTDIR=${TREE_INSTALL_HNP_PATH}
export prefix=/usr

# 构建主程序(跳过翻译文件)
make VERBOSE=1 whois mkpasswd || {
    echo "Error: Failed to build whois or mkpasswd"
    exit 1
}

# 安装主程序
make install-whois install-mkpasswd install-bashcomp || {
    echo "Error: Failed to install whois or mkpasswd"
    exit 1
}

# 尝试安装翻译文件(可选)
make install-pos || {
    echo "Warning: Failed to install translation files, continuing..."
}

4.4 打包和输出

bash 复制代码
# 打包 HNP 和 tar.gz
# ... (见问题六的解决方案)

# 打印构建结果
echo ""
echo "=========================================="
echo "Build completed successfully!"
echo "=========================================="
echo "HNP Package: ${HNP_FILE}"
echo "Tar Archive: ${TAR_FILE}"
echo "Installation Path: ${TREE_INSTALL_HNP_PATH}"
echo "=========================================="
echo ""

5. HNP 包配置

创建 hnp.json 配置文件:

json 复制代码
{
    "type": "hnp-config",
    "name": "whois",
    "version": "5.5.10",
    "install": {}
}

6. 构建结果

成功构建后,生成以下内容:

复制代码
whois_5.5.10/
├── usr/
│   ├── bin/
│   │   ├── whois          # 主程序
│   │   └── mkpasswd       # 密码生成工具
│   └── share/
│       └── man/
│           ├── man1/
│           │   ├── whois.1
│           │   └── mkpasswd.1
│           └── man5/
│               └── whois.conf.5
├── etc/
│   └── bash_completion.d/
│       ├── whois
│       └── mkpasswd
└── hnp.json                # HNP 包配置

7. 关键要点总结

7.1 版本信息管理

  1. 生成时机 : 在 make clean 之后生成 version.h
  2. 生成工具 : 使用 make_version_h.pldebian/changelog 提取版本信息
  3. 检查机制: 生成后检查文件是否存在,确保构建可以继续

7.2 依赖库检测

  1. pkg-config 包装: 创建临时脚本返回失败状态
  2. 可选依赖: 大多数依赖库检测失败不影响构建
  3. 清理机制: 构建完成后清理临时文件

7.3 系统函数实现

  1. getpass 实现 : 使用 termios API 实现密码输入
  2. 信号处理 : 正确处理 SIGINT 信号
  3. 内存管理: 使用静态缓冲区(符合 POSIX 规范)
  4. 错误处理: 完善的错误处理和资源清理

7.4 构建流程优化

  1. 分离构建: 主程序和翻译文件分开构建
  2. 可选组件: 翻译文件构建失败不影响主程序
  3. 错误处理: 每个步骤都有错误检查和退出机制
  4. 结果输出: 构建完成后打印包文件路径

8. 常见问题

Q1: 为什么需要实现 getpass 函数?

A: HarmonyOS SDK 可能没有提供 getpass 函数的实现。mkpasswd 工具需要这个函数来安全地读取用户密码(不回显)。我们实现了一个符合 POSIX 规范的替代实现。

Q2: 为什么翻译文件构建失败不影响构建?

A: 翻译文件(.mo 文件)是可选的,主要用于多语言支持。主程序(whoismkpasswd)的功能不依赖于翻译文件,即使翻译文件构建失败,主程序仍然可以正常使用。

Q3: 为什么使用静态缓冲区存储密码?

A: POSIX getpass 函数规范要求使用静态缓冲区。虽然这在多线程环境中可能不安全,但对于 mkpasswd 这种单线程命令行工具来说是可以接受的。

Q4: pkg-config 包装脚本的作用是什么?

A: Makefile 使用 pkg-config 来检测可选依赖库(如 libidn2、libidn 等)。如果 pkg-config 不存在,这些检测会失败,但不会影响构建。包装脚本返回失败状态,让检测失败但不产生错误信息。

9. 参考资源

10. 版本信息

  • Whois 版本: 5.5.10
  • 构建日期: 2024年
  • 目标平台: HarmonyOS PC (ARM64)
  • 构建平台: macOS (ARM64)
  • 工具链: HarmonyOS SDK LLVM

相关链接

相关推荐
m0_685535083 小时前
华为光学工程师笔试真题(含答案与深度解析)
华为·光学·光学设计·光学工程·镜头设计
lqj_本人3 小时前
鸿蒙原生与Qt混合开发:性能优化与资源管理
qt·harmonyos
lqj_本人4 小时前
鸿蒙Qt字体实战:消灭“豆腐块“乱码与自定义字体加载
qt·华为·harmonyos
大侠课堂4 小时前
海康大华大疆华为中兴追觅经典面试题200道完整版
华为
爱笑的眼睛114 小时前
深入探索HarmonyOS中RichText组件的HTML渲染机制
华为·harmonyos
IT闫4 小时前
figlet 在鸿蒙PC上的构建与适配
华为·harmonyos
空白诗5 小时前
tokei 在鸿蒙PC上的构建与适配
后端·华为·rust·harmonyos
汉堡黄6 小时前
鸿蒙开发:案例集合Tabs:分段按钮组件
harmonyos
哈__6 小时前
exa 在 HarmonyOS 上的构建与适配
elasticsearch·华为·harmonyos