Pitch: Simplifying target based dependencies syntax

Hello folks,

We would like to simplify the syntax for target based dependencies, which has been causing a bit of confusion since it was introduced in Swift 5.2. This draft amendment to SE-0262 attempts to address the biggest friction area - the need to specify a name on dependencies.

Looking forward to hearing your feedback.

7 Likes

First, for clarification, ā€œthe last path component of the URLā€ means the URL list in the dependencies section, regardless of any configured mirrors, or similar adjustments correct?


This change will surface URL details in more places. If this is going to happen we need to formalize exactly what representation SwiftPM expects for the manifest, and how all build tools should interpret it. Is the URL in the manifest the userā€facing kind you type in your address bar (preā€percent encoding), or is it the strict network kind you use in a DNS request (postā€percent encoding)?

SwiftPM itself has thus far required the preā€percent encoding form, but Xcode has waffled back and forth from release to release. Currently the two are at odds again (5.3.3 vs 12.4), and it requires if ProcessInfo.processInfo.environment["PATH"] shenanigans to support both tools.

This is of immense importance, because where I currently have this...

.product(name: "ĪœĪ˜Ī”ĪœĪ Īš", package: "ĪœĪ˜Ī”ĪœĪ Īš")

...I do not see this as an improvement:

.product(name: "ĪœĪ˜Ī”ĪœĪ Īš", package: "%CE%9C%CE%98%CE%94%CE%9C%CE%A0%CE%9A")

hi @SDGGiesbrecht

First, for clarification, ā€œthe last path component of the URLā€ means the URL list in the dependencies section, regardless of any configured mirrors, or similar adjustments correct?

Yes, I will clarify the amendment language

This change will surface URL details in more places.

This is true, but transitional. SE-0292 will continue to refine how we compute the identity away from using the ā€œthe last path component of the URL". perhaps important to note, the use of "the last path component of the URL" as the package identity is not introduced in this amendment - its just reused here instead of a manually typed name that needs to be looked up in the dependency's manifest file

Is the URL in the manifest the userā€facing kind you type in your address bar (preā€percent encoding), or is it the strict network kind you use in a DNS request (postā€percent encoding)?

Great point. We treat the URL as-typed, no percent encoding. We should add some language and a couple of tests to codify this.

Would an internationalized domain name (IDN) also be as-typed, or in Punycode to avoid an IDN homograph attack?

Thanks for bringing it up @benrimmington. Exploring usage of punycode transcoding in SwiftPM could be interesting, but it's probably beyond the scope of this amendment. As noted, the use of "the last path component of the URL" as the package identity is not introduced in this amendment - its just reused here instead of requiring a manually typed name that needs to be looked up in the dependency's manifest file. In other word, if SwiftPM has issues with IDNs, this amendment does not change that.

Also to note that GitHub.com and other popular git-as-a-service solutions are fairly restrictive with repository URLs for the same reason.

FWIW, IDNA is more than just Punycode. Punycode is just a way of representing a Unicode string as ASCII (like percent encoding). IDNA is actually a full-blown Unicode normalisation algorithm (spec), just like NFC and NFD normalisation, with its own data tables and everything - and it's the result of that which is Punycode-encoded to an ASCII string, as URLs must always be ASCII.

For instance, the URL http://ļ¼§ļ½.com/ (using full-width characters) is normalised to http://go.com/. It isn't Punycode because the result of normalisation is already ASCII. Also, http://www.foo怂bar.com (using U+3002 IDEOGRAPHIC FULL STOP) is normalised to http://www.foo.bar.com, and the resulting ASCII period is treated the same as any other WRT interpreting the domain and its subdomains.

Actually doing IDNA in Swift would require ICU or alternative. It's something I plan to look in to quite soon.

2 Likes

That apparently did not happen. The manifest shims required to keep Xcode from instantly crashing doubled in size with Xcode 13.3:

// #workaround(xcodebuild -version 13.3, Xcode mishandles the URLs.) @exempt(from: unicode)
import Foundation
let path = ProcessInfo.processInfo.environment["PATH"] ?? ""
let firstColon = path.range(of: ":")?.lowerBound ?? path.endIndex
let firstEntry = path[..<firstColon]
if firstEntry.hasSuffix("/Contents/Developer/usr/bin") {
  package.dependencies = package.dependencies.map { dependency in
    switch dependency.kind {
    case .sourceControl(_, var modifiedURL, let requirement):
      switch requirement {
      case .range(let range):
        var components = modifiedURL.components(separatedBy: "/")
        var last = components.removeLast()
        last = last.addingPercentEncoding(withAllowedCharacters: .urlPathAllowed) ?? last
        components.append(last)
        modifiedURL = components.joined(separator: "/")
        return .package(url: modifiedURL, range)
      default:
        return dependency
      }
    default:
      return dependency
    }
  }
  for target in package.targets {
    target.dependencies = target.dependencies.map { dependency in
      switch dependency {
      case .productItem(let name, var package, let condition):
        package = package?.addingPercentEncoding(withAllowedCharacters: .urlPathAllowed) ?? package
        return .productItem(name: name, package: package, condition: condition)
      default:
        return dependency
      }
    }
  }
}

...and it still fails to load the graph due to ā€œSwiftPM.SPMRepositoryError error 5ā€ unless I mirror the package at an Xcodeā€friendly URL.

(Command line SwiftPM still handles everything just fine; it does have tests for this.)

Ugh, looks like an Xcode integration issue then. Do you mind submitting a bug report and we will look into it.

cc @abertelrud @NeoNacho

Feedback number: FB9964441

cc @abertelrud @NeoNacho

Thanks for reporting, we will work to address this in one of the next releases.

1 Like

It seems to be fixed finally in Xcode 14. Many thanks!