Skip to content

Commit e2ad936

Browse files
Merge branch 'main' into sai/template-test-runner
2 parents 987f225 + 6165f02 commit e2ad936

File tree

12 files changed

+491
-3
lines changed

12 files changed

+491
-3
lines changed

.buildkite/go.rules.test.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44
# Go module directories get their module tag
55
wanda/main.go: wanda
66
wanda/cmd/wanda/main.go: wanda
7+
wheels/raymake/build_wheels.py: wanda
8+
wheels/raymake/pyproject.toml: wanda
79
raycicmd/main.go: raycicmd
810
raycicmd/config.go: raycicmd
911
raycicmd/test_rules_cmd.go: raycicmd

.buildkite/go.rules.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
! wanda raycicmd raycirun reefd
1717

1818
wanda/
19+
wheels/raymake/
1920
@ wanda
2021
;
2122

.buildkite/release.rayci.yaml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ steps:
99
- raycicmd
1010
instance_type: release
1111
commands:
12+
- pip install uv
13+
- export PATH="$(python -m site --user-base)/bin:$PATH"
1214
- bash release.sh
1315
- cp _release/* /artifact-mount
1416
depends_on: forge

.buildkite/tests.rayci.yaml

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,20 @@ steps:
4444
depends_on:
4545
- python-base
4646
- forge
47+
- label: Wanda wheel tests
48+
tags: wanda
49+
command:
50+
- pip install uv
51+
- export PATH="$(python -m site --user-base)/bin:$PATH"
52+
- cd wheels/raymake
53+
- uv run build_wheels.py --platform linux-amd64 --output-dir dist
54+
- uv run --with pytest pytest build_wheels_test.py -v
55+
# Smoke test: install wheel and verify binary runs
56+
- pip install dist/*.whl
57+
- raymake --help
58+
queue: small
59+
depends_on: forge
60+
4761
- label: "Failing test"
4862
tags: disabled
4963
command: exit 1

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,3 +5,5 @@
55

66
__pycache__
77
.hypothesis
8+
9+
wheels/raymake/VERSION

release.sh

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,10 @@
22

33
set -euxo pipefail
44

5-
mkdir -p _release
6-
rm -f _release/*
5+
OUTPUT_DIR="_release"
6+
7+
rm -rf "$OUTPUT_DIR"
8+
mkdir -p "$OUTPUT_DIR"
79

810
build_go() {
911
local name="$1"
@@ -12,7 +14,11 @@ build_go() {
1214
local arch="$4"
1315

1416
GOOS="$os" GOARCH="$arch" \
15-
go build -trimpath -o "_release/${name}-${os}-${arch}" "$pkg"
17+
go build -trimpath -o "$OUTPUT_DIR/${name}-${os}-${arch}" "$pkg"
18+
}
19+
20+
build_wheels() {
21+
uv run wheels/raymake/build_wheels.py --output-dir "$OUTPUT_DIR"
1622
}
1723

1824
build_goqualgate() { build_go goqualgate ./goqualgate/goqualgate "$1" "$2"; }
@@ -32,3 +38,5 @@ build_wanda darwin arm64
3238
build_wanda linux amd64
3339
build_wanda linux arm64
3440
build_wanda windows amd64
41+
42+
build_wheels

wheels/raymake/README.md

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
# raymake
2+
3+
Container image builder that uses a container registry as a content-addressed
4+
build cache. Used for projects within https://github.com/ray-project
5+
6+
## Why raymake?
7+
8+
- **Content-addressed caching** - Computes a digest from source files, base
9+
image digests, and build args, skipping builds entirely on cache hit.
10+
- **Declarative spec files** - One `.wanda.yaml` declares all inputs. Share
11+
Dockerfiles across specs with different `build_args`. Supports `$VAR`
12+
expansion and `-env_file`.
13+
- **Explicit build context** - Only files in `srcs` are sent to Docker.
14+
Dependencies automatically discovered and built in topological order.
15+
16+
## Installation
17+
18+
```bash
19+
uv tool install raymake
20+
21+
# Or with pip
22+
pip install raymake
23+
```
24+
25+
## Usage
26+
27+
```bash
28+
# Build from a spec file
29+
raymake spec.wanda.yaml
30+
```
31+
32+
## Distribution
33+
34+
Distributed as a pre-compiled Go binary via the wheel `scripts` data directory
35+
([PEP 427]). The install location is determined by Python's [sysconfig]:
36+
37+
- `~/.local/bin/raymake` (Linux/macOS with `uv tool install`)
38+
- `{venv}/bin/raymake` (virtual environment)
39+
- `C:\Users\{user}\AppData\Local\Programs\Python\Scripts\raymake.exe` (Windows)
40+
41+
## Links
42+
43+
- [RayCI Repository]
44+
- [Wanda Source]
45+
46+
[PEP 427]: https://peps.python.org/pep-0427/
47+
[sysconfig]: https://docs.python.org/3/library/sysconfig.html#installation-paths
48+
[RayCI Repository]: https://github.com/ray-project/rayci
49+
[Wanda Source]: https://github.com/ray-project/rayci/tree/main/wanda

wheels/raymake/build_wheels.py

Lines changed: 158 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,158 @@
1+
#!/usr/bin/env python3
2+
# /// script
3+
# requires-python = ">=3.9"
4+
# ///
5+
"""Build raymake wheels for all supported platforms.
6+
7+
Usage:
8+
# Build all platforms (default)
9+
uv run pypi/raymake/build_wheels.py
10+
11+
# Build specific platform
12+
uv run pypi/raymake/build_wheels.py --platform darwin-arm64
13+
"""
14+
15+
import argparse
16+
import os
17+
import shutil
18+
import subprocess
19+
from pathlib import Path
20+
21+
22+
def get_version_from_git() -> str:
23+
"""Extract version from git tag (e.g., v0.27.0 -> 0.27.0)."""
24+
try:
25+
result = subprocess.run(
26+
["git", "describe", "--tags", "--abbrev=0"],
27+
capture_output=True,
28+
text=True,
29+
check=True,
30+
)
31+
version = result.stdout.strip().lstrip("v")
32+
return version
33+
except subprocess.CalledProcessError:
34+
return "0.0.0"
35+
36+
37+
def write_version_file(script_dir: Path) -> str:
38+
"""Generate VERSION file from git tag."""
39+
version = get_version_from_git()
40+
version_file = script_dir / "VERSION"
41+
version_file.write_text(f'__version__ = "{version}"\n')
42+
print(f"Generated VERSION file: {version}")
43+
return version
44+
45+
46+
PLATFORM_MAP = {
47+
"darwin-arm64": {
48+
"goos": "darwin",
49+
"goarch": "arm64",
50+
"platform": "macosx_12_0_arm64",
51+
},
52+
"linux-amd64": {
53+
"goos": "linux",
54+
"goarch": "amd64",
55+
"platform": "manylinux_2_17_x86_64",
56+
},
57+
"linux-arm64": {
58+
"goos": "linux",
59+
"goarch": "arm64",
60+
"platform": "manylinux_2_17_aarch64",
61+
},
62+
}
63+
64+
65+
def build_wheel(platform_key: str, output_dir: Path) -> Path:
66+
"""Build a wheel for the specified platform."""
67+
if platform_key not in PLATFORM_MAP:
68+
raise ValueError(
69+
f"Unknown platform: {platform_key}. Valid: {list(PLATFORM_MAP.keys())}"
70+
)
71+
72+
config = PLATFORM_MAP[platform_key]
73+
goos = config["goos"]
74+
goarch = config["goarch"]
75+
platform_tag = config["platform"]
76+
77+
print(f"\n{'=' * 60}")
78+
print(f"Building wheel for {platform_key}")
79+
print(f" GOOS={goos} GOARCH={goarch}")
80+
print(f" Platform tag: {platform_tag}")
81+
print(f"{'=' * 60}\n")
82+
83+
# Set environment for cross-compilation
84+
env = os.environ.copy()
85+
env["GOOS"] = goos
86+
env["GOARCH"] = goarch
87+
env["CGO_ENABLED"] = "0"
88+
89+
# Get the pypi/raymake directory
90+
script_dir = Path(__file__).parent
91+
dist_dir = script_dir / "dist"
92+
93+
# Clean dist directory for this build
94+
if dist_dir.exists():
95+
shutil.rmtree(dist_dir)
96+
97+
# Build the wheel
98+
args = ["uv", "build", "--wheel", f"--config-setting=--plat-name={platform_tag}"]
99+
subprocess.run(args, check=True, cwd=script_dir, env=env)
100+
101+
# Find the built wheel
102+
wheels = list(dist_dir.glob("*.whl"))
103+
if len(wheels) != 1:
104+
raise RuntimeError(f"Expected 1 wheel in {dist_dir}, but found {len(wheels)}")
105+
wheel_path = wheels[0]
106+
107+
# Copy to output directory (skip if same location)
108+
output_dir.mkdir(parents=True, exist_ok=True)
109+
final_path = output_dir / wheel_path.name
110+
if wheel_path.resolve() != final_path.resolve():
111+
shutil.copy2(wheel_path, final_path)
112+
print(f"Created: {final_path}")
113+
114+
return final_path
115+
116+
117+
def main():
118+
parser = argparse.ArgumentParser(description="Build raymake wheels")
119+
parser.add_argument(
120+
"--platform",
121+
choices=list(PLATFORM_MAP.keys()) + ["all"],
122+
default="all",
123+
help="Platform to build (default: all)",
124+
)
125+
parser.add_argument(
126+
"--output-dir",
127+
type=Path,
128+
default=Path(__file__).parent.parent.parent / "_release",
129+
help="Output directory for wheels (default: _release/)",
130+
)
131+
args = parser.parse_args()
132+
133+
if args.platform == "all":
134+
platforms = list(PLATFORM_MAP.keys())
135+
else:
136+
platforms = [args.platform]
137+
138+
# Generate VERSION file from git tag
139+
script_dir = Path(__file__).parent
140+
write_version_file(script_dir)
141+
142+
print(f"Building wheels for: {', '.join(platforms)}")
143+
print(f"Output directory: {args.output_dir}")
144+
145+
built_wheels = []
146+
for platform in platforms:
147+
wheel_path = build_wheel(platform, args.output_dir)
148+
built_wheels.append(wheel_path)
149+
150+
print(f"\n{'=' * 60}")
151+
print("Build complete! Created wheels:")
152+
for wheel in built_wheels:
153+
print(f" {wheel}")
154+
print(f"{'=' * 60}\n")
155+
156+
157+
if __name__ == "__main__":
158+
main()

0 commit comments

Comments
 (0)