OXYGENHUB
# c1a24672dbd5 oxygenflow / images / rust / recipe.py
2.3 KiB · Python · last changed The build image prebuilds a workspace's dependencies 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 local
5
package's Cargo.toml (and the workspace roots its path dependencies
6
inherit from), the lock file, real build scripts, and an empty file for
7
each other target. Building it compiles exactly the dependencies the real
8
workspace uses, with the same features, so the build image can hold them
9
prebuilt: jobs copy that target directory in and compile only their own
10
crates.
11
12
    recipe.py WORKSPACE OUT
13
"""
14
15
import json
16
import os
17
import shutil
18
import subprocess
19
import sys
20
21
22
def main():
23
    ws, out = os.path.abspath(sys.argv[1]), os.path.abspath(sys.argv[2])
24
    meta = json.loads(
25
        subprocess.run(
26
            ["cargo", "metadata", "--format-version", "1", "--locked", "--manifest-path", os.path.join(ws, "Cargo.toml")],
27
            check=True,
28
            capture_output=True,
29
        ).stdout
30
    )
31
    root = meta["workspace_root"]
32
    if os.path.exists(out):
33
        shutil.rmtree(out)
34
35
    def put(path, data=None):
36
        rel = os.path.relpath(path, root)
37
        if rel.startswith(".."):
38
            sys.exit(f"{path} is outside the workspace; a recipe holds only paths under it")
39
        dest = os.path.join(out, rel)
40
        os.makedirs(os.path.dirname(dest), exist_ok=True)
41
        if data is None:
42
            shutil.copy2(path, dest)
43
        else:
44
            with open(dest, "w") as f:
45
                f.write(data)
46
47
    for name in ("Cargo.toml", "Cargo.lock"):
48
        put(os.path.join(root, name))
49
    for p in meta["packages"]:
50
        if p["source"] is not None:
51
            continue
52
        manifest = p["manifest_path"]
53
        put(manifest)
54
        # Workspace roots above a path dependency, for `workspace = true`.
55
        d = os.path.dirname(os.path.dirname(manifest))
56
        while d.startswith(root) and d != root:
57
            top = os.path.join(d, "Cargo.toml")
58
            if os.path.exists(top):
59
                put(top)
60
            d = os.path.dirname(d)
61
        for t in p["targets"]:
62
            kind = t["kind"][0]
63
            src = t["src_path"]
64
            if kind == "custom-build":
65
                put(src)
66
            elif kind in ("lib", "rlib", "proc-macro"):
67
                put(src, "")
68
            else:
69
                put(src, "fn main() {}\n")
70
71
72
if __name__ == "__main__":
73
    main()