QtProtobufGen.cmake 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208
  1. # TODO: give the variable a proper name while migrating to Qt6
  2. set(_qt_internal_qtprotobuf_top_level_dir "${CMAKE_CURRENT_LIST_DIR}")
  3. function(qtprotobuf_link_target target generated_target)
  4. target_sources(${target} PRIVATE $<TARGET_OBJECTS:${generated_target}>)
  5. target_include_directories(${target} PRIVATE $<TARGET_PROPERTY:${generated_target},INTERFACE_INCLUDE_DIRECTORIES>)
  6. add_dependencies(${target} ${generated_target})
  7. endfunction()
  8. function(qtprotobuf_generate)
  9. set(options MULTI QML COMMENTS FOLDER FIELDENUM)
  10. set(oneValueArgs OUTPUT_DIRECTORY TARGET GENERATED_TARGET EXTRA_NAMESPACE)
  11. set(multiValueArgs EXCLUDE_HEADERS PROTO_FILES PROTO_INCLUDES)
  12. cmake_parse_arguments(arg "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
  13. if(NOT DEFINED arg_PROTO_FILES)
  14. message(FATAL_ERROR "Missing the PROTO_FILES argument.")
  15. endif()
  16. if(DEFINED arg_GENERATED_TARGET)
  17. set(generated_target_name ${arg_GENERATED_TARGET})
  18. elseif(TARGET ${arg_TARGET})
  19. set(generated_target_name ${arg_TARGET}_qtprotobuf_gen)
  20. else()
  21. message(FATAL_ERROR "Either 'TARGET' or 'GENERATED_TARGET' argument needs to be set.")
  22. endif()
  23. set(generation_options "")
  24. set(extra_pre_parse_options "")
  25. set(generation_type "SIGNLE")
  26. if(arg_MULTI)
  27. set(generation_type "MULTI")
  28. list(APPEND extra_pre_parse_options "-DMULTI=TRUE")
  29. set(arg_FOLDER TRUE)
  30. message(STATUS "Enabling Multi-file generation. Folder-based generation is enabled"
  31. " automatically for ${generated_target_name}.")
  32. endif()
  33. list(APPEND generation_options "${generation_type}")
  34. if(arg_QML)
  35. if(NOT TARGET Qt::Qml)
  36. message(FATAL_ERROR "Trying to enable QML support for '${generated_target_name}',"
  37. " but Qt::Qml is not a target."
  38. "\nfind_package(Qt<VERSION> COMPONENTS Qml) is missing?"
  39. )
  40. endif()
  41. message(STATUS "Enabling QML generation for ${generated_target_name}")
  42. list(APPEND generation_options "QML")
  43. endif()
  44. if(arg_COMMENTS)
  45. message(STATUS "Enabling COMMENTS generation for ${generated_target_name}")
  46. list(APPEND generation_options "COMMENTS")
  47. endif()
  48. if(arg_FOLDER)
  49. message(STATUS "Enabling FOLDER generation for ${generated_target_name}")
  50. list(APPEND generation_options "FOLDER")
  51. list(APPEND extra_pre_parse_options "-DFOLDER=TRUE")
  52. endif()
  53. if(arg_FIELDENUM)
  54. message(STATUS "Enabling FIELDENUM generation for ${generated_target_name}")
  55. list(APPEND generation_options "FIELDENUM")
  56. endif()
  57. list(JOIN generation_options ":" generation_options_string)
  58. if(arg_EXTRA_NAMESPACE)
  59. set(generation_options_string "${generation_options_string}:EXTRA_NAMESPACE=\"${arg_EXTRA_NAMESPACE}\"")
  60. endif()
  61. if(WIN32)
  62. set(PROTOC_COMMAND set QT_PROTOBUF_OPTIONS=${generation_options_string}&& $<TARGET_FILE:protobuf::protoc>)
  63. else()
  64. set(PROTOC_COMMAND "QT_PROTOBUF_OPTIONS=${generation_options_string}" $<TARGET_FILE:protobuf::protoc>)
  65. endif()
  66. set(proto_includes ${arg_PROTO_INCLUDES})
  67. set(pre_parse_script "${_qt_internal_qtprotobuf_top_level_dir}/QtPreParseProtoFile.cmake")
  68. if(NOT EXISTS "${pre_parse_script}")
  69. message(FATAL_ERROR "Unable to locate the QtPreParseProtoFile script"
  70. " ${pre_parse_script}.")
  71. endif()
  72. unset(generated_files)
  73. foreach(proto_file IN LISTS arg_PROTO_FILES)
  74. get_filename_component(proto_file_base_dir "${proto_file}" DIRECTORY)
  75. list(PREPEND proto_includes "-I\"${proto_file_base_dir}\"")
  76. execute_process(COMMAND ${CMAKE_COMMAND}
  77. "${extra_pre_parse_options}"
  78. -DPROTO_FILE=${proto_file}
  79. -P ${pre_parse_script}
  80. OUTPUT_VARIABLE output
  81. ERROR_VARIABLE error_output # CMake writes messages to standard error.
  82. RESULT_VARIABLE result)
  83. string(STRIP "${output}" output)
  84. string(STRIP "${error_output}" error_output)
  85. if(NOT result EQUAL 0)
  86. message(FATAL_ERROR "Unable to parse '${proto_file}': ${error_output}")
  87. endif()
  88. list(APPEND generated_files ${output} ${error_output})
  89. endforeach()
  90. if(NOT generated_files)
  91. message(FATAL_ERROR "Unable to get the list of generated sources for target"
  92. " '${generated_target_name}'")
  93. endif()
  94. if(arg_MULTI)
  95. # TODO: add globalenums by default. But it's better to verify if proto file contains a
  96. # global enum.
  97. list(APPEND generated_files globalenums.h)
  98. endif()
  99. foreach(header IN LISTS arg_EXCLUDE_HEADERS)
  100. list(REMOVE_ITEM generated_files ${header})
  101. endforeach()
  102. list(REMOVE_DUPLICATES generated_files)
  103. if(NOT DEFINED arg_OUTPUT_DIRECTORY OR "${arg_OUTPUT_DIRECTORY}" STREQUAL "")
  104. set(out_dir ${CMAKE_CURRENT_BINARY_DIR})
  105. else()
  106. set(out_dir ${arg_OUTPUT_DIRECTORY})
  107. endif()
  108. file(MAKE_DIRECTORY ${out_dir})
  109. list(TRANSFORM generated_files PREPEND "${out_dir}/")
  110. set_source_files_properties(${generated_files} PROPERTIES
  111. GENERATED TRUE
  112. SKIP_AUTOGEN ON
  113. )
  114. # Filter generated headers
  115. unset(generated_headers)
  116. foreach(header IN LISTS generated_files)
  117. get_filename_component(extension "${header}" LAST_EXT)
  118. if(extension STREQUAL ".h")
  119. list(APPEND generated_headers "${header}")
  120. endif()
  121. endforeach()
  122. qt5_wrap_cpp(moc_sources ${generated_headers})
  123. get_property(num_deps GLOBAL PROPERTY ${generated_target_name}_deps_num)
  124. if(NOT num_deps)
  125. set(num_deps 0)
  126. endif()
  127. set(deps_target ${generated_target_name}_deps_${num_deps})
  128. math(EXPR num_deps "${num_deps} + 1")
  129. set_property(GLOBAL PROPERTY ${generated_target_name}_deps_num "${num_deps}")
  130. add_custom_command(OUTPUT ${generated_files}
  131. COMMAND ${PROTOC_COMMAND}
  132. --plugin=protoc-gen-qtprotobufgen=$<TARGET_FILE:${QT_PROTOBUF_NAMESPACE}::qtprotobufgen>
  133. --qtprotobufgen_out=${out_dir}
  134. ${proto_includes}
  135. ${arg_PROTO_FILES}
  136. WORKING_DIRECTORY ${out_dir}
  137. DEPENDS
  138. ${QT_PROTOBUF_NAMESPACE}::qtprotobufgen
  139. ${arg_PROTO_FILES}
  140. COMMENT "Generating QtProtobuf ${generated_target_name} sources..."
  141. COMMAND_EXPAND_LISTS
  142. )
  143. add_custom_target(${deps_target} DEPENDS ${generated_files})
  144. if(NOT TARGET ${generated_target_name})
  145. add_library(${generated_target_name} OBJECT ${generated_files} ${moc_sources})
  146. else()
  147. target_sources(${generated_target_name} PRIVATE ${generated_files} ${moc_sources})
  148. endif()
  149. add_dependencies(${generated_target_name} ${deps_target})
  150. set_target_properties(${generated_target_name} PROPERTIES PUBLIC_HEADER "${generated_headers}")
  151. #Add include directories in case if targets are found by find_project or in source tree
  152. target_include_directories(${generated_target_name} PUBLIC ${out_dir} PRIVATE
  153. $<TARGET_PROPERTY:${QT_PROTOBUF_NAMESPACE}::Protobuf,INTERFACE_INCLUDE_DIRECTORIES>)
  154. #TODO: Do not link targets if they are not used in .proto files.
  155. if(TARGET ${QT_PROTOBUF_NAMESPACE}::Grpc)
  156. target_include_directories(${generated_target_name} PRIVATE
  157. $<TARGET_PROPERTY:${QT_PROTOBUF_NAMESPACE}::Grpc,INTERFACE_INCLUDE_DIRECTORIES>)
  158. endif()
  159. if(TARGET ${QT_PROTOBUF_NAMESPACE}::ProtobufWellKnownTypes)
  160. target_include_directories(${generated_target_name} PRIVATE
  161. $<TARGET_PROPERTY:${QT_PROTOBUF_NAMESPACE}::ProtobufWellKnownTypes,INTERFACE_INCLUDE_DIRECTORIES>)
  162. endif()
  163. if(TARGET ${QT_PROTOBUF_NAMESPACE}::ProtobufQtTypes)
  164. target_include_directories(${generated_target_name} PRIVATE
  165. $<TARGET_PROPERTY:${QT_PROTOBUF_NAMESPACE}::ProtobufQtTypes,INTERFACE_INCLUDE_DIRECTORIES>)
  166. endif()
  167. # Automatically link whole static library to specified in parameters target
  168. if(TARGET ${arg_TARGET})
  169. qtprotobuf_link_target(${arg_TARGET} ${generated_target_name})
  170. endif()
  171. endfunction()