1#!/usr/bin/env python3
2import argparse
3import json
4import os
5from pathlib import Path
6import re
7import shlex
8import subprocess
9
10
11parser = argparse.ArgumentParser()
12parser.add_argument("--platform", choices=["windows", "linux"], default="windows")
13parser.add_argument("--arch", choices=["x86", "x86_64", "aarch64"])
14parser.add_argument("--output", type=Path, default=Path(__file__).parent / "build")
15args = parser.parse_args()
16source = Path(__file__).resolve().parent
17args.output.mkdir(parents=True, exist_ok=True)
18schema = json.loads((source / "protocol.json").read_text())
19header = ["#ifndef SG_PROTOCOL_H", "#define SG_PROTOCOL_H"]
20header += [f"#define SG_MAX_{key.upper()} {value}u" for key, value in schema["limits"].items()]
21header += [f"#define SG_XOR_{key.upper()} {value}u" for key, value in schema["xor_bits"].items()]
22for 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"])]
25header += [f"#define SG_WINDOW_WORDS {len(schema['window_fields'])}u"]
26header += [f"#define SG_WINDOW_{field.upper()} {index}u" for index, field in enumerate(schema["window_fields"])]
27header += [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")
29if 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)
40args.arch = args.arch or "x86_64"
41subsystem = "10.0" if args.arch == "aarch64" else "6.1"
42environment = subprocess.check_output(["zig", "env"], text=True)
43match = re.search(r'\.lib_dir\s*=\s*"([^"]+)"', environment)
44library = Path(match[1] if match else json.loads(environment)["lib_dir"])
45subprocess.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)