diff --git a/CMake/FindArmadillo.cmake b/CMake/FindArmadillo.cmake index 3d696b0abb..8c5e0260ca 100644 --- a/CMake/FindArmadillo.cmake +++ b/CMake/FindArmadillo.cmake @@ -81,23 +81,43 @@ endif() # Link to support libraries in either case on MSVC. if(NOT _ARMA_USE_WRAPPER OR MSVC) if(_ARMA_USE_LAPACK) - if(ARMADILLO_FIND_QUIETLY OR NOT ARMADILLO_FIND_REQUIRED) - find_package(LAPACK QUIET) + if(APPLE) + # Use -framework Accelerate to link against the Accelerate framework on + # MacOS; ignore OpenBLAS or other variants. + set(LAPACK_LIBRARIES "-framework Accelerate") + set(LAPACK_FOUND YES) else() - find_package(LAPACK REQUIRED) + if(ARMADILLO_FIND_QUIETLY OR NOT ARMADILLO_FIND_REQUIRED) + find_package(LAPACK QUIET) + else() + find_package(LAPACK REQUIRED) + endif() endif() + if(LAPACK_FOUND) set(_ARMA_SUPPORT_LIBRARIES "${_ARMA_SUPPORT_LIBRARIES}" "${LAPACK_LIBRARIES}") endif() endif() if(_ARMA_USE_BLAS) - if(ARMADILLO_FIND_QUIETLY OR NOT ARMADILLO_FIND_REQUIRED) - find_package(BLAS QUIET) + if(APPLE) + # Use -framework Accelerate to link against the Accelerate framework on + # MacOS; ignore OpenBLAS or other variants. + set(BLAS_LIBRARIES "-framework Accelerate") + set(BLAS_FOUND YES) else() - find_package(BLAS REQUIRED) + if(ARMADILLO_FIND_QUIETLY OR NOT ARMADILLO_FIND_REQUIRED) + find_package(BLAS QUIET) + else() + find_package(BLAS REQUIRED) + endif() endif() + if(BLAS_FOUND) - set(_ARMA_SUPPORT_LIBRARIES "${_ARMA_SUPPORT_LIBRARIES}" "${BLAS_LIBRARIES}") + # Avoid doubly linking (not that it makes much difference other than a + # nicer command-line). + if (NOT BLAS_LIBRARIES EQUAL LAPACK_LIBRARIES) + set(_ARMA_SUPPORT_LIBRARIES "${_ARMA_SUPPORT_LIBRARIES}" "${BLAS_LIBRARIES}") + endif () endif() endif() if(_ARMA_USE_ARPACK) diff --git a/HISTORY.md b/HISTORY.md index 96ce4eacb5..e9fe27ace3 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -8,6 +8,9 @@ * Optimize and strip compiled Python bindings when possible, resulting in significant size minimization (#3310). + * The `/std:c++17` and `/Zc:__cplusplus` options are now required when using + Visual Studio (#3318). Documentation and compile-time checks added. + ### mlpack 4.0.0 ###### 2022-10-23 * Bump C++ standard requirement to C++14 (#3233). diff --git a/doc/user/build_windows.md b/doc/user/build_windows.md index f5cc05e79c..61cbae34ef 100644 --- a/doc/user/build_windows.md +++ b/doc/user/build_windows.md @@ -36,6 +36,9 @@ an existing one). The library is immediately ready to be included (via preprocessor directives) and used in your project without additional configuration. +Note that when building mlpack, the `/std:c++17` and `/Zc:__cplusplus` options +are required for Visual Studio. + ## Build Environment This tutorial has been designed and tested using: diff --git a/doc/user/sample_ml_app.md b/doc/user/sample_ml_app.md index 883758fcba..dab879f68f 100644 --- a/doc/user/sample_ml_app.md +++ b/doc/user/sample_ml_app.md @@ -43,6 +43,11 @@ this issue, disable "Conformance Mode" under C/C++ > Language. *Note*: you may need to change the paths of the include directories or libraries above, given how you installed the dependencies. +*Note*: mlpack requires that the `/std:c++17` and `/Zc:__cplusplus` options be +set for the Visual Studio compiler. This is done by default in the provided +example, but for your own projects, make sure that these options are set, +otherwise compilation will fail. + ## The App's Goal This app aims to exercise an end-to-end machine learning workflow. We will diff --git a/src/mlpack/base.hpp b/src/mlpack/base.hpp index 4879167814..151a59b589 100644 --- a/src/mlpack/base.hpp +++ b/src/mlpack/base.hpp @@ -69,12 +69,14 @@ // Backport std::any from C+17 to C++11 to replace boost::any. // Use mnmlstc backport implementation only if compiler does not // support C++17. -#if __cplusplus < 201703L +#if __cplusplus < 201703L && !defined(_MSC_VER) #include #include #define MLPACK_ANY core::v2::any #define MLPACK_ANY_CAST core::v2::any_cast #define MLPACK_STRING_VIEW core::v2::string_view +#elif __cplusplus < 201703L && defined(_MSC_VER) + #error "When using Visual Studio, mlpack should be compiled with /Zc:__cplusplus and /std:c++17 or newer." #else #include #include diff --git a/src/mlpack/bindings/R/CMakeLists.txt b/src/mlpack/bindings/R/CMakeLists.txt index 4aa4033aee..804528c099 100644 --- a/src/mlpack/bindings/R/CMakeLists.txt +++ b/src/mlpack/bindings/R/CMakeLists.txt @@ -374,8 +374,12 @@ if (BUILD_R_BINDINGS) ${CMAKE_SOURCE_DIR}/src/mlpack/bindings/R/print_input_processing.hpp ${CMAKE_SOURCE_DIR}/src/mlpack/bindings/R/print_serialize_util.hpp ${CMAKE_SOURCE_DIR}/src/mlpack/bindings/R/print_output_processing.hpp) - target_link_libraries(generate_r_${name} ${MLPACK_LIBRARIES}) - set_target_properties(generate_r_${name} PROPERTIES + if (BUILD_SHARED_LIBS) + target_link_libraries(generate_r_${name} ${MLPACK_LIBRARIES}) + else () + target_link_libraries(generate_r_${name} -static ${MLPACK_LIBRARIES}) + endif () + set_target_properties(generate_r_${name} PROPERTIES COMPILE_FLAGS "-DBINDING_TYPE=BINDING_TYPE_R" RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/src/mlpack/bindings/R/build/bin/") add_custom_command(TARGET generate_r_${name} POST_BUILD diff --git a/src/mlpack/bindings/go/CMakeLists.txt b/src/mlpack/bindings/go/CMakeLists.txt index efc5396a3b..f9b9dbd074 100644 --- a/src/mlpack/bindings/go/CMakeLists.txt +++ b/src/mlpack/bindings/go/CMakeLists.txt @@ -140,7 +140,11 @@ if (BUILD_GO_SHLIB) add_library(mlpack_go_util SHARED ${CMAKE_SOURCE_DIR}/src/mlpack/bindings/go/mlpack/capi/arma_util.cpp ${CMAKE_SOURCE_DIR}/src/mlpack/bindings/go/mlpack/capi/io_util.cpp) - target_link_libraries(mlpack_go_util ${MLPACK_LIBRARIES}) + if (BUILD_SHARED_LIBS) + target_link_libraries(mlpack_go_util ${MLPACK_LIBRARIES}) + else () + target_link_libraries(mlpack_go_util -static ${MLPACK_LIBRARIES}) + endif () target_compile_definitions(mlpack_go_util PUBLIC -DBINDING_TYPE=BINDING_TYPE_GO -DMLPACK_PRINT_INFO @@ -213,7 +217,11 @@ if (BUILD_GO_BINDINGS) ${CMAKE_BINARY_DIR}/src/mlpack/bindings/go/generate_go_${name}.cpp ${CMAKE_SOURCE_DIR}/src/mlpack/bindings/go/print_go.hpp ${CMAKE_SOURCE_DIR}/src/mlpack/bindings/go/print_go.cpp) - target_link_libraries(generate_go_${name} ${MLPACK_LIBRARIES}) + if (BUILD_SHARED_LIBS) + target_link_libraries(generate_go_${name} ${MLPACK_LIBRARIES}) + else () + target_link_libraries(generate_go_${name} -static ${MLPACK_LIBRARIES}) + endif () set_target_properties(generate_go_${name} PROPERTIES COMPILE_FLAGS -DBINDING_TYPE=BINDING_TYPE_GO) add_custom_command(TARGET generate_go_${name} POST_BUILD @@ -242,7 +250,11 @@ if(BUILD_GO_SHLIB) # Build libmlpack_go_${name}.so. add_library(mlpack_go_${name} SHARED ${CMAKE_BINARY_DIR}/src/mlpack/bindings/go/build/${name}.cpp) - target_link_libraries(mlpack_go_${name} mlpack_go_util) + if (BUILD_SHARED_LIBS) + target_link_libraries(mlpack_go_${name} mlpack_go_util) + else () + target_link_libraries(mlpack_go_${name} -static mlpack_go_util) + endif () target_compile_definitions(mlpack_go_${name} PUBLIC -DMLPACK_PRINT_INFO -DMLPACK_PRINT_WARN) set_target_properties(mlpack_go_${name} PROPERTIES diff --git a/src/mlpack/bindings/julia/CMakeLists.txt b/src/mlpack/bindings/julia/CMakeLists.txt index 003b5113ab..cad28cf062 100644 --- a/src/mlpack/bindings/julia/CMakeLists.txt +++ b/src/mlpack/bindings/julia/CMakeLists.txt @@ -53,7 +53,11 @@ if (BUILD_JULIA_BINDINGS) add_library(mlpack_julia_util julia_util.h julia_util.cpp) - target_link_libraries(mlpack_julia_util ${MLPACK_LIBRARIES}) + if (BUILD_SHARED_LIBS) + target_link_libraries(mlpack_julia_util ${MLPACK_LIBRARIES}) + else () + target_link_libraries(mlpack_julia_util -static ${MLPACK_LIBRARIES}) + endif () target_compile_definitions(mlpack_julia_util PUBLIC -DMLPACK_PRINT_INFO -DMLPACK_PRINT_WARN) set_target_properties(mlpack_julia_util PROPERTIES @@ -189,7 +193,11 @@ if (BUILD_JULIA_BINDINGS) add_library(mlpack_julia_${name} ${CMAKE_BINARY_DIR}/src/mlpack/bindings/julia/mlpack/build/julia_${name}.h ${CMAKE_BINARY_DIR}/src/mlpack/bindings/julia/mlpack/build/julia_${name}.cpp) - target_link_libraries(mlpack_julia_${name} mlpack_julia_util) + if (BUILD_SHARED_LIBS) + target_link_libraries(mlpack_julia_${name} mlpack_julia_util) + else () + target_link_libraries(mlpack_julia_${name} -static mlpack_julia_util) + endif () target_compile_definitions(mlpack_julia_${name} PUBLIC -DMLPACK_PRINT_INFO -DMLPACK_PRINT_WARN) set_target_properties(mlpack_julia_${name} PROPERTIES @@ -229,7 +237,11 @@ if (BUILD_JULIA_BINDINGS) ${CMAKE_SOURCE_DIR}/src/mlpack/bindings/julia/print_model_type_import.hpp ${CMAKE_SOURCE_DIR}/src/mlpack/bindings/julia/default_param.hpp ${CMAKE_SOURCE_DIR}/src/mlpack/bindings/julia/default_param_impl.hpp) - target_link_libraries(generate_jl_${name} ${MLPACK_LIBRARIES}) + if (BUILD_SHARED_LIBS) + target_link_libraries(generate_jl_${name} ${MLPACK_LIBRARIES}) + else () + target_link_libraries(generate_jl_${name} -static ${MLPACK_LIBRARIES}) + endif () set_target_properties(generate_jl_${name} PROPERTIES COMPILE_FLAGS "-DBINDING_TYPE=BINDING_TYPE_JL" RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/src/mlpack/bindings/julia/mlpack/build/bin/") diff --git a/src/mlpack/bindings/python/CMakeLists.txt b/src/mlpack/bindings/python/CMakeLists.txt index 2d240a88eb..d7a9b67454 100644 --- a/src/mlpack/bindings/python/CMakeLists.txt +++ b/src/mlpack/bindings/python/CMakeLists.txt @@ -125,8 +125,8 @@ if (DEBUG) set(DISABLE_CFLAGS "NDEBUG;MLPACK_HAS_BFD_DL" PARENT_SCOPE) endif () -add_custom_target(python ALL DEPENDS mlpack) -add_custom_target(python_copy ALL DEPENDS mlpack) +add_custom_target(python ALL DEPENDS python_copy) +add_custom_target(python_copy ALL) # The python_configure target is added later; this is a dummy target. add_custom_target(python_configured ALL) @@ -242,7 +242,11 @@ macro (add_python_binding directory name) ${CMAKE_BINARY_DIR}/src/mlpack/bindings/python/generate_pyx_${name}.cpp ${CMAKE_SOURCE_DIR}/src/mlpack/bindings/python/print_pyx.hpp ${CMAKE_SOURCE_DIR}/src/mlpack/bindings/python/print_pyx.cpp) - target_link_libraries(generate_pyx_${name} ${MLPACK_LIBRARIES}) + if (BUILD_SHARED_LIBS) + target_link_libraries(generate_pyx_${name} ${MLPACK_LIBRARIES}) + else () + target_link_libraries(generate_pyx_${name} -static ${MLPACK_LIBRARIES}) + endif () set_target_properties(generate_pyx_${name} PROPERTIES COMPILE_FLAGS -DBINDING_TYPE=BINDING_TYPE_PYX) add_custom_command(TARGET generate_pyx_${name} POST_BUILD @@ -309,7 +313,12 @@ macro (add_python_wrapper directory group_name) ${CMAKE_SOURCE_DIR}/src/mlpack/bindings/python/print_wrapper_py.hpp ${CMAKE_SOURCE_DIR}/src/mlpack/bindings/python/print_wrapper_py.cpp) - target_link_libraries(generate_py_wrapper_${group_name} ${MLPACK_LIBRARIES}) + if (BUILD_SHARED_LIBS) + target_link_libraries(generate_py_wrapper_${group_name} ${MLPACK_LIBRARIES}) + else () + target_link_libraries(generate_py_wrapper_${group_name} -static + ${MLPACK_LIBRARIES}) + endif () add_custom_command(TARGET generate_py_wrapper_${group_name} POST_BUILD COMMAND ${CMAKE_COMMAND} diff --git a/src/mlpack/bindings/python/PythonInstall.cmake b/src/mlpack/bindings/python/PythonInstall.cmake index 6e25fb926e..880f57cc21 100644 --- a/src/mlpack/bindings/python/PythonInstall.cmake +++ b/src/mlpack/bindings/python/PythonInstall.cmake @@ -3,15 +3,18 @@ # A utility script to install Python bindings and fail fatally if installation # was not successful. if (DEFINED ENV{DESTDIR}) - execute_process(COMMAND ${PYTHON_EXECUTABLE} - "${CMAKE_BINARY_DIR}/src/mlpack/bindings/python/setup.py" install - --prefix=${PYTHON_INSTALL_PREFIX} --root=$ENV{DESTDIR} + execute_process(COMMAND ${PYTHON_EXECUTABLE} -m pip install + --prefix=${PYTHON_INSTALL_PREFIX} + --root=$ENV{DESTDIR} . + WORKING_DIRECTORY "${CMAKE_BINARY_DIR}/src/mlpack/bindings/python/" + RESULT_VARIABLE setup_res) +elseif (WIN32) + execute_process(COMMAND ${PYTHON_EXECUTABLE} -m pip install . WORKING_DIRECTORY "${CMAKE_BINARY_DIR}/src/mlpack/bindings/python/" RESULT_VARIABLE setup_res) else () - execute_process(COMMAND ${PYTHON_EXECUTABLE} - "${CMAKE_BINARY_DIR}/src/mlpack/bindings/python/setup.py" install - --prefix=${PYTHON_INSTALL_PREFIX} + execute_process(COMMAND ${PYTHON_EXECUTABLE} -m pip install + --prefix=${PYTHON_INSTALL_PREFIX} . WORKING_DIRECTORY "${CMAKE_BINARY_DIR}/src/mlpack/bindings/python/" RESULT_VARIABLE setup_res) endif () diff --git a/src/mlpack/bindings/python/setup.py.in b/src/mlpack/bindings/python/setup.py.in index aabd85a6fb..23d85b51ff 100644 --- a/src/mlpack/bindings/python/setup.py.in +++ b/src/mlpack/bindings/python/setup.py.in @@ -35,6 +35,11 @@ else: # Windows and Linux linking behavior. libraries = '${MLPACK_LIBRARIES}'.split(' ') +# Workaround: if we receive "m" as a library, what was actually meant was -lm. +for i in range(len(libraries)): + if libraries[i] == 'm': + libraries[i] = '-lm' + # Potentially faulty assumption: we can always link against libraries directly # by just specifying the full path to them on the command line. extra_link_args += libraries @@ -49,16 +54,29 @@ else: extra_cxx_flags = re.sub(' +', ' ', cxx_flags) cxx_flags += ' ' cxx_flags += extra_cxx_flags - extra_args = ['-DBINDING_TYPE=BINDING_TYPE_PYX', '-std=c++14'] + + extra_args = [] + if platform.system() == 'Windows': + # Argument specification is different on MSVC, and also use C++17. + extra_args.extend(['/DBINDING_TYPE=BINDING_TYPE_PYX', '/std:c++17', '/MD', + '/O2', '/Ob2', '/DNDEBUG']) + elif platform.system() == 'Darwin': + extra_args.append('-DBINDING_TYPE=BINDING_TYPE_PYX') + extra_args.append('-std=c++17') + # On OS X and Linux, we try to reduce the size of the generated libraries + # by removing debugging symbols and stripping. + extra_args.append('-g0') + else: + extra_args.append('-DBINDING_TYPE=BINDING_TYPE_PYX') + extra_args.append('-std=c++17') + extra_args.append('-g0') + extra_link_args.append('-Wl,--strip-all') + if '${OpenMP_CXX_FLAGS}' != '': extra_args.append('${OpenMP_CXX_FLAGS}') if cxx_flags: extra_args.extend(cxx_flags.split(' ')) - # Extra options for MSVC compiler. - if platform.system() == 'Windows': - extra_args.extend(['/MD', '/O2', '/Ob2', '/DNDEBUG']) - # This is used for parallel builds; CMake will set PYX_TO_BUILD accordingly. if module is not None: modules=[\