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 <leander.beernaert@qt.io>
Reviewed-by: Alexandru Croitor <alexandru.croitor@qt.io>
This commit is contained in:
Alexandru Croitor 2020-01-31 22:21:09 +01:00
parent 4ddd4bf1aa
commit c5b61d2e90
2 changed files with 59 additions and 14 deletions

View File

@ -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_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_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) 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}\".") message(FATAL_ERROR "Trying to extend non-existing target \"${target}\".")
endif() endif()
qt_parse_all_arguments(arg "qt_extend_target" "HEADER_MODULE" "PRECOMPILED_HEADER" 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) if ("x${arg_CONDITION}" STREQUAL x)
set(arg_CONDITION ON) set(arg_CONDITION ON)
endif() endif()
@ -1193,7 +1193,8 @@ function(qt_extend_target target)
set(target_private "${target}Private") set(target_private "${target}Private")
if(TARGET "${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() endif()
qt_register_target_dependencies("${target}" qt_register_target_dependencies("${target}"
"${arg_PUBLIC_LIBRARIES}" "${arg_PUBLIC_LIBRARIES}"
@ -1533,7 +1534,7 @@ function(qt_add_module target)
qt_parse_all_arguments(arg "qt_add_module" 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" "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" "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) if(NOT DEFINED arg_CONFIG_MODULE_NAME)
set(arg_CONFIG_MODULE_NAME "${module_lower}") set(arg_CONFIG_MODULE_NAME "${module_lower}")
@ -1751,6 +1752,7 @@ function(qt_add_module target)
"${deprecation_define}" "${deprecation_define}"
PUBLIC_LIBRARIES ${arg_PUBLIC_LIBRARIES} PUBLIC_LIBRARIES ${arg_PUBLIC_LIBRARIES}
LIBRARIES ${arg_LIBRARIES} Qt::PlatformModuleInternal LIBRARIES ${arg_LIBRARIES} Qt::PlatformModuleInternal
PRIVATE_MODULE_INTERFACE ${arg_PRIVATE_MODULE_INTERFACE}
FEATURE_DEPENDENCIES ${arg_FEATURE_DEPENDENCIES} FEATURE_DEPENDENCIES ${arg_FEATURE_DEPENDENCIES}
DBUS_ADAPTOR_SOURCES ${arg_DBUS_ADAPTOR_SOURCES} DBUS_ADAPTOR_SOURCES ${arg_DBUS_ADAPTOR_SOURCES}
DBUS_ADAPTOR_FLAGS ${arg_DBUS_ADAPTOR_FLAGS} DBUS_ADAPTOR_FLAGS ${arg_DBUS_ADAPTOR_FLAGS}

View File

@ -904,6 +904,8 @@ class Scope(object):
self._visited_keys = set() # type: Set[str] self._visited_keys = set() # type: Set[str]
self._total_condition = None # type: Optional[str] self._total_condition = None # type: Optional[str]
self._parent_include_line_no = parent_include_line_no self._parent_include_line_no = parent_include_line_no
self._is_public_module = False
self._has_private_module = False
def __repr__(self): def __repr__(self):
return ( return (
@ -935,6 +937,14 @@ class Scope(object):
def currentdir(self) -> str: def currentdir(self) -> str:
return self._currentdir 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): def can_merge_condition(self):
if self._condition == "else": if self._condition == "else":
return False return False
@ -1826,16 +1836,9 @@ def extract_cmake_libraries(
private_dependencies += [map_qt_library(q) for q in scope.expand(key)] private_dependencies += [map_qt_library(q) for q in scope.expand(key)]
for key in ["QT"]: 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): for lib in scope.expand(key):
mapped_lib = map_qt_library(lib) mapped_lib = map_qt_library(lib)
public_dependencies.append(mapped_lib)
if mapped_lib.endswith("Private"):
private_dependencies.append(mapped_lib)
public_dependencies.append(mapped_lib[:-7])
else:
public_dependencies.append(mapped_lib)
return ( return (
_map_libraries_to_cmake(public_dependencies, known_libraries, is_example=is_example), _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 scope, known_libraries=known_libraries
) )
write_list(cm_fh, private_dependencies, "LIBRARIES", indent + 1) is_public_module = scope.is_public_module
write_list(cm_fh, public_dependencies, "PUBLIC_LIBRARIES", indent + 1) 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): 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. # 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_static = "static" in scope.get("CONFIG") or "host_build" in scope.get("_OPTION")
is_public_module = True
if is_static: if is_static:
extra.append("STATIC") extra.append("STATIC")
if "internal_module" in scope.get("CONFIG"): if "internal_module" in scope.get("CONFIG"):
is_public_module = False
extra.append("INTERNAL_MODULE") extra.append("INTERNAL_MODULE")
if "no_module_headers" in scope.get("CONFIG"): if "no_module_headers" in scope.get("CONFIG"):
extra.append("NO_MODULE_HEADERS") 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") extra.append("NO_SYNC_QT")
if "no_private_module" in scope.get("CONFIG"): if "no_private_module" in scope.get("CONFIG"):
extra.append("NO_PRIVATE_MODULE") extra.append("NO_PRIVATE_MODULE")
else:
scope._has_private_module = True
if "header_module" in scope.get("CONFIG"): if "header_module" in scope.get("CONFIG"):
extra.append("HEADER_MODULE") extra.append("HEADER_MODULE")
if "metatypes" in scope.get("CONFIG") or "qmltypes" in scope.get("CONFIG"): 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: if module_plugin_types:
extra.append(f"PLUGIN_TYPES {' '.join(module_plugin_types)}") extra.append(f"PLUGIN_TYPES {' '.join(module_plugin_types)}")
scope._is_public_module = is_public_module
target_name = module_name[2:] target_name = module_name[2:]
write_main_part( write_main_part(
cm_fh, cm_fh,