You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

无需外部脚本,如何在CMake中为子项目指定不同工具链构建多版本产物

无需外部Shell脚本的CMake多工具链构建方案

当然有办法在CMake内部搞定这个需求!不过得先明确一个核心限制:CMake的单个构建树只能绑定一套工具链,所以没法直接像你设想的那样在同一个CMakeLists.txt里用add_subdirectory(client TOOLCHAIN=XXX)这种语法重复构建同一个子目录。不过我们有几个优雅的方案绕开这个限制,完全不需要外部脚本:

方案1:CMake Presets(推荐,现代CMake标准方式)

CMake 3.19+支持的Presets功能可以让你在一个配置文件里定义多套构建环境,用户可以按需选择构建哪一套,甚至批量构建。

步骤:

  1. 在项目根目录创建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的环境变量)

  1. 修改顶层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()
  1. 构建方式:
  • 构建原生版本: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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.28 15:42:38