Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add CMake option to enable sanitizers and build gtest #3555

Merged
merged 7 commits into from
Mar 12, 2021
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -269,6 +269,7 @@ _Pvt_Extensions
*.app
/windows/LightGBM.VC.db
lightgbm
/testlightgbm

# Created by https://www.gitignore.io/api/python

Expand Down
33 changes: 33 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,12 @@ OPTION(USE_HDFS "Enable HDFS support (EXPERIMENTAL)" OFF)
OPTION(USE_TIMETAG "Set to ON to output time costs" OFF)
OPTION(USE_CUDA "Enable CUDA-accelerated training (EXPERIMENTAL)" OFF)
OPTION(USE_DEBUG "Set to ON for Debug mode" OFF)
OPTION(USE_SANITIZER "Use santizer flags" OFF)
OPTION(SANITIZER_PATH "Path to sanitizer libs")
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Based on #3555 (comment), this should be SET, right?

SET(ENABLED_SANITIZERS "address" "leak" "undefined" CACHE STRING
"Semicolon separated list of sanitizer names. E.g 'address;leak'. Supported sanitizers are
address, leak, undefined and thread.")
OPTION(BUILD_CPP_TEST "Build C++ tests with Google Test" OFF)
OPTION(BUILD_STATIC_LIB "Build static library" OFF)
OPTION(__BUILD_FOR_R "Set to ON if building lib_lightgbm for use with the R package" OFF)
OPTION(__INTEGRATE_OPENCL "Set to ON if building LightGBM with the OpenCL ICD Loader and its dependencies included" OFF)
Expand Down Expand Up @@ -37,6 +43,14 @@ else()
PROJECT(lightgbm LANGUAGES C CXX)
endif()

list(APPEND CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/cmake/modules")

#-- Sanitizer
if (USE_SANITIZER)
include(cmake/Sanitizer.cmake)
enable_sanitizers("${ENABLED_SANITIZERS}")
endif (USE_SANITIZER)

if(__INTEGRATE_OPENCL)
set(__INTEGRATE_OPENCL ON CACHE BOOL "" FORCE)
set(USE_GPU OFF CACHE BOOL "" FORCE)
Expand Down Expand Up @@ -451,6 +465,25 @@ if(__BUILD_FOR_R)
endif(MSVC)
endif(__BUILD_FOR_R)

#-- Google C++ tests
if(BUILD_CPP_TEST)
find_package(GTest CONFIG)
if(NOT GTEST_FOUND)
message(STATUS "Did not find Google Test in the system root. Fetching Google Test now...")
include(FetchContent)
FetchContent_Declare(
googletest
GIT_REPOSITORY https://github.com/google/googletest.git
GIT_TAG release-1.10.0
)
FetchContent_MakeAvailable(googletest)
add_library(GTest::GTest ALIAS gtest)
endif()
file(GLOB CPP_TEST_SOURCES tests/cpp_test/*.cpp)
add_executable(testlightgbm ${CPP_TEST_SOURCES} ${SOURCES})
target_link_libraries(testlightgbm PRIVATE GTest::GTest)
endif()

install(TARGETS lightgbm _lightgbm
RUNTIME DESTINATION ${CMAKE_INSTALL_PREFIX}/bin
LIBRARY DESTINATION ${CMAKE_INSTALL_PREFIX}/lib
Expand Down
69 changes: 69 additions & 0 deletions cmake/Sanitizer.cmake
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
# Set appropriate compiler and linker flags for sanitizers.
#
# Usage of this module:
# enable_sanitizers("address;leak")

# Add flags
macro(enable_sanitizer sanitizer)
if(${sanitizer} MATCHES "address")
find_package(ASan)
set(SAN_COMPILE_FLAGS "${SAN_COMPILE_FLAGS} -fsanitize=address")
if (ASan_FOUND)
link_libraries(${ASan_LIBRARY})
endif (ASan_FOUND)

elseif(${sanitizer} MATCHES "thread")
find_package(TSan)
set(SAN_COMPILE_FLAGS "${SAN_COMPILE_FLAGS} -fsanitize=thread")
if (TSan_FOUND)
link_libraries(${TSan_LIBRARY})
endif (TSan_FOUND)

elseif(${sanitizer} MATCHES "leak")
find_package(LSan)
set(SAN_COMPILE_FLAGS "${SAN_COMPILE_FLAGS} -fsanitize=leak")
if (LSan_FOUND)
link_libraries(${LSan_LIBRARY})
endif (LSan_FOUND)

elseif(${sanitizer} MATCHES "undefined")
find_package(UBSan)
set(SAN_COMPILE_FLAGS "${SAN_COMPILE_FLAGS} -fsanitize=undefined -fno-sanitize-recover=undefined")
if (UBSan_FOUND)
link_libraries(${UBSan_LIBRARY})
endif (UBSan_FOUND)

else()
message(FATAL_ERROR "Santizer ${sanitizer} not supported.")
endif()
endmacro()

macro(enable_sanitizers SANITIZERS)
# Check sanitizers compatibility.
foreach ( _san ${SANITIZERS} )
string(TOLOWER ${_san} _san)
if (_san MATCHES "thread")
if (${_use_other_sanitizers})
message(FATAL_ERROR
"thread sanitizer is not compatible with ${_san} sanitizer.")
endif()
set(_use_thread_sanitizer 1)
else ()
if (${_use_thread_sanitizer})
message(FATAL_ERROR
"${_san} sanitizer is not compatible with thread sanitizer.")
endif()
set(_use_other_sanitizers 1)
endif()
endforeach()

message("Sanitizers: ${SANITIZERS}")

foreach( _san ${SANITIZERS} )
string(TOLOWER ${_san} _san)
enable_sanitizer(${_san})
endforeach()
message("Sanitizers compile flags: ${SAN_COMPILE_FLAGS}")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${SAN_COMPILE_FLAGS}")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${SAN_COMPILE_FLAGS}")
endmacro()
13 changes: 13 additions & 0 deletions cmake/modules/FindASan.cmake
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
set(ASan_LIB_NAME ASan)

find_library(ASan_LIBRARY
NAMES libasan.so libasan.so.5 libasan.so.4 libasan.so.3 libasan.so.2 libasan.so.1 libasan.so.0
PATHS ${SANITIZER_PATH} /usr/lib64 /usr/lib /usr/local/lib64 /usr/local/lib ${CMAKE_PREFIX_PATH}/lib)

include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(ASan DEFAULT_MSG
ASan_LIBRARY)

mark_as_advanced(
ASan_LIBRARY
ASan_LIB_NAME)
13 changes: 13 additions & 0 deletions cmake/modules/FindLSan.cmake
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
set(LSan_LIB_NAME lsan)

find_library(LSan_LIBRARY
NAMES liblsan.so liblsan.so.0 liblsan.so.0.0.0
PATHS ${SANITIZER_PATH} /usr/lib64 /usr/lib /usr/local/lib64 /usr/local/lib ${CMAKE_PREFIX_PATH}/lib)

include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(LSan DEFAULT_MSG
LSan_LIBRARY)

mark_as_advanced(
LSan_LIBRARY
LSan_LIB_NAME)
13 changes: 13 additions & 0 deletions cmake/modules/FindTSan.cmake
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
set(TSan_LIB_NAME tsan)

find_library(TSan_LIBRARY
NAMES libtsan.so libtsan.so.0 libtsan.so.0.0.0
PATHS ${SANITIZER_PATH} /usr/lib64 /usr/lib /usr/local/lib64 /usr/local/lib ${CMAKE_PREFIX_PATH}/lib)

include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(TSan DEFAULT_MSG
TSan_LIBRARY)

mark_as_advanced(
TSan_LIBRARY
TSan_LIB_NAME)
13 changes: 13 additions & 0 deletions cmake/modules/FindUBSan.cmake
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
set(UBSan_LIB_NAME UBSan)

find_library(UBSan_LIBRARY
NAMES libubsan.so libubsan.so.5 libubsan.so.4 libubsan.so.3 libubsan.so.2 libubsan.so.1 libubsan.so.0
PATHS ${SANITIZER_PATH} /usr/lib64 /usr/lib /usr/local/lib64 /usr/local/lib ${CMAKE_PREFIX_PATH}/lib)

include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(UBSan DEFAULT_MSG
UBSan_LIBRARY)

mark_as_advanced(
UBSan_LIBRARY
UBSan_LIB_NAME)
11 changes: 11 additions & 0 deletions tests/cpp_test/test_main.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
/*!
* Copyright (c) 2020 Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See LICENSE file in the project root for license information.
*/
#include <gtest/gtest.h>

int main(int argc, char** argv) {
testing::InitGoogleTest(&argc, argv);
testing::FLAGS_gtest_death_test_style = "threadsafe";
return RUN_ALL_TESTS();
}