[PATCH] C++ : Add the -stdlib= option.

Jason Merrill jason@redhat.com
Wed Dec 9 18:44:21 GMT 2020


On 11/11/20 3:58 PM, Iain Sandoe wrote:
> resending - the first & second attempt didn’t seem to make it to 
> gcc-patches.
> 
> Hi
> 
> This option allows the user to specify alternate C++ runtime libraries,
> for example when a platform uses libc++ as the installed C++ runtime.
> 
> It is the same spelling as a clang option that allows that to use 
> libstdc++.
> 
> I have had this patch for some time now (more than a year) on Darwin
> branches.
> 
> For Darwin [>=11] (and I expect modern FreeBSD) the fact that the installed
> C++ runtime is libc++ means conflicts can and do occur when using G++.
> 
> I expect that the facility will also be useful for folks who regularly 
> try to
> ensure that GCC and clang stay compatible, it is a credit to that effort 
> that
> the replacement is pretty much “drop in”.
> 
> Testing:
> 
> The patch applies without regression on *darwin* and x86_64-linux-gnu.
> 
> That doesn’t say much about whether it does what’s intended, of course,
> and testing in-tree is not a viable option (it would need a lot of work, 
> not
> to mention the fact that it depends on an external source base).  So I’ve
> tested this quite extensively on x86 Darwin and Linux.
> 
> It’s a lot easier to use an LLVM branch >= 9 for this since there is a
> missing __cxa symbol before that (I originally used LLVM-7 for ‘reasons’).
> Since coroutines was committed to GCC we have a <coroutine> header
> where the libc++ implementation is still using the <experimental/coroutine>
> version, so that one needs to  account for this.
> 
> Here’s an LLVM-9 tree with an added <coroutine> header (as an example)
> https://github.com/iains/llvm-project/tree/9.0.1-gcc-stdlib
> (in case someone wants to try this out in the near future; I don’t think 
> that
> LLVM-10 will be much different, at least the coroutine header is unchanged
> there)
> 
> I’ve used this ‘in anger’ on Darwin to build a toolset which includes a 
> number
> of C++ heavy applications (e.g. LLVM, cmake, etc) and it allowed some of
> these to work effectively where it had not been possible before.
> 
> One can also do an “installed test” of g++
> for that there are (a relatively modest number of) test fails.
> AFAICT, there is nothing significant there - some tests fail because the 
> output
> isn’t expecting to see libc++ __1 inline namespace, some fail because 
> libc++
> (as per current branches) doesn’t allow use with GCC + std=c++98, some
> are warning diagnostics etc.
> 
> [how compatible libc++ is, is somewhat independent of the patch itself; but
> it seems “very compatible” is a starting assessment].
> 
> phew… description longer than patch, it seems.
> 
> OK for master?
> thanks
> Iain
> 
> —— commit message
> 
> This option allows the user to specify alternate C++ runtime libraries,
> for example when a platform uses libc++ as the installed C++ runtime.
> 
> We introduce the command line option: -stdlib= which is the user-facing
> mechanism to select the C++ runtime to be used when compiling and linking
> code.  This is the same option spelling as that used by clang to allow the
> use of libstdc++.
> 
> The availability (and thus function) of the option are a configure-time
> choice using the configuration control:
> --with-gxx-libcxx-include-dir=
> 
> Specification of the path for the libc++ headers, enables the -stdlib=
> option (using the path as given), default values are set when the path
> is unconfigured.
> 
> If --with-gxx-libcxx-include-dir is given together with --with-sysroot=,
> then we test to see if the include path starts with the sysroot and, if so,
> record the sysroot-relative component as the local path.  At runtime, we
> prepend the sysroot that is actually active.
> 
> At link time, we use the C++ runtime in force and (if that is libc++) also
> append the libc++abi ABI library. As for other cases, if a target sets the
> name pointer for the ABI library to NULL the G++ driver will omit it from
> the link line.
> 
> gcc/ChangeLog:
> 
>      * configure.ac: Add gxx-libcxx-include-dir handled
>      in the same way as the regular cxx header directory.
>      * Makefile.in: Regenerated.
>      * config.in: Likewise.
>      * configure: Likewise.
>      * cppdefault.c: Pick up libc++ headers if the option
>      is enabled.
>      * incpath.c (add_standard_paths): Allow for multiple
>      c++ header include path variants.
>      * doc/invoke.texi: Document the -stdlib= option.
> 
> gcc/c-family/ChangeLog:
> 
>      * c.opt: Add -stdlib= option and enumerations for
>      libstdc++ and libc++.
> 
> gcc/cp/ChangeLog:
> 
>      * g++spec.c (LIBCXX, LIBCXX_PROFILE, LIBCXX_STATIC): New.
>      (LIBCXXABI, LIBCXXABI_PROFILE, LIBCXXABI_STATIC): New.
>      (lang_specific_driver): Allow selection amongst multiple
>      c++ libraries to be added to the link command.
> ---
> gcc/Makefile.in     |  6 +++++
> gcc/c-family/c.opt  | 14 +++++++++++
> gcc/config.in       |  6 +++++
> gcc/configure       | 57 +++++++++++++++++++++++++++++++++++++++++++--
> gcc/configure.ac    | 44 ++++++++++++++++++++++++++++++++++
> gcc/cp/g++spec.c    | 53 ++++++++++++++++++++++++++++++++++++++---
> gcc/cppdefault.c    |  5 ++++
> gcc/doc/invoke.texi | 11 +++++++++
> gcc/incpath.c       |  6 +++--
> 9 files changed, 195 insertions(+), 7 deletions(-)
> 
> diff --git a/gcc/c-family/c.opt b/gcc/c-family/c.opt
> index fe16357db85..5fada472db1 100644
> --- a/gcc/c-family/c.opt
> +++ b/gcc/c-family/c.opt
> @@ -2269,6 +2269,20 @@ std=iso9899:2018
> C ObjC Alias(std=c17)
> Conform to the ISO 2017 C standard (published in 2018).
> 
> +stdlib=
> +Driver C++ ObjC++ Common Report Condition(ENABLE_STDLIB_OPTION) 
> Var(flag_stdlib_kind) Joined Enum(stdlib_kind) RejectNegative Init(1)
> +-stdlib=[libstdc++|libc++]    The standard library to be used for C++ 
> headers
> +and runtime.
> +
> +Enum
> +Name(stdlib_kind) Type(int)
> +
> +EnumValue
> +Enum(stdlib_kind) String(libstdc++) Value(1)
> +
> +EnumValue
> +Enum(stdlib_kind) String(libc++) Value(2)
> +
> traditional
> Driver
> 
> diff --git a/gcc/configure.ac b/gcc/configure.ac
> index 73034bb902b..56b32de4c68 100644
> --- a/gcc/configure.ac
> +++ b/gcc/configure.ac
> @@ -228,6 +228,48 @@ elif test "${with_sysroot+set}" = set; then
>   fi
> fi
> 
> +# Configuration for an alternate set of C++ headers.
> +gcc_gxx_libcxx_include_dir=
> +# Specify the alternate g++ header file directory
> +AC_ARG_WITH(gxx-libcxx-include-dir,
> +[AS_HELP_STRING([--with-gxx-libcxx-include-dir=DIR],
> +                [specifies directory to find libc++ header files])],
> +[case "${withval}" in
> +yes)    AC_MSG_ERROR(bad value ${withval} given for libc++ include 
> directory) ;;
> +no)    ;;
> +*)    gcc_gxx_libcxx_include_dir=$with_gxx_libcxx_include_dir ;;
> +esac])
> +
> +# If both --with-sysroot and --with-gxx-libcxx-include-dir are passed, we
> +# check to see if the latter starts with the former and, upon success, 
> compute
> +# gcc_gxx_libcxx_include_dir as relative to the sysroot.
> +gcc_gxx_libcxx_include_dir_add_sysroot=0
> +
> +if test x${gcc_gxx_libcxx_include_dir} != x; then
> +  AC_DEFINE(ENABLE_STDLIB_OPTION, 1,
> +            [Define if the -stdlib= option should be enabled.])
> +else
> +  AC_DEFINE(ENABLE_STDLIB_OPTION, 0)
> +fi
> +# ??? This logic must match 
> libstdc++-v3/acinclude.m4:GLIBCXX_EXPORT_INSTALL_INFO.
> +if test x${gcc_gxx_libcxx_include_dir} = x; then
> +  if test x${enable_version_specific_runtime_libs} = xyes; then
> +    gcc_gxx_libcxx_include_dir='${libsubdir}/libc++_include/c++/v1'
> +  else
> +    libcxx_incdir='libc++_include/c++/$(version)/v1'
> +    if test x$host != x$target; then
> +       libcxx_incdir="$target_alias/$libcxx_incdir"
> +    fi
> +    
> gcc_gxx_libcxx_include_dir="\$(libsubdir)/\$(libsubdir_to_prefix)$libcxx_incdir" 
> 
> +  fi
> +elif test "${with_sysroot+set}" = set; then
> +  gcc_gxx_libcxx_without_sysroot=`expr "${gcc_gxx_libcxx_include_dir}" 
> : "${with_sysroot}"'\(.*\)'`
> +  if test "${gcc_gxx_libcxx_without_sysroot}"; then
> +    gcc_gxx_libcxx_include_dir="${gcc_gxx_libcxx_without_sysroot}"
> +    gcc_gxx_libcxx_include_dir_add_sysroot=1
> +  fi
> +fi
> +
> AC_ARG_WITH(cpp_install_dir,
> [AC_HELP_STRING([--with-cpp-install-dir=DIR],
>                 [install the user visible C preprocessor in DIR
> @@ -6872,6 +6914,8 @@ AC_SUBST(float_h_file)
> AC_SUBST(gcc_config_arguments)
> AC_SUBST(gcc_gxx_include_dir)
> AC_SUBST(gcc_gxx_include_dir_add_sysroot)
> +AC_SUBST(gcc_gxx_libcxx_include_dir)
> +AC_SUBST(gcc_gxx_libcxx_include_dir_add_sysroot)
> AC_SUBST(host_exeext)
> AC_SUBST(host_xm_file_list)
> AC_SUBST(host_xm_include_list)
> diff --git a/gcc/cp/g++spec.c b/gcc/cp/g++spec.c
> index 0ab63bcd211..a2f5c7d3cc7 100644
> --- a/gcc/cp/g++spec.c
> +++ b/gcc/cp/g++spec.c
> @@ -55,6 +55,26 @@ along with GCC; see the file COPYING3.  If not see
> #define LIBSTDCXX_STATIC NULL
> #endif
> 
> +#ifndef LIBCXX
> +#define LIBCXX "c++"
> +#endif
> +#ifndef LIBCXX_PROFILE
> +#define LIBCXX_PROFILE LIBCXX
> +#endif
> +#ifndef LIBCXX_STATIC
> +#define LIBCXX_STATIC NULL
> +#endif
> +
> +#ifndef LIBCXXABI
> +#define LIBCXXABI "c++abi"
> +#endif
> +#ifndef LIBCXXABI_PROFILE
> +#define LIBCXXABI_PROFILE LIBCXXABI
> +#endif
> +#ifndef LIBCXXABI_STATIC
> +#define LIBCXXABI_STATIC NULL
> +#endif
> +
> void
> lang_specific_driver (struct cl_decoded_option **in_decoded_options,
>                unsigned int *in_decoded_options_count,
> @@ -72,6 +92,11 @@ lang_specific_driver (struct cl_decoded_option 
> **in_decoded_options,
>      2  means libstdc++ is needed and should be linked statically.  */
>   int library = 0;
> 
> +  /* Which standard library to link.
> +     1 = libstdc++
> +     2 = libc++.  */
> +  int which_library = 1;

Even if this variable is an int, let's introduce an enum for comparing 
it to specific values.

> +
>   /* The number of arguments being added to what's in argv, other than
>      libraries.  We use this to track the number of times we've inserted
>      -xc++/-xnone.  */
> @@ -208,6 +233,10 @@ lang_specific_driver (struct cl_decoded_option 
> **in_decoded_options,
>        args[i] |= SKIPOPT;
>        break;
> 
> +    case OPT_stdlib_:
> +      which_library = decoded_options[i].value;
> +      break;
> +
>      case OPT_SPECIAL_input_file:
>        {
>          int len;
> @@ -264,6 +293,8 @@ lang_specific_driver (struct cl_decoded_option 
> **in_decoded_options,
> 
>   /* Add one for shared_libgcc or extra static library.  */
>   num_args = argc + added + need_math + (library > 0) * 4 + 1;
> +  if (which_library > 1 && LIBCXXABI != NULL)
> +    num_args += 4;

Why does libc++ need more args than libstdc++?  Please add a comment.

>   new_decoded_options = XNEWVEC (struct cl_decoded_option, num_args);
> 
>   i = 0;
> @@ -343,9 +374,25 @@ lang_specific_driver (struct cl_decoded_option 
> **in_decoded_options,
>        j++;
>      }
> #endif
> -      generate_option (OPT_l,
> -               saw_profile_flag ? LIBSTDCXX_PROFILE : LIBSTDCXX, 1,
> -               CL_DRIVER, &new_decoded_options[j]);
> +      if (which_library == 2)
> +    {
> +      generate_option (OPT_l,
> +             saw_profile_flag ? LIBCXX_PROFILE : LIBCXX, 1,
> +             CL_DRIVER, &new_decoded_options[j]);
> +      if (LIBCXXABI != NULL)
> +        {
> +          j++;
> +          added_libraries++;
> +          generate_option (OPT_l,
> +                   saw_profile_flag ? LIBCXXABI_PROFILE
> +                        : LIBCXXABI, 1,
> +                   CL_DRIVER, &new_decoded_options[j]);
> +        }
> +    }
> +      else
> +    generate_option (OPT_l,
> +             saw_profile_flag ? LIBSTDCXX_PROFILE : LIBSTDCXX, 1,
> +             CL_DRIVER, &new_decoded_options[j]);
>       added_libraries++;
>       j++;
>       /* Add target-dependent static library, if necessary.  */
> diff --git a/gcc/cppdefault.c b/gcc/cppdefault.c
> index af38cc494ea..eb6f94162cd 100644
> --- a/gcc/cppdefault.c
> +++ b/gcc/cppdefault.c
> @@ -55,6 +55,11 @@ const struct default_include cpp_include_defaults[]
>     { GPLUSPLUS_BACKWARD_INCLUDE_DIR, "G++", 1, 1,
>       GPLUSPLUS_INCLUDE_DIR_ADD_SYSROOT, 0 },
> #endif
> +#ifdef GPLUSPLUS_LIBCXX_INCLUDE_DIR
> +    /* Pick up libc++ include files, if we have -stdlib=libc++.  */
> +    { GPLUSPLUS_LIBCXX_INCLUDE_DIR, "G++", 2, 1,

If you're going to give cplusplus a non-boolean value, the comment in 
cppdefault.h needs to reflect that.

> +      GPLUSPLUS_LIBCXX_INCLUDE_DIR_ADD_SYSROOT, 0 },
> +#endif
> #ifdef GCC_INCLUDE_DIR
>     /* This is the dir for gcc's private headers.  */
>     { GCC_INCLUDE_DIR, "GCC", 0, 0, 0, 0 },
> diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi
> index 8d0d2136831..a14a25c330f 100644
> --- a/gcc/doc/invoke.texi
> +++ b/gcc/doc/invoke.texi
> @@ -233,6 +233,7 @@ in the following sections.
> -fvisibility-inlines-hidden @gol
> -fvisibility-ms-compat @gol
> -fext-numeric-literals @gol
> +-stdlib=@var{libstdc++,libc++} @gol
> -Wabi-tag  -Wcatch-value  -Wcatch-value=@var{n} @gol
> -Wno-class-conversion  -Wclass-memaccess @gol
> -Wcomma-subscript  -Wconditionally-supported @gol
> @@ -3285,6 +3286,16 @@ for ISO C++11 onwards (@option{-std=c++11}, ...).
> Do not search for header files in the standard directories specific to
> C++, but do still search the other standard directories.  (This option
> is used when building the C++ library.)
> +
> +@item -stdlib=@var{libstdc++,libc++}
> +@opindex stdlib
> +When G++ is configured to support this option, it allows specification of
> +alternate C++ runtime libraries.  Two options are available: 
> @var{libstdc++}
> +(the default, native C++ runtime for G++) and @var{libc++} which is the
> +C++ runtime installed on some operating systems (e.g. Darwin versions from
> +Darwin11 onwards).  The option switches G++ to use the headers from the
> +specified library and to emit @code{-lstdc++} or @code{-lc++} 
> respectively,
> +when a C++ runtime is required for linking.
> @end table
> 
> In addition, these warning options have meanings only for C++ programs:
> diff --git a/gcc/incpath.c b/gcc/incpath.c
> index 8437939bf1e..14593a1f4c3 100644
> --- a/gcc/incpath.c
> +++ b/gcc/incpath.c
> @@ -137,7 +137,8 @@ add_standard_paths (const char *sysroot, const char 
> *iprefix,
>       IPREFIX and search them first.  */
>       for (p = cpp_include_defaults; p->fname; p++)
>      {
> -      if (!p->cplusplus || cxx_stdinc)
> +      if (p->cplusplus == 0
> +          || (cxx_stdinc && (p->cplusplus == flag_stdlib_kind)))
>          {
>            /* Should we be translating sysrooted dirs too?  Assume
>           that iprefix and sysroot are mutually exclusive, for
> @@ -168,7 +169,8 @@ add_standard_paths (const char *sysroot, const char 
> *iprefix,
> 
>   for (p = cpp_include_defaults; p->fname; p++)
>     {
> -      if (!p->cplusplus || cxx_stdinc)
> +      if (p->cplusplus == 0
> +      || (cxx_stdinc && (p->cplusplus == flag_stdlib_kind)))
>      {
>        char *str;
> 



More information about the Gcc-patches mailing list