diff --git a/.github/workflows/build_wheels.yml b/.github/workflows/build_wheels.yml index e0bd12e4..2a9606a5 100644 --- a/.github/workflows/build_wheels.yml +++ b/.github/workflows/build_wheels.yml @@ -51,8 +51,8 @@ jobs: CIBW_BUILD_FRONTEND: build # 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_BEFORE_BUILD: brew install gmp && python3 -m pip install setuptools_scm + CIBW_ENVIRONMENT: SYMFORCE_REWRITE_LOCAL_DEPENDENCIES=$(python3 -m setuptools_scm) MACOSX_DEPLOYMENT_TARGET=13.0 - name: Upload wheels uses: actions/upload-artifact@v4 @@ -77,8 +77,8 @@ jobs: 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_BEFORE_BUILD: yum install -y gmp-devel git && python3 -m pip install setuptools_scm + CIBW_ENVIRONMENT: SYMFORCE_REWRITE_LOCAL_DEPENDENCIES=$(python3 -m setuptools_scm) - name: Upload Wheels uses: actions/upload-artifact@v4 diff --git a/CMakeLists.txt b/CMakeLists.txt index 7a8f9497..cab37f36 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -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='hayk@skydio.com', - 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 = 'hayk@skydio.com' }] +license = { text = 'Apache 2.0' } +requires-python = '>=3.5' +dynamic = ['version'] + +[tool.setuptools_scm] +root = '../../..' " ) endif() diff --git a/dev_requirements.txt b/dev_requirements.txt index 6a11a1a1..53522d5e 100644 --- a/dev_requirements.txt +++ b/dev_requirements.txt @@ -193,6 +193,7 @@ packaging==24.0 # matplotlib # nbconvert # plotly + # setuptools-scm # sphinx pandas==2.0.3 # via symforce (setup.py) @@ -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 @@ -321,6 +324,7 @@ tomli==2.0.1 # build # mypy # pip-tools + # setuptools-scm tornado==6.4 # via # ipykernel @@ -349,6 +353,7 @@ typing-extensions==4.11.0 # via # ipython # mypy + # setuptools-scm tzdata==2024.1 # via pandas urllib3==2.2.1 @@ -378,4 +383,5 @@ pip==24.0 setuptools==69.5.1 # via # pip-tools + # setuptools-scm # symforce (setup.py) diff --git a/docs/development.rst b/docs/development.rst index 08545b59..777c3473 100644 --- a/docs/development.rst +++ b/docs/development.rst @@ -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 diff --git a/gen/python/pyproject.toml b/gen/python/pyproject.toml index 080c347e..8ae737aa 100644 --- a/gen/python/pyproject.toml +++ b/gen/python/pyproject.toml @@ -6,7 +6,7 @@ [build-system] -requires = ["setuptools"] +requires = ["setuptools", "setuptools-scm>=8"] build-backend = "setuptools.build_meta" [project] @@ -14,11 +14,14 @@ name = "symforce-sym" description = "generated numerical python package (installed by SymForce)" authors = [{ name = "Skydio, Inc.", email = "hayk@skydio.com" }] license = { text = "Apache 2.0" } -version = "0.9.0" 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 = "../.." diff --git a/pyproject.toml b/pyproject.toml index 960f2036..08383f43 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -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 # -------------------------------------------------------------------------------- diff --git a/setup.py b/setup.py index 301d757a..dec52e8c 100644 --- a/setup.py +++ b/setup.py @@ -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 @@ -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. @@ -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. @@ -248,10 +228,9 @@ def run(self) -> None: # are hosted as their own PyPI packages. We can't just decide whether to do this e.g. based # on whether we're building a wheel, since `pip install .` also builds a wheel to install. if self.rewrite_local_dependencies: - 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] @@ -338,6 +317,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", @@ -404,7 +384,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 diff --git a/symforce/__init__.py b/symforce/__init__.py index 4387fcd1..81d736d4 100644 --- a/symforce/__init__.py +++ b/symforce/__init__.py @@ -24,7 +24,20 @@ # ------------------------------------------------------------------------------------------------- # isort: split -from ._version import version as __version__ +from importlib.metadata import PackageNotFoundError +from importlib.metadata import version + +try: + __version__ = version("symforce") +except PackageNotFoundError: + # package is not installed. Try running setuptools_scm + import setuptools_scm + + try: + __version__ = setuptools_scm.get_version() + except LookupError: + pass + # ------------------------------------------------------------------------------------------------- # Logging configuration diff --git a/symforce/_version.py b/symforce/_version.py deleted file mode 100644 index 4c1c1d42..00000000 --- a/symforce/_version.py +++ /dev/null @@ -1,6 +0,0 @@ -# ---------------------------------------------------------------------------- -# SymForce - Copyright 2022, Skydio, Inc. -# This source code is under the Apache 2.0 license found in the LICENSE file. -# ---------------------------------------------------------------------------- - -version = "0.9.0" diff --git a/symforce/codegen/backends/python/templates/pyproject.toml.jinja b/symforce/codegen/backends/python/templates/pyproject.toml.jinja index ac4ebd62..2c2a9a14 100644 --- a/symforce/codegen/backends/python/templates/pyproject.toml.jinja +++ b/symforce/codegen/backends/python/templates/pyproject.toml.jinja @@ -4,7 +4,7 @@ # ---------------------------------------------------------------------------- #} [build-system] -requires = ["setuptools"] +requires = ["setuptools", "setuptools-scm>=8"] build-backend = "setuptools.build_meta" [project] @@ -12,11 +12,14 @@ name = "{{ package_name }}" description = "{{ description }} (installed by SymForce)" authors = [{ name = "Skydio, Inc.", email = "hayk@skydio.com" }] 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 = "../.." diff --git a/test/symforce_gen_codegen_test.py b/test/symforce_gen_codegen_test.py index 03f8d6e9..17db2aad 100644 --- a/test/symforce_gen_codegen_test.py +++ b/test/symforce_gen_codegen_test.py @@ -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), diff --git a/third_party/skymarshal/pyproject.toml b/third_party/skymarshal/pyproject.toml index 725ea121..845ae3f4 100644 --- a/third_party/skymarshal/pyproject.toml +++ b/third_party/skymarshal/pyproject.toml @@ -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" @@ -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.*"]