XX客户定制编译环境经验总结

1、问题背景

XX原有的应用软件已经适配了特定的软件编译环境,但该环境与openEuler社区发行版差异较大,该问题长时间未得到解决,直接影响到业务。鉴于此,openEuler社区派出多名技术专家耗时2周帮助XX完成定制编译环境,节省了数十人月的人力。

2、定制过程

第一步 编译环境选型

glibc和gcc是系统最核心的两个软件组件,系统上其他所有软件几乎都直接或间接依赖于这两个软件,因此首先选择正确的gcc和glibc版本。

XX的众多应用的编译构建都是基于定制的gcc及glibc,所以在服务器操作系统切换到openEuler之后,需要构建一个欧拉底包+定制的gcc和glibc混杂而成的构建环境,这样既能保障操作系统的连续性,也能让应用迁移的过程尽量平滑。

当前选型的是gcc12.3和glibc2.33。如前所述,它是系统最核心的组件,依赖它们的还有包括systemd、nss等超过30款的软件。所以XX计划在openEuler社区发行版22.03版本上继续沿用原来的gcc12.3、glibc2.33等软件,但是当前所有的openEuler社区发行版本均未适配过glibc2.33版本。如表1所示:

表1 - openEuler社区发行版及对应软件版本同XX期望版本对比


只有openEuler 22.03支持glibc2.34,与目标版本最靠近,于是选择在openEuler22.03上面编译glibc2.33,但多次编译均不成功,导致后续依赖包编译动作无法执行,进而影响自研软件的直接移植。方案如下图所示:

图1 - 原始方案

原因分析:由于glibc编译时需要依赖编译环境,且只能基于高于目标版本或与目标相同版本的****glibc 环境 ,因此XX在glibc2.34环境上编译glibc2.33无法成功。

鉴于openEuler 20.03上支持glibc 2.28,满足升级glibc的向后兼容的条件,于是和沟通了解的核心目标是要在****openEuler 上面运行现有的业务软件和自研库,业务软件和自研库需要有glibc2.33 、gcc12.3 和一些依赖软件包的环境,对openEuler 版本无特别要求 ,只要保证业务软件能正常运行即可。即接受从openEuler22.03变更为openEuler20.03;如下图所示:


图2 - 目标版本变更

基于以上思考和选择,最终的编译基座定为openEuler20.03+glibc2.33+gcc12.3 ;其他库和软件基于此之上进行编译适配,整体编译方案如下图所示:


图3 - 变更后的整体编译方案

第二步 glibc和gcc的编译


图4 - glibc和gcc的编译过程

编译步骤如下:

  1. 基于openEuler 20.03 版本(原生支持glibc2.28和gcc7.3),升级glibc版本2.28到2.33,编译glibc2.33:
    i.选定编译系统为openEuler20.03 之后,编译glibc2.33过程中需要编译工程文件,因glibc 2.33没有适配过任何版本的openEuler,所以需要针对glibc 2.33制作对应的工程文件;
    ii. openEuler22.03已有的glibc2.34与glibc2.33版本接近,因此选择使用glibc2.34的工程文件;glibc2.34工程文件中包含245个patch,这些patch文件是针对glibc2.34生成的,不能用于glibc2.33,因为glibc2.33与glibc2.34代码的行数存在差异。因此需要删除工程文件中以patch结尾的文件,然后删除工程文件中引入patch的行的代码;
    iii. glibc2.34与glibc2.33的源码文件存在差异,需要逐个修改工程文件中对差异文件的处理方法;

编译后的新环境为:
openEuler 20.03 +glibc2.33+gcc7.3,其中glibc2.33基于gcc7.3编译;

2)基于1的环境,升级gcc7.3为gcc12.3,编译gcc12.3。
编译后的新环境为:
openEuler 20.03 +glibc2.33+gcc12.3,其中gcc12.3基于glibc2.33编译;

3)基于2中的环境重新编译glibc2.33,
编译后的新环境为:
openEuler 20.03 +glibc2.33+gcc12.3;其中glibc2.33基于gcc12.3编译。

4)基于3中的环境重新编译gcc12.3,完成这两个软件包的循环编译
循环编译原因:gcc和glibc相互依赖,为了保证新生成的gcc和glibc都不依赖原操作系统上的glibc2.28和gcc7.3,需要进行循环编译。

编译的最终效果为:确保glibc和gcc都是在glibc2.33和gcc12.3的环境上编译出来的并且得出一个正确的编译环境: openEuler 20.03 +glibc2.33+gcc12.3

第三步 30+基础软件编译

完成了glibc和gcc的编译之后,接着需要将编译成功得到的glibc和gcc的源码和工程文件上传到gitee个人仓库中,同时将软件信息填入下方的表2-软件仓库信息。接着,对systemd等30+基础软件进行编译。

image

2 - 软件仓库信息

挑战:
基础软件编译的过程工作量巨大,单个软件往往与其他库和软件存在复杂的依赖关系,某些软件甚至需要循环编译。下图以libselinux为例进行说明,通过梳理目标软件libselinux与周边软件的依赖关系发现,对其进行成功编译需要同时处理20+条依赖,其中还存在libselinux->gcc->glibc->systemd->libselinux这样的多层循环依赖,人工处理极其复杂,且本次需要编译的目标软件包数量达到30以上。

图5-libselinux的依赖关系(此图由EulerMaker自动生成)

解决办法:
引入openEuler社区的构建平台–EulerMaker来解决复杂的依赖关系和巨大的工作量问题。

编译思路:


图6 - 30+基础软件编译流程

编译流程说明如下:
1)基础软件的编译大部分都依赖于上一步构建好的glibc、gcc,需要编译成功所使用的glibc和gcc的源码和工程文件上传到gitee个人仓库中,同时将软件信息填入下方的表2-软件仓库信息。
2)先从openEuler官方gitee仓库中依次找到如下表3-待编译软件清单中的软件,拉取代码到gitee个人仓库并整理填入表2-软件仓库信息,其中分支默认填写openEuler-20.03 ;
3)注册并登录EulerMaker,继承一个openEuler20.03的工程;
4)在刚继承的EulerMaker工程上使用表2-软件仓库信息添加软件包并进行构建;
5)查看EulerMaker构建失败的软件包日志,修改gitee个人仓库的工程文件,如果修改spec文件仍然有问题,可以修改软件包代码分支(比如openEuler22.03或24.03等),然后EulerMaker使用新分支代码进行构建,直到EulerMaker构建通过;
6)刷新表2-软件仓库信息,EulerMaker最后全部构建成功的软件包仓库信息即为表4—最终软件仓库信息。

3 - 待编译软件清单

下表即为EulerMaker最后全部构建成功的32个软件包仓库信息:

序列 包名 分支版本 个人仓库
1 attr openEuler-20.03 huanglg/attr
2 avahi openEuler-20.03 huanglg/avahi
3 bzip2 openEuler-20.03 huanglg/bzip2
4 curl openEuler-20.03 huanglg/curl
5 cyrus-sasl openEuler-20.03 huanglg/cyrus-sasl
6 dbus openEuler-20.03 huanglg/dbus
7 dmidecode openEuler-20.03 huanglg/dmidecode
8 e2fsprogs openEuler-20.03 huanglg/e2fsprogs
9 elfutils openEuler-24.03-LTS huanglg/elfutils
10 keyutils openEuler-20.03 huanglg/keyutils
11 krb5 openEuler-20.03 huanglg/krb5
12 libcap openEuler-22.03 huanglg/libcap
13 libgcrypt openEuler-20.03 huanglg/libgcrypt
14 libgpg-error openEuler-20.03 huanglg/libgpg-error
15 libidn openEuler-20.03 huanglg/libidn
16 libpciaccess openEuler-20.03 huanglg/libpciaccess
17 libselinux openEuler-20.03 huanglg/libselinux
18 libsepol openEuler-20.03 huanglg/libsepol
19 libssh2 openEuler-20.03 huanglg/libssh2
20 libtirpc openEuler-20.03 huanglg/libtirpc
21 libxml2 openEuler-20.03 huanglg/libxml2
22 lvm2 openEuler-24.03-LTS huanglg/lvm2
23 lz4 openEuler-20.03 huanglg/lz4
24 nspr openEuler-24.03-LTS huanglg/nspr
25 nss openEuler-20.03 huanglg/nss
26 nss-util openEuler-20.03 包含在nss包中
27 openldap openEuler-20.03 huanglg/openldap
28 pcre openEuler-20.03 huanglg/pcre
29 systemd openEuler-22.03 huanglg/systemd
30 xz openEuler-20.03 huanglg/xz
31 yajl openEuler-20.03 huanglg/yajl
32 openssl openEuler-20.03 huanglg/openssl

4– 最终软件仓库信息

小结:
glibc+gcc+30多个基础软件的构建,且涉及到glibc和gcc的循环编译,工作量很大,但是在提前测试好之后再通过EulerMaker构建平台只需要3个小时左右就能完成一次全部的构建工作,大大提高了编译效率,方便使用和后续的工程化构建。
如需进一步了解EulerMaker,可以访问openEuler开源社区:

第四步 测试

新开一台主机(或虚拟机)安装openEuler20.03系统且保障主机可以上网,只配置一个eulermaker的repo源,重建yum缓存后,使用yum update来更新系统,重启主机后,检查glibc、gcc和30+个基础软件的版本和基础命令的使用是否报错。具体操作步骤如下:

1)配置一个eulermaker的repo源,如下所示:
# cat /etc/yum.repos.d/eulermaker.repo
[eulermaker]
name=eulermaker
baseurl=https://eulermaker.compass-ci.openeuler.openatom.cn/api/ems3/repositories/2003sp4-20241029/openEuler%3A20.03/aarch64/
enabled=1
gpgcheck=0
2)然后重建缓存:
# yum clean all && yum makecache
3)接着更新包:
# yum update -y
4)最后回显如下:
这里升级成功的软件和之前EulerMaker构建好的软件版本是对应的。
Upgraded:
cpp-12.3.1-35.oe2003sp4.aarch64
curl-7.71.1-37.oe2003sp4.aarch64
cyrus-sasl-2.1.27-16.oe2003sp4.aarch64
cyrus-sasl-lib-2.1.27-16.oe2003sp4.aarch64
dbus-1:1.12.16-23.oe2003sp4.aarch64
dbus-common-1:1.12.16-23.oe2003sp4.noarch
dbus-daemon-1:1.12.16-23.oe2003sp4.aarch64
dbus-libs-1:1.12.16-23.oe2003sp4.aarch64
dbus-tools-1:1.12.16-23.oe2003sp4.aarch64
device-mapper-8:1.02.195-11.oe2003sp4.aarch64
device-mapper-event-8:1.02.195-11.oe2003sp4.aarch64
e2fsprogs-1.45.6-21.oe2003sp4.aarch64
e2fsprogs-help-1.45.6-21.oe2003sp4.noarch
elfutils-0.190-4.oe2003sp4.aarch64
elfutils-default-yama-scope-0.190-4.oe2003sp4.noarch
elfutils-libelf-0.190-4.oe2003sp4.aarch64
elfutils-libs-0.190-4.oe2003sp4.aarch64
gcc-12.3.1-35.oe2003sp4.aarch64
glibc-2.33-2.oe2003sp4.aarch64
glibc-common-2.33-2.oe2003sp4.aarch64
glibc-devel-2.33-2.oe2003sp4.aarch64
krb5-1.18.2-14.oe2003sp4.aarch64
krb5-libs-1.18.2-14.oe2003sp4.aarch64
libcap-2.61-7.oe2003sp4.aarch64
libcurl-7.71.1-37.oe2003sp4.aarch64
libfdisk-2.35.2-16.oe2003sp4.aarch64
libgcc-12.3.1-35.oe2003sp4.aarch64
libgomp-12.3.1-35.oe2003sp4.aarch64
libgpg-error-1.38-3.oe2003sp4.aarch64
libstdc+±12.3.1-35.oe2003sp4.aarch64
lvm2-8:2.03.21-11.oe2003sp4.aarch64
lvm2-help-8:2.03.21-11.oe2003sp4.noarch
nspr-4.35.0-3.oe2003sp4.aarch64
openldap-2.4.50-9.oe2003sp4.aarch64
openssl-1:1.1.1f-36.oe2003sp4.aarch64
openssl-help-1:1.1.1f-36.oe2003sp4.noarch
openssl-libs-1:1.1.1f-36.oe2003sp4.aarch64
systemd-249-86.oe2003sp4.aarch64
systemd-help-249-86.oe2003sp4.noarch
systemd-libs-249-86.oe2003sp4.aarch64
systemd-udev-249-86.oe2003sp4.aarch64
xz-5.2.5-4.oe2003sp4.aarch64
xz-libs-5.2.5-4.oe2003sp4.aarch64

Complete!
5)重启主机:
# reboot
6)对已更新的软件进行测试:
# gcc --version
gcc (GCC) 12.3.1 (openEuler 12.3.1-35.oe2003sp4)
Copyright (C) 2022 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

# ldd --version
ldd (GNU libc) 2.33
Copyright (C) 2021 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
Written by Roland McGrath and Ulrich Drepper.

# curl --version
curl 7.71.1 (aarch64-openEuler-linux-gnu) libcurl/7.71.1 OpenSSL/1.1.1f-fips zlib/1.2.11 brotli/1.0.7 libidn2/2.3.0 libpsl/0.21.1 (+libidn2/2.3.0) libssh/0.9.4/openssl/zlib nghttp2/1.41.0
Release-Date: 2020-07-01
Protocols: dict file ftp ftps gopher http https imap imaps ldap ldaps pop3 pop3s rtsp scp sftp smb smbs smtp smtps telnet tftp
Features: AsynchDNS brotli GSS-API HTTP2 HTTPS-proxy IDN IPv6 Kerberos Largefile libz NTLM NTLM_WB PSL SPNEGO SSL TLS-SRP UnixSockets

# rpm -qa|grep dbus
dbus-libs-1.12.16-23.oe2003sp4.aarch64
dbus-common-1.12.16-23.oe2003sp4.noarch
dbus-1.12.16-23.oe2003sp4.aarch64
dbus-tools-1.12.16-23.oe2003sp4.aarch64
dbus-daemon-1.12.16-23.oe2003sp4.aarch64

# openssl version
OpenSSL 1.1.1f 31 Mar 2020
后面不再逐一罗列,主要检查软件版本和基础命令使用是否会报错。

第五步 输出成果

向提供如下成果:

  1. EulerMaker构建好的rpm包,提供仓库地址给使用;
    见如下链接:
    https://eulermaker.compass-ci.openeuler.openatom.cn/api/ems3/repositories/2003sp4-20241029/openEuler%3A20.03/aarch64/
  2. 提供编译过程中的FAQ文档;
  3. 分配EulerMaker工程的权限,方便后续构建;
  4. 提供已构建好的软件包信息(包括包名、仓库及分支等信息)。
    见表4-最终软件仓库信息

3、 附件1 FAQ

1)gcc编译报debugsource.txt为空文件


解决办法:
在spec文件的开头添加如下行:

%global debug_package %{nil}

2)编译glibc报Werror-use-after-free


解决办法:
按下图内容修改spec文件。

3)编译glibc报C.utf8找不到


解决办法:
按下图内容修改spec文件。

4)编译libnsl报错


解决办法:
按下图内容修改spec文件。

5)编译systemd报错


解决办法:
参考Issues · msys2/MINGW-packages · GitHub 的最新版本进行构建

6)编译systemd报错:debugsourcefiles.list报空文件


解决办法:
在spec文件的开头添加如下行:

%global debug_package %{nil}

7)编译nss报错


解决办法:
参考:
https://groups.google.com/g/linux.debian.maint.glibc/c/L164IF-7_oI

Patch1: posix-Fix-attribute-access-mode-on-getcwd-BZ-27476.patch
给glibc打上这个Patch,然后重新编译nss解决。

8)编译libcap报错


解决办法:
在spec文件的开头添加如下行:

%global debug_package %{nil}

3 个赞

写得太详细了,大赞呀 :+1: