diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..42b43ad --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,179 @@ +name: Build & Package + +# Build FindMUS against a pinned libminizinc release (see +# scripts/fetch_minizinc.sh for how that release is chosen) and publish it, so the +# MiniZinc IDE can bundle a build that matches the compiler it ships. +# +# Platforms match libminizinc's, so every published compiler has a matching build. + +on: + push: + branches: [develop] + tags: ["*"] + pull_request: {} + workflow_dispatch: + inputs: + minizinc_release: + description: "libminizinc release to build against (blank = tag or edge)." + type: string + default: "" + +concurrency: + group: ci-${{ github.ref }} + cancel-in-progress: true + +permissions: + contents: read + +env: + GH_TOKEN: ${{ github.token }} + # Honoured by scripts/fetch_minizinc.sh; blank means "resolve from the ref". + MZN_RELEASE: ${{ github.event.inputs.minizinc_release }} + +jobs: + build: + name: build (${{ matrix.platform }}) + strategy: + fail-fast: false + matrix: + include: + - { platform: linux, triple: x86_64-linux-gnu, runner: ubuntu-24.04, container: "quay.io/pypa/manylinux_2_28_x86_64", setup: "", generator: "Unix Makefiles", cmakedir: "lib64/cmake" } + - { platform: linux-arm64, triple: aarch64-linux-gnu, runner: ubuntu-24.04-arm, container: "quay.io/pypa/manylinux_2_28_aarch64", setup: "", generator: "Unix Makefiles", cmakedir: "lib64/cmake" } + - { platform: musl, triple: x86_64-linux-musl, runner: ubuntu-24.04, container: "alpine:3.20", setup: "apk add --no-cache bash build-base cmake ninja git zlib-dev", generator: "Ninja", cmakedir: "lib/cmake" } + - { platform: musl-arm64, triple: aarch64-linux-musl, runner: ubuntu-24.04-arm, container: "alpine:3.20", setup: "apk add --no-cache bash build-base cmake ninja git zlib-dev", generator: "Ninja", cmakedir: "lib/cmake" } + - { platform: osx, triple: aarch64-apple-darwin, runner: macos-14, container: "", setup: "", generator: "Ninja", cmakedir: "lib/cmake" } + - { platform: osx-intel, triple: x86_64-apple-darwin, runner: macos-26-intel, container: "", setup: "", generator: "Ninja", cmakedir: "lib/cmake" } + - { platform: win64, triple: x86_64-windows, runner: windows-2022, container: "", setup: "", generator: "Ninja", cmakedir: "CMake" } + - { platform: win64-arm, triple: aarch64-windows, runner: windows-11-arm, container: "", setup: "", generator: "Ninja", cmakedir: "CMake" } + runs-on: ${{ matrix.runner }} + steps: + - uses: actions/checkout@v7 + + - name: Fetch libminizinc + Gecode + shell: bash + run: bash scripts/fetch_minizinc.sh ${{ matrix.triple }} + + - uses: TheMrMilchmann/setup-msvc-dev@v4.1.0 + if: startsWith(matrix.platform, 'win64') + with: + arch: ${{ matrix.platform == 'win64-arm' && 'arm64' || 'x64' }} + + # Containers match the compiler's: manylinux_2_28 for the glibc 2.28 floor, + # alpine for musl. `setup` installs what the bare image lacks. + - name: Configure & build + shell: bash + env: + # MSVC reports __cplusplus as 199711L unless asked not to, so Gecode's + # bundled Boost cannot pick a rounding-control implementation on ARM64. + CXXFLAGS: ${{ matrix.platform == 'win64-arm' && '/Zc:__cplusplus' || '' }} + # Matches minizinc-vendor and libminizinc's own per-platform choice; + # ignored (and harmless) on every non-macOS platform. + CMAKE_OSX_ARCHITECTURES: ${{ matrix.platform == 'osx-intel' && 'x86_64' || 'arm64' }} + run: | + set -eux + SETUP='${{ matrix.setup }}' + if [ -n "${{ matrix.container }}" ]; then + docker run --rm -v "$PWD:/work" -w /work \ + -e ROOT=/work \ + -e CMAKE_GENERATOR="${{ matrix.generator }}" \ + -e CMAKEDIR="${{ matrix.cmakedir }}" \ + -e CMAKE_OSX_ARCHITECTURES="$CMAKE_OSX_ARCHITECTURES" \ + "${{ matrix.container }}" \ + sh -c "${SETUP}${SETUP:+ && }bash scripts/ci_build.sh" + else + ROOT="$PWD" \ + CMAKE_GENERATOR="${{ matrix.generator }}" \ + CMAKEDIR="${{ matrix.cmakedir }}" \ + CMAKE_OSX_ARCHITECTURES="$CMAKE_OSX_ARCHITECTURES" \ + bash scripts/ci_build.sh + fi + + - name: Package + shell: bash + run: tar -czf "findMUS-${{ matrix.triple }}.tar.gz" -C findMUS . + + - uses: actions/upload-artifact@v7 + with: + name: findMUS-${{ matrix.platform }} + path: findMUS-${{ matrix.triple }}.tar.gz + + publish: + name: publish release + needs: build + if: github.event_name != 'pull_request' && (github.ref == 'refs/heads/develop' || startsWith(github.ref, 'refs/tags/')) + runs-on: ubuntu-24.04 + permissions: + contents: write + env: + GH_TOKEN: ${{ github.token }} + steps: + - uses: actions/checkout@v7 + - uses: actions/download-artifact@v8 + with: + path: artifacts + - name: Publish + shell: bash + run: | + set -eux + mkdir -p upload + cp artifacts/findMUS-*/findMUS-*.tar.gz upload/ + if [ "${{ startsWith(github.ref, 'refs/tags/') }}" = "true" ]; then + TAG="${{ github.ref_name }}" + # Only create it if absent; anything else is a real error. + if ! gh release view "$TAG" >/dev/null 2>&1; then + gh release create "$TAG" --title "FindMUS $TAG" --verify-tag \ + --notes "Built against the MiniZinc $TAG release." + fi + else + TAG=edge + # Rolling prerelease, updated in place: deleting and recreating it would + # notify every watcher on each develop build. + git config user.name "github-actions[bot]" + git config user.email "41898282+github-actions[bot]@users.noreply.github.com" + git tag -f -a -m "Development build of FindMUS" "$TAG" "$GITHUB_SHA" + git push -f origin "refs/tags/$TAG" + NOTES="Continuously updated build of the latest development version, made against the MiniZinc edge release." + if gh release view "$TAG" >/dev/null 2>&1; then + gh release edit "$TAG" --prerelease --title "FindMUS (edge)" --notes "$NOTES" + else + gh release create "$TAG" --prerelease --title "FindMUS (edge)" --notes "$NOTES" + fi + fi + gh release upload "$TAG" upload/* --clobber + + # Next link in the develop chain: libminizinc -> mzn-analyse -> FindMUS -> + # MiniZincIDE. Fires only after this repo's own assets are published, so the + # next one never builds against a half-updated upstream. + # Runs on tags too: this is the last repository the CLI bundles depend on, so by + # the time it has published, every input they need exists at that tag. + notify-downstream: + name: trigger the IDE and the CLI bundles + needs: publish + if: github.event_name != 'pull_request' && (github.ref == 'refs/heads/develop' || startsWith(github.ref, 'refs/tags/')) + runs-on: ubuntu-24.04 + steps: + - uses: actions/create-github-app-token@v3 + id: app-token + with: + app-id: ${{ secrets.MINIZINC_BOT_APP_ID }} + private-key: ${{ secrets.MINIZINC_BOT_APP_KEY }} + owner: MiniZinc + repositories: MiniZincIDE, libminizinc + - name: Dispatch + env: + GH_TOKEN: ${{ steps.app-token.outputs.token }} + run: | + # workflow_dispatch, not repository_dispatch: the latter always runs the + # default branch's workflow, so it could not rebuild develop. + # + # Not on tags: a tagged release is cut by hand, one repository at a + # time, so the IDE gets tagged when its turn comes. + if [ "$GITHUB_REF_TYPE" != tag ]; then + gh workflow run ci.yml --repo MiniZinc/MiniZincIDE --ref "$GITHUB_REF_NAME" \ + -f minizinc_release=edge + fi + + rel=edge + [ "$GITHUB_REF_TYPE" = tag ] && rel="$GITHUB_REF_NAME" + gh workflow run bundle.yml --repo MiniZinc/libminizinc --ref "$GITHUB_REF_NAME" \ + -f release="$rel" diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml deleted file mode 100644 index 1932566..0000000 --- a/.gitlab-ci.yml +++ /dev/null @@ -1,64 +0,0 @@ -stages: - - build - - trigger - -.build: - stage: build - script: - - sh ${VENDOR_SCRIPT} minizinc-vendor master vendor:${MZNARCH} vendor.zip - - unzip -q vendor.zip - - sh ${VENDOR_SCRIPT} minizinc develop build:${MZNARCH} minizinc.zip - - unzip -q minizinc.zip - - cmake -S . -B build -G"$CMAKE_ARCH" -DCMAKE_BUILD_TYPE=Release -DUSE_SYSTEM_LIBMINIZINC=1 -Dlibminizinc_DIR=$CI_PROJECT_DIR/minizinc/$CMAKEDIR/libminizinc -DGecode_ROOT="$CI_PROJECT_DIR/vendor/gecode" -DOsiCBC_ROOT="$CI_PROJECT_DIR/vendor/cbc" -DHIGHS_DIR="$CI_PROJECT_DIR/vendor/highs/$CMAKEDIR/highs" -DCMAKE_INSTALL_PREFIX="$CI_PROJECT_DIR/findMUS" - - cmake --build build --config Release --target install - artifacts: - paths: - - findMUS/ - cache: - key: "$CI_JOB_NAME" - paths: [minizinc.zip*, vendor.zip*] - -build:linux: - extends: .build - image: ghcr.io/minizinc/docker-build-environment:cpp - variables: - MZNARCH: "linux" - CMAKE_ARCH: "Ninja" - CMAKEDIR: "lib64/cmake" - tags: [linux, docker] - -build:osx: - extends: .build - variables: - MZNARCH: "osx" - CMAKE_ARCH: "Ninja" - CMAKEDIR: "lib/cmake" - MACOSX_DEPLOYMENT_TARGET: "10.9" - CMAKE_OSX_ARCHITECTURES: "x86_64;arm64" - tags: [osx, cmake, cpp] - -build:win64: - extends: .build - variables: - MZNARCH: "win64" - CMAKEDIR: "CMake/libminizinc" - CMAKE_ARCH: "Ninja" - script: - - sh %VENDOR_SCRIPT% minizinc-vendor master vendor:%MZNARCH% vendor.zip - - unzip -q vendor.zip - - sh %VENDOR_SCRIPT% minizinc develop build:%MZNARCH% minizinc.zip - - unzip -q minizinc.zip - - cmake -S . -B build -G"%CMAKE_ARCH%" -DCMAKE_BUILD_TYPE=Release -DUSE_SYSTEM_LIBMINIZINC=1 -Dlibminizinc_DIR="%CI_PROJECT_DIR%/minizinc/%CMAKEDIR%" -DGecode_ROOT="%CI_PROJECT_DIR%/vendor/gecode" -DOsiCBC_ROOT="%CI_PROJECT_DIR%/vendor/cbc" -DHIGHS_DIR="%CI_PROJECT_DIR%/vendor/highs/lib/cmake/highs" -DCMAKE_INSTALL_PREFIX="%CI_PROJECT_DIR%/findMUS" - - cmake --build build --config Release --target install - cache: - key: "build_win64" - tags: [win64, cmake, cpp] - -# ----------- Trigger mzn-analyse pipeline ----------- - -trigger:mzn-analyse: - stage: trigger - trigger: - project: minizinc/mzn-analyse - branch: develop - only: [develop] diff --git a/mapsolvers/minisat/minisat/utils/System.h b/mapsolvers/minisat/minisat/utils/System.h index a51d4c2..5810191 100644 --- a/mapsolvers/minisat/minisat/utils/System.h +++ b/mapsolvers/minisat/minisat/utils/System.h @@ -21,7 +21,9 @@ OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWA #ifndef Minisat_System_h #define Minisat_System_h -#if defined(__linux__) +// musl has no fpu_control.h. The only use of it (System.cc) is already guarded +// on the macros this header defines, so skipping it costs nothing there. +#if defined(__linux__) && __has_include() #include #endif diff --git a/scripts/ci_build.sh b/scripts/ci_build.sh new file mode 100755 index 0000000..f43f3bc --- /dev/null +++ b/scripts/ci_build.sh @@ -0,0 +1,22 @@ +#!/usr/bin/env bash +# Configure, build and install findMUS into $ROOT/findMUS, against the +# libminizinc SDK and Gecode already in $ROOT (see fetch_minizinc.sh). +# +# Env: ROOT, CMAKE_GENERATOR, CMAKE_OSX_ARCHITECTURES, CMAKEDIR (where the SDK +# keeps its CMake packages: lib64/cmake on manylinux, lib/cmake on macOS, +# CMake on Windows) +set -eux + +: "${ROOT:?ROOT must be set}" +: "${CMAKEDIR:?CMAKEDIR must be set}" + +cmake -S "$ROOT" -B "$ROOT/build" -G "${CMAKE_GENERATOR:-Ninja}" \ + -DCMAKE_BUILD_TYPE=Release \ + -DUSE_SYSTEM_LIBMINIZINC=1 \ + -Dlibminizinc_DIR="$ROOT/minizinc/${CMAKEDIR}/libminizinc" \ + -DGecode_ROOT="$ROOT/vendor/gecode" \ + -DOsiCBC_ROOT="$ROOT/vendor/cbc" \ + -DCMAKE_INSTALL_PREFIX="$ROOT/findMUS" \ + -DCMAKE_OSX_ARCHITECTURES="${CMAKE_OSX_ARCHITECTURES:-arm64}" \ + -DCMAKE_OSX_DEPLOYMENT_TARGET="12.0" +cmake --build "$ROOT/build" --config Release --target install diff --git a/scripts/fetch_minizinc.sh b/scripts/fetch_minizinc.sh new file mode 100755 index 0000000..952132c --- /dev/null +++ b/scripts/fetch_minizinc.sh @@ -0,0 +1,50 @@ +#!/usr/bin/env bash +# Fetch what findMUS builds against: the libminizinc SDK and Gecode. +# +# Usage: fetch_minizinc.sh +# +# Release selection: a tagged build uses the libminizinc release with the same +# tag, anything else uses `edge` (republished on every libminizinc develop push). +# Override with MZN_RELEASE. +# +# `minizinc-compiler-only` is the full install tree, headers and CMake package +# included. Gecode and CBC come from that release's vendor.lock: libminizinc's +# exported package does find_dependency on both, so they have to be the versions +# it was built against. +# +# Requires: gh (via $GH_TOKEN), tar. +set -euxo pipefail + +TRIPLE="${1:?usage: fetch_minizinc.sh }" +MZN_REPO="${MZN_REPO:-MiniZinc/libminizinc}" +VENDOR_REPO="${VENDOR_REPO:-MiniZinc/minizinc-vendor}" +here="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" +cd "$here" + +if [ -n "${MZN_RELEASE:-}" ]; then + ref="$MZN_RELEASE" +elif [ "${GITHUB_REF_TYPE:-}" = "tag" ]; then + ref="${GITHUB_REF_NAME}" +else + ref="edge" +fi + +mkdir -p minizinc vendor +gh release download "$ref" --repo "$MZN_REPO" \ + --pattern "minizinc-compiler-only-${TRIPLE}.tar.gz" --dir . --clobber +tar -xzf "minizinc-compiler-only-${TRIPLE}.tar.gz" -C minizinc +rm -f "minizinc-compiler-only-${TRIPLE}.tar.gz" + +gh release download "$ref" --repo "$MZN_REPO" --pattern "vendor.lock" --dir . --clobber + +deps=gecode +case "$TRIPLE" in aarch64-windows) ;; *) deps="$deps cbc" ;; esac + +for dep in $deps; do + ver="$(grep -E "^${dep}=" vendor.lock | head -1 | cut -d= -f2-)" + [ -n "$ver" ] || { echo "no version pinned for '$dep' in vendor.lock" >&2; exit 1; } + asset="${dep}-${ver}-${TRIPLE}.tar.gz" + gh release download "${dep}-${ver}" --repo "$VENDOR_REPO" --pattern "$asset" --dir . --clobber + tar -xzf "$asset" -C vendor + rm -f "$asset" +done