Skip to content

Commit 491ea0c

Browse files
authored
Merge pull request #170 from robotpy/semiwrap
Migrate mostrobotpy to semiwrap
2 parents eeb8541 + 5a61a3f commit 491ea0c

File tree

664 files changed

+3226
-12556
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

664 files changed

+3226
-12556
lines changed

.github/workflows/dist.yml

Lines changed: 176 additions & 85 deletions
Large diffs are not rendered by default.

.gitignore

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11

22
*.py[ciod]
33
/dist
4+
/dist-other
5+
/cache
46

57
rpy-include
6-
py.typed
78
.venv/

.readthedocs.yml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,8 @@ build:
99
python: "3.11"
1010
jobs:
1111
post_install:
12-
- python -m robotpy_sphinx.download_and_install robotpy mostrobotpy main dist pypi-Linux-3.11
12+
- python -m robotpy_sphinx.download_and_install robotpy mostrobotpy main dist pypi-other-X64-3.11
13+
- python -m robotpy_sphinx.download_and_install robotpy mostrobotpy main dist pypi-Linux-X64-3.11
1314
post_build:
1415
- mkdir --parents _readthedocs/htmlzip
1516
- cp --recursive _readthedocs/html _readthedocs/$READTHEDOCS_PROJECT

devtools/__main__.py

Lines changed: 31 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import os
2+
import pathlib
23
import sys
34

45
import click
@@ -11,19 +12,24 @@
1112
# Environment variables for configuring the builds
1213
#
1314

14-
# Always run robotpy-build in parallel
15-
os.environ["RPYBUILD_PARALLEL"] = "1"
15+
# cache downloaded files by default
16+
if "HATCH_ROBOTPY_CACHE" not in os.environ:
17+
os.environ["HATCH_ROBOTPY_CACHE"] = str(
18+
(pathlib.Path(__file__).parent.parent / "cache").resolve()
19+
)
20+
1621

1722
# MACOSX_DEPLOYMENT_TARGET is required for linking to WPILib
1823
if sys.platform == "darwin":
1924
os.environ["MACOSX_DEPLOYMENT_TARGET"] = "13.3"
2025

2126

2227
@click.group()
28+
@click.option("-v", "--verbose", default=False, is_flag=True)
2329
@click.pass_context
24-
def main(ctx: click.Context):
30+
def main(ctx: click.Context, verbose: bool):
2531
"""RobotPy development tool"""
26-
ctx.obj = Context()
32+
ctx.obj = Context(verbose)
2733

2834

2935
main.add_command(ci.ci)
@@ -35,7 +41,7 @@ def main(ctx: click.Context):
3541
def info(ctx: Context):
3642
"""Display information"""
3743
for project in ctx.subprojects.values():
38-
print(project.name, project.requires)
44+
print(project.name, project.build_requires)
3945

4046

4147
@main.command()
@@ -55,12 +61,31 @@ def develop(ctx: Context, package: str):
5561
project.develop()
5662

5763

64+
@main.command
65+
@click.argument("package", required=False)
66+
@click.pass_obj
67+
def uninstall(ctx: Context, package: str):
68+
"""Uninstall robotpy packages"""
69+
if package:
70+
for project in ctx.subprojects.values():
71+
if project.name == package:
72+
project.uninstall()
73+
break
74+
else:
75+
raise click.BadParameter(f"invalid package {package}")
76+
else:
77+
for project in ctx.subprojects.values():
78+
project.uninstall()
79+
80+
5881
@main.command()
5982
@click.pass_obj
6083
def update_init(ctx: Context):
6184
"""Update __init__.py in all projects"""
6285
for project in ctx.subprojects.values():
63-
project.update_init()
86+
if project.is_semiwrap_project():
87+
with ctx.handle_exception(f"update-init {project.name}"):
88+
project.update_init()
6489

6590

6691
@main.command()

devtools/ci.py

Lines changed: 59 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,12 @@
22
# CI commands
33
#
44

5+
import pathlib
56
import sys
7+
import typing as T
68

79
import click
810
from packaging.requirements import Requirement
9-
import setuptools_scm
1011

1112
from .ctx import Context
1213
from .update_pyproject import ProjectUpdater
@@ -39,24 +40,68 @@ def check_pyproject(ctx: Context):
3940
@ci.command()
4041
@click.option("--no-test", default=False, is_flag=True)
4142
@click.pass_obj
42-
def run(ctx: Context, no_test: bool):
43+
def build_other_wheels(ctx: Context, no_test: bool):
4344
"""
44-
Builds wheels, runs tests
45+
Builds wheels that don't use meson, runs tests
4546
"""
4647

47-
# Get the current build version
48-
version = setuptools_scm.get_version()
48+
for project in ctx.subprojects.values():
49+
if project.is_meson_project():
50+
continue
51+
52+
with ctx.handle_exception(project.name):
53+
ctx.install_build_deps(subproject=project)
54+
55+
project.build_wheel(
56+
wheel_path=ctx.wheel_path,
57+
other_wheel_path=ctx.other_wheel_path,
58+
install=True,
59+
config_settings=[],
60+
)
61+
if not no_test:
62+
project.test(install_requirements=True)
63+
64+
65+
@ci.command()
66+
@click.option("--no-test", default=False, is_flag=True)
67+
@click.option(
68+
"--cross",
69+
help="meson cross.txt file (installed at ~/.local/share/meson/cross/FILENAME)",
70+
)
71+
@click.pass_obj
72+
def build_meson_wheels(ctx: Context, no_test: bool, cross: T.Optional[str]):
73+
"""
74+
Builds wheels that use meson, runs tests.
75+
76+
Needs wheels that are in the non-meson builds
77+
"""
4978

5079
# Fix build dependencies to be == what we are building
5180
# - install_requires already has this via ==THIS_VERSION in robotpy-build
52-
for project in ctx.subprojects.values():
53-
for i in range(len(project.requires)):
54-
req = project.requires[i]
55-
if req.name in ctx.subprojects:
56-
project.requires[i] = Requirement(f"{req.name}=={version}")
81+
# for project in ctx.subprojects.values():
82+
# for i in range(len(project.build_requires)):
83+
# req = project.build_requires[i]
84+
# if req.name in ctx.subprojects:
85+
# project.build_requires[i] = Requirement(f"{req.name}=={version}")
86+
87+
# Check that the build dependencies match the versions of the projects
88+
# that we're building
89+
90+
config_settings = []
91+
if cross:
92+
config_settings.append(f"setup-args=--cross-file={cross}")
5793

5894
for project in ctx.subprojects.values():
59-
project.install_build_deps(wheel_path=ctx.wheel_path)
60-
project.bdist_wheel(wheel_path=ctx.wheel_path, install=True)
61-
if not no_test:
62-
project.test(install_requirements=True)
95+
if not project.is_meson_project():
96+
continue
97+
98+
with ctx.handle_exception(project.name):
99+
ctx.install_build_deps(subproject=project)
100+
project.build_wheel(
101+
wheel_path=ctx.wheel_path,
102+
other_wheel_path=ctx.other_wheel_path,
103+
install=True,
104+
config_settings=config_settings,
105+
)
106+
if not no_test:
107+
project.test(install_requirements=True)

devtools/config.py

Lines changed: 17 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,38 +1,38 @@
1-
import pydantic
1+
import dataclasses
22
import tomlkit
3-
import typing
3+
import typing as T
44

5+
from .util import parse_input
56

6-
class Model(pydantic.BaseModel):
7-
class Config:
8-
extra = "forbid"
97

10-
11-
class SubprojectConfig(Model):
12-
#: If any managed project has one of these as a dependency, the
13-
#: minimum version should be this
14-
min_version: str
8+
@dataclasses.dataclass
9+
class SubprojectConfig:
10+
#: The key in `py_versions` to set the project version from
11+
py_version: str
1512

1613
#: Whether this should be built on roborio or not
1714
roborio: bool
1815

1916

20-
class Parameters(Model):
17+
@dataclasses.dataclass
18+
class Parameters:
2119
wpilib_bin_version: str
2220
wpilib_bin_url: str
2321

24-
robotpy_build_req: str
22+
exclude_artifacts: T.Set[str]
2523

26-
exclude_artifacts: typing.Set[str]
24+
requirements: T.Dict[str, str]
2725

2826

29-
class UpdateConfig(Model):
27+
@dataclasses.dataclass
28+
class UpdateConfig:
29+
py_versions: T.Dict[str, str]
3030
params: Parameters
31-
subprojects: typing.Dict[str, SubprojectConfig]
31+
subprojects: T.Dict[str, SubprojectConfig]
3232

3333

34-
def load(fname) -> typing.Tuple[UpdateConfig, tomlkit.TOMLDocument]:
34+
def load(fname) -> T.Tuple[UpdateConfig, tomlkit.TOMLDocument]:
3535
with open(fname) as fp:
3636
cfgdata = tomlkit.parse(fp.read())
3737

38-
return UpdateConfig(**cfgdata), cfgdata
38+
return parse_input(cfgdata, UpdateConfig, fname), cfgdata

devtools/ctx.py

Lines changed: 58 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,22 @@
1+
import contextlib
12
import pathlib
23
import subprocess
4+
import sys
35
import sysconfig
46
import typing
57

68
import toposort
79

810
from . import config
911
from .subproject import Subproject
12+
from .util import run_pip
1013

1114

1215
class Context:
1316
"""Global context used by all rdev commands"""
1417

15-
def __init__(self) -> None:
18+
def __init__(self, verbose: bool) -> None:
19+
self.verbose = verbose
1620
self.root_path = pathlib.Path(__file__).parent.parent
1721
self.subprojects_path = self.root_path / "subprojects"
1822
self.cfgpath = self.root_path / "rdev.toml"
@@ -21,6 +25,7 @@ def __init__(self) -> None:
2125
self.is_roborio = sysconfig.get_platform() == "linux-roborio"
2226

2327
self.wheel_path = self.root_path / "dist"
28+
self.other_wheel_path = self.root_path / "dist-other"
2429

2530
subprojects: typing.List[Subproject] = []
2631
for project, cfg in self.cfg.subprojects.items():
@@ -33,7 +38,7 @@ def __init__(self) -> None:
3338
# Create a sorted dictionary of subprojects ordered by build order
3439
si = {p.pyproject_name: i for i, p in enumerate(subprojects)}
3540
ti = {
36-
i: [si[r.name] for r in p.requires if r.name in si]
41+
i: [si[r.name] for r in p.build_requires + p.dependencies if r.name in si]
3742
for i, p in enumerate(subprojects)
3843
}
3944

@@ -61,3 +66,54 @@ def git_is_file_dirty(self, relpath: str) -> bool:
6166
["git", "status", "--porcelain", relpath], cwd=self.root_path
6267
).decode("utf-8")
6368
return output != ""
69+
70+
@contextlib.contextmanager
71+
def handle_exception(self, msg: str):
72+
try:
73+
yield
74+
except Exception as e:
75+
if self.verbose:
76+
raise
77+
78+
print(f"ERROR: {msg}: {e}", file=sys.stderr)
79+
sys.exit(1)
80+
81+
@property
82+
def internal_pyprojects(self):
83+
if not hasattr(self, "_internal_pyprojects"):
84+
self._internal_pyprojects = [
85+
s.pyproject_name for s in self.subprojects.values()
86+
]
87+
return self._internal_pyprojects
88+
89+
def install_build_deps(
90+
self,
91+
*,
92+
subproject: Subproject,
93+
):
94+
# separate requirements into internal and external
95+
internal = []
96+
external = []
97+
98+
for req in subproject.build_requires:
99+
if req.name in self.internal_pyprojects:
100+
internal.append(req)
101+
else:
102+
external.append(req)
103+
104+
if external:
105+
run_pip(
106+
"install",
107+
*[str(req) for req in external],
108+
)
109+
110+
if internal:
111+
run_pip(
112+
"install",
113+
"--no-index",
114+
"--find-links",
115+
str(self.wheel_path),
116+
"--find-links",
117+
str(self.other_wheel_path),
118+
*[str(req) for req in internal],
119+
)

0 commit comments

Comments
 (0)