Commit 7fd7dfd9 by Maarten L. Hekkelman

Work on WIN32 with or without CCP4

parent 9a76f027
...@@ -11,3 +11,4 @@ Testing/ ...@@ -11,3 +11,4 @@ Testing/
include/cif++/exports.hpp include/cif++/exports.hpp
docs/api docs/api
docs/conf.py docs/conf.py
build_ci/
\ No newline at end of file
...@@ -29,7 +29,6 @@ project(libcifpp VERSION 6.0.0 LANGUAGES CXX) ...@@ -29,7 +29,6 @@ project(libcifpp VERSION 6.0.0 LANGUAGES CXX)
list(PREPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake") list(PREPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake")
include(GNUInstallDirs)
include(CheckFunctionExists) include(CheckFunctionExists)
include(CheckIncludeFiles) include(CheckIncludeFiles)
include(CheckLibraryExists) include(CheckLibraryExists)
...@@ -37,7 +36,6 @@ include(CMakePackageConfigHelpers) ...@@ -37,7 +36,6 @@ include(CMakePackageConfigHelpers)
include(CheckCXXSourceCompiles) include(CheckCXXSourceCompiles)
include(GenerateExportHeader) include(GenerateExportHeader)
include(CTest) include(CTest)
include(CMakeDependentOption)
include(FetchContent) include(FetchContent)
include(ExternalProject) include(ExternalProject)
...@@ -60,17 +58,11 @@ endif() ...@@ -60,17 +58,11 @@ endif()
# Build documentation? # Build documentation?
option(BUILD_DOCUMENTATION "Build the documentation" OFF) option(BUILD_DOCUMENTATION "Build the documentation" OFF)
# We do not want to write an export file for all our symbols...
set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS ON)
# Optionally build a version to be installed inside CCP4 # Optionally build a version to be installed inside CCP4
option(BUILD_FOR_CCP4 "Build a version to be installed in CCP4") option(BUILD_FOR_CCP4 "Build a version to be installed in CCP4")
# Building shared libraries? # Building shared libraries?
if(CMAKE_VERSION VERSION_GREATER_EQUAL 3.22) if(NOT(BUILD_FOR_CCP4 AND WIN32))
cmake_policy(SET CMP0127 NEW)
cmake_dependent_option(BUILD_SHARED_LIBS "Build a shared library instead of a static one" OFF "NOT (BUILD_FOR_CCP4 AND WIN32)" ON)
else()
option(BUILD_SHARED_LIBS "Build a shared library instead of a static one" OFF) option(BUILD_SHARED_LIBS "Build a shared library instead of a static one" OFF)
endif() endif()
...@@ -78,13 +70,13 @@ endif() ...@@ -78,13 +70,13 @@ endif()
option(CIFPP_DOWNLOAD_CCD "Download the CCD file components.cif during installation" ON) option(CIFPP_DOWNLOAD_CCD "Download the CCD file components.cif during installation" ON)
# An optional cron script can be installed to keep the data files up-to-date # An optional cron script can be installed to keep the data files up-to-date
if("${CMAKE_SYSTEM_NAME}" STREQUAL "Linux") if("${CMAKE_SYSTEM_NAME}" STREQUAL "Linux" AND NOT BUILD_FOR_CCP4)
option(CIFPP_INSTALL_UPDATE_SCRIPT "Install the script to update CCD and dictionary files" ON) option(CIFPP_INSTALL_UPDATE_SCRIPT "Install the script to update CCD and dictionary files" ON)
endif() endif()
# When CCP4 is sourced in the environment, we can recreate the symmetry operations table # When CCP4 is sourced in the environment, we can recreate the symmetry operations table
if(EXISTS "$ENV{CCP4}") if(EXISTS "$ENV{CCP4}")
if(EXISTS "$ENV{CLIBD}/syminfo.lib") if(EXISTS "$ENV{CCP4}/lib/data/syminfo.lib")
option(CIFPP_RECREATE_SYMOP_DATA "Recreate SymOp data table in case it is out of date" ON) option(CIFPP_RECREATE_SYMOP_DATA "Recreate SymOp data table in case it is out of date" ON)
else() else()
set(CIFPP_RECREATE_SYMOP_DATA OFF) set(CIFPP_RECREATE_SYMOP_DATA OFF)
...@@ -95,14 +87,14 @@ else() ...@@ -95,14 +87,14 @@ else()
message("Not trying to recreate symop_table_data.hpp since CCP4 is not defined") message("Not trying to recreate symop_table_data.hpp since CCP4 is not defined")
endif() endif()
# Unit tests # CCP4 build
if(BUILD_FOR_CCP4) if(BUILD_FOR_CCP4)
if("$ENV{CCP4}" STREQUAL "" OR NOT EXISTS $ENV{CCP4}) if("$ENV{CCP4}" STREQUAL "" OR NOT EXISTS $ENV{CCP4})
message(FATAL_ERROR "A CCP4 built was requested but CCP4 was not sourced") message(FATAL_ERROR "A CCP4 built was requested but CCP4 was not sourced")
else() else()
list(PREPEND CMAKE_MODULE_PATH "$ENV{CCP4}") list(PREPEND CMAKE_MODULE_PATH "$ENV{CCP4}")
list(PREPEND CMAKE_PREFIX_PATH "$ENV{CCP4}") list(PREPEND CMAKE_PREFIX_PATH "$ENV{CCP4}")
set(CMAKE_INSTALL_PREFIX "$ENV{CCP4}" CACHE PATH "The directory containing CCP4") set(CMAKE_INSTALL_PREFIX "$ENV{CCP4}")
if(WIN32) if(WIN32)
set(BUILD_SHARED_LIBS ON) set(BUILD_SHARED_LIBS ON)
...@@ -110,6 +102,9 @@ if(BUILD_FOR_CCP4) ...@@ -110,6 +102,9 @@ if(BUILD_FOR_CCP4)
endif() endif()
endif() endif()
# Now include the GNUInstallDirs module
include(GNUInstallDirs)
if(WIN32) if(WIN32)
if(${CMAKE_SYSTEM_VERSION} GREATER_EQUAL 10) # Windows 10 if(${CMAKE_SYSTEM_VERSION} GREATER_EQUAL 10) # Windows 10
add_definitions(-D _WIN32_WINNT=0x0A00) add_definitions(-D _WIN32_WINNT=0x0A00)
...@@ -126,6 +121,9 @@ if(WIN32) ...@@ -126,6 +121,9 @@ if(WIN32)
endif() endif()
add_definitions(-DNOMINMAX) add_definitions(-DNOMINMAX)
# We do not want to write an export file for all our symbols...
set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS ON)
endif() endif()
if(MSVC) if(MSVC)
...@@ -162,7 +160,8 @@ if(GXX_LIBSTDCPP) ...@@ -162,7 +160,8 @@ if(GXX_LIBSTDCPP)
message(STATUS "You are probably trying to compile using the g++ standard library which contains a crashing std::regex implementation. Will use boost::regex instead") message(STATUS "You are probably trying to compile using the g++ standard library which contains a crashing std::regex implementation. Will use boost::regex instead")
find_package(Boost 1.80 QUIET COMPONENTS regex) find_package(Boost 1.80 QUIET COMPONENTS regex)
if (NOT Boost_FOUND)
if(NOT Boost_FOUND)
set(BOOST_REGEX_STANDALONE ON) set(BOOST_REGEX_STANDALONE ON)
FetchContent_Declare( FetchContent_Declare(
...@@ -199,6 +198,10 @@ if(MSVC) ...@@ -199,6 +198,10 @@ if(MSVC)
unset(_ZLIB_x86) unset(_ZLIB_x86)
list(APPEND _ZLIB_SEARCHES _ZLIB_SEARCH_NORMAL) list(APPEND _ZLIB_SEARCHES _ZLIB_SEARCH_NORMAL)
if(BUILD_FOR_CCP4)
list(PREPEND _ZLIB_SEARCHES "$ENV{CCP4}/lib")
endif()
foreach(search ${_ZLIB_SEARCHES}) foreach(search ${_ZLIB_SEARCHES})
find_library(ZLIB_LIBRARY NAMES zlibstatic NAMES_PER_DIR ${${search}} PATH_SUFFIXES lib) find_library(ZLIB_LIBRARY NAMES zlibstatic NAMES_PER_DIR ${${search}} PATH_SUFFIXES lib)
endforeach() endforeach()
...@@ -336,6 +339,10 @@ target_include_directories(cifpp ...@@ -336,6 +339,10 @@ target_include_directories(cifpp
target_link_libraries(cifpp PUBLIC Threads::Threads ZLIB::ZLIB ${CIFPP_REQUIRED_LIBRARIES}) target_link_libraries(cifpp PUBLIC Threads::Threads ZLIB::ZLIB ${CIFPP_REQUIRED_LIBRARIES})
if(${EIGEN3_D})
add_dependencies(cifpp ${EIGEN3_D})
endif()
if(CMAKE_CXX_COMPILER_ID STREQUAL "AppleClang") if(CMAKE_CXX_COMPILER_ID STREQUAL "AppleClang")
target_link_options(cifpp PRIVATE -undefined dynamic_lookup) target_link_options(cifpp PRIVATE -undefined dynamic_lookup)
endif(CMAKE_CXX_COMPILER_ID STREQUAL "AppleClang") endif(CMAKE_CXX_COMPILER_ID STREQUAL "AppleClang")
...@@ -346,7 +353,8 @@ if(CIFPP_DOWNLOAD_CCD) ...@@ -346,7 +353,8 @@ if(CIFPP_DOWNLOAD_CCD)
if(EXISTS ${COMPONENTS_CIF}) if(EXISTS ${COMPONENTS_CIF})
file(SIZE ${COMPONENTS_CIF} CCD_FILE_SIZE) file(SIZE ${COMPONENTS_CIF} CCD_FILE_SIZE)
if (CCD_FILE_SIZE EQUAL 0)
if(CCD_FILE_SIZE EQUAL 0)
message(STATUS "Removing empty ${COMPONENTS_CIF} file") message(STATUS "Removing empty ${COMPONENTS_CIF} file")
file(REMOVE "${COMPONENTS_CIF}") file(REMOVE "${COMPONENTS_CIF}")
endif() endif()
...@@ -357,6 +365,9 @@ if(CIFPP_DOWNLOAD_CCD) ...@@ -357,6 +365,9 @@ if(CIFPP_DOWNLOAD_CCD)
file(MAKE_DIRECTORY ${PROJECT_SOURCE_DIR}/data/) file(MAKE_DIRECTORY ${PROJECT_SOURCE_DIR}/data/)
endif() endif()
# Since the file(DOWNLOAD) command in cmake does not use
# compression, we try to download the gzipped version and
# decompress it ourselves.
find_program(GUNZIP gunzip) find_program(GUNZIP gunzip)
if(GUNZIP) if(GUNZIP)
...@@ -364,6 +375,7 @@ if(CIFPP_DOWNLOAD_CCD) ...@@ -364,6 +375,7 @@ if(CIFPP_DOWNLOAD_CCD)
file(DOWNLOAD https://files.wwpdb.org/pub/pdb/data/monomers/components.cif.gz ${COMPONENTS_CIF}.gz file(DOWNLOAD https://files.wwpdb.org/pub/pdb/data/monomers/components.cif.gz ${COMPONENTS_CIF}.gz
SHOW_PROGRESS STATUS CCD_FETCH_STATUS) SHOW_PROGRESS STATUS CCD_FETCH_STATUS)
endif() endif()
add_custom_command(OUTPUT ${COMPONENTS_CIF} add_custom_command(OUTPUT ${COMPONENTS_CIF}
COMMAND ${GUNZIP} ${COMPONENTS_CIF}.gz COMMAND ${GUNZIP} ${COMPONENTS_CIF}.gz
WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}/data/) WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}/data/)
...@@ -372,7 +384,9 @@ if(CIFPP_DOWNLOAD_CCD) ...@@ -372,7 +384,9 @@ if(CIFPP_DOWNLOAD_CCD)
SHOW_PROGRESS STATUS CCD_FETCH_STATUS) SHOW_PROGRESS STATUS CCD_FETCH_STATUS)
endif() endif()
# Do not continue if downloading went wrong
list(POP_FRONT CCD_FETCH_STATUS CCD_FETCH_STATUS_CODE) list(POP_FRONT CCD_FETCH_STATUS CCD_FETCH_STATUS_CODE)
if(CCD_FETCH_STATUS_CODE) if(CCD_FETCH_STATUS_CODE)
message(FATAL_ERROR "Error trying to download CCD file: ${CCD_FETCH_STATUS}") message(FATAL_ERROR "Error trying to download CCD file: ${CCD_FETCH_STATUS}")
endif() endif()
...@@ -389,12 +403,9 @@ else() ...@@ -389,12 +403,9 @@ else()
endif() endif()
target_compile_definitions(cifpp PUBLIC DATA_DIR="${CIFPP_DATA_DIR}") target_compile_definitions(cifpp PUBLIC DATA_DIR="${CIFPP_DATA_DIR}")
if(${EIGEN3_D})
add_dependencies(cifpp ${EIGEN3_D})
endif()
if(UNIX) if(UNIX AND NOT BUILD_FOR_CCP4)
if ("${CMAKE_INSTALL_PREFIX}" STREQUAL "/usr/local") if("${CMAKE_INSTALL_PREFIX}" STREQUAL "/usr/local")
set(CIFPP_CACHE_DIR "/var/cache/libcifpp" CACHE PATH "The directory where downloaded data files are stored") set(CIFPP_CACHE_DIR "/var/cache/libcifpp" CACHE PATH "The directory where downloaded data files are stored")
else() else()
set(CIFPP_CACHE_DIR "${CMAKE_INSTALL_FULL_LOCALSTATEDIR}/cache/libcifpp" CACHE PATH "The directory where downloaded data files are stored") set(CIFPP_CACHE_DIR "${CMAKE_INSTALL_FULL_LOCALSTATEDIR}/cache/libcifpp" CACHE PATH "The directory where downloaded data files are stored")
...@@ -403,6 +414,8 @@ if(UNIX) ...@@ -403,6 +414,8 @@ if(UNIX)
target_compile_definitions(cifpp PUBLIC CACHE_DIR="${CIFPP_CACHE_DIR}") target_compile_definitions(cifpp PUBLIC CACHE_DIR="${CIFPP_CACHE_DIR}")
set(CIFPP_ETC_DIR "${CMAKE_INSTALL_FULL_SYSCONFDIR}" CACHE PATH "The directory where the update configuration file is stored") set(CIFPP_ETC_DIR "${CMAKE_INSTALL_FULL_SYSCONFDIR}" CACHE PATH "The directory where the update configuration file is stored")
else()
unset(CIFPP_CACHE_DIR)
endif() endif()
# Install rules # Install rules
...@@ -422,12 +435,12 @@ endif() ...@@ -422,12 +435,12 @@ endif()
# Clean up old config files (with old names) # Clean up old config files (with old names)
file(GLOB OLD_CONFIG_FILES file(GLOB OLD_CONFIG_FILES
${CMAKE_INSTALL_FULL_LIBDIR}/cmake/cifpp/cifppConfig*.cmake ${CMAKE_INSTALL_FULL_LIBDIR}/cmake/cifpp/cifppConfig*.cmake
${CMAKE_INSTALL_FULL_LIBDIR}/cmake/cifpp/cifppTargets*.cmake) ${CMAKE_INSTALL_FULL_LIBDIR}/cmake/cifpp/cifppTargets*.cmake)
if (OLD_CONFIG_FILES) if(OLD_CONFIG_FILES)
message(STATUS "Installation will remove old config files: ${OLD_CONFIG_FILES}") message(STATUS "Installation will remove old config files: ${OLD_CONFIG_FILES}")
install(CODE "file(REMOVE ${OLD_CONFIG_FILES})") install(CODE "file(REMOVE ${OLD_CONFIG_FILES})")
endif() endif()
install(EXPORT cifpp-targets install(EXPORT cifpp-targets
...@@ -456,7 +469,7 @@ install(FILES ...@@ -456,7 +469,7 @@ install(FILES
DESTINATION ${CIFPP_DATA_DIR} DESTINATION ${CIFPP_DATA_DIR}
) )
if(${CIFPP_CACHE_DIR}) if(CIFPP_CACHE_DIR)
install(FILES install(FILES
${PROJECT_SOURCE_DIR}/rsrc/mmcif_ddl.dic ${PROJECT_SOURCE_DIR}/rsrc/mmcif_ddl.dic
${PROJECT_SOURCE_DIR}/rsrc/mmcif_pdbx.dic ${PROJECT_SOURCE_DIR}/rsrc/mmcif_pdbx.dic
...@@ -498,15 +511,12 @@ write_basic_package_version_file( ...@@ -498,15 +511,12 @@ write_basic_package_version_file(
) )
if(BUILD_TESTING) if(BUILD_TESTING)
set(CATCH_BUILD_TESTING OFF)
# Use a private version of Catch2 since the one in the OS # We're using the older version 2 of Catch2
# might not have been compiled with C++17 as standard
# resulting in link errors.
FetchContent_Declare( FetchContent_Declare(
Catch2 Catch2
GIT_REPOSITORY https://github.com/catchorg/Catch2.git GIT_REPOSITORY https://github.com/catchorg/Catch2.git
GIT_TAG v3.4.0 GIT_TAG v2.13.9
) )
FetchContent_MakeAvailable(Catch2) FetchContent_MakeAvailable(Catch2)
...@@ -549,7 +559,7 @@ endif() ...@@ -549,7 +559,7 @@ endif()
# Optionally install the update scripts for CCD and dictionary files # Optionally install the update scripts for CCD and dictionary files
if(CIFPP_INSTALL_UPDATE_SCRIPT) if(CIFPP_INSTALL_UPDATE_SCRIPT)
if(${CMAKE_SYSTEM_NAME} STREQUAL "Linux" OR ${CMAKE_SYSTEM_NAME} STREQUAL "GNU") if(${CMAKE_SYSTEM_NAME} STREQUAL "Linux" OR ${CMAKE_SYSTEM_NAME} STREQUAL "GNU")
if ("${CMAKE_INSTALL_PREFIX}" STREQUAL "/usr/local") if("${CMAKE_INSTALL_PREFIX}" STREQUAL "/usr/local")
set(CIFPP_CRON_DIR "/etc/cron.weekly" CACHE PATH "The cron directory, for the update script") set(CIFPP_CRON_DIR "/etc/cron.weekly" CACHE PATH "The cron directory, for the update script")
else() else()
set(CIFPP_CRON_DIR "${CIFPP_ETC_DIR}/cron.weekly" CACHE PATH "The cron directory, for the update script") set(CIFPP_CRON_DIR "${CIFPP_ETC_DIR}/cron.weekly" CACHE PATH "The cron directory, for the update script")
......
...@@ -315,11 +315,7 @@ class validator_factory ...@@ -315,11 +315,7 @@ class validator_factory
{ {
public: public:
/// @brief Return the singleton instance /// @brief Return the singleton instance
static validator_factory &instance() static validator_factory &instance();
{
static validator_factory s_instance;
return s_instance;
}
/// @brief Return the validator with name @a dictionary_name /// @brief Return the validator with name @a dictionary_name
const validator &operator[](std::string_view dictionary_name); const validator &operator[](std::string_view dictionary_name);
......
...@@ -586,12 +586,13 @@ void compound_factory::report_missing_compound(const std::string &compound_id) ...@@ -586,12 +586,13 @@ void compound_factory::report_missing_compound(const std::string &compound_id)
std::clog << "\n" << cif::coloured("Configuration error:", white, red) << "\n\n" std::clog << "\n" << cif::coloured("Configuration error:", white, red) << "\n\n"
<< "The attempt to retrieve compound information for " << std::quoted(compound_id) << " failed.\n\n" << "The attempt to retrieve compound information for " << std::quoted(compound_id) << " failed.\n\n"
<< "This information is searched for in a CCD file called components.cif or components.cif.gz\n" << "This information is searched for in a CCD file called components.cif or\n"
<< "which should be located in one of the following directories:\n\n"; << "components.cif.gz which should be located in one of the following directories:\n\n";
cif::list_data_directories(std::clog); cif::list_data_directories(std::clog);
std::clog << "\n(Note that you can add a directory to the search paths by setting the LIBCIFPP_DATA_DIR environmental variable)\n\n"; std::clog << "\n(Note that you can add a directory to the search paths by setting the \n"
<< "LIBCIFPP_DATA_DIR environmental variable)\n\n";
#if defined(CACHE_DIR) #if defined(CACHE_DIR)
std::clog << "On Linux an optional cron script might have been installed that automatically updates\n" std::clog << "On Linux an optional cron script might have been installed that automatically updates\n"
......
...@@ -381,6 +381,12 @@ void validator::report_error(const std::string &msg, bool fatal) const ...@@ -381,6 +381,12 @@ void validator::report_error(const std::string &msg, bool fatal) const
// -------------------------------------------------------------------- // --------------------------------------------------------------------
validator_factory &validator_factory::instance()
{
static validator_factory s_instance;
return s_instance;
}
const validator &validator_factory::operator[](std::string_view dictionary_name) const validator &validator_factory::operator[](std::string_view dictionary_name)
{ {
try try
......
...@@ -24,7 +24,7 @@ ...@@ -24,7 +24,7 @@
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/ */
#include <catch2/catch_test_macros.hpp> #include <catch2/catch.hpp>
#include <stdexcept> #include <stdexcept>
......
...@@ -26,7 +26,7 @@ ...@@ -26,7 +26,7 @@
#include "test-main.hpp" #include "test-main.hpp"
#include <catch2/catch_test_macros.hpp> #include <catch2/catch.hpp>
#include <stdexcept> #include <stdexcept>
......
...@@ -28,7 +28,7 @@ ...@@ -28,7 +28,7 @@
#include <cif++.hpp> #include <cif++.hpp>
#include <catch2/catch_test_macros.hpp> #include <catch2/catch.hpp>
#include <iostream> #include <iostream>
#include <fstream> #include <fstream>
......
#include "cif++/utilities.hpp" #include "cif++/utilities.hpp"
#include <catch2/catch_test_macros.hpp> #include <catch2/catch.hpp>
#include <random> #include <random>
#include <thread> #include <thread>
......
...@@ -26,7 +26,7 @@ ...@@ -26,7 +26,7 @@
#include "test-main.hpp" #include "test-main.hpp"
#include <catch2/catch_test_macros.hpp> #include <catch2/catch.hpp>
#include <stdexcept> #include <stdexcept>
......
...@@ -2,7 +2,8 @@ ...@@ -2,7 +2,8 @@
#include <cif++.hpp> #include <cif++.hpp>
#include <catch2/catch_session.hpp> #define CATCH_CONFIG_RUNNER
#include <catch2/catch.hpp>
std::filesystem::path gTestDir = std::filesystem::current_path(); std::filesystem::path gTestDir = std::filesystem::current_path();
...@@ -11,7 +12,7 @@ int main(int argc, char *argv[]) ...@@ -11,7 +12,7 @@ int main(int argc, char *argv[])
Catch::Session session; // There must be exactly one instance Catch::Session session; // There must be exactly one instance
// Build a new parser on top of Catch2's // Build a new parser on top of Catch2's
using namespace Catch::Clara; using namespace Catch::clara;
auto cli = session.cli() // Get Catch2's command line parser auto cli = session.cli() // Get Catch2's command line parser
| Opt(gTestDir, "data-dir") // bind variable to a new option, with a hint string | Opt(gTestDir, "data-dir") // bind variable to a new option, with a hint string
["-D"]["--data-dir"] // the option names it will respond to ["-D"]["--data-dir"] // the option names it will respond to
......
...@@ -26,8 +26,7 @@ ...@@ -26,8 +26,7 @@
#include "test-main.hpp" #include "test-main.hpp"
#include <catch2/catch_test_macros.hpp> #include <catch2/catch.hpp>
#include <catch2/matchers/catch_matchers_floating_point.hpp>
#include <stdexcept> #include <stdexcept>
......
...@@ -26,7 +26,7 @@ ...@@ -26,7 +26,7 @@
#include "test-main.hpp" #include "test-main.hpp"
#include <catch2/catch_test_macros.hpp> #include <catch2/catch.hpp>
#include <cif++.hpp> #include <cif++.hpp>
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment