@@ -130,20 +130,27 @@ jobs:
130130
131131 cd ..\..
132132 displayName: 'Build PYD for $(targetArch)'
133+ continueOnError: false
133134
135+ # TODO: Reactivate and use pytests in build
134136 # Run pytests before packaging
135- - script : |
136- python -m pytest -v
137- displayName: 'Run pytests'
138- env:
139- DB_CONNECTION_STRING: 'Server=(localdb)\MSSQLLocalDB;Database=TestDB;Uid=testuser;Pwd=$(DB_PASSWORD);TrustServerCertificate=yes'
137+ # - powershell: |
138+ # Write-Host "Running pytests to validate bindings"
139+ # if ("$(targetArch)" -eq "arm64") {
140+ # Write-Host "Skipping pytests on Windows ARM64"
141+ # } else {
142+ # python -m pytest -v
143+ # }
144+ # displayName: 'Run pytests'
145+ # env:
146+ # DB_CONNECTION_STRING: 'Server=(localdb)\MSSQLLocalDB;Database=TestDB;Uid=testuser;Pwd=$(DB_PASSWORD);TrustServerCertificate=yes'
140147
141148 # Copy the built .pyd file to staging folder for artifacts
142149 - task : CopyFiles@2
143150 inputs :
144151 SourceFolder : ' $(Build.SourcesDirectory)\mssql_python\pybind\build\$(targetArch)\py$(shortPyVer)\Release'
145152 Contents : ' ddbc_bindings.cp$(shortPyVer)-*.pyd'
146- TargetFolder : ' $(Build.ArtifactStagingDirectory)\ddbc-bindings'
153+ TargetFolder : ' $(Build.ArtifactStagingDirectory)\ddbc-bindings\windows '
147154 displayName : ' Place PYD file into artifacts directory'
148155
149156 # Copy the built .pdb files to staging folder for artifacts
@@ -263,13 +270,14 @@ jobs:
263270 # Call build.sh to build the .so file
264271 ./build.sh
265272 displayName: 'Build .so file'
273+ continueOnError: false
266274
267275 # Copy the built .so file to staging folder for artifacts
268276 - task : CopyFiles@2
269277 inputs :
270278 SourceFolder : ' $(Build.SourcesDirectory)/mssql_python'
271279 Contents : ' *.so'
272- TargetFolder : ' $(Build.ArtifactStagingDirectory)/ddbc-bindings'
280+ TargetFolder : ' $(Build.ArtifactStagingDirectory)/ddbc-bindings/macOS '
273281 displayName : ' Place .so file into artifacts directory'
274282
275283 - script : |
@@ -310,13 +318,14 @@ jobs:
310318 displayName: 'Pull & start SQL Server (Docker)'
311319 env:
312320 DB_PASSWORD: $(DB_PASSWORD)
313-
321+
322+ # TODO: Reactivate and use pytests in build
314323 # Run Pytest to ensure the bindings work correctly
315- - script : |
316- python -m pytest -v
317- displayName: 'Run Pytest to validate bindings'
318- env:
319- DB_CONNECTION_STRING: 'Driver=ODBC Driver 18 for SQL Server;Server=localhost;Database=master;Uid=SA;Pwd=$(DB_PASSWORD);TrustServerCertificate=yes'
324+ # - script: |
325+ # python -m pytest -v
326+ # displayName: 'Run Pytest to validate bindings'
327+ # env:
328+ # DB_CONNECTION_STRING: 'Driver=ODBC Driver 18 for SQL Server;Server=localhost;Database=master;Uid=SA;Pwd=$(DB_PASSWORD);TrustServerCertificate=yes'
320329
321330 # Build wheel package for universal2
322331 - script : |
@@ -350,3 +359,278 @@ jobs:
350359 ArtifactName : ' mssql-python-wheels-dist'
351360 publishLocation : ' Container'
352361 displayName : ' Publish all wheels as artifacts'
362+
363+ - job : BuildLinuxWheels
364+ # Use the latest Ubuntu image for building
365+ pool :
366+ vmImage : ' ubuntu-latest'
367+ displayName : ' Build Linux -'
368+ # Strategy matrix to build all combinations
369+ strategy :
370+ matrix :
371+ # Python 3.10 (x86_64 and ARM64)
372+ py310_x86_64 :
373+ shortPyVer : ' 310'
374+ targetArch : ' x86_64'
375+ dockerPlatform : ' linux/amd64'
376+ manylinuxTag : ' manylinux2014_x86_64'
377+ py310_arm64 :
378+ shortPyVer : ' 310'
379+ targetArch : ' aarch64'
380+ dockerPlatform : ' linux/arm64'
381+ manylinuxTag : ' manylinux2014_aarch64'
382+
383+ # Python 3.11 (x86_64 and ARM64)
384+ py311_x86_64 :
385+ shortPyVer : ' 311'
386+ targetArch : ' x86_64'
387+ dockerPlatform : ' linux/amd64'
388+ manylinuxTag : ' manylinux2014_x86_64'
389+ py311_arm64 :
390+ shortPyVer : ' 311'
391+ targetArch : ' aarch64'
392+ dockerPlatform : ' linux/arm64'
393+ manylinuxTag : ' manylinux2014_aarch64'
394+
395+ # Python 3.12 (x86_64 and ARM64)
396+ py312_x86_64 :
397+ shortPyVer : ' 312'
398+ targetArch : ' x86_64'
399+ dockerPlatform : ' linux/amd64'
400+ manylinuxTag : ' manylinux2014_x86_64'
401+ py312_arm64 :
402+ shortPyVer : ' 312'
403+ targetArch : ' aarch64'
404+ dockerPlatform : ' linux/arm64'
405+ manylinuxTag : ' manylinux2014_aarch64'
406+
407+ # Python 3.13 (x86_64 and ARM64)
408+ py313_x86_64 :
409+ shortPyVer : ' 313'
410+ targetArch : ' x86_64'
411+ dockerPlatform : ' linux/amd64'
412+ manylinuxTag : ' manylinux2014_x86_64'
413+ py313_arm64 :
414+ shortPyVer : ' 313'
415+ targetArch : ' aarch64'
416+ dockerPlatform : ' linux/arm64'
417+ manylinuxTag : ' manylinux2014_aarch64'
418+
419+ steps :
420+ # Set up Docker buildx for multi-architecture support
421+ - script : |
422+ docker run --rm --privileged multiarch/qemu-user-static --reset -p yes
423+ docker buildx create --name multiarch --driver docker-container --use || true
424+ docker buildx inspect --bootstrap
425+ displayName: 'Setup Docker buildx for multi-architecture support'
426+
427+ # Create manylinux2014 container for building
428+ - script : |
429+ docker run -d --name build-container-$(targetArch) \
430+ --platform $(dockerPlatform) \
431+ -v $(Build.SourcesDirectory):/workspace \
432+ -w /workspace \
433+ --network bridge \
434+ quay.io/pypa/manylinux2014_$(targetArch):latest \
435+ tail -f /dev/null
436+ displayName: 'Create manylinux2014 $(targetArch) container'
437+
438+ # Start SQL Server container for testing
439+ - script : |
440+ docker run -d --name sqlserver-$(targetArch) \
441+ --platform linux/amd64 \
442+ -e ACCEPT_EULA=Y \
443+ -e MSSQL_SA_PASSWORD="$(DB_PASSWORD)" \
444+ -p 1433:1433 \
445+ mcr.microsoft.com/mssql/server:2022-latest
446+
447+ # Wait for SQL Server to be ready
448+ echo "Waiting for SQL Server to start..."
449+ for i in {1..60}; do
450+ if docker exec sqlserver-$(targetArch) \
451+ /opt/mssql-tools18/bin/sqlcmd \
452+ -S localhost \
453+ -U SA \
454+ -P "$(DB_PASSWORD)" \
455+ -C -Q "SELECT 1" >/dev/null 2>&1; then
456+ echo "SQL Server is ready!"
457+ break
458+ fi
459+ echo "Waiting... ($i/60)"
460+ sleep 2
461+ done
462+
463+ # Create test database
464+ docker exec sqlserver-$(targetArch) \
465+ /opt/mssql-tools18/bin/sqlcmd \
466+ -S localhost \
467+ -U SA \
468+ -P "$(DB_PASSWORD)" \
469+ -C -Q "CREATE DATABASE TestDB"
470+ displayName: 'Start SQL Server container for $(targetArch)'
471+ env:
472+ DB_PASSWORD: $(DB_PASSWORD)
473+
474+ # Install dependencies and set up Python in manylinux container
475+ - script : |
476+ docker exec build-container-$(targetArch) bash -c "
477+ # List available Python versions in manylinux container
478+ echo 'Available Python versions in manylinux container:'
479+ ls -la /opt/python/
480+
481+ # Find the correct Python binary for cp$(shortPyVer)
482+ PYTHON_TAG=cp$(shortPyVer)
483+ PYTHON_BIN=''
484+
485+ # Try different naming patterns
486+ for pattern in \${PYTHON_TAG}-\${PYTHON_TAG} \${PYTHON_TAG}-\${PYTHON_TAG}m; do
487+ if [ -x /opt/python/\${pattern}/bin/python ]; then
488+ PYTHON_BIN=/opt/python/\${pattern}/bin/python
489+ PIP_BIN=/opt/python/\${pattern}/bin/pip
490+ break
491+ fi
492+ done
493+
494+ if [ -z \"\$PYTHON_BIN\" ]; then
495+ echo 'Python $(shortPyVer) not found in manylinux container'
496+ echo 'Available Python versions:'
497+ ls -la /opt/python/
498+ exit 1
499+ fi
500+
501+ echo \"Using Python binary: \$PYTHON_BIN\"
502+ \$PYTHON_BIN --version
503+
504+ # Create symlinks for easier access
505+ ln -sf \$PYTHON_BIN /usr/local/bin/python
506+ ln -sf \$PIP_BIN /usr/local/bin/pip
507+
508+ # Install build dependencies
509+ python -m pip install --upgrade pip
510+ python -m pip install -r requirements.txt
511+ python -m pip install cmake pybind11 wheel setuptools
512+
513+ # Verify installation
514+ python --version
515+ pip --version
516+ uname -m
517+ echo 'Python path:' \$(which python)
518+ echo 'Pip path:' \$(which pip)
519+ "
520+ displayName: 'Install dependencies in manylinux2014 $(targetArch) container'
521+
522+ # Install ODBC Driver to build
523+ - script : |
524+ docker exec build-container-$(targetArch) bash -c "
525+ yum update -y
526+ yum install -y unixODBC-devel
527+ "
528+ displayName: 'Install ODBC Driver in manylinux2014 $(targetArch) container'
529+
530+ # Build pybind bindings in manylinux container
531+ - script : |
532+ docker exec build-container-$(targetArch) bash -c "
533+ cd mssql_python/pybind
534+ chmod +x build.sh
535+ ./build.sh
536+
537+ # Verify .so file was built
538+ ls -la ../ddbc_bindings.*.so
539+ "
540+ displayName: 'Build pybind bindings (.so) in manylinux2014 $(targetArch) container'
541+ continueOnError: false
542+
543+ # TODO: Reactivate and use pytests in build
544+ # # Run tests to validate the build
545+ # - script: |
546+ # # Get SQL Server container IP
547+ # SQLSERVER_IP=$(docker inspect sqlserver-$(targetArch) --format='{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}')
548+ # echo "SQL Server IP: $SQLSERVER_IP"
549+ #
550+ # docker exec \
551+ # -e DB_CONNECTION_STRING="Driver=ODBC Driver 18 for SQL Server;Server=$SQLSERVER_IP;Database=TestDB;Uid=SA;Pwd=$(DB_PASSWORD);TrustServerCertificate=yes" \
552+ # -e DB_PASSWORD="$(DB_PASSWORD)" \
553+ # build-container-$(targetArch) bash -c "
554+ # echo 'Running tests on manylinux2014 $(targetArch)'
555+ # echo 'Architecture:' \$(uname -m)
556+ # echo 'Python version:' \$(python --version)
557+ # python -m pytest -v
558+ # "
559+ # displayName: 'Run tests in manylinux2014 $(targetArch) container'
560+ # env:
561+ # DB_PASSWORD: $(DB_PASSWORD)
562+
563+ # Build manylinux wheel
564+ - script : |
565+ docker exec build-container-$(targetArch) bash -c "
566+ echo 'Building manylinux2014 wheel for Python cp$(shortPyVer) on $(targetArch)'
567+ echo 'Python version:' \$(python --version)
568+ python -m pip install --upgrade pip wheel setuptools
569+
570+ # Build the wheel
571+ python setup.py bdist_wheel
572+
573+ # Verify the wheel was created
574+ ls -la dist/
575+
576+ # Use auditwheel to create manylinux wheel
577+ python -m pip install auditwheel
578+ auditwheel repair dist/*.whl --plat-tag $(manylinuxTag) -w dist/
579+
580+ # Remove the original wheel, keep only the manylinux one
581+ rm -f dist/*-linux_*.whl
582+
583+ # Verify final wheel
584+ echo 'Final manylinux wheel:'
585+ ls -la dist/
586+ "
587+ displayName: 'Build manylinux2014 wheel for Python cp$(shortPyVer) ($(targetArch))'
588+
589+ # Copy the wheel file to the artifacts
590+ - script : |
591+ # Debug: Check what's in the dist directory
592+ docker exec build-container-$(targetArch) bash -c "
593+ echo 'Contents of /workspace/dist after wheel build:'
594+ ls -la /workspace/dist/ || echo 'dist directory does not exist'
595+ "
596+
597+ mkdir -p $(Build.ArtifactStagingDirectory)/dist
598+ docker cp build-container-$(targetArch):/workspace/dist/. $(Build.ArtifactStagingDirectory)/dist/ || echo "Failed to copy dist directory"
599+
600+ # Verify what was copied
601+ echo "Contents of staging dist directory:"
602+ ls -la $(Build.ArtifactStagingDirectory)/dist/ || echo "Staging dist directory is empty"
603+ displayName: 'Copy wheel from container to artifacts'
604+
605+ # Copy the built .so file to staging folder for artifacts
606+ - script : |
607+ mkdir -p $(Build.ArtifactStagingDirectory)/ddbc-bindings/linux/$(targetArch)
608+ docker cp build-container-$(targetArch):/workspace/mssql_python/ddbc_bindings.cp$(shortPyVer)-$(targetArch).so $(Build.ArtifactStagingDirectory)/ddbc-bindings/linux/$(targetArch)/
609+ displayName: 'Copy .so file to artifacts directory'
610+
611+ # Clean up containers
612+ - script : |
613+ docker stop build-container-$(targetArch) || true
614+ docker rm build-container-$(targetArch) || true
615+ docker stop sqlserver-$(targetArch) || true
616+ docker rm sqlserver-$(targetArch) || true
617+ displayName: 'Clean up $(targetArch) containers'
618+ condition: always()
619+
620+ # Publish the collected .so file(s) as build artifacts
621+ - task : PublishBuildArtifacts@1
622+ condition : succeededOrFailed()
623+ inputs :
624+ PathtoPublish : ' $(Build.ArtifactStagingDirectory)/ddbc-bindings'
625+ ArtifactName : ' mssql-python-ddbc-bindings'
626+ publishLocation : ' Container'
627+ displayName : ' Publish .so files as artifacts'
628+
629+ # Publish the collected wheel file(s) as build artifacts
630+ - task : PublishBuildArtifacts@1
631+ condition : succeededOrFailed()
632+ inputs :
633+ PathtoPublish : ' $(Build.ArtifactStagingDirectory)/dist'
634+ ArtifactName : ' mssql-python-wheels-dist'
635+ publishLocation : ' Container'
636+ displayName : ' Publish manylinux wheels as artifacts'
0 commit comments