Interrogate.cmake 11 KB

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