diff --git a/.gitmodules b/.gitmodules deleted file mode 100644 index 48d3f598b3a6f239bb9a51048a4f4a6cb1a22291..0000000000000000000000000000000000000000 --- a/.gitmodules +++ /dev/null @@ -1,3 +0,0 @@ -[submodule "bpl-subset"] - path = bpl-subset - url = https://github.com/inducer/bpl-subset diff --git a/aksetup_helper.py b/aksetup_helper.py index 50507afca8e8af736d51ef154316b6badef69b42..36422892cbf07f30736c2bd2d9733bf312545c6b 100644 --- a/aksetup_helper.py +++ b/aksetup_helper.py @@ -12,6 +12,7 @@ def count_down_delay(delay): sleep(1) print("") + DASH_SEPARATOR = 75 * "-" @@ -23,7 +24,7 @@ def setup(*args, **kwargs): raise except SystemExit: raise - except: + except Exception: print(DASH_SEPARATOR) print("Sorry, your build failed. Try rerunning configure.py with " "different options.") @@ -212,7 +213,7 @@ def expand_value(v, options): for i in v: try: exp_i = expand_value(i, options) - except: + except Exception: pass else: result.append(exp_i) @@ -687,17 +688,20 @@ def substitute(substitutions, fname): fname_in = fname+".in" lines = open(fname_in, "r").readlines() new_lines = [] - for l in lines: + for line in lines: made_change = True while made_change: made_change = False - match = var_re.search(l) + match = var_re.search(line) if match: varname = match.group(1) - l = l[:match.start()] + str(substitutions[varname]) + l[match.end():] + line = ( + line[:match.start()] + + str(substitutions[varname]) + + line[match.end():]) made_change = True - match = string_var_re.search(l) + match = string_var_re.search(line) if match: varname = match.group(1) subst = substitutions[varname] @@ -706,9 +710,9 @@ def substitute(substitutions, fname): else: subst = '"%s"' % subst - l = l[:match.start()] + subst + l[match.end():] + line = line[:match.start()] + subst + line[match.end():] made_change = True - new_lines.append(l) + new_lines.append(line) new_lines.insert(1, "# DO NOT EDIT THIS FILE -- " "it was generated by configure.py\n") new_lines.insert(2, "# %s\n" % (" ".join(sys.argv))) @@ -823,3 +827,24 @@ def check_git_submodules(): print(DASH_SEPARATOR) print("git submodules initialized successfully") print(DASH_SEPARATOR) + + +def check_pybind11(): + try: + import pybind11 # noqa + except ImportError: + print(DASH_SEPARATOR) + print("Pybind11 is not installed.") + print(DASH_SEPARATOR) + print("Very likely, the build process after this message will fail.") + print("") + print("Simply press Ctrl+C and type") + print("python -m pip install pybind11") + print("to fix this. If you don't, the build will continue ") + print("in a few seconds.") + print("") + print("[1] https://pybind11.readthedocs.io/en/stable/") + print(DASH_SEPARATOR) + + from aksetup_helper import count_down_delay + count_down_delay(delay=10) diff --git a/bpl-subset b/bpl-subset deleted file mode 160000 index 0d99742a3613fef0504705897ce6c74193e501e3..0000000000000000000000000000000000000000 --- a/bpl-subset +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 0d99742a3613fef0504705897ce6c74193e501e3 diff --git a/requirements.txt b/requirements.txt index 18cafc6250e2da1799bd1545d2e110eb3e7d762f..f9b6a7adc6546de73916bdd368c4b2bf2c15c90c 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,3 +1,4 @@ +pybind11 numpy # mesh generation for tests meshpy diff --git a/setup.py b/setup.py index bd17ab9b481cb8d82aff7279c504ac969a21a1a2..00e8b55178ab4e05fbc89dfed70b8abdc04375ee 100644 --- a/setup.py +++ b/setup.py @@ -1,36 +1,108 @@ #!/usr/bin/env python +import sys +import setuptools +from setuptools.command.build_ext import build_ext -def get_config_schema(): - from aksetup_helper import (ConfigSchema, Switch, - BoostLibraries, StringListOption, make_boost_base_options) - - return ConfigSchema(make_boost_base_options() + [ - Switch("USE_SHIPPED_BOOST", True, "Use included Boost library"), - BoostLibraries("python"), +def get_config_schema(): + from aksetup_helper import ConfigSchema, StringListOption + return ConfigSchema([ StringListOption("CXXFLAGS", [], help="Any extra C++ compiler options to include"), ]) +# {{{ boilerplate from https://github.com/pybind/python_example/blob/2ed5a68759cd6ff5d2e5992a91f08616ef457b5c/setup.py # noqa + +class get_pybind_include(object): # noqa: N801 + """Helper class to determine the pybind11 include path + + The purpose of this class is to postpone importing pybind11 + until it is actually installed, so that the ``get_include()`` + method can be invoked. """ + + def __init__(self, user=False): + self.user = user + + def __str__(self): + import pybind11 + return pybind11.get_include(self.user) + + +# As of Python 3.6, CCompiler has a `has_flag` method. +# cf http://bugs.python.org/issue26689 +def has_flag(compiler, flagname): + """Return a boolean indicating whether a flag name is supported on + the specified compiler. + """ + import tempfile + with tempfile.NamedTemporaryFile('w', suffix='.cpp', delete=False) as f: + f.write('int main (int argc, char **argv) { return 0; }') + fname = f.name + try: + compiler.compile([fname], extra_postargs=[flagname]) + except setuptools.distutils.errors.CompileError: + return False + return True + + +def cpp_flag(compiler): + """Return the -std=c++[11/14] compiler flag. + + The c++14 is prefered over c++11 (when it is available). + """ + if has_flag(compiler, '-std=c++14'): + return '-std=c++14' + elif has_flag(compiler, '-std=c++11'): + return '-std=c++11' + else: + raise RuntimeError('Unsupported compiler -- at least C++11 support ' + 'is needed!') + + +class BuildExt(build_ext): + """A custom build extension for adding compiler-specific options.""" + c_opts = { + 'msvc': ['/EHsc'], + 'unix': [], + } + + if sys.platform == 'darwin': + c_opts['unix'] += ['-stdlib=libc++', '-mmacosx-version-min=10.7'] + + def build_extensions(self): + ct = self.compiler.compiler_type + opts = self.c_opts.get(ct, []) + if ct in ['unix', 'mingw32']: + opts.append('-DVERSION_INFO="%s"' % self.distribution.get_version()) + opts.append(cpp_flag(self.compiler)) + if has_flag(self.compiler, '-fvisibility=hidden'): + opts.append('-fvisibility=hidden') + elif ct == 'msvc': + opts.append('/DVERSION_INFO=\\"%s\\"' % self.distribution.get_version()) + for ext in self.extensions: + ext.extra_compile_args = ext.extra_compile_args + opts + build_ext.build_extensions(self) + +# }}} + + def main(): import glob + from setuptools import find_packages from aksetup_helper import ( - hack_distutils, get_config, setup, Extension, - set_up_shipped_boost_if_requested) + check_pybind11, + hack_distutils, get_config, setup, Extension) + + check_pybind11() hack_distutils() conf = get_config(get_config_schema(), warn_about_no_config=False) - EXTRA_OBJECTS, EXTRA_DEFINES = \ - set_up_shipped_boost_if_requested("pymetis", conf) - - INCLUDE_DIRS = conf["BOOST_INC_DIR"] # noqa - LIBRARY_DIRS = conf["BOOST_LIB_DIR"] # noqa - LIBRARIES = conf["BOOST_PYTHON_LIBNAME"] # noqa + EXTRA_DEFINES = {} EXTRA_DEFINES["HAVE_MREMAP"] = 0 # mremap() buggy on amd64? @@ -75,27 +147,36 @@ def main(): 'Topic :: Software Development :: Libraries', ], - packages=["pymetis"], + packages=find_packages(), + + setup_requires=[ + "pybind11", + ], install_requires=["six"], + ext_modules=[ Extension( "pymetis._internal", + ["src/wrapper/wrapper.cpp"] + glob.glob("src/metis/GKlib/*.c") + glob.glob("src/metis/*.c") + - glob.glob("src/metis/libmetis/*.c") + - ["src/wrapper/wrapper.cpp"] + EXTRA_OBJECTS, + glob.glob("src/metis/libmetis/*.c"), define_macros=list(EXTRA_DEFINES.items()), include_dirs=["src/metis/GKlib"] + ["src/metis/include"] + ["src/metis/libmetis"] + - INCLUDE_DIRS, - library_dirs=LIBRARY_DIRS, - libraries=LIBRARIES, + [ + get_pybind_include(), + get_pybind_include(user=True) + ], extra_compile_args=conf["CXXFLAGS"], ), - ] - ) + ], + cmdclass={'build_ext': BuildExt}, + zip_safe=False) if __name__ == '__main__': main() + +# vim: foldmethod=marker diff --git a/src/wrapper/wrapper.cpp b/src/wrapper/wrapper.cpp index 7306a2f9d892259a35124c05350ecbe78abe2733..3b0483a59728834c168114f1cbae848207854ea0 100644 --- a/src/wrapper/wrapper.cpp +++ b/src/wrapper/wrapper.cpp @@ -1,7 +1,6 @@ -#include -#include -#include +#include #include +#include #include #include #include @@ -9,7 +8,7 @@ -using namespace boost::python; +namespace py = pybind11; using namespace std; @@ -17,12 +16,12 @@ using namespace std; #define COPY_IDXTYPE_LIST(NAME) \ { \ - stl_input_iterator begin(NAME##_py), end; \ - std::copy(begin, end, back_inserter(NAME)); \ + for (auto it: NAME##_py) \ + NAME.push_back(py::cast(*it)); \ } #define COPY_OUTPUT(NAME, LEN) \ - list NAME##_py; \ + py::list NAME##_py; \ { \ for (idx_t i = 0; i perm, iperm; COPY_IDXTYPE_LIST(perm); @@ -87,19 +87,18 @@ namespace } - object - wrap_node_nd(const object &xadj_py, const object &adjncy_py) + py::object + wrap_node_nd(const py::object &xadj_py, const py::object &adjncy_py) { - int i; - int nvtxs = len(xadj_py) - 1; + idx_t nvtxs = py::len(xadj_py) - 1; vector xadj, adjncy; COPY_IDXTYPE_LIST(xadj); COPY_IDXTYPE_LIST(adjncy); idx_t * vwgt = NULL; - boost::scoped_array perm(new idx_t[nvtxs]); - boost::scoped_array iperm(new idx_t[nvtxs]); + std::unique_ptr perm(new idx_t[nvtxs]); + std::unique_ptr iperm(new idx_t[nvtxs]); int options[METIS_NOPTIONS]; METIS_SetDefaultOptions(options); @@ -114,19 +113,19 @@ namespace COPY_OUTPUT(perm, nvtxs); COPY_OUTPUT(iperm, nvtxs); - return boost::python::make_tuple(perm_py, iperm_py); + return py::make_tuple(perm_py, iperm_py); } - object + py::object wrap_part_graph( int nparts, - const object &xadj_py, - const object &adjncy_py, - const object &vwgt_py, - const object &adjwgt_py, + const py::object &xadj_py, + const py::object &adjncy_py, + const py::object &vwgt_py, + const py::object &adjwgt_py, bool recursive) { - idx_t nvtxs = len(xadj_py) - 1; + idx_t nvtxs = py::len(xadj_py) - 1; vector xadj, adjncy, vwgt, adjwgt; COPY_IDXTYPE_LIST(xadj); COPY_IDXTYPE_LIST(adjncy); @@ -139,11 +138,11 @@ namespace // pymetis defaults to the minimizing-edge-cut objective idx_t * pvsize = NULL; - if (vwgt_py != object()) + if (!vwgt_py.is_none()) { COPY_IDXTYPE_LIST(vwgt); } - if (adjwgt_py != object()) + if (!adjwgt_py.is_none()) { COPY_IDXTYPE_LIST(adjwgt); } @@ -153,7 +152,7 @@ namespace options[METIS_OPTION_NUMBERING] = 0; // C-style numbering idx_t edgecut; - boost::scoped_array part(new idx_t[nvtxs]); + std::unique_ptr part(new idx_t[nvtxs]); if (recursive) { @@ -176,14 +175,14 @@ namespace COPY_OUTPUT(part, nvtxs); - return boost::python::make_tuple(edgecut, part_py); + return py::make_tuple(edgecut, part_py); } } -BOOST_PYTHON_MODULE(_internal) +PYBIND11_MODULE(_internal, m) { - def("verify_nd", wrap_verify_nd); - def("node_nd", wrap_node_nd); - def("edge_nd", wrap_node_nd); // DEPRECATED - def("part_graph", wrap_part_graph); + m.def("verify_nd", wrap_verify_nd); + m.def("node_nd", wrap_node_nd); + m.def("edge_nd", wrap_node_nd); // DEPRECATED + m.def("part_graph", wrap_part_graph); }