From 3cd7d7ae9c8da53d05d9cc827566abe3d94255b5 Mon Sep 17 00:00:00 2001
From: SineStriker
Date: Sat, 24 Oct 2026 17:37:56 +0800
Subject: [PATCH 1/8] Show the target platform of the build in the about dialog
---
CMakeLists.txt | 5 ++
cmake/DetectTriplet.cmake | 46 +++++++++++++++++++
src/CMakeLists.txt | 1 +
src/plugins/coreplugin/core/CoreInterface.cpp | 6 ++-
4 files changed, 56 insertions(+), 2 deletions(-)
create mode 100644 cmake/DetectTriplet.cmake
diff --git a/CMakeLists.txt b/CMakeLists.txt
index b94172ea..b470bdab 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -39,6 +39,11 @@ else()
set(CMAKE_INSTALL_RPATH_USE_LINK_PATH ON)
endif()
+list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake)
+
+# Sets the cache entry APPLICATION_TRIPLET, the triplet of the build such as x86_64-windows-msvc
+include(DetectTriplet)
+
if(APPLICATION_INSTALL)
include(GNUInstallDirs)
include(CMakePackageConfigHelpers)
diff --git a/cmake/DetectTriplet.cmake b/cmake/DetectTriplet.cmake
new file mode 100644
index 00000000..83dfe2fe
--- /dev/null
+++ b/cmake/DetectTriplet.cmake
@@ -0,0 +1,46 @@
+# SPDX-FileCopyrightText: Team OpenVPI
+# SPDX-License-Identifier: Apache-2.0
+
+# The triplet of the build, --, such as x86_64-windows-msvc, which the
+# about dialog shows as the target platform. The naming follows HelloUtau.
+#
+# The names are those that users know rather than those of vcpkg: x86_64 for the architecture
+# rather than x64, a name of Microsoft, arm64 as Apple names the architecture, and mac for the
+# system rather than osx.
+# The ABI rather than the compiler is named, because a build of clang for the MSVC ABI loads the
+# plugins of a build of MSVC.
+#
+# Sets the cache entry APPLICATION_TRIPLET, which -DAPPLICATION_TRIPLET= overrides.
+
+if(CMAKE_SYSTEM_PROCESSOR MATCHES "^(AMD64|x86_64|x64)$")
+ set(_application_arch x86_64)
+elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "^(ARM64|aarch64|arm64)$")
+ set(_application_arch arm64)
+else()
+ string(TOLOWER "${CMAKE_SYSTEM_PROCESSOR}" _application_arch)
+endif()
+
+if(WIN32)
+ set(_application_system windows)
+elseif(APPLE)
+ set(_application_system mac)
+else()
+ set(_application_system linux)
+endif()
+
+if(MSVC OR CMAKE_CXX_SIMULATE_ID STREQUAL "MSVC")
+ set(_application_abi msvc)
+elseif(MINGW)
+ set(_application_abi mingw)
+elseif(CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
+ set(_application_abi gcc)
+else()
+ string(TOLOWER "${CMAKE_CXX_COMPILER_ID}" _application_abi)
+endif()
+
+set(APPLICATION_TRIPLET "${_application_arch}-${_application_system}-${_application_abi}" CACHE STRING
+ "The triplet of the build, which the about dialog shows")
+
+unset(_application_arch)
+unset(_application_system)
+unset(_application_abi)
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 64d6e8e2..779dbd4c 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -46,6 +46,7 @@ qm_add_definition(APPLICATION_DISPLAY_NAME ${APPLICATION_DISPLAY_NAME} STRING_LI
qm_add_definition(APPLICATION_VERSION ${APPLICATION_VERSION} STRING_LITERAL)
qm_add_definition(APPLICATION_ORG_NAME "OpenVPI" STRING_LITERAL)
qm_add_definition(APPLICATION_SEMVER ${APPLICATION_SEMVER} STRING_LITERAL)
+qm_add_definition(APPLICATION_TRIPLET ${APPLICATION_TRIPLET} STRING_LITERAL)
qm_add_definition(APPLICATION_VENDOR_NAME ${APPLICATION_VENDOR_NAME} STRING_LITERAL)
qm_add_definition(APPLICATION_DEV_START_YEAR ${APPLICATION_DEV_START_YEAR} STRING_LITERAL)
qm_add_definition(APPLICATION_URL ${APPLICATION_URL} STRING_LITERAL)
diff --git a/src/plugins/coreplugin/core/CoreInterface.cpp b/src/plugins/coreplugin/core/CoreInterface.cpp
index 1bc393ae..964f307e 100644
--- a/src/plugins/coreplugin/core/CoreInterface.cpp
+++ b/src/plugins/coreplugin/core/CoreInterface.cpp
@@ -295,14 +295,16 @@ namespace Core {
"Branch: %1 "
"Commit: %2 "
"Build date: %3 "
- "Toolchain: %4 %5 %6"
+ "Toolchain: %4 %5 %6 "
+ "Target platform: %7"
"
")
.arg(QStringLiteral(APPLICATION_GIT_BRANCH), //
QStringLiteral(APPLICATION_GIT_LAST_COMMIT_HASH), //
QLocale().toString(QDateTime::fromString(QStringLiteral(APPLICATION_BUILD_TIME), Qt::ISODate).toLocalTime()), //
QStringLiteral(APPLICATION_COMPILER_ARCH), //
QStringLiteral(APPLICATION_COMPILER_ID), //
- QStringLiteral(APPLICATION_COMPILER_VERSION));
+ QStringLiteral(APPLICATION_COMPILER_VERSION), //
+ QStringLiteral(APPLICATION_TRIPLET));
QQmlComponent component(RuntimeInterface::qmlEngine(), "SVSCraft.UIComponents", "MessageBoxDialog");
if (component.isError()) {
From c0bfe38c0bea42838ff66a5ab7054d68bc334e97 Mon Sep 17 00:00:00 2001
From: SineStriker
Date: Sat, 24 Oct 2026 18:38:22 +0800
Subject: [PATCH 2/8] Check out vcpkg and the overlays into deps with a script
in place of the overlay submodule
---
.github/actions/initialize-vcpkg/action.yml | 36 +-
.github/workflows/build.yml | 2 -
.gitignore | 1 +
.gitmodules | 4 -
AGENTS.md | 8 +-
README.md | 12 +-
scripts/vcpkg | 1 -
scripts/vcpkg-manifest/vcpkg.json | 10 +-
scripts/vcpkg-sources.json | 15 +
scripts/vcpkg-workspace.cmake | 659 ++++++++++++++++++++
10 files changed, 702 insertions(+), 46 deletions(-)
delete mode 160000 scripts/vcpkg
create mode 100644 scripts/vcpkg-sources.json
create mode 100644 scripts/vcpkg-workspace.cmake
diff --git a/.github/actions/initialize-vcpkg/action.yml b/.github/actions/initialize-vcpkg/action.yml
index 5f6f6dd4..8e3b34a5 100644
--- a/.github/actions/initialize-vcpkg/action.yml
+++ b/.github/actions/initialize-vcpkg/action.yml
@@ -2,13 +2,9 @@
# SPDX-License-Identifier: Apache-2.0
name: Initialize Vcpkg
-description: Initialize vcpkg
+description: Prepare the vcpkg workspace in deps/ with scripts/vcpkg-workspace.cmake
inputs:
- ref:
- description: "The ref to checkout"
- required: false
- default: "master"
use_cache:
description: Restore and save vcpkg binary caches
required: false
@@ -25,51 +21,45 @@ runs:
New-Item -ItemType Directory -Path ${{ github.workspace }}/../vcpkg_archives -Force
Write-Output VCPKG_DEFAULT_BINARY_CACHE=$(Resolve-Path ${{ github.workspace }}/../vcpkg_archives) >> $env:GITHUB_ENV
+ # The binary cache of vcpkg is keyed by the ABI of each package, so a cache of other sources
+ # is also of use.
- name: Restore vcpkg binary cache
id: cache_vcpkg
if: inputs.use_cache == 'true'
uses: actions/cache/restore@v4
with:
path: ${{ env.VCPKG_DEFAULT_BINARY_CACHE }}
- key: ${{ runner.os }}-vcpkg-${{ inputs.ref }}-${{ github.sha }}
+ key: ${{ runner.os }}-vcpkg-${{ hashFiles('scripts/vcpkg-sources.json') }}-${{ github.sha }}
restore-keys: |
- ${{ runner.os }}-vcpkg-${{ inputs.ref }}-
+ ${{ runner.os }}-vcpkg-${{ hashFiles('scripts/vcpkg-sources.json') }}-
+ ${{ runner.os }}-vcpkg-
- name: Install vcpkg dependencies
env:
USE_CACHE: ${{ inputs.use_cache }}
QT_DIR: ${{ env.Qt_ROOT_DIR }}/lib/cmake/Qt6
- Qt6_DIR: ${{ env.Qt_ROOT_DIR }}/lib/cmake/Qt6
- VCPKG_KEEP_ENV_VARS: "QT_DIR;Qt6_DIR;VCPKG_BUILD_TYPE;MACOSX_DEPLOYMENT_TARGET"
+ VCPKG_KEEP_ENV_VARS: "VCPKG_BUILD_TYPE;MACOSX_DEPLOYMENT_TARGET"
shell: pwsh
run: |
if ($env:USE_CACHE -eq 'true' -and !(Test-Path $env:VCPKG_DEFAULT_BINARY_CACHE)) {
New-Item $env:VCPKG_DEFAULT_BINARY_CACHE -ItemType directory
}
- Set-Location ${{ github.workspace }}/..
- git clone https://github.com/microsoft/vcpkg.git
- Set-Location vcpkg
- Write-Output VCPKG_ROOT_DIR=$(Resolve-Path .) >> $env:GITHUB_ENV
- git checkout ${{ inputs.ref }}
- ${{ runner.os == 'Windows' && './bootstrap-vcpkg.bat' || 'sh ./bootstrap-vcpkg.sh' }}
- $arguments = @(
- 'install'
- '--x-manifest-root=${{ github.workspace }}/scripts/vcpkg-manifest'
- '--x-install-root=./installed'
- )
+ Set-Location ${{ github.workspace }}
+ $arguments = @('-P', 'scripts/vcpkg-workspace.cmake', 'install')
if ($env:USE_CACHE -ne 'true') {
$env:VCPKG_BINARY_SOURCES = 'clear'
'VCPKG_BINARY_SOURCES=clear' >> $env:GITHUB_ENV
- $arguments += '--binarysource=clear'
+ $arguments += @('--', '--binarysource=clear')
}
- ./vcpkg @arguments
+ cmake @arguments
if ($LASTEXITCODE -ne 0) {
throw 'vcpkg installation failed'
}
+ Write-Output VCPKG_ROOT_DIR=$(Resolve-Path deps/vcpkg) >> $env:GITHUB_ENV
- name: Save vcpkg binary cache
if: inputs.use_cache == 'true'
uses: actions/cache/save@v4
with:
path: ${{ env.VCPKG_DEFAULT_BINARY_CACHE }}
- key: ${{ runner.os }}-vcpkg-${{ inputs.ref }}-${{ github.sha }}
+ key: ${{ runner.os }}-vcpkg-${{ hashFiles('scripts/vcpkg-sources.json') }}-${{ github.sha }}
diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index d396da5b..bfac6280 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -78,7 +78,6 @@ jobs:
runs-on: ${{ matrix.os }}
env:
QT_VERSION: 6.11.2
- VCPKG_REF: 39344dff01c5a5a0134caf2624cdd492f05d30ea
VCPKG_DISABLE_METRICS: '1'
INNOSETUP_REF: is-6_5_4
DSSP_CHANNEL: nightly
@@ -170,7 +169,6 @@ jobs:
- name: Initialize Vcpkg
uses: ./.github/actions/initialize-vcpkg
with:
- ref: ${{ env.VCPKG_REF }}
use_cache: ${{ inputs.build_type != 'nightly' && inputs.use_cache }}
- name: Restore CCache cache
diff --git a/.gitignore b/.gitignore
index d41a831d..8abbd663 100644
--- a/.gitignore
+++ b/.gitignore
@@ -90,6 +90,7 @@ cache
out/
CMakeSettings.json
/vcpkg
+/deps
/data
/*.natvis
diff --git a/.gitmodules b/.gitmodules
index fb15e35e..1f88717f 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -22,10 +22,6 @@
path = src/libs/3rdparty/talcs
url = ../../CrSjimo/talcs.git
-[submodule "vcpkg"]
- path = scripts/vcpkg
- url = ../../stdware/vcpkg-overlay.git
-
[submodule "opendspx"]
path = src/libs/3rdparty/opendspx
url = ../../diffscope/opendspx.git
diff --git a/AGENTS.md b/AGENTS.md
index 862d079d..0a607c91 100644
--- a/AGENTS.md
+++ b/AGENTS.md
@@ -16,7 +16,7 @@ DiffScope is a C++20/Qt 6 singing-voice editor. Keep `src/app` a thin launcher;
- `src/share/`: shared install/deployment rules used after ChorusKit finishes configuring the application.
- `src/libs/3rdparty/`: Git submodules. Treat these as upstream projects; do not mix ordinary feature changes with submodule edits.
- `src/libs/application/uishell/tests/`: standalone visual/smoke applications. `src/tests/` is reserved for application-wide tests.
-- `scripts/vcpkg-manifest/` and `scripts/vcpkg/`: dependency manifest and overlay ports/triplets. `scripts/ci/` is the packaging reference. `dist/` contains installers.
+- `scripts/vcpkg-manifest/`: dependency manifest. `scripts/vcpkg-sources.json` specifies the commits of vcpkg and of the overlays, which `scripts/vcpkg-workspace.cmake` checks out into the ignored directory `deps/`. `scripts/ci/` is the packaging reference. `dist/` contains installers.
Never commit generated files from `build/`, `cmake-build-*`, `out/`, `.qm`, MOC, QML cache, or synchronized include directories.
@@ -215,15 +215,15 @@ View-model bindings maintain explicit document-to-view and view-to-document maps
## Configure, Build, and Run
-Use a recursive checkout, CMake 3.19+, Qt 6.10-compatible development packages, Ninja, and a C++20 compiler. During vcpkg installation, expose the Qt CMake directory through `QT_DIR`/`Qt6_DIR` and retain those variables for overlay ports.
+Use a recursive checkout, CMake 3.19+, Qt 6.10-compatible development packages, Ninja, and a C++20 compiler. `scripts/vcpkg-workspace.cmake` prepares vcpkg, the overlays, and the dependencies in `deps/`, and passes the Qt CMake directory to the ports that build against Qt. `cmake -P scripts/vcpkg-workspace.cmake help` lists its commands, and `--dry-run` prints the commands without running them. The configuration requires `CMAKE_TOOLCHAIN_FILE`, the toolchain of `deps/vcpkg`.
Do not perform any test, build, run, or automatic formatting operations unless explicitly requested by the user. Also do not include any of these operations in the plan unless explicitly requested by the user.
```sh
git submodule update --init --recursive
-/vcpkg install --x-manifest-root=scripts/vcpkg-manifest --x-install-root=/installed --triplet=
+cmake -P scripts/vcpkg-workspace.cmake install --qt-dir /lib/cmake/Qt6 --triplet
cmake -S . -B build -G Ninja -DCMAKE_BUILD_TYPE=Debug \
- -DCMAKE_TOOLCHAIN_FILE=/scripts/buildsystems/vcpkg.cmake \
+ -DCMAKE_TOOLCHAIN_FILE=deps/vcpkg/scripts/buildsystems/vcpkg.cmake \
-DQt6_DIR=/lib/cmake/Qt6 -DAPPLICATION_BUILD_TESTS=ON \
-DTALCS_DSPX=ON -DTALCS_WIDGETS=ON
cmake --build build --parallel
diff --git a/README.md b/README.md
index 2f5b2201..17047b7f 100644
--- a/README.md
+++ b/README.md
@@ -41,13 +41,19 @@ If you are interested in contributing or collaborating, please feel free to reac
git clone --recursive https://github.com/diffscope/diffscope-project.git
```
-3. Install dependencies via Vcpkg.
+3. Install dependencies via Vcpkg. The script checks out vcpkg and the overlays at the commits that `scripts/vcpkg-sources.json` specifies into `deps/`, and installs the dependencies there. It runs on Windows, macOS and Linux.
```bash
- vcpkg install --x-manifest-root=/path/to/diffscope-project/scripts/vcpkg-manifest --x-install-root=/path/to/vcpkg/installed
+ cmake -P scripts/vcpkg-workspace.cmake install --qt-dir /path/to/Qt/lib/cmake/Qt6
```
+
+ `cmake -P scripts/vcpkg-workspace.cmake help` lists the other commands. `--dry-run` prints the commands without running them.
-4. Build using CMake.
+4. Build using CMake with the vcpkg toolchain in `deps/`.
+
+ ```bash
+ cmake -S . -B build -DCMAKE_TOOLCHAIN_FILE=deps/vcpkg/scripts/buildsystems/vcpkg.cmake -DCMAKE_PREFIX_PATH=/path/to/Qt
+ ```
You may refer to CI scripts for more details.
diff --git a/scripts/vcpkg b/scripts/vcpkg
deleted file mode 160000
index 7acffe30..00000000
--- a/scripts/vcpkg
+++ /dev/null
@@ -1 +0,0 @@
-Subproject commit 7acffe30cb3fc5de2196603de20c9d68c5018415
diff --git a/scripts/vcpkg-manifest/vcpkg.json b/scripts/vcpkg-manifest/vcpkg.json
index c2e4d151..5a544caa 100644
--- a/scripts/vcpkg-manifest/vcpkg.json
+++ b/scripts/vcpkg-manifest/vcpkg.json
@@ -44,13 +44,5 @@
"xxhash",
"signalsmith-dsp",
"rubberband"
- ],
- "vcpkg-configuration": {
- "overlay-ports": [
- "../vcpkg/ports"
- ],
- "overlay-triplets": [
- "../vcpkg/triplets"
- ]
- }
+ ]
}
diff --git a/scripts/vcpkg-sources.json b/scripts/vcpkg-sources.json
new file mode 100644
index 00000000..3fcca2fa
--- /dev/null
+++ b/scripts/vcpkg-sources.json
@@ -0,0 +1,15 @@
+{
+ "vcpkg": {
+ "repository": "https://github.com/microsoft/vcpkg.git",
+ "ref": "master"
+ },
+ "overlays": [
+ {
+ "repository": "../../stdware/vcpkg-overlay.git",
+ "ref": "main"
+ }
+ ],
+ "tasks": {
+ "list": ["${VCPKG_EXECUTABLE}", "list", "--x-install-root=${VCPKG_INSTALLED_DIR}"]
+ }
+}
diff --git a/scripts/vcpkg-workspace.cmake b/scripts/vcpkg-workspace.cmake
new file mode 100644
index 00000000..b990d42c
--- /dev/null
+++ b/scripts/vcpkg-workspace.cmake
@@ -0,0 +1,659 @@
+# SPDX-FileCopyrightText: Team OpenVPI
+# SPDX-License-Identifier: Apache-2.0
+
+#[[
+Prepares the vcpkg workspace of DiffScope in deps/: vcpkg and the overlays at the commits that
+scripts/vcpkg-sources.json specifies, and the dependencies of scripts/vcpkg-manifest installed into
+deps/vcpkg/installed.
+
+Usage:
+ cmake -P scripts/vcpkg-workspace.cmake [...] [-- ...]
+
+Commands:
+ fetch Checks out vcpkg and every overlay at the specified commits, and bootstraps
+ vcpkg after a checkout of another commit
+ install Runs fetch, then installs the dependencies with every overlay
+ status Lists the specified and the checked-out commit of every repository
+ run Runs a task of scripts/vcpkg-sources.json
+ help Prints this text
+
+Options:
+ --dry-run Prints the commands that change the workspace without running them
+ --triplet The triplet to install. vcpkg selects its default triplet if absent.
+ --qt-dir The Qt 6 or Qt 5 for the ports that build against Qt, as its CMake
+ directory, /lib/cmake/Qt6 or /lib/cmake/Qt5, or as its prefix
+ . The environment variable QT_DIR, Qt6_DIR or Qt5_DIR applies if
+ absent, in this order.
+
+The arguments after -- are appended to the command of vcpkg install or of the task.
+
+The ref of a repository is a full commit hash, a branch or a tag. A branch and a tag are fetched
+each time, and the checkout follows the commit that they name.
+
+A repository is created with git clone --no-checkout [--branch [] ] ,
+with --branch for a branch or a tag, and updated with git fetch origin [. The
+members "clone-args" and "fetch-args" of vcpkg or of an overlay are lists of arguments for that
+repository, and the members of the same names at the top level are the lists for the others,
+each ["--quiet", "--depth", "1"] if absent. clone-args apply only to a repository that does not
+exist yet. A commit hash that is checked out is not fetched again, so other fetch-args apply at
+the next change of the ref. A shallow checkout keeps its depth unless fetch-args include
+--unshallow or --deepen.
+
+On Windows, the proxy of the Internet settings applies to git and vcpkg unless the environment
+variable HTTP_PROXY or HTTPS_PROXY is set.
+
+A repository that begins with ./ or ../ is relative to the remote origin of this repository, as
+in .gitmodules, so that it is fetched by the same protocol. A checkout whose remote differs, such
+as after a change between HTTPS and SSH, has its remote set to the resolved URL.
+
+An overlay is checked out into deps/]. The member "name" defaults to the last component of
+the repository without .git, and two overlays from repositories of the same name need a name of
+their own.
+
+The overlays precede vcpkg, and an overlay precedes the overlays after it in the list. The
+directories ports and triplets of an overlay are passed to vcpkg if they exist. The members
+"ports" and "triplets" of an overlay name other directories, relative to the overlay.
+
+A task is a list of arguments in "tasks" of scripts/vcpkg-sources.json, run in the root of the
+repository. The variables ${VCPKG_ROOT}, ${VCPKG_EXECUTABLE}, ${VCPKG_INSTALLED_DIR} and
+${VCPKG_MANIFEST_DIR} in an argument are replaced with their values. An argument that is exactly
+${VCPKG_OVERLAY_ARGS} is replaced with the overlay arguments of vcpkg.
+]]
+
+cmake_minimum_required(VERSION 3.19)
+
+get_filename_component(_root_dir "${CMAKE_CURRENT_LIST_DIR}/.." ABSOLUTE)
+set(_sources_file "${_root_dir}/scripts/vcpkg-sources.json")
+set(_manifest_dir "${_root_dir}/scripts/vcpkg-manifest")
+set(_deps_dir "${_root_dir}/deps")
+set(_vcpkg_dir "${_deps_dir}/vcpkg")
+set(_installed_dir "${_vcpkg_dir}/installed")
+
+if(CMAKE_HOST_WIN32)
+ set(_vcpkg_executable "${_vcpkg_dir}/vcpkg.exe")
+else()
+ set(_vcpkg_executable "${_vcpkg_dir}/vcpkg")
+endif()
+
+# ----------------------------------
+# Output and execution
+# ----------------------------------
+function(_vw_print _text)
+ execute_process(COMMAND "${CMAKE_COMMAND}" -E echo "${_text}")
+endfunction()
+
+# Prints the command in ARGN, quoting an argument with a space, and runs it in the root of the
+# repository unless the run is a dry run. Stops if the command fails.
+function(_vw_execute)
+ set(_text)
+ foreach(_arg IN LISTS ARGN)
+ if(_arg STREQUAL "" OR _arg MATCHES " ")
+ set(_arg "\"${_arg}\"")
+ endif()
+ string(APPEND _text " ${_arg}")
+ endforeach()
+ _vw_print(">${_text}")
+
+ if(_dry_run)
+ return()
+ endif()
+
+ execute_process(COMMAND ${ARGN} WORKING_DIRECTORY "${_root_dir}" RESULT_VARIABLE _result)
+ if(NOT _result EQUAL 0)
+ message(FATAL_ERROR "The command failed with ${_result}.")
+ endif()
+endfunction()
+
+# Sets to the output of the git command in ARGN, empty if the command fails. A query runs
+# also in a dry run, because it does not change the workspace.
+function(_vw_git_query _out)
+ execute_process(COMMAND "${_git}" ${ARGN}
+ OUTPUT_VARIABLE _output
+ ERROR_QUIET
+ RESULT_VARIABLE _result
+ OUTPUT_STRIP_TRAILING_WHITESPACE
+ )
+ if(NOT _result EQUAL 0)
+ set(_output)
+ endif()
+ set(${_out} "${_output}" PARENT_SCOPE)
+endfunction()
+
+# ----------------------------------
+# Arguments
+# ----------------------------------
+set(_command)
+set(_task)
+set(_dry_run OFF)
+set(_triplet)
+set(_qt_dir)
+foreach(_variable IN ITEMS QT_DIR Qt6_DIR Qt5_DIR)
+ if(NOT "$ENV{${_variable}}" STREQUAL "")
+ set(_qt_dir "$ENV{${_variable}}")
+ break()
+ endif()
+endforeach()
+set(_extra_args)
+
+set(_index 0)
+while(_index LESS CMAKE_ARGC)
+ if(CMAKE_ARGV${_index} STREQUAL "-P")
+ break()
+ endif()
+ math(EXPR _index "${_index} + 1")
+endwhile()
+math(EXPR _index "${_index} + 2")
+
+while(_index LESS CMAKE_ARGC)
+ set(_arg "${CMAKE_ARGV${_index}}")
+ math(EXPR _index "${_index} + 1")
+
+ if(_arg STREQUAL "--")
+ while(_index LESS CMAKE_ARGC)
+ list(APPEND _extra_args "${CMAKE_ARGV${_index}}")
+ math(EXPR _index "${_index} + 1")
+ endwhile()
+ elseif(_arg STREQUAL "--dry-run")
+ set(_dry_run ON)
+ elseif(_arg STREQUAL "--triplet" OR _arg STREQUAL "--qt-dir")
+ if(NOT _index LESS CMAKE_ARGC)
+ message(FATAL_ERROR "${_arg} requires a value.")
+ endif()
+ if(_arg STREQUAL "--triplet")
+ set(_triplet "${CMAKE_ARGV${_index}}")
+ else()
+ set(_qt_dir "${CMAKE_ARGV${_index}}")
+ endif()
+ math(EXPR _index "${_index} + 1")
+ elseif(_arg MATCHES "^-")
+ message(FATAL_ERROR "Unknown option ${_arg}. See help.")
+ elseif(NOT _command)
+ set(_command "${_arg}")
+ elseif(_command STREQUAL "run" AND NOT _task)
+ set(_task "${_arg}")
+ else()
+ message(FATAL_ERROR "Unexpected argument ${_arg}. See help.")
+ endif()
+endwhile()
+
+if(NOT _command OR _command STREQUAL "help")
+ file(READ "${CMAKE_CURRENT_LIST_FILE}" _self)
+ string(REGEX MATCH "#\\[\\[\n(.*)\n\\]\\]" _ "${_self}")
+ _vw_print("${CMAKE_MATCH_1}")
+ return()
+endif()
+
+if(NOT _command MATCHES "^(fetch|install|status|run)$")
+ message(FATAL_ERROR "Unknown command ${_command}. See help.")
+endif()
+
+if(_command STREQUAL "run" AND NOT _task)
+ message(FATAL_ERROR "run requires the name of a task.")
+endif()
+
+find_program(_git NAMES git)
+if(NOT _git)
+ message(FATAL_ERROR "git is required and was not found.")
+endif()
+
+# ----------------------------------
+# Proxy
+# ----------------------------------
+
+# Sets to the proxy server of the Internet settings of Windows, empty if the proxy is
+# disabled. Taken from qm_get_windows_proxy() of qmsetup. A server given for each protocol, such
+# as http=host:port;https=host:port, yields the server of HTTPS, else that of HTTP.
+function(_vw_get_windows_proxy _out)
+ set(${_out} "" PARENT_SCOPE)
+ set(_key "HKEY_CURRENT_USER\\Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings")
+
+ execute_process(COMMAND reg query "${_key}" /v ProxyEnable
+ OUTPUT_VARIABLE _enable
+ OUTPUT_STRIP_TRAILING_WHITESPACE
+ ERROR_QUIET
+ )
+ if(NOT _enable MATCHES "ProxyEnable[ \t\r\n]+REG_DWORD[ \t\r\n]+0x1")
+ return()
+ endif()
+
+ execute_process(COMMAND reg query "${_key}" /v ProxyServer
+ OUTPUT_VARIABLE _server
+ OUTPUT_STRIP_TRAILING_WHITESPACE
+ ERROR_QUIET
+ )
+ if(NOT _server MATCHES "ProxyServer[ \t\r\n]+REG_SZ[ \t\r\n]+([^\r\n]*)")
+ return()
+ endif()
+ set(_server "${CMAKE_MATCH_1}")
+
+ if(_server MATCHES "=")
+ if(_server MATCHES "(^|;)https=([^;]+)")
+ set(_server "${CMAKE_MATCH_2}")
+ elseif(_server MATCHES "(^|;)http=([^;]+)")
+ set(_server "${CMAKE_MATCH_2}")
+ else()
+ return()
+ endif()
+ endif()
+
+ set(${_out} "${_server}" PARENT_SCOPE)
+endfunction()
+
+if(CMAKE_HOST_WIN32 AND NOT DEFINED ENV{HTTP_PROXY} AND NOT DEFINED ENV{HTTPS_PROXY})
+ _vw_get_windows_proxy(_proxy)
+ if(_proxy)
+ set(ENV{HTTP_PROXY} "http://${_proxy}")
+ set(ENV{HTTPS_PROXY} "http://${_proxy}")
+ set(ENV{ALL_PROXY} "http://${_proxy}")
+ _vw_print("Proxy: http://${_proxy}")
+ endif()
+endif()
+
+# ----------------------------------
+# Sources
+# ----------------------------------
+file(READ "${_sources_file}" _json)
+
+# Sets to the repository . A URL that begins with ./ or ../ is relative to the URL of the
+# remote origin of this repository, as a relative URL of a git submodule, so that the repository
+# is fetched by the protocol of this repository. Each ../ removes the last component of the base.
+function(_vw_resolve_url _url _out)
+ if(NOT _url MATCHES "^\\.\\.?/")
+ set(${_out} "${_url}" PARENT_SCOPE)
+ return()
+ endif()
+
+ _vw_git_query(_base -C "${_root_dir}" remote get-url origin)
+ if(_base STREQUAL "")
+ message(FATAL_ERROR "The repository ${_url} is relative, and this repository has no "
+ "remote origin to resolve it against.")
+ endif()
+ string(REGEX REPLACE "/+$" "" _base "${_base}")
+
+ set(_rest "${_url}")
+ while(TRUE)
+ if(_rest MATCHES "^\\./(.*)$")
+ set(_rest "${CMAKE_MATCH_1}")
+ elseif(_rest MATCHES "^\\.\\./(.*)$")
+ set(_rest "${CMAKE_MATCH_1}")
+ if(NOT _base MATCHES "^(.*[/:])[^/:]+$")
+ message(FATAL_ERROR "The repository ${_url} leaves the remote ${_base}.")
+ endif()
+ set(_base "${CMAKE_MATCH_1}")
+ if(_base MATCHES "^(.*[^/])/+$")
+ set(_base "${CMAKE_MATCH_1}")
+ endif()
+ else()
+ break()
+ endif()
+ endwhile()
+
+ if(_base MATCHES ":$")
+ set(${_out} "${_base}${_rest}" PARENT_SCOPE)
+ else()
+ set(${_out} "${_base}/${_rest}" PARENT_SCOPE)
+ endif()
+endfunction()
+
+# Sets to the strings of the array at the path in ARGN of the sources, and to whether
+# the member exists. A member that is not an array stops the run.
+function(_vw_json_string_array _out _found)
+ string(JSON _type ERROR_VARIABLE _error TYPE "${_json}" ${ARGN})
+ if(_error)
+ set(${_found} OFF PARENT_SCOPE)
+ set(${_out} "" PARENT_SCOPE)
+ return()
+ endif()
+ if(NOT _type STREQUAL "ARRAY")
+ string(JOIN "." _path ${ARGN})
+ message(FATAL_ERROR "${_path} of scripts/vcpkg-sources.json is not an array of strings.")
+ endif()
+ string(JSON _count LENGTH "${_json}" ${ARGN})
+ set(_items)
+ if(_count GREATER 0)
+ math(EXPR _last "${_count} - 1")
+ foreach(_i RANGE ${_last})
+ string(JSON _item GET "${_json}" ${ARGN} ${_i})
+ list(APPEND _items "${_item}")
+ endforeach()
+ endif()
+ set(${_found} ON PARENT_SCOPE)
+ set(${_out} "${_items}" PARENT_SCOPE)
+endfunction()
+
+# The arguments of git clone and git fetch for a repository without clone-args or fetch-args of
+# its own
+foreach(_kind IN ITEMS clone fetch)
+ _vw_json_string_array(_default_${_kind}_args _found ${_kind}-args)
+ if(NOT _found)
+ set(_default_${_kind}_args --quiet --depth 1)
+ endif()
+endforeach()
+
+# Sets the clone and the fetch arguments of the repository of from the member of the
+# sources at the path in ARGN, or from the defaults.
+macro(_vw_read_git_args _number)
+ foreach(_kind IN ITEMS clone fetch)
+ _vw_json_string_array(_repo_${_kind}_args_${_number} _found ${ARGN} ${_kind}-args)
+ if(NOT _found)
+ set(_repo_${_kind}_args_${_number} ${_default_${_kind}_args})
+ endif()
+ endforeach()
+endmacro()
+
+# The repositories by index: 0 is vcpkg, and 1 and later are the overlays in their order.
+string(JSON _repo_url_0 GET "${_json}" vcpkg repository)
+_vw_resolve_url("${_repo_url_0}" _repo_url_0)
+string(JSON _repo_ref_0 GET "${_json}" vcpkg ref)
+set(_repo_name_0 vcpkg)
+set(_repo_dir_0 "${_vcpkg_dir}")
+_vw_read_git_args(0 vcpkg)
+
+string(JSON _overlay_count ERROR_VARIABLE _error LENGTH "${_json}" overlays)
+if(_error)
+ set(_overlay_count 0)
+endif()
+
+set(_repo_last ${_overlay_count})
+set(_overlay_names)
+foreach(_number RANGE 1 ${_overlay_count})
+ if(_overlay_count EQUAL 0)
+ break()
+ endif()
+ math(EXPR _json_index "${_number} - 1")
+ string(JSON _repo_url_${_number} GET "${_json}" overlays ${_json_index} repository)
+ _vw_resolve_url("${_repo_url_${_number}}" _repo_url_${_number})
+
+ # The name defaults to the last component of the repository without .git
+ string(JSON _name ERROR_VARIABLE _error GET "${_json}" overlays ${_json_index} name)
+ if(_error)
+ string(REGEX REPLACE "/+$" "" _name "${_repo_url_${_number}}")
+ string(REGEX REPLACE "^.*[/:]" "" _name "${_name}")
+ string(REGEX REPLACE "\\.git$" "" _name "${_name}")
+ endif()
+ if(NOT _name MATCHES "^[A-Za-z0-9._-]+$" OR _name STREQUAL "vcpkg")
+ message(FATAL_ERROR "The overlay name \"${_name}\" is not a valid directory name or is "
+ "reserved for vcpkg. Give the overlay a name.")
+ endif()
+ if(_name IN_LIST _overlay_names)
+ message(FATAL_ERROR "Two overlays are named \"${_name}\". Give one of them another name.")
+ endif()
+ list(APPEND _overlay_names "${_name}")
+ set(_repo_name_${_number} "${_name}")
+ set(_repo_dir_${_number} "${_deps_dir}/${_name}")
+ string(JSON _repo_ref_${_number} GET "${_json}" overlays ${_json_index} ref)
+ _vw_read_git_args(${_number} overlays ${_json_index})
+
+ foreach(_kind IN ITEMS ports triplets)
+ string(JSON _subdir ERROR_VARIABLE _error GET "${_json}" overlays ${_json_index} ${_kind})
+ if(_error)
+ set(_subdir ${_kind})
+ endif()
+ set(_repo_${_kind}_${_number} "${_repo_dir_${_number}}/${_subdir}")
+ endforeach()
+endforeach()
+
+# Sets to whether [ is a full commit hash, 40 hexadecimal digits. The regular expressions
+# of CMake have no repetition count.
+function(_vw_is_full_hash _ref _out)
+ string(LENGTH "${_ref}" _length)
+ if(_ref MATCHES "^[0-9a-f]+$" AND _length EQUAL 40)
+ set(${_out} ON PARENT_SCOPE)
+ else()
+ set(${_out} OFF PARENT_SCOPE)
+ endif()
+endfunction()
+
+# A remote serves a commit by its full hash only, and an abbreviated hash cannot be fetched
+foreach(_number RANGE 0 ${_repo_last})
+ set(_ref "${_repo_ref_${_number}}")
+ if(_ref STREQUAL "")
+ message(FATAL_ERROR "The ref of ${_repo_name_${_number}} is empty.")
+ endif()
+ _vw_is_full_hash("${_ref}" _full)
+ if(_ref MATCHES "^[0-9a-f]+$" AND NOT _full)
+ message(FATAL_ERROR "The ref ${_ref} of ${_repo_name_${_number}} looks like an "
+ "abbreviated commit hash. Give the full hash of 40 digits, a branch or a tag.")
+ endif()
+endforeach()
+
+# Sets ] to the arguments of vcpkg for the overlays. A directory that does not exist is left
+# out, except in a dry run, which may precede the checkout.
+function(_vw_overlay_args _out)
+ set(_args)
+ foreach(_number RANGE 1 ${_repo_last})
+ if(_repo_last EQUAL 0)
+ break()
+ endif()
+ if(_dry_run OR IS_DIRECTORY "${_repo_ports_${_number}}")
+ list(APPEND _args "--overlay-ports=${_repo_ports_${_number}}")
+ endif()
+ if(_dry_run OR IS_DIRECTORY "${_repo_triplets_${_number}}")
+ list(APPEND _args "--overlay-triplets=${_repo_triplets_${_number}}")
+ endif()
+ endforeach()
+ set(${_out} ${_args} PARENT_SCOPE)
+endfunction()
+
+# ----------------------------------
+# Commands
+# ----------------------------------
+
+# Checks out the repository of at its specified commit. Sets to whether the
+# checked-out commit changed. A repository with changes to tracked files is not checked out.
+function(_vw_checkout _number _changed)
+ set(_name "${_repo_name_${_number}}")
+ set(_dir "${_repo_dir_${_number}}")
+ set(_url "${_repo_url_${_number}}")
+ set(_ref "${_repo_ref_${_number}}")
+
+ _vw_is_full_hash("${_ref}" _full)
+
+ # The clone leaves the checkout to the steps below. git clone takes a branch or a tag but no
+ # commit, so a commit is fetched after the clone of the default branch.
+ if(NOT EXISTS "${_dir}/.git")
+ set(_branch)
+ if(NOT _full)
+ set(_branch --branch "${_ref}")
+ endif()
+ _vw_execute("${_git}" clone ${_repo_clone_args_${_number}} --no-checkout ${_branch}
+ "${_url}" "${_dir}")
+ set(_head)
+ else()
+ _vw_git_query(_current_url -C "${_dir}" remote get-url origin)
+ if(NOT _current_url STREQUAL _url)
+ _vw_execute("${_git}" -C "${_dir}" remote set-url origin "${_url}")
+ endif()
+ _vw_git_query(_head -C "${_dir}" rev-parse HEAD)
+ endif()
+
+ # A commit hash needs no fetch if it is checked out. A branch or a tag is fetched each time,
+ # and the fetched commit is compared.
+ if(_full AND _head STREQUAL _ref)
+ _vw_print("${_name} is at ${_ref}.")
+ set(${_changed} OFF PARENT_SCOPE)
+ return()
+ endif()
+
+ _vw_execute("${_git}" -C "${_dir}" fetch ${_repo_fetch_args_${_number}} origin "${_ref}")
+
+ if(NOT _dry_run)
+ _vw_git_query(_fetched -C "${_dir}" rev-parse FETCH_HEAD)
+ if(_head AND _fetched STREQUAL _head)
+ _vw_print("${_name} is at ${_ref}, ${_head}.")
+ set(${_changed} OFF PARENT_SCOPE)
+ return()
+ endif()
+ endif()
+
+ if(_head)
+ _vw_git_query(_changes -C "${_dir}" status --porcelain --untracked-files=no)
+ if(_changes)
+ message(FATAL_ERROR "${_dir} has changes to tracked files. Commit or discard them "
+ "before ${_name} is checked out at ${_ref}.")
+ endif()
+ endif()
+
+ _vw_execute("${_git}" -C "${_dir}" checkout --quiet --detach FETCH_HEAD)
+ set(${_changed} ON PARENT_SCOPE)
+endfunction()
+
+function(_vw_fetch)
+ file(MAKE_DIRECTORY "${_deps_dir}")
+
+ _vw_checkout(0 _vcpkg_changed)
+ foreach(_number RANGE 1 ${_repo_last})
+ if(_repo_last EQUAL 0)
+ break()
+ endif()
+ _vw_checkout(${_number} _)
+ endforeach()
+
+ # The bootstrap downloads the vcpkg tool of the checked-out commit
+ if(_vcpkg_changed OR NOT EXISTS "${_vcpkg_executable}")
+ if(CMAKE_HOST_WIN32)
+ file(TO_NATIVE_PATH "${_vcpkg_dir}/bootstrap-vcpkg.bat" _bootstrap)
+ _vw_execute(cmd /c "${_bootstrap}" -disableMetrics)
+ else()
+ _vw_execute(sh "${_vcpkg_dir}/bootstrap-vcpkg.sh" -disableMetrics)
+ endif()
+ endif()
+endfunction()
+
+# Sets to the CMake directory of the Qt in , the directory itself or
+# /lib/cmake/Qt, and to 6 or 5 by the configuration file that it holds. A dry
+# run accepts a directory without Qt as the CMake directory of Qt 6.
+function(_vw_resolve_qt_dir _dir _cmake_dir _major)
+ foreach(_candidate_major IN ITEMS 6 5)
+ foreach(_candidate IN ITEMS "${_dir}" "${_dir}/lib/cmake/Qt${_candidate_major}")
+ if(EXISTS "${_candidate}/Qt${_candidate_major}Config.cmake")
+ set(${_cmake_dir} "${_candidate}" PARENT_SCOPE)
+ set(${_major} ${_candidate_major} PARENT_SCOPE)
+ return()
+ endif()
+ endforeach()
+ endforeach()
+
+ if(_dry_run)
+ message(WARNING "${_dir} holds neither Qt6Config.cmake nor Qt5Config.cmake. The dry run "
+ "takes it as the CMake directory of Qt 6.")
+ set(${_cmake_dir} "${_dir}" PARENT_SCOPE)
+ set(${_major} 6 PARENT_SCOPE)
+ return()
+ endif()
+
+ message(FATAL_ERROR "${_dir} is neither the CMake directory of Qt 6 or Qt 5, which holds "
+ "Qt6Config.cmake or Qt5Config.cmake, nor a Qt prefix with lib/cmake/Qt6 or lib/cmake/Qt5.")
+endfunction()
+
+function(_vw_install)
+ set(_args "${_vcpkg_executable}" install
+ "--x-manifest-root=${_manifest_dir}"
+ "--x-install-root=${_installed_dir}"
+ )
+ if(_triplet)
+ list(APPEND _args "--triplet=${_triplet}")
+ endif()
+ _vw_overlay_args(_overlay_args)
+ list(APPEND _args ${_overlay_args} ${_extra_args})
+
+ # The ports that build against Qt find it through QT_DIR and Qt_DIR, which vcpkg passes
+ # to them only if VCPKG_KEEP_ENV_VARS names them
+ if(_qt_dir)
+ set(ENV{QT_DIR} "${_qt_cmake_dir}")
+ set(ENV{Qt${_qt_major}_DIR} "${_qt_cmake_dir}")
+ set(_keep "$ENV{VCPKG_KEEP_ENV_VARS}")
+ list(APPEND _keep QT_DIR Qt${_qt_major}_DIR)
+ list(REMOVE_DUPLICATES _keep)
+ string(JOIN ";" _keep_text ${_keep})
+ set(ENV{VCPKG_KEEP_ENV_VARS} "${_keep_text}")
+ string(CONCAT _text "Environment: QT_DIR=${_qt_cmake_dir} "
+ "Qt${_qt_major}_DIR=${_qt_cmake_dir} VCPKG_KEEP_ENV_VARS=${_keep_text}")
+ _vw_print("${_text}")
+ else()
+ message(WARNING "Neither --qt-dir nor the environment variable QT_DIR, Qt6_DIR or Qt5_DIR "
+ "is set. The ports that build against Qt fail to find it.")
+ endif()
+
+ _vw_execute(${_args})
+endfunction()
+
+function(_vw_status)
+ foreach(_number RANGE 0 ${_repo_last})
+ set(_dir "${_repo_dir_${_number}}")
+ set(_ref "${_repo_ref_${_number}}")
+ if(NOT EXISTS "${_dir}/.git")
+ set(_head "(none)")
+ set(_state "missing")
+ else()
+ _vw_git_query(_head -C "${_dir}" rev-parse HEAD)
+ _vw_git_query(_changes -C "${_dir}" status --porcelain --untracked-files=no)
+
+ # A branch or a tag is resolved to its commit on the remote
+ set(_commit "${_ref}")
+ _vw_is_full_hash("${_ref}" _full)
+ if(NOT _full)
+ _vw_git_query(_remote -C "${_dir}" ls-remote origin "${_ref}")
+ if(_remote MATCHES "^([0-9a-f]+)")
+ set(_commit "${CMAKE_MATCH_1}")
+ string(APPEND _ref " (${_commit})")
+ endif()
+ endif()
+
+ if(_head STREQUAL _commit)
+ set(_state "up to date")
+ else()
+ set(_state "differs")
+ endif()
+ if(_changes)
+ string(APPEND _state ", local changes")
+ endif()
+ endif()
+ string(CONCAT _text "${_repo_name_${_number}}\n specified: ${_ref}\n"
+ " checked out: ${_head}\n state: ${_state}")
+ _vw_print("${_text}")
+ endforeach()
+endfunction()
+
+function(_vw_run)
+ string(JSON _type ERROR_VARIABLE _error TYPE "${_json}" tasks "${_task}")
+ if(_error OR NOT _type STREQUAL "ARRAY")
+ message(FATAL_ERROR "scripts/vcpkg-sources.json has no task \"${_task}\" as a list of "
+ "arguments.")
+ endif()
+
+ string(JSON _count LENGTH "${_json}" tasks "${_task}")
+ _vw_overlay_args(_overlay_args)
+ set(_args)
+ math(EXPR _last "${_count} - 1")
+ foreach(_i RANGE ${_last})
+ string(JSON _arg GET "${_json}" tasks "${_task}" ${_i})
+ if(_arg STREQUAL [=[${VCPKG_OVERLAY_ARGS}]=])
+ list(APPEND _args ${_overlay_args})
+ continue()
+ endif()
+ string(REPLACE [=[${VCPKG_ROOT}]=] "${_vcpkg_dir}" _arg "${_arg}")
+ string(REPLACE [=[${VCPKG_EXECUTABLE}]=] "${_vcpkg_executable}" _arg "${_arg}")
+ string(REPLACE [=[${VCPKG_INSTALLED_DIR}]=] "${_installed_dir}" _arg "${_arg}")
+ string(REPLACE [=[${VCPKG_MANIFEST_DIR}]=] "${_manifest_dir}" _arg "${_arg}")
+ list(APPEND _args "${_arg}")
+ endforeach()
+ list(APPEND _args ${_extra_args})
+
+ _vw_execute(${_args})
+endfunction()
+
+if(_command STREQUAL "fetch")
+ _vw_fetch()
+elseif(_command STREQUAL "install")
+ # Qt is resolved before the fetch, so that a wrong directory stops the run at once
+ if(_qt_dir)
+ _vw_resolve_qt_dir("${_qt_dir}" _qt_cmake_dir _qt_major)
+ endif()
+ _vw_fetch()
+ _vw_install()
+elseif(_command STREQUAL "status")
+ _vw_status()
+elseif(_command STREQUAL "run")
+ _vw_run()
+endif()
From 8e50b4006ab19979cb40daa49cb27347ba5b8913 Mon Sep 17 00:00:00 2001
From: SineStriker
Date: Sat, 24 Oct 2026 19:22:08 +0800
Subject: [PATCH 3/8] Pass the Qt of install-qt-action to vcpkg through
QT_ROOT_DIR, which Qt_ROOT_DIR does not name
---
.github/actions/initialize-vcpkg/action.yml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/.github/actions/initialize-vcpkg/action.yml b/.github/actions/initialize-vcpkg/action.yml
index 8e3b34a5..0fd9bc4e 100644
--- a/.github/actions/initialize-vcpkg/action.yml
+++ b/.github/actions/initialize-vcpkg/action.yml
@@ -37,7 +37,7 @@ runs:
- name: Install vcpkg dependencies
env:
USE_CACHE: ${{ inputs.use_cache }}
- QT_DIR: ${{ env.Qt_ROOT_DIR }}/lib/cmake/Qt6
+ QT_DIR: ${{ env.QT_ROOT_DIR }}/lib/cmake/Qt6
VCPKG_KEEP_ENV_VARS: "VCPKG_BUILD_TYPE;MACOSX_DEPLOYMENT_TARGET"
shell: pwsh
run: |
From 9efdfe8f331e88416e7f43232bb515270e554111 Mon Sep 17 00:00:00 2001
From: SineStriker
Date: Sat, 24 Oct 2026 23:33:03 +0800
Subject: [PATCH 4/8] Merge several files of vcpkg sources, with a local file
and local directories
---
AGENTS.md | 2 +-
scripts/.gitignore | 5 +
scripts/vcpkg-workspace.cmake | 453 ++++++++++++++++++++++++----------
3 files changed, 327 insertions(+), 133 deletions(-)
create mode 100644 scripts/.gitignore
diff --git a/AGENTS.md b/AGENTS.md
index 0a607c91..918da4c3 100644
--- a/AGENTS.md
+++ b/AGENTS.md
@@ -16,7 +16,7 @@ DiffScope is a C++20/Qt 6 singing-voice editor. Keep `src/app` a thin launcher;
- `src/share/`: shared install/deployment rules used after ChorusKit finishes configuring the application.
- `src/libs/3rdparty/`: Git submodules. Treat these as upstream projects; do not mix ordinary feature changes with submodule edits.
- `src/libs/application/uishell/tests/`: standalone visual/smoke applications. `src/tests/` is reserved for application-wide tests.
-- `scripts/vcpkg-manifest/`: dependency manifest. `scripts/vcpkg-sources.json` specifies the commits of vcpkg and of the overlays, which `scripts/vcpkg-workspace.cmake` checks out into the ignored directory `deps/`. `scripts/ci/` is the packaging reference. `dist/` contains installers.
+- `scripts/vcpkg-manifest/`: dependency manifest. `scripts/vcpkg-sources.json` specifies the repositories and refs of vcpkg and of the overlays, which `scripts/vcpkg-workspace.cmake` checks out into the ignored directory `deps/`. The ignored file `scripts/vcpkg-sources.local.json` takes precedence over it, for example to use a local overlay through `"path"`. `scripts/ci/` is the packaging reference. `dist/` contains installers.
Never commit generated files from `build/`, `cmake-build-*`, `out/`, `.qm`, MOC, QML cache, or synchronized include directories.
diff --git a/scripts/.gitignore b/scripts/.gitignore
new file mode 100644
index 00000000..db5b2399
--- /dev/null
+++ b/scripts/.gitignore
@@ -0,0 +1,5 @@
+# SPDX-FileCopyrightText: Team OpenVPI
+# SPDX-License-Identifier: Apache-2.0
+
+# The local sources of scripts/vcpkg-workspace.cmake, which take precedence over vcpkg-sources.json
+/vcpkg-sources.local.json
diff --git a/scripts/vcpkg-workspace.cmake b/scripts/vcpkg-workspace.cmake
index b990d42c..723ee185 100644
--- a/scripts/vcpkg-workspace.cmake
+++ b/scripts/vcpkg-workspace.cmake
@@ -2,9 +2,8 @@
# SPDX-License-Identifier: Apache-2.0
#[[
-Prepares the vcpkg workspace of DiffScope in deps/: vcpkg and the overlays at the commits that
-scripts/vcpkg-sources.json specifies, and the dependencies of scripts/vcpkg-manifest installed into
-deps/vcpkg/installed.
+Prepares the vcpkg workspace of DiffScope in deps/: vcpkg and the overlays as the sources specify
+them, and the dependencies of the manifest installed into /installed.
Usage:
cmake -P scripts/vcpkg-workspace.cmake [...] [-- ...]
@@ -14,66 +13,82 @@ Commands:
vcpkg after a checkout of another commit
install Runs fetch, then installs the dependencies with every overlay
status Lists the specified and the checked-out commit of every repository
- run Runs a task of scripts/vcpkg-sources.json
+ run Runs a task of the sources
help Prints this text
Options:
- --dry-run Prints the commands that change the workspace without running them
- --triplet The triplet to install. vcpkg selects its default triplet if absent.
- --qt-dir The Qt 6 or Qt 5 for the ports that build against Qt, as its CMake
- directory, /lib/cmake/Qt6 or /lib/cmake/Qt5, or as its prefix
- . The environment variable QT_DIR, Qt6_DIR or Qt5_DIR applies if
- absent, in this order.
+ --dry-run Prints the commands that change the workspace without running them
+ --triplet The triplet to install. vcpkg selects its default triplet if absent.
+ --qt-dir The Qt 6 or Qt 5 for the ports that build against Qt, as its CMake
+ directory, /lib/cmake/Qt6 or /lib/cmake/Qt5, or as its
+ prefix . The environment variable QT_DIR, Qt6_DIR or Qt5_DIR
+ applies if absent, in this order.
+ --sources-file A file of sources. The option may be given more than once, and a
+ file given earlier takes precedence. Without the option, the files
+ are scripts/vcpkg-sources.local.json if it exists, then
+ scripts/vcpkg-sources.json.
+ --manifest-dir The directory of vcpkg.json, scripts/vcpkg-manifest if absent
+
+A path on the command line is relative to the current directory.
The arguments after -- are appended to the command of vcpkg install or of the task.
-The ref of a repository is a full commit hash, a branch or a tag. A branch and a tag are fetched
-each time, and the checkout follows the commit that they name.
-
-A repository is created with git clone --no-checkout [--branch [] ] ,
-with --branch for a branch or a tag, and updated with git fetch origin [. The
-members "clone-args" and "fetch-args" of vcpkg or of an overlay are lists of arguments for that
-repository, and the members of the same names at the top level are the lists for the others,
-each ["--quiet", "--depth", "1"] if absent. clone-args apply only to a repository that does not
-exist yet. A commit hash that is checked out is not fetched again, so other fetch-args apply at
-the next change of the ref. A shallow checkout keeps its depth unless fetch-args include
---unshallow or --deepen.
-
-On Windows, the proxy of the Internet settings applies to git and vcpkg unless the environment
-variable HTTP_PROXY or HTTPS_PROXY is set.
-
-A repository that begins with ./ or ../ is relative to the remote origin of this repository, as
-in .gitmodules, so that it is fetched by the same protocol. A checkout whose remote differs, such
-as after a change between HTTPS and SSH, has its remote set to the resolved URL.
-
-An overlay is checked out into deps/]. The member "name" defaults to the last component of
-the repository without .git, and two overlays from repositories of the same name need a name of
-their own.
-
-The overlays precede vcpkg, and an overlay precedes the overlays after it in the list. The
-directories ports and triplets of an overlay are passed to vcpkg if they exist. The members
-"ports" and "triplets" of an overlay name other directories, relative to the overlay.
-
-A task is a list of arguments in "tasks" of scripts/vcpkg-sources.json, run in the root of the
-repository. The variables ${VCPKG_ROOT}, ${VCPKG_EXECUTABLE}, ${VCPKG_INSTALLED_DIR} and
-${VCPKG_MANIFEST_DIR} in an argument are replaced with their values. An argument that is exactly
-${VCPKG_OVERLAY_ARGS} is replaced with the overlay arguments of vcpkg.
+Sources:
+ The files of sources are merged, and a file earlier in the list takes precedence over the
+ files after it. vcpkg and an overlay of the same name take the members that the file of the
+ highest precedence gives. The overlays of the last file keep their order, and an overlay that
+ a file of higher precedence adds precedes the overlays of the files after that file. A task,
+ "clone-args" and "fetch-args" at the top level are taken from the file of the highest
+ precedence that gives them.
+
+ vcpkg and the overlays of the same name in several files are one repository, which is cloned
+ at most once.
+
+ The member "path" of vcpkg or of an overlay names a local directory, relative to the file that
+ gives it, which is used as it is: neither cloned, fetched nor checked out. A local vcpkg is
+ bootstrapped only if it has no vcpkg executable. The file of the highest precedence that gives
+ a path or a repository decides between them, and an entry that gives both uses the path.
+
+Repositories:
+ The ref of a repository is a full commit hash, a branch or a tag. A branch and a tag are
+ fetched each time, and the checkout follows the commit that they name.
+
+ A repository is created with git clone --no-checkout [--branch [] ] ,
+ with --branch for a branch or a tag, and updated with git fetch origin [. The
+ members "clone-args" and "fetch-args" of vcpkg or of an overlay are lists of arguments for
+ that repository, and the members of the same names at the top level are the lists for the
+ others, each ["--quiet", "--depth", "1"] if absent. clone-args apply only to a repository that
+ does not exist yet. A commit hash that is checked out is not fetched again, so other
+ fetch-args apply at the next change of the ref. A shallow checkout keeps its depth unless
+ fetch-args include --unshallow or --deepen.
+
+ A repository that begins with ./ or ../ is relative to the remote origin of this repository,
+ as in .gitmodules, so that it is fetched by the same protocol. A checkout whose remote
+ differs, such as after a change between HTTPS and SSH, has its remote set to the resolved URL.
+
+ On Windows, the proxy of the Internet settings applies to git and vcpkg unless the environment
+ variable HTTP_PROXY or HTTPS_PROXY is set.
+
+Overlays:
+ An overlay is checked out into deps/]. The member "name" defaults to the last component
+ of the repository without .git, or of the path, and two overlays of the same last component
+ need a name of their own.
+
+ The overlays precede vcpkg, and an overlay precedes the overlays after it. The directories
+ ports and triplets of an overlay are passed to vcpkg if they exist. The members "ports" and
+ "triplets" of an overlay name other directories, relative to the overlay.
+
+Tasks:
+ A task is a list of arguments in "tasks", run in the root of the repository. The variables
+ ${VCPKG_ROOT}, ${VCPKG_EXECUTABLE}, ${VCPKG_INSTALLED_DIR} and ${VCPKG_MANIFEST_DIR} in an
+ argument are replaced with their values. An argument that is exactly ${VCPKG_OVERLAY_ARGS} is
+ replaced with the overlay arguments of vcpkg.
]]
cmake_minimum_required(VERSION 3.19)
get_filename_component(_root_dir "${CMAKE_CURRENT_LIST_DIR}/.." ABSOLUTE)
-set(_sources_file "${_root_dir}/scripts/vcpkg-sources.json")
-set(_manifest_dir "${_root_dir}/scripts/vcpkg-manifest")
set(_deps_dir "${_root_dir}/deps")
-set(_vcpkg_dir "${_deps_dir}/vcpkg")
-set(_installed_dir "${_vcpkg_dir}/installed")
-
-if(CMAKE_HOST_WIN32)
- set(_vcpkg_executable "${_vcpkg_dir}/vcpkg.exe")
-else()
- set(_vcpkg_executable "${_vcpkg_dir}/vcpkg")
-endif()
# ----------------------------------
# Output and execution
@@ -133,6 +148,8 @@ foreach(_variable IN ITEMS QT_DIR Qt6_DIR Qt5_DIR)
break()
endif()
endforeach()
+set(_source_files)
+set(_manifest_dir "${_root_dir}/scripts/vcpkg-manifest")
set(_extra_args)
set(_index 0)
@@ -155,16 +172,29 @@ while(_index LESS CMAKE_ARGC)
endwhile()
elseif(_arg STREQUAL "--dry-run")
set(_dry_run ON)
- elseif(_arg STREQUAL "--triplet" OR _arg STREQUAL "--qt-dir")
+ elseif(_arg MATCHES "^--(triplet|qt-dir|sources-file|manifest-dir)$")
if(NOT _index LESS CMAKE_ARGC)
message(FATAL_ERROR "${_arg} requires a value.")
endif()
+ set(_value "${CMAKE_ARGV${_index}}")
+ math(EXPR _index "${_index} + 1")
+
+ # A path on the command line is relative to the current directory, which is the source
+ # directory of a script
+ if(_arg MATCHES "^--(sources-file|manifest-dir)$")
+ get_filename_component(_value "${_value}" ABSOLUTE
+ BASE_DIR "${CMAKE_CURRENT_SOURCE_DIR}")
+ endif()
+
if(_arg STREQUAL "--triplet")
- set(_triplet "${CMAKE_ARGV${_index}}")
+ set(_triplet "${_value}")
+ elseif(_arg STREQUAL "--qt-dir")
+ set(_qt_dir "${_value}")
+ elseif(_arg STREQUAL "--sources-file")
+ list(APPEND _source_files "${_value}")
else()
- set(_qt_dir "${CMAKE_ARGV${_index}}")
+ set(_manifest_dir "${_value}")
endif()
- math(EXPR _index "${_index} + 1")
elseif(_arg MATCHES "^-")
message(FATAL_ERROR "Unknown option ${_arg}. See help.")
elseif(NOT _command)
@@ -252,7 +282,6 @@ endif()
# ----------------------------------
# Sources
# ----------------------------------
-file(READ "${_sources_file}" _json)
# Sets to the repository . A URL that begins with ./ or ../ is relative to the URL of the
# remote origin of this repository, as a relative URL of a git submodule, so that the repository
@@ -295,10 +324,23 @@ function(_vw_resolve_url _url _out)
endif()
endfunction()
-# Sets to the strings of the array at the path in ARGN of the sources, and to whether
-# the member exists. A member that is not an array stops the run.
-function(_vw_json_string_array _out _found)
- string(JSON _type ERROR_VARIABLE _error TYPE "${_json}" ${ARGN})
+# Sets to the string at the path in ARGN of the JSON in the variable , and to
+# whether the member exists.
+function(_vw_json_string _json _out _found)
+ string(JSON _value ERROR_VARIABLE _error GET "${${_json}}" ${ARGN})
+ if(_error)
+ set(${_found} OFF PARENT_SCOPE)
+ set(${_out} "" PARENT_SCOPE)
+ else()
+ set(${_found} ON PARENT_SCOPE)
+ set(${_out} "${_value}" PARENT_SCOPE)
+ endif()
+endfunction()
+
+# Sets to the strings of the array at the path in ARGN of the JSON in the variable , and
+# to whether the member exists. A member that is not an array stops the run.
+function(_vw_json_string_array _json _out _found)
+ string(JSON _type ERROR_VARIABLE _error TYPE "${${_json}}" ${ARGN})
if(_error)
set(${_found} OFF PARENT_SCOPE)
set(${_out} "" PARENT_SCOPE)
@@ -306,14 +348,14 @@ function(_vw_json_string_array _out _found)
endif()
if(NOT _type STREQUAL "ARRAY")
string(JOIN "." _path ${ARGN})
- message(FATAL_ERROR "${_path} of scripts/vcpkg-sources.json is not an array of strings.")
+ message(FATAL_ERROR "${_path} of ${_source_file} is not an array of strings.")
endif()
- string(JSON _count LENGTH "${_json}" ${ARGN})
+ string(JSON _count LENGTH "${${_json}}" ${ARGN})
set(_items)
if(_count GREATER 0)
math(EXPR _last "${_count} - 1")
foreach(_i RANGE ${_last})
- string(JSON _item GET "${_json}" ${ARGN} ${_i})
+ string(JSON _item GET "${${_json}}" ${ARGN} ${_i})
list(APPEND _items "${_item}")
endforeach()
endif()
@@ -321,78 +363,193 @@ function(_vw_json_string_array _out _found)
set(${_out} "${_items}" PARENT_SCOPE)
endfunction()
-# The arguments of git clone and git fetch for a repository without clone-args or fetch-args of
-# its own
-foreach(_kind IN ITEMS clone fetch)
- _vw_json_string_array(_default_${_kind}_args _found ${_kind}-args)
- if(NOT _found)
- set(_default_${_kind}_args --quiet --depth 1)
- endif()
-endforeach()
+# Sets to the last component of , a URL or a path, without .git
+function(_vw_default_name _location _out)
+ string(REGEX REPLACE "[/\\\\]+$" "" _name "${_location}")
+ string(REGEX REPLACE "^.*[/\\\\:]" "" _name "${_name}")
+ string(REGEX REPLACE "\\.git$" "" _name "${_name}")
+ set(${_out} "${_name}" PARENT_SCOPE)
+endfunction()
-# Sets the clone and the fetch arguments of the repository of from the member of the
-# sources at the path in ARGN, or from the defaults.
-macro(_vw_read_git_args _number)
+# Copies the members of the entry at the path in ARGN of the current source into the variables
+# _entry__, and records the members of arrays in _entry__has_. A path is
+# made absolute against the directory of the source, and a repository is resolved.
+macro(_vw_merge_entry _key)
+ # A repository of a file of higher precedence replaces a path of the files merged before it,
+ # unless the same entry also gives a path
+ _vw_json_string(_source_json _value _found ${ARGN} repository)
+ if(_found)
+ _vw_resolve_url("${_value}" _entry_${_key}_url)
+ set(_entry_${_key}_path)
+ endif()
+ _vw_json_string(_source_json _value _found ${ARGN} ref)
+ if(_found)
+ set(_entry_${_key}_ref "${_value}")
+ endif()
+ _vw_json_string(_source_json _value _found ${ARGN} path)
+ if(_found)
+ get_filename_component(_entry_${_key}_path "${_value}" ABSOLUTE BASE_DIR "${_source_dir}")
+ endif()
+ foreach(_member IN ITEMS ports triplets)
+ _vw_json_string(_source_json _value _found ${ARGN} ${_member})
+ if(_found)
+ set(_entry_${_key}_${_member} "${_value}")
+ endif()
+ endforeach()
foreach(_kind IN ITEMS clone fetch)
- _vw_json_string_array(_repo_${_kind}_args_${_number} _found ${ARGN} ${_kind}-args)
- if(NOT _found)
- set(_repo_${_kind}_args_${_number} ${_default_${_kind}_args})
+ _vw_json_string_array(_source_json _value _found ${ARGN} ${_kind}-args)
+ if(_found)
+ set(_entry_${_key}_${_kind}_args "${_value}")
+ set(_entry_${_key}_has_${_kind}_args ON)
endif()
endforeach()
endmacro()
-# The repositories by index: 0 is vcpkg, and 1 and later are the overlays in their order.
-string(JSON _repo_url_0 GET "${_json}" vcpkg repository)
-_vw_resolve_url("${_repo_url_0}" _repo_url_0)
-string(JSON _repo_ref_0 GET "${_json}" vcpkg ref)
-set(_repo_name_0 vcpkg)
-set(_repo_dir_0 "${_vcpkg_dir}")
-_vw_read_git_args(0 vcpkg)
+# The files of sources in their precedence, the local file before the sources by default
+if(NOT _source_files)
+ if(EXISTS "${_root_dir}/scripts/vcpkg-sources.local.json")
+ list(APPEND _source_files "${_root_dir}/scripts/vcpkg-sources.local.json")
+ endif()
+ list(APPEND _source_files "${_root_dir}/scripts/vcpkg-sources.json")
+endif()
-string(JSON _overlay_count ERROR_VARIABLE _error LENGTH "${_json}" overlays)
-if(_error)
- set(_overlay_count 0)
+list(LENGTH _source_files _length)
+if(_length GREATER 1)
+ foreach(_source_file IN LISTS _source_files)
+ _vw_print("Sources: ${_source_file}")
+ endforeach()
endif()
-set(_repo_last ${_overlay_count})
+set(_default_clone_args --quiet --depth 1)
+set(_default_fetch_args --quiet --depth 1)
set(_overlay_names)
-foreach(_number RANGE 1 ${_overlay_count})
- if(_overlay_count EQUAL 0)
- break()
+set(_source_count 0)
+
+# The files are merged from the lowest precedence, so that a file of higher precedence replaces
+# the members of the files merged before it
+set(_merge_order ${_source_files})
+list(REVERSE _merge_order)
+
+foreach(_source_file IN LISTS _merge_order)
+ if(NOT EXISTS "${_source_file}")
+ message(FATAL_ERROR "The sources ${_source_file} do not exist.")
endif()
- math(EXPR _json_index "${_number} - 1")
- string(JSON _repo_url_${_number} GET "${_json}" overlays ${_json_index} repository)
- _vw_resolve_url("${_repo_url_${_number}}" _repo_url_${_number})
+ math(EXPR _source_count "${_source_count} + 1")
+ file(READ "${_source_file}" _source_json)
+ set(_source_json_${_source_count} "${_source_json}")
+ get_filename_component(_source_dir "${_source_file}" DIRECTORY)
- # The name defaults to the last component of the repository without .git
- string(JSON _name ERROR_VARIABLE _error GET "${_json}" overlays ${_json_index} name)
+ foreach(_kind IN ITEMS clone fetch)
+ _vw_json_string_array(_source_json _value _found ${_kind}-args)
+ if(_found)
+ set(_default_${_kind}_args "${_value}")
+ endif()
+ endforeach()
+
+ string(JSON _type ERROR_VARIABLE _error TYPE "${_source_json}" vcpkg)
+ if(NOT _error)
+ _vw_merge_entry(vcpkg vcpkg)
+ endif()
+
+ string(JSON _count ERROR_VARIABLE _error LENGTH "${_source_json}" overlays)
if(_error)
- string(REGEX REPLACE "/+$" "" _name "${_repo_url_${_number}}")
- string(REGEX REPLACE "^.*[/:]" "" _name "${_name}")
- string(REGEX REPLACE "\\.git$" "" _name "${_name}")
- endif()
- if(NOT _name MATCHES "^[A-Za-z0-9._-]+$" OR _name STREQUAL "vcpkg")
- message(FATAL_ERROR "The overlay name \"${_name}\" is not a valid directory name or is "
- "reserved for vcpkg. Give the overlay a name.")
- endif()
- if(_name IN_LIST _overlay_names)
- message(FATAL_ERROR "Two overlays are named \"${_name}\". Give one of them another name.")
- endif()
- list(APPEND _overlay_names "${_name}")
- set(_repo_name_${_number} "${_name}")
- set(_repo_dir_${_number} "${_deps_dir}/${_name}")
- string(JSON _repo_ref_${_number} GET "${_json}" overlays ${_json_index} ref)
- _vw_read_git_args(${_number} overlays ${_json_index})
-
- foreach(_kind IN ITEMS ports triplets)
- string(JSON _subdir ERROR_VARIABLE _error GET "${_json}" overlays ${_json_index} ${_kind})
- if(_error)
- set(_subdir ${_kind})
+ set(_count 0)
+ endif()
+ set(_new_names)
+ set(_file_names)
+ foreach(_json_index RANGE ${_count})
+ if(_json_index EQUAL _count)
+ break()
+ endif()
+
+ _vw_json_string(_source_json _name _found overlays ${_json_index} name)
+ if(NOT _found)
+ _vw_json_string(_source_json _location _found overlays ${_json_index} repository)
+ if(NOT _found)
+ _vw_json_string(_source_json _location _found overlays ${_json_index} path)
+ endif()
+ if(NOT _found)
+ message(FATAL_ERROR "Overlay ${_json_index} of ${_source_file} has neither a "
+ "repository nor a path.")
+ endif()
+ _vw_default_name("${_location}" _name)
+ endif()
+ if(NOT _name MATCHES "^[A-Za-z0-9._-]+$" OR _name STREQUAL "vcpkg")
+ message(FATAL_ERROR "The overlay name \"${_name}\" in ${_source_file} is not a valid "
+ "directory name or is reserved for vcpkg. Give the overlay a name.")
+ endif()
+ if(_name IN_LIST _file_names)
+ message(FATAL_ERROR "Two overlays of ${_source_file} are named \"${_name}\". Give one "
+ "of them another name.")
+ endif()
+ list(APPEND _file_names "${_name}")
+ if(NOT _name IN_LIST _overlay_names)
+ list(APPEND _new_names "${_name}")
+ endif()
+
+ _vw_merge_entry(${_name} overlays ${_json_index})
+ endforeach()
+
+ # The overlays of the file of the lowest precedence keep their order, and the new overlays of
+ # a file of higher precedence precede the overlays merged before them
+ if(_source_count EQUAL 1)
+ list(APPEND _overlay_names ${_new_names})
+ else()
+ list(PREPEND _overlay_names ${_new_names})
+ endif()
+endforeach()
+
+# The repositories by index: 0 is vcpkg, and 1 and later are the overlays in their order.
+set(_repo_name_0 vcpkg)
+set(_repo_keys vcpkg ${_overlay_names})
+list(LENGTH _overlay_names _repo_last)
+
+set(_number 0)
+foreach(_key IN LISTS _repo_keys)
+ set(_repo_url_${_number} "${_entry_${_key}_url}")
+ set(_repo_ref_${_number} "${_entry_${_key}_ref}")
+ if(_number GREATER 0)
+ set(_repo_name_${_number} "${_key}")
+ endif()
+
+ if(NOT "${_entry_${_key}_path}" STREQUAL "")
+ set(_repo_local_${_number} ON)
+ set(_repo_dir_${_number} "${_entry_${_key}_path}")
+ else()
+ set(_repo_local_${_number} OFF)
+ set(_repo_dir_${_number} "${_deps_dir}/${_key}")
+ if(_repo_url_${_number} STREQUAL "" OR _repo_ref_${_number} STREQUAL "")
+ message(FATAL_ERROR "${_key} needs a repository and a ref, or a path.")
+ endif()
+ endif()
+
+ foreach(_kind IN ITEMS clone fetch)
+ if(_entry_${_key}_has_${_kind}_args)
+ set(_repo_${_kind}_args_${_number} ${_entry_${_key}_${_kind}_args})
+ else()
+ set(_repo_${_kind}_args_${_number} ${_default_${_kind}_args})
endif()
- set(_repo_${_kind}_${_number} "${_repo_dir_${_number}}/${_subdir}")
endforeach()
+
+ foreach(_member IN ITEMS ports triplets)
+ set(_subdir "${_entry_${_key}_${_member}}")
+ if(_subdir STREQUAL "")
+ set(_subdir ${_member})
+ endif()
+ set(_repo_${_member}_${_number} "${_repo_dir_${_number}}/${_subdir}")
+ endforeach()
+
+ math(EXPR _number "${_number} + 1")
endforeach()
+set(_vcpkg_dir "${_repo_dir_0}")
+set(_installed_dir "${_vcpkg_dir}/installed")
+if(CMAKE_HOST_WIN32)
+ set(_vcpkg_executable "${_vcpkg_dir}/vcpkg.exe")
+else()
+ set(_vcpkg_executable "${_vcpkg_dir}/vcpkg")
+endif()
+
# Sets to whether [ is a full commit hash, 40 hexadecimal digits. The regular expressions
# of CMake have no repetition count.
function(_vw_is_full_hash _ref _out)
@@ -406,10 +563,10 @@ endfunction()
# A remote serves a commit by its full hash only, and an abbreviated hash cannot be fetched
foreach(_number RANGE 0 ${_repo_last})
- set(_ref "${_repo_ref_${_number}}")
- if(_ref STREQUAL "")
- message(FATAL_ERROR "The ref of ${_repo_name_${_number}} is empty.")
+ if(_repo_local_${_number})
+ continue()
endif()
+ set(_ref "${_repo_ref_${_number}}")
_vw_is_full_hash("${_ref}" _full)
if(_ref MATCHES "^[0-9a-f]+$" AND NOT _full)
message(FATAL_ERROR "The ref ${_ref} of ${_repo_name_${_number}} looks like an "
@@ -439,13 +596,23 @@ endfunction()
# Commands
# ----------------------------------
-# Checks out the repository of ] at its specified commit. Sets to whether the
-# checked-out commit changed. A repository with changes to tracked files is not checked out.
+# Checks out the repository of at its specified commit. Sets to whether the
+# checked-out commit changed. A repository with changes to tracked files is not checked out. A
+# local directory is used as it is.
function(_vw_checkout _number _changed)
set(_name "${_repo_name_${_number}}")
set(_dir "${_repo_dir_${_number}}")
set(_url "${_repo_url_${_number}}")
set(_ref "${_repo_ref_${_number}}")
+ set(${_changed} OFF PARENT_SCOPE)
+
+ if(_repo_local_${_number})
+ if(NOT IS_DIRECTORY "${_dir}")
+ message(FATAL_ERROR "The local directory ${_dir} of ${_name} does not exist.")
+ endif()
+ _vw_print("${_name} uses the local directory ${_dir}.")
+ return()
+ endif()
_vw_is_full_hash("${_ref}" _full)
@@ -471,7 +638,6 @@ function(_vw_checkout _number _changed)
# and the fetched commit is compared.
if(_full AND _head STREQUAL _ref)
_vw_print("${_name} is at ${_ref}.")
- set(${_changed} OFF PARENT_SCOPE)
return()
endif()
@@ -481,7 +647,6 @@ function(_vw_checkout _number _changed)
_vw_git_query(_fetched -C "${_dir}" rev-parse FETCH_HEAD)
if(_head AND _fetched STREQUAL _head)
_vw_print("${_name} is at ${_ref}, ${_head}.")
- set(${_changed} OFF PARENT_SCOPE)
return()
endif()
endif()
@@ -582,7 +747,16 @@ function(_vw_status)
foreach(_number RANGE 0 ${_repo_last})
set(_dir "${_repo_dir_${_number}}")
set(_ref "${_repo_ref_${_number}}")
- if(NOT EXISTS "${_dir}/.git")
+ if(_repo_local_${_number})
+ set(_ref "local: ${_dir}")
+ if(IS_DIRECTORY "${_dir}")
+ _vw_git_query(_head -C "${_dir}" rev-parse HEAD)
+ set(_state "local")
+ else()
+ set(_head "(none)")
+ set(_state "missing")
+ endif()
+ elseif(NOT EXISTS "${_dir}/.git")
set(_head "(none)")
set(_state "missing")
else()
@@ -609,6 +783,9 @@ function(_vw_status)
string(APPEND _state ", local changes")
endif()
endif()
+ if(_head STREQUAL "")
+ set(_head "(not a git repository)")
+ endif()
string(CONCAT _text "${_repo_name_${_number}}\n specified: ${_ref}\n"
" checked out: ${_head}\n state: ${_state}")
_vw_print("${_text}")
@@ -616,18 +793,30 @@ function(_vw_status)
endfunction()
function(_vw_run)
- string(JSON _type ERROR_VARIABLE _error TYPE "${_json}" tasks "${_task}")
- if(_error OR NOT _type STREQUAL "ARRAY")
- message(FATAL_ERROR "scripts/vcpkg-sources.json has no task \"${_task}\" as a list of "
- "arguments.")
+ # The file of the highest precedence that gives the task, which is merged last
+ set(_found_json)
+ set(_number ${_source_count})
+ while(_number GREATER 0)
+ string(JSON _type ERROR_VARIABLE _error TYPE "${_source_json_${_number}}" tasks "${_task}")
+ if(NOT _error)
+ if(NOT _type STREQUAL "ARRAY")
+ message(FATAL_ERROR "The task \"${_task}\" is not a list of arguments.")
+ endif()
+ set(_found_json "${_source_json_${_number}}")
+ break()
+ endif()
+ math(EXPR _number "${_number} - 1")
+ endwhile()
+ if(_found_json STREQUAL "")
+ message(FATAL_ERROR "No sources give the task \"${_task}\".")
endif()
- string(JSON _count LENGTH "${_json}" tasks "${_task}")
+ string(JSON _count LENGTH "${_found_json}" tasks "${_task}")
_vw_overlay_args(_overlay_args)
set(_args)
math(EXPR _last "${_count} - 1")
foreach(_i RANGE ${_last})
- string(JSON _arg GET "${_json}" tasks "${_task}" ${_i})
+ string(JSON _arg GET "${_found_json}" tasks "${_task}" ${_i})
if(_arg STREQUAL [=[${VCPKG_OVERLAY_ARGS}]=])
list(APPEND _args ${_overlay_args})
continue()
From 0b61608b8a9243484cebddb42076fdc206539494 Mon Sep 17 00:00:00 2001
From: SineStriker
Date: Sun, 25 Oct 2026 00:10:17 +0800
Subject: [PATCH 5/8] Download vcpkg-workspace.cmake from dev-scripts in place
of a copy in the repository
---
.github/actions/initialize-vcpkg/action.yml | 4 +
.github/workflows/build.yml | 1 +
AGENTS.md | 3 +-
README.md | 11 +-
scripts/.gitignore | 4 +-
scripts/vcpkg-sources.json | 9 +-
scripts/vcpkg-workspace.cmake | 848 --------------------
7 files changed, 26 insertions(+), 854 deletions(-)
delete mode 100644 scripts/vcpkg-workspace.cmake
diff --git a/.github/actions/initialize-vcpkg/action.yml b/.github/actions/initialize-vcpkg/action.yml
index 0fd9bc4e..511f2c51 100644
--- a/.github/actions/initialize-vcpkg/action.yml
+++ b/.github/actions/initialize-vcpkg/action.yml
@@ -39,12 +39,16 @@ runs:
USE_CACHE: ${{ inputs.use_cache }}
QT_DIR: ${{ env.QT_ROOT_DIR }}/lib/cmake/Qt6
VCPKG_KEEP_ENV_VARS: "VCPKG_BUILD_TYPE;MACOSX_DEPLOYMENT_TARGET"
+ DEV_SCRIPTS_REF: ${{ env.DEV_SCRIPTS_REF || 'main' }}
shell: pwsh
run: |
if ($env:USE_CACHE -eq 'true' -and !(Test-Path $env:VCPKG_DEFAULT_BINARY_CACHE)) {
New-Item $env:VCPKG_DEFAULT_BINARY_CACHE -ItemType directory
}
Set-Location ${{ github.workspace }}
+ # vcpkg-workspace.cmake is not part of the repository, see scripts/.gitignore
+ Invoke-WebRequest -OutFile scripts/vcpkg-workspace.cmake `
+ "https://raw.githubusercontent.com/stdware/dev-scripts/$env:DEV_SCRIPTS_REF/scripts/vcpkg-workspace.cmake"
$arguments = @('-P', 'scripts/vcpkg-workspace.cmake', 'install')
if ($env:USE_CACHE -ne 'true') {
$env:VCPKG_BINARY_SOURCES = 'clear'
diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index bfac6280..6f2e83d3 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -78,6 +78,7 @@ jobs:
runs-on: ${{ matrix.os }}
env:
QT_VERSION: 6.11.2
+ DEV_SCRIPTS_REF: main
VCPKG_DISABLE_METRICS: '1'
INNOSETUP_REF: is-6_5_4
DSSP_CHANNEL: nightly
diff --git a/AGENTS.md b/AGENTS.md
index 918da4c3..c35e37cf 100644
--- a/AGENTS.md
+++ b/AGENTS.md
@@ -215,12 +215,13 @@ View-model bindings maintain explicit document-to-view and view-to-document maps
## Configure, Build, and Run
-Use a recursive checkout, CMake 3.19+, Qt 6.10-compatible development packages, Ninja, and a C++20 compiler. `scripts/vcpkg-workspace.cmake` prepares vcpkg, the overlays, and the dependencies in `deps/`, and passes the Qt CMake directory to the ports that build against Qt. `cmake -P scripts/vcpkg-workspace.cmake help` lists its commands, and `--dry-run` prints the commands without running them. The configuration requires `CMAKE_TOOLCHAIN_FILE`, the toolchain of `deps/vcpkg`.
+Use a recursive checkout, CMake 3.19+, Qt 6.10-compatible development packages, Ninja, and a C++20 compiler. `scripts/vcpkg-workspace.cmake`, which is downloaded from https://github.com/stdware/dev-scripts and ignored by git, prepares vcpkg, the overlays, and the dependencies in `deps/`, and passes the Qt CMake directory to the ports that build against Qt. `cmake -P scripts/vcpkg-workspace.cmake help` lists its commands, and `--dry-run` prints the commands without running them. The configuration requires `CMAKE_TOOLCHAIN_FILE`, the toolchain of `deps/vcpkg`.
Do not perform any test, build, run, or automatic formatting operations unless explicitly requested by the user. Also do not include any of these operations in the plan unless explicitly requested by the user.
```sh
git submodule update --init --recursive
+curl -fsSL -o scripts/vcpkg-workspace.cmake https://raw.githubusercontent.com/stdware/dev-scripts/main/scripts/vcpkg-workspace.cmake
cmake -P scripts/vcpkg-workspace.cmake install --qt-dir /lib/cmake/Qt6 --triplet
cmake -S . -B build -G Ninja -DCMAKE_BUILD_TYPE=Debug \
-DCMAKE_TOOLCHAIN_FILE=deps/vcpkg/scripts/buildsystems/vcpkg.cmake \
diff --git a/README.md b/README.md
index 17047b7f..79aaae82 100644
--- a/README.md
+++ b/README.md
@@ -41,13 +41,20 @@ If you are interested in contributing or collaborating, please feel free to reac
git clone --recursive https://github.com/diffscope/diffscope-project.git
```
-3. Install dependencies via Vcpkg. The script checks out vcpkg and the overlays at the commits that `scripts/vcpkg-sources.json` specifies into `deps/`, and installs the dependencies there. It runs on Windows, macOS and Linux.
+3. Install dependencies via Vcpkg with [vcpkg-workspace.cmake](https://github.com/stdware/dev-scripts/blob/main/docs/about-scripts/vcpkg-workspace.md), downloaded into `scripts/`. The script checks out vcpkg and the overlays that `scripts/vcpkg-sources.json` specifies into `deps/`, and installs the dependencies there. It runs on Windows, macOS and Linux.
```bash
+ curl -fsSL -o scripts/vcpkg-workspace.cmake https://raw.githubusercontent.com/stdware/dev-scripts/main/scripts/vcpkg-workspace.cmake
cmake -P scripts/vcpkg-workspace.cmake install --qt-dir /path/to/Qt/lib/cmake/Qt6
```
- `cmake -P scripts/vcpkg-workspace.cmake help` lists the other commands. `--dry-run` prints the commands without running them.
+ On Windows, the script is downloaded in PowerShell or in cmd with
+
+ ```bat
+ powershell -Command "Invoke-WebRequest -OutFile scripts/vcpkg-workspace.cmake https://raw.githubusercontent.com/stdware/dev-scripts/main/scripts/vcpkg-workspace.cmake"
+ ```
+
+ `cmake -P scripts/vcpkg-workspace.cmake help` lists the other commands. `--dry-run` prints the commands without running them. `scripts/vcpkg-sources.local.json` takes precedence over `scripts/vcpkg-sources.json`, for example to use a local overlay.
4. Build using CMake with the vcpkg toolchain in `deps/`.
diff --git a/scripts/.gitignore b/scripts/.gitignore
index db5b2399..9f556eb6 100644
--- a/scripts/.gitignore
+++ b/scripts/.gitignore
@@ -1,5 +1,7 @@
# SPDX-FileCopyrightText: Team OpenVPI
# SPDX-License-Identifier: Apache-2.0
-# The local sources of scripts/vcpkg-workspace.cmake, which take precedence over vcpkg-sources.json
+# vcpkg-workspace.cmake from https://github.com/stdware/dev-scripts, downloaded before use, and its
+# local sources, which take precedence over vcpkg-sources.json
+/vcpkg-workspace.cmake
/vcpkg-sources.local.json
diff --git a/scripts/vcpkg-sources.json b/scripts/vcpkg-sources.json
index 3fcca2fa..59c42162 100644
--- a/scripts/vcpkg-sources.json
+++ b/scripts/vcpkg-sources.json
@@ -1,4 +1,5 @@
{
+ "manifest-dir": "vcpkg-manifest",
"vcpkg": {
"repository": "https://github.com/microsoft/vcpkg.git",
"ref": "master"
@@ -10,6 +11,10 @@
}
],
"tasks": {
- "list": ["${VCPKG_EXECUTABLE}", "list", "--x-install-root=${VCPKG_INSTALLED_DIR}"]
+ "list": [
+ "${VCPKG_EXECUTABLE}",
+ "list",
+ "--x-install-root=${VCPKG_INSTALLED_DIR}"
+ ]
}
-}
+}
\ No newline at end of file
diff --git a/scripts/vcpkg-workspace.cmake b/scripts/vcpkg-workspace.cmake
deleted file mode 100644
index 723ee185..00000000
--- a/scripts/vcpkg-workspace.cmake
+++ /dev/null
@@ -1,848 +0,0 @@
-# SPDX-FileCopyrightText: Team OpenVPI
-# SPDX-License-Identifier: Apache-2.0
-
-#[[
-Prepares the vcpkg workspace of DiffScope in deps/: vcpkg and the overlays as the sources specify
-them, and the dependencies of the manifest installed into /installed.
-
-Usage:
- cmake -P scripts/vcpkg-workspace.cmake [...] [-- ...]
-
-Commands:
- fetch Checks out vcpkg and every overlay at the specified commits, and bootstraps
- vcpkg after a checkout of another commit
- install Runs fetch, then installs the dependencies with every overlay
- status Lists the specified and the checked-out commit of every repository
- run Runs a task of the sources
- help Prints this text
-
-Options:
- --dry-run Prints the commands that change the workspace without running them
- --triplet The triplet to install. vcpkg selects its default triplet if absent.
- --qt-dir The Qt 6 or Qt 5 for the ports that build against Qt, as its CMake
- directory, /lib/cmake/Qt6 or /lib/cmake/Qt5, or as its
- prefix . The environment variable QT_DIR, Qt6_DIR or Qt5_DIR
- applies if absent, in this order.
- --sources-file A file of sources. The option may be given more than once, and a
- file given earlier takes precedence. Without the option, the files
- are scripts/vcpkg-sources.local.json if it exists, then
- scripts/vcpkg-sources.json.
- --manifest-dir The directory of vcpkg.json, scripts/vcpkg-manifest if absent
-
-A path on the command line is relative to the current directory.
-
-The arguments after -- are appended to the command of vcpkg install or of the task.
-
-Sources:
- The files of sources are merged, and a file earlier in the list takes precedence over the
- files after it. vcpkg and an overlay of the same name take the members that the file of the
- highest precedence gives. The overlays of the last file keep their order, and an overlay that
- a file of higher precedence adds precedes the overlays of the files after that file. A task,
- "clone-args" and "fetch-args" at the top level are taken from the file of the highest
- precedence that gives them.
-
- vcpkg and the overlays of the same name in several files are one repository, which is cloned
- at most once.
-
- The member "path" of vcpkg or of an overlay names a local directory, relative to the file that
- gives it, which is used as it is: neither cloned, fetched nor checked out. A local vcpkg is
- bootstrapped only if it has no vcpkg executable. The file of the highest precedence that gives
- a path or a repository decides between them, and an entry that gives both uses the path.
-
-Repositories:
- The ref of a repository is a full commit hash, a branch or a tag. A branch and a tag are
- fetched each time, and the checkout follows the commit that they name.
-
- A repository is created with git clone --no-checkout [--branch [] ] ,
- with --branch for a branch or a tag, and updated with git fetch origin [. The
- members "clone-args" and "fetch-args" of vcpkg or of an overlay are lists of arguments for
- that repository, and the members of the same names at the top level are the lists for the
- others, each ["--quiet", "--depth", "1"] if absent. clone-args apply only to a repository that
- does not exist yet. A commit hash that is checked out is not fetched again, so other
- fetch-args apply at the next change of the ref. A shallow checkout keeps its depth unless
- fetch-args include --unshallow or --deepen.
-
- A repository that begins with ./ or ../ is relative to the remote origin of this repository,
- as in .gitmodules, so that it is fetched by the same protocol. A checkout whose remote
- differs, such as after a change between HTTPS and SSH, has its remote set to the resolved URL.
-
- On Windows, the proxy of the Internet settings applies to git and vcpkg unless the environment
- variable HTTP_PROXY or HTTPS_PROXY is set.
-
-Overlays:
- An overlay is checked out into deps/]. The member "name" defaults to the last component
- of the repository without .git, or of the path, and two overlays of the same last component
- need a name of their own.
-
- The overlays precede vcpkg, and an overlay precedes the overlays after it. The directories
- ports and triplets of an overlay are passed to vcpkg if they exist. The members "ports" and
- "triplets" of an overlay name other directories, relative to the overlay.
-
-Tasks:
- A task is a list of arguments in "tasks", run in the root of the repository. The variables
- ${VCPKG_ROOT}, ${VCPKG_EXECUTABLE}, ${VCPKG_INSTALLED_DIR} and ${VCPKG_MANIFEST_DIR} in an
- argument are replaced with their values. An argument that is exactly ${VCPKG_OVERLAY_ARGS} is
- replaced with the overlay arguments of vcpkg.
-]]
-
-cmake_minimum_required(VERSION 3.19)
-
-get_filename_component(_root_dir "${CMAKE_CURRENT_LIST_DIR}/.." ABSOLUTE)
-set(_deps_dir "${_root_dir}/deps")
-
-# ----------------------------------
-# Output and execution
-# ----------------------------------
-function(_vw_print _text)
- execute_process(COMMAND "${CMAKE_COMMAND}" -E echo "${_text}")
-endfunction()
-
-# Prints the command in ARGN, quoting an argument with a space, and runs it in the root of the
-# repository unless the run is a dry run. Stops if the command fails.
-function(_vw_execute)
- set(_text)
- foreach(_arg IN LISTS ARGN)
- if(_arg STREQUAL "" OR _arg MATCHES " ")
- set(_arg "\"${_arg}\"")
- endif()
- string(APPEND _text " ${_arg}")
- endforeach()
- _vw_print(">${_text}")
-
- if(_dry_run)
- return()
- endif()
-
- execute_process(COMMAND ${ARGN} WORKING_DIRECTORY "${_root_dir}" RESULT_VARIABLE _result)
- if(NOT _result EQUAL 0)
- message(FATAL_ERROR "The command failed with ${_result}.")
- endif()
-endfunction()
-
-# Sets to the output of the git command in ARGN, empty if the command fails. A query runs
-# also in a dry run, because it does not change the workspace.
-function(_vw_git_query _out)
- execute_process(COMMAND "${_git}" ${ARGN}
- OUTPUT_VARIABLE _output
- ERROR_QUIET
- RESULT_VARIABLE _result
- OUTPUT_STRIP_TRAILING_WHITESPACE
- )
- if(NOT _result EQUAL 0)
- set(_output)
- endif()
- set(${_out} "${_output}" PARENT_SCOPE)
-endfunction()
-
-# ----------------------------------
-# Arguments
-# ----------------------------------
-set(_command)
-set(_task)
-set(_dry_run OFF)
-set(_triplet)
-set(_qt_dir)
-foreach(_variable IN ITEMS QT_DIR Qt6_DIR Qt5_DIR)
- if(NOT "$ENV{${_variable}}" STREQUAL "")
- set(_qt_dir "$ENV{${_variable}}")
- break()
- endif()
-endforeach()
-set(_source_files)
-set(_manifest_dir "${_root_dir}/scripts/vcpkg-manifest")
-set(_extra_args)
-
-set(_index 0)
-while(_index LESS CMAKE_ARGC)
- if(CMAKE_ARGV${_index} STREQUAL "-P")
- break()
- endif()
- math(EXPR _index "${_index} + 1")
-endwhile()
-math(EXPR _index "${_index} + 2")
-
-while(_index LESS CMAKE_ARGC)
- set(_arg "${CMAKE_ARGV${_index}}")
- math(EXPR _index "${_index} + 1")
-
- if(_arg STREQUAL "--")
- while(_index LESS CMAKE_ARGC)
- list(APPEND _extra_args "${CMAKE_ARGV${_index}}")
- math(EXPR _index "${_index} + 1")
- endwhile()
- elseif(_arg STREQUAL "--dry-run")
- set(_dry_run ON)
- elseif(_arg MATCHES "^--(triplet|qt-dir|sources-file|manifest-dir)$")
- if(NOT _index LESS CMAKE_ARGC)
- message(FATAL_ERROR "${_arg} requires a value.")
- endif()
- set(_value "${CMAKE_ARGV${_index}}")
- math(EXPR _index "${_index} + 1")
-
- # A path on the command line is relative to the current directory, which is the source
- # directory of a script
- if(_arg MATCHES "^--(sources-file|manifest-dir)$")
- get_filename_component(_value "${_value}" ABSOLUTE
- BASE_DIR "${CMAKE_CURRENT_SOURCE_DIR}")
- endif()
-
- if(_arg STREQUAL "--triplet")
- set(_triplet "${_value}")
- elseif(_arg STREQUAL "--qt-dir")
- set(_qt_dir "${_value}")
- elseif(_arg STREQUAL "--sources-file")
- list(APPEND _source_files "${_value}")
- else()
- set(_manifest_dir "${_value}")
- endif()
- elseif(_arg MATCHES "^-")
- message(FATAL_ERROR "Unknown option ${_arg}. See help.")
- elseif(NOT _command)
- set(_command "${_arg}")
- elseif(_command STREQUAL "run" AND NOT _task)
- set(_task "${_arg}")
- else()
- message(FATAL_ERROR "Unexpected argument ${_arg}. See help.")
- endif()
-endwhile()
-
-if(NOT _command OR _command STREQUAL "help")
- file(READ "${CMAKE_CURRENT_LIST_FILE}" _self)
- string(REGEX MATCH "#\\[\\[\n(.*)\n\\]\\]" _ "${_self}")
- _vw_print("${CMAKE_MATCH_1}")
- return()
-endif()
-
-if(NOT _command MATCHES "^(fetch|install|status|run)$")
- message(FATAL_ERROR "Unknown command ${_command}. See help.")
-endif()
-
-if(_command STREQUAL "run" AND NOT _task)
- message(FATAL_ERROR "run requires the name of a task.")
-endif()
-
-find_program(_git NAMES git)
-if(NOT _git)
- message(FATAL_ERROR "git is required and was not found.")
-endif()
-
-# ----------------------------------
-# Proxy
-# ----------------------------------
-
-# Sets to the proxy server of the Internet settings of Windows, empty if the proxy is
-# disabled. Taken from qm_get_windows_proxy() of qmsetup. A server given for each protocol, such
-# as http=host:port;https=host:port, yields the server of HTTPS, else that of HTTP.
-function(_vw_get_windows_proxy _out)
- set(${_out} "" PARENT_SCOPE)
- set(_key "HKEY_CURRENT_USER\\Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings")
-
- execute_process(COMMAND reg query "${_key}" /v ProxyEnable
- OUTPUT_VARIABLE _enable
- OUTPUT_STRIP_TRAILING_WHITESPACE
- ERROR_QUIET
- )
- if(NOT _enable MATCHES "ProxyEnable[ \t\r\n]+REG_DWORD[ \t\r\n]+0x1")
- return()
- endif()
-
- execute_process(COMMAND reg query "${_key}" /v ProxyServer
- OUTPUT_VARIABLE _server
- OUTPUT_STRIP_TRAILING_WHITESPACE
- ERROR_QUIET
- )
- if(NOT _server MATCHES "ProxyServer[ \t\r\n]+REG_SZ[ \t\r\n]+([^\r\n]*)")
- return()
- endif()
- set(_server "${CMAKE_MATCH_1}")
-
- if(_server MATCHES "=")
- if(_server MATCHES "(^|;)https=([^;]+)")
- set(_server "${CMAKE_MATCH_2}")
- elseif(_server MATCHES "(^|;)http=([^;]+)")
- set(_server "${CMAKE_MATCH_2}")
- else()
- return()
- endif()
- endif()
-
- set(${_out} "${_server}" PARENT_SCOPE)
-endfunction()
-
-if(CMAKE_HOST_WIN32 AND NOT DEFINED ENV{HTTP_PROXY} AND NOT DEFINED ENV{HTTPS_PROXY})
- _vw_get_windows_proxy(_proxy)
- if(_proxy)
- set(ENV{HTTP_PROXY} "http://${_proxy}")
- set(ENV{HTTPS_PROXY} "http://${_proxy}")
- set(ENV{ALL_PROXY} "http://${_proxy}")
- _vw_print("Proxy: http://${_proxy}")
- endif()
-endif()
-
-# ----------------------------------
-# Sources
-# ----------------------------------
-
-# Sets to the repository . A URL that begins with ./ or ../ is relative to the URL of the
-# remote origin of this repository, as a relative URL of a git submodule, so that the repository
-# is fetched by the protocol of this repository. Each ../ removes the last component of the base.
-function(_vw_resolve_url _url _out)
- if(NOT _url MATCHES "^\\.\\.?/")
- set(${_out} "${_url}" PARENT_SCOPE)
- return()
- endif()
-
- _vw_git_query(_base -C "${_root_dir}" remote get-url origin)
- if(_base STREQUAL "")
- message(FATAL_ERROR "The repository ${_url} is relative, and this repository has no "
- "remote origin to resolve it against.")
- endif()
- string(REGEX REPLACE "/+$" "" _base "${_base}")
-
- set(_rest "${_url}")
- while(TRUE)
- if(_rest MATCHES "^\\./(.*)$")
- set(_rest "${CMAKE_MATCH_1}")
- elseif(_rest MATCHES "^\\.\\./(.*)$")
- set(_rest "${CMAKE_MATCH_1}")
- if(NOT _base MATCHES "^(.*[/:])[^/:]+$")
- message(FATAL_ERROR "The repository ${_url} leaves the remote ${_base}.")
- endif()
- set(_base "${CMAKE_MATCH_1}")
- if(_base MATCHES "^(.*[^/])/+$")
- set(_base "${CMAKE_MATCH_1}")
- endif()
- else()
- break()
- endif()
- endwhile()
-
- if(_base MATCHES ":$")
- set(${_out} "${_base}${_rest}" PARENT_SCOPE)
- else()
- set(${_out} "${_base}/${_rest}" PARENT_SCOPE)
- endif()
-endfunction()
-
-# Sets to the string at the path in ARGN of the JSON in the variable , and to
-# whether the member exists.
-function(_vw_json_string _json _out _found)
- string(JSON _value ERROR_VARIABLE _error GET "${${_json}}" ${ARGN})
- if(_error)
- set(${_found} OFF PARENT_SCOPE)
- set(${_out} "" PARENT_SCOPE)
- else()
- set(${_found} ON PARENT_SCOPE)
- set(${_out} "${_value}" PARENT_SCOPE)
- endif()
-endfunction()
-
-# Sets to the strings of the array at the path in ARGN of the JSON in the variable , and
-# to whether the member exists. A member that is not an array stops the run.
-function(_vw_json_string_array _json _out _found)
- string(JSON _type ERROR_VARIABLE _error TYPE "${${_json}}" ${ARGN})
- if(_error)
- set(${_found} OFF PARENT_SCOPE)
- set(${_out} "" PARENT_SCOPE)
- return()
- endif()
- if(NOT _type STREQUAL "ARRAY")
- string(JOIN "." _path ${ARGN})
- message(FATAL_ERROR "${_path} of ${_source_file} is not an array of strings.")
- endif()
- string(JSON _count LENGTH "${${_json}}" ${ARGN})
- set(_items)
- if(_count GREATER 0)
- math(EXPR _last "${_count} - 1")
- foreach(_i RANGE ${_last})
- string(JSON _item GET "${${_json}}" ${ARGN} ${_i})
- list(APPEND _items "${_item}")
- endforeach()
- endif()
- set(${_found} ON PARENT_SCOPE)
- set(${_out} "${_items}" PARENT_SCOPE)
-endfunction()
-
-# Sets to the last component of , a URL or a path, without .git
-function(_vw_default_name _location _out)
- string(REGEX REPLACE "[/\\\\]+$" "" _name "${_location}")
- string(REGEX REPLACE "^.*[/\\\\:]" "" _name "${_name}")
- string(REGEX REPLACE "\\.git$" "" _name "${_name}")
- set(${_out} "${_name}" PARENT_SCOPE)
-endfunction()
-
-# Copies the members of the entry at the path in ARGN of the current source into the variables
-# _entry__, and records the members of arrays in _entry__has_. A path is
-# made absolute against the directory of the source, and a repository is resolved.
-macro(_vw_merge_entry _key)
- # A repository of a file of higher precedence replaces a path of the files merged before it,
- # unless the same entry also gives a path
- _vw_json_string(_source_json _value _found ${ARGN} repository)
- if(_found)
- _vw_resolve_url("${_value}" _entry_${_key}_url)
- set(_entry_${_key}_path)
- endif()
- _vw_json_string(_source_json _value _found ${ARGN} ref)
- if(_found)
- set(_entry_${_key}_ref "${_value}")
- endif()
- _vw_json_string(_source_json _value _found ${ARGN} path)
- if(_found)
- get_filename_component(_entry_${_key}_path "${_value}" ABSOLUTE BASE_DIR "${_source_dir}")
- endif()
- foreach(_member IN ITEMS ports triplets)
- _vw_json_string(_source_json _value _found ${ARGN} ${_member})
- if(_found)
- set(_entry_${_key}_${_member} "${_value}")
- endif()
- endforeach()
- foreach(_kind IN ITEMS clone fetch)
- _vw_json_string_array(_source_json _value _found ${ARGN} ${_kind}-args)
- if(_found)
- set(_entry_${_key}_${_kind}_args "${_value}")
- set(_entry_${_key}_has_${_kind}_args ON)
- endif()
- endforeach()
-endmacro()
-
-# The files of sources in their precedence, the local file before the sources by default
-if(NOT _source_files)
- if(EXISTS "${_root_dir}/scripts/vcpkg-sources.local.json")
- list(APPEND _source_files "${_root_dir}/scripts/vcpkg-sources.local.json")
- endif()
- list(APPEND _source_files "${_root_dir}/scripts/vcpkg-sources.json")
-endif()
-
-list(LENGTH _source_files _length)
-if(_length GREATER 1)
- foreach(_source_file IN LISTS _source_files)
- _vw_print("Sources: ${_source_file}")
- endforeach()
-endif()
-
-set(_default_clone_args --quiet --depth 1)
-set(_default_fetch_args --quiet --depth 1)
-set(_overlay_names)
-set(_source_count 0)
-
-# The files are merged from the lowest precedence, so that a file of higher precedence replaces
-# the members of the files merged before it
-set(_merge_order ${_source_files})
-list(REVERSE _merge_order)
-
-foreach(_source_file IN LISTS _merge_order)
- if(NOT EXISTS "${_source_file}")
- message(FATAL_ERROR "The sources ${_source_file} do not exist.")
- endif()
- math(EXPR _source_count "${_source_count} + 1")
- file(READ "${_source_file}" _source_json)
- set(_source_json_${_source_count} "${_source_json}")
- get_filename_component(_source_dir "${_source_file}" DIRECTORY)
-
- foreach(_kind IN ITEMS clone fetch)
- _vw_json_string_array(_source_json _value _found ${_kind}-args)
- if(_found)
- set(_default_${_kind}_args "${_value}")
- endif()
- endforeach()
-
- string(JSON _type ERROR_VARIABLE _error TYPE "${_source_json}" vcpkg)
- if(NOT _error)
- _vw_merge_entry(vcpkg vcpkg)
- endif()
-
- string(JSON _count ERROR_VARIABLE _error LENGTH "${_source_json}" overlays)
- if(_error)
- set(_count 0)
- endif()
- set(_new_names)
- set(_file_names)
- foreach(_json_index RANGE ${_count})
- if(_json_index EQUAL _count)
- break()
- endif()
-
- _vw_json_string(_source_json _name _found overlays ${_json_index} name)
- if(NOT _found)
- _vw_json_string(_source_json _location _found overlays ${_json_index} repository)
- if(NOT _found)
- _vw_json_string(_source_json _location _found overlays ${_json_index} path)
- endif()
- if(NOT _found)
- message(FATAL_ERROR "Overlay ${_json_index} of ${_source_file} has neither a "
- "repository nor a path.")
- endif()
- _vw_default_name("${_location}" _name)
- endif()
- if(NOT _name MATCHES "^[A-Za-z0-9._-]+$" OR _name STREQUAL "vcpkg")
- message(FATAL_ERROR "The overlay name \"${_name}\" in ${_source_file} is not a valid "
- "directory name or is reserved for vcpkg. Give the overlay a name.")
- endif()
- if(_name IN_LIST _file_names)
- message(FATAL_ERROR "Two overlays of ${_source_file} are named \"${_name}\". Give one "
- "of them another name.")
- endif()
- list(APPEND _file_names "${_name}")
- if(NOT _name IN_LIST _overlay_names)
- list(APPEND _new_names "${_name}")
- endif()
-
- _vw_merge_entry(${_name} overlays ${_json_index})
- endforeach()
-
- # The overlays of the file of the lowest precedence keep their order, and the new overlays of
- # a file of higher precedence precede the overlays merged before them
- if(_source_count EQUAL 1)
- list(APPEND _overlay_names ${_new_names})
- else()
- list(PREPEND _overlay_names ${_new_names})
- endif()
-endforeach()
-
-# The repositories by index: 0 is vcpkg, and 1 and later are the overlays in their order.
-set(_repo_name_0 vcpkg)
-set(_repo_keys vcpkg ${_overlay_names})
-list(LENGTH _overlay_names _repo_last)
-
-set(_number 0)
-foreach(_key IN LISTS _repo_keys)
- set(_repo_url_${_number} "${_entry_${_key}_url}")
- set(_repo_ref_${_number} "${_entry_${_key}_ref}")
- if(_number GREATER 0)
- set(_repo_name_${_number} "${_key}")
- endif()
-
- if(NOT "${_entry_${_key}_path}" STREQUAL "")
- set(_repo_local_${_number} ON)
- set(_repo_dir_${_number} "${_entry_${_key}_path}")
- else()
- set(_repo_local_${_number} OFF)
- set(_repo_dir_${_number} "${_deps_dir}/${_key}")
- if(_repo_url_${_number} STREQUAL "" OR _repo_ref_${_number} STREQUAL "")
- message(FATAL_ERROR "${_key} needs a repository and a ref, or a path.")
- endif()
- endif()
-
- foreach(_kind IN ITEMS clone fetch)
- if(_entry_${_key}_has_${_kind}_args)
- set(_repo_${_kind}_args_${_number} ${_entry_${_key}_${_kind}_args})
- else()
- set(_repo_${_kind}_args_${_number} ${_default_${_kind}_args})
- endif()
- endforeach()
-
- foreach(_member IN ITEMS ports triplets)
- set(_subdir "${_entry_${_key}_${_member}}")
- if(_subdir STREQUAL "")
- set(_subdir ${_member})
- endif()
- set(_repo_${_member}_${_number} "${_repo_dir_${_number}}/${_subdir}")
- endforeach()
-
- math(EXPR _number "${_number} + 1")
-endforeach()
-
-set(_vcpkg_dir "${_repo_dir_0}")
-set(_installed_dir "${_vcpkg_dir}/installed")
-if(CMAKE_HOST_WIN32)
- set(_vcpkg_executable "${_vcpkg_dir}/vcpkg.exe")
-else()
- set(_vcpkg_executable "${_vcpkg_dir}/vcpkg")
-endif()
-
-# Sets to whether [ is a full commit hash, 40 hexadecimal digits. The regular expressions
-# of CMake have no repetition count.
-function(_vw_is_full_hash _ref _out)
- string(LENGTH "${_ref}" _length)
- if(_ref MATCHES "^[0-9a-f]+$" AND _length EQUAL 40)
- set(${_out} ON PARENT_SCOPE)
- else()
- set(${_out} OFF PARENT_SCOPE)
- endif()
-endfunction()
-
-# A remote serves a commit by its full hash only, and an abbreviated hash cannot be fetched
-foreach(_number RANGE 0 ${_repo_last})
- if(_repo_local_${_number})
- continue()
- endif()
- set(_ref "${_repo_ref_${_number}}")
- _vw_is_full_hash("${_ref}" _full)
- if(_ref MATCHES "^[0-9a-f]+$" AND NOT _full)
- message(FATAL_ERROR "The ref ${_ref} of ${_repo_name_${_number}} looks like an "
- "abbreviated commit hash. Give the full hash of 40 digits, a branch or a tag.")
- endif()
-endforeach()
-
-# Sets ] to the arguments of vcpkg for the overlays. A directory that does not exist is left
-# out, except in a dry run, which may precede the checkout.
-function(_vw_overlay_args _out)
- set(_args)
- foreach(_number RANGE 1 ${_repo_last})
- if(_repo_last EQUAL 0)
- break()
- endif()
- if(_dry_run OR IS_DIRECTORY "${_repo_ports_${_number}}")
- list(APPEND _args "--overlay-ports=${_repo_ports_${_number}}")
- endif()
- if(_dry_run OR IS_DIRECTORY "${_repo_triplets_${_number}}")
- list(APPEND _args "--overlay-triplets=${_repo_triplets_${_number}}")
- endif()
- endforeach()
- set(${_out} ${_args} PARENT_SCOPE)
-endfunction()
-
-# ----------------------------------
-# Commands
-# ----------------------------------
-
-# Checks out the repository of at its specified commit. Sets to whether the
-# checked-out commit changed. A repository with changes to tracked files is not checked out. A
-# local directory is used as it is.
-function(_vw_checkout _number _changed)
- set(_name "${_repo_name_${_number}}")
- set(_dir "${_repo_dir_${_number}}")
- set(_url "${_repo_url_${_number}}")
- set(_ref "${_repo_ref_${_number}}")
- set(${_changed} OFF PARENT_SCOPE)
-
- if(_repo_local_${_number})
- if(NOT IS_DIRECTORY "${_dir}")
- message(FATAL_ERROR "The local directory ${_dir} of ${_name} does not exist.")
- endif()
- _vw_print("${_name} uses the local directory ${_dir}.")
- return()
- endif()
-
- _vw_is_full_hash("${_ref}" _full)
-
- # The clone leaves the checkout to the steps below. git clone takes a branch or a tag but no
- # commit, so a commit is fetched after the clone of the default branch.
- if(NOT EXISTS "${_dir}/.git")
- set(_branch)
- if(NOT _full)
- set(_branch --branch "${_ref}")
- endif()
- _vw_execute("${_git}" clone ${_repo_clone_args_${_number}} --no-checkout ${_branch}
- "${_url}" "${_dir}")
- set(_head)
- else()
- _vw_git_query(_current_url -C "${_dir}" remote get-url origin)
- if(NOT _current_url STREQUAL _url)
- _vw_execute("${_git}" -C "${_dir}" remote set-url origin "${_url}")
- endif()
- _vw_git_query(_head -C "${_dir}" rev-parse HEAD)
- endif()
-
- # A commit hash needs no fetch if it is checked out. A branch or a tag is fetched each time,
- # and the fetched commit is compared.
- if(_full AND _head STREQUAL _ref)
- _vw_print("${_name} is at ${_ref}.")
- return()
- endif()
-
- _vw_execute("${_git}" -C "${_dir}" fetch ${_repo_fetch_args_${_number}} origin "${_ref}")
-
- if(NOT _dry_run)
- _vw_git_query(_fetched -C "${_dir}" rev-parse FETCH_HEAD)
- if(_head AND _fetched STREQUAL _head)
- _vw_print("${_name} is at ${_ref}, ${_head}.")
- return()
- endif()
- endif()
-
- if(_head)
- _vw_git_query(_changes -C "${_dir}" status --porcelain --untracked-files=no)
- if(_changes)
- message(FATAL_ERROR "${_dir} has changes to tracked files. Commit or discard them "
- "before ${_name} is checked out at ${_ref}.")
- endif()
- endif()
-
- _vw_execute("${_git}" -C "${_dir}" checkout --quiet --detach FETCH_HEAD)
- set(${_changed} ON PARENT_SCOPE)
-endfunction()
-
-function(_vw_fetch)
- file(MAKE_DIRECTORY "${_deps_dir}")
-
- _vw_checkout(0 _vcpkg_changed)
- foreach(_number RANGE 1 ${_repo_last})
- if(_repo_last EQUAL 0)
- break()
- endif()
- _vw_checkout(${_number} _)
- endforeach()
-
- # The bootstrap downloads the vcpkg tool of the checked-out commit
- if(_vcpkg_changed OR NOT EXISTS "${_vcpkg_executable}")
- if(CMAKE_HOST_WIN32)
- file(TO_NATIVE_PATH "${_vcpkg_dir}/bootstrap-vcpkg.bat" _bootstrap)
- _vw_execute(cmd /c "${_bootstrap}" -disableMetrics)
- else()
- _vw_execute(sh "${_vcpkg_dir}/bootstrap-vcpkg.sh" -disableMetrics)
- endif()
- endif()
-endfunction()
-
-# Sets to the CMake directory of the Qt in , the directory itself or
-# /lib/cmake/Qt, and to 6 or 5 by the configuration file that it holds. A dry
-# run accepts a directory without Qt as the CMake directory of Qt 6.
-function(_vw_resolve_qt_dir _dir _cmake_dir _major)
- foreach(_candidate_major IN ITEMS 6 5)
- foreach(_candidate IN ITEMS "${_dir}" "${_dir}/lib/cmake/Qt${_candidate_major}")
- if(EXISTS "${_candidate}/Qt${_candidate_major}Config.cmake")
- set(${_cmake_dir} "${_candidate}" PARENT_SCOPE)
- set(${_major} ${_candidate_major} PARENT_SCOPE)
- return()
- endif()
- endforeach()
- endforeach()
-
- if(_dry_run)
- message(WARNING "${_dir} holds neither Qt6Config.cmake nor Qt5Config.cmake. The dry run "
- "takes it as the CMake directory of Qt 6.")
- set(${_cmake_dir} "${_dir}" PARENT_SCOPE)
- set(${_major} 6 PARENT_SCOPE)
- return()
- endif()
-
- message(FATAL_ERROR "${_dir} is neither the CMake directory of Qt 6 or Qt 5, which holds "
- "Qt6Config.cmake or Qt5Config.cmake, nor a Qt prefix with lib/cmake/Qt6 or lib/cmake/Qt5.")
-endfunction()
-
-function(_vw_install)
- set(_args "${_vcpkg_executable}" install
- "--x-manifest-root=${_manifest_dir}"
- "--x-install-root=${_installed_dir}"
- )
- if(_triplet)
- list(APPEND _args "--triplet=${_triplet}")
- endif()
- _vw_overlay_args(_overlay_args)
- list(APPEND _args ${_overlay_args} ${_extra_args})
-
- # The ports that build against Qt find it through QT_DIR and Qt_DIR, which vcpkg passes
- # to them only if VCPKG_KEEP_ENV_VARS names them
- if(_qt_dir)
- set(ENV{QT_DIR} "${_qt_cmake_dir}")
- set(ENV{Qt${_qt_major}_DIR} "${_qt_cmake_dir}")
- set(_keep "$ENV{VCPKG_KEEP_ENV_VARS}")
- list(APPEND _keep QT_DIR Qt${_qt_major}_DIR)
- list(REMOVE_DUPLICATES _keep)
- string(JOIN ";" _keep_text ${_keep})
- set(ENV{VCPKG_KEEP_ENV_VARS} "${_keep_text}")
- string(CONCAT _text "Environment: QT_DIR=${_qt_cmake_dir} "
- "Qt${_qt_major}_DIR=${_qt_cmake_dir} VCPKG_KEEP_ENV_VARS=${_keep_text}")
- _vw_print("${_text}")
- else()
- message(WARNING "Neither --qt-dir nor the environment variable QT_DIR, Qt6_DIR or Qt5_DIR "
- "is set. The ports that build against Qt fail to find it.")
- endif()
-
- _vw_execute(${_args})
-endfunction()
-
-function(_vw_status)
- foreach(_number RANGE 0 ${_repo_last})
- set(_dir "${_repo_dir_${_number}}")
- set(_ref "${_repo_ref_${_number}}")
- if(_repo_local_${_number})
- set(_ref "local: ${_dir}")
- if(IS_DIRECTORY "${_dir}")
- _vw_git_query(_head -C "${_dir}" rev-parse HEAD)
- set(_state "local")
- else()
- set(_head "(none)")
- set(_state "missing")
- endif()
- elseif(NOT EXISTS "${_dir}/.git")
- set(_head "(none)")
- set(_state "missing")
- else()
- _vw_git_query(_head -C "${_dir}" rev-parse HEAD)
- _vw_git_query(_changes -C "${_dir}" status --porcelain --untracked-files=no)
-
- # A branch or a tag is resolved to its commit on the remote
- set(_commit "${_ref}")
- _vw_is_full_hash("${_ref}" _full)
- if(NOT _full)
- _vw_git_query(_remote -C "${_dir}" ls-remote origin "${_ref}")
- if(_remote MATCHES "^([0-9a-f]+)")
- set(_commit "${CMAKE_MATCH_1}")
- string(APPEND _ref " (${_commit})")
- endif()
- endif()
-
- if(_head STREQUAL _commit)
- set(_state "up to date")
- else()
- set(_state "differs")
- endif()
- if(_changes)
- string(APPEND _state ", local changes")
- endif()
- endif()
- if(_head STREQUAL "")
- set(_head "(not a git repository)")
- endif()
- string(CONCAT _text "${_repo_name_${_number}}\n specified: ${_ref}\n"
- " checked out: ${_head}\n state: ${_state}")
- _vw_print("${_text}")
- endforeach()
-endfunction()
-
-function(_vw_run)
- # The file of the highest precedence that gives the task, which is merged last
- set(_found_json)
- set(_number ${_source_count})
- while(_number GREATER 0)
- string(JSON _type ERROR_VARIABLE _error TYPE "${_source_json_${_number}}" tasks "${_task}")
- if(NOT _error)
- if(NOT _type STREQUAL "ARRAY")
- message(FATAL_ERROR "The task \"${_task}\" is not a list of arguments.")
- endif()
- set(_found_json "${_source_json_${_number}}")
- break()
- endif()
- math(EXPR _number "${_number} - 1")
- endwhile()
- if(_found_json STREQUAL "")
- message(FATAL_ERROR "No sources give the task \"${_task}\".")
- endif()
-
- string(JSON _count LENGTH "${_found_json}" tasks "${_task}")
- _vw_overlay_args(_overlay_args)
- set(_args)
- math(EXPR _last "${_count} - 1")
- foreach(_i RANGE ${_last})
- string(JSON _arg GET "${_found_json}" tasks "${_task}" ${_i})
- if(_arg STREQUAL [=[${VCPKG_OVERLAY_ARGS}]=])
- list(APPEND _args ${_overlay_args})
- continue()
- endif()
- string(REPLACE [=[${VCPKG_ROOT}]=] "${_vcpkg_dir}" _arg "${_arg}")
- string(REPLACE [=[${VCPKG_EXECUTABLE}]=] "${_vcpkg_executable}" _arg "${_arg}")
- string(REPLACE [=[${VCPKG_INSTALLED_DIR}]=] "${_installed_dir}" _arg "${_arg}")
- string(REPLACE [=[${VCPKG_MANIFEST_DIR}]=] "${_manifest_dir}" _arg "${_arg}")
- list(APPEND _args "${_arg}")
- endforeach()
- list(APPEND _args ${_extra_args})
-
- _vw_execute(${_args})
-endfunction()
-
-if(_command STREQUAL "fetch")
- _vw_fetch()
-elseif(_command STREQUAL "install")
- # Qt is resolved before the fetch, so that a wrong directory stops the run at once
- if(_qt_dir)
- _vw_resolve_qt_dir("${_qt_dir}" _qt_cmake_dir _qt_major)
- endif()
- _vw_fetch()
- _vw_install()
-elseif(_command STREQUAL "status")
- _vw_status()
-elseif(_command STREQUAL "run")
- _vw_run()
-endif()
From ea62c3163d23f3c3af95fd7588f98b1e5c0b1033 Mon Sep 17 00:00:00 2001
From: SineStriker
Date: Sun, 25 Oct 2026 00:19:13 +0800
Subject: [PATCH 6/8] Stop ignoring the vcpkg directory in the root, which
deps/vcpkg replaces
---
.gitignore | 1 -
1 file changed, 1 deletion(-)
diff --git a/.gitignore b/.gitignore
index 8abbd663..e135dc16 100644
--- a/.gitignore
+++ b/.gitignore
@@ -89,7 +89,6 @@ cache
.vs
out/
CMakeSettings.json
-/vcpkg
/deps
/data
/*.natvis
From 8280f3e3dcefe181d4746636a83614996756a277 Mon Sep 17 00:00:00 2001
From: SineStriker
Date: Sun, 25 Oct 2026 00:28:21 +0800
Subject: [PATCH 7/8] Pass Qt to vcpkg-workspace.cmake with --qt-dir in place
of the environment variable QT_DIR
---
.github/actions/initialize-vcpkg/action.yml | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/.github/actions/initialize-vcpkg/action.yml b/.github/actions/initialize-vcpkg/action.yml
index 511f2c51..c90ecb65 100644
--- a/.github/actions/initialize-vcpkg/action.yml
+++ b/.github/actions/initialize-vcpkg/action.yml
@@ -37,7 +37,6 @@ runs:
- name: Install vcpkg dependencies
env:
USE_CACHE: ${{ inputs.use_cache }}
- QT_DIR: ${{ env.QT_ROOT_DIR }}/lib/cmake/Qt6
VCPKG_KEEP_ENV_VARS: "VCPKG_BUILD_TYPE;MACOSX_DEPLOYMENT_TARGET"
DEV_SCRIPTS_REF: ${{ env.DEV_SCRIPTS_REF || 'main' }}
shell: pwsh
@@ -49,7 +48,7 @@ runs:
# vcpkg-workspace.cmake is not part of the repository, see scripts/.gitignore
Invoke-WebRequest -OutFile scripts/vcpkg-workspace.cmake `
"https://raw.githubusercontent.com/stdware/dev-scripts/$env:DEV_SCRIPTS_REF/scripts/vcpkg-workspace.cmake"
- $arguments = @('-P', 'scripts/vcpkg-workspace.cmake', 'install')
+ $arguments = @('-P', 'scripts/vcpkg-workspace.cmake', 'install', '--qt-dir', $env:QT_ROOT_DIR)
if ($env:USE_CACHE -ne 'true') {
$env:VCPKG_BINARY_SOURCES = 'clear'
'VCPKG_BINARY_SOURCES=clear' >> $env:GITHUB_ENV
From df67e0f6b6736c1e9b7f88a49eb0d6d22505eca0 Mon Sep 17 00:00:00 2001
From: SineStriker
Date: Sun, 25 Oct 2026 20:06:05 +0800
Subject: [PATCH 8/8] Install the vcpkg dependencies in CI with the action
vcpkg-workspace of dev-scripts
---
.github/actions/initialize-vcpkg/action.yml | 68 ---------------------
.github/workflows/build.yml | 12 +++-
2 files changed, 9 insertions(+), 71 deletions(-)
delete mode 100644 .github/actions/initialize-vcpkg/action.yml
diff --git a/.github/actions/initialize-vcpkg/action.yml b/.github/actions/initialize-vcpkg/action.yml
deleted file mode 100644
index c90ecb65..00000000
--- a/.github/actions/initialize-vcpkg/action.yml
+++ /dev/null
@@ -1,68 +0,0 @@
-# SPDX-FileCopyrightText: Team OpenVPI
-# SPDX-License-Identifier: Apache-2.0
-
-name: Initialize Vcpkg
-description: Prepare the vcpkg workspace in deps/ with scripts/vcpkg-workspace.cmake
-
-inputs:
- use_cache:
- description: Restore and save vcpkg binary caches
- required: false
- default: 'true'
-
-
-runs:
- using: "composite"
- steps:
- - name: Prepare vcpkg
- if: inputs.use_cache == 'true'
- shell: pwsh
- run: |
- New-Item -ItemType Directory -Path ${{ github.workspace }}/../vcpkg_archives -Force
- Write-Output VCPKG_DEFAULT_BINARY_CACHE=$(Resolve-Path ${{ github.workspace }}/../vcpkg_archives) >> $env:GITHUB_ENV
-
- # The binary cache of vcpkg is keyed by the ABI of each package, so a cache of other sources
- # is also of use.
- - name: Restore vcpkg binary cache
- id: cache_vcpkg
- if: inputs.use_cache == 'true'
- uses: actions/cache/restore@v4
- with:
- path: ${{ env.VCPKG_DEFAULT_BINARY_CACHE }}
- key: ${{ runner.os }}-vcpkg-${{ hashFiles('scripts/vcpkg-sources.json') }}-${{ github.sha }}
- restore-keys: |
- ${{ runner.os }}-vcpkg-${{ hashFiles('scripts/vcpkg-sources.json') }}-
- ${{ runner.os }}-vcpkg-
-
- - name: Install vcpkg dependencies
- env:
- USE_CACHE: ${{ inputs.use_cache }}
- VCPKG_KEEP_ENV_VARS: "VCPKG_BUILD_TYPE;MACOSX_DEPLOYMENT_TARGET"
- DEV_SCRIPTS_REF: ${{ env.DEV_SCRIPTS_REF || 'main' }}
- shell: pwsh
- run: |
- if ($env:USE_CACHE -eq 'true' -and !(Test-Path $env:VCPKG_DEFAULT_BINARY_CACHE)) {
- New-Item $env:VCPKG_DEFAULT_BINARY_CACHE -ItemType directory
- }
- Set-Location ${{ github.workspace }}
- # vcpkg-workspace.cmake is not part of the repository, see scripts/.gitignore
- Invoke-WebRequest -OutFile scripts/vcpkg-workspace.cmake `
- "https://raw.githubusercontent.com/stdware/dev-scripts/$env:DEV_SCRIPTS_REF/scripts/vcpkg-workspace.cmake"
- $arguments = @('-P', 'scripts/vcpkg-workspace.cmake', 'install', '--qt-dir', $env:QT_ROOT_DIR)
- if ($env:USE_CACHE -ne 'true') {
- $env:VCPKG_BINARY_SOURCES = 'clear'
- 'VCPKG_BINARY_SOURCES=clear' >> $env:GITHUB_ENV
- $arguments += @('--', '--binarysource=clear')
- }
- cmake @arguments
- if ($LASTEXITCODE -ne 0) {
- throw 'vcpkg installation failed'
- }
- Write-Output VCPKG_ROOT_DIR=$(Resolve-Path deps/vcpkg) >> $env:GITHUB_ENV
-
- - name: Save vcpkg binary cache
- if: inputs.use_cache == 'true'
- uses: actions/cache/save@v4
- with:
- path: ${{ env.VCPKG_DEFAULT_BINARY_CACHE }}
- key: ${{ runner.os }}-vcpkg-${{ hashFiles('scripts/vcpkg-sources.json') }}-${{ github.sha }}
diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index 6f2e83d3..a3bb31a0 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -78,7 +78,6 @@ jobs:
runs-on: ${{ matrix.os }}
env:
QT_VERSION: 6.11.2
- DEV_SCRIPTS_REF: main
VCPKG_DISABLE_METRICS: '1'
INNOSETUP_REF: is-6_5_4
DSSP_CHANNEL: nightly
@@ -168,9 +167,13 @@ jobs:
"DSSP_DIRECTORY=$dsspDirectory" >> $env:GITHUB_ENV
- name: Initialize Vcpkg
- uses: ./.github/actions/initialize-vcpkg
+ id: vcpkg
+ uses: stdware/dev-scripts/actions/vcpkg-workspace@main
+ env:
+ VCPKG_KEEP_ENV_VARS: VCPKG_BUILD_TYPE;MACOSX_DEPLOYMENT_TARGET
with:
- use_cache: ${{ inputs.build_type != 'nightly' && inputs.use_cache }}
+ qt-dir: ${{ env.QT_ROOT_DIR }}
+ cache: ${{ inputs.build_type != 'nightly' && inputs.use_cache }}
- name: Restore CCache cache
if: inputs.build_type != 'nightly' && inputs.use_cache && inputs.use_ccache
@@ -184,6 +187,7 @@ jobs:
- name: Build
env:
DSPM_EXECUTABLE: ${{ steps.download_dspm.outputs.path }}
+ VCPKG_ROOT_DIR: ${{ steps.vcpkg.outputs.vcpkg-root }}
run: |
$expectedRunnerArch = $env:ARTIFACT_ARCH -eq 'amd64' ? 'X64' : 'ARM64'
if ($env:RUNNER_ARCH -ne $expectedRunnerArch) {
@@ -217,6 +221,8 @@ jobs:
key: ${{ runner.os }}-ccache-${{ inputs.source_sha }}
- name: Collect symbol files
+ env:
+ VCPKG_ROOT_DIR: ${{ steps.vcpkg.outputs.vcpkg-root }}
run: |
$output = & ./scripts/ci/Collect-Symbol-Files.ps1 `
-VcpkgRootDir $env:VCPKG_ROOT_DIR `