diff --git a/Package.resolved b/Package.resolved index 71dabee..ecff5d2 100644 --- a/Package.resolved +++ b/Package.resolved @@ -41,8 +41,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/tuist/XcodeProj.git", "state" : { - "revision" : "b6de1bfe021b861c94e7c83821b595083f74b997", - "version" : "8.8.0" + "revision" : "447c159b0c5fb047a024fd8d942d4a76cf47dde0", + "version" : "8.16.0" } } ], diff --git a/Package.swift b/Package.swift index 78fc674..238f906 100644 --- a/Package.swift +++ b/Package.swift @@ -11,7 +11,7 @@ let package = Package( ], dependencies: [ .package(url: "https://github.com/apple/swift-argument-parser", from: "1.2.0"), - .package(url: "https://github.com/tuist/XcodeProj.git", .upToNextMajor(from: "8.8.0")) + .package(url: "https://github.com/tuist/XcodeProj.git", .upToNextMajor(from: "8.16.0")) ], targets: [ .executableTarget(name: "Main", dependencies: [ @@ -226,6 +226,7 @@ let package = Package( ]), .testTarget(name: "XcodeProjectParserLiveTests", dependencies: [ "FileSystem", + "FileSystemLive", "XcodeProject", "XcodeProjectParserLive" ], resources: [.copy("Example")]) diff --git a/Sources/Library/Parsing/XcodeProjectParserLive/XcodeProjectParserLive.swift b/Sources/Library/Parsing/XcodeProjectParserLive/XcodeProjectParserLive.swift index 7e0560f..5382c0e 100644 --- a/Sources/Library/Parsing/XcodeProjectParserLive/XcodeProjectParserLive.swift +++ b/Sources/Library/Parsing/XcodeProjectParserLive/XcodeProjectParserLive.swift @@ -93,9 +93,10 @@ private extension PBXFileReference { } func potentialPackageSwiftFileURL(forSourceRoot sourceRoot: URL) -> URL? { - guard let path = path else { + guard let fullPath = try? fullPath(sourceRoot: sourceRoot.path) else { return nil } - return ((sourceRoot as NSURL).appendingPathComponent(path) as? NSURL)?.appendingPathComponent("Package.swift") + let packageUrl = URL(fileURLWithPath: fullPath) + return packageUrl.appendingPathComponent("Package.swift") } } diff --git a/Tests/ExampleProject/Example.xcodeproj/project.pbxproj b/Tests/ExampleProject/Example.xcodeproj/project.pbxproj index 19ea4e0..2918ac0 100644 --- a/Tests/ExampleProject/Example.xcodeproj/project.pbxproj +++ b/Tests/ExampleProject/Example.xcodeproj/project.pbxproj @@ -20,6 +20,7 @@ 72B5FC29293E2857007CA7EA /* Runestone in Frameworks */ = {isa = PBXBuildFile; productRef = 72B5FC28293E2857007CA7EA /* Runestone */; }; 72DBC59729404943001D5DEF /* ExampleLibraryA in Frameworks */ = {isa = PBXBuildFile; productRef = 72DBC59629404943001D5DEF /* ExampleLibraryA */; }; 72DBC59929404945001D5DEF /* ExampleLibraryB in Frameworks */ = {isa = PBXBuildFile; productRef = 72DBC59829404945001D5DEF /* ExampleLibraryB */; }; + AE5D64FD2D115AC700B6A28D /* TreeSitterJSONRunestone in Frameworks */ = {isa = PBXBuildFile; productRef = AE5D64FC2D115AC700B6A28D /* TreeSitterJSONRunestone */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -54,7 +55,8 @@ 72A7F377293AB6C2004FBAB5 /* ExampleUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ExampleUITests.swift; sourceTree = ""; }; 72A7F379293AB6C2004FBAB5 /* ExampleUITestsLaunchTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ExampleUITestsLaunchTests.swift; sourceTree = ""; }; 72A7F387293AB740004FBAB5 /* ExamplePackageA */ = {isa = PBXFileReference; lastKnownFileType = wrapper; path = ExamplePackageA; sourceTree = ""; }; - 72A7F38B293AB756004FBAB5 /* ExamplePackageB */ = {isa = PBXFileReference; lastKnownFileType = wrapper; path = ExamplePackageB; sourceTree = ""; }; + AE5D64FE2D115BE200B6A28D /* ExamplePackageB */ = {isa = PBXFileReference; lastKnownFileType = wrapper; path = ExamplePackageB; sourceTree = ""; }; + AE5D64FF2D115BE200B6A28D /* ExamplePackageC */ = {isa = PBXFileReference; lastKnownFileType = wrapper; path = ExamplePackageC; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -65,6 +67,7 @@ 72B5FC29293E2857007CA7EA /* Runestone in Frameworks */, 72DBC59929404945001D5DEF /* ExampleLibraryB in Frameworks */, 72B014E2294214E0007A98FF /* TreeSitterJavaScriptRunestone in Frameworks */, + AE5D64FD2D115AC700B6A28D /* TreeSitterJSONRunestone in Frameworks */, 72DBC59729404943001D5DEF /* ExampleLibraryA in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; @@ -89,6 +92,7 @@ 72A7F34A293AB6C1004FBAB5 = { isa = PBXGroup; children = ( + AE5D65002D115BE200B6A28D /* NestedPackages */, 72A7F386293AB740004FBAB5 /* Packages */, 72A7F355293AB6C1004FBAB5 /* Example */, 72A7F36C293AB6C2004FBAB5 /* ExampleTests */, @@ -143,7 +147,6 @@ isa = PBXGroup; children = ( 72A7F387293AB740004FBAB5 /* ExamplePackageA */, - 72A7F38B293AB756004FBAB5 /* ExamplePackageB */, ); name = Packages; sourceTree = ""; @@ -155,6 +158,15 @@ name = Frameworks; sourceTree = ""; }; + AE5D65002D115BE200B6A28D /* NestedPackages */ = { + isa = PBXGroup; + children = ( + AE5D64FE2D115BE200B6A28D /* ExamplePackageB */, + AE5D64FF2D115BE200B6A28D /* ExamplePackageC */, + ); + path = NestedPackages; + sourceTree = ""; + }; /* End PBXGroup section */ /* Begin PBXNativeTarget section */ @@ -176,6 +188,7 @@ 72DBC59629404943001D5DEF /* ExampleLibraryA */, 72DBC59829404945001D5DEF /* ExampleLibraryB */, 72B014E1294214E0007A98FF /* TreeSitterJavaScriptRunestone */, + AE5D64FC2D115AC700B6A28D /* TreeSitterJSONRunestone */, ); productName = Example; productReference = 72A7F353293AB6C1004FBAB5 /* Example.app */; @@ -679,6 +692,11 @@ isa = XCSwiftPackageProductDependency; productName = ExampleLibraryB; }; + AE5D64FC2D115AC700B6A28D /* TreeSitterJSONRunestone */ = { + isa = XCSwiftPackageProductDependency; + package = 72B014E0294214E0007A98FF /* XCRemoteSwiftPackageReference "TreeSitterLanguages" */; + productName = TreeSitterJSONRunestone; + }; /* End XCSwiftPackageProductDependency section */ }; rootObject = 72A7F34B293AB6C1004FBAB5 /* Project object */; diff --git a/Tests/ExampleProject/ExamplePackageB/.gitignore b/Tests/ExampleProject/NestedPackages/ExamplePackageB/.gitignore similarity index 100% rename from Tests/ExampleProject/ExamplePackageB/.gitignore rename to Tests/ExampleProject/NestedPackages/ExamplePackageB/.gitignore diff --git a/Tests/ExampleProject/ExamplePackageB/Package.swift b/Tests/ExampleProject/NestedPackages/ExamplePackageB/Package.swift similarity index 100% rename from Tests/ExampleProject/ExamplePackageB/Package.swift rename to Tests/ExampleProject/NestedPackages/ExamplePackageB/Package.swift diff --git a/Tests/ExampleProject/ExamplePackageB/Sources/ExampleLibraryB/Dummy.swift b/Tests/ExampleProject/NestedPackages/ExamplePackageB/Sources/ExampleLibraryB/Dummy.swift similarity index 100% rename from Tests/ExampleProject/ExamplePackageB/Sources/ExampleLibraryB/Dummy.swift rename to Tests/ExampleProject/NestedPackages/ExamplePackageB/Sources/ExampleLibraryB/Dummy.swift diff --git a/Tests/ExampleProject/ExamplePackageC/.gitignore b/Tests/ExampleProject/NestedPackages/ExamplePackageC/.gitignore similarity index 100% rename from Tests/ExampleProject/ExamplePackageC/.gitignore rename to Tests/ExampleProject/NestedPackages/ExamplePackageC/.gitignore diff --git a/Tests/ExampleProject/ExamplePackageC/Package.swift b/Tests/ExampleProject/NestedPackages/ExamplePackageC/Package.swift similarity index 100% rename from Tests/ExampleProject/ExamplePackageC/Package.swift rename to Tests/ExampleProject/NestedPackages/ExamplePackageC/Package.swift diff --git a/Tests/ExampleProject/ExamplePackageC/Sources/ExampleLibraryC/Dummy.swift b/Tests/ExampleProject/NestedPackages/ExamplePackageC/Sources/ExampleLibraryC/Dummy.swift similarity index 100% rename from Tests/ExampleProject/ExamplePackageC/Sources/ExampleLibraryC/Dummy.swift rename to Tests/ExampleProject/NestedPackages/ExamplePackageC/Sources/ExampleLibraryC/Dummy.swift diff --git a/Tests/XcodeProjectParserLiveTests/Example/Example.xcodeproj/project.pbxproj b/Tests/XcodeProjectParserLiveTests/Example/Example.xcodeproj/project.pbxproj index f6d5a60..2918ac0 100644 --- a/Tests/XcodeProjectParserLiveTests/Example/Example.xcodeproj/project.pbxproj +++ b/Tests/XcodeProjectParserLiveTests/Example/Example.xcodeproj/project.pbxproj @@ -16,11 +16,11 @@ 72A7F36E293AB6C2004FBAB5 /* ExampleTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 72A7F36D293AB6C2004FBAB5 /* ExampleTests.swift */; }; 72A7F378293AB6C2004FBAB5 /* ExampleUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 72A7F377293AB6C2004FBAB5 /* ExampleUITests.swift */; }; 72A7F37A293AB6C2004FBAB5 /* ExampleUITestsLaunchTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 72A7F379293AB6C2004FBAB5 /* ExampleUITestsLaunchTests.swift */; }; - 72B014E529421A15007A98FF /* TreeSitterJSONRunestone in Frameworks */ = {isa = PBXBuildFile; productRef = 72B014E429421A15007A98FF /* TreeSitterJSONRunestone */; }; - 72B014E729421A15007A98FF /* TreeSitterJavaScriptRunestone in Frameworks */ = {isa = PBXBuildFile; productRef = 72B014E629421A15007A98FF /* TreeSitterJavaScriptRunestone */; }; + 72B014E2294214E0007A98FF /* TreeSitterJavaScriptRunestone in Frameworks */ = {isa = PBXBuildFile; productRef = 72B014E1294214E0007A98FF /* TreeSitterJavaScriptRunestone */; }; 72B5FC29293E2857007CA7EA /* Runestone in Frameworks */ = {isa = PBXBuildFile; productRef = 72B5FC28293E2857007CA7EA /* Runestone */; }; 72DBC59729404943001D5DEF /* ExampleLibraryA in Frameworks */ = {isa = PBXBuildFile; productRef = 72DBC59629404943001D5DEF /* ExampleLibraryA */; }; 72DBC59929404945001D5DEF /* ExampleLibraryB in Frameworks */ = {isa = PBXBuildFile; productRef = 72DBC59829404945001D5DEF /* ExampleLibraryB */; }; + AE5D64FD2D115AC700B6A28D /* TreeSitterJSONRunestone in Frameworks */ = {isa = PBXBuildFile; productRef = AE5D64FC2D115AC700B6A28D /* TreeSitterJSONRunestone */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -55,7 +55,8 @@ 72A7F377293AB6C2004FBAB5 /* ExampleUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ExampleUITests.swift; sourceTree = ""; }; 72A7F379293AB6C2004FBAB5 /* ExampleUITestsLaunchTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ExampleUITestsLaunchTests.swift; sourceTree = ""; }; 72A7F387293AB740004FBAB5 /* ExamplePackageA */ = {isa = PBXFileReference; lastKnownFileType = wrapper; path = ExamplePackageA; sourceTree = ""; }; - 72A7F38B293AB756004FBAB5 /* ExamplePackageB */ = {isa = PBXFileReference; lastKnownFileType = wrapper; path = ExamplePackageB; sourceTree = ""; }; + AE5D64FE2D115BE200B6A28D /* ExamplePackageB */ = {isa = PBXFileReference; lastKnownFileType = wrapper; path = ExamplePackageB; sourceTree = ""; }; + AE5D64FF2D115BE200B6A28D /* ExamplePackageC */ = {isa = PBXFileReference; lastKnownFileType = wrapper; path = ExamplePackageC; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -65,9 +66,9 @@ files = ( 72B5FC29293E2857007CA7EA /* Runestone in Frameworks */, 72DBC59929404945001D5DEF /* ExampleLibraryB in Frameworks */, + 72B014E2294214E0007A98FF /* TreeSitterJavaScriptRunestone in Frameworks */, + AE5D64FD2D115AC700B6A28D /* TreeSitterJSONRunestone in Frameworks */, 72DBC59729404943001D5DEF /* ExampleLibraryA in Frameworks */, - 72B014E529421A15007A98FF /* TreeSitterJSONRunestone in Frameworks */, - 72B014E729421A15007A98FF /* TreeSitterJavaScriptRunestone in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -91,6 +92,7 @@ 72A7F34A293AB6C1004FBAB5 = { isa = PBXGroup; children = ( + AE5D65002D115BE200B6A28D /* NestedPackages */, 72A7F386293AB740004FBAB5 /* Packages */, 72A7F355293AB6C1004FBAB5 /* Example */, 72A7F36C293AB6C2004FBAB5 /* ExampleTests */, @@ -145,7 +147,6 @@ isa = PBXGroup; children = ( 72A7F387293AB740004FBAB5 /* ExamplePackageA */, - 72A7F38B293AB756004FBAB5 /* ExamplePackageB */, ); name = Packages; sourceTree = ""; @@ -157,6 +158,15 @@ name = Frameworks; sourceTree = ""; }; + AE5D65002D115BE200B6A28D /* NestedPackages */ = { + isa = PBXGroup; + children = ( + AE5D64FE2D115BE200B6A28D /* ExamplePackageB */, + AE5D64FF2D115BE200B6A28D /* ExamplePackageC */, + ); + path = NestedPackages; + sourceTree = ""; + }; /* End PBXGroup section */ /* Begin PBXNativeTarget section */ @@ -177,8 +187,8 @@ 72B5FC28293E2857007CA7EA /* Runestone */, 72DBC59629404943001D5DEF /* ExampleLibraryA */, 72DBC59829404945001D5DEF /* ExampleLibraryB */, - 72B014E429421A15007A98FF /* TreeSitterJSONRunestone */, - 72B014E629421A15007A98FF /* TreeSitterJavaScriptRunestone */, + 72B014E1294214E0007A98FF /* TreeSitterJavaScriptRunestone */, + AE5D64FC2D115AC700B6A28D /* TreeSitterJSONRunestone */, ); productName = Example; productReference = 72A7F353293AB6C1004FBAB5 /* Example.app */; @@ -254,7 +264,7 @@ mainGroup = 72A7F34A293AB6C1004FBAB5; packageReferences = ( 72B5FC27293E2857007CA7EA /* XCRemoteSwiftPackageReference "Runestone" */, - 72B014E329421A15007A98FF /* XCRemoteSwiftPackageReference "TreeSitterLanguages" */, + 72B014E0294214E0007A98FF /* XCRemoteSwiftPackageReference "TreeSitterLanguages" */, ); productRefGroup = 72A7F354293AB6C1004FBAB5 /* Products */; projectDirPath = ""; @@ -645,7 +655,7 @@ /* End XCConfigurationList section */ /* Begin XCRemoteSwiftPackageReference section */ - 72B014E329421A15007A98FF /* XCRemoteSwiftPackageReference "TreeSitterLanguages" */ = { + 72B014E0294214E0007A98FF /* XCRemoteSwiftPackageReference "TreeSitterLanguages" */ = { isa = XCRemoteSwiftPackageReference; repositoryURL = "git@github.com:simonbs/TreeSitterLanguages.git"; requirement = { @@ -664,14 +674,9 @@ /* End XCRemoteSwiftPackageReference section */ /* Begin XCSwiftPackageProductDependency section */ - 72B014E429421A15007A98FF /* TreeSitterJSONRunestone */ = { + 72B014E1294214E0007A98FF /* TreeSitterJavaScriptRunestone */ = { isa = XCSwiftPackageProductDependency; - package = 72B014E329421A15007A98FF /* XCRemoteSwiftPackageReference "TreeSitterLanguages" */; - productName = TreeSitterJSONRunestone; - }; - 72B014E629421A15007A98FF /* TreeSitterJavaScriptRunestone */ = { - isa = XCSwiftPackageProductDependency; - package = 72B014E329421A15007A98FF /* XCRemoteSwiftPackageReference "TreeSitterLanguages" */; + package = 72B014E0294214E0007A98FF /* XCRemoteSwiftPackageReference "TreeSitterLanguages" */; productName = TreeSitterJavaScriptRunestone; }; 72B5FC28293E2857007CA7EA /* Runestone */ = { @@ -687,6 +692,11 @@ isa = XCSwiftPackageProductDependency; productName = ExampleLibraryB; }; + AE5D64FC2D115AC700B6A28D /* TreeSitterJSONRunestone */ = { + isa = XCSwiftPackageProductDependency; + package = 72B014E0294214E0007A98FF /* XCRemoteSwiftPackageReference "TreeSitterLanguages" */; + productName = TreeSitterJSONRunestone; + }; /* End XCSwiftPackageProductDependency section */ }; rootObject = 72A7F34B293AB6C1004FBAB5 /* Project object */; diff --git a/Tests/XcodeProjectParserLiveTests/Example/ExamplePackageB/.gitignore b/Tests/XcodeProjectParserLiveTests/Example/NestedPackages/ExamplePackageB/.gitignore similarity index 100% rename from Tests/XcodeProjectParserLiveTests/Example/ExamplePackageB/.gitignore rename to Tests/XcodeProjectParserLiveTests/Example/NestedPackages/ExamplePackageB/.gitignore diff --git a/Tests/XcodeProjectParserLiveTests/Example/ExamplePackageB/Package.swift b/Tests/XcodeProjectParserLiveTests/Example/NestedPackages/ExamplePackageB/Package.swift similarity index 100% rename from Tests/XcodeProjectParserLiveTests/Example/ExamplePackageB/Package.swift rename to Tests/XcodeProjectParserLiveTests/Example/NestedPackages/ExamplePackageB/Package.swift diff --git a/Tests/XcodeProjectParserLiveTests/Example/ExamplePackageC/.gitignore b/Tests/XcodeProjectParserLiveTests/Example/NestedPackages/ExamplePackageC/.gitignore similarity index 100% rename from Tests/XcodeProjectParserLiveTests/Example/ExamplePackageC/.gitignore rename to Tests/XcodeProjectParserLiveTests/Example/NestedPackages/ExamplePackageC/.gitignore diff --git a/Tests/XcodeProjectParserLiveTests/Example/ExamplePackageC/Package.swift b/Tests/XcodeProjectParserLiveTests/Example/NestedPackages/ExamplePackageC/Package.swift similarity index 100% rename from Tests/XcodeProjectParserLiveTests/Example/ExamplePackageC/Package.swift rename to Tests/XcodeProjectParserLiveTests/Example/NestedPackages/ExamplePackageC/Package.swift diff --git a/Tests/XcodeProjectParserLiveTests/XcodeProjectParserLiveTests.swift b/Tests/XcodeProjectParserLiveTests/XcodeProjectParserLiveTests.swift index 5d7ef9f..2daebde 100644 --- a/Tests/XcodeProjectParserLiveTests/XcodeProjectParserLiveTests.swift +++ b/Tests/XcodeProjectParserLiveTests/XcodeProjectParserLiveTests.swift @@ -1,5 +1,6 @@ @testable import XcodeProject @testable import XcodeProjectParserLive +import FileSystemLive import XCTest final class XcodeProjectParserLiveTests: XCTestCase { @@ -33,9 +34,11 @@ final class XcodeProjectParserLiveTests: XCTestCase { } func testSwiftPackageCount() throws { - let parser = XcodeProjectParserLive(fileSystem: FileSystemMock()) + // The example project contains 5 Swift packages: + // 2 remote and 3 local (2 of which are nested in group) + let parser = XcodeProjectParserLive(fileSystem: FileSystemLive()) let xcodeProject = try parser.parseProject(at: URL.Mock.exampleXcodeProject) - XCTAssertEqual(xcodeProject.swiftPackages.count, 4) + XCTAssertEqual(xcodeProject.swiftPackages.count, 5) } func testParsesLocalSwiftPackage() throws { @@ -51,6 +54,20 @@ final class XcodeProjectParserLiveTests: XCTestCase { XCTFail("Expected ExamplePackageA to be a local package") } } + + func testParsesLocalSwiftPackageInNestedGroupDirectory() throws { + let parser = XcodeProjectParserLive(fileSystem: FileSystemLive()) + let xcodeProject = try parser.parseProject(at: URL.Mock.exampleXcodeProject) + let swiftPackage = xcodeProject.swiftPackages.first { $0.name == "ExamplePackageB" } + XCTAssertNotNil(swiftPackage) + if case let .local(parameters) = swiftPackage { + XCTAssertEqual(parameters.name, "ExamplePackageB") + let fileURLHasPackageSwiftSuffix = parameters.fileURL.absoluteString.hasSuffix("NestedPackages/ExamplePackageB/Package.swift") + XCTAssertTrue(fileURLHasPackageSwiftSuffix, "Expected file URL to end with the package name and Package.swift") + } else { + XCTFail("Expected ExamplePackageB to be a local package") + } + } func testParsesRemoteSwiftPackageWithSingleProduct() throws { let parser = XcodeProjectParserLive(fileSystem: FileSystemMock())