diff --git a/Sources/Basics/FileSystem/AbsolutePath.swift b/Sources/Basics/FileSystem/AbsolutePath.swift index c472e5c0608..08c1237f49a 100644 --- a/Sources/Basics/FileSystem/AbsolutePath.swift +++ b/Sources/Basics/FileSystem/AbsolutePath.swift @@ -220,7 +220,7 @@ extension AbsolutePath { /// This method is strictly syntactic and does not access the file system /// in any way. public func isAncestor(of descendant: AbsolutePath) -> Bool { - self.underlying.isAncestor(of: descendant.underlying) + descendant.isDescendant(of: self) } /// Returns true if the path is an ancestor of or equal to the given path. @@ -228,7 +228,7 @@ extension AbsolutePath { /// This method is strictly syntactic and does not access the file system /// in any way. public func isAncestorOfOrEqual(to descendant: AbsolutePath) -> Bool { - self.underlying.isAncestorOfOrEqual(to: descendant.underlying) + descendant.isDescendantOfOrEqual(to: self) } /// Returns true if the path is a descendant of the given path. @@ -236,7 +236,30 @@ extension AbsolutePath { /// This method is strictly syntactic and does not access the file system /// in any way. public func isDescendant(of ancestor: AbsolutePath) -> Bool { - self.underlying.isDescendant(of: ancestor.underlying) + let selfPath = self.pathString + let ancestorPath = ancestor.pathString + + guard selfPath.count > ancestorPath.count else { + return false + } + + guard selfPath.hasPrefix(ancestorPath) else { + return false + } + +#if os(Windows) + let pathSeparator: Character = "\\" +#else + let pathSeparator: Character = "/" +#endif + + if ancestorPath.hasSuffix(String(pathSeparator)) { + return true + } + + // Handle non-root paths by ensuring the character after the prefix is a separator + let indexAfterPrefix = selfPath.index(selfPath.startIndex, offsetBy: ancestorPath.count) + return selfPath[indexAfterPrefix] == pathSeparator } /// Returns true if the path is a descendant of or equal to the given path. @@ -244,7 +267,10 @@ extension AbsolutePath { /// This method is strictly syntactic and does not access the file system /// in any way. public func isDescendantOfOrEqual(to ancestor: AbsolutePath) -> Bool { - self.underlying.isDescendantOfOrEqual(to: ancestor.underlying) + if self == ancestor { + return true + } + return self.isDescendant(of: ancestor) } } diff --git a/Sources/PackageLoading/PackageBuilder.swift b/Sources/PackageLoading/PackageBuilder.swift index 77169042aa0..5d544f4324a 100644 --- a/Sources/PackageLoading/PackageBuilder.swift +++ b/Sources/PackageLoading/PackageBuilder.swift @@ -992,7 +992,11 @@ public final class PackageBuilder { let publicHeaderComponent = manifestTarget.publicHeadersPath ?? ClangModule.defaultPublicHeadersComponent let publicHeadersPath = try potentialModule.path.appending(RelativePath(validating: publicHeaderComponent)) guard publicHeadersPath.isDescendantOfOrEqual(to: potentialModule.path) else { - throw ModuleError.invalidPublicHeadersDirectory(potentialModule.name) + let publicHeadersPathString = publicHeadersPath.pathString + let potentialModulePathString = potentialModule.path.pathString + let potentialModuleName = potentialModule.name + let errorMessage = "\(publicHeadersPathString) isDescendantOfOrEqual \(potentialModulePathString) - potentialModule.name(\(potentialModuleName))" + throw ModuleError.invalidPublicHeadersDirectory(errorMessage) } let sourcesBuilder = TargetSourcesBuilder(