diff options
31 files changed, 441 insertions, 151 deletions
diff --git a/.coveragerc b/.coveragerc deleted file mode 100644 index c3d41d4..0000000 --- a/.coveragerc +++ /dev/null @@ -1,50 +0,0 @@ -[run] -branch = True -concurrency = - multiprocessing -data_file = .coverage/coverage -parallel = True -source = - meson.py - mesonbuild/ - mesonconf.py - mesonintrospect.py - mesonrewriter.py - mesontest.py - run_cross_test.py - run_project_tests.py - run_tests.py - run_unittests.py - -# Aliases to /root/ are needed because Travis runs in docker at /root/. -[paths] -meson = - meson.py - /root/meson.py -mesonbuild = - mesonbuild/ - /root/mesonbuild/ -mesonconf = - mesonconf.py - /root/mesonconf.py -mesonintrospect = - mesonintrospect.py - /root/mesonintrospect.py -mesonrewriter = - mesonrewriter.py - /root/mesonrewriter.py -mesontest = - mesontest.py - /root/mesontest.py -run_cross_test = - run_cross_test.py - /root/run_cross_test.py -run_project_tests = - run_project_tests.py - /root/run_project_tests.py -run_tests = - run_tests.py - /root/run_tests.py -run_unittests = - run_unittests.py - /root/run_unittests.py diff --git a/.github/workflows/cygwin.yml b/.github/workflows/cygwin.yml index 32d33d8..3954094 100644 --- a/.github/workflows/cygwin.yml +++ b/.github/workflows/cygwin.yml @@ -68,13 +68,13 @@ jobs: - name: Run pip run: | export PATH=/usr/bin:/usr/local/bin:$(cygpath ${SYSTEMROOT})/system32 - python3 -m pip --disable-pip-version-check install gcovr jsonschema pefile pytest pytest-xdist + python3 -m pip --disable-pip-version-check install gcovr jsonschema pefile pytest pytest-xdist coverage codecov shell: C:\cygwin\bin\bash.exe --noprofile --norc -o igncr -eo pipefail '{0}' - name: Run tests run: | export PATH=/usr/bin:/usr/local/bin:$(cygpath ${SYSTEMROOT})/system32 - python3 run_tests.py --backend=ninja + python3 ./tools/run_with_cov.py run_tests.py --backend=ninja env: # Cygwin's static boost installation is broken (some static library # variants such as boost_thread are not present) @@ -87,3 +87,6 @@ jobs: path: meson-test-run.* # test log should be saved on failure if: ${{ !cancelled() }} + + - name: Upload coverage report + run: ./ci/upload_cov.sh "${{ matrix.NAME }}" diff --git a/.github/workflows/macos.yml b/.github/workflows/macos.yml index faff3ae..c7ef7d3 100644 --- a/.github/workflows/macos.yml +++ b/.github/workflows/macos.yml @@ -24,7 +24,7 @@ jobs: python-version: '3.x' - run: | python -m pip install --upgrade pip - python -m pip install pytest pytest-xdist jsonschema + python -m pip install pytest pytest-xdist jsonschema coverage codecov - run: brew install pkg-config ninja llvm qt@5 - env: CPPFLAGS: "-I/usr/local/include" @@ -35,7 +35,10 @@ jobs: export SDKROOT="$(xcodebuild -version -sdk macosx Path)" export PATH="$HOME/tools:/usr/local/opt/qt@5/bin:$PATH:$(brew --prefix llvm)/bin" export PKG_CONFIG_PATH="/usr/local/opt/qt@5/lib/pkgconfig:$PKG_CONFIG_PATH" - ./run_unittests.py + ./tools/run_with_cov.py ./run_unittests.py + - name: Upload coverage report + run: ./ci/upload_cov.sh "appleclang [unit tests]" + project-tests-appleclang: runs-on: macos-latest @@ -50,7 +53,7 @@ jobs: - run: | python3 -m pip install --upgrade setuptools python3 -m pip install --upgrade pip - python3 -m pip install cython + python3 -m pip install cython coverage codecov - env: CPPFLAGS: "-I/usr/local/include" LDFLAGS: "-L/usr/local/lib" @@ -61,7 +64,9 @@ jobs: export SDKROOT="$(xcodebuild -version -sdk macosx Path)" export PATH="$HOME/tools:/usr/local/opt/qt@5/bin:$PATH:$(brew --prefix llvm)/bin" export PKG_CONFIG_PATH="/usr/local/opt/qt@5/lib/pkgconfig:$PKG_CONFIG_PATH" - ./run_project_tests.py --backend=ninja + ./tools/run_with_cov.py ./run_project_tests.py --backend=ninja + - name: Upload coverage report + run: ./ci/upload_cov.sh "appleclang [project tests; unity=${{ matrix.unity }}]" Qt4macos: runs-on: macos-latest diff --git a/.github/workflows/msys2.yml b/.github/workflows/msys2.yml index cbab904..64a0979 100644 --- a/.github/workflows/msys2.yml +++ b/.github/workflows/msys2.yml @@ -71,7 +71,7 @@ jobs: - name: Install dependencies run: | - python3 -m pip --disable-pip-version-check install gcovr jsonschema pefile pytest pytest-xdist + python3 -m pip --disable-pip-version-check install gcovr jsonschema pefile pytest pytest-xdist coverage codecov - name: Run Tests run: | @@ -89,9 +89,12 @@ jobs: pacman --noconfirm --needed -S mingw-w64-${{ matrix.MSYS2_ARCH }}-${{ matrix.MSYS2_CURSES }} fi - MSYSTEM= python3 run_tests.py --backend=ninja + MSYSTEM= python3 ./tools/run_with_cov.py run_tests.py --backend=ninja - uses: actions/upload-artifact@v2 with: name: ${{ matrix.NAME }} path: meson-test-run.* + + - name: Upload coverage report + run: ./ci/upload_cov.sh "${{ matrix.NAME }}" diff --git a/.github/workflows/nonative.yml b/.github/workflows/nonative.yml index 1fc43b4..c4bad95 100644 --- a/.github/workflows/nonative.yml +++ b/.github/workflows/nonative.yml @@ -26,6 +26,9 @@ jobs: - run: | apt-get -y purge clang gcc gdc apt-get -y autoremove + python3 -m pip install coverage codecov - uses: actions/checkout@v2 - name: Run tests - run: bash -c 'source /ci/env_vars.sh; cd $GITHUB_WORKSPACE; ./run_tests.py $CI_ARGS --cross ubuntu-armhf.json --cross-only' + run: bash -c 'source /ci/env_vars.sh; cd $GITHUB_WORKSPACE; ./tools/run_with_cov.py ./run_tests.py $CI_ARGS --cross ubuntu-armhf.json --cross-only' + - name: Upload coverage report + run: ./ci/upload_cov.sh "Ubuntu nonnative" diff --git a/.github/workflows/os_comp.yml b/.github/workflows/os_comp.yml index 34b113b..6531e2b 100644 --- a/.github/workflows/os_comp.yml +++ b/.github/workflows/os_comp.yml @@ -40,7 +40,9 @@ jobs: # All environment variables are stored inside the docker image in /ci/env_vars.sh # They are defined in the `env` section in each image.json. CI_ARGS should be set # via the `args` array ub the image.json - run: bash -c 'source /ci/env_vars.sh; cd $GITHUB_WORKSPACE; ./run_tests.py $CI_ARGS' + run: bash -c 'source /ci/env_vars.sh; cd $GITHUB_WORKSPACE; ./tools/run_with_cov.py ./run_tests.py $CI_ARGS' + - name: Upload coverage report + run: ./ci/upload_cov.sh "OS Comp [${{ matrix.cfg.name }}]" ubuntu-rolling: name: 'Ubuntu Rolling' @@ -110,4 +112,7 @@ jobs: update-alternatives --set i686-w64-mingw32-gcc /usr/bin/i686-w64-mingw32-gcc-posix update-alternatives --set i686-w64-mingw32-g++ /usr/bin/i686-w64-mingw32-g++-posix - ./run_tests.py $RUN_TESTS_ARGS -- $MESON_ARGS + ./tools/run_with_cov.py ./run_tests.py $RUN_TESTS_ARGS -- $MESON_ARGS + + - name: Upload coverage report + run: ./ci/upload_cov.sh "Ubuntu [${{ matrix.cfg.CC }} ${{ matrix.cfg.RUN_TESTS_ARGS }} ${{ matrix.cfg.MESON_ARGS }}]" diff --git a/.github/workflows/unusedargs_missingreturn.yml b/.github/workflows/unusedargs_missingreturn.yml index 3e82568..8e6e42d 100644 --- a/.github/workflows/unusedargs_missingreturn.yml +++ b/.github/workflows/unusedargs_missingreturn.yml @@ -45,7 +45,10 @@ jobs: run: | sudo apt update -yq sudo apt install -yq --no-install-recommends g++ gfortran ninja-build gobjc gobjc++ - - run: python run_project_tests.py --only cmake common fortran platform-linux "objective c" "objective c++" + python -m pip install coverage codecov + - run: ./tools/run_with_cov.py run_project_tests.py --only cmake common fortran platform-linux "objective c" "objective c++" + - name: Upload coverage report + run: ./ci/upload_cov.sh "UnusedMissingReturn" windows: runs-on: windows-latest @@ -55,10 +58,13 @@ jobs: with: python-version: '3.x' - - run: pip install ninja pefile + - run: pip install ninja pefile coverage codecov - - run: python run_project_tests.py --only platform-windows + - run: python ./tools/run_with_cov.py run_project_tests.py --only platform-windows env: CC: gcc CXX: g++ FC: gfortran + + - name: Upload coverage report + run: ./ci/upload_cov.sh "UnusedMissingReturn Windows" @@ -8,7 +8,8 @@ /.vscode __pycache__ -.coverage +/.coverage/ +/.coveragerc /install dir /work area diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 14ff1bd..614f344 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -9,6 +9,7 @@ trigger: variables: CI: 1 + SOURCE_VERSION: $(Build.SourceVersion) jobs: @@ -68,11 +68,26 @@ python --version # Needed for running unit tests in parallel. echo "" -python -m pip --disable-pip-version-check install --upgrade pefile pytest-xdist jsonschema +python -m pip --disable-pip-version-check install --upgrade pefile pytest-xdist jsonschema coverage echo "" echo "=== Start running tests ===" # 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 $env:backend $env:extraargs" +cmd /c "python 2>&1 ./tools/run_with_cov.py run_tests.py --backend $env:backend $env:extraargs" + +echo "" +echo "" +echo "=== Gathering coverage report ===" +echo "" + +python3 -m coverage combine +python3 -m coverage xml +python3 -m coverage report + +# Currently codecov.py does not handle Azure, use this fork of a fork to get it +# working without requireing a token +git clone https://github.com/mensinda/codecov-python +python3 -m pip install --ignore-installed ./codecov-python +python3 -m codecov -f .coverage/coverage.xml -n "VS$env:compiler $env:arch $env:backend" -c $env:SOURCE_VERSION diff --git a/ci/upload_cov.sh b/ci/upload_cov.sh new file mode 100755 index 0000000..089641b --- /dev/null +++ b/ci/upload_cov.sh @@ -0,0 +1,13 @@ +#!/bin/bash + +echo "Combining coverage reports..." +coverage combine + +echo "Generating XML report..." +coverage xml + +echo "Printing report" +coverage report + +echo "Uploading to codecov..." +codecov -f .coverage/coverage.xml -n "$1" diff --git a/ci/usercustomize.py b/ci/usercustomize.py new file mode 100644 index 0000000..72421ba --- /dev/null +++ b/ci/usercustomize.py @@ -0,0 +1,19 @@ +# Copyright 2021 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. + +# This script is used by coverage (see tools/run_with_cov.py) to enable coverage +# reports in python subprocesses + +import coverage +coverage.process_startup() diff --git a/data/.coveragerc.in b/data/.coveragerc.in new file mode 100644 index 0000000..328e13c --- /dev/null +++ b/data/.coveragerc.in @@ -0,0 +1,25 @@ +[run] +branch = True +parallel = True +concurrency = multiprocessing +data_file = @ROOT@/.coverage/coverage +source = @ROOT@/mesonbuild/ + +[report] +exclude_lines = + if T.TYPE_CHECKING: + +[paths] +mesonbuild = + mesonbuild/ + __w/meson/meson/mesonbuild/ + @ROOT@/mesonbuild/ + +[html] +directory = @ROOT@/.coverage/html + +[xml] +output = @ROOT@/.coverage/coverage.xml + +[json] +output = @ROOT@/.coverage/coverage.json diff --git a/docs/markdown/Builtin-options.md b/docs/markdown/Builtin-options.md index ce33fce..0536e77 100644 --- a/docs/markdown/Builtin-options.md +++ b/docs/markdown/Builtin-options.md @@ -69,7 +69,7 @@ machine](#specifying-options-per-machine) section for details. | Option | Default value | Description | Is per machine | Is per subproject | | ------ | ------------- | ----------- | -------------- | ----------------- | | auto_features {enabled, disabled, auto} | auto | Override value of all 'auto' features | no | no | -| backend {ninja, vs,<br>vs2010, vs2015, vs2017, vs2019, xcode} | ninja | Backend to use | no | no | +| backend {ninja, vs,<br>vs2010, vs2012, vs2013, vs2015, vs2017, vs2019, xcode} | ninja | Backend to use | no | no | | buildtype {plain, debug,<br>debugoptimized, release, minsize, custom} | debug | Build type to use | no | no | | debug | true | Debug | no | no | | default_library {shared, static, both} | shared | Default library type | no | yes | diff --git a/docs/markdown/Reference-manual.md b/docs/markdown/Reference-manual.md index de7f9f2..388c975 100644 --- a/docs/markdown/Reference-manual.md +++ b/docs/markdown/Reference-manual.md @@ -1984,8 +1984,8 @@ the following methods. *used as the `script_name` parameter. - `backend()` *(since 0.37.0)*: returns a string representing the - current backend: `ninja`, `vs2010`, `vs2015`, `vs2017`, `vs2019`, - or `xcode`. + current backend: `ninja`, `vs2010`, `vs2012`, `vs2013`, `vs2015`, + `vs2017`, `vs2019`, or `xcode`. - `build_root()`: returns a string with the absolute path to the build root directory. *(deprecated since 0.56.0)*: this function will return the diff --git a/docs/markdown/snippets/newvsbackends.md b/docs/markdown/snippets/newvsbackends.md new file mode 100644 index 0000000..cb7437a --- /dev/null +++ b/docs/markdown/snippets/newvsbackends.md @@ -0,0 +1,15 @@ +## New `vs2012` and `vs2013` backend options + +Adds the ability to generate Visual Studio 2012 and 2013 projects. This is an +extension to the existing Visual Studio 2010 projects so that it is no longer +required to manually upgrade the generated Visual Studio 2010 projects. + +Generating Visual Studio 2010 projects has also been fixed since its developer +command prompt does not provide a `%VisualStudioVersion%` envvar. + +## Developer environment + +Expand the support for the `link_whole:` project option for pre-Visual Studio 2015 +Update 2, where previously Visual Studio 2015 Update 2 or later was required for +this, for the Ninja backend as well as the vs2010 (as well as the newly-added +vs2012 and vs2013 backends). diff --git a/mesonbuild/backend/backends.py b/mesonbuild/backend/backends.py index 2652ae6..2239fdd 100644 --- a/mesonbuild/backend/backends.py +++ b/mesonbuild/backend/backends.py @@ -199,6 +199,12 @@ def get_backend_from_name(backend: str, build: T.Optional[build.Build] = None, i elif backend == 'vs2010': from . import vs2010backend return vs2010backend.Vs2010Backend(build, interpreter) + elif backend == 'vs2012': + from . import vs2012backend + return vs2012backend.Vs2012Backend(build, interpreter) + elif backend == 'vs2013': + from . import vs2013backend + return vs2013backend.Vs2013Backend(build, interpreter) elif backend == 'vs2015': from . import vs2015backend return vs2015backend.Vs2015Backend(build, interpreter) diff --git a/mesonbuild/backend/vs2010backend.py b/mesonbuild/backend/vs2010backend.py index 2d3197a..5595fd0 100644 --- a/mesonbuild/backend/vs2010backend.py +++ b/mesonbuild/backend/vs2010backend.py @@ -27,7 +27,7 @@ from .. import mlog from .. import compilers from ..interpreter import Interpreter from ..mesonlib import ( - MesonException, python_command, replace_if_different, OptionKey, + File, MesonException, python_command, replace_if_different, OptionKey, version_compare, ) from ..environment import Environment, build_filename @@ -37,8 +37,14 @@ def autodetect_vs_version(build: T.Optional[build.Build], interpreter: T.Optiona if not vs_install_dir: raise MesonException('Could not detect Visual Studio: Environment variable VSINSTALLDIR is not set!\n' 'Are you running meson from the Visual Studio Developer Command Prompt?') - # VisualStudioVersion is set since Visual Studio 12.0, but sometimes + # VisualStudioVersion is set since Visual Studio 11.0, but sometimes # vcvarsall.bat doesn't set it, so also use VSINSTALLDIR + if vs_version == '11.0' or 'Visual Studio 11' in vs_install_dir: + from mesonbuild.backend.vs2012backend import Vs2012Backend + return Vs2012Backend(build, interpreter) + if vs_version == '12.0' or 'Visual Studio 12' in vs_install_dir: + from mesonbuild.backend.vs2013backend import Vs2013Backend + return Vs2013Backend(build, interpreter) if vs_version == '14.0' or 'Visual Studio 14' in vs_install_dir: from mesonbuild.backend.vs2015backend import Vs2015Backend return Vs2015Backend(build, interpreter) @@ -210,7 +216,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 is not None and vs_version >= '15.0' else '' script_path = os.environ['VCINSTALLDIR'] + relative_path + 'vcvarsall.bat' if os.path.exists(script_path): if has_arch_values: @@ -1149,8 +1155,32 @@ class Vs2010Backend(backends.Backend): lobj = self.build.targets[t.get_id()] linkname = os.path.join(down, self.get_target_filename_for_linking(lobj)) if t in target.link_whole_targets: - # /WHOLEARCHIVE:foo must go into AdditionalOptions - extra_link_args += compiler.get_link_whole_for(linkname) + if compiler.id == 'msvc' and version_compare(compiler.version, '<19.00.23918'): + # Expand our object lists manually if we are on pre-Visual Studio 2015 Update 2 + l = t.extract_all_objects(False) + + # Unforunately, we can't use self.object_filename_from_source() + gensrclist: T.List[File] = [] + for gen in l.genlist: + for src in gen.get_outputs(): + if self.environment.is_source(src) and not self.environment.is_header(src): + path = self.get_target_generated_dir(t, gen, src) + gen_src_ext = '.' + os.path.splitext(path)[1][1:] + extra_link_args.append(path[:-len(gen_src_ext)] + '.obj') + + for src in l.srclist: + obj_basename = None + if self.environment.is_source(src) and not self.environment.is_header(src): + obj_basename = self.object_filename_from_source(t, src) + target_private_dir = self.relpath(self.get_target_private_dir(t), + self.get_target_dir(t)) + rel_obj = os.path.join(target_private_dir, obj_basename) + extra_link_args.append(rel_obj) + + extra_link_args.extend(self.flatten_object_list(t)) + else: + # /WHOLEARCHIVE:foo must go into AdditionalOptions + extra_link_args += compiler.get_link_whole_for(linkname) # To force Visual Studio to build this project even though it # has no sources, we include a reference to the vcxproj file # that builds this target. Technically we should add this only diff --git a/mesonbuild/backend/vs2012backend.py b/mesonbuild/backend/vs2012backend.py new file mode 100644 index 0000000..a9ba5f4 --- /dev/null +++ b/mesonbuild/backend/vs2012backend.py @@ -0,0 +1,38 @@ +# Copyright 2014-2016 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. + +from .vs2010backend import Vs2010Backend +from ..mesonlib import MesonException +from ..interpreter import Interpreter +from ..build import Build +import typing as T + + +class Vs2012Backend(Vs2010Backend): + def __init__(self, build: T.Optional[Build], interpreter: T.Optional[Interpreter]): + super().__init__(build, interpreter) + self.name = 'vs2012' + self.vs_version = '2012' + if self.environment is not None: + # TODO: we assume host == build + comps = self.environment.coredata.compilers.host + if comps and all(c.id == 'intel-cl' for c in comps.values()): + c = list(comps.values())[0] + if c.version.startswith('19'): + self.platform_toolset = 'Intel C++ Compiler 19.0' + else: + # We don't have support for versions older than 2019 right now. + raise MesonException('There is currently no support for ICL before 19, patches welcome.') + if self.platform_toolset is None: + self.platform_toolset = 'v110' diff --git a/mesonbuild/backend/vs2013backend.py b/mesonbuild/backend/vs2013backend.py new file mode 100644 index 0000000..0f2c8bd --- /dev/null +++ b/mesonbuild/backend/vs2013backend.py @@ -0,0 +1,38 @@ +# Copyright 2014-2016 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. + +from .vs2010backend import Vs2010Backend +from ..mesonlib import MesonException +from ..interpreter import Interpreter +from ..build import Build +import typing as T + + +class Vs2013Backend(Vs2010Backend): + def __init__(self, build: T.Optional[Build], interpreter: T.Optional[Interpreter]): + super().__init__(build, interpreter) + self.name = 'vs2013' + self.vs_version = '2013' + if self.environment is not None: + # TODO: we assume host == build + comps = self.environment.coredata.compilers.host + if comps and all(c.id == 'intel-cl' for c in comps.values()): + c = list(comps.values())[0] + if c.version.startswith('19'): + self.platform_toolset = 'Intel C++ Compiler 19.0' + else: + # We don't have support for versions older than 2019 right now. + raise MesonException('There is currently no support for ICL before 19, patches welcome.') + if self.platform_toolset is None: + self.platform_toolset = 'v120' diff --git a/mesonbuild/cmake/common.py b/mesonbuild/cmake/common.py index d1f86f0..5cc154c 100644 --- a/mesonbuild/cmake/common.py +++ b/mesonbuild/cmake/common.py @@ -39,7 +39,9 @@ backend_generator_map = { 'ninja': 'Ninja', 'xcode': 'Xcode', 'vs2010': 'Visual Studio 10 2010', - 'vs2015': 'Visual Studio 15 2017', + 'vs2012': 'Visual Studio 11 2012', + 'vs2013': 'Visual Studio 12 2013', + 'vs2015': 'Visual Studio 14 2015', 'vs2017': 'Visual Studio 15 2017', 'vs2019': 'Visual Studio 16 2019', } diff --git a/mesonbuild/coredata.py b/mesonbuild/coredata.py index 107e4b8..a0ee7df 100644 --- a/mesonbuild/coredata.py +++ b/mesonbuild/coredata.py @@ -44,7 +44,7 @@ if T.TYPE_CHECKING: CompilerCheckCacheKey = T.Tuple[T.Tuple[str, ...], str, str, T.Tuple[str, ...], str] version = '0.58.999' -backendlist = ['ninja', 'vs', 'vs2010', 'vs2015', 'vs2017', 'vs2019', 'xcode'] +backendlist = ['ninja', 'vs', 'vs2010', 'vs2012', 'vs2013', 'vs2015', 'vs2017', 'vs2019', 'xcode'] default_yielding = False diff --git a/mesonbuild/interpreter/interpreter.py b/mesonbuild/interpreter/interpreter.py index f1869e3..2faec4e 100644 --- a/mesonbuild/interpreter/interpreter.py +++ b/mesonbuild/interpreter/interpreter.py @@ -752,14 +752,7 @@ external dependencies (including libraries) must go to "dependencies".''') def func_subproject(self, nodes, args, kwargs): if len(args) != 1: raise InterpreterException('Subproject takes exactly one argument') - subp_name = args[0] - subp = self.do_subproject(subp_name, 'meson', kwargs) - # Update the holder maps from the subproject. Additional entries to the - # holder maps can be added through imported Meson modules - if isinstance(subp.held_object, Interpreter): - self.holder_map.update(subp.held_object.holder_map) - self.bound_holder_map.update(subp.held_object.bound_holder_map) - return subp + return self.do_subproject(args[0], 'meson', kwargs) def disabled_subproject(self, subp_name, disabled_feature=None, exception=None): sub = SubprojectHolder(NullSubprojectInterpreter(), os.path.join(self.subproject_dir, subp_name), @@ -824,7 +817,7 @@ external dependencies (including libraries) must go to "dependencies".''') elif method == 'cmake': return self._do_subproject_cmake(subp_name, subdir, subdir_abs, default_options, kwargs) else: - raise InterpreterException(f'The method {method} is invalid for the subproject {subp_name}') + raise mesonlib.MesonBugException(f'The method {method} is invalid for the subproject {subp_name}') # Invalid code is always an error except InvalidCode: raise @@ -879,6 +872,10 @@ external dependencies (including libraries) must go to "dependencies".''') self.build.subprojects[subp_name] = subi.project_version self.coredata.initialized_subprojects.add(subp_name) self.summary.update(subi.summary) + # Update the holder maps from the subproject. Additional entries to the + # holder maps can be added through imported Meson modules + self.holder_map.update(subi.holder_map) + self.bound_holder_map.update(subi.bound_holder_map) return self.subprojects[subp_name] def _do_subproject_cmake(self, subp_name, subdir, subdir_abs, default_options, kwargs): @@ -2675,7 +2672,7 @@ This will become a hard error in the future.''', location=self.current_node) if len(args) != 2: raise InvalidCode('Set_variable takes two arguments.') varname, value = args - self.set_variable(varname, value) + self.set_variable(varname, value, holderify=True) @noKwargs @noArgsFlattening diff --git a/mesonbuild/interpreterbase/interpreterbase.py b/mesonbuild/interpreterbase/interpreterbase.py index 0acb699..3ea10f4 100644 --- a/mesonbuild/interpreterbase/interpreterbase.py +++ b/mesonbuild/interpreterbase/interpreterbase.py @@ -16,7 +16,7 @@ # or an interpreter-based tool. from .. import mparser, mesonlib, mlog -from .. import environment, dependencies +from .. import environment from .baseobjects import ( InterpreterObject, @@ -592,7 +592,7 @@ The result of this is undefined and will become a hard error in a future Meson r obj.current_node = node return self._holderify(obj.method_call(method_name, args, kwargs)) - def _holderify(self, res: T.Optional[TYPE_var]) -> T.Union[TYPE_elementary, InterpreterObject]: + def _holderify(self, res: T.Union[TYPE_var, InterpreterObject, None]) -> T.Union[TYPE_elementary, InterpreterObject]: if res is None: return None if isinstance(res, (int, bool, str)): @@ -799,7 +799,7 @@ The result of this is undefined and will become a hard error in a future Meson r index = posargs[0] fallback = None if len(posargs) == 2: - fallback = posargs[1] + fallback = self._holderify(posargs[1]) elif len(posargs) > 2: m = 'Array method \'get()\' only takes two arguments: the ' \ 'index and an optional fallback value if the index is ' \ @@ -845,7 +845,7 @@ The result of this is undefined and will become a hard error in a future Meson r return obj[key] if len(posargs) == 2: - fallback = posargs[1] + fallback = self._holderify(posargs[1]) if isinstance(fallback, mparser.BaseNode): return self.evaluate_statement(fallback) return fallback @@ -909,20 +909,34 @@ To specify a keyword argument, use : instead of =.''') raise InvalidArguments('Tried to assign value to a non-variable.') value = self.evaluate_statement(node.value) if not self.is_assignable(value): - raise InvalidCode('Tried to assign an invalid value to variable.') + raise InvalidCode(f'Tried to assign the invalid value "{value}" of type {type(value).__name__} to variable.') # For mutable objects we need to make a copy on assignment if isinstance(value, MutableInterpreterObject): value = copy.deepcopy(value) self.set_variable(var_name, value) return None - def set_variable(self, varname: str, variable: T.Union[TYPE_var, InterpreterObject]) -> None: + def set_variable(self, varname: str, variable: T.Union[TYPE_var, InterpreterObject], *, holderify: bool = False) -> None: if variable is None: raise InvalidCode('Can not assign None to variable.') + if holderify: + variable = self._holderify(variable) + else: + # Ensure that we are never storing a HoldableObject + def check(x: T.Union[TYPE_var, InterpreterObject]) -> None: + if isinstance(x, mesonlib.HoldableObject): + raise mesonlib.MesonBugException(f'set_variable in InterpreterBase called with a HoldableObject {x} of type {type(x).__name__}') + elif isinstance(x, list): + for y in x: + check(y) + elif isinstance(x, dict): + for v in x.values(): + check(v) + check(variable) if not isinstance(varname, str): raise InvalidCode('First argument to set_variable must be a string.') if not self.is_assignable(variable): - raise InvalidCode('Assigned value not of assignable type.') + raise InvalidCode(f'Assigned value "{variable}" of type {type(variable).__name__} is not an assignable type.') if re.match('[_a-zA-Z][_0-9a-zA-Z]*$', varname) is None: raise InvalidCode('Invalid variable name: ' + varname) if varname in self.builtin: @@ -937,8 +951,7 @@ To specify a keyword argument, use : instead of =.''') raise InvalidCode('Unknown variable "%s".' % varname) def is_assignable(self, value: T.Any) -> bool: - return isinstance(value, (InterpreterObject, dependencies.Dependency, - str, int, list, dict, mesonlib.File)) + return isinstance(value, (InterpreterObject, str, int, list, dict)) def validate_extraction(self, buildtarget: mesonlib.HoldableObject) -> None: raise InterpreterException('validate_extraction is not implemented in this context (please file a bug)') diff --git a/run_tests.py b/run_tests.py index 10d63df..ae96bfa 100755 --- a/run_tests.py +++ b/run_tests.py @@ -20,7 +20,6 @@ import sys import time import shutil import subprocess -import tempfile import platform import argparse import traceback @@ -311,7 +310,6 @@ def print_system_info(): def main(): print_system_info() parser = argparse.ArgumentParser() - parser.add_argument('--cov', action='store_true') parser.add_argument('--backend', default=None, dest='backend', choices=backendlist) parser.add_argument('--cross', default=[], dest='cross', action='append') @@ -319,13 +317,6 @@ def main(): parser.add_argument('--failfast', action='store_true') parser.add_argument('--no-unittests', action='store_true', default=False) (options, _) = parser.parse_known_args() - # Enable coverage early... - enable_coverage = options.cov - if enable_coverage: - os.makedirs('.coverage', exist_ok=True) - sys.argv.remove('--cov') - import coverage - coverage.process_startup() returncode = 0 backend, _ = guess_backend(options.backend, shutil.which('msbuild')) no_unittests = options.no_unittests @@ -349,52 +340,41 @@ def main(): # Run tests # Can't pass arguments to unit tests, so set the backend to use in the environment env = os.environ.copy() - with tempfile.TemporaryDirectory() as temp_dir: - # Enable coverage on all subsequent processes. - if enable_coverage: - Path(temp_dir, 'usercustomize.py').open('w').write( - 'import coverage\n' - 'coverage.process_startup()\n') - env['COVERAGE_PROCESS_START'] = '.coveragerc' - if 'PYTHONPATH' in env: - env['PYTHONPATH'] = os.pathsep.join([temp_dir, env.get('PYTHONPATH')]) - else: - env['PYTHONPATH'] = temp_dir - if not options.cross: - cmd = mesonlib.python_command + ['run_meson_command_tests.py', '-v'] + if not options.cross: + cmd = mesonlib.python_command + ['run_meson_command_tests.py', '-v'] + if options.failfast: + cmd += ['--failfast'] + returncode += subprocess.call(cmd, env=env) + if options.failfast and returncode != 0: + return returncode + if no_unittests: + print('Skipping all unit tests.') + print(flush=True) + returncode = 0 + else: + print(mlog.bold('Running unittests.')) + print(flush=True) + cmd = mesonlib.python_command + ['run_unittests.py', '--backend=' + backend.name, '-v'] if options.failfast: cmd += ['--failfast'] returncode += subprocess.call(cmd, env=env) if options.failfast and returncode != 0: return returncode - if no_unittests: - print('Skipping all unit tests.') - print(flush=True) - returncode = 0 - else: - print(mlog.bold('Running unittests.')) - print(flush=True) - cmd = mesonlib.python_command + ['run_unittests.py', '--backend=' + backend.name, '-v'] - if options.failfast: - cmd += ['--failfast'] - returncode += subprocess.call(cmd, env=env) - if options.failfast and returncode != 0: - return returncode - cmd = mesonlib.python_command + ['run_project_tests.py'] + sys.argv[1:] + cmd = mesonlib.python_command + ['run_project_tests.py'] + sys.argv[1:] + returncode += subprocess.call(cmd, env=env) + else: + cross_test_args = mesonlib.python_command + ['run_cross_test.py'] + for cf in options.cross: + print(mlog.bold(f'Running {cf} cross tests.')) + print(flush=True) + cmd = cross_test_args + ['cross/' + cf] + if options.failfast: + cmd += ['--failfast'] + if options.cross_only: + cmd += ['--cross-only'] returncode += subprocess.call(cmd, env=env) - else: - cross_test_args = mesonlib.python_command + ['run_cross_test.py'] - for cf in options.cross: - print(mlog.bold(f'Running {cf} cross tests.')) - print(flush=True) - cmd = cross_test_args + ['cross/' + cf] - if options.failfast: - cmd += ['--failfast'] - if options.cross_only: - cmd += ['--cross-only'] - returncode += subprocess.call(cmd, env=env) - if options.failfast and returncode != 0: - return returncode + if options.failfast and returncode != 0: + return returncode return returncode if __name__ == '__main__': diff --git a/run_unittests.py b/run_unittests.py index b55ba96..13149ce 100755 --- a/run_unittests.py +++ b/run_unittests.py @@ -10302,6 +10302,13 @@ def main(): pytest_args += ['--color=yes'] pytest_args += ['./run_unittests.py'] pytest_args += convert_args(sys.argv[1:]) + # Always disable pytest-cov because we use a custom setup + try: + import pytest_cov # noqa: F401 + print('Disabling pytest-cov') + pytest_args += ['-p' 'no:cov'] + except ImportError: + pass return subprocess.run(python_command + ['-m', 'pytest'] + pytest_args).returncode except ImportError: print('pytest-xdist not found, using unittest instead') diff --git a/test cases/common/137 whole archive/meson.build b/test cases/common/137 whole archive/meson.build index 133146b..d4cbb83 100644 --- a/test cases/common/137 whole archive/meson.build +++ b/test cases/common/137 whole archive/meson.build @@ -1,15 +1,14 @@ project('whole archive', 'c') -if meson.backend() == 'xcode' - error('MESON_SKIP_TEST: whole-archive not supported in Xcode. Patches welcome.') +if meson.backend() == 'xcode' or \ + meson.backend() == 'vs2010' or \ + meson.backend() == 'vs2012' or \ + meson.backend() == 'vs2013' + error('MESON_SKIP_TEST: whole-archive not supported in Xcode nor pre-VS2015 IDE. Patches welcome.') endif add_project_arguments('-I' + meson.source_root(), language : 'c') -if meson.backend() == 'vs2010' - error('MESON_SKIP_TEST whole-archive not supported in VS2010. Patches welcome.') -endif - # Test 1: link_whole keeps all symbols # Make static func1 subdir('st_func1') diff --git a/test cases/common/242 set and get variable/meson.build b/test cases/common/242 set and get variable/meson.build new file mode 100644 index 0000000..6023e88 --- /dev/null +++ b/test cases/common/242 set and get variable/meson.build @@ -0,0 +1,63 @@ +project('set and get') + +var1 = 'test1.txt' +var2 = files('test1.txt')[0] + +# Use is_disabler for accessing variables +assert(var1 == 'test1.txt') +assert(not is_disabler(var2)) + +# Ensure that set variables behave correctly +set_variable('var3', 'test2.txt') +set_variable('var4', files('test2.txt')[0]) + +assert(var3 == 'test2.txt') +assert(not is_disabler(var4)) + +# Test get_variable directly +assert(get_variable('var1') == 'test1.txt') +assert(not is_disabler(get_variable('var2'))) +assert(get_variable('var3') == 'test2.txt') +assert(not is_disabler(get_variable('var4'))) + +# Test get_variable indirectly + +var5 = get_variable('var1') +var6 = get_variable('var2') +var7 = get_variable('var3') +var8 = get_variable('var4') +set_variable('var9', get_variable('var7')) +set_variable('var0', get_variable('var8')) + +assert(var5 == 'test1.txt') +assert(not is_disabler(var6)) +assert(var7 == 'test2.txt') +assert(not is_disabler(var8)) +assert(get_variable('var9') == 'test2.txt') +assert(not is_disabler(get_variable('var0'))) + +# test dict get +dict = {'a': var2} + +dict_t1 = dict['a'] +dict_t2 = dict.get('a') +dict_t3 = dict.get('a', var2) +dict_t4 = dict.get('b', var2) + +assert(not is_disabler(dict_t1)) +assert(not is_disabler(dict_t2)) +assert(not is_disabler(dict_t3)) +assert(not is_disabler(dict_t4)) + +# test lists +list = [var2] + +list_t1 = list[0] +list_t2 = list.get(0) +list_t3 = list.get(0, var2) +list_t4 = list.get(1, var2) + +assert(not is_disabler(list_t1)) +assert(not is_disabler(list_t2)) +assert(not is_disabler(list_t3)) +assert(not is_disabler(list_t4)) diff --git a/test cases/common/242 set and get variable/test1.txt b/test cases/common/242 set and get variable/test1.txt new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/test cases/common/242 set and get variable/test1.txt diff --git a/test cases/common/242 set and get variable/test2.txt b/test cases/common/242 set and get variable/test2.txt new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/test cases/common/242 set and get variable/test2.txt diff --git a/tools/run_with_cov.py b/tools/run_with_cov.py new file mode 100755 index 0000000..17fb300 --- /dev/null +++ b/tools/run_with_cov.py @@ -0,0 +1,53 @@ +#!/usr/bin/env python3 +# Copyright 2021 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 subprocess +import coverage +import os +import sys +from pathlib import Path + +root_path = Path(__file__).parent.parent.absolute() + +# Python magic so we can import mesonlib +sys.path.append(root_path.as_posix()) +from mesonbuild import mesonlib + +def generate_coveragerc() -> Path: + i_file = (root_path / 'data' / '.coveragerc.in') + o_file = (root_path / '.coveragerc') + raw = i_file.read_text() + raw = raw.replace('@ROOT@', root_path.as_posix()) + o_file.write_text(raw) + return o_file + +def main() -> int: + # Remove old run data + out_dir = root_path / '.coverage' + mesonlib.windows_proof_rmtree(out_dir.as_posix()) + out_dir.mkdir(parents=True, exist_ok=True) + + # Setup coverage + python_path = (root_path / 'ci').as_posix() + os.environ['PYTHONPATH'] = os.pathsep.join([python_path, os.environ.get('PYTHONPATH', '')]) + os.environ['COVERAGE_PROCESS_START'] = generate_coveragerc().as_posix() + coverage.process_startup() + + # Run the actual command + cmd = mesonlib.python_command + sys.argv[1:] + return subprocess.run(cmd, env=os.environ.copy()).returncode + +if __name__ == '__main__': + raise SystemExit(main()) |