Skip to content

Commit 4b518c9

Browse files
authored
FEAT: Pipeline & Setup to Build WHL Files and Python Backward Compatibility (#46)
* Added Build steps and Backward compatibility * checked with main * restored other files * restored another file * changes for ddbc_bindings * restoring requirements * tests updated with new build process * fixed pyd path for artifact * added auth DLLs and upgraded to latest driver DLLs 18.5 * test pipelines to store python arm64 libs as artifacts * fixed arch * debugging arch related things * win arm whl fix * setuptools dependency * import bdist_wheel from wheel * version change for release * fix loading path for mssql-auth DLL * store python_libs for arm64 as artifacts and remove unsupported architectures * get python arm64 libs from previous successful run * remove all python arm64 libs * fix pipeline format to download from latest branch run * remove artifacts etc. from test pipeline * clean out pyds directory - minimal now * updated README * clean up a bunch of print statements * cleanup * cleanup for other archs
1 parent e03449e commit 4b518c9

19 files changed

+766
-170
lines changed

.gitignore

Lines changed: 22 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,8 @@ mssql_python/pybind/build/
33

44
mssql_python/pybind/pymsbuild/build/
55

6-
# Ignore all pyd files
7-
**.pyd
6+
# Ignore pyd file
7+
mssql_python/ddbc_bindings.pyd
88

99
# Ignore pycache files and folders
1010
__pycache__/
@@ -26,11 +26,25 @@ mssql_python/.vs
2626
test-*.xml
2727
**/test-**.xml
2828

29-
# Ignore coverage files
30-
coverage.xml
31-
.coverage
32-
.coverage.*
33-
3429
# Ignore the build & mssql_python.egg-info directories
3530
build/
36-
mssql_python.egg-info/
31+
mssql_python.egg-info/
32+
33+
# Python bytecode
34+
__pycache__/
35+
*.py[cod]
36+
*$py.class
37+
38+
# Distribution / packaging
39+
dist/
40+
build/
41+
*.egg-info/
42+
43+
# C extensions
44+
*.so
45+
*.pyd
46+
47+
# IDE files
48+
.vscode/
49+
.idea/
50+
*.swp
Lines changed: 164 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,164 @@
1+
# Pipeline name shown in ADO UI
2+
name: build-whl-pipeline
3+
4+
# Trigger the pipeline on pushes to these branches
5+
trigger:
6+
branches:
7+
include:
8+
- main
9+
- dev
10+
11+
# Use Microsoft-hosted Windows VM
12+
pool:
13+
vmImage: 'windows-latest'
14+
15+
jobs:
16+
- job: BuildPYDs
17+
displayName: 'Build -'
18+
# Strategy matrix to build all combinations
19+
strategy:
20+
matrix:
21+
# Python 3.10 (only x64)
22+
py310_x64:
23+
pythonVersion: '3.10' # Host Python version
24+
shortPyVer: '310' # Used in filenames like cp310
25+
architecture: 'x64' # Host Python architecture
26+
targetArch: 'x64' # Target architecture to pass to build.bat
27+
28+
# Python 3.11
29+
py311_x64:
30+
pythonVersion: '3.11' # Host Python version
31+
shortPyVer: '311' # Used in filenames like cp311
32+
architecture: 'x64' # Host Python architecture
33+
targetArch: 'x64' # Target architecture to pass to build.bat
34+
py311_arm64:
35+
pythonVersion: '3.11'
36+
shortPyVer: '311'
37+
architecture: 'x64' # No arm64 Python, use x64 host
38+
targetArch: 'arm64'
39+
40+
# Python 3.12
41+
py312_x64:
42+
pythonVersion: '3.12'
43+
shortPyVer: '312'
44+
architecture: 'x64'
45+
targetArch: 'x64'
46+
py312_arm64:
47+
pythonVersion: '3.12'
48+
shortPyVer: '312'
49+
architecture: 'x64'
50+
targetArch: 'arm64'
51+
52+
# Python 3.13
53+
py313_x64:
54+
pythonVersion: '3.13'
55+
shortPyVer: '313'
56+
architecture: 'x64'
57+
targetArch: 'x64'
58+
py313_arm64:
59+
pythonVersion: '3.13'
60+
shortPyVer: '313'
61+
architecture: 'x64'
62+
targetArch: 'arm64'
63+
64+
steps:
65+
# Use correct Python version and architecture for the current job
66+
- task: UsePythonVersion@0
67+
inputs:
68+
versionSpec: '$(pythonVersion)'
69+
architecture: '$(architecture)'
70+
addToPath: true
71+
displayName: 'Use Python $(pythonVersion) ($(architecture))'
72+
73+
# Install required packages: pip, CMake, pybind11
74+
- script: |
75+
python -m pip install --upgrade pip
76+
pip install -r requirements.txt
77+
pip install cmake pybind11
78+
displayName: 'Install dependencies'
79+
80+
- task: DownloadPipelineArtifact@2
81+
condition: eq(variables['targetArch'], 'arm64')
82+
inputs:
83+
buildType: 'specific'
84+
project: '$(System.TeamProject)'
85+
definition: 2134
86+
buildVersionToDownload: 'latestFromBranch'
87+
branchName: 'refs/heads/bewithgaurav/build_whl_pipeline' # Or 'refs/heads/dev'
88+
artifactName: 'mssql-python-arm64-libs'
89+
targetPath: '$(Build.SourcesDirectory)\mssql_python\pybind\python_libs\arm64'
90+
displayName: 'Download ARM64 Python libs from latest successful run on branches'
91+
92+
# Build the PYD file by calling build.bat
93+
- script: |
94+
echo "Python Version: $(pythonVersion)"
95+
echo "Short Tag: $(shortPyVer)"
96+
echo "Architecture: Host=$(architecture), Target=$(targetArch)"
97+
98+
cd "$(Build.SourcesDirectory)\mssql_python\pybind"
99+
100+
REM Optional: override lib path if building for ARM64 since we cannot install arm64 python on x64 host
101+
if "$(targetArch)"=="arm64" (
102+
echo Using arm64-specific Python library...
103+
set CUSTOM_PYTHON_LIB_DIR=$(Build.SourcesDirectory)\mssql_python\pybind\python_libs\arm64
104+
)
105+
106+
REM Call build.bat to build the PYD file
107+
call build.bat $(targetArch)
108+
109+
REM Calling keep_single_arch.bat to remove ODBC libs of other architectures
110+
call keep_single_arch.bat $(targetArch)
111+
112+
cd ..\..
113+
displayName: 'Build PYD for $(targetArch)'
114+
115+
# Copy the built .pyd file to staging folder for artifacts
116+
- task: CopyFiles@2
117+
inputs:
118+
SourceFolder: '$(Build.SourcesDirectory)\mssql_python\pybind\build\$(targetArch)\py$(shortPyVer)\Release'
119+
Contents: 'ddbc_bindings.cp$(shortPyVer)-*.pyd'
120+
TargetFolder: '$(Build.ArtifactStagingDirectory)\all-pyds'
121+
displayName: 'Place PYD file into artifacts directory'
122+
123+
# Build wheel package for the current architecture
124+
- script: |
125+
python -m pip install --upgrade pip
126+
pip install wheel setuptools
127+
set ARCHITECTURE=$(targetArch)
128+
python setup.py bdist_wheel
129+
displayName: 'Build wheel package for Python $(pythonVersion) ($(targetArch))'
130+
131+
# Copy the wheel file to the artifacts
132+
- task: CopyFiles@2
133+
inputs:
134+
SourceFolder: '$(Build.SourcesDirectory)\dist'
135+
Contents: '*.whl'
136+
TargetFolder: '$(Build.ArtifactStagingDirectory)\dist'
137+
displayName: 'Collect wheel package'
138+
139+
# Publish the collected .pyd file(s) as build artifacts
140+
- task: PublishBuildArtifacts@1
141+
condition: succeededOrFailed()
142+
inputs:
143+
PathtoPublish: '$(Build.ArtifactStagingDirectory)\all-pyds'
144+
ArtifactName: 'mssql-python-pyds'
145+
publishLocation: 'Container'
146+
displayName: 'Publish all PYDs as artifacts'
147+
148+
# Publish the python arm64 libraries as build artifacts for next builds if ARM64
149+
- task: PublishBuildArtifacts@1
150+
condition: eq(variables['targetArch'], 'arm64')
151+
inputs:
152+
PathtoPublish: '$(Build.SourcesDirectory)\mssql_python\pybind\python_libs\arm64'
153+
ArtifactName: 'mssql-python-arm64-libs'
154+
publishLocation: 'Container'
155+
displayName: 'Publish arm64 libs as artifacts'
156+
157+
# Publish the collected wheel file(s) as build artifacts
158+
- task: PublishBuildArtifacts@1
159+
condition: succeededOrFailed()
160+
inputs:
161+
PathtoPublish: '$(Build.ArtifactStagingDirectory)\dist'
162+
ArtifactName: 'mssql-python-wheels-dist'
163+
publishLocation: 'Container'
164+
displayName: 'Publish all wheels as artifacts'

eng/pipelines/test-pipeline.yml

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -36,11 +36,7 @@ jobs:
3636
3737
- script: |
3838
cd mssql_python\pybind
39-
mkdir build
40-
cd build
41-
cmake -DPython3_EXECUTABLE="python3" -DCMAKE_BUILD_TYPE=Debug ..
42-
cmake --build . --config Debug
43-
copy Debug\ddbc_bindings.pyd ..\..\ddbc_bindings.pyd
39+
build.bat x64
4440
displayName: 'Build .pyd file'
4541
4642
- script: |
@@ -51,7 +47,7 @@ jobs:
5147
5248
- task: PublishBuildArtifacts@1
5349
inputs:
54-
PathtoPublish: 'mssql_python/ddbc_bindings.pyd'
50+
PathtoPublish: 'mssql_python/ddbc_bindings.cp313-amd64.pyd'
5551
ArtifactName: 'ddbc_bindings'
5652
publishLocation: 'Container'
5753
displayName: 'Publish pyd file as artifact'

mssql_python/connection.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,6 @@ def _construct_connection_string(self, connection_str: str = "", **kwargs) -> st
9595
else:
9696
continue
9797
conn_str += f"{key}={value};"
98-
print(f"Connection string after adding driver: {conn_str}")
9998

10099
if ENABLE_LOGGING:
101100
logger.info("Final connection string: %s", conn_str)

mssql_python/ddbc_bindings.py

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
import os
2+
import importlib.util
3+
import sys
4+
import platform
5+
6+
# Get current Python version and architecture
7+
python_version = f"cp{sys.version_info.major}{sys.version_info.minor}"
8+
if platform.machine().lower() in ('amd64', 'x86_64', 'x64'):
9+
architecture = "amd64"
10+
elif platform.machine().lower() in ('arm64', 'aarch64'):
11+
architecture = "arm64"
12+
else:
13+
architecture = platform.machine().lower()
14+
15+
# Find the specifically matching PYD file
16+
module_dir = os.path.dirname(__file__)
17+
expected_pyd = f"ddbc_bindings.{python_version}-{architecture}.pyd"
18+
pyd_path = os.path.join(module_dir, expected_pyd)
19+
20+
if not os.path.exists(pyd_path):
21+
# Fallback to searching for any matching PYD if the specific one isn't found
22+
pyd_files = [f for f in os.listdir(module_dir) if f.startswith('ddbc_bindings.') and f.endswith('.pyd')]
23+
if not pyd_files:
24+
raise ImportError(f"No ddbc_bindings PYD module found for {python_version}-{architecture}")
25+
pyd_path = os.path.join(module_dir, pyd_files[0])
26+
print(f"Warning: Using fallback PYD file {pyd_files[0]} instead of {expected_pyd}")
27+
28+
# Use the original module name 'ddbc_bindings' that the C extension was compiled with
29+
name = "ddbc_bindings"
30+
spec = importlib.util.spec_from_file_location(name, pyd_path)
31+
module = importlib.util.module_from_spec(spec)
32+
sys.modules[name] = module
33+
spec.loader.exec_module(module)
34+
35+
# Copy all attributes from the loaded module to this module
36+
for attr in dir(module):
37+
if not attr.startswith('__'):
38+
globals()[attr] = getattr(module, attr)

mssql_python/helpers.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ def add_driver_to_connection_str(connection_str):
4646
# Insert the driver attribute at the beginning of the connection string
4747
final_connection_attributes.insert(0, driver_name)
4848
connection_str = ";".join(final_connection_attributes)
49-
print(f"Connection string after adding driver: {connection_str}")
49+
5050
except Exception as e:
5151
raise Exception(
5252
"Invalid connection string, Please follow the format: "
File renamed without changes.

0 commit comments

Comments
 (0)