From 66e0e21482cb6ce920bb17446ee716fd22a36fca Mon Sep 17 00:00:00 2001 From: Andreas Traczyk <andreas.traczyk@savoirfairelinux.com> Date: Thu, 13 Apr 2023 14:55:39 -0400 Subject: [PATCH] packaging: windows: optionally skip windeployqt This is a big time saver in the context of CI testing where we only need to supply some env vars instead. It would also be nice to also have the env vars filled out for CMake-generated project runtime configs to prevent unnecessary deployment for development builds, but that's not done in this commit. Change-Id: I338827cb87c9fc71c9a6b4b3076b22aa7cf7cfa3 --- CMakeLists.txt | 41 ------ JamiInstaller/HarvestFilter.xslt | 21 --- extras/build/cmake/time_stamp_check.cmake | 13 -- extras/build/cmake/time_stamp_create.cmake | 8 -- .../build/cmake/windows_daemon_deploy.cmake | 21 --- extras/build/cmake/windows_qt_deploy.cmake | 14 -- extras/scripts/build-windows.py | 125 +++++++++++++----- 7 files changed, 92 insertions(+), 151 deletions(-) delete mode 100644 extras/build/cmake/time_stamp_check.cmake delete mode 100644 extras/build/cmake/time_stamp_create.cmake delete mode 100644 extras/build/cmake/windows_daemon_deploy.cmake delete mode 100644 extras/build/cmake/windows_qt_deploy.cmake diff --git a/CMakeLists.txt b/CMakeLists.txt index f7fd193ca..e9cac8f50 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -556,47 +556,6 @@ if(MSVC) target_sources(${PROJECT_NAME} PRIVATE ${QM_FILES}) endif() - # POST_BUILD steps - - # check time stamp - set(TIME_STAMP_FILE ".deploy.stamp") - add_custom_command( - TARGET ${PROJECT_NAME} - POST_BUILD - WORKING_DIRECTORY "$<TARGET_FILE_DIR:${PROJECT_NAME}>" - COMMAND ${CMAKE_COMMAND} -DTIME_STAMP_FILE=${TIME_STAMP_FILE} - -P ${EXTRAS_DIR}/build/cmake/time_stamp_check.cmake) - - # copy runtime files and run windeployqt on target and deploy Qt libs - add_custom_command( - TARGET ${PROJECT_NAME} - POST_BUILD - WORKING_DIRECTORY "$<TARGET_FILE_DIR:${PROJECT_NAME}>" - COMMAND ${CMAKE_COMMAND} -DTIME_STAMP_FILE=${TIME_STAMP_FILE} - -DCOPY_TO_PATH=$<TARGET_FILE_DIR:${PROJECT_NAME}> - -DDRING_PATH=${DAEMON_DIR} - -DPROJECT_ROOT_DIR=${PROJECT_SOURCE_DIR} - -DPACKAGING_DIR=${PACKAGING_DIR} - -P ${EXTRAS_DIR}/build/cmake/windows_daemon_deploy.cmake) - - add_custom_command( - TARGET ${PROJECT_NAME} - POST_BUILD - WORKING_DIRECTORY "$<TARGET_FILE_DIR:${PROJECT_NAME}>" - COMMAND ${CMAKE_COMMAND} -DTIME_STAMP_FILE=${TIME_STAMP_FILE} - -DWIN_DEPLOY_QT_PATH=${CMAKE_PREFIX_PATH}/bin - -DQML_SRC_DIR=${APP_SRC_DIR} - -DEXE_NAME=$<TARGET_FILE:${PROJECT_NAME}> - -P ${EXTRAS_DIR}/build/cmake/windows_qt_deploy.cmake) - - # create time stamp - add_custom_command( - TARGET ${PROJECT_NAME} - POST_BUILD - WORKING_DIRECTORY "$<TARGET_FILE_DIR:${PROJECT_NAME}>" - COMMAND ${CMAKE_COMMAND} -DTIME_STAMP_FILE=${TIME_STAMP_FILE} - -P ${EXTRAS_DIR}/build/cmake/time_stamp_create.cmake) - # executable name set_target_properties(${PROJECT_NAME} PROPERTIES OUTPUT_NAME "Jami") elseif (NOT APPLE) diff --git a/JamiInstaller/HarvestFilter.xslt b/JamiInstaller/HarvestFilter.xslt index 4b78b7c76..0a043ec87 100644 --- a/JamiInstaller/HarvestFilter.xslt +++ b/JamiInstaller/HarvestFilter.xslt @@ -12,33 +12,12 @@ xmlns:wix="http://schemas.microsoft.com/wix/2006/wi"> </xsl:template> <xsl:key name="service-search" match="wix:Component[contains(wix:File/@Source, 'Jami.exe')]" use="@Id" /> - <xsl:key name="vc-service-search" match="wix:Component[contains(wix:File/@Source, 'redist') and contains(wix:File/@Source, 'vc')]" use="@Id" /> <xsl:key name="pdb-search" match="wix:Component[contains(wix:File/@Source, '.pdb')]" use="@Id" /> - <xsl:key name="lib-search" match="wix:Component[contains(wix:File/@Source, 'Jami.lib')]" use="@Id" /> - <xsl:key name="exp-search" match="wix:Component[contains(wix:File/@Source, 'Jami.exp')]" use="@Id" /> - <xsl:key name="qmake-search" match="wix:Component[contains(wix:File/@Source, 'qmake')]" use="@Id" /> - <xsl:key name="obj-search" match="wix:Component[contains(wix:File/@Source, '.obj')]" use="@Id" /> - <xsl:key name="tlog-search" match="wix:Component[contains(wix:File/@Source, '.tlog')]" use="@Id" /> - <xsl:key name="log-search" match="wix:Component[contains(wix:File/@Source, '.log')]" use="@Id" /> <xsl:template match="wix:Component[key('service-search', @Id)]" /> - <xsl:template match="wix:Component[key('vc-service-search', @Id)]" /> <xsl:template match="wix:Component[key('pdb-search', @Id)]" /> - <xsl:template match="wix:Component[key('lib-search', @Id)]" /> - <xsl:template match="wix:Component[key('exp-search', @Id)]" /> - <xsl:template match="wix:Component[key('qmake-search', @Id)]" /> - <xsl:template match="wix:Component[key('obj-search', @Id)]" /> - <xsl:template match="wix:Component[key('tlog-search', @Id)]" /> - <xsl:template match="wix:Component[key('log-search', @Id)]" /> <xsl:template match="wix:ComponentRef[key('service-search', @Id)]" /> - <xsl:template match="wix:ComponentRef[key('vc-service-search', @Id)]" /> <xsl:template match="wix:ComponentRef[key('pdb-search', @Id)]" /> - <xsl:template match="wix:ComponentRef[key('lib-search', @Id)]" /> - <xsl:template match="wix:ComponentRef[key('exp-search', @Id)]" /> - <xsl:template match="wix:ComponentRef[key('qmake-search', @Id)]" /> - <xsl:template match="wix:ComponentRef[key('obj-search', @Id)]" /> - <xsl:template match="wix:ComponentRef[key('tlog-search', @Id)]" /> - <xsl:template match="wix:ComponentRef[key('log-search', @Id)]" /> </xsl:stylesheet> \ No newline at end of file diff --git a/extras/build/cmake/time_stamp_check.cmake b/extras/build/cmake/time_stamp_check.cmake deleted file mode 100644 index b0f44b7d9..000000000 --- a/extras/build/cmake/time_stamp_check.cmake +++ /dev/null @@ -1,13 +0,0 @@ -execute_process(COMMAND git rev-parse HEAD - OUTPUT_VARIABLE VERSION_PATCH) - -# remove leading and trailing spaces -string(STRIP "${VERSION_PATCH}" VERSION_PATCH) - -message("Checking time stamp ...") -if(EXISTS ${TIME_STAMP_FILE}) - file (STRINGS ${TIME_STAMP_FILE} VERSION_IN_FILE) - if(NOT "${VERSION_IN_FILE}" STREQUAL "${VERSION_PATCH}") - file (REMOVE "${TIME_STAMP_FILE}") - endif() -endif() \ No newline at end of file diff --git a/extras/build/cmake/time_stamp_create.cmake b/extras/build/cmake/time_stamp_create.cmake deleted file mode 100644 index 7b8aff2b1..000000000 --- a/extras/build/cmake/time_stamp_create.cmake +++ /dev/null @@ -1,8 +0,0 @@ -execute_process(COMMAND git rev-parse HEAD - OUTPUT_VARIABLE VERSION_PATCH) -if (EXISTS ${TIME_STAMP_FILE}) - message("Keep the old time stamp") -else() - message("Creating time stamp ...") - file(WRITE ${TIME_STAMP_FILE} "${VERSION_PATCH}") -endif() \ No newline at end of file diff --git a/extras/build/cmake/windows_daemon_deploy.cmake b/extras/build/cmake/windows_daemon_deploy.cmake deleted file mode 100644 index 25ff6a3c0..000000000 --- a/extras/build/cmake/windows_daemon_deploy.cmake +++ /dev/null @@ -1,21 +0,0 @@ -if (EXISTS ${TIME_STAMP_FILE}) - message("No need for daemon deployment") -else() - message("Daemon deploying ...") - file(COPY "${DRING_PATH}/contrib/build/openssl/libcrypto-1_1-x64.dll" - "${DRING_PATH}/contrib/build/openssl/libssl-1_1-x64.dll" - "${PACKAGING_DIR}/wix/qt.conf" - "${PROJECT_ROOT_DIR}/resources/images/jami.ico" - "${PACKAGING_DIR}/wix/License.rtf" - DESTINATION ${COPY_TO_PATH}) - # Cannot copy symbolic link using file COPY, create insread. - file(GLOB_RECURSE RingTones "${DRING_PATH}/ringtones/*.ul" - "${DRING_PATH}/ringtones/*.ogg" - "${DRING_PATH}/ringtones/*.wav" - "${DRING_PATH}/ringtones/*.opus") - list(REMOVE_ITEM RingTones "${DRING_PATH}/ringtones/default.opus") - file(COPY ${RingTones} - DESTINATION ${COPY_TO_PATH}/ringtones) - file(CREATE_LINK "${COPY_TO_PATH}/ringtones/01_AfroNigeria.opus" - "${COPY_TO_PATH}/ringtones/default.opus") -endif() diff --git a/extras/build/cmake/windows_qt_deploy.cmake b/extras/build/cmake/windows_qt_deploy.cmake deleted file mode 100644 index 8957d1bdb..000000000 --- a/extras/build/cmake/windows_qt_deploy.cmake +++ /dev/null @@ -1,14 +0,0 @@ -if (EXISTS ${TIME_STAMP_FILE}) - message("No need for Qt deployment in dir " ${QML_SRC_DIR}) -else() - message("Qt deploying in dir " ${QML_SRC_DIR}) - execute_process(COMMAND "${WIN_DEPLOY_QT_PATH}/windeployqt.exe" - --verbose 1 - --qmldir ${QML_SRC_DIR} - --release ${EXE_NAME}) - if (DEFINED OFF_SCREEN_PLUGIN_REQUESTED) - # for not showing window when testing - file(COPY "${OFF_SCREEN_PLUGIN_PATH}/qoffscreen.dll" - DESTINATION ${COPY_TO_PATH}) - endif() -endif() \ No newline at end of file diff --git a/extras/scripts/build-windows.py b/extras/scripts/build-windows.py index b259ed817..5ba8ae059 100644 --- a/extras/scripts/build-windows.py +++ b/extras/scripts/build-windows.py @@ -41,6 +41,8 @@ import subprocess import platform import argparse import multiprocessing +import shutil +import time # Visual Studio helpers @@ -93,43 +95,26 @@ def execute_cmd(cmd, with_shell=False, env_vars=None, cmd_dir=repo_root_dir): return proc.returncode -def get_latest_vs_version(): - """Find the latest visual c++ compiler tools version.""" +def get_vs_prop(prop): + """Get a visual studio property.""" args = [ "-latest", "-products *", "-requires Microsoft.VisualStudio.Component.VC.Tools.x86.x64", - "-property installationVersion", + "-property " + prop, ] cmd = [VS_WHERE_PATH] + args output = subprocess.check_output(" ".join(cmd)).decode("utf-8") - if output: - return output.splitlines()[0].split(".")[0] - else: - return - - -def find_latest_vs_dir(): - """Find the latest visual c++ compiler tools path.""" - args = [ - "-latest", - "-products *", - "-requires Microsoft.VisualStudio.Component.VC.Tools.x86.x64", - "-property installationPath", - ] - cmd = [VS_WHERE_PATH] + args - output = subprocess.check_output( - " ".join(cmd)).decode("utf-8", errors="ignore") if output: return output.splitlines()[0] else: - return + return None def find_ms_build(): """Find the latest msbuild executable.""" filename = "MSBuild.exe" - vs_path = find_latest_vs_dir() + vs_path = get_vs_prop("installationPath") if vs_path is None: return for root, _, files in os.walk(os.path.join(vs_path, "MSBuild")): @@ -169,7 +154,7 @@ def get_vs_env(arch="x64", _platform="", version=""): def get_vs_env_cmd(arch="x64", _platform="", version=""): """Get the vcvarsall.bat command.""" - vs_path = find_latest_vs_dir() + vs_path = get_vs_prop("installationPath") if vs_path is None: return vc_env_init = [os.path.join( @@ -286,6 +271,7 @@ def build(config_str, qt_dir, tests): cmake_options = [ "-DWITH_DAEMON_SUBMODULE=ON", "-DCMAKE_PREFIX_PATH=" + qt_dir, + "-DCMAKE_MSVCIDE_RUN_PATH=" + qt_dir + "\\bin", "-DCMAKE_INSTALL_PREFIX=" + daemon_bin_dir, "-DLIBJAMI_INCLUDE_DIR=" + daemon_dir + "\\src\\jami", "-DCMAKE_SYSTEM_VERSION=" + WIN_SDK_VERSION, @@ -307,11 +293,73 @@ def build(config_str, qt_dir, tests): sys.exit(1) +def deploy_runtimes(qt_dir): + """Deploy the dependencies to the runtime directory.""" + print("Deploying runtime dependencies") + + runtime_dir = os.path.join(repo_root_dir, "x64", "Release") + stamp_file = os.path.join(runtime_dir, ".deploy.stamp") + if os.path.exists(stamp_file): + return + + daemon_dir = os.path.join(repo_root_dir, "daemon") + ringtone_dir = os.path.join(daemon_dir, "ringtones") + packaging_dir = os.path.join(repo_root_dir, "extras", "packaging") + + def install_file(src, rel_path): + shutil.copy(os.path.join(rel_path, src), runtime_dir) + + print("Copying libjami dependencies") + install_file("contrib/build/openssl/libcrypto-1_1-x64.dll", daemon_dir) + install_file("contrib/build/openssl/libssl-1_1-x64.dll", daemon_dir) + # Ringtone files (ul,ogg,wav,opus files in the daemon ringtone dir). + + print("Copying ringtones") + ringtone_dir = os.path.join(daemon_dir, "ringtones") + ringtone_files = [f for f in os.listdir(ringtone_dir) if f.endswith( + (".ul", ".ogg", ".wav", ".opus"))] + ringtone_files = [os.path.join(ringtone_dir, f) for f in ringtone_files] + default_ringtone = os.path.join(ringtone_dir, "default.opus") + ringtone_files.remove(default_ringtone) + ringtone_dir_out = os.path.join(runtime_dir, "ringtones") + if os.path.exists(ringtone_dir_out): + shutil.rmtree(ringtone_dir_out) + os.makedirs(ringtone_dir_out, exist_ok=True) + for ringtone in ringtone_files: + shutil.copy(ringtone, os.path.join(runtime_dir, "ringtones")) + # Create a hard link to the default ringtone (Windows). + os.link(os.path.join(runtime_dir, "ringtones", "01_AfroNigeria.opus"), + os.path.join(runtime_dir, "ringtones", "default.opus")) + + print("Copying misc. client configuration files") + install_file("wix/qt.conf", packaging_dir) + install_file("wix/License.rtf", packaging_dir) + install_file("resources/images/jami.ico", repo_root_dir) + + # windeployqt + print("Running windeployqt (this may take a while)...") + win_deploy_qt = os.path.join(qt_dir, "bin", "windeployqt.exe") + qml_src_dir = os.path.join(repo_root_dir, "src", "app") + os.environ["VCINSTALLDIR"] = os.path.join( + get_vs_prop("installationPath"), "VC") + executable = os.path.join(runtime_dir, "Jami.exe") + execute_cmd([win_deploy_qt, "--verbose", "1", "--no-compiler-runtime", + "--qmldir", qml_src_dir, "--release", executable], + False, cmd_dir=runtime_dir) + + with open(stamp_file, "w", encoding="utf-8") as file: + # Write the current time to the file. + file.write(str(time.time())) + + def run_tests(config_str, qt_dir): """Run tests.""" print("Running client tests") os.environ["PATH"] += os.pathsep + os.path.join(qt_dir, 'bin') + daemon_dir = os.path.join(repo_root_dir, "daemon") + os.environ["PATH"] += os.pathsep + \ + os.path.join(daemon_dir, "contrib", "build", "openssl") os.environ["QT_QPA_PLATFORM"] = "offscreen" os.environ["QT_QUICK_BACKEND"] = "software" os.environ['QT_QPA_FONTDIR'] = os.path.join( @@ -319,6 +367,9 @@ def run_tests(config_str, qt_dir): os.environ['QT_PLUGIN_PATH'] = os.path.join(qt_dir, 'plugins') os.environ['QT_QPA_PLATFORM_PLUGIN_PATH'] = os.path.join( qt_dir, 'plugins', 'platforms') + os.environ['QTWEBENGINEPROCESS_PATH'] = os.path.join( + qt_dir, 'bin', 'QtWebEngineProcess.exe') + os.environ["QML2_IMPORT_PATH"] = os.path.join(qt_dir, "qml") tests_dir = os.path.join(build_dir, "tests") if execute_cmd(["ctest", "-V", "-C", config_str], @@ -357,7 +408,7 @@ def generate_zip(version): app_output_dir = os.path.join(repo_root_dir, 'x64', 'Release') app_files = os.path.join(app_output_dir, '*') - # TODO: exclude Jami.PDB, .deploy.stamp, and vc_redist.x64.exe + # TODO: exclude Jami.PDB, .deploy.stamp artifacts_dir = os.path.join(build_dir, 'artifacts') if not os.path.exists(artifacts_dir): @@ -404,7 +455,13 @@ def parse_args(): parser.add_argument( "-i", "--init", action="store_true", help="Initialize submodules") parser.add_argument( - "-s", + '-sd', + '--skip-deploy', + action='store_true', + default=False, + help='Force skip deployment of runtime files needed for packaging') + parser.add_argument( + "-sb", "--skip-build", action="store_true", default=False, @@ -426,7 +483,7 @@ def main(): print("These scripts will only run on a 64-bit system for now.") sys.exit(1) if sys.platform == "win32": - vs_version = get_latest_vs_version() + vs_version = get_vs_prop("installationVersion").split(".")[0] if vs_version is None or int(vs_version) < 15: print("Visual Studio 2017 or later is required.") sys.exit(1) @@ -454,19 +511,21 @@ def main(): sys.exit(0) config_str = ('Release', 'Beta')[parsed_args.beta] - skip_build = parsed_args.skip_build + + def do_build(do_tests): + if not parsed_args.skip_build: + build(config_str, parsed_args.qt, do_tests) + if not parsed_args.skip_deploy: + deploy_runtimes(parsed_args.qt) if parsed_args.subcommand == "pack": - if not skip_build: - build(config_str, parsed_args.qt, False) - elif parsed_args.msi: + do_build(False) + if parsed_args.msi: generate_msi(get_version()) elif parsed_args.zip: generate_zip(get_version()) else: - if not skip_build: - build(config_str, parsed_args.qt, - parsed_args.tests) + do_build(parsed_args.tests) if parsed_args.tests: run_tests(config_str, parsed_args.qt) -- GitLab