用【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编译文件
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)。
2.3.7 target_compile_options设置目标的编译选项
add_executable(${PROJECT_NAME} ${PROJECT_SOURCE_DIR}/main_blas.c) target_compile_options(${PROJECT_NAME} PRIVATE "-O3")
2.3.8 option选项
1、option(<variable> "<help_text>" [value])
variable:定义选项名称
help_text:说明选项的含义
value:定义选项默认状态,一般是OFF或者ON,除去ON之外,其他所有值都为认为是OFF。
相当于bool变量,用于控制是否进入if分支。
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 将目标文件保存到指定目录下
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.9 设置目标的链接选项
target_compile_options(${PROJECT_NAME} PRIVATE "-mhybrid -Wl,-zmuldefs") target_link_options(${PROJECT_NAME} PRIVATE "-mhybrid" "-Wl,-zmuldefs")
aaaa