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