#!/usr/bin/env python
import functools
import os
import platform
import subprocess
import sys
from pathlib
import Path
import buildconfig
from mach.util
import get_state_dir
from mozbuild.vendor.moz_yaml
import load_moz_yaml
TOPSRCDIR = Path(buildconfig.topsrcdir)
COMPONENT_DIR = Path(__file__).parent.parent
def protoc_binary(revision:
None | str =
None) -> Path:
if not revision:
revision = _get_protobuf_revision_from_moz_yaml()
if not _get_protoc_dir(revision).exists():
_download_protoc_binary(revision)
return (
_get_protoc_dir(revision)
/
"bin"
/ (f
"protoc{'.exe' if platform.system() == 'Windows' else ''}")
)
@functools.cache
def _get_protobuf_revision_from_moz_yaml():
return load_moz_yaml(COMPONENT_DIR /
"moz.yaml", verify=
False, require_license_file=
False)[
"origin"
][
"revision"]
def _get_protoc_dir(revision):
return Path(get_state_dir()) /
"protobuf" / f
"protoc-{revision}"
def _download_protoc_binary(revision):
import requests
from tempfile
import TemporaryFile
from zipfile
import ZipFile, ZipInfo
with TemporaryFile()
as temp:
with requests.get(
"https://github.com/protocolbuffers/protobuf/releases/download/"
f
"{revision}/protoc-{revision[1:]}-{_get_protoc_platform_ident()}.zip",
stream=
True,
)
as r:
r.raise_for_status()
for chunk
in r.iter_content(chunk_size=
8192):
temp.write(chunk)
class ZipFileWithPermissions(ZipFile):
"""Custom ZipFile class handling file permissions."""
def _extract_member(self, member, targetpath, pwd):
if not isinstance(member, ZipInfo):
member = self.getinfo(member)
targetpath = ZipFile._extract_member(self, member, targetpath, pwd)
attr = member.external_attr >>
16
if attr !=
0:
os.chmod(targetpath, attr)
return targetpath
with ZipFileWithPermissions(temp)
as protoc_bin_release:
protoc_bin_release.extractall(_get_protoc_dir(revision))
def _get_protoc_platform_ident():
system = platform.system()
machine = platform.machine()
if system ==
"Darwin":
return "osx-universal_binary"
elif system ==
"Linux":
if machine ==
"x86_64":
return "linux-x86_64"
elif machine ==
"i686":
return "linux-x86_32"
elif system ==
"Windows":
if machine ==
"AMD64":
return "win64"
elif machine ==
"i686":
return "win32"
raise Exception(
"Unsupported system")
if __name__ ==
"__main__":
args = sys.argv[
1:]
# The protoc compiler has support to generate rust files but it's currently
# highly experimental.
#
# This is a workaround for now
if "--rust_out" in args:
env = os.environ.copy()
env[
"PROTOC"] = protoc_binary()
env[
"TOPSRCDIR"] = buildconfig.topsrcdir
subprocess.run(
[
"cargo",
"run",
"--locked",
"--quiet",
"--",
*args,
],
cwd=Path(__file__).parent,
env=env,
check=
True,
)
else:
subprocess.run([protoc_binary(), *args], check=
True)