diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 1bc24496..d8cb82f0 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -72,8 +72,8 @@ repos: hooks: - id: cmake-format additional_dependencies: [pyyaml] - - id: cmake-lint - additional_dependencies: [pyyaml] + types: [file] + files: (\.cmake|CMakeLists.txt)(.in)?$ # Clang-format the C++ part of the code base automatically - repo: https://github.com/pre-commit/mirrors-clang-format @@ -95,7 +95,7 @@ repos: hooks: - id: mypy files: ^(src/mqt|test/python) - args: ["--enable-incomplete-feature=Unpack"] + args: [] additional_dependencies: - importlib_resources - numpy diff --git a/CMakeLists.txt b/CMakeLists.txt index 4d81788f..2adff484 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -2,23 +2,10 @@ cmake_minimum_required(VERSION 3.19...3.28) project( - qcec + mqt-qcec LANGUAGES CXX DESCRIPTION "MQT QCEC - A tool for Quantum Circuit Equivalence Checking") -# check whether `modulename` is correctly cloned in the `extern` directory. -macro(CHECK_SUBMODULE_PRESENT modulename) - if(NOT EXISTS "${PROJECT_SOURCE_DIR}/extern/${modulename}/CMakeLists.txt") - message( - FATAL_ERROR - "${modulename} submodule not cloned properly. \ - Please run `git submodule update --init --recursive` \ - from the main project directory") - endif() -endmacro() - -check_submodule_present(mqt-core) - option(BUILD_MQT_QCEC_TESTS "Also build tests for the MQT QCEC project" ON) option(BUILD_MQT_QCEC_BINDINGS "Build the MQT QCEC Python bindings" OFF) @@ -26,17 +13,20 @@ if(BUILD_MQT_QCEC_BINDINGS) # ensure that the BINDINGS option is set set(BINDINGS ON - CACHE BOOL "Enable settings related to Python bindings" FORCE) - # cmake-lint: disable=C0103 + CACHE INTERNAL "Enable settings related to Python bindings") + # Some common settings for finding Python set(Python_FIND_VIRTUALENV FIRST CACHE STRING "Give precedence to virtualenvs when searching for Python") - # cmake-lint: disable=C0103 + set(Python_FIND_FRAMEWORK + LAST + CACHE STRING "Prefer Brew/Conda to Apple framework Python") set(Python_ARTIFACTS_INTERACTIVE ON CACHE BOOL "Prevent multiple searches for Python and instead cache the results.") + # top-level call to find Python find_package( Python 3.8 REQUIRED @@ -55,3 +45,10 @@ if(BUILD_MQT_QCEC_TESTS) include(GoogleTest) add_subdirectory(test) endif() + +configure_file( + ${CMAKE_CURRENT_SOURCE_DIR}/cmake/cmake_uninstall.cmake.in + ${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake IMMEDIATE @ONLY) +add_custom_target( + uninstall COMMAND ${CMAKE_COMMAND} -P + ${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake) diff --git a/cmake/ExternalDependencies.cmake b/cmake/ExternalDependencies.cmake index a2ef920d..724b287a 100644 --- a/cmake/ExternalDependencies.cmake +++ b/cmake/ExternalDependencies.cmake @@ -3,80 +3,88 @@ include(FetchContent) set(FETCH_PACKAGES "") -# A macro to declare a dependency that takes into account the different CMake -# versions and the features that they make available. In particular: - CMake -# 3.24 introduced the `FIND_PACKAGE_ARGS` option to `FetchContent` which allows -# to combine `FetchContent_Declare` and `find_package` in a single call. - CMake -# 3.25 introduced the `SYSTEM` option to `FetchContent_Declare` which marks the -# dependency as a system dependency. This is useful to avoid compiler warnings -# from external header only libraries. - CMake 3.28 introduced the -# `EXCLUDE_FROM_ALL` option to `FetchContent_Declare` which allows to exclude -# all targets from the dependency from the `all` target. -macro(DECLARE_DEPENDENCY) - cmake_parse_arguments(DEPENDENCY "SYSTEM;EXCLUDE_FROM_ALL" - "NAME;URL;MD5;MIN_VERSION;ALT_NAME" "" ${ARGN}) - set(ADDITIONAL_OPTIONS "") - if(DEPENDENCY_SYSTEM AND CMAKE_VERSION VERSION_GREATER_EQUAL 3.25) - list(APPEND ADDITIONAL_OPTIONS SYSTEM) - endif() - if(DEPENDENCY_EXCLUDE_FROM_ALL AND CMAKE_VERSION VERSION_GREATER_EQUAL 3.28) - list(APPEND ADDITIONAL_OPTIONS EXCLUDE_FROM_ALL) +if(BUILD_MQT_QCEC_BINDINGS) + if(NOT SKBUILD) + # Manually detect the installed pybind11 package. + execute_process( + COMMAND "${Python_EXECUTABLE}" -m pybind11 --cmakedir + OUTPUT_STRIP_TRAILING_WHITESPACE + OUTPUT_VARIABLE pybind11_DIR) + + # Add the detected directory to the CMake prefix path. + list(APPEND CMAKE_PREFIX_PATH "${pybind11_DIR}") endif() - if(CMAKE_VERSION VERSION_GREATER_EQUAL 3.28) - FetchContent_Declare( - ${DEPENDENCY_NAME} - URL ${DEPENDENCY_URL} - URL_MD5 ${DEPENDENCY_MD5} - ${ADDITIONAL_OPTIONS} FIND_PACKAGE_ARGS ${DEPENDENCY_MIN_VERSION} NAMES - ${DEPENDENCY_ALT_NAME}) - list(APPEND FETCH_PACKAGES ${DEPENDENCY_NAME}) - elseif(CMAKE_VERSION VERSION_GREATER_EQUAL 3.25) - FetchContent_Declare( - ${DEPENDENCY_NAME} - URL ${DEPENDENCY_URL} - URL_MD5 ${DEPENDENCY_MD5} - ${ADDITIONAL_OPTIONS} FIND_PACKAGE_ARGS ${DEPENDENCY_MIN_VERSION} NAMES - ${DEPENDENCY_ALT_NAME}) - list(APPEND FETCH_PACKAGES ${DEPENDENCY_NAME}) - elseif(CMAKE_VERSION VERSION_GREATER_EQUAL 3.24) + + # add pybind11 library + find_package(pybind11 CONFIG REQUIRED) +endif() + +set(FETCHCONTENT_SOURCE_DIR_MQT-CORE + ${PROJECT_SOURCE_DIR}/extern/mqt-core + CACHE + PATH + "Path to the source directory of the mqt-core library. This variable is used by FetchContent to download the library if it is not already available." +) +set(MQT_CORE_VERSION + 2.2.2 + CACHE STRING "MQT Core version") +if(CMAKE_VERSION VERSION_GREATER_EQUAL 3.24) + FetchContent_Declare( + mqt-core + GIT_REPOSITORY https://github.com/cda-tum/mqt-core.git + GIT_TAG v${MQT_CORE_VERSION} + FIND_PACKAGE_ARGS ${MQT_CORE_VERSION}) + list(APPEND FETCH_PACKAGES mqt-core) +else() + find_package(mqt-core ${MQT_CORE_VERSION} QUIET) + if(NOT mqt-core_FOUND) FetchContent_Declare( - ${DEPENDENCY_NAME} - URL ${DEPENDENCY_URL} - URL_MD5 ${DEPENDENCY_MD5} - ${ADDITIONAL_OPTIONS} FIND_PACKAGE_ARGS ${DEPENDENCY_MIN_VERSION} NAMES - ${DEPENDENCY_ALT_NAME}) - list(APPEND FETCH_PACKAGES ${DEPENDENCY_NAME}) - else() - # try to get the system installed version - find_package(${DEPENDENCY_NAME} ${DEPENDENCY_MIN_VERSION} QUIET NAMES - ${DEPENDENCY_ALT_NAME}) - if(NOT ${DEPENDENCY_NAME}_FOUND) - FetchContent_Declare( - ${DEPENDENCY_NAME} - URL ${DEPENDENCY_URL} - URL_MD5 ${DEPENDENCY_MD5}) - list(APPEND FETCH_PACKAGES ${DEPENDENCY_NAME}) - endif() + mqt-core + GIT_REPOSITORY https://github.com/cda-tum/mqt-core.git + GIT_TAG v${MQT_CORE_VERSION}) + list(APPEND FETCH_PACKAGES mqt-core) endif() -endmacro() +endif() if(BUILD_MQT_QCEC_TESTS) - set(gtest_force_shared_crt # cmake-lint: disable=C0103 + set(gtest_force_shared_crt ON CACHE BOOL "" FORCE) - declare_dependency( - NAME - googletest - URL - https://github.com/google/googletest/archive/refs/tags/v1.14.0.tar.gz - MD5 - c8340a482851ef6a3fe618a082304cfc - MIN_VERSION - 1.14.0 - ALT_NAME - GTest - SYSTEM - EXCLUDE_FROM_ALL) + set(GTEST_VERSION + 1.14.0 + CACHE STRING "Google Test version") + set(GTEST_URL + https://github.com/google/googletest/archive/refs/tags/v${GTEST_VERSION}.tar.gz + ) + if(CMAKE_VERSION VERSION_GREATER_EQUAL 3.24) + FetchContent_Declare(googletest URL ${GTEST_URL} FIND_PACKAGE_ARGS + ${GTEST_VERSION} NAMES GTest) + list(APPEND FETCH_PACKAGES googletest) + else() + find_package(googletest ${GTEST_VERSION} QUIET NAMES GTest) + if(NOT googletest_FOUND) + FetchContent_Declare(googletest URL ${GTEST_URL}) + list(APPEND FETCH_PACKAGES googletest) + endif() + endif() +endif() + +if(BUILD_MQT_QCEC_BINDINGS) + # add pybind11_json library + if(CMAKE_VERSION VERSION_GREATER_EQUAL 3.24) + FetchContent_Declare( + pybind11_json + GIT_REPOSITORY https://github.com/pybind/pybind11_json + FIND_PACKAGE_ARGS) + list(APPEND FETCH_PACKAGES pybind11_json) + else() + find_package(pybind11_json QUIET) + if(NOT pybind11_json_FOUND) + FetchContent_Declare( + pybind11_json GIT_REPOSITORY https://github.com/pybind/pybind11_json) + list(APPEND FETCH_PACKAGES pybind11_json) + endif() + endif() endif() # Make all declared dependencies available. diff --git a/cmake/cmake_uninstall.cmake.in b/cmake/cmake_uninstall.cmake.in new file mode 100644 index 00000000..629772f8 --- /dev/null +++ b/cmake/cmake_uninstall.cmake.in @@ -0,0 +1,26 @@ +# Source: +# https://gitlab.kitware.com/cmake/community/-/wikis/FAQ#can-i-do-make-uninstall-with-cmake + +if(NOT EXISTS "@CMAKE_BINARY_DIR@/install_manifest.txt") + message( + FATAL_ERROR + "Cannot find install manifest: @CMAKE_BINARY_DIR@/install_manifest.txt") +endif() + +file(READ "@CMAKE_BINARY_DIR@/install_manifest.txt" files) +string(REGEX REPLACE "\n" ";" files "${files}") +foreach(file ${files}) + message(STATUS "Uninstalling $ENV{DESTDIR}${file}") + if(IS_SYMLINK "$ENV{DESTDIR}${file}" OR EXISTS "$ENV{DESTDIR}${file}") + exec_program( + "@CMAKE_COMMAND@" ARGS + "-E remove \"$ENV{DESTDIR}${file}\"" + OUTPUT_VARIABLE rm_out + RETURN_VALUE rm_retval) + if(NOT "${rm_retval}" STREQUAL 0) + message(FATAL_ERROR "Problem when removing $ENV{DESTDIR}${file}") + endif() + else(IS_SYMLINK "$ENV{DESTDIR}${file}" OR EXISTS "$ENV{DESTDIR}${file}") + message(STATUS "File $ENV{DESTDIR}${file} does not exist.") + endif() +endforeach() diff --git a/extern/mqt-core b/extern/mqt-core index 8294bc9a..3d5e14b2 160000 --- a/extern/mqt-core +++ b/extern/mqt-core @@ -1 +1 @@ -Subproject commit 8294bc9aef2a2022300ef61a272f27062df33c41 +Subproject commit 3d5e14b20809108b90de6df03fa25def3d2851c9 diff --git a/noxfile.py b/noxfile.py index 7793ff82..f6ef02ce 100644 --- a/noxfile.py +++ b/noxfile.py @@ -12,7 +12,7 @@ if TYPE_CHECKING: from collections.abc import Sequence -nox.options.sessions = ["lint", "pylint", "tests"] +nox.options.sessions = ["lint", "tests"] PYTHON_ALL_VERSIONS = ["3.8", "3.9", "3.10", "3.11", "3.12"] diff --git a/pyproject.toml b/pyproject.toml index 5465b410..f325710c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -72,8 +72,8 @@ Discussions = "https://github.com/cda-tum/mqt-qcec/discussions" # Protect the configuration against future changes in scikit-build-core minimum-version = "0.6.1" -# Set the target to build -cmake.targets = ["pyqcec"] +# Set the wheel install directory +wheel.install-dir = "mqt/qcec" # Set required CMake and Ninja versions cmake.minimum-version = "3.19" @@ -82,9 +82,6 @@ ninja.minimum-version = "1.10" # Setuptools-style build caching in a local directory build-dir = "build/{wheel_tag}" -# Build stable ABI wheels for CPython 3.12+ -wheel.py-api = "cp312" - # Explicitly set the package directory wheel.packages = ["src/mqt"] @@ -141,6 +138,8 @@ run.omit = ["src/mqt/qcec/types.py"] report.exclude_also = [ '\.\.\.', 'if TYPE_CHECKING:', + 'raise AssertionError', + 'raise NotImplementedError', ] @@ -148,12 +147,12 @@ report.exclude_also = [ files = ["src/mqt", "test/python"] mypy_path = ["$MYPY_CONFIG_FILE_DIR/src"] python_version = "3.8" -strict = true -show_error_codes = true +warn_unused_configs = true enable_error_code = ["ignore-without-code", "redundant-expr", "truthy-bool"] -warn_unreachable = true +strict = true +disallow_untyped_defs = false explicit_package_bases = true -pretty = true +warn_unreachable = true [[tool.mypy.overrides]] module = ["qiskit.*"] @@ -240,7 +239,6 @@ build = "cp3*" skip = "*-musllinux_*" archs = "auto64" test-command = "python -c \"from mqt import qcec\"" -test-skip = "cp312-*" # Qiskit Terra does not support Python 3.12 yet build-frontend = "build" [tool.cibuildwheel.linux] diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 6a6ba080..c929aedd 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1,10 +1,3 @@ -# add MQT::Core target -set(BUILD_MQT_CORE_TESTS - OFF - CACHE BOOL "Build MQT Core tests") -add_subdirectory("${PROJECT_SOURCE_DIR}/extern/mqt-core" "extern/mqt-core" - EXCLUDE_FROM_ALL) - add_library( ${PROJECT_NAME} ${PROJECT_SOURCE_DIR}/include/checker @@ -28,9 +21,11 @@ target_include_directories(${PROJECT_NAME} PUBLIC ${PROJECT_SOURCE_DIR}/include # link to the MQT::Core libraries target_link_libraries(${PROJECT_NAME} PUBLIC MQT::CoreDD MQT::CoreZX) +target_link_libraries(${PROJECT_NAME} PRIVATE MQT::ProjectWarnings + MQT::ProjectOptions) # add MQT alias -add_library(MQT::${PROJECT_NAME} ALIAS ${PROJECT_NAME}) +add_library(MQT::QCEC ALIAS ${PROJECT_NAME}) if(BUILD_MQT_QCEC_BINDINGS) add_subdirectory(python) diff --git a/src/python/CMakeLists.txt b/src/python/CMakeLists.txt index a20e1379..3b616099 100644 --- a/src/python/CMakeLists.txt +++ b/src/python/CMakeLists.txt @@ -1,43 +1,13 @@ -if(NOT SKBUILD) - message( - NOTICE - "\ - This CMake file is meant to be executed using 'scikit-build'. Running - it directly will almost certainly not produce the desired result. If - you are a user trying to install this package, please use the command - below, which will install all necessary build dependencies, compile - the package in an isolated environment, and then install it. - ===================================================================== - $ pip install . - ===================================================================== - If you are a software developer, and this is your own package, then - it is usually much more efficient to install the build dependencies - in your environment once and use the following command that avoids - a costly creation of a new virtual environment at every compilation: - ===================================================================== - $ pip install 'scikit-build-core[pyproject]' setuptools_scm pybind11 - $ pip install --no-build-isolation -ve . - ===================================================================== - You may optionally add -Ceditable.rebuild=true to auto-rebuild when - the package is imported. Otherwise, you need to re-run the above - after editing C++ files.") -endif() - -if(NOT SKBUILD) - # Manually detect the installed pybind11 package and import it into CMake. - execute_process( - COMMAND "${Python_EXECUTABLE}" -m pybind11 --cmakedir - OUTPUT_STRIP_TRAILING_WHITESPACE - OUTPUT_VARIABLE pybind11_DIR) - list(APPEND CMAKE_PREFIX_PATH "${pybind11_DIR}") -endif() - -# Import pybind11 through CMake's find_package mechanism -find_package(pybind11 CONFIG REQUIRED) - -# We are now ready to compile the actual extension module -pybind11_add_module(py${PROJECT_NAME} bindings.cpp) -target_link_libraries(py${PROJECT_NAME} PRIVATE ${PROJECT_NAME} MQT::CorePython) +pybind11_add_module( + pyqcec + # Prefer thin LTO if available + THIN_LTO + # Optimize the bindings for size + OPT_SIZE + # Source code goes here + bindings.cpp) +target_link_libraries(pyqcec PRIVATE MQT::QCEC MQT::CorePython pybind11_json + MQT::ProjectOptions MQT::ProjectWarnings) # Install directive for scikit-build-core -install(TARGETS py${PROJECT_NAME} LIBRARY DESTINATION mqt/qcec) +install(TARGETS pyqcec LIBRARY DESTINATION .) diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 8b65e5de..2d1374f1 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -1,6 +1,6 @@ package_add_test( - ${PROJECT_NAME}_test - ${PROJECT_NAME} + mqt-qcec-test + MQT::QCEC legacy/test_functionality.cpp legacy/test_journal.cpp legacy/test_compilationflow.cpp