diff --git a/.github/workflows/libcxx-build-and-test.yaml b/.github/workflows/libcxx-build-and-test.yaml index d707902d05dc..aad977ce4ea9 100644 --- a/.github/workflows/libcxx-build-and-test.yaml +++ b/.github/workflows/libcxx-build-and-test.yaml @@ -138,7 +138,6 @@ jobs: 'generic-no-experimental', 'generic-no-filesystem', 'generic-no-localization', - 'generic-no-terminal', 'generic-no-random_device', 'generic-no-threads', 'generic-no-tzdb', diff --git a/libcxx/CMakeLists.txt b/libcxx/CMakeLists.txt index 0ac582cb561a..0ec6d196e977 100644 --- a/libcxx/CMakeLists.txt +++ b/libcxx/CMakeLists.txt @@ -106,8 +106,6 @@ option(LIBCXX_ENABLE_UNICODE "Whether to include support for Unicode in the library. Disabling Unicode can be useful when porting to platforms that don't support UTF-8 encoding (e.g. embedded)." ON) -option(LIBCXX_HAS_TERMINAL_AVAILABLE - "Build libc++ with support for checking whether a stream is a terminal." ON) option(LIBCXX_ENABLE_WIDE_CHARACTERS "Whether to include support for wide characters in the library. Disabling wide character support can be useful when porting to platforms that don't @@ -750,7 +748,6 @@ config_define(${LIBCXX_ABI_FORCE_ITANIUM} _LIBCPP_ABI_FORCE_ITANIUM) config_define(${LIBCXX_ABI_FORCE_MICROSOFT} _LIBCPP_ABI_FORCE_MICROSOFT) config_define(${LIBCXX_ENABLE_THREADS} _LIBCPP_HAS_THREADS) config_define(${LIBCXX_ENABLE_MONOTONIC_CLOCK} _LIBCPP_HAS_MONOTONIC_CLOCK) -config_define(${LIBCXX_HAS_TERMINAL_AVAILABLE} _LIBCPP_HAS_TERMINAL) if (NOT LIBCXX_TYPEINFO_COMPARISON_IMPLEMENTATION STREQUAL "default") config_define("${LIBCXX_TYPEINFO_COMPARISON_IMPLEMENTATION}" _LIBCPP_TYPEINFO_COMPARISON_IMPLEMENTATION) endif() diff --git a/libcxx/cmake/caches/Generic-no-terminal.cmake b/libcxx/cmake/caches/Generic-no-terminal.cmake deleted file mode 100644 index 9f712ebce02d..000000000000 --- a/libcxx/cmake/caches/Generic-no-terminal.cmake +++ /dev/null @@ -1,5 +0,0 @@ -set(LIBCXX_HAS_TERMINAL_AVAILABLE OFF CACHE BOOL "") - -# Speed up the CI -set(LIBCXX_TEST_PARAMS "enable_modules=clang" CACHE STRING "") -set(LIBCXXABI_TEST_PARAMS "${LIBCXX_TEST_PARAMS}" CACHE STRING "") diff --git a/libcxx/include/__config_site.in b/libcxx/include/__config_site.in index a1cc1208aeff..eec6cb720ba5 100644 --- a/libcxx/include/__config_site.in +++ b/libcxx/include/__config_site.in @@ -15,7 +15,6 @@ #cmakedefine01 _LIBCPP_ABI_FORCE_MICROSOFT #cmakedefine01 _LIBCPP_HAS_THREADS #cmakedefine01 _LIBCPP_HAS_MONOTONIC_CLOCK -#cmakedefine01 _LIBCPP_HAS_TERMINAL #cmakedefine01 _LIBCPP_HAS_MUSL_LIBC #cmakedefine01 _LIBCPP_HAS_THREAD_API_PTHREAD #cmakedefine01 _LIBCPP_HAS_THREAD_API_EXTERNAL diff --git a/libcxx/include/__configuration/availability.h b/libcxx/include/__configuration/availability.h index 32e5565b50f8..f1a598b5206f 100644 --- a/libcxx/include/__configuration/availability.h +++ b/libcxx/include/__configuration/availability.h @@ -255,12 +255,6 @@ #define _LIBCPP_AVAILABILITY_HAS_INIT_PRIMARY_EXCEPTION _LIBCPP_INTRODUCED_IN_LLVM_18 #define _LIBCPP_AVAILABILITY_INIT_PRIMARY_EXCEPTION _LIBCPP_INTRODUCED_IN_LLVM_18_ATTRIBUTE -// This controls the availability of C++23 , which -// has a dependency on the built library (it needs access to -// the underlying buffer types of std::cout, std::cerr, and std::clog. -#define _LIBCPP_AVAILABILITY_HAS_PRINT _LIBCPP_INTRODUCED_IN_LLVM_18 -#define _LIBCPP_AVAILABILITY_PRINT _LIBCPP_INTRODUCED_IN_LLVM_18_ATTRIBUTE - // This controls the availability of the C++17 std::pmr library, // which is implemented in large part in the built library. // diff --git a/libcxx/include/__ostream/print.h b/libcxx/include/__ostream/print.h index c5906c41b95b..6b6c9033d1e6 100644 --- a/libcxx/include/__ostream/print.h +++ b/libcxx/include/__ostream/print.h @@ -87,7 +87,7 @@ _LIBCPP_EXPORTED_FROM_ABI FILE* __get_ostream_file(ostream& __os); # if _LIBCPP_HAS_UNICODE template // TODO PRINT template or availability markup fires too eagerly (http://llvm.org/PR61563). _LIBCPP_HIDE_FROM_ABI void __vprint_unicode(ostream& __os, string_view __fmt, format_args __args, bool __write_nl) { -# if _LIBCPP_AVAILABILITY_HAS_PRINT == 0 +# ifndef _LIBCPP_WIN32API return std::__vprint_nonunicode(__os, __fmt, __args, __write_nl); # else FILE* __file = std::__get_ostream_file(__os); @@ -110,10 +110,8 @@ _LIBCPP_HIDE_FROM_ABI void __vprint_unicode(ostream& __os, string_view __fmt, fo # endif // _LIBCPP_HAS_EXCEPTIONS ostream::sentry __s(__os); if (__s) { -# ifndef _LIBCPP_WIN32API - __print::__vprint_unicode_posix(__file, __fmt, __args, __write_nl, true); -# elif _LIBCPP_HAS_WIDE_CHARACTERS - __print::__vprint_unicode_windows(__file, __fmt, __args, __write_nl, true); +# if _LIBCPP_HAS_WIDE_CHARACTERS + __print::__vprint_unicode_windows(__file, __fmt, __args, __write_nl); # else # error "Windows builds with wchar_t disabled are not supported." # endif @@ -124,7 +122,7 @@ _LIBCPP_HIDE_FROM_ABI void __vprint_unicode(ostream& __os, string_view __fmt, fo __os.__set_badbit_and_consider_rethrow(); } # endif // _LIBCPP_HAS_EXCEPTIONS -# endif // _LIBCPP_AVAILABILITY_HAS_PRINT +# endif // _LIBCPP_WIN32API } template // TODO PRINT template or availability markup fires too eagerly (http://llvm.org/PR61563). diff --git a/libcxx/include/print b/libcxx/include/print index 0ff314c22dcd..5ea2f47ef197 100644 --- a/libcxx/include/print +++ b/libcxx/include/print @@ -69,9 +69,7 @@ _LIBCPP_EXPORTED_FROM_ABI bool __is_windows_terminal(FILE* __stream); // Note the function is only implemented on the Windows platform. _LIBCPP_EXPORTED_FROM_ABI void __write_to_windows_console(FILE* __stream, wstring_view __view); # endif // _LIBCPP_HAS_WIDE_CHARACTERS -# elif __has_include() -_LIBCPP_EXPORTED_FROM_ABI bool __is_posix_terminal(FILE* __stream); -# endif // _LIBCPP_WIN32API +# endif // _LIBCPP_WIN32API # if _LIBCPP_STD_VER >= 23 @@ -197,21 +195,17 @@ inline constexpr bool __use_unicode_execution_charset = _MSVC_EXECUTION_CHARACTE inline constexpr bool __use_unicode_execution_charset = true; # endif +# ifdef _LIBCPP_WIN32API _LIBCPP_HIDE_FROM_ABI inline bool __is_terminal([[maybe_unused]] FILE* __stream) { // The macro _LIBCPP_TESTING_PRINT_IS_TERMINAL is used to change // the behavior in the test. This is not part of the public API. -# ifdef _LIBCPP_TESTING_PRINT_IS_TERMINAL +# ifdef _LIBCPP_TESTING_PRINT_IS_TERMINAL return _LIBCPP_TESTING_PRINT_IS_TERMINAL(__stream); -# elif _LIBCPP_AVAILABILITY_HAS_PRINT == 0 || !_LIBCPP_HAS_TERMINAL - return false; -# elif defined(_LIBCPP_WIN32API) +# else return std::__is_windows_terminal(__stream); -# elif __has_include() - return std::__is_posix_terminal(__stream); -# else -# error "Provide a way to determine whether a FILE* is a terminal" -# endif +# endif } +# endif // _LIBCPP_WIN32API template // TODO PRINT template or availability markup fires too eagerly (http://llvm.org/PR61563). _LIBCPP_HIDE_FROM_ABI inline void @@ -236,26 +230,11 @@ __vprint_nonunicode(FILE* __stream, string_view __fmt, format_args __args, bool // terminal when the output is redirected. Typically during testing the // output is redirected to be able to capture it. This makes it hard to // test this code path. -template // TODO PRINT template or availability markup fires too eagerly (http://llvm.org/PR61563). -_LIBCPP_HIDE_FROM_ABI inline void -__vprint_unicode_posix(FILE* __stream, string_view __fmt, format_args __args, bool __write_nl, bool __is_terminal) { - // TODO PRINT Should flush errors throw too? - if (__is_terminal) - std::fflush(__stream); - - __print::__vprint_nonunicode(__stream, __fmt, __args, __write_nl); -} # if _LIBCPP_HAS_WIDE_CHARACTERS template // TODO PRINT template or availability markup fires too eagerly (http://llvm.org/PR61563). _LIBCPP_HIDE_FROM_ABI inline void -__vprint_unicode_windows(FILE* __stream, string_view __fmt, format_args __args, bool __write_nl, bool __is_terminal) { - if (!__is_terminal) - return __print::__vprint_nonunicode(__stream, __fmt, __args, __write_nl); - - // TODO PRINT Should flush errors throw too? - std::fflush(__stream); - +__vprint_unicode_windows([[maybe_unused]] FILE* __stream, string_view __fmt, format_args __args, bool __write_nl) { string __str = std::vformat(__fmt, __args); // UTF-16 uses the same number or less code units than UTF-8. // However the size of the code unit is 16 bits instead of 8 bits. @@ -316,9 +295,15 @@ __vprint_unicode([[maybe_unused]] FILE* __stream, // Windows there is a different API. This API requires transcoding. # ifndef _LIBCPP_WIN32API - __print::__vprint_unicode_posix(__stream, __fmt, __args, __write_nl, __print::__is_terminal(__stream)); + __print::__vprint_nonunicode(__stream, __fmt, __args, __write_nl); # elif _LIBCPP_HAS_WIDE_CHARACTERS - __print::__vprint_unicode_windows(__stream, __fmt, __args, __write_nl, __print::__is_terminal(__stream)); + if (__print::__is_terminal(__stream)) { + // TODO PRINT Should flush errors throw too? + std::fflush(__stream); + __print::__vprint_unicode_windows(__stream, __fmt, __args, __write_nl); + } else { + __print::__vprint_nonunicode(__stream, __fmt, __args, __write_nl); + } # else # error "Windows builds with wchar_t disabled are not supported." # endif diff --git a/libcxx/src/print.cpp b/libcxx/src/print.cpp index 82cf2afd052e..a5edcc463219 100644 --- a/libcxx/src/print.cpp +++ b/libcxx/src/print.cpp @@ -64,9 +64,12 @@ __write_to_windows_console([[maybe_unused]] FILE* __stream, [[maybe_unused]] wst } # endif // _LIBCPP_HAS_WIDE_CHARACTERS -#elif defined(HAS_FILENO_AND_ISATTY) // !_LIBCPP_WIN32API +#elif defined(HAS_FILENO_AND_ISATTY) && _LIBCPP_AVAILABILITY_MINIMUM_HEADER_VERSION < 23 // !_LIBCPP_WIN32API +_LIBCPP_DIAGNOSTIC_PUSH +_LIBCPP_CLANG_DIAGNOSTIC_IGNORED("-Wmissing-prototypes") _LIBCPP_EXPORTED_FROM_ABI bool __is_posix_terminal(FILE* __stream) { return isatty(fileno(__stream)); } +_LIBCPP_DIAGNOSTIC_POP #endif _LIBCPP_END_NAMESPACE_STD diff --git a/libcxx/test/libcxx/input.output/iostream.format/output.streams/ostream.formatted/ostream.formatted.print/vprint_unicode.pass.cpp b/libcxx/test/libcxx/input.output/iostream.format/output.streams/ostream.formatted/ostream.formatted.print/vprint_unicode.pass.cpp index 52d8500f7fa3..27387311b9f1 100644 --- a/libcxx/test/libcxx/input.output/iostream.format/output.streams/ostream.formatted/ostream.formatted.print/vprint_unicode.pass.cpp +++ b/libcxx/test/libcxx/input.output/iostream.format/output.streams/ostream.formatted/ostream.formatted.print/vprint_unicode.pass.cpp @@ -13,13 +13,8 @@ // XFAIL: availability-fp_to_chars-missing -// When std::print is unavailable, we don't rely on an implementation of -// std::__is_terminal and we always assume a non-unicode and non-terminal -// output. -// XFAIL: availability-print-missing - // Clang modules do not work with the definiton of _LIBCPP_TESTING_PRINT_IS_TERMINAL -// XFAIL: clang-modules-build +// ADDITIONAL_COMPILE_FLAGS: -fno-modules // // Tests the implementation of @@ -81,14 +76,22 @@ static void test_is_terminal_file_stream() { assert(stream.is_open()); assert(stream.good()); std::print(stream, "test"); +#ifdef _WIN32 assert(is_terminal_calls == 1); +#else + assert(is_terminal_calls == 0); +#endif } { std::ofstream stream(filename); assert(stream.is_open()); assert(stream.good()); std::print(stream, "test"); +#ifdef _WIN32 assert(is_terminal_calls == 2); +#else + assert(is_terminal_calls == 0); +#endif } } @@ -105,7 +108,11 @@ static void test_is_terminal_rdbuf_derived_from_filebuf() { std::ostream stream(&buf); std::print(stream, "test"); +#ifdef _WIN32 assert(is_terminal_calls == 1); +#else + assert(is_terminal_calls == 0); +#endif } // When the stream is cout, clog, or cerr, its FILE* may be a terminal. Validate @@ -115,15 +122,27 @@ static void test_is_terminal_std_cout_cerr_clog() { is_terminal_result = false; { std::print(std::cout, "test"); +#ifdef _WIN32 assert(is_terminal_calls == 1); +#else + assert(is_terminal_calls == 0); +#endif } { std::print(std::cerr, "test"); +#ifdef _WIN32 assert(is_terminal_calls == 2); +#else + assert(is_terminal_calls == 0); +#endif } { std::print(std::clog, "test"); +#ifdef _WIN32 assert(is_terminal_calls == 3); +#else + assert(is_terminal_calls == 0); +#endif } } @@ -156,7 +175,11 @@ static void test_is_terminal_is_flushed() { // A terminal sync is called. is_terminal_result = true; std::print(stream, ""); +#ifdef _WIN32 assert(buf.sync_calls == 1); // only called from the destructor of the sentry +#else + assert(buf.sync_calls == 0); +#endif } int main(int, char**) { diff --git a/libcxx/test/libcxx/input.output/iostream.format/print.fun/vprint_unicode_posix.pass.cpp b/libcxx/test/libcxx/input.output/iostream.format/print.fun/vprint_unicode_posix.pass.cpp deleted file mode 100644 index b89d02ba9942..000000000000 --- a/libcxx/test/libcxx/input.output/iostream.format/print.fun/vprint_unicode_posix.pass.cpp +++ /dev/null @@ -1,79 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// 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 -// -//===----------------------------------------------------------------------===// - -// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20 -// UNSUPPORTED: no-filesystem -// UNSUPPORTED: libcpp-has-no-unicode -// UNSUPPORTED: GCC-ALWAYS_INLINE-FIXME - -// XFAIL: availability-fp_to_chars-missing - -// fmemopen is available starting in Android M (API 23) -// XFAIL: target={{.+}}-android{{(eabi)?(21|22)}} - -// REQUIRES: has-unix-headers - -// - -// Tests the implementation of -// void __print::__vprint_unicode_posix(FILE* __stream, string_view __fmt, -// format_args __args, bool __write_nl, -// bool __is_terminal); -// -// In the library when the stdout is redirected to a file it is no -// longer considered a terminal and the special terminal handling is no -// longer executed. By testing this function we can "force" emulate a -// terminal. -// Note __write_nl is tested by the public API. - -#include -#include -#include -#include -#include - -#include "test_macros.h" - -int main(int, char**) { - std::array buffer; - std::ranges::fill(buffer, '*'); - - FILE* file = fmemopen(buffer.data(), buffer.size(), "wb"); - assert(file); - - // Test the file is buffered. - std::fprintf(file, "Hello"); - assert(std::ftell(file) == 5); -#if defined(TEST_HAS_GLIBC) && \ - !(__has_feature(address_sanitizer) || __has_feature(thread_sanitizer) || __has_feature(memory_sanitizer)) - assert(std::ranges::all_of(buffer, [](char c) { return c == '*'; })); -#endif - - // Test writing to a "non-terminal" stream does not flush. - std::__print::__vprint_unicode_posix(file, " world", std::make_format_args(), false, false); - assert(std::ftell(file) == 11); -#if defined(TEST_HAS_GLIBC) && \ - !(__has_feature(address_sanitizer) || __has_feature(thread_sanitizer) || __has_feature(memory_sanitizer)) - assert(std::ranges::all_of(buffer, [](char c) { return c == '*'; })); -#endif - - // Test writing to a "terminal" stream flushes before writing. - std::__print::__vprint_unicode_posix(file, "!", std::make_format_args(), false, true); - assert(std::ftell(file) == 12); - assert(std::string_view(buffer.data(), buffer.data() + 11) == "Hello world"); -#if defined(TEST_HAS_GLIBC) - // glibc does not flush after a write. - assert(buffer[11] != '!'); -#endif - - // Test everything is written when closing the stream. - std::fclose(file); - assert(std::string_view(buffer.data(), buffer.data() + 12) == "Hello world!"); - - return 0; -} diff --git a/libcxx/test/libcxx/input.output/iostream.format/print.fun/vprint_unicode_windows.pass.cpp b/libcxx/test/libcxx/input.output/iostream.format/print.fun/vprint_unicode_windows.pass.cpp index bcd1d05a3aee..162579027831 100644 --- a/libcxx/test/libcxx/input.output/iostream.format/print.fun/vprint_unicode_windows.pass.cpp +++ b/libcxx/test/libcxx/input.output/iostream.format/print.fun/vprint_unicode_windows.pass.cpp @@ -13,7 +13,7 @@ // UNSUPPORTED: GCC-ALWAYS_INLINE-FIXME // Clang modules do not work with the definiton of _LIBCPP_TESTING_PRINT_WRITE_TO_WINDOWS_CONSOLE_FUNCTION -// XFAIL: clang-modules-build +// ADDITIONAL_COMPILE_FLAGS: -fno-modules // XFAIL: availability-fp_to_chars-missing @@ -21,8 +21,7 @@ // Tests the implementation of // void __print::__vprint_unicode_windows(FILE* __stream, string_view __fmt, -// format_args __args, bool __write_nl, -// bool __is_terminal); +// format_args __args, bool __write_nl); // // In the library when the stdout is redirected to a file it is no // longer considered a terminal and the special terminal handling is no @@ -62,40 +61,19 @@ static void test_basics() { FILE* file = std::fopen(filename.c_str(), "wb"); assert(file); - // Test writing to a "non-terminal" stream does not call WriteConsoleW. - std::__print::__vprint_unicode_windows(file, "Hello", std::make_format_args(), false, false); - assert(std::ftell(file) == 5); - - // It's not possible to reliably test whether writing to a "terminal" stream - // flushes before writing. Testing flushing a closed stream worked on some - // platforms, but was unreliable. calling = true; - std::__print::__vprint_unicode_windows(file, " world", std::make_format_args(), false, true); + std::__print::__vprint_unicode_windows(file, " world", std::make_format_args(), false); } -// When the output is a file the data is written as-is. -// When the output is a "terminal" invalid UTF-8 input is flagged. +// Invalid UTF-8 input is flagged. static void test(std::wstring_view output, std::string_view input) { - // *** File *** FILE* file = std::fopen(filename.c_str(), "wb"); assert(file); - std::__print::__vprint_unicode_windows(file, input, std::make_format_args(), false, false); - assert(std::ftell(file) == static_cast(input.size())); - std::fclose(file); - - file = std::fopen(filename.c_str(), "rb"); - assert(file); - - std::vector buffer(input.size()); - size_t read = fread(buffer.data(), 1, buffer.size(), file); - assert(read == input.size()); - assert(input == std::string_view(buffer.begin(), buffer.end())); - std::fclose(file); - - // *** Terminal *** expected = output; - std::__print::__vprint_unicode_windows(file, input, std::make_format_args(), false, true); + std::__print::__vprint_unicode_windows(file, input, std::make_format_args(), false); + assert(std::ftell(file) == 0); + std::fclose(file); } static void test() { diff --git a/libcxx/utils/ci/run-buildbot b/libcxx/utils/ci/run-buildbot index f607ff82538c..78c9cee27042 100755 --- a/libcxx/utils/ci/run-buildbot +++ b/libcxx/utils/ci/run-buildbot @@ -486,11 +486,6 @@ generic-no-localization) generate-cmake -C "${MONOREPO_ROOT}/libcxx/cmake/caches/Generic-no-localization.cmake" check-runtimes ;; -generic-no-terminal) - clean - generate-cmake -C "${MONOREPO_ROOT}/libcxx/cmake/caches/Generic-no-terminal.cmake" - check-runtimes -;; generic-no-unicode) clean generate-cmake -C "${MONOREPO_ROOT}/libcxx/cmake/caches/Generic-no-unicode.cmake" diff --git a/libcxx/utils/libcxx/test/features/libcxx_macros.py b/libcxx/utils/libcxx/test/features/libcxx_macros.py index 02705bff1c09..aeb4696501a8 100644 --- a/libcxx/utils/libcxx/test/features/libcxx_macros.py +++ b/libcxx/utils/libcxx/test/features/libcxx_macros.py @@ -67,7 +67,6 @@ inverted_macros = { "_LIBCPP_HAS_VENDOR_AVAILABILITY_ANNOTATIONS": "libcpp-has-no-availability-markup", "_LIBCPP_HAS_RANDOM_DEVICE": "no-random-device", "_LIBCPP_HAS_UNICODE": "libcpp-has-no-unicode", - "_LIBCPP_HAS_TERMINAL": "no-terminal", } for macro, feature in inverted_macros.items(): features.append(