You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@arrow.apache.org by li...@apache.org on 2023/01/25 16:38:07 UTC

[arrow-adbc] branch main updated: chore(ci): add Conda package build support (#375)

This is an automated email from the ASF dual-hosted git repository.

lidavidm pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/arrow-adbc.git


The following commit(s) were added to refs/heads/main by this push:
     new a2411c2  chore(ci): add Conda package build support (#375)
a2411c2 is described below

commit a2411c20940c8b7848521418761b2973abb29934
Author: David Li <li...@gmail.com>
AuthorDate: Wed Jan 25 11:38:02 2023 -0500

    chore(ci): add Conda package build support (#375)
    
    Fixes #229.
    
    Co-authored-by: Sutou Kouhei <ko...@cozmixng.org>
---
 .env                                               |   1 +
 .github/workflows/packaging.yml                    | 165 +++++++++++++
 .pre-commit-config.yaml                            |   1 +
 LICENSE.txt                                        |   8 +
 ci/conda/.ci_support/README                        |   6 +
 ci/conda/.ci_support/linux_64_.yaml                |  33 +++
 ci/conda/.ci_support/linux_aarch64_.yaml           |  37 +++
 ci/conda/.ci_support/linux_ppc64le_.yaml           |  33 +++
 ci/conda/.ci_support/osx_64_.yaml                  |  33 +++
 ci/conda/.ci_support/osx_arm64_.yaml               |  33 +++
 ci/conda/.ci_support/win_64_.yaml                  |  22 ++
 ci/conda/.gitattributes                            |  27 +++
 ci/conda/build-cpp.bat                             |  52 ++++
 ci/conda/build-cpp.sh                              |  62 +++++
 ci/conda/build-python.bat                          |  48 ++++
 ci/conda/build-python.sh                           |  49 ++++
 .../__init__.py => ci/conda/conda-forge.yml        |  28 +--
 ci/conda/meta.yaml                                 | 262 +++++++++++++++++++++
 .../scripts/python_conda_build.sh                  |  40 +++-
 ci/scripts/python_conda_test.sh                    |  68 ++++++
 .../scripts/python_conda_upload.sh                 |  24 +-
 dev/release/rat_exclude_files.txt                  |   2 +
 dev/release/utils-prepare.sh                       |   8 +
 docker-compose.yml                                 |  15 ++
 docs/source/development/nightly.rst                |  34 ++-
 .../adbc_driver_flightsql/__init__.py              |  38 ++-
 python/adbc_driver_flightsql/setup.py              |   2 +
 .../adbc_driver_postgresql/__init__.py             |  36 ++-
 python/adbc_driver_postgresql/setup.py             |   3 +-
 .../adbc_driver_sqlite/__init__.py                 |  40 +++-
 python/adbc_driver_sqlite/setup.py                 |   2 +
 31 files changed, 1156 insertions(+), 56 deletions(-)

diff --git a/.env b/.env
index 9a9d958..44ec3d8 100644
--- a/.env
+++ b/.env
@@ -25,6 +25,7 @@ REPO=apache/arrow-dev
 ARCH=amd64
 ARCH_ALIAS=x86_64
 ARCH_SHORT=amd64
+ARCH_CONDA_FORGE=linux_64_
 
 # Default versions for various dependencies
 JDK=8
diff --git a/.github/workflows/packaging.yml b/.github/workflows/packaging.yml
index 33184f1..dd47023 100644
--- a/.github/workflows/packaging.yml
+++ b/.github/workflows/packaging.yml
@@ -333,6 +333,135 @@ jobs:
           rake --trace ${TASK_NAMESPACE}:test
           popd
 
+  python-conda-linux:
+    name: "Python ${{ matrix.arch }} Conda"
+    runs-on: ubuntu-latest
+    needs:
+      - source
+    strategy:
+      matrix:
+        # TODO: "linux_aarch64_"
+        arch: ["linux_64_"]
+    steps:
+      - uses: actions/download-artifact@v3
+        with:
+          name: source
+
+      - name: Extract source archive
+        run: |
+          source_archive=$(echo apache-arrow-adbc-*.tar.gz)
+          VERSION=${source_archive#apache-arrow-adbc-}
+          VERSION=${VERSION%.tar.gz}
+          echo "VERSION=${VERSION}" >> $GITHUB_ENV
+
+          tar xf apache-arrow-adbc-${VERSION}.tar.gz
+          mv apache-arrow-adbc-${VERSION} adbc
+
+      - name: Show inputs
+        shell: bash
+        run: |
+          echo "upload_artifacts: ${{ github.event.inputs.upload_artifacts }}"
+          echo "schedule: ${{ github.event.schedule }}"
+          echo "ref: ${{ github.ref }}"
+
+      - name: Build Conda package
+        shell: bash
+        env:
+          ARCH_CONDA_FORGE: ${{ matrix.arch }}
+        run: |
+          pushd adbc
+          docker-compose run \
+            -e HOST_USER_ID=$(id -u) \
+            python-conda
+          popd
+
+      - name: Archive Conda packages
+        uses: actions/upload-artifact@v3
+        with:
+          name: python-${{ matrix.arch }}-conda
+          retention-days: 7
+          path: |
+            adbc/build/conda/*/*.tar.bz2
+
+      - name: Test Conda packages
+        if: matrix.arch == 'linux_64_'
+        shell: bash
+        env:
+          ARCH_CONDA_FORGE: ${{ matrix.arch }}
+        run: |
+          pushd adbc
+          docker-compose run \
+            python-conda-test
+          popd
+
+  python-conda-macos:
+    name: "Python ${{ matrix.arch }} Conda"
+    runs-on: macos-latest
+    needs:
+      - source
+    strategy:
+      matrix:
+        # TODO: "osx_arm64_"
+        arch: ["osx_64_"]
+    steps:
+      - uses: actions/download-artifact@v3
+        with:
+          name: source
+
+      - name: Extract source archive
+        run: |
+          source_archive=$(echo apache-arrow-adbc-*.tar.gz)
+          VERSION=${source_archive#apache-arrow-adbc-}
+          VERSION=${VERSION%.tar.gz}
+          echo "VERSION=${VERSION}" >> $GITHUB_ENV
+
+          tar xf apache-arrow-adbc-${VERSION}.tar.gz
+          mv apache-arrow-adbc-${VERSION} adbc
+
+      - name: Show inputs
+        shell: bash
+        run: |
+          echo "upload_artifacts: ${{ github.event.inputs.upload_artifacts }}"
+          echo "schedule: ${{ github.event.schedule }}"
+          echo "ref: ${{ github.ref }}"
+
+      - uses: conda-incubator/setup-miniconda@v2
+        with:
+          miniforge-variant: Mambaforge
+          miniforge-version: latest
+          use-only-tar-bz2: false
+          use-mamba: true
+
+      - name: Install Dependencies
+        shell: bash -l {0}
+        run: |
+          mamba install -c conda-forge boa conda-smithy conda-verify
+          conda config --remove channels defaults
+          conda config --add channels conda-forge
+
+      - name: Build Conda package
+        shell: bash -l {0}
+        env:
+          ARCH_CONDA_FORGE: ${{ matrix.arch }}
+        run: |
+          ./adbc/ci/scripts/python_conda_build.sh $(pwd)/adbc ${ARCH_CONDA_FORGE}.yaml $(pwd)/adbc/build
+
+      - name: Archive Conda packages
+        uses: actions/upload-artifact@v3
+        with:
+          name: python-${{ matrix.arch }}-conda
+          retention-days: 7
+          path: |
+            adbc/build/conda/*/*.tar.bz2
+
+      - name: Test Conda packages
+        shell: bash -l {0}
+        if: matrix.arch == 'osx_64_'
+        env:
+          ARCH_CONDA_FORGE: ${{ matrix.arch }}
+        run: |
+          ./adbc/ci/scripts/python_conda_test.sh $(pwd)/adbc $(pwd)/adbc/build
+
   python-manylinux:
     name: "Python ${{ matrix.arch }} manylinux${{ matrix.manylinux_version }}"
     runs-on: ubuntu-latest
@@ -733,6 +862,42 @@ jobs:
         env:
           GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
 
+  # TODO(apache/arrow-adbc#377): clean-anaconda job
+
+  upload-anaconda:
+    name: "Upload packages to Anaconda.org"
+    runs-on: ubuntu-latest
+    if: github.ref == 'refs/heads/main' && (github.event.schedule || inputs.upload_artifacts)
+    needs:
+      - python-conda-linux
+      - python-conda-macos
+    steps:
+      - uses: actions/checkout@v3
+        with:
+          fetch-depth: 0
+          persist-credentials: true
+      - name: Get All Artifacts
+        uses: actions/download-artifact@v3
+        with:
+          path: conda-packages
+      - uses: conda-incubator/setup-miniconda@v2
+        with:
+          miniforge-variant: Mambaforge
+          miniforge-version: latest
+          use-only-tar-bz2: false
+          use-mamba: true
+      - name: Install Dependencies
+        shell: bash -l {0}
+        run: |
+          mamba install -c conda-forge anaconda-client
+      - name: Upload
+        shell: bash -l {0}
+        run: |
+          ls -laR conda-packages
+          ./ci/scripts/python_conda_upload.sh conda-packages/python-*-conda/*/*.tar.bz2
+        env:
+          ANACONDA_API_TOKEN: ${{ secrets.ANACONDA_API_TOKEN }}
+
   upload-gemfury:
     name: "Upload packages to Gemfury"
     runs-on: ubuntu-latest
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index 1a0ef8b..d6b56fd 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -26,6 +26,7 @@ repos:
     hooks:
     - id: check-xml
     - id: check-yaml
+      exclude: ci/conda/meta.yaml
     - id: end-of-file-fixer
     - id: trailing-whitespace
   - repo: https://github.com/pocc/pre-commit-hooks
diff --git a/LICENSE.txt b/LICENSE.txt
index 9331de4..22c9f15 100644
--- a/LICENSE.txt
+++ b/LICENSE.txt
@@ -222,6 +222,14 @@ which is made available under the Creative Commons CC0 license.
 
 --------------------------------------------------------------------------------
 
+The files under ci/conda/.ci-support have the following license
+
+BSD 3-clause license
+Copyright (c) 2015-2022, conda-forge
+All rights reserved.
+
+--------------------------------------------------------------------------------
+
 3rdparty dependency Go is statically linked in certain binary distributions,
 like the Python wheels. The Go project is under the BSD 3-clause license +
 PATENTS weak patent termination clause
diff --git a/ci/conda/.ci_support/README b/ci/conda/.ci_support/README
new file mode 100644
index 0000000..a47316b
--- /dev/null
+++ b/ci/conda/.ci_support/README
@@ -0,0 +1,6 @@
+This file is automatically generated by conda-smithy. If any
+particular build configuration is expected, but it is not found,
+please make sure all dependencies are satisfiable. To add/modify any
+matrix elements, you should create/change conda-smithy's input
+recipe/conda_build_config.yaml and re-render the recipe, rather than
+editing these files directly.
diff --git a/ci/conda/.ci_support/linux_64_.yaml b/ci/conda/.ci_support/linux_64_.yaml
new file mode 100644
index 0000000..2ab4439
--- /dev/null
+++ b/ci/conda/.ci_support/linux_64_.yaml
@@ -0,0 +1,33 @@
+c_compiler:
+- gcc
+c_compiler_version:
+- '11'
+cdt_name:
+- cos6
+channel_sources:
+- conda-forge
+channel_targets:
+- conda-forge main
+cxx_compiler:
+- gxx
+cxx_compiler_version:
+- '11'
+docker_image:
+- quay.io/condaforge/linux-anvil-cos7-x86_64
+libpq:
+- '15'
+libsqlite:
+- '3'
+pin_run_as_build:
+  python:
+    min_pin: x.x
+    max_pin: x.x
+python:
+- 3.10.* *_cpython
+- 3.11.* *_cpython
+- 3.9.* *_cpython
+target_platform:
+- linux-64
+zip_keys:
+- - c_compiler_version
+  - cxx_compiler_version
diff --git a/ci/conda/.ci_support/linux_aarch64_.yaml b/ci/conda/.ci_support/linux_aarch64_.yaml
new file mode 100644
index 0000000..96b82b7
--- /dev/null
+++ b/ci/conda/.ci_support/linux_aarch64_.yaml
@@ -0,0 +1,37 @@
+BUILD:
+- aarch64-conda_cos7-linux-gnu
+c_compiler:
+- gcc
+c_compiler_version:
+- '11'
+cdt_arch:
+- aarch64
+cdt_name:
+- cos7
+channel_sources:
+- conda-forge
+channel_targets:
+- conda-forge main
+cxx_compiler:
+- gxx
+cxx_compiler_version:
+- '11'
+docker_image:
+- quay.io/condaforge/linux-anvil-cos7-x86_64
+libpq:
+- '15'
+libsqlite:
+- '3'
+pin_run_as_build:
+  python:
+    min_pin: x.x
+    max_pin: x.x
+python:
+- 3.10.* *_cpython
+- 3.11.* *_cpython
+- 3.9.* *_cpython
+target_platform:
+- linux-aarch64
+zip_keys:
+- - c_compiler_version
+  - cxx_compiler_version
diff --git a/ci/conda/.ci_support/linux_ppc64le_.yaml b/ci/conda/.ci_support/linux_ppc64le_.yaml
new file mode 100644
index 0000000..381aefe
--- /dev/null
+++ b/ci/conda/.ci_support/linux_ppc64le_.yaml
@@ -0,0 +1,33 @@
+c_compiler:
+- gcc
+c_compiler_version:
+- '11'
+cdt_name:
+- cos7
+channel_sources:
+- conda-forge
+channel_targets:
+- conda-forge main
+cxx_compiler:
+- gxx
+cxx_compiler_version:
+- '11'
+docker_image:
+- quay.io/condaforge/linux-anvil-cos7-x86_64
+libpq:
+- '15'
+libsqlite:
+- '3'
+pin_run_as_build:
+  python:
+    min_pin: x.x
+    max_pin: x.x
+python:
+- 3.10.* *_cpython
+- 3.8.* *_cpython
+- 3.9.* *_cpython
+target_platform:
+- linux-ppc64le
+zip_keys:
+- - c_compiler_version
+  - cxx_compiler_version
diff --git a/ci/conda/.ci_support/osx_64_.yaml b/ci/conda/.ci_support/osx_64_.yaml
new file mode 100644
index 0000000..8216590
--- /dev/null
+++ b/ci/conda/.ci_support/osx_64_.yaml
@@ -0,0 +1,33 @@
+MACOSX_DEPLOYMENT_TARGET:
+- '10.9'
+c_compiler:
+- clang
+c_compiler_version:
+- '14'
+channel_sources:
+- conda-forge
+channel_targets:
+- conda-forge main
+cxx_compiler:
+- clangxx
+cxx_compiler_version:
+- '14'
+libpq:
+- '15'
+libsqlite:
+- '3'
+macos_machine:
+- x86_64-apple-darwin13.4.0
+pin_run_as_build:
+  python:
+    min_pin: x.x
+    max_pin: x.x
+python:
+- 3.10.* *_cpython
+- 3.11.* *_cpython
+- 3.9.* *_cpython
+target_platform:
+- osx-64
+zip_keys:
+- - c_compiler_version
+  - cxx_compiler_version
diff --git a/ci/conda/.ci_support/osx_arm64_.yaml b/ci/conda/.ci_support/osx_arm64_.yaml
new file mode 100644
index 0000000..eaed0e6
--- /dev/null
+++ b/ci/conda/.ci_support/osx_arm64_.yaml
@@ -0,0 +1,33 @@
+MACOSX_DEPLOYMENT_TARGET:
+- '11.0'
+c_compiler:
+- clang
+c_compiler_version:
+- '14'
+channel_sources:
+- conda-forge
+channel_targets:
+- conda-forge main
+cxx_compiler:
+- clangxx
+cxx_compiler_version:
+- '14'
+libpq:
+- '15'
+libsqlite:
+- '3'
+macos_machine:
+- arm64-apple-darwin20.0.0
+pin_run_as_build:
+  python:
+    min_pin: x.x
+    max_pin: x.x
+python:
+- 3.10.* *_cpython
+- 3.8.* *_cpython
+- 3.9.* *_cpython
+target_platform:
+- osx-arm64
+zip_keys:
+- - c_compiler_version
+  - cxx_compiler_version
diff --git a/ci/conda/.ci_support/win_64_.yaml b/ci/conda/.ci_support/win_64_.yaml
new file mode 100644
index 0000000..aed7574
--- /dev/null
+++ b/ci/conda/.ci_support/win_64_.yaml
@@ -0,0 +1,22 @@
+c_compiler:
+- vs2019
+channel_sources:
+- conda-forge
+channel_targets:
+- conda-forge main
+cxx_compiler:
+- vs2019
+libpq:
+- '15'
+libsqlite:
+- '3'
+pin_run_as_build:
+  python:
+    min_pin: x.x
+    max_pin: x.x
+python:
+- 3.10.* *_cpython
+- 3.8.* *_cpython
+- 3.9.* *_cpython
+target_platform:
+- win-64
diff --git a/ci/conda/.gitattributes b/ci/conda/.gitattributes
new file mode 100644
index 0000000..7f32763
--- /dev/null
+++ b/ci/conda/.gitattributes
@@ -0,0 +1,27 @@
+* text=auto
+
+*.patch binary
+*.diff binary
+meta.yaml text eol=lf
+build.sh text eol=lf
+bld.bat text eol=crlf
+
+# github helper pieces to make some files not show up in diffs automatically
+.azure-pipelines/* linguist-generated=true
+.circleci/* linguist-generated=true
+.ci_support/README linguist-generated=true
+.drone/* linguist-generated=true
+.drone.yml linguist-generated=true
+.github/* linguist-generated=true
+.travis/* linguist-generated=true
+.appveyor.yml linguist-generated=true
+.gitattributes linguist-generated=true
+.gitignore linguist-generated=true
+.travis.yml linguist-generated=true
+.scripts/* linguist-generated=true
+.woodpecker.yml linguist-generated=true
+LICENSE.txt linguist-generated=true
+README.md linguist-generated=true
+azure-pipelines.yml linguist-generated=true
+build-locally.py linguist-generated=true
+shippable.yml linguist-generated=true
diff --git a/ci/conda/build-cpp.bat b/ci/conda/build-cpp.bat
new file mode 100644
index 0000000..06cf03c
--- /dev/null
+++ b/ci/conda/build-cpp.bat
@@ -0,0 +1,52 @@
+REM Licensed to the Apache Software Foundation (ASF) under one
+REM or more contributor license agreements.  See the NOTICE file
+REM distributed with this work for additional information
+REM regarding copyright ownership.  The ASF licenses this file
+REM to you under the Apache License, Version 2.0 (the
+REM "License"); you may not use this file except in compliance
+REM with the License.  You may obtain a copy of the License at
+REM
+REM   http://www.apache.org/licenses/LICENSE-2.0
+REM
+REM Unless required by applicable law or agreed to in writing,
+REM software distributed under the License is distributed on an
+REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+REM KIND, either express or implied.  See the License for the
+REM specific language governing permissions and limitations
+REM under the License.
+
+if "%PKG_NAME%" == "adbc-driver-manager-cpp" (
+    set PKG_ROOT=c\driver_manager
+    goto BUILD
+)
+if "%PKG_NAME%" == "adbc-driver-flightsql-go" (
+    set PKG_ROOT=c\driver\flightsql
+    goto BUILD
+)
+if "%PKG_NAME%" == "adbc-driver-postgresql-cpp" (
+    set PKG_ROOT=c\driver\postgresql
+    goto BUILD
+)
+if "%PKG_NAME%" == "adbc-driver-sqlite-cpp" (
+    set PKG_ROOT=c\driver\sqlite
+    goto BUILD
+)
+echo Unknown package %PKG_NAME%
+exit 1
+
+:BUILD
+
+mkdir "%SRC_DIR%"\build-cpp\%PKG_NAME%
+pushd "%SRC_DIR%"\build-cpp\%PKG_NAME%
+
+cmake ..\..\%PKG_ROOT% ^
+      -G Ninja ^
+      -DADBC_BUILD_SHARED=ON ^
+      -DADBC_BUILD_STATIC=OFF ^
+      -DCMAKE_INSTALL_PREFIX=%LIBRARY_PREFIX% ^
+      -DCMAKE_PREFIX_PATH=%PREFIX% ^
+      || exit /B 1
+
+cmake --build . --target install --config Release -j || exit /B 1
+
+popd
diff --git a/ci/conda/build-cpp.sh b/ci/conda/build-cpp.sh
new file mode 100644
index 0000000..7828f37
--- /dev/null
+++ b/ci/conda/build-cpp.sh
@@ -0,0 +1,62 @@
+#!/bin/bash
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+set -ex
+
+case "${PKG_NAME}" in
+    adbc-driver-manager-cpp)
+        export PKG_ROOT=c/driver_manager
+        ;;
+    adbc-driver-flightsql-go)
+        export CGO_ENABLED=1
+        export PKG_ROOT=c/driver/flightsql
+        ;;
+    adbc-driver-postgresql-cpp)
+        export PKG_ROOT=c/driver/postgresql
+        ;;
+    adbc-driver-sqlite-cpp)
+        export PKG_ROOT=c/driver/sqlite
+        ;;
+    *)
+        echo "Unknown package ${PKG_NAME}"
+        exit 1
+        ;;
+esac
+
+if [[ "${target_platform}" == "linux-aarch64" ]] ||
+       [[ "${target_platform}" == "osx-arm64" ]]; then
+    export GOARCH="arm64"
+elif [[ "${target_platform}" == "linux-ppc64le" ]]; then
+    export GOARCH="ppc64le"
+else
+    export GOARCH="amd64"
+fi
+
+mkdir -p "build-cpp/${PKG_NAME}"
+pushd "build-cpp/${PKG_NAME}"
+
+cmake "../../${PKG_ROOT}" \
+      -G Ninja \
+      -DADBC_BUILD_SHARED=ON \
+      -DADBC_BUILD_STATIC=OFF \
+      -DCMAKE_INSTALL_PREFIX="${PREFIX}" \
+      -DCMAKE_PREFIX_PATH="${PREFIX}"
+
+cmake --build . --target install -j
+
+popd
diff --git a/ci/conda/build-python.bat b/ci/conda/build-python.bat
new file mode 100644
index 0000000..3c3fb2b
--- /dev/null
+++ b/ci/conda/build-python.bat
@@ -0,0 +1,48 @@
+REM Licensed to the Apache Software Foundation (ASF) under one
+REM or more contributor license agreements.  See the NOTICE file
+REM distributed with this work for additional information
+REM regarding copyright ownership.  The ASF licenses this file
+REM to you under the Apache License, Version 2.0 (the
+REM "License"); you may not use this file except in compliance
+REM with the License.  You may obtain a copy of the License at
+REM
+REM   http://www.apache.org/licenses/LICENSE-2.0
+REM
+REM Unless required by applicable law or agreed to in writing,
+REM software distributed under the License is distributed on an
+REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+REM KIND, either express or implied.  See the License for the
+REM specific language governing permissions and limitations
+REM under the License.
+
+if "%PKG_NAME%" == "adbc-driver-manager" (
+    pushd "%SRC_DIR%"\python\adbc_driver_manager
+    goto BUILD
+)
+if "%PKG_NAME%" == "adbc-driver-flightsql" (
+    pushd "%SRC_DIR%"\python\adbc_driver_flightsql
+    set ADBC_FLIGHTSQL_LIBRARY=%LIBRARY_BIN%\adbc_driver_flightsql.dll
+    goto BUILD
+)
+if "%PKG_NAME%" == "adbc-driver-postgresql" (
+    pushd "%SRC_DIR%"\python\adbc_driver_postgresql
+    set ADBC_POSTGRESQL_LIBRARY=%LIBRARY_BIN%\adbc_driver_postgresql.dll
+    goto BUILD
+)
+if "%PKG_NAME%" == "adbc-driver-sqlite" (
+    pushd "%SRC_DIR%"\python\adbc_driver_sqlite
+    set ADBC_SQLITE_LIBRARY=%LIBRARY_BIN%\adbc_driver_sqlite.dll
+    goto BUILD
+)
+echo Unknown package %PKG_NAME%
+exit 1
+
+:BUILD
+
+set _ADBC_IS_CONDA=1
+set SETUPTOOLS_SCM_PRETEND_VERSION=%PKG_VERSION%
+
+echo "==== INSTALL %PKG_NAME%"
+%PYTHON% -m pip install . -vvv --no-deps || exit /B 1
+
+popd
diff --git a/ci/conda/build-python.sh b/ci/conda/build-python.sh
new file mode 100644
index 0000000..9684041
--- /dev/null
+++ b/ci/conda/build-python.sh
@@ -0,0 +1,49 @@
+#!/bin/bash
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+set -ex
+
+if [[ "$(uname)" = "Darwin" ]]; then
+    LIB_SUFFIX="dylib"
+else
+    LIB_SUFFIX="so"
+fi
+
+if [[ "${PKG_NAME}" = "adbc-driver-manager" ]]; then
+    pushd python/adbc_driver_manager
+elif [[ "${PKG_NAME}" = "adbc-driver-flightsql" ]]; then
+    pushd python/adbc_driver_flightsql
+    export ADBC_FLIGHTSQL_LIBRARY=$PREFIX/lib/libadbc_driver_flightsql.$LIB_SUFFIX
+elif [[ "${PKG_NAME}" = "adbc-driver-postgresql" ]]; then
+    pushd python/adbc_driver_postgresql
+    export ADBC_POSTGRESQL_LIBRARY=$PREFIX/lib/libadbc_driver_postgresql.$LIB_SUFFIX
+elif [[ "${PKG_NAME}" = "adbc-driver-sqlite" ]]; then
+    pushd python/adbc_driver_sqlite
+    export ADBC_SQLITE_LIBRARY=$PREFIX/lib/libadbc_driver_sqlite.$LIB_SUFFIX
+else
+    echo "Unknown package ${PKG_NAME}"
+    exit 1
+fi
+
+export _ADBC_IS_CONDA=1
+export SETUPTOOLS_SCM_PRETEND_VERSION=$PKG_VERSION
+
+echo "==== INSTALL ${PKG_NAME}"
+$PYTHON -m pip install . -vvv --no-deps
+
+popd
diff --git a/python/adbc_driver_postgresql/adbc_driver_postgresql/__init__.py b/ci/conda/conda-forge.yml
similarity index 64%
copy from python/adbc_driver_postgresql/adbc_driver_postgresql/__init__.py
copy to ci/conda/conda-forge.yml
index 8b17176..6c99042 100644
--- a/python/adbc_driver_postgresql/adbc_driver_postgresql/__init__.py
+++ b/ci/conda/conda-forge.yml
@@ -15,17 +15,17 @@
 # specific language governing permissions and limitations
 # under the License.
 
-import importlib.resources
-
-import adbc_driver_manager
-
-from ._version import __version__
-
-__all__ = ["connect", "__version__"]
-
-
-def connect(uri: str) -> adbc_driver_manager.AdbcDatabase:
-    """Create a low level ADBC connection to PostgreSQL."""
-    root = importlib.resources.files(__package__)
-    entrypoint = root.joinpath("libadbc_driver_postgresql.so")
-    return adbc_driver_manager.AdbcDatabase(driver=str(entrypoint), uri=uri)
+build_platform:
+  linux_aarch64: linux_64
+  linux_ppc64le: linux_64
+  osx_arm64: osx_64
+conda_forge_output_validation: true
+github:
+  branch_name: main
+  tooling_branch_name: main
+recipe_dir: .
+skip_render:
+  - .gitignore
+  - LICENSE.txt
+  - README.md
+test_on_native_only: true
diff --git a/ci/conda/meta.yaml b/ci/conda/meta.yaml
new file mode 100644
index 0000000..92e2879
--- /dev/null
+++ b/ci/conda/meta.yaml
@@ -0,0 +1,262 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+package:
+  name: arrow-adbc-split
+  # TODO: this needs to get bumped by the release process
+  version: 0.2.0
+
+source:
+  path: ../../
+
+build:
+  number: 0
+
+outputs:
+  - name: adbc-driver-manager-cpp
+    script: build-cpp.sh                                                         # [not win]
+    script: build-cpp.bat                                                        # [win]
+    requirements:
+      build:
+        - {{ compiler('cxx') }}
+        - cmake
+        - ninja
+      host:
+      run:
+    test:
+      commands:
+        - test -f $PREFIX/include/adbc.h                                         # [unix]
+        - test -f $PREFIX/include/adbc_driver_manager.h                          # [unix]
+        - test -d $PREFIX/lib/cmake/AdbcDriverManager/                           # [unix]
+        - test -f $PREFIX/lib/pkgconfig/adbc-driver-manager.pc                   # [unix]
+        - test ! -f $PREFIX/lib/libadbc_driver_manager.a                         # [unix]
+        - test -f $PREFIX/lib/libadbc_driver_manager.so                          # [linux]
+        - test -f $PREFIX/lib/libadbc_driver_manager.dylib                       # [osx]
+
+        - if not exist %LIBRARY_BIN%\adbc_driver_manager.dll exit 1              # [win]
+        - if not exist %LIBRARY_INC%\adbc.h exit 1                               # [win]
+        - if not exist %LIBRARY_INC%\adbc_driver_manager.h exit 1                # [win]
+        - if not exist %LIBRARY_LIB%\adbc_driver_manager.lib exit 1              # [win]
+        - if not exist %LIBRARY_LIB%\cmake\AdbcDriverManager exit 1              # [win]
+        - if not exist %LIBRARY_LIB%\pkgconfig\adbc-driver-manager.pc exit 1     # [win]
+
+  - name: adbc-driver-flightsql-go
+    script: build-cpp.sh                                                         # [not win]
+    script: build-cpp.bat                                                        # [win]
+    requirements:
+      build:
+        - {{ compiler('c') }}
+        - {{ compiler('cxx') }}
+        - _go_select *=cgo
+        - cmake
+        - go-cgo >=1.18
+        - ninja
+        - pkg-config
+    test:
+      commands:
+        - test ! -f $PREFIX/include/adbc.h                                       # [unix]
+        - test -f $PREFIX/lib/pkgconfig/adbc-driver-flightsql.pc                 # [unix]
+        - test ! -f $PREFIX/lib/libadbc_driver_flightsql.a                       # [unix]
+        - test -f $PREFIX/lib/libadbc_driver_flightsql.so                        # [linux]
+        - test -f $PREFIX/lib/libadbc_driver_flightsql.dylib                     # [osx]
+
+        - if exist %LIBRARY_INC%\adbc.h exit 1                                   # [win]
+        - if not exist %LIBRARY_BIN%\adbc_driver_flightsql.dll exit 1            # [win]
+        - if not exist %LIBRARY_LIB%\adbc_driver_flightsql.lib exit 1            # [win]
+        - if not exist %LIBRARY_LIB%\pkgconfig\adbc-driver-flightsql.pc exit 1   # [win]
+
+  - name: adbc-driver-postgresql-cpp
+    script: build-cpp.sh                                                         # [not win]
+    script: build-cpp.bat                                                        # [win]
+    requirements:
+      build:
+        - {{ compiler('c') }}
+        - {{ compiler('cxx') }}
+        - cmake
+        - ninja
+        - pkg-config
+      host:
+        - libpq
+      run:
+        - libpq
+    test:
+      commands:
+        - test ! -f $PREFIX/include/adbc.h                                       # [unix]
+        - test -d $PREFIX/lib/cmake/AdbcDriverPostgreSQL/                        # [unix]
+        - test -f $PREFIX/lib/pkgconfig/adbc-driver-postgresql.pc                # [unix]
+        - test ! -f $PREFIX/lib/libadbc_driver_postgresql.a                      # [unix]
+        - test -f $PREFIX/lib/libadbc_driver_postgresql.so                       # [linux]
+        - test -f $PREFIX/lib/libadbc_driver_postgresql.dylib                    # [osx]
+
+        - if exist %LIBRARY_INC%\adbc.h exit 1                                   # [win]
+        - if not exist %LIBRARY_BIN%\adbc_driver_postgresql.dll exit 1           # [win]
+        - if not exist %LIBRARY_LIB%\adbc_driver_postgresql.lib exit 1           # [win]
+        - if not exist %LIBRARY_LIB%\cmake\AdbcDriverPostgreSQL exit 1           # [win]
+        - if not exist %LIBRARY_LIB%\pkgconfig\adbc-driver-postgresql.pc exit 1  # [win]
+
+  - name: adbc-driver-sqlite-cpp
+    script: build-cpp.sh                                                         # [not win]
+    script: build-cpp.bat                                                        # [win]
+    requirements:
+      build:
+        - {{ compiler('c') }}
+        - {{ compiler('cxx') }}
+        - cmake
+        - ninja
+        - pkg-config
+      host:
+        - libsqlite
+      run:
+        - libsqlite
+    test:
+      commands:
+        - test ! -f $PREFIX/include/adbc.h                                       # [unix]
+        - test -d $PREFIX/lib/cmake/AdbcDriverSQLite/                            # [unix]
+        - test -f $PREFIX/lib/pkgconfig/adbc-driver-sqlite.pc                    # [unix]
+        - test ! -f $PREFIX/lib/libadbc_driver_sqlite.a                          # [unix]
+        - test -f $PREFIX/lib/libadbc_driver_sqlite.so                           # [linux]
+        - test -f $PREFIX/lib/libadbc_driver_sqlite.dylib                        # [osx]
+
+        - if exist %LIBRARY_INC%\adbc.h exit 1                                   # [win]
+        - if not exist %LIBRARY_BIN%\adbc_driver_sqlite.dll exit 1               # [win]
+        - if not exist %LIBRARY_LIB%\adbc_driver_sqlite.lib exit 1               # [win]
+        - if not exist %LIBRARY_LIB%\cmake\AdbcDriverSQLite exit 1               # [win]
+        - if not exist %LIBRARY_LIB%\pkgconfig\adbc-driver-sqlite.pc exit 1      # [win]
+
+  - name: adbc-driver-manager
+    script: build-python.sh                                                      # [not win]
+    script: build-python.bat                                                     # [win]
+    requirements:
+      build:
+        - {{ compiler('cxx') }}
+        - cross-python_{{ target_platform }}                                     # [build_platform != target_platform]
+        - cython
+        - pip
+        - python {{ python }}
+        - setuptools
+      host:
+        - cython
+        - pip
+        - python
+        - setuptools
+      run:
+        - python
+      run_constrained:
+        - pyarrow >=8.0.0
+    test:
+      imports:
+        - adbc_driver_manager
+        - adbc_driver_manager._lib
+
+  - name: adbc-driver-flightsql
+    build:
+      noarch: python
+    script: build-python.sh                                                      # [not win]
+    script: build-python.bat                                                     # [win]
+    requirements:
+      build:
+        - cross-python_{{ target_platform }}                                     # [build_platform != target_platform]
+        - pip
+        - setuptools
+        - python
+      host:
+        - {{ pin_subpackage('adbc-driver-flightsql-go', exact=True) }}
+        - pip
+        - python
+        - setuptools
+      run:
+        - {{ pin_subpackage('adbc-driver-manager') }}
+        - {{ pin_subpackage('adbc-driver-flightsql-go', exact=True) }}
+        - python
+      run_constrained:
+        - pyarrow >=8.0.0
+    test:
+      imports:
+        - adbc_driver_flightsql
+
+  - name: adbc-driver-postgresql
+    build:
+      noarch: python
+    script: build-python.sh                                                      # [not win]
+    script: build-python.bat                                                     # [win]
+    requirements:
+      build:
+        - cross-python_{{ target_platform }}                                     # [build_platform != target_platform]
+        - pip
+        - setuptools
+        - python
+      host:
+        - {{ pin_subpackage('adbc-driver-postgresql-cpp', exact=True) }}
+        - pip
+        - python
+        - setuptools
+      run:
+        - {{ pin_subpackage('adbc-driver-manager') }}
+        - {{ pin_subpackage('adbc-driver-postgresql-cpp', exact=True) }}
+        - python
+      run_constrained:
+        - pyarrow >=8.0.0
+    test:
+      imports:
+        - adbc_driver_postgresql
+
+  - name: adbc-driver-sqlite
+    build:
+      noarch: python
+    script: build-python.sh                                                      # [not win]
+    script: build-python.bat                                                     # [win]
+    requirements:
+      build:
+        - cross-python_{{ target_platform }}                                     # [build_platform != target_platform]
+        - pip
+        - setuptools
+        - python
+      host:
+        - {{ pin_subpackage('adbc-driver-sqlite-cpp', exact=True) }}
+        - pip
+        - python
+        - setuptools
+      run:
+        - {{ pin_subpackage('adbc-driver-manager') }}
+        - {{ pin_subpackage('adbc-driver-sqlite-cpp', exact=True) }}
+        - python
+      run_constrained:
+        - pyarrow >=8.0.0
+    test:
+      imports:
+        - adbc_driver_sqlite
+
+about:
+  home: https://arrow.apache.org
+  summary: 'Database access libraries for Apache Arrow'
+  description: |
+    ADBC is an API standard for database access libraries ("drivers")
+    in C and Java that uses Arrow for data. Instead of writing code
+    for each individual database, applications can build against the
+    ADBC APIs, and link against drivers that implement the
+    standard. Additionally, a JDBC/ODBC-style driver manager is
+    provided. This also implements the ADBC APIs, but dynamically
+    loads drivers and dispatches calls to them.
+  license: Apache-2.0
+  license_family: APACHE
+  license_file: LICENSE.txt
+  doc_url: https://arrow.apache.org/docs/
+  dev_url: https://github.com/apache/arrow-adbc
+
+extra:
+  recipe-maintainers:
+    - lidavidm
diff --git a/python/adbc_driver_sqlite/adbc_driver_sqlite/__init__.py b/ci/scripts/python_conda_build.sh
old mode 100644
new mode 100755
similarity index 52%
copy from python/adbc_driver_sqlite/adbc_driver_sqlite/__init__.py
copy to ci/scripts/python_conda_build.sh
index 184fb46..580a3ff
--- a/python/adbc_driver_sqlite/adbc_driver_sqlite/__init__.py
+++ b/ci/scripts/python_conda_build.sh
@@ -1,3 +1,5 @@
+#!/usr/bin/env bash
+#
 # Licensed to the Apache Software Foundation (ASF) under one
 # or more contributor license agreements.  See the NOTICE file
 # distributed with this work for additional information
@@ -15,20 +17,34 @@
 # specific language governing permissions and limitations
 # under the License.
 
-import importlib.resources
-import typing
+# Build the Conda packages
+
+set -e
+set -o pipefail
+set -u
+set -x
 
-import adbc_driver_manager
+main() {
+    if [[ "$#" != 3 ]]; then
+       echo "Usage: ${0} <adbc-checkout> <platform-config> <output-path>"
+       echo "<platform-config> should be the name of a YAML file in ci/conda/.ci_support."
+       exit 1
+    fi
+    # Path to ADBC repo
+    local -r repo="${1}"
+    local -r platform_config="${repo}/ci/conda/.ci_support/${2}"
+    # Path to output directory
+    local -r output="${3}"
 
-from ._version import __version__
+    mkdir -p "${output}"
+    export PYTHONUNBUFFERED=1
 
-__all__ = ["connect", "__version__"]
+    conda config --show
 
+    conda mambabuild \
+          "${repo}/ci/conda" \
+          -m "${platform_config}" \
+          --output-folder "${output}/conda"
+}
 
-def connect(uri: typing.Optional[str] = None) -> adbc_driver_manager.AdbcDatabase:
-    """Create a low level ADBC connection to SQLite."""
-    root = importlib.resources.files(__package__)
-    entrypoint = root.joinpath("libadbc_driver_sqlite.so")
-    if uri is None:
-        return adbc_driver_manager.AdbcDatabase(driver=str(entrypoint))
-    return adbc_driver_manager.AdbcDatabase(driver=str(entrypoint), uri=uri)
+main "$@"
diff --git a/ci/scripts/python_conda_test.sh b/ci/scripts/python_conda_test.sh
new file mode 100755
index 0000000..f430f25
--- /dev/null
+++ b/ci/scripts/python_conda_test.sh
@@ -0,0 +1,68 @@
+#!/usr/bin/env bash
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+# Build the Conda packages
+
+set -e
+set -o pipefail
+set -u
+set -x
+
+PYTHON_VERSIONS="3.9 3.10 3.11"
+
+main() {
+    if [[ "$#" != 2 ]]; then
+       echo "Usage: ${0} <adbc-checkout> <output-path>"
+       exit 1
+    fi
+    # Path to ADBC repo
+    local -r source_dir="${1}"
+    # Path to output directory
+    local -r output="${2}"
+
+    # For $COMPONENTS, test_packages
+    source "${source_dir}/ci/scripts/python_util.sh"
+
+    eval "$(conda shell.bash hook)"
+
+    for python_version in $PYTHON_VERSIONS; do
+        echo "=== Testing Python ${python_version} ==="
+        local env="${output}/conda-test/${python_version}"
+        mkdir -p "${env}"
+
+        mamba create \
+              -y \
+              -p "${env}" \
+              -c conda-forge \
+              --file "${source_dir}/ci/conda_env_python.txt" \
+              python="${python_version}" \
+              pyarrow
+
+        conda activate "${env}"
+
+        mamba install \
+              -y \
+              -c "${output}/conda" \
+              ${COMPONENTS//_/-}
+
+        test_packages
+    done
+}
+
+main "$@"
diff --git a/python/adbc_driver_postgresql/adbc_driver_postgresql/__init__.py b/ci/scripts/python_conda_upload.sh
old mode 100644
new mode 100755
similarity index 64%
copy from python/adbc_driver_postgresql/adbc_driver_postgresql/__init__.py
copy to ci/scripts/python_conda_upload.sh
index 8b17176..c8ac293
--- a/python/adbc_driver_postgresql/adbc_driver_postgresql/__init__.py
+++ b/ci/scripts/python_conda_upload.sh
@@ -1,3 +1,4 @@
+#!/usr/bin/env bash
 # Licensed to the Apache Software Foundation (ASF) under one
 # or more contributor license agreements.  See the NOTICE file
 # distributed with this work for additional information
@@ -15,17 +16,18 @@
 # specific language governing permissions and limitations
 # under the License.
 
-import importlib.resources
+set -e
+set -o pipefail
+set -u
+set -x
 
-import adbc_driver_manager
+: ${CHANNEL:="arrow-adbc-nightlies"}
 
-from ._version import __version__
+main() {
+    anaconda -t "${ANACONDA_API_TOKEN}" \
+             upload \
+             --user "${CHANNEL}" \
+             "$@"
+}
 
-__all__ = ["connect", "__version__"]
-
-
-def connect(uri: str) -> adbc_driver_manager.AdbcDatabase:
-    """Create a low level ADBC connection to PostgreSQL."""
-    root = importlib.resources.files(__package__)
-    entrypoint = root.joinpath("libadbc_driver_postgresql.so")
-    return adbc_driver_manager.AdbcDatabase(driver=str(entrypoint), uri=uri)
+main "$@"
diff --git a/dev/release/rat_exclude_files.txt b/dev/release/rat_exclude_files.txt
index 36b36e0..c808677 100644
--- a/dev/release/rat_exclude_files.txt
+++ b/dev/release/rat_exclude_files.txt
@@ -1,4 +1,6 @@
 *.json
+ci/conda/.ci_support/*
+ci/conda/.gitattributes
 ci/linux-packages/changelog
 ci/linux-packages/*.install
 dev/release/rat_exclude_files.txt
diff --git a/dev/release/utils-prepare.sh b/dev/release/utils-prepare.sh
index 1127ac2..22c32eb 100644
--- a/dev/release/utils-prepare.sh
+++ b/dev/release/utils-prepare.sh
@@ -25,10 +25,12 @@ update_versions() {
   case ${type} in
     release)
       local version=${base_version}
+      local conda_version=${base_version}
       local docs_version=${base_version}
       ;;
     snapshot)
       local version=${next_version}-SNAPSHOT
+      local conda_version=${next_version}
       local docs_version="${next_version} (dev)"
       ;;
   esac
@@ -40,6 +42,12 @@ update_versions() {
   git add cmake_modules/AdbcVersion.cmake
   popd
 
+  pushd "${ADBC_DIR}/ci/conda/"
+  sed -i.bak -E "s/version: .+/version: ${conda_version}/g" meta.yaml
+  rm meta.yaml.bak
+  git add meta.yaml
+  popd
+
   sed -i.bak -E "s/release = \".+\"/release = \"${docs_version}\"/g" "${ADBC_DIR}/docs/source/conf.py"
   rm "${ADBC_DIR}/docs/source/conf.py.bak"
   git add "${ADBC_DIR}/docs/source/conf.py"
diff --git a/docker-compose.yml b/docker-compose.yml
index 6c78ece..acd47d1 100644
--- a/docker-compose.yml
+++ b/docker-compose.yml
@@ -51,6 +51,21 @@ services:
       - .:/adbc:delegated
     command: "/bin/bash -c '/adbc/ci/scripts/java_build.sh /adbc /adbc/dist && /adbc/ci/scripts/java_test.sh /adbc'"
 
+  ############################ Python conda ##################################
+
+  # Must be run as docker-compose run -e HOST_USER_ID=$(id -u) python-conda
+  python-conda:
+    image: condaforge/linux-anvil-cos7-x86_64
+    volumes:
+      - .:/adbc:delegated
+    command: "/bin/bash -c 'conda config --remove channels defaults && /adbc/ci/scripts/python_conda_build.sh /adbc ${ARCH_CONDA_FORGE}.yaml /adbc/build'"
+
+  python-conda-test:
+    image: condaforge/mambaforge
+    volumes:
+      - .:/adbc:delegated
+    command: "/bin/bash -c '/adbc/ci/scripts/python_conda_test.sh /adbc /adbc/build'"
+
   ############################ Python sdist ##################################
 
   python-sdist:
diff --git a/docs/source/development/nightly.rst b/docs/source/development/nightly.rst
index 750edd0..f781d4b 100644
--- a/docs/source/development/nightly.rst
+++ b/docs/source/development/nightly.rst
@@ -28,7 +28,33 @@ Nightly builds of some binary packages are available.
 C/C++
 =====
 
-Not yet available.
+Conda users can install nightly builds from a Conda channel:
+https://anaconda.org/arrow-adbc-nightlies
+
+This should be used with conda-forge.  Example::
+
+  mamba install -c conda-forge -c arrow-adbc-nightlies adbc-driver-manager
+
+Both C/C++ and Python packages are available.
+
+.. list-table:: Supported platforms for nightly Conda packages
+   :header-rows: 1
+
+   * - Operating System
+     - AMD64 (x86_64)
+     - AArch64
+
+   * - Linux
+     - Yes ✅
+     - No ❌
+
+   * - macOS
+     - Yes ✅
+     - No ❌
+
+   * - Windows
+     - No ✅
+     - No ❌
 
 Java
 ====
@@ -41,15 +67,11 @@ Python
 Packages can be installed from an alternative package index:
 https://gemfury.com/arrow-adbc-nightlies
 
-.. warning:: You MUST explicitly specify the version, since otherwise
-             pip will install the latest version by lexicographical
-             ordering, which will likely be some random, old build.
-
 Example::
 
   pip install \
         --extra-index-url https://repo.fury.io/arrow-adbc-nightlies \
-        adbc_driver_manager==0.0.0+g265a1b6
+        adbc-driver-manager
 
 .. list-table:: Supported platforms for nightly Python wheels
    :header-rows: 1
diff --git a/python/adbc_driver_flightsql/adbc_driver_flightsql/__init__.py b/python/adbc_driver_flightsql/adbc_driver_flightsql/__init__.py
index 9dd1992..c6a9342 100644
--- a/python/adbc_driver_flightsql/adbc_driver_flightsql/__init__.py
+++ b/python/adbc_driver_flightsql/adbc_driver_flightsql/__init__.py
@@ -15,7 +15,7 @@
 # specific language governing permissions and limitations
 # under the License.
 
-import importlib.resources
+import functools
 import typing
 
 import adbc_driver_manager
@@ -38,8 +38,38 @@ def connect(
     db_kwargs : dict, optional
         Initial database connection parameters.
     """
-    root = importlib.resources.files(__package__)
-    entrypoint = root.joinpath("libadbc_driver_flightsql.so")
     return adbc_driver_manager.AdbcDatabase(
-        driver=str(entrypoint), uri=uri, **(db_kwargs or {})
+        driver=_driver_path(), uri=uri, **(db_kwargs or {})
     )
+
+
+@functools.cache
+def _driver_path() -> str:
+    import importlib.resources
+    import pathlib
+    import sys
+
+    driver = "adbc_driver_flightsql"
+
+    # Wheels bundle the shared library
+    root = importlib.resources.files(__package__)
+    # The filename is always the same regardless of platform
+    entrypoint = root.joinpath(f"lib{driver}.so")
+    if entrypoint.is_file():
+        return str(entrypoint)
+
+    # Search sys.prefix + '/lib' (Unix, Conda on Unix)
+    root = pathlib.Path(sys.prefix)
+    for filename in (f"lib{driver}.so", f"lib{driver}.dylib"):
+        entrypoint = root.joinpath("lib", filename)
+        if entrypoint.is_file():
+            return str(entrypoint)
+
+    # Conda on Windows
+    entrypoint = root.joinpath("bin", f"{driver}.dll")
+    if entrypoint.is_file():
+        return str(entrypoint)
+
+    # Let the driver manager fall back to (DY)LD_LIBRARY_PATH/PATH
+    # (It will insert 'lib', 'so', etc. as needed)
+    return driver
diff --git a/python/adbc_driver_flightsql/setup.py b/python/adbc_driver_flightsql/setup.py
index 83c2fcb..d8424b7 100644
--- a/python/adbc_driver_flightsql/setup.py
+++ b/python/adbc_driver_flightsql/setup.py
@@ -36,6 +36,8 @@ target = source_root.joinpath(
 if not library:
     if os.environ.get("_ADBC_IS_SDIST", "").strip().lower() in ("1", "true"):
         print("Building sdist, not requiring ADBC_FLIGHTSQL_LIBRARY")
+    elif os.environ.get("_ADBC_IS_CONDA", "").strip().lower() in ("1", "true"):
+        print("Building Conda package, not requiring ADBC_FLIGHTSQL_LIBRARY")
     elif target.is_file():
         print("Driver already exists (but may be stale?), continuing")
     else:
diff --git a/python/adbc_driver_postgresql/adbc_driver_postgresql/__init__.py b/python/adbc_driver_postgresql/adbc_driver_postgresql/__init__.py
index 8b17176..7df1504 100644
--- a/python/adbc_driver_postgresql/adbc_driver_postgresql/__init__.py
+++ b/python/adbc_driver_postgresql/adbc_driver_postgresql/__init__.py
@@ -15,7 +15,7 @@
 # specific language governing permissions and limitations
 # under the License.
 
-import importlib.resources
+import functools
 
 import adbc_driver_manager
 
@@ -26,6 +26,36 @@ __all__ = ["connect", "__version__"]
 
 def connect(uri: str) -> adbc_driver_manager.AdbcDatabase:
     """Create a low level ADBC connection to PostgreSQL."""
+    return adbc_driver_manager.AdbcDatabase(driver=_driver_path(), uri=uri)
+
+
+@functools.cache
+def _driver_path() -> str:
+    import importlib.resources
+    import pathlib
+    import sys
+
+    driver = "adbc_driver_postgresql"
+
+    # Wheels bundle the shared library
     root = importlib.resources.files(__package__)
-    entrypoint = root.joinpath("libadbc_driver_postgresql.so")
-    return adbc_driver_manager.AdbcDatabase(driver=str(entrypoint), uri=uri)
+    # The filename is always the same regardless of platform
+    entrypoint = root.joinpath(f"lib{driver}.so")
+    if entrypoint.is_file():
+        return str(entrypoint)
+
+    # Search sys.prefix + '/lib' (Unix, Conda on Unix)
+    root = pathlib.Path(sys.prefix)
+    for filename in (f"lib{driver}.so", f"lib{driver}.dylib"):
+        entrypoint = root.joinpath("lib", filename)
+        if entrypoint.is_file():
+            return str(entrypoint)
+
+    # Conda on Windows
+    entrypoint = root.joinpath("bin", f"{driver}.dll")
+    if entrypoint.is_file():
+        return str(entrypoint)
+
+    # Let the driver manager fall back to (DY)LD_LIBRARY_PATH/PATH
+    # (It will insert 'lib', 'so', etc. as needed)
+    return driver
diff --git a/python/adbc_driver_postgresql/setup.py b/python/adbc_driver_postgresql/setup.py
index 088fcf6..9987209 100644
--- a/python/adbc_driver_postgresql/setup.py
+++ b/python/adbc_driver_postgresql/setup.py
@@ -33,10 +33,11 @@ library = os.environ.get("ADBC_POSTGRESQL_LIBRARY")
 target = source_root.joinpath(
     "./adbc_driver_postgresql/libadbc_driver_postgresql.so"
 ).resolve()
-
 if not library:
     if os.environ.get("_ADBC_IS_SDIST", "").strip().lower() in ("1", "true"):
         print("Building sdist, not requiring ADBC_POSTGRESQL_LIBRARY")
+    elif os.environ.get("_ADBC_IS_CONDA", "").strip().lower() in ("1", "true"):
+        print("Building Conda package, not requiring ADBC_POSTGRESQL_LIBRARY")
     elif target.is_file():
         print("Driver already exists (but may be stale?), continuing")
     else:
diff --git a/python/adbc_driver_sqlite/adbc_driver_sqlite/__init__.py b/python/adbc_driver_sqlite/adbc_driver_sqlite/__init__.py
index 184fb46..5422ff3 100644
--- a/python/adbc_driver_sqlite/adbc_driver_sqlite/__init__.py
+++ b/python/adbc_driver_sqlite/adbc_driver_sqlite/__init__.py
@@ -15,7 +15,7 @@
 # specific language governing permissions and limitations
 # under the License.
 
-import importlib.resources
+import functools
 import typing
 
 import adbc_driver_manager
@@ -27,8 +27,38 @@ __all__ = ["connect", "__version__"]
 
 def connect(uri: typing.Optional[str] = None) -> adbc_driver_manager.AdbcDatabase:
     """Create a low level ADBC connection to SQLite."""
-    root = importlib.resources.files(__package__)
-    entrypoint = root.joinpath("libadbc_driver_sqlite.so")
     if uri is None:
-        return adbc_driver_manager.AdbcDatabase(driver=str(entrypoint))
-    return adbc_driver_manager.AdbcDatabase(driver=str(entrypoint), uri=uri)
+        return adbc_driver_manager.AdbcDatabase(driver=_driver_path())
+    return adbc_driver_manager.AdbcDatabase(driver=_driver_path(), uri=uri)
+
+
+@functools.cache
+def _driver_path() -> str:
+    import importlib.resources
+    import pathlib
+    import sys
+
+    driver = "adbc_driver_sqlite"
+
+    # Wheels bundle the shared library
+    root = importlib.resources.files(__package__)
+    # The filename is always the same regardless of platform
+    entrypoint = root.joinpath(f"lib{driver}.so")
+    if entrypoint.is_file():
+        return str(entrypoint)
+
+    # Search sys.prefix + '/lib' (Unix, Conda on Unix)
+    root = pathlib.Path(sys.prefix)
+    for filename in (f"lib{driver}.so", f"lib{driver}.dylib"):
+        entrypoint = root.joinpath("lib", filename)
+        if entrypoint.is_file():
+            return str(entrypoint)
+
+    # Conda on Windows
+    entrypoint = root.joinpath("bin", f"{driver}.dll")
+    if entrypoint.is_file():
+        return str(entrypoint)
+
+    # Let the driver manager fall back to (DY)LD_LIBRARY_PATH/PATH
+    # (It will insert 'lib', 'so', etc. as needed)
+    return driver
diff --git a/python/adbc_driver_sqlite/setup.py b/python/adbc_driver_sqlite/setup.py
index eaa47a7..4bc1336 100644
--- a/python/adbc_driver_sqlite/setup.py
+++ b/python/adbc_driver_sqlite/setup.py
@@ -34,6 +34,8 @@ target = source_root.joinpath("./adbc_driver_sqlite/libadbc_driver_sqlite.so").r
 if not library:
     if os.environ.get("_ADBC_IS_SDIST", "").strip().lower() in ("1", "true"):
         print("Building sdist, not requiring ADBC_SQLITE_LIBRARY")
+    elif os.environ.get("_ADBC_IS_CONDA", "").strip().lower() in ("1", "true"):
+        print("Building Conda package, not requiring ADBC_SQLITE_LIBRARY")
     elif target.is_file():
         print("Driver already exists (but may be stale?), continuing")
     else: