qbhf2's picture
added NvidiaWarp and GarmentCode repos
66c9c8a
import argparse
import os
import shutil
import pathlib
import platform
from typing import NamedTuple
import setuptools
from wheel.bdist_wheel import bdist_wheel
# Parse --build-option arguments meant for the bdist_wheel command. We have to parse these
# ourselves because when bdist_wheel runs it's too late to select a subset of libraries for package_data.
parser = argparse.ArgumentParser()
parser.add_argument("command")
parser.add_argument(
"--platform", "-P", type=str, default="", help="Wheel platform: windows|linux|macos-x86_64|aarch64|universal"
)
args = parser.parse_known_args()[0]
# return a canonical machine architecture string
# - "x86_64" for x86-64, aka. AMD64, aka. x64
# - "aarch64" for AArch64, aka. ARM64
def machine_architecture() -> str:
machine = platform.machine()
if machine == "x86_64" or machine == "AMD64":
return "x86_64"
if machine == "aarch64" or machine == "arm64":
return "aarch64"
raise RuntimeError(f"Unrecognized machine architecture {machine}")
class Platform(NamedTuple):
os: str
arch: str
fancy_name: str
extension: str
tag: str
def name(self) -> str:
return self.os + "-" + self.arch
platforms = [
Platform("windows", "x86_64", "Windows x86-64", ".dll", "win_amd64"),
Platform("linux", "x86_64", "Linux x86-64", ".so", "manylinux2014_x86_64"),
Platform("linux", "aarch64", "Linux AArch64", ".so", "manylinux2014_aarch64"),
Platform("macos", "universal", "macOS universal", ".dylib", "macosx_10_13_universal2"),
]
class Library(NamedTuple):
file: str
directory: str
platform: Platform
# Enumerate warp/bin libraries
def detect_warp_libraries():
detected_libraries = set()
warp_bin = pathlib.Path("warp/bin")
for file in warp_bin.rglob("*.*"):
for p in platforms:
if os.path.splitext(file.name)[1] == p.extension:
# If this is a local build, assume we want a wheel for this machine's architecture
if file.parent.name == "bin" and (p.arch == machine_architecture() or p.arch == "universal"):
detected_libraries.add(Library(file.name, "bin/", p))
else:
# Excpect libraries to be in a subdirectory named after the wheel platform
platform_name = p.name()
if file.parent.name == platform_name:
detected_libraries.add(Library(file.name, "bin/" + platform_name + "/", p))
if len(detected_libraries) == 0:
raise Exception("No libraries found in warp/bin. Please run build_lib.py first.")
return detected_libraries
detected_libraries = detect_warp_libraries()
detected_platforms = set([lib.platform for lib in detected_libraries])
wheel_platform = None # The one platform for which we're building a wheel
if args.command == "bdist_wheel":
if args.platform != "":
for p in platforms:
if args.platform == p.name():
wheel_platform = p
print(f"Platform argument specified for building {p.fancy_name} wheel")
break
if wheel_platform is None:
print(f"Platform argument '{args.platform}' not recognized")
elif wheel_platform not in detected_platforms:
print(f"No libraries found for {wheel_platform.fancy_name}")
print("Falling back to auto-detection")
wheel_platform = None
if wheel_platform is None:
if len(detected_platforms) > 1:
print("Libraries for multiple platforms were detected. Picking the first one.")
print(
"Run `python -m build --wheel -C--build-option=-P[windows|linux|macos]-[x86_64|aarch64|universal]` to select a specific one."
)
wheel_platform = next(iter(detected_platforms))
print("Creating Warp wheel for " + wheel_platform.fancy_name)
# Binary wheel distribution builds assume that the platform you're building on will be the platform
# of the package. This class overrides the platform tag.
# https://packaging.python.org/en/latest/specifications/platform-compatibility-tags
class WarpBDistWheel(bdist_wheel):
# Even though we parse the platform argument ourselves, we need to declare it here as well so
# setuptools.Command can validate the command line options.
user_options = bdist_wheel.user_options + [
("platform=", "P", "Wheel platform: windows|linux|macos-x86_64|aarch64|universal"),
]
def initialize_options(self):
super().initialize_options()
self.platform = ""
def get_tag(self):
if wheel_platform is not None:
# The wheel's complete tag format is {python tag}-{abi tag}-{platform tag}.
return "py3", "none", wheel_platform.tag
else:
# The target platform was not overridden. Fall back to base class behavior.
return bdist_wheel.get_tag(self)
def run(self):
super().run()
# Clean up so we can re-invoke `py -m build --wheel -C--build-option=--platform=...`
# See https://github.com/pypa/setuptools/issues/1871 for details.
shutil.rmtree("./build", ignore_errors=True)
shutil.rmtree("./warp_lang.egg-info", ignore_errors=True)
# Distributions are identified as non-pure (i.e. containing non-Python code, or binaries) if the
# setuptools.setup() `ext_modules` parameter is not empty, but this assumes building extension
# modules from source through the Python build. This class provides an override for prebuilt binaries:
class BinaryDistribution(setuptools.Distribution):
def has_ext_modules(self):
return True
def get_warp_libraries(platform):
libraries = []
for library in detected_libraries:
if library.platform == platform:
src = "warp/" + library.directory + library.file
dst = "warp/bin/" + library.file
if src != dst:
shutil.copyfile(src, dst)
libraries.append("bin/" + library.file)
return libraries
if wheel_platform is not None:
warp_binary_libraries = get_warp_libraries(wheel_platform)
else:
warp_binary_libraries = [] # Not needed during egg_info command
setuptools.setup(
package_data={
"": [
"native/*.cpp",
"native/*.cu",
"native/*.h",
"native/clang/*.cpp",
"native/nanovdb/*.h",
"tests/assets/*",
]
+ warp_binary_libraries,
},
distclass=BinaryDistribution,
cmdclass={
"bdist_wheel": WarpBDistWheel,
},
)