mirror of
https://github.com/YosysHQ/yosys
synced 2026-05-28 04:46:29 +00:00
Update top-level Python project for CMake compatibility.
This commit reimplements the (no longer recommended) setuptools based build system using a standards-based in-tree PEP517 build backend. The implementation is partially based on https://codeberg.org/ziglang/zig-pypi/src/branch/main/make_wheels.py which is licensed under BSD-0-clause.
This commit is contained in:
parent
0b55fad93a
commit
63fae93998
6 changed files with 168 additions and 141 deletions
14
cmake/GetPyosysVersion.cmake
Normal file
14
cmake/GetPyosysVersion.cmake
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
cmake_minimum_required(VERSION 3.27)
|
||||
set(CMAKE_MESSAGE_LOG_LEVEL ERROR)
|
||||
set(CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake" ${CMAKE_MODULE_PATH})
|
||||
include(YosysVersion)
|
||||
|
||||
yosys_extract_version()
|
||||
if (YOSYS_VERSION_COMMIT EQUAL "0")
|
||||
set(yosys_version "${YOSYS_VERSION_MAJOR}.${YOSYS_VERSION_MINOR}")
|
||||
elseif (YOSYS_VERSION_COMMIT STREQUAL "")
|
||||
set(yosys_version "${YOSYS_VERSION_MAJOR}.${YOSYS_VERSION_MINOR}.post9999")
|
||||
else()
|
||||
set(yosys_version "${YOSYS_VERSION_MAJOR}.${YOSYS_VERSION_MINOR}.post${YOSYS_VERSION_COMMIT}")
|
||||
endif()
|
||||
execute_process(COMMAND ${CMAKE_COMMAND} -E echo "${yosys_version}")
|
||||
|
|
@ -228,6 +228,7 @@ PYBIND11_MODULE(pyosys, m) {
|
|||
// This should not affect using wheels as the dylib has to actually be called
|
||||
// libyosys_dummy.so for this function to be interacted with at all.
|
||||
PYBIND11_MODULE(libyosys_dummy, _) {
|
||||
(void)_;
|
||||
throw py::import_error("Change your import from 'import libyosys' to 'from pyosys import libyosys'.");
|
||||
}
|
||||
#endif
|
||||
|
|
|
|||
146
pyosys/build/local_backend.py
Normal file
146
pyosys/build/local_backend.py
Normal file
|
|
@ -0,0 +1,146 @@
|
|||
# To build a wheel with additional CMake options, use `--build-option`, e.g.:
|
||||
#
|
||||
# python -m build -w -Ccmake=-DYOSYS_COMPILER_LAUNCHER=ccache
|
||||
# pip install -Ccmake=-DYOSYS_COMPILER_LAUNCHER=ccache .
|
||||
|
||||
import os
|
||||
import sys
|
||||
import pathlib
|
||||
import tarfile
|
||||
import tempfile
|
||||
import subprocess
|
||||
import sysconfig
|
||||
from email.policy import EmailPolicy
|
||||
from email.message import EmailMessage
|
||||
from wheel.wheelfile import WheelFile
|
||||
|
||||
|
||||
PROJECT_NAME = "pyosys"
|
||||
PROJECT_VERSION = subprocess.check_output([
|
||||
"cmake",
|
||||
f"-DCMAKE_SOURCE_DIR={os.getcwd()}",
|
||||
"-P", "cmake/GetPyosysVersion.cmake"
|
||||
], encoding="ascii").strip()
|
||||
DIST_NAME = f"{PROJECT_NAME}-{PROJECT_VERSION}"
|
||||
|
||||
# https://packaging.python.org/en/latest/specifications/platform-compatibility-tags/
|
||||
if sys.implementation.name == "cpython":
|
||||
PYTHON_TAG = f"cp{sysconfig.get_config_var("py_version_nodot")}"
|
||||
else:
|
||||
raise NotImplementedError("unsupported Python implementation")
|
||||
PLATFORM_TAG = sysconfig.get_platform().replace("-", "_")
|
||||
COMPAT_TAG = f"{PYTHON_TAG}-none-{PLATFORM_TAG}"
|
||||
|
||||
|
||||
def compile_pyosys(cmake_options=[], parallel=os.cpu_count() or 1):
|
||||
install_dir = tempfile.TemporaryDirectory(prefix="pyosys_install")
|
||||
with tempfile.TemporaryDirectory(prefix="pyosys_build") as build_dir:
|
||||
subprocess.check_call([
|
||||
"cmake",
|
||||
"-S", ".",
|
||||
"-B", build_dir,
|
||||
"-DYOSYS_WITH_PYTHON=ON",
|
||||
"-DYOSYS_INSTALL_DRIVER=OFF",
|
||||
"-DYOSYS_INSTALL_LIBRARY=OFF",
|
||||
"-DYOSYS_INSTALL_PYTHON=ON",
|
||||
f"-DCMAKE_INSTALL_PREFIX={install_dir.name}",
|
||||
f"-DYOSYS_INSTALL_PYTHON_SITEDIR=python",
|
||||
*cmake_options,
|
||||
])
|
||||
subprocess.check_call([
|
||||
"cmake",
|
||||
"--build", build_dir,
|
||||
"-t", "pyosys",
|
||||
f"-j{parallel}",
|
||||
])
|
||||
subprocess.check_call([
|
||||
"cmake",
|
||||
"--install", build_dir,
|
||||
"--strip",
|
||||
])
|
||||
return install_dir
|
||||
|
||||
|
||||
def make_message(headers, payload=None):
|
||||
msg = EmailMessage(policy=EmailPolicy(max_line_length=0))
|
||||
for name, value in headers:
|
||||
if isinstance(value, list):
|
||||
for value_part in value:
|
||||
msg[name] = value_part
|
||||
else:
|
||||
msg[name] = value
|
||||
if payload:
|
||||
msg.set_payload(payload)
|
||||
return bytes(msg)
|
||||
|
||||
|
||||
def build_sdist(sdist_dir, config_settings=None):
|
||||
sdist_filename = f"{DIST_NAME}.tar.gz"
|
||||
|
||||
with tarfile.open(pathlib.Path(sdist_dir) / sdist_filename, "w:gz",
|
||||
format=tarfile.PAX_FORMAT) as sdist:
|
||||
def exclude_build(entry):
|
||||
name = entry.name.removeprefix(f"{DIST_NAME}/")
|
||||
if name in (".cache", "build", "dist"):
|
||||
return
|
||||
if os.path.basename(name) in (".git", "__pycache__"):
|
||||
return
|
||||
return entry
|
||||
sdist.add(os.getcwd(), arcname=DIST_NAME, filter=exclude_build)
|
||||
|
||||
return sdist_filename
|
||||
|
||||
|
||||
def get_metadata_files():
|
||||
with open("README.md", "rb") as readme:
|
||||
long_description = readme.read()
|
||||
|
||||
return {
|
||||
"WHEEL": make_message([
|
||||
("Wheel-Version", "1.0"),
|
||||
("Generator", "pyosys build backend"),
|
||||
("Root-Is-Purelib", "false"),
|
||||
("Tag", [COMPAT_TAG]),
|
||||
]),
|
||||
"METADATA": make_message([
|
||||
("Metadata-Version", "2.4"),
|
||||
("Name", PROJECT_NAME),
|
||||
("Version", PROJECT_VERSION),
|
||||
("Summary", "Python access to libyosys"),
|
||||
("Description-Content-Type", "text/markdown"),
|
||||
("License-Expression", "MIT"),
|
||||
("Classifier", "Programming Language :: Python :: 3"),
|
||||
("Classifier", "Intended Audience :: Developers"),
|
||||
("Classifier", "Operating System :: POSIX :: Linux"),
|
||||
("Classifier", "Operating System :: MacOS :: MacOS X"),
|
||||
("Requires-Python", ">=3.8"),
|
||||
], long_description)
|
||||
}
|
||||
|
||||
|
||||
def prepare_metadata_for_build_wheel(metadata_directory, config_settings=None):
|
||||
os.mkdir(f"{metadata_directory}/{DIST_NAME}.dist-info")
|
||||
|
||||
for filename, contents in get_metadata_files().items():
|
||||
with open(f"{metadata_directory}/{DIST_NAME}.dist-info/{filename}", "wb") as f:
|
||||
f.write(contents)
|
||||
|
||||
return f"{DIST_NAME}.dist-info"
|
||||
|
||||
|
||||
def build_wheel(wheel_dir, config_settings=None, metadata_directory=None):
|
||||
wheel_filename = f"{DIST_NAME}-{COMPAT_TAG}.whl"
|
||||
|
||||
with WheelFile(pathlib.Path(wheel_dir) / wheel_filename, "w") as wheel:
|
||||
for filename, contents in get_metadata_files().items():
|
||||
wheel.writestr(f"{DIST_NAME}.dist-info/{filename}", contents)
|
||||
|
||||
cmake_options = []
|
||||
if config_settings is not None:
|
||||
if cmake_options := config_settings.get("cmake"):
|
||||
if isinstance(cmake_options, str):
|
||||
cmake_options = [cmake_options]
|
||||
with compile_pyosys(cmake_options) as install_dir:
|
||||
wheel.write_files(pathlib.Path(install_dir) / "python")
|
||||
|
||||
return wheel_filename
|
||||
|
|
@ -482,9 +482,11 @@ void bind_idict(module &m, const char *name_cstr) {
|
|||
return make_iterator(s.begin(), s.end());
|
||||
})
|
||||
.def("values", [](args _){
|
||||
(void)_;
|
||||
throw type_error("idicts do not support iteration on the integers");
|
||||
})
|
||||
.def("items", [](args _){
|
||||
(void)_;
|
||||
throw type_error("idicts do not support pairwise iteration");
|
||||
})
|
||||
.def("update", [](C &s, iterable other) {
|
||||
|
|
@ -521,6 +523,7 @@ void bind_idict(module &m, const char *name_cstr) {
|
|||
|
||||
for (const char *mutator: {"__setitem__", "__delitem__", "pop", "popitem", "setdefault"}) {
|
||||
cls.def(mutator, [](args _) {
|
||||
(void)_;
|
||||
throw type_error("idicts do not support arbitrary element mutation");
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,10 +1,11 @@
|
|||
[build-system]
|
||||
requires = [
|
||||
"setuptools>=42",
|
||||
"wheel",
|
||||
"pybind11>=3,<4",
|
||||
"cxxheaderparser"
|
||||
"cxxheaderparser",
|
||||
]
|
||||
build-backend = "setuptools.build_meta"
|
||||
backend-path = ["pyosys/build"]
|
||||
build-backend = "local_backend"
|
||||
|
||||
[tool.ruff]
|
||||
target-version = "py38"
|
||||
|
|
|
|||
138
setup.py
138
setup.py
|
|
@ -1,138 +0,0 @@
|
|||
#!/usr/bin/env python3
|
||||
# Copyright (C) 2024 Efabless Corporation
|
||||
#
|
||||
# Permission to use, copy, modify, and/or distribute this software for any
|
||||
# purpose with or without fee is hereby granted, provided that the above
|
||||
# copyright notice and this permission notice appear in all copies.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
import os
|
||||
import re
|
||||
import shlex
|
||||
import shutil
|
||||
from pathlib import Path
|
||||
from setuptools import setup, Extension
|
||||
|
||||
import pybind11
|
||||
from pybind11.setup_helpers import build_ext
|
||||
|
||||
__yosys_root__ = Path(__file__).parent
|
||||
|
||||
yosys_version_rx = re.compile(r"YOSYS_VER\s*:=\s*([\w\-\+\.]+)")
|
||||
|
||||
with open(__yosys_root__ / "Makefile", encoding="utf8") as f:
|
||||
# Extract and convert + to patch version
|
||||
version = yosys_version_rx.search(f.read())[1].replace("+", ".")
|
||||
|
||||
|
||||
class libyosys_so_ext(Extension):
|
||||
def __init__(
|
||||
self,
|
||||
) -> None:
|
||||
super().__init__(
|
||||
"libyosys.so",
|
||||
[],
|
||||
)
|
||||
|
||||
# when iterating locally, you probably want to set this variable
|
||||
# to avoid mass rebuilds bec of pybind11's include path changing
|
||||
pybind_include = os.getenv("_FORCE_PYBIND_INCLUDE", pybind11.get_include())
|
||||
|
||||
self.args = [
|
||||
f"PYBIND11_INCLUDE={pybind_include}",
|
||||
"ENABLE_PYOSYS=1",
|
||||
# Would need to be installed separately by the user
|
||||
"ENABLE_TCL=0",
|
||||
"ENABLE_READLINE=0",
|
||||
"ENABLE_EDITLINE=0",
|
||||
"PYOSYS_USE_UV=0", # + install requires takes its role when building wheels
|
||||
# Always compile and include ABC in wheel
|
||||
"ABCEXTERNAL=",
|
||||
# Show compile commands
|
||||
"PRETTY=0",
|
||||
]
|
||||
|
||||
def custom_build(self, bext: build_ext):
|
||||
make_flags_split = shlex.split(os.getenv("makeFlags", ""))
|
||||
# abc linking takes a lot of memory, best get it out of the way first
|
||||
bext.spawn(
|
||||
[
|
||||
"make",
|
||||
f"-j{os.cpu_count() or 1}",
|
||||
"yosys-abc",
|
||||
*make_flags_split,
|
||||
*self.args,
|
||||
]
|
||||
)
|
||||
# build libyosys and share with abc out of the way
|
||||
bext.spawn(
|
||||
[
|
||||
"make",
|
||||
f"-j{os.cpu_count() or 1}",
|
||||
self.name,
|
||||
"share",
|
||||
*make_flags_split,
|
||||
*self.args,
|
||||
]
|
||||
)
|
||||
ext_fullpath = Path(bext.get_ext_fullpath(self.name))
|
||||
build_path = ext_fullpath.parents[1]
|
||||
pyosys_path = build_path / "pyosys"
|
||||
os.makedirs(pyosys_path, exist_ok=True)
|
||||
|
||||
# libyosys.so
|
||||
target = pyosys_path / self.name
|
||||
shutil.copy(self.name, target)
|
||||
bext.spawn(["strip", "-S", str(target)])
|
||||
|
||||
# yosys-abc
|
||||
yosys_abc_target = pyosys_path / "yosys-abc"
|
||||
shutil.copy("yosys-abc", yosys_abc_target)
|
||||
bext.spawn(["strip", "-S", str(yosys_abc_target)])
|
||||
|
||||
# share directory
|
||||
share_target = pyosys_path / "share"
|
||||
try:
|
||||
shutil.rmtree(share_target)
|
||||
except FileNotFoundError:
|
||||
pass
|
||||
|
||||
shutil.copytree("share", share_target)
|
||||
|
||||
|
||||
class custom_build_ext(build_ext):
|
||||
def build_extension(self, ext) -> None:
|
||||
if not hasattr(ext, "custom_build"):
|
||||
return super().build_extension(ext)
|
||||
return ext.custom_build(self)
|
||||
|
||||
|
||||
with open(__yosys_root__ / "README.md", encoding="utf8") as f:
|
||||
long_description = f.read()
|
||||
|
||||
setup(
|
||||
name="pyosys",
|
||||
packages=["pyosys"],
|
||||
version=version,
|
||||
description="Python access to libyosys",
|
||||
long_description=long_description,
|
||||
long_description_content_type="text/markdown",
|
||||
license="MIT",
|
||||
classifiers=[
|
||||
"Programming Language :: Python :: 3",
|
||||
"Intended Audience :: Developers",
|
||||
"Operating System :: POSIX :: Linux",
|
||||
"Operating System :: MacOS :: MacOS X",
|
||||
],
|
||||
python_requires=">=3.8",
|
||||
ext_modules=[libyosys_so_ext()],
|
||||
cmdclass={
|
||||
"build_ext": custom_build_ext,
|
||||
},
|
||||
)
|
||||
Loading…
Add table
Add a link
Reference in a new issue