From 2e2f9435857c8476cc5c639514c0a235eda87579 Mon Sep 17 00:00:00 2001 From: Don van den Bergh Date: Tue, 23 Jun 2026 10:14:19 +0200 Subject: [PATCH 01/10] add basic r cmd check action --- .github/workflows/rcmdcheck.yml | 45 +++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) create mode 100644 .github/workflows/rcmdcheck.yml diff --git a/.github/workflows/rcmdcheck.yml b/.github/workflows/rcmdcheck.yml new file mode 100644 index 0000000..d0f77a7 --- /dev/null +++ b/.github/workflows/rcmdcheck.yml @@ -0,0 +1,45 @@ +on: + push: + paths: ['**.R', '**.svg', '**.Rd', 'DESCRIPTION', 'NAMESPACE', '.github/workflows/rcmdcheck.yml'] + pull_request: + paths: ['**.R', '**.svg', '**.Rd', 'DESCRIPTION', 'NAMESPACE', '.github/workflows/rcmdcheck.yml'] + +name: R-CMD-check + +jobs: + R-CMD-check: + runs-on: ${{ matrix.config.os }} + + name: ${{ matrix.config.os }} (${{ matrix.config.r }}) + + strategy: + fail-fast: false + matrix: + config: # current jasp version, latest r-release, and development + - {os: windows-latest, r: '4.5.2'} + - {os: macOS-latest, r: '4.5.2'} + - {os: ubuntu-latest, r: '4.5.2'} + - {os: ubuntu-latest, r: 'release'} + + env: + GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }} + R_KEEP_PKG_SOURCE: yes + + steps: + - uses: actions/checkout@v2 + +# adapted from https://github.com/r-lib/actions/blob/v2/examples/check-standard.yaml + - uses: r-lib/actions/setup-pandoc@v2 + + - uses: r-lib/actions/setup-r@v2 + with: + r-version: ${{ matrix.config.r }} + http-user-agent: ${{ matrix.config.http-user-agent }} + use-public-rspm: true + + - uses: r-lib/actions/setup-r-dependencies@v2 + with: + extra-packages: any::rcmdcheck + needs: check + + - uses: r-lib/actions/check-r-package@v2 \ No newline at end of file From 91c32c4a6593689c189875f601dfd0694e43fc92 Mon Sep 17 00:00:00 2001 From: Don van den Bergh Date: Tue, 23 Jun 2026 10:20:41 +0200 Subject: [PATCH 02/10] bump version in configure files --- configure | 2 +- configure.win | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/configure b/configure index d699fea..e16d8ce 100755 --- a/configure +++ b/configure @@ -9,7 +9,7 @@ set -e # ---------- GitHub Release configuration ---------- # Pre-built binaries are hosted as GitHub Release assets. # The build-syntaxinterface.yml workflow produces these. -GITHUB_RELEASE_TAG="${JASPSYNTAX_RELEASE_TAG:-v0.97.0}" +GITHUB_RELEASE_TAG="${JASPSYNTAX_RELEASE_TAG:-v0.97.1}" GITHUB_RELEASE_REPO="${JASPSYNTAX_RELEASE_REPO:-jasp-stats/jaspSyntax}" GITHUB_RELEASE_URL="https://github.com/${GITHUB_RELEASE_REPO}/releases/download/${GITHUB_RELEASE_TAG}" SOURCE_BUNDLE_ASSET="SyntaxInterface-sources.tar.gz" diff --git a/configure.win b/configure.win index 8c57792..3e12d3c 100644 --- a/configure.win +++ b/configure.win @@ -7,7 +7,7 @@ set -e # ---------- GitHub Release configuration ---------- -GITHUB_RELEASE_TAG="${JASPSYNTAX_RELEASE_TAG:-v0.97.0}" +GITHUB_RELEASE_TAG="${JASPSYNTAX_RELEASE_TAG:-v0.97.1}" GITHUB_RELEASE_REPO="${JASPSYNTAX_RELEASE_REPO:-jasp-stats/jaspSyntax}" GITHUB_RELEASE_URL="https://github.com/${GITHUB_RELEASE_REPO}/releases/download/${GITHUB_RELEASE_TAG}" SOURCE_BUNDLE_ASSET="SyntaxInterface-sources.tar.gz" From 92b4d89c5812f4c40bc08c8ba5719908b4474e9a Mon Sep 17 00:00:00 2001 From: Don van den Bergh Date: Tue, 23 Jun 2026 10:40:31 +0200 Subject: [PATCH 03/10] pin windows to 2022 in syntaxinterface --- .github/workflows/build-syntaxinterface.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/build-syntaxinterface.yml b/.github/workflows/build-syntaxinterface.yml index e97f35c..27af22f 100644 --- a/.github/workflows/build-syntaxinterface.yml +++ b/.github/workflows/build-syntaxinterface.yml @@ -48,13 +48,14 @@ jobs: matrix: ${{ steps.set-matrix.outputs.matrix }} steps: - id: set-matrix + # when updating windows-2022 to windows-latest (2026 as if writing) make sure to change "2022" in the steps below run: | ALL='{"include":[ {"os":"ubuntu-latest", "arch":"x86_64","artifact":"libSyntaxInterface-linux-x86_64.so", "lib_file":"libSyntaxInterface.so"}, {"os":"ubuntu-24.04-arm", "arch":"arm64", "artifact":"libSyntaxInterface-linux-arm64.so", "lib_file":"libSyntaxInterface.so"}, {"os":"macos-15-intel", "arch":"x86_64","artifact":"libSyntaxInterface-darwin-x86_64.dylib", "lib_file":"libSyntaxInterface.dylib"}, {"os":"macos-15", "arch":"arm64", "artifact":"libSyntaxInterface-darwin-arm64.dylib", "lib_file":"libSyntaxInterface.dylib"}, - {"os":"windows-latest", "arch":"x86_64","artifact":"SyntaxInterface-windows-x86_64.dll", "lib_file":"SyntaxInterface.dll"} + {"os":"windows-2022", "arch":"x86_64","artifact":"SyntaxInterface-windows-x86_64.dll", "lib_file":"SyntaxInterface.dll"} ]}' FILTER='${{ github.event.inputs.os_filter }}' if [ -z "$FILTER" ]; then From dd5c47363f41230b5ea8c03bb2b55ce97062ff7e Mon Sep 17 00:00:00 2001 From: Don van den Bergh Date: Tue, 23 Jun 2026 13:11:29 +0200 Subject: [PATCH 04/10] try to fix windows build --- .github/workflows/build-syntaxinterface.yml | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/.github/workflows/build-syntaxinterface.yml b/.github/workflows/build-syntaxinterface.yml index 27af22f..116ffb1 100644 --- a/.github/workflows/build-syntaxinterface.yml +++ b/.github/workflows/build-syntaxinterface.yml @@ -151,6 +151,15 @@ jobs: copy C:\rtools45\ucrt64\bin\make.exe C:\rtools45\ucrt64\bin\mingw32-make.exe ) ) + echo C:\rtools45\ucrt64\bin>>"%GITHUB_PATH%" + + - name: Test Rtools45 ucrt64 compiler (Windows) + if: runner.os == 'Windows' + shell: cmd + run: | + echo int main(void) { return 0; } > "%RUNNER_TEMP%\test.c" + gcc "%RUNNER_TEMP%\test.c" -o "%RUNNER_TEMP%\test.exe" + "%RUNNER_TEMP%\test.exe" # ---- Install Qt from source and build static ---- From fd3ba3ecd5d62865c93451775e03b5f2f42b95be Mon Sep 17 00:00:00 2001 From: Don van den Bergh Date: Tue, 23 Jun 2026 13:35:09 +0200 Subject: [PATCH 05/10] Potential fix for pull request finding 'CodeQL / Workflow does not contain permissions' Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> --- .github/workflows/rcmdcheck.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/rcmdcheck.yml b/.github/workflows/rcmdcheck.yml index d0f77a7..132d221 100644 --- a/.github/workflows/rcmdcheck.yml +++ b/.github/workflows/rcmdcheck.yml @@ -6,6 +6,9 @@ on: name: R-CMD-check +permissions: + contents: read + jobs: R-CMD-check: runs-on: ${{ matrix.config.os }} From 2fe35f62e1f99e077e6696548fd6a6d30b556712 Mon Sep 17 00:00:00 2001 From: Don van den Bergh Date: Tue, 23 Jun 2026 14:52:16 +0200 Subject: [PATCH 06/10] try to fix ci --- .Rbuildignore | 3 +++ DESCRIPTION | 4 ++-- configure | 4 ---- man/nativeBridge.Rd | 1 + src/Makevars.in | 2 +- 5 files changed, 7 insertions(+), 7 deletions(-) diff --git a/.Rbuildignore b/.Rbuildignore index 8ed19a6..32a2f96 100644 --- a/.Rbuildignore +++ b/.Rbuildignore @@ -13,3 +13,6 @@ ^src/SyntaxInterface\.provenance$ ^src/syntaxbridge_interface\.h$ ^src/json$ +^tests/figs/\.gitkeep$ +^tests/testthat/\.gitkeep$ +^tests/testthat/fixtures/minimalModule/inst/icons/\.gitkeep$ diff --git a/DESCRIPTION b/DESCRIPTION index bc4c407..71dd724 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -7,11 +7,11 @@ Author: JASP Team Website: jasp-stats.org Maintainer: JASP Team Description: Exposes the native JASP SyntaxInterface bridge to R so package code can parse module descriptions, replay QML option binding, load datasets, and read saved .jasp files using the same runtime preparation path as JASP Desktop. -License: GPL (>= 2) +License: GPL-3 + file LICENSE Encoding: UTF-8 URL: https://github.com/jasp-stats/jaspSyntax BugReports: https://github.com/jasp-stats/jaspSyntax/issues -SystemRequirements: libcurl or wget (for downloading the SyntaxInterface library during installation) +SystemRequirements: libcurl or wget (for downloading the SyntaxInterface library during installation), libglvnd Imports: callr, jsonlite, diff --git a/configure b/configure index e16d8ce..8dad9e9 100755 --- a/configure +++ b/configure @@ -294,10 +294,6 @@ SYNTAXINTERFACE_HEADER_ORIGIN="${SYNTAXINTERFACE_HEADER_ORIGIN}" \ SYNTAXINTERFACE_BINARY_ORIGIN="${SYNTAXINTERFACE_BINARY_ORIGIN}" \ "${BASH:-bash}" tools/check-syntaxinterface-symbols.sh "${SYNTAXINTERFACE_HEADER_PATH}" "${SYNTAXINTERFACE_BINARY_PATH}" "src/syntaxfunctions.cpp" -mkdir -p inst/libs -cp "src/${DLL_NAME}" "inst/libs/${DLL_NAME}" -cp "src/SyntaxInterface.provenance" "inst/libs/SyntaxInterface.provenance" - PKG_LIBS=-lSyntaxInterface # Set platform-specific RPATH so jaspSyntax.so can find libSyntaxInterface at runtime diff --git a/man/nativeBridge.Rd b/man/nativeBridge.Rd index d465a0d..ea2b389 100644 --- a/man/nativeBridge.Rd +++ b/man/nativeBridge.Rd @@ -1,4 +1,5 @@ \name{nativeBridge} +\alias{nativeBridge} \alias{analysisOptionsFromJaspFile} \alias{cleanUp} \alias{generateAnalysisWrapper} diff --git a/src/Makevars.in b/src/Makevars.in index 921df0e..4914388 100644 --- a/src/Makevars.in +++ b/src/Makevars.in @@ -2,7 +2,7 @@ PKG_CPPFLAGS = -I. @cppflags@ CXX_STD = CXX20 JSON_OBJECTS = json/json_reader.o json/json_value.o json/json_writer.o OBJECTS = RcppExports.o dataframeimporter.o syntaxfunctions.o $(JSON_OBJECTS) -PKG_LIBS += -L. @libflags@ @rpathflags@ +PKG_LIBS = -L. @libflags@ @rpathflags@ json/%.o: json/%.cpp $(CXX) $(ALL_CPPFLAGS) $(ALL_CXXFLAGS) -c $< -o $@ From 767e4b37107be94fa1c1277f0a99a7f8b01c61d9 Mon Sep 17 00:00:00 2001 From: Don van den Bergh Date: Tue, 23 Jun 2026 15:29:12 +0200 Subject: [PATCH 07/10] try to fix ci --- .github/workflows/rcmdcheck.yml | 6 +- DESCRIPTION | 2 +- configure | 94 ++--- configure.win | 525 +++++++++++++------------ man/jaspSyntax-package.Rd | 4 - src/install.libs.R | 2 +- tools/check-syntaxinterface-symbols.sh | 80 ++-- 7 files changed, 363 insertions(+), 350 deletions(-) diff --git a/.github/workflows/rcmdcheck.yml b/.github/workflows/rcmdcheck.yml index 132d221..e045cc5 100644 --- a/.github/workflows/rcmdcheck.yml +++ b/.github/workflows/rcmdcheck.yml @@ -1,10 +1,12 @@ on: push: paths: ['**.R', '**.svg', '**.Rd', 'DESCRIPTION', 'NAMESPACE', '.github/workflows/rcmdcheck.yml'] + branches: + - master pull_request: paths: ['**.R', '**.svg', '**.Rd', 'DESCRIPTION', 'NAMESPACE', '.github/workflows/rcmdcheck.yml'] - -name: R-CMD-check + branches: + - master permissions: contents: read diff --git a/DESCRIPTION b/DESCRIPTION index 71dd724..ba1f886 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -11,7 +11,7 @@ License: GPL-3 + file LICENSE Encoding: UTF-8 URL: https://github.com/jasp-stats/jaspSyntax BugReports: https://github.com/jasp-stats/jaspSyntax/issues -SystemRequirements: libcurl or wget (for downloading the SyntaxInterface library during installation), libglvnd +SystemRequirements: libcurl or wget (for downloading the SyntaxInterface library during installation), OpenGL (libglvnd; provides libOpenGL.so.0) Imports: callr, jsonlite, diff --git a/configure b/configure index 8dad9e9..a7bdb21 100755 --- a/configure +++ b/configure @@ -1,4 +1,4 @@ -#!/bin/bash +#!/bin/sh # To manually specify a location for JASP_BUILD_DIR or JASP_SOURCE_DIR do # # options(configure.vars = c(jaspSyntax = "JASP_SOURCE_DIR=''")) @@ -19,45 +19,45 @@ SOURCE_BUNDLE_JASP_DESKTOP_SHA="" SOURCE_BUNDLE_JASP_SYNTAX_SHA="" SOURCE_BUNDLE_QT_VERSION="" -function sourceBundleProvenanceValue() { - local FILE_PATH="$1" - local KEY="$2" +sourceBundleProvenanceValue() { + _sbpv_path="$1" + _sbpv_key="$2" - if [ -f "${FILE_PATH}" ]; then - grep -E "^${KEY}=" "${FILE_PATH}" | head -n 1 | sed -E 's/^[^=]+=//' + if [ -f "${_sbpv_path}" ]; then + grep -E "^${_sbpv_key}=" "${_sbpv_path}" | head -n 1 | sed 's/^[^=][^=]*=//' fi } -function downloadFile() { +downloadFile() { # downloadFile # Downloads a file using curl or wget. Exits on failure. - local URL="$1" - local OUTPUT="$2" - local DOWNLOAD_SUCCESS=1 + _dl_url="$1" + _dl_output="$2" + _dl_success=1 if command -v curl >/dev/null 2>&1; then - echo "Downloading ${URL}" - curl --fail --silent --location --output "${OUTPUT}" "${URL}" - DOWNLOAD_SUCCESS=$? + echo "Downloading ${_dl_url}" + curl --fail --silent --location --output "${_dl_output}" "${_dl_url}" + _dl_success=$? fi - if [ "${DOWNLOAD_SUCCESS}" -ne "0" ]; then + if [ "${_dl_success}" -ne "0" ]; then if command -v wget >/dev/null 2>&1; then - echo "Trying wget for ${URL}" - wget --quiet -O "${OUTPUT}" "${URL}" - DOWNLOAD_SUCCESS=$? + echo "Trying wget for ${_dl_url}" + wget --quiet -O "${_dl_output}" "${_dl_url}" + _dl_success=$? fi fi - if [ "${DOWNLOAD_SUCCESS}" -ne "0" ]; then - echo "Failed to download: ${URL}" + if [ "${_dl_success}" -ne "0" ]; then + echo "Failed to download: ${_dl_url}" return 1 fi return 0 } -function loadFile() { +loadFile() { # loadFile # Downloads / into src/. if ! downloadFile "$1/$2" "src/$2"; then @@ -68,40 +68,40 @@ Either download from \"https://github.com/jasp-stats/jasp-desktop/\" manually an fi } -function loadReleaseSourceBundle() { - local ARCHIVE_PATH="src/${SOURCE_BUNDLE_ASSET}" - local EXTRACT_DIR="src/.SyntaxInterface-sources" - local FILE_NAME +loadReleaseSourceBundle() { + _lrsb_archive="src/${SOURCE_BUNDLE_ASSET}" + _lrsb_extract="src/.SyntaxInterface-sources" + _lrsb_fname="" - if ! downloadFile "${GITHUB_RELEASE_URL}/${SOURCE_BUNDLE_ASSET}" "${ARCHIVE_PATH}"; then + if ! downloadFile "${GITHUB_RELEASE_URL}/${SOURCE_BUNDLE_ASSET}" "${_lrsb_archive}"; then printf "Installing jaspSyntax failed because the SyntaxInterface source bundle is missing from release %s.\n\ Set JASP_SOURCE_DIR to a matching jasp-desktop checkout, or publish %s together with the SyntaxInterface binaries.\n" "${GITHUB_RELEASE_TAG}" "${SOURCE_BUNDLE_ASSET}" exit 1 fi - rm -rf "${EXTRACT_DIR}" - mkdir -p "${EXTRACT_DIR}" "src/json" - tar -xzf "${ARCHIVE_PATH}" -C "${EXTRACT_DIR}" + rm -rf "${_lrsb_extract}" + mkdir -p "${_lrsb_extract}" "src/json" + tar -xzf "${_lrsb_archive}" -C "${_lrsb_extract}" - if [ ! -f "${EXTRACT_DIR}/SyntaxInterface/syntaxbridge_interface.h" ]; then + if [ ! -f "${_lrsb_extract}/SyntaxInterface/syntaxbridge_interface.h" ]; then echo "Installing jaspSyntax failed because ${SOURCE_BUNDLE_ASSET} does not contain SyntaxInterface/syntaxbridge_interface.h" exit 1 fi - cp "${EXTRACT_DIR}/SyntaxInterface/syntaxbridge_interface.h" "${SYNTAXINTERFACE_HEADER_PATH}" - for FILE_NAME in ${JSON_FILES}; do - cp "${EXTRACT_DIR}/Common/json/${FILE_NAME}" "src/json/${FILE_NAME}" + cp "${_lrsb_extract}/SyntaxInterface/syntaxbridge_interface.h" "${SYNTAXINTERFACE_HEADER_PATH}" + for _lrsb_fname in ${JSON_FILES}; do + cp "${_lrsb_extract}/Common/json/${_lrsb_fname}" "src/json/${_lrsb_fname}" done - if [ -f "${EXTRACT_DIR}/BUILD_PROVENANCE" ]; then + if [ -f "${_lrsb_extract}/BUILD_PROVENANCE" ]; then SOURCE_BUNDLE_PROVENANCE_ORIGIN="${GITHUB_RELEASE_URL}/${SOURCE_BUNDLE_ASSET}:BUILD_PROVENANCE" - SOURCE_BUNDLE_JASP_DESKTOP_REF=$(sourceBundleProvenanceValue "${EXTRACT_DIR}/BUILD_PROVENANCE" "jasp_desktop_ref") - SOURCE_BUNDLE_JASP_DESKTOP_SHA=$(sourceBundleProvenanceValue "${EXTRACT_DIR}/BUILD_PROVENANCE" "jasp_desktop_sha") - SOURCE_BUNDLE_JASP_SYNTAX_SHA=$(sourceBundleProvenanceValue "${EXTRACT_DIR}/BUILD_PROVENANCE" "jasp_syntax_sha") - SOURCE_BUNDLE_QT_VERSION=$(sourceBundleProvenanceValue "${EXTRACT_DIR}/BUILD_PROVENANCE" "qt_version") + SOURCE_BUNDLE_JASP_DESKTOP_REF=$(sourceBundleProvenanceValue "${_lrsb_extract}/BUILD_PROVENANCE" "jasp_desktop_ref") + SOURCE_BUNDLE_JASP_DESKTOP_SHA=$(sourceBundleProvenanceValue "${_lrsb_extract}/BUILD_PROVENANCE" "jasp_desktop_sha") + SOURCE_BUNDLE_JASP_SYNTAX_SHA=$(sourceBundleProvenanceValue "${_lrsb_extract}/BUILD_PROVENANCE" "jasp_syntax_sha") + SOURCE_BUNDLE_QT_VERSION=$(sourceBundleProvenanceValue "${_lrsb_extract}/BUILD_PROVENANCE" "qt_version") fi - rm -rf "${EXTRACT_DIR}" "${ARCHIVE_PATH}" + rm -rf "${_lrsb_extract}" "${_lrsb_archive}" SYNTAXINTERFACE_HEADER_ORIGIN="${GITHUB_RELEASE_URL}/${SOURCE_BUNDLE_ASSET}:SyntaxInterface/syntaxbridge_interface.h" } @@ -154,21 +154,21 @@ SYNTAXINTERFACE_HEADER_ORIGIN="" SYNTAXINTERFACE_BINARY_PATH="src/${DLL_NAME}" SYNTAXINTERFACE_BINARY_ORIGIN="" -function fileSha256() { - local FILE_PATH="$1" +fileSha256() { + _fs_path="$1" - if [ ! -f "${FILE_PATH}" ]; then + if [ ! -f "${_fs_path}" ]; then echo "unavailable" elif command -v sha256sum >/dev/null 2>&1; then - sha256sum "${FILE_PATH}" | awk '{print $1}' + sha256sum "${_fs_path}" | awk '{print $1}' elif command -v shasum >/dev/null 2>&1; then - shasum -a 256 "${FILE_PATH}" | awk '{print $1}' + shasum -a 256 "${_fs_path}" | awk '{print $1}' else echo "unavailable" fi } -function writeSyntaxInterfaceProvenance() { +writeSyntaxInterfaceProvenance() { if [ -z "${SYNTAXINTERFACE_HEADER_ORIGIN}" ]; then SYNTAXINTERFACE_HEADER_ORIGIN="local:${SYNTAXINTERFACE_HEADER_PATH}" fi @@ -205,7 +205,7 @@ EOF JSON_FILES="allocator.h assertions.h config.h forwards.h json.h json_features.h json_reader.cpp json_tool.h json_value.cpp json_valueiterator.inl json_writer.cpp reader.h value.h version.h writer.h" -if [[ "${JASP_SOURCE_DIR}" ]]; then +if [ -n "${JASP_SOURCE_DIR}" ]; then echo "JASP_SOURCE_DIR: ${JASP_SOURCE_DIR}" PKG_CXXFLAGS="-I\"${JASP_SOURCE_DIR}/SyntaxInterface\" -I\"${JASP_SOURCE_DIR}/Common\"" mkdir -p 'src/json' @@ -230,11 +230,11 @@ fi # ---------- Download pre-built library if needed ---------- -if [[ "${JASPSYNTAX_LIB_PATH}" ]]; then +if [ -n "${JASPSYNTAX_LIB_PATH}" ]; then echo "Using JASPSYNTAX_LIB_PATH: ${JASPSYNTAX_LIB_PATH}" SYNTAXINTERFACE_BINARY_ORIGIN="${JASPSYNTAX_LIB_PATH}" cp "${JASPSYNTAX_LIB_PATH}" "src/${DLL_NAME}" -elif [[ "${JASP_BUILD_DIR}" ]]; then +elif [ -n "${JASP_BUILD_DIR}" ]; then echo "JASP_BUILD_DIR: ${JASP_BUILD_DIR}" SYNTAXINTERFACE_BINARY_ORIGIN="${JASP_BUILD_DIR}/SyntaxInterface/${DLL_NAME}" cp "${SYNTAXINTERFACE_BINARY_ORIGIN}" "src/${DLL_NAME}" diff --git a/configure.win b/configure.win index 3e12d3c..0aa9b20 100644 --- a/configure.win +++ b/configure.win @@ -1,4 +1,4 @@ -#!/bin/bash +#!/bin/sh # To manually specify a location for JASP_BUILD_DIR or JASP_SOURCE_DIR do # # options(configure.vars = c(jaspSyntax = "JASP_SOURCE_DIR=''")) @@ -17,20 +17,20 @@ SOURCE_BUNDLE_JASP_DESKTOP_SHA="" SOURCE_BUNDLE_JASP_SYNTAX_SHA="" SOURCE_BUNDLE_QT_VERSION="" -function sourceBundleProvenanceValue() { - local FILE_PATH="$1" - local KEY="$2" +sourceBundleProvenanceValue() { + _sbpv_path="$1" + _sbpv_key="$2" - if [ -f "${FILE_PATH}" ]; then - grep -E "^${KEY}=" "${FILE_PATH}" | head -n 1 | sed -E 's/^[^=]+=//' + if [ -f "${_sbpv_path}" ]; then + grep -E "^${_sbpv_key}=" "${_sbpv_path}" | head -n 1 | sed 's/^[^=][^=]*=//' fi } -function downloadFile() { - local URL="$1" - local OUTPUT="$2" - local DOWNLOAD_SUCCESS=1 +downloadFile() { + URL="$1" + OUTPUT="$2" + DOWNLOAD_SUCCESS=1 if command -v curl >/dev/null 2>&1; then echo "Downloading ${URL}" @@ -53,7 +53,7 @@ function downloadFile() { return 0 } -function loadFile() { +loadFile() { if ! downloadFile "$1/$2" "src/$2"; then printf "Installing jaspSyntax failed because the required file %s is missing.\n\ Normally this is downloaded automatically if either curl or wget is available, but apparently this failed.\n\ @@ -62,10 +62,10 @@ Either download from \"https://github.com/jasp-stats/jasp-desktop/\" manually an fi } -function loadReleaseSourceBundle() { - local ARCHIVE_PATH="src/${SOURCE_BUNDLE_ASSET}" - local EXTRACT_DIR="src/.SyntaxInterface-sources" - local FILE_NAME +loadReleaseSourceBundle() { + ARCHIVE_PATH="src/${SOURCE_BUNDLE_ASSET}" + EXTRACT_DIR="src/.SyntaxInterface-sources" + FILE_NAME if ! downloadFile "${GITHUB_RELEASE_URL}/${SOURCE_BUNDLE_ASSET}" "${ARCHIVE_PATH}"; then printf "Installing jaspSyntax failed because the SyntaxInterface source bundle is missing from release %s.\n\ @@ -99,11 +99,11 @@ Set JASP_SOURCE_DIR to a matching jasp-desktop checkout, or publish %s together SYNTAXINTERFACE_HEADER_ORIGIN="${GITHUB_RELEASE_URL}/${SOURCE_BUNDLE_ASSET}:SyntaxInterface/syntaxbridge_interface.h" } -function verifyChecksum() { +verifyChecksum() { # verifyChecksum # Verifies the checksum of against SHA256SUMS from the GitHub Release. - local FILE_PATH="$1" - local ASSET_NAME="$2" + _vc_path="$1" + _vc_asset="$2" if command -v sha256sum >/dev/null 2>&1; then CHECKSUM_CMD="sha256sum" @@ -114,18 +114,18 @@ function verifyChecksum() { return 0 fi - echo "Verifying checksum for ${ASSET_NAME}..." + echo "Verifying checksum for ${_vc_asset}..." if downloadFile "${GITHUB_RELEASE_URL}/SHA256SUMS" "src/SHA256SUMS"; then - EXPECTED=$(grep "${ASSET_NAME}" src/SHA256SUMS | awk '{print $1}') - ACTUAL=$(${CHECKSUM_CMD} "${FILE_PATH}" | awk '{print $1}') + EXPECTED=$(grep "${_vc_asset}" src/SHA256SUMS | awk '{print $1}') + ACTUAL=$(${CHECKSUM_CMD} "${_vc_path}" | awk '{print $1}') if [ -z "${EXPECTED}" ]; then - echo "Warning: no checksum found for ${ASSET_NAME} in SHA256SUMS" + echo "Warning: no checksum found for ${_vc_asset} in SHA256SUMS" elif [ "${ACTUAL}" != "${EXPECTED}" ]; then - echo "Checksum verification FAILED for ${ASSET_NAME}!" + echo "Checksum verification FAILED for ${_vc_asset}!" echo " Expected: ${EXPECTED}" echo " Got: ${ACTUAL}" - rm -f "${FILE_PATH}" src/SHA256SUMS + rm -f "${_vc_path}" src/SHA256SUMS exit 1 else echo "Checksum verified OK" @@ -136,25 +136,25 @@ function verifyChecksum() { fi } -function findRuntimeCandidate() { - local FILE_NAME="$1" +findRuntimeCandidate() { + _frc_fname="$1" shift - local SOURCE_DIR - local CANDIDATE + _frc_srcdir="" + _frc_candidate="" - for SOURCE_DIR in "$@"; do - if [ -z "${SOURCE_DIR}" ] || [ ! -d "${SOURCE_DIR}" ]; then + for _frc_srcdir in "$@"; do + if [ -z "${_frc_srcdir}" ] || [ ! -d "${_frc_srcdir}" ]; then continue fi - if [ -f "${SOURCE_DIR}/${FILE_NAME}" ]; then - echo "${SOURCE_DIR}/${FILE_NAME}" + if [ -f "${_frc_srcdir}/${_frc_fname}" ]; then + echo "${_frc_srcdir}/${_frc_fname}" return 0 fi - CANDIDATE=$(find "${SOURCE_DIR}" -maxdepth 1 -type f -iname "${FILE_NAME}" 2>/dev/null | head -n 1) - if [ -n "${CANDIDATE}" ]; then - echo "${CANDIDATE}" + _frc_candidate=$(find "${_frc_srcdir}" -maxdepth 1 -type f -iname "${_frc_fname}" 2>/dev/null | head -n 1) + if [ -n "${_frc_candidate}" ]; then + echo "${_frc_candidate}" return 0 fi done @@ -162,167 +162,165 @@ function findRuntimeCandidate() { return 1 } -function copyOptionalFile() { - local FILE_NAME="$1" +copyOptionalFile() { + _cof_fname="$1" shift - local CANDIDATE - local DESTINATION="src/${FILE_NAME}" - - CANDIDATE=$(findRuntimeCandidate "${FILE_NAME}" "$@" || true) - if [ -n "${CANDIDATE}" ]; then - echo "Copying ${FILE_NAME} from ${CANDIDATE%/*}" - chmod u+w "${DESTINATION}" 2>/dev/null || true - cp -f "${CANDIDATE}" "${DESTINATION}" - chmod u+w "${DESTINATION}" 2>/dev/null || true + _cof_candidate="" + _cof_dest="src/${_cof_fname}" + + _cof_candidate=$(findRuntimeCandidate "${_cof_fname}" "$@" || true) + if [ -n "${_cof_candidate}" ]; then + echo "Copying ${_cof_fname} from ${_cof_candidate%/*}" + chmod u+w "${_cof_dest}" 2>/dev/null || true + cp -f "${_cof_candidate}" "${_cof_dest}" + chmod u+w "${_cof_dest}" 2>/dev/null || true return 0 fi return 1 } -function addRuntimeSearchDir() { - local DIR_PATH="$1" - local KNOWN_DIR +addRuntimeSearchDir() { + _arsd_dir="$1" - if [ -z "${DIR_PATH}" ] || [ ! -d "${DIR_PATH}" ]; then + if [ -z "${_arsd_dir}" ] || [ ! -d "${_arsd_dir}" ]; then return 0 fi - for KNOWN_DIR in "${RUNTIME_SEARCH_DIRS[@]}"; do - if [ "${KNOWN_DIR}" = "${DIR_PATH}" ]; then - return 0 - fi - done + # deduplicate: only add if not already in RUNTIME_DIRS_FILE + if grep -Fx "${_arsd_dir}" "${RUNTIME_DIRS_FILE}" >/dev/null 2>&1; then + return 0 + fi - RUNTIME_SEARCH_DIRS+=("${DIR_PATH}") + printf '%s\n' "${_arsd_dir}" >> "${RUNTIME_DIRS_FILE}" return 0 } -function discoverLocalRtoolsRuntimeDirs() { - local GCC_PATH="" - local GCC_DIR="" - local RTOOLS_ROOT="" - local ENV_NAME - local ENV_VALUE - local PATH_DIR - local OLD_IFS +discoverLocalRtoolsRuntimeDirs() { + _dlrt_gccpath="" + _dlrt_gccdir="" + _dlrt_rtroot="" + _dlrt_envname="" + _dlrt_envval="" + _dlrt_pathdir="" + _dlrt_oldifs="" if command -v g++ >/dev/null 2>&1; then - GCC_PATH=$(command -v g++) - GCC_DIR=$(dirname "${GCC_PATH}") - addRuntimeSearchDir "${GCC_DIR}" + _dlrt_gccpath=$(command -v g++) + _dlrt_gccdir=$(dirname "${_dlrt_gccpath}") + addRuntimeSearchDir "${_dlrt_gccdir}" - case "${GCC_DIR}" in + case "${_dlrt_gccdir}" in */x86_64-w64-mingw32.static.posix/bin) - RTOOLS_ROOT="${GCC_DIR%/x86_64-w64-mingw32.static.posix/bin}" - addRuntimeSearchDir "${RTOOLS_ROOT}/ucrt64/bin" + _dlrt_rtroot="${_dlrt_gccdir%/x86_64-w64-mingw32.static.posix/bin}" + addRuntimeSearchDir "${_dlrt_rtroot}/ucrt64/bin" ;; */ucrt64/bin) - addRuntimeSearchDir "${GCC_DIR}" + addRuntimeSearchDir "${_dlrt_gccdir}" ;; esac fi - for ENV_NAME in RTOOLS45_HOME RTOOLS44_HOME RTOOLS43_HOME RTOOLS42_HOME; do - ENV_VALUE=$(printenv "${ENV_NAME}" 2>/dev/null || true) - addRuntimeSearchDir "${ENV_VALUE}" - addRuntimeSearchDir "${ENV_VALUE}/bin" - addRuntimeSearchDir "${ENV_VALUE}/ucrt64/bin" - case "${ENV_VALUE}" in + for _dlrt_envname in RTOOLS45_HOME RTOOLS44_HOME RTOOLS43_HOME RTOOLS42_HOME; do + _dlrt_envval=$(printenv "${_dlrt_envname}" 2>/dev/null || true) + addRuntimeSearchDir "${_dlrt_envval}" + addRuntimeSearchDir "${_dlrt_envval}/bin" + addRuntimeSearchDir "${_dlrt_envval}/ucrt64/bin" + case "${_dlrt_envval}" in */ucrt64) - addRuntimeSearchDir "${ENV_VALUE}/bin" + addRuntimeSearchDir "${_dlrt_envval}/bin" ;; esac done - for RTOOLS_ROOT in /c/rtools45 /c/rtools44 /c/rtools43 /c/rtools42; do - addRuntimeSearchDir "${RTOOLS_ROOT}/ucrt64/bin" + for _dlrt_rtroot in /c/rtools45 /c/rtools44 /c/rtools43 /c/rtools42; do + addRuntimeSearchDir "${_dlrt_rtroot}/ucrt64/bin" done - OLD_IFS="${IFS}" + _dlrt_oldifs="${IFS}" IFS=':' - for PATH_DIR in ${PATH}; do - case "${PATH_DIR}" in + for _dlrt_pathdir in ${PATH}; do + case "${_dlrt_pathdir}" in *rtools*/ucrt64/bin) - addRuntimeSearchDir "${PATH_DIR}" + addRuntimeSearchDir "${_dlrt_pathdir}" ;; *rtools*/x86_64-w64-mingw32.static.posix/bin) - RTOOLS_ROOT="${PATH_DIR%/x86_64-w64-mingw32.static.posix/bin}" - addRuntimeSearchDir "${RTOOLS_ROOT}/ucrt64/bin" + _dlrt_rtroot="${_dlrt_pathdir%/x86_64-w64-mingw32.static.posix/bin}" + addRuntimeSearchDir "${_dlrt_rtroot}/ucrt64/bin" ;; esac done - IFS="${OLD_IFS}" + IFS="${_dlrt_oldifs}" } -function discoverWindowsSystemRuntimeDirs() { - local SYSTEM_ROOT - local SYSTEM_DIR +discoverWindowsSystemRuntimeDirs() { + _dwsr_sysroot="" + _dwsr_sysdir="" - SYSTEM_ROOT=$(printenv SystemRoot 2>/dev/null || printenv SYSTEMROOT 2>/dev/null || true) - if [ -n "${SYSTEM_ROOT}" ]; then - SYSTEM_DIR="${SYSTEM_ROOT}/System32" + _dwsr_sysroot=$(printenv SystemRoot 2>/dev/null || printenv SYSTEMROOT 2>/dev/null || true) + if [ -n "${_dwsr_sysroot}" ]; then + _dwsr_sysdir="${_dwsr_sysroot}/System32" if command -v cygpath >/dev/null 2>&1; then - addRuntimeSearchDir "$(cygpath -u "${SYSTEM_DIR}")" + addRuntimeSearchDir "$(cygpath -u "${_dwsr_sysdir}")" fi - addRuntimeSearchDir "${SYSTEM_DIR}" + addRuntimeSearchDir "${_dwsr_sysdir}" fi addRuntimeSearchDir "/c/Windows/System32" } -function addQtRuntimeDirFromCMakeCache() { - local CACHE_PATH="$1" - local PREFIX_PATH="" - local QT_CORE_DIR="" +addQtRuntimeDirFromCMakeCache() { + _aqrd_cache="$1" + _aqrd_prefix="" + _aqrd_qtcore="" - if [ ! -f "${CACHE_PATH}" ]; then + if [ ! -f "${_aqrd_cache}" ]; then return 0 fi - PREFIX_PATH=$(grep -E '^CMAKE_PREFIX_PATH:PATH=' "${CACHE_PATH}" | sed -E 's/^[^=]+=//' | head -n 1) - addRuntimeSearchDir "${PREFIX_PATH}/bin" + _aqrd_prefix=$(grep -E '^CMAKE_PREFIX_PATH:PATH=' "${_aqrd_cache}" | sed 's/^[^=][^=]*=//' | head -n 1) + addRuntimeSearchDir "${_aqrd_prefix}/bin" - QT_CORE_DIR=$(grep -E '^Qt6Core_DIR:PATH=' "${CACHE_PATH}" | sed -E 's/^[^=]+=//' | head -n 1) - case "${QT_CORE_DIR}" in + _aqrd_qtcore=$(grep -E '^Qt6Core_DIR:PATH=' "${_aqrd_cache}" | sed 's/^[^=][^=]*=//' | head -n 1) + case "${_aqrd_qtcore}" in */lib/cmake/Qt6Core) - addRuntimeSearchDir "${QT_CORE_DIR%/lib/cmake/Qt6Core}/bin" + addRuntimeSearchDir "${_aqrd_qtcore%/lib/cmake/Qt6Core}/bin" ;; esac } -function discoverLocalQtRuntimeDirs() { - local PATH_DIR - local QT_DIR - local OLD_IFS +discoverLocalQtRuntimeDirs() { + _dlqd_pathdir="" + _dlqd_qtdir="" + _dlqd_oldifs="" addRuntimeSearchDir "${JASPSYNTAX_QT_DIR}" addRuntimeSearchDir "${JASPSYNTAX_QT_DIR}/bin" addQtRuntimeDirFromCMakeCache "${JASP_BUILD_DIR}/CMakeCache.txt" - for QT_DIR in /c/Qt/*/msvc*/bin /c/Qt/*/mingw*/bin /c/Qt/*/*/bin; do - addRuntimeSearchDir "${QT_DIR}" + for _dlqd_qtdir in /c/Qt/*/msvc*/bin /c/Qt/*/mingw*/bin /c/Qt/*/*/bin; do + addRuntimeSearchDir "${_dlqd_qtdir}" done - OLD_IFS="${IFS}" + _dlqd_oldifs="${IFS}" IFS=':' - for PATH_DIR in ${PATH}; do - case "${PATH_DIR}" in + for _dlqd_pathdir in ${PATH}; do + case "${_dlqd_pathdir}" in *Qt*/bin) - addRuntimeSearchDir "${PATH_DIR}" + addRuntimeSearchDir "${_dlqd_pathdir}" ;; esac done - IFS="${OLD_IFS}" + IFS="${_dlqd_oldifs}" } -function dllNameLower() { +dllNameLower() { echo "$1" | tr '[:upper:]' '[:lower:]' } -function findObjdumpTool() { - local SEARCH_DIR - local CANDIDATE +findObjdumpTool() { + _fot_dir="" + _fot_candidate="" if [ -n "${OBJDUMP_TOOL}" ] && [ -x "${OBJDUMP_TOOL}" ]; then echo "${OBJDUMP_TOOL}" @@ -335,37 +333,37 @@ function findObjdumpTool() { return 0 fi - for SEARCH_DIR in "${RUNTIME_SEARCH_DIRS[@]}"; do - if [ -z "${SEARCH_DIR}" ] || [ ! -d "${SEARCH_DIR}" ]; then + while IFS= read -r _fot_dir; do + if [ -z "${_fot_dir}" ] || [ ! -d "${_fot_dir}" ]; then continue fi - for CANDIDATE in "${SEARCH_DIR}/objdump.exe" "${SEARCH_DIR}/objdump"; do - if [ -x "${CANDIDATE}" ]; then - OBJDUMP_TOOL="${CANDIDATE}" + for _fot_candidate in "${_fot_dir}/objdump.exe" "${_fot_dir}/objdump"; do + if [ -x "${_fot_candidate}" ]; then + OBJDUMP_TOOL="${_fot_candidate}" echo "${OBJDUMP_TOOL}" return 0 fi done - done + done < "${RUNTIME_DIRS_FILE}" return 1 } -function importedDllsForFile() { - local BINARY_PATH="$1" - local OBJDUMP_PATH +importedDllsForFile() { + _idf_bin="$1" + _idf_objdump="" - OBJDUMP_PATH=$(findObjdumpTool || true) - if [ -z "${OBJDUMP_PATH}" ]; then + _idf_objdump=$(findObjdumpTool || true) + if [ -z "${_idf_objdump}" ]; then return 1 fi - "${OBJDUMP_PATH}" -p "${BINARY_PATH}" 2>/dev/null | - sed -n -E 's/^[[:space:]]*DLL Name:[[:space:]]*//p' + "${_idf_objdump}" -p "${_idf_bin}" 2>/dev/null | + sed -n 's/^[[:space:]]*DLL Name:[[:space:]]*//p' } -function syntaxInterfaceImportedDlls() { +syntaxInterfaceImportedDlls() { if importedDllsForFile "${SYNTAXINTERFACE_BINARY_PATH}"; then return 0 fi @@ -378,11 +376,11 @@ function syntaxInterfaceImportedDlls() { fi } -function isPlatformRuntimeDll() { - local DLL_LOWER - DLL_LOWER=$(dllNameLower "$1") +isPlatformRuntimeDll() { + _iprd_lower="" + _iprd_lower=$(dllNameLower "$1") - case "${DLL_LOWER}" in + case "${_iprd_lower}" in api-ms-*.dll|ext-ms-*.dll|advapi32.dll|authz.dll|bcrypt.dll|cfgmgr32.dll|clbcatq.dll|combase.dll|comctl32.dll|comdlg32.dll|coremessaging.dll|coreuicomponents.dll|crypt32.dll|cryptbase.dll|cryptsp.dll|d3d*.dll|dcomp.dll|dnsapi.dll|dwmapi.dll|dwrite.dll|dxgi.dll|gdi32.dll|gdi32full.dll|glu32.dll|icu*.dll|imm32.dll|iphlpapi.dll|kernel32.dll|kernelbase.dll|mpr.dll|msasn1.dll|msvcp140*.dll|msvcp_win.dll|msvcrt.dll|ncrypt.dll|ncryptsslp.dll|normaliz.dll|ntasn1.dll|ntdll.dll|ole32.dll|oleacc.dll|oleaut32.dll|opengl32.dll|powrprof.dll|profapi.dll|propsys.dll|psapi.dll|rpcrt4.dll|secur32.dll|setupapi.dll|shell32.dll|shlwapi.dll|sspicli.dll|textinputframework.dll|uiautomationcore.dll|user32.dll|userenv.dll|usp10.dll|ucrtbase.dll|uxtheme.dll|vcruntime140*.dll|version.dll|win32u.dll|windows.storage.dll|winhttp.dll|wininet.dll|winmm.dll|wldp.dll|ws2_32.dll|wtsapi32.dll|xmllite.dll|r.dll|rgraphapp.dll|rblas.dll|rlapack.dll) return 0 ;; @@ -391,128 +389,132 @@ function isPlatformRuntimeDll() { return 1 } -function wasRuntimeBinaryProcessed() { - local BINARY_PATH="$1" - local PROCESSED - - for PROCESSED in "${PROCESSED_RUNTIME_BINARIES[@]}"; do - if [ "${PROCESSED}" = "${BINARY_PATH}" ]; then - return 0 - fi - done - - return 1 +wasRuntimeBinaryProcessed() { + grep -Fx "$1" "${PROCESSED_FILE}" >/dev/null 2>&1 } -function markRuntimeBinaryProcessed() { - PROCESSED_RUNTIME_BINARIES+=("$1") +markRuntimeBinaryProcessed() { + printf '%s\n' "$1" >> "${PROCESSED_FILE}" } -function addMissingTransitiveRuntimeDll() { - local DLL_NAME="$1" - local IMPORTER="$2" - local ENTRY="${DLL_NAME} imported by ${IMPORTER}" - local KNOWN_ENTRY +addMissingTransitiveRuntimeDll() { + _amtd_dll="$1" + _amtd_importer="$2" + _amtd_entry="${_amtd_dll} imported by ${_amtd_importer}" - for KNOWN_ENTRY in "${MISSING_TRANSITIVE_RUNTIME_DLLS[@]}"; do - if [ "${KNOWN_ENTRY}" = "${ENTRY}" ]; then - return 0 - fi - done + # deduplicate + if grep -Fx "${_amtd_entry}" "${MISSING_DLLS_FILE}" >/dev/null 2>&1; then + return 0 + fi - MISSING_TRANSITIVE_RUNTIME_DLLS+=("${ENTRY}") + printf '%s\n' "${_amtd_entry}" >> "${MISSING_DLLS_FILE}" } -function collectBundledRuntimeBinaries() { - local CANDIDATE +collectBundledRuntimeBinaries() { + _cbrb_candidate="" - for CANDIDATE in src/*.dll src/platforms/*.dll; do - if [ -f "${CANDIDATE}" ]; then - echo "${CANDIDATE}" + for _cbrb_candidate in src/*.dll src/platforms/*.dll; do + if [ -f "${_cbrb_candidate}" ]; then + echo "${_cbrb_candidate}" fi done } -function bundleTransitiveRuntimeDlls() { - local QUEUE=() - local INDEX=0 - local BINARY_PATH - local IMPORTED_DLL - local CANDIDATE - local ENTRY - local SEARCH_DIR - local OBJDUMP_PATH - - OBJDUMP_PATH=$(findObjdumpTool || true) - if [ -z "${OBJDUMP_PATH}" ]; then +bundleTransitiveRuntimeDlls() { + _btd_queue="/tmp/jaspsyntax-queue.$$" + _btd_index=0 + _btd_bin="" + _btd_dll="" + _btd_candidate="" + _btd_entry="" + _btd_dir="" + _btd_objdump="" + _btd_tmp="/tmp/jaspsyntax-imports.$$" + _btd_missing_count=0 + + : > "${_btd_queue}" + + _btd_objdump=$(findObjdumpTool || true) + if [ -z "${_btd_objdump}" ]; then printf "Warning: objdump is not available; jaspSyntax cannot inspect transitive Windows DLL dependencies automatically.\n\ If package loading later fails with a DLL load error, reinstall with JASPSYNTAX_RUNTIME_DIR pointing to Rtools/ucrt64/bin and, for dynamic Qt builds, JASPSYNTAX_QT_DIR pointing to the Qt bin directory.\n" return 0 fi - for BINARY_PATH in "$@"; do - if [ -f "${BINARY_PATH}" ]; then - QUEUE+=("${BINARY_PATH}") + for _btd_bin in "$@"; do + if [ -f "${_btd_bin}" ]; then + printf '%s\n' "${_btd_bin}" >> "${_btd_queue}" fi done - while [ "${INDEX}" -lt "${#QUEUE[@]}" ]; do - BINARY_PATH="${QUEUE[$INDEX]}" - INDEX=$((INDEX + 1)) + while true; do + _btd_index=$((_btd_index + 1)) + _btd_bin=$(sed -n "${_btd_index}p" "${_btd_queue}" 2>/dev/null) + [ -z "${_btd_bin}" ] && break - if wasRuntimeBinaryProcessed "${BINARY_PATH}"; then + if wasRuntimeBinaryProcessed "${_btd_bin}"; then continue fi - markRuntimeBinaryProcessed "${BINARY_PATH}" + markRuntimeBinaryProcessed "${_btd_bin}" - while IFS= read -r IMPORTED_DLL; do - if [ -z "${IMPORTED_DLL}" ] || isPlatformRuntimeDll "${IMPORTED_DLL}"; then + importedDllsForFile "${_btd_bin}" | sort -u > "${_btd_tmp}" + while IFS= read -r _btd_dll; do + if [ -z "${_btd_dll}" ] || isPlatformRuntimeDll "${_btd_dll}"; then continue fi - CANDIDATE="" - if copyOptionalFile "${IMPORTED_DLL}" "${RUNTIME_SEARCH_DIRS[@]}"; then - CANDIDATE="src/${IMPORTED_DLL}" - elif [ -f "src/${IMPORTED_DLL}" ]; then - CANDIDATE="src/${IMPORTED_DLL}" - elif [ -f "src/platforms/${IMPORTED_DLL}" ]; then - CANDIDATE="src/platforms/${IMPORTED_DLL}" + _btd_candidate="" + # collect runtime dirs from file, pass as args to copyOptionalFile + set -- + while IFS= read -r _btd_dir; do + [ -n "${_btd_dir}" ] && set -- "$@" "${_btd_dir}" + done < "${RUNTIME_DIRS_FILE}" + if copyOptionalFile "${_btd_dll}" "$@"; then + _btd_candidate="src/${_btd_dll}" + elif [ -f "src/${_btd_dll}" ]; then + _btd_candidate="src/${_btd_dll}" + elif [ -f "src/platforms/${_btd_dll}" ]; then + _btd_candidate="src/platforms/${_btd_dll}" else - addMissingTransitiveRuntimeDll "${IMPORTED_DLL}" "${BINARY_PATH}" + addMissingTransitiveRuntimeDll "${_btd_dll}" "${_btd_bin}" continue fi - QUEUE+=("${CANDIDATE}") - done < <(importedDllsForFile "${BINARY_PATH}" | sort -u) + printf '%s\n' "${_btd_candidate}" >> "${_btd_queue}" + done < "${_btd_tmp}" + rm -f "${_btd_tmp}" done - if [ "${#MISSING_TRANSITIVE_RUNTIME_DLLS[@]}" -gt 0 ]; then + rm -f "${_btd_queue}" "${_btd_tmp}" + + _btd_missing_count=$(wc -l < "${MISSING_DLLS_FILE}" 2>/dev/null | tr -d '[:space:]') + if [ -n "${_btd_missing_count}" ] && [ "${_btd_missing_count}" -gt 0 ]; then printf "Installing jaspSyntax failed because transitive Windows runtime DLL dependencies could not be bundled automatically.\n\ Set JASPSYNTAX_RUNTIME_DIR to the Rtools/ucrt64/bin directory and, for dynamic Qt builds, set JASPSYNTAX_QT_DIR to the Qt bin directory. If you are using a local Desktop build, prefer JASPSYNTAX_LIB_DIR or JASP_BUILD_DIR from the same build tree.\n" echo "Missing DLL dependencies:" - for ENTRY in "${MISSING_TRANSITIVE_RUNTIME_DLLS[@]}"; do - echo " ${ENTRY}" - done + while IFS= read -r _btd_entry; do + echo " ${_btd_entry}" + done < "${MISSING_DLLS_FILE}" echo "Searched runtime directories:" - for SEARCH_DIR in "${RUNTIME_SEARCH_DIRS[@]}"; do - if [ -n "${SEARCH_DIR}" ]; then - echo " ${SEARCH_DIR}" + while IFS= read -r _btd_dir; do + if [ -n "${_btd_dir}" ]; then + echo " ${_btd_dir}" fi - done + done < "${RUNTIME_DIRS_FILE}" exit 1 fi } -function requireRuntimeFile() { - local FILE_NAME="$1" +requireRuntimeFile() { + _rrf_fname="$1" shift - local SEARCH_DIR + _rrf_dir="" - if [ -f "src/${FILE_NAME}" ]; then + if [ -f "src/${_rrf_fname}" ]; then return 0 fi - if copyOptionalFile "${FILE_NAME}" "$@"; then + if copyOptionalFile "${_rrf_fname}" "$@"; then return 0 fi @@ -520,13 +522,13 @@ function requireRuntimeFile() { The selected SyntaxInterface binary requires matching runtime DLLs. For Rtools \ DLLs set JASPSYNTAX_RUNTIME_DIR; for dynamic Qt builds set JASPSYNTAX_QT_DIR, \ for example:\n\ -options(configure.vars = c(jaspSyntax = \"JASPSYNTAX_RUNTIME_DIR='/ucrt64/bin' JASPSYNTAX_QT_DIR='/bin'\"))\n" "${FILE_NAME}" +options(configure.vars = c(jaspSyntax = \"JASPSYNTAX_RUNTIME_DIR='/ucrt64/bin' JASPSYNTAX_QT_DIR='/bin'\"))\n" "${_rrf_fname}" if [ "$#" -gt 0 ]; then echo "Searched runtime directories:" - for SEARCH_DIR in "$@"; do - if [ -n "${SEARCH_DIR}" ]; then - echo " ${SEARCH_DIR}" + for _rrf_dir in "$@"; do + if [ -n "${_rrf_dir}" ]; then + echo " ${_rrf_dir}" fi done fi @@ -534,51 +536,51 @@ options(configure.vars = c(jaspSyntax = \"JASPSYNTAX_RUNTIME_DIR=' "${RUNTIME_DIRS_FILE}" +: > "${PROCESSED_FILE}" +: > "${MISSING_DLLS_FILE}" OBJDUMP_TOOL="" SYNTAXINTERFACE_HEADER_PATH="src/syntaxbridge_interface.h" SYNTAXINTERFACE_HEADER_ORIGIN="" SYNTAXINTERFACE_BINARY_PATH="src/SyntaxInterface.dll" SYNTAXINTERFACE_BINARY_ORIGIN="" -function fileSha256() { - local FILE_PATH="$1" +fileSha256() { + _fs_path="$1" - if [ ! -f "${FILE_PATH}" ]; then + if [ ! -f "${_fs_path}" ]; then echo "unavailable" elif command -v sha256sum >/dev/null 2>&1; then - sha256sum "${FILE_PATH}" | awk '{print $1}' + sha256sum "${_fs_path}" | awk '{print $1}' elif command -v shasum >/dev/null 2>&1; then - shasum -a 256 "${FILE_PATH}" | awk '{print $1}' + shasum -a 256 "${_fs_path}" | awk '{print $1}' else echo "unavailable" fi } -function writeSyntaxInterfaceProvenance() { +writeSyntaxInterfaceProvenance() { if [ -z "${SYNTAXINTERFACE_HEADER_ORIGIN}" ]; then SYNTAXINTERFACE_HEADER_ORIGIN="local:${SYNTAXINTERFACE_HEADER_PATH}" fi @@ -666,7 +671,7 @@ EOF JSON_FILES="allocator.h assertions.h config.h forwards.h json.h json_features.h json_reader.cpp json_tool.h json_value.cpp json_valueiterator.inl json_writer.cpp reader.h value.h version.h writer.h" -if [[ "${JASP_SOURCE_DIR}" ]]; then +if [ -n "${JASP_SOURCE_DIR}" ]; then echo "JASP_SOURCE_DIR: ${JASP_SOURCE_DIR}" PKG_CXXFLAGS="-I\"${JASP_SOURCE_DIR}/SyntaxInterface\" -I\"${JASP_SOURCE_DIR}/Common\"" mkdir -p 'src/json' @@ -694,11 +699,11 @@ fi SYNTAXINTERFACE_DLL="SyntaxInterface.dll" SYNTAXINTERFACE_ASSET="SyntaxInterface-windows-x86_64.dll" -if [[ "${JASPSYNTAX_LIB_DIR}" ]]; then +if [ -n "${JASPSYNTAX_LIB_DIR}" ]; then echo "Using JASPSYNTAX_LIB_DIR: ${JASPSYNTAX_LIB_DIR}" SYNTAXINTERFACE_BINARY_ORIGIN="${JASPSYNTAX_LIB_DIR}/${SYNTAXINTERFACE_DLL}" cp "${SYNTAXINTERFACE_BINARY_ORIGIN}" "src/${SYNTAXINTERFACE_DLL}" -elif [[ "${JASP_BUILD_DIR}" ]]; then +elif [ -n "${JASP_BUILD_DIR}" ]; then echo "JASP_BUILD_DIR: ${JASP_BUILD_DIR}" for CANDIDATE in \ "${JASP_BUILD_DIR}/${SYNTAXINTERFACE_DLL}" \ @@ -734,9 +739,9 @@ fi RINTERFACE_DLL="libR-InterfaceNoRInside.dll" RINTERFACE_ASSET="libR-InterfaceNoRInside-windows-x86_64.dll" -if [[ "${JASPSYNTAX_LIB_DIR}" ]] && [ -f "${JASPSYNTAX_LIB_DIR}/${RINTERFACE_DLL}" ]; then +if [ -n "${JASPSYNTAX_LIB_DIR}" ] && [ -f "${JASPSYNTAX_LIB_DIR}/${RINTERFACE_DLL}" ]; then cp "${JASPSYNTAX_LIB_DIR}/${RINTERFACE_DLL}" src/ -elif [[ "${JASP_BUILD_DIR}" ]]; then +elif [ -n "${JASP_BUILD_DIR}" ]; then RINTERFACE_ORIGIN="" for CANDIDATE in \ "${JASP_BUILD_DIR}/R-Interface/${RINTERFACE_DLL}" \ @@ -776,20 +781,30 @@ discoverLocalRtoolsRuntimeDirs discoverWindowsSystemRuntimeDirs for RUNTIME_DLL in ${RUNTIME_DLLS}; do - requireRuntimeFile "${RUNTIME_DLL}" "${RUNTIME_SEARCH_DIRS[@]}" + # Collect runtime dirs as positional arguments + set -- + while IFS= read -r _rrf_d; do + [ -n "${_rrf_d}" ] && set -- "$@" "${_rrf_d}" + done < "${RUNTIME_DIRS_FILE}" + requireRuntimeFile "${RUNTIME_DLL}" "$@" done discoverLocalQtRuntimeDirs QT_RUNTIME_DLLS=$(syntaxInterfaceImportedDlls | grep -E '^Qt6.*\.dll$' | sort -u || true) for QT_RUNTIME_DLL in ${QT_RUNTIME_DLLS}; do - requireRuntimeFile "${QT_RUNTIME_DLL}" "${RUNTIME_SEARCH_DIRS[@]}" + # Collect runtime dirs as positional arguments + set -- + while IFS= read -r _rrf_d; do + [ -n "${_rrf_d}" ] && set -- "$@" "${_rrf_d}" + done < "${RUNTIME_DIRS_FILE}" + requireRuntimeFile "${QT_RUNTIME_DLL}" "$@" done if [ -n "${QT_RUNTIME_DLLS}" ]; then requireQtPlatformPlugin "qminimal.dll" fi bundleTransitiveRuntimeDlls $(collectBundledRuntimeBinaries) -if compgen -G "src/Qt6*.dll" >/dev/null; then +if ls src/Qt6*.dll >/dev/null 2>&1; then requireQtPlatformPlugin "qminimal.dll" bundleTransitiveRuntimeDlls $(collectBundledRuntimeBinaries) fi diff --git a/man/jaspSyntax-package.Rd b/man/jaspSyntax-package.Rd index a405f0e..ec3a876 100644 --- a/man/jaspSyntax-package.Rd +++ b/man/jaspSyntax-package.Rd @@ -33,10 +33,6 @@ JASP Team Maintainer: JASP Team } -\references{ - This optional section can contain literature or other references for - background information. -} \keyword{ package } \seealso{ \code{\link{readAnalysisOptionsFromJaspFile}}, diff --git a/src/install.libs.R b/src/install.libs.R index fc1e341..5f9cd67 100644 --- a/src/install.libs.R +++ b/src/install.libs.R @@ -5,7 +5,7 @@ if (!file.exists(package_library)) { } shared_libraries <- Sys.glob(c("*.dll", "*.so", "*.dylib")) -metadata_files <- Sys.glob("*.provenance") +metadata_files <- Sys.glob(c("*.provenance", "symbols.rds")) files <- unique(c(package_library, shared_libraries, metadata_files)) files <- files[file.exists(files)] diff --git a/tools/check-syntaxinterface-symbols.sh b/tools/check-syntaxinterface-symbols.sh index cd514ef..d312066 100644 --- a/tools/check-syntaxinterface-symbols.sh +++ b/tools/check-syntaxinterface-symbols.sh @@ -1,6 +1,6 @@ -#!/bin/bash +#!/bin/sh -set -euo pipefail +set -eu HEADER_PATH="${1:-}" BINARY_PATH="${2:-}" @@ -9,11 +9,11 @@ SOURCE_PATH="${3:-src/syntaxfunctions.cpp}" HEADER_ORIGIN="${SYNTAXINTERFACE_HEADER_ORIGIN:-}" BINARY_ORIGIN="${SYNTAXINTERFACE_BINARY_ORIGIN:-}" -function usage() { +usage() { echo "Usage: $0 [syntaxfunctions.cpp]" >&2 } -function print_path_context() { +print_path_context() { echo " Header path: ${HEADER_PATH}" >&2 if [ -n "${HEADER_ORIGIN}" ] && [ "${HEADER_ORIGIN}" != "${HEADER_PATH}" ]; then echo " Header source: ${HEADER_ORIGIN}" >&2 @@ -25,49 +25,49 @@ function print_path_context() { echo " Native source: ${SOURCE_PATH}" >&2 } -function print_missing_symbols() { - local FILE_PATH="$1" - local SYMBOL +print_missing_symbols() { + _pms_path="$1" + _pms_sym="" - while IFS= read -r SYMBOL; do - [ -n "${SYMBOL}" ] && echo " ${SYMBOL}" >&2 - done < "${FILE_PATH}" + while IFS= read -r _pms_sym; do + [ -n "${_pms_sym}" ] && echo " ${_pms_sym}" >&2 + done < "${_pms_path}" } -function fail_with_missing_symbols() { - local TITLE="$1" - local DETAILS="$2" - local MISSING_FILE="$3" +fail_with_missing_symbols() { + _fwms_title="$1" + _fwms_details="$2" + _fwms_missing="$3" echo "" >&2 - echo "ERROR: ${TITLE}" >&2 + echo "ERROR: ${_fwms_title}" >&2 print_path_context echo " Missing symbols:" >&2 - print_missing_symbols "${MISSING_FILE}" + print_missing_symbols "${_fwms_missing}" echo "" >&2 - echo "${DETAILS}" >&2 + echo "${_fwms_details}" >&2 exit 1 } -function find_export_tool() { - local CANDIDATE +find_export_tool() { + _fet_candidate="" - for CANDIDATE in dumpbin llvm-objdump objdump x86_64-w64-mingw32-objdump nm x86_64-w64-mingw32-nm; do - if command -v "${CANDIDATE}" >/dev/null 2>&1; then - command -v "${CANDIDATE}" + for _fet_candidate in dumpbin llvm-objdump objdump x86_64-w64-mingw32-objdump nm x86_64-w64-mingw32-nm; do + if command -v "${_fet_candidate}" >/dev/null 2>&1; then + command -v "${_fet_candidate}" return 0 fi done - for CANDIDATE in \ + for _fet_candidate in \ /c/rtools46/ucrt64/bin/objdump \ /c/rtools45/ucrt64/bin/objdump \ /c/rtools44/ucrt64/bin/objdump \ /c/rtools43/ucrt64/bin/objdump \ /c/rtools42/ucrt64/bin/objdump do - if [ -x "${CANDIDATE}" ]; then - echo "${CANDIDATE}" + if [ -x "${_fet_candidate}" ]; then + echo "${_fet_candidate}" return 0 fi done @@ -75,36 +75,36 @@ function find_export_tool() { return 1 } -function write_exports() { - local TOOL_PATH="$1" - local DLL_PATH="$2" - local OUTPUT_PATH="$3" - local TOOL_NAME +write_exports() { + _we_tool="$1" + _we_dll="$2" + _we_output="$3" + _we_toolname="" - TOOL_NAME="$(basename "${TOOL_PATH}")" + _we_toolname="$(basename "${_we_tool}")" - case "${TOOL_NAME}" in + case "${_we_toolname}" in dumpbin*) - "${TOOL_PATH}" /exports "${DLL_PATH}" > "${OUTPUT_PATH}" 2>&1 + "${_we_tool}" /exports "${_we_dll}" > "${_we_output}" 2>&1 ;; *objdump*) - case "${DLL_PATH}" in + case "${_we_dll}" in *.dll|*.DLL) - "${TOOL_PATH}" -p "${DLL_PATH}" > "${OUTPUT_PATH}" 2>&1 + "${_we_tool}" -p "${_we_dll}" > "${_we_output}" 2>&1 ;; *.dylib) # macOS's objdump exits 0 for -T on Mach-O but emits only a # warning with no symbol data. Use nm -g instead. - nm -g "${DLL_PATH}" > "${OUTPUT_PATH}" 2>&1 + nm -g "${_we_dll}" > "${_we_output}" 2>&1 ;; *) - "${TOOL_PATH}" -T "${DLL_PATH}" > "${OUTPUT_PATH}" 2>&1 || - "${TOOL_PATH}" -t "${DLL_PATH}" > "${OUTPUT_PATH}" 2>&1 + "${_we_tool}" -T "${_we_dll}" > "${_we_output}" 2>&1 || + "${_we_tool}" -t "${_we_dll}" > "${_we_output}" 2>&1 ;; esac ;; *nm*) - "${TOOL_PATH}" -g "${DLL_PATH}" > "${OUTPUT_PATH}" 2>&1 + "${_we_tool}" -g "${_we_dll}" > "${_we_output}" 2>&1 ;; *) return 1 @@ -147,7 +147,7 @@ trap 'rm -f "${SYMBOLS_FILE}" "${MISSING_HEADER_FILE}" "${MISSING_EXPORTS_FILE}" : > "${MISSING_EXPORTS_FILE}" { grep -Eho 'syntaxBridge[A-Za-z0-9_]+[[:space:]]*\(' "${SOURCE_PATH}" || true; } \ - | sed -E 's/[[:space:]]*\($//' \ + | sed 's/[[:space:]]*\($//' \ | sort -u > "${SYMBOLS_FILE}" if [ ! -s "${SYMBOLS_FILE}" ]; then From 7cea7f2912c64d379be0f23e55e02ee8dbd5e0ba Mon Sep 17 00:00:00 2001 From: Don van den Bergh Date: Tue, 23 Jun 2026 15:40:02 +0200 Subject: [PATCH 08/10] try to fix ci --- tools/check-syntaxinterface-symbols.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/check-syntaxinterface-symbols.sh b/tools/check-syntaxinterface-symbols.sh index d312066..e8142a6 100644 --- a/tools/check-syntaxinterface-symbols.sh +++ b/tools/check-syntaxinterface-symbols.sh @@ -147,7 +147,7 @@ trap 'rm -f "${SYMBOLS_FILE}" "${MISSING_HEADER_FILE}" "${MISSING_EXPORTS_FILE}" : > "${MISSING_EXPORTS_FILE}" { grep -Eho 'syntaxBridge[A-Za-z0-9_]+[[:space:]]*\(' "${SOURCE_PATH}" || true; } \ - | sed 's/[[:space:]]*\($//' \ + | sed 's/[[:space:]]*($//' \ | sort -u > "${SYMBOLS_FILE}" if [ ! -s "${SYMBOLS_FILE}" ]; then From 2f463737adf9b5dfb4873c959a083eb2286c37cc Mon Sep 17 00:00:00 2001 From: Don van den Bergh Date: Tue, 23 Jun 2026 15:51:06 +0200 Subject: [PATCH 09/10] try to fix ci --- configure.win | 1 - 1 file changed, 1 deletion(-) diff --git a/configure.win b/configure.win index 0aa9b20..f8ff239 100644 --- a/configure.win +++ b/configure.win @@ -65,7 +65,6 @@ Either download from \"https://github.com/jasp-stats/jasp-desktop/\" manually an loadReleaseSourceBundle() { ARCHIVE_PATH="src/${SOURCE_BUNDLE_ASSET}" EXTRACT_DIR="src/.SyntaxInterface-sources" - FILE_NAME if ! downloadFile "${GITHUB_RELEASE_URL}/${SOURCE_BUNDLE_ASSET}" "${ARCHIVE_PATH}"; then printf "Installing jaspSyntax failed because the SyntaxInterface source bundle is missing from release %s.\n\ From e55171c7f0e871fbc9699527d2c2909e7a5c8a61 Mon Sep 17 00:00:00 2001 From: boutinb Date: Thu, 1 Oct 2026 12:51:15 +0200 Subject: [PATCH 10/10] Fail fast when the Windows build has no MSVC environment On the windows-latest VS 2026 image the hard-coded vcvarsall.bat does not exist; cmd printed "The system cannot find the path specified." and carried on, so CMake fell back to the Rtools GCC on PATH and the build only failed much later, inside Conan's MSVC build of bzip2. Keep the vcvarsall path in one place, stop the job when it is missing or fails, and configure jasp-desktop with cl explicitly: Rtools' ucrt64/bin is now on GITHUB_PATH, so GCC sits right next to MSVC for every later step. Co-Authored-By: Claude Opus 5.5 --- .github/workflows/build-syntaxinterface.yml | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/.github/workflows/build-syntaxinterface.yml b/.github/workflows/build-syntaxinterface.yml index 116ffb1..b196352 100644 --- a/.github/workflows/build-syntaxinterface.yml +++ b/.github/workflows/build-syntaxinterface.yml @@ -37,6 +37,9 @@ env: QT_VERSION: '6.10.2' QT_SUBMODULES: 'qtbase,qtdeclarative,qtshadertools' QT_SUBMODULES_CACHE_KEY: 'qtbase-qtdeclarative-qtshadertools' + # Only exists on the windows-2022 image. Without it the Windows steps have no MSVC environment and CMake silently + # falls back to whatever GCC is on PATH (Rtools), which builds Qt and jasp-desktop with the wrong compiler. + VCVARSALL: 'C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build\vcvarsall.bat' jobs: @@ -48,7 +51,7 @@ jobs: matrix: ${{ steps.set-matrix.outputs.matrix }} steps: - id: set-matrix - # when updating windows-2022 to windows-latest (2026 as if writing) make sure to change "2022" in the steps below + # when updating windows-2022 to windows-latest (2026 as of writing) make sure to update VCVARSALL above run: | ALL='{"include":[ {"os":"ubuntu-latest", "arch":"x86_64","artifact":"libSyntaxInterface-linux-x86_64.so", "lib_file":"libSyntaxInterface.so"}, @@ -196,7 +199,8 @@ jobs: if: steps.cache-qt.outputs.cache-hit != 'true' && runner.os == 'Windows' shell: cmd run: | - call "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build\vcvarsall.bat" amd64 + if not exist "%VCVARSALL%" ( echo ::error::vcvarsall.bat not found at %VCVARSALL% - did the runner image change? & exit /b 1 ) + call "%VCVARSALL%" amd64 || exit /b 1 for /f "usebackq delims=" %%P in (`powershell -NoProfile -Command "($Env:PATH -split ';' | Where-Object {$_ -notmatch 'rtools'}) -join ';'"`) do set "PATH=%%P" cd qt-src\${{ env.QT_VERSION }}\Src call configure.bat -release -static -opensource -confirm-license ^ @@ -278,9 +282,12 @@ jobs: if: runner.os == 'Windows' shell: cmd run: | - call "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build\vcvarsall.bat" amd64 + if not exist "%VCVARSALL%" ( echo ::error::vcvarsall.bat not found at %VCVARSALL% - did the runner image change? & exit /b 1 ) + call "%VCVARSALL%" amd64 || exit /b 1 for /f %%i in ('Rscript -e "cat(R.home())"') do set R_HOME=%%i cmake -G Ninja -S jasp-desktop -B build ^ + -DCMAKE_C_COMPILER=cl ^ + -DCMAKE_CXX_COMPILER=cl ^ -DCMAKE_BUILD_TYPE=Release ^ -DCMAKE_PREFIX_PATH=${{ github.workspace }}\qt-static ^ -DREQUIRE_GITHUB_PAT=OFF ^ @@ -304,7 +311,8 @@ jobs: if: runner.os == 'Windows' shell: cmd run: | - call "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build\vcvarsall.bat" amd64 + if not exist "%VCVARSALL%" ( echo ::error::vcvarsall.bat not found at %VCVARSALL% - did the runner image change? & exit /b 1 ) + call "%VCVARSALL%" amd64 || exit /b 1 cmake --build build --target SyntaxInterface --parallel # ---- Collect and upload artifact ----