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

Fix dependency resolution when building #1139

Merged
merged 15 commits into from Feb 22, 2016

Conversation

erichoracek
Copy link
Member

As of #1100, the build command uses a comparison sort to resolve ordering of dependencies to build. That change resulted in frameworks being built out of order in significantly complex dependency graphs, causing "module not found" errors.

To resolve this, Kahn's topological sort is used in place of a comparison sort to ensure that the dependency graph is built in the correct order.

In our project with 43 unique dependencies (including transitive dependencies), this change fixed "module not found" errors when using the build command in the most recent release. Additionally, since this is a generic algorithm, it can be easily tested (assuming there is a reasonable place to put it). It would appear #1100 removed the previous tests around build order.

This PR has some open questions:

  • Is there another place to put the generic topological sort algorithm?
  • Is it reasonable to fail out the build command when a cycle is detected during a topological sort?

As of Carthage#1100, dependency resolution for the "build" command uses a comparison sort to resolve ordering of dependencies that need to be built. This change does not build dependencies in the correct order in significantly complex dependency graphs, causing "module not found" errors.

To resolve this, Kahn's topological sort is used in place of a comparison sort to ensure that the dependency graph is built in the correct order.

In our test project with 43 unique dependencies (including transitive dependencies), this change fixed the "module not found" issues. Additionally, since this is a generic algorithm, it can be easily tested (once I get some guidance on a place to put it).
}
.reduce([:]) { (working: DependencyGraph, next: DependencyGraph) in
var result = working
next.forEach { result.updateValue($1, forKey: $0) }
Copy link
Member

Choose a reason for hiding this comment

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

Could you make these named parameters? I think it will make it more obvious how this works.

Copy link
Member

Choose a reason for hiding this comment

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

I think we could actually use mergeGraphs here instead. It doesn't seem like this properly handles all cases of shared, recursive dependencies as-is.

Also, I had no idea you could use updateValue like this. 😕

@mdiep
Copy link
Member

mdiep commented Feb 19, 2016

  1. Sorry for introducing bugs. 😞
  2. Thank you so much for opening a pull request to fix them! ⚡⚡⚡
  3. This seems like a brilliant approach.
  4. I wish I'd thought of it!

I left a few notes about the structure and some of the details. I still need to look over topologicalSort in detail; I'll try to do that later today.

// Open Question: Is there a place that a generic topological
// sort should live?
func topologicalSort<Node: Equatable>(edges: Dictionary<Node, Set<Node>>, @noescape isOrderedBefore: (Node, Node) -> Bool) -> [Node] {
var nodeQueue: [Node] = edges.filter { $1.isEmpty }.map { return $0.0 }
Copy link
Member

Choose a reason for hiding this comment

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

I'd prefer to use named variables in the blocks. I think that makes it easier to read the code. It will probably make sense to split the filter and map on separate lines with the added characters.

@mdiep
Copy link
Member

mdiep commented Feb 20, 2016

Great stuff. ⚡ I left some comments that I think would make the code more readable, but the implementation looks spot on.

If you're up for it, I'd love to see tests around topologicalSort. That's the real beauty of this PR—the code is far more testable. But honestly, I'd take this without the tests and add them later if you're not up for it.

}
}
return result
.reduce([:]) { (working: DependencyGraph, next: DependencyGraph) in
Copy link
Member Author

Choose a reason for hiding this comment

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

@mdiep I couldn't figure out how to use mergeGraphs here—it would seem that there's two types of DependencyGraph in CarthageKit.

Copy link
Member

Choose a reason for hiding this comment

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

Oh, that's right. This is just a typealias in this method. 😜

@mdiep
Copy link
Member

mdiep commented Feb 21, 2016

This is looking really great. Just a few more small issues.

@mdiep
Copy link
Member

mdiep commented Feb 22, 2016

Awesome. ⚡

mdiep added a commit that referenced this pull request Feb 22, 2016
Fix dependency resolution when building
@mdiep mdiep merged commit 930373b into Carthage:master Feb 22, 2016
@ikesyo
Copy link
Member

ikesyo commented Feb 22, 2016

👏

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

3 participants