[libc] Add integration tests.

Summary:
This patch aims to add integration tests to check the following:
1) Header files are generated as expected.
2) Libc functions have the correct public name.
3) Libc functions have the correct return type and parameter types.
4) Symbols are exposed in the public lib.a files.

Reviewers: sivachandra, abrachet

Reviewed By: sivachandra

Subscribers: aheejin, ecnelises, dxf, mgorny, jfb, tschuett, libc-commits

Tags: #libc-project

Differential Revision: https://reviews.llvm.org/D79192
This commit is contained in:
Paula Toth 2020-06-02 12:13:57 -07:00
parent 9244be7b05
commit b836ae24a9
9 changed files with 241 additions and 63 deletions

View File

@ -81,6 +81,10 @@ function(add_entrypoint_object target_name)
)
get_fq_target_name(${target_name} fq_target_name)
set(entrypoint_name ${target_name})
if(ADD_ENTRYPOINT_OBJ_NAME)
set(entrypoint_name ${ADD_ENTRYPOINT_OBJ_NAME})
endif()
if(ADD_ENTRYPOINT_OBJ_ALIAS)
# Alias targets help one add aliases to other entrypoint object targets.
@ -109,6 +113,7 @@ function(add_entrypoint_object target_name)
set_target_properties(
${fq_target_name}
PROPERTIES
"ENTRYPOINT_NAME" ${entrypoint_name}
"TARGET_TYPE" ${ENTRYPOINT_OBJ_TARGET_TYPE}
"IS_ALIAS" "YES"
"OBJECT_FILE" ""
@ -125,11 +130,6 @@ function(add_entrypoint_object target_name)
message(FATAL_ERROR "`add_entrypoint_object` rule requires HDRS to be specified.")
endif()
set(entrypoint_name ${target_name})
if(ADD_ENTRYPOINT_OBJ_NAME)
set(entrypoint_name ${ADD_ENTRYPOINT_OBJ_NAME})
endif()
set(objects_target_name "${fq_target_name}_objects")
add_library(
@ -199,6 +199,7 @@ function(add_entrypoint_object target_name)
set_target_properties(
${fq_target_name}
PROPERTIES
"ENTRYPOINT_NAME" ${entrypoint_name}
"TARGET_TYPE" ${ENTRYPOINT_OBJ_TARGET_TYPE}
"OBJECT_FILE" "${object_file}"
"OBJECT_FILE_RAW" "${object_file_raw}"
@ -255,7 +256,7 @@ function(add_entrypoint_object target_name)
# crossplatform touch.
COMMAND "${CMAKE_COMMAND}" -E touch ${lint_timestamp}
COMMENT "Linting... ${target_name}"
DEPENDS ${clang-tidy} ${objects_target_name} ${ADD_ENTRYPOINT_OBJ_SRCS}
DEPENDS clang-tidy ${objects_target_name} ${ADD_ENTRYPOINT_OBJ_SRCS}
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
)

View File

@ -0,0 +1,49 @@
set(LIBC_ENTRYPOINTS
# assert.h entrypoints
libc.src.assert.__assert_fail
# errno.h entrypoints
libc.src.errno.__errno_location
# signal.h entrypoints
libc.src.signal.raise
libc.src.signal.sigaction
libc.src.signal.sigdelset
libc.src.signal.sigaddset
libc.src.signal.sigemptyset
libc.src.signal.sigprocmask
libc.src.signal.sigfillset
libc.src.signal.signal
# stdlib.h entrypoints
libc.src.stdlib._Exit
libc.src.stdlib.abort
# string.h entrypoints
libc.src.string.memcpy
libc.src.string.strcpy
libc.src.string.strcat
libc.src.string.strlen
# sys/mman.h entrypoints
libc.src.sys.mman.mmap
libc.src.sys.mman.munmap
# threads.h entrypoints
libc.src.threads.mtx_init
libc.src.threads.mtx_lock
libc.src.threads.mtx_unlock
libc.src.threads.thrd_create
libc.src.threads.thrd_join
# unistd.h entrypoints
libc.src.unistd.write
)
set(LIBM_ENTRYPOINTS
# math.h entrypoints
libc.src.math.cosf
libc.src.math.round
libc.src.math.sincosf
libc.src.math.sinf
)

View File

@ -0,0 +1,12 @@
set(PUBLIC_HEADERS
libc.include.assert_h
libc.include.errno
libc.include.math
libc.include.signal
libc.include.stdio
libc.include.stdlib
libc.include.sys_mman
libc.include.sys_syscall
libc.include.threads
libc.include.unistd
)

View File

@ -1,69 +1,16 @@
include("${LIBC_SOURCE_DIR}/config/${LIBC_TARGET_OS}/${LIBC_TARGET_MACHINE}/entrypoints.txt")
include("${LIBC_SOURCE_DIR}/config/${LIBC_TARGET_OS}/${LIBC_TARGET_MACHINE}/headers.txt")
add_entrypoint_library(
llvmlibc
DEPENDS
# assert.h entrypoints
libc.src.assert.__assert_fail
# errno.h entrypoints
libc.src.errno.__errno_location
# signal.h entrypoints
libc.src.signal.raise
libc.src.signal.sigaction
libc.src.signal.sigdelset
libc.src.signal.sigaddset
libc.src.signal.sigemptyset
libc.src.signal.sigprocmask
libc.src.signal.sigfillset
libc.src.signal.signal
# stdlib.h entrypoints
libc.src.stdlib._Exit
libc.src.stdlib.abort
# string.h entrypoints
libc.src.string.bzero
libc.src.string.memcpy
libc.src.string.memset
libc.src.string.strcat
libc.src.string.strcpy
libc.src.string.strlen
# sys/mman.h entrypoints
libc.src.sys.mman.mmap
libc.src.sys.mman.munmap
# threads.h entrypoints
libc.src.threads.call_once
libc.src.threads.mtx_init
libc.src.threads.mtx_lock
libc.src.threads.mtx_unlock
libc.src.threads.thrd_create
libc.src.threads.thrd_join
# unistd.h entrypoints
libc.src.unistd.write
${LIBC_ENTRYPOINTS}
)
add_entrypoint_library(
llvmlibm
DEPENDS
# math.h entrypoints
libc.src.math.ceil
libc.src.math.ceilf
libc.src.math.cosf
libc.src.math.fabs
libc.src.math.fabsf
libc.src.math.floor
libc.src.math.floorf
libc.src.math.expf
libc.src.math.exp2f
libc.src.math.round
libc.src.math.sincosf
libc.src.math.sinf
libc.src.math.trunc
libc.src.math.truncf
${LIBM_ENTRYPOINTS}
)
add_redirector_library(

View File

@ -8,3 +8,91 @@ add_subdirectory(string)
add_subdirectory(sys)
add_subdirectory(threads)
add_subdirectory(unistd)
include("${LIBC_SOURCE_DIR}/config/${LIBC_TARGET_OS}/${LIBC_TARGET_MACHINE}/entrypoints.txt")
include("${LIBC_SOURCE_DIR}/config/${LIBC_TARGET_OS}/${LIBC_TARGET_MACHINE}/headers.txt")
set(public_test ${CMAKE_CURRENT_BINARY_DIR}/public_integration_test.cpp)
set(entrypoints_name_list "")
foreach(entry IN LISTS LIBC_ENTRYPOINTS LIBM_ENTRYPOINTS)
get_target_property(entry_name ${entry} "ENTRYPOINT_NAME")
list(APPEND entrypoints_name_list ${entry_name})
endforeach()
# TODO: Remove these when they are added to the TableGen.
list(REMOVE_ITEM entrypoints_name_list "__assert_fail" "__errno_location")
list(TRANSFORM entrypoints_name_list PREPEND "-e=")
# Generate integration test souce code.
add_custom_command(
OUTPUT ${public_test}
COMMAND $<TARGET_FILE:libc-prototype-testgen> -o ${public_test}
${entrypoints_name_list}
-I ${LIBC_SOURCE_DIR}
${LIBC_SOURCE_DIR}/config/${LIBC_TARGET_OS}/api.td
DEPENDS ${LIBC_SOURCE_DIR}/config/${LIBC_TARGET_OS}/api.td
libc-prototype-testgen ${PUBLIC_HEADERS}
llvmlibc llvmlibm
)
add_executable(
public_integration_test
EXCLUDE_FROM_ALL
${public_test}
)
# Blank out default include directories to prevent accidentally including
# system headers or our own internal headers.
set_target_properties(
public_integration_test
PROPERTIES
INCLUDE_DIRECTORIES ""
)
# Only include we need is the include for cpp::IsSame and our generated
# public headers.
target_include_directories(
public_integration_test BEFORE
PRIVATE
"${LIBC_SOURCE_DIR}/utils/CPP"
"${LIBC_BUILD_DIR}/include"
)
target_compile_options(
public_integration_test
PRIVATE
-ffreestanding
)
target_link_options(
public_integration_test
PRIVATE "-nostdlib"
)
set(library_files)
foreach(library_name IN LISTS "llvmlibc;llvmlibm")
get_target_property(library_file ${library_name} "LIBRARY_FILE")
list(APPEND library_files ${library_file})
endforeach()
if(COMPILER_RESOURCE_DIR AND LLVM_LIBC_ENABLE_LINTING)
add_custom_target(
public_integration_test-tidy
VERBATIM
COMMAND $<TARGET_FILE:clang-tidy> --system-headers
--checks=-*,llvmlibc-restrict-system-libc-headers
"--extra-arg=-resource-dir=${COMPILER_RESOURCE_DIR}"
--header-filter=.*
--warnings-as-errors=llvmlibc-*
"-config={CheckOptions: [{key: llvmlibc-restrict-system-libc-headers.Includes, value: '-*, linux/*, asm/*.h, asm-generic/*.h'}]}"
--quiet
-p ${PROJECT_BINARY_DIR}
${public_test}
DEPENDS
clang-tidy ${public_test}
)
add_dependencies(check-libc public_integration_test-tidy)
endif()
target_link_libraries(public_integration_test
PRIVATE
${library_files}
)
add_dependencies(check-libc public_integration_test)

View File

@ -11,3 +11,5 @@ add_tablegen(libc-hdrgen llvm-libc
PublicAPICommand.cpp
PublicAPICommand.h
)
add_subdirectory(PrototypeTestGen)

View File

@ -0,0 +1,3 @@
CheckOptions:
- key: readability-identifier-naming.VariableCase
value: camelBack

View File

@ -0,0 +1,5 @@
add_tablegen(libc-prototype-testgen llvm-libc
PrototypeTestGen.cpp
../PublicAPICommand.cpp
../Command.cpp
)

View File

@ -0,0 +1,71 @@
//===-- PrototypeTestGen.cpp ----------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
#include "../PublicAPICommand.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/TableGen/Main.h"
#include "llvm/TableGen/Record.h"
namespace {
llvm::cl::list<std::string>
EntrypointNamesOption("e", llvm::cl::desc("<list of entrypoints>"),
llvm::cl::OneOrMore);
} // anonymous namespace
bool TestGeneratorMain(llvm::raw_ostream &OS, llvm::RecordKeeper &records) {
OS << "#include \"TypeTraits.h\"\n";
llvm_libc::APIIndexer G(records);
for (const auto &header : G.PublicHeaders)
OS << "#include <" << header << ">\n";
OS << '\n';
OS << "int main() {\n";
for (const auto &entrypoint : EntrypointNamesOption) {
auto match = G.FunctionSpecMap.find(entrypoint);
if (match == G.FunctionSpecMap.end()) {
llvm::errs() << "ERROR: entrypoint '" << entrypoint
<< "' could not be found in spec in any public header\n";
return true;
}
llvm::Record *functionSpec = match->second;
llvm::Record *retValSpec = functionSpec->getValueAsDef("Return");
std::string returnType =
G.getTypeAsString(retValSpec->getValueAsDef("ReturnType"));
// _Noreturn is an indication for the compiler that a function
// doesn't return, and isn't a type understood by c++ templates.
if (llvm::StringRef(returnType).contains("_Noreturn"))
returnType = "void";
OS << " static_assert(__llvm_libc::cpp::IsSame<" << returnType << '(';
auto args = functionSpec->getValueAsListOfDefs("Args");
for (size_t i = 0, size = args.size(); i < size; ++i) {
llvm::Record *argType = args[i]->getValueAsDef("ArgType");
OS << G.getTypeAsString(argType);
if (i < size - 1)
OS << ", ";
}
OS << "), decltype(" << entrypoint << ")>::Value, ";
OS << '"' << entrypoint
<< " prototype in TableGen does not match public header" << '"';
OS << ");\n";
}
OS << '\n';
OS << " return 0;\n";
OS << "}\n\n";
return false;
}
int main(int argc, char *argv[]) {
llvm::cl::ParseCommandLineOptions(argc, argv);
return TableGenMain(argv[0], TestGeneratorMain);
}