From ca7fcf235488b5628ac35167f7c9dd9cdaad89a9 Mon Sep 17 00:00:00 2001 From: Alexey Samsonov Date: Wed, 19 Dec 2012 12:33:39 +0000 Subject: [PATCH] Significantly change the way we build ASan unittests in CMake build tree. Now just-built Clang is used to: 1) compile instrumented sources (as before); 2) compile non-instrumented sources; 3) compile our own instrumented version of googletest; 4) link it all together using -fsanitize=address flag (instead of trying to copy linker behavior in CMake build rules). This makes ASan unittests pretty much self-consistent and independent of other LLVM libraries. llvm-svn: 170541 --- compiler-rt/CMakeLists.txt | 8 + compiler-rt/cmake/Modules/CompilerRTCompile.cmake | 16 ++ .../cmake/Modules/CompilerRTUnittests.cmake | 30 ++++ compiler-rt/lib/asan/tests/CMakeLists.txt | 186 ++++++++++----------- 4 files changed, 145 insertions(+), 95 deletions(-) create mode 100644 compiler-rt/cmake/Modules/CompilerRTCompile.cmake create mode 100644 compiler-rt/cmake/Modules/CompilerRTUnittests.cmake diff --git a/compiler-rt/CMakeLists.txt b/compiler-rt/CMakeLists.txt index 4544f15..bbf41f7 100644 --- a/compiler-rt/CMakeLists.txt +++ b/compiler-rt/CMakeLists.txt @@ -15,6 +15,14 @@ include(LLVMParseArguments) # runtime libraries. cmake_minimum_required(VERSION 2.8.8) +# Add path for custom modules +set(CMAKE_MODULE_PATH + ${CMAKE_MODULE_PATH} + "${CMAKE_CURRENT_SOURCE_DIR}/cmake/Modules" + ) + +set(COMPILER_RT_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}) + # FIXME: Below we assume that the target build of LLVM/Clang is x86, which is # not at all valid. Much of this can be fixed just by switching to use # a just-built-clang binary for the compiles. diff --git a/compiler-rt/cmake/Modules/CompilerRTCompile.cmake b/compiler-rt/cmake/Modules/CompilerRTCompile.cmake new file mode 100644 index 0000000..2794cab --- /dev/null +++ b/compiler-rt/cmake/Modules/CompilerRTCompile.cmake @@ -0,0 +1,16 @@ +include(LLVMParseArguments) + +# Compile a source into an object file with just-built Clang using +# a provided compile flags and dependenices. +# clang_compile( +# CFLAGS +# DEPS ) +macro(clang_compile object_file source) + parse_arguments(SOURCE "CFLAGS;DEPS" "" ${ARGN}) + get_filename_component(source_rpath ${source} REALPATH) + add_custom_command( + OUTPUT ${object_file} + COMMAND clang ${SOURCE_CFLAGS} -c -o "${object_file}" ${source_rpath} + MAIN_DEPENDENCY ${source} + DEPENDS clang ${SOURCE_DEPS}) +endmacro() diff --git a/compiler-rt/cmake/Modules/CompilerRTUnittests.cmake b/compiler-rt/cmake/Modules/CompilerRTUnittests.cmake new file mode 100644 index 0000000..96ad673 --- /dev/null +++ b/compiler-rt/cmake/Modules/CompilerRTUnittests.cmake @@ -0,0 +1,30 @@ +include(AddLLVM) +include(LLVMParseArguments) + +set(COMPILER_RT_GTEST_PATH ${LLVM_MAIN_SRC_DIR}/utils/unittest/googletest) +set(COMPILER_RT_GTEST_SOURCE ${COMPILER_RT_GTEST_PATH}/gtest-all.cc) +set(COMPILER_RT_GTEST_INCLUDE_CFLAGS + -DGTEST_NO_LLVM_RAW_OSTREAM=1 + -I${COMPILER_RT_GTEST_PATH}/include +) + +# Use Clang to link objects into a single executable with just-built +# Clang, using specific link flags. Make executable a part of provided +# test_suite. +# add_compiler_rt_test( +# OBJECTS +# DEPS +# LINK_FLAGS ) +macro(add_compiler_rt_test test_suite test_name) + parse_arguments(TEST "OBJECTS;DEPS;LINK_FLAGS" "" ${ARGN}) + get_unittest_directory(OUTPUT_DIR) + set(output_bin "${OUTPUT_DIR}/${test_name}") + add_custom_command( + OUTPUT ${output_bin} + COMMAND clang ${TEST_OBJECTS} -o "${output_bin}" + ${TEST_LINK_FLAGS} + DEPENDS clang ${TEST_DEPS} ${TEST_OBJECTS}) + add_custom_target(${test_name} DEPENDS ${output_bin}) + # Make the test suite depend on the binary. + add_dependencies(${test_suite} ${test_name}) +endmacro() diff --git a/compiler-rt/lib/asan/tests/CMakeLists.txt b/compiler-rt/lib/asan/tests/CMakeLists.txt index 44f188cf..7db5af7 100644 --- a/compiler-rt/lib/asan/tests/CMakeLists.txt +++ b/compiler-rt/lib/asan/tests/CMakeLists.txt @@ -10,15 +10,20 @@ # instrumentation against the just-built runtime library. include(CheckCXXCompilerFlag) +include(CompilerRTCompile) +include(CompilerRTUnittests) include_directories(..) include_directories(../..) set(ASAN_UNITTEST_COMMON_CFLAGS + ${COMPILER_RT_GTEST_INCLUDE_CFLAGS} + -I${COMPILER_RT_SOURCE_DIR}/include + -I${COMPILER_RT_SOURCE_DIR}/lib + -I${COMPILER_RT_SOURCE_DIR}/lib/asan -Wall -Wno-format -Werror - -fvisibility=hidden -g -O2 ) @@ -46,26 +51,29 @@ else() ) endif() +set(ASAN_LINK_FLAGS -fsanitize=address) +if(ANDROID) + list(APPEND ASAN_LINK_FLAGS -pie) +elseif(APPLE) + # Unit tests on Mac depend on Foundation. + list(APPEND ASAN_LINK_FLAGS -framework Foundation) +endif() +# Unit tests require libstdc++. +list(APPEND ASAN_LINK_FLAGS -lstdc++) + # Support 64-bit and 32-bit builds. if(LLVM_BUILD_32_BITS) list(APPEND ASAN_UNITTEST_COMMON_CFLAGS -m32) + list(APPEND ASAN_LINK_FLAGS -m32) else() list(APPEND ASAN_UNITTEST_COMMON_CFLAGS -m64) + list(APPEND ASAN_LINK_FLAGS -m64) endif() -set(ASAN_GTEST_INCLUDE_CFLAGS - -I${LLVM_MAIN_SRC_DIR}/utils/unittest/googletest/include - -I${LLVM_MAIN_SRC_DIR}/include - -I${LLVM_BINARY_DIR}/include - -D__STDC_CONSTANT_MACROS - -D__STDC_LIMIT_MACROS -) - set(ASAN_BLACKLIST_FILE "${CMAKE_CURRENT_SOURCE_DIR}/asan_test.ignore") set(ASAN_UNITTEST_INSTRUMENTED_CFLAGS ${ASAN_UNITTEST_COMMON_CFLAGS} - ${ASAN_GTEST_INCLUDE_CFLAGS} -fsanitize=address -mllvm "-asan-blacklist=${ASAN_BLACKLIST_FILE}" -mllvm -asan-stack=1 @@ -75,103 +83,91 @@ set(ASAN_UNITTEST_INSTRUMENTED_CFLAGS -mllvm -asan-use-after-return=0 ) -function(add_asan_test testsuite testname) - add_unittest(${testsuite} ${testname} ${ARGN}) - if (APPLE) - # Darwin-specific linker flags. - set_property(TARGET ${testname} APPEND PROPERTY - LINK_FLAGS "-framework Foundation") - target_link_libraries(${testname} clang_rt.asan_osx) - elseif (ANDROID) - target_link_libraries(${testname} clang_rt.asan-arm-android) - elseif (UNIX) - # Linux-specific linker flags. - set_property(TARGET ${testname} APPEND PROPERTY - LINK_FLAGS "-lpthread -ldl -rdynamic") - if(LLVM_BUILD_32_BITS) - target_link_libraries(${testname} clang_rt.asan-i386) - else() - target_link_libraries(${testname} clang_rt.asan-x86_64) - endif() - endif() - set(add_compile_flags "") - get_property(compile_flags TARGET ${testname} PROPERTY COMPILE_FLAGS) - foreach(arg ${ASAN_UNITTEST_COMMON_CFLAGS}) - set(add_compile_flags "${add_compile_flags} ${arg}") - endforeach(arg ${ASAN_UNITTEST_COMMON_CFLAGS}) - set_property(TARGET ${testname} PROPERTY COMPILE_FLAGS - "${compile_flags} ${add_compile_flags}") -endfunction() - -set(ASAN_NOINST_TEST_SOURCES - asan_noinst_test.cc - asan_test_main.cc -) +# Compile source and add it to the object list using compiler +# options in ${ARGN}. +macro(asan_compile obj_list source) + get_filename_component(basename ${source} NAME) + set(output_obj "${basename}.o") + clang_compile(${output_obj} ${source} + CFLAGS ${ARGN} + DEPS gtest ${ASAN_RUNTIME_LIBRARIES} + ${ASAN_BLACKLIST_FILE}) + list(APPEND ${obj_list} ${output_obj}) +endmacro() + +# Link ASan unit test from a set of objects in ${ARGN}. +macro(add_asan_test test_suite test_name) + message(STATUS "Link flags: ${ASAN_LINK_FLAGS}") + add_compiler_rt_test(${test_suite} ${test_name} + OBJECTS ${ARGN} + DEPS ${ASAN_RUNTIME_LIBRARIES} + LINK_FLAGS ${ASAN_LINK_FLAGS}) +endmacro() -set(ASAN_INST_TEST_OBJECTS) +# Main AddressSanitizer unit tests. +add_custom_target(AsanUnitTests) +set_target_properties(AsanUnitTests PROPERTIES FOLDER "ASan unit tests") +# ASan benchmarks (not actively used now). +add_custom_target(AsanBenchmarks) +set_target_properties(AsanBenchmarks PROPERTIES FOLDER "Asan benchmarks") # We only support building instrumented tests when we're not cross compiling # and targeting a unix-like system where we can predict viable compilation and # linking strategies. # We use a different approach to build these tests for Android. See below. if("${CMAKE_HOST_SYSTEM}" STREQUAL "${CMAKE_SYSTEM}" AND UNIX AND NOT ANDROID) - - # This function is a custom routine to manage manually compiling source files - # for unit tests with the just-built Clang binary, using the ASan - # instrumentation, and linking them into a test executable. - function(add_asan_compile_command source extra_cflags) - set(output_obj "${source}.asan.o") - add_custom_command( - OUTPUT ${output_obj} - COMMAND clang - ${ASAN_UNITTEST_INSTRUMENTED_CFLAGS} - ${extra_cflags} - -c -o "${output_obj}" - ${CMAKE_CURRENT_SOURCE_DIR}/${source} - MAIN_DEPENDENCY ${source} - DEPENDS clang ${ASAN_RUNTIME_LIBRARIES} ${ASAN_BLACKLIST_FILE} ${ARGN} - ) - endfunction() - - add_asan_compile_command(asan_globals_test.cc "") - add_asan_compile_command(asan_test.cc "") - list(APPEND ASAN_INST_TEST_OBJECTS asan_globals_test.cc.asan.o - asan_test.cc.asan.o) + # Build gtest instrumented with ASan. + set(ASAN_INST_GTEST) + asan_compile(ASAN_INST_GTEST ${COMPILER_RT_GTEST_SOURCE} + ${ASAN_UNITTEST_INSTRUMENTED_CFLAGS}) + # Instrumented tests. + set(ASAN_INST_TEST_OBJECTS) + asan_compile(ASAN_INST_TEST_OBJECTS asan_globals_test.cc + ${ASAN_UNITTEST_INSTRUMENTED_CFLAGS}) + asan_compile(ASAN_INST_TEST_OBJECTS asan_test.cc + ${ASAN_UNITTEST_INSTRUMENTED_CFLAGS}) if (APPLE) - add_asan_compile_command(asan_mac_test.mm "-ObjC") - list(APPEND ASAN_INST_TEST_OBJECTS asan_mac_test.mm.asan.o) + asan_compile(ASAN_INST_TEST_OBJECTS asan_mac_test.mm + ${ASAN_UNITTEST_INSTRUMENTED_CFLAGS} -ObjC) endif() - - # Build benchmarks test instrumented with AddressSanitizer. - add_asan_compile_command(asan_benchmarks_test.cc "") - add_custom_target(AsanBenchmarks) - set_target_properties(AsanBenchmarks PROPERTIES FOLDER "Asan benchmarks") - add_asan_test(AsanBenchmarks AsanBenchmark asan_benchmarks_test.cc.asan.o) + # Uninstrumented tests. + set(ASAN_NOINST_TEST_OBJECTS) + asan_compile(ASAN_NOINST_TEST_OBJECTS asan_noinst_test.cc + ${ASAN_UNITTEST_COMMON_CFLAGS}) + asan_compile(ASAN_NOINST_TEST_OBJECTS asan_test_main.cc + ${ASAN_UNITTEST_COMMON_CFLAGS}) + + # Link everything together. + add_asan_test(AsanUnitTests AsanTest ${ASAN_NOINST_TEST_OBJECTS} + ${ASAN_INST_TEST_OBJECTS} ${ASAN_INST_GTEST}) + + # Instrumented benchmarks. + set(ASAN_BENCHMARKS_OBJECTS) + asan_compile(ASAN_BENCHMARKS_OBJECTS asan_benchmarks_test.cc + ${ASAN_UNITTEST_INSTRUMENTED_CFLAGS}) + # Link benchmarks. + add_asan_test(AsanBenchmarks AsanBenchmark ${ASAN_BENCHMARKS_OBJECTS} + ${ASAN_INST_GTEST}) endif() -# Main AddressSanitizer unit tests. -add_custom_target(AsanUnitTests) -set_target_properties(AsanUnitTests PROPERTIES FOLDER "ASan unit tests") - if(ANDROID) + # We assume that unit tests on Android are built in a build + # tree with fresh Clang as a host compiler. + set(ASAN_NOINST_TEST_SOURCES asan_noinst_test.cc asan_test_main.cc) set(ASAN_INST_TEST_SOURCES asan_globals_test.cc asan_test.cc) - add_library(asan_noinst_test OBJECT - ${ASAN_NOINST_TEST_SOURCES} - ) - set_target_compile_flags(asan_noinst_test - ${ASAN_UNITTEST_COMMON_CFLAGS} ${ASAN_GTEST_INCLUDE_CFLAGS} - ) - add_asan_test(AsanUnitTests AsanTest - ${ASAN_INST_TEST_SOURCES} + add_library(asan_noinst_test OBJECT ${ASAN_NOINST_TEST_SOURCES}) + set_target_compile_flags(asan_noinst_test ${ASAN_UNITTEST_COMMON_CFLAGS}) + add_library(asan_inst_test OBJECT + ${ASAN_INST_TEST_SOURCES} ${COMPILER_RT_GTEST_SOURCE}) + set_target_compile_flags(asan_inst_test ${ASAN_UNITTEST_INSTRUMENTED_CFLAGS}) + add_executable(AsanTest $ - ) - set_target_compile_flags(AsanTest - ${ASAN_UNITTEST_INSTRUMENTED_CFLAGS} ${ASAN_GTEST_INCLUDE_CFLAGS} - ) - set_target_link_flags(AsanTest - -pie - ) -else() - add_asan_test(AsanUnitTests AsanTest ${ASAN_NOINST_TEST_SOURCES} - ${ASAN_INST_TEST_OBJECTS}) + $ + ) + # Setup correct output directory and link flags. + get_unittest_directory(OUTPUT_DIR) + set_target_properties(AsanTest PROPERTIES + RUNTIME_OUTPUT_DIRECTORY ${OUTPUT_DIR}) + set_target_link_flags(AsanTest ${ASAN_LINK_FLAGS}) + add_dependencies(AsanUnitTests AsanTest) endif() -- 2.7.4