less 工具 HarmonyOS 适配故障排除详细记录

目录


概述

本文档详细记录了在将 less 工具适配到 HarmonyOS PC 平台过程中遇到的所有编译错误和链接错误,以及完整的故障排除过程。每个错误都包含了:

  • 完整的错误信息
  • 错误原因分析
  • 调试步骤
  • 解决方案
  • 验证方法

环境准备

构建环境

  • 主机系统:macOS 24.2.0 (Darwin)
  • 目标平台:HarmonyOS (aarch64-linux-ohos)
  • 编译器:HarmonyOS SDK LLVM/Clang
  • 构建系统:autotools (configure + make)

初始构建命令

cd /Users/jianguo/HarmonyOSPC/build
./build.sh --sdk /Users/jianguo/Desktop/ohosdk

项目结构

code/less/
├── build_ohos.sh          # HarmonyOS 构建脚本
├── configure              # autotools 配置脚本
├── Makefile.in           # Makefile 模板
├── defines.h             # 配置头文件(由 configure 生成)
├── screen.c              # 终端控制相关代码
├── os.c                  # 操作系统相关代码
├── lsystem.c             # 系统调用相关代码
└── ...                   # 其他源文件

错误 1:FILE 类型未定义

错误信息

funcs.h:174:30: error: unknown type name 'FILE'
public char * readfd(FILE *fd);
                    ^
funcs.h:290:30: error: unknown type name 'FILE'
public void save_marks(FILE *fout, constant char *hdr);
                       ^
funcs.h:440:30: error: unknown type name 'FILE'
public int pclose(FILE *f);
                  ^

错误分析

第一步:定位错误位置

错误发生在 funcs.h 文件中,该文件声明了多个使用 FILE * 类型的函数。FILE 是 C 标准库中定义的类型,通常定义在 stdio.h 中。

第二步:检查头文件包含

检查 less.hfuncs.h 被包含在 less.h 中)的头文件包含逻辑:

// less.h
#if HAVE_STDIO_H
#include <stdio.h>
#endif

发现问题:HAVE_STDIO_H 宏控制着 stdio.h 的包含。

第三步:检查 defines.h

查看 defines.h 文件:

/* Define to 1 if you have the <stdio.h> header file. */
#undef HAVE_STDIO_H

确认问题:HAVE_STDIO_H 被设置为 #undef,导致 stdio.h 没有被包含。

第四步:分析原因

在交叉编译环境中,configure 脚本无法运行测试程序来检测系统特性。虽然 HarmonyOS SDK 中确实有 stdio.h,但 configure 脚本无法检测到,因此将 HAVE_STDIO_H 设置为 #undef

解决方案

方案 1:手动修改 defines.h

直接修改 defines.h 文件:

/* Define to 1 if you have the <stdio.h> header file. */
#define HAVE_STDIO_H 1
方案 2:在构建脚本中自动修复(推荐)

build_ohos.sh 中添加自动修复逻辑:

# 修复 defines.h 中的关键宏定义
if [ -f "./defines.h" ]; then
    echo "Fixing defines.h for HarmonyOS cross-compilation..."
    # 确保 stdio.h 被启用(FILE 类型需要)
    sed -i.bak 's/^#undef HAVE_STDIO_H$/#define HAVE_STDIO_H 1/' defines.h 2>/dev/null || \
    sed -i '' 's/^#undef HAVE_STDIO_H$/#define HAVE_STDIO_H 1/' defines.h 2>/dev/null || true
fi

为什么使用两种 sed 命令?

  • sed -i.bak:GNU sed 语法(Linux)
  • sed -i '':BSD sed 语法(macOS)
  • || true:确保即使 sed 失败也不会中断脚本

验证方法

# 检查 defines.h 是否已修复
grep "HAVE_STDIO_H" defines.h
# 应该输出:#define HAVE_STDIO_H 1

# 重新编译
make clean
make VERBOSE=1

结果

✅ 编译通过,FILE 类型错误消失。


错误 2:PATTERN_TYPE 类型未定义

错误信息

funcs.h:378:45: error: unknown type name 'PATTERN_TYPE'
public int compile_pattern(constant char *pattern, int search_type, int show_error, PATTERN_TYPE *comp_pattern);
                                            ^
funcs.h:379:30: error: unknown type name 'PATTERN_TYPE'
public void uncompile_pattern(PATTERN_TYPE *pattern);
                             ^
funcs.h:381:30: error: unknown type name 'PATTERN_TYPE'
public lbool is_null_pattern(PATTERN_TYPE pattern);
                             ^
funcs.h:382:30: error: unknown type name 'PATTERN_TYPE'
public lbool match_pattern(PATTERN_TYPE pattern, constant char *tpattern, constant char *line, size_t line_len, size_t line_off, constant char **sp, constant char **ep, int nsp, int notbol, int search_type);
                             ^

错误分析

第一步:查找 PATTERN_TYPE 定义

使用 grep 搜索 PATTERN_TYPE 的定义:

grep -r "PATTERN_TYPE" code/less/

发现 PATTERN_TYPE 定义在 pattern.h 文件中。

第二步:检查 pattern.h
// pattern.h
#if HAVE_GNU_REGEX
#define PATTERN_TYPE struct re_pattern_buffer *
#elif HAVE_POSIX_REGCOMP
#define PATTERN_TYPE regex_t *
#elif HAVE_PCRE
#define PATTERN_TYPE pcre *
#elif HAVE_PCRE2
#define PATTERN_TYPE pcre2_code *
#elif HAVE_RE_COMP
#define PATTERN_TYPE int
#elif HAVE_REGCMP
#define PATTERN_TYPE char **
#elif HAVE_V8_REGCOMP
#define PATTERN_TYPE struct regexp *
#elif NO_REGEX
#define PATTERN_TYPE void *
#endif

发现问题:PATTERN_TYPE 的定义依赖于多个条件编译宏,如果所有条件都不满足,PATTERN_TYPE 就不会被定义。

第三步:检查 defines.h 中的正则表达式相关宏
grep -E "HAVE_GNU_REGEX|HAVE_POSIX_REGCOMP|HAVE_PCRE|HAVE_PCRE2|HAVE_RE_COMP|HAVE_REGCMP|HAVE_V8_REGCOMP|NO_REGEX" defines.h

输出:

#undef HAVE_GNU_REGEX
#undef HAVE_POSIX_REGCOMP
#undef HAVE_PCRE
#undef HAVE_PCRE2
#undef HAVE_RE_COMP
#undef HAVE_REGCMP
#undef HAVE_V8_REGCOMP
#undef NO_REGEX

确认问题:所有正则表达式相关的宏都是 #undef,包括 NO_REGEX,导致 PATTERN_TYPE 没有被定义。

第四步:分析解决方案

HarmonyOS SDK 中没有标准的正则表达式库(GNU regex、POSIX regex、PCRE 等),但 less 支持在没有正则表达式库的情况下编译(使用 NO_REGEX 宏)。在这种情况下,PATTERN_TYPE 被定义为 void *,搜索功能会被禁用,但基本的分页功能仍然可用。

解决方案

启用 NO_REGEX 宏:

# 在 build_ohos.sh 中添加
sed -i.bak 's/^#undef NO_REGEX$/#define NO_REGEX 1/' defines.h 2>/dev/null || \
sed -i '' 's/^#undef NO_REGEX$/#define NO_REGEX 1/' defines.h 2>/dev/null || true

验证方法

# 检查 defines.h
grep "NO_REGEX" defines.h
# 应该输出:#define NO_REGEX 1

# 检查 pattern.h 是否定义了 PATTERN_TYPE
grep "PATTERN_TYPE" pattern.h | grep "void"
# 应该输出:#define PATTERN_TYPE void *

结果

PATTERN_TYPE 被定义为 void *,编译通过。

注意:启用 NO_REGEX 后,less 的搜索功能会被禁用,但基本的分页浏览功能仍然可用。


错误 3:sprintf 未声明警告

错误信息

main.c:165:9: warning: call to undeclared library function 'sprintf'; ISO C99 and later do not support implicit function declarations [-Wimplicit-function-declaration]
SNPRINTF3(msg, msglen, "LESSSECURE_ALLOW: %s feature name \"%.*s\"", type, (int) len, name);

错误分析

第一步:检查 SNPRINTF3 宏定义

查看 less.h 中的宏定义:

#if HAVE_SNPRINTF
#define SNPRINTF3(str, size, fmt, v1, v2, v3) snprintf((str), (size), (fmt), (v1), (v2), (v3))
#else
/* Use unsafe sprintf if we don't have snprintf. */
#define SNPRINTF3(str, size, fmt, v1, v2, v3) sprintf((str), (fmt), (v1), (v2), (v3))
#endif

发现问题:由于 HAVE_SNPRINTF 未定义,代码回退到使用 sprintf

第二步:检查 defines.h
grep "HAVE_SNPRINTF" defines.h

输出:

#undef HAVE_SNPRINTF

确认问题:HAVE_SNPRINTF 被设置为 #undef,导致使用 sprintf 而不是 snprintf

第三步:分析原因

虽然这是一个警告而不是错误,但:

  1. sprintf 是不安全的函数(容易缓冲区溢出)
  2. snprintf 是更安全的替代品
  3. HarmonyOS SDK 应该支持 snprintf

解决方案

启用 HAVE_SNPRINTF

sed -i.bak 's/^#undef HAVE_SNPRINTF$/#define HAVE_SNPRINTF 1/' defines.h 2>/dev/null || \
sed -i '' 's/^#undef HAVE_SNPRINTF$/#define HAVE_SNPRINTF 1/' defines.h 2>/dev/null || true

验证方法

# 检查 defines.h
grep "HAVE_SNPRINTF" defines.h
# 应该输出:#define HAVE_SNPRINTF 1

# 重新编译,警告应该消失
make clean
make VERBOSE=1 2>&1 | grep -i sprintf
# 应该没有输出

结果

✅ 警告消失,代码使用更安全的 snprintf


错误 4:sgtty.h 文件未找到

错误信息

screen.c:66:10: fatal error: 'sgtty.h' file not found
#include <sgtty.h>
         ^~~~~~~~~
1 error generated.

错误分析

第一步:检查 screen.c 的包含逻辑

查看 screen.c 中的头文件包含顺序:

// screen.c
#if HAVE_TERMIOS_H && HAVE_TERMIOS_FUNCS
#include <termios.h>
#else
#if HAVE_TERMIO_H
#include <termio.h>
#else
#if HAVE_SGSTAT_H
#include <sgstat.h>
#else
#include <sgtty.h>  // 这里!
#endif
#endif
#endif

发现问题:代码会依次尝试包含 termios.htermio.hsgstat.h,如果都不可用,最后回退到 sgtty.h

第二步:检查 defines.h 中的终端控制相关宏
grep -E "HAVE_TERMIOS_H|HAVE_TERMIOS_FUNCS|HAVE_TERMIO_H|HAVE_SGSTAT_H" defines.h

输出:

#undef HAVE_TERMIOS_FUNCS
#undef HAVE_TERMIOS_H
#undef HAVE_TERMIO_H
#undef HAVE_SGSTAT_H

确认问题:所有终端控制相关的宏都是 #undef,导致代码回退到使用 sgtty.h

第三步:检查 HarmonyOS SDK
find /Users/jianguo/Desktop/ohosdk/native/sysroot -name "termios.h" 2>/dev/null
find /Users/jianguo/Desktop/ohosdk/native/sysroot -name "sgtty.h" 2>/dev/null

结果:

  • termios.h:✅ 存在
  • sgtty.h:❌ 不存在
第四步:分析原因
  • sgtty.h 是旧式的终端控制接口(BSD 风格)
  • termios.h 是 POSIX 标准的终端控制接口
  • HarmonyOS SDK 支持 POSIX termios.h,但不支持旧的 sgtty.h

解决方案

启用 POSIX termios 支持:

sed -i.bak 's/^#undef HAVE_TERMIOS_H$/#define HAVE_TERMIOS_H 1/' defines.h 2>/dev/null || \
sed -i '' 's/^#undef HAVE_TERMIOS_H$/#define HAVE_TERMIOS_H 1/' defines.h 2>/dev/null || true

sed -i.bak 's/^#undef HAVE_TERMIOS_FUNCS$/#define HAVE_TERMIOS_FUNCS 1/' defines.h 2>/dev/null || \
sed -i '' 's/^#undef HAVE_TERMIOS_FUNCS$/#define HAVE_TERMIOS_FUNCS 1/' defines.h 2>/dev/null || true

验证方法

# 检查 defines.h
grep -E "HAVE_TERMIOS_H|HAVE_TERMIOS_FUNCS" defines.h
# 应该输出:
# #define HAVE_TERMIOS_H 1
# #define HAVE_TERMIOS_FUNCS 1

# 重新编译
make clean
make VERBOSE=1 2>&1 | grep -i sgtty
# 应该没有输出

结果

✅ 编译通过,代码使用 POSIX termios.h 而不是 sgtty.h


错误 5:INT_MAX 未定义

错误信息

screen.c:1878:15: error: use of undeclared identifier 'INT_MAX'
delay = INT_MAX;
       ^

错误分析

第一步:检查 INT_MAX 的使用位置

查看 screen.c:1878 附近的代码:

delay = lstrtoic(str, &str, 10);
if (*str == '*')
    if (ckd_mul(&delay, delay, affcnt))
        delay = INT_MAX;  // 这里!

INT_MAX 用于表示整数的最大值,通常定义在 limits.h 中。

第二步:检查 less.h 的头文件包含
// less.h
#if HAVE_LIMITS_H
#include <limits.h>
#endif

发现问题:HAVE_LIMITS_H 控制着 limits.h 的包含。

第三步:检查 defines.h
grep "HAVE_LIMITS_H" defines.h

输出:

#undef HAVE_LIMITS_H

确认问题:HAVE_LIMITS_H 被设置为 #undef

解决方案

启用 HAVE_LIMITS_H

sed -i.bak 's/^#undef HAVE_LIMITS_H$/#define HAVE_LIMITS_H 1/' defines.h 2>/dev/null || \
sed -i '' 's/^#undef HAVE_LIMITS_H$/#define HAVE_LIMITS_H 1/' defines.h 2>/dev/null || true

验证方法

# 检查 defines.h
grep "HAVE_LIMITS_H" defines.h
# 应该输出:#define HAVE_LIMITS_H 1

# 重新编译
make clean
make VERBOSE=1 2>&1 | grep -i "INT_MAX"
# 应该没有错误

结果

✅ 编译通过,INT_MAX 正确定义。


错误 6:termcap 函数未声明

错误信息

screen.c:775:30: warning: call to undeclared function 'tgetflag'; ISO C99 and later do not support implicit function declarations [-Wimplicit-function-declaration]
return tcname == NULL ? 0 : tgetflag(tcname);
                            ^
screen.c:790:31: warning: call to undeclared function 'tgetnum'
screen.c:812:9: warning: call to undeclared function 'tgetstr'
screen.c:1295:9: warning: call to undeclared function 'tgoto'
screen.c:1418:6: warning: call to undeclared function 'tgetent'
screen.c:1657:2: warning: call to undeclared function 'tputs'

错误分析

第一步:检查 screen.c 的 termcap 包含逻辑

查看 screen.c 中的包含逻辑:

// screen.c
#if USE_TERMINFO
#include <curses.h>
#include <term.h>
#else
#if HAVE_NCURSESW_TERMCAP_H
#include <ncursesw/termcap.h>
#else
#if HAVE_NCURSES_TERMCAP_H
#include <ncurses/termcap.h>
#else
#if HAVE_TERMCAP_H
#include <termcap.h>
#else
// 没有包含任何 termcap 头文件!
#endif
#endif
#endif
#endif

发现问题:如果所有条件都不满足,termcap 函数就不会被声明。

第二步:检查 defines.h
grep -E "HAVE_TERMCAP_H|HAVE_TERMINFO|USE_TERMINFO" defines.h

输出:

#undef HAVE_TERMCAP_H
#undef HAVE_TERMINFO

确认问题:HAVE_TERMCAP_HHAVE_TERMINFO 都是 #undef

第三步:检查 HarmonyOS SDK
find /Users/jianguo/Desktop/ohosdk/native/sysroot -name "termcap.h" 2>/dev/null

结果:❌ 不存在

第四步:分析解决方案

由于 HarmonyOS SDK 中没有 termcap 库,有两个选择:

  1. 方案 A:启用 HAVE_TERMCAP_H,但需要提供 termcap 实现
  2. 方案 B:不启用 HAVE_TERMCAP_H,创建 stub 实现(推荐)

选择方案 B,因为:

  • 不需要链接外部库
  • 可以提供最小功能实现
  • 代码更可控

解决方案

步骤 1:创建 termcap_stub.h

创建 code/less/termcap_stub.h

/*
 * termcap_stub.h - Termcap stub header for HarmonyOS
 * 
 * This file provides minimal termcap function declarations
 * for systems that don't have termcap library.
 */

#ifndef TERMCAP_STUB_H
#define TERMCAP_STUB_H

#include <stdio.h>

/* Termcap function declarations */
extern int tgetflag(const char *id);
extern int tgetnum(const char *id);
extern char *tgetstr(const char *id, char **area);
extern char *tgoto(const char *cap, int col, int row);
extern int tgetent(char *bp, const char *name);
extern int tputs(const char *str, int affcnt, int (*putc)(int));

/* TGETENT_OK is typically 1 */
#ifndef TGETENT_OK
#define TGETENT_OK 1
#endif

#endif /* TERMCAP_STUB_H */
步骤 2:创建 termcap_stub.c

创建 code/less/termcap_stub.c

/*
 * termcap_stub.c - Termcap stub implementation for HarmonyOS
 * 
 * This file provides minimal termcap function implementations
 * for systems that don't have termcap library.
 */

#include "termcap_stub.h"
#include <stdlib.h>
#include <string.h>

/* Stub implementations */
int tgetflag(const char *id)
{
    (void)id;
    return 0; /* Feature not available */
}

int tgetnum(const char *id)
{
    (void)id;
    return -1; /* Not available */
}

char *tgetstr(const char *id, char **area)
{
    (void)id;
    (void)area;
    return NULL; /* Not available */
}

char *tgoto(const char *cap, int col, int row)
{
    /* Simple stub - return a static buffer with basic escape sequence */
    static char buf[32];
    snprintf(buf, sizeof(buf), "\033[%d;%dH", row + 1, col + 1);
    return buf;
}

int tgetent(char *bp, const char *name)
{
    (void)bp;
    (void)name;
    /* Return OK to allow compilation, but functionality will be limited */
    return TGETENT_OK;
}

int tputs(const char *str, int affcnt, int (*putc)(int))
{
    (void)affcnt;
    if (str != NULL && putc != NULL) {
        while (*str) {
            putc(*str++);
        }
    }
    return 0;
}
步骤 3:修改 screen.c

修改 screen.c 的包含逻辑:

#if HAVE_TERMCAP_H
#include <termcap.h>
#else
/* HarmonyOS doesn't have termcap.h, use stub */
#include "termcap_stub.h"
#endif

重要:不要启用 HAVE_TERMCAP_H,因为 HarmonyOS SDK 中没有 termcap.h 文件。如果启用了,预处理器会尝试包含不存在的文件,导致编译错误。

验证方法

# 检查文件是否创建
ls -la termcap_stub.h termcap_stub.c

# 检查 screen.c 是否包含 stub
grep "termcap_stub" screen.c

# 重新编译
make clean
make VERBOSE=1 2>&1 | grep -i "tgetflag\|tgetnum\|tgetstr"
# 应该没有警告

结果

✅ 编译通过,termcap 函数警告消失。


错误 7:termcap 函数链接错误

错误信息

ld.lld: error: undefined symbol: tgetstr
>>> referenced by screen.c
>>>               screen.o:(ltgetstr)
ld.lld: error: undefined symbol: tgetent
>>> referenced by screen.c
>>>               screen.o:(get_term)
ld.lld: error: undefined symbol: tgetflag
>>> referenced by screen.c
>>>               screen.o:(ltgetflag)
ld.lld: error: undefined symbol: tgetnum
>>> referenced by screen.c
>>>               screen.o:(ltgetnum)
ld.lld: error: undefined symbol: tgoto
>>> referenced by screen.c
>>>               screen.o:(ltgoto)
ld.lld: error: undefined symbol: tputs
>>> referenced by screen.c
>>>               screen.o:(cost)
>>> referenced by screen.c
>>>               screen.o:(do_tputs)

错误分析

第一步:检查 termcap_stub.c 是否被编译
ls -la termcap_stub.o

结果:✅ 文件存在

第二步:检查 Makefile 的 OBJ 变量
grep "^OBJ" Makefile
grep "termcap_stub" Makefile

发现问题:termcap_stub.o 没有被添加到 OBJ 变量中。

第三步:检查 Makefile 结构

查看 Makefile.in

OBJ = \
	main.${O} screen.${O} brac.${O} ch.${O} charset.${O} cmdbuf.${O} \
	command.${O} cvt.${O} decode.${O} edit.${O} evar.${O} filename.${O} forwback.${O} \
	help.${O} ifile.${O} input.${O} jump.${O} \
	line.${O} linenum.${O} \
	lsystem.${O} mark.${O} optfunc.${O} option.${O} opttbl.${O} os.${O} \
	output.${O} pattern.${O} position.${O} prompt.${O} search.${O} signal.${O} \
	tags.${O} ttyin.${O} version.${O} xbuf.${O}  @REGEX_O@

less$(EXEEXT): ${OBJ}
	${CC} ${LDFLAGS} -o $@ ${OBJ} ${LIBS}

发现问题:termcap_stub.o 需要被添加到 OBJ 变量中。

第四步:分析添加位置

由于 @REGEX_O@ 在配置时被替换为空(因为 NO_REGEX 被启用),OBJ 变量的最后一行是:

tags.${O} ttyin.${O} version.${O} xbuf.${O}

需要在这一行末尾添加 termcap_stub.${O}

解决方案

build_ohos.sh 中添加逻辑,自动将 termcap_stub.o 添加到 Makefile:

# 如果使用 termcap stub,需要将其添加到 Makefile 的 OBJ 变量中
if [ -f "./termcap_stub.c" ] && ! grep -q "termcap_stub" Makefile 2>/dev/null; then
    echo "Adding termcap_stub.c to Makefile..."
    # 只在 OBJ 变量定义的行(以制表符开头的行,包含 xbuf.${O})末尾添加 termcap_stub.o
    # 使用 awk 更精确地匹配 OBJ 变量定义的行
    awk '/^OBJ = /{in_obj=1} in_obj && /xbuf\.\${O}/{sub(/$/, " termcap_stub.${O}"); in_obj=0} {print}' Makefile > Makefile.tmp && mv Makefile.tmp Makefile || \
    # 如果 awk 失败,使用 sed 匹配以制表符开头且包含 xbuf.${O} 的行
    sed -i.bak '/^\t.*xbuf\.\${O}/s/$/ termcap_stub.${O}/' Makefile 2>/dev/null || \
    sed -i '' '/^\t.*xbuf\.\${O}/s/$/ termcap_stub.${O}/' Makefile 2>/dev/null || true
    rm -f Makefile.bak 2>/dev/null || true
fi

为什么使用 awk?

  • awk 可以更精确地匹配多行变量定义
  • 只修改 OBJ 变量定义中的行,不影响其他行(如 lesskey 的目标行)

为什么还要使用 sed?

  • 作为 awk 的备选方案
  • 匹配以制表符开头且包含 xbuf.${O} 的行

验证方法

# 检查 Makefile
grep "termcap_stub" Makefile
# 应该输出:tags.${O} ttyin.${O} version.${O} xbuf.${O} termcap_stub.${O}

# 检查链接命令
make VERBOSE=1 2>&1 | grep "ld.lld.*termcap_stub"
# 应该包含 termcap_stub.o

结果

✅ 链接成功,所有 termcap 函数都找到了定义。


错误 8:open/time/strerror 函数未声明

错误信息

os.c:401:6: warning: call to undeclared function 'open'; ISO C99 and later do not support implicit function declarations [-Wimplicit-function-declaration]
r = open(filename, flags);
    ^
os.c:429:2: warning: call to undeclared function 'time'; ISO C99 and later do not support implicit function declarations [-Wimplicit-function-declaration]
time(&t);
^
os.c:439:15: error: static declaration of 'strerror' follows non-static declaration
static char * strerror(int err)
              ^
/Users/jianguo/Desktop/ohosdk/native/sysroot/usr/include/string.h:54:7: note: previous declaration is here
char *strerror (int);
      ^

错误分析

第一步:分析 open 函数

open 函数通常声明在 fcntl.hunistd.h 中。检查 less.h

#if HAVE_FCNTL_H
#include <fcntl.h>
#endif
#if HAVE_UNISTD_H
#include <unistd.h>
#endif

检查 defines.h

grep -E "HAVE_FCNTL_H|HAVE_UNISTD_H" defines.h

输出:

#undef HAVE_FCNTL_H
#define HAVE_UNISTD_H 1

问题:HAVE_FCNTL_H 未定义,但 HAVE_UNISTD_H 已定义。虽然 unistd.h 可能也声明了 open,但最好明确启用 HAVE_FCNTL_H

第二步:分析 time 函数

time 函数声明在 time.h 中。检查 os.c

#if HAVE_TIME_H
#include <time.h>
#endif

检查 defines.h

grep "HAVE_TIME_H" defines.h

输出:

#undef HAVE_TIME_H

问题:HAVE_TIME_H 未定义。

第三步:分析 strerror 函数

os.c 中有一个静态的 strerror 实现:

#if !HAVE_STRERROR
/*
 * Local version of strerror, if not available from the system.
 */
static char * strerror(int err)
{
    // ...
}
#endif

检查 defines.h

grep "HAVE_STRERROR" defines.h

输出:

#undef HAVE_STRERROR

问题:HAVE_STRERROR 未定义,导致 os.c 定义了静态的 strerror,但系统头文件 <string.h> 中也声明了 strerror,造成冲突。

解决方案

启用三个宏:

# 启用 fcntl.h(open 函数需要)
sed -i.bak 's/^#undef HAVE_FCNTL_H$/#define HAVE_FCNTL_H 1/' defines.h 2>/dev/null || \
sed -i '' 's/^#undef HAVE_FCNTL_H$/#define HAVE_FCNTL_H 1/' defines.h 2>/dev/null || true

# 启用 time.h(time 函数需要)
sed -i.bak 's/^#undef HAVE_TIME_H$/#define HAVE_TIME_H 1/' defines.h 2>/dev/null || \
sed -i '' 's/^#undef HAVE_TIME_H$/#define HAVE_TIME_H 1/' defines.h 2>/dev/null || true

# 启用 strerror(避免与系统 strerror 冲突)
sed -i.bak 's/^#undef HAVE_STRERROR$/#define HAVE_STRERROR 1/' defines.h 2>/dev/null || \
sed -i '' 's/^#undef HAVE_STRERROR$/#define HAVE_STRERROR 1/' defines.h 2>/dev/null || true

验证方法

# 检查 defines.h
grep -E "HAVE_FCNTL_H|HAVE_TIME_H|HAVE_STRERROR" defines.h
# 应该输出:
# #define HAVE_FCNTL_H 1
# #define HAVE_TIME_H 1
# #define HAVE_STRERROR 1

# 重新编译
make clean
make VERBOSE=1 2>&1 | grep -E "open|time|strerror"
# 应该没有错误或警告

结果

✅ 编译通过,所有函数都正确声明。


错误 9:lsystem 函数未定义

错误信息

ld.lld: error: undefined symbol: lsystem
>>> referenced by command.c
>>>               command.o:(commands)
>>> referenced by command.c
>>>               command.o:(exec_mca)

错误分析

第一步:查找 lsystem 函数定义
grep -r "^public.*lsystem\|^lsystem" code/less/

输出:

code/less/lsystem.c:44:public void lsystem(constant char *cmd, constant char *donemsg)

确认:lsystem 函数定义在 lsystem.c 中。

第二步:检查 lsystem.c 的条件编译

查看 lsystem.c

#if HAVE_SYSTEM

/*
 * Pass the specified command to a shell to be executed.
 * Like plain "system()", but handles resetting terminal modes, etc.
 */
public void lsystem(constant char *cmd, constant char *donemsg)
{
    // ...
}
#endif

发现问题:lsystem 函数被 #if HAVE_SYSTEM 条件编译保护。

第三步:检查 defines.h
grep "HAVE_SYSTEM" defines.h

输出:

#undef HAVE_SYSTEM

确认问题:HAVE_SYSTEM 未定义,导致 lsystem 函数没有被编译。

第四步:检查 lsystem.o 是否被编译
ls -la lsystem.o

结果:✅ 文件存在,但函数没有被编译进去。

第五步:检查 Makefile
grep "lsystem" Makefile

输出:

lsystem.${O} mark.${O} optfunc.${O} option.${O} opttbl.${O} os.${O} \

确认:lsystem.oOBJ 变量中,但源文件中的函数没有被编译。

解决方案

启用 HAVE_SYSTEM

sed -i.bak 's/^#undef HAVE_SYSTEM$/#define HAVE_SYSTEM 1/' defines.h 2>/dev/null || \
sed -i '' 's/^#undef HAVE_SYSTEM$/#define HAVE_SYSTEM 1/' defines.h 2>/dev/null || true

验证方法

# 检查 defines.h
grep "HAVE_SYSTEM" defines.h
# 应该输出:#define HAVE_SYSTEM 1

# 重新编译
make clean
make VERBOSE=1

# 检查链接
make 2>&1 | grep -i "lsystem"
# 应该没有错误

结果

✅ 链接成功,lsystem 函数被正确编译和链接。


错误 10:手册页文件缺失

错误信息

make: *** No rule to make target 'lesskey.nro', needed by 'install'. Stop.

错误分析

第一步:检查 Makefile 的 install 目标

查看 Makefile.in

install: all ${srcdir}/less.nro ${srcdir}/lesskey.nro ${srcdir}/lessecho.nro installdirs
	${INSTALL_PROGRAM} less$(EXEEXT) ${DESTDIR}${bindir}/${binprefix}less$(EXEEXT)
	${INSTALL_PROGRAM} lesskey$(EXEEXT) ${DESTDIR}${bindir}/${binprefix}lesskey$(EXEEXT)
	${INSTALL_PROGRAM} lessecho$(EXEEXT) ${DESTDIR}${bindir}/${binprefix}lessecho$(EXEEXT)
	${INSTALL_DATA} ${srcdir}/less.nro ${DESTDIR}${mandir}/man${manext}/${manprefix}less.${manext}
	${INSTALL_DATA} ${srcdir}/lesskey.nro ${DESTDIR}${mandir}/man${manext}/${manprefix}lesskey.${manext}
	${INSTALL_DATA} ${srcdir}/lessecho.nro ${DESTDIR}${mandir}/man${manext}/${manprefix}lessecho.${manext}

发现问题:install 目标依赖于 lesskey.nrolessecho.nro 文件。

第二步:检查文件是否存在
ls -la *.nro *.nro.VER 2>/dev/null

输出:

-rw-r--r-- less.nro
-rw-r--r-- less.nro.VER
-rw-r--r-- lesskey.nro.VER
-rw-r--r-- lessecho.nro.VER

确认问题:只有 less.nro 存在,lesskey.nrolessecho.nro 不存在,但有对应的 .VER 模板文件。

第三步:分析 .VER 文件

.VER 文件是模板文件,需要处理(替换版本号等)才能生成 .nro 文件。但在我们的场景中,可以简单地复制模板文件作为占位文件。

解决方案

在安装前创建占位文件:

# 如果缺少手册页文件,创建占位文件(安装时需要)
if [ ! -f "./lesskey.nro" ]; then
    echo "Creating placeholder lesskey.nro..."
    cp lesskey.nro.VER lesskey.nro 2>/dev/null || touch lesskey.nro
fi
if [ ! -f "./lessecho.nro" ]; then
    echo "Creating placeholder lessecho.nro..."
    cp lessecho.nro.VER lessecho.nro 2>/dev/null || touch lessecho.nro
fi

为什么使用 || touch

  • 如果 cp 失败(文件不存在),创建一个空文件
  • 空文件可以满足 Makefile 的依赖要求

验证方法

# 检查文件是否创建
ls -la lesskey.nro lessecho.nro

# 尝试安装
make install
# 应该成功

结果

✅ 安装成功,手册页文件被正确安装。


总结与经验

适配难点总结

  1. 交叉编译限制

    • configure 脚本无法运行测试程序
    • 需要手动修复大量宏定义
    • 解决方案:在构建脚本中自动修复
  2. 依赖库缺失

    • HarmonyOS SDK 中没有 termcap 库
    • 需要创建 stub 实现
    • 解决方案:创建 termcap_stub.htermcap_stub.c
  3. 头文件缺失

    • 多个标准头文件的宏定义需要手动启用
    • 解决方案:在构建脚本中批量修复
  4. 条件编译复杂

    • 大量使用条件编译,需要正确设置宏定义
    • 解决方案:仔细分析代码逻辑,逐个修复

修复的宏定义汇总

宏定义修复前修复后用途
HAVE_STDIO_H#undef#define HAVE_STDIO_H 1FILE 类型定义
HAVE_SNPRINTF#undef#define HAVE_SNPRINTF 1使用 snprintf
NO_REGEX#undef#define NO_REGEX 1PATTERN_TYPE 定义
HAVE_TERMIOS_H#undef#define HAVE_TERMIOS_H 1POSIX termios 支持
HAVE_TERMIOS_FUNCS#undef#define HAVE_TERMIOS_FUNCS 1termios 函数支持
HAVE_LIMITS_H#undef#define HAVE_LIMITS_H 1INT_MAX 定义
HAVE_FCNTL_H#undef#define HAVE_FCNTL_H 1open 函数声明
HAVE_TIME_H#undef#define HAVE_TIME_H 1time 函数声明
HAVE_STRERROR#undef#define HAVE_STRERROR 1使用系统 strerror
HAVE_SYSTEM#undef#define HAVE_SYSTEM 1lsystem 函数编译

相关链接

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值