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

相关链接

相关推荐
以太浮标20 小时前
华为eNSP模拟器综合实验之-DHCP服务中继配置案例
网络·华为·智能路由器·信息与通信
游戏技术分享20 小时前
【鸿蒙游戏技术分享 第75期】AGC后台批量导入商品失败,提示“参数错误”
游戏·华为·harmonyos
No Silver Bullet21 小时前
HarmonyOS NEXT开发进阶(十七):WebView 拉起 H5 页面
华为·harmonyos
liuhaikang21 小时前
【鸿蒙HarmonyOS Next App实战开发】口语小搭档——应用技术实践
harmonyos
北方的流星1 天前
华为交换机MSTP和VRRP综合应用配置
运维·网络·华为
C雨后彩虹1 天前
简易内存池
java·数据结构·算法·华为·面试
liuhaikang1 天前
鸿蒙VR视频播放库——md360player
音视频·vr·harmonyos
北方的流星1 天前
华为交换机IPv6静态路由、默认路由、RIPng和OSPFv3路由配置
运维·网络·华为
飞露1 天前
鸿蒙Preview预览文件失败原因
华为·harmonyos
夏小鱼的blog1 天前
【HarmonyOS应用开发入门】第五期:状态管理V2入门 - 1
harmonyos·状态管理