关于llvm和clang开发环境的配置之前已经发过一篇,当时是因为llvm官网给的文档省略了很多重要信息,需要额外补充一些信息才能完成环境配置。
时隔许久,重新打开llvm官网,发现他们的文档已经更新了,写的非常详细,专门列举了一节用来解释如何在远古linux发行版上编译安装llvm+clang的方法。内容大体上和我上篇差不多,但是有些细节需要注意,是我上篇内容中遗漏的,并且当时对于cmake的rpath设置也并不完全掌握。
注意:本文是针对有需要在古老的linxu服务器上进行llvm+clang环境开发的指导说明,如果你的发行版在2017年以后,基本不需要参考这篇文档。本文档也是傻瓜式教程,帮助新手快速部署环境,免于折腾。
关于宿主机的环境
根据llvm官网文档的介绍,想要成功编译llvm项目,必须的环境要求:
- Clang 5.0
- Apple Clang 10.0
- GCC 7.1
- Visual Studio 2019 16.7
以GCC7.1为例,官方给出的步骤为:
% gcc_version=7.1.0
% wget https://ftp.gnu.org/gnu/gcc/gcc-${gcc_version}/gcc-${gcc_version}.tar.bz2
% wget https://ftp.gnu.org/gnu/gcc/gcc-${gcc_version}/gcc-${gcc_version}.tar.bz2.sig
% wget https://ftp.gnu.org/gnu/gnu-keyring.gpg
% signature_invalid=`gpg --verify --no-default-keyring --keyring ./gnu-keyring.gpg gcc-${gcc_version}.tar.bz2.sig`
% if [ $signature_invalid ]; then echo "Invalid signature" ; exit 1 ; fi
% tar -xvjf gcc-${gcc_version}.tar.bz2
% cd gcc-${gcc_version}
% ./contrib/download_prerequisites
% cd ..
% mkdir gcc-${gcc_version}-build
% cd gcc-${gcc_version}-build
% $PWD/../gcc-${gcc_version}/configure --prefix=$HOME/toolchains --enable-languages=c,c++
% make -j$(nproc)
% make install
以上的步骤中,$HOME
变量可以自行替换为想要在宿主机上部署高版本GCC的路径,这个路径可以任选,除了/usr目录下;建议选择/opt/toolchains
这个路径。如果用户不具备root权限,安装在自己加目录下也是可以的。
安装好之后,可以执行$HOME/toolchains/bin/g++ --version
查看和确认版本。
之后,进入llvm项目源码目录,也就是从github下载的llvm-project-15.0.7.src.tar.xz
这种压缩包。
注意,我们需要进入的目录是解压后的目录内的llvm目录,比方说,解压后产生的文件夹叫llvm-project-15.0.7.src
,那么我们就需要进入cd llvm-project-15.0.7.src/llvm
这个目录下面。
然后,在该目录下执行cmake
命令:
CC=/opt/toolchains/bin/gcc \
CXX=/opt/toolchains/bin/g++ \
cmake -H. -Bbuild \
-DCMAKE_CXX_LINK_FLAGS="-Wl,-rpath,/opt/toolchains/lib64 -L/opt/toolchains/lib64" \
-DLLVM_LOCAL_RPATH="/opt/toolchains/lib64" \
-DCMAKE_BUILD_TYPE=Release \
-DLLVM_ENABLE_PROJECTS="clang;clang-tools-extra" \
-DLLVM_ENABLE_RUNTIMES="libcxx;libcxxabi"
注意:
- 此处
CC
和CXX
环境变量指定的工具链路径是刚才上面安装高版本GCC的路径,千万不要搞错了。 CMAKE_CXX_LINK_FLAGS
非常关键,用于指定连接时的libstdc++.so
的路径。DLLVM_LOCAL_RPATH
这个变量是看了llvm官网更新后的文档才了解到的;因为llvm默认所有的rpath都指向’$ORIGIN/…/lib’,也就是当前可执行文件上一层的lib目录;使用这个变量后,可以添加固定路径的rpath,这样在本机编译出的可执行文件,就无需设置LD_LIBRARY_PATH
环境变量就可以直接执行。如果编译后的文件需要在其他机器上运行,则只需要拷贝libstdc++.so
到可执行文件上层的lib目录下。LLVM_ENABLE_PROJECTS
和LLVM_ENABLE_RUNTIMES
这两个变量是llvm官方文档忽略的,他给出的cmake命令例子里面没有这两个变量,所以编译出来只有llvm套件,没有clang和其他工具;如果要用clang,建议同时编译出libcxx(也就是libc++.so
)。
按照官方说法,将目录设置成如下形式,即可正常运行clang编译出来的可执行文件:
.
├── bin
│ ├── a.out
│ └── hello.cc
└── lib
├── libc++abi.so.1 -> /usr/local/lib/x86_64-unknown-linux-gnu/libc++abi.so.1
└── libc++.so.1 -> /usr/local/lib/x86_64-unknown-linux-gnu/libc++.so.1
测试代码:
# cat hello.cc
#include <iostream>
#include <string>
#include <functional>
#include <memory>
void foo(std::function<void()> f)
{ f(); }
int main(int argc, char** argv)
{
auto msg = std::make_unique<std::string>("hello wolrd"); // make_unique
foo( [n = std::shared_ptr<std::string>(msg.release())]{ std::cout << *n << std::endl;}); // 移动捕获
return 0;
}
但是其实是有问题的,需要分情况讨论:
- 当执行
clang++ hello.cc
的时候,默认搜索的是系统自带的GCC,是不支持make_unique
和移动捕获的; - 当执行
clang++ -stdlib=libc++ hello.cc
的时候,默认是不带rpath的,因此会提示找不到libc++.so
;
想要让可执行文件能够正确执行,在上述目录结构的情况下,使用以下命令编译:
clang++ -stdlib=libc++ -Wl,-rpath,\$ORIGIN\/..\/lib hello.cc
这样手动给a.out增加一个rpath就可以执行了。
因此,总结一下我们的需求,如果想要:
- 利用clang帮助解决编译问题,则不需要带
-stdlib=libc++
使用,仅仅让clang做一个用户友好的编译器前端; - 如果要利用llvm后端,则必须指定rpath,链接到
libc++.so
和libc++abi.so
,但是这样编译产出的可执行文件就同时依赖libc++.so
,libc++abi.so
和libstdc++.so.6
(高版本的);如果要放到其他古董机上执行,需要把这三个动态库都拷过去,并且指定rpath或者LD_LIBRARY_PATH环境变量。