[clang] [compiler-rt] [libcxx] [llvm] [libc++] Replace LIBCXX_ENABLE_STATIC_ABI_LIBRARY & friends by a new LIBCXX_CXX_ABI choice (PR #112978)

Louis Dionne via cfe-commits cfe-commits at lists.llvm.org
Tue Nov 19 15:47:36 PST 2024


https://github.com/ldionne updated https://github.com/llvm/llvm-project/pull/112978

>From e42db172b2397e6358dc60850e4c8b085c6bd4bf Mon Sep 17 00:00:00 2001
From: Louis Dionne <ldionne.2 at gmail.com>
Date: Fri, 13 May 2022 09:26:01 -0400
Subject: [PATCH 1/4] [libc++] Replace LIBCXX_ENABLE_STATIC_ABI_LIBRARY &
 friends by a new LIBCXX_CXX_ABI choice

Instead of having complicated options like LIBCXX_ENABLE_STATIC_ABI_LIBRARY
and LIBCXX_STATICALLY_LINK_ABI_IN_STATIC_LIBRARY, introduce a more general
mechanism to select the ABI library used by libc++. The new mechanism allows
specifying the ABI library for the static libc++ and the shared libc++
separately, and allows selecting a "merged" flavor of libc++ for both
the in-tree libc++abi and any external static ABI library.

As an example, one can now specify arbitrary combinations like
   -DLIBCXX_ABILIB_FOR_SHARED="shared-libcxxabi"
   -DLIBCXX_ABILIB_FOR_STATIC="merged-libcxxabi"

which would have been impossible or very brittle in the past. In theory,
one can even select an entirely different ABI library for the static and
the shared libc++ (e.g. libc++abi vs libsupc++), although supporting that
is not a primary goal of this patch but merely a result of the general
mechanism.

Closes #77655
Fixes #57759

Differential Revision: https://reviews.llvm.org/D125683
---
 clang/cmake/caches/Android.cmake              |   4 +-
 clang/cmake/caches/CrossWinToARMLinux.cmake   |   4 +-
 clang/cmake/caches/Fuchsia-stage2.cmake       |   8 +-
 clang/cmake/caches/Fuchsia.cmake              |   4 +-
 compiler-rt/cmake/Modules/AddCompilerRT.cmake |   3 +-
 libcxx/CMakeLists.txt                         |  68 +++--
 libcxx/cmake/Modules/HandleLibCXXABI.cmake    | 254 +++++++++---------
 libcxx/cmake/caches/AMDGPU.cmake              |   4 +-
 libcxx/cmake/caches/AndroidNDK.cmake          |   2 +-
 libcxx/cmake/caches/Generic-merged.cmake      |   3 +-
 libcxx/cmake/caches/MinGW.cmake               |   4 +-
 libcxx/cmake/caches/NVPTX.cmake               |   4 +-
 libcxx/docs/ReleaseNotes/20.rst               |   6 +-
 libcxx/docs/VendorDocumentation.rst           |  24 +-
 libcxx/include/CMakeLists.txt                 |   2 +-
 libcxx/src/CMakeLists.txt                     |  19 +-
 libcxx/utils/ci/run-buildbot                  |   4 -
 .../docs/HowToBuildWindowsItaniumPrograms.rst |  28 +-
 18 files changed, 215 insertions(+), 230 deletions(-)

diff --git a/clang/cmake/caches/Android.cmake b/clang/cmake/caches/Android.cmake
index d5ca6b50d4ada7..dbf66539591394 100644
--- a/clang/cmake/caches/Android.cmake
+++ b/clang/cmake/caches/Android.cmake
@@ -21,8 +21,8 @@ if (LIBCXX_ENABLE_ABI_LINKER_SCRIPT)
   list(APPEND EXTRA_ARGS -DLIBCXX_ENABLE_ABI_LINKER_SCRIPT=${LIBCXX_ENABLE_ABI_LINKER_SCRIPT})
 endif()
 
-if (LIBCXX_ENABLE_STATIC_ABI_LIBRARY)
-  list(APPEND EXTRA_ARGS -DLIBCXX_ENABLE_STATIC_ABI_LIBRARY=${LIBCXX_ENABLE_STATIC_ABI_LIBRARY})
+if (LIBCXX_CXX_ABI)
+  list(APPEND EXTRA_ARGS -DLIBCXX_CXX_ABI=${LIBCXX_CXX_ABI})
 endif()
 
 if (LLVM_BUILD_EXTERNAL_COMPILER_RT)
diff --git a/clang/cmake/caches/CrossWinToARMLinux.cmake b/clang/cmake/caches/CrossWinToARMLinux.cmake
index 87118bbd33377d..15341aeb283801 100644
--- a/clang/cmake/caches/CrossWinToARMLinux.cmake
+++ b/clang/cmake/caches/CrossWinToARMLinux.cmake
@@ -215,10 +215,8 @@ set(RUNTIMES_${TOOLCHAIN_TARGET_TRIPLE}_LIBCXXABI_ENABLE_SHARED
 set(RUNTIMES_${TOOLCHAIN_TARGET_TRIPLE}_LIBCXX_USE_COMPILER_RT                    ON CACHE BOOL "")
 set(RUNTIMES_${TOOLCHAIN_TARGET_TRIPLE}_LIBCXX_ENABLE_SHARED                      ${TOOLCHAIN_SHARED_LIBS} CACHE BOOL "")
 set(RUNTIMES_${TOOLCHAIN_TARGET_TRIPLE}_LIBCXX_ABI_VERSION                        ${LIBCXX_ABI_VERSION} CACHE STRING "")
-set(RUNTIMES_${TOOLCHAIN_TARGET_TRIPLE}_LIBCXX_CXX_ABI                            "libcxxabi" CACHE STRING "")    #!!!
+set(RUNTIMES_${TOOLCHAIN_TARGET_TRIPLE}_LIBCXX_CXX_ABI                            "merged-libcxxabi" CACHE STRING "")    #!!!
 set(RUNTIMES_${TOOLCHAIN_TARGET_TRIPLE}_LIBCXX_ENABLE_NEW_DELETE_DEFINITIONS      ON CACHE BOOL "")
-# Merge libc++ and libc++abi libraries into the single libc++ library file.
-set(RUNTIMES_${TOOLCHAIN_TARGET_TRIPLE}_LIBCXX_ENABLE_STATIC_ABI_LIBRARY          ON CACHE BOOL "")
 
 # Avoid searching for the python3 interpreter during the runtimes configuration for the cross builds.
 # It starts searching the python3 package using the target's sysroot path, that usually is not compatible with the build host.
diff --git a/clang/cmake/caches/Fuchsia-stage2.cmake b/clang/cmake/caches/Fuchsia-stage2.cmake
index 747d9974828984..14f55541944793 100644
--- a/clang/cmake/caches/Fuchsia-stage2.cmake
+++ b/clang/cmake/caches/Fuchsia-stage2.cmake
@@ -83,7 +83,7 @@ if(APPLE)
   set(LIBCXXABI_USE_LLVM_UNWINDER ON CACHE BOOL "")
   set(LIBCXX_ABI_VERSION 2 CACHE STRING "")
   set(LIBCXX_ENABLE_SHARED OFF CACHE BOOL "")
-  set(LIBCXX_ENABLE_STATIC_ABI_LIBRARY ON CACHE BOOL "")
+  set(LIBCXX_CXX_ABI "merged-libcxxabi" CACHE STRING "")
   set(LIBCXX_HARDENING_MODE "none" CACHE STRING "")
   set(LIBCXX_USE_COMPILER_RT ON CACHE BOOL "")
   set(RUNTIMES_CMAKE_ARGS "-DCMAKE_OSX_DEPLOYMENT_TARGET=10.13;-DCMAKE_OSX_ARCHITECTURES=arm64|x86_64" CACHE STRING "")
@@ -180,9 +180,9 @@ foreach(target aarch64-unknown-linux-gnu;armv7-unknown-linux-gnueabihf;i386-unkn
     set(RUNTIMES_${target}_LIBCXXABI_ENABLE_SHARED OFF CACHE BOOL "")
     set(RUNTIMES_${target}_LIBCXXABI_USE_LLVM_UNWINDER ON CACHE BOOL "")
     set(RUNTIMES_${target}_LIBCXXABI_INSTALL_LIBRARY OFF CACHE BOOL "")
+    set(RUNTIMES_${target}_LIBCXX_CXX_ABI "merged-libcxxabi" CACHE STRING "")
     set(RUNTIMES_${target}_LIBCXX_USE_COMPILER_RT ON CACHE BOOL "")
     set(RUNTIMES_${target}_LIBCXX_ENABLE_SHARED OFF CACHE BOOL "")
-    set(RUNTIMES_${target}_LIBCXX_ENABLE_STATIC_ABI_LIBRARY ON CACHE BOOL "")
     set(RUNTIMES_${target}_LIBCXX_ABI_VERSION 2 CACHE STRING "")
     set(RUNTIMES_${target}_LLVM_ENABLE_ASSERTIONS OFF CACHE BOOL "")
     set(RUNTIMES_${target}_SANITIZER_CXX_ABI "libc++" CACHE STRING "")
@@ -244,10 +244,10 @@ if(FUCHSIA_SDK)
     set(RUNTIMES_${target}_LIBCXXABI_USE_LLVM_UNWINDER ON CACHE BOOL "")
     set(RUNTIMES_${target}_LIBCXXABI_HERMETIC_STATIC_LIBRARY ON CACHE BOOL "")
     set(RUNTIMES_${target}_LIBCXXABI_INSTALL_STATIC_LIBRARY OFF CACHE BOOL "")
+    set(RUNTIMES_${target}_LIBCXX_ABILIB_FOR_STATIC "merged-libcxxabi" CACHE STRING "")
+    set(RUNTIMES_${target}_LIBCXX_ABILIB_FOR_SHARED "shared-libcxxabi" CACHE STRING "")
     set(RUNTIMES_${target}_LIBCXX_USE_COMPILER_RT ON CACHE BOOL "")
-    set(RUNTIMES_${target}_LIBCXX_ENABLE_STATIC_ABI_LIBRARY ON CACHE BOOL "")
     set(RUNTIMES_${target}_LIBCXX_HERMETIC_STATIC_LIBRARY ON CACHE BOOL "")
-    set(RUNTIMES_${target}_LIBCXX_STATICALLY_LINK_ABI_IN_SHARED_LIBRARY OFF CACHE BOOL "")
     set(RUNTIMES_${target}_LIBCXX_ABI_VERSION 2 CACHE STRING "")
     set(RUNTIMES_${target}_LLVM_ENABLE_ASSERTIONS OFF CACHE BOOL "")
     set(RUNTIMES_${target}_LLVM_ENABLE_RUNTIMES "compiler-rt;libcxx;libcxxabi;libunwind" CACHE STRING "")
diff --git a/clang/cmake/caches/Fuchsia.cmake b/clang/cmake/caches/Fuchsia.cmake
index 2d2dcb9ae6798d..5e31fe4fdd33df 100644
--- a/clang/cmake/caches/Fuchsia.cmake
+++ b/clang/cmake/caches/Fuchsia.cmake
@@ -109,6 +109,7 @@ endif()
 
 if(WIN32)
   set(LIBCXX_ABI_VERSION 2 CACHE STRING "")
+  set(LIBCXX_CXX_ABI "merged-libcxxabi" CACHE STRING "")
   set(LIBCXX_ENABLE_ABI_LINKER_SCRIPT OFF CACHE BOOL "")
   set(LIBCXX_ENABLE_SHARED OFF CACHE BOOL "")
   set(BUILTINS_CMAKE_ARGS -DCMAKE_SYSTEM_NAME=Windows CACHE STRING "")
@@ -125,7 +126,6 @@ else()
   set(LIBCXXABI_USE_LLVM_UNWINDER ON CACHE BOOL "")
   set(LIBCXX_ABI_VERSION 2 CACHE STRING "")
   set(LIBCXX_ENABLE_SHARED OFF CACHE BOOL "")
-  set(LIBCXX_ENABLE_STATIC_ABI_LIBRARY ON CACHE BOOL "")
   set(LIBCXX_HARDENING_MODE "none" CACHE STRING "")
   set(LIBCXX_USE_COMPILER_RT ON CACHE BOOL "")
   set(LLVM_ENABLE_RUNTIMES "compiler-rt;libcxx;libcxxabi;libunwind" CACHE STRING "")
@@ -155,8 +155,8 @@ if(BOOTSTRAP_CMAKE_SYSTEM_NAME)
     set(RUNTIMES_${target}_LIBCXXABI_INSTALL_LIBRARY OFF CACHE BOOL "")
     set(RUNTIMES_${target}_LIBCXX_USE_COMPILER_RT ON CACHE BOOL "")
     set(RUNTIMES_${target}_LIBCXX_ENABLE_SHARED OFF CACHE BOOL "")
-    set(RUNTIMES_${target}_LIBCXX_ENABLE_STATIC_ABI_LIBRARY ON CACHE BOOL "")
     set(RUNTIMES_${target}_LIBCXX_ABI_VERSION 2 CACHE STRING "")
+    set(RUNTIMES_${target}_LIBCXX_CXX_ABI "merged-libcxxabi" CACHE STRING "")
     set(RUNTIMES_${target}_LLVM_ENABLE_ASSERTIONS OFF CACHE BOOL "")
     set(RUNTIMES_${target}_LLVM_ENABLE_RUNTIMES "compiler-rt;libcxx;libcxxabi;libunwind" CACHE STRING "")
     set(RUNTIMES_${target}_SANITIZER_CXX_ABI "libc++" CACHE STRING "")
diff --git a/compiler-rt/cmake/Modules/AddCompilerRT.cmake b/compiler-rt/cmake/Modules/AddCompilerRT.cmake
index 77261f631ea117..319468ae93aabb 100644
--- a/compiler-rt/cmake/Modules/AddCompilerRT.cmake
+++ b/compiler-rt/cmake/Modules/AddCompilerRT.cmake
@@ -697,12 +697,11 @@ macro(add_custom_libcxx name prefix)
                -DLIBCXXABI_ENABLE_SHARED=OFF
                -DLIBCXXABI_HERMETIC_STATIC_LIBRARY=ON
                -DLIBCXXABI_INCLUDE_TESTS=OFF
-               -DLIBCXX_CXX_ABI=libcxxabi
+               -DLIBCXX_CXX_ABI="merged-libcxxabi"
                -DLIBCXX_ENABLE_SHARED=OFF
                -DLIBCXX_HERMETIC_STATIC_LIBRARY=ON
                -DLIBCXX_INCLUDE_BENCHMARKS=OFF
                -DLIBCXX_INCLUDE_TESTS=OFF
-               -DLIBCXX_ENABLE_STATIC_ABI_LIBRARY=ON
                -DLLVM_INCLUDE_TESTS=OFF
                -DLLVM_INCLUDE_DOCS=OFF
                ${LIBCXX_CMAKE_ARGS}
diff --git a/libcxx/CMakeLists.txt b/libcxx/CMakeLists.txt
index abe12c2805a7cf..57d86d97e33f02 100644
--- a/libcxx/CMakeLists.txt
+++ b/libcxx/CMakeLists.txt
@@ -241,11 +241,22 @@ else()
 endif()
 
 set(LIBCXX_SUPPORTED_ABI_LIBRARIES none libcxxabi system-libcxxabi libcxxrt libstdc++ libsupc++ vcruntime)
-set(LIBCXX_CXX_ABI "${LIBCXX_DEFAULT_ABI_LIBRARY}" CACHE STRING "Specify C++ ABI library to use. Supported values are ${LIBCXX_SUPPORTED_ABI_LIBRARIES}.")
-if (NOT "${LIBCXX_CXX_ABI}" IN_LIST LIBCXX_SUPPORTED_ABI_LIBRARIES)
-  message(FATAL_ERROR "Unsupported C++ ABI library: '${LIBCXX_CXX_ABI}'. Supported values are ${LIBCXX_SUPPORTED_ABI_LIBRARIES}.")
-endif()
-
+set(LIBCXX_CXX_ABI "${LIBCXX_DEFAULT_ABI_LIBRARY}" CACHE STRING
+  "Specify the C++ ABI library to use for the shared and the static libc++ libraries. Supported values are ${LIBCXX_SUPPORTED_ABI_LIBRARIES}.
+   This CMake option also supports \"consumption specifiers\", which specify how the selected ABI library should be consumed by
+   libc++. The supported specifiers are:
+   - `shared`: The selected ABI library should be used as a shared library.
+   - `static`: The selected ABI library should be used as a static library.
+   - `merged`: The selected ABI library should be a static library whose object files will be merged directly into the produced libc++ library.
+
+   A consumption specifier is provided by appending it to the name of the library, such as `LIBCXX_CXX_ABI=merged-libcxxabi`.
+   If no consumption specifier is provided, the libc++ shared library will default to using a shared ABI library, and the
+   libc++ static library will default to using a static ABI library.")
+set(LIBCXX_ABILIB_FOR_SHARED "${LIBCXX_CXX_ABI}" CACHE STRING "C++ ABI library to use for the shared libc++ library.")
+set(LIBCXX_ABILIB_FOR_STATIC "${LIBCXX_CXX_ABI}" CACHE STRING "C++ ABI library to use for the static libc++ library.")
+
+#############################
+# TODO: Remove these options in LLVM 21.
 option(LIBCXX_ENABLE_STATIC_ABI_LIBRARY
   "Use a static copy of the ABI library when linking libc++.
    This option cannot be used with LIBCXX_ENABLE_ABI_LINKER_SCRIPT." OFF)
@@ -258,17 +269,34 @@ option(LIBCXX_STATICALLY_LINK_ABI_IN_SHARED_LIBRARY
   "Statically link the ABI library to shared library"
   ${LIBCXX_ENABLE_STATIC_ABI_LIBRARY})
 
+if (LIBCXX_ENABLE_STATIC_ABI_LIBRARY OR LIBCXX_STATICALLY_LINK_ABI_IN_STATIC_LIBRARY OR LIBCXX_STATICALLY_LINK_ABI_IN_SHARED_LIBRARY)
+  message(WARNING "The LIBCXX_ENABLE_STATIC_ABI_LIBRARY, LIBCXX_STATICALLY_LINK_ABI_IN_STATIC_LIBRARY and "
+                  "LIBCXX_STATICALLY_LINK_ABI_IN_SHARED_LIBRARY options have been deprecated in favor of "
+                  "using LIBCXX_CXX_ABI=merged-libcxxabi. This will become an error in LLVM 21.")
+endif()
+if (LIBCXX_STATICALLY_LINK_ABI_IN_STATIC_LIBRARY)
+  set(LIBCXX_ABILIB_FOR_STATIC "merged-libcxxabi" CACHE STRING "" FORCE)
+endif()
+if (LIBCXX_STATICALLY_LINK_ABI_IN_SHARED_LIBRARY)
+  set(LIBCXX_ABILIB_FOR_SHARED "merged-libcxxabi" CACHE STRING "" FORCE)
+endif()
+#############################
+
 # Generate and install a linker script inplace of libc++.so. The linker script
 # will link libc++ to the correct ABI library. This option is on by default
-# on UNIX platforms other than Apple, and on the Fuchsia platform unless we
-# statically link libc++abi inside libc++.so, we don't build libc++.so at all
-# or we don't have any ABI library.
-if (LIBCXX_STATICALLY_LINK_ABI_IN_SHARED_LIBRARY
-    OR NOT LIBCXX_ENABLE_SHARED
-    OR LIBCXX_CXX_ABI STREQUAL "none")
-  set(ENABLE_LINKER_SCRIPT_DEFAULT_VALUE OFF)
-elseif((UNIX OR FUCHSIA) AND NOT APPLE)
-  set(ENABLE_LINKER_SCRIPT_DEFAULT_VALUE ON)
+# on UNIX platforms other than Apple unless we are linking the ABI library as
+# an object library. This option is also disabled when the ABI library is not
+# specified or is specified to be "none".
+if(LIBCXX_ENABLE_SHARED)
+  if ((UNIX OR FUCHSIA) AND NOT APPLE)
+    if (LIBCXX_ABILIB_FOR_SHARED STREQUAL "merged-libcxxabi" OR LIBCXX_ABILIB_FOR_SHARED STREQUAL "none")
+      set(ENABLE_LINKER_SCRIPT_DEFAULT_VALUE OFF)
+    else()
+      set(ENABLE_LINKER_SCRIPT_DEFAULT_VALUE ON)
+    endif()
+  else()
+    set(ENABLE_LINKER_SCRIPT_DEFAULT_VALUE OFF)
+  endif()
 else()
   set(ENABLE_LINKER_SCRIPT_DEFAULT_VALUE OFF)
 endif()
@@ -382,12 +410,6 @@ if (LIBCXX_ENABLE_ABI_LINKER_SCRIPT)
     endif()
 endif()
 
-if (LIBCXX_STATICALLY_LINK_ABI_IN_SHARED_LIBRARY AND LIBCXX_ENABLE_ABI_LINKER_SCRIPT)
-    message(FATAL_ERROR "Conflicting options given.
-        LIBCXX_STATICALLY_LINK_ABI_IN_SHARED_LIBRARY cannot be specified with
-        LIBCXX_ENABLE_ABI_LINKER_SCRIPT")
-endif()
-
 if (LIBCXX_ABI_FORCE_ITANIUM AND LIBCXX_ABI_FORCE_MICROSOFT)
   message(FATAL_ERROR "Only one of LIBCXX_ABI_FORCE_ITANIUM and LIBCXX_ABI_FORCE_MICROSOFT can be specified.")
 endif ()
@@ -484,10 +506,6 @@ include(config-ix)
 #===============================================================================
 # Setup Compiler Flags
 #===============================================================================
-
-include(HandleLibC) # Setup the C library flags
-include(HandleLibCXXABI) # Setup the ABI library flags
-
 # FIXME(EricWF): See the FIXME on LIBCXX_ENABLE_PEDANTIC.
 # Remove the -pedantic flag and -Wno-pedantic and -pedantic-errors
 # so they don't get transformed into -Wno and -errors respectively.
@@ -820,7 +838,7 @@ if (DEFINED WIN32 AND LIBCXX_ENABLE_STATIC AND NOT LIBCXX_ENABLE_SHARED)
   config_define(ON _LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS)
 endif()
 
-if (WIN32 AND LIBCXX_ENABLE_STATIC_ABI_LIBRARY)
+if (WIN32 AND LIBCXX_CXX_ABI STREQUAL "merged-libcxxabi")
   # If linking libcxxabi statically into libcxx, skip the dllimport attributes
   # on symbols we refer to from libcxxabi.
   add_definitions(-D_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS)
diff --git a/libcxx/cmake/Modules/HandleLibCXXABI.cmake b/libcxx/cmake/Modules/HandleLibCXXABI.cmake
index 52236f473f35de..fa4b761e818021 100644
--- a/libcxx/cmake/Modules/HandleLibCXXABI.cmake
+++ b/libcxx/cmake/Modules/HandleLibCXXABI.cmake
@@ -2,18 +2,10 @@
 # Define targets for linking against the selected ABI library
 #
 # After including this file, the following targets are defined:
-# - libcxx-abi-headers: An interface target that allows getting access to the
-#                       headers of the selected ABI library.
-# - libcxx-abi-shared: A target representing the selected shared ABI library.
-# - libcxx-abi-static: A target representing the selected static ABI library.
-#
-# Furthermore, some ABI libraries also define the following target:
-# - libcxx-abi-shared-objects: An object library representing a set of object files
-#                              constituting the ABI library, suitable for bundling
-#                              into a shared library.
-# - libcxx-abi-static-objects: An object library representing a set of object files
-#                              constituting the ABI library, suitable for bundling
-#                              into a static library.
+# - libcxx-abi-shared: A target representing the selected ABI library for linking into
+#                      the libc++ shared library.
+# - libcxx-abi-static: A target representing the selected ABI library for linking into
+#                      the libc++ static library.
 #===============================================================================
 
 include(GNUInstallDirs)
@@ -29,6 +21,9 @@ include(GNUInstallDirs)
 # the search path. Instead, what we do is copy just the ABI library headers to
 # a private directory and add just that path when we build libc++.
 function(import_private_headers target include_dirs headers)
+  if (NOT ${include_dirs})
+    message(FATAL_ERROR "Missing include paths for the selected ABI library!")
+  endif()
   foreach(header ${headers})
     set(found FALSE)
     foreach(incpath ${include_dirs})
@@ -38,11 +33,11 @@ function(import_private_headers target include_dirs headers)
         get_filename_component(dstdir ${header} PATH)
         get_filename_component(header_file ${header} NAME)
         set(src ${incpath}/${header})
-        set(dst "${LIBCXX_BINARY_DIR}/private-abi-headers/${dstdir}/${header_file}")
+        set(dst "${LIBCXX_BINARY_DIR}/private-abi-headers/${target}/${dstdir}/${header_file}")
 
         add_custom_command(OUTPUT ${dst}
             DEPENDS ${src}
-            COMMAND ${CMAKE_COMMAND} -E make_directory "${LIBCXX_BINARY_DIR}/private-abi-headers/${dstdir}"
+            COMMAND ${CMAKE_COMMAND} -E make_directory "${LIBCXX_BINARY_DIR}/private-abi-headers/${target}/${dstdir}"
             COMMAND ${CMAKE_COMMAND} -E copy_if_different ${src} ${dst}
             COMMENT "Copying C++ ABI header ${header}")
         list(APPEND abilib_headers "${dst}")
@@ -60,7 +55,7 @@ function(import_private_headers target include_dirs headers)
   set_target_properties(${target}-generate-private-headers PROPERTIES LINKER_LANGUAGE CXX)
 
   target_link_libraries(${target} INTERFACE ${target}-generate-private-headers)
-  target_include_directories(${target} INTERFACE "${LIBCXX_BINARY_DIR}/private-abi-headers")
+  target_include_directories(${target} INTERFACE "${LIBCXX_BINARY_DIR}/private-abi-headers/${target}")
 endfunction()
 
 # This function creates an imported static library named <target>.
@@ -74,128 +69,135 @@ function(import_static_library target path name)
   set_target_properties(${target} PROPERTIES IMPORTED_LOCATION "${file}")
 endfunction()
 
-# This function creates an imported shared (interface) library named <target>
-# for the given library <name>.
-function(import_shared_library target name)
-  add_library(${target} INTERFACE IMPORTED GLOBAL)
-  set_target_properties(${target} PROPERTIES IMPORTED_LIBNAME "${name}")
-endfunction()
-
-# Link against a system-provided libstdc++
-if ("${LIBCXX_CXX_ABI}" STREQUAL "libstdc++")
-  if(NOT LIBCXX_CXX_ABI_INCLUDE_PATHS)
-    message(FATAL_ERROR "LIBCXX_CXX_ABI_INCLUDE_PATHS must be set when selecting libstdc++ as an ABI library")
+# This function creates a library target for linking against an external ABI library.
+#
+# <target>: The name of the target to create
+# <name>: The name of the library file to search for (e.g. c++abi, stdc++, cxxrt, supc++)
+# <type>: Whether to set up a static or a shared library (e.g. SHARED or STATIC)
+# <merged>: Whether to include the ABI library's object files directly into libc++. Only makes sense for a static ABI library.
+function(import_external_abi_library target name type merged)
+  if (${merged} AND "${type}" STREQUAL "SHARED")
+    message(FATAL_ERROR "Can't import an external ABI library for merging when requesting a shared ABI library.")
   endif()
 
-  add_library(libcxx-abi-headers INTERFACE)
-  import_private_headers(libcxx-abi-headers "${LIBCXX_CXX_ABI_INCLUDE_PATHS}"
-    "cxxabi.h;bits/c++config.h;bits/os_defines.h;bits/cpu_defines.h;bits/cxxabi_tweaks.h;bits/cxxabi_forced.h")
-  target_compile_definitions(libcxx-abi-headers INTERFACE "-DLIBSTDCXX" "-D__GLIBCXX__")
-
-  import_shared_library(libcxx-abi-shared stdc++)
-  target_link_libraries(libcxx-abi-shared INTERFACE libcxx-abi-headers)
-
-  import_static_library(libcxx-abi-static "${LIBCXX_CXX_ABI_LIBRARY_PATH}" stdc++)
-  target_link_libraries(libcxx-abi-static INTERFACE libcxx-abi-headers)
-
-# Link against a system-provided libsupc++
-elseif ("${LIBCXX_CXX_ABI}" STREQUAL "libsupc++")
-  if(NOT LIBCXX_CXX_ABI_INCLUDE_PATHS)
-    message(FATAL_ERROR "LIBCXX_CXX_ABI_INCLUDE_PATHS must be set when selecting libsupc++ as an ABI library")
+  if ("${type}" STREQUAL "SHARED")
+    add_library(${target} INTERFACE IMPORTED GLOBAL)
+    set_target_properties(${target} PROPERTIES IMPORTED_LIBNAME "${name}")
+  elseif ("${type}" STREQUAL "STATIC")
+    if (${merged})
+      import_static_library(${target}-impl "${LIBCXX_CXX_ABI_LIBRARY_PATH}" ${name})
+      add_library(${target} INTERFACE)
+      if (APPLE)
+        target_link_options(${target} INTERFACE
+          "-Wl,-force_load" "$<TARGET_PROPERTY:${target}-impl,IMPORTED_LOCATION>")
+      else()
+        target_link_options(${target} INTERFACE
+          "-Wl,--whole-archive" "-Wl,-Bstatic"
+          "$<TARGET_PROPERTY:${target}-impl,IMPORTED_LOCATION>"
+          "-Wl,-Bdynamic" "-Wl,--no-whole-archive")
+      endif()
+    else()
+      import_static_library(${target} "${LIBCXX_CXX_ABI_LIBRARY_PATH}" ${name})
+    endif()
   endif()
+endfunction()
 
-  add_library(libcxx-abi-headers INTERFACE)
-  import_private_headers(libcxx-abi-headers "${LIBCXX_CXX_ABI_INCLUDE_PATHS}"
-    "cxxabi.h;bits/c++config.h;bits/os_defines.h;bits/cpu_defines.h;bits/cxxabi_tweaks.h;bits/cxxabi_forced.h")
-  target_compile_definitions(libcxx-abi-headers INTERFACE "-D__GLIBCXX__")
-
-  import_shared_library(libcxx-abi-shared supc++)
-  target_link_libraries(libcxx-abi-shared INTERFACE libcxx-abi-headers)
-
-  import_static_library(libcxx-abi-static "${LIBCXX_CXX_ABI_LIBRARY_PATH}" supc++)
-  target_link_libraries(libcxx-abi-static INTERFACE libcxx-abi-headers)
-
-# Link against the in-tree libc++abi
-elseif ("${LIBCXX_CXX_ABI}" STREQUAL "libcxxabi")
-  add_library(libcxx-abi-headers INTERFACE)
-  target_link_libraries(libcxx-abi-headers INTERFACE cxxabi-headers)
-  target_compile_definitions(libcxx-abi-headers INTERFACE "-DLIBCXX_BUILDING_LIBCXXABI")
+# This function parses an ABI library choice (including optional consumption specifiers)
+# and generates a target representing the ABI library to link against.
+#
+# When a merged ABI library is requested, we only look for a static ABI library because
+# doing otherwise makes no sense. Otherwise, we search for the same type of ABI library
+# as we're linking into, i.e. we search for a shared ABI library when linking the shared
+# libc++ library, and a static ABI library otherwise.
+#
+# <abi_target>: The name of the target to create
+# <linked_into>: Whether this ABI library is linked into a STATIC or SHARED libc++
+# <input>: Input to parse as an ABI library choice with an optional consumption specifier.
+function(setup_abi_library abi_target linked_into input)
+  if ("${input}" MATCHES "^merged-(.+)$")
+    set(merged TRUE)
+    set(search_type "STATIC")
+  elseif ("${input}" MATCHES "^static-(.+)$")
+    set(merged FALSE)
+    set(search_type "STATIC")
+  elseif ("${input}" MATCHES "^shared-(.+)$")
+    set(merged FALSE)
+    set(search_type "SHARED")
+  else()
+    set(merged FALSE)
+    set(search_type "${linked_into}")
+  endif()
 
-  if (TARGET cxxabi_shared)
-    add_library(libcxx-abi-shared INTERFACE)
-    target_link_libraries(libcxx-abi-shared INTERFACE cxxabi_shared)
+  # Strip the consumption specifier from the name, which leaves the name of the standard library.
+  string(REGEX REPLACE "^(merged-|static-|shared-)" "" stdlib "${input}")
+
+  # Link against a system-provided libstdc++
+  if ("${stdlib}" STREQUAL "libstdc++")
+    import_external_abi_library(${abi_target} stdc++ ${search_type} ${merged})
+    import_private_headers(${abi_target} "${LIBCXX_CXX_ABI_INCLUDE_PATHS}"
+      "cxxabi.h;bits/c++config.h;bits/os_defines.h;bits/cpu_defines.h;bits/cxxabi_tweaks.h;bits/cxxabi_forced.h")
+    target_compile_definitions(${abi_target} INTERFACE "-DLIBSTDCXX" "-D__GLIBCXX__")
+
+  # Link against a system-provided libsupc++
+  elseif ("${stdlib}" STREQUAL "libsupc++")
+    import_external_abi_library(${abi_target} supc++ ${search_type} ${merged})
+    import_private_headers(${abi_target} "${LIBCXX_CXX_ABI_INCLUDE_PATHS}"
+      "cxxabi.h;bits/c++config.h;bits/os_defines.h;bits/cpu_defines.h;bits/cxxabi_tweaks.h;bits/cxxabi_forced.h")
+    target_compile_definitions(${abi_target} INTERFACE "-D__GLIBCXX__")
+
+  # Link against a system-provided libcxxrt
+  elseif ("${stdlib}" STREQUAL "libcxxrt")
+    import_external_abi_library(${abi_target} cxxrt ${search_type} ${merged})
+    import_private_headers(${abi_target} "${LIBCXX_CXX_ABI_INCLUDE_PATHS}"
+      "cxxabi.h;unwind.h;unwind-arm.h;unwind-itanium.h")
+    target_compile_definitions(${abi_target} INTERFACE "-DLIBCXXRT")
+
+  # Link against a system-provided vcruntime
+  elseif ("${stdlib}" STREQUAL "vcruntime")
+    # FIXME: Figure out how to configure the ABI library on Windows.
+    add_library(${abi_target} INTERFACE)
+
+  # Link against a system-provided libc++abi
+  elseif ("${stdlib}" STREQUAL "system-libcxxabi")
+    import_external_abi_library(${abi_target} c++abi ${search_type} ${merged})
+    import_private_headers(${abi_target} "${LIBCXX_CXX_ABI_INCLUDE_PATHS}"
+      "cxxabi.h;__cxxabi_config.h")
+    target_compile_definitions(${abi_target} INTERFACE "-DLIBCXX_BUILDING_LIBCXXABI")
+
+  # Link against the in-tree libc++abi.
+  elseif ("${stdlib}" STREQUAL "libcxxabi")
+    if (${merged})
+      get_target_property(_outdir cxxabi_static ARCHIVE_OUTPUT_DIRECTORY)
+      get_target_property(_outname cxxabi_static OUTPUT_NAME)
+      set(LIBCXX_CXX_ABI_LIBRARY_PATH "${_outdir}")
+      import_external_abi_library(${abi_target} "${_outname}" STATIC TRUE)
+    else()
+      string(TOLOWER "${search_type}" type)
+      add_library(${abi_target} INTERFACE)
+      target_link_libraries(${abi_target} INTERFACE cxxabi_${type})
+
+      # Populate the OUTPUT_NAME property of the target because that is used when
+      # generating a linker script.
+      get_target_property(_outname cxxabi_${type} OUTPUT_NAME)
+      set_target_properties(${abi_target} PROPERTIES "OUTPUT_NAME" "${_outname}")
+    endif()
 
     # When using the in-tree libc++abi as an ABI library, libc++ re-exports the
     # libc++abi symbols (on platforms where it can) because libc++abi is only an
     # implementation detail of libc++.
-    target_link_libraries(libcxx-abi-shared INTERFACE cxxabi-reexports)
+    target_link_libraries(${abi_target} INTERFACE cxxabi-reexports)
 
-    # Populate the OUTPUT_NAME property of libcxx-abi-shared because that is used when
-    # generating a linker script.
-    get_target_property(_output_name cxxabi_shared OUTPUT_NAME)
-    set_target_properties(libcxx-abi-shared PROPERTIES "OUTPUT_NAME" "${_output_name}")
-  endif()
-
-  if (TARGET cxxabi_static)
-    add_library(libcxx-abi-static ALIAS cxxabi_static)
-  endif()
+    target_compile_definitions(${abi_target} INTERFACE "LIBCXX_BUILDING_LIBCXXABI")
 
-  if (TARGET cxxabi_shared_objects)
-    add_library(libcxx-abi-shared-objects ALIAS cxxabi_shared_objects)
-  endif()
+  # Don't link against any ABI library
+  elseif ("${stdlib}" STREQUAL "none")
+    add_library(${abi_target} INTERFACE)
+    target_compile_definitions(${abi_target} INTERFACE "-D_LIBCPP_BUILDING_HAS_NO_ABI_LIBRARY")
 
-  if (TARGET cxxabi_static_objects)
-    add_library(libcxx-abi-static-objects ALIAS cxxabi_static_objects)
+  else()
+    message(FATAL_ERROR "Unsupported C++ ABI library selection: Got ABI selection '${input}', which parsed into standard library '${stdlib}'.")
   endif()
+endfunction()
 
-# Link against a system-provided libc++abi
-elseif ("${LIBCXX_CXX_ABI}" STREQUAL "system-libcxxabi")
-  if(NOT LIBCXX_CXX_ABI_INCLUDE_PATHS)
-    message(FATAL_ERROR "LIBCXX_CXX_ABI_INCLUDE_PATHS must be set when selecting system-libcxxabi as an ABI library")
-  endif()
-
-  add_library(libcxx-abi-headers INTERFACE)
-  import_private_headers(libcxx-abi-headers "${LIBCXX_CXX_ABI_INCLUDE_PATHS}" "cxxabi.h;__cxxabi_config.h")
-  target_compile_definitions(libcxx-abi-headers INTERFACE "-DLIBCXX_BUILDING_LIBCXXABI")
-
-  import_shared_library(libcxx-abi-shared c++abi)
-  target_link_libraries(libcxx-abi-shared INTERFACE libcxx-abi-headers)
-
-  import_static_library(libcxx-abi-static "${LIBCXX_CXX_ABI_LIBRARY_PATH}" c++abi)
-  target_link_libraries(libcxx-abi-static INTERFACE libcxx-abi-headers)
-
-# Link against a system-provided libcxxrt
-elseif ("${LIBCXX_CXX_ABI}" STREQUAL "libcxxrt")
-  if(NOT LIBCXX_CXX_ABI_INCLUDE_PATHS)
-    message(STATUS "LIBCXX_CXX_ABI_INCLUDE_PATHS not set, using /usr/include/c++/v1")
-    set(LIBCXX_CXX_ABI_INCLUDE_PATHS "/usr/include/c++/v1")
-  endif()
-  add_library(libcxx-abi-headers INTERFACE)
-  import_private_headers(libcxx-abi-headers "${LIBCXX_CXX_ABI_INCLUDE_PATHS}"
-    "cxxabi.h;unwind.h;unwind-arm.h;unwind-itanium.h")
-  target_compile_definitions(libcxx-abi-headers INTERFACE "-DLIBCXXRT")
-
-  import_shared_library(libcxx-abi-shared cxxrt)
-  target_link_libraries(libcxx-abi-shared INTERFACE libcxx-abi-headers)
-
-  import_static_library(libcxx-abi-static "${LIBCXX_CXX_ABI_LIBRARY_PATH}" cxxrt)
-  target_link_libraries(libcxx-abi-static INTERFACE libcxx-abi-headers)
-
-# Link against a system-provided vcruntime
-# FIXME: Figure out how to configure the ABI library on Windows.
-elseif ("${LIBCXX_CXX_ABI}" STREQUAL "vcruntime")
-  add_library(libcxx-abi-headers INTERFACE)
-  add_library(libcxx-abi-shared INTERFACE)
-  add_library(libcxx-abi-static INTERFACE)
-
-# Don't link against any ABI library
-elseif ("${LIBCXX_CXX_ABI}" STREQUAL "none")
-  add_library(libcxx-abi-headers INTERFACE)
-  target_compile_definitions(libcxx-abi-headers INTERFACE "-D_LIBCPP_BUILDING_HAS_NO_ABI_LIBRARY")
-
-  add_library(libcxx-abi-shared INTERFACE)
-  target_link_libraries(libcxx-abi-shared INTERFACE libcxx-abi-headers)
-
-  add_library(libcxx-abi-static INTERFACE)
-  target_link_libraries(libcxx-abi-static INTERFACE libcxx-abi-headers)
-endif()
+setup_abi_library(libcxx-abi-shared SHARED "${LIBCXX_ABILIB_FOR_SHARED}")
+setup_abi_library(libcxx-abi-static STATIC "${LIBCXX_ABILIB_FOR_STATIC}")
diff --git a/libcxx/cmake/caches/AMDGPU.cmake b/libcxx/cmake/caches/AMDGPU.cmake
index 49abb6475fc41e..a3a7a38d3ecc07 100644
--- a/libcxx/cmake/caches/AMDGPU.cmake
+++ b/libcxx/cmake/caches/AMDGPU.cmake
@@ -1,5 +1,5 @@
 # Configuration options for libcxx.
-set(LIBCXX_CXX_ABI libcxxabi CACHE STRING "")
+set(LIBCXX_CXX_ABI "merged-libcxxabi" CACHE STRING "")
 set(LIBCXX_ENABLE_EXCEPTIONS OFF CACHE BOOL "")
 set(LIBCXX_ENABLE_FILESYSTEM OFF CACHE BOOL "")
 set(LIBCXX_ENABLE_LOCALIZATION OFF CACHE BOOL "")
@@ -8,7 +8,6 @@ set(LIBCXX_ENABLE_NEW_DELETE_DEFINITIONS ON CACHE BOOL "")
 set(LIBCXX_ENABLE_RANDOM_DEVICE OFF CACHE BOOL "")
 set(LIBCXX_ENABLE_RTTI OFF CACHE BOOL "")
 set(LIBCXX_ENABLE_SHARED OFF CACHE BOOL "")
-set(LIBCXX_ENABLE_STATIC_ABI_LIBRARY ON CACHE BOOL "")
 set(LIBCXX_ENABLE_STATIC ON CACHE BOOL "")
 set(LIBCXX_ENABLE_THREADS OFF CACHE BOOL "")
 set(LIBCXX_ENABLE_UNICODE OFF CACHE BOOL "")
@@ -16,7 +15,6 @@ set(LIBCXX_ENABLE_WIDE_CHARACTERS OFF CACHE BOOL "")
 set(LIBCXX_HAS_TERMINAL_AVAILABLE OFF CACHE BOOL "")
 set(LIBCXX_INSTALL_LIBRARY ON CACHE BOOL "")
 set(LIBCXX_LIBC "llvm-libc" CACHE STRING "")
-set(LIBCXX_STATICALLY_LINK_ABI_IN_STATIC_LIBRARY ON CACHE BOOL "")
 set(LIBCXX_USE_COMPILER_RT ON CACHE BOOL "")
 
 # Configuration options for libcxxabi.
diff --git a/libcxx/cmake/caches/AndroidNDK.cmake b/libcxx/cmake/caches/AndroidNDK.cmake
index 298518781e9b7a..9d923328a75e44 100644
--- a/libcxx/cmake/caches/AndroidNDK.cmake
+++ b/libcxx/cmake/caches/AndroidNDK.cmake
@@ -19,7 +19,7 @@ set(LIBCXX_ABI_NAMESPACE __ndk1 CACHE STRING "")
 # output path (the script clobbers the binary). Turn off the linker script.
 set(LIBCXX_ENABLE_ABI_LINKER_SCRIPT OFF CACHE BOOL "")
 
-set(LIBCXX_STATICALLY_LINK_ABI_IN_SHARED_LIBRARY ON CACHE BOOL "")
+set(LIBCXX_CXX_ABI "merged-libcxxabi" CACHE STRING "")
 set(LIBCXXABI_ENABLE_SHARED OFF CACHE BOOL "")
 
 # Android uses its own unwinder library
diff --git a/libcxx/cmake/caches/Generic-merged.cmake b/libcxx/cmake/caches/Generic-merged.cmake
index 7ebb8026236ddf..e86f7ced358f43 100644
--- a/libcxx/cmake/caches/Generic-merged.cmake
+++ b/libcxx/cmake/caches/Generic-merged.cmake
@@ -1,8 +1,7 @@
 # Build a libc++ shared library, but merge libc++abi and libunwind into it.
 set(LIBCXX_ENABLE_SHARED ON CACHE BOOL "")
 set(LIBCXX_ENABLE_ABI_LINKER_SCRIPT OFF CACHE BOOL "")
-set(LIBCXX_ENABLE_STATIC_ABI_LIBRARY ON CACHE BOOL "")
-set(LIBCXX_STATICALLY_LINK_ABI_IN_SHARED_LIBRARY ON CACHE BOOL "")
+set(LIBCXX_CXX_ABI "merged-libcxxabi" CACHE STRING "")
 
 set(LIBCXXABI_ENABLE_SHARED OFF CACHE BOOL "")
 set(LIBCXXABI_ENABLE_STATIC_UNWINDER ON CACHE BOOL "")
diff --git a/libcxx/cmake/caches/MinGW.cmake b/libcxx/cmake/caches/MinGW.cmake
index 09e6ea5c1bab20..595eb90974c1df 100644
--- a/libcxx/cmake/caches/MinGW.cmake
+++ b/libcxx/cmake/caches/MinGW.cmake
@@ -1,7 +1,5 @@
-set(LIBCXX_CXX_ABI libcxxabi CACHE STRING "")
-
+set(LIBCXX_CXX_ABI "merged-libcxxabi" CACHE STRING "")
 set(LIBCXXABI_ENABLE_SHARED OFF CACHE BOOL "")
-set(LIBCXX_ENABLE_STATIC_ABI_LIBRARY ON CACHE BOOL "")
 
 set(LIBCXX_USE_COMPILER_RT ON CACHE BOOL "")
 set(LIBCXXABI_USE_COMPILER_RT ON CACHE BOOL "")
diff --git a/libcxx/cmake/caches/NVPTX.cmake b/libcxx/cmake/caches/NVPTX.cmake
index b6ec6c04ea700c..b7e93c8e9b1d0c 100644
--- a/libcxx/cmake/caches/NVPTX.cmake
+++ b/libcxx/cmake/caches/NVPTX.cmake
@@ -1,5 +1,5 @@
 # Configuration options for libcxx.
-set(LIBCXX_CXX_ABI libcxxabi CACHE STRING "")
+set(LIBCXX_CXX_ABI "merged-libcxxabi" CACHE STRING "")
 set(LIBCXX_ENABLE_EXCEPTIONS OFF CACHE BOOL "")
 set(LIBCXX_ENABLE_FILESYSTEM OFF CACHE BOOL "")
 set(LIBCXX_ENABLE_LOCALIZATION OFF CACHE BOOL "")
@@ -8,7 +8,6 @@ set(LIBCXX_ENABLE_NEW_DELETE_DEFINITIONS ON CACHE BOOL "")
 set(LIBCXX_ENABLE_RANDOM_DEVICE OFF CACHE BOOL "")
 set(LIBCXX_ENABLE_RTTI OFF CACHE BOOL "")
 set(LIBCXX_ENABLE_SHARED OFF CACHE BOOL "")
-set(LIBCXX_ENABLE_STATIC_ABI_LIBRARY ON CACHE BOOL "")
 set(LIBCXX_ENABLE_STATIC ON CACHE BOOL "")
 set(LIBCXX_ENABLE_THREADS OFF CACHE BOOL "")
 set(LIBCXX_ENABLE_UNICODE OFF CACHE BOOL "")
@@ -16,7 +15,6 @@ set(LIBCXX_ENABLE_WIDE_CHARACTERS OFF CACHE BOOL "")
 set(LIBCXX_HAS_TERMINAL_AVAILABLE OFF CACHE BOOL "")
 set(LIBCXX_INSTALL_LIBRARY ON CACHE BOOL "")
 set(LIBCXX_LIBC "llvm-libc" CACHE STRING "")
-set(LIBCXX_STATICALLY_LINK_ABI_IN_STATIC_LIBRARY ON CACHE BOOL "")
 set(LIBCXX_USE_COMPILER_RT ON CACHE BOOL "")
 
 # Configuration options for libcxxabi.
diff --git a/libcxx/docs/ReleaseNotes/20.rst b/libcxx/docs/ReleaseNotes/20.rst
index 9039c6f046445b..41390891195525 100644
--- a/libcxx/docs/ReleaseNotes/20.rst
+++ b/libcxx/docs/ReleaseNotes/20.rst
@@ -144,4 +144,8 @@ ABI Affecting Changes
 Build System Changes
 --------------------
 
-- TODO
+- The following CMake options to control how the ABI library is linked into libc++ have been deprecated:
+  ``LIBCXX_ENABLE_STATIC_ABI_LIBRARY``, ``LIBCXX_STATICALLY_LINK_ABI_IN_SHARED_LIBRARY``,
+  ``LIBCXX_STATICALLY_LINK_ABI_IN_STATIC_LIBRARY``. If you want to merge the objects of libc++abi into
+  libc++, please specify ``-DLIBCXX_CXX_ABI=merged-libcxxabi`` instead. These options will be
+  removed entirely in LLVM 21.
diff --git a/libcxx/docs/VendorDocumentation.rst b/libcxx/docs/VendorDocumentation.rst
index 959a28607d75dd..ac41dd1d9e0b5a 100644
--- a/libcxx/docs/VendorDocumentation.rst
+++ b/libcxx/docs/VendorDocumentation.rst
@@ -304,10 +304,20 @@ The following options allow building libc++ for a different ABI version.
   See ``include/__config`` for the list of ABI macros.
 
 .. option:: LIBCXX_CXX_ABI:STRING
+.. option:: LIBCXX_ABILIB_FOR_SHARED:STRING
+.. option:: LIBCXX_ABILIB_FOR_STATIC:STRING
 
   **Values**: ``none``, ``libcxxabi``, ``system-libcxxabi``, ``libcxxrt``, ``libstdc++``, ``libsupc++``, ``vcruntime``.
 
-  Select the ABI library to build libc++ against.
+  Select the ABI library to build libc++ against. This CMake option also supports "consumption specifiers", which
+  specify how the selected ABI library should be consumed by libc++. The supported specifiers are:
+    - ``shared``: The selected ABI library should be used as a shared library.
+    - ``static``: The selected ABI library should be used as a static library.
+    - ``merged``: The selected ABI library should be a static library whose object files will be merged directly into the produced libc++ library.
+
+  A consumption specifier is provided by appending it to the name of the library, such as ``LIBCXX_CXX_ABI=merged-libcxxabi``.
+  If no consumption specifier is provided, the libc++ shared library will default to using a shared ABI library, and the
+  libc++ static library will default to using a static ABI library.
 
 .. option:: LIBCXX_CXX_ABI_INCLUDE_PATHS:PATHS
 
@@ -318,17 +328,10 @@ The following options allow building libc++ for a different ABI version.
   Provide the path to the ABI library that libc++ should link against. This is only
   useful when linking against an out-of-tree ABI library.
 
-.. option:: LIBCXX_ENABLE_STATIC_ABI_LIBRARY:BOOL
-
-  **Default**: ``OFF``
-
-  If this option is enabled, libc++ will try and link the selected ABI library
-  statically.
-
 .. option:: LIBCXX_ENABLE_ABI_LINKER_SCRIPT:BOOL
 
   **Default**: ``ON`` by default on UNIX platforms other than Apple unless
-  'LIBCXX_ENABLE_STATIC_ABI_LIBRARY' is ON. Otherwise the default value is ``OFF``.
+  using the ``merged-libcxxabi`` ABI library. Otherwise the default value is ``OFF``.
 
   This option generate and installs a linker script as ``libc++.so`` which
   links the correct ABI library.
@@ -444,8 +447,7 @@ e.g. the ``mingw-w64-x86_64-clang`` package), together with CMake and ninja.
           -DCMAKE_CXX_COMPILER=clang++                                                \
           -DLLVM_ENABLE_LLD=ON                                                        \
           -DLLVM_ENABLE_RUNTIMES="libcxx;libcxxabi"                                   \
-          -DLIBCXXABI_ENABLE_SHARED=OFF                                               \
-          -DLIBCXX_ENABLE_STATIC_ABI_LIBRARY=ON
+          -DLIBCXX_CXX_ABI="merged-libcxxabi"
   > ninja -C build cxx
   > ninja -C build check-cxx
 
diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt
index 0ae031e5365aef..f555ac4b6ab86e 100644
--- a/libcxx/include/CMakeLists.txt
+++ b/libcxx/include/CMakeLists.txt
@@ -1070,7 +1070,7 @@ list(APPEND _all_includes "${LIBCXX_GENERATED_INCLUDE_DIR}/libcxx.imp")
 add_custom_target(generate-cxx-headers ALL DEPENDS ${_all_includes})
 
 add_library(cxx-headers INTERFACE)
-target_link_libraries(cxx-headers INTERFACE libcxx-libc-headers libcxx-abi-headers)
+target_link_libraries(cxx-headers INTERFACE libcxx-libc-headers)
 add_dependencies(cxx-headers generate-cxx-headers)
 # It's important that the arch directory be included first so that its header files
 # which interpose on the default include dir be included instead of the default ones.
diff --git a/libcxx/src/CMakeLists.txt b/libcxx/src/CMakeLists.txt
index cce8b8976f73c0..f08a64018b49ee 100644
--- a/libcxx/src/CMakeLists.txt
+++ b/libcxx/src/CMakeLists.txt
@@ -174,11 +174,13 @@ split_list(LIBCXX_COMPILE_FLAGS)
 split_list(LIBCXX_LINK_FLAGS)
 
 include(FindLibcCommonUtils)
+include(HandleLibC)
+include(HandleLibCXXABI)
 
 # Build the shared library.
 add_library(cxx_shared SHARED ${LIBCXX_SOURCES} ${LIBCXX_HEADERS})
 target_include_directories(cxx_shared PRIVATE ${CMAKE_CURRENT_SOURCE_DIR})
-target_link_libraries(cxx_shared PUBLIC cxx-headers libcxx-libc-shared
+target_link_libraries(cxx_shared PUBLIC cxx-headers libcxx-libc-shared libcxx-abi-shared
                                   PRIVATE ${LIBCXX_LIBRARIES}
                                   PRIVATE llvm-libc-common-utilities)
 set_target_properties(cxx_shared
@@ -203,17 +205,10 @@ if(ZOS)
   )
 endif()
 
-# Link against libc++abi
-if (LIBCXX_STATICALLY_LINK_ABI_IN_SHARED_LIBRARY)
-  target_link_libraries(cxx_shared PRIVATE libcxx-abi-shared-objects)
-else()
-  target_link_libraries(cxx_shared PUBLIC libcxx-abi-shared)
-endif()
-
 # Maybe force some symbols to be weak, not weak or not exported.
 # TODO: This shouldn't depend on the platform, and ideally it should be done in the sources.
-if (APPLE AND LIBCXX_CXX_ABI MATCHES "libcxxabi$"
-          AND NOT LIBCXX_STATICALLY_LINK_ABI_IN_SHARED_LIBRARY)
+if (APPLE AND LIBCXX_ABILIB_FOR_SHARED MATCHES "(.*)libcxxabi$"
+          AND NOT LIBCXX_ABILIB_FOR_SHARED MATCHES "^merged-(.+)")
   target_link_libraries(cxx_shared PRIVATE
     "-Wl,-force_symbols_not_weak_list,${CMAKE_CURRENT_SOURCE_DIR}/../lib/notweak.exp"
     "-Wl,-force_symbols_weak_list,${CMAKE_CURRENT_SOURCE_DIR}/../lib/weak.exp")
@@ -301,10 +296,6 @@ endif()
 if (LIBCXX_ENABLE_STATIC)
   list(APPEND LIBCXX_BUILD_TARGETS "cxx_static")
 endif()
-# Attempt to merge the libc++.a archive and the ABI library archive into one.
-if (LIBCXX_STATICALLY_LINK_ABI_IN_STATIC_LIBRARY)
-  target_link_libraries(cxx_static PRIVATE libcxx-abi-static-objects)
-endif()
 
 # Add a meta-target for both libraries.
 add_custom_target(cxx DEPENDS ${LIBCXX_BUILD_TARGETS})
diff --git a/libcxx/utils/ci/run-buildbot b/libcxx/utils/ci/run-buildbot
index 3df7b00a8aa09d..0873c92e75d03b 100755
--- a/libcxx/utils/ci/run-buildbot
+++ b/libcxx/utils/ci/run-buildbot
@@ -143,7 +143,6 @@ function generate-cmake-base() {
 function generate-cmake() {
     generate-cmake-base \
           -DLLVM_ENABLE_RUNTIMES="libcxx;libcxxabi;libunwind" \
-          -DLIBCXX_CXX_ABI=libcxxabi \
           "${@}"
 }
 
@@ -158,7 +157,6 @@ function generate-cmake-libcxx-win() {
 function generate-cmake-android() {
     generate-cmake-base \
           -DLLVM_ENABLE_RUNTIMES="libcxx;libcxxabi" \
-          -DLIBCXX_CXX_ABI=libcxxabi \
           "${@}"
 }
 
@@ -560,7 +558,6 @@ apple-system-hardened)
         -DCMAKE_INSTALL_PREFIX="${INSTALL_DIR}/cxx" \
         -DLLVM_LIT_ARGS="-sv --xunit-xml-output test-results.xml --timeout=1500 --time-tests" \
         -DLLVM_ENABLE_RUNTIMES="libcxx;libcxxabi" \
-        -DLIBCXX_CXX_ABI=libcxxabi \
         -C "${MONOREPO_ROOT}/libcxx/cmake/caches/Apple.cmake" \
         -DLIBCXX_TEST_CONFIG="apple-libc++-system.cfg.in" \
         -DLIBCXXABI_TEST_CONFIG="apple-libc++abi-system.cfg.in" \
@@ -606,7 +603,6 @@ apple-system)
         -DCMAKE_INSTALL_PREFIX="${INSTALL_DIR}/cxx" \
         -DLLVM_LIT_ARGS="-sv --xunit-xml-output test-results.xml --timeout=1500 --time-tests" \
         -DLLVM_ENABLE_RUNTIMES="libcxx;libcxxabi" \
-        -DLIBCXX_CXX_ABI=libcxxabi \
         -C "${MONOREPO_ROOT}/libcxx/cmake/caches/Apple.cmake" \
         -DLIBCXX_TEST_CONFIG="apple-libc++-system.cfg.in" \
         -DLIBCXXABI_TEST_CONFIG="apple-libc++abi-system.cfg.in" \
diff --git a/llvm/docs/HowToBuildWindowsItaniumPrograms.rst b/llvm/docs/HowToBuildWindowsItaniumPrograms.rst
index 48ca7b25b11ef8..0c972330bc4fbf 100644
--- a/llvm/docs/HowToBuildWindowsItaniumPrograms.rst
+++ b/llvm/docs/HowToBuildWindowsItaniumPrograms.rst
@@ -110,13 +110,14 @@ We use the MS runtime.
 
 The CMake files will need to be edited to prevent them adding GNU specific libraries to the link line.
 
-Building libc++abi:
--------------------
+Building libc++ and libc++abi:
+------------------------------
 
+* ``-DLIBCXX_ENABLE_SHARED=ON``
+* ``-DLIBCXX_ENABLE_STATIC=OFF``
+* ``-DLIBCXX_CXX_ABI="merged-libcxxabi"``
 * ``-DLIBCXXABI_ENABLE_SHARED=OFF``
 * ``-DLIBCXXABI_ENABLE_STATIC=ON``
-* ``-DLIBCXX_ENABLE_SHARED=ON'``
-* ``-DLIBCXX_ENABLE_STATIC_ABI_LIBRARY=ON``
 
 To break the symbol dependency between libc++abi and libc++ we
 build libc++abi as a static library and then statically link it
@@ -125,18 +126,6 @@ to ensure that the visibility macros (which expand to dllexport/import)
 are expanded as they will be needed when creating the final libc++
 DLL later, see: https://reviews.llvm.org/D90021.
 
-* ``-DLIBCXXABI_LIBCXX_INCLUDES=<path to libcxx>/include``
-
-Where to find the libc++ headers
-
-Building libc++:
-----------------
-
-* ``-DLIBCXX_ENABLE_SHARED=ON``
-* ``-DLIBCXX_ENABLE_STATIC=OFF``
-
-We build libc++ as a DLL and statically link libc++abi into it.
-
 * ``-DLIBCXX_INSTALL_HEADERS=ON``
 
 Install the headers.
@@ -149,13 +138,6 @@ We use the MS runtime.
 
 Windows Itanium does not offer a POSIX-like layer over WIN32.
 
-* ``-DLIBCXX_ENABLE_STATIC_ABI_LIBRARY=ON``
-* ``-DLIBCXX_CXX_ABI=libcxxabi``
-* ``-DLIBCXX_CXX_ABI_INCLUDE_PATHS=<libcxxabi src path>/include``
-* ``-DLIBCXX_CXX_ABI_LIBRARY_PATH=<libcxxabi build path>/lib``
-
-Use the static libc++abi library built earlier.
-
 * ``-DLIBCXX_NO_VCRUNTIME=ON``
 
 Remove any dependency on the VC runtime - we need libc++abi to supply the C++ runtime.

>From d149f83a396f100d09e08f02fab9bd79735a6d84 Mon Sep 17 00:00:00 2001
From: Louis Dionne <ldionne.2 at gmail.com>
Date: Mon, 21 Oct 2024 10:43:38 -0400
Subject: [PATCH 2/4] Fix indentation in documentation

---
 libcxx/docs/VendorDocumentation.rst | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/libcxx/docs/VendorDocumentation.rst b/libcxx/docs/VendorDocumentation.rst
index ac41dd1d9e0b5a..e46110e79f58c4 100644
--- a/libcxx/docs/VendorDocumentation.rst
+++ b/libcxx/docs/VendorDocumentation.rst
@@ -311,9 +311,9 @@ The following options allow building libc++ for a different ABI version.
 
   Select the ABI library to build libc++ against. This CMake option also supports "consumption specifiers", which
   specify how the selected ABI library should be consumed by libc++. The supported specifiers are:
-    - ``shared``: The selected ABI library should be used as a shared library.
-    - ``static``: The selected ABI library should be used as a static library.
-    - ``merged``: The selected ABI library should be a static library whose object files will be merged directly into the produced libc++ library.
+  - ``shared``: The selected ABI library should be used as a shared library.
+  - ``static``: The selected ABI library should be used as a static library.
+  - ``merged``: The selected ABI library should be a static library whose object files will be merged directly into the produced libc++ library.
 
   A consumption specifier is provided by appending it to the name of the library, such as ``LIBCXX_CXX_ABI=merged-libcxxabi``.
   If no consumption specifier is provided, the libc++ shared library will default to using a shared ABI library, and the

>From 81b197b036a1ddc62f7fc8bcf196a4cbf6ba0f7a Mon Sep 17 00:00:00 2001
From: Louis Dionne <ldionne.2 at gmail.com>
Date: Mon, 21 Oct 2024 11:29:54 -0400
Subject: [PATCH 3/4] Simplify and fix dependencies for headers

---
 libcxx/cmake/Modules/HandleLibCXXABI.cmake | 71 +++++++++++-----------
 1 file changed, 36 insertions(+), 35 deletions(-)

diff --git a/libcxx/cmake/Modules/HandleLibCXXABI.cmake b/libcxx/cmake/Modules/HandleLibCXXABI.cmake
index fa4b761e818021..689306d46587f1 100644
--- a/libcxx/cmake/Modules/HandleLibCXXABI.cmake
+++ b/libcxx/cmake/Modules/HandleLibCXXABI.cmake
@@ -20,7 +20,7 @@ include(GNUInstallDirs)
 # since we would end up also adding the system-provided C++ Standard Library to
 # the search path. Instead, what we do is copy just the ABI library headers to
 # a private directory and add just that path when we build libc++.
-function(import_private_headers target include_dirs headers)
+function(_import_private_headers target include_dirs headers)
   if (NOT ${include_dirs})
     message(FATAL_ERROR "Missing include paths for the selected ABI library!")
   endif()
@@ -58,15 +58,14 @@ function(import_private_headers target include_dirs headers)
   target_include_directories(${target} INTERFACE "${LIBCXX_BINARY_DIR}/private-abi-headers/${target}")
 endfunction()
 
-# This function creates an imported static library named <target>.
-# It imports a library named <name> searched at the given <path>.
-function(import_static_library target path name)
-  add_library(${target} STATIC IMPORTED GLOBAL)
-  find_library(file
-    NAMES "${CMAKE_STATIC_LIBRARY_PREFIX}${name}${CMAKE_STATIC_LIBRARY_SUFFIX}"
-    PATHS "${path}"
-    NO_CACHE)
-  set_target_properties(${target} PROPERTIES IMPORTED_LOCATION "${file}")
+# This function adds linker flags to a <target> appropriate for merging the object
+# files of another static <library> into whoever links against <target>.
+function(_merge_static_library target library)
+  if (APPLE)
+    target_link_options(${target} INTERFACE "-Wl,-force_load" "${library}")
+  else()
+    target_link_options(${target} INTERFACE "-Wl,--whole-archive" "-Wl,-Bstatic" "${library}" "-Wl,-Bdynamic" "-Wl,--no-whole-archive")
+  endif()
 endfunction()
 
 # This function creates a library target for linking against an external ABI library.
@@ -75,7 +74,7 @@ endfunction()
 # <name>: The name of the library file to search for (e.g. c++abi, stdc++, cxxrt, supc++)
 # <type>: Whether to set up a static or a shared library (e.g. SHARED or STATIC)
 # <merged>: Whether to include the ABI library's object files directly into libc++. Only makes sense for a static ABI library.
-function(import_external_abi_library target name type merged)
+function(_import_external_abi_library target name type merged)
   if (${merged} AND "${type}" STREQUAL "SHARED")
     message(FATAL_ERROR "Can't import an external ABI library for merging when requesting a shared ABI library.")
   endif()
@@ -83,21 +82,23 @@ function(import_external_abi_library target name type merged)
   if ("${type}" STREQUAL "SHARED")
     add_library(${target} INTERFACE IMPORTED GLOBAL)
     set_target_properties(${target} PROPERTIES IMPORTED_LIBNAME "${name}")
+
   elseif ("${type}" STREQUAL "STATIC")
+    add_library(${target}-imported STATIC IMPORTED GLOBAL)
+    find_library(file
+      NAMES "${CMAKE_STATIC_LIBRARY_PREFIX}${name}${CMAKE_STATIC_LIBRARY_SUFFIX}"
+      PATHS "${LIBCXX_CXX_ABI_LIBRARY_PATH}"
+      NO_CACHE)
+    if (NOT "${file}")
+      message(FATAL_ERROR "Failed to find static library ${name} at path ${LIBCXX_CXX_ABI_LIBRARY_PATH}")
+    endif()
+    set_target_properties(${target}-imported PROPERTIES IMPORTED_LOCATION "${file}")
+
+    add_library(${target} INTERFACE)
     if (${merged})
-      import_static_library(${target}-impl "${LIBCXX_CXX_ABI_LIBRARY_PATH}" ${name})
-      add_library(${target} INTERFACE)
-      if (APPLE)
-        target_link_options(${target} INTERFACE
-          "-Wl,-force_load" "$<TARGET_PROPERTY:${target}-impl,IMPORTED_LOCATION>")
-      else()
-        target_link_options(${target} INTERFACE
-          "-Wl,--whole-archive" "-Wl,-Bstatic"
-          "$<TARGET_PROPERTY:${target}-impl,IMPORTED_LOCATION>"
-          "-Wl,-Bdynamic" "-Wl,--no-whole-archive")
-      endif()
+      _merge_static_library(${target} "$<TARGET_PROPERTY:${target}-imported,IMPORTED_LOCATION>")
     else()
-      import_static_library(${target} "${LIBCXX_CXX_ABI_LIBRARY_PATH}" ${name})
+      target_link_libraries(${target} INTERFACE ${target}-imported)
     endif()
   endif()
 endfunction()
@@ -133,22 +134,22 @@ function(setup_abi_library abi_target linked_into input)
 
   # Link against a system-provided libstdc++
   if ("${stdlib}" STREQUAL "libstdc++")
-    import_external_abi_library(${abi_target} stdc++ ${search_type} ${merged})
-    import_private_headers(${abi_target} "${LIBCXX_CXX_ABI_INCLUDE_PATHS}"
+    _import_external_abi_library(${abi_target} stdc++ ${search_type} ${merged})
+    _import_private_headers(${abi_target} "${LIBCXX_CXX_ABI_INCLUDE_PATHS}"
       "cxxabi.h;bits/c++config.h;bits/os_defines.h;bits/cpu_defines.h;bits/cxxabi_tweaks.h;bits/cxxabi_forced.h")
     target_compile_definitions(${abi_target} INTERFACE "-DLIBSTDCXX" "-D__GLIBCXX__")
 
   # Link against a system-provided libsupc++
   elseif ("${stdlib}" STREQUAL "libsupc++")
-    import_external_abi_library(${abi_target} supc++ ${search_type} ${merged})
-    import_private_headers(${abi_target} "${LIBCXX_CXX_ABI_INCLUDE_PATHS}"
+    _import_external_abi_library(${abi_target} supc++ ${search_type} ${merged})
+    _import_private_headers(${abi_target} "${LIBCXX_CXX_ABI_INCLUDE_PATHS}"
       "cxxabi.h;bits/c++config.h;bits/os_defines.h;bits/cpu_defines.h;bits/cxxabi_tweaks.h;bits/cxxabi_forced.h")
     target_compile_definitions(${abi_target} INTERFACE "-D__GLIBCXX__")
 
   # Link against a system-provided libcxxrt
   elseif ("${stdlib}" STREQUAL "libcxxrt")
-    import_external_abi_library(${abi_target} cxxrt ${search_type} ${merged})
-    import_private_headers(${abi_target} "${LIBCXX_CXX_ABI_INCLUDE_PATHS}"
+    _import_external_abi_library(${abi_target} cxxrt ${search_type} ${merged})
+    _import_private_headers(${abi_target} "${LIBCXX_CXX_ABI_INCLUDE_PATHS}"
       "cxxabi.h;unwind.h;unwind-arm.h;unwind-itanium.h")
     target_compile_definitions(${abi_target} INTERFACE "-DLIBCXXRT")
 
@@ -159,18 +160,18 @@ function(setup_abi_library abi_target linked_into input)
 
   # Link against a system-provided libc++abi
   elseif ("${stdlib}" STREQUAL "system-libcxxabi")
-    import_external_abi_library(${abi_target} c++abi ${search_type} ${merged})
-    import_private_headers(${abi_target} "${LIBCXX_CXX_ABI_INCLUDE_PATHS}"
+    _import_external_abi_library(${abi_target} c++abi ${search_type} ${merged})
+    _import_private_headers(${abi_target} "${LIBCXX_CXX_ABI_INCLUDE_PATHS}"
       "cxxabi.h;__cxxabi_config.h")
     target_compile_definitions(${abi_target} INTERFACE "-DLIBCXX_BUILDING_LIBCXXABI")
 
   # Link against the in-tree libc++abi.
   elseif ("${stdlib}" STREQUAL "libcxxabi")
     if (${merged})
-      get_target_property(_outdir cxxabi_static ARCHIVE_OUTPUT_DIRECTORY)
-      get_target_property(_outname cxxabi_static OUTPUT_NAME)
-      set(LIBCXX_CXX_ABI_LIBRARY_PATH "${_outdir}")
-      import_external_abi_library(${abi_target} "${_outname}" STATIC TRUE)
+      add_library(${abi_target} INTERFACE)
+      _merge_static_library(${abi_target}
+        "$<TARGET_PROPERTY:cxxabi_static,LIBRARY_OUTPUT_DIRECTORY>/${CMAKE_STATIC_LIBRARY_PREFIX}$<TARGET_PROPERTY:cxxabi_static,OUTPUT_NAME>${CMAKE_STATIC_LIBRARY_SUFFIX}")
+      target_link_libraries(${abi_target} INTERFACE cxxabi-headers)
     else()
       string(TOLOWER "${search_type}" type)
       add_library(${abi_target} INTERFACE)

>From 614968a1557b95e84daba431703c34adacafa2ea Mon Sep 17 00:00:00 2001
From: Louis Dionne <ldionne.2 at gmail.com>
Date: Tue, 22 Oct 2024 16:02:45 -0400
Subject: [PATCH 4/4] Fix missing dependency on cxxabi_static

---
 libcxx/cmake/Modules/HandleLibCXXABI.cmake | 1 +
 1 file changed, 1 insertion(+)

diff --git a/libcxx/cmake/Modules/HandleLibCXXABI.cmake b/libcxx/cmake/Modules/HandleLibCXXABI.cmake
index 689306d46587f1..4a6cfe0cf84fdc 100644
--- a/libcxx/cmake/Modules/HandleLibCXXABI.cmake
+++ b/libcxx/cmake/Modules/HandleLibCXXABI.cmake
@@ -172,6 +172,7 @@ function(setup_abi_library abi_target linked_into input)
       _merge_static_library(${abi_target}
         "$<TARGET_PROPERTY:cxxabi_static,LIBRARY_OUTPUT_DIRECTORY>/${CMAKE_STATIC_LIBRARY_PREFIX}$<TARGET_PROPERTY:cxxabi_static,OUTPUT_NAME>${CMAKE_STATIC_LIBRARY_SUFFIX}")
       target_link_libraries(${abi_target} INTERFACE cxxabi-headers)
+      add_dependencies(${abi_target} cxxabi_static)
     else()
       string(TOLOWER "${search_type}" type)
       add_library(${abi_target} INTERFACE)



More information about the cfe-commits mailing list