Important: Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

A story (and a few questions) about macdeployqt and PostgreSQL



  • Hello everyone!

    While investigating why on of our apps wouldn't run on the new macOS Big Sur, I stumbled upon some interesting things. This will be a very lengthy post and I apologize, but I don't want to hold back any details, so here we go:

    The problem was that the Qt PostgreSQL plugin could not be loaded since it didn't find its dependent libraries. The application still worked on older versions of macOs, even on machines that did not have PostgreSQL installed. The reason for that was that when the libqsqlpsql plugin loads its dependencies it loaded the libpq found under /usr/lib. I found out by setting this environment variable by calling

    export DYLD_PRINT_LIBRARIES_POST_LAUNCH=1

    Starting the application now outputs the libraries loaded after the app has launched (since the sqldrivers are plugins which are loaded after the application has started this is sufficient).

    Sadly Apple removed the libpq from its OS in the latest update.

    For deploying our app, we use macdeployqt on the application bundle which correctly puts the sqldriver plugins and the dependent client libraries into the bundle. Well, at least when you have those client libraries installed in the right path. (For anyone looking for this, PostgreSQL has to be installed under /Application/Postgres.app in the version 9.6, since apparently this is how the libqsqlpsql plugin is linked)

    For my test application this results in the following structure (I omitted some paths but the important thing is /Contents/Frameworks/libpq.5.dylib):

    psql-plugin-test.app
    └── Contents
        ├── Frameworks
        │   ├── QtCore.framework
        │   ├── QtDBus.framework
        │   ├── QtGui.framework
        │   ├── QtPrintSupport.framework
        │   ├── QtSql.framework
        │   ├── QtSvg.framework
        │   ├── QtWidgets.framework
        │   ├── libiodbc.2.dylib
        │   └── libpq.5.dylib
        ├── Info.plist
        ├── MacOS
        │   └── psql-plugin-test
        ├── PkgInfo
        ├── PlugIns
        │   ├── iconengines
        │   ├── imageformats
        │   ├── platforms
        │   ├── printsupport
        │   ├── sqldrivers
        │   │   ├── libqsqlite.dylib
        │   │   ├── libqsqlodbc.dylib
        │   │   └── libqsqlpsql.dylib
        │   └── styles
        └── Resources
            ├── empty.lproj
            └── qt.conf
    

    Nice!
    However when inspecting a little further I found out that libpq.5.dylib is never used.

    otool -L psql-plugin-test.app/Contents/PlugIns/sqldrivers/libqsqlpsql.dylib
    gives the following:

    libqsqlpsql.dylib (compatibility version 0.0.0, current version 0.0.0)
    @rpath/QtSql.framework/Versions/5/QtSql (compatibility version 5.15.0, current version 5.15.1)
    @rpath/QtCore.framework/Versions/5/QtCore (compatibility version 5.15.0, current version 5.15.1)
    /System/Library/Frameworks/DiskArbitration.framework/Versions/A/DiskArbitration (compatibility version 1.0.0, current version 1.0.0)
    /System/Library/Frameworks/IOKit.framework/Versions/A/IOKit (compatibility version 1.0.0, current version 275.0.0)
    /Applications/Postgres.app/Contents/Versions/9.6/lib/libpq.5.dylib (compatibility version 5.0.0, current version 5.9.0)
    /usr/lib/libc++.1.dylib (compatibility version 1.0.0, current version 400.9.4)
    /usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 1252.200.5)
    

    The reference to libpq is still the one from the installed version under /Applications.

    The reason for that can be found by looking at the output of macdeployqt: macdeployqt.log Almost at the end it says the following:

    Log:  copied: "/Applications/Postgres.app/Contents/Versions/9.6/lib/libpq.5.dylib"
    Log:  to "psql-plugin-test.app/Contents/Frameworks//libpq.5.dylib"
    Log: Using install_name_tool:
    Log:  in "psql-plugin-test.app/Contents/PlugIns/sqldrivers/libqsqlpsql.dylib"
    Log:  change reference "libpq.5.dylib"
    Log:  to "@executable_path/../Frameworks/libpq.5.dylib"
    

    Interesting, it copies the libpq.dyliband then tries to update the reference in libqsqlpsql.dylibby using just the filename. install_name_tool doesn't find that reference and therefore doesn't change anything. Remember: the old reference is the whole path under /Applications/Postgres.app.

    This seemed like a bug to me, so I looked into it myself. I tweaked one line in the sources of macdeployqt where the arguments for the call to install_name_tool were determined and it worked! otool on the psql plugin now outputs the correct path @executable_path/../Frameworks/libpq.5.dylib.

    However my test app still didn't work... because libpq depends on libssl and librcypto and macdeployqt doesn't seem to care about dependencies of dependencies. That's when I realized (once again) that deploying on a Mac is a pain in the a** and why I wrote this post.

    Here come my questions:

    • Is my finding worth a bug report? Or even a pull request? I didn't contribute to Qt before but I'm willing to.
    • If I commit my changes how can I make sure they work in other environments? I noticed that the unit test for macdeployqt is rather sparse. The test doesn't even try to deploy plugins.
    • Is macdeployqt expected to deploy dependencies of dependencies?
    • How do you handle deployment on macOS? Are there any better alternatives? Or am I just missing something?

    I would love to hear from you since I really put some effort into this and would love to get this issue out of my life 😄

    Thanks
    Tobi


  • Lifetime Qt Champion

    Hi,

    Bug report and patch submission ? Sure ! Go on for both. Less bugs is more happiness for developers and you'll have the fix sooner integrated.

    What other environments do you have in mind ?

    For the dependencies of dependencies, that's a good question I can't currently answer, sorry. You can ask that on the bug report when you post it.

    As for better alternatives, I am not sure cmake allows for better automation of that part.



  • @SGaist Thanks for your quick answer!

    At this point I'm very unsure about the "bug fix" since it may have helped in my situation but it may have made other situations worse. In the manual tests I made it all worked out but I'm not sure if that's always the case. But that would be a discussion for the Gerrit review board right?

    With "other environments" I mean other people's setup like installed software, environment variables and so on.

    Yeah I thought about cmake as well but that still means a lot of work in order to get it to do the deployment correctly.



  • @tobias-reuen I also want to encourage you to open a bug report. It seems to me that if you post your information into https://bugreports.qt.io it will reach an audience of people who tend to "hack on" Qt itself. (Whereas this forum tends to contain more people who hack on other things. They "hack with" Qt, but not on it).

    Great sleuthing so far.

    You also might try the developer mailing list: https://lists.qt-project.org/listinfo/development

    As you said, you put some non-trivial effort into this (it's impressive!). Please share it far and wide :)


  • Lifetime Qt Champion

    @tobias-reuen said in A story (and a few questions) about macdeployqt and PostgreSQL:

    At this point I'm very unsure about the "bug fix" since it may have helped in my situation but it may have made other situations worse. In the manual tests I made it all worked out but I'm not sure if that's always the case. But that would be a discussion for the Gerrit review board right?

    Indeed, not that there might already be a report for that issue. Just check for it before opening a new one. Then fire of the patch :-)



  • Hi, thanks for your post, I was pulling my hair on this one.
    Was the bug reported in the end?


Log in to reply