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

Deduplicate pod targets #3550

Merged
merged 55 commits into from
Jun 24, 2015
Merged

Deduplicate pod targets #3550

merged 55 commits into from
Jun 24, 2015

Conversation

mrackwitz
Copy link
Member

This de-duplicates pod targets in the analyzer, if they share the same subspec set and target the same platform. If there is any dependency from at least one target definition, which doesn't fulfill this condition, then all dependencies with this root spec will be scoped.
This should fix diverse issues related to archiving with Xcode, especially when extensions are integrated. Beside that it will reduce build times, drastically.
A new configuration key was added (38ec3cd), which allows to opt-out of the feature, because these changes cause massive changes to the existing integration and are likely to break edge-case setups.

Open Todos:

The following things are still open to do:

  • A PodTarget can't be deduplicated if any of its transitive dependencies can't be deduplicated.
  • Validate that PodTargets are only initialized legitimate: They can't be scoped when they have more then one target definition.
  • Add missing unit tests
  • Decide whether to add integration tests with the old behavior
  • Tests & experience with real-life projects
  • Add a CHANGELOG entry

Dependencies:

This depends on the following PRs:

Changes to the Integration

Deduplication / Descoping of PodTargets

This de-duplicates pod targets in the analyzer, if they share the same subspec set and target the same platform. If there is any dependency from at least one target definition, which doesn't fulfill this condition, then all dependencies with this root spec will be scoped.
A deduplicated pod target will be fully descoped, which means the prefix of all names is skipped and with frameworks, it builds in the CONTENTS_FOLDER_PATH as usual given by Xcode, without the introduced level of further scoping to avoid conflicts, which can't happen that way.
Concrete this means the following: If you used to integrate with the following Podfile:

use_frameworks!
platform :ios, "8.1"
pod "Alamofire"
pod "Stargate"
pod "MRProgress/Circular"

target "ExampleCLI" do
  platform :osx, "10.10"
  pod "Alamofire"
end

target "ExampleWatchExtension" do
  pod "Stargate"
end

target "ExampleWidget" do
  pod "Alamofire"
  pod "MRProgress/ActivityIndicator"
  pod "MRProgress/Circular"
end

You would end up with an integration, which looks like:

  • Pods-Alamofire
  • Pods-Stargate
  • Pods-MRProgress
  • Pods-ExampleCLI-Alamofire
  • Pods-ExampleWatchExtension-Stargate
  • Pods-ExampleWidget-Alamofire
  • Pods-ExampleWidget-MRProgress

Now you should end up with:

  • Pods-Alamofire
  • Stargate 🎉
  • Pods-MRProgress
  • Pods-ExampleCLI-Alamofire
  • Pods-ExampleWatchExtension-Stargate 🎉
  • Pods-ExampleWidget-Alamofire
  • Pods-ExampleWidget-MRProgress

As you can see subspecs and different platforms prevent the deduplication, even when there is a subset of target definitions where the deduplication could been applied as seen for Alamofire in the example above.

Environment Header

All PodTargets from the same AggregateTarget used to share an environment header. This won't be possible anymore for deduplicated pod targets. As the environment header defines precompiler macros to indicate the version for each integrated dependency in the same target definition, altering anything around this can cause unexpected failures when pods have implicit dependencies or offer additional behavior when used along other pods. This PR for now replaces the shared environment header with a dedicated environment header, which in the best case should only have information about all explicit dependencies of the given podspec. Another variant would be that it has additional access to the intersection of all dependencies of all target definitions it belongs to.

Highlighted Architecture Changes

  • Pull down relation target_definition from Target into AggregateTarget and all getters, which depend on this and are only needed for the subclass.
  • Pluralize prior singular inherited relation from PodTarget to TargetDefinition, now known as PodTarget#target_definitions
  • Add a flag attribute scoped to PodTarget to indicate them explicitly as scoped, which is only possible if there is only one related TargetDefinition
  • Extract TargetInspector and TargetInspectionResult out of Analyzer

@segiddins
Copy link
Member

❤️

@neonichu
Copy link
Member

✨ 💖 ✨

@neonichu
Copy link
Member

Tested with the example of https://github.com/contentful-labs/Stargate:

  • Seemingly, warning settings are different between iPhone and WatchKit extension targets, so I get this a lot when installing:
[!] The pod `MMWormhole` is linked to different targets (["Pods-StargateExample", "Pods-StargateExample WatchKit Extension"]), which contain different settings to inhibit warnings. CocoaPods does not currently support different settings and will fall back to your preference set in the root target definition.
  • The OS X target is no longer buildable, somehow i386 ends up in the supported architectures, which isn't supported by Swift and also it becomes the default to build. Maybe related:
2015-05-11 08:43:05.125 xcodebuild[15650:1427685] stream error: stream error at offset 29: created by an unsupported XCDependencyGraph build
2015-05-11 08:43:05.148 xcodebuild[15650:1427686] stream error: stream error at offset 29: created by an unsupported XCDependencyGraph build

in the xcodebuild output.

  • In the shared Pods, the source files are seemingly added twice.

installer.aggregate_targets.map(&:pod_targets).flatten.uniq.each do |lib|
lib.target_definitions.each do |target_definition|
pod_names = [lib.root_spec.name]
pod_reps = pods.select { |rep| pod_names.include?(rep.name) }
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

check with == ?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

@yonaskolb
Copy link

This is great! Been wondering about this

@ItsTipTop
Copy link

Is it fixed now?
I just can't archive my app

@mrackwitz
Copy link
Member Author

@ItsTipTop: Are you using the latest released CocoaPods? That doesn't include those changes yet. Or are you using a development installation? Then you could help if you would describe what exactly fails when you try to archive your app.

@ItsTipTop
Copy link

@mrackwitz I'm using 0.37.1 version.
When I run my app on device/simulator all works fine. But when I try to archive my app I recieve warnings. Xcode successfully archived my application, but the Archives Organizer does not list my archive. My issue very similar to @andreamazz in #3200

2015-05-23 13 04 30

Umbrella header for module 'UICKeyChainStore' does not include header 'Pods-TJShareExtension-UICKeyChainStore-umbrella.h'
Umbrella header for module 'pop' does not include header 'Pods-TJPostKit-pop-umbrella.h'
Umbrella header for module 'UIImage_Categories' does not include header 'Pods-TJPostKit-UIImage-Categories-umbrella.h'

Warnings appear only for frameworks that included in several targets.

Here my podfile:

source 'https://github.com/CocoaPods/Specs.git'

platform :ios, '8.0'
inhibit_all_warnings!
use_frameworks!

   target 'AppTarget' do
       link_with 'AppTargetTests'
       pod 'pop', '~> 1.0'
       pod 'UIImage-Categories'
       pod 'UICKeyChainStore'
       pod 'AFNetworking'
    end

target 'AppPostKit' do
       pod 'pop', '~> 1.0'
       pod 'UIImage-Categories'
end

target 'AppShareExtension' do
    pod 'UICKeyChainStore'
    pod 'AFNetworking'
end

Now i can't archive my app and send update to App Store :(

Waiting for your reply. Thank you.

@mrackwitz
Copy link
Member Author

@neonichu: I could address the following issues:

  • Seemingly, warning settings are different between iPhone and WatchKit extension targets, so I get this a lot when installing:
[!] The pod `MMWormhole` is linked to different targets (["Pods-StargateExample", "Pods-StargateExample WatchKit Extension"]), which contain different settings to inhibit warnings. CocoaPods does not currently support different settings and will fall back to your preference set in the root target definition.
  • In the shared Pods, the source files are seemingly added twice.

Left open is:

  • The OS X target is no longer buildable, somehow i386 ends up in the supported architectures, which isn't supported by Swift and also it becomes the default to build.

@mrackwitz
Copy link
Member Author

@ItsTipTop: Thanks for your detailed issue report, really sorry that this tooling issue prevents you from shipping your app to the app store. But unless you have feedback to a development installation with this branch, this really shouldn't go in this PR, which tries to address the issues which are linked, which also apply for your specific issue. This makes it easier for us to work focused on an actual solution. Meanwhile, did you tried the workaround here? Hope that this will work for you.

@ItsTipTop
Copy link

@mrackwitz No. This workaround does not work for me.

@kexoth
Copy link

kexoth commented May 26, 2015

I'm having the same issue, I cannot archive my app & tried everything available on the Internet, but never suspected this is the issue until I found this on the Xcode Console:

5/26/15 8:25:43.730 PM Xcode[88529]: Cannot make directory /Users/<my-username>/Library/Developer/Xcode/Archives/2015-05-26/App 5-26-15, 8.25 PM.xcarchive/dSYMs/Realm.framework.dSYM: File exists

Then I tried everything regarding the issue available here but with no success, here's my Podfile:

    platform :ios, '8.0'
    use_frameworks!
    pod 'RealmSwift', '~> 0.92'
    link_with 'AppToday', 'App WatchKit Extension'

    target 'App', :exclusive => true do
        pod 'RealmSwift', '~> 0.92'
        pod 'R.swift', '~> 0.7'
        pod 'NGAParallaxMotion', '~> 1.1.0'
    end

    post_install do |installer_representation|
        installer_representation.project.targets.each do |target|
            target.build_configurations.each do |config|

                config.build_settings['ONLY_ACTIVE_ARCH'] = 'NO'

                if !config.to_s.include? 'Debug'
                    config.build_settings['CODE_SIGN_IDENTITY[sdk=iphoneos*]'] = 'iPhone Distribution'
                end
            end
        end
    end

This way I get the duplicate thing & the app doesn't archive, although every target gets the dependancies it needs.

On the other hand with an approach for having a shared pod for every target & a dedicated pod for the iOS app in order to avoid the duplication, I changed the Podfile:

    platform :ios, '8.0'
    use_frameworks!
    pod 'RealmSwift', '~> 0.92'
    link_with 'App', 'AppToday', 'App WatchKit Extension'

    target 'App' do
        pod 'R.swift', '~> 0.7'
        pod 'NGAParallaxMotion', '~> 1.1.0'
    end

    post_install do |installer_representation|
        installer_representation.project.targets.each do |target|
            target.build_configurations.each do |config|

                config.build_settings['ONLY_ACTIVE_ARCH'] = 'NO'

                if !config.to_s.include? 'Debug'
                    config.build_settings['CODE_SIGN_IDENTITY[sdk=iphoneos*]'] = 'iPhone Distribution'
                end
            end
        end
    end

This way I get this error when installing the pods & I don't get the Pods-App.xcconfig with the App's specific dependancies:

[!] CocoaPods did not set the base configuration of your project because your project already has a custom config set. In order for CocoaPods integration to work at all, please either set the base configurations of the target `App` to `Pods/Target Support Files/Pods-App/Pods-App.debug.xcconfig` or include the `Pods/Target Support Files/Pods-App/Pods-App.debug.xcconfig` in your build configuration.

We should wait for this patch to fix the duplication issue in a Cocoapods update?
What should be the right approach for this in the future between these 2?

I'm waiting for your reply,
Thanks,
@kexoth

@segiddins segiddins added this to the 0.38 milestone May 29, 2015
@segiddins segiddins mentioned this pull request Jun 2, 2015
@mrackwitz mrackwitz force-pushed the mr-deduplicate-frameworks branch 2 times, most recently from 46b5652 to 1a98fb2 Compare June 3, 2015 13:19
For disambugiation with stdlib's debugging helper #inspect.
Remove the "Pods-" prefix from the labels in the expected error messages, because the fixture targets are deduplicated.
mrackwitz added a commit that referenced this pull request Jun 24, 2015
@mrackwitz mrackwitz merged commit 4c1f4a8 into master Jun 24, 2015
@mrackwitz mrackwitz deleted the mr-deduplicate-frameworks branch June 24, 2015 22:34
@neonichu
Copy link
Member

🎉 🍷 🍻

@supermarin
Copy link

@k0nserv
Copy link
Member

k0nserv commented Jun 25, 2015

Great job

@sebromero
Copy link

Great job! Already any plans for releasing it?

@ItsTipTop
Copy link

Great! I installed 0.38.0.beta.1 and Xcode successfully archived my application. But when i'm trying to submit my app to App Store xcode fail:
"No matching provisioning profiles found for.." app and Extension too.

@tanis2000
Copy link

What if you're using the same Pod like AFNetworking both in the application and the widget? You need to be able to set AF_APP_EXTENSIONS=1 in GCC_PREPROCESSOR_DEFINITIONS for the widget but you cannot do it as long as the targets get deduplicated. And since we're a team working on the same project it's not reasonable to force everyone to remember to add something to the config.yml
Is there any other way around this issue?

@neonichu
Copy link
Member

neonichu commented Aug 3, 2015

See discussion in #3794

100mango referenced this pull request in m1entus/MZFormSheetPresentationController Apr 7, 2016
[Some APIs Are Unavailable to App
Extensions](https://developer.apple.com/library/ios/documentation/Genera
l/Conceptual/ExtensibilityPG/ExtensionOverview.html#//apple_ref/doc/uid/
TP40014214-CH2-SW6)

According To Apple Doc:

> Because of its focused role in the system, an app extension is
ineligible to participate in certain activities. An app extension
cannot:

> Access a sharedApplication object, and so cannot use any of the
methods on that object

Test in my own app.We need the `NS_EXTENSION_UNAVAILABLE_IOS` make it
available in App extension
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet