Skip to content

Commit 7966b90

Browse files
committed
Modernize build system with pyproject.toml and custom build backend
- Add build_ddbc package for native extension compilation - python -m build_ddbc: standalone compilation CLI - Custom PEP 517 backend: auto-compiles on python -m build - Consolidate project config into pyproject.toml - Moved pytest config from pytest.ini - Added optional dependencies [dev], [lint], [all] - Added coverage, mypy, black, autopep8, pylint configs - Simplified setup.py to platform-specific wheel logic only - Delete pytest.ini (moved to pyproject.toml)
1 parent dc0e162 commit 7966b90

7 files changed

Lines changed: 618 additions & 119 deletions

File tree

build_ddbc/__init__.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
"""
2+
build_ddbc - Build system for mssql-python native extensions.
3+
4+
This package provides:
5+
1. A CLI tool: `python -m build_ddbc`
6+
2. A PEP 517 build backend that auto-compiles ddbc_bindings
7+
8+
Usage:
9+
python -m build_ddbc # Compile ddbc_bindings only
10+
python -m build_ddbc --arch arm64 # Specify architecture (Windows)
11+
python -m build_ddbc --coverage # Enable coverage (Linux)
12+
python -m build # Compile + create wheel (automatic)
13+
"""
14+
15+
from .compiler import compile_ddbc, get_platform_info
16+
17+
__all__ = ["compile_ddbc", "get_platform_info"]
18+
__version__ = "1.0.0"

build_ddbc/__main__.py

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
"""
2+
CLI entry point for build_ddbc.
3+
4+
Usage:
5+
python -m build_ddbc # Compile ddbc_bindings
6+
python -m build_ddbc --arch arm64 # Specify architecture (Windows)
7+
python -m build_ddbc --coverage # Enable coverage (Linux)
8+
python -m build_ddbc --help # Show help
9+
"""
10+
11+
import argparse
12+
import sys
13+
14+
from .compiler import compile_ddbc, get_platform_info
15+
16+
17+
def main() -> int:
18+
"""Main entry point for the CLI."""
19+
parser = argparse.ArgumentParser(
20+
prog="python -m build_ddbc",
21+
description="Compile ddbc_bindings native extension for mssql-python",
22+
formatter_class=argparse.RawDescriptionHelpFormatter,
23+
epilog="""
24+
Examples:
25+
python -m build_ddbc # Build for current platform
26+
python -m build_ddbc --arch arm64 # Build for ARM64 (Windows)
27+
python -m build_ddbc --coverage # Build with coverage (Linux)
28+
python -m build_ddbc --quiet # Build without output
29+
""",
30+
)
31+
32+
parser.add_argument(
33+
"--arch", "-a",
34+
choices=["x64", "x86", "arm64", "x86_64", "aarch64", "universal2"],
35+
help="Target architecture (Windows: x64, x86, arm64)",
36+
)
37+
38+
parser.add_argument(
39+
"--coverage", "-c",
40+
action="store_true",
41+
help="Enable coverage instrumentation (Linux only)",
42+
)
43+
44+
parser.add_argument(
45+
"--quiet", "-q",
46+
action="store_true",
47+
help="Suppress build output",
48+
)
49+
50+
parser.add_argument(
51+
"--version", "-V",
52+
action="version",
53+
version="%(prog)s 1.0.0",
54+
)
55+
56+
args = parser.parse_args()
57+
58+
# Show platform info
59+
if not args.quiet:
60+
arch, platform_tag = get_platform_info()
61+
print(f"[build_ddbc] Platform: {sys.platform}")
62+
print(f"[build_ddbc] Architecture: {arch}")
63+
print(f"[build_ddbc] Platform tag: {platform_tag}")
64+
print()
65+
66+
try:
67+
compile_ddbc(
68+
arch=args.arch,
69+
coverage=args.coverage,
70+
verbose=not args.quiet,
71+
)
72+
return 0
73+
except FileNotFoundError as e:
74+
print(f"Error: {e}", file=sys.stderr)
75+
return 1
76+
except RuntimeError as e:
77+
print(f"Build failed: {e}", file=sys.stderr)
78+
return 1
79+
80+
81+
if __name__ == "__main__":
82+
sys.exit(main())

build_ddbc/build_backend.py

Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
"""
2+
PEP 517 Build Backend for mssql-python.
3+
4+
This module wraps setuptools' build backend and adds automatic
5+
ddbc_bindings compilation before building wheels.
6+
7+
Usage in pyproject.toml:
8+
[build-system]
9+
requires = ["setuptools>=61.0", "wheel", "pybind11"]
10+
build-backend = "build_ddbc.build_backend"
11+
backend-path = ["."]
12+
"""
13+
14+
import sys
15+
from pathlib import Path
16+
17+
# Import setuptools build backend - we'll wrap its functions
18+
from setuptools.build_meta import (
19+
build_wheel as _setuptools_build_wheel,
20+
build_sdist as _setuptools_build_sdist,
21+
get_requires_for_build_wheel as _get_requires_for_build_wheel,
22+
get_requires_for_build_sdist as _get_requires_for_build_sdist,
23+
prepare_metadata_for_build_wheel as _prepare_metadata_for_build_wheel,
24+
)
25+
26+
from .compiler import compile_ddbc
27+
28+
29+
# =============================================================================
30+
# PEP 517 Required Hooks
31+
# =============================================================================
32+
33+
def get_requires_for_build_wheel(config_settings=None):
34+
"""Return build requirements for wheel."""
35+
return _get_requires_for_build_wheel(config_settings)
36+
37+
38+
def get_requires_for_build_sdist(config_settings=None):
39+
"""Return build requirements for sdist."""
40+
return _get_requires_for_build_sdist(config_settings)
41+
42+
43+
def prepare_metadata_for_build_wheel(metadata_directory, config_settings=None):
44+
"""Prepare wheel metadata."""
45+
return _prepare_metadata_for_build_wheel(metadata_directory, config_settings)
46+
47+
48+
def build_wheel(wheel_directory, config_settings=None, metadata_directory=None):
49+
"""
50+
Build a wheel, compiling ddbc_bindings first.
51+
52+
This is the main hook - it compiles the native extension before
53+
delegating to setuptools to create the wheel.
54+
"""
55+
print("[build_backend] Starting wheel build...")
56+
57+
# Check if we should skip compilation (e.g., for sdist-only builds)
58+
skip_compile = False
59+
if config_settings:
60+
skip_compile = config_settings.get("--skip-ddbc-compile", False)
61+
62+
if not skip_compile:
63+
# Extract build options from config_settings
64+
arch = None
65+
coverage = False
66+
67+
if config_settings:
68+
arch = config_settings.get("--arch")
69+
coverage = config_settings.get("--coverage", False)
70+
71+
print("[build_backend] Compiling ddbc_bindings...")
72+
try:
73+
compile_ddbc(arch=arch, coverage=coverage, verbose=True)
74+
print("[build_backend] Compilation successful!")
75+
except FileNotFoundError:
76+
# If build scripts don't exist, assume pre-compiled binaries
77+
print("[build_backend] Build scripts not found, assuming pre-compiled binaries")
78+
except RuntimeError as e:
79+
print(f"[build_backend] Compilation failed: {e}")
80+
raise
81+
else:
82+
print("[build_backend] Skipping ddbc compilation (--skip-ddbc-compile)")
83+
84+
# Now build the wheel using setuptools
85+
print("[build_backend] Creating wheel...")
86+
return _setuptools_build_wheel(wheel_directory, config_settings, metadata_directory)
87+
88+
89+
def build_sdist(sdist_directory, config_settings=None):
90+
"""
91+
Build a source distribution.
92+
93+
For sdist, we don't compile - just package the source including build scripts.
94+
"""
95+
print("[build_backend] Building source distribution...")
96+
return _setuptools_build_sdist(sdist_directory, config_settings)
97+
98+
99+
# =============================================================================
100+
# Optional PEP 660 Hooks (Editable Installs)
101+
# =============================================================================
102+
103+
def get_requires_for_build_editable(config_settings=None):
104+
"""Return build requirements for editable install."""
105+
return get_requires_for_build_wheel(config_settings)
106+
107+
108+
def build_editable(wheel_directory, config_settings=None, metadata_directory=None):
109+
"""
110+
Build an editable wheel, compiling ddbc_bindings first.
111+
112+
This enables `pip install -e .` to automatically compile.
113+
"""
114+
print("[build_backend] Starting editable install...")
115+
116+
# Compile ddbc_bindings for editable installs too
117+
print("[build_backend] Compiling ddbc_bindings for editable install...")
118+
try:
119+
compile_ddbc(verbose=True)
120+
print("[build_backend] Compilation successful!")
121+
except FileNotFoundError:
122+
print("[build_backend] Build scripts not found, assuming pre-compiled binaries")
123+
except RuntimeError as e:
124+
print(f"[build_backend] Compilation failed: {e}")
125+
raise
126+
127+
# Import here to avoid issues if not available
128+
from setuptools.build_meta import build_editable as _setuptools_build_editable
129+
return _setuptools_build_editable(wheel_directory, config_settings, metadata_directory)

0 commit comments

Comments
 (0)