diff --git a/src/tools/qtprotobufgen/Qt6ProtobufToolsMacros.cmake b/src/tools/qtprotobufgen/Qt6ProtobufToolsMacros.cmake index 0e364b82..f95b0527 100644 --- a/src/tools/qtprotobufgen/Qt6ProtobufToolsMacros.cmake +++ b/src/tools/qtprotobufgen/Qt6ProtobufToolsMacros.cmake @@ -170,91 +170,58 @@ function(_qt_internal_protoc_generate target generator output_directory) target_include_directories(${target} PUBLIC "$") endfunction() -# The function looks for the enum and message definitions inside provided PROTO_FILES and returns -# list of the absolute .proto file paths, protobuf include paths and files that are expected to be -# generated by qtprotobufgen. -# Optional arguments: -# GENERATE_PACKAGE_SUBFOLDERS - generated files will be located in package-base subdirectories. -# -# Multi-value arguments: -# PROTO_FILES - input list of the proto files. May contain either absolute or relative paths. -function(_qt_internal_protobuf_preparse_proto_files - out_proto_files out_proto_includes out_generated_files out_qml_uri base_dir) +# The function looks for the enum and message definitions inside provided proto files and returns +# list of the absolute .proto file paths, protobuf include paths and packages found here. +function(_qt_internal_protobuf_preparse_proto_files target base_dir + out_proto_files out_proto_includes out_proto_packages) - cmake_parse_arguments(arg "GENERATE_PACKAGE_SUBFOLDERS;QML" "QML_URI" "PROTO_FILES" ${ARGN}) - - unset(proto_files) - unset(proto_includes) - unset(output_files) + set(proto_files "") + set(proto_includes "") set(proto_packages "") - foreach(f IN LISTS arg_PROTO_FILES) + foreach(f IN LISTS ARGN) if(NOT IS_ABSOLUTE "${f}") set(f "${base_dir}/${f}") get_filename_component(f "${f}" ABSOLUTE) endif() get_filename_component(f "${f}" REALPATH) - list(APPEND proto_files "${f}") + + if("${f}" IN_LIST proto_files) + message(WARNING "The .proto file ${f} is listed twice for ${target}." + " Skip processing for the second time.") + continue() + endif() _qt_internal_preparse_proto_file_common(result proto_package "${f}" "message;enum") if(NOT result) message(STATUS "No messages or enums found in ${f}. Skipping.") - return() + continue() endif() get_filename_component(proto_file_base_dir "${f}" DIRECTORY) - list(PREPEND proto_includes "${proto_file_base_dir}") - - string(REPLACE "." "/" package_full_path "${proto_package}") - set(folder_path "") - if(arg_GENERATE_PACKAGE_SUBFOLDERS) - set(folder_path "${package_full_path}/") - endif() - - get_filename_component(basename "${f}" NAME_WLE) - list(APPEND output_files - "${folder_path}${basename}.qpb.h" - "${folder_path}${basename}.qpb.cpp" - "${folder_path}${basename}_protobuftyperegistrations.cpp" - ) - if(arg_QML) - list(APPEND proto_packages "${proto_package}") - endif() + list(APPEND proto_files "${f}") + list(APPEND proto_includes "${proto_file_base_dir}") + list(APPEND proto_packages "${proto_package}") endforeach() - list(REMOVE_DUPLICATES proto_files) - list(REMOVE_DUPLICATES proto_includes) - list(REMOVE_DUPLICATES output_files) - - if(arg_QML) - if(arg_QML_URI) - set(qml_uri "${arg_QML_URI}") - elseif(proto_packages) - list(REMOVE_DUPLICATES proto_packages) - list(LENGTH proto_packages length) - if(NOT length EQUAL 1) - string(JOIN "\n" proto_packages_string "${proto_packages}") - message(FATAL_ERROR "All *.proto files must have single package name," - " that will be used for QML plugin registration." - "\nThe following packages found in the .proto files for ${target}:" - "\n${proto_packages_string}." - " Please split the ${target} target per package." - ) - endif() - list(GET proto_packages 0 qml_uri) - else() - message(FATAL_ERROR ".proto files of ${target} don't specify a package." - " Please, set QML_URI when using .proto without package name." - ) - endif() - if(qml_uri) - string(REPLACE "." "_" plguin_base_name "${qml_uri}") - list(APPEND output_files "${plguin_base_name}plugin.cpp") - set(${out_qml_uri} "${qml_uri}" PARENT_SCOPE) - endif() - endif() set(${out_proto_files} "${proto_files}" PARENT_SCOPE) set(${out_proto_includes} "${proto_includes}" PARENT_SCOPE) - set(${out_generated_files} "${output_files}" PARENT_SCOPE) + set(${out_proto_packages} "${proto_packages}" PARENT_SCOPE) +endfunction() + +function(_qt_internal_protobuf_package_qml_uri out_uri) + list(REMOVE_DUPLICATES ARGN) + list(LENGTH ARGN length) + if(NOT length EQUAL 1) + string(JOIN "\n" proto_packages_string "${ARGN}") + message(FATAL_ERROR "All *.proto files must have single package name," + " that will be used for QML plugin registration." + "\nThe following packages found in the .proto files for ${target}:" + "\n${proto_packages_string}." + " Please split the ${target} target per package." + ) + endif() + list(GET ARGN 0 qml_uri) + set(${out_uri} ${qml_uri} PARENT_SCOPE) endfunction() # TODO Qt6: @@ -296,25 +263,61 @@ function(qt6_add_protobuf target) else() set(base_dir "${CMAKE_CURRENT_SOURCE_DIR}") endif() - unset(extra_pre_parse_options) - if(arg_GENERATE_PACKAGE_SUBFOLDERS) - list(APPEND extra_pre_parse_options "GENERATE_PACKAGE_SUBFOLDERS") + + _qt_internal_protobuf_preparse_proto_files(${target} + "${base_dir}" + proto_files proto_includes proto_packages + ${arg_PROTO_FILES} + ) + + set(output_directory "${CMAKE_CURRENT_BINARY_DIR}") + if(DEFINED arg_OUTPUT_DIRECTORY) + set(output_directory "${arg_OUTPUT_DIRECTORY}") endif() + set(cpp_sources "") + set(idx 0) + foreach(f IN LISTS proto_files) + if(arg_GENERATE_PACKAGE_SUBFOLDERS) + if(proto_packages) + list(GET proto_packages ${idx} package) + else() + set(package "") + endif() + string(REPLACE "." "/" package_full_path "${package}/") + math(EXPR idx "${idx} + 1") + else() + set(package_full_path "") + endif() + + get_filename_component(basename "${f}" NAME_WLE) + list(APPEND cpp_sources + "${output_directory}/${package_full_path}${basename}.qpb.h" + "${output_directory}/${package_full_path}${basename}.qpb.cpp" + ) + + list(APPEND type_registrations + "${output_directory}/${package_full_path}${basename}_protobuftyperegistrations.cpp") + endforeach() + + set(qml_sources "") if(arg_QML) - set(arg_QML QML) - else() - set(arg_QML "") + if(arg_QML_URI) + set(qml_uri "${arg_QML_URI}") + elseif(proto_packages) + _qt_internal_protobuf_package_qml_uri(qml_uri ${proto_packages}) + else() + message(FATAL_ERROR ".proto files of ${target} don't specify a package." + " Please, set QML_URI when using .proto without package name." + ) + endif() + list(APPEND generation_options "QML_URI=${qml_uri}") + + string(REPLACE "." "_" qml_plugin_base_name "${qml_uri}") + list(APPEND qml_sources + "${output_directory}/${qml_plugin_base_name}plugin.cpp") endif() - _qt_internal_protobuf_preparse_proto_files(proto_files proto_includes generated_files qml_uri - "${base_dir}" - ${extra_pre_parse_options} - ${arg_QML} - QML_URI - ${arg_QML_URI} - PROTO_FILES - ${arg_PROTO_FILES} - ) + if(arg_PROTO_INCLUDES) list(APPEND proto_includes ${arg_PROTO_INCLUDES}) endif() @@ -351,40 +354,23 @@ function(qt6_add_protobuf target) PRIVATE "QT_BUILD_${target_upper}_LIB") endif() - if(qml_uri) - list(APPEND generation_options "QML_URI=${qml_uri}") - endif() - - set(output_directory "${CMAKE_CURRENT_BINARY_DIR}") - if(DEFINED arg_OUTPUT_DIRECTORY) - set(output_directory "${arg_OUTPUT_DIRECTORY}") - endif() - - list(TRANSFORM generated_files PREPEND "${output_directory}/") - _qt_internal_protoc_generate(${target} qtprotobufgen "${output_directory}" PROTO_FILES ${proto_files} PROTO_INCLUDES ${proto_includes} - GENERATED_FILES ${generated_files} + GENERATED_FILES ${cpp_sources} ${qml_sources} ${type_registrations} OPTIONS ${generation_options} ) # Filter generated headers unset(generated_headers) - unset(generated_sources) - unset(generated_typeregistrations) - foreach(generated_file IN LISTS generated_files) + foreach(generated_file IN LISTS cpp_sources) get_filename_component(extension "${generated_file}" LAST_EXT) if(extension STREQUAL ".h") list(APPEND generated_headers "${generated_file}") - elseif(generated_file MATCHES "_protobuftyperegistrations.cpp$") - list(APPEND generated_typeregistrations "${generated_file}") - elseif(extension STREQUAL ".cpp") - list(APPEND generated_sources "${generated_file}") endif() endforeach() - target_sources(${target} PRIVATE ${generated_headers} ${generated_sources}) + target_sources(${target} PRIVATE ${cpp_sources} ${qml_sources}) if(is_static OR is_shared) set_target_properties(${target} @@ -404,9 +390,9 @@ function(qt6_add_protobuf target) if(is_static OR (WIN32 AND NOT is_executable)) if(TARGET ${target}_protobuf_registration) - target_sources(${target}_protobuf_registration PRIVATE ${generated_typeregistrations}) + target_sources(${target}_protobuf_registration PRIVATE ${type_registrations}) else() - add_library(${target}_protobuf_registration OBJECT ${generated_typeregistrations}) + add_library(${target}_protobuf_registration OBJECT ${type_registrations}) target_link_libraries(${target} INTERFACE "$>") add_dependencies(${target} ${target}_protobuf_registration) @@ -430,7 +416,7 @@ function(qt6_add_protobuf target) "${target}_protobuf_registration") endif() else() - target_sources(${target} PRIVATE ${generated_typeregistrations}) + target_sources(${target} PRIVATE ${type_registrations}) endif() if(DEFINED arg_OUTPUT_HEADERS)