tuist / tuist

Tuist's CLI
https://tuist.io
MIT License
4.51k stars 537 forks source link

📦 [Dependencies.swift][SPM] Module map linking not working for ObjectiveC packages #4688

Closed danieleformichelli closed 7 months ago

danieleformichelli commented 2 years ago

What happened?

When a package defines a modulemap file, it is linked to the projects depending on it using the -fmodule-map-file=… in OTHER_C_FLAGS and OTHER_CPP_FLAGS.

This doesn't seem to work correctly if the dependent package is an Objective-C target.

As there were a number of issues related to this, I have created this one and will close the others as duplicate

How do we reproduce it?

There are a number of issues related to it, which you can use to reproduce it:

What were you expecting?

Import should work correctly out of the box

macOS version

12.0

Tuist version

3.10.0

Xcode version

13.4.1

Upvote & Fund

Fund with Polar

pgawlowski commented 2 years ago

Any suggested workaround until it's done?

sdgoij commented 2 years ago

Use the previously deprecated .packages in Project.swift, this imports the dependencies using "native" SPM mechanism.

See:

kabirkhaan commented 2 years ago

@danyf90 I would like to work it as this is also a blocker for our project, I really appreciate your help here

danieleformichelli commented 2 years ago

Hello @kabirkhaan and thank you for your interest! 🎉

As stated above, I think the problem comes from the fact that -fmodule-map-file= is ignored by ObjC target, hence they can't see headers of their dependencies.

There was this PR by @adellibovi which was trying to add the concept of imparted settings that could be the right direction, but we never managed to bring it to the finish line.

In general, the first thing to do should be to understand how the correct generated project should look like. This can be investigated either:

Once we know how to fix it, most of the logic should be in the PackageInfoMapper, but we can think about it later 💪

If you need, feel free to write me on Slack 🚀

kabirkhaan commented 2 years ago

@danyf90 I am starting working on it Just to be on a same page, Meanwhile working with tuist test I found an issue with brain tree library where they are using absolute path I created a PR and write a proposal also.

Do you think issue and PR is also related to this 🐞

danieleformichelli commented 2 years ago

@danyf90 I am starting working on it Just to be on a same page, Meanwhile working with tuist test I found an issue with brain tree library where they are using absolute path I created a PR and write a proposal also.

Do you think issue and PR is also related to this 🐞

No, it shouldn't be related 🙏

vapor-pawelw commented 1 year ago

Using GoogleSignIn and also experiencing this issue :(

denizdogan commented 1 year ago

@vapor-pawelw I use GoogleSignIn in my Tuist project with no issues. What's your configuration like? I haven't really done anything special to make it work, maybe it's .otherLinkerFlags(["-ObjC"]) in my Settings that's making the difference?

vapor-pawelw commented 1 year ago

@denizdogan hmm will check that, I started converting the project to Tuist just yesterday and that's one of the issues I had when defining dependencies, didn't have that problem when using SPM (and switched to native SPM later there was no problem with that either). It was complaining about GTMSessionFetcher, GTMAppAuth and some others and the problem was that the library #included a header of another library but it just didn't see it even though the project with that header was there. Also when I found it I thought it would be related to https://github.com/tuist/tuist/issues/4471, but I'll have a look at your solution and see if it helps - thanks!

vapor-pawelw commented 1 year ago

Also which version are you checking out @denizdogan ? I read in #4471 that this problem started with 6.2.0 but is not happening on 6.1.0 and earlier versions, I know that's a workaround but I've been using 6.2.0 already in the project and it doesn't feel good to downgrade now when it's already been working and deployed with that version

parse commented 1 year ago

Also which version are you checking out @denizdogan ? I read in #4471 that this problem started with 6.2.0 but is not happening on 6.1.0 and earlier versions, I know that's a workaround but I've been using 6.2.0 already in the project and it doesn't feel good to downgrade now when it's already been working and deployed with that version

Correct, for me 6.1.0 works. Newer versions (e.g. 6.2.x) does not work with the error message above.

denizdogan commented 1 year ago

@vapor-pawelw I've stuck with 6.1.0, and now that you mention it, I did try to upgrade to 6.2.0, but had some issues, so I just didn't upgrade, as it wasn't necessary for my project. I will be following this issue to see if anyone has a solution.

IceFloe commented 1 year ago

Firebase and GoogleSignIn can't work as external dependency, because of this only as a native SPM

jesus-mg-ios commented 1 year ago

I don't know if it's possible to create something using this approach. If the repo contains a way to download xcframeworks directly, download them and make a local spm, using it to generate the project. I'm referring something like https://github.com/akaffenberger/firebase-ios-sdk-xcframeworks that works with tuist, but instead of using something hosted in GitHub, creating it at local. @kwridan What do you think about?

nexron171 commented 1 year ago

Adding this lines to xcconfig

HEADER_SEARCH_PATHS=$(inherited) $(SRCROOT)/Tuist/Dependencies/SwiftPackageManager/.build/checkouts/GoogleSignIn/Sources/Public $(SRCROOT)/Tuist/Dependencies/SwiftPackageManager/.build/checkouts/AppAuth-iOS/Source/AppAuth $(SRCROOT)/Tuist/Dependencies/SwiftPackageManager/.build/checkouts/AppAuth-iOS/Source/AppAuthCore $(SRCROOT)/Tuist/Dependencies/SwiftPackageManager/.build/checkouts/gtm-session-fetcher/Source/SwiftPackage $(SRCROOT)/Tuist/Dependencies/SwiftPackageManager/.build/checkouts/GoogleSignIn/Sources/../../ $(SRCROOT)/Tuist/Dependencies/SwiftPackageManager/.build/checkouts/GTMAppAuth/GTMAppAuth/Sources/Public/GTMAppAuth

OTHER_LDFLAGS=-ObjC

helped me to build the project successfully in case of use GoogleSignIn SPM package

parse commented 1 year ago

Adding this lines to xcconfig

HEADER_SEARCH_PATHS=$(inherited) $(SRCROOT)/Tuist/Dependencies/SwiftPackageManager/.build/checkouts/GoogleSignIn/Sources/Public $(SRCROOT)/Tuist/Dependencies/SwiftPackageManager/.build/checkouts/AppAuth-iOS/Source/AppAuth $(SRCROOT)/Tuist/Dependencies/SwiftPackageManager/.build/checkouts/AppAuth-iOS/Source/AppAuthCore $(SRCROOT)/Tuist/Dependencies/SwiftPackageManager/.build/checkouts/gtm-session-fetcher/Source/SwiftPackage $(SRCROOT)/Tuist/Dependencies/SwiftPackageManager/.build/checkouts/GoogleSignIn/Sources/../../ $(SRCROOT)/Tuist/Dependencies/SwiftPackageManager/.build/checkouts/GTMAppAuth/GTMAppAuth/Sources/Public/GTMAppAuth

OTHER_LDFLAGS=-ObjC

helped me to build the project successfully in case of use GoogleSignIn SPM package

Thanks for the tip! What versions did you use of GoogleSignIn (and Firebase if applicable)? It doesn't work for me hence the question. Thanks!

nexron171 commented 1 year ago

Thanks for the tip! What versions did you use of GoogleSignIn (and Firebase if applicable)? It doesn't work for me hence the question. Thanks!

GoogleSignIn 6.2.4 firebase-ios-sdk 8.15.0

parse commented 1 year ago

Thanks for the tip! What versions did you use of GoogleSignIn (and Firebase if applicable)? It doesn't work for me hence the question. Thanks!

GoogleSignIn 6.2.4 firebase-ios-sdk 8.15.0

Thanks!

It worked for me with some slight modifications (paths were incorrect for me):

"OTHER_LDFLAGS": SettingValue(stringLiteral: "-ObjC"),
"HEADER_SEARCH_PATHS": SettingValue(stringLiteral: "$(inherited) $(SRCROOT)/Tuist/Dependencies/SwiftPackageManager/.build/checkouts/GoogleSignIn-iOS/GoogleSignIn/Sources/Public $(SRCROOT)/Tuist/Dependencies/SwiftPackageManager/.build/checkouts/AppAuth-iOS/Source/AppAuth $(SRCROOT)/Tuist/Dependencies/SwiftPackageManager/.build/checkouts/AppAuth-iOS/Source/AppAuthCore $(SRCROOT)/Tuist/Dependencies/SwiftPackageManager/.build/checkouts/gtm-session-fetcher/Sources/Core/Public $(SRCROOT)/Tuist/Dependencies/SwiftPackageManager/.build/checkouts/GoogleSignIn-iOS/GoogleSignIn/Sources/../../ $(SRCROOT)/Tuist/Dependencies/SwiftPackageManager/.build/checkouts/GTMAppAuth/GTMAppAuth/Sources/Public/GTMAppAuth"),
kientux commented 1 year ago

Thanks for the tip! What versions did you use of GoogleSignIn (and Firebase if applicable)? It doesn't work for me hence the question. Thanks!

GoogleSignIn 6.2.4 firebase-ios-sdk 8.15.0

Thanks!

It worked for me with some slight modifications (paths were incorrect for me):

"OTHER_LDFLAGS": SettingValue(stringLiteral: "-ObjC"),
"HEADER_SEARCH_PATHS": SettingValue(stringLiteral: "$(inherited) $(SRCROOT)/Tuist/Dependencies/SwiftPackageManager/.build/checkouts/GoogleSignIn-iOS/GoogleSignIn/Sources/Public $(SRCROOT)/Tuist/Dependencies/SwiftPackageManager/.build/checkouts/AppAuth-iOS/Source/AppAuth $(SRCROOT)/Tuist/Dependencies/SwiftPackageManager/.build/checkouts/AppAuth-iOS/Source/AppAuthCore $(SRCROOT)/Tuist/Dependencies/SwiftPackageManager/.build/checkouts/gtm-session-fetcher/Sources/Core/Public $(SRCROOT)/Tuist/Dependencies/SwiftPackageManager/.build/checkouts/GoogleSignIn-iOS/GoogleSignIn/Sources/../../ $(SRCROOT)/Tuist/Dependencies/SwiftPackageManager/.build/checkouts/GTMAppAuth/GTMAppAuth/Sources/Public/GTMAppAuth"),

where do you put this @parse? I'm integrating CocoaLumberjack into my project and it produces error:

DDASLLogCapture.h:16:9 'CocoaLumberjack/DDASLLogger.h' file not found
Could not build Objective-C module 'CocoaLumberjack'

// Oh thanks I figured it out. I put it in the right place but the path is wrong so I cannot build. Fixed it.

mpsmithhatch commented 1 year ago

This appears to be impacting the ability to use 'Airship' through Dependencies.swift as well.

Their Obj-C modules' modulemap file does not appear to be working with the -fmodule-map-file approach so far.

This causes their umbrella headers to not be automatically loaded when importing their modules.

One example: their AirshipMessageCenter module. One of the things those [Airship] umbrella headers do is set up NS_SWIFT namespacing of their ObjC prefixed classnames (i.e. - 'UAMessageCenter' -> 'MessageCenter'). That's a quick way to check if their umbrella headers are being brought in successfully in a sample project that depends on the AirshipMessageCenter product/module. i.e. - in a project, add the Airship dependency to Dependencies.swift, then in some Swift code attempt to import AirshipMessageCenter, then see if you can compile successfully with a reference 'MessageCenter'.

Buju77 commented 1 year ago

@kabirkhaan have you made any progress on this ticket? Because we are also facing this issue. We are also trying to integrate CocoaLumberjack and SSZipArchive (like in #3463)

Buju77 commented 1 year ago

I was able to find a workaround for #3463 for SSZipArchive when used as external Tuist dependency:


let projectSettings: Settings = .settings(
    base: [
        "CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER": "NO",
        "CLANG_ALLOW_NON_MODULAR_INCLUDES_IN_FRAMEWORK_MODULES": "YES",
        "OTHER_SWIFT_FLAGS": "-Xcc -Wno-error=non-modular-include-in-framework-module" // fix SSZipArchive compiler error: https://stackoverflow.com/a/60187291/1146019
)

let targetSettings: Settings = .settings(
    base: [
        "OTHER_LDFLAGS": "-ObjC",
        "HEADER_SEARCH_PATHS": ["$(inherited)",
                                "Tuist/Dependencies/SwiftPackageManager/.build/checkouts/CocoaLumberjack/Sources/**",
                                "Tuist/Dependencies/SwiftPackageManager/.build/checkouts/ZipArchive/**"]
    ]
)

let target = Target(name: "...",
                    dependencies: [.external(name: "ZipArchive")],
                    settings: targetSettings)

let project = Project(name: "...",
                      settings: projectSettings,
                      targets: [target])```
azilbershtein commented 1 year ago

Anyone got a way around this issue on the latest google sign in version?

oleksiyPetlyuk commented 1 year ago

Anyone got a way around this issue on the latest google sign in version?

I'm trying to integrate GoogleSignIn 7.0.0 but there is no luck, tried to pass HEADER_SEARCH_PATHS but still the same errors:

Could not build module 'GTMAppAuth'
Failed to find GTMSessionFetcher
shahzadmajeed commented 1 year ago

Anyone got a way around this issue on the latest google sign in version?

I'm trying to integrate GoogleSignIn 7.0.0 but there is no luck, tried to pass HEADER_SEARCH_PATHS but still the same errors:


Could not build module 'GTMAppAuth'

Failed to find GTMSessionFetcher

The best thing you can do is try to fix errors in generated project and note down those changes (you also check a diff of changes as well) and then try to apply those changes via. Tuist

cleaninglab-ernie commented 1 year ago

AppLayer ---- Module A -------- Firebase (10.10.*) -------- Google Sign In (7.0.0) or (6.2.4) ---- Module B ---- Module C ...

try add build settings at Module A's build settings.

"HEADER_SEARCH_PATHS": [
    "$(inherited)",
    "$(SRCROOT)/../../Tuist/Dependencies/SwiftPackageManager/.build/checkouts/GTMAppAuth/GTMAppAuth/Sources/Public/GTMAppAuth"
    "$(SRCROOT)/../../Tuist/Dependencies/SwiftPackageManager/.build/checkouts/gtm-session-fetcher/Sources/Core/Public"
],
"OTHER_LDFLAGS" : "-ObjC"

in my case, I solved. pay attention to the file path (public header path).

pepicrft commented 1 year ago

Hey folks 👋🏼. I started looking into this one. It seems that some build settings are missing in the projects in which dependencies are integrated and that causes the compilation errors that you are all seeing. I'll use all the dependencies mentioned in this thread to reproduce this issue. Please, if you had issues with other dependencies, I'd appreciate if you could share the name with me.

PhilippeWeidmann commented 1 year ago

Hi, that's great news ! Here is some dependencies that had issues: Realm https://github.com/realm/realm-swift Sentry https://github.com/getsentry/sentry-cocoa

danieleformichelli commented 1 year ago

Also CleverTapSDK: https://github.com/tuist/tuist/issues/4436

shurale85 commented 1 year ago

Hi! Cocoalumberjack generates "Could not build Object-module error" when it is installed as external dependency. Installing it as a project package goes fine

denysco commented 11 months ago

Hi, that's great news ! Here is some dependencies that had issues: Realm https://github.com/realm/realm-swift Sentry https://github.com/getsentry/sentry-cocoa

Hey, setting "USE_HEADERMAP": "YES" in targetSettings fixed errors in Sentry 8.10.0

nrurnru commented 10 months ago

https://github.com/amplitude/Amplitude-iOS has Objective-C target, but it works without doing something.

azilbershtein commented 10 months ago

https://github.com/BranchMetrics/ios-branch-sdk-spm Same issue

yongbeomkwak commented 10 months ago

Does anyone get any errors about GoogleSignIn

I keep getting this error in my SignIn target not even i attach code about below code

"HEADER_SEARCH_PATHS": [
    "$(inherited)",
    "$(SRCROOT)/../../Tuist/Dependencies/SwiftPackageManager/.build/checkouts/GTMAppAuth/GTMAppAuth/Sources/Public/GTMAppAuth"
    "$(SRCROOT)/../../Tuist/Dependencies/SwiftPackageManager/.build/checkouts/gtm-session-fetcher/Sources/Core/Public"
],
"OTHER_LDFLAGS" : "-ObjC"
Could not build module 'GTMAppAuth'

Failed to find GTMSessionFetcher
azilbershtein commented 10 months ago

Does anyone get any errors about GoogleSignIn

I keep getting this error in my SignIn target not even i attach code about below code

"HEADER_SEARCH_PATHS": [
    "$(inherited)",
    "$(SRCROOT)/../../Tuist/Dependencies/SwiftPackageManager/.build/checkouts/GTMAppAuth/GTMAppAuth/Sources/Public/GTMAppAuth"
    "$(SRCROOT)/../../Tuist/Dependencies/SwiftPackageManager/.build/checkouts/gtm-session-fetcher/Sources/Core/Public"
],
"OTHER_LDFLAGS" : "-ObjC"
Could not build module 'GTMAppAuth'

Failed to find GTMSessionFetcher

Do you add it to the module target itself? not for the GoogleSignIn SDK?

shahzadmajeed commented 10 months ago

"OTHER_LDFLAGS" : "-ObjC" is added to the target that depends on GoogleSignIn and the header search paths, module maps, define module settings are added in objective-c target itself i.e GoogleSignIn

azilbershtein commented 10 months ago

Try to add the header search paths to the target who depends on GoogleSignIn, that works for me

pepicrft commented 8 months ago

You can pledge behind and help support this effort using Polar.sh

Fund with Polar

adam-govan commented 7 months ago

https://github.com/amplitude/Amplitude-iOS has Objective-C target, but it works without doing something.

I don't know if it's relevant but it looks like the publicHeadersPath is defined in Amplitude-iOS's Package.swift file, Whereas AppAuth is left at "". I've tried importing both and Amplitude does have it's headers?

how to use that info? I'm not sure yet, just thought it might be something to share in case anyone else is still looking too.

cadaniel commented 7 months ago

I'm having this problem as well with Sentry.

[REDACTED]/Tuist/Dependencies/SwiftPackageManager/.build/checkouts/sentry-cocoa/Sources/Sentry/Public/SentryDefines.h:104:28 Redefinition of 'SentryLogLevel'

I've tried various offered workaround offered here, with full details HERE

I was able to work around it by finding someone that offers a pre-compiled XCFramework found HERE

Using this package has it working for now, but would love to move back to the official repository.

zkhCreator commented 7 months ago

In my project, which based on the basic tuist init project. I setup settings in my private static func makeFrameworkTargets(name: String, destinations: Destinations) -> [Target] {} function.

I use GoogleSignIn in my UI Target.

here are my code in Project+Templates.swift:

    private static func makeFrameworkTargets(name: String, destinations: Destinations) -> [Target] {
        // ... other config
        var settings: Settings? = .settings(
            base: [
                "OTHER_LDFLAGS": "-ObjC",
                "HEADER_SEARCH_PATHS": ["$(inherited)",
                                        "$(SRCROOT)/Tuist/Dependencies/SwiftPackageManager/.build/checkouts/GTMAppAuth/GTMAppAuth/Sources/Public/GTMAppAuth",
                                        "$(SRCROOT)/Tuist/Dependencies/SwiftPackageManager/.build/checkouts/gtm-session-fetcher/Sources/Core/Public"]
            ]
        )

        let sources = Target(name: name,
                             destinations: destinations,
                             product: .framework,
                             bundleId: "bundle.\(name)",
                             infoPlist: .default,
                             sources: ["Targets/\(name)/Sources/**"],
                             resources: resources,
                             dependencies: dependencies, settings: settings)
        let tests = Target(name: "\(name)Tests",
                           destinations: destinations,
                           product: .unitTests,
                           bundleId: "bundle.\(name)Tests",
                           infoPlist: .default,
                           sources: ["Targets/\(name)/Tests/**"],
                           resources: [],
                           dependencies: [.target(name: name)])
        return [sources, tests]
    }

after that check the path of HEADER_SEARCH_PATHS are correct in the target Build Settings:

image
fortmarek commented 7 months ago

Hey, folks 👋

This PR should finally fix this issue: https://github.com/tuist/tuist/pull/5887

If there's any complex dependency that you'd like us to try out, let us know. We hope to include the fix in the next tuist release.