Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[SR-12198] SwiftPM's only-Clang-or-Swift-sources-per-target restriction disallows use of _ObjectiveCBridgeable #4594

Open
swift-ci opened this issue Feb 13, 2020 · 1 comment
Labels

Comments

@swift-ci
Copy link
Contributor

Previous ID SR-12198
Radar rdar://problem/59496029
Original Reporter capnslipp (JIRA User)
Type Bug
Environment
  • macOS 10.15.3

  • Xcode 11.3.1 (11C505)

  • Apple Swift version 5.1.3 (swiftlang-1100.0.282.1 clang-1100.0.33.15)

Additional Detail from JIRA
Votes 0
Component/s Package Manager
Labels Bug
Assignee None
Priority Medium

md5: 98a587546d81c6f2fe1e37b11a63512e

Issue Description:

Swift Package Manager's restriction to disallow targets to contain both Clang sources (C/C+/Obj-C/Obj-C+) and Swift sources (swiftpm/Sources/PackageLoading/PackageBuilder.swift createTarget(…) in the current master) prevents Obj-C and Swift code from being compiled into the same module, which prevents use of some APIs that require Obj-C and Swift code to exist in the same module— in the case I'm facing, it prevents use of _ObjectiveCBridgeable (swift/lib/Sema/TypeCheckProtocol.cpp ConformanceChecker::checkConformance(…) in the current master).

When attempting to Swift-Package-Managerize an existing Swift-Obj-C bridge library that uses _ObjectiveCBridgeable that my project is reliant on, I had to split the Obj-C and Swift code into separate targets like this:

// swift-tools-version:5.0
import PackageDescriptionlet package = Package(
    name: "MyLibrary",
    products: [
        .library(name: "MyLibrary_ObjC", targets: ["MyLibrary_ObjC"]),
        .library(name: "MyLibrary", targets: ["MyLibrary"]),
    ],
    dependencies: [],
    targets: [
        .target(name: "MyLibrary_ObjC", dependencies: [], path: "Sources/",
            sources: [ "Int2/Int2.mm", "Int3/Int3.mm", "Int4/Int4.mm", ]
        ),
        .target(name: "MyLibrary", dependencies: ["MyLibrary_ObjC"], path: "Sources/",
            exclude: [ "Int2/Int2.mm", "Int3/Int3.mm", "Int4/Int4.mm", ]
        )
    ],
    cLanguageStandard: .c11,
    cxxLanguageStandard: .cxx11
)

However, when running swift build, I receive errors along the lines of:

error: conformance of 'Float3' to '_ObjectiveCBridgeable' can only be written in module 'MyLibrary_ObjC'

which is an understandable restriction of _ObjectiveCBridgeable, since it does some fancy compiler stuff to minimize marshaling overhead between Obj-C and Swift (AFAIK).

The end result is that functionality that's allowed and compatible in Xcode targets, with CocoaPods, and with Carthage isn't allowed in SwiftPM, and that breaks core functionality of the language.


I'm fully aware that _ObjectiveCBridgeable is considered a “private-ish” API (subject to change, yada yada yada), but it is also the only way to properly marshal custom non-class abstract data types (e.g. C structs) between Obj-C and Swift, by telling the Swift compiler how to properly box and unbox one's data in an NSValue. A more elegant formalized solution would be nice, but in the present the only way (that I'm aware of) to be able to continue interfacing with existing Obj-C libraries that make use of custom ADTs without problems is the _ObjectiveCBridgeable interface.

Perhaps someday my entire project will end up ported over to Swift, as seems to be par for course now, but that's unlikely to happen anytime soon since my Obj-C code is using features that aren't available in Swift, specifically NSProxy and -forwardInvocation:.

I believe the larger picture, however, is cross-compatibility with other languages— while Obj-C is on its way out, Swift compatibility with Python is on its way in and one could hope compatibility with languages like C++, C#, Kotlin, Rust, or others could be on the horizon, and efficiently marshaling data types is essential to making those integrations practicle. This is a lot of supposition for the present issue — _ObjectiveCBridgeable — but it seems like it'll be a continuous hurdle to not be able to compile multiple languages' representations, extensions, and methods of a single set of data types into one module. It's the kind of hurdle that annoys me when using Swift, that makes me miss the older Obj-C days — as well as how things work in just about every other popular programming language under the sun — where there's always a way to tell the compiler/linker/etc. to munge the data and produce the output you know will work, unsafety and compiler warnings be damned.

A good programmer with a good programming language toolset has the power to do just about anything, but knows to keep the “dirtiest hacky” code to a minimum in a thoroughly error-checked and tested shared library. Swift is an opinionated language, which is often a good thing, but IMHO SwiftPM takes those opinions a bit too far and hurts functionality. Perhaps something like a .bridgeLibrary() with documentation that it's intended for language-bridging code and not general use would be a suitable salve for edge-cases like mine.

@beccadax
Copy link
Contributor

@swift-ci create

@swift-ci swift-ci transferred this issue from apple/swift-issues Apr 25, 2022
@shahmishal shahmishal transferred this issue from apple/swift May 4, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

3 participants