无需外部脚本,如何在CMake中为子项目指定不同工具链构建多版本产物
无需外部Shell脚本的CMake多工具链构建方案
当然有办法在CMake内部搞定这个需求!不过得先明确一个核心限制:CMake的单个构建树只能绑定一套工具链,所以没法直接像你设想的那样在同一个CMakeLists.txt里用add_subdirectory(client TOOLCHAIN=XXX)这种语法重复构建同一个子目录。不过我们有几个优雅的方案绕开这个限制,完全不需要外部脚本:
方案1:CMake Presets(推荐,现代CMake标准方式)
CMake 3.19+支持的Presets功能可以让你在一个配置文件里定义多套构建环境,用户可以按需选择构建哪一套,甚至批量构建。
步骤:
- 在项目根目录创建
CMakePresets.json文件,定义两个预设:
{ "version": 3, "configurePresets": [ { "name": "native", "displayName": "Native Build (Client + Server)", "description": "Build both client and server with default toolchain", "binaryDir": "${sourceDir}/build/native", "cacheVariables": { "BUILD_CLIENT": "ON", "BUILD_SERVER": "ON" } }, { "name": "emscripten-client", "displayName": "Emscripten Client Only", "description": "Build client with Emscripten toolchain", "binaryDir": "${sourceDir}/build/emscripten", "cacheVariables": { "BUILD_CLIENT": "ON", "BUILD_SERVER": "OFF" }, "toolchainFile": "${EMSCRIPTEN_ROOT_PATH}/cmake/Modules/Platform/Emscripten.cmake" } ] }
(这里用EMSCRIPTEN_ROOT_PATH环境变量自动获取工具链路径,前提是你已经正确配置了Emscripten的环境变量)
- 修改顶层CMakeLists.txt,添加开关变量来控制是否构建客户端/服务端:
option(BUILD_CLIENT "Build the client application" ON) option(BUILD_SERVER "Build the server application" ON) if(BUILD_SERVER) add_subdirectory(server) endif() if(BUILD_CLIENT) add_subdirectory(client) endif()
- 构建方式:
- 构建原生版本:
cmake --preset native && cmake --build --preset native - 构建Emscripten版本:
cmake --preset emscripten-client && cmake --build --preset emscripten-client - 一次性构建两个版本:
cmake --build --preset native --preset emscripten-client
这种方式的好处是完全符合CMake现代生态,配置集中清晰,用户可以灵活选择构建目标。
方案2:用ExternalProject_Add在构建时自动触发多工具链构建
如果你希望用户执行一次make(或ninja)就能同时得到两个版本的产物,可以用ExternalProject_Add来在原生构建过程中启动Emscripten的子构建。
修改顶层CMakeLists.txt:
# 先构建原生版本(客户端+服务端) add_subdirectory(server) add_subdirectory(client) # 再添加Emscripten客户端的外部构建 include(ExternalProject) ExternalProject_Add( emscripten_client SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR} BINARY_DIR ${CMAKE_CURRENT_BINARY_DIR}/emscripten_build CMAKE_ARGS -DCMAKE_TOOLCHAIN_FILE=${EMSCRIPTEN_ROOT_PATH}/cmake/Modules/Platform/Emscripten.cmake -DBUILD_CLIENT=ON -DBUILD_SERVER=OFF BUILD_ALWAYS OFF INSTALL_COMMAND "" # 不需要安装 )
这样当用户构建原生版本的all目标时,会自动触发Emscripten客户端的构建,产物会放在build/emscripten_build目录下。
方案3:自定义CMake目标触发子构建
另一种轻量方式是通过自定义目标调用CMake命令来启动Emscripten构建:
# 原生构建部分 add_subdirectory(server) add_subdirectory(client) # 自定义目标构建Emscripten客户端 add_custom_target( build_emscripten_client COMMAND ${CMAKE_COMMAND} -S . -B ${CMAKE_CURRENT_BINARY_DIR}/emscripten_build -DCMAKE_TOOLCHAIN_FILE=${EMSCRIPTEN_ROOT_PATH}/cmake/Modules/Platform/Emscripten.cmake -DBUILD_CLIENT=ON -DBUILD_SERVER=OFF COMMAND ${CMAKE_COMMAND} --build ${CMAKE_CURRENT_BINARY_DIR}/emscripten_build COMMENT "Building Emscripten client..." ) # 可选:让默认的all目标依赖这个自定义目标,这样构建all时会自动生成Emscripten版本 add_dependencies(all build_emscripten_client)
这种方式最接近你原来的shell脚本逻辑,但完全整合在CMake内部。
关键注意事项
- 你的
client/CMakeLists.txt里的if(EMSCRIPTEN)判断可以保留,在Emscripten工具链下会自动生效,不需要额外修改。 - 不同工具链的构建目录必须分开(比如原生用
build/,Emscripten用build/emscripten/),避免缓存冲突。 - 确保Emscripten的环境变量
EMSCRIPTEN_ROOT_PATH已经正确设置,这样CMake才能找到对应的工具链文件。
内容的提问来源于stack exchange,提问作者mystackoverflowpseudo
相关产品推荐
相关产品推荐

