diff --git a/libc/src/__support/File/CMakeLists.txt b/libc/src/__support/File/CMakeLists.txt --- a/libc/src/__support/File/CMakeLists.txt +++ b/libc/src/__support/File/CMakeLists.txt @@ -35,20 +35,32 @@ endif() add_subdirectory(${LIBC_TARGET_OS}) -set(target_file libc.src.__support.File.${LIBC_TARGET_OS}.${LIBC_TARGET_OS}_file) + +set(target_file libc.src.__support.File.${LIBC_TARGET_OS}.file) +set(target_stdout libc.src.__support.File.${LIBC_TARGET_OS}.stdout) +set(target_stderr libc.src.__support.File.${LIBC_TARGET_OS}.stderr) +set(target_stdin libc.src.__support.File.${LIBC_TARGET_OS}.stdin) + +set(file_targets "${target_file};${target_stdout};${target_stdin};${target_stderr}") +set(file_aliases "platform_file;platform_stdout;platform_stdin;platform_stderr") + +foreach(alias target IN ZIP_LISTS file_aliases file_targets) + if(TARGET ${target}) + add_object_library( + ${alias} + ALIAS + ${target} + DEPENDS + ${target} + ) + endif() +endforeach() + set(target_dir libc.src.__support.File.${LIBC_TARGET_OS}.${LIBC_TARGET_OS}_dir) -if((NOT TARGET ${target_file}) OR (NOT TARGET ${target_dir})) +if(NOT TARGET ${target_dir}) return() endif() -add_object_library( - platform_file - ALIAS - ${target_file} - DEPENDS - ${target_file} -) - add_object_library( platform_dir ALIAS diff --git a/libc/src/__support/File/linux/CMakeLists.txt b/libc/src/__support/File/linux/CMakeLists.txt --- a/libc/src/__support/File/linux/CMakeLists.txt +++ b/libc/src/__support/File/linux/CMakeLists.txt @@ -1,7 +1,9 @@ add_object_library( - linux_file + file SRCS file.cpp + HDRS + file.h DEPENDS libc.include.fcntl libc.include.stdio @@ -13,6 +15,30 @@ libc.src.__support.File.file ) +add_object_library( + stdout + SRCS + stdout.cpp + DEPENDS + .file +) + +add_object_library( + stdin + SRCS + stdin.cpp + DEPENDS + .file +) + +add_object_library( + stderr + SRCS + stderr.cpp + DEPENDS + .file +) + add_object_library( linux_dir SRCS diff --git a/libc/src/__support/File/linux/file.h b/libc/src/__support/File/linux/file.h new file mode 100644 --- /dev/null +++ b/libc/src/__support/File/linux/file.h @@ -0,0 +1,32 @@ +//===--- Linux specialization of the File data structure ------------------===// +// +// 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 "src/__support/File/file.h" + +namespace __llvm_libc { + +FileIOResult linux_file_write(File *, const void *, size_t); +FileIOResult linux_file_read(File *, void *, size_t); +ErrorOr linux_file_seek(File *, long, int); +int linux_file_close(File *); + +class LinuxFile : public File { + int fd; + +public: + constexpr LinuxFile(int file_descriptor, uint8_t *buffer, size_t buffer_size, + int buffer_mode, bool owned, File::ModeFlags modeflags) + : File(&linux_file_write, &linux_file_read, &linux_file_seek, + &linux_file_close, buffer, buffer_size, buffer_mode, owned, + modeflags), + fd(file_descriptor) {} + + int get_fd() const { return fd; } +}; + +} // namespace __llvm_libc diff --git a/libc/src/__support/File/linux/file.cpp b/libc/src/__support/File/linux/file.cpp --- a/libc/src/__support/File/linux/file.cpp +++ b/libc/src/__support/File/linux/file.cpp @@ -1,4 +1,4 @@ -//===--- Linux specialization of the File data structure ------------------===// +//===--- Implementation of the Linux specialization of File ---------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -6,6 +6,8 @@ // //===----------------------------------------------------------------------===// +#include "file.h" + #include "src/__support/File/file.h" #include "src/__support/CPP/new.h" @@ -18,31 +20,7 @@ namespace __llvm_libc { -namespace { - -FileIOResult write_func(File *, const void *, size_t); -FileIOResult read_func(File *, void *, size_t); -ErrorOr seek_func(File *, long, int); -int close_func(File *); - -} // anonymous namespace - -class LinuxFile : public File { - int fd; - -public: - constexpr LinuxFile(int file_descriptor, uint8_t *buffer, size_t buffer_size, - int buffer_mode, bool owned, File::ModeFlags modeflags) - : File(&write_func, &read_func, &seek_func, &close_func, buffer, - buffer_size, buffer_mode, owned, modeflags), - fd(file_descriptor) {} - - int get_fd() const { return fd; } -}; - -namespace { - -FileIOResult write_func(File *f, const void *data, size_t size) { +FileIOResult linux_file_write(File *f, const void *data, size_t size) { auto *lf = reinterpret_cast(f); int ret = __llvm_libc::syscall_impl(SYS_write, lf->get_fd(), data, size); if (ret < 0) { @@ -51,7 +29,7 @@ return ret; } -FileIOResult read_func(File *f, void *buf, size_t size) { +FileIOResult linux_file_read(File *f, void *buf, size_t size) { auto *lf = reinterpret_cast(f); int ret = __llvm_libc::syscall_impl(SYS_read, lf->get_fd(), buf, size); if (ret < 0) { @@ -60,7 +38,7 @@ return ret; } -ErrorOr seek_func(File *f, long offset, int whence) { +ErrorOr linux_file_seek(File *f, long offset, int whence) { auto *lf = reinterpret_cast(f); long result; #ifdef SYS_lseek @@ -90,7 +68,7 @@ return result; } -int close_func(File *f) { +int linux_file_close(File *f) { auto *lf = reinterpret_cast(f); int ret = __llvm_libc::syscall_impl(SYS_close, lf->get_fd()); if (ret < 0) { @@ -100,8 +78,6 @@ return 0; } -} // anonymous namespace - ErrorOr openfile(const char *path, const char *mode) { using ModeFlags = File::ModeFlags; auto modeflags = File::mode_flags(mode); @@ -166,28 +142,4 @@ return lf->get_fd(); } -constexpr size_t STDIN_BUFFER_SIZE = 512; -uint8_t stdin_buffer[STDIN_BUFFER_SIZE]; -static LinuxFile StdIn(0, stdin_buffer, STDIN_BUFFER_SIZE, _IOFBF, false, - File::ModeFlags(File::OpenMode::READ)); -File *stdin = &StdIn; - -constexpr size_t STDOUT_BUFFER_SIZE = 1024; -uint8_t stdout_buffer[STDOUT_BUFFER_SIZE]; -static LinuxFile StdOut(1, stdout_buffer, STDOUT_BUFFER_SIZE, _IOLBF, false, - File::ModeFlags(File::OpenMode::APPEND)); -File *stdout = &StdOut; - -constexpr size_t STDERR_BUFFER_SIZE = 0; -static LinuxFile StdErr(2, nullptr, STDERR_BUFFER_SIZE, _IONBF, false, - File::ModeFlags(File::OpenMode::APPEND)); -File *stderr = &StdErr; - } // namespace __llvm_libc - -// Provide the external defintitions of the standard IO streams. -extern "C" { -FILE *stdin = reinterpret_cast(&__llvm_libc::StdIn); -FILE *stderr = reinterpret_cast(&__llvm_libc::StdErr); -FILE *stdout = reinterpret_cast(&__llvm_libc::StdOut); -} diff --git a/libc/src/__support/File/linux/stderr.cpp b/libc/src/__support/File/linux/stderr.cpp new file mode 100644 --- /dev/null +++ b/libc/src/__support/File/linux/stderr.cpp @@ -0,0 +1,23 @@ +//===--- Definition of Linux stderr ---------------------------------------===// +// +// 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 "file.h" +#include + +namespace __llvm_libc { + +constexpr size_t STDERR_BUFFER_SIZE = 0; +static LinuxFile StdErr(2, nullptr, STDERR_BUFFER_SIZE, _IONBF, false, + File::ModeFlags(File::OpenMode::APPEND)); +File *stderr = &StdErr; + +} // namespace __llvm_libc + +extern "C" { +FILE *stderr = reinterpret_cast(&__llvm_libc::StdErr); +} diff --git a/libc/src/__support/File/linux/stdin.cpp b/libc/src/__support/File/linux/stdin.cpp new file mode 100644 --- /dev/null +++ b/libc/src/__support/File/linux/stdin.cpp @@ -0,0 +1,24 @@ +//===--- Definition of Linux stdin ----------------------------------------===// +// +// 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 "file.h" +#include + +namespace __llvm_libc { + +constexpr size_t STDIN_BUFFER_SIZE = 512; +uint8_t stdin_buffer[STDIN_BUFFER_SIZE]; +static LinuxFile StdIn(0, stdin_buffer, STDIN_BUFFER_SIZE, _IOFBF, false, + File::ModeFlags(File::OpenMode::READ)); +File *stdin = &StdIn; + +} // namespace __llvm_libc + +extern "C" { +FILE *stdin = reinterpret_cast(&__llvm_libc::StdIn); +} // extern "C" diff --git a/libc/src/__support/File/linux/stdout.cpp b/libc/src/__support/File/linux/stdout.cpp new file mode 100644 --- /dev/null +++ b/libc/src/__support/File/linux/stdout.cpp @@ -0,0 +1,24 @@ +//===--- Definition of Linux stdout ---------------------------------------===// +// +// 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 "file.h" +#include + +namespace __llvm_libc { + +constexpr size_t STDOUT_BUFFER_SIZE = 1024; +uint8_t stdout_buffer[STDOUT_BUFFER_SIZE]; +static LinuxFile StdOut(1, stdout_buffer, STDOUT_BUFFER_SIZE, _IOLBF, false, + File::ModeFlags(File::OpenMode::APPEND)); +File *stdout = &StdOut; + +} // namespace __llvm_libc + +extern "C" { +FILE *stdout = reinterpret_cast(&__llvm_libc::StdOut); +} // extern "C" diff --git a/libc/src/stdio/CMakeLists.txt b/libc/src/stdio/CMakeLists.txt --- a/libc/src/stdio/CMakeLists.txt +++ b/libc/src/stdio/CMakeLists.txt @@ -422,6 +422,7 @@ list(APPEND printf_deps libc.src.__support.File.file libc.src.__support.File.platform_file + libc.src.__support.File.platform_stdout ) else() set(printf_copts "-DLIBC_COPT_PRINTF_USE_SYSTEM_FILE") diff --git a/libc/src/stdio/generic/CMakeLists.txt b/libc/src/stdio/generic/CMakeLists.txt --- a/libc/src/stdio/generic/CMakeLists.txt +++ b/libc/src/stdio/generic/CMakeLists.txt @@ -72,7 +72,7 @@ libc.src.errno.errno libc.include.stdio libc.src.__support.File.file - libc.src.__support.File.platform_file + libc.src.__support.File.platform_stdout ) add_entrypoint_object( @@ -84,7 +84,7 @@ DEPENDS libc.include.stdio libc.src.__support.File.file - libc.src.__support.File.platform_file + libc.src.__support.File.platform_stdin ) add_entrypoint_object( @@ -96,7 +96,7 @@ DEPENDS libc.include.stdio libc.src.__support.File.file - libc.src.__support.File.platform_file + libc.src.__support.File.platform_stdout ) add_entrypoint_object( @@ -108,5 +108,5 @@ DEPENDS libc.include.stdio libc.src.__support.File.file - libc.src.__support.File.platform_file + libc.src.__support.File.platform_stderr ) diff --git a/libc/src/unistd/CMakeLists.txt b/libc/src/unistd/CMakeLists.txt --- a/libc/src/unistd/CMakeLists.txt +++ b/libc/src/unistd/CMakeLists.txt @@ -266,6 +266,7 @@ libc.src.__support.CPP.optional libc.src.__support.CPP.string_view libc.src.__support.File.file + libc.src.__support.File.platform_stderr libc.src.stdio.fprintf ) diff --git a/libc/test/src/__support/File/CMakeLists.txt b/libc/test/src/__support/File/CMakeLists.txt --- a/libc/test/src/__support/File/CMakeLists.txt +++ b/libc/test/src/__support/File/CMakeLists.txt @@ -21,18 +21,34 @@ libc.src.__support.File.file ) -if (TARGET libc.src.__support.File.platform_file) - add_libc_test( - platform_file_test - SUITE - libc-support-tests - SRCS - platform_file_test.cpp - DEPENDS - libc.src.__support.File.file - libc.src.__support.File.platform_file - libc.include.stdio - ) -endif() +add_libc_test( + platform_file_test + SUITE + libc-support-tests + SRCS + platform_file_test.cpp + DEPENDS + libc.src.__support.File.file + libc.src.__support.File.platform_file + libc.include.stdio +) + +set(platform_stream_targets "platform_stdout;platform_stdin;platform_stderr") +foreach(target IN LISTS platform_file_targets) + if(TARGET libc.src.__support.File.${target}) + add_libc_test( + ${target}_test + HERMETIC_TEST_ONLY + SUITE + libc-support-tests + SRCS + ${target}_test.cpp + DEPENDS + libc.src.__support.File.file + libc.src.__support.File.${target} + libc.include.stdio + ) + endif() +endforeach() add_subdirectory(testdata) diff --git a/libc/test/src/__support/File/platform_file_test.cpp b/libc/test/src/__support/File/platform_file_test.cpp --- a/libc/test/src/__support/File/platform_file_test.cpp +++ b/libc/test/src/__support/File/platform_file_test.cpp @@ -201,8 +201,3 @@ ASSERT_TRUE(file->error()); ASSERT_EQ(file->close(), 0); } - -TEST(LlvmLibcPlatformFileTest, StdOutStdErrSmokeTest) { - EXPECT_FALSE(__llvm_libc::stdout == nullptr); - EXPECT_FALSE(__llvm_libc::stderr == nullptr); -} diff --git a/libc/test/src/__support/File/platform_stderr_test.cpp b/libc/test/src/__support/File/platform_stderr_test.cpp new file mode 100644 --- /dev/null +++ b/libc/test/src/__support/File/platform_stderr_test.cpp @@ -0,0 +1,12 @@ +// 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 "src/__support/File/file.h" +#include "test/UnitTest/Test.h" + +TEST(LlvmLibcPlatformStreamTest, StdErrSmokeTest) { + EXPECT_FALSE(__llvm_libc::stderr == nullptr); +} diff --git a/libc/test/src/__support/File/platform_stdin_test.cpp b/libc/test/src/__support/File/platform_stdin_test.cpp new file mode 100644 --- /dev/null +++ b/libc/test/src/__support/File/platform_stdin_test.cpp @@ -0,0 +1,12 @@ +// 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 "src/__support/File/file.h" +#include "test/UnitTest/Test.h" + +TEST(LlvmLibcPlatformStreamTest, StdInSmokeTest) { + EXPECT_FALSE(__llvm_libc::stdin == nullptr); +} diff --git a/libc/test/src/__support/File/platform_stdout_test.cpp b/libc/test/src/__support/File/platform_stdout_test.cpp new file mode 100644 --- /dev/null +++ b/libc/test/src/__support/File/platform_stdout_test.cpp @@ -0,0 +1,12 @@ +// 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 "src/__support/File/file.h" +#include "test/UnitTest/Test.h" + +TEST(LlvmLibcPlatformStreamTest, StdOutSmokeTest) { + EXPECT_FALSE(__llvm_libc::stdout == nullptr); +} diff --git a/libc/test/src/stdio/CMakeLists.txt b/libc/test/src/stdio/CMakeLists.txt --- a/libc/test/src/stdio/CMakeLists.txt +++ b/libc/test/src/stdio/CMakeLists.txt @@ -136,13 +136,16 @@ ) if(LLVM_LIBC_FULL_BUILD) -# In fullbuild mode, fprintf's tests use the internal FILE for other functions. - list(APPEND fprintf_test_deps - libc.src.stdio.fclose - libc.src.stdio.ferror - libc.src.stdio.fopen - libc.src.stdio.fread + # In fullbuild mode, fprintf's tests use the internal FILE for other functions. + list(APPEND fprintf_test_deps + libc.src.stdio.fclose + libc.src.stdio.ferror + libc.src.stdio.fopen + libc.src.stdio.fread ) + # This is to be used for tests which write to libc's platform streams + # under full build but write to system-lib's streams otherwise. + set(hermetic_test_only HERMETIC_TEST_ONLY) else() # Else in overlay mode they use the system's FILE. set(fprintf_test_copts "-DLIBC_COPT_PRINTF_USE_SYSTEM_FILE") @@ -161,8 +164,9 @@ ${fprintf_test_copts} ) -add_libc_unittest( +add_libc_test( printf_test + ${hermetic_test_only} SUITE libc_stdio_unittests SRCS @@ -205,8 +209,9 @@ ${fprintf_test_copts} ) -add_libc_unittest( +add_libc_test( vprintf_test + ${hermetic_test_only} SUITE libc_stdio_unittests SRCS @@ -244,6 +249,7 @@ add_libc_test( puts_test + HERMETIC_TEST_ONLY # writes to libc's stdout SUITE libc_stdio_unittests SRCS @@ -254,6 +260,7 @@ add_libc_test( fputs_test + HERMETIC_TEST_ONLY # writes to libc's stdout and stderr SUITE libc_stdio_unittests SRCS diff --git a/libc/test/src/unistd/CMakeLists.txt b/libc/test/src/unistd/CMakeLists.txt --- a/libc/test/src/unistd/CMakeLists.txt +++ b/libc/test/src/unistd/CMakeLists.txt @@ -430,8 +430,9 @@ libc.src.unistd.sysconf ) -add_libc_unittest( +add_libc_test( getopt_test + HERMETIC_TEST_ONLY # Uses libc's own stderr SUITE libc_unistd_unittests SRCS diff --git a/libc/test/src/unistd/getopt_test.cpp b/libc/test/src/unistd/getopt_test.cpp --- a/libc/test/src/unistd/getopt_test.cpp +++ b/libc/test/src/unistd/getopt_test.cpp @@ -34,27 +34,25 @@ &test_globals::optpos, &test_globals::opterr, errstream); } +static void my_memcpy(char *dest, const char *src, size_t size) { + for (size_t i = 0; i < size; i++) + dest[i] = src[i]; +} + +ssize_t cookie_write(void *cookie, const char *buf, size_t size) { + char **pos = static_cast(cookie); + my_memcpy(*pos, buf, size); + *pos += size; + return size; +} + +static cookie_io_functions_t cookie{nullptr, &cookie_write, nullptr, nullptr}; + // TODO: could be either llvm-libc's or the system libc's. The former // doesn't currently support fmemopen but does have fopencookie. In the future // just use that instead. This memopen does no error checking for the size // of the buffer, etc. -FILE *memopen(char **pos) { - static auto memcpy = [](char *dest, const char *src, size_t size) { - for (size_t i = 0; i < size; i++) - dest[i] = src[i]; - }; - - static auto *write = - +[](void *cookie, const char *buf, size_t size) -> ssize_t { - char **pos = static_cast(cookie); - memcpy(*pos, buf, size); - *pos += size; - return size; - }; - - static cookie_io_functions_t cookie{nullptr, write, nullptr, nullptr}; - return __llvm_libc::fopencookie(pos, "w", cookie); -} +FILE *memopen(char **pos) { return __llvm_libc::fopencookie(pos, "w", cookie); } struct LlvmLibcGetoptTest : public __llvm_libc::testing::Test { FILE *errstream;