From 1210a67f66df15a54fc7c65f7406bcd5391d15aa Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Mon, 24 Feb 2020 15:15:06 -0800 Subject: mesonbuild: Add mcompile command This is tested working with both msbuild and ninja/samu. Since our xcode support is pretty much broken I didn't bother. Fixes #6670 --- .github/workflows/lint_mypy.yml | 2 +- docs/markdown/snippets/meson_compile_command.md | 21 ++++ mesonbuild/mcompile.py | 123 ++++++++++++++++++++++++ mesonbuild/mesonmain.py | 4 +- run_unittests.py | 15 +++ 5 files changed, 163 insertions(+), 2 deletions(-) create mode 100644 docs/markdown/snippets/meson_compile_command.md create mode 100644 mesonbuild/mcompile.py diff --git a/.github/workflows/lint_mypy.yml b/.github/workflows/lint_mypy.yml index 8f6d917..7c83714 100644 --- a/.github/workflows/lint_mypy.yml +++ b/.github/workflows/lint_mypy.yml @@ -30,4 +30,4 @@ jobs: with: python-version: '3.x' - run: python -m pip install mypy - - run: mypy --follow-imports=skip mesonbuild/interpreterbase.py mesonbuild/mtest.py mesonbuild/minit.py mesonbuild/mintro.py mesonbuild/mparser.py mesonbuild/msetup.py mesonbuild/ast mesonbuild/wrap tools/ mesonbuild/modules/fs.py mesonbuild/dependencies/boost.py mesonbuild/dependencies/mpi.py mesonbuild/dependencies/hdf5.py mesonbuild/compilers/mixins/intel.py mesonbuild/mlog.py + - run: mypy --follow-imports=skip mesonbuild/interpreterbase.py mesonbuild/mtest.py mesonbuild/minit.py mesonbuild/mintro.py mesonbuild/mparser.py mesonbuild/msetup.py mesonbuild/ast mesonbuild/wrap tools/ mesonbuild/modules/fs.py mesonbuild/dependencies/boost.py mesonbuild/dependencies/mpi.py mesonbuild/dependencies/hdf5.py mesonbuild/compilers/mixins/intel.py mesonbuild/mlog.py mesonbuild/mcompile.py diff --git a/docs/markdown/snippets/meson_compile_command.md b/docs/markdown/snippets/meson_compile_command.md new file mode 100644 index 0000000..e029ff8 --- /dev/null +++ b/docs/markdown/snippets/meson_compile_command.md @@ -0,0 +1,21 @@ +## Backend agnostic compile command + +A new `meson compile` command has been added to support backend agnostic +compilation. It accepts two arguments, `-j` and `-l`, which are used if +possible (`-l` does nothing with msbuild). A `-j` or `-l` value < 1 lets the +backend decide how many threads to use. For msbuild this means `-m`, for +ninja it means passing no arguments. + +```console +meson builddir --backend vs +meson compile -C builddir -j0 # this is the same as `msbuild builddir/my.sln -m` +``` + +```console +meson builddir +meson compile -C builddir -j3 # this is the same as `ninja -C builddir -j3` +``` + +Additionally `meson compile` provides a `--clean` switch to clean the project. + +A complete list of arguments is always documented via `meson compile --help` diff --git a/mesonbuild/mcompile.py b/mesonbuild/mcompile.py new file mode 100644 index 0000000..372c853 --- /dev/null +++ b/mesonbuild/mcompile.py @@ -0,0 +1,123 @@ +# Copyright 2020 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. + +"""Entrypoint script for backend agnostic compile.""" + +import os +import pathlib +import shutil +import sys +import typing as T + +from . import mlog +from . import mesonlib +from .mesonlib import MesonException + +if T.TYPE_CHECKING: + import argparse + + +def add_arguments(parser: 'argparse.ArgumentParser') -> None: + """Add compile specific arguments.""" + parser.add_argument( + '-j', '--jobs', + action='store', + default=0, + type=int, + help='The number of worker jobs to run (if supported). If the value is less than 1 the build program will guess.' + ) + parser.add_argument( + '-l', '--load-average', + action='store', + default=0, + type=int, + help='The system load average to try to maintain (if supported)' + ) + parser.add_argument( + '--clean', + action='store_true', + help='Clean the build directory.' + ) + parser.add_argument( + '-C', + action='store', + dest='builddir', + type=pathlib.Path, + required=True, + default='.', + help='The directory containing build files to be built.' + ) + + +def run(options: 'argparse.Namespace') -> int: + bdir = options.builddir # type: pathlib.Path + if not bdir.exists(): + raise MesonException('Path to builddir {} does not exist!'.format(str(bdir.resolve()))) + if not bdir.is_dir(): + raise MesonException('builddir path should be a directory.') + + cmd = [] # type: T.List[str] + runner = None # type T.Optional[str] + slns = list(bdir.glob('*.sln')) + + if (bdir / 'build.ninja').exists(): + runner = os.environ.get('NINJA') + if not runner: + if shutil.which('ninja'): + runner = 'ninja' + elif shutil.which('samu'): + runner = 'samu' + + if runner is None: + raise MesonException('Cannot find either ninja or samu.') + + cmd = [runner, '-C', bdir.as_posix()] + + # If the value is set to < 1 then don't set anything, which let's + # ninja/samu decide what to do. + if options.jobs > 0: + cmd.extend(['-j', str(options.jobs)]) + if options.load_average > 0: + cmd.extend(['-l', str(options.load_average)]) + if options.clean: + cmd.append('clean') + + # TODO: with python 3.8 this could be `elif slns := bdir.glob('*.sln'):` + elif slns: + assert len(slns) == 1, 'More than one solution in a project?' + + sln = slns[0] + cmd = ['msbuild', str(sln.resolve())] + + # In msbuild `-m` with no number means "detect cpus", the default is `-m1` + if options.jobs > 0: + cmd.append('-m{}'.format(options.jobs)) + else: + cmd.append('-m') + + if options.load_average: + mlog.warning('Msbuild does not have a load-average switch, ignoring.') + if options.clean: + cmd.extend(['/t:Clean']) + + # TODO: xcode? + else: + raise MesonException( + 'Could not find any runner or backend for directory {}'.format(bdir.resolve().as_posix())) + + mlog.log('Found runner:', runner) + + p, *_ = mesonlib.Popen_safe(cmd, stdout=sys.stdout.buffer, stderr=sys.stderr.buffer) + + return p.returncode diff --git a/mesonbuild/mesonmain.py b/mesonbuild/mesonmain.py index 6ec9682..b6b11df 100644 --- a/mesonbuild/mesonmain.py +++ b/mesonbuild/mesonmain.py @@ -22,7 +22,7 @@ import shutil from . import mesonlib from . import mlog -from . import mconf, mdist, minit, minstall, mintro, msetup, mtest, rewriter, msubprojects, munstable_coredata +from . import mconf, mdist, minit, minstall, mintro, msetup, mtest, rewriter, msubprojects, munstable_coredata, mcompile from .mesonlib import MesonException from .environment import detect_msys2_arch from .wrap import wraptool @@ -62,6 +62,8 @@ class CommandLineParser: help_msg='Print help of a subcommand') self.add_command('rewrite', lambda parser: rewriter.add_arguments(parser, self.formatter), rewriter.run, help_msg='Modify the project definition') + self.add_command('compile', mcompile.add_arguments, mcompile.run, + help_msg='Build the project') # Hidden commands self.add_command('runpython', self.add_runpython_arguments, self.run_runpython_command, diff --git a/run_unittests.py b/run_unittests.py index 3f5c87e..b9dcd41 100755 --- a/run_unittests.py +++ b/run_unittests.py @@ -4412,6 +4412,21 @@ recommended as it is not supported on some platforms''') else: self.assertEqual(expected_lines, out_lines) + def test_meson_compile(self): + """Test the meson compile command.""" + prog = 'trivialprog' + if is_windows(): + prog = '{}.exe'.format(prog) + + testdir = os.path.join(self.common_test_dir, '1 trivial') + self.init(testdir) + self._run([*self.meson_command, 'compile', '-C', self.builddir]) + # If compile worked then we should get a program + self.assertPathExists(os.path.join(self.builddir, prog)) + + self._run([*self.meson_command, 'compile', '-C', self.builddir, '--clean']) + self.assertPathDoesNotExist(os.path.join(self.builddir, prog)) + class FailureTests(BasePlatformTests): ''' -- cgit v1.1