diff --git a/CMakeLists.txt b/CMakeLists.txt index f7fd193ca3beb458675cc23b5f23620743eb8526..e9cac8f50dc6d98c4911f4ca7d9cf4ad7b1b35f1 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 4b78b7c76bf71f7a92955a8cf0810be6f0406217..0a043ec8700bef6469c6557d14d758791a294a82 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 b0f44b7d91931214597e60db7d36baf37314615c..0000000000000000000000000000000000000000 --- 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 7b8aff2b1a794382154d1e59d4414c69ed082508..0000000000000000000000000000000000000000 --- 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 25ff6a3c062388f17c8a1a299cf7bf2e3d10f336..0000000000000000000000000000000000000000 --- 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 8957d1bdb16a345ba375be981328624a7bcb5d7b..0000000000000000000000000000000000000000 --- 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 b259ed817cbeebd85277ebbb23cbb993ef282310..5ba8ae05962289ca940aa9683dff5f3bf01d50ac 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)