diff --git a/libc/test/utils/CMakeLists.txt b/libc/test/utils/CMakeLists.txt --- a/libc/test/utils/CMakeLists.txt +++ b/libc/test/utils/CMakeLists.txt @@ -1,2 +1,3 @@ add_subdirectory(FPUtil) add_subdirectory(CPP) +add_subdirectory(WrapperGen) diff --git a/libc/test/utils/WrapperGen/CMakeLists.txt b/libc/test/utils/WrapperGen/CMakeLists.txt new file mode 100644 --- /dev/null +++ b/libc/test/utils/WrapperGen/CMakeLists.txt @@ -0,0 +1,48 @@ +function(add_libc_tool_unittest target_name) + if(NOT LLVM_INCLUDE_TESTS) + return() + endif() + + cmake_parse_arguments( + "LIBC_TOOL_UNITTEST" + "" # No optional arguments + "" # Single value arguments + "SRCS;DEPENDS" # Multi-value arguments + ${ARGN} + ) + + add_executable(${target_name} + EXCLUDE_FROM_ALL + ${LIBC_TOOL_UNITTEST_SRCS} + ) + target_link_libraries(${target_name} + PRIVATE + gtest_main + gtest + ${LIBC_TOOL_UNITTEST_DEPENDS} + ) + + add_custom_command( + TARGET ${target_name} + POST_BUILD + COMMAND $ + --path=${LIBC_SOURCE_DIR} + --wrappergen=${CMAKE_BINARY_DIR}/bin/libc-wrappergen + --targetos=${LIBC_TARGET_OS} + ) + add_dependencies(libc-tool-util-tests ${target_name}) +endfunction() + + +add_custom_target(libc-tool-util-tests) + +add_libc_tool_unittest( wrappergen_test + SRCS + wrappergen_test.cpp + DEPENDS +) + +add_dependencies(wrappergen_test libc-wrappergen) + +target_compile_options(wrappergen_test PUBLIC -fno-rtti) +target_link_libraries(wrappergen_test PRIVATE LLVMSupport) \ No newline at end of file diff --git a/libc/test/utils/WrapperGen/wrappergen_test.cpp b/libc/test/utils/WrapperGen/wrappergen_test.cpp new file mode 100644 --- /dev/null +++ b/libc/test/utils/WrapperGen/wrappergen_test.cpp @@ -0,0 +1,106 @@ +//===-- Unittests for WrapperGen ------------------------------------------===// +// +// 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 "llvm/ADT/SmallString.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/ADT/Twine.h" +#include "llvm/Support/CommandLine.h" +#include "llvm/Support/Error.h" +#include "llvm/Support/FileSystem.h" +#include "llvm/Support/Program.h" +#include "llvm/Support/raw_ostream.h" +#include "gmock/gmock.h" +#include "gtest/gtest.h" +#include + +llvm::cl::opt + LibcPath("path", llvm::cl::desc("Path to the top level libc directory."), + llvm::cl::value_desc(""), llvm::cl::Required); +llvm::cl::opt WrapperGenPath( + "wrappergen", llvm::cl::desc("Path to the wrappergen executable."), + llvm::cl::value_desc(""), llvm::cl::Required); +llvm::cl::opt + TargetOS("targetos", llvm::cl::desc("Name of the target operating system."), + llvm::cl::value_desc(""), llvm::cl::Required); + +class WrapperGenTest : public testing::Test { +public: + std::string IncludeArg; + std::string APIArg; + llvm::StringRef ProgPath; + llvm::Expected STDOutFile = + llvm::sys::fs::TempFile::create("wrappergen-stdout-%%-%%-%%-%%.txt"); + llvm::Expected STDErrFile = + llvm::sys::fs::TempFile::create("wrappergen-stderr-%%-%%-%%-%%.txt"); + +protected: + void SetUp() override { + IncludeArg = "-I="; + IncludeArg.append(LibcPath); + APIArg = LibcPath; + APIArg.append("/config/"); + APIArg.append(TargetOS); + APIArg.append("/api.td"); + ProgPath = llvm::StringRef(WrapperGenPath); + + if (!STDOutFile) { + llvm::errs() << "Error: " << llvm::toString(STDOutFile.takeError()) + << "\n"; + llvm::report_fatal_error( + "Temporary file failed to initialize for libc-wrappergen tests."); + } + if (!STDErrFile) { + llvm::errs() << "Error: " << llvm::toString(STDErrFile.takeError()) + << "\n"; + llvm::report_fatal_error( + "Temporary file failed to initialize for libc-wrappergen tests."); + } + } + void TearDown() override { + llvm::consumeError(STDOutFile.get().discard()); + llvm::consumeError(STDErrFile.get().discard()); + } +}; + +TEST_F(WrapperGenTest, RunWrapperGen) { + llvm::Optional Redirects[] = { + llvm::None, llvm::StringRef(STDOutFile.get().TmpName), + llvm::StringRef(STDErrFile.get().TmpName)}; + + llvm::StringRef ArgV[] = {ProgPath, llvm::StringRef(IncludeArg), + llvm::StringRef(APIArg), "--name", "strlen"}; + + int ExitCode = + llvm::sys::ExecuteAndWait(ProgPath, ArgV, llvm::None, Redirects); + + EXPECT_EQ(ExitCode, 0); + + constexpr size_t BufferSize = 128; + char Buffer[BufferSize]; + + int STDErrFD = STDErrFile.get().FD; + std::string STDErrOutput; + while (read(STDErrFD, Buffer, BufferSize)) { + STDErrOutput.append(Buffer); + } + ASSERT_EQ(STDErrOutput, ""); + + int STDOutFD = STDOutFile.get().FD; + std::string STDOutOutput; + + while (read(STDOutFD, Buffer, BufferSize)) { + + STDOutOutput.append(Buffer); + } + + ASSERT_EQ(STDOutOutput, "#include \"src/string/strlen.h\"\n" + "extern \"C\" size_t strlen(const char * __arg0) {\n" + " return __llvm_libc::strlen(__arg0);\n" + "}\n"); + // TODO:(michaelrj) Figure out how to make this less brittle. +}