From 6d85459f34b2586509c89fcf10c915d568f769ef Mon Sep 17 00:00:00 2001 From: lerandc Date: Wed, 10 Jul 2019 13:52:34 -0700 Subject: [PATCH] New CMakeLists file to facilitate HDF5 library finding and building of pyprismatic; new pyprismatic setup script to utilize CMake --- CMakeLists.txt | 87 ++++++++++++++++++++------- setup.py | 158 +++++++++++++++++++++++++++++-------------------- 2 files changed, 159 insertions(+), 86 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index b647db94c..c77918128 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,4 +1,5 @@ cmake_minimum_required(VERSION 3.5) +message("CMake prefix path: " ${CMAKE_PREFIX_PATH}) project(PRISM) set(CMAKE_CXX_STANDARD 11) set(CMAKE_BUILD_TYPE RELEASE) @@ -15,8 +16,10 @@ endif ( MSVC ) set(PRISMATIC_ENABLE_GUI 0 CACHE BOOL PRISMATIC_ENABLE_GUI) set(PRISMATIC_ENABLE_GPU 0 CACHE BOOL PRISMATIC_ENABLE_GPU) set(PRISMATIC_ENABLE_CLI 1 CACHE BOOL PRISMATIC_ENABLE_GPU) -set(PRISMATIC_ENABLE_PYTHON_GPU 0 CACHE BOOL PRISMATIC_ENABLE_PYTHON_GPU) +#set(PRISMATIC_ENABLE_PYTHON_GPU 0 CACHE BOOL PRISMATIC_ENABLE_PYTHON_GPU) set(PRISMATIC_ENABLE_DOUBLE_PRECISION 0 CACHE BOOL PRISMATIC_ENABLE_DOUBLE_PRECISION) +set(PRISMATIC_ENABLE_PYPRISMATIC 0 CACHE BOOL PRISMATIC_ENABLE_PYPRISMATIC) +set(PRISMATIC_USE_HDF5_STATIC 0 CACHE BOOL PRISMATIC_USE_HDF5_STATIC) #set (CMAKE_BUILD_TYPE DEBUG) if (PRISMATIC_ENABLE_GPU) @@ -68,16 +71,25 @@ endif (PRISMATIC_ENABLE_GPU) find_package (Threads REQUIRED) find_package (Boost REQUIRED) find_package (FFTW3 REQUIRED) -find_package (HDF5 REQUIRED) -find_library(HDF5_HL_LIBRARY hdf5_hl) -find_library(HDF5_LIBRARY hdf5) -find_library(HDF5_HL_CPP_LIBRARY hdf5_hl_cpp) -find_library(HDF5_CPP_LIBRARY hdf5_cpp) +if(PRISMATIC_ENABLE_PYPRISMATIC) + find_package (PythonInterp 3.5 REQUIRED) + find_package (PythonLibs 3.5 REQUIRED) +endif(PRISMATIC_ENABLE_PYPRISMATIC) -set(HDF5_LIBRARIES ${HDF5_HL_LIBRARY} ${HDF5_LIBRARY} ${HDF5_HL_CPP_LIBRARY} ${HDF5_CPP_LIBRARY}) -#set(HDF5_LIBRARIES ${HDF5_LIBRARIES}) +if (PRISMATIC_USE_HDF5_STATIC) + message("entered") + set(HDF5_USE_STATIC_LIBRARIES ON) +endif (PRISMATIC_USE_HDF5_STATIC) +set(HDF5_FIND_COMPONENTS "C" "HL" "CXX") +find_package (HDF5 REQUIRED ${HDF5_FIND_COMPONENTS}) +find_library(HDF5_C_HL_LIBRARIES hdf5_hl) +find_library(HDF5_C_LIBRARIES hdf5) +find_library(HDF5_CXX_HL_LIBRARIES hdf5_hl_cpp) +find_library(HDF5_CXX_LIBRARIES hdf5_cpp) + +set(HDF5_LIBRARIES ${HDF5_C_HL_LIBRARIES} ${HDF5_C_LIBRARIES} ${HDF5_CXX_HL_LIBRARIES} ${HDF5_CXX_LIBRARIES}) message("Boost dir " ${Boost_INCLUDE_DIRS}) include_directories(${CMAKE_SOURCE_DIR}/include @@ -233,16 +245,49 @@ if (PRISMATIC_ENABLE_CLI) install(TARGETS prismatic RUNTIME DESTINATION ${CMAKE_INSTALL_PREFIX}/bin) endif(PRISMATIC_ENABLE_CLI) -if (PRISMATIC_ENABLE_PYTHON_GPU AND PRISMATIC_ENABLE_GPU) - cuda_add_library(cuprismatic SHARED - ${SOURCE_FILES} - ${CUDA_SOURCE_FILES}) - cuda_add_cufft_to_target(cuprismatic) - target_link_libraries(cuprismatic - ${FFTW_LIBRARIES} - ${HDF5_LIBRARIES}) - target_compile_definitions(cuprismatic PRIVATE PRISMATIC_ENABLE_GPU=1 BUILDING_CUPRISMATIC=1 CUPRISMATIC_EXPORT=1) - install(TARGETS cuprismatic - ARCHIVE DESTINATION ${CMAKE_INSTALL_PREFIX}/lib - LIBRARY DESTINATION ${CMAKE_INSTALL_PREFIX}/lib) -endif(PRISMATIC_ENABLE_PYTHON_GPU AND PRISMATIC_ENABLE_GPU) +# if (PRISMATIC_ENABLE_PYTHON_GPU AND PRISMATIC_ENABLE_GPU) +# cuda_add_library(cuprismatic SHARED +# ${SOURCE_FILES} +# ${CUDA_SOURCE_FILES}) +# cuda_add_cufft_to_target(cuprismatic) +# target_link_libraries(cuprismatic +# ${FFTW_LIBRARIES} +# ${HDF5_LIBRARIES}) +# target_compile_definitions(cuprismatic PRIVATE PRISMATIC_ENABLE_GPU=1 BUILDING_CUPRISMATIC=1 CUPRISMATIC_EXPORT=1) +# install(TARGETS cuprismatic +# ARCHIVE DESTINATION ${CMAKE_INSTALL_PREFIX}/lib +# LIBRARY DESTINATION ${CMAKE_INSTALL_PREFIX}/lib) +# endif(PRISMATIC_ENABLE_PYTHON_GPU AND PRISMATIC_ENABLE_GPU) + +if(PRISMATIC_ENABLE_PYPRISMATIC) + + include_directories(${PYTHON_INCLUDE_DIRS}) + if (PRISMATIC_ENABLE_GPU) + cuda_add_library(pyprismatic_core SHARED pyprismatic/core.cpp ${SOURCE_FILES} ${CUDA_SOURCE_FILES}) + cuda_add_cufft_to_target(pyprismatic_core) + else (PRISMATIC_ENABLE_GPU) + add_library(pyprismatic_core SHARED pyprismatic/core.cpp ${SOURCE_FILES}) + endif(PRISMATIC_ENABLE_GPU) + + target_link_libraries(pyprismatic_core + ${CMAKE_THREAD_LIBS_INIT} + ${FFTW_LIBRARIES} + ${HDF5_LIBRARIES} + ${PYTHON_LIBRARIES}) + + set_target_properties( + pyprismatic_core + PROPERTIES + PREFIX "" + OUTPUT_NAME "core" + LINKER_LANGUAGE CXX + ) + + if(NOT UNIX) + set_target_properties( + pyprismatic_core + PROPERTIES + SUFFIX ".pyd" + ) + endif(NOT UNIX) +endif(PRISMATIC_ENABLE_PYPRISMATIC) \ No newline at end of file diff --git a/setup.py b/setup.py index cfa8a0f4d..ab80de27f 100644 --- a/setup.py +++ b/setup.py @@ -14,14 +14,30 @@ from setuptools import setup, Extension from setuptools.command.install import install from setuptools.command.develop import develop +from setuptools.command.build_ext import build_ext import sys import os +import platform +import subprocess -#os.environ['CC'] = 'gcc-7' -#os.environ['CXX'] = 'g++-7' +# Filename for the C extension module library +c_module_name = 'pyprismatic.core' -prismatic_extra_definitions = [] -prismatic_libs = [] +# Parse command line flags +options = {k: 'OFF' for k in ['--opt', '--debug', '--enable-gpu']} +for flag in options.keys(): + if flag in sys.argv: + options[flag] = 'ON' + sys.argv.remove(flag) + +# Command line flags forwarded to CMake +cmake_cmd_args = [] +for f in sys.argv: + if f.startswith('-D'): + cmake_cmd_args.append(f) + +for f in cmake_cmd_args: + sys.argv.remove(f) # In CPU-only mode, all Prismatic C++ source files will be compiled into the Python package. For GPU support, a shared library is # built from the CUDA/C++ sources and the Python package links against it. The C++ files that are potentially compiled into @@ -29,42 +45,6 @@ # The reason for doing it this way is for user convenience so that compiling/linking to an extra Prismatic library is not required until # enabling for GPU, at which point it is already necessary for the other CUDA libraries. -prismatic_sources_base = ["pyprismatic/core.cpp"] -obj_prefix = "build/CMakeFiles/prismatic.dir/src/" -prismatic_gpu_objects = ["prismatic_generated_Multislice_calcOutput.cu.o", - "prismatic_generated_PRISM02_calcSMatrix.cu.o", - "prismatic_generated_PRISM03_calcOutput.cu.o", - "prismatic_generated_utility.cu.o", - ] - -prismatic_gpu_objects = [obj_prefix + obj for obj in prismatic_gpu_objects] -prismatic_sources_extra = [ - "src/go.cpp", - "src/atom.cpp", - "src/configure.cpp", - "src/Multislice_calcOutput.cpp", - "src/Multislice_entry.cpp", # - "src/parseInput.cpp", # - "src/PRISM01_calcPotential.cpp", - "src/PRISM02_calcSMatrix.cpp", # - "src/PRISM03_calcOutput.cpp", # - "src/PRISM_entry.cpp", - "src/projectedPotential.cpp", # - "src/utility.cpp", # - "src/WorkDispatcher.cpp", -] - -prismatic_sources = prismatic_sources_base -prismatic_include_dirs = ["./include"] -prismatic_library_dirs = [] -prismatic_extra_objects = [] #"/usr/local/hdf/lib/libhdf5_hl.so","/usr/local/hdf/lib/libhdf5.so","/usr/local/hdf/lib/libhdf5_hl_cpp.so","/usr/local/hdf/lib/libhdf5_cpp.so"] - -if os.name == "nt": # check for Windows OS - prismatic_libs = ["libfftw3f-3"] -else: - prismatic_libs = ["fftw3f", "fftw3f_threads","hdf5_hl","hdf5","hdf5_hl_cpp","hdf5_cpp","pthread","dl","m"] -prismatic_extra_compile_defs = ["-std=c++11"] - class InstallCommand(install): user_options = install.user_options + [("enable-gpu", None, None)] @@ -91,28 +71,75 @@ def finalize_options(self): def run(self): develop.run(self) -if ( - "--enable-gpu" in sys.argv -): # GPU-mode, add some macro definitions and link with the CUDA libraries - prismatic_extra_definitions.extend( - [("PRISMATIC_ENABLE_GPU", 1), ("BUILDING_CUPRISMATIC", 1)] - ) - prismatic_libs.extend(["cufft", "cudart"]) - prismatic_sources.extend(prismatic_sources_extra) - prismatic_extra_objects.extend(prismatic_gpu_objects) -else: # CPU-only mode, add the extra C++ sources to the python package - prismatic_sources.extend(prismatic_sources_extra) - -pyprimsatic_core = Extension( - "pyprismatic.core", - sources=prismatic_sources, - include_dirs=prismatic_include_dirs, - extra_compile_args=prismatic_extra_compile_defs, - define_macros=prismatic_extra_definitions, - library_dirs=prismatic_library_dirs, - libraries=prismatic_libs, - extra_objects=prismatic_extra_objects, -) +class CMakeExtension(Extension): + def __init__(self, name, cmake_lists_dir='.', **kwa): + Extension.__init__(self, name, sources=[], **kwa) + self.cmake_lists_dir = os.path.abspath(cmake_lists_dir) + +class cmake_build_ext(build_ext): + def build_extensions(self): + # Ensure that CMake is present and working + try: + out = subprocess.check_output(['cmake', '--version']) + except OSError: + raise RuntimeError('Cannot find CMake executable') + + for ext in self.extensions: + + extdir = os.path.abspath(os.path.dirname(self.get_ext_fullpath(ext.name))) + cfg = 'Debug' if options['--debug'] == 'ON' else 'Release' + if options['--enable-gpu'] == 'ON': + gpu_enable = '1' + else: + gpu_enable = '0' + + cmake_args = [ + '-DCMAKE_BUILD_TYPE=%s' % cfg, + # Ask CMake to place the resulting library in the directory + # containing the extension + '-DCMAKE_LIBRARY_OUTPUT_DIRECTORY_{}={}'.format(cfg.upper(), extdir), + # Other intermediate static libraries are placed in a + # temporary build directory instead + '-DCMAKE_ARCHIVE_OUTPUT_DIRECTORY_{}={}'.format(cfg.upper(), self.build_temp), + # Hint CMake to use the same Python executable that + # is launching the build, prevents possible mismatching if + # multiple versions of Python are installed + '-DPYTHON_EXECUTABLE={}'.format(sys.executable), + '-DPRISMATIC_ENABLE_PYPRISMATIC=1', + '-DPRISMATIC_ENABLE_CLI=0', + '-DPRISMATIC_ENABLE_GPU={}'.format(gpu_enable) + ] + + # We can handle some platform-specific settings at our discretion + if platform.system() == 'Windows': + plat = ('x64' if platform.architecture()[0] == '64bit' else 'Win32') + cmake_args += [ + # These options are likely to be needed under Windows + '-DCMAKE_WINDOWS_EXPORT_ALL_SYMBOLS=TRUE', + '-DCMAKE_RUNTIME_OUTPUT_DIRECTORY_{}={}'.format(cfg.upper(), extdir), + ] + # Assuming that Visual Studio and MinGW are supported compilers + if self.compiler.compiler_type == 'msvc': + cmake_args += [ + '-DCMAKE_GENERATOR_PLATFORM=%s' % plat, + ] + else: + cmake_args += [ + '-G', 'MinGW Makefiles', + ] + + cmake_args += cmake_cmd_args + + if not os.path.exists(self.build_temp): + os.makedirs(self.build_temp) + + # Config + subprocess.check_call(['cmake', ext.cmake_lists_dir] + cmake_args, + cwd=self.build_temp) + + # Build + subprocess.check_call(['cmake', '--build', '.', '--config', cfg], + cwd=self.build_temp) setup( name="PyPrismatic", @@ -120,9 +147,10 @@ def run(self): author_email="apryor6@gmail.com", version="1.2.0", description="Python wrapper for Prismatic package for fast image simulation using the PRISM and multislice algorithms in Scanning Transmission Electron Microscopy (STEM)", - ext_modules=[pyprimsatic_core], + ext_modules=[CMakeExtension(c_module_name)], packages=["pyprismatic"], install_requires=["numpy>=1.13.0", "matplotlib>=2.0.2","h5py>=2.9.0"], cmdclass={"install": InstallCommand, - "develop":DevelopCommand}, -) + "develop":DevelopCommand, + "build_ext":cmake_build_ext}, +) \ No newline at end of file