cmake + 痛饮 + 依赖项
cmake + swig + dependicies
今天我尝试使用 "cmake + swig" 的组合来为我的代码生成绑定。基本上它有效:
set(SWIG_EXECUTABLE "/usr/bin/swig")
find_package(SWIG REQUIRED)
include(${CMAKE_CURRENT_SOURCE_DIR}/UseSWIG.cmake)
set(CMAKE_SWIG_FLAGS -package example)
set(CMAKE_SWIG_OUTDIR "${CMAKE_CURRENT_SOURCE_DIR}/example")
set_source_files_properties(native.i PROPERTIES CPLUSPLUS ON)
SWIG_ADD_MODULE(core Java native.i lib.cpp)
include_directories(${CMAKE_CURRENT_SOURCE_DIR})
这样 native.i:
%module native
%include "lib.hpp"
%{
#include "lib.hpp"
%}
但如果我更改 lib.hpp
构建系统 cmake build
则不会调用 swig 重新生成代码。所以我必须 touch native.i
才能让它工作,这很烦人。
我找到了 swig
的 -M
选项来生成依赖项,
知道如何使用它来修复 UseSWIG.cmake
吗?
UseSWIG.cmake
仅使用 add_custom_command
进行生成,
所以我需要以某种方式 add_custom_command
依赖于动态文件集,
不是静态的?
确实,在源代码中查找 UseSWIG.cmake, the custom commands are built on the .i files and miss dependency to other sources. What you can do is add extra dependencies 的自定义命令:
set(SWIG_MODULE_core_EXTRA_DEPS lib.hpp)
SWIG_ADD_MODULE(core Java native.i lib.cpp)
或者,您可以修改UseSWIG.cmake以自动添加依赖项:
macro(SWIG_ADD_SOURCE_TO_MODULE name outfiles infile other_sources)
# ...
add_custom_command(
# ...
DEPENDS ${SWIG_MODULE_${name}_EXTRA_DEPS} ${other_sources}
# ...
)
endmacro()
# ...
foreach(it ${swig_dot_i_sources})
SWIG_ADD_SOURCE_TO_MODULE(${name} swig_generated_source ${it} ${swig_other_sources})
set(swig_generated_sources ${swig_generated_sources} "${swig_generated_source}")
endforeach()
这引出了我最初的评论:
SWIG_ADD_MODULE(core Java native.i lib.cpp lib.hpp)
在配置过程中,您可以 运行 使用 -M 标志 swig 来生成依赖项。然后您可以解析它的输出并将其作为 DEPDENDS 传递给 add_custom_command.
输出如下:
test_wrap.c: \
.../swig.swg \
... \
test.i \
test.h
这可以用 execute_command 生成,需要进一步处理:
execute_process(COMMAND swig -M <SWIG_ARGUMENTs> OUTPUT_VARIABLES swig_deps)
# Match all lines except the first one until " \"
string(REGEX MATCHALL "\n [^ ]+" temp ${swig_deps})
set(swig_deps)
foreach(t ${temp})
string(STRIP "${t}" t)
set(swig_deps ${swig_deps} "${t}")
endforeach()
...
add_custom_command(... DEPENDS ${swig_deps})
这使得 swig 依赖于 .i 文件中包含的所有 header。如果其中一个 .i 或 header 文件以添加新依赖项的方式进行编辑,您需要重新配置以便 cmake 知道它。如果您添加 CMAKE_CONFIGURE_DEPENDS.
,这会自动发生
set_property(DIRECTORY ${CMAKE_SOURCE_DIR} APPEND PROPERTY CMAKE_CONFIGURE_DEPENDS ${swig_deps} test.i)
我相信我已经找到了解决您的请求的方法。
我目前正在使用一个解决方案,其中添加了所有相关依赖项,以便 SWIG
re-generates 接口,每当任何 headers 解析被修改时。
想法是制作一个自定义目标,除了删除生成的接口文件外,它还涉及一个虚拟文件。我已经在下面为一个名为 fnm
的项目提供了一个带有包装器 swig_fnm
.
的解决方案
# Method to make swig_fnm.i depend on input headers
execute_process(COMMAND swig -M -python -c++ -I${CMAKE_CURRENT_BINARY_DIR}/.. -I${CMAKE_CURRENT_SOURCE_DIR}/.. swig_fnm.i
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
OUTPUT_VARIABLE swig_deps
INPUT_FILE swig_fnm.i)
# Match all lines except the first one until " \"
string(REGEX MATCHALL "\n [^ ]+" temp ${swig_deps})
# Valid dependency extensions
set(valid_ext .h .hpp)
# Dependency list
set(swig_deps_actual)
foreach(t ${temp})
string(STRIP "${t}" t)
# Add to dependency list
if (EXISTS "${t}")
set(filter)
get_filename_component(filter "${t}" EXT)
if (";${valid_ext};" MATCHES ";${filter};")
set(swig_deps_actual ${swig_deps_actual} "${t}")
endif()
endif()
endforeach()
# This makes configure run again, but does not regenerate the SWIG interface.
set_property(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} APPEND PROPERTY CMAKE_CONFIGURE_DEPENDS ${swig_deps_actual})
# All headers except for the single .i file are ignored
swig_add_module(swig_fnm python swig_fnm.i ${swig_fnm_HEADERS} ${swig_deps_actual})
# Removes generated file (if any of the dependent files are changed)
add_custom_command(
OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/swig.stamp
COMMAND ${CMAKE_COMMAND} -E remove ${swig_generated_file_fullname}
COMMAND ${CMAKE_COMMAND} -E touch ${CMAKE_CURRENT_BINARY_DIR}/swig.stamp
DEPENDS ${swig_deps_actual} # The dependent files
COMMENT "Removing old SWIG generated file" VERBATIM)
# Custom target for establishing dependency
add_custom_target(
swigtrick
DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/swig.stamp)
# Dependency
add_dependencies(_swig_fnm swigtrick)
今天我尝试使用 "cmake + swig" 的组合来为我的代码生成绑定。基本上它有效:
set(SWIG_EXECUTABLE "/usr/bin/swig")
find_package(SWIG REQUIRED)
include(${CMAKE_CURRENT_SOURCE_DIR}/UseSWIG.cmake)
set(CMAKE_SWIG_FLAGS -package example)
set(CMAKE_SWIG_OUTDIR "${CMAKE_CURRENT_SOURCE_DIR}/example")
set_source_files_properties(native.i PROPERTIES CPLUSPLUS ON)
SWIG_ADD_MODULE(core Java native.i lib.cpp)
include_directories(${CMAKE_CURRENT_SOURCE_DIR})
这样 native.i:
%module native
%include "lib.hpp"
%{
#include "lib.hpp"
%}
但如果我更改 lib.hpp
构建系统 cmake build
则不会调用 swig 重新生成代码。所以我必须 touch native.i
才能让它工作,这很烦人。
我找到了 swig
的 -M
选项来生成依赖项,
知道如何使用它来修复 UseSWIG.cmake
吗?
UseSWIG.cmake
仅使用 add_custom_command
进行生成,
所以我需要以某种方式 add_custom_command
依赖于动态文件集,
不是静态的?
确实,在源代码中查找 UseSWIG.cmake, the custom commands are built on the .i files and miss dependency to other sources. What you can do is add extra dependencies 的自定义命令:
set(SWIG_MODULE_core_EXTRA_DEPS lib.hpp)
SWIG_ADD_MODULE(core Java native.i lib.cpp)
或者,您可以修改UseSWIG.cmake以自动添加依赖项:
macro(SWIG_ADD_SOURCE_TO_MODULE name outfiles infile other_sources)
# ...
add_custom_command(
# ...
DEPENDS ${SWIG_MODULE_${name}_EXTRA_DEPS} ${other_sources}
# ...
)
endmacro()
# ...
foreach(it ${swig_dot_i_sources})
SWIG_ADD_SOURCE_TO_MODULE(${name} swig_generated_source ${it} ${swig_other_sources})
set(swig_generated_sources ${swig_generated_sources} "${swig_generated_source}")
endforeach()
这引出了我最初的评论:
SWIG_ADD_MODULE(core Java native.i lib.cpp lib.hpp)
在配置过程中,您可以 运行 使用 -M 标志 swig 来生成依赖项。然后您可以解析它的输出并将其作为 DEPDENDS 传递给 add_custom_command.
输出如下:
test_wrap.c: \
.../swig.swg \
... \
test.i \
test.h
这可以用 execute_command 生成,需要进一步处理:
execute_process(COMMAND swig -M <SWIG_ARGUMENTs> OUTPUT_VARIABLES swig_deps)
# Match all lines except the first one until " \"
string(REGEX MATCHALL "\n [^ ]+" temp ${swig_deps})
set(swig_deps)
foreach(t ${temp})
string(STRIP "${t}" t)
set(swig_deps ${swig_deps} "${t}")
endforeach()
...
add_custom_command(... DEPENDS ${swig_deps})
这使得 swig 依赖于 .i 文件中包含的所有 header。如果其中一个 .i 或 header 文件以添加新依赖项的方式进行编辑,您需要重新配置以便 cmake 知道它。如果您添加 CMAKE_CONFIGURE_DEPENDS.
,这会自动发生set_property(DIRECTORY ${CMAKE_SOURCE_DIR} APPEND PROPERTY CMAKE_CONFIGURE_DEPENDS ${swig_deps} test.i)
我相信我已经找到了解决您的请求的方法。
我目前正在使用一个解决方案,其中添加了所有相关依赖项,以便 SWIG
re-generates 接口,每当任何 headers 解析被修改时。
想法是制作一个自定义目标,除了删除生成的接口文件外,它还涉及一个虚拟文件。我已经在下面为一个名为 fnm
的项目提供了一个带有包装器 swig_fnm
.
# Method to make swig_fnm.i depend on input headers
execute_process(COMMAND swig -M -python -c++ -I${CMAKE_CURRENT_BINARY_DIR}/.. -I${CMAKE_CURRENT_SOURCE_DIR}/.. swig_fnm.i
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
OUTPUT_VARIABLE swig_deps
INPUT_FILE swig_fnm.i)
# Match all lines except the first one until " \"
string(REGEX MATCHALL "\n [^ ]+" temp ${swig_deps})
# Valid dependency extensions
set(valid_ext .h .hpp)
# Dependency list
set(swig_deps_actual)
foreach(t ${temp})
string(STRIP "${t}" t)
# Add to dependency list
if (EXISTS "${t}")
set(filter)
get_filename_component(filter "${t}" EXT)
if (";${valid_ext};" MATCHES ";${filter};")
set(swig_deps_actual ${swig_deps_actual} "${t}")
endif()
endif()
endforeach()
# This makes configure run again, but does not regenerate the SWIG interface.
set_property(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} APPEND PROPERTY CMAKE_CONFIGURE_DEPENDS ${swig_deps_actual})
# All headers except for the single .i file are ignored
swig_add_module(swig_fnm python swig_fnm.i ${swig_fnm_HEADERS} ${swig_deps_actual})
# Removes generated file (if any of the dependent files are changed)
add_custom_command(
OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/swig.stamp
COMMAND ${CMAKE_COMMAND} -E remove ${swig_generated_file_fullname}
COMMAND ${CMAKE_COMMAND} -E touch ${CMAKE_CURRENT_BINARY_DIR}/swig.stamp
DEPENDS ${swig_deps_actual} # The dependent files
COMMENT "Removing old SWIG generated file" VERBATIM)
# Custom target for establishing dependency
add_custom_target(
swigtrick
DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/swig.stamp)
# Dependency
add_dependencies(_swig_fnm swigtrick)