☰
CMake UseSWIG 模块完全指南:用 swig_add_library 将 C/C++ 封装为 Python、Java、C 等语言扩展
2026/10/9 12:12:14 网站建设 项目流程
  • 构建工具
  • 开发工具
  • CLI

【免费下载链接】CMake

Mirror of CMake upstream repository

项目地址:https://gitcode.com/gh_mirrors/cm/CMake
点击查看免费下载

UseSWIG是 CMake 官方模块,用于在构建系统中集成 SWIG 中的底层实现原理(自定义命令生成、支持文件管理、依赖跟踪与各语言目标命名规则),能够直接在真实项目中编写可复制的 SWIG 绑定构建配置。

前置条件:先 FindSWIG 再 include(UseSWIG)

UseSWIG模块本身不负责查找 SWIG 可执行文件,官方文档明确说明“假设 FindSWIG 模块已被加载”。因此标准用法是:

find_package(SWIG COMPONENTS python) # 先定位 SWIG 与目标语言支持 if(SWIG_FOUND) include(UseSWIG) swig_add_library(mymod LANGUAGE python SOURCES mymod.i) endif()

find_package(SWIG ...)由 FindSWIG 提供,它会解析swig -version与swig -swiglib输出,产出以下结果变量:

  • SWIG_FOUND:是否找到(指定版本的)SWIG 及所请求的语言组件;
  • SWIG_VERSION:SWIG 版本号;
  • SWIG_<lang>_FOUND:使用COMPONENTS/OPTIONAL_COMPONENTS时,每个小写目标语言是否可用;
  • SWIG_DIR:SWIG 安装的Lib目录路径(swig -swiglib的结果),实现中会以SWIG_LIB=${SWIG_DIR}环境变量传给 swig 调用;
  • SWIG_EXECUTABLE(缓存变量):SWIG 可执行文件路径,可手工指定。

COMPONENTS中的语言名必须是小写,且与swig_add_library的LANGUAGE参数一致(如python、perl5)。也支持版本范围与可选组件:

find_package(SWIG 4.0 COMPONENTS python OPTIONAL_COMPONENTS fortran)

swig_add_library:模块的核心命令

该命令自 CMake 3.8 引入,用于“以给定名称和指定语言定义 swig 模块”,完整语法为:

swig_add_library(<name> [TYPE <SHARED|MODULE|STATIC|USE_BUILD_SHARED_LIBS>] LANGUAGE <language> [NO_PROXY] [DEBUG_POSTFIX <postfix>] [OUTPUT_DIR <directory>] [OUTFILE_DIR <directory>] SOURCES <file>... )

由swig_add_library创建的目标拥有与 add_library 目标相同的能力,可直接用于任何接受目标的命令(例如target_link_libraries)。

TYPE:库类型

SHARED、MODULE、STATIC语义与add_library完全一致;指定USE_BUILD_SHARED_LIBS时,根据当前 BUILD_SHARED_LIBS 变量值为ON或OFF决定生成SHARED或STATIC库;未指定时默认MODULE(动态加载模块)。实现中(Modules/UseSWIG.cmake),当类型为MODULE时还会自动设置NO_SONAME ON,避免模块携带 soname。

LANGUAGE:目标语言

指定 SWIG 的封装目标语言,版本演进如下:

  • CMake 3.1 起支持 Go 和 Lua;
  • CMake 3.2 起支持 R;
  • CMake 3.18 起支持 Fortran。

模块内部会通过SWIG_MODULE_INITIALIZE宏将语言名转大写与小写,分别用于生成文件和 swig 的-<lang>命令行旗标。语言名无法识别时会直接FATAL_ERROR(SWIG Error: Language "..." not found)。

NO_PROXY:跳过代理层

CMake 3.12 引入,对应 swig 的-noproxy选项,阻止生成目标语言的代理(proxy)包装层。例如 Python 模式下,正常会生成一个mymod.py代理文件(内含import _mymod),而NO_PROXY会跳过该层。实现中(Modules/UseSWIG.cmake)会将该标记并入SWIG_MODULE_<name>_EXTRA_FLAGS,且若CMAKE_SWIG_FLAGS已含-noproxy则不重复追加。

DEBUG_POSTFIX:调试配置后缀

CMake 4.2 引入,用于管理目标的 DEBUG_POSTFIX 属性,目前仅对python语言有意义。若全局属性 DEBUG_CONFIGURATIONS 已定义,则为每个调试配置定义<CONFIG>_POSTFIX目标属性;未定义时默认使用DEBUG(实现见 Modules/UseSWIG.cmake 与 L1005-L1009)。

OUTPUT_DIR 与 OUTFILE_DIR:输出目录控制

两者均为 CMake 3.12 引入,分别对应 swig 的-outdir(语言特定文件输出目录)与-o(生成的 C/C++ 源文件输出目录)。

OUTPUT_DIR的取值优先级为:

  1. 命令的OUTPUT_DIR选项;
  2. CMAKE_SWIG_OUTDIR变量;
  3. 未指定时取决于UseSWIG_MODULE_VERSION:值为 1 或未定义时输出到 CMAKE_CURRENT_BINARY_DIR;值为 2 时使用专有目录,其路径可通过只读目标属性SWIG_SUPPORT_FILES_DIRECTORY获取。

OUTFILE_DIR的取值优先级为:命令选项OUTFILE_DIR→ 变量SWIG_OUTFILE_DIR→ 否则回退到OUTPUT_DIR或CMAKE_SWIG_OUTDIR。

重要提示:当UseSWIG_MODULE_VERSION为 2 时,强烈建议为每个目标使用独立的专有输出目录。因为目标构建时输出目录内容会被整体清空(见下文支持文件管理),共用一个目录会在不同目标间互相干扰、甚至误删文件。

SOURCES:接口文件识别

SOURCES中扩展名为.i的文件会被识别为 SWIG 工具输入,其余文件按常规方式编译链接。该默认行为可通过变量SWIG_SOURCE_FILE_EXTENSIONS(CMake 3.14 起)覆盖。实现中(Modules/UseSWIG.cmake)会把扩展名列表拼成正则过滤出 SWIG 输入,若一个都没有则报错SWIG_ADD_LIBRARY: no SWIG interface files specified。

版本行为变化与注意事项

  • CMake 3.13:当策略 CMP0078 为NEW时,命令创建名为<name>的标准目标;旧行为则使用不同目标名,并存入SWIG_MODULE_<name>_REAL_NAME变量。
  • CMake 3.15:替代库名(例如通过 OUTPUT_NAME 属性设置)会传递给 Python 和 CSharp 包装库。
  • CMake 3.21:当策略 CMP0122 为NEW时,CSharp 生成的库使用标准命名约定,否则沿用旧行为。
  • 多配置生成器:模块不支持 SWIG 生成的“随配置变化”的文件,所有构建配置必须产生相同的生成源文件。
  • Makefile 生成器:若某些源文件的USE_SWIG_DEPENDENCIES属性为FALSE,模块不追踪文件依赖,需要依赖<name>_swig_compilation自定义目标来确保 SWIG 生成文件已存在;其他生成器可直接依赖 SWIG 生成的源文件。

源文件属性:在调用 swig_add_library 之前设置

SWIG 输入文件(.i)上的源文件属性必须在调用swig_add_library之前设置,以便生成文件正确继承所需设置:

属性引入版本说明
CPLUSPLUS—以 C++ 模式调用 SWIG(-c++),生成.cxx而非.c
SWIG_FLAGS3.12 弃用向 SWIG 可执行文件传递自定义旗标(已被下列细粒度属性取代)
INCLUDE_DIRECTORIES/COMPILE_DEFINITIONS/COMPILE_OPTIONS3.12追加 SWIG 编译旗标,语义同 INCLUDE_DIRECTORIES 等属性
USE_TARGET_INCLUDE_DIRECTORIES3.13为TRUE时把目标 INCLUDE_DIRECTORIES 转发给 SWIG;FALSE时忽略;未设置时参考目标属性SWIG_USE_TARGET_INCLUDE_DIRECTORIES
GENERATED_INCLUDE_DIRECTORIES/GENERATED_COMPILE_DEFINITIONS/GENERATED_COMPILE_OPTIONS3.12作用于生成的 C/C++ 文件,填充生成文件的INCLUDE_DIRECTORIES、COMPILE_DEFINITIONS、COMPILE_OPTIONS属性
DEPENDS3.12为源文件指定额外依赖
USE_SWIG_DEPENDENCIES3.20为TRUE时由 swig 工具自身生成隐式依赖,仅对 Makefile、Ninja、Xcode(3.21 起)和 Visual Studio(3.22 起)生成器有意义,默认FALSE
SWIG_MODULE_NAME—指定目标语言中的实际模块导入名,当无法从源码自动扫描或与文件基名不同时必须设置
OUTPUT_DIR3.19为该源文件指定语言特定文件的输出目录(-outdir)
OUTFILE_DIR3.19为该源文件指定生成源文件的输出目录(-o)

典型示例:

set_property(SOURCE mymod.i PROPERTY CPLUSPLUS ON) set_property(SOURCE mymod.i PROPERTY SWIG_MODULE_NAME mymod_realname) swig_add_library(mymod LANGUAGE python SOURCES mymod.i)

关于SWIG_MODULE_NAME:从 CMake 3.14 起,若策略 CMP0086 为NEW,会向 SWIG 编译器传递-module <module_name>;否则模块名依赖 SWIG 自行从%module指令扫描。模块名自动扫描逻辑见 Modules/UseSWIG.cmake:先读取源属性SWIG_MODULE_NAME,其次用正则匹配.i文件中的%module foo语法,再次匹配%module (options=...) foo语法,最后回退到文件基名。

目标属性:对整个 SWIG 模块统一配置

目标级属性可对模块内所有 SWIG 输入文件统一生效:

属性引入版本说明
SWIG_INCLUDE_DIRECTORIES/SWIG_COMPILE_DEFINITIONS/SWIG_COMPILE_OPTIONS3.12作用于所有 SWIG 输入文件,语义同目标属性 INCLUDE_DIRECTORIES、COMPILE_DEFINITIONS、COMPILE_OPTIONS
SWIG_USE_TARGET_INCLUDE_DIRECTORIES3.13为TRUE时转发目标INCLUDE_DIRECTORIES给 SWIG;FALSE或未定义时忽略;可被源属性USE_TARGET_INCLUDE_DIRECTORIES覆盖
SWIG_GENERATED_INCLUDE_DIRECTORIES/SWIG_GENERATED_COMPILE_DEFINITIONS/SWIG_GENERATED_COMPILE_OPTIONS3.12填充所有生成的 C/C++ 文件的INCLUDE_DIRECTORIES、COMPILE_DEFINITIONS、COMPILE_FLAGS属性
SWIG_DEPENDS3.12为所有 SWIG 输入文件增加依赖

官方示例:

set(UseSWIG_TARGET_NAME_PREFERENCE STANDARD) swig_add_library(mymod LANGUAGE python SOURCES mymod.i) set_property(TARGET mymod PROPERTY SWIG_COMPILE_DEFINITIONS MY_DEF1 MY_DEF2) set_property(TARGET mymod PROPERTY SWIG_COMPILE_OPTIONS -bla -blb)

只读目标属性:获取支持文件信息

以下两个是输出型(只读)属性,用于查询 SWIG 接口编译产生的支持文件信息:

  • SWIG_SUPPORT_FILES(3.12 起):SWIG 编译期间生成的包装文件列表。例如:

    set(UseSWIG_TARGET_NAME_PREFERENCE STANDARD) swig_add_library(mymod LANGUAGE python SOURCES mymod.i) get_property(support_files TARGET mymod PROPERTY SWIG_SUPPORT_FILES)

    注意:只列出最主要的支持文件;若使用%template等 SWIG 高级特性,关联支持文件可能未列出,此时优先使用SWIG_SUPPORT_FILES_DIRECTORY属性处理支持文件。

  • SWIG_SUPPORT_FILES_DIRECTORY(3.12 起):支持文件生成目录。当源属性OUTPUT_DIR被定义时,该属性可能包含多个目录(实现见 Modules/UseSWIG.cmake,所有去重后的输出目录都会追加进该属性)。

支持文件清单的组装逻辑(Modules/UseSWIG.cmake)依据模块头部的语言扩展名表按后缀过滤生成源:例如 Python 为.py、Java 为.java与JNI.java、CSharp 为.cs与PINVOKE.cs、Perl/Perl5 为.pm(见 L424-L428)。

CMake 变量:全局定制 swig_add_library 与 SWIG

变量引入版本说明
UseSWIG_MODULE_VERSION3.12取 1 或未定义:应用旧行为;取 2:采用关于支持文件的新策略——SWIG 接口编译前会清空支持文件输出目录。取值非法时报错
CMAKE_SWIG_FLAGS—为所有 swig 调用追加旗标
CMAKE_SWIG_OUTDIR—指定语言特定文件输出目录(-outdir),优先级低于命令OUTPUT_DIR选项
SWIG_OUTFILE_DIR3.8指定生成源文件输出目录;未指定时使用CMAKE_SWIG_OUTDIR
SWIG_MODULE_<name>_EXTRA_DEPS—为<name>生成的模块指定额外依赖
SWIG_SOURCE_FILE_EXTENSIONS3.14覆盖默认仅将.i视为 SWIG 源的行为,例如set(SWIG_SOURCE_FILE_EXTENSIONS ".i" ".swg")
SWIG_USE_SWIG_DEPENDENCIES3.20为TRUE时由 swig 自身生成隐式依赖,仅对 Makefile、Ninja、Xcode(3.21 起)、Visual Studio(3.22 起)生成器有意义,默认FALSE;未定义的源文件属性USE_SWIG_DEPENDENCIES会以该变量值初始化

另外,模块还维护UseSWIG_TARGET_NAME_PREFERENCE变量(LEGACY或STANDARD)控制目标命名:STANDARD使用<name>原名,LEGACY下 Python 非NO_PROXY模块的目标名带_前缀(_<name>),因为生成的module.py含import _modulename语句,需要对应的_modulename.so(Unix)/_modulename.pyd(Windows)二进制(见 Modules/UseSWIG.cmake)。策略CMP0078为NEW时强制为STANDARD。

已废弃命令:swig_link_libraries

swig_link_libraries(<name> <item>...)自 CMake 3.13 起弃用,应改用带标准目标名的 target_link_libraries,或在旧目标命名下使用${SWIG_MODULE_<name>_REAL_NAME}。新命令与target_link_libraries能力相同:

  • 策略CMP0078为NEW时,swig_add_library创建标准目标<name>,应直接使用target_link_libraries;
  • 旧行为(CMP0078为OLD且UseSWIG_TARGET_NAME_PREFERENCE为"LEGACY",或 CMake 3.12 之前)下,推荐使用target_link_libraries(${SWIG_MODULE_<name>_REAL_NAME} ...)而非该命令。

实现中(Modules/UseSWIG.cmake)还兼容了SWIG_ADD_MODULE宏——调用它会打印弃用警告并转调swig_add_library。

源码级原理:SWIG 编译是如何接入构建系统的

自定义命令与生成文件命名

核心函数SWIG_ADD_SOURCE_TO_MODULE(Modules/UseSWIG.cmake)为每个.i文件生成一个add_custom_command:先创建输出目录,再以SWIG_LIB=${SWIG_DIR}环境变量调用${SWIG_EXECUTABLE},依次拼接-<lang>、源文件旗标(含-I、-D前缀转换的生成器表达式)、-outdir、-c++(CPLUSPLUS 时)、-module(CMP0086 NEW 时)、额外旗标、-o <生成文件>,最后是输入文件本身。

生成的主源文件名规则为:

<outfiledir>/<源文件基名><语言大写>_wrap.c # C 模式 <outfiledir>/<源文件基名><语言大写>_wrap.cxx # C++ 模式(SWIG_CXX_EXTENSION)

把语言名拼入文件名是为了让同一个.i文件可被封装为多种语言而互不冲突。生成文件与额外支持文件均被标记GENERATED 1,并写入ADDITIONAL_CLEAN_FILES。

输出目录与支持文件管理(UseSWIG_MODULE_VERSION 2)

当UseSWIG_MODULE_VERSION大于 1 时,每个源文件使用专有工作目录<workingdir>/<源文件基名>.files,并在自定义命令中插入两步脚本调用(Modules/UseSWIG.cmake):

  • ACTION=CLEAN:先清空输出目录中的旧生成文件,再删除工作目录,防止过时文件残留;
  • ACTION=COPY:编译完成后把工作目录中的新文件复制回输出目录。

这两步由 Modules/UseSWIG/ManageSupportFiles.cmake 实现(GLOB_RECURSE收集文件 →file(REMOVE)/file(COPY))。这正是文档强调“每个目标使用独立输出目录”的原因。

依赖跟踪的两种策略

  • 默认关闭:USE_SWIG_DEPENDENCIES为FALSE时,Makefile 生成器额外引入时间戳机制(__SWIG_COMPUTE_TIMESTAMP,见 Modules/UseSWIG.cmake):自定义命令输出为<name><LANGUAGE>.stamp时间戳文件,生成源作为BYPRODUCTS,并利用IMPLICIT_DEPENDS CXX做隐式依赖;同时创建<name>_swig_compilation自定义目标依赖这些时间戳(L954-L958),解决依赖文件被移除时的重建问题(对应上游 issue #16830)。
  • 开启时:USE_SWIG_DEPENDENCIES为TRUE(仅 Makefile/Ninja/Xcode/Visual Studio)时,swig 以-MD -MF <name>.d生成 depfile,通过自定义命令的DEPFILE机制接入 Ninja 等的依赖扫描。

各语言目标命名规则

SWIG_ADD_LIBRARY函数后半段(Modules/UseSWIG.cmake)针对不同语言调整目标 PREFIX/SUFFIX 属性,使产物名符合各语言运行时约定:

  • Octave:PREFIX ""、SUFFIX ".oct";
  • Go:PREFIX "";
  • Java:macOS 上SUFFIX ".jnilib",MINGW/CYGWIN/MSYS 上PREFIX ""(对应System.loadLibrary("LIBRARY")的查找规则);
  • Lua(MODULE 类型):PREFIX "";
  • Python:非 NO_PROXY 时PREFIX "_",Windows 上SUFFIX ".pyd"(自 Python 2.5 起扩展模块后缀),并应用DEBUG_POSTFIX;
  • R / Ruby / Perl / Perl5:PREFIX "",Ruby 与 Perl 在 macOS 上分别使用.bundle、.dylib后缀;
  • CSharp:策略 CMP0122 为NEW时保持默认前缀,否则PREFIX ""(macOS 上SUFFIX ".dylib");
  • Fortran:不覆盖库前缀。

CSharp 与 Python 的特殊旗标

  • CSharp 模式下自动补-dllimport $<TARGET_FILE_BASE_NAME:...>,确保生成代码里的DllImport名称与 CMake 创建的库名一致(Modules/UseSWIG.cmake);
  • Python 单输入文件且非 NO_PROXY 时自动补-interface旗标,使代理代码中的名称与库名匹配(L662-L669)。

测试与验证:RunCMake/UseSWIG

仓库在 Tests/RunCMake/UseSWIG 提供针对策略行为的回归测试,是理解模块行为边界的直接参考:

  • CMP0078-NEW/OLD/WARN.cmake与CMP0078-common.cmake:验证新旧目标命名行为。公共测试文件设置CMP0086 NEW、指定SWIG_EXECUTABLE与SWIG_DIR后include(UseSWIG)并调用swig_add_library(example LANGUAGE python TYPE MODULE SOURCES example.i),随后打印PREFIX与SWIG_MODULE_example_REAL_NAME,对照-stdout.txt/-stderr.txt期望输出;
  • CMP0086-NEW/OLD/WARN.cmake:验证SWIG_MODULE_NAME是否传递-module旗标;
  • CMP0122-NEW/OLD/WARN.cmake与-check.cmake:验证 CSharp 目标命名约定;
  • example.i(%module example)与runme.py:最小 Python 绑定示例;
  • SetPOSTFIX.cmake:验证调试后缀设置。

这些测试同时展示了最小可用配置形态:cmake_minimum_required+project(... C)+include(<测试>.cmake),再配合SWIG_EXECUTABLE变量即可在 CI 中模拟 SWIG 存在的情形。

最小实战模板

综合以上内容,一个完整的 Python 绑定项目只需:

cmake_minimum_required(VERSION 3.20) project(mypybind CXX) find_package(SWIG 4.0 COMPONENTS python REQUIRED) include(UseSWIG) set(UseSWIG_MODULE_VERSION 2) # 启用支持文件目录管理 # 源文件属性必须在 swig_add_library 之前设置 set_property(SOURCE mymod.i PROPERTY CPLUSPLUS ON) set_property(SOURCE mymod.i PROPERTY SWIG_MODULE_NAME mymod) swig_add_library(mymod TYPE MODULE LANGUAGE python SOURCES mymod.i ) target_link_libraries(mymod PRIVATE MyCppLib) target_include_directories(mymod PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}) # 查询生成的支持文件(只读属性) get_property(_swig_support TARGET mymod PROPERTY SWIG_SUPPORT_FILES) message(STATUS "SWIG support files: ${_swig_support}")

构建后即可在输出目录获得mymod.py(代理层)与_mymod.so/_mymod.pyd(二进制扩展),完成一次完整的“C/C++ → Python”自动封装流程。

适用前提与限制:以上行为以当前仓库(Modules/UseSWIG.cmake)为准;多配置生成器不支持随配置变化的生成文件,Makefile 生成器默认不跟踪 SWIG 隐式依赖(需依赖<name>_swig_compilation目标或显式开启USE_SWIG_DEPENDENCIES),使用UseSWIG_MODULE_VERSION 2时务必为每个目标配置独立输出目录。

  • 构建工具
  • 开发工具
  • CLI

【免费下载链接】CMake

Mirror of CMake upstream repository

项目地址:https://gitcode.com/gh_mirrors/cm/CMake
点击查看免费下载
上一篇:RedwoodRecord 实战指南:基于 Prisma 的 Redwood 原生 ORM 全解析
下一篇:Airbyte source-youtube-data 连接器工程剖析:增量策略、错误处理与配额治理实战

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询