From c5b61d2e90fde5d9f2e968f4443ca393e52393f5 Mon Sep 17 00:00:00 2001 From: Alexandru Croitor Date: Fri, 31 Jan 2020 22:21:09 +0100 Subject: pro2cmake: Handle QT += core-private correctly for modules If a module project (Quick) contains QT += core-private, the qmake semantics translated to CMake would mean the following: target_link_libraries(Quick PUBLIC Core) target_link_libraries(Quick PRIVATE CorePrivate) target_link_libraries(QuickPrivate INTERFACE CorePrivate) Whereas a QT_PRIVATE += core-private only means target_link_libraries(Quick PRIVATE CorePrivate) without adding any public dependencies to QuickPrivate. To achieve that, we need a few modifications to both pro2cmake and QtBuild.cmake - pro2cmake doesn't automagically add public and private dependencies to targets when encountering a private module assigned to QT. Instead it generates the logic described above by passing correct LIBRARIES, PUBLIC_LIBRARIES, and PRIVATE_MODULE_INTERFACE values. - pro2cmake doesn't do any dependency magic for non-module targets anymore, like executables, plugins, internal_modules. This means that QT assignments are now regular public dependencies. - qt_add_module and qt_extend_target now accept a new PRIVATE_MODULE_INTERFACE option. - qt_extend_target does not automagically make private modules be public dependencies on other private modules. - qt_extend_target correctly assigns PRIVATE_MODULE_INTERFACE values to Private module only. For other target types, it's a no-op. The change requires regeneration of all projects. When we fix pro2cmake and QtBuild.cmake to properly handle internal_modules (create only Private modules without creating a non-Private counter part), we will need another project regeneration to correctly assign dependencies. Change-Id: I4c21f26b3ef3b2a4ed208b58bccb65a5b7312f81 Task-number: QTBUG-81780 Reviewed-by: Leander Beernaert Reviewed-by: Alexandru Croitor --- cmake/QtBuild.cmake | 10 ++++---- util/cmake/pro2cmake.py | 63 +++++++++++++++++++++++++++++++++++++++++-------- 2 files changed, 59 insertions(+), 14 deletions(-) diff --git a/cmake/QtBuild.cmake b/cmake/QtBuild.cmake index d27d2aae46..5246f533f5 100644 --- a/cmake/QtBuild.cmake +++ b/cmake/QtBuild.cmake @@ -946,7 +946,7 @@ endfunction() set(__default_private_args "SOURCES;LIBRARIES;INCLUDE_DIRECTORIES;DEFINES;DBUS_ADAPTOR_BASENAME;DBUS_ADAPTOR_FLAGS;DBUS_ADAPTOR_SOURCES;DBUS_INTERFACE_BASENAME;DBUS_INTERFACE_FLAGS;DBUS_INTERFACE_SOURCES;FEATURE_DEPENDENCIES;COMPILE_OPTIONS;LINK_OPTIONS;MOC_OPTIONS;DISABLE_AUTOGEN_TOOLS;ENABLE_AUTOGEN_TOOLS;PLUGIN_TYPES") set(__default_public_args "PUBLIC_LIBRARIES;PUBLIC_INCLUDE_DIRECTORIES;PUBLIC_DEFINES;PUBLIC_COMPILE_OPTIONS;PUBLIC_LINK_OPTIONS") - +set(__default_private_module_args "PRIVATE_MODULE_INTERFACE") option(QT_CMAKE_DEBUG_EXTEND_TARGET "Debug extend_target calls in Qt's build system" OFF) @@ -1085,7 +1085,7 @@ function(qt_extend_target target) message(FATAL_ERROR "Trying to extend non-existing target \"${target}\".") endif() qt_parse_all_arguments(arg "qt_extend_target" "HEADER_MODULE" "PRECOMPILED_HEADER" - "CONDITION;${__default_public_args};${__default_private_args};COMPILE_FLAGS;NO_PCH_SOURCES" ${ARGN}) + "CONDITION;${__default_public_args};${__default_private_args};${__default_private_module_args};COMPILE_FLAGS;NO_PCH_SOURCES" ${ARGN}) if ("x${arg_CONDITION}" STREQUAL x) set(arg_CONDITION ON) endif() @@ -1193,7 +1193,8 @@ function(qt_extend_target target) set(target_private "${target}Private") if(TARGET "${target_private}") - target_link_libraries("${target_private}" INTERFACE "${target}" "${qt_libs_private}") + target_link_libraries("${target_private}" + INTERFACE "${target}" ${arg_PRIVATE_MODULE_INTERFACE}) endif() qt_register_target_dependencies("${target}" "${arg_PUBLIC_LIBRARIES}" @@ -1533,7 +1534,7 @@ function(qt_add_module target) qt_parse_all_arguments(arg "qt_add_module" "NO_MODULE_HEADERS;STATIC;DISABLE_TOOLS_EXPORT;EXCEPTIONS;INTERNAL_MODULE;NO_SYNC_QT;NO_PRIVATE_MODULE;HEADER_MODULE;GENERATE_METATYPES" "CONFIG_MODULE_NAME;PRECOMPILED_HEADER" - "${__default_private_args};${__default_public_args};QMAKE_MODULE_CONFIG;EXTRA_CMAKE_FILES;EXTRA_CMAKE_INCLUDES;NO_PCH_SOURCES" ${ARGN}) + "${__default_private_args};${__default_public_args};${__default_private_module_args};QMAKE_MODULE_CONFIG;EXTRA_CMAKE_FILES;EXTRA_CMAKE_INCLUDES;NO_PCH_SOURCES" ${ARGN}) if(NOT DEFINED arg_CONFIG_MODULE_NAME) set(arg_CONFIG_MODULE_NAME "${module_lower}") @@ -1751,6 +1752,7 @@ function(qt_add_module target) "${deprecation_define}" PUBLIC_LIBRARIES ${arg_PUBLIC_LIBRARIES} LIBRARIES ${arg_LIBRARIES} Qt::PlatformModuleInternal + PRIVATE_MODULE_INTERFACE ${arg_PRIVATE_MODULE_INTERFACE} FEATURE_DEPENDENCIES ${arg_FEATURE_DEPENDENCIES} DBUS_ADAPTOR_SOURCES ${arg_DBUS_ADAPTOR_SOURCES} DBUS_ADAPTOR_FLAGS ${arg_DBUS_ADAPTOR_FLAGS} diff --git a/util/cmake/pro2cmake.py b/util/cmake/pro2cmake.py index 04443b00b7..9d01dd73e2 100755 --- a/util/cmake/pro2cmake.py +++ b/util/cmake/pro2cmake.py @@ -904,6 +904,8 @@ class Scope(object): self._visited_keys = set() # type: Set[str] self._total_condition = None # type: Optional[str] self._parent_include_line_no = parent_include_line_no + self._is_public_module = False + self._has_private_module = False def __repr__(self): return ( @@ -935,6 +937,14 @@ class Scope(object): def currentdir(self) -> str: return self._currentdir + @property + def is_public_module(self) -> bool: + return self._is_public_module + + @property + def has_private_module(self) -> bool: + return self._has_private_module + def can_merge_condition(self): if self._condition == "else": return False @@ -1826,16 +1836,9 @@ def extract_cmake_libraries( private_dependencies += [map_qt_library(q) for q in scope.expand(key)] for key in ["QT"]: - # Qt public libs: These may include FooPrivate in which case we get - # a private dependency on FooPrivate as well as a public dependency on Foo for lib in scope.expand(key): mapped_lib = map_qt_library(lib) - - if mapped_lib.endswith("Private"): - private_dependencies.append(mapped_lib) - public_dependencies.append(mapped_lib[:-7]) - else: - public_dependencies.append(mapped_lib) + public_dependencies.append(mapped_lib) return ( _map_libraries_to_cmake(public_dependencies, known_libraries, is_example=is_example), @@ -1972,8 +1975,41 @@ def write_library_section( scope, known_libraries=known_libraries ) - write_list(cm_fh, private_dependencies, "LIBRARIES", indent + 1) - write_list(cm_fh, public_dependencies, "PUBLIC_LIBRARIES", indent + 1) + is_public_module = scope.is_public_module + current_scope = scope + while not is_public_module and current_scope.parent: + current_scope = current_scope.parent + is_public_module = current_scope.is_public_module + + # When handling module dependencies, handle QT += foo-private magic. + # This implies: + # target_link_libraries(Module PUBLIC Qt::Foo) + # target_link_libraries(Module PRIVATE Qt::FooPrivate) + # target_link_libraries(ModulePrivate INTERFACE Qt::FooPrivate) + if is_public_module: + private_module_dep_pattern = re.compile(r"^(Qt::(.+))Private$") + + public_module_public_deps = [] + public_module_private_deps = private_dependencies + private_module_interface_deps = [] + + for dep in public_dependencies: + match = re.match(private_module_dep_pattern, dep) + if match: + if match[1] not in public_module_public_deps: + public_module_public_deps.append(match[1]) + private_module_interface_deps.append(dep) + if dep not in public_module_private_deps: + public_module_private_deps.append(dep) + else: + if dep not in public_module_public_deps: + public_module_public_deps.append(dep) + write_list(cm_fh, public_module_private_deps, "LIBRARIES", indent + 1) + write_list(cm_fh, public_module_public_deps, "PUBLIC_LIBRARIES", indent + 1) + write_list(cm_fh, private_module_interface_deps, "PRIVATE_MODULE_INTERFACE", indent + 1) + else: + write_list(cm_fh, private_dependencies, "LIBRARIES", indent + 1) + write_list(cm_fh, public_dependencies, "PUBLIC_LIBRARIES", indent + 1) def write_autogen_section(cm_fh: IO[str], scope: Scope, *, indent: int = 0): @@ -2827,9 +2863,12 @@ def write_module(cm_fh: IO[str], scope: Scope, *, indent: int = 0) -> str: # or when option(host_build) is used, as described in qt_module.prf. is_static = "static" in scope.get("CONFIG") or "host_build" in scope.get("_OPTION") + is_public_module = True + if is_static: extra.append("STATIC") if "internal_module" in scope.get("CONFIG"): + is_public_module = False extra.append("INTERNAL_MODULE") if "no_module_headers" in scope.get("CONFIG"): extra.append("NO_MODULE_HEADERS") @@ -2837,6 +2876,8 @@ def write_module(cm_fh: IO[str], scope: Scope, *, indent: int = 0) -> str: extra.append("NO_SYNC_QT") if "no_private_module" in scope.get("CONFIG"): extra.append("NO_PRIVATE_MODULE") + else: + scope._has_private_module = True if "header_module" in scope.get("CONFIG"): extra.append("HEADER_MODULE") if "metatypes" in scope.get("CONFIG") or "qmltypes" in scope.get("CONFIG"): @@ -2850,6 +2891,8 @@ def write_module(cm_fh: IO[str], scope: Scope, *, indent: int = 0) -> str: if module_plugin_types: extra.append(f"PLUGIN_TYPES {' '.join(module_plugin_types)}") + scope._is_public_module = is_public_module + target_name = module_name[2:] write_main_part( cm_fh, -- cgit v1.2.3