diff --git a/OneBranchPipelines/build-release-package-pipeline.yml b/OneBranchPipelines/build-release-package-pipeline.yml index eff7fd9d..04e67887 100644 --- a/OneBranchPipelines/build-release-package-pipeline.yml +++ b/OneBranchPipelines/build-release-package-pipeline.yml @@ -130,9 +130,10 @@ parameters: - name: linuxConfigs type: object default: - # manylinux (glibc-based) for x86_64 and ARM64 - - { tag: 'manylinux', arch: 'x86_64', platform: 'linux/amd64' } - - { tag: 'manylinux', arch: 'aarch64', platform: 'linux/arm64' } + # manylinux_2_28 (glibc 2.28, AlmaLinux 8 / RHEL 8+) for x86_64 and ARM64 + # manylinux_2_28 wheels are forward-compatible: pip on glibc 2.34+ systems installs them fine + - { tag: 'manylinux_2_28', arch: 'x86_64', platform: 'linux/amd64' } + - { tag: 'manylinux_2_28', arch: 'aarch64', platform: 'linux/arm64' } # musllinux (musl-based) for x86_64 and ARM64 - { tag: 'musllinux', arch: 'x86_64', platform: 'linux/amd64' } - { tag: 'musllinux', arch: 'aarch64', platform: 'linux/arm64' } @@ -394,14 +395,14 @@ extends: # LINUX BUILD STAGES # ========================= # Strategy: One stage per distribution × architecture - # Total: 4 stages (manylinux×2 + musllinux×2) + # Total: 4 stages (manylinux_2_28×2 + musllinux×2) # Each stage builds ALL Python versions (3.10-3.14) in a loop # Distributions: - # - manylinux: glibc-based (Ubuntu, CentOS, etc.) + # - manylinux_2_28: glibc 2.28+ (RHEL 8, Ubuntu 20.04+, etc.) — forward-compatible with newer glibc # - musllinux: musl-based (Alpine Linux) # Architectures: x86_64 (AMD/Intel), aarch64 (ARM64) # Each stage: - # 1. Starts PyPA Docker container (manylinux_2_34 or musllinux_1_2) + # 1. Starts PyPA Docker container (manylinux_2_28 or musllinux_1_2) # 2. Starts SQL Server Docker container # 3. For each Python version (cp310-cp314): # a. Builds .so native extension @@ -452,8 +453,8 @@ extends: - MacOS_py313 - MacOS_py314 # Linux dependencies (4 stages) - - Linux_manylinux_x86_64 - - Linux_manylinux_aarch64 + - Linux_manylinux_2_28_x86_64 + - Linux_manylinux_2_28_aarch64 - Linux_musllinux_x86_64 - Linux_musllinux_aarch64 jobs: diff --git a/OneBranchPipelines/stages/build-linux-single-stage.yml b/OneBranchPipelines/stages/build-linux-single-stage.yml index 17815cad..6d82f5a6 100644 --- a/OneBranchPipelines/stages/build-linux-single-stage.yml +++ b/OneBranchPipelines/stages/build-linux-single-stage.yml @@ -3,14 +3,14 @@ # Builds for Python 3.10, 3.11, 3.12, 3.13, 3.14 within single job # Tests each wheel after building with isolated pytest execution parameters: - # Stage identifier (e.g., 'Linux_manylinux_x86_64') + # Stage identifier (e.g., 'Linux_manylinux_2_28_x86_64') - name: stageName type: string # Job identifier within the stage - name: jobName type: string default: 'BuildWheels' - # Linux distribution type: 'manylinux' (glibc-based) or 'musllinux' (musl libc-based) + # Linux distribution type: 'manylinux_2_28' (glibc 2.28+) or 'musllinux' (musl libc-based) - name: linuxTag type: string # CPU architecture: 'x86_64' (AMD64) or 'aarch64' (ARM64) @@ -114,12 +114,15 @@ stages: - script: | # Determine image based on LINUX_TAG and ARCH # Images are mirrored weekly from Quay.io into tdslibrs.azurecr.io - # manylinux_2_34 = AlmaLinux 9 (glibc 2.34) + # manylinux_2_28 = AlmaLinux 8 / RHEL 8 compatible (glibc 2.28) # musllinux_1_2 = Alpine-based (musl libc) - if [[ "$(LINUX_TAG)" == "musllinux" ]]; then + if [[ "$(LINUX_TAG)" == "manylinux_2_28" ]]; then + IMAGE="tdslibrs.azurecr.io/import/python-build/manylinux_2_28_$(ARCH):latest" + elif [[ "$(LINUX_TAG)" == "musllinux" ]]; then IMAGE="tdslibrs.azurecr.io/import/python-build/musllinux_1_2_$(ARCH):latest" else - IMAGE="tdslibrs.azurecr.io/import/python-build/manylinux_2_34_$(ARCH):latest" + echo "ERROR: Unsupported LINUX_TAG '$(LINUX_TAG)'. Expected: manylinux_2_28 or musllinux" >&2 + exit 1 fi docker run -d --name build-$(LINUX_TAG)-$(ARCH) \ @@ -133,7 +136,7 @@ stages: - script: | set -euxo pipefail export PATH=$PATH:`pwd`/docker - if [[ "$(LINUX_TAG)" == "manylinux" ]]; then + if [[ "$(LINUX_TAG)" == "manylinux_2_28" ]]; then docker exec build-$(LINUX_TAG)-$(ARCH) bash -lc ' set -euxo pipefail if command -v dnf >/dev/null 2>&1; then @@ -191,7 +194,7 @@ stages: # Build wheels for all Python versions (3.10-3.14) and test each one - script: | set -euxo pipefail - if [[ "$(LINUX_TAG)" == "manylinux" ]]; then SHELL_EXE=bash; else SHELL_EXE=sh; fi + if [[ "$(LINUX_TAG)" == "manylinux_2_28" ]]; then SHELL_EXE=bash; else SHELL_EXE=sh; fi docker exec build-$(LINUX_TAG)-$(ARCH) $SHELL_EXE -lc 'mkdir -p /workspace/dist' # Loop through all Python versions: build wheel -> test wheel -> repeat @@ -201,9 +204,9 @@ stages: echo "Building and testing $PYBIN on $(LINUX_TAG)/$(ARCH)" echo "=====================================================" - if [[ "$(LINUX_TAG)" == "manylinux" ]]; then + if [[ "$(LINUX_TAG)" == "manylinux_2_28" ]]; then # Manylinux (glibc-based) - use bash - docker exec -e PYBIN=$PYBIN -e SQL_IP=$(SQL_IP) -e DB_PASSWORD="$(DB_PASSWORD)" build-$(LINUX_TAG)-$(ARCH) bash -lc ' + docker exec -e PYBIN=$PYBIN -e SQL_IP=$(SQL_IP) -e DB_PASSWORD="$(DB_PASSWORD)" -e MANYLINUX_TAG="$(LINUX_TAG)" build-$(LINUX_TAG)-$(ARCH) bash -lc ' set -euxo pipefail; # Step 1: Setup Python environment @@ -271,7 +274,7 @@ stages: ' else # Musllinux (musl libc-based) - use sh - docker exec -e PYBIN=$PYBIN -e SQL_IP=$(SQL_IP) -e DB_PASSWORD="$(DB_PASSWORD)" build-$(LINUX_TAG)-$(ARCH) sh -lc ' + docker exec -e PYBIN=$PYBIN -e SQL_IP=$(SQL_IP) -e DB_PASSWORD="$(DB_PASSWORD)" -e MANYLINUX_TAG="$(LINUX_TAG)" build-$(LINUX_TAG)-$(ARCH) sh -lc ' set -euxo pipefail; # Step 1: Setup Python environment @@ -361,7 +364,7 @@ stages: # Copy native .so bindings for artifact archival echo "Copying .so bindings to host..." mkdir -p "$(ob_outputDirectory)/bindings/$(LINUX_TAG)-$(ARCH)" - docker exec build-$(LINUX_TAG)-$(ARCH) $([[ "$(LINUX_TAG)" == "manylinux" ]] && echo bash -lc || echo sh -lc) ' + docker exec build-$(LINUX_TAG)-$(ARCH) $([[ "$(LINUX_TAG)" == "manylinux_2_28" ]] && echo bash -lc || echo sh -lc) ' OUT="/tmp/ddbc-out"; rm -rf "$OUT"; mkdir -p "$OUT"; find /workspace/mssql_python -maxdepth 1 -type f -name "*.so" -exec cp -v {} "$OUT"/ \; || true diff --git a/setup.py b/setup.py index 0e719c48..30a177fe 100644 --- a/setup.py +++ b/setup.py @@ -47,10 +47,13 @@ def get_platform_info(): libc_name, _ = platform.libc_ver() is_musl = libc_name == "" or "musl" in libc_name.lower() + # Allow explicit override via MANYLINUX_TAG env var (defaults to manylinux_2_28) + manylinux_tag = os.environ.get("MANYLINUX_TAG", "manylinux_2_28") + if target_arch == "x86_64": - return "x86_64", "musllinux_1_2_x86_64" if is_musl else "manylinux_2_34_x86_64" + return "x86_64", f"musllinux_1_2_x86_64" if is_musl else f"{manylinux_tag}_x86_64" elif target_arch in ["aarch64", "arm64"]: - return "aarch64", "musllinux_1_2_aarch64" if is_musl else "manylinux_2_34_aarch64" + return "aarch64", f"musllinux_1_2_aarch64" if is_musl else f"{manylinux_tag}_aarch64" else: raise OSError( f"Unsupported architecture '{target_arch}' for Linux; expected 'x86_64' or 'aarch64'."