本文记录使用命令 OHOS_ARCH=aarch64 OHOS_ABI=arm64-v8a sh ./create-hnp.sh 构建 Zlib 1.3.1 的完整过程,包括环境、构建链路、关键日志、常见问题与解决方案、产物验证与重建方法,便于复现与运维。

📖 Zlib 简介

Zlib 是一个广泛使用的压缩库,提供了 DEFLATE 压缩算法的实现。它是许多应用程序和系统的基础组件,被广泛用于文件压缩、网络传输、数据库存储等场景。Zlib 是 gzip 格式的基础,也是 HTTP 压缩的标准实现。

🎯 Zlib 的作用与重要性

Zlib 是现代软件生态中压缩处理的核心组件,提供了:

  • DEFLATE 压缩算法:高效的压缩和解压缩功能
  • gzip 格式支持:与 gzip 工具兼容的压缩格式
  • 流式压缩:支持流式压缩和解压缩,适合大文件处理
  • 内存压缩:支持内存中的压缩和解压缩操作
  • 开发库:提供 libz 库供应用程序使用
  • 广泛兼容性:被几乎所有主流软件和系统使用

🔧 Zlib 核心特性

1. 压缩算法
  • DEFLATE 算法:结合了 LZ77 算法和霍夫曼编码
  • 压缩级别:支持 0-9 的压缩级别(0=无压缩,9=最大压缩)
  • 压缩格式:支持 zlib、gzip、raw deflate 格式
2. 性能特点
  • 快速压缩:在压缩速度和压缩比之间取得良好平衡
  • 快速解压:解压速度通常比压缩速度快
  • 内存效率:内存占用小,适合嵌入式系统
3. 应用场景
  • 文件压缩:gzip、zip 等工具的基础
  • 网络传输:HTTP 压缩、FTP 压缩等
  • 数据库存储:数据库压缩存储
  • 游戏开发:资源文件压缩
  • 嵌入式系统:固件压缩、日志压缩等
4. API 特性
  • 流式 APIz_stream 结构支持流式压缩
  • 简单 APIcompress/uncompress 函数提供简单接口
  • 文件 APIgzopen/gzread/gzwrite 等文件操作函数
  • 错误处理:完善的错误码和错误处理机制

🚀 构建入口与环境

  • 📝 执行命令OHOS_ARCH=aarch64 OHOS_ABI=arm64-v8a sh ./create-hnp.sh
  • 🔧 入口脚本create-hnp.sh
    • 检查必需的环境变量 OHOS_ARCHOHOS_ABI
    • 导出 LC_CTYPETOOL_HOMEOHOS_SDK_HOME
    • 执行 make -C build-hnp
  • 📦 顶层构建build-hnp/Makefile
    • PKGS 变量定义需要构建的包列表(包含 zlib
    • 通过 check-pkgs 机制自动检测 PKGS 变化并触发重新构建
    • 自动合并 external-hnp 目录下的外部 HNP 包
    • base.hnp 依赖所有包的 .stamp 和外部 HNP 包
    • 总目标 all: copy,打包 base.hnp 并拷贝到 entry/hnp/$(OHOS_ABI)

⚙️ Zlib 包的构建配置

  • 📁 包目录build-hnp/zlib/Makefile
    • 继承通用规则:include ../utils/Makefrag
    • 源地址:https://zlib.net/zlib-1.3.1.tar.gz
    • 版本:1.3.1
  • ⚙️ Autotools 配置参数
    • --prefix=$(PREFIX) - 安装前缀(/data/app/base.org/base_1.0
    • --enable-shared - 构建共享库
    • 注意:zlib 使用自定义的 configure 脚本,不是标准的 Autotools
  • 🔨 构建流程
    1. 下载源码包(支持多镜像回退)
    2. 解包并进入 temp/zlib-1.3.1 目录
    3. 运行 ./configure 配置构建系统
    4. 使用 make -j $(nproc) 并行编译
    5. 使用 make install 安装
    6. 执行 ELF strip 减小体积
    7. 复制到 ../sysroot
  • 🔧 通用工具链与路径build-hnp/utils/Makefrag
    • CC/CXX/LD/AR/RANLIB/... 均指向 OHOS SDK 的 LLVM 工具链
    • 下载支持多镜像回退:wgetcurl,主镜像失败时自动尝试备用镜像

📋 关键日志与过程节点

  • 📥 下载与解包
    • zlib.net 下载 zlib-1.3.1.tar.gz(约 1.4MB)
    • 完成解包并进入 temp/zlib-1.3.1 目录
    • 下载规则支持多镜像回退:wgetcurl 兜底
  • ⚙️ 配置阶段
    • 运行 ./configure --prefix=... --enable-shared
    • zlib 使用自定义的 configure 脚本,检测系统特性
    • 配置成功,生成 Makefile 和构建配置
  • 🔨 编译与安装
    • 使用 make -j $(nproc) 并行编译
    • 成功编译生成 libz.so.1.3.1libz.a 和头文件
    • 使用 make install 安装到临时前缀
    • 执行 llvm-strip 剥离共享库符号
    • 复制到 ../sysroot
  • 📦 打包
    • 完成 base.hnp 重打包,拷贝产物到 entry/hnp/arm64-v8a/
    • Zlib 库已成功打包到 base.hnp

✅ 产物验证

📦 检查打包文件

ls build-hnp/base.hnp  # 应存在
ls entry/hnp/arm64-v8a/*.hnp  # 应包含 base.hnp 与 base-public.hnp

🔍 检查库文件

# 检查 zlib 库
ls -lh build-hnp/sysroot/lib/libz.so* build-hnp/sysroot/lib/libz.a
file build-hnp/sysroot/lib/libz.so.1.3.1

# 检查头文件
ls -lh build-hnp/sysroot/include/zlib.h build-hnp/sysroot/include/zconf.h

# 检查 pkg-config 文件
ls -lh build-hnp/sysroot/lib/pkgconfig/zlib.pc
cat build-hnp/sysroot/lib/pkgconfig/zlib.pc

✅ 构建验证结果

  • ✅ Zlib 库已成功安装:
    • libz.so.1.3.1 (89K) - 主库文件
    • libz.so.1 - 版本符号链接
    • libz.so - 通用符号链接
    • libz.a (118K) - 静态库
  • ✅ 文件类型:ELF 64-bit LSB shared object, ARM aarch64
  • ✅ 头文件已安装:zlib.h (95K)、zconf.h (17K)
  • ✅ pkg-config 文件已安装:zlib.pc (275 bytes)
  • ✅ 版本信息:1.3.1
  • ⚠️ 注意:Zlib 是库,不提供命令行工具,需要通过编程接口使用

💻 终端中执行的示例命令

📦 Zlib 编程接口使用

1. 基本压缩和解压缩(C 语言)
#include <stdio.h>
#include <zlib.h>
#include <string.h>

int main() {
    const char *source = "Hello, Zlib! This is a test string.";
    unsigned long sourceLen = strlen(source) + 1;
    unsigned long destLen = compressBound(sourceLen);
    unsigned char *dest = (unsigned char *)malloc(destLen);
    
    // 压缩
    if (compress(dest, &destLen, (unsigned char *)source, sourceLen) != Z_OK) {
        fprintf(stderr, "Compression failed\n");
        return 1;
    }
    
    printf("Original size: %lu bytes\n", sourceLen);
    printf("Compressed size: %lu bytes\n", destLen);
    printf("Compression ratio: %.2f%%\n", 
           (1.0 - (double)destLen / sourceLen) * 100);
    
    // 解压缩
    unsigned char *uncompressed = (unsigned char *)malloc(sourceLen);
    unsigned long uncompressedLen = sourceLen;
    if (uncompress(uncompressed, &uncompressedLen, dest, destLen) != Z_OK) {
        fprintf(stderr, "Decompression failed\n");
        return 1;
    }
    
    printf("Decompressed: %s\n", uncompressed);
    
    free(dest);
    free(uncompressed);
    return 0;
}
2. 流式压缩(C 语言)
#include <stdio.h>
#include <zlib.h>
#include <string.h>

int compress_file(const char *source_file, const char *dest_file) {
    FILE *source = fopen(source_file, "rb");
    gzFile dest = gzopen(dest_file, "wb");
    
    if (!source || !dest) {
        if (source) fclose(source);
        if (dest) gzclose(dest);
        return 1;
    }
    
    char buffer[1024];
    int bytes_read;
    
    while ((bytes_read = fread(buffer, 1, sizeof(buffer), source)) > 0) {
        if (gzwrite(dest, buffer, bytes_read) != bytes_read) {
            fclose(source);
            gzclose(dest);
            return 1;
        }
    }
    
    fclose(source);
    gzclose(dest);
    return 0;
}

int decompress_file(const char *source_file, const char *dest_file) {
    gzFile source = gzopen(source_file, "rb");
    FILE *dest = fopen(dest_file, "wb");
    
    if (!source || !dest) {
        if (source) gzclose(source);
        if (dest) fclose(dest);
        return 1;
    }
    
    char buffer[1024];
    int bytes_read;
    
    while ((bytes_read = gzread(source, buffer, sizeof(buffer))) > 0) {
        if (fwrite(buffer, 1, bytes_read, dest) != bytes_read) {
            gzclose(source);
            fclose(dest);
            return 1;
        }
    }
    
    gzclose(source);
    fclose(dest);
    return 0;
}
3. 使用 pkg-config 编译
# 编译使用 Zlib 的程序
gcc -o program program.c $(pkg-config --cflags --libs zlib)

# 查看 Zlib 版本
pkg-config --modversion zlib

# 查看编译标志
pkg-config --cflags zlib

# 查看链接标志
pkg-config --libs zlib
4. 使用 gzip 工具(依赖 zlib)
# 压缩文件
gzip file.txt  # 生成 file.txt.gz

# 解压文件
gunzip file.txt.gz
# 或
gzip -d file.txt.gz

# 压缩并保留原文件
gzip -k file.txt

# 显示压缩信息
gzip -l file.txt.gz

# 压缩级别(1-9,默认 6)
gzip -1 file.txt  # 最快压缩
gzip -9 file.txt  # 最大压缩

# 压缩到标准输出
gzip -c file.txt > file.txt.gz

# 从标准输入压缩
cat file.txt | gzip > file.txt.gz

# 解压到标准输出
gunzip -c file.txt.gz
# 或
zcat file.txt.gz
5. 使用其他依赖 zlib 的工具
# 使用 curl 进行 HTTP 压缩传输(如果 curl 支持 zlib)
curl -H "Accept-Encoding: gzip" https://example.com

# 使用 tar 配合 gzip 压缩目录
tar czf archive.tar.gz directory/

# 解压 tar.gz 文件
tar xzf archive.tar.gz

# 查看 tar.gz 文件内容
tar tzf archive.tar.gz

# 使用 zip 工具(如果支持 zlib)
zip -r archive.zip directory/
unzip archive.zip
6. 压缩级别示例
#include <zlib.h>

// 设置压缩级别(0-9)
int compress_with_level(unsigned char *dest, unsigned long *destLen,
                        const unsigned char *source, unsigned long sourceLen,
                        int level) {
    z_stream stream;
    int err;
    
    stream.zalloc = Z_NULL;
    stream.zfree = Z_NULL;
    stream.opaque = Z_NULL;
    
    err = deflateInit(&stream, level);
    if (err != Z_OK) return err;
    
    stream.next_in = (unsigned char *)source;
    stream.avail_in = sourceLen;
    stream.next_out = dest;
    stream.avail_out = *destLen;
    
    err = deflate(&stream, Z_FINISH);
    if (err != Z_STREAM_END) {
        deflateEnd(&stream);
        return err == Z_OK ? Z_BUF_ERROR : err;
    }
    
    *destLen = stream.total_out;
    err = deflateEnd(&stream);
    return err;
}
7. 错误处理示例
#include <stdio.h>
#include <zlib.h>
#include <string.h>

const char *zlib_error_string(int err) {
    switch (err) {
        case Z_OK: return "OK";
        case Z_STREAM_END: return "Stream end";
        case Z_NEED_DICT: return "Need dictionary";
        case Z_ERRNO: return "File error";
        case Z_STREAM_ERROR: return "Stream error";
        case Z_DATA_ERROR: return "Data error";
        case Z_MEM_ERROR: return "Memory error";
        case Z_BUF_ERROR: return "Buffer error";
        case Z_VERSION_ERROR: return "Version error";
        default: return "Unknown error";
    }
}

int main() {
    const char *source = "test data";
    unsigned long sourceLen = strlen(source) + 1;
    unsigned long destLen = compressBound(sourceLen);
    unsigned char *dest = (unsigned char *)malloc(destLen);
    
    int err = compress(dest, &destLen, (unsigned char *)source, sourceLen);
    if (err != Z_OK) {
        fprintf(stderr, "Compression error: %s\n", zlib_error_string(err));
        free(dest);
        return 1;
    }
    
    printf("Compression successful\n");
    free(dest);
    return 0;
}
8. 实际应用示例
// 压缩日志文件
#include <stdio.h>
#include <zlib.h>

int compress_log(const char *log_file) {
    FILE *source = fopen(log_file, "rb");
    if (!source) return 1;
    
    char gz_file[256];
    snprintf(gz_file, sizeof(gz_file), "%s.gz", log_file);
    gzFile dest = gzopen(gz_file, "wb9");  // 级别 9,最大压缩
    
    if (!dest) {
        fclose(source);
        return 1;
    }
    
    char buffer[8192];
    int bytes_read;
    
    while ((bytes_read = fread(buffer, 1, sizeof(buffer), source)) > 0) {
        if (gzwrite(dest, buffer, bytes_read) != bytes_read) {
            fclose(source);
            gzclose(dest);
            return 1;
        }
    }
    
    fclose(source);
    gzclose(dest);
    return 0;
}
9. 内存压缩示例
#include <stdio.h>
#include <zlib.h>
#include <stdlib.h>
#include <string.h>

int main() {
    // 原始数据
    const char *data = "This is a test string for compression. "
                       "It contains multiple words and sentences. "
                       "The goal is to demonstrate zlib compression.";
    unsigned long dataLen = strlen(data) + 1;
    
    // 压缩
    unsigned long compressedLen = compressBound(dataLen);
    unsigned char *compressed = (unsigned char *)malloc(compressedLen);
    
    if (compress(compressed, &compressedLen, 
                 (unsigned char *)data, dataLen) != Z_OK) {
        fprintf(stderr, "Compression failed\n");
        free(compressed);
        return 1;
    }
    
    printf("Original: %lu bytes\n", dataLen);
    printf("Compressed: %lu bytes\n", compressedLen);
    printf("Ratio: %.2f%%\n", 
           (1.0 - (double)compressedLen / dataLen) * 100);
    
    // 解压缩
    unsigned long decompressedLen = dataLen;
    unsigned char *decompressed = (unsigned char *)malloc(decompressedLen);
    
    if (uncompress(decompressed, &decompressedLen, 
                   compressed, compressedLen) != Z_OK) {
        fprintf(stderr, "Decompression failed\n");
        free(compressed);
        free(decompressed);
        return 1;
    }
    
    printf("Decompressed: %s\n", decompressed);
    
    free(compressed);
    free(decompressed);
    return 0;
}
10. 流式处理大文件
#include <stdio.h>
#include <zlib.h>

int compress_large_file(const char *input, const char *output) {
    FILE *in = fopen(input, "rb");
    gzFile out = gzopen(output, "wb6");  // 级别 6,平衡压缩
    
    if (!in || !out) {
        if (in) fclose(in);
        if (out) gzclose(out);
        return 1;
    }
    
    unsigned char buffer[64 * 1024];  // 64KB 缓冲区
    int bytes_read;
    unsigned long total_in = 0, total_out = 0;
    
    while ((bytes_read = fread(buffer, 1, sizeof(buffer), in)) > 0) {
        total_in += bytes_read;
        int written = gzwrite(out, buffer, bytes_read);
        if (written != bytes_read) {
            fclose(in);
            gzclose(out);
            return 1;
        }
        total_out += written;
    }
    
    printf("Compressed %lu bytes to %lu bytes\n", total_in, total_out);
    
    fclose(in);
    gzclose(out);
    return 0;
}

🧪 功能验证脚本

#!/bin/bash
# Zlib 库验证脚本

ZLIB_LIB="build-hnp/sysroot/lib"
ZLIB_INCLUDE="build-hnp/sysroot/include"

echo "=== Zlib 库验证 ==="

# 检查库文件
echo ""
echo "=== 库文件验证 ==="
for lib in libz.so.1.3.1 libz.a; do
    if [ -f "$ZLIB_LIB/$lib" ]; then
        echo "✓ $lib: 存在"
        file "$ZLIB_LIB/$lib"
        echo "文件大小: $(ls -lh "$ZLIB_LIB/$lib" | awk '{print $5}')"
    else
        echo "✗ $lib: 缺失"
    fi
done

# 检查符号链接
for link in libz.so libz.so.1; do
    if [ -L "$ZLIB_LIB/$link" ]; then
        echo "✓ $link: 符号链接 -> $(readlink "$ZLIB_LIB/$link")"
    else
        echo "✗ $link: 缺失"
    fi
done

# 检查头文件
echo ""
echo "=== 头文件验证 ==="
for header in zlib.h zconf.h; do
    if [ -f "$ZLIB_INCLUDE/$header" ]; then
        echo "✓ $header: 存在"
        echo "  文件大小: $(ls -lh "$ZLIB_INCLUDE/$header" | awk '{print $5}')"
    else
        echo "✗ $header: 缺失"
    fi
done

# 检查 pkg-config 文件
echo ""
echo "=== pkg-config 验证 ==="
if [ -f "build-hnp/sysroot/lib/pkgconfig/zlib.pc" ]; then
    echo "✓ zlib.pc: 存在"
    cat build-hnp/sysroot/lib/pkgconfig/zlib.pc
else
    echo "✗ zlib.pc: 缺失"
fi

# 注意:Zlib 是库,不提供命令行工具
echo ""
echo "注意:Zlib 是压缩库,不提供命令行工具"
echo "需要通过编程接口使用,或使用依赖 zlib 的工具(如 gzip)"

🐛 常见问题与处理

❌ 问题 1:下载失败

  • 🔍 症状:从 zlib.net 下载 zlib-1.3.1.tar.gz 失败
  • 🔎 原因:网络问题或服务器不可达
  • ✅ 解决方法
    • 使用通用下载规则中的 curl 兜底机制
    • 下载失败后清理坏归档再重试:rm -f build-hnp/zlib/download/zlib-1.3.1.tar.gz
    • 位置:build-hnp/utils/Makefrag(通用下载规则)

❌ 问题 2:交叉编译工具链问题

  • 🔍 症状configure 或编译失败,提示找不到编译器或工具
  • 🔎 原因:环境变量未正确设置或工具链路径不正确
  • ✅ 解决方法
    • 确保 OHOS_SDK_HOME 环境变量正确设置
    • 确保使用 create-hnp.sh 脚本执行构建(自动设置环境变量)
    • zlib 的 configure 脚本会自动检测交叉编译器
    • 位置:build-hnp/zlib/Makefile(使用 define_autotools_package 宏)

❌ 问题 3:静态/共享库选择

  • 🔍 症状:不确定应该构建静态库还是共享库
  • 🔎 原因:不同应用场景需要不同的库类型
  • ✅ 解决方法
    • 当前配置:--enable-shared(构建共享库)
    • 如果需要静态库,可以添加 --enable-static
    • 如果需要仅静态库,可以使用 --disable-shared --enable-static
    • 位置:build-hnp/zlib/Makefile:8

❌ 问题 4:链接错误

  • 🔍 症状:编译使用 zlib 的程序时出现链接错误
  • 🔎 原因:未正确链接 zlib 库
  • ✅ 解决方法
    • 使用 pkg-config:gcc -o program program.c $(pkg-config --cflags --libs zlib)
    • 手动链接:gcc -o program program.c -lz
    • 确保库路径正确:export LD_LIBRARY_PATH=/path/to/lib:$LD_LIBRARY_PATH

❌ 问题 5:版本兼容性

  • 🔍 症状:使用不同版本的 zlib 编译的程序不兼容
  • 🔎 原因:zlib 的 ABI 在不同版本间可能不兼容
  • ✅ 解决方法
    • 确保使用相同版本的 zlib 编译和运行
    • 检查 zlib 版本:pkg-config --modversion zlib
    • 使用符号版本控制确保兼容性

🔄 重建与扩展

  • 🔧 重建单包

    make -C build-hnp rebuild-zlib  # 触发子包重新编译并刷新 .stamp
    
  • 🧹 清理

    make -C build-hnp clean  # 清理 sysroot、所有 .stamp 和 PKGS_MARKER
    
  • 📦 扩展:Zlib 是许多应用程序的基础依赖(如 curllibarchivegit 等)

  • 🔄 自动重建机制

    • 修改 PKGS 后,check-pkgs 会自动检测变化并触发重新构建
    • 新增外部 HNP 包到 external-hnp 目录后,会自动合并到 base.hnp

💡 实践建议

  • 🔧 压缩级别选择:根据应用场景选择合适的压缩级别
    • 实时场景:级别 1-3(快速压缩)
    • 存储场景:级别 6-9(高压缩比)
    • 默认场景:级别 6(平衡)
  • 🚀 性能优化:对于大文件,使用流式 API 可以显著减少内存占用
  • 📦 依赖管理:Zlib 是许多包的基础依赖,确保正确构建和安装
  • 🔗 压缩栈建设:建议与其他压缩库(zstd/lz4/xz)一起构建,随后构建 libarchive
  • 🌐 工具使用:虽然 zlib 本身不提供命令行工具,但可以使用依赖 zlib 的工具(如 gzipcurl 等)

📝 结论与建议

  • ✅ 本次已在 aarch64 环境下完成 Zlib 1.3.1 的交叉编译与打包,zlib 库已安装到 sysroot 并纳入 HNP 包。
  • 💡 为保证构建稳定
    • 固定可靠的上游镜像,避免下载阶段随机失败(已实现自动回退机制)
    • 使用 Autotools 构建系统,支持共享库和静态库
    • zlib 是库,不提供命令行工具,需要通过编程接口使用
    • 利用 check-pkgs 机制自动检测包列表变化,无需手动清理
    • 将 Zlib 与其他压缩库(zstd/lz4/xz)一并构建,随后构建 libarchive,提升归档/解压生态的完整性
    • 根据后续依赖选择静态或共享产物,兼顾体积与兼容性

📚 以上为 Zlib 构建的深度解读与实践记录。

Logo

开源鸿蒙跨平台开发社区汇聚开发者与厂商,共建“一次开发,多端部署”的开源生态,致力于降低跨端开发门槛,推动万物智联创新

更多推荐