cmake + swig + dependicies

I believe that I have found a solution to your request.

I am currently using a solution, where all relevant dependencies are added such that SWIG re-generates the interface, whenever any of the headers parsed are modified.

The idea is to make a custom target, which touch a dummy file in addition to deleting the generated interface file. I have put the solution below for a project named fnm with a wrapper 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)

During configuration you can run swig with the -M flag to generate the dependencies. You can then parse the output of that and pass it as DEPDENDS to add_custom_command.

The output looks like:

test_wrap.c: \
  .../swig.swg \
  ... \
  test.i \
  test.h

This can be generated with execute_command and needs further processing:

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})

This makes swig depend on all headers that are included in the .i files. If one of the .i or the header files is edited in a way that adds new dependencies you need to reconfigure so cmake knows about it. This can happen automatically if you add CMAKE_CONFIGURE_DEPENDS.

set_property(DIRECTORY ${CMAKE_SOURCE_DIR} APPEND PROPERTY CMAKE_CONFIGURE_DEPENDS ${swig_deps} test.i)

Tags:

C++

Swig

Cmake