diff --git a/.github/ci/cmake-consumer-smoke-vcpkg.ps1 b/.github/ci/cmake-consumer-smoke-vcpkg.ps1 index f2c0edc203..0651b83175 100644 --- a/.github/ci/cmake-consumer-smoke-vcpkg.ps1 +++ b/.github/ci/cmake-consumer-smoke-vcpkg.ps1 @@ -1,5 +1,15 @@ $ErrorActionPreference = "Stop" +function Assert-NativeCommandSucceeded { + param( + [int] $ExitCode, + [string] $Description + ) + if ($ExitCode -ne 0) { + throw "$Description failed with exit code $ExitCode." + } +} + $prefix = $args[0] if (-not $prefix) { throw "usage: cmake-consumer-smoke-vcpkg.ps1 " @@ -111,10 +121,9 @@ cmake -S (Join-Path $work "a") -B (Join-Path $work "a\build") -G "Visual Studio "-DVCPKG_INSTALLED_DIR=$consumerVcpkgInstalled" ` "-DVCPKG_TARGET_TRIPLET=x64-windows" ` "-DCMAKE_PREFIX_PATH=$prefix" 2>&1 | Tee-Object -FilePath (Join-Path $work "a\configure.log") -if ($LASTEXITCODE -ne 0) { - throw "consumer_a configure failed." -} +Assert-NativeCommandSucceeded $LASTEXITCODE "consumer_a configure" cmake --build (Join-Path $work "a\build") --config $env:BUILD_TYPE +Assert-NativeCommandSucceeded $LASTEXITCODE "consumer_a build" cmake -S (Join-Path $work "b") -B (Join-Path $work "b\build") -G "Visual Studio 17 2022" -A x64 ` "-DCMAKE_TOOLCHAIN_FILE=$toolchain" ` @@ -123,7 +132,9 @@ cmake -S (Join-Path $work "b") -B (Join-Path $work "b\build") -G "Visual Studio "-DVCPKG_INSTALLED_DIR=$consumerVcpkgInstalled" ` "-DVCPKG_TARGET_TRIPLET=x64-windows" ` "-DCMAKE_PREFIX_PATH=$prefix" +Assert-NativeCommandSucceeded $LASTEXITCODE "consumer_b configure" cmake --build (Join-Path $work "b\build") --config $env:BUILD_TYPE +Assert-NativeCommandSucceeded $LASTEXITCODE "consumer_b build" cmake -S (Join-Path $work "c") -B (Join-Path $work "c\build") -G "Visual Studio 17 2022" -A x64 ` "-DCMAKE_TOOLCHAIN_FILE=$toolchain" ` @@ -132,7 +143,9 @@ cmake -S (Join-Path $work "c") -B (Join-Path $work "c\build") -G "Visual Studio "-DVCPKG_INSTALLED_DIR=$consumerVcpkgInstalled" ` "-DVCPKG_TARGET_TRIPLET=x64-windows" ` "-DCMAKE_PREFIX_PATH=$prefix" +Assert-NativeCommandSucceeded $LASTEXITCODE "consumer_c configure" cmake --build (Join-Path $work "c\build") --config $env:BUILD_TYPE +Assert-NativeCommandSucceeded $LASTEXITCODE "consumer_c build" cmake -S (Join-Path $work "d") -B (Join-Path $work "d\build") -G "Visual Studio 17 2022" -A x64 ` "-DCMAKE_TOOLCHAIN_FILE=$toolchain" ` @@ -141,7 +154,9 @@ cmake -S (Join-Path $work "d") -B (Join-Path $work "d\build") -G "Visual Studio "-DVCPKG_INSTALLED_DIR=$consumerVcpkgInstalled" ` "-DVCPKG_TARGET_TRIPLET=x64-windows" ` "-DCMAKE_PREFIX_PATH=$prefix" +Assert-NativeCommandSucceeded $LASTEXITCODE "consumer_d configure" cmake --build (Join-Path $work "d\build") --config $env:BUILD_TYPE +Assert-NativeCommandSucceeded $LASTEXITCODE "consumer_d build" if (-not (Select-String -Path (Join-Path $work "a\configure.log") -Pattern "Gecode_VERSION=" -Quiet)) { throw "Gecode_VERSION was not reported during consumer configure." diff --git a/.github/ci/cmake-consumer-smoke.ps1 b/.github/ci/cmake-consumer-smoke.ps1 index b87b14941f..24cd4c3ccb 100644 --- a/.github/ci/cmake-consumer-smoke.ps1 +++ b/.github/ci/cmake-consumer-smoke.ps1 @@ -1,5 +1,15 @@ $ErrorActionPreference = "Stop" +function Assert-NativeCommandSucceeded { + param( + [int] $ExitCode, + [string] $Description + ) + if ($ExitCode -ne 0) { + throw "$Description failed with exit code $ExitCode." + } +} + $prefix = $args[0] if (-not $prefix) { throw "usage: cmake-consumer-smoke.ps1 " @@ -7,7 +17,11 @@ if (-not $prefix) { $work = Join-Path $env:RUNNER_TEMP "gecode-consumers-win" if (Test-Path $work) { Remove-Item -Recurse -Force $work } -New-Item -ItemType Directory -Force -Path (Join-Path $work "a"), (Join-Path $work "b"), (Join-Path $work "c") | Out-Null +New-Item -ItemType Directory -Force -Path ` + (Join-Path $work "a"), ` + (Join-Path $work "b"), ` + (Join-Path $work "c"), ` + (Join-Path $work "manual") | Out-Null @' cmake_minimum_required(VERSION 3.21) @@ -52,14 +66,56 @@ int main(void) { } '@ | Set-Content -Encoding utf8 (Join-Path $work "c\main.cpp") +@' +cmake_minimum_required(VERSION 3.21) +project(manual_consumer LANGUAGES CXX) +set(GECODE_PREFIX "" CACHE PATH "Installed Gecode prefix") +find_path(GECODE_INCLUDE_DIR + NAMES gecode/support/config.hpp + PATHS "${GECODE_PREFIX}/include" + NO_DEFAULT_PATH + REQUIRED) +find_library(GECODE_SUPPORT_LIBRARY + NAMES gecodesupport + PATHS "${GECODE_PREFIX}/lib" + NO_DEFAULT_PATH + REQUIRED) +add_executable(manual_consumer main.cpp) +target_include_directories(manual_consumer PRIVATE "${GECODE_INCLUDE_DIR}") +target_link_libraries(manual_consumer PRIVATE "${GECODE_SUPPORT_LIBRARY}") +'@ | Set-Content -Encoding utf8 (Join-Path $work "manual\CMakeLists.txt") +@' +#include +#ifndef GECODE_NO_AUTOLINK +#error "CMake-installed headers must disable legacy MSVC auto-linking" +#endif +int main(void) { + (void) Gecode::Support::hwrnd(); + return 0; +} +'@ | Set-Content -Encoding utf8 (Join-Path $work "manual\main.cpp") + cmake -S (Join-Path $work "a") -B (Join-Path $work "a\build") -G "Visual Studio 17 2022" -A x64 "-DCMAKE_PREFIX_PATH=$prefix" 2>&1 | Tee-Object -FilePath (Join-Path $work "a\configure.log") +Assert-NativeCommandSucceeded $LASTEXITCODE "consumer_a configure" cmake --build (Join-Path $work "a\build") --config $env:BUILD_TYPE +Assert-NativeCommandSucceeded $LASTEXITCODE "consumer_a build" cmake -S (Join-Path $work "b") -B (Join-Path $work "b\build") -G "Visual Studio 17 2022" -A x64 "-DCMAKE_PREFIX_PATH=$prefix" +Assert-NativeCommandSucceeded $LASTEXITCODE "consumer_b configure" cmake --build (Join-Path $work "b\build") --config $env:BUILD_TYPE +Assert-NativeCommandSucceeded $LASTEXITCODE "consumer_b build" cmake -S (Join-Path $work "c") -B (Join-Path $work "c\build") -G "Visual Studio 17 2022" -A x64 "-DCMAKE_PREFIX_PATH=$prefix" +Assert-NativeCommandSucceeded $LASTEXITCODE "consumer_c configure" cmake --build (Join-Path $work "c\build") --config $env:BUILD_TYPE +Assert-NativeCommandSucceeded $LASTEXITCODE "consumer_c build" + +# Exercise the installed headers and library without loading the CMake package +# or inheriting compile definitions from an exported target. +cmake -S (Join-Path $work "manual") -B (Join-Path $work "manual\build") -G "Visual Studio 17 2022" -A x64 "-DGECODE_PREFIX=$prefix" +Assert-NativeCommandSucceeded $LASTEXITCODE "manual_consumer configure" +cmake --build (Join-Path $work "manual\build") --config $env:BUILD_TYPE +Assert-NativeCommandSucceeded $LASTEXITCODE "manual_consumer build" if (-not (Select-String -Path (Join-Path $work "a\configure.log") -Pattern "Gecode_VERSION=" -Quiet)) { throw "Gecode_VERSION was not reported during consumer configure." diff --git a/.github/ci/cmake-consumer-smoke.sh b/.github/ci/cmake-consumer-smoke.sh index 7a48fb00b5..c9488fb8ea 100644 --- a/.github/ci/cmake-consumer-smoke.sh +++ b/.github/ci/cmake-consumer-smoke.sh @@ -20,12 +20,16 @@ esac work="$RUNNER_TEMP/gecode-consumers" rm -rf "$work" mkdir -p "$work/a" "$work/b" "$work/c" "$work/d/cmake" "$work/e/cmake" "$work/e/stale" \ + "$work/f" "$work/g" "$work/h" "$work/i" "$work/j" "$work/k" \ "$work/mpfr-prefix/lib/cmake/MPFR" cat > "$work/a/CMakeLists.txt" <<'EOF' cmake_minimum_required(VERSION 3.21) project(consumer_a LANGUAGES CXX) find_package(Gecode CONFIG REQUIRED) +if(NOT Gecode_LIBRARIES STREQUAL "Gecode::gecode") + message(FATAL_ERROR "Expected aggregate Gecode_LIBRARIES, got: ${Gecode_LIBRARIES}") +endif() message(STATUS "Gecode_VERSION=${Gecode_VERSION}") message(STATUS "Gecode_INCLUDE_DIRS=${Gecode_INCLUDE_DIRS}") set(EXPECTED_GECODE_INCLUDE_DIR "" CACHE PATH "Expected Gecode include directory") @@ -58,6 +62,9 @@ cat > "$work/b/CMakeLists.txt" <<'EOF' cmake_minimum_required(VERSION 3.21) project(consumer_b LANGUAGES CXX) find_package(Gecode CONFIG REQUIRED COMPONENTS driver) +if(NOT Gecode_LIBRARIES STREQUAL "Gecode::gecodedriver") + message(FATAL_ERROR "Expected driver-only Gecode_LIBRARIES, got: ${Gecode_LIBRARIES}") +endif() add_executable(consumer_b main.cpp) target_link_libraries(consumer_b PRIVATE Gecode::gecodedriver) EOF @@ -73,6 +80,9 @@ cat > "$work/c/CMakeLists.txt" <<'EOF' cmake_minimum_required(VERSION 3.21) project(consumer_c LANGUAGES CXX) find_package(Gecode CONFIG REQUIRED COMPONENTS gecodedriver) +if(NOT Gecode_LIBRARIES STREQUAL "Gecode::gecodedriver") + message(FATAL_ERROR "Expected canonical library for legacy component, got: ${Gecode_LIBRARIES}") +endif() add_executable(consumer_c main.cpp) target_link_libraries(consumer_c PRIVATE Gecode::gecodedriver) EOF @@ -89,6 +99,9 @@ cmake_minimum_required(VERSION 3.21) project(consumer_d LANGUAGES CXX) list(PREPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/cmake") find_package(Gecode CONFIG REQUIRED COMPONENTS float) +if(NOT Gecode_LIBRARIES STREQUAL "Gecode::gecodefloat") + message(FATAL_ERROR "Expected float-only Gecode_LIBRARIES, got: ${Gecode_LIBRARIES}") +endif() if(NOT TARGET MPFR::MPFR) message(FATAL_ERROR "Expected Gecode package to provide MPFR::MPFR") endif() @@ -103,7 +116,7 @@ if(NOT gecodefloat_links MATCHES "MPFR::MPFR") message(FATAL_ERROR "Expected gecodefloat to link MPFR::MPFR, got: ${gecodefloat_links}") endif() add_executable(consumer_d main.cpp) -target_link_libraries(consumer_d PRIVATE Gecode::gecode) +target_link_libraries(consumer_d PRIVATE ${Gecode_LIBRARIES}) EOF cat > "$work/d/main.cpp" <<'EOF' #include @@ -155,6 +168,82 @@ set(MPFR_INCLUDE_DIRS "/bad/prior/mpfr/include") set(MPFR_LIBRARIES "/bad/prior/mpfr/lib/libmpfr.a") EOF +cat > "$work/f/CMakeLists.txt" <<'EOF' +cmake_minimum_required(VERSION 3.21) +project(consumer_f LANGUAGES CXX) +find_package(Gecode CONFIG REQUIRED COMPONENTS support) +if(NOT Gecode_LIBRARIES STREQUAL "Gecode::gecodesupport") + message(FATAL_ERROR "Expected support-only Gecode_LIBRARIES, got: ${Gecode_LIBRARIES}") +endif() +add_executable(consumer_f main.cpp) +target_link_libraries(consumer_f PRIVATE ${Gecode_LIBRARIES}) +EOF +cat > "$work/f/main.cpp" <<'EOF' +#include +int main(void) { return Gecode::Support::Thread::npu() > 0 ? 0 : 1; } +EOF + +cat > "$work/g/CMakeLists.txt" <<'EOF' +cmake_minimum_required(VERSION 3.21) +project(consumer_g LANGUAGES CXX) +find_package(Gecode CONFIG REQUIRED COMPONENTS gecodesupport) +if(NOT Gecode_LIBRARIES STREQUAL "Gecode::gecodesupport") + message(FATAL_ERROR "Expected canonical library for legacy component, got: ${Gecode_LIBRARIES}") +endif() +add_executable(consumer_g main.cpp) +target_link_libraries(consumer_g PRIVATE ${Gecode_LIBRARIES}) +EOF +cat > "$work/g/main.cpp" <<'EOF' +#include +int main(void) { return 0; } +EOF + +cat > "$work/h/CMakeLists.txt" <<'EOF' +cmake_minimum_required(VERSION 3.21) +project(consumer_h LANGUAGES CXX) +find_package(Gecode CONFIG REQUIRED COMPONENTS unknown_component) +EOF + +cat > "$work/i/CMakeLists.txt" <<'EOF' +cmake_minimum_required(VERSION 3.21) +project(consumer_i LANGUAGES CXX) +find_package(Gecode CONFIG REQUIRED COMPONENTS float) +EOF + +cat > "$work/j/CMakeLists.txt" <<'EOF' +cmake_minimum_required(VERSION 3.21) +project(consumer_j LANGUAGES CXX) +find_package(Gecode CONFIG REQUIRED COMPONENTS gist) +if(NOT Gecode_LIBRARIES STREQUAL "Gecode::gecodegist") + message(FATAL_ERROR "Expected gist-only Gecode_LIBRARIES, got: ${Gecode_LIBRARIES}") +endif() +add_executable(consumer_j main.cpp) +target_link_libraries(consumer_j PRIVATE ${Gecode_LIBRARIES}) +EOF +cat > "$work/j/main.cpp" <<'EOF' +#include +int main(void) { return 0; } +EOF + +cat > "$work/k/CMakeLists.txt" <<'EOF' +cmake_minimum_required(VERSION 3.21) +project(consumer_k LANGUAGES CXX) +find_package(Gecode CONFIG REQUIRED COMPONENTS support) +find_package(Gecode CONFIG REQUIRED COMPONENTS float) +if(NOT TARGET MPFR::MPFR) + message(FATAL_ERROR "Expected the second component request to resolve MPFR") +endif() +if(NOT Gecode_LIBRARIES STREQUAL "Gecode::gecodefloat") + message(FATAL_ERROR "Expected the second request to replace Gecode_LIBRARIES, got: ${Gecode_LIBRARIES}") +endif() +add_executable(consumer_k main.cpp) +target_link_libraries(consumer_k PRIVATE ${Gecode_LIBRARIES}) +EOF +cat > "$work/k/main.cpp" <<'EOF' +#include +int main(void) { Gecode::Float::Rounding rounding; return rounding.sqrt_down(4.0) == 2.0 ? 0 : 1; } +EOF + cat > "$work/mpfr-prefix/lib/cmake/MPFR/MPFRConfig.cmake" <<'EOF' get_filename_component(PACKAGE_PREFIX_DIR "${CMAKE_CURRENT_LIST_DIR}/../../.." ABSOLUTE) if(NOT TARGET MPFR::MPFR) @@ -182,16 +271,66 @@ cmake --build "$work/b/build" -j4 cmake -S "$work/c" -B "$work/c/build" -DCMAKE_PREFIX_PATH="$prefix" cmake --build "$work/c/build" -j4 +# A support-only consumer must not discover dependencies belonging exclusively +# to Float, FlatZinc, or Gist, even when those targets exist in the package. +cmake -S "$work/f" -B "$work/f/build" \ + -DCMAKE_PREFIX_PATH="$prefix" \ + -DCMAKE_DISABLE_FIND_PACKAGE_MPFR=TRUE \ + -DCMAKE_DISABLE_FIND_PACKAGE_Qt5=TRUE \ + -DCMAKE_DISABLE_FIND_PACKAGE_Qt6=TRUE +cmake --build "$work/f/build" -j4 + +cmake -S "$work/g" -B "$work/g/build" \ + -DCMAKE_PREFIX_PATH="$prefix" \ + -DCMAKE_DISABLE_FIND_PACKAGE_MPFR=TRUE \ + -DCMAKE_DISABLE_FIND_PACKAGE_Qt5=TRUE \ + -DCMAKE_DISABLE_FIND_PACKAGE_Qt6=TRUE +cmake --build "$work/g/build" -j4 + +if cmake -S "$work/h" -B "$work/h/build" -DCMAKE_PREFIX_PATH="$prefix"; then + echo "Unknown required Gecode component unexpectedly succeeded" >&2 + exit 1 +fi + if [ "$mode" = "mpfr" ]; then cmake -S "$work/d" -B "$work/d/build" -DCMAKE_PREFIX_PATH="$prefix" cmake --build "$work/d/build" -j4 cmake -S "$work/e" -B "$work/e/build" -DCMAKE_PREFIX_PATH="$prefix" cmake --build "$work/e/build" -j4 + + cmake -S "$work/k" -B "$work/k/build" -DCMAKE_PREFIX_PATH="$prefix" + cmake --build "$work/k/build" -j4 + + if cmake -S "$work/i" -B "$work/i/build" \ + -DCMAKE_PREFIX_PATH="$prefix" \ + -DCMAKE_DISABLE_FIND_PACKAGE_MPFR=TRUE; then + echo "Float component unexpectedly succeeded without MPFR" >&2 + exit 1 + fi +fi + +targets_file="$(find "$prefix" -name GecodeTargets.cmake -print -quit)" +if grep -q 'add_library(Gecode::gecodegist ' "$targets_file"; then + if cmake -S "$work/j" -B "$work/j/no-qt-build" \ + -DCMAKE_PREFIX_PATH="$prefix" \ + -DCMAKE_DISABLE_FIND_PACKAGE_Qt5=TRUE \ + -DCMAKE_DISABLE_FIND_PACKAGE_Qt6=TRUE; then + echo "Gist component unexpectedly succeeded without Qt" >&2 + exit 1 + fi + cmake -S "$work/j" -B "$work/j/build" -DCMAKE_PREFIX_PATH="$prefix" + cmake --build "$work/j/build" -j4 +else + if cmake -S "$work/j" -B "$work/j/missing-build" -DCMAKE_PREFIX_PATH="$prefix"; then + echo "Missing required Gist component unexpectedly succeeded" >&2 + exit 1 + fi fi grep -q "Gecode_VERSION=" "$work/a/configure.log" grep -q "Gecode_INCLUDE_DIRS=$expected_include" "$work/a/configure.log" +grep -q '^#define GECODE_NO_AUTOLINK 1$' "$expected_include/gecode/support/config.hpp" if [ "$mode" = "mpfr" ]; then grep -q '^#define GECODE_HAS_MPFR /\*\*/' "$expected_include/gecode/support/config.hpp" fi diff --git a/.github/ci/cmake-custom-vis-smoke.sh b/.github/ci/cmake-custom-vis-smoke.sh new file mode 100755 index 0000000000..0dec437ec5 --- /dev/null +++ b/.github/ci/cmake-custom-vis-smoke.sh @@ -0,0 +1,136 @@ +#!/usr/bin/env bash +set -euxo pipefail + +repo_root="$(git rev-parse --show-toplevel)" +temp_root="${RUNNER_TEMP:-${TMPDIR:-/tmp}}" +work_parent="" +fixture_dir="" + +cleanup() { + if [[ -n "$fixture_dir" && + "$fixture_dir" == "$repo_root"/.cmake-custom-vis-smoke.* ]]; then + rm -rf "$fixture_dir" + fi + if [[ -n "$work_parent" && + "$work_parent" == "$temp_root"/gecode-cmake-custom-vis-smoke.* ]]; then + rm -rf "$work_parent" + fi +} +trap cleanup EXIT + +work_parent="$(mktemp -d "$temp_root/gecode-cmake-custom-vis-smoke.XXXXXX")" +fixture_dir="$(mktemp -d "$repo_root/.cmake-custom-vis-smoke.XXXXXX")" +fixture_relative="${fixture_dir#"$repo_root"/}" +build_dir="$work_parent/build" +mkdir -p "$fixture_dir/custom vis" + +awk ' + /^Name:[[:space:]]*Int$/ { print "Name: CustomOne"; next } + /^Namespace:[[:space:]]*Gecode::Int$/ { print "Namespace: Gecode::CustomOne"; next } + /^Ifdef:[[:space:]]*GECODE_HAS_INT_VARS$/ { next } + { print } +' "$repo_root/gecode/int/var-imp/int.vis" > "$fixture_dir/custom vis/custom one.vis" + +awk ' + /^Name:[[:space:]]*Int$/ { print "Name: CustomTwo"; next } + /^Namespace:[[:space:]]*Gecode::Int$/ { print "Namespace: Gecode::CustomTwo"; next } + /^Ifdef:[[:space:]]*GECODE_HAS_INT_VARS$/ { next } + { print } +' "$repo_root/gecode/int/var-imp/int.vis" > "$fixture_dir/custom-two.vis" + +source_type_hash="$(cmake -E sha256sum "$repo_root/gecode/kernel/var-type.hpp")" +source_imp_hash="$(cmake -E sha256sum "$repo_root/gecode/kernel/var-imp.hpp")" + +cmake -S "$repo_root" -B "$build_dir" -G Ninja \ + -DGECODE_WITH_VIS="$fixture_relative/custom vis/custom one.vis,$fixture_dir/custom-two.vis" \ + -DGECODE_REGENERATE_VARIMP=OFF \ + -DGECODE_ENABLE_EXAMPLES=OFF \ + -DGECODE_ENABLE_QT=OFF \ + -DGECODE_ENABLE_GIST=OFF \ + -DBUILD_TESTING=OFF + +grep -q '^GECODE_REGENERATE_VARIMP:BOOL=OFF$' "$build_dir/CMakeCache.txt" +cmake --build "$build_dir" --target gecode-varimp-gen -v + +type_header="$build_dir/gecode/kernel/var-type.hpp" +imp_header="$build_dir/gecode/kernel/var-imp.hpp" +grep -q 'CustomOneVarImpConf' "$type_header" +grep -q 'CustomTwoVarImpConf' "$type_header" +grep -q 'CustomOneVarImp' "$imp_header" +grep -q 'CustomTwoVarImp' "$imp_header" + +custom_one_line="$(grep -n -m1 'class CustomOneVarImpConf' "$type_header" | cut -d: -f1)" +custom_two_line="$(grep -n -m1 'class CustomTwoVarImpConf' "$type_header" | cut -d: -f1)" +int_line="$(grep -n -m1 'class IntVarImpConf' "$type_header" | cut -d: -f1)" +bool_line="$(grep -n -m1 'class BoolVarImpConf' "$type_header" | cut -d: -f1)" +set_line="$(grep -n -m1 'class SetVarImpConf' "$type_header" | cut -d: -f1)" +float_line="$(grep -n -m1 'class FloatVarImpConf' "$type_header" | cut -d: -f1)" +test "$int_line" -lt "$bool_line" +test "$bool_line" -lt "$set_line" +test "$set_line" -lt "$float_line" +test "$float_line" -lt "$custom_one_line" +test "$custom_one_line" -lt "$custom_two_line" + +mtime() { + if stat -c '%Y' "$1" >/dev/null 2>&1; then + stat -c '%Y' "$1" + else + stat -f '%m' "$1" + fi +} +type_mtime="$(mtime "$type_header")" +imp_mtime="$(mtime "$imp_header")" +cmake -E sleep 1 +printf '\n' >> "$fixture_dir/custom vis/custom one.vis" +cmake --build "$build_dir" --target gecode-varimp-gen -v 2>&1 | tee "$work_parent/rebuild.log" +test "$(mtime "$type_header")" -gt "$type_mtime" +test "$(mtime "$imp_header")" -gt "$imp_mtime" +grep -q 'GenerateVarImp.cmake' "$work_parent/rebuild.log" + +# CMake list syntax is supported in addition to the command-line comma form. +cmake -S "$repo_root" -B "$work_parent/list-build" -G Ninja \ + -DGECODE_WITH_VIS="$fixture_relative/custom vis/custom one.vis;$fixture_dir/custom-two.vis" \ + -DGECODE_ENABLE_EXAMPLES=OFF \ + -DGECODE_ENABLE_QT=OFF \ + -DGECODE_ENABLE_GIST=OFF \ + -DBUILD_TESTING=OFF +cmake --build "$work_parent/list-build" --target gecode-varimp-gen + +if cmake -S "$repo_root" -B "$work_parent/missing-build" -G Ninja \ + -DGECODE_WITH_VIS="$fixture_dir/does-not-exist.vis" \ + -DGECODE_ENABLE_EXAMPLES=OFF -DBUILD_TESTING=OFF \ + >"$work_parent/missing.log" 2>&1; then + echo "Missing GECODE_WITH_VIS file unexpectedly configured" >&2 + exit 1 +fi +grep -q "does not exist" "$work_parent/missing.log" +grep -q "does-not-exist.vis" "$work_parent/missing.log" + +if cmake -S "$repo_root" -B "$work_parent/directory-build" -G Ninja \ + -DGECODE_WITH_VIS="$fixture_dir/custom vis" \ + -DGECODE_ENABLE_EXAMPLES=OFF -DBUILD_TESTING=OFF \ + >"$work_parent/directory.log" 2>&1; then + echo "Directory GECODE_WITH_VIS entry unexpectedly configured" >&2 + exit 1 +fi +grep -q "directory; expected a .vis file" "$work_parent/directory.log" + +test "$source_type_hash" = "$(cmake -E sha256sum "$repo_root/gecode/kernel/var-type.hpp")" +test "$source_imp_hash" = "$(cmake -E sha256sum "$repo_root/gecode/kernel/var-imp.hpp")" + +# Clearing the custom list restores the user's cached OFF choice and makes the +# generation target a no-op in the same build tree. +cmake -S "$repo_root" -B "$build_dir" -G Ninja \ + -DGECODE_WITH_VIS= \ + -DGECODE_REGENERATE_VARIMP=OFF \ + -DGECODE_ENABLE_EXAMPLES=OFF \ + -DGECODE_ENABLE_QT=OFF \ + -DGECODE_ENABLE_GIST=OFF \ + -DBUILD_TESTING=OFF +grep -q '^GECODE_REGENERATE_VARIMP:BOOL=OFF$' "$build_dir/CMakeCache.txt" +rm -f "$type_header" "$imp_header" +cmake --build "$build_dir" --target gecode-varimp-gen -v \ + >"$work_parent/cleared.log" 2>&1 +test ! -e "$type_header" +test ! -e "$imp_header" +grep -q 'no work to do' "$work_parent/cleared.log" diff --git a/.github/ci/cmake-package-version-checks.sh b/.github/ci/cmake-package-version-checks.sh new file mode 100755 index 0000000000..396502d0ba --- /dev/null +++ b/.github/ci/cmake-package-version-checks.sh @@ -0,0 +1,129 @@ +#!/usr/bin/env bash +set -euo pipefail + +prefix="${1:?usage: cmake-package-version-checks.sh }" +temp_root="${RUNNER_TEMP:-${TMPDIR:-/tmp}}" +if [ ! -d "$temp_root" ]; then + echo "Temporary root is not a directory: $temp_root" >&2 + exit 1 +fi + +work= +workspace= +cleanup() { + if [ -n "$work" ]; then + rm -rf -- "$work" + fi +} +trap cleanup EXIT + +# Record the cleanup boundary before creating anything inside it. In +# particular, a signal delivered after mktemp creates its directory but before +# command substitution assigns its output can still remove this known parent. +# Ignore termination signals only while ownership of the parent is being +# established. This prevents cleanup from deleting a colliding path and also +# prevents a signal between mkdir and assignment from leaking a directory we +# just created. +trap '' HUP INT TERM +umask 077 +for attempt in {1..100}; do + candidate="$temp_root/gecode-package-version-checks.${BASHPID:-$$}.$RANDOM.$RANDOM.$attempt" + if mkdir -m 700 "$candidate"; then + work=$candidate + break + fi +done +unset candidate +trap 'exit 1' HUP INT TERM +if [ -z "$work" ]; then + echo "Could not create a private package-version workspace below $temp_root" >&2 + exit 1 +fi +workspace=$(mktemp -d "$work/run.XXXXXX") +config_version=$(find "$prefix" -type f -name GecodeConfigVersion.cmake -print -quit) + +if [ -z "$config_version" ]; then + echo "GecodeConfigVersion.cmake not found below $prefix" >&2 + exit 1 +fi + +config_dir=$(dirname "$config_version") +version=$(sed -n 's/^set(PACKAGE_VERSION "\([0-9][0-9.]*\)")$/\1/p' "$config_version" | head -n 1) +IFS=. read -r major minor patch <<<"$version" + +if [ -z "${major:-}" ] || [ -z "${minor:-}" ] || [ -z "${patch:-}" ]; then + echo "Could not read a three-part package version from $config_version" >&2 + exit 1 +fi + +previous_minor=$((minor - 1)) +next_minor=$((minor + 1)) +next_patch=$((patch + 1)) + +mkdir -p "$workspace/source" + +cat >"$workspace/source/CMakeLists.txt" <<'EOF' +cmake_minimum_required(VERSION 3.21) +project(GecodePackageVersionCheck LANGUAGES CXX) + +if(REQUEST_MODE STREQUAL "NONE") + find_package(Gecode CONFIG REQUIRED) +elseif(REQUEST_MODE STREQUAL "EXACT") + find_package(Gecode ${REQUEST_VERSION} EXACT CONFIG REQUIRED) +else() + find_package(Gecode ${REQUEST_VERSION} CONFIG REQUIRED) +endif() +EOF + +configure_case() { + name=$1 + package_dir=$2 + mode=$3 + requested=${4:-} + if [ -n "$requested" ]; then + cmake -S "$workspace/source" -B "$workspace/$name" \ + -DGecode_DIR="$package_dir" \ + -DREQUEST_MODE="$mode" \ + -DREQUEST_VERSION="$requested" + else + cmake -S "$workspace/source" -B "$workspace/$name" \ + -DGecode_DIR="$package_dir" \ + -DREQUEST_MODE="$mode" + fi +} + +check_accepts() { + if ! configure_case "$@"; then + echo "Gecode package unexpectedly rejected the $1 case" >&2 + exit 1 + fi +} + +check_rejects() { + name=$1 + requested=$2 + if configure_case "$name" "$config_dir" NORMAL "$requested"; then + echo "Gecode $version unexpectedly satisfied request $requested" >&2 + exit 1 + fi +} + +check_accepts no-version "$config_dir" NONE +check_accepts exact-version "$config_dir" EXACT "$version" +check_rejects previous-minor "$major.$previous_minor.0" +check_rejects next-minor "$major.$next_minor.0" +check_rejects newer-patch "$major.$minor.$next_patch" + +# A newer patch release in the same minor line must satisfy this package's +# version, while still rejecting an exact request for the older patch. +mkdir -p "$workspace/newer-patch-package" +cat >"$workspace/newer-patch-package/GecodeConfig.cmake" <"$workspace/newer-patch-package/GecodeConfigVersion.cmake" +check_accepts older-patch-request "$workspace/newer-patch-package" NORMAL "$version" +if configure_case exact-older-patch-request "$workspace/newer-patch-package" EXACT "$version"; then + echo "A newer patch unexpectedly satisfied an EXACT request for $version" >&2 + exit 1 +fi diff --git a/.github/ci/custom-vis-smoke.sh b/.github/ci/custom-vis-smoke.sh index b9f0a85e4f..19dab14b8a 100644 --- a/.github/ci/custom-vis-smoke.sh +++ b/.github/ci/custom-vis-smoke.sh @@ -19,23 +19,35 @@ cd "$work" touch configure awk ' - /^Name:[[:space:]]*Int$/ { print "Name: Custom"; next } - /^Namespace:[[:space:]]*Gecode::Int$/ { print "Namespace: Gecode::Custom"; next } + /^Name:[[:space:]]*Int$/ { print "Name: CustomOne"; next } + /^Namespace:[[:space:]]*Gecode::Int$/ { print "Namespace: Gecode::CustomOne"; next } /^Ifdef:[[:space:]]*GECODE_HAS_INT_VARS$/ { next } { print } -' gecode/int/var-imp/int.vis > custom.vis +' gecode/int/var-imp/int.vis > custom-one.vis + +awk ' + /^Name:[[:space:]]*Int$/ { print "Name: CustomTwo"; next } + /^Namespace:[[:space:]]*Gecode::Int$/ { print "Namespace: Gecode::CustomTwo"; next } + /^Ifdef:[[:space:]]*GECODE_HAS_INT_VARS$/ { next } + { print } +' gecode/int/var-imp/int.vis > custom-two.vis CFLAGS="-fPIC" \ CXXFLAGS="-std=c++11 -fPIC -DGECODE_MEMORY_ALIGNMENT=16" \ - ./configure \ - --with-vis="$PWD/custom.vis" \ + dash ./configure \ + --with-vis="$PWD/custom-one.vis,$PWD/custom-two.vis" \ --disable-cpprofiler \ --disable-examples \ --disable-flatzinc \ --disable-float-vars \ --disable-gist \ - --disable-qt + --disable-qt 2>&1 | tee configure.log + +! grep -Fq "unexpected operator" configure.log +grep -Eq '^export DLLSUFFIX[[:space:]]*=[[:space:]]*\.' Makefile +grep -Fq "VIS = \$(top_srcdir)/gecode/int/var-imp/int.vis \$(top_srcdir)/gecode/int/var-imp/bool.vis \$(top_srcdir)/gecode/set/var-imp/set.vis \$(top_srcdir)/gecode/float/var-imp/float.vis $PWD/custom-one.vis $PWD/custom-two.vis" Makefile rm -f gecode/kernel/var-type.hpp gecode/kernel/var-imp.hpp make -j4 -grep -q "CustomVarImpConf" gecode/kernel/var-type.hpp +grep -q "CustomOneVarImpConf" gecode/kernel/var-type.hpp +grep -q "CustomTwoVarImpConf" gecode/kernel/var-type.hpp diff --git a/.github/ci/generated-source-freshness.sh b/.github/ci/generated-source-freshness.sh new file mode 100755 index 0000000000..92cce3ae6f --- /dev/null +++ b/.github/ci/generated-source-freshness.sh @@ -0,0 +1,281 @@ +#!/usr/bin/env bash +set -euo pipefail + +if [ "$#" -gt 1 ]; then + echo "usage: $0 [--check|--update]" >&2 + exit 2 +fi +mode=check +case "${1:-}" in + ""|--check) ;; + --update) mode=update ;; + *) + echo "usage: $0 [--check|--update]" >&2 + exit 2 + ;; +esac + +repo_root="${GECODE_SOURCE_ROOT:-$(git rev-parse --show-toplevel)}" +temp_root="${RUNNER_TEMP:-${TMPDIR:-/tmp}}" + +if [ ! -d "$temp_root" ]; then + echo "Temporary root is not a directory: $temp_root" >&2 + exit 1 +fi +if [ ! -d "$repo_root/.git" ] && [ ! -f "$repo_root/.git" ]; then + echo "Source root is not a Git worktree: $repo_root" >&2 + exit 1 +fi + +cleanup_root= +autoconf_build_root= +workspace= +pending_signal= +cleanup() { + if [ -n "$autoconf_build_root" ]; then + rm -rf -- "$autoconf_build_root" + fi + if [ -n "$cleanup_root" ]; then + rm -rf -- "$cleanup_root" + fi +} +trap cleanup EXIT + +# Defer signals while ownership moves from an unowned candidate name to a +# successfully created directory. On collision, no cleanup path is recorded; +# on success, a pending signal is handled only after cleanup owns the path. +trap 'pending_signal=HUP' HUP +trap 'pending_signal=INT' INT +trap 'pending_signal=TERM' TERM + +umask 077 +for attempt in {1..100}; do + candidate="$temp_root/gecode-generated-check.${BASHPID:-$$}.$RANDOM.$RANDOM.$attempt" + if mkdir -m 700 "$candidate"; then + cleanup_root=$candidate + break + fi + if [ -n "$pending_signal" ]; then + exit 1 + fi +done +if [ -z "$cleanup_root" ]; then + echo "Could not create a private generated-file workspace below $temp_root" >&2 + exit 1 +fi +if [ -n "$pending_signal" ]; then + exit 1 +fi +trap 'exit 1' HUP INT TERM +workspace=$(mktemp -d "$cleanup_root/work.XXXXXX") +source_copy="$workspace/source" +mkdir "$source_copy" + +autoconf_version=2.72 +autoconf_expected="autoconf (GNU Autoconf) $autoconf_version" +autoconf_sha256=afb181a76e1ee72832f6581c0eddf8df032b83e2e0239ef79ebedc4467d92d6e + +prepend_brew_bin() { + formula=$1 + if ! command -v brew >/dev/null 2>&1; then + return + fi + prefix=$(brew --prefix "$formula" 2>/dev/null || true) + if [ -n "$prefix" ] && [ -d "$prefix/bin" ]; then + PATH="$prefix/bin:$PATH" + fi +} + +# Homebrew keeps bison, flex, and m4 keg-only on macOS. Make the same helper +# work from a developer shell without requiring them to edit PATH manually. +prepend_brew_bin m4 +prepend_brew_bin bison +prepend_brew_bin flex +export PATH + +autoconf_is_expected() { + command -v autoconf >/dev/null 2>&1 && + [ "$(autoconf --version | sed -n '1p')" = "$autoconf_expected" ] +} + +ensure_autoconf() { + if autoconf_is_expected; then + return + fi + + if [ -n "${GECODE_GENERATED_TOOLS_DIR:-}" ]; then + tools_root=$GECODE_GENERATED_TOOLS_DIR + elif [ -n "${RUNNER_TEMP:-}" ]; then + tools_root="$RUNNER_TEMP/gecode-generated-tools" + else + tools_root="${XDG_CACHE_HOME:-$HOME/.cache}/gecode/generated-tools" + fi + install_root="$tools_root/autoconf-$autoconf_version" + + if [ -x "$install_root/bin/autoconf" ]; then + PATH="$install_root/bin:$PATH" + export PATH + if autoconf_is_expected; then + return + fi + fi + + for tool in curl make tar; do + if ! command -v "$tool" >/dev/null 2>&1; then + echo "Cannot bootstrap Autoconf $autoconf_version: missing $tool." >&2 + exit 1 + fi + done + if ! command -v shasum >/dev/null 2>&1 && + ! command -v sha256sum >/dev/null 2>&1; then + echo "Cannot bootstrap Autoconf $autoconf_version: no SHA-256 checker is available." >&2 + exit 1 + fi + if ! command -v m4 >/dev/null 2>&1; then + echo "Cannot bootstrap Autoconf $autoconf_version: GNU m4 is unavailable." >&2 + exit 1 + fi + + mkdir -p "$tools_root" + autoconf_build_root=$(mktemp -d "$temp_root/gecode-autoconf-build.XXXXXX") + archive="$autoconf_build_root/autoconf-$autoconf_version.tar.gz" + curl --fail --silent --show-error --location --retry 3 \ + --output "$archive" \ + "https://ftp.gnu.org/gnu/autoconf/autoconf-$autoconf_version.tar.gz" + if command -v shasum >/dev/null 2>&1; then + printf '%s %s\n' "$autoconf_sha256" "$archive" | shasum -a 256 -c - + else + printf '%s %s\n' "$autoconf_sha256" "$archive" | sha256sum -c - + fi + tar -xzf "$archive" -C "$autoconf_build_root" + rm -rf -- "$install_root" + ( + cd "$autoconf_build_root/autoconf-$autoconf_version" + ./configure --prefix="$install_root" >/dev/null + make -j2 >/dev/null + make install >/dev/null + ) + PATH="$install_root/bin:$PATH" + export PATH +} + +require_version() { + tool=$1 + expected=$2 + if ! command -v "$tool" >/dev/null 2>&1; then + echo "Required generator is unavailable: $tool ($expected required)" >&2 + exit 1 + fi + actual=$($tool --version | sed -n '1p') + if [ "$actual" != "$expected" ]; then + echo "Unsupported $tool version." >&2 + echo " required: $expected" >&2 + echo " found: $actual" >&2 + echo "Install the required generator, then run 'make regenerate'." >&2 + exit 1 + fi +} + +ensure_autoconf +require_version autoconf "$autoconf_expected" +require_version bison "bison (GNU Bison) 3.8.2" +require_version flex "flex 2.6.4" +if ! command -v uv >/dev/null 2>&1; then + echo "Required generator runner is unavailable: uv" >&2 + exit 1 +fi + +# Copy the current tracked worktree content, including local tracked changes, +# without copying build products or allowing generators to touch the source. +git -C "$repo_root" ls-files -z | + tar -C "$repo_root" --null -T - -cf - | + tar -C "$source_copy" -xf - + +vis_files=( + ./gecode/int/var-imp/int.vis + ./gecode/int/var-imp/bool.vis + ./gecode/set/var-imp/set.vis + ./gecode/float/var-imp/float.vis +) + +normalize_generated_whitespace() { + output=$1 + awk ' + { + sub(/[ \t\r]+$/, "") + line[NR] = $0 + } + END { + last = NR + while ((last > 0) && (line[last] == "")) + last-- + for (i = 1; i <= last; i++) + print line[i] + } + ' "$output" > "$output.normalized" + mv "$output.normalized" "$output" +} + +( + cd "$source_copy" + # Ignore any autom4te cache so output matches a clean source release. + autoconf --force + # Autoconf 2.72 appends blank lines after the generated script body; source + # releases retain exactly one final newline. + normalize_generated_whitespace configure + uv run --script ./misc/genvarimp.py -typehpp "${vis_files[@]}" \ + > gecode/kernel/var-type.hpp + uv run --script ./misc/genvarimp.py -header "${vis_files[@]}" \ + > gecode/kernel/var-imp.hpp + bison -t -o gecode/flatzinc/parser.tab.cpp -d \ + ./gecode/flatzinc/parser.yxx + # Source releases keep generated Bison files free of whitespace-only lines + # and excess blank lines at EOF. Bison 3.8.2 preserves indentation from an + # otherwise blank grammar line and appends a blank line to its .cpp output. + for output in \ + gecode/flatzinc/parser.tab.cpp \ + gecode/flatzinc/parser.tab.hpp; do + normalize_generated_whitespace "$output" + done + flex -ogecode/flatzinc/lexer.yy.cpp ./gecode/flatzinc/lexer.lxx +) + +generated_files=( + configure + gecode/kernel/var-type.hpp + gecode/kernel/var-imp.hpp + gecode/flatzinc/parser.tab.cpp + gecode/flatzinc/parser.tab.hpp + gecode/flatzinc/lexer.yy.cpp +) + +stale=0 +for file in "${generated_files[@]}"; do + if cmp -s "$repo_root/$file" "$source_copy/$file"; then + continue + fi + if [ "$mode" = update ]; then + cp "$source_copy/$file" "$repo_root/$file" + echo "Updated generated source: $file" + continue + fi + stale=1 + echo "Generated source is stale: $file" >&2 + diff -u \ + --label "tracked/$file" \ + --label "regenerated/$file" \ + "$repo_root/$file" "$source_copy/$file" >&2 || true +done + +if [ "$mode" = update ]; then + echo "Generated source regeneration completed" + exit 0 +fi + +if [ "$stale" -ne 0 ]; then + echo "Regenerate with Autoconf 2.72, GNU Bison 3.8.2, GNU Flex 2.6.4, and uv, then commit the reviewed outputs." >&2 + echo "Run 'make regenerate' to use the canonical generator workflow." >&2 + exit 1 +fi + +echo "Generated source freshness check passed" diff --git a/.github/ci/gist-static-macro-smoke.sh b/.github/ci/gist-static-macro-smoke.sh new file mode 100755 index 0000000000..5207f674de --- /dev/null +++ b/.github/ci/gist-static-macro-smoke.sh @@ -0,0 +1,55 @@ +#!/usr/bin/env bash +set -euo pipefail + +repo_root="$(git rev-parse --show-toplevel)" +temp_root="${RUNNER_TEMP:-${TMPDIR:-/tmp}}" +work="$(mktemp -d "$temp_root/gecode-gist-static-macro.XXXXXX")" +trap 'rm -rf -- "$work"' EXIT + +mkdir -p "$work/stubs/gecode/gist" "$work/stubs/gecode" +touch \ + "$work/stubs/gecode/kernel.hh" \ + "$work/stubs/gecode/search.hh" \ + "$work/stubs/gecode/int.hh" \ + "$work/stubs/gecode/set.hh" \ + "$work/stubs/gecode/float.hh" \ + "$work/stubs/gecode/gist/treecanvas.hh" \ + "$work/stubs/gecode/gist/nodestats.hh" + +cxx="${CXX:-c++}" + +dump_macros() { + header=$1 + platform=$2 + shift 2 + "$cxx" -E -dM -x c++ \ + -I"$work/stubs" -I"$repo_root" \ + "$platform" -DGECODE_NO_AUTOLINK "$@" \ + -include "$header" /dev/null +} + +check_header() { + header=$1 + platform=$2 + + legacy="$(dump_macros "$header" "$platform" -DGIST_STATIC_LIBS)" + grep -Eq '^#define GECODE_GIST_EXPORT[[:space:]]*$' <<<"$legacy" + ! grep -Eq '^#define GECODE_STATIC_LIBS([[:space:]]|$)' <<<"$legacy" + + current="$(dump_macros "$header" "$platform" -DGECODE_STATIC_LIBS)" + grep -Eq '^#define GECODE_GIST_EXPORT[[:space:]]*$' <<<"$current" + ! grep -Eq '^#define GIST_STATIC_LIBS([[:space:]]|$)' <<<"$current" + + shared="$(dump_macros "$header" "$platform")" + grep -Eq '^#define GECODE_GIST_EXPORT __declspec\([[:space:]]*dllimport[[:space:]]*\)$' \ + <<<"$shared" + + building="$(dump_macros "$header" "$platform" -DGECODE_BUILD_GIST)" + grep -Eq '^#define GECODE_GIST_EXPORT __declspec\([[:space:]]*dllexport[[:space:]]*\)$' \ + <<<"$building" +} + +for header in gecode/gist.hh gecode/gist/qtgist.hh; do + check_header "$header" -D__MINGW32__ + check_header "$header" -D_MSC_VER=1930 +done diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 14eaf6e57a..58922c9c9b 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -15,6 +15,21 @@ env: BUILD_TYPE: Release jobs: + tidy: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 + + - name: Install uv + uses: astral-sh/setup-uv@fac544c07dec837d0ccb6301d7b5580bf5edae39 + with: + enable-cache: false + + - name: Tidy checks + shell: bash + run: uv run misc/tidy.py --check + build-autoconf: runs-on: ${{ matrix.os }} strategy: @@ -36,7 +51,19 @@ jobs: sudo apt-get update sudo apt-get install -y flex bison libgmp-dev libmpfr-dev - - name: Stabilize generated configure script timestamp + - name: Install generated-source tools + if: runner.os == 'macOS' + shell: bash + run: | + # The freshness helper bootstraps the pinned Autoconf version itself. + brew install m4 bison flex + + - name: Check generated source freshness + if: runner.os == 'macOS' + shell: bash + run: bash .github/ci/generated-source-freshness.sh + + - name: Avoid redundant configure regeneration during build shell: bash run: touch configure @@ -56,10 +83,6 @@ jobs: shell: bash run: bash .github/ci/autoconf-install-smoke.sh "$RUNNER_TEMP/gecode-autoconf-install" - - name: Generated Autoconf files stay clean - shell: bash - run: git diff --exit-code -- configure gecode/kernel/var-type.hpp gecode/kernel/var-imp.hpp - - name: Custom VIS dependency smoke if: runner.os == 'Linux' shell: bash @@ -121,7 +144,6 @@ jobs: run: | set -euxo pipefail cmake --build . --config $BUILD_TYPE --target check - ctest --output-on-failure -R '^test$' - name: Install CMake package working-directory: ${{github.workspace}}/build @@ -144,11 +166,25 @@ jobs: core fi + - name: Package version checks + shell: bash + run: bash .github/ci/cmake-package-version-checks.sh "$GITHUB_WORKSPACE/install" + - name: Disabled module closure checks if: matrix.os == 'ubuntu-latest' shell: bash run: bash .github/ci/cmake-closure-checks.sh + - name: CMake custom VIS checks + if: matrix.os == 'ubuntu-latest' + shell: bash + run: bash .github/ci/cmake-custom-vis-smoke.sh + + - name: Gist static macro compatibility + if: matrix.os == 'ubuntu-latest' + shell: bash + run: bash .github/ci/gist-static-macro-smoke.sh + - name: Qt minimum version checks if: matrix.os == 'ubuntu-latest' shell: bash @@ -187,10 +223,9 @@ jobs: - name: Check shell: bash - run: | - set -euxo pipefail - cmake --build "$GITHUB_WORKSPACE/build-ninja" --target check - ctest --test-dir "$GITHUB_WORKSPACE/build-ninja" --output-on-failure -R '^test$' + run: > + ctest --test-dir "$GITHUB_WORKSPACE/build-ninja" + --output-on-failure -R '^(build-gecode-test|test)$' - name: Configure CMake Debug audit smoke shell: bash @@ -482,7 +517,7 @@ jobs: export PATH="$UV_DIR:$PATH" uv --version - - name: Stabilize generated configure script timestamp + - name: Avoid redundant configure regeneration during build shell: msys2 {0} run: touch configure diff --git a/.gitignore b/.gitignore index baa4a7fcf5..9e4fc48bb1 100644 --- a/.gitignore +++ b/.gitignore @@ -48,11 +48,64 @@ gmon.out doc GecodeReference.chm ChangeLog -examples/* -!examples/CMakeLists.txt -!examples/*.cpp -!examples/*.hpp -!examples/*.hh +# In-source example executables; source, data, and documentation stay visible. +/examples/all-interval +/examples/alpha +/examples/archimedean-spiral +/examples/bacp +/examples/bibd +/examples/bin-packing +/examples/black-hole +/examples/car-sequencing +/examples/cartesian-heart +/examples/colored-matrix +/examples/crew +/examples/crossword +/examples/crowded-chess +/examples/descartes-folium +/examples/dominating-queens +/examples/domino +/examples/donald +/examples/efpa +/examples/eq20 +/examples/golden-spiral +/examples/golf +/examples/golomb-ruler +/examples/graph-color +/examples/grocery +/examples/hamming +/examples/ind-set +/examples/job-shop +/examples/kakuro +/examples/knights +/examples/langford-number +/examples/magic-sequence +/examples/magic-square-partial +/examples/magic-square +/examples/minesweeper +/examples/money +/examples/multi-bin-packing +/examples/nonogram +/examples/open-shop +/examples/ortho-latin +/examples/partition +/examples/pentominoes +/examples/perfect-square +/examples/photo +/examples/qcp +/examples/queen-armies +/examples/queens +/examples/radiotherapy +/examples/sat +/examples/schurs-lemma +/examples/sports-league +/examples/steel-mill +/examples/steiner +/examples/sudoku-advanced +/examples/sudoku +/examples/tsp +/examples/warehouses +/examples/word-square test/test # Ignore build tree outputs (version metadata lives in top-level diff --git a/CMakeLists.txt b/CMakeLists.txt index b8cad38e5b..865522b7f0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -136,6 +136,12 @@ set(GECODE_WITH_VIS "" CACHE STRING "Additional .vis files (comma-separated)") set(GECODE_LIB_PREFIX "" CACHE STRING "User-defined prefix for generated library basenames") set(GECODE_LIB_SUFFIX "" CACHE STRING "User-defined suffix for generated library basenames") +if(NOT GECODE_WITH_VIS STREQUAL "" AND NOT GECODE_REGENERATE_VARIMP) + set(GECODE_REGENERATE_VARIMP ON) + message(STATUS + "GECODE_WITH_VIS activates GECODE_REGENERATE_VARIMP for generated headers") +endif() + # Deprecated compatibility aliases for the Gecode 6.x line. if(GECODE_USED_ENABLE_THREADS_ALIAS) set(GECODE_ENABLE_THREAD ${GECODE_ALIAS_ENABLE_THREAD_VALUE} CACHE BOOL "Enable thread support" FORCE) @@ -526,6 +532,9 @@ string(REGEX MATCHALL "[^\n]*\n" CONFIG /* Define to 1 if you have a working `mmap' system call. */ #undef HAVE_MMAP +/* Whether legacy MSVC automatic library selection is disabled. */ +#undef GECODE_NO_AUTOLINK + /* Define to the address where bug reports for this package should be sent. */ #undef PACKAGE_BUGREPORT @@ -584,6 +593,10 @@ endif() set(GECODE_DLL_USERPREFIX "${GECODE_LIB_PREFIX}") set(GECODE_DLL_USERSUFFIX "${GECODE_LIB_SUFFIX}") +# CMake consumers link explicitly, either through the exported targets or by +# naming the installed libraries. Keep the installed headers from also +# injecting the legacy, Autoconf-specific MSVC library names. +set(GECODE_NO_AUTOLINK 1) if(GECODE_ENABLE_INT_VARS) set(GECODE_HAS_INT_VARS "/**/") endif() @@ -714,7 +727,12 @@ if(GECODE_ENABLE_FLOAT_VARS) endif() if(GECODE_WITH_VIS) string(REPLACE "," ";" _extra_vis "${GECODE_WITH_VIS}") - foreach(vis ${_extra_vis}) + foreach(vis IN LISTS _extra_vis) + string(STRIP "${vis}" vis) + if(vis STREQUAL "") + message(FATAL_ERROR + "GECODE_WITH_VIS contains an empty variable implementation specification") + endif() list(APPEND GECODE_VIS_FILES "${vis}") endforeach() endif() @@ -727,19 +745,28 @@ endif() set(GECODE_VIS_DEPENDS) set(GECODE_VIS_FILES_FOR_GEN) -foreach(vis ${GECODE_VIS_FILES}) +foreach(vis IN LISTS GECODE_VIS_FILES) if(IS_ABSOLUTE "${vis}") - list(APPEND GECODE_VIS_DEPENDS "${vis}") - list(APPEND GECODE_VIS_FILES_FOR_GEN "${vis}") + set(_vis_path "${vis}") else() - list(APPEND GECODE_VIS_DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/${vis}") - if(vis MATCHES "^\\./") - list(APPEND GECODE_VIS_FILES_FOR_GEN "${vis}") - else() - list(APPEND GECODE_VIS_FILES_FOR_GEN "./${vis}") - endif() + set(_vis_path "${CMAKE_CURRENT_SOURCE_DIR}/${vis}") endif() + cmake_path(NORMAL_PATH _vis_path) + if(NOT EXISTS "${_vis_path}") + message(FATAL_ERROR + "Variable implementation specification '${vis}' does not exist " + "(resolved to '${_vis_path}')") + endif() + if(IS_DIRECTORY "${_vis_path}") + message(FATAL_ERROR + "Variable implementation specification '${vis}' is a directory; " + "expected a .vis file") + endif() + list(APPEND GECODE_VIS_DEPENDS "${_vis_path}") + list(APPEND GECODE_VIS_FILES_FOR_GEN "${_vis_path}") endforeach() +list(REMOVE_DUPLICATES GECODE_VIS_DEPENDS) +list(REMOVE_DUPLICATES GECODE_VIS_FILES_FOR_GEN) string(JOIN "||" GECODE_VIS_FILES_SERIALIZED ${GECODE_VIS_FILES_FOR_GEN}) set(GECODE_VAR_TYPE_HPP_SOURCE ${CMAKE_CURRENT_SOURCE_DIR}/gecode/kernel/var-type.hpp) @@ -1257,8 +1284,35 @@ if(BUILD_TESTING) Set::Sequence::SeqU1 Set::Wait) endif() + if(GECODE_ENABLE_FLOAT_VARS) + set(GECODE_FLOAT_CHECK_TESTS + Float::Arithmetic::PositiveNRootBounds + Float::Arithmetic::PowConsistency + Float::Arithmetic::MultZeroEndpoint + Float::Arithmetic::Pow::N::2::XY::Sol::C + Float::Arithmetic::NRoot::N::2::XY::Sol::C + Float::Arithmetic::Mult::XYZ::Sol::C) + list(APPEND GECODE_CHECK_TESTS ${GECODE_FLOAT_CHECK_TESTS}) + + # Keep the fast CI selection honest when Float regressions are added or + # the list above is edited. These names must remain registered in the + # ordinary check target rather than living only in ad-hoc test commands. + set(GECODE_REQUIRED_FLOAT_CHECK_TESTS + Float::Arithmetic::PositiveNRootBounds + Float::Arithmetic::PowConsistency + Float::Arithmetic::MultZeroEndpoint + Float::Arithmetic::Pow::N::2::XY::Sol::C + Float::Arithmetic::NRoot::N::2::XY::Sol::C + Float::Arithmetic::Mult::XYZ::Sol::C) + foreach(gecode_required_float_test ${GECODE_REQUIRED_FLOAT_CHECK_TESTS}) + if(NOT gecode_required_float_test IN_LIST GECODE_CHECK_TESTS) + message(FATAL_ERROR + "Required Float check test is missing: ${gecode_required_float_test}") + endif() + endforeach() + endif() - set(GECODE_CHECK_ARGS -iter 2 -threads 0) + set(GECODE_CHECK_ARGS -iter 2 -threads 0 -fixprob 1) foreach(gecode_check_test ${GECODE_CHECK_TESTS}) list(APPEND GECODE_CHECK_ARGS -test ${gecode_check_test}) endforeach() @@ -1272,9 +1326,20 @@ if(BUILD_TESTING) add_test(NAME test COMMAND gecode-test ${GECODE_CHECK_ARGS}) set_tests_properties(test PROPERTIES FIXTURES_REQUIRED gecode-test-built) + set(GECODE_CHECK_DEPENDS gecode-test) + if(GECODE_ENABLE_FLOAT_VARS) + add_custom_target(verify-gecode-check-tests + COMMAND ${CMAKE_COMMAND} + "-DTEST_EXECUTABLE=$" + "-DREQUIRED_TESTS=${GECODE_REQUIRED_FLOAT_CHECK_TESTS}" + -P ${CMAKE_CURRENT_SOURCE_DIR}/cmake/VerifyTestSelection.cmake + DEPENDS gecode-test + VERBATIM) + list(APPEND GECODE_CHECK_DEPENDS verify-gecode-check-tests) + endif() add_custom_target(check COMMAND $ ${GECODE_CHECK_ARGS} - DEPENDS gecode-test + DEPENDS ${GECODE_CHECK_DEPENDS} USES_TERMINAL) else() message(WARNING "Skipping gecode-test/check targets because required modules are disabled") @@ -1419,7 +1484,7 @@ if(GECODE_INSTALL) write_basic_package_version_file( ${CMAKE_CURRENT_BINARY_DIR}/GecodeConfigVersion.cmake VERSION ${GECODE_PROJECT_VERSION} - COMPATIBILITY SameMajorVersion) + COMPATIBILITY SameMinorVersion) install(EXPORT GecodeTargets FILE GecodeTargets.cmake diff --git a/Makefile.in b/Makefile.in index b9140bf348..1a0f806ccc 100755 --- a/Makefile.in +++ b/Makefile.in @@ -1303,23 +1303,55 @@ compileexamples: $(EXAMPLEEXE) test: mkcompiledirs @$(MAKE) $(VARIMP) $(TESTEXE) +CHECKTESTS = Branch::Int::Dense::3 \ + FlatZinc::magic_square \ + Int::Arithmetic::Abs \ + Int::Arithmetic::ArgMax \ + Int::Arithmetic::Max::Nary \ + Int::Cumulative::Man::Fix::0::4 \ + Int::Distinct::Random \ + Int::Linear::Bool::Int::Lq \ + Int::MiniModel::LinExpr::Bool::352 \ + NoGoods::Queens \ + Search::DFS::Sol::Binary::Nary::Binary::1::1::1 \ + Set::Dom::Dom::Gr \ + Set::RelOp::ConstSSI::Union \ + Set::Sequence::SeqU1 \ + Set::Wait + +ifeq "@enable_float_vars@" "yes" +FLOATCHECKTESTS = Float::Arithmetic::PositiveNRootBounds \ + Float::Arithmetic::PowConsistency \ + Float::Arithmetic::MultZeroEndpoint \ + Float::Arithmetic::Pow::N::2::XY::Sol::C \ + Float::Arithmetic::NRoot::N::2::XY::Sol::C \ + Float::Arithmetic::Mult::XYZ::Sol::C +CHECKTESTS += $(FLOATCHECKTESTS) +REQUIREDFLOATCHECKTESTS = Float::Arithmetic::PositiveNRootBounds \ + Float::Arithmetic::PowConsistency \ + Float::Arithmetic::MultZeroEndpoint \ + Float::Arithmetic::Pow::N::2::XY::Sol::C \ + Float::Arithmetic::NRoot::N::2::XY::Sol::C \ + Float::Arithmetic::Mult::XYZ::Sol::C +endif + # A basic integrity test check: test - $(RUNENVIRONMENT) $(TESTEXE) -iter 2 -threads 0 -test Branch::Int::Dense::3 \ - -test FlatZinc::magic_square \ - -test Int::Arithmetic::Abs \ - -test Int::Arithmetic::ArgMax \ - -test Int::Arithmetic::Max::Nary \ - -test Int::Cumulative::Man::Fix::0::4 \ - -test Int::Distinct::Random \ - -test Int::Linear::Bool::Int::Lq \ - -test Int::MiniModel::LinExpr::Bool::352 \ - -test NoGoods::Queens \ - -test Search::DFS::Sol::Binary::Nary::Binary::1::1::1 \ - -test Set::Dom::Dom::Gr \ - -test Set::RelOp::ConstSSI::Union \ - -test Set::Sequence::SeqU1 \ - -test Set::Wait + @for t in $(REQUIREDFLOATCHECKTESTS); do \ + case " $(CHECKTESTS) " in *" $$t "*) ;; \ + *) echo "Required Float check test is missing: $$t" >&2; exit 1 ;; \ + esac; \ + if ! $(RUNENVIRONMENT) $(TESTEXE) -list | grep -Fqx "$$t"; then \ + echo "Required Float check test is not registered: $$t" >&2; \ + exit 1; \ + fi; \ + done + $(RUNENVIRONMENT) $(TESTEXE) -iter 2 -threads 0 -fixprob 1 \ + $(CHECKTESTS:%=-test %) + +.PHONY: regenerate +regenerate: + bash "$(top_srcdir)/.github/ci/generated-source-freshness.sh" --update ifeq "@top_srcdir@" "." mkcompiledirs: diff --git a/README.md b/README.md index 0b3f5112eb..bf55a9e2c1 100644 --- a/README.md +++ b/README.md @@ -72,12 +72,19 @@ Version metadata shared by autoconf and CMake lives in `gecode-version.m4`. By default, CMake uses checked-in `gecode/kernel/var-type.hpp` and `gecode/kernel/var-imp.hpp`; regeneration is opt-in via -`-DGECODE_REGENERATE_VARIMP=ON`. +`-DGECODE_REGENERATE_VARIMP=ON`. A nonempty `GECODE_WITH_VIS` list enables +regeneration automatically and validates every specification during +configuration; relative paths are resolved from the source directory. Build-time script execution uses `uv run --script ...` and requires Python 3.11 or newer. Autoconf builds require `uv` on `PATH`; CMake requires it only for generated-script paths such as `-DGECODE_REGENERATE_VARIMP=ON` and the `doc` target. Documentation generation requires Doxygen 1.17.0 or newer. +When changing generated-source inputs, run `make regenerate` after configuring +the project. This uses the same version-checked workflow as CI and caches the +Autoconf 2.72 bootstrap locally; Bison 3.8.2, Flex 2.6.4, and `uv` must be +available. Ordinary C++ changes do not require regeneration. + `GECODE_ENABLE_QT` and `GECODE_ENABLE_GIST` accept `AUTO`, `ON`, and `OFF`. Use `ON` to require the dependency and fail configuration if it is missing or too old, `OFF` to disable it, and `AUTO` to use it when the local toolchain diff --git a/changelog.in b/changelog.in index 3b76bb1a95..3b5570d293 100755 --- a/changelog.in +++ b/changelog.in @@ -52,8 +52,8 @@ # An entry is described as follows: # [ENTRY] # Module: -# kernel|search|int|set|cpltset|example|minimodel|iter|support| -# driver|test|gist|flatzinc|other +# kernel|search|int|set|float|cpltset|scheduling|graph|minimodel| +# driver|iter|support|example|test|gist|flatzinc|other # What: bug|documentation|performance|new|removed|change # Rank: minor|major # Bug: @@ -69,7 +69,7 @@ [RELEASE] Version: 6.4.0 -Date: 2026-07-07 +Date: 2026-??-?? [DESCRIPTION] This release modernizes the Gecode build infrastructure, adds a first-class CMake package for downstream consumers, refreshes the @@ -83,6 +83,14 @@ Rank: minor Fix MSVC compilation of matrix slice conversion operators by fully qualifying their dependent return types. +[ENTRY] +Module: gist +What: change +Rank: minor +[DESCRIPTION] +Restore the legacy GIST_STATIC_LIBS compatibility macro for static +Windows clients. GECODE_STATIC_LIBS remains the preferred spelling. + [ENTRY] Module: float What: bug @@ -130,6 +138,17 @@ Prefer Qt 6 for Gist and Qt-backed builds while keeping Qt 5.15+ as the supported legacy baseline. Installed CMake packages now request the Qt major/minor version used to build Gecode. +[ENTRY] +Module: flatzinc +What: removed +Rank: major +[DESCRIPTION] +Remove the public GECODE_FLATZINC_VERSION macro. It described a fixed +FlatZinc language revision that no longer matches the capability-based +MiniZinc interface. Code that only needs the Gecode release should use +GECODE_VERSION; FlatZinc integrations should use the installed MiniZinc +solver metadata instead of gating parser use on a language-version macro. + [ENTRY] Module: other What: change @@ -887,7 +906,7 @@ exp(0.0) now correctly evaluates to 1.0 (and not 0.0). [ENTRY] Module: int -What: changed +What: change Rank: minor Thanks: Jens Krueger [DESCRIPTION] @@ -1031,6 +1050,21 @@ Rank: minor Added missing definitions for declarations of creating Home objects from spaces with propagator and brancher groups. +[ENTRY] +Module: kernel +What: new +Rank: minor +Thanks: Gilles Pesant and Samuel Gagnon +[DESCRIPTION] +Introduction of a basic structure for supporting counting-based +search as described in: +S. Gagnon, G. Pesant, Accelerating Counting-Based Search, CPAIOR 2018. +This is only enabled if Gecode has been compiled with the option +--enable-cbs. +[MORE] +A branching option will be added to compatible examples as +counting algorithms are added to constraints. + [ENTRY] Module: int What: new @@ -1844,7 +1878,7 @@ Tracing now also records failure-events, see MPG for details. [ENTRY] Module: int -What: changed +What: change Rank: minor Thanks: Conrad Drescher [DESCRIPTION] @@ -1941,7 +1975,7 @@ heuristic for branching. See MPG for details. [ENTRY] Module: kernel -What: changed +What: change Rank: minor [DESCRIPTION] Do not count number of weakly monotonic propagators, nobody used @@ -2032,7 +2066,7 @@ and sequence constraints). [ENTRY] Module: int -What: changed +What: change Rank: major [DESCRIPTION] All branching functions (merit, value, commit, tie-breaking @@ -2040,7 +2074,7 @@ limit, filter, print) can now be of std::function type. [ENTRY] Module: set -What: changed +What: change Rank: major [DESCRIPTION] All branching functions (merit, value, commit, tie-breaking @@ -2048,7 +2082,7 @@ limit, filter, print) can now be of std::function type. [ENTRY] Module: float -What: changed +What: change Rank: major [DESCRIPTION] All branching functions (merit, value, commit, tie-breaking @@ -2056,7 +2090,7 @@ limit, filter, print) can now be of std::function type. [ENTRY] Module: kernel -What: changed +What: change Rank: major [DESCRIPTION] All branching functions (merit, value, commit, tie-breaking @@ -6634,8 +6668,8 @@ Sudoku and GraphColor now uses smallest size over accumulated failure count (AFC) as the default heuristic. [ENTRY] -Module: bug -What: set +Module: set +What: bug Rank: minor [DESCRIPTION] For branching, SET_VAR_DEGREE_MAX would select the variable with @@ -8383,7 +8417,7 @@ Rank: minor Fixed bug in unreflection of empty VarArrays. [ENTRY] -Module: linear +Module: int What: bug Rank: major [DESCRIPTION] @@ -8550,7 +8584,7 @@ The number of propagators and branchings can be accurately retrieved from both failed and non-stable spaces. [ENTRY] -Module: examples +Module: example What: new Rank: minor [DESCRIPTION] @@ -10613,18 +10647,3 @@ Version: 1.0.0 Date: 2005-12-06 [DESCRIPTION] Initial release. - -[ENTRY] -Module: kernel -What: new -Rank: minor -Thanks: Gilles Pesant and Samuel Gagnon -[DESCRIPTION] -Introduction of a basic structure for supporting counting-based -search as described in: -S. Gagnon, G. Pesant, Accelerating Counting-Based Search, CPAIOR 2018. -This is only enabled if Gecode has been compiled with the option ---enable-cbs. -[MORE] -A branching option will be added to compatible examples as -counting algorithms are added to constraints. diff --git a/cmake/GecodeConfig.cmake.in b/cmake/GecodeConfig.cmake.in index e7c8327bed..4099c3b2bd 100644 --- a/cmake/GecodeConfig.cmake.in +++ b/cmake/GecodeConfig.cmake.in @@ -2,11 +2,97 @@ set(_gecode_package_prefix_dir "${PACKAGE_PREFIX_DIR}") +set(_gecode_supported_components + support + kernel + search + int + set + float + minimodel + driver + flatzinc + gist) +set(_gecode_built_components "@GECODE_LIBRARY_COMPONENTS@") + +foreach(_gecode_component IN LISTS _gecode_supported_components) + if(_gecode_component IN_LIST _gecode_built_components) + set(_gecode_component_found TRUE) + else() + set(_gecode_component_found FALSE) + endif() + set(Gecode_${_gecode_component}_FOUND ${_gecode_component_found}) + set(Gecode_gecode${_gecode_component}_FOUND ${_gecode_component_found}) +endforeach() + +set(_gecode_dependency_roots) +set(Gecode_LIBRARIES) +if(Gecode_FIND_COMPONENTS) + foreach(_gecode_requested_component IN LISTS Gecode_FIND_COMPONENTS) + set(_gecode_canonical_component "${_gecode_requested_component}") + if(_gecode_canonical_component MATCHES "^gecode(.+)$") + set(_gecode_canonical_component "${CMAKE_MATCH_1}") + endif() + if(_gecode_canonical_component IN_LIST _gecode_supported_components) + if(_gecode_canonical_component IN_LIST _gecode_built_components) + list(APPEND _gecode_dependency_roots ${_gecode_canonical_component}) + list(APPEND Gecode_LIBRARIES "Gecode::gecode${_gecode_canonical_component}") + endif() + elseif(NOT DEFINED Gecode_${_gecode_requested_component}_FOUND) + set(Gecode_${_gecode_requested_component}_FOUND FALSE) + endif() + endforeach() + list(REMOVE_DUPLICATES _gecode_dependency_roots) + list(REMOVE_DUPLICATES Gecode_LIBRARIES) +else() + set(_gecode_dependency_roots ${_gecode_built_components}) + set(Gecode_LIBRARIES Gecode::gecode) +endif() + +# Compute the transitive component closure before importing targets. Importing +# an unused target whose interface names an unavailable external target can +# otherwise make CMake's generate step fail for a support-only consumer. +set(_gecode_dependency_queue ${_gecode_dependency_roots}) +set(_gecode_component_closure) +while(_gecode_dependency_queue) + list(POP_FRONT _gecode_dependency_queue _gecode_dependency_component) + if(_gecode_dependency_component IN_LIST _gecode_component_closure) + continue() + endif() + list(APPEND _gecode_component_closure ${_gecode_dependency_component}) + set(_gecode_component_dependencies) + if(_gecode_dependency_component STREQUAL kernel) + set(_gecode_component_dependencies support) + elseif(_gecode_dependency_component STREQUAL search OR + _gecode_dependency_component STREQUAL int) + set(_gecode_component_dependencies kernel) + elseif(_gecode_dependency_component STREQUAL set) + set(_gecode_component_dependencies int) + elseif(_gecode_dependency_component STREQUAL float) + set(_gecode_component_dependencies int kernel) + elseif(_gecode_dependency_component STREQUAL minimodel) + set(_gecode_component_dependencies int set search float) + elseif(_gecode_dependency_component STREQUAL gist) + set(_gecode_component_dependencies search int set float) + elseif(_gecode_dependency_component STREQUAL driver) + set(_gecode_component_dependencies int search minimodel gist) + elseif(_gecode_dependency_component STREQUAL flatzinc) + set(_gecode_component_dependencies minimodel driver gist) + endif() + foreach(_gecode_component_dependency IN LISTS _gecode_component_dependencies) + if(_gecode_component_dependency IN_LIST _gecode_built_components) + list(APPEND _gecode_dependency_queue ${_gecode_component_dependency}) + endif() + endforeach() +endwhile() + include(CMakeFindDependencyMacro) -if("@GECODE_PACKAGE_NEEDS_THREADS@" STREQUAL "ON") +if(support IN_LIST _gecode_component_closure AND + "@GECODE_PACKAGE_NEEDS_THREADS@" STREQUAL "ON") find_dependency(Threads) endif() -if("@GECODE_PACKAGE_NEEDS_MPFR@" STREQUAL "ON") +if(float IN_LIST _gecode_component_closure AND + "@GECODE_PACKAGE_NEEDS_MPFR@" STREQUAL "ON") set(_gecode_mpfr_args) if(Gecode_FIND_QUIETLY) list(APPEND _gecode_mpfr_args QUIET) @@ -69,9 +155,15 @@ if("@GECODE_PACKAGE_NEEDS_MPFR@" STREQUAL "ON") endif() set(_gecode_qt_components "@GECODE_PACKAGE_QT_COMPONENTS@") set(_gecode_qt_version "@GECODE_PACKAGE_QT_VERSION@") -if("@GECODE_PACKAGE_QT_MAJOR@" STREQUAL "6") +set(_gecode_needs_qt FALSE) +if(gist IN_LIST _gecode_component_closure OR + (flatzinc IN_LIST _gecode_component_closure AND + NOT "@GECODE_PACKAGE_QT_MAJOR@" STREQUAL "")) + set(_gecode_needs_qt TRUE) +endif() +if(_gecode_needs_qt AND "@GECODE_PACKAGE_QT_MAJOR@" STREQUAL "6") find_dependency(Qt6 ${_gecode_qt_version} COMPONENTS ${_gecode_qt_components}) -elseif("@GECODE_PACKAGE_QT_MAJOR@" STREQUAL "5") +elseif(_gecode_needs_qt AND "@GECODE_PACKAGE_QT_MAJOR@" STREQUAL "5") find_dependency(Qt5 ${_gecode_qt_version} COMPONENTS ${_gecode_qt_components}) endif() unset(_gecode_qt_version) @@ -82,41 +174,20 @@ include("${CMAKE_CURRENT_LIST_DIR}/GecodeTargets.cmake") set(Gecode_VERSION "@GECODE_PROJECT_VERSION@") set_and_check(Gecode_INCLUDE_DIRS "@PACKAGE_CMAKE_INSTALL_INCLUDEDIR@") -set(Gecode_LIBRARIES Gecode::gecode) -unset(_gecode_package_prefix_dir) - -set(_gecode_supported_components - support - kernel - search - int - set - float - minimodel - driver - flatzinc - gist) - -foreach(_gecode_component IN LISTS _gecode_supported_components) - set(_gecode_component_target "Gecode::gecode${_gecode_component}") - if(TARGET ${_gecode_component_target}) - set(_gecode_component_found TRUE) - else() - set(_gecode_component_found FALSE) - endif() - set(Gecode_${_gecode_component}_FOUND ${_gecode_component_found}) - set(Gecode_gecode${_gecode_component}_FOUND ${_gecode_component_found}) -endforeach() - -foreach(_gecode_requested_component IN LISTS Gecode_FIND_COMPONENTS) - if(NOT DEFINED Gecode_${_gecode_requested_component}_FOUND) - set(Gecode_${_gecode_requested_component}_FOUND FALSE) - endif() -endforeach() +unset(_gecode_canonical_component) +unset(_gecode_built_components) unset(_gecode_component) +unset(_gecode_component_closure) +unset(_gecode_component_dependencies) +unset(_gecode_component_dependency) unset(_gecode_component_found) unset(_gecode_component_target) +unset(_gecode_dependency_component) +unset(_gecode_dependency_queue) +unset(_gecode_dependency_roots) +unset(_gecode_needs_qt) +unset(_gecode_package_prefix_dir) unset(_gecode_requested_component) unset(_gecode_supported_components) diff --git a/cmake/VerifyTestSelection.cmake b/cmake/VerifyTestSelection.cmake new file mode 100644 index 0000000000..6991a45e7d --- /dev/null +++ b/cmake/VerifyTestSelection.cmake @@ -0,0 +1,24 @@ +if(NOT DEFINED TEST_EXECUTABLE OR NOT DEFINED REQUIRED_TESTS) + message(FATAL_ERROR "TEST_EXECUTABLE and REQUIRED_TESTS are required") +endif() + +execute_process( + COMMAND "${TEST_EXECUTABLE}" -list + RESULT_VARIABLE list_result + OUTPUT_VARIABLE registered_tests + ERROR_VARIABLE list_error) +if(NOT list_result EQUAL 0) + message(FATAL_ERROR + "Could not list registered Gecode tests (${list_result}): ${list_error}") +endif() + +string(REPLACE "\r\n" "\n" registered_tests "${registered_tests}") +string(REPLACE "\r" "\n" registered_tests "${registered_tests}") +set(registered_tests "\n${registered_tests}\n") +foreach(required_test IN LISTS REQUIRED_TESTS) + string(FIND "${registered_tests}" "\n${required_test}\n" test_position) + if(test_position EQUAL -1) + message(FATAL_ERROR + "Required check test is not registered: ${required_test}") + endif() +endforeach() diff --git a/configure b/configure index 44c2067c27..e045291b3c 100755 --- a/configure +++ b/configure @@ -13043,7 +13043,7 @@ then : fi if test "${with_vis:-no}" != "no"; then - with_vis=${with_vis//,/ } + with_vis=`printf '%s\n' "$with_vis" | sed 's/,/ /g'` ac_gecode_vis="${with_vis}${ac_gecode_vis:+ }${ac_gecode_vis}" fi @@ -14546,9 +14546,9 @@ printf "%s\n" "#define GECODE_VERSION_NUMBER ${PACKAGE_VERSION_NUMBER}" >>confde ac_gecode_library_architecture=-${VERSION_DASHES}${ac_gecode_library_architecture} -if test "$ac_gecode_compiler_vendor" == "microsoft" \ - -o \( "$ac_gecode_compiler_vendor" == "intel" \ - -a "$gecode_host_os" == "windows" \) ; then +if test "$ac_gecode_compiler_vendor" = "microsoft" \ + -o \( "$ac_gecode_compiler_vendor" = "intel" \ + -a "$gecode_host_os" = "windows" \) ; then DLL_ARCH=${ac_gecode_library_architecture} else diff --git a/configure.ac b/configure.ac index de94afbba0..0f6ec71a5e 100644 --- a/configure.ac +++ b/configure.ac @@ -334,9 +334,9 @@ AC_DEFINE_UNQUOTED(GECODE_VERSION_NUMBER, [Gecode version]) ac_gecode_library_architecture=-${VERSION_DASHES}${ac_gecode_library_architecture} -if test "$ac_gecode_compiler_vendor" == "microsoft" \ - -o \( "$ac_gecode_compiler_vendor" == "intel" \ - -a "$gecode_host_os" == "windows" \) ; then +if test "$ac_gecode_compiler_vendor" = "microsoft" \ + -o \( "$ac_gecode_compiler_vendor" = "intel" \ + -a "$gecode_host_os" = "windows" \) ; then AC_SUBST(DLL_ARCH,[${ac_gecode_library_architecture}]) else AC_SUBST(DLL_ARCH,[""]) diff --git a/gecode.m4 b/gecode.m4 index 424f3c05b3..34aa92cf1d 100755 --- a/gecode.m4 +++ b/gecode.m4 @@ -343,7 +343,7 @@ AC_DEFUN([AC_GECODE_VIS],[ AS_HELP_STRING([--with-vis], [additional variable implementation specifications])) if test "${with_vis:-no}" != "no"; then - with_vis=${with_vis//,/ } + with_vis=`printf '%s\n' "$with_vis" | sed 's/,/ /g'` ac_gecode_vis="${with_vis}${ac_gecode_vis:+ }${ac_gecode_vis}" fi]) AC_DEFUN([AC_GECODE_VTI], diff --git a/gecode/flatzinc/parser.tab.cpp b/gecode/flatzinc/parser.tab.cpp index 4a78bcece5..dd2ca07f3c 100644 --- a/gecode/flatzinc/parser.tab.cpp +++ b/gecode/flatzinc/parser.tab.cpp @@ -644,7 +644,7 @@ namespace Gecode { namespace FlatZinc { }} -#line 644 "gecode/flatzinc/parser.tab.cpp" +#line 648 "gecode/flatzinc/parser.tab.cpp" # ifndef YY_CAST # ifdef __cplusplus @@ -1188,23 +1188,23 @@ static const yytype_int8 yytranslate[] = /* YYRLINE[YYN] -- Source line where rule number YYN was defined. */ static const yytype_int16 yyrline[] = { - 0, 712, 712, 714, 716, 719, 720, 722, 724, 727, - 728, 730, 732, 735, 736, 743, 746, 748, 751, 752, - 755, 759, 760, 761, 762, 765, 767, 769, 770, 773, - 774, 777, 778, 784, 784, 787, 819, 851, 890, 923, - 932, 942, 951, 963, 1033, 1099, 1170, 1238, 1259, 1279, - 1299, 1322, 1326, 1341, 1365, 1366, 1370, 1372, 1375, 1375, - 1377, 1381, 1383, 1398, 1421, 1422, 1426, 1428, 1432, 1436, - 1438, 1453, 1476, 1477, 1481, 1483, 1486, 1489, 1491, 1506, - 1529, 1530, 1534, 1536, 1539, 1544, 1545, 1550, 1551, 1556, - 1557, 1562, 1563, 1567, 1724, 1738, 1763, 1765, 1767, 1773, - 1775, 1788, 1790, 1799, 1801, 1808, 1809, 1813, 1815, 1820, - 1821, 1825, 1827, 1832, 1833, 1837, 1839, 1844, 1845, 1849, - 1851, 1859, 1861, 1865, 1867, 1872, 1873, 1877, 1879, 1881, - 1883, 1885, 1981, 1996, 1997, 2001, 2003, 2011, 2045, 2052, - 2059, 2085, 2086, 2094, 2095, 2099, 2101, 2105, 2109, 2113, - 2115, 2119, 2121, 2123, 2126, 2126, 2129, 2131, 2133, 2135, - 2137, 2243, 2254 + 0, 716, 716, 718, 720, 723, 724, 726, 728, 731, + 732, 734, 736, 739, 740, 747, 750, 752, 755, 756, + 759, 763, 764, 765, 766, 769, 771, 773, 774, 777, + 778, 781, 782, 788, 788, 791, 823, 855, 894, 927, + 936, 946, 955, 967, 1037, 1103, 1174, 1242, 1263, 1283, + 1303, 1326, 1330, 1345, 1369, 1370, 1374, 1376, 1379, 1379, + 1381, 1385, 1387, 1402, 1425, 1426, 1430, 1432, 1436, 1440, + 1442, 1457, 1480, 1481, 1485, 1487, 1490, 1493, 1495, 1510, + 1533, 1534, 1538, 1540, 1543, 1548, 1549, 1554, 1555, 1560, + 1561, 1566, 1567, 1571, 1728, 1742, 1767, 1769, 1771, 1777, + 1779, 1792, 1794, 1803, 1805, 1812, 1813, 1817, 1819, 1824, + 1825, 1829, 1831, 1836, 1837, 1841, 1843, 1848, 1849, 1853, + 1855, 1863, 1865, 1869, 1871, 1876, 1877, 1881, 1883, 1885, + 1887, 1889, 1985, 2000, 2001, 2005, 2007, 2015, 2049, 2056, + 2063, 2089, 2090, 2098, 2099, 2103, 2105, 2109, 2113, 2117, + 2119, 2123, 2125, 2127, 2130, 2130, 2133, 2135, 2137, 2139, + 2141, 2247, 2258 }; #endif @@ -2286,31 +2286,31 @@ YYSTYPE yylval YY_INITIAL_VALUE (= yyval_default); switch (yyn) { case 15: /* preddecl_item: FZ_PREDICATE FZ_ID '(' pred_arg_list ')' */ -#line 744 "./gecode/flatzinc/parser.yxx" +#line 748 "./gecode/flatzinc/parser.yxx" { free((yyvsp[-3].sValue)); } -#line 2288 "gecode/flatzinc/parser.tab.cpp" +#line 2292 "gecode/flatzinc/parser.tab.cpp" break; case 20: /* pred_arg: pred_arg_type ':' FZ_ID */ -#line 756 "./gecode/flatzinc/parser.yxx" +#line 760 "./gecode/flatzinc/parser.yxx" { free((yyvsp[0].sValue)); } -#line 2294 "gecode/flatzinc/parser.tab.cpp" +#line 2298 "gecode/flatzinc/parser.tab.cpp" break; case 25: /* pred_arg_simple_type: int_ti_expr_tail */ -#line 766 "./gecode/flatzinc/parser.yxx" +#line 770 "./gecode/flatzinc/parser.yxx" { if ((yyvsp[0].oSet)()) delete (yyvsp[0].oSet).some(); } -#line 2300 "gecode/flatzinc/parser.tab.cpp" +#line 2304 "gecode/flatzinc/parser.tab.cpp" break; case 26: /* pred_arg_simple_type: FZ_SET FZ_OF int_ti_expr_tail */ -#line 768 "./gecode/flatzinc/parser.yxx" +#line 772 "./gecode/flatzinc/parser.yxx" { if ((yyvsp[0].oSet)()) delete (yyvsp[0].oSet).some(); } -#line 2306 "gecode/flatzinc/parser.tab.cpp" +#line 2310 "gecode/flatzinc/parser.tab.cpp" break; case 35: /* vardecl_item: FZ_VAR int_ti_expr_tail ':' var_par_id annotations non_array_expr_opt */ -#line 788 "./gecode/flatzinc/parser.yxx" +#line 792 "./gecode/flatzinc/parser.yxx" { ParserState* pp = static_cast(parm); bool print = (yyvsp[-1].argVec) != NULL && (yyvsp[-1].argVec)->hasAtom("output_var"); @@ -2342,11 +2342,11 @@ YYSTYPE yylval YY_INITIAL_VALUE (= yyval_default); } delete (yyvsp[-1].argVec); free((yyvsp[-2].sValue)); } -#line 2342 "gecode/flatzinc/parser.tab.cpp" +#line 2346 "gecode/flatzinc/parser.tab.cpp" break; case 36: /* vardecl_item: FZ_VAR bool_ti_expr_tail ':' var_par_id annotations non_array_expr_opt */ -#line 820 "./gecode/flatzinc/parser.yxx" +#line 824 "./gecode/flatzinc/parser.yxx" { ParserState* pp = static_cast(parm); bool print = (yyvsp[-1].argVec) != NULL && (yyvsp[-1].argVec)->hasAtom("output_var"); @@ -2378,11 +2378,11 @@ YYSTYPE yylval YY_INITIAL_VALUE (= yyval_default); } delete (yyvsp[-1].argVec); free((yyvsp[-2].sValue)); } -#line 2378 "gecode/flatzinc/parser.tab.cpp" +#line 2382 "gecode/flatzinc/parser.tab.cpp" break; case 37: /* vardecl_item: FZ_VAR float_ti_expr_tail ':' var_par_id annotations non_array_expr_opt */ -#line 852 "./gecode/flatzinc/parser.yxx" +#line 856 "./gecode/flatzinc/parser.yxx" { ParserState* pp = static_cast(parm); bool print = (yyvsp[-1].argVec) != NULL && (yyvsp[-1].argVec)->hasAtom("output_var"); @@ -2421,11 +2421,11 @@ YYSTYPE yylval YY_INITIAL_VALUE (= yyval_default); } delete (yyvsp[-1].argVec); free((yyvsp[-2].sValue)); } -#line 2421 "gecode/flatzinc/parser.tab.cpp" +#line 2425 "gecode/flatzinc/parser.tab.cpp" break; case 38: /* vardecl_item: FZ_VAR FZ_SET FZ_OF int_ti_expr_tail ':' var_par_id annotations non_array_expr_opt */ -#line 891 "./gecode/flatzinc/parser.yxx" +#line 895 "./gecode/flatzinc/parser.yxx" { ParserState* pp = static_cast(parm); bool print = (yyvsp[-1].argVec) != NULL && (yyvsp[-1].argVec)->hasAtom("output_var"); @@ -2458,11 +2458,11 @@ YYSTYPE yylval YY_INITIAL_VALUE (= yyval_default); } delete (yyvsp[-1].argVec); free((yyvsp[-2].sValue)); } -#line 2458 "gecode/flatzinc/parser.tab.cpp" +#line 2462 "gecode/flatzinc/parser.tab.cpp" break; case 39: /* vardecl_item: FZ_INT ':' var_par_id annotations '=' non_array_expr */ -#line 924 "./gecode/flatzinc/parser.yxx" +#line 928 "./gecode/flatzinc/parser.yxx" { ParserState* pp = static_cast(parm); yyassert(pp, (yyvsp[0].arg)->isInt(), "Invalid int initializer"); @@ -2471,11 +2471,11 @@ YYSTYPE yylval YY_INITIAL_VALUE (= yyval_default); "Duplicate symbol"); delete (yyvsp[-2].argVec); free((yyvsp[-3].sValue)); } -#line 2471 "gecode/flatzinc/parser.tab.cpp" +#line 2475 "gecode/flatzinc/parser.tab.cpp" break; case 40: /* vardecl_item: FZ_FLOAT ':' var_par_id annotations '=' non_array_expr */ -#line 933 "./gecode/flatzinc/parser.yxx" +#line 937 "./gecode/flatzinc/parser.yxx" { ParserState* pp = static_cast(parm); yyassert(pp, (yyvsp[0].arg)->isFloat(), "Invalid float initializer"); @@ -2485,11 +2485,11 @@ YYSTYPE yylval YY_INITIAL_VALUE (= yyval_default); "Duplicate symbol"); delete (yyvsp[-2].argVec); free((yyvsp[-3].sValue)); } -#line 2485 "gecode/flatzinc/parser.tab.cpp" +#line 2489 "gecode/flatzinc/parser.tab.cpp" break; case 41: /* vardecl_item: FZ_BOOL ':' var_par_id annotations '=' non_array_expr */ -#line 943 "./gecode/flatzinc/parser.yxx" +#line 947 "./gecode/flatzinc/parser.yxx" { ParserState* pp = static_cast(parm); yyassert(pp, (yyvsp[0].arg)->isBool(), "Invalid bool initializer"); @@ -2498,11 +2498,11 @@ YYSTYPE yylval YY_INITIAL_VALUE (= yyval_default); "Duplicate symbol"); delete (yyvsp[-2].argVec); free((yyvsp[-3].sValue)); } -#line 2498 "gecode/flatzinc/parser.tab.cpp" +#line 2502 "gecode/flatzinc/parser.tab.cpp" break; case 42: /* vardecl_item: FZ_SET FZ_OF FZ_INT ':' var_par_id annotations '=' non_array_expr */ -#line 952 "./gecode/flatzinc/parser.yxx" +#line 956 "./gecode/flatzinc/parser.yxx" { ParserState* pp = static_cast(parm); yyassert(pp, (yyvsp[0].arg)->isSet(), "Invalid set initializer"); @@ -2514,11 +2514,11 @@ YYSTYPE yylval YY_INITIAL_VALUE (= yyval_default); delete set; delete (yyvsp[-2].argVec); free((yyvsp[-3].sValue)); } -#line 2514 "gecode/flatzinc/parser.tab.cpp" +#line 2518 "gecode/flatzinc/parser.tab.cpp" break; case 43: /* vardecl_item: FZ_ARRAY '[' FZ_INT_LIT FZ_DOTDOT FZ_INT_LIT ']' FZ_OF FZ_VAR int_ti_expr_tail ':' var_par_id annotations vardecl_int_var_array_init */ -#line 965 "./gecode/flatzinc/parser.yxx" +#line 969 "./gecode/flatzinc/parser.yxx" { ParserState* pp = static_cast(parm); yyassert(pp, (yyvsp[-10].iValue)==1, "Arrays must start at 1"); @@ -2587,11 +2587,11 @@ YYSTYPE yylval YY_INITIAL_VALUE (= yyval_default); } delete (yyvsp[-1].argVec); free((yyvsp[-2].sValue)); } -#line 2587 "gecode/flatzinc/parser.tab.cpp" +#line 2591 "gecode/flatzinc/parser.tab.cpp" break; case 44: /* vardecl_item: FZ_ARRAY '[' FZ_INT_LIT FZ_DOTDOT FZ_INT_LIT ']' FZ_OF FZ_VAR bool_ti_expr_tail ':' var_par_id annotations vardecl_bool_var_array_init */ -#line 1035 "./gecode/flatzinc/parser.yxx" +#line 1039 "./gecode/flatzinc/parser.yxx" { ParserState* pp = static_cast(parm); bool print = (yyvsp[-1].argVec) != NULL && (yyvsp[-1].argVec)->hasCall("output_array"); @@ -2656,11 +2656,11 @@ YYSTYPE yylval YY_INITIAL_VALUE (= yyval_default); } delete (yyvsp[-1].argVec); free((yyvsp[-2].sValue)); } -#line 2656 "gecode/flatzinc/parser.tab.cpp" +#line 2660 "gecode/flatzinc/parser.tab.cpp" break; case 45: /* vardecl_item: FZ_ARRAY '[' FZ_INT_LIT FZ_DOTDOT FZ_INT_LIT ']' FZ_OF FZ_VAR float_ti_expr_tail ':' var_par_id annotations vardecl_float_var_array_init */ -#line 1102 "./gecode/flatzinc/parser.yxx" +#line 1106 "./gecode/flatzinc/parser.yxx" { ParserState* pp = static_cast(parm); yyassert(pp, (yyvsp[-10].iValue)==1, "Arrays must start at 1"); @@ -2729,11 +2729,11 @@ YYSTYPE yylval YY_INITIAL_VALUE (= yyval_default); if ((yyvsp[-4].oPFloat)()) delete (yyvsp[-4].oPFloat).some(); delete (yyvsp[-1].argVec); free((yyvsp[-2].sValue)); } -#line 2729 "gecode/flatzinc/parser.tab.cpp" +#line 2733 "gecode/flatzinc/parser.tab.cpp" break; case 46: /* vardecl_item: FZ_ARRAY '[' FZ_INT_LIT FZ_DOTDOT FZ_INT_LIT ']' FZ_OF FZ_VAR FZ_SET FZ_OF int_ti_expr_tail ':' var_par_id annotations vardecl_set_var_array_init */ -#line 1172 "./gecode/flatzinc/parser.yxx" +#line 1176 "./gecode/flatzinc/parser.yxx" { ParserState* pp = static_cast(parm); bool print = (yyvsp[-1].argVec) != NULL && (yyvsp[-1].argVec)->hasCall("output_array"); @@ -2800,11 +2800,11 @@ YYSTYPE yylval YY_INITIAL_VALUE (= yyval_default); } delete (yyvsp[-1].argVec); free((yyvsp[-2].sValue)); } -#line 2800 "gecode/flatzinc/parser.tab.cpp" +#line 2804 "gecode/flatzinc/parser.tab.cpp" break; case 47: /* vardecl_item: FZ_ARRAY '[' FZ_INT_LIT FZ_DOTDOT FZ_INT_LIT ']' FZ_OF FZ_INT ':' var_par_id annotations '=' '[' int_list ']' */ -#line 1240 "./gecode/flatzinc/parser.yxx" +#line 1244 "./gecode/flatzinc/parser.yxx" { ParserState* pp = static_cast(parm); yyassert(pp, (yyvsp[-12].iValue)==1, "Arrays must start at 1"); @@ -2824,11 +2824,11 @@ YYSTYPE yylval YY_INITIAL_VALUE (= yyval_default); free((yyvsp[-5].sValue)); delete (yyvsp[-4].argVec); } -#line 2824 "gecode/flatzinc/parser.tab.cpp" +#line 2828 "gecode/flatzinc/parser.tab.cpp" break; case 48: /* vardecl_item: FZ_ARRAY '[' FZ_INT_LIT FZ_DOTDOT FZ_INT_LIT ']' FZ_OF FZ_BOOL ':' var_par_id annotations '=' '[' bool_list ']' */ -#line 1261 "./gecode/flatzinc/parser.yxx" +#line 1265 "./gecode/flatzinc/parser.yxx" { ParserState* pp = static_cast(parm); yyassert(pp, (yyvsp[-12].iValue)==1, "Arrays must start at 1"); @@ -2847,11 +2847,11 @@ YYSTYPE yylval YY_INITIAL_VALUE (= yyval_default); free((yyvsp[-5].sValue)); delete (yyvsp[-4].argVec); } -#line 2847 "gecode/flatzinc/parser.tab.cpp" +#line 2851 "gecode/flatzinc/parser.tab.cpp" break; case 49: /* vardecl_item: FZ_ARRAY '[' FZ_INT_LIT FZ_DOTDOT FZ_INT_LIT ']' FZ_OF FZ_FLOAT ':' var_par_id annotations '=' '[' float_list ']' */ -#line 1281 "./gecode/flatzinc/parser.yxx" +#line 1285 "./gecode/flatzinc/parser.yxx" { ParserState* pp = static_cast(parm); yyassert(pp, (yyvsp[-12].iValue)==1, "Arrays must start at 1"); @@ -2870,11 +2870,11 @@ YYSTYPE yylval YY_INITIAL_VALUE (= yyval_default); delete (yyvsp[-1].floatSetValue); delete (yyvsp[-4].argVec); free((yyvsp[-5].sValue)); } -#line 2870 "gecode/flatzinc/parser.tab.cpp" +#line 2874 "gecode/flatzinc/parser.tab.cpp" break; case 50: /* vardecl_item: FZ_ARRAY '[' FZ_INT_LIT FZ_DOTDOT FZ_INT_LIT ']' FZ_OF FZ_SET FZ_OF FZ_INT ':' var_par_id annotations '=' '[' set_literal_list ']' */ -#line 1301 "./gecode/flatzinc/parser.yxx" +#line 1305 "./gecode/flatzinc/parser.yxx" { ParserState* pp = static_cast(parm); yyassert(pp, (yyvsp[-14].iValue)==1, "Arrays must start at 1"); @@ -2894,19 +2894,19 @@ YYSTYPE yylval YY_INITIAL_VALUE (= yyval_default); delete (yyvsp[-1].setValueList); delete (yyvsp[-4].argVec); free((yyvsp[-5].sValue)); } -#line 2894 "gecode/flatzinc/parser.tab.cpp" +#line 2898 "gecode/flatzinc/parser.tab.cpp" break; case 51: /* int_init: FZ_INT_LIT */ -#line 1323 "./gecode/flatzinc/parser.yxx" +#line 1327 "./gecode/flatzinc/parser.yxx" { (yyval.varSpec) = new IntVarSpec((yyvsp[0].iValue),false,false); } -#line 2902 "gecode/flatzinc/parser.tab.cpp" +#line 2906 "gecode/flatzinc/parser.tab.cpp" break; case 52: /* int_init: var_par_id */ -#line 1327 "./gecode/flatzinc/parser.yxx" +#line 1331 "./gecode/flatzinc/parser.yxx" { SymbolEntry e; ParserState* pp = static_cast(parm); @@ -2921,11 +2921,11 @@ YYSTYPE yylval YY_INITIAL_VALUE (= yyval_default); } free((yyvsp[0].sValue)); } -#line 2921 "gecode/flatzinc/parser.tab.cpp" +#line 2925 "gecode/flatzinc/parser.tab.cpp" break; case 53: /* int_init: var_par_id '[' FZ_INT_LIT ']' */ -#line 1342 "./gecode/flatzinc/parser.yxx" +#line 1346 "./gecode/flatzinc/parser.yxx" { vector v; SymbolEntry e; @@ -2946,47 +2946,47 @@ YYSTYPE yylval YY_INITIAL_VALUE (= yyval_default); } free((yyvsp[-3].sValue)); } -#line 2946 "gecode/flatzinc/parser.tab.cpp" +#line 2950 "gecode/flatzinc/parser.tab.cpp" break; case 54: /* int_init_list: %empty */ -#line 1365 "./gecode/flatzinc/parser.yxx" +#line 1369 "./gecode/flatzinc/parser.yxx" { (yyval.varSpecVec) = new vector(0); } -#line 2952 "gecode/flatzinc/parser.tab.cpp" +#line 2956 "gecode/flatzinc/parser.tab.cpp" break; case 55: /* int_init_list: int_init_list_head list_tail */ -#line 1367 "./gecode/flatzinc/parser.yxx" +#line 1371 "./gecode/flatzinc/parser.yxx" { (yyval.varSpecVec) = (yyvsp[-1].varSpecVec); } -#line 2958 "gecode/flatzinc/parser.tab.cpp" +#line 2962 "gecode/flatzinc/parser.tab.cpp" break; case 56: /* int_init_list_head: int_init */ -#line 1371 "./gecode/flatzinc/parser.yxx" +#line 1375 "./gecode/flatzinc/parser.yxx" { (yyval.varSpecVec) = new vector(1); (*(yyval.varSpecVec))[0] = (yyvsp[0].varSpec); } -#line 2964 "gecode/flatzinc/parser.tab.cpp" +#line 2968 "gecode/flatzinc/parser.tab.cpp" break; case 57: /* int_init_list_head: int_init_list_head ',' int_init */ -#line 1373 "./gecode/flatzinc/parser.yxx" +#line 1377 "./gecode/flatzinc/parser.yxx" { (yyval.varSpecVec) = (yyvsp[-2].varSpecVec); (yyval.varSpecVec)->push_back((yyvsp[0].varSpec)); } -#line 2970 "gecode/flatzinc/parser.tab.cpp" +#line 2974 "gecode/flatzinc/parser.tab.cpp" break; case 60: /* int_var_array_literal: '[' int_init_list ']' */ -#line 1378 "./gecode/flatzinc/parser.yxx" +#line 1382 "./gecode/flatzinc/parser.yxx" { (yyval.varSpecVec) = (yyvsp[-1].varSpecVec); } -#line 2976 "gecode/flatzinc/parser.tab.cpp" +#line 2980 "gecode/flatzinc/parser.tab.cpp" break; case 61: /* float_init: FZ_FLOAT_LIT */ -#line 1382 "./gecode/flatzinc/parser.yxx" +#line 1386 "./gecode/flatzinc/parser.yxx" { (yyval.varSpec) = new FloatVarSpec((yyvsp[0].dValue),false,false); } -#line 2982 "gecode/flatzinc/parser.tab.cpp" +#line 2986 "gecode/flatzinc/parser.tab.cpp" break; case 62: /* float_init: var_par_id */ -#line 1384 "./gecode/flatzinc/parser.yxx" +#line 1388 "./gecode/flatzinc/parser.yxx" { SymbolEntry e; ParserState* pp = static_cast(parm); @@ -3001,11 +3001,11 @@ YYSTYPE yylval YY_INITIAL_VALUE (= yyval_default); } free((yyvsp[0].sValue)); } -#line 3001 "gecode/flatzinc/parser.tab.cpp" +#line 3005 "gecode/flatzinc/parser.tab.cpp" break; case 63: /* float_init: var_par_id '[' FZ_INT_LIT ']' */ -#line 1399 "./gecode/flatzinc/parser.yxx" +#line 1403 "./gecode/flatzinc/parser.yxx" { SymbolEntry e; ParserState* pp = static_cast(parm); @@ -3025,47 +3025,47 @@ YYSTYPE yylval YY_INITIAL_VALUE (= yyval_default); } free((yyvsp[-3].sValue)); } -#line 3025 "gecode/flatzinc/parser.tab.cpp" +#line 3029 "gecode/flatzinc/parser.tab.cpp" break; case 64: /* float_init_list: %empty */ -#line 1421 "./gecode/flatzinc/parser.yxx" +#line 1425 "./gecode/flatzinc/parser.yxx" { (yyval.varSpecVec) = new vector(0); } -#line 3031 "gecode/flatzinc/parser.tab.cpp" +#line 3035 "gecode/flatzinc/parser.tab.cpp" break; case 65: /* float_init_list: float_init_list_head list_tail */ -#line 1423 "./gecode/flatzinc/parser.yxx" +#line 1427 "./gecode/flatzinc/parser.yxx" { (yyval.varSpecVec) = (yyvsp[-1].varSpecVec); } -#line 3037 "gecode/flatzinc/parser.tab.cpp" +#line 3041 "gecode/flatzinc/parser.tab.cpp" break; case 66: /* float_init_list_head: float_init */ -#line 1427 "./gecode/flatzinc/parser.yxx" +#line 1431 "./gecode/flatzinc/parser.yxx" { (yyval.varSpecVec) = new vector(1); (*(yyval.varSpecVec))[0] = (yyvsp[0].varSpec); } -#line 3043 "gecode/flatzinc/parser.tab.cpp" +#line 3047 "gecode/flatzinc/parser.tab.cpp" break; case 67: /* float_init_list_head: float_init_list_head ',' float_init */ -#line 1429 "./gecode/flatzinc/parser.yxx" +#line 1433 "./gecode/flatzinc/parser.yxx" { (yyval.varSpecVec) = (yyvsp[-2].varSpecVec); (yyval.varSpecVec)->push_back((yyvsp[0].varSpec)); } -#line 3049 "gecode/flatzinc/parser.tab.cpp" +#line 3053 "gecode/flatzinc/parser.tab.cpp" break; case 68: /* float_var_array_literal: '[' float_init_list ']' */ -#line 1433 "./gecode/flatzinc/parser.yxx" +#line 1437 "./gecode/flatzinc/parser.yxx" { (yyval.varSpecVec) = (yyvsp[-1].varSpecVec); } -#line 3055 "gecode/flatzinc/parser.tab.cpp" +#line 3059 "gecode/flatzinc/parser.tab.cpp" break; case 69: /* bool_init: FZ_BOOL_LIT */ -#line 1437 "./gecode/flatzinc/parser.yxx" +#line 1441 "./gecode/flatzinc/parser.yxx" { (yyval.varSpec) = new BoolVarSpec((yyvsp[0].iValue),false,false); } -#line 3061 "gecode/flatzinc/parser.tab.cpp" +#line 3065 "gecode/flatzinc/parser.tab.cpp" break; case 70: /* bool_init: var_par_id */ -#line 1439 "./gecode/flatzinc/parser.yxx" +#line 1443 "./gecode/flatzinc/parser.yxx" { SymbolEntry e; ParserState* pp = static_cast(parm); @@ -3080,11 +3080,11 @@ YYSTYPE yylval YY_INITIAL_VALUE (= yyval_default); } free((yyvsp[0].sValue)); } -#line 3080 "gecode/flatzinc/parser.tab.cpp" +#line 3084 "gecode/flatzinc/parser.tab.cpp" break; case 71: /* bool_init: var_par_id '[' FZ_INT_LIT ']' */ -#line 1454 "./gecode/flatzinc/parser.yxx" +#line 1458 "./gecode/flatzinc/parser.yxx" { SymbolEntry e; ParserState* pp = static_cast(parm); @@ -3104,47 +3104,47 @@ YYSTYPE yylval YY_INITIAL_VALUE (= yyval_default); } free((yyvsp[-3].sValue)); } -#line 3104 "gecode/flatzinc/parser.tab.cpp" +#line 3108 "gecode/flatzinc/parser.tab.cpp" break; case 72: /* bool_init_list: %empty */ -#line 1476 "./gecode/flatzinc/parser.yxx" +#line 1480 "./gecode/flatzinc/parser.yxx" { (yyval.varSpecVec) = new vector(0); } -#line 3110 "gecode/flatzinc/parser.tab.cpp" +#line 3114 "gecode/flatzinc/parser.tab.cpp" break; case 73: /* bool_init_list: bool_init_list_head list_tail */ -#line 1478 "./gecode/flatzinc/parser.yxx" +#line 1482 "./gecode/flatzinc/parser.yxx" { (yyval.varSpecVec) = (yyvsp[-1].varSpecVec); } -#line 3116 "gecode/flatzinc/parser.tab.cpp" +#line 3120 "gecode/flatzinc/parser.tab.cpp" break; case 74: /* bool_init_list_head: bool_init */ -#line 1482 "./gecode/flatzinc/parser.yxx" +#line 1486 "./gecode/flatzinc/parser.yxx" { (yyval.varSpecVec) = new vector(1); (*(yyval.varSpecVec))[0] = (yyvsp[0].varSpec); } -#line 3122 "gecode/flatzinc/parser.tab.cpp" +#line 3126 "gecode/flatzinc/parser.tab.cpp" break; case 75: /* bool_init_list_head: bool_init_list_head ',' bool_init */ -#line 1484 "./gecode/flatzinc/parser.yxx" +#line 1488 "./gecode/flatzinc/parser.yxx" { (yyval.varSpecVec) = (yyvsp[-2].varSpecVec); (yyval.varSpecVec)->push_back((yyvsp[0].varSpec)); } -#line 3128 "gecode/flatzinc/parser.tab.cpp" +#line 3132 "gecode/flatzinc/parser.tab.cpp" break; case 76: /* bool_var_array_literal: '[' bool_init_list ']' */ -#line 1486 "./gecode/flatzinc/parser.yxx" +#line 1490 "./gecode/flatzinc/parser.yxx" { (yyval.varSpecVec) = (yyvsp[-1].varSpecVec); } -#line 3134 "gecode/flatzinc/parser.tab.cpp" +#line 3138 "gecode/flatzinc/parser.tab.cpp" break; case 77: /* set_init: set_literal */ -#line 1490 "./gecode/flatzinc/parser.yxx" +#line 1494 "./gecode/flatzinc/parser.yxx" { (yyval.varSpec) = new SetVarSpec((yyvsp[0].setLit),false,false); } -#line 3140 "gecode/flatzinc/parser.tab.cpp" +#line 3144 "gecode/flatzinc/parser.tab.cpp" break; case 78: /* set_init: var_par_id */ -#line 1492 "./gecode/flatzinc/parser.yxx" +#line 1496 "./gecode/flatzinc/parser.yxx" { ParserState* pp = static_cast(parm); SymbolEntry e; @@ -3159,11 +3159,11 @@ YYSTYPE yylval YY_INITIAL_VALUE (= yyval_default); } free((yyvsp[0].sValue)); } -#line 3159 "gecode/flatzinc/parser.tab.cpp" +#line 3163 "gecode/flatzinc/parser.tab.cpp" break; case 79: /* set_init: var_par_id '[' FZ_INT_LIT ']' */ -#line 1507 "./gecode/flatzinc/parser.yxx" +#line 1511 "./gecode/flatzinc/parser.yxx" { SymbolEntry e; ParserState* pp = static_cast(parm); @@ -3183,89 +3183,89 @@ YYSTYPE yylval YY_INITIAL_VALUE (= yyval_default); } free((yyvsp[-3].sValue)); } -#line 3183 "gecode/flatzinc/parser.tab.cpp" +#line 3187 "gecode/flatzinc/parser.tab.cpp" break; case 80: /* set_init_list: %empty */ -#line 1529 "./gecode/flatzinc/parser.yxx" +#line 1533 "./gecode/flatzinc/parser.yxx" { (yyval.varSpecVec) = new vector(0); } -#line 3189 "gecode/flatzinc/parser.tab.cpp" +#line 3193 "gecode/flatzinc/parser.tab.cpp" break; case 81: /* set_init_list: set_init_list_head list_tail */ -#line 1531 "./gecode/flatzinc/parser.yxx" +#line 1535 "./gecode/flatzinc/parser.yxx" { (yyval.varSpecVec) = (yyvsp[-1].varSpecVec); } -#line 3195 "gecode/flatzinc/parser.tab.cpp" +#line 3199 "gecode/flatzinc/parser.tab.cpp" break; case 82: /* set_init_list_head: set_init */ -#line 1535 "./gecode/flatzinc/parser.yxx" +#line 1539 "./gecode/flatzinc/parser.yxx" { (yyval.varSpecVec) = new vector(1); (*(yyval.varSpecVec))[0] = (yyvsp[0].varSpec); } -#line 3201 "gecode/flatzinc/parser.tab.cpp" +#line 3205 "gecode/flatzinc/parser.tab.cpp" break; case 83: /* set_init_list_head: set_init_list_head ',' set_init */ -#line 1537 "./gecode/flatzinc/parser.yxx" +#line 1541 "./gecode/flatzinc/parser.yxx" { (yyval.varSpecVec) = (yyvsp[-2].varSpecVec); (yyval.varSpecVec)->push_back((yyvsp[0].varSpec)); } -#line 3207 "gecode/flatzinc/parser.tab.cpp" +#line 3211 "gecode/flatzinc/parser.tab.cpp" break; case 84: /* set_var_array_literal: '[' set_init_list ']' */ -#line 1540 "./gecode/flatzinc/parser.yxx" +#line 1544 "./gecode/flatzinc/parser.yxx" { (yyval.varSpecVec) = (yyvsp[-1].varSpecVec); } -#line 3213 "gecode/flatzinc/parser.tab.cpp" +#line 3217 "gecode/flatzinc/parser.tab.cpp" break; case 85: /* vardecl_int_var_array_init: %empty */ -#line 1544 "./gecode/flatzinc/parser.yxx" +#line 1548 "./gecode/flatzinc/parser.yxx" { (yyval.oVarSpecVec) = Option* >::none(); } -#line 3219 "gecode/flatzinc/parser.tab.cpp" +#line 3223 "gecode/flatzinc/parser.tab.cpp" break; case 86: /* vardecl_int_var_array_init: '=' int_var_array_literal */ -#line 1546 "./gecode/flatzinc/parser.yxx" +#line 1550 "./gecode/flatzinc/parser.yxx" { (yyval.oVarSpecVec) = Option* >::some((yyvsp[0].varSpecVec)); } -#line 3225 "gecode/flatzinc/parser.tab.cpp" +#line 3229 "gecode/flatzinc/parser.tab.cpp" break; case 87: /* vardecl_bool_var_array_init: %empty */ -#line 1550 "./gecode/flatzinc/parser.yxx" +#line 1554 "./gecode/flatzinc/parser.yxx" { (yyval.oVarSpecVec) = Option* >::none(); } -#line 3231 "gecode/flatzinc/parser.tab.cpp" +#line 3235 "gecode/flatzinc/parser.tab.cpp" break; case 88: /* vardecl_bool_var_array_init: '=' bool_var_array_literal */ -#line 1552 "./gecode/flatzinc/parser.yxx" +#line 1556 "./gecode/flatzinc/parser.yxx" { (yyval.oVarSpecVec) = Option* >::some((yyvsp[0].varSpecVec)); } -#line 3237 "gecode/flatzinc/parser.tab.cpp" +#line 3241 "gecode/flatzinc/parser.tab.cpp" break; case 89: /* vardecl_float_var_array_init: %empty */ -#line 1556 "./gecode/flatzinc/parser.yxx" +#line 1560 "./gecode/flatzinc/parser.yxx" { (yyval.oVarSpecVec) = Option* >::none(); } -#line 3243 "gecode/flatzinc/parser.tab.cpp" +#line 3247 "gecode/flatzinc/parser.tab.cpp" break; case 90: /* vardecl_float_var_array_init: '=' float_var_array_literal */ -#line 1558 "./gecode/flatzinc/parser.yxx" +#line 1562 "./gecode/flatzinc/parser.yxx" { (yyval.oVarSpecVec) = Option* >::some((yyvsp[0].varSpecVec)); } -#line 3249 "gecode/flatzinc/parser.tab.cpp" +#line 3253 "gecode/flatzinc/parser.tab.cpp" break; case 91: /* vardecl_set_var_array_init: %empty */ -#line 1562 "./gecode/flatzinc/parser.yxx" +#line 1566 "./gecode/flatzinc/parser.yxx" { (yyval.oVarSpecVec) = Option* >::none(); } -#line 3255 "gecode/flatzinc/parser.tab.cpp" +#line 3259 "gecode/flatzinc/parser.tab.cpp" break; case 92: /* vardecl_set_var_array_init: '=' set_var_array_literal */ -#line 1564 "./gecode/flatzinc/parser.yxx" +#line 1568 "./gecode/flatzinc/parser.yxx" { (yyval.oVarSpecVec) = Option* >::some((yyvsp[0].varSpecVec)); } -#line 3261 "gecode/flatzinc/parser.tab.cpp" +#line 3265 "gecode/flatzinc/parser.tab.cpp" break; case 93: /* constraint_item: FZ_CONSTRAINT FZ_ID '(' flat_expr_list ')' annotations */ -#line 1568 "./gecode/flatzinc/parser.yxx" +#line 1572 "./gecode/flatzinc/parser.yxx" { ParserState *pp = static_cast(parm); if (!pp->hadError) { @@ -3421,11 +3421,11 @@ YYSTYPE yylval YY_INITIAL_VALUE (= yyval_default); } free((yyvsp[-4].sValue)); } -#line 3421 "gecode/flatzinc/parser.tab.cpp" +#line 3425 "gecode/flatzinc/parser.tab.cpp" break; case 94: /* solve_item: FZ_SOLVE annotations FZ_SATISFY */ -#line 1725 "./gecode/flatzinc/parser.yxx" +#line 1729 "./gecode/flatzinc/parser.yxx" { ParserState *pp = static_cast(parm); initfg(pp); @@ -3439,11 +3439,11 @@ YYSTYPE yylval YY_INITIAL_VALUE (= yyval_default); delete (yyvsp[-1].argVec); } } -#line 3439 "gecode/flatzinc/parser.tab.cpp" +#line 3443 "gecode/flatzinc/parser.tab.cpp" break; case 95: /* solve_item: FZ_SOLVE annotations minmax solve_expr */ -#line 1739 "./gecode/flatzinc/parser.yxx" +#line 1743 "./gecode/flatzinc/parser.yxx" { ParserState *pp = static_cast(parm); initfg(pp); @@ -3462,37 +3462,37 @@ YYSTYPE yylval YY_INITIAL_VALUE (= yyval_default); delete (yyvsp[-2].argVec); } } -#line 3462 "gecode/flatzinc/parser.tab.cpp" +#line 3466 "gecode/flatzinc/parser.tab.cpp" break; case 96: /* int_ti_expr_tail: FZ_INT */ -#line 1764 "./gecode/flatzinc/parser.yxx" +#line 1768 "./gecode/flatzinc/parser.yxx" { (yyval.oSet) = Option::none(); } -#line 3468 "gecode/flatzinc/parser.tab.cpp" +#line 3472 "gecode/flatzinc/parser.tab.cpp" break; case 97: /* int_ti_expr_tail: '{' int_list '}' */ -#line 1766 "./gecode/flatzinc/parser.yxx" +#line 1770 "./gecode/flatzinc/parser.yxx" { (yyval.oSet) = Option::some(new AST::SetLit(*(yyvsp[-1].setValue))); } -#line 3474 "gecode/flatzinc/parser.tab.cpp" +#line 3478 "gecode/flatzinc/parser.tab.cpp" break; case 98: /* int_ti_expr_tail: FZ_INT_LIT FZ_DOTDOT FZ_INT_LIT */ -#line 1768 "./gecode/flatzinc/parser.yxx" +#line 1772 "./gecode/flatzinc/parser.yxx" { (yyval.oSet) = Option::some(new AST::SetLit((yyvsp[-2].iValue), (yyvsp[0].iValue))); } -#line 3482 "gecode/flatzinc/parser.tab.cpp" +#line 3486 "gecode/flatzinc/parser.tab.cpp" break; case 99: /* bool_ti_expr_tail: FZ_BOOL */ -#line 1774 "./gecode/flatzinc/parser.yxx" +#line 1778 "./gecode/flatzinc/parser.yxx" { (yyval.oSet) = Option::none(); } -#line 3488 "gecode/flatzinc/parser.tab.cpp" +#line 3492 "gecode/flatzinc/parser.tab.cpp" break; case 100: /* bool_ti_expr_tail: '{' bool_list_head list_tail '}' */ -#line 1776 "./gecode/flatzinc/parser.yxx" +#line 1780 "./gecode/flatzinc/parser.yxx" { bool haveTrue = false; bool haveFalse = false; for (int i=(yyvsp[-2].setValue)->size(); i--;) { @@ -3503,192 +3503,192 @@ YYSTYPE yylval YY_INITIAL_VALUE (= yyval_default); (yyval.oSet) = Option::some( new AST::SetLit(!haveFalse,haveTrue)); } -#line 3503 "gecode/flatzinc/parser.tab.cpp" +#line 3507 "gecode/flatzinc/parser.tab.cpp" break; case 101: /* float_ti_expr_tail: FZ_FLOAT */ -#line 1789 "./gecode/flatzinc/parser.yxx" +#line 1793 "./gecode/flatzinc/parser.yxx" { (yyval.oPFloat) = Option* >::none(); } -#line 3509 "gecode/flatzinc/parser.tab.cpp" +#line 3513 "gecode/flatzinc/parser.tab.cpp" break; case 102: /* float_ti_expr_tail: FZ_FLOAT_LIT FZ_DOTDOT FZ_FLOAT_LIT */ -#line 1791 "./gecode/flatzinc/parser.yxx" +#line 1795 "./gecode/flatzinc/parser.yxx" { std::pair* dom = new std::pair((yyvsp[-2].dValue),(yyvsp[0].dValue)); (yyval.oPFloat) = Option* >::some(dom); } -#line 3516 "gecode/flatzinc/parser.tab.cpp" +#line 3520 "gecode/flatzinc/parser.tab.cpp" break; case 103: /* set_literal: '{' int_list '}' */ -#line 1800 "./gecode/flatzinc/parser.yxx" +#line 1804 "./gecode/flatzinc/parser.yxx" { (yyval.setLit) = new AST::SetLit(*(yyvsp[-1].setValue)); } -#line 3522 "gecode/flatzinc/parser.tab.cpp" +#line 3526 "gecode/flatzinc/parser.tab.cpp" break; case 104: /* set_literal: FZ_INT_LIT FZ_DOTDOT FZ_INT_LIT */ -#line 1802 "./gecode/flatzinc/parser.yxx" +#line 1806 "./gecode/flatzinc/parser.yxx" { (yyval.setLit) = new AST::SetLit((yyvsp[-2].iValue), (yyvsp[0].iValue)); } -#line 3528 "gecode/flatzinc/parser.tab.cpp" +#line 3532 "gecode/flatzinc/parser.tab.cpp" break; case 105: /* int_list: %empty */ -#line 1808 "./gecode/flatzinc/parser.yxx" +#line 1812 "./gecode/flatzinc/parser.yxx" { (yyval.setValue) = new vector(0); } -#line 3534 "gecode/flatzinc/parser.tab.cpp" +#line 3538 "gecode/flatzinc/parser.tab.cpp" break; case 106: /* int_list: int_list_head list_tail */ -#line 1810 "./gecode/flatzinc/parser.yxx" +#line 1814 "./gecode/flatzinc/parser.yxx" { (yyval.setValue) = (yyvsp[-1].setValue); } -#line 3540 "gecode/flatzinc/parser.tab.cpp" +#line 3544 "gecode/flatzinc/parser.tab.cpp" break; case 107: /* int_list_head: FZ_INT_LIT */ -#line 1814 "./gecode/flatzinc/parser.yxx" +#line 1818 "./gecode/flatzinc/parser.yxx" { (yyval.setValue) = new vector(1); (*(yyval.setValue))[0] = (yyvsp[0].iValue); } -#line 3546 "gecode/flatzinc/parser.tab.cpp" +#line 3550 "gecode/flatzinc/parser.tab.cpp" break; case 108: /* int_list_head: int_list_head ',' FZ_INT_LIT */ -#line 1816 "./gecode/flatzinc/parser.yxx" +#line 1820 "./gecode/flatzinc/parser.yxx" { (yyval.setValue) = (yyvsp[-2].setValue); (yyval.setValue)->push_back((yyvsp[0].iValue)); } -#line 3552 "gecode/flatzinc/parser.tab.cpp" +#line 3556 "gecode/flatzinc/parser.tab.cpp" break; case 109: /* bool_list: %empty */ -#line 1820 "./gecode/flatzinc/parser.yxx" +#line 1824 "./gecode/flatzinc/parser.yxx" { (yyval.setValue) = new vector(0); } -#line 3558 "gecode/flatzinc/parser.tab.cpp" +#line 3562 "gecode/flatzinc/parser.tab.cpp" break; case 110: /* bool_list: bool_list_head list_tail */ -#line 1822 "./gecode/flatzinc/parser.yxx" +#line 1826 "./gecode/flatzinc/parser.yxx" { (yyval.setValue) = (yyvsp[-1].setValue); } -#line 3564 "gecode/flatzinc/parser.tab.cpp" +#line 3568 "gecode/flatzinc/parser.tab.cpp" break; case 111: /* bool_list_head: FZ_BOOL_LIT */ -#line 1826 "./gecode/flatzinc/parser.yxx" +#line 1830 "./gecode/flatzinc/parser.yxx" { (yyval.setValue) = new vector(1); (*(yyval.setValue))[0] = (yyvsp[0].iValue); } -#line 3570 "gecode/flatzinc/parser.tab.cpp" +#line 3574 "gecode/flatzinc/parser.tab.cpp" break; case 112: /* bool_list_head: bool_list_head ',' FZ_BOOL_LIT */ -#line 1828 "./gecode/flatzinc/parser.yxx" +#line 1832 "./gecode/flatzinc/parser.yxx" { (yyval.setValue) = (yyvsp[-2].setValue); (yyval.setValue)->push_back((yyvsp[0].iValue)); } -#line 3576 "gecode/flatzinc/parser.tab.cpp" +#line 3580 "gecode/flatzinc/parser.tab.cpp" break; case 113: /* float_list: %empty */ -#line 1832 "./gecode/flatzinc/parser.yxx" +#line 1836 "./gecode/flatzinc/parser.yxx" { (yyval.floatSetValue) = new vector(0); } -#line 3582 "gecode/flatzinc/parser.tab.cpp" +#line 3586 "gecode/flatzinc/parser.tab.cpp" break; case 114: /* float_list: float_list_head list_tail */ -#line 1834 "./gecode/flatzinc/parser.yxx" +#line 1838 "./gecode/flatzinc/parser.yxx" { (yyval.floatSetValue) = (yyvsp[-1].floatSetValue); } -#line 3588 "gecode/flatzinc/parser.tab.cpp" +#line 3592 "gecode/flatzinc/parser.tab.cpp" break; case 115: /* float_list_head: FZ_FLOAT_LIT */ -#line 1838 "./gecode/flatzinc/parser.yxx" +#line 1842 "./gecode/flatzinc/parser.yxx" { (yyval.floatSetValue) = new vector(1); (*(yyval.floatSetValue))[0] = (yyvsp[0].dValue); } -#line 3594 "gecode/flatzinc/parser.tab.cpp" +#line 3598 "gecode/flatzinc/parser.tab.cpp" break; case 116: /* float_list_head: float_list_head ',' FZ_FLOAT_LIT */ -#line 1840 "./gecode/flatzinc/parser.yxx" +#line 1844 "./gecode/flatzinc/parser.yxx" { (yyval.floatSetValue) = (yyvsp[-2].floatSetValue); (yyval.floatSetValue)->push_back((yyvsp[0].dValue)); } -#line 3600 "gecode/flatzinc/parser.tab.cpp" +#line 3604 "gecode/flatzinc/parser.tab.cpp" break; case 117: /* set_literal_list: %empty */ -#line 1844 "./gecode/flatzinc/parser.yxx" +#line 1848 "./gecode/flatzinc/parser.yxx" { (yyval.setValueList) = new vector(0); } -#line 3606 "gecode/flatzinc/parser.tab.cpp" +#line 3610 "gecode/flatzinc/parser.tab.cpp" break; case 118: /* set_literal_list: set_literal_list_head list_tail */ -#line 1846 "./gecode/flatzinc/parser.yxx" +#line 1850 "./gecode/flatzinc/parser.yxx" { (yyval.setValueList) = (yyvsp[-1].setValueList); } -#line 3612 "gecode/flatzinc/parser.tab.cpp" +#line 3616 "gecode/flatzinc/parser.tab.cpp" break; case 119: /* set_literal_list_head: set_literal */ -#line 1850 "./gecode/flatzinc/parser.yxx" +#line 1854 "./gecode/flatzinc/parser.yxx" { (yyval.setValueList) = new vector(1); (*(yyval.setValueList))[0] = *(yyvsp[0].setLit); delete (yyvsp[0].setLit); } -#line 3618 "gecode/flatzinc/parser.tab.cpp" +#line 3622 "gecode/flatzinc/parser.tab.cpp" break; case 120: /* set_literal_list_head: set_literal_list_head ',' set_literal */ -#line 1852 "./gecode/flatzinc/parser.yxx" +#line 1856 "./gecode/flatzinc/parser.yxx" { (yyval.setValueList) = (yyvsp[-2].setValueList); (yyval.setValueList)->push_back(*(yyvsp[0].setLit)); delete (yyvsp[0].setLit); } -#line 3624 "gecode/flatzinc/parser.tab.cpp" +#line 3628 "gecode/flatzinc/parser.tab.cpp" break; case 121: /* flat_expr_list: flat_expr */ -#line 1860 "./gecode/flatzinc/parser.yxx" +#line 1864 "./gecode/flatzinc/parser.yxx" { (yyval.argVec) = new AST::Array((yyvsp[0].arg)); } -#line 3630 "gecode/flatzinc/parser.tab.cpp" +#line 3634 "gecode/flatzinc/parser.tab.cpp" break; case 122: /* flat_expr_list: flat_expr_list ',' flat_expr */ -#line 1862 "./gecode/flatzinc/parser.yxx" +#line 1866 "./gecode/flatzinc/parser.yxx" { (yyval.argVec) = (yyvsp[-2].argVec); (yyval.argVec)->append((yyvsp[0].arg)); } -#line 3636 "gecode/flatzinc/parser.tab.cpp" +#line 3640 "gecode/flatzinc/parser.tab.cpp" break; case 123: /* flat_expr: non_array_expr */ -#line 1866 "./gecode/flatzinc/parser.yxx" +#line 1870 "./gecode/flatzinc/parser.yxx" { (yyval.arg) = (yyvsp[0].arg); } -#line 3642 "gecode/flatzinc/parser.tab.cpp" +#line 3646 "gecode/flatzinc/parser.tab.cpp" break; case 124: /* flat_expr: '[' non_array_expr_list ']' */ -#line 1868 "./gecode/flatzinc/parser.yxx" +#line 1872 "./gecode/flatzinc/parser.yxx" { (yyval.arg) = (yyvsp[-1].argVec); } -#line 3648 "gecode/flatzinc/parser.tab.cpp" +#line 3652 "gecode/flatzinc/parser.tab.cpp" break; case 125: /* non_array_expr_opt: %empty */ -#line 1872 "./gecode/flatzinc/parser.yxx" +#line 1876 "./gecode/flatzinc/parser.yxx" { (yyval.oArg) = Option::none(); } -#line 3654 "gecode/flatzinc/parser.tab.cpp" +#line 3658 "gecode/flatzinc/parser.tab.cpp" break; case 126: /* non_array_expr_opt: '=' non_array_expr */ -#line 1874 "./gecode/flatzinc/parser.yxx" +#line 1878 "./gecode/flatzinc/parser.yxx" { (yyval.oArg) = Option::some((yyvsp[0].arg)); } -#line 3660 "gecode/flatzinc/parser.tab.cpp" +#line 3664 "gecode/flatzinc/parser.tab.cpp" break; case 127: /* non_array_expr: FZ_BOOL_LIT */ -#line 1878 "./gecode/flatzinc/parser.yxx" +#line 1882 "./gecode/flatzinc/parser.yxx" { (yyval.arg) = new AST::BoolLit((yyvsp[0].iValue)); } -#line 3666 "gecode/flatzinc/parser.tab.cpp" +#line 3670 "gecode/flatzinc/parser.tab.cpp" break; case 128: /* non_array_expr: FZ_INT_LIT */ -#line 1880 "./gecode/flatzinc/parser.yxx" +#line 1884 "./gecode/flatzinc/parser.yxx" { (yyval.arg) = new AST::IntLit((yyvsp[0].iValue)); } -#line 3672 "gecode/flatzinc/parser.tab.cpp" +#line 3676 "gecode/flatzinc/parser.tab.cpp" break; case 129: /* non_array_expr: FZ_FLOAT_LIT */ -#line 1882 "./gecode/flatzinc/parser.yxx" +#line 1886 "./gecode/flatzinc/parser.yxx" { (yyval.arg) = new AST::FloatLit((yyvsp[0].dValue)); } -#line 3678 "gecode/flatzinc/parser.tab.cpp" +#line 3682 "gecode/flatzinc/parser.tab.cpp" break; case 130: /* non_array_expr: set_literal */ -#line 1884 "./gecode/flatzinc/parser.yxx" +#line 1888 "./gecode/flatzinc/parser.yxx" { (yyval.arg) = (yyvsp[0].setLit); } -#line 3684 "gecode/flatzinc/parser.tab.cpp" +#line 3688 "gecode/flatzinc/parser.tab.cpp" break; case 131: /* non_array_expr: var_par_id */ -#line 1886 "./gecode/flatzinc/parser.yxx" +#line 1890 "./gecode/flatzinc/parser.yxx" { ParserState* pp = static_cast(parm); SymbolEntry e; @@ -3784,11 +3784,11 @@ YYSTYPE yylval YY_INITIAL_VALUE (= yyval_default); } free((yyvsp[0].sValue)); } -#line 3784 "gecode/flatzinc/parser.tab.cpp" +#line 3788 "gecode/flatzinc/parser.tab.cpp" break; case 132: /* non_array_expr: var_par_id '[' non_array_expr ']' */ -#line 1982 "./gecode/flatzinc/parser.yxx" +#line 1986 "./gecode/flatzinc/parser.yxx" { ParserState* pp = static_cast(parm); int i = -1; @@ -3800,35 +3800,35 @@ YYSTYPE yylval YY_INITIAL_VALUE (= yyval_default); delete (yyvsp[-1].arg); free((yyvsp[-3].sValue)); } -#line 3800 "gecode/flatzinc/parser.tab.cpp" +#line 3804 "gecode/flatzinc/parser.tab.cpp" break; case 133: /* non_array_expr_list: %empty */ -#line 1996 "./gecode/flatzinc/parser.yxx" +#line 2000 "./gecode/flatzinc/parser.yxx" { (yyval.argVec) = new AST::Array(0); } -#line 3806 "gecode/flatzinc/parser.tab.cpp" +#line 3810 "gecode/flatzinc/parser.tab.cpp" break; case 134: /* non_array_expr_list: non_array_expr_list_head list_tail */ -#line 1998 "./gecode/flatzinc/parser.yxx" +#line 2002 "./gecode/flatzinc/parser.yxx" { (yyval.argVec) = (yyvsp[-1].argVec); } -#line 3812 "gecode/flatzinc/parser.tab.cpp" +#line 3816 "gecode/flatzinc/parser.tab.cpp" break; case 135: /* non_array_expr_list_head: non_array_expr */ -#line 2002 "./gecode/flatzinc/parser.yxx" +#line 2006 "./gecode/flatzinc/parser.yxx" { (yyval.argVec) = new AST::Array((yyvsp[0].arg)); } -#line 3818 "gecode/flatzinc/parser.tab.cpp" +#line 3822 "gecode/flatzinc/parser.tab.cpp" break; case 136: /* non_array_expr_list_head: non_array_expr_list_head ',' non_array_expr */ -#line 2004 "./gecode/flatzinc/parser.yxx" +#line 2008 "./gecode/flatzinc/parser.yxx" { (yyval.argVec) = (yyvsp[-2].argVec); (yyval.argVec)->append((yyvsp[0].arg)); } -#line 3824 "gecode/flatzinc/parser.tab.cpp" +#line 3828 "gecode/flatzinc/parser.tab.cpp" break; case 137: /* solve_expr: var_par_id */ -#line 2012 "./gecode/flatzinc/parser.yxx" +#line 2016 "./gecode/flatzinc/parser.yxx" { ParserState *pp = static_cast(parm); SymbolEntry e; @@ -3862,33 +3862,33 @@ YYSTYPE yylval YY_INITIAL_VALUE (= yyval_default); } free((yyvsp[0].sValue)); } -#line 3862 "gecode/flatzinc/parser.tab.cpp" +#line 3866 "gecode/flatzinc/parser.tab.cpp" break; case 138: /* solve_expr: FZ_INT_LIT */ -#line 2046 "./gecode/flatzinc/parser.yxx" +#line 2050 "./gecode/flatzinc/parser.yxx" { ParserState *pp = static_cast(parm); pp->intvars.push_back(varspec("OBJ_CONST_INTRODUCED", new IntVarSpec(0,true,false))); (yyval.iValue) = pp->intvars.size()-1; } -#line 3873 "gecode/flatzinc/parser.tab.cpp" +#line 3877 "gecode/flatzinc/parser.tab.cpp" break; case 139: /* solve_expr: FZ_FLOAT_LIT */ -#line 2053 "./gecode/flatzinc/parser.yxx" +#line 2057 "./gecode/flatzinc/parser.yxx" { ParserState *pp = static_cast(parm); pp->intvars.push_back(varspec("OBJ_CONST_INTRODUCED", new IntVarSpec(0,true,false))); (yyval.iValue) = pp->intvars.size()-1; } -#line 3884 "gecode/flatzinc/parser.tab.cpp" +#line 3888 "gecode/flatzinc/parser.tab.cpp" break; case 140: /* solve_expr: var_par_id '[' FZ_INT_LIT ']' */ -#line 2060 "./gecode/flatzinc/parser.yxx" +#line 2064 "./gecode/flatzinc/parser.yxx" { SymbolEntry e; ParserState *pp = static_cast(parm); @@ -3912,103 +3912,103 @@ YYSTYPE yylval YY_INITIAL_VALUE (= yyval_default); } free((yyvsp[-3].sValue)); } -#line 3912 "gecode/flatzinc/parser.tab.cpp" +#line 3916 "gecode/flatzinc/parser.tab.cpp" break; case 143: /* annotations: %empty */ -#line 2094 "./gecode/flatzinc/parser.yxx" +#line 2098 "./gecode/flatzinc/parser.yxx" { (yyval.argVec) = NULL; } -#line 3918 "gecode/flatzinc/parser.tab.cpp" +#line 3922 "gecode/flatzinc/parser.tab.cpp" break; case 144: /* annotations: annotations_head */ -#line 2096 "./gecode/flatzinc/parser.yxx" +#line 2100 "./gecode/flatzinc/parser.yxx" { (yyval.argVec) = (yyvsp[0].argVec); } -#line 3924 "gecode/flatzinc/parser.tab.cpp" +#line 3928 "gecode/flatzinc/parser.tab.cpp" break; case 145: /* annotations_head: FZ_COLONCOLON annotation */ -#line 2100 "./gecode/flatzinc/parser.yxx" +#line 2104 "./gecode/flatzinc/parser.yxx" { (yyval.argVec) = new AST::Array((yyvsp[0].arg)); } -#line 3930 "gecode/flatzinc/parser.tab.cpp" +#line 3934 "gecode/flatzinc/parser.tab.cpp" break; case 146: /* annotations_head: annotations_head FZ_COLONCOLON annotation */ -#line 2102 "./gecode/flatzinc/parser.yxx" +#line 2106 "./gecode/flatzinc/parser.yxx" { (yyval.argVec) = (yyvsp[-2].argVec); (yyval.argVec)->append((yyvsp[0].arg)); } -#line 3936 "gecode/flatzinc/parser.tab.cpp" +#line 3940 "gecode/flatzinc/parser.tab.cpp" break; case 147: /* annotation: FZ_ID '(' annotation_list ')' */ -#line 2106 "./gecode/flatzinc/parser.yxx" +#line 2110 "./gecode/flatzinc/parser.yxx" { (yyval.arg) = new AST::Call((yyvsp[-3].sValue), AST::extractSingleton((yyvsp[-1].arg))); free((yyvsp[-3].sValue)); } -#line 3944 "gecode/flatzinc/parser.tab.cpp" +#line 3948 "gecode/flatzinc/parser.tab.cpp" break; case 148: /* annotation: annotation_expr */ -#line 2110 "./gecode/flatzinc/parser.yxx" +#line 2114 "./gecode/flatzinc/parser.yxx" { (yyval.arg) = (yyvsp[0].arg); } -#line 3950 "gecode/flatzinc/parser.tab.cpp" +#line 3954 "gecode/flatzinc/parser.tab.cpp" break; case 149: /* annotation_list: annotation */ -#line 2114 "./gecode/flatzinc/parser.yxx" +#line 2118 "./gecode/flatzinc/parser.yxx" { (yyval.arg) = new AST::Array((yyvsp[0].arg)); } -#line 3956 "gecode/flatzinc/parser.tab.cpp" +#line 3960 "gecode/flatzinc/parser.tab.cpp" break; case 150: /* annotation_list: annotation_list ',' annotation */ -#line 2116 "./gecode/flatzinc/parser.yxx" +#line 2120 "./gecode/flatzinc/parser.yxx" { (yyval.arg) = (yyvsp[-2].arg); (yyval.arg)->append((yyvsp[0].arg)); } -#line 3962 "gecode/flatzinc/parser.tab.cpp" +#line 3966 "gecode/flatzinc/parser.tab.cpp" break; case 151: /* annotation_expr: ann_non_array_expr */ -#line 2120 "./gecode/flatzinc/parser.yxx" +#line 2124 "./gecode/flatzinc/parser.yxx" { (yyval.arg) = (yyvsp[0].arg); } -#line 3968 "gecode/flatzinc/parser.tab.cpp" +#line 3972 "gecode/flatzinc/parser.tab.cpp" break; case 152: /* annotation_expr: '[' ']' */ -#line 2122 "./gecode/flatzinc/parser.yxx" +#line 2126 "./gecode/flatzinc/parser.yxx" { (yyval.arg) = new AST::Array(); } -#line 3974 "gecode/flatzinc/parser.tab.cpp" +#line 3978 "gecode/flatzinc/parser.tab.cpp" break; case 153: /* annotation_expr: '[' annotation_list annotation_list_tail ']' */ -#line 2124 "./gecode/flatzinc/parser.yxx" +#line 2128 "./gecode/flatzinc/parser.yxx" { (yyval.arg) = (yyvsp[-2].arg); } -#line 3980 "gecode/flatzinc/parser.tab.cpp" +#line 3984 "gecode/flatzinc/parser.tab.cpp" break; case 156: /* ann_non_array_expr: FZ_BOOL_LIT */ -#line 2130 "./gecode/flatzinc/parser.yxx" +#line 2134 "./gecode/flatzinc/parser.yxx" { (yyval.arg) = new AST::BoolLit((yyvsp[0].iValue)); } -#line 3986 "gecode/flatzinc/parser.tab.cpp" +#line 3990 "gecode/flatzinc/parser.tab.cpp" break; case 157: /* ann_non_array_expr: FZ_INT_LIT */ -#line 2132 "./gecode/flatzinc/parser.yxx" +#line 2136 "./gecode/flatzinc/parser.yxx" { (yyval.arg) = new AST::IntLit((yyvsp[0].iValue)); } -#line 3992 "gecode/flatzinc/parser.tab.cpp" +#line 3996 "gecode/flatzinc/parser.tab.cpp" break; case 158: /* ann_non_array_expr: FZ_FLOAT_LIT */ -#line 2134 "./gecode/flatzinc/parser.yxx" +#line 2138 "./gecode/flatzinc/parser.yxx" { (yyval.arg) = new AST::FloatLit((yyvsp[0].dValue)); } -#line 3998 "gecode/flatzinc/parser.tab.cpp" +#line 4002 "gecode/flatzinc/parser.tab.cpp" break; case 159: /* ann_non_array_expr: set_literal */ -#line 2136 "./gecode/flatzinc/parser.yxx" +#line 2140 "./gecode/flatzinc/parser.yxx" { (yyval.arg) = (yyvsp[0].setLit); } -#line 4004 "gecode/flatzinc/parser.tab.cpp" +#line 4008 "gecode/flatzinc/parser.tab.cpp" break; case 160: /* ann_non_array_expr: var_par_id */ -#line 2138 "./gecode/flatzinc/parser.yxx" +#line 2142 "./gecode/flatzinc/parser.yxx" { ParserState* pp = static_cast(parm); SymbolEntry e; @@ -4114,11 +4114,11 @@ YYSTYPE yylval YY_INITIAL_VALUE (= yyval_default); (yyval.arg) = getVarRefArg(pp,(yyvsp[0].sValue),true); free((yyvsp[0].sValue)); } -#line 4114 "gecode/flatzinc/parser.tab.cpp" +#line 4118 "gecode/flatzinc/parser.tab.cpp" break; case 161: /* ann_non_array_expr: var_par_id '[' ann_non_array_expr ']' */ -#line 2244 "./gecode/flatzinc/parser.yxx" +#line 2248 "./gecode/flatzinc/parser.yxx" { ParserState* pp = static_cast(parm); int i = -1; @@ -4129,20 +4129,20 @@ YYSTYPE yylval YY_INITIAL_VALUE (= yyval_default); (yyval.arg) = new AST::IntLit(0); // keep things consistent free((yyvsp[-3].sValue)); } -#line 4129 "gecode/flatzinc/parser.tab.cpp" +#line 4133 "gecode/flatzinc/parser.tab.cpp" break; case 162: /* ann_non_array_expr: FZ_STRING_LIT */ -#line 2255 "./gecode/flatzinc/parser.yxx" +#line 2259 "./gecode/flatzinc/parser.yxx" { (yyval.arg) = new AST::String((yyvsp[0].sValue)); free((yyvsp[0].sValue)); } -#line 4138 "gecode/flatzinc/parser.tab.cpp" +#line 4142 "gecode/flatzinc/parser.tab.cpp" break; -#line 4142 "gecode/flatzinc/parser.tab.cpp" +#line 4146 "gecode/flatzinc/parser.tab.cpp" default: break; } diff --git a/gecode/flatzinc/parser.tab.hpp b/gecode/flatzinc/parser.tab.hpp index 695c7a276d..094a72380a 100644 --- a/gecode/flatzinc/parser.tab.hpp +++ b/gecode/flatzinc/parser.tab.hpp @@ -107,7 +107,7 @@ extern int yydebug; #if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED union YYSTYPE { -#line 610 "./gecode/flatzinc/parser.yxx" +#line 614 "./gecode/flatzinc/parser.yxx" int iValue; char* sValue; bool bValue; double dValue; std::vector* setValue; Gecode::FlatZinc::AST::SetLit* setLit; diff --git a/gecode/float/arithmetic/mult.hpp b/gecode/float/arithmetic/mult.hpp index 6194e09956..8018e377f5 100755 --- a/gecode/float/arithmetic/mult.hpp +++ b/gecode/float/arithmetic/mult.hpp @@ -196,14 +196,38 @@ namespace Gecode { namespace Float { namespace Arithmetic { return new (home) Mult(home,*this); } + /// Contract a quotient when the denominator has zero as one endpoint + template + forceinline ModEvent + div_zero_endpoint(Home home, View x, const FloatVal& n, const View& d) { + if ((d.min() == 0.0) && (d.max() > 0.0)) { + const FloatVal q = n / FloatVal(d.max()); + if (n.min() > 0.0) + return x.gq(home,q.min()); + if (n.max() < 0.0) + return x.lq(home,q.max()); + } else if ((d.max() == 0.0) && (d.min() < 0.0)) { + const FloatVal q = n / FloatVal(d.min()); + if (n.min() > 0.0) + return x.lq(home,q.max()); + if (n.max() < 0.0) + return x.gq(home,q.min()); + } + return ME_FLOAT_NONE; + } + template ExecStatus Mult::propagate(Space& home, const ModEventDelta&) { GECODE_ME_CHECK(x2.eq(home,x0.val()*x1.val())); if (!x1.zero_in()) GECODE_ME_CHECK(x0.eq(home,x2.val() / x1.val())); + else + GECODE_ME_CHECK(div_zero_endpoint(home,x0,x2.val(),x1)); if (!x0.zero_in()) GECODE_ME_CHECK(x1.eq(home,x2.val() / x0.val())); + else + GECODE_ME_CHECK(div_zero_endpoint(home,x1,x2.val(),x0)); GECODE_ME_CHECK(x2.eq(home,x0.val()*x1.val())); if (x0.assigned() && x1.assigned() && x2.assigned()) return home.ES_SUBSUMED(*this); @@ -222,8 +246,12 @@ namespace Gecode { namespace Float { namespace Arithmetic { GECODE_ME_CHECK(x2.eq(home,x0.val()*x1.val())); if (!x1.zero_in()) GECODE_ME_CHECK(x0.eq(home,x2.val() / x1.val())); + else + GECODE_ME_CHECK(div_zero_endpoint(home,x0,x2.val(),x1)); if (!x0.zero_in()) GECODE_ME_CHECK(x1.eq(home,x2.val() / x0.val())); + else + GECODE_ME_CHECK(div_zero_endpoint(home,x1,x2.val(),x0)); GECODE_ME_CHECK(x2.eq(home,x0.val()*x1.val())); if (x0.assigned() && x1.assigned() && x2.assigned()) return ES_OK; diff --git a/gecode/float/arithmetic/pow-nroot.hpp b/gecode/float/arithmetic/pow-nroot.hpp index daef6ad981..0c4de4aa46 100755 --- a/gecode/float/arithmetic/pow-nroot.hpp +++ b/gecode/float/arithmetic/pow-nroot.hpp @@ -33,12 +33,104 @@ namespace Gecode { namespace Float { namespace Arithmetic { + /// Propagator maintaining that a float view does not contain zero + template + class NonZero : public UnaryPropagator { + protected: + using UnaryPropagator::x0; + /// Constructor for cloning a p + NonZero(Space& home, NonZero& p) + : UnaryPropagator(home,p) {} + /// Constructor for posting + NonZero(Home home, View x) + : UnaryPropagator(home,x) {} + public: + /// Post a non-zero propagator + static ExecStatus post(Home home, View x) { + if ((x.max() < 0.0) || (x.min() > 0.0)) + return ES_OK; + if ((x.min() == 0.0) && (x.max() == 0.0)) + return ES_FAILED; + (void) new (home) NonZero(home,x); + return ES_OK; + } + /// Copy propagator during cloning + virtual Actor* copy(Space& home) { + return new (home) NonZero(home,*this); + } + /// Perform propagation + virtual ExecStatus propagate(Space& home, const ModEventDelta&) { + if ((x0.max() < 0.0) || (x0.min() > 0.0)) + return home.ES_SUBSUMED(*this); + return ((x0.min() == 0.0) && (x0.max() == 0.0)) + ? ES_FAILED : ES_FIX; + } + }; + + /// Return a downward-rounded positive integer power + forceinline FloatNum + positive_pow_down(FloatNum x, int n) { + typedef gecode_boost::numeric::interval_lib::rounded_arith_std + DirectRoundingBase; + typedef gecode_boost::numeric::interval_lib::save_state + DirectRounding; + DirectRounding r; + FloatNum y = 1.0; + while (n > 0) { + if ((n & 1) != 0) + y = r.mul_down(y,x); + n >>= 1; + if (n > 0) + x = r.mul_down(x,x); + } + return y; + } + /// Return nth root for a non-negative interval forceinline FloatVal positive_nroot(const FloatVal& x, int n) { if (((n % 2) == 0) && (x.min() == 0.0)) { + if (x.max() == 0.0) + return FloatVal(0.0,0.0); + + // std::pow is not guaranteed to be correctly rounded. Use it only as + // a seed, and certify the upper bound with outward interval arithmetic. FloatNum u = std::pow(x.max(), 1.0 / static_cast(n)); - return FloatVal(0.0, std::nextafter(u, Limits::max)); + if (!(u > 0.0)) + u = std::nextafter(0.0,Limits::max); + else if (u > Limits::max) + u = Limits::max; + + if (positive_pow_down(u,n) < x.max()) { + FloatNum l = u; + do { + u = (l <= Limits::max / 2.0) ? l * 2.0 : Limits::max; + if (u > Limits::max) + u = Limits::max; + else if (!(u > l)) + u = std::nextafter(l,Limits::max); + if (u == l) + break; + if (positive_pow_down(u,n) < x.max()) + l = u; + else + break; + } while (true); + + // Find the least representable value between the failed lower bound + // and the certified upper bound. This keeps the result tight even if + // the initial approximation was many ulps too small. + while (std::nextafter(l,u) < u) { + FloatNum m = l + (u-l) / 2.0; + if (!(m > l) || !(m < u)) + m = std::nextafter(l,u); + if (positive_pow_down(m,n) < x.max()) + l = m; + else + u = m; + } + } + return FloatVal(0.0,u); } return nroot(x,n); } @@ -58,14 +150,13 @@ namespace Gecode { namespace Float { namespace Arithmetic { ExecStatus Pow::post(Home home, A x0, B x1, int n) { if (n == 0) { - if ((x0.min() == 0.0) && (x0.max() == 0.0)) return ES_FAILED; GECODE_ME_CHECK(x1.eq(home,1.0)); - return ES_OK; + return NonZero::post(home,x0); } GECODE_ME_CHECK(x1.eq(home,pow(x0.domain(),n))); if ((x1.min() == 0.0) && (x1.max() == 0.0)) { - GECODE_ME_CHECK(x1.eq(home,0.0)); + GECODE_ME_CHECK(x0.eq(home,0.0)); return ES_OK; } @@ -88,6 +179,10 @@ namespace Gecode { namespace Float { namespace Arithmetic { GECODE_ME_CHECK(x0.eq(home,nroot(x1.domain(),n))); } + // Inverse propagation can tighten x0. Recompute the forward image before + // an assigned x0 causes the constraint to be discarded. + GECODE_ME_CHECK(x1.eq(home,pow(x0.domain(),n))); + if (!x0.assigned()) (void) new (home) Pow(home,x0,x1,n); return ES_OK; } @@ -106,11 +201,10 @@ namespace Gecode { namespace Float { namespace Arithmetic { template ExecStatus Pow::propagate(Space& home, const ModEventDelta&) { - if ((x0.min() == 0.0) && (x0.max() == 0.0)) return ES_FAILED; GECODE_ME_CHECK(x1.eq(home,pow(x0.domain(),m_n))); if ((x1.min() == 0.0) && (x1.max() == 0.0)) { - GECODE_ME_CHECK(x1.eq(home,0.0)); + GECODE_ME_CHECK(x0.eq(home,0.0)); return home.ES_SUBSUMED(*this); } @@ -132,6 +226,7 @@ namespace Gecode { namespace Float { namespace Arithmetic { } else { GECODE_ME_CHECK(x0.eq(home,nroot(x1.domain(),m_n))); } + GECODE_ME_CHECK(x1.eq(home,pow(x0.domain(),m_n))); return x0.assigned() ? home.ES_SUBSUMED(*this) : ES_NOFIX; } diff --git a/gecode/gist.hh b/gecode/gist.hh index e4f5dd9f6a..73cd71f6d1 100644 --- a/gecode/gist.hh +++ b/gecode/gist.hh @@ -48,7 +48,7 @@ * */ -#if !defined(GECODE_STATIC_LIBS) && \ +#if !defined(GECODE_STATIC_LIBS) && !defined(GIST_STATIC_LIBS) && \ (defined(__CYGWIN__) || defined(__MINGW32__) || defined(_MSC_VER)) #ifdef GECODE_BUILD_GIST diff --git a/gecode/gist/qtgist.hh b/gecode/gist/qtgist.hh index 3cb90f2279..3099c37146 100644 --- a/gecode/gist/qtgist.hh +++ b/gecode/gist/qtgist.hh @@ -42,7 +42,7 @@ * */ -#if !defined(GECODE_STATIC_LIBS) && \ +#if !defined(GECODE_STATIC_LIBS) && !defined(GIST_STATIC_LIBS) && \ (defined(__CYGWIN__) || defined(__MINGW32__) || defined(_MSC_VER)) #ifdef GECODE_BUILD_GIST diff --git a/gecode/third-party/boost/numeric/interval/detail/msvc_rounding_control.hpp.orig b/gecode/third-party/boost/numeric/interval/detail/msvc_rounding_control.hpp.orig index 45597f6d5f..c0dc8fce04 100644 --- a/gecode/third-party/boost/numeric/interval/detail/msvc_rounding_control.hpp.orig +++ b/gecode/third-party/boost/numeric/interval/detail/msvc_rounding_control.hpp.orig @@ -1,7 +1,7 @@ /* Boost interval/detail/msvc_rounding_control.hpp file * * Copyright 2000 Maarten Keijzer - * Copyright 2002 Hervé Brönnimann, Guillaume Melquiond, Sylvain Pion + * Copyright 2002 HervĂ© Brönnimann, Guillaume Melquiond, Sylvain Pion * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or diff --git a/misc/genchangelog.py b/misc/genchangelog.py index 2990ec4614..9bf2db440f 100644 --- a/misc/genchangelog.py +++ b/misc/genchangelog.py @@ -80,6 +80,27 @@ def main() -> int: versions = "" body = "" + def flush_current_release() -> None: + nonlocal versions, body + + sid = f"SectionChanges_{version}" + sid = sid.replace(".", "_") + versions = versions + f'\n - \\ref {sid} "Gecode {version} ({date})"' + body = body + f"\\section {sid} Changes in Version {version} ({date})\n\n" + body = body + f"{info}\n\n" + + for mod in modorder: + if hastext[mod]: + body = body + " - " + modclear[mod] + "\n" + hastext[mod] = 0 + for what in whatorder: + k = f"{mod}-{what}" + if text[k] != "": + body = body + " - " + whatclear[what] + "\n" + body = body + text[k] + text[k] = "" + body = body + "\n\n" + lines = sys.stdin.readlines() idx = 0 current = None @@ -99,23 +120,7 @@ def main() -> int: if re.match(r"^\[RELEASE\]", l): if not first: - sid = f"SectionChanges_{version}" - sid = sid.replace(".", "_") - versions = versions + f'\n - \\ref {sid} "Gecode {version} ({date})"' - body = body + f"\\section {sid} Changes in Version {version} ({date})\n\n" - body = body + f"{info}\n\n" - - for mod in modorder: - if hastext[mod]: - body = body + " - " + modclear[mod] + "\n" - hastext[mod] = 0 - for what in whatorder: - k = f"{mod}-{what}" - if text[k] != "": - body = body + " - " + whatclear[what] + "\n" - body = body + text[k] - text[k] = "" - body = body + "\n\n" + flush_current_release() first = False version = "" @@ -206,12 +211,13 @@ def main() -> int: more = trim_trailing_ws(more) more = more.replace("<", "\\<") more = more.replace(">", "\\>") - more = more.replace("<", "\\<") more = more.replace("&", "\\&") more = re.sub(r"\n([ \t]*)\n", r"\n\1
\n", more) hastext[mod] = 1 rb = rankclear.get(rank, "") + if bug != "": + rb = rb + f", bugzilla entry {bug}" if issue != "": rb = rb + f',
issue {issue}' if thanks != "": @@ -237,6 +243,9 @@ def main() -> int: current = l continue + if not first: + flush_current_release() + sys.stdout.write( f"""/** @@ -245,7 +254,6 @@ def main() -> int: \\section SectionChangeList Changes in Gecode Versions {versions} - - \\ref SectionChanges_1_0_0 "Gecode 1.0.0 (2005-12-06)" \\section SectionChangeWhat Gecode Version Numbers @@ -263,14 +271,6 @@ def main() -> int: ) sys.stdout.write(body) - sys.stdout.write( - """\\section SectionChanges_1_0_0 Changes in Version 1.0.0 (2005-12-06, initial release) - -No changes, of course. - - -""" - ) sys.stdout.write("\n*/\n") return 0 diff --git a/misc/gencurrentchangelog.py b/misc/gencurrentchangelog.py index 1a49f750d8..2537024980 100644 --- a/misc/gencurrentchangelog.py +++ b/misc/gencurrentchangelog.py @@ -21,6 +21,7 @@ def main() -> int: "search": "Search engines", "int": "Finite domain integers", "set": "Finite integer sets", + "float": "Floats", "cpltset": "Finite integer sets with complete representation", "minimodel": "Minimal modeling support", "graph": "Graph constraints", @@ -51,6 +52,7 @@ def main() -> int: "search", "int", "set", + "float", "cpltset", "scheduling", "graph", @@ -72,6 +74,7 @@ def main() -> int: lines = sys.stdin.readlines() idx = 0 current = None + entry_number = 0 while True: if current is None: @@ -131,6 +134,7 @@ def main() -> int: continue if re.match(r"^\[ENTRY\]", l): + entry_number += 1 desc = "" bug = "" rank = "" @@ -175,8 +179,30 @@ def main() -> int: if desc.endswith("\n"): desc = desc[:-1] + if mod not in modclear or mod not in modorder: + print( + f"gencurrentchangelog: entry {entry_number}: " + f"unknown module {mod!r}", + file=sys.stderr, + ) + return 1 + if what not in whatclear or what not in whatorder: + print( + f"gencurrentchangelog: entry {entry_number}: " + f"unknown change type {what!r}", + file=sys.stderr, + ) + return 1 + if rank not in rankclear: + print( + f"gencurrentchangelog: entry {entry_number}: " + f"unknown rank {rank!r}", + file=sys.stderr, + ) + return 1 + hastext[mod] = 1 - rb = rankclear.get(rank, "") + rb = rankclear[rank] if bug != "": rb = rb + f", bugzilla entry {bug}" if thanks != "": diff --git a/misc/tidy.py b/misc/tidy.py new file mode 100644 index 0000000000..592c699390 --- /dev/null +++ b/misc/tidy.py @@ -0,0 +1,517 @@ +#!/usr/bin/env -S uv run --script +# /// script +# requires-python = ">=3.11" +# /// + +"""Run repository formatting and generated-file consistency checks.""" + +from __future__ import annotations + +import argparse +import difflib +import re +import subprocess +import sys +from collections import Counter +from dataclasses import dataclass +from html.parser import HTMLParser +from pathlib import Path + + +ROOT = Path(__file__).resolve().parents[1] +CHANGELOG_IN = ROOT / "changelog.in" +CHANGELOG_HH = ROOT / "changelog.hh" +GEN_CHANGELOG = ROOT / "misc" / "genchangelog.py" +GEN_CURRENT_CHANGELOG = ROOT / "misc" / "gencurrentchangelog.py" + +MODULES = { + "kernel", + "search", + "int", + "set", + "float", + "cpltset", + "minimodel", + "graph", + "scheduling", + "driver", + "iter", + "support", + "example", + "test", + "gist", + "flatzinc", + "other", +} +WHATS = {"bug", "performance", "documentation", "new", "removed", "change"} +RANKS = {"minor", "major"} + + +@dataclass +class Release: + line: int + version: str + date: str + + +@dataclass +class Entry: + line: int + release: Release + module: str + what: str + rank: str + bug: str + issue: str + thanks: str + author: str + description: str + more: str + + +def run(cmd: list[str], *, stdin: str | None = None) -> subprocess.CompletedProcess[str]: + return subprocess.run( + cmd, + cwd=ROOT, + input=stdin, + text=True, + capture_output=True, + check=False, + ) + + +def generate_changelog() -> str: + source = CHANGELOG_IN.read_text() + result = run(["uv", "run", str(GEN_CHANGELOG)], stdin=source) + if result.returncode != 0: + sys.stderr.write(result.stdout) + sys.stderr.write(result.stderr) + raise SystemExit(result.returncode) + return result.stdout + + +def generate_current_changelog(source: str) -> subprocess.CompletedProcess[str]: + return run(["uv", "run", str(GEN_CURRENT_CHANGELOG)], stdin=source) + + +def is_tracked(path: Path) -> bool: + rel = str(path.relative_to(ROOT)) + return run(["git", "ls-files", "--error-unmatch", rel]).returncode == 0 + + +def parse_changelog() -> tuple[list[Release], list[Entry]]: + lines = CHANGELOG_IN.read_text().splitlines(keepends=True) + releases: list[Release] = [] + entries: list[Entry] = [] + current_release: Release | None = None + idx = 0 + + while idx < len(lines): + line = lines[idx] + if line.startswith("#") or not line.strip(): + idx += 1 + continue + + if line.startswith("[RELEASE]"): + start = idx + 1 + idx += 1 + version = "" + date = "" + while idx < len(lines): + current = lines[idx] + idx += 1 + match = re.search(r"Version:[\t ]*(.*)$", current) + if match: + version = match.group(1).rstrip("\n") + match = re.search(r"Date:[\t ]*(.*)$", current) + if match: + date = match.group(1).rstrip("\n") + if "[DESCRIPTION]" in current: + break + while idx < len(lines) and "[ENTRY]" not in lines[idx]: + idx += 1 + current_release = Release(start, version, date) + releases.append(current_release) + continue + + if line.startswith("[ENTRY]"): + if current_release is None: + raise ValueError(f"entry at line {idx + 1} has no preceding release") + + start = idx + 1 + idx += 1 + metadata = { + "module": "", + "what": "", + "rank": "", + "bug": "", + "issue": "", + "thanks": "", + "author": "", + } + fields = { + "module": "Module", + "what": "What", + "rank": "Rank", + "bug": "Bug", + "issue": "Issue", + "thanks": "Thanks", + "author": "Author", + } + while idx < len(lines): + current = lines[idx] + idx += 1 + if "[DESCRIPTION]" in current: + break + for key, field in fields.items(): + match = re.search(rf"{field}:[\t ]*(.*)$", current) + if match: + metadata[key] = match.group(1).rstrip("\n") + + description: list[str] = [] + more: list[str] = [] + in_more = False + while idx < len(lines) and "[ENTRY]" not in lines[idx] and "[RELEASE]" not in lines[idx]: + current = lines[idx] + idx += 1 + if "[MORE]" in current: + in_more = True + continue + (more if in_more else description).append(current) + + entries.append( + Entry( + line=start, + release=current_release, + description="".join(description), + more="".join(more), + **metadata, + ) + ) + continue + + idx += 1 + + return releases, entries + + +def changelog_text(raw: str) -> str: + text = "".join(" " + line for line in raw.splitlines(keepends=True)) + text = re.sub(r"^ ", "", text) + text = re.sub(r"[ \t\n\r]*$", "", text) + text = text.replace("<", "\\<") + text = text.replace(">", "\\>") + text = text.replace("&", "\\&") + return re.sub(r"\n([ \t]*)\n", r"\n\1
\n", text) + + +def generated_section(generated: str, version: str) -> str: + section_id = version.replace(".", "_") + marker = f"\\section SectionChanges_{section_id} " + start = generated.find(marker) + if start < 0: + return "" + end = generated.find("\n\\section SectionChanges_", start + 1) + return generated[start:] if end < 0 else generated[start:end] + + +def validate_changelog_entries(generated: str) -> list[str]: + releases, entries = parse_changelog() + errors: list[str] = [] + + for release in releases: + section_id = release.version.replace(".", "_") + heading = ( + f"\\section SectionChanges_{section_id} " + f"Changes in Version {release.version} ({release.date})" + ) + if heading not in generated: + errors.append(f"line {release.line}: missing generated section for {release.version}") + + for entry in entries: + if entry.module not in MODULES: + errors.append(f"line {entry.line}: unknown module {entry.module!r}") + if entry.what not in WHATS: + errors.append(f"line {entry.line}: unknown change type {entry.what!r}") + if entry.rank not in RANKS: + errors.append(f"line {entry.line}: unknown rank {entry.rank!r}") + + if errors: + return errors + + for entry in entries: + section = generated_section(generated, entry.release.version) + description = changelog_text(entry.description) + position = section.find(description) + if position < 0: + errors.append( + f"line {entry.line}: description missing from generated section " + f"{entry.release.version}" + ) + continue + + tail = section[position : position + len(description) + 800] + expected = [entry.rank] + if entry.bug: + expected.append(f"bugzilla entry {entry.bug}") + if entry.issue: + expected.append(f"issue {entry.issue}") + if entry.thanks: + expected.append(f"thanks to {entry.thanks}") + if entry.author: + expected.append(f"contributed by {entry.author}") + for item in expected: + if item not in tail: + errors.append(f"line {entry.line}: missing generated metadata {item!r}") + + if entry.more and changelog_text(entry.more) not in section: + errors.append(f"line {entry.line}: [MORE] text missing from generated section") + + return errors + + +def current_changelog_text(raw: str) -> str: + description = "" + for line in raw.splitlines(keepends=True): + description += line if not description else " " + line + if description.endswith("\n"): + description = description[:-1] + return description.replace("%Gecode", "Gecode") + + +def normalize_rendered_text(text: str) -> str: + return " ".join(text.split()) + + +def current_description_from_body(body: str) -> str | None: + if not body.endswith(")"): + return None + depth = 0 + for position in range(len(body) - 1, -1, -1): + if body[position] == ")": + depth += 1 + elif body[position] == "(": + depth -= 1 + if depth == 0: + metadata = body[position + 1 : -1] + if re.match(r"^(?:minor|major)(?:,|$)", metadata): + return body[:position].rstrip() + return None + return None + + +class CurrentChangelogEntryParser(HTMLParser): + def __init__(self) -> None: + super().__init__() + self._items: list[tuple[list[str], bool]] = [] + self.entries: list[str] = [] + self.errors: list[str] = [] + + def handle_starttag( + self, tag: str, attrs: list[tuple[str, str | None]] + ) -> None: + del attrs + if tag == "li": + self._items.append(([], False)) + elif tag == "ul" and self._items: + text, _ = self._items[-1] + self._items[-1] = (text, True) + + def handle_data(self, data: str) -> None: + if self._items: + self._items[-1][0].append(data) + + def handle_endtag(self, tag: str) -> None: + if tag != "li" or not self._items: + return + text, has_nested_list = self._items.pop() + if has_nested_list: + return + body = normalize_rendered_text("".join(text)) + description = current_description_from_body(body) + if description is None: + self.errors.append(f"entry has no rank metadata: {body!r}") + return + self.entries.append(description) + + +def rendered_current_descriptions(html: str) -> tuple[Counter[str], list[str]]: + parser = CurrentChangelogEntryParser() + parser.feed(html) + parser.close() + return Counter(parser.entries), parser.errors + + +def compare_current_descriptions( + html: str, expected: list[str], *, context: str +) -> list[str]: + actual, errors = rendered_current_descriptions(html) + expected_counts = Counter(normalize_rendered_text(text) for text in expected) + errors = [f"{context}: {error}" for error in errors] + + for description in sorted(expected_counts.keys() | actual.keys()): + expected_count = expected_counts[description] + actual_count = actual[description] + if actual_count != expected_count: + errors.append( + f"{context}: entry {description!r} appears {actual_count} times, " + f"expected {expected_count}" + ) + return errors + + +def validate_current_changelog() -> list[str]: + source = CHANGELOG_IN.read_text() + result = generate_current_changelog(source) + if result.returncode != 0: + return [ + "current-release changelog generator failed: " + + (result.stderr.strip() or result.stdout.strip()) + ] + + releases, entries = parse_changelog() + if not releases: + return ["changelog has no release for current-release generation"] + current_entries = [entry for entry in entries if entry.release == releases[0]] + expected_descriptions = [ + current_changelog_text(entry.description) for entry in current_entries + ] + errors = compare_current_descriptions( + result.stdout, expected_descriptions, context="current-release HTML" + ) + + regression_source = """[RELEASE] +Version: 1.0.0 +Date: 2000-01-01 +[DESCRIPTION] +Test release. + +[ENTRY] +Module: other +What: change +Rank: minor +[DESCRIPTION] +Short. + +[ENTRY] +Module: other +What: change +Rank: minor +[DESCRIPTION] +Short. longer. + +[ENTRY] +Module: other +What: change +Rank: minor +[DESCRIPTION] +Short. + +[RELEASE] +Version: 0.9.0 +Date: 1999-01-01 +[DESCRIPTION] +Previous release. +""" + regression_result = generate_current_changelog(regression_source) + if regression_result.returncode != 0: + errors.append( + "current-release exact-entry regression failed to generate: " + + (regression_result.stderr.strip() or regression_result.stdout.strip()) + ) + else: + errors.extend( + compare_current_descriptions( + regression_result.stdout, + ["Short.", "Short. longer.", "Short."], + context="current-release exact-entry regression", + ) + ) + + invalid_fields = { + "Module": ("unknown module", "invalid-module"), + "What": ("unknown change type", "invalid-what"), + "Rank": ("unknown rank", "invalid-rank"), + } + for field, (expected_error, value) in invalid_fields.items(): + metadata = { + "Module": "other", + "What": "change", + "Rank": "minor", + } + metadata[field] = value + invalid_source = f"""[RELEASE] +Version: 1.0.0 +Date: 2000-01-01 +[DESCRIPTION] +Test release. + +[ENTRY] +Module: {metadata['Module']} +What: {metadata['What']} +Rank: {metadata['Rank']} +[DESCRIPTION] +Test entry. + +[RELEASE] +Version: 0.9.0 +Date: 1999-01-01 +[DESCRIPTION] +Previous release. +""" + invalid_result = generate_current_changelog(invalid_source) + if invalid_result.returncode == 0 or expected_error not in invalid_result.stderr: + errors.append( + f"current-release generator did not reject invalid {field.lower()}" + ) + + return errors + + +def check_changelog(*, fix: bool) -> int: + generated = generate_changelog() + errors = validate_changelog_entries(generated) + errors.extend(validate_current_changelog()) + + if is_tracked(CHANGELOG_HH): + if CHANGELOG_HH.exists() and CHANGELOG_HH.read_text() == generated: + pass + elif fix: + CHANGELOG_HH.write_text(generated) + else: + current = CHANGELOG_HH.read_text() if CHANGELOG_HH.exists() else "" + diff = difflib.unified_diff( + current.splitlines(keepends=True), + generated.splitlines(keepends=True), + fromfile="changelog.hh", + tofile="generated changelog.hh", + ) + errors.append("changelog.hh is not up to date with changelog.in") + errors.extend(" " + line.rstrip("\n") for line in diff) + elif fix: + CHANGELOG_HH.write_text(generated) + + if errors: + print("Changelog tidy check failed:", file=sys.stderr) + for error in errors: + print(f"- {error}", file=sys.stderr) + return 1 + + print("Changelog tidy check passed") + return 0 + + +def main() -> int: + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument("--check", action="store_true", help="check without modifying tracked files") + parser.add_argument("--fix", action="store_true", help="rewrite generated files where applicable") + args = parser.parse_args() + + if args.check and args.fix: + parser.error("--check and --fix are mutually exclusive") + + return check_changelog(fix=args.fix) + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/test/float/arithmetic.cpp b/test/float/arithmetic.cpp index 4fba78d9ed..7044a2543a 100755 --- a/test/float/arithmetic.cpp +++ b/test/float/arithmetic.cpp @@ -39,6 +39,8 @@ #include #include +#include +#include namespace Test { namespace Float { @@ -50,6 +52,474 @@ namespace Test { namespace Float { * \ingroup TaskTestFloat */ //@{ + /// Regression test for outward positive even-root bounds + class PositiveNRootBounds : public Base { + protected: + /// Space exercising the Pow and NthRoot propagators + class RootSpace : public Gecode::Space { + public: + /// Variables used by the propagator + Gecode::FloatVar x0, x1; + /// Post an extreme Pow or NthRoot instance + RootSpace(Gecode::FloatNum hi, Gecode::FloatNum witness, int n, + bool usePow) + : x0(*this, 0.0, usePow ? witness : hi), + x1(*this, usePow ? 0.0 : witness, usePow ? hi : witness) { + if (usePow) { + Gecode::pow(*this,x0,n,x1); + Gecode::rel(*this,x0,Gecode::FRT_EQ,witness); + } else { + Gecode::nroot(*this,x0,n,x1); + } + } + /// Clone constructor + RootSpace(RootSpace& s) : Gecode::Space(s) { + x0.update(*this,s.x0); + x1.update(*this,s.x1); + } + /// Copy space during cloning + virtual Gecode::Space* copy(void) { + return new RootSpace(*this); + } + }; + + /// Space exposing the propagated NthRoot upper bound + class NRootBoundSpace : public Gecode::Space { + public: + /// Source and root variables + Gecode::FloatVar source, root; + /// Post an NthRoot constraint with an unconstrained root + NRootBoundSpace(Gecode::FloatNum hi, int n) + : source(*this,0.0,hi), + root(*this,0.0,Gecode::Float::Limits::max) { + Gecode::nroot(*this,source,n,root); + } + /// Clone constructor + NRootBoundSpace(NRootBoundSpace& s) : Gecode::Space(s) { + source.update(*this,s.source); + root.update(*this,s.root); + } + /// Copy space during cloning + virtual Gecode::Space* copy(void) { + return new NRootBoundSpace(*this); + } + }; + + /// Check that a known feasible boundary value is not removed + bool check(Gecode::FloatNum hi, Gecode::FloatNum witness, int n, + int mode, bool usePow) const { + if (std::fesetround(mode) != 0) + return true; + RootSpace s(hi,witness,n,usePow); + if (s.status() != Gecode::SS_FAILED) + return true; + olog << "Root bound removed a solution for " + << (usePow ? "pow" : "nroot") << ", n=" << n + << ", mode=" << mode << std::endl; + return false; + } + /// Check that propagation returns a genuinely outward upper bound + bool checkBound(Gecode::FloatNum hi, Gecode::FloatNum rejected, + int n, int mode) const { + if (std::fesetround(mode) != 0) + return true; + NRootBoundSpace s(hi,n); + if ((s.status() != Gecode::SS_FAILED) && + (s.root.max() > rejected)) + return true; + olog << "NRoot upper bound was not outward for n=" << n + << ", mode=" << mode << std::endl; + return false; + } + public: + /// Create and register test + PositiveNRootBounds(void) + : Base("Float::Arithmetic::PositiveNRootBounds") {} + /// Run test under every supported IEEE-754 rounding mode + virtual bool run(void) { + const int oldMode = std::fegetround(); + const int modes[] = { + FE_TONEAREST, FE_DOWNWARD, FE_UPWARD, FE_TOWARDZERO + }; + struct Case { + Gecode::FloatNum hi; + Gecode::FloatNum witness; + int n; + } cases[] = { + {std::ldexp(static_cast(0x156e1fc2f8f359ULL), + -1049), + std::ldexp(static_cast(0x14484bfeebc28cULL), + -152), 10}, + {std::numeric_limits::denorm_min(), + std::ldexp(static_cast(0x18406003b2ae42ULL), + -160), 10}, + {std::numeric_limits::max(), + std::ldexp(static_cast(0x1965fea53d6e0fULL), + 118), 6} + }; + bool result = true; + const Gecode::FloatNum roundingHi = + std::ldexp(static_cast(0x1751def03d6f38ULL), + 458); + const Gecode::FloatNum rejectedUpper = + std::ldexp(static_cast(0x1350f4c43079ceULL), + 203); + for (unsigned int m=0; m::denorm_min(), + positive ? std::numeric_limits::denorm_min() + : -0.0), + y(*this,-1.0,2.0) { + Gecode::pow(*this,x,0,y); + } + /// Clone constructor + AdjacentZeroSpace(AdjacentZeroSpace& s) : Gecode::Space(s) { + x.update(*this,s.x); + y.update(*this,s.y); + } + /// Copy space during cloning + virtual Gecode::Space* copy(void) { + return new AdjacentZeroSpace(*this); + } + }; + + /// Space reproducing a Pow inverse-to-forward fixpoint + class FixpointSpace : public Gecode::Space { + public: + /// Base and result variables + Gecode::FloatVar x, y; + /// Post the fixpoint regression instance + FixpointSpace(void) + : x(*this, + -std::ldexp(static_cast + (0x10000000000006ULL),-53), + -std::ldexp(static_cast + (0x10000000000004ULL),-53)), + y(*this, + std::ldexp(static_cast + (0x1000000000000aULL),-54), + std::ldexp(static_cast + (0x1000000000000cULL),-54)) { + Gecode::pow(*this,x,2,y); + } + /// Clone constructor + FixpointSpace(FixpointSpace& s) : Gecode::Space(s) { + x.update(*this,s.x); + y.update(*this,s.y); + } + /// Copy space during cloning + virtual Gecode::Space* copy(void) { + return new FixpointSpace(*this); + } + }; + + /// Space for direct and delayed assignment of the result to zero + class ZeroResultSpace : public Gecode::Space { + public: + /// Base and result variables + Gecode::FloatVar x, y; + /// Post a power constraint with either a zero or a wide result + ZeroResultSpace(int n, bool direct) + : x(*this,-1.0,1.0), + y(*this,direct ? 0.0 : -1.0,direct ? 0.0 : 1.0) { + Gecode::pow(*this,x,n,y); + } + /// Clone constructor + ZeroResultSpace(ZeroResultSpace& s) : Gecode::Space(s) { + x.update(*this,s.x); + y.update(*this,s.y); + } + /// Copy space during cloning + virtual Gecode::Space* copy(void) { + return new ZeroResultSpace(*this); + } + }; + + /// Test one exponent with direct and delayed zero assignment + bool checkZero(int n) const { + ZeroSpace direct(n,true); + ZeroSpace delayed(n,false); + if (delayed.status() == Gecode::SS_FAILED) { + olog << "Pow wide base failed for n=" << n << std::endl; + return false; + } + ZeroSpace* clone = static_cast(delayed.clone()); + Gecode::rel(*clone,clone->x,Gecode::FRT_EQ,0.0); + if (n == 0) { + const bool zeroRejected = + (direct.status() == Gecode::SS_FAILED) && + (clone->status() == Gecode::SS_FAILED); + delete clone; + ZeroSpace* nonZero = static_cast(delayed.clone()); + Gecode::rel(*nonZero,nonZero->x,Gecode::FRT_EQ,0.5); + const bool nonZeroAccepted = + (nonZero->status() != Gecode::SS_FAILED) && + (nonZero->y.min() == 1.0) && (nonZero->y.max() == 1.0) && + (Gecode::PropagatorGroup::all.size(*nonZero) == 0); + delete nonZero; + if (!zeroRejected || !nonZeroAccepted) + olog << "Pow exponent-zero semantics failed" << std::endl; + return zeroRejected && nonZeroAccepted; + } + const bool result = + (direct.status() != Gecode::SS_FAILED) && + (direct.y.min() == 0.0) && (direct.y.max() == 0.0) && + (clone->status() != Gecode::SS_FAILED) && + (clone->y.min() == 0.0) && (clone->y.max() == 0.0); + delete clone; + if (!result) + olog << "Pow delayed zero failed for n=" << n << std::endl; + return result; + } + /// Test that a zero result forces a zero base + bool checkZeroResult(int n, bool direct) const { + ZeroResultSpace initial(n,direct); + if (initial.status() == Gecode::SS_FAILED) { + olog << "Pow zero result failed for n=" << n << std::endl; + return false; + } + ZeroResultSpace* clone = + static_cast(initial.clone()); + if (!direct) + Gecode::rel(*clone,clone->y,Gecode::FRT_EQ,0.0); + const bool contracted = + (clone->status() != Gecode::SS_FAILED) && + (clone->x.min() == 0.0) && (clone->x.max() == 0.0); + Gecode::rel(*clone,clone->x,Gecode::FRT_EQ,1.0); + const bool inconsistentRejected = + clone->status() == Gecode::SS_FAILED; + delete clone; + if (!contracted || !inconsistentRejected) + olog << "Pow zero result did not force zero base for n=" << n + << (direct ? " (direct)" : " (delayed)") << std::endl; + return contracted && inconsistentRejected; + } + /// Test exponent zero on an adjacent interval containing zero + bool checkAdjacentZero(bool positive) const { + AdjacentZeroSpace initial(positive); + const bool actorRetained = + (initial.status() != Gecode::SS_FAILED) && + (initial.y.min() == 1.0) && (initial.y.max() == 1.0) && + (Gecode::PropagatorGroup::all.size(initial) == 1); + + AdjacentZeroSpace* clone = + static_cast(initial.clone()); + const bool cloneRetained = + (clone->status() != Gecode::SS_FAILED) && + (clone->y.min() == 1.0) && (clone->y.max() == 1.0) && + (Gecode::PropagatorGroup::all.size(*clone) == 1); + delete clone; + + if (!actorRetained || !cloneRetained) + olog << "Pow adjacent exponent-zero semantics failed" + << (positive ? " (positive)" : " (negative)") << std::endl; + return actorRetained && cloneRetained; + } + public: + /// Create and register test + PowConsistency(void) : Base("Float::Arithmetic::PowConsistency") {} + /// Run zero and fixpoint regressions + virtual bool run(void) { + bool result = true; + for (int n=0; n<=3; n++) + result = checkZero(n) && result; + for (int n=1; n<=3; n++) { + result = checkZeroResult(n,true) && result; + result = checkZeroResult(n,false) && result; + } + result = checkAdjacentZero(true) && result; + result = checkAdjacentZero(false) && result; + + FixpointSpace once; + if (once.status() == Gecode::SS_FAILED) { + olog << "Pow fixpoint instance failed" << std::endl; + return false; + } + FixpointSpace* twice = static_cast(once.clone()); + Gecode::pow(*twice,twice->x,2,twice->y); + const bool fixpoint = + (twice->status() != Gecode::SS_FAILED) && + (once.x.min() == twice->x.min()) && + (once.x.max() == twice->x.max()) && + (once.y.min() == twice->y.min()) && + (once.y.max() == twice->y.max()); + delete twice; + if (!fixpoint) + olog << "Pow posting did not reach a fixpoint" << std::endl; + return fixpoint && result; + } + } pow_consistency; + + /// Regression tests for multiplication through a zero endpoint + class MultZeroEndpoint : public Base { + protected: + /// Space exposing multiplication propagation at the public API + class MultSpace : public Gecode::Space { + public: + /// Factors and product + Gecode::FloatVar x, y, z; + /// Post x * y = z for the supplied intervals + MultSpace(Gecode::FloatNum xl, Gecode::FloatNum xu, + Gecode::FloatNum yl, Gecode::FloatNum yu, + Gecode::FloatNum zl, Gecode::FloatNum zu) + : x(*this,xl,xu), y(*this,yl,yu), z(*this,zl,zu) { + Gecode::mult(*this,x,y,z); + } + /// Clone constructor + MultSpace(MultSpace& s) : Gecode::Space(s) { + x.update(*this,s.x); + y.update(*this,s.y); + z.update(*this,s.z); + } + /// Copy space during cloning + virtual Gecode::Space* copy(void) { + return new MultSpace(*this); + } + }; + + /// Check one endpoint-zero sign and magnitude contraction + bool check(Gecode::FloatNum xl, Gecode::FloatNum xu, + Gecode::FloatNum yl, Gecode::FloatNum yu, + Gecode::FloatNum zl, Gecode::FloatNum zu, + Gecode::FloatNum expectedMin, + Gecode::FloatNum expectedMax) const { + MultSpace s(xl,xu,yl,yu,zl,zu); + if ((s.status() == Gecode::SS_FAILED) || + (s.x.min() < expectedMin) || (s.x.max() > expectedMax)) { + olog << "Multiplication endpoint-zero contraction failed: x=" + << s.x << ", y=" << s.y << ", z=" << s.z << std::endl; + return false; + } + MultSpace* clone = static_cast(s.clone()); + Gecode::mult(*clone,clone->x,clone->y,clone->z); + const bool fixpoint = + (clone->status() != Gecode::SS_FAILED) && + (clone->x.min() == s.x.min()) && (clone->x.max() == s.x.max()) && + (clone->y.min() == s.y.min()) && (clone->y.max() == s.y.max()) && + (clone->z.min() == s.z.min()) && (clone->z.max() == s.z.max()); + delete clone; + if (!fixpoint) + olog << "Multiplication endpoint-zero case was not at a fixpoint" + << std::endl; + return fixpoint; + } + /// Exercise supported points across endpoint-zero sign combinations + bool fuzz(void) const { + unsigned int state = 0x6d756c74U; + for (unsigned int i=0; i<5000; i++) { + state = state * 1664525U + 1013904223U; + const Gecode::FloatNum am = + 0.125 + static_cast((state >> 8) % 8000) / + 512.0; + state = state * 1664525U + 1013904223U; + const Gecode::FloatNum bm = + 0.125 + static_cast((state >> 8) % 8000) / + 512.0; + const Gecode::FloatNum a = (i & 1U) ? -am : am; + const Gecode::FloatNum b = (i & 2U) ? -bm : bm; + const Gecode::FloatNum p = a * b; + const Gecode::FloatNum slack = 1.0 + am / 4.0; + const Gecode::FloatNum xl = a - slack; + const Gecode::FloatNum xu = a + slack; + const Gecode::FloatNum yl = (b < 0.0) ? -(bm + 1.0) : 0.0; + const Gecode::FloatNum yu = (b < 0.0) ? -0.0 : bm + 1.0; + MultSpace s(xl,xu,yl,yu,p,p); + if ((s.status() == Gecode::SS_FAILED) || + (s.x.min() > a) || (s.x.max() < a) || + (s.y.min() > b) || (s.y.max() < b) || + (s.z.min() > p) || (s.z.max() < p)) { + olog << "Multiplication removed supported fuzz point " << i + << ": (" << a << ", " << b << ", " << p << ")" + << std::endl; + return false; + } + } + return true; + } + public: + /// Create and register test + MultZeroEndpoint(void) + : Base("Float::Arithmetic::MultZeroEndpoint") {} + /// Run sign, symmetry, signed-zero, and zero-product cases + virtual bool run(void) { + bool result = true; + result = check(-1.0,1.0, 0.0,1.0, 0.5,1.0, + 0.5,1.0) && result; + result = check(-1.0,1.0,-1.0,-0.0, 0.5,1.0, + -1.0,-0.5) && result; + result = check(-1.0,1.0, 0.0,1.0,-1.0,-0.5, + -1.0,-0.5) && result; + result = check(-1.0,1.0,-1.0,-0.0,-1.0,-0.5, + 0.5,1.0) && result; + + // Swapping the factors must recover the same contraction. + MultSpace swapped(0.0,1.0,-1.0,1.0,0.5,1.0); + result = (swapped.status() != Gecode::SS_FAILED) && + (swapped.y.min() >= 0.5) && result; + + // A zero product gives no sign information about the other factor. + MultSpace zeroProduct(-1.0,1.0,0.0,1.0,0.0,0.0); + result = (zeroProduct.status() != Gecode::SS_FAILED) && + (zeroProduct.x.min() == -1.0) && + (zeroProduct.x.max() == 1.0) && result; + MultSpace zeroFactor(0.0,0.0,-1.0,1.0,0.0,0.0); + result = (zeroFactor.status() != Gecode::SS_FAILED) && + (zeroFactor.y.min() == -1.0) && + (zeroFactor.y.max() == 1.0) && result; + if (!result) + olog << "Multiplication zero or swapped regression failed" + << std::endl; + return fuzz() && result; + } + } mult_zero_endpoint; + /// %Test for multiplication constraint class MultXYZ : public Test { public: