From 7e9fe2977666e47a7ff7dadfa2dd107bf049e9a5 Mon Sep 17 00:00:00 2001 From: fortmarek Date: Thu, 6 Feb 2025 11:56:40 +0100 Subject: [PATCH] feat: support selective testing --- Sources/XcodeGraph/Graph/Graph.swift | 2 +- Sources/XcodeGraph/Models/Target.swift | 15 +++++- .../Mappers/Graph/XcodeGraphMapper.swift | 11 +++-- .../PBXFrameworksBuildPhaseMapper.swift | 12 +++++ .../Mappers/Schemes/XCSchemeMapper.swift | 2 +- .../Mappers/Targets/PBXTargetMapper.swift | 49 +++++++++++++++++-- 6 files changed, 80 insertions(+), 11 deletions(-) diff --git a/Sources/XcodeGraph/Graph/Graph.swift b/Sources/XcodeGraph/Graph/Graph.swift index 5f749c82..8cdf72d7 100644 --- a/Sources/XcodeGraph/Graph/Graph.swift +++ b/Sources/XcodeGraph/Graph/Graph.swift @@ -68,7 +68,7 @@ extension [GraphEdge: PlatformCondition] { #if DEBUG extension Graph { - static func test( + public static func test( name: String = "graph", path: AbsolutePath = .root, workspace: Workspace = .test(), diff --git a/Sources/XcodeGraph/Models/Target.swift b/Sources/XcodeGraph/Models/Target.swift index bd86c180..438f5166 100644 --- a/Sources/XcodeGraph/Models/Target.swift +++ b/Sources/XcodeGraph/Models/Target.swift @@ -8,10 +8,21 @@ public struct Target: Equatable, Hashable, Comparable, Codable, Sendable { // Note: The `.docc` file type is technically both a valid source extension and folder extension // in order to compile the documentation archive (including Tutorials, Articles, etc.) public static let validSourceCompatibleFolderExtensions: [String] = [ - "playground", "rcproject", "mlpackage", "docc", "xcmappingmodel", + "playground", "rcproject", "mlpackage", "docc", "xcmappingmodel", "xcdatamodeld", ] public static let validSourceExtensions: [String] = [ - "m", "swift", "mm", "cpp", "c++", "cc", "c", "d", "s", "intentdefinition", "metal", "mlmodel", + "m", "swift", "mm", "cpp", "c++", "cc", "c", "d", "s", "intentdefinition", "metal", "mlmodel", "clp", + ] + public static let validResourceExtensions: [String] = [ + // Resource + "md", "xcstrings", "plist", "rtf", "tutorial", "sks", "xcprivacy", "gpx", "strings", "stringsdict", "geojson", + // User interface + "storyboard", "xib", + // Other + "xcfilelist", "xcconfig", + ] + public static let validResourceCompatibleFolderExtensions: [String] = [ + "xcassets", "scnassets", "bundle", "xcstickers", "app", ] public static let validFolderExtensions: [String] = [ "framework", "bundle", "app", "xcassets", "appiconset", "scnassets", diff --git a/Sources/XcodeGraphMapper/Mappers/Graph/XcodeGraphMapper.swift b/Sources/XcodeGraphMapper/Mappers/Graph/XcodeGraphMapper.swift index 8dc56b3b..49fb8bdc 100644 --- a/Sources/XcodeGraphMapper/Mappers/Graph/XcodeGraphMapper.swift +++ b/Sources/XcodeGraphMapper/Mappers/Graph/XcodeGraphMapper.swift @@ -173,7 +173,7 @@ public struct XcodeGraphMapper: XcodeGraphMapping { let xcodeProj = try XcodeProj(pathString: path.pathString) let projectMapper = PBXProjectMapper() let project = try await projectMapper.map(xcodeProj: xcodeProj) - projects[path] = project + projects[path.parentDirectory] = project } return projects @@ -209,7 +209,12 @@ public struct XcodeGraphMapper: XcodeGraphMapping { for (path, project) in projects { for (name, target) in project.targets { - let sourceDependency = GraphDependency.target(name: name, path: path.parentDirectory) + let sourceDependency = GraphDependency.target(name: name, path: path) + print("Adding dependency") + print(name) + print(path) + print(path.parentDirectory) + print("---") // Build edges for each target dependency let edgesAndDeps = try await target.dependencies.serialCompactMap { (dep: TargetDependency) async throws -> ( @@ -218,7 +223,7 @@ public struct XcodeGraphMapper: XcodeGraphMapping { GraphDependency ) in let graphDep = try await dep.graphDependency( - sourceDirectory: path.parentDirectory, + sourceDirectory: path, allTargetsMap: allTargetsMap, target: target ) diff --git a/Sources/XcodeGraphMapper/Mappers/Phases/PBXFrameworksBuildPhaseMapper.swift b/Sources/XcodeGraphMapper/Mappers/Phases/PBXFrameworksBuildPhaseMapper.swift index 6bd5b64b..e87851ab 100644 --- a/Sources/XcodeGraphMapper/Mappers/Phases/PBXFrameworksBuildPhaseMapper.swift +++ b/Sources/XcodeGraphMapper/Mappers/Phases/PBXFrameworksBuildPhaseMapper.swift @@ -42,6 +42,18 @@ struct PBXFrameworksBuildPhaseMapper: PBXFrameworksBuildPhaseMapping { xcodeProj: XcodeProj ) throws -> TargetDependency { let fileRef = try buildFile.file.throwing(PBXFrameworksBuildPhaseMappingError.missingFileReference) + switch fileRef.sourceTree { + case .buildProductsDir: + guard let path = fileRef.path else { break } + let name = path.replacingOccurrences(of: ".framework", with: "") + return .target( + name: name, + status: .required, + condition: nil + ) + default: + break + } let filePathString = try fileRef.fullPath(sourceRoot: xcodeProj.srcPathString) .throwing(PBXFrameworksBuildPhaseMappingError.missingFilePath(name: fileRef.name)) diff --git a/Sources/XcodeGraphMapper/Mappers/Schemes/XCSchemeMapper.swift b/Sources/XcodeGraphMapper/Mappers/Schemes/XCSchemeMapper.swift index eeda5bdb..3337bad2 100644 --- a/Sources/XcodeGraphMapper/Mappers/Schemes/XCSchemeMapper.swift +++ b/Sources/XcodeGraphMapper/Mappers/Schemes/XCSchemeMapper.swift @@ -200,7 +200,7 @@ struct XCSchemeMapper: SchemeMapping { let relPath = try RelativePath(validating: relativeContainerPath) projectPath = xcworkspace.workspacePath.parentDirectory.appending(relPath) case let .project(xcodeProj): - projectPath = xcodeProj.projectPath + projectPath = xcodeProj.projectPath.parentDirectory } return TargetReference(projectPath: projectPath, name: targetName) diff --git a/Sources/XcodeGraphMapper/Mappers/Targets/PBXTargetMapper.swift b/Sources/XcodeGraphMapper/Mappers/Targets/PBXTargetMapper.swift index 432d0c65..9e38e92b 100644 --- a/Sources/XcodeGraphMapper/Mappers/Targets/PBXTargetMapper.swift +++ b/Sources/XcodeGraphMapper/Mappers/Targets/PBXTargetMapper.swift @@ -64,6 +64,7 @@ struct PBXTargetMapper: PBXTargetMapping { private let frameworksMapper: PBXFrameworksBuildPhaseMapping private let dependencyMapper: PBXTargetDependencyMapping private let buildRuleMapper: BuildRuleMapping + private let fileSystem: FileSysteming init( settingsMapper: SettingsMapping = XCConfigurationMapper(), @@ -75,7 +76,8 @@ struct PBXTargetMapper: PBXTargetMapping { coreDataModelsMapper: PBXCoreDataModelsBuildPhaseMapping = PBXCoreDataModelsBuildPhaseMapper(), frameworksMapper: PBXFrameworksBuildPhaseMapping = PBXFrameworksBuildPhaseMapper(), dependencyMapper: PBXTargetDependencyMapping = PBXTargetDependencyMapper(), - buildRuleMapper: BuildRuleMapping = PBXBuildRuleMapper() + buildRuleMapper: BuildRuleMapping = PBXBuildRuleMapper(), + fileSystem: FileSysteming = FileSystem() ) { self.settingsMapper = settingsMapper self.sourcesMapper = sourcesMapper @@ -87,6 +89,7 @@ struct PBXTargetMapper: PBXTargetMapping { self.frameworksMapper = frameworksMapper self.dependencyMapper = dependencyMapper self.buildRuleMapper = buildRuleMapper + self.fileSystem = fileSystem } func map(pbxTarget: PBXTarget, xcodeProj: XcodeProj) async throws -> Target { @@ -102,13 +105,51 @@ struct PBXTargetMapper: PBXTargetMapping { ) // Build Phases - let sources = try pbxTarget.sourcesBuildPhase().map { + var sources = try pbxTarget.sourcesBuildPhase().map { try sourcesMapper.map($0, xcodeProj: xcodeProj) } ?? [] - - let resources = try pbxTarget.resourcesBuildPhase().map { + + var resources = try pbxTarget.resourcesBuildPhase().map { try resourcesMapper.map($0, xcodeProj: xcodeProj) } ?? [] + + if let fileSystemSynchronizedGroups = pbxTarget.fileSystemSynchronizedGroups { + for fileSystemSynchronizedGroup in try fileSystemSynchronizedGroups { + if let path = fileSystemSynchronizedGroup.path { + let groupSources = try await fileSystem.glob( + directory: xcodeProj.srcPath.appending(component: path), + include: [ + "**/*.{\(Target.validSourceExtensions.joined(separator: ","))}", + "**/*.{\(Target.validSourceCompatibleFolderExtensions.joined(separator: ","))}", + ] + ) + .collect() + .map { SourceFile(path: $0) } + sources.append(contentsOf: groupSources) + +// print( +// try await fileSystem.glob( +// directory: xcodeProj.srcPath.appending(component: path), +// include: ["**/*.xcassets"] +// ) +// .collect() +// ) + + let groupResources = try await fileSystem.glob( + directory: xcodeProj.srcPath.appending(component: path), + include: [ + "**/*.{\(Target.validResourceExtensions.joined(separator: ","))}", + "**/*.{\(Target.validResourceCompatibleFolderExtensions.joined(separator: ","))}", + ] + ) + .collect() + .map { + ResourceFileElement(path: $0) + } + resources.append(contentsOf: groupResources) + } + } + } let headers = try pbxTarget.headersBuildPhase().map { try headersMapper.map($0, xcodeProj: xcodeProj)