@@ -771,7 +771,7 @@ extension Workspace {
771
771
772
772
return diagnostics. wrap {
773
773
let contents = try fileSystem. readFileContents ( path)
774
- return checksumAlgorithm. hash ( contents) . hexadecimalRepresentation
774
+ return self . checksumAlgorithm. hash ( contents) . hexadecimalRepresentation
775
775
} ?? " "
776
776
}
777
777
}
@@ -1546,7 +1546,7 @@ extension Workspace {
1546
1546
targetName: target. name,
1547
1547
path: absolutePath)
1548
1548
)
1549
- } else if let url = target. url, let checksum = target. checksum {
1549
+ } else if let url = target. url. flatMap ( URL . init ( string : ) ) , let checksum = target. checksum {
1550
1550
remoteArtifacts. append (
1551
1551
. init(
1552
1552
packageRef: packageRef,
@@ -1566,23 +1566,78 @@ extension Workspace {
1566
1566
private func download( _ artifacts: [ RemoteArtifact ] , diagnostics: DiagnosticsEngine ) throws -> [ ManagedArtifact ] {
1567
1567
let group = DispatchGroup ( )
1568
1568
let tempDiagnostics = DiagnosticsEngine ( )
1569
+ let result = ThreadSafeArrayStore < ManagedArtifact > ( )
1569
1570
1570
1571
var authProvider : AuthorizationProviding ? = nil
1571
- #if os(macOS)
1572
- // Netrc feature currently only supported on macOS
1572
+ #if os(macOS) // Netrc feature currently only supported on macOS
1573
1573
authProvider = try ? Netrc . load ( fromFileAtPath: netrcFilePath) . get ( )
1574
1574
#endif
1575
1575
1576
- var didDownloadAnyArtifact = false
1577
- let result = ThreadSafeArrayStore < ManagedArtifact > ( )
1576
+ // zip files to download
1577
+ // stored in a thread-safe way as we may fetch more from "ari" files
1578
+ let zipArtifacts = ThreadSafeArrayStore < RemoteArtifact > ( artifacts. filter { $0. url. pathExtension. lowercased ( ) == " zip " } )
1579
+
1580
+ // fetch and parse "ari" files, if any
1581
+ let indexFiles = artifacts. filter { $0. url. pathExtension. lowercased ( ) == " ari " }
1582
+ if !indexFiles. isEmpty {
1583
+ let hostToolchain = try UserToolchain ( destination: . hostDestination( ) )
1584
+ let jsonDecoder = JSONDecoder . makeWithDefaults ( )
1585
+ for indexFile in indexFiles {
1586
+ group. enter ( )
1587
+ var request = HTTPClient . Request ( method: . get, url: indexFile. url)
1588
+ request. options. validResponseCodes = [ 200 ]
1589
+ request. options. authorizationProvider = authProvider? . authorization ( for: )
1590
+ self . httpClient. execute ( request) { result in
1591
+ defer { group. leave ( ) }
1578
1592
1579
- for artifact in artifacts {
1580
- group. enter ( )
1581
- defer { group. leave ( ) }
1593
+ do {
1594
+ switch result {
1595
+ case . failure( let error) :
1596
+ throw error
1597
+ case . success( let response) :
1598
+ guard let body = response. body else {
1599
+ throw StringError ( " Body is empty " )
1600
+ }
1601
+ // FIXME: would be nice if checksumAlgorithm.hash took Data directly
1602
+ let bodyChecksum = self . checksumAlgorithm. hash ( ByteString ( body) ) . hexadecimalRepresentation
1603
+ guard bodyChecksum == indexFile. checksum else {
1604
+ throw StringError ( " checksum of downloaded artifact of binary target ' \( indexFile. targetName) ' ( \( bodyChecksum) ) does not match checksum specified by the manifest ( \( indexFile. checksum ) ) " )
1605
+ }
1606
+ let metadata = try jsonDecoder. decode ( ArchiveIndexFile . self, from: body)
1607
+ // FIXME: this filter needs to become more sophisticated
1608
+ guard let supportedArchive = metadata. archives. first ( where: { $0. fileName. lowercased ( ) . hasSuffix ( " .zip " ) && $0. supportedTriples. contains ( hostToolchain. triple) } ) else {
1609
+ throw StringError ( " No supported archive was found for ' \( hostToolchain. triple. tripleString) ' " )
1610
+ }
1611
+ // add relevant archive
1612
+ zipArtifacts. append (
1613
+ RemoteArtifact (
1614
+ packageRef: indexFile. packageRef,
1615
+ targetName: indexFile. targetName,
1616
+ url: indexFile. url. deletingLastPathComponent ( ) . appendingPathComponent ( supportedArchive. fileName) ,
1617
+ checksum: supportedArchive. checksum)
1618
+ )
1619
+ }
1620
+ } catch {
1621
+ tempDiagnostics. emit ( . error( " failed retrieving ' \( indexFile. url) ': \( error) " ) )
1622
+ }
1623
+ }
1624
+ }
1582
1625
1583
- guard let parsedURL = URL ( string: artifact. url) else {
1584
- throw StringError ( " invalid url \( artifact. url) " )
1626
+ // wait for all "ari" files to be processed
1627
+ group. wait ( )
1628
+
1629
+ // no reason to continue if we already ran into issues
1630
+ if tempDiagnostics. hasErrors {
1631
+ // collect all diagnostics
1632
+ diagnostics. append ( contentsOf: tempDiagnostics)
1633
+ throw Diagnostics . fatalError
1585
1634
}
1635
+ }
1636
+
1637
+ // finally download zip files, if any
1638
+ for artifact in ( zipArtifacts. map { $0 } ) {
1639
+ group. enter ( )
1640
+ defer { group. leave ( ) }
1586
1641
1587
1642
let parentDirectory = self . artifactsPath. appending ( component: artifact. packageRef. name)
1588
1643
@@ -1593,20 +1648,17 @@ extension Workspace {
1593
1648
continue
1594
1649
}
1595
1650
1596
- let archivePath = parentDirectory. appending ( component: parsedURL. lastPathComponent)
1597
-
1598
- didDownloadAnyArtifact = true
1651
+ let archivePath = parentDirectory. appending ( component: artifact. url. lastPathComponent)
1599
1652
1600
1653
group. enter ( )
1601
-
1602
- var request = HTTPClient . Request. download ( url: parsedURL, fileSystem: self . fileSystem, destination: archivePath)
1654
+ var request = HTTPClient . Request. download ( url: artifact. url, fileSystem: self . fileSystem, destination: archivePath)
1603
1655
request. options. authorizationProvider = authProvider? . authorization ( for: )
1604
1656
request. options. validResponseCodes = [ 200 ]
1605
1657
self . httpClient. execute (
1606
1658
request,
1607
1659
progress: { bytesDownloaded, totalBytesToDownload in
1608
1660
self . delegate? . downloadingBinaryArtifact (
1609
- from: artifact. url,
1661
+ from: artifact. url. absoluteString ,
1610
1662
bytesDownloaded: bytesDownloaded,
1611
1663
totalBytesToDownload: totalBytesToDownload)
1612
1664
} ,
@@ -1642,33 +1694,32 @@ extension Workspace {
1642
1694
. remote(
1643
1695
packageRef: artifact. packageRef,
1644
1696
targetName: artifact. targetName,
1645
- url: artifact. url,
1697
+ url: artifact. url. absoluteString ,
1646
1698
checksum: artifact. checksum,
1647
1699
path: artifactPath
1648
1700
)
1649
1701
)
1650
1702
case . failure( let error) :
1651
1703
let reason = ( error as? LocalizedError ) ? . errorDescription ?? error. localizedDescription
1652
- tempDiagnostics. emit ( . artifactFailedExtraction( targetName: artifact. targetName, reason: reason) )
1704
+ tempDiagnostics. emit ( . artifactFailedExtraction( artifactURL : artifact . url , targetName: artifact. targetName, reason: reason) )
1653
1705
}
1654
1706
1655
1707
tempDiagnostics. wrap { try self . fileSystem. removeFileTree ( archivePath) }
1656
1708
} )
1657
1709
case . failure( let error) :
1658
- tempDiagnostics. emit ( . artifactFailedDownload( targetName: artifact. targetName, reason: " \( error) " ) )
1710
+ tempDiagnostics. emit ( . artifactFailedDownload( artifactURL : artifact . url , targetName: artifact. targetName, reason: " \( error) " ) )
1659
1711
}
1660
1712
} )
1661
1713
}
1662
1714
1663
1715
group. wait ( )
1664
1716
1665
- if didDownloadAnyArtifact {
1717
+ if zipArtifacts . count > 0 {
1666
1718
delegate? . didDownloadBinaryArtifacts ( )
1667
1719
}
1668
1720
1669
- for diagnostic in tempDiagnostics. diagnostics {
1670
- diagnostics. emit ( diagnostic. message, location: diagnostic. location)
1671
- }
1721
+ // collect all diagnostics
1722
+ diagnostics. append ( contentsOf: tempDiagnostics)
1672
1723
1673
1724
return result. map { $0 }
1674
1725
}
@@ -2565,10 +2616,34 @@ public final class LoadableResult<Value> {
2565
2616
private struct RemoteArtifact {
2566
2617
let packageRef : PackageReference
2567
2618
let targetName : String
2568
- let url : String
2619
+ let url : Foundation . URL
2569
2620
let checksum : String
2570
2621
}
2571
2622
2623
+ private struct ArchiveIndexFile : Decodable {
2624
+ let schemaVersion : String
2625
+ let archives : [ Archive ]
2626
+
2627
+ struct Archive : Decodable {
2628
+ let fileName : String
2629
+ let checksum : String
2630
+ let supportedTriples : [ Triple ]
2631
+
2632
+ enum CodingKeys : String , CodingKey {
2633
+ case fileName
2634
+ case checksum
2635
+ case supportedTriples
2636
+ }
2637
+
2638
+ public init ( from decoder: Decoder ) throws {
2639
+ let container = try decoder. container ( keyedBy: CodingKeys . self)
2640
+ self . fileName = try container. decode ( String . self, forKey: . fileName)
2641
+ self . checksum = try container. decode ( String . self, forKey: . checksum)
2642
+ self . supportedTriples = try container. decode ( [ String ] . self, forKey: . supportedTriples) . map ( Triple . init)
2643
+ }
2644
+ }
2645
+ }
2646
+
2572
2647
private extension ManagedArtifact {
2573
2648
var originURL : String ? {
2574
2649
switch self . source {
@@ -2596,3 +2671,11 @@ private extension PackageDependencyDescription {
2596
2671
}
2597
2672
}
2598
2673
}
2674
+
2675
+ private extension DiagnosticsEngine {
2676
+ func append( contentsOf other: DiagnosticsEngine ) {
2677
+ for diagnostic in other. diagnostics {
2678
+ self . emit ( diagnostic. message, location: diagnostic. location)
2679
+ }
2680
+ }
2681
+ }
0 commit comments