aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorXavier Claessens <xavier.claessens@collabora.com>2021-09-30 11:54:43 -0400
committerJussi Pakkanen <jpakkane@gmail.com>2021-10-10 23:15:18 +0300
commit928078982c8643bffd95a8da06a1b4494fe87e2b (patch)
tree1e8e50892e5f329927d9196cea85e66801c1af03
parent31bea202c9dc9d288d787f0073f0e221971669ba (diff)
downloadmeson-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.md14
-rw-r--r--mesonbuild/backend/ninjabackend.py3
-rw-r--r--mesonbuild/build.py1
-rw-r--r--mesonbuild/interpreter/interpreter.py10
-rw-r--r--mesonbuild/mcompile.py22
-rw-r--r--mesonbuild/mdevenv.py3
-rw-r--r--mesonbuild/mdist.py3
-rw-r--r--mesonbuild/mesonlib/__init__.py1
-rw-r--r--mesonbuild/mesonlib/vsenv.py100
-rw-r--r--mesonbuild/mesonmain.py91
-rw-r--r--mesonbuild/msetup.py4
-rwxr-xr-xrun_project_tests.py3
-rwxr-xr-xrun_tests.py4
-rwxr-xr-xrun_unittests.py6
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