diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml new file mode 100644 index 00000000..58e1029b --- /dev/null +++ b/.github/workflows/main.yml @@ -0,0 +1,48 @@ + +name: Privateer Build + +on: + push: + branches: [ "master" ] + paths: + - src/** + - CMakeLists.txt + + pull_request: + branches: [ "master" ] + paths: + - src/** + - CMakeLists.txt + +jobs: + build: + + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v3 + - uses: actions/setup-python@v4 + with: + python-version: '3.7' + repo-token: ${{secrets.GITHUB_TOKEN}} + + - name: Check out dependencies + run : | + git clone https://github.com/Dialpuri/privateer_dependencies.git + + - name: Source Environment + run: source ccp4.envsetup-sh + + - name: Load python environment + run : pip install -r requirements.txt + + - name: Setup cmake + uses: jwlawson/actions-setup-cmake@v1.13 + + - name: Build + run : | + pwd + export CLIBD=privateer_dependencies/lib/data + + python setup_test.py install + \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt index 60445892..d8592b93 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -19,41 +19,63 @@ set(PRIVATEERDATA ${CMAKE_SOURCE_DIR}/data) set(CLIBDENV $ENV{CLIBD}) ## Find Privateer's dependencies that have been compiled locally. -# -find_library(MMDB2DEP NAMES mmdb2 - HINTS ${CMAKE_SOURCE_DIR}/dependencies/lib) -find_library(CCP4CDEP NAMES ccp4c - HINTS ${CMAKE_SOURCE_DIR}/dependencies/lib) -find_library(CCP4SRSDEP NAMES ccp4srs - HINTS ${CMAKE_SOURCE_DIR}/dependencies/lib) -find_library(CLIPPERCOREDEP NAMES clipper-core - HINTS ${CMAKE_SOURCE_DIR}/dependencies/lib) -find_library(CLIPPERMMDBDEP NAMES clipper-mmdb - HINTS ${CMAKE_SOURCE_DIR}/dependencies/lib) -find_library(CLIPPERMINIMOLDEP NAMES clipper-minimol - HINTS ${CMAKE_SOURCE_DIR}/dependencies/lib) -find_library(CLIPPERCONTRIBDEP NAMES clipper-contrib - HINTS ${CMAKE_SOURCE_DIR}/dependencies/lib) -find_library(CLIPPERCCP4DEP NAMES clipper-ccp4 - HINTS ${CMAKE_SOURCE_DIR}/dependencies/lib) -find_library(CLIPPERCIFDEP NAMES clipper-cif - HINTS ${CMAKE_SOURCE_DIR}/dependencies/lib) - -add_subdirectory(${CMAKE_SOURCE_DIR}/dependencies/pybind11) - +if (MODE STREQUAL "TESTING") + set (MMDB2DEP ${CMAKE_SOURCE_DIR}/privateer_dependencies/lib/libmmdb2.so) + set (CCP4CDEP ${CMAKE_SOURCE_DIR}/privateer_dependencies/lib/libccp4srs.so) + set (CCP4SRSDEP ${CMAKE_SOURCE_DIR}/privateer_dependencies/lib/libccp4c.so) + set (CLIPPERCOREDEP ${CMAKE_SOURCE_DIR}/privateer_dependencies/lib/libclipper-core.so) + set (CLIPPERMMDBDEP ${CMAKE_SOURCE_DIR}/privateer_dependencies/lib/libclipper-mmdb.so) + set (CLIPPERMINIMOLDEP ${CMAKE_SOURCE_DIR}/privateer_dependencies/lib/libclipper-minimol.so) + set (CLIPPERCONTRIBDEP ${CMAKE_SOURCE_DIR}/privateer_dependencies/lib/libclipper-contrib.so) + set (CLIPPERCCP4DEP ${CMAKE_SOURCE_DIR}/privateer_dependencies/lib/libclipper-ccp4.so) + set (CLIPPERCIFDEP ${CMAKE_SOURCE_DIR}/privateer_dependencies/lib/libclipper-cif.so) +else() + find_library(MMDB2DEP NAMES mmdb2 + HINTS ${CMAKE_SOURCE_DIR}/dependencies/lib) + find_library(CCP4CDEP NAMES ccp4c + HINTS ${CMAKE_SOURCE_DIR}/dependencies/lib) + find_library(CCP4SRSDEP NAMES ccp4srs + HINTS ${CMAKE_SOURCE_DIR}/dependencies/lib) + find_library(CLIPPERCOREDEP NAMES clipper-core + HINTS ${CMAKE_SOURCE_DIR}/dependencies/lib) + find_library(CLIPPERMMDBDEP NAMES clipper-mmdb + HINTS ${CMAKE_SOURCE_DIR}/dependencies/lib) + find_library(CLIPPERMINIMOLDEP NAMES clipper-minimol + HINTS ${CMAKE_SOURCE_DIR}/dependencies/lib) + find_library(CLIPPERCONTRIBDEP NAMES clipper-contrib + HINTS ${CMAKE_SOURCE_DIR}/dependencies/lib) + find_library(CLIPPERCCP4DEP NAMES clipper-ccp4 + HINTS ${CMAKE_SOURCE_DIR}/dependencies/lib) + find_library(CLIPPERCIFDEP NAMES clipper-cif + HINTS ${CMAKE_SOURCE_DIR}/dependencies/lib) +endif() + +if (MODE STREQUAL "TESTING") + add_subdirectory(${CMAKE_SOURCE_DIR}/privateer_dependencies/pybind11) +else() + add_subdirectory(${CMAKE_SOURCE_DIR}/dependencies/pybind11) +endif() find_package(Threads REQUIRED) - #Find Privateer's main.cpp file set(PRIVATEER_SOURCE ${PRIVATEER_SOURCE_DIR}/cpp/privateer.cpp) #Location of include files to be linked -include_directories(${CMAKE_SOURCE_DIR}/dependencies/include +if (MODE STREQUAL "TESTING") + include_directories(${CMAKE_SOURCE_DIR}/privateer_dependencies/include + ${CMAKE_SOURCE_DIR}/privateer_dependencies/gemmi/include + ${PYBIND11_INCLUDE_DIR} + ${PYTHON_INCLUDE_DIRS} + ${PRIVATEER_SOURCE_DIR}/cpp + ${PRIVATEER_SOURCE_DIR}/cpp/pybind11) +else() + include_directories(${CMAKE_SOURCE_DIR}/dependencies/include ${CMAKE_SOURCE_DIR}/dependencies/gemmi/include ${PYBIND11_INCLUDE_DIR} ${PYTHON_INCLUDE_DIRS} ${PRIVATEER_SOURCE_DIR}/cpp ${PRIVATEER_SOURCE_DIR}/cpp/pybind11) +endif() file(COPY ${PRIVATEERDATA}/glycomics/privateer_glycomics_database.json DESTINATION ${CLIBDENV}) diff --git a/README.md b/README.md index 71b8c19b..babb89cd 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,8 @@ ![ScreenShot](/logo.png) # Privateer +![Privateer Build](https://github.com/glycojones/privateer/actions/workflows/main.yml/badge.svg) + Privateer is a tool for carbohydrate structure validation, re-refinement and graphical analysis. It carries out automatic assignments of ring conformation (IUPAC nomenclature), anomeric form, absolute configuration and comparison to reference values for validation. It computes omit mFo-DFc maps and calculates a correlation coefficient between model and electron density. For structure refinement, it is able to generate chemical dictionaries with unimodal torsion restraints which will help keep the lowest energy conformation. In terms of graphical analysis, it will produce vector diagrams in SNFG nomenclature (SVG format), which are annotated using the validation information (ring conformation, anomeric form, etc). Privateer is implemented in C++11 and Python3 (via pybind11), and produces Scheme and Python scripts for use with Coot (https://github.com/pemsley/coot). diff --git a/setup_test.py b/setup_test.py new file mode 100644 index 00000000..21f238ca --- /dev/null +++ b/setup_test.py @@ -0,0 +1,138 @@ +import os +import re +import sys +import platform +import subprocess +import shutil + +from distutils.version import LooseVersion +import distutils.command.build +from setuptools import setup, find_packages, Extension, Command +import setuptools.command.develop +import setuptools.command.install +from setuptools.command.build_ext import build_ext + + +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): + try: + out = subprocess.check_output(['cmake', '--version']) + except OSError: + raise RuntimeError( + "CMake must be installed to build the following extensions: " + + ", ".join(e.name for e in self.extensions)) + + if platform.system() == "Windows": + cmake_version = LooseVersion(re.search(r'version\s*([\d.]+)', + out.decode()).group(1)) + if cmake_version < '3.1.0': + raise RuntimeError("CMake >= 3.1.0 is required on Windows") + + for ext in self.extensions: + self.build_extension(ext) + + def build_extension(self, ext): + extdir = os.path.abspath( + os.path.dirname(self.get_ext_fullpath(ext.name))) + cmake_args = ['-DCMAKE_LIBRARY_OUTPUT_DIRECTORY=' + extdir, + '-DPYTHON_EXECUTABLE=' + sys.executable] + + cfg = 'Debug' if self.debug else 'Release' + build_args = ['--config', cfg] + + if platform.system() == "Windows": + cmake_args += ['-DCMAKE_LIBRARY_OUTPUT_DIRECTORY_{}={}'.format( + cfg.upper(), + extdir)] + if sys.maxsize > 2**32: + cmake_args += ['-A', 'x64'] + build_args += ['--', '/m'] + else: + cmake_args += ['-DCMAKE_BUILD_TYPE=' + cfg] + build_args += ['--', '-j2'] + + if platform.system() == "Darwin" : + if shutil.which('brew') == '/usr/local/bin/brew': + cmake_args += ['-DCMAKE_C_COMPILER=/usr/local/bin/gcc-12', '-DCMAKE_CXX_COMPILER=/usr/local/bin/g++-12'] + else: + cmake_args += ['-DCMAKE_C_COMPILER=/opt/homebrew/bin/gcc-12', '-DCMAKE_CXX_COMPILER=/opt/homebrew/bin/g++-12'] + + env = os.environ.copy() + env['CXXFLAGS'] = '{} -DVERSION_INFO=\\"{}\\"'.format( + env.get('CXXFLAGS', ''), + self.distribution.get_version()) + exedir = 'build/executable' + if not os.path.exists(exedir): + os.makedirs(exedir) + + cmake_args += ["-D MODE=TESTING"] + + subprocess.check_call(['cmake', ext.sourcedir] + cmake_args, + cwd=exedir, env=env) + subprocess.check_call(['cmake', '--build', '.'] + build_args, + cwd=exedir) + print() # Add an empty line for cleaner output + + + +# This function downloads and builds the shared-library +# def run_privateer_install_script(): + # build_privateer_cmd = ['bash', 'DependencyInstaller.sh'] + # if subprocess.call(build_privateer_cmd) != 0: + # sys.exit("Failed to build Privateer's dependencies") + +# I make a new command that will build the shared-library +class build_privateerdeps(Command): + user_options = [] + def initialize_options(self): + pass + def finalize_options(self): + pass + def run(self): + pass + # run_privateer_install_script() + +# I subclass install so that it will call my new command +class install(setuptools.command.install.install): + def run(self): + self.run_command('build_privateerdeps') + setuptools.command.install.install.run(self) + +# I do the same for build... +class build(distutils.command.build.build): + sub_commands = [ + ('build_privateerdeps', lambda self: True), + ] + distutils.command.build.build.sub_commands + +# ...and the same for develop +class develop(setuptools.command.develop.develop): + def run(self): + self.run_command('build_privateerdeps') + setuptools.command.develop.develop.run(self) + + +setup( + name='privateer', + version='0.4', + author='Jon Agirre', + author_email='jon.agirre@york.ac.uk', + description='The Swiss Army knife for carbohydrate structure validation, refinement and analysis', + long_description='', + packages=find_packages('src'), + package_dir={'':'src'}, + ext_modules=[CMakeExtension('privateer/privateer')], + cmdclass={ + 'install': install, + 'build_privateerdeps': build_privateerdeps, 'build': build, + 'develop': develop, + 'build_ext': CMakeBuild, + }, + zip_safe=False, +) +