Index: libcxx/CMakeLists.txt =================================================================== --- libcxx/CMakeLists.txt +++ libcxx/CMakeLists.txt @@ -88,7 +88,13 @@ option(LIBCXX_ENABLE_ASSERTIONS "Enable assertions independent of build mode." OFF) option(LIBCXX_ENABLE_SHARED "Build libc++ as a shared library." ON) option(LIBCXX_ENABLE_STATIC "Build libc++ as a static library." ON) -option(LIBCXX_ENABLE_EXPERIMENTAL_LIBRARY "Build libc++experimental.a" ON) +set(ENABLE_EXPERIMENTAL_DEFAULT ON) +if (WIN32 AND LIBCXX_ENABLE_SHARED) + # When libc++ is built as a DLL, all headers indicate DLL linkage, while + # libc++experimental always is linked statically. + set(ENABLE_EXPERIMENTAL_DEFAULT OFF) +endif() +option(LIBCXX_ENABLE_EXPERIMENTAL_LIBRARY "Build libc++experimental.a" ${ENABLE_EXPERIMENTAL_DEFAULT}) set(ENABLE_FILESYSTEM_DEFAULT ON) if (WIN32 AND NOT MINGW) # Filesystem is buildable for windows, but it requires __int128 helper @@ -399,6 +405,14 @@ message(FATAL_ERROR "Only one of LIBCXX_ABI_FORCE_ITANIUM and LIBCXX_ABI_FORCE_MICROSOFT can be specified.") endif () +if (WIN32 AND LIBCXX_ENABLE_SHARED AND LIBCXX_ENABLE_EXPERIMENTAL_LIBRARY) + message(FATAL_ERROR "LIBCXX_ENABLE_EXPERIMENTAL_LIBRARY can't be enabled" + " when LIBCXX_ENABLE_SHARED also is enabled for Windows." + " The c++experimental library is only built as a" + " static library, while the headers signal dllimport" + " linkage.") +endif() + #=============================================================================== # Configure System #=============================================================================== Index: libcxx/docs/BuildingLibcxx.rst =================================================================== --- libcxx/docs/BuildingLibcxx.rst +++ libcxx/docs/BuildingLibcxx.rst @@ -90,7 +90,6 @@ -T "ClangCL" ^ -DLIBCXX_ENABLE_SHARED=YES ^ -DLIBCXX_ENABLE_STATIC=NO ^ - -DLIBCXX_ENABLE_EXPERIMENTAL_LIBRARY=NO ^ \path\to\libcxx > cmake --build . @@ -120,7 +119,6 @@ -DCMAKE_BUILD_TYPE=Release ^ -DCMAKE_C_COMPILER=clang-cl ^ -DCMAKE_CXX_COMPILER=clang-cl ^ - -DLIBCXX_ENABLE_EXPERIMENTAL_LIBRARY=NO ^ path/to/libcxx > ninja cxx > ninja check-cxx Index: libcxx/test/libcxx/experimental/lit.local.cfg =================================================================== --- libcxx/test/libcxx/experimental/lit.local.cfg +++ libcxx/test/libcxx/experimental/lit.local.cfg @@ -1,3 +1,7 @@ # Disable all of the experimental tests if the correct feature is not available. if 'c++experimental' not in config.available_features: config.unsupported = True +# If built as a DLL on Windows, headers indicate DLL linkage, which breaks +# linking against the static-only libc++experimental. +if config.enable_shared and 'windows' in config.available_features: + config.unsupported = True Index: libcxx/test/std/experimental/lit.local.cfg =================================================================== --- libcxx/test/std/experimental/lit.local.cfg +++ libcxx/test/std/experimental/lit.local.cfg @@ -1,3 +1,7 @@ # Disable all of the experimental tests if the correct feature is not available. if 'c++experimental' not in config.available_features: config.unsupported = True +# If built as a DLL on Windows, headers indicate DLL linkage, which breaks +# linking against the static-only libc++experimental. +if config.enable_shared and 'windows' in config.available_features: + config.unsupported = True Index: libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.cons/path.pass.cpp =================================================================== --- libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.cons/path.pass.cpp +++ libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.cons/path.pass.cpp @@ -8,8 +8,6 @@ // UNSUPPORTED: c++03 -// XFAIL: LIBCXX-WINDOWS-FIXME - // // class directory_entry @@ -155,7 +153,8 @@ // reading directories; test using a special inaccessible directory // instead. const path dir = GetWindowsInaccessibleDir(); - TEST_REQUIRE(!dir.empty()); + if (dir.empty()) + TEST_UNSUPPORTED(); const path file = dir / "file"; { std::error_code ec = GetTestEC(); Index: libcxx/test/std/input.output/filesystems/class.directory_iterator/directory_iterator.members/ctor.pass.cpp =================================================================== --- libcxx/test/std/input.output/filesystems/class.directory_iterator/directory_iterator.members/ctor.pass.cpp +++ libcxx/test/std/input.output/filesystems/class.directory_iterator/directory_iterator.members/ctor.pass.cpp @@ -8,8 +8,6 @@ // UNSUPPORTED: c++03 -// XFAIL: LIBCXX-WINDOWS-FIXME - // // class directory_iterator @@ -94,7 +92,8 @@ // reading directories; test using a special inaccessible directory // instead. const path testDir = GetWindowsInaccessibleDir(); - TEST_REQUIRE(!testDir.empty()); + if (testDir.empty()) + TEST_UNSUPPORTED(); #else scoped_test_env env; path const testDir = env.make_env_path("dir1"); Index: libcxx/test/std/input.output/filesystems/class.rec.dir.itr/rec.dir.itr.members/ctor.pass.cpp =================================================================== --- libcxx/test/std/input.output/filesystems/class.rec.dir.itr/rec.dir.itr.members/ctor.pass.cpp +++ libcxx/test/std/input.output/filesystems/class.rec.dir.itr/rec.dir.itr.members/ctor.pass.cpp @@ -8,8 +8,6 @@ // UNSUPPORTED: c++03 -// XFAIL: LIBCXX-WINDOWS-FIXME - // // class directory_iterator @@ -95,7 +93,8 @@ // reading directories; test using a special inaccessible directory // instead. const path testDir = GetWindowsInaccessibleDir(); - TEST_REQUIRE(!testDir.empty()); + if (testDir.empty()) + TEST_UNSUPPORTED(); #else scoped_test_env env; path const testDir = env.make_env_path("dir1"); Index: libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.exists/exists.pass.cpp =================================================================== --- libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.exists/exists.pass.cpp +++ libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.exists/exists.pass.cpp @@ -8,8 +8,6 @@ // UNSUPPORTED: c++03 -// XFAIL: LIBCXX-WINDOWS-FIXME - // // bool exists(file_status s) noexcept @@ -91,7 +89,8 @@ // reading directories; test using a special inaccessible directory // instead. const path p = GetWindowsInaccessibleDir(); - TEST_REQUIRE(!p.empty()); + if (p.empty()) + TEST_UNSUPPORTED(); #else scoped_test_env env; const path dir = env.create_dir("dir"); Index: libcxx/test/support/filesystem_test_helper.h =================================================================== --- libcxx/test/support/filesystem_test_helper.h +++ libcxx/test/support/filesystem_test_helper.h @@ -678,22 +678,28 @@ const fs::path dir("C:\\System Volume Information"); std::error_code ec; const fs::path root("C:\\"); - fs::directory_iterator it(root, ec); - if (ec) - return fs::path(); - const fs::directory_iterator endIt{}; - while (it != endIt) { - const fs::directory_entry &ent = *it; - if (ent == dir) { - // Basic sanity checks on the directory_entry - if (!ent.exists()) - return fs::path(); - if (!ent.is_directory()) - return fs::path(); - return ent; + for (const auto &ent : fs::directory_iterator(root, ec)) { + if (ent != dir) + continue; + // Basic sanity checks on the directory_entry + if (!ent.exists() || !ent.is_directory()) { + fprintf(stderr, "The expected inaccessible directory \"%s\" was found " + "but doesn't behave as expected, skipping tests " + "regarding it\n", dir.string().c_str()); + return fs::path(); } - ++it; + // Check that it indeed is inaccessible as expected + (void)fs::exists(ent, ec); + if (!ec) { + fprintf(stderr, "The expected inaccessible directory \"%s\" was found " + "but seems to be accessible, skipping tests " + "regarding it\n", dir.string().c_str()); + return fs::path(); + } + return ent; } + fprintf(stderr, "No inaccessible directory \"%s\" found, skipping tests " + "regarding it\n", dir.string().c_str()); return fs::path(); } Index: libcxx/utils/ci/buildkite-pipeline.yml =================================================================== --- libcxx/utils/ci/buildkite-pipeline.yml +++ libcxx/utils/ci/buildkite-pipeline.yml @@ -299,10 +299,23 @@ - exit_status: -1 # Agent was lost limit: 2 - - label: "Windows" + - label: "Windows (DLL)" # TODO: The CI runner doesn't have bash in the path currently. Once it # has that, remove the absolute path and just call 'bash' here. - command: "\"\\Program Files\\Git\\usr\\bin\\bash\" libcxx/utils/ci/run-buildbot generic-win" + command: "\"\\Program Files\\Git\\usr\\bin\\bash\" libcxx/utils/ci/run-buildbot generic-win-dll" + artifact_paths: + - "**/test-results.xml" + agents: + queue: "windows" + retry: + automatic: + - exit_status: -1 # Agent was lost + limit: 2 + + - label: "Windows (Static)" + # TODO: The CI runner doesn't have bash in the path currently. Once it + # has that, remove the absolute path and just call 'bash' here. + command: "\"\\Program Files\\Git\\usr\\bin\\bash\" libcxx/utils/ci/run-buildbot generic-win-static" artifact_paths: - "**/test-results.xml" agents: Index: libcxx/utils/ci/run-buildbot =================================================================== --- libcxx/utils/ci/run-buildbot +++ libcxx/utils/ci/run-buildbot @@ -455,7 +455,7 @@ generate-cmake -C "${MONOREPO_ROOT}/libcxx/cmake/caches/Armv7Thumb-noexceptions.cmake" check-cxx-cxxabi ;; -generic-win) +generic-win*) clean # TODO: The CI runner doesn't have bash in the path currently, and it's # needed for running tests. Once it's available out of the box, remove this. @@ -475,12 +475,10 @@ # the "clang-cl" driver for compiling). When the CI runner runs # Clang 12, the "-loldnames" option can be dropped. - # TODO: Currently, building with the experimental library breaks running - # tests (the test linking look for the c++experimental library with the - # wrong name, and the statically linked c++experimental can't be linked - # correctly when libc++ visibility attributes indicate dllimport linkage - # anyway), thus just disable the experimental library. Remove this - # setting when cmake and the test driver does the right thing automatically. + CMAKE_FLAGS="" + if [ "${BUILDER}" = "generic-win-static" ]; then + CMAKE_FLAGS="$CMAKE_FLAGS -DLIBCXX_ENABLE_SHARED=NO" + fi echo "--- Generating CMake" cmake -S "${MONOREPO_ROOT}/libcxx" \ @@ -490,11 +488,11 @@ -DCMAKE_C_COMPILER=clang-cl \ -DCMAKE_CXX_COMPILER=clang-cl \ -DLLVM_LIT_ARGS="-sv --show-unsupported --xunit-xml-output test-results.xml" \ - -DLIBCXX_ENABLE_EXPERIMENTAL_LIBRARY=NO \ -DLIBCXX_ENABLE_FILESYSTEM=YES \ -DCMAKE_CXX_FLAGS="-D_LIBCPP_HAS_NO_INT128" \ -DLIBCXX_TEST_COMPILER_FLAGS="-D_LIBCPP_HAS_NO_INT128" \ - -DLIBCXX_TEST_LINKER_FLAGS="-loldnames" + -DLIBCXX_TEST_LINKER_FLAGS="-loldnames" \ + ${CMAKE_FLAGS} echo "+++ Running the libc++ tests" ${NINJA} -vC "${BUILD_DIR}" check-cxx ;; Index: libcxx/utils/libcxx/test/features.py =================================================================== --- libcxx/utils/libcxx/test/features.py +++ libcxx/utils/libcxx/test/features.py @@ -14,6 +14,7 @@ _isClang = lambda cfg: '__clang__' in compilerMacros(cfg) and '__apple_build_version__' not in compilerMacros(cfg) _isAppleClang = lambda cfg: '__apple_build_version__' in compilerMacros(cfg) _isGCC = lambda cfg: '__GNUC__' in compilerMacros(cfg) and '__clang__' not in compilerMacros(cfg) +_isMSVC = lambda cfg: '_MSC_VER' in compilerMacros(cfg) DEFAULT_FEATURES = [ Feature(name='fcoroutines-ts', @@ -81,6 +82,8 @@ Feature(name=lambda cfg: 'gcc-{__GNUC__}'.format(**compilerMacros(cfg)), when=_isGCC), Feature(name=lambda cfg: 'gcc-{__GNUC__}.{__GNUC_MINOR__}'.format(**compilerMacros(cfg)), when=_isGCC), Feature(name=lambda cfg: 'gcc-{__GNUC__}.{__GNUC_MINOR__}.{__GNUC_PATCHLEVEL__}'.format(**compilerMacros(cfg)), when=_isGCC), + + Feature(name='msvc', when=_isMSVC), ] # Deduce and add the test features that that are implied by the #defines in Index: libcxx/utils/libcxx/test/params.py =================================================================== --- libcxx/utils/libcxx/test/params.py +++ libcxx/utils/libcxx/test/params.py @@ -7,6 +7,7 @@ #===----------------------------------------------------------------------===## from libcxx.test.dsl import * +from libcxx.test.features import _isMSVC _allStandards = ['c++03', 'c++11', 'c++14', 'c++17', 'c++2a', 'c++2b'] _warningFlags = [ @@ -91,7 +92,12 @@ help="Whether to enable tests for experimental C++ libraries (typically Library Fundamentals TSes).", actions=lambda experimental: [] if not experimental else [ AddFeature('c++experimental'), - PrependLinkFlag('-lc++experimental') + # When linking in MSVC mode via the Clang driver, a -l + # maps to .lib, so we need to use -llibc++experimental here + # to make it link against the static libc++experimental.lib. + # We can't check for the feature 'msvc' in available_features + # as those features are added after processing parameters. + PrependLinkFlag(lambda config: '-llibc++experimental' if _isMSVC(config) else '-lc++experimental') ]), Parameter(name='long_tests', choices=[True, False], type=bool, default=True,