Can't consume two Swift Packages with the same name

Hi folks,

I believe the implementation of SE-0226 is overly restrictive, not allowing the name parameter for a dependency to be overridden which leads to an issue when depending on two Swift Packages that declare the same Package Name. I'm happy to file a bug on this, but want to ensure that my expectations are correct. Originally discovered in firebase-ios-sdk#6357.

I created a repo at GitHub - ryanwilson/GitHub-6357: Repro of SwiftPM issue https://github.com/firebase/firebase-ios-sdk/issues/6357 that uses the Firebase repo and a dummy repo I created that uses the same package name ("Firebase"). This is using Xcode 12.0 that was just released.

Essentially, when using two dependencies that use the same name an error occurs that recommends using the name argument to differentiate:

"error: duplicate dependency named 'Firebase'; consider differentiating them using the 'name' argument"

After using the name parameter in dependencies as recommended to rename one from "Firebase" to "DummyFirebase" it eventually fails with:

error: declared name 'DummyFirebase' for package dependency 'https://github.com/ryanwilson/DummyFirebasePackage.git' does not match the actual package name 'Firebase'

It's also reproducible when just trying to give the plain Firebase repo a different name other than Firebase, but this is mostly an issue for when two dependencies use the same name.

I believe the implementation lives in this function of PackageGraphLoader.swift. I'm happy to make an attempt at a fix, I'd only like some guidance on what the expectation is here. I am guessing that the restriction for matching the expected name should only exist if a custom name argument was not provided to the dependency (so it's using the name given to it in the original Package manifest), but would like to double check!

Thanks,
Ryan

2 Likes

My understanding is that the reason this limitation is present is because the definition of package identity is not the URL from which you clone the package but its name, as declared in the Package.swift file. This really does have to be unique: otherwise dependency resolution is unclear throughout the package ecosystem.

Essentially, conceptually, package names are a global namespace. Collisions are not policed, but if they occur they render build-trees unusable. I recommend renaming your package.

The name parameter is not an opportunity to alias the dependency. The name you provide there must match the dependency’s declared name, and must be present if the last path component of the URL is not the package name (since tools version 5.2). This parameter exists so that SwiftPM can know the package’s identity without fetching it, allowing SwiftPM to completely skip fetching any unreachable dependencies.

Thank you for your replies.

It sounds like the very first improvement to make is the error message produced when there are dependencies with the same nam, in order to disambiguate which name argument needs to change, although this is likely something that the developer doesn't have control over if it's a third party dependency.

This seems like it doesn't match well with the decentralized nature of SwiftPM. This way, a package author should likely ensure that their package name doesn't collide with the name of a popular package in order to avoid collisions, but that may be tough to find. A name like "Promises" comes to mind for a Promises framework, or another generic framework of that nature.

Re: "I recommend renaming your Package" that would be a major breaking change for any existing package to make - something we try to do very infrequently on Firebase for example.

This is likely a naive question and more for my own understanding, but given that a git repo can only have a single package manifest wouldn't the identify of the package make sense to be the URL from which you clone the package? Then use the name parameter locally to determine if a dependency could be skipped based on the target being built.

I think collisions of package names and module names (inherited from target names) are on the list of things to be fixed one day. The current definition of a package’s identity seems to be an accident of history that no one is quite satisfied with. It just hasn’t caused enough problems to prompt anyone to invest the necessary time to design a good a replacement.

  1. There has been the intention of supporting multiple packages in the same repository some day.
  2. Due to mirroring, redirection, ambiguous equality (a client cannot tell if the server’s file system is case sensitive or not) and other issues with URLs, they are not as great for this as they might seem.

Everyone wants things in this area to be improved, but it is not a simple bug fix. It will require a thoughtful design that can survive the gauntlet of the evolution process.

2 Likes

Yep, this is correct, but we have been discussing amongst the team that we have to handle naming conflicts better as the ecosystem grows.

The limitation that module names have to be unique is something we may not be able to address in the near term because it involves a lot of other components like the compiler, but conflicts at the package-level are unique to SwiftPM and we should strive to avoid a global namespace here at some point in the future.

@Ryan_Wilson if that's something you're interested in helping with, we can discuss more and potentially collaborate.

2 Likes

Thanks for the extra info folks!

This would be immensely helpful for Firebase, and I know we've looked into it before.

Happy to help where I can! We anticipate SwiftPM will turn into a large percentage of our distribution channels in the coming years for Firebase, I'd be happy to contribute and make it the best possible experience for the community as usage grows.

1 Like

The package name is used very infrequently (really only in .product dependency specifications), so a good idea is to make it as specific as possible. For example, if you fork a project, add your GitHub username to the package name to automatically namespace it.

I meant to rename your new package, not Firebase.

URLs are not unique. Even for git repositories you can get them from multiple places: on-disk, from Github.com with an SSH URL, from Github.com with a https URL, with or without a trailing .git, from a different hosting provider, and so-on. When you add to the mix the ongoing efforts to add a package registry for Swift, which will also add new URLs for packages to be obtained from, and this gets even messier.

It’s good to have package metadata that indicates which package we’re talking about: that’s what name is. Even if we allow disambiguation in future, being able to answer the question of “what package specifically are you talking about” is important.