mirror of https://github.com/YosysHQ/yosys.git
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. It also adds a new option `YOSYS_BUILD_PYTHON_ONLY` that is available only if the binary or the library aren't going to be installed, which turns off these targets entirely, as well as some dependent ones (e.g. tests). Co-authored-by: Mohamed Gaber <me@donn.website>
This commit is contained in:
parent
780588f28c
commit
afc0e78d11
|
|
@ -36,13 +36,11 @@ jobs:
|
|||
runner: "macos-15",
|
||||
archs: "arm64",
|
||||
},
|
||||
## Windows is disabled because of an issue with compiling FFI as
|
||||
## under MinGW in the GitHub Actions environment (SHELL variable has
|
||||
## whitespace.)
|
||||
## Windows still needs to be tested.
|
||||
# {
|
||||
# name: "Windows Server 2019",
|
||||
# name: "Windows Server 2025",
|
||||
# family: "windows",
|
||||
# runner: "windows-2019",
|
||||
# runner: "windows-2025",
|
||||
# archs: "AMD64",
|
||||
# },
|
||||
]
|
||||
|
|
@ -77,17 +75,14 @@ jobs:
|
|||
name: "[Windows] Flex/Bison"
|
||||
run: |
|
||||
choco install winflexbison3
|
||||
- if: ${{ matrix.os.family == 'macos' && matrix.os.archs == 'arm64' }}
|
||||
name: "[macOS/arm64] Install Python 3.8 (see: https://cibuildwheel.pypa.io/en/stable/faq/#macos-building-cpython-38-wheels-on-arm64)"
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: 3.8
|
||||
- name: Build wheels
|
||||
uses: pypa/cibuildwheel@v2.21.1
|
||||
uses: pypa/cibuildwheel@v3.4.1
|
||||
env:
|
||||
# * APIs not supported by PyPy
|
||||
# * Musllinux disabled because it increases build time from 48m to ~3h
|
||||
CIBW_SKIP: >
|
||||
cp38*
|
||||
cp39*
|
||||
pp*
|
||||
*musllinux*
|
||||
CIBW_ARCHS: ${{ matrix.os.archs }}
|
||||
|
|
@ -104,7 +99,6 @@ jobs:
|
|||
OPTFLAGS=-O3
|
||||
PKG_CONFIG_PATH=./ffi/pfx/lib/pkgconfig
|
||||
MACOSX_DEPLOYMENT_TARGET=11
|
||||
makeFlags='CONFIG=clang'
|
||||
PATH="$PWD/bison/src:$PATH"
|
||||
CIBW_BEFORE_BUILD: bash ./.github/workflows/wheels/cibw_before_build.sh
|
||||
CIBW_TEST_COMMAND: python3 {project}/tests/pyosys/run_tests.py
|
||||
|
|
|
|||
|
|
@ -1,52 +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.
|
||||
"""
|
||||
This runs the cibuildwheel step from the wheels workflow locally.
|
||||
"""
|
||||
|
||||
import os
|
||||
import yaml
|
||||
import platform
|
||||
import subprocess
|
||||
from pathlib import Path
|
||||
|
||||
__yosys_root__ = Path(__file__).absolute().parents[3]
|
||||
|
||||
for source in ["ffi", "bison"]:
|
||||
if not (__yosys_root__ / source).is_dir():
|
||||
print(
|
||||
"You need to download ffi and bison in a similar manner to wheels.yml first."
|
||||
)
|
||||
exit(-1)
|
||||
|
||||
with open(__yosys_root__ / ".github" / "workflows" / "wheels.yml") as f:
|
||||
workflow = yaml.safe_load(f)
|
||||
|
||||
env = os.environ.copy()
|
||||
|
||||
steps = workflow["jobs"]["build_wheels"]["steps"]
|
||||
cibw_step = None
|
||||
for step in steps:
|
||||
if (step.get("uses") or "").startswith("pypa/cibuildwheel"):
|
||||
cibw_step = step
|
||||
break
|
||||
|
||||
for key, value in cibw_step["env"].items():
|
||||
if key.endswith("WIN") or key.endswith("MAC"):
|
||||
continue
|
||||
env[key] = value
|
||||
|
||||
env["CIBW_ARCHS"] = os.getenv("CIBW_ARCHS", platform.machine())
|
||||
subprocess.check_call(["cibuildwheel"], env=env)
|
||||
|
|
@ -1,9 +1,6 @@
|
|||
set -e
|
||||
set -x
|
||||
|
||||
# Don't use Python objects from previous compiles
|
||||
make clean-py
|
||||
|
||||
# DEBUG: show python3 and python3-config outputs
|
||||
if [ "$(uname)" != "Linux" ]; then
|
||||
# https://github.com/pypa/cibuildwheel/issues/2021
|
||||
|
|
|
|||
145
CMakeLists.txt
145
CMakeLists.txt
|
|
@ -62,6 +62,13 @@ cmake_dependent_option(YOSYS_INSTALL_PYTHON "Install Python extension module" OF
|
|||
YOSYS_WITH_PYTHON OFF)
|
||||
set(YOSYS_INSTALL_PYTHON_SITEDIR "" CACHE STRING "Path to Python package installation directory")
|
||||
|
||||
# This option is something of a hack to make Python wheels buildable in an environment that has
|
||||
# the `Development.Module` component, but not `Development.Embed` (e.g. cibuildwheel). It is only
|
||||
# present to be used in the wheel build and is not supported otherwise.
|
||||
cmake_dependent_option(YOSYS_BUILD_PYTHON_ONLY "Build only Pyosys components" ON
|
||||
"NOT (YOSYS_INSTALL_DRIVER OR YOSYS_INSTALL_LIBRARY) AND YOSYS_INSTALL_PYTHON" OFF)
|
||||
mark_as_advanced(YOSYS_BUILD_PYTHON_ONLY)
|
||||
|
||||
# Configure compiler.
|
||||
set(CMAKE_EXPORT_COMPILE_COMMANDS YES)
|
||||
|
||||
|
|
@ -253,9 +260,9 @@ if (tcl_FOUND)
|
|||
endif()
|
||||
|
||||
if (YOSYS_WITH_PYTHON)
|
||||
find_package(Python3Embed REQUIRED)
|
||||
set_property(GLOBAL PROPERTY _CMAKE_Python3Embed_REQUIRED_VERSION "== ${Python3_VERSION}")
|
||||
set_package_properties(Python3Embed PROPERTIES
|
||||
find_package(Python3Devel REQUIRED)
|
||||
set_property(GLOBAL PROPERTY _CMAKE_Python3Devel_REQUIRED_VERSION "== ${Python3_VERSION}")
|
||||
set_package_properties(Python3Devel PROPERTIES
|
||||
URL "https://www.python.org/"
|
||||
DESCRIPTION "Dynamic programming language (Embedding)"
|
||||
PURPOSE "Binding Yosys API"
|
||||
|
|
@ -288,7 +295,7 @@ condition(YOSYS_ENABLE_LIBFFI Dlfcn_FOUND AND libffi_FOUND AND NOT YOSYS_WITHOUT
|
|||
condition(YOSYS_ENABLE_READLINE readline_FOUND AND NOT YOSYS_WITHOUT_READLINE)
|
||||
condition(YOSYS_ENABLE_EDITLINE editline_FOUND AND NOT YOSYS_WITHOUT_EDITLINE AND NOT YOSYS_ENABLE_READLINE)
|
||||
condition(YOSYS_ENABLE_TCL tcl_FOUND AND libtommath_FOUND AND NOT YOSYS_WITHOUT_TCL)
|
||||
condition(YOSYS_ENABLE_PYTHON Python3Embed_FOUND AND PyosysEnv_FOUND AND YOSYS_WITH_PYTHON)
|
||||
condition(YOSYS_ENABLE_PYTHON Python3Devel_FOUND AND PyosysEnv_FOUND AND YOSYS_WITH_PYTHON)
|
||||
condition(YOSYS_ENABLE_VERIFIC YOSYS_VERIFIC_DIR AND zlib_FOUND)
|
||||
|
||||
# Describe dependencies and features
|
||||
|
|
@ -391,46 +398,52 @@ endif()
|
|||
|
||||
# Compute a transitive closure of enabled components.
|
||||
yosys_expand_components(library_components essentials ${YOSYS_COMPONENTS})
|
||||
yosys_expand_components(driver_components driver ${YOSYS_COMPONENTS})
|
||||
if (NOT YOSYS_BUILD_PYTHON_ONLY)
|
||||
yosys_expand_components(driver_components driver ${YOSYS_COMPONENTS})
|
||||
endif()
|
||||
|
||||
# Main Yosys executable (compiler driver).
|
||||
yosys_cxx_executable(yosys
|
||||
OUTPUT_NAME yosys
|
||||
INSTALL_IF ${YOSYS_INSTALL_DRIVER}
|
||||
)
|
||||
yosys_link_components(yosys PRIVATE ${driver_components})
|
||||
set_property(TARGET yosys PROPERTY ENABLE_EXPORTS ON)
|
||||
if (MINGW)
|
||||
target_link_options(yosys PRIVATE LINKER:--export-all-symbols)
|
||||
set_target_properties(yosys PROPERTIES
|
||||
# Final name: `yosys.exe.a` (linked to explicitly)
|
||||
IMPORT_PREFIX ""
|
||||
IMPORT_SUFFIX ".exe.a"
|
||||
if (NOT YOSYS_BUILD_PYTHON_ONLY)
|
||||
yosys_cxx_executable(yosys
|
||||
OUTPUT_NAME yosys
|
||||
INSTALL_IF ${YOSYS_INSTALL_DRIVER}
|
||||
)
|
||||
if (YOSYS_INSTALL_DRIVER)
|
||||
install(FILES ${CMAKE_BINARY_DIR}/yosys.exe.a DESTINATION ${YOSYS_INSTALL_LIBDIR})
|
||||
yosys_link_components(yosys PRIVATE ${driver_components})
|
||||
set_property(TARGET yosys PROPERTY ENABLE_EXPORTS ON)
|
||||
if (MINGW)
|
||||
target_link_options(yosys PRIVATE LINKER:--export-all-symbols)
|
||||
set_target_properties(yosys PROPERTIES
|
||||
# Final name: `yosys.exe.a` (linked to explicitly)
|
||||
IMPORT_PREFIX ""
|
||||
IMPORT_SUFFIX ".exe.a"
|
||||
)
|
||||
if (YOSYS_INSTALL_DRIVER)
|
||||
install(FILES ${CMAKE_BINARY_DIR}/yosys.exe.a DESTINATION ${YOSYS_INSTALL_LIBDIR})
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
||||
|
||||
target_compile_options(yosys PRIVATE -fsanitize=undefined)
|
||||
target_compile_options(yosys PRIVATE -fsanitize=undefined)
|
||||
endif()
|
||||
|
||||
# Yosys components as a library.
|
||||
if (BUILD_SHARED_LIBS)
|
||||
set(libyosys_type SHARED)
|
||||
else()
|
||||
set(libyosys_type STATIC)
|
||||
endif()
|
||||
yosys_cxx_library(libyosys ${libyosys_type}
|
||||
OUTPUT_NAME libyosys
|
||||
INSTALL_IF ${YOSYS_INSTALL_LIBRARY}
|
||||
)
|
||||
yosys_link_components(libyosys PRIVATE ${library_components})
|
||||
add_library(Yosys::libyosys ALIAS libyosys)
|
||||
if (MINGW)
|
||||
set_target_properties(libyosys PROPERTIES
|
||||
# Final name: `libyosys.dll.a` (linked to via `-lyosys`)
|
||||
IMPORT_PREFIX ""
|
||||
if (NOT YOSYS_BUILD_PYTHON_ONLY)
|
||||
if (BUILD_SHARED_LIBS)
|
||||
set(libyosys_type SHARED)
|
||||
else()
|
||||
set(libyosys_type STATIC)
|
||||
endif()
|
||||
yosys_cxx_library(libyosys ${libyosys_type}
|
||||
OUTPUT_NAME libyosys
|
||||
INSTALL_IF ${YOSYS_INSTALL_LIBRARY}
|
||||
)
|
||||
yosys_link_components(libyosys PRIVATE ${library_components})
|
||||
add_library(Yosys::libyosys ALIAS libyosys)
|
||||
if (MINGW)
|
||||
set_target_properties(libyosys PROPERTIES
|
||||
# Final name: `libyosys.dll.a` (linked to via `-lyosys`)
|
||||
IMPORT_PREFIX ""
|
||||
)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# Yosys data files (mainly headers and technological libraries).
|
||||
|
|
@ -500,37 +513,39 @@ set(makefile_depends
|
|||
$<$<TARGET_EXISTS:yosys-witness>:yosys-witness>
|
||||
)
|
||||
|
||||
# Tests.
|
||||
add_subdirectory(tests/unit)
|
||||
if (NOT YOSYS_BUILD_PYTHON_ONLY)
|
||||
# Tests.
|
||||
add_subdirectory(tests/unit)
|
||||
|
||||
add_custom_target(test-unit
|
||||
COMMAND ${CMAKE_CTEST_COMMAND} --test-dir tests/unit --output-on-failure
|
||||
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
|
||||
)
|
||||
|
||||
add_custom_target(test-vanilla
|
||||
COMMAND make vanilla-test ${makefile_vars}
|
||||
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/tests
|
||||
DEPENDS ${makefile_depends}
|
||||
)
|
||||
|
||||
add_custom_target(test
|
||||
DEPENDS test-unit test-vanilla
|
||||
)
|
||||
|
||||
# Docs.
|
||||
add_custom_target(docs-prepare
|
||||
COMMAND make gen ${makefile_vars}
|
||||
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/docs
|
||||
DEPENDS ${makefile_depends}
|
||||
)
|
||||
foreach (format html latexpdf)
|
||||
add_custom_target(docs-${format}
|
||||
COMMAND make ${format}
|
||||
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/docs
|
||||
DEPENDS docs-prepare
|
||||
add_custom_target(test-unit
|
||||
COMMAND ${CMAKE_CTEST_COMMAND} --test-dir tests/unit --output-on-failure
|
||||
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
|
||||
)
|
||||
endforeach()
|
||||
|
||||
add_custom_target(test-vanilla
|
||||
COMMAND make vanilla-test ${makefile_vars}
|
||||
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/tests
|
||||
DEPENDS ${makefile_depends}
|
||||
)
|
||||
|
||||
add_custom_target(test
|
||||
DEPENDS test-unit test-vanilla
|
||||
)
|
||||
|
||||
# Docs.
|
||||
add_custom_target(docs-prepare
|
||||
COMMAND make gen ${makefile_vars}
|
||||
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/docs
|
||||
DEPENDS ${makefile_depends}
|
||||
)
|
||||
foreach (format html latexpdf)
|
||||
add_custom_target(docs-${format}
|
||||
COMMAND make ${format}
|
||||
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/docs
|
||||
DEPENDS docs-prepare
|
||||
)
|
||||
endforeach()
|
||||
endif()
|
||||
|
||||
# Utilities.
|
||||
add_custom_target(print-version
|
||||
|
|
|
|||
|
|
@ -4,8 +4,10 @@
|
|||
# whether the host interpreter has the necessary dependencies first, and if it
|
||||
# does not, fall back to using `uv`.
|
||||
|
||||
foreach (strategy host uv fail)
|
||||
if (strategy STREQUAL "host")
|
||||
foreach (strategy virtualenv host uv fail)
|
||||
if (strategy STREQUAL "virtualenv")
|
||||
set(PyosysEnv_PYTHON $ENV{VIRTUAL_ENV}/bin/python)
|
||||
elseif (strategy STREQUAL "host")
|
||||
set(PyosysEnv_PYTHON ${Python3_EXECUTABLE})
|
||||
elseif (strategy STREQUAL "uv")
|
||||
set(PyosysEnv_PYTHON uv run --no-project --with pybind11>3,<4 --with cxxheaderparser python)
|
||||
|
|
|
|||
|
|
@ -6,10 +6,22 @@ get_property(packages_found GLOBAL PROPERTY PACKAGES_FOUND)
|
|||
get_property(packages_not_found GLOBAL PROPERTY PACKAGES_NOT_FOUND)
|
||||
get_property(required_version GLOBAL PROPERTY _CMAKE_Python3_REQUIRED_VERSION)
|
||||
|
||||
# A hack to make pyosys buildable in wheel-only environments.
|
||||
# `Interpreter` is a part of the component set to ensure that a Python implementation without
|
||||
# an interpreter that's earlier in the search order won't be selected instead of the desired one.
|
||||
# (This is awful and should be removed once CMake 4.0 is here.)
|
||||
if (YOSYS_BUILD_PYTHON_ONLY)
|
||||
set(components Interpreter Development.Module)
|
||||
else()
|
||||
set(components Interpreter Development)
|
||||
endif()
|
||||
|
||||
# The `EXACT` specifier prevents the situation of `FindPython3` discovering a newer libpython-dev
|
||||
# than the interpreter found in the past, rejecting it because it is too new, and giving up.
|
||||
find_package(Python3 EXACT ${Python3_VERSION} COMPONENTS Development.Embed)
|
||||
set(Python3Embed_FOUND ${Python3_Development.Embed_FOUND})
|
||||
find_package(Python3 EXACT ${Python3_VERSION} COMPONENTS ${components})
|
||||
if (Python3_Development.Embed_FOUND OR Python3_Development.Module_FOUND)
|
||||
set(Python3Devel_FOUND YES)
|
||||
endif()
|
||||
|
||||
set_property(GLOBAL PROPERTY PACKAGES_FOUND "${packages_found}")
|
||||
set_property(GLOBAL PROPERTY PACKAGES_NOT_FOUND "${packages_not_found}")
|
||||
|
|
@ -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}")
|
||||
|
|
@ -96,7 +96,7 @@ yosys_core(kernel
|
|||
$<${YOSYS_ENABLE_READLINE}:PkgConfig::readline>
|
||||
$<${YOSYS_ENABLE_EDITLINE}:PkgConfig::editline>
|
||||
$<${YOSYS_ENABLE_TCL}:PkgConfig::tcl>
|
||||
$<${YOSYS_ENABLE_PYTHON}:Python3::Python>
|
||||
$<${YOSYS_ENABLE_PYTHON}:Python3::Module>
|
||||
REQUIRES
|
||||
bigint
|
||||
ezsat
|
||||
|
|
@ -172,16 +172,18 @@ yosys_core(fstdata
|
|||
fstdata.h
|
||||
)
|
||||
|
||||
yosys_core(driver
|
||||
driver.cc
|
||||
INCLUDE_DIRS
|
||||
${pybind11_INCLUDE_DIR}
|
||||
LIBRARIES
|
||||
$<${YOSYS_ENABLE_READLINE}:PkgConfig::readline>
|
||||
$<${YOSYS_ENABLE_EDITLINE}:PkgConfig::editline>
|
||||
$<${YOSYS_ENABLE_TCL}:PkgConfig::tcl>
|
||||
$<${YOSYS_ENABLE_PYTHON}:Python3::Python>
|
||||
REQUIRES
|
||||
essentials
|
||||
BOOTSTRAP
|
||||
)
|
||||
if (NOT YOSYS_BUILD_PYTHON_ONLY)
|
||||
yosys_core(driver
|
||||
driver.cc
|
||||
INCLUDE_DIRS
|
||||
${pybind11_INCLUDE_DIR}
|
||||
LIBRARIES
|
||||
$<${YOSYS_ENABLE_READLINE}:PkgConfig::readline>
|
||||
$<${YOSYS_ENABLE_EDITLINE}:PkgConfig::editline>
|
||||
$<${YOSYS_ENABLE_TCL}:PkgConfig::tcl>
|
||||
$<${YOSYS_ENABLE_PYTHON}:Python3::Python>
|
||||
REQUIRES
|
||||
essentials
|
||||
BOOTSTRAP
|
||||
)
|
||||
endif()
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -115,7 +115,7 @@ yosys_pass(plugin
|
|||
${pybind11_INCLUDE_DIR}
|
||||
LIBRARIES
|
||||
$<${YOSYS_ENABLE_PLUGINS}:${Dlfcn_LIBRARIES}>
|
||||
$<${YOSYS_ENABLE_PYTHON}:Python3::Python>
|
||||
$<${YOSYS_ENABLE_PYTHON}:Python3::Module>
|
||||
ESSENTIAL
|
||||
)
|
||||
yosys_pass(check
|
||||
|
|
|
|||
|
|
@ -24,6 +24,6 @@ yosys_core(pyosys
|
|||
INCLUDE_DIRS
|
||||
${pybind11_INCLUDE_DIR}
|
||||
LIBRARIES
|
||||
$<${YOSYS_ENABLE_PYTHON}:Python3::Python>
|
||||
$<${YOSYS_ENABLE_PYTHON}:Python3::Module>
|
||||
ESSENTIAL
|
||||
)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,175 @@
|
|||
# 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 re
|
||||
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')}"
|
||||
# freethreaded builds have an ABI flag appended, "t"
|
||||
ABI_TAG = f"cp{sysconfig.get_config_var('py_version_nodot')}{sysconfig.get_config_var('abiflags')}"
|
||||
else:
|
||||
raise NotImplementedError("unsupported Python implementation")
|
||||
# get_platform() always returns the MACOSX_DEPLOYMENT_TARGET this intepreter is
|
||||
# configured with:
|
||||
# https://github.com/python/cpython/blob/494f2e3c92cc1b7774cca16fca5c7d1ff18c0de2/Lib/_osx_support.py#L504
|
||||
PLATFORM_TAG_RAW = sysconfig.get_platform()
|
||||
MACOSX_DEPLOYMENT_TARGET_FLAGS = []
|
||||
if interpreter_deployment_target := sysconfig.get_config_var("MACOSX_DEPLOYMENT_TARGET"):
|
||||
cmake_deployment_target = interpreter_deployment_target
|
||||
interpreter_deployment_target = tuple(int(v) for v in interpreter_deployment_target.split("."))
|
||||
# Yosys fails to compile for anything below 10.15 because of std::filesystem
|
||||
requested_deployment_target = tuple(int(v) for v in os.environ.get("MACOSX_DEPLOYMENT_TARGET", "10.15").split("."))
|
||||
if requested_deployment_target > interpreter_deployment_target:
|
||||
resolved_platform_version_string = ".".join(str(v) for v in requested_deployment_target)
|
||||
cmake_deployment_target = resolved_platform_version_string
|
||||
if "." not in resolved_platform_version_string:
|
||||
# macOS 11+ need to be "bare" for MACOSX_DEPLOYMENT_TARGET but have
|
||||
# the .0 for Python platform versions
|
||||
resolved_platform_version_string += ".0"
|
||||
PLATFORM_TAG_RAW = re.sub(r"(macosx)-\d+\.\d+", rf"\1-{resolved_platform_version_string}", PLATFORM_TAG_RAW)
|
||||
MACOSX_DEPLOYMENT_TARGET_FLAGS = [f"-DCMAKE_OSX_DEPLOYMENT_TARGET={cmake_deployment_target}"]
|
||||
# Source for these substitutions:
|
||||
# https://github.com/pypa/wheel/blob/197012dcb8a9da10570d6486bc1a70305861e7f2/src/wheel/_bdist_wheel.py#L351
|
||||
PLATFORM_TAG = PLATFORM_TAG_RAW.lower().replace("-", "_").replace(".", "_").replace(" ", "_")
|
||||
COMPAT_TAG = f"{PYTHON_TAG}-{ABI_TAG}-{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,
|
||||
"-DCMAKE_BUILD_TYPE=Release",
|
||||
f"-DPython3_EXECUTABLE={sys.executable}",
|
||||
"-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",
|
||||
"-DYOSYS_BUILD_PYTHON_ONLY=ON",
|
||||
*cmake_options,
|
||||
*MACOSX_DEPLOYMENT_TARGET_FLAGS,
|
||||
])
|
||||
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.10"),
|
||||
], 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", cmake_options):
|
||||
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,11 +1,13 @@
|
|||
[build-system]
|
||||
requires = [
|
||||
"setuptools>=42",
|
||||
"wheel",
|
||||
"packaging",
|
||||
"pybind11>=3,<4",
|
||||
"cxxheaderparser"
|
||||
"cxxheaderparser",
|
||||
]
|
||||
build-backend = "setuptools.build_meta"
|
||||
backend-path = ["pyosys/build"]
|
||||
build-backend = "local_backend"
|
||||
|
||||
[tool.ruff]
|
||||
target-version = "py38"
|
||||
target-version = "py310"
|
||||
lint.ignore = ["F541"]
|
||||
|
|
|
|||
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…
Reference in New Issue