如何通过CMake在构建时设置全局可用的DISTCC_HOSTS环境变量?
你的核心问题是:构建时在子shell中设置的环境变量无法传递给后续编译进程。下面是几种可行的解决方案,覆盖CMake配置时动态获取和构建时实时获取两种场景:
方案一:CMake配置时注入环境变量到编译器启动器
如果可以接受每次CMake配置时获取一次主机列表(适合主机列表不会频繁变化的场景),可以直接把DISTCC_HOSTS拼接到CMAKE_CXX_COMPILER_LAUNCHER中:
# 执行命令获取可用distcc主机列表 execute_process( COMMAND sh -c "lsdistcc $LSDISTCC_HOSTS | sed 's/.*/&\\/8,lzo /g' | tr -d '\n'" OUTPUT_VARIABLE DISTCC_HOSTS_VALUE OUTPUT_STRIP_TRAILING_WHITESPACE RESULT_VARIABLE LSDISTCC_EXIT_CODE ) # 检查lsdistcc是否执行成功,避免空值导致构建失败 if(NOT LSDISTCC_EXIT_CODE EQUAL 0) message(WARNING "lsdistcc failed to retrieve hosts, falling back to local compilation") set(CMAKE_CXX_COMPILER_LAUNCHER "") else() # 将环境变量直接附加到distcc命令前 set(CMAKE_CXX_COMPILER_LAUNCHER "env DISTCC_HOSTS=${DISTCC_HOSTS_VALUE} distcc") endif()
这种方式会在CMake生成构建文件时就把DISTCC_HOSTS固定下来,所有后续编译命令都会使用这个变量。
方案二:构建时实时获取主机的Wrapper脚本(推荐)
如果需要每次编译都实时获取最新的可用主机列表,最可靠的方式是写一个Shell脚本作为distcc的Wrapper,每次调用时先获取主机列表再执行distcc:
- 在项目根目录创建
distcc_wrapper.sh.in模板文件:
#!/bin/bash # 实时获取当前可用的distcc主机 DISTCC_HOSTS=$(lsdistcc $LSDISTCC_HOSTS | sed 's/.*/&\/8,lzo /g' | tr -d '\n') # 传递所有参数给distcc env DISTCC_HOSTS="${DISTCC_HOSTS}" distcc "$@"
- 在CMakeLists.txt中生成可执行的Wrapper脚本,并设置为编译器启动器:
# 生成Wrapper脚本 configure_file( ${CMAKE_SOURCE_DIR}/distcc_wrapper.sh.in ${CMAKE_BINARY_DIR}/distcc_wrapper.sh @ONLY ) # 给脚本添加执行权限 file(CHMOD ${CMAKE_BINARY_DIR}/distcc_wrapper.sh PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE ) # 设置C++编译器启动器为这个Wrapper set(CMAKE_CXX_COMPILER_LAUNCHER ${CMAKE_BINARY_DIR}/distcc_wrapper.sh)
这种方式下,每次调用distcc编译代码时,都会先执行lsdistcc获取最新主机列表,完全避免了环境变量传递的问题,同时兼容IDE和命令行构建。
方案三:针对Makefile生成器的全局环境变量传递
如果你的项目只使用Makefile作为构建文件,可以通过修改CMAKE_MAKE_PROGRAM来传递全局环境变量:
execute_process( COMMAND sh -c "lsdistcc $LSDISTCC_HOSTS | sed 's/.*/&\\/8,lzo /g' | tr -d '\n'" OUTPUT_VARIABLE DISTCC_HOSTS_VALUE OUTPUT_STRIP_TRAILING_WHITESPACE ) # 让make命令在全局环境中带上DISTCC_HOSTS set(CMAKE_MAKE_PROGRAM "env DISTCC_HOSTS=${DISTCC_HOSTS_VALUE} ${CMAKE_MAKE_PROGRAM}")
注意:这种方式同样是在CMake配置时获取一次主机列表,如果构建过程中主机状态变化,需要重新运行cmake ..更新变量。
为什么你之前的方法无效?
你通过add_custom_target执行的CMake脚本是在构建时的独立子shell中运行的,脚本中设置的ENV{DISTCC_HOSTS}仅对该子shell有效,后续编译命令会在新的子shell中执行,无法继承这个变量。CMake本身没有提供修改父进程环境变量的能力,所以必须通过上述方法将环境变量直接传递给编译命令。
内容的提问来源于stack exchange,提问作者Steve Lorimer

