12
12
13
13
import Basics
14
14
import Foundation
15
+ import PackageLoading
15
16
import PackageModel
16
17
import SPMBuildCore
17
- import PackageLoading
18
18
19
19
import struct TSCBasic. ByteString
20
20
import protocol TSCBasic. HashAlgorithm
@@ -696,7 +696,8 @@ extension Workspace.BinaryArtifactsManager {
696
696
_ = try decoder. decode ( XCFrameworkMetadata . self, from: fileSystem. readFileContents ( infoPlist) )
697
697
return . xcframework
698
698
} catch {
699
- observabilityScope. emit ( debug: " info.plist found in ' \( path) ' but failed to parse: \( error. interpolationDescription) " )
699
+ observabilityScope
700
+ . emit ( debug: " info.plist found in ' \( path) ' but failed to parse: \( error. interpolationDescription) " )
700
701
}
701
702
}
702
703
@@ -716,6 +717,156 @@ extension Workspace.BinaryArtifactsManager {
716
717
}
717
718
}
718
719
720
+ extension Workspace {
721
+ func updateBinaryArtifacts(
722
+ manifests: DependencyManifests ,
723
+ addedOrUpdatedPackages: [ PackageReference ] ,
724
+ observabilityScope: ObservabilityScope
725
+ ) throws {
726
+ let manifestArtifacts = try self . binaryArtifactsManager. parseArtifacts (
727
+ from: manifests,
728
+ observabilityScope: observabilityScope
729
+ )
730
+
731
+ var artifactsToRemove : [ ManagedArtifact ] = [ ]
732
+ var artifactsToAdd : [ ManagedArtifact ] = [ ]
733
+ var artifactsToDownload : [ BinaryArtifactsManager . RemoteArtifact ] = [ ]
734
+ var artifactsToExtract : [ ManagedArtifact ] = [ ]
735
+
736
+ for artifact in state. artifacts {
737
+ if !manifestArtifacts. local
738
+ . contains ( where: { $0. packageRef == artifact. packageRef && $0. targetName == artifact. targetName } ) &&
739
+ !manifestArtifacts. remote
740
+ . contains ( where: { $0. packageRef == artifact. packageRef && $0. targetName == artifact. targetName } )
741
+ {
742
+ artifactsToRemove. append ( artifact)
743
+ }
744
+ }
745
+
746
+ for artifact in manifestArtifacts. local {
747
+ let existingArtifact = self . state. artifacts [
748
+ packageIdentity: artifact. packageRef. identity,
749
+ targetName: artifact. targetName
750
+ ]
751
+
752
+ if artifact. path. extension? . lowercased ( ) == " zip " {
753
+ // If we already have an artifact that was extracted from an archive with the same checksum,
754
+ // we don't need to extract the artifact again.
755
+ if case . local( let existingChecksum) = existingArtifact? . source,
756
+ try existingChecksum == ( self . binaryArtifactsManager. checksum ( forBinaryArtifactAt: artifact. path) )
757
+ {
758
+ continue
759
+ }
760
+
761
+ artifactsToExtract. append ( artifact)
762
+ } else {
763
+ guard let _ = try BinaryArtifactsManager . deriveBinaryArtifact (
764
+ fileSystem: self . fileSystem,
765
+ path: artifact. path,
766
+ observabilityScope: observabilityScope
767
+ ) else {
768
+ observabilityScope. emit ( . localArtifactNotFound(
769
+ artifactPath: artifact. path,
770
+ targetName: artifact. targetName
771
+ ) )
772
+ continue
773
+ }
774
+ artifactsToAdd. append ( artifact)
775
+ }
776
+
777
+ if let existingArtifact, isAtArtifactsDirectory ( existingArtifact) {
778
+ // Remove the old extracted artifact, be it local archived or remote one.
779
+ artifactsToRemove. append ( existingArtifact)
780
+ }
781
+ }
782
+
783
+ for artifact in manifestArtifacts. remote {
784
+ let existingArtifact = self . state. artifacts [
785
+ packageIdentity: artifact. packageRef. identity,
786
+ targetName: artifact. targetName
787
+ ]
788
+
789
+ if let existingArtifact {
790
+ if case . remote( let existingURL, let existingChecksum) = existingArtifact. source {
791
+ // If we already have an artifact with the same checksum, we don't need to download it again.
792
+ if artifact. checksum == existingChecksum {
793
+ continue
794
+ }
795
+
796
+ let urlChanged = artifact. url != URL ( string: existingURL)
797
+ // If the checksum is different but the package wasn't updated, this is a security risk.
798
+ if !urlChanged && !addedOrUpdatedPackages. contains ( artifact. packageRef) {
799
+ observabilityScope. emit ( . artifactChecksumChanged( targetName: artifact. targetName) )
800
+ continue
801
+ }
802
+ }
803
+
804
+ if isAtArtifactsDirectory ( existingArtifact) {
805
+ // Remove the old extracted artifact, be it local archived or remote one.
806
+ artifactsToRemove. append ( existingArtifact)
807
+ }
808
+ }
809
+
810
+ artifactsToDownload. append ( artifact)
811
+ }
812
+
813
+ // Remove the artifacts and directories which are not needed anymore.
814
+ observabilityScope. trap {
815
+ for artifact in artifactsToRemove {
816
+ state. artifacts. remove ( packageIdentity: artifact. packageRef. identity, targetName: artifact. targetName)
817
+
818
+ if isAtArtifactsDirectory ( artifact) {
819
+ try fileSystem. removeFileTree ( artifact. path)
820
+ }
821
+ }
822
+
823
+ for directory in try fileSystem. getDirectoryContents ( self . location. artifactsDirectory) {
824
+ let directoryPath = self . location. artifactsDirectory. appending ( component: directory)
825
+ if try fileSystem. isDirectory ( directoryPath) && fileSystem. getDirectoryContents ( directoryPath) . isEmpty {
826
+ try fileSystem. removeFileTree ( directoryPath)
827
+ }
828
+ }
829
+ }
830
+
831
+ guard !observabilityScope. errorsReported else {
832
+ throw Diagnostics . fatalError
833
+ }
834
+
835
+ // Download the artifacts
836
+ let downloadedArtifacts = try self . binaryArtifactsManager. download (
837
+ artifactsToDownload,
838
+ artifactsDirectory: self . location. artifactsDirectory,
839
+ observabilityScope: observabilityScope
840
+ )
841
+ artifactsToAdd. append ( contentsOf: downloadedArtifacts)
842
+
843
+ // Extract the local archived artifacts
844
+ let extractedLocalArtifacts = try self . binaryArtifactsManager. extract (
845
+ artifactsToExtract,
846
+ artifactsDirectory: self . location. artifactsDirectory,
847
+ observabilityScope: observabilityScope
848
+ )
849
+ artifactsToAdd. append ( contentsOf: extractedLocalArtifacts)
850
+
851
+ // Add the new artifacts
852
+ for artifact in artifactsToAdd {
853
+ self . state. artifacts. add ( artifact)
854
+ }
855
+
856
+ guard !observabilityScope. errorsReported else {
857
+ throw Diagnostics . fatalError
858
+ }
859
+
860
+ observabilityScope. trap {
861
+ try self . state. save ( )
862
+ }
863
+
864
+ func isAtArtifactsDirectory( _ artifact: ManagedArtifact ) -> Bool {
865
+ artifact. path. isDescendant ( of: self . location. artifactsDirectory)
866
+ }
867
+ }
868
+ }
869
+
719
870
extension FileSystem {
720
871
// helper to decide if an archive directory would benefit from stripping first level
721
872
fileprivate func shouldStripFirstLevel(
0 commit comments