Merge pull request #5185 from mensinda/cmakeLLVM
CMake llvm dependency backend
This commit is contained in:
commit
6c61edeb30
|
@ -35,7 +35,7 @@ from .. import mesonlib
|
|||
from ..compilers import clib_langs
|
||||
from ..environment import BinaryTable, Environment, MachineInfo
|
||||
from ..mesonlib import MachineChoice, MesonException, OrderedSet, PerMachine
|
||||
from ..mesonlib import Popen_safe, version_compare_many, version_compare, listify
|
||||
from ..mesonlib import Popen_safe, version_compare_many, version_compare, listify, stringlistify, extract_as_list
|
||||
from ..mesonlib import Version, LibType
|
||||
|
||||
# These must be defined in this file to avoid cyclical references.
|
||||
|
@ -971,6 +971,24 @@ class CMakeDependency(ExternalDependency):
|
|||
def _gen_exception(self, msg):
|
||||
return DependencyException('Dependency {} not found: {}'.format(self.name, msg))
|
||||
|
||||
def _main_cmake_file(self) -> str:
|
||||
return 'CMakeLists.txt'
|
||||
|
||||
def _extra_cmake_opts(self) -> List[str]:
|
||||
return []
|
||||
|
||||
def _map_module_list(self, modules: List[Tuple[str, bool]]) -> List[Tuple[str, bool]]:
|
||||
# Map the input module list to something else
|
||||
# This function will only be executed AFTER the initial CMake
|
||||
# interpreter pass has completed. Thus variables defined in the
|
||||
# CMakeLists.txt can be accessed here.
|
||||
return modules
|
||||
|
||||
def _original_module_name(self, module: str) -> str:
|
||||
# Reverse the module mapping done by _map_module_list for
|
||||
# one module
|
||||
return module
|
||||
|
||||
def __init__(self, name: str, environment: Environment, kwargs, language=None):
|
||||
super().__init__('cmake', environment, language, kwargs)
|
||||
self.name = name
|
||||
|
@ -990,6 +1008,9 @@ class CMakeDependency(ExternalDependency):
|
|||
# Where all CMake "build dirs" are located
|
||||
self.cmake_root_dir = environment.scratch_dir
|
||||
|
||||
# List of successfully found modules
|
||||
self.found_modules = []
|
||||
|
||||
# When finding dependencies for cross-compiling, we don't care about
|
||||
# the 'native' CMake binary
|
||||
# TODO: Test if this works as expected
|
||||
|
@ -1061,15 +1082,10 @@ class CMakeDependency(ExternalDependency):
|
|||
if self.cmakeinfo is None:
|
||||
raise self._gen_exception('Unable to obtain CMake system information')
|
||||
|
||||
modules = kwargs.get('modules', [])
|
||||
cm_path = kwargs.get('cmake_module_path', [])
|
||||
cm_args = kwargs.get('cmake_args', [])
|
||||
if not isinstance(modules, list):
|
||||
modules = [modules]
|
||||
if not isinstance(cm_path, list):
|
||||
cm_path = [cm_path]
|
||||
if not isinstance(cm_args, list):
|
||||
cm_args = [cm_args]
|
||||
modules = [(x, True) for x in stringlistify(extract_as_list(kwargs, 'modules'))]
|
||||
modules += [(x, False) for x in stringlistify(extract_as_list(kwargs, 'optional_modules'))]
|
||||
cm_path = stringlistify(extract_as_list(kwargs, 'cmake_module_path'))
|
||||
cm_args = stringlistify(extract_as_list(kwargs, 'cmake_args'))
|
||||
cm_path = [x if os.path.isabs(x) else os.path.join(environment.get_source_dir(), x) for x in cm_path]
|
||||
if cm_path:
|
||||
cm_args += ['-DCMAKE_MODULE_PATH={}'.format(';'.join(cm_path))]
|
||||
|
@ -1242,7 +1258,7 @@ class CMakeDependency(ExternalDependency):
|
|||
|
||||
return False
|
||||
|
||||
def _detect_dep(self, name: str, modules: List[str], args: List[str]):
|
||||
def _detect_dep(self, name: str, modules: List[Tuple[str, bool]], args: List[str]):
|
||||
# Detect a dependency with CMake using the '--find-package' mode
|
||||
# and the trace output (stderr)
|
||||
#
|
||||
|
@ -1265,11 +1281,12 @@ class CMakeDependency(ExternalDependency):
|
|||
|
||||
# Prepare options
|
||||
cmake_opts = ['--trace-expand', '-DNAME={}'.format(name), '-DARCHS={}'.format(';'.join(self.cmakeinfo['archs']))] + args + ['.']
|
||||
cmake_opts += self._extra_cmake_opts()
|
||||
if len(i) > 0:
|
||||
cmake_opts = ['-G', i] + cmake_opts
|
||||
|
||||
# Run CMake
|
||||
ret1, out1, err1 = self._call_cmake(cmake_opts, 'CMakeLists.txt')
|
||||
ret1, out1, err1 = self._call_cmake(cmake_opts, self._main_cmake_file())
|
||||
|
||||
# Current generator was successful
|
||||
if ret1 == 0:
|
||||
|
@ -1327,6 +1344,11 @@ class CMakeDependency(ExternalDependency):
|
|||
self.version = vers_raw[0]
|
||||
self.version.strip('"\' ')
|
||||
|
||||
# Post-process module list. Used in derived classes to modify the
|
||||
# module list (append prepend a string, etc.).
|
||||
modules = self._map_module_list(modules)
|
||||
autodetected_module_list = False
|
||||
|
||||
# Try guessing a CMake target if none is provided
|
||||
if len(modules) == 0:
|
||||
for i in self.targets:
|
||||
|
@ -1334,17 +1356,19 @@ class CMakeDependency(ExternalDependency):
|
|||
lname = name.lower()
|
||||
if '{}::{}'.format(lname, lname) == tg or lname == tg.replace('::', ''):
|
||||
mlog.debug('Guessed CMake target \'{}\''.format(i))
|
||||
modules = [i]
|
||||
modules = [(i, True)]
|
||||
autodetected_module_list = True
|
||||
break
|
||||
|
||||
# Failed to guess a target --> try the old-style method
|
||||
if len(modules) == 0:
|
||||
incDirs = self.get_first_cmake_var_of(['PACKAGE_INCLUDE_DIRS'])
|
||||
defs = self.get_first_cmake_var_of(['PACKAGE_DEFINITIONS'])
|
||||
libs = self.get_first_cmake_var_of(['PACKAGE_LIBRARIES'])
|
||||
|
||||
# Try to use old style variables if no module is specified
|
||||
if len(libs) > 0:
|
||||
self.compile_args = list(map(lambda x: '-I{}'.format(x), incDirs))
|
||||
self.compile_args = list(map(lambda x: '-I{}'.format(x), incDirs)) + defs
|
||||
self.link_args = libs
|
||||
mlog.debug('using old-style CMake variables for dependency {}'.format(name))
|
||||
return
|
||||
|
@ -1361,13 +1385,19 @@ class CMakeDependency(ExternalDependency):
|
|||
compileDefinitions = []
|
||||
compileOptions = []
|
||||
libraries = []
|
||||
for i in modules:
|
||||
for i, required in modules:
|
||||
if i not in self.targets:
|
||||
raise self._gen_exception('CMake: invalid CMake target {} for {}.\n'
|
||||
if not required:
|
||||
mlog.warning('CMake: Optional module', mlog.bold(self._original_module_name(i)), 'for', mlog.bold(name), 'was not found')
|
||||
continue
|
||||
raise self._gen_exception('CMake: invalid module {} for {}.\n'
|
||||
'Try to explicitly specify one or more targets with the "modules" property.\n'
|
||||
'Valid targets are:\n{}'.format(i, name, list(self.targets.keys())))
|
||||
'Valid targets are:\n{}'.format(self._original_module_name(i), name, list(self.targets.keys())))
|
||||
|
||||
targets = [i]
|
||||
if not autodetected_module_list:
|
||||
self.found_modules += [i]
|
||||
|
||||
while len(targets) > 0:
|
||||
curr = targets.pop(0)
|
||||
|
||||
|
@ -1432,7 +1462,7 @@ class CMakeDependency(ExternalDependency):
|
|||
self.compile_args = compileOptions + compileDefinitions + list(map(lambda x: '-I{}'.format(x), incDirs))
|
||||
self.link_args = libraries
|
||||
|
||||
def get_first_cmake_var_of(self, var_list):
|
||||
def get_first_cmake_var_of(self, var_list: List[str]) -> List[str]:
|
||||
# Return the first found CMake variable in list var_list
|
||||
for i in var_list:
|
||||
if i in self.vars:
|
||||
|
@ -1440,7 +1470,7 @@ class CMakeDependency(ExternalDependency):
|
|||
|
||||
return []
|
||||
|
||||
def get_cmake_var(self, var):
|
||||
def get_cmake_var(self, var: str) -> List[str]:
|
||||
# Return the value of the CMake variable var or an empty list if var does not exist
|
||||
if var in self.vars:
|
||||
return self.vars[var]
|
||||
|
@ -1747,6 +1777,13 @@ set(CMAKE_SIZEOF_VOID_P "{}")
|
|||
def log_tried(self):
|
||||
return self.type_name
|
||||
|
||||
def log_details(self) -> str:
|
||||
modules = [self._original_module_name(x) for x in self.found_modules]
|
||||
modules = sorted(set(modules))
|
||||
if modules:
|
||||
return 'modules: ' + ', '.join(modules)
|
||||
return ''
|
||||
|
||||
class DubDependency(ExternalDependency):
|
||||
class_dubbin = None
|
||||
|
||||
|
|
|
@ -0,0 +1,95 @@
|
|||
cmake_minimum_required(VERSION ${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION}.${CMAKE_PATCH_VERSION} )
|
||||
|
||||
set(PACKAGE_FOUND FALSE)
|
||||
|
||||
while(TRUE)
|
||||
find_package(LLVM REQUIRED CONFIG QUIET)
|
||||
|
||||
# ARCHS has to be set via the CMD interface
|
||||
if(LLVM_FOUND OR "${ARCHS}" STREQUAL "")
|
||||
break()
|
||||
endif()
|
||||
|
||||
list(GET ARCHS 0 CMAKE_LIBRARY_ARCHITECTURE)
|
||||
list(REMOVE_AT ARCHS 0)
|
||||
endwhile()
|
||||
|
||||
if(LLVM_FOUND)
|
||||
set(PACKAGE_FOUND TRUE)
|
||||
|
||||
foreach(mod IN LISTS LLVM_MESON_MODULES)
|
||||
# Reset variables
|
||||
set(out_mods)
|
||||
set(real_mods)
|
||||
|
||||
# Generate a lower and upper case version
|
||||
string(TOLOWER "${mod}" mod_L)
|
||||
string(TOUPPER "${mod}" mod_U)
|
||||
|
||||
# Get the mapped components
|
||||
llvm_map_components_to_libnames(out_mods ${mod} ${mod_L} ${mod_U})
|
||||
list(SORT out_mods)
|
||||
list(REMOVE_DUPLICATES out_mods)
|
||||
|
||||
# Make sure that the modules exist
|
||||
foreach(i IN LISTS out_mods)
|
||||
if(TARGET ${i})
|
||||
list(APPEND real_mods ${i})
|
||||
endif()
|
||||
endforeach()
|
||||
|
||||
# Set the output variables
|
||||
set(MESON_LLVM_TARGETS_${mod} ${real_mods})
|
||||
foreach(i IN LISTS real_mods)
|
||||
set(MESON_TARGET_TO_LLVM_${i} ${mod})
|
||||
endforeach()
|
||||
endforeach()
|
||||
|
||||
# Check the following variables:
|
||||
# LLVM_PACKAGE_VERSION
|
||||
# LLVM_VERSION
|
||||
# LLVM_VERSION_STRING
|
||||
if(NOT DEFINED PACKAGE_VERSION)
|
||||
if(DEFINED LLVM_PACKAGE_VERSION)
|
||||
set(PACKAGE_VERSION "${LLVM_PACKAGE_VERSION}")
|
||||
elseif(DEFINED LLVM_VERSION)
|
||||
set(PACKAGE_VERSION "${LLVM_VERSION}")
|
||||
elseif(DEFINED LLVM_VERSION_STRING)
|
||||
set(PACKAGE_VERSION "${LLVM_VERSION_STRING}")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# Check the following variables:
|
||||
# LLVM_LIBRARIES
|
||||
# LLVM_LIBS
|
||||
set(libs)
|
||||
if(DEFINED LLVM_LIBRARIES)
|
||||
set(libs LLVM_LIBRARIES)
|
||||
elseif(DEFINED LLVM_LIBS)
|
||||
set(libs LLVM_LIBS)
|
||||
endif()
|
||||
|
||||
# Check the following variables:
|
||||
# LLVM_INCLUDE_DIRS
|
||||
# LLVM_INCLUDES
|
||||
# LLVM_INCLUDE_DIR
|
||||
set(includes)
|
||||
if(DEFINED LLVM_INCLUDE_DIRS)
|
||||
set(includes LLVM_INCLUDE_DIRS)
|
||||
elseif(DEFINED LLVM_INCLUDES)
|
||||
set(includes LLVM_INCLUDES)
|
||||
elseif(DEFINED LLVM_INCLUDE_DIR)
|
||||
set(includes LLVM_INCLUDE_DIR)
|
||||
endif()
|
||||
|
||||
# Check the following variables:
|
||||
# LLVM_DEFINITIONS
|
||||
set(definitions)
|
||||
if(DEFINED LLVM_DEFINITIONS)
|
||||
set(definitions LLVM_DEFINITIONS)
|
||||
endif()
|
||||
|
||||
set(PACKAGE_INCLUDE_DIRS "${${includes}}")
|
||||
set(PACKAGE_DEFINITIONS "${${definitions}}")
|
||||
set(PACKAGE_LIBRARIES "${${libs}}")
|
||||
endif()
|
|
@ -20,14 +20,16 @@ import glob
|
|||
import os
|
||||
import re
|
||||
|
||||
from .. import mesonlib
|
||||
from .. import mesonlib, mlog
|
||||
from ..mesonlib import version_compare, stringlistify, extract_as_list, MachineChoice
|
||||
from .base import (
|
||||
DependencyException, DependencyMethods, ExternalDependency, PkgConfigDependency,
|
||||
strip_system_libdirs, ConfigToolDependency,
|
||||
strip_system_libdirs, ConfigToolDependency, CMakeDependency
|
||||
)
|
||||
from .misc import ThreadDependency
|
||||
|
||||
from typing import List, Tuple
|
||||
|
||||
|
||||
def get_shared_library_suffix(environment, native):
|
||||
"""This is only gauranteed to work for languages that compile to machine
|
||||
|
@ -192,7 +194,7 @@ class GMockDependency(ExternalDependency):
|
|||
return [DependencyMethods.PKGCONFIG, DependencyMethods.SYSTEM]
|
||||
|
||||
|
||||
class LLVMDependency(ConfigToolDependency):
|
||||
class LLVMDependencyConfigTool(ConfigToolDependency):
|
||||
"""
|
||||
LLVM uses a special tool, llvm-config, which has arguments for getting
|
||||
c args, cxx args, and ldargs as well as version.
|
||||
|
@ -399,6 +401,66 @@ class LLVMDependency(ConfigToolDependency):
|
|||
return 'modules: ' + ', '.join(self.module_details)
|
||||
return ''
|
||||
|
||||
class LLVMDependencyCMake(CMakeDependency):
|
||||
def __init__(self, env, kwargs):
|
||||
self.llvm_modules = stringlistify(extract_as_list(kwargs, 'modules'))
|
||||
self.llvm_opt_modules = stringlistify(extract_as_list(kwargs, 'optional_modules'))
|
||||
super().__init__(name='LLVM', environment=env, language='cpp', kwargs=kwargs)
|
||||
|
||||
# Extract extra include directories and definitions
|
||||
inc_dirs = self.get_cmake_var('PACKAGE_INCLUDE_DIRS')
|
||||
defs = self.get_cmake_var('PACKAGE_DEFINITIONS')
|
||||
temp = ['-I' + x for x in inc_dirs] + defs
|
||||
self.compile_args += [x for x in temp if x not in self.compile_args]
|
||||
self._add_sub_dependency(ThreadDependency, env, kwargs)
|
||||
|
||||
def _main_cmake_file(self) -> str:
|
||||
# Use a custom CMakeLists.txt for LLVM
|
||||
return 'CMakeListsLLVM.txt'
|
||||
|
||||
def _extra_cmake_opts(self) -> List[str]:
|
||||
return ['-DLLVM_MESON_MODULES={}'.format(';'.join(self.llvm_modules + self.llvm_opt_modules))]
|
||||
|
||||
def _map_module_list(self, modules: List[Tuple[str, bool]]) -> List[Tuple[str, bool]]:
|
||||
res = []
|
||||
for mod, required in modules:
|
||||
cm_targets = self.get_cmake_var('MESON_LLVM_TARGETS_{}'.format(mod))
|
||||
if not cm_targets:
|
||||
if required:
|
||||
raise self._gen_exception('LLVM module {} was not found'.format(mod))
|
||||
else:
|
||||
mlog.warning('Optional LLVM module', mlog.bold(mod), 'was not found')
|
||||
continue
|
||||
for i in cm_targets:
|
||||
res += [(i, required)]
|
||||
return res
|
||||
|
||||
def _original_module_name(self, module: str) -> str:
|
||||
orig_name = self.get_cmake_var('MESON_TARGET_TO_LLVM_{}'.format(module))
|
||||
if orig_name:
|
||||
return orig_name[0]
|
||||
return module
|
||||
|
||||
class LLVMDependency(ExternalDependency):
|
||||
def __init__(self, env, kwargs):
|
||||
super().__init__('LLVM', env, 'cpp', kwargs)
|
||||
|
||||
@classmethod
|
||||
def _factory(cls, env, kwargs):
|
||||
methods = cls._process_method_kw(kwargs)
|
||||
candidates = []
|
||||
|
||||
if DependencyMethods.CMAKE in methods:
|
||||
candidates.append(functools.partial(LLVMDependencyCMake, env, kwargs))
|
||||
|
||||
if DependencyMethods.CONFIG_TOOL in methods:
|
||||
candidates.append(functools.partial(LLVMDependencyConfigTool, env, kwargs))
|
||||
|
||||
return candidates
|
||||
|
||||
@staticmethod
|
||||
def get_methods():
|
||||
return [DependencyMethods.CMAKE, DependencyMethods.CONFIG_TOOL]
|
||||
|
||||
class ValgrindDependency(PkgConfigDependency):
|
||||
'''
|
||||
|
|
2
setup.py
2
setup.py
|
@ -35,7 +35,7 @@ packages = ['mesonbuild',
|
|||
'mesonbuild.modules',
|
||||
'mesonbuild.scripts',
|
||||
'mesonbuild.wrap']
|
||||
package_data = {'mesonbuild.dependencies': ['data/CMakeLists.txt', 'data/CMakePathInfo.txt']}
|
||||
package_data = {'mesonbuild.dependencies': ['data/CMakeLists.txt', 'data/CMakeListsLLVM.txt', 'data/CMakePathInfo.txt']}
|
||||
data_files = []
|
||||
if sys.platform != 'win32':
|
||||
# Only useful on UNIX-like systems
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
project('config tool variable', 'cpp')
|
||||
|
||||
|
||||
dep_llvm = dependency('llvm', required : false)
|
||||
dep_llvm = dependency('llvm', method : 'config-tool', required : false)
|
||||
if not dep_llvm.found()
|
||||
error('MESON_SKIP_TEST LLVM not installed.')
|
||||
endif
|
||||
|
|
|
@ -35,7 +35,7 @@ foreach static : [true, false]
|
|||
llvm_dep = dependency(
|
||||
'llvm',
|
||||
modules : ['bitwriter', 'asmprinter', 'executionengine', 'target',
|
||||
'mcjit', 'nativecodegen'],
|
||||
'mcjit', 'nativecodegen', 'amdgpu'],
|
||||
required : false,
|
||||
static : static,
|
||||
)
|
||||
|
|
|
@ -8,7 +8,7 @@ if case == 'find_program'
|
|||
assert(result.stdout().strip().endswith('12345'), 'Didn\'t load bash from config file')
|
||||
elif case == 'config_dep'
|
||||
add_languages('cpp')
|
||||
dep = dependency('llvm')
|
||||
dep = dependency('llvm', method : 'config-tool')
|
||||
assert(dep.get_configtool_variable('version').endswith('12345'), 'Didn\'t load llvm from config file')
|
||||
elif case == 'python3'
|
||||
prog = import('python3').find_python()
|
||||
|
|
Loading…
Reference in New Issue