diff --git a/libc/cmake/modules/LLVMLibCTestRules.cmake b/libc/cmake/modules/LLVMLibCTestRules.cmake --- a/libc/cmake/modules/LLVMLibCTestRules.cmake +++ b/libc/cmake/modules/LLVMLibCTestRules.cmake @@ -269,3 +269,129 @@ ) add_dependencies(libc-fuzzer ${fq_target_name}) endfunction(add_libc_fuzzer) + +# Rule to add an integration test. An integration test is like a unit test +# but does not use the system libc. Not even the loader from the system libc +# is linked to the final executable. The final exe is fully statically linked. +# The libc that the final exe links to consists of only the object files of +# the DEPENDS targets. +# +# Usage: +# add_integration_test( +# +# SUITE +# SRCS [src2.cpp ...] +# HDRS [hdr1.cpp ...] +# LOADER +# DEPENDS +# ARGS +# ENV +# ) +# +# The loader target should provide a property named LOADER_OBJECT which is +# the full path to the object file produces when the loader is built. +# +# The DEPENDS list can be empty. If not empty, it should be a list of +# targets added with add_entrypoint_object or add_object_library. +function(add_integration_test test_name) + cmake_parse_arguments( + "INTEGRATION_TEST" + "" # No optional arguments + "SUITE;LOADER" # Single value arguments + "SRCS;HDRS;DEPENDS;ARGS;ENV" # Multi-value arguments + ${ARGN} + ) + get_fq_target_name(${test_name} fq_target_name) + + if(NOT INTEGRATION_TEST_SUITE) + message(FATAL_ERROR "SUITE not specified for ${fq_target_name}") + endif() + if(NOT INTEGRATION_TEST_LOADER) + message(FATAL_ERROR "The LOADER to link to the integration test is missing.") + endif() + if(NOT INTEGRATION_TEST_SRCS) + message(FATAL_ERROR "The SRCS list for add_integration_test is missing.") + endif() + + get_fq_target_name(${test_name}.libc fq_libc_target_name) + + get_fq_deps_list(fq_deps_list ${INTEGRATION_TEST_DEPENDS}) + # Add memory functions to which compilers can emit calls. + list(APPEND fq_deps_list + libc.src.string.bcmp + libc.src.string.bzero + libc.src.string.memcmp + libc.src.string.memcpy + libc.src.string.memset) + list(REMOVE_DUPLICATES fq_deps_list) + # TODO: Instead of gathering internal object files from entrypoints, + # collect the object files with public names of entrypoints. + get_object_files_for_test( + link_object_files skipped_entrypoints_list ${fq_deps_list}) + if(skipped_entrypoints_list) + message(STATUS "Skipping ${fq_target_name} as it has skipped deps.") + return() + endif() + + # Create a sysroot structure + set(sysroot ${CMAKE_CURRENT_BINARY_DIR}/${test_name}/sysroot) + file(MAKE_DIRECTORY ${sysroot}) + file(MAKE_DIRECTORY ${sysroot}/include) + set(sysroot_lib ${sysroot}/lib) + file(MAKE_DIRECTORY ${sysroot_lib}) + # Add dummy crti.o, crtn.o, libm.a and libc++.a + file(TOUCH ${sysroot_lib}/crti.o) + file(TOUCH ${sysroot_lib}/crtn.o) + file(TOUCH ${sysroot_lib}/libm.a) + file(TOUCH ${sysroot_lib}/libc++.a) + # Copy the loader object + get_target_property(loader_object_file ${INTEGRATION_TEST_LOADER} LOADER_OBJECT) + if(NOT loader_object_file) + message(FATAL_ERROR "Missing LOADER_OBJECT property of ${INTEGRATION_TEST_LOADER}.") + endif() + set(loader_dst ${sysroot_lib}/${LIBC_TARGET_ARCHITECTURE}-linux-gnu/crt1.o) + add_custom_command( + OUTPUT ${loader_dst} + COMMAND cmake -E copy ${loader_object_file} ${loader_dst} + DEPENDS ${INTEGRATION_TEST_LOADER} + ) + add_custom_target( + ${fq_target_name}.__copy_loader__ + DEPENDS ${loader_dst} + ) + + add_library( + ${fq_libc_target_name} + STATIC + ${link_object_files} + ) + set_target_properties(${fq_libc_target_name} PROPERTIES ARCHIVE_OUTPUT_NAME c) + set_target_properties(${fq_libc_target_name} PROPERTIES ARCHIVE_OUTPUT_DIRECTORY ${sysroot_lib}) + + add_executable( + ${fq_target_name} + EXCLUDE_FROM_ALL + ${INTEGRATION_TEST_SRCS} + ${INTEGRATION_TEST_HDRS} + ) + target_include_directories( + ${fq_target_name} + PRIVATE + ${LIBC_SOURCE_DIR} + ${LIBC_BUILD_DIR} + ${LIBC_BUILD_DIR}/include + ) + target_link_options(${fq_target_name} PRIVATE --sysroot=${sysroot} -static -stdlib=libc++) + add_dependencies(${fq_target_name} + ${fq_target_name}.__copy_loader__ + ${fq_libc_target_name} + libc.utils.IntegrationTest.test) + + add_custom_command( + TARGET ${fq_target_name} + POST_BUILD + COMMAND ${INTEGRATION_TEST_ENV} $ ${INTEGRATION_TEST_ARGS} + ) + + add_dependencies(${INTEGRATION_TEST_SUITE} ${fq_target_name}) +endfunction(add_integration_test) diff --git a/libc/config/linux/x86_64/entrypoints.txt b/libc/config/linux/x86_64/entrypoints.txt --- a/libc/config/linux/x86_64/entrypoints.txt +++ b/libc/config/linux/x86_64/entrypoints.txt @@ -226,6 +226,7 @@ # libc.src.stdlib.abort libc.src.stdlib.atexit libc.src.stdlib.exit + libc.src.stdlib.getenv # signal.h entrypoints # TODO: Enable signal.h entrypoints after fixing signal.h diff --git a/libc/loader/linux/CMakeLists.txt b/libc/loader/linux/CMakeLists.txt --- a/libc/loader/linux/CMakeLists.txt +++ b/libc/loader/linux/CMakeLists.txt @@ -19,11 +19,12 @@ add_custom_target(${fq_target_name}) add_dependencies(${fq_target_name} ${fq_dep_name}) - get_target_property(dep_objfile ${fq_dep_name} OBJECT_FILES) + get_target_property(loader_object ${fq_dep_name} LOADER_OBJECT) set_target_properties( ${fq_target_name} PROPERTIES "TARGET_TYPE" "${OBJECT_LIBRARY_TARGET_TYPE}" + "LOADER_OBJECT" "${loader_object}" "OBJECT_FILES" "" "DEPS" "${fq_dep_name}" ) @@ -51,6 +52,7 @@ ${fq_target_name} PROPERTIES "TARGET_TYPE" "${OBJECT_LIBRARY_TARGET_TYPE}" + "LOADER_OBJECT" "${objfile}" "OBJECT_FILES" "" "DEPS" "${fq_target_name}.__objects__" ) diff --git a/libc/test/CMakeLists.txt b/libc/test/CMakeLists.txt --- a/libc/test/CMakeLists.txt +++ b/libc/test/CMakeLists.txt @@ -19,4 +19,3 @@ endif() add_subdirectory(integration) -add_subdirectory(loader) diff --git a/libc/test/integration/CMakeLists.txt b/libc/test/integration/CMakeLists.txt --- a/libc/test/integration/CMakeLists.txt +++ b/libc/test/integration/CMakeLists.txt @@ -1 +1,5 @@ +add_custom_target(libc-integration-tests) + +add_subdirectory(loader) add_subdirectory(scudo) +add_subdirectory(src) diff --git a/libc/test/loader/CMakeLists.txt b/libc/test/integration/loader/CMakeLists.txt rename from libc/test/loader/CMakeLists.txt rename to libc/test/integration/loader/CMakeLists.txt --- a/libc/test/loader/CMakeLists.txt +++ b/libc/test/integration/loader/CMakeLists.txt @@ -1,5 +1,3 @@ -add_custom_target(libc_loader_tests) - # A rule to add loader tests. When we have a complete loader, we should # be able to use the add_libc_unittest rule or an extension of it. But, # while the loader is getting built, we need to use a special rule like diff --git a/libc/test/integration/loader/linux/CMakeLists.txt b/libc/test/integration/loader/linux/CMakeLists.txt new file mode 100644 --- /dev/null +++ b/libc/test/integration/loader/linux/CMakeLists.txt @@ -0,0 +1,57 @@ +if(NOT (EXISTS ${LIBC_SOURCE_DIR}/loader/linux/${LIBC_TARGET_ARCHITECTURE})) + message("Skipping loader integration tests for target architecture ${LIBC_TARGET_ARCHITECTURE}.") + return() +endif() + +add_custom_target(libc-loader-tests) +add_dependencies(libc-integration-tests libc-loader-tests) + +add_integration_test( + loader_args_test + SUITE libc-loader-tests + LOADER + libc.loader.linux.crt1 + SRCS + args_test.cpp + ARGS + 1 2 3 + ENV + FRANCE=Paris + GERMANY=Berlin +) + +add_integration_test( + loader_no_envp_test + SUITE libc-loader-tests + LOADER + libc.loader.linux.crt1 + SRCS + main_without_envp.cpp +) + +add_integration_test( + loader_no_args_test + SUITE libc-loader-tests + LOADER + libc.loader.linux.crt1 + SRCS + main_without_args.cpp +) + +if(NOT (${LIBC_TARGET_ARCHITECTURE} STREQUAL "x86_64")) + return() +endif() + +add_integration_test( + loader_tls_test + SUITE libc-loader-tests + LOADER + libc.loader.linux.crt1 + SRCS + tls_test.cpp + DEPENDS + libc.include.errno + libc.include.sys_mman + libc.src.errno.errno + libc.src.sys.mman.mmap +) diff --git a/libc/test/loader/linux/args_test.cpp b/libc/test/integration/loader/linux/args_test.cpp rename from libc/test/loader/linux/args_test.cpp rename to libc/test/integration/loader/linux/args_test.cpp --- a/libc/test/loader/linux/args_test.cpp +++ b/libc/test/integration/loader/linux/args_test.cpp @@ -6,7 +6,7 @@ // //===----------------------------------------------------------------------===// -#include "loader_test.h" +#include "utils/IntegrationTest/test.h" static bool my_streq(const char *lhs, const char *rhs) { const char *l, *r; diff --git a/libc/test/loader/linux/main_without_args.cpp b/libc/test/integration/loader/linux/main_without_args.cpp rename from libc/test/loader/linux/main_without_args.cpp rename to libc/test/integration/loader/linux/main_without_args.cpp diff --git a/libc/test/loader/linux/main_without_envp.cpp b/libc/test/integration/loader/linux/main_without_envp.cpp rename from libc/test/loader/linux/main_without_envp.cpp rename to libc/test/integration/loader/linux/main_without_envp.cpp diff --git a/libc/test/loader/linux/tls_test.cpp b/libc/test/integration/loader/linux/tls_test.cpp rename from libc/test/loader/linux/tls_test.cpp rename to libc/test/integration/loader/linux/tls_test.cpp --- a/libc/test/loader/linux/tls_test.cpp +++ b/libc/test/integration/loader/linux/tls_test.cpp @@ -6,13 +6,12 @@ // //===----------------------------------------------------------------------===// -#include "loader_test.h" - #include "include/errno.h" #include "include/sys/mman.h" #include "src/errno/llvmlibc_errno.h" #include "src/sys/mman/mmap.h" +#include "utils/IntegrationTest/test.h" constexpr int threadLocalDataSize = 101; _Thread_local int a[threadLocalDataSize] = {123}; diff --git a/libc/test/integration/src/CMakeLists.txt b/libc/test/integration/src/CMakeLists.txt new file mode 100644 --- /dev/null +++ b/libc/test/integration/src/CMakeLists.txt @@ -0,0 +1 @@ +add_subdirectory(stdlib) diff --git a/libc/test/integration/src/stdlib/CMakeLists.txt b/libc/test/integration/src/stdlib/CMakeLists.txt new file mode 100644 --- /dev/null +++ b/libc/test/integration/src/stdlib/CMakeLists.txt @@ -0,0 +1,18 @@ +add_custom_target(stdlib-integration-tests) +add_dependencies(libc-integration-tests stdlib-integration-tests) + +add_integration_test( + getenv_test + SUITE + stdlib-integration-tests + SRCS + getenv_test.cpp + LOADER + libc.loader.linux.crt1 + DEPENDS + libc.src.stdlib.getenv + ENV + FRANCE=Paris + GERMANY=Berlin +) + diff --git a/libc/test/loader/linux/getenv_test.cpp b/libc/test/integration/src/stdlib/getenv_test.cpp rename from libc/test/loader/linux/getenv_test.cpp rename to libc/test/integration/src/stdlib/getenv_test.cpp --- a/libc/test/loader/linux/getenv_test.cpp +++ b/libc/test/integration/src/stdlib/getenv_test.cpp @@ -6,9 +6,10 @@ // //===----------------------------------------------------------------------===// -#include "loader_test.h" #include "src/stdlib/getenv.h" +#include "utils/IntegrationTest/test.h" + static bool my_streq(const char *lhs, const char *rhs) { if (lhs == rhs) return true; diff --git a/libc/test/loader/linux/CMakeLists.txt b/libc/test/loader/linux/CMakeLists.txt deleted file mode 100644 --- a/libc/test/loader/linux/CMakeLists.txt +++ /dev/null @@ -1,75 +0,0 @@ -if(NOT (EXISTS ${LIBC_SOURCE_DIR}/loader/linux/${LIBC_TARGET_ARCHITECTURE})) - message("Skipping loader tests for target architecture ${LIBC_TARGET_ARCHITECTURE}.") - return() -endif() - -add_header_library( - loader_test - HDRS - loader_test.h - DEPENDS - libc.src.__support.OSUtil.osutil -) - -add_loader_test( - loader_args_test - SRC - args_test.cpp - DEPENDS - .loader_test - libc.loader.linux.crt1 - ARGS - 1 2 3 - ENV - FRANCE=Paris - GERMANY=Berlin -) - -add_loader_test( - loader_no_envp_test - SRC - main_without_envp.cpp - DEPENDS - .loader_test - libc.loader.linux.crt1 -) - -add_loader_test( - loader_no_args_test - SRC - main_without_args.cpp - DEPENDS - .loader_test - libc.loader.linux.crt1 -) - -# TODO: Disableing this test temporarily. -# add_loader_test( -# getenv_test -# SRC -# getenv_test.cpp -# DEPENDS -# .loader_test -# libc.loader.linux.crt1 -# libc.src.stdlib.getenv -# ENV -# FRANCE=Paris -# GERMANY=Berlin -# ) - -if(NOT (${LIBC_TARGET_ARCHITECTURE} STREQUAL "x86_64")) - return() -endif() - -add_loader_test( - loader_tls_test - SRC - tls_test.cpp - DEPENDS - .loader_test - libc.include.errno - libc.include.sys_mman - libc.loader.linux.crt1 - libc.src.errno.errno - libc.src.sys.mman.mmap -) diff --git a/libc/utils/CMakeLists.txt b/libc/utils/CMakeLists.txt --- a/libc/utils/CMakeLists.txt +++ b/libc/utils/CMakeLists.txt @@ -3,5 +3,6 @@ add_subdirectory(UnitTest) if(LLVM_LIBC_FULL_BUILD) + add_subdirectory(IntegrationTest) add_subdirectory(tools) endif() diff --git a/libc/utils/IntegrationTest/CMakeLists.txt b/libc/utils/IntegrationTest/CMakeLists.txt new file mode 100644 --- /dev/null +++ b/libc/utils/IntegrationTest/CMakeLists.txt @@ -0,0 +1,7 @@ +add_header_library( + test + HDRS + test.h + DEPENDS + libc.src.__support.OSUtil.osutil +) diff --git a/libc/test/loader/linux/loader_test.h b/libc/utils/IntegrationTest/test.h rename from libc/test/loader/linux/loader_test.h rename to libc/utils/IntegrationTest/test.h --- a/libc/test/loader/linux/loader_test.h +++ b/libc/utils/IntegrationTest/test.h @@ -1,4 +1,4 @@ -//===-- Simple checkers for loader tests ------------------------*- C++ -*-===// +//===-- Simple checkers for integrations tests ------------------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -6,8 +6,8 @@ // //===----------------------------------------------------------------------===// -#ifndef LLVM_LIBC_TEST_LOADER_LINUX_LOADER_TEST_H -#define LLVM_LIBC_TEST_LOADER_LINUX_LOADER_TEST_H +#ifndef LLVM_LIBC_UTILS_INTEGRATION_TEST_TEST_H +#define LLVM_LIBC_UTILS_INTEGRATION_TEST_TEST_H #include "src/__support/OSUtil/io.h" #include "src/__support/OSUtil/quick_exit.h" @@ -34,4 +34,4 @@ #define EXPECT_FALSE(val) __CHECK_NE(__FILE__, __LINE__, val, false) #define ASSERT_FALSE(val) __CHECK_NE(__FILE__, __LINE__, val, true) -#endif // LLVM_LIBC_TEST_LOADER_LINUX_LOADER_TEST_H +#endif // LLVM_LIBC_UTILS_INTEGRATION_TEST_TEST_H