aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--docs/markdown/Wrap-dependency-system-manual.md24
-rw-r--r--docs/markdown/snippets/wrap.md12
-rw-r--r--mesonbuild/interpreter/dependencyfallbacks.py2
-rw-r--r--mesonbuild/interpreter/interpreter.py14
-rw-r--r--mesonbuild/modules/cmake.py2
-rwxr-xr-xmesonbuild/msubprojects.py6
-rw-r--r--mesonbuild/wrap/wrap.py40
-rw-r--r--test cases/cmake/26 dependency fallback/main.cpp10
-rw-r--r--test cases/cmake/26 dependency fallback/meson.build30
-rw-r--r--test cases/cmake/26 dependency fallback/subprojects/broken_method.wrap2
-rw-r--r--test cases/cmake/26 dependency fallback/subprojects/cmMod.wrap5
-rw-r--r--test cases/cmake/26 dependency fallback/subprojects/cmMod/CMakeLists.txt20
-rw-r--r--test cases/cmake/26 dependency fallback/subprojects/cmMod/cmMod.cpp15
-rw-r--r--test cases/cmake/26 dependency fallback/subprojects/cmMod/cmMod.hpp18
-rw-r--r--test cases/cmake/26 dependency fallback/subprojects/cmMod/cpp_pch.hpp2
-rw-r--r--test cases/cmake/26 dependency fallback/subprojects/cmake_subp/CMakeLists.txt2
-rw-r--r--test cases/cmake/26 dependency fallback/subprojects/force_cmake.wrap2
-rw-r--r--test cases/cmake/26 dependency fallback/subprojects/force_cmake/CMakeLists.txt2
-rw-r--r--test cases/cmake/26 dependency fallback/subprojects/force_cmake/meson.build4
-rw-r--r--test cases/cmake/26 dependency fallback/subprojects/meson_method.wrap2
-rw-r--r--test cases/cmake/26 dependency fallback/subprojects/meson_subp/meson.build1
21 files changed, 188 insertions, 27 deletions
diff --git a/docs/markdown/Wrap-dependency-system-manual.md b/docs/markdown/Wrap-dependency-system-manual.md
index 3aeea14..9000c40 100644
--- a/docs/markdown/Wrap-dependency-system-manual.md
+++ b/docs/markdown/Wrap-dependency-system-manual.md
@@ -87,6 +87,10 @@ previously reserved to `wrap-file`:
`subprojects/packagefiles`.
- `diff_files` - *Since 0.63.0* Comma-separated list of local diff files (see
[Diff files](#diff-files) below).
+- `method` - *Since 1.3.0* The build system used by this subproject. Defaults to `meson`.
+ Supported methods:
+ - `meson` requires `meson.build` file.
+ - `cmake` requires `CMakeLists.txt` file. [See details](#cmake-wraps).
### Specific to wrap-file
- `source_url` - download url to retrieve the wrap-file source archive
@@ -290,6 +294,26 @@ With such wrap file, `find_program('myprog')` will automatically
fallback to use the subproject, assuming it uses
`meson.override_find_program('myprog')`.
+### CMake wraps
+
+Since the CMake module does not know the public name of the provided
+dependencies, a CMake `.wrap` file cannot use the `dependency_names = foo`
+syntax. Instead, the `dep_name = <target_name>_dep` syntax should be used, where
+`<target_name>` is the name of a CMake library with all non alphanumeric
+characters replaced by underscores `_`.
+
+For example, a CMake project that contains `add_library(foo-bar ...)` in its
+`CMakeList.txt` and that applications would usually find using the dependency
+name `foo-bar-1.0` (e.g. via pkg-config) would have a wrap file like this:
+
+```ini
+[wrap-file]
+...
+method = cmake
+[provide]
+foo-bar-1.0 = foo_bar_dep
+```
+
## Using wrapped projects
Wraps provide a convenient way of obtaining a project into your
diff --git a/docs/markdown/snippets/wrap.md b/docs/markdown/snippets/wrap.md
new file mode 100644
index 0000000..6e03c2e
--- /dev/null
+++ b/docs/markdown/snippets/wrap.md
@@ -0,0 +1,12 @@
+## Automatic fallback to `cmake` subproject
+
+CMake subprojects have been supported for a while using the `cmake.subproject()`
+module method. However until now it was not possible to use a CMake subproject
+as fallback in a `dependency()` call.
+
+A wrap file can now specify the method used to build it by setting the `method`
+key in the wrap file's first section. The method defaults to `meson`.
+
+Supported methods:
+- `meson` requires `meson.build` file.
+- `cmake` requires `CMakeLists.txt` file. [See details](Wrap-dependency-system-manual.md#cmake-wraps).
diff --git a/mesonbuild/interpreter/dependencyfallbacks.py b/mesonbuild/interpreter/dependencyfallbacks.py
index 7ef1527..eca6a2c 100644
--- a/mesonbuild/interpreter/dependencyfallbacks.py
+++ b/mesonbuild/interpreter/dependencyfallbacks.py
@@ -127,7 +127,7 @@ class DependencyFallbacksHolder(MesonInterpreterObject):
func_kwargs.setdefault('version', [])
if 'default_options' in kwargs and isinstance(kwargs['default_options'], str):
func_kwargs['default_options'] = listify(kwargs['default_options'])
- self.interpreter.do_subproject(subp_name, 'meson', func_kwargs)
+ self.interpreter.do_subproject(subp_name, func_kwargs)
return self._get_subproject_dep(subp_name, varname, kwargs)
def _get_subproject(self, subp_name: str) -> T.Optional[SubprojectHolder]:
diff --git a/mesonbuild/interpreter/interpreter.py b/mesonbuild/interpreter/interpreter.py
index a5c8a5a..e0c17e6 100644
--- a/mesonbuild/interpreter/interpreter.py
+++ b/mesonbuild/interpreter/interpreter.py
@@ -116,8 +116,6 @@ import copy
if T.TYPE_CHECKING:
import argparse
- from typing_extensions import Literal
-
from . import kwargs as kwtypes
from ..backend.backends import Backend
from ..interpreterbase.baseobjects import InterpreterObject, TYPE_var, TYPE_kwargs
@@ -868,7 +866,7 @@ class Interpreter(InterpreterBase, HoldableObject):
'options': None,
'cmake_options': [],
}
- return self.do_subproject(args[0], 'meson', kw)
+ return self.do_subproject(args[0], kw)
def disabled_subproject(self, subp_name: str, disabled_feature: T.Optional[str] = None,
exception: T.Optional[Exception] = None) -> SubprojectHolder:
@@ -877,7 +875,7 @@ class Interpreter(InterpreterBase, HoldableObject):
self.subprojects[subp_name] = sub
return sub
- def do_subproject(self, subp_name: str, method: Literal['meson', 'cmake'], kwargs: kwtypes.DoSubproject) -> SubprojectHolder:
+ def do_subproject(self, subp_name: str, kwargs: kwtypes.DoSubproject, force_method: T.Optional[wrap.Method] = None) -> SubprojectHolder:
disabled, required, feature = extract_required_kwarg(kwargs, self.subproject)
if disabled:
mlog.log('Subproject', mlog.bold(subp_name), ':', 'skipped: feature', mlog.bold(feature), 'disabled')
@@ -913,7 +911,7 @@ class Interpreter(InterpreterBase, HoldableObject):
r = self.environment.wrap_resolver
try:
- subdir = r.resolve(subp_name, method)
+ subdir, method = r.resolve(subp_name, force_method)
except wrap.WrapException as e:
if not required:
mlog.log(e)
@@ -1009,8 +1007,8 @@ class Interpreter(InterpreterBase, HoldableObject):
prefix = self.coredata.options[OptionKey('prefix')].value
from ..modules.cmake import CMakeSubprojectOptions
- options = kwargs['options'] or CMakeSubprojectOptions()
- cmake_options = kwargs['cmake_options'] + options.cmake_options
+ options = kwargs.get('options') or CMakeSubprojectOptions()
+ cmake_options = kwargs.get('cmake_options', []) + options.cmake_options
cm_int = CMakeInterpreter(new_build, Path(subdir), Path(subdir_abs), Path(prefix), new_build.environment, self.backend)
cm_int.initialise(cmake_options)
cm_int.analyse()
@@ -1734,7 +1732,7 @@ class Interpreter(InterpreterBase, HoldableObject):
'cmake_options': [],
'options': None,
}
- self.do_subproject(fallback, 'meson', sp_kwargs)
+ self.do_subproject(fallback, sp_kwargs)
return self.program_from_overrides(args, extra_info)
@typed_pos_args('find_program', varargs=(str, mesonlib.File), min_varargs=1)
diff --git a/mesonbuild/modules/cmake.py b/mesonbuild/modules/cmake.py
index bec1b2a..ee4e844 100644
--- a/mesonbuild/modules/cmake.py
+++ b/mesonbuild/modules/cmake.py
@@ -435,7 +435,7 @@ class CmakeModule(ExtensionModule):
'default_options': {},
'version': [],
}
- subp = self.interpreter.do_subproject(dirname, 'cmake', kw)
+ subp = self.interpreter.do_subproject(dirname, kw, force_method='cmake')
if not subp.found():
return subp
return CMakeSubproject(subp)
diff --git a/mesonbuild/msubprojects.py b/mesonbuild/msubprojects.py
index 64a09b0..45b711d 100755
--- a/mesonbuild/msubprojects.py
+++ b/mesonbuild/msubprojects.py
@@ -189,7 +189,7 @@ class Runner:
# cached.
windows_proof_rmtree(self.repo_dir)
try:
- self.wrap_resolver.resolve(self.wrap.name, 'meson')
+ self.wrap_resolver.resolve(self.wrap.name)
self.log(' -> New version extracted')
return True
except WrapException as e:
@@ -292,7 +292,7 @@ class Runner:
# Delete existing directory and redownload
windows_proof_rmtree(self.repo_dir)
try:
- self.wrap_resolver.resolve(self.wrap.name, 'meson')
+ self.wrap_resolver.resolve(self.wrap.name)
self.update_git_done()
return True
except WrapException as e:
@@ -464,7 +464,7 @@ class Runner:
self.log(' -> Already downloaded')
return True
try:
- self.wrap_resolver.resolve(self.wrap.name, 'meson')
+ self.wrap_resolver.resolve(self.wrap.name)
self.log(' -> done')
except WrapException as e:
self.log(' ->', mlog.red(str(e)))
diff --git a/mesonbuild/wrap/wrap.py b/mesonbuild/wrap/wrap.py
index c0f0f07..a1bf725 100644
--- a/mesonbuild/wrap/wrap.py
+++ b/mesonbuild/wrap/wrap.py
@@ -45,6 +45,9 @@ from .. import mesonlib
if T.TYPE_CHECKING:
import http.client
+ from typing_extensions import Literal
+
+ Method = Literal['meson', 'cmake']
try:
# Importing is just done to check if SSL exists, so all warnings
@@ -406,7 +409,7 @@ class Resolver:
return wrap_name
return None
- def resolve(self, packagename: str, method: str) -> str:
+ def resolve(self, packagename: str, force_method: T.Optional[Method] = None) -> T.Tuple[str, Method]:
self.packagename = packagename
self.directory = packagename
self.wrap = self.wraps.get(packagename)
@@ -443,17 +446,28 @@ class Resolver:
self.dirname = self.wrap.filename
rel_path = os.path.relpath(self.dirname, self.source_dir)
- if method == 'meson':
- buildfile = os.path.join(self.dirname, 'meson.build')
- elif method == 'cmake':
- buildfile = os.path.join(self.dirname, 'CMakeLists.txt')
- else:
- raise WrapException('Only the methods "meson" and "cmake" are supported')
+ # Map each supported method to a file that must exist at the root of source tree.
+ methods_map: T.Dict[Method, str] = {
+ 'meson': 'meson.build',
+ 'cmake': 'CMakeLists.txt',
+ }
+
+ # Check if this wrap forces a specific method, use meson otherwise.
+ method = T.cast('T.Optional[Method]', self.wrap.values.get('method', force_method))
+ if method and method not in methods_map:
+ allowed_methods = ', '.join(methods_map.keys())
+ raise WrapException(f'Wrap method {method!r} is not supported, must be one of: {allowed_methods}')
+ if force_method and method != force_method:
+ raise WrapException(f'Wrap method is {method!r} but we are trying to configure it with {force_method}')
+ method = method or 'meson'
+
+ def has_buildfile() -> bool:
+ return os.path.exists(os.path.join(self.dirname, methods_map[method]))
# The directory is there and has meson.build? Great, use it.
- if os.path.exists(buildfile):
+ if has_buildfile():
self.validate()
- return rel_path
+ return rel_path, method
# Check if the subproject is a git submodule
self.resolve_git_submodule()
@@ -491,16 +505,14 @@ class Resolver:
windows_proof_rmtree(self.dirname)
raise
- # A meson.build or CMakeLists.txt file is required in the directory
- if not os.path.exists(buildfile):
- raise WrapException(f'Subproject exists but has no {os.path.basename(buildfile)} file')
+ if not has_buildfile():
+ raise WrapException(f'Subproject exists but has no {methods_map[method]} file.')
# At this point, the subproject has been successfully resolved for the
# first time so save off the hash of the entire wrap file for future
# reference.
self.wrap.update_hash_cache(self.dirname)
-
- return rel_path
+ return rel_path, method
def check_can_download(self) -> None:
# Don't download subproject data based on wrap file if requested.
diff --git a/test cases/cmake/26 dependency fallback/main.cpp b/test cases/cmake/26 dependency fallback/main.cpp
new file mode 100644
index 0000000..9507961
--- /dev/null
+++ b/test cases/cmake/26 dependency fallback/main.cpp
@@ -0,0 +1,10 @@
+#include <iostream>
+#include <cmMod.hpp>
+
+using namespace std;
+
+int main(void) {
+ cmModClass obj("Hello");
+ cout << obj.getStr() << endl;
+ return 0;
+}
diff --git a/test cases/cmake/26 dependency fallback/meson.build b/test cases/cmake/26 dependency fallback/meson.build
new file mode 100644
index 0000000..b36aaac
--- /dev/null
+++ b/test cases/cmake/26 dependency fallback/meson.build
@@ -0,0 +1,30 @@
+project('cmakeSubTest', ['c', 'cpp'])
+
+# Fallback to a CMake subproject
+sub_dep = dependency('cmModLib++')
+exe1 = executable('main', ['main.cpp'], dependencies: [sub_dep])
+test('test1', exe1)
+
+# Subproject contains both meson.build and CMakeLists.txt. It should default
+# to meson but wrap force cmake.
+subproject('force_cmake')
+
+testcase expect_error('Wrap method \'notfound\' is not supported, must be one of: meson, cmake')
+ subproject('broken_method')
+endtestcase
+
+# With method=meson we can't use cmake.subproject()
+cmake = import('cmake')
+testcase expect_error('Wrap method is \'meson\' but we are trying to configure it with cmake')
+ cmake.subproject('meson_method')
+endtestcase
+
+# cmake.subproject() force cmake method even if meson.build exists.
+testcase expect_error('Subproject exists but has no CMakeLists.txt file.')
+ cmake.subproject('meson_subp')
+endtestcase
+
+# Without specifying the method it defaults to meson even if CMakeLists.txt exists.
+testcase expect_error('Subproject exists but has no meson.build file.')
+ subproject('cmake_subp')
+endtestcase
diff --git a/test cases/cmake/26 dependency fallback/subprojects/broken_method.wrap b/test cases/cmake/26 dependency fallback/subprojects/broken_method.wrap
new file mode 100644
index 0000000..ce0690a
--- /dev/null
+++ b/test cases/cmake/26 dependency fallback/subprojects/broken_method.wrap
@@ -0,0 +1,2 @@
+[wrap-file]
+method=notfound
diff --git a/test cases/cmake/26 dependency fallback/subprojects/cmMod.wrap b/test cases/cmake/26 dependency fallback/subprojects/cmMod.wrap
new file mode 100644
index 0000000..9e6d855
--- /dev/null
+++ b/test cases/cmake/26 dependency fallback/subprojects/cmMod.wrap
@@ -0,0 +1,5 @@
+[wrap-file]
+method = cmake
+
+[provide]
+cmModLib++ = cmModLib___dep
diff --git a/test cases/cmake/26 dependency fallback/subprojects/cmMod/CMakeLists.txt b/test cases/cmake/26 dependency fallback/subprojects/cmMod/CMakeLists.txt
new file mode 100644
index 0000000..d08e55c
--- /dev/null
+++ b/test cases/cmake/26 dependency fallback/subprojects/cmMod/CMakeLists.txt
@@ -0,0 +1,20 @@
+cmake_minimum_required(VERSION 3.5)
+
+project(cmMod)
+set(CMAKE_CXX_STANDARD 14)
+
+include_directories(${CMAKE_CURRENT_BINARY_DIR})
+
+add_definitions("-DDO_NOTHING_JUST_A_FLAG=1")
+
+add_library(cmModLib++ SHARED cmMod.cpp)
+target_compile_definitions(cmModLib++ PRIVATE MESON_MAGIC_FLAG=21)
+target_compile_definitions(cmModLib++ INTERFACE MESON_MAGIC_FLAG=42)
+
+# Test PCH support
+if(${CMAKE_VERSION} VERSION_GREATER_EQUAL "3.16.0")
+ target_precompile_headers(cmModLib++ PRIVATE "cpp_pch.hpp")
+endif()
+
+include(GenerateExportHeader)
+generate_export_header(cmModLib++)
diff --git a/test cases/cmake/26 dependency fallback/subprojects/cmMod/cmMod.cpp b/test cases/cmake/26 dependency fallback/subprojects/cmMod/cmMod.cpp
new file mode 100644
index 0000000..f4cbea0
--- /dev/null
+++ b/test cases/cmake/26 dependency fallback/subprojects/cmMod/cmMod.cpp
@@ -0,0 +1,15 @@
+#include "cmMod.hpp"
+
+using namespace std;
+
+#if MESON_MAGIC_FLAG != 21
+#error "Invalid MESON_MAGIC_FLAG (private)"
+#endif
+
+cmModClass::cmModClass(string foo) {
+ str = foo + " World";
+}
+
+string cmModClass::getStr() const {
+ return str;
+}
diff --git a/test cases/cmake/26 dependency fallback/subprojects/cmMod/cmMod.hpp b/test cases/cmake/26 dependency fallback/subprojects/cmMod/cmMod.hpp
new file mode 100644
index 0000000..4445e1f
--- /dev/null
+++ b/test cases/cmake/26 dependency fallback/subprojects/cmMod/cmMod.hpp
@@ -0,0 +1,18 @@
+#pragma once
+
+#include "cmmodlib++_export.h"
+#include <string>
+
+#if MESON_MAGIC_FLAG != 42 && MESON_MAGIC_FLAG != 21
+#error "Invalid MESON_MAGIC_FLAG"
+#endif
+
+class CMMODLIB___EXPORT cmModClass {
+private:
+ std::string str;
+
+public:
+ cmModClass(std::string foo);
+
+ std::string getStr() const;
+};
diff --git a/test cases/cmake/26 dependency fallback/subprojects/cmMod/cpp_pch.hpp b/test cases/cmake/26 dependency fallback/subprojects/cmMod/cpp_pch.hpp
new file mode 100644
index 0000000..aa7ceb3
--- /dev/null
+++ b/test cases/cmake/26 dependency fallback/subprojects/cmMod/cpp_pch.hpp
@@ -0,0 +1,2 @@
+#include <vector>
+#include <string>
diff --git a/test cases/cmake/26 dependency fallback/subprojects/cmake_subp/CMakeLists.txt b/test cases/cmake/26 dependency fallback/subprojects/cmake_subp/CMakeLists.txt
new file mode 100644
index 0000000..6443fca
--- /dev/null
+++ b/test cases/cmake/26 dependency fallback/subprojects/cmake_subp/CMakeLists.txt
@@ -0,0 +1,2 @@
+cmake_minimum_required(VERSION 3.5)
+project(cmModDummy)
diff --git a/test cases/cmake/26 dependency fallback/subprojects/force_cmake.wrap b/test cases/cmake/26 dependency fallback/subprojects/force_cmake.wrap
new file mode 100644
index 0000000..b24754e
--- /dev/null
+++ b/test cases/cmake/26 dependency fallback/subprojects/force_cmake.wrap
@@ -0,0 +1,2 @@
+[wrap-file]
+method=cmake
diff --git a/test cases/cmake/26 dependency fallback/subprojects/force_cmake/CMakeLists.txt b/test cases/cmake/26 dependency fallback/subprojects/force_cmake/CMakeLists.txt
new file mode 100644
index 0000000..497beb9
--- /dev/null
+++ b/test cases/cmake/26 dependency fallback/subprojects/force_cmake/CMakeLists.txt
@@ -0,0 +1,2 @@
+cmake_minimum_required(VERSION 3.5)
+project(cmModBoth)
diff --git a/test cases/cmake/26 dependency fallback/subprojects/force_cmake/meson.build b/test cases/cmake/26 dependency fallback/subprojects/force_cmake/meson.build
new file mode 100644
index 0000000..9264974
--- /dev/null
+++ b/test cases/cmake/26 dependency fallback/subprojects/force_cmake/meson.build
@@ -0,0 +1,4 @@
+project('both methods')
+
+# Ensure the meson method is not used.
+notfound()
diff --git a/test cases/cmake/26 dependency fallback/subprojects/meson_method.wrap b/test cases/cmake/26 dependency fallback/subprojects/meson_method.wrap
new file mode 100644
index 0000000..e52701e
--- /dev/null
+++ b/test cases/cmake/26 dependency fallback/subprojects/meson_method.wrap
@@ -0,0 +1,2 @@
+[wrap-file]
+method=meson
diff --git a/test cases/cmake/26 dependency fallback/subprojects/meson_subp/meson.build b/test cases/cmake/26 dependency fallback/subprojects/meson_subp/meson.build
new file mode 100644
index 0000000..e4746ce
--- /dev/null
+++ b/test cases/cmake/26 dependency fallback/subprojects/meson_subp/meson.build
@@ -0,0 +1 @@
+project('dummy')