colcon-uv 0.0.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- colcon_uv-0.0.1/.gitignore +165 -0
- colcon_uv-0.0.1/PKG-INFO +72 -0
- colcon_uv-0.0.1/README.md +53 -0
- colcon_uv-0.0.1/colcon_uv/__init__.py +3 -0
- colcon_uv-0.0.1/colcon_uv/cli.py +102 -0
- colcon_uv-0.0.1/colcon_uv/dependencies/__init__.py +0 -0
- colcon_uv-0.0.1/colcon_uv/dependencies/install.py +187 -0
- colcon_uv-0.0.1/colcon_uv/package_augmentation/__init__.py +5 -0
- colcon_uv-0.0.1/colcon_uv/package_augmentation/uv.py +122 -0
- colcon_uv-0.0.1/colcon_uv/package_identification/__init__.py +5 -0
- colcon_uv-0.0.1/colcon_uv/package_identification/uv.py +53 -0
- colcon_uv-0.0.1/colcon_uv/task/__init__.py +6 -0
- colcon_uv-0.0.1/colcon_uv/task/uv/__init__.py +6 -0
- colcon_uv-0.0.1/colcon_uv/task/uv/build.py +170 -0
- colcon_uv-0.0.1/colcon_uv/task/uv/test.py +47 -0
- colcon_uv-0.0.1/pyproject.toml +77 -0
- colcon_uv-0.0.1/tests/test_cli.py +116 -0
- colcon_uv-0.0.1/tests/test_dependencies.py +169 -0
- colcon_uv-0.0.1/tests/test_package_augmentation.py +166 -0
- colcon_uv-0.0.1/tests/test_package_identification.py +78 -0
- colcon_uv-0.0.1/tests/test_task_build.py +386 -0
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
# Byte-compiled / optimized / DLL files
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[cod]
|
|
4
|
+
*$py.class
|
|
5
|
+
|
|
6
|
+
# C extensions
|
|
7
|
+
*.so
|
|
8
|
+
|
|
9
|
+
# Distribution / packaging
|
|
10
|
+
.Python
|
|
11
|
+
build/
|
|
12
|
+
develop-eggs/
|
|
13
|
+
dist/
|
|
14
|
+
downloads/
|
|
15
|
+
eggs/
|
|
16
|
+
.eggs/
|
|
17
|
+
lib/
|
|
18
|
+
lib64/
|
|
19
|
+
parts/
|
|
20
|
+
sdist/
|
|
21
|
+
var/
|
|
22
|
+
wheels/
|
|
23
|
+
pip-wheel-metadata/
|
|
24
|
+
share/python-wheels/
|
|
25
|
+
*.egg-info/
|
|
26
|
+
.installed.cfg
|
|
27
|
+
*.egg
|
|
28
|
+
MANIFEST
|
|
29
|
+
|
|
30
|
+
# PyInstaller
|
|
31
|
+
# Usually these files are written by a python script from a template
|
|
32
|
+
# before PyInstaller builds the exe, so as to inject date/other infos into it.
|
|
33
|
+
*.manifest
|
|
34
|
+
*.spec
|
|
35
|
+
|
|
36
|
+
# Installer logs
|
|
37
|
+
pip-log.txt
|
|
38
|
+
pip-delete-this-directory.txt
|
|
39
|
+
|
|
40
|
+
# Unit test / coverage reports
|
|
41
|
+
htmlcov/
|
|
42
|
+
.tox/
|
|
43
|
+
.nox/
|
|
44
|
+
.coverage
|
|
45
|
+
.coverage.*
|
|
46
|
+
.cache
|
|
47
|
+
nosetests.xml
|
|
48
|
+
coverage.xml
|
|
49
|
+
*.cover
|
|
50
|
+
*.py,cover
|
|
51
|
+
.hypothesis/
|
|
52
|
+
.pytest_cache/
|
|
53
|
+
|
|
54
|
+
# Translations
|
|
55
|
+
*.mo
|
|
56
|
+
*.pot
|
|
57
|
+
|
|
58
|
+
# Django stuff:
|
|
59
|
+
*.log
|
|
60
|
+
local_settings.py
|
|
61
|
+
db.sqlite3
|
|
62
|
+
db.sqlite3-journal
|
|
63
|
+
|
|
64
|
+
# Flask stuff:
|
|
65
|
+
instance/
|
|
66
|
+
.webassets-cache
|
|
67
|
+
|
|
68
|
+
# Scrapy stuff:
|
|
69
|
+
.scrapy
|
|
70
|
+
|
|
71
|
+
# Sphinx documentation
|
|
72
|
+
docs/_build/
|
|
73
|
+
|
|
74
|
+
# PyBuilder
|
|
75
|
+
target/
|
|
76
|
+
|
|
77
|
+
# Jupyter Notebook
|
|
78
|
+
.ipynb_checkpoints
|
|
79
|
+
|
|
80
|
+
# IPython
|
|
81
|
+
profile_default/
|
|
82
|
+
ipython_config.py
|
|
83
|
+
|
|
84
|
+
# pyenv
|
|
85
|
+
.python-version
|
|
86
|
+
|
|
87
|
+
# pipenv
|
|
88
|
+
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
|
|
89
|
+
# However, in case of collaboration, if having platform-specific dependencies or dependencies
|
|
90
|
+
# having no cross-platform support, pipenv may install dependencies that don't work, or not
|
|
91
|
+
# install all needed dependencies.
|
|
92
|
+
#Pipfile.lock
|
|
93
|
+
|
|
94
|
+
# PEP 582; used by e.g. github.com/David-OConnor/pyflow
|
|
95
|
+
__pypackages__/
|
|
96
|
+
|
|
97
|
+
# Celery stuff
|
|
98
|
+
celerybeat-schedule
|
|
99
|
+
celerybeat.pid
|
|
100
|
+
|
|
101
|
+
# SageMath parsed files
|
|
102
|
+
*.sage.py
|
|
103
|
+
|
|
104
|
+
# Environments
|
|
105
|
+
.env
|
|
106
|
+
.venv
|
|
107
|
+
env/
|
|
108
|
+
venv/
|
|
109
|
+
ENV/
|
|
110
|
+
env.bak/
|
|
111
|
+
venv.bak/
|
|
112
|
+
|
|
113
|
+
# Spyder project settings
|
|
114
|
+
.spyderproject
|
|
115
|
+
.spyproject
|
|
116
|
+
|
|
117
|
+
# Rope project settings
|
|
118
|
+
.ropeproject
|
|
119
|
+
|
|
120
|
+
# mkdocs documentation
|
|
121
|
+
/site
|
|
122
|
+
|
|
123
|
+
# mypy
|
|
124
|
+
.mypy_cache/
|
|
125
|
+
.dmypy.json
|
|
126
|
+
dmypy.json
|
|
127
|
+
|
|
128
|
+
# Pyre type checker
|
|
129
|
+
.pyre/
|
|
130
|
+
|
|
131
|
+
# ROS specific
|
|
132
|
+
install/
|
|
133
|
+
build/
|
|
134
|
+
log/
|
|
135
|
+
devel/
|
|
136
|
+
|
|
137
|
+
# Colcon specific
|
|
138
|
+
.colcon_install_layout
|
|
139
|
+
.colcon_build_layout
|
|
140
|
+
|
|
141
|
+
# UV specific
|
|
142
|
+
.uv/
|
|
143
|
+
uv.lock
|
|
144
|
+
|
|
145
|
+
# Docker
|
|
146
|
+
.dockerignore
|
|
147
|
+
|
|
148
|
+
# IDE
|
|
149
|
+
.vscode/
|
|
150
|
+
.idea/
|
|
151
|
+
*.swp
|
|
152
|
+
*.swo
|
|
153
|
+
*~
|
|
154
|
+
|
|
155
|
+
# OS
|
|
156
|
+
.DS_Store
|
|
157
|
+
.DS_Store?
|
|
158
|
+
._*
|
|
159
|
+
.Spotlight-V100
|
|
160
|
+
.Trashes
|
|
161
|
+
ehthumbs.db
|
|
162
|
+
Thumbs.db
|
|
163
|
+
|
|
164
|
+
# Github Actions
|
|
165
|
+
act*
|
colcon_uv-0.0.1/PKG-INFO
ADDED
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: colcon-uv
|
|
3
|
+
Version: 0.0.1
|
|
4
|
+
Summary: A Colcon extension providing support for Python projects that use UV
|
|
5
|
+
Project-URL: Homepage, https://github.com/nzlz/colcon-uv
|
|
6
|
+
Author-email: Nestor Gonzalez <nestor94gonzalez@gmail.com>
|
|
7
|
+
Maintainer-email: Nestor Gonzalez <nestor94gonzalez@gmail.com>
|
|
8
|
+
License: BSD-3-Clause
|
|
9
|
+
Keywords: colcon
|
|
10
|
+
Classifier: Development Status :: 3 - Alpha
|
|
11
|
+
Classifier: Environment :: Plugins
|
|
12
|
+
Classifier: Intended Audience :: Developers
|
|
13
|
+
Classifier: License :: OSI Approved :: BSD License
|
|
14
|
+
Classifier: Programming Language :: Python
|
|
15
|
+
Classifier: Topic :: Software Development :: Build Tools
|
|
16
|
+
Requires-Dist: colcon-core~=0.6
|
|
17
|
+
Requires-Dist: tomli>=1.2.0
|
|
18
|
+
Description-Content-Type: text/markdown
|
|
19
|
+
|
|
20
|
+
# colcon-uv
|
|
21
|
+
|
|
22
|
+
[](https://github.com/nzlz/colcon-uv/actions/workflows/ci.yml)
|
|
23
|
+
|
|
24
|
+
A **colcon extension** for building and testing Python packages that use **[uv](https://github.com/astral-sh/uv)** for dependency management.
|
|
25
|
+
|
|
26
|
+
## Features
|
|
27
|
+
|
|
28
|
+
- **Fast Dependency Management**: Leverages UV's lightning-fast dependency resolution and installation
|
|
29
|
+
- **Modern Python Packaging**: Support for `pyproject.toml`-based packages following PEP 517/518 standards
|
|
30
|
+
- **ROS Integration**: Seamless integration with colcon build system and ROS package management
|
|
31
|
+
- **Dependency Isolation**: Prevents dependency conflicts between packages
|
|
32
|
+
|
|
33
|
+
## Installation
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
pip install colcon-uv
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
## Configuration
|
|
40
|
+
|
|
41
|
+
### Data Files
|
|
42
|
+
|
|
43
|
+
Similar to [colcon-poetry-ros](https://github.com/UrbanMachine/colcon-poetry-ros), you can specify data files using the `[tool.colcon-uv-ros.data-files]` section:
|
|
44
|
+
|
|
45
|
+
```toml
|
|
46
|
+
[tool.colcon-uv-ros.data-files]
|
|
47
|
+
"share/ament_index/resource_index/packages" = ["resource/{package_name}"]
|
|
48
|
+
"share/{package_name}" = ["package.xml", "launch/", "config/"]
|
|
49
|
+
"lib/{package_name}" = ["scripts/"]
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
**Required entries** for all ROS packages:
|
|
53
|
+
|
|
54
|
+
```toml
|
|
55
|
+
[tool.colcon-uv-ros.data-files]
|
|
56
|
+
"share/ament_index/resource_index/packages" = ["resource/{package_name}"]
|
|
57
|
+
"share/{package_name}" = ["package.xml"]
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
### Package Dependencies
|
|
61
|
+
|
|
62
|
+
Specify package dependencies for build ordering and to use system libraries (fetched from system paths, not installed in virtual environment):
|
|
63
|
+
|
|
64
|
+
```toml
|
|
65
|
+
[tool.colcon-uv-ros.dependencies]
|
|
66
|
+
depend = ["rclpy", "geometry_msgs"] # System packages (adds to both build_depend and exec_depend)
|
|
67
|
+
build_depend = ["bar_package"] # Build-time only dependency
|
|
68
|
+
exec_depend = ["std_msgs"] # Runtime system library
|
|
69
|
+
test_depend = ["qux_package"] # Test-time only dependency
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
**Important**: ROS system libraries like `rclpy`, `geometry_msgs`, `std_msgs`, etc. should be listed here so they are resolved from the system installation rather than being installed into the virtual environment.
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
# colcon-uv
|
|
2
|
+
|
|
3
|
+
[](https://github.com/nzlz/colcon-uv/actions/workflows/ci.yml)
|
|
4
|
+
|
|
5
|
+
A **colcon extension** for building and testing Python packages that use **[uv](https://github.com/astral-sh/uv)** for dependency management.
|
|
6
|
+
|
|
7
|
+
## Features
|
|
8
|
+
|
|
9
|
+
- **Fast Dependency Management**: Leverages UV's lightning-fast dependency resolution and installation
|
|
10
|
+
- **Modern Python Packaging**: Support for `pyproject.toml`-based packages following PEP 517/518 standards
|
|
11
|
+
- **ROS Integration**: Seamless integration with colcon build system and ROS package management
|
|
12
|
+
- **Dependency Isolation**: Prevents dependency conflicts between packages
|
|
13
|
+
|
|
14
|
+
## Installation
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
pip install colcon-uv
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
## Configuration
|
|
21
|
+
|
|
22
|
+
### Data Files
|
|
23
|
+
|
|
24
|
+
Similar to [colcon-poetry-ros](https://github.com/UrbanMachine/colcon-poetry-ros), you can specify data files using the `[tool.colcon-uv-ros.data-files]` section:
|
|
25
|
+
|
|
26
|
+
```toml
|
|
27
|
+
[tool.colcon-uv-ros.data-files]
|
|
28
|
+
"share/ament_index/resource_index/packages" = ["resource/{package_name}"]
|
|
29
|
+
"share/{package_name}" = ["package.xml", "launch/", "config/"]
|
|
30
|
+
"lib/{package_name}" = ["scripts/"]
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
**Required entries** for all ROS packages:
|
|
34
|
+
|
|
35
|
+
```toml
|
|
36
|
+
[tool.colcon-uv-ros.data-files]
|
|
37
|
+
"share/ament_index/resource_index/packages" = ["resource/{package_name}"]
|
|
38
|
+
"share/{package_name}" = ["package.xml"]
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
### Package Dependencies
|
|
42
|
+
|
|
43
|
+
Specify package dependencies for build ordering and to use system libraries (fetched from system paths, not installed in virtual environment):
|
|
44
|
+
|
|
45
|
+
```toml
|
|
46
|
+
[tool.colcon-uv-ros.dependencies]
|
|
47
|
+
depend = ["rclpy", "geometry_msgs"] # System packages (adds to both build_depend and exec_depend)
|
|
48
|
+
build_depend = ["bar_package"] # Build-time only dependency
|
|
49
|
+
exec_depend = ["std_msgs"] # Runtime system library
|
|
50
|
+
test_depend = ["qux_package"] # Test-time only dependency
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
**Important**: ROS system libraries like `rclpy`, `geometry_msgs`, `std_msgs`, etc. should be listed here so they are resolved from the system installation rather than being installed into the virtual environment.
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
"""Command-line interface for colcon-uv."""
|
|
2
|
+
|
|
3
|
+
import argparse
|
|
4
|
+
import logging
|
|
5
|
+
import sys
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
|
|
8
|
+
from colcon_core.plugin_system import satisfies_version
|
|
9
|
+
from colcon_core.verb import VerbExtensionPoint
|
|
10
|
+
|
|
11
|
+
from .dependencies.install import discover_packages, install_dependencies
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class UvVerb(VerbExtensionPoint):
|
|
15
|
+
"""UV command for colcon."""
|
|
16
|
+
|
|
17
|
+
def __init__(self): # noqa: D107
|
|
18
|
+
super().__init__()
|
|
19
|
+
satisfies_version(VerbExtensionPoint.EXTENSION_POINT_VERSION, "^1.0")
|
|
20
|
+
|
|
21
|
+
def add_arguments(self, *, parser): # noqa: D102
|
|
22
|
+
subparsers = parser.add_subparsers(dest="uv_command", help="UV commands")
|
|
23
|
+
|
|
24
|
+
# Install subcommand
|
|
25
|
+
install_parser = subparsers.add_parser(
|
|
26
|
+
"install", help="Install dependencies for UV-based packages"
|
|
27
|
+
)
|
|
28
|
+
install_parser.add_argument(
|
|
29
|
+
"--base-paths",
|
|
30
|
+
nargs="*",
|
|
31
|
+
help="The base paths to recursively crawl for packages",
|
|
32
|
+
)
|
|
33
|
+
install_parser.add_argument(
|
|
34
|
+
"--uv-args", nargs="*", help="Additional arguments to pass to UV"
|
|
35
|
+
)
|
|
36
|
+
|
|
37
|
+
def main(self, *, context): # noqa: D102
|
|
38
|
+
args = context.args
|
|
39
|
+
logger = logging.getLogger("colcon.uv.cli")
|
|
40
|
+
|
|
41
|
+
if not hasattr(args, "uv_command") or args.uv_command != "install":
|
|
42
|
+
logger.error("Usage: colcon uv install [options]")
|
|
43
|
+
return 1
|
|
44
|
+
|
|
45
|
+
# Get base paths
|
|
46
|
+
base_paths = args.base_paths
|
|
47
|
+
if not base_paths:
|
|
48
|
+
base_paths = [Path(".")]
|
|
49
|
+
else:
|
|
50
|
+
base_paths = [Path(p) for p in base_paths]
|
|
51
|
+
|
|
52
|
+
# Discover UV packages using the install.py logic
|
|
53
|
+
try:
|
|
54
|
+
packages = discover_packages(base_paths)
|
|
55
|
+
except SystemExit:
|
|
56
|
+
logger.warning("No UV packages found")
|
|
57
|
+
return 0
|
|
58
|
+
|
|
59
|
+
# Install dependencies for each package using install.py logic
|
|
60
|
+
install_base = Path("install") # Default install base
|
|
61
|
+
merge_install = False # Default to separate installs
|
|
62
|
+
|
|
63
|
+
for package in packages:
|
|
64
|
+
try:
|
|
65
|
+
install_dependencies(package, install_base, merge_install)
|
|
66
|
+
except Exception as e:
|
|
67
|
+
logger.error(
|
|
68
|
+
f"Failed to install dependencies for '{package.name}': {e}"
|
|
69
|
+
)
|
|
70
|
+
return 1
|
|
71
|
+
|
|
72
|
+
return 0
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
def main():
|
|
76
|
+
"""Run the CLI."""
|
|
77
|
+
parser = argparse.ArgumentParser(
|
|
78
|
+
description="Install dependencies for UV-based packages"
|
|
79
|
+
)
|
|
80
|
+
parser.add_argument(
|
|
81
|
+
"--base-paths",
|
|
82
|
+
nargs="*",
|
|
83
|
+
help="The base paths to recursively crawl for packages",
|
|
84
|
+
)
|
|
85
|
+
parser.add_argument(
|
|
86
|
+
"--uv-args", nargs="*", help="Additional arguments to pass to UV"
|
|
87
|
+
)
|
|
88
|
+
|
|
89
|
+
args = parser.parse_args()
|
|
90
|
+
|
|
91
|
+
# Set up logging
|
|
92
|
+
logging.basicConfig(
|
|
93
|
+
level=logging.INFO, format="%(levelname)s:%(name)s: %(message)s"
|
|
94
|
+
)
|
|
95
|
+
|
|
96
|
+
# Run the UV verb
|
|
97
|
+
verb = UvVerb()
|
|
98
|
+
return verb.main(context=argparse.Namespace(args=args))
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
if __name__ == "__main__":
|
|
102
|
+
sys.exit(main())
|
|
File without changes
|
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
"""Install dependencies for UV packages."""
|
|
2
|
+
|
|
3
|
+
import argparse
|
|
4
|
+
import logging
|
|
5
|
+
import subprocess
|
|
6
|
+
import sys
|
|
7
|
+
from pathlib import Path
|
|
8
|
+
from typing import List
|
|
9
|
+
|
|
10
|
+
import tomli
|
|
11
|
+
|
|
12
|
+
logger = logging.getLogger("colcon.uv.dependencies")
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class NotAUvPackageError(Exception):
|
|
16
|
+
"""Raised when a directory is not a UV package."""
|
|
17
|
+
|
|
18
|
+
pass
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class UvPackage:
|
|
22
|
+
"""Represents a UV package."""
|
|
23
|
+
|
|
24
|
+
def __init__(self, path: Path, logger=None):
|
|
25
|
+
"""Initialize UV package."""
|
|
26
|
+
self.path = path
|
|
27
|
+
self.logger = logger or logging.getLogger(__name__)
|
|
28
|
+
|
|
29
|
+
self.pyproject_file = path / "pyproject.toml"
|
|
30
|
+
if not self.pyproject_file.exists():
|
|
31
|
+
raise NotAUvPackageError(f"No pyproject.toml found in {path}")
|
|
32
|
+
|
|
33
|
+
# Load pyproject.toml
|
|
34
|
+
with open(self.pyproject_file, "rb") as f:
|
|
35
|
+
self.pyproject_data = tomli.load(f)
|
|
36
|
+
|
|
37
|
+
# Check if it's a UV package
|
|
38
|
+
if (
|
|
39
|
+
"tool" not in self.pyproject_data
|
|
40
|
+
or "colcon-uv-ros" not in self.pyproject_data["tool"]
|
|
41
|
+
):
|
|
42
|
+
raise NotAUvPackageError(
|
|
43
|
+
f"No [tool.colcon-uv-ros] section found in {self.pyproject_file}"
|
|
44
|
+
)
|
|
45
|
+
|
|
46
|
+
# Get package name
|
|
47
|
+
if (
|
|
48
|
+
"project" in self.pyproject_data
|
|
49
|
+
and "name" in self.pyproject_data["project"]
|
|
50
|
+
):
|
|
51
|
+
self.name = self.pyproject_data["project"]["name"]
|
|
52
|
+
else:
|
|
53
|
+
self.name = path.name
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def main():
|
|
57
|
+
"""Main entry point for UV dependency installation."""
|
|
58
|
+
args = _parse_args()
|
|
59
|
+
logging.basicConfig(
|
|
60
|
+
level=logging.DEBUG if args.verbose else logging.INFO,
|
|
61
|
+
format="%(levelname)s:%(name)s: %(message)s",
|
|
62
|
+
)
|
|
63
|
+
|
|
64
|
+
for project in discover_packages(args.base_paths):
|
|
65
|
+
logger.info(f"Installing dependencies for {project.path.name}...")
|
|
66
|
+
install_dependencies(project, args.install_base, args.merge_install)
|
|
67
|
+
|
|
68
|
+
logger.info("Dependencies installed!")
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
def discover_packages(base_paths: List[Path]) -> List[UvPackage]:
|
|
72
|
+
"""Discover UV packages in the given base paths."""
|
|
73
|
+
projects: List[UvPackage] = []
|
|
74
|
+
|
|
75
|
+
potential_packages = []
|
|
76
|
+
for path in base_paths:
|
|
77
|
+
potential_packages += list(path.glob("*"))
|
|
78
|
+
|
|
79
|
+
for path in potential_packages:
|
|
80
|
+
if path.is_dir():
|
|
81
|
+
try:
|
|
82
|
+
project = UvPackage(path)
|
|
83
|
+
except NotAUvPackageError:
|
|
84
|
+
continue
|
|
85
|
+
else:
|
|
86
|
+
projects.append(project)
|
|
87
|
+
|
|
88
|
+
if len(projects) == 0:
|
|
89
|
+
base_paths_str = ", ".join([str(p) for p in base_paths])
|
|
90
|
+
logger.error(
|
|
91
|
+
f"No UV packages were found in the following paths: {base_paths_str}"
|
|
92
|
+
)
|
|
93
|
+
sys.exit(1)
|
|
94
|
+
|
|
95
|
+
return projects
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
def install_dependencies(
|
|
99
|
+
project: UvPackage, install_base: Path, merge_install: bool
|
|
100
|
+
) -> None:
|
|
101
|
+
"""Install dependencies for a UV package using UV."""
|
|
102
|
+
# Handle both contexts:
|
|
103
|
+
# 1. Direct install: install_base = /install, need to add package name
|
|
104
|
+
# 2. Build task: install_base = /install/package_name, already included
|
|
105
|
+
|
|
106
|
+
if not merge_install:
|
|
107
|
+
# Check if install_base already ends with the package name
|
|
108
|
+
if install_base.name != project.name:
|
|
109
|
+
install_base /= project.name
|
|
110
|
+
|
|
111
|
+
# Create the install directory first
|
|
112
|
+
install_base.mkdir(parents=True, exist_ok=True)
|
|
113
|
+
|
|
114
|
+
# Venv path - this should be /install/PACKAGE_NAME/venv/
|
|
115
|
+
venv_path = install_base / "venv"
|
|
116
|
+
|
|
117
|
+
# Create virtual environment at the target location with system packages access
|
|
118
|
+
# --system-site-packages is needed because ROS 2 packages like rclpy are installed
|
|
119
|
+
# system-wide (not available on PyPI) and our nodes need access to them
|
|
120
|
+
subprocess.run(["uv", "venv", "--system-site-packages", str(venv_path)], check=True)
|
|
121
|
+
|
|
122
|
+
# Install dependencies and the package itself to the target venv
|
|
123
|
+
# Use --python to specify the target venv's python
|
|
124
|
+
python_exe = venv_path / "bin" / "python"
|
|
125
|
+
subprocess.run(
|
|
126
|
+
["uv", "pip", "install", "--python", str(python_exe), "-e", str(project.path)],
|
|
127
|
+
check=True,
|
|
128
|
+
)
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
def install_dependencies_from_descriptor(
|
|
132
|
+
pkg_descriptor, install_base: Path, merge_install: bool
|
|
133
|
+
):
|
|
134
|
+
"""Install dependencies from a PackageDescriptor object.
|
|
135
|
+
|
|
136
|
+
This is a convenience function for use by colcon build tasks.
|
|
137
|
+
"""
|
|
138
|
+
try:
|
|
139
|
+
uv_package = UvPackage(pkg_descriptor.path)
|
|
140
|
+
install_dependencies(uv_package, install_base, merge_install)
|
|
141
|
+
except NotAUvPackageError as e:
|
|
142
|
+
# Skip packages that aren't UV packages
|
|
143
|
+
logger.debug(f"Skipping non-UV package {pkg_descriptor.name}: {e}")
|
|
144
|
+
return
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
def _parse_args() -> argparse.Namespace:
|
|
148
|
+
"""Parse command line arguments."""
|
|
149
|
+
parser = argparse.ArgumentParser(
|
|
150
|
+
description="Searches for UV packages and installs their dependencies "
|
|
151
|
+
"to a configurable install base"
|
|
152
|
+
)
|
|
153
|
+
|
|
154
|
+
parser.add_argument(
|
|
155
|
+
"--base-paths",
|
|
156
|
+
nargs="+",
|
|
157
|
+
type=Path,
|
|
158
|
+
default=[Path.cwd()],
|
|
159
|
+
help="The paths to start looking for UV projects in. Defaults to the "
|
|
160
|
+
"current directory.",
|
|
161
|
+
)
|
|
162
|
+
|
|
163
|
+
parser.add_argument(
|
|
164
|
+
"--install-base",
|
|
165
|
+
type=Path,
|
|
166
|
+
default=Path("install"),
|
|
167
|
+
help="The base path for all install prefixes (default: install)",
|
|
168
|
+
)
|
|
169
|
+
|
|
170
|
+
parser.add_argument(
|
|
171
|
+
"--merge-install",
|
|
172
|
+
action="store_true",
|
|
173
|
+
help="Merge all install prefixes into a single location",
|
|
174
|
+
)
|
|
175
|
+
|
|
176
|
+
parser.add_argument(
|
|
177
|
+
"-v",
|
|
178
|
+
"--verbose",
|
|
179
|
+
action="store_true",
|
|
180
|
+
help="If provided, debug logs will be printed",
|
|
181
|
+
)
|
|
182
|
+
|
|
183
|
+
return parser.parse_args()
|
|
184
|
+
|
|
185
|
+
|
|
186
|
+
if __name__ == "__main__":
|
|
187
|
+
main()
|