diff options
31 files changed, 523 insertions, 273 deletions
@@ -16,7 +16,7 @@ build system. #### Dependencies - [Python](https://python.org) (version 3.5 or newer) - - [Ninja](https://ninja-build.org) (version 1.5 or newer) + - [Ninja](https://ninja-build.org) (version 1.7 or newer) #### Installing from source diff --git a/docs/markdown/Cross-compilation.md b/docs/markdown/Cross-compilation.md index 9e64a3e..4c4b7bf 100644 --- a/docs/markdown/Cross-compilation.md +++ b/docs/markdown/Cross-compilation.md @@ -195,14 +195,18 @@ surprisingly, `build_machine`, `host_machine` and `target_machine`. Determining the operating system of your host machine is simply a matter of calling `host_machine.system()`. -There are two different values for the CPU. The first one is -`cpu_family`. It is a general type of the CPU. Common values might -include `x86`, `arm` or `x86_64`. The second value is `cpu` which is a -more specific subtype for the CPU. Typical values for a `x86` CPU -family might include `i386` or `i586` and for `arm` family `armv5` or -`armv7hl`. Note that CPU type strings are very system dependent. You -might get a different value if you check its value on the same machine -but with different operating systems. +There are two different values for the CPU. The first one is `cpu_family`. It +is a general type of the CPU. This should have a value from [the CPU Family +table](Reference-tables.md#cpu-families). *Note* that meson does not add +`el` to end cpu_family value for little endian systems. Big endian and little +endian mips are both just `mips`, with the `endian` field set approriately. + +The second value is `cpu` which is +a more specific subtype for the CPU. Typical values for a `x86` CPU family +might include `i386` or `i586` and for `arm` family `armv5` or `armv7hl`. +Note that CPU type strings are very system dependent. You might get a +different value if you check its value on the same machine but with different +operating systems. If you do not define your host machine, it is assumed to be the build machine. Similarly if you do not specify target machine, it is assumed diff --git a/docs/markdown/IDE-integration.md b/docs/markdown/IDE-integration.md index f86a818..a6c6f4b 100644 --- a/docs/markdown/IDE-integration.md +++ b/docs/markdown/IDE-integration.md @@ -253,6 +253,21 @@ line arguments, environment variable settings and how to process the output. } ``` +## Build system files + +It is also possible to get Meson build files used in your current project. This +can be done by running `meson introspect --buildsystem-files /path/to/builddir`. + +The output format is as follows: + +```json +[ + "/Path/to/the/targets/meson.build", + "/Path/to/the/targets/meson_options.txt", + "/Path/to/the/targets/subdir/meson.build" +] +``` + # Programmatic interface Meson also provides the `meson introspect` for project introspection via the diff --git a/mesonbuild/backend/backends.py b/mesonbuild/backend/backends.py index 138708d..31ddfb4 100644 --- a/mesonbuild/backend/backends.py +++ b/mesonbuild/backend/backends.py @@ -166,18 +166,21 @@ class Backend: return os.path.join(self.environment.get_build_dir(), self.get_target_filename(target)) def get_base_options_for_target(self, target): - return OptionOverrideProxy(target.option_overrides, + return OptionOverrideProxy(target.option_overrides_base, self.environment.coredata.builtins, self.environment.coredata.base_options) def get_compiler_options_for_target(self, target): - return OptionOverrideProxy( - target.option_overrides, - self.environment.coredata.compiler_options[target.for_machine]) + comp_reg = self.environment.coredata.compiler_options[target.for_machine] + comp_override = target.option_overrides_compiler + return { + lang: OptionOverrideProxy(comp_override[lang], comp_reg[lang]) + for lang in set(comp_reg.keys()) | set(comp_override.keys()) + } def get_option_for_target(self, option_name, target): - if option_name in target.option_overrides: - override = target.option_overrides[option_name] + if option_name in target.option_overrides_base: + override = target.option_overrides_base[option_name] return self.environment.coredata.validate_option_value(option_name, override) return self.environment.coredata.get_builtin_option(option_name, target.subproject) @@ -581,7 +584,7 @@ class Backend: # starting from hard-coded defaults followed by build options and so on. commands = CompilerArgs(compiler) - copt_proxy = self.get_compiler_options_for_target(target) + copt_proxy = self.get_compiler_options_for_target(target)[compiler.language] # First, the trivial ones that are impossible to override. # # Add -nostdinc/-nostdinc++ if needed; can't be overridden diff --git a/mesonbuild/backend/ninjabackend.py b/mesonbuild/backend/ninjabackend.py index 03ccd19..e765466 100644 --- a/mesonbuild/backend/ninjabackend.py +++ b/mesonbuild/backend/ninjabackend.py @@ -294,7 +294,7 @@ int dummy; def generate(self): ninja = environment.detect_ninja_command_and_version(log=True) if ninja is None: - raise MesonException('Could not detect Ninja v1.5 or newer') + raise MesonException('Could not detect Ninja v1.7 or newer') (self.ninja_command, self.ninja_version) = ninja outfilename = os.path.join(self.environment.get_build_dir(), self.ninja_filename) tempfilename = outfilename + '~' @@ -303,7 +303,7 @@ int dummy; self.build.get_project()) outfile.write('# It is autogenerated by the Meson build system.\n') outfile.write('# Do not edit by hand.\n\n') - outfile.write('ninja_required_version = 1.5.1\n\n') + outfile.write('ninja_required_version = 1.7.1\n\n') num_pools = self.environment.coredata.backend_options['backend_max_links'].value if num_pools > 0: @@ -2560,7 +2560,14 @@ https://gcc.gnu.org/bugzilla/show_bug.cgi?id=47485''')) # to be after all internal and external libraries so that unresolved # symbols from those can be found here. This is needed when the # *_winlibs that we want to link to are static mingw64 libraries. - commands += linker.get_option_link_args(self.environment.coredata.compiler_options[target.for_machine]) + if hasattr(linker, 'get_language'): + # The static linker doesn't know what language it is building, so we + # don't know what option. Fortunately, it doesn't care to see the + # language-specific options either. + # + # We shouldn't check whether we are making a static library, because + # in the LTO case we do use a real compiler here. + commands += linker.get_option_link_args(self.environment.coredata.compiler_options[target.for_machine][linker.get_language()]) dep_targets = [] dep_targets.extend(self.guess_external_link_dependencies(linker, target, commands, internal)) diff --git a/mesonbuild/backend/vs2010backend.py b/mesonbuild/backend/vs2010backend.py index 9a7ebf2..ef849e1 100644 --- a/mesonbuild/backend/vs2010backend.py +++ b/mesonbuild/backend/vs2010backend.py @@ -891,8 +891,10 @@ class Vs2010Backend(backends.Backend): # generate_single_compile() and generate_basic_compiler_args() for l, comp in target.compilers.items(): if l in file_args: - file_args[l] += compilers.get_base_compile_args(self.get_base_options_for_target(target), comp) - file_args[l] += comp.get_option_compile_args(self.environment.coredata.compiler_options[target.for_machine]) + file_args[l] += compilers.get_base_compile_args( + self.get_base_options_for_target(target), comp) + file_args[l] += comp.get_option_compile_args( + self.environment.coredata.compiler_options[target.for_machine][comp.language]) # Add compile args added using add_project_arguments() for l, args in self.build.projects_args[target.for_machine].get(target.subproject, {}).items(): @@ -905,10 +907,11 @@ class Vs2010Backend(backends.Backend): file_args[l] += args # Compile args added from the env or cross file: CFLAGS/CXXFLAGS, etc. We want these # to override all the defaults, but not the per-target compile args. - for key, opt in self.environment.coredata.compiler_options[target.for_machine].items(): - l, suffix = key.split('_', 1) - if suffix == 'args' and l in file_args: - file_args[l] += opt.value + for l in file_args.keys(): + opts = self.environment.coredata.compiler_options[target.for_machine][l] + k = 'args' + if k in opts: + file_args[l] += opts[k].value for args in file_args.values(): # This is where Visual Studio will insert target_args, target_defines, # etc, which are added later from external deps (see below). @@ -1115,7 +1118,8 @@ class Vs2010Backend(backends.Backend): # to be after all internal and external libraries so that unresolved # symbols from those can be found here. This is needed when the # *_winlibs that we want to link to are static mingw64 libraries. - extra_link_args += compiler.get_option_link_args(self.environment.coredata.compiler_options[compiler.for_machine]) + extra_link_args += compiler.get_option_link_args( + self.environment.coredata.compiler_options[compiler.for_machine][comp.language]) (additional_libpaths, additional_links, extra_link_args) = self.split_link_args(extra_link_args.to_native()) # Add more libraries to be linked if needed diff --git a/mesonbuild/build.py b/mesonbuild/build.py index b1bf9d4..c200261 100644 --- a/mesonbuild/build.py +++ b/mesonbuild/build.py @@ -13,7 +13,7 @@ # limitations under the License. import copy, os, re -from collections import OrderedDict +from collections import OrderedDict, defaultdict import itertools, pathlib import hashlib import pickle @@ -28,7 +28,10 @@ from .mesonlib import ( extract_as_list, typeslistify, stringlistify, classify_unity_sources, get_filenames_templates_dict, substitute_values, has_path_sep, unholder ) -from .compilers import Compiler, is_object, clink_langs, sort_clink, lang_suffixes, is_known_suffix +from .compilers import ( + Compiler, all_languages, is_object, clink_langs, sort_clink, lang_suffixes, + is_known_suffix +) from .linkers import StaticLinker from .interpreterbase import FeatureNew @@ -358,7 +361,8 @@ a hard error in the future.'''.format(name)) self.for_machine = for_machine self.install = False self.build_always_stale = False - self.option_overrides = {} + self.option_overrides_base = {} + self.option_overrides_compiler = defaultdict(dict) if not hasattr(self, 'typename'): raise RuntimeError('Target type is not set for target class "{}". This is a bug'.format(type(self).__name__)) @@ -448,7 +452,15 @@ a hard error in the future.'''.format(name)) # set, use the value of 'install' if it's enabled. self.build_by_default = True - self.option_overrides = self.parse_overrides(kwargs) + option_overrides = self.parse_overrides(kwargs) + + for k, v in option_overrides.items(): + if '_' in k: + lang, k2 = k.split('_', 1) + if lang in all_languages: + self.option_overrides_compiler[lang][k2] = v + continue + self.option_overrides_base[k] = v def parse_overrides(self, kwargs) -> dict: result = {} diff --git a/mesonbuild/compilers/c.py b/mesonbuild/compilers/c.py index eba7131..1bc9e84 100644 --- a/mesonbuild/compilers/c.py +++ b/mesonbuild/compilers/c.py @@ -107,25 +107,32 @@ class ClangCCompiler(ClangCompiler, CCompiler): if version_compare(self.version, self._C18_VERSION): c_stds += ['c18'] g_stds += ['gnu18'] - opts.update({'c_std': coredata.UserComboOption('C language standard to use', - ['none'] + c_stds + g_stds, - 'none')}) + opts.update({ + 'std': coredata.UserComboOption( + 'C language standard to use', + ['none'] + c_stds + g_stds, + 'none', + ), + }) if self.info.is_windows() or self.info.is_cygwin(): opts.update({ - 'c_winlibs': coredata.UserArrayOption('Standard Win libraries to link against', - gnu_winlibs), }) + 'winlibs': coredata.UserArrayOption( + 'Standard Win libraries to link against', + gnu_winlibs, + ), + }) return opts def get_option_compile_args(self, options): args = [] - std = options['c_std'] + std = options['std'] if std.value != 'none': args.append('-std=' + std.value) return args def get_option_link_args(self, options): if self.info.is_windows() or self.info.is_cygwin(): - return options['c_winlibs'].value[:] + return options['winlibs'].value[:] return [] @@ -166,15 +173,18 @@ class ArmclangCCompiler(ArmclangCompiler, CCompiler): def get_options(self): opts = CCompiler.get_options(self) - opts.update({'c_std': coredata.UserComboOption('C language standard to use', - ['none', 'c90', 'c99', 'c11', - 'gnu90', 'gnu99', 'gnu11'], - 'none')}) + opts.update({ + 'std': coredata.UserComboOption( + 'C language standard to use', + ['none', 'c90', 'c99', 'c11', 'gnu90', 'gnu99', 'gnu11'], + 'none', + ), + }) return opts def get_option_compile_args(self, options): args = [] - std = options['c_std'] + std = options['std'] if std.value != 'none': args.append('-std=' + std.value) return args @@ -204,25 +214,32 @@ class GnuCCompiler(GnuCompiler, CCompiler): if version_compare(self.version, v): c_stds += ['c17', 'c18'] g_stds += ['gnu17', 'gnu18'] - opts.update({'c_std': coredata.UserComboOption('C language standard to use', - ['none'] + c_stds + g_stds, - 'none')}) + opts.update({ + 'std': coredata.UserComboOption( + 'C language standard to use', + ['none'] + c_stds + g_stds, + 'none', + ), + }) if self.info.is_windows() or self.info.is_cygwin(): opts.update({ - 'c_winlibs': coredata.UserArrayOption('Standard Win libraries to link against', - gnu_winlibs), }) + 'winlibs': coredata.UserArrayOption( + 'Standard Win libraries to link against', + gnu_winlibs, + ), + }) return opts def get_option_compile_args(self, options): args = [] - std = options['c_std'] + std = options['std'] if std.value != 'none': args.append('-std=' + std.value) return args def get_option_link_args(self, options): if self.info.is_windows() or self.info.is_cygwin(): - return options['c_winlibs'].value[:] + return options['winlibs'].value[:] return [] def get_pch_use_args(self, pch_dir, header): @@ -248,11 +265,17 @@ class ElbrusCCompiler(GnuCCompiler, ElbrusCompiler): # It does support some various ISO standards and c/gnu 90, 9x, 1x in addition to those which GNU CC supports. def get_options(self): opts = CCompiler.get_options(self) - opts.update({'c_std': coredata.UserComboOption('C language standard to use', - ['none', 'c89', 'c90', 'c9x', 'c99', 'c1x', 'c11', - 'gnu89', 'gnu90', 'gnu9x', 'gnu99', 'gnu1x', 'gnu11', - 'iso9899:2011', 'iso9899:1990', 'iso9899:199409', 'iso9899:1999'], - 'none')}) + opts.update({ + 'std': coredata.UserComboOption( + 'C language standard to use', + [ + 'none', 'c89', 'c90', 'c9x', 'c99', 'c1x', 'c11', + 'gnu89', 'gnu90', 'gnu9x', 'gnu99', 'gnu1x', 'gnu11', + 'iso9899:2011', 'iso9899:1990', 'iso9899:199409', 'iso9899:1999', + ], + 'none', + ), + }) return opts # Elbrus C compiler does not have lchmod, but there is only linker warning, not compiler error. @@ -285,14 +308,18 @@ class IntelCCompiler(IntelGnuLikeCompiler, CCompiler): g_stds = ['gnu89', 'gnu99'] if version_compare(self.version, '>=16.0.0'): c_stds += ['c11'] - opts.update({'c_std': coredata.UserComboOption('C language standard to use', - ['none'] + c_stds + g_stds, - 'none')}) + opts.update({ + 'std': coredata.UserComboOption( + 'C language standard to use', + ['none'] + c_stds + g_stds, + 'none', + ), + }) return opts def get_option_compile_args(self, options): args = [] - std = options['c_std'] + std = options['std'] if std.value != 'none': args.append('-std=' + std.value) return args @@ -304,12 +331,16 @@ class VisualStudioLikeCCompilerMixin: def get_options(self): opts = super().get_options() - opts.update({'c_winlibs': coredata.UserArrayOption('Windows libs to link against.', - msvc_winlibs)}) + opts.update({ + 'winlibs': coredata.UserArrayOption( + 'Windows libs to link against.', + msvc_winlibs, + ), + }) return opts def get_option_link_args(self, options): - return options['c_winlibs'].value[:] + return options['winlibs'].value[:] class VisualStudioCCompiler(MSVCCompiler, VisualStudioLikeCCompilerMixin, CCompiler): @@ -343,14 +374,18 @@ class IntelClCCompiler(IntelVisualStudioLikeCompiler, VisualStudioLikeCCompilerM def get_options(self): opts = super().get_options() c_stds = ['none', 'c89', 'c99', 'c11'] - opts.update({'c_std': coredata.UserComboOption('C language standard to use', - c_stds, - 'none')}) + opts.update({ + 'std': coredata.UserComboOption( + 'C language standard to use', + c_stds, + 'none', + ), + }) return opts def get_option_compile_args(self, options): args = [] - std = options['c_std'] + std = options['std'] if std.value == 'c89': mlog.warning("ICL doesn't explicitly implement c89, setting the standard to 'none', which is close.", once=True) elif std.value != 'none': @@ -367,14 +402,18 @@ class ArmCCompiler(ArmCompiler, CCompiler): def get_options(self): opts = CCompiler.get_options(self) - opts.update({'c_std': coredata.UserComboOption('C language standard to use', - ['none', 'c90', 'c99'], - 'none')}) + opts.update({ + 'std': coredata.UserComboOption( + 'C language standard to use', + ['none', 'c90', 'c99'], + 'none', + ), + }) return opts def get_option_compile_args(self, options): args = [] - std = options['c_std'] + std = options['std'] if std.value != 'none': args.append('--' + std.value) return args @@ -393,9 +432,13 @@ class CcrxCCompiler(CcrxCompiler, CCompiler): def get_options(self): opts = CCompiler.get_options(self) - opts.update({'c_std': coredata.UserComboOption('C language standard to use', - ['none', 'c89', 'c99'], - 'none')}) + opts.update({ + 'std': coredata.UserComboOption( + 'C language standard to use', + ['none', 'c89', 'c99'], + 'none', + ), + }) return opts def get_no_stdinc_args(self): @@ -403,7 +446,7 @@ class CcrxCCompiler(CcrxCompiler, CCompiler): def get_option_compile_args(self, options): args = [] - std = options['c_std'] + std = options['std'] if std.value == 'c89': args.append('-lang=c') elif std.value == 'c99': diff --git a/mesonbuild/compilers/c_function_attributes.py b/mesonbuild/compilers/c_function_attributes.py index 3b9fdf9..e5de485 100644 --- a/mesonbuild/compilers/c_function_attributes.py +++ b/mesonbuild/compilers/c_function_attributes.py @@ -127,7 +127,4 @@ CXX_FUNC_ATTRIBUTES = { 'static int (*resolve_foo(void))(void) { return my_foo; }' '}' 'int foo(void) __attribute__((ifunc("resolve_foo")));'), - # Clang >= 10 requires the 'extern' keyword - 'gnu_inline': - 'extern inline __attribute__((gnu_inline)) int foo(void) { return 0; }', } diff --git a/mesonbuild/compilers/compilers.py b/mesonbuild/compilers/compilers.py index 52b9592..3d3a503 100644 --- a/mesonbuild/compilers/compilers.py +++ b/mesonbuild/compilers/compilers.py @@ -1272,10 +1272,10 @@ def get_global_options(lang: str, """Retreive options that apply to all compilers for a given language.""" description = 'Extra arguments passed to the {}'.format(lang) opts = { - lang + '_args': coredata.UserArrayOption( + 'args': coredata.UserArrayOption( description + ' compiler', [], split_args=True, user_input=True, allow_dups=True), - lang + '_link_args': coredata.UserArrayOption( + 'link_args': coredata.UserArrayOption( description + ' linker', [], split_args=True, user_input=True, allow_dups=True), } @@ -1288,12 +1288,13 @@ def get_global_options(lang: str, comp.INVOKES_LINKER) for k, o in opts.items(): - if k in properties: + user_k = lang + '_' + k + if user_k in properties: # Get from configuration files. - o.set_value(properties[k]) - elif k == lang + '_args': + o.set_value(properties[user_k]) + elif k == 'args': o.set_value(compile_args) - elif k == lang + '_link_args': + elif k == 'link_args': o.set_value(link_args) return opts diff --git a/mesonbuild/compilers/cpp.py b/mesonbuild/compilers/cpp.py index 9894cd3..d30017f 100644 --- a/mesonbuild/compilers/cpp.py +++ b/mesonbuild/compilers/cpp.py @@ -167,36 +167,45 @@ class ClangCPPCompiler(ClangCompiler, CPPCompiler): def get_options(self): opts = CPPCompiler.get_options(self) - opts.update({'cpp_eh': coredata.UserComboOption('C++ exception handling type.', - ['none', 'default', 'a', 's', 'sc'], - 'default'), - 'cpp_rtti': coredata.UserBooleanOption('Enable RTTI', True), - 'cpp_std': coredata.UserComboOption('C++ language standard to use', - ['none', 'c++98', 'c++03', 'c++11', 'c++14', 'c++17', 'c++1z', 'c++2a', - 'gnu++11', 'gnu++14', 'gnu++17', 'gnu++1z', 'gnu++2a'], - 'none')}) + opts.update({ + 'eh': coredata.UserComboOption( + 'C++ exception handling type.', + ['none', 'default', 'a', 's', 'sc'], + 'default', + ), + 'rtti': coredata.UserBooleanOption('Enable RTTI', True), + 'std': coredata.UserComboOption( + 'C++ language standard to use', + ['none', 'c++98', 'c++03', 'c++11', 'c++14', 'c++17', 'c++1z', 'c++2a', + 'gnu++11', 'gnu++14', 'gnu++17', 'gnu++1z', 'gnu++2a'], + 'none', + ), + }) if self.info.is_windows() or self.info.is_cygwin(): opts.update({ - 'cpp_winlibs': coredata.UserArrayOption('Standard Win libraries to link against', - gnu_winlibs), }) + 'winlibs': coredata.UserArrayOption( + 'Standard Win libraries to link against', + gnu_winlibs, + ), + }) return opts def get_option_compile_args(self, options): args = [] - std = options['cpp_std'] + std = options['std'] if std.value != 'none': args.append(self._find_best_cpp_std(std.value)) - non_msvc_eh_options(options['cpp_eh'].value, args) + non_msvc_eh_options(options['eh'].value, args) - if not options['cpp_rtti'].value: + if not options['rtti'].value: args.append('-fno-rtti') return args def get_option_link_args(self, options): if self.info.is_windows() or self.info.is_cygwin(): - return options['cpp_winlibs'].value[:] + return options['winlibs'].value[:] return [] def language_stdlib_only_link_flags(self): @@ -220,7 +229,7 @@ class EmscriptenCPPCompiler(EmscriptenMixin, LinkerEnvVarsMixin, ClangCPPCompile def get_option_compile_args(self, options): args = [] - std = options['cpp_std'] + std = options['std'] if std.value != 'none': args.append(self._find_best_cpp_std(std.value)) return args @@ -239,22 +248,30 @@ class ArmclangCPPCompiler(ArmclangCompiler, CPPCompiler): def get_options(self): opts = CPPCompiler.get_options(self) - opts.update({'cpp_eh': coredata.UserComboOption('C++ exception handling type.', - ['none', 'default', 'a', 's', 'sc'], - 'default'), - 'cpp_std': coredata.UserComboOption('C++ language standard to use', - ['none', 'c++98', 'c++03', 'c++11', 'c++14', 'c++17', - 'gnu++98', 'gnu++03', 'gnu++11', 'gnu++14', 'gnu++17'], - 'none')}) + opts.update({ + 'eh': coredata.UserComboOption( + 'C++ exception handling type.', + ['none', 'default', 'a', 's', 'sc'], + 'default', + ), + 'std': coredata.UserComboOption( + 'C++ language standard to use', + [ + 'none', 'c++98', 'c++03', 'c++11', 'c++14', 'c++17', + 'gnu++98', 'gnu++03', 'gnu++11', 'gnu++14', 'gnu++17', + ], + 'none', + ), + }) return opts def get_option_compile_args(self, options): args = [] - std = options['cpp_std'] + std = options['std'] if std.value != 'none': args.append('-std=' + std.value) - non_msvc_eh_options(options['cpp_eh'].value, args) + non_msvc_eh_options(options['eh'].value, args) return args @@ -275,40 +292,51 @@ class GnuCPPCompiler(GnuCompiler, CPPCompiler): def get_options(self): opts = CPPCompiler.get_options(self) - opts.update({'cpp_eh': coredata.UserComboOption('C++ exception handling type.', - ['none', 'default', 'a', 's', 'sc'], - 'default'), - 'cpp_rtti': coredata.UserBooleanOption('Enable RTTI', True), - 'cpp_std': coredata.UserComboOption('C++ language standard to use', - ['none', 'c++98', 'c++03', 'c++11', 'c++14', 'c++17', 'c++1z', 'c++2a', - 'gnu++03', 'gnu++11', 'gnu++14', 'gnu++17', 'gnu++1z', 'gnu++2a'], - 'none'), - 'cpp_debugstl': coredata.UserBooleanOption('STL debug mode', - False)}) + opts.update({ + 'eh': coredata.UserComboOption( + 'C++ exception handling type.', + ['none', 'default', 'a', 's', 'sc'], + 'default', + ), + 'rtti': coredata.UserBooleanOption('Enable RTTI', True), + 'std': coredata.UserComboOption( + 'C++ language standard to use', + ['none', 'c++98', 'c++03', 'c++11', 'c++14', 'c++17', 'c++1z', 'c++2a', + 'gnu++03', 'gnu++11', 'gnu++14', 'gnu++17', 'gnu++1z', 'gnu++2a'], + 'none', + ), + 'debugstl': coredata.UserBooleanOption( + 'STL debug mode', + False, + ) + }) if self.info.is_windows() or self.info.is_cygwin(): opts.update({ - 'cpp_winlibs': coredata.UserArrayOption('Standard Win libraries to link against', - gnu_winlibs), }) + 'winlibs': coredata.UserArrayOption( + 'Standard Win libraries to link against', + gnu_winlibs, + ), + }) return opts def get_option_compile_args(self, options): args = [] - std = options['cpp_std'] + std = options['std'] if std.value != 'none': args.append(self._find_best_cpp_std(std.value)) - non_msvc_eh_options(options['cpp_eh'].value, args) + non_msvc_eh_options(options['eh'].value, args) - if not options['cpp_rtti'].value: + if not options['rtti'].value: args.append('-fno-rtti') - if options['cpp_debugstl'].value: + if options['debugstl'].value: args.append('-D_GLIBCXX_DEBUG=1') return args def get_option_link_args(self, options): if self.info.is_windows() or self.info.is_cygwin(): - return options['cpp_winlibs'].value[:] + return options['winlibs'].value[:] return [] def get_pch_use_args(self, pch_dir, header): @@ -337,15 +365,25 @@ class ElbrusCPPCompiler(GnuCPPCompiler, ElbrusCompiler): # It does not support c++/gnu++ 17 and 1z, but still does support 0x, 1y, and gnu++98. def get_options(self): opts = CPPCompiler.get_options(self) - opts.update({'cpp_eh': coredata.UserComboOption('C++ exception handling type.', - ['none', 'default', 'a', 's', 'sc'], - 'default'), - 'cpp_std': coredata.UserComboOption('C++ language standard to use', - ['none', 'c++98', 'c++03', 'c++0x', 'c++11', 'c++14', 'c++1y', - 'gnu++98', 'gnu++03', 'gnu++0x', 'gnu++11', 'gnu++14', 'gnu++1y'], - 'none'), - 'cpp_debugstl': coredata.UserBooleanOption('STL debug mode', - False)}) + opts.update({ + 'eh': coredata.UserComboOption( + 'C++ exception handling type.', + ['none', 'default', 'a', 's', 'sc'], + 'default', + ), + 'std': coredata.UserComboOption( + 'C++ language standard to use', + [ + 'none', 'c++98', 'c++03', 'c++0x', 'c++11', 'c++14', 'c++1y', + 'gnu++98', 'gnu++03', 'gnu++0x', 'gnu++11', 'gnu++14', 'gnu++1y', + ], + 'none', + ), + 'debugstl': coredata.UserBooleanOption( + 'STL debug mode', + False, + ), + }) return opts # Elbrus C++ compiler does not have lchmod, but there is only linker warning, not compiler error. @@ -361,13 +399,13 @@ class ElbrusCPPCompiler(GnuCPPCompiler, ElbrusCompiler): # Elbrus C++ compiler does not support RTTI, so don't check for it. def get_option_compile_args(self, options): args = [] - std = options['cpp_std'] + std = options['std'] if std.value != 'none': args.append(self._find_best_cpp_std(std.value)) - non_msvc_eh_options(options['cpp_eh'].value, args) + non_msvc_eh_options(options['eh'].value, args) - if options['cpp_debugstl'].value: + if options['debugstl'].value: args.append('-D_GLIBCXX_DEBUG=1') return args @@ -400,31 +438,36 @@ class IntelCPPCompiler(IntelGnuLikeCompiler, CPPCompiler): c_stds += ['c++17'] if version_compare(self.version, '>=17.0.0'): g_stds += ['gnu++14'] - opts.update({'cpp_eh': coredata.UserComboOption('C++ exception handling type.', - ['none', 'default', 'a', 's', 'sc'], - 'default'), - 'cpp_rtti': coredata.UserBooleanOption('Enable RTTI', True), - 'cpp_std': coredata.UserComboOption('C++ language standard to use', - ['none'] + c_stds + g_stds, - 'none'), - 'cpp_debugstl': coredata.UserBooleanOption('STL debug mode', - False)}) + opts.update({ + 'eh': coredata.UserComboOption( + 'C++ exception handling type.', + ['none', 'default', 'a', 's', 'sc'], + 'default', + ), + 'rtti': coredata.UserBooleanOption('Enable RTTI', True), + 'std': coredata.UserComboOption( + 'C++ language standard to use', + ['none'] + c_stds + g_stds, + 'none', + ), + 'debugstl': coredata.UserBooleanOption('STL debug mode', False), + }) return opts def get_option_compile_args(self, options): args = [] - std = options['cpp_std'] + std = options['std'] if std.value != 'none': remap_cpp03 = { 'c++03': 'c++98', 'gnu++03': 'gnu++98' } args.append('-std=' + remap_cpp03.get(std.value, std.value)) - if options['cpp_eh'].value == 'none': + if options['eh'].value == 'none': args.append('-fno-exceptions') - if not options['cpp_rtti'].value: + if not options['rtti'].value: args.append('-fno-rtti') - if options['cpp_debugstl'].value: + if options['debugstl'].value: args.append('-D_GLIBCXX_DEBUG=1') return args @@ -449,24 +492,32 @@ class VisualStudioLikeCPPCompilerMixin: } def get_option_link_args(self, options): - return options['cpp_winlibs'].value[:] + return options['winlibs'].value[:] def _get_options_impl(self, opts, cpp_stds: T.List[str]): - opts.update({'cpp_eh': coredata.UserComboOption('C++ exception handling type.', - ['none', 'default', 'a', 's', 'sc'], - 'default'), - 'cpp_rtti': coredata.UserBooleanOption('Enable RTTI', True), - 'cpp_std': coredata.UserComboOption('C++ language standard to use', - cpp_stds, - 'none'), - 'cpp_winlibs': coredata.UserArrayOption('Windows libs to link against.', - msvc_winlibs)}) + opts.update({ + 'eh': coredata.UserComboOption( + 'C++ exception handling type.', + ['none', 'default', 'a', 's', 'sc'], + 'default', + ), + 'rtti': coredata.UserBooleanOption('Enable RTTI', True), + 'std': coredata.UserComboOption( + 'C++ language standard to use', + cpp_stds, + 'none', + ), + 'winlibs': coredata.UserArrayOption( + 'Windows libs to link against.', + msvc_winlibs, + ), + }) return opts def get_option_compile_args(self, options): args = [] - eh = options['cpp_eh'] + eh = options['eh'] if eh.value == 'default': args.append('/EHsc') elif eh.value == 'none': @@ -474,10 +525,10 @@ class VisualStudioLikeCPPCompilerMixin: else: args.append('/EH' + eh.value) - if not options['cpp_rtti'].value: + if not options['rtti'].value: args.append('/GR-') - permissive, ver = self.VC_VERSION_MAP[options['cpp_std'].value] + permissive, ver = self.VC_VERSION_MAP[options['std'].value] if ver is not None: args.append('/std:c++{}'.format(ver)) @@ -504,17 +555,17 @@ class CPP11AsCPP14Mixin: # which means setting the C++ standard version to C++14, in compilers that support it # (i.e., after VS2015U3) # if one is using anything before that point, one cannot set the standard. - if options['cpp_std'].value in {'vc++11', 'c++11'}: + if options['std'].value in {'vc++11', 'c++11'}: mlog.warning(self.id, 'does not support C++11;', 'attempting best effort; setting the standard to C++14', once=True) # Don't mutate anything we're going to change, we need to use # deepcopy since we're messing with members, and we can't simply # copy the members because the option proxy doesn't support it. options = copy.deepcopy(options) - if options['cpp_std'].value == 'vc++11': - options['cpp_std'].value = 'vc++14' + if options['std'].value == 'vc++11': + options['std'].value = 'vc++14' else: - options['cpp_std'].value = 'c++14' + options['std'].value = 'c++14' return super().get_option_compile_args(options) @@ -537,10 +588,10 @@ class VisualStudioCPPCompiler(CPP11AsCPP14Mixin, VisualStudioLikeCPPCompilerMixi return self._get_options_impl(super().get_options(), cpp_stds) def get_option_compile_args(self, options): - if options['cpp_std'].value != 'none' and version_compare(self.version, '<19.00.24210'): + if options['std'].value != 'none' and version_compare(self.version, '<19.00.24210'): mlog.warning('This version of MSVC does not support cpp_std arguments') options = copy.copy(options) - options['cpp_std'].value = 'none' + options['std'].value = 'none' args = super().get_option_compile_args(options) @@ -588,14 +639,18 @@ class ArmCPPCompiler(ArmCompiler, CPPCompiler): def get_options(self): opts = CPPCompiler.get_options(self) - opts.update({'cpp_std': coredata.UserComboOption('C++ language standard to use', - ['none', 'c++03', 'c++11'], - 'none')}) + opts.update({ + 'std': coredata.UserComboOption( + 'C++ language standard to use', + ['none', 'c++03', 'c++11'], + 'none', + ), + }) return opts def get_option_compile_args(self, options): args = [] - std = options['cpp_std'] + std = options['std'] if std.value == 'c++11': args.append('--cpp11') elif std.value == 'c++03': diff --git a/mesonbuild/compilers/fortran.py b/mesonbuild/compilers/fortran.py index 16a93f5..01283a1 100644 --- a/mesonbuild/compilers/fortran.py +++ b/mesonbuild/compilers/fortran.py @@ -186,14 +186,18 @@ class GnuFortranCompiler(GnuCompiler, FortranCompiler): fortran_stds += ['f2008'] if version_compare(self.version, '>=8.0.0'): fortran_stds += ['f2018'] - opts.update({'fortran_std': coredata.UserComboOption('Fortran language standard to use', - ['none'] + fortran_stds, - 'none')}) + opts.update({ + 'std': coredata.UserComboOption( + 'Fortran language standard to use', + ['none'] + fortran_stds, + 'none', + ), + }) return opts def get_option_compile_args(self, options) -> T.List[str]: args = [] - std = options['fortran_std'] + std = options['std'] if std.value != 'none': args.append('-std=' + std.value) return args @@ -292,14 +296,18 @@ class IntelFortranCompiler(IntelGnuLikeCompiler, FortranCompiler): def get_options(self): opts = FortranCompiler.get_options(self) fortran_stds = ['legacy', 'f95', 'f2003', 'f2008', 'f2018'] - opts.update({'fortran_std': coredata.UserComboOption('Fortran language standard to use', - ['none'] + fortran_stds, - 'none')}) + opts.update({ + 'std': coredata.UserComboOption( + 'Fortran language standard to use', + ['none'] + fortran_stds, + 'none', + ), + }) return opts def get_option_compile_args(self, options) -> T.List[str]: args = [] - std = options['fortran_std'] + std = options['std'] stds = {'legacy': 'none', 'f95': 'f95', 'f2003': 'f03', 'f2008': 'f08', 'f2018': 'f18'} if std.value != 'none': args.append('-stand=' + stds[std.value]) @@ -342,14 +350,18 @@ class IntelClFortranCompiler(IntelVisualStudioLikeCompiler, FortranCompiler): def get_options(self): opts = FortranCompiler.get_options(self) fortran_stds = ['legacy', 'f95', 'f2003', 'f2008', 'f2018'] - opts.update({'fortran_std': coredata.UserComboOption('Fortran language standard to use', - ['none'] + fortran_stds, - 'none')}) + opts.update({ + 'std': coredata.UserComboOption( + 'Fortran language standard to use', + ['none'] + fortran_stds, + 'none', + ), + }) return opts def get_option_compile_args(self, options) -> T.List[str]: args = [] - std = options['fortran_std'] + std = options['std'] stds = {'legacy': 'none', 'f95': 'f95', 'f2003': 'f03', 'f2008': 'f08', 'f2018': 'f18'} if std.value != 'none': args.append('/stand:' + stds[std.value]) diff --git a/mesonbuild/compilers/mixins/clang.py b/mesonbuild/compilers/mixins/clang.py index 92f8c5f..1c0ee45 100644 --- a/mesonbuild/compilers/mixins/clang.py +++ b/mesonbuild/compilers/mixins/clang.py @@ -112,3 +112,8 @@ class ClangCompiler(GnuLikeCompiler): 'Cannot find linker {}.'.format(linker)) return ['-fuse-ld={}'.format(linker)] return super().use_linker_args(linker) + + def get_has_func_attribute_extra_args(self, name): + # Clang only warns about unknown or ignored attributes, so force an + # error. + return ['-Werror=attributes'] diff --git a/mesonbuild/compilers/mixins/clike.py b/mesonbuild/compilers/mixins/clike.py index 41848bc..24f4796 100644 --- a/mesonbuild/compilers/mixins/clike.py +++ b/mesonbuild/compilers/mixins/clike.py @@ -1115,6 +1115,12 @@ class CLikeCompiler: m = pattern.match(ret) return ret + def get_has_func_attribute_extra_args(self, name): + # Most compilers (such as GCC and Clang) only warn about unknown or + # ignored attributes, so force an error. Overriden in GCC and Clang + # mixins. + return ['-Werror'] + def has_func_attribute(self, name, env): # Just assume that if we're not on windows that dllimport and dllexport # don't work @@ -1123,6 +1129,5 @@ class CLikeCompiler: if name in ['dllimport', 'dllexport']: return False, False - # Clang and GCC both return warnings if the __attribute__ is undefined, - # so set -Werror - return self.compiles(self.attribute_check_func(name), env, extra_args='-Werror') + return self.compiles(self.attribute_check_func(name), env, + extra_args=self.get_has_func_attribute_extra_args(name)) diff --git a/mesonbuild/compilers/mixins/gnu.py b/mesonbuild/compilers/mixins/gnu.py index 29552f3..3526a91 100644 --- a/mesonbuild/compilers/mixins/gnu.py +++ b/mesonbuild/compilers/mixins/gnu.py @@ -369,3 +369,8 @@ class GnuCompiler(GnuLikeCompiler): if self.language in {'c', 'objc'} and 'is valid for C++/ObjC++' in p.stde: result = False return result, p.cached + + def get_has_func_attribute_extra_args(self, name): + # GCC only warns about unknown or ignored attributes, so force an + # error. + return ['-Werror=attributes'] diff --git a/mesonbuild/coredata.py b/mesonbuild/coredata.py index e71d674..0b79084 100644 --- a/mesonbuild/coredata.py +++ b/mesonbuild/coredata.py @@ -17,7 +17,7 @@ import pickle, os, uuid import sys from itertools import chain from pathlib import PurePath -from collections import OrderedDict +from collections import OrderedDict, defaultdict from .mesonlib import ( MesonException, MachineChoice, PerMachine, OrderedSet, default_libdir, default_libexecdir, default_prefix, split_args @@ -370,7 +370,10 @@ class CoreData: self.builtins_per_machine = PerMachine({}, {}) self.backend_options = {} # : OptionDictType self.user_options = {} # : OptionDictType - self.compiler_options = PerMachine({}, {}) + self.compiler_options = PerMachine( + defaultdict(dict), + defaultdict(dict), + ) # : PerMachine[T.defaultdict[str, OptionDictType]] self.base_options = {} # : OptionDictType self.cross_files = self.__load_config_files(options, scratch_dir, 'cross') self.compilers = PerMachine(OrderedDict(), OrderedDict()) @@ -614,28 +617,42 @@ class CoreData: mode = 'custom' self.builtins['buildtype'].set_value(mode) - @staticmethod + @classmethod def get_prefixed_options_per_machine( + cls, options_per_machine # : PerMachine[T.Dict[str, _V]]] - ) -> T.Iterable[T.Dict[str, _V]]: - for for_machine in iter(MachineChoice): - prefix = for_machine.get_prefix() - yield { - prefix + k: v - for k, v in options_per_machine[for_machine].items() - } + ) -> T.Iterable[T.Tuple[str, _V]]: + return cls._flatten_pair_iterator( + (for_machine.get_prefix(), options_per_machine[for_machine]) + for for_machine in iter(MachineChoice) + ) + + @classmethod + def flatten_lang_iterator( + cls, + outer # : T.Iterable[T.Tuple[str, T.Dict[str, _V]]] + ) -> T.Iterable[T.Tuple[str, _V]]: + return cls._flatten_pair_iterator((lang + '_', opts) for lang, opts in outer) + + @staticmethod + def _flatten_pair_iterator( + outer # : T.Iterable[T.Tuple[str, T.Dict[str, _V]]] + ) -> T.Iterable[T.Tuple[str, _V]]: + for k0, v0 in outer: + for k1, v1 in v0.items(): + yield (k0 + k1, v1) def _get_all_nonbuiltin_options(self) -> T.Iterable[T.Dict[str, UserOption]]: yield self.backend_options yield self.user_options - yield from self.get_prefixed_options_per_machine(self.compiler_options) + yield dict(self.flatten_lang_iterator(self.get_prefixed_options_per_machine(self.compiler_options))) yield self.base_options - def _get_all_builtin_options(self) -> T.Dict[str, UserOption]: - yield from self.get_prefixed_options_per_machine(self.builtins_per_machine) + def _get_all_builtin_options(self) -> T.Iterable[T.Dict[str, UserOption]]: + yield dict(self.get_prefixed_options_per_machine(self.builtins_per_machine)) yield self.builtins - def get_all_options(self) -> T.Dict[str, UserOption]: + def get_all_options(self) -> T.Iterable[T.Dict[str, UserOption]]: yield from self._get_all_nonbuiltin_options() yield from self._get_all_builtin_options() @@ -651,10 +668,10 @@ class CoreData: raise MesonException('Tried to validate unknown option %s.' % option_name) def get_external_args(self, for_machine: MachineChoice, lang): - return self.compiler_options[for_machine][lang + '_args'].value + return self.compiler_options[for_machine][lang]['args'].value def get_external_link_args(self, for_machine: MachineChoice, lang): - return self.compiler_options[for_machine][lang + '_link_args'].value + return self.compiler_options[for_machine][lang]['link_args'].value def merge_user_options(self, options): for (name, value) in options.items(): @@ -680,9 +697,11 @@ class CoreData: assert(not self.is_cross_build()) for k, o in self.builtins_per_machine.host.items(): self.builtins_per_machine.build[k].set_value(o.value) - for k, o in self.compiler_options.host.items(): - if k in self.compiler_options.build: - self.compiler_options.build[k].set_value(o.value) + for lang, host_opts in self.compiler_options.host.items(): + build_opts = self.compiler_options.build[lang] + for k, o in host_opts.items(): + if k in build_opts: + build_opts[k].set_value(o.value) def set_options(self, options, *, subproject='', warn_unknown=True): if not self.is_cross_build(): @@ -783,13 +802,17 @@ class CoreData: 'path instead.' ) + def remove_prefix(text, prefix): + if text.startswith(prefix): + return text[len(prefix):] + return text + for k, v in env.cmd_line_options.items(): if subproject: if not k.startswith(subproject + ':'): continue elif k not in builtin_options.keys() \ - and 'build.' + k not in builtin_options_per_machine.keys() \ - and k not in builtin_options_per_machine.keys(): + and remove_prefix(k, 'build.') not in builtin_options_per_machine.keys(): if ':' in k: continue if optinterpreter.is_invalid_name(k, log=False): @@ -803,20 +826,18 @@ class CoreData: """Add global language arguments that are needed before compiler/linker detection.""" from .compilers import compilers - optprefix = lang + '_' for k, o in compilers.get_global_options( lang, comp, for_machine, env.is_cross_build(), env.properties[for_machine]).items(): - if not k.startswith(optprefix): - raise MesonException('Internal error, %s has incorrect prefix.' % k) # prefixed compiler options affect just this machine opt_prefix = for_machine.get_prefix() - if opt_prefix + k in env.cmd_line_options: - o.set_value(env.cmd_line_options[opt_prefix + k]) - self.compiler_options[for_machine].setdefault(k, o) + user_k = opt_prefix + lang + '_' + k + if user_k in env.cmd_line_options: + o.set_value(env.cmd_line_options[user_k]) + self.compiler_options[for_machine][lang].setdefault(k, o) def process_new_compiler(self, lang: str, comp: T.Type['Compiler'], env: 'Environment') -> None: from . import compilers @@ -824,15 +845,13 @@ class CoreData: self.compilers[comp.for_machine][lang] = comp enabled_opts = [] - optprefix = lang + '_' for k, o in comp.get_options().items(): - if not k.startswith(optprefix): - raise MesonException('Internal error, %s has incorrect prefix.' % k) # prefixed compiler options affect just this machine opt_prefix = comp.for_machine.get_prefix() - if opt_prefix + k in env.cmd_line_options: - o.set_value(env.cmd_line_options[opt_prefix + k]) - self.compiler_options[comp.for_machine].setdefault(k, o) + user_k = opt_prefix + lang + '_' + k + if user_k in env.cmd_line_options: + o.set_value(env.cmd_line_options[user_k]) + self.compiler_options[comp.for_machine][lang].setdefault(k, o) enabled_opts = [] for optname in comp.base_options: diff --git a/mesonbuild/envconfig.py b/mesonbuild/envconfig.py index 339f980..25b3c7f 100644 --- a/mesonbuild/envconfig.py +++ b/mesonbuild/envconfig.py @@ -93,15 +93,15 @@ class MesonConfigFile: # Windows paths... value = value.replace('\\', '\\\\') if ' ' in entry or '\t' in entry or "'" in entry or '"' in entry: - raise EnvironmentException('Malformed variable name %s in cross file..' % entry) + raise EnvironmentException('Malformed variable name {} in cross file..'.format(entry)) try: res = eval(value, {'__builtins__': None}, {'true': True, 'false': False}) except Exception: - raise EnvironmentException('Malformed value in cross file variable %s.' % entry) + raise EnvironmentException('Malformed value in cross file variable {}.'.format(entry)) for i in (res if isinstance(res, list) else [res]): if not isinstance(i, (str, int, bool)): - raise EnvironmentException('Malformed value in cross file variable %s.' % entry) + raise EnvironmentException('Malformed value in cross file variable {}.'.format(entry)) section[entry] = res @@ -224,11 +224,11 @@ class MachineInfo: cpu_family = literal['cpu_family'] if cpu_family not in known_cpu_families: - mlog.warning('Unknown CPU family %s, please report this at https://github.com/mesonbuild/meson/issues/new' % cpu_family) + mlog.warning('Unknown CPU family {}, please report this at https://github.com/mesonbuild/meson/issues/new'.format(cpu_family)) endian = literal['endian'] if endian not in ('little', 'big'): - mlog.warning('Unknown endian %s' % endian) + mlog.warning('Unknown endian {}'.format(endian)) return cls(literal['system'], cpu_family, literal['cpu'], endian) diff --git a/mesonbuild/environment.py b/mesonbuild/environment.py index 11bc76c..64efda6 100644 --- a/mesonbuild/environment.py +++ b/mesonbuild/environment.py @@ -771,7 +771,7 @@ class Environment: elif isinstance(comp_class.LINKER_PREFIX, list): check_args = comp_class.LINKER_PREFIX + ['/logo'] + comp_class.LINKER_PREFIX + ['--version'] - check_args += self.coredata.compiler_options[for_machine][comp_class.language + '_args'].value + check_args += self.coredata.compiler_options[for_machine][comp_class.language]['args'].value override = [] # type: T.List[str] value = self.lookup_binary_entry(for_machine, comp_class.language + '_ld') @@ -833,7 +833,7 @@ class Environment: """ self.coredata.add_lang_args(comp_class.language, comp_class, for_machine, self) extra_args = T.cast(T.List[str], extra_args or []) - extra_args += self.coredata.compiler_options[for_machine][comp_class.language + '_args'].value + extra_args += self.coredata.compiler_options[for_machine][comp_class.language]['args'].value if isinstance(comp_class.LINKER_PREFIX, str): check_args = [comp_class.LINKER_PREFIX + '--version'] + extra_args @@ -1360,7 +1360,7 @@ class Environment: try: p, out, err = Popen_safe(exelist + ['-version']) except OSError: - raise EnvironmentException('Could not execute Java compiler "%s"' % ' '.join(exelist)) + raise EnvironmentException('Could not execute Java compiler "{}"'.format(' '.join(exelist))) if 'javac' in out or 'javac' in err: version = search_version(err if 'javac' in err else out) if not version or version == 'unknown version': @@ -1408,7 +1408,7 @@ class Environment: try: p, out = Popen_safe(exelist + ['--version'])[0:2] except OSError: - raise EnvironmentException('Could not execute Vala compiler "%s"' % ' '.join(exelist)) + raise EnvironmentException('Could not execute Vala compiler "{}"'.format(' '.join(exelist))) version = search_version(out) if 'Vala' in out: comp_class = ValaCompiler @@ -1610,7 +1610,7 @@ class Environment: try: p, _, err = Popen_safe(exelist + ['-v']) except OSError: - raise EnvironmentException('Could not execute Swift compiler "%s"' % ' '.join(exelist)) + raise EnvironmentException('Could not execute Swift compiler "{}"'.format(' '.join(exelist))) version = search_version(err) if 'Swift' in err: # As for 5.0.1 swiftc *requires* a file to check the linker: @@ -1730,7 +1730,7 @@ class Environment: if p.returncode == 1 and err.startswith('ar: bad option: --'): # Solaris return ArLinker(linker) self._handle_exceptions(popen_exceptions, linkers, 'linker') - raise EnvironmentException('Unknown static linker "%s"' % ' '.join(linkers)) + raise EnvironmentException('Unknown static linker "{}"'.format(' '.join(linkers))) def get_source_dir(self): return self.source_dir diff --git a/mesonbuild/interpreter.py b/mesonbuild/interpreter.py index c1636d7..48b6bd6 100644 --- a/mesonbuild/interpreter.py +++ b/mesonbuild/interpreter.py @@ -42,7 +42,6 @@ import re import shlex import subprocess import collections -from itertools import chain import functools import typing as T @@ -1112,7 +1111,7 @@ class CompilerHolder(InterpreterObject): args += self.compiler.get_include_args(idir, False) if not nobuiltins: for_machine = Interpreter.machine_from_native_kwarg(kwargs) - opts = self.environment.coredata.compiler_options[for_machine] + opts = self.environment.coredata.compiler_options[for_machine][self.compiler.language] args += self.compiler.get_option_compile_args(opts) if mode == 'link': args += self.compiler.get_option_link_args(opts) @@ -2422,14 +2421,14 @@ class Interpreter(InterpreterBase): return f = os.path.normpath(f.relative_name()) elif os.path.isfile(f) and not f.startswith('/dev'): - srcdir = self.environment.get_source_dir() - builddir = self.environment.get_build_dir() - f = os.path.normpath(f) - rel_path = mesonlib.relpath(f, start=srcdir) - if not rel_path.startswith('..'): - f = rel_path - elif not mesonlib.relpath(f, start=builddir).startswith('..'): + srcdir = Path(self.environment.get_source_dir()) + builddir = Path(self.environment.get_build_dir()) + f = Path(f).resolve() + if builddir in f.parents: return + if srcdir in f.parents: + f = f.relative_to(srcdir) + f = str(f) else: return if f not in self.build_def_files: @@ -2800,11 +2799,12 @@ external dependencies (including libraries) must go to "dependencies".''') if self.is_subproject(): optname = self.subproject + ':' + optname - for opts in chain( - [self.coredata.base_options, compilers.base_options, self.coredata.builtins], - self.coredata.get_prefixed_options_per_machine(self.coredata.builtins_per_machine), - self.coredata.get_prefixed_options_per_machine(self.coredata.compiler_options), - ): + for opts in [ + self.coredata.base_options, compilers.base_options, self.coredata.builtins, + dict(self.coredata.get_prefixed_options_per_machine(self.coredata.builtins_per_machine)), + dict(self.coredata.flatten_lang_iterator( + self.coredata.get_prefixed_options_per_machine(self.coredata.compiler_options))), + ]: v = opts.get(optname) if v is None or v.yielding: v = opts.get(raw_optname) diff --git a/mesonbuild/linkers.py b/mesonbuild/linkers.py index fa898d0..44c720f 100644 --- a/mesonbuild/linkers.py +++ b/mesonbuild/linkers.py @@ -201,7 +201,7 @@ class CcrxLinker(StaticLinker): return False def get_output_args(self, target: str) -> T.List[str]: - return ['-output=%s' % target] + return ['-output={}'.format(target)] def get_linker_always_args(self) -> T.List[str]: return ['-nologo', '-form=library'] @@ -217,7 +217,7 @@ class Xc16Linker(StaticLinker): return False def get_output_args(self, target: str) -> T.List[str]: - return ['%s' % target] + return ['{}'.format(target)] def get_linker_always_args(self) -> T.List[str]: return ['rcs'] @@ -233,7 +233,7 @@ class C2000Linker(StaticLinker): return False def get_output_args(self, target: str) -> T.List[str]: - return ['%s' % target] + return ['{}'.format(target)] def get_linker_always_args(self) -> T.List[str]: return ['-r'] @@ -781,7 +781,7 @@ class CcrxDynamicLinker(DynamicLinker): return [] def get_output_args(self, outputname: str) -> T.List[str]: - return ['-output=%s' % outputname] + return ['-output={}'.format(outputname)] def get_search_args(self, dirname: str) -> 'T.NoReturn': raise EnvironmentError('rlink.exe does not have a search dir argument') @@ -819,7 +819,7 @@ class Xc16DynamicLinker(DynamicLinker): return [] def get_output_args(self, outputname: str) -> T.List[str]: - return ['-o%s' % outputname] + return ['-o{}'.format(outputname)] def get_search_args(self, dirname: str) -> 'T.NoReturn': raise EnvironmentError('xc16-gcc.exe does not have a search dir argument') @@ -862,7 +862,7 @@ class C2000DynamicLinker(DynamicLinker): return [] def get_output_args(self, outputname: str) -> T.List[str]: - return ['-z', '--output_file=%s' % outputname] + return ['-z', '--output_file={}'.format(outputname)] def get_search_args(self, dirname: str) -> 'T.NoReturn': raise EnvironmentError('cl2000.exe does not have a search dir argument') diff --git a/mesonbuild/mconf.py b/mesonbuild/mconf.py index cadc1f5..05e9518 100644 --- a/mesonbuild/mconf.py +++ b/mesonbuild/mconf.py @@ -97,9 +97,9 @@ class Conf: else: print('{0:{width[0]}} {1:{width[1]}} {3}'.format(*line, width=col_widths)) - def split_options_per_subproject(self, options): + def split_options_per_subproject(self, options_iter): result = {} - for k, o in options.items(): + for k, o in options_iter: subproject = '' if ':' in k: subproject, optname = k.split(':') @@ -211,10 +211,15 @@ class Conf: return 'build.' + k return k[:idx + 1] + 'build.' + k[idx + 1:] - core_options = self.split_options_per_subproject(core_options) - host_compiler_options = self.split_options_per_subproject(self.coredata.compiler_options.host) - build_compiler_options = self.split_options_per_subproject({insert_build_prefix(k): o for k, o in self.coredata.compiler_options.build.items()}) - project_options = self.split_options_per_subproject(self.coredata.user_options) + core_options = self.split_options_per_subproject(core_options.items()) + host_compiler_options = self.split_options_per_subproject( + self.coredata.flatten_lang_iterator( + self.coredata.compiler_options.host.items())) + build_compiler_options = self.split_options_per_subproject( + self.coredata.flatten_lang_iterator( + (insert_build_prefix(k), o) + for k, o in self.coredata.compiler_options.build.items())) + project_options = self.split_options_per_subproject(self.coredata.user_options.items()) show_build_options = self.default_values_only or self.build.environment.is_cross_build() self.add_section('Main project options') diff --git a/mesonbuild/mesonmain.py b/mesonbuild/mesonmain.py index b6b11df..fc9d6a6 100644 --- a/mesonbuild/mesonmain.py +++ b/mesonbuild/mesonmain.py @@ -182,7 +182,7 @@ def ensure_stdout_accepts_unicode(): def run(original_args, mainfile): if sys.version_info < (3, 5): print('Meson works correctly only with python 3.5+.') - print('You have python %s.' % sys.version) + print('You have python {}.'.format(sys.version)) print('Please update your environment') return 1 diff --git a/mesonbuild/mintro.py b/mesonbuild/mintro.py index 9278584..d5516d4 100644 --- a/mesonbuild/mintro.py +++ b/mesonbuild/mintro.py @@ -253,9 +253,16 @@ def list_buildoptions(coredata: cdata.CoreData, subprojects: T.Optional[T.List[s ) add_keys(coredata.backend_options, 'backend') add_keys(coredata.base_options, 'base') - add_keys(coredata.compiler_options.host, 'compiler', machine='host') add_keys( - {'build.' + k: o for k, o in coredata.compiler_options.build.items()}, + dict(coredata.flatten_lang_iterator(coredata.compiler_options.host.items())), + 'compiler', + machine='host', + ) + add_keys( + { + 'build.' + k: o for k, o in + coredata.flatten_lang_iterator(coredata.compiler_options.build.items()) + }, 'compiler', machine='build', ) diff --git a/mesonbuild/mlog.py b/mesonbuild/mlog.py index a5fb320..8cbd248 100644 --- a/mesonbuild/mlog.py +++ b/mesonbuild/mlog.py @@ -302,7 +302,7 @@ def exception(e: Exception, prefix: T.Optional[AnsiDecorator] = None) -> None: # Mypy doesn't follow hasattr, and it's pretty easy to visually inspect # that this is correct, so we'll just ignore it. path = get_relative_path(Path(e.file), Path(os.getcwd())) # type: ignore - args.append('%s:%d:%d:' % (path, e.lineno, e.colno)) # type: ignore + args.append('{}:{}:{}:'.format(path, e.lineno, e.colno)) # type: ignore if prefix: args.append(prefix) args.append(str(e)) diff --git a/mesonbuild/modules/sourceset.py b/mesonbuild/modules/sourceset.py index a14bd70..e23e12e 100644 --- a/mesonbuild/modules/sourceset.py +++ b/mesonbuild/modules/sourceset.py @@ -150,7 +150,7 @@ class SourceSetHolder(MutableInterpreterObject, ObjectHolder): if isinstance(config_data, dict): def _get_from_config_data(key): if strict and key not in config_data: - raise InterpreterException('Entry %s not in configuration dictionary.' % key) + raise InterpreterException('Entry {} not in configuration dictionary.'.format(key)) return config_data.get(key, False) else: config_cache = dict() diff --git a/mesonbuild/rewriter.py b/mesonbuild/rewriter.py index 785451f..7730ab6 100644 --- a/mesonbuild/rewriter.py +++ b/mesonbuild/rewriter.py @@ -469,8 +469,8 @@ class Rewriter: **{'build.' + k: o for k, o in cdata.builtins_per_machine.build.items()}, **cdata.backend_options, **cdata.base_options, - **cdata.compiler_options.host, - **{'build.' + k: o for k, o in cdata.compiler_options.build.items()}, + **(dict(cdata.flatten_lang_iterator(cdata.compiler_options.host.items()))), + **{'build.' + k: o for k, o in cdata.flatten_lang_iterator(cdata.compiler_options.build.items())}, **cdata.user_options, } diff --git a/mesonbuild/scripts/gtkdochelper.py b/mesonbuild/scripts/gtkdochelper.py index ddcc8c0..6b174a6 100644 --- a/mesonbuild/scripts/gtkdochelper.py +++ b/mesonbuild/scripts/gtkdochelper.py @@ -59,6 +59,7 @@ def gtkdoc_run_check(cmd, cwd, library_paths=None): if 'PATH' in env: library_paths.extend(env['PATH'].split(os.pathsep)) env['PATH'] = os.pathsep.join(library_paths) + cmd.insert(0, sys.executable) else: if 'LD_LIBRARY_PATH' in env: library_paths.extend(env['LD_LIBRARY_PATH'].split(os.pathsep)) @@ -73,7 +74,14 @@ def gtkdoc_run_check(cmd, cwd, library_paths=None): err_msg.append(out) raise MesonException('\n'.join(err_msg)) elif out: - print(out) + # Unfortunately Windows cmd.exe consoles may be using a codepage + # that might choke print() with a UnicodeEncodeError, so let's + # ignore such errors for now, as a compromise as we are outputting + # console output here... + try: + print(out) + except UnicodeEncodeError: + pass def build_gtkdoc(source_root, build_root, doc_subdir, src_subdirs, main_file, module, module_version, diff --git a/run_tests.py b/run_tests.py index 3237e85..005d9a0 100755 --- a/run_tests.py +++ b/run_tests.py @@ -126,7 +126,7 @@ def get_fake_env(sdir='', bdir=None, prefix='', opts=None): if opts is None: opts = get_fake_options(prefix) env = Environment(sdir, bdir, opts) - env.coredata.compiler_options.host['c_args'] = FakeCompilerOptions() + env.coredata.compiler_options.host['c']['args'] = FakeCompilerOptions() env.machines.host.cpu_family = 'x86_64' # Used on macOS inside find_library return env diff --git a/run_unittests.py b/run_unittests.py index cc294dd..831e53f 100755 --- a/run_unittests.py +++ b/run_unittests.py @@ -836,7 +836,7 @@ class InternalTests(unittest.TestCase): env = get_fake_env() compiler = env.detect_c_compiler(MachineChoice.HOST) env.coredata.compilers.host = {'c': compiler} - env.coredata.compiler_options.host['c_link_args'] = FakeCompilerOptions() + env.coredata.compiler_options.host['c']['link_args'] = FakeCompilerOptions() p1 = Path(tmpdir) / '1' p2 = Path(tmpdir) / '2' p1.mkdir() @@ -3745,11 +3745,11 @@ recommended as it is not supported on some platforms''') # c_args value should be parsed with split_args self.init(testdir, extra_args=['-Dc_args=-Dfoo -Dbar "-Dthird=one two"']) obj = mesonbuild.coredata.load(self.builddir) - self.assertEqual(obj.compiler_options.host['c_args'].value, ['-Dfoo', '-Dbar', '-Dthird=one two']) + self.assertEqual(obj.compiler_options.host['c']['args'].value, ['-Dfoo', '-Dbar', '-Dthird=one two']) self.setconf('-Dc_args="foo bar" one two') obj = mesonbuild.coredata.load(self.builddir) - self.assertEqual(obj.compiler_options.host['c_args'].value, ['foo bar', 'one', 'two']) + self.assertEqual(obj.compiler_options.host['c']['args'].value, ['foo bar', 'one', 'two']) self.wipe() self.init(testdir, extra_args=['-Dset_percent_opt=myoption%']) @@ -3767,7 +3767,7 @@ recommended as it is not supported on some platforms''') self.assertEqual(obj.builtins['bindir'].value, 'bar') self.assertEqual(obj.builtins['buildtype'].value, 'release') self.assertEqual(obj.base_options['b_sanitize'].value, 'thread') - self.assertEqual(obj.compiler_options.host['c_args'].value, ['-Dbar']) + self.assertEqual(obj.compiler_options.host['c']['args'].value, ['-Dbar']) self.setconf(['--bindir=bar', '--bindir=foo', '-Dbuildtype=release', '-Dbuildtype=plain', '-Db_sanitize=thread', '-Db_sanitize=address', @@ -3776,7 +3776,7 @@ recommended as it is not supported on some platforms''') self.assertEqual(obj.builtins['bindir'].value, 'foo') self.assertEqual(obj.builtins['buildtype'].value, 'plain') self.assertEqual(obj.base_options['b_sanitize'].value, 'address') - self.assertEqual(obj.compiler_options.host['c_args'].value, ['-Dfoo']) + self.assertEqual(obj.compiler_options.host['c']['args'].value, ['-Dfoo']) self.wipe() except KeyError: # Ignore KeyError, it happens on CI for compilers that does not @@ -4588,6 +4588,34 @@ recommended as it is not supported on some platforms''') self._run([*self.meson_command, 'compile', '-C', self.builddir, '--clean']) self.assertPathDoesNotExist(os.path.join(self.builddir, prog)) + def test_spurious_reconfigure_built_dep_file(self): + testdir = os.path.join(self.unit_test_dir, '74 dep files') + + # Regression test: Spurious reconfigure was happening when build + # directory is inside source directory. + # See https://gitlab.freedesktop.org/gstreamer/gst-build/-/issues/85. + srcdir = os.path.join(self.builddir, 'srctree') + shutil.copytree(testdir, srcdir) + builddir = os.path.join(srcdir, '_build') + self.change_builddir(builddir) + + self.init(srcdir) + self.build() + + # During first configure the file did not exist so no dependency should + # have been set. A rebuild should not trigger a reconfigure. + self.clean() + out = self.build() + self.assertNotIn('Project configured', out) + + self.init(srcdir, extra_args=['--reconfigure']) + + # During the reconfigure the file did exist, but is inside build + # directory, so no dependency should have been set. A rebuild should not + # trigger a reconfigure. + self.clean() + out = self.build() + self.assertNotIn('Project configured', out) class FailureTests(BasePlatformTests): ''' @@ -5587,8 +5615,6 @@ class LinuxlikeTests(BasePlatformTests): self.assertEqual(Oargs, [Oflag, '-O0']) def _test_stds_impl(self, testdir, compiler, p: str): - lang_std = p + '_std' - has_cpp17 = (compiler.get_id() not in {'clang', 'gcc'} or compiler.get_id() == 'clang' and _clang_at_least(compiler, '>=5.0.0', '>=9.1') or compiler.get_id() == 'gcc' and version_compare(compiler.version, '>=5.0.0')) @@ -5601,7 +5627,8 @@ class LinuxlikeTests(BasePlatformTests): # Check that all the listed -std=xxx options for this compiler work just fine when used # https://en.wikipedia.org/wiki/Xcode#Latest_versions # https://www.gnu.org/software/gcc/projects/cxx-status.html - for v in compiler.get_options()[lang_std].choices: + for v in compiler.get_options()['std'].choices: + lang_std = p + '_std' # we do it like this to handle gnu++17,c++17 and gnu17,c17 cleanly # thus, C++ first if '++17' in v and not has_cpp17: diff --git a/test cases/unit/74 dep files/foo.c b/test cases/unit/74 dep files/foo.c new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/test cases/unit/74 dep files/foo.c diff --git a/test cases/unit/74 dep files/meson.build b/test cases/unit/74 dep files/meson.build new file mode 100644 index 0000000..4829f56 --- /dev/null +++ b/test cases/unit/74 dep files/meson.build @@ -0,0 +1,16 @@ +project('test', 'c') + +python = import('python').find_installation() + +lib = library('foo', 'foo.c') + +# The library does not yet exist but we can already use its path during +# configuration. This should not trigger a reconfigure when the library is +# rebuilt. +configure_file( + output: 'out.txt', + capture: true, + command: [python, '-c', 'import sys; print(sys.argv[1])', lib.full_path()], +) + +message('Project configured') |