gengetopt 适配 HarmonyOS 构建问题解决实录

目录

前言

在将 gengetopt 命令行工具适配到 HarmonyOS PC 平台的过程中,遇到了多个构建错误。本文记录了完整的问题诊断和解决过程,希望能为遇到类似问题的开发者提供参考。

项目背景

gengetopt 是一个用于生成命令行参数解析代码的工具,使用 autotools 构建系统。在 HarmonyOS 交叉编译环境中构建时,遇到了以下主要问题:

  1. struct option 重复定义错误
  2. gengetopt 工具路径配置问题
  3. gengen 工具缺失
  4. gengen 与现代 C++ 编译器的兼容性问题
  5. config.h 宏定义错误
  6. 构建路径配置问题

问题一:struct option 重复定义

错误现象

./getopt.h:194:8: error: redefinition of 'option'
struct option
       ^
/Users/jianguo/Desktop/ohosdk/native/sysroot/usr/include/getopt.h:12:8: note: previous definition is here
struct option {
       ^

问题分析

gengetopt 项目使用 gnulib 提供的 getopt 实现。当 HAVE_GETOPT_H 被定义为 1 时,getopt.h 会使用 include_next 包含系统的 getopt.h,导致 struct option 被重复定义。

解决方案

需要将 HAVE_GETOPT_H 设置为 0,让 gengetopt 使用自己的 getopt 实现,避免与系统头文件冲突。

修复步骤:

  1. 修复 config.h
sed -i '' 's/^#define HAVE_GETOPT_H 1$/#undef HAVE_GETOPT_H/' config.h
  1. 修复 Makefile(用于生成 getopt.h):
sed -i '' 's/^HAVE_GETOPT_H = 1$/HAVE_GETOPT_H = 0/' gl/Makefile
  1. 清理并重新生成 getopt.h
rm -f gl/getopt.h
make -C gl getopt.h

验证:

检查生成的 getopt.h 文件,确认 #if 0 块(不会包含系统的 getopt.h):

#if 0
# define _GL_SYSTEM_GETOPT
# include_next <getopt.h>
# undef _GL_SYSTEM_GETOPT
#endif

问题二:gengetopt 工具路径配置

错误现象

/Users/jianguo/HarmonyOSPC/build/code/gengetopt/gengetopt --input cmdline.ggo
make[2]: /Users/jianguo/HarmonyOSPC/build/code/gengetopt/gengetopt: No such file or directory

问题分析

构建脚本检测到系统的 gengetopt 工具(/opt/homebrew/bin/gengetopt),但在更新 Makefile 时,路径计算错误,将 GENGETOPT 设置为不存在的路径。

解决方案

使用 command -v gengetopt 直接获取完整路径,而不是先设置为命令名再计算路径。

修复代码:

# 检查 gengetopt 工具是否可用
GENGETOPT_TOOL=""
ABS_GENGETOPT_TOOL=""

if command -v gengetopt >/dev/null 2>&1; then
    # 使用系统的 gengetopt,获取完整路径
    ABS_GENGETOPT_TOOL=$(command -v gengetopt)
    GENGETOPT_TOOL="${ABS_GENGETOPT_TOOL}"
    echo "Using system gengetopt: ${ABS_GENGETOPT_TOOL}"
    
    # 更新 Makefile 中的 GENGETOPT 变量
    for makefile in $(find . -name "Makefile" 2>/dev/null); do
        if grep -q "^GENGETOPT = " "$makefile" 2>/dev/null; then
            sed -i '' "s|^GENGETOPT = .*|GENGETOPT = ${ABS_GENGETOPT_TOOL}|" "$makefile" 2>/dev/null || \
            sed -i.bak "s|^GENGETOPT = .*|GENGETOPT = ${ABS_GENGETOPT_TOOL}|" "$makefile"
            rm -f "${makefile}.bak"
        fi
    done
else
    echo "Error: gengetopt tool is required but not found"
    exit 1
fi

问题三:gengen 工具缺失

错误现象

gengen -i c_source.h_skel -F c_source.h -f c_source
make[4]: gengen: No such file or directory

问题分析

gengengengetopt 构建过程中必需的代码生成工具,用于从 .skel 模板文件生成 .h.cc 文件。该工具在 macOS 的包管理器(Homebrew、MacPorts)中不可用。

解决方案

需要从源代码构建 gengen。最新版本是 gengen-1.4.2(2009年发布)。

安装步骤:

cd /tmp
curl -O https://ftp.gnu.org/gnu/gengen/gengen-1.4.2.tar.gz
tar xzf gengen-1.4.2.tar.gz
cd gengen-1.4.2
./configure && make && sudo make install

问题四:gengen 与现代 C++ 编译器的兼容性

错误现象

构建 gengen 时遇到多个错误:

  1. config.h 文件未找到
getopt.c:23:11: fatal error: 'config.h' file not found
  1. struct option 重复定义
./getopt.h:194:8: error: redefinition of 'option'
  1. 旧版 C++ 头文件问题
fatal error: 'iostream.h' file not found
fatal error: 'streambuf.h' file not found
  1. C++ 标准库兼容性问题
error: no matching constructor for initialization of 'streambuf'
error: reference to 'stringbuf' is ambiguous

解决方案

4.1 创建 config.h

config.status 报告创建了 config.h,但文件实际上不存在。手动创建:

sed -e 's/@[A-Z_][A-Z0-9_]*@/1/g' config.h.in > config.h
4.2 修复 HAVE_GETOPT_H

gengetopt 相同的问题:

sed -i '' 's/^#define HAVE_GETOPT_H 1$/#undef HAVE_GETOPT_H/' config.h
sed -i '' 's/^HAVE_GETOPT_H = 1$/HAVE_GETOPT_H = 0/' lib/Makefile
rm -f lib/getopt.h
4.3 启用标准库的 sstream

gengen 的代码检查 HAVE_SSTREAM,如果未定义则使用自定义的 sstream 实现。现代 C++ 编译器都支持 <sstream>,应该使用标准库版本:

echo "#define HAVE_SSTREAM 1" >> config.h
4.4 修复 PACKAGE 和 VERSION 定义

config.h 中的 PACKAGEVERSION 被错误定义为数字,需要改为字符串:

sed -i '' \
    -e 's/^#undef PACKAGE$/#define PACKAGE "gengen"/' \
    -e 's/^#undef VERSION$/#define VERSION "1.4.2"/' \
    config.h
4.5 跳过文档构建

gengen 的文档构建需要 makeinfo 工具,且文档不是构建可执行文件的必需部分:

make -C src gengen

完整的 gengen 修复脚本:

#!/bin/bash
cd /tmp/gengen-1.4.2

# 1. 创建 config.h
if [ ! -f config.h ]; then
    sed -e 's/@[A-Z_][A-Z0-9_]*@/1/g' config.h.in > config.h
fi

# 2. 修复 HAVE_GETOPT_H
sed -i '' 's/^#define HAVE_GETOPT_H 1$/#undef HAVE_GETOPT_H/' config.h
sed -i '' 's/^HAVE_GETOPT_H = 1$/HAVE_GETOPT_H = 0/' lib/Makefile
rm -f lib/getopt.h

# 3. 启用标准库 sstream
if ! grep -q "^#define HAVE_SSTREAM 1" config.h; then
    echo "#define HAVE_SSTREAM 1" >> config.h
fi

# 4. 修复 PACKAGE 和 VERSION
sed -i '' \
    -e 's/^#undef PACKAGE$/#define PACKAGE "gengen"/' \
    -e 's/^#undef VERSION$/#define VERSION "1.4.2"/' \
    config.h

# 5. 构建 gengen(跳过文档)
make -C src gengen

# 6. 安装到用户目录
mkdir -p ~/bin
cp src/gengen ~/bin/gengen
chmod +x ~/bin/gengen
export PATH="$HOME/bin:$PATH"

问题五:config.h 宏定义错误

错误现象

cmdline.c:342:43: error: pointer/integer type mismatch
     (strlen(CMDLINE_PARSER_PACKAGE_NAME) ? CMDLINE_PARSER_PACKAGE_NAME : CMDLINE_PARSER_PACKAGE),

问题分析

gengetoptconfig.h 中,PACKAGEVERSION 等宏被错误定义为数字 1,而不是字符串。这是因为之前使用 sed 替换 @VAR@ 模板时,将所有变量都替换为了 1

解决方案

需要将这些宏正确定义为字符串:

sed -i '' \
    -e 's/^#define PACKAGE 1$/#define PACKAGE "gengetopt"/' \
    -e 's/^#define PACKAGE_NAME 1$/#define PACKAGE_NAME "gengetopt"/' \
    -e 's/^#define PACKAGE_TARNAME 1$/#define PACKAGE_TARNAME "gengetopt"/' \
    -e 's/^#define PACKAGE_VERSION 1$/#define PACKAGE_VERSION "2.23.1"/' \
    -e 's/^#define PACKAGE_STRING 1$/#define PACKAGE_STRING "gengetopt 2.23.1"/' \
    -e 's/^#define PACKAGE_BUGREPORT 1$/#define PACKAGE_BUGREPORT "bug-gengetopt@gnu.org"/' \
    -e 's/^#define VERSION 1$/#define VERSION "2.23.1"/' \
    config.h

问题六:构建路径配置

错误现象

make[4]: *** No rule to make target `/libgen.la', needed by `libgengetopt.la'.  Stop.

问题分析

src/Makefilelibgengetopt_la_DEPENDENCIES 包含了错误的路径 /libgen.la(绝对路径),应该是相对路径 skels/libgen.la

解决方案

修复 Makefile 中的路径:

sed -i '' 's|/libgen\.la|skels/libgen.la|g' src/Makefile

问题七:文档构建问题

错误现象

../src/gengetopt: ../src/gengetopt: cannot execute binary file

问题分析

doc 目录的构建需要使用 gengetopt 工具生成文档,但构建的是交叉编译的二进制文件(针对 HarmonyOS aarch64),无法在 macOS 上执行。

解决方案

跳过文档构建,因为文档不是运行时必需的:

sed -i '' 's/ doc//g' Makefile

或者使用主机的 gengetopt 工具(如果可用)。

完整的构建脚本修复

以下是修复后的 build_ohos.sh 关键部分:

#!/bin/bash
# ... 前面的配置代码 ...

# 修复 config.h 中的 HAVE_GETOPT_H
if grep -q "^#define HAVE_GETOPT_H 1" config.h 2>/dev/null; then
    echo "Fixing HAVE_GETOPT_H in config.h..."
    sed -i '' 's/^#define HAVE_GETOPT_H 1$/#undef HAVE_GETOPT_H/' config.h
fi

# 修复 Makefile 中的 HAVE_GETOPT_H
for makefile in $(find . -name "Makefile" 2>/dev/null); do
    if grep -q "^HAVE_GETOPT_H = 1" "$makefile" 2>/dev/null; then
        sed -i '' 's/^HAVE_GETOPT_H = 1$/HAVE_GETOPT_H = 0/' "$makefile"
    fi
done

# 清理并重新生成 getopt.h
if [ -f "gl/getopt.h" ]; then
    rm -f gl/getopt.h
fi

# 检查 gengetopt 工具
if command -v gengetopt >/dev/null 2>&1; then
    ABS_GENGETOPT_TOOL=$(command -v gengetopt)
    echo "Using system gengetopt: ${ABS_GENGETOPT_TOOL}"
    # 更新 Makefile
    for makefile in $(find . -name "Makefile" 2>/dev/null); do
        if grep -q "^GENGETOPT = " "$makefile" 2>/dev/null; then
            sed -i '' "s|^GENGETOPT = .*|GENGETOPT = ${ABS_GENGETOPT_TOOL}|" "$makefile"
        fi
    done
fi

# 检查 gengen 工具
if command -v gengen >/dev/null 2>&1; then
    ABS_GENGEN_TOOL=$(command -v gengen)
    echo "Using system gengen: ${ABS_GENGEN_TOOL}"
    # 更新 Makefile
    for makefile in $(find . -name "Makefile" 2>/dev/null); do
        if grep -q "^GENGEN = " "$makefile" 2>/dev/null; then
            sed -i '' "s|^GENGEN = .*|GENGEN = ${ABS_GENGEN_TOOL}|" "$makefile"
        fi
    done
else
    echo "Error: gengen tool is required but not found"
    echo "Please install gengen first (see installation guide)"
    exit 1
fi

# 修复 config.h 中的宏定义
sed -i '' \
    -e 's/^#define PACKAGE 1$/#define PACKAGE "gengetopt"/' \
    -e 's/^#define PACKAGE_NAME 1$/#define PACKAGE_NAME "gengetopt"/' \
    -e 's/^#define PACKAGE_TARNAME 1$/#define PACKAGE_TARNAME "gengetopt"/' \
    -e 's/^#define PACKAGE_VERSION 1$/#define PACKAGE_VERSION "2.23.1"/' \
    -e 's/^#define PACKAGE_STRING 1$/#define PACKAGE_STRING "gengetopt 2.23.1"/' \
    -e 's/^#define PACKAGE_BUGREPORT 1$/#define PACKAGE_BUGREPORT "bug-gengetopt@gnu.org"/' \
    -e 's/^#define VERSION 1$/#define VERSION "2.23.1"/' \
    config.h

# 修复 libgen.la 路径
sed -i '' 's|/libgen\.la|skels/libgen.la|g' src/Makefile

# 跳过文档构建
sed -i '' 's/ doc//g' Makefile

# 清理并构建
make clean || true
make VERBOSE=1
make install

# ... 后续的打包代码 ...

构建结果

成功构建后,生成以下文件:

  • output/gengetopt.hnp - HarmonyOS HNP 格式包(253KB)
  • output/ohos_gengetopt_2.23.1.tar.gz - 传统 tar.gz 压缩包(250KB)

经验总结

  1. 交叉编译环境下的工具依赖:构建工具(如 gengetoptgengen)需要在主机系统上可用,而不是交叉编译的目标。

  2. autotools 配置文件的修复:当 config.h 生成不正确时,需要手动修复关键宏定义。

  3. 旧版软件的兼容性gengen-1.4.2 是 2009 年的版本,与现代 C++ 编译器存在兼容性问题,需要启用标准库特性(如 HAVE_SSTREAM)。

  4. 路径配置的重要性:确保 Makefile 中的路径配置正确,避免绝对路径错误。

  5. 构建顺序:某些目录(如 doc)的构建可能依赖交叉编译的二进制文件,需要跳过或使用主机工具。

参考资源

附录:gengen 安装完整脚本

#!/bin/bash
# gengen 1.4.2 安装脚本(适用于 macOS)

set -e

GENGEN_VERSION="1.4.2"
INSTALL_DIR="$HOME/bin"

echo "Downloading gengen ${GENGEN_VERSION}..."
cd /tmp
curl -O "https://ftp.gnu.org/gnu/gengen/gengen-${GENGEN_VERSION}.tar.gz"
tar xzf "gengen-${GENGEN_VERSION}.tar.gz"
cd "gengen-${GENGEN_VERSION}"

echo "Configuring..."
./configure

echo "Applying compatibility fixes..."

# 1. 创建 config.h
if [ ! -f config.h ]; then
    sed -e 's/@[A-Z_][A-Z0-9_]*@/1/g' config.h.in > config.h
fi

# 2. 修复 HAVE_GETOPT_H
sed -i '' 's/^#define HAVE_GETOPT_H 1$/#undef HAVE_GETOPT_H/' config.h 2>/dev/null || \
sed -i.bak 's/^#define HAVE_GETOPT_H 1$/#undef HAVE_GETOPT_H/' config.h
sed -i '' 's/^HAVE_GETOPT_H = 1$/HAVE_GETOPT_H = 0/' lib/Makefile 2>/dev/null || \
sed -i.bak 's/^HAVE_GETOPT_H = 1$/HAVE_GETOPT_H = 0/' lib/Makefile
rm -f lib/getopt.h

# 3. 启用标准库 sstream
if ! grep -q "^#define HAVE_SSTREAM 1" config.h; then
    echo "#define HAVE_SSTREAM 1" >> config.h
fi

# 4. 修复 PACKAGE 和 VERSION
sed -i '' \
    -e 's/^#undef PACKAGE$/#define PACKAGE "gengen"/' \
    -e 's/^#undef VERSION$/#define VERSION "1.4.2"/' \
    config.h 2>/dev/null || \
sed -i.bak \
    -e 's/^#undef PACKAGE$/#define PACKAGE "gengen"/' \
    -e 's/^#undef VERSION$/#define VERSION "1.4.2"/' \
    config.h

echo "Building gengen..."
make -C src gengen

echo "Installing to ${INSTALL_DIR}..."
mkdir -p "${INSTALL_DIR}"
cp src/gengen "${INSTALL_DIR}/gengen"
chmod +x "${INSTALL_DIR}/gengen"

# 添加到 PATH
if ! echo "$PATH" | grep -q "${INSTALL_DIR}"; then
    echo "Adding ${INSTALL_DIR} to PATH..."
    echo 'export PATH="$HOME/bin:$PATH"' >> ~/.zshrc
    export PATH="${INSTALL_DIR}:${PATH}"
fi

echo "gengen installed successfully!"
echo "Location: ${INSTALL_DIR}/gengen"
which gengen
gengen --version

相关链接

作者: 坚果

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值