diff --git a/docker/build/devdeps.manylinux.Dockerfile b/docker/build/devdeps.manylinux.Dockerfile index 81b161ad515..5e957649acb 100644 --- a/docker/build/devdeps.manylinux.Dockerfile +++ b/docker/build/devdeps.manylinux.Dockerfile @@ -83,14 +83,17 @@ RUN curl -L https://github.com/Kitware/CMake/releases/download/v3.28.4/cmake-3.2 # Build the the LLVM libraries and compiler toolchain needed to build CUDA-Q. ADD ./scripts/build_llvm.sh /scripts/build_llvm.sh +ADD ./scripts/build_mlir_python_bindings.sh /scripts/build_mlir_python_bindings.sh ADD ./cmake/caches/LLVM.cmake /cmake/caches/LLVM.cmake ADD ./tpls/customizations/llvm/ /tpls/customizations/llvm/ RUN LLVM_PROJECTS='clang;lld;mlir' LLVM_SOURCE=/llvm-project \ LLVM_CMAKE_CACHE=/cmake/caches/LLVM.cmake \ LLVM_CMAKE_PATCHES=/tpls/customizations/llvm \ bash /scripts/build_llvm.sh -c Release -v - # No clean up of the build or source directory, - # since we need to re-build llvm for each python version to get the bindings. + # The build directory at /llvm-project/build is intentionally retained: + # build_mlir_python_bindings.sh reuses it in the wheel container to add + # the python-binding targets per Python version without recompiling + # the rest of the LLVM/MLIR/clang/lld tree. # Install CUDA diff --git a/docker/release/cudaq.wheel.Dockerfile b/docker/release/cudaq.wheel.Dockerfile index d3e9956f375..52518db9d8c 100644 --- a/docker/release/cudaq.wheel.Dockerfile +++ b/docker/release/cudaq.wheel.Dockerfile @@ -51,11 +51,11 @@ RUN --mount=from=ccache-data,target=/tmp/ccache-import,rw \ fi RUN echo "Building MLIR bindings for python${python_version}" && \ CCACHE_DISABLE=1 python${python_version} -m pip install --no-cache-dir numpy "nanobind>=2.9.0" && \ - rm -rf "$LLVM_INSTALL_PREFIX/src" "$LLVM_INSTALL_PREFIX/python_packages" && \ + rm -rf "$LLVM_INSTALL_PREFIX/python_packages" && \ Python3_EXECUTABLE="$(which python${python_version})" \ - LLVM_PROJECTS='clang;lld;mlir;python-bindings' \ - LLVM_CMAKE_CACHE=/cmake/caches/LLVM.cmake LLVM_SOURCE=/llvm-project \ - bash /scripts/build_llvm.sh -c Release -v + LLVM_SOURCE=/llvm-project \ + LLVM_INSTALL_PREFIX="$LLVM_INSTALL_PREFIX" \ + bash /scripts/build_mlir_python_bindings.sh # Build wheel using unified wheel build script RUN cd /cuda-quantum && \ diff --git a/scripts/build_mlir_python_bindings.sh b/scripts/build_mlir_python_bindings.sh new file mode 100755 index 00000000000..7907f5aa322 --- /dev/null +++ b/scripts/build_mlir_python_bindings.sh @@ -0,0 +1,80 @@ +#!/bin/bash + +# ============================================================================ # +# Copyright (c) 2022 - 2026 NVIDIA Corporation & Affiliates. # +# All rights reserved. # +# # +# This source code and the accompanying materials are made available under # +# the terms of the Apache License 2.0 which accompanies this distribution. # +# ============================================================================ # + +# Builds the MLIR Python bindings against an existing LLVM build tree and +# installs them into LLVM_INSTALL_PREFIX. Requires the manylinux devdeps +# base image, where build_llvm.sh has already configured and built the +# LLVM/MLIR libraries (without python-bindings) at LLVM_SOURCE/build. +# +# This avoids reconfiguring/rebuilding the full LLVM tree once per Python +# version: only the binding objects (linked against the active interpreter +# ABI) and a small amount of tablegen output get produced. +# +# Required environment: +# LLVM_SOURCE path to the llvm-project source (default: /llvm-project) +# LLVM_INSTALL_PREFIX install destination (default: /usr/local/llvm) +# Python3_EXECUTABLE interpreter to bind against +# NANOBIND_INSTALL_PREFIX nanobind install dir (default: /usr/local/nanobind) +# +# Usage: +# Python3_EXECUTABLE=$(which python3.11) bash scripts/build_mlir_python_bindings.sh + +set -e + +LLVM_SOURCE=${LLVM_SOURCE:-/llvm-project} +LLVM_INSTALL_PREFIX=${LLVM_INSTALL_PREFIX:-/usr/local/llvm} +NANOBIND_INSTALL_PREFIX=${NANOBIND_INSTALL_PREFIX:-/usr/local/nanobind} +LLVM_BUILD_FOLDER=${LLVM_BUILD_FOLDER:-build} + +if [ -z "$Python3_EXECUTABLE" ]; then + echo "Python3_EXECUTABLE must be set." + exit 1 +fi + +llvm_build_dir="$LLVM_SOURCE/$LLVM_BUILD_FOLDER" +if [ ! -f "$llvm_build_dir/CMakeCache.txt" ]; then + echo "Expected pre-configured LLVM build at $llvm_build_dir." + echo "This script must run on top of the manylinux devdeps base image." + exit 1 +fi + +this_file_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" + +# nanobind links against the active interpreter ABI; build it if missing. +if [ ! -d "$NANOBIND_INSTALL_PREFIX" ] || [ -z "$(ls -A "$NANOBIND_INSTALL_PREFIX"/* 2>/dev/null)" ]; then + echo "Building nanobind..." + cd "$this_file_dir/.." && repo_root=$(git rev-parse --show-toplevel) && cd "$repo_root" + git submodule update --init --recursive --recommend-shallow --single-branch tpls/nanobind + mkdir -p tpls/nanobind/build && cd tpls/nanobind/build + cmake -G Ninja ../ \ + -DCMAKE_INSTALL_PREFIX="$NANOBIND_INSTALL_PREFIX" \ + -DPython3_EXECUTABLE="$Python3_EXECUTABLE" \ + -DNB_TEST=False + cmake --build . --target install --config Release +fi + +cd "$llvm_build_dir" + +# Reconfigure the existing build tree: enable MLIR python bindings and +# point at the active interpreter. Other cache entries (compiler flags, +# enabled projects, ccache launcher, build type) are preserved. +echo "Reconfiguring LLVM build for python bindings (Python: $Python3_EXECUTABLE)..." +cmake . \ + -DMLIR_ENABLE_BINDINGS_PYTHON=ON \ + -DPython3_EXECUTABLE="$Python3_EXECUTABLE" \ + -Dnanobind_DIR="$NANOBIND_INSTALL_PREFIX/nanobind/cmake" + +# Build and install only the python-binding components. Other targets in +# the existing tree are left untouched and remain installed at +# LLVM_INSTALL_PREFIX from the base image. +echo "Building and installing MLIR python bindings..." +ninja install-MLIRPythonModules install-mlir-python-sources + +echo "Installed MLIR python bindings into $LLVM_INSTALL_PREFIX."