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

Set BUILD_LIBRARY_FOR_DISTRIBUTION when the user target has it set #9232

Closed
1 task done
juanjonol opened this issue Oct 9, 2019 · 21 comments
Closed
1 task done

Set BUILD_LIBRARY_FOR_DISTRIBUTION when the user target has it set #9232

juanjonol opened this issue Oct 9, 2019 · 21 comments
Labels
s7:workaround available A workaround for the issue is available
Milestone

Comments

@juanjonol
Copy link
Contributor

Report

When using a project that has the BUILD_LIBRARY_FOR_DISTRIBUTION set to YES, the Pods included should have this setting applied too, to avoid the warnings "Module '<pod_name>' was not compiled with library evolution support; using it means binary compatibility for '<project>' can't be guaranteed..

This is the same that was done for the APPLICATION_EXTENSION_API_ONLY Build Setting in #4321.

Related to #9148, as the BUILD_LIBRARY_FOR_DISTRIBUTION setting is needed for XCFrameworks.

What did you do?

  1. Create a new project.
  2. Set BUILD_LIBRARY_FOR_DISTRIBUTION to YES.
  3. Use pod install to install any pod.

What did you expect to happen?

The pods should be installed with the BUILD_LIBRARY_FOR_DISTRIBUTION set to YES.

What happened instead?

The pods are installed with the default value of BUILD_LIBRARY_FOR_DISTRIBUTION (NO) and a warning is generated for every pod.

Workaround

Use a post_install hook to set BUILD_LIBRARY_FOR_DISTRIBUTION to YES in all pods.

@dnkoutso
Copy link
Contributor

@juanjonol want to make a PR for it?

@dnkoutso dnkoutso added this to the 1.9.0 milestone Oct 29, 2019
@dnkoutso dnkoutso added the s7:workaround available A workaround for the issue is available label Dec 4, 2019
@dnkoutso
Copy link
Contributor

dnkoutso commented Dec 4, 2019

@juanjonol can you provide a bit more information under what conditions should BUILD_LIBRARY_FOR_DISTRIBUTION be set for the pods of the target? Is it when the target is a library? Does it matter? Is it only for extensions?

@tamastimar
Copy link

@dnkoutso
Dependencies of .xcframeworks must be built with BUILD_LIBRARY_FOR_DISTRIBUTION enabled.

So if your target is a library with that flag set, then the pods should be installed that way. Otherwise you’ll get dyld: Symbol not found error in runtime.

Another use case to think of: you have binary pod vendoring an .xcframework. Its (Swift) dependencies should be installed with the flag set to YES also. It may be covered in #9334.

@dnkoutso
Copy link
Contributor

dnkoutso commented Dec 9, 2019

Another use case to think of: you have binary pod vendoring an .xcframework. Its (Swift) dependencies should be installed with the flag set to YES also. It may be covered in #9334.

@amorde is this covered you think from #9334?

@juanjonol
Copy link
Contributor Author

First, sorry for taking this long to respond.

@juanjonol can you provide a bit more information under what conditions should BUILD_LIBRARY_FOR_DISTRIBUTION be set for the pods of the target? Is it when the target is a library? Does it matter? Is it only for extensions?

What @tamastimar has said is correct:

  1. To create an XCFramework, the BUILD_LIBRARY_FOR_DISTRIBUTION setting must be yes. If the XCFramework's (not-vendored) dependencies are integrated using CocoaPods, that setting is not applied automatically to those dependencies, so there are warnings at compilation time and there can be errors at runtime. So for now, everyone that is creating an XCFramework and using CocoaPods needs to create a post_install hook to apply that setting to its dependencies.
  2. When an XCFramework is distributed as a vendored library and integrated in another library or application using CocoaPods, the XCFramework's dependencies should also have BUILD_LIBRARY_FOR_DISTRIBUTION set to yes, to avoid the same errors as before.
  3. Finally, although BUILD_LIBRARY_FOR_DISTRIBUTION doesn't do anything to applications per se (as far as I know), it makes sense that, if present, it means that the developer intends to compile all its dependencies with that setting (even if there isn't any XCFramework currently in use or if the XCFrameworks are integrated without using CocoaPods).

The simplest way I can think of solving this is just looking up if BUILD_LIBRARY_FOR_DISTRIBUTION is set in a target, and then apply that setting to all its dependencies. With this, the first and third case are covered, and in the second case the developer just needs to add that setting to the target to solve the warnings.

I hope this makes sense.

I don't see this covered in #9334, but I haven't looked it closely.

@juanjonol want to make a PR for it?

I didn't have time to do it, but if @amorde confirms that this is not covered in #9334 I'll look into it this week.

@amorde
Copy link
Member

amorde commented Dec 10, 2019

#9334 doesn't cover this, that was only for supporting vendored XCFrameworks.

This sounds like a reasonable change. To confirm, this is for the case where CocoaPods is being used to pull in dependencies for a framework or static library, correct?

target 'MyFramework' do
...
end

@dnkoutso
Copy link
Contributor

To confirm, this is for the case where CocoaPods is being used to pull in dependencies for a framework or static library, correct?

Yes I want to know the answer to this too and that is what I mean in my original question. I want us to be explicit on when this logic should run, for what type of targets etc.

@tamastimar
Copy link

Another use case to think of: you have binary pod vendoring an .xcframework. Its (Swift) dependencies should be installed with the flag set to YES also. It may be covered in #9334.

@amorde is this covered you think from #9334?

#9334 doesn't cover this, that was only for supporting vendored XCFrameworks.

This sounds like a reasonable change. To confirm, this is for the case where CocoaPods is being used to pull in dependencies for a framework or static library, correct?

I've run through #9334 and I can't see that BUILD_LIBRARY_FOR_DISTRIBUTION is used/touched anywhere. So I think there are two issues and they are mixed here.

Should I open another issue for the case of dependencies of vendored XCFrameworks?

@juanjonol
Copy link
Contributor Author

#9334 doesn't cover this, that was only for supporting vendored XCFrameworks.

This sounds like a reasonable change. To confirm, this is for the case where CocoaPods is being used to pull in dependencies for a framework or static library, correct?

Sorry for not being clear enough. This is to pull dependencies for a framework, but that can be done when the target is the framework itself or when the target is an app that embeds that framework. For example, given a framework with this podfile:

target 'MyXCFramework' do
    pod 'MyXCFrameworkDependency'
end

That MyXCFrameworkDependency should have BUILD_LIBRARY_FOR_DISTRIBUTION set to YES.

But also, if an application uses MyXCFramework, its dependencies should have that setting applied too. For example, in an app with this podfile:

target 'MyApp' do
    pod 'MyXCFramework'
end

Because MyXCFramework is an XCFramework, BUILD_LIBRARY_FOR_DISTRIBUTION should be set for its dependencies (MyXCFrameworkDependency).

Finally, if an app has BUILD_LIBRARY_FOR_DISTRIBUTION set to YES (for example, because MyXCFramework is added manually, without CocoaPods), all its dependencies should inherit BUILD_LIBRARY_FOR_DISTRIBUTION from MyApp. So, in an app with this podfile:

target 'MyApp' do
    pod 'MyXCFrameworkDependency'
end

Although MyApp doesn't use an XCFramework as far as CocoaPods knows, if it has BUILD_LIBRARY_FOR_DISTRIBUTION manually set to YES, MyXCFrameworkDependency should have it set to YES too. This is the same that was done with APPLICATION_EXTENSION_API_ONLY in #4321.

@amorde
Copy link
Member

amorde commented Dec 12, 2019

Yep, propagating this from the user target to its dependencies sounds reasonable to me. One thing that's a bit confusing here is the mention of XCFramework targets - Xcode doesn't support .xcframeworks as a product type as far as I'm aware. So this is really for framework targets right?

Regarding the mention of dependencies of the xcframework itself, there's no way for CocoaPods to know what dependencies are used by the vendored xcframework vs. the pod itself.

For example, let's say I have FrameworkXYZ which depends on BananaLib. When developing the framework, we could do something like this:

target 'FrameworkXYZ' do
  # FrameworkXYZ has `BUILD_LIBRARY_FOR_DISTRIBUTION` set to 'YES',
  # so we will apply that to BananaLib as well
  pod 'BananaLib'
end

Shipping this inside of a pod might look like this:

Pod::Spec.new do |s|
  s.name = 'AwesomeLib' 
  s.source_files = 'Source/**/*.swift'
  # ...
  s.vendored_framework 'FrameworkXYZ.xcframework'
  # Should CocoaPods set `BUILD_LIBRARY_FOR_DISTRIBUTION=YES` to 'BananaLib'?
  s.dependency 'BananaLib'
end

CocoaPods doesn't know if BananaLib is used in the source code of AwesomeLib, or at runtime by FrameworkXYZ, or both. So it doesn't make sense to automatically apply BUILD_LIBRARY_FOR_DISTRIBUTION to BananaLib since the consumer of AwesomeLib might not need that. This is especially true if BananaLib is also a dependency of some completely unrelated pod that happens to be included by the user target.

Totally possible I'm misunderstanding something here so please correct me if that's the case

@tamastimar
Copy link

tamastimar commented Dec 12, 2019

So it doesn't make sense to automatically apply BUILD_LIBRARY_FOR_DISTRIBUTION to BananaLib since the consumer of AwesomeLib might not need that. This is especially true if BananaLib is also a dependency of some completely unrelated pod that happens to be included by the user target.

If a vendored XCFramework is present, would automatic application have any drawbacks ? Why does it matter if BananaLib maybe a dependency of others too?

@juanjonol
Copy link
Contributor Author

Yep, propagating this from the user target to its dependencies sounds reasonable to me. One thing that's a bit confusing here is the mention of XCFramework targets - Xcode doesn't support .xcframeworks as a product type as far as I'm aware. So this is really for framework targets right?

That's right, Xcode doesn't generate XCFrameworks directly, they must be created with xcodebuild. They are framework targets indeed. When I talk about XCFramework targets I mean a framework target with BUILD_LIBRARY_FOR_DISTRIBUTION=YES.

Regarding the mention of dependencies of the xcframework itself, there's no way for CocoaPods to know what dependencies are used by the vendored xcframework vs. the pod itself.

That's correct too.

For example, let's say I have FrameworkXYZ which depends on BananaLib. (..) Shipping this inside of a pod might look like this:

Pod::Spec.new do |s|
  s.name = 'AwesomeLib' 
  s.source_files = 'Source/**/*.swift'
  # ...
  s.vendored_framework 'FrameworkXYZ.xcframework'
  # Should CocoaPods set `BUILD_LIBRARY_FOR_DISTRIBUTION=YES` to 'BananaLib'?
  s.dependency 'BananaLib'
end

CocoaPods doesn't know if BananaLib is used in the source code of AwesomeLib, or at runtime by FrameworkXYZ, or both. So it doesn't make sense to automatically apply BUILD_LIBRARY_FOR_DISTRIBUTION to BananaLib since the consumer of AwesomeLib might not need that. This is especially true if BananaLib is also a dependency of some completely unrelated pod that happens to be included by the user target.

Totally possible I'm misunderstanding something here so please correct me if that's the case

I think you're understanding this correctly. This is why I think that, if BUILD_LIBRARY_FOR_DISTRIBUTION is set in a target (whatever that target is a framework, an static library or an application) it should be applied to all its dependencies. This way, BananaLib's developer can choose to set BUILD_LIBRARY_FOR_DISTRIBUTION in its target (automatically applying that setting to all its dependencies) or if there's some problem just set BUILD_LIBRARY_FOR_DISTRIBUTION to the dependencies that need it with a post_install hook (although that's not the best UX...), and the users of BananaLib can do the same. This is the same that was done in APPLICATION_EXTENSION_API_ONLY in #4321: whatever the target is, if that setting is present it is applied to all its dependencies.

Would automatic application have any drawbacks if a vendored XCFramework is present? Why does it matter if BananaLib maybe a dependency of others too?

That's a good question. Maybe even BUILD_LIBRARY_FOR_DISTRIBUTION can be always set by default in CocoaPods (pods are libraries being distributed I guess?), but I think that would need extensive testing (Xcode don't apply this by default, so it probably means it can break some libraries...). I think detecting if BUILD_LIBRARY_FOR_DISTRIBUTION is present in a target and then applying it to all its dependencies its the safer path for now.

@tamastimar
Copy link

@amorde After trying 1.9.0.beta.1, I can confirm that the flag is missed by the dependent pods and it causes crash at app launch.

Besides that it integrates the XCFramework nicely. 👍 Thanks for your work!

@tgoyne
Copy link
Contributor

tgoyne commented Dec 17, 2019

pods are libraries being distributed I guess?

Not in the way that BUILD_LIBRARY_FOR_DISTRIBUTION is for. Swift Library Evolution makes it so that you can link an application against one version of a Swift library, then upgrade the library to a newer version without rebuilding the application. This is very important for system libraries, but it's not really applicable to cocoapods.

Enabling BUILD_LIBRARY_FOR_DISTRIBUTION by default would just make everything a bit slower for no clear benefit, and it changes how the swift/obj-c runtime integration works.

@amorde
Copy link
Member

amorde commented Dec 17, 2019

Enabling BUILD_LIBRARY_FOR_DISTRIBUTION by default would just make everything a bit slower for no clear benefit, and it changes how the swift/obj-c runtime integration works.

Yes, this is why I'd like to avoid applying that setting by default.

I think we could do something like propagate it to dependencies if its in the podspec:

s.pod_target_xcconfig = {
  # this could theoretically apply to any `s.dependency` as well
  'BUILD_LIBRARY_FOR_DISTRIBUTION' => 'YES'
}

However, it might make more sense to build out Podspec DSL for this. In the meantime pod consumers can use a post_install hook to apply that build setting as needed.

@dnkoutso
Copy link
Contributor

Starting to think that it seems kinda hard to converge on simple concrete cases in which this will always apply therefore I am inclined to just let that be managed by post install hooks.

The most concrete case is to do what we did for APPLICATION_EXTENSION_API_ONLY...

What do folks think?

@tamastimar
Copy link

tamastimar commented Dec 20, 2019

The most concrete case is to do what we did for APPLICATION_EXTENSION_API_ONLY...

What do folks think?

Would it work if the flag comes from a Podspec? I.e.,

spec.user_target_xcconfig = { 'BUILD_LIBRARY_FOR_DISTRIBUTION' => 'YES' }

@juanjonol
Copy link
Contributor Author

pods are libraries being distributed I guess?

Not in the way that BUILD_LIBRARY_FOR_DISTRIBUTION is for. Swift Library Evolution makes it so that you can link an application against one version of a Swift library, then upgrade the library to a newer version without rebuilding the application. This is very important for system libraries, but it's not really applicable to cocoapods.

Enabling BUILD_LIBRARY_FOR_DISTRIBUTION by default would just make everything a bit slower for no clear benefit, and it changes how the swift/obj-c runtime integration works.

Thank you for the clarification. This is important for vendored frameworks too, but I agree this shouldn't be enabled by default.

The most concrete case is to do what we did for APPLICATION_EXTENSION_API_ONLY...

What do folks think?

I agree. Like APPLICATION_EXTENSION_API_ONLY, if the user's target has BUILD_LIBRARY_FOR_DISTRIBUTION, it'd be applied to all its dependencies, and the user can opt-out of this behavior removing that setting or with a post_install hook on a dependency-by-dependency basis. And in the future, a Podspec DSL can be added like @amorde says, to automatically apply this setting only to the dependencies of a concrete pod.

Would it work if the flag comes from a Podspec? I.e.,

spec.user_target_xcconfig = { 'BUILD_LIBRARY_FOR_DISTRIBUTION' => 'YES' }

This would work too.

@vg-identance
Copy link

Could anyone help me with this?
So. I building a binary library (based on Dynamic Framework) = L which also is a pod.
It have dependency resolved via pod = D
And finally exist external (not controlled) applications = A1, A2, etc which should use L

At first L have BUILD_LIBRARY_FOR_DISTRIBUTION set to YES. Because of that its podfile which include D have post_install hook to set D's BUILD_LIBRARY_FOR_DISTRIBUTION to YES too.
L distributed via pods so it's podspec have dependency to set to D
The problem is that when L integrated into host application A the BUILD_LIBRARY_FOR_DISTRIBUTION of D is set to default NO and application crashes. I can't control A so can't update podfile of A with post_install hook. I have control only of L's podspec.

So how I could set BUILD_LIBRARY_FOR_DISTRIBUTION to YES of D target in Pods project of A via podspec of L?

@Alex-Anyvision
Copy link

Could anyone help me with this?
So. I building a binary library (based on Dynamic Framework) = L which also is a pod.
It have dependency resolved via pod = D
And finally exist external (not controlled) applications = A1, A2, etc which should use L

At first L have BUILD_LIBRARY_FOR_DISTRIBUTION set to YES. Because of that its podfile which include D have post_install hook to set D's BUILD_LIBRARY_FOR_DISTRIBUTION to YES too.
L distributed via pods so it's podspec have dependency to set to D
The problem is that when L integrated into host application A the BUILD_LIBRARY_FOR_DISTRIBUTION of D is set to default NO and application crashes. I can't control A so can't update podfile of A with post_install hook. I have control only of L's podspec.

So how I could set BUILD_LIBRARY_FOR_DISTRIBUTION to YES of D target in Pods project of A via podspec of L?

Have you solved the problem? Seems like you should use post_install hook in A to set BUILD_LIBRARY_FOR_DISTRIBUTION to YES for all dependencies (both L and D)

@vg-identance
Copy link

@Alex-Anyvision nope. Currently I use post_install hook as you mentioned but this is not best choice because A is not controlled by me so it could be forgot to set which lead to run time crashes.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
s7:workaround available A workaround for the issue is available
Projects
None yet
Development

No branches or pull requests

7 participants