diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 05b1ed330..29129ee4c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -39,9 +39,6 @@ jobs: container: ${{ matrix.target.container }} timeout-minutes: 20 - env: - QT_BASE_DIR: ${{ github.workspace }}\deps\Qt - strategy: matrix: target: @@ -55,32 +52,24 @@ jobs: submodules: "recursive" - name: Cache Chocolatey packages - uses: actions/cache@v3 + uses: actions/cache@v4 with: path: ${{ runner.temp }}/choco key: choco-${{ hashFiles('Chocolatey.config') }} - - name: Install dependencies - run: python ./scripts/install_deps.py - - - name: Set QT_VERSION env var - run: python ./scripts/github_env.py --set-qt-version - - - name: Cache Qt + - name: Cache deps dir uses: actions/cache@v4 with: - path: ${{ env.QT_BASE_DIR }} - key: ${{ runner.os }}-Qt_${{ env.QT_VERSION }} + path: ./deps + key: ${{ runner.os }}-deps-${{ hashFiles('config.yaml') }} - - name: Install Qt - run: python ./scripts/install_deps.py --only qt + - name: Install dependencies + run: python ./scripts/install_deps.py --ci-env - name: Setup VC++ environment uses: ilammy/msvc-dev-cmd@v1 - name: Configure - env: - CMAKE_PREFIX_PATH: "${{ env.QT_BASE_DIR }}\\${{ env.QT_VERSION }}\\msvc2019_64\\" run: cmake -B build --preset=windows-release - name: Build @@ -138,11 +127,17 @@ jobs: with: submodules: "recursive" + - name: Cache deps dir + uses: actions/cache@v4 + with: + path: ./deps + key: ${{ runner.os }}-deps-${{ hashFiles('config.yaml') }} + - name: Install dependencies - run: ./scripts/install_deps.py + run: ./scripts/install_deps.py --ci-env - name: Configure - run: cmake -B build --preset=macos-release -DCMAKE_PREFIX_PATH=$(brew --prefix qt@6) + run: cmake -B build --preset=macos-release - name: Build run: cmake --build build -j8 @@ -240,7 +235,7 @@ jobs: run: git config --global --add safe.directory $GITHUB_WORKSPACE - name: Install dependencies - run: ./scripts/install_deps.py + run: ./scripts/install_deps.py --ci-env - name: Configure run: cmake -B build --preset=linux-release diff --git a/Brewfile b/Brewfile index 1b5188c42..07574f885 100644 --- a/Brewfile +++ b/Brewfile @@ -1,4 +1,3 @@ brew 'make' brew 'cmake' brew 'openssl' -brew 'qt@6' diff --git a/ChangeLog b/ChangeLog index c0308b168..823a46627 100644 --- a/ChangeLog +++ b/ChangeLog @@ -51,6 +51,7 @@ Enhancements: - #7381 Set macOS min version to macOS 12.0 - #7382 Re-run `macdeployqt6` to copy missing Qt 6 dependencies - #7384 Run `install_deps.py` script when building containers weekly +- #7389 Correct Qt macOS target and drop `Core5Compat` lib # 1.14.6 diff --git a/config.yaml b/config.yaml index 98367f5bb..a77296b0c 100644 --- a/config.yaml +++ b/config.yaml @@ -3,21 +3,22 @@ config: dependencies: command: choco install Chocolatey.config -y qt: - version: 6.7.2 + version: 6.7 mirror: https://qt.mirror.constant.com/ - install-dir: C:\Qt - modules: - - qt5compat - choco-ci: + base-dir: ./deps/qt + ci: edit-config: Chocolatey.config skip-packages: - cmake - visualstudio2022buildtools mac: - qt-prefix-command: brew --prefix qt@6 dependencies: command: brew bundle --file=Brewfile + qt: + version: 6.7 + mirror: https://qt.mirror.constant.com/ + base-dir: ./deps/qt linux: debian: &debian diff --git a/scripts/github_env.py b/scripts/github_env.py deleted file mode 100755 index d24356896..000000000 --- a/scripts/github_env.py +++ /dev/null @@ -1,33 +0,0 @@ -#!/usr/bin/env python3 - -import argparse -import lib.env as env -import lib.github as github - -qt_version_key = "QT_VERSION" - - -def main(): - parser = argparse.ArgumentParser() - parser.add_argument( - "--set-qt-version", - action="store_true", - help=f"Set {qt_version_key} env var", - ) - args = parser.parse_args() - - # important: load venv before loading modules that install deps. - env.ensure_in_venv(__file__) - - from lib.config import Config - - if args.set_qt_version: - config = Config() - _, qt_version, _, _ = config.get_qt_config() - github.set_env(qt_version_key, qt_version) - else: - raise RuntimeError("No option selected") - - -if __name__ == "__main__": - main() diff --git a/scripts/install_deps.py b/scripts/install_deps.py index c8515972a..b58dd4876 100755 --- a/scripts/install_deps.py +++ b/scripts/install_deps.py @@ -3,6 +3,11 @@ import os, sys, argparse, traceback import lib.env as env import lib.cmd_utils as cmd_utils +import lib.qt_utils as qt_utils +import lib.github as github + +path_env_var = "PATH" +cmake_prefix_env_var = "CMAKE_PREFIX_PATH" def main(): @@ -10,14 +15,16 @@ def main(): parser.add_argument( "--pause-on-exit", action="store_true", help="Useful on Windows" ) - parser.add_argument( - "--only", type=str, help="Only install the specified dependency" - ) parser.add_argument( "--only-python", action="store_true", help="Only install Python dependencies", ) + parser.add_argument( + "--ci-env", + action="store_true", + help="Set if running in CI environment", + ) args = parser.parse_args() env.ensure_dependencies() @@ -27,7 +34,7 @@ def main(): error = False if not args.only_python: try: - deps = Dependencies(args.only) + deps = Dependencies(args.ci_env) deps.install() except Exception: traceback.print_exc() @@ -42,12 +49,11 @@ def main(): class Dependencies: - def __init__(self, only): + def __init__(self, ci_env): from lib.config import Config self.config = Config() - self.only = only - self.ci_env = env.is_running_in_ci() + self.ci_env = ci_env if self.ci_env: print("CI environment detected") @@ -72,27 +78,18 @@ class Dependencies: windows.relaunch_as_admin(__file__) sys.exit() - only_qt = self.only == "qt" + qt = qt_utils.WindowsQt(*self.config.get_qt_config()) + qt.install() - # for ci, skip qt; we install qt separately so we can cache it. - if not self.ci_env or only_qt: - qt = windows.WindowsQt(*self.config.get_qt_config()) - qt_install_dir = qt.get_install_dir() - if qt_install_dir: - print(f"Skipping Qt, already installed at: {qt_install_dir}") - else: - qt.install() - - if not self.ci_env: - qt.set_env_vars() - - if only_qt: - return + if self.ci_env: + github.set_env_var(cmake_prefix_env_var, qt.get_install_dir()) + else: + windows.set_env_var(cmake_prefix_env_var, qt.get_install_dir()) choco = windows.WindowsChoco() if self.ci_env: choco.config_ci_cache() - edit_config, skip_packages = self.config.get_choco_ci_config() + edit_config, skip_packages = self.config.get_windows_ci_config() choco.remove_from_config(edit_config, skip_packages) command = self.config.get_deps_command() @@ -102,11 +99,24 @@ class Dependencies: """Installs dependencies on macOS.""" import lib.mac as mac + qt = qt_utils.MacQt(*self.config.get_qt_config()) + qt.install() + + qt_dir = qt.get_install_dir() + qt_bin_dir = os.path.join(qt_dir, "bin") + env_vars_set = 0 + if self.ci_env: + github.set_env_var(cmake_prefix_env_var, qt_dir) + github.add_to_path(qt_bin_dir) + else: + env_vars_set += mac.set_env_var(cmake_prefix_env_var, qt_dir) + env_vars_set += mac.set_env_var(path_env_var, qt_bin_dir) + command = self.config.get_os_deps_value("command") cmd_utils.run(command, shell=True, print_cmd=True) - if not self.ci_env: - mac.set_env_vars(self.config.get_os_value("qt-prefix-command")) + if env_vars_set: + print(f"To load env vars, run: source {mac.shell_rc}") def linux(self): """Installs dependencies on Linux.""" diff --git a/scripts/lib/config.py b/scripts/lib/config.py index a9100264f..0bc29c42c 100644 --- a/scripts/lib/config.py +++ b/scripts/lib/config.py @@ -16,10 +16,10 @@ class ConfigKeyError(RuntimeError): return f"Not found in {self.config_file}: {self.key}" -def _get(dict, key, key_parent=None): +def _get(dict, key, key_parent=None, required=True): value = dict.get(key) - if not value: + if required and not value: key_path = f"{root_key}:{key_parent}:{key}" if key_parent else key raise ConfigKeyError(config_file, key_path) @@ -50,8 +50,8 @@ class Config: parent_key = f"{self.os_name}:{deps_key}" mirror_url = _get(qt, "mirror", parent_key) version = _get(qt, "version", parent_key) - base_dir = _get(qt, "install-dir", parent_key) - modules = _get(qt, "modules", parent_key) + base_dir = _get(qt, "base-dir", parent_key) + modules = _get(qt, "modules", parent_key, required=False) return mirror_url, version, base_dir, modules @@ -70,8 +70,8 @@ class Config: command = _get(deps, "command", f"{self.os_name}:{distro}:{deps_key}") return cmd_utils.strip_continuation_sequences(command) - def get_choco_ci_config(self): - choco_ci_key = "choco-ci" + def get_windows_ci_config(self): + choco_ci_key = "ci" choco_ci = self.get_os_deps_value(choco_ci_key) choco_ci_path = f"{self.os_name}:{deps_key}:{choco_ci_key}" diff --git a/scripts/lib/env.py b/scripts/lib/env.py index a5ff5c855..c037b6ace 100644 --- a/scripts/lib/env.py +++ b/scripts/lib/env.py @@ -37,11 +37,6 @@ def is_linux(): return get_os() == "linux" -def is_running_in_ci(): - """Returns True if running in a CI environment.""" - return os.environ.get("CI") - - def get_linux_distro(): """Detects the Linux distro.""" os_file = "/etc/os-release" diff --git a/scripts/lib/github.py b/scripts/lib/github.py index 070b8b54b..6a3940fd7 100644 --- a/scripts/lib/github.py +++ b/scripts/lib/github.py @@ -1,15 +1,16 @@ import os -github_key = "GITHUB_ENV" +github_env_key = "GITHUB_ENV" +github_path_key = "GITHUB_PATH" -def set_env(key, value): +def set_env_var(key, value): """ Appends the key=value pair to the GitHub environment file. """ - env_file = os.getenv(github_key) + env_file = os.getenv(github_env_key) if not env_file: - raise RuntimeError(f"Env var {github_key} not set") + raise RuntimeError(f"Env var {github_env_key} not set") if not os.path.exists(env_file): raise RuntimeError(f"File not found: {env_file}") @@ -17,3 +18,19 @@ def set_env(key, value): print(f"Setting GitHub env var: {key}={value}") with open(env_file, "a") as env_file: env_file.write(f"{key}={value}\n") + + +def add_to_path(value): + """ + Appends the value to the GitHub path. + """ + path_file = os.getenv(github_path_key) + if not path_file: + raise RuntimeError(f"Env var {github_path_key} not set") + + if not os.path.exists(path_file): + raise RuntimeError(f"File not found: {path_file}") + + print(f"Adding to GitHub path: {value}") + with open(path_file, "a") as path_file: + path_file.write(f"{value}\n") diff --git a/scripts/lib/mac.py b/scripts/lib/mac.py index b766336ff..9c9894c0f 100644 --- a/scripts/lib/mac.py +++ b/scripts/lib/mac.py @@ -4,8 +4,6 @@ import lib.cmd_utils as cmd_utils import lib.env as env from lib.certificate import Certificate -path_env = "PATH" -cmake_env = "CMAKE_PREFIX_PATH" cert_p12_env = "APPLE_P12_CERTIFICATE" notary_user_env = "APPLE_NOTARY_USER" shell_rc = "~/.zshrc" @@ -22,23 +20,23 @@ keychain_path = "/Library/Keychains/System.keychain" def set_env_var(name, value): - text = f'export {name}="${name}:{value}"' + """ + Adds to an environment variable in the shell rc file. + + Returns True if the variable was added, False if it already exists. + """ + text = f'export {name}="{value}:${name}"' file = os.path.expanduser(shell_rc) if os.path.exists(file): with open(file, "r") as f: if text in f.read(): - return + return False print(f"Setting environment variable: {name}={name}") with open(file, "a") as f: f.write(f"\n{text}\n") print(f"Appended to {shell_rc}: {text}") - - -def set_env_vars(cmake_prefix_command): - cmd_sub = f"$({cmake_prefix_command})" - set_env_var(path_env, cmd_sub) - set_env_var(cmake_env, cmd_sub) + return True def package(filename_base): diff --git a/scripts/lib/qt_utils.py b/scripts/lib/qt_utils.py new file mode 100644 index 000000000..4e7694174 --- /dev/null +++ b/scripts/lib/qt_utils.py @@ -0,0 +1,74 @@ +import os +import lib.cmd_utils as cmd_utils +import lib.env as env +import glob + + +class Qt: + def __init__( + self, mirror_url, version, base_dir, modules, os_name, compiler, tool_dir + ): + self.mirror_url = mirror_url + self.version = version + self.base_dir = base_dir + self.modules = modules + self.os_name = os_name + self.compiler = compiler + self.tool_dir = tool_dir + self.dir_pattern = f"{self.base_dir}{os.sep}{self.version}*/{self.tool_dir}" + + def get_install_dir(self): + match = glob.glob(self.dir_pattern) + return os.path.abspath(match[0]) if match else None + + def install(self): + """Install Qt.""" + + if self.get_install_dir(): + print(f"Skipping Qt, already installed at: {self.dir_pattern}") + return + + args = [env.get_python_executable(), "-m", "aqt", "install-qt"] + args.extend(["--outputdir", self.base_dir]) + args.extend(["--base", self.mirror_url]) + args.extend([self.os_name, "desktop", str(self.version), self.compiler]) + + if self.modules: + args.extend(["-m"] + self.modules) + + print(args) + cmd_utils.run( + args, + print_cmd=True, + ) + + if not self.get_install_dir(): + raise RuntimeError( + f"Qt was not installed, path not found: {self.dir_pattern}" + ) + + +class WindowsQt(Qt): + def __init__(self, mirror_url, version, base_dir, modules): + super().__init__( + mirror_url, + version, + base_dir, + modules, + "windows", + "win64_msvc2019_64", + "msvc2019_64", + ) + + +class MacQt(Qt): + def __init__(self, mirror_url, version, base_dir, modules): + super().__init__( + mirror_url, + version, + base_dir, + modules, + "mac", + "clang_64", + "macos", + ) diff --git a/scripts/lib/windows.py b/scripts/lib/windows.py index 8bbf47594..72ef6d4a1 100644 --- a/scripts/lib/windows.py +++ b/scripts/lib/windows.py @@ -7,9 +7,7 @@ from lib.certificate import Certificate msbuild_cmd = "msbuild" signtool_cmd = "signtool" certutil_cmd = "certutil" -cmake_env_var = "CMAKE_PREFIX_PATH" runner_temp_env_var = "RUNNER_TEMP" -qt_base_dir_env_var = "QT_BASE_DIR" dist_dir = "dist" build_dir = "build" wix_solution_file = f"{build_dir}/installer/Synergy.sln" @@ -188,54 +186,3 @@ class WindowsChoco: print(f"Removed package from choco config: {remove}") tree.write(choco_config_file) - - -class WindowsQt: - """Qt for Windows.""" - - def __init__(self, mirror_url, version, base_dir, modules): - self.mirror_url = mirror_url - self.version = version - self.modules = modules - - # allows ci to override the qt base dir path - self.base_dir = os.environ.get(qt_base_dir_env_var) - if not self.base_dir: - print(f"QT_BASE_DIR not set, using: {base_dir}") - self.base_dir = base_dir - - self.install_dir = f"{self.base_dir}\\{self.version}" - - def get_install_dir(self): - if os.path.isdir(self.install_dir): - return self.install_dir - - def install(self): - """Installs Qt on Windows.""" - - cmd_utils.run( - ["pip", "install", "aqtinstall"], - shell=True, - print_cmd=True, - ) - - args = ["python", "-m", "aqt", "install-qt"] - args.extend(["--outputdir", self.base_dir]) - args.extend(["--base", self.mirror_url]) - args.extend( - ["windows", "desktop", self.version, "win64_msvc2019_64", "-m"] - + self.modules - ) - - cmd_utils.run( - args, - shell=True, - print_cmd=True, - ) - - install_dir = self.get_install_dir() - if not install_dir: - raise RuntimeError(f"Qt not installed, path not found: {install_dir}") - - def set_env_vars(self): - set_env_var(cmake_env_var, f"{self.get_install_dir()}\\msvc2019_64") diff --git a/scripts/requirements.txt b/scripts/requirements.txt index 5c483c105..6b5becaae 100644 --- a/scripts/requirements.txt +++ b/scripts/requirements.txt @@ -3,3 +3,4 @@ clang-format python-dotenv pyyaml dmgbuild; sys_platform == 'darwin' +aqtinstall; sys_platform == 'win32' or sys_platform == 'darwin' diff --git a/src/gui/CMakeLists.txt b/src/gui/CMakeLists.txt index c9b7c46a4..ae476dae7 100644 --- a/src/gui/CMakeLists.txt +++ b/src/gui/CMakeLists.txt @@ -1,6 +1,6 @@ find_package( Qt6 - COMPONENTS Core5Compat Core Widgets Network + COMPONENTS Core Widgets Network REQUIRED) set(CMAKE_AUTOMOC ON) @@ -32,8 +32,7 @@ add_executable(synergy WIN32 ${GUI_SOURCE_FILES} ${GUI_UI_FILES} include_directories(./src) target_link_libraries(synergy shared) -target_link_libraries(synergy Qt6::Core Qt6::Widgets Qt6::Network - Qt6::Core5Compat) +target_link_libraries(synergy Qt6::Core Qt6::Widgets Qt6::Network) target_compile_definitions( synergy PRIVATE -DSYNERGY_VERSION_STAGE="${SYNERGY_VERSION_STAGE}") target_compile_definitions(synergy @@ -55,12 +54,6 @@ if(${CMAKE_SYSTEM_NAME} MATCHES "Darwin") install(CODE "MESSAGE (\"Running: ${MACDEPLOYQT_CMD}\")") install(CODE "execute_process(COMMAND ${MACDEPLOYQT_CMD})") - # HACK: Bundle again to fix missing deps. Since Qt 6, it seems that the first - # pass misses out many dependencies from the Framework dir (it also finishes - # quickly with a bunch of warnings), and after the 2nd pass (which takes much - # longer), all of the dependencies are copied to the bundle. - install(CODE "execute_process(COMMAND ${MACDEPLOYQT_CMD})") - elseif(${CMAKE_SYSTEM_NAME} MATCHES "Linux") install(TARGETS synergy DESTINATION bin)