Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion prelude/python/tools/BUCK
Original file line number Diff line number Diff line change
Expand Up @@ -120,9 +120,12 @@ prelude.python_bootstrap_binary(
visibility = ["PUBLIC"],
)

prelude.python_bootstrap_binary(
prelude.python_binary(
name = "wheel",
main = "wheel.py",
deps = [
"//third-party/pypi/packaging:packaging",
],
visibility = ["PUBLIC"],
)

Expand Down
17 changes: 11 additions & 6 deletions prelude/python/tools/wheel.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@
from types import TracebackType
from typing import cast, Dict, List, Optional, Set, Tuple, Type

from packaging.version import Version as PEP440Version


def normalize_name(name: str) -> str:
"""
Expand All @@ -35,6 +37,13 @@ def normalize_name(name: str) -> str:
return pep503_normalized_name.replace("-", "_")


from packaging.version import Version as PEP440Version


def normalize_version(version: str) -> str:
return str(PEP440Version(version))


# pyre-fixme[24]: Generic type `AbstractContextManager` expects 1 type parameter.
class WheelBuilder(contextlib.AbstractContextManager):
def __init__(
Expand All @@ -50,17 +59,13 @@ def __init__(

self._normalized_name: str = normalize_name(name)

# TODO normalize version like we normalized name above
# can follow pypi/packaging.utils.canonicalize_version (see: https://fburl.com/code/amuvl3d2)
# punted for later since it was not a clean copy/paste and
# taking a dep to tp from toolchains is not straightforward
self._version = version
self._version = normalize_version(version)
self._record: list[str] = []
self._outf = zipfile.ZipFile(output, mode="w")
self._entry_points: Optional[Dict[str, str]] = entry_points
self._metadata: List[Tuple[str, str]] = []
self._metadata.append(("Name", name))
self._metadata.append(("Version", version))
self._metadata.append(("Version", self._version))
if metadata is not None:
self._metadata.extend(metadata)

Expand Down
7 changes: 7 additions & 0 deletions third-party/pypi/packaging/BUCK
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
load("@prelude//python:python.bzl", "python_library")

python_library(
name = "packaging",
srcs = glob(["**/*.py"]),
visibility = ["PUBLIC"],
)
15 changes: 15 additions & 0 deletions third-party/pypi/packaging/packaging/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# This file is dual licensed under the terms of the Apache License, Version
# 2.0, and the BSD License. See the LICENSE file in the root of this repository
# for complete details.

__title__ = "packaging"
__summary__ = "Core utilities for Python packages"
__uri__ = "https://github.com/pypa/packaging"

__version__ = "25.1.dev0"

__author__ = "Donald Stufft and individual contributors"
__email__ = "[email protected]"

__license__ = "BSD-2-Clause or Apache-2.0"
__copyright__ = f"2014 {__author__}"
109 changes: 109 additions & 0 deletions third-party/pypi/packaging/packaging/_elffile.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
"""
ELF file parser.
This provides a class ``ELFFile`` that parses an ELF executable in a similar
interface to ``ZipFile``. Only the read interface is implemented.
Based on: https://gist.github.com/lyssdod/f51579ae8d93c8657a5564aefc2ffbca
ELF header: https://refspecs.linuxfoundation.org/elf/gabi4+/ch4.eheader.html
"""

from __future__ import annotations

import enum
import os
import struct
from typing import IO


class ELFInvalid(ValueError):
pass


class EIClass(enum.IntEnum):
C32 = 1
C64 = 2


class EIData(enum.IntEnum):
Lsb = 1
Msb = 2


class EMachine(enum.IntEnum):
I386 = 3
S390 = 22
Arm = 40
X8664 = 62
AArc64 = 183


class ELFFile:
"""
Representation of an ELF executable.
"""

def __init__(self, f: IO[bytes]) -> None:
self._f = f

try:
ident = self._read("16B")
except struct.error as e:
raise ELFInvalid("unable to parse identification") from e
magic = bytes(ident[:4])
if magic != b"\x7fELF":
raise ELFInvalid(f"invalid magic: {magic!r}")

self.capacity = ident[4] # Format for program header (bitness).
self.encoding = ident[5] # Data structure encoding (endianness).

try:
# e_fmt: Format for program header.
# p_fmt: Format for section header.
# p_idx: Indexes to find p_type, p_offset, and p_filesz.
e_fmt, self._p_fmt, self._p_idx = {
(1, 1): ("<HHIIIIIHHH", "<IIIIIIII", (0, 1, 4)), # 32-bit LSB.
(1, 2): (">HHIIIIIHHH", ">IIIIIIII", (0, 1, 4)), # 32-bit MSB.
(2, 1): ("<HHIQQQIHHH", "<IIQQQQQQ", (0, 2, 5)), # 64-bit LSB.
(2, 2): (">HHIQQQIHHH", ">IIQQQQQQ", (0, 2, 5)), # 64-bit MSB.
}[(self.capacity, self.encoding)]
except KeyError as e:
raise ELFInvalid(
f"unrecognized capacity ({self.capacity}) or encoding ({self.encoding})"
) from e

try:
(
_,
self.machine, # Architecture type.
_,
_,
self._e_phoff, # Offset of program header.
_,
self.flags, # Processor-specific flags.
_,
self._e_phentsize, # Size of section.
self._e_phnum, # Number of sections.
) = self._read(e_fmt)
except struct.error as e:
raise ELFInvalid("unable to parse machine and section information") from e

def _read(self, fmt: str) -> tuple[int, ...]:
return struct.unpack(fmt, self._f.read(struct.calcsize(fmt)))

@property
def interpreter(self) -> str | None:
"""
The path recorded in the ``PT_INTERP`` section header.
"""
for index in range(self._e_phnum):
self._f.seek(self._e_phoff + self._e_phentsize * index)
try:
data = self._read(self._p_fmt)
except struct.error:
continue
if data[self._p_idx[0]] != 3: # Not PT_INTERP.
continue
self._f.seek(data[self._p_idx[1]])
return os.fsdecode(self._f.read(data[self._p_idx[2]])).strip("\0")
return None
Loading