Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

subprojects always inherit c_std and cpp_std from the parent project #1889

Closed
Timmmm opened this issue Jun 3, 2017 · 29 comments
Closed

subprojects always inherit c_std and cpp_std from the parent project #1889

Timmmm opened this issue Jun 3, 2017 · 29 comments

Comments

@Timmmm
Copy link
Contributor

Timmmm commented Jun 3, 2017

If you have a main project that uses c11 for example, and then a subproject needs gnu99 for example, the subproject will actually be compiled with c11.

I'm not sure meson pays any attention to language versions in subprojects.

@jpakkane
Copy link
Member

jpakkane commented Jun 8, 2017

The language version is a global option that is shared by every target. There are languages (such as C++) where combining different language versions fails.

@Timmmm
Copy link
Contributor Author

Timmmm commented Jun 9, 2017

But it only fails in some fairly limited cases. I feel like it should be respected but you can print a warning something like:

Warning: Mixing C++ versions c++11 and gnu++14 may cause hard-to-find bugs.

If you really don't want to allow this then at the very least it should print a warning like this:

Warning: Subproject has different C++ version. Language versions are global
options shared between all targets. Ignoring 'gnu++14' and using 'c++11'.

@germandiagogomez
Copy link
Contributor

I am having exactly the same problem but with another variation.

I have a project, the default_options is c++14 or c++1z depending on wether my build is for ios or desktop due to this bug: https://bugreports.qt.io/browse/QTBUG-63747

I do something like this (@jpakkane correct me if I am wrong in the way to approach this):

project('myproject', 'cpp', version : '0.5')


if host_machine.system() == 'darwin' and meson.is_cross_build()
  # Workaround exactly this bug: https://bugreports.qt.io/browse/QTBUG-63747
  add_project_arguments(['-std=c++14'], language : 'cpp')
  add_project_link_arguments(['-std=c++14'], language : 'cpp')
else
  add_project_arguments(['-std=c++1z'], language : 'cpp')
  add_project_link_arguments(['-std=c++1z'], language : 'cpp')
endif

So the net result of this is that now, due to project not having default options (since it is conditional), my subproject gets no c++11/14 flag and cannot be compiled. Can I define default_options conditionally? I tried but meson complained that project must be the first thing in the project.

@nirbheek nirbheek changed the title Subproject C/C++ version is ignored. subprojects always inherit c_std and cpp_std from the parent project Feb 11, 2018
@nirbheek
Copy link
Member

Setting aside cpp_std, I think c_std should never be inherited by subprojects.

@dpirch
Copy link
Contributor

dpirch commented Aug 3, 2018

I think it is ok that config options (build type, warning options...) are inherited from the parent project. The problem here is that c_std shouldn't be a config option at all.

It does not make sense that the user can set the C standard version via meson configure - it is not a preference by the user who is compiling the project, but determined by authors of the project, similar to dependencies.

So instead of abusing default_options, there should be a method in the meson or compiler object to set the language dialect and version (mininum/maximum or exact) in a compiler-agnostic way, and a way to override it for individual build targets.

@jpakkane
Copy link
Member

jpakkane commented Aug 3, 2018

it is not a preference by the user who is compiling the project, but determined by authors of the project, similar to dependencies.

There are other people who have the exact opposite opinion. Do not assume that "the way I personally want to do something" is is global truth shared by all. Other people have other needs, requirements and preferences.

a way to override it for individual build targets.

This is already possible with the override_options kwarg.

A more general overrider mechanism will eventually be done as #3001.

@Timmmm
Copy link
Contributor Author

Timmmm commented Aug 31, 2018

Do not assume that "the way I personally want to do something" is is global truth shared by all.

He's right though. If you have a dependency or sub-project whose code requires gnu++17, you can't just say "well I want to compile it with c++14 so there".

@jpakkane
Copy link
Member

In C++ mixing different language versions is UB and not guaranteed to work. For example in stdlibc++ there are two different implementations of std::string depending on the language version and they are not ABI compatible.

@Timmmm
Copy link
Contributor Author

Timmmm commented Aug 31, 2018

True, but that is a separate problem. There's no reason you can't have a subproject that requires C++17 to compile that exposes a C API that is consumed by a C++11 super-project.

Printing a warning is probably a good idea anyway.

@dpirch
Copy link
Contributor

dpirch commented Sep 1, 2018

In C++ mixing different language versions is UB and not guaranteed to work.

(Sub)projects that build libraries which are usable only from certain language versions could simply provide different dependency objects for the different language versions.

@jcaesar
Copy link

jcaesar commented Oct 19, 2018

If I may add my two cents: readline-6.3 won't compile without gnu11 (or a similarly "relaxed" version of the standard), but I'd like to wrap it from a project that prefers c11. Now, if I link readline from whatever my distribution's package manager provides, the used dialect won't be checked at all, hence I don't understand why meson cares. (@dpirch's solution does not appeal to me…)

@xclaesse
Copy link
Member

This could be fixed by a PR similar to #4157, c_std could be a per-subproject option.

I agree it's debatable that c_std is an option at all, since it's a characteristic of the project and something the user can rarely decide. But at least with a PR similar to #4157, if the subproject has project(..., default_options:'c_std=c11') it will be respected even if the parent project has default_options:'c_std=c99'.

One use-case I can imagine is a parent project that wants to keep C89 compat and has an optional subproject that requires C11. That means that if user builds with -Dsome_feature=enabled he has to know to also pass -Dc_std=c11. That's not great user experience.

gnomesysadmins pushed a commit to GNOME/gtk that referenced this issue Nov 30, 2018
Meson propagates c_std to all subprojects, including those that
require gnu99 or c11, etc.

mesonbuild/meson#1889
gnomesysadmins pushed a commit to GNOME/gtk that referenced this issue Dec 4, 2018
Meson propagates c_std to all subprojects, including those that
require gnu99 or c11, etc.

mesonbuild/meson#1889
gnomesysadmins pushed a commit to GNOME/gtk that referenced this issue Dec 4, 2018
Meson propagates c_std to all subprojects, including those that
require gnu99 or c11, etc.

mesonbuild/meson#1889
gnomesysadmins pushed a commit to GNOME/gtk that referenced this issue Dec 4, 2018
Meson propagates c_std to all subprojects, including those that
require gnu99 or c11, etc.

mesonbuild/meson#1889
gnomesysadmins pushed a commit to GNOME/gtk that referenced this issue Mar 13, 2019
Meson propagates c_std to all subprojects, including those that
require gnu99 or c11, etc.

mesonbuild/meson#1889
@Akaricchi
Copy link
Contributor

This is already possible with the override_options kwarg.

@jpakkane this doesn't work for compiler checks like cc.has_header_symbol, which unfortunately also get infected with the superproject's std. I build my main project in strict C11 mode, in which many non-standard symbols are not exposed by headers, but I have a 3rd-party dependency that can optionally use many such non-standard features if it detects their presence, resorting to internal polyfils otherwise.

I definitely agree that the standard should not be a user option at all, but if you must keep it that way, please at least make it per-project, or provide a more complete overriding mechanism.

gnomesysadmins pushed a commit to GNOME/gtk that referenced this issue Mar 22, 2019
Meson propagates c_std to all subprojects, including those that
require gnu99 or c11, etc.

mesonbuild/meson#1889
Akaricchi added a commit to taisei-project/SDL-mirror that referenced this issue Mar 22, 2019
gnomesysadmins pushed a commit to GNOME/gtk that referenced this issue Mar 25, 2019
Meson propagates c_std to all subprojects, including those that
require gnu99 or c11, etc.

mesonbuild/meson#1889
gnomesysadmins pushed a commit to GNOME/gtk that referenced this issue Mar 26, 2019
Meson propagates c_std to all subprojects, including those that
require gnu99 or c11, etc.

mesonbuild/meson#1889
gnomesysadmins pushed a commit to GNOME/gtk that referenced this issue Mar 29, 2019
Meson propagates c_std to all subprojects, including those that
require gnu99 or c11, etc.

mesonbuild/meson#1889
gnomesysadmins pushed a commit to GNOME/gtk that referenced this issue Mar 30, 2019
Meson propagates c_std to all subprojects, including those that
require gnu99 or c11, etc.

mesonbuild/meson#1889
gnomesysadmins pushed a commit to GNOME/gtk that referenced this issue Mar 30, 2019
Meson propagates c_std to all subprojects, including those that
require gnu99 or c11, etc.

mesonbuild/meson#1889
@andlabs
Copy link

andlabs commented Apr 3, 2019

Does loading a C++11 dynamic library that exposes a C API into a C++17 program really cause UB? If so, why? How? And how are we able to have a Linux ecosystem full of libraries written in C++ at all if so? If that was the case, you would be forced to require that a C++ compiler on that system must only support the langauge that all the libraries on the system use, and by extension that the entire system would need to be reinstalled if the programmer wants to upgrade to the next C++ standard.

This kind of restriction makes a lot more sense for a static library, but then you have the problem of subprojects from other developers: my current library project will complain if the standard is not C99/C++11 because that's the target version I want to compile with, but I should be able to allow overriding that for a higher version if and only if this is a subproject...? Otherwise we would all have to write for the lowest common denominator (c89 or c++98).

Also this kind of restriction only makes sense if meson projects could only be consumed by other meson projects. I wouldn't be able to enforce this restriction on a project that doesn't use meson at all, such as the Go bindings to my library =P

@dcbaker
Copy link
Member

dcbaker commented Apr 3, 2019

I see a couple possible solutions to this.

  1. is to respect the "gnu" vs "c/cpp" standards, so that if a subproject is "gnu++11" and the parent is "c++14" the subproject would be compiled as "gnu++14", at least in that case we'd increase the chances of compilation working.
  2. we carry special rules for C++ that basically take the set of standards in the project and all subprojects, and then pick the highest version for all projects. The two problems I see here is that if a project uses C++ internally, but exposes a C ABI we may bump for no reason. The second is that several keywords have different meanings before and after C++11 (auto comes to mind)
  3. we allow projects to specify the language standard with a comparison operator, ala default_options : ['cpp_std>=c++14'], then calculate promotion rules based on that, if we can come up with a version that satisfies all the dependencies great, otherwise we warn/error. for languages like C where the linking ABI is the same no matter which version you pick we can just ignore this and compile them all with the standard they asked for.

gnomesysadmins pushed a commit to GNOME/gtk that referenced this issue Apr 3, 2019
Meson propagates c_std to all subprojects, including those that
require gnu99 or c11, etc.

mesonbuild/meson#1889
gnomesysadmins pushed a commit to GNOME/gtk that referenced this issue Apr 8, 2019
Meson propagates c_std to all subprojects, including those that
require gnu99 or c11, etc.

mesonbuild/meson#1889
gnomesysadmins pushed a commit to GNOME/gtk that referenced this issue Apr 10, 2019
Meson propagates c_std to all subprojects, including those that
require gnu99 or c11, etc.

mesonbuild/meson#1889
gnomesysadmins pushed a commit to GNOME/gtk that referenced this issue Apr 11, 2019
Meson propagates c_std to all subprojects, including those that
require gnu99 or c11, etc.

mesonbuild/meson#1889
@TheQwertiest
Copy link
Contributor

TheQwertiest commented Jul 17, 2019

In C++ mixing different language versions is UB and not guaranteed to work

But that is not true though? It's not UB for every C++ compiler, and I don't know anything about C , but at least three major C++ compilers (gcc, msvc, clang) maintain a strict ABI compatibility between various C++ versions (when built with the same compiler obviously). And they go a long way to keep it (e.g. like not approving features for C++20).
I.e. a library built with gcc --std=c++03 will always work when linking with library built with --std=c++17 (sometimes they do need additional flags though, e.g. for resurrecting std::auto_ptr or _GLIBCXX_USE_CXX11_ABI to solve the problem with two std::string implementations in gcc).

Yes, there might be some C++ compilers that do not provide any guarantees about ABI compatibility, but there a lot of other ways how the compiler might not be fully functional (e.g. some compilers have broken library grouping in linker, despite providing and accepting such a flag).

@xclaesse
Copy link
Member

I recently ran into a case where I had to use a gnu++ library that exposes a small C wrapper, in a c++11 parent project. So as far as I understand, both parts uses different C++ ABI but could still be working together. AFAIK, it's pretty common to wrap a C++ implementation into a C API, to avoid all those ABI issues, no?

@jpakkane
Copy link
Member

It's not that simple. If you have liba that internally uses one version of the C++ standard library and libb that internally uses a different version and you bring them in the same process, things explode. If you need to static link them, things explode even worse. (This is what has been reported to me, I have not tested it myself.)

@xclaesse
Copy link
Member

Well, in my case it seems to be working, currently with horrible hand written build scripts that I wanted to port to Meson, but I have to admit I have no idea if it generally works, or if they's are doing evil stuff and are just lucky.

@jpakkane
Copy link
Member

Note that this ties into #3001. Now that we have the native file, adding per project configuration overrides to that is feasible.

@Timmmm
Copy link
Contributor Author

Timmmm commented Jul 17, 2019

things explode ... This is what has been reported to me, I have not tested it myself.

It would be good to find an actual example of this happening, because I have since researched this and found what TheQwertiest said to be true. Things have a right to explode, but in reality all the major compiler vendors arrange things so that they don't, exactly because limitations like the one that Meson is currently imposing are really inconvenient.

@TheQwertiest
Copy link
Contributor

TheQwertiest commented Jul 17, 2019

AFAIK, it's pretty common to wrap a C++ implementation into a C API, to avoid all those ABI issues, no?

Well yeah, some even provide a C++ header-only wrapper to avoid using the C interface directly =)
But there are quite a few projects that has C++ interface and does not provide a pure C interface. And having C interface helps only when using prebuilt shared libraries and it does not help in the case when you are building the dependency from sources.

If you have liba that internally uses one version of the C++ standard library and libb that internally uses a different version and you bring them in the same process, things explode

I thought we were talking about building from sources though? Using random C++ libraries together is UB (IMO) (and should not be performed without consulting your doctor first), building them with the same compiler (but different std switches) is not (always) an UB.

FWIW, I had successfully built various projects with mismatched std versions (C+03/C+11/C++14) on msvs and gcc. And there were no problems (apart from having to ressurect std::auto_ptr in one of the projects...).

I have to admit I have no idea if it generally works, or if they's are doing evil stuff and are just lucky.

It all depends on the compiler you are using.
gcc: https://stackoverflow.com/a/49119902 (answered by gcc dev)
msvc: can't find the source right now, but I think it was mentioned by STL (MSVC STL maintainer) on reddit in r/cpp

@strega-nil
Copy link
Contributor

If you're using the same compiler and version, across any of the major compilers, there will not be an issue, assuming that you don't pull an abseil and change your ABI in different C++ versions. This is the same for Rust and C. It'd be really nice if we could pull the C++ standard out of the options.

@jpakkane

In C++ mixing different language versions is UB and not guaranteed to work. For example in stdlibc++ there are two different implementations of std::string depending on the language version and they are not ABI compatible.

This is not true, see the documentation. libstdc++ uses a macro, and it's independent of the C++ version.

Also, as long as we're complaining about things being options, warning level should not be an option. That doesn't have anything to do with ABI, but if I want to use a library that doesn't compile under /W4 /Wx, I should be able to turn off both warnings-as-errors and set the warning_level lower, just for that library.

@andlabs
Copy link

andlabs commented Jul 17, 2019

We should probably keep things to the C++ spec, if anything. I should have rephrased my earlier comment to be asking for specific documentation, because even though "it works as you would want it to" does fall under "undefined behavior", the fact that this indeed is undefined behavior is news to all of us says something.

That being said, I have been mulling about this some more, and I suppose that yes, parent projects that link directly to subprojects should be able to have the child inherit all the predefined built-in options, for maximum safety. I therefore concede that ideally we should have a situation where the minimum version is specified, and by default it will use the minimum version when building standalone, but inherit the actual version from the parent. If there's an incompatibility, that incompatibility should be reported by meson itself. And yes, I am extending it to all the builtin variables, not just c_std and cpp_std.

@TheQwertiest
Copy link
Contributor

TheQwertiest commented Jul 17, 2019

I therefore concede that ideally we should have a situation where the minimum version is specified, and by default it will use the minimum version when building standalone, but inherit the actual version from the parent.

Uhm, no. That would be mostly the same as we have right now. The problem is that parent settings can not always be applied on it's subprojects. A scenario when parent project is built with C++17 (which can't be built with any other) and linked against subproject built with C++03 (which can't be built with any other) with the same compiler is a valid scenario and supported by compiler vendors (as discussed above).

I don't have an universal solution though: sometimes project supports multiple std=c++, sometimes it supports only one, some projects might not specify the requirement at all...

At the minimum what should be possible is to use different std=c++ in main project and it's subprojects. The prettiness of the solution is secondary to me =) (note: secondary != unimportant)

warning level should not be an option.

^ this!!! I'm using VERY ugly hacks right now to avoid warning errors in subprojects...

@Timmmm
Copy link
Contributor Author

Timmmm commented Jul 17, 2019

We should probably keep things to the C++ spec

I would be very surprised if the C++ spec has anything to say about this at all.

@strega-nil
Copy link
Contributor

strega-nil commented Jul 18, 2019

We should probably keep things to the C++ spec, if anything. I should have rephrased my earlier comment to be asking for specific documentation, because even though "it works as you would want it to" does fall under "undefined behavior", the fact that this indeed is undefined behavior is news to all of us says something.

It's not undefined behavior, to be clear. The behavior is not specified by the C++ standard; that doesn't mean it's undefined behavior, and the behavior is very well specified by all implementations (the ones that matter, anyways).

For different versions of the same compilers, GCC guarantees ABI compatibility back to the beginning of architectures, and MSVC guarantees ABI compatibility for one major version, generally (i.e., VS2015 is ABI compatible with VS2015), and has guaranteed ABI compatibility across 2019, 2017, and 2015 (although I don't think we've every actually broken ABI). clang, depending on the triplet one uses, is either compatible with GCC or with MSVC.

There are zero compilers that I know of which change ABI across standard flags; GCC and clang don't change ABI across standard flags, and MSVC has only had them for two major versions now, but it doesn't change ABI across the three standard versions it has (c++14, c++17, c++latest).

@xclaesse
Copy link
Member

This is now fixed in master. All compiler options, including c_std/cpp_std, are per-subproject. That means they inherit the value from main project, but if they define another value in default_options then that overrides the value from main project.

@nirbheek
Copy link
Member

FTR, this was fixed in #10170 which went into the 0.63.0 release.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests