Skip to content

Commit

Permalink
swift: Fix duplicate SDK include paths causing a compile error
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
thewildtree committed Jan 29, 2025
1 parent ae1bb2f commit b79cba1
Show file tree
Hide file tree
Showing 3 changed files with 48 additions and 2 deletions.
34 changes: 32 additions & 2 deletions mesonbuild/compilers/swift.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,17 @@

from __future__ import annotations

import re
import subprocess, os.path
import typing as T

from ..mesonlib import EnvironmentException

from .. import mlog
from ..mesonlib import EnvironmentException, MesonException
from .compilers import Compiler, clike_debug_args


if T.TYPE_CHECKING:
from ..dependencies import Dependency
from ..envconfig import MachineInfo
from ..environment import Environment
from ..linkers.linkers import DynamicLinker
Expand Down Expand Up @@ -39,6 +42,17 @@ def __init__(self, exelist: T.List[str], version: str, for_machine: MachineChoic
is_cross=is_cross, full_version=full_version,
linker=linker)
self.version = version
if self.info.is_darwin():
try:
self.sdk_path = subprocess.check_output(['xcrun', '--show-sdk-path'],
universal_newlines=True,
encoding='utf-8', stderr=subprocess.STDOUT).strip()
except subprocess.CalledProcessError as e:
mlog.error("Failed to get Xcode SDK path: " + e.output)
raise MesonException('Xcode license not accepted yet. Run `sudo xcodebuild -license`.')
except FileNotFoundError:
mlog.error('xcrun not found. Install Xcode to compile Swift code.')
raise MesonException('Could not detect Xcode. Please install it to compile Swift code.')

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

def get_dependency_compile_args(self, dep: Dependency) -> T.List[str]:
args = dep.get_compile_args()
# Some deps might sneak in a hardcoded path to an older macOS SDK, which can
# cause compilation errors. Let's replace all .sdk paths with the current one.
# SwiftPM does it this way: https://github.com/swiftlang/swift-package-manager/pull/6772
# Not tested on anything else than macOS for now.
if not self.info.is_darwin():
return args
pattern = re.compile(r'.*\/MacOSX[^\/]*\.sdk(\/.*|$)')
for i, arg in enumerate(args):
if arg.startswith('-I'):
match = pattern.match(arg)
if match:
args[i] = '-I' + self.sdk_path + match.group(1)
return args

def depfile_for_object(self, objfile: str) -> T.Optional[str]:
return os.path.splitext(objfile)[0] + '.' + self.get_depfile_suffix()

Expand Down
4 changes: 4 additions & 0 deletions test cases/swift/9 sdk path from dep/foo.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
// This import is needed for swiftc to implictly import the FFI module
// which will in turn conflict with the dependency's include path and error out
// if we don't manually replace all SDK paths with the newest one.
import Foundation
12 changes: 12 additions & 0 deletions test cases/swift/9 sdk path from dep/meson.build
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
project('swift sdk include dir test', 'swift')

bar_dep = declare_dependency(
# Simulates including 'libffi' from brew as a dep via pkg-config
# Without a workaround that replaces all SDK paths with the most recent one,
# a compile error will occur due to conflicting definitions of the FFI module.
compile_args: '-I/Library/Developer/CommandLineTools/SDKs/MacOSX12.sdk/usr/include/ffi',
)

foo = static_library('foo', 'foo.swift',
dependencies: [bar_dep],
)

0 comments on commit b79cba1

Please sign in to comment.