如何用CMake为含多个main函数的C++源码生成独立可执行文件?
为多main函数的测试代码生成独立可执行文件的CMake方案
问题背景
我找到josuttis的jthread项目源码,直接执行make命令编译失败,报错如下:
Makefile:1: Makefile.h: No such file or directory
make: *** No rule to make target `Makefile.h'. Stop.
因不清楚Makefile.h相关情况,我编写了如下CMake文件:
cmake_minimum_required (VERSION 3.6.2) project (JtTest) set(CMAKE_INCLUDE_CURRENT_DIR ON) if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC") set(CMAKE_CXX_STANDARD 23) else() set(CMAKE_CXX_STANDARD 20) endif() if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang") # using Clang elseif ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU") # using GCC add_definitions("-Wall -Wextra -Wno-unused-function -pedantic -pthread") elseif ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Intel") # using Intel C++ elseif ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC") # using Visual Studio C++ # add_compile_options("/std:c++latest") add_compile_options("/W4" "/Zc:__cplusplus") add_definitions(-MP -D_UNICODE) endif() find_package(Threads) file(GLOB_RECURSE CPP_FILES ${CMAKE_SOURCE_DIR}/*.h ${CMAKE_SOURCE_DIR}/*.cpp) add_executable(${PROJECT_NAME} ${CPP_FILES}) target_link_libraries (${PROJECT_NAME} ${CMAKE_THREAD_LIBS_INIT})
所有源码编译成功但链接失败,原因是CMAKE_SOURCE_DIR目录下存在多个包含main函数的cpp文件:
test_cv.cpp test_cvcb.cpp test_cvprodcons.cpp test_cvrace.cpp test_cvrace_hh.cpp test_cvrace_pred.cpp test_cvrace_stop.cpp test_jthread1.cpp test_jthread2.cpp test_stoken.cpp test_stokencb.cpp test_stokenrace.cpp test_stopcb.cpp
我希望为每个cpp文件生成独立的可执行文件,请问用CMake正确的编译方式是什么?
编辑1:
若将CMake中的*.cpp改为单个文件(如test_cv.cpp)可成功生成可执行文件:
file(GLOB_RECURSE CPP_FILES ${CMAKE_SOURCE_DIR}/*.h ${CMAKE_SOURCE_DIR}/test_cv.cpp)
但使用*.cpp时会出现如下链接错误:
duplicate symbol '_main' in: CMakeFiles/JtTest.dir/test_cv.cpp.o CMakeFiles/JtTest.dir/test_cvrace_pred.cpp.o duplicate symbol '_main' in: CMakeFiles/JtTest.dir/test_cv.cpp.o CMakeFiles/JtTest.dir/test_cvcb.cpp.o duplicate symbol '_main' in: CMakeFiles/JtTest.dir/test_cv.cpp.o CMakeFiles/JtTest.dir/test_cvprodcons.cpp.o duplicate symbol '_main' in: CMakeFiles/JtTest.dir/test_cv.cpp.o CMakeFiles/JtTest.dir/test_cvrace.cpp.o duplicate symbol '_main' in: CMakeFiles/JtTest.dir/test_cv.cpp.o CMakeFiles/JtTest.dir/test_cvrace_hh.cpp.o duplicate symbol 'testCVAnyMutex()' in: CMakeFiles/JtTest.dir/test_cvrace_hh.cpp.o CMakeFiles/JtTest.dir/test_cvrace_stop.cpp.o duplicate symbol '_cv' in: CMakeFiles/JtTest.dir/test_cvrace_hh.cpp.o CMakeFiles/JtTest.dir/test_cvrace_stop.cpp.o duplicate symbol '_main' in: CMakeFiles/JtTest.dir/test_cv.cpp.o CMakeFiles/JtTest.dir/test_cvrace_stop.cpp.o duplicate symbol '_m' in: CMakeFiles/JtTest.dir/test_cvrace_hh.cpp.o CMakeFiles/JtTest.dir/test_cvrace_stop.cpp.o duplicate symbol '_main' in: CMakeFiles/JtTest.dir/test_cv.cpp.o CMakeFiles/JtTest.dir/test_jthread1.cpp.o duplicate symbol '_main' in: CMakeFiles/JtTest.dir/test_cv.cpp.o CMakeFiles/JtTest.dir/test_jthread2.cpp.o duplicate symbol '_main' in: CMakeFiles/JtTest.dir/test_cv.cpp.o CMakeFiles/JtTest.dir/test_stoken.cpp.o duplicate symbol '_main' in: CMakeFiles/JtTest.dir/test_cv.cpp.o CMakeFiles/JtTest.dir/test_stokencb.cpp.o duplicate symbol '_main' in: CMakeFiles/JtTest.dir/test_cv.cpp.o CMakeFiles/JtTest.dir/test_stokenrace.cpp.o duplicate symbol '_main' in: CMakeFiles/JtTest.dir/test_cv.cpp.o CMakeFiles/JtTest.dir/test_stopcb.cpp.o ld: 16 duplicate symbols for architecture x86_64 clang: error: linker command failed with exit code 1 (use -v to see invocation) make[2]: *** [JtTest] Error 1 make[1]: *** [CMakeFiles/JtTest.dir/all] Error 2 make: *** [all] Error 2
解决方案
核心思路是遍历每个测试cpp文件,为每个文件单独创建可执行目标,避免多个main函数冲突。修改后的CMakeLists.txt如下:
cmake_minimum_required (VERSION 3.6.2) project (JtTest) set(CMAKE_INCLUDE_CURRENT_DIR ON) # 设置C++标准,确保严格遵循 if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC") set(CMAKE_CXX_STANDARD 23) else() set(CMAKE_CXX_STANDARD 20) endif() set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_CXX_EXTENSIONS OFF) # 编译器特定编译选项 if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU") add_compile_options(-Wall -Wextra -Wno-unused-function -pedantic -pthread) elseif ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC") add_compile_options(/W4 /Zc:__cplusplus) add_definitions(-MP -D_UNICODE) endif() find_package(Threads REQUIRED) # 精准匹配所有测试cpp文件 file(GLOB TEST_CPP_FILES ${CMAKE_SOURCE_DIR}/test_*.cpp) # 遍历每个测试文件,生成独立可执行 foreach(TEST_FILE ${TEST_CPP_FILES}) # 提取文件名(不含路径和后缀)作为可执行名称 get_filename_component(TEST_NAME ${TEST_FILE} NAME_WE) add_executable(${TEST_NAME} ${TEST_FILE}) # 链接线程库 target_link_libraries(${TEST_NAME} PRIVATE Threads::Threads) endforeach()
关键修改说明
- 用
test_*.cpp精准匹配测试文件,避免意外包含其他无关cpp文件 - 通过
foreach循环为每个测试文件单独创建可执行目标,文件名直接作为可执行名(比如test_cv.cpp对应test_cv) - 优化标准设置:添加
CMAKE_CXX_STANDARD_REQUIRED ON和CMAKE_CXX_EXTENSIONS OFF,强制编译器使用指定的C++标准 - 改用
Threads::Threads链接线程库,是CMake 3.1+推荐的现代用法 - 调整编译器选项的设置方式,更符合CMake最佳实践
内容的提问来源于stack exchange,提问作者Alexey Starinsky
相关产品推荐
相关产品推荐

