Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Compute relative paths to support sources outside a spec's directory #524

Merged
merged 7 commits into from
Feb 26, 2019

Conversation

elliottwilliams
Copy link
Contributor

Closes #318

Previously, XcodeGen would determine the relative path to a source by removing the project's basePath from the source's path, but this doesn't work when the source's path is outside the base path. For example, given a base path /a/b and a path to a source ../c, XcodeGen would unsuccessfully try to remove "/a/b" from "/a/c" and wind up creating a "relative" file reference to "/a/c".

This change adds a method to Path that computes the relative path between two paths, inspired by methods like Pathname#relative_path_from in Ruby, and uses it to determine the paths for top-level references in the project.

I'm trying to decide whether I should really be contributing this to PathKit, since this solves a generalizable path problem, but since it fixes a specific bug in XcodeGen I wanted to put it up here. I'm happy to open a PR on PathKit in addition to this one, so that you don't get stuck maintaining a pathing algorithm 🙂

Copy link
Owner

@yonaskolb yonaskolb left a comment

Choose a reason for hiding this comment

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

Fantastic, thank you for this @elliottwilliams! Nice work 👏

As you say it would be good to have this in PathKit itself, so I would encourage you to open a PR there as well. That doesn't stop us from adding this here for now though.

Could you also please add a changelog entry

Sources/XcodeGenKit/PathExtensions.swift Outdated Show resolved Hide resolved
Sources/XcodeGenKit/SourceGenerator.swift Show resolved Hide resolved
This uncovered a seeming inconsisteny with how folder reference paths
were specified vs all other paths.
@@ -474,7 +474,7 @@ class SourceGenerator {
var sourcePath = path
switch type {
case .folder:
let folderPath = Path(targetSource.path)
let folderPath = project.basePath + Path(targetSource.path)
Copy link
Contributor Author

Choose a reason for hiding this comment

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

I had to change this line to fix a test that broke when I swapped out byRemovingBase(path:) with relativePath(from:).

It seems like folder target sources were being created without the base directory as part of the path. In the tests, I saw that a folder "Sources/A" would passed to getFileReference as Path("Sources/A"), but a file "Sources/a.swift" would be passed as Path("TestDirectory/Sources/a.swift").

@elliottwilliams
Copy link
Contributor Author

Thanks! It was fun to write :) Changelog updated.

/// - `../a/b` simplifies to `../a/b`
/// - `a/../../c` simplifies to `../c`
public func simplifyingParentDirectoryReferences() -> Path {
return normalize().components.reduce(Path(), +)
Copy link
Owner

Choose a reason for hiding this comment

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

Does normalize just do the same thing? What does adding the components together again do?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

normalize calls NSString.standardizingPath, which almost does the same thing but doesn't remove parent directory references in relative paths:

For absolute paths only, resolving references to the parent directory (that is, the component “..”) to the real parent directory if possible using resolvingSymlinksInPath. For relative paths, references to the parent directory are left in place.

PathKit's + operator backs up a level if you try to add .. to a path, which means it can handle this normalization edge case for us. In other words:

Path("foo/../bar").normalize() == Path("foo/../bar")
Path("foo") + ".." + "bar" == Path("bar")

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Add sources outside project file's parent folder?
2 participants