Cmake入门笔录
- 最常用到的命令,够搭一个流程,注释后面有示例,拷贝修改使用。
include_directories #添加头文件目录include_directories(/usr/local/lib/include)
link_directories #添加库文件目录,动态库link_directories(/usr/local/lib/)
add_subdirectory #添加一个文件夹进行编译,该文件夹下的CMakeLists.txt负责编译该文件夹下的源码add_subdirectory(./subdirectory)
add_executable #为工程生成目标文件add_executable(quad_interface_test quad_test.c)
add_custom_target #增加一个没有输出的目标,使得它总是被构建。就是构建一个MakeFile文件的目标。执行指定的命令。做拷贝。add_custom_target(target DEPENDS b)
add_library #指定的源文件生成链接文件,然后添加到工程中去,静态库。add_library(target STATIC b) STATIC可改SHARED,生成共享库
target_link_libraries #设置要链接的库文件名称,动态静态库,如果有多个依赖,按从后往前的顺序链接。
动态库的添加:target_link_libraries(quad_interface_test "/src/libphg.so" -lmpi) #添加libphg.so、libmpi.so
静态库的添加:target_link_libraries(project_name mxnet) #添加libmxnet.a
add_dependencies #让一个顶层目标依赖于其他的顶层目标。add_dependencies(surfactant misc-scalarFunctions)
find_package #查找库find_package(MPI)后,${MPI_INCLUDE_PATH}、${MPI_C_LIBRARIES}表示头文件和库文件目录
find_library #查找库所在目录find_library(name path)。不常用。
link_libraries #添加需要链接的库文件路径,静态库。link_libraries("/bin/glnxa64")。不常用。使用target_link_libraries取代。
aux_source_directory #查找指定目录下的所有源文件,然后将结果存进指定变量名。aux_source_directory(dir_variable)
- 路径加不加引号都可以,名称对大小写不敏感。
- gcc生成动态链接库。
gcc -fPIC -shared quad-interface.c -o libquad.so
从一个简单的示例入门Cmake
文件树形目录结构
├─meshes
│ └─poisson
├─param
│ ├─geom
│ │ └─unspecified
│ ├─levelset
│ │ ├─brick_transp
│ │ ├─film
│ │ ├─prJump
│ │ ├─twophasedrops
│ │ └─unspecified
│ ├─osmosis
│ │ └─osmosis
│ ├─partests
│ │ ├─dist_ref_rising_drop
│ │ └─dist_TestRefPar
│ ├─poisson
│ │ └─cdrdrops
│ ├─spacetimetransp
│ │ └─st_transp
│ ├─stokes
│ │ ├─errorestimator
│ │ └─sdropsP2
│ ├─surfactant
│ │ ├─surfacestokes
│ │ └─surfactant
│ ├─surfnavierstokes
│ ├─surfphasesep
│ ├─tests
│ │ ├─f_Gamma
│ │ ├─meshreader
│ │ ├─reparamtest
│ │ ├─reparam_init
│ │ ├─serialization
│ │ └─show_domain
│ └─transport
│ └─ns_transp
└─src
├─DiST
├─doc
├─geom
├─levelset
├─misc
│ └─kd-tree
├─navstokes
├─num
├─osmosis
├─out
├─parallel
├─partests
├─poisson
│ └─matlab
│ ├─directProblem
│ ├─doc
│ └─optimization
│ └─opt1D
├─spacetimetransp
├─stokes
├─surfactant
├─surfnavierstokes
├─surfphasesep
├─tests
│ ├─scripts
│ └─specifications
│ ├─parallel
│ └─serial
└─transport
我们比较在意的是src文件夹下的文件,树形目录结构提取出来如下:
SRC
│ .gitignore
│ .project
│ CMakeLists.txt
│ CMakeSettings.txt
│ dox.cfg
│ ParamToJSON.py
│
├─DiST
│ CMakeLists.txt
│ DiST.cpp
│ DiST.h
│ DiST.tpp
│ geomid.h
│ mpistream.cpp
│ mpistream.h
│ mpistream.tpp
│ remotedata.cpp
│ remotedata.h
│ remotedata.tpp
│
├─doc
│ dropsappendix.tex
│ dropsnotes.pdf
│ dropsnotes.tex
│ globaldoc.h
│ history.txt
│ Readme.cmake
│ tetra.eps
│ tetra.fig
│ tetra.pdf
│ ToDo.txt
│
├─geom
│ boundary.cpp
│ boundary.h
│ builder.cpp
│ builder.h
│ CMakeLists.txt
│ csg.cpp
│ csg.h
│ deformation.cpp
│ deformation.h
│ geomselect.cpp
│ geomselect.h
│ geomselect.tpp
│ isoparamP2.h
│ locator.cpp
│ locator.h
│ maketopo.cpp
│ maketopo_helper.cpp
│ multigrid.cpp
│ multigrid.h
│ principallattice.cpp
│ principallattice.h
│ reftetracut.cpp
│ reftetracut.h
│ signtraits.h
│ simplex.cpp
│ simplex.h
│ simplex.tpp
│ subtriangulation.cpp
│ subtriangulation.h
│ subtriangulation.tpp
│ topo.cpp
│ topo.h
│ triang.cpp
│
├─levelset
│ adaptriang.cpp
│ adaptriang.h
│ brick_transp.cpp
│ CMakeLists.txt
│ coupling.cpp
│ coupling.h
│ coupling.tpp
│ fastmarch.cpp
│ fastmarch.h
│ fastmarch.tpp
│ film.cpp
│ filmCoeff.cpp
│ levelset.cpp
│ levelset.h
│ levelset.tpp
│ levelsetmapper.cpp
│ levelsetmapper.h
│ lsshear.cpp
│ marking_strategy.cpp
│ marking_strategy.h
│ mgobserve.h
│ mzelle_hdr.h
│ prJump.cpp
│ reparam.cpp
│ surfacetension.cpp
│ surfacetension.h
│ twophaseCoeff.cpp
│ twophasedrops.cpp
│ twophaseutils.cpp
│ twophaseutils.h
│ volume_adjustment.cpp
│ volume_adjustment.h
│
├─misc
│ │ auto_diff.h
│ │ base64.cpp
│ │ base64.h
│ │ CMakeLists.txt
│ │ compensight.cpp
│ │ container.h
│ │ csgFunctions.cpp
│ │ dynamicload.cpp
│ │ dynamicload.h
│ │ funcmap.cpp
│ │ funcmap.h
│ │ loadlib.cpp
│ │ omp_variable.h
│ │ params.cpp
│ │ params.h
│ │ problem.cpp
│ │ problem.h
│ │ progressaccu.cpp
│ │ progressaccu.h
│ │ read_drops_matrix.m
│ │ scalarFunctions.cpp
│ │ scopetimer.cpp
│ │ scopetimer.h
│ │ singletonmap.h
│ │ staticinit.h
│ │ utils.cpp
│ │ utils.h
│ │ vectorFunctions.cpp
│ │ write_drops_matrix.m
│ │ xfem.cpp
│ │ xfem.h
│ │
│ └─kd-tree
│ bounding_box.h
│ bucket.h
│ doc.h
│ kd_tree_utils.h
│ metric.h
│ node.h
│ result.h
│ search.h
│ tree.h
│ tree_builder.h
│
├─navstokes
│ CMakeLists.txt
│ insadrops.cpp
│ insdrops.cpp
│ instatnavstokes2phase.cpp
│ instatnavstokes2phase.h
│ instatnavstokes2phase.tpp
│ integrTime.h
│ navstokes.h
│ navstokes.tpp
│ nsdrops.cpp
│ nsdrops_begehung.cpp
│
├─num
│ accumulator.h
│ bndData.cpp
│ bndData.h
│ CMakeLists.txt
│ directsolver.h
│ discretize.cpp
│ discretize.h
│ discretize.tpp
│ fe.cpp
│ fe.h
│ fe.tpp
│ fe_repair.h
│ fe_repair.tpp
│ gauss.h
│ gradient_recovery.cpp
│ gradient_recovery.h
│ hypre.cpp
│ hypre.h
│ interfacePatch.cpp
│ interfacePatch.h
│ interfacePatch.tpp
│ krylovsolver.h
│ lattice-eval.h
│ lattice-eval.tpp
│ MGsolver.cpp
│ MGsolver.h
│ MGsolver.tpp
│ newton.h
│ nssolver.h
│ oseenprecond.cpp
│ oseenprecond.h
│ oseensolver.cpp
│ oseensolver.h
│ oswald_projection.h
│ pardisosolver.h
│ parlanczos.h
│ parlanczos.tpp
│ poissonsolverfactory.h
│ precond.h
│ prolongation.h
│ prolongation.tpp
│ quadrature.cpp
│ quadrature.h
│ quadrature.tpp
│ renumber.cpp
│ renumber.h
│ renumber.tpp
│ solverbase.h
│ spacetime_geom.cpp
│ spacetime_geom.h
│ spacetime_map.cpp
│ spacetime_map.h
│ spacetime_quad.cpp
│ spacetime_quad.h
│ spacetime_quad.tpp
│ spblockmat.h
│ spmat.h
│ stokespardiso.cpp
│ stokespardiso.h
│ stokessolverfactory.h
│ unknowns.cpp
│ unknowns.h
│
├─osmosis
│ CMakeLists.txt
│ localsetups.cpp
│ osmosis.cpp
│ osmosisCoeff.cpp
│ osmosisSetup.cpp
│ osmosisSetup.h
│ tests.cpp
│
├─out
│ CMakeLists.txt
│ ensightOut.cpp
│ ensightOut.h
│ gridOut.h
│ output.cpp
│ output.h
│ vtkOut.cpp
│ vtkOut.h
│
├─parallel
│ CMakeLists.txt
│ decompose.cpp
│ decompose.h
│ decompose.tpp
│ exchange.cpp
│ exchange.h
│ exchange.tpp
│ loadbal.cpp
│ loadbal.h
│ logger.cpp
│ logger.h
│ migrateunknowns.cpp
│ migrateunknowns.h
│ parallel.cpp
│ parallel.h
│ parallel.tpp
│ parmultigrid.cpp
│ parmultigrid.h
│ parmultigrid.tpp
│ partime.cpp
│ partime.h
│ partitioner.cpp
│ partitioner.h
│ partitioner.tpp
│
├─partests
│ CMakeLists.txt
│ dist_exchange.cpp
│ dist_interface.cpp
│ dist_interp1.cpp
│ dist_interp2.cpp
│ dist_migration.cpp
│ dist_modify.cpp
│ dist_ref.cpp
│ dist_ref_rising_drop.cpp
│ dist_remotedatatest.cpp
│ dist_sdropsP2.cpp
│ dist_sendtetraofbrick.cpp
│ dist_simplexsend.cpp
│ dist_streamtest.cpp
│ dist_TestRefPar.cpp
│
├─poisson
│ │ ale.cpp
│ │ ale.h
│ │ cdrdrops.cpp
│ │ CMakeLists.txt
│ │ integrTime.h
│ │ ipdropsAD.cpp
│ │ poisson.cpp
│ │ poisson.h
│ │ poisson.tpp
│ │ poissonaccus.tpp
│ │ poissonCoeff.cpp
│ │ poissonParam.cpp
│ │ poissonParam.h
│ │ transport2phase.cpp
│ │ transport2phase.h
│ │
│ └─matlab
│ │ cxxopts.sh
│ │ dpfilm_test.m
│ │ ipdrops.cpp
│ │ ipfilm.cpp
│ │ Makefile
│ │
│ ├─directProblem
│ │ getParameters.m
│ │ plot_T.m
│ │ plot_Temp.m
│ │ qcfun.m
│ │ solveDP.m
│ │
│ ├─doc
│ │ MexDll.pdf
│ │ MexPort.txt
│ │
│ └─optimization
│ │ getParameters.m
│ │ loadIterData.m
│ │ optip.m
│ │ plotData.m
│ │ plotq.m
│ │ qcfun.m
│ │ simdp.m
│ │ simip.m
│ │ simipad.m
│ │
│ └─opt1D
│ batch.m
│ ihcp.m
│ q.m
│ solve_direct.m
│
├─spacetimetransp
│ CMakeLists.txt
│ indicator.cpp
│ indicator.h
│ spacetime_error.cpp
│ spacetime_error.h
│ spacetime_setup.cpp
│ spacetime_setup.h
│ spacetime_sol.cpp
│ spacetime_sol.h
│ sttranspCoeff.cpp
│ stxfem.cpp
│ stxfem.h
│ st_transp.cpp
│
├─stokes
│ CMakeLists.txt
│ errorestimator.cpp
│ instatstokes2phase.cpp
│ instatstokes2phase.h
│ instatstokes2phase.tpp
│ integrTime.cpp
│ integrTime.h
│ integrTime2phase.cpp
│ integrTime2phase.h
│ sdrops.cpp
│ sdropsP2.cpp
│ slipBndOnePhase.cpp
│ slipBndOnePhase.h
│ stokes.h
│ stokes.tpp
│ stokesCoeff.cpp
│ stokesCoeff.h
│
├─surfactant
│ CMakeLists.txt
│ ifacetransp.cpp
│ ifacetransp.h
│ ifacetransp.tpp
│ surfacestokes.cpp
│ surfacestokes_funcs.h
│ surfacestokes_tests.h
│ surfacestokes_utils.h
│ surfactant.cpp
│
├─surfnavierstokes
│ CMakeLists.txt
│ surfnavierstokes.cpp
│ surfnavierstokes_funcs.h
│ surfnavierstokes_tests.h
│ surfnavierstokes_utils.h
│
├─surfphasesep
│ CMakeLists.txt
│ separation.cpp
│ surfphasesep.cpp
│ surfphasesep_funcs.h
│
├─tests
│ │ base64test.cpp
│ │ bicgstab.cpp
│ │ blockmat.cpp
│ │ bndTrianglePartition.cpp
│ │ CMakeLists.txt
│ │ combiner.cpp
│ │ csgtest.cpp
│ │ directsolver.cpp
│ │ downwind.cpp
│ │ dynload.cpp
│ │ extendP1onChild.cpp
│ │ f_Gamma.cpp
│ │ gcr.cpp
│ │ globallist.cpp
│ │ interfaceP1FE.cpp
│ │ interfacepatch_doublecut.cpp
│ │ interp1.cpp
│ │ interp2.cpp
│ │ jacobi.cpp
│ │ mattest.cpp
│ │ meshreader.cpp
│ │ minres.cpp
│ │ neq.cpp
│ │ p2local.cpp
│ │ pardiso.cpp
│ │ principallattice.cpp
│ │ prolongationp2test.cpp
│ │ quad5.cpp
│ │ quad5_2D.cpp
│ │ quadbase.cpp
│ │ quadCut.cpp
│ │ quad_extra.cpp
│ │ reftest.cpp
│ │ reparamtest.cpp
│ │ reparam_init.cpp
│ │ restrictp2.cpp
│ │ sbuffer.cpp
│ │ serialization.cpp
│ │ show_domain.cpp
│ │ spacetime_decomp.cpp
│ │ spacetime_surf.cpp
│ │ splitboundary.cpp
│ │ testfe.cpp
│ │ tetrabuildertest.cpp
│ │ triang.cpp
│ │ vectest.cpp
│ │ xfem.cpp
│ │
│ ├─scripts
│ │ checktest.py
│ │ classes.py
│ │ CMakeLists.txt
│ │ readtests.py
│ │ report.py
│ │ setup.py
│ │ testDROPS.py
│ │
│ └─specifications
│ │ CMakeLists.txt
│ │
│ ├─parallel
│ │ CMakeLists.txt
│ │ DistExchange.ref
│ │ DistInterface.ref
│ │ DistInterP1.ref
│ │ DistInterP2.ref
│ │ DistMigration.ref
│ │ DistRemoteData.ref
│ │ DistSendTetraOfBrick.ref
│ │ DistSimplexSend.ref
│ │ DistStreamTest.ref
│ │
│ └─serial
│ CMakeLists.txt
│ TestBiCGStab.ref
│ Testblockmat.ref
│ TestCDRDrops.ref
│ Testcsgtest.ref
│ Testdirectsolver.ref
│ Testdownwind.ref
│ TestExtendP1onChild.ref
│ TestfGamma.ref
│ TestGCR.ref
│ TestGloballist.ref
│ TestinterfaceP1FE.ref
│ TestInterfacePatchDCut.ref
│ Testinterp1.ref
│ Testinterp2.ref
│ TestMattest.ref
│ TestMeshreader.ref
│ TestMinres.ref
│ TestP2Local.ref
│ Testprincipallattice.ref
│ TestProlongationP2Test.ref
│ TestQuad5.ref
│ TestQuad5_2D.ref
│ Testquadbase.ref
│ TestQuadCut.ref
│ Testquad_extra.ref
│ TestRef.ref
│ TestReparaminit.ref
│ TestReparamtest.ref
│ TestRestrictP2.ref
│ TestSbuffer.ref
│ TestSerialization.ref
│ TestSerialization2.ref
│ TestSplitboundary.ref
│ TestTetrabuilder.ref
│ TestTriang.ref
│ Testvectest.ref
│
└─transport
CMakeLists.txt
localsetups.cpp
ns_transp.cpp
transportCoeff.cpp
transportNitsche.cpp
transportNitsche.h
CMakeLists文件内容
源文件根目录下:drops/src/CMakeLists.txt
# 建议:使用aux_source_directory自动查找源文件,并赋值,否则源文件越多,需要添加次数就越多。
#而且,每增加一个源文件就需要修改CMakeLists.txt文件,“耦合性”太大。
# add_library:使用指定的源文件给项目添加一个库,add_library告诉生成一个库文件。
cmake_minimum_required(VERSION 2.8.1) #检查从make的版本,至少为2.8.1
project(drops) #新建一个工程,名字叫drops
cmake_policy(SET CMP0015 NEW) #cmake_policy命令的SET选项可以用来明确地指定一个特定策略。CMake在添加新特性后可能不会完全兼容旧的CMake版本,这导致了在新版本的 CMake中使用旧的CMakeLists文件时可能会存在一些问题。策略的引入就是帮助用户和开发者解决这些问题,它是CMake中用来改善向后兼容性和追踪兼容性的一种机制。允许我们再${PARMETIS_HOME} and ${METIS_HOME}中给出相对路径。(给给定的相对路径在库中,那么这些会被解释为相对于根源目录)
include(CMakeSettings.txt) #将CMakeSettings文件包含进来
#告诉CMake,总是把CMake的安装目录的家目录包含进来,src=source,bin=build,bin
#include_directories将给定的目录添加到编译器用于搜索包含文件的目录。
include_directories(${CMAKE_SOURCE_DIR})# 添加头文件的搜索目录
#使用openMP
message(STATUS "### GENERATOR is ${CMAKE_GENERATOR}")#向用户显示消息,STATUS是Mode可选项
if(CMAKE_GENERATOR MATCHES "Visual Studio 10")#如果生成为VS10工程
set(OpenMP_CXX_FLAGS "/openmp")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /openmp")
else()
if(NOT CMAKE_CXX_COMPILER_ID STREQUAL "Clang") #如果编译器ID不是Clang
find_package(OpenMP REQUIRED)#查找并加载外部项目的设置。REQUIRED可选字段。表示一定要找到包,找不到的话就立即停掉整个cmake。
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${OpenMP_CXX_FLAGS}")
endif()
endif()
#使用MPI
if(MPI)
message(STATUS "### MPI active ###")
find_package(MPI REQUIRED)
set(METIS_INCLUDE ${METIS_HOME}/include)
set(METIS_LIBRARY ${METIS_HOME}/build/Linux-x86_64/libmetis/libmetis.a)
set(PARMETIS_INCLUDE ${PARMETIS_HOME}/include)
set(PARMETIS_LIBRARY ${PARMETIS_HOME}/build/Linux-x86_64/libparmetis/libparmetis.a)
include_directories(${MPI_INCLUDE_PATH} ${PARMETIS_INCLUDE} ${METIS_INCLUDE})
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${MPI_CXX_FLAGS} -DMPICH_IGNORE_CXX_SEEK -DOMPI_SKIP_MPICXX")
set(PARMETIS_LIBRARIES ${MPI_CXX_LIBRARIES} ${METIS_LIBRARY} ${PARMETIS_LIBRARY} m)
add_definitions("-D_PAR=1")
else(MPI)
message(STATUS "### MPI disabled ###")
endif(MPI)
if(WIN32)#如果给出WIN32
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /DDROPS_WIN /I${BOOST_HOME}")
else(WIN32)
if (BOOST_HOME STREQUAL "")
else (BOOST_HOME STREQUAL "")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -I${BOOST_HOME}")
endif (BOOST_HOME STREQUAL "")
if (MKL_HOME STREQUAL "")
else (MKL_HOME STREQUAL "")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DDROPS_PARDISO -DMKL_ILP64 -m64 -I${MKL_HOME}/include -Wno-long-long")
endif (MKL_HOME STREQUAL "")
endif(WIN32)
#编译器指定选项
if(CMAKE_CXX_COMPILER_ID STREQUAL "Intel")
message(STATUS "### setting flags for INTEL compiler ###")
set(DROPS_CXX_FLAGS_DEBUG "${IPC_CXX_FLAGS_DEBUG}")
set(DROPS_CXX_FLAGS_RELEASE "${IPC_CXX_FLAGS_RELEASE}")
elseif(CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
message(STATUS "### setting flags for GCC compiler ###")
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,--as-needed")
set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -Wl,--as-needed")
set(DROPS_CXX_FLAGS_DEBUG "${GCC_CXX_FLAGS_DEBUG}")
set(DROPS_CXX_FLAGS_RELEASE "${GCC_CXX_FLAGS_RELEASE}")
elseif(CMAKE_CXX_COMPILER_ID STREQUAL "XL")
message(STATUS "### setting flags for IBM XL compiler ###")
set(DROPS_CXX_FLAGS_DEBUG "${XL_CXX_FLAGS_DEBUG}")
set(DROPS_CXX_FLAGS_RELEASE "${XL_CXX_FLAGS_RELEASE}")
elseif(CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
message(STATUS "### setting flags for CLANG compiler ###")
set(DROPS_CXX_FLAGS_DEBUG "${CLANG_CXX_FLAGS_DEBUG}")
set(DROPS_CXX_FLAGS_RELEASE "${CLANG_CXX_FLAGS_RELEASE}")
else()
message(STATUS "### could not determine compiler ${CMAKE_CXX_COMPILER_ID}, it seems to be neither gcc nor intel")
endif()
if(NOT DROPS_BUILD_TYPE)
set(DROPS_BUILD_TYPE "DEBUG")
message(STATUS "### Setting DEFAULT build type: ${DROPS_BUILD_TYPE} ###")
else(NOT DROPS_BUILD_TYPE)
message(STATUS "### BUILD TYPE: ${DROPS_BUILD_TYPE} ###")
endif(NOT DROPS_BUILD_TYPE)
#应用编译链接类型指定选项
if(DROPS_BUILD_TYPE STREQUAL "RELEASE")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${DROPS_CXX_FLAGS_RELEASE}")
elseif(DROPS_BUILD_TYPE STREQUAL "DEBUG")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${DROPS_CXX_FLAGS_DEBUG}")
endif()
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${DROPS_CXX_FLAGS} -fPIC")
message(STATUS "### CXX_COMPILER: ${CMAKE_CXX_COMPILER}")
message(STATUS "### GLOBAL COMPILER FLAGS: ${CMAKE_CXX_FLAGS} ###")
message(STATUS "### GLOBAL SHARED LINKER FLAGS: ${CMAKE_SHARED_LINKER_FLAGS} ###")
message(STATUS "### GLOBAL EXE LINKER FLAGS: ${CMAKE_EXE_LINKER_FLAGS} ###")
###############################################################################
# libs*函数是add_library的一种特殊情况,允许我们在一行中添加多个文件
# ${HOME}自动被准备好了,这些函数需要${HOME}
# 静态链接库
function(libs)
foreach(lib ${ARGN})# 变量ARGN也是一个包含传入参数的list,遍历赋值为lib
add_library(${HOME}-${lib} STATIC ${lib}) #STATIC静态库,在链接其他目标时使用。生成一个中间库,供别的调用
endforeach(lib)
endfunction(libs)
function(libs_ser)
if (NOT MPI)
libs(${ARGN})
endif()
endfunction(libs_ser)
function(libs_par)
if (MPI)
libs(${ARGN})
endif()
endfunction(libs_par)
###############################################################################
# target_link_libraries_par 把 ${PAR_OBJ} ${MPI_CXX_LIBRARIES}
# ${PARMETIS_LIBRARIES} 这几个库链接到调用的库目标上
function(target_link_libraries_ser)
if (NOT MPI)
target_link_libraries(${ARGN})#target_link_libraries将给定的库链接到一个目标上。
endif()
endfunction(target_link_libraries_ser)
function(target_link_libraries_par)
if (MPI)
target_link_libraries(${ARGN} ${PAR_OBJ} ${MPI_CXX_LIBRARIES} ${PARMETIS_LIBRARIES})
endif()
endfunction(target_link_libraries_par)
###############################################################################
# 设置MPI的一个变量依赖
function(set_par)
if (MPI)
set(${ARGN} PARENT_SCOPE)
endif()
endfunction(set_par)
if (WIN32)
# set(BEGIN_STATIC_LIBS -OPT:NOREF)
# set(END_STATIC_LIBS -OPT:REF)
else(WIN32)
set(BEGIN_STATIC_LIBS -Wl,--whole-archive)
set(END_STATIC_LIBS -Wl,--no-whole-archive)
endif(WIN32)
###############################################################################
# exec命令能否用于方便地添加一个可执行程序
# 用法:filename <link_objects...>
# 首先一个库和带有合适名字的可执行目标被创建
# 然后,我们添加<link_objects...>作为可执行程序的依赖需求
# 这个函数需要 ${HOME} and ${PAR_OBJ}
function(exec_ f)
# libs(${f})
add_executable(${f} ${f}) #使用指定的源文件给项目添加一个可执行文件。第一个参数是可执行文件名,关键一步
# 使用c文件进行编译链接
endfunction(exec_)
function(exec_ser f)
if(NOT MPI)
exec_(${f})
target_link_libraries(${f} ${ARGN} ${${f}-staticlibs})#f生成f,并将输入参数向量以及f-staticlibs的依赖传入
endif()
endfunction(exec_ser)
function(exec_par f)
if(MPI)
exec_(${f})
target_link_libraries_par(${f} ${ARGN} ${${f}-staticlibs})
endif()
endfunction(exec_par)
function(exec f)
exec_ser(${f} ${ARGN})
exec_par(${f} ${ARGN})
endfunction(exec)
function(add_my_custom_targets f)
add_custom_target(${f}-param-files
echo \"copy $f json files...\" \;
cp -f ${CMAKE_CURRENT_SOURCE_DIR}/*.json ${CMAKE_CURRENT_BINARY_DIR}/ 2> /dev/null \;
COMMENT "copy param files from ${CMAKE_CURRENT_SOURCE_DIR}/ to ${CMAKE_CURRENT_BINARY_DIR}/"# COMMENT可选命令,在构建时执行命令之前显示给定消息。
)# add_custom_target该命令可以给指定名称的目标执行指定的命令,该目标没有输出文件,并始终被构建。
add_custom_target(${f}-stat COMMAND ls ${CMAKE_CURRENT_SOURCE_DIR}/*.cpp ${CMAKE_CURRENT_SOURCE_DIR}/*.tpp ${CMAKE_CURRENT_SOURCE_DIR}/*.h | xargs wc -l WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR})# xargs是给命令传递参数的一个过滤器,也是组合多个命令的一个工具。
add_custom_target(${f}-coeffs DEPENDS misc-scalarFunctions misc-vectorFunctions levelset-twophaseCoeff levelset-filmCoeff poisson-poissonCoeff stokes-stokesCoeff)#DEPENDS
endfunction(add_my_custom_targets f)
set(PACKAGES parallel levelset poisson DiST geom num out misc osmosis stokes navstokes surfactant spacetimetransp transport partests tests surfnavierstokes surfphasesep)
string(REPLACE ";" " " PACKAGES_STRING "${PACKAGES}")# string(REPLACE <match_string><replace_string> <output variable><input> [<input>...])
option(TESTS "compile tests" OFF)# 可选项TESTS关闭
if(TESTS)
list(APPEND PACKAGES tests)# 列表操作list(APPEND <list> [<element> ...])
enable_testing()# 使能,从这一刻起,就可以在工程中添加测试
endif(TESTS)
foreach(package ${PACKAGES})
add_subdirectory(${package})# 向构建中添加子目录。这样的话,子文件夹中的CMakelists也被执行
# 顶层的 CMakeList.txt 文件中使用 add_subdirectory 告诉cmake去子目录寻找新的CMakeList.txt子文件
endforeach(${package})
add_custom_target(param-files
COMMAND for PACK in ${PACKAGES} \; do
echo \"copy \$$PACK json files...\" \;
cp -f ${CMAKE_CURRENT_SOURCE_DIR}/\$$PACK/*.json ${CMAKE_CURRENT_BINARY_DIR}/\$$PACK/ 2> /dev/null \;
done
COMMENT "copy param files from ${CMAKE_CURRENT_SOURCE_DIR} to ${CMAKE_CURRENT_BINARY_DIR}"
)
add_custom_target(stat COMMAND ls */*.cpp */*.tpp */*.h | xargs wc -l WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR})
add_custom_target(distclean COMMAND rm -rf ${CMAKE_CURRENT_BINARY_DIR}/*)
add_custom_target(ctagsdb COMMAND ctags -R --c++-kinds=+p --extra=+q --fields=+iaS --tag-relative=yes --c++-types=+px --excmd=pattern --exclude=Makefile --exclude= --langmap=c++:+.tpp -f ${CMAKE_CURRENT_BINARY_DIR}/ctagsdb WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR})
# CMAKE_CURRENT_BINARY_DIR当前正在被处理的二进制目录的路径。CMAKE_CURRENT_SOURCE_DIR指向正在被处理的源码目录的路径。
find_package(Doxygen)
if(DOXYGEN_FOUND)
FILE(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/doxygen)# 文件操作file(MAKE_DIRECTORY [<directories>...])
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/dox.cfg ${CMAKE_CURRENT_BINARY_DIR}/doxygen/dox.cfg)#configure_file将文件复制到其他位置并修改其内容。
add_custom_target(doc
${DOXYGEN_EXECUTABLE} ${CMAKE_CURRENT_BINARY_DIR}/doxygen/dox.cfg
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}#WORKING_DIRECTORY给定的当前工作目录
COMMENT "Generating API documentation with Doxygen" VERBATIM
)
endif()
add_custom_target(coeffs DEPENDS misc-scalarFunctions misc-vectorFunctions levelset-twophaseCoeff levelset-filmCoeff poisson-poissonCoeff stokes-stokesCoeff)
源文件根目录下:drops/src/CMakeSettings.txt
##########################################################################
# 外部库文件的引用
##########################################################################
# set的用法表示变量赋值,前面一个变量名表示后面字符串里面的含义
# cache是说这个变量缓存的位置。你在运行cmake的时候,变量的值都会被缓存一份到文件里。
# 当你重新运行cmake的时候,那些变量会使用这个缓存的值,除非你显式地重新赋值。
# CMAKE_SOURCE_DIR是个内置宏,表示Cmake的安装路径
set(PARMETIS_HOME "${CMAKE_SOURCE_DIR}/../parmetis-4.0.2" CACHE PATH "")
set(METIS_HOME "${CMAKE_SOURCE_DIR}/../parmetis-4.0.2/metis" CACHE PATH "")
set(ZOLTAN_HOME "${CMAKE_SOURCE_DIR}/../Zoltan_v3.2" CACHE PATH "")
set(HYPRE_HOME "${CMAKE_SOURCE_DIR}/../hypre-2.0.0" CACHE PATH "")
set(SCOTCH_HOME "${CMAKE_SOURCE_DIR}/../scotch_5.1.8a" CACHE PATH "")
set(SUITESPARSE_HOME "${CMAKE_SOURCE_DIR}/../SuiteSparse" CACHE PATH "")
set(BOOST_HOME "" CACHE PATH "")
set(MKL_HOME "" CACHE PATH "")
set(DROPS_BUILD_TYPE "" CACHE STRING "")
##########################################################################
# 预定义编译器的一些标志
##########################################################################
# Intel 编译编译器
set(IPC_CXX_FLAGS_DEBUG "-DVALARRAY_DEBUG -wd1572 -g -std=c++11")
set(IPC_CXX_FLAGS_RELEASE "-O3 -ip -fp-model fast=2 -std=c++11")
# GNU 编译器
set(GCC_CXX_FLAGS_DEBUG "-g -O0 -W -Wall -pedantic -std=c++11")
set(GCC_CXX_FLAGS_RELEASE "-O3 --fast-math -W -Wall -pedantic -std=c++11")
# IBM 编译器
set(XL_CXX_FLAGS_DEBUG "-O0 -g -qlanglvl=extended0x")
set(XL_CXX_FLAGS_RELEASE "-O5 -qlanglvl=extended0x")
# CLANG 编译器
set(CLANG_CXX_FLAGS_DEBUG "-g -O0 -W -Wall -pedantic -Wno-unknown-pragmas -std=c++11")
set(CLANG_CXX_FLAGS_RELEASE "-O3 -W -Wall -pedantic -Wno-unknown-pragmas -std=c++11")
# 编译脚本传入参数,MPI 默认关闭,使用cmake -DMPI=ON
option(MPI FALSE)
子文件夹下举例:drops/src/surfactant/CMakeSettings.txt
set(HOME surfactant)# HOME表示当前包目录
libs(ifacetransp)# libs是根CMakeLists定义的函数,生成库文件surfactant-ifacetransp
target_link_libraries(surfactant-ifacetransp levelset-levelsetmapper)# 生成最后总目标的时候需要的库文件添加进来
# 将目标文件surfactant-ifacetransp和levelset-levelsetmapper链接
exec_ser(surfactant geom-boundary geom-builder geom-simplex geom-multigrid geom-deformation num-unknowns geom-topo num-fe misc-problem levelset-levelset levelset-marking_strategy levelset-adaptriang misc-scopetimer misc-progressaccu misc-utils out-output num-discretize misc-params num-interfacePatch levelset-fastmarch surfactant-ifacetransp num-fe out-ensightOut levelset-surfacetension out-vtkOut geom-principallattice geom-reftetracut geom-subtriangulation num-quadrature misc-dynamicload misc-funcmap misc-scopetimer num-gradient_recovery levelset-levelsetmapper)
# exec_ser是根CMakeLists定义的,生成总目标添加最后的库文件
exec_ser(surfacestokes geom-boundary geom-builder geom-simplex geom-multigrid geom-deformation num-unknowns geom-topo num-fe misc-problem levelset-levelset levelset-marking_strategy levelset-adaptriang misc-scopetimer misc-progressaccu misc-utils out-output num-discretize misc-params num-interfacePatch levelset-fastmarch surfactant-ifacetransp num-fe out-ensightOut levelset-surfacetension out-vtkOut geom-principallattice geom-reftetracut geom-subtriangulation num-quadrature misc-dynamicload misc-funcmap misc-scopetimer)
if(NOT MPI)
add_dependencies(surfactant misc-scalarFunctions misc-vectorFunctions) # 使顶级目标依赖于其他顶级目标
# 这里的顶级目标是由add_executable,add_library或add_custom_target命令之一创建的目标。
add_dependencies(surfacestokes misc-scalarFunctions misc-vectorFunctions levelset-twophaseCoeff)
endif(NOT MPI)
add_my_custom_targets(surfactant surfacestokes)
# add_my_custom_targets指定的目标执行指定的命令,根CMakeLists中定义
参考外链
子目录cmake文件一个形象的比喻
做车间一般零件
libs(ifacetransp)
给车间一般零件上漆
target_link_libraries(surfactant-ifacetransp levelset-levelsetmapper)
生成顶级的零件
add_library(poisson-poissonCoeff SHARED poissonCoeff)
拿自己和别的车厢的一般零件拼成整车
exec_ser(surfactant geom-boundary geom-builder geom-simplex geom-multigrid geom-deformation num-unknowns geom-topo num-fe misc-problem levelset-levelset levelset-marking_strategy levelset-adaptriang misc-scopetimer misc-progressaccu misc-utils out-output num-discretize misc-params num-interfacePatch levelset-fastmarch surfactant-ifacetransp num-fe out-ensightOut levelset-surfacetension out-vtkOut geom-principallattice geom-reftetracut geom-subtriangulation num-quadrature misc-dynamicload misc-funcmap misc-scopetimer num-gradient_recovery levelset-levelsetmapper)
用顶级零件再给车子装饰一下
add_dependencies(surfactant misc-scalarFunctions misc-vectorFunctions)
下面这个似乎没什么用
add_my_custom_targets(surfactant surfacestokes)
下附一份Cmake官方文档(中文翻译):
Cmake tutorial
cmake官方教程(翻译)
本教程所有源代码取自cmake源码Test/Tutorial
本教程运行环境为ubuntu 16.04,cmake 版本3.5.1
安装cmake:
apt install cmake
Step0 起步
最基础的项目工程是通过单一源文件编译得到可执行文件。对于这样简单的工程,只需要两行CMakeLists.txt代码即可实现。以下是一个简单示例。
先建立一个文件夹,用于存放我们的工程。
mkdir -p cmake/step1
cd cmake/step1
touch tutorial.cxx
touch CMakeLists.txt
源代码tutoriol.cxx实现了求一个数的平方根的功能:
#include <cmath>
#include <cstdlib>
#include <iostream>
#include <string>
int main(int argc, char *argv[])
{
if (argc < 2)
{
std::cout << "Usage: " << argv[0] << " number" << std::endl;
return 1;
}
double inputValue = atof(argv[1]);
double outputValue = sqrt(inputValue);
std::cout << "The square root of " << inputValue
<< " is " << outputValue << std::endl;
return 0;
}
CMakeLists.txt的内容:
project (Tutorial)
add_executable(Tutorial tutorial.cxx)
CMakeLists.txt中的命令支持大写、小写,大小写混合。本例中使用小写。
编译的时候,在当前目录下新建一个build目录,用于存放编译过程产生的文件以及生成的可执行文件。
mkdir build
cd build
cmake ..
make
cmake … 表示在…(上级)目录中寻找CMakeLists.txt进行编译,cmake执行成功后会自动生成Makefile文件,执行make即可完成编译。
cmake 执行结果:
root@x:~/qinrui/cmake/step1/build# cmake ..
-- The C compiler identification is GNU 5.4.0
-- The CXX compiler identification is GNU 5.4.0
-- Check for working C compiler: /usr/bin/cc
-- Check for working C compiler: /usr/bin/cc -- works
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Detecting C compile features
-- Detecting C compile features - done
-- Check for working CXX compiler: /usr/bin/c++
-- Check for working CXX compiler: /usr/bin/c++ -- works
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Detecting CXX compile features
-- Detecting CXX compile features - done
-- Configuring done
-- Generating done
-- Build files have been written to: /root/qinrui/cmake/step1/build
make执行结果:
root@x:~/qinrui/cmake/step1/build# make
Scanning dependencies of target Tutorial
[ 50%] Building CXX object CMakeFiles/Tutorial.dir/tutorial.cxx.o
[100%] Linking CXX executable Tutorial
[100%] Built target Tutorial
Step1 添加版本号和配置头文件
我们要给当前工程添加的第一个特性是版本号信息。尽管可以在源代码中完成该操作,但在CMakeLists.txt中完成此操作能够大大提升灵活性。我们可以按照以下方式修改CMakeLists.txt
注意,在CMakeLists.txt中,"#"表示注释。
cmake_minimum_required (VERSION 3.3)
project (Tutorial)
# 版本号信息
set (Tutorial_VERSION_MAJOR 1)
set (Tutorial_VERSION_MINOR 0)
# 配置一个头文件,从而将CMake的一些设置传递到源文件中。
# 以TutorialConfig.h.in为模板,生成TutorialConfig.h
configure_file (
"${PROJECT_SOURCE_DIR}/TutorialConfig.h.in"
"${PROJECT_BINARY_DIR}/TutorialConfig.h"
)
# 将构建目录添加到include的搜索路径中
# 这样就能够找到TutorialConfig.h
include_directories("${PROJECT_BINARY_DIR}")
# 添加可执行文件名
add_executable(Tutorial tutorial.cxx)
因为配置文件会被写入构建目录中,所以我们将这个目录添加到include文件的搜索范围中。
我们在工程中添加头文件模板TutorialConfig.h.in:
// 配置信息
#define Tutorial_VERSION_MAJOR @Tutorial_VERSION_MAJOR@
#define Tutorial_VERSION_MINOR @Tutorial_VERSION_MINOR@
当Cmake生成这个配置文件时,@Tutorial_VERSION_MAJOR@ 和 @Tutorial_VERSION_MINOR@ 将会被CMakeLists.txt中设置的值替换。接下来我们修改tutorial.cxx来include头文件以获取版本号。
添加了一条include以引用TutorialConfig.h。在usage 信息中添加了版本号。
#include <cmath>
#include <cstdlib>
#include <iostream>
#include <string>
#include "TutorialConfig.h"
int main(int argc, char *argv[])
{
if (argc < 2)
{
std::cout << argv[0] << " Version "
<< Tutorial_VERSION_MAJOR << "."
<< Tutorial_VERSION_MINOR << std::endl;
std::cout << "Usage: " << argv[0] << " number" << std::endl;
return 1;
}
double inputValue = atof(argv[1]);
double outputValue = sqrt(inputValue);
std::cout << "The square root of " << inputValue
<< " is " << outputValue << std::endl;
return 0;
}
将build的内容清空,重新cmake,make编译。
cd build
rm -rf *
cmake ..
make
此时的文件目录结构为:(省略了CMakeFiles内的文件)
.
├── build
│ ├── CMakeCache.txt
│ ├── CMakeFiles
│ ├── cmake_install.cmake
│ ├── Makefile
│ ├── Tutorial
│ └── TutorialConfig.h
├── CMakeLists.txt
├── TutorialConfig.h.in
└── tutorial.cxx
运行结果:
root@x:~/qinrui/cmake/step1/build# ./Tutorial
./Tutorial Version 1.0
Usage: ./Tutorial number
Step2 添加库文件
接下来我们将会往项目中添加库文件。这个库包含了我们自己写的sqrt的实现,以替代系统库。本教程中我们将库文件放入MathFunctions子目录中。这个字母录也包含一个CMakeLists.txt文件,只有一行
add_library(MathFunctions mysqrt.cxx)
添加MathFunctions.h头文件,同样只有一行定义:
double mysqrt(double x);
在mysqrt.cxx中给出mysqrt实现:
#include "MathFunctions.h"
#include <iostream>
double mysqrt(double x)
{
if (x <= 0)
{
return 0;
}
double result = x;
// 循环计算10次
for (int i = 0; i < 10; ++i)
{
if (result <= 0)
{
result = 0.1;
}
double delta = x - (result * result);
result = result + 0.5 * delta / result;
}
return result;
}
在mysqrt.cxx源代码中提供了mysqrt函数,实现了和sqrt函数相同的功能。为了使用新的库,我们在顶层CMakeLists中添加一行add_subdirectory调用,这样库文件就会被构建。同时添加include路径,使得MathFunctions/MathFunctions.h 头文件中定义的函数原型能被找到。最后一步是将库文件添加到可执行文件中。在顶层CMakeLists.txt中添加:
include_directories ("${PROJECT_SOURCE_DIR}/MathFunctions")
add_subdirectory (MathFunctions)
# 添加可执行文件
add_executable (Tutorial tutorial.cxx)
target_link_libraries (Tutorial MathFunctions)
下面考虑将MathFunctions这个库设置成可选的。当使用较大的库或者第三方库时会用到该功能。在顶层CMakeLists.txt中添加:
# 是否使用自定义函数库
option (USE_MYMATH "Use tutorial provided math implementation" ON)
下一步我们要将MathFunctions库的构建和链接设置成可选的。对此我们做出如下修改:
# 是添加自定义库
if (USE_MYMATH)
include_directories ("${PROJECT_SOURCE_DIR}/MathFunctions")
add_subdirectory (MathFunctions)
set (EXTRA_LIBS ${EXTRA_LIBS} MathFunctions)
endif (USE_MYMATH)
# 添加可执行文件
add_executable (Tutorial tutorial.cxx)
target_link_libraries (Tutorial ${EXTRA_LIBS})
使用USE_MYMATH来判定是否需要编译使用MathFunctions。这里使用了一个EXTRA_LIBS变量来收集所有可选的库,用于链接到可执行文件。这是一种保证具有多重选择的大型项目整洁性的常用方法。相关源代码的修改如下:
#include <cmath>
#include <iostream>
#include <string>
#include "TutorialConfig.h"
#ifdef USE_MYMATH
#include "MathFunctions.h"
#endif
int main(int argc, char *argv[])
{
if (argc < 2)
{
std::cout << argv[0] << " Version " << Tutorial_VERSION_MAJOR
<< "." << Tutorial_VERSION_MINOR << std::endl;
std::cout << "Usage: " << argv[0] << " number" << std::endl;
return 1;
}
double inputValue = std::stod(argv[1]);
#ifdef USE_MYMATH
double outputValue = mysqrt(inputValue);
std::cout << "Mysqrt: The square root of " << inputValue
<< " is " << outputValue << std::endl;
#else
double outputValue = sqrt(inputValue);
std::cout << "sqrt: The square root of " << inputValue
<< " is " << outputValue << std::endl;
#endif
return 0;
}
在源代码中我们同样使用了USE_MYMATH,CMake通过TutorialConfig.h.in将该值传递到源代码中。需要在该文件内添加:
#cmakedefine USE_MYMATH
Step3 添加库的依赖项
使用依赖项可以更好地控制库/可执行文件的链接和引用。同时也提供了对CMake内目标属性的控制。使用依赖项的主要语法是:
- target_compile_definitions
- target_compile_options
- target_include_directories
- target_link_libraries
首先是MathFunctions。我们首先声明任何链接到MathFunctions的文件都 需要包含当前的源目录,而MathFunctions本身 没有。因此,这可以使用INTERFACE 依赖项。在MathFunction目录下的CMakeLists.txt中添加:
# 声明所有链接到我们的都需要引用当前目录,
# 去寻找MathFunctions.h,而我们自身不需要
target_include_directories(MathFunctions
INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}
)
这样我们就为MathFunctions指定了使用要求,我们可以安全地删除之前使用的EXTRA_INCLUDES变量。
Step4 安装与测试
现在我们可以开始添加测试功能和安装规则。
安装规则相对简单:对MathFunctions我们安装库文件和头文件,对应用我们安装可执行文件并配置头文件。
在MathFunctions/CMakeLists.txt中添加:
install (TARGETS MathFunctions DESTINATION bin)
install (FILES MathFunctions.h DESTINATION include)
在顶层CMakeLists.txt中添加:
install(TARGETS Tutorial DESTINATION bin)
install(FILES "${PROJECT_BINARY_DIR}/TutorialConfig.h"
DESTINATION include
)
在经过cmake和make后,使用make installl 命令进行安装,输出如下:
root@x:~/qinrui/cmake/step4/build# make install
[ 50%] Built target MathFunctions
[100%] Built target Tutorial
Install the project...
-- Install configuration: ""
-- Installing: /usr/local/bin/Tutorial
-- Installing: /usr/local/include/TutorialConfig.h
-- Installing: /usr/local/bin/libMathFunctions.a
-- Installing: /usr/local/include/MathFunctions.h
此时Tutorial即已经被安装到系统中,不需要再当前目录下执行,也不需要使用./运行。
接下来对我们的应用进行测试。在顶层CMakeLists.txt中我们可以添加一些测试样例来验证应用的正确性。
# 启用测试
enable_testing()
# 测试应用能否运行
add_test(NAME Runs COMMAND Tutorial 25)
# 测试Usage语句是否正确
add_test(NAME Usage COMMAND Tutorial)
set_tests_properties(Usage
PROPERTIES PASS_REGULAR_EXPRESSION "Usage:.*number"
)
# 定义一个测试样例函数
function(do_test target arg result)
add_test(NAME Comp${arg} COMMAND ${target} ${arg})
set_tests_properties(Comp${arg}
PROPERTIES PASS_REGULAR_EXPRESSION ${result}
)
endfunction(do_test)
# 添加测试样例
do_test(Tutorial 25 "25 is 5")
do_test(Tutorial -25 "-25 is [-nan|nan|0]")
do_test(Tutorial 0.0001 "0.0001 is 0.01")
编译完成后,要运行这些测试,需要执行
ctest -N
ctest -VV
root@x:~/qinrui/cmake/step4/build# ctest -N
Test project /root/qinrui/cmake/step4/build
Test #1: Runs
Test #2: Usage
Test #3: Comp25
Test #4: Comp-25
Test #5: Comp0.0001
Total Tests: 5
root@x:~/qinrui/cmake/step4/build# ctest -VV
...from :/root/qinrui/cmake/step4/build/DartConfiguration.tcl
...from :/root/qinrui/cmake/step4/build/DartConfiguration.tcl
Test project /root/qinrui/cmake/step4/build
Constructing a list of tests
Done constructing a list of tests
Checking test dependency graph...
Checking test dependency graph end
test 1
Start 1: Runs
1: Test command: /root/qinrui/cmake/step4/build/Tutorial "25"
1: Test timeout computed to be: 9.99988e+06
1: Mysqrt: The square root of 25 is 5
1/5 Test #1: Runs ............................. Passed 0.00 sec
test 2
Start 2: Usage
2: Test command: /root/qinrui/cmake/step4/build/Tutorial
2: Test timeout computed to be: 9.99988e+06
2: /root/qinrui/cmake/step4/build/Tutorial Version 1.0
2: Usage: /root/qinrui/cmake/step4/build/Tutorial number
2/5 Test #2: Usage ............................ Passed 0.00 sec
test 3
Start 3: Comp25
3: Test command: /root/qinrui/cmake/step4/build/Tutorial "25"
3: Test timeout computed to be: 9.99988e+06
3: Mysqrt: The square root of 25 is 5
3/5 Test #3: Comp25 ........................... Passed 0.00 sec
test 4
Start 4: Comp-25
4: Test command: /root/qinrui/cmake/step4/build/Tutorial "-25"
4: Test timeout computed to be: 9.99988e+06
4: Mysqrt: The square root of -25 is 0
4/5 Test #4: Comp-25 .......................... Passed 0.00 sec
test 5
Start 5: Comp0.0001
5: Test command: /root/qinrui/cmake/step4/build/Tutorial "0.0001"
5: Test timeout computed to be: 9.99988e+06
5: Mysqrt: The square root of 0.0001 is 0.01
5/5 Test #5: Comp0.0001 ....................... Passed 0.00 sec
100% tests passed, 0 tests failed out of 5
Total Test time (real) = 0.01 sec
Step5 添加系统自检
让我们考虑当往项目中添加的代码中依赖的功能目标平台没有的情况。比如,我们需要添加的代码取决于目标平台是否有log和exp函数。我们假设这些功能不常见。
如果平台拥有log和exp函数,我们就在mysqrt函数中使用它们来计算平方根。我们先通过在顶层CMakeLists.txt中使用CheckSymbolExists.cmake宏来测试这些功能。
# 当前系统是否提供log和exp函数?
include(CheckSymbolExists)
set(CMAKE_REQUIRED_LIBRARIES "m")
check_symbol_exists(log "math.h" HAVE_LOG)
check_symbol_exists(exp "math.h" HAVE_EXP)
cmake时的输出:
-- Looking for log
-- Looking for log - found
-- Looking for exp
-- Looking for exp - found
接下来在TutorialConfig.h.in中添加这些定义,使我们能够在mysqrt.cxx中使用它们
// 平台是否有log和exp函数
#cmakedefine HAVE_LOG
#cmakedefine HAVE_EXP
接下来修改MathFunctions/mysqrt.cxx。先引用cmath头文件,然后使用#ifdef,给出一个由log和exp函数实现的sqrt函数。
#include "MathFunctions.h"
//#include "TutorialConfig.h"
#include <iostream>
#include <cmath>
double mysqrt(double x)
{
if (x <= 0)
{
return 0;
}
// 如果有log和exp就使用它们
#if defined(HAVE_LOG) && defined(HAVE_EXP)
double result = exp(log(x) * 0.5);
std::cout << "Computing sqrt of " << x
<< " to be " << result << " using log" << std::endl;
#else
double result = x;
//循环十次
for (int i = 0; i < 10; ++i)
{
if (result <= 0)
{
result = 0.1;
}
double delta = x - (result * result);
result = result + 0.5 * delta / result;
std::cout << "Computing sqrt of " << x
<< " to be " << result << std::endl;
}
#endif
return result;
}
此时如果我们运行cmake,HAVE_LOG和HAVE_EXP都被定义了,但是mysqrt并没有使用它们。那是因为我们没有在mysqrt.cxx中引用TutorialConfig.h(被我特意注释了)。同时我们要更新对应的CMakeLists.txt文件,告诉它该头文件在哪。
add_library(MathFunctions mysqrt.cxx)
# 声明所有链接到我们的都需要引用当前目录,
# 去寻找MathFunctions.h,而我们自身不需要
# 我们自己需要 Tutorial_BINARY_DIR 但我们的使用者不需要,
# 所以定义为PRIVATE
target_include_directories(MathFunctions
INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}
PRIVATE ${Tutorial_BINARY_DIR}
)
install(TARGETS MathFunctions DESTINATION lib)
install(FILES MathFunctions.h DESTINATION include)
实际上,更好地处理方法是将HAVE_LOG和HAVE_EXP定义在MathFunctions.h中。
运行结果:
root@x:~/qinrui/cmake/step5/build# ./Tutorial 123
Computing sqrt of 123 to be 11.0905 using log
Mysqrt: The square root of 123 is 11.0905
Step6 添加自定义命令和生成文件
本节我们会展示如何将一个生成的文件添加到应用程序的构建过程中。在本例中,我们建立一张预先计算好的平方根表,作为构建的一部分,将表编译进应用程序中。
首先,我们需要一个生成表的程序。我们在MathFunctions子目录下新建MakeTable.cxx文件来实现这个功能。
// 建立平方根表
#include <cmath>
#include <fstream>
#include <iostream>
int main(int argc, char *argv[])
{
if (argc < 2)
{
return 1;
}
std::ofstream fout(argv[1], std::ios_base::out);
const bool fileOpen = fout.is_open();
if (fileOpen)
{
fout << "double sqrtTable[] = {" << std::endl;
for (int i = 0; i < 10; ++i)
{
fout << sqrt(static_cast<double>(i)) << "," << std::endl;
}
fout << "0};" << std::endl;
fout.close();
}
return fileOpen ? 0 : 1;
}
注意在这段C++程序中,输出文件名是由命令行参数决定的。
接下来就是要在MathFunctions的CMakeLists.txt中添加命令,使改程序的运行成为构建的一部分。
# 首先添加一个生成表格的可执行文件
add_executable(MakeTable MakeTable.cxx)
# 添加命令去生成源代码
add_custom_command(
OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/Table.h
COMMAND MakeTable ${CMAKE_CURRENT_BINARY_DIR}/Table.h
DEPENDS MakeTable
)
# 添加库
add_library(MathFunctions
mysqrt.cxx
${CMAKE_CURRENT_BINARY_DIR}/Table.h
)
# 声明所有链接到我们的都需要引用当前目录,
# 去寻找MathFunctions.h,而我们自身不需要
# 我们自己需要 Tutorial_BINARY_DIR 但我们的使用者不需要,
# 所以定义为PRIVATE
# 我们需要可执行目录去寻找table.h
target_include_directories(MathFunctions
INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}
PRIVATE ${Tutorial_BINARY_DIR}
${CMAKE_CURRENT_BINARY_DIR}
)
install(TARGETS MathFunctions DESTINATION lib)
install(FILES MathFunctions.h DESTINATION include)
首先,MakeTable的可执行文件被添加。之后我们添加一个命令,用于指定Table.h并运行生成MakeTable 所以我们需要让CMake知道mysqrt.cxx依赖于生成的文件Table.h。将Table.h添加到MathFunctions库中的源代码列表。 此外还需要将可执行目录添加到include目录列表中,以便Table.h能够发现并包含在mysqrt.cxx中。
#include "MathFunctions.h"
#include "TutorialConfig.h"
#include <iostream>
// 引用生成的表
#include "Table.h"
#include <cmath>
double mysqrt(double x)
{
if (x <= 0)
{
return 0;
}
// 通过查表来辅助查找一个初值
double result = x;
if (x >= 1 && x < 10)
{
result = sqrtTable[static_cast<int>(x)];
}
// 循环计算十次
for (int i = 0; i < 10; ++i)
{
if (result <= 0)
{
result = 0.1;
}
double delta = x - (result * result);
result = result + 0.5 * delta / result;
std::cout << "Computing sqrt of " << x
<< " to be " << result << std::endl;
}
return result;
}
cmake编译运行,找到Table.h的位置及内容
root@x:~/qinrui/cmake/step6# find . -name Table.h
./build/MathFunctions/Table.h
root@x:~/qinrui/cmake/step6/build# cat MathFunctions/Table.h
double sqrtTable[] = {
0,
1,
1.41421,
1.73205,
2,
2.23607,
2.44949,
2.64575,
2.82843,
3,
0};
Step7 构建安装程序
接下来我们假设我们需要将项目提供给他人使用。我们希望在多种平台上提供二进制和源代码。 这和我们之前的安装操作(Step4)有一些不同。在这个例子中,我们会构建支持二进制安装和包管理的安装程序。为此,我们使用CPack来创建平台安装程序。具体来说,需要在顶层CMakeLists.txt中添加几行。
include(InstallRequiredSystemLibraries)
set(CPACK_RESOURCE_FILE_LICENSE \
"${CMAKE_CURRENT_SOURCE_DIR}/License.txt")
set(CPACK_PACKAGE_VERSION_MAJOR "${Tutorial_VERSION_MAJOR}")
set(CPACK_PACKAGE_VERSION_MINOR "${Tutorial_VERSION_MINOR}")
include(CPack)
我们首先include(InstallRequiredSystemLibraries),这个模块包含项目所需的运行库。 然后设置CPack变量,用于存储许可证和版本信息。版本信息就是之前我们设置的。最后include(CPack) 模块,它会利用这些变量和一些系统设置来搭建安装包。
正常Cmake make之后,用cpack命令构建二进制分发。命令输出:
root@x:~/qinrui/cmake/step7/build# cpack
CPack: Create package using STGZ
CPack: Install projects
CPack: - Run preinstall target for: Tutorial
CPack: - Install project: Tutorial
CPack: Create package
CPack: - package: /root/qinrui/cmake/step7/build/ \
Tutorial-1.0.1-Linux.sh generated.
CPack: Create package using TGZ
CPack: Install projects
CPack: - Run preinstall target for: Tutorial
CPack: - Install project: Tutorial
CPack: Create package
CPack: - package: /root/qinrui/cmake/step7/build/ \
Tutorial-1.0.1-Linux.tar.gz generated.
CPack: Create package using TZ
CPack: Install projects
CPack: - Run preinstall target for: Tutorial
CPack: - Install project: Tutorial
CPack: Create package
CPack: - package: /root/qinrui/cmake/step7/build/ \
Tutorial-1.0.1-Linux.tar.Z generated.
生成的压缩文件中,有三个目录,/bin,/include, /lib 分别对应可执行文件、包含文件、库文件
Step8 添加仪表盘
添加对将测试结果提交到仪表板的支持非常简单。我们 在之前的步骤中已经为我们的项目定义了许多测试 教程。我们只需运行这些测试并将其提交到仪表板。为了include仪表盘我们在顶层CMakeLists.txt include CTest模块
Replace:
# enable testing
enable_testing()
With:
# enable dashboard scripting
include(CTest)
Ctest模块会自动调用enable_testing,所以可以删除。
我们还需要创建一个CTestConfig.cmake文件,我们可以在其中指定项目名称以及需要提交到仪表盘的部分。