From 774b35cccfb2d3c94374bd47398d19e1b3c6f012 Mon Sep 17 00:00:00 2001 From: Dominik Riebeling Date: Thu, 23 Dec 2021 11:12:31 +0100 Subject: [PATCH] rbutil: Deploy support in cmake. Add a "deploy" target that will create a distributable file. - Linux: AppImage. - Windows: zip file. - MacOS: dmg. Change-Id: Id8ae9c021bc5bbb1abf066205b57d943c3f3b327 --- utils/CMakeLists.txt | 9 +++ utils/cmake/deploy.cmake | 141 ++++++++++++++++++++++++++++++++++++ utils/cmake/download.cmake | 47 ++++++++++++ utils/rbutilqt/dmgbuild.cfg | 10 ++- 4 files changed, 205 insertions(+), 2 deletions(-) create mode 100644 utils/cmake/deploy.cmake create mode 100644 utils/cmake/download.cmake diff --git a/utils/CMakeLists.txt b/utils/CMakeLists.txt index 1536465e3b..84d085d792 100644 --- a/utils/CMakeLists.txt +++ b/utils/CMakeLists.txt @@ -48,6 +48,8 @@ else() find_package(Qt6 REQUIRED COMPONENTS Core Core5Compat Widgets Svg Multimedia Network LinguistTools OPTIONAL_COMPONENTS Test) endif() +get_target_property(_qmake_executable Qt${QT_VERSION_MAJOR}::qmake IMPORTED_LOCATION) +get_filename_component(QT_BINDIR "${_qmake_executable}" DIRECTORY) message("-- Found Qt${QT_VERSION_MAJOR}: ${Qt${QT_VERSION_MAJOR}_DIR}") # If we're on Linux, try to find the used libs in the system. @@ -197,6 +199,13 @@ set_property(TARGET RockboxUtility PROPERTY AUTOMOC ON) set_property(TARGET RockboxUtility PROPERTY AUTORCC ON) set_property(TARGET RockboxUtility PROPERTY AUTOUIC ON) +include(${CMAKE_CURRENT_LIST_DIR}/cmake/deploy.cmake) +deploy_qt(RockboxUtility + ${QT_BINDIR} + ${CMAKE_CURRENT_LIST_DIR}/../docs/logo/rockbox-clef.svg + ${CMAKE_CURRENT_LIST_DIR}/rbutilqt/RockboxUtility.desktop + ${CMAKE_CURRENT_LIST_DIR}/rbutilqt/dmgbuild.cfg) + add_library(rbbase ${CMAKE_CURRENT_LIST_DIR}/../tools/iriver.c ${CMAKE_CURRENT_LIST_DIR}/../tools/iriver.h diff --git a/utils/cmake/deploy.cmake b/utils/cmake/deploy.cmake new file mode 100644 index 0000000000..8b1c837bcd --- /dev/null +++ b/utils/cmake/deploy.cmake @@ -0,0 +1,141 @@ +# +# __________ __ ___. +# Open \______ \ ____ ____ | | _\_ |__ _______ ___ +# Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / +# Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < +# Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ +# \/ \/ \/ \/ \/ +# +# All files in this archive are subject to the GNU General Public License. +# See the file COPYING in the source tree root for full license agreement. +# +# This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY +# KIND, either express or implied. +# + +# include this file to +# - get a new target "deploy" +# - get a function "deploy_qt()" which will add a deploy target that creates a +# zip / AppImage / dmg and depends on "deploy". + +if(NOT have_deploy) + add_custom_target(deploy) + set(have_deploy ON) +endif() + +# Linux: Build AppImage +if(CMAKE_SYSTEM_NAME STREQUAL "Linux") + set(LINUXDEPLOY ${CMAKE_CURRENT_BINARY_DIR}/linuxdeploy-x86_64.AppImage) + add_custom_command( + OUTPUT ${LINUXDEPLOY} + COMMAND ${CMAKE_COMMAND} + -DOUTDIR=${CMAKE_CURRENT_BINARY_DIR} + -DURL=https://github.com/linuxdeploy/linuxdeploy/releases/download/continuous/linuxdeploy-x86_64.AppImage + -P ${CMAKE_CURRENT_LIST_DIR}/download.cmake + COMMAND ${CMAKE_COMMAND} + -DOUTDIR=${CMAKE_CURRENT_BINARY_DIR} + -DURL=https://github.com/linuxdeploy/linuxdeploy-plugin-qt/releases/download/continuous/linuxdeploy-plugin-qt-x86_64.AppImage + -P ${CMAKE_CURRENT_LIST_DIR}/download.cmake + ) + + function(deploy_qt target qtbindir iconfile desktopfile dmgbuildcfg) + if("${CMAKE_BUILD_TYPE}" STREQUAL "Debug") + message(WARNING "Deploying a Debug build.") + endif() + add_custom_command( + OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/${target}.AppImage + COMMENT "Creating AppImage ${target}" + COMMAND OUTPUT=${CMAKE_CURRENT_BINARY_DIR}/${target}.AppImage + ${LINUXDEPLOY} + --icon-file=${iconfile} + --desktop-file=${desktopfile} + --executable=$ + --appdir=AppImage-${target} + --output=appimage + --verbosity=2 + DEPENDS ${target} + ${LINUXDEPLOY} + ) + add_custom_target(deploy_${target} + DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/${target}.AppImage) + add_dependencies(deploy deploy_${target}) + endfunction() +endif() + +# MacOS: Build dmg +if(CMAKE_SYSTEM_NAME STREQUAL "Darwin") + function(deploy_qt target qtbindir iconfile desktopfile dmgbuildcfg) + if("${CMAKE_BUILD_TYPE}" STREQUAL "Debug") + message(WARNING "Deploying a Debug build.") + endif() + set(DMGBUILD ${CMAKE_CURRENT_BINARY_DIR}/venv/bin/dmgbuild) + find_program(MACDEPLOYQT_EXECUTABLE macdeployqt HINTS "${qtbindir}") + + add_custom_command( + COMMENT "Setting up dmgbuild virtualenv" + OUTPUT ${DMGBUILD} + COMMAND python3 -m venv ${CMAKE_CURRENT_BINARY_DIR}/venv + COMMAND ${CMAKE_CURRENT_BINARY_DIR}/venv/bin/python -m pip install -q dmgbuild biplist + ) + + add_custom_command( + # TODO: find a better way to figure the app bundle name. + OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/${target}.dmg + COMMENT "Running macdeployqt and creating dmg ${target}" + COMMAND ${MACDEPLOYQT_EXECUTABLE} ${target}.app + COMMAND ${DMGBUILD} -s ${dmgbuildcfg} + -Dappbundle=${target}.app + ${target} ${CMAKE_CURRENT_BINARY_DIR}/${target}.dmg + DEPENDS ${target} + ${DMGBUILD} + ) + add_custom_target(deploy_${target} + DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/${target}.dmg) + add_dependencies(deploy deploy_${target}) + endfunction() +endif() + +# Windows. Copy to dist folder, run windeployqt on the binary, compress to zip. +if(CMAKE_SYSTEM_NAME STREQUAL "Windows") + function(deploy_qt target qtbindir iconfile desktopfile dmgbuildcfg) + if("${CMAKE_BUILD_TYPE}" STREQUAL "Debug") + message(WARNING "Deploying a Debug build.") + endif() + set(_targetfile ${target}.exe) # TODO: Use property. OUTPUT_NAME seems to fail. + find_program(WINDEPLOYQT_EXECUTABLE windeployqt HINTS "${qtbindir}") + set(deploydir ${CMAKE_CURRENT_BINARY_DIR}/deploy-${target}) + if(WINDEPLOYQT_EXECUTABLE) + add_custom_command( + COMMENT "Creating deploy folder and running windeployqt" + OUTPUT ${deploydir}/${_targetfile} + COMMAND ${CMAKE_COMMAND} -E make_directory ${deploydir} + COMMAND ${CMAKE_COMMAND} -E copy_if_different ${_targetfile} ${deploydir} + COMMAND ${WINDEPLOYQT_EXECUTABLE} + $,--debug,--release> # on MinGW, release is mistaken as debug. + ${deploydir}/${_targetfile} + DEPENDS ${target} + ) + else() + add_custom_command( + COMMENT "Creating deploy folder" + OUTPUT ${deploydir}/${_targetfile} + COMMAND ${CMAKE_COMMAND} -E make_directory ${deploydir} + COMMAND ${CMAKE_COMMAND} -E copy_if_different ${_targetfile} ${deploydir} + DEPENDS ${target} + ) + endif() + add_custom_command( + COMMENT "Compressing to zip" + OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/${target}.zip + WORKING_DIRECTORY ${deploydir} + COMMAND ${CMAKE_COMMAND} -E tar c ${CMAKE_CURRENT_BINARY_DIR}/${target}.zip + --format=zip . + DEPENDS ${deploydir}/${_targetfile} + ) + + add_custom_target(deploy_${target} + DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/${target}.zip) + add_dependencies(deploy deploy_${target}) + endfunction() +endif() + diff --git a/utils/cmake/download.cmake b/utils/cmake/download.cmake new file mode 100644 index 0000000000..bd3df4d83d --- /dev/null +++ b/utils/cmake/download.cmake @@ -0,0 +1,47 @@ +# +# __________ __ ___. +# Open \______ \ ____ ____ | | _\_ |__ _______ ___ +# Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / +# Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < +# Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ +# \/ \/ \/ \/ \/ +# +# All files in this archive are subject to the GNU General Public License. +# See the file COPYING in the source tree root for full license agreement. +# +# This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY +# KIND, either express or implied. +# + +# This is a separate cmake script, to be invoked as +# cmake -P -DURL= -DOUTDIR= +# Downloads the file and store it in OUTDIR, using the file basename as output +# filename. +# The downloaded file gets its executable flag set. + +function(gettempdir basedir tmpdir) + # Create a random filename in current directory. + # Result stored in tmpdir. + string(RANDOM LENGTH 24 _tmp) + while(EXISTS "${basedir}/${_tmp}.tmp") + string(RANDOM LENGTH 24 _tmp) + endwhile() + set("${tmpdir}" "${basedir}/${_tmp}.tmp" PARENT_SCOPE) +endfunction() + +get_filename_component(fname "${URL}" NAME) + +if(EXISTS "${OUTDIR}/${fname}") + message("-- Found ${fname}") +else() + message("-- Downloading ${URL} ...") + gettempdir(${OUTDIR} tmp) + + # cmake CHOWN is 3.19+, thus download to a temporary folder, then copy. + file(DOWNLOAD "${URL}" "${tmp}/${fname}") + file(COPY "${tmp}/${fname}" DESTINATION "${OUTDIR}" + FILE_PERMISSIONS OWNER_EXECUTE OWNER_WRITE OWNER_READ) + file(REMOVE_RECURSE "${tmp}") +endif() + + diff --git a/utils/rbutilqt/dmgbuild.cfg b/utils/rbutilqt/dmgbuild.cfg index 65d7b274a0..8f7a933237 100644 --- a/utils/rbutilqt/dmgbuild.cfg +++ b/utils/rbutilqt/dmgbuild.cfg @@ -1,9 +1,15 @@ # Configuration for creating a dmg with dmgbuild # (https://github.com/al45tair/dmgbuild) +# Needs biplist as additional package. import os +import biplist -files = [ 'RockboxUtility.app' ] +_appbundle = defines['appbundle'] +_plist = biplist.readPlist(os.path.join(_appbundle, 'Contents/Info.plist')) +_iconfile = os.path.join(_appbundle, 'Contents/Resources', _plist['CFBundleIconFile']) + +files = [ _appbundle ] +icon = _iconfile background = '#c6d6f5' -icon = os.path.join(defines['basepath'], 'rbutilqt/icons/rbutilqt.icns')