summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--docs/markdown/snippets/swift_cxx_interoperability.md16
-rw-r--r--mesonbuild/backend/ninjabackend.py17
-rw-r--r--mesonbuild/backend/xcodebackend.py2
-rw-r--r--mesonbuild/build.py7
-rw-r--r--mesonbuild/compilers/compilers.py3
-rw-r--r--mesonbuild/compilers/swift.py13
-rw-r--r--mesonbuild/interpreter/kwargs.py1
-rw-r--r--mesonbuild/interpreter/type_checking.py1
-rw-r--r--test cases/swift/11 mixed cpp/meson.build2
-rw-r--r--test cases/swift/13 mixed objcpp/meson.build2
10 files changed, 47 insertions, 17 deletions
diff --git a/docs/markdown/snippets/swift_cxx_interoperability.md b/docs/markdown/snippets/swift_cxx_interoperability.md
index f18e114d2..9e324f9bd 100644
--- a/docs/markdown/snippets/swift_cxx_interoperability.md
+++ b/docs/markdown/snippets/swift_cxx_interoperability.md
@@ -1,13 +1,23 @@
## Swift/C++ interoperability is now supported
It is now possible to create Swift executables that can link to C++ or
-Objective-C++ libraries. Only specifying a bridging header for the Swift
-target is required.
+Objective-C++ libraries. To enable this feature, set the target kwarg
+_swift\_interoperability\_mode_ to 'cpp'.
+
+To import C++ code, specify a bridging header in the Swift target's
+sources, or use another way such as adding a directory containing a
+Clang module map to its include path.
+
+Note: Enabling C++ interoperability in a library target is a breaking
+change. Swift libraries that enable it need their consumers to enable
+it as well, as per [the Swift documentation][1].
Swift 5.9 is required to use this feature. Xcode 15 is required if the
Xcode backend is used.
```meson
lib = static_library('mylib', 'mylib.cpp')
-exe = executable('prog', 'main.swift', 'mylib.h', link_with: lib)
+exe = executable('prog', 'main.swift', 'mylib.h', link_with: lib, swift_interoperability_mode: 'cpp')
```
+
+[1]: https://www.swift.org/documentation/cxx-interop/project-build-setup/#vending-packages-that-enable-c-interoperability
diff --git a/mesonbuild/backend/ninjabackend.py b/mesonbuild/backend/ninjabackend.py
index b2fb60580..595a27a05 100644
--- a/mesonbuild/backend/ninjabackend.py
+++ b/mesonbuild/backend/ninjabackend.py
@@ -2266,6 +2266,20 @@ class NinjaBackend(backends.Backend):
relsrc = []
abs_headers = []
header_imports = []
+
+ if not target.uses_swift_cpp_interop():
+ cpp_targets = [t for t in target.link_targets if t.uses_swift_cpp_interop()]
+ if cpp_targets != []:
+ target_word = 'targets' if len(cpp_targets) > 1 else 'target'
+ first = ', '.join(repr(t.name) for t in cpp_targets[:-1])
+ and_word = ' and ' if len(cpp_targets) > 1 else ''
+ last = repr(cpp_targets[-1].name)
+ enable_word = 'enable' if len(cpp_targets) > 1 else 'enables'
+ raise MesonException('Swift target {0} links against {1} {2}{3}{4} which {5} C++ interoperability. '
+ 'This requires {0} to also have it enabled. '
+ 'Add "swift_interoperability_mode: \'cpp\'" to the definition of {0}.'
+ .format(repr(target.name), target_word, first, and_word, last, enable_word))
+
for i in target.get_sources():
if swiftc.can_compile(i):
rels = i.rel_to_builddir(self.build_to_src)
@@ -2282,8 +2296,7 @@ class NinjaBackend(backends.Backend):
os.makedirs(self.get_target_private_dir_abs(target), exist_ok=True)
compile_args = self.generate_basic_compiler_args(target, swiftc)
compile_args += swiftc.get_module_args(module_name)
- if mesonlib.version_compare(swiftc.version, '>=5.9'):
- compile_args += swiftc.get_cxx_interoperability_args(target.compilers)
+ compile_args += swiftc.get_cxx_interoperability_args(target)
compile_args += self.build.get_project_args(swiftc, target.subproject, target.for_machine)
compile_args += self.build.get_global_args(swiftc, target.for_machine)
if isinstance(target, (build.StaticLibrary, build.SharedLibrary)):
diff --git a/mesonbuild/backend/xcodebackend.py b/mesonbuild/backend/xcodebackend.py
index c0522e3c7..e7bd48700 100644
--- a/mesonbuild/backend/xcodebackend.py
+++ b/mesonbuild/backend/xcodebackend.py
@@ -1831,7 +1831,7 @@ class XCodeBackend(backends.Backend):
settings_dict.add_item('SECTORDER_FLAGS', '')
if is_swift and bridging_header:
settings_dict.add_item('SWIFT_OBJC_BRIDGING_HEADER', bridging_header)
- if self.objversion >= 60 and 'cpp' in langs:
+ if self.objversion >= 60 and target.uses_swift_cpp_interop():
settings_dict.add_item('SWIFT_OBJC_INTEROP_MODE', 'objcxx')
settings_dict.add_item('BUILD_DIR', symroot)
settings_dict.add_item('OBJROOT', f'{symroot}/build')
diff --git a/mesonbuild/build.py b/mesonbuild/build.py
index 9014d09fb..2adfb9803 100644
--- a/mesonbuild/build.py
+++ b/mesonbuild/build.py
@@ -75,7 +75,7 @@ lang_arg_kwargs |= {
vala_kwargs = {'vala_header', 'vala_gir', 'vala_vapi'}
rust_kwargs = {'rust_crate_type', 'rust_dependency_map'}
cs_kwargs = {'resources', 'cs_args'}
-swift_kwargs = {'swift_module_name'}
+swift_kwargs = {'swift_interoperability_mode', 'swift_module_name'}
buildtarget_kwargs = {
'build_by_default',
@@ -1275,6 +1275,8 @@ class BuildTarget(Target):
raise InvalidArguments(f'Invalid rust_dependency_map "{rust_dependency_map}": must be a dictionary with string values.')
self.rust_dependency_map = rust_dependency_map
+ self.swift_interoperability_mode = kwargs.get('swift_interoperability_mode')
+
self.swift_module_name = kwargs.get('swift_module_name')
if self.swift_module_name == '':
self.swift_module_name = self.name
@@ -1702,6 +1704,9 @@ class BuildTarget(Target):
def uses_fortran(self) -> bool:
return 'fortran' in self.compilers
+ def uses_swift_cpp_interop(self) -> bool:
+ return self.swift_interoperability_mode == 'cpp' and 'swift' in self.compilers
+
def get_using_msvc(self) -> bool:
'''
Check if the dynamic linker is MSVC. Used by Executable, StaticLibrary,
diff --git a/mesonbuild/compilers/compilers.py b/mesonbuild/compilers/compilers.py
index f32205db9..a823aeb00 100644
--- a/mesonbuild/compilers/compilers.py
+++ b/mesonbuild/compilers/compilers.py
@@ -1121,9 +1121,6 @@ class Compiler(HoldableObject, metaclass=abc.ABCMeta):
def get_compile_only_args(self) -> T.List[str]:
return []
- def get_cxx_interoperability_args(self, lang: T.Dict[str, Compiler]) -> T.List[str]:
- raise EnvironmentException('This compiler does not support CXX interoperability')
-
def get_preprocess_only_args(self) -> T.List[str]:
raise EnvironmentException('This compiler does not have a preprocessor')
diff --git a/mesonbuild/compilers/swift.py b/mesonbuild/compilers/swift.py
index 3fff7a1ff..89da306ab 100644
--- a/mesonbuild/compilers/swift.py
+++ b/mesonbuild/compilers/swift.py
@@ -153,11 +153,14 @@ class SwiftCompiler(Compiler):
return ['-working-directory', path]
- def get_cxx_interoperability_args(self, lang: T.Dict[str, Compiler]) -> T.List[str]:
- if 'cpp' in lang or 'objcpp' in lang:
- return ['-cxx-interoperability-mode=default']
- else:
- return ['-cxx-interoperability-mode=off']
+ def get_cxx_interoperability_args(self, target: T.Optional[build.BuildTarget] = None) -> T.List[str]:
+ if target is not None and not target.uses_swift_cpp_interop():
+ return []
+
+ if version_compare(self.version, '<5.9'):
+ raise MesonException(f'Compiler {self} does not support C++ interoperability')
+
+ return ['-cxx-interoperability-mode=default']
def get_library_args(self) -> T.List[str]:
return ['-parse-as-library']
diff --git a/mesonbuild/interpreter/kwargs.py b/mesonbuild/interpreter/kwargs.py
index 193e100f3..7dd49a1db 100644
--- a/mesonbuild/interpreter/kwargs.py
+++ b/mesonbuild/interpreter/kwargs.py
@@ -363,6 +363,7 @@ class _BuildTarget(_BaseBuildTarget):
d_module_versions: T.List[T.Union[str, int]]
d_unittest: bool
rust_dependency_map: T.Dict[str, str]
+ swift_interoperability_mode: Literal['c', 'cpp']
swift_module_name: str
sources: SourcesVarargsType
c_args: T.List[str]
diff --git a/mesonbuild/interpreter/type_checking.py b/mesonbuild/interpreter/type_checking.py
index ffa561975..a551d0f7c 100644
--- a/mesonbuild/interpreter/type_checking.py
+++ b/mesonbuild/interpreter/type_checking.py
@@ -633,6 +633,7 @@ _BUILD_TARGET_KWS: T.List[KwargInfo] = [
default={},
since='1.2.0',
),
+ KwargInfo('swift_interoperability_mode', str, default='c', validator=in_set_validator({'c', 'cpp'}), since='1.9.0'),
KwargInfo('swift_module_name', str, default='', since='1.9.0'),
KwargInfo('build_rpath', str, default='', since='0.42.0'),
KwargInfo(
diff --git a/test cases/swift/11 mixed cpp/meson.build b/test cases/swift/11 mixed cpp/meson.build
index 94b70f07b..60273415b 100644
--- a/test cases/swift/11 mixed cpp/meson.build
+++ b/test cases/swift/11 mixed cpp/meson.build
@@ -8,5 +8,5 @@ if not swiftc.version().version_compare('>= 5.9')
endif
lib = static_library('mylib', 'mylib.cpp')
-exe = executable('prog', 'main.swift', 'mylib.h', link_with: lib)
+exe = executable('prog', 'main.swift', 'mylib.h', link_with: lib, swift_interoperability_mode: 'cpp')
test('cpp interface', exe)
diff --git a/test cases/swift/13 mixed objcpp/meson.build b/test cases/swift/13 mixed objcpp/meson.build
index 69098e246..a76162acb 100644
--- a/test cases/swift/13 mixed objcpp/meson.build
+++ b/test cases/swift/13 mixed objcpp/meson.build
@@ -8,5 +8,5 @@ if not swiftc.version().version_compare('>= 5.9')
endif
lib = static_library('mylib', 'mylib.mm')
-exe = executable('prog', 'main.swift', 'mylib.h', link_with: lib)
+exe = executable('prog', 'main.swift', 'mylib.h', link_with: lib, swift_interoperability_mode: 'cpp')
test('objcpp interface', exe)