Address corner case when generating version file (#641)

Address corner case for version file generation

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: github-actions <github-actions@github.com>
This commit is contained in:
Binyang Li
2025-10-07 14:32:33 -07:00
committed by GitHub
parent 3d94383696
commit ddca185add
6 changed files with 22 additions and 201 deletions

View File

@@ -1 +1 @@
0.7.0
0.8.0

View File

@@ -1,135 +0,0 @@
# Copyright (c) Microsoft Corporation.
# Licensed under the MIT license.
"""Custom build backend wrapper to ensure version generation."""
import os
import sys
import re
import logging
from pathlib import Path
# Import the original backend
from scikit_build_core.build import * # noqa: F401, F403
from scikit_build_core.build import build_wheel as _orig_build_wheel
from scikit_build_core.build import build_sdist as _orig_build_sdist
from scikit_build_core.build import build_editable as _orig_build_editable
from scikit_build_core.build import get_requires_for_build_wheel as _orig_get_requires_for_build_wheel
from scikit_build_core.build import prepare_metadata_for_build_wheel as _orig_prepare_metadata_for_build_wheel
logging.basicConfig(level=logging.INFO)
def _get_version():
"""Get version using setuptools-scm, VERSION and clean it up"""
try:
with open("VERSION", "r") as vf:
base_version = vf.read().strip()
except FileNotFoundError:
base_version = "0.0.0"
try:
import setuptools_scm
version = setuptools_scm.get_version(root=".")
# Remove the .dYYYYMMDD timestamp if present
# Convert "0.7.1.dev36+g6e2360d69.d20250926" to "0.7.1.dev36+g6e2360d69"
version = re.sub(r"\.d\d{8}", "", version)
# Use the value in VERSION as the base version
# Change to "0.7.0.dev36+g6e2360d69"
version = re.sub(r"^[0-9]+\.[0-9]+\.[0-9]+", base_version, version)
logging.info(f"Generated version with setuptools-scm: {version}")
return version
except Exception as e:
logging.warning(f"setuptools-scm failed: {e}, using fallback")
return base_version + "+unknown"
def _generate_version_file():
"""Generate _version.py file using setuptools-scm"""
version = _get_version()
# Write version file
version_file = Path("python/mscclpp/_version.py")
version_file.parent.mkdir(parents=True, exist_ok=True)
with open(version_file, "w") as f:
f.write(f"# Generated by build backend\n")
f.write(f'__version__ = "{version}"\n')
f.write(f'version = "{version}"\n')
logging.info(f"Wrote version {version} to {version_file}")
# Also write a metadata file that scikit-build-core can read
metadata_file = Path("python/mscclpp/PKG-INFO")
metadata_file.parent.mkdir(parents=True, exist_ok=True)
with open(metadata_file, "w") as f:
f.write(f"Metadata-Version: 2.1\n")
f.write(f"Name: mscclpp\n")
f.write(f"Version: {version}\n")
# Set environment variable for scikit-build-core
os.environ["SETUPTOOLS_SCM_PRETEND_VERSION"] = version
return version
def get_requires_for_build_wheel(config_settings=None):
"""Get requirements with version generation"""
_generate_version_file()
return _orig_get_requires_for_build_wheel(config_settings)
def prepare_metadata_for_build_wheel(metadata_directory, config_settings=None):
"""Prepare metadata with version generation"""
version = _generate_version_file()
# Call original function
result = _orig_prepare_metadata_for_build_wheel(metadata_directory, config_settings)
# Patch the metadata with correct version
import configparser
metadata_file = Path(metadata_directory) / f"{result}/METADATA"
if metadata_file.exists():
with open(metadata_file, "r") as f:
content = f.read()
# Replace version line
lines = content.split("\n")
for i, line in enumerate(lines):
if line.startswith("Version:"):
lines[i] = f"Version: {version}"
break
with open(metadata_file, "w") as f:
f.write("\n".join(lines))
return result
def build_wheel(wheel_directory, config_settings=None, metadata_directory=None):
"""Build wheel with version generation"""
version = _generate_version_file()
# Set version in environment for scikit-build-core to pick up
os.environ["SETUPTOOLS_SCM_PRETEND_VERSION"] = version
return _orig_build_wheel(wheel_directory, config_settings, metadata_directory)
def build_sdist(sdist_directory, config_settings=None):
"""Build sdist with version generation"""
version = _generate_version_file()
os.environ["SETUPTOOLS_SCM_PRETEND_VERSION"] = version
return _orig_build_sdist(sdist_directory, config_settings)
def build_editable(wheel_directory, config_settings=None, metadata_directory=None):
"""Build editable with version generation"""
version = _generate_version_file()
os.environ["SETUPTOOLS_SCM_PRETEND_VERSION"] = version
return _orig_build_editable(wheel_directory, config_settings, metadata_directory)

View File

@@ -9,7 +9,7 @@
project = "mscclpp"
copyright = "2025, MSCCL++ Team"
author = "MSCCL++ Team"
release = "v0.7.0"
release = "v0.8.0"
# -- General configuration ---------------------------------------------------
# https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration

View File

@@ -206,18 +206,17 @@ export LD_LIBRARY_PATH=$MSCCLPP_INSTALL_DIR:$LD_LIBRARY_PATH
torchrun --nnodes=1 --nproc_per_node=8 your_script.py
```
### Version Tracking
## Version Tracking
The MSCCL++ Python package includes comprehensive version tracking that captures git repository information at build time. This feature allows users to identify the exact source code version of their installed package.
#### Version Format
### Version Format
The package version includes the git commit hash directly in the version string for development builds:
- **Release version**: `0.7.0`
- **Development version**: `0.7.0.dev36+g6e2360d69` (includes short commit hash)
- **Development with uncommitted changes**: `0.7.0.dev36+g6e2360d69.dirty`
- **Development version**: `mscclpp-0.8.0.post1.dev0+gc632fee37.d20251007`
#### Checking Version Information
### Checking Version Information
After installation, you can check the version information in several ways:
@@ -227,16 +226,10 @@ import mscclpp
# Access individual attributes
print(f"Version: {mscclpp.__version__}") # Full version with commit
Version: 0.7.0.dev36+g6e2360d69
Version: 0.8.0.post1.dev0+gc632fee37.d20251007
# Get as dictionary
mscclpp.version
{'version': '0.7.0.dev36+g6e2360d69', 'base_version': '0.7.0', 'git_commit': '6e2360d69'}
{'version': '0.8.0.post1.dev0+gc632fee37.d20251007', 'git_commit': 'g50382c567'}
```
#### Version Information Details
The version tracking captures:
- **Package Version** (`mscclpp.__version__`): Full version string including git commit (e.g., `0.7.1.dev36+g6e2360d69`)
This information is embedded during the package build process and remains accessible even after distribution, making it easier to debug issues and ensure reproducibility.

View File

@@ -3,11 +3,10 @@
[build-system]
requires = [
"scikit-build-core>=0.4.3",
"setuptools-scm[toml]>=6.2"
"scikit-build-core>=0.10.0",
"setuptools-scm[toml]>=8"
]
build-backend = "_build_version_metadata"
backend-path = ["."]
build-backend = "scikit_build_core.build"
[project]
name = "mscclpp"
@@ -17,6 +16,7 @@ requires-python = ">=3.8"
[tool.setuptools_scm]
write_to = "python/mscclpp/_version.py"
version_scheme = "no-guess-dev"
[tool.scikit-build]
cmake.version = ">=3.25.0"
@@ -31,6 +31,9 @@ install-dir = "mscclpp"
license-files = ["VERSION", "LICENSE", "CITATION.cff", "CODE_OF_CONDUCT.md", "README.md", "SECURITY.md", "SUPPORT.md"]
exclude = ["mscclpp/*.cpp"]
[tool.scikit-build.sdist]
include= ["python/mscclpp/_version.py"]
[tool.scikit-build.cmake.define]
MSCCLPP_BUILD_PYTHON_BINDINGS = "ON"
MSCCLPP_BUILD_TESTS = "OFF"

View File

@@ -5,59 +5,22 @@
import os
import warnings
import re
from functools import wraps
# Get version
def _get_version():
"""Get version from the best available source"""
if os.environ.get("MSCCLPP_HOME", None) is None:
os.environ["MSCCLPP_HOME"] = os.path.abspath(os.path.dirname(__file__))
# Try setuptools-scm generated _version.py (most reliable)
try:
from ._version import __version__
from ._version import __version__, __commit_id__
return __version__
except ImportError:
raise RuntimeError("Could not determine MSCCL++ version from setuptools-scm generated _version.py.")
# Parse version components
def _parse_version(version_string):
"""Parse version components from setuptools-scm generated version"""
# Pattern for versions like "0.7.0.dev36+g6e2360d69" (without .dYYYYMMDD)
pattern = r"^v?(?P<base>[\d\.]+)(?:\.dev(?P<distance>\d+))?(?:\+g(?P<commit>[a-f0-9]+))?(?P<dirty>\.dirty)?$"
match = re.match(pattern, version_string)
if match:
return {"base_version": match.group("base"), "git_commit": match.group("commit") or "unknown"}
else:
# Fallback parsing - try to extract what we can
base = version_string.split("+")[0].lstrip("v").split(".dev")[0]
commit = "unknown"
return {"base_version": base, "git_commit": commit}
__version__ = _get_version()
# Parse the version
_version_info = _parse_version(__version__)
__base_version__ = _version_info["base_version"]
__git_commit__ = _version_info["git_commit"]
version = {
"version": __version__,
"git_commit": __commit_id__,
}
def _version():
"""Get complete version information as a dictionary"""
return {
"version": __version__,
"base_version": __base_version__,
"git_commit": __git_commit__,
}
version: dict = _version()
from ._mscclpp import (
Env,
ErrorCode,
@@ -132,9 +95,6 @@ __all__ = [
"SmDevice2DeviceSemaphore",
]
if os.environ.get("MSCCLPP_HOME", None) is None:
os.environ["MSCCLPP_HOME"] = os.path.abspath(os.path.dirname(__file__))
def get_include() -> str:
"""Return the directory that contains the MSCCL++ headers."""