Protobuf.cmake 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141
  1. include_guard(DIRECTORY)
  2. #[[
  3. Add rules for creating Google Protobuf source files.
  4. qm_create_protobuf(<OUT>
  5. INPUT <files...>
  6. [OUTPUT_DIR <dir>]
  7. [TARGET <target>]
  8. [INCLUDE_DIRECTORIES <dirs...>]
  9. [OPTIONS <options...>]
  10. [DEPENDS <deps...>]
  11. [CREATE_ONCE]
  12. )
  13. INPUT: source files
  14. OUTPUT_DIR: output directory
  15. TARGET: add a custom target to run the generating command
  16. INCLUDE_DIRECTORIES: extra include directories
  17. OPTIONS: extra options passed to protobuf compiler
  18. DEPENDS: dependencies
  19. CREATE_ONCE: create proto code files at configure phase if not exist
  20. OUT: output source file paths
  21. #]]
  22. function(qm_create_protobuf _out)
  23. set(options CREATE_ONCE)
  24. set(oneValueArgs OUTPUT_DIR TARGET)
  25. set(multiValueArgs INPUT INCLUDE_DIRECTORIES OPTIONS DEPENDS)
  26. cmake_parse_arguments(FUNC "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
  27. # Find `protoc`
  28. if(NOT PROTOC_EXECUTABLE)
  29. if(NOT TARGET protobuf::protoc)
  30. message(FATAL_ERROR "qm_create_protobuf: protobuf compiler not found. Add find_package(Protobuf) to CMake to enable.")
  31. endif()
  32. get_target_property(PROTOC_EXECUTABLE protobuf::protoc LOCATION)
  33. if(NOT PROTOC_EXECUTABLE)
  34. message(FATAL_ERROR "qm_create_protobuf: failed to get the location of `protoc`.")
  35. endif()
  36. # Cache value
  37. set(PROTOC_EXECUTABLE ${PROTOC_EXECUTABLE} PARENT_SCOPE)
  38. endif()
  39. if(NOT FUNC_INPUT)
  40. message(FATAL_ERROR "qm_create_protobuf: INPUT not specified.")
  41. endif()
  42. if(FUNC_OUTPUT_DIR)
  43. get_filename_component(_out_dir ${FUNC_OUTPUT_DIR} ABSOLUTE)
  44. file(MAKE_DIRECTORY ${_out_dir})
  45. else()
  46. set(_out_dir ${CMAKE_CURRENT_BINARY_DIR})
  47. endif()
  48. # Collect include paths and out files
  49. set(_include_options)
  50. set(_out_files)
  51. set(_input_names)
  52. foreach(_item IN LISTS FUNC_INCLUDE_DIRECTORIES)
  53. get_filename_component(_abs_path ${_item} ABSOLUTE)
  54. list(APPEND _include_options -I${_abs_path})
  55. endforeach()
  56. foreach(_item IN LISTS FUNC_INPUT)
  57. get_filename_component(_item ${_item} ABSOLUTE)
  58. get_filename_component(_abs_path ${_item} DIRECTORY)
  59. list(APPEND _include_options -I${_abs_path})
  60. endforeach()
  61. list(REMOVE_DUPLICATES _include_options)
  62. set(_create_once_warning)
  63. set(_create_once_warning_printed off)
  64. # Prepare for create once
  65. if(FUNC_CREATE_ONCE)
  66. # Check if options contain generator expressions
  67. foreach(_opt IN LISTS _include_options LISTS FUNC_OPTIONS)
  68. string(GENEX_STRIP "${_opt}" _no_genex)
  69. if(NOT _no_genex STREQUAL _opt)
  70. set(_create_once_warning "options contain generator expressions, skip generating source file now")
  71. break()
  72. endif()
  73. endforeach()
  74. endif()
  75. foreach(_item IN LISTS FUNC_INPUT)
  76. get_filename_component(_basename ${_item} NAME_WLE)
  77. list(APPEND _out_files ${_out_dir}/${_basename}.pb.h ${_out_dir}/${_basename}.pb.cc)
  78. get_filename_component(_name ${_item} NAME)
  79. list(APPEND _input_names ${_name})
  80. if(FUNC_CREATE_ONCE AND(NOT EXISTS ${_out_dir}/${_basename}.pb.h OR NOT EXISTS ${_out_dir}/${_basename}.pb.cc))
  81. if(_create_once_warning)
  82. if(NOT _create_once_warning_printed)
  83. message(WARNING "qm_create_protobuf: ${_create_once_warning}")
  84. set(_create_once_warning_printed on)
  85. endif()
  86. else()
  87. get_filename_component(_abs_file ${_item} ABSOLUTE)
  88. if(NOT EXISTS ${_abs_file})
  89. message(WARNING "qm_create_protobuf: input file \"${_name}\" is not available, skip generating source file now")
  90. else()
  91. message(STATUS "Protoc: Generating ${_basename}.pb.h, ${_basename}.pb.cc")
  92. execute_process(COMMAND
  93. ${PROTOC_EXECUTABLE} --cpp_out=${_out_dir} ${_include_options} ${FUNC_OPTIONS} ${_name}
  94. )
  95. endif()
  96. endif()
  97. endif()
  98. add_custom_command(
  99. OUTPUT ${_out_dir}/${_basename}.pb.h ${_out_dir}/${_basename}.pb.cc
  100. COMMAND ${PROTOC_EXECUTABLE} --cpp_out=${_out_dir} ${_include_options} ${FUNC_OPTIONS} ${_name}
  101. DEPENDS ${_item} ${FUNC_DEPENDS}
  102. WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
  103. VERBATIM
  104. )
  105. endforeach()
  106. if(FUNC_TARGET)
  107. if(NOT TARGET ${FUNC_TARGET})
  108. add_custom_target(${FUNC_TARGET})
  109. endif()
  110. add_dependencies(${FUNC_TARGET} ${_out_files})
  111. endif()
  112. set(${_out} ${_out_files} PARENT_SCOPE)
  113. endfunction()