Skip to content

Commit b79cba1

Browse files
committed
swift: Fix duplicate SDK include paths causing a compile error
Some dependencies can bring include paths pointing to older macOS SDK's. In this case, it was libffi pointing to SDK from 12.0. When the Foundation framework is imported in Swift, swiftc attempts to import the FFI module from the most recent version of the SDK, which causes a compilation error because of conflicting definitions between the two SDK versions. SwiftPM also had this problem: swiftlang/swift-package-manager#6772 The solution on our side is a simplified version of what SwiftPM did. Let's naively look for .sdk paths in the compile args of our dependencies and replace them with the most recent one. I included a test which is confirmed to fail without the workaround added in this patch. This was not tested on anything else than macOS, but I don't expect it to make the situation worse in any case.
1 parent ae1bb2f commit b79cba1

File tree

3 files changed

+48
-2
lines changed

3 files changed

+48
-2
lines changed

mesonbuild/compilers/swift.py

Lines changed: 32 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,17 @@
33

44
from __future__ import annotations
55

6+
import re
67
import subprocess, os.path
78
import typing as T
89

9-
from ..mesonlib import EnvironmentException
10-
10+
from .. import mlog
11+
from ..mesonlib import EnvironmentException, MesonException
1112
from .compilers import Compiler, clike_debug_args
1213

14+
1315
if T.TYPE_CHECKING:
16+
from ..dependencies import Dependency
1417
from ..envconfig import MachineInfo
1518
from ..environment import Environment
1619
from ..linkers.linkers import DynamicLinker
@@ -39,6 +42,17 @@ def __init__(self, exelist: T.List[str], version: str, for_machine: MachineChoic
3942
is_cross=is_cross, full_version=full_version,
4043
linker=linker)
4144
self.version = version
45+
if self.info.is_darwin():
46+
try:
47+
self.sdk_path = subprocess.check_output(['xcrun', '--show-sdk-path'],
48+
universal_newlines=True,
49+
encoding='utf-8', stderr=subprocess.STDOUT).strip()
50+
except subprocess.CalledProcessError as e:
51+
mlog.error("Failed to get Xcode SDK path: " + e.output)
52+
raise MesonException('Xcode license not accepted yet. Run `sudo xcodebuild -license`.')
53+
except FileNotFoundError:
54+
mlog.error('xcrun not found. Install Xcode to compile Swift code.')
55+
raise MesonException('Could not detect Xcode. Please install it to compile Swift code.')
4256

4357
def get_pic_args(self) -> T.List[str]:
4458
return []
@@ -55,6 +69,22 @@ def get_werror_args(self) -> T.List[str]:
5569
def get_dependency_gen_args(self, outtarget: str, outfile: str) -> T.List[str]:
5670
return ['-emit-dependencies']
5771

72+
def get_dependency_compile_args(self, dep: Dependency) -> T.List[str]:
73+
args = dep.get_compile_args()
74+
# Some deps might sneak in a hardcoded path to an older macOS SDK, which can
75+
# cause compilation errors. Let's replace all .sdk paths with the current one.
76+
# SwiftPM does it this way: https://github.com/swiftlang/swift-package-manager/pull/6772
77+
# Not tested on anything else than macOS for now.
78+
if not self.info.is_darwin():
79+
return args
80+
pattern = re.compile(r'.*\/MacOSX[^\/]*\.sdk(\/.*|$)')
81+
for i, arg in enumerate(args):
82+
if arg.startswith('-I'):
83+
match = pattern.match(arg)
84+
if match:
85+
args[i] = '-I' + self.sdk_path + match.group(1)
86+
return args
87+
5888
def depfile_for_object(self, objfile: str) -> T.Optional[str]:
5989
return os.path.splitext(objfile)[0] + '.' + self.get_depfile_suffix()
6090

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
// This import is needed for swiftc to implictly import the FFI module
2+
// which will in turn conflict with the dependency's include path and error out
3+
// if we don't manually replace all SDK paths with the newest one.
4+
import Foundation
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
project('swift sdk include dir test', 'swift')
2+
3+
bar_dep = declare_dependency(
4+
# Simulates including 'libffi' from brew as a dep via pkg-config
5+
# Without a workaround that replaces all SDK paths with the most recent one,
6+
# a compile error will occur due to conflicting definitions of the FFI module.
7+
compile_args: '-I/Library/Developer/CommandLineTools/SDKs/MacOSX12.sdk/usr/include/ffi',
8+
)
9+
10+
foo = static_library('foo', 'foo.swift',
11+
dependencies: [bar_dep],
12+
)

0 commit comments

Comments
 (0)