meson: 0.51.0: cpp_std setting not being applied

This is tested with 0.51.0 and master @ commit 54b1c43277d16dcef2d8acc98d131ab9232d2fac.

I have a project which is specifying c_std and cpp_std:

project('libc',
	['c', 'cpp'],
	default_options : ['c_std=c11','cpp_std=c++17'])

I can see the C standard being applied in build.ninja:

build src/e88f707@@c@sta/crt__Exit.c.o: c_COMPILER ../src/crt/_Exit.c
 DEPFILE = src/e88f707@@c@sta/crt__Exit.c.o.d
 ARGS = -Isrc/e88f707@@c@sta -Isrc/ -I../src -I../include -I../src/gdtoa/include -I../arch/x86_64/include -Xclang -fcolor-diagnostics -pipe -Wall -Winvalid-pch -std=c11 -g -isystem ../printf -isystem ../src/gdtoa/include -isystem ../openlibm/src -isystem ../openlibm/include -isystem ../include -fno-builtin -nodefaultlibs -fno-stack-protector -DNO_ERRNO -DIFNAN_CHECK -DGDTOA_NO_ASSERT -DNO_FENV_H -Wno-reserved-id-macro -nostdinc

Build I don’t see the C++ standard being set:

build printf_test@exe/printf_test_test_suite.cpp.o: cpp_COMPILER_FOR_BUILD ../printf/test/test_suite.cpp
 DEPFILE = printf_test@exe/printf_test_test_suite.cpp.o.d
 ARGS = -Iprintf_test@exe -I. -I.. -I../printf/test -Xclang -fcolor-diagnostics -pipe -Wall -Winvalid-pch -Wnon-virtual-dtor -g -Wno-format -Wno-format-invalid-specifier -Wno-old-style-cast -Wno-missing-prototypes

And the incorrect standard is further reflected by the failure to recognize the noexcept keyword:

[1/2] Compiling C++ object 'printf_test@exe/printf_test_test_suite.cpp.o'.
FAILED: printf_test@exe/printf_test_test_suite.cpp.o
c++ -Iprintf_test@exe -I. -I.. -I../printf/test -Xclang -fcolor-diagnostics -pipe -Wno-format -Wno-format-invalid-specifier -Wno-old-style-cast -Wno-missing-prototypes -MD -MQ 'printf_test@exe/printf_test_test_suite.cpp.o' -MF 'printf_test@exe/printf_test_test_suite.cpp.o.d' -o 'printf_test@exe/printf_test_test_suite.cpp.o' -c ../printf/test/test_suite.cpp
In file included from ../printf/test/test_suite.cpp:31:
../printf/test/catch.hpp:420:63: error: expected ';' at end of declaration list
        SourceLineInfo( char const* _file, std::size_t _line ) noexcept
                                                              ^
../printf/test/catch.hpp:427:43: error: expected ';' at end of declaration list
        SourceLineInfo( SourceLineInfo&& )              noexcept = default;

The target is quite simple:

printf_tests = executable('printf_test',
	sources: ['printf/test/test_suite.cpp'],
	cpp_args: ['-Wno-format', '-Wno-format-invalid-specifier', '-Wno-old-style-cast', '-Wno-missing-prototypes'],
	include_directories: [include_directories('printf/test')],
	native: true
)

This happens with both clang and GCC.

About this issue

  • Original URL
  • State: open
  • Created 5 years ago
  • Reactions: 3
  • Comments: 65 (53 by maintainers)

Most upvoted comments

If you do this:

project('xxx', 'c', default_options: ['c_std=c99'])
executable('foo', 'foo.c', native: true)

Then it must use -std=c99 to build the target. No ifs, buts or maybes. There is not even any option to set the standard version for this case, because build. options are not exposed when not cross compiling. Not doing that is counterintuitive and unexpected and will bury us in a stream of bug reports.

It does not matter so much how this is solved (like maybe make c_std mirror the host setting when native compiling) but this has to work.

I still can’t reproduce this with 0.55.3, here’s my meson.build for testing:

project('foo', 'cpp', default_options : ['cpp_std=c++17'])

executable('main', 'main.cpp')

and my main.cpp:

#include <iostream>
#include <optional>  // Unused, but force the use of C++17

int main() {
    std::cout << "Hello World!" << std::endl;
}

That’s a very unreliable way to test for C++ language level. Here’s a trivial program (ref this table):

app.cpp

#include <iostream>

int main() {
  std::cout << __cplusplus << '\n';
  return 0;
}

meson.build:

project(
  'test-proj',
  'cpp',
  version : '0.1',
  default_options : [
    'warning_level=3',
    'cpp_std=c++20'
  ]
)

boost_dep = dependency('boost')

exe = executable(
  'test-proj',
  'app.cpp',
  dependencies : boost_dep
)

test('basic', exe)
$> meson --version
1.0.1
$> ./test-proj
201703 # C++ 17, not C++20

I tried Meson for the first time today, this has not been a great experience.

What I meant is why is it set at a global build-option level, rather than, say, per target or per project in the build rules.

Managed to replicate this. The fix wlll be in 0.51.1.

@charlesnicholson, meson sets two standards, one for build machine targets, and one for host machine targets, the undecorated cpp_std=... is for host targets, and build.* is only for build targets. what you want is:

project(
    ...,
    default_options : ['cpp_std=c++17', 'build.cpp_std=c++17'],
)

I still think there is no justification for changing the behavior of things when a cross-supporting project is built natively. That gets us right back to the messy situation we were in before where it’s hard to write cross-supporting projects simply and correctly, because everything works different depending on whether the user happens to do a native or cross build.

Not really. A different way of saying what I am aiming for here is this:

When not cross building all build.XXX options are mandated to have the same value as the
corresponding XXX option.

Build definitions in files (flags for for-host and for-build targets etc) remain just as they are now. It’s just that the external options you can set from the outside are not duplicated. There are only one set of those.

Say the subproject had a C++ component and set a build.cpp_std default option, and then the master project doesn’t use C++. Same issue! The subproject has no idea what the master project will be, and the master subproject has no “mandatory arguments” or whatever it must tell the subproject, establishing a contract between them. [mandatory arguments could let the master project author know “oh, I need to pass some sort of cpp_std”.]

Setting up default values for new options like this could be added (it might even work like that already, don’t remember) but it does not really work for build. variants for existing options. You can’t really know when they should spring into existance. For example you could have a master project that does not define c_std at all but which does build cross stuff. When the project call is first evaluated it is impossible to know whether the master project will use build. options or not or whether they come from a subproject. It might even be the case that building of build target is conditional based on some option.

It can get even more complicated than this but the outcome of this is clear: if there is to be build.foo options in addition to plain foo option, they both must come into existance at the same time.