From ba3fd6ed46bffa06aa5f4c5e5156fc7a1ec7f258 Mon Sep 17 00:00:00 2001 From: Per Stark Date: Tue, 23 Jun 2026 10:38:30 +0200 Subject: [PATCH] fix: skip CI for release build since release action runs checks and naming --- .github/workflows/ci.yml | 5 +- .github/workflows/release.yml | 371 +++++++++++----------------------- devenv.nix | 1 - dist-workspace.toml | 24 --- docs/installation.md | 7 +- flake.lock | 39 ++++ flake.nix | 251 +++++++++++++++++++++-- nix/minne-release.nix | 249 +++++++++++++++++++++++ nix/windows-cross.nix | 171 ++++++++++++++++ 9 files changed, 814 insertions(+), 304 deletions(-) delete mode 100644 dist-workspace.toml create mode 100644 nix/minne-release.nix create mode 100644 nix/windows-cross.nix diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 6286793..17b4035 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -11,7 +11,8 @@ on: jobs: check: name: Format, lint, build & test - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 + if: ${{ github.event_name == 'workflow_dispatch' || !startsWith(github.event.head_commit.message, 'release:') }} steps: - uses: actions/checkout@v4 @@ -24,7 +25,7 @@ jobs: with: primary-key: nix-${{ runner.os }}-${{ hashFiles('**/*.nix', '**/flake.lock', 'Cargo.lock') }} restore-prefixes-first-match: nix-${{ runner.os }}- - gc-max-store-size-linux: 10G + gc-max-store-size-linux: 5G - name: Check formatting, clippy lint, unit tests & ort version run: nix flake check --show-trace diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index ebbef07..d60d5f2 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -10,16 +10,12 @@ on: - "**[0-9]+.[0-9]+.[0-9]+*" jobs: - plan: - runs-on: ubuntu-22.04 + ci: + runs-on: ubuntu-24.04 outputs: - val: ${{ steps.plan.outputs.manifest }} tag: ${{ !github.event.pull_request && github.ref_name || '' }} - tag-flag: ${{ !github.event.pull_request && format('--tag={0}', github.ref_name) || '' }} publishing: ${{ !github.event.pull_request }} ort-version: ${{ steps.ort_version.outputs.value }} - env: - GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} steps: - uses: actions/checkout@v4 with: @@ -32,171 +28,85 @@ jobs: with: primary-key: nix-${{ runner.os }}-${{ hashFiles('**/*.nix', '**/flake.lock', 'Cargo.lock') }} restore-prefixes-first-match: nix-${{ runner.os }}- - gc-max-store-size-linux: 10G + gc-max-store-size-linux: 5G - name: Read ORT version from flake id: ort_version run: echo "value=$(nix eval .#lib.ortVersion --raw)" >> "$GITHUB_OUTPUT" - - name: Verify ort-version matches nixpkgs onnxruntime + - name: Run nix flake check run: nix flake check --system x86_64-linux - - name: Install dist - shell: bash - run: "curl --proto '=https' --tlsv1.2 -LsSf https://github.com/axodotdev/cargo-dist/releases/download/v0.30.3/cargo-dist-installer.sh | sh" - - - name: Cache dist - uses: actions/upload-artifact@v4 - with: - name: cargo-dist-cache - path: ~/.cargo/bin/dist - - - id: plan - run: | - dist ${{ (!github.event.pull_request && format('host --steps=create --tag={0}', github.ref_name)) || 'plan' }} --output-format=json > plan-dist-manifest.json - echo "dist ran successfully" - cat plan-dist-manifest.json - echo "manifest=$(jq -c . plan-dist-manifest.json)" >> "$GITHUB_OUTPUT" - - - name: Upload dist-manifest.json - uses: actions/upload-artifact@v4 - with: - name: artifacts-plan-dist-manifest - path: plan-dist-manifest.json - - build-local-artifacts: - name: build-local-artifacts (${{ join(matrix.targets, ', ') }}) - needs: [plan] - if: ${{ fromJson(needs.plan.outputs.val).ci.github.artifacts_matrix.include != null && (needs.plan.outputs.publishing == 'true' || fromJson(needs.plan.outputs.val).ci.github.pr_run_mode == 'upload') }} + build-nix-artifacts: + name: build (${{ matrix.triple }}) + needs: [ci] + if: ${{ needs.ci.outputs.publishing == 'true' }} strategy: fail-fast: false - matrix: ${{ fromJson(needs.plan.outputs.val).ci.github.artifacts_matrix }} + matrix: + include: + - runner: ubuntu-24.04 + triple: x86_64-unknown-linux-gnu + nix_package: minne-release + cache_save: false + - runner: macos-latest + triple: aarch64-apple-darwin + nix_package: minne-release + cache_save: true + - runner: ubuntu-24.04 + triple: x86_64-pc-windows-msvc + nix_package: minne-release-windows + cache_save: false runs-on: ${{ matrix.runner }} - container: ${{ matrix.container && matrix.container.image || null }} - env: - GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - BUILD_MANIFEST_NAME: target/distrib/${{ join(matrix.targets, '-') }}-dist-manifest.json steps: - - name: enable windows longpaths - run: git config --global core.longpaths true - - uses: actions/checkout@v4 with: submodules: recursive - - name: Load ONNX Runtime version - shell: bash - run: echo "ORT_VER=${{ needs.plan.outputs.ort-version }}" >> "$GITHUB_ENV" + - name: Install Nix + uses: DeterminateSystems/determinate-nix-action@v3 - - name: Install Rust non-interactively if not already installed - if: ${{ matrix.container }} + - uses: nix-community/cache-nix-action@v7 + with: + primary-key: nix-${{ runner.os }}-${{ hashFiles('**/*.nix', '**/flake.lock', 'Cargo.lock') }} + restore-prefixes-first-match: nix-${{ runner.os }}- + gc-max-store-size-linux: 5G + gc-max-store-size-darwin: 5G + save: ${{ matrix.cache_save }} + + - name: Build release archive (Nix) + run: nix build .#${{ matrix.nix_package }} -L --out-link minne-release + + - name: Stage artifact + shell: bash run: | - if ! command -v cargo > /dev/null 2>&1; then - curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y - echo "$HOME/.cargo/bin" >> $GITHUB_PATH + set -euo pipefail + TRIPLE="${{ matrix.triple }}" + if [[ "$TRIPLE" == *windows* ]]; then + ARTIFACT="main-${TRIPLE}.zip" + else + ARTIFACT="main-${TRIPLE}.tar.xz" + fi + RELEASE="$(nix path-info ./minne-release)" + cp "$RELEASE/${ARTIFACT}" "$ARTIFACT" + if command -v sha256sum >/dev/null; then + sha256sum "$ARTIFACT" > "${ARTIFACT}.sha256" + else + shasum -a 256 "$ARTIFACT" > "${ARTIFACT}.sha256" fi - - name: Install dist - run: ${{ matrix.install_dist.run }} - - - name: Fetch local artifacts - uses: actions/download-artifact@v4 - with: - pattern: artifacts-* - path: target/distrib/ - merge-multiple: true - - # ===== BEGIN: Injected ORT staging for cargo-dist bundling ===== - - run: echo "=== BUILD-SETUP START ===" - - # Unix shells - - name: Prepare lib dir (Unix) - if: runner.os != 'Windows' - shell: bash - run: | - mkdir -p lib - rm -f lib/* - - # Windows PowerShell - - name: Prepare lib dir (Windows) - if: runner.os == 'Windows' - shell: pwsh - run: | - New-Item -ItemType Directory -Force -Path lib | Out-Null - # remove contents if any - Get-ChildItem -Path lib -Force | Remove-Item -Force -Recurse -ErrorAction SilentlyContinue - - - name: Fetch ONNX Runtime (Linux) - if: runner.os == 'Linux' - run: | - set -euo pipefail - ARCH="$(uname -m)" - case "$ARCH" in - x86_64) URL="https://github.com/microsoft/onnxruntime/releases/download/v${ORT_VER}/onnxruntime-linux-x64-${ORT_VER}.tgz" ;; - aarch64) URL="https://github.com/microsoft/onnxruntime/releases/download/v${ORT_VER}/onnxruntime-linux-aarch64-${ORT_VER}.tgz" ;; - *) echo "Unsupported arch $ARCH"; exit 1 ;; - esac - curl -fsSL -o ort.tgz "$URL" - tar -xzf ort.tgz - cp -v onnxruntime-*/lib/libonnxruntime.so* lib/ - # normalize to stable name if needed - [ -f lib/libonnxruntime.so ] || cp -v lib/libonnxruntime.so.* lib/libonnxruntime.so - - - name: Fetch ONNX Runtime (macOS) - if: runner.os == 'macOS' - run: | - set -euo pipefail - curl -fsSL -o ort.tgz "https://github.com/microsoft/onnxruntime/releases/download/v${ORT_VER}/onnxruntime-osx-universal2-${ORT_VER}.tgz" - tar -xzf ort.tgz - cp -v onnxruntime-*/lib/libonnxruntime*.dylib lib/ - [ -f lib/libonnxruntime.dylib ] || cp -v lib/libonnxruntime*.dylib lib/libonnxruntime.dylib - - - name: Fetch ONNX Runtime (Windows) - if: runner.os == 'Windows' - shell: pwsh - run: | - $url = "https://github.com/microsoft/onnxruntime/releases/download/v$env:ORT_VER/onnxruntime-win-x64-$env:ORT_VER.zip" - Invoke-WebRequest $url -OutFile ort.zip - Expand-Archive ort.zip -DestinationPath ort - $dll = Get-ChildItem -Recurse -Path ort -Filter onnxruntime.dll | Select-Object -First 1 - Copy-Item $dll.FullName lib\onnxruntime.dll - - - run: | - echo "=== BUILD-SETUP END ===" - echo "lib/ contents:" - ls -l lib || dir lib - # ===== END: Injected ORT staging ===== - - name: Install dependencies - run: | - ${{ matrix.packages_install }} - - - name: Build artifacts - run: | - dist build ${{ needs.plan.outputs.tag-flag }} --print=linkage --output-format=json ${{ matrix.dist_args }} > dist-manifest.json - echo "dist ran successfully" - - - id: cargo-dist - name: Post-build - shell: bash - run: | - echo "paths<> "$GITHUB_OUTPUT" - dist print-upload-files-from-manifest --manifest dist-manifest.json >> "$GITHUB_OUTPUT" - echo "EOF" >> "$GITHUB_OUTPUT" - cp dist-manifest.json "$BUILD_MANIFEST_NAME" - - - name: Upload artifacts + - name: Upload artifact uses: actions/upload-artifact@v4 with: - name: artifacts-build-local-${{ join(matrix.targets, '_') }} + name: release-${{ matrix.triple }} path: | - ${{ steps.cargo-dist.outputs.paths }} - ${{ env.BUILD_MANIFEST_NAME }} + main-${{ matrix.triple }}.* build_and_push_docker_image: name: Build and Push Docker Image (Nix) - runs-on: ubuntu-latest - needs: [plan] - if: ${{ needs.plan.outputs.publishing == 'true' }} + runs-on: ubuntu-24.04 + needs: [ci] + if: ${{ needs.ci.outputs.publishing == 'true' }} permissions: contents: read id-token: write @@ -214,7 +124,8 @@ jobs: with: primary-key: nix-${{ runner.os }}-${{ hashFiles('**/*.nix', '**/flake.lock', 'Cargo.lock') }} restore-prefixes-first-match: nix-${{ runner.os }}- - gc-max-store-size-linux: 10G + gc-max-store-size-linux: 5G + save: false - name: Build Docker image with Nix run: nix build .#dockerImage -L --show-trace @@ -226,134 +137,84 @@ jobs: username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} - - name: Extract Docker metadata - id: meta - uses: docker/metadata-action@v5 - with: - images: ghcr.io/${{ github.repository }} - - name: Load and push Docker image env: IMAGE_NAME: ghcr.io/${{ github.repository }} - IMAGE_TAG: ${{ needs.plan.outputs.tag }} + IMAGE_TAG: ${{ needs.ci.outputs.tag }} run: | - docker load < result - docker tag "minne:1.0.3" "$IMAGE_NAME:$IMAGE_TAG" - docker tag "minne:1.0.3" "$IMAGE_NAME:latest" + set -euo pipefail + LOADED_IMAGE="$(docker load < result | awk '/Loaded image:/ {print $3; exit}')" + if [ -z "$LOADED_IMAGE" ]; then + echo "failed to load docker image from nix result" >&2 + exit 1 + fi + docker tag "$LOADED_IMAGE" "$IMAGE_NAME:$IMAGE_TAG" + docker tag "$LOADED_IMAGE" "$IMAGE_NAME:latest" docker push "$IMAGE_NAME:$IMAGE_TAG" docker push "$IMAGE_NAME:latest" - build-global-artifacts: - needs: [plan, build-local-artifacts] - runs-on: ubuntu-22.04 + release: + name: Create GitHub Release + needs: [ci, build-nix-artifacts, build_and_push_docker_image] + if: ${{ needs.ci.outputs.publishing == 'true' }} + runs-on: ubuntu-24.04 env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - BUILD_MANIFEST_NAME: target/distrib/global-dist-manifest.json steps: - uses: actions/checkout@v4 with: submodules: recursive - - name: Install cached dist + - name: Download release artifacts uses: actions/download-artifact@v4 with: - name: cargo-dist-cache - path: ~/.cargo/bin/ - - run: chmod +x ~/.cargo/bin/dist - - - name: Fetch local artifacts - uses: actions/download-artifact@v4 - with: - pattern: artifacts-* - path: target/distrib/ - merge-multiple: true - - - id: cargo-dist - shell: bash - run: | - dist build ${{ needs.plan.outputs.tag-flag }} --output-format=json "--artifacts=global" > dist-manifest.json - echo "dist ran successfully" - echo "paths<> "$GITHUB_OUTPUT" - jq --raw-output ".upload_files[]" dist-manifest.json >> "$GITHUB_OUTPUT" - echo "EOF" >> "$GITHUB_OUTPUT" - cp dist-manifest.json "$BUILD_MANIFEST_NAME" - - - name: Upload artifacts - uses: actions/upload-artifact@v4 - with: - name: artifacts-build-global - path: | - ${{ steps.cargo-dist.outputs.paths }} - ${{ env.BUILD_MANIFEST_NAME }} - - host: - needs: [plan, build-local-artifacts, build-global-artifacts] - if: ${{ always() && needs.plan.outputs.publishing == 'true' && (needs.build-global-artifacts.result == 'skipped' || needs.build-global-artifacts.result == 'success') && (needs.build-local-artifacts.result == 'skipped' || needs.build-local-artifacts.result == 'success') }} - env: - GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - runs-on: ubuntu-22.04 - outputs: - val: ${{ steps.host.outputs.manifest }} - steps: - - uses: actions/checkout@v4 - with: - submodules: recursive - - - name: Install cached dist - uses: actions/download-artifact@v4 - with: - name: cargo-dist-cache - path: ~/.cargo/bin/ - - run: chmod +x ~/.cargo/bin/dist - - - name: Fetch artifacts - uses: actions/download-artifact@v4 - with: - pattern: artifacts-* - path: target/distrib/ - merge-multiple: true - - - id: host - shell: bash - run: | - dist host ${{ needs.plan.outputs.tag-flag }} --steps=upload --steps=release --output-format=json > dist-manifest.json - echo "artifacts uploaded and released successfully" - cat dist-manifest.json - echo "manifest=$(jq -c . dist-manifest.json)" >> "$GITHUB_OUTPUT" - - - name: Upload dist-manifest.json - uses: actions/upload-artifact@v4 - with: - name: artifacts-dist-manifest - path: dist-manifest.json - - - name: Download GitHub Artifacts - uses: actions/download-artifact@v4 - with: - pattern: artifacts-* + pattern: release-* path: artifacts merge-multiple: true - - name: Cleanup - run: rm -f artifacts/*-dist-manifest.json + - name: Flatten artifacts + run: find artifacts -type f -exec mv {} . \; + + - name: Prepare release notes + env: + VERSION: ${{ needs.ci.outputs.tag }} + run: | + set -euo pipefail + if grep -q "^## ${VERSION} (" CHANGELOG.md; then + awk -v ver="$VERSION" ' + /^## / { if (found) exit; if ($0 ~ "^## " ver " \\(") found=1; next } + found { print } + ' CHANGELOG.md > "$RUNNER_TEMP/notes.txt" + else + awk ' + /^## Unreleased/ { found=1; next } + found && /^## [0-9]/ { exit } + found { print } + ' CHANGELOG.md > "$RUNNER_TEMP/notes.txt" + fi + if [ ! -s "$RUNNER_TEMP/notes.txt" ]; then + echo "Release ${VERSION}" > "$RUNNER_TEMP/notes.txt" + fi - name: Create GitHub Release env: - PRERELEASE_FLAG: "${{ fromJson(steps.host.outputs.manifest).announcement_is_prerelease && '--prerelease' || '' }}" - ANNOUNCEMENT_TITLE: "${{ fromJson(steps.host.outputs.manifest).announcement_title }}" - ANNOUNCEMENT_BODY: "${{ fromJson(steps.host.outputs.manifest).announcement_github_body }}" - RELEASE_COMMIT: "${{ github.sha }}" + TAG: ${{ needs.ci.outputs.tag }} + PRERELEASE_FLAG: ${{ contains(needs.ci.outputs.tag, 'alpha') || contains(needs.ci.outputs.tag, 'beta') || contains(needs.ci.outputs.tag, 'rc') && '--prerelease' || '' }} run: | - echo "$ANNOUNCEMENT_BODY" > $RUNNER_TEMP/notes.txt - gh release create "${{ needs.plan.outputs.tag }}" --target "$RELEASE_COMMIT" $PRERELEASE_FLAG --title "$ANNOUNCEMENT_TITLE" --notes-file "$RUNNER_TEMP/notes.txt" artifacts/* - - announce: - needs: [plan, host] - if: ${{ always() && needs.host.result == 'success' }} - runs-on: ubuntu-22.04 - env: - GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - steps: - - uses: actions/checkout@v4 - with: - submodules: recursive + set -euo pipefail + FILES=() + for f in main-*; do + [ -f "$f" ] || continue + FILES+=("$f") + done + if [ "${#FILES[@]}" -eq 0 ]; then + echo "no release artifacts found" >&2 + ls -la + exit 1 + fi + gh release create "$TAG" \ + --target "${{ github.sha }}" \ + --title "minne $TAG" \ + --notes-file "$RUNNER_TEMP/notes.txt" \ + $PRERELEASE_FLAG \ + "${FILES[@]}" diff --git a/devenv.nix b/devenv.nix index 74532f3..f9d0cbc 100644 --- a/devenv.nix +++ b/devenv.nix @@ -35,7 +35,6 @@ in { pkgs.nodejs pkgs.watchman pkgs.vscode-langservers-extracted - pkgs.cargo-dist pkgs.cargo-xwin pkgs.clang pkgs.onnxruntime diff --git a/dist-workspace.toml b/dist-workspace.toml deleted file mode 100644 index 225ded9..0000000 --- a/dist-workspace.toml +++ /dev/null @@ -1,24 +0,0 @@ -[workspace] -members = ["cargo:."] - -# Config for 'dist' -[dist] -# The preferred dist version to use in CI (Cargo.toml SemVer syntax) -cargo-dist-version = "0.30.3" -# CI backends to support -ci = "github" -# Extra static files to include in each App (path relative to this Cargo.toml's dir) -include = ["lib"] -# The installers to generate for each app -installers = [] -# Target platforms to build apps for (Rust target-triple syntax) -targets = ["aarch64-apple-darwin", "x86_64-apple-darwin", "x86_64-unknown-linux-gnu", "x86_64-pc-windows-msvc"] -# Skip checking whether the specified configuration files are up to date -allow-dirty = ["ci"] - -[dist.github-custom-runners] -aarch64-apple-darwin = "macos-latest" -x86_64-apple-darwin = "macos-15-intel" -x86_64-unknown-linux-gnu = "ubuntu-22.04" -x86_64-unknown-linux-musl = "ubuntu-22.04" -x86_64-pc-windows-msvc = "windows-latest" diff --git a/docs/installation.md b/docs/installation.md index a30412b..8401697 100644 --- a/docs/installation.md +++ b/docs/installation.md @@ -28,12 +28,15 @@ Configure via environment variables or a `config.yaml` file. See [Configuration] ## Pre-built Binaries -Download binaries for Windows, macOS, and Linux from [GitHub Releases](https://github.com/perstarkse/minne/releases/latest). +Download binaries for Windows, macOS (Apple Silicon), and Linux from [GitHub Releases](https://github.com/perstarkse/minne/releases/latest). + +**macOS:** Release builds target `aarch64-apple-darwin` (Apple Silicon). Intel Macs can run the binary via [Rosetta 2](https://support.apple.com/en-us/102527). **Requirements:** - SurrealDB instance (local or remote) -- `libEGL` + `libfontconfig` (for servo-fetch web scraping) +- Linux: `libEGL` + `libfontconfig` for servo-fetch (bundled in release archives) +- macOS: system frameworks; ONNX Runtime is bundled in the archive `lib/` directory ## Build from Source diff --git a/flake.lock b/flake.lock index 7b26a4d..49d28c6 100644 --- a/flake.lock +++ b/flake.lock @@ -15,6 +15,27 @@ "type": "github" } }, + "fenix": { + "inputs": { + "nixpkgs": [ + "nixpkgs" + ], + "rust-analyzer-src": "rust-analyzer-src" + }, + "locked": { + "lastModified": 1782294578, + "narHash": "sha256-ctIw0dB+vAkpKnNhwbdXWm0jtzWHOKl+75OsrIcpQK8=", + "owner": "nix-community", + "repo": "fenix", + "rev": "bd1a9586894a7702d9fbd0da7f6e3f09d6510c36", + "type": "github" + }, + "original": { + "owner": "nix-community", + "repo": "fenix", + "type": "github" + } + }, "flake-utils": { "inputs": { "systems": "systems" @@ -52,10 +73,28 @@ "root": { "inputs": { "crane": "crane", + "fenix": "fenix", "flake-utils": "flake-utils", "nixpkgs": "nixpkgs" } }, + "rust-analyzer-src": { + "flake": false, + "locked": { + "lastModified": 1782126558, + "narHash": "sha256-HLXplzCGoc5mBVXdJriEsfX0gLDNXZ+O5urcdTmLP+E=", + "owner": "rust-lang", + "repo": "rust-analyzer", + "rev": "ff5fcc70d9bcdc54db365114e2f7b1c18056ed4f", + "type": "github" + }, + "original": { + "owner": "rust-lang", + "ref": "nightly", + "repo": "rust-analyzer", + "type": "github" + } + }, "systems": { "locked": { "lastModified": 1681028828, diff --git a/flake.nix b/flake.nix index c818350..a56c407 100644 --- a/flake.nix +++ b/flake.nix @@ -5,6 +5,8 @@ nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable"; flake-utils.url = "github:numtide/flake-utils"; crane.url = "github:ipetkov/crane"; + fenix.url = "github:nix-community/fenix"; + fenix.inputs.nixpkgs.follows = "nixpkgs"; }; outputs = { @@ -12,6 +14,7 @@ nixpkgs, flake-utils, crane, + fenix, }: let inherit (nixpkgs.legacyPackages.x86_64-linux) lib; ortVersion = "1.23.2"; @@ -25,12 +28,30 @@ then "dylib" else "so"; minneVersion = "1.0.4"; + mozjsRelease = "mozjs-sys-v140.10.1-0"; + + mozjsTarget = + { + "x86_64-linux" = "x86_64-unknown-linux-gnu"; + "aarch64-linux" = "aarch64-unknown-linux-gnu"; + "aarch64-darwin" = "aarch64-apple-darwin"; + "x86_64-darwin" = "x86_64-apple-darwin"; + } + .${system} + or (throw "mozjs prebuilt archive not configured for system ${system}"); + + mozjsHashes = { + "x86_64-unknown-linux-gnu" = "sha256-e5kW8HTg6Hrd3sGgU9bqFNTTf7wJCChFOwKE3xyYT4Q="; + "aarch64-unknown-linux-gnu" = "sha256-VXrcktvjSH+14tO9Kzx+n9f/9ZQGAzfEsniiT+xKT6Q="; + "aarch64-apple-darwin" = "sha256-T3y73nVic6R60keUpmVRFe110Eh7AcE/VwZQWXRU9A0="; + "x86_64-apple-darwin" = "sha256-4v6f6c1OwYdg1FKnFfdLEsrRdyghcxup4gF7ioTZzm4="; + }; # Pre-download mozjs binary archive for mozjs_sys (servo dep). - # When updating mozjs_sys version in Cargo.lock, update this URL too. + # When updating mozjs_sys version in Cargo.lock, update mozjsRelease + hashes. mozjsArchive = pkgs.fetchurl { - url = "https://github.com/servo/mozjs/releases/download/mozjs-sys-v140.10.1-0/libmozjs-x86_64-unknown-linux-gnu.tar.gz"; - hash = "sha256-e5kW8HTg6Hrd3sGgU9bqFNTTf7wJCChFOwKE3xyYT4Q="; + url = "https://github.com/servo/mozjs/releases/download/${mozjsRelease}/libmozjs-${mozjsTarget}.tar.gz"; + hash = mozjsHashes.${mozjsTarget} or (throw "missing mozjs hash for ${mozjsTarget}"); }; # Extra paths (common/db, html-router/templates, html-router/assets) are @@ -71,15 +92,13 @@ MOZJS_ARCHIVE = "${mozjsArchive}"; }; - # cargoBuild (not buildDepsOnly) avoids mkDummySrc breaking native build scripts. - cargoArtifacts = craneLib.cargoBuild (commonArgs + # Build *just* the cargo dependencies using a dummy source so that source + # code changes don't invalidate the cached dependency layer. + cargoArtifacts = craneLib.buildDepsOnly (commonArgs // { - cargoArtifacts = null; - pname = "minne-deps"; + pname = "minne"; cargoExtraArgs = "--workspace"; doCheck = false; - doInstallCargoArtifacts = true; - installPhaseCommand = ""; }); minne-pkg = @@ -93,20 +112,205 @@ doCheck = false; # checks are in separate derivations doInstallCargoArtifacts = true; # for reuse by check derivations - postInstall = '' - wrapProgram $out/bin/main \ - --prefix LD_LIBRARY_PATH : ${pkgs.libglvnd}/lib \ - --set ORT_DYLIB_PATH ${pkgs.onnxruntime}/lib/libonnxruntime.${libExt} - for b in worker server; do - if [ -x "$out/bin/$b" ]; then - wrapProgram $out/bin/$b \ - --prefix LD_LIBRARY_PATH : ${pkgs.libglvnd}/lib \ - --set ORT_DYLIB_PATH ${pkgs.onnxruntime}/lib/libonnxruntime.${libExt} - fi + postInstall = + lib.optionalString pkgs.stdenv.isLinux '' + wrapProgram $out/bin/main \ + --prefix LD_LIBRARY_PATH : ${pkgs.libglvnd}/lib \ + --set ORT_DYLIB_PATH ${pkgs.onnxruntime}/lib/libonnxruntime.${libExt} + for b in worker server; do + if [ -x "$out/bin/$b" ]; then + wrapProgram $out/bin/$b \ + --prefix LD_LIBRARY_PATH : ${pkgs.libglvnd}/lib \ + --set ORT_DYLIB_PATH ${pkgs.onnxruntime}/lib/libonnxruntime.${libExt} + fi + done + '' + + lib.optionalString pkgs.stdenv.isDarwin '' + for b in main worker server; do + if [ -x "$out/bin/$b" ]; then + wrapProgram $out/bin/$b \ + --set ORT_DYLIB_PATH ${pkgs.onnxruntime}/lib/libonnxruntime.${libExt} + fi + done + ''; + }) + else throw "pkgs.onnxruntime.version (${pkgs.onnxruntime.version}) must match ortVersion in flake.nix (${ortVersion})"; + + targetTriple = pkgs.stdenv.hostPlatform.config; + + releaseCommonArgs = { + inherit minneVersion targetTriple; + bzip2 = pkgs.bzip2.out; + brotli = pkgs.brotli.lib; + srcRoot = ./.; + }; + + minne-release = + if pkgs.stdenv.isLinux + then + pkgs.callPackage ./nix/minne-release.nix (releaseCommonArgs // { + platform = "linux"; + inherit minne-pkg; + }) + else if pkgs.stdenv.isDarwin + then + pkgs.callPackage ./nix/minne-release.nix (releaseCommonArgs // { + platform = "darwin"; + inherit minne-pkg; + }) + else null; + + windowsTarget = "x86_64-pc-windows-msvc"; + + windowsRustToolchain = + if system == "x86_64-linux" + then + let + fenixPkgs = fenix.packages.${system}; + in + fenixPkgs.combine [ + fenixPkgs.stable.defaultToolchain + fenixPkgs.targets.${windowsTarget}.stable.rust-std + ] + else null; + + windowsCraneLib = + if system == "x86_64-linux" + then craneLib.overrideToolchain (_: windowsRustToolchain) + else craneLib; + + mozjsArchiveWindows = pkgs.fetchurl { + url = "https://github.com/servo/mozjs/releases/download/${mozjsRelease}/libmozjs-${windowsTarget}.tar.gz"; + hash = "sha256-nEX55a4vZJGxlDMCea9TEee60HiNe/yQzXtUqMlaM3c="; + }; + + ortArchiveWindows = pkgs.fetchurl { + url = "https://github.com/microsoft/onnxruntime/releases/download/v${ortVersion}/onnxruntime-win-x64-${ortVersion}.zip"; + hash = "sha256-CzjfmvIYNOQec9YC2Q21ywbb0cphiUi48dZtYHrJ880="; + }; + + windowsCross = pkgs.callPackage ./nix/windows-cross.nix {}; + + inherit (windowsCross) clangClWrapper xwinCargoCache; + + # blake3's build.rs only enables MSVC asm when CC_x86_64_pc_windows_msvc is exactly + # "cl" or "cl.exe" (not a store path). Route through the clang-cl wrapper. + # cc-rs invokes ml64.exe for MSVC asm; llvm-ml64 is ml64-compatible. + msvcShim = pkgs.symlinkJoin { + name = "minne-msvc-shim"; + paths = [ + (pkgs.writeShellScriptBin "cl.exe" '' + exec ${clangClWrapper} "$@" + '') + (pkgs.writeShellScriptBin "ml64.exe" '' + exec ${pkgs.llvmPackages.llvm}/bin/llvm-ml64 "$@" + '') + ]; + }; + + # Offline MSVC env (nixpkgs cargo-xwin lacks `cache` and tries to download CRT in sandbox). + xwinSetup = pkgs.writeShellScript "minne-xwin-setup" '' + set -eo pipefail + + cache=${xwinCargoCache} + crt="$cache/xwin/crt" + sdk="$cache/xwin/sdk" + + export PATH="${msvcShim}/bin:${pkgs.llvmPackages.clang-unwrapped}/bin:${pkgs.llvmPackages.lld}/bin:${pkgs.llvmPackages.llvm}/bin:$PATH" + + # Host build scripts (webrender, etc.) run on Linux and need libstdc++ at runtime. + export LD_LIBRARY_PATH="${pkgs.stdenv.cc.cc.lib}/lib''${LD_LIBRARY_PATH:+:$LD_LIBRARY_PATH}" + + export AR_x86_64_pc_windows_msvc=${pkgs.llvmPackages.llvm}/bin/llvm-lib + export BINDGEN_EXTRA_CLANG_ARGS_x86_64_pc_windows_msvc="-I$crt/include -I$sdk/include/ucrt -I$sdk/include/um -I$sdk/include/shared -I$sdk/include/winrt" + export CARGO_TARGET_X86_64_PC_WINDOWS_MSVC_LINKER=${pkgs.llvmPackages.lld}/bin/lld-link + export CARGO_TARGET_X86_64_PC_WINDOWS_MSVC_RUSTFLAGS="-C linker-flavor=lld-link -Lnative=$crt/lib/x86_64 -Lnative=$sdk/lib/um/x86_64 -Lnative=$sdk/lib/ucrt/x86_64" + export CC_x86_64_pc_windows_msvc=cl.exe + export CXX_x86_64_pc_windows_msvc=cl.exe + export REAL_CLANG_CL=${pkgs.llvmPackages.clang-unwrapped}/bin/clang-cl + export REAL_LLD_LINK=${pkgs.llvmPackages.lld}/bin/lld-link + + _imsvc="--target=x86_64-pc-windows-msvc -Wno-unused-command-line-argument -fuse-ld=lld-link /imsvc $crt/include /imsvc $sdk/include/ucrt /imsvc $sdk/include/um /imsvc $sdk/include/shared /imsvc $sdk/include/winrt" + export CFLAGS_x86_64_pc_windows_msvc="$_imsvc" + export CXXFLAGS_x86_64_pc_windows_msvc="$_imsvc /EHsc" + export CL_FLAGS="--target=x86_64-pc-windows-msvc -Wno-unused-command-line-argument -fuse-ld=lld-link /imsvc $crt/include /imsvc $sdk/include/ucrt /imsvc $sdk/include/um /imsvc $sdk/include/shared /imsvc $sdk/include/winrt" + + export CMAKE_GENERATOR=Ninja + export CMAKE_SYSTEM_NAME=Windows + export CMAKE_TOOLCHAIN_FILE_x86_64_pc_windows_msvc="$cache/cmake/clang-cl/x86_64-pc-windows-msvc-toolchain.cmake" + + export LIB="$crt/lib/x86_64;$sdk/lib/um/x86_64;$sdk/lib/ucrt/x86_64" + export RCFLAGS="-I$crt/include -I$sdk/include/ucrt -I$sdk/include/um -I$sdk/include/shared -I$sdk/include/winrt" + export TARGET_AR=${pkgs.llvmPackages.llvm}/bin/llvm-lib + export TARGET_CC=${pkgs.llvmPackages.clang-unwrapped}/bin/clang-cl + export TARGET_CXX=${pkgs.llvmPackages.clang-unwrapped}/bin/clang-cl + export WINEDEBUG=-all + ''; + + windowsCommonArgs = + commonArgs + // { + MOZJS_ARCHIVE = "${mozjsArchiveWindows}"; + CARGO_BUILD_TARGET = windowsTarget; + doIncludeCrossToolchainEnv = false; + env.CARGO_PROFILE = "dist"; + buildInputs = [ + pkgs.openssl + pkgs.fontconfig + pkgs.libclang.lib + ]; + nativeBuildInputs = + commonArgs.nativeBuildInputs + ++ [ + pkgs.llvmPackages.llvm + pkgs.llvmPackages.clang-unwrapped + pkgs.llvmPackages.lld + pkgs.stdenv.cc.cc.lib # host build scripts (e.g. webrender) link libstdc++ + ]; + }; + + windowsCargoArtifacts = + if system == "x86_64-linux" + then + windowsCraneLib.buildDepsOnly (windowsCommonArgs + // { + pname = "minne"; + cargoExtraArgs = "--workspace"; + doCheck = false; + preBuild = "source ${xwinSetup}"; + }) + else null; + + minne-pkg-windows = + if system == "x86_64-linux" + then + windowsCraneLib.buildPackage (windowsCommonArgs + // { + pname = "minne-windows"; + version = minneVersion; + cargoArtifacts = windowsCargoArtifacts; + cargoExtraArgs = "--target ${windowsTarget} -p main --bin main --bin server --bin worker"; + doCheck = false; + doInstallCargoArtifacts = false; + preBuild = "source ${xwinSetup}"; + installPhaseCommand = '' + mkdir -p "$out/bin" + for b in main server worker; do + install -m 755 "target/${windowsTarget}/dist/$b.exe" "$out/bin/$b.exe" done ''; }) - else throw "pkgs.onnxruntime.version (${pkgs.onnxruntime.version}) must match ortVersion in flake.nix (${ortVersion})"; + else null; + + minne-release-windows = + if system == "x86_64-linux" + then + pkgs.callPackage ./nix/minne-release.nix (releaseCommonArgs // { + platform = "windows"; + inherit minne-pkg-windows ortArchiveWindows; + targetTriple = windowsTarget; + }) + else null; dockerImage = pkgs.dockerTools.buildLayeredImage { name = "minne"; @@ -139,6 +343,13 @@ packages = { inherit minne-pkg dockerImage; default = minne-pkg; + } + // lib.optionalAttrs (minne-release != null) { + minne-release = minne-release; + } + // lib.optionalAttrs (minne-release-windows != null) { + inherit xwinCargoCache; + minne-release-windows = minne-release-windows; }; apps = { diff --git a/nix/minne-release.nix b/nix/minne-release.nix new file mode 100644 index 0000000..1cdc542 --- /dev/null +++ b/nix/minne-release.nix @@ -0,0 +1,249 @@ +# Portable release archives for cargo-dist-compatible GitHub Releases layout. +{ + lib, + stdenv, + platform ? (if stdenv.isLinux then "linux" else if stdenv.isDarwin then "darwin" else "windows"), + patchelf ? null, + xz, + unzip ? null, + zip ? null, + minne-pkg ? null, + minne-pkg-windows ? null, + onnxruntime ? null, + ortArchiveWindows ? null, + libglvnd ? null, + fontconfig, + freetype, + openssl, + zlib, + bzip2, + libpng, + brotli, + expat, + gcc ? null, + glibc ? null, + minneVersion, + targetTriple, + srcRoot, +}: +let + archiveName = "main-${targetTriple}"; + binaries = ["main" "server" "worker"]; + + copyBinary = '' + if [ -f "${minne-pkg}/bin/.$b-wrapped" ]; then + cp "${minne-pkg}/bin/.$b-wrapped" "$exe" + elif [ -f "${minne-pkg}/bin/$b" ]; then + cp "${minne-pkg}/bin/$b" "$exe" + else + echo "missing binary: $b" >&2 + exit 1 + fi + ''; + + copyDocs = '' + cp ${srcRoot}/README.md ${srcRoot}/LICENSE ${srcRoot}/CHANGELOG.md "$root/" + ''; + + mkLinux = + let + isAarch64 = lib.hasSuffix "aarch64-unknown-linux-gnu" targetTriple; + dynamicLinker = + if isAarch64 + then "ld-linux-aarch64.so.1" + else "ld-linux-x86-64.so.2"; + + copySharedLibs = pkg: '' + if [ -d "${pkg}/lib" ]; then + find -L "${pkg}/lib" -maxdepth 1 \( -name '*.so' -o -name '*.so.*' \) -exec cp -L {} "$root/lib/" \; + fi + ''; + in + stdenv.mkDerivation { + pname = "main"; + version = minneVersion; + + nativeBuildInputs = [patchelf xz]; + + dontUnpack = true; + dontStrip = true; + dontPatchELF = true; + + installPhase = '' + runHook preInstall + + root="$TMPDIR/root/${archiveName}" + mkdir -p "$root/lib" + + for b in ${lib.concatStringsSep " " binaries}; do + exe="$root/.$b-bin" + ${copyBinary} + chmod u+w "$exe" + patchelf --set-interpreter 'lib/${dynamicLinker}' "$exe" + patchelf --set-rpath '$ORIGIN/lib' "$exe" + printf '%s\n' \ + '#!/bin/sh' \ + 'set -eu' \ + 'DIR="$(CDPATH= cd "$(dirname "$0")" && pwd)"' \ + 'export LD_LIBRARY_PATH="$DIR/lib"' \ + 'export ORT_DYLIB_PATH="$DIR/lib/libonnxruntime.so"' \ + 'exec "$DIR/.'"$b"'-bin" "$@"' \ + > "$root/$b" + chmod +x "$root/$b" + done + + cp -L ${onnxruntime}/lib/libonnxruntime.so* "$root/lib/" + ${copySharedLibs libglvnd} + ${copySharedLibs fontconfig.lib} + ${copySharedLibs freetype} + ${copySharedLibs openssl.out} + ${copySharedLibs zlib} + ${copySharedLibs bzip2} + ${copySharedLibs libpng} + ${copySharedLibs brotli} + ${copySharedLibs expat} + ${copySharedLibs gcc.cc.lib} + + # Bundle glibc so binaries run on older distros (e.g. Ubuntu 22.04). + cp -L ${glibc}/lib/${dynamicLinker} "$root/lib/" + for lib in libc.so.6 libm.so.6 libpthread.so.0 libdl.so.2 librt.so.1 libresolv.so.2; do + if [ -f "${glibc}/lib/$lib" ]; then + cp -L "${glibc}/lib/$lib" "$root/lib/" + fi + done + + ${copyDocs} + + mkdir -p "$out" + tar -cJf "$out/${archiveName}.tar.xz" -C "$TMPDIR/root" "${archiveName}" + + runHook postInstall + ''; + + meta = { + description = "Minne release archive for ${targetTriple}"; + platforms = lib.platforms.linux; + }; + }; + + mkDarwin = + let + copyDylibs = pkg: '' + if [ -d "${pkg}/lib" ]; then + find -L "${pkg}/lib" -maxdepth 1 \( -name '*.dylib' -o -name '*.so' \) -exec cp -L {} "$root/lib/" \; + fi + ''; + in + stdenv.mkDerivation { + pname = "main"; + version = minneVersion; + + nativeBuildInputs = [xz]; + + dontUnpack = true; + dontStrip = true; + dontFixDarwinDylibs = true; + + installPhase = '' + runHook preInstall + + root="$TMPDIR/root/${archiveName}" + mkdir -p "$root/lib" + + for b in ${lib.concatStringsSep " " binaries}; do + exe="$root/.$b-bin" + ${copyBinary} + chmod +x "$exe" + printf '%s\n' \ + '#!/bin/sh' \ + 'set -eu' \ + 'DIR="$(CDPATH= cd "$(dirname "$0")" && pwd)"' \ + 'export DYLD_LIBRARY_PATH="$DIR/lib''${DYLD_LIBRARY_PATH:+:$DYLD_LIBRARY_PATH}"' \ + 'exec "$DIR/.'"$b"'-bin" "$@"' \ + > "$root/$b" + chmod +x "$root/$b" + done + + cp -L ${onnxruntime}/lib/libonnxruntime*.dylib "$root/lib/" 2>/dev/null || true + if [ ! -f "$root/lib/libonnxruntime.dylib" ]; then + ort="$(find -L "$root/lib" -maxdepth 1 -name 'libonnxruntime*.dylib' | head -n1)" + if [ -n "$ort" ]; then + cp -L "$ort" "$root/lib/libonnxruntime.dylib" + else + echo "missing libonnxruntime.dylib" >&2 + exit 1 + fi + fi + + ${copyDylibs fontconfig.lib} + ${copyDylibs freetype} + ${copyDylibs openssl.out} + ${copyDylibs zlib} + ${copyDylibs bzip2} + ${copyDylibs libpng} + ${copyDylibs brotli} + ${copyDylibs expat} + + ${copyDocs} + + mkdir -p "$out" + tar -cJf "$out/${archiveName}.tar.xz" -C "$TMPDIR/root" "${archiveName}" + + runHook postInstall + ''; + + meta = { + description = "Minne release archive for ${targetTriple}"; + platforms = lib.platforms.darwin; + }; + }; + + mkWindows = stdenv.mkDerivation { + pname = "main"; + version = minneVersion; + + nativeBuildInputs = [unzip zip]; + + dontUnpack = true; + dontStrip = true; + + installPhase = '' + runHook preInstall + + root="$TMPDIR/root" + mkdir -p "$root/lib" + + for b in ${lib.concatStringsSep " " binaries}; do + if [ ! -f "${minne-pkg-windows}/bin/$b.exe" ]; then + echo "missing binary: $b.exe" >&2 + exit 1 + fi + cp "${minne-pkg-windows}/bin/$b.exe" "$root/$b.exe" + done + + unzip -q ${ortArchiveWindows} -d "$TMPDIR/ort" + dll="$(find "$TMPDIR/ort" -name onnxruntime.dll -print -quit)" + if [ -z "$dll" ]; then + echo "missing onnxruntime.dll in ORT archive" >&2 + exit 1 + fi + cp "$dll" "$root/lib/onnxruntime.dll" + + ${copyDocs} + + mkdir -p "$out" + (cd "$root" && zip -qr "$out/${archiveName}.zip" .) + + runHook postInstall + ''; + + meta = { + description = "Minne release archive for ${targetTriple}"; + platforms = [ "x86_64-linux" ]; + }; + }; +in +if platform == "linux" then mkLinux +else if platform == "darwin" then mkDarwin +else if platform == "windows" then mkWindows +else throw "minne-release: unknown platform ${platform}" diff --git a/nix/windows-cross.nix b/nix/windows-cross.nix new file mode 100644 index 0000000..4af1ab5 --- /dev/null +++ b/nix/windows-cross.nix @@ -0,0 +1,171 @@ +# Offline MSVC CRT + Windows SDK for cross-compiling to x86_64-pc-windows-msvc. +{ + lib, + stdenv, + xwin, + cacert, + writeText, + writeShellScript, +}: +let + cmakeOverride = writeText "override.cmake" '' + # macOS paths usually start with /Users/*. Unfortunately, clang-cl interprets + # paths starting with /U as macro undefines, so we need to put a -- before the + # input file path to force it to be treated as a path. + string(REPLACE "-c " "-c -- " CMAKE_C_COMPILE_OBJECT "''${CMAKE_C_COMPILE_OBJECT}") + string(REPLACE "-c " "-c -- " CMAKE_CXX_COMPILE_OBJECT "''${CMAKE_CXX_COMPILE_OBJECT}") + string(REPLACE "/D" "-D" CMAKE_RC_FLAGS "''${CMAKE_RC_FLAGS_INIT}") + string(REPLACE "/D" "-D" CMAKE_RC_FLAGS_DEBUG "''${CMAKE_RC_FLAGS_DEBUG_INIT}") + if(NOT CMAKE_HOST_WIN32) + set(CMAKE_NINJA_CMCLDEPS_RC 0) + endif() + ''; + + cmakeToolchain = writeText "x86_64-pc-windows-msvc-toolchain.cmake" '' + set(CMAKE_SYSTEM_NAME Windows) + set(CMAKE_SYSTEM_PROCESSOR AMD64) + set(CMAKE_C_COMPILER clang-cl CACHE FILEPATH "") + set(CMAKE_CXX_COMPILER clang-cl CACHE FILEPATH "") + set(CMAKE_AR llvm-lib) + set(CMAKE_LINKER lld-link CACHE FILEPATH "") + set(CMAKE_MSVC_RUNTIME_LIBRARY CACHE STRING "MultiThreadedDLL") + + # Paths relative to this file (cmake/clang-cl/) so the FOD output stays store-path-free. + set(_XWIN_ROOT "''${CMAKE_CURRENT_LIST_DIR}/../..") + set(_CRT "''${_XWIN_ROOT}/xwin/crt") + set(_SDK "''${_XWIN_ROOT}/xwin/sdk") + + set(COMPILE_FLAGS + --target=x86_64-pc-windows-msvc + -Wno-unused-command-line-argument + -fuse-ld=lld-link + /imsvc ''${_CRT}/include + /imsvc ''${_SDK}/include/ucrt + /imsvc ''${_SDK}/include/um + /imsvc ''${_SDK}/include/shared + /imsvc ''${_SDK}/include/winrt) + set(LINK_FLAGS + /manifest:no + -libpath:"''${_CRT}/lib/x86_64" + -libpath:"''${_SDK}/lib/um/x86_64" + -libpath:"''${_SDK}/lib/ucrt/x86_64") + string(REPLACE ";" " " COMPILE_FLAGS "''${COMPILE_FLAGS}") + set(_CMAKE_C_FLAGS_INITIAL "''${CMAKE_C_FLAGS}" CACHE STRING "") + set(CMAKE_C_FLAGS "''${_CMAKE_C_FLAGS_INITIAL} ''${COMPILE_FLAGS}" CACHE STRING "" FORCE) + set(_CMAKE_CXX_FLAGS_INITIAL "''${CMAKE_CXX_FLAGS}" CACHE STRING "") + set(CMAKE_CXX_FLAGS "''${_CMAKE_CXX_FLAGS_INITIAL} ''${COMPILE_FLAGS} /EHsc" CACHE STRING "" FORCE) + string(REPLACE ";" " " LINK_FLAGS "''${LINK_FLAGS}") + set(_CMAKE_EXE_LINKER_FLAGS_INITIAL "''${CMAKE_EXE_LINKER_FLAGS}" CACHE STRING "") + set(CMAKE_EXE_LINKER_FLAGS "''${_CMAKE_EXE_LINKER_FLAGS_INITIAL} ''${LINK_FLAGS}" CACHE STRING "" FORCE) + set(_CMAKE_MODULE_LINKER_FLAGS_INITIAL "''${CMAKE_MODULE_LINKER_FLAGS}" CACHE STRING "") + set(CMAKE_MODULE_LINKER_FLAGS "''${_CMAKE_MODULE_LINKER_FLAGS_INITIAL} ''${LINK_FLAGS}" CACHE STRING "" FORCE) + set(_CMAKE_SHARED_LINKER_FLAGS_INITIAL "''${CMAKE_SHARED_LINKER_FLAGS}" CACHE STRING "") + set(CMAKE_SHARED_LINKER_FLAGS "''${_CMAKE_SHARED_LINKER_FLAGS_INITIAL} ''${LINK_FLAGS}" CACHE STRING "" FORCE) + set(CMAKE_C_STANDARD_LIBRARIES "" CACHE STRING "" FORCE) + set(CMAKE_CXX_STANDARD_LIBRARIES "" CACHE STRING "" FORCE) + set(CMAKE_TRY_COMPILE_CONFIGURATION Release) + set(CMAKE_USER_MAKE_RULES_OVERRIDE "''${CMAKE_CURRENT_LIST_DIR}/override.cmake") + ''; + + clangClWrapper = writeShellScript "clang-cl-msvc-link-wrapper" '' + # Route mozangle DLL link invocations (clang-cl + /LD + .obj) to lld-link. + set -euo pipefail + + real_clang_cl="''${REAL_CLANG_CL:-clang-cl}" + real_lld_link="''${REAL_LLD_LINK:-lld-link}" + + objs=() + libs=() + fe="" + def="" + has_ld=false + + for arg in "$@"; do + case "$arg" in + *.obj|*.o) objs+=("$arg") ;; + *.lib) libs+=("$arg") ;; + /LD|/Ld) has_ld=true ;; + /Fe*) fe="''${arg#/Fe}" ;; + /DEF:*) def="$arg" ;; + esac + done + + if [[ "$has_ld" == true && ''${#objs[@]} -gt 0 ]]; then + lld_args=() + for o in "''${objs[@]}"; do + lld_args+=("$o") + done + for l in "''${libs[@]}"; do + lld_args+=("$l") + done + lld_args+=("/DLL") + if [[ -n "$fe" ]]; then + lld_args+=("/OUT:$fe") + fi + if [[ -n "$def" ]]; then + lld_args+=("$def") + fi + exec "$real_lld_link" "''${lld_args[@]}" + fi + + exec "$real_clang_cl" "$@" + ''; +in +{ + inherit clangClWrapper; + + xwinCargoCache = stdenv.mkDerivation { + pname = "cargo-xwin-cache"; + version = "17.2"; + + nativeBuildInputs = [xwin cacert]; + + preferLocalBuild = true; + allowSubstitutes = false; + outputHashMode = "recursive"; + outputHashAlgo = "sha256"; + outputHash = "sha256-eYRtNXLttjlC1l+9mMoeXjmGwSC86hhY3on1IN4dym8="; + + buildCommand = '' + set -eo pipefail + export XWIN_ACCEPT_LICENSE=true + export SSL_CERT_FILE="${cacert}/etc/ssl/certs/ca-certificates.crt" + export SSL_CERT_DIR="${cacert}/etc/ssl/certs" + export NIX_SSL_CERT_FILE="${cacert}/etc/ssl/certs/ca-certificates.crt" + + dl="$TMPDIR/xwin-dl" + splat="$TMPDIR/splat" + mkdir -p "$dl" "$splat" + + ${xwin}/bin/xwin \ + --manifest-version 17 \ + --arch x86_64 \ + --cache-dir "$dl" \ + download + ${xwin}/bin/xwin \ + --manifest-version 17 \ + --arch x86_64 \ + --cache-dir "$dl" \ + unpack + ${xwin}/bin/xwin \ + --manifest-version 17 \ + --arch x86_64 \ + --cache-dir "$dl" \ + splat --output "$splat" + + mkdir -p "$out/xwin" "$out/cmake/clang-cl" + cp -a "$splat"/. "$out/xwin/" + echo -n "x86_64" > "$out/xwin/DONE" + + cp ${cmakeToolchain} "$out/cmake/clang-cl/x86_64-pc-windows-msvc-toolchain.cmake" + cp ${cmakeOverride} "$out/cmake/clang-cl/override.cmake" + + # lld-link expects Dbghelp.lib; xwin ships dbghelp.lib (case-sensitive FS). + for dir in "$out/xwin/crt/lib/x86_64" "$out/xwin/sdk/lib/um/x86_64"; do + if [ -f "$dir/dbghelp.lib" ] && [ ! -e "$dir/Dbghelp.lib" ]; then + ln -sf dbghelp.lib "$dir/Dbghelp.lib" + fi + done + ''; + }; +}