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

Xcode 10 new build system makes asset catalog invalid specified by podspec'sresource(s) #8122

Open
1 task done
yam-liu opened this issue Sep 22, 2018 · 83 comments
Open
1 task done
Labels
r:new build system Issues related to Xcode's new build system introduced in Xcode 9 s7:workaround available A workaround for the issue is available
Milestone

Comments

@yam-liu
Copy link

yam-liu commented Sep 22, 2018

Report

What did you do?

ℹ Please replace these two lines with what you did.
Xcode 10 new build system, CocoaPods 1.5.3.
Private pod (named Pod1), podspec: s.resources = ['Pod1/Assets/*'], this folder includes a asset catalog(images.xcassets).
Pod install and compile

What did you expect to happen?

image in Pod1's images.xcassets do not show in UI by using -[UIImage imageNamed:] with new build system

What happened instead?

  • images not show.
  • images in pod's images.assets not in Asset.car in .app
  • build log:
Showing All Messages
:-1: ignoring duplicated output file: '/Users/ooops/Library/Developer/Xcode/DerivedData/Example-aavrswxzmgwwppbxcwetebxamdir/Build/Products/Debug-iphonesimulator/Example.app/Assets.car' (in target 'Example')

CocoaPods Environment

Stack

   CocoaPods : 1.5.3
        Ruby : ruby 2.2.7p470 (2017-03-28 revision 58194) [x86_64-darwin17]
    RubyGems : 2.6.14
        Host : Mac OS X 10.14 (18A384a)
       Xcode : 10.0 (10A255)
         Git : git version 2.19.0
Ruby lib dir : /Users/ooops/.rvm/rubies/ruby-2.2.7/lib
Repositories : master - https://github.com/CocoaPods/Specs.git @ 29c32347b0e16c19f1f2b438980959b5240bcc48
               sankuai-binaryspecs - ssh://git@git.sankuai.com/ios/binaryspecs.git @ 5c5f950f2d3697f524280a4d7bcb3dee485ba9ca
               sankuai-bizapp-specs - ssh://git@git.sankuai.com/sjst/bizapp-specs.git @ f4413e94a43dc0539f11d4d453bdd09a5710d370
               sankuai-specs - ssh://git@git.sankuai.com/ios/specs.git @ 33e04c7093977188ea0944da9f9a5f6582957c8e
               sankuai-specs-1 - ssh://git@git.sankuai.com/wm/specs.git @ dbee903fe0a3627de127fda74117f76338db224f

Installation Source

Executable Path: /Users/ooops/.rvm/gems/ruby-2.2.7/bin/pod

Plugins

cocoapods-deintegrate : 1.0.2
cocoapods-plugins     : 1.0.0
cocoapods-search      : 1.0.0
cocoapods-stats       : 1.0.0
cocoapods-trunk       : 1.3.0
cocoapods-try         : 1.1.0

Podfile

# Uncomment the next line to define a global platform for your project
platform :ios, '9.0'

target 'Example' do
  # Uncomment the next line if you're using Swift or would like to use dynamic frameworks
  # use_frameworks!

  # Pods for Example
  pod 'Pod1', :path => '../Pod1'
  pod 'Pod2', :path => '../Pod2'

end

Project that demonstrates the issue

cocoapods-resources.zip
I didn't show image in demo app, just open the build artifact and show package content of .app to see if Asset.car contains the image in Pod1/Assets/images.xcassets

Investigation

I have read the Build System Release Notes for Xcode 10, it said

Targets which have multiple asset catalogs that aren't in the same build phase may produce an error regarding a "duplicate output file". (39810274)

Workaround: Ensure that all asset catalogs are processed by the same build phase in the target.

The problem is I don't know how to ensure all asset catalogs are processed in same build phase for the target.
Any advice or guidance would be greatly appreciated.

@dnkoutso dnkoutso added the r:new build system Issues related to Xcode's new build system introduced in Xcode 9 label Sep 24, 2018
@ansonbtl
Copy link

I am having similar issue too. clean and rebuild will include all assets. but when I re-run the project, Assets.car will be empty, recompiled without assets from pods. It seems the "re-run" doesn't copy the assets from pods.

I had to switch back to legacy build system on Xcode 10. Please fix this.

@dnkoutso
Copy link
Contributor

I havent verified but this might be the same issue as this #8073

Worth trying master to see if it fixes it and use:

install! 'cocoapods', :disable_input_output_paths => true

Other than that I need to find time to investigate this so I am uncertain yet if its a CocoaPods issue or an Xcode 10 new build system issue.

@samirGuerdah
Copy link

Meanwhile investigation you can you can use a work arounds : using the Xcode Legacy Build System and not the Xcode 10 new build system.

Xcode / File / Workspace Settings / Build system / select "Legacy Build System"

capture d ecran 2018-09-25 a 11 04 14

@yam-liu
Copy link
Author

yam-liu commented Sep 25, 2018

@samirGuerdah Thanks for your workaround. I have already known this way can make it work. But I also hope this issue can be solved when using new build system.

@dnkoutso dnkoutso added the s7:workaround available A workaround for the issue is available label Sep 25, 2018
@dnkoutso
Copy link
Contributor

Uncertain yet if its a CocoaPods bug itself or new build system bug. It appears that switching to legacy makes it work for folks. I haven't had the time to investigate it though.

@yam-liu
Copy link
Author

yam-liu commented Oct 1, 2018

@dnkoutso Remind

@dnkoutso
Copy link
Contributor

dnkoutso commented Oct 1, 2018

@ooopscc there is no specific time an issue will be dealt with. This is mostly handled by free time of a few folks. If you need this faster I highly recommend to investigate the sources, otherwise I cannot provide a time frame sorry.

@yam-liu
Copy link
Author

yam-liu commented Oct 1, 2018

It is ok to leave it alone for now, because I can use legacy build system. I post this issue not asking for workaround, I already know it. I think it’s not compatible with new build system, just solve it whenever it’s convenient for you. Thanks.

@OneSman7
Copy link

OneSman7 commented Oct 3, 2018

Trying to investigate this issue. Found that warning is fixed by removing input/output paths (or using related install option). But assets are still not present on incremental builds just like @ansonbtl described.

@shixiaoda
Copy link

*.xcassets of Copy Bundle Resources --> Assets.car
*.xcassets of [CP] Copy Pods Resource --> other Assets.car
The first one covers second in the New Build System,That's the reason.

@OneSman7
Copy link

OneSman7 commented Oct 5, 2018

Great guess! Anyone have ideas how to guarantee [CP] Copy Pods Resource execution after Copy Bundle Resources?

@OneSman7
Copy link

OneSman7 commented Oct 5, 2018

It seems I found the solution.

Placing Assets.car in input files of the [CP] Copy Pods Resource script tells the build system to wait for it to be created before running the script. See Xcode 10 Build System Release notes:

In the new build system, shell scripts can't rely on the state of build artifacts not listed in other build phases (for example, the Info.plist file or .dSYM files.) Add files the script build phase depends on as explicit input dependencies to the shell script build phase. (40852184)

As for the warning about duplicated output, if we remove Assets.car from output files of the script, it is not executed at all. Seems that build system do not see that script processes it + there are no changes in input files. Thus it is skipped. The warning itself does not cause any change in the build process as I can see. I guess we can ignore this warning since it is a known issue with provided workaround . Thus I hope it will be fixed by Apple at some point.

I also inspected build system CompileAssetsCatalog phase and found more arguments to actool that it uses. For example resource thinning in debug configuration and enabling on demand resources. These parameters are passed via env variables to resources script so they can be added to actool call in it.

@dnkoutso I want to create PR that will incorporate the fix and changes above. I found that I can modify the script in copy_resources_script.rb, but I do not see where I can alter script`s input and output files. Maybe you can help me?

Meanwhile the workaround for new build system will be to copy ${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/Assets.car to the input files of [CP] Copy Pods Resource after pod install or pod update.

@yam-liu
Copy link
Author

yam-liu commented Oct 5, 2018

@OneSman7 Probably here.

@yam-liu
Copy link
Author

yam-liu commented Oct 7, 2018

@dnkoutso I try to put the action that @OneSman7 describes in post_install hook, but fails.
Would you please help me figure out why this modification will be reverted.

post_install do |installer|
    app_project = installer.aggregate_targets.first.user_project
    main_target = app_project.targets.first
    build_phases = main_target.build_phases.grep(Xcodeproj::Project::Object::PBXShellScriptBuildPhase)
    phase = build_phases[1]  # This is [CP] Copy Pod Resources phase, just for testing.
    phase.input_paths.clear
    phase.shell_script = ""
    app_project.save
end

@OneSman7
Copy link

OneSman7 commented Oct 7, 2018

@ooopscc as I see your script removes any code from Copy Pod Resources and input paths.
This just breaks the phase.
The cause of the "assets bug" is the parallelization of the new build system. It tries to run as much tasks as possible at once on multiple cores. Thus in incremental builds Copy Pod Resources phase is executed a bit earlier that regular Compile assets phase. So Xcode just overwrites Assets.car.
We need to make sure Copy Pod Resources phase is executed later. As stated in new docs, if script phases depend on certain file it should be stated in input paths. So I proposed to make Copy Pod Resources phase dependant on Assets.car existing in build folder -> add it to input path of this phase.
No other change is needed. To remove the warning we can remove Assets.car from output path, but it hides form the build system that Assets.car is processed by this script, so it decides not to execute it (all other input files do not change from build to build). This warning is a known issue, so I hope that Apple will fix it in the future.

@yam-liu
Copy link
Author

yam-liu commented Oct 8, 2018

@OneSman7 Thanks for the details. The hook I wrote is just a test to see if the modification will work. The full script should add the Assets.car to the input paths.
Use the above script, I can get the correct input paths (same as the final one), and the clear and save seems work, but after a while, it is modified and my changes is reverted.

@OneSman7
Copy link

OneSman7 commented Oct 8, 2018

I see. I do not know what is reverting your changes, but I think that modifying anything except for Pods project in post install hook can have unexpected consequences since it was not intended.
I suggest adding input path manually after each pod install or update. It is not happening so often and the change is not reverted :)

@OneSman7
Copy link

OneSman7 commented Oct 8, 2018

It seems I overestimated my Ruby skills, my current workload and the complexity of CocoaPods project.
I cannot tell if will be able to create PR any time soon, so I will write about my findings here. Maybe someone else can use them to fix the issue.

The origin of the issue is described in full detail above. To reenable pods assets in incremental builds we need to add ${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/Assets.car path to the input paths of the Copy Pod Resource script build phase (maybe just copy from output paths). We can ignore the warning of duplicated output since it is a known issue by Apple and do not affect anything.

In CocoaPods/lib/cocoapods/installer/user_project_integrator/target_integrator.rb in resource_output_paths method there is a line basename = extname == '.xcassets' ? 'Assets' : File.basename(resource_input_path). Here we are able to determine that we will output Assets.car. I was thinking about 2 possible additions:

  1. Making 'Assets' a constant. Search for it in output paths in add_copy_resources_script_phase method. If a path with this constant is found, add it to the input path also.
  2. Avoid searching, make resource_output_paths method return Assets.car path if it was added. If it was returned add it to the input paths.

I also studied CompileAssetCatalog command in Xcode 10:

/Applications/Xcode.app/Contents/Developer/usr/bin/actool --output-format human-readable-text --notices --warnings --export-dependency-info .../assetcatalog_dependencies --output-partial-info-plist .../assetcatalog_generated_info.plist --app-icon AppIcon --compress-pngs --enable-on-demand-resources YES --filter-for-device-model iPhone10,6 --filter-for-device-os-version 11.4 --target-device iphone --minimum-deployment-target 9.3 --platform iphonesimulator --product-type com.apple.product-type.application --compile .../Assets.xcassets

I was thinking about adding additional options to actool call in CocoaPods/lib/cocoapods/generator/copy_resources_script.rb:

  1. --enable-on-demand-resources YES. The value can be obtained from ENABLE_ON_DEMAND_RESOURCES=YES env var.
  2. --filter-for-device-model iPhone10,6 --filter-for-device-os-version 11.4. If these options should be added can be obtained from BUILD_ACTIVE_RESOURCES_ONLY=YES env var. The values for options themselves are in TARGET_DEVICE_MODEL=iPhone10,6 and TARGET_DEVICE_OS_VERSION=11.4 env vars.
  3. --product-type com.apple.product-type.application. The value can be obtained from PRODUCT_TYPE=com.apple.product-type.application env var.
  4. The value of COMPRESS_PNG_FILES=YES env var can be used to determine whether to add --compress-pngs option.

These additions can bring support for on demand resources and improve debugging (they enable app resources thinning in debug builds, which decreases resources compile time and helps testing app thinning).

@matt-loflin
Copy link

Thank you @OneSman7 for the very thorough investigation and explanation!

For others trying to find a workaround, one option is to add the following to your project's Podfile:

# Work around for issue described here: https://github.com/CocoaPods/CocoaPods/issues/8122#issuecomment-427680543
post_install do |installer|
  project_path = '<Your_Project_File_Here>.xcodeproj'
  project = Xcodeproj::Project.open(project_path)
  project.targets.each do |target|
    build_phase = target.build_phases.find { |bp| bp.display_name == '[CP] Copy Pods Resources' }
    
    if build_phase.present?
      build_phase.input_paths.push('${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/Assets.car')
    end
  end
  
  project.save(project_path)
end

This would make sure that every time pod install or pod update is run, the copy pod resources phase is updated with the workaround suggested by others in this issue.

@smarchant-owlet
Copy link

smarchant-owlet commented Nov 28, 2018

Unfortunately, @matt-loflin's script didn't quite work for me, though I appreciate seeing that script spelled out. Using the script produced the following compiler error on my project:

invalid task ('PhaseScriptExecution [CP]\ Copy\ Pods\ Resources ... with mutable output but no other virtual output node (in target 'Secret')

I'm surprised more people aren't hitting this issue. For me, that warning about duplicated output file on Assets.car isn't benign. My assets (colors in xcassets) don't actually load at runtime.

I was able to reproduce this issue with the finished tutorial project on raywenderlich.com (https://www.raywenderlich.com/5823-how-to-create-a-cocoapod-in-swift). With that project, you see the same warning. And at runtime, you see Could not load the "background" image referenced from a nib in the bundle with identifier.

Seems like using xcassets as resources in a CocoaPod framework is broken under Xcode 10.1 when using the new build system.

@philtre
Copy link

philtre commented Dec 10, 2018

I've also been having trouble with @matt-loflin's script.
When I run pod install the script works fine; the input path is added to the "[CP] Copy Pods Resources" build phase and all the images work as expected.

However, when I run pod install again, the input path is removed from the build phase. This is obviously a problem and it's an even bigger one when it occurs in a CI workflow.

According to this StackOverflow answer, the script would have to be run after the "Integrating client project" step, but there are no installer hooks that run after the project integrator:
https://stackoverflow.com/questions/33846361/hook-in-podfile-to-edit-my-project-file/38208513#38208513

@gph1991
Copy link

gph1991 commented Dec 27, 2018

*.xcassets of Copy Bundle Resources --> Assets.car
*.xcassets of [CP] Copy Pods Resource --> other Assets.car
The first one covers second in the New Build System,That's the reason.

@shixiaoda

I don't think so,If this is true,Why first time run it was work?

@stale
Copy link

stale bot commented Mar 27, 2019

There hasn't been any activity on this issue recently. Due to the high number of incoming GitHub notifications, we have to clean some of the old issues, as many of them have already been resolved with the latest updates.

@stale stale bot added the s1:awaiting input Waiting for input from the original author label Mar 27, 2019
@One1Light
Copy link

Use legacy is not work on xcode10.2. Have some idea?

@stale stale bot removed the s1:awaiting input Waiting for input from the original author label Mar 28, 2019
@njuxjy
Copy link

njuxjy commented Apr 22, 2019

+1. Still broken for the new build system in Xcode 10.2.
Also for both cocoapods version 1.5.3 and 1.7.0.beta.3.
Waiting for solutions.

@xilin
Copy link

xilin commented Apr 25, 2019

+1. Still broken for the new build system in Xcode 10.2.

@tidbeck
Copy link

tidbeck commented Mar 18, 2020

@adamgins I put it just under platform :ios, '9.0'. But as commented buy many in this thread already it will probably slow down your incremental builds a lot. I'm using the legacy build system for now.

weiran added a commit to weiran/watch-it-later that referenced this issue Apr 28, 2020
This fixes a bug with a duplicate Assets.car output path but also slows down incremental build as Xcode will rebuild asset catalogs every time.

See: CocoaPods/CocoaPods#8122
@BadChoice
Copy link

BadChoice commented Jun 8, 2020

Adding this to podfile

install! 'cocoapods', :disable_input_output_paths => true

And changing the copy pods order worked for us

@justinseanmartin
Copy link
Contributor

If you want to avoid the incremental compilation time hit, using resource_bundles rather than resources as mentioned above by @cgossain should help.

@raven
Copy link

raven commented Jun 23, 2020

https://developer.apple.com/documentation/xcode-release-notes/xcode-12-beta-release-notes#Build-System

image

@dnkoutso
Copy link
Contributor

Yeap! The change you highlighted fixed a long standing issue also for development pods and dynamic frameworks #8073.

However, other issues regarding XCAssets are still remaining.

@zaptrem
Copy link

zaptrem commented Jun 23, 2020

@dnkoutso @raven
Does this mean Xcode 12 fixes this whole issue (i.e. no more need for slow workarounds?)

@bitomule
Copy link

bitomule commented Jun 26, 2020

So, legacy build system is being deprecated and we will not be able to use cocoapods with the new one. Is there any workaround without performance issues?

@bitomule
Copy link

bitomule commented Jun 26, 2020

@zaptrem I can confirm we still have the same issue with Xcode 12 beta1 (12A6159)

@arpitdsoni
Copy link

arpitdsoni commented Jul 31, 2020

If it helps anyone. So disable_input_output_paths slowed down our build times a lot. So I had to find another workaround, the pod which is causing assets.car error, install it as dynamic framework. Note: You also have to install pod's dependencies as dynamic framework.

use_frameworks! :linkage => :static

dynamic_frameworks = ['Pod1', 'dependentPod']
pre_install do |installer|
    installer.pod_targets.each do |pod|
        if dynamic_frameworks.include?(pod.name)
            puts "Overriding the build_as_dynamic_framework? method for #{pod.name}"
            def pod.build_as_dynamic_framework?;
                true
            end
            def pod.build_type;
                Pod::BuildType.dynamic_framework
            end
        end
    end
end

@artemwalla
Copy link

Hello all,

I've found a workaround to this problem that is working well for me, and allows me to keep using *.xcassets files.

Broken Setup:

MyPod.podspec:

s.resources = 'MyPod/Assets/Images/**/*'

UIImage+Bundle.swift helper extension:

import UIKit

extension UIImage {
    /// Creates an image object using the named image asset from the current library bundle.
    static func makeLibraryImage(named: String) -> UIImage? {
        return UIImage(named: named, in: Bundle(for: SomeClassInMyLibrary.self), compatibleWith: nil)
    }
}

Working Setup:

MyPod.podspec:

s.resource_bundles = { 'MyPod-Images' => ['MyPod/Assets/Images/*.xcassets'] }

UIImage+Bundle.swift helper extension:

import UIKit

extension UIImage {
    /// Creates an image object using the named image asset from the current library bundle.
    static func makeLibraryImage(named: String) -> UIImage? {
        guard let bundlePath = Bundle(for: SomeClassInMyLibrary.self).path(forResource: "MyPod-Images", ofType: "bundle") else {
            return nil
        }
        let bundle = Bundle(path: bundlePath)
        return UIImage(named: named, in: bundle, compatibleWith: nil)
    }
}

Summary

In summary, in my working solution I've reconfigured my .podspec to wrap my .xcassets file into a resource bundle, and I've updated my helper extension accordingly. This is currently working reliably for me and I don't need to add additional plugins or code into my Podfile.

Update - Nov 21, 2019

It seems like I still need to run pod install after a pod update for this solution to work.

Apparently the pod update command removes the paths to the ressource bundles from the [CP] Copy Pods Resources build phase script, but then pod install adds them back in.

this solution worked for me as well. only need to check that all the Pod dependencies that you're using are also implementing resource_bundles instead of resources. this is also a better solution to avoid naming collisions between your assets and assets from 3rd party dependencies.

@stale
Copy link

stale bot commented Dec 19, 2020

There hasn't been any activity on this issue recently. Due to the high number of incoming GitHub notifications, we have to clean some of the old issues, as many of them have already been resolved with the latest updates.

@stale stale bot added the s1:awaiting input Waiting for input from the original author label Dec 19, 2020
@Davarg
Copy link

Davarg commented Jan 14, 2021

Still not working properly

@LJ8116
Copy link

LJ8116 commented Mar 28, 2024

any progress?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
r:new build system Issues related to Xcode's new build system introduced in Xcode 9 s7:workaround available A workaround for the issue is available
Projects
None yet
Development

No branches or pull requests