Skip to content

Instantly share code, notes, and snippets.

@alcroito
Created October 3, 2020 17:05
Show Gist options
  • Star 3 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save alcroito/549d47b4d961936a24a518aaada685f3 to your computer and use it in GitHub Desktop.
Save alcroito/549d47b4d961936a24a518aaada685f3 to your computer and use it in GitHub Desktop.
Installing Qt via Online installer, building and deploying a qmake app project with Github Actions
set(CMAKE_EXECUTE_PROCESS_COMMAND_ECHO STDOUT)
if(CMAKE_HOST_WIN32)
set(qt_target_dir "msvc2019_64")
elseif(CMAKE_HOST_APPLE)
set(qt_target_dir "clang_64")
else()
set(qt_target_dir "gcc_64")
endif()
if(NOT SOURCE_DIR)
set(SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}")
endif()
# Expects a path to a Qt installed via the Qt installer, which has a subdir for each target platform.
# e.g. /path/to/Qt/5.15.1 (has a subdir /path/to/qt/5.15.1/gcc_64)
if(NOT QT_PREFIX_DIR)
set(QT_PREFIX_DIR "${CMAKE_CURRENT_SOURCE_DIR}/official_qt")
message(STATUS "Inferring Qt prefix location to: ${QT_PREFIX_DIR}")
else()
get_filename_component(QT_PREFIX_DIR "${QT_PREFIX_DIR}" ABSOLUTE)
endif()
set(qmake_path "${QT_PREFIX_DIR}/${qt_target_dir}/bin/qmake")
if(CMAKE_HOST_WIN32)
string(APPEND qmake_path ".exe")
endif()
message(STATUS "Using qmake at ${qmake_path}.")
if(NOT BUILD_DIR)
set(BUILD_DIR "${CMAKE_CURRENT_SOURCE_DIR}/build")
endif()
set(build_dir "${BUILD_DIR}")
message(STATUS "Setting build directory to ${build_dir}.")
file(MAKE_DIRECTORY "${build_dir}")
# I'm surprised this works on Windows. CMake docs says the process
# is executed using system API directly (without an intermediate shell).
# And yet it seems to work.
set(command_prefix_args "")
if(ENV_SCRIPT)
set(command_prefix_args "${ENV_SCRIPT}" "&&")
endif()
execute_process(COMMAND
${command_prefix_args}
"${qmake_path}" "${SOURCE_DIR}"
WORKING_DIRECTORY "${build_dir}"
RESULT_VARIABLE result)
if(NOT result EQUAL 0)
message(FATAL_ERROR "Failed with error: ${result}")
endif()
if(CMAKE_HOST_WIN32)
execute_process(COMMAND
${command_prefix_args}
"nmake"
WORKING_DIRECTORY "${build_dir}"
RESULT_VARIABLE result)
else()
execute_process(
${command_prefix_args}
COMMAND "make"
WORKING_DIRECTORY "${build_dir}"
RESULT_VARIABLE result)
endif()
if(NOT result EQUAL 0)
message(FATAL_ERROR "Failed with error: ${result}")
endif()
set(CMAKE_EXECUTE_PROCESS_COMMAND_ECHO STDOUT)
if(CMAKE_HOST_WIN32)
set(qt_target_dir "msvc2019_64")
elseif(CMAKE_HOST_APPLE)
set(qt_target_dir "clang_64")
else()
set(qt_target_dir "gcc_64")
endif()
# Expects a path to a Qt installed via the Qt installer, which has a subdir for each target platform.
# e.g. /path/to/Qt/5.15.1 (has a subdir /path/to/qt/5.15.1/gcc_64)
if(NOT QT_PREFIX_DIR)
set(QT_PREFIX_DIR "${CMAKE_CURRENT_SOURCE_DIR}/official_qt")
message(STATUS "Inferring Qt prefix location to: ${QT_PREFIX_DIR}")
else()
get_filename_component(QT_PREFIX_DIR "${QT_PREFIX_DIR}" ABSOLUTE)
endif()
if(NOT TARGET_APP_PATH)
message(FATAL_ERROR "No TARGET_APP_PATH path given: '${TARGET_APP_PATH}'.")
endif()
get_filename_component(TARGET_APP_PATH "${TARGET_APP_PATH}" ABSOLUTE)
set(qt_bin_dir "${QT_PREFIX_DIR}/${qt_target_dir}/bin")
# TODO Implement for all platforms.
if(CMAKE_HOST_APPLE)
set(deploy_tool "${qt_bin_dir}/macdeployqt")
message(STATUS "Using '${deploy_tool}'.")
set(args "")
list(APPEND args "${TARGET_APP_PATH}")
if(QML_DIR)
list(APPEND args "-qmldir=${QML_DIR}")
endif()
if(VERBOSE)
list(APPEND args "-verbose=3")
endif()
execute_process(COMMAND
"${deploy_tool}"
${args}
RESULT_VARIABLE result)
if(NOT result EQUAL 0)
message(FATAL_ERROR "Failed with error: ${result}")
endif()
endif()
if(WIN32)
set(installer_file_os "windows-x86")
set(installer_file_ext "exe")
set(qt_package_arch "win64_msvc2019_64")
elseif(APPLE)
set(installer_file_os "mac-x64")
set(installer_file_ext "dmg")
set(qt_package_arch "clang_64")
else()
set(installer_file_os "linux-x64")
set(installer_file_ext "run")
set(qt_package_arch "gcc_64")
endif()
set(installer_version "4.0.0_beta")
if(NOT PACKAGE)
set(PACKAGE "qt.qt5.5151")
endif()
if(NOT QT_PACKAGE_ARCH)
set(QT_PACKAGE_ARCH "${qt_package_arch}")
endif()
if(NOT INSTALL_ROOT)
set(INSTALL_ROOT "${CMAKE_CURRENT_SOURCE_DIR}/official_qt")
endif()
get_filename_component(install_root_abs "${INSTALL_ROOT}" ABSOLUTE)
if(EXISTS "${install_root_abs}")
message(STATUS "Found existing directory at ${install_root_abs}.")
if(REUSE_CACHED_QT)
message(STATUS "Assuming Qt is already installed at ${install_root_abs}. Exiting.")
return()
endif()
endif()
if(PACKAGES)
set(packages_to_install "${PACKAGES}")
else()
set(packages_to_install "${PACKAGE}.${QT_PACKAGE_ARCH}")
endif()
set(installer_file_base "qt-unified-${installer_file_os}-${installer_version}-online")
set(qt_base_url "http://download.qt.io/development_releases/online_installers")
set(installer_file "${installer_file_base}.${installer_file_ext}")
if(DEFINED ENV{TEMP})
set(TMPDIR $ENV{TEMP})
else()
set(TMPDIR "/tmp")
endif()
set(final_installer_path "${CMAKE_CURRENT_SOURCE_DIR}/${installer_file}")
if(EXISTS "${final_installer_path}")
message(STATUS "Found existing installer at ${final_installer_path}.")
endif()
if(EXISTS "${final_installer_path}" AND REUSE_INSTALLER)
message(STATUS "Reusing existing installer found at '${final_installer_path}'.")
else()
message(STATUS "Downloading ${installer_file} from ${qt_base_url}/${installer_file} to ${TMPDIR}/${installer_file}")
file(DOWNLOAD "${qt_base_url}/${installer_file}" ${TMPDIR}/${installer_file} SHOW_PROGRESS)
message(STATUS "Copying ${TMPDIR}/${installer_file} to ${final_installer_path}")
file(COPY "${TMPDIR}/${installer_file}" DESTINATION . FILE_PERMISSIONS OWNER_EXECUTE OWNER_WRITE OWNER_READ)
file(REMOVE "${TMPDIR}/${installer_file}")
endif()
if(APPLE)
execute_process(COMMAND hdiutil attach "${installer_file}")
set(installer_file "/Volumes/${installer_file_base}/${installer_file_base}.app/Contents/MacOS/${installer_file_base}")
else()
set(installer_file "./${installer_file}")
endif()
set(args "${installer_file}" install ${packages_to_install} --root "${install_root_abs}" --accept-licenses --accept-obligations --confirm-command --auto-answer telemetry-question=No,AssociateCommonFiletypes=No,installationErrorWithCancel=Ignore,OverwriteTargetDirectory=No)
string(REPLACE ";" " " args_space_delimited "${args}")
message(STATUS "Running ${args_space_delimited}")
execute_process(COMMAND ${args}
RESULT_VARIABLE result)
if(NOT result EQUAL 0)
message(FATAL_ERROR "Installation failed. Exit code: ${result}")
endif()
if(APPLE)
execute_process(COMMAND hdiutil detach "/Volumes/${installer_file_base}")
endif()
name: Build App
on: push
env:
QT_VERSION: "5.15.1"
QT_PACKAGE_NAME: "qt.qt5.5151"
QT_INSTALL_DIR_NAME: "official_qt"
CI_APP_NAME: 'APP_NAME'
CI_ARCHIVE_EXT: '.tar.xz'
jobs:
build:
name: Build app on ${{ matrix.config.name }}
runs-on: ${{ matrix.config.os }}
strategy:
fail-fast: false
matrix:
config:
- {
name: "Ubuntu Latest GCC",
os: ubuntu-latest,
}
- {
name: "macOS Latest Clang",
os: macos-latest,
app_suffix: '.app',
artifact: "macOS"
}
- {
name: "Windows Latest MSVC 2019",
os: windows-latest,
environment_script: "C:/Program Files (x86)/Microsoft Visual Studio/2019/Enterprise/VC/Auxiliary/Build/vcvars64.bat",
}
steps:
- name: Checkout code
uses: actions/checkout@v2
- name: Install Qt Installer dependencies
if: runner.os == 'Linux'
run: sudo apt-get install libxkbcommon-x11-0 libgl1-mesa-dev
- name: Use cached Qt
uses: actions/cache@v2
env:
cache-name: cache-qt
with:
path: ${{ env.QT_INSTALL_DIR_NAME }}
key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ env.QT_PACKAGE_NAME }}
- name: Install Qt if not cached
run: cmake -DPACKAGE_NAME="${{ env.QT_PACKAGE_NAME }}" -DINSTALL_ROOT="${{ env.QT_INSTALL_DIR_NAME }}" -DREUSE_CACHED_QT=ON -P ci/install_qt.cmake
env:
QT_INSTALLER_JWT_TOKEN: ${{ secrets.QT_INSTALLER_JWT_TOKEN }}
- name: Build app
run: cmake -DSOURCE_DIR="${{ github.workspace }}" -DQT_PREFIX_DIR="${{ env.QT_INSTALL_DIR_NAME }}/${{ env.QT_VERSION }}" -DENV_SCRIPT="${{ matrix.config.environment_script }}" -P ci/build_with_qmake.cmake
- name: Deploy app
if: runner.os == 'macOS'
run: cmake -DQT_PREFIX_DIR="${{ env.QT_INSTALL_DIR_NAME }}/${{ env.QT_VERSION }}" -DTARGET_APP_PATH="build/${{ env.CI_APP_NAME }}${{ matrix.config.app_suffix }}" -DVERBOSE=ON -DQML_DIR=${{ github.workspace }} -P ci/deploy_app.cmake
- name: Pack
if: runner.os == 'macOS'
working-directory: build
run: cmake -E tar cJfv ../${{ env.CI_APP_NAME }}-${{ matrix.config.artifact }}-${{ github.run_id }}${{ env.CI_ARCHIVE_EXT }} "${{ env.CI_APP_NAME }}${{ matrix.config.app_suffix }}"
- name: Upload
if: runner.os == 'macOS'
uses: actions/upload-artifact@v1
with:
path: ${{ env.CI_APP_NAME }}-${{ matrix.config.artifact }}-${{ github.run_id }}${{ env.CI_ARCHIVE_EXT }}
name: ${{ env.CI_APP_NAME }}-${{ matrix.config.artifact }}-${{ github.run_id }}${{ env.CI_ARCHIVE_EXT }}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment