aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSami Shalayel <sami.shalayel@qt.io>2022-12-22 17:24:16 +0100
committerSami Shalayel <sami.shalayel@qt.io>2023-02-02 18:11:07 +0100
commit01d84ffc74d4328240e4f242f35f34c2164dbbca (patch)
tree36fa3292d150277179ea5492c589926cb6a8501d
parent55f3bf8882fc8eeb132b4d2af9158bd1fee6bd78 (diff)
qmltc: export generated classes
Add QMLTC_EXPORT_MACRO_NAME and QMLTC_EXPORT_FILE_NAME arguments to qt_add_qml_module() that allows the user to export qmltc-generated code from its library. The qmltc-generated code will include the header-file specified in QMLTC_EXPORT_FILE_NAME and will be exported with the macro specified by QMLTC_EXPORT_MACRO_NAME. Leave both options unspecified to not export the code generated by qmltc. Describe the options in the documentation and write a test to see if the class really has an export macro in the generated code: 1) tst_qmltc_qprocess will test if the macro and the header are correctly inserted in the generated code. 2) tst_qmltc_{no,}diskcache will test if the generated code can still be used from a static library. Fixes: QTBUG-106840 Task-number: QTBUG-96040 Change-Id: I554f03bcdf043e8114e42f51a7289a5c00de4f89 Reviewed-by: Ulf Hermann <ulf.hermann@qt.io>
-rw-r--r--src/qml/Qt6QmlMacros.cmake41
-rw-r--r--src/qml/doc/src/cmake/qt_add_qml_module.qdoc14
-rw-r--r--tests/auto/qml/qmltc/CMakeLists.txt3
-rw-r--r--tests/auto/qml/qmltc/QmltcExportedTests/CMakeLists.txt30
-rw-r--r--tests/auto/qml/qmltc/QmltcExportedTests/HelloExportedWorld.qml5
-rw-r--r--tests/auto/qml/qmltc/tst_qmltc.cpp8
-rw-r--r--tests/auto/qml/qmltc/tst_qmltc.h1
-rw-r--r--tests/auto/qml/qmltc_qprocess/tst_qmltc_qprocess.cpp34
-rw-r--r--tools/qmltc/main.cpp14
-rw-r--r--tools/qmltc/qmltccodewriter.cpp12
-rw-r--r--tools/qmltc/qmltccodewriter.h2
-rw-r--r--tools/qmltc/qmltccompiler.cpp3
-rw-r--r--tools/qmltc/qmltccompiler.h2
-rw-r--r--tools/qmltc/qmltcoutputir.h2
14 files changed, 160 insertions, 11 deletions
diff --git a/src/qml/Qt6QmlMacros.cmake b/src/qml/Qt6QmlMacros.cmake
index 6b3e8ffb08..70f33d0749 100644
--- a/src/qml/Qt6QmlMacros.cmake
+++ b/src/qml/Qt6QmlMacros.cmake
@@ -58,6 +58,8 @@ function(qt6_add_qml_module target)
INSTALL_DIRECTORY
INSTALL_LOCATION
TYPE_COMPILER_NAMESPACE
+ QMLTC_EXPORT_DIRECTIVE
+ QMLTC_EXPORT_FILE_NAME
)
set(args_multi
@@ -147,11 +149,32 @@ function(qt6_add_qml_module target)
)
endif()
- if (DEFINED arg_TYPE_COMPILER_NAMESPACE AND NOT arg_ENABLE_TYPE_COMPILER)
- message(WARNING
- "TYPE_COMPILER_NAMESPACE is set, but ENABLE_TYPE_COMPILER is not specified. "
- "The TYPE_COMPILER_NAMESPACE value will be ignored."
- )
+ if (NOT arg_ENABLE_TYPE_COMPILER)
+ if (DEFINED arg_TYPE_COMPILER_NAMESPACE)
+ message(WARNING
+ "TYPE_COMPILER_NAMESPACE is set, but ENABLE_TYPE_COMPILER is not specified. "
+ "The TYPE_COMPILER_NAMESPACE value will be ignored."
+ )
+ endif()
+
+ if (DEFINED arg_QMLTC_EXPORT_DIRECTIVE)
+ message(WARNING
+ "QMLTC_EXPORT_DIRECTIVE is set, but ENABLE_TYPE_COMPILER is not specified. "
+ "The QMLTC_EXPORT_DIRECTIVE value will be ignored."
+ )
+ endif()
+ if (DEFINED arg_QMLTC_EXPORT_FILE_NAME)
+ message(WARNING
+ "QMLTC_EXPORT_FILE_NAME is set, but ENABLE_TYPE_COMPILER is not specified. "
+ "The QMLTC_EXPORT_FILE_NAME will be ignored."
+ )
+ endif()
+ else()
+ if ((DEFINED arg_QMLTC_EXPORT_FILE_NAME) AND (NOT (DEFINED arg_QMLTC_EXPORT_DIRECTIVE)))
+ message(FATAL_ERROR
+ "Specifying a value for QMLTC_EXPORT_FILE_NAME also requires one for QMLTC_EXPORT_DIRECTIVE."
+ )
+ endif()
endif()
set(is_executable FALSE)
@@ -670,6 +693,8 @@ Check https://doc.qt.io/qt-6/qt-cmake-policy-qtp0001.html for policy details."
QML_FILES ${arg_QML_FILES}
IMPORT_PATHS ${arg_IMPORT_PATH}
NAMESPACE ${qmltc_namespace}
+ EXPORT_MACRO_NAME ${arg_QMLTC_EXPORT_DIRECTIVE}
+ EXPORT_FILE_NAME ${arg_QMLTC_EXPORT_FILE_NAME}
)
endif()
@@ -1243,7 +1268,7 @@ endfunction()
# Compile Qml files (.qml) to C++ source files with Qml Type Compiler (qmltc).
function(_qt_internal_target_enable_qmltc target)
set(args_option "")
- set(args_single NAMESPACE)
+ set(args_single NAMESPACE EXPORT_MACRO_NAME EXPORT_FILE_NAME)
set(args_multi QML_FILES IMPORT_PATHS)
cmake_parse_arguments(PARSE_ARGV 1 arg
@@ -1276,6 +1301,10 @@ function(_qt_internal_target_enable_qmltc target)
if(arg_NAMESPACE)
list(APPEND common_args --namespace "${arg_NAMESPACE}")
endif()
+ if(arg_EXPORT_MACRO_NAME)
+ list(APPEND common_args --export "${arg_EXPORT_MACRO_NAME}")
+ list(APPEND common_args --exportInclude "${arg_EXPORT_FILE_NAME}")
+ endif()
get_target_property(output_dir ${target} QT_QML_MODULE_OUTPUT_DIRECTORY)
set(qmldir_file ${output_dir}/qmldir)
diff --git a/src/qml/doc/src/cmake/qt_add_qml_module.qdoc b/src/qml/doc/src/cmake/qt_add_qml_module.qdoc
index 857779af2d..e7572163f0 100644
--- a/src/qml/doc/src/cmake/qt_add_qml_module.qdoc
+++ b/src/qml/doc/src/cmake/qt_add_qml_module.qdoc
@@ -50,6 +50,9 @@ qt_add_qml_module(
[NO_IMPORT_SCAN]
[ENABLE_TYPE_COMPILER]
[TYPE_COMPILER_NAMESPACE namespace]
+ [QMLTC_EXPORT_DIRECTIVE export_macro]
+ [QMLTC_EXPORT_FILE_NAME header_defining_export_macro]
+
)
\endcode
@@ -691,4 +694,15 @@ can be put instead in a custom namespace, where different subnamespaces are to
be separated by a "::", e.g. "MyNamespace::MySubnamespace" for the namespace MySubnamespace that
is inside the MyNamespace. Apart from the "::", C++ namespace naming rules
apply.
+
+\c QMLTC_QMLTC_EXPORT_DIRECTIVE should be used with \c QMLTC_EXPORT_FILE_NAME when
+the classes generated by \l{QML Type Compiler}{qmltc} should be exported from
+the qml library. By default, classes generated by qmltc are not exported from
+their library.
+The header defining the export macro for the current library
+can be specified as an optional argument to \c QMLTC_EXPORT_FILE_NAME while the
+exporting macro name should be specified as an argument to
+\c QMLTC_QMLTC_EXPORT_DIRECTIVE. If no additional include is required or wanted,
+e.g. when the header of the export macro is already indirectly included by a base
+class, then the \c QMLTC_EXPORT_FILE_NAME option can be left out.
*/
diff --git a/tests/auto/qml/qmltc/CMakeLists.txt b/tests/auto/qml/qmltc/CMakeLists.txt
index 4b86a6c018..1b7fbf6ee1 100644
--- a/tests/auto/qml/qmltc/CMakeLists.txt
+++ b/tests/auto/qml/qmltc/CMakeLists.txt
@@ -3,6 +3,7 @@
add_subdirectory(QmltcTests)
add_subdirectory(NamespaceTest/Subfolder)
+add_subdirectory(QmltcExportedTests)
set(test_sources
nameconflict.h nameconflict.cpp
@@ -18,6 +19,8 @@ set(qmltc_module_libs
# automatic type registration that comes from the plugin)
qmltc_test_moduleplugin
qmltc_namespace_test_module
+ qmltc_exported_tests_module
+ qmltc_exported_tests_moduleplugin
)
qt_internal_add_test(tst_qmltc_diskcache
SOURCES ${test_sources}
diff --git a/tests/auto/qml/qmltc/QmltcExportedTests/CMakeLists.txt b/tests/auto/qml/qmltc/QmltcExportedTests/CMakeLists.txt
new file mode 100644
index 0000000000..1e1454baff
--- /dev/null
+++ b/tests/auto/qml/qmltc/QmltcExportedTests/CMakeLists.txt
@@ -0,0 +1,30 @@
+# Copyright (C) 2023 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
+qt_add_library(qmltc_exported_tests_module STATIC)
+qt_autogen_tools_initial_setup(qmltc_exported_tests_module)
+
+include(GenerateExportHeader)
+generate_export_header(qmltc_exported_tests_module)
+
+set(common_libraries
+ Qt::QuickPrivate
+)
+
+target_link_libraries(qmltc_exported_tests_module PUBLIC ${common_libraries})
+
+qt6_add_qml_module(qmltc_exported_tests_module
+ URI QmltcExportedTests
+ AUTO_RESOURCE_PREFIX
+ QML_FILES
+ HelloExportedWorld.qml
+ DEPENDENCIES
+ Qt::Quick
+ ENABLE_TYPE_COMPILER
+ QMLTC_EXPORT_DIRECTIVE "QMLTC_EXPORTED_TESTS_MODULE_EXPORT"
+ QMLTC_EXPORT_FILE_NAME "qmltc_exported_tests_module_export.h"
+)
+
+target_include_directories(qmltc_exported_tests_module PUBLIC ${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_CURRENT_SOURCE_DIR})
+
+qt_autogen_tools_initial_setup(qmltc_exported_tests_moduleplugin)
diff --git a/tests/auto/qml/qmltc/QmltcExportedTests/HelloExportedWorld.qml b/tests/auto/qml/qmltc/QmltcExportedTests/HelloExportedWorld.qml
new file mode 100644
index 0000000000..5e6886bced
--- /dev/null
+++ b/tests/auto/qml/qmltc/QmltcExportedTests/HelloExportedWorld.qml
@@ -0,0 +1,5 @@
+import QtQuick
+
+Item {
+ property string myString: "Hello! I should be exported by qmltc"
+}
diff --git a/tests/auto/qml/qmltc/tst_qmltc.cpp b/tests/auto/qml/qmltc/tst_qmltc.cpp
index 1fa4030642..381701991e 100644
--- a/tests/auto/qml/qmltc/tst_qmltc.cpp
+++ b/tests/auto/qml/qmltc/tst_qmltc.cpp
@@ -77,6 +77,7 @@
#include "repeatercrash.h"
#include "aliases.h"
#include "inlinecomponentsfromdifferentfiles.h"
+#include "helloexportedworld.h"
#include "testprivateproperty.h"
#include "singletons.h"
@@ -3208,4 +3209,11 @@ void tst_qmltc::namespacedName()
Q_UNUSED(t);
}
+void tst_qmltc::checkExportsAreCompiling()
+{
+ QQmlEngine e;
+ QmltcExportedTests::HelloExportedWorld w(&e);
+ QCOMPARE(w.myString(), u"Hello! I should be exported by qmltc"_s);
+}
+
QTEST_MAIN(tst_qmltc)
diff --git a/tests/auto/qml/qmltc/tst_qmltc.h b/tests/auto/qml/qmltc/tst_qmltc.h
index af084dcc01..f00a172522 100644
--- a/tests/auto/qml/qmltc/tst_qmltc.h
+++ b/tests/auto/qml/qmltc/tst_qmltc.h
@@ -93,4 +93,5 @@ private slots:
void constSignalParameters();
void cppNamespaces();
void namespacedName();
+ void checkExportsAreCompiling();
};
diff --git a/tests/auto/qml/qmltc_qprocess/tst_qmltc_qprocess.cpp b/tests/auto/qml/qmltc_qprocess/tst_qmltc_qprocess.cpp
index 7f12f29342..f0b99fcdf0 100644
--- a/tests/auto/qml/qmltc_qprocess/tst_qmltc_qprocess.cpp
+++ b/tests/auto/qml/qmltc_qprocess/tst_qmltc_qprocess.cpp
@@ -51,6 +51,7 @@ private slots:
void topLevelComponent();
void dashesInFilename();
void invalidSignalHandlers();
+ void exports();
};
#ifndef TST_QMLTC_QPROCESS_RESOURCES
@@ -261,5 +262,38 @@ void tst_qmltc_qprocess::invalidSignalHandlers()
}
}
+static QString fileToString(const QString &path)
+{
+ QFile f(path);
+ if (f.open(QIODevice::ReadOnly))
+ return QString::fromLatin1(f.readAll());
+ return QString();
+}
+
+void tst_qmltc_qprocess::exports()
+{
+ const QString fileName = u"dummy.qml"_s;
+ QStringList extraArgs;
+ extraArgs << "--export"
+ << "MYLIB_EXPORT_MACRO"
+ << "--exportInclude"
+ << "exportheader.h";
+ const auto errors = runQmltc(fileName, true, extraArgs);
+
+ const QString headerName = m_tmpPath + u"/"_s + QFileInfo(fileName).baseName() + u".h"_s;
+ const QString header = fileToString(headerName);
+ const QString implementationName =
+ m_tmpPath + u"/"_s + QFileInfo(fileName).baseName() + u".cpp"_s;
+ const QString implementation = fileToString(implementationName);
+
+ QCOMPARE(errors.size(), 0);
+
+ QVERIFY(header.contains(u"class MYLIB_EXPORT_MACRO dummy : public QObject\n"_s));
+ QVERIFY(!implementation.contains(u"MYLIB_EXPORT_MACRO"_s));
+
+ QVERIFY(header.contains(u"#include \"exportheader.h\"\n"_s));
+ QVERIFY(!implementation.contains(u"exportheader.h"_s));
+}
+
QTEST_MAIN(tst_qmltc_qprocess)
#include "tst_qmltc_qprocess.moc"
diff --git a/tools/qmltc/main.cpp b/tools/qmltc/main.cpp
index 6ef0afe105..65560c70e1 100644
--- a/tools/qmltc/main.cpp
+++ b/tools/qmltc/main.cpp
@@ -99,6 +99,18 @@ int main(int argc, char **argv)
QCoreApplication::translate("main", "namespace")
};
parser.addOption(namespaceOption);
+ QCommandLineOption exportOption{ u"export"_s,
+ QCoreApplication::translate(
+ "main", "Export macro used in the generated C++ code"),
+ QCoreApplication::translate("main", "export") };
+ parser.addOption(exportOption);
+ QCommandLineOption exportIncludeOption{
+ u"exportInclude"_s,
+ QCoreApplication::translate(
+ "main", "Header defining the export macro to be used in the generated C++ code"),
+ QCoreApplication::translate("main", "exportInclude")
+ };
+ parser.addOption(exportIncludeOption);
parser.process(app);
@@ -224,6 +236,8 @@ int main(int argc, char **argv)
info.outputHFile = parser.value(outputHOption);
info.resourcePath = firstQml(paths);
info.outputNamespace = parser.value(namespaceOption);
+ info.exportMacro = parser.value(exportOption);
+ info.exportInclude = parser.value(exportIncludeOption);
if (info.outputCppFile.isEmpty()) {
fprintf(stderr, "An output C++ file is required. Pass one using --impl");
diff --git a/tools/qmltc/qmltccodewriter.cpp b/tools/qmltc/qmltccodewriter.cpp
index 8010f1066c..6996277473 100644
--- a/tools/qmltc/qmltccodewriter.cpp
+++ b/tools/qmltc/qmltccodewriter.cpp
@@ -209,7 +209,7 @@ void QmltcCodeWriter::write(QmltcOutputWrapper &code, const QmltcProgram &progra
code.rawAppendToHeader(u"class " + type.cppType + u";");
// write all the types and their content
for (const QmltcType &type : std::as_const(program.compiledTypes))
- write(code, type);
+ write(code, type, program.exportMacro);
// add typeCount definitions. after all types have been written down (so
// they are now complete types as per C++). practically, this only concerns
@@ -251,10 +251,14 @@ static void dumpFunctions(QmltcOutputWrapper &code, const QList<QmltcMethod> &fu
}
}
-void QmltcCodeWriter::write(QmltcOutputWrapper &code, const QmltcType &type)
+void QmltcCodeWriter::write(QmltcOutputWrapper &code, const QmltcType &type,
+ const QString &exportMacro)
{
const auto constructClassString = [&]() {
- QString str = u"class " + type.cppType;
+ QString str = u"class "_s;
+ if (!exportMacro.isEmpty())
+ str.append(exportMacro).append(u" "_s);
+ str.append(type.cppType);
QStringList nonEmptyBaseClasses;
nonEmptyBaseClasses.reserve(type.baseClasses.size());
std::copy_if(type.baseClasses.cbegin(), type.baseClasses.cend(),
@@ -340,7 +344,7 @@ void QmltcCodeWriter::write(QmltcOutputWrapper &code, const QmltcType &type)
// children
for (const auto &child : std::as_const(type.children))
- QmltcCodeWriter::write(code, child);
+ QmltcCodeWriter::write(code, child, exportMacro);
// (non-visible) functions
dumpFunctions(code, type.functions, std::not_fn(isUserVisibleFunction));
diff --git a/tools/qmltc/qmltccodewriter.h b/tools/qmltc/qmltccodewriter.h
index e95777998e..04446f4c4e 100644
--- a/tools/qmltc/qmltccodewriter.h
+++ b/tools/qmltc/qmltccodewriter.h
@@ -20,7 +20,7 @@ struct QmltcCodeWriter
static void writeGlobalFooter(QmltcOutputWrapper &code, const QString &sourcePath,
const QString &outNamespace);
static void write(QmltcOutputWrapper &code, const QmltcProgram &program);
- static void write(QmltcOutputWrapper &code, const QmltcType &type);
+ static void write(QmltcOutputWrapper &code, const QmltcType &type, const QString &exportMacro);
static void write(QmltcOutputWrapper &code, const QmltcEnum &enumeration);
static void write(QmltcOutputWrapper &code, const QmltcMethod &method);
static void write(QmltcOutputWrapper &code, const QmltcCtor &ctor);
diff --git a/tools/qmltc/qmltccompiler.cpp b/tools/qmltc/qmltccompiler.cpp
index 4050136ef2..d97fc26ecf 100644
--- a/tools/qmltc/qmltccompiler.cpp
+++ b/tools/qmltc/qmltccompiler.cpp
@@ -142,8 +142,11 @@ void QmltcCompiler::compile(const QmltcCompilerInfo &info)
program.cppPath = m_info.outputCppFile;
program.hPath = m_info.outputHFile;
program.outNamespace = m_info.outputNamespace;
+ program.exportMacro = m_info.exportMacro;
program.compiledTypes = compiledTypes;
program.includes = m_visitor->cppIncludeFiles();
+ if (!m_info.exportMacro.isEmpty() && !m_info.exportInclude.isEmpty())
+ program.includes += (m_info.exportInclude);
program.urlMethod = urlMethod;
QmltcOutput out;
diff --git a/tools/qmltc/qmltccompiler.h b/tools/qmltc/qmltccompiler.h
index d5f07aa3b0..baa5b23256 100644
--- a/tools/qmltc/qmltccompiler.h
+++ b/tools/qmltc/qmltccompiler.h
@@ -25,6 +25,8 @@ struct QmltcCompilerInfo
QString outputHFile;
QString outputNamespace;
QString resourcePath;
+ QString exportMacro;
+ QString exportInclude;
};
class QmltcCompiler
diff --git a/tools/qmltc/qmltcoutputir.h b/tools/qmltc/qmltcoutputir.h
index 8884ddc3a3..cd094ee7b1 100644
--- a/tools/qmltc/qmltcoutputir.h
+++ b/tools/qmltc/qmltcoutputir.h
@@ -140,6 +140,8 @@ struct QmltcProgram
QString cppPath; // C++ output .cpp path
QString hPath; // C++ output .h path
QString outNamespace;
+ QString exportMacro; // if not empty, the macro that should be used to export the generated
+ // classes
QSet<QString> includes; // non-default C++ include files
QmltcMethod urlMethod; // returns QUrl of the QML document