From 98fd4e5557cb21affd2e2c124a132953b471a748 Mon Sep 17 00:00:00 2001 From: "Michael Hirsch, Ph.D" Date: Wed, 27 Nov 2019 01:35:15 -0500 Subject: [PATCH] cmake: add project language to cmakelists.txt cmake: get language from Meson project if not specified as depedency(..., langugage: ...) deps: add threads method:cmake dependency('threads', method: 'cmake') is useful for cmake unit tests or those who just want to find threads using cmake. cmake: project(... Fortran) generally also requires C language --- mesonbuild/dependencies/base.py | 53 +++++++++++++++++-- mesonbuild/dependencies/data/CMakeLists.txt | 6 ++- mesonbuild/dependencies/misc.py | 38 +++++++++---- mesonbuild/mesonlib.py | 4 +- .../cmake/14 fortran threads/meson.build | 9 ++++ test cases/frameworks/1 boost/meson.build | 1 + test cases/frameworks/2 gtest/meson.build | 1 + .../linuxlike/13 cmake dependency/meson.build | 4 +- 8 files changed, 101 insertions(+), 15 deletions(-) create mode 100644 test cases/cmake/14 fortran threads/meson.build diff --git a/mesonbuild/dependencies/base.py b/mesonbuild/dependencies/base.py index 8e7179a68..cbc9f65d1 100644 --- a/mesonbuild/dependencies/base.py +++ b/mesonbuild/dependencies/base.py @@ -1037,7 +1037,23 @@ class CMakeDependency(ExternalDependency): # one module return module - def __init__(self, name: str, environment: Environment, kwargs, language=None): + def __init__(self, name: str, environment: Environment, kwargs, language: str = None): + if language is None: + if kwargs.get('native', False): + if 'c' in environment.coredata.compilers.build.keys(): + language = 'c' + elif 'cpp' in environment.coredata.compilers.build.keys(): + language = 'cpp' + elif 'fortran' in environment.coredata.compilers.build.keys(): + language = 'fortran' + else: + if 'c' in environment.coredata.compilers.host.keys(): + language = 'c' + elif 'cpp' in environment.coredata.compilers.host.keys(): + language = 'cpp' + elif 'fortran' in environment.coredata.compilers.host.keys(): + language = 'fortran' + super().__init__('cmake', environment, language, kwargs) self.name = name self.is_libtool = False @@ -1460,9 +1476,40 @@ class CMakeDependency(ExternalDependency): build_dir = Path(self.cmake_root_dir) / 'cmake_{}'.format(self.name) build_dir.mkdir(parents=True, exist_ok=True) - # Copy the CMakeLists.txt + # Insert language parameters into the CMakeLists.txt and write new CMakeLists.txt src_cmake = Path(__file__).parent / 'data' / cmake_file - shutil.copyfile(str(src_cmake), str(build_dir / 'CMakeLists.txt')) # str() is for Python 3.5 + cmake_txt = src_cmake.read_text() + + # In general, some Fortran CMake find_package() also require C language enabled, + # even if nothing from C is directly used. An easy Fortran example that fails + # without C language is + # find_package(Threads) + # To make this general to + # any other language that might need this, we use a list for all + # languages and expand in the cmake Project(... LANGUAGES ...) statement. + if self.language is None: + cmake_language = ['NONE'] + elif self.language == 'c': + cmake_language = ['C'] + elif self.language == 'cpp': + cmake_language = ['CXX'] + elif self.language == 'cs': + cmake_language = ['CSharp'] + elif self.language == 'cuda': + cmake_language = ['CUDA'] + elif self.language == 'fortran': + cmake_language = ['C', 'Fortran'] + elif self.language == 'objc': + cmake_language = ['OBJC'] + elif self.language == 'objcpp': + cmake_language = ['OBJCXX'] + + cmake_txt = """ +cmake_minimum_required(VERSION ${{CMAKE_VERSION}}) +project(MesonTemp LANGUAGES {}) +""".format(' '.join(cmake_language)) + cmake_txt + + (build_dir / 'CMakeLists.txt').write_text(cmake_txt) return str(build_dir) diff --git a/mesonbuild/dependencies/data/CMakeLists.txt b/mesonbuild/dependencies/data/CMakeLists.txt index 64f5b235c..b52a69a85 100644 --- a/mesonbuild/dependencies/data/CMakeLists.txt +++ b/mesonbuild/dependencies/data/CMakeLists.txt @@ -1,4 +1,8 @@ -cmake_minimum_required(VERSION ${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION}.${CMAKE_PATCH_VERSION} ) +# fail noisily if attempt to use this file without setting: +# cmake_minimum_required(VERSION ${CMAKE_VERSION}) +# project(... LANGUAGES ...) + +cmake_policy(SET CMP0000 NEW) set(PACKAGE_FOUND FALSE) set(_packageName "${NAME}") diff --git a/mesonbuild/dependencies/misc.py b/mesonbuild/dependencies/misc.py index bfd450c4f..d773eb7ba 100644 --- a/mesonbuild/dependencies/misc.py +++ b/mesonbuild/dependencies/misc.py @@ -22,6 +22,7 @@ import sysconfig from .. import mlog from .. import mesonlib from ..environment import detect_cpu_family +from ..mesonlib import listify from .base import ( DependencyException, DependencyMethods, ExternalDependency, @@ -109,15 +110,34 @@ class ThreadDependency(ExternalDependency): def __init__(self, environment, kwargs): super().__init__('threads', environment, None, kwargs) self.name = 'threads' - self.is_found = True - # Happens if you are using a language with threads - # concept without C, such as plain Cuda. - if self.clib_compiler is None: - self.compile_args = [] - self.link_args = [] - else: - self.compile_args = self.clib_compiler.thread_flags(environment) - self.link_args = self.clib_compiler.thread_link_flags(environment) + self.is_found = False + methods = listify(self.methods) + if DependencyMethods.AUTO in methods: + self.is_found = True + # Happens if you are using a language with threads + # concept without C, such as plain Cuda. + if self.clib_compiler is None: + self.compile_args = [] + self.link_args = [] + else: + self.compile_args = self.clib_compiler.thread_flags(environment) + self.link_args = self.clib_compiler.thread_link_flags(environment) + return + + if DependencyMethods.CMAKE in methods: + # for unit tests and for those who simply want + # dependency('threads', method: 'cmake') + cmakedep = CMakeDependency('Threads', environment, kwargs) + if cmakedep.found(): + self.compile_args = cmakedep.get_compile_args() + self.link_args = cmakedep.get_link_args() + self.version = cmakedep.get_version() + self.is_found = True + return + + @staticmethod + def get_methods(): + return [DependencyMethods.AUTO, DependencyMethods.CMAKE] class BlocksDependency(ExternalDependency): diff --git a/mesonbuild/mesonlib.py b/mesonbuild/mesonlib.py index 0d8ec3cff..5c3fc45cf 100644 --- a/mesonbuild/mesonlib.py +++ b/mesonbuild/mesonlib.py @@ -976,7 +976,9 @@ def replace_if_different(dst, dst_tmp): else: os.unlink(dst_tmp) -def listify(item, flatten=True, unholder=False): +def listify(item: typing.Any, + flatten: bool = True, + unholder: bool = False) -> typing.List[typing.Any]: ''' Returns a list with all args embedded in a list if they are not a list. This function preserves order. diff --git a/test cases/cmake/14 fortran threads/meson.build b/test cases/cmake/14 fortran threads/meson.build new file mode 100644 index 000000000..f160ca806 --- /dev/null +++ b/test cases/cmake/14 fortran threads/meson.build @@ -0,0 +1,9 @@ +project('FortranThreads') + +if not add_languages('fortran', required: false) + error('MESON_SKIP_TEST: Fortran language not available.') +endif + +# want to be sure that CMake can find dependencies where even if the +# project isn't C, the C language is required to find the library. +threads = dependency('threads', method: 'cmake', required: true) diff --git a/test cases/frameworks/1 boost/meson.build b/test cases/frameworks/1 boost/meson.build index 8f45dc7fe..4526c300c 100644 --- a/test cases/frameworks/1 boost/meson.build +++ b/test cases/frameworks/1 boost/meson.build @@ -1,3 +1,4 @@ +# this test requires the following on Ubuntu: libboost-{system,python,log,thread,test}-dev project('boosttest', 'cpp', default_options : ['cpp_std=c++11']) diff --git a/test cases/frameworks/2 gtest/meson.build b/test cases/frameworks/2 gtest/meson.build index 3f30215de..2d93b5294 100644 --- a/test cases/frameworks/2 gtest/meson.build +++ b/test cases/frameworks/2 gtest/meson.build @@ -1,3 +1,4 @@ +# on Ubuntu this test requires libgtest-dev project('gtest', 'cpp') gtest = dependency('gtest', main : true, required : false) diff --git a/test cases/linuxlike/13 cmake dependency/meson.build b/test cases/linuxlike/13 cmake dependency/meson.build index 682d6d84f..775355025 100644 --- a/test cases/linuxlike/13 cmake dependency/meson.build +++ b/test cases/linuxlike/13 cmake dependency/meson.build @@ -1,3 +1,5 @@ +# this test can ONLY be run successfully from run_project_test.py +# due to use of setup_env.json project('external CMake dependency', 'c') if not find_program('cmake', required: false).found() @@ -37,7 +39,7 @@ depf2 = dependency('ZLIB', required : false, method : 'cmake', modules : 'dfggh: assert(depf2.found() == false, 'Invalid CMake targets should fail') # Try to find cmMesonTestDep in a custom prefix - +# setup_env.json is used by run_project_tests.py:_run_test to point to ./cmake_pref_env/ depPrefEnv = dependency('cmMesonTestDep', required : true, method : 'cmake') # Try to find a dependency with a custom CMake module