Interrogate.cmake 9.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269
  1. # Filename: Interrogate.cmake
  2. #
  3. # Description: This file contains macros and functions that are used to invoke
  4. # interrogate, to generate wrappers for Python and/or other languages.
  5. #
  6. # Functions:
  7. # target_interrogate(target [ALL] [source1 [source2 ...]])
  8. # add_python_module(module [lib1 [lib2 ...]])
  9. #
  10. set(IGATE_FLAGS -DCPPPARSER -D__cplusplus -Dvolatile -Dmutable -python-native)
  11. # In addition, Interrogate needs to know if this is a 64-bit build:
  12. include(CheckTypeSize)
  13. check_type_size(long CMAKE_SIZEOF_LONG)
  14. if(CMAKE_SIZEOF_LONG EQUAL 8)
  15. list(APPEND IGATE_FLAGS "-D_LP64")
  16. endif()
  17. # This is a list of regexes that are applied to every filename. If one of the
  18. # regexes matches, that file will not be passed to Interrogate.
  19. set(INTERROGATE_EXCLUDE_REGEXES
  20. ".*\\.I$"
  21. ".*\\.N$"
  22. ".*\\.lxx$"
  23. ".*\\.yxx$"
  24. ".*_src\\..*")
  25. if(WIN32)
  26. list(APPEND IGATE_FLAGS -longlong __int64 -D_X86_ -D__STDC__=1 -DWIN32_VC -D "_declspec(param)=" -D "__declspec(param)=" -D_near -D_far -D__near -D__far -D_WIN32 -D__stdcall -DWIN32)
  27. endif()
  28. if(INTERROGATE_VERBOSE)
  29. list(APPEND IGATE_FLAGS "-v")
  30. endif()
  31. set(IMOD_FLAGS -python-native)
  32. #
  33. # Function: target_interrogate(target [ALL] [source1 [source2 ...]])
  34. # NB. This doesn't actually invoke interrogate, but merely adds the
  35. # sources to the list of scan sources associated with the target.
  36. # Interrogate will be invoked when add_python_module is called.
  37. # If ALL is specified, all of the sources from the associated
  38. # target are added.
  39. #
  40. function(target_interrogate target)
  41. if(HAVE_PYTHON AND HAVE_INTERROGATE)
  42. set(sources)
  43. set(want_all OFF)
  44. foreach(arg ${ARGN})
  45. if(arg STREQUAL "ALL")
  46. set(want_all ON)
  47. else()
  48. list(APPEND sources "${arg}")
  49. endif()
  50. endforeach()
  51. # If ALL was specified, pull in all sources from the target.
  52. if(want_all)
  53. get_target_property(target_sources "${target}" SOURCES)
  54. list(APPEND sources ${target_sources})
  55. endif()
  56. list(REMOVE_DUPLICATES sources)
  57. # Now let's get everything's absolute path, so that it can be passed
  58. # through a property while still preserving the reference.
  59. set(absolute_sources)
  60. foreach(source ${sources})
  61. get_source_file_property(location "${source}" LOCATION)
  62. set(absolute_sources ${absolute_sources} ${location})
  63. endforeach(source)
  64. set_target_properties("${target}" PROPERTIES IGATE_SOURCES
  65. "${absolute_sources}")
  66. # CMake has no property for determining the source directory where the
  67. # target was originally added. interrogate_sources makes use of this
  68. # property (if it is set) in order to make all paths on the command-line
  69. # relative to it, thereby shortening the command-line even more.
  70. # Since this is not an Interrogate-specific property, it is not named with
  71. # an IGATE_ prefix.
  72. set_target_properties("${target}" PROPERTIES TARGET_SRCDIR
  73. "${CMAKE_CURRENT_SOURCE_DIR}")
  74. # HACK HACK HACK -- this is part of the below hack.
  75. target_link_libraries(${target} ${target}_igate)
  76. endif()
  77. endfunction(target_interrogate)
  78. #
  79. # Function: interrogate_sources(target output database module)
  80. #
  81. # This function actually runs a component-level interrogation against 'target'.
  82. # It generates the outfile.cxx (output) and dbfile.in (database) files, which
  83. # can then be used during the interrogate_module step to produce language
  84. # bindings.
  85. #
  86. # The target must first have had sources selected with target_interrogate.
  87. # Failure to do so will result in an error.
  88. #
  89. function(interrogate_sources target output database module)
  90. if(HAVE_PYTHON AND HAVE_INTERROGATE)
  91. get_target_property(sources "${target}" IGATE_SOURCES)
  92. if(NOT sources)
  93. message(FATAL_ERROR
  94. "Cannot interrogate ${target} unless it's run through target_interrogate first!")
  95. endif()
  96. get_target_property(srcdir "${target}" TARGET_SRCDIR)
  97. if(NOT srcdir)
  98. # No TARGET_SRCDIR was set, so we'll do everything relative to our
  99. # current binary dir instead:
  100. set(srcdir "${CMAKE_CURRENT_BINARY_DIR}")
  101. endif()
  102. set(scan_sources)
  103. set(nfiles)
  104. foreach(source ${sources})
  105. get_filename_component(source_basename "${source}" NAME)
  106. # Only certain sources should actually be scanned by Interrogate. The
  107. # rest are merely dependencies. This uses the exclusion regex above in
  108. # order to determine what files are okay:
  109. set(exclude OFF)
  110. foreach(regex ${INTERROGATE_EXCLUDE_REGEXES})
  111. if("${source_basename}" MATCHES "${regex}")
  112. set(exclude ON)
  113. endif()
  114. endforeach(regex)
  115. get_source_file_property(source_excluded ${source} WRAP_EXCLUDE)
  116. if(source_excluded)
  117. set(exclude ON)
  118. endif()
  119. if(NOT exclude)
  120. # This file is to be scanned by Interrogate. In order to avoid
  121. # cluttering up the command line, we should first make it relative:
  122. file(RELATIVE_PATH rel_source "${srcdir}" "${source}")
  123. list(APPEND scan_sources "${rel_source}")
  124. # Also see if this file has a .N counterpart, which has directives
  125. # specific for Interrogate. If there is a .N file, we add it as a dep,
  126. # so that CMake will rerun Interrogate if the .N files are modified:
  127. get_filename_component(source_path "${source}" PATH)
  128. get_filename_component(source_name_we "${source}" NAME_WE)
  129. set(nfile "${source_path}/${source_name_we}.N")
  130. if(EXISTS "${nfile}")
  131. list(APPEND nfiles "${nfile}")
  132. endif()
  133. endif()
  134. endforeach(source)
  135. # Interrogate also needs the include paths, so we'll extract them from the
  136. # target:
  137. set(include_flags)
  138. get_target_property(include_dirs "${target}" INTERFACE_INCLUDE_DIRECTORIES)
  139. foreach(include_dir ${include_dirs})
  140. # To keep the command-line small, also make this relative:
  141. # Note that Interrogate does NOT handle -I paths relative to -srcdir, so
  142. # we make them relative to the directory where it's invoked.
  143. file(RELATIVE_PATH rel_include_dir "${CMAKE_CURRENT_BINARY_DIR}" "${include_dir}")
  144. list(APPEND include_flags "-I${rel_include_dir}")
  145. endforeach(include_dir)
  146. # The above must also be included when compiling the resulting _igate.cxx file:
  147. include_directories(${include_dirs})
  148. # Get the compiler definition flags. These must be passed to Interrogate
  149. # in the same way that they are passed to the compiler so that Interrogate
  150. # will preprocess each file in the same way.
  151. set(define_flags)
  152. get_target_property(target_defines "${target}" COMPILE_DEFINITIONS)
  153. foreach(target_define ${target_defines})
  154. list(APPEND define_flags "-D${target_define}")
  155. endforeach(target_define)
  156. # If this is a release build that has NDEBUG defined, we need that too:
  157. string(TOUPPER "${CMAKE_BUILD_TYPE}" build_type)
  158. if("${CMAKE_CXX_FLAGS_${build_type}}" MATCHES ".*NDEBUG.*")
  159. list(APPEND define_flags "-DNDEBUG")
  160. endif()
  161. # CMake offers no way to directly depend on the composite files from here,
  162. # because the composite files are created in a different directory from
  163. # where CMake itself is run. Therefore, we need to depend on the
  164. # TARGET_composite target, if it exists.
  165. if(TARGET ${target}_composite)
  166. set(sources ${target}_composite ${sources})
  167. endif()
  168. add_custom_command(
  169. OUTPUT "${output}" "${database}"
  170. COMMAND interrogate
  171. -oc "${output}"
  172. -od "${database}"
  173. -srcdir "${srcdir}"
  174. -module ${module} -library ${target}
  175. ${INTERROGATE_OPTIONS}
  176. ${IGATE_FLAGS}
  177. ${define_flags}
  178. -S "${PROJECT_BINARY_DIR}/include"
  179. -S "${PROJECT_SOURCE_DIR}/dtool/src/parser-inc"
  180. -S "${PROJECT_BINARY_DIR}/include/parser-inc"
  181. ${include_flags}
  182. ${scan_sources}
  183. DEPENDS interrogate ${sources} ${nfiles}
  184. COMMENT "Interrogating ${target}"
  185. )
  186. endif()
  187. endfunction(interrogate_sources)
  188. #
  189. # Function: add_python_module(module [lib1 [lib2 ...]])
  190. # Uses interrogate to create a Python module.
  191. #
  192. function(add_python_module module)
  193. if(HAVE_PYTHON AND HAVE_INTERROGATE)
  194. set(targets ${ARGN})
  195. set(infiles)
  196. set(sources)
  197. set(HACKlinklibs)
  198. foreach(target ${targets})
  199. interrogate_sources(${target} "${target}_igate.cxx" "${target}.in" "${module}")
  200. list(APPEND infiles "${target}.in")
  201. #list(APPEND sources "${target}_igate.cxx")
  202. # HACK HACK HACK:
  203. # Currently, the codebase has dependencies on the Interrogate-generated
  204. # code when HAVE_PYTHON is enabled. rdb is working to remove this, but
  205. # until then, the generated code must somehow be made available to the
  206. # modules themselves. The workaround here is to put the _igate.cxx into
  207. # its own micro-library, which is linked both here on the module and
  208. # against the component library in question.
  209. add_library(${target}_igate ${target}_igate.cxx)
  210. list(APPEND HACKlinklibs "${target}_igate")
  211. get_target_property(target_links "${target}" LINK_LIBRARIES)
  212. target_link_libraries(${target}_igate ${target_links})
  213. endforeach(target)
  214. add_custom_command(
  215. OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/${module}_module.cxx"
  216. COMMAND interrogate_module
  217. -oc "${CMAKE_CURRENT_BINARY_DIR}/${module}_module.cxx"
  218. -module ${module} -library ${module}
  219. ${INTERROGATE_MODULE_OPTIONS}
  220. ${IMOD_FLAGS} ${infiles}
  221. DEPENDS interrogate_module ${infiles}
  222. COMMENT "Generating module ${module}"
  223. )
  224. add_library(${module} MODULE "${module}_module.cxx" ${sources})
  225. target_link_libraries(${module}
  226. ${targets} ${PYTHON_LIBRARIES} p3interrogatedb)
  227. target_link_libraries(${module} ${HACKlinklibs})
  228. set_target_properties(${module} PROPERTIES
  229. LIBRARY_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}/panda3d"
  230. PREFIX ""
  231. )
  232. if(WIN32 AND NOT CYGWIN)
  233. set_target_properties(${module} PROPERTIES SUFFIX ".pyd")
  234. endif()
  235. endif()
  236. endfunction(add_python_module)