Any changes to SPM 5.2 (with Xcode 11.4 Beta2) ? Pure Objective-C project which contains multiple targets now fail to integrate

The Package.swift looks like this:

// swift-tools-version:5.0
// The swift-tools-version declares the minimum version of Swift required to build this package.
import PackageDescription

let package = Package(
    name: "SDWebImage",
    platforms: [
        .macOS(.v10_10),
        .iOS(.v8),
        .tvOS(.v9),
        .watchOS(.v2)
    ],
    products: [
        // Products define the executables and libraries produced by a package, and make them visible to other packages.
        .library(
            name: "SDWebImage",
            targets: ["SDWebImage"]),
        .library(
            name: "SDWebImageMapKit",
            targets: ["SDWebImageMapKit"])
    ],
    dependencies: [
        // Dependencies declare other packages that this package depends on.
        // .package(url: /* package url */, from: "1.0.0"),
    ],
    targets: [
        // Targets are the basic building blocks of a package. A target can define a module or a test suite.
        // Targets can depend on other targets in this package, and on products in packages which this package depends on.
        .target(
            name: "SDWebImage",
            dependencies: [],
            path: ".",
            sources: ["SDWebImage/Core", "SDWebImage/Private"],
            publicHeadersPath: "SDWebImage/Core",
            cSettings: [
                .headerSearchPath("SDWebImage/Core"),
                .headerSearchPath("SDWebImage/Private")
            ]
        ),
        .target(
            name: "SDWebImageMapKit",
            dependencies: ["SDWebImage"],
            path: ".",
            sources: ["SDWebImage/MapKit"],
            publicHeadersPath: "SDWebImage/MapKit",
            cSettings: [
                .headerSearchPath("SDWebImage/Core"),
                .headerSearchPath("SDWebImage/Private")
            ]
        )
    ]
)

The folder structure looks like this:

- SDWebImage
-- Core
--- xxx.h
--- xxx.m
-- Private
--- yyy.h
--- yyy.m
-- MapKit
--- MKAnnotationView+WebCache.h
--- MKAnnotationView+WebCache.m

This Package.swift DSL previously works fine on Xcode 11.3.1 via SwiftPM, however, when using Xcode 11.4-Beta2, this through error with:

target 'SDWebImageMapKit' referenced in product 'SDWebImageMapKit' is empty .

As I imagine, the SDWebImageMapKit should match the MKAnnotationView+WebCache.h/.m files. Does the category only source files cause the issue ?

I've already fired Apple Feedback report via FB7571208, but I remember that Xcode use the SwiftPM dynamic lib, so they should share the same codebase. Is some internel changes break this ?

I'm the maintainer of this SDWebImage Objective-C framework (can be mixed Swift/Objc in the future).

The issue report can be found here: SPM: SDWebImage.git could not be resolved · Issue #2941 · SDWebImage/SDWebImage · GitHub

I'll be appreciated if some people can provide detail explaintation or workaround solution.

That other thread is not the same thing.

If this worked before it was an accident. sources is intended as an array of exact files, not an array of search paths. It restricts the source files to only those files specified, and it is for when an explicit list would be shorter than accomplishing the same thing with a ton of exclude entries.

What it appears you mean should be specified roughly like this instead:

.target(
  name: "SDWebImage",
  dependencies: [], // Can be omitted.
  path: "SDWebImage",
  exclude: ["MapKit"],
  sources: nil, // Can be omitted.
  publicHeadersPath: "",
  cSettings: [] // Can be omitted.
),
.target(
  name: "SDWebImageMapKit",
  dependencies: ["SDWebImage"],
  path: "SDGWebImage/MapKit",
  sources: nil, // Can be omitted.
  publicHeadersPath: "",
  cSettings: [] // Can be omitted.
)

Hi.

sources is intended as an array of exact files, not an array of search paths. It restricts the source files to only those files specified

Any documentation here about this behavior ? Many the Swift Package written in Objc I found treat this as a Path allows directory, why this now become the Explicit individual files ?

And one more question, for the strcture above, there are questions to implments Swift PM without Moving the File Struture (which I don't want to do):

  1. If I don't have a list of source, how can I limit the SDWebImage only use Core source files, but SDWebImageMapKit use MapKit source files ? Do I need to use .path ?
  2. If I use the .path in SDWebImageMapKit target, new issue comes: The publicHeadersPath now forbid to write ../ to point to the SDWebImage/Core (SwiftPM report error). This is needed because the MapKit source code relay on the search path like #include "xxx.h", which is not located on SDWebImage/MapKit folder

Now I use another DSL to make it compile, this changes include the source code changes. Here it is:

  1. SDWebImage/MapKit/MKAnnotationView+WebCache.m, now it can not search the headers in SDWebImage/Private/MyPrivateHeader.h, it now looks like any third party downstream dependency. So I have to duplicated the helper method twice and use some Runtime method call to workaround. I think targets in same package, is different from targets outside this pacakge, because the previous one can share the private utils code and header, but last one can not this cause the issue ? But I found this is common design in most C/C++/ObjC code ?

2.The Package.swift DSL now changes to

    targets: [
        .target(
            name: "SDWebImage",
            dependencies: [],
            path: "SDWebImage",
            exclude: ["MapKit"],
            publicHeadersPath: "Core"
        ),
        .target(
            name: "SDWebImageMapKit",
            dependencies: ["SDWebImage"],
            path: "SDWebImage/MapKit"
        )
    ]

Note the publicHeadersPath: "Core" is required, because the Private folder source files, need the file (SDWebImageCompat.h) inside Core folder, they are not independent. Or this will cause compile error

Which I feels a little dissatisfied, this package also support CocoaPods && Carthage project, only SwiftPM need extra source code changes but not only package manager's DSL changes...

The only published documentation I knew about, and the reason I believed that to be the case, is here. I admit “An explicit list of source files.” is rather brief, but it says files, not directories or something ambiguous like locations. That is also the only way I’ve seen it used until now.

That said, I see now that that the documentation comment on the sources property and all the static factory methods do say that any directories will be searched for valid sources. So apparently it is supposed to work. :man_shrugging:

Can you restore it using the following?

cSettings: [
  .headerSearchPath("Private")
]

My earlier example had mistakenly removed it from cSettings because it was covered by the publicHeadersPath that I had adjusted to ""—I thought you were trying to make it visible to clients. But with the publicHeadersPath set to "Core" as you have it, you would need to put the Private directory back into the cSettings. With that arrangement SDWebImage can see those headers but none of its clients can. It sounds now like that is actually what you want.

Yes. This is what I want, because those private header have some side-effect which is not safe to use outside for client.

But anyway, those Copy Paste Code is small, and in the future, we will split the MapKit part into another Git repo, I think I can just prepare for that in advance. Thanks for your reply

This a regression that was reported by SR-12141 as well. The issue is addressed on SwiftPM trunk and swift-5.2-branch. Since SDWebImage supports macOS, you can download a trunk or 5.2 toolchain from swift.org and try verifying the fix for your package using swift build.

The new Package.swift DLS does not solve the issue. During my local test with Xcode 11.4-beta2, this still cause issue because it does not compile the SDWebImage/Core all source files. Only SDWebImage/Private source files get compiled, so this result a linker error.

Another issue, there are really strange log here:

no rule to process file '/Users/lizhuoli/Documents/GitHub/SDWebImage/SDWebImage/Core/SDWebImageCacheKeyFilter.h' of type 'file' for architecture 'x86_64'

What's that means type 'file', isn't .h means the C header ? WTF

Found the main reason of this bug, if using publicHeadersPath which match a directory contains both .h, .c, .m files, the source files (.c, .m) will be totally ignored.
See my test, first remove the publicHeadersPath:

When add the publicHeadersPath, test again:

Why SwiftPM 5.2 now design like this ? This break most 99% Objective-C project.

For C/C++ project, developer usually seperate the .h and .c/.cpp in different directory. But Objective-C, the .h and .m placed in the same directory. I don't find any Objective-C project which have a seperate include folder.

Should I fire Radar, or using the Swift Jira to fire this issue ?

1 Like

The xcrun swift build with the Xcode now seems broken as well...

image

image

This issue should be a bug and fixed via: [PackageLoading] Handle header files in TargetSourcesBuilder by aciidb0mb3r · Pull Request #2555 · apple/swift-package-manager · GitHub

This issue seems been solved by Xcode 11.4-Beta3.