OXYGENHUB
# 0d663e91c598 oxygenflow / images / rust / recipe.py
3.1 KiB · Python · last changed Build image: coverage tools; recipe copies any vendor/ 1 month ago source blame history raw
1
#!/usr/bin/env python3
2
"""Write a dependency recipe of a Cargo workspace (backlog F6b2).
3
4
The recipe is the workspace with its sources stubbed out: every member's
5
Cargo.toml (and the workspace roots its path dependencies inherit from),
6
the lock file, real build scripts, and an empty file for each other
7
target. A third-party crate patched in from vendor/ is copied whole:
8
registry crates with real code depend on it. Building it compiles exactly the dependencies the real
9
workspace uses, with the same features, so the build image can hold them
10
prebuilt: jobs copy that target directory in and compile only their own
11
crates.
12
13
    recipe.py WORKSPACE OUT
14
"""
15
16
import json
17
import os
18
import shutil
19
import subprocess
20
import sys
21
22
23
def main():
24
    ws, out = os.path.abspath(sys.argv[1]), os.path.abspath(sys.argv[2])
25
    meta = json.loads(
26
        subprocess.run(
27
            ["cargo", "metadata", "--format-version", "1", "--locked", "--manifest-path", os.path.join(ws, "Cargo.toml")],
28
            check=True,
29
            capture_output=True,
30
        ).stdout
31
    )
32
    root = meta["workspace_root"]
33
    if os.path.exists(out):
34
        shutil.rmtree(out)
35
36
    def put(path, data=None):
37
        rel = os.path.relpath(path, root)
38
        if rel.startswith(".."):
39
            sys.exit(f"{path} is outside the workspace; a recipe holds only paths under it")
40
        dest = os.path.join(out, rel)
41
        os.makedirs(os.path.dirname(dest), exist_ok=True)
42
        if data is None:
43
            shutil.copy2(path, dest)
44
        else:
45
            with open(dest, "w") as f:
46
                f.write(data)
47
48
    for name in ("Cargo.toml", "Cargo.lock"):
49
        put(os.path.join(root, name))
50
    members = set(meta["workspace_members"])
51
    for p in meta["packages"]:
52
        if p["source"] is not None:
53
            continue
54
        manifest = p["manifest_path"]
55
        src_dir = os.path.dirname(manifest)
56
        rel = os.path.relpath(src_dir, root)
57
        if p["id"] not in members and "vendor" in rel.split(os.sep):
58
            # A third-party crate patched in from a vendor/ directory (the
59
            # workspace's own, or a pinned subrepo's, as oxygenhub patches
60
            # from deps/…/oxygen/vendor/): registry crates
61
            # with real code depend on it, so it goes in whole. (A pinned
62
            # subrepo's crates under deps/ are stubbed like the members:
63
            # only stubbed code depends on them.)
64
            shutil.copytree(src_dir, os.path.join(out, rel), dirs_exist_ok=True)
65
            continue
66
        put(manifest)
67
        # Workspace roots above a path dependency, for `workspace = true`.
68
        d = os.path.dirname(os.path.dirname(manifest))
69
        while d.startswith(root) and d != root:
70
            top = os.path.join(d, "Cargo.toml")
71
            if os.path.exists(top):
72
                put(top)
73
            d = os.path.dirname(d)
74
        for t in p["targets"]:
75
            kind = t["kind"][0]
76
            src = t["src_path"]
77
            if kind == "custom-build":
78
                put(src)
79
            elif kind in ("lib", "rlib", "proc-macro"):
80
                put(src, "")
81
            else:
82
                put(src, "fn main() {}\n")
83
84
85
if __name__ == "__main__":
86
    main()