CPack: Refactor AppImage and Apple DMG Generation (#7252)

* CPack: Refactor AppImage and Apple DMG Generation
* Switch from linuxdeployqt to linuxdelpoy
* Add ARM64 AppImage support
* Add support for `.run` installers using `makeself`, an alternative to AppImage
* Refactor BashCompletion.cmake
* Enable CPack debugging via `WANT_DEBUG_CPACK`
* Add `download_binary`, `create_symlink` macros
* Qt6: Fix @rpath bug on macOS
* Detect and bundle LV2 UI Suil modules (Related #7201)
* Allow remote plugins to honor `LMMS_PLUGIN_DIR`
* Add .github/workflows/deps-ubuntu-24.04-gcc.txt
* Fix waveforms FileDialog

Co-authored-by: Dalton Messmer <messmer.dalton@gmail.com>
This commit is contained in:
Tres Finocchiaro
2025-02-01 04:02:19 -05:00
committed by GitHub
parent 77ca0f8994
commit 10bdf122f8
30 changed files with 1069 additions and 510 deletions

View File

@@ -5,8 +5,8 @@ concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs:
linux:
name: linux
linux-x86_64:
name: linux-x86_64
runs-on: ubuntu-latest
container: ghcr.io/lmms/linux.gcc:20.04
env:
@@ -39,7 +39,6 @@ jobs:
source /opt/qt5*/bin/qt5*-env.sh || true
cmake -S . \
-B build \
-DCMAKE_INSTALL_PREFIX=./install \
$CMAKE_OPTS
- name: Build
run: cmake --build build
@@ -49,8 +48,7 @@ jobs:
ctest --output-on-failure -j2
- name: Package
run: |
cmake --build build --target install
cmake --build build --target appimage
cmake --build build --target package
- name: Upload artifacts
uses: actions/upload-artifact@v4
with:
@@ -65,6 +63,71 @@ jobs:
ccache --show-stats
env:
CCACHE_MAXSIZE: 500M
linux-arm64:
name: linux-arm64
runs-on: ubuntu-24.04-arm
env:
CMAKE_OPTS: >-
-DUSE_WERROR=ON
-DCMAKE_BUILD_TYPE=RelWithDebInfo
-DUSE_COMPILE_CACHE=ON
-DWANT_DEBUG_CPACK=ON
CCACHE_MAXSIZE: 0
CCACHE_NOCOMPRESS: 1
MAKEFLAGS: -j2
DEBIAN_FRONTEND: noninteractive
steps:
- name: Configure git
run: git config --global --add safe.directory "$GITHUB_WORKSPACE"
- name: Check out
uses: actions/checkout@v3
with:
fetch-depth: 0
submodules: recursive
- name: Install system packages
run: |
sudo apt-get update -y
sudo apt-get install -y --no-install-recommends \
$(xargs < .github/workflows/deps-ubuntu-24.04-gcc.txt)
- name: Cache ccache data
uses: actions/cache@v3
with:
key: ccache-${{ github.job }}-${{ github.ref }}-${{ github.run_id }}
restore-keys: |
ccache-${{ github.job }}-${{ github.ref }}-
ccache-${{ github.job }}-
path: ~/.ccache
- name: Configure
run: |
ccache --zero-stats
cmake -S . \
-B build \
-DWANT_VST_32=OFF \
-DWANT_VST_64=OFF \
$CMAKE_OPTS
- name: Build
run: cmake --build build
- name: Run tests
run: |
cd build/tests
ctest --output-on-failure -j2
- name: Package
run: |
cmake --build build --target package
- name: Upload artifacts
uses: actions/upload-artifact@v4
with:
name: linux-arm64
path: build/lmms-*.AppImage
- name: Trim ccache and print statistics
run: |
ccache --cleanup
echo "[ccache config]"
ccache --show-config
echo "[ccache stats]"
ccache --show-stats
env:
CCACHE_MAXSIZE: 500M
macos:
strategy:
fail-fast: false
@@ -129,7 +192,6 @@ jobs:
mkdir build
cmake -S . \
-B build \
-DCMAKE_INSTALL_PREFIX="../target" \
-DCMAKE_PREFIX_PATH="$(brew --prefix qt@5)" \
-DCMAKE_OSX_ARCHITECTURES=${{ matrix.arch }} \
$CMAKE_OPTS \
@@ -142,8 +204,7 @@ jobs:
ctest --output-on-failure -j3
- name: Package
run: |
cmake --build build --target install
cmake --build build --target dmg
cmake --build build --target package
- name: Upload artifacts
uses: actions/upload-artifact@v4
with:
@@ -208,7 +269,6 @@ jobs:
ccache --zero-stats
cmake -S . \
-B build \
-DCMAKE_INSTALL_PREFIX=./install \
-DCMAKE_TOOLCHAIN_FILE="./cmake/toolchains/MinGW-W64-64.cmake" \
$CMAKE_OPTS
- name: Build

View File

@@ -0,0 +1,52 @@
binutils
ca-certificates
ccache
cmake
file
fluid
gcc
git
gpg
g++
libasound2-dev
libc6-dev
libfftw3-dev
libfltk1.3-dev
libfluidsynth-dev
libgig-dev
libgtk2.0-0
libjack-jackd2-dev
liblilv-dev
liblist-moreutils-perl
libmp3lame-dev
libogg-dev
libqt5svg5-dev
libqt5x11extras5-dev
libsamplerate0-dev
libsdl2-dev
libsndfile1-dev
libsoundio-dev
libstk-dev
libsuil-dev
libvorbis-dev
libx11-xcb-dev
libxcb-keysyms1-dev
libxcb-util0-dev
libxft-dev
libxinerama-dev
libxml2-utils
libxml-perl
lsb-release
lv2-dev
make
perl
portaudio19-dev
qt5-qmake
qtbase5-dev
qtbase5-dev-tools
qtbase5-private-dev
qttools5-dev-tools
software-properties-common
ssh-client
stk
wget

View File

@@ -104,6 +104,8 @@ option(WANT_DEBUG_MSAN "Enable MemorySanitizer" OFF)
option(WANT_DEBUG_UBSAN "Enable UndefinedBehaviorSanitizer" OFF)
option(WANT_DEBUG_GPROF "Enable gprof profiler" OFF)
OPTION(BUNDLE_QT_TRANSLATIONS "Install Qt translation files for LMMS" OFF)
option(WANT_DEBUG_CPACK "Show detailed logs for packaging commands" OFF)
option(WANT_CPACK_TARBALL "Request CPack to create a tarball instead of an installer" OFF)
IF(LMMS_BUILD_APPLE)
@@ -268,6 +270,7 @@ IF(WANT_SUIL)
IF(SUIL_FOUND)
SET(LMMS_HAVE_SUIL TRUE)
SET(STATUS_SUIL "OK")
find_package(SuilModules)
ELSE()
SET(STATUS_SUIL "not found, install it or set PKG_CONFIG_PATH appropriately")
ENDIF()
@@ -606,6 +609,17 @@ ELSE()
SET (STATUS_DEBUG_FPE "Disabled")
ENDIF(WANT_DEBUG_FPE)
if(WANT_DEBUG_CPACK)
if((LMMS_BUILD_WIN32 AND CMAKE_VERSION VERSION_LESS "3.19") OR WANT_CPACK_TARBALL)
set(STATUS_DEBUG_CPACK "Wanted but disabled due to unsupported configuration")
else()
set(CPACK_DEBUG TRUE)
set(STATUS_DEBUG_CPACK "Enabled")
endif()
else()
set(STATUS_DEBUG_CPACK "Disabled")
endif()
# check for libsamplerate
FIND_PACKAGE(Samplerate 0.1.8 MODULE REQUIRED)
@@ -679,7 +693,7 @@ if(WANT_DEBUG_GPROF)
add_link_options(-pg)
set(STATUS_GPROF "OK")
else()
set(STATUS_GPROF "DISABLED ${STATUS_GPROF}")
set(STATUS_GPROF "Disabled ${STATUS_GPROF}")
endif()
# add enabled sanitizers
@@ -745,7 +759,7 @@ IF(LMMS_BUILD_LINUX)
"${CMAKE_BINARY_DIR}/lmmsconfig.h"
"${CMAKE_BINARY_DIR}/lmmsversion.h"
"${CMAKE_SOURCE_DIR}/src/gui/embed.cpp"
DESTINATION "${CMAKE_INSTALL_PREFIX}/include/lmms/")
DESTINATION "include/lmms/")
ENDIF(LMMS_BUILD_LINUX)
#
@@ -849,6 +863,7 @@ MESSAGE(
"* Debug using ThreadSanitizer : ${STATUS_DEBUG_TSAN}\n"
"* Debug using MemorySanitizer : ${STATUS_DEBUG_MSAN}\n"
"* Debug using UBSanitizer : ${STATUS_DEBUG_UBSAN}\n"
"* Debug packaging commands : ${STATUS_DEBUG_CPACK}\n"
"* Profile using GNU profiler : ${STATUS_GPROF}\n"
)

View File

@@ -14,10 +14,14 @@ ENDIF()
SET(CPACK_PACKAGE_INSTALL_DIRECTORY "${PROJECT_NAME_UCASE}")
SET(CPACK_SOURCE_GENERATOR "TBZ2")
SET(CPACK_SOURCE_PACKAGE_FILE_NAME "${CMAKE_PROJECT_NAME}-${VERSION}")
IF(NOT DEFINED WIN32)
SET(CPACK_STRIP_FILES "bin/${CMAKE_PROJECT_NAME};${PLUGIN_DIR}/*.so")
SET(CPACK_PACKAGE_EXECUTABLES "${CMAKE_PROJECT_NAME}" "${PROJECT_NAME_UCASE} binary")
ENDIF()
SET(CPACK_PACKAGE_EXECUTABLES "${CMAKE_PROJECT_NAME}" "${PROJECT_NAME_UCASE} binary")
# Disable strip for Debug|RelWithDebInfo
if(CMAKE_BUILD_TYPE MATCHES "Deb")
unset(CPACK_STRIP_FILES)
else()
set(CPACK_STRIP_FILES TRUE)
endif()
IF(LMMS_BUILD_WIN32)
ADD_SUBDIRECTORY(nsis)

View File

@@ -1,24 +1,3 @@
SET(MACOSX_BUNDLE_ICON_FILE "icon.icns")
SET(MACOSX_BUNDLE_GUI_IDENTIFIER "${PROJECT_NAME_UCASE}")
SET(MACOSX_BUNDLE_LONG_VERSION_STRING "${VERSION}")
SET(MACOSX_BUNDLE_BUNDLE_NAME "${PROJECT_NAME_UCASE}")
SET(MACOSX_BUNDLE_SHORT_VERSION_STRING "${VERSION}")
SET(MACOSX_BUNDLE_BUNDLE_VERSION "${VERSION}")
SET(MACOSX_BUNDLE_COPYRIGHT "${PROJECT_COPYRIGHT}")
SET(MACOSX_BUNDLE_MIMETYPE "application/x-lmms-project")
SET(MACOSX_BUNDLE_MIMETYPE_ICON "project.icns")
SET(MACOSX_BUNDLE_MIMETYPE_ID "io.lmms")
SET(MACOSX_BUNDLE_PROJECT_URL "${PROJECT_URL}")
SET(MACOSX_BUNDLE_DMG_TITLE "${MACOSX_BUNDLE_BUNDLE_NAME} ${MACOSX_BUNDLE_LONG_VERSION_STRING}")
# FIXME: appdmg won't allow volume names > 27 char
# See also https://github.com/LinusU/node-appdmg/issues/48
STRING(SUBSTRING "${MACOSX_BUNDLE_DMG_TITLE}" 0 27 MACOSX_BUNDLE_DMG_TITLE)
CONFIGURE_FILE("lmms.plist.in" "${CMAKE_BINARY_DIR}/Info.plist")
CONFIGURE_FILE("install_apple.sh.in" "${CMAKE_BINARY_DIR}/install_apple.sh" @ONLY)
CONFIGURE_FILE("package_apple.json.in" "${CMAKE_BINARY_DIR}/package_apple.json" @ONLY)
IF(CMAKE_OSX_ARCHITECTURES)
SET(DMG_ARCH "${CMAKE_OSX_ARCHITECTURES}")
ELSEIF(IS_ARM64)
@@ -29,18 +8,31 @@ ELSE()
SET(DMG_ARCH "x86_64")
ENDIF()
# DMG creation target
SET(DMG_FILE "${CMAKE_BINARY_DIR}/${CMAKE_PROJECT_NAME}-${VERSION}-mac${APPLE_OS_VER}-${DMG_ARCH}.dmg")
# Standard CPack options
set(CPACK_GENERATOR "External" PARENT_SCOPE)
set(CPACK_EXTERNAL_ENABLE_STAGING TRUE PARENT_SCOPE)
set(CPACK_PACKAGE_FILE_NAME "${CMAKE_PROJECT_NAME}-${VERSION}-mac${APPLE_OS_VER}-${DMG_ARCH}")
set(CPACK_PACKAGE_FILE_NAME "${CPACK_PACKAGE_FILE_NAME}" PARENT_SCOPE)
set(CPACK_PRE_BUILD_SCRIPTS "${CMAKE_CURRENT_SOURCE_DIR}/MacDeployQt.cmake" PARENT_SCOPE)
# disable cpack's strip: causes missing symbols on macOS
set(CPACK_STRIP_FILES_ORIG "${CPACK_STRIP_FILES}" PARENT_SCOPE)
set(CPACK_STRIP_FILES FALSE PARENT_SCOPE)
FILE(REMOVE "${DMG_FILE}")
ADD_CUSTOM_TARGET(removedmg
COMMAND touch "\"${DMG_FILE}\"" && rm "\"${DMG_FILE}\""
COMMENT "Removing old DMG")
ADD_CUSTOM_TARGET(dmg
COMMAND appdmg "\"${CMAKE_BINARY_DIR}/package_apple.json\"" "\"${DMG_FILE}\""
DEPENDS "${CMAKE_BINARY_DIR}/package_apple.json"
COMMENT "Generating DMG")
ADD_DEPENDENCIES(dmg removedmg)
# see also ../postinstall/CMakeLists.txt
# Custom vars to expose to Cpack
# must be prefixed with "CPACK_" per https://stackoverflow.com/a/46526757/3196753)
set(CPACK_CURRENT_BINARY_DIR "${CMAKE_CURRENT_BINARY_DIR}" PARENT_SCOPE)
set(CPACK_CURRENT_SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}" PARENT_SCOPE)
set(CPACK_BINARY_DIR "${CMAKE_BINARY_DIR}" PARENT_SCOPE)
set(CPACK_SOURCE_DIR "${CMAKE_SOURCE_DIR}" PARENT_SCOPE)
set(CPACK_QMAKE_EXECUTABLE "${QT_QMAKE_EXECUTABLE}" PARENT_SCOPE)
set(CPACK_CARLA_LIBRARIES "${CARLA_LIBRARIES}" PARENT_SCOPE)
set(CPACK_PROJECT_NAME "${PROJECT_NAME}" PARENT_SCOPE)
set(CPACK_PROJECT_VERSION "${VERSION}" PARENT_SCOPE)
set(CPACK_PROJECT_NAME_UCASE "${PROJECT_NAME_UCASE}" PARENT_SCOPE)
set(CPACK_PROJECT_URL "${PROJECT_URL}" PARENT_SCOPE)
set(CPACK_SUIL_MODULES "${Suil_MODULES}" PARENT_SCOPE)
set(CPACK_SUIL_MODULES_PREFIX "${Suil_MODULES_PREFIX}" PARENT_SCOPE)
if(CMAKE_VERSION VERSION_LESS "3.19")
message(WARNING "DMG creation requires at least CMake 3.19")
endif()

View File

@@ -0,0 +1,172 @@
# Create a macOS .dmg desktop installer using macdeployqt
#
# Copyright (c) 2025, Tres Finocchiaro, <tres.finocchiaro@gmail.com>
#
# Redistribution and use is allowed according to the terms of the BSD license.
# For details see the accompanying COPYING-CMAKE-SCRIPTS file.
# Variables must be prefixed with "CPACK_" to be visible here
set(lmms "${CPACK_PROJECT_NAME}")
set(APP "${CPACK_TEMPORARY_INSTALL_DIRECTORY}/${CPACK_PROJECT_NAME_UCASE}.app")
# Toggle command echoing & verbosity
# 0 = no output, 1 = error/warning, 2 = normal, 3 = debug
if(NOT CPACK_DEBUG)
set(VERBOSITY 1)
set(COMMAND_ECHO NONE)
else()
set(VERBOSITY 2)
set(COMMAND_ECHO STDOUT)
endif()
# Detect release|debug build
if(NOT CPACK_STRIP_FILES_ORIG)
# -use-debug-libs implies -no-strip
if(CPACK_QMAKE_EXECUTABLE MATCHES "/homebrew/|/usr/local")
message(STATUS "Homebrew does not provide debug qt libraries, replacing \"-use-debug-libs\" with \"-no-strip\" instead")
set(USE_DEBUG_LIBS -no-strip)
else()
set(USE_DEBUG_LIBS -use-debug-libs)
endif()
endif()
# Cleanup CPack "External" json, txt files, old DMG files
file(GLOB cleanup "${CPACK_BINARY_DIR}/${lmms}-*.json"
"${CPACK_BINARY_DIR}/${lmms}-*.dmg"
"${CPACK_BINARY_DIR}/install_manifest.txt")
list(SORT cleanup)
file(REMOVE ${cleanup})
# Create bundle structure
file(MAKE_DIRECTORY "${APP}/Contents/MacOS")
file(MAKE_DIRECTORY "${APP}/Contents/Frameworks")
file(MAKE_DIRECTORY "${APP}/Contents/Resources")
# Fix layout
file(RENAME "${CPACK_TEMPORARY_INSTALL_DIRECTORY}/lib" "${APP}/Contents/lib")
file(RENAME "${CPACK_TEMPORARY_INSTALL_DIRECTORY}/share" "${APP}/Contents/share")
file(RENAME "${CPACK_TEMPORARY_INSTALL_DIRECTORY}/bin" "${APP}/Contents/bin")
# Move binaries into Contents/MacOS
file(RENAME "${APP}/Contents/bin/${lmms}" "${APP}/Contents/MacOS/${lmms}")
file(RENAME "${APP}/Contents/lib/${lmms}/RemoteZynAddSubFx" "${APP}/Contents/MacOS/RemoteZynAddSubFx")
file(REMOVE_RECURSE "${APP}/Contents/bin")
file(REMOVE_RECURSE "${APP}/Contents/share/man1")
file(REMOVE_RECURSE "${APP}/Contents/include")
# Copy missing files
# Convert https://lmms.io to io.lmms
string(REPLACE "." ";" mime_parts "${CPACK_PROJECT_URL}")
string(REPLACE ":" ";" mime_parts "${mime_parts}")
string(REPLACE "/" "" mime_parts "${mime_parts}")
list(REMOVE_AT mime_parts 0)
list(REVERSE mime_parts)
list(JOIN mime_parts "." MACOS_MIMETYPE_ID)
configure_file("${CPACK_CURRENT_SOURCE_DIR}/lmms.plist.in" "${APP}/Contents/Info.plist" @ONLY)
file(COPY "${CPACK_CURRENT_SOURCE_DIR}/project.icns" DESTINATION "${APP}/Contents/Resources")
file(COPY "${CPACK_CURRENT_SOURCE_DIR}/icon.icns" DESTINATION "${APP}/Contents/Resources")
file(RENAME "${APP}/Contents/Resources/icon.icns" "${APP}/Contents/Resources/${lmms}.icns")
# Copy Suil modules
if(CPACK_SUIL_MODULES)
set(SUIL_MODULES_TARGET "${APP}/Contents/Frameworks/${CPACK_SUIL_MODULES_PREFIX}")
file(MAKE_DIRECTORY "${SUIL_MODULES_TARGET}")
file(COPY ${CPACK_SUIL_MODULES} DESTINATION "${SUIL_MODULES_TARGET}")
endif()
# Make all libraries writable for macdeployqt
file(CHMOD_RECURSE "${APP}/Contents" PERMISSIONS
OWNER_EXECUTE OWNER_WRITE OWNER_READ
GROUP_EXECUTE GROUP_WRITE GROUP_READ
WORLD_READ)
# Qt6: Fix @rpath bug https://github.com/orgs/Homebrew/discussions/2823
include(CreateSymlink)
get_filename_component(QTBIN "${CPACK_QMAKE_EXECUTABLE}" DIRECTORY)
get_filename_component(QTDIR "${QTBIN}" DIRECTORY)
create_symlink("${QTDIR}/lib" "${CPACK_TEMPORARY_INSTALL_DIRECTORY}/lib")
# Replace @rpath with @loader_path for Carla
execute_process(COMMAND install_name_tool -change
"@rpath/libcarlabase.dylib"
"@loader_path/libcarlabase.dylib"
"${APP}/Contents/lib/${lmms}/libcarlapatchbay.so"
COMMAND_ECHO ${COMMAND_ECHO}
COMMAND_ERROR_IS_FATAL ANY)
execute_process(COMMAND install_name_tool -change
"@rpath/libcarlabase.dylib"
"@loader_path/libcarlabase.dylib"
"${APP}/Contents/lib/${lmms}/libcarlarack.so"
COMMAND_ECHO ${COMMAND_ECHO}
COMMAND_ERROR_IS_FATAL ANY)
# Build list of executables to inform macdeployqt about
# e.g. -executable=foo.dylib -executable=bar.dylib
file(GLOB LIBS "${APP}/Contents/lib/${lmms}/*.so")
# Inform macdeployqt about LADSPA plugins; may depend on bundled fftw3f, etc.
file(GLOB LADSPA "${APP}/Contents/lib/${lmms}/ladspa/*.so")
# Inform linuxdeploy about remote plugins
list(APPEND REMOTE_PLUGINS "${APP}/Contents/MacOS/*Remote*")
# Collect, sort and dedupe all libraries
list(APPEND LIBS ${LADSPA})
list(APPEND LIBS ${REMOTE_PLUGINS})
list(APPEND LIBS ${CPACK_SUIL_MODULES})
list(REMOVE_DUPLICATES LIBS)
list(SORT LIBS)
# Construct macdeployqt parameters
foreach(_lib IN LISTS LIBS)
if(EXISTS "${_lib}")
list(APPEND EXECUTABLES "-executable=${_lib}")
endif()
endforeach()
# Call macdeployqt
get_filename_component(QTBIN "${CPACK_QMAKE_EXECUTABLE}" DIRECTORY)
message(STATUS "Calling ${QTBIN}/macdeployqt ${APP} [... executables]")
execute_process(COMMAND "${QTBIN}/macdeployqt" "${APP}" ${EXECUTABLES}
-verbose=${VERBOSITY}
${USE_DEBUG_LIBS}
COMMAND_ECHO ${COMMAND_ECHO}
COMMAND_ERROR_IS_FATAL ANY)
# Cleanup symlink
file(REMOVE "${CPACK_TEMPORARY_INSTALL_DIRECTORY}/lib")
# Remove dummy carla libs, relink to a sane location (e.g. /Applications/Carla.app/...)
# (must be done after calling macdeployqt)
file(GLOB CARLALIBS "${APP}/Contents/lib/${lmms}/libcarla*")
foreach(_carlalib IN LISTS CARLALIBS)
foreach(_lib "${CPACK_CARLA_LIBRARIES}")
set(_oldpath "../../Frameworks/lib${_lib}.dylib")
set(_newpath "Carla.app/Contents/MacOS/lib${_lib}.dylib")
execute_process(COMMAND install_name_tool -change
"@loader_path/${_oldpath}"
"@executable_path/../../../${_newpath}"
"${_carlalib}"
COMMAND_ECHO ${COMMAND_ECHO}
COMMAND_ERROR_IS_FATAL ANY)
file(REMOVE "${APP}/Contents/Frameworks/lib${_lib}.dylib")
endforeach()
endforeach()
# Call ad-hoc codesign manually (CMake offers this as well)
execute_process(COMMAND codesign --force --deep --sign - "${APP}"
COMMAND_ECHO ${COMMAND_ECHO}
COMMAND_ERROR_IS_FATAL ANY)
# Create DMG
# appdmg won't allow volume names > 27 char https://github.com/LinusU/node-alias/issues/7
find_program(APPDMG_BIN appdmg REQUIRED)
string(SUBSTRING "${CPACK_PROJECT_NAME_UCASE} ${CPACK_PROJECT_VERSION}" 0 27 APPDMG_VOLUME_NAME)
# We'll configure this file twice (again in MacDeployQt.cmake once we know CPACK_TEMPORARY_INSTALL_DIRECTORY)
configure_file("${CPACK_CURRENT_SOURCE_DIR}/appdmg.json.in" "${CPACK_CURRENT_BINARY_DIR}/appdmg.json" @ONLY)
execute_process(COMMAND "${APPDMG_BIN}"
"${CPACK_CURRENT_BINARY_DIR}/appdmg.json"
"${CPACK_BINARY_DIR}/${CPACK_PACKAGE_FILE_NAME}.dmg"
COMMAND_ECHO ${COMMAND_ECHO}
COMMAND_ERROR_IS_FATAL ANY)

View File

@@ -0,0 +1,9 @@
{
"title": "@APPDMG_VOLUME_NAME@",
"background": "@CPACK_SOURCE_DIR@/cmake/apple/background.png",
"icon-size": 128,
"contents": [
{ "x": 139, "y": 200, "type": "file", "path": "@CPACK_TEMPORARY_INSTALL_DIRECTORY@/@CPACK_PROJECT_NAME_UCASE@.app" },
{ "x": 568, "y": 200, "type": "link", "path": "/Applications" }
]
}

View File

Before

Width:  |  Height:  |  Size: 50 KiB

After

Width:  |  Height:  |  Size: 50 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 114 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 113 KiB

View File

@@ -1,104 +0,0 @@
#!/bin/bash
# Creates Apple ".app" bundle for @PROJECT_NAME_UCASE@
# Note:
# Examine linkings using `otool -L somelib.so`
# Debug the loading of dynamic libraries using `export DYLD_PRINT_LIBRARIES=1`
set -e
# Place to create ".app" bundle
APP="@CMAKE_BINARY_DIR@/@PROJECT_NAME_UCASE@.app"
MSG_COLOR='\x1B[1;36m'
COLOR_RESET='\x1B[0m'
echo -e "$MSG_COLOR\n\nCreating App Bundle \"$APP\"...$COLOR_RESET"
qtpath="$(dirname "@QT_QMAKE_EXECUTABLE@")"
export PATH="$PATH:$qtpath"
# Remove any old .app bundles
rm -Rf "$APP"
# Copy/overwrite Info.plist
command cp "@CMAKE_BINARY_DIR@/Info.plist" "@CMAKE_INSTALL_PREFIX@/"
# Create .app bundle containing contents from CMAKE_INSTALL_PREFIX
mkdir -p "$APP/Contents/MacOS"
mkdir -p "$APP/Contents/Frameworks"
mkdir -p "$APP/Contents/Resources"
cd "@CMAKE_INSTALL_PREFIX@"
cp -R ./* "$APP/Contents"
cp "@CMAKE_SOURCE_DIR@/cmake/apple/"*.icns "$APP/Contents/Resources/"
# Make all libraries writable for macdeployqt
cd "$APP"
find . -type f -print0 | xargs -0 chmod u+w
lmmsbin="MacOS/@CMAKE_PROJECT_NAME@"
zynbin="MacOS/RemoteZynAddSubFx"
# Move lmms binary
mv "$APP/Contents/bin/@CMAKE_PROJECT_NAME@" "$APP/Contents/$lmmsbin"
# Fix zyn linking
mv "$APP/Contents/lib/lmms/RemoteZynAddSubFx" "$APP/Contents/$zynbin"
# Replace @rpath with @loader_path for Carla
# See also plugins/CarlaBase/CMakeLists.txt
# This MUST be done BEFORE calling macdeployqt
install_name_tool -change @rpath/libcarlabase.dylib \
@loader_path/libcarlabase.dylib \
"$APP/Contents/lib/lmms/libcarlapatchbay.so"
install_name_tool -change @rpath/libcarlabase.dylib \
@loader_path/libcarlabase.dylib \
"$APP/Contents/lib/lmms/libcarlarack.so"
# Link lmms binary
_executables="${_executables} -executable=$APP/Contents/$zynbin"
# Build a list of shared objects in target/lib/lmms
for file in "$APP/Contents/lib/lmms/"*.so; do
_thisfile="$APP/Contents/lib/lmms/${file##*/}"
_executables="${_executables} -executable=$_thisfile"
done
# Build a list of shared objects in target/lib/lmms/ladspa
for file in "$APP/Contents/lib/lmms/ladspa/"*.so; do
_thisfile="$APP/Contents/lib/lmms/ladspa/${file##*/}"
_executables="${_executables} -executable=$_thisfile"
done
# Finalize .app
# shellcheck disable=SC2086
macdeployqt "$APP" $_executables
# Carla is a standalone plugin. Remove library, look for it side-by-side LMMS.app
# This MUST be done AFTER calling macdeployqt
#
# For example:
# /Applications/LMMS.app
# /Applications/Carla.app
carlalibs=$(echo "@CARLA_LIBRARIES@"|tr ";" "\n")
# Loop over all libcarlas, fix linking
for file in "$APP/Contents/lib/lmms/"libcarla*; do
_thisfile="$APP/Contents/lib/lmms/${file##*/}"
for lib in $carlalibs; do
_oldpath="../../Frameworks/lib${lib}.dylib"
_newpath="Carla.app/Contents/MacOS/lib${lib}.dylib"
# shellcheck disable=SC2086
install_name_tool -change @loader_path/$_oldpath \
@executable_path/../../../$_newpath \
"$_thisfile"
rm -f "$APP/Contents/Frameworks/lib${lib}.dylib"
done
done
# Cleanup
rm -rf "$APP/Contents/bin"
# Codesign
codesign --force --deep --sign - "$APP"
echo -e "\nFinished.\n\n"

View File

@@ -7,13 +7,13 @@
<string>English</string>
<key>CFBundleIconFile</key>
<string>@MACOSX_BUNDLE_ICON_FILE@</string>
<string>@CPACK_PROJECT_NAME@</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleGetInfoString</key>
<string>@MACOSX_BUNDLE_GUI_IDENTIFIER@ @MACOSX_BUNDLE_LONG_VERSION_STRING@</string>
<string>@CPACK_PROJECT_NAME_UCASE@ @CPACK_PROJECT_VERSION@</string>
<!--
#############################################################
@@ -29,38 +29,38 @@
#############################################################
-->
<key>CFBundleSignature</key>
<string>@MACOSX_BUNDLE_GUI_IDENTIFIER@</string>
<string>@CPACK_PROJECT_NAME_UCASE@</string>
<key>CFBundleExecutable</key>
<string>@MACOSX_BUNDLE_GUI_IDENTIFIER@</string>
<string>@CPACK_PROJECT_NAME@</string>
<key>CFBundleVersion</key>
<string>@MACOSX_BUNDLE_LONG_VERSION_STRING@</string>
<string>@CPACK_PROJECT_VERSION@</string>
<key>CFBundleShortVersionString</key>
<string>@MACOSX_BUNDLE_LONG_VERSION_STRING@</string>
<string>@VERSIONCPACK_PROJECT_VERSION@</string>
<key>CFBundleName</key>
<string>@MACOSX_BUNDLE_BUNDLE_NAME@</string>
<string>@CPACK_PROJECT_NAME_UCASE@</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleIdentifier</key>
<string>@MACOSX_BUNDLE_MIMETYPE_ID@</string>
<string>@MACOS_MIMETYPE_ID@</string>
<key>CFBundleDocumentTypes</key>
<array>
<dict>
<key>CFBundleTypeExtensions</key>
<array>
<string>mmpz</string>
</array>
<key>CFBundleTypeIconFile</key>
<string>@MACOSX_BUNDLE_MIMETYPE_ICON@</string>
<string>project</string>
<key>CFBundleTypeName</key>
<string>@MACOSX_BUNDLE_GUI_IDENTIFIER@ Project</string>
<string>@CPACK_PROJECT_NAME_UCASE@ Project</string>
<key>CFBundleTypeOSTypes</key>
<array>
<string>mmpz</string>
@@ -69,19 +69,19 @@
<string>Editor</string>
<key>CFBundleTypeMIMETypes</key>
<array>
<string>@MACOSX_BUNDLE_MIMETYPE@</string>
<string>application/x-@CPACK_PROJECT_NAME@-project</string>
</array>
</dict>
<dict>
<key>CFBundleTypeExtensions</key>
<array>
<string>mmp</string>
</array>
<key>CFBundleTypeIconFile</key>
<string>@MACOSX_BUNDLE_MIMETYPE_ICON@</string>
<string>project</string>
<key>CFBundleTypeName</key>
<string>@MACOSX_BUNDLE_GUI_IDENTIFIER@ Project (uncompressed)</string>
<string>@CPACK_PROJECT_NAME_UCASE@ Project (uncompressed)</string>
<key>CFBundleTypeOSTypes</key>
<array>
<string>mmp</string>
@@ -90,23 +90,23 @@
<string>Editor</string>
<key>CFBundleTypeMIMETypes</key>
<array>
<string>@MACOSX_BUNDLE_MIMETYPE@</string>
<string>application/x-@CPACK_PROJECT_NAME@-project</string>
</array>
</dict>
</array>
<key>UTExportedTypeDeclarations</key>
<array>
<dict>
<key>UTTypeIdentifier</key>
<string>@MACOSX_BUNDLE_MIMETYPE_ID@.mmpz</string>
<string>@MACOS_MIMETYPE_ID@.mmpz</string>
<key>UTTypeReferenceURL</key>
<string>@MACOSX_BUNDLE_PROJECT_URL@</string>
<string>@CPACK_PROJECT_URL@</string>
<key>UTTypeDescription</key>
<string>@MACOSX_BUNDLE_GUI_IDENTIFIER@ Project</string>
<string>@CPACK_PROJECT_VERSIONPROJECT_NAME_UCASE@ Project</string>
<key>UTTypeIconFile</key>
<string>@MACOSX_BUNDLE_MIMETYPE_ICON@</string>
<string>project</string>
<key>UTTypeConformsTo</key>
<array>
<string>public.data</string>
@@ -122,13 +122,13 @@
<dict>
<key>UTTypeIdentifier</key>
<string>@MACOSX_BUNDLE_MIMETYPE_ID@.mmp</string>
<string>@MACOS_MIMETYPE_ID@.mmp</string>
<key>UTTypeReferenceURL</key>
<string>@MACOSX_BUNDLE_PROJECT_URL@</string>
<string>@CPACK_PROJECT_URL@</string>
<key>UTTypeDescription</key>
<string>@MACOSX_BUNDLE_GUI_IDENTIFIER@ Project (uncompressed)</string>
<string>@CPACK_PROJECT_NAME_UCASE@ Project (uncompressed)</string>
<key>UTTypeIconFile</key>
<string>@MACOSX_BUNDLE_MIMETYPE_ICON@</string>
<string>project</string>
<key>UTTypeConformsTo</key>
<array>
<string>public.xml</string>

View File

@@ -1,9 +0,0 @@
{
"title": "@MACOSX_BUNDLE_DMG_TITLE@",
"background": "@CMAKE_SOURCE_DIR@/cmake/apple/dmg_branding.png",
"icon-size": 128,
"contents": [
{ "x": 139, "y": 200, "type": "file", "path": "@CMAKE_BINARY_DIR@/@MACOSX_BUNDLE_BUNDLE_NAME@.app" },
{ "x": 568, "y": 200, "type": "link", "path": "/Applications" }
]
}

View File

@@ -43,17 +43,10 @@ ENDIF()
if(LMMS_HAVE_STK AND (LMMS_BUILD_WIN32 OR LMMS_BUILD_APPLE))
if(STK_RAWWAVE_ROOT)
file(GLOB RAWWAVES "${STK_RAWWAVE_ROOT}/*.raw")
list(SORT RAWWAVES)
install(FILES ${RAWWAVES} DESTINATION "${DATA_DIR}/stk/rawwaves")
else()
message(WARNING "Can't find STK rawwave root!")
endif()
endif()
IF(LMMS_BUILD_APPLE)
INSTALL(CODE "EXECUTE_PROCESS(COMMAND chmod u+x ${CMAKE_BINARY_DIR}/install_apple.sh)")
INSTALL(CODE "EXECUTE_PROCESS(COMMAND ${CMAKE_BINARY_DIR}/install_apple.sh RESULT_VARIABLE EXIT_CODE)
IF(NOT EXIT_CODE EQUAL 0)
MESSAGE(FATAL_ERROR \"Execution of install_apple.sh failed\")
ENDIF()
")
ENDIF()

View File

@@ -1,19 +1,53 @@
INSTALL(DIRECTORY icons/ DESTINATION "${DATA_DIR}/icons/hicolor")
INSTALL(FILES lmms.desktop DESTINATION "${DATA_DIR}/applications")
INSTALL(FILES lmms.xml DESTINATION "${DATA_DIR}/mime/packages")
install(DIRECTORY icons/ DESTINATION "${DATA_DIR}/icons/hicolor")
install(FILES lmms.desktop DESTINATION "${DATA_DIR}/applications")
install(FILES lmms.xml DESTINATION "${DATA_DIR}/mime/packages")
# AppImage creation target
SET(APPIMAGE_FILE "${CMAKE_BINARY_DIR}/${CMAKE_PROJECT_NAME}-${VERSION}-linux-${CMAKE_SYSTEM_PROCESSOR}.AppImage")
# Preserve old CPack behavior
if(WANT_CPACK_TARBALL)
message(STATUS "Skipping AppImage creation")
return()
endif()
CONFIGURE_FILE("package_linux.sh.in" "${CMAKE_BINARY_DIR}/package_linux.sh" @ONLY)
install(FILES launch_lmms.sh DESTINATION bin)
FILE(REMOVE "${APPIMAGE_FILE}")
ADD_CUSTOM_TARGET(removeappimage
COMMAND rm -f "${APPIMAGE_FILE}"
COMMENT "Removing old AppImage")
ADD_CUSTOM_TARGET(appimage
COMMAND chmod +x "${CMAKE_BINARY_DIR}/package_linux.sh"
COMMAND "${CMAKE_BINARY_DIR}/package_linux.sh"
COMMENT "Generating AppImage"
WORKING_DIRECTORY "${CMAKE_BINARY_DIR}")
ADD_DEPENDENCIES(appimage removeappimage)
# Standard CPack options
set(CPACK_GENERATOR "External" PARENT_SCOPE)
set(CPACK_EXTERNAL_ENABLE_STAGING true PARENT_SCOPE)
set(CPACK_PACKAGE_FILE_NAME "${CMAKE_PROJECT_NAME}-${VERSION}-linux-${CMAKE_SYSTEM_PROCESSOR}")
set(CPACK_PACKAGE_FILE_NAME "${CPACK_PACKAGE_FILE_NAME}" PARENT_SCOPE)
set(CPACK_PRE_BUILD_SCRIPTS "${CMAKE_CURRENT_SOURCE_DIR}/LinuxDeploy.cmake" PARENT_SCOPE)
# Custom vars to expose to Cpack
# must be prefixed with "CPACK_" per https://stackoverflow.com/a/46526757/3196753)
set(CPACK_CURRENT_BINARY_DIR "${CMAKE_CURRENT_BINARY_DIR}" PARENT_SCOPE)
set(CPACK_CURRENT_SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}" PARENT_SCOPE)
set(CPACK_BINARY_DIR "${CMAKE_BINARY_DIR}" PARENT_SCOPE)
set(CPACK_SOURCE_DIR "${CMAKE_SOURCE_DIR}" PARENT_SCOPE)
set(CPACK_QMAKE_EXECUTABLE "${QT_QMAKE_EXECUTABLE}" PARENT_SCOPE)
set(CPACK_CARLA_LIBRARIES "${CARLA_LIBRARIES}" PARENT_SCOPE)
set(CPACK_WINE_32_LIBRARY_DIRS "${WINE_32_LIBRARY_DIRS}" PARENT_SCOPE)
set(CPACK_WINE_64_LIBRARY_DIRS "${WINE_64_LIBRARY_DIRS}" PARENT_SCOPE)
set(CPACK_PROJECT_NAME "${PROJECT_NAME}" PARENT_SCOPE)
set(CPACK_PROJECT_NAME_UCASE "${PROJECT_NAME_UCASE}" PARENT_SCOPE)
set(CPACK_PROJECT_VERSION "${VERSION}" PARENT_SCOPE)
set(CPACK_CMAKE_COMMAND "${CMAKE_COMMAND}" PARENT_SCOPE)
set(CPACK_SUIL_MODULES "${Suil_MODULES}" PARENT_SCOPE)
set(CPACK_SUIL_MODULES_PREFIX "${Suil_MODULES_PREFIX}" PARENT_SCOPE)
set(CPACK_STK_RAWWAVE_ROOT "${STK_RAWWAVE_ROOT}" PARENT_SCOPE)
# TODO: Canidate for DetectMachine.cmake
if(IS_X86_64)
set(CPACK_TARGET_ARCH x86_64 PARENT_SCOPE)
elseif(IS_X86)
set(CPACK_TARGET_ARCH i386 PARENT_SCOPE)
elseif(IS_ARM64)
set(CPACK_TARGET_ARCH aarch64 PARENT_SCOPE)
elseif(IS_ARM32)
set(CPACK_TARGET_ARCH armhf PARENT_SCOPE)
else()
set(CPACK_TARGET_ARCH unknown PARENT_SCOPE)
endif()
if(CMAKE_VERSION VERSION_LESS "3.19")
message(WARNING "AppImage creation requires at least CMake 3.19")
endif()

View File

@@ -0,0 +1,295 @@
# Create a Linux desktop installer using linuxdeploy
# * Creates a relocatable LMMS.AppDir installation in build/_CPack_Packages using linuxdeploy
# * If CPACK_TOOL=appimagetool or is not set, bundles AppDir into redistributable ".AppImage" file
# * If CPACK_TOOL=makeself is provided, bundles into a redistributable ".run" file
#
# Copyright (c) 2025, Tres Finocchiaro, <tres.finocchiaro@gmail.com>
#
# Redistribution and use is allowed according to the terms of the BSD license.
# For details see the accompanying COPYING-CMAKE-SCRIPTS file.
# Variables must be prefixed with "CPACK_" to be visible here
set(lmms "${CPACK_PROJECT_NAME}")
set(LMMS "${CPACK_PROJECT_NAME_UCASE}")
set(ARCH "${CPACK_TARGET_ARCH}")
set(APP "${CPACK_TEMPORARY_INSTALL_DIRECTORY}/${LMMS}.AppDir")
# Target AppImage file
set(APPIMAGE_FILE "${CPACK_BINARY_DIR}/${CPACK_PACKAGE_FILE_NAME}.AppImage")
set(APPIMAGE_BEFORE_RENAME "${CPACK_BINARY_DIR}/${LMMS}-${ARCH}.AppImage")
set(DESKTOP_FILE "${APP}/usr/share/applications/${lmms}.desktop")
# Determine which packaging tool to use
if(NOT CPACK_TOOL)
# Pick up environmental variable
if(DEFINED ENV{CPACK_TOOL})
set(CPACK_TOOL "$ENV{CPACK_TOOL}")
else()
set(CPACK_TOOL "appimagetool")
endif()
endif()
# Toggle command echoing & verbosity
# 0 = no output, 1 = error/warning, 2 = normal, 3 = debug
if(NOT CPACK_DEBUG)
set(VERBOSITY 1)
set(APPIMAGETOOL_VERBOSITY "")
set(COMMAND_ECHO NONE)
set(OUTPUT_QUIET OUTPUT_QUIET)
else()
set(VERBOSITY 2)
set(APPIMAGETOOL_VERBOSITY "--verbose")
set(COMMAND_ECHO STDOUT)
unset(OUTPUT_QUIET)
endif()
include(DownloadBinary)
include(CreateSymlink)
# Cleanup CPack "External" json, txt files, old AppImage files
file(GLOB cleanup "${CPACK_BINARY_DIR}/${lmms}-*.json"
"${CPACK_BINARY_DIR}/${lmms}-*.AppImage"
"${CPACK_BINARY_DIR}/install_manifest.txt")
list(SORT cleanup)
file(REMOVE ${cleanup})
# Download linuxdeploy, expose bundled appimagetool to PATH
download_binary(LINUXDEPLOY_BIN
"https://github.com/linuxdeploy/linuxdeploy/releases/download/continuous/linuxdeploy-${ARCH}.AppImage"
linuxdeploy-${ARCH}.AppImage
FALSE)
# Symlink nested appimagetool
set(_APPIMAGETOOL_LINK "${CPACK_CURRENT_BINARY_DIR}/appimagetool")
if(NOT EXISTS "${_APPIMAGETOOL_LINK}")
set(_APPIMAGETOOL "${CPACK_CURRENT_BINARY_DIR}/.linuxdeploy-${ARCH}.AppImage/squashfs-root/plugins/linuxdeploy-plugin-appimage/appimagetool-prefix/AppRun")
message(STATUS "Creating a symbolic link ${_APPIMAGETOOL_LINK} which points to ${_APPIMAGETOOL}")
create_symlink("${_APPIMAGETOOL}" "${_APPIMAGETOOL_LINK}")
endif()
# Download linuxdeploy-plugin-qt
download_binary(LINUXDEPLOY_PLUGIN_BIN
"https://github.com/linuxdeploy/linuxdeploy-plugin-qt/releases/download/continuous/linuxdeploy-plugin-qt-${ARCH}.AppImage"
linuxdeploy-plugin-qt-${ARCH}.AppImage
FALSE)
message(STATUS "Creating AppDir ${APP}...")
file(REMOVE_RECURSE "${CPACK_TEMPORARY_INSTALL_DIRECTORY}/include")
file(MAKE_DIRECTORY "${APP}/usr")
# Setup AppDir structure (/usr/bin, /usr/lib, /usr/share... etc)
file(GLOB files "${CPACK_TEMPORARY_INSTALL_DIRECTORY}/*")
list(SORT files)
foreach(_file ${files})
get_filename_component(_filename "${_file}" NAME)
if(NOT _filename MATCHES ".AppDir")
file(RENAME "${_file}" "${APP}/usr/${_filename}")
endif()
endforeach()
# Copy Suil modules
if(CPACK_SUIL_MODULES)
set(SUIL_MODULES_TARGET "${APP}/usr/lib/${CPACK_SUIL_MODULES_PREFIX}")
file(MAKE_DIRECTORY "${SUIL_MODULES_TARGET}")
file(COPY ${CPACK_SUIL_MODULES} DESTINATION "${SUIL_MODULES_TARGET}")
endif()
# Copy stk/rawwaves
if(CPACK_STK_RAWWAVE_ROOT)
set(STK_RAWWAVE_TARGET "${APP}/usr/share/stk/rawwaves/")
file(MAKE_DIRECTORY "${STK_RAWWAVE_TARGET}")
file(GLOB RAWWAVES "${CPACK_STK_RAWWAVE_ROOT}/*.raw")
file(COPY ${RAWWAVES} DESTINATION "${STK_RAWWAVE_TARGET}")
endif()
# Ensure project's "qmake" executable is first on the PATH
get_filename_component(QTBIN "${CPACK_QMAKE_EXECUTABLE}" DIRECTORY)
set(ENV{PATH} "${QTBIN}:$ENV{PATH}")
# Ensure "linuxdeploy-<arch>.AppImage" and "appimagetool" binaries are first on the PATH
set(ENV{PATH} "${CPACK_CURRENT_BINARY_DIR}:$ENV{PATH}")
# Promote finding our own libraries first
set(ENV{LD_LIBRARY_PATH} "${APP}/usr/lib/${lmms}/:${APP}/usr/lib/${lmms}/optional:$ENV{LD_LIBRARY_PATH}")
# Skip slow searching of copyright files https://github.com/linuxdeploy/linuxdeploy/issues/278
set(ENV{DISABLE_COPYRIGHT_FILES_DEPLOYMENT} 1)
# Patch desktop file
file(APPEND "${DESKTOP_FILE}" "X-AppImage-Version=${CPACK_PROJECT_VERSION}\n")
# Prefer a hard-copy of .DirIcon over appimagetool's symlinking
# 256x256 default for Cinnamon Desktop https://forums.linuxmint.com/viewtopic.php?p=2585952
file(COPY "${APP}/usr/share/icons/hicolor/256x256/apps/${lmms}.png" DESTINATION "${APP}")
file(RENAME "${APP}/${lmms}.png" "${APP}/.DirIcon")
file(COPY "${APP}/usr/share/icons/hicolor/256x256/apps/${lmms}.png" DESTINATION "${APP}")
# Build list of libraries to inform linuxdeploy about
# e.g. --library=foo.so --library=bar.so
file(GLOB LIBS "${APP}/usr/lib/${lmms}/*.so")
# Inform linuxdeploy about LADSPA plugins; may depend on bundled fftw3f, etc.
file(GLOB LADSPA "${APP}/usr/lib/${lmms}/ladspa/*.so")
# Inform linuxdeploy about remote plugins
file(GLOB REMOTE_PLUGINS "${APP}/usr/lib/${lmms}/*Remote*")
# Collect, sort and dedupe all libraries
list(APPEND LIBS ${LADSPA})
list(APPEND LIBS ${REMOTE_PLUGINS})
list(APPEND LIBS ${CPACK_SUIL_MODULES})
list(REMOVE_DUPLICATES LIBS)
list(SORT LIBS)
# Handle non-relinkable files (e.g. RemoveVstPlugin[32|64], but not NativeLinuxRemoteVstPlugin)
list(FILTER LIBS EXCLUDE REGEX "\\/RemoteVst")
# Construct linuxdeploy parameters
foreach(_lib IN LISTS LIBS)
if(EXISTS "${_lib}")
list(APPEND LIBRARIES "--library=${_lib}")
endif()
endforeach()
# Call linuxdeploy
message(STATUS "Calling ${LINUXDEPLOY_BIN} --appdir \"${APP}\" ... [... libraries].")
execute_process(COMMAND "${LINUXDEPLOY_BIN}"
--appdir "${APP}"
--desktop-file "${DESKTOP_FILE}"
--custom-apprun "${CPACK_SOURCE_DIR}/cmake/linux/launch_lmms.sh"
--plugin qt
${LIBRARIES}
--verbosity ${VERBOSITY}
${OUTPUT_QUIET}
COMMAND_ECHO ${COMMAND_ECHO}
COMMAND_ERROR_IS_FATAL ANY)
# Remove svg ambitiously placed by linuxdeploy
file(REMOVE "${APP}/${lmms}.svg")
# Remove libraries that are normally sytem-provided
file(GLOB EXCLUDE_LIBS
"${APP}/usr/lib/libwine*"
"${APP}/usr/lib/libcarla_native*"
"${APP}/usr/lib/${lmms}/optional/libcarla*"
"${APP}/usr/lib/libjack*")
list(SORT EXCLUDE_LIBS)
foreach(_lib IN LISTS EXCLUDE_LIBS)
if(EXISTS "${_lib}")
file(REMOVE "${_lib}")
endif()
endforeach()
# FIXME: Remove when linuxdeploy supports subfolders https://github.com/linuxdeploy/linuxdeploy/issues/305
foreach(_lib IN LISTS LIBS)
if(EXISTS "${_lib}")
file(REMOVE "${_lib}")
endif()
endforeach()
# Move RemotePlugins into to LMMS_PLUGIN_DIR
file(GLOB WINE_VST_LIBS
"${APP}/usr/lib/${lmms}/RemoteVstPlugin*"
"${APP}/usr/lib/${lmms}/32")
foreach(_file IN LISTS WINE_VST_LIBS)
if(EXISTS "${_file}")
get_filename_component(_name "${_file}" NAME)
file(RENAME "${_file}" "${APP}/usr/lib/${_name}")
endif()
endforeach()
file(GLOB WINE_32_LIBS
"${APP}/usr/lib/${lmms}/RemoteVstPlugin*")
foreach(_lib IN LISTS WINE_64_LIBS)
if(EXISTS "${_lib}")
get_filename_component(_file "${_lib}" NAME)
file(RENAME "${_lib}" "${APP}/usr/lib/${_file}")
endif()
endforeach()
file(REMOVE_RECURSE "${SUIL_MODULES_TARGET}" "${APP}/usr/lib/${lmms}/ladspa/")
# Bundle jack to avoid crash for systems without it
# See https://github.com/LMMS/lmms/pull/4186
execute_process(COMMAND ldd "${APP}/usr/bin/${lmms}"
OUTPUT_VARIABLE LDD_OUTPUT
OUTPUT_STRIP_TRAILING_WHITESPACE
COMMAND_ECHO ${COMMAND_ECHO}
COMMAND_ERROR_IS_FATAL ANY)
string(REPLACE "\n" ";" LDD_LIST "${LDD_OUTPUT}")
foreach(line ${LDD_LIST})
if(line MATCHES "libjack\\.so")
# Assume format "libjack.so.0 => /lib/x86_64-linux-gnu/libjack.so.0 (0x00007f48d0b0e000)"
string(REPLACE " " ";" parts "${line}")
list(LENGTH parts len)
math(EXPR index "${len}-2")
list(GET parts ${index} lib)
# Get symlink target
file(REAL_PATH "${lib}" libreal)
get_filename_component(symname "${lib}" NAME)
get_filename_component(realname "${libreal}" NAME)
file(MAKE_DIRECTORY "${APP}/usr/lib/${lmms}/optional/")
# Copy, but with original symlink name
file(COPY "${libreal}" DESTINATION "${APP}/usr/lib/${lmms}/optional/")
file(RENAME "${APP}/usr/lib/${lmms}/optional/${realname}" "${APP}/usr/lib/${lmms}/optional/${symname}")
continue()
endif()
endforeach()
if(CPACK_TOOL STREQUAL "appimagetool")
# Create ".AppImage" file using appimagetool (default)
# appimage plugin needs ARCH set when running in extracted form from squashfs-root / CI
set(ENV{ARCH} "${ARCH}")
message(STATUS "Finishing the AppImage...")
execute_process(COMMAND ${CPACK_TOOL} "${APP}" "${APPIMAGE_FILE}"
${APPIMAGETOOL_VERBOSITY}
${OUTPUT_QUIET}
COMMAND_ECHO ${COMMAND_ECHO}
COMMAND_ERROR_IS_FATAL ANY)
message(STATUS "AppImage created: ${APPIMAGE_FILE}")
elseif(CPACK_TOOL STREQUAL "makeself")
# Create self-extracting ".run" script using makeself
find_program(MAKESELF_BIN makeself REQUIRED)
message(STATUS "Finishing the .run file using ${MAKESELF_BIN}...")
string(REPLACE ".AppImage" ".run" RUN_FILE "${APPIMAGE_FILE}")
configure_file(
"${CPACK_SOURCE_DIR}/cmake/linux/makeself_setup.sh.in" "${APP}/setup.sh" @ONLY
FILE_PERMISSIONS
OWNER_EXECUTE OWNER_WRITE OWNER_READ
GROUP_EXECUTE GROUP_WRITE GROUP_READ
WORLD_READ)
if(OUTPUT_QUIET)
set(MAKESELF_QUIET "--quiet")
set(ERROR_QUIET ERROR_QUIET)
endif()
# makeself.sh [args] archive_dir file_name label startup_script [script_args]
file(REMOVE "${RUN_FILE}")
execute_process(COMMAND "${MAKESELF_BIN}"
--keep-umask
--nox11
${MAKESELF_QUIET}
"${APP}"
"${RUN_FILE}"
"${LMMS} Installer"
"./setup.sh"
${OUTPUT_QUIET}
COMMAND_ECHO ${COMMAND_ECHO}
COMMAND_ERROR_IS_FATAL ANY)
# ensure the installer can be executed as a script file
execute_process(COMMAND "${RUN_FILE}" --help
${OUTPUT_QUIET}
${ERROR_QUIET}
COMMAND_ECHO ${COMMAND_ECHO}
COMMAND_ERROR_IS_FATAL ANY)
message(STATUS "Installer created: ${RUN_FILE}")
else()
message(FATAL_ERROR "Packaging tool CPACK_TOOL=\"${CPACK_TOOL}\" is not yet supported")
endif()

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.5 KiB

View File

@@ -21,4 +21,10 @@ else
echo "Jack does not appear to be installed. That's OK, we'll use a dummy version instead." >&2
export LD_LIBRARY_PATH=$DIR/usr/lib/lmms/optional:$LD_LIBRARY_PATH
fi
QT_X11_NO_NATIVE_MENUBAR=1 "$DIR"/usr/bin/lmms.real "$@"
# FIXME: Remove when linuxdeploy supports subfolders https://github.com/linuxdeploy/linuxdeploy/issues/305
export LMMS_PLUGIN_DIR="$DIR/usr/lib/"
export LADSPA_PATH="$DIR/usr/lib/"
export SUIL_MODULE_DIR="$DIR/usr/lib/"
QT_X11_NO_NATIVE_MENUBAR=1 "$DIR"/usr/bin/lmms "$@"

View File

@@ -0,0 +1,35 @@
#!/bin/bash
# Halt on first error
set -e
DESTDIR="/opt/@CPACK_PROJECT_NAME@"
BASHCOMPLETIONS="/usr/share/bash-completion/completions"
if [ "$(id -u)" != "0" ]; then
# Prepend "$HOME" so we can install to a writable location
DESTDIR="${HOME}${DESTDIR}"
BASHCOMPLETIONS="${HOME}/.local/share/bash-completion/completions"
echo "Installing as a regular user to ${DESTDIR}/..."
else
echo "Installing as elevated user to ${DESTDIR}/..."
fi
# Deploy @CPACK_PROJECT_NAME_UCASE@
mkdir -p "${DESTDIR}"
unalias cp &> /dev/null || true
cp -rf ./* "${DESTDIR}"
rm -f "${DESTDIR}/setup.sh"
mv "${DESTDIR}/AppRun" "${DESTDIR}/@CPACK_PROJECT_NAME@"
# Install bash completions
mkdir -p "${BASHCOMPLETIONS}"
ln -sf "${DESTDIR}/usr/share/@CPACK_PROJECT_NAME@/bash-completion/completions/@CPACK_PROJECT_NAME@" "${BASHCOMPLETIONS}/@CPACK_PROJECT_NAME@"
# Test @CPACK_PROJECT_NAME_UCASE@
echo "Installation complete... Testing \"@CPACK_PROJECT_NAME@\"..."
"${DESTDIR}/@CPACK_PROJECT_NAME@" --allowroot --version &> /dev/null
# TODO: Register file associations, desktop icon, etc
echo "@CPACK_PROJECT_NAME_UCASE@ was installed successfully to ${DESTDIR}. To run:"
echo " ${DESTDIR}/@CPACK_PROJECT_NAME@"

View File

@@ -1,213 +0,0 @@
#!/usr/bin/env bash
# Creates Linux ".AppImage" for @PROJECT_NAME_UCASE@
#
# Depends: linuxdeployqt
#
# Notes: Will attempt to fetch linuxdeployqt automatically (x86_64 only)
# See Also: https://github.com/probonopd/linuxdeployqt/blob/master/BUILDING.md
VERBOSITY=2 # 3=debug
LOGFILE="@CMAKE_BINARY_DIR@/appimage.log"
APPDIR="@CMAKE_BINARY_DIR@/@PROJECT_NAME_UCASE@.AppDir/"
DESKTOPFILE="${APPDIR}usr/share/applications/lmms.desktop"
STRIP=""
# Don't strip for Debug|RelWithDebInfo builds
# shellcheck disable=SC2193
if [[ "@CMAKE_BUILD_TYPE@" == *"Deb"* ]]; then
STRIP="-no-strip"
fi
# Console colors
RED="\\x1B[1;31m"
GREEN="\\x1B[1;32m"
YELLOW="\\x1B[1;33m"
PLAIN="\\x1B[0m"
function error {
echo -e " ${PLAIN}[${RED}error${PLAIN}] ${1}"
return 1
}
function success {
echo -e " ${PLAIN}[${GREEN}success${PLAIN}] ${1}"
}
function skipped {
echo -e " ${PLAIN}[${YELLOW}skipped${PLAIN}] ${1}"
}
# Exit with error message if any command fails
trap "error 'Failed to generate AppImage'; exit 1" ERR
# Run a command silently, but print output if it fails
function run_and_log {
echo -e "\n\n>>>>> $1" >> "$LOGFILE"
output="$("$@" 2>&1)"
status=$?
echo "$output" >> "$LOGFILE"
[[ $status != 0 ]] && echo "$output"
return $status
}
# Blindly assume system arch is appimage arch
ARCH=$(uname -m)
export ARCH
# Check for problematic install locations
INSTALL=$(echo "@CMAKE_INSTALL_PREFIX@" | sed 's/\/*$//g')
if [ "$INSTALL" == "/usr/local" ] || [ "$INSTALL" == "/usr" ] ; then
error "Incompatible CMAKE_INSTALL_PREFIX for creating AppImage: @CMAKE_INSTALL_PREFIX@"
fi
# Ensure linuxdeployqt uses the same qmake version as cmake
PATH="$(dirname "@QT_QMAKE_EXECUTABLE@"):$PATH"
export PATH
# Use linuxdeployqt from env or in PATH
[[ $LINUXDEPLOYQT ]] || LINUXDEPLOYQT="$(which linuxdeployqt 2>/dev/null)" || true
[[ $APPIMAGETOOL ]] || APPIMAGETOOL="$(which appimagetool 2>/dev/null)" || true
# Fetch portable linuxdeployqt if not in PATH
if [[ -z $LINUXDEPLOYQT || -z $APPIMAGETOOL ]]; then
filename="linuxdeployqt-continuous-$ARCH.AppImage"
url="https://github.com/probonopd/linuxdeployqt/releases/download/continuous/$filename"
echo " [.......] Downloading: ${url}"
wget -N -q "$url" && err=0 || err=$?
case "$err" in
0) success "Downloaded $PWD/$filename" ;;
# 8 == server issued 4xx error
8) error "Download failed (perhaps no package available for $ARCH)" ;;
*) error "Download failed" ;;
esac
# Extract AppImage and replace LINUXDEPLOYQT variable with extracted binary
# to support systems without fuse
# Also, we need to set LD_LIBRARY_PATH, but linuxdepoyqt's AppRun unsets it
# See https://github.com/probonopd/linuxdeployqt/pull/370/
chmod +x "$filename"
./"$filename" --appimage-extract >/dev/null
success "Extracted $filename"
# Use the extracted linuxdeployqt and appimagetool
PATH="$(pwd -P)/squashfs-root/usr/bin:$PATH"
[[ $LINUXDEPLOYQT ]] || LINUXDEPLOYQT="$(which linuxdeployqt)"
[[ $APPIMAGETOOL ]] || APPIMAGETOOL="$(which appimagetool)"
fi
# Make skeleton AppDir
echo -e "\nCreating ${APPDIR}..."
rm -rf "${APPDIR}"
mkdir -p "${APPDIR}usr"
success "Created ${APPDIR}"
# Clone install to AppDir
echo -e "\nCopying @CMAKE_INSTALL_PREFIX@ to ${APPDIR}..."
cp -R "@CMAKE_INSTALL_PREFIX@/." "${APPDIR}usr"
rm -rf "${APPDIR}usr/include"
success "${APPDIR}"
# Copy rawwaves directory for stk/mallets
mkdir -p "${APPDIR}usr/share/stk/"
cp -R /usr/share/stk/rawwaves/ "${APPDIR}usr/share/stk/"
# Create a wrapper script which calls the lmms executable
mv "${APPDIR}usr/bin/lmms" "${APPDIR}usr/bin/lmms.real"
cp "@CMAKE_CURRENT_SOURCE_DIR@/launch_lmms.sh" "${APPDIR}usr/bin/lmms"
chmod +x "${APPDIR}usr/bin/lmms"
# Per https://github.com/probonopd/linuxdeployqt/issues/129
unset LD_LIBRARY_PATH
# Ensure linuxdeployqt can find shared objects
export LD_LIBRARY_PATH="${APPDIR}"usr/lib/lmms/:"${APPDIR}"usr/lib/lmms/optional:"$LD_LIBRARY_PATH"
# Move executables so linuxdeployqt can find them
ZYNLIB="${APPDIR}usr/lib/lmms/RemoteZynAddSubFx"
VSTLIB32="${APPDIR}usr/lib/lmms/32/RemoteVstPlugin32.exe.so"
VSTLIB64="${APPDIR}usr/lib/lmms/RemoteVstPlugin64.exe.so"
ZYNBIN="${APPDIR}usr/bin/RemoteZynAddSubFx"
VSTBIN32="${APPDIR}usr/bin/RemoteVstPlugin32.exe.so"
VSTBIN64="${APPDIR}usr/bin/RemoteVstPlugin64.exe.so"
mv "$ZYNLIB" "$ZYNBIN"
mv "$VSTLIB32" "$VSTBIN32" || true
mv "$VSTLIB64" "$VSTBIN64" || true
# Handle wine linking
if [ -d "@WINE_32_LIBRARY_DIR@" ] && \
ldd "$VSTBIN32" | grep "libwine\.so" | grep "not found"; then
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:"@WINE_32_LIBRARY_DIRS@"
fi
if [ -d "@WINE_64_LIBRARY_DIR@" ] && \
ldd "$VSTBIN64" | grep "libwine\.so" | grep "not found"; then
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:"@WINE_64_LIBRARY_DIRS@"
fi
# Patch the desktop file
sed -i 's/.*Exec=.*/Exec=lmms.real/' "$DESKTOPFILE"
echo "X-AppImage-Version=@VERSION@" >> "$DESKTOPFILE"
# Fix linking for soft-linked plugins
for file in "${APPDIR}usr/lib/lmms/"*.so; do
thisfile="${APPDIR}usr/lib/lmms/${file##*/}"
executables="${executables} -executable=$thisfile"
done
executables="${executables} -executable=${ZYNBIN}"
executables="${executables} -executable=${VSTBIN32}"
executables="${executables} -executable=${VSTBIN64}"
executables="${executables} -executable=${APPDIR}usr/lib/lmms/ladspa/imp_1199.so"
executables="${executables} -executable=${APPDIR}usr/lib/lmms/ladspa/imbeq_1197.so"
executables="${executables} -executable=${APPDIR}usr/lib/lmms/ladspa/pitch_scale_1193.so"
executables="${executables} -executable=${APPDIR}usr/lib/lmms/ladspa/pitch_scale_1194.so"
echo -e "\nWriting verbose output to \"${LOGFILE}\""
echo -n > "$LOGFILE"
# Bundle both qt and non-qt dependencies into appimage format
echo -e "\nBundling and relinking system dependencies..."
# shellcheck disable=SC2086
run_and_log "$LINUXDEPLOYQT" "$DESKTOPFILE" $executables -bundle-non-qt-libs -verbose=$VERBOSITY $STRIP
success "Bundled and relinked dependencies"
# Link to original location so lmms can find them
ln -sr "$ZYNBIN" "$ZYNLIB"
ln -sr "$VSTBIN32" "$VSTLIB32" || true
ln -sr "$VSTBIN64" "$VSTLIB64" || true
# Remove wine library conflict
rm -f "${APPDIR}/usr/lib/libwine.so.1"
# Use system-provided carla
rm -f "${APPDIR}usr/lib/"libcarla*.so
rm -f "${APPDIR}usr/lib/lmms/optional/"libcarla*.so
# Remove bundled jack in LD_LIBRARY_PATH if exists
if [ -e "${APPDIR}/usr/lib/libjack.so.0" ]; then
rm "${APPDIR}/usr/lib/libjack.so.0"
fi
# Bundle jack out of LD_LIBRARY_PATH
JACK_LIB=$(ldd "${APPDIR}/usr/bin/lmms.real" | sed -n 's/\tlibjack\.so\.0 => \(.\+\) (0x[0-9a-f]\+)/\1/p')
if [ -e "$JACK_LIB" ]; then
mkdir -p "${APPDIR}usr/lib/lmms/optional/"
cp "$JACK_LIB" "${APPDIR}usr/lib/lmms/optional/"
fi
# Point the AppRun to the shim launcher
rm -f "${APPDIR}/AppRun"
ln -sr "${APPDIR}/usr/bin/lmms" "${APPDIR}/AppRun"
# Add icon
ln -srf "${APPDIR}/lmms.png" "${APPDIR}/.DirIcon"
# Create AppImage
echo -e "\nFinishing the AppImage..."
run_and_log "$APPIMAGETOOL" "${APPDIR}" "@APPIMAGE_FILE@"
success "Created @APPIMAGE_FILE@"
echo -e "\nFinished"

View File

@@ -1,93 +1,78 @@
# A wrapper around pkg-config-provided and cmake-provided bash completion that
# will have dynamic behavior at INSTALL() time to allow both root-level
# INSTALL() as well as user-level INSTALL().
#
# See also https://github.com/scop/bash-completion
#
# Copyright (c) 2018, Tres Finocchiaro, <tres.finocchiaro@gmail.com>
# Copyright (c) 2024, Tres Finocchiaro, <tres.finocchiaro@gmail.com>
# Redistribution and use is allowed according to the terms of the BSD license.
# For details see the accompanying COPYING-CMAKE-SCRIPTS file.
#
# Description:
# Fail-safe bash-completion installation support
# - Installs to ${CMAKE_INSTALL_PREFIX}/share/bash-completion/completions
# - Attempts to calculate and install to system-wide location
# - See also https://github.com/scop/bash-completion
#
# Usage:
# INCLUDE(BashCompletion)
# BASHCOMP_INSTALL(foo)
# ... where "foo" is a shell script adjacent to the CMakeLists.txt
#
# How it determines BASHCOMP_PKG_PATH, in order:
# 1. Uses BASHCOMP_PKG_PATH if already set (e.g. -DBASHCOMP_PKG_PATH=...)
# a. If not, uses pkg-config's PKG_CHECK_MODULES to determine path
# b. Fallback to cmake's FIND_PACKAGE(bash-completion) path
# c. Fallback to hard-coded /usr/share/bash-completion/completions
# 2. Final fallback to ${CMAKE_INSTALL_PREFIX}/share/bash-completion/completions if
# detected path is unwritable.
# - Windows does not support bash completion
# - macOS support should eventually be added for Homebrew (TODO)
IF(WIN32)
MESSAGE(STATUS "Bash completion is not supported on this platform.")
ELSEIF(APPLE)
MESSAGE(STATUS "Bash completion is not yet implemented for this platform.")
ELSE()
INCLUDE(FindUnixCommands)
# Honor manual override if provided
IF(NOT BASHCOMP_PKG_PATH)
# First, use pkg-config, which is the most reliable
FIND_PACKAGE(PkgConfig QUIET)
IF(PKGCONFIG_FOUND)
PKG_CHECK_MODULES(BASH_COMPLETION bash-completion)
PKG_GET_VARIABLE(BASHCOMP_PKG_PATH bash-completion completionsdir)
ELSE()
# Second, use cmake (preferred but less common)
FIND_PACKAGE(bash-completion QUIET)
IF(BASH_COMPLETION_FOUND)
SET(BASHCOMP_PKG_PATH "${BASH_COMPLETION_COMPLETIONSDIR}")
ENDIF()
ENDIF()
# Honor manual override if provided
if(NOT BASHCOMP_PKG_PATH)
# First, use pkg-config, which is the most reliable
find_package(PkgConfig QUIET)
if(PKGCONFIG_FOUND)
PKG_CHECK_MODULES(BASH_COMPLETION bash-completion)
PKG_GET_VARIABLE(BASHCOMP_PKG_PATH bash-completion completionsdir)
else()
# Second, use cmake (preferred but less common)
find_package(bash-completion QUIET)
if(BASH_COMPLETION_FOUND)
set(BASHCOMP_PKG_PATH "${BASH_COMPLETION_COMPLETIONSDIR}")
endif()
endif()
# Third, use a hard-coded fallback value
IF("${BASHCOMP_PKG_PATH}" STREQUAL "")
SET(BASHCOMP_PKG_PATH "/usr/share/bash-completion/completions")
ENDIF()
ENDIF()
# Third, use a hard-coded fallback value
if("${BASHCOMP_PKG_PATH}" STREQUAL "")
set(BASHCOMP_PKG_PATH "/usr/share/bash-completion/completions")
endif()
endif()
# Always provide a fallback for non-root INSTALL()
SET(BASHCOMP_USER_PATH "${CMAKE_INSTALL_PREFIX}/share/bash-completion/completions")
# Always provide a fallback for non-root INSTALL()
# * "lmms" subfolder ensures we don't pollute /usr/local/share/ on default "make install"
set(BASHCOMP_USER_PATH "share/${PROJECT_NAME}/bash-completion/completions")
# Cmake doesn't allow easy use of conditional logic at INSTALL() time
# this is a problem because ${BASHCOMP_PKG_PATH} may not be writable and we
# need sane fallback behavior for bundled INSTALL() (e.g. .AppImage, etc).
#
# The reason this can't be detected by cmake is that it's fairly common to
# run "cmake" as a one user (i.e. non-root) and "make install" as another user
# (i.e. root).
#
# - Creates a script called "install_${SCRIPT_NAME}_completion.sh" into the
# working binary directory and invokes this script at install.
# - Script handles INSTALL()-time conditional logic for sane ballback behavior
# when ${BASHCOMP_PKG_PATH} is unwritable (i.e. non-root); Something cmake
# can't handle on its own at INSTALL() time)
MACRO(BASHCOMP_INSTALL SCRIPT_NAME)
# A shell script for wrapping conditionl logic
SET(BASHCOMP_SCRIPT "${CMAKE_CURRENT_BINARY_DIR}/install_${SCRIPT_NAME}_completion.sh")
macro(BASHCOMP_INSTALL SCRIPT_NAME)
# Note: When running from CPack, message(...) will be supressed unless WARNING
if(WIN32)
message(STATUS "Bash completion is not supported on this platform.")
else()
# Install a copy of bash completion to the default install prefix
# See also: https://github.com/LMMS/lmms/pull/7252/files#r1815749125
install(FILES "${SCRIPT_NAME}" DESTINATION "${BASHCOMP_USER_PATH}")
# Next, blindly attempt a system-wide install, ignoring failure
# See also: https://stackoverflow.com/q/58448332
# * CPack doesn't use CMAKE_INSTALL_PREFIX, so the original will be missing when packaging
# and this step will be skipped
# * For non-root installs (e.g. ../target), this will silently fail
set(BASHCOMP_ORIG "${CMAKE_INSTALL_PREFIX}/${BASHCOMP_USER_PATH}/${CMAKE_PROJECT_NAME}")
set(BASHCOMP_LINK "${BASHCOMP_PKG_PATH}/${CMAKE_PROJECT_NAME}")
if(BASHCOMP_PKG_PATH)
# TODO: CMake 3.21 Use "file(COPY_FILE ...)"
install(CODE "
if(EXISTS \"${BASHCOMP_ORIG}\")
file(REMOVE \"${BASHCOMP_LINK}\")
execute_process(COMMAND ${CMAKE_COMMAND} -E create_symlink
\"${BASHCOMP_ORIG}\"
\"${BASHCOMP_LINK}\"
ERROR_QUIET
RESULT_VARIABLE result)
if(result EQUAL 0)
message(STATUS \"Bash completion-support has been installed to ${BASHCOMP_LINK}\")
endif()
endif()
")
endif()
endif()
endmacro()
FILE(WRITE ${BASHCOMP_SCRIPT} "\
#!${BASH}\n\
set -e\n\
if [ -w \"${BASHCOMP_PKG_PATH}\" ]; then\n\
BASHCOMP_PKG_PATH=\"${BASHCOMP_PKG_PATH}\"\n\
else \n\
BASHCOMP_PKG_PATH=\"\$DESTDIR${BASHCOMP_USER_PATH}\"\n\
fi\n\
echo -e \"\\nInstalling bash completion...\\n\"\n\
mkdir -p \"\$BASHCOMP_PKG_PATH\"\n\
cp \"${CMAKE_CURRENT_SOURCE_DIR}/${SCRIPT_NAME}\" \"\$BASHCOMP_PKG_PATH\"\n\
chmod a+r \"\$BASHCOMP_PKG_PATH/${SCRIPT_NAME}\"\n\
echo -e \"Bash completion for ${SCRIPT_NAME} has been installed to \$BASHCOMP_PKG_PATH/${SCRIPT_NAME}\"\n\
")
INSTALL(CODE "EXECUTE_PROCESS(COMMAND chmod u+x \"install_${SCRIPT_NAME}_completion.sh\" WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} )")
INSTALL(CODE "EXECUTE_PROCESS(COMMAND \"./install_${SCRIPT_NAME}_completion.sh\" WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} )")
MESSAGE(STATUS "Bash completion script for ${SCRIPT_NAME} will be installed to ${BASHCOMP_PKG_PATH} or fallback to ${BASHCOMP_USER_PATH} if unwritable.")
ENDMACRO()
ENDIF()

View File

@@ -0,0 +1,34 @@
# Offer relative symlink support via "cmake -E create_symlink"
# For verbose, set COMMAND_ECHO to STDOUT in calling script
macro(create_symlink filepath sympath)
if(CMAKE_COMMAND)
set(_cmake_command "${CMAKE_COMMAND}")
elseif(CPACK_CMAKE_COMMAND)
set(_cmake_command "${CPACK_CMAKE_COMMAND}")
else()
message(FATAL_ERROR "Sorry, can't resolve variable CMAKE_COMMAND")
endif()
if(NOT IS_ABSOLUTE "${sympath}")
message(FATAL_ERROR "Sorry, this command only works with absolute paths")
endif()
if(NOT DEFINED COMMAND_ECHO)
set(_command_echo NONE)
else()
set(_command_echo "${COMMAND_ECHO}")
endif()
# Calculate the relative path
file(RELATIVE_PATH reldir "${sympath}/../" "${filepath}")
get_filename_component(symname "${sympath}" NAME)
# Calculate the working directory
get_filename_component(sympath_parent "${sympath}" DIRECTORY)
# Create the symbolic link
execute_process(COMMAND "${_cmake_command}" -E create_symlink "${reldir}" "${symname}"
WORKING_DIRECTORY "${sympath_parent}"
COMMAND_ECHO ${_command_echo}
COMMAND_ERROR_IS_FATAL ANY)
endmacro()

View File

@@ -0,0 +1,143 @@
# Downloads an executable from the provided URL for use in a build system
# and optionally prepends it to the PATH
#
# Assumes:
# - CMAKE_CURRENT_BINARY_DIR/[${_name}]
# - CPACK_CURRENT_BINARY_DIR/[${_name}]
# - Fallback to $ENV{TMPDIR}/[RANDOM]/[${_name}]
# - For verbose, set COMMAND_ECHO to STDOUT in calling script
#
macro(download_binary RESULT_VARIABLE _url _name _prepend_to_path)
if(NOT COMMAND_ECHO OR "${COMMAND_ECHO}" STREQUAL "NONE")
set(_command_echo NONE)
set(_output_quiet OUTPUT_QUIET)
set(_error_quiet ERROR_QUIET)
else()
set(_command_echo "${COMMAND_ECHO}")
set(_output_quiet "")
set(_error_quiet "")
endif()
# Check if fuse is needed
if("${RESULT_VARIABLE}" MATCHES "\\.AppImage$" OR "${_name}" MATCHES "\\.AppImage$")
message(STATUS "AppImage detected, we'll extract the AppImage before using")
set(_${RESULT_VARIABLE}_IS_APPIMAGE TRUE)
endif()
# Determine a suitable working directory
if(CMAKE_CURRENT_BINARY_DIR)
# Assume we're called from configure step
set(_working_dir "${CMAKE_CURRENT_BINARY_DIR}")
elseif(CPACK_CURRENT_BINARY_DIR)
# Assume cpack (non-standard variable name, but used throughout)
set(_working_dir "${CPACK_CURRENT_BINARY_DIR}")
else()
# Fallback to somewhere temporary, writable
if($ENV{_tmpdir})
# POSIX
set(_tmpdir "$ENV{_tmpdir}")
elseif($ENV{TEMP})
# Windows
set(_tmpdir "$ENV{TEMP}")
else()
# Linux, shame on you!
find_program(MKTEMP mktemp)
if(MKTEMP)
execute_process(COMMAND mktemp
OUTPUT_VARIABLE _working_dir
OUTPUT_STRIP_TRAILING_WHITESPACE
${_output_quiet}
COMMAND_ECHO ${_command_echo})
# mktemp formats it how we want it
else()
# Ummm... Linux you can do better!
set(_tmpdir "/tmp")
endif()
endif()
if(NOT DEFINED _working_dir)
string(RANDOM subdir)
set(_working_dir "${_tmpdir}/tmp.${subdir}")
endif()
if(NOT EXISTS "${_working_dir}")
file(MAKE_DIRECTORY "${_working_dir}")
endif()
endif()
if(_prepend_to_path)
# Ensure the PATH is configured
string(FIND "$ENV{PATH}" "${_working_dir}" _pathloc)
if(NOT $_pathloc EQUAL 0)
set(ENV{PATH} "${_working_dir}:$ENV{PATH}")
endif()
endif()
# First ensure the binary doesn't already exist
find_program(_${RESULT_VARIABLE} "${_name}" HINTS "${_working_dir}")
set(_binary_path "${_working_dir}/${_name}")
if(NOT _${RESULT_VARIABLE})
message(STATUS "Downloading ${_name} from ${_url}...")
file(DOWNLOAD
"${_url}"
"${_binary_path}"
STATUS DOWNLOAD_STATUS)
# Check if download was successful.
list(GET DOWNLOAD_STATUS 0 STATUS_CODE)
list(GET DOWNLOAD_STATUS 1 ERROR_MESSAGE)
if(NOT ${STATUS_CODE} EQUAL 0)
file(REMOVE "${_binary_path}")
message(FATAL_ERROR "Error downloading ${_url} ${ERROR_MESSAGE}")
endif()
# Ensure the file is executable
file(CHMOD "${_binary_path}" PERMISSIONS
OWNER_EXECUTE OWNER_WRITE OWNER_READ
GROUP_EXECUTE GROUP_WRITE GROUP_READ)
# Ensure it's found
find_program(_${RESULT_VARIABLE} "${_name}" HINTS "${_working_dir}" REQUIRED)
endif()
# We need to create a subdirectory for this binary and symlink it's AppRun to where it's expected
if(_${RESULT_VARIABLE}_IS_APPIMAGE AND NOT IS_SYMLINK "${_${RESULT_VARIABLE}}")
if(NOT COMMAND create_symlink)
include(CreateSymlink)
endif()
message(STATUS "Extracting ${_${RESULT_VARIABLE}} to ${_working_dir}/.${_name}/")
# extract appimage
execute_process(COMMAND "${_${RESULT_VARIABLE}}" --appimage-extract
WORKING_DIRECTORY "${_working_dir}"
COMMAND_ECHO ${_command_echo}
${_output_quiet}
${_error_quiet}
COMMAND_ERROR_IS_FATAL ANY)
# move extracted files to dedicated location (e.g. ".linuxdeploy-x86_64.AppImage/squashfs-root/")
file(MAKE_DIRECTORY "${_working_dir}/.${_name}/")
file(RENAME "${_working_dir}/squashfs-root/" "${_working_dir}/.${_name}/squashfs-root/")
# remove the unusable binary
file(REMOVE "${_${RESULT_VARIABLE}}")
# symlink the expected binary name to the AppRun file
message(STATUS "Creating a symbolic link ${_${RESULT_VARIABLE}} which points to ${_working_dir}/.${_name}/squashfs-root/AppRun")
create_symlink("${_working_dir}/.${_name}/squashfs-root/AppRun" "${_${RESULT_VARIABLE}}")
endif()
# Test the binary
# - TODO: Add support for bad binaries that set "$?" to an error code for no good reason
# - TODO: Add support for Windows binaries expecting "/?" instead of "--help"
message(STATUS "Testing that ${_name} works on this system...")
set(_test_param "--help")
execute_process(COMMAND "${_${RESULT_VARIABLE}}" ${_test_param}
COMMAND_ECHO ${_command_echo}
${_output_quiet}
${_error_quiet}
COMMAND_ERROR_IS_FATAL ANY)
message(STATUS "The binary \"${_${RESULT_VARIABLE}}\" is now available")
set(${RESULT_VARIABLE} "${_${RESULT_VARIABLE}}")
endmacro()

View File

@@ -0,0 +1,40 @@
# Copyright (c) 2024 Tres Finocchiaro
#
# Redistribution and use is allowed according to the terms of the New BSD license.
# For details see the accompanying COPYING-CMAKE-SCRIPTS file.
# This module defines
# Suil_MODULES: List of full paths to Suil modules (e.g. "/usr/lib/suil-0/libsuil_x11.so;...")
# Suil_MODULES_PREFIX: Only the directory name of the Suil_MODULES path (e.g. "suil-0")
pkg_check_modules(Suil QUIET suil-0)
if(Suil_FOUND)
if(APPLE)
set(_lib_ext "dylib")
elseif(WIN32)
set(_lib_ext "dll")
else()
set(_lib_ext "so")
endif()
# Isolate -- if needed -- the first suil library path (e.g. "/usr/lib/libsuil-0.so")
list(GET Suil_LINK_LIBRARIES 0 _lib)
if(EXISTS "${_lib}")
# Isolate -- if needed -- the first suil library name (e.g. "suil-0")
list(GET Suil_LIBRARIES 0 _modules_prefix)
get_filename_component(_lib_dir "${_lib}" DIRECTORY)
# Construct modules path (e.g. "/usr/lib/suil-0")
set(_modules_dir "${_lib_dir}/${_modules_prefix}")
if(IS_DIRECTORY "${_modules_dir}")
set(Suil_MODULES_PREFIX "${_modules_prefix}")
file(GLOB Suil_MODULES "${_modules_dir}/*.${_lib_ext}")
list(SORT Suil_MODULES)
endif()
endif()
endif()
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(SuilModules
REQUIRED_VARS Suil_MODULES Suil_MODULES_PREFIX
)

View File

@@ -53,6 +53,15 @@ SET(CPACK_NSIS_DEFINES "${CPACK_NSIS_DEFINES}" PARENT_SCOPE)
SET(CPACK_PACKAGE_ICON "${CPACK_PACKAGE_ICON}" PARENT_SCOPE)
SET(CPACK_NSIS_MUI_ICON "${CPACK_NSIS_MUI_ICON}" PARENT_SCOPE)
# Disable cpack's strip for historic reasons
set(CPACK_STRIP_FILES_ORIG "${CPACK_STRIP_FILES}" PARENT_SCOPE)
set(CPACK_STRIP_FILES FALSE PARENT_SCOPE)
if(CPACK_DEBUG)
# CMake 3.19+
set(CPACK_NSIS_EXECUTABLE_PRE_ARGUMENTS "-V4")
endif()
# Windows resource compilers
CONFIGURE_FILE("lmms.rc.in" "${CMAKE_BINARY_DIR}/lmms.rc")
CONFIGURE_FILE("zynaddsubfx.rc.in" "${CMAKE_BINARY_DIR}/plugins/ZynAddSubFx/zynaddsubfx.rc")

View File

@@ -181,11 +181,11 @@ set_target_properties(lmms PROPERTIES
set_target_properties(lmmsobjs PROPERTIES AUTOUIC_SEARCH_PATHS "gui/modals")
IF(NOT WIN32)
IF(NOT WIN32 AND NOT LMMS_BUILD_APPLE)
if(CMAKE_INSTALL_MANDIR)
SET(INSTALL_MANDIR ${CMAKE_INSTALL_MANDIR})
ELSE(CMAKE_INSTALL_MANDIR)
SET(INSTALL_MANDIR ${CMAKE_INSTALL_PREFIX}/share/man)
SET(INSTALL_MANDIR share/man)
ENDIF(CMAKE_INSTALL_MANDIR)
INSTALL(FILES "${CMAKE_BINARY_DIR}/lmms.1.gz"
DESTINATION "${INSTALL_MANDIR}/man1/"

View File

@@ -235,6 +235,11 @@ bool RemotePlugin::init(const QString &pluginExecutable,
m_failed = false;
}
QString exec = QFileInfo(QDir("plugins:"), pluginExecutable).absoluteFilePath();
// We may have received a directory via a environment variable
if (const char* env_path = std::getenv("LMMS_PLUGIN_DIR"))
exec = QFileInfo(QDir(env_path), pluginExecutable).absoluteFilePath();
#ifdef LMMS_BUILD_APPLE
// search current directory first
QString curDir = QCoreApplication::applicationDirPath() + "/" + pluginExecutable;
@@ -252,7 +257,7 @@ bool RemotePlugin::init(const QString &pluginExecutable,
if( ! QFile( exec ).exists() )
{
qWarning( "Remote plugin '%s' not found.",
qWarning( "Remote plugin '%s' not found",
exec.toUtf8().constData() );
m_failed = true;
invalidate();

View File

@@ -467,7 +467,8 @@ AudioSoundIo::setupWidget::setupWidget( QWidget * _parent ) :
reconnectSoundIo();
bool ok = connect( &m_backendModel, SIGNAL(dataChanged()), &m_setupUtil, SLOT(reconnectSoundIo()));
[[maybe_unused]] bool ok = connect(&m_backendModel, &ComboBoxModel::dataChanged,
&m_setupUtil, &AudioSoundIoSetupUtil::reconnectSoundIo);
assert(ok);
m_backend->setModel( &m_backendModel );
@@ -476,7 +477,8 @@ AudioSoundIo::setupWidget::setupWidget( QWidget * _parent ) :
AudioSoundIo::setupWidget::~setupWidget()
{
bool ok = disconnect( &m_backendModel, SIGNAL(dataChanged()), &m_setupUtil, SLOT(reconnectSoundIo()));
[[maybe_unused]] bool ok = disconnect(&m_backendModel, &ComboBoxModel::dataChanged,
&m_setupUtil, &AudioSoundIoSetupUtil::reconnectSoundIo);
assert(ok);
if (m_soundio)
{

View File

@@ -39,7 +39,7 @@ namespace lmms::gui {
QString SampleLoader::openAudioFile(const QString& previousFile)
{
auto openFileDialog = FileDialog(nullptr, QObject::tr("Open audio file"));
auto dir = !previousFile.isEmpty() ? PathUtil::toAbsolute(previousFile) : ConfigManager::inst()->userSamplesDir();
auto dir = !previousFile.isEmpty() ? QFileInfo(PathUtil::toAbsolute(previousFile)).absolutePath() : ConfigManager::inst()->userSamplesDir();
// change dir to position of previously opened file
openFileDialog.setDirectory(dir);