Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 30 additions & 4 deletions Sources/Basics/FileSystem/AbsolutePath.swift
Original file line number Diff line number Diff line change
Expand Up @@ -220,31 +220,57 @@ 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.
///
/// 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.
///
/// This method is strictly syntactic and does not access the file system
/// in any way.
public func isDescendant(of ancestor: AbsolutePath) -> Bool {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Might be worth renaming this to isSyntacticDescendant of since at a glance it looks like it's being misused in a few places where we should be considering symlinks, etc.

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.
///
/// 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)
}
}

Expand Down
6 changes: 5 additions & 1 deletion Sources/PackageLoading/PackageBuilder.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down