aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--tests/auto/cmake/CMakeLists.txt12
-rw-r--r--tests/auto/cmake/qmlimportscanner/CMakeLists.txt18
-rw-r--r--tests/auto/cmake/qmlimportscanner/main.cpp53
-rw-r--r--tests/auto/cmake/qmlimportscanner/main.qml5
-rw-r--r--tests/auto/cmake/qmlimportscanner/qis_test.qrc6
-rw-r--r--tools/qmlimportscanner/Qt5QmlImportScannerConfig.cmake.in184
-rw-r--r--tools/qmlimportscanner/Qt5QmlImportScannerTemplate.cpp.in5
-rw-r--r--tools/qmlimportscanner/main.cpp46
-rw-r--r--tools/qmlimportscanner/qmlimportscanner.pro45
9 files changed, 371 insertions, 3 deletions
diff --git a/tests/auto/cmake/CMakeLists.txt b/tests/auto/cmake/CMakeLists.txt
index f304a99705..bda5d626a9 100644
--- a/tests/auto/cmake/CMakeLists.txt
+++ b/tests/auto/cmake/CMakeLists.txt
@@ -27,3 +27,15 @@ add_test(qtquickcompiler ${CMAKE_CTEST_COMMAND}
--build-options "-DCMAKE_PREFIX_PATH=${CMAKE_PREFIX_PATH}" ${BUILD_OPTIONS_LIST}
--test-command qqc_test
)
+
+add_test(qmlimportscanner ${CMAKE_CTEST_COMMAND}
+ --build-and-test
+ "${CMAKE_CURRENT_SOURCE_DIR}/qmlimportscanner/"
+ "${CMAKE_CURRENT_BINARY_DIR}/qmlimportscanner"
+ --build-config "${CMAKE_BUILD_TYPE}"
+ --build-generator ${CMAKE_GENERATOR}
+ --build-makeprogram ${CMAKE_MAKE_PROGRAM}
+ --build-project qis_test
+ --build-options "-DCMAKE_PREFIX_PATH=${CMAKE_PREFIX_PATH}" ${BUILD_OPTIONS_LIST}
+ --test-command qis_test
+)
diff --git a/tests/auto/cmake/qmlimportscanner/CMakeLists.txt b/tests/auto/cmake/qmlimportscanner/CMakeLists.txt
new file mode 100644
index 0000000000..354b0f8dfc
--- /dev/null
+++ b/tests/auto/cmake/qmlimportscanner/CMakeLists.txt
@@ -0,0 +1,18 @@
+
+cmake_minimum_required(VERSION 3.1)
+project(qis_test)
+
+find_package(Qt5Qml 5.0.0 REQUIRED)
+find_package(Qt5Gui 5.0.0 REQUIRED)
+find_package(Qt5Test 5.0.0 REQUIRED)
+find_package(Qt5QmlImportScanner REQUIRED)
+
+set(CMAKE_CXXFLAGS "${CMAKE_CXXFLAGS} ${Qt5Core_EXECUTABLE_COMPILE_FLAGS}")
+
+set(CMAKE_AUTOMOC ON)
+set(CMAKE_AUTORCC ON)
+
+add_executable(qis_test "${CMAKE_CURRENT_SOURCE_DIR}/main.cpp"
+ "${CMAKE_CURRENT_SOURCE_DIR}/qis_test.qrc")
+target_link_libraries(qis_test PRIVATE Qt5::Gui Qt5::Qml Qt5::Test)
+qt5_import_qml_plugins(qis_test)
diff --git a/tests/auto/cmake/qmlimportscanner/main.cpp b/tests/auto/cmake/qmlimportscanner/main.cpp
new file mode 100644
index 0000000000..370b10e113
--- /dev/null
+++ b/tests/auto/cmake/qmlimportscanner/main.cpp
@@ -0,0 +1,53 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the test suite of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <QtCore>
+#include <QtQml>
+#include <QtTest>
+
+class tst_QQC : public QObject
+{
+ Q_OBJECT
+private slots:
+ void staticBuildTest();
+};
+
+void tst_QQC::staticBuildTest()
+{
+#ifdef QT_STATIC
+ QQmlEngine engine;
+ QQmlComponent component(&engine, QUrl("qrc:/main.qml"));
+ QScopedPointer<QObject> obj(component.create());
+ QVERIFY(!obj.isNull());
+ QCOMPARE(obj->property("success").toInt(), 42);
+#endif
+}
+
+QTEST_MAIN(tst_QQC)
+
+#include "main.moc"
diff --git a/tests/auto/cmake/qmlimportscanner/main.qml b/tests/auto/cmake/qmlimportscanner/main.qml
new file mode 100644
index 0000000000..e0101958ea
--- /dev/null
+++ b/tests/auto/cmake/qmlimportscanner/main.qml
@@ -0,0 +1,5 @@
+import QtQml 2.0
+import QtQuick 2.0
+QtObject {
+ property int success: 42
+}
diff --git a/tests/auto/cmake/qmlimportscanner/qis_test.qrc b/tests/auto/cmake/qmlimportscanner/qis_test.qrc
new file mode 100644
index 0000000000..1f88fc4e71
--- /dev/null
+++ b/tests/auto/cmake/qmlimportscanner/qis_test.qrc
@@ -0,0 +1,6 @@
+<!DOCTYPE RCC><RCC version="1.0">
+<qresource>
+<file>./main.qml</file>
+<file alias="main.cpp">./main.cpp</file>
+</qresource>
+</RCC>
diff --git a/tools/qmlimportscanner/Qt5QmlImportScannerConfig.cmake.in b/tools/qmlimportscanner/Qt5QmlImportScannerConfig.cmake.in
new file mode 100644
index 0000000000..6cdfaf8f6f
--- /dev/null
+++ b/tools/qmlimportscanner/Qt5QmlImportScannerConfig.cmake.in
@@ -0,0 +1,184 @@
+include(CMakeParseArguments)
+
+function(QT5_IMPORT_QML_PLUGINS target)
+!!IF !isEmpty(CMAKE_STATIC_TYPE)
+ set(options)
+ set(oneValueArgs \"PATH_TO_SCAN\")
+ set(multiValueArgs)
+
+ cmake_parse_arguments(arg \"${options}\" \"${oneValueArgs}\" \"${multiValueArgs}\" ${ARGN})
+ if(NOT arg_PATH_TO_SCAN)
+ set(arg_PATH_TO_SCAN \"${CMAKE_CURRENT_SOURCE_DIR}\")
+ endif()
+
+ # Find location of qmlimportscanner.
+ find_package(Qt5 COMPONENTS Core)
+!!IF isEmpty(CMAKE_BIN_DIR_IS_ABSOLUTE)
+ set(tool_path
+ \"${_qt5Core_install_prefix}/$${CMAKE_BIN_DIR}qmlimportscanner$$CMAKE_BIN_SUFFIX\")
+!!ELSE
+ set(tool_path \"$${CMAKE_BIN_DIR}qmlimportscanner$$CMAKE_BIN_SUFFIX\")
+!!ENDIF
+ if(NOT EXISTS \"${tool_path}\" )
+ message(FATAL_ERROR \"The package \\\"Qt5QmlImportScannerConfig\\\" references the file
+ \\\"${tool_path}\\\"
+but this file does not exist. Possible reasons include:
+* The file was deleted, renamed, or moved to another location.
+* An install or uninstall procedure did not complete successfully.
+* The installation package was faulty.
+\")
+ endif()
+
+ # Find location of qml dir.
+!!IF isEmpty(CMAKE_QML_DIR_IS_ABSOLUTE)
+ set(qml_path \"${_qt5Core_install_prefix}/$${CMAKE_QML_DIR}\")
+!!ELSE
+ set(qml_path \"$${CMAKE_QML_DIR}\")
+!!ENDIF
+
+ # Small macro to avoid duplicating code in two different loops.
+ macro(_qt5_QmlImportScanner_parse_entry)
+ set(entry_name \"qml_import_scanner_import_${idx}\")
+ cmake_parse_arguments(\"entry\"
+ \"\"
+ \"CLASSNAME;NAME;PATH;PLUGIN;RELATIVEPATH;TYPE;VERSION;\" \"\"
+ ${${entry_name}})
+ endmacro()
+
+ # Macro used to populate the dependency link flags for a certain configuriation (debug vs
+ # release) of a plugin.
+ macro(_qt5_link_to_QmlImportScanner_library_dependencies Plugin Configuration PluginLocation
+ IsDebugAndRelease)
+
+ set_property(TARGET \"${Plugin}\" APPEND PROPERTY IMPORTED_CONFIGURATIONS ${Configuration})
+ set(_imported_location \"${PluginLocation}\")
+ _qt5_Core_check_file_exists(\"${_imported_location}\")
+ set_target_properties(\"${Plugin}\" PROPERTIES
+ \"IMPORTED_LOCATION_${Configuration}\" \"${_imported_location}\"
+ )
+
+ set(_static_deps
+ ${_Qt5${entry_PLUGIN}_STATIC_${Configuration}_LIB_DEPENDENCIES}
+ )
+
+ if(NOT "${IsDebugAndRelease}")
+ set(_genex_condition \"1\")
+ else()
+ if("${Configuration}" STREQUAL "DEBUG")
+ set(_genex_condition \"$<CONFIG:Debug>\")
+ else()
+ set(_genex_condition \"$<NOT:$<CONFIG:Debug>>\")
+ endif()
+ endif()
+ if(_static_deps)
+ set(_static_deps_genex \"$<${_genex_condition}:${_static_deps}>\")
+ target_link_libraries(${imported_target} INTERFACE \"${_static_deps_genex}\")
+ endif()
+
+ set(_static_link_flags \"${_Qt5${entry_PLUGIN}_STATIC_${Configuration}_LINK_FLAGS}\")
+ if(NOT CMAKE_VERSION VERSION_LESS \"3.13\" AND _static_link_flags)
+ set(_static_link_flags_genex \"$<${_genex_condition}:${_static_link_flags}>\")
+ target_link_options(${imported_target} INTERFACE \"${_static_link_flags_genex}\")
+ endif()
+ endmacro()
+
+ # Run qmlimportscanner and include the generated cmake file.
+ set(qml_imports_file_path
+ \"${CMAKE_CURRENT_BINARY_DIR}/Qt5_QmlPlugins_Imports_${target}.cmake\")
+
+ message(STATUS \"Running qmlimportscanner to find used QML plugins. \")
+ execute_process(COMMAND
+ \"${tool_path}\" \"${arg_PATH_TO_SCAN}\" -importPath \"${qml_path}\"
+ -cmake-output
+ OUTPUT_FILE \"${qml_imports_file_path}\")
+
+ include(\"${qml_imports_file_path}\" OPTIONAL RESULT_VARIABLE qml_imports_file_path_found)
+ if(NOT qml_imports_file_path_found)
+ message(FATAL_ERROR \"Could not find ${qml_imports_file_path} which was supposed to be generated by qmlimportscanner.\")
+ endif()
+
+ # Parse the generate cmake file.
+ # It is possible for the scanner to find no usage of QML, in which case the import count is 0.
+ if(qml_import_scanner_imports_count)
+ set(added_plugins \"\")
+ foreach(idx RANGE \"${qml_import_scanner_imports_count}\")
+ _qt5_QmlImportScanner_parse_entry()
+ if(entry_PATH AND entry_PLUGIN)
+ # Sometimes a plugin appears multiple times with different versions.
+ # Make sure to process it only once.
+ list(FIND added_plugins \"${entry_PLUGIN}\" _index)
+ if(NOT _index EQUAL -1)
+ continue()
+ endif()
+ list(APPEND added_plugins \"${entry_PLUGIN}\")
+
+ # Add an imported target that will contain the link libraries and link options read
+ # from one plugin prl file. This target will point to the actual plugin and contain
+ # static dependency libraries and link flags.
+ # By creating a target for each qml plugin, CMake will take care of link flag
+ # deduplication.
+ set(imported_target \"${target}_QmlImport_${entry_PLUGIN}\")
+ add_library(\"${imported_target}\" MODULE IMPORTED)
+ target_link_libraries(\"${target}\" PRIVATE \"${imported_target}\")
+
+ # Read static library dependencies from the plugin .prl file.
+ # And then set the link flags to the library dependencies extracted from the .prl
+ # file.
+!!IF !isEmpty(CMAKE_RELEASE_TYPE)
+ _qt5_Core_process_prl_file(
+ \"${entry_PATH}/$$QMAKE_PREFIX_STATICLIB${entry_PLUGIN}$${CMAKE_QML_PLUGIN_SUFFIX_RELEASE}.prl\" RELEASE
+ _Qt5${entry_PLUGIN}_STATIC_RELEASE_LIB_DEPENDENCIES
+ _Qt5${entry_PLUGIN}_STATIC_RELEASE_LINK_FLAGS
+ )
+ _qt5_link_to_QmlImportScanner_library_dependencies(
+ \"${imported_target}\"
+ RELEASE
+ \"${entry_PATH}/$$QMAKE_PREFIX_STATICLIB${entry_PLUGIN}$${CMAKE_QML_PLUGIN_SUFFIX_RELEASE}.$$QMAKE_EXTENSION_STATICLIB\"
+ $${CMAKE_DEBUG_AND_RELEASE})
+!!ENDIF
+
+!!IF !isEmpty(CMAKE_DEBUG_TYPE)
+ _qt5_Core_process_prl_file(
+ \"${entry_PATH}/$$QMAKE_PREFIX_STATICLIB${entry_PLUGIN}$${CMAKE_QML_PLUGIN_SUFFIX_DEBUG}.prl\" DEBUG
+ _Qt5${entry_PLUGIN}_STATIC_DEBUG_LIB_DEPENDENCIES
+ _Qt5${entry_PLUGIN}_STATIC_DEBUG_LINK_FLAGS
+ )
+ _qt5_link_to_QmlImportScanner_library_dependencies(
+ \"${imported_target}\"
+ DEBUG
+ \"${entry_PATH}/$$QMAKE_PREFIX_STATICLIB${entry_PLUGIN}$${CMAKE_QML_PLUGIN_SUFFIX_DEBUG}.$$QMAKE_EXTENSION_STATICLIB\"
+ $${CMAKE_DEBUG_AND_RELEASE})
+!!ENDIF
+ endif()
+ endforeach()
+
+ # Generate content for plugin initialization cpp file.
+ set(added_imports \"\")
+ set(qt5_qml_import_cpp_file_content \"\")
+ foreach(idx RANGE \"${qml_import_scanner_imports_count}\")
+ _qt5_QmlImportScanner_parse_entry()
+ if(entry_PLUGIN)
+ if(entry_CLASSNAME)
+ list(FIND added_imports \"${entry_PLUGIN}\" _index)
+ if(_index EQUAL -1)
+ string(APPEND qt5_qml_import_cpp_file_content
+ \"Q_IMPORT_PLUGIN(${entry_CLASSNAME})\n\")
+ list(APPEND added_imports \"${entry_PLUGIN}\")
+ endif()
+ else()
+ message(FATAL_ERROR
+ \"Plugin ${entry_PLUGIN} is missing a classname entry, please add one to the qmldir file.\")
+ endif()
+ endif()
+ endforeach()
+
+ # Write to the generated file, and include it as a source for the given target.
+ set(generated_import_cpp_path
+ \"${CMAKE_CURRENT_BINARY_DIR}/Qt5_QmlPlugins_Imports_${target}.cpp\")
+ configure_file(\"${Qt5QmlImportScanner_DIR}/Qt5QmlImportScannerTemplate.cpp.in\"
+ \"${generated_import_cpp_path}\"
+ @ONLY)
+ target_sources(${target} PRIVATE \"${generated_import_cpp_path}\")
+ endif()
+!!ENDIF // !isEmpty(CMAKE_STATIC_TYPE)
+endfunction()
diff --git a/tools/qmlimportscanner/Qt5QmlImportScannerTemplate.cpp.in b/tools/qmlimportscanner/Qt5QmlImportScannerTemplate.cpp.in
new file mode 100644
index 0000000000..4ed747a555
--- /dev/null
+++ b/tools/qmlimportscanner/Qt5QmlImportScannerTemplate.cpp.in
@@ -0,0 +1,5 @@
+// This file is autogenerated by CMake. It imports static plugin classes for
+// static plugins used by QML imports.
+#include <QtPlugin>
+
+@qt5_qml_import_cpp_file_content@
diff --git a/tools/qmlimportscanner/main.cpp b/tools/qmlimportscanner/main.cpp
index 05d1f7fdc0..6d48f6203d 100644
--- a/tools/qmlimportscanner/main.cpp
+++ b/tools/qmlimportscanner/main.cpp
@@ -496,6 +496,36 @@ QVariantList findQmlImportsRecursively(const QStringList &qmlDirs, const QString
return ret;
}
+
+QString generateCmakeIncludeFileContent(const QVariantList &importList) {
+ // The function assumes that "list" is a QVariantList with 0 or more QVariantMaps, where
+ // each map contains QString -> QVariant<QString> mappings. This matches with the structure
+ // that qmake parses for static qml plugin auto imporitng.
+ // So: [ {"a": "a","b": "b"}, {"c": "c"} ]
+ QString content;
+ QTextStream s(&content);
+ int importsCount = 0;
+ for (const QVariant &importVariant: importList) {
+ if (static_cast<QMetaType::Type>(importVariant.type()) == QMetaType::QVariantMap) {
+ s << QStringLiteral("set(qml_import_scanner_import_") << importsCount
+ << QStringLiteral(" \"");
+
+ const QMap<QString, QVariant> &importDict = importVariant.toMap();
+ for (auto it = importDict.cbegin(); it != importDict.cend(); ++it) {
+ s << it.key().toUpper() << QLatin1Char(';')
+ << it.value().toString() << QLatin1Char(';');
+ }
+ s << QStringLiteral("\")\n");
+ ++importsCount;
+ }
+ }
+ if (importsCount >= 0) {
+ content.prepend(QString(QStringLiteral("set(qml_import_scanner_imports_count %1)\n"))
+ .arg(importsCount));
+ }
+ return content;
+}
+
} // namespace
int main(int argc, char *argv[])
@@ -512,6 +542,7 @@ int main(int argc, char *argv[])
QStringList qmlRootPaths;
QStringList scanFiles;
QStringList qmlImportPaths;
+ bool generateCmakeContent = false;
int i = 1;
while (i < args.count()) {
@@ -536,6 +567,8 @@ int main(int argc, char *argv[])
if (i >= args.count())
std::cerr << "-importPath requires an argument\n";
argReceiver = &qmlImportPaths;
+ } else if (arg == QLatin1String("-cmake-output")) {
+ generateCmakeContent = true;
} else {
std::cerr << qPrintable(appName) << ": Invalid argument: \""
<< qPrintable(arg) << "\"\n";
@@ -562,8 +595,15 @@ int main(int argc, char *argv[])
// Find the imports!
QVariantList imports = findQmlImportsRecursively(qmlRootPaths, scanFiles);
- // Convert to JSON
- QByteArray json = QJsonDocument(QJsonArray::fromVariantList(imports)).toJson();
- std::cout << json.constData() << std::endl;
+ QByteArray content;
+ if (generateCmakeContent) {
+ // Convert to CMake code
+ content = generateCmakeIncludeFileContent(imports).toUtf8();
+ } else {
+ // Convert to JSON
+ content = QJsonDocument(QJsonArray::fromVariantList(imports)).toJson();
+ }
+
+ std::cout << content.constData() << std::endl;
return 0;
}
diff --git a/tools/qmlimportscanner/qmlimportscanner.pro b/tools/qmlimportscanner/qmlimportscanner.pro
index 0b3a03abf3..a29b582274 100644
--- a/tools/qmlimportscanner/qmlimportscanner.pro
+++ b/tools/qmlimportscanner/qmlimportscanner.pro
@@ -5,6 +5,51 @@ DEFINES += QT_NO_CAST_TO_ASCII QT_NO_CAST_FROM_ASCII
SOURCES += main.cpp
+load(cmake_functions)
+
+CMAKE_BIN_DIR = $$cmakeRelativePath($$[QT_HOST_BINS], $$[QT_INSTALL_PREFIX])
+contains(CMAKE_BIN_DIR, "^\\.\\./.*") {
+ CMAKE_BIN_DIR = $$[QT_HOST_BINS]/
+ CMAKE_BIN_DIR_IS_ABSOLUTE = True
+}
+
+CMAKE_QML_DIR = $$cmakeRelativePath($$[QT_INSTALL_QML/get], $$[QT_INSTALL_PREFIX])
+contains(CMAKE_QML_DIR, "^\\.\\./.*") {
+ CMAKE_QML_DIR = $$[QT_INSTALL_QML/get]/
+ CMAKE_QML_DIR_IS_ABSOLUTE = True
+}
+load(qt_build_paths)
+
+static|staticlib:CMAKE_STATIC_TYPE = true
+
+# Compute the platform target suffix.
+CMAKE_QML_PLUGIN_SUFFIX_RELEASE =
+win32: CMAKE_QML_PLUGIN_SUFFIX_DEBUG = d
+else:darwin: CMAKE_QML_PLUGIN_SUFFIX_DEBUG = _debug
+else: CMAKE_QML_PLUGIN_SUFFIX_DEBUG =
+
+# Find out which configurations should be handled in the generated Config.cmake file.
+CMAKE_DEBUG_TYPE =
+CMAKE_RELEASE_TYPE =
+if(qtConfig(debug_and_release)|contains(QT_CONFIG, debug, debug|release)): CMAKE_DEBUG_TYPE = debug
+if(qtConfig(debug_and_release)|contains(QT_CONFIG, release, debug|release)): CMAKE_RELEASE_TYPE = release
+
+qtConfig(debug_and_release) {
+ CMAKE_DEBUG_AND_RELEASE = TRUE
+} else {
+ CMAKE_DEBUG_AND_RELEASE = FALSE
+}
+
+equals(QMAKE_HOST.os, Windows): CMAKE_BIN_SUFFIX = ".exe"
+cmake_config_file.input = $$PWD/Qt5QmlImportScannerConfig.cmake.in
+cmake_config_file.output = $$MODULE_BASE_OUTDIR/lib/cmake/Qt5QmlImportScanner/Qt5QmlImportScannerConfig.cmake
+QMAKE_SUBSTITUTES += cmake_config_file
+
+cmake_build_integration.files = $$cmake_config_file.output $$PWD/Qt5QmlImportScannerTemplate.cpp.in
+cmake_build_integration.path = $$[QT_INSTALL_LIBS]/cmake/Qt5QmlImportScanner
+prefix_build: INSTALLS += cmake_build_integration
+else: COPIES += cmake_build_integration
+
QMAKE_TARGET_DESCRIPTION = QML Import Scanner
load(qt_tool)