CMake: More fixes for QT_CMAKE_EXPORT_NAMESPACE not being available

The _qt_internal_write_qmldir_part function is
deferred to be called in the root CMAKE_BINARY_DIR, where
QT_CMAKE_EXPORT_NAMESPACE is not defined if find_package(Qt6) is not
called in the root of the project.

Make sure to pass the value of QT_CMAKE_EXPORT_NAMESPACE to the
function explicitly, from a scope where it is available.

This avoids errors like:
CMake Error at Qt6QmlMacros.cmake:155 (add_custom_command):
  Error evaluating generator expression:

    $<TARGET_FILE:::qmltyperegistrar>

  No target "::qmltyperegistrar"

As a drive by, do the same for _qt_internal_deferred_aotstats_setup
and use that variable, instead of the hardcoded Qt6:: prefix.

Add test.

Amends b47555feff
Amends f2889262c8

Pick-to: 6.8 6.9 6.10
Fixes: QTBUG-138559
Change-Id: I9ecf2149737f3522fa61b7188403c8470b5a15d3
Reviewed-by: Joerg Bornemann <joerg.bornemann@qt.io>
This commit is contained in:
Alexandru Croitor 2025-07-21 15:07:24 +02:00
parent e365250e9d
commit afe28ca1cb
11 changed files with 188 additions and 7 deletions

View File

@ -96,7 +96,14 @@ function(_qt_internal_parse_qml_module_dependency dependency was_marked_as_targe
endif()
endfunction()
function(_qt_internal_write_qmldir_part target)
function(_qt_internal_write_qmldir_part target qt_cmake_export_namespace)
# _qt_internal_write_qmldir_part is deferred to be called in the root
# CMAKE_BINARY_DIR. If find_package(Qt6) is not called in the root of the project (like in the
# Qt Creator super repo), QT_CMAKE_EXPORT_NAMESPACE will not be defined.
# Manually define it here, based on the value passed to the function, from a scope where it
# is available.
set(QT_CMAKE_EXPORT_NAMESPACE "${qt_cmake_export_namespace}")
set(effective_outdir $<TARGET_FILE_DIR:${target}>)
set(qtconf_file "${effective_outdir}/${target}_qt.part.conf")
get_target_property(dependency_targets "${target}" QT_QML_DEPENDENT_QML_MODULE_TARGETS)
@ -1124,7 +1131,8 @@ Check https://doc.qt.io/qt-6/qt-cmake-policy-qtp0001.html for policy details."
)
cmake_language(EVAL CODE "
cmake_language(DEFER DIRECTORY [[${PROJECT_SOURCE_DIR}]]
CALL _qt_internal_write_qmldir_part ${target})
CALL _qt_internal_write_qmldir_part \"${target}\"
\"${QT_CMAKE_EXPORT_NAMESPACE}\")
")
else()
# Before CMake 3.19, we don't have DEFER, so we immediately write out the qt.conf
@ -1168,12 +1176,20 @@ Check https://doc.qt.io/qt-6/qt-cmake-policy-qtp0001.html for policy details."
if(NOT aotstats_setup_called)
set_property(GLOBAL PROPERTY _qt_internal_deferred_aotstats_setup TRUE)
cmake_language(EVAL CODE "cmake_language(DEFER DIRECTORY \"${CMAKE_BINARY_DIR}\" "
"CALL _qt_internal_deferred_aotstats_setup)")
"CALL _qt_internal_deferred_aotstats_setup \"${QT_CMAKE_EXPORT_NAMESPACE}\"
)")
endif()
endif()
endfunction()
function(_qt_internal_deferred_aotstats_setup)
function(_qt_internal_deferred_aotstats_setup qt_cmake_export_namespace)
# _qt_internal_deferred_aotstats_setup is deferred to be called in the root
# CMAKE_BINARY_DIR. If find_package(Qt6) is not called in the root of the project (like in the
# Qt Creator super repo), QT_CMAKE_EXPORT_NAMESPACE will not be defined.
# Manually define it here, based on the value passed to the function, from a scope where it
# is available.
set(QT_CMAKE_EXPORT_NAMESPACE "${qt_cmake_export_namespace}")
get_property(module_targets GLOBAL PROPERTY _qt_qml_aotstats_module_targets)
set(onlybytecode_modules "")
@ -1206,7 +1222,7 @@ function(_qt_internal_deferred_aotstats_setup)
DEPENDS ${aotstats_files} ${module_aotstats_list_file}
COMMAND
${tool_wrapper}
$<TARGET_FILE:Qt6::qmlaotstats>
$<TARGET_FILE:${QT_CMAKE_EXPORT_NAMESPACE}::qmlaotstats>
aggregate
${module_aotstats_list_file}
${output}
@ -1259,13 +1275,13 @@ function(_qt_internal_deferred_aotstats_setup)
DEPENDS ${module_aotstats_targets} ${module_aotstats_files}
COMMAND
${tool_wrapper}
$<TARGET_FILE:Qt6::qmlaotstats>
$<TARGET_FILE:${QT_CMAKE_EXPORT_NAMESPACE}::qmlaotstats>
aggregate
"${aotstats_list_file}"
"${all_aotstats_file}"
COMMAND
${tool_wrapper}
$<TARGET_FILE:Qt6::qmlaotstats>
$<TARGET_FILE:${QT_CMAKE_EXPORT_NAMESPACE}::qmlaotstats>
format
"${all_aotstats_file}"
"${formatted_stats_file}"

View File

@ -5,6 +5,7 @@ _qt_internal_get_cmake_test_configure_options(config_flags)
if(NOT CMAKE_CROSSCOMPILING)
add_RunCMake_test(qt_target_qml_sources ${config_flags} "-D_Qt6CTestMacros=${_Qt6CTestMacros}")
endif()
add_RunCMake_test(export_namespace_in_root ${config_flags})
_qt_internal_get_cmake_test_configure_options(option_list)
add_RunCMake_test(generate_qmlls_ini ${option_list})

View File

@ -0,0 +1,5 @@
cmake_minimum_required(VERSION 3.16)
project(${RunCMake_TEST} LANGUAGES CXX)
add_subdirectory(Foo/Bar)
add_subdirectory(app)

View File

@ -0,0 +1,21 @@
find_package(Qt6 6.8 REQUIRED COMPONENTS Gui Quick REQUIRED)
qt_standard_project_setup(REQUIRES 6.8)
qt_add_qml_module(FooBar
URI Foo.Bar
VERSION 1.0
STATIC
SOURCES
foobar.h foobar.cpp
QML_FILES
FoobarQML.qml
IMPORTS
QtQuick
)
target_link_libraries(FooBar
PRIVATE
Qt6::Quick
Qt6::Gui
)

View File

@ -0,0 +1,13 @@
// Copyright (C) 2025 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
Item {
id: root
Rectangle {
anchors.fill: root
color: "green"
}
}

View File

@ -0,0 +1,16 @@
// Copyright (C) 2025 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include "foobar.h"
#include <QPainter>
void Foobar::paint(QPainter *painter)
{
QPen pen(QColorConstants::Red, 2);
QBrush brush(QColorConstants::Red);
painter->setPen(pen);
painter->setBrush(brush);
painter->drawRect(0, 0, 100, 100);
}

View File

@ -0,0 +1,16 @@
// Copyright (C) 2025 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#pragma once
#include <QtQuick/QQuickPaintedItem>
class Foobar : public QQuickPaintedItem
{
Q_OBJECT
QML_ELEMENT
public:
using QQuickPaintedItem::QQuickPaintedItem;
void paint(QPainter *painter) override;
};

View File

@ -0,0 +1,24 @@
include(RunCMake)
include(${_Qt6CTestMacros})
# Stub function to make `_qt_internal_get_cmake_test_configure_options` work
function(_qt_internal_get_build_vars_for_external_projects)
endfunction()
_qt_internal_get_cmake_test_configure_options(config_flags)
list(APPEND config_flags "-DCMAKE_PREFIX_PATH=${CMAKE_PREFIX_PATH}")
function(run_cmake_and_build case)
# Set common build directory for configure and build
set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/${case}-build)
run_cmake_with_options(${case} ${config_flags})
# Do not remove the current RunCMake_TEST_BINARY_DIR
set(RunCMake_TEST_NO_CLEAN 1)
run_cmake_command(${case}-build ${CMAKE_COMMAND} --build .)
endfunction()
if(CMAKE_VERSION VERSION_GREATER_EQUAL "3.19"
AND RunCMake_GENERATOR MATCHES "^Ninja"
)
run_cmake_and_build(export_namespace_in_root)
endif()

View File

@ -0,0 +1,18 @@
find_package(Qt6 6.8 COMPONENTS Quick REQUIRED)
qt_standard_project_setup(REQUIRES 6.8)
qt_add_executable(app app.cpp)
qt_add_qml_module(app
URI ExampleProjectApp
VERSION 1.0
QML_FILES Main.qml
DEPENDENCIES
TARGET FooBar
)
target_link_libraries(app
PRIVATE
Qt6::Quick
FooBarplugin
)

View File

@ -0,0 +1,29 @@
// Copyright (C) 2025 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import Foo.Bar
Window {
width: 640
height: 400
visible: true
title: qsTr("Example Project")
Foobar {
id: controls
anchors.left: parent.left
anchors.top: parent.top
anchors.margins: 20
width: 100
height: 100
}
FoobarQML {
id: rect
anchors.left: controls.right
anchors.top: parent.top
anchors.margins: 20
width: 100
height: 100
}
}

View File

@ -0,0 +1,22 @@
// Copyright (C) 2025 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QDebug>
#include <QDirIterator>
int main(int argc, char *argv[])
{
QGuiApplication app(argc, argv);
QDirIterator dirIterator(":/", QDirIterator::Subdirectories);
while (dirIterator.hasNext()) {
qDebug() << dirIterator.next();
}
QQmlApplicationEngine engine;
engine.loadFromModule("ExampleProjectApp", "Main");
return app.exec();
}