| 1 | #!/usr/bin/env python3 |
| 2 | import argparse |
| 3 | import json |
| 4 | import os |
| 5 | from pathlib import Path |
| 6 | import re |
| 7 | import shlex |
| 8 | import subprocess |
| 9 | |
| 10 | |
| 11 | parser = argparse.ArgumentParser() |
| 12 | parser.add_argument("--platform", choices=["windows", "linux"], default="windows") |
| 13 | parser.add_argument("--arch", choices=["x86", "x86_64", "aarch64"]) |
| 14 | parser.add_argument("--output", type=Path, default=Path(__file__).parent / "build") |
| 15 | args = parser.parse_args() |
| 16 | source = Path(__file__).resolve().parent |
| 17 | args.output.mkdir(parents=True, exist_ok=True) |
| 18 | schema = json.loads((source / "protocol.json").read_text()) |
| 19 | header = ["#ifndef SG_PROTOCOL_H", "#define SG_PROTOCOL_H"] |
| 20 | header += [f"#define SG_MAX_{key.upper()} {value}u" for key, value in schema["limits"].items()] |
| 21 | header += [f"#define SG_XOR_{key.upper()} {value}u" for key, value in schema["xor_bits"].items()] |
| 22 | for name, packet in schema["packets"].items(): |
| 23 | header += [f"#define SG_{name.upper()} {packet['id']}u", f"#define SG_{name.upper()}_WORDS {len(packet['fields'])}u"] |
| 24 | header += [f"#define SG_{name.upper()}_{field.upper()} {index}u" for index, field in enumerate(packet["fields"])] |
| 25 | header += [f"#define SG_WINDOW_WORDS {len(schema['window_fields'])}u"] |
| 26 | header += [f"#define SG_WINDOW_{field.upper()} {index}u" for index, field in enumerate(schema["window_fields"])] |
| 27 | header += [f'#define SG_MAGIC "{schema["magic"]}"', f'#define SG_CHANNEL_NAME "{schema["channel"]}"', f'#define SG_CHANNEL L"{schema["channel"]}"', "#endif"] |
| 28 | (args.output / "protocol.h").write_text("\n".join(header) + "\n") |
| 29 | if args.platform == "linux": |
| 30 | if args.arch: |
| 31 | parser.error("Linux builds use the native compiler; omit --arch.") |
| 32 | packages = ["gio-unix-2.0", "libpipewire-0.3", "x11", "xfixes", "xext"] |
| 33 | flags = shlex.split(subprocess.check_output(["pkg-config", "--cflags", "--libs", *packages], text=True)) |
| 34 | subprocess.run([ |
| 35 | *shlex.split(os.environ.get("CC", "cc")), "-std=gnu99", "-O2", "-Wall", "-Wextra", "-Werror", |
| 36 | "-I", str(args.output), str(source / "linux.c"), str(source / "cursor.c"), |
| 37 | *flags, "-o", str(args.output / "snowglobe-guest"), |
| 38 | ], check=True) |
| 39 | raise SystemExit(0) |
| 40 | args.arch = args.arch or "x86_64" |
| 41 | subsystem = "10.0" if args.arch == "aarch64" else "6.1" |
| 42 | environment = subprocess.check_output(["zig", "env"], text=True) |
| 43 | match = re.search(r'\.lib_dir\s*=\s*"([^"]+)"', environment) |
| 44 | library = Path(match[1] if match else json.loads(environment)["lib_dir"]) |
| 45 | subprocess.run([ |
| 46 | "zig", "cc", "-target", args.arch + ("-windows.win10-gnu" if args.arch == "aarch64" else "-windows.win7-gnu"), "-std=c99", "-Os", "-Wall", "-Wextra", "-Werror", |
| 47 | "-ffreestanding", "-fno-stack-protector", "-nostdlib", "-D_WIN32_WINNT=0x0601", "-DWINVER=0x0601", |
| 48 | "-isystem", str(library / "libc/include/any-windows-any"), |
| 49 | "-I", str(args.output), str(source / "cursor.c"), str(source / "windows.c"), str(source / "capture.c"), |
| 50 | "-lkernel32", "-luser32", "-lgdi32", "-Wl,--entry,mainCRTStartup", "-Wl,--subsystem,windows", |
| 51 | "-Wl,--major-subsystem-version," + subsystem.split(".")[0], "-Wl,--minor-subsystem-version," + subsystem.split(".")[1], |
| 52 | "-o", str(args.output / ("snowglobe-guest-" + args.arch + ".exe")), |
| 53 | ], check=True) |