Open Aksem opened 1 year ago
At which path is your Qt installation's plugins
directory?
/opt/Qt/5.15.2/gcc_64/plugins
Where is this build of Qt coming from? Was it originally compiled at /home/qt/work/install
maybe?
Qt build is pre-built on another machine and downloaded via aqtinstall (it's a tool that downloads Qt binaries from Qt website and prepares them for usage: extracts etc.). Yes, it was compiled originally at /home/qt/work/install
; this I meant in the first comment.
OK, so the qt_prfxpath
compiled into your Qt does not match the location at which your Qt actually is located.
Which means that we cannot trust qt_prfxpath
and find out where Qt lives in some other way. Only question: How...? Maybe using qmake
on the $PATH
?
Currently, I use linuxdeployqt and pass path to qmake as parameter; this approach works well also with both locally built Qt and Qt build downloaded with aqtinstall. It is possible to add qmake to $PATH, it would be important to prepend it and not append it to avoid conflict with other Qt versions, e.g., system one.
I am running into the same issue: https://github.com/Murmele/Gittyup/pull/579
We need to find a reliable way of knowing the path in which Qt' s plugins directory is.
Currently we just read qt_prfxpath
compiled into your Qtcompiled into Qt. But it can be overridden by a
qt.conffile. So we may also need to read the
qt.conf` on the system and check whether it sets a different Qt path.
@Murmele, @Aksem can you confirm that qt.conf
files exist on your system? At which location and what do they contain?
Places to chek:
$QT_CONF_PATH
environment variable (if present)$HOME/.config/
or $HOME/.local/share/
/usr/local/share/
or /etc/xdg
or /usr/local/etc
Here is a small Go program that tries to find the relevant qt.conf
file:
package main
import (
"fmt"
"os"
"path/filepath"
)
func getPossibleQtConfPaths(appPath string) []string {
paths := []string{
filepath.Join(filepath.Dir(appPath), "qt.conf"), // Next to the application
":/qt.conf", // Application resource directory
"/etc/xdg/qt.conf", // System-wide configuration directory
"/usr/local/etc/qt.conf",
filepath.Join(os.Getenv("HOME"), ".config", "qt.conf"), // User's home directory
filepath.Join(os.Getenv("HOME"), ".local", "share", "qt.conf"), // User's home directory
os.Getenv("QT_CONF_PATH"), // Environment variable
}
return paths
}
func findQtConfFile(appPath string) string {
paths := getPossibleQtConfPaths(appPath)
for _, path := range paths {
info, err := os.Stat(path)
if err == nil && !info.IsDir() {
return path
}
}
return "" // Empty string if no qt.conf file found
}
func main() {
if len(os.Args) < 2 {
fmt.Println("Please provide the path to the application as an argument.")
return
}
appPath := os.Args[1]
qtConfPath := findQtConfFile(appPath)
if qtConfPath != "" {
fmt.Println("Found qt.conf file at:", qtConfPath)
} else {
fmt.Println("No qt.conf file found.")
}
}
Paste it into qtpath.go
and then go run qtpath.go ./Gittyup.AppDir/usr/bin/gittyup
on a "fresh" AppDir on which linuxdeployqt
has not run yet.
Here is an updated version that tries to read the path to Qt from the found qt.conf
file - does it give you the correct path on your system?
package main
import (
"fmt"
"io/ioutil"
"os"
"path/filepath"
"strings"
)
func getPossibleQtConfPaths(appPath string) []string {
paths := []string{
os.Getenv("QT_CONF_PATH"), // Environment variable
filepath.Join(filepath.Dir(appPath), "qt.conf"), // Next to the application
":/qt.conf", // Application resource directory
filepath.Join(os.Getenv("HOME"), ".config", "qt.conf"), // User's home directory
filepath.Join(os.Getenv("HOME"), ".local", "share", "qt.conf"), // User's home directory
"/etc/xdg/qt.conf", // System-wide configuration directory
"/usr/local/etc/qt.conf",
}
return paths
}
func getQtPathFromConfFile(qtConfPath string) string {
content, err := ioutil.ReadFile(qtConfPath)
if err != nil {
return ""
}
// Extract the Qt path from the qt.conf file
lines := strings.Split(string(content), "\n")
for _, line := range lines {
trimmedLine := strings.TrimSpace(line)
if strings.HasPrefix(trimmedLine, "Prefix") {
parts := strings.SplitN(trimmedLine, "=", 2)
if len(parts) == 2 {
key := strings.TrimSpace(parts[0])
value := strings.TrimSpace(parts[1])
if strings.TrimPrefix(key, "Prefix") == "" {
return value
}
}
}
}
return ""
}
func resolvePath(appPath, qtPath string) (string, error) {
if filepath.IsAbs(qtPath) {
return qtPath, nil
}
absPath, err := filepath.Abs(filepath.Join(filepath.Dir(appPath), qtPath))
if err != nil {
return "", err
}
return absPath, nil
}
func findQtPath(appPath string) (string, error) {
qtConfPaths := getPossibleQtConfPaths(appPath)
for _, qtConfPath := range qtConfPaths {
if qtConfPath == "" {
continue
}
qtPath := getQtPathFromConfFile(qtConfPath)
if qtPath != "" {
absPath, err := resolvePath(appPath, qtPath)
if err != nil {
return "", err
}
return absPath, nil
}
}
return "", nil
}
func main() {
if len(os.Args) < 2 {
fmt.Println("Please provide the path to the application as an argument.")
return
}
appPath := os.Args[1]
qtPath, err := findQtPath(appPath)
if err != nil {
fmt.Println("Error:", err)
return
}
if qtPath != "" {
fmt.Println("Qt path:", qtPath)
} else {
fmt.Println("No qt.conf file found or no valid Qt path in the qt.conf files.")
}
}
Further updated:
/*
The program is designed to find the Qt prefix path and Qt plugins path based on the given application path.
1. The program takes the application path as an argument.
2. The `GetQtPrefix` function is called to find the Qt prefix path:
- It retrieves a list of possible `qt.conf` file paths.
- It reads each `qt.conf` file and extracts the Qt prefix path.
- If a Qt prefix path is found, it is returned.
- If no Qt prefix path is found, an empty string is returned.
3. The `GetQtPluginsPath` function is called to find the Qt plugins path:
- It calls the `GetQtPrefix` function to get the Qt prefix path.
- If a Qt prefix path exists:
- It retrieves a list of possible `qt.conf` file paths.
- It reads each `qt.conf` file and extracts the Qt plugins path.
- If a Qt plugins path is found, it is returned.
- If no Qt plugins path is found, the custom plugins path is appended to the Qt prefix path.
- If no Qt prefix path exists, an error is returned.
4. The `resolvePath` function is used to handle path resolution:
- If the path is an absolute path, it is returned as is.
- If the path is a relative path and a Qt prefix exists, it is appended to the Qt prefix.
- If the path is a relative path and no Qt prefix exists, it is appended to the directory of the application.
5. Finally, in the `main` function:
- The application path is retrieved from the command-line arguments.
- The `GetQtPrefix` function is called to get the Qt prefix path.
- The `GetQtPluginsPath` function is called to get the Qt plugins path.
- The Qt prefix path and Qt plugins path are printed.
Note: If no Qt prefix path is found or if an error occurs during the process, appropriate error messages are displayed.
*/
package main
import (
"fmt"
"io/ioutil"
"os"
"path/filepath"
"strings"
)
func GetQtPrefix(appPath string) (string, error) {
qtConfPaths := getPossibleQtConfPaths(appPath)
for _, qtConfPath := range qtConfPaths {
if qtConfPath == "" {
continue
}
qtPrefix := getQtPrefixFromConfFile(qtConfPath)
if qtPrefix != "" {
absPath, err := resolvePath(appPath, "", qtPrefix)
if err != nil {
return "", err
}
return absPath, nil
}
}
return "", nil
}
func GetQtPluginsPath(appPath string) (string, error) {
qtPrefix, err := GetQtPrefix(appPath)
if err != nil {
return "", err
}
if qtPrefix == "" {
return "", fmt.Errorf("Qt prefix not found")
}
qtConfPaths := getPossibleQtConfPaths(appPath)
for _, qtConfPath := range qtConfPaths {
if qtConfPath == "" {
continue
}
qtPluginsPath := getQtPluginsPathFromConfFile(qtConfPath)
if qtPluginsPath != "" {
absPath, err := resolvePath(appPath, qtPrefix, qtPluginsPath)
if err != nil {
return "", err
}
return absPath, nil
}
}
// If no Qt plugins path found in the conf files, append "plugins" to the Qt prefix path
return filepath.Join(qtPrefix, "plugins"), nil
}
func getPossibleQtConfPaths(appPath string) []string {
paths := []string{
os.Getenv("QT_CONF_PATH"), // Environment variable
filepath.Join(filepath.Dir(appPath), "qt.conf"), // Next to the application
":/qt.conf", // Application resource directory
filepath.Join(os.Getenv("HOME"), ".config", "qt.conf"), // User's home directory
filepath.Join(os.Getenv("HOME"), ".local", "share", "qt.conf"), // User's home directory
"/etc/xdg/qt.conf", // System-wide configuration directory
"/usr/local/etc/qt.conf",
}
return paths
}
func getQtPrefixFromConfFile(qtConfPath string) string {
content, err := ioutil.ReadFile(qtConfPath)
if err != nil {
return ""
}
// Extract the Qt prefix from the qt.conf file
lines := strings.Split(string(content), "\n")
for _, line := range lines {
trimmedLine := strings.TrimSpace(line)
if strings.HasPrefix(trimmedLine, "Prefix") {
parts := strings.SplitN(trimmedLine, "=", 2)
if len(parts) == 2 {
key := strings.TrimSpace(parts[0])
value := strings.TrimSpace(parts[1])
if strings.TrimPrefix(key, "Prefix") == "" {
return value
}
}
}
}
return ""
}
func getQtPluginsPathFromConfFile(qtConfPath string) string {
qtPrefix, _ := GetQtPrefix(qtConfPath)
content, err := ioutil.ReadFile(qtConfPath)
if err != nil {
return ""
}
// Extract the Qt plugins path from the qt.conf file
lines := strings.Split(string(content), "\n")
for _, line := range lines {
trimmedLine := strings.TrimSpace(line)
if strings.HasPrefix(trimmedLine, "Plugins") {
parts := strings.SplitN(trimmedLine, "=", 2)
if len(parts) == 2 {
key := strings.TrimSpace(parts[0])
value := strings.TrimSpace(parts[1])
if strings.TrimPrefix(key, "Plugins") == "" {
if filepath.IsAbs(value) {
return value
}
// If path is a relative path and a Qt prefix exists, append it to the Qt prefix
if qtPrefix != "" {
return filepath.Join(qtPrefix, value)
}
return value
}
}
}
}
// If no Qt plugins path found in the conf files, use the default relative path
return "plugins"
}
func resolvePath(appPath, qtPrefix, path string) (string, error) {
if filepath.IsAbs(path) {
return path, nil
}
// If path is a relative path and a Qt prefix exists, append it to the Qt prefix
if qtPrefix != "" {
return filepath.Join(qtPrefix, path), nil
}
// If Qt prefix doesn't exist or path is an absolute path, append the relative path to the directory of the application
dir := filepath.Dir(appPath)
return filepath.Join(dir, path), nil
}
func main() {
if len(os.Args) < 2 {
fmt.Println("Please provide the path to the application as an argument.")
return
}
appPath := os.Args[1]
// Check if the supplied binary/program exists and is a file
fileInfo, err := os.Stat(appPath)
if os.IsNotExist(err) {
fmt.Println("Error: The specified application does not exist.")
return
}
if fileInfo.IsDir() {
fmt.Println("Error: The specified application path is a directory.")
return
}
qtPrefixPath, err := GetQtPrefix(appPath)
if err != nil {
fmt.Println("Error:", err)
return
}
qtPluginsPath, err := GetQtPluginsPath(appPath)
if err != nil {
fmt.Println("Error:", err)
return
}
fmt.Println("Qt prefix path:", qtPrefixPath)
fmt.Println("Qt plugins path:", qtPluginsPath)
}
Please do test it and please let me know if it works.
@probonopd
Run go run qtpath.go ./build/release/AppDir/usr/bin/gittyup
go run qtpath.go ./build/release/AppDir/usr/bin/gittyup
shell: /usr/bin/bash -e {0}
env:
IS_RELEASE: false
CMAKE_FLAGS: -DDEV_BUILD="579/merge"
pythonLocation: /opt/hostedtoolcache/Python/3.11.4/x64
LD_LIBRARY_PATH: /opt/hostedtoolcache/Python/3.11.4/x64/lib:/home/runner/work/Gittyup/Qt/5.15.2/gcc_64/lib
PKG_CONFIG_PATH: /home/runner/work/Gittyup/Qt/5.15.2/gcc_64/lib/pkgconfig
Qt5_Dir: /home/runner/work/Gittyup/Qt/5.15.2/gcc_64
Qt5_DIR: /home/runner/work/Gittyup/Qt/5.15.2/gcc_64
QT_PLUGIN_PATH: /home/runner/work/Gittyup/Qt/5.15.2/gcc_64/plugins
QML2_IMPORT_PATH: /home/runner/work/Gittyup/Qt/5.15.2/gcc_64/qml
Error: Qt prefix not found
for flatpak we are doing in the cmake:
if(FLATPAK)
qt_import_plugins(gittyup INCLUDE ${QT_PLUGINS})
/*
The program is designed to find the Qt prefix path and Qt plugins path based on the given application path.
1. The program takes the application path as an argument.
2. The `GetQtPrefix` function is called to find the Qt prefix path:
- It first checks the `QTDIR` environment variable and returns its value if it is set.
- If the `QTDIR` environment variable is not set, it retrieves a list of possible `qt.conf` file paths.
- It reads each `qt.conf` file and extracts the Qt prefix path.
- If a Qt prefix path is found, it is returned.
- If no Qt prefix path is found, an empty string is returned.
3. The `GetQtPluginsPath` function is called to find the Qt plugins path:
- It first checks the `QT_PLUGIN_PATH` environment variable and returns its value if it is set.
- If the `QT_PLUGIN_PATH` environment variable is not set, it calls the `GetQtPrefix` function to get the Qt prefix path.
- If a Qt prefix path exists:
- It retrieves a list of possible `qt.conf` file paths.
- It reads each `qt.conf` file and extracts the Qt plugins path.
- If a Qt plugins path is found, it is returned.
- If no Qt plugins path is found, the custom plugins path is appended to the Qt prefix path.
- If no Qt prefix path exists, an error is returned.
4. The Qt QML patch is determined in a similar way.
5. The `resolvePath` function is used to handle path resolution:
- If the path is an absolute path, it is returned as is.
- If the path is a relative path and a Qt prefix exists, it is appended to the Qt prefix.
- If the path is a relative path and no Qt prefix exists, it is appended to the directory of the application.
6. Finally, in the `main` function:
- The application path is retrieved from the command-line arguments.
- The `GetQtPrefix` function is called to get the Qt prefix path.
- The `GetQtPluginsPath` function is called to get the Qt plugins path.
- The `GetQtQmlPath` function is called to get the Qt QML path.
- The identified paths are printed.
*/
package main
import (
"fmt"
"io/ioutil"
"os"
"path/filepath"
"strings"
)
func GetQtPrefix(appPath string) (string, error) {
qtPrefixEnv := os.Getenv("QTDIR")
if qtPrefixEnv != "" {
return qtPrefixEnv, nil
}
qtConfPaths := getPossibleQtConfPaths(appPath)
for _, qtConfPath := range qtConfPaths {
if qtConfPath == "" {
continue
}
qtPrefix := getQtPrefixFromConfFile(qtConfPath)
if qtPrefix != "" {
absPath, err := resolvePath(appPath, "", qtPrefix)
if err != nil {
return "", err
}
return absPath, nil
}
}
return "", nil
}
func GetQtPluginsPath(appPath string) (string, error) {
qtPluginsPathEnv := os.Getenv("QT_PLUGIN_PATH")
if qtPluginsPathEnv != "" {
return qtPluginsPathEnv, nil
}
qtPrefix, err := GetQtPrefix(appPath)
if err != nil {
return "", err
}
if qtPrefix == "" {
return "", fmt.Errorf("Qt prefix not found")
}
qtConfPaths := getPossibleQtConfPaths(appPath)
for _, qtConfPath := range qtConfPaths {
if qtConfPath == "" {
continue
}
qtPluginsPath := getQtPluginsPathFromConfFile(qtConfPath)
if qtPluginsPath != "" {
absPath, err := resolvePath(appPath, qtPrefix, qtPluginsPath)
if err != nil {
return "", err
}
return absPath, nil
}
}
// If no Qt plugins path found in the conf files, append "plugins" to the Qt prefix path
return filepath.Join(qtPrefix, "plugins"), nil
}
func getPossibleQtConfPaths(appPath string) []string {
paths := []string{
os.Getenv("QT_CONF_PATH"), // Environment variable
filepath.Join(filepath.Dir(appPath), "qt.conf"), // Next to the application
":/qt.conf", // Application resource directory
filepath.Join(os.Getenv("HOME"), ".config", "qt.conf"), // User's home directory
filepath.Join(os.Getenv("HOME"), ".local", "share", "qt.conf"), // User's home directory
"/etc/xdg/qt.conf", // System-wide configuration directory
"/usr/local/etc/qt.conf",
}
return paths
}
func getQtPrefixFromConfFile(qtConfPath string) string {
content, err := ioutil.ReadFile(qtConfPath)
if err != nil {
return ""
}
// Extract the Qt prefix from the qt.conf file
lines := strings.Split(string(content), "\n")
for _, line := range lines {
trimmedLine := strings.TrimSpace(line)
if strings.HasPrefix(trimmedLine, "Prefix") {
parts := strings.SplitN(trimmedLine, "=", 2)
if len(parts) == 2 {
key := strings.TrimSpace(parts[0])
value := strings.TrimSpace(parts[1])
if strings.TrimPrefix(key, "Prefix") == "" {
return value
}
}
}
}
return ""
}
func getQtPluginsPathFromConfFile(qtConfPath string) string {
qtPrefix, _ := GetQtPrefix(qtConfPath)
content, err := ioutil.ReadFile(qtConfPath)
if err != nil {
return ""
}
// Extract the Qt plugins path from the qt.conf file
lines := strings.Split(string(content), "\n")
for _, line := range lines {
trimmedLine := strings.TrimSpace(line)
if strings.HasPrefix(trimmedLine, "Plugins") {
parts := strings.SplitN(trimmedLine, "=", 2)
if len(parts) == 2 {
key := strings.TrimSpace(parts[0])
value := strings.TrimSpace(parts[1])
if strings.TrimPrefix(key, "Plugins") == "" {
if filepath.IsAbs(value) {
return value
}
// If path is a relative path and a Qt prefix exists, append it to the Qt prefix
if qtPrefix != "" {
return filepath.Join(qtPrefix, value)
}
return value
}
}
}
}
// If no Qt plugins path found in the conf files, use the default relative path
return "plugins"
}
func GetQtQmlPath(appPath string) (string, error) {
qmlDirectoryEnv := os.Getenv("QML2_IMPORT_PATH")
if qmlDirectoryEnv != "" {
return qmlDirectoryEnv, nil
}
qtPrefix, err := GetQtPrefix(appPath)
if err != nil {
return "", err
}
if qtPrefix == "" {
return "", fmt.Errorf("Qt prefix not found")
}
qtConfPaths := getPossibleQtConfPaths(appPath)
for _, qtConfPath := range qtConfPaths {
if qtConfPath == "" {
continue
}
qmlDirectory := getQtQmlPathFromConfFile(qtConfPath)
if qmlDirectory != "" {
absPath, err := resolvePath(appPath, qtPrefix, qmlDirectory)
if err != nil {
return "", err
}
return absPath, nil
}
}
// If no QML directory found in the conf files, use the default relative path
return filepath.Join(qtPrefix, "qml"), nil
}
func getQtQmlPathFromConfFile(qtConfPath string) string {
qtPrefix, _ := GetQtPrefix(qtConfPath)
content, err := ioutil.ReadFile(qtConfPath)
if err != nil {
return ""
}
// Extract the QML directory from the qt.conf file
lines := strings.Split(string(content), "\n")
for _, line := range lines {
trimmedLine := strings.TrimSpace(line)
if strings.HasPrefix(trimmedLine, "Qml2Imports") {
parts := strings.SplitN(trimmedLine, "=", 2)
if len(parts) == 2 {
key := strings.TrimSpace(parts[0])
value := strings.TrimSpace(parts[1])
if strings.TrimPrefix(key, "Qml2Imports") == "" {
if filepath.IsAbs(value) {
return value
}
// If path is a relative path and a Qt prefix exists, append it to the Qt prefix
if qtPrefix != "" {
return filepath.Join(qtPrefix, value)
}
return value
}
}
}
}
// If no QML directory found in the conf files, use the default relative path
return "qml"
}
func resolvePath(appPath, qtPrefix, path string) (string, error) {
if filepath.IsAbs(path) {
return path, nil
}
// If path is a relative path and a Qt prefix exists, append it to the Qt prefix
if qtPrefix != "" {
return filepath.Join(qtPrefix, path), nil
}
// If Qt prefix doesn't exist or path is an absolute path, append the relative path to the directory of the application
dir := filepath.Dir(appPath)
return filepath.Join(dir, path), nil
}
func main() {
if len(os.Args) < 2 {
fmt.Println("Please provide the path to the application as an argument.")
return
}
appPath := os.Args[1]
// Check if the supplied binary/program exists and is a file
fileInfo, err := os.Stat(appPath)
if os.IsNotExist(err) {
fmt.Println("Error: The specified application does not exist.")
return
}
if fileInfo.IsDir() {
fmt.Println("Error: The specified application path is a directory.")
return
}
qtPrefixPath, err := GetQtPrefix(appPath)
if err != nil {
fmt.Println("Error:", err)
return
}
qtPluginsPath, err := GetQtPluginsPath(appPath)
if err != nil {
fmt.Println("Error:", err)
return
}
qtQmlPath, err := GetQtQmlPath(appPath)
if err != nil {
fmt.Println("Error:", err)
return
}
fmt.Println("Qt prefix path:", qtPrefixPath)
fmt.Println("Qt plugins path:", qtPluginsPath)
fmt.Println("Qml path:", qtQmlPath)
}
Maybe this is overkill, and we should just run with QTDIR
if it is set.
For now, we are just relying on QTDIR
if it is set, as this seems to be
/opt/qt*/bin/qt*-env.sh
in the Qt builds from https://launchpad.net/~beinerito which folder does QTDIR point?
to which folder does QTDIR point?
The folder of your Qt install that contains /plugins
, /qml
,...
I try to use appimagetool for creating AppImage with Qt app by running following command:
appimagetool -s deploy ../install/usr/share/applications/*.desktop
. For the build of the app, I use pre-compiled Qt installed using aqtinstall.I've checked qt_prfxpath in libQtCore5.so in the same way as go-appimage detects it, and it is a path from the system where Qt was built, and it doesn't correspond to the path in the system where the app is built.
As result, creating AppImage fails:
Update: after hardcoding of qt prefix in source code & rebuild of appimagetool, deployment of successful.