aboutsummaryrefslogtreecommitdiff
path: root/mesonbuild/cmake/toolchain.py
diff options
context:
space:
mode:
Diffstat (limited to 'mesonbuild/cmake/toolchain.py')
-rw-r--r--mesonbuild/cmake/toolchain.py163
1 files changed, 97 insertions, 66 deletions
diff --git a/mesonbuild/cmake/toolchain.py b/mesonbuild/cmake/toolchain.py
index c9f821a..f3e487d 100644
--- a/mesonbuild/cmake/toolchain.py
+++ b/mesonbuild/cmake/toolchain.py
@@ -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.
-
- Most of these can be handled by a simple table lookup, with a few
- exceptions.
-
- 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')
+ def update_cmake_compiler_state(self) -> None:
+ # Check if all variables are already cached
+ if self.cmakestate.languages.issuperset(self.compilers.keys()):
+ return
+
+ # 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)})
+ ''')
+
+ 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)