工程编译基本常识
1 简介
编译器(compiler)是一种特殊的程序,它将用某种程序语言(比如说Pascal、C++)编写的计算机代码转换成另外一种目标语言。
一般来说,编译器可以有以下处理过程:1)预处理(pre-processing);2)词法分析(lexical analysis);3)解析(parsing);4)语义分析(semantic analysis);5)将输入程序转换成中间表示;6)代码优化(code optimization);7)代码生成(code generation)。
如下图所示,可以将它们分成三个阶段:
- Front end: 主要负责词法分析和语法分析,将源代码转换成抽象语法树;
- Middle end(Optimizer): 对中间表示优化,使代码更加高效;
- Back end:将经过优化的中间代码转换成针对各平台的机器代码。
2 编译器
2.1 GCC
GCC(GNU Compiler Collection: GNU编译器套装)是一套由GNU开发的编译器,我们常用的命令有gcc和g++。无论是gcc还是g++,它们的定位都是driver,driver负责调用编译器将源码编译到汇编代码,再调用as,将汇编代码转换成二进制的机器码,最后调用ld将二进制代码拼接起来。可以用CSAPP中这张图来理解。
2.2 LLVM
LLVM最初表示Low Level Virtual Machine,现在它不再局限在这个意思,而是表示一个项目的名称,这个项目包含一系列模块化、可复用的编译器和工具链。从(官网)可以看到,整个LLVM项目包括LLVM Core
、Clang
、OpenMP
等11个部分。其中对LLVM Core的描述为:
The LLVM Core libraries provide a modern source- and target-independent optimizer, along with code generation support for many popular CPUs (as well as some less common ones!) These libraries are built around a well specified code representation known as the LLVM intermediate representation ("LLVM IR"). The LLVM Core libraries are well documented, and it is particularly easy to invent your own language (or port an existing compiler) to use LLVM as an optimizer and code generator.
可以看到LLVM Core主要提供了optimizer和code generation support,充当的角色为中间端和后端。
2.3 Clang
LLVM官网对Clang的描述
Clang is an "LLVM native" C/C++/Objective-C compiler, which aims to deliver amazingly fast compiles, extremely useful error and warning messages and to provide a platform for building great source level tools. The Clang Static Analyzer and clang-tidy are tools that automatically find bugs in your code, and are great examples of the sort of tools that can be built using the Clang frontend as a library to parse C/C++ code.
可以看到Clang是一个C/C++的前端。
3 编译工具
当源文件只有一个时,可以直接用g++命令对代码进行编译,但是如果是一个大工程,用g++命令去逐个处理就会变得非常复杂与混乱,这时我们可以借助make进行处理。
3.1 make
make是一种构建自动化工具(build automation tool),它通过Makefile来完成并自动维护编译工作,Makefile文件描述了整个工程的编译、链接规则。
3.2 Makefile
如果工程中有某个文件改变了,我们并不需要完全重新编译,只需要重新编译发生改变以及会受到影响的文件。
通常来说,Makefile包含definitions
和rules
。definitions声明变量并给变量赋值;rules采用下面的格式定义target,其中dependency是生成该target所依赖的文件或target,command是该target要执行的命令。
target: dependency(prerequests)
command
Makefile定义的target
, 通过命令make <target>
就能启动,比如常见的make clean
和make install
。
TensorRT的quickstart和tools两个目录下有Makefile,以tools/Polygraphy/Makefile为例看看
.PHONY: test leak_check clean build install docs
NPROC ?= 8
# Tests also check that docs can build
test: docs
export PYTHONPATH=$(CURDIR):$${PYTHONPATH} && \
export PATH=$(CURDIR)/bin:$${PATH} && \
export POLYGRAPHY_INTERNAL_CORRECTNESS_CHECKS=1 && \
python3 -m pytest tests/ -v -x -n $(NPROC) --dist=loadscope --durations=5
leak_check:
export PYTHONPATH=$(CURDIR):$${PYTHONPATH} && \
export PATH=$(CURDIR)/bin:$${PATH} && \
export POLYGRAPHY_INTERNAL_CORRECTNESS_CHECKS=1 && \
valgrind --leak-check=full python3 -m pytest tests/ -v --durations=5 2>&1 | tee leak-check.log
clean:
rm -rf dist/ build/ polygraphy.egg-info/
build: clean
python3 setup.py bdist_wheel
install_deps: build
-python3 -m pip install colored wheel
install: install_deps
python3 -m pip install --force-reinstall $(CURDIR)/dist/*.whl
docs: clean
mkdir -p build/docs
python3 `which sphinx-build` docs build/docs/ -j $(NPROC) -W
发现有很多Makefile都有.PHONY(phony: 假的)这个target,它的作用是什么呢?一般来说Makefile中的target都是只file target,也就是执行完命令make <target>
后回生成target
这个文件,但是有一些target(比如clean)只是执行一系列命令,并不生成文件。如果将某个target标识为.PHONY
,那么即使存在一个同名的文件target
,make <target>
也会执行对应的命令。否则,如果存在文件target
,make <target>
就不会执行对应的命令了。
stackoverflow
Phony Target
3.3 CMake
Wiki上对CMake的介绍,注意他和Unix上常见的“make”系统时分开的。
CMake是个一个开源的跨平台自动化建构系统,用来管理软件建置的程序,并不依赖于某特定编译器,并可支持多层目录、多个应用程序与多个库。它用配置文件控制建构过程(build process)的方式和Unix的make相似,只是CMake的配置文件取名为CMakeLists.txt。CMake并不直接建构出最终的软件,而是产生标准的建构档(如Unix的Makefile或Windows Visual C++的projects/workspaces),然后再依一般的建构方式使用。
“CMake”这个名字是"Cross platform Make"的缩写。虽然名字中含有"make",但是CMake和Unix上常见的“make”系统是分开的,而且更为高端。 它可与原生建置环境结合使用,例如:make、ninja、苹果的Xcode与微软的Visual Studio。
3.4 CMakeLists.txt
如果工程只有几个文件,直接编写Makefile文件就可以,没必要写CMakeLists.txt,然后通过cmake命令生成Makefile。学习编写CMakeLists.txt和cmake工具是一个实践的过程,本节拿TensorRT为例,简单介绍一下CMakeLists.txt中有哪些基本内容,以及它们代表的含义。整个TensorRT项目中有多个CMakeLists.txt,我们以根目录下的该文件举例。
project(TensorRT
LANGUAGES CXX CUDA
VERSION ${TRT_VERSION}
DESCRIPTION "TensorRT is a C++ library that facilitates high performance inference on NVIDIA GPUs and deep learning accelerators."
HOMEPAGE_URL "https://github.com/NVIDIA/TensorRT")
project指令的基本语法为project(project_name, [CXX] [C] [Java])
,第二个参数指定了工程支持的语言,其中CXX表示C++。上面的TRT_VERSION是一个变量,通过set指令可以显式地定义变量。
set(TRT_VERSION "${TRT_MAJOR}.${TRT_MINOR}.${TRT_PATCH}" CACHE STRING "TensorRT project version")
set指令可以设置3种变量:1)普通变量
;2)缓存变量
;3)环境变量
。上面的CACHE表示设置的是一个缓存变量,类型为STRING。
message指令用于向终端输出用户定义的信息,包含了3中类型的信息:1)STATUS
(状态信息);2)FATAL_ERROR
(立即终止所有cmake过程);3)SEND_ERROR
(产生错误,生成过程被跳过)
message("Building for TensorRT version: ${TRT_VERSION}, library version: ${TRT_SOVERSION}")
这篇笔记希望能够搭建一个最基本的认知框架,关于Makefile和CMakeLists.txt的具体学习可以参考以下资料:
1 (cmake实践)[http://file.ncnynl.com/ros/CMake Practice.pdf]
2 (Cmake Tutorial)[https://cmake.org/cmake/help/latest/guide/tutorial/index.html]
3 (跟我一起写Makefile)[https://seisman.github.io/how-to-write-makefile/]