Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Proper way to deploy 3rdparty Qt Quick plugins #319

Closed
IlyaBizyaev opened this issue Sep 29, 2018 · 15 comments
Closed

Proper way to deploy 3rdparty Qt Quick plugins #319

IlyaBizyaev opened this issue Sep 29, 2018 · 15 comments

Comments

@IlyaBizyaev
Copy link
Contributor

Hello! What is the recommended way to add custom Qt Quick plugins into the AppImage?

E.g. I use Kirigami, and my build script uses this trick:

./3rdparty/linuxdeployqt-continuous-x86_64.AppImage ./AppDir/usr/share/applications/kaidan.desktop -qmake=$QT_LINUX/bin/qmake -qmldir=./src/qml/ -bundle-non-qt-libs
    
cp $KIRIGAMI_BUILD/lib64/libKF5Kirigami2.so* ./AppDir/usr/lib
cp -r $KIRIGAMI_BUILD/lib64/qml/org ./AppDir/usr/qml
    
./3rdparty/linuxdeployqt-continuous-x86_64.AppImage ./AppDir/usr/share/applications/kaidan.desktop -qmake=$QT_LINUX/bin/qmake -qmldir=./src/qml/ -appimage

It works, but requires to launch linuxdeployqt twice.

@TheAssassin
Copy link
Collaborator

Why don't you copy in the files before launching linuxdeploy? Just use mkdir -p ... to prepare the directories.

@IlyaBizyaev
Copy link
Contributor Author

Fair point! :)
Now it's one run, but still feels a bit hacky.

@TheAssassin
Copy link
Collaborator

Not at all. That's how it works, if your build system doesn't do the job correctly.

Closing as resolved.

@IlyaBizyaev
Copy link
Contributor Author

My app uses CMake. Is it possible to do the job correctly with it?

@TheAssassin
Copy link
Collaborator

Make it install all your files using https://cmake.org/cmake/help/v3.12/command/install.html, and then follow the guide.

@IlyaBizyaev
Copy link
Contributor Author

qmlimportscanner is capable of receiving multiple -importPath arguments, so I'm able to specify 3rdparty module location and get correct output for it:

    ...
    {
        "classname": "KirigamiPlugin",
        "name": "org.kde.kirigami",
        "path": "/tmp/kirigami-linux-build/lib64/qml/org/kde/kirigami.2",
        "plugin": "kirigamiplugin",
        "relativePath": "org/kde/kirigami.2",
        "type": "module",
        "version": "2.0"
    },
    ...

Maybe linuxdeployqt should be able to accept a list of QML import paths or at least use QML2_IMPORT_PATH env variable?

https://github.com/probonopd/linuxdeployqt/blob/master/tools/linuxdeployqt/shared.cpp#L1561

@probonopd
Copy link
Owner

You can pass in additional directories using one or more -qml options,

// build argument list for qmlimportsanner: "-rootPath foo/ -rootPath bar/ -importPath path/to/qt/qml"
// ("rootPath" points to a directory containing app qml, "importPath" is where the Qt imports are installed)
QStringList argumentList;
foreach (const QString &qmlDir, qmlDirs) {
argumentList.append("-rootPath");
argumentList.append(qmlDir);
}

Does this solve it for you @IlyaBizyaev?

@IlyaBizyaev
Copy link
Contributor Author

No, unfortunately, it doesn't.

Passing a path as a -qmldir option to linuxdeployqt makes it add that path as a -rootPath argument to qmlimportscanner (see the snippet above). But rootPath is a location of application's *.qml files, not QML library install path, and it's treated accordingly (scanned for import *).

By default libraries that are needed to satisfy those imports are only searched in the Qt installation, qml subdirectory. You can specify additional search paths at runtime using the QML2_IMPORT_PATH env variable, but here the goal is to bundle them into the AppImage to avoid separate distribution. To do that, we need to ensure they are located by qmlimportscanner, which can be achieved by passing the -importPath option to it (notice the difference in name).

linuxdeployqt, however, only passes that qml subdirectory of the Qt installation (the default location), so one cannot specify custom locations for library search. As a hack one can create a link to the custom location from inside the Qt installation, but it is inconvenient if a distribution-provided Qt is used (requires root permissions) or there are multiple versions of this library.

As a solution, linuxdeployqt could provide a flag like -qmlimport and process it just like -qmldir, but for the -importPath argument. Or, alternatively, it could read the QML2_IMPORT_PATH env variable, which is also widely used in Qt.

@probonopd
Copy link
Owner

probonopd commented Oct 3, 2018

Thanks @IlyaBizyaev for the explanation. Let me understand this correctly:

If a QML import needs a .so file as a dependency, then the path to the directory in which the .so file resides needs to be passed in using -importPath?

What is the scenario in which you woould need to pass in an -importPath rather than a -rootPath? (I was assuming that the latter was for application-provided QML files; and that Qt-provided QML files is nothing that need to be changed.)

What would we need to change to support the QML2_IMPORT_PATH env variable?

Can the whole thing be made in a way that it recognizes all needed paths automatically, without the user having to pass in arguments? I mean, if the application can run on the build system prior to running linuxdeployqt, then clearly all the needed information must be "there" somewhere?

Would it be possible for you to send a PR with the requested changes?

@IlyaBizyaev
Copy link
Contributor Author

I was assuming that -rootPath was for application-provided QML files

Yeah, that's right :)

and that Qt-provided QML files is nothing that need to be changed

The idea is not to replace Qt files, but to use a Qt Quick library that is neither part of Qt nor part of the application.

Consider the following situation:
bitmap

If a QML import needs a .so file as a dependency, then the path to the directory in which the .so file resides needs to be passed in using -importPath?

Not exactly. Qt Quick uses the idea of import paths, which basically are paths to the root directories of package installations, kind of like Java's CLASSPATH. E.g. if I have a Qt quick module imported like this:

import com.example.test 1.0

and located in /home/user/dev/quick-modules/com/example/test, I need to specify /home/user/dev/quick-modules/ as the import path, and qmlimportscanner will locate the package. Qt will then read a qmldir file inside it:

module com.example.test
plugin testplugin
classname TestPlugin
depends QtQuick.Controls 2.0
depends QtGraphicalEffects 1.0
designersupported

and load libtestplugin.so from this directory.

For example, this is how Kirigami looks when built:
screenshot_20181003_211633

if the application can run on the build system prior to running linuxdeployqt, then clearly all the needed information must be "there" somewhere?

It can be run thanks to the QML2_IMPORT_PATH variable queried by Qt at startup:

QML2_IMPORT_PATH=/tmp/kirigami-linux-build/lib64/qml ./build/bin/kaidan

Without this definition, you get an error like this:

QQmlApplicationEngine failed to load component
qrc:/qml/main.qml:33 module "org.kde.kirigami" is not installed

— which is explained by the fact that the QML module is not required at compilation, so paths don't get recorded. Qt searches for modules in their default locations, and on Linux it's ok and works for all sorts of packages.

As a solution, linuxdeployqt could provide a flag like -qmlimport and process it just like -qmldir, but for the -importPath argument.

From how I understand the code, a block like this:

} else if (argument.startsWith(QByteArray("-qmldir"))) {
LogDebug() << "Argument found:" << argument;
qmldirArgumentUsed = true;
int index = argument.indexOf('=');
if (index == -1)
LogError() << "Missing qml directory path";
else
qmlDirs << argument.mid(index+1);

will need to be added for -qmlimport, and then a loop like this:
// build argument list for qmlimportsanner: "-rootPath foo/ -rootPath bar/ -importPath path/to/qt/qml"
// ("rootPath" points to a directory containing app qml, "importPath" is where the Qt imports are installed)
QStringList argumentList;
foreach (const QString &qmlDir, qmlDirs) {
argumentList.append("-rootPath");
argumentList.append(qmlDir);
}

to add -importPaths to qmlimportscanner's call.

Would it be possible for you to send a PR with the requested changes?

Yes it would, but will take me a while to build linuxdeployqt from source code.

@probonopd
Copy link
Owner

Thank you very much for your detailed explanation, I think I understand it now. Would highly welcome a PR. Again, thank you very much.

@IlyaBizyaev
Copy link
Contributor Author

Opened a pull request.

@probonopd
Copy link
Owner

...merged 👍 💯

@trmdi
Copy link

trmdi commented Jan 21, 2021

Hello, could you please tell me how to apply these flags for my case? https://build.opensuse.org/package/view_file/home:trmdi:test/latte-dock/appimage.yml?expand=1
I can't find it anywhere in the docs.

@probonopd
Copy link
Owner

probonopd commented Jan 22, 2021

Please see

https://github.com/probonopd/linuxdeployqt/pull/320/files#diff-1e7279c8098c42e32e06f1ef908c14e7b616ac746d56043cab30f041f52c61edR92

-qmlimport=<path>        : Add the given path to QML module search locations.

I have added it to the README.md.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants