本文记录了将 Whois 工具(版本 5.5.10)成功交叉编译到 HarmonyOS PC 平台的完整过程。Whois 是一个用于查询域名注册信息的命令行工具,在交叉编译过程中遇到了版本信息生成、依赖库检测、系统函数缺失、翻译文件构建等多个挑战。通过系统性的问题排查和解决,最终成功构建并打包为 HNP(HarmonyOS Native Package)格式。
Whois 使用传统的 Makefile 构建系统,在交叉编译场景下,主要挑战包括:
debian/changelog 生成 version.hpkg-config 工具缺失导致依赖检测失败getpass 函数msgfmt 工具可能出现段错误DESTDIR 和 prefixohos SDK 提供的交叉编译工具链位于:
/Users/jianguo/Desktop/ohosdk/native/llvm/bin/
关键工具:
clang: C 编译器ld.lld: 链接器llvm-ar: 归档工具llvm-ranlib: 索引工具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"
错误信息:
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 文件:
# 生成 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 文件内容:
#define VERSION "5.6.5"
#define IDSTRING "Md5.6.5"
错误信息:
/bin/sh: pkg-config: command not found
原因分析:pkg-config 工具不存在,导致 Makefile 中的依赖库检测失败。虽然这些检测不是致命的,但会产生大量警告。
解决方案: 创建一个临时的 pkg-config 包装脚本:
# 设置 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 的依赖检测失败但不影响构建。
错误信息:
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 函数:
/* 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>
staticchar *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);
returnNULL;
}
/* 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);
returnNULL;
}
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 信号,确保中断时清理资源getpass 规范)错误信息:
install: chmod 755 /usr/bin/: Operation not permitted
install: /usr/bin/whois: Operation not permitted
原因分析: Makefile 默认安装到 /usr/bin/,这是系统目录,需要 root 权限。在交叉编译场景下,应该安装到指定的安装目录。
解决方案: 设置 DESTDIR 和 prefix 环境变量:
# 设置安装路径
export DESTDIR=${TREE_INSTALL_HNP_PATH}
export prefix=/usr
这样文件会安装到 ${TREE_INSTALL_HNP_PATH}/usr/bin/,而不是系统的 /usr/bin/。
错误信息:
msgfmt --statistics --check --verbose --output-file=cs.mo cs.po
make[1]: *** [cs.mo] Segmentation fault: 11
原因分析:msgfmt 工具在构建翻译文件时出现段错误。这可能是工具本身的问题,或者是交叉编译环境导致的。
解决方案: 将翻译文件构建设为可选,失败不影响整体构建:
# 构建主程序(跳过翻译文件,因为 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..."
}
需求: 构建成功后,需要打印生成的 HNP 包和 tar 包的路径,方便用户查找和使用。
解决方案: 在构建脚本末尾添加结果输出:
# 打包 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""
完整的构建脚本 build_ohos.sh 包含以下关键步骤:
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
# 设置 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
# 设置安装路径
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..."
}
# 打包 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""
创建 hnp.json 配置文件:
{
"type": "hnp-config",
"name": "whois",
"version": "5.5.10",
"install": {}
}
成功构建后,生成以下内容:
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 包配置
make clean 之后生成 version.hmake_version_h.pl 从 debian/changelog 提取版本信息termios API 实现密码输入SIGINT 信号A: HarmonyOS SDK 可能没有提供 getpass 函数的实现。mkpasswd 工具需要这个函数来安全地读取用户密码(不回显)。我们实现了一个符合 POSIX 规范的替代实现。
A: 翻译文件(.mo 文件)是可选的,主要用于多语言支持。主程序(whois 和 mkpasswd)的功能不依赖于翻译文件,即使翻译文件构建失败,主程序仍然可以正常使用。
A: POSIX getpass 函数规范要求使用静态缓冲区。虽然这在多线程环境中可能不安全,但对于 mkpasswd 这种单线程命令行工具来说是可以接受的。
A: Makefile 使用 pkg-config 来检测可选依赖库(如 libidn2、libidn 等)。如果 pkg-config 不存在,这些检测会失败,但不会影响构建。包装脚本返回失败状态,让检测失败但不产生错误信息。
参考资料
[1]
Whois 官方仓库: https://github.com/rfc1036/whois
[2]
POSIX getpass 规范: https://pubs.opengroup.org/onlinepubs/9699919799/functions/getpass.html
[3]
HarmonyOS Native 开发文档: https://developer.harmonyos.com/
[4]
PC代码仓: https://gitcode.com/OpenHarmonyPCDeveloper
[5]
PC社区: https://harmonyospc.csdn.net/