# SPDX-License-Identifier: Apache-2.0
# Copyright 2014-2016 The Meson development team
from __future__ import annotations
import copy
import itertools
import os
import xml.dom.minidom
import xml.etree.ElementTree as ET
import uuid
import typing as T
from pathlib import Path, PurePath, PureWindowsPath
import re
from collections import Counter
from . import backends
from .. import build
from .. import mlog
from .. import compilers
from .. import mesonlib
from ..mesonlib import (
File, MesonBugException, MesonException, replace_if_different, version_compare, MachineChoice
)
from ..options import OptionKey
from ..environment import Environment, build_filename
from .. import coredata
if T.TYPE_CHECKING:
from ..arglist import CompilerArgs
from ..interpreter import Interpreter
Project = T.Tuple[str, Path, str, MachineChoice]
def autodetect_vs_version(build: T.Optional[build.Build], interpreter: T.Optional[Interpreter]) -> backends.Backend:
vs_version = os.getenv('VisualStudioVersion', None)
vs_install_dir = os.getenv('VSINSTALLDIR', None)
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 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)
if vs_version == '15.0' or 'Visual Studio 17' in vs_install_dir or \
'Visual Studio\\2017' in vs_install_dir:
from mesonbuild.backend.vs2017backend import Vs2017Backend
return Vs2017Backend(build, interpreter)
if vs_version == '16.0' or 'Visual Studio 19' in vs_install_dir or \
'Visual Studio\\2019' in vs_install_dir:
from mesonbuild.backend.vs2019backend import Vs2019Backend
return Vs2019Backend(build, interpreter)
if vs_version == '17.0' or 'Visual Studio 22' in vs_install_dir or \
'Visual Studio\\2022' in vs_install_dir:
from mesonbuild.backend.vs2022backend import Vs2022Backend
return Vs2022Backend(build, interpreter)
if 'Visual Studio 10.0' in vs_install_dir:
return Vs2010Backend(build, interpreter)
raise MesonException('Could not detect Visual Studio using VisualStudioVersion: {!r} or VSINSTALLDIR: {!r}!\n'
'Please specify the exact backend to use.'.format(vs_version, vs_install_dir))
def split_o_flags_args(args: T.List[str]) -> T.List[str]:
"""
Splits any /O args and returns them. Does not take care of flags overriding
previous ones. Skips non-O flag arguments.
['/Ox', '/Ob1'] returns ['/Ox', '/Ob1']
['/Oxj', '/MP'] returns ['/Ox', '/Oj']
"""
o_flags = []
for arg in args:
if not arg.startswith('/O'):
continue
flags = list(arg[2:])
# Assume that this one can't be clumped with the others since it takes
# an argument itself
if 'b' in flags:
o_flags.append(arg)
else:
o_flags += ['/O' + f for f in flags]
return o_flags
def generate_guid_from_path(path, path_type) -> str:
return str(uuid.uuid5(uuid.NAMESPACE_URL, 'meson-vs-' + path_type + ':' + str(path))).upper()
def detect_microsoft_gdk(platform: str) -> bool:
return re.match(r'Gaming\.(Desktop|Xbox.XboxOne|Xbox.Scarlett)\.x64', platform, re.IGNORECASE)
def filtered_src_langs_generator(sources: T.List[str]):
for src in sources:
ext = src.split('.')[-1]
if compilers.compilers.is_source_suffix(ext):
yield compilers.compilers.SUFFIX_TO_LANG[ext]
# Returns the source language (i.e. a key from 'lang_suffixes') of the most frequent source language in the given
# list of sources.
# We choose the most frequent language as 'primary' because it means the most sources in a target/project can
# simply refer to the project's shared intellisense define and include fields, rather than have to fill out their
# own duplicate full set of defines/includes/opts intellisense fields. All of which helps keep the vcxproj file
# size down.
def get_primary_source_lang(target_sources: T.List[File], custom_sources: T.List[str]) -> T.Optional[str]:
lang_counts = Counter([compilers.compilers.SUFFIX_TO_LANG[src.suffix] for src in target_sources if compilers.compilers.is_source_suffix(src.suffix)])
lang_counts += Counter(filtered_src_langs_generator(custom_sources))
most_common_lang_list = lang_counts.most_common(1)
# It may be possible that we have a target with no actual src files of interest (e.g. a generator target),
# leaving us with an empty list, which we should handle -
return most_common_lang_list[0][0] if most_common_lang_list else None
# Returns a dictionary (by [src type][build type]) that contains a tuple of -
# (pre-processor defines, include paths, additional compiler options)
# fields to use to fill in the respective intellisense fields of sources that can't simply
# reference and re-use the shared 'primary' language intellisense fields of the vcxproj.
def get_non_primary_lang_intellisense_fields(vslite_ctx: dict,
target_id: str,
primary_src_lang: str) -> T.Dict[str, T.Dict[str, T.Tuple[str, str, str]]]:
defs_paths_opts_per_lang_and_buildtype = {}
for buildtype in coredata.get_genvs_default_buildtype_list():
captured_build_args = vslite_ctx[buildtype][target_id] # Results in a 'Src types to compile args' dict
non_primary_build_args_per_src_lang = [(lang, build_args) for lang, build_args in captured_build_args.items() if lang != primary_src_lang] # Only need to individually populate intellisense fields for sources of non-primary types.
for src_lang, args_list in non_primary_build_args_per_src_lang:
if src_lang not in defs_paths_opts_per_lang_and_buildtype:
defs_paths_opts_per_lang_and_buildtype[src_lang] = {}
defs_paths_opts_per_lang_and_buildtype[src_lang][buildtype] = Vs2010Backend._extract_nmake_fields(args_list)
return defs_paths_opts_per_lang_and_buildtype
class Vs2010Backend(backends.Backend):
name = 'vs2010'
def __init__(self, build: T.Optional[build.Build], interpreter: T.Optional[Interpreter], gen_lite: bool = False):
super().__init__(build, interpreter)
self.project_file_version = '10.0.30319.1'
self.sln_file_version = '11.00'
self.sln_version_comment = '2010'
self.platform_toolset = None
self.vs_version = '2010'
self.windows_target_platform_version = None
self.subdirs = {}
self.handled_target_deps = {}
self.gen_lite = gen_lite # Synonymous with generating the simpler makefile-style multi-config projects that invoke 'meson compile' builds, avoiding native MSBuild complications
def get_target_private_dir(self, target):
return os.path.join(self.get_target_dir(target), target.get_id())
|