Skip to content

Commit aa45d7f

Browse files
authored
CHORE: Add standalone mssql-python-odbc package for ODBC driver binaries (#663)
### Work Item / Issue Reference > [AB#46217](https://sqlclientdrivers.visualstudio.com/c6d89619-62de-46a0-8b46-70b92a84d85e/_workitems/edit/46217) ------------------------------------------------------------------- ### Summary Splits the ODBC driver binaries out of `mssql-python` into a new standalone, pure-data package `mssql-python-odbc` (import name `mssql_python_odbc`, version 18.6.0). This is the code / packaging half of the split; the ADO release pipelines are in companion PR #664. #### What is included - `mssql_python_odbc/__init__.py` - pure-Python package (no native extension) that ships the ODBC driver `libs/` tree and exposes `get_driver_path()` and `get_libs_dir()` with the same per-platform layout the C++ loader expects. - `setup_odbc.py` - builds the `mssql-python-odbc` wheels. Each wheel is platform-specific but Python-agnostic (`py3-none-<platform>`), so we ship 7 wheels total (Windows amd64/arm64, macOS universal2, manylinux_2_28 x86_64/aarch64, musllinux_1_2 x86_64/aarch64) instead of a per-Python matrix. On Windows the wheel intentionally ships the VC++ runtime (`vcredist/msvcp140.dll` + license) alongside the driver binaries - see "VC++ runtime" below. - `mssql_python/pybind/ddbc_bindings.cpp` - `LoadDriverOrThrowException()` now resolves the driver / libs base directory via a new `GetOdbcLibsBaseDir()` helper, which imports `mssql_python_odbc` when present and falls back to the bundled `mssql_python` libs when it is not. The fallback is GIL-safe and Alpine/musl-safe. - `tests/test_024_odbc_package_split.py` - verifies Python vs C++ driver-path parity. - `.gitignore` - ignores local `mssql_python_odbc/libs/` build copies and egg-info. #### VC++ runtime (`vcredist`) on Windows The Windows wheel ships `libs/windows/<arch>/vcredist/msvcp140.dll` (with its license). This is a deliberate, documented choice - **not** "parity" with the main wheel's file layout, and not a reversal of a licensing/size decision: - The main `mssql-python` wheel also ships the VC++ runtime, just relocated: `build.bat` copies `msvcp140.dll` out of `vcredist/` to the package root next to the compiled `ddbc_bindings` extension, and `setup.py` then excludes the now-duplicate `vcredist/` folder. That exclusion is **de-duplication**, not a removal of the runtime. - This package has no compiled extension, so there is no package-root `.pyd` to place the runtime beside. Keeping `msvcp140.dll` in its original `vcredist/` folder keeps the driver binaries and the runtime they were built against together, and keeps the wheel self-contained. - No Phase-2 runtime skew: `msodbcsql18.dll`'s dependency on `msvcp140.dll` is satisfied in-process by the mssql-python extension (built `/MD`, which loads `msvcp140.dll` from beside the `.pyd`), so the external-package and bundled-libs paths behave identically. This copy is completeness + attribution, which is why `GetOdbcLibsBaseDir()`'s completeness check verifies the driver + `mssql-auth.dll` but not `vcredist`. #### Phasing (no breaking change in this PR) - Phase 2 (this PR): `mssql-python` prefers the external package but keeps bundling `libs/` as a fallback, so existing installs keep working. - Phase 3 (future, v2.0.0): remove the bundled `libs/` and depend on `mssql-python-odbc` exclusively. #### Testing - `test_024` parity suite: 7 passed, 1 skipped. - Built `mssql_python_odbc-18.6.0-py3-none-win_amd64.whl` locally: correct wheel tag, `Root-Is-Purelib: false`, driver DLLs present, `vcredist` (`msvcp140.dll` + license) present, `twine check` passes. #### Notes Companion PR (release pipelines): #664. Infra registration (ESRP onboarding of `mssql-python-odbc`, ADO build-definition id) is tracked there.
1 parent 0966b05 commit aa45d7f

10 files changed

Lines changed: 695 additions & 46 deletions

File tree

.gitignore

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,13 @@ build/
2323
**/build/
2424
mssql_python.egg-info/
2525

26+
# ODBC package (mssql-python-odbc): transition-period build artifacts.
27+
# During Phase 2, setup_odbc.py copies the current platform's driver binaries
28+
# from mssql_python/libs into this tree so a wheel can be built locally. The
29+
# release pipeline populates these per-platform; do not commit the copies.
30+
mssql_python_odbc/libs/
31+
mssql_python_odbc.egg-info/
32+
2633
# Python bytecode
2734
__pycache__/
2835
*.py[cod]

CHANGELOG.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,14 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
1414
connection. The callback is invoked by mssql-tds mid-handshake (FedAuth
1515
workflow 0x02) so the tenant id can be resolved from the server-supplied
1616
STS URL. Requires `mssql-py-core` 0.1.5+. Partial fix for #534.
17+
- **Standalone `mssql-python-odbc` package (PRs #663, #664):** the Microsoft
18+
ODBC Driver 18 for SQL Server binaries are now also published as a separate,
19+
platform-specific `mssql-python-odbc` package. When it is installed, the
20+
native driver loader resolves the driver from it; when it is absent or
21+
incomplete, mssql-python transparently falls back to its own bundled `libs/`.
22+
This is a non-breaking step toward decoupling driver-binary updates from
23+
mssql-python releases; a future major version will make the dependency
24+
explicit and drop the bundled binaries.
1725

1826
### Changed
1927
- Improved error handling in the connection module.

eng/pipelines/pr-validation-pipeline.yml

Lines changed: 62 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -772,6 +772,9 @@ jobs:
772772
773773
- script: |
774774
# Install ODBC driver in the container
775+
DRIVER_VERSION=$(awk -F= '/^__version__/ {gsub(/[[:space:]"]/, "", $2); print $2; exit}' mssql_python_odbc/__init__.py)
776+
DRIVER_MAJOR_MINOR=$(echo "$DRIVER_VERSION" | cut -d. -f1,2)
777+
if [ -z "$DRIVER_MAJOR_MINOR" ]; then echo 'Error: failed to parse mssql_python_odbc.__version__'; exit 1; fi
775778
docker exec test-container-$(distroName) bash -c "
776779
export DEBIAN_FRONTEND=noninteractive
777780
@@ -833,6 +836,9 @@ jobs:
833836

834837
- script: |
835838
# Uninstall ODBC Driver before running tests
839+
DRIVER_VERSION=$(awk -F= '/^__version__/ {gsub(/[[:space:]"]/, "", $2); print $2; exit}' mssql_python_odbc/__init__.py)
840+
DRIVER_MAJOR_MINOR=$(echo "$DRIVER_VERSION" | cut -d. -f1,2)
841+
if [ -z "$DRIVER_MAJOR_MINOR" ]; then echo 'Error: failed to parse mssql_python_odbc.__version__'; exit 1; fi
836842
docker exec test-container-$(distroName) bash -c "
837843
export DEBIAN_FRONTEND=noninteractive
838844
apt-get remove --purge -y msodbcsql18 mssql-tools18 unixodbc-dev
@@ -843,7 +849,7 @@ jobs:
843849
odbcinst -u -d -n 'ODBC Driver 18 for SQL Server' || true
844850
echo 'Uninstalled ODBC Driver and cleaned up libraries'
845851
echo 'Verifying x86_64 debian_ubuntu driver library signatures:'
846-
ldd mssql_python/libs/linux/debian_ubuntu/x86_64/lib/libmsodbcsql-18.6.so.2.1
852+
ldd mssql_python/libs/linux/debian_ubuntu/x86_64/lib/libmsodbcsql-${DRIVER_MAJOR_MINOR}.so.2.1
847853
"
848854
displayName: 'Uninstall ODBC Driver before running tests in $(distroName) container'
849855
@@ -1070,32 +1076,41 @@ jobs:
10701076
# Install dependencies in the ARM64 container
10711077
if [ "$(distroName)" = "Ubuntu" ]; then
10721078
docker exec test-container-$(distroName)-$(archName) bash -c "
1079+
set -euo pipefail
10731080
export DEBIAN_FRONTEND=noninteractive
10741081
export TZ=UTC
10751082
ln -snf /usr/share/zoneinfo/\$TZ /etc/localtime && echo \$TZ > /etc/timezone
1076-
apt-get update &&
1083+
apt-get update
10771084
apt-get install -y python3 python3-pip python3-venv python3-full cmake curl wget gnupg software-properties-common build-essential python3-dev pybind11-dev
10781085
# Verify architecture
10791086
uname -m
10801087
dpkg --print-architecture
1088+
python3 --version
1089+
cmake --version
10811090
"
10821091
else
10831092
# Debian ARM64
10841093
docker exec test-container-$(distroName)-$(archName) bash -c "
1094+
set -euo pipefail
10851095
export DEBIAN_FRONTEND=noninteractive
10861096
export TZ=UTC
10871097
ln -snf /usr/share/zoneinfo/\$TZ /etc/localtime && echo \$TZ > /etc/timezone
1088-
apt-get update &&
1098+
apt-get update
10891099
apt-get install -y python3 python3-pip python3-venv python3-full cmake curl wget gnupg software-properties-common build-essential python3-dev pybind11-dev
10901100
# Verify architecture
10911101
uname -m
10921102
dpkg --print-architecture
1103+
python3 --version
1104+
cmake --version
10931105
"
10941106
fi
10951107
displayName: 'Install basic dependencies in $(distroName) ARM64 container'
10961108
10971109
- script: |
10981110
# Install ODBC driver in the ARM64 container
1111+
DRIVER_VERSION=$(awk -F= '/^__version__/ {gsub(/[[:space:]"]/, "", $2); print $2; exit}' mssql_python_odbc/__init__.py)
1112+
DRIVER_MAJOR_MINOR=$(echo "$DRIVER_VERSION" | cut -d. -f1,2)
1113+
if [ -z "$DRIVER_MAJOR_MINOR" ]; then echo 'Error: failed to parse mssql_python_odbc.__version__'; exit 1; fi
10991114
docker exec test-container-$(distroName)-$(archName) bash -c "
11001115
export DEBIAN_FRONTEND=noninteractive
11011116
@@ -1126,9 +1141,14 @@ jobs:
11261141
- script: |
11271142
# Install Python dependencies in the ARM64 container using virtual environment
11281143
docker exec test-container-$(distroName)-$(archName) bash -c "
1144+
set -euo pipefail
11291145
# Create a virtual environment
11301146
python3 -m venv /opt/venv
11311147
source /opt/venv/bin/activate
1148+
1149+
# Ensure the expected runtime tools are available before build/test steps
1150+
command -v python >/dev/null 2>&1
1151+
command -v cmake >/dev/null 2>&1
11321152
11331153
# Install dependencies in the virtual environment
11341154
python -m pip install --upgrade pip
@@ -1142,7 +1162,14 @@ jobs:
11421162
- script: |
11431163
# Build pybind bindings in the ARM64 container
11441164
docker exec test-container-$(distroName)-$(archName) bash -c "
1165+
set -euo pipefail
1166+
if [ ! -f /opt/venv/bin/activate ]; then
1167+
echo '/opt/venv/bin/activate is missing. Python dependency setup did not complete.'
1168+
exit 1
1169+
fi
11451170
source /opt/venv/bin/activate
1171+
command -v python >/dev/null 2>&1
1172+
command -v cmake >/dev/null 2>&1
11461173
cd mssql_python/pybind
11471174
chmod +x build.sh
11481175
./build.sh
@@ -1158,6 +1185,9 @@ jobs:
11581185

11591186
- script: |
11601187
# Uninstall ODBC Driver before running tests
1188+
DRIVER_VERSION=$(awk -F= '/^__version__/ {gsub(/[[:space:]"]/, "", $2); print $2; exit}' mssql_python_odbc/__init__.py)
1189+
DRIVER_MAJOR_MINOR=$(echo "$DRIVER_VERSION" | cut -d. -f1,2)
1190+
if [ -z "$DRIVER_MAJOR_MINOR" ]; then echo 'Error: failed to parse mssql_python_odbc.__version__'; exit 1; fi
11611191
docker exec test-container-$(distroName)-$(archName) bash -c "
11621192
export DEBIAN_FRONTEND=noninteractive
11631193
apt-get remove --purge -y msodbcsql18 mssql-tools18 unixodbc-dev
@@ -1168,7 +1198,7 @@ jobs:
11681198
odbcinst -u -d -n 'ODBC Driver 11 for SQL Server' || true
11691199
echo 'Uninstalled ODBC Driver and cleaned up libraries'
11701200
echo 'Verifying arm64 debian_ubuntu driver library signatures:'
1171-
ldd mssql_python/libs/linux/debian_ubuntu/arm64/lib/libmsodbcsql-18.6.so.2.1
1201+
ldd mssql_python/libs/linux/debian_ubuntu/arm64/lib/libmsodbcsql-${DRIVER_MAJOR_MINOR}.so.2.1
11721202
"
11731203
displayName: 'Uninstall ODBC Driver before running tests in $(distroName) ARM64 container'
11741204
@@ -1268,6 +1298,9 @@ jobs:
12681298
12691299
- script: |
12701300
# Install dependencies in the RHEL 9 container
1301+
DRIVER_VERSION=$(awk -F= '/^__version__/ {gsub(/[[:space:]"]/, "", $2); print $2; exit}' mssql_python_odbc/__init__.py)
1302+
DRIVER_MAJOR_MINOR=$(echo "$DRIVER_VERSION" | cut -d. -f1,2)
1303+
if [ -z "$DRIVER_MAJOR_MINOR" ]; then echo 'Error: failed to parse mssql_python_odbc.__version__'; exit 1; fi
12711304
docker exec test-container-rhel9 bash -c "
12721305
# Enable CodeReady Builder repository for additional packages
12731306
dnf update -y
@@ -1372,6 +1405,9 @@ jobs:
13721405

13731406
- script: |
13741407
# Uninstall ODBC Driver before running tests
1408+
DRIVER_VERSION=$(awk -F= '/^__version__/ {gsub(/[[:space:]"]/, "", $2); print $2; exit}' mssql_python_odbc/__init__.py)
1409+
DRIVER_MAJOR_MINOR=$(echo "$DRIVER_VERSION" | cut -d. -f1,2)
1410+
if [ -z "$DRIVER_MAJOR_MINOR" ]; then echo 'Error: failed to parse mssql_python_odbc.__version__'; exit 1; fi
13751411
docker exec test-container-rhel9 bash -c "
13761412
dnf remove -y msodbcsql18 mssql-tools18 unixODBC-devel
13771413
rm -f /usr/bin/sqlcmd
@@ -1381,7 +1417,7 @@ jobs:
13811417
odbcinst -u -d -n 'ODBC Driver 11 for SQL Server' || true
13821418
echo 'Uninstalled ODBC Driver and cleaned up libraries'
13831419
echo 'Verifying x86_64 rhel driver library signatures:'
1384-
ldd mssql_python/libs/linux/rhel/x86_64/lib/libmsodbcsql-18.6.so.2.1
1420+
ldd mssql_python/libs/linux/rhel/x86_64/lib/libmsodbcsql-${DRIVER_MAJOR_MINOR}.so.2.1
13851421
"
13861422
displayName: 'Uninstall ODBC Driver before running tests in RHEL 9 container'
13871423
@@ -1489,6 +1525,9 @@ jobs:
14891525
14901526
- script: |
14911527
# Install dependencies in the RHEL 9 ARM64 container
1528+
DRIVER_VERSION=$(awk -F= '/^__version__/ {gsub(/[[:space:]"]/, "", $2); print $2; exit}' mssql_python_odbc/__init__.py)
1529+
DRIVER_MAJOR_MINOR=$(echo "$DRIVER_VERSION" | cut -d. -f1,2)
1530+
if [ -z "$DRIVER_MAJOR_MINOR" ]; then echo 'Error: failed to parse mssql_python_odbc.__version__'; exit 1; fi
14921531
docker exec test-container-rhel9-arm64 bash -c "
14931532
# Enable CodeReady Builder repository for additional packages
14941533
dnf update -y
@@ -1597,6 +1636,9 @@ jobs:
15971636

15981637
- script: |
15991638
# Uninstall ODBC Driver before running tests
1639+
DRIVER_VERSION=$(awk -F= '/^__version__/ {gsub(/[[:space:]"]/, "", $2); print $2; exit}' mssql_python_odbc/__init__.py)
1640+
DRIVER_MAJOR_MINOR=$(echo "$DRIVER_VERSION" | cut -d. -f1,2)
1641+
if [ -z "$DRIVER_MAJOR_MINOR" ]; then echo 'Error: failed to parse mssql_python_odbc.__version__'; exit 1; fi
16001642
docker exec test-container-rhel9-arm64 bash -c "
16011643
dnf remove -y msodbcsql18 mssql-tools18 unixODBC-devel
16021644
rm -f /usr/bin/sqlcmd
@@ -1606,7 +1648,7 @@ jobs:
16061648
odbcinst -u -d -n 'ODBC Driver 18 for SQL Server' || true
16071649
echo 'Uninstalled ODBC Driver and cleaned up libraries'
16081650
echo 'Verifying arm64 rhel driver library signatures:'
1609-
ldd mssql_python/libs/linux/rhel/arm64/lib/libmsodbcsql-18.6.so.2.1
1651+
ldd mssql_python/libs/linux/rhel/arm64/lib/libmsodbcsql-${DRIVER_MAJOR_MINOR}.so.2.1
16101652
"
16111653
displayName: 'Uninstall ODBC Driver before running tests in RHEL 9 ARM64 container'
16121654
@@ -1750,6 +1792,9 @@ jobs:
17501792
17511793
- script: |
17521794
# Install ODBC driver in the Alpine x86_64 container
1795+
DRIVER_VERSION=$(awk -F= '/^__version__/ {gsub(/[[:space:]"]/, "", $2); print $2; exit}' mssql_python_odbc/__init__.py)
1796+
DRIVER_MAJOR_MINOR=$(echo "$DRIVER_VERSION" | cut -d. -f1,2)
1797+
if [ -z "$DRIVER_MAJOR_MINOR" ]; then echo 'Error: failed to parse mssql_python_odbc.__version__'; exit 1; fi
17531798
docker exec test-container-alpine bash -c "
17541799
# Detect architecture for ODBC driver download
17551800
case \$(uname -m) in
@@ -1830,6 +1875,9 @@ jobs:
18301875

18311876
- script: |
18321877
# Uninstall ODBC Driver before running tests to use bundled libraries
1878+
DRIVER_VERSION=$(awk -F= '/^__version__/ {gsub(/[[:space:]"]/, "", $2); print $2; exit}' mssql_python_odbc/__init__.py)
1879+
DRIVER_MAJOR_MINOR=$(echo "$DRIVER_VERSION" | cut -d. -f1,2)
1880+
if [ -z "$DRIVER_MAJOR_MINOR" ]; then echo 'Error: failed to parse mssql_python_odbc.__version__'; exit 1; fi
18331881
docker exec test-container-alpine bash -c "
18341882
# Remove system ODBC installation
18351883
apk del msodbcsql18 mssql-tools18 unixodbc-dev || echo 'ODBC packages not installed via apk'
@@ -1840,7 +1888,7 @@ jobs:
18401888
odbcinst -u -d -n 'ODBC Driver 18 for SQL Server' || true
18411889
echo 'Uninstalled system ODBC Driver and cleaned up libraries'
18421890
echo 'Verifying x86_64 alpine driver library signatures:'
1843-
ldd mssql_python/libs/linux/alpine/x86_64/lib/libmsodbcsql-18.6.so.2.1 || echo 'Driver library not found'
1891+
ldd mssql_python/libs/linux/alpine/x86_64/lib/libmsodbcsql-${DRIVER_MAJOR_MINOR}.so.2.1 || echo 'Driver library not found'
18441892
"
18451893
displayName: 'Uninstall system ODBC Driver before running tests in Alpine x86_64 container'
18461894
@@ -2000,6 +2048,9 @@ jobs:
20002048
20012049
- script: |
20022050
# Install ODBC driver in the Alpine ARM64 container
2051+
DRIVER_VERSION=$(awk -F= '/^__version__/ {gsub(/[[:space:]"]/, "", $2); print $2; exit}' mssql_python_odbc/__init__.py)
2052+
DRIVER_MAJOR_MINOR=$(echo "$DRIVER_VERSION" | cut -d. -f1,2)
2053+
if [ -z "$DRIVER_MAJOR_MINOR" ]; then echo 'Error: failed to parse mssql_python_odbc.__version__'; exit 1; fi
20032054
docker exec test-container-alpine-arm64 bash -c "
20042055
# Detect architecture for ODBC driver download
20052056
case \$(uname -m) in
@@ -2081,6 +2132,9 @@ jobs:
20812132

20822133
- script: |
20832134
# Uninstall ODBC Driver before running tests to use bundled libraries
2135+
DRIVER_VERSION=$(awk -F= '/^__version__/ {gsub(/[[:space:]"]/, "", $2); print $2; exit}' mssql_python_odbc/__init__.py)
2136+
DRIVER_MAJOR_MINOR=$(echo "$DRIVER_VERSION" | cut -d. -f1,2)
2137+
if [ -z "$DRIVER_MAJOR_MINOR" ]; then echo 'Error: failed to parse mssql_python_odbc.__version__'; exit 1; fi
20842138
docker exec test-container-alpine-arm64 bash -c "
20852139
# Remove system ODBC installation
20862140
apk del msodbcsql18 mssql-tools18 unixodbc-dev || echo 'ODBC packages not installed via apk'
@@ -2091,7 +2145,7 @@ jobs:
20912145
odbcinst -u -d -n 'ODBC Driver 18 for SQL Server' || true
20922146
echo 'Uninstalled system ODBC Driver and cleaned up libraries'
20932147
echo 'Verifying arm64 alpine driver library signatures:'
2094-
ldd mssql_python/libs/linux/alpine/arm64/lib/libmsodbcsql-18.6.so.2.1 || echo 'Driver library not found'
2148+
ldd mssql_python/libs/linux/alpine/arm64/lib/libmsodbcsql-${DRIVER_MAJOR_MINOR}.so.2.1 || echo 'Driver library not found'
20952149
"
20962150
displayName: 'Uninstall system ODBC Driver before running tests in Alpine ARM64 container'
20972151

mssql_python/pybind/CMakeLists.txt

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,36 @@ endif()
7373
add_definitions(-DARCHITECTURE="${ARCHITECTURE}")
7474
add_definitions(-DPLATFORM_NAME="${PLATFORM_NAME}")
7575

76+
# --- msodbcsql driver version (single source of truth) -----------------------
77+
# The ODBC driver filename embeds the msodbcsql version (e.g.
78+
# libmsodbcsql-18.6.so.2.1 on Linux, libmsodbcsql.18.dylib on macOS,
79+
# msodbcsql18.dll on Windows). Derive that version from
80+
# mssql_python_odbc.__version__ -- the single source of truth for the driver
81+
# version -- and inject it into GetDriverPathCpp() via -D defines, so the native
82+
# resolver and the Python package can never drift. The literal is read straight
83+
# from the file (not by importing the package) to keep configure hermetic and
84+
# independent of whatever packages the build interpreter happens to have.
85+
set(MSODBCSQL_VERSION_SOURCE "${CMAKE_CURRENT_SOURCE_DIR}/../../mssql_python_odbc/__init__.py")
86+
if(NOT EXISTS "${MSODBCSQL_VERSION_SOURCE}")
87+
message(FATAL_ERROR
88+
"Cannot resolve the msodbcsql driver version: ${MSODBCSQL_VERSION_SOURCE} not found. "
89+
"The native driver filename is derived from mssql_python_odbc.__version__; "
90+
"build from a full repository checkout.")
91+
endif()
92+
file(STRINGS "${MSODBCSQL_VERSION_SOURCE}" _msodbcsql_version_line REGEX "^__version__")
93+
string(REGEX REPLACE ".*['\"]([0-9]+\\.[0-9]+\\.[0-9]+)['\"].*" "\\1"
94+
MSODBCSQL_VERSION "${_msodbcsql_version_line}")
95+
if(NOT MSODBCSQL_VERSION MATCHES "^[0-9]+\\.[0-9]+\\.[0-9]+$")
96+
message(FATAL_ERROR
97+
"Failed to parse __version__ from ${MSODBCSQL_VERSION_SOURCE} "
98+
"(got '${MSODBCSQL_VERSION}').")
99+
endif()
100+
string(REGEX MATCH "^[0-9]+" MSODBCSQL_VERSION_MAJOR "${MSODBCSQL_VERSION}")
101+
string(REGEX MATCH "^[0-9]+\\.[0-9]+" MSODBCSQL_VERSION_MAJOR_MINOR "${MSODBCSQL_VERSION}")
102+
message(STATUS "msodbcsql driver version (from mssql_python_odbc.__version__): ${MSODBCSQL_VERSION} -> major=${MSODBCSQL_VERSION_MAJOR}, major.minor=${MSODBCSQL_VERSION_MAJOR_MINOR}")
103+
add_definitions(-DMSODBCSQL_VERSION_MAJOR="${MSODBCSQL_VERSION_MAJOR}")
104+
add_definitions(-DMSODBCSQL_VERSION_MAJOR_MINOR="${MSODBCSQL_VERSION_MAJOR_MINOR}")
105+
76106
# For macOS, always set universal build
77107
if(APPLE)
78108
set(CMAKE_OSX_ARCHITECTURES "arm64;x86_64" CACHE STRING "Build architectures for macOS" FORCE)

mssql_python/pybind/build.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -181,7 +181,7 @@ fi
181181

182182
# TODO: Linux-specific: use patchelf to set RPATH of the driver .so file
183183
# Currently added Driver SO files right now are already patched
184-
# patchelf --set-rpath '$ORIGIN' libmsodbcsql-18.6.so.2.1
184+
# patchelf --set-rpath '$ORIGIN' libmsodbcsql-<major>.<minor>.so.2.1
185185
# This command sets the RPATH of the specified .so file to the directory containing the file (similar to Windows)
186186
# Needed since libodbcinst.so.2 is located in the same directory and needs to be resolved
187187

mssql_python/pybind/configure_dylibs.sh

Lines changed: 23 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,28 @@
66
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
77
PROJECT_DIR="$(dirname "$SCRIPT_DIR")"
88

9+
ODBC_VERSION_FILE="$PROJECT_DIR/../mssql_python_odbc/__init__.py"
10+
if [ ! -f "$ODBC_VERSION_FILE" ]; then
11+
echo "Error: SSOT version file not found: $ODBC_VERSION_FILE"
12+
exit 1
13+
fi
14+
15+
DRIVER_MAJOR=$(sed -nE "s/^__version__[[:space:]]*=[[:space:]]*['\"]([0-9]+)\.[0-9]+\.[0-9]+['\"].*/\1/p" "$ODBC_VERSION_FILE" | head -1)
16+
if [ -z "$DRIVER_MAJOR" ]; then
17+
echo "Error: failed to parse __version__ from $ODBC_VERSION_FILE"
18+
exit 1
19+
fi
20+
21+
DRIVER_DYLIB_NAME="libmsodbcsql.${DRIVER_MAJOR}.dylib"
22+
923
# The universal2 wheel bundles a per-arch copy of the driver dylibs, so every
1024
# bundled arch must be configured, not just the build host's ($(uname -m)).
1125
# install_name_tool and codesign both work cross-arch. Fixing only the host arch
1226
# is what shipped the broken arm64 driver in issue #656.
1327
for ARCH in arm64 x86_64; do
1428
LIB_DIR="$PROJECT_DIR/libs/macos/$ARCH/lib"
15-
LIBMSODBCSQL_PATH="$LIB_DIR/libmsodbcsql.18.dylib"
29+
LIBMSODBCSQL_PATH="$LIB_DIR/$DRIVER_DYLIB_NAME"
30+
LIBMSODBCSQL_NAME="$DRIVER_DYLIB_NAME"
1631
LIBODBCINST_PATH="$LIB_DIR/libodbcinst.2.dylib"
1732
LIBLTDL_PATH="$LIB_DIR/libltdl.7.dylib"
1833

@@ -23,7 +38,7 @@ if [ ! -d "$LIB_DIR" ]; then
2338
fi
2439

2540
if [ ! -f "$LIBMSODBCSQL_PATH" ]; then
26-
echo "Error: libmsodbcsql.18.dylib not found at: $LIBMSODBCSQL_PATH"
41+
echo "Error: $DRIVER_DYLIB_NAME not found at: $LIBMSODBCSQL_PATH"
2742
exit 1
2843
fi
2944

@@ -42,7 +57,7 @@ fi
4257
echo "Configuring dylibs in: $LIB_DIR"
4358

4459
# Get the existing library paths which are linked to the dylibs
45-
echo "Reading dependencies from libmsodbcsql.18.dylib..."
60+
echo "Reading dependencies from $LIBMSODBCSQL_NAME..."
4661
OTOOL_LIST=$(otool -L "$LIBMSODBCSQL_PATH")
4762
OLD_LIBODBCINST_PATH=""
4863

@@ -68,12 +83,12 @@ done <<< "$OTOOL_LIST"
6883

6984
# Configure the library paths if dependencies were found
7085
if [ -n "$OLD_LIBODBCINST_PATH" ]; then
71-
echo "Fixing libmsodbcsql.18.dylib dependency on libodbcinst.2.dylib..."
86+
echo "Fixing $LIBMSODBCSQL_NAME dependency on libodbcinst.2.dylib..."
7287
echo " Changing: $OLD_LIBODBCINST_PATH"
7388
echo " To: @loader_path/libodbcinst.2.dylib"
7489
install_name_tool -change "$OLD_LIBODBCINST_PATH" "@loader_path/libodbcinst.2.dylib" "$LIBMSODBCSQL_PATH"
7590
else
76-
echo "Warning: libodbcinst dependency not found in libmsodbcsql.18.dylib"
91+
echo "Warning: libodbcinst dependency not found in $LIBMSODBCSQL_NAME"
7792
fi
7893

7994
if [ -n "$OLD_LIBLTDL_PATH" ] && [ -f "$LIBLTDL_PATH" ]; then
@@ -89,8 +104,8 @@ fi
89104

90105
# First set the IDs of the libraries using @loader_path
91106
echo "Setting library IDs with @loader_path..."
92-
echo "Setting ID for libmsodbcsql.18.dylib..."
93-
install_name_tool -id "@loader_path/libmsodbcsql.18.dylib" "$LIBMSODBCSQL_PATH"
107+
echo "Setting ID for $LIBMSODBCSQL_NAME..."
108+
install_name_tool -id "@loader_path/$LIBMSODBCSQL_NAME" "$LIBMSODBCSQL_PATH"
94109

95110
echo "Setting ID for libodbcinst.2.dylib..."
96111
install_name_tool -id "@loader_path/libodbcinst.2.dylib" "$LIBODBCINST_PATH"
@@ -100,7 +115,7 @@ if [ -f "$LIBLTDL_PATH" ]; then
100115
install_name_tool -id "@loader_path/libltdl.7.dylib" "$LIBLTDL_PATH"
101116
fi
102117

103-
echo "Codesigning libmsodbcsql.18.dylib..."
118+
echo "Codesigning $LIBMSODBCSQL_NAME..."
104119
codesign -s - -f "$LIBMSODBCSQL_PATH" 2>/dev/null
105120

106121
echo "Codesigning libodbcinst.2.dylib..."

0 commit comments

Comments
 (0)