#!/usr/bin/env python3
"""
Builds a metadata file for a rust binary.
"""
import argparse
import os
import subprocess
import json
import sys
from enum import Enum, EnumMeta
from typing import Any, Dict, List, Union
# A slightly more Rust-y feeling enum
# Thanks to: https://stackoverflow.com/a/65225753
class MetaEnum(EnumMeta):
def __contains__(self: type[Any], member: object) -> bool:
try:
self(member)
except ValueError:
return False
return True
class BaseEnum(Enum, metaclass=MetaEnum):
pass
class PlatformArchitecture(BaseEnum):
Aarch64 = "aarch64"
X86_64 = "x86_64"
class PlatformOS(BaseEnum):
Darwin = "darwin"
Linux = "linux"
VARIANT = "binary"
def parse_args() -> argparse.Namespace:
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument(
"--binary",
required=True,
help="Path to the binary to compute the b3sum",
)
parser.add_argument(
"--git-info-json",
required=True,
help="Path to the Git metadata JSON file",
)
parser.add_argument(
"--build-metadata-out-file",
required=True,
help="Path to write the build metadata JSON file",
)
parser.add_argument(
"--name",
required=True,
help="Name of binary to build",
)
parser.add_argument(
"--author",
required=True,
help="Author to be used in binary metadata",
)
parser.add_argument(
"--source-url",
required=True,
help="Source code URL to be used in binary metadata",
)
parser.add_argument(
"--license",
required=True,
help="Image license to be used in binary metadata",
)
return parser.parse_args()
def main() -> int:
args = parse_args()
git_info = load_git_info(args.git_info_json)
architecture = detect_architecture()
os = detect_os()
b3sum = compute_b3sum(args.binary)
build_metadata = compute_build_metadata(
git_info,
args.name,
architecture,
os,
b3sum,
)
write_json(args.build_metadata_out_file, build_metadata)
return 0
def write_json(output: str, metadata: Union[Dict[str, str], List[str]]):
with open(output, "w") as file:
json.dump(metadata, file, sort_keys=True)
# Possible machine architecture detection comes from reading the Rustup shell
# script installer--thank you for your service!
# See: https://github.com/rust-lang/rustup/blob/master/rustup-init.sh
def detect_architecture() -> PlatformArchitecture:
machine = os.uname().machine
if (machine == "amd64" or machine == "x86_64" or machine == "x86-64"
or machine == "x64"):
return PlatformArchitecture.X86_64
elif (machine == "arm64" or machine == "aarch64" or machine == "arm64v8"):
return PlatformArchitecture.Aarch64
else:
print(
f"xxx Failed to determine architecure or unsupported: {machine}",
file=sys.stderr,
)
sys.exit(1)
# Possible machine operating system detection comes from reading the Rustup shell
# script installer--thank you for your service!
# See: https://github.com/rust-lang/rustup/blob/master/rustup-init.sh
def detect_os() -> PlatformOS:
platform_os = os.uname().sysname
if (platform_os == "Darwin"):
return PlatformOS.Darwin
elif (platform_os == "Linux"):
return PlatformOS.Linux
else:
print(
f"xxx Failed to determine operating system or unsupported: {platform_os}",
file=sys.stderr,
)
sys.exit(1)
def load_git_info(git_info_file: str) -> Dict[str, str | int | bool]:
with open(git_info_file) as file:
return json.load(file)
def compute_binary_metadata(
git_info: Dict[str, str | int | bool],
name: str,
author: str,
source_url: str,
license: str,
architecture: PlatformArchitecture,
platform_os: PlatformOS,
) -> Dict[str, str]:
metadata = {
"name": name,
"version": git_info.get("canonical_version"),
"author": author,
"source_url": source_url,
"license": license,
"architecture": architecture.value,
"os": platform_os.value,
"commit": git_info.get("commit_hash"),
"branch": git_info.get("branch"),
}
return metadata
def compute_b3sum(artifact_file: str) -> str:
cmd = [
"b3sum",
"--no-names",
artifact_file,
]
result = subprocess.run(cmd, capture_output=True)
# Print out stderr from process if it failed
if result.returncode != 0:
sys.stderr.write(result.stderr.decode("ascii"))
result.check_returncode()
b3sum = result.stdout.decode("ascii").rstrip()
return b3sum
def compute_build_metadata(
git_info: Dict[str, str | int | bool],
family: str,
platform_arch: PlatformArchitecture,
platform_os: PlatformOS,
b3sum: str,
) -> Dict[str, str]:
metadata = {
"family": family,
"variant": VARIANT,
"version": git_info.get("canonical_version"),
"arch": platform_arch.value,
"os": platform_os.value,
"commit": git_info.get("commit_hash"),
"branch": git_info.get("branch"),
"b3sum": b3sum,
}
return metadata
if __name__ == "__main__":
sys.exit(main())