@@ -495,6 +495,7 @@ public class InMemoryFileSystem: FileSystem {
495495 private enum NodeContents {
496496 case file( ByteString )
497497 case directory( DirectoryContents )
498+ case symlink( AbsolutePath )
498499
499500 /// Creates deep copy of the object.
500501 func copy( ) -> NodeContents {
@@ -503,6 +504,8 @@ public class InMemoryFileSystem: FileSystem {
503504 return . file( bytes)
504505 case . directory( let contents) :
505506 return . directory( contents. copy ( ) )
507+ case . symlink( let path) :
508+ return . symlink( path)
506509 }
507510 }
508511 }
@@ -550,7 +553,7 @@ public class InMemoryFileSystem: FileSystem {
550553 }
551554
552555 /// Get the node corresponding to the given path.
553- private func getNode( _ path: AbsolutePath ) throws -> Node ? {
556+ private func getNode( _ path: AbsolutePath , followSymlink : Bool = true ) throws -> Node ? {
554557 func getNodeInternal( _ path: AbsolutePath ) throws -> Node ? {
555558 // If this is the root node, return it.
556559 if path. isRoot {
@@ -568,7 +571,16 @@ public class InMemoryFileSystem: FileSystem {
568571 }
569572
570573 // Return the directory entry.
571- return contents. entries [ path. basename]
574+ let node = contents. entries [ path. basename]
575+
576+ switch node? . contents {
577+ case . directory, . file:
578+ return node
579+ case . symlink( let destination) :
580+ return followSymlink ? try getNodeInternal ( destination) : node
581+ case . none:
582+ return nil
583+ }
572584 }
573585
574586 // Get the node that corresponds to the path.
@@ -579,7 +591,10 @@ public class InMemoryFileSystem: FileSystem {
579591
580592 public func exists( _ path: AbsolutePath , followSymlink: Bool ) -> Bool {
581593 do {
582- return try getNode ( path) != nil
594+ switch try getNode ( path, followSymlink: followSymlink) ? . contents {
595+ case . file, . directory, . symlink: return true
596+ case . none: return false
597+ }
583598 } catch {
584599 return false
585600 }
@@ -608,9 +623,14 @@ public class InMemoryFileSystem: FileSystem {
608623 }
609624
610625 public func isSymlink( _ path: AbsolutePath ) -> Bool {
611- // FIXME: Always return false until in-memory implementation
612- // gets symbolic link semantics.
613- return false
626+ do {
627+ if case . symlink? = try getNode ( path, followSymlink: false ) ? . contents {
628+ return true
629+ }
630+ return false
631+ } catch {
632+ return false
633+ }
614634 }
615635
616636 public func isExecutableFile( _ path: AbsolutePath ) -> Bool {
@@ -689,6 +709,24 @@ public class InMemoryFileSystem: FileSystem {
689709 contents. entries [ path. basename] = Node ( . directory( DirectoryContents ( ) ) )
690710 }
691711
712+ public func createSymbolicLink( _ path: AbsolutePath , pointingAt destination: AbsolutePath ) throws {
713+ // Create directory to destination parent.
714+ guard let destinationParent = try getNode ( path. parentDirectory) else {
715+ throw FileSystemError . noEntry
716+ }
717+
718+ // Check that the parent is a directory.
719+ guard case . directory( let contents) = destinationParent. contents else {
720+ throw FileSystemError . notDirectory
721+ }
722+
723+ guard contents. entries [ path. basename] == nil else {
724+ throw FileSystemError . alreadyExistsAtDestination
725+ }
726+
727+ contents. entries [ path. basename] = Node ( . symlink( destination) )
728+ }
729+
692730 public func readFileContents( _ path: AbsolutePath ) throws -> ByteString {
693731 // Get the node.
694732 guard let node = try getNode ( path) else {
@@ -797,13 +835,20 @@ public class InMemoryFileSystem: FileSystem {
797835 }
798836
799837 public func withLock< T> ( on path: AbsolutePath , type: FileLock . LockType = . exclusive, _ body: ( ) throws -> T ) throws -> T {
838+ let resolvedPath : AbsolutePath
839+
840+ if case let . symlink( destination) = try getNode ( path) ? . contents {
841+ resolvedPath = destination
842+ } else {
843+ resolvedPath = path
844+ }
800845
801846 let fileQueue : DispatchQueue = lockFilesLock. withLock {
802- if let queueReference = lockFiles [ path ] , let queue = queueReference. reference {
847+ if let queueReference = lockFiles [ resolvedPath ] , let queue = queueReference. reference {
803848 return queue
804849 } else {
805850 let queue = DispatchQueue ( label: " org.swift.swiftpm.in-memory-file-system.file-queue " , attributes: . concurrent)
806- lockFiles [ path ] = WeakReference ( queue)
851+ lockFiles [ resolvedPath ] = WeakReference ( queue)
807852 return queue
808853 }
809854 }
0 commit comments