3
0
Fork 0
mirror of https://github.com/Z3Prover/z3 synced 2025-11-24 14:41:28 +00:00

Fix NuGet package missing Microsoft.Z3.dll due to inverted replace() logic (#8029)

* Initial plan

* Fix NuGet packaging and add GitHub Actions workflow

- Fixed critical bug in mk_nuget_task.py replace() function
- Created comprehensive GitHub Actions workflow for building NuGet packages

Co-authored-by: NikolajBjorner <3085284+NikolajBjorner@users.noreply.github.com>

* Add documentation and improve code comments

- Added comprehensive README for NuGet build workflow
- Added detailed comments explaining the replace() function fix
- Verified all Python syntax is correct

Co-authored-by: NikolajBjorner <3085284+NikolajBjorner@users.noreply.github.com>

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: NikolajBjorner <3085284+NikolajBjorner@users.noreply.github.com>
This commit is contained in:
Copilot 2025-11-16 11:46:16 -08:00 committed by GitHub
parent 59eec25102
commit 62cd39729f
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 354 additions and 1 deletions

87
.github/workflows/NUGET_BUILD_README.md vendored Normal file
View file

@ -0,0 +1,87 @@
# NuGet Package Build Workflow
This document describes the GitHub Actions workflow for building Z3 NuGet packages.
## Overview
The NuGet build workflow (`.github/workflows/nuget-build.yml`) creates Microsoft.Z3 NuGet packages for distribution. It builds Z3 for all supported platforms and assembles them into NuGet packages.
## Triggering the Workflow
The workflow can be triggered in two ways:
### 1. Manual Trigger
You can manually trigger the workflow from the GitHub Actions tab:
1. Go to the "Actions" tab in the repository
2. Select "Build NuGet Package" workflow
3. Click "Run workflow"
4. Enter the version number (e.g., `4.15.5`)
5. Click "Run workflow"
### 2. Tag-based Trigger
The workflow automatically runs when you push a tag with the `z3-` prefix:
```bash
git tag z3-4.15.5
git push origin z3-4.15.5
```
## Workflow Structure
The workflow consists of multiple jobs:
### Build Jobs
1. **build-windows-x64**: Builds Windows x64 binaries with .NET support
2. **build-windows-x86**: Builds Windows x86 binaries with .NET support
3. **build-windows-arm64**: Builds Windows ARM64 binaries with .NET support
4. **build-ubuntu**: Builds Linux x64 binaries with .NET support
5. **build-macos-x64**: Builds macOS x64 binaries with .NET support
6. **build-macos-arm64**: Builds macOS ARM64 binaries with .NET support
### Package Jobs
1. **package-nuget-x64**: Creates the main NuGet package (Microsoft.Z3.nupkg) with x64, ARM64, Linux, and macOS support
2. **package-nuget-x86**: Creates the x86 NuGet package (Microsoft.Z3.x86.nupkg)
## Output
The workflow produces two NuGet packages as artifacts:
- `Microsoft.Z3.{version}.nupkg` and `Microsoft.Z3.{version}.snupkg` (x64 + multi-platform)
- `Microsoft.Z3.x86.{version}.nupkg` and `Microsoft.Z3.x86.{version}.snupkg` (x86 only)
These can be downloaded from the workflow run's artifacts section.
## Key Files
- `.github/workflows/nuget-build.yml`: The workflow definition
- `scripts/mk_nuget_task.py`: Script that assembles the NuGet package from build artifacts
- `scripts/mk_win_dist.py`: Script for building Windows x86/x64 distributions
- `scripts/mk_win_dist_cmake.py`: Script for building Windows ARM64 distributions
- `scripts/mk_unix_dist.py`: Script for building Linux and macOS distributions
## Bug Fix
This workflow includes a fix for a critical bug in `mk_nuget_task.py` where the `replace()` function had incorrect logic that would fail to copy files when the destination already existed. The fix ensures that Microsoft.Z3.dll and related files are always properly included in the NuGet package under `lib/netstandard2.0/`.
## Development
To test changes to the NuGet packaging locally, you can:
1. Build the platform-specific binaries using the appropriate build scripts
2. Collect the resulting ZIP files in a directory
3. Run `mk_nuget_task.py` to assemble the package:
```bash
python scripts/mk_nuget_task.py <packages_dir> <version> <repo_url> <branch> <commit> <source_dir> [symbols] [x86]
```
4. Use the NuGet CLI to pack the package:
```bash
nuget pack out/Microsoft.Z3.sym.nuspec -OutputDirectory . -Verbosity detailed -Symbols -SymbolPackageFormat snupkg -BasePath out
```

256
.github/workflows/nuget-build.yml vendored Normal file
View file

@ -0,0 +1,256 @@
name: Build NuGet Package
on:
workflow_dispatch:
inputs:
version:
description: 'Version number for the NuGet package (e.g., 4.15.5)'
required: true
default: '4.15.5'
push:
tags:
- 'z3-*'
permissions:
contents: write
jobs:
# Build Windows binaries
build-windows-x64:
runs-on: windows-latest
steps:
- name: Checkout code
uses: actions/checkout@v5
- name: Setup Python
uses: actions/setup-python@v5
with:
python-version: '3.x'
- name: Build Windows x64
shell: cmd
run: |
call "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build\vcvarsall.bat" x64
python scripts\mk_win_dist.py --x64-only --dotnet-key=%GITHUB_WORKSPACE%\resources\z3.snk --assembly-version=${{ github.event.inputs.version || '4.15.5' }} --zip
- name: Upload Windows x64 artifact
uses: actions/upload-artifact@v4
with:
name: windows-x64
path: dist/*.zip
retention-days: 1
build-windows-x86:
runs-on: windows-latest
steps:
- name: Checkout code
uses: actions/checkout@v5
- name: Setup Python
uses: actions/setup-python@v5
with:
python-version: '3.x'
- name: Build Windows x86
shell: cmd
run: |
call "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build\vcvarsall.bat" x86
python scripts\mk_win_dist.py --x86-only --dotnet-key=%GITHUB_WORKSPACE%\resources\z3.snk --assembly-version=${{ github.event.inputs.version || '4.15.5' }} --zip
- name: Upload Windows x86 artifact
uses: actions/upload-artifact@v4
with:
name: windows-x86
path: dist/*.zip
retention-days: 1
build-windows-arm64:
runs-on: windows-latest
steps:
- name: Checkout code
uses: actions/checkout@v5
- name: Setup Python
uses: actions/setup-python@v5
with:
python-version: '3.x'
- name: Build Windows ARM64
shell: cmd
run: |
call "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build\vcvarsall.bat" amd64_arm64
python scripts\mk_win_dist_cmake.py --arm64-only --dotnet-key=%GITHUB_WORKSPACE%\resources\z3.snk --assembly-version=${{ github.event.inputs.version || '4.15.5' }} --zip
- name: Upload Windows ARM64 artifact
uses: actions/upload-artifact@v4
with:
name: windows-arm64
path: build-dist\arm64\dist\*.zip
retention-days: 1
build-ubuntu:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v5
- name: Setup Python
uses: actions/setup-python@v5
with:
python-version: '3.x'
- name: Build Ubuntu
run: python scripts/mk_unix_dist.py --dotnet-key=$GITHUB_WORKSPACE/resources/z3.snk
- name: Upload Ubuntu artifact
uses: actions/upload-artifact@v4
with:
name: ubuntu
path: dist/*.zip
retention-days: 1
build-macos-x64:
runs-on: macos-13
steps:
- name: Checkout code
uses: actions/checkout@v5
- name: Setup Python
uses: actions/setup-python@v5
with:
python-version: '3.x'
- name: Build macOS x64
run: python scripts/mk_unix_dist.py --dotnet-key=$GITHUB_WORKSPACE/resources/z3.snk
- name: Upload macOS x64 artifact
uses: actions/upload-artifact@v4
with:
name: macos-x64
path: dist/*.zip
retention-days: 1
build-macos-arm64:
runs-on: macos-13
steps:
- name: Checkout code
uses: actions/checkout@v5
- name: Setup Python
uses: actions/setup-python@v5
with:
python-version: '3.x'
- name: Build macOS ARM64
run: python scripts/mk_unix_dist.py --dotnet-key=$GITHUB_WORKSPACE/resources/z3.snk --arch=arm64
- name: Upload macOS ARM64 artifact
uses: actions/upload-artifact@v4
with:
name: macos-arm64
path: dist/*.zip
retention-days: 1
# Package NuGet x64 (includes all platforms except x86)
package-nuget-x64:
needs: [build-windows-x64, build-windows-arm64, build-ubuntu, build-macos-x64, build-macos-arm64]
runs-on: windows-latest
steps:
- name: Checkout code
uses: actions/checkout@v5
- name: Setup Python
uses: actions/setup-python@v5
with:
python-version: '3.x'
- name: Download all artifacts
uses: actions/download-artifact@v4
with:
path: packages
- name: List downloaded artifacts
shell: bash
run: find packages -type f
- name: Move artifacts to flat directory
shell: bash
run: |
mkdir -p package-files
find packages -name "*.zip" -exec cp {} package-files/ \;
ls -la package-files/
- name: Setup NuGet
uses: nuget/setup-nuget@v2
with:
nuget-version: 'latest'
- name: Assemble NuGet package
shell: cmd
run: |
cd package-files
python ..\scripts\mk_nuget_task.py . ${{ github.event.inputs.version || '4.15.5' }} https://github.com/Z3Prover/z3 ${{ github.ref_name }} ${{ github.sha }} ${{ github.workspace }} symbols
- name: Pack NuGet package
shell: cmd
run: |
cd package-files
nuget pack out\Microsoft.Z3.sym.nuspec -OutputDirectory . -Verbosity detailed -Symbols -SymbolPackageFormat snupkg -BasePath out
- name: Upload NuGet package
uses: actions/upload-artifact@v4
with:
name: nuget-x64
path: |
package-files/*.nupkg
package-files/*.snupkg
retention-days: 30
# Package NuGet x86
package-nuget-x86:
needs: [build-windows-x86]
runs-on: windows-latest
steps:
- name: Checkout code
uses: actions/checkout@v5
- name: Setup Python
uses: actions/setup-python@v5
with:
python-version: '3.x'
- name: Download x86 artifact
uses: actions/download-artifact@v4
with:
name: windows-x86
path: packages
- name: List downloaded artifacts
shell: bash
run: find packages -type f
- name: Setup NuGet
uses: nuget/setup-nuget@v2
with:
nuget-version: 'latest'
- name: Assemble NuGet package
shell: cmd
run: |
cd packages
python ..\scripts\mk_nuget_task.py . ${{ github.event.inputs.version || '4.15.5' }} https://github.com/Z3Prover/z3 ${{ github.ref_name }} ${{ github.sha }} ${{ github.workspace }} symbols x86
- name: Pack NuGet package
shell: cmd
run: |
cd packages
nuget pack out\Microsoft.Z3.x86.sym.nuspec -OutputDirectory . -Verbosity detailed -Symbols -SymbolPackageFormat snupkg -BasePath out
- name: Upload NuGet package
uses: actions/upload-artifact@v4
with:
name: nuget-x86
path: |
packages/*.nupkg
packages/*.snupkg
retention-days: 30

View file

@ -44,9 +44,19 @@ def classify_package(f, arch):
return None
def replace(src, dst):
"""
Replace destination file with source file.
Removes the destination file if it exists, then moves the source file to the destination.
This ensures that the file is always moved, whether or not the destination exists.
Previous buggy implementation only moved when removal failed, causing files to be
deleted but not replaced when the destination already existed.
"""
try:
os.remove(dst)
except:
pass
shutil.move(src, dst)
def unpack(packages, symbols, arch):