前言
本文介绍跨平台cmake的编写,主要是linux和windows用cmake对项目的编译。这是一个通用模板,能够应用到更加复杂的项目中,项目例子用https://blog.csdn.net/qq_36472340/article/details/126395749?spm=1001.2014.3001.5502这篇文章。
一、CMakeList.txt如何高效组织项目
一个简洁的工程,一般把源码放在一个目录src,将编译结果放在一个目录build,将头文件、库、可执行文件放在一个目录install。为了高效组织项目,CMakeList.txt应该如下图分布:
二、常用的CMAKE编译命令
1.cmake_minimum_required (VERSION 2.8) 指定cmake的最低版本,比指定的cmake版本低则不会编译。 2.PROJECT(test) 指定工程名称,MSVC编译时生成test.sln项目,其他编译器没有体现 3.ADD_DEFINITIONS(-DEXPORTS) 添加宏 4.CMAKE_C_FLAGS、指定gcc编译选项;CMAKE_CXX_FLAGS指定g++编译选项 5. CMAKE_C_COMPILER 指定编译所用的c编译器;CMAKE_CXX_COMPILER指定c++编译器。通过设置这两个参数能够实现任何平台上的交叉编译。如果不设置,则会默认选择编译器,我的平台下:linux平台/usr/bin/cc;windows用vsual c++编译C:/Program Files (x86)/Microsoft Visual Studio/2019/Community/VC/Tools/MSVC/14.29.30133/bin/Hostx64/x64/cl.exe。 6.ADD_SUBDIRECTORY(src) 包含的子目录,之前设置的东西在整个src当中都生效。 7. INCLUDE_DIRECTORIES() 添加头文件相当于gcc中的 -I。 8. *FILE(GLOB_RECURSE SRC_LIST RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} .c) 将cmakelist.txt所在目录下的所有.c文件赋给SRC_LIST。 9. aux_source_directory(dir,var) 包含目录下的所有源文件,可以与8相互交换。 10. ADD_LIBRARY(${SHARED_NAME} SHARED ${SRC_LIST} ${HEADER_LIST}) 生成动态库,没有SHARED则生成静态库。 11. LINK_DIRECTORIES 添加库文件收缩路径,相当于-L。引用工程中生成的库不需要该命令,引用第三方库需要加上该命令。 12. TARGET_LINK_LIBRARIES 链接动态库,相当于gcc中的-l。 13. **LINK_LIBRARYS(“…/…/shared.so”)添加依赖库文件,相当于-I, 需要全路径,并且只能用于add_executable之前。 14. ADD_EXECUTABLE 编译可执行文件。 15. INSTALL(TARGETS ${SHARED_NAME} DESTINATION ${CMAKE_INSTALL_PREFIX}/lib/) 安装库或者可执行文件。 ** INSTALL(FILES add.h DESTINATION ${CMAKE_INSTALL_PREFIX}/include/) 安装文件到指定目录下,编译完成后才会执行INSTALL操作,不能在编译过程中引用安装目录中的库或者头文件。 INSTALL(DIRECTORY …)安装目录。 16.CMAKE常用变量:LIBRARY_OUTPUT_PATH指定库文件输出路径;EXEC_OUTPUT_PATH指定可执行文件输出路径,可以替代INSTALL;CMAKE_CUREENT_SOURCE_DIR cmakelist所在目录;CMAKE_INSTALL_PREFIX目标文件安装目录。$ENV{NAME}系统环境变量。 注意: CMAKE中的命令大小写均可。
三、CMakeList.txt脚本的编写
从最外层目录一直到最里面的CMakeList.txt脚本如下:
# CMake 最低版本号要求
cmake_minimum_required (VERSION 2.8)
#指定工程名称
PROJECT(test)
#通过不同的编译器进行参数设置,visual studio编译器为MSVC
if("${CMAKE_C_COMPILER_ID}" STREQUAL "Clang" OR "${CMAKE_C_COMPILER_ID}" STREQUAL "GNU")
if("${CMAKE_OS_ARCH}" STREQUAL "x86")
SET(CMAKE_C_FLAGS_DEBUG "-m32 -D_GNU_SOURCE -O0 -g -ggdb -Wall")
SET(CMAKE_C_FLAGS_RELEASE "-m32 -D_GNU_SOURCE -O2 -Wall -DNDEBUG")
elseif("${CMAKE_OS_ARCH}" STREQUAL "X64")
SET(CMAKE_C_FLAGS_DEBUG "-fPIC -m64 -D_GNU_SOURCE -O0 -g -ggdb -Wall")
SET(CMAKE_C_FLAGS_RELEASE "-fPIC -m64 -D_GNU_SOURCE -O2 -Wall -Wl,-Bsymbolic -Wl,-Bsymbolic-functions -DNDEBUG")
endif()
elseif("CMAKE_C_COMPILER_ID" STREQUAL "MSVC")
SET(CMAKE_C_FLAGS_DEBUG "$ENV{CFLAGS} -D_CRT_SECURE_NO_WARNINGS=1 /Zi /Od /Ob0 /RTC1 /MDd /W4 /utf-8")
SET(CMAKE_C_FLAGS_RELEASE "$ENV{CFLAGS} -D_CRT_SECURE_NO_WARNINGS=1 /Ob2 /MD /W4 /utf-8")
endif()
#设置编译器选项
if("${CMAKE_BUILD_TYPE}" STREQUAL "Debug")
SET(CMAKE_C_FLAGS ${CMAKE_C_FLAGS_DEBUG})
elseif("${CMAKE_BUILD_TYPE}" STREQUAL "Release")
SET(CMAKE_C_FLAGS ${CMAKE_C_FLAGS_RELEASE})
endif()
#windows平台导出函数需要加宏
if("${CMAKE_OS_TYPE}" STREQUAL "windows")
ADD_DEFINITIONS(-DEXPORTS)
ADD_DEFINITIONS(-DWIN32)
endif()
#添加src子目录,上面的设置在src当中都生效
ADD_SUBDIRECTORY(src)
#添加头文件和子目录
INCLUDE_DIRECTORIES("${CMAKE_CURRENT_SOURCE_DIR}/shared")
ADD_SUBDIRECTORY(shared)
INCLUDE_DIRECTORIES("${CMAKE_CURRENT_SOURCE_DIR}/static")
ADD_SUBDIRECTORY(static)
ADD_SUBDIRECTORY(main)
shared目录下
#获取当前目录下所有的.c和.h文件
FILE(GLOB_RECURSE SRC_LIST RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} *.c)
FILE(GLOB_RECURSE HEADER_LIST RELATIVE $(CMAKE_CURRENT_SOURCE_DIR) *.h)
#设置动态库的名字
SET(SHARED_NAME shared)
#生成动态库
ADD_LIBRARY(${SHARED_NAME} SHARED ${SRC_LIST} ${HEADER_LIST})
#安装头文件和库目录
INSTALL(TARGETS ${SHARED_NAME} DESTINATION ${CMAKE_INSTALL_PREFIX}/lib/)
INSTALL(FILES add.h DESTINATION ${CMAKE_INSTALL_PREFIX}/include/)
static目录下
FILE(GLOB_RECURSE SRC_LIST RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} *.c)
FILE(GLOB_RECURSE HEADER_LIST RELATIVE $(CMAKE_CURRENT_SOURCE_DIR) *.h)
SET(STATIC_NAME static)
#编译静态库
ADD_LIBRARY(${STATIC_NAME} ${SRC_LIST} ${HEADER_LIST})
INSTALL(TARGETS ${STATIC_NAME} DESTINATION ${CMAKE_INSTALL_PREFIX}/lib/)
INSTALL(FILES multi.h DESTINATION ${CMAKE_INSTALL_PREFIX}/include/)
main目录下
FILE(GLOB_RECURSE SRC_LIST RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} *.c)
SET(EXE_NAME main)
ADD_DEFINITIONS(-DDEBUG)
#编译可执行文件
ADD_EXECUTABLE(${EXE_NAME} ${SRC_LIST})
#编译出的库,不用指定库的路径,直接写出需要链接的库即可
TARGET_LINK_LIBRARIES(${EXE_NAME} shared static)
INSTALL(TARGETS ${EXE_NAME} DESTINATION ${CMAKE_INSTALL_PREFIX}/bin/)
四、运行cmake windows脚本的编写
@echo off
set OS_TYPE=windows
set OS_ARCH=x86
set WIN_PLAT=Win32
set COMPILE_TYPE=Release
set INSTALL_DIR=%cd%/install
@REM 解析编译选项
:loop
set arg=%1
if %arg%! == ! goto end
if %arg% == -a (
set OS_ARCH=%2
set WIN_PLAT=%2
shift
) else if %arg% == -d (
set COMPILE_TYPE=Debug
) else if %arg% == -i (
set INSTALL_DIR =%2
shift
) else if %arg% == -h (
call:usage
goto:eof
) else (
echo unknow param!
)
shift
goto loop
:end
@REM 设置cmake的安装目录
set INSTALL_PREFIX=%INSTALL_DIR%/%OS_TYPE%/%OS_ARCH%/%COMPILE_TYPE%/
echo %INSTALL_PREFIX%
@REM 设置cmake的编译变量
set cmd=cmake .. -DCMAKE_INSTALL_PREFIX=%INSTALL_PREFIX% -DCMAKE_OS_TYPE=%OS_TYPE% -DCMAKE_BUILD_TYPE=%COMPILE_TYPE% -DCMAKE_OS_ARCH=%OS_ARCH%
@REM visual studio 2019 编译平台的设置
if %OS_ARCH% == x86 (
set cmd=%cmd% -G "Visual Studio 16 2019" -A Win32
) else (
set cmd=%cmd% -G "Visual Studio 16 2019" -A x64
)
@REM 构建visual studio 项目
cd %cd%/build
%cmd%
@REM 删掉缓存,下一次编译才能够重新构建工程
del /q/a/f CMakeCache.txt
cd ..
@REM 编译visual studio项目
set build_project=MSBuild %cd%\build\INSTALL.vcxproj /t:Rebuild /p:Configuration=%COMPILE_TYPE% /p:Platform=%WIN_PLAT%
%build_project%
goto:build_end
:usage
echo "-a set OS_ARCH"
echo "-d set COMPILE_TYPE"
echo "-i set INSTALL_DIR"
goto:eof
:build_end
echo build over
脚本使用: build.bat -a x86 -d 编译x86平台下的Debug版本。 build.bat -a x64 编译x64平台下的release版本。 -G “Visual Studio 16 2019” -A Win32 这个是使用visual studio 2019编译x86时的设置,不同的版本设置不一样,可以在这个链接下面查看https://cmake.org/cmake/help/latest/generator/Visual%20Studio%2016%202019.html 工程构建后build目录如下: 编译后动态库的输出目录为./build/shared/debug/shared.dll。当mian程序链接时,链接器自动台这个目录下进行链接;INSTALL安装时将这个目录下的动态库安装到指定的位置。 注意: 编译时一定要在build目录下进行编译,不然编译后的东西将会分布在目录中的各个地方,不方便整理。
五、运行cmake linux脚本的编写
#!/bin/bash
OS_TYPE=linux
OS_ARCH=x86
COMPILE_TYPE=Release
INSTALL_DIR=$PWD/install
show_help() {
cat<<EOF
-a set OS_ARCH
-d set COMPILE_TYPE
-i set INSTALL_DIR
EOF
}
#解析编译选项,a:会解析成-a :表示-a后面需要跟上参数
while getopts "a:di:h" opt; do
case $opt in
a)
OS_ARCH="$OPTARG"
;;
d)
COMPILE_TYPE=Debug
;;
i)
INSTALL_DIR=$OPTARG
;;
h)
show_help
exit 0
;;
esac
done
INSTALL_PREFIX=${INSTALL_DIR}/${OS_TYPE}/${OS_ARCH}/${COMPILE_TYPE}/
#编译cmake
cmd="cmake .. -DCMAKE_INSTALL_PREFIX=${INSTALL_PREFIX} -DCMAKE_BUILD_TYPE=${COMPILE_TYPE} -DCMAKE_OS_ARCH=${OS_ARCH} -DCMAKE_OS_TYPE=${OS_TYPE}"
cd $PWD/build
$cmd
make
make install
cd ..
脚本使用: ./build.sh -a x86 -d 生成32位的调试版程序 ./build.sh -a x64 生成64位调试版程序。 linux平台的编译器用的是GNU,运行cmake生成makefile,运行makefile编译工程。与windows的MSVC编译有所不同:MSVC先构建visual studio工程,然后 通过MSbuild 进行该工程的编译。
六、总结
本文给出了大多数用cmake进行构建项目的一个模板,能够在不同平台和不同的架构下进行编译,cmake相比于gcc和makefile能够更加高效的编译复杂项目。
|