diff --git a/.gitmodules b/.gitmodules index bd24c247..3a22d917 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,9 +1,9 @@ [submodule "pybmix/src/bayesmix"] - path = pybmix/core/pybmixcpp/bayesmix + path = pybmixlib/bayesmix url = https://github.com/bayesmix-dev/bayesmix.git depth = 1 update = merge [submodule "lib/pybind11"] - path = lib/pybind11 + path = pybmixlib/pybind11 depth = 1 url = https://github.com/pybind/pybind11.git diff --git a/CMakeLists.txt b/CMakeLists.txt index 3d0b208f..3bdfffc3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,27 +1,24 @@ cmake_minimum_required(VERSION 3.13.0) -project(pybmixcpp) +project(pybmix) -set (CMAKE_CXX_STANDARD 14) -set(COMPILE_OPTIONS -D_REENTRANT -fPIC) -set(CMAKE_BUILD_TYPE Release) -set(CMAKE_CXX_FLAGS_RELEASE "-O3 -funroll-loops -ftree-vectorize") -set(CMAKE_FIND_PACKAGE_PREFER_CONFIG TRUE) +SET(SOURCE_DIR "${CMAKE_CURRENT_LIST_DIR}/pybmixlib/bayesmix") +option(DISABLE_TESTS ON) +option(DISABLE_BENCHMARKS ON) +option(DISABLE_DOCS ON) +option(DISABLE_PLOTS ON) +option(DISABLE_EXAMPLES ON) +option(BUILD_RUN OFF) -SET(SOURCE_DIR "${CMAKE_CURRENT_LIST_DIR}/pybmix/core/pybmixcpp") -add_subdirectory(${SOURCE_DIR}/bayesmix) +add_subdirectory(${SOURCE_DIR}) include_directories(${SOURCE_DIR}) SET_SOURCE_FILES_PROPERTIES(${PROTO_HEADERS} ${PROTO_SOURCES} PROPERTIES GENERATED TRUE) -# Generate python module -add_subdirectory(lib/pybind11) -pybind11_add_module(pybmixcpp ${SOURCES} - "${SOURCE_DIR}/module.cpp" - "${SOURCE_DIR}/algorithm_wrapper.hpp" - "${SOURCE_DIR}/algorithm_wrapper.cpp" - "${SOURCE_DIR}/serialized_collector.hpp" - "${SOURCE_DIR}/serialized_collector.cpp" - ${PROTO_HEADERS} ${PROTO_SOURCES}) +message ("*** PROTOC " protobuf::protoc) +message ("*** Protobuf_VERSION " ${Protobuf_VERSION}) +message ("*** Protobuf_PROTOC_LIBRARIES " ${Protobuf_PROTOC_LIBRARIES}) + + # generate Python's proto classes set(PY_PROTO_DIR "${CMAKE_CURRENT_LIST_DIR}/pybmix/proto") @@ -34,7 +31,7 @@ foreach(PROTO_FILE IN LISTS ProtoFiles) message(STATUS "GENERATING PYTHON protoc proto(cc): ${PROTO_FILE} --> ${PROTO_PY}") add_custom_command( OUTPUT ${PROTO_PY} - COMMAND protobuf::protoc "--proto_path=${SOURCE_DIR}/bayesmix/src/proto" + COMMAND /usr/local/bin/protoc "--proto_path=${SOURCE_DIR}/src/proto" ${PROTO_DIRS} "--python_out=${PY_PROTO_DIR}" ${PROTO_FILE} DEPENDS ${PROTO_FILE} protobuf::protoc COMMENT "Generate Python protocol buffer for ${PROTO_FILE}" @@ -42,14 +39,7 @@ foreach(PROTO_FILE IN LISTS ProtoFiles) list(APPEND PROTO_PYS ${PROTO_PY}) endforeach() -find_package (TBB EXACT PATHS ${TBB_ROOT}) -find_package(OpenMP REQUIRED) - -message("INCLUDE_PATHS" ${INCLUDE_PATHS}) - -target_include_directories(pybmixcpp PUBLIC ${BAYESMIX_INCLUDE_PATHS}) -target_compile_definitions(pybmixcpp PRIVATE VERSION_INFO=${EXAMPLE_VERSION_INFO}) -target_link_libraries(pybmixcpp PUBLIC bayesmixlib ${BAYESMIX_LINK_LIBRARIES}) -target_compile_options(pybmixcpp PUBLIC ${BAYESMIX_COMPILE_OPTIONS}) +SET_SOURCE_FILES_PROPERTIES(${PROTO_PYS} PROPERTIES GENERATED TRUE) -add_custom_target(genterate_protos ALL DEPENDS ${PROTO_PYS}) +add_custom_target(generate_protos ALL DEPENDS ${PROTO_PYS}) +add_custom_target(two_to_three ALL COMMAND ${CMAKE_CURRENT_LIST_DIR}/convert_proto.sh DEPENDS generate_protos) diff --git a/docs/examples/clustering_univ_data.ipynb b/docs/examples/clustering_univ_data.ipynb index 90a669d7..455ec1ef 100644 --- a/docs/examples/clustering_univ_data.ipynb +++ b/docs/examples/clustering_univ_data.ipynb @@ -16,9 +16,23 @@ }, { "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], + "execution_count": 1, + "metadata": {}, + "outputs": [ + { + "ename": "AttributeError", + "evalue": "'NoneType' object has no attribute 'enum_types_by_name'", + "output_type": "error", + "traceback": [ + "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[0;31mAttributeError\u001b[0m Traceback (most recent call last)", + "Input \u001b[0;32mIn [1]\u001b[0m, in \u001b[0;36m\u001b[0;34m()\u001b[0m\n\u001b[1;32m 1\u001b[0m \u001b[38;5;28;01mimport\u001b[39;00m \u001b[38;5;21;01mnumpy\u001b[39;00m \u001b[38;5;28;01mas\u001b[39;00m \u001b[38;5;21;01mnp\u001b[39;00m\n\u001b[1;32m 2\u001b[0m \u001b[38;5;28;01mimport\u001b[39;00m \u001b[38;5;21;01mmatplotlib\u001b[39;00m\u001b[38;5;21;01m.\u001b[39;00m\u001b[38;5;21;01mpyplot\u001b[39;00m \u001b[38;5;28;01mas\u001b[39;00m \u001b[38;5;21;01mplt\u001b[39;00m\n\u001b[0;32m----> 4\u001b[0m \u001b[38;5;28;01mfrom\u001b[39;00m \u001b[38;5;21;01mpybmix\u001b[39;00m\u001b[38;5;21;01m.\u001b[39;00m\u001b[38;5;21;01mcore\u001b[39;00m\u001b[38;5;21;01m.\u001b[39;00m\u001b[38;5;21;01mmixing\u001b[39;00m \u001b[38;5;28;01mimport\u001b[39;00m DirichletProcessMixing, StickBreakMixing\n\u001b[1;32m 5\u001b[0m \u001b[38;5;28;01mfrom\u001b[39;00m \u001b[38;5;21;01mpybmix\u001b[39;00m\u001b[38;5;21;01m.\u001b[39;00m\u001b[38;5;21;01mcore\u001b[39;00m\u001b[38;5;21;01m.\u001b[39;00m\u001b[38;5;21;01mhierarchy\u001b[39;00m \u001b[38;5;28;01mimport\u001b[39;00m UnivariateNormal\n\u001b[1;32m 6\u001b[0m \u001b[38;5;28;01mfrom\u001b[39;00m \u001b[38;5;21;01mpybmix\u001b[39;00m\u001b[38;5;21;01m.\u001b[39;00m\u001b[38;5;21;01mcore\u001b[39;00m\u001b[38;5;21;01m.\u001b[39;00m\u001b[38;5;21;01mmixture_model\u001b[39;00m \u001b[38;5;28;01mimport\u001b[39;00m MixtureModel\n", + "File \u001b[0;32m~/dev/tests/pybmix/pybmix/core/mixing.py:8\u001b[0m, in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[1;32m 5\u001b[0m \u001b[38;5;28;01mfrom\u001b[39;00m \u001b[38;5;21;01mjoblib\u001b[39;00m \u001b[38;5;28;01mimport\u001b[39;00m Parallel, delayed\n\u001b[1;32m 6\u001b[0m \u001b[38;5;28;01mfrom\u001b[39;00m \u001b[38;5;21;01mscipy\u001b[39;00m\u001b[38;5;21;01m.\u001b[39;00m\u001b[38;5;21;01mspecial\u001b[39;00m \u001b[38;5;28;01mimport\u001b[39;00m loggamma, gamma\n\u001b[0;32m----> 8\u001b[0m \u001b[38;5;28;01mimport\u001b[39;00m \u001b[38;5;21;01mpybmix\u001b[39;00m\u001b[38;5;21;01m.\u001b[39;00m\u001b[38;5;21;01mproto\u001b[39;00m\u001b[38;5;21;01m.\u001b[39;00m\u001b[38;5;21;01mmixing_id_pb2\u001b[39;00m \u001b[38;5;28;01mas\u001b[39;00m \u001b[38;5;21;01mmixing_id\u001b[39;00m\n\u001b[1;32m 9\u001b[0m \u001b[38;5;28;01mfrom\u001b[39;00m \u001b[38;5;21;01mpybmix\u001b[39;00m\u001b[38;5;21;01m.\u001b[39;00m\u001b[38;5;21;01mproto\u001b[39;00m\u001b[38;5;21;01m.\u001b[39;00m\u001b[38;5;21;01mdistribution_pb2\u001b[39;00m \u001b[38;5;28;01mimport\u001b[39;00m BetaDistribution, GammaDistribution\n\u001b[1;32m 10\u001b[0m \u001b[38;5;28;01mfrom\u001b[39;00m \u001b[38;5;21;01mpybmix\u001b[39;00m\u001b[38;5;21;01m.\u001b[39;00m\u001b[38;5;21;01mproto\u001b[39;00m\u001b[38;5;21;01m.\u001b[39;00m\u001b[38;5;21;01mmixing_prior_pb2\u001b[39;00m \u001b[38;5;28;01mimport\u001b[39;00m DPPrior, PYPrior, TruncSBPrior\n", + "File \u001b[0;32m~/dev/tests/pybmix/pybmix/proto/mixing_id_pb2.py:20\u001b[0m, in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[1;32m 13\u001b[0m _sym_db \u001b[38;5;241m=\u001b[39m _symbol_database\u001b[38;5;241m.\u001b[39mDefault()\n\u001b[1;32m 18\u001b[0m DESCRIPTOR \u001b[38;5;241m=\u001b[39m _descriptor_pool\u001b[38;5;241m.\u001b[39mDefault()\u001b[38;5;241m.\u001b[39mAddSerializedFile(\u001b[38;5;124mb\u001b[39m\u001b[38;5;124m'\u001b[39m\u001b[38;5;130;01m\\n\u001b[39;00m\u001b[38;5;130;01m\\x0f\u001b[39;00m\u001b[38;5;124mmixing_id.proto\u001b[39m\u001b[38;5;130;01m\\x12\u001b[39;00m\u001b[38;5;130;01m\\x08\u001b[39;00m\u001b[38;5;130;01m\\x62\u001b[39;00m\u001b[38;5;130;01m\\x61\u001b[39;00m\u001b[38;5;124myesmix*O\u001b[39m\u001b[38;5;130;01m\\n\u001b[39;00m\u001b[38;5;130;01m\\x08\u001b[39;00m\u001b[38;5;124mMixingId\u001b[39m\u001b[38;5;130;01m\\x12\u001b[39;00m\u001b[38;5;130;01m\\x12\u001b[39;00m\u001b[38;5;130;01m\\n\u001b[39;00m\u001b[38;5;130;01m\\x0e\u001b[39;00m\u001b[38;5;124mUNKNOWN_MIXING\u001b[39m\u001b[38;5;130;01m\\x10\u001b[39;00m\u001b[38;5;130;01m\\x00\u001b[39;00m\u001b[38;5;130;01m\\x12\u001b[39;00m\u001b[38;5;130;01m\\x06\u001b[39;00m\u001b[38;5;130;01m\\n\u001b[39;00m\u001b[38;5;130;01m\\x02\u001b[39;00m\u001b[38;5;130;01m\\x44\u001b[39;00m\u001b[38;5;124mP\u001b[39m\u001b[38;5;130;01m\\x10\u001b[39;00m\u001b[38;5;130;01m\\x01\u001b[39;00m\u001b[38;5;130;01m\\x12\u001b[39;00m\u001b[38;5;130;01m\\x06\u001b[39;00m\u001b[38;5;130;01m\\n\u001b[39;00m\u001b[38;5;130;01m\\x02\u001b[39;00m\u001b[38;5;124mPY\u001b[39m\u001b[38;5;130;01m\\x10\u001b[39;00m\u001b[38;5;130;01m\\x02\u001b[39;00m\u001b[38;5;130;01m\\x12\u001b[39;00m\u001b[38;5;130;01m\\t\u001b[39;00m\u001b[38;5;130;01m\\n\u001b[39;00m\u001b[38;5;130;01m\\x05\u001b[39;00m\u001b[38;5;124mLogSB\u001b[39m\u001b[38;5;130;01m\\x10\u001b[39;00m\u001b[38;5;130;01m\\x03\u001b[39;00m\u001b[38;5;130;01m\\x12\u001b[39;00m\u001b[38;5;130;01m\\x0b\u001b[39;00m\u001b[38;5;130;01m\\n\u001b[39;00m\u001b[38;5;130;01m\\x07\u001b[39;00m\u001b[38;5;124mTruncSB\u001b[39m\u001b[38;5;130;01m\\x10\u001b[39;00m\u001b[38;5;130;01m\\x04\u001b[39;00m\u001b[38;5;130;01m\\x12\u001b[39;00m\u001b[38;5;130;01m\\x07\u001b[39;00m\u001b[38;5;130;01m\\n\u001b[39;00m\u001b[38;5;130;01m\\x03\u001b[39;00m\u001b[38;5;124mMFM\u001b[39m\u001b[38;5;130;01m\\x10\u001b[39;00m\u001b[38;5;130;01m\\x05\u001b[39;00m\u001b[38;5;130;01m\\x62\u001b[39;00m\u001b[38;5;130;01m\\x06\u001b[39;00m\u001b[38;5;124mproto3\u001b[39m\u001b[38;5;124m'\u001b[39m)\n\u001b[0;32m---> 20\u001b[0m _MIXINGID \u001b[38;5;241m=\u001b[39m \u001b[43mDESCRIPTOR\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43menum_types_by_name\u001b[49m[\u001b[38;5;124m'\u001b[39m\u001b[38;5;124mMixingId\u001b[39m\u001b[38;5;124m'\u001b[39m]\n\u001b[1;32m 21\u001b[0m MixingId \u001b[38;5;241m=\u001b[39m enum_type_wrapper\u001b[38;5;241m.\u001b[39mEnumTypeWrapper(_MIXINGID)\n\u001b[1;32m 22\u001b[0m UNKNOWN_MIXING \u001b[38;5;241m=\u001b[39m \u001b[38;5;241m0\u001b[39m\n", + "\u001b[0;31mAttributeError\u001b[0m: 'NoneType' object has no attribute 'enum_types_by_name'" + ] + } + ], "source": [ "import numpy as np\n", "import matplotlib.pyplot as plt\n", @@ -215,7 +229,7 @@ ], "metadata": { "kernelspec": { - "display_name": "Python 3", + "display_name": "Python 3 (ipykernel)", "language": "python", "name": "python3" }, @@ -229,7 +243,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.8.0" + "version": "3.10.2" } }, "nbformat": 4, diff --git a/pybmix/core/mixture_model.py b/pybmix/core/mixture_model.py index dfa55b3e..f5dcd001 100644 --- a/pybmix/core/mixture_model.py +++ b/pybmix/core/mixture_model.py @@ -6,7 +6,7 @@ from pybmix.core.hierarchy import BaseHierarchy from pybmix.core.chain import MCMCchain from pybmix.proto.algorithm_state_pb2 import AlgorithmState -from pybmix.core.pybmixcpp import AlgorithmWrapper, ostream_redirect +from pybmixlib import AlgorithmWrapper, ostream_redirect MARGINAL_ALGORITHMS = ["Neal2", "Neal3", "Neal8"] CONDITIONAL_ALGORITHMS = ["BlockedGibbs"] diff --git a/pybmix/core/pybmixcpp/bayesmix b/pybmix/core/pybmixcpp/bayesmix deleted file mode 160000 index 98e8e624..00000000 --- a/pybmix/core/pybmixcpp/bayesmix +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 98e8e6244183c6d04c8f8667640918712589603e diff --git a/pybmix/estimators/cluster_estimator.py b/pybmix/estimators/cluster_estimator.py index 8dadc719..06305e27 100644 --- a/pybmix/estimators/cluster_estimator.py +++ b/pybmix/estimators/cluster_estimator.py @@ -1,7 +1,7 @@ import numpy as np from pybmix.core.mixture_model import MixtureModel -from pybmix.core.pybmixcpp import _minbinder_cluster_estimate, ostream_redirect +from pybmixlib import _minbinder_cluster_estimate, ostream_redirect class ClusterEstimator(object): """ diff --git a/pybmix/core/pybmixcpp/algorithm_wrapper.cpp b/pybmixlib/algorithm_wrapper.cpp similarity index 100% rename from pybmix/core/pybmixcpp/algorithm_wrapper.cpp rename to pybmixlib/algorithm_wrapper.cpp diff --git a/pybmix/core/pybmixcpp/algorithm_wrapper.hpp b/pybmixlib/algorithm_wrapper.hpp similarity index 98% rename from pybmix/core/pybmixcpp/algorithm_wrapper.hpp rename to pybmixlib/algorithm_wrapper.hpp index 535576c8..19349116 100644 --- a/pybmix/core/pybmixcpp/algorithm_wrapper.hpp +++ b/pybmixlib/algorithm_wrapper.hpp @@ -1,6 +1,7 @@ #ifndef PYBMIX_ALGORITHM_WRAPPER_ #define PYBMIX_ALGORITHM_WRAPPER_ +#include #include #include #include diff --git a/pybmixlib/bayesmix b/pybmixlib/bayesmix new file mode 160000 index 00000000..fd27aff5 --- /dev/null +++ b/pybmixlib/bayesmix @@ -0,0 +1 @@ +Subproject commit fd27aff55fca9b519ffbf74141333482aed1623a diff --git a/pybmix/core/pybmixcpp/example.cpp b/pybmixlib/example.cpp similarity index 92% rename from pybmix/core/pybmixcpp/example.cpp rename to pybmixlib/example.cpp index fa6766c3..a8272016 100644 --- a/pybmix/core/pybmixcpp/example.cpp +++ b/pybmixlib/example.cpp @@ -1,8 +1,10 @@ -#include +#include + +// #include #include #include -#include +// #include #include "algorithm_wrapper.hpp" #include "bayesmix/src/utils/distributions.hpp" diff --git a/pybmix/core/pybmixcpp/module.cpp b/pybmixlib/module.cpp similarity index 86% rename from pybmix/core/pybmixcpp/module.cpp rename to pybmixlib/module.cpp index 34894b87..7d4fa46f 100644 --- a/pybmix/core/pybmixcpp/module.cpp +++ b/pybmixlib/module.cpp @@ -1,4 +1,4 @@ -#include +// #include #include #include #include @@ -9,7 +9,7 @@ namespace py = pybind11; -PYBIND11_MODULE(pybmixcpp, m) { +PYBIND11_MODULE(pybmixlib, m) { py::add_ostream_redirect(m, "ostream_redirect"); add_algorithm_wrapper(m); add_serialized_collector(m); diff --git a/lib/pybind11 b/pybmixlib/pybind11 similarity index 100% rename from lib/pybind11 rename to pybmixlib/pybind11 diff --git a/pybmix/core/pybmixcpp/serialized_collector.cpp b/pybmixlib/serialized_collector.cpp similarity index 100% rename from pybmix/core/pybmixcpp/serialized_collector.cpp rename to pybmixlib/serialized_collector.cpp diff --git a/pybmix/core/pybmixcpp/serialized_collector.hpp b/pybmixlib/serialized_collector.hpp similarity index 100% rename from pybmix/core/pybmixcpp/serialized_collector.hpp rename to pybmixlib/serialized_collector.hpp diff --git a/pybmixlib/setup.py b/pybmixlib/setup.py new file mode 100644 index 00000000..9517cb51 --- /dev/null +++ b/pybmixlib/setup.py @@ -0,0 +1,140 @@ +import os +import re +import subprocess +import sys + +from setuptools import Extension, setup +from setuptools.command.build_ext import build_ext + +# Convert distutils Windows platform specifiers to CMake -A arguments +PLAT_TO_CMAKE = { + "win32": "Win32", + "win-amd64": "x64", + "win-arm32": "ARM", + "win-arm64": "ARM64", +} + + +# A CMakeExtension needs a sourcedir instead of a file list. +# The name must be the _single_ output extension from the CMake build. +# If you need multiple extensions, see scikit-build. +class CMakeExtension(Extension): + def __init__(self, name, sourcedir=""): + Extension.__init__(self, name, sources=[]) + self.sourcedir = os.path.abspath(sourcedir) + + +class CMakeBuild(build_ext): + def build_extension(self, ext): + extdir = os.path.abspath(os.path.dirname(self.get_ext_fullpath(ext.name))) + + # required for auto-detection & inclusion of auxiliary "native" libs + if not extdir.endswith(os.path.sep): + extdir += os.path.sep + + debug = int(os.environ.get("DEBUG", 0)) if self.debug is None else self.debug + cfg = "Debug" if debug else "Release" + + # CMake lets you override the generator - we need to check this. + # Can be set with Conda-Build, for example. + cmake_generator = os.environ.get("CMAKE_GENERATOR", "") + + # Set Python_EXECUTABLE instead if you use PYBIND11_FINDPYTHON + # EXAMPLE_VERSION_INFO shows you how to pass a value into the C++ code + # from Python. + cmake_args = [ + f"-DDISABLE_BENCHMARKS=TRUE", + f"-DDISABLE_PLOTS=TRUE", + f"-DDISABLE_EXAMPLES=TRUE", + f"-DDISABLE_DOCS=TRUE", + f"-DDISABLE_TESTS=TRUE", + f"-DFORCE_PROTO_BUILD=TRUE", + f"-DCMAKE_LIBRARY_OUTPUT_DIRECTORY={extdir}", + f"-DPYTHON_EXECUTABLE={sys.executable}", + f"-DCMAKE_BUILD_TYPE={cfg}", # not used on MSVC, but no harm + ] + build_args = [] + # Adding CMake arguments set as environment variable + # (needed e.g. to build for ARM OSx on conda-forge) + if "CMAKE_ARGS" in os.environ: + cmake_args += [item for item in os.environ["CMAKE_ARGS"].split(" ") if item] + + # In this example, we pass in the version to C++. You might not need to. + cmake_args += [f"-DEXAMPLE_VERSION_INFO={self.distribution.get_version()}"] + + if self.compiler.compiler_type != "msvc": + # Using Ninja-build since it a) is available as a wheel and b) + # multithreads automatically. MSVC would require all variables be + # exported for Ninja to pick it up, which is a little tricky to do. + # Users can override the generator with CMAKE_GENERATOR in CMake + # 3.15+. + if not cmake_generator or cmake_generator == "Ninja": + try: + import ninja # noqa: F401 + + ninja_executable_path = os.path.join(ninja.BIN_DIR, "ninja") + cmake_args += [ + "-GNinja", + f"-DCMAKE_MAKE_PROGRAM:FILEPATH={ninja_executable_path}", + ] + except ImportError: + pass + + else: + + # Single config generators are handled "normally" + single_config = any(x in cmake_generator for x in {"NMake", "Ninja"}) + + # CMake allows an arch-in-generator style for backward compatibility + contains_arch = any(x in cmake_generator for x in {"ARM", "Win64"}) + + # Specify the arch if using MSVC generator, but only if it doesn't + # contain a backward-compatibility arch spec already in the + # generator name. + if not single_config and not contains_arch: + cmake_args += ["-A", PLAT_TO_CMAKE[self.plat_name]] + + # Multi-config generators have a different way to specify configs + if not single_config: + cmake_args += [ + f"-DCMAKE_LIBRARY_OUTPUT_DIRECTORY_{cfg.upper()}={extdir}" + ] + build_args += ["--config", cfg] + + if sys.platform.startswith("darwin"): + # Cross-compile support for macOS - respect ARCHFLAGS if set + archs = re.findall(r"-arch (\S+)", os.environ.get("ARCHFLAGS", "")) + if archs: + cmake_args += ["-DCMAKE_OSX_ARCHITECTURES={}".format(";".join(archs))] + + # Set CMAKE_BUILD_PARALLEL_LEVEL to control the parallel build level + # across all generators. + if "CMAKE_BUILD_PARALLEL_LEVEL" not in os.environ: + # self.parallel is a Python 3 only way to set parallel jobs by hand + # using -j in the build_ext call, not supported by pip or PyPA-build. + if hasattr(self, "parallel") and self.parallel: + # CMake 3.12+ only. + build_args += [f"-j{self.parallel}"] + + build_temp = os.path.join(self.build_temp, ext.name) + if not os.path.exists(build_temp): + os.makedirs(build_temp) + + subprocess.check_call(["cmake", ext.sourcedir] + cmake_args, cwd=build_temp) + subprocess.check_call(["cmake", "--build", "."] + build_args, cwd=build_temp) + + +if __name__ == "__main__": + + setup( + name="pybmixlib", + version="0.0.1", + author="Mario Beraha", + author_email="berahamario@gmail.com", + description="Python Bayesian Mixtures", + long_description="", + ext_modules=[CMakeExtension("pybmixlib")], + cmdclass={"build_ext": CMakeBuild}, + zip_safe=False, + python_requires=">=3.6", + ) \ No newline at end of file diff --git a/setup.py b/setup.py index e55d82e4..6be506e5 100644 --- a/setup.py +++ b/setup.py @@ -1,21 +1,14 @@ -import glob import os -import platform -import sys -import shutil +import re import subprocess +import sys -from setuptools import setup, Extension, find_packages +from distutils.spawn import find_executable +from setuptools import Extension, setup from setuptools.command.build_ext import build_ext -from distutils.command.build_py import build_py as _build_py +from setuptools.command.build_py import build_py as _build_py from distutils.command.clean import clean as _clean -from distutils.spawn import find_executable -HERE = os.path.abspath('.') -PYBMIXCPP_PATH = os.path.join(HERE, "pybmix", "core", "pybmixcpp") -BAYEXMIX_PATH = os.path.join(PYBMIXCPP_PATH , "bayesmix") -PROTO_IN_DIR = os.path.join(BAYEXMIX_PATH, "proto") -PROTO_OUT_DIR = os.path.join(HERE, "pybmix", "proto/") # Convert distutils Windows platform specifiers to CMake -A arguments PLAT_TO_CMAKE = { @@ -25,8 +18,36 @@ "win-arm64": "ARM64", } + py2to3 = find_executable("2to3") +HERE = os.path.abspath('.') +PROTO_OUT_DIR = os.path.join(HERE, "pybmix/proto/") + + +class build_py(_build_py): + def run(self): + build_dir = os.path.join(HERE, "build") + os.makedirs(build_dir, exist_ok=True) + + cmake_args = [ + f"-DDISABLE_BENCHMARKS=TRUE", + f"-DDISABLE_PLOTS=TRUE", + f"-DDISABLE_EXAMPLES=TRUE", + f"-DDISABLE_DOCS=TRUE", + f"-DDISABLE_TESTS=TRUE", + ] + + subprocess.check_call(["cmake", ".."] + cmake_args, cwd=build_dir) + subprocess.check_call(["make", "generate_protos"], cwd=build_dir) + two_to_three_command = [ + py2to3, "--output-dir={0}".format(PROTO_OUT_DIR), "-W", "-n", PROTO_OUT_DIR] + print("********* CALLING 2to3 ***********") + print(" ".join(two_to_three_command)) + if subprocess.call(two_to_three_command) != 0: + sys.exit(-1) + + _build_py.run(self) class clean(_clean): def run(self): @@ -34,139 +55,14 @@ def run(self): for (dirpath, dirnames, filenames) in os.walk("."): for filename in filenames: filepath = os.path.join(dirpath, filename) - if filepath.endswith("_pb2.py") or filepath.endswith(".pyc") or \ - filepath.endswith(".so") or filepath.endswith(".o"): + if filepath.endswith("_pb2.py"): os.remove(filepath) # _clean is an old-style class, so super() doesn't work. _clean.run(self) - -# A CMakeExtension needs a sourcedir instead of a file list. -class CMakeExtension(Extension): - def __init__(self, name, sourcedir=""): - Extension.__init__(self, name, sources=[]) - self.sourcedir = os.path.abspath(sourcedir) - - -class CMakeBuild(build_ext): - def run(self): - # This is optional - will print a nicer error if CMake is missing. - # Since we force CMake via PEP 518 in the pyproject.toml, this should - # never happen and this whole method can be removed in your code if you - # want. - try: - subprocess.check_output(["cmake", "--version"]) - except OSError: - msg = "CMake missing - probably upgrade to a newer version of Pip?" - raise RuntimeError(msg) - - # To support Python 2, we have to avoid super(), since distutils is all - # old-style classes. - build_ext.run(self) - - def build_extension(self, ext): - extdir = os.path.abspath(os.path.dirname(self.get_ext_fullpath(ext.name))) - - # required for auto-detection of auxiliary "native" libs - if not extdir.endswith(os.path.sep): - extdir += os.path.sep - - cfg = "Debug" if self.debug else "Release" - - # CMake lets you override the generator - we need to check this. - # Can be set with Conda-Build, for example. - cmake_generator = os.environ.get("CMAKE_GENERATOR", "") - - # Set Python_EXECUTABLE instead if you use PYBIND11_FINDPYTHON - # EXAMPLE_VERSION_INFO shows you how to pass a value into the C++ code - # from Python. - cmake_args = [ - "-DBUILD_RUN=OFF", - "-DDISABLE_TESTS=ON", - "-DDISABLE_BENCHMARKS=ON", - "-DCMAKE_LIBRARY_OUTPUT_DIRECTORY={}".format(extdir), - "-DPYTHON_EXECUTABLE={}".format(sys.executable), - "-DEXAMPLE_VERSION_INFO={}".format(self.distribution.get_version()), - "-DCMAKE_BUILD_TYPE={}".format(cfg), # not used on MSVC, but no harm - ] - build_args = [] - - if self.compiler.compiler_type != "msvc": - # Using Ninja-build since it a) is available as a wheel and b) - # multithreads automatically. MSVC would require all variables be - # exported for Ninja to pick it up, which is a little tricky to do. - # Users can override the generator with CMAKE_GENERATOR in CMake - # 3.15+. - if not cmake_generator: - cmake_args += ["-GNinja"] - - else: - - # Single config generators are handled "normally" - single_config = any(x in cmake_generator for x in {"NMake", "Ninja"}) - - # CMake allows an arch-in-generator style for backward compatibility - contains_arch = any(x in cmake_generator for x in {"ARM", "Win64"}) - - # Specify the arch if using MSVC generator, but only if it doesn't - # contain a backward-compatibility arch spec already in the - # generator name. - if not single_config and not contains_arch: - cmake_args += ["-A", PLAT_TO_CMAKE[self.plat_name]] - - # Multi-config generators have a different way to specify configs - if not single_config: - cmake_args += [ - "-DCMAKE_LIBRARY_OUTPUT_DIRECTORY_{}={}".format(cfg.upper(), extdir) - ] - build_args += ["--config", cfg] - - # Set CMAKE_BUILD_PARALLEL_LEVEL to control the parallel build level - # across all generators. - if "CMAKE_BUILD_PARALLEL_LEVEL" not in os.environ: - # self.parallel is a Python 3 only way to set parallel jobs by hand - # using -j in the build_ext call, not supported by pip or PyPA-build. - if hasattr(self, "parallel") and self.parallel: - # CMake 3.12+ only. - build_args += ["-j{}".format(self.parallel)] - - if not os.path.exists(self.build_temp): - os.makedirs(self.build_temp) - - subprocess.check_call( - ["cmake", ext.sourcedir] + cmake_args, cwd=self.build_temp - ) - - subprocess.check_call( - ["cmake", "--build", "."] + build_args, cwd=self.build_temp - ) - - two_to_three_command = [ - py2to3, "--output-dir={0}".format(PROTO_OUT_DIR), "-W", "-n", PROTO_OUT_DIR] - print(" ".join(two_to_three_command)) - if subprocess.call(two_to_three_command) != 0: - sys.exit(-1) - - -class build_py(_build_py): - def run(self): - self.run_command("build_ext") - return super().run() - - if __name__ == "__main__": - folder = os.path.dirname(__file__) - - install_requires = ["2to3", "ninja", "numpy", "scipy", "protobuf==3.14.0", "cmake>=3.21.0"] - - # with open(os.path.join(folder, 'requirements.txt')) as fp: - # install_requires.extend([line.strip() for line in fp]) - - with open(os.path.join(folder, "docs", 'requirements.txt')) as fp: - install_requires.extend([line.strip() for line in fp]) - setup( name="pybmix", version="0.0.1", @@ -174,13 +70,7 @@ def run(self): author_email="berahamario@gmail.com", description="Python Bayesian Mixtures", long_description="", - packages=find_packages(), - ext_modules=[CMakeExtension('pybmix.core.pybmixcpp')], - cmdclass={ - "clean": clean, - "build_ext": CMakeBuild, - "build_py": build_py - }, - install_requires=install_requires, + cmdclass={"build_py": build_py, "clean": clean}, zip_safe=False, - ) + python_requires=">=3.6", + ) \ No newline at end of file