Skip to content

Dynamically determine the version of the pip package. #3259

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Apr 23, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,10 @@ build-backend = "setuptools.build_meta"

[project]
name = "executorch"
# TODO(dbort): Use setuptools-git-versioning or setuptools-scm to get the
# version from the git branch state. For now, use a version that doesn't look
# like a real release.
version = "0.2.0.dev0+unknown"
dynamic = [
# setup.py will set the version.
'version',
]
# Python dependencies required for development
dependencies=[
"expecttest",
Expand Down
76 changes: 76 additions & 0 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@
from distutils import log
from distutils.sysconfig import get_python_lib
from pathlib import Path
from typing import Optional

from setuptools import Extension, setup
from setuptools.command.build import build
Expand Down Expand Up @@ -84,6 +85,77 @@ def pybindings(cls) -> bool:
return cls._is_env_enabled("EXECUTORCH_BUILD_PYBIND", default=False)


class Version:
"""Static properties that describe the version of the pip package."""

# Cached values returned by the properties.
__root_dir_attr: Optional[str] = None
__string_attr: Optional[str] = None
__git_hash_attr: Optional[str] = None

@classmethod
@property
def _root_dir(cls) -> str:
"""The path to the root of the git repo."""
if cls.__root_dir_attr is None:
# This setup.py file lives in the root of the repo.
cls.__root_dir_attr = str(Path(__file__).parent.resolve())
return str(cls.__root_dir_attr)

@classmethod
@property
def git_hash(cls) -> Optional[str]:
"""The current git hash, if known."""
if cls.__git_hash_attr is None:
import subprocess

try:
cls.__git_hash_attr = (
subprocess.check_output(
["git", "rev-parse", "HEAD"], cwd=cls._root_dir
)
.decode("ascii")
.strip()
)
except subprocess.CalledProcessError:
cls.__git_hash_attr = "" # Non-None but empty.
# A non-None but empty value indicates that we don't know it.
return cls.__git_hash_attr if cls.__git_hash_attr else None

@classmethod
@property
def string(cls) -> str:
"""The version string."""
if cls.__string_attr is None:
# If set, BUILD_VERSION should override any local version
# information. CI will use this to manage, e.g., release vs. nightly
# versions.
version = os.getenv("BUILD_VERSION", "").strip()
if not version:
# Otherwise, read the version from a local file and add the git
# commit if available.
version = (
open(os.path.join(cls._root_dir, "version.txt")).read().strip()
)
if cls.git_hash:
version += "+" + cls.git_hash[:7]
cls.__string_attr = version
return cls.__string_attr

@classmethod
def write_to_python_file(cls, path: str) -> None:
"""Creates a file similar to PyTorch core's `torch/version.py`."""
lines = [
"from typing import Optional",
'__all__ = ["__version__", "git_version"]',
f'__version__ = "{cls.string}"',
# A string or None.
f"git_version: Optional[str] = {repr(cls.git_hash)}",
]
with open(path, "w") as fp:
fp.write("\n".join(lines) + "\n")


class _BaseExtension(Extension):
"""A base class that maps an abstract source to an abstract destination."""

Expand Down Expand Up @@ -269,6 +341,9 @@ def run(self):
# package subdirectory.
dst_root = os.path.join(self.build_lib, self.get_package_dir("executorch"))

# Create the version file.
Version.write_to_python_file(os.path.join(dst_root, "version.py"))

# Manually copy files into the output package directory. These are
# typically python "resource" files that will live alongside the python
# code that uses them.
Expand Down Expand Up @@ -447,6 +522,7 @@ def get_ext_modules() -> list[Extension]:


setup(
version=Version.string,
# TODO(dbort): Could use py_modules to restrict the set of modules we
# package, and package_data to restrict the set up non-python files we
# include. See also setuptools/discovery.py for custom finders.
Expand Down
Loading