跳转至

CMake 入门

CMake 是一个支持多平台的构建工具,它可以使用一份配置文件配置源代码的编译和链接方法,就能在不同平台上构建。

配置编译工具链

默认情况下,CMake 会调用系统的默认编译工具链,编译出可以在计算机上运行的可执行文件。但是我们自己的电脑的 CPU 一般是 x86/ARM 指令集,编译出来的二进制不能在我们的 RISC-V CPU 上运行。所以,我们需要通过配置文件来指定自己的编译工具链。

工具链配置文件

在 C 语言程序项目目录下,创建 toolchain.cmake 文件,并在文件中配置好我们自己的编译工具链。

通过设置 CMAKE_***_COMPILER 变量,我们可以指定使用 clang 作为编译器。而 CMAKE_***_COMPILER_TARGET 则设置了交叉编译的编译目标三元组。编译目标三元组中,第一个分量代表目标指令集,第二个分量代表目标操作系统,第三个分量代表是可执行程序的格式。riscv32-unknown-elf 就表示我们编译的目标是 RISC-V 32 位指令集,在未知操作系统上运行的 ELF 格式的可执行程序。

另外,我们还需要指定编译器的初始额外参数,这些参数可以在 CMAKE_***_FLAGS 变量中指定。其中重要的是 -fuse-ld=lld-no-std-static。第一个参数告诉 CMake 使用 lld 作为链接器,而不是默认的 ld。第二个参数告诉 CMake 不使用标准库,而是使用自己的库。最后一个参数告诉编译使用静态链接,而不是动态链接。

set(CMAKE_SYSTEM_NAME Generic)
set(CMAKE_SYSTEM_PROCESSOR riscv32)

set(triple riscv32-unknown-elf)

set(CMAKE_C_COMPILER clang)
set(CMAKE_C_COMPILER_TARGET ${triple})
set(CMAKE_CXX_COMPILER clang++)
set(CMAKE_CXX_COMPILER_TARGET ${triple})
set(CMAKE_ASM_COMPILER clang)
set(CMAKE_ASM_COMPILER_TARGET ${triple})
set(CMAKE_AR llvm-ar CACHE FILEPATH "Archiver")
set(CMAKE_OBJCOPY llvm-objcopy)

set(CMAKE_C_FLAGS_INIT "-mno-relax")
set(CMAKE_CXX_FLAGS_INIT "-mno-relax")
set(CMAKE_ASM_FLAGS_INIT "-mno-relax")
set(CMAKE_EXE_LINKER_FLAGS_INIT "-fuse-ld=lld -nostdlib -static -mno-relax")
set(CMAKE_MODULE_LINKER_FLAGS_INIT "-fuse-ld=lld -nostdlib -static -mno-relax")
set(CMAKE_SHARED_LINKER_FLAGS_INIT "-fuse-ld=lld -nostdlib -static -mno-relax")

使用 CMake 生成项目并编译

一个 CMake 文件中最重要的是 add_libraryadd_executable 指令。add_library 指令用于添加一个库目标,add_executable 指令用于添加一个可执行文件目标。为了方便测试,初始化程序运行栈的代码和链接脚本已经提供了,后续只需要在 csrc 文件夹中添加自己的 C 语言程序,并修改 CMakeLists.txt 文件添加可执行程序目标即可。

CMake 项目的编译需要两步,第一步是运行 cmake 生成平台相关的编译配置文件,在 Linux 下通常是 Makefile,在 Windows 下通常是 Visual Studio 的解决方案文件,或者微软的 nmake 工具的配置文件。第二步是运行 make 或者 nmake 等工具进行编译。

为了避免污染源代码目录,配置和编译通常在单独的文件夹中进行。项目提供的编译脚本 build.sh(Linux)或 build.bat(Windows)将配置和编译文件放在 build 文件夹中。

如果想要添加 C 语言程序,只需要在 CMakeLists.txt 的开头的:

set(C_PROGRAMS tetris hello fibonacci quicksort)

添加自己的 C 语言程序文件名,保存后然后重新运行一次编译脚本即可。

ELF 格式的文件可以使用 llvm-objdump 工具反汇编,在 build 文件夹中运行:

llvm-objdump -d --arch-name=riscv32 程序名

编译出来的 CPU 可执行文件放置在 src/main/resources 文件夹中,文件名后缀是 .asmbin


最后更新: 2023-10-21 02:03:12
本页作者: Howard Lau