用【Makefile】或【Cmake】编译【C/Fortran】程序文件
一、vimrc文件设置
set tabstop=4 "tab空格4
set expandtab "将tab扩展成空格
set softtabstop=4 "表示在编辑模式下按退格键时候退回缩进的长度
set shiftwidth=4 "每一级缩进是4个空格
set number "显示行号
filetype plugin on "Makefile空格
set fileencodings=utf-8,ucs-bom,gbk,cp936,gb2312,gb18030 "文件编码
"将.cuf文件用fortran的语法高亮配置进行高亮显示
au BufRead,BufNewFile *.cuf set filetype=fortran
if has("persistent_undo")
let target_path = expand('~/.vim/undodir')
if !isdirectory(target_path) "如果位置不存在,则创建目录
call mkdir(target_path, "p", 0700)
endif
let &undodir=target_path "变量赋值
set undofile "开启撤销永久化
endif
二、用Makefile编译文件
sudo apt install build-essentialbuild-essential中包含gcc,g++等编译工具
1.1 C语言程序
创建Makefile文件(vim Makefile)
CC = sw9gcc
FC = sw9gfortran
MPI_C = /usr/sw/mpi/mpi_20210219_SEA/bin/mpicc
EXE = main_dnn
LIB = ./utils/libs/libswdnn_xu.a
OBJDIR = ./obj
CFLAGS = -msimd -I./utils/include/ -mieee -g -O3 -funroll-nest-loops -mfma -fipa-type-escape -fipa-struct-peeling -fprefetch-loop-arrays
#CFLAGS += -DDEBUG
LIBFLAGS = -fcache-hint -mhybrid
slavesrc = $(wildcard ./utils/slave/*.c)
mastersrc = $(wildcard ./utils/master/*.c)
utilsrc = $(wildcard ./utils/src/*.c)
LIBOBJ1 += $(patsubst %.c, %.o, $(slavesrc) $(mastersrc) $(utilsrc))
LIBOBJ2 = $(notdir $(LIBOBJ1))
OBJ += $(patsubst %.o, $(OBJDIR)/%.o, $(LIBOBJ2))
all: $(EXE)
ar: $(LIB)
$(EXE): $(OBJDIR)/main_dnn.o $(LIB)
$(CC) $(LIBFLAGS) -o $@ $^ -lm -lm_slave -L/usr/sw/yyzlib/xMath-SACA -lswblas
$(LIB): $(OBJ)
sw9ar rcs $@ $^
$(OBJDIR)/main_dnn.o: main_dnn.c
$(CC) -Dcheck_res $(CFLAGS) -mhost -c $^ -o $@
$(OBJDIR)/%.o: ./utils/master/%.c
$(CC) $(CFLAGS) -mhost -c $^ -o $@
$(OBJDIR)/%.o: ./utils/src/%.c
$(CC) $(CFLAGS) -mhost -c $^ -o $@
$(OBJDIR)/%.o: ./utils/slave/%.c
$(CC) $(CFLAGS) -mpws -mslave -Dperf -c $^ -o $@
.PHONY: clean
clean:
rm -f ${LIB} $(EXE) $(OBJDIR)/*.o
~
1.2 Fortran语言程序
创建Makefile文件(vim Makefile)
# 获取文件夹中所有.f90文件列表 notdir把展开的文件去除掉路径信息 SRCS_F90 = $(wildcard *.f90) SRCS_F = $(wildcard ./*.f) SRCS_DIR = $(notdir $(SRCS_F)) # 替换.f90后缀为.o后缀 得到.o文件列表 notdir用于去掉文件的绝对路径,只保留文件名 OBJS_F90 = $(patsubst %.f90, %.o, $(SRCS_F90)) OBJS_F = $(patsubst %.f, %.o, $(SRCS_DIR)) # module依赖文件 MOD_OBJS = variable.o mpi_variable.o mod_split_funs.o MOD_SRCS = variable.f90 mpi_variable.f90 mod_split_funs.f90 # 定义编译器变量 F90= mpif90 FC = mpif90 FFLAGS = -O2 # 定义目标变量 EXE = main all:$(EXE) # @echo $(SRCS_F) # @echo $(SRCS_DIR) # @echo $(OBJS_F) # $^表示所有依赖文件 $@--目标文件,$<第一个依赖文件 $(EXE): $(OBJS_F90) $(OBJS_F) $(F90) -o $@ $^ # $(FC) -o $(EXE) $(OBJS) # %.o %.f90 %.mod 表示任意文件 %.o: %.f $(FC) -c $< -o $@ %.o: %.f90 $(MOD_OBJS) $(F90) $(CFLAGS) -c $< -o $@ $(MOD_OBJS): $(MOD_SRCS) $(F90) -c $^ # *.o 表示所有.o文件 .PHONY: clean clean: rm -f *.o *.mod
~
三、用Cmake编译文件
2.1 C语言程序
1、创建CMakeLists.txt文件(vim CMakeLists.txt)
cmake_minimum_required(VERSION 3.16.2) # 声明使用CMAKE的最低版本要求
project(main_blas LANGUAGES C) # 定义项目的名字
#find_package(MPI REQUIRED) # 寻找 MPI
#include_directories(SYSTEM ${PROJECT_SOURCE_DIR}/include) # 引入头文件目录
# 打印信息
message(WARNING "This application cannot compile without MPI end end ")
# 设置编译目标类型是Debug、Release版还是RelWithDebInfo版本
#set(DEFAULT_BUILD_TYPE "RelWithDebInfo")
set(CMAKE_BUILD_TYPE Release)
# 设置编译器选项
set(CMAKE_C_FLAGS "-msimd -mieee")
set(CMAKE_C_FLAGS_RELEASE "-O3 ") # Release版编译器选项
set(CMAKE_C_FLAGS_RELWITHDEBINFO "-g -O3")
# 静态库的输出目录
#set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${PROJECT_SOURCE_DIR}/utils/libs)
# 添加工作目录下子目录src中的源文件到SRC变量
file(GLOB SRC ${PROJECT_SOURCE_DIR}/utils/src/*.c)
file(GLOB MASTER_SRC ${PROJECT_SOURCE_DIR}/utils/master/*.c)
file(GLOB SLAVE_SRC ${PROJECT_SOURCE_DIR}/utils/slave/*.c)
# 传递编译器选项
set_source_files_properties(${SRC} PROPERTIES COMPILE_FLAGS "-mhost")
set_source_files_properties(${MASTER_SRC} PROPERTIES COMPILE_FLAGS "-mhost")
set_source_files_properties(${SLAVE_SRC} PROPERTIES COMPILE_FLAGS "-mslave -mfma -mpws -Dperf")
# 将SRCS编译为swdnn_xu这个静态链接库
add_library(swdnn_xu STATIC ${SRC} ${MASTER_SRC} ${SLAVE_SRC})
target_include_directories(swdnn_xu PRIVATE ${PROJECT_SOURCE_DIR}/utils/include)
set_target_properties(swdnn_xu PROPERTIES ARCHIVE_OUTPUT_DIRECTORY ${PROJECT_SOURCE_DIR}/utils/libs) # 静态库的输出目录
link_directories("${PROJECT_SOURCE_DIR}/utils/libs" "/usr/sw/yyzlib/xMath-SACA/") # 指定静态库路径
# 生成可执行文件 ${CMAKE_PROJECT_NAME}指项目名 main_blas
add_executable(${PROJECT_NAME} ${PROJECT_SOURCE_DIR}/main_blas.c)
target_link_options(${PROJECT_NAME} PRIVATE "-mhybrid" "-Wl,-zmuldefs")
#set_target_properties(${PROJECT_NAME} PROPERTIES LINK_FLAGS "-mhybrid -Wl,-zmuldefs") # 指定链接选项
target_include_directories(${PROJECT_NAME} PRIVATE ${PROJECT_SOURCE_DIR}/utils/include) # 指定头文件路径
target_link_libraries(${PROJECT_NAME} swdnn_xu -lcblas -lswblas -lgfortran -lm -lm_slave) # 指定要链接的静态库
2、执行编译文件(./cm.sh)
#!/bin/sh
set -ex
file=build
rm -rf ${file}
DIR=$(dirname $(readlink -f ${BASH_SOURCE[0]}))
# -DCMAKE_TOOLCHAIN_FILE=${DIR}/cmake/toolchain-sw_64.cmake
cmake . -B ${file} \
-DCMAKE_C_COMPILER=/usr/sw/mpi/mpi_20210219_SEA/bin/mpicc \
-DCMAKE_AR=/usr/sw/swgcc/swgcc710-tools-SEA-1307/usr/bin/sw9ar
cd ${file}
make -j
3、编译器环境变量设置(不需要)
vim cmake/toolchain-sw_64.cmake
set(CMAKE_SYSTEM_NAME Linux) set(CMAKE_SYSTEM_PROCESSOR sw_64) set(CMAKE_C_COMPILER /usr/sw/mpi/mpi_20210219_SEA/bin/mpicc) set(CMAKE_CXX_COMPILER /usr/sw/mpi/mpi_20210219_SEA/bin/mpicxx) set(CMAKE_AR /usr/sw/swgcc/swgcc710-tools-SEA-1208/usr/bin/sw9ar) set(MPI_C_COMPILER /usr/sw/mpi/mpi_20210219_SEA/bin/mpicc) set(MPI_CXX_COMPILER /usr/sw/mpi/mpi_20210219_SEA/bin/mpicxx) set(SW_C_COMPILER /usr/sw/swgcc/swgcc710-tools-SEA-1208/usr/bin/sw9gcc) set(SW_CXX_COMPILER /usr/sw/swgcc/swgcc710-tools-SEA-1208/usr/bin/sw9g++)
~
2.2 Fortran语言程序
创建CMakeLists.txt文件(vim CMakeLists.txt)
简单版Cmake(FC=mpif90 cmake . -B build -DCMAKE_Fortran_COMPILER=/usr/local/bin/mpif90)
cmake_minimum_required(VERSION 3.21.4) # 声明使用CMAKE的最低版本要求
project(xu_main LANGUAGES Fortran) # 定义项目的名字
find_package(MPI REQUIRED) # 寻找 MPI
#include_directories(SYSTEM ${MPI_INCLUDE_PATH}) # 引入头文件目录
# 设置编译目标类型是release版还是debug版本
set(DEFAULT_BUILD_TYPE "Release")
set(CMAKE_Fortran_FLAGS "-O2") # 设置编译器选项
# 添加工作目录下子目录src中的源文件到SRC变量
file(GLOB SRC ./funs9/*.f90 ${PROJECT_SOURCE_DIR}/funs9/*.f)
# 生成可执行文件 ${CMAKE_PROJECT_NAME}指项目名 xu_main
add_executable(${CMAKE_PROJECT_NAME} ${SRC} )
2.3 命令简介
2.3.1 project
project命令用于指定cmake工程的名称,实际上,它还可以指定cmake工程的版本号(VERSION关键字)、简短的描述(DESCRIPTION关键字)、主页URL(HOMEPAGE_URL关键字)和编译工程使用的语言(LANGUAGES关键字)。(1)参数project(<PROJECT_NAME> [VERSION <major>] [DESCRIPTION <project-description-string>] [HOMEPAGE_URL <url-string>] [LANGUAGES <language-name>])
PROJECT_NAME:将当前工程的名称赋值给PROJECT_NAME,同时${PROJECT_NAME}变量赋值为PROJECT_NAME。
VERSION:指定工程的版本号。
DESCRIPTION:对工程的文本描述。
HOMEPAGE_URL:指定工程的主页URL。
LANGUAGES选项:选择构建工程需要的编程语言。
(2)当定义了project()后,一些cmake自带变量会自动赋值
PROJECT_NAME:将当前工程的名称赋值给PROJECT_NAME。
PROJECT_SOURCE_DIR:当前工程的源码路径。
CMAKE_PROJECT_NAME:顶层工程的名称。例如当前调用的CMakeLists.txt位于顶层目录(可以理解为使用cmake命令首次调用的那个CMakeLists.txt),那么工程名还会赋值给CMAKE_PROJECT_NAME。
2.3.2 add_executable生成可执行文件
1、参数add_executable(<name> <EXCLUDE_FROM_ALL> <source1> <source2 ...>)name:可执行目标文件的名字,在一个cmake工程中,这个名字必须全局唯一。
EXCLUDE_FROM_ALL:用于指定可执行目标是否会被构建,当该选项使用的时候,可执行目标不会被构建。
[source1] [source2 …]:构建可执行目标文件所需要的源文件。
2、EXCLUDE_FROM_ALL参数用法
project(test) add_executable(test EXCLUDE_FROM_ALL test.f90) // test加了EXCLUDE_FROM_ALL属性,在默认编译的时候,不会被编译, // 如果要编译它,需要手动编译,比如make test指定编译名为test make test
2.3.3 add_library编译出静态库和动态库
1、add_library(<name> [STATIC | SHARED | MODULE] [EXCLUDE_FROM_ALL] [source1 source2 ...])STATIC、SHARED 和 MODULE 表示库文件的类型,分别表示静态库、动态库和可加载模块;
# 将SRCS编译为swdnn_xu这个静态链接库
add_library(swdnn_xu STATIC ${SRC} ${MASTER_SRC} ${SLAVE_SRC})
2.3.4 link_libraries和target_link_libraries 链接库
1、link_libraries(link_libraries 基本上被遗弃了,尽可能用 target_link_libraries)
link_libraries("/usr/sw/yyzlib/xMath-SACA/libswblas.a")
add_executable(${PROJECT_NAME} main.c)
2、target_link_libraries(<target> <PRIVATE | PUBLIC | INTERFACE> <item>...)
link_directories("/usr/sw/yyzlib/xMath-SACA") # 指定静态库路径
add_executable(${PROJECT_NAME} main.c)
target_link_libraries(${PROJECT_NAME} swzgemm_xu /usr/sw/yyzlib/xMath-SACA/libswblas.a)
target_link_libraries(${PROJECT_NAME} libswzgemm_xu.a) #这些库名写法都可以。
target_link_libraries(${PROJECT_NAME} swzgemm_xu)
target_link_libraries(${PROJECT_NAME} -lswzgemm_xu)
【注】:调用link_directories必须在生成可执行文件之前调用,也就是在add_executable之前调用
target_link_libraries(PUBLIC target target1 target2) target_link_libraries(PUBLIC target3 target target4) //PUBLIC:表示target能够使用target1&target2库中内容,target3也能使用target1&target2库中内容;默认状态为PUBLIC。 target_link_libraries(PRIVATE target target1 target2) target_link_libraries(PRIVATE target3 target target4) //PRIVATE:表示target能够使用target1&target2库中内容,target3不能使用target1&target2中定义的内容,只能使用target中定义的内容。 target_link_libraries(INTERFACE target target1 target2) target_link_libraries(INTERFACE target3 target target4) //INTERFACE:表示target无法使用target1&target2库中内容,但是target3能使用target1&target2中内容。
3、link_libraries 和 target_link_libraries 区别
(1)link_libraries用在add_executable之前,target_link_libraries用在add_executable之后
(2)link_libraries用来链接静态库,target_link_libraries用来链接导入库,即按照header file + .lib + .dll方式隐式调用动态库的.lib库
(3)link_libraries 基本上被遗弃了,尽可能用 target_link_libraries
2.3.5 include_directories 和 target_include_directories引用头文件
1、include_directories([AFTER | BEFORE] [SYSTEM] [<dir1> <dir2> ...])include_directories(SYSTEM ${PROJECT_SOURCE_DIR}/include) # 引入头文件目录
add_executable(${PROJECT_NAME} ${PROJECT_SOURCE_DIR}/main_blas.c)
如果给出了SYSTEM选项,编译器将被告知在某些平台上目录是系统包含目录。
2、target_include_directories(<target> [SYSTEM][AFTER|BEFORE] <INTERFACE|PUBLIC|PRIVATE> [items1...][<INTERFACE|PUBLIC|PRIVATE> [items2...] ...])
add_executable(${PROJECT_NAME} ${PROJECT_SOURCE_DIR}/main_blas.c)
target_include_directories(${PROJECT_NAME} PRIVATE ${PROJECT_SOURCE_DIR}/utils/include) # 指定头文件路径
(1)include_directories:当前CMakeLists.txt的所有目标,以及之后添加的所有子目录的目标添加头文件搜索路径。因此,慎用include_directories,因为会影响全局target。
(2)target_include_directories:指定目标包含的头文件路径。如果想为不同目标设置不同的搜索路径,那么用target_include_directories更合适。
2.3.6 add_subdirectory添加子目录
1、语法:add_subdirectory (source_dir [binary_dir] [EXCLUDE_FROM_ALL])
2、作用:添加一个子目录并构建该子目录,告诉CMAKE我还有其它子目录的CMakeList.txt需要编译。
3、参数
(1)source_dir:必选参数。该参数指定一个子目录,子目录下应该包含CMakeLists.txt文件和代码文件。子目录可以是相对路径也可以是绝对路径,如果是相对路径,则是相对当前目录的一个相对路径。
(2)binary_dir:可选参数。该参数指定一个目录,用于存放输出文件。可以是相对路径也可以是绝对路径,如果是相对路径,则是相对当前输出目录的一个相对路径。如果该参数没有指定,则默认的输出目录使用source_dir。
(3)EXCLUDE_FROM_ALL:可选参数。当指定了该参数,则子目录下的目标不会被父目录下的目标文件包含进去,父目录的CMakeLists.txt不会构建子目录的目标文件,必须在子目录下显式去构建。例外情况:当父目录的目标依赖于子目录的目标,则子目录的目标仍然会被构建出来以满足依赖关系(例如使用了target_link_libraries)。
add_subdirectory(test EXCLUDE_FROM_ALL)
2.3.7 将目标文件保存到指定目录下
1、设置输出目录
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${PROJECT_SOURCE_DIR}/lib) # 动态库
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${PROJECT_SOURCE_DIR}/static) # 静态库
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${PROJECT_SOURCE_DIR}/bin) # 执行文件
2、指向性保存
同样是指定输出目录,但是不同的动态库文件指定不同的输出目录。(静态库和二进制执行文件也是同理)。目标文件可以大致分为三种类型:二进制执行文件、动态库、静态库。保存不同目标文件所用到的属性不一样。具体分类如下。
RUNTIME_OUTPUT_DIRECTORY:二进制执行文件
LIBRARY_OUTPUT_DIRECTORY:动态库
ARCHIVE_OUTPUT_DIRECTORY:静态库
add_library(swdnn_xu STATIC ${SRC} ${MASTER_SRC} ${SLAVE_SRC})
set_target_properties(swdnn_xu PROPERTIES ARCHIVE_OUTPUT_DIRECTORY ${PROJECT_SOURCE_DIR}/utils/libs)
2.3.8 option选项
1、option(<variable> "<help_text>" [value])variable:定义选项名称
help_text:说明选项的含义
value:定义选项默认状态,一般是OFF或者ON,除去ON之外,其他所有值都为认为是OFF。
相当于bool变量,用于控制是否进入if分支。
【注】:修改option选项后需要删除CMakeCache.txt重新构建cmake,否则option修改后不生效。
option(ENABLE_AUTO_INIT "Automatically initialize the thread pool" ON)
if(ENABLE_AUTO_INIT)
add_definitions(-DCRTS_AUTO_INIT)
endif()
在CMakeLists.txt中加如下语句
include option.txt
在option.txt中添加以下语句
/*USE_MYMATH 为编译开关,中间的字符串为描述信息,ON/OFF 为默认选项*/ option (USE_CRTS “Use SW_CRTS” ON)
make VERBOSE=1
2.3.9 add_definitions 和 target_compile_definitions添加预处理器宏
1、add_definitions
基本用法:add_definitions(-DDEBUG)
2、target_compile_definitions(<target> <INTERFACE|PUBLIC|PRIVATE> [items1...] [<INTERFACE|PUBLIC|PRIVATE> [items2...] ...])
PRIVATE:只有目标自己会使用这些编译参数。
PUBLIC:目标自己和其他依赖这个目标的目标都会使用这些编译参数。
INTERFACE:只有其他依赖这个目标的目标会使用这些编译参数
target_compile_definitions(target PRIVATE DEBUG)
target_compile_definitions(target PRIVATE -DDEBUG) # -D 被移除
int main(int argc, char** argv)
{
#ifdef DEBUG
std::cout << "main:In debug print mode..." << std::endl;
#endif
return 0;
}
对于大型项目和需要更细致管理的情况,建议使用 target_compile_definitions 来为特定目标设置特定的编译器定义,以保持项目的清晰和模块化。
2.3.10 target_compile_options设置目标的编译选项
add_executable(${PROJECT_NAME} ${PROJECT_SOURCE_DIR}/main_blas.c)
target_compile_options(${PROJECT_NAME} PRIVATE "-O3")
2、与CMAKE_C_FLAGS等区别
target_compile_options适用于需要为特定目标设置特定编译器选项的场景
CMAKE_C_FLAGS适用于需要为整个项目设置通用编译器选项的场景
# 设置编译目标类型是Debug、Release版还是RelWithDebInfo版本 # set(DEFAULT_BUILD_TYPE "RelWithDebInfo") set(CMAKE_BUILD_TYPE Release) # 设置编译器选项 set(CMAKE_C_FLAGS "-O2") #-msimd -mieee set(CMAKE_CXX_FLAGS "-O2") #-msimd -mieee -fcommon set(CMAKE_C_FLAGS_RELEASE "-O2") # Release版编译器选项 set(CMAKE_CXX_FLAGS_RELEASE "-O2") set(CMAKE_C_FLAGS_RELWITHDEBINFO "-g -O3") #set(CMAKE_CUDA_FLAGS "--expt-relaxed-constexpr --expt-extended-lambda") #set(CMAKE_NVCC_FLAGS "CMAKE_NVCC_FLAGS -std=c++11")
2.3.11 设置目标的链接选项
target_compile_options(${PROJECT_NAME} PRIVATE "-mhybrid -Wl,-zmuldefs")
target_link_options(${PROJECT_NAME} PRIVATE "-mhybrid" "-Wl,-zmuldefs")
2.3.12 get_filename_component获取文件信息
get_filename_component(<VAR> <FileName> <COMP> [CACHE])<var>: 用于存储结果的变量名。
<FileName>: 要处理的文件路径。
<mode>: 你想提取的文件路径的组成部分,可以是以下之一:
NAME: 文件的基本名称(去掉路径部分)。
NAME_WE: 文件的基本名称,但去掉文件扩展名。
EXT: 文件扩展名。
PATH、DIR: 文件的路径,不包含文件名。
REALPATH: 文件的绝对路径(解决符号链接)。
ABSOLUTE: 文件的绝对路径(如果文件是相对路径的话)。
[CACHE]: 这是一个可选的参数,用来指定将结果缓存到 CMake 的缓存中。

浙公网安备 33010602011771号