diff options
author | Xavier Claessens <xavier.claessens@collabora.com> | 2021-09-30 11:54:43 -0400 |
---|---|---|
committer | Jussi Pakkanen <jpakkane@gmail.com> | 2021-10-10 23:15:18 +0300 |
commit | 928078982c8643bffd95a8da06a1b4494fe87e2b (patch) | |
tree | 1e8e50892e5f329927d9196cea85e66801c1af03 | |
parent | 31bea202c9dc9d288d787f0073f0e221971669ba (diff) | |
download | meson-928078982c8643bffd95a8da06a1b4494fe87e2b.zip meson-928078982c8643bffd95a8da06a1b4494fe87e2b.tar.gz meson-928078982c8643bffd95a8da06a1b4494fe87e2b.tar.bz2 |
Add --vsenv command line option and active VS only when needed
-rw-r--r-- | docs/markdown/snippets/vsenv.md | 14 | ||||
-rw-r--r-- | mesonbuild/backend/ninjabackend.py | 3 | ||||
-rw-r--r-- | mesonbuild/build.py | 1 | ||||
-rw-r--r-- | mesonbuild/interpreter/interpreter.py | 10 | ||||
-rw-r--r-- | mesonbuild/mcompile.py | 22 | ||||
-rw-r--r-- | mesonbuild/mdevenv.py | 3 | ||||
-rw-r--r-- | mesonbuild/mdist.py | 3 | ||||
-rw-r--r-- | mesonbuild/mesonlib/__init__.py | 1 | ||||
-rw-r--r-- | mesonbuild/mesonlib/vsenv.py | 100 | ||||
-rw-r--r-- | mesonbuild/mesonmain.py | 91 | ||||
-rw-r--r-- | mesonbuild/msetup.py | 4 | ||||
-rwxr-xr-x | run_project_tests.py | 3 | ||||
-rwxr-xr-x | run_tests.py | 4 | ||||
-rwxr-xr-x | run_unittests.py | 6 |
14 files changed, 150 insertions, 115 deletions
diff --git a/docs/markdown/snippets/vsenv.md b/docs/markdown/snippets/vsenv.md new file mode 100644 index 0000000..16e9424 --- /dev/null +++ b/docs/markdown/snippets/vsenv.md @@ -0,0 +1,14 @@ +## Force Visual Studio environment activation + +Since `0.59.0`, meson automatically activates Visual Studio environment on Windows +for all its subcommands, but only if no other compilers (e.g. `gcc` or `clang`) +are found, and silently continue if Visual Studio activation fails. + +`meson setup --vsenv` command line argument can now be used to force Visual Studio +activation even when other compilers are found. It also make Meson abort with an +error message when activation fails. This is especially useful for Github Action +because their Windows images have gcc in their PATH by default. + +`--vsenv` is set by default when using `vs` backend. + +Only `setup`, `compile`, `dist` and `devenv` subcommands now activate Visual Studio. diff --git a/mesonbuild/backend/ninjabackend.py b/mesonbuild/backend/ninjabackend.py index a053be6..37f5259 100644 --- a/mesonbuild/backend/ninjabackend.py +++ b/mesonbuild/backend/ninjabackend.py @@ -49,7 +49,6 @@ from ..mesonlib import get_compiler_for_source, has_path_sep, OptionKey from .backends import CleanTrees from ..build import GeneratedList, InvalidArguments, ExtractedObjects from ..interpreter import Interpreter -from ..mesonmain import need_setup_vsenv if T.TYPE_CHECKING: from .._typing import ImmutableListProtocol @@ -513,7 +512,7 @@ class NinjaBackend(backends.Backend): def generate(self): ninja = environment.detect_ninja_command_and_version(log=True) - if need_setup_vsenv: + if self.build.need_vsenv: builddir = Path(self.environment.get_build_dir()) try: # For prettier printing, reduce to a relative path. If diff --git a/mesonbuild/build.py b/mesonbuild/build.py index 273f457..37c2e02 100644 --- a/mesonbuild/build.py +++ b/mesonbuild/build.py @@ -272,6 +272,7 @@ class Build: environment.is_cross_build(), {}, {}) self.devenv: T.List[EnvironmentVariables] = [] self.modules: T.List[str] = [] + self.need_vsenv = False def get_build_targets(self): build_targets = OrderedDict() diff --git a/mesonbuild/interpreter/interpreter.py b/mesonbuild/interpreter/interpreter.py index b82525f..9f2c705 100644 --- a/mesonbuild/interpreter/interpreter.py +++ b/mesonbuild/interpreter/interpreter.py @@ -1115,6 +1115,16 @@ external dependencies (including libraries) must go to "dependencies".''') mlog.log('Project name:', mlog.bold(proj_name)) mlog.log('Project version:', mlog.bold(self.project_version)) + if not self.is_subproject(): + # We have to activate VS before adding languages and before calling + # self.set_backend() otherwise it wouldn't be able to detect which + # vs backend version we need. But after setting default_options in case + # the project sets vs backend by default. + backend = self.coredata.get_option(OptionKey('backend')) + force_vsenv = self.user_defined_options.vsenv or backend.startswith('vs') + if mesonlib.setup_vsenv(force_vsenv): + self.build.need_vsenv = True + self.add_languages(proj_langs, True, MachineChoice.HOST) self.add_languages(proj_langs, False, MachineChoice.BUILD) diff --git a/mesonbuild/mcompile.py b/mesonbuild/mcompile.py index f586fde..146ea13 100644 --- a/mesonbuild/mcompile.py +++ b/mesonbuild/mcompile.py @@ -26,9 +26,10 @@ from pathlib import Path from . import mlog from . import mesonlib from . import coredata -from .mesonlib import MesonException +from .mesonlib import MesonException, RealPathAction, setup_vsenv from mesonbuild.environment import detect_ninja from mesonbuild.coredata import UserArrayOption +from mesonbuild import build if T.TYPE_CHECKING: import argparse @@ -286,14 +287,9 @@ def add_arguments(parser: 'argparse.ArgumentParser') -> None: action='store_true', help='Clean the build directory.' ) - parser.add_argument( - '-C', - action='store', - dest='builddir', - type=Path, - default='.', - help='The directory containing build files to be built.' - ) + parser.add_argument('-C', dest='wd', action=RealPathAction, + help='directory to cd into before running') + parser.add_argument( '-j', '--jobs', action='store', @@ -333,8 +329,12 @@ def add_arguments(parser: 'argparse.ArgumentParser') -> None: ) def run(options: 'argparse.Namespace') -> int: - bdir = options.builddir # type: Path - validate_builddir(bdir.resolve()) + bdir = Path(options.wd) + buildfile = bdir / 'meson-private' / 'build.dat' + if not buildfile.is_file(): + raise MesonException(f'Directory {options.wd!r} does not seem to be a Meson build directory.') + b = build.load(options.wd) + setup_vsenv(b.need_vsenv) cmd = [] # type: T.List[str] env = None # type: T.Optional[T.Dict[str, str]] diff --git a/mesonbuild/mdevenv.py b/mesonbuild/mdevenv.py index c304cbb..eb05020 100644 --- a/mesonbuild/mdevenv.py +++ b/mesonbuild/mdevenv.py @@ -4,7 +4,7 @@ import tempfile from pathlib import Path from . import build -from .mesonlib import MesonException, RealPathAction, is_windows +from .mesonlib import MesonException, RealPathAction, is_windows, setup_vsenv import typing as T @@ -41,6 +41,7 @@ def run(options: argparse.Namespace) -> int: if not buildfile.is_file(): raise MesonException(f'Directory {options.wd!r} does not seem to be a Meson build directory.') b = build.load(options.wd) + setup_vsenv(b.need_vsenv) devenv = get_env(b, options.wd) diff --git a/mesonbuild/mdist.py b/mesonbuild/mdist.py index 43bc66f..d5db4fb 100644 --- a/mesonbuild/mdist.py +++ b/mesonbuild/mdist.py @@ -26,7 +26,7 @@ from glob import glob from pathlib import Path from mesonbuild.environment import detect_ninja from mesonbuild.mesonlib import (MesonException, RealPathAction, quiet_git, - windows_proof_rmtree) + windows_proof_rmtree, setup_vsenv) from mesonbuild.wrap import wrap from mesonbuild import mlog, build from .scripts.meson_exe import run_exe @@ -281,6 +281,7 @@ def run(options): if not buildfile.is_file(): raise MesonException(f'Directory {options.wd!r} does not seem to be a Meson build directory.') b = build.load(options.wd) + setup_vsenv(b.need_vsenv) # This import must be load delayed, otherwise it will get the default # value of None. from mesonbuild.mesonlib import get_meson_command diff --git a/mesonbuild/mesonlib/__init__.py b/mesonbuild/mesonlib/__init__.py index 5b646b5..5b2e82d 100644 --- a/mesonbuild/mesonlib/__init__.py +++ b/mesonbuild/mesonlib/__init__.py @@ -19,6 +19,7 @@ import os from .universal import * +from .vsenv import setup_vsenv # Here we import either the posix implementations, the windows implementations, # or a generic no-op implementation diff --git a/mesonbuild/mesonlib/vsenv.py b/mesonbuild/mesonlib/vsenv.py new file mode 100644 index 0000000..1966634 --- /dev/null +++ b/mesonbuild/mesonlib/vsenv.py @@ -0,0 +1,100 @@ +import os +import subprocess +import json +import pathlib +import shutil +import tempfile + +from .. import mlog +from .universal import MesonException, is_windows + + +bat_template = '''@ECHO OFF + +call "{}" + +ECHO {} +SET +''' + +# If on Windows and VS is installed but not set up in the environment, +# set it to be runnable. In this way Meson can be directly invoked +# from any shell, VS Code etc. +def _setup_vsenv(force: bool) -> bool: + if not is_windows(): + return False + if os.environ.get('OSTYPE') == 'cygwin': + return False + if 'Visual Studio' in os.environ['PATH']: + return False + # VSINSTALL is set when running setvars from a Visual Studio installation + # Tested with Visual Studio 2012 and 2017 + if 'VSINSTALLDIR' in os.environ: + return False + # Check explicitly for cl when on Windows + if shutil.which('cl.exe'): + return False + if not force: + if shutil.which('cc'): + return False + if shutil.which('gcc'): + return False + if shutil.which('clang'): + return False + if shutil.which('clang-cl'): + return False + + root = os.environ.get("ProgramFiles(x86)") or os.environ.get("ProgramFiles") + bat_locator_bin = pathlib.Path(root, 'Microsoft Visual Studio/Installer/vswhere.exe') + if not bat_locator_bin.exists(): + raise MesonException(f'Could not find {bat_locator_bin}') + bat_json = subprocess.check_output( + [ + str(bat_locator_bin), + '-latest', + '-prerelease', + '-requiresAny', + '-requires', 'Microsoft.VisualStudio.Component.VC.Tools.x86.x64', + '-products', '*', + '-utf8', + '-format', + 'json' + ] + ) + bat_info = json.loads(bat_json) + if not bat_info: + # VS installer instelled but not VS itself maybe? + raise MesonException(f'Could not parse vswhere.exe output') + bat_root = pathlib.Path(bat_info[0]['installationPath']) + bat_path = bat_root / 'VC/Auxiliary/Build/vcvars64.bat' + if not bat_path.exists(): + raise MesonException(f'Could not find {bat_path}') + + mlog.log('Activating VS', bat_info[0]['catalog']['productDisplayVersion']) + bat_separator = '---SPLIT---' + bat_contents = bat_template.format(bat_path, bat_separator) + bat_file = tempfile.NamedTemporaryFile('w', suffix='.bat', encoding='utf-8') + bat_file.write(bat_contents) + bat_file.flush() + bat_output = subprocess.check_output(str(bat_file), universal_newlines=True) + bat_lines = bat_output.split('\n') + bat_separator_seen = False + for bat_line in bat_lines: + if bat_line == bat_separator: + bat_separator_seen = True + continue + if not bat_separator_seen: + continue + if not bat_line: + continue + k, v = bat_line.split('=', 1) + os.environ[k] = v + return True + +def setup_vsenv(force: bool = False): + try: + _setup_vsenv(force) + except MesonException: + if force: + raise + mlog.warning('Failed to activate VS environment:', str(e)) diff --git a/mesonbuild/mesonmain.py b/mesonbuild/mesonmain.py index 5d84b37..a60f914 100644 --- a/mesonbuild/mesonmain.py +++ b/mesonbuild/mesonmain.py @@ -31,96 +31,6 @@ from .mesonlib import MesonException from .environment import detect_msys2_arch from .wrap import wraptool -need_setup_vsenv = False - -bat_template = '''@ECHO OFF - -call "{}" - -ECHO {} -SET -''' - -# If on Windows and VS is installed but not set up in the environment, -# set it to be runnable. In this way Meson can be directly invoked -# from any shell, VS Code etc. -def setup_vsenv() -> None: - import subprocess, json, pathlib - if not mesonlib.is_windows(): - return - bat_placeholder = 'nananananananananananananananana' - # If an existing build tool chain exists in PATH -> do nothing. - if shutil.which('cc'): - return - if shutil.which('gcc'): - return - if shutil.which('clang'): - return - if shutil.which('clang-cl'): - return - if os.environ.get('OSTYPE', bat_placeholder) == 'cygwin': - return - if 'Visual Studio' in os.environ['PATH']: - return - # VSINSTALL is set when running setvars from a Visual Studio installation - # Tested with Visual Studio 2012 and 2017 - if 'VSINSTALLDIR' in os.environ: - return - # Check explicitly for cl when on Windows - if shutil.which('cl.exe'): - return - - root = os.environ.get("ProgramFiles(x86)") or os.environ.get("ProgramFiles") - bat_locator_bin = pathlib.Path(root, 'Microsoft Visual Studio/Installer/vswhere.exe') - if not bat_locator_bin.exists(): - return - bat_json = subprocess.check_output( - [ - str(bat_locator_bin), - '-latest', - '-prerelease', - '-requiresAny', - '-requires', 'Microsoft.VisualStudio.Component.VC.Tools.x86.x64', - '-products', '*', - '-utf8', - '-format', - 'json' - ] - ) - bat_info = json.loads(bat_json) - if not bat_info: - # VS installer instelled but not VS itself maybe? - return - print('Activating VS', bat_info[0]['catalog']['productDisplayVersion']) - bat_root = pathlib.Path(bat_info[0]['installationPath']) - bat_path = bat_root / 'VC/Auxiliary/Build/vcvars64.bat' - if not bat_path.exists(): - return - - bat_file = pathlib.Path.home() / 'vsdetect.bat' - - bat_separator = '---SPLIT---' - bat_contents = bat_template.format(bat_path, bat_separator) - bat_file.write_text(bat_contents, encoding='utf-8') - try: - bat_output = subprocess.check_output(str(bat_file), universal_newlines=True) - finally: - bat_file.unlink() - bat_lines = bat_output.split('\n') - bat_separator_seen = False - for bat_line in bat_lines: - if bat_line == bat_separator: - bat_separator_seen = True - continue - if not bat_separator_seen: - continue - if not bat_line: - continue - k, v = bat_line.split('=', 1) - os.environ[k] = v - global need_setup_vsenv - need_setup_vsenv = True - # Note: when adding arguments, please also add them to the completion # scripts in $MESONSRC/data/shell-completions/ @@ -316,7 +226,6 @@ def run(original_args, mainfile): return CommandLineParser().run(args) def main(): - setup_vsenv() # Always resolve the command path so Ninja can find it for regen, tests, etc. if 'meson.exe' in sys.executable: assert os.path.isabs(sys.executable) diff --git a/mesonbuild/msetup.py b/mesonbuild/msetup.py index a7d28dd..7f2f7ed 100644 --- a/mesonbuild/msetup.py +++ b/mesonbuild/msetup.py @@ -50,6 +50,10 @@ def add_arguments(parser: argparse.ArgumentParser) -> None: default=[], action='append', help='File describing cross compilation environment.') + parser.add_argument('--vsenv', action='store_true', + help='Setup Visual Studio environment even when other compilers are found, ' + + 'abort if Visual Studio is not found. This option has no effect on other ' + + 'platforms than Windows. Defaults to True when using "vs" backend.') parser.add_argument('-v', '--version', action='version', version=coredata.version) parser.add_argument('--profile-self', action='store_true', dest='profile', diff --git a/run_project_tests.py b/run_project_tests.py index 0619bdf..176a938 100755 --- a/run_project_tests.py +++ b/run_project_tests.py @@ -48,10 +48,9 @@ from mesonbuild import mlog from mesonbuild import mtest from mesonbuild.compilers import compiler_from_language, detect_objc_compiler, detect_objcpp_compiler from mesonbuild.build import ConfigurationData -from mesonbuild.mesonlib import MachineChoice, Popen_safe, TemporaryDirectoryWinProof +from mesonbuild.mesonlib import MachineChoice, Popen_safe, TemporaryDirectoryWinProof, setup_vsenv from mesonbuild.mlog import blue, bold, cyan, green, red, yellow, normal_green from mesonbuild.coredata import backendlist, version as meson_version -from mesonbuild.mesonmain import setup_vsenv from mesonbuild.modules.python import PythonExternalProgram from run_tests import get_fake_options, run_configure, get_meson_script from run_tests import get_backend_commands, get_backend_args_for_dir, Backend diff --git a/run_tests.py b/run_tests.py index 90641ad..b6f8884 100755 --- a/run_tests.py +++ b/run_tests.py @@ -42,7 +42,7 @@ from mesonbuild import mtest from mesonbuild import mlog from mesonbuild.environment import Environment, detect_ninja from mesonbuild.coredata import backendlist, version as meson_version -from mesonbuild.mesonlib import OptionKey +from mesonbuild.mesonlib import OptionKey, setup_vsenv NINJA_1_9_OR_NEWER = False NINJA_CMD = None @@ -382,6 +382,6 @@ def main(): return returncode if __name__ == '__main__': - mesonmain.setup_vsenv() + setup_vsenv() print('Meson build system', meson_version, 'Project and Unit Tests') raise SystemExit(main()) diff --git a/run_unittests.py b/run_unittests.py index f438daa..bc20b22 100755 --- a/run_unittests.py +++ b/run_unittests.py @@ -33,14 +33,10 @@ import mesonbuild.environment import mesonbuild.mesonlib import mesonbuild.coredata import mesonbuild.modules.gnome -from mesonbuild.mesonlib import ( - python_command -) +from mesonbuild.mesonlib import python_command, setup_vsenv import mesonbuild.dependencies.base import mesonbuild.modules.pkgconfig -from mesonbuild.mesonmain import setup_vsenv - from unittests.allplatformstests import AllPlatformTests from unittests.darwintests import DarwinTests from unittests.failuretests import FailureTests |