Interrogate.cmake 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308
  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. # This stores the names of every module added to the Interrogate system:
  33. set(ALL_INTERROGATE_MODULES CACHE INTERNAL "Internal variable")
  34. #
  35. # Function: target_interrogate(target [ALL] [source1 [source2 ...]])
  36. # NB. This doesn't actually invoke interrogate, but merely adds the
  37. # sources to the list of scan sources associated with the target.
  38. # Interrogate will be invoked when add_python_module is called.
  39. # If ALL is specified, all of the sources from the associated
  40. # target are added.
  41. #
  42. function(target_interrogate target)
  43. if(HAVE_PYTHON AND HAVE_INTERROGATE)
  44. set(sources)
  45. set(want_all OFF)
  46. foreach(arg ${ARGN})
  47. if(arg STREQUAL "ALL")
  48. set(want_all ON)
  49. else()
  50. list(APPEND sources "${arg}")
  51. endif()
  52. endforeach()
  53. # If ALL was specified, pull in all sources from the target.
  54. if(want_all)
  55. get_target_property(target_sources "${target}" SOURCES)
  56. list(APPEND sources ${target_sources})
  57. endif()
  58. list(REMOVE_DUPLICATES sources)
  59. # Now let's get everything's absolute path, so that it can be passed
  60. # through a property while still preserving the reference.
  61. set(absolute_sources)
  62. foreach(source ${sources})
  63. get_source_file_property(exclude "${source}" WRAP_EXCLUDE)
  64. if(NOT exclude)
  65. get_source_file_property(location "${source}" LOCATION)
  66. list(APPEND absolute_sources ${location})
  67. endif()
  68. endforeach(source)
  69. set_target_properties("${target}" PROPERTIES IGATE_SOURCES
  70. "${absolute_sources}")
  71. # CMake has no property for determining the source directory where the
  72. # target was originally added. interrogate_sources makes use of this
  73. # property (if it is set) in order to make all paths on the command-line
  74. # relative to it, thereby shortening the command-line even more.
  75. # Since this is not an Interrogate-specific property, it is not named with
  76. # an IGATE_ prefix.
  77. set_target_properties("${target}" PROPERTIES TARGET_SRCDIR
  78. "${CMAKE_CURRENT_SOURCE_DIR}")
  79. # HACK HACK HACK -- this is part of the below hack.
  80. target_link_libraries(${target} ${target}_igate)
  81. endif()
  82. endfunction(target_interrogate)
  83. #
  84. # Function: interrogate_sources(target output database module)
  85. #
  86. # This function actually runs a component-level interrogation against 'target'.
  87. # It generates the outfile.cxx (output) and dbfile.in (database) files, which
  88. # can then be used during the interrogate_module step to produce language
  89. # bindings.
  90. #
  91. # The target must first have had sources selected with target_interrogate.
  92. # Failure to do so will result in an error.
  93. #
  94. function(interrogate_sources target output database module)
  95. if(HAVE_PYTHON AND HAVE_INTERROGATE)
  96. get_target_property(sources "${target}" IGATE_SOURCES)
  97. if(NOT sources)
  98. message(FATAL_ERROR
  99. "Cannot interrogate ${target} unless it's run through target_interrogate first!")
  100. endif()
  101. get_target_property(srcdir "${target}" TARGET_SRCDIR)
  102. if(NOT srcdir)
  103. # No TARGET_SRCDIR was set, so we'll do everything relative to our
  104. # current binary dir instead:
  105. set(srcdir "${CMAKE_CURRENT_BINARY_DIR}")
  106. endif()
  107. set(scan_sources)
  108. set(nfiles)
  109. foreach(source ${sources})
  110. get_filename_component(source_basename "${source}" NAME)
  111. # Only certain sources should actually be scanned by Interrogate. The
  112. # rest are merely dependencies. This uses the exclusion regex above in
  113. # order to determine what files are okay:
  114. set(exclude OFF)
  115. foreach(regex ${INTERROGATE_EXCLUDE_REGEXES})
  116. if("${source_basename}" MATCHES "${regex}")
  117. set(exclude ON)
  118. endif()
  119. endforeach(regex)
  120. if(NOT exclude)
  121. # This file is to be scanned by Interrogate. In order to avoid
  122. # cluttering up the command line, we should first make it relative:
  123. file(RELATIVE_PATH rel_source "${srcdir}" "${source}")
  124. list(APPEND scan_sources "${rel_source}")
  125. # Also see if this file has a .N counterpart, which has directives
  126. # specific for Interrogate. If there is a .N file, we add it as a dep,
  127. # so that CMake will rerun Interrogate if the .N files are modified:
  128. get_filename_component(source_path "${source}" PATH)
  129. get_filename_component(source_name_we "${source}" NAME_WE)
  130. set(nfile "${source_path}/${source_name_we}.N")
  131. if(EXISTS "${nfile}")
  132. list(APPEND nfiles "${nfile}")
  133. endif()
  134. endif()
  135. endforeach(source)
  136. # Interrogate also needs the include paths, so we'll extract them from the
  137. # target:
  138. set(include_flags)
  139. get_target_property(include_dirs "${target}" INTERFACE_INCLUDE_DIRECTORIES)
  140. foreach(include_dir ${include_dirs})
  141. # To keep the command-line small, also make this relative:
  142. # Note that Interrogate does NOT handle -I paths relative to -srcdir, so
  143. # we make them relative to the directory where it's invoked.
  144. file(RELATIVE_PATH rel_include_dir "${CMAKE_CURRENT_BINARY_DIR}" "${include_dir}")
  145. list(APPEND include_flags "-I${rel_include_dir}")
  146. endforeach(include_dir)
  147. # The above must also be included when compiling the resulting _igate.cxx file:
  148. include_directories(${include_dirs})
  149. # Get the compiler definition flags. These must be passed to Interrogate
  150. # in the same way that they are passed to the compiler so that Interrogate
  151. # will preprocess each file in the same way.
  152. set(define_flags)
  153. get_target_property(target_defines "${target}" COMPILE_DEFINITIONS)
  154. if(target_defines)
  155. foreach(target_define ${target_defines})
  156. list(APPEND define_flags "-D${target_define}")
  157. # And add the same definition when we compile the _igate.cxx file:
  158. add_definitions("-D${target_define}")
  159. endforeach(target_define)
  160. endif()
  161. # If this is a release build that has NDEBUG defined, we need that too:
  162. string(TOUPPER "${CMAKE_BUILD_TYPE}" build_type)
  163. if("${CMAKE_CXX_FLAGS_${build_type}}" MATCHES ".*NDEBUG.*")
  164. list(APPEND define_flags "-DNDEBUG")
  165. endif()
  166. # CMake offers no way to directly depend on the composite files from here,
  167. # because the composite files are created in a different directory from
  168. # where CMake itself is run. Therefore, we need to depend on the
  169. # TARGET_composite target, if it exists.
  170. if(TARGET ${target}_composite)
  171. set(sources ${target}_composite ${sources})
  172. endif()
  173. add_custom_command(
  174. OUTPUT "${output}" "${database}"
  175. COMMAND interrogate
  176. -oc "${output}"
  177. -od "${database}"
  178. -srcdir "${srcdir}"
  179. -module ${module} -library ${target}
  180. ${INTERROGATE_OPTIONS}
  181. ${IGATE_FLAGS}
  182. ${define_flags}
  183. -S "${PROJECT_BINARY_DIR}/include"
  184. -S "${PROJECT_SOURCE_DIR}/dtool/src/parser-inc"
  185. -S "${PROJECT_BINARY_DIR}/include/parser-inc"
  186. ${include_flags}
  187. ${scan_sources}
  188. DEPENDS interrogate ${sources} ${nfiles}
  189. COMMENT "Interrogating ${target}"
  190. )
  191. endif()
  192. endfunction(interrogate_sources)
  193. #
  194. # Function: add_python_module(module [lib1 [lib2 ...]] [LINK lib1 ...])
  195. # Uses interrogate to create a Python module. If the LINK keyword is specified,
  196. # the Python module is linked against the specified libraries instead of those
  197. # listed before.
  198. #
  199. function(add_python_module module)
  200. if(HAVE_PYTHON AND HAVE_INTERROGATE)
  201. set(targets)
  202. set(link_targets)
  203. set(infiles)
  204. set(sources)
  205. set(HACKlinklibs)
  206. set(link_keyword OFF)
  207. foreach(arg ${ARGN})
  208. if(arg STREQUAL "LINK")
  209. set(link_keyword ON)
  210. elseif(link_keyword)
  211. list(APPEND link_targets "${arg}")
  212. else()
  213. list(APPEND targets "${arg}")
  214. endif()
  215. endforeach(arg)
  216. if(NOT link_keyword)
  217. set(link_targets ${targets})
  218. endif()
  219. foreach(target ${targets})
  220. interrogate_sources(${target} "${target}_igate.cxx" "${target}.in" "${module}")
  221. list(APPEND infiles "${target}.in")
  222. #list(APPEND sources "${target}_igate.cxx")
  223. # HACK HACK HACK:
  224. # Currently, the codebase has dependencies on the Interrogate-generated
  225. # code when HAVE_PYTHON is enabled. rdb is working to remove this, but
  226. # until then, the generated code must somehow be made available to the
  227. # modules themselves. The workaround here is to put the _igate.cxx into
  228. # its own micro-library, which is linked both here on the module and
  229. # against the component library in question.
  230. add_library(${target}_igate ${target}_igate.cxx)
  231. list(APPEND HACKlinklibs "${target}_igate")
  232. install(TARGETS ${target}_igate DESTINATION lib)
  233. get_target_property(target_links "${target}" LINK_LIBRARIES)
  234. target_link_libraries(${target}_igate ${target_links})
  235. endforeach(target)
  236. add_custom_command(
  237. OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/${module}_module.cxx"
  238. COMMAND interrogate_module
  239. -oc "${CMAKE_CURRENT_BINARY_DIR}/${module}_module.cxx"
  240. -module ${module} -library ${module}
  241. ${INTERROGATE_MODULE_OPTIONS}
  242. ${IMOD_FLAGS} ${infiles}
  243. DEPENDS interrogate_module ${infiles}
  244. COMMENT "Generating module ${module}"
  245. )
  246. add_library(${module} MODULE "${module}_module.cxx" ${sources})
  247. target_link_libraries(${module}
  248. ${link_targets} ${PYTHON_LIBRARIES} p3interrogatedb)
  249. target_link_libraries(${module} ${HACKlinklibs})
  250. set_target_properties(${module} PROPERTIES
  251. LIBRARY_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}/panda3d"
  252. PREFIX ""
  253. )
  254. if(WIN32 AND NOT CYGWIN)
  255. set_target_properties(${module} PROPERTIES SUFFIX ".pyd")
  256. endif()
  257. install(TARGETS ${module} DESTINATION "${PYTHON_ARCH_INSTALL_DIR}/panda3d")
  258. list(APPEND ALL_INTERROGATE_MODULES "${module}")
  259. set(ALL_INTERROGATE_MODULES "${ALL_INTERROGATE_MODULES}" CACHE INTERNAL "Internal variable")
  260. endif()
  261. endfunction(add_python_module)
  262. if(HAVE_PYTHON)
  263. # We have to create an __init__.py so that Python 2.x can recognize 'panda3d'
  264. # as a package.
  265. file(WRITE "${PROJECT_BINARY_DIR}/panda3d/__init__.py" "")
  266. # The Interrogate path needs to be installed to the architecture-dependent
  267. # Python directory.
  268. install(FILES "${PROJECT_BINARY_DIR}/panda3d/__init__.py" DESTINATION "${PYTHON_ARCH_INSTALL_DIR}/panda3d")
  269. endif()