使用CMake结合SWIG生成的源码构建C#库时,如何正确处理构建阶段生成的文件依赖
我太懂你这个头疼的问题了——用SWIG给C++项目生成C#绑定,本来想靠CMake的UseSWIG模块顺顺当当构建出C#库,结果新鲜构建时直接翻车:GLOB是在配置阶段跑的,这时候SWIG还没生成半毛钱.cs文件,拿空文件列表去建库肯定失败;官方给的SWIG_SUPPORT_FILES属性又漏了一大堆生成的.cs文件,完全没法用;你不想提前在配置阶段就跑SWIG生成文件,也不想硬编码未来会生成的文件列表,折腾CMAKE_CONFIGURE_DEPENDS也没达到预期效果,属实卡壳了对吧?
我给你两个靠谱的解决方案,都不用碰你想避开的那些操作,完美适配你的需求:
方案一:用带CONFIGURE_DEPENDS的GLOB + 目标依赖(简洁版)
这个方案利用CMake的CONFIGURE_DEPENDS让GLOB在构建阶段也能感知文件变化,同时确保SWIG先跑完生成文件再处理C#库:
# 第一步:定义SWIG生成目标,生成C#绑定文件 swig_add_library(net_wrapper TYPE SHARED LANGUAGE CSharp SOURCES ${NET_SWIG_SOURCES}) # 获取SWIG输出的C#文件所在目录 get_property(swig_support_files_folder TARGET net_wrapper PROPERTY SWIG_SUPPORT_FILES_DIRECTORY) # 用带CONFIGURE_DEPENDS的GLOB抓取文件——这个参数会让CMake在每次构建前检查目录,发现文件变化就自动重新配置 file(GLOB support_files CONFIGURE_DEPENDS "${swig_support_files_folder}/*.cs" ) # 定义C#库目标(哪怕配置阶段文件不存在也没关系,构建时会重新配置补全) add_library(net SHARED ${support_files}) # 关键:让C#库目标依赖于SWIG目标,确保先生成文件再构建库 add_dependencies(net net_wrapper) # 额外:监测SWIG的输入文件(比如你的.i接口文件),如果这些文件修改,自动触发重新配置 set_property(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} APPEND PROPERTY CMAKE_CONFIGURE_DEPENDS ${NET_SWIG_SOURCES})
为什么这个能行?CONFIGURE_DEPENDS会强制CMake在每次构建前扫描指定目录,一旦发现新增/删除了.cs文件,就自动重新运行配置流程,这时候GLOB就能抓到所有SWIG刚生成的文件了。加上add_dependencies保证了SWIG先完成生成,再处理C#库的构建。
方案二:自定义触发文件 + CMAKE_CONFIGURE_DEPENDS(兼容老版本VS)
如果你的Visual Studio版本比较老,对GLOB的CONFIGURE_DEPENDS支持不好,可以试试这个兼容性更强的方案:
# 第一步:定义SWIG生成目标 swig_add_library(net_wrapper TYPE SHARED LANGUAGE CSharp SOURCES ${NET_SWIG_SOURCES}) # 获取SWIG输出目录 get_property(swig_support_files_folder TARGET net_wrapper PROPERTY SWIG_SUPPORT_FILES_DIRECTORY) # 定义一个标记文件,用来通知CMake是否需要重新配置 set(swig_generated_marker ${CMAKE_CURRENT_BINARY_DIR}/.swig_files_generated) # 让SWIG完成生成后,更新这个标记文件的时间戳 add_custom_command( TARGET net_wrapper POST_BUILD COMMAND ${CMAKE_COMMAND} -E touch ${swig_generated_marker} COMMENT "标记SWIG C#文件已生成" ) # 让CMake监测这个标记文件,一旦它变化就重新运行配置 set_property(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} APPEND PROPERTY CMAKE_CONFIGURE_DEPENDS ${swig_generated_marker}) # 抓取生成的.cs文件 file(GLOB support_files "${swig_support_files_folder}/*.cs") # 定义C#库目标并设置依赖 add_library(net SHARED ${support_files}) add_dependencies(net net_wrapper) # 同样监测SWIG输入文件的变化 set_property(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} APPEND PROPERTY CMAKE_CONFIGURE_DEPENDS ${NET_SWIG_SOURCES})
这个方案的逻辑
每次SWIG成功生成所有.cs文件后,都会“触摸”一下标记文件,更新它的时间戳。CMake会时刻盯着这个标记文件,一旦发现它的时间变了,就知道SWIG刚生成了新文件,会自动重新运行配置,这时候GLOB就能抓到所有最新的.cs文件了。
为啥你之前的CMAKE_CONFIGURE_DEPENDS没生效?
你之前是把SWIG_SUPPORT_FILES加到全局的CMAKE_CONFIGURE_DEPENDS里,但这些文件在配置阶段还根本不存在,CMake没法监测一个还没出生的文件的变化。上面的两个方案都是监测确实存在的文件(比如你的SWIG输入.i文件,或者构建阶段生成的标记文件),这样CMake才能正确触发重新配置的逻辑。
这样不管是新鲜构建还是后续修改SWIG接口文件,整个流程都会自动跑通:配置→构建前触发SWIG生成→重新配置抓取文件→完成C#库构建,完全不用你手动干预。
备注:内容来源于stack exchange,提问作者T. Kiley

