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

CPack: Refactor AppImage and Apple DMG Generation #7252

Open
wants to merge 65 commits into
base: master
Choose a base branch
from

Conversation

tresf
Copy link
Member

@tresf tresf commented May 12, 2024

Features:

  • Simplifies Mac and Linux packaging command to simply: make package
    - make install && make appimage
    - make install && make dmg
    + make package
    # -- AppImage create: /__w/lmms/lmms/build/lmms-1.3.0-alpha.1.638+pr7252.gaaa9b4816-linux-x86_64.AppImage
  • Adds new WANT_DEBUG_CPACK flag to easily show detailed console messages about packaging
    cmake .. -DWANT_DEBUG_CPACK=true
      Developer options
      -----------------------------------------
      * Debug FP exceptions               : Disabled
      * Debug using AddressSanitizer      : Disabled
      * Debug using ThreadSanitizer       : Disabled
      * Debug using MemorySanitizer       : Disabled
      * Debug using UBSanitizer           : Disabled
    + * Debug packaging commands          : Enabled
    • Preserves old CPack TGZ behavior through a hidden flag, WANT_CPACK_TARBALL. Does anyone use this?
  • Adds new CPACK_TOOL parameter to switch between .AppImage (default) and .run (experimental)
    export CPACK_TOOL=makeself
    make package
    # -- Installer create: /__w/lmms/lmms/build/lmms-1.3.0-alpha.1.638+pr7252.gaaa9b4816-linux-x86_64.run

TODO:

  • Fix Carla crashing
  • Investigate possibly using the provided Mac DMG generators.
    • Port the install_apple.sh script to CMake.
      It's called MacDeployQt.cmake now.
  • Investigate possibility using CPack for creating Linux AppImages
    • Port the package_linux.sh script to Cmake
      It's called LinuxDeploy.cmake now.
  • Upgrade CMake for Linux runners

BUGS:

STRETCH GOALS:

Background:

Click to expand
  • The AppImage and Apple DMG installers historically (confusingly) require make install to be run prior to making packages. This PR is a proposal to remove that by switching to CPack.
    • Utilizes make package now, just like Windows does
    • Mac utilizes CPACK_GENERATOR of DragNDrop Bundle which will create a DMG without the need for node's appdmg package (still requires macdeployqt)
    • Linux will utilize CPACK_GENERATOR of "External", which will use custom CMake commands to replace package_linux.sh (still requires linuxdeployqt).

@tresf
Copy link
Member Author

tresf commented May 12, 2024

Rule out possibly using the provided Mac DMG generators.

I was able to get the CPack DMG generator to work with macdeployqt however in order for us to brand our own DMG, we either need to leverage AppleScript (which is much slower and requires user interaction over appdmg), or we need to provide a premade .DS_Store file to bundle.

Edit: For simplicity, I've checked in the .DS_Store from our current DMG. Since it will be unobvious how to update this file in the event of a rebranding, I've kept basic support for appdmg for recreating it, although it's not used or needed unless we rebrand the installer.

@tresf tresf changed the title CPack: Proof of concept for making Apple installers CPack: Refactor AppImage and Apple DMG Generation May 14, 2024
@tresf
Copy link
Member Author

tresf commented May 14, 2024

Hmm... the AppImage is missing... investigating...

@tresf
Copy link
Member Author

tresf commented May 14, 2024

The AppImage is missing because the CPack feature for running custom scripts through variable CPACK_PRE_BUILD_SCRIPTS was not available in Ubuntu 20.04. There doesn't seem to be a replacement for this feature, which is alarming, I wonder how people used "External" packaging techniques prior to CMake 3.19. Hmm...

@midi-pascal
Copy link
Contributor

Perhaps I have a hint for this:

I use Ubuntu 20.04 that comes with cmake version 3.16 BUT Qt (5.15.2 installed from Qt site) comes with cmake 3.27 in its tools directory. This is the one I use.

@tresf
Copy link
Member Author

tresf commented May 14, 2024

I use Ubuntu 20.04 that comes with cmake version 3.16 BUT Qt (5.15.2 installed from Qt site) comes with cmake 3.27 in its tools directory. This is the one I use.

Thanks! Any recommendation for getting this into a CI (command line?)

@midi-pascal
Copy link
Contributor

midi-pascal commented May 14, 2024

Not sure of what I will suggest below since I build using Qt Creator(so all is set automagically) but there are environment variables related to cmake:

CMAKE_PREFIX_PATH

CMAKE_PREFIX_PATH
Semicolon-separated list of directories specifying installation
prefixes to be searched by the find_package(),
find_program(), find_library(), find_file(), and
find_path() commands. Each command will add appropriate
subdirectories (like bin, lib, or include) as specified in its own
documentation.
By default this is empty. It is intended to be set by the project.
There is also an environment variable CMAKE_PREFIX_PATH, which is used
as an additional list of search prefixes.
See also CMAKE_SYSTEM_PREFIX_PATH, CMAKE_INCLUDE_PATH,
CMAKE_LIBRARY_PATH, CMAKE_PROGRAM_PATH, and
CMAKE_IGNORE_PATH.

Hope this can help :-)

@PhysSong
Copy link
Member

FYI, CMake homepage lists the official PPA and pip package as alternative installation methods.

@messmerd
Copy link
Member

@tresf I could update the Linux image with a newer CMake using the official PPA if you want. That might be easiest and also won't impact build times

@tresf
Copy link
Member Author

tresf commented May 15, 2024

@tresf I could update the Linux image with a newer CMake using the official PPA if you want. That might be easiest and also won't impact build times

If parties agree on this CPack strategy, yes please (or from snap).

Tested on Ubuntu 20.04:

sudo snap install cmake --classic
alias cmake='snap run cmake'
# cmake --version
# cmake version 3.29.3
#
# CMake suite maintained and supported by Kitware (kitware.com/cmake).

... I had reservations about whether or not the snap version of cmake would play nicely... and it seems to work just fine.

@tresf
Copy link
Member Author

tresf commented May 15, 2024

@tresf I could update the Linux image with a newer CMake using the official PPA if you want. That might be easiest and also won't impact build times

@messmerd yeah, I'll take that offer. 🤣

image

@Rossmaxx
Copy link
Contributor

can we use the PPA for mingw ci image too?

@messmerd
Copy link
Member

messmerd commented May 15, 2024

yeah, I'll take that offer. 🤣

I'll try to do it sometime today

can we use the PPA for mingw ci image too?

Yes, I think so, though I don't think it will be needed for this PR

@tresf
Copy link
Member Author

tresf commented May 15, 2024

Yes, I think so, though I don't think it will be needed for this PR

Actually, we can justify it here since bad79a8.

@tresf tresf marked this pull request as ready for review May 16, 2024 04:40
@tresf
Copy link
Member Author

tresf commented May 16, 2024

I've marked this ready for review because I think it's in pretty good shape. (artifacts should start generating once cmake is updated).

ARM64.AppImage.mp4

@tresf tresf mentioned this pull request May 16, 2024
@JohannesLorenz JohannesLorenz added this to the 1.3 milestone Aug 4, 2024
@messmerd
Copy link
Member

messmerd commented Oct 23, 2024

On Linux, Carla Rack and Carla Patchbay crash when I try to use them, and this is not the case on master

EDIT: Here's the command-line output:

Carla appears to be installed on this system at /usr/lib[64]/carla so we'll use it.
Jack appears to be installed on this system, so we'll use it.
Lv2 plugin SUMMARY: 18 of 40 loaded in 80 msecs.
For details about not loaded plugins, please set
environment variable "LMMS_LV2_DEBUG" to nonempty.
/tmp/.mount_lmms-1XSszgf/AppRun.wrapped: line 24: 800103 Segmentation fault (core dumped) QT_X11_NO_NATIVE_MENUBAR=1 "$DIR"/usr/bin/lmms "$@"

if(BASHCOMP_PKG_PATH)
# TODO: CMake 3.21 Use "file(COPY_FILE ...)"
install(CODE "
execute_process(COMMAND ${CMAKE_COMMAND} -E copy \"${SCRIPT_NAME}\" \"${BASHCOMP_PKG_PATH}\" ERROR_QUIET RESULT_VARIABLE result)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why does it install SCRIPT_NAME twice, once in line 47 and once in line 51?
Also, where is the BASHCOMP_PKG_PATH variable from?

Copy link
Member Author

@tresf tresf Oct 24, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why does it install SCRIPT_NAME twice, once in line 47 and once in line 51?

It's due to cmake's lack of conditional INSTALL(...) logic. For some reason, I removed the comment that describes this. It's here: 00d4ddd#diff-7960a523ea4d93951917fb0445e1145899f31c5a796e123ee06c805095e37221R59-R71.

In short:

  1. Always install to user-space
  2. Attempt to install system-wide, but don't fail the build if it doesn't work.

We did have some talks about this "system wide" install previously because this is the first time that we've had a component to install in a prefix that's outside of our own install prefix. I suppose we could instead detect a system-wide prefix and handle this in a more standard fashion. If that's the case, we can fix it as part of this PR if needed. I know some environments (such as Apple's Homebrew) ARE user-writable, so perhaps that was my motivation for this but that does NOT make sense to try when building a DMG or AppImage, so this logic is likely flawed.

Furthermore, I think there's a typo in the status message though.... I think this should say BASHCOMP_PKG_PATH not BASHCOMP_USER_PATH.

-						message(STATUS  "Unable to install bash-completion support system-wide: ${BASHCOMP_USER_PATH}/${SCRIPT_NAME})
+ -						message(STATUS  "Unable to install bash-completion support system-wide: ${BASHCOMP_PKG_PATH}/${SCRIPT_NAME})

Also, where is the BASHCOMP_PKG_PATH variable from?

It's from #4604. This is not a newly introduced variable with this PR. I believe its intended to be the completionsdir component, found by pkg-config. My memory is foggy from back then and the conversations seem to have largely occurred on Discord so decisions are not all obvious. Anyway, we also re-use this variable name for cmake's find_package(bash-completion) portion as well. It's probably a bad name, but I do not plan on changing it as part of this PR but can if needed.

Copy link
Contributor

@JohannesLorenz JohannesLorenz Oct 26, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe its intended to be the completionsdir component, found by pkg-config.

Indeed! The top of the file actually explains it. You can even set this variable using -DBASHCOMP_PKG_PATH=....

Furthermore, I think there's a typo in the status message though.... I think this should say BASHCOMP_PKG_PATH not BASHCOMP_USER_PATH.

Agreeing. Please feel free to fix.

Attempt to install system-wide, but don't fail the build if it doesn't work.

IMO, the natural behavior would be: If the user does a local install, install bashcomp locally. If they do a system install ("root"), install it system-wide. I assume that back then, we thought: "An install should always try to be system-wide, because local installed bashcomp is not sourced by your bashrc". However, you could give the same argument for the PATH variable (a local install of bin/lmms will never be in your PATH by default). In both cases, you can extend your bashrc to source a local bashcomp or to extend your PATH to a local directory. All in all, I would favor to do the install exactly one, and always in the normal install tree. However, it is just my basic opinion, and also it might be considered out of scope of this PR. Tagging @PhysSong because they were also active in that bashcomp PR in 2018.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the feedback. I'll put some time into fixing this.

@tresf
Copy link
Member Author

tresf commented Oct 25, 2024

On Linux, Carla Rack and Carla Patchbay crash when I try to use them, and this is not the case on master

Thanks! Should be fixed via aab4cc5.

@tresf
Copy link
Member Author

tresf commented Oct 25, 2024

On Linux, Carla Rack and Carla Patchbay crash when I try to use them, and this is not the case on master

Thanks! Should be fixed via aab4cc5.

Nevermind, still crashes. Investigating...

@messmerd
Copy link
Member

@tresf
There are 119 files and 2 directories under squashfs-root/usr/lib in this PR, but just 92 files and 1 directory in master.

I'm not sure why there are so many more, but of those extra files, there were libcarlabase.so and libcarla_native-plugin.so. After I removed them, Carla now longer crashed LMMS.

@tresf
Copy link
Member Author

tresf commented Oct 25, 2024

@tresf There are 119 files and 2 directories under squashfs-root/usr/lib in this PR, but just 92 files and 1 directory in master.

I'm not sure why there are so many more, but of those extra files, there were libcarlabase.so and libcarla_native-plugin.so. After I removed them, Carla now longer crashed LMMS.

@messmerd thank you. I'm fully aware of the cause of the crash. linuxdeploy is run twice. First using a qt plugin, which we first use to crawl dependencies and create the .AppDir structure, but then we call it a second time passing the --output appimage flag which we call only after manual cleanup of these carla libraries is performed. Instead of being a replacement for the appimagetool command, it re-runs the dependency tree, so any manually removed files are copied back in. Here's the offending line:

execute_process(COMMAND "${LINUXDEPLOY_BIN}"
--appdir "${APP}"
--output appimage
--verbosity ${VERBOSITY}
${OUTPUT_QUIET}
COMMAND_ECHO ${COMMAND_ECHO}
COMMAND_ERROR_IS_FATAL ANY)

There's a new --exclude-library flag which takes a globbing pattern that I'm experimenting with to see if it can replace the manual cleanup efforts.

@tresf
Copy link
Member Author

tresf commented Oct 25, 2024

Apologies for the whitespace changes in the latest commit 508f430, but spaces and tabs were inconsistent.

The relevant fix is the addition of ${EXCLUDES} variable which adds --exclude-library=pattern. It could be argued that the glob should be provided to both calls to linuxdeploy and the manual cleanup effort removed (rather than having two steps 1. cmake do the glob, 2. provide any found filenames to the --exclude-library flag) but at this point I'm afraid to change too much in fear of causing other issues.

Another big miss was never providing linuxdeploy the -executable= flags. We were building it but never passing it to linuxdeploy. This was would likely have caused some linking issues if not fixed prior to merging. (ignore the double dash change, that's just for readiblity).

There are 119 files and 2 directories under squashfs-root/usr/lib in this PR, but just 92 files and 1 directory in master.

I haven't done a comparison yet, but one thing that linuxdeploy does that linuxdeployqt did not is to attempt to fetch the licensing information for all dependencies. Most of these attempts fail when verbose logging is turned on, but it's one possible culprit. The second directory is a qt wrapper script exclusive to this tool.

I haven't tested the AppImage yet but I believe the Carla loading issue is at least resolved. I'm curious what the directory tree compare looks like. I used to use a commercial tool called "Beyond Compare" for comparing file trees, perhaps there's a FOSS equivalent.

@messmerd
Copy link
Member

I've confirmed that Carla no longer crashes.

However, it looks like there are copies of all the LMMS plugins and some other .so files in both usr/bin and usr/lib/lmms instead of just the latter.
And for at least the past couple commits, the LMMS icon hasn't been showing up on the AppImage file.

As a side note, I just realized xpressive is a whopping 71 MB. It's also always last to finish compiling in our CI builds, so maybe something should be done about that. (Though not in this PR of course)

@tresf
Copy link
Member Author

tresf commented Oct 25, 2024

Although undocumented, it appears appimagetool is available as part of the linuxdeploy tool, as can be observed when verbose logging WANT_DEBUG_CPACK is enabled. I may attempt to use this directly. I'll have to put a few more hours into it.

@tresf
Copy link
Member Author

tresf commented Dec 26, 2024

Although undocumented, it appears appimagetool is available as part of the linuxdeploy tool, as can be observed when verbose logging WANT_DEBUG_CPACK is enabled. I may attempt to use this directly. I'll have to put a few more hours into it.

This is really the way forward because otherwise, linuxdeploy runs the tree shaking again which takes so much time to run. Starting with b8950f4, I've reverted the project to use appimagetool again. This is how we do it on master and I'm hopeful that it will make builds quicker and and better management of what we do and don't bundle.

it looks like there are copies of all the LMMS plugins and some other .so files in both usr/bin and usr/lib/lmms instead of just the latter.

@messmerd Thanks for this find! The prophecy was foretold! So with both Mac and Linux installers -- for some reason -- I had linking issues unless I added certain plugins to linuxdeployqt|macdeployqt respectively, however, I believe the behavior has changed slightly with linuxdeploy and actually copies these to bin. Comically, this was already brought to question in this PR here: (nevermind, I'm was lib/lmms with lib/lmms/ladspa which are unrelated), this mystery still exists.

When this happens with Zyn, I move the files and symlink so the binary can still be found, but for plugins I feel like I can just blindly remove them.

  • I have a sneaking suspicion that they're needed to fix linking against Qt libs, but since they're plugins they'd normally not get fixed.
  • I think the reason that they're copied to bin is because of the explicit use of the --executable= notation. Perhaps using the --library= notation will fix this, but then I still fear that these files will be copied to lib instead, when they actually belong in lib/lmms.

I'll take a swing at this new --library= option and see how well it works. If it does the same thing as before, I can workaround any misplaced files just before calling appimagetool, since we're back to using that now, it shouldn't be so hard to shuffle files around.

@tresf
Copy link
Member Author

tresf commented Dec 26, 2024

this mystery still exists.

It turns out that this is caused by the dependency of libfftw. I'll update the comments.

then I still fear that these files will be copied to lib instead, when they actually belong in lib/lmms.

This is exactly what happens, it just ignores our directory structure for items we've added using --library=. I'll have to introduce some more variables to track where these were so we can safely move them back to the correct location. This is a bit of a regression from linuxdeployqt, but it shouldn't be too hard to patch.

@tresf
Copy link
Member Author

tresf commented Dec 26, 2024

I'll have to introduce some more variables to track where these were so we can safely move them back to the correct location. This is a bit of a regression from linuxdeployqt, but it shouldn't be too hard to patch.

It is hard to patch. Since it uses relative paths when relinking, when we move them back, it breaks any dependant links.

For example, take libamplifier.so, when the lib is placed in <appimage>/usr/lib/libamplifier.so, it properly links to libQt5Gui.so.5 => LMMS.AppDir/usr/lib/libQt5Gui.so.5. The moment I move it to <appimage>/usr/lib/lmms, linking breaks.

Maybe I can find a flag to preserve the existing directory structure. If not, dumping all libs into one directory is going to get ugly (both cosmetically as well as in our code that searches for plugins).

@tresf
Copy link
Member Author

tresf commented Dec 27, 2024

Maybe I can find a flag to preserve the existing directory structure. If not, dumping all libs into one directory is going to get ugly (both cosmetically as well as in our code that searches for plugins).

I could not find one. I tried to reproduce the directory structure with a bunch of symbolic links, but they still fail to find the bundled libraries due to how the paths are loaded. More information here: https://stackoverflow.com/questions/34332571/how-is-the-rpath-resolved-when-the-application-is-started-via-a-symlink.

This is very bad and I'm at a bit of a crossroads. I may need to beg linuxdeploy to allow a structure preservation feature because this just clobbers all .so's into /usr/lib, which I'm not sure we can maintain.

@messmerd
Copy link
Member

@tresf

I may need to beg linuxdeploy to allow a structure preservation feature because this just clobbers all .so's into /usr/lib, which I'm not sure we can maintain.

Could you open an issue upstream? Maybe it wouldn't be difficult for them to implement. It'd be better to have proper features/fixes upstream than any hacky workarounds here.

@tresf
Copy link
Member Author

tresf commented Jan 14, 2025

@tresf

I may need to beg linuxdeploy to allow a structure preservation feature because this just clobbers all .so's into /usr/lib, which I'm not sure we can maintain.

Could you open an issue upstream? Maybe it wouldn't be difficult for them to implement. It'd be better to have proper features/fixes upstream than any hacky workarounds here.

Yes, I had decided this was my next steps but hadn't updated this report. I'll crosslink when done.

@tresf
Copy link
Member Author

tresf commented Jan 14, 2025

Question raised upstream: linuxdeploy/linuxdeploy#305. If there's any wording there that's incorrect or can be improved, please let me know.

@tresf
Copy link
Member Author

tresf commented Jan 16, 2025

@tresf

I may need to beg linuxdeploy to allow a structure preservation feature because this just clobbers all .so's into /usr/lib, which I'm not sure we can maintain.

Could you open an issue upstream? Maybe it wouldn't be difficult for them to implement. It'd be better to have proper features/fixes upstream than any hacky workarounds here.

@messmerd no word from the upstream tracker or IRC (I hung out for the majority of a day, not one single reply). To move this PR forward, we may have to change the runtime code to find these plugins in /usr/lib. I don't love the idea of scanning unrelated files to populate our plugins listing, but we have to pick our battles.

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

Successfully merging this pull request may close these issues.

Request: ARM64 version for Raspberry Pi OS
6 participants