由于父亲大人病重,自上月上旬以来基本停止一切开发方向的活动,无论是开源库也好、工具也好,还是 blog 文字也好,都基本停滞。偶尔会有一些不费事的更新,如同本文内容这样的。

要等。

想要有尊严地活着,是多么地不容易啊。

由于时间关系,本文暂时仅作列举而不讨论编程(脚本)细节。

安装 gcc-13

上个月某一天,更新 homebrew 时偶然注意到 gcc-13 都发布了。尽管它有些啥新东西这阵子根本没关注,但也还是更了。

然后就涉及到 Linux Server 上的问题。我主要的 server 平台基本都是 Ubuntu 22.04 Server,这比较省心。然而这上面经过研究发现无法通过 apt/ppa 安装 gcc-13。

起初以为过些时日 ppa 上就会更新。但它们没有。于是抽空去检查了下,没太认真,得到的结论是:如果升级到 Ubuntu 23.04 Server 的话,那么就可以直接安装 gcc-13,也可以通过 ppa 安装;但对于这之前到发行版,就没有可能性了,除非通过源码自行编译。

Ubuntu 23.04 - apt

在 Ubuntu 23.04 上,可以直接安装 gcc-13。这有两种方法:借助于 build-essential 伪 package;直接安装 gcc-13。

它们的 bash 指令分别如下:

1
2
3
4
sudo apt-get install build-essentail


sudo apt-get install g++-13 gcc-13-locales g++-multilib

两者没有什么区别,但后者或许会有 alternate links 破损的可能性,不建议那么做,除非你手工 update-alternate。

Ubuntu 22.04 and earlier - source

在 Ubuntu 早期版本,或者其他 Linux 发行本,你可能需要从源码编译 gcc,这会需要一些附加操作来解决 links 问题,即将编译后的二进制融合到系统中的问题。

本文中针对 Ubuntu Server 做出相应解决,其他发行版则需要自行研究。

编译 gcc 源码

首先是 gcc 源码的编译与构建,其本身是可以精简如下:

1
2
3
4
5
6
7
8
9
tar xzf gcc-4.6.2.tar.gz
cd gcc-4.6.2
./contrib/download_prerequisites
cd ..
mkdir objdir
cd objdir
$PWD/../gcc-4.6.2/configure --prefix=$HOME/GCC-4.6.2 --enable-languages=c,c++,fortran,go
make
make install

参阅 https://gcc.gnu.org/wiki/InstallingGCC

但,实际上用在 devops 中的脚本是不可能这么简单的,还需要检测一系列的冥等性问题。所以我这里给出一个范本:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
install_gcc_13_src() {
	echo && headline "  - $(fn_name_dyn)"

	install_gcc # bundled gcc first for building gcc-13 from sources
	ig13src
	upd_alter_gcc
}


install_gcc() {
	echo && headline "$(fn_name_dyn)"

	# $SUDO add-apt-repository --yes -u ppa:ubuntu-toolchain-r/test

	# install_packages 等价于 sudo apt-get install -y
  install_packages build-essential gcc-locales g++-multilib \
		git \
		gdb gdbserver \
    automake bison flex xsltproc libfl-dev \
		libtool pkg-config \
		make || :

	upd_alter_gcc
}

ig13src() {
	SRCDIR=$HOME/gcc-source
	INSTDIR=$HOME/gcc-install
	BUILDDIR=./gcc-1x-build
	VER=gcc-13
	VERSION=gcc-13.1.0
	# VER=gcc-12
	# VERSION=gcc-12.2.0

	[ -d $SRCDIR ] || {
		# tip set git insteadOf
		git config --global url."https://gcc.gnu.org/".insteadOf https://gcc.gnu.org
		# tip clone gcc sources
		git clone https://gcc.gnu.org/git/gcc.git $SRCDIR
	}
	cd $SRCDIR/
	# git branch -a | grep $VER
	# git checkout remotes/origin/releases/$VERSION
	git checkout releases/$VERSION

	# instead of: ./contrib/download_prerequisites
	apt-cache search MPFR | grep dev
	$SUDO apt-get install -y libmpfrc++-dev
	apt-cache search MPC | grep dev
	$SUDO apt-get install -y libmpc-dev
	apt-cache search GMP | grep dev
	$SUDO apt-get install -y libgmp-dev
	$SUDO apt-get install -y gcc-multilib

	echo && gcc -v && echo && ls -l $(which gcc) && echo

	[ -d $BUILDDIR ] || mkdir $BUILDDIR
	cd $BUILDDIR

	[ -f Makefile ] ||
		../configure --prefix=$INSTDIR --enable-languages=c,c++ # ,fortran,go

	if [ ! -x $INSTDIR/bin/gcc ]; then
		[ -f /swapfile ] || {
			# $SUDO dd if=/dev/zero of=/swapfile bs=64M count=16  # 1GB swap file
			$SUDO dd if=/dev/zero of=/swapfile bs=64M count=64 # 4GB swap file
			# $SUDO dd if=/dev/zero of=/swapfile bs=64M count=320 # 20GB swap file
			$SUDO mkswap /swapfile
		}
		$SUDO swapon --show | grep -q '/swapfile' || $SUDO swapon /swapfile
		$SUDO swapon --show

		make -j4 # decerase the cores so that reduce the memery usages to avoid out of memory whenn compiling
		make install

		[ -f /swapfile ] && $SUDO swapoff /swapfile && $SUDO rm /swapfile
	fi

	ig13local
}

ig13local() {
	PREFIX=/usr/local/lib/gcc-$VERSION
	if [ ! -L $PREFIX ]; then
		# $SUDO cp -R $INSTDIR $PREFIX
		$SUDO ln -sf $INSTDIR $PREFIX
		$SUDO ln -sf $PREFIX/bin/gcc /usr/local/bin/gcc-13
		$SUDO ln -sf $PREFIX/bin/g++ /usr/local/bin/g++-13
		#
		# To run an executable compiled with this gcc, PRELOAD its lib64/ directory:
		# LD_LIBRARY_PATH=/usr/local/lib/gcc-13.1.0/lib64 ./hello
		[ -f /etc/profile.d/gcc-$VERSION ] || $SUDO echo "LD_LIBRARY_PATH=$PREFIX/lib64" >/etc/profile.d/gcc-$VERSION
	fi
	upd_alter_gcc
}

upd_alter_gcc() {
	$SUDO update-alternatives --list gcc || :
	$SUDO update-alternatives --list g++ || :
	W=/usr/bin/gcc-7 && [ -x $W ] && $SUDO update-alternatives --install /usr/bin/gcc gcc $W 90 --slave /usr/bin/g++ g++ "${W//gcc/g++}" && V="$W"
	W=/usr/bin/gcc-9 && [ -x $W ] && $SUDO update-alternatives --install /usr/bin/gcc gcc $W 90 --slave /usr/bin/g++ g++ "${W//gcc/g++}" && V="$W"
	W=/usr/bin/gcc-10 && [ -x $W ] && $SUDO update-alternatives --install /usr/bin/gcc gcc $W 90 --slave /usr/bin/g++ g++ "${W//gcc/g++}" && V="$W"
	W=/usr/bin/gcc-11 && [ -x $W ] && $SUDO update-alternatives --install /usr/bin/gcc gcc $W 90 --slave /usr/bin/g++ g++ "${W//gcc/g++}" && V="$W"
	W=/usr/bin/gcc-12 && [ -x $W ] && $SUDO update-alternatives --install /usr/bin/gcc gcc $W 90 --slave /usr/bin/g++ g++ "${W//gcc/g++}" && V="$W"
	W=/usr/bin/gcc-13 && [ -x $W ] && $SUDO update-alternatives --install /usr/bin/gcc gcc $W 90 --slave /usr/bin/g++ g++ "${W//gcc/g++}" && V="$W"

	W="$(which gcc-11)" && WX="$(which g++-11)"
	[ "$W" != "" ] && [ -x $W ] && $SUDO update-alternatives --install /usr/bin/gcc gcc $W 60 --slave /usr/bin/g++ g++ $WX && V="$W"
	W="$(which gcc-12)" && WX="$(which g++-12)"
	[ "$W" != "" ] && [ -x $W ] && $SUDO update-alternatives --install /usr/bin/gcc gcc $W 50 --slave /usr/bin/g++ g++ $WX && V="$W"
	W="$(which gcc-13)" && WX="$(which g++-13)"
	[ "$W" != "" ] && [ -x $W ] && $SUDO update-alternatives --install /usr/bin/gcc gcc $W 49 --slave /usr/bin/g++ g++ $WX && V="$W"

	echo
	# echo 1 | $SUDO update-alternatives --config gcc
	[ "$V" != "" ] && $SUDO update-alternatives --set gcc "$V"

	echo
	gcc --version
	echo
	g++ -v
	echo
}

headline, install_packages 等等是 devops 工具脚本函数,大部分源码已经公开在 bash.sh 中,可以参阅,亦可自行改写。

需要说明的内容如下:

make -j4

我是在一台 vagrant VM 上调试上述脚本的,该 VM 拿到了 8 core 8GB Memory,但一开始编译总是崩掉。后来我注意到 swap file 问题,所以 make -j4 的前面和后面分别有打开交换文件、回收交换文件的脚本嵌入。除此之外,使用 make -j 仍然继续崩,所以必须用 -j4 来限制 make 使用的 CPU 核心数,否则依然会内存不足。

如果发生内存不足,那就要继续缩减核心数。但 VM 不要小于 4 core 8GB,否则恐怕减无可减都无法完成编译。

ig13local

为了防止污染,我们首先将 gcc 编译到一个临时的安装目录。即设置 --prefix=$HOME/gcc13-install 完成 configure,从而让 make install 将最终的有效安装文件全部复制到 $HOME/gcc13-install 中,再通过 ig13local 来真正安装到系统中。

ig13local 的细节值得注意,针对 Ubuntu / Debian 体系,它要将安装文件放到 /usr/local/lib/gcc-13.1.0 之下,然后在 /usr/local/bin/ 中建立 gcc-13, g++-13 的符号链接,最后通过 update-alternative config 将这两个符号链接绑定到 /usr/bin/gcc 之中。

/usr/bin/gcc 会跟踪多个版本的 gcc ,并选择活动的替代品来提供标准 gcc 功能。

这里原本有一堆需要解说的内容。

但鉴于我没有时间讲解,那就自己看源码、自己理解吧。

小结

暂时写这么多。

需要完整的 bootstrap.sh 源码的话,留言或者开 issue,如果真的有那么多人需要,我就抽空整理 vagrant folder 发布出来。

留下评论