Merge pull request #8812 from mensinda/cmakeToolchain
CMake toolchain file improvements (fixes #8293)
This commit is contained in:
commit
99452dde26
|
@ -30,11 +30,13 @@ __all__ = [
|
|||
'TargetOptions',
|
||||
'parse_generator_expressions',
|
||||
'language_map',
|
||||
'backend_generator_map',
|
||||
'cmake_get_generator_args',
|
||||
'cmake_defines_to_args',
|
||||
'check_cmake_args',
|
||||
]
|
||||
|
||||
from .common import CMakeException, SingleTargetOptions, TargetOptions, cmake_defines_to_args, language_map, check_cmake_args
|
||||
from .common import CMakeException, SingleTargetOptions, TargetOptions, cmake_defines_to_args, language_map, backend_generator_map, cmake_get_generator_args, check_cmake_args
|
||||
from .client import CMakeClient
|
||||
from .executor import CMakeExecutor
|
||||
from .fileapi import CMakeFileAPI
|
||||
|
|
|
@ -15,11 +15,14 @@
|
|||
# This class contains the basic functionality needed to run any interpreter
|
||||
# or an interpreter-based tool.
|
||||
|
||||
from ..mesonlib import MesonException
|
||||
from ..mesonlib import MesonException, OptionKey
|
||||
from .. import mlog
|
||||
from pathlib import Path
|
||||
import typing as T
|
||||
|
||||
if T.TYPE_CHECKING:
|
||||
from ..environment import Environment
|
||||
|
||||
language_map = {
|
||||
'c': 'C',
|
||||
'cpp': 'CXX',
|
||||
|
@ -32,6 +35,15 @@ language_map = {
|
|||
'swift': 'Swift',
|
||||
}
|
||||
|
||||
backend_generator_map = {
|
||||
'ninja': 'Ninja',
|
||||
'xcode': 'Xcode',
|
||||
'vs2010': 'Visual Studio 10 2010',
|
||||
'vs2015': 'Visual Studio 15 2017',
|
||||
'vs2017': 'Visual Studio 15 2017',
|
||||
'vs2019': 'Visual Studio 16 2019',
|
||||
}
|
||||
|
||||
blacklist_cmake_defs = [
|
||||
'CMAKE_TOOLCHAIN_FILE',
|
||||
'CMAKE_PROJECT_INCLUDE',
|
||||
|
@ -87,6 +99,12 @@ def _flags_to_list(raw: str) -> T.List[str]:
|
|||
res = list(filter(lambda x: len(x) > 0, res))
|
||||
return res
|
||||
|
||||
def cmake_get_generator_args(env: 'Environment') -> T.List[str]:
|
||||
backend_name = env.coredata.get_option(OptionKey('backend'))
|
||||
assert isinstance(backend_name, str)
|
||||
assert backend_name in backend_generator_map
|
||||
return ['-G', backend_generator_map[backend_name]]
|
||||
|
||||
def cmake_defines_to_args(raw: T.Any, permissive: bool = False) -> T.List[str]:
|
||||
res = [] # type: T.List[str]
|
||||
if not isinstance(raw, list):
|
||||
|
|
|
@ -23,6 +23,10 @@ def parse_generator_expressions(raw: str) -> str:
|
|||
use cases.
|
||||
'''
|
||||
|
||||
# Early abort if no generator expression present
|
||||
if '$<' not in raw:
|
||||
return raw
|
||||
|
||||
out = '' # type: str
|
||||
i = 0 # type: int
|
||||
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
# This class contains the basic functionality needed to run any interpreter
|
||||
# or an interpreter-based tool.
|
||||
|
||||
from .common import CMakeException, CMakeTarget, TargetOptions, CMakeConfiguration, language_map, check_cmake_args
|
||||
from .common import CMakeException, CMakeTarget, TargetOptions, CMakeConfiguration, language_map, backend_generator_map, cmake_get_generator_args, check_cmake_args
|
||||
from .client import CMakeClient, RequestCMakeInputs, RequestConfigure, RequestCompute, RequestCodeModel, ReplyCMakeInputs, ReplyCodeModel
|
||||
from .fileapi import CMakeFileAPI
|
||||
from .executor import CMakeExecutor
|
||||
|
@ -74,15 +74,6 @@ disable_policy_warnings = [
|
|||
'CMP0102',
|
||||
]
|
||||
|
||||
backend_generator_map = {
|
||||
'ninja': 'Ninja',
|
||||
'xcode': 'Xcode',
|
||||
'vs2010': 'Visual Studio 10 2010',
|
||||
'vs2015': 'Visual Studio 15 2017',
|
||||
'vs2017': 'Visual Studio 15 2017',
|
||||
'vs2019': 'Visual Studio 16 2019',
|
||||
}
|
||||
|
||||
target_type_map = {
|
||||
'STATIC_LIBRARY': 'static_library',
|
||||
'MODULE_LIBRARY': 'shared_module',
|
||||
|
@ -892,15 +883,14 @@ class CMakeInterpreter:
|
|||
self.trace = CMakeTraceParser(cmake_exe.version(), self.build_dir, permissive=True)
|
||||
|
||||
preload_file = mesondata['cmake/data/preload.cmake'].write_to_private(self.env)
|
||||
toolchain = CMakeToolchain(self.env, self.for_machine, CMakeExecScope.SUBPROJECT, self.build_dir.parent, preload_file)
|
||||
toolchain = CMakeToolchain(cmake_exe, self.env, self.for_machine, CMakeExecScope.SUBPROJECT, self.build_dir, preload_file)
|
||||
toolchain_file = toolchain.write()
|
||||
|
||||
# TODO: drop this check once the deprecated `cmake_args` kwarg is removed
|
||||
extra_cmake_options = check_cmake_args(extra_cmake_options)
|
||||
|
||||
generator = backend_generator_map[self.backend_name]
|
||||
cmake_args = []
|
||||
cmake_args += ['-G', generator]
|
||||
cmake_args += cmake_get_generator_args(self.env)
|
||||
cmake_args += [f'-DCMAKE_INSTALL_PREFIX={self.install_prefix}']
|
||||
cmake_args += extra_cmake_options
|
||||
trace_args = self.trace.trace_args()
|
||||
|
@ -953,6 +943,7 @@ class CMakeInterpreter:
|
|||
cmake_files = self.fileapi.get_cmake_sources()
|
||||
self.bs_files = [x.file for x in cmake_files if not x.is_cmake and not x.is_temp]
|
||||
self.bs_files = [relative_to_if_possible(x, Path(self.env.get_source_dir())) for x in self.bs_files]
|
||||
self.bs_files = [x for x in self.bs_files if not path_is_in_root(x, Path(self.env.get_build_dir()), resolve=True)]
|
||||
self.bs_files = list(OrderedSet(self.bs_files))
|
||||
|
||||
# Load the codemodel configurations
|
||||
|
@ -980,6 +971,7 @@ class CMakeInterpreter:
|
|||
src_dir = bs_reply.src_dir
|
||||
self.bs_files = [x.file for x in bs_reply.build_files if not x.is_cmake and not x.is_temp]
|
||||
self.bs_files = [relative_to_if_possible(src_dir / x, Path(self.env.get_source_dir()), resolve=True) for x in self.bs_files]
|
||||
self.bs_files = [x for x in self.bs_files if not path_is_in_root(x, Path(self.env.get_build_dir()), resolve=True)]
|
||||
self.bs_files = list(OrderedSet(self.bs_files))
|
||||
self.codemodel_configs = cm_reply.configs
|
||||
|
||||
|
|
|
@ -13,9 +13,10 @@
|
|||
# limitations under the License.
|
||||
|
||||
from pathlib import Path
|
||||
from .traceparser import CMakeTraceParser
|
||||
from ..envconfig import CMakeSkipCompilerTest
|
||||
from ..mesonlib import MachineChoice
|
||||
from .common import language_map
|
||||
from .common import language_map, cmake_get_generator_args
|
||||
from .. import mlog
|
||||
|
||||
import shutil
|
||||
|
@ -24,53 +25,49 @@ from enum import Enum
|
|||
from textwrap import dedent
|
||||
|
||||
if T.TYPE_CHECKING:
|
||||
from .executor import CMakeExecutor
|
||||
from ..envconfig import MachineInfo, Properties, CMakeVariables
|
||||
from ..environment import Environment
|
||||
from ..compilers import Compiler
|
||||
|
||||
|
||||
_MESON_TO_CMAKE_MAPPING = {
|
||||
'arm': 'ARMCC',
|
||||
'armclang': 'ARMClang',
|
||||
'clang': 'Clang',
|
||||
'clang-cl': 'MSVC',
|
||||
'flang': 'Flang',
|
||||
'g95': 'G95',
|
||||
'gcc': 'GNU',
|
||||
'intel': 'Intel',
|
||||
'intel-cl': 'MSVC',
|
||||
'msvc': 'MSVC',
|
||||
'pathscale': 'PathScale',
|
||||
'pgi': 'PGI',
|
||||
'sun': 'SunPro',
|
||||
}
|
||||
|
||||
class CMakeExecScope(Enum):
|
||||
SUBPROJECT = 'subproject'
|
||||
DEPENDENCY = 'dependency'
|
||||
|
||||
class CMakeToolchain:
|
||||
def __init__(self, env: 'Environment', for_machine: MachineChoice, exec_scope: CMakeExecScope, out_dir: Path, preload_file: T.Optional[Path] = None) -> None:
|
||||
def __init__(self, cmakebin: 'CMakeExecutor', env: 'Environment', for_machine: MachineChoice, exec_scope: CMakeExecScope, build_dir: Path, preload_file: T.Optional[Path] = None) -> None:
|
||||
self.env = env
|
||||
self.cmakebin = cmakebin
|
||||
self.for_machine = for_machine
|
||||
self.exec_scope = exec_scope
|
||||
self.preload_file = preload_file
|
||||
self.toolchain_file = out_dir / 'CMakeMesonToolchainFile.cmake'
|
||||
self.toolchain_file = self.toolchain_file.resolve()
|
||||
self.build_dir = build_dir
|
||||
self.build_dir = self.build_dir.resolve()
|
||||
self.toolchain_file = build_dir / 'CMakeMesonToolchainFile.cmake'
|
||||
self.cmcache_file = build_dir / 'CMakeCache.txt'
|
||||
self.minfo = self.env.machines[self.for_machine]
|
||||
self.properties = self.env.properties[self.for_machine]
|
||||
self.compilers = self.env.coredata.compilers[self.for_machine]
|
||||
self.cmakevars = self.env.cmakevars[self.for_machine]
|
||||
self.cmakestate = self.env.coredata.cmake_cache[self.for_machine]
|
||||
|
||||
self.variables = self.get_defaults()
|
||||
self.variables.update(self.cmakevars.get_variables())
|
||||
|
||||
# Determine whether CMake the compiler test should be skipped
|
||||
skip_status = self.properties.get_cmake_skip_compiler_test()
|
||||
self.skip_check = skip_status == CMakeSkipCompilerTest.ALWAYS
|
||||
if skip_status == CMakeSkipCompilerTest.DEP_ONLY and self.exec_scope == CMakeExecScope.DEPENDENCY:
|
||||
self.skip_check = True
|
||||
if not self.properties.get_cmake_defaults():
|
||||
self.skip_check = False
|
||||
|
||||
assert self.toolchain_file.is_absolute()
|
||||
|
||||
def write(self) -> Path:
|
||||
if not self.toolchain_file.parent.exists():
|
||||
self.toolchain_file.parent.mkdir(parents=True)
|
||||
self.toolchain_file.write_text(self.generate())
|
||||
self.cmcache_file.write_text(self.generate_cache())
|
||||
mlog.cmd_ci_include(self.toolchain_file.as_posix())
|
||||
return self.toolchain_file
|
||||
|
||||
|
@ -80,6 +77,16 @@ class CMakeToolchain:
|
|||
args += ['-DMESON_PRELOAD_FILE=' + self.preload_file.as_posix()]
|
||||
return args
|
||||
|
||||
@staticmethod
|
||||
def _print_vars(vars: T.Dict[str, T.List[str]]) -> str:
|
||||
res = ''
|
||||
for key, value in vars.items():
|
||||
res += 'set(' + key
|
||||
for i in value:
|
||||
res += f' "{i}"'
|
||||
res += ')\n'
|
||||
return res
|
||||
|
||||
def generate(self) -> str:
|
||||
res = dedent('''\
|
||||
######################################
|
||||
|
@ -100,14 +107,19 @@ class CMakeToolchain:
|
|||
for key, value in self.variables.items():
|
||||
self.variables[key] = [x.replace('\\', '/') for x in value]
|
||||
|
||||
# Set compiler
|
||||
if self.skip_check:
|
||||
self.update_cmake_compiler_state()
|
||||
res += '# CMake compiler state variables\n'
|
||||
for lang, vars in self.cmakestate:
|
||||
res += f'# -- Variables for language {lang}\n'
|
||||
res += self._print_vars(vars)
|
||||
res += '\n'
|
||||
res += '\n'
|
||||
|
||||
# Set variables from the current machine config
|
||||
res += '# Variables from meson\n'
|
||||
for key, value in self.variables.items():
|
||||
res += 'set(' + key
|
||||
for i in value:
|
||||
res += f' "{i}"'
|
||||
|
||||
res += ')\n'
|
||||
res += self._print_vars(self.variables)
|
||||
res += '\n'
|
||||
|
||||
# Add the user provided toolchain file
|
||||
|
@ -121,6 +133,15 @@ class CMakeToolchain:
|
|||
|
||||
return res
|
||||
|
||||
def generate_cache(self) -> str:
|
||||
if not self.skip_check:
|
||||
return ''
|
||||
|
||||
res = ''
|
||||
for name, v in self.cmakestate.cmake_cache.items():
|
||||
res += f'{name}:{v.type}={";".join(v.value)}\n'
|
||||
return res
|
||||
|
||||
def get_defaults(self) -> T.Dict[str, T.List[str]]:
|
||||
defaults = {} # type: T.Dict[str, T.List[str]]
|
||||
|
||||
|
@ -151,11 +172,6 @@ class CMakeToolchain:
|
|||
if sys_root:
|
||||
defaults['CMAKE_SYSROOT'] = [sys_root]
|
||||
|
||||
# Determine whether CMake the compiler test should be skipped
|
||||
skip_check = self.properties.get_cmake_skip_compiler_test() == CMakeSkipCompilerTest.ALWAYS
|
||||
if self.properties.get_cmake_skip_compiler_test() == CMakeSkipCompilerTest.DEP_ONLY and self.exec_scope == CMakeExecScope.DEPENDENCY:
|
||||
skip_check = True
|
||||
|
||||
def make_abs(exe: str) -> str:
|
||||
if Path(exe).is_absolute():
|
||||
return exe
|
||||
|
@ -168,9 +184,6 @@ class CMakeToolchain:
|
|||
# Set the compiler variables
|
||||
for lang, comp_obj in self.compilers.items():
|
||||
exe_list = [make_abs(x) for x in comp_obj.get_exelist()]
|
||||
comp_id = CMakeToolchain.meson_compiler_to_cmake_id(comp_obj)
|
||||
comp_version = comp_obj.version.upper()
|
||||
|
||||
prefix = 'CMAKE_{}_'.format(language_map.get(lang, lang.upper()))
|
||||
|
||||
if not exe_list:
|
||||
|
@ -183,35 +196,53 @@ class CMakeToolchain:
|
|||
if comp_obj.get_id() == 'clang-cl':
|
||||
defaults['CMAKE_LINKER'] = comp_obj.get_linker_exelist()
|
||||
|
||||
# Setting the variables after this check cause CMake to skip
|
||||
# validating the compiler
|
||||
if not skip_check:
|
||||
continue
|
||||
|
||||
defaults[prefix + 'COMPILER_ID'] = [comp_id]
|
||||
defaults[prefix + 'COMPILER_VERSION'] = [comp_version]
|
||||
#defaults[prefix + 'COMPILER_LOADED'] = ['1']
|
||||
defaults[prefix + 'COMPILER_FORCED'] = ['1']
|
||||
defaults[prefix + 'COMPILER_WORKS'] = ['TRUE']
|
||||
#defaults[prefix + 'ABI_COMPILED'] = ['TRUE']
|
||||
|
||||
return defaults
|
||||
|
||||
@staticmethod
|
||||
def meson_compiler_to_cmake_id(cobj: 'Compiler') -> str:
|
||||
"""Translate meson compiler's into CMAKE compiler ID's.
|
||||
def update_cmake_compiler_state(self) -> None:
|
||||
# Check if all variables are already cached
|
||||
if self.cmakestate.languages.issuperset(self.compilers.keys()):
|
||||
return
|
||||
|
||||
Most of these can be handled by a simple table lookup, with a few
|
||||
exceptions.
|
||||
# Generate the CMakeLists.txt
|
||||
mlog.debug('CMake Toolchain: Calling CMake once to generate the compiler state')
|
||||
languages = list(self.compilers.keys())
|
||||
lang_ids = [language_map.get(x, x.upper()) for x in languages]
|
||||
cmake_content = dedent(f'''
|
||||
cmake_minimum_required(VERSION 3.7)
|
||||
project(CompInfo {' '.join(lang_ids)})
|
||||
''')
|
||||
|
||||
Clang and Apple's Clang are both identified as "clang" by meson. To make
|
||||
things more complicated gcc and vanilla clang both use Apple's ld64 on
|
||||
macOS. The only way to know for sure is to do an isinstance() check.
|
||||
"""
|
||||
from ..compilers import (AppleClangCCompiler, AppleClangCPPCompiler,
|
||||
AppleClangObjCCompiler, AppleClangObjCPPCompiler)
|
||||
if isinstance(cobj, (AppleClangCCompiler, AppleClangCPPCompiler,
|
||||
AppleClangObjCCompiler, AppleClangObjCPPCompiler)):
|
||||
return 'AppleClang'
|
||||
# If no mapping, try GNU and hope that the build files don't care
|
||||
return _MESON_TO_CMAKE_MAPPING.get(cobj.get_id(), 'GNU')
|
||||
build_dir = Path(self.env.scratch_dir) / '__CMake_compiler_info__'
|
||||
build_dir.mkdir(parents=True, exist_ok=True)
|
||||
cmake_file = build_dir / 'CMakeLists.txt'
|
||||
cmake_file.write_text(cmake_content)
|
||||
|
||||
# Generate the temporary toolchain file
|
||||
temp_toolchain_file = build_dir / 'CMakeMesonTempToolchainFile.cmake'
|
||||
temp_toolchain_file.write_text(CMakeToolchain._print_vars(self.variables))
|
||||
|
||||
# Configure
|
||||
trace = CMakeTraceParser(self.cmakebin.version(), build_dir)
|
||||
self.cmakebin.set_exec_mode(print_cmout=False, always_capture_stderr=trace.requires_stderr())
|
||||
cmake_args = []
|
||||
cmake_args += trace.trace_args()
|
||||
cmake_args += cmake_get_generator_args(self.env)
|
||||
cmake_args += [f'-DCMAKE_TOOLCHAIN_FILE={temp_toolchain_file.as_posix()}', '.']
|
||||
rc, _, raw_trace = self.cmakebin.call(cmake_args, build_dir=build_dir, disable_cache=True)
|
||||
|
||||
if rc != 0:
|
||||
mlog.warning('CMake Toolchain: Failed to determine CMake compilers state')
|
||||
return
|
||||
|
||||
# Parse output
|
||||
trace.parse(raw_trace)
|
||||
self.cmakestate.cmake_cache = {**trace.cache}
|
||||
|
||||
vars_by_file = {k.name: v for (k, v) in trace.vars_by_file.items()}
|
||||
|
||||
for lang in languages:
|
||||
lang_cmake = language_map.get(lang, lang.upper())
|
||||
file_name = f'CMake{lang_cmake}Compiler.cmake'
|
||||
vars = vars_by_file.setdefault(file_name, {})
|
||||
vars[f'CMAKE_{lang_cmake}_COMPILER_FORCED'] = ['1']
|
||||
self.cmakestate.update(lang, vars)
|
||||
|
|
|
@ -22,21 +22,31 @@ from ..mesonlib import version_compare
|
|||
|
||||
import typing as T
|
||||
from pathlib import Path
|
||||
from functools import lru_cache
|
||||
import re
|
||||
import json
|
||||
import textwrap
|
||||
|
||||
class CMakeTraceLine:
|
||||
def __init__(self, file: Path, line: int, func: str, args: T.List[str]) -> None:
|
||||
self.file = file
|
||||
def __init__(self, file_str: str, line: int, func: str, args: T.List[str]) -> None:
|
||||
self.file = CMakeTraceLine._to_path(file_str)
|
||||
self.line = line
|
||||
self.func = func.lower()
|
||||
self.args = args
|
||||
|
||||
@staticmethod
|
||||
@lru_cache(maxsize=None)
|
||||
def _to_path(file_str: str) -> Path:
|
||||
return Path(file_str)
|
||||
|
||||
def __repr__(self) -> str:
|
||||
s = 'CMake TRACE: {0}:{1} {2}({3})'
|
||||
return s.format(self.file, self.line, self.func, self.args)
|
||||
|
||||
class CMakeCacheEntry(T.NamedTuple):
|
||||
value: T.List[str]
|
||||
type: str
|
||||
|
||||
class CMakeTarget:
|
||||
def __init__(
|
||||
self,
|
||||
|
@ -81,8 +91,10 @@ class CMakeGeneratorTarget(CMakeTarget):
|
|||
|
||||
class CMakeTraceParser:
|
||||
def __init__(self, cmake_version: str, build_dir: Path, permissive: bool = True) -> None:
|
||||
self.vars = {} # type: T.Dict[str, T.List[str]]
|
||||
self.targets = {} # type: T.Dict[str, CMakeTarget]
|
||||
self.vars: T.Dict[str, T.List[str]] = {}
|
||||
self.vars_by_file: T.Dict[Path, T.Dict[str, T.List[str]]] = {}
|
||||
self.targets: T.Dict[str, CMakeTarget] = {}
|
||||
self.cache: T.Dict[str, CMakeCacheEntry] = {}
|
||||
|
||||
self.explicit_headers = set() # type: T.Set[Path]
|
||||
|
||||
|
@ -234,6 +246,14 @@ class CMakeTraceParser:
|
|||
"""
|
||||
# DOC: https://cmake.org/cmake/help/latest/command/set.html
|
||||
|
||||
cache_type = None
|
||||
cache_force = 'FORCE' in tline.args
|
||||
try:
|
||||
cache_idx = tline.args.index('CACHE')
|
||||
cache_type = tline.args[cache_idx + 1]
|
||||
except (ValueError, IndexError):
|
||||
pass
|
||||
|
||||
# 1st remove PARENT_SCOPE and CACHE from args
|
||||
args = []
|
||||
for i in tline.args:
|
||||
|
@ -256,12 +276,19 @@ class CMakeTraceParser:
|
|||
identifier = args.pop(0)
|
||||
value = ' '.join(args)
|
||||
|
||||
# Write to the CMake cache instead
|
||||
if cache_type:
|
||||
# Honor how the CMake FORCE parameter works
|
||||
if identifier not in self.cache or cache_force:
|
||||
self.cache[identifier] = CMakeCacheEntry(value.split(';'), cache_type)
|
||||
|
||||
if not value:
|
||||
# Same as unset
|
||||
if identifier in self.vars:
|
||||
del self.vars[identifier]
|
||||
else:
|
||||
self.vars[identifier] = value.split(';')
|
||||
self.vars_by_file.setdefault(tline.file, {})[identifier] = value.split(';')
|
||||
|
||||
def _cmake_unset(self, tline: CMakeTraceLine) -> None:
|
||||
# DOC: https://cmake.org/cmake/help/latest/command/unset.html
|
||||
|
@ -437,17 +464,18 @@ class CMakeTraceParser:
|
|||
if not value:
|
||||
return
|
||||
|
||||
def do_target(tgt: str) -> None:
|
||||
if i not in self.targets:
|
||||
return self._gen_exception('set_property', f'TARGET {i} not found', tline)
|
||||
def do_target(t: str) -> None:
|
||||
if t not in self.targets:
|
||||
return self._gen_exception('set_property', f'TARGET {t} not found', tline)
|
||||
|
||||
if identifier not in self.targets[i].properties:
|
||||
self.targets[i].properties[identifier] = []
|
||||
tgt = self.targets[t]
|
||||
if identifier not in tgt.properties:
|
||||
tgt.properties[identifier] = []
|
||||
|
||||
if append:
|
||||
self.targets[i].properties[identifier] += value
|
||||
tgt.properties[identifier] += value
|
||||
else:
|
||||
self.targets[i].properties[identifier] = value
|
||||
tgt.properties[identifier] = value
|
||||
|
||||
def do_source(src: str) -> None:
|
||||
if identifier != 'HEADER_FILE_ONLY' or not self._str_to_bool(value):
|
||||
|
@ -652,7 +680,7 @@ class CMakeTraceParser:
|
|||
argl = args.split(' ')
|
||||
argl = list(map(lambda x: x.strip(), argl))
|
||||
|
||||
yield CMakeTraceLine(Path(file), int(line), func, argl)
|
||||
yield CMakeTraceLine(file, int(line), func, argl)
|
||||
|
||||
def _lex_trace_json(self, trace: str) -> T.Generator[CMakeTraceLine, None, None]:
|
||||
lines = trace.splitlines(keepends=False)
|
||||
|
@ -667,7 +695,7 @@ class CMakeTraceParser:
|
|||
for j in args:
|
||||
assert isinstance(j, str)
|
||||
args = [parse_generator_expressions(x) for x in args]
|
||||
yield CMakeTraceLine(Path(data['file']), data['line'], data['cmd'], args)
|
||||
yield CMakeTraceLine(data['file'], data['line'], data['cmd'], args)
|
||||
|
||||
def _flatten_args(self, args: T.List[str]) -> T.List[str]:
|
||||
# Split lists in arguments
|
||||
|
|
|
@ -36,6 +36,7 @@ if T.TYPE_CHECKING:
|
|||
from .compilers.compilers import Compiler, CompileResult # noqa: F401
|
||||
from .environment import Environment
|
||||
from .mesonlib import OptionOverrideProxy
|
||||
from .cmake.traceparser import CMakeCacheEntry
|
||||
|
||||
OptionDictType = T.Union[T.Dict[str, 'UserOption[T.Any]'], OptionOverrideProxy]
|
||||
KeyedOptionDictType = T.Union[T.Dict['OptionKey', 'UserOption[T.Any]'], OptionOverrideProxy]
|
||||
|
@ -374,6 +375,34 @@ class DependencyCache:
|
|||
def clear(self) -> None:
|
||||
self.__cache.clear()
|
||||
|
||||
|
||||
class CMakeStateCache:
|
||||
"""Class that stores internal CMake compiler states.
|
||||
|
||||
This cache is used to reduce the startup overhead of CMake by caching
|
||||
all internal CMake compiler variables.
|
||||
"""
|
||||
|
||||
def __init__(self) -> None:
|
||||
self.__cache: T.Dict[str, T.Dict[str, T.List[str]]] = {}
|
||||
self.cmake_cache: T.Dict[str, 'CMakeCacheEntry'] = {}
|
||||
|
||||
def __iter__(self) -> T.Iterator[T.Tuple[str, T.Dict[str, T.List[str]]]]:
|
||||
return iter(self.__cache.items())
|
||||
|
||||
def items(self) -> T.Iterator[T.Tuple[str, T.Dict[str, T.List[str]]]]:
|
||||
return iter(self.__cache.items())
|
||||
|
||||
def update(self, language: str, variables: T.Dict[str, T.List[str]]):
|
||||
if language not in self.__cache:
|
||||
self.__cache[language] = {}
|
||||
self.__cache[language].update(variables)
|
||||
|
||||
@property
|
||||
def languages(self) -> T.Set[str]:
|
||||
return set(self.__cache.keys())
|
||||
|
||||
|
||||
# Can't bind this near the class method it seems, sadly.
|
||||
_V = T.TypeVar('_V')
|
||||
|
||||
|
@ -414,6 +443,9 @@ class CoreData:
|
|||
|
||||
self.compiler_check_cache = OrderedDict() # type: T.Dict[CompilerCheckCacheKey, compiler.CompileResult]
|
||||
|
||||
# CMake cache
|
||||
self.cmake_cache: PerMachine[CMakeStateCache] = PerMachine(CMakeStateCache(), CMakeStateCache())
|
||||
|
||||
# Only to print a warning if it changes between Meson invocations.
|
||||
self.config_files = self.__load_config_files(options, scratch_dir, 'native')
|
||||
self.builtin_options_libdir_cross_fixup()
|
||||
|
|
|
@ -1146,7 +1146,7 @@ class CMakeDependency(ExternalDependency):
|
|||
gen_list += CMakeDependency.class_cmake_generators
|
||||
|
||||
temp_parser = CMakeTraceParser(self.cmakebin.version(), self._get_build_dir())
|
||||
toolchain = CMakeToolchain(self.env, self.for_machine, CMakeExecScope.DEPENDENCY, self._get_build_dir())
|
||||
toolchain = CMakeToolchain(self.cmakebin, self.env, self.for_machine, CMakeExecScope.DEPENDENCY, self._get_build_dir())
|
||||
toolchain.write()
|
||||
|
||||
for i in gen_list:
|
||||
|
@ -1346,7 +1346,7 @@ class CMakeDependency(ExternalDependency):
|
|||
|
||||
# Map the components
|
||||
comp_mapped = self._map_component_list(modules, components)
|
||||
toolchain = CMakeToolchain(self.env, self.for_machine, CMakeExecScope.DEPENDENCY, self._get_build_dir())
|
||||
toolchain = CMakeToolchain(self.cmakebin, self.env, self.for_machine, CMakeExecScope.DEPENDENCY, self._get_build_dir())
|
||||
toolchain.write()
|
||||
|
||||
for i in gen_list:
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
cmake_minimum_required(VERSION 3.5)
|
||||
|
||||
project(cmMod CXX)
|
||||
project(cmMod C CXX)
|
||||
set (CMAKE_CXX_STANDARD 14)
|
||||
|
||||
if(NOT USE_PTHREAD STREQUAL NOT_SET)
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
project('cmake toolchain test', ['c', 'cpp'])
|
||||
project('cmake toolchain test', ['c'])
|
||||
|
||||
if meson.is_cross_build()
|
||||
error('MESON_SKIP_TEST: skip this on cross builds')
|
||||
|
@ -7,3 +7,7 @@ endif
|
|||
cm = import('cmake')
|
||||
|
||||
sub_pro = cm.subproject('cmMod')
|
||||
|
||||
add_languages('cpp')
|
||||
|
||||
sub_pro = cm.subproject('cmModFortran')
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
[properties]
|
||||
|
||||
cmake_toolchain_file = '@MESON_TEST_ROOT@/CMakeToolchain.cmake'
|
||||
cmake_skip_compiler_test = 'always'
|
||||
|
||||
[cmake]
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
cmake_minimum_required(VERSION 3.5)
|
||||
|
||||
project(cmMod)
|
||||
project(cmMod NONE)
|
||||
|
||||
if(NOT "${MESON_TEST_VAR1}" STREQUAL "VAR1 space")
|
||||
message(FATAL_ERROR "MESON_TEST_VAR1 -- '${MESON_TEST_VAR1}' != 'VAR1 space'")
|
||||
|
@ -9,3 +9,7 @@ endif()
|
|||
if(NOT "${MESON_TEST_VAR2}" STREQUAL "VAR2")
|
||||
message(FATAL_ERROR "MESON_TEST_VAR2 -- '${MESON_TEST_VAR2}' != 'VAR2'")
|
||||
endif()
|
||||
|
||||
if(NOT DEFINED CMAKE_C_COMPILER_VERSION)
|
||||
message(FATAL_ERROR "CMAKE_C_COMPILER_VERSION was not defined")
|
||||
endif()
|
||||
|
|
|
@ -0,0 +1,19 @@
|
|||
cmake_minimum_required(VERSION 3.5)
|
||||
|
||||
project(cmMod NONE)
|
||||
|
||||
if(NOT "${MESON_TEST_VAR1}" STREQUAL "VAR1 space")
|
||||
message(FATAL_ERROR "MESON_TEST_VAR1 -- '${MESON_TEST_VAR1}' != 'VAR1 space'")
|
||||
endif()
|
||||
|
||||
if(NOT "${MESON_TEST_VAR2}" STREQUAL "VAR2")
|
||||
message(FATAL_ERROR "MESON_TEST_VAR2 -- '${MESON_TEST_VAR2}' != 'VAR2'")
|
||||
endif()
|
||||
|
||||
if(NOT DEFINED CMAKE_C_COMPILER_VERSION)
|
||||
message(FATAL_ERROR "CMAKE_C_COMPILER_VERSION was not defined")
|
||||
endif()
|
||||
|
||||
if(NOT DEFINED CMAKE_CXX_COMPILER_VERSION)
|
||||
message(FATAL_ERROR "CMAKE_CXX_COMPILER_VERSION was not defined")
|
||||
endif()
|
Loading…
Reference in New Issue