[libc++] Introduce a setting to remove fstream from the library

This allows porting the library to platforms that are able to support
<iostream> but that do not have a notion of a filesystem, and where it
hence doesn't make sense to support std::fstream (and never will).

Also, remove reliance on <fstream> in various tests that didn't
actually need it.

Differential Revision: https://reviews.llvm.org/D138327
This commit is contained in:
Louis Dionne 2022-11-18 15:01:33 -05:00
parent 242a9cf7e6
commit af8c49dc1e
30 changed files with 149 additions and 120 deletions

View File

@ -84,6 +84,8 @@ option(LIBCXX_ENABLE_LOCALIZATION
the C locale API (e.g. embedded). When localization is not supported,
several parts of the library will be disabled: <iostream>, <regex>, <locale>
will be completely unusable, and other parts may be only partly available." ON)
option(LIBCXX_ENABLE_FSTREAM
"Whether to include support for <fstream>." ON) # TODO: Consider rolling that into LIBCXX_ENABLE_FILESYSTEM
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.
@ -860,6 +862,7 @@ config_define_if(LIBCXX_ENABLE_PARALLEL_ALGORITHMS _LIBCPP_HAS_PARALLEL_ALGORITH
config_define_if_not(LIBCXX_ENABLE_FILESYSTEM _LIBCPP_HAS_NO_FILESYSTEM_LIBRARY)
config_define_if_not(LIBCXX_ENABLE_RANDOM_DEVICE _LIBCPP_HAS_NO_RANDOM_DEVICE)
config_define_if_not(LIBCXX_ENABLE_LOCALIZATION _LIBCPP_HAS_NO_LOCALIZATION)
config_define_if_not(LIBCXX_ENABLE_FSTREAM _LIBCPP_HAS_NO_FSTREAM)
config_define_if_not(LIBCXX_ENABLE_UNICODE _LIBCPP_HAS_NO_UNICODE)
config_define_if_not(LIBCXX_ENABLE_WIDE_CHARACTERS _LIBCPP_HAS_NO_WIDE_CHARACTERS)
config_define_if_not(LIBCXX_ENABLE_VENDOR_AVAILABILITY_ANNOTATIONS _LIBCPP_HAS_NO_VENDOR_AVAILABILITY_ANNOTATIONS)

View File

@ -0,0 +1 @@
set(LIBCXX_ENABLE_FSTREAM OFF CACHE BOOL "")

View File

@ -838,7 +838,7 @@ set(files
wctype.h
)
foreach(feature LIBCXX_ENABLE_FILESYSTEM LIBCXX_ENABLE_LOCALIZATION LIBCXX_ENABLE_THREADS LIBCXX_ENABLE_WIDE_CHARACTERS)
foreach(feature LIBCXX_ENABLE_FILESYSTEM LIBCXX_ENABLE_LOCALIZATION LIBCXX_ENABLE_FSTREAM LIBCXX_ENABLE_THREADS LIBCXX_ENABLE_WIDE_CHARACTERS)
if (NOT ${${feature}})
set(requires_${feature} "requires LIBCXX_CONFIGURED_WITHOUT_SUPPORT_FOR_THIS_HEADER")
endif()

View File

@ -28,6 +28,7 @@
#cmakedefine _LIBCPP_HAS_PARALLEL_ALGORITHMS
#cmakedefine _LIBCPP_HAS_NO_RANDOM_DEVICE
#cmakedefine _LIBCPP_HAS_NO_LOCALIZATION
#cmakedefine _LIBCPP_HAS_NO_FSTREAM
#cmakedefine _LIBCPP_HAS_NO_WIDE_CHARACTERS
#cmakedefine01 _LIBCPP_ENABLE_ASSERTIONS_DEFAULT
#cmakedefine _LIBCPP_ENABLE_DEBUG_MODE

View File

@ -210,6 +210,8 @@ _LIBCPP_PUSH_MACROS
# define _LIBCPP_HAS_NO_OFF_T_FUNCTIONS
#endif
#if !defined(_LIBCPP_HAS_NO_FSTREAM)
_LIBCPP_BEGIN_NAMESPACE_STD
template <class _CharT, class _Traits>
@ -1742,6 +1744,8 @@ extern template class _LIBCPP_EXTERN_TEMPLATE_TYPE_VIS basic_filebuf<char>;
_LIBCPP_END_NAMESPACE_STD
#endif // _LIBCPP_HAS_NO_FSTREAM
_LIBCPP_POP_MACROS
#if !defined(_LIBCPP_REMOVE_TRANSITIVE_INCLUDES) && _LIBCPP_STD_VER <= 20

View File

@ -855,6 +855,7 @@ module std [system] {
}
module fstream {
@requires_LIBCXX_ENABLE_LOCALIZATION@
@requires_LIBCXX_ENABLE_FSTREAM@
header "fstream"
export *
}

View File

@ -36,9 +36,12 @@ template class _LIBCPP_CLASS_TEMPLATE_INSTANTIATION_VIS basic_stringbuf<char>;
template class _LIBCPP_CLASS_TEMPLATE_INSTANTIATION_VIS basic_stringstream<char>;
template class _LIBCPP_CLASS_TEMPLATE_INSTANTIATION_VIS basic_ostringstream<char>;
template class _LIBCPP_CLASS_TEMPLATE_INSTANTIATION_VIS basic_istringstream<char>;
#ifndef _LIBCPP_HAS_NO_FSTREAM
template class _LIBCPP_CLASS_TEMPLATE_INSTANTIATION_VIS basic_ifstream<char>;
template class _LIBCPP_CLASS_TEMPLATE_INSTANTIATION_VIS basic_ofstream<char>;
template class _LIBCPP_CLASS_TEMPLATE_INSTANTIATION_VIS basic_filebuf<char>;
#endif
// Add more here if needed...

View File

@ -325,7 +325,7 @@ int main(int, char**) { return 0; }
#endif
// RUN: %{build} -DTEST_51
#if defined(TEST_51) && !defined(_LIBCPP_HAS_NO_LOCALIZATION)
#if defined(TEST_51) && !defined(_LIBCPP_HAS_NO_LOCALIZATION) && !defined(_LIBCPP_HAS_NO_FSTREAM)
# include <fstream>
using HandlerType = decltype(std::__libcpp_verbose_abort);
#endif

View File

@ -98,7 +98,7 @@ END-SCRIPT
#include <float.h>
#include <format>
#include <forward_list>
#if !defined(_LIBCPP_HAS_NO_LOCALIZATION)
#if !defined(_LIBCPP_HAS_NO_LOCALIZATION) && !defined(_LIBCPP_HAS_NO_FSTREAM)
# include <fstream>
#endif
#include <functional>

View File

@ -100,7 +100,7 @@ END-SCRIPT
#include <float.h>
#include <format>
#include <forward_list>
#if !defined(_LIBCPP_HAS_NO_LOCALIZATION)
#if !defined(_LIBCPP_HAS_NO_LOCALIZATION) && !defined(_LIBCPP_HAS_NO_FSTREAM)
# include <fstream>
#endif
#include <functional>

View File

@ -151,7 +151,7 @@ TEST_MACROS();
TEST_MACROS();
#include <forward_list>
TEST_MACROS();
#if !defined(_LIBCPP_HAS_NO_LOCALIZATION)
#if !defined(_LIBCPP_HAS_NO_LOCALIZATION) && !defined(_LIBCPP_HAS_NO_FSTREAM)
# include <fstream>
TEST_MACROS();
#endif

View File

@ -251,7 +251,7 @@ END-SCRIPT
#include <forward_list>
#endif
// RUN: %{cxx} %s %{flags} %{compile_flags} -fmodules -fcxx-modules -fmodules-cache-path=%t -fsyntax-only -DTEST_51
#if defined(TEST_51) && !defined(_LIBCPP_HAS_NO_LOCALIZATION)
#if defined(TEST_51) && !defined(_LIBCPP_HAS_NO_LOCALIZATION) && !defined(_LIBCPP_HAS_NO_FSTREAM)
#include <fstream>
#endif
// RUN: %{cxx} %s %{flags} %{compile_flags} -fmodules -fcxx-modules -fmodules-cache-path=%t -fsyntax-only -DTEST_52

View File

@ -219,7 +219,7 @@ END-SCRIPT
#include <float.h>
#include <format>
#include <forward_list>
#if !defined(_LIBCPP_HAS_NO_LOCALIZATION)
#if !defined(_LIBCPP_HAS_NO_LOCALIZATION) && !defined(_LIBCPP_HAS_NO_FSTREAM)
# include <fstream>
#endif
#include <functional>

View File

@ -97,7 +97,7 @@ END-SCRIPT
#include <float.h>
#include <format>
#include <forward_list>
#if !defined(_LIBCPP_HAS_NO_LOCALIZATION)
#if !defined(_LIBCPP_HAS_NO_LOCALIZATION) && !defined(_LIBCPP_HAS_NO_FSTREAM)
# include <fstream>
#endif
#include <functional>

View File

@ -13,8 +13,6 @@
// pos_type seekpos(pos_type sp,
// ios_base::openmode which = ios_base::in | ios_base::out);
// FILE_DEPENDENCIES: underflow.dat
#include <fstream>
#include <cassert>

View File

@ -1,3 +1,6 @@
# All non-trivial uses of iostreams require localization support
if 'no-localization' in config.available_features:
config.unsupported = True
if 'no-fstream' in config.available_features:
config.unsupported = True

View File

@ -17,6 +17,8 @@
// basic_streambuf, but I can't seem to reproduce without going through one
// of its derived classes.
// UNSUPPORTED: no-fstream
#include <cassert>
#include <cstddef>
#include <cstdio>

View File

@ -19,9 +19,10 @@
// XFAIL: no-wide-characters
#include <locale>
#include <cassert>
#include <codecvt>
#include <fstream>
#include <cassert>
#include <sstream>
#include "test_macros.h"
@ -46,8 +47,10 @@ struct test_buf
int main(int, char**)
{
{
std::ofstream bs("overflow.dat");
test_buf f(bs.rdbuf());
std::string s;
{
std::ostringstream out;
test_buf f(out.rdbuf());
assert(f.pbase() == 0);
assert(f.pptr() == 0);
assert(f.epptr() == 0);
@ -55,16 +58,19 @@ int main(int, char**)
assert(f.pbase() != 0);
assert(f.pptr() == f.pbase());
assert(f.epptr() - f.pbase() == 4095);
s = out.str();
}
{
std::ifstream bs("overflow.dat");
test_buf f(bs.rdbuf());
std::istringstream in(s);
test_buf f(in.rdbuf());
assert(f.sgetc() == L'a');
}
std::remove("overflow.dat");
}
{
std::ofstream bs("overflow.dat");
test_buf f(bs.rdbuf());
std::string s;
{
std::ostringstream out;
test_buf f(out.rdbuf());
f.pubsetbuf(0, 0);
assert(f.pbase() == 0);
assert(f.pptr() == 0);
@ -73,13 +79,17 @@ int main(int, char**)
assert(f.pbase() == 0);
assert(f.pptr() == 0);
assert(f.epptr() == 0);
s = out.str();
}
{
std::ifstream bs("overflow.dat");
test_buf f(bs.rdbuf());
std::istringstream in(s);
test_buf f(in.rdbuf());
assert(f.sgetc() == L'a');
}
std::remove("overflow.dat");
}
// TODO: Move this to std::stringstream once https://llvm.org/PR59083 has been resolved
#ifndef TEST_HAS_NO_FSTREAM
{
{
std::ofstream bs("overflow.dat");
test_buf f(bs.rdbuf());
@ -102,6 +112,8 @@ int main(int, char**)
assert(f.get() == -1);
}
std::remove("overflow.dat");
}
#endif // TEST_HAS_NO_FSTREAM
return 0;
}

View File

@ -6,8 +6,6 @@
//
//===----------------------------------------------------------------------===//
// FILE_DEPENDENCIES: underflow.dat
// <locale>
// ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_DISABLE_DEPRECATION_WARNINGS
@ -21,11 +19,9 @@
// XFAIL: no-wide-characters
#include <locale>
#include <codecvt>
#include <fstream>
#include <cassert>
#include "test_macros.h"
#include <codecvt>
#include <sstream>
struct test_buf
: public std::wbuffer_convert<std::codecvt_utf8<wchar_t> >
@ -47,16 +43,17 @@ struct test_buf
int main(int, char**)
{
std::string const s = "123456789";
{
std::ifstream bs("underflow.dat");
test_buf f(bs.rdbuf());
std::istringstream in(s);
test_buf f(in.rdbuf());
assert(f.sbumpc() == L'1');
assert(f.sgetc() == L'2');
assert(f.pbackfail(L'a') == test_buf::traits_type::eof());
}
{
std::fstream bs("underflow.dat");
test_buf f(bs.rdbuf());
std::istringstream in(s);
test_buf f(in.rdbuf());
assert(f.sbumpc() == L'1');
assert(f.sgetc() == L'2');
assert(f.pbackfail(L'a') == test_buf::traits_type::eof());

View File

@ -21,13 +21,14 @@
// XFAIL: no-wide-characters
// TODO: Avoid using <fstream> in this test.
// XFAIL: no-fstream
#include <locale>
#include <codecvt>
#include <fstream>
#include <cassert>
#include "test_macros.h"
class test_codecvt
: public std::codecvt<wchar_t, char, std::mbstate_t>
{

View File

@ -14,30 +14,28 @@
// XFAIL: no-wide-characters
#include <fstream>
#include <locale>
#include <codecvt>
#include <cassert>
#include <codecvt>
#include <locale>
#include <sstream>
#include "test_macros.h"
int main(int, char**)
int main(int, char**) {
std::string storage;
{
{
std::ofstream bytestream("myfile.txt");
std::ostringstream bytestream;
std::wbuffer_convert<std::codecvt_utf8<wchar_t> > mybuf(bytestream.rdbuf());
std::wostream mystr(&mybuf);
mystr << L"Hello" << std::endl;
storage = bytestream.str();
}
{
std::ifstream bytestream("myfile.txt");
std::istringstream bytestream(storage);
std::wbuffer_convert<std::codecvt_utf8<wchar_t> > mybuf(bytestream.rdbuf());
std::wistream mystr(&mybuf);
std::wstring ws;
mystr >> ws;
assert(ws == L"Hello");
}
std::remove("myfile.txt");
return 0;
}

View File

@ -6,8 +6,6 @@
//
//===----------------------------------------------------------------------===//
// FILE_DEPENDENCIES: underflow.dat, underflow_utf8.dat
// <locale>
// ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_DISABLE_DEPRECATION_WARNINGS
@ -21,9 +19,9 @@
// XFAIL: no-wide-characters
#include <locale>
#include <codecvt>
#include <fstream>
#include <cassert>
#include <codecvt>
#include <sstream>
#include "test_macros.h"
@ -48,7 +46,8 @@ struct test_buf
int main(int, char**)
{
{
std::ifstream bs("underflow.dat");
std::string s = "123456789";
std::istringstream bs(s);
test_buf f(bs.rdbuf());
assert(f.eback() == 0);
assert(f.gptr() == 0);
@ -60,7 +59,8 @@ int main(int, char**)
assert(f.egptr() - f.eback() == 9);
}
{
std::ifstream bs("underflow.dat");
std::string s = "123456789";
std::istringstream bs(s);
test_buf f(bs.rdbuf());
assert(f.eback() == 0);
assert(f.gptr() == 0);
@ -81,7 +81,8 @@ int main(int, char**)
assert(f.egptr() - f.gptr() == 1);
}
{
std::ifstream bs("underflow_utf8.dat");
std::string s = "乑乒乓";
std::istringstream bs(s);
test_buf f(bs.rdbuf());
assert(f.sbumpc() == 0x4E51);
assert(f.sbumpc() == 0x4E52);

View File

@ -9,7 +9,6 @@
#include <charconv>
#include <chrono>
#include <cmath>
#include <fstream>
#include <functional>
#include <iterator>
#include <limits>
@ -49,7 +48,7 @@
using namespace std;
void initialize_randomness(mt19937_64& mt64, const int argc, char** const argv) {
void initialize_randomness(mt19937_64& mt64, const int argc, char** const /*argv*/) {
constexpr size_t n = mt19937_64::state_size;
constexpr size_t w = mt19937_64::word_size;
static_assert(w % 32 == 0);
@ -59,32 +58,11 @@ void initialize_randomness(mt19937_64& mt64, const int argc, char** const argv)
puts("USAGE:");
puts("test.exe : generates seed data from random_device.");
puts("test.exe filename.txt : loads seed data from a given text file.");
if (argc == 1) {
random_device rd;
generate(vec.begin(), vec.end(), ref(rd));
puts("Generated seed data.");
} else if (argc == 2) {
const char* const filename = argv[1];
ifstream file(filename);
if (!file) {
printf("ERROR: Can't open %s.\n", filename);
abort();
}
for (auto& elem : vec) {
file >> elem;
if (!file) {
printf("ERROR: Can't read seed data from %s.\n", filename);
abort();
}
}
printf("Loaded seed data from %s.\n", filename);
} else {
puts("ERROR: Too many command-line arguments.");
abort();

View File

@ -387,6 +387,10 @@ inline void DoNotOptimize(Tp const& value) {
# define TEST_HAS_NO_FILESYSTEM_LIBRARY
#endif
#if defined(_LIBCPP_HAS_NO_FSTREAM)
# define TEST_HAS_NO_FSTREAM
#endif
#if defined(_LIBCPP_HAS_NO_FGETPOS_FSETPOS)
# define TEST_HAS_NO_FGETPOS_FSETPOS
#endif

View File

@ -520,6 +520,23 @@ steps:
limit: 2
timeout_in_minutes: 120
- label: "No fstream"
command: "libcxx/utils/ci/run-buildbot generic-no-fstream"
artifact_paths:
- "**/test-results.xml"
- "**/*.abilist"
env:
CC: "clang-${LLVM_HEAD_VERSION}"
CXX: "clang++-${LLVM_HEAD_VERSION}"
agents:
queue: "libcxx-builders"
os: "linux"
retry:
automatic:
- exit_status: -1 # Agent was lost
limit: 2
timeout_in_minutes: 120
- label: "No locale"
command: "libcxx/utils/ci/run-buildbot generic-no-localization"
artifact_paths:

View File

@ -196,6 +196,7 @@ check-generated-output)
--exclude 'locale-specific_form.pass.cpp' \
--exclude 'ostream.pass.cpp' \
--exclude 'std_format_spec_string_unicode.bench.cpp' \
--exclude 'underflow.pass.cpp' \
|| false
# Reject code with trailing whitespace
@ -334,6 +335,11 @@ generic-no-random_device)
generate-cmake -C "${MONOREPO_ROOT}/libcxx/cmake/caches/Generic-no-random_device.cmake"
check-runtimes
;;
generic-no-fstream)
clean
generate-cmake -C "${MONOREPO_ROOT}/libcxx/cmake/caches/Generic-no-fstream.cmake"
check-runtimes
;;
generic-no-localization)
clean
generate-cmake -C "${MONOREPO_ROOT}/libcxx/cmake/caches/Generic-no-localization.cmake"

View File

@ -21,7 +21,7 @@ header_restrictions = {
"clocale": "!defined(_LIBCPP_HAS_NO_LOCALIZATION)",
"codecvt": "!defined(_LIBCPP_HAS_NO_LOCALIZATION)",
"fstream": "!defined(_LIBCPP_HAS_NO_LOCALIZATION)",
"fstream": "!defined(_LIBCPP_HAS_NO_LOCALIZATION) && !defined(_LIBCPP_HAS_NO_FSTREAM)",
"iomanip": "!defined(_LIBCPP_HAS_NO_LOCALIZATION)",
"ios": "!defined(_LIBCPP_HAS_NO_LOCALIZATION)",
"iostream": "!defined(_LIBCPP_HAS_NO_LOCALIZATION)",

View File

@ -206,6 +206,7 @@ macros = {
'_LIBCPP_HAS_NO_FILESYSTEM_LIBRARY': 'no-filesystem',
'_LIBCPP_HAS_NO_RANDOM_DEVICE': 'no-random-device',
'_LIBCPP_HAS_NO_LOCALIZATION': 'no-localization',
'_LIBCPP_HAS_NO_FSTREAM': 'no-fstream',
'_LIBCPP_HAS_NO_WIDE_CHARACTERS': 'no-wide-characters',
'_LIBCPP_HAS_NO_UNICODE': 'libcpp-has-no-unicode',
'_LIBCPP_ENABLE_DEBUG_MODE': 'libcpp-has-debug-mode',