diff options
27 files changed, 311 insertions, 75 deletions
diff --git a/azure-pipelines.yml b/azure-pipelines.yml index d408762..3d04ffc 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -57,6 +57,29 @@ jobs: architecture: 'x64' - template: ci/azure-steps.yml +- job: vs2019 + pool: + vmImage: windows-2019 + + strategy: + matrix: + vc2019x64ninja: + arch: x64 + compiler: msvc2019 + backend: ninja + vc2019x64vs: + arch: x64 + compiler: msvc2019 + backend: vs2019 + + steps: + - task: UsePythonVersion@0 + inputs: + versionSpec: '3.7' + addToPath: true + architecture: 'x64' + - template: ci/azure-steps.yml + - job: cygwin pool: vmImage: VS2017-Win2016 diff --git a/ci/azure-steps.yml b/ci/azure-steps.yml index 9d143db..abbed65 100644 --- a/ci/azure-steps.yml +++ b/ci/azure-steps.yml @@ -99,6 +99,8 @@ steps: # import visual studio variables if ($env:compiler -eq 'msvc2015') { $vcvars = "C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\vcvarsall.bat" + } elseif($env:compiler -eq 'msvc2019') { + $vcvars = "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VC\Auxiliary\Build\vcvarsall.bat" } else { $vcvars = "C:\Program Files (x86)\Microsoft Visual Studio\2017\Enterprise\VC\Auxiliary\Build\vcvarsall.bat" } @@ -154,7 +156,10 @@ steps: echo "" echo "=== Start running tests ===" - python run_tests.py --backend $(backend) + # Starting from VS2019 Powershell(?) will fail the test run + # if it prints anything to stderr. Python's test runner + # does that by default so we need to forward it. + cmd /c 'python 2>&1' run_tests.py --backend $(backend) - task: PublishTestResults@2 inputs: diff --git a/docs/markdown/Configuring-a-build-directory.md b/docs/markdown/Configuring-a-build-directory.md index 73585e2..b0fb574 100644 --- a/docs/markdown/Configuring-a-build-directory.md +++ b/docs/markdown/Configuring-a-build-directory.md @@ -24,7 +24,7 @@ sample output for a simple project. Option Current Value Possible Values Description ------ ------------- --------------- ----------- auto_features auto [enabled, disabled, auto] Override value of all 'auto' features - backend ninja [ninja, vs, vs2010, vs2015, vs2017, xcode] Backend to use + backend ninja [ninja, vs, vs2010, vs2015, vs2017, vs2019, xcode] Backend to use buildtype release [plain, debug, debugoptimized, release, minsize, custom] Build type to use debug false [true, false] Debug default_library shared [shared, static, both] Default library type diff --git a/docs/markdown/Dependencies.md b/docs/markdown/Dependencies.md index 2789ee0..50dbcf5 100644 --- a/docs/markdown/Dependencies.md +++ b/docs/markdown/Dependencies.md @@ -394,7 +394,7 @@ The `language` keyword may used. *(added 0.51.0)* -`method` may be `auto` or `config-tool`. +`method` may be `auto`, `config-tool` or `pkg-config`. ## Python3 diff --git a/docs/markdown/Reference-manual.md b/docs/markdown/Reference-manual.md index 5825004..056612d 100644 --- a/docs/markdown/Reference-manual.md +++ b/docs/markdown/Reference-manual.md @@ -1066,8 +1066,9 @@ res2 = foo / bar ``` Builds a library that is either static, shared or both depending on -the value of `default_library` user option. You should use this -instead of [`shared_library`](#shared_library), +the value of `default_library` +user [option](https://mesonbuild.com/Builtin-options.html). +You should use this instead of [`shared_library`](#shared_library), [`static_library`](#static_library) or [`both_libraries`](#both_libraries) most of the time. This allows you to toggle your entire project (including subprojects) from shared to @@ -1552,7 +1553,8 @@ the following methods. `MESON_SOURCE_ROOT` and `MESON_BUILD_ROOT` set. - `backend()` *(added 0.37.0)* returns a string representing the - current backend: `ninja`, `vs2010`, `vs2015`, `vs2017`, or `xcode`. + current backend: `ninja`, `vs2010`, `vs2015`, `vs2017`, `vs2019`, + or `xcode`. - `build_root()` returns a string with the absolute path to the build root directory. Note: this function will return the build root of diff --git a/docs/markdown/fallback-wraptool.md b/docs/markdown/fallback-wraptool.md index 2754022..ebf8b91 100644 --- a/docs/markdown/fallback-wraptool.md +++ b/docs/markdown/fallback-wraptool.md @@ -4,21 +4,26 @@ title: fallback wraptool # In case of emergency -In case wraptool is down we have created a backup script that you can use to download wraps directly from the GitHub repos. It is not as slick and may have bugs but at least it will allow you to use wraps. +In case wraptool is down we have created a backup script that you can +use to download wraps directly from the GitHub repos. It is not as +slick and may have bugs but at least it will allow you to use wraps. ## Using it -Currently the emergency tool is only in Meson trunk so you need a git checkout to use it. It also does not on releases older than 0.32.0 because they had a bug. (A workaround for older versions is to delete existing `foobar.wrap` files if you have them.) - To list all available wraps: ghwt.py list -To install a wrap, go to your source root, make sure that the `subprojects` directory exists and run this command: +To install a wrap, go to your source root, make sure that the +`subprojects` directory exists and run this command: ghwt.py install <projectname> -This will stage the subproject ready to use. If you have multiple subprojects you need to download them all manually. +This will stage the subproject ready to use. If you have multiple +subprojects you need to download them all manually. + +*Note* The tool was added in 0.32.0, for versions older than that you +need to delete the `foo.wrap` file to work around this issue. ## How to upgrade an existing dir/fix broken state/any other problem @@ -26,6 +31,9 @@ Nuke the contents of `subprojects` and start again. ## Known issues -Some repositories show up in the list but are not installable. They would not show up in the real WrapDB because they are works in progress. +Some repositories show up in the list but are not installable. They +would not show up in the real WrapDB because they are works in +progress. -GitHub web API limits the amount of queries you can do to 60/hour. If you exceed that you need to wait for the timer to reset. +GitHub web API limits the amount of queries you can do to 60/hour. If +you exceed that you need to wait for the timer to reset. diff --git a/docs/markdown/snippets/gpgme-config.md b/docs/markdown/snippets/gpgme-config.md index 08a7d38..65569fb 100644 --- a/docs/markdown/snippets/gpgme-config.md +++ b/docs/markdown/snippets/gpgme-config.md @@ -1,3 +1,3 @@ ## gpgme dependency now supports gpgme-config -Previously, we could only detect GPGME with custom invocations of `gpgme-config`. Now we added support to Meson allowing us to use `dependency('gpgme')` instead. +Previously, we could only detect GPGME with custom invocations of `gpgme-config` or when the GPGME version was recent enough (>=1.13.0) to install pkg-config files. Now we added support to Meson allowing us to use `dependency('gpgme')` and fall back on `gpgme-config` parsing. diff --git a/mesonbuild/ast/interpreter.py b/mesonbuild/ast/interpreter.py index f1602c0..a82ba5d 100644 --- a/mesonbuild/ast/interpreter.py +++ b/mesonbuild/ast/interpreter.py @@ -171,6 +171,7 @@ class AstInterpreter(interpreterbase.InterpreterBase): assert(isinstance(node, mparser.PlusAssignmentNode)) if node.var_name not in self.assignments: self.assignments[node.var_name] = [] + self.assign_vals[node.var_name] = [] self.assignments[node.var_name] += [node.value] # Save a reference to the value node if hasattr(node.value, 'ast_id'): self.reverse_assignment[node.value.ast_id] = node diff --git a/mesonbuild/backend/ninjabackend.py b/mesonbuild/backend/ninjabackend.py index a3b9ce8..08b14c8 100644 --- a/mesonbuild/backend/ninjabackend.py +++ b/mesonbuild/backend/ninjabackend.py @@ -1757,7 +1757,7 @@ rule FORTRAN_DEP_HACK%s exe_arr = self.exe_object_to_cmd_array(exe) infilelist = genlist.get_inputs() outfilelist = genlist.get_outputs() - extra_dependencies = [os.path.join(self.build_to_src, i) for i in genlist.extra_depends] + extra_dependencies = self.get_custom_target_depend_files(genlist) for i in range(len(infilelist)): curfile = infilelist[i] if len(generator.outputs) == 1: @@ -2418,7 +2418,7 @@ rule FORTRAN_DEP_HACK%s guessed_dependencies = [] # TODO The get_library_naming requirement currently excludes link targets that use d or fortran as their main linker if hasattr(linker, 'get_library_naming'): - search_dirs = tuple(search_dirs) + linker.get_library_dirs(self.environment) + search_dirs = tuple(search_dirs) + tuple(linker.get_library_dirs(self.environment)) static_patterns = linker.get_library_naming(self.environment, LibType.STATIC, strict=True) shared_patterns = linker.get_library_naming(self.environment, LibType.SHARED, strict=True) for libname in libs: diff --git a/mesonbuild/backend/vs2010backend.py b/mesonbuild/backend/vs2010backend.py index 2ef8187..d1bf1e5 100644 --- a/mesonbuild/backend/vs2010backend.py +++ b/mesonbuild/backend/vs2010backend.py @@ -126,6 +126,7 @@ class Vs2010Backend(backends.Backend): sole_output = '' curfile = infilelist[i] infilename = os.path.join(down, curfile.rel_to_builddir(self.build_to_src)) + deps = self.get_custom_target_depend_files(genlist, True) base_args = generator.get_arglist(infilename) outfiles_rel = genlist.get_outputs_for(curfile) outfiles = [os.path.join(target_private_dir, of) for of in outfiles_rel] @@ -156,6 +157,8 @@ class Vs2010Backend(backends.Backend): cbs = ET.SubElement(idgroup, 'CustomBuild', Include=infilename) ET.SubElement(cbs, 'Command').text = ' '.join(self.quote_arguments(cmd)) ET.SubElement(cbs, 'Outputs').text = ';'.join(outfiles) + if deps: + ET.SubElement(cbs, 'AdditionalInputs').text = ';'.join(deps) return generator_output_files, custom_target_output_files, custom_target_include_dirs def generate(self, interp): @@ -207,7 +210,7 @@ class Vs2010Backend(backends.Backend): if 'VCINSTALLDIR' in os.environ: vs_version = os.environ['VisualStudioVersion'] \ if 'VisualStudioVersion' in os.environ else None - relative_path = 'Auxiliary\\Build\\' if vs_version == '15.0' else '' + relative_path = 'Auxiliary\\Build\\' if vs_version >= '15.0' else '' script_path = os.environ['VCINSTALLDIR'] + relative_path + 'vcvarsall.bat' if os.path.exists(script_path): if has_arch_values: @@ -507,7 +510,6 @@ class Vs2010Backend(backends.Backend): def gen_run_target_vcxproj(self, target, ofname, guid): root = self.create_basic_crap(target, guid) - action = ET.SubElement(root, 'ItemDefinitionGroup') cmd_raw = [target.command] + target.args cmd = python_command + \ [os.path.join(self.environment.get_script_dir(), 'commandrunner.py'), diff --git a/mesonbuild/backend/vs2019backend.py b/mesonbuild/backend/vs2019backend.py new file mode 100644 index 0000000..c6e78cb --- /dev/null +++ b/mesonbuild/backend/vs2019backend.py @@ -0,0 +1,34 @@ +# Copyright 2014-2019 The Meson development team + +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at + +# http://www.apache.org/licenses/LICENSE-2.0 + +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import os +import xml.etree.ElementTree as ET + +from .vs2010backend import Vs2010Backend + + +class Vs2019Backend(Vs2010Backend): + def __init__(self, build): + super().__init__(build) + self.name = 'vs2019' + self.platform_toolset = 'v142' + self.vs_version = '2019' + # WindowsSDKVersion should be set by command prompt. + sdk_version = os.environ.get('WindowsSDKVersion', None) + if sdk_version: + self.windows_target_platform_version = sdk_version.rstrip('\\') + + def generate_debug_information(self, link): + # valid values for vs2019 is 'false', 'true', 'DebugFastLink', 'DebugFull' + ET.SubElement(link, 'GenerateDebugInformation').text = 'DebugFull' diff --git a/mesonbuild/build.py b/mesonbuild/build.py index 020c47b..66f08d8 100644 --- a/mesonbuild/build.py +++ b/mesonbuild/build.py @@ -296,11 +296,20 @@ class ExtractedObjects: class EnvironmentVariables: def __init__(self): self.envvars = [] + # The set of all env vars we have operations for. Only used for self.has_name() + self.varnames = set() def __repr__(self): repr_str = "<{0}: {1}>" return repr_str.format(self.__class__.__name__, self.envvars) + def add_var(self, method, name, args, kwargs): + self.varnames.add(name) + self.envvars.append((method, name, args, kwargs)) + + def has_name(self, name): + return name in self.varnames + def get_value(self, values, kwargs): separator = kwargs.get('separator', os.pathsep) @@ -1356,8 +1365,17 @@ class GeneratedList: self.outfilelist = [] self.outmap = {} self.extra_depends = [] + self.depend_files = [] self.preserve_path_from = preserve_path_from self.extra_args = extra_args + if isinstance(generator.exe, dependencies.ExternalProgram): + if not generator.exe.found(): + raise InvalidArguments('Tried to use not-found external program as generator') + path = generator.exe.get_path() + if os.path.isabs(path): + # Can only add a dependency on an external program which we + # know the absolute path of + self.depend_files.append(File.from_absolute_file(path)) def add_preserved_path_segment(self, infile, outfiles, state): result = [] @@ -1953,8 +1971,7 @@ class CustomTarget(Target): final_cmd.append(c) elif isinstance(c, dependencies.ExternalProgram): if not c.found(): - m = 'Tried to use not-found external program {!r} in "command"' - raise InvalidArguments(m.format(c.name)) + raise InvalidArguments('Tried to use not-found external program in "command"') path = c.get_path() if os.path.isabs(path): # Can only add a dependency on an external program which we diff --git a/mesonbuild/compilers/c.py b/mesonbuild/compilers/c.py index 12f7838..da9a5dc 100644 --- a/mesonbuild/compilers/c.py +++ b/mesonbuild/compilers/c.py @@ -1674,6 +1674,9 @@ class VisualStudioCCompiler(CCompiler): return '14.0' # (Visual Studio 2015) elif version < 1920: return '14.1' # (Visual Studio 2017) + elif version < 1930: + return '14.2' # (Visual Studio 2019) + mlog.warning('Could not find toolset for version {!r}'.format(self.version)) return None def get_default_include_dirs(self): diff --git a/mesonbuild/compilers/compilers.py b/mesonbuild/compilers/compilers.py index ed158b6..2239aa8 100644 --- a/mesonbuild/compilers/compilers.py +++ b/mesonbuild/compilers/compilers.py @@ -1742,7 +1742,7 @@ class ElbrusCompiler(GnuCompiler): # FIXME: use _build_wrapper to call this so that linker flags from the env # get applied - def get_library_dirs(self, env): + def get_library_dirs(self, env, elf_class = None): os_env = os.environ.copy() os_env['LC_ALL'] = 'C' stdo = Popen_safe(self.exelist + ['--print-search-dirs'], env=os_env)[1] diff --git a/mesonbuild/compilers/cpp.py b/mesonbuild/compilers/cpp.py index 67de684..87e6ffc 100644 --- a/mesonbuild/compilers/cpp.py +++ b/mesonbuild/compilers/cpp.py @@ -115,8 +115,8 @@ class CPPCompiler(CCompiler): 'gnu++17': 'gnu++1z' } - # Currently, remapping is only supported for Clang and GCC - assert(self.id in frozenset(['clang', 'gcc'])) + # Currently, remapping is only supported for Clang, Elbrus and GCC + assert(self.id in frozenset(['clang', 'lcc', 'gcc'])) if cpp_std not in CPP_FALLBACKS: # 'c++03' and 'c++98' don't have fallback types @@ -251,10 +251,13 @@ 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['cpp_std'] = coredata.UserComboOption('cpp_std', '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') + opts.update({'cpp_std': coredata.UserComboOption('cpp_std', '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('cpp_debugstl', + 'STL debug mode', + False)}) return opts # Elbrus C++ compiler does not have lchmod, but there is only linker warning, not compiler error. diff --git a/mesonbuild/coredata.py b/mesonbuild/coredata.py index 6b86529..51b36f0 100644 --- a/mesonbuild/coredata.py +++ b/mesonbuild/coredata.py @@ -773,39 +773,41 @@ class BuiltinOption(Generic[_U]): kwargs['dest'] = 'cross_' + name parser.add_argument(self.argparse_name_to_arg('cross_' + name), help=h + ' (for host in cross compiles)', **kwargs) - -builtin_options = { - 'buildtype': BuiltinOption(UserComboOption, 'Build type to use', 'debug', - choices=['plain', 'debug', 'debugoptimized', 'release', 'minsize', 'custom']), - 'strip': BuiltinOption(UserBooleanOption, 'Strip targets on install', False), - 'unity': BuiltinOption(UserComboOption, 'Unity build', 'off', choices=['on', 'off', 'subprojects']), - 'prefix': BuiltinOption(UserStringOption, 'Installation prefix', default_prefix()), - 'libdir': BuiltinOption(UserStringOption, 'Library directory', default_libdir()), - 'libexecdir': BuiltinOption(UserStringOption, 'Library executable directory', default_libexecdir()), - 'bindir': BuiltinOption(UserStringOption, 'Executable directory', 'bin'), - 'sbindir': BuiltinOption(UserStringOption, 'System executable directory', 'sbin'), - 'includedir': BuiltinOption(UserStringOption, 'Header file directory', 'include'), - 'datadir': BuiltinOption(UserStringOption, 'Data file directory', 'share'), - 'mandir': BuiltinOption(UserStringOption, 'Manual page directory', 'share/man'), - 'infodir': BuiltinOption(UserStringOption, 'Info page directory', 'share/info'), - 'localedir': BuiltinOption(UserStringOption, 'Locale data directory', 'share/locale'), - 'sysconfdir': BuiltinOption(UserStringOption, 'Sysconf data directory', 'etc'), - 'localstatedir': BuiltinOption(UserStringOption, 'Localstate data directory', 'var'), - 'sharedstatedir': BuiltinOption(UserStringOption, 'Architecture-independent data directory', 'com'), - 'werror': BuiltinOption(UserBooleanOption, 'Treat warnings as errors', False), - 'warning_level': BuiltinOption(UserComboOption, 'Compiler warning level to use', '1', choices=['0', '1', '2', '3']), - 'layout': BuiltinOption(UserComboOption, 'Build directory layout', 'mirror', choices=['mirror', 'flat']), - 'default_library': BuiltinOption(UserComboOption, 'Default library type', 'shared', choices=['shared', 'static', 'both']), - 'backend': BuiltinOption(UserComboOption, 'Backend to use', 'ninja', choices=backendlist), - 'stdsplit': BuiltinOption(UserBooleanOption, 'Split stdout and stderr in test logs', True), - 'errorlogs': BuiltinOption(UserBooleanOption, "Whether to print the logs from failing tests", True), - 'install_umask': BuiltinOption(UserUmaskOption, 'Default umask to apply on permissions of installed files', '022'), - 'auto_features': BuiltinOption(UserFeatureOption, "Override value of all 'auto' features", 'auto'), - 'optimization': BuiltinOption(UserComboOption, 'Optimization level', '0', choices=['0', 'g', '1', '2', '3', 's']), - 'debug': BuiltinOption(UserBooleanOption, 'Debug', True), - 'wrap_mode': BuiltinOption(UserComboOption, 'Wrap mode', 'default', choices=['default', 'nofallback', 'nodownload', 'forcefallback']), - 'pkg_config_path': BuiltinOption(UserArrayOption, 'List of additional paths for pkg-config to search', [], separate_cross=True), -} +# Update `docs/markdown/Builtin-options.md` after changing the options below +builtin_options = OrderedDict([ + # Directories + ('prefix', BuiltinOption(UserStringOption, 'Installation prefix', default_prefix())), + ('bindir', BuiltinOption(UserStringOption, 'Executable directory', 'bin')), + ('datadir', BuiltinOption(UserStringOption, 'Data file directory', 'share')), + ('includedir', BuiltinOption(UserStringOption, 'Header file directory', 'include')), + ('infodir', BuiltinOption(UserStringOption, 'Info page directory', 'share/info')), + ('libdir', BuiltinOption(UserStringOption, 'Library directory', default_libdir())), + ('libexecdir', BuiltinOption(UserStringOption, 'Library executable directory', default_libexecdir())), + ('localedir', BuiltinOption(UserStringOption, 'Locale data directory', 'share/locale')), + ('localstatedir', BuiltinOption(UserStringOption, 'Localstate data directory', 'var')), + ('mandir', BuiltinOption(UserStringOption, 'Manual page directory', 'share/man')), + ('sbindir', BuiltinOption(UserStringOption, 'System executable directory', 'sbin')), + ('sharedstatedir', BuiltinOption(UserStringOption, 'Architecture-independent data directory', 'com')), + ('sysconfdir', BuiltinOption(UserStringOption, 'Sysconf data directory', 'etc')), + # Core options + ('auto_features', BuiltinOption(UserFeatureOption, "Override value of all 'auto' features", 'auto')), + ('backend', BuiltinOption(UserComboOption, 'Backend to use', 'ninja', choices=backendlist)), + ('buildtype', BuiltinOption(UserComboOption, 'Build type to use', 'debug', + choices=['plain', 'debug', 'debugoptimized', 'release', 'minsize', 'custom'])), + ('debug', BuiltinOption(UserBooleanOption, 'Debug', True)), + ('default_library', BuiltinOption(UserComboOption, 'Default library type', 'shared', choices=['shared', 'static', 'both'])), + ('errorlogs', BuiltinOption(UserBooleanOption, "Whether to print the logs from failing tests", True)), + ('install_umask', BuiltinOption(UserUmaskOption, 'Default umask to apply on permissions of installed files', '022')), + ('layout', BuiltinOption(UserComboOption, 'Build directory layout', 'mirror', choices=['mirror', 'flat'])), + ('pkg_config_path', BuiltinOption(UserArrayOption, 'List of additional paths for pkg-config to search', [], separate_cross=True)), + ('optimization', BuiltinOption(UserComboOption, 'Optimization level', '0', choices=['0', 'g', '1', '2', '3', 's'])), + ('stdsplit', BuiltinOption(UserBooleanOption, 'Split stdout and stderr in test logs', True)), + ('strip', BuiltinOption(UserBooleanOption, 'Strip targets on install', False)), + ('unity', BuiltinOption(UserComboOption, 'Unity build', 'off', choices=['on', 'off', 'subprojects'])), + ('warning_level', BuiltinOption(UserComboOption, 'Compiler warning level to use', '1', choices=['0', '1', '2', '3'])), + ('werror', BuiltinOption(UserBooleanOption, 'Treat warnings as errors', False)), + ('wrap_mode', BuiltinOption(UserComboOption, 'Wrap mode', 'default', choices=['default', 'nofallback', 'nodownload', 'forcefallback'])), +]) # Special prefix-dependent defaults for installation directories that reside in # a path outside of the prefix in FHS and common usage. diff --git a/mesonbuild/dependencies/misc.py b/mesonbuild/dependencies/misc.py index 219b3d6..55cb569 100644 --- a/mesonbuild/dependencies/misc.py +++ b/mesonbuild/dependencies/misc.py @@ -680,6 +680,9 @@ class GpgmeDependency(ExternalDependency): methods = cls._process_method_kw(kwargs) candidates = [] + if DependencyMethods.PKGCONFIG in methods: + candidates.append(functools.partial(PkgConfigDependency, 'gpgme', environment, kwargs)) + if DependencyMethods.CONFIG_TOOL in methods: candidates.append(functools.partial(ConfigToolDependency.factory, 'gpgme', environment, None, kwargs, ['gpgme-config'], @@ -696,7 +699,7 @@ class GpgmeDependency(ExternalDependency): @staticmethod def get_methods(): - return [DependencyMethods.CONFIG_TOOL] + return [DependencyMethods.PKGCONFIG, DependencyMethods.CONFIG_TOOL] class ShadercDependency(ExternalDependency): @@ -732,12 +735,25 @@ class ShadercDependency(ExternalDependency): methods = cls._process_method_kw(kwargs) candidates = [] + if DependencyMethods.PKGCONFIG in methods: + # ShaderC packages their shared and static libs together + # and provides different pkg-config files for each one. We + # smooth over this difference by handling the static + # keyword before handing off to the pkg-config handler. + shared_libs = ['shaderc'] + static_libs = ['shaderc_combined', 'shaderc_static'] + + if kwargs.get('static', False): + c = [functools.partial(PkgConfigDependency, name, environment, kwargs) + for name in static_libs + shared_libs] + else: + c = [functools.partial(PkgConfigDependency, name, environment, kwargs) + for name in shared_libs + static_libs] + candidates.exend(c) + if DependencyMethods.SYSTEM in methods: candidates.append(functools.partial(ShadercDependency, environment, kwargs)) - if DependencyMethods.PKGCONFIG in methods: - candidates.append(functools.partial(PkgConfigDependency, 'shaderc', environment, kwargs)) - return candidates @staticmethod diff --git a/mesonbuild/interpreter.py b/mesonbuild/interpreter.py index 79da399..3a3fb81 100644 --- a/mesonbuild/interpreter.py +++ b/mesonbuild/interpreter.py @@ -235,7 +235,13 @@ class EnvironmentVariablesHolder(MutableInterpreterObject, ObjectHolder): raise InterpreterException("EnvironmentVariablesHolder methods require at least" "2 arguments, first is the name of the variable and" " following one are values") - self.held_object.envvars.append((method, args[0], args[1:], kwargs)) + # Warn when someone tries to use append() or prepend() on an env var + # which already has an operation set on it. People seem to think that + # multiple append/prepend operations stack, but they don't. + if method != self.held_object.set and self.held_object.has_name(args[0]): + mlog.warning('Overriding previous value of environment variable {!r} with a new one' + .format(args[0]), location=self.current_node) + self.held_object.add_var(method, args[0], args[1:], kwargs) @stringArgs @permittedKwargs({'separator'}) @@ -2569,6 +2575,12 @@ external dependencies (including libraries) must go to "dependencies".''') if ':' in proj_name: raise InvalidArguments("Project name {!r} must not contain ':'".format(proj_name)) + if 'meson_version' in kwargs: + cv = coredata.version + pv = kwargs['meson_version'] + if not mesonlib.version_compare(cv, pv): + raise InterpreterException('Meson version is %s but project requires %s' % (cv, pv)) + if os.path.exists(self.option_file): oi = optinterpreter.OptionInterpreter(self.subproject) oi.process(self.option_file) @@ -2615,11 +2627,8 @@ external dependencies (including libraries) must go to "dependencies".''') mesonlib.project_meson_versions[self.subproject] = '' if 'meson_version' in kwargs: - cv = coredata.version - pv = kwargs['meson_version'] - mesonlib.project_meson_versions[self.subproject] = pv - if not mesonlib.version_compare(cv, pv): - raise InterpreterException('Meson version is %s but project requires %s.' % (cv, pv)) + mesonlib.project_meson_versions[self.subproject] = kwargs['meson_version'] + self.build.projects[self.subproject] = proj_name mlog.log('Project name:', mlog.bold(proj_name)) mlog.log('Project version:', mlog.bold(self.project_version)) diff --git a/mesonbuild/msetup.py b/mesonbuild/msetup.py index ef0511d..563b811 100644 --- a/mesonbuild/msetup.py +++ b/mesonbuild/msetup.py @@ -209,8 +209,6 @@ class MesonApp: else: intr.backend.generate(intr) build.save(b, dumpfile) - # Post-conf scripts must be run after writing coredata or else introspection fails. - intr.backend.run_postconf_scripts() if env.first_invocation: coredata.write_cmd_line_file(self.build_dir, self.options) else: @@ -223,6 +221,9 @@ class MesonApp: else: mintro.generate_introspection_file(b, intr.backend) mintro.write_meson_info_file(b, [], True) + + # Post-conf scripts must be run after writing coredata or else introspection fails. + intr.backend.run_postconf_scripts() except Exception as e: mintro.write_meson_info_file(b, [e]) if 'cdf' in locals(): diff --git a/run_unittests.py b/run_unittests.py index 99e3c49..faef00f 100755 --- a/run_unittests.py +++ b/run_unittests.py @@ -958,6 +958,25 @@ class InternalTests(unittest.TestCase): self.assertEqual(ver_a.__cmp__(ver_b), result) self.assertEqual(ver_b.__cmp__(ver_a), -result) + def test_msvc_toolset_version(self): + ''' + Ensure that the toolset version returns the correct value for this MSVC + ''' + env = get_fake_env() + cc = env.detect_c_compiler(False) + if cc.get_argument_syntax() != 'msvc': + raise unittest.SkipTest('Test only applies to MSVC-like compilers') + toolset_ver = cc.get_toolset_version() + self.assertIsNotNone(toolset_ver) + # Visual Studio 2015 and older versions do not define VCToolsVersion + if int(''.join(cc.version.split('.')[0:2])) < 1910: + return + self.assertIn('VCToolsVersion', os.environ) + vctools_ver = os.environ['VCToolsVersion'] + self.assertTrue(vctools_ver.startswith(toolset_ver), + msg='{!r} does not start with {!r}'.format(vctools_ver, toolset_ver)) + + @unittest.skipIf(is_tarball(), 'Skipping because this is a tarball release') class DataTests(unittest.TestCase): @@ -1672,6 +1691,17 @@ class AllPlatformTests(BasePlatformTests): self.build() self.run_tests() + def test_env_ops_dont_stack(self): + ''' + Test that env ops prepend/append do not stack, and that this usage issues a warning + ''' + testdir = os.path.join(self.unit_test_dir, '59 test env doesn\'t stack') + out = self.init(testdir) + self.assertRegex(out, r'WARNING: Overriding.*TEST_VAR_APPEND') + self.assertRegex(out, r'WARNING: Overriding.*TEST_VAR_PREPEND') + self.assertNotRegex(out, r'WARNING: Overriding.*TEST_VAR_SET') + self.run_tests() + def test_testsetups(self): if not shutil.which('valgrind'): raise unittest.SkipTest('Valgrind not installed.') @@ -2133,6 +2163,20 @@ class AllPlatformTests(BasePlatformTests): self.utime(os.path.join(testdir, f)) self.assertRebuiltTarget('prog') + def test_source_generator_program_cause_rebuild(self): + ''' + Test that changes to generator programs in the source tree cause + a rebuild. + ''' + testdir = os.path.join(self.common_test_dir, '95 gen extra') + self.init(testdir) + self.build() + # Immediately rebuilding should not do anything + self.assertBuildIsNoop() + # Changing mtime of generator should rebuild the executable + self.utime(os.path.join(testdir, 'srcgen.py')) + self.assertRebuiltTarget('basic') + def test_static_library_lto(self): ''' Test that static libraries can be built with LTO and linked to @@ -3600,12 +3644,13 @@ class FailureTests(BasePlatformTests): super().setUp() self.srcdir = os.path.realpath(tempfile.mkdtemp()) self.mbuild = os.path.join(self.srcdir, 'meson.build') + self.moptions = os.path.join(self.srcdir, 'meson_options.txt') def tearDown(self): super().tearDown() windows_proof_rmtree(self.srcdir) - def assertMesonRaises(self, contents, match, extra_args=None, langs=None, meson_version=None): + def assertMesonRaises(self, contents, match, extra_args=None, langs=None, meson_version=None, options=None): ''' Assert that running meson configure on the specified @contents raises a error message matching regex @match. @@ -3620,6 +3665,9 @@ class FailureTests(BasePlatformTests): for lang in langs: f.write("add_languages('{}', required : false)\n".format(lang)) f.write(contents) + if options is not None: + with open(self.moptions, 'w') as f: + f.write(options) # Force tracebacks so we can detect them properly os.environ['MESON_FORCE_BACKTRACE'] = '1' with self.assertRaisesRegex(MesonException, match, msg=contents): @@ -3852,6 +3900,14 @@ class FailureTests(BasePlatformTests): "sub1.get_variable('naaa')", """Subproject "subprojects/not-found-subproject" disabled can't get_variable on it.""") + def test_version_checked_before_parsing_options(self): + ''' + https://github.com/mesonbuild/meson/issues/5281 + ''' + options = "option('some-option', type: 'foo', value: '')" + match = 'Meson version is.*but project requires >=2000' + self.assertMesonRaises("", match, meson_version='>=2000', options=options) + @unittest.skipUnless(is_windows() or is_cygwin(), "requires Windows (or Windows via Cygwin)") class WindowsTests(BasePlatformTests): @@ -5785,7 +5841,11 @@ class NativeFileTests(BasePlatformTests): @skip_if_env_set('FC') def test_fortran_compiler(self): def cb(comp): - if comp.id == 'gcc': + if comp.id == 'lcc': + if shutil.which('lfortran'): + return 'lfortran', 'lcc' + raise unittest.SkipTest('No alternate Fortran implementation.') + elif comp.id == 'gcc': if shutil.which('ifort'): return 'ifort', 'intel' elif shutil.which('flang'): diff --git a/test cases/common/144 mesonintrospect from scripts/check_introspection.py b/test cases/common/144 mesonintrospect from scripts/check_introspection.py new file mode 100644 index 0000000..851a415 --- /dev/null +++ b/test cases/common/144 mesonintrospect from scripts/check_introspection.py @@ -0,0 +1,18 @@ +#!/usr/bin/env python3 + +import os +import shlex +import subprocess + + +if 'MESONINTROSPECT' not in os.environ: + raise RuntimeError('MESONINTROSPECT not found') +if 'MESON_BUILD_ROOT' not in os.environ: + raise RuntimeError('MESON_BUILD_ROOT not found') + +mesonintrospect = os.environ['MESONINTROSPECT'] +introspect_arr = shlex.split(mesonintrospect) + +buildroot = os.environ['MESON_BUILD_ROOT'] + +subprocess.check_output([*introspect_arr, '--all', buildroot]) diff --git a/test cases/common/144 mesonintrospect from scripts/meson.build b/test cases/common/144 mesonintrospect from scripts/meson.build index f78710b..1117672 100644 --- a/test cases/common/144 mesonintrospect from scripts/meson.build +++ b/test cases/common/144 mesonintrospect from scripts/meson.build @@ -10,5 +10,5 @@ else message(ret.stderr()) endif -meson.add_postconf_script('check_env.py') +meson.add_postconf_script('check_introspection.py') meson.add_install_script('check_env.py') diff --git a/test cases/failing/96 dependency not config-tool/96 unknown config tool/meson.build b/test cases/failing/96 dependency not config-tool/96 unknown config tool/meson.build new file mode 100644 index 0000000..536976e --- /dev/null +++ b/test cases/failing/96 dependency not config-tool/96 unknown config tool/meson.build @@ -0,0 +1,2 @@ +project('no-such-config-tool') +dependency('no-such-config-tool', method:'config-tool') diff --git a/test cases/frameworks/27 gpgme/meson.build b/test cases/frameworks/27 gpgme/meson.build index 220a4c0..91b1aaa 100644 --- a/test cases/frameworks/27 gpgme/meson.build +++ b/test cases/frameworks/27 gpgme/meson.build @@ -19,3 +19,9 @@ dependency('gpgme', method: 'config-tool') # Check we can apply a version constraint dependency('gpgme', version: '>=@0@'.format(gpgme_dep.version()), method: 'config-tool') + +# If gpgme is new enough, make sure it picks up pkg-config by default: + +if gpgme_ver.version_compare('>=1.13.0') + assert(gpgme_dep.type_name() == 'pkgconfig', 'dependency found via pkg-config') +endif diff --git a/test cases/unit/55 introspection/meson.build b/test cases/unit/55 introspection/meson.build index 588f71c..95a7c5f 100644 --- a/test cases/unit/55 introspection/meson.build +++ b/test cases/unit/55 introspection/meson.build @@ -9,6 +9,9 @@ b2 = get_option('test_opt2') test_bool = b1 or b2 test_bool = b1 and b2 +set_variable('list_test_plusassign', []) +list_test_plusassign += ['bugs everywhere'] + if false dependency('somethingthatdoesnotexist', required: true) dependency('look_i_have_a_fallback', fallback: ['oh_no', 'the_subproject_does_not_exist']) diff --git a/test cases/unit/59 test env doesn't stack/meson.build b/test cases/unit/59 test env doesn't stack/meson.build new file mode 100644 index 0000000..01f2637 --- /dev/null +++ b/test cases/unit/59 test env doesn't stack/meson.build @@ -0,0 +1,12 @@ +project('test env var stacking') + +testenv = environment() +testenv.set('TEST_VAR_SET', 'some-value') +testenv.set('TEST_VAR_APPEND', 'some-value') +testenv.set('TEST_VAR_PREPEND', 'some-value') + +testenv.append('TEST_VAR_APPEND', 'another-value-append', separator: ':') +testenv.prepend('TEST_VAR_PREPEND', 'another-value-prepend', separator: ':') +testenv.set('TEST_VAR_SET', 'another-value-set') + +test('check env', find_program('script.py'), env: testenv) diff --git a/test cases/unit/59 test env doesn't stack/script.py b/test cases/unit/59 test env doesn't stack/script.py new file mode 100755 index 0000000..2a76673 --- /dev/null +++ b/test cases/unit/59 test env doesn't stack/script.py @@ -0,0 +1,9 @@ +#!/usr/bin/env python3 + +import os + +for name in ('append', 'prepend', 'set'): + envname = 'TEST_VAR_' + name.upper() + value = 'another-value-' + name + envvalue = os.environ[envname] + assert (envvalue == value), (name, envvalue) |