Skip to content
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

Use setuptools_scm for versioning #417

Closed
wants to merge 1 commit into from
Closed
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
22 changes: 20 additions & 2 deletions .github/workflows/build_wheels.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,10 @@ jobs:
steps:
- name: Checkout
uses: actions/checkout@v4
with:
# Should include history since the last tag for setuptools_scm to work
fetch-depth: 200
fetch-tags: true

- name: Build Wheels
run: |
Expand Down Expand Up @@ -42,6 +46,13 @@ jobs:
steps:
- name: Checkout
uses: actions/checkout@v4
with:
# Should include history since the last tag for setuptools_scm to work
fetch-depth: 200
fetch-tags: true

- run: python3 -m pip install setuptools_scm
- run: echo "SYMFORCE_VERSION=$(python3 -m setuptools_scm)" >> $GITHUB_ENV

- name: Build wheels
uses: pypa/[email protected]
Expand All @@ -52,7 +63,7 @@ jobs:
# For arm64; for x86, it's at /usr/local but cmake finds that ok already
GMP_ROOT: /opt/homebrew
CIBW_BEFORE_BUILD: brew install gmp
CIBW_ENVIRONMENT: SYMFORCE_REWRITE_LOCAL_DEPENDENCIES=True MACOSX_DEPLOYMENT_TARGET=13.0
CIBW_ENVIRONMENT: SYMFORCE_REWRITE_LOCAL_DEPENDENCIES=$SYMFORCE_VERSION MACOSX_DEPLOYMENT_TARGET=13.0

- name: Upload wheels
uses: actions/upload-artifact@v4
Expand All @@ -71,14 +82,21 @@ jobs:
steps:
- name: Checkout
uses: actions/checkout@v4
with:
# Should include history since the last tag for setuptools_scm to work
fetch-depth: 200
fetch-tags: true

- run: python3 -m pip install setuptools_scm
- run: echo "SYMFORCE_VERSION=$(python3 -m setuptools_scm)" >> $GITHUB_ENV

- name: Build wheels
uses: pypa/[email protected]
env:
CIBW_BUILD: ${{ matrix.python-version }}-manylinux_x86_64
CIBW_BUILD_FRONTEND: build
CIBW_BEFORE_BUILD: yum install -y gmp-devel git
CIBW_ENVIRONMENT: SYMFORCE_REWRITE_LOCAL_DEPENDENCIES=True
CIBW_ENVIRONMENT: SYMFORCE_REWRITE_LOCAL_DEPENDENCIES=$SYMFORCE_VERSION

- name: Upload Wheels
uses: actions/upload-artifact@v4
Expand Down
42 changes: 15 additions & 27 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -237,35 +237,23 @@ else()

add_custom_target(symforce_eigen_lcm_py ALL DEPENDS eigen_lcm_py)

# Get SymForce version
execute_process(
COMMAND ${SYMFORCE_PYTHON} -c "from _version import version; print(version, end='')"
WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR}/symforce
RESULT_VARIABLE STATUS
OUTPUT_VARIABLE SYMFORCE_VERSION
ERROR_VARIABLE SYMFORCE_VERSION_STDERR
)
if(STATUS AND NOT STATUS EQUAL 0)
message(FATAL_ERROR
"Failed getting symforce version with exit code ${STATUS} and output ${SYMFORCE_VERSION}, stderr ${SYMFORCE_VERSION_STDERR}"
)
endif()

file(WRITE
${CMAKE_CURRENT_BINARY_DIR}/lcmtypes/python2.7/setup.py
${CMAKE_CURRENT_BINARY_DIR}/lcmtypes/python2.7/pyproject.toml
"
from setuptools import setup, find_packages
setup(
name='lcmtypes',
version='${SYMFORCE_VERSION}',
description='lcmtype python bindings (installed by SymForce)',
long_description='lcmtype python bindings (installed by SymForce)',
author='Skydio, Inc',
author_email='[email protected]',
license='Apache 2.0',
packages=find_packages(),
zip_safe=False,
)
[build-system]
requires = ['setuptools', 'setuptools-scm>=8']
build-backend = 'setuptools.build_meta'

[project]
name = 'lcmtypes'
description='lcmtype python bindings (installed by SymForce)'
authors = [{ name = 'Skydio, Inc.', email = '[email protected]' }]
license = { text = 'Apache 2.0' }
requires-python = '>=3.5'
dynamic = ['version']

[tool.setuptools_scm]
root = '../../..'
"
)
endif()
Expand Down
6 changes: 6 additions & 0 deletions dev_requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,7 @@ packaging==24.0
# matplotlib
# nbconvert
# plotly
# setuptools-scm
# sphinx
pandas==2.0.3
# via symforce (setup.py)
Expand Down Expand Up @@ -267,6 +268,8 @@ ruff==0.7.1
# via symforce (setup.py)
scipy==1.10.1
# via symforce (setup.py)
setuptools-scm==8.1.0
# via symforce (setup.py)
six==1.16.0
# via
# asttokens
Expand Down Expand Up @@ -321,6 +324,7 @@ tomli==2.0.1
# build
# mypy
# pip-tools
# setuptools-scm
tornado==6.4
# via
# ipykernel
Expand Down Expand Up @@ -349,6 +353,7 @@ typing-extensions==4.11.0
# via
# ipython
# mypy
# setuptools-scm
tzdata==2024.1
# via pandas
urllib3==2.2.1
Expand Down Expand Up @@ -378,4 +383,5 @@ pip==24.0
setuptools==69.5.1
# via
# pip-tools
# setuptools-scm
# symforce (setup.py)
4 changes: 2 additions & 2 deletions docs/development.rst
Original file line number Diff line number Diff line change
Expand Up @@ -103,8 +103,8 @@ You should be able to build Python wheels of symforce the standard ways. We rec
``build``, i.e. running ``python3 -m build --wheel`` from the ``symforce`` directory. By default,
this will build a wheel that includes local dependencies on the ``skymarshal`` and ``symforce-sym``
packages (which are separate Python packages from ``symforce`` itself). For distribution, you'll
typically want to set the environment variable ``SYMFORCE_REWRITE_LOCAL_DEPENDENCIES=True`` when
building, and also run ``python3 -m build --wheel third_party/skymarshal`` and
typically want to set the environment variable ``SYMFORCE_REWRITE_LOCAL_DEPENDENCIES`` to the
release version when building, and also run ``python3 -m build --wheel third_party/skymarshal`` and
``python3 -m build --wheel gen/python`` to build wheels for those packages separately.

For SymForce releases, all of this is handled by the ``build_wheels`` GitHub Actions workflow. This
Expand Down
7 changes: 5 additions & 2 deletions gen/python/pyproject.toml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 4 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,9 @@ dynamic = ["version", "readme", "dependencies", "optional-dependencies"]
"Bug Tracker" = "https://github.com/symforce-org/symforce/issues"
Source = "https://github.com/symforce-org/symforce"

[tool.setuptools_scm]
# Empty, presence enables setuptools_scm

# --------------------------------------------------------------------------------
# Ruff
# --------------------------------------------------------------------------------
Expand Down Expand Up @@ -153,6 +156,7 @@ module = [
"numba.*",
"ruff.*",
"scipy.*",
"setuptools_scm.*",
"skymarshal.*",
"symengine.*",
"sympy.*",
Expand Down
28 changes: 4 additions & 24 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
# This source code is under the Apache 2.0 license found in the LICENSE file.
# ----------------------------------------------------------------------------

import distutils.util
import multiprocessing
import os
import re
Expand All @@ -25,18 +24,6 @@
ESCAPED_SOURCE_DIR = Path(str(SOURCE_DIR).replace(" ", "%20"))


def symforce_version() -> str:
"""
Fetch the current symforce version from _version.py

We can't import the version here, so we have to do some text parsing
"""
version_file_contents = (Path(__file__).parent / "symforce" / "_version.py").read_text()
version_match = re.search(r'^version = "(.+)"$', version_file_contents, flags=re.MULTILINE)
assert version_match is not None
return version_match.group(1)


class CMakeExtension(Extension):
"""
CMake extension type.
Expand Down Expand Up @@ -227,20 +214,13 @@ class SymForceEggInfo(egg_info):

def initialize_options(self) -> None:
super().initialize_options()
self.rewrite_local_dependencies = False
self.rewrite_local_dependencies: T.Optional[str] = None

def finalize_options(self) -> None:
super().finalize_options()

if not isinstance(self.rewrite_local_dependencies, bool):
self.rewrite_local_dependencies = bool(
distutils.util.strtobool(self.rewrite_local_dependencies)
)

if "SYMFORCE_REWRITE_LOCAL_DEPENDENCIES" in os.environ:
self.rewrite_local_dependencies = bool(
distutils.util.strtobool(os.environ["SYMFORCE_REWRITE_LOCAL_DEPENDENCIES"])
)
self.rewrite_local_dependencies = os.environ["SYMFORCE_REWRITE_LOCAL_DEPENDENCIES"]

def run(self) -> None:
# Rewrite dependencies from the local `file:` versions to generic pinned package versions.
Expand All @@ -251,7 +231,7 @@ def run(self) -> None:

def filter_local(s: str) -> str:
if "@" in s:
s = f"{s.split('@')[0]}=={symforce_version()}"
s = f"{s.split('@')[0]}=={self.rewrite_local_dependencies}"
return s

self.distribution.install_requires = [ # type: ignore[attr-defined]
Expand Down Expand Up @@ -338,6 +318,7 @@ def run(self) -> None:

setup_requirements = [
"setuptools>=62.3.0", # For package data globs
"setuptools-scm>=8",
"wheel",
"pip",
"cmake>=3.17,<3.27",
Expand Down Expand Up @@ -404,7 +385,6 @@ def fixed_readme() -> str:

if __name__ == "__main__":
setup(
version=symforce_version(),
long_description=fixed_readme(),
long_description_content_type="text/markdown",
# The SymForce package is a namespace package (important for data-only subdirectories
Expand Down
35 changes: 28 additions & 7 deletions symforce/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,6 @@
from dataclasses import dataclass
from types import ModuleType

# -------------------------------------------------------------------------------------------------
# Version
# -------------------------------------------------------------------------------------------------

# isort: split
from ._version import version as __version__

# -------------------------------------------------------------------------------------------------
# Logging configuration
# -------------------------------------------------------------------------------------------------
Expand Down Expand Up @@ -66,6 +59,34 @@ def set_log_level(log_level: str) -> None:
# Set default
set_log_level(os.environ.get("SYMFORCE_LOGLEVEL", "INFO"))

# -------------------------------------------------------------------------------------------------
# Version
# -------------------------------------------------------------------------------------------------

# isort: split
from importlib.metadata import PackageNotFoundError
from importlib.metadata import version

try:
__version__ = version("symforce")
except PackageNotFoundError:
logger.debug(
"symforce package is not being run from an installed context; falling back to"
" setuptools_scm for __version__"
)

try:
import setuptools_scm
except ImportError:
# setuptools_scm isn't required to be installed
logger.debug("setuptools_scm not installed; __version__ will not be set")
else:
try:
__version__ = setuptools_scm.get_version(root="..", relative_to=__file__)
except LookupError:
logger.debug("setuptools_scm failed to find version; __version__ will not be set")


# -------------------------------------------------------------------------------------------------
# Symbolic API configuration
# -------------------------------------------------------------------------------------------------
Expand Down
6 changes: 0 additions & 6 deletions symforce/_version.py

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -4,19 +4,22 @@
# ---------------------------------------------------------------------------- #}

[build-system]
requires = ["setuptools"]
requires = ["setuptools", "setuptools-scm>=8"]
build-backend = "setuptools.build_meta"

[project]
name = "{{ package_name }}"
description = "{{ description }} (installed by SymForce)"
authors = [{ name = "Skydio, Inc.", email = "[email protected]" }]
license = { text = "Apache 2.0" }
version = "{{ version }}"
dependencies = ["numpy"]
requires-python = ">=3.5"
dynamic = ["version"]

[project.urls]
SymForce = "https://symforce.org"
"Bug Tracker" = "https://github.com/symforce-org/symforce/issues"
Source = "https://github.com/symforce-org/symforce/tree/main/gen/python"

[tool.setuptools_scm]
root = "../.."
1 change: 0 additions & 1 deletion test/symforce_gen_codegen_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,6 @@ def test_gen_package_codegen_python(self) -> None:
output_path=output_dir / "pyproject.toml",
data=dict(
package_name="symforce-sym",
version=symforce.__version__,
description="generated numerical python package",
),
config=dataclasses.replace(config.render_template_config, autoformat=False),
Expand Down
7 changes: 5 additions & 2 deletions third_party/skymarshal/pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
[build-system]
requires = ["setuptools"]
requires = ["setuptools", "setuptools-scm>=8"]
build-backend = "setuptools.build_meta"

[project]
name = "skymarshal"
description = "Python implementation of marshalling for LCM messages"
version = "0.9.0"
authors = [{ name = "Skydio, Inc." }]
license = { text = "LGPL-2.1-or-later" }
readme = "README.md"
Expand All @@ -20,10 +19,14 @@ classifiers = [
]
requires-python = ">=3.8"
dependencies = ["argh", "jinja2", "numpy", "ply"]
dynamic = ["version"]

[project.urls]
"Bug Tracker" = "https://github.com/symforce-org/symforce/issues"
Source = "https://github.com/symforce-org/symforce/tree/main/third_party/skymarshal"

[tool.setuptools_scm]
root = "../.."

[tool.setuptools.packages.find]
include = ["skymarshal", "skymarshal.*"]