Cocoapods: [Question] Why does added build phases take time when pods are not changed?

Created on 29 Nov 2016  路  19Comments  路  Source: CocoaPods/CocoaPods

After installing pods, two build phases are added in my project:

# [CP] Embed Pods Framework
"${SRCROOT}/Pods/Target Support Files/Pods-ProjectName/Pods-ProjectName-frameworks.sh"

that runs a script generated by this
and

#[CP] Copy Pods Resources
"${SRCROOT}/Pods/Target Support Files/Pods-ProjectName/Pods-ProjectName-resources.sh"

that runs a script generated by this

They take quite a while to run. Do they have run every time when the pods haven't change? If I remove them manually the app compiles fine when the pods have not changed.

moderate enhancement

Most helpful comment

I think someone could investigate if there's any low hanging fruit here in the way of skipping code signing that's already been done.

There certainly is. As it currently is, the script always always unconditionally does three things:

  1. Use rsync to copy the framework (pretty efficient)
  2. Possibly strip the binaries (a bit less)
  3. Codesign the binaries (very inefficient, typically adds seconds to build time even with --timestamp=none & on powerful CPU)

2+3 should be conditional on 1 changing anything. There are at least two ways to accomplish that:

(1) Xcode has builtin dependencies tracking via the Input Files and Output Files properties of each script action. The action won't run if all output files exist and no input file is newer (i.e. the action is certain to just redo something it already did previously, with the same inputs). See this article for a bit more details. If the action declared its inputs/outputs, Xcode would optimize it out and not run the script at all.

(2) Alternatively, the script can do it itself and use [ ! $newest_source_file -nt $oldest_destination_file ] to bail out early (many ways to get the representative files).

(Apologies for not making a PR just yet, I'll return to optimizing build times once I get some more urgent things off my plate.)

All 19 comments

Hi there! In general, Xcode will always run script-type build phases, which is what these are. We could add something to verify that these phases have already been completed and don't need to be re-run before running them, but given how fast they usually are, the cost to verify might get close to the cost to just run the script. It's certainly worth investigating though.

That said, is can you add time in front of each of the scripts in each build phase your Xcode project? Then, you can see how long they actually take. It'd be curious to know if one took more than a few seconds. Thanks!

Like this for example:
screen shot 2016-11-29 at 1 10 52 pm

Thanks. How do I get the output of time? I think it roughly takes 5s for me. Which is not the end of the world but which 1/3 of my total build time.

It should show up in your build log (right most panel in the left pane in Xcode).

That's what I thought. Somehow I see logs while the script is running and then they disappear. Anyway my watch is measuring my 5s.

No prob. For which script is it measuring 5s? Is it the resources one or the embed one?

For the embed one. Sorry should have said earlier

Ah okay. Can you have a look at the comments in this thread really quick? Specifically, the linked SO post seemed to help. People seemed to have success after auditing their keychains for duplicate/expired certs.

I indeed had a number of duplicated keys in my keychain, which I removed, but I don't think it changed things. However setting COCOAPODS_PARALLEL_CODE_SIGN to true does make things faster. If that step could be avoided when pods have not changed that'd be great, but I don't know much about the build process so that might not be possible

AFAIK, it can't be completely avoided. Xcode will always run that build step, so at the very least we'll have to do some verification which will also take time.

I think someone could investigate if there's any low hanging fruit here in the way of skipping code signing that's already been done. I'll leave this ticket open, so that someone else can have a look if possible.

Thanks. Yes I meant have the script to run every time, since this is what Xcode will do, but only run expensive stuff when needed, if possible

I know for a lot of folks it takes 5s but for me it is several minutes. I might have a lot of pods that is why, but I agree, there needs to be some sort of cache check to see if these should be run over and over.

@electic we're certainly open to ideas! At present, there are no new suggestions. In my mind, you'd still have to verify the signature to make sure you didn't need to re-run codesign for each framework, and that's also expensive. Typically with runtimes of several minutes, people were able to fix that by finding and removing dups and expired certificates in there keychains.

Maybe there is a way to hash the file and keep a cache? I am not too familiar with the internals, but if we can keep a cache, and if that hash changes, then we can re-run codesign. From a cursory look at things, it seems like it runs a ruby script.

I think someone could investigate if there's any low hanging fruit here in the way of skipping code signing that's already been done.

There certainly is. As it currently is, the script always always unconditionally does three things:

  1. Use rsync to copy the framework (pretty efficient)
  2. Possibly strip the binaries (a bit less)
  3. Codesign the binaries (very inefficient, typically adds seconds to build time even with --timestamp=none & on powerful CPU)

2+3 should be conditional on 1 changing anything. There are at least two ways to accomplish that:

(1) Xcode has builtin dependencies tracking via the Input Files and Output Files properties of each script action. The action won't run if all output files exist and no input file is newer (i.e. the action is certain to just redo something it already did previously, with the same inputs). See this article for a bit more details. If the action declared its inputs/outputs, Xcode would optimize it out and not run the script at all.

(2) Alternatively, the script can do it itself and use [ ! $newest_source_file -nt $oldest_destination_file ] to bail out early (many ways to get the representative files).

(Apologies for not making a PR just yet, I'll return to optimizing build times once I get some more urgent things off my plate.)

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.

Closing this. Will ship with 1.3.0.beta.2.

Was this page helpful?
0 / 5 - 0 ratings