ctkScriptWrapPythonQt_Light.cmake 8.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272
  1. ###########################################################################
  2. #
  3. # Library: CTK
  4. #
  5. # Copyright (c) Kitware Inc.
  6. #
  7. # Licensed under the Apache License, Version 2.0 (the "License");
  8. # you may not use this file except in compliance with the License.
  9. # You may obtain a copy of the License at
  10. #
  11. # http://www.apache.org/licenses/LICENSE-2.0.txt
  12. #
  13. # Unless required by applicable law or agreed to in writing, software
  14. # distributed under the License is distributed on an "AS IS" BASIS,
  15. # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  16. # See the License for the specific language governing permissions and
  17. # limitations under the License.
  18. #
  19. ###########################################################################
  20. #
  21. # ctkScriptWrapPythonQt_Light
  22. #
  23. #
  24. # Depends on:
  25. # CTK/CMake/ctkMacroWrapPythonQt.cmake
  26. #
  27. #
  28. # This script should be invoked either as a CUSTOM_COMMAND
  29. # or from the command line using the following syntax:
  30. #
  31. # cmake -DWRAPPING_NAMESPACE:STRING=org.commontk -DTARGET:STRING=MyLib
  32. # -DSOURCES:STRING="file1^^file2" -DINCLUDE_DIRS:STRING=/path1:/path2
  33. # -DWRAP_INT_DIR:STRING=subir/subir/
  34. # -DOUTPUT_DIR:PATH=/path [-DQT_QMAKE_EXECUTABLE:PATH=/path/to/qt/qmake]
  35. # -DPYTHON_EXECUTABLE:FILEPATH=/path/to/python
  36. # -DPYTHON_LIBRARY_PATH:PATH=/path/to/pythonlib
  37. # -DHAS_DECORATOR:BOOL=True
  38. # -P ctkScriptWrapPythonQt_Light.cmake
  39. #
  40. #
  41. # LOG FILE:
  42. # File ctkScriptWrapPythonQt_Light_log.txt will be created in the current directory.
  43. # It will contain the list of class and the constructor signature that will be wrapped.
  44. #
  45. set(verbose 0)
  46. #
  47. # Convenient function allowing to log the reason why a given class hasn't been wrapped
  48. # If verbose=1, it will also be displayed on the standard output
  49. #
  50. function(log msg)
  51. if(verbose)
  52. message(${msg})
  53. endif()
  54. file(APPEND "${CMAKE_CURRENT_BINARY_DIR}/ctkScriptWrapPythonQt_Light_log.txt" "${msg}\n")
  55. endfunction()
  56. #
  57. # Convenient function allowing to invoke re.search(regex, string) using the given interpreter.
  58. # Note that is_matching will be set to True if there is a match
  59. #
  60. function(reSearchFile python_exe python_library_path regex file is_matching)
  61. set(python_cmd "import re\; f = open('${file}', 'r')\;
  62. res = re.search\(\"${regex}\", f.read(), re.MULTILINE\)\;
  63. if res == None: print \"FALSE\"
  64. else: print \"TRUE\"
  65. ")
  66. #message("python_cmd: ${python_cmd}")
  67. if(WIN32)
  68. set(ENV{PATH} ${python_library_path};$ENV{PATH})
  69. elseif(APPLE)
  70. set(ENV{DYLD_LIBRARY_PATH} ${python_library_path}:$ENV{DYLD_LIBRARY_PATH})
  71. else()
  72. set(ENV{LD_LIBRARY_PATH} ${python_library_path}:$ENV{LD_LIBRARY_PATH})
  73. endif()
  74. execute_process(
  75. COMMAND ${python_exe} -c ${python_cmd};
  76. RESULT_VARIABLE result
  77. OUTPUT_VARIABLE output
  78. ERROR_VARIABLE error
  79. OUTPUT_STRIP_TRAILING_WHITESPACE
  80. )
  81. if(result)
  82. message(FATAL_ERROR "reSearchFile - Problem with regex: ${regex}\n${error}")
  83. endif()
  84. #message(${output})
  85. set(is_matching ${output} PARENT_SCOPE)
  86. endfunction()
  87. if(NOT DEFINED CMAKE_CURRENT_LIST_DIR)
  88. get_filename_component(CMAKE_CURRENT_LIST_DIR ${CMAKE_CURRENT_LIST_FILE} PATH)
  89. endif()
  90. if(NOT DEFINED CMAKE_CURRENT_LIST_FILENAME)
  91. get_filename_component(CMAKE_CURRENT_LIST_FILENAME ${CMAKE_CURRENT_LIST_FILE} NAME)
  92. endif()
  93. # Check for non-defined var
  94. foreach(var WRAPPING_NAMESPACE TARGET SOURCES INCLUDE_DIRS WRAP_INT_DIR HAS_DECORATOR)
  95. if(NOT DEFINED ${var})
  96. message(FATAL_ERROR "${var} not specified when calling ctkScriptWrapPythonQt")
  97. endif()
  98. endforeach()
  99. # Check for non-existing ${var}
  100. set(requiredVariables OUTPUT_DIR PYTHON_EXECUTABLE PYTHON_LIBRARY_PATH)
  101. if (CTK_QT_VERSION VERSION_EQUAL "4")
  102. list(APPEND QT_QMAKE_EXECUTABLE requiredVariables)
  103. endif()
  104. foreach(var )
  105. if(NOT EXISTS ${${var}})
  106. message(FATAL_ERROR "Failed to find ${var}=\"${${var}}\" when calling ctkScriptWrapPythonQt")
  107. endif()
  108. endforeach()
  109. # Clear log file
  110. file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/ctkScriptWrapPythonQt_Light_log.txt" "")
  111. # Convert wrapping namespace to subdir
  112. string(REPLACE "." "_" WRAPPING_NAMESPACE_UNDERSCORE ${WRAPPING_NAMESPACE})
  113. # Convert ^^ separated string to list
  114. string(REPLACE "^^" ";" SOURCES "${SOURCES}")
  115. foreach(FILE ${SOURCES})
  116. # what is the filename without the extension
  117. get_filename_component(TMP_FILENAME ${FILE} NAME_WE)
  118. set(includes
  119. "${includes}\n#include \"${TMP_FILENAME}.h\"")
  120. # Extract classname - NOTE: We assume the filename matches the associated class
  121. set(className ${TMP_FILENAME})
  122. #message(STATUS "FILE:${FILE}, className:${className}")
  123. # Extract parent classname
  124. set(parentClassName)
  125. if("${parentClassName}" STREQUAL "")
  126. # Does constructor signature is of the form: myclass()
  127. set(regex "[^~]${className}[\\s\\n]*\\([\\s\\n]*\\)")
  128. reSearchfile(${PYTHON_EXECUTABLE} ${PYTHON_LIBRARY_PATH} ${regex} ${FILE} is_matching)
  129. if(is_matching)
  130. set(parentClassName "No")
  131. log("${TMP_FILENAME} - constructor of the form: ${className}\(\)")
  132. endif()
  133. endif()
  134. if("${parentClassName}" STREQUAL "")
  135. # Does constructor signature is of the form: myclass(QObject * parent ...)
  136. set(regex "${className}[\\s\\n]*\\([\\s\\n]*QObject[\\s\\n]*\\*[\\s\\n]*\\w+[\\s\\n]*(\\=[\\s\\n]*(0|NULL)|,.*\\=.*\\)|\\))")
  137. reSearchfile(${PYTHON_EXECUTABLE} ${PYTHON_LIBRARY_PATH} ${regex} ${FILE} is_matching)
  138. if(is_matching)
  139. set(parentClassName "QObject")
  140. log("${TMP_FILENAME} - constructor of the form: ${className}\(QObject * parent ... \)")
  141. endif()
  142. endif()
  143. if("${parentClassName}" STREQUAL "")
  144. # Does constructor signature is of the form: myclass(QWidget * parent ...)
  145. set(regex "${className}[\\s\\n]*\\([\\s\\n]*QWidget[\\s\\n]*\\*[\\s\\n]*\\w+[\\s\\n]*(\\=[\\s\\n]*(0|NULL)|,.*\\=.*\\)|\\))")
  146. reSearchfile(${PYTHON_EXECUTABLE} ${PYTHON_LIBRARY_PATH} ${regex} ${FILE} is_matching)
  147. if(is_matching)
  148. set(parentClassName "QWidget")
  149. log("${TMP_FILENAME} - constructor of the form: ${className}\(QWidget * parent ... \)")
  150. endif()
  151. endif()
  152. # Generate PythonQtWrapper class
  153. if("${parentClassName}" STREQUAL "QObject" OR "${parentClassName}" STREQUAL "QWidget")
  154. set(pythonqtWrappers
  155. "${pythonqtWrappers}
  156. //-----------------------------------------------------------------------------
  157. class PythonQtWrapper_${className} : public QObject
  158. {
  159. Q_OBJECT
  160. public:
  161. public Q_SLOTS:
  162. ${className}* new_${className}(${parentClassName}* parent = 0)
  163. {
  164. return new ${className}(parent);
  165. }
  166. void delete_${className}(${className}* obj) { delete obj; }
  167. };
  168. ")
  169. elseif("${parentClassName}" STREQUAL "No")
  170. set(pythonqtWrappers
  171. "${pythonqtWrappers}
  172. //-----------------------------------------------------------------------------
  173. class Q_DECL_EXPORT PythonQtWrapper_${className} : public QObject
  174. {
  175. Q_OBJECT
  176. public:
  177. public Q_SLOTS:
  178. ${className}* new_${className}()
  179. {
  180. return new ${className}();
  181. }
  182. void delete_${className}(${className}* obj) { delete obj; }
  183. };
  184. ")
  185. else() # Case parentClassName is empty
  186. message(WARNING "ctkScriptWrapPythonQt_Light - Problem wrapping ${FILE}")
  187. endif()
  188. # Generate code allowing to register the class metaobject and its associated "light" wrapper
  189. set(registerclasses "${registerclasses}
  190. PythonQt::self()->registerClass(
  191. &${className}::staticMetaObject, \"${TARGET}\",
  192. PythonQtCreateObject<PythonQtWrapper_${className}>);\n")
  193. endforeach()
  194. # Write master include file
  195. file(WRITE ${OUTPUT_DIR}/${WRAP_INT_DIR}${WRAPPING_NAMESPACE_UNDERSCORE}_${TARGET}0.h "//
  196. // File auto-generated by cmake macro ctkScriptWrapPythonQt_Light
  197. //
  198. #ifndef __${WRAPPING_NAMESPACE_UNDERSCORE}_${TARGET}0_h
  199. #define __${WRAPPING_NAMESPACE_UNDERSCORE}_${TARGET}0_h
  200. #include <QWidget>
  201. ${includes}
  202. ${pythonqtWrappers}
  203. #endif
  204. ")
  205. # Write wrapper header
  206. file(WRITE ${OUTPUT_DIR}/${WRAP_INT_DIR}${WRAPPING_NAMESPACE_UNDERSCORE}_${TARGET}_init.cpp "//
  207. // File auto-generated by cmake macro ctkScriptWrapPythonQt_Light
  208. //
  209. #include <PythonQt.h>
  210. #include \"${WRAPPING_NAMESPACE_UNDERSCORE}_${TARGET}0.h\"
  211. void PythonQt_init_${WRAPPING_NAMESPACE_UNDERSCORE}_${TARGET}(PyObject* module)
  212. {
  213. Q_UNUSED(module);
  214. ${registerclasses}
  215. }
  216. ")
  217. # Configure 'ctkMacroWrapPythonQtModuleInit.cpp.in' replacing TARGET and
  218. # WRAPPING_NAMESPACE_UNDERSCORE.
  219. configure_file(
  220. ${CMAKE_CURRENT_LIST_DIR}/ctkMacroWrapPythonQtModuleInit.cpp.in
  221. ${OUTPUT_DIR}/${WRAP_INT_DIR}${WRAPPING_NAMESPACE_UNDERSCORE}_${TARGET}_module_init.cpp
  222. )
  223. # Since file(WRITE ) doesn't update the timestamp - Let's touch the files
  224. execute_process(
  225. COMMAND ${CMAKE_COMMAND} -E touch
  226. ${OUTPUT_DIR}/${WRAP_INT_DIR}${WRAPPING_NAMESPACE_UNDERSCORE}_${TARGET}_init.cpp
  227. )