From 351abe7d6aebe15264bc71d171a0f451b591beda Mon Sep 17 00:00:00 2001 From: NinjaLikesCheez Date: Tue, 7 May 2024 10:56:47 +0200 Subject: [PATCH 01/21] Add PIFSupport Library This library derives it's behaviour from SPM's PIF decoding, but adjusts it to work with Xcode's internal json file format. --- .swiftlint.yml | 1 + PIF/.gitignore | 8 + PIF/NOTICE.txt | 11 + PIF/Package.swift | 35 + PIF/Sources/PIFSupport/PIF.swift | 909 ++++++++++++++++++++++++ PIF/Sources/PIFSupport/PIFSupport.swift | 49 ++ PIF/Sources/pif-parser/pif-parser.swift | 27 + PIF/Tests/PIFTests/PIFTests.swift | 12 + 8 files changed, 1052 insertions(+) create mode 100644 PIF/.gitignore create mode 100644 PIF/NOTICE.txt create mode 100644 PIF/Package.swift create mode 100644 PIF/Sources/PIFSupport/PIF.swift create mode 100644 PIF/Sources/PIFSupport/PIFSupport.swift create mode 100644 PIF/Sources/pif-parser/pif-parser.swift create mode 100644 PIF/Tests/PIFTests/PIFTests.swift diff --git a/.swiftlint.yml b/.swiftlint.yml index 5d6a87b..8b2d54e 100644 --- a/.swiftlint.yml +++ b/.swiftlint.yml @@ -8,6 +8,7 @@ excluded: disabled_rules: - todo + - nesting line_length: warning: 200 diff --git a/PIF/.gitignore b/PIF/.gitignore new file mode 100644 index 0000000..0023a53 --- /dev/null +++ b/PIF/.gitignore @@ -0,0 +1,8 @@ +.DS_Store +/.build +/Packages +xcuserdata/ +DerivedData/ +.swiftpm/configuration/registries.json +.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata +.netrc diff --git a/PIF/NOTICE.txt b/PIF/NOTICE.txt new file mode 100644 index 0000000..26b34a9 --- /dev/null +++ b/PIF/NOTICE.txt @@ -0,0 +1,11 @@ + + PIF Support + =========== + +This product contains a derivation of Swift Package Manager's XCBuildSupport library, specifically `PIF.swift` + + * LICENSE (Apache License 2.0): + * https://raw.githubusercontent.com/apple/swift-package-manager/main/LICENSE.txt + * HOMEPAGE: + * https://github.com/apple/swift-package-manager/ + * https://swift.org diff --git a/PIF/Package.swift b/PIF/Package.swift new file mode 100644 index 0000000..9391feb --- /dev/null +++ b/PIF/Package.swift @@ -0,0 +1,35 @@ +// swift-tools-version: 5.10 +// The swift-tools-version declares the minimum version of Swift required to build this package. + +import PackageDescription + +let package = Package( + name: "PIF", + platforms: [.macOS(.v12)], + products: [ + // Products define the executables and libraries a package produces, making them visible to other packages. + .library( + name: "PIFSupport", + targets: ["PIFSupport"] + ) + ], + dependencies: [], + targets: [ + // Targets are the basic building blocks of a package, defining a module or a test suite. + // Targets can depend on other targets in this package and products from dependencies. + .target( + name: "PIFSupport", + dependencies: [] + ), + .testTarget( + name: "PIFSupportTests", + dependencies: ["PIFSupport"] + ), + .executableTarget( + name: "pif-parser", + dependencies: [ + "PIFSupport" + ] + ) + ] +) diff --git a/PIF/Sources/PIFSupport/PIF.swift b/PIF/Sources/PIFSupport/PIF.swift new file mode 100644 index 0000000..ef342cb --- /dev/null +++ b/PIF/Sources/PIFSupport/PIF.swift @@ -0,0 +1,909 @@ +// ===----------------------------------------------------------------------=== // +// +// This source file contains derivative work from the Swift Open Source Project +// +// Copyright (c) 2014-2020 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See http://swift.org/LICENSE.txt for license information +// See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +// ===----------------------------------------------------------------------=== // + +/* Changes: Thomas Hedderwick: + - adjust structures to allow for only decoding Xcode's PIF files found in DerivedData + - adjust types to remove external dependencies on SPM +*/ + +// swiftlint:disable file_length type_body_length +import Foundation + +/// The Project Interchange Format (PIF) is a structured representation of the +/// project model created by clients (Xcode/SwiftPM) to send to XCBuild. +/// +/// The PIF is a representation of the project model describing the static +/// objects which contribute to building products from the project, independent +/// of "how" the user has chosen to build those products in any particular +/// build. This information can be cached by XCBuild between builds (even +/// between builds which use different schemes or configurations), and can be +/// incrementally updated by clients when something changes. +public enum PIF { + /// This is used as part of the signature for the high-level PIF objects, to ensure that changes to the PIF schema + /// are represented by the objects which do not use a content-based signature scheme (workspaces and projects, + /// currently). + static let schemaVersion = 11 + + /// The file extension for files in the PIF cache + static let cacheFileExtension = "-json" + + /// The type used for identifying PIF objects. + public typealias GUID = String + + public enum Error: Swift.Error { + case decodingError(String) + case userInfoError(String) + case dataReadingFailure(String) + } + + /// The top-level PIF object. + public struct TopLevelObject: Decodable { + public let workspace: PIF.Workspace + + public init(workspace: PIF.Workspace) { + self.workspace = workspace + } + } + + public class TypedObject: Decodable { + class var type: String { + fatalError("\(self) missing implementation") + } + + let type: String? + + fileprivate init() { + type = Swift.type(of: self).type + } + + private enum CodingKeys: CodingKey { + case type + } + + required public init(from decoder: Decoder) throws { + let container = try decoder.container(keyedBy: CodingKeys.self) + type = try container.decode(String.self, forKey: .type) + } + } + + public final class Workspace: Decodable { + public let guid: GUID + public let name: String + public let path: URL + public let projects: [Project] + + private enum CodingKeys: CodingKey { + case guid, name, path, projects + } + + public required init(from decoder: Decoder) throws { + guard let cachePath = decoder.userInfo[.pifCachePath] as? URL else { + throw Error.userInfoError("decoder's userInfo doesn't container required key .cachePath, or that value isn't a URL") + } + + let container = try decoder.container(keyedBy: CodingKeys.self) + + guid = try container.decode(GUID.self, forKey: .guid) + name = try container.decode(String.self, forKey: .name) + path = try container.decode(URL.self, forKey: .path) + + let projectPaths = try container.decode([String].self, forKey: .projects) + .map { + cachePath + .appendingPathComponent("project") + .appendingPathComponent("\($0)\(PIF.cacheFileExtension)") + } + + let projectContents = try projectPaths + .map { + do { + return try Data(contentsOf: $0) + } catch { + throw Error.dataReadingFailure(error.localizedDescription) + } + } + + projects = try projectContents + .map { + // do { + return try PIFDecoder(cache: cachePath).decode(PIF.Project.self, from: $0) + // } catch { + // fatalError(String(data: $0, encoding: .utf8)!) + // } + } + } + } + + /// A PIF project, consisting of a tree of groups and file references, a list of targets, and some additional + /// information. + public final class Project: Decodable { + public let guid: GUID + public let projectName: String? + public let path: URL + public let projectDirectory: URL + public let developmentRegion: String? + public let buildConfigurations: [BuildConfiguration] + public let targets: [BaseTarget] + public let groupTree: Group + + private enum CodingKeys: CodingKey { + case guid, projectName, projectIsPackage, path, projectDirectory, developmentRegion, defaultConfigurationName, buildConfigurations, targets, groupTree + } + + public required init(from decoder: Decoder) throws { + guard let cachePath = decoder.userInfo[.pifCachePath] as? URL else { + throw Error.userInfoError("decoder's userInfo doesn't container required key .cachePath, or that value isn't a URL") + } + let container = try decoder.container(keyedBy: CodingKeys.self) + + guid = try container.decode(GUID.self, forKey: .guid) + projectName = try container.decodeIfPresent(String.self, forKey: .projectName) + path = try container.decode(URL.self, forKey: .path) + projectDirectory = try container.decode(URL.self, forKey: .projectDirectory) + developmentRegion = try container.decodeIfPresent(String.self, forKey: .developmentRegion) + buildConfigurations = try container.decode([BuildConfiguration].self, forKey: .buildConfigurations) + + let targetContents = try container.decode([String].self, forKey: .targets) + .map { + cachePath + .appendingPathComponent("target") + .appendingPathComponent("\($0)\(PIF.cacheFileExtension)") + } + .map { + do { + return try Data(contentsOf: $0) + } catch { + throw Error.dataReadingFailure(error.localizedDescription) + } + } + + targets = try targetContents + .map { targetData -> BaseTarget in + let pifDecoder = PIFDecoder(cache: cachePath) + let untypedTarget = try pifDecoder.decode(PIF.TypedObject.self, from: targetData) + switch untypedTarget.type { + case "aggregate": + return try pifDecoder.decode(PIF.AggregateTarget.self, from: targetData) + case "standard", "packageProduct": + return try pifDecoder.decode(PIF.Target.self, from: targetData) + default: + throw Error.decodingError("Target type unknown: \(untypedTarget)") + } + } + + self.groupTree = try container.decode(Group.self, forKey: .groupTree) + } + } + + /// Abstract base class for all items in the group hierarchy. + public class Reference: TypedObject { + /// Determines the base path for a reference's relative path. + public enum SourceTree: String, Decodable { + /// Indicates that the path is relative to the source root (i.e. the "project directory"). + case sourceRoot = "SOURCE_ROOT" + + /// Indicates that the path is relative to the path of the parent group. + case group = "" + + /// Indicates that the path is relative to the effective build directory (which varies depending on active + /// scheme, active run destination, or even an overridden build setting. + case builtProductsDir = "BUILT_PRODUCTS_DIR" + + /// Indicates that the path is an absolute path. + case absolute = "" + + /// Indicates that the path is relative to the SDKROOT + case sdkRoot = "SDKROOT" + } + + public let guid: GUID + + /// Relative path of the reference. It is usually a literal, but may in fact contain build settings. + public let path: String + + /// Determines the base path for the reference's relative path. + public let sourceTree: SourceTree + + /// Name of the reference, if different from the last path component (if not set, the last path component will + /// be used as the name). + public let name: String? + + private enum CodingKeys: CodingKey { + case guid, sourceTree, path, name, type + } + + public required init(from decoder: Decoder) throws { + let container = try decoder.container(keyedBy: CodingKeys.self) + + guid = try container.decode(String.self, forKey: .guid) + sourceTree = try container.decode(SourceTree.self, forKey: .sourceTree) + path = try container.decode(String.self, forKey: .path) + name = try container.decodeIfPresent(String.self, forKey: .name) + + try super.init(from: decoder) + } + } + + /// A reference to a file system entity (a file, folder, etc). + public final class FileReference: Reference { + override class var type: String { "file" } + + public var fileType: String + + private enum CodingKeys: CodingKey { + case fileType + } + + public required init(from decoder: Decoder) throws { + let container = try decoder.container(keyedBy: CodingKeys.self) + + fileType = try container.decode(String.self, forKey: .fileType) + + try super.init(from: decoder) + } + } + + /// A group that can contain References (FileReferences and other Groups). The resolved path of a group is used as + /// the base path for any child references whose source tree type is GroupRelative. + public final class VariantGroup: Group { + override class var type: String { "variantGroup" } + + private enum CodingKeys: CodingKey { + case children, type + } + + public required init(from decoder: Decoder) throws { + try super.init(from: decoder) + } + } + + public final class VersionGroup: Group { + override class var type: String { "versionGroup" } + + private enum CodingKeys: CodingKey { + case children, type + } + + public required init(from decoder: Decoder) throws { + try super.init(from: decoder) + } + } + + /// A group that can contain References (FileReferences and other Groups). The resolved path of a group is used as + /// the base path for any child references whose source tree type is GroupRelative. + public class Group: Reference { + override class var type: String { "group" } + + public let children: [Reference] + + private enum CodingKeys: CodingKey { + case children, type + } + + public required init(from decoder: Decoder) throws { + let container = try decoder.container(keyedBy: CodingKeys.self) + + let untypedChildren = try container.decodeIfPresent([TypedObject].self, forKey: .children) ?? [] + if !untypedChildren.isEmpty { + var childrenContainer = try container.nestedUnkeyedContainer(forKey: .children) + + children = try untypedChildren.compactMap { child in + switch child.type { + case Group.type: + return try childrenContainer.decode(Group.self) + case VariantGroup.type: + return try childrenContainer.decode(VariantGroup.self) + case VersionGroup.type: + return try childrenContainer.decode(VersionGroup.self) + case FileReference.type: + return try childrenContainer.decode(FileReference.self) + default: + // TODO: use logger here + print("unknown reference type: \(child.type ?? "")") + // TODO: support variantGroup etc + return nil + // throw Error.decodingError("unknown reference type \(child.type ?? "")") + } + } + } else { + children = [] + } + + try super.init(from: decoder) + } + } + + /// Represents a dependency on another target (identified by its PIF GUID). + public struct TargetDependency: Decodable { + /// Identifier of depended-upon target. + public let targetGUID: String + + /// The platform filters for this target dependency. + public let platformFilters: [PlatformFilter] + + private enum CodingKeys: CodingKey { + case guid, platformFilters + } + + public init(from decoder: Decoder) throws { + let container = try decoder.container(keyedBy: CodingKeys.self) + + targetGUID = try container.decode(String.self, forKey: .guid) + platformFilters = try container.decodeIfPresent([PlatformFilter].self, forKey: .platformFilters) ?? [] + } + } + + public class BaseTarget: TypedObject { + class override var type: String { "target" } + + public let guid: GUID + public let name: String + public let buildConfigurations: [BuildConfiguration] + public let buildPhases: [BuildPhase] + public let dependencies: [TargetDependency] + public let impartedBuildProperties: ImpartedBuildProperties? + + fileprivate init( + guid: GUID, + name: String, + buildConfigurations: [BuildConfiguration], + buildPhases: [BuildPhase], + dependencies: [TargetDependency], + impartedBuildSettings: PIF.BuildSettings? + ) { + self.guid = guid + self.name = name + self.buildConfigurations = buildConfigurations + self.buildPhases = buildPhases + self.dependencies = dependencies + self.impartedBuildProperties = ImpartedBuildProperties(buildSettings: impartedBuildSettings ?? .init()) + super.init() + } + + public required init(from decoder: Decoder) throws { + throw Error.decodingError("init(from:) has not been implemented") + } + } + + public final class AggregateTarget: BaseTarget { + private enum CodingKeys: CodingKey { + case type, guid, name, buildConfigurations, buildPhases, dependencies, impartedBuildProperties + } + + public required init(from decoder: Decoder) throws { + let container = try decoder.container(keyedBy: CodingKeys.self) + + let guid = try container.decode(GUID.self, forKey: .guid) + let name = try container.decode(String.self, forKey: .name) + let buildConfigurations = try container.decode([BuildConfiguration].self, forKey: .buildConfigurations) + + let untypedBuildPhases = try container.decode([TypedObject].self, forKey: .buildPhases) + var buildPhasesContainer = try container.nestedUnkeyedContainer(forKey: .buildPhases) + + let buildPhases: [BuildPhase] = try untypedBuildPhases.compactMap { + guard let type = $0.type else { + throw Error.decodingError("Expected type in build phase \($0)") + } + return try BuildPhase.decode(container: &buildPhasesContainer, type: type) + } + + let dependencies = try container.decode([TargetDependency].self, forKey: .dependencies) + let impartedBuildProperties = try container.decodeIfPresent(BuildSettings.self, forKey: .impartedBuildProperties) + + super.init( + guid: guid, + name: name, + buildConfigurations: buildConfigurations, + buildPhases: buildPhases, + dependencies: dependencies, + impartedBuildSettings: impartedBuildProperties + ) + } + } + + /// An Xcode target, representing a single entity to build. + public final class Target: BaseTarget { + public enum ProductType: String, Decodable { + case appExtension = "com.apple.product-type.app-extension" + case appExtensionMessages = "com.apple.product-type.app-extension.messages" + case stickerPackExtension = "com.apple.product-type.app-extension.messages-sticker-pack" + case application = "com.apple.product-type.application" + case applicationMessages = "com.apple.product-type.application.messages" + case appClip = "com.apple.product-type.application.on-demand-install-capable" + case bundle = "com.apple.product-type.bundle" + case externalTest = "com.apple.product-type.bundle.external-test" + case ocUnitTest = "com.apple.product-type.bundle.ocunit-test" + case uiTesting = "com.apple.product-type.bundle.ui-testing" + case unitTest = "com.apple.product-type.bundle.unit-test" + case extensionKitExtension = "com.apple.product-type.extensionkit-extension" + case framework = "com.apple.product-type.framework" + case staticFramework = "com.apple.product-type.framework.static" + case instrumentsPackage = "com.apple.product-type.instruments-package" + case kernelExtension = "com.apple.product-type.kernel-extension" + case ioKitKernelExtension = "com.apple.product-type.kernel-extension.iokit" + case dynamicLibrary = "com.apple.product-type.library.dynamic" + case staticLibrary = "com.apple.product-type.library.static" + case objectFile = "com.apple.product-type.objfile" + case pluginKitPlugin = "com.apple.product-type.pluginkit-plugin" + case packageProduct = "packageProduct" + case systemExtension = "com.apple.product-type.system-extension" + case tool = "com.apple.product-type.tool" + case hostBuild = "com.apple.product-type.tool.host-build" + case xpcService = "com.apple.product-type.xpc-service" + } + + public let productName: String + public let productType: ProductType + + private enum CodingKeys: CodingKey { + case guid, name, dependencies, buildConfigurations, type, frameworksBuildPhase, productTypeIdentifier, productReference, buildRules, buildPhases, impartedBuildProperties + } + + public required init(from decoder: Decoder) throws { + let container = try decoder.container(keyedBy: CodingKeys.self) + + let guid = try container.decode(GUID.self, forKey: .guid) + let name = try container.decode(String.self, forKey: .name) + let buildConfigurations = try container.decode([BuildConfiguration].self, forKey: .buildConfigurations) + let dependencies = try container.decode([TargetDependency].self, forKey: .dependencies) + let type = try container.decode(String.self, forKey: .type) + + let buildPhases: [BuildPhase] + let impartedBuildProperties: ImpartedBuildProperties + + if type == "packageProduct" { + self.productType = .packageProduct + self.productName = "" + let fwkBuildPhase = try container.decodeIfPresent(FrameworksBuildPhase.self, forKey: .frameworksBuildPhase) + buildPhases = fwkBuildPhase.map { [$0] } ?? [] + impartedBuildProperties = ImpartedBuildProperties(buildSettings: BuildSettings()) + } else if type == "standard" { + self.productType = try container.decode(ProductType.self, forKey: .productTypeIdentifier) + + let productReference = try container.decode([String: String].self, forKey: .productReference) + self.productName = productReference["name"]! + + let untypedBuildPhases = try container.decodeIfPresent([TypedObject].self, forKey: .buildPhases) ?? [] + var buildPhasesContainer = try container.nestedUnkeyedContainer(forKey: .buildPhases) + + buildPhases = try untypedBuildPhases.compactMap { + guard let type = $0.type else { + throw Error.decodingError("Expected type in build phase \($0)") + } + return try BuildPhase.decode(container: &buildPhasesContainer, type: type) + } + + impartedBuildProperties = try container.decodeIfPresent(ImpartedBuildProperties.self, forKey: .impartedBuildProperties) ?? .init(buildSettings: .init()) + } else { + throw Error.decodingError("Unhandled target type \(type)") + } + + super.init( + guid: guid, + name: name, + buildConfigurations: buildConfigurations, + buildPhases: buildPhases, + dependencies: dependencies, + impartedBuildSettings: impartedBuildProperties.buildSettings + ) + } + } + + /// Abstract base class for all build phases in a target. + public class BuildPhase: TypedObject { + static func decode(container: inout UnkeyedDecodingContainer, type: String) throws -> BuildPhase? { + switch type { + case HeadersBuildPhase.type: + return try container.decode(HeadersBuildPhase.self) + case SourcesBuildPhase.type: + return try container.decode(SourcesBuildPhase.self) + case FrameworksBuildPhase.type: + return try container.decode(FrameworksBuildPhase.self) + case ResourcesBuildPhase.type: + return try container.decode(ResourcesBuildPhase.self) + default: + // TODO: replace with logger + print("unknown build phase: \(type)") + return nil + // TODO: we should probably handle these: + /* + case copyFiles = "com.apple.buildphase.copy-files" + case frameworks = "com.apple.buildphase.frameworks" + case headers = "com.apple.buildphase.headers" + case resources = "com.apple.buildphase.resources" + case shellScript = "com.apple.buildphase.shell-script" + case sources = "com.apple.buildphase.sources"*/ + // throw Error.decodingError("unknown build phase \(type)") + } + } + + public let guid: GUID + public let buildFiles: [BuildFile] + + private enum CodingKeys: CodingKey { + case guid, buildFiles + } + + public required init(from decoder: Decoder) throws { + let container = try decoder.container(keyedBy: CodingKeys.self) + + guid = try container.decode(GUID.self, forKey: .guid) + buildFiles = try container.decode([BuildFile].self, forKey: .buildFiles) + + try super.init(from: decoder) + } + } + + /// A "headers" build phase, i.e. one that copies headers into a directory of the product, after suitable + /// processing. + public final class HeadersBuildPhase: BuildPhase { + override class var type: String { "com.apple.buildphase.headers" } + } + + /// A "sources" build phase, i.e. one that compiles sources and provides them to be linked into the executable code + /// of the product. + public final class SourcesBuildPhase: BuildPhase { + override class var type: String { "com.apple.buildphase.sources" } + } + + /// A "frameworks" build phase, i.e. one that links compiled code and libraries into the executable of the product. + public final class FrameworksBuildPhase: BuildPhase { + override class var type: String { "com.apple.buildphase.frameworks" } + } + + public final class ResourcesBuildPhase: BuildPhase { + override class var type: String { "com.apple.buildphase.resources" } + } + + /// A build file, representing the membership of either a file or target product reference in a build phase. + public struct BuildFile: Decodable { + public enum Reference { + case file(guid: PIF.GUID) + case target(guid: PIF.GUID) + } + + public enum HeaderVisibility: String, Decodable { + case `public` = "public" + case `private` = "private" + } + + public let guid: GUID + public let reference: Reference + public let headerVisibility: HeaderVisibility? + public let platformFilters: [PlatformFilter] + + private enum CodingKeys: CodingKey { + case guid, platformFilters, fileReference, targetReference, headerVisibility + } + + public init(from decoder: Decoder) throws { + let container = try decoder.container(keyedBy: CodingKeys.self) + + guid = try container.decode(GUID.self, forKey: .guid) + platformFilters = try container.decodeIfPresent([PlatformFilter].self, forKey: .platformFilters) ?? [] + headerVisibility = try container.decodeIfPresent(HeaderVisibility.self, forKey: .headerVisibility) ?? nil + + if container.allKeys.contains(.fileReference) { + reference = try .file(guid: container.decode(GUID.self, forKey: .fileReference)) + } else if container.allKeys.contains(.targetReference) { + reference = .target(guid: try container.decode(GUID.self, forKey: .targetReference)) + } else { + throw Error.decodingError("Expected \(CodingKeys.fileReference) or \(CodingKeys.targetReference) in the keys") + } + } + } + + /// Represents a generic platform filter. + public struct PlatformFilter: Decodable, Equatable { + /// The name of the platform (`LC_BUILD_VERSION`). + /// + /// Example: macos, ios, watchos, tvos. + public let platform: String + + /// The name of the environment (`LC_BUILD_VERSION`) + /// + /// Example: simulator, maccatalyst. + public let environment: String? + } + + /// A build configuration, which is a named collection of build settings. + public struct BuildConfiguration: Decodable { + public let guid: GUID + public let name: String + public let buildSettings: BuildSettings + public let impartedBuildProperties: ImpartedBuildProperties + + private enum CodingKeys: CodingKey { + case guid, name, buildSettings, impartedBuildProperties + } + + public init(from decoder: any Decoder) throws { + let container = try decoder.container(keyedBy: CodingKeys.self) + + guid = try container.decode(GUID.self, forKey: .guid) + name = try container.decode(String.self, forKey: .name) + buildSettings = try container.decode(BuildSettings.self, forKey: .buildSettings) + impartedBuildProperties = try container.decodeIfPresent(ImpartedBuildProperties.self, forKey: .impartedBuildProperties) ?? .init(buildSettings: .init()) + } + } + + public struct ImpartedBuildProperties: Decodable { + public let buildSettings: BuildSettings + } + + // swiftlint:disable identifier_name inclusive_language + /// A set of build settings, which is represented as a struct of optional build settings. This is not optimally + /// efficient, but it is great for code completion and type-checking. + public struct BuildSettings: Decodable { + public enum SingleValueSetting: String, Decodable { + case APPLICATION_EXTENSION_API_ONLY + case BUILT_PRODUCTS_DIR + case CLANG_CXX_LANGUAGE_STANDARD + case CLANG_ENABLE_MODULES + case CLANG_ENABLE_OBJC_ARC + case CODE_SIGNING_REQUIRED + case CODE_SIGN_IDENTITY + case COMBINE_HIDPI_IMAGES + case COPY_PHASE_STRIP + case DEBUG_INFORMATION_FORMAT + case DEFINES_MODULE + case DRIVERKIT_DEPLOYMENT_TARGET + case DYLIB_INSTALL_NAME_BASE + case EMBEDDED_CONTENT_CONTAINS_SWIFT + case ENABLE_NS_ASSERTIONS + case ENABLE_TESTABILITY + case ENABLE_TESTING_SEARCH_PATHS + case ENTITLEMENTS_REQUIRED + case EXECUTABLE_NAME + case GENERATE_INFOPLIST_FILE + case GCC_C_LANGUAGE_STANDARD + case GCC_OPTIMIZATION_LEVEL + case GENERATE_MASTER_OBJECT_FILE + case INFOPLIST_FILE + case IPHONEOS_DEPLOYMENT_TARGET + case KEEP_PRIVATE_EXTERNS + case CLANG_COVERAGE_MAPPING_LINKER_ARGS + case MACH_O_TYPE + case MACOSX_DEPLOYMENT_TARGET + case MODULEMAP_FILE + case MODULEMAP_FILE_CONTENTS + case MODULEMAP_PATH + case MODULE_CACHE_DIR + case ONLY_ACTIVE_ARCH + case PACKAGE_RESOURCE_BUNDLE_NAME + case PACKAGE_RESOURCE_TARGET_KIND + case PRODUCT_BUNDLE_IDENTIFIER + case PRODUCT_MODULE_NAME + case PRODUCT_NAME + case PROJECT_NAME + case SDKROOT + case SDK_VARIANT + case SKIP_INSTALL + case INSTALL_PATH + case SUPPORTS_MACCATALYST + case SWIFT_SERIALIZE_DEBUGGING_OPTIONS + case SWIFT_FORCE_STATIC_LINK_STDLIB + case SWIFT_FORCE_DYNAMIC_LINK_STDLIB + case SWIFT_INSTALL_OBJC_HEADER + case SWIFT_OBJC_INTERFACE_HEADER_NAME + case SWIFT_OBJC_INTERFACE_HEADER_DIR + case SWIFT_OPTIMIZATION_LEVEL + case SWIFT_VERSION + case TARGET_NAME + case TARGET_BUILD_DIR + case TVOS_DEPLOYMENT_TARGET + case USE_HEADERMAP + case USES_SWIFTPM_UNSAFE_FLAGS + case WATCHOS_DEPLOYMENT_TARGET + case XROS_DEPLOYMENT_TARGET + case MARKETING_VERSION + case CURRENT_PROJECT_VERSION + case SWIFT_EMIT_MODULE_INTERFACE + case GENERATE_RESOURCE_ACCESSORS + } + + public enum MultipleValueSetting: String, Decodable { + case EMBED_PACKAGE_RESOURCE_BUNDLE_NAMES + case FRAMEWORK_SEARCH_PATHS + case GCC_PREPROCESSOR_DEFINITIONS + case HEADER_SEARCH_PATHS + case LD_RUNPATH_SEARCH_PATHS + case LIBRARY_SEARCH_PATHS + case OTHER_CFLAGS + case OTHER_CPLUSPLUSFLAGS + case OTHER_LDFLAGS + case OTHER_LDRFLAGS + case OTHER_SWIFT_FLAGS + case PRELINK_FLAGS + case SPECIALIZATION_SDK_OPTIONS + case SUPPORTED_PLATFORMS + case SWIFT_ACTIVE_COMPILATION_CONDITIONS + case SWIFT_MODULE_ALIASES + } + // swiftlint:enable identifier_name inclusive_language + + public enum Platform: String, CaseIterable, Decodable { + case macOS = "macos" + case macCatalyst = "maccatalyst" + case iOS = "ios" + case tvOS = "tvos" + case watchOS = "watchos" + case driverKit = "driverkit" + case linux + } + + public private(set) var platformSpecificSingleValueSettings = [Platform: [SingleValueSetting: String]]() + public private(set) var platformSpecificMultipleValueSettings = [Platform: [MultipleValueSetting: [String]]]() + public private(set) var singleValueSettings: [SingleValueSetting: String] = [:] + public private(set) var multipleValueSettings: [MultipleValueSetting: [String]] = [:] + + public subscript(_ setting: SingleValueSetting) -> String? { + get { singleValueSettings[setting] } + set { singleValueSettings[setting] = newValue } + } + + public subscript(_ setting: SingleValueSetting, for platform: Platform) -> String? { + get { platformSpecificSingleValueSettings[platform]?[setting] } + set { platformSpecificSingleValueSettings[platform, default: [:]][setting] = newValue } + } + + public subscript(_ setting: SingleValueSetting, default defaultValue: @autoclosure () -> String) -> String { + get { singleValueSettings[setting, default: defaultValue()] } + set { singleValueSettings[setting] = newValue } + } + + public subscript(_ setting: MultipleValueSetting) -> [String]? { + get { multipleValueSettings[setting] } + set { multipleValueSettings[setting] = newValue } + } + + public subscript(_ setting: MultipleValueSetting, for platform: Platform) -> [String]? { + get { platformSpecificMultipleValueSettings[platform]?[setting] } + set { platformSpecificMultipleValueSettings[platform, default: [:]][setting] = newValue } + } + + public subscript( + _ setting: MultipleValueSetting, + default defaultValue: @autoclosure () -> [String] + ) -> [String] { + get { multipleValueSettings[setting, default: defaultValue()] } + set { multipleValueSettings[setting] = newValue } + } + + public subscript( + _ setting: MultipleValueSetting, + for platform: Platform, + default defaultValue: @autoclosure () -> [String] + ) -> [String] { + get { platformSpecificMultipleValueSettings[platform, default: [:]][setting, default: defaultValue()] } + set { platformSpecificMultipleValueSettings[platform, default: [:]][setting] = newValue } + } + + public init() {} + + private enum CodingKeys: CodingKey { + case platformSpecificSingleValueSettings, platformSpecificMultipleValueSettings, singleValueSettings, multipleValueSettings + } + + public init(from decoder: Decoder) throws { + let container = try decoder.container(keyedBy: CodingKeys.self) + + platformSpecificSingleValueSettings = try container.decodeIfPresent([Platform: [SingleValueSetting: String]].self, forKey: .platformSpecificSingleValueSettings) ?? .init() + platformSpecificMultipleValueSettings = try container.decodeIfPresent([Platform: [MultipleValueSetting: [String]]].self, forKey: .platformSpecificMultipleValueSettings) ?? .init() + singleValueSettings = try container.decodeIfPresent([SingleValueSetting: String].self, forKey: .singleValueSettings) ?? [:] + multipleValueSettings = try container.decodeIfPresent([MultipleValueSetting: [String]] .self, forKey: .multipleValueSettings) ?? [:] + } + } +} + +/// Represents a filetype recognized by the Xcode build system. +public struct XCBuildFileType: CaseIterable { + public static let xcdatamodeld: XCBuildFileType = XCBuildFileType( + fileType: "xcdatamodeld", + fileTypeIdentifier: "wrapper.xcdatamodeld" + ) + + public static let xcdatamodel: XCBuildFileType = XCBuildFileType( + fileType: "xcdatamodel", + fileTypeIdentifier: "wrapper.xcdatamodel" + ) + + public static let xcmappingmodel: XCBuildFileType = XCBuildFileType( + fileType: "xcmappingmodel", + fileTypeIdentifier: "wrapper.xcmappingmodel" + ) + + public static let allCases: [XCBuildFileType] = [ + .xcdatamodeld, + .xcdatamodel, + .xcmappingmodel + ] + + public let fileTypes: Set + public let fileTypeIdentifier: String + + private init(fileTypes: Set, fileTypeIdentifier: String) { + self.fileTypes = fileTypes + self.fileTypeIdentifier = fileTypeIdentifier + } + + private init(fileType: String, fileTypeIdentifier: String) { + self.init(fileTypes: [fileType], fileTypeIdentifier: fileTypeIdentifier) + } +} + +extension PIF.FileReference { + // fileprivate static func fileTypeIdentifier(forPath path: String) -> String { + // let pathExtension: String? + // if let path = try? URL(validating: path) { + // pathExtension = path.extension + // } else if let path = try? RelativePath(validating: path) { + // pathExtension = path.extension + // } else { + // pathExtension = nil + // } + + // switch pathExtension { + // case "a": + // return "archive.ar" + // case "s", "S": + // return "sourcecode.asm" + // case "c": + // return "sourcecode.c.c" + // case "cl": + // return "sourcecode.opencl" + // case "cpp", "cp", "cxx", "cc", "c++", "C", "tcc": + // return "sourcecode.cpp.cpp" + // case "d": + // return "sourcecode.dtrace" + // case "defs", "mig": + // return "sourcecode.mig" + // case "m": + // return "sourcecode.c.objc" + // case "mm", "M": + // return "sourcecode.cpp.objcpp" + // case "metal": + // return "sourcecode.metal" + // case "l", "lm", "lmm", "lpp", "lp", "lxx": + // return "sourcecode.lex" + // case "swift": + // return "sourcecode.swift" + // case "y", "ym", "ymm", "ypp", "yp", "yxx": + // return "sourcecode.yacc" + + // case "xcassets": + // return "folder.assetcatalog" + // case "xcstrings": + // return "text.json.xcstrings" + // case "storyboard": + // return "file.storyboard" + // case "xib": + // return "file.xib" + + // case "xcframework": + // return "wrapper.xcframework" + + // default: + // return pathExtension.flatMap({ pathExtension in + // XCBuildFileType.allCases.first(where: ({ $0.fileTypes.contains(pathExtension) })) + // })?.fileTypeIdentifier ?? "file" + // } + // } +} + +private struct UntypedTarget: Decodable { + struct TargetContents: Decodable { + let type: String + } + let contents: TargetContents +} +// swiftlint:enable file_length type_body_length diff --git a/PIF/Sources/PIFSupport/PIFSupport.swift b/PIF/Sources/PIFSupport/PIFSupport.swift new file mode 100644 index 0000000..b3379ed --- /dev/null +++ b/PIF/Sources/PIFSupport/PIFSupport.swift @@ -0,0 +1,49 @@ +import Foundation + +public class PIFParser { + private let cachePath: URL + + public enum Error: Swift.Error { + case workspaceNotFound + } + + public init(cachePath: URL) { + self.cachePath = cachePath + } + + public func parse() throws -> PIF.Workspace { + let workspace = try workspacePath() + let data = try Data(contentsOf: workspace) + + return try PIF.PIFDecoder(cache: cachePath).decode(PIF.Workspace.self, from: data) + } + + private func workspacePath() throws -> URL { + let path = cachePath.appendingPathComponent("workspace") + + let workspaces = try FileManager.default.contentsOfDirectory(at: path, includingPropertiesForKeys: [.isRegularFileKey]) + .filter { $0.lastPathComponent.starts(with: "WORKSPACE@") } + + precondition(workspaces.count == 1, "Encountered more than one workspace - it is expected that a single workspace exists: \(workspaces)") + + guard workspaces.count > 0 else { + throw Error.workspaceNotFound + } + + return workspaces[0] + } +} + +extension PIF { + class PIFDecoder: JSONDecoder { + internal init(cache path: URL) { + super.init() + + userInfo[.pifCachePath] = path + } + } +} + +extension CodingUserInfoKey { + static let pifCachePath: CodingUserInfoKey = CodingUserInfoKey(rawValue: "PIFCachePath")! +} diff --git a/PIF/Sources/pif-parser/pif-parser.swift b/PIF/Sources/pif-parser/pif-parser.swift new file mode 100644 index 0000000..78db443 --- /dev/null +++ b/PIF/Sources/pif-parser/pif-parser.swift @@ -0,0 +1,27 @@ +// +// pif-parser.swift +// +// +// Created by Thomas Hedderwick on 03/05/2024. +// + +import Foundation +import PIFSupport + +@main +struct PIFParser { + static func main() throws { + guard CommandLine.arguments.count == 2 else { + print("USAGE: \(CommandLine.arguments.first!) [PIFCache path]") + return + } + + let cachePath = URL(fileURLWithPath: CommandLine.arguments[1]) + let parser = PIFSupport.PIFParser(cachePath: cachePath) + let workspace = try parser.parse() + + print("workspace: \(workspace.guid):") + print("projects: \(workspace.projects.map { $0.guid }.joined(separator: "\n"))\n") + print("targets: \(workspace.projects.flatMap { $0.targets }.map { $0.guid }.joined(separator: "\n"))") + } +} diff --git a/PIF/Tests/PIFTests/PIFTests.swift b/PIF/Tests/PIFTests/PIFTests.swift new file mode 100644 index 0000000..000a103 --- /dev/null +++ b/PIF/Tests/PIFTests/PIFTests.swift @@ -0,0 +1,12 @@ +import XCTest +@testable import PIF + +final class PIFTests: XCTestCase { + func testExample() throws { + // XCTest Documentation + // https://developer.apple.com/documentation/xctest + + // Defining Test Cases and Test Methods + // https://developer.apple.com/documentation/xctest/defining_test_cases_and_test_methods + } +} From 43f8ddaa14aac0971a3ebafaa34db5e09d86ded8 Mon Sep 17 00:00:00 2001 From: NinjaLikesCheez Date: Tue, 7 May 2024 15:44:27 +0200 Subject: [PATCH 02/21] Bring PIFSupport into Gen IR --- PIF/Sources/PIFSupport/PIFSupport.swift | 13 +++---- Package.swift | 6 ++- Sources/GenIR/GenIR.swift | 3 ++ Sources/GenIR/PIFCache.swift | 51 +++++++++++++++++++++++++ 4 files changed, 63 insertions(+), 10 deletions(-) create mode 100644 Sources/GenIR/PIFCache.swift diff --git a/PIF/Sources/PIFSupport/PIFSupport.swift b/PIF/Sources/PIFSupport/PIFSupport.swift index b3379ed..2b8665a 100644 --- a/PIF/Sources/PIFSupport/PIFSupport.swift +++ b/PIF/Sources/PIFSupport/PIFSupport.swift @@ -2,23 +2,20 @@ import Foundation public class PIFParser { private let cachePath: URL + public let workspace: PIF.Workspace public enum Error: Swift.Error { case workspaceNotFound } - public init(cachePath: URL) { + public init(cachePath: URL) throws { self.cachePath = cachePath - } - - public func parse() throws -> PIF.Workspace { - let workspace = try workspacePath() - let data = try Data(contentsOf: workspace) - return try PIF.PIFDecoder(cache: cachePath).decode(PIF.Workspace.self, from: data) + let data = try Data(contentsOf: try Self.workspacePath(in: cachePath)) + workspace = try PIF.PIFDecoder(cache: cachePath).decode(PIF.Workspace.self, from: data) } - private func workspacePath() throws -> URL { + private static func workspacePath(in cachePath: URL) throws -> URL { let path = cachePath.appendingPathComponent("workspace") let workspaces = try FileManager.default.contentsOfDirectory(at: path, includingPropertiesForKeys: [.isRegularFileKey]) diff --git a/Package.swift b/Package.swift index 566f581..612c472 100644 --- a/Package.swift +++ b/Package.swift @@ -14,7 +14,8 @@ let package = Package( .package(url: "https://github.com/apple/swift-argument-parser.git", from: "1.0.0"), .package(url: "https://github.com/apple/swift-log.git", from: "1.0.0"), .package(url: "https://github.com/apple/swift-docc-plugin", from: "1.0.0"), - .package(path: "PBXProjParser") + .package(path: "PBXProjParser"), + .package(path: "PIF") ], targets: [ // Targets are the basic building blocks of a package. A target can define a module or a test suite. @@ -24,7 +25,8 @@ let package = Package( dependencies: [ .product(name: "ArgumentParser", package: "swift-argument-parser"), .product(name: "Logging", package: "swift-log"), - .product(name: "PBXProjParser", package: "PBXProjParser") + .product(name: "PBXProjParser", package: "PBXProjParser"), + .product(name: "PIFSupport", package: "PIF") ], path: "Sources/GenIR" ), diff --git a/Sources/GenIR/GenIR.swift b/Sources/GenIR/GenIR.swift index 58694a0..d2687cb 100644 --- a/Sources/GenIR/GenIR.swift +++ b/Sources/GenIR/GenIR.swift @@ -118,6 +118,9 @@ struct IREmitterCommand: ParsableCommand { let log = try logParser(for: log) try log.parse(&targets) + // Find and parse the PIF cache + let pifCache = try PIFCache(buildCache: log.buildCachePath) + let buildCacheManipulator = try BuildCacheManipulator( buildCachePath: log.buildCachePath, buildSettings: log.settings, diff --git a/Sources/GenIR/PIFCache.swift b/Sources/GenIR/PIFCache.swift new file mode 100644 index 0000000..4f4373d --- /dev/null +++ b/Sources/GenIR/PIFCache.swift @@ -0,0 +1,51 @@ +import Foundation +import PIFSupport + +struct PIFCache { + private let buildCache: URL + private let pifCachePath: URL + private let workspace: PIF.Workspace + + enum Error: Swift.Error { + case nonexistentCache(String) + case pifError(String) + } + + init(buildCache: URL) throws { + self.buildCache = buildCache + self.pifCachePath = try Self.pifCachePath(in: buildCache) + + do { + let cache = try PIFParser(cachePath: pifCachePath) + workspace = cache.workspace + } catch { + throw Error.pifError(error.localizedDescription) + } + } + + private static func pifCachePath(in buildCache: URL) throws -> URL { + // TODO: test this variation, because I haven't seen this personally + let cmakePIFCachePath = buildCache + .deletingLastPathComponent() + .appendingPathComponent("XCBuildData") + .appendingPathComponent("PIFCache") + + let regularPIFCachePath = buildCache + .appendingPathComponent("Intermediates.noindex") + .appendingPathComponent("XCBuildData") + .appendingPathComponent("PIFCache") + + if FileManager.default.directoryExists(at: cmakePIFCachePath) { + return cmakePIFCachePath + } else if FileManager.default.directoryExists(at: regularPIFCachePath) { + return regularPIFCachePath + } else { + throw Error.nonexistentCache( + """ + Couldn't find cache at: \(regularPIFCachePath). Ensure a clean build **AND** \ + ensure `xcodebuild clean` happens separately from `xcodebuild archive` command + """ + ) + } + } +} From 300319e35bc1499377bb33b7c0731c21b42fc6f9 Mon Sep 17 00:00:00 2001 From: NinjaLikesCheez Date: Wed, 8 May 2024 13:11:29 +0200 Subject: [PATCH 03/21] Make the DependencyGraph generic This allows us to switch out the Node value without massive changes --- .../Dependency Graph/DependencyGraph.swift | 58 ++++++++--------- .../DependencyGraphBuilder.swift | 63 ++++++++----------- Sources/GenIR/Dependency Graph/Edge.swift | 10 +-- Sources/GenIR/Dependency Graph/Node.swift | 29 +++++---- Sources/GenIR/OutputPostprocessor.swift | 14 +++-- Sources/GenIR/PIFCache.swift | 42 ++++++++++++- Sources/GenIR/Targets/Target.swift | 6 ++ Sources/GenIR/Targets/Targets.swift | 35 ++++++----- 8 files changed, 153 insertions(+), 104 deletions(-) diff --git a/Sources/GenIR/Dependency Graph/DependencyGraph.swift b/Sources/GenIR/Dependency Graph/DependencyGraph.swift index b2d4098..7cd0806 100644 --- a/Sources/GenIR/Dependency Graph/DependencyGraph.swift +++ b/Sources/GenIR/Dependency Graph/DependencyGraph.swift @@ -7,41 +7,41 @@ import Foundation -/// A directed graph that maps dependencies between targets (nodes) via edges (directions between nodes) -class DependencyGraph { +/// A directed graph that maps dependencies between values (nodes) via edges (directions between nodes) +class DependencyGraph { /// All the nodes in the graph - private(set) var nodes: [Node] = [] - - /// Adds a target to the graph - /// - Parameter target: the target to add - /// - Returns: the node added, iff a node for this target didn't already exist in the graph - func addNode(target: Target) -> Node? { - if findNode(for: target) != nil { - return nil + private(set) var nodes: [Node] = [] + + /// Adds a value to the graph + /// - Parameter value: the value to add + /// - Returns: the node added + func addNode(value: Value) -> Node { + if let node = findNode(for: value) { + return node } - let node = Node(target) + let node = Node(value) nodes.append(node) return node } - /// Finds a target's node in the graph - /// - Parameter target: the target to look for - /// - Returns: the node for the given target, if found - func findNode(for target: Target) -> Node? { - nodes.first(where: { $0.target == target }) + /// Finds a value's node in the graph + /// - Parameter value: the value to look for + /// - Returns: the node for the given value, if found + func findNode(for value: Value) -> Node? { + nodes.first(where: { $0.value == value }) } - /// Builds a dependency 'chain' for a target using a depth-first search - /// - Parameter target: the target to get a chain for + /// Builds a dependency 'chain' for a value using a depth-first search + /// - Parameter value: the value to get a chain for /// - Returns: the chain of nodes, starting - func chain(for target: Target) -> [Node] { - guard let targetNode = findNode(for: target) else { - logger.debug("Couldn't find node for target: \(target.name)") + func chain(for value: Value) -> [Node] { + guard let node = findNode(for: value) else { + logger.debug("Couldn't find node for value: \(value.name)") return [] } - return depthFirstSearch(startingAt: targetNode) + return depthFirstSearch(startingAt: node) } func toDot(_ path: String) throws { @@ -60,13 +60,13 @@ class DependencyGraph { /// Perform a depth-first search starting at the provided node /// - Parameter node: the node whose children to search through /// - Returns: an array of nodes ordered by a depth-first search approach - private func depthFirstSearch(startingAt node: Node) -> [Node] { - logger.debug("----\nSearching for: \(node.target.name)") - var visited = Set() - var chain = [Node]() + private func depthFirstSearch(startingAt node: Node) -> [Node] { + logger.debug("----\nSearching for: \(node.value.name)") + var visited = Set>() + var chain = [Node]() - func depthFirst(node: Node) { - logger.debug("inserting node: \(node.target.name)") + func depthFirst(node: Node) { + logger.debug("inserting node: \(node.value.name)") visited.insert(node) logger.debug("visited: \(visited)") @@ -80,7 +80,7 @@ class DependencyGraph { } } - logger.debug("appending to chain: \(node.target.name)") + logger.debug("appending to chain: \(node.value.name)") chain.append(node) } diff --git a/Sources/GenIR/Dependency Graph/DependencyGraphBuilder.swift b/Sources/GenIR/Dependency Graph/DependencyGraphBuilder.swift index e2985d0..cdc6b49 100644 --- a/Sources/GenIR/Dependency Graph/DependencyGraphBuilder.swift +++ b/Sources/GenIR/Dependency Graph/DependencyGraphBuilder.swift @@ -5,52 +5,43 @@ // Created by Thomas Hedderwick on 28/08/2023. // -class DependencyGraphBuilder { - /// Builds a dependency graph for the given collection of targets - /// - Parameter targets: the targets to build a graph for - /// - Returns: the dependency graph - static func build(targets: Targets) -> DependencyGraph { - let graph = DependencyGraph() - - targets.forEach { - add(target: $0, in: targets, to: graph) - } - - return graph +protocol DependencyProviding { + associatedtype Value: NodeValue + func dependencies(for value: Value) -> [Value] +} +class DependencyGraphBuilder where Value == Provider.Value { + private let provider: Provider + let graph = DependencyGraph() + + /// Inits the Graph Builder + /// - Parameter provider: the dependency provider for the values + init(provider: Provider, values: [Value]) { + self.provider = provider + values.forEach { add(value: $0) } } - /// Adds a target (and it's dependencies) to the graph + /// Adds a value (and it's dependencies) to the graph /// - Parameters: - /// - graph: the graph to add a target to - /// - target: the target to add - /// - targets: the targets containing the target and it's dependencies - static func add(target: Target, in targets: Targets, to graph: DependencyGraph) { - logger.debug("Adding target: \(target.name) to graph") - - guard let node = graph.addNode(target: target) else { - logger.debug("Already inserted node: \(target.name). Skipping.") - return + /// - value: the value to add + @discardableResult + private func add(value: Value) -> Node { + logger.debug("Adding value: \(value.name) to graph") + + if let existingNode = graph.findNode(for: value) { + logger.debug("Already inserted node: \(existingNode.name). Skipping") + return existingNode } - let dependencies = targets.calculateDependencies(for: target) + let dependencies = provider.dependencies(for: value) + let node = graph.addNode(value: value) for dependency in dependencies { - guard let dependencyTarget = targets.target(for: dependency) else { - logger.debug("Couldn't lookup dependency in targets: \(dependency)") - continue - } + let dependencyNode = add(value: dependency) - add(target: dependencyTarget, in: targets, to: graph) - - guard let dependencyNode = graph.findNode(for: dependencyTarget) else { - logger.debug("Couldn't find node for target (\(dependencyTarget.name)) even though it was just inserted?") - continue - } - - // We add both an edge from and to the node and dependency - this way we can do bidirectional - // searches for a given node and see what it's dependencies are and who depends on it node.add(edge: .init(to: dependencyNode, from: node, relationship: .dependency)) dependencyNode.add(edge: .init(to: node, from: dependencyNode, relationship: .depender)) } + + return node } } diff --git a/Sources/GenIR/Dependency Graph/Edge.swift b/Sources/GenIR/Dependency Graph/Edge.swift index 4e62019..c78de34 100644 --- a/Sources/GenIR/Dependency Graph/Edge.swift +++ b/Sources/GenIR/Dependency Graph/Edge.swift @@ -6,12 +6,12 @@ // // swiftlint:disable identifier_name -/// An edge describes the relationship between two Nodes in a graph -class Edge { +/// An edge describes the relationship between two Nodes in a graph +class Edge { /// The source node - let to: Node + let to: Node /// The destination node - let from: Node + let from: Node /// The relationship between the two nodes let relationship: Relationship @@ -23,7 +23,7 @@ class Edge { case depender } - init(to: Node, from: Node, relationship: Relationship) { + init(to: Node, from: Node, relationship: Relationship) { self.to = to self.from = from self.relationship = relationship diff --git a/Sources/GenIR/Dependency Graph/Node.swift b/Sources/GenIR/Dependency Graph/Node.swift index dd9a8c1..f576253 100644 --- a/Sources/GenIR/Dependency Graph/Node.swift +++ b/Sources/GenIR/Dependency Graph/Node.swift @@ -7,22 +7,27 @@ import Foundation -class Node { +protocol NodeValue: Hashable { + /// The name of this node, mostly used for debugging and printing + var name: String { get } +} + +class Node { /// The edges from and to this node - private(set) var edges = [Edge]() - /// The target this node represents - let target: Target + private(set) var edges = [Edge]() + /// The value this node represents + let value: Value /// The name of this node, mostly used for debugging and printing let name: String - init(_ target: Target) { - self.target = target - self.name = target.name + init(_ value: Value) { + self.value = value + self.name = value.name } /// Adds an edge to this node /// - Parameter edge: the edge to add - func add(edge: Edge) { + func add(edge: Edge) { if edges.filter({ $0.to.name == edge.to.name }).count == 0 { edges.append(edge) } @@ -31,7 +36,7 @@ class Node { extension Node: Equatable { static func == (_ lhs: Node, rhs: Node) -> Bool { - lhs.target == rhs.target && lhs.edges == rhs.edges + lhs.value == rhs.value && lhs.edges == rhs.edges } } @@ -40,9 +45,9 @@ extension Node: CustomStringConvertible { var description = "" if !edges.isEmpty { - description += "[Node: \(target.name), edges: \(edges.filter({ $0.relationship == .dependency }).map { $0.to.target.name})] " + description += "[Node: \(value.name), edges: \(edges.filter({ $0.relationship == .dependency }).map { $0.to.value.name})] " } else { - description += "[Node: \(target.name)] " + description += "[Node: \(value.name)] " } return description @@ -52,6 +57,6 @@ extension Node: CustomStringConvertible { extension Node: Hashable { func hash(into hasher: inout Hasher) { hasher.combine(name) - hasher.combine(target) + hasher.combine(value) } } diff --git a/Sources/GenIR/OutputPostprocessor.swift b/Sources/GenIR/OutputPostprocessor.swift index 643f8c2..2d397fe 100644 --- a/Sources/GenIR/OutputPostprocessor.swift +++ b/Sources/GenIR/OutputPostprocessor.swift @@ -24,7 +24,7 @@ class OutputPostprocessor { /// Mapping of dynamic dependencies (inside the xcarchive) to their paths on disk private let dynamicDependencyToPath: [String: URL] - private let graph: DependencyGraph + private let graph: DependencyGraph private var seenConflictingFiles: [URL: [SizeAndCreation]] = [:] @@ -36,7 +36,8 @@ class OutputPostprocessor { dynamicDependencyToPath = dynamicDependencies(in: self.archive) - graph = DependencyGraphBuilder.build(targets: targets) + let builder = DependencyGraphBuilder(provider: targets, values: Array(targets.targets)) + graph = builder.graph if dumpGraph { do { try graph.toDot(output.appendingPathComponent("graph.dot").filePath) @@ -83,7 +84,8 @@ class OutputPostprocessor { private func process( target: Target ) throws -> Set { - let chain = graph.chain(for: target) + // let chain = graph.chain(for: target) + let chain = [Node]() logger.debug("Chain for target: \(target.nameForOutput):\n") chain.forEach { logger.debug("\($0)") } @@ -94,7 +96,7 @@ class OutputPostprocessor { for node in chain { logger.debug("Processing Node: \(node.name)") // Ensure node is not a dynamic dependency - guard dynamicDependencyToPath[node.target.nameForOutput] == nil else { continue } + guard dynamicDependencyToPath[node.value.nameForOutput] == nil else { continue } // Only care about moving dependencies into dependers - check this node's edges to dependent relationships let dependers = node.edges @@ -102,13 +104,13 @@ class OutputPostprocessor { .map { $0.to } // Move node's IR into depender's IR folder - guard let nodeFolderPath = node.target.irFolderPath else { + guard let nodeFolderPath = node.value.irFolderPath else { logger.debug("IR folder for node: \(node) is nil") continue } for depender in dependers { - guard let dependerFolderPath = depender.target.irFolderPath else { + guard let dependerFolderPath = depender.value.irFolderPath else { logger.debug("IR folder for depender node \(depender) is nil") continue } diff --git a/Sources/GenIR/PIFCache.swift b/Sources/GenIR/PIFCache.swift index 4f4373d..c45f555 100644 --- a/Sources/GenIR/PIFCache.swift +++ b/Sources/GenIR/PIFCache.swift @@ -1,7 +1,9 @@ import Foundation import PIFSupport -struct PIFCache { +class PIFCache { + public typealias GUID = String + private let buildCache: URL private let pifCachePath: URL private let workspace: PIF.Workspace @@ -48,4 +50,42 @@ struct PIFCache { ) } } + + var projects: [PIF.Project] { + workspace.projects + } + + private lazy var projectsByGUID: [GUID: PIF.Project] = { + workspace + .projects + .reduce(into: [GUID: PIF.Project]()) { result, element in + result[element.guid] = element + } + }() + + func project(for guid: GUID) -> PIF.Project? { + projectsByGUID[guid] + } + + // TODO: do we need to handle Aggregate targets here? Probably - investigate and update to BaseTarget if so + var targets: [PIF.Target] { + workspace + .projects + .flatMap { + $0 + .targets + .compactMap { $0 as? PIF.Target } + } + } + + private lazy var targetsByGUID: [GUID: PIF.Target] = { + targets + .reduce(into: [GUID: PIF.Target]()) { result, element in + result[element.guid] = element + } + }() + + func target(for guid: GUID) -> PIF.Target? { + targetsByGUID[guid] + } } diff --git a/Sources/GenIR/Targets/Target.swift b/Sources/GenIR/Targets/Target.swift index 6d4835d..d77af00 100644 --- a/Sources/GenIR/Targets/Target.swift +++ b/Sources/GenIR/Targets/Target.swift @@ -132,3 +132,9 @@ extension Target: CustomStringConvertible { """ } } + +extension Target: NodeValue { + var value: Self { + self + } +} diff --git a/Sources/GenIR/Targets/Targets.swift b/Sources/GenIR/Targets/Targets.swift index 7d9e582..3c6dfe3 100644 --- a/Sources/GenIR/Targets/Targets.swift +++ b/Sources/GenIR/Targets/Targets.swift @@ -85,21 +85,6 @@ class Targets { return nil } - - // TODO: once we stabilize Targets, this should return a Set not [String] - func calculateDependencies(for target: Target) -> [String] { - // TODO: eventually we'd like to move some of the project dependencies calculations here - var dependencies = project.dependencies(for: target.name) - - if dependencies.count == 0, let productName = target.productName { - // HACK: once we stabilize Targets to not use one of two potential names, this can be removed... - dependencies = project.dependencies(for: productName) - } - - logger.debug("Calculated dependencies for target: \(target.name). Dependencies: \(dependencies)") - - return dependencies - } } extension Targets: Collection { @@ -123,3 +108,23 @@ extension Targets: Collection { targets.makeIterator() } } + +extension Targets: DependencyProviding { + typealias Value = Target + + func dependencies(for value: Target) -> [Target] { + // // TODO: once we stabilize Targets, this should return a Set not [String] + // func calculateDependencies(for target: Target) -> [String] { + // TODO: eventually we'd like to move some of the project dependencies calculations here + var dependencies = project.dependencies(for: value.name) + + if dependencies.count == 0, let productName = value.productName { + // HACK: once we stabilize Targets to not use one of two potential names, this can be removed... + dependencies = project.dependencies(for: productName) + } + + logger.debug("Calculated dependencies for target: \(value.name). Dependencies: \(dependencies)") + + return dependencies.compactMap { target(for: $0) } + } +} From c4fc00c490002d7bae9bbd949a3151881ecef6c8 Mon Sep 17 00:00:00 2001 From: NinjaLikesCheez Date: Fri, 10 May 2024 14:13:06 +0200 Subject: [PATCH 04/21] Adopt PIF Targets into the rest of the project --- Sources/GenIR/CompilerCommandRunner.swift | 9 +- .../Dependency Graph/DependencyGraph.swift | 23 +-- .../DependencyGraphBuilder.swift | 8 +- Sources/GenIR/Dependency Graph/Node.swift | 20 +-- Sources/GenIR/GenIR.swift | 32 +++- Sources/GenIR/OutputPostprocessor.swift | 78 +++++----- Sources/GenIR/PIFCache.swift | 85 ++++++++--- Sources/GenIR/Target.swift | 41 +++++ Sources/GenIR/Targets/Target.swift | 140 ------------------ Sources/GenIR/Targets/Targets.swift | 130 ---------------- Sources/GenIR/XcodeLogParser.swift | 38 ++--- 11 files changed, 219 insertions(+), 385 deletions(-) create mode 100644 Sources/GenIR/Target.swift delete mode 100644 Sources/GenIR/Targets/Target.swift delete mode 100644 Sources/GenIR/Targets/Targets.swift diff --git a/Sources/GenIR/CompilerCommandRunner.swift b/Sources/GenIR/CompilerCommandRunner.swift index bec6c29..ac8edf4 100644 --- a/Sources/GenIR/CompilerCommandRunner.swift +++ b/Sources/GenIR/CompilerCommandRunner.swift @@ -42,7 +42,7 @@ struct CompilerCommandRunner { } /// Starts the runner - func run(targets: Targets) throws { + func run(targets: [Target]) throws { // Quick, do a hack! try buildCacheManipulator.manipulate() @@ -50,7 +50,10 @@ struct CompilerCommandRunner { defer { try? fileManager.removeItem(at: tempDirectory) } logger.debug("Using temp directory as working directory: \(tempDirectory.filePath)") - let totalCommands = targets.totalCommandCount + let totalCommands = targets + .map { $0.commands.count } + .reduce(0, +) + logger.info("Total commands to run: \(totalCommands)") var totalModulesRun = 0 @@ -58,7 +61,7 @@ struct CompilerCommandRunner { for target in targets { logger.info("Operating on target: \(target.name). Total modules processed: \(totalModulesRun)") - totalModulesRun += try run(commands: target.commands, for: target.nameForOutput, at: tempDirectory) + totalModulesRun += try run(commands: target.commands, for: target.productName, at: tempDirectory) } try fileManager.moveItemReplacingExisting(from: tempDirectory, to: output) diff --git a/Sources/GenIR/Dependency Graph/DependencyGraph.swift b/Sources/GenIR/Dependency Graph/DependencyGraph.swift index 7cd0806..7db2f3d 100644 --- a/Sources/GenIR/Dependency Graph/DependencyGraph.swift +++ b/Sources/GenIR/Dependency Graph/DependencyGraph.swift @@ -10,7 +10,7 @@ import Foundation /// A directed graph that maps dependencies between values (nodes) via edges (directions between nodes) class DependencyGraph { /// All the nodes in the graph - private(set) var nodes: [Node] = [] + private(set) var nodes = [String: Node]() /// Adds a value to the graph /// - Parameter value: the value to add @@ -21,7 +21,7 @@ class DependencyGraph { } let node = Node(value) - nodes.append(node) + nodes[value.valueName] = node return node } @@ -29,7 +29,7 @@ class DependencyGraph { /// - Parameter value: the value to look for /// - Returns: the node for the given value, if found func findNode(for value: Value) -> Node? { - nodes.first(where: { $0.value == value }) + nodes[value.valueName] } /// Builds a dependency 'chain' for a value using a depth-first search @@ -37,7 +37,7 @@ class DependencyGraph { /// - Returns: the chain of nodes, starting func chain(for value: Value) -> [Node] { guard let node = findNode(for: value) else { - logger.debug("Couldn't find node for value: \(value.name)") + logger.debug("Couldn't find node for value: \(value.valueName)") return [] } @@ -47,9 +47,14 @@ class DependencyGraph { func toDot(_ path: String) throws { var contents = "digraph DependencyGraph {\n" - for node in nodes { + for node in nodes.values { for edge in node.edges.filter({ $0.relationship == .dependency }) { - contents.append("\(node.name.replacingOccurrences(of: "-", with: "_")) -> \(edge.to.name.replacingOccurrences(of: "-", with: "_"))\n") + func dotSanitized(for name: String) -> String { + name + .replacingOccurrences(of: "-", with: "_") + .replacingOccurrences(of: ".", with: "_") + } + contents.append("\(dotSanitized(for: node.valueName)) -> \(dotSanitized(for: edge.to.valueName))\n") } } @@ -61,12 +66,12 @@ class DependencyGraph { /// - Parameter node: the node whose children to search through /// - Returns: an array of nodes ordered by a depth-first search approach private func depthFirstSearch(startingAt node: Node) -> [Node] { - logger.debug("----\nSearching for: \(node.value.name)") + logger.debug("----\nSearching for: \(node.value.valueName)") var visited = Set>() var chain = [Node]() func depthFirst(node: Node) { - logger.debug("inserting node: \(node.value.name)") + logger.debug("inserting node: \(node.value.valueName)") visited.insert(node) logger.debug("visited: \(visited)") @@ -80,7 +85,7 @@ class DependencyGraph { } } - logger.debug("appending to chain: \(node.value.name)") + logger.debug("appending to chain: \(node.value.valueName)") chain.append(node) } diff --git a/Sources/GenIR/Dependency Graph/DependencyGraphBuilder.swift b/Sources/GenIR/Dependency Graph/DependencyGraphBuilder.swift index cdc6b49..9e88715 100644 --- a/Sources/GenIR/Dependency Graph/DependencyGraphBuilder.swift +++ b/Sources/GenIR/Dependency Graph/DependencyGraphBuilder.swift @@ -9,7 +9,8 @@ protocol DependencyProviding { associatedtype Value: NodeValue func dependencies(for value: Value) -> [Value] } -class DependencyGraphBuilder where Value == Provider.Value { + +class DependencyGraphBuilder where Value == Provider.Value { private let provider: Provider let graph = DependencyGraph() @@ -25,13 +26,12 @@ class DependencyGraphBuilder wh /// - value: the value to add @discardableResult private func add(value: Value) -> Node { - logger.debug("Adding value: \(value.name) to graph") - if let existingNode = graph.findNode(for: value) { - logger.debug("Already inserted node: \(existingNode.name). Skipping") return existingNode } + logger.debug("Adding value: \(value.valueName) to graph") + let dependencies = provider.dependencies(for: value) let node = graph.addNode(value: value) diff --git a/Sources/GenIR/Dependency Graph/Node.swift b/Sources/GenIR/Dependency Graph/Node.swift index f576253..659dad4 100644 --- a/Sources/GenIR/Dependency Graph/Node.swift +++ b/Sources/GenIR/Dependency Graph/Node.swift @@ -8,8 +8,8 @@ import Foundation protocol NodeValue: Hashable { - /// The name of this node, mostly used for debugging and printing - var name: String { get } + /// The name of this node, this should be unique + var valueName: String { get } } class Node { @@ -17,18 +17,20 @@ class Node { private(set) var edges = [Edge]() /// The value this node represents let value: Value - /// The name of this node, mostly used for debugging and printing - let name: String + /// The name of this node + var valueName: String { + value.valueName + } init(_ value: Value) { self.value = value - self.name = value.name } /// Adds an edge to this node /// - Parameter edge: the edge to add func add(edge: Edge) { - if edges.filter({ $0.to.name == edge.to.name }).count == 0 { + // TODO: slow - change. + if edges.filter({ $0.to.valueName == edge.to.valueName }).count == 0 { edges.append(edge) } } @@ -45,9 +47,9 @@ extension Node: CustomStringConvertible { var description = "" if !edges.isEmpty { - description += "[Node: \(value.name), edges: \(edges.filter({ $0.relationship == .dependency }).map { $0.to.value.name})] " + description += "[Node: \(value.valueName), edges: \(edges.filter({ $0.relationship == .dependency }).map { $0.to.value.valueName})] " } else { - description += "[Node: \(value.name)] " + description += "[Node: \(value.valueName)] " } return description @@ -56,7 +58,7 @@ extension Node: CustomStringConvertible { extension Node: Hashable { func hash(into hasher: inout Hasher) { - hasher.combine(name) + hasher.combine(valueName) hasher.combine(value) } } diff --git a/Sources/GenIR/GenIR.swift b/Sources/GenIR/GenIR.swift index d2687cb..b7563c6 100644 --- a/Sources/GenIR/GenIR.swift +++ b/Sources/GenIR/GenIR.swift @@ -2,6 +2,7 @@ import Foundation import ArgumentParser import Logging import PBXProjParser +import PIFSupport /// Global logger object var logger = Logger(label: Bundle.main.bundleIdentifier ?? "com.veracode.gen-ir", factory: StdOutLogHandler.init) @@ -112,11 +113,11 @@ struct IREmitterCommand: ParsableCommand { dumpDependencyGraph: Bool ) throws { let output = archive.appendingPathComponent("IR") - let project = try ProjectParser(path: project, logLevel: level) - var targets = Targets(for: project) + // let project = try ProjectParser(path: project, logLevel: level) + // var targets = Targets(for: project) let log = try logParser(for: log) - try log.parse(&targets) + try log.parse() // Find and parse the PIF cache let pifCache = try PIFCache(buildCache: log.buildCachePath) @@ -128,6 +129,12 @@ struct IREmitterCommand: ParsableCommand { dryRun: dryRun ) + let targets = pifCache + .targets + .map { + Target(baseTarget: $0, commands: log.targetCommands[$0.name] ?? []) + } + let runner = CompilerCommandRunner( output: output, buildCacheManipulator: buildCacheManipulator, @@ -135,13 +142,26 @@ struct IREmitterCommand: ParsableCommand { ) try runner.run(targets: targets) + let provider = PIFDependencyProvider(targets: targets, cache: pifCache) + let builder = DependencyGraphBuilder(provider: provider, values: targets) + let graph = builder.graph + + if dumpDependencyGraph { + do { + try graph.toDot(output.appendingPathComponent("graph.dot").filePath) + } catch { + logger.error("toDot error: \(error)") + } + } + let postprocessor = try OutputPostprocessor( archive: archive, output: output, - targets: targets, - dumpGraph: dumpDependencyGraph + graph: graph, + targets: targets ) - try postprocessor.process(targets: &targets) + + try postprocessor.process() } /// Gets an `XcodeLogParser` for a path diff --git a/Sources/GenIR/OutputPostprocessor.swift b/Sources/GenIR/OutputPostprocessor.swift index 2d397fe..4b3877a 100644 --- a/Sources/GenIR/OutputPostprocessor.swift +++ b/Sources/GenIR/OutputPostprocessor.swift @@ -25,55 +25,49 @@ class OutputPostprocessor { private let dynamicDependencyToPath: [String: URL] private let graph: DependencyGraph + private let targets: [Target] private var seenConflictingFiles: [URL: [SizeAndCreation]] = [:] + private let targetsToPaths: [Target: URL] private let manager: FileManager = .default - init(archive: URL, output: URL, targets: Targets, dumpGraph: Bool) throws { + init(archive: URL, output: URL, graph: DependencyGraph, targets: [Target]) throws { self.output = output self.archive = archive + self.graph = graph + self.targets = targets - dynamicDependencyToPath = dynamicDependencies(in: self.archive) - - let builder = DependencyGraphBuilder(provider: targets, values: Array(targets.targets)) - graph = builder.graph - if dumpGraph { - do { - try graph.toDot(output.appendingPathComponent("graph.dot").filePath) - } catch { - logger.error("toDot error: \(error)") + let namesToTargets = targets + .reduce(into: [String: Target]()) { partial, target in + partial[target.productName] = target } - } - } - /// Starts the OutputPostprocessor - /// - Parameter targets: the targets to operate on - func process(targets: inout Targets) throws { - try manager.directories(at: output, recursive: false) - .forEach { path in - let product = path.lastPathComponent.deletingPathExtension() - - guard let target = targets.target(for: product) else { - logger.error("Failed to look up target for product: \(product)") - return + targetsToPaths = try manager + .directories(at: output, recursive: false) + .reduce(into: [Target: URL]()) { partial, path in + if let target = namesToTargets[path.lastPathComponent] { + partial[target] = path + } else { + logger.error("Failed to look up target for path: \(path)") } - - target.irFolderPath = path } - // TODO: remove 'static' deps so we don't duplicate them in the submission? - _ = try manager.directories(at: output, recursive: false) - .flatMap { path in - let product = path.lastPathComponent.deletingPathExtension() + let namedTargetSet = Set(namesToTargets.values) + let pathTargetSet = Set(targetsToPaths.keys) + let difference = namedTargetSet.symmetricDifference(pathTargetSet) - guard let target = targets.target(for: product) else { - logger.error("Failed to look up target for product: \(product)") - return Set() - } + logger.debug("target set difference: \(difference)") - return try process(target: target) - } + dynamicDependencyToPath = dynamicDependencies(in: self.archive) + } + + /// Starts the OutputPostprocessor + /// - Parameter targets: the targets to operate on + func process() throws { + // TODO: remove 'static' deps so we don't duplicate them in the submission? + _ = try targetsToPaths + .map { try process(target: $0.key, at: $0.value) } } /// Processes an individual target @@ -82,21 +76,21 @@ class OutputPostprocessor { /// - path: the output path /// - Returns: private func process( - target: Target + target: Target, + at path: URL ) throws -> Set { - // let chain = graph.chain(for: target) - let chain = [Node]() + let chain = graph.chain(for: target) - logger.debug("Chain for target: \(target.nameForOutput):\n") + logger.debug("Chain for target: \(target.productName):\n") chain.forEach { logger.debug("\($0)") } // We want to process the chain, visiting each node _shallowly_ and copy it's dependencies into it's parent var processed = Set() for node in chain { - logger.debug("Processing Node: \(node.name)") + logger.debug("Processing Node: \(node.valueName)") // Ensure node is not a dynamic dependency - guard dynamicDependencyToPath[node.value.nameForOutput] == nil else { continue } + guard dynamicDependencyToPath[node.value.productName] == nil else { continue } // Only care about moving dependencies into dependers - check this node's edges to dependent relationships let dependers = node.edges @@ -104,13 +98,13 @@ class OutputPostprocessor { .map { $0.to } // Move node's IR into depender's IR folder - guard let nodeFolderPath = node.value.irFolderPath else { + guard let nodeFolderPath = targetsToPaths[node.value] else { logger.debug("IR folder for node: \(node) is nil") continue } for depender in dependers { - guard let dependerFolderPath = depender.value.irFolderPath else { + guard let dependerFolderPath = targetsToPaths[depender.value] else { logger.debug("IR folder for depender node \(depender) is nil") continue } diff --git a/Sources/GenIR/PIFCache.swift b/Sources/GenIR/PIFCache.swift index c45f555..f1e174b 100644 --- a/Sources/GenIR/PIFCache.swift +++ b/Sources/GenIR/PIFCache.swift @@ -55,37 +55,76 @@ class PIFCache { workspace.projects } - private lazy var projectsByGUID: [GUID: PIF.Project] = { + // private lazy var projectsByGUID: [GUID: PIF.Project] = { + // workspace + // .projects + // .reduce(into: [GUID: PIF.Project]()) { result, element in + // result[element.guid] = element + // } + // }() + + // func project(for guid: GUID) -> PIF.Project? { + // projectsByGUID[guid] + // } + + // TODO: We cab possibly filter out some targets here for performance + var targets: [PIF.BaseTarget] { workspace .projects - .reduce(into: [GUID: PIF.Project]()) { result, element in - result[element.guid] = element - } - }() + .flatMap { $0.targets } + } + + // private lazy var targetsByGUID: [GUID: PIF.BaseTarget] = { + // targets + // .reduce(into: [GUID: PIF.BaseTarget]()) { result, element in + // result[element.guid] = element + // } + // }() - func project(for guid: GUID) -> PIF.Project? { - projectsByGUID[guid] + // func target(for guid: GUID) -> PIF.BaseTarget? { + // targetsByGUID[guid] + // } +} + +extension PIF.BaseTarget: NodeValue { + var valueName: String { + if let target = self as? PIF.Target, !target.productName.isEmpty { + return target.productName + } + + return name } +} - // TODO: do we need to handle Aggregate targets here? Probably - investigate and update to BaseTarget if so - var targets: [PIF.Target] { - workspace - .projects - .flatMap { - $0 - .targets - .compactMap { $0 as? PIF.Target } - } +extension PIF.BaseTarget: Hashable { + public func hash(into hasher: inout Hasher) { + hasher.combine(ObjectIdentifier(self)) } - private lazy var targetsByGUID: [GUID: PIF.Target] = { - targets - .reduce(into: [GUID: PIF.Target]()) { result, element in - result[element.guid] = element + public static func == (lhs: PIF.BaseTarget, rhs: PIF.BaseTarget) -> Bool { + ObjectIdentifier(lhs) == ObjectIdentifier(rhs) + } +} + +struct PIFDependencyProvider: DependencyProviding { + private let targets: [Target] + private let cache: PIFCache + private var guidToTargets: [PIFCache.GUID: Target] + + init(targets: [Target], cache: PIFCache) { + self.targets = targets + self.cache = cache + + self.guidToTargets = targets + .reduce(into: [PIFCache.GUID: Target]()) { partial, target in + partial[target.baseTarget.guid] = target } - }() + } - func target(for guid: GUID) -> PIF.Target? { - targetsByGUID[guid] + func dependencies(for value: Target) -> [Target] { + value + .baseTarget + .dependencies + .map { guidToTargets[$0.targetGUID]! } } } diff --git a/Sources/GenIR/Target.swift b/Sources/GenIR/Target.swift new file mode 100644 index 0000000..f480f4a --- /dev/null +++ b/Sources/GenIR/Target.swift @@ -0,0 +1,41 @@ +// +// Target.swift +// +// +// Created by Thomas Hedderwick on 28/02/2023. +// + +import Foundation +import PBXProjParser +import PIFSupport + +class Target { + var name: String { baseTarget.name } + var productName: String { + (baseTarget as? PIF.Target)?.productName ?? baseTarget.name + } + // TODO: we need to handle SPM's insane naming scheme for products here ^ + + let baseTarget: PIF.BaseTarget + let commands: [CompilerCommand] + + init(baseTarget: PIF.BaseTarget, commands: [CompilerCommand]) { + self.baseTarget = baseTarget + self.commands = commands + } +} + +extension Target: NodeValue { + var value: Self { self } + var valueName: String { productName } +} + +extension Target: Hashable { + func hash(into hasher: inout Hasher) { + hasher.combine(ObjectIdentifier(self)) + } + + static func == (lhs: Target, rhs: Target) -> Bool { + ObjectIdentifier(lhs) == ObjectIdentifier(rhs) + } +} diff --git a/Sources/GenIR/Targets/Target.swift b/Sources/GenIR/Targets/Target.swift deleted file mode 100644 index d77af00..0000000 --- a/Sources/GenIR/Targets/Target.swift +++ /dev/null @@ -1,140 +0,0 @@ -// -// Target.swift -// -// -// Created by Thomas Hedderwick on 28/02/2023. -// - -import Foundation -import PBXProjParser - -/// Represents the concept of a 'Target' that is loosely: a target in Xcode parlance, a swift package product. -class Target { - /// The name of the target - let name: String - - /// The product name of the target, if one exists - lazy var productName: String? = { - switch backingTarget { - case .native(let target): - return target.productName - case .packageDependency(let spm): - return spm.productName - default: - return nil - } - }() - - /// The various types of target we're wrapping - enum BackingTarget: Hashable { - /// The Native Target this Target represents - case native(PBXNativeTarget) - - /// The Swift Dependency this Target represents - case packageDependency(XCSwiftPackageProductDependency) - } - - /// The backing object this Target represents - var backingTarget: BackingTarget? - - /// A list of CompilerCommands relating to this target - var commands: [CompilerCommand] = [] - - // TODO: Remove - /// A list of dependencies of this Target - private(set) var dependencies: [String] = [] - - /// The project parser relating to this target - let project: ProjectParser? - - /// The name to use when writing IR to disk, prefer the product name if possible. - lazy var nameForOutput: String = { - switch backingTarget { - case .native(let target): - return path(for: target) ?? productName ?? name - case .packageDependency, .none: - return productName ?? name - } - }() - - /// Gets the path for native targets - lazy var path: String? = { - switch backingTarget { - case .native(let target): - return path(for: target) - case .packageDependency, .none: - return nil - } - }() - - /// The path to the IR folder on disk - var irFolderPath: URL? - - init( - name: String, - backingTarget: BackingTarget? = nil, - commands: [CompilerCommand] = [], - dependencies: [String] = [], - project: ProjectParser? = nil - ) { - self.name = name - self.backingTarget = backingTarget - self.commands = commands - self.dependencies = dependencies - self.project = project - } - - /// Gets the 'path' (normally the name of the target's product) for a given target - private func path(for target: PBXNativeTarget) -> String? { - guard let model = project?.model(for: target.name) else { - logger.debug("Failed to get model for target: \(target)") - return nil - } - - guard let productReference = target.productReference else { - logger.debug("Failed to get product reference for target: \(target). Possibly a SPM Package description?") - return nil - } - - guard let reference = model.object(forKey: productReference, as: PBXFileReference.self) else { - logger.error("Failed to get object for target productReference: \(productReference)") - return nil - } - - return (reference.path as NSString).lastPathComponent as String - } -} - -// MARK: - Protocol Conformance for Hashable storage -extension Target: Equatable { - static func == (lhs: Target, rhs: Target) -> Bool { - lhs.name == rhs.name && - lhs.productName == rhs.productName && - lhs.backingTarget == rhs.backingTarget && - lhs.commands == rhs.commands && - lhs.dependencies == rhs.dependencies - } -} - -extension Target: Hashable { - public func hash(into hasher: inout Hasher) { - hasher.combine(name) - hasher.combine(productName) - hasher.combine(backingTarget) - } -} - -extension Target: CustomStringConvertible { - var description: String { - """ - Target(name: \(name), product: \(productName ?? "nil"), \ - commands: \(commands.count), backing target: \(String(describing: backingTarget))) - """ - } -} - -extension Target: NodeValue { - var value: Self { - self - } -} diff --git a/Sources/GenIR/Targets/Targets.swift b/Sources/GenIR/Targets/Targets.swift deleted file mode 100644 index 3c6dfe3..0000000 --- a/Sources/GenIR/Targets/Targets.swift +++ /dev/null @@ -1,130 +0,0 @@ -// -// Target.swift -// -// -// Created by Thomas Hedderwick on 05/04/2023. -// - -import Foundation -import PBXProjParser - -/// Represents a collection of `Target`s -class Targets { - /// The underlying storage of `Target`s - private(set) var targets: Set = [] - - /// The project targets where parsed from - private let project: ProjectParser - - init(for project: ProjectParser) { - self.project = project - - project.targets.forEach { insert(native: $0) } - project.packages.forEach { insert(package: $0) } - } - - /// The sum of all commands for all stored targets - var totalCommandCount: Int { - targets - .map { $0.commands.count } - .reduce(0, +) - } - - /// Inserts the given native target into the container if it's not already present - /// - Parameter target: the element to insert - /// - Returns: `(true, target)` if `target` wasn't in the container. `(false, existingElement)` if `target` is in the container. - @discardableResult - func insert(native target: PBXNativeTarget) -> (inserted: Bool, memberAfterInsert: Element) { - let newTarget = Target( - name: target.name, - backingTarget: .native(target), - project: project - ) - - return targets.insert(newTarget) - } - - /// Inserts the given package into the container if it's not already present - /// - Parameter package: the element to insert - /// - Returns: `(true, target)` if `target` wasn't in the container. `(false, existingElement)` if `target` is in the container. - @discardableResult - func insert(package target: XCSwiftPackageProductDependency) -> (inserted: Bool, memberAfterInsert: Element) { - // TODO: when we can handle SPM transitive deps, should we look up the name here? Can we even do that? - let newTarget = Target( - name: target.productName, - backingTarget: .packageDependency(target), - project: project - ) - - return targets.insert(newTarget) - } - - /// Inserts the given target into the container if it's not already present - /// - Parameter target: the element to insert - /// - Returns: `(true, target)` if `target` wasn't in the container. `(false, existingElement)` if `target` is in the container. - @discardableResult - func insert(target: Target) -> (inserted: Bool, memberAfterInsert: Element) { - targets.insert(target) - } - - // TODO: maybe specialize a product vs name lookup for those sweet sweet milliseconds - func target(for key: String) -> Target? { - for target in targets { - if target.name == key { - return target - } else if target.productName == key { - return target - } else if target.path == key { - return target - } - } - - for target in targets where target.nameForOutput.deletingPathExtension() == key { - return target - } - - return nil - } -} - -extension Targets: Collection { - typealias CollectionType = Set - typealias Index = CollectionType.Index - typealias Element = CollectionType.Element - - var startIndex: Index { targets.startIndex } - var endIndex: Index { targets.endIndex } - -// TODO: Add subscripting support for looking up targets by name or product - subscript(index: Index) -> CollectionType.Element { - targets[index] - } - - func index(after index: Index) -> Index { - targets.index(after: index) - } - - func makeIterator() -> CollectionType.Iterator { - targets.makeIterator() - } -} - -extension Targets: DependencyProviding { - typealias Value = Target - - func dependencies(for value: Target) -> [Target] { - // // TODO: once we stabilize Targets, this should return a Set not [String] - // func calculateDependencies(for target: Target) -> [String] { - // TODO: eventually we'd like to move some of the project dependencies calculations here - var dependencies = project.dependencies(for: value.name) - - if dependencies.count == 0, let productName = value.productName { - // HACK: once we stabilize Targets to not use one of two potential names, this can be removed... - dependencies = project.dependencies(for: productName) - } - - logger.debug("Calculated dependencies for target: \(value.name). Dependencies: \(dependencies)") - - return dependencies.compactMap { target(for: $0) } - } -} diff --git a/Sources/GenIR/XcodeLogParser.swift b/Sources/GenIR/XcodeLogParser.swift index 083a16b..ced8b74 100644 --- a/Sources/GenIR/XcodeLogParser.swift +++ b/Sources/GenIR/XcodeLogParser.swift @@ -17,6 +17,7 @@ class XcodeLogParser { private(set) var settings: [String: String] = [:] /// The path to the Xcode build cache private(set) var buildCachePath: URL! + private(set) var targetCommands: [String: [CompilerCommand]] = [:] enum Error: Swift.Error { case noCommandsFound(String) @@ -32,11 +33,11 @@ class XcodeLogParser { /// Start parsing the build log /// - Parameter targets: The global list of targets - func parse(_ targets: inout Targets) throws { - parseBuildLog(log, &targets) + func parse() throws { + parseBuildLog(log) - if targets.isEmpty { - logger.debug("Found no targets in log: \(log)") + if targetCommands.isEmpty { + logger.debug("Found no targets in log") throw Error.noTargetsFound( """ @@ -45,8 +46,13 @@ class XcodeLogParser { ) } - if targets.totalCommandCount == 0 { - logger.debug("Found no commands in log: \(log)") + let totalCommandCount = targetCommands + .values + .compactMap { $0.count } + .reduce(0, +) + + if totalCommandCount == 0 { + logger.debug("Found no commands in log") throw Error.noCommandsFound( """ @@ -60,12 +66,11 @@ class XcodeLogParser { } } - /// Parses an array representing the contents of an Xcode build log + /// Parses an array representing the contents of an Xcode build log /// - Parameters: /// - lines: contents of the Xcode build log lines - /// - targets: the container to add found targets to - private func parseBuildLog(_ lines: [String], _ targets: inout Targets) { - var currentTarget: Target? + private func parseBuildLog(_ lines: [String]) { + var currentTarget: String? var seenTargets = Set() for (index, line) in lines.enumerated() { @@ -97,17 +102,12 @@ class XcodeLogParser { .deletingLastPathComponent() } - if let target = target(from: line), currentTarget?.name != target { + if let target = target(from: line), currentTarget != target { if seenTargets.insert(target).inserted { logger.debug("Found target: \(target)") } - if let targetObject = targets.target(for: target) { - currentTarget = targetObject - } else { - currentTarget = .init(name: target) - targets.insert(target: currentTarget!) - } + currentTarget = target } guard let currentTarget else { @@ -121,9 +121,9 @@ class XcodeLogParser { continue } - logger.debug("Found \(compilerCommand.compiler.rawValue) compiler command for target: \(currentTarget.name)") + logger.debug("Found \(compilerCommand.compiler.rawValue) compiler command for target: \(currentTarget)") - currentTarget.commands.append(compilerCommand) + targetCommands[currentTarget, default: [CompilerCommand]()].append(compilerCommand) } } From c1dc4ba558290ed74fb1b113d408d610ad911d06 Mon Sep 17 00:00:00 2001 From: NinjaLikesCheez Date: Mon, 13 May 2024 10:44:22 +0200 Subject: [PATCH 05/21] Helper function for creating Targets --- Sources/GenIR/GenIR.swift | 6 +----- Sources/GenIR/Target.swift | 10 ++++++++++ 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/Sources/GenIR/GenIR.swift b/Sources/GenIR/GenIR.swift index b7563c6..465ce17 100644 --- a/Sources/GenIR/GenIR.swift +++ b/Sources/GenIR/GenIR.swift @@ -129,11 +129,7 @@ struct IREmitterCommand: ParsableCommand { dryRun: dryRun ) - let targets = pifCache - .targets - .map { - Target(baseTarget: $0, commands: log.targetCommands[$0.name] ?? []) - } + let targets = Target.targets(from: pifCache.targets, with: log.targetCommands) let runner = CompilerCommandRunner( output: output, diff --git a/Sources/GenIR/Target.swift b/Sources/GenIR/Target.swift index f480f4a..cce6585 100644 --- a/Sources/GenIR/Target.swift +++ b/Sources/GenIR/Target.swift @@ -25,6 +25,16 @@ class Target { } } +extension Target { + static func targets(from targets: [PIF.BaseTarget], with targetsToCommands: [String: [CompilerCommand]]) -> [Target] { + targets + .map { + Target(baseTarget: $0, commands: targetsToCommands[$0.name] ?? []) + } + } +} + + extension Target: NodeValue { var value: Self { self } var valueName: String { productName } From 88ef1f95165342133957231da73e0f363a8091cd Mon Sep 17 00:00:00 2001 From: NinjaLikesCheez Date: Mon, 13 May 2024 15:08:19 +0200 Subject: [PATCH 06/21] Update tests to use new PIF targets One test is failing because the output postprocessor is not moving dependencies - that still needs to be addressed --- PIF/Sources/PIFSupport/PIF.swift | 9 +- Sources/GenIR/PIFCache.swift | 101 ++++++++++++++---- Sources/GenIR/Target.swift | 3 +- .../App/App.xcodeproj/project.pbxproj | 12 ++- .../FrameworkA.xcodeproj/project.pbxproj | 12 ++- .../FrameworkB.xcodeproj/project.pbxproj | 12 ++- Tests/GenIRTests/DependencyGraphTests.swift | 34 +++--- Tests/GenIRTests/MultipleAppTests.swift | 20 ++-- .../OutputPostprocessorFileMoverTests.swift | 12 +-- Tests/GenIRTests/TestContext.swift | 45 +++++++- Tests/GenIRTests/UmbrellaTests.swift | 40 +++---- .../GenIRTests/WorkspaceContainerTests.swift | 9 +- Tests/GenIRTests/WorkspaceTests.swift | 13 ++- Tests/GenIRTests/gen_irTests.swift | 7 +- 14 files changed, 218 insertions(+), 111 deletions(-) diff --git a/PIF/Sources/PIFSupport/PIF.swift b/PIF/Sources/PIFSupport/PIF.swift index ef342cb..9674784 100644 --- a/PIF/Sources/PIFSupport/PIF.swift +++ b/PIF/Sources/PIFSupport/PIF.swift @@ -114,11 +114,7 @@ public enum PIF { projects = try projectContents .map { - // do { - return try PIFDecoder(cache: cachePath).decode(PIF.Project.self, from: $0) - // } catch { - // fatalError(String(data: $0, encoding: .utf8)!) - // } + try PIFDecoder(cache: cachePath).decode(PIF.Project.self, from: $0) } } } @@ -203,6 +199,9 @@ public enum PIF { /// Indicates that the path is relative to the SDKROOT case sdkRoot = "SDKROOT" + + /// Indicates that the path is relative to the DEVELOPER_DIR (normally in the Xcode.app bundle) + case developerDir = "DEVELOPER_DIR" } public let guid: GUID diff --git a/Sources/GenIR/PIFCache.swift b/Sources/GenIR/PIFCache.swift index f1e174b..9de608d 100644 --- a/Sources/GenIR/PIFCache.swift +++ b/Sources/GenIR/PIFCache.swift @@ -2,15 +2,13 @@ import Foundation import PIFSupport class PIFCache { - public typealias GUID = String - private let buildCache: URL private let pifCachePath: URL private let workspace: PIF.Workspace enum Error: Swift.Error { case nonexistentCache(String) - case pifError(String) + case pifError(Swift.Error) } init(buildCache: URL) throws { @@ -21,7 +19,7 @@ class PIFCache { let cache = try PIFParser(cachePath: pifCachePath) workspace = cache.workspace } catch { - throw Error.pifError(error.localizedDescription) + throw Error.pifError(error) } } @@ -74,6 +72,63 @@ class PIFCache { .flatMap { $0.targets } } + private lazy var namesToTargets: [String: PIF.BaseTarget] = { + targets + .reduce(into: [String: PIF.BaseTarget]()) { partial, target in + partial[target.name] = target + } + }() + + private lazy var productNamesToTargets: [String: PIF.BaseTarget] = { + targets + .compactMap { $0 as? PIF.Target } + .reduce(into: [String: PIF.BaseTarget]()) { partial, target in + partial[target.productName] = target + } + }() + + private func fileReferences(for project: PIF.Project) -> [PIF.FileReference] { + func resolveChildren(starting children: [PIF.Reference], result: inout [PIF.FileReference]) { + for child in children { + if let fileReference = child as? PIF.FileReference { + result.append(fileReference) + } else if let group = child as? PIF.Group { + resolveChildren(starting: group.children, result: &result) + } else { + print("Unhandled reference type: \(child)") + } + } + } + + var result = [PIF.FileReference]() + resolveChildren(starting: project.groupTree.children, result: &result) + return result + } + + lazy var frameworks: [PIF.GUID: PIF.Target] = { + // Here we have to get all the wrapper.framework references from the group, and then attempt to map them to targets + let frameworkFileReferences = projects + .flatMap { fileReferences(for: $0) } + .filter { $0.fileType == "wrapper.framework" } + // TODO: do we filter on sourceTree == "BUILT_PRODUCTS_DIR" here too? + + // Now, stupidly, we have to do a name lookup on the path and use that to look up a target + let frameworks = targets + .compactMap { $0 as? PIF.Target } + .filter { $0.productType == .framework } + .reduce(into: [String: PIF.Target]()) { partial, target in + partial[target.productName] = target + } + + return frameworkFileReferences + // .compactMap { frameworks[$0.path] } // TODO: I think we should get the last path component as the key here - check that + .reduce(into: [PIF.GUID: PIF.Target]()) { partial, fileReference in + // partial[target.guid] = target + // Use the _file reference_ GUID as the key here - we're looking up frameworks by their file reference and not target GUID! + partial[fileReference.guid] = frameworks[fileReference.path] + } + }() + // private lazy var targetsByGUID: [GUID: PIF.BaseTarget] = { // targets // .reduce(into: [GUID: PIF.BaseTarget]()) { result, element in @@ -86,16 +141,6 @@ class PIFCache { // } } -extension PIF.BaseTarget: NodeValue { - var valueName: String { - if let target = self as? PIF.Target, !target.productName.isEmpty { - return target.productName - } - - return name - } -} - extension PIF.BaseTarget: Hashable { public func hash(into hasher: inout Hasher) { hasher.combine(ObjectIdentifier(self)) @@ -109,22 +154,42 @@ extension PIF.BaseTarget: Hashable { struct PIFDependencyProvider: DependencyProviding { private let targets: [Target] private let cache: PIFCache - private var guidToTargets: [PIFCache.GUID: Target] + private var guidToTargets: [PIF.GUID: Target] init(targets: [Target], cache: PIFCache) { self.targets = targets self.cache = cache self.guidToTargets = targets - .reduce(into: [PIFCache.GUID: Target]()) { partial, target in + .reduce(into: [PIF.GUID: Target]()) { partial, target in partial[target.baseTarget.guid] = target } } func dependencies(for value: Target) -> [Target] { - value + // Direct dependencies + let dependencyTargets = value .baseTarget .dependencies - .map { guidToTargets[$0.targetGUID]! } + .compactMap { guidToTargets[$0.targetGUID] } + + // Framework build phase dependencies + let frameworkBuildPhases = value + .baseTarget + .buildPhases + .compactMap { $0 as? PIF.FrameworksBuildPhase } + + let frameworks = frameworkBuildPhases + .flatMap { $0.buildFiles } + .compactMap { + switch $0.reference { + case .file(let guid): return guid + case .target: return nil // TODO: is this fine? I think so since we're looking for .framework file references here not targets which should be a dependency + } + } + .compactMap { cache.frameworks[$0] } + .compactMap { guidToTargets[$0.guid] } + + return dependencyTargets + frameworks } } diff --git a/Sources/GenIR/Target.swift b/Sources/GenIR/Target.swift index cce6585..f33723b 100644 --- a/Sources/GenIR/Target.swift +++ b/Sources/GenIR/Target.swift @@ -34,10 +34,9 @@ extension Target { } } - extension Target: NodeValue { var value: Self { self } - var valueName: String { productName } + var valueName: String { name } } extension Target: Hashable { diff --git a/TestAssets/WorkspaceContainerTest/App/App.xcodeproj/project.pbxproj b/TestAssets/WorkspaceContainerTest/App/App.xcodeproj/project.pbxproj index 8e3249d..879dacb 100644 --- a/TestAssets/WorkspaceContainerTest/App/App.xcodeproj/project.pbxproj +++ b/TestAssets/WorkspaceContainerTest/App/App.xcodeproj/project.pbxproj @@ -215,6 +215,7 @@ CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + CODE_SIGN_IDENTITY = ""; COPY_PHASE_STRIP = NO; DEBUG_INFORMATION_FORMAT = dwarf; ENABLE_STRICT_OBJC_MSGSEND = YES; @@ -275,6 +276,7 @@ CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + CODE_SIGN_IDENTITY = ""; COPY_PHASE_STRIP = NO; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; ENABLE_NS_ASSERTIONS = NO; @@ -302,9 +304,11 @@ buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; - CODE_SIGN_STYLE = Automatic; + CODE_SIGN_IDENTITY = ""; + CODE_SIGN_STYLE = Manual; CURRENT_PROJECT_VERSION = 1; DEVELOPMENT_ASSET_PATHS = "\"App/Preview Content\""; + DEVELOPMENT_TEAM = ""; ENABLE_PREVIEWS = YES; GENERATE_INFOPLIST_FILE = YES; INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES; @@ -319,6 +323,7 @@ MARKETING_VERSION = 1.0; PRODUCT_BUNDLE_IDENTIFIER = com.test.App; PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; @@ -330,9 +335,11 @@ buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; - CODE_SIGN_STYLE = Automatic; + CODE_SIGN_IDENTITY = ""; + CODE_SIGN_STYLE = Manual; CURRENT_PROJECT_VERSION = 1; DEVELOPMENT_ASSET_PATHS = "\"App/Preview Content\""; + DEVELOPMENT_TEAM = ""; ENABLE_PREVIEWS = YES; GENERATE_INFOPLIST_FILE = YES; INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES; @@ -347,6 +354,7 @@ MARKETING_VERSION = 1.0; PRODUCT_BUNDLE_IDENTIFIER = com.test.App; PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; diff --git a/TestAssets/WorkspaceContainerTest/Modules/FrameworkA/FrameworkA.xcodeproj/project.pbxproj b/TestAssets/WorkspaceContainerTest/Modules/FrameworkA/FrameworkA.xcodeproj/project.pbxproj index 3bfedf6..b3429ba 100644 --- a/TestAssets/WorkspaceContainerTest/Modules/FrameworkA/FrameworkA.xcodeproj/project.pbxproj +++ b/TestAssets/WorkspaceContainerTest/Modules/FrameworkA/FrameworkA.xcodeproj/project.pbxproj @@ -172,6 +172,7 @@ CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + CODE_SIGN_IDENTITY = ""; COPY_PHASE_STRIP = NO; CURRENT_PROJECT_VERSION = 1; DEBUG_INFORMATION_FORMAT = dwarf; @@ -235,6 +236,7 @@ CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + CODE_SIGN_IDENTITY = ""; COPY_PHASE_STRIP = NO; CURRENT_PROJECT_VERSION = 1; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; @@ -264,9 +266,11 @@ isa = XCBuildConfiguration; buildSettings = { CLANG_ENABLE_MODULES = YES; - CODE_SIGN_STYLE = Automatic; + CODE_SIGN_IDENTITY = ""; + CODE_SIGN_STYLE = Manual; CURRENT_PROJECT_VERSION = 1; DEFINES_MODULE = YES; + DEVELOPMENT_TEAM = ""; DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; DYLIB_INSTALL_NAME_BASE = "@rpath"; @@ -284,6 +288,7 @@ MODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS = "gnu11 gnu++20"; PRODUCT_BUNDLE_IDENTIFIER = com.test.FrameworkA; PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; + PROVISIONING_PROFILE_SPECIFIER = ""; SKIP_INSTALL = YES; SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; @@ -296,9 +301,11 @@ isa = XCBuildConfiguration; buildSettings = { CLANG_ENABLE_MODULES = YES; - CODE_SIGN_STYLE = Automatic; + CODE_SIGN_IDENTITY = ""; + CODE_SIGN_STYLE = Manual; CURRENT_PROJECT_VERSION = 1; DEFINES_MODULE = YES; + DEVELOPMENT_TEAM = ""; DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; DYLIB_INSTALL_NAME_BASE = "@rpath"; @@ -316,6 +323,7 @@ MODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS = "gnu11 gnu++20"; PRODUCT_BUNDLE_IDENTIFIER = com.test.FrameworkA; PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; + PROVISIONING_PROFILE_SPECIFIER = ""; SKIP_INSTALL = YES; SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_VERSION = 5.0; diff --git a/TestAssets/WorkspaceContainerTest/Modules/FrameworkB/FrameworkB.xcodeproj/project.pbxproj b/TestAssets/WorkspaceContainerTest/Modules/FrameworkB/FrameworkB.xcodeproj/project.pbxproj index 2c9c860..b51d48a 100644 --- a/TestAssets/WorkspaceContainerTest/Modules/FrameworkB/FrameworkB.xcodeproj/project.pbxproj +++ b/TestAssets/WorkspaceContainerTest/Modules/FrameworkB/FrameworkB.xcodeproj/project.pbxproj @@ -208,6 +208,7 @@ CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + CODE_SIGN_IDENTITY = ""; COPY_PHASE_STRIP = NO; CURRENT_PROJECT_VERSION = 1; DEBUG_INFORMATION_FORMAT = dwarf; @@ -271,6 +272,7 @@ CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + CODE_SIGN_IDENTITY = ""; COPY_PHASE_STRIP = NO; CURRENT_PROJECT_VERSION = 1; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; @@ -300,9 +302,11 @@ isa = XCBuildConfiguration; buildSettings = { CLANG_ENABLE_MODULES = YES; - CODE_SIGN_STYLE = Automatic; + CODE_SIGN_IDENTITY = ""; + CODE_SIGN_STYLE = Manual; CURRENT_PROJECT_VERSION = 1; DEFINES_MODULE = YES; + DEVELOPMENT_TEAM = ""; DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; DYLIB_INSTALL_NAME_BASE = "@rpath"; @@ -320,6 +324,7 @@ MODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS = "gnu11 gnu++20"; PRODUCT_BUNDLE_IDENTIFIER = com.test.FrameworkB; PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; + PROVISIONING_PROFILE_SPECIFIER = ""; SKIP_INSTALL = YES; SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; @@ -332,9 +337,11 @@ isa = XCBuildConfiguration; buildSettings = { CLANG_ENABLE_MODULES = YES; - CODE_SIGN_STYLE = Automatic; + CODE_SIGN_IDENTITY = ""; + CODE_SIGN_STYLE = Manual; CURRENT_PROJECT_VERSION = 1; DEFINES_MODULE = YES; + DEVELOPMENT_TEAM = ""; DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; DYLIB_INSTALL_NAME_BASE = "@rpath"; @@ -352,6 +359,7 @@ MODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS = "gnu11 gnu++20"; PRODUCT_BUNDLE_IDENTIFIER = com.test.FrameworkB; PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; + PROVISIONING_PROFILE_SPECIFIER = ""; SKIP_INSTALL = YES; SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_VERSION = 5.0; diff --git a/Tests/GenIRTests/DependencyGraphTests.swift b/Tests/GenIRTests/DependencyGraphTests.swift index 300b933..31edaf8 100644 --- a/Tests/GenIRTests/DependencyGraphTests.swift +++ b/Tests/GenIRTests/DependencyGraphTests.swift @@ -3,45 +3,39 @@ import XCTest import PBXProjParser final class DependencyGraphTests: XCTestCase { - static private var testPath: URL = { + let testPath: URL = { TestContext.testAssetPath .appendingPathComponent("WorkspaceTest") .appendingPathComponent("Workspace.xcworkspace") }() - - static private var scheme = "App" + let scheme = "App" func testChains() throws { // Test Setup - let context = try TestContext() - let process = try context.build(test: Self.testPath, scheme: Self.scheme) + let context = TestContext() + let process = try context.build(test: testPath, scheme: scheme) XCTAssertEqual(process.code, 0, "Build returned non-zero exit code") - let project = try ProjectParser(path: Self.testPath, logLevel: .debug) - var targets = Targets(for: project) - - let buildLog = try String(contentsOf: context.buildLog).components(separatedBy: .newlines) - let logParser = XcodeLogParser(log: buildLog) + let targets = context.targets + let graph = context.graph - try logParser.parse(&targets) - - let graph = DependencyGraphBuilder.build(targets: targets) - let appTarget = try XCTUnwrap(targets.target(for: "App"), "Failed to get App target from targets") + let appTarget = try XCTUnwrap(targets.first(where: { $0.name == "App"}), "Failed to get App target from targets") let app = try XCTUnwrap(graph.findNode(for: appTarget), "Failed to find App node in graph") // App should have two nodes - Framework & Common XCTAssertTrue(app.edges.count == 2, "App's edges is not equal to 2") - _ = try XCTUnwrap(app.edges.first(where: { $0.to.name == "Framework" }), "Failed to get Framework edge from App") - let commonEdge = try XCTUnwrap(app.edges.first(where: { $0.to.name == "Common" }), "Failed to get Common edge from App") + _ = try XCTUnwrap(app.edges.first(where: { $0.to.valueName == "Framework" }), "Failed to get Framework edge from App") + let commonEdge = try XCTUnwrap(app.edges.first(where: { $0.to.valueName == "Common" }), "Failed to get Common edge from App") - let frameworkTarget = try XCTUnwrap(targets.target(for: "Framework"), "Failed to get Framework from targets") + let frameworkTarget = try XCTUnwrap(targets.first(where: { $0.name == "Framework"}), "Failed to get Framework from targets") let framework = try XCTUnwrap(graph.findNode(for: frameworkTarget), "Failed to find Framework node in graph") // Framework should have two dependency edges - Common & SFSafeSymbols and one depender edge - App XCTAssertTrue(framework.edges.count == 3, "Framework's edges is not equal to 3") - let symbolsEdge = try XCTUnwrap(framework.edges.first(where: { $0.to.name == "SFSafeSymbols" }), "Failed to get SFSafeSymbols edge from Framework") - let frameworkCommonEdge = try XCTUnwrap(framework.edges.first(where: { $0.to.name == "Common" }), "Failed to get SFSafeSymbols edge from Framework") - let frameworkAppEdge = try XCTUnwrap(framework.edges.first(where: { $0.to.name == "App" }), "Failed to get App edge from Framework") + let symbolsEdge = try XCTUnwrap(framework.edges.first(where: { $0.to.valueName == "SFSafeSymbols" }), "Failed to get SFSafeSymbols edge from Framework") + let frameworkCommonEdge = try XCTUnwrap(framework.edges.first(where: { $0.to.valueName == "Common" }), "Failed to get SFSafeSymbols edge from Framework") + let frameworkAppEdge = try XCTUnwrap(framework.edges.first(where: { $0.to.valueName == "App" }), "Failed to get App edge from Framework") + XCTAssertNotEqual(commonEdge, frameworkCommonEdge, "App's Common edge is equal to Framework's Common edge - they should have different from values") XCTAssertEqual(symbolsEdge.relationship, .dependency) XCTAssertEqual(frameworkCommonEdge.relationship, .dependency) diff --git a/Tests/GenIRTests/MultipleAppTests.swift b/Tests/GenIRTests/MultipleAppTests.swift index 5d946c3..9791fa0 100644 --- a/Tests/GenIRTests/MultipleAppTests.swift +++ b/Tests/GenIRTests/MultipleAppTests.swift @@ -1,28 +1,22 @@ import XCTest @testable import gen_ir -import PBXProjParser final class MultipleAppTests: XCTestCase { - static private var testPath: URL = { + let testPath: URL = { TestContext.testAssetPath .appendingPathComponent("MultipleApp") .appendingPathComponent("MultipleApp.xcodeproj") }() + let scheme = "MultipleApp" func testExpectedTargetLookup() throws { - let context = try TestContext() - let result = try context.build(test: Self.testPath, scheme: "MultipleApp") - XCTAssertEqual(result.code, 0, "Build returned non-zero exit code") + let context = TestContext() + try context.build(test: testPath, scheme: "MultipleApp") - let project: ProjectParser = try ProjectParser(path: Self.testPath, logLevel: .debug) - var targets = Targets(for: project) + let targets = context.targets - let logContents = try String(contentsOf: context.buildLog).components(separatedBy: .newlines) - let log = XcodeLogParser(log: logContents) - try log.parse(&targets) - - let app = try XCTUnwrap(targets.target(for: "MultipleApp")) - let copy = try XCTUnwrap(targets.target(for: "MultipleApp Copy")) + let app = try XCTUnwrap(targets.first(where: { $0.name == "MultipleApp" })) + let copy = try XCTUnwrap(targets.first(where: { $0.name == "MultipleApp Copy" })) XCTAssertEqual(app.commands.count, 3) XCTAssertEqual(copy.commands.count, 3) diff --git a/Tests/GenIRTests/OutputPostprocessorFileMoverTests.swift b/Tests/GenIRTests/OutputPostprocessorFileMoverTests.swift index b4ca4e8..ba4b784 100644 --- a/Tests/GenIRTests/OutputPostprocessorFileMoverTests.swift +++ b/Tests/GenIRTests/OutputPostprocessorFileMoverTests.swift @@ -3,20 +3,20 @@ import XCTest import PBXProjParser final class OutputPostprocessorFileMoverTests: XCTestCase { - static private var testPath: URL = { + let testPath: URL = { TestContext.testAssetPath .appendingPathComponent("OutputPostprocessorFileMoverTests") .appendingPathComponent("OutputPostprocessorFileMoverTests.xcodeproj") }() + let scheme = "OutputPostprocessorFileMoverTests" func testFileMoving() throws { - let context = try TestContext() - let result = try context.build(test: Self.testPath, scheme: "OutputPostprocessorFileMoverTests") - XCTAssertEqual(result.code, 0, "Build returned non-zero exit code") + let context = TestContext() + try context.build(test: testPath, scheme: scheme) var runner = IREmitterCommand() try runner.run( - project: Self.testPath, + project: testPath, log: context.buildLog.filePath, archive: context.archive, level: .debug, @@ -25,13 +25,11 @@ final class OutputPostprocessorFileMoverTests: XCTestCase { ) // Check the output path for unique Image files - print("archivePAth: \(context.archive)") let appIRPath = context.archive .appendingPathComponent("IR") .appendingPathComponent("OutputPostprocessorFileMoverTests.app") let files = try FileManager.default.contentsOfDirectory(at: appIRPath, includingPropertiesForKeys: nil) let imageFilesCount = files.filter { $0.lastPathComponent.starts(with: "Image") }.count - print("imageFilesCount: \(imageFilesCount)") // Only expecting two - one of the dependencies is dynamic and won't be moved. XCTAssertEqual(imageFilesCount, 2, "2 Image*.bc files expected, \(imageFilesCount) were found.") diff --git a/Tests/GenIRTests/TestContext.swift b/Tests/GenIRTests/TestContext.swift index 661ebfb..8132ac5 100644 --- a/Tests/GenIRTests/TestContext.swift +++ b/Tests/GenIRTests/TestContext.swift @@ -1,10 +1,12 @@ import Foundation @testable import gen_ir +import XCTest class TestContext { enum Error: Swift.Error { case commandFailed(Process.ReturnValue) case invalidArgument(String) + case notBuilt } static let baseTestingPath: URL = { @@ -36,6 +38,7 @@ class TestContext { ) } + @discardableResult func build( test path: URL, scheme: String, @@ -74,18 +77,56 @@ class TestContext { throw Error.commandFailed(process) } else if let stdout = process.stdout { try stdout.write(to: buildLog, atomically: true, encoding: .utf8) + buildLogContents = stdout.components(separatedBy: .newlines) } + built = true + return process } let archive: URL let buildLog: URL let temporaryDirectory: URL + private(set) var built = false + private(set) var buildLogContents = [String]() - init() throws { - temporaryDirectory = try FileManager.default.temporaryDirectory(named: "gen-ir-tests-\(UUID().uuidString)") + init() { + // swiftlint:disable force_try + temporaryDirectory = try! FileManager.default.temporaryDirectory(named: "gen-ir-tests-\(UUID().uuidString)") archive = temporaryDirectory.appendingPathComponent("x.xcarchive") buildLog = temporaryDirectory.appendingPathComponent("build.log") } + + deinit { + try! FileManager.default.removeItem(at: temporaryDirectory) + // swiftlint:enable force_try + } + + lazy var logParser: XcodeLogParser = { + XCTAssertTrue(built, "Requests a log parser without building the project") + let parser = XcodeLogParser(log: buildLogContents) + do { + try parser.parse() + } catch { + fatalError("XcodeLogParser error: \(error)") + } + return parser + }() + + lazy var pifCache: PIFCache = { + do { + return try PIFCache(buildCache: logParser.buildCachePath) + } catch { + fatalError("PIFCache init failed with error: \(error)") + } + }() + + lazy var targets: [Target] = { + Target.targets(from: pifCache.targets, with: logParser.targetCommands) + }() + + lazy var graph: DependencyGraph = { + DependencyGraphBuilder(provider: PIFDependencyProvider(targets: targets, cache: pifCache), values: targets).graph + }() } diff --git a/Tests/GenIRTests/UmbrellaTests.swift b/Tests/GenIRTests/UmbrellaTests.swift index 167b05f..eac8475 100644 --- a/Tests/GenIRTests/UmbrellaTests.swift +++ b/Tests/GenIRTests/UmbrellaTests.swift @@ -3,15 +3,15 @@ import XCTest import PBXProjParser final class UmbrellaTests: XCTestCase { - static private var testPath: URL = { + let testPath: URL = { TestContext.testAssetPath .appendingPathComponent("Umbrella") .appendingPathComponent("Umbrella.xcworkspace") }() - static private var scheme = "Umbrella" + let scheme = "Umbrella" - static private let targetsToFiles = [ + let targetsToFiles = [ "Common.framework": ["Common_vers.bc", "Common-dummy.bc", "OrgModel.bc"].sorted(), "Networking.framework": ["Networking_vers.bc", "Networking-dummy.bc", "Networking.bc"].sorted(), "Pods_Umbrella.framework": ["Pods_Umbrella_vers.bc", "Pods-Umbrella-dummy.bc"].sorted(), @@ -19,18 +19,14 @@ final class UmbrellaTests: XCTestCase { ] func testUmbrellaTargets() throws { - let context = try TestContext() - let process = try context.build(test: Self.testPath, scheme: Self.scheme) - XCTAssertEqual(process.code, 0, "Build returned non-zero exit code") - - let projectParser = try ProjectParser(path: Self.testPath, logLevel: .info) - let targets = Targets(for: projectParser) + let context = TestContext() + try context.build(test: testPath, scheme: scheme) + let targets = context.targets XCTAssertEqual(targets.count, 4, "Expected 4 targets, got \(targets.count)") let expectedTargetNames = ["Umbrella", "Common", "Networking", "Pods-Umbrella"].sorted() let actualTargetNames = targets.map { $0.name }.sorted() - XCTAssertEqual( actualTargetNames, expectedTargetNames, "Expected target names: \(expectedTargetNames), got: \(actualTargetNames)" @@ -38,16 +34,14 @@ final class UmbrellaTests: XCTestCase { } func testSkipInstallNo() throws { - let context = try TestContext() - defer { try? FileManager.default.removeItem(at: context.temporaryDirectory) } - let result = try context.build(test: Self.testPath, scheme: Self.scheme, additionalArguments: ["SKIP_INSTALL=NO"]) - XCTAssertEqual(result.code, 0, "Build returned non-zero exit code") + let context = TestContext() + try context.build(test: testPath, scheme: scheme, additionalArguments: ["SKIP_INSTALL=NO"]) let output = context.archive.appendingPathComponent("IR") var genIR = gen_ir.IREmitterCommand() try genIR.run( - project: Self.testPath, + project: testPath, log: context.buildLog.filePath, archive: context.archive, level: .debug, @@ -58,28 +52,26 @@ final class UmbrellaTests: XCTestCase { let directories = try FileManager.default.directories(at: output, recursive: false) let targets = directories.map { $0.lastPathComponent } - XCTAssertEqual(targets.sorted(), Self.targetsToFiles.keys.sorted(), "Targets list doesn't match the known targets") + XCTAssertEqual(targets.sorted(), targetsToFiles.keys.sorted(), "Targets list doesn't match the known targets") for directory in directories { let contents = try FileManager.default.files(at: directory, withSuffix: "bc") .map { $0.lastPathComponent } .sorted() - XCTAssertEqual(contents, Self.targetsToFiles[directory.lastPathComponent]) + XCTAssertEqual(contents, targetsToFiles[directory.lastPathComponent]) } } func testCustomDerivedDataAndSkipInstallNo() throws { - let context = try TestContext() - defer { try? FileManager.default.removeItem(at: context.temporaryDirectory) } - let result = try context.build(test: Self.testPath, scheme: Self.scheme, additionalArguments: ["SKIP_INSTALL=NO", "-derivedDataPath", "_build"]) - XCTAssertEqual(result.code, 0, "Build returned non-zero exit code") + let context = TestContext() + try context.build(test: testPath, scheme: scheme, additionalArguments: ["SKIP_INSTALL=NO", "-derivedDataPath", "_build"]) let output = context.archive.appendingPathComponent("IR") var genIR = gen_ir.IREmitterCommand() try genIR.run( - project: Self.testPath, + project: testPath, log: context.buildLog.filePath, archive: context.archive, level: .debug, @@ -90,14 +82,14 @@ final class UmbrellaTests: XCTestCase { let directories = try FileManager.default.directories(at: output, recursive: false) let targets = directories.map { $0.lastPathComponent } - XCTAssertEqual(targets.sorted(), Self.targetsToFiles.keys.sorted(), "Targets list doesn't match the known targets") + XCTAssertEqual(targets.sorted(), targetsToFiles.keys.sorted(), "Targets list doesn't match the known targets") for directory in directories { let contents = try FileManager.default.files(at: directory, withSuffix: "bc") .map { $0.lastPathComponent } .sorted() - XCTAssertEqual(contents, Self.targetsToFiles[directory.lastPathComponent]) + XCTAssertEqual(contents, targetsToFiles[directory.lastPathComponent]) } } } diff --git a/Tests/GenIRTests/WorkspaceContainerTests.swift b/Tests/GenIRTests/WorkspaceContainerTests.swift index e910bf5..8e1275e 100644 --- a/Tests/GenIRTests/WorkspaceContainerTests.swift +++ b/Tests/GenIRTests/WorkspaceContainerTests.swift @@ -1,17 +1,18 @@ import XCTest @testable import gen_ir -import PBXProjParser final class WorkspaceContainerTests: XCTestCase { - static private var testPath: URL = { + let testPath: URL = { TestContext.testAssetPath .appendingPathComponent("WorkspaceContainerTest") .appendingPathComponent("WorkspaceContainerTest.xcworkspace") }() + let scheme = "App" func testWeirdGroupTagLocationParsing() throws { - let parser = try ProjectParser(path: Self.testPath, logLevel: .debug) - let targets = parser.targets + let context = TestContext() + try context.build(test: testPath, scheme: scheme) + let targets = context.targets XCTAssert(targets.count == 3) XCTAssertNotNil(targets.first(where: { $0.name == "App" })) diff --git a/Tests/GenIRTests/WorkspaceTests.swift b/Tests/GenIRTests/WorkspaceTests.swift index 7c2851a..0b2530f 100644 --- a/Tests/GenIRTests/WorkspaceTests.swift +++ b/Tests/GenIRTests/WorkspaceTests.swift @@ -3,13 +3,13 @@ import XCTest import PBXProjParser final class WorkspaceTests: XCTestCase { - static private var testPath: URL = { + let testPath: URL = { TestContext.testAssetPath .appendingPathComponent("WorkspaceTest") .appendingPathComponent("Workspace.xcworkspace") }() - static private var scheme = "App" + let scheme = "App" static let appIRFiles: Set = ["AppApp.bc", "ContentView.bc", "GeneratedAssetSymbols.bc"] static let commonIRFiles: Set = ["Common_vers.bc", "Model.bc"] @@ -50,14 +50,13 @@ final class WorkspaceTests: XCTestCase { ] func testWorkspace() throws { - let context = try TestContext() - let process = try context.build(test: Self.testPath, scheme: Self.scheme) - XCTAssertEqual(process.code, 0, "Build returned non-zero exit code") + let context = TestContext() + try context.build(test: testPath, scheme: scheme) var genIR = gen_ir.IREmitterCommand() try genIR.run( - project: Self.testPath, + project: testPath, log: context.buildLog.filePath, archive: context.archive, level: .debug, @@ -69,7 +68,7 @@ final class WorkspaceTests: XCTestCase { let appIRPath = context.archive.appending(path: "IR/App.app/") let commonIRPath = context.archive.appending(path: "IR/Common.framework/") let frameworkIRPath = context.archive.appending(path: "IR/Framework.framework/") - let sfSafeSymbolsIRPath = context.archive.appending(path: "IR/SFSafeSymbols/") + let sfSafeSymbolsIRPath = context.archive.appending(path: "IR/SFSafeSymbols.framework/") let appIRPathContents = try FileManager.default.contentsOfDirectory(at: appIRPath, includingPropertiesForKeys: nil) .reduce(into: Set(), { $0.insert($1.lastPathComponent) }) diff --git a/Tests/GenIRTests/gen_irTests.swift b/Tests/GenIRTests/gen_irTests.swift index ffdfd9f..7f55708 100644 --- a/Tests/GenIRTests/gen_irTests.swift +++ b/Tests/GenIRTests/gen_irTests.swift @@ -1,15 +1,16 @@ import XCTest @testable import gen_ir -import PBXProjParser final class GenIRTests: XCTestCase { func testManyTargetTestTargets() throws { + let context = TestContext() let projectPath = TestContext.baseTestingPath .appendingPathComponent("TestAssets") .appendingPathComponent("ManyTargetTest") .appendingPathComponent("ManyTargetTest.xcodeproj") - let project = try ProjectParser(path: projectPath, logLevel: logger.logLevel) - let targets = Targets(for: project) + + try context.build(test: projectPath, scheme: "ManyTargetTest") + let targets = context.targets XCTAssert(targets.count == 3, "Targets count expected to be 3, was \(targets.count)") From 7f1a9b95b3e19c238a879636fb8b53391f866e89 Mon Sep 17 00:00:00 2001 From: NinjaLikesCheez Date: Mon, 13 May 2024 15:15:38 +0200 Subject: [PATCH 07/21] Remove references to PBXProjectParser --- .swiftlint.yml | 2 +- PBXProjParser/.gitignore | 9 - PBXProjParser/Package.resolved | 14 - PBXProjParser/Package.swift | 38 - PBXProjParser/README.md | 12 - .../Extensions/DecodingExtensions.swift | 102 --- .../PBXProjParser/Models/PBXBuildFile.swift | 46 -- .../PBXProjParser/Models/PBXBuildPhase.swift | 99 --- .../Models/PBXContainerItemProxy.swift | 41 - .../Models/PBXFileReference.swift | 51 -- .../PBXProjParser/Models/PBXGroup.swift | 32 - .../PBXProjParser/Models/PBXObject.swift | 110 --- .../PBXProjParser/Models/PBXProj.swift | 111 --- .../PBXProjParser/Models/PBXProject.swift | 64 -- .../PBXProjParser/Models/PBXTarget.swift | 170 ---- .../Models/PBXVariantGroup.swift | 34 - .../Models/XCBuildConfiguration.swift | 34 - .../Models/XCConfigurationList.swift | 34 - .../XCSwiftPackageProductDependency.swift | 48 -- .../Sources/PBXProjParser/ProjectParser.swift | 121 --- .../PBXProjParser/Workspace/Reference.swift | 86 -- .../Workspace/WorkspaceParser.swift | 88 --- .../Sources/PBXProjParser/XcodeProject.swift | 231 ------ .../PBXProjParser/XcodeWorkspace.swift | 61 -- .../Sources/projparser/projparser.swift | 22 - .../PBXProjParserTests.swift | 47 -- .../project.pbxproj | 418 ---------- .../contents.xcworkspacedata | 7 - .../xcshareddata/IDEWorkspaceChecks.plist | 8 - .../xcschemes/DoubleTargetTest.xcscheme | 78 -- .../contents.xcworkspacedata | 10 - .../xcshareddata/IDEWorkspaceChecks.plist | 8 - .../Pods/Local Podspecs/MyBundle.podspec.json | 27 - .../DoubleTargetTest/Pods/Manifest.lock | 16 - .../Pods/Pods.xcodeproj/project.pbxproj | 734 ------------------ .../MyBundle/MyBundle-Info.plist | 26 - .../MyBundle/MyBundle-dummy.m | 5 - .../MyBundle/MyBundle-prefix.pch | 12 - .../MyBundle/MyBundle-umbrella.h | 16 - .../MyBundle/MyBundle.debug.xcconfig | 13 - .../MyBundle/MyBundle.modulemap | 6 - .../MyBundle/MyBundle.release.xcconfig | 13 - ...esourceBundle-MyBundle-MyBundle-Info.plist | 24 - .../Pods-DoubleTargetTest-Info.plist | 26 - ...DoubleTargetTest-acknowledgements.markdown | 26 - ...ds-DoubleTargetTest-acknowledgements.plist | 58 -- .../Pods-DoubleTargetTest-dummy.m | 5 - ...st-frameworks-Debug-input-files.xcfilelist | 2 - ...t-frameworks-Debug-output-files.xcfilelist | 1 - ...-frameworks-Release-input-files.xcfilelist | 2 - ...frameworks-Release-output-files.xcfilelist | 1 - .../Pods-DoubleTargetTest-frameworks.sh | 186 ----- .../Pods-DoubleTargetTest-umbrella.h | 16 - .../Pods-DoubleTargetTest.debug.xcconfig | 15 - .../Pods-DoubleTargetTest.modulemap | 6 - .../Pods-DoubleTargetTest.release.xcconfig | 15 - Package.swift | 2 - Sources/GenIR/CompilerCommandRunner.swift | 1 - Sources/GenIR/GenIR.swift | 1 - Sources/GenIR/OutputPostprocessor.swift | 1 - Sources/GenIR/Target.swift | 1 - Tests/GenIRTests/DependencyGraphTests.swift | 1 - .../OutputPostprocessorFileMoverTests.swift | 1 - Tests/GenIRTests/UmbrellaTests.swift | 1 - Tests/GenIRTests/WorkspaceTests.swift | 1 - 65 files changed, 1 insertion(+), 3496 deletions(-) delete mode 100644 PBXProjParser/.gitignore delete mode 100644 PBXProjParser/Package.resolved delete mode 100644 PBXProjParser/Package.swift delete mode 100644 PBXProjParser/README.md delete mode 100644 PBXProjParser/Sources/PBXProjParser/Extensions/DecodingExtensions.swift delete mode 100644 PBXProjParser/Sources/PBXProjParser/Models/PBXBuildFile.swift delete mode 100644 PBXProjParser/Sources/PBXProjParser/Models/PBXBuildPhase.swift delete mode 100644 PBXProjParser/Sources/PBXProjParser/Models/PBXContainerItemProxy.swift delete mode 100644 PBXProjParser/Sources/PBXProjParser/Models/PBXFileReference.swift delete mode 100644 PBXProjParser/Sources/PBXProjParser/Models/PBXGroup.swift delete mode 100644 PBXProjParser/Sources/PBXProjParser/Models/PBXObject.swift delete mode 100644 PBXProjParser/Sources/PBXProjParser/Models/PBXProj.swift delete mode 100644 PBXProjParser/Sources/PBXProjParser/Models/PBXProject.swift delete mode 100644 PBXProjParser/Sources/PBXProjParser/Models/PBXTarget.swift delete mode 100644 PBXProjParser/Sources/PBXProjParser/Models/PBXVariantGroup.swift delete mode 100644 PBXProjParser/Sources/PBXProjParser/Models/XCBuildConfiguration.swift delete mode 100644 PBXProjParser/Sources/PBXProjParser/Models/XCConfigurationList.swift delete mode 100644 PBXProjParser/Sources/PBXProjParser/Models/XCSwiftPackageProductDependency.swift delete mode 100644 PBXProjParser/Sources/PBXProjParser/ProjectParser.swift delete mode 100644 PBXProjParser/Sources/PBXProjParser/Workspace/Reference.swift delete mode 100644 PBXProjParser/Sources/PBXProjParser/Workspace/WorkspaceParser.swift delete mode 100644 PBXProjParser/Sources/PBXProjParser/XcodeProject.swift delete mode 100644 PBXProjParser/Sources/PBXProjParser/XcodeWorkspace.swift delete mode 100644 PBXProjParser/Sources/projparser/projparser.swift delete mode 100644 PBXProjParser/Tests/PBXProjParserTests/PBXProjParserTests.swift delete mode 100644 PBXProjParser/Tests/PBXProjParserTests/TestAssets/Projects/DoubleTargetTest/DoubleTargetTest.xcodeproj/project.pbxproj delete mode 100644 PBXProjParser/Tests/PBXProjParserTests/TestAssets/Projects/DoubleTargetTest/DoubleTargetTest.xcodeproj/project.xcworkspace/contents.xcworkspacedata delete mode 100644 PBXProjParser/Tests/PBXProjParserTests/TestAssets/Projects/DoubleTargetTest/DoubleTargetTest.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist delete mode 100644 PBXProjParser/Tests/PBXProjParserTests/TestAssets/Projects/DoubleTargetTest/DoubleTargetTest.xcodeproj/xcshareddata/xcschemes/DoubleTargetTest.xcscheme delete mode 100644 PBXProjParser/Tests/PBXProjParserTests/TestAssets/Projects/DoubleTargetTest/DoubleTargetTest.xcworkspace/contents.xcworkspacedata delete mode 100644 PBXProjParser/Tests/PBXProjParserTests/TestAssets/Projects/DoubleTargetTest/DoubleTargetTest.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist delete mode 100644 PBXProjParser/Tests/PBXProjParserTests/TestAssets/Projects/DoubleTargetTest/Pods/Local Podspecs/MyBundle.podspec.json delete mode 100644 PBXProjParser/Tests/PBXProjParserTests/TestAssets/Projects/DoubleTargetTest/Pods/Manifest.lock delete mode 100644 PBXProjParser/Tests/PBXProjParserTests/TestAssets/Projects/DoubleTargetTest/Pods/Pods.xcodeproj/project.pbxproj delete mode 100644 PBXProjParser/Tests/PBXProjParserTests/TestAssets/Projects/DoubleTargetTest/Pods/Target Support Files/MyBundle/MyBundle-Info.plist delete mode 100644 PBXProjParser/Tests/PBXProjParserTests/TestAssets/Projects/DoubleTargetTest/Pods/Target Support Files/MyBundle/MyBundle-dummy.m delete mode 100644 PBXProjParser/Tests/PBXProjParserTests/TestAssets/Projects/DoubleTargetTest/Pods/Target Support Files/MyBundle/MyBundle-prefix.pch delete mode 100644 PBXProjParser/Tests/PBXProjParserTests/TestAssets/Projects/DoubleTargetTest/Pods/Target Support Files/MyBundle/MyBundle-umbrella.h delete mode 100644 PBXProjParser/Tests/PBXProjParserTests/TestAssets/Projects/DoubleTargetTest/Pods/Target Support Files/MyBundle/MyBundle.debug.xcconfig delete mode 100644 PBXProjParser/Tests/PBXProjParserTests/TestAssets/Projects/DoubleTargetTest/Pods/Target Support Files/MyBundle/MyBundle.modulemap delete mode 100644 PBXProjParser/Tests/PBXProjParserTests/TestAssets/Projects/DoubleTargetTest/Pods/Target Support Files/MyBundle/MyBundle.release.xcconfig delete mode 100644 PBXProjParser/Tests/PBXProjParserTests/TestAssets/Projects/DoubleTargetTest/Pods/Target Support Files/MyBundle/ResourceBundle-MyBundle-MyBundle-Info.plist delete mode 100644 PBXProjParser/Tests/PBXProjParserTests/TestAssets/Projects/DoubleTargetTest/Pods/Target Support Files/Pods-DoubleTargetTest/Pods-DoubleTargetTest-Info.plist delete mode 100644 PBXProjParser/Tests/PBXProjParserTests/TestAssets/Projects/DoubleTargetTest/Pods/Target Support Files/Pods-DoubleTargetTest/Pods-DoubleTargetTest-acknowledgements.markdown delete mode 100644 PBXProjParser/Tests/PBXProjParserTests/TestAssets/Projects/DoubleTargetTest/Pods/Target Support Files/Pods-DoubleTargetTest/Pods-DoubleTargetTest-acknowledgements.plist delete mode 100644 PBXProjParser/Tests/PBXProjParserTests/TestAssets/Projects/DoubleTargetTest/Pods/Target Support Files/Pods-DoubleTargetTest/Pods-DoubleTargetTest-dummy.m delete mode 100644 PBXProjParser/Tests/PBXProjParserTests/TestAssets/Projects/DoubleTargetTest/Pods/Target Support Files/Pods-DoubleTargetTest/Pods-DoubleTargetTest-frameworks-Debug-input-files.xcfilelist delete mode 100644 PBXProjParser/Tests/PBXProjParserTests/TestAssets/Projects/DoubleTargetTest/Pods/Target Support Files/Pods-DoubleTargetTest/Pods-DoubleTargetTest-frameworks-Debug-output-files.xcfilelist delete mode 100644 PBXProjParser/Tests/PBXProjParserTests/TestAssets/Projects/DoubleTargetTest/Pods/Target Support Files/Pods-DoubleTargetTest/Pods-DoubleTargetTest-frameworks-Release-input-files.xcfilelist delete mode 100644 PBXProjParser/Tests/PBXProjParserTests/TestAssets/Projects/DoubleTargetTest/Pods/Target Support Files/Pods-DoubleTargetTest/Pods-DoubleTargetTest-frameworks-Release-output-files.xcfilelist delete mode 100755 PBXProjParser/Tests/PBXProjParserTests/TestAssets/Projects/DoubleTargetTest/Pods/Target Support Files/Pods-DoubleTargetTest/Pods-DoubleTargetTest-frameworks.sh delete mode 100644 PBXProjParser/Tests/PBXProjParserTests/TestAssets/Projects/DoubleTargetTest/Pods/Target Support Files/Pods-DoubleTargetTest/Pods-DoubleTargetTest-umbrella.h delete mode 100644 PBXProjParser/Tests/PBXProjParserTests/TestAssets/Projects/DoubleTargetTest/Pods/Target Support Files/Pods-DoubleTargetTest/Pods-DoubleTargetTest.debug.xcconfig delete mode 100644 PBXProjParser/Tests/PBXProjParserTests/TestAssets/Projects/DoubleTargetTest/Pods/Target Support Files/Pods-DoubleTargetTest/Pods-DoubleTargetTest.modulemap delete mode 100644 PBXProjParser/Tests/PBXProjParserTests/TestAssets/Projects/DoubleTargetTest/Pods/Target Support Files/Pods-DoubleTargetTest/Pods-DoubleTargetTest.release.xcconfig diff --git a/.swiftlint.yml b/.swiftlint.yml index 8b2d54e..993bb96 100644 --- a/.swiftlint.yml +++ b/.swiftlint.yml @@ -2,7 +2,7 @@ excluded: - .build/ - .swiftpm/ - .vscode/ - - PBXProjParser/.build/ # https://github.com/realm/SwiftLint/issues/2329 doesn't support recursive globs yet + # https://github.com/realm/SwiftLint/issues/2329 doesn't support recursive globs yet - GenIRLogging/.build/ - TestAssets/ diff --git a/PBXProjParser/.gitignore b/PBXProjParser/.gitignore deleted file mode 100644 index 3b29812..0000000 --- a/PBXProjParser/.gitignore +++ /dev/null @@ -1,9 +0,0 @@ -.DS_Store -/.build -/Packages -/*.xcodeproj -xcuserdata/ -DerivedData/ -.swiftpm/config/registries.json -.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata -.netrc diff --git a/PBXProjParser/Package.resolved b/PBXProjParser/Package.resolved deleted file mode 100644 index 40da984..0000000 --- a/PBXProjParser/Package.resolved +++ /dev/null @@ -1,14 +0,0 @@ -{ - "pins" : [ - { - "identity" : "swift-log", - "kind" : "remoteSourceControl", - "location" : "https://github.com/apple/swift-log.git", - "state" : { - "revision" : "32e8d724467f8fe623624570367e3d50c5638e46", - "version" : "1.5.2" - } - } - ], - "version" : 2 -} diff --git a/PBXProjParser/Package.swift b/PBXProjParser/Package.swift deleted file mode 100644 index 4e57d05..0000000 --- a/PBXProjParser/Package.swift +++ /dev/null @@ -1,38 +0,0 @@ -// swift-tools-version: 5.7 -// The swift-tools-version declares the minimum version of Swift required to build this package. - -import PackageDescription - -let package = Package( - name: "PBXProjParser", - products: [ - // Products define the executables and libraries a package produces, and make them visible to other packages. - .library( - name: "PBXProjParser", - targets: ["PBXProjParser"]) - ], - dependencies: [ - // Dependencies declare other packages that this package depends on. - // .package(url: /* package url */, from: "1.0.0"), - .package(url: "https://github.com/apple/swift-log.git", from: "1.0.0") - ], - targets: [ - // Targets are the basic building blocks of a package. A target can define a module or a test suite. - // Targets can depend on other targets in this package, and on products in packages this package depends on. - .target( - name: "PBXProjParser", - dependencies: [ - .product(name: "Logging", package: "swift-log") - ]), - .testTarget( - name: "PBXProjParserTests", - dependencies: ["PBXProjParser"]), - .executableTarget( - name: "projparser", - dependencies: [ - .product(name: "Logging", package: "swift-log"), - "PBXProjParser" - ] - ) - ] -) diff --git a/PBXProjParser/README.md b/PBXProjParser/README.md deleted file mode 100644 index aea5328..0000000 --- a/PBXProjParser/README.md +++ /dev/null @@ -1,12 +0,0 @@ -# PBXProjParser - -As the name alludes to, this package handles parsing a pbxproj via xcodeproj or xcworkspace folders. - -It also contains some helper functions and structures to wrap an API around a pretty terrible file format, as well as an executable target for testing locally. - -## Note - -A _lot_ of the parsing is hidden behind a compiler flag for a couple reasons: - -- This is a largely undocumented format, and what little documentation is out there is often either slightly wrong or outdated. -- Only a handful of information is required for `Gen IR` so to speed things up/have less decoding issues we don't parse most of the files although the structures exist to do so diff --git a/PBXProjParser/Sources/PBXProjParser/Extensions/DecodingExtensions.swift b/PBXProjParser/Sources/PBXProjParser/Extensions/DecodingExtensions.swift deleted file mode 100644 index a9ed543..0000000 --- a/PBXProjParser/Sources/PBXProjParser/Extensions/DecodingExtensions.swift +++ /dev/null @@ -1,102 +0,0 @@ -// -// DecodingExtensions.swift -// -// -// Created by Thomas Hedderwick on 03/02/2023. -// - -import Foundation - -// https://gist.github.com/mikebuss/17142624da4baf9cdcc337861e256533 - -struct PlistCodingKeys: CodingKey { - var stringValue: String - - init(stringValue: String) { - self.stringValue = stringValue - } - - var intValue: Int? - - init?(intValue: Int) { - self.init(stringValue: "\(intValue)") - self.intValue = intValue - } -} - -extension KeyedDecodingContainer { - func decode(_ type: [String: Any].Type, forKey key: K) throws -> [String: Any] { - let container = try self.nestedContainer(keyedBy: PlistCodingKeys.self, forKey: key) - return try container.decode(type) - } - - func decodeIfPresent(_ type: [String: Any].Type, forKey key: K) throws -> [String: Any]? { - return try? self.nestedContainer(keyedBy: PlistCodingKeys.self, forKey: key).decode(type) - } - - func decode(_ type: [Any].Type, forKey key: K) throws -> [Any] { - var container = try self.nestedUnkeyedContainer(forKey: key) - return try container.decode(type) - } - - func decodeIfPresent(_ type: [Any].Type, forKey key: K) throws -> [Any]? { - guard var container = try? self.nestedUnkeyedContainer(forKey: key) else { - return nil - } - - return try? container.decode(type) - } - - func decode(_ type: [String: Any].Type) throws -> [String: Any] { - var dictionary = [String: Any]() - - for key in allKeys { - if let boolValue = try? decode(Bool.self, forKey: key) { - dictionary[key.stringValue] = boolValue - } else if let stringValue = try? decode(String.self, forKey: key) { - dictionary[key.stringValue] = stringValue - } else if let intValue = try? decode(Int.self, forKey: key) { - dictionary[key.stringValue] = intValue - } else if let doubleValue = try? decode(Double.self, forKey: key) { - dictionary[key.stringValue] = doubleValue - } else if let nestedDictionary = try? decode([String: Any].self, forKey: key) { - dictionary[key.stringValue] = nestedDictionary - } else if let nestedArray = try? decode([Any].self, forKey: key) { - dictionary[key.stringValue] = nestedArray - } - } - return dictionary - } -} - -extension UnkeyedDecodingContainer { - mutating func decode(_ type: [Any].Type) throws -> [Any] { - var array: [Any] = [] - - while isAtEnd == false { - let value: String? = try decode(String?.self) - if value == nil { - continue - } - if let value = try? decode(Bool.self) { - array.append(value) - } else if let value = try? decode(Int.self) { - array.append(value) - } else if let value = try? decode(Double.self) { - array.append(value) - } else if let value = try? decode(String.self) { - array.append(value) - } else if let nestedDictionary = try? decode([String: Any].self) { - array.append(nestedDictionary) - } else if let nestedArray = try? decode([Any].self) { - array.append(nestedArray) - } - } - return array - } - - mutating func decode(_ type: [String: Any].Type) throws -> [String: Any] { - let nestedContainer = try self.nestedContainer(keyedBy: PlistCodingKeys.self) - return try nestedContainer.decode(type) - } -} diff --git a/PBXProjParser/Sources/PBXProjParser/Models/PBXBuildFile.swift b/PBXProjParser/Sources/PBXProjParser/Models/PBXBuildFile.swift deleted file mode 100644 index 25bc290..0000000 --- a/PBXProjParser/Sources/PBXProjParser/Models/PBXBuildFile.swift +++ /dev/null @@ -1,46 +0,0 @@ -// -// PBXBuildFile.swift -// -// -// Created by Thomas Hedderwick on 31/01/2023. -// - -import Foundation - -public class PBXBuildFile: PBXObject { - public let productRef: String? - public let fileRef: String? - - #if FULL_PBX_PARSING - public let platformFilter: String? - public let platformFilters: [String]? - public let settings: [String: Any]? - #endif - - private enum CodingKeys: String, CodingKey { - case productRef - case fileRef - - #if FULL_PBX_PARSING - case platformFilter - case platformFilters - case settings - #endif - } - - required init(from decoder: Decoder) throws { - let container = try decoder.container(keyedBy: CodingKeys.self) - productRef = try container.decodeIfPresent(String.self, forKey: .productRef) - fileRef = try container.decodeIfPresent(String.self, forKey: .fileRef) - - #if FULL_PBX_PARSING - platformFilter = try container.decodeIfPresent(String.self, forKey: .platformFilter) - platformFilters = try container.decodeIfPresent([String].self, forKey: .platformFilters) - settings = try container.decodeIfPresent([String: Any].self, forKey: .settings) - #endif - - try super.init(from: decoder) - } -} - -public class PBXBuildRule: PBXObject {} diff --git a/PBXProjParser/Sources/PBXProjParser/Models/PBXBuildPhase.swift b/PBXProjParser/Sources/PBXProjParser/Models/PBXBuildPhase.swift deleted file mode 100644 index cbc5d70..0000000 --- a/PBXProjParser/Sources/PBXProjParser/Models/PBXBuildPhase.swift +++ /dev/null @@ -1,99 +0,0 @@ -// -// PBXBuildPhase.swift -// -// -// Created by Thomas Hedderwick on 31/01/2023. -// - -import Foundation - -public class PBXBuildPhase: PBXObject { - public let files: [String] -#if FULL_PBX_PARSING - public let alwaysOutOfDate: String? - public let buildActionMask: UInt32 - public let runOnlyForDeploymentPostprocessing: Int -#endif - - private enum CodingKeys: String, CodingKey { - case files - #if FULL_PBX_PARSING - case alwaysOutOfDate - case buildActionMask - case runOnlyForDeploymentPostprocessing - #endif - } - - required init(from decoder: Decoder) throws { - let container = try decoder.container(keyedBy: CodingKeys.self) - - #if FULL_PBX_PARSING - alwaysOutOfDate = try container.decodeIfPresent(String.self, forKey: .alwaysOutOfDate) - - let mask = try container.decode(String.self, forKey: .buildActionMask) - buildActionMask = UInt32(mask) ?? 0 - - let flag = try container.decode(String.self, forKey: .runOnlyForDeploymentPostprocessing) - runOnlyForDeploymentPostprocessing = Int(flag) ?? 0 - #endif - - files = try container.decodeIfPresent([String].self, forKey: .files) ?? [] - - try super.init(from: decoder) - } -} - -public class PBXCopyFilesBuildPhase: PBXBuildPhase { -#if FULL_PBX_PARSING - let dstPath: String - let dstSubfolderSpec: String - - private enum CodingKeys: String, CodingKey { - case dstPath - case dstSubfolderSpec - } - - required init(from decoder: Decoder) throws { - let container = try decoder.container(keyedBy: CodingKeys.self) - - dstPath = try container.decode(String.self, forKey: .dstPath) - dstSubfolderSpec = try container.decode(String.self, forKey: .dstSubfolderSpec) - - try super.init(from: decoder) - } -#endif -} - -public class PBXShellScriptBuildPhase: PBXBuildPhase { -#if FULL_PBX_PARSING - let inputPaths: [String]? - let outputPaths: [String]? - let shellPath: String - let shellScript: String - - private enum CodingKeys: String, CodingKey { - case inputPaths - case outputPaths - case shellPath - case shellScript - } - - required init(from decoder: Decoder) throws { - let container = try decoder.container(keyedBy: CodingKeys.self) - - inputPaths = try container.decodeIfPresent([String].self, forKey: .inputPaths) - outputPaths = try container.decodeIfPresent([String].self, forKey: .outputPaths) - shellPath = try container.decode(String.self, forKey: .shellPath) - shellScript = try container.decode(String.self, forKey: .shellScript) - - try super.init(from: decoder) - } -#endif -} - -public class PBXFrameworksBuildPhase: PBXBuildPhase {} -public class PBXHeadersBuildPhase: PBXBuildPhase {} -public class PBXResourcesBuildPhase: PBXBuildPhase {} -public class PBXSourcesBuildPhase: PBXBuildPhase {} -public class PBXAppleScriptBuildPhase: PBXBuildPhase {} -public class PBXRezBuildPhase: PBXBuildPhase {} diff --git a/PBXProjParser/Sources/PBXProjParser/Models/PBXContainerItemProxy.swift b/PBXProjParser/Sources/PBXProjParser/Models/PBXContainerItemProxy.swift deleted file mode 100644 index 079eb04..0000000 --- a/PBXProjParser/Sources/PBXProjParser/Models/PBXContainerItemProxy.swift +++ /dev/null @@ -1,41 +0,0 @@ -// -// PBXContainerItemProxy.swift -// -// -// Created by Thomas Hedderwick on 31/01/2023. -// - -import Foundation - -public class PBXContainerItemProxy: PBXObject { - #if FULL_PBX_PARSING - public let containerPortal: String - public let proxyType: String - public let remoteInfo: String - #endif - public let remoteGlobalIDString: String - - private enum CodingKeys: String, CodingKey { - #if FULL_PBX_PARSING - case containerPortal - case proxyType - case remoteInfo - #endif - case remoteGlobalIDString - - } - - required init(from decoder: Decoder) throws { - let container = try decoder.container(keyedBy: CodingKeys.self) - - #if FULL_PBX_PARSING - containerPortal = try container.decode(String.self, forKey: .containerPortal) - proxyType = try container.decode(String.self, forKey: .proxyType) - remoteInfo = try container.decode(String.self, forKey: .remoteInfo) - #endif - - remoteGlobalIDString = try container.decode(String.self, forKey: .remoteGlobalIDString) - - try super.init(from: decoder) - } -} diff --git a/PBXProjParser/Sources/PBXProjParser/Models/PBXFileReference.swift b/PBXProjParser/Sources/PBXProjParser/Models/PBXFileReference.swift deleted file mode 100644 index 9dea7fd..0000000 --- a/PBXProjParser/Sources/PBXProjParser/Models/PBXFileReference.swift +++ /dev/null @@ -1,51 +0,0 @@ -// -// PBXFileReference.swift -// -// -// Created by Thomas Hedderwick on 31/01/2023. -// - -import Foundation - -public class PBXFileReference: PBXObject { - #if FULL_PBX_PARSING - public let fileEncoding: String? - public let includeInIndex: String? - public let lastKnownFileType: String? - public let name: String? - public let sourceTree: String - #endif - public let explicitFileType: String? - public let path: String - - private enum CodingKeys: String, CodingKey { - #if FULL_PBX_PARSING - case fileEncoding - case includeInIndex - case lastKnownFileType - case name - case sourceTree - #endif - case explicitFileType - case path - } - - required init(from decoder: Decoder) throws { - let container = try decoder.container(keyedBy: CodingKeys.self) - - explicitFileType = try container.decodeIfPresent(String.self, forKey: .explicitFileType) - path = try container.decode(String.self, forKey: .path) - - #if FULL_PBX_PARSING - fileEncoding = try container.decodeIfPresent(String.self, forKey: .fileEncoding) - includeInIndex = try container.decodeIfPresent(String.self, forKey: .includeInIndex) - lastKnownFileType = try container.decodeIfPresent(String.self, forKey: .lastKnownFileType) - name = try container.decodeIfPresent(String.self, forKey: .name) - sourceTree = try container.decode(String.self, forKey: .sourceTree) - #endif - - try super.init(from: decoder) - } -} - -public class PBXReferenceProxy: PBXObject {} diff --git a/PBXProjParser/Sources/PBXProjParser/Models/PBXGroup.swift b/PBXProjParser/Sources/PBXProjParser/Models/PBXGroup.swift deleted file mode 100644 index 14f2f33..0000000 --- a/PBXProjParser/Sources/PBXProjParser/Models/PBXGroup.swift +++ /dev/null @@ -1,32 +0,0 @@ -// -// PBXGroup.swift -// -// -// Created by Thomas Hedderwick on 31/01/2023. -// - -import Foundation - -public class PBXGroup: PBXObject { -#if FULL_PBX_PARSING - public let children: [String] - public let name: String? - public let sourceTree: String - - private enum CodingKeys: String, CodingKey { - case children - case name - case sourceTree - } - - required init(from decoder: Decoder) throws { - let container = try decoder.container(keyedBy: CodingKeys.self) - - children = try container.decode([String].self, forKey: .children) - name = try container.decodeIfPresent(String.self, forKey: .name) - sourceTree = try container.decode(String.self, forKey: .sourceTree) - - try super.init(from: decoder) - } -#endif -} diff --git a/PBXProjParser/Sources/PBXProjParser/Models/PBXObject.swift b/PBXProjParser/Sources/PBXProjParser/Models/PBXObject.swift deleted file mode 100644 index 4dbd1f2..0000000 --- a/PBXProjParser/Sources/PBXProjParser/Models/PBXObject.swift +++ /dev/null @@ -1,110 +0,0 @@ -// -// File.swift -// -// -// Created by Thomas Hedderwick on 31/01/2023. -// - -import Foundation - -/// Base class for all PBX objects -public class PBXObject: Decodable { - /// Objects class name - public let isa: ObjectType - // / The 'UUID-like' reference key found at the start of an object declaration - public var reference: String! - - public enum ObjectType: String, Decodable, CaseIterable { - case buildFile = "PBXBuildFile" - case appleScriptBuildPhase = "PBXAppleScriptBuildPhase" - case copyFilesBuildPhase = "PBXCopyFilesBuildPhase" - case frameworksBuildPhase = "PBXFrameworksBuildPhase" - case headersBuildPhase = "PBXHeadersBuildPhase" - case resourcesBuildPhase = "PBXResourcesBuildPhase" - case shellScriptBuildPhase = "PBXShellScriptBuildPhase" - case sourcesBuildPhase = "PBXSourcesBuildPhase" - case containerItemProxy = "PBXContainerItemProxy" - case fileReference = "PBXFileReference" - case group = "PBXGroup" - case variantGroup = "PBXVariantGroup" - case aggregateTarget = "PBXAggregateTarget" - case legacyTarget = "PBXLegacyTarget" - case nativeTarget = "PBXNativeTarget" - case project = "PBXProject" - case targetDependency = "PBXTargetDependency" - case buildConfiguration = "XCBuildConfiguration" - case configurationList = "XCConfigurationList" - case swiftPackageProductDependency = "XCSwiftPackageProductDependency" - case localSwiftPackageReference = "XCLocalSwiftPackageReference" - case remoteSwiftPackageReference = "XCRemoteSwiftPackageReference" - case referenceProxy = "PBXReferenceProxy" - case versionGroup = "XCVersionGroup" - case buildRule = "PBXBuildRule" - case rezBuildPhase = "PBXRezBuildPhase" - - // swiftlint:disable cyclomatic_complexity - public func getType() -> PBXObject.Type { - switch self { - case .buildFile: return PBXBuildFile.self - case .appleScriptBuildPhase: return PBXAppleScriptBuildPhase.self - case .copyFilesBuildPhase: return PBXCopyFilesBuildPhase.self - case .frameworksBuildPhase: return PBXFrameworksBuildPhase.self - case .headersBuildPhase: return PBXHeadersBuildPhase.self - case .resourcesBuildPhase: return PBXResourcesBuildPhase.self - case .shellScriptBuildPhase: return PBXShellScriptBuildPhase.self - case .sourcesBuildPhase: return PBXSourcesBuildPhase.self - case .containerItemProxy: return PBXContainerItemProxy.self - case .fileReference: return PBXFileReference.self - case .group: return PBXGroup.self - case .variantGroup: return PBXVariantGroup.self - case .aggregateTarget: return PBXAggregateTarget.self - case .legacyTarget: return PBXLegacyTarget.self - case .nativeTarget: return PBXNativeTarget.self - case .project: return PBXProject.self - case .targetDependency: return PBXTargetDependency.self - case .buildConfiguration: return XCBuildConfiguration.self - case .configurationList: return XCConfigurationList.self - case .swiftPackageProductDependency: return XCSwiftPackageProductDependency.self - case .remoteSwiftPackageReference: return XCRemoteSwiftPackageReference.self - case .localSwiftPackageReference: return XCLocalSwiftPackageReference.self - case .referenceProxy: return PBXReferenceProxy.self - case .versionGroup: return XCVersionGroup.self - case .buildRule: return PBXBuildRule.self - case .rezBuildPhase: return PBXRezBuildPhase.self - } - // swiftlint:enable cyclomatic_complexity - } -} - -} - -/// Single case enum that decodes and holds a reference to an underlying `PBXObject` subclass -enum Object: Decodable { - /// The wrapped object - case object(PBXObject) - - private enum CodingKeys: String, CodingKey { - case isa - } - - init(from decoder: Decoder) throws { - let container = try decoder.container(keyedBy: CodingKeys.self) - let isa = try container.decode(PBXObject.ObjectType.self, forKey: .isa) - let singleContainer = try decoder.singleValueContainer() - - self = .object(try singleContainer.decode(isa.getType())) - } - - func unwrap() -> PBXObject { - if case .object(let object) = self { - return object - } - - fatalError( - """ - Failed to unwrap the underlying PBXObject, this should only happen if someone adds a case to `Object` and \ - didn't handle it. - """ - ) - } -} diff --git a/PBXProjParser/Sources/PBXProjParser/Models/PBXProj.swift b/PBXProjParser/Sources/PBXProjParser/Models/PBXProj.swift deleted file mode 100644 index 05c4033..0000000 --- a/PBXProjParser/Sources/PBXProjParser/Models/PBXProj.swift +++ /dev/null @@ -1,111 +0,0 @@ -// -// pbxproj.swift -// -// -// Created by Thomas Hedderwick on 27/01/2023. -// - -import Foundation - -/* - Note: This is based _largely_ on prior art. Thanks to: - http://www.monobjc.net/xcode-project-file-format.html - Cocoapods - - Some of the references here are reverse engineered from the `XCBuild.framework` from Xcode: - /Applications/Xcode.app/Contents/SharedFrameworks/XCBuild.framework/Versions/A/PlugIns/XCBBuildService.bundle/Contents/Frameworks/XCBProjectModel.framework/Versions/A/XCBProjectModel - */ - -// NOTE! Big thanks to http://www.monobjc.net/xcode-project-file-format.html for the file format reference - a lot of the layout here is based on that work - -/// Represents a pbxproj file -public class PBXProj: Decodable { - /// Version of the pbxproj - let archiveVersion: String - /// ??? - let classes: [String: String] - /// Version of the `objects` - let objectVersion: String - /// Mapping of UUID to their corresponding object - let objects: [String: Object] - /// UUID of the root object (probably a PBXProject - let rootObject: String - - enum Error: Swift.Error { - case projectNotFound(String) - } - - /// Decodes a `pbxproj` object from the contents of `path` - /// - Parameter path: path to `project.pbxproj` to parse - /// - Returns: a deserialized pbxproj structure - static func contentsOf(_ path: URL) throws -> PBXProj { - let data: Data - - do { - data = try Data(contentsOf: path) - } catch { - logger.error("Failed to get contents of path: \(path), please check that this path exists and is readable.") - throw error - } - - let decoder = PropertyListDecoder() - - do { - let project = try decoder.decode(Self.self, from: data) - project.fixup() - return project - } catch { - logger.error( - "Failed to decode the pbxproj for path: \(path). Please report this as an error with the pbxproj!" - ) - throw error - } - } - - /// Fixes `Object`s by unwrapping them and assigning the UUID key that represents them to the reference field - private func fixup() { - objects.forEach { (key, object) in - object.unwrap().reference = key - } - } -} - -/// Helper functions for operating on the project structure -public extension PBXProj { - /// Returns the object for a given key as the given type - /// - Parameters: - /// - key: the reference key for the object - /// - type: the type the object should be cast to - /// - Returns: the object, if the reference exists and the type conversion succeeded. Otherwise, nil. - func object(forKey key: String, as type: T.Type = T.self) -> T? { - objects[key]?.unwrap() as? T - } - - /// Returns all the objects of a given type in the project structure - /// - Parameter objectType: the type of objects to find - /// - Parameter type: the type to cast the object to - /// - Returns: an array of all the typed objects found in the project structure - func objects(of objectType: PBXObject.ObjectType, as type: T.Type) -> [T] { - objects - .map { $1.unwrap() } - .filter { $0.isa == objectType } - .compactMap { $0 as? T } - } - - func project() throws -> PBXProject { - guard let project: PBXProject = object(forKey: rootObject) else { - throw Error.projectNotFound( - "The root object of the pbxproj doesn't exist, or isn't castable to PBXProject... this shouldn't happen" - ) - } - - return project - } - - /// Returns a list of objects for a given list of references - /// - Parameter references: a list of references to lookup - /// - Returns: a list of objects that matches a reference in the references list - func objects(for references: [String]) -> [T] { - references.compactMap { object(forKey: $0) } - } -} diff --git a/PBXProjParser/Sources/PBXProjParser/Models/PBXProject.swift b/PBXProjParser/Sources/PBXProjParser/Models/PBXProject.swift deleted file mode 100644 index 45c50ca..0000000 --- a/PBXProjParser/Sources/PBXProjParser/Models/PBXProject.swift +++ /dev/null @@ -1,64 +0,0 @@ -// -// PBXProject.swift -// -// -// Created by Thomas Hedderwick on 31/01/2023. -// - -import Foundation - -public class PBXProject: PBXObject { - #if FULL_PBX_PARSING - public let attributes: [String: Any] - public let buildConfigurationList: String - public let compatibilityVersion: String - public let developmentRegion: String - public let hasScannedForEncodings: String - public let knownRegions: [String] - public let mainGroup: String - public let productRefGroup: String - public let projectDirPath: String - public let projectReferences: [[String: String]]? - public let projectRoot: String - #endif - public let packageReferences: [String] - public let targets: [String] /// Hold references to targets via their identifiers - - private enum CodingKeys: String, CodingKey { - case attributes - case buildConfigurationList - case compatibilityVersion - case developmentRegion - case hasScannedForEncodings - case knownRegions - case mainGroup - case productRefGroup - case projectDirPath - case projectReferences - case packageReferences - case projectRoot - case targets - } - - required init(from decoder: Decoder) throws { - let container = try decoder.container(keyedBy: CodingKeys.self) -#if FULL_PBX_PARSING - // We currently don't decode this as it's painful and we don't need it - attributes = [:] - buildConfigurationList = try container.decode(String.self, forKey: .buildConfigurationList) - compatibilityVersion = try container.decode(String.self, forKey: .compatibilityVersion) - developmentRegion = try container.decode(String.self, forKey: .developmentRegion) - hasScannedForEncodings = try container.decode(String.self, forKey: .hasScannedForEncodings) - knownRegions = try container.decode([String].self, forKey: .knownRegions) - mainGroup = try container.decode(String.self, forKey: .mainGroup) - productRefGroup = try container.decode(String.self, forKey: .productRefGroup) - projectDirPath = try container.decode(String.self, forKey: .projectDirPath) - projectReferences = try container.decodeIfPresent([[String: String]].self, forKey: .projectReferences) - projectRoot = try container.decode(String.self, forKey: .projectRoot) -#endif - packageReferences = try container.decodeIfPresent([String].self, forKey: .packageReferences) ?? [] - targets = try container.decode([String].self, forKey: .targets) - - try super.init(from: decoder) - } -} diff --git a/PBXProjParser/Sources/PBXProjParser/Models/PBXTarget.swift b/PBXProjParser/Sources/PBXProjParser/Models/PBXTarget.swift deleted file mode 100644 index 26b458c..0000000 --- a/PBXProjParser/Sources/PBXProjParser/Models/PBXTarget.swift +++ /dev/null @@ -1,170 +0,0 @@ -// -// PBXTarget.swift -// -// -// Created by Thomas Hedderwick on 31/01/2023. -// - -import Foundation - -public class PBXTarget: PBXObject { - #if FULL_PBX_PARSING - public let buildConfigurationList: String - public let comments: String? - #endif - public let productName: String? - public let name: String - public let dependencies: [String] - - private enum CodingKeys: String, CodingKey { - #if FULL_PBX_PARSING - case buildConfigurationList - case comments - #endif - case productName - case name - case dependencies - - } - - required init(from decoder: Decoder) throws { - let container = try decoder.container(keyedBy: CodingKeys.self) - - #if FULL_PBX_PARSING - buildConfigurationList = try container.decode(String.self, forKey: .buildConfigurationList) - comments = try container.decodeIfPresent(String.self, forKey: .comments) - #endif - productName = try container.decodeIfPresent(String.self, forKey: .productName) - name = try container.decode(String.self, forKey: .name) - dependencies = try container.decode([String].self, forKey: .dependencies) - - try super.init(from: decoder) - } -} - -public class PBXAggregateTarget: PBXTarget { - public let buildPhases: [String] - - private enum CodingKeys: String, CodingKey { - case buildPhases - } - - required init(from decoder: Decoder) throws { - let container = try decoder.container(keyedBy: CodingKeys.self) - - buildPhases = try container.decode([String].self, forKey: .buildPhases) - - try super.init(from: decoder) - } -} - -public class PBXLegacyTarget: PBXTarget {} - -public class PBXNativeTarget: PBXTarget { - #if FULL_PBX_PARSING - public let productInstallPath: String? - #endif - public let buildPhases: [String] - public let productType: String? - public let productReference: String? - public let packageProductDependencies: [String] - - private(set) var targetDependencies: [String: TargetDependency] = [:] - - public enum TargetDependency { - case native(PBXNativeTarget) - case package(XCSwiftPackageProductDependency) - case externalProjectFramework(String) - - public var name: String { - switch self { - case .native(let target): - return target.name - case .package(let package): - return package.productName - case .externalProjectFramework(let filename): - return (filename as NSString).deletingPathExtension - } - } - } - - private enum CodingKeys: String, CodingKey { - #if FULL_PBX_PARSING - case productInstallPath - #endif - case buildPhases - case productType - case productReference - case packageProductDependencies - } - - required init(from decoder: Decoder) throws { - let container = try decoder.container(keyedBy: CodingKeys.self) - - #if FULL_PBX_PARSING - productInstallPath = try container.decodeIfPresent(String.self, forKey: .productInstallPath) - #endif - buildPhases = try container.decodeIfPresent([String].self, forKey: .buildPhases) ?? [] - productType = try container.decodeIfPresent(String.self, forKey: .productType) - productReference = try container.decodeIfPresent(String.self, forKey: .productReference) - packageProductDependencies = try container.decodeIfPresent([String].self, forKey: .packageProductDependencies) ?? [] - - try super.init(from: decoder) - } - - func add(dependency: TargetDependency) { - targetDependencies[dependency.name] = dependency - } -} - -extension PBXNativeTarget: Hashable { - public func hash(into hasher: inout Hasher) { - hasher.combine(isa) - hasher.combine(reference) - hasher.combine(productName) - hasher.combine(name) - } -} - -extension PBXNativeTarget: Equatable { - public static func == (lhs: PBXNativeTarget, rhs: PBXNativeTarget) -> Bool { - // This should be enough as references _should_ be unique to the object - lhs.reference == rhs.reference - } -} - -extension PBXNativeTarget: CustomStringConvertible { - public var description: String { - #if FULL_PBX_PARSING - """ - - """ - #else - """ - - """ - #endif - } -} - -public class PBXTargetDependency: PBXObject { - public let target: String? - public let targetProxy: String? - - private enum CodingKeys: String, CodingKey { - case target - case targetProxy - } - - required init(from decoder: Decoder) throws { - let container = try decoder.container(keyedBy: CodingKeys.self) - - target = try container.decodeIfPresent(String.self, forKey: .target) - targetProxy = try container.decodeIfPresent(String.self, forKey: .targetProxy) - - try super.init(from: decoder) - } -} diff --git a/PBXProjParser/Sources/PBXProjParser/Models/PBXVariantGroup.swift b/PBXProjParser/Sources/PBXProjParser/Models/PBXVariantGroup.swift deleted file mode 100644 index d086ea1..0000000 --- a/PBXProjParser/Sources/PBXProjParser/Models/PBXVariantGroup.swift +++ /dev/null @@ -1,34 +0,0 @@ -// -// PBXVariantGroup.swift -// -// -// Created by Thomas Hedderwick on 31/01/2023. -// - -import Foundation - -public class PBXVariantGroup: PBXObject { - #if FULL_PBX_PARSING - public let children: [String] - public let name: String - public let sourceTree: String - - private enum CodingKeys: String, CodingKey { - case children - case name - case sourceTree - } - #endif - - required init(from decoder: Decoder) throws { - #if FULL_PBX_PARSING - let container = try decoder.container(keyedBy: CodingKeys.self) - - children = try container.decode([String].self, forKey: .children) - name = try container.decode(String.self, forKey: .name) - sourceTree = try container.decode(String.self, forKey: .sourceTree) - #endif - - try super.init(from: decoder) - } -} diff --git a/PBXProjParser/Sources/PBXProjParser/Models/XCBuildConfiguration.swift b/PBXProjParser/Sources/PBXProjParser/Models/XCBuildConfiguration.swift deleted file mode 100644 index b93d554..0000000 --- a/PBXProjParser/Sources/PBXProjParser/Models/XCBuildConfiguration.swift +++ /dev/null @@ -1,34 +0,0 @@ -// -// XCBuildConfiguration.swift -// -// -// Created by Thomas Hedderwick on 31/01/2023. -// - -import Foundation - -public class XCBuildConfiguration: PBXObject { - #if FULL_PBX_PARSING - public var baseConfigurationReference: String? - public var buildSettings: [String: Any] - public var name: String - - private enum CodingKeys: String, CodingKey { - case baseConfigurationReference - case buildSettings - case name - } - #endif - - required init(from decoder: Decoder) throws { - #if FULL_PBX_PARSING - let container = try decoder.container(keyedBy: CodingKeys.self) - - baseConfigurationReference = try container.decodeIfPresent(String.self, forKey: .baseConfigurationReference) - buildSettings = try container.decode([String: Any].self, forKey: .buildSettings) - name = try container.decode(String.self, forKey: .name) - #endif - - try super.init(from: decoder) - } -} diff --git a/PBXProjParser/Sources/PBXProjParser/Models/XCConfigurationList.swift b/PBXProjParser/Sources/PBXProjParser/Models/XCConfigurationList.swift deleted file mode 100644 index 4e12a1f..0000000 --- a/PBXProjParser/Sources/PBXProjParser/Models/XCConfigurationList.swift +++ /dev/null @@ -1,34 +0,0 @@ -// -// XCConfigurationList.swift -// -// -// Created by Thomas Hedderwick on 31/01/2023. -// - -import Foundation - -public class XCConfigurationList: PBXObject { - #if FULL_PBX_PARSING - public let buildConfigurations: [String] - public let defaultConfigurationIsVisible: String - public let defaultConfigurationName: String - - private enum CodingKeys: String, CodingKey { - case buildConfigurations - case defaultConfigurationIsVisible - case defaultConfigurationName - } - #endif - - required init(from decoder: Decoder) throws { - #if FULL_PBX_PARSING - let container = try decoder.container(keyedBy: CodingKeys.self) - - buildConfigurations = try container.decode([String].self, forKey: .buildConfigurations) - defaultConfigurationIsVisible = try container.decode(String.self, forKey: .defaultConfigurationIsVisible) - defaultConfigurationName = try container.decode(String.self, forKey: .defaultConfigurationName) - #endif - - try super.init(from: decoder) - } -} diff --git a/PBXProjParser/Sources/PBXProjParser/Models/XCSwiftPackageProductDependency.swift b/PBXProjParser/Sources/PBXProjParser/Models/XCSwiftPackageProductDependency.swift deleted file mode 100644 index 9274529..0000000 --- a/PBXProjParser/Sources/PBXProjParser/Models/XCSwiftPackageProductDependency.swift +++ /dev/null @@ -1,48 +0,0 @@ -// -// XCSwiftPackageProductDependency.swift -// -// -// Created by Thomas Hedderwick on 15/02/2023. -// - -import Foundation - -public class XCSwiftPackageProductDependency: PBXObject { - public let package: String? - public let productName: String - - private enum CodingKeys: CodingKey { - case package - case productName - } - - required init(from decoder: Decoder) throws { - let container = try decoder.container(keyedBy: CodingKeys.self) - - package = try container.decodeIfPresent(String.self, forKey: .package) - productName = try container.decode(String.self, forKey: .productName) - - try super.init(from: decoder) - } -} - -extension XCSwiftPackageProductDependency: Hashable { - public func hash(into hasher: inout Hasher) { - hasher.combine(isa) - hasher.combine(reference) - hasher.combine(package) - hasher.combine(productName) - } -} - -extension XCSwiftPackageProductDependency: Equatable { - public static func == (lhs: XCSwiftPackageProductDependency, rhs: XCSwiftPackageProductDependency) -> Bool { - lhs.reference == rhs.reference && - lhs.package == rhs.package && - lhs.productName == rhs.productName - } -} - -public class XCRemoteSwiftPackageReference: PBXObject {} -public class XCLocalSwiftPackageReference: PBXObject {} -public class XCVersionGroup: PBXObject {} diff --git a/PBXProjParser/Sources/PBXProjParser/ProjectParser.swift b/PBXProjParser/Sources/PBXProjParser/ProjectParser.swift deleted file mode 100644 index 8e4fbc6..0000000 --- a/PBXProjParser/Sources/PBXProjParser/ProjectParser.swift +++ /dev/null @@ -1,121 +0,0 @@ -import Foundation -import Logging - -var logger: Logger = .init(label: "com.veracode.PBXProjParser") - -/// An Xcode project parser (note: not an Xcode Project parser!) -public struct ProjectParser { - /// Path to the xcodeproj or xcworkspace bundle - let path: URL - - /// The type of project - let type: ProjectType - - /// All the native targets for the project - public var targets: [PBXNativeTarget] { - switch type { - case .project(let project): - return project.targets - case .workspace(let workspace): - return workspace.targets - } - } - - /// All the packages for the project - public var packages: [XCSwiftPackageProductDependency] { - switch type { - case .project(let project): - return project.packages - case .workspace(let workspace): - return workspace.packages - } - } - - /// Type of project this parser is working on - enum ProjectType { - /// A single Xcode Project - case project(XcodeProject) - /// An Xcode Workspace, which is a collection of Xcode Projects with some metadata - case workspace(XcodeWorkspace) - } - - public enum Error: Swift.Error { - case invalidPath(String) - } - - public init(path: URL, logLevel level: Logger.Level) throws { - self.path = path - logger.logLevel = level - - switch path.pathExtension { - case "xcodeproj": - let project = try XcodeProject(path: path) - type = .project(project) - case "xcworkspace": - let workspace = try XcodeWorkspace(path: path) - type = .workspace(workspace) - default: - throw Error.invalidPath("Path should be a xcodeproj or xcworkspace, got: \(path.lastPathComponent)") - } - } - - /// Returns a list of dependencies for a given target - /// - Parameter target: the target to get dependencies for - /// - Returns: an array of dependency references - public func dependencies(for target: String) -> [String] { - guard let project = project(for: target) else { - logger.error("Couldn't find project for target: \(target)") - return [] - } - - guard let target = project.target(for: target) else { - // SPMs don't list their dependencies in the pbxproj, skip warning about them - if project.package(for: target) == nil { - // TODO: once SPM dependencies work better, move this back to error level warning - logger.debug( - """ - Failed to find a target: \(target) in project: \(project.path). \ - Possible targets: \(project.targets.map { ($0.name, $0.productName ?? "nil")}). \ - Possible Packages: \(project.packages.map { $0.productName}) - """ - ) - } - - return [] - } - - return target.targetDependencies - .values - .map { dependency in - switch dependency { - case .native(let target): - if let path = project.path(for: target) { - return path - } - - fallthrough - default: - return dependency.name - } - } - } - - /// Gets a project for a given target - /// - Parameter target: the target to search for - /// - Returns: a `XcodeProject` that holds the target, if one was found - private func project(for target: String) -> XcodeProject? { - switch type { - case .project(let project): - return project - case .workspace(let workspace): - return workspace.targetsToProject[target] - } - } - - /// Gets the project model for a given target - /// - Parameter target: the target to search for - /// - Returns: a `PBXProj` that represents the pbxproj this target is a part of, if one was found - public func model(for target: String) -> PBXProj? { - project(for: target)?.model - } -} diff --git a/PBXProjParser/Sources/PBXProjParser/Workspace/Reference.swift b/PBXProjParser/Sources/PBXProjParser/Workspace/Reference.swift deleted file mode 100644 index a205cdf..0000000 --- a/PBXProjParser/Sources/PBXProjParser/Workspace/Reference.swift +++ /dev/null @@ -1,86 +0,0 @@ -// -// Reference.swift -// -// -// Created by Thomas Hedderwick on 18/10/2023. -// -import Foundation - -protocol Reference { - var location: Location { get } - static var elementName: String { get } -} - -enum Location { - // TODO: Find where we can get a definitive list of these. Xcode must have them somewhere? - case container(String) - case group(String) - - enum Error: Swift.Error { - case invalidLocation(String) - } - - init(_ location: String) throws { - let split = location - .split(separator: ":", maxSplits: 1, omittingEmptySubsequences: false) - .map(String.init) - - guard - let key = split.first, - let value = split.last - else { throw Error.invalidLocation("Couldn't extract key/value pair from split: \(split)") } - - switch key { - case "container": self = .container(value) - case "group": self = .group(value) - default: throw Error.invalidLocation("Key didn't match a supported location key: \(key)") - } - } - - var path: String { - switch self { - case .container(let path): return path - case .group(let path): return path - } - } -} - -class Group: Reference { - static let elementName: String = "Group" - - let location: Location - let name: String? - var references: [Reference] = [] - - init(location: String, name: String?) throws { - self.location = try .init(location) - self.name = name - } -} - -struct FileRef: Reference { - static let elementName: String = "FileRef" - - let location: Location - let enclosingGroup: Group? - - init(location: String, enclosingGroup: Group? = nil) throws { - self.location = try .init(location) - self.enclosingGroup = enclosingGroup - } - - var path: String { - guard - let enclosingGroup - else { return location.path } - - switch enclosingGroup.location { - case let .group(path), let .container(path): - if path.last == "/" { - return path + location.path - } - - return path + "/" + location.path - } - } -} diff --git a/PBXProjParser/Sources/PBXProjParser/Workspace/WorkspaceParser.swift b/PBXProjParser/Sources/PBXProjParser/Workspace/WorkspaceParser.swift deleted file mode 100644 index 4bca3fe..0000000 --- a/PBXProjParser/Sources/PBXProjParser/Workspace/WorkspaceParser.swift +++ /dev/null @@ -1,88 +0,0 @@ -// -// WorkspaceParser.swift -// -// -// Created by Thomas Hedderwick on 18/10/2023. -// - -import Foundation - -struct Workspace { - private(set) var fileReferences: [FileRef] = [] - private(set) var groupReferences: [Group] = [] -} - -struct WorkspaceParser { - static func parse(_ path: URL) throws -> Workspace { - // Parse the `contents.xcworkspacedata` (XML) file and get the list of projects - let contentsPath = path.appendingPathComponent("contents.xcworkspacedata") - - let data = try Data(contentsOf: contentsPath) - let delegate = WorkspaceDataParserDelegate() - let parser = XMLParser(data: data) - parser.delegate = delegate - parser.parse() - - return .init( - fileReferences: delegate.fileReferences, - groupReferences: delegate.groupReferences - ) - } -} - -private class WorkspaceDataParserDelegate: NSObject, XMLParserDelegate { - private(set) var fileReferences: [FileRef] = [] - private(set) var groupReferences: [Group] = [] - - static let supportedElements = [Group.elementName, FileRef.elementName] - - private var groupPath: [Group] = [] - - func parser( - _ parser: XMLParser, - didStartElement elementName: String, - namespaceURI: String?, - qualifiedName qName: String?, - attributes attributeDict: [String: String] = [:] - ) { - guard Self.supportedElements.contains(elementName) else { - logger.debug("Skipping parsing of unsupported element: \(elementName)") - return - } - - guard - let location = attributeDict["location"] - else { - logger.debug("Location attribute for element \(elementName) is nil, this shouldn't be the case: \(attributeDict)") - return - } - - do { - switch elementName { - case Group.elementName: - let group = try Group(location: location, name: attributeDict["name"]) - groupPath.append(group) - groupReferences.append(group) - case FileRef.elementName: - let file = try FileRef(location: location, enclosingGroup: groupPath.last) - fileReferences.append(file) - groupPath.last?.references.append(file) - // Ignore any element that doesn't match the search space - default: - break - } - } catch { - logger.debug("Parsing element: \(elementName) failed. Reason: \(error)") - } - } - - func parser( - _ parser: XMLParser, - didEndElement elementName: String, - namespaceURI: String?, - qualifiedName qName: String? - ) { - guard elementName == Group.elementName else { return } - groupPath.removeLast() - } -} diff --git a/PBXProjParser/Sources/PBXProjParser/XcodeProject.swift b/PBXProjParser/Sources/PBXProjParser/XcodeProject.swift deleted file mode 100644 index 90e9a77..0000000 --- a/PBXProjParser/Sources/PBXProjParser/XcodeProject.swift +++ /dev/null @@ -1,231 +0,0 @@ -// -// XcodeProject.swift -// -// -// Created by Thomas Hedderwick on 27/01/2023. -// - -import Foundation - -/// Represents an xcodeproj bundle -public struct XcodeProject { - /// Path to the Workspace - public let path: URL - - /// The underlying pbxproj model - public let model: PBXProj - - /// The 'project' object for the pbxproj - let project: PBXProject - - /// All the native targets in this project - let targets: [PBXNativeTarget] - - /// All the swift packages in this project - let packages: [XCSwiftPackageProductDependency] - - enum Error: Swift.Error { - case invalidPBXProj(String) - } - - public init(path: URL) throws { - self.path = path - model = try PBXProj.contentsOf(path.appendingPathComponent("project.pbxproj")) - project = try model.project() - - targets = model.objects(for: project.targets) - .filter { - // Cocoapods likes to insert resource bundles as native targets. On iOS resource bundles - // cannot contain executables, therefore we should ignore them - IR will never be generated for them. - $0.productType != "com.apple.product-type.bundle" - } - - packages = model.objects(of: .swiftPackageProductDependency, as: XCSwiftPackageProductDependency.self) - - // get all the direct dependencies - targets.forEach { determineDirectDependencies($0) } - - targets.forEach { target in - logger.debug("target: \(target.name). Dependencies: \(target.targetDependencies.map { $0.1.name })") - } - - packages.forEach { package in - logger.debug("package: \(package.productName)") - } - } - - /// Gets the native target for a given name - /// - Parameter name: the target name or product name to lookup - /// - Returns: the native target corresponding to the name provided - func target(for name: String) -> PBXNativeTarget? { - if let target = targets.filter({ $0.name == name }).first { - return target - } else if let target = targets.filter({ $0.productName == name }).first { - return target - } - - return nil - } - - /// Gets the package dependency object for a given name - /// - Parameter name: the product name to lookup - /// - Returns: the swift package product dependency object corresponding to the name provided - func package(for key: String) -> XCSwiftPackageProductDependency? { - packages.filter({ $0.productName == key }).first - } - - /// Determines the target & swift package dependencies for a target - /// - Parameter target: the target to get direct dependencies for - private func determineDirectDependencies(_ target: PBXNativeTarget) { - // Calculate the native target dependencies - target.dependencies - .compactMap { model.object(forKey: $0, as: PBXTargetDependency.self) } - .compactMap { dependency in - if let target = dependency.target { - return target - } - - guard - let targetProxy = dependency.targetProxy, - let proxy = model.object(forKey: targetProxy, as: PBXContainerItemProxy.self) - else { - return nil - } - - return proxy.remoteGlobalIDString - } - .compactMap { model.object(forKey: $0, as: PBXNativeTarget.self) } - .forEach { target.add(dependency: .native($0)) } - - // Calculate the swift package dependencies - target.packageProductDependencies - .compactMap { model.object(forKey: $0, as: XCSwiftPackageProductDependency.self) } - .forEach { target.add(dependency: .package($0)) } - - // Calculate dependencies from "Embed Frameworks" copy files build phase - let embeddedFrameworks = determineEmbeddedFrameworksDependencies(target, with: model) - - // Calculate the dependencies from "Link Binary with Library" build phase - let linkLibraries = determineBuildPhaseFrameworkDependencies(target, with: model) - - let buildFiles = embeddedFrameworks + linkLibraries - - // Now, we have two potential targets - file & package dependencies. - // File dependencies will likely have a reference in another Xcode Project. We might not have seen said project yet, so we need to offload discovery until after we've parsed all projects... - // Package dependencies will be a swift package - those we can handle easily :) - - // ONE: package dependencies - they are the easiest - buildFiles - .compactMap { $0.productRef } - .compactMap { model.object(forKey: $0, as: XCSwiftPackageProductDependency.self) } - .forEach { target.add(dependency: .package($0)) } - - // TWO: Resolve dependencies to... a thing that refers to something in the other project - let fileReferences = buildFiles - .compactMap { $0.fileRef } - .compactMap { model.object(forKey: $0, as: PBXFileReference.self) } - - fileReferences - .filter { $0.explicitFileType == "wrapper.framework" } - .compactMap { $0.path } // TODO: do we want to last path component the path here? Need to figure out matching... - .filter { !$0.contains("System/Library/Frameworks/")} // System frameworks will contain this path - .forEach { target.add(dependency: .externalProjectFramework($0)) } - } - - /// Determines transitive dependencies by looping through direct dependencies and finding the items they depend on - /// - Parameter target: the target to find transitive dependencies for - private func determineTransitiveDependencies(_ target: PBXNativeTarget) { - logger.debug("Target: \(target.name). Deps: \(target.targetDependencies.map { $0.0 })") - - var targetDependencies = target.targetDependencies.map { $0.1 } - var seen = Set() - var count = 50 // recursion guard - - while !targetDependencies.isEmpty, count != 0 { - let dependency = targetDependencies.removeFirst() - count -= 1 - - if seen.contains(dependency.name) { - continue - } - - seen.insert(dependency.name) - - switch dependency { - case .native(let nativeTarget): - logger.debug("Adding native dependency: \(dependency.name), deps: \(nativeTarget.targetDependencies.map { $0.0 })") - targetDependencies.append(contentsOf: nativeTarget.targetDependencies.map { $0.1 }) - nativeTarget.targetDependencies.forEach { target.add(dependency: $0.1) } - case .package: - // Packages don't have a transitive dependency field like native targets do, so we can't find dependency of a dependency from the project file - logger.debug("Adding package dependency: \(dependency.name)") - target.add(dependency: dependency) - case .externalProjectFramework: - // Can't move IR dependencies for prebuilt frameworks - continue - } - } - - logger.debug("--- FINAL ---") - logger.debug("Target: \(target.name), deps: \(target.targetDependencies.map { $0.0 })") - } - - /// Gets the 'path' (normally the name of the target's product) for a given target - /// - Parameters: - /// - target: the target to get the path for - /// - removeExtension: should the file extension be removed from the returned path - /// - Returns: the path, if one was found - func path(for target: PBXNativeTarget, removeExtension: Bool = false) -> String? { - guard let productReference = target.productReference else { - logger.debug("Failed to get product reference for target: \(target). Possibly a SPM Package description?") - return nil - } - - guard let reference = model.object(forKey: productReference, as: PBXFileReference.self) else { - logger.error("Failed to get object for target productReference: \(productReference)") - return nil - } - - var path = ((reference.path as NSString).lastPathComponent as String) - - if removeExtension, let index = path.firstIndex(of: ".") { - path = String(path[path.startIndex.. [PBXBuildFile] { - // Find the 'Link Binary with Libraries' build phase - let buildPhase = target.buildPhases - .compactMap { model.object(forKey: $0, as: PBXFrameworksBuildPhase.self) } - .first - - guard let buildPhase else { - logger.debug("No PBXFrameworkBuild phase for target: \(target) found, continuing.") - return [] - } - - return buildPhase - .files - .compactMap { model.object(forKey: $0, as: PBXBuildFile.self) } -} - -private func determineEmbeddedFrameworksDependencies(_ target: PBXNativeTarget, with model: PBXProj) -> [PBXBuildFile] { - // Find the "Embed Frameworks" build phase (copy files build phase) - let buildPhases = target - .buildPhases - .compactMap { model.object(forKey: $0, as: PBXCopyFilesBuildPhase.self) } - - return buildPhases - .flatMap { $0.files } - .compactMap { model.object(forKey: $0, as: PBXBuildFile.self) } - .filter { file in - guard let ref = file.fileRef else { return false } - guard let object = model.object(forKey: ref, as: PBXFileReference.self) else { return false } - guard object.explicitFileType == "wrapper.framework" else { return false } - - return true - } -} diff --git a/PBXProjParser/Sources/PBXProjParser/XcodeWorkspace.swift b/PBXProjParser/Sources/PBXProjParser/XcodeWorkspace.swift deleted file mode 100644 index 608dfea..0000000 --- a/PBXProjParser/Sources/PBXProjParser/XcodeWorkspace.swift +++ /dev/null @@ -1,61 +0,0 @@ -// -// XcodeWorkspace.swift -// -// -// Created by Thomas Hedderwick on 27/01/2023. -// - -import Foundation - -/// Represents an xcworkspace - which is a set of xcodeproj bundles -class XcodeWorkspace { - /// Path to the Workspace - let path: URL - /// Path to the various underlying xcodeproj bundles - private(set) var projectPaths: [URL] - /// List of projects this workspace references - let projects: [XcodeProject] - - /// A mapping of targets to the projects that define them - let targetsToProject: [String: XcodeProject] - - init(path: URL) throws { - self.path = path - - // Parse the `contents.xcworkspacedata` (XML) file and get the list of projects - let workspace = try WorkspaceParser.parse(path) - let paths = workspace - .fileReferences - .map { $0.path } - .filter { $0.hasSuffix("xcodeproj") } - - let baseFolder = path.deletingLastPathComponent() - projectPaths = paths - .map { baseFolder.appendingPathComponent($0, isDirectory: true) } - - projects = try projectPaths.map(XcodeProject.init(path:)) - - targetsToProject = projects.reduce(into: [String: XcodeProject](), { partialResult, project in - project.targets.forEach { (target) in - partialResult[target.name] = project - if let productName = target.productName, productName != target.name { - partialResult[productName] = project - } - } - - project.packages.forEach { (target) in - partialResult[target.productName] = project - } - }) - } - - /// All native targets in the workspace - var targets: [PBXNativeTarget] { - projects.flatMap { $0.targets } - } - - /// All packages in the workspace - var packages: [XCSwiftPackageProductDependency] { - projects.flatMap { $0.packages } - } -} diff --git a/PBXProjParser/Sources/projparser/projparser.swift b/PBXProjParser/Sources/projparser/projparser.swift deleted file mode 100644 index ff2cb85..0000000 --- a/PBXProjParser/Sources/projparser/projparser.swift +++ /dev/null @@ -1,22 +0,0 @@ -// -// projparser.swift -// -// -// Created by Thomas Hedderwick on 24/02/2023. -// - -import Foundation -import PBXProjParser - -@main -struct ProjParser { - static func main() throws { - guard CommandLine.arguments.count == 2 else { - print("USAGE: \(CommandLine.arguments.first!) [project path]") - return - } - - let projectPath = URL(fileURLWithPath: CommandLine.arguments[1]) - _ = try ProjectParser(path: projectPath, logLevel: .debug) - } -} diff --git a/PBXProjParser/Tests/PBXProjParserTests/PBXProjParserTests.swift b/PBXProjParser/Tests/PBXProjParserTests/PBXProjParserTests.swift deleted file mode 100644 index 3d9d527..0000000 --- a/PBXProjParser/Tests/PBXProjParserTests/PBXProjParserTests.swift +++ /dev/null @@ -1,47 +0,0 @@ -import XCTest -@testable import PBXProjParser - -final class PBXProjParserTests: XCTestCase { - func testDoubleTargetsHaveValidTargets() throws { - let path = URL(fileURLWithPath: - "Tests/PBXProjParserTests/TestAssets/Projects/DoubleTargetTest/DoubleTargetTest.xcworkspace" - ) - - let parser = try ProjectParser(path: path, logLevel: .debug) - - let targets = parser.targets - let targetNames = targets.map { $0.name } - - let knownNames = ["DoubleTargetTest", "MyBundle", "Pods-DoubleTargetTest"] - - XCTAssert(targetNames.count == knownNames.count, "The list of targets doesn't match the size of allowed names") - - for name in targetNames { - XCTAssert(knownNames.contains(name), "\(name) wasn't in knownNames: \(knownNames)") - } - } - - func testDoubleTargetsHaveValidDependencies() throws { - let path = URL(fileURLWithPath: - "Tests/PBXProjParserTests/TestAssets/Projects/DoubleTargetTest/DoubleTargetTest.xcworkspace" - ) - - let parser = try ProjectParser(path: path, logLevel: .debug) - let dependencies = parser.targets.reduce(into: [String: [String]]()) { partialResult, target in - partialResult[target.name] = parser.dependencies(for: target.name) - } - - let knownDependencies = [ - // TODO: This should probably handle Cocoapods doing their stupid embedding thing without listing it as a dependency... - "DoubleTargetTest": [], - // TODO: should we also disregard Cocoapods doing their stupid bundle as a native target even though it isn't - "MyBundle": ["MyBundle.bundle"], - "Pods-DoubleTargetTest": ["MyBundle.framework", "MyBundle.bundle"] - ] - - XCTAssert( - dependencies == knownDependencies, - "Dependencies: \(dependencies) doesn't equal known dependencies: \(knownDependencies)" - ) - } -} diff --git a/PBXProjParser/Tests/PBXProjParserTests/TestAssets/Projects/DoubleTargetTest/DoubleTargetTest.xcodeproj/project.pbxproj b/PBXProjParser/Tests/PBXProjParserTests/TestAssets/Projects/DoubleTargetTest/DoubleTargetTest.xcodeproj/project.pbxproj deleted file mode 100644 index ccd69a9..0000000 --- a/PBXProjParser/Tests/PBXProjParserTests/TestAssets/Projects/DoubleTargetTest/DoubleTargetTest.xcodeproj/project.pbxproj +++ /dev/null @@ -1,418 +0,0 @@ -// !$*UTF8*$! -{ - archiveVersion = 1; - classes = { - }; - objectVersion = 56; - objects = { - -/* Begin PBXBuildFile section */ - 35460152C27D425AD341441A /* Pods_DoubleTargetTest.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 85C8A3B68F9E974DC6038BFF /* Pods_DoubleTargetTest.framework */; }; - CE25174229C9C2C100A2E14B /* DoubleTargetTestApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE25174129C9C2C100A2E14B /* DoubleTargetTestApp.swift */; }; - CE25174429C9C2C100A2E14B /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE25174329C9C2C100A2E14B /* ContentView.swift */; }; - CE25174629C9C2C200A2E14B /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = CE25174529C9C2C200A2E14B /* Assets.xcassets */; }; - CE25174929C9C2C200A2E14B /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = CE25174829C9C2C200A2E14B /* Preview Assets.xcassets */; }; -/* End PBXBuildFile section */ - -/* Begin PBXFileReference section */ - 815B8E932FA5793B81A510EC /* Pods-DoubleTargetTest.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-DoubleTargetTest.release.xcconfig"; path = "Target Support Files/Pods-DoubleTargetTest/Pods-DoubleTargetTest.release.xcconfig"; sourceTree = ""; }; - 85C8A3B68F9E974DC6038BFF /* Pods_DoubleTargetTest.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_DoubleTargetTest.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - BC9AAD5FA65122E6FCDEC90E /* Pods-DoubleTargetTest.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-DoubleTargetTest.debug.xcconfig"; path = "Target Support Files/Pods-DoubleTargetTest/Pods-DoubleTargetTest.debug.xcconfig"; sourceTree = ""; }; - CE25173E29C9C2C100A2E14B /* DoubleTargetTest.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = DoubleTargetTest.app; sourceTree = BUILT_PRODUCTS_DIR; }; - CE25174129C9C2C100A2E14B /* DoubleTargetTestApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DoubleTargetTestApp.swift; sourceTree = ""; }; - CE25174329C9C2C100A2E14B /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = ""; }; - CE25174529C9C2C200A2E14B /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; - CE25174829C9C2C200A2E14B /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = ""; }; -/* End PBXFileReference section */ - -/* Begin PBXFrameworksBuildPhase section */ - CE25173B29C9C2C100A2E14B /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - 35460152C27D425AD341441A /* Pods_DoubleTargetTest.framework in Frameworks */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXFrameworksBuildPhase section */ - -/* Begin PBXGroup section */ - 01D7ED154039FF16EE28AD13 /* Pods */ = { - isa = PBXGroup; - children = ( - BC9AAD5FA65122E6FCDEC90E /* Pods-DoubleTargetTest.debug.xcconfig */, - 815B8E932FA5793B81A510EC /* Pods-DoubleTargetTest.release.xcconfig */, - ); - path = Pods; - sourceTree = ""; - }; - 04DE6891E5415C30C89A0396 /* Frameworks */ = { - isa = PBXGroup; - children = ( - 85C8A3B68F9E974DC6038BFF /* Pods_DoubleTargetTest.framework */, - ); - name = Frameworks; - sourceTree = ""; - }; - CE25173529C9C2C100A2E14B = { - isa = PBXGroup; - children = ( - CE25174029C9C2C100A2E14B /* DoubleTargetTest */, - CE25173F29C9C2C100A2E14B /* Products */, - 01D7ED154039FF16EE28AD13 /* Pods */, - 04DE6891E5415C30C89A0396 /* Frameworks */, - ); - sourceTree = ""; - }; - CE25173F29C9C2C100A2E14B /* Products */ = { - isa = PBXGroup; - children = ( - CE25173E29C9C2C100A2E14B /* DoubleTargetTest.app */, - ); - name = Products; - sourceTree = ""; - }; - CE25174029C9C2C100A2E14B /* DoubleTargetTest */ = { - isa = PBXGroup; - children = ( - CE25174129C9C2C100A2E14B /* DoubleTargetTestApp.swift */, - CE25174329C9C2C100A2E14B /* ContentView.swift */, - CE25174529C9C2C200A2E14B /* Assets.xcassets */, - CE25174729C9C2C200A2E14B /* Preview Content */, - ); - path = DoubleTargetTest; - sourceTree = ""; - }; - CE25174729C9C2C200A2E14B /* Preview Content */ = { - isa = PBXGroup; - children = ( - CE25174829C9C2C200A2E14B /* Preview Assets.xcassets */, - ); - path = "Preview Content"; - sourceTree = ""; - }; -/* End PBXGroup section */ - -/* Begin PBXNativeTarget section */ - CE25173D29C9C2C100A2E14B /* DoubleTargetTest */ = { - isa = PBXNativeTarget; - buildConfigurationList = CE25174C29C9C2C200A2E14B /* Build configuration list for PBXNativeTarget "DoubleTargetTest" */; - buildPhases = ( - 60C22A37CD715212D57E629D /* [CP] Check Pods Manifest.lock */, - CE25173A29C9C2C100A2E14B /* Sources */, - CE25173B29C9C2C100A2E14B /* Frameworks */, - CE25173C29C9C2C100A2E14B /* Resources */, - 16B418F3FB824614A4DADAF1 /* [CP] Embed Pods Frameworks */, - ); - buildRules = ( - ); - dependencies = ( - ); - name = DoubleTargetTest; - productName = DoubleTargetTest; - productReference = CE25173E29C9C2C100A2E14B /* DoubleTargetTest.app */; - productType = "com.apple.product-type.application"; - }; -/* End PBXNativeTarget section */ - -/* Begin PBXProject section */ - CE25173629C9C2C100A2E14B /* Project object */ = { - isa = PBXProject; - attributes = { - BuildIndependentTargetsInParallel = 1; - LastSwiftUpdateCheck = 1420; - LastUpgradeCheck = 1420; - TargetAttributes = { - CE25173D29C9C2C100A2E14B = { - CreatedOnToolsVersion = 14.2; - }; - }; - }; - buildConfigurationList = CE25173929C9C2C100A2E14B /* Build configuration list for PBXProject "DoubleTargetTest" */; - compatibilityVersion = "Xcode 14.0"; - developmentRegion = en; - hasScannedForEncodings = 0; - knownRegions = ( - en, - Base, - ); - mainGroup = CE25173529C9C2C100A2E14B; - productRefGroup = CE25173F29C9C2C100A2E14B /* Products */; - projectDirPath = ""; - projectRoot = ""; - targets = ( - CE25173D29C9C2C100A2E14B /* DoubleTargetTest */, - ); - }; -/* End PBXProject section */ - -/* Begin PBXResourcesBuildPhase section */ - CE25173C29C9C2C100A2E14B /* Resources */ = { - isa = PBXResourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - CE25174929C9C2C200A2E14B /* Preview Assets.xcassets in Resources */, - CE25174629C9C2C200A2E14B /* Assets.xcassets in Resources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXResourcesBuildPhase section */ - -/* Begin PBXShellScriptBuildPhase section */ - 16B418F3FB824614A4DADAF1 /* [CP] Embed Pods Frameworks */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputFileListPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-DoubleTargetTest/Pods-DoubleTargetTest-frameworks-${CONFIGURATION}-input-files.xcfilelist", - ); - name = "[CP] Embed Pods Frameworks"; - outputFileListPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-DoubleTargetTest/Pods-DoubleTargetTest-frameworks-${CONFIGURATION}-output-files.xcfilelist", - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-DoubleTargetTest/Pods-DoubleTargetTest-frameworks.sh\"\n"; - showEnvVarsInLog = 0; - }; - 60C22A37CD715212D57E629D /* [CP] Check Pods Manifest.lock */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputFileListPaths = ( - ); - inputPaths = ( - "${PODS_PODFILE_DIR_PATH}/Podfile.lock", - "${PODS_ROOT}/Manifest.lock", - ); - name = "[CP] Check Pods Manifest.lock"; - outputFileListPaths = ( - ); - outputPaths = ( - "$(DERIVED_FILE_DIR)/Pods-DoubleTargetTest-checkManifestLockResult.txt", - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; - showEnvVarsInLog = 0; - }; -/* End PBXShellScriptBuildPhase section */ - -/* Begin PBXSourcesBuildPhase section */ - CE25173A29C9C2C100A2E14B /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - CE25174429C9C2C100A2E14B /* ContentView.swift in Sources */, - CE25174229C9C2C100A2E14B /* DoubleTargetTestApp.swift in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXSourcesBuildPhase section */ - -/* Begin XCBuildConfiguration section */ - CE25174A29C9C2C200A2E14B /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - CLANG_ANALYZER_NONNULL = YES; - CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; - CLANG_ENABLE_MODULES = YES; - CLANG_ENABLE_OBJC_ARC = YES; - CLANG_ENABLE_OBJC_WEAK = YES; - CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; - CLANG_WARN_BOOL_CONVERSION = YES; - CLANG_WARN_COMMA = YES; - CLANG_WARN_CONSTANT_CONVERSION = YES; - CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; - CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; - CLANG_WARN_DOCUMENTATION_COMMENTS = YES; - CLANG_WARN_EMPTY_BODY = YES; - CLANG_WARN_ENUM_CONVERSION = YES; - CLANG_WARN_INFINITE_RECURSION = YES; - CLANG_WARN_INT_CONVERSION = YES; - CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; - CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; - CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; - CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; - CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; - CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; - CLANG_WARN_STRICT_PROTOTYPES = YES; - CLANG_WARN_SUSPICIOUS_MOVE = YES; - CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; - CLANG_WARN_UNREACHABLE_CODE = YES; - CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; - COPY_PHASE_STRIP = NO; - DEBUG_INFORMATION_FORMAT = dwarf; - ENABLE_STRICT_OBJC_MSGSEND = YES; - ENABLE_TESTABILITY = YES; - GCC_C_LANGUAGE_STANDARD = gnu11; - GCC_DYNAMIC_NO_PIC = NO; - GCC_NO_COMMON_BLOCKS = YES; - GCC_OPTIMIZATION_LEVEL = 0; - GCC_PREPROCESSOR_DEFINITIONS = ( - "DEBUG=1", - "$(inherited)", - ); - GCC_WARN_64_TO_32_BIT_CONVERSION = YES; - GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; - GCC_WARN_UNDECLARED_SELECTOR = YES; - GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; - GCC_WARN_UNUSED_FUNCTION = YES; - GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 16.2; - MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; - MTL_FAST_MATH = YES; - ONLY_ACTIVE_ARCH = YES; - SDKROOT = iphoneos; - SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; - SWIFT_OPTIMIZATION_LEVEL = "-Onone"; - }; - name = Debug; - }; - CE25174B29C9C2C200A2E14B /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - CLANG_ANALYZER_NONNULL = YES; - CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; - CLANG_ENABLE_MODULES = YES; - CLANG_ENABLE_OBJC_ARC = YES; - CLANG_ENABLE_OBJC_WEAK = YES; - CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; - CLANG_WARN_BOOL_CONVERSION = YES; - CLANG_WARN_COMMA = YES; - CLANG_WARN_CONSTANT_CONVERSION = YES; - CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; - CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; - CLANG_WARN_DOCUMENTATION_COMMENTS = YES; - CLANG_WARN_EMPTY_BODY = YES; - CLANG_WARN_ENUM_CONVERSION = YES; - CLANG_WARN_INFINITE_RECURSION = YES; - CLANG_WARN_INT_CONVERSION = YES; - CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; - CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; - CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; - CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; - CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; - CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; - CLANG_WARN_STRICT_PROTOTYPES = YES; - CLANG_WARN_SUSPICIOUS_MOVE = YES; - CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; - CLANG_WARN_UNREACHABLE_CODE = YES; - CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; - COPY_PHASE_STRIP = NO; - DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; - ENABLE_NS_ASSERTIONS = NO; - ENABLE_STRICT_OBJC_MSGSEND = YES; - GCC_C_LANGUAGE_STANDARD = gnu11; - GCC_NO_COMMON_BLOCKS = YES; - GCC_WARN_64_TO_32_BIT_CONVERSION = YES; - GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; - GCC_WARN_UNDECLARED_SELECTOR = YES; - GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; - GCC_WARN_UNUSED_FUNCTION = YES; - GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 16.2; - MTL_ENABLE_DEBUG_INFO = NO; - MTL_FAST_MATH = YES; - SDKROOT = iphoneos; - SWIFT_COMPILATION_MODE = wholemodule; - SWIFT_OPTIMIZATION_LEVEL = "-O"; - VALIDATE_PRODUCT = YES; - }; - name = Release; - }; - CE25174D29C9C2C200A2E14B /* Debug */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = BC9AAD5FA65122E6FCDEC90E /* Pods-DoubleTargetTest.debug.xcconfig */; - buildSettings = { - ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; - ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; - CODE_SIGN_IDENTITY = "Apple Development"; - CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 1; - DEVELOPMENT_ASSET_PATHS = "\"DoubleTargetTest/Preview Content\""; - DEVELOPMENT_TEAM = M2QJQZX6EU; - ENABLE_PREVIEWS = YES; - GENERATE_INFOPLIST_FILE = YES; - INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES; - INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; - INFOPLIST_KEY_UILaunchScreen_Generation = YES; - INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; - INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/Frameworks", - ); - MARKETING_VERSION = 1.0; - PRODUCT_BUNDLE_IDENTIFIER = com.veracode.DoubleTargetTest; - PRODUCT_NAME = "$(TARGET_NAME)"; - PROVISIONING_PROFILE_SPECIFIER = ""; - SWIFT_EMIT_LOC_STRINGS = YES; - SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2"; - }; - name = Debug; - }; - CE25174E29C9C2C200A2E14B /* Release */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = 815B8E932FA5793B81A510EC /* Pods-DoubleTargetTest.release.xcconfig */; - buildSettings = { - ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; - ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; - CODE_SIGN_IDENTITY = "Apple Development"; - CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 1; - DEVELOPMENT_ASSET_PATHS = "\"DoubleTargetTest/Preview Content\""; - DEVELOPMENT_TEAM = M2QJQZX6EU; - ENABLE_PREVIEWS = YES; - GENERATE_INFOPLIST_FILE = YES; - INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES; - INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; - INFOPLIST_KEY_UILaunchScreen_Generation = YES; - INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; - INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/Frameworks", - ); - MARKETING_VERSION = 1.0; - PRODUCT_BUNDLE_IDENTIFIER = com.veracode.DoubleTargetTest; - PRODUCT_NAME = "$(TARGET_NAME)"; - PROVISIONING_PROFILE_SPECIFIER = ""; - SWIFT_EMIT_LOC_STRINGS = YES; - SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2"; - }; - name = Release; - }; -/* End XCBuildConfiguration section */ - -/* Begin XCConfigurationList section */ - CE25173929C9C2C100A2E14B /* Build configuration list for PBXProject "DoubleTargetTest" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - CE25174A29C9C2C200A2E14B /* Debug */, - CE25174B29C9C2C200A2E14B /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - CE25174C29C9C2C200A2E14B /* Build configuration list for PBXNativeTarget "DoubleTargetTest" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - CE25174D29C9C2C200A2E14B /* Debug */, - CE25174E29C9C2C200A2E14B /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; -/* End XCConfigurationList section */ - }; - rootObject = CE25173629C9C2C100A2E14B /* Project object */; -} diff --git a/PBXProjParser/Tests/PBXProjParserTests/TestAssets/Projects/DoubleTargetTest/DoubleTargetTest.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/PBXProjParser/Tests/PBXProjParserTests/TestAssets/Projects/DoubleTargetTest/DoubleTargetTest.xcodeproj/project.xcworkspace/contents.xcworkspacedata deleted file mode 100644 index 919434a..0000000 --- a/PBXProjParser/Tests/PBXProjParserTests/TestAssets/Projects/DoubleTargetTest/DoubleTargetTest.xcodeproj/project.xcworkspace/contents.xcworkspacedata +++ /dev/null @@ -1,7 +0,0 @@ - - - - - diff --git a/PBXProjParser/Tests/PBXProjParserTests/TestAssets/Projects/DoubleTargetTest/DoubleTargetTest.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/PBXProjParser/Tests/PBXProjParserTests/TestAssets/Projects/DoubleTargetTest/DoubleTargetTest.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist deleted file mode 100644 index 18d9810..0000000 --- a/PBXProjParser/Tests/PBXProjParserTests/TestAssets/Projects/DoubleTargetTest/DoubleTargetTest.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist +++ /dev/null @@ -1,8 +0,0 @@ - - - - - IDEDidComputeMac32BitWarning - - - diff --git a/PBXProjParser/Tests/PBXProjParserTests/TestAssets/Projects/DoubleTargetTest/DoubleTargetTest.xcodeproj/xcshareddata/xcschemes/DoubleTargetTest.xcscheme b/PBXProjParser/Tests/PBXProjParserTests/TestAssets/Projects/DoubleTargetTest/DoubleTargetTest.xcodeproj/xcshareddata/xcschemes/DoubleTargetTest.xcscheme deleted file mode 100644 index 9c2834b..0000000 --- a/PBXProjParser/Tests/PBXProjParserTests/TestAssets/Projects/DoubleTargetTest/DoubleTargetTest.xcodeproj/xcshareddata/xcschemes/DoubleTargetTest.xcscheme +++ /dev/null @@ -1,78 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/PBXProjParser/Tests/PBXProjParserTests/TestAssets/Projects/DoubleTargetTest/DoubleTargetTest.xcworkspace/contents.xcworkspacedata b/PBXProjParser/Tests/PBXProjParserTests/TestAssets/Projects/DoubleTargetTest/DoubleTargetTest.xcworkspace/contents.xcworkspacedata deleted file mode 100644 index a81118b..0000000 --- a/PBXProjParser/Tests/PBXProjParserTests/TestAssets/Projects/DoubleTargetTest/DoubleTargetTest.xcworkspace/contents.xcworkspacedata +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - diff --git a/PBXProjParser/Tests/PBXProjParserTests/TestAssets/Projects/DoubleTargetTest/DoubleTargetTest.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/PBXProjParser/Tests/PBXProjParserTests/TestAssets/Projects/DoubleTargetTest/DoubleTargetTest.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist deleted file mode 100644 index 18d9810..0000000 --- a/PBXProjParser/Tests/PBXProjParserTests/TestAssets/Projects/DoubleTargetTest/DoubleTargetTest.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist +++ /dev/null @@ -1,8 +0,0 @@ - - - - - IDEDidComputeMac32BitWarning - - - diff --git a/PBXProjParser/Tests/PBXProjParserTests/TestAssets/Projects/DoubleTargetTest/Pods/Local Podspecs/MyBundle.podspec.json b/PBXProjParser/Tests/PBXProjParserTests/TestAssets/Projects/DoubleTargetTest/Pods/Local Podspecs/MyBundle.podspec.json deleted file mode 100644 index 0e86514..0000000 --- a/PBXProjParser/Tests/PBXProjParserTests/TestAssets/Projects/DoubleTargetTest/Pods/Local Podspecs/MyBundle.podspec.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - "name": "MyBundle", - "version": "0.1.0", - "summary": "A short description of MyBundle.", - "description": "TODO: Add long description of the pod here.", - "homepage": "https://github.com/thedderwick/MyBundle", - "license": { - "type": "MIT", - "file": "LICENSE" - }, - "authors": { - "thedderwick": "thedderwick@veracode.com" - }, - "source": { - "git": "https://github.com/thedderwick/MyBundle.git", - "tag": "0.1.0" - }, - "platforms": { - "ios": "10.0" - }, - "source_files": "MyBundle/Classes/**/*", - "resource_bundles": { - "MyBundle": [ - "MyBundle/Assets/*.png" - ] - } -} diff --git a/PBXProjParser/Tests/PBXProjParserTests/TestAssets/Projects/DoubleTargetTest/Pods/Manifest.lock b/PBXProjParser/Tests/PBXProjParserTests/TestAssets/Projects/DoubleTargetTest/Pods/Manifest.lock deleted file mode 100644 index 71ccc8f..0000000 --- a/PBXProjParser/Tests/PBXProjParserTests/TestAssets/Projects/DoubleTargetTest/Pods/Manifest.lock +++ /dev/null @@ -1,16 +0,0 @@ -PODS: - - MyBundle (0.1.0) - -DEPENDENCIES: - - MyBundle (from `/Users/thedderwick/dev/ios_tests/double_target_test/DoubleTargetTest/MyBundle`) - -EXTERNAL SOURCES: - MyBundle: - :path: "/Users/thedderwick/dev/ios_tests/double_target_test/DoubleTargetTest/MyBundle" - -SPEC CHECKSUMS: - MyBundle: e1f22e3445c35cc16854bc29f5159058eb370c1a - -PODFILE CHECKSUM: c56d07086d026876301d9d46d35896e3a7be8a1b - -COCOAPODS: 1.11.3 diff --git a/PBXProjParser/Tests/PBXProjParserTests/TestAssets/Projects/DoubleTargetTest/Pods/Pods.xcodeproj/project.pbxproj b/PBXProjParser/Tests/PBXProjParserTests/TestAssets/Projects/DoubleTargetTest/Pods/Pods.xcodeproj/project.pbxproj deleted file mode 100644 index 02766b2..0000000 --- a/PBXProjParser/Tests/PBXProjParserTests/TestAssets/Projects/DoubleTargetTest/Pods/Pods.xcodeproj/project.pbxproj +++ /dev/null @@ -1,734 +0,0 @@ -// !$*UTF8*$! -{ - archiveVersion = 1; - classes = { - }; - objectVersion = 56; - objects = { - -/* Begin PBXBuildFile section */ - 0505D039F580D8C1C4DA3135641F7BD3 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 73010CC983E3809BECEE5348DA1BB8C6 /* Foundation.framework */; }; - 33DD7B8459A3A39525345E644BEEF41A /* Pods-DoubleTargetTest-dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = F65DA5A28E59EDF453B5AAA1A8D8949E /* Pods-DoubleTargetTest-dummy.m */; }; - 44651C95EE6FC2391DD4F3A167F41A44 /* MyBundle-dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = 2B24F4C49C16832C3D1AF6AED1995658 /* MyBundle-dummy.m */; }; - 674BA68B5EAA5F9008004BF3F5C02EC5 /* MyBundle-umbrella.h in Headers */ = {isa = PBXBuildFile; fileRef = F3E212D006779EE067440F2CB7785A4B /* MyBundle-umbrella.h */; settings = {ATTRIBUTES = (Public, ); }; }; - A67798D7C4577858327517567612E2CD /* Pods-DoubleTargetTest-umbrella.h in Headers */ = {isa = PBXBuildFile; fileRef = A0B870A89DF602D8AAEA39600D8FDFDB /* Pods-DoubleTargetTest-umbrella.h */; settings = {ATTRIBUTES = (Public, ); }; }; - E25F7D067CF0E1D9B934CEECF2384CB4 /* ReplaceMe.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7F486712B4AA55E662E1657576D8133B /* ReplaceMe.swift */; }; - EE3C3BE914FAF168E38D719BEC2768D8 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 73010CC983E3809BECEE5348DA1BB8C6 /* Foundation.framework */; }; - FC2645FD54DC4BE00237174806868D1D /* MyBundle.bundle in Resources */ = {isa = PBXBuildFile; fileRef = D93F17E53BD7D266CAF6F17003C375A8 /* MyBundle.bundle */; }; -/* End PBXBuildFile section */ - -/* Begin PBXContainerItemProxy section */ - 34E01361C037C7F47C9C4A6FD583961C /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = BFDFE7DC352907FC980B868725387E98 /* Project object */; - proxyType = 1; - remoteGlobalIDString = 5E26517DF39E585BCF742D88F142F59B; - remoteInfo = "MyBundle-MyBundle"; - }; - A6F9652FCB6D63EEE2F28D6506461B50 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = BFDFE7DC352907FC980B868725387E98 /* Project object */; - proxyType = 1; - remoteGlobalIDString = D44D2D0674B19633F70576544F03D1F6; - remoteInfo = MyBundle; - }; -/* End PBXContainerItemProxy section */ - -/* Begin PBXFileReference section */ - 05D2F3E34E29E2AEEABAF781437B56B1 /* MyBundle.podspec */ = {isa = PBXFileReference; explicitFileType = text.script.ruby; includeInIndex = 1; indentWidth = 2; path = MyBundle.podspec; sourceTree = ""; tabWidth = 2; xcLanguageSpecificationIdentifier = xcode.lang.ruby; }; - 224DC3273E1C05AE11AF83EA85B8FD36 /* ResourceBundle-MyBundle-MyBundle-Info.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = "ResourceBundle-MyBundle-MyBundle-Info.plist"; sourceTree = ""; }; - 2B24F4C49C16832C3D1AF6AED1995658 /* MyBundle-dummy.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = "MyBundle-dummy.m"; sourceTree = ""; }; - 57786B8140E731F1F6FF148B154D46B9 /* README.md */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = ""; }; - 5FBA60238FECB2540F038F4AB9345480 /* Pods-DoubleTargetTest.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = "Pods-DoubleTargetTest.release.xcconfig"; sourceTree = ""; }; - 6A70FA6E6FCA926AAA416C9CB244CEDE /* LICENSE */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text; path = LICENSE; sourceTree = ""; }; - 73010CC983E3809BECEE5348DA1BB8C6 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS14.0.sdk/System/Library/Frameworks/Foundation.framework; sourceTree = DEVELOPER_DIR; }; - 736A5947EB69EC1CF1800C6EE8A9A4D8 /* MyBundle-Info.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = "MyBundle-Info.plist"; sourceTree = ""; }; - 7F486712B4AA55E662E1657576D8133B /* ReplaceMe.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ReplaceMe.swift; path = MyBundle/Classes/ReplaceMe.swift; sourceTree = ""; }; - 80BF02E1C33CF6CD2922D48629338A1B /* MyBundle-prefix.pch */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "MyBundle-prefix.pch"; sourceTree = ""; }; - 8138D938080D00037C41432BBDEF69FB /* Pods_DoubleTargetTest.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_DoubleTargetTest.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - 9D940727FF8FB9C785EB98E56350EF41 /* Podfile */ = {isa = PBXFileReference; explicitFileType = text.script.ruby; includeInIndex = 1; indentWidth = 2; name = Podfile; path = ../Podfile; sourceTree = SOURCE_ROOT; tabWidth = 2; xcLanguageSpecificationIdentifier = xcode.lang.ruby; }; - A0B870A89DF602D8AAEA39600D8FDFDB /* Pods-DoubleTargetTest-umbrella.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "Pods-DoubleTargetTest-umbrella.h"; sourceTree = ""; }; - C10B3C1ABAD75C99CAE60E63E4C5C226 /* MyBundle.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = MyBundle.debug.xcconfig; sourceTree = ""; }; - C72825197160F688488EC69B39E06FDC /* MyBundle.modulemap */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.module; path = MyBundle.modulemap; sourceTree = ""; }; - CA43AB7A1931E11A8E1FA62FC3D322F8 /* Pods-DoubleTargetTest.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = "Pods-DoubleTargetTest.debug.xcconfig"; sourceTree = ""; }; - D65C2A1ACEDD3BF60470333B525BD1DC /* Pods-DoubleTargetTest-acknowledgements.markdown */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text; path = "Pods-DoubleTargetTest-acknowledgements.markdown"; sourceTree = ""; }; - D93F17E53BD7D266CAF6F17003C375A8 /* MyBundle.bundle */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = MyBundle.bundle; sourceTree = BUILT_PRODUCTS_DIR; }; - D989667258FDEA181A33B4DCADEBFEF1 /* Pods-DoubleTargetTest-acknowledgements.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = "Pods-DoubleTargetTest-acknowledgements.plist"; sourceTree = ""; }; - DC48A4403E54985F3959D0D6E0045204 /* Pods-DoubleTargetTest.modulemap */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.module; path = "Pods-DoubleTargetTest.modulemap"; sourceTree = ""; }; - E511354FF29F23818915A2B3632C07CC /* Pods-DoubleTargetTest-Info.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = "Pods-DoubleTargetTest-Info.plist"; sourceTree = ""; }; - E8E0EC7D175B97912F7CBD8BFDF2B45E /* MyBundle.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = MyBundle.release.xcconfig; sourceTree = ""; }; - ED7431F391879458DE73243628EBC744 /* Pods-DoubleTargetTest-frameworks.sh */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.script.sh; path = "Pods-DoubleTargetTest-frameworks.sh"; sourceTree = ""; }; - F3E212D006779EE067440F2CB7785A4B /* MyBundle-umbrella.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "MyBundle-umbrella.h"; sourceTree = ""; }; - F65DA5A28E59EDF453B5AAA1A8D8949E /* Pods-DoubleTargetTest-dummy.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = "Pods-DoubleTargetTest-dummy.m"; sourceTree = ""; }; - FF65CA6FF03DFFFE7F7FFCF321A2EF48 /* MyBundle.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = MyBundle.framework; sourceTree = BUILT_PRODUCTS_DIR; }; -/* End PBXFileReference section */ - -/* Begin PBXFrameworksBuildPhase section */ - 2A12A09328EBBF7ECA09B932C9A7C0E8 /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; - C5E0F87B0CA6C796BA0B765746BF2AFB /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - 0505D039F580D8C1C4DA3135641F7BD3 /* Foundation.framework in Frameworks */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - EABB4E9BB1FC17E80981BA9096434936 /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - EE3C3BE914FAF168E38D719BEC2768D8 /* Foundation.framework in Frameworks */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXFrameworksBuildPhase section */ - -/* Begin PBXGroup section */ - 07FF93065E54DBA0271C6409D3DB7967 /* Targets Support Files */ = { - isa = PBXGroup; - children = ( - 1D5473B0E8035066E3E94EEDE7E5A8AE /* Pods-DoubleTargetTest */, - ); - name = "Targets Support Files"; - sourceTree = ""; - }; - 1D5473B0E8035066E3E94EEDE7E5A8AE /* Pods-DoubleTargetTest */ = { - isa = PBXGroup; - children = ( - DC48A4403E54985F3959D0D6E0045204 /* Pods-DoubleTargetTest.modulemap */, - D65C2A1ACEDD3BF60470333B525BD1DC /* Pods-DoubleTargetTest-acknowledgements.markdown */, - D989667258FDEA181A33B4DCADEBFEF1 /* Pods-DoubleTargetTest-acknowledgements.plist */, - F65DA5A28E59EDF453B5AAA1A8D8949E /* Pods-DoubleTargetTest-dummy.m */, - ED7431F391879458DE73243628EBC744 /* Pods-DoubleTargetTest-frameworks.sh */, - E511354FF29F23818915A2B3632C07CC /* Pods-DoubleTargetTest-Info.plist */, - A0B870A89DF602D8AAEA39600D8FDFDB /* Pods-DoubleTargetTest-umbrella.h */, - CA43AB7A1931E11A8E1FA62FC3D322F8 /* Pods-DoubleTargetTest.debug.xcconfig */, - 5FBA60238FECB2540F038F4AB9345480 /* Pods-DoubleTargetTest.release.xcconfig */, - ); - name = "Pods-DoubleTargetTest"; - path = "Target Support Files/Pods-DoubleTargetTest"; - sourceTree = ""; - }; - 20FDC45F1DD3EF707467BF569B570407 /* Pod */ = { - isa = PBXGroup; - children = ( - 6A70FA6E6FCA926AAA416C9CB244CEDE /* LICENSE */, - 05D2F3E34E29E2AEEABAF781437B56B1 /* MyBundle.podspec */, - 57786B8140E731F1F6FF148B154D46B9 /* README.md */, - ); - name = Pod; - sourceTree = ""; - }; - 2DA60530B2F5B4007868F26C4A31DCB8 /* Support Files */ = { - isa = PBXGroup; - children = ( - C72825197160F688488EC69B39E06FDC /* MyBundle.modulemap */, - 2B24F4C49C16832C3D1AF6AED1995658 /* MyBundle-dummy.m */, - 736A5947EB69EC1CF1800C6EE8A9A4D8 /* MyBundle-Info.plist */, - 80BF02E1C33CF6CD2922D48629338A1B /* MyBundle-prefix.pch */, - F3E212D006779EE067440F2CB7785A4B /* MyBundle-umbrella.h */, - C10B3C1ABAD75C99CAE60E63E4C5C226 /* MyBundle.debug.xcconfig */, - E8E0EC7D175B97912F7CBD8BFDF2B45E /* MyBundle.release.xcconfig */, - 224DC3273E1C05AE11AF83EA85B8FD36 /* ResourceBundle-MyBundle-MyBundle-Info.plist */, - ); - name = "Support Files"; - path = "../Pods/Target Support Files/MyBundle"; - sourceTree = ""; - }; - 530F2AFE538911ABCDFFFAD240B6C43F /* Development Pods */ = { - isa = PBXGroup; - children = ( - 779A4C1FC7F0153F4E499ED3B1262F9D /* MyBundle */, - ); - name = "Development Pods"; - sourceTree = ""; - }; - 578452D2E740E91742655AC8F1636D1F /* iOS */ = { - isa = PBXGroup; - children = ( - 73010CC983E3809BECEE5348DA1BB8C6 /* Foundation.framework */, - ); - name = iOS; - sourceTree = ""; - }; - 779A4C1FC7F0153F4E499ED3B1262F9D /* MyBundle */ = { - isa = PBXGroup; - children = ( - 7F486712B4AA55E662E1657576D8133B /* ReplaceMe.swift */, - 20FDC45F1DD3EF707467BF569B570407 /* Pod */, - 2DA60530B2F5B4007868F26C4A31DCB8 /* Support Files */, - ); - name = MyBundle; - path = /Users/thedderwick/dev/ios_tests/double_target_test/DoubleTargetTest/MyBundle; - sourceTree = ""; - }; - 8820BDE64C1284039B9392B149BBF3F1 /* Products */ = { - isa = PBXGroup; - children = ( - FF65CA6FF03DFFFE7F7FFCF321A2EF48 /* MyBundle.framework */, - D93F17E53BD7D266CAF6F17003C375A8 /* MyBundle.bundle */, - 8138D938080D00037C41432BBDEF69FB /* Pods_DoubleTargetTest.framework */, - ); - name = Products; - sourceTree = ""; - }; - CF1408CF629C7361332E53B88F7BD30C = { - isa = PBXGroup; - children = ( - 9D940727FF8FB9C785EB98E56350EF41 /* Podfile */, - 530F2AFE538911ABCDFFFAD240B6C43F /* Development Pods */, - D210D550F4EA176C3123ED886F8F87F5 /* Frameworks */, - 8820BDE64C1284039B9392B149BBF3F1 /* Products */, - 07FF93065E54DBA0271C6409D3DB7967 /* Targets Support Files */, - ); - sourceTree = ""; - }; - D210D550F4EA176C3123ED886F8F87F5 /* Frameworks */ = { - isa = PBXGroup; - children = ( - 578452D2E740E91742655AC8F1636D1F /* iOS */, - ); - name = Frameworks; - sourceTree = ""; - }; -/* End PBXGroup section */ - -/* Begin PBXHeadersBuildPhase section */ - DFDB214017CAB64FA5759DEEE88C6482 /* Headers */ = { - isa = PBXHeadersBuildPhase; - buildActionMask = 2147483647; - files = ( - 674BA68B5EAA5F9008004BF3F5C02EC5 /* MyBundle-umbrella.h in Headers */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - EF90799616133DD7D69BDFBBA5CEA166 /* Headers */ = { - isa = PBXHeadersBuildPhase; - buildActionMask = 2147483647; - files = ( - A67798D7C4577858327517567612E2CD /* Pods-DoubleTargetTest-umbrella.h in Headers */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXHeadersBuildPhase section */ - -/* Begin PBXNativeTarget section */ - 2C5F8EFB5430A4211A0B84AAF4F13F9C /* Pods-DoubleTargetTest */ = { - isa = PBXNativeTarget; - buildConfigurationList = 4BA9A3509482DBEAB6E2815BA49CFF38 /* Build configuration list for PBXNativeTarget "Pods-DoubleTargetTest" */; - buildPhases = ( - EF90799616133DD7D69BDFBBA5CEA166 /* Headers */, - B3CB7EA40906C61E205292A9A351D3CB /* Sources */, - EABB4E9BB1FC17E80981BA9096434936 /* Frameworks */, - 87E5F63D5CCB13C8680E20FD9006DE94 /* Resources */, - ); - buildRules = ( - ); - dependencies = ( - 72A1629C1F4E73D2E84B18F70BC27E45 /* PBXTargetDependency */, - ); - name = "Pods-DoubleTargetTest"; - productName = Pods_DoubleTargetTest; - productReference = 8138D938080D00037C41432BBDEF69FB /* Pods_DoubleTargetTest.framework */; - productType = "com.apple.product-type.framework"; - }; - 5E26517DF39E585BCF742D88F142F59B /* MyBundle-MyBundle */ = { - isa = PBXNativeTarget; - buildConfigurationList = 653128552AEA47E651B38399510A6921 /* Build configuration list for PBXNativeTarget "MyBundle-MyBundle" */; - buildPhases = ( - 8E15891098856B42AEF03BBCF6C6CF8B /* Sources */, - 2A12A09328EBBF7ECA09B932C9A7C0E8 /* Frameworks */, - 51CCDFF654B64A04999C81E4263E6D99 /* Resources */, - ); - buildRules = ( - ); - dependencies = ( - ); - name = "MyBundle-MyBundle"; - productName = MyBundle; - productReference = D93F17E53BD7D266CAF6F17003C375A8 /* MyBundle.bundle */; - productType = "com.apple.product-type.bundle"; - }; - D44D2D0674B19633F70576544F03D1F6 /* MyBundle */ = { - isa = PBXNativeTarget; - buildConfigurationList = 2F9F1F2725B9CBC6E4E1D2BC535F21BB /* Build configuration list for PBXNativeTarget "MyBundle" */; - buildPhases = ( - DFDB214017CAB64FA5759DEEE88C6482 /* Headers */, - C44C0BBB4071B406F57E947248DC4312 /* Sources */, - C5E0F87B0CA6C796BA0B765746BF2AFB /* Frameworks */, - 4CB90525191925AD5A3E2E39B45FECCD /* Resources */, - ); - buildRules = ( - ); - dependencies = ( - C624302602CF69389AEA79A23EA0372D /* PBXTargetDependency */, - ); - name = MyBundle; - productName = MyBundle; - productReference = FF65CA6FF03DFFFE7F7FFCF321A2EF48 /* MyBundle.framework */; - productType = "com.apple.product-type.framework"; - }; -/* End PBXNativeTarget section */ - -/* Begin PBXProject section */ - BFDFE7DC352907FC980B868725387E98 /* Project object */ = { - isa = PBXProject; - attributes = { - LastSwiftUpdateCheck = 1300; - LastUpgradeCheck = 1300; - }; - buildConfigurationList = 4821239608C13582E20E6DA73FD5F1F9 /* Build configuration list for PBXProject "Pods" */; - compatibilityVersion = "Xcode 14.0"; - developmentRegion = en; - hasScannedForEncodings = 0; - knownRegions = ( - Base, - en, - ); - mainGroup = CF1408CF629C7361332E53B88F7BD30C; - productRefGroup = 8820BDE64C1284039B9392B149BBF3F1 /* Products */; - projectDirPath = ""; - projectRoot = ""; - targets = ( - D44D2D0674B19633F70576544F03D1F6 /* MyBundle */, - 5E26517DF39E585BCF742D88F142F59B /* MyBundle-MyBundle */, - 2C5F8EFB5430A4211A0B84AAF4F13F9C /* Pods-DoubleTargetTest */, - ); - }; -/* End PBXProject section */ - -/* Begin PBXResourcesBuildPhase section */ - 4CB90525191925AD5A3E2E39B45FECCD /* Resources */ = { - isa = PBXResourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - FC2645FD54DC4BE00237174806868D1D /* MyBundle.bundle in Resources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 51CCDFF654B64A04999C81E4263E6D99 /* Resources */ = { - isa = PBXResourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 87E5F63D5CCB13C8680E20FD9006DE94 /* Resources */ = { - isa = PBXResourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXResourcesBuildPhase section */ - -/* Begin PBXSourcesBuildPhase section */ - 8E15891098856B42AEF03BBCF6C6CF8B /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; - B3CB7EA40906C61E205292A9A351D3CB /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - 33DD7B8459A3A39525345E644BEEF41A /* Pods-DoubleTargetTest-dummy.m in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - C44C0BBB4071B406F57E947248DC4312 /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - 44651C95EE6FC2391DD4F3A167F41A44 /* MyBundle-dummy.m in Sources */, - E25F7D067CF0E1D9B934CEECF2384CB4 /* ReplaceMe.swift in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXSourcesBuildPhase section */ - -/* Begin PBXTargetDependency section */ - 72A1629C1F4E73D2E84B18F70BC27E45 /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - name = MyBundle; - target = D44D2D0674B19633F70576544F03D1F6 /* MyBundle */; - targetProxy = A6F9652FCB6D63EEE2F28D6506461B50 /* PBXContainerItemProxy */; - }; - C624302602CF69389AEA79A23EA0372D /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - name = "MyBundle-MyBundle"; - target = 5E26517DF39E585BCF742D88F142F59B /* MyBundle-MyBundle */; - targetProxy = 34E01361C037C7F47C9C4A6FD583961C /* PBXContainerItemProxy */; - }; -/* End PBXTargetDependency section */ - -/* Begin XCBuildConfiguration section */ - 20B1B8D2F3D27105B0D81D14421C20CA /* Debug */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = C10B3C1ABAD75C99CAE60E63E4C5C226 /* MyBundle.debug.xcconfig */; - buildSettings = { - CODE_SIGN_IDENTITY = ""; - CODE_SIGN_STYLE = Manual; - CONFIGURATION_BUILD_DIR = "$(BUILD_DIR)/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)/MyBundle"; - DEVELOPMENT_TEAM = ""; - IBSC_MODULE = MyBundle; - INFOPLIST_FILE = "Target Support Files/MyBundle/ResourceBundle-MyBundle-MyBundle-Info.plist"; - IPHONEOS_DEPLOYMENT_TARGET = 10.0; - PRODUCT_NAME = MyBundle; - PROVISIONING_PROFILE_SPECIFIER = ""; - SDKROOT = iphoneos; - SKIP_INSTALL = YES; - TARGETED_DEVICE_FAMILY = "1,2"; - WRAPPER_EXTENSION = bundle; - }; - name = Debug; - }; - 446BA1B9A1F1F2F95D04E5F82EB1B0F4 /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; - CLANG_ANALYZER_NONNULL = YES; - CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; - CLANG_CXX_LIBRARY = "libc++"; - CLANG_ENABLE_MODULES = YES; - CLANG_ENABLE_OBJC_ARC = YES; - CLANG_ENABLE_OBJC_WEAK = YES; - CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; - CLANG_WARN_BOOL_CONVERSION = YES; - CLANG_WARN_COMMA = YES; - CLANG_WARN_CONSTANT_CONVERSION = YES; - CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; - CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; - CLANG_WARN_DOCUMENTATION_COMMENTS = YES; - CLANG_WARN_EMPTY_BODY = YES; - CLANG_WARN_ENUM_CONVERSION = YES; - CLANG_WARN_INFINITE_RECURSION = YES; - CLANG_WARN_INT_CONVERSION = YES; - CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; - CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; - CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; - CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; - CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; - CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; - CLANG_WARN_STRICT_PROTOTYPES = YES; - CLANG_WARN_SUSPICIOUS_MOVE = YES; - CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; - CLANG_WARN_UNREACHABLE_CODE = YES; - CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; - COPY_PHASE_STRIP = NO; - DEBUG_INFORMATION_FORMAT = dwarf; - ENABLE_STRICT_OBJC_MSGSEND = YES; - ENABLE_TESTABILITY = YES; - GCC_C_LANGUAGE_STANDARD = gnu11; - GCC_DYNAMIC_NO_PIC = NO; - GCC_NO_COMMON_BLOCKS = YES; - GCC_OPTIMIZATION_LEVEL = 0; - GCC_PREPROCESSOR_DEFINITIONS = ( - "POD_CONFIGURATION_DEBUG=1", - "DEBUG=1", - "$(inherited)", - ); - GCC_WARN_64_TO_32_BIT_CONVERSION = YES; - GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; - GCC_WARN_UNDECLARED_SELECTOR = YES; - GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; - GCC_WARN_UNUSED_FUNCTION = YES; - GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 16.2; - MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; - MTL_FAST_MATH = YES; - ONLY_ACTIVE_ARCH = YES; - PRODUCT_NAME = "$(TARGET_NAME)"; - STRIP_INSTALLED_PRODUCT = NO; - SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; - SWIFT_OPTIMIZATION_LEVEL = "-Onone"; - SWIFT_VERSION = 5.0; - SYMROOT = "${SRCROOT}/../build"; - }; - name = Debug; - }; - 5D47E2EFAC6FAC81A782D9BFD8736F53 /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; - CLANG_ANALYZER_NONNULL = YES; - CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; - CLANG_CXX_LIBRARY = "libc++"; - CLANG_ENABLE_MODULES = YES; - CLANG_ENABLE_OBJC_ARC = YES; - CLANG_ENABLE_OBJC_WEAK = YES; - CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; - CLANG_WARN_BOOL_CONVERSION = YES; - CLANG_WARN_COMMA = YES; - CLANG_WARN_CONSTANT_CONVERSION = YES; - CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; - CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; - CLANG_WARN_DOCUMENTATION_COMMENTS = YES; - CLANG_WARN_EMPTY_BODY = YES; - CLANG_WARN_ENUM_CONVERSION = YES; - CLANG_WARN_INFINITE_RECURSION = YES; - CLANG_WARN_INT_CONVERSION = YES; - CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; - CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; - CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; - CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; - CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; - CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; - CLANG_WARN_STRICT_PROTOTYPES = YES; - CLANG_WARN_SUSPICIOUS_MOVE = YES; - CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; - CLANG_WARN_UNREACHABLE_CODE = YES; - CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; - COPY_PHASE_STRIP = NO; - DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; - ENABLE_NS_ASSERTIONS = NO; - ENABLE_STRICT_OBJC_MSGSEND = YES; - GCC_C_LANGUAGE_STANDARD = gnu11; - GCC_NO_COMMON_BLOCKS = YES; - GCC_PREPROCESSOR_DEFINITIONS = ( - "POD_CONFIGURATION_RELEASE=1", - "$(inherited)", - ); - GCC_WARN_64_TO_32_BIT_CONVERSION = YES; - GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; - GCC_WARN_UNDECLARED_SELECTOR = YES; - GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; - GCC_WARN_UNUSED_FUNCTION = YES; - GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 16.2; - MTL_ENABLE_DEBUG_INFO = NO; - MTL_FAST_MATH = YES; - PRODUCT_NAME = "$(TARGET_NAME)"; - STRIP_INSTALLED_PRODUCT = NO; - SWIFT_COMPILATION_MODE = wholemodule; - SWIFT_OPTIMIZATION_LEVEL = "-O"; - SWIFT_VERSION = 5.0; - SYMROOT = "${SRCROOT}/../build"; - }; - name = Release; - }; - 962BDC6723AD0C2121208533EE1026E7 /* Debug */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = CA43AB7A1931E11A8E1FA62FC3D322F8 /* Pods-DoubleTargetTest.debug.xcconfig */; - buildSettings = { - ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = NO; - CLANG_ENABLE_OBJC_WEAK = NO; - CODE_SIGN_IDENTITY = ""; - "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; - "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; - "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; - CURRENT_PROJECT_VERSION = 1; - DEFINES_MODULE = YES; - DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 1; - DYLIB_INSTALL_NAME_BASE = "@rpath"; - INFOPLIST_FILE = "Target Support Files/Pods-DoubleTargetTest/Pods-DoubleTargetTest-Info.plist"; - INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; - IPHONEOS_DEPLOYMENT_TARGET = 16.2; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/Frameworks", - "@loader_path/Frameworks", - ); - MACH_O_TYPE = staticlib; - MODULEMAP_FILE = "Target Support Files/Pods-DoubleTargetTest/Pods-DoubleTargetTest.modulemap"; - OTHER_LDFLAGS = ""; - OTHER_LIBTOOLFLAGS = ""; - PODS_ROOT = "$(SRCROOT)"; - PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.${PRODUCT_NAME:rfc1034identifier}"; - PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; - SDKROOT = iphoneos; - SKIP_INSTALL = YES; - TARGETED_DEVICE_FAMILY = "1,2"; - VERSIONING_SYSTEM = "apple-generic"; - VERSION_INFO_PREFIX = ""; - }; - name = Debug; - }; - 9C1B3E43E825EB8D4540CCEEDD20FEFC /* Release */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = 5FBA60238FECB2540F038F4AB9345480 /* Pods-DoubleTargetTest.release.xcconfig */; - buildSettings = { - ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = NO; - CLANG_ENABLE_OBJC_WEAK = NO; - CODE_SIGN_IDENTITY = ""; - "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; - "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; - "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; - CURRENT_PROJECT_VERSION = 1; - DEFINES_MODULE = YES; - DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 1; - DYLIB_INSTALL_NAME_BASE = "@rpath"; - INFOPLIST_FILE = "Target Support Files/Pods-DoubleTargetTest/Pods-DoubleTargetTest-Info.plist"; - INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; - IPHONEOS_DEPLOYMENT_TARGET = 16.2; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/Frameworks", - "@loader_path/Frameworks", - ); - MACH_O_TYPE = staticlib; - MODULEMAP_FILE = "Target Support Files/Pods-DoubleTargetTest/Pods-DoubleTargetTest.modulemap"; - OTHER_LDFLAGS = ""; - OTHER_LIBTOOLFLAGS = ""; - PODS_ROOT = "$(SRCROOT)"; - PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.${PRODUCT_NAME:rfc1034identifier}"; - PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; - SDKROOT = iphoneos; - SKIP_INSTALL = YES; - TARGETED_DEVICE_FAMILY = "1,2"; - VALIDATE_PRODUCT = YES; - VERSIONING_SYSTEM = "apple-generic"; - VERSION_INFO_PREFIX = ""; - }; - name = Release; - }; - AF63CE00DDA4FBC37E5031858C625514 /* Release */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = E8E0EC7D175B97912F7CBD8BFDF2B45E /* MyBundle.release.xcconfig */; - buildSettings = { - CODE_SIGN_IDENTITY = ""; - CODE_SIGN_STYLE = Manual; - CONFIGURATION_BUILD_DIR = "$(BUILD_DIR)/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)/MyBundle"; - DEVELOPMENT_TEAM = ""; - IBSC_MODULE = MyBundle; - INFOPLIST_FILE = "Target Support Files/MyBundle/ResourceBundle-MyBundle-MyBundle-Info.plist"; - IPHONEOS_DEPLOYMENT_TARGET = 10.0; - PRODUCT_NAME = MyBundle; - PROVISIONING_PROFILE_SPECIFIER = ""; - SDKROOT = iphoneos; - SKIP_INSTALL = YES; - TARGETED_DEVICE_FAMILY = "1,2"; - WRAPPER_EXTENSION = bundle; - }; - name = Release; - }; - B8B137A9E60B7D7D73FFD3D06FC029D7 /* Release */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = E8E0EC7D175B97912F7CBD8BFDF2B45E /* MyBundle.release.xcconfig */; - buildSettings = { - CLANG_ENABLE_OBJC_WEAK = NO; - CODE_SIGN_IDENTITY = ""; - "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; - "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; - "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; - CURRENT_PROJECT_VERSION = 1; - DEFINES_MODULE = YES; - DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 1; - DYLIB_INSTALL_NAME_BASE = "@rpath"; - GCC_PREFIX_HEADER = "Target Support Files/MyBundle/MyBundle-prefix.pch"; - INFOPLIST_FILE = "Target Support Files/MyBundle/MyBundle-Info.plist"; - INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; - IPHONEOS_DEPLOYMENT_TARGET = 10.0; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/Frameworks", - "@loader_path/Frameworks", - ); - MODULEMAP_FILE = "Target Support Files/MyBundle/MyBundle.modulemap"; - PRODUCT_MODULE_NAME = MyBundle; - PRODUCT_NAME = MyBundle; - SDKROOT = iphoneos; - SKIP_INSTALL = YES; - SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) "; - SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2"; - VALIDATE_PRODUCT = YES; - VERSIONING_SYSTEM = "apple-generic"; - VERSION_INFO_PREFIX = ""; - }; - name = Release; - }; - E1E03382DD524E8939F7CC62A62FDE3A /* Debug */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = C10B3C1ABAD75C99CAE60E63E4C5C226 /* MyBundle.debug.xcconfig */; - buildSettings = { - CLANG_ENABLE_OBJC_WEAK = NO; - CODE_SIGN_IDENTITY = ""; - "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; - "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; - "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; - CURRENT_PROJECT_VERSION = 1; - DEFINES_MODULE = YES; - DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 1; - DYLIB_INSTALL_NAME_BASE = "@rpath"; - GCC_PREFIX_HEADER = "Target Support Files/MyBundle/MyBundle-prefix.pch"; - INFOPLIST_FILE = "Target Support Files/MyBundle/MyBundle-Info.plist"; - INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; - IPHONEOS_DEPLOYMENT_TARGET = 10.0; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/Frameworks", - "@loader_path/Frameworks", - ); - MODULEMAP_FILE = "Target Support Files/MyBundle/MyBundle.modulemap"; - PRODUCT_MODULE_NAME = MyBundle; - PRODUCT_NAME = MyBundle; - SDKROOT = iphoneos; - SKIP_INSTALL = YES; - SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) "; - SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2"; - VERSIONING_SYSTEM = "apple-generic"; - VERSION_INFO_PREFIX = ""; - }; - name = Debug; - }; -/* End XCBuildConfiguration section */ - -/* Begin XCConfigurationList section */ - 2F9F1F2725B9CBC6E4E1D2BC535F21BB /* Build configuration list for PBXNativeTarget "MyBundle" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - E1E03382DD524E8939F7CC62A62FDE3A /* Debug */, - B8B137A9E60B7D7D73FFD3D06FC029D7 /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - 4821239608C13582E20E6DA73FD5F1F9 /* Build configuration list for PBXProject "Pods" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 446BA1B9A1F1F2F95D04E5F82EB1B0F4 /* Debug */, - 5D47E2EFAC6FAC81A782D9BFD8736F53 /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - 4BA9A3509482DBEAB6E2815BA49CFF38 /* Build configuration list for PBXNativeTarget "Pods-DoubleTargetTest" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 962BDC6723AD0C2121208533EE1026E7 /* Debug */, - 9C1B3E43E825EB8D4540CCEEDD20FEFC /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - 653128552AEA47E651B38399510A6921 /* Build configuration list for PBXNativeTarget "MyBundle-MyBundle" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 20B1B8D2F3D27105B0D81D14421C20CA /* Debug */, - AF63CE00DDA4FBC37E5031858C625514 /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; -/* End XCConfigurationList section */ - }; - rootObject = BFDFE7DC352907FC980B868725387E98 /* Project object */; -} diff --git a/PBXProjParser/Tests/PBXProjParserTests/TestAssets/Projects/DoubleTargetTest/Pods/Target Support Files/MyBundle/MyBundle-Info.plist b/PBXProjParser/Tests/PBXProjParserTests/TestAssets/Projects/DoubleTargetTest/Pods/Target Support Files/MyBundle/MyBundle-Info.plist deleted file mode 100644 index 161a9d3..0000000 --- a/PBXProjParser/Tests/PBXProjParserTests/TestAssets/Projects/DoubleTargetTest/Pods/Target Support Files/MyBundle/MyBundle-Info.plist +++ /dev/null @@ -1,26 +0,0 @@ - - - - - CFBundleDevelopmentRegion - en - CFBundleExecutable - ${EXECUTABLE_NAME} - CFBundleIdentifier - ${PRODUCT_BUNDLE_IDENTIFIER} - CFBundleInfoDictionaryVersion - 6.0 - CFBundleName - ${PRODUCT_NAME} - CFBundlePackageType - FMWK - CFBundleShortVersionString - 0.1.0 - CFBundleSignature - ???? - CFBundleVersion - ${CURRENT_PROJECT_VERSION} - NSPrincipalClass - - - diff --git a/PBXProjParser/Tests/PBXProjParserTests/TestAssets/Projects/DoubleTargetTest/Pods/Target Support Files/MyBundle/MyBundle-dummy.m b/PBXProjParser/Tests/PBXProjParserTests/TestAssets/Projects/DoubleTargetTest/Pods/Target Support Files/MyBundle/MyBundle-dummy.m deleted file mode 100644 index e24399a..0000000 --- a/PBXProjParser/Tests/PBXProjParserTests/TestAssets/Projects/DoubleTargetTest/Pods/Target Support Files/MyBundle/MyBundle-dummy.m +++ /dev/null @@ -1,5 +0,0 @@ -#import -@interface PodsDummy_MyBundle : NSObject -@end -@implementation PodsDummy_MyBundle -@end diff --git a/PBXProjParser/Tests/PBXProjParserTests/TestAssets/Projects/DoubleTargetTest/Pods/Target Support Files/MyBundle/MyBundle-prefix.pch b/PBXProjParser/Tests/PBXProjParserTests/TestAssets/Projects/DoubleTargetTest/Pods/Target Support Files/MyBundle/MyBundle-prefix.pch deleted file mode 100644 index beb2a24..0000000 --- a/PBXProjParser/Tests/PBXProjParserTests/TestAssets/Projects/DoubleTargetTest/Pods/Target Support Files/MyBundle/MyBundle-prefix.pch +++ /dev/null @@ -1,12 +0,0 @@ -#ifdef __OBJC__ -#import -#else -#ifndef FOUNDATION_EXPORT -#if defined(__cplusplus) -#define FOUNDATION_EXPORT extern "C" -#else -#define FOUNDATION_EXPORT extern -#endif -#endif -#endif - diff --git a/PBXProjParser/Tests/PBXProjParserTests/TestAssets/Projects/DoubleTargetTest/Pods/Target Support Files/MyBundle/MyBundle-umbrella.h b/PBXProjParser/Tests/PBXProjParserTests/TestAssets/Projects/DoubleTargetTest/Pods/Target Support Files/MyBundle/MyBundle-umbrella.h deleted file mode 100644 index f846ef6..0000000 --- a/PBXProjParser/Tests/PBXProjParserTests/TestAssets/Projects/DoubleTargetTest/Pods/Target Support Files/MyBundle/MyBundle-umbrella.h +++ /dev/null @@ -1,16 +0,0 @@ -#ifdef __OBJC__ -#import -#else -#ifndef FOUNDATION_EXPORT -#if defined(__cplusplus) -#define FOUNDATION_EXPORT extern "C" -#else -#define FOUNDATION_EXPORT extern -#endif -#endif -#endif - - -FOUNDATION_EXPORT double MyBundleVersionNumber; -FOUNDATION_EXPORT const unsigned char MyBundleVersionString[]; - diff --git a/PBXProjParser/Tests/PBXProjParserTests/TestAssets/Projects/DoubleTargetTest/Pods/Target Support Files/MyBundle/MyBundle.debug.xcconfig b/PBXProjParser/Tests/PBXProjParserTests/TestAssets/Projects/DoubleTargetTest/Pods/Target Support Files/MyBundle/MyBundle.debug.xcconfig deleted file mode 100644 index 8a95b1f..0000000 --- a/PBXProjParser/Tests/PBXProjParserTests/TestAssets/Projects/DoubleTargetTest/Pods/Target Support Files/MyBundle/MyBundle.debug.xcconfig +++ /dev/null @@ -1,13 +0,0 @@ -CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO -CONFIGURATION_BUILD_DIR = ${PODS_CONFIGURATION_BUILD_DIR}/MyBundle -GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 -LIBRARY_SEARCH_PATHS = $(inherited) "${DT_TOOLCHAIN_DIR}/usr/lib/swift/${PLATFORM_NAME}" /usr/lib/swift -OTHER_SWIFT_FLAGS = $(inherited) -D COCOAPODS -PODS_BUILD_DIR = ${BUILD_DIR} -PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) -PODS_ROOT = ${SRCROOT} -PODS_TARGET_SRCROOT = ${PODS_ROOT}/../MyBundle -PODS_XCFRAMEWORKS_BUILD_DIR = $(PODS_CONFIGURATION_BUILD_DIR)/XCFrameworkIntermediates -PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier} -SKIP_INSTALL = YES -USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES diff --git a/PBXProjParser/Tests/PBXProjParserTests/TestAssets/Projects/DoubleTargetTest/Pods/Target Support Files/MyBundle/MyBundle.modulemap b/PBXProjParser/Tests/PBXProjParserTests/TestAssets/Projects/DoubleTargetTest/Pods/Target Support Files/MyBundle/MyBundle.modulemap deleted file mode 100644 index 23d5f54..0000000 --- a/PBXProjParser/Tests/PBXProjParserTests/TestAssets/Projects/DoubleTargetTest/Pods/Target Support Files/MyBundle/MyBundle.modulemap +++ /dev/null @@ -1,6 +0,0 @@ -framework module MyBundle { - umbrella header "MyBundle-umbrella.h" - - export * - module * { export * } -} diff --git a/PBXProjParser/Tests/PBXProjParserTests/TestAssets/Projects/DoubleTargetTest/Pods/Target Support Files/MyBundle/MyBundle.release.xcconfig b/PBXProjParser/Tests/PBXProjParserTests/TestAssets/Projects/DoubleTargetTest/Pods/Target Support Files/MyBundle/MyBundle.release.xcconfig deleted file mode 100644 index 8a95b1f..0000000 --- a/PBXProjParser/Tests/PBXProjParserTests/TestAssets/Projects/DoubleTargetTest/Pods/Target Support Files/MyBundle/MyBundle.release.xcconfig +++ /dev/null @@ -1,13 +0,0 @@ -CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO -CONFIGURATION_BUILD_DIR = ${PODS_CONFIGURATION_BUILD_DIR}/MyBundle -GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 -LIBRARY_SEARCH_PATHS = $(inherited) "${DT_TOOLCHAIN_DIR}/usr/lib/swift/${PLATFORM_NAME}" /usr/lib/swift -OTHER_SWIFT_FLAGS = $(inherited) -D COCOAPODS -PODS_BUILD_DIR = ${BUILD_DIR} -PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) -PODS_ROOT = ${SRCROOT} -PODS_TARGET_SRCROOT = ${PODS_ROOT}/../MyBundle -PODS_XCFRAMEWORKS_BUILD_DIR = $(PODS_CONFIGURATION_BUILD_DIR)/XCFrameworkIntermediates -PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier} -SKIP_INSTALL = YES -USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES diff --git a/PBXProjParser/Tests/PBXProjParserTests/TestAssets/Projects/DoubleTargetTest/Pods/Target Support Files/MyBundle/ResourceBundle-MyBundle-MyBundle-Info.plist b/PBXProjParser/Tests/PBXProjParserTests/TestAssets/Projects/DoubleTargetTest/Pods/Target Support Files/MyBundle/ResourceBundle-MyBundle-MyBundle-Info.plist deleted file mode 100644 index 99a40ff..0000000 --- a/PBXProjParser/Tests/PBXProjParserTests/TestAssets/Projects/DoubleTargetTest/Pods/Target Support Files/MyBundle/ResourceBundle-MyBundle-MyBundle-Info.plist +++ /dev/null @@ -1,24 +0,0 @@ - - - - - CFBundleDevelopmentRegion - en - CFBundleIdentifier - ${PRODUCT_BUNDLE_IDENTIFIER} - CFBundleInfoDictionaryVersion - 6.0 - CFBundleName - ${PRODUCT_NAME} - CFBundlePackageType - BNDL - CFBundleShortVersionString - 0.1.0 - CFBundleSignature - ???? - CFBundleVersion - 1 - NSPrincipalClass - - - diff --git a/PBXProjParser/Tests/PBXProjParserTests/TestAssets/Projects/DoubleTargetTest/Pods/Target Support Files/Pods-DoubleTargetTest/Pods-DoubleTargetTest-Info.plist b/PBXProjParser/Tests/PBXProjParserTests/TestAssets/Projects/DoubleTargetTest/Pods/Target Support Files/Pods-DoubleTargetTest/Pods-DoubleTargetTest-Info.plist deleted file mode 100644 index 2243fe6..0000000 --- a/PBXProjParser/Tests/PBXProjParserTests/TestAssets/Projects/DoubleTargetTest/Pods/Target Support Files/Pods-DoubleTargetTest/Pods-DoubleTargetTest-Info.plist +++ /dev/null @@ -1,26 +0,0 @@ - - - - - CFBundleDevelopmentRegion - en - CFBundleExecutable - ${EXECUTABLE_NAME} - CFBundleIdentifier - ${PRODUCT_BUNDLE_IDENTIFIER} - CFBundleInfoDictionaryVersion - 6.0 - CFBundleName - ${PRODUCT_NAME} - CFBundlePackageType - FMWK - CFBundleShortVersionString - 1.0.0 - CFBundleSignature - ???? - CFBundleVersion - ${CURRENT_PROJECT_VERSION} - NSPrincipalClass - - - diff --git a/PBXProjParser/Tests/PBXProjParserTests/TestAssets/Projects/DoubleTargetTest/Pods/Target Support Files/Pods-DoubleTargetTest/Pods-DoubleTargetTest-acknowledgements.markdown b/PBXProjParser/Tests/PBXProjParserTests/TestAssets/Projects/DoubleTargetTest/Pods/Target Support Files/Pods-DoubleTargetTest/Pods-DoubleTargetTest-acknowledgements.markdown deleted file mode 100644 index 8bb471a..0000000 --- a/PBXProjParser/Tests/PBXProjParserTests/TestAssets/Projects/DoubleTargetTest/Pods/Target Support Files/Pods-DoubleTargetTest/Pods-DoubleTargetTest-acknowledgements.markdown +++ /dev/null @@ -1,26 +0,0 @@ -# Acknowledgements -This application makes use of the following third party libraries: - -## MyBundle - -Copyright (c) 2023 thedderwick - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. - -Generated by CocoaPods - https://cocoapods.org diff --git a/PBXProjParser/Tests/PBXProjParserTests/TestAssets/Projects/DoubleTargetTest/Pods/Target Support Files/Pods-DoubleTargetTest/Pods-DoubleTargetTest-acknowledgements.plist b/PBXProjParser/Tests/PBXProjParserTests/TestAssets/Projects/DoubleTargetTest/Pods/Target Support Files/Pods-DoubleTargetTest/Pods-DoubleTargetTest-acknowledgements.plist deleted file mode 100644 index 711f6ee..0000000 --- a/PBXProjParser/Tests/PBXProjParserTests/TestAssets/Projects/DoubleTargetTest/Pods/Target Support Files/Pods-DoubleTargetTest/Pods-DoubleTargetTest-acknowledgements.plist +++ /dev/null @@ -1,58 +0,0 @@ - - - - - PreferenceSpecifiers - - - FooterText - This application makes use of the following third party libraries: - Title - Acknowledgements - Type - PSGroupSpecifier - - - FooterText - Copyright (c) 2023 thedderwick <thedderwick@veracode.com> - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. - - License - MIT - Title - MyBundle - Type - PSGroupSpecifier - - - FooterText - Generated by CocoaPods - https://cocoapods.org - Title - - Type - PSGroupSpecifier - - - StringsTable - Acknowledgements - Title - Acknowledgements - - diff --git a/PBXProjParser/Tests/PBXProjParserTests/TestAssets/Projects/DoubleTargetTest/Pods/Target Support Files/Pods-DoubleTargetTest/Pods-DoubleTargetTest-dummy.m b/PBXProjParser/Tests/PBXProjParserTests/TestAssets/Projects/DoubleTargetTest/Pods/Target Support Files/Pods-DoubleTargetTest/Pods-DoubleTargetTest-dummy.m deleted file mode 100644 index beca502..0000000 --- a/PBXProjParser/Tests/PBXProjParserTests/TestAssets/Projects/DoubleTargetTest/Pods/Target Support Files/Pods-DoubleTargetTest/Pods-DoubleTargetTest-dummy.m +++ /dev/null @@ -1,5 +0,0 @@ -#import -@interface PodsDummy_Pods_DoubleTargetTest : NSObject -@end -@implementation PodsDummy_Pods_DoubleTargetTest -@end diff --git a/PBXProjParser/Tests/PBXProjParserTests/TestAssets/Projects/DoubleTargetTest/Pods/Target Support Files/Pods-DoubleTargetTest/Pods-DoubleTargetTest-frameworks-Debug-input-files.xcfilelist b/PBXProjParser/Tests/PBXProjParserTests/TestAssets/Projects/DoubleTargetTest/Pods/Target Support Files/Pods-DoubleTargetTest/Pods-DoubleTargetTest-frameworks-Debug-input-files.xcfilelist deleted file mode 100644 index cd39096..0000000 --- a/PBXProjParser/Tests/PBXProjParserTests/TestAssets/Projects/DoubleTargetTest/Pods/Target Support Files/Pods-DoubleTargetTest/Pods-DoubleTargetTest-frameworks-Debug-input-files.xcfilelist +++ /dev/null @@ -1,2 +0,0 @@ -${PODS_ROOT}/Target Support Files/Pods-DoubleTargetTest/Pods-DoubleTargetTest-frameworks.sh -${BUILT_PRODUCTS_DIR}/MyBundle/MyBundle.framework \ No newline at end of file diff --git a/PBXProjParser/Tests/PBXProjParserTests/TestAssets/Projects/DoubleTargetTest/Pods/Target Support Files/Pods-DoubleTargetTest/Pods-DoubleTargetTest-frameworks-Debug-output-files.xcfilelist b/PBXProjParser/Tests/PBXProjParserTests/TestAssets/Projects/DoubleTargetTest/Pods/Target Support Files/Pods-DoubleTargetTest/Pods-DoubleTargetTest-frameworks-Debug-output-files.xcfilelist deleted file mode 100644 index 07517cb..0000000 --- a/PBXProjParser/Tests/PBXProjParserTests/TestAssets/Projects/DoubleTargetTest/Pods/Target Support Files/Pods-DoubleTargetTest/Pods-DoubleTargetTest-frameworks-Debug-output-files.xcfilelist +++ /dev/null @@ -1 +0,0 @@ -${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/MyBundle.framework \ No newline at end of file diff --git a/PBXProjParser/Tests/PBXProjParserTests/TestAssets/Projects/DoubleTargetTest/Pods/Target Support Files/Pods-DoubleTargetTest/Pods-DoubleTargetTest-frameworks-Release-input-files.xcfilelist b/PBXProjParser/Tests/PBXProjParserTests/TestAssets/Projects/DoubleTargetTest/Pods/Target Support Files/Pods-DoubleTargetTest/Pods-DoubleTargetTest-frameworks-Release-input-files.xcfilelist deleted file mode 100644 index cd39096..0000000 --- a/PBXProjParser/Tests/PBXProjParserTests/TestAssets/Projects/DoubleTargetTest/Pods/Target Support Files/Pods-DoubleTargetTest/Pods-DoubleTargetTest-frameworks-Release-input-files.xcfilelist +++ /dev/null @@ -1,2 +0,0 @@ -${PODS_ROOT}/Target Support Files/Pods-DoubleTargetTest/Pods-DoubleTargetTest-frameworks.sh -${BUILT_PRODUCTS_DIR}/MyBundle/MyBundle.framework \ No newline at end of file diff --git a/PBXProjParser/Tests/PBXProjParserTests/TestAssets/Projects/DoubleTargetTest/Pods/Target Support Files/Pods-DoubleTargetTest/Pods-DoubleTargetTest-frameworks-Release-output-files.xcfilelist b/PBXProjParser/Tests/PBXProjParserTests/TestAssets/Projects/DoubleTargetTest/Pods/Target Support Files/Pods-DoubleTargetTest/Pods-DoubleTargetTest-frameworks-Release-output-files.xcfilelist deleted file mode 100644 index 07517cb..0000000 --- a/PBXProjParser/Tests/PBXProjParserTests/TestAssets/Projects/DoubleTargetTest/Pods/Target Support Files/Pods-DoubleTargetTest/Pods-DoubleTargetTest-frameworks-Release-output-files.xcfilelist +++ /dev/null @@ -1 +0,0 @@ -${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/MyBundle.framework \ No newline at end of file diff --git a/PBXProjParser/Tests/PBXProjParserTests/TestAssets/Projects/DoubleTargetTest/Pods/Target Support Files/Pods-DoubleTargetTest/Pods-DoubleTargetTest-frameworks.sh b/PBXProjParser/Tests/PBXProjParserTests/TestAssets/Projects/DoubleTargetTest/Pods/Target Support Files/Pods-DoubleTargetTest/Pods-DoubleTargetTest-frameworks.sh deleted file mode 100755 index 8fe186b..0000000 --- a/PBXProjParser/Tests/PBXProjParserTests/TestAssets/Projects/DoubleTargetTest/Pods/Target Support Files/Pods-DoubleTargetTest/Pods-DoubleTargetTest-frameworks.sh +++ /dev/null @@ -1,186 +0,0 @@ -#!/bin/sh -set -e -set -u -set -o pipefail - -function on_error { - echo "$(realpath -mq "${0}"):$1: error: Unexpected failure" -} -trap 'on_error $LINENO' ERR - -if [ -z ${FRAMEWORKS_FOLDER_PATH+x} ]; then - # If FRAMEWORKS_FOLDER_PATH is not set, then there's nowhere for us to copy - # frameworks to, so exit 0 (signalling the script phase was successful). - exit 0 -fi - -echo "mkdir -p ${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" -mkdir -p "${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" - -COCOAPODS_PARALLEL_CODE_SIGN="${COCOAPODS_PARALLEL_CODE_SIGN:-false}" -SWIFT_STDLIB_PATH="${DT_TOOLCHAIN_DIR}/usr/lib/swift/${PLATFORM_NAME}" -BCSYMBOLMAP_DIR="BCSymbolMaps" - - -# This protects against multiple targets copying the same framework dependency at the same time. The solution -# was originally proposed here: https://lists.samba.org/archive/rsync/2008-February/020158.html -RSYNC_PROTECT_TMP_FILES=(--filter "P .*.??????") - -# Copies and strips a vendored framework -install_framework() -{ - if [ -r "${BUILT_PRODUCTS_DIR}/$1" ]; then - local source="${BUILT_PRODUCTS_DIR}/$1" - elif [ -r "${BUILT_PRODUCTS_DIR}/$(basename "$1")" ]; then - local source="${BUILT_PRODUCTS_DIR}/$(basename "$1")" - elif [ -r "$1" ]; then - local source="$1" - fi - - local destination="${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" - - if [ -L "${source}" ]; then - echo "Symlinked..." - source="$(readlink "${source}")" - fi - - if [ -d "${source}/${BCSYMBOLMAP_DIR}" ]; then - # Locate and install any .bcsymbolmaps if present, and remove them from the .framework before the framework is copied - find "${source}/${BCSYMBOLMAP_DIR}" -name "*.bcsymbolmap"|while read f; do - echo "Installing $f" - install_bcsymbolmap "$f" "$destination" - rm "$f" - done - rmdir "${source}/${BCSYMBOLMAP_DIR}" - fi - - # Use filter instead of exclude so missing patterns don't throw errors. - echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --links --filter \"- CVS/\" --filter \"- .svn/\" --filter \"- .git/\" --filter \"- .hg/\" --filter \"- Headers\" --filter \"- PrivateHeaders\" --filter \"- Modules\" \"${source}\" \"${destination}\"" - rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --links --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${source}" "${destination}" - - local basename - basename="$(basename -s .framework "$1")" - binary="${destination}/${basename}.framework/${basename}" - - if ! [ -r "$binary" ]; then - binary="${destination}/${basename}" - elif [ -L "${binary}" ]; then - echo "Destination binary is symlinked..." - dirname="$(dirname "${binary}")" - binary="${dirname}/$(readlink "${binary}")" - fi - - # Strip invalid architectures so "fat" simulator / device frameworks work on device - if [[ "$(file "$binary")" == *"dynamically linked shared library"* ]]; then - strip_invalid_archs "$binary" - fi - - # Resign the code if required by the build settings to avoid unstable apps - code_sign_if_enabled "${destination}/$(basename "$1")" - - # Embed linked Swift runtime libraries. No longer necessary as of Xcode 7. - if [ "${XCODE_VERSION_MAJOR}" -lt 7 ]; then - local swift_runtime_libs - swift_runtime_libs=$(xcrun otool -LX "$binary" | grep --color=never @rpath/libswift | sed -E s/@rpath\\/\(.+dylib\).*/\\1/g | uniq -u) - for lib in $swift_runtime_libs; do - echo "rsync -auv \"${SWIFT_STDLIB_PATH}/${lib}\" \"${destination}\"" - rsync -auv "${SWIFT_STDLIB_PATH}/${lib}" "${destination}" - code_sign_if_enabled "${destination}/${lib}" - done - fi -} -# Copies and strips a vendored dSYM -install_dsym() { - local source="$1" - warn_missing_arch=${2:-true} - if [ -r "$source" ]; then - # Copy the dSYM into the targets temp dir. - echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter \"- CVS/\" --filter \"- .svn/\" --filter \"- .git/\" --filter \"- .hg/\" --filter \"- Headers\" --filter \"- PrivateHeaders\" --filter \"- Modules\" \"${source}\" \"${DERIVED_FILES_DIR}\"" - rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${source}" "${DERIVED_FILES_DIR}" - - local basename - basename="$(basename -s .dSYM "$source")" - binary_name="$(ls "$source/Contents/Resources/DWARF")" - binary="${DERIVED_FILES_DIR}/${basename}.dSYM/Contents/Resources/DWARF/${binary_name}" - - # Strip invalid architectures from the dSYM. - if [[ "$(file "$binary")" == *"Mach-O "*"dSYM companion"* ]]; then - strip_invalid_archs "$binary" "$warn_missing_arch" - fi - if [[ $STRIP_BINARY_RETVAL == 0 ]]; then - # Move the stripped file into its final destination. - echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --links --filter \"- CVS/\" --filter \"- .svn/\" --filter \"- .git/\" --filter \"- .hg/\" --filter \"- Headers\" --filter \"- PrivateHeaders\" --filter \"- Modules\" \"${DERIVED_FILES_DIR}/${basename}.framework.dSYM\" \"${DWARF_DSYM_FOLDER_PATH}\"" - rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --links --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${DERIVED_FILES_DIR}/${basename}.dSYM" "${DWARF_DSYM_FOLDER_PATH}" - else - # The dSYM was not stripped at all, in this case touch a fake folder so the input/output paths from Xcode do not reexecute this script because the file is missing. - mkdir -p "${DWARF_DSYM_FOLDER_PATH}" - touch "${DWARF_DSYM_FOLDER_PATH}/${basename}.dSYM" - fi - fi -} - -# Used as a return value for each invocation of `strip_invalid_archs` function. -STRIP_BINARY_RETVAL=0 - -# Strip invalid architectures -strip_invalid_archs() { - binary="$1" - warn_missing_arch=${2:-true} - # Get architectures for current target binary - binary_archs="$(lipo -info "$binary" | rev | cut -d ':' -f1 | awk '{$1=$1;print}' | rev)" - # Intersect them with the architectures we are building for - intersected_archs="$(echo ${ARCHS[@]} ${binary_archs[@]} | tr ' ' '\n' | sort | uniq -d)" - # If there are no archs supported by this binary then warn the user - if [[ -z "$intersected_archs" ]]; then - if [[ "$warn_missing_arch" == "true" ]]; then - echo "warning: [CP] Vendored binary '$binary' contains architectures ($binary_archs) none of which match the current build architectures ($ARCHS)." - fi - STRIP_BINARY_RETVAL=1 - return - fi - stripped="" - for arch in $binary_archs; do - if ! [[ "${ARCHS}" == *"$arch"* ]]; then - # Strip non-valid architectures in-place - lipo -remove "$arch" -output "$binary" "$binary" - stripped="$stripped $arch" - fi - done - if [[ "$stripped" ]]; then - echo "Stripped $binary of architectures:$stripped" - fi - STRIP_BINARY_RETVAL=0 -} - -# Copies the bcsymbolmap files of a vendored framework -install_bcsymbolmap() { - local bcsymbolmap_path="$1" - local destination="${BUILT_PRODUCTS_DIR}" - echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${bcsymbolmap_path}" "${destination}"" - rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${bcsymbolmap_path}" "${destination}" -} - -# Signs a framework with the provided identity -code_sign_if_enabled() { - if [ -n "${EXPANDED_CODE_SIGN_IDENTITY:-}" -a "${CODE_SIGNING_REQUIRED:-}" != "NO" -a "${CODE_SIGNING_ALLOWED}" != "NO" ]; then - # Use the current code_sign_identity - echo "Code Signing $1 with Identity ${EXPANDED_CODE_SIGN_IDENTITY_NAME}" - local code_sign_cmd="/usr/bin/codesign --force --sign ${EXPANDED_CODE_SIGN_IDENTITY} ${OTHER_CODE_SIGN_FLAGS:-} --preserve-metadata=identifier,entitlements '$1'" - - if [ "${COCOAPODS_PARALLEL_CODE_SIGN}" == "true" ]; then - code_sign_cmd="$code_sign_cmd &" - fi - echo "$code_sign_cmd" - eval "$code_sign_cmd" - fi -} - -if [[ "$CONFIGURATION" == "Debug" ]]; then - install_framework "${BUILT_PRODUCTS_DIR}/MyBundle/MyBundle.framework" -fi -if [[ "$CONFIGURATION" == "Release" ]]; then - install_framework "${BUILT_PRODUCTS_DIR}/MyBundle/MyBundle.framework" -fi -if [ "${COCOAPODS_PARALLEL_CODE_SIGN}" == "true" ]; then - wait -fi diff --git a/PBXProjParser/Tests/PBXProjParserTests/TestAssets/Projects/DoubleTargetTest/Pods/Target Support Files/Pods-DoubleTargetTest/Pods-DoubleTargetTest-umbrella.h b/PBXProjParser/Tests/PBXProjParserTests/TestAssets/Projects/DoubleTargetTest/Pods/Target Support Files/Pods-DoubleTargetTest/Pods-DoubleTargetTest-umbrella.h deleted file mode 100644 index 570ba23..0000000 --- a/PBXProjParser/Tests/PBXProjParserTests/TestAssets/Projects/DoubleTargetTest/Pods/Target Support Files/Pods-DoubleTargetTest/Pods-DoubleTargetTest-umbrella.h +++ /dev/null @@ -1,16 +0,0 @@ -#ifdef __OBJC__ -#import -#else -#ifndef FOUNDATION_EXPORT -#if defined(__cplusplus) -#define FOUNDATION_EXPORT extern "C" -#else -#define FOUNDATION_EXPORT extern -#endif -#endif -#endif - - -FOUNDATION_EXPORT double Pods_DoubleTargetTestVersionNumber; -FOUNDATION_EXPORT const unsigned char Pods_DoubleTargetTestVersionString[]; - diff --git a/PBXProjParser/Tests/PBXProjParserTests/TestAssets/Projects/DoubleTargetTest/Pods/Target Support Files/Pods-DoubleTargetTest/Pods-DoubleTargetTest.debug.xcconfig b/PBXProjParser/Tests/PBXProjParserTests/TestAssets/Projects/DoubleTargetTest/Pods/Target Support Files/Pods-DoubleTargetTest/Pods-DoubleTargetTest.debug.xcconfig deleted file mode 100644 index c9bfcc2..0000000 --- a/PBXProjParser/Tests/PBXProjParserTests/TestAssets/Projects/DoubleTargetTest/Pods/Target Support Files/Pods-DoubleTargetTest/Pods-DoubleTargetTest.debug.xcconfig +++ /dev/null @@ -1,15 +0,0 @@ -ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES -CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO -FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/MyBundle" -GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 -HEADER_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/MyBundle/MyBundle.framework/Headers" -LD_RUNPATH_SEARCH_PATHS = $(inherited) /usr/lib/swift '@executable_path/Frameworks' '@loader_path/Frameworks' -LIBRARY_SEARCH_PATHS = $(inherited) "${DT_TOOLCHAIN_DIR}/usr/lib/swift/${PLATFORM_NAME}" /usr/lib/swift -OTHER_LDFLAGS = $(inherited) -framework "MyBundle" -OTHER_SWIFT_FLAGS = $(inherited) -D COCOAPODS -PODS_BUILD_DIR = ${BUILD_DIR} -PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) -PODS_PODFILE_DIR_PATH = ${SRCROOT}/. -PODS_ROOT = ${SRCROOT}/Pods -PODS_XCFRAMEWORKS_BUILD_DIR = $(PODS_CONFIGURATION_BUILD_DIR)/XCFrameworkIntermediates -USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES diff --git a/PBXProjParser/Tests/PBXProjParserTests/TestAssets/Projects/DoubleTargetTest/Pods/Target Support Files/Pods-DoubleTargetTest/Pods-DoubleTargetTest.modulemap b/PBXProjParser/Tests/PBXProjParserTests/TestAssets/Projects/DoubleTargetTest/Pods/Target Support Files/Pods-DoubleTargetTest/Pods-DoubleTargetTest.modulemap deleted file mode 100644 index 0fe645c..0000000 --- a/PBXProjParser/Tests/PBXProjParserTests/TestAssets/Projects/DoubleTargetTest/Pods/Target Support Files/Pods-DoubleTargetTest/Pods-DoubleTargetTest.modulemap +++ /dev/null @@ -1,6 +0,0 @@ -framework module Pods_DoubleTargetTest { - umbrella header "Pods-DoubleTargetTest-umbrella.h" - - export * - module * { export * } -} diff --git a/PBXProjParser/Tests/PBXProjParserTests/TestAssets/Projects/DoubleTargetTest/Pods/Target Support Files/Pods-DoubleTargetTest/Pods-DoubleTargetTest.release.xcconfig b/PBXProjParser/Tests/PBXProjParserTests/TestAssets/Projects/DoubleTargetTest/Pods/Target Support Files/Pods-DoubleTargetTest/Pods-DoubleTargetTest.release.xcconfig deleted file mode 100644 index c9bfcc2..0000000 --- a/PBXProjParser/Tests/PBXProjParserTests/TestAssets/Projects/DoubleTargetTest/Pods/Target Support Files/Pods-DoubleTargetTest/Pods-DoubleTargetTest.release.xcconfig +++ /dev/null @@ -1,15 +0,0 @@ -ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES -CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO -FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/MyBundle" -GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 -HEADER_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/MyBundle/MyBundle.framework/Headers" -LD_RUNPATH_SEARCH_PATHS = $(inherited) /usr/lib/swift '@executable_path/Frameworks' '@loader_path/Frameworks' -LIBRARY_SEARCH_PATHS = $(inherited) "${DT_TOOLCHAIN_DIR}/usr/lib/swift/${PLATFORM_NAME}" /usr/lib/swift -OTHER_LDFLAGS = $(inherited) -framework "MyBundle" -OTHER_SWIFT_FLAGS = $(inherited) -D COCOAPODS -PODS_BUILD_DIR = ${BUILD_DIR} -PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) -PODS_PODFILE_DIR_PATH = ${SRCROOT}/. -PODS_ROOT = ${SRCROOT}/Pods -PODS_XCFRAMEWORKS_BUILD_DIR = $(PODS_CONFIGURATION_BUILD_DIR)/XCFrameworkIntermediates -USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES diff --git a/Package.swift b/Package.swift index 612c472..ac47272 100644 --- a/Package.swift +++ b/Package.swift @@ -14,7 +14,6 @@ let package = Package( .package(url: "https://github.com/apple/swift-argument-parser.git", from: "1.0.0"), .package(url: "https://github.com/apple/swift-log.git", from: "1.0.0"), .package(url: "https://github.com/apple/swift-docc-plugin", from: "1.0.0"), - .package(path: "PBXProjParser"), .package(path: "PIF") ], targets: [ @@ -25,7 +24,6 @@ let package = Package( dependencies: [ .product(name: "ArgumentParser", package: "swift-argument-parser"), .product(name: "Logging", package: "swift-log"), - .product(name: "PBXProjParser", package: "PBXProjParser"), .product(name: "PIFSupport", package: "PIF") ], path: "Sources/GenIR" diff --git a/Sources/GenIR/CompilerCommandRunner.swift b/Sources/GenIR/CompilerCommandRunner.swift index ac8edf4..e5586d5 100644 --- a/Sources/GenIR/CompilerCommandRunner.swift +++ b/Sources/GenIR/CompilerCommandRunner.swift @@ -7,7 +7,6 @@ import Foundation import Logging -import PBXProjParser /// A model of the contents of an output file map json typealias OutputFileMap = [String: [String: String]] diff --git a/Sources/GenIR/GenIR.swift b/Sources/GenIR/GenIR.swift index 465ce17..a35af57 100644 --- a/Sources/GenIR/GenIR.swift +++ b/Sources/GenIR/GenIR.swift @@ -1,7 +1,6 @@ import Foundation import ArgumentParser import Logging -import PBXProjParser import PIFSupport /// Global logger object diff --git a/Sources/GenIR/OutputPostprocessor.swift b/Sources/GenIR/OutputPostprocessor.swift index 4b3877a..bddbef6 100644 --- a/Sources/GenIR/OutputPostprocessor.swift +++ b/Sources/GenIR/OutputPostprocessor.swift @@ -6,7 +6,6 @@ // import Foundation -import PBXProjParser private typealias SizeAndCreation = (Int, Date) diff --git a/Sources/GenIR/Target.swift b/Sources/GenIR/Target.swift index f33723b..1e72245 100644 --- a/Sources/GenIR/Target.swift +++ b/Sources/GenIR/Target.swift @@ -6,7 +6,6 @@ // import Foundation -import PBXProjParser import PIFSupport class Target { diff --git a/Tests/GenIRTests/DependencyGraphTests.swift b/Tests/GenIRTests/DependencyGraphTests.swift index 31edaf8..2c72eda 100644 --- a/Tests/GenIRTests/DependencyGraphTests.swift +++ b/Tests/GenIRTests/DependencyGraphTests.swift @@ -1,6 +1,5 @@ import XCTest @testable import gen_ir -import PBXProjParser final class DependencyGraphTests: XCTestCase { let testPath: URL = { diff --git a/Tests/GenIRTests/OutputPostprocessorFileMoverTests.swift b/Tests/GenIRTests/OutputPostprocessorFileMoverTests.swift index ba4b784..acf034d 100644 --- a/Tests/GenIRTests/OutputPostprocessorFileMoverTests.swift +++ b/Tests/GenIRTests/OutputPostprocessorFileMoverTests.swift @@ -1,6 +1,5 @@ import XCTest @testable import gen_ir -import PBXProjParser final class OutputPostprocessorFileMoverTests: XCTestCase { let testPath: URL = { diff --git a/Tests/GenIRTests/UmbrellaTests.swift b/Tests/GenIRTests/UmbrellaTests.swift index eac8475..eba625a 100644 --- a/Tests/GenIRTests/UmbrellaTests.swift +++ b/Tests/GenIRTests/UmbrellaTests.swift @@ -1,6 +1,5 @@ import XCTest @testable import gen_ir -import PBXProjParser final class UmbrellaTests: XCTestCase { let testPath: URL = { diff --git a/Tests/GenIRTests/WorkspaceTests.swift b/Tests/GenIRTests/WorkspaceTests.swift index 0b2530f..a7844fb 100644 --- a/Tests/GenIRTests/WorkspaceTests.swift +++ b/Tests/GenIRTests/WorkspaceTests.swift @@ -1,6 +1,5 @@ import XCTest @testable import gen_ir -import PBXProjParser final class WorkspaceTests: XCTestCase { let testPath: URL = { From c7ea9056250c1f9f438fce23b804ce5ed2de577a Mon Sep 17 00:00:00 2001 From: NinjaLikesCheez Date: Tue, 14 May 2024 10:24:18 +0200 Subject: [PATCH 08/21] Initial support for SPM product to target resolution --- PIF/Sources/PIFSupport/PIF.swift | 11 ++----- .../Dependency Graph/DependencyGraph.swift | 2 +- Sources/GenIR/Dependency Graph/Node.swift | 1 - Sources/GenIR/GenIR.swift | 6 +++- Sources/GenIR/OutputPostprocessor.swift | 3 +- Sources/GenIR/PIFCache.swift | 33 +++++++++++++++---- Sources/GenIR/Target.swift | 8 +++-- 7 files changed, 44 insertions(+), 20 deletions(-) diff --git a/PIF/Sources/PIFSupport/PIF.swift b/PIF/Sources/PIFSupport/PIF.swift index 9674784..db57d0b 100644 --- a/PIF/Sources/PIFSupport/PIF.swift +++ b/PIF/Sources/PIFSupport/PIF.swift @@ -308,9 +308,7 @@ public enum PIF { default: // TODO: use logger here print("unknown reference type: \(child.type ?? "")") - // TODO: support variantGroup etc return nil - // throw Error.decodingError("unknown reference type \(child.type ?? "")") } } } else { @@ -515,12 +513,9 @@ public enum PIF { return nil // TODO: we should probably handle these: /* - case copyFiles = "com.apple.buildphase.copy-files" - case frameworks = "com.apple.buildphase.frameworks" - case headers = "com.apple.buildphase.headers" - case resources = "com.apple.buildphase.resources" - case shellScript = "com.apple.buildphase.shell-script" - case sources = "com.apple.buildphase.sources"*/ + case copyFiles = "com.apple.buildphase.copy-files" + case shellScript = "com.apple.buildphase.shell-script" + case sources = "com.apple.buildphase.sources"*/ // throw Error.decodingError("unknown build phase \(type)") } } diff --git a/Sources/GenIR/Dependency Graph/DependencyGraph.swift b/Sources/GenIR/Dependency Graph/DependencyGraph.swift index 7db2f3d..7552665 100644 --- a/Sources/GenIR/Dependency Graph/DependencyGraph.swift +++ b/Sources/GenIR/Dependency Graph/DependencyGraph.swift @@ -75,7 +75,7 @@ class DependencyGraph { visited.insert(node) logger.debug("visited: \(visited)") - for edge in node.edges { + for edge in node.edges where edge.relationship == .dependency { logger.debug("edge to: \(edge.to)") if visited.insert(edge.to).inserted { logger.debug("inserted, recursing") diff --git a/Sources/GenIR/Dependency Graph/Node.swift b/Sources/GenIR/Dependency Graph/Node.swift index 659dad4..a2e9406 100644 --- a/Sources/GenIR/Dependency Graph/Node.swift +++ b/Sources/GenIR/Dependency Graph/Node.swift @@ -29,7 +29,6 @@ class Node { /// Adds an edge to this node /// - Parameter edge: the edge to add func add(edge: Edge) { - // TODO: slow - change. if edges.filter({ $0.to.valueName == edge.to.valueName }).count == 0 { edges.append(edge) } diff --git a/Sources/GenIR/GenIR.swift b/Sources/GenIR/GenIR.swift index a35af57..0aab100 100644 --- a/Sources/GenIR/GenIR.swift +++ b/Sources/GenIR/GenIR.swift @@ -143,7 +143,11 @@ struct IREmitterCommand: ParsableCommand { if dumpDependencyGraph { do { - try graph.toDot(output.appendingPathComponent("graph.dot").filePath) + try graph.toDot(output + .deletingLastPathComponent() + .appendingPathComponent("graph.dot") + .filePath + ) } catch { logger.error("toDot error: \(error)") } diff --git a/Sources/GenIR/OutputPostprocessor.swift b/Sources/GenIR/OutputPostprocessor.swift index bddbef6..acba0ac 100644 --- a/Sources/GenIR/OutputPostprocessor.swift +++ b/Sources/GenIR/OutputPostprocessor.swift @@ -78,9 +78,10 @@ class OutputPostprocessor { target: Target, at path: URL ) throws -> Set { + // TODO: we need better handling of swift package products and targets in the dependency graph or we fail to move dependencies here let chain = graph.chain(for: target) - logger.debug("Chain for target: \(target.productName):\n") + logger.debug("Chain for target: \(target.productName):\n\(chain.map { "\($0)\n" })") chain.forEach { logger.debug("\($0)") } // We want to process the chain, visiting each node _shallowly_ and copy it's dependencies into it's parent diff --git a/Sources/GenIR/PIFCache.swift b/Sources/GenIR/PIFCache.swift index 9de608d..d8e22b3 100644 --- a/Sources/GenIR/PIFCache.swift +++ b/Sources/GenIR/PIFCache.swift @@ -166,12 +166,32 @@ struct PIFDependencyProvider: DependencyProviding { } } + private func resolveSwiftPackage(_ packageGUID: PIF.GUID) -> PIF.GUID { + let productToken = "PACKAGE-PRODUCT:" + let targetToken = "PACKAGE-TARGET:" + guard packageGUID.starts(with: productToken), let product = guidToTargets[packageGUID] else { return packageGUID } + + let productName = String(packageGUID.dropFirst(productToken.count)) + + // TODO: should this also use the framework build phase to determine a dependency? + let packageTargetDependencies = product + .baseTarget + .dependencies + .filter { $0.targetGUID.starts(with: targetToken) } + .filter { $0.targetGUID.dropFirst(targetToken.count) == productName } + + precondition(packageTargetDependencies.count == 1, "Expecting one matching package target - found \(packageTargetDependencies.count): \(packageTargetDependencies). Returning first match") + + return packageTargetDependencies.first?.targetGUID ?? packageGUID + } + func dependencies(for value: Target) -> [Target] { // Direct dependencies - let dependencyTargets = value + let dependencyTargetGUIDs = value .baseTarget .dependencies - .compactMap { guidToTargets[$0.targetGUID] } + .map { $0.targetGUID } + .map { resolveSwiftPackage($0) } // Framework build phase dependencies let frameworkBuildPhases = value @@ -179,7 +199,7 @@ struct PIFDependencyProvider: DependencyProviding { .buildPhases .compactMap { $0 as? PIF.FrameworksBuildPhase } - let frameworks = frameworkBuildPhases + let frameworkGUIDs = frameworkBuildPhases .flatMap { $0.buildFiles } .compactMap { switch $0.reference { @@ -187,9 +207,10 @@ struct PIFDependencyProvider: DependencyProviding { case .target: return nil // TODO: is this fine? I think so since we're looking for .framework file references here not targets which should be a dependency } } - .compactMap { cache.frameworks[$0] } - .compactMap { guidToTargets[$0.guid] } + .compactMap { cache.frameworks[$0]?.guid } + + let dependencyTargets = (dependencyTargetGUIDs + frameworkGUIDs).compactMap { guidToTargets[$0] } - return dependencyTargets + frameworks + return dependencyTargets } } diff --git a/Sources/GenIR/Target.swift b/Sources/GenIR/Target.swift index 1e72245..d32fcf6 100644 --- a/Sources/GenIR/Target.swift +++ b/Sources/GenIR/Target.swift @@ -11,9 +11,13 @@ import PIFSupport class Target { var name: String { baseTarget.name } var productName: String { - (baseTarget as? PIF.Target)?.productName ?? baseTarget.name + if let target = baseTarget as? PIF.Target, !target.productName.isEmpty { + return target.productName + } + + return baseTarget.name } - // TODO: we need to handle SPM's insane naming scheme for products here ^ + // TODO: we need to handle SPM's insane naming scheme for products here ^ including the potential of a dynamic variant let baseTarget: PIF.BaseTarget let commands: [CompilerCommand] From 9d9a335da6a2fb56a5620411f190a29b7380323d Mon Sep 17 00:00:00 2001 From: NinjaLikesCheez Date: Tue, 14 May 2024 15:28:31 +0200 Subject: [PATCH 09/21] Clean up logging in PIFSupport, use product name for target node values --- PIF/Sources/PIFSupport/PIF.swift | 6 ++-- PIF/Sources/PIFSupport/PIFSupport.swift | 6 +++- .../Dependency Graph/DependencyGraph.swift | 34 ++++++++----------- Sources/GenIR/PIFCache.swift | 2 +- Sources/GenIR/Target.swift | 2 +- 5 files changed, 23 insertions(+), 27 deletions(-) diff --git a/PIF/Sources/PIFSupport/PIF.swift b/PIF/Sources/PIFSupport/PIF.swift index db57d0b..48d8145 100644 --- a/PIF/Sources/PIFSupport/PIF.swift +++ b/PIF/Sources/PIFSupport/PIF.swift @@ -306,8 +306,7 @@ public enum PIF { case FileReference.type: return try childrenContainer.decode(FileReference.self) default: - // TODO: use logger here - print("unknown reference type: \(child.type ?? "")") + logger.debug("unknown reference type: \(child.type ?? "")") return nil } } @@ -508,8 +507,7 @@ public enum PIF { case ResourcesBuildPhase.type: return try container.decode(ResourcesBuildPhase.self) default: - // TODO: replace with logger - print("unknown build phase: \(type)") + logger.debug("unknown build phase: \(type)") return nil // TODO: we should probably handle these: /* diff --git a/PIF/Sources/PIFSupport/PIFSupport.swift b/PIF/Sources/PIFSupport/PIFSupport.swift index 2b8665a..46b1857 100644 --- a/PIF/Sources/PIFSupport/PIFSupport.swift +++ b/PIF/Sources/PIFSupport/PIFSupport.swift @@ -1,4 +1,7 @@ import Foundation +import Logging + +var logger: Logger! public class PIFParser { private let cachePath: URL @@ -8,7 +11,8 @@ public class PIFParser { case workspaceNotFound } - public init(cachePath: URL) throws { + public init(cachePath: URL, logger log: Logger) throws { + logger = log self.cachePath = cachePath let data = try Data(contentsOf: try Self.workspacePath(in: cachePath)) diff --git a/Sources/GenIR/Dependency Graph/DependencyGraph.swift b/Sources/GenIR/Dependency Graph/DependencyGraph.swift index 7552665..e1cd9e6 100644 --- a/Sources/GenIR/Dependency Graph/DependencyGraph.swift +++ b/Sources/GenIR/Dependency Graph/DependencyGraph.swift @@ -44,24 +44,6 @@ class DependencyGraph { return depthFirstSearch(startingAt: node) } - func toDot(_ path: String) throws { - var contents = "digraph DependencyGraph {\n" - - for node in nodes.values { - for edge in node.edges.filter({ $0.relationship == .dependency }) { - func dotSanitized(for name: String) -> String { - name - .replacingOccurrences(of: "-", with: "_") - .replacingOccurrences(of: ".", with: "_") - } - contents.append("\(dotSanitized(for: node.valueName)) -> \(dotSanitized(for: edge.to.valueName))\n") - } - } - - contents.append("}") - try contents.write(toFile: path, atomically: true, encoding: .utf8) - } - /// Perform a depth-first search starting at the provided node /// - Parameter node: the node whose children to search through /// - Returns: an array of nodes ordered by a depth-first search approach @@ -76,9 +58,8 @@ class DependencyGraph { logger.debug("visited: \(visited)") for edge in node.edges where edge.relationship == .dependency { - logger.debug("edge to: \(edge.to)") if visited.insert(edge.to).inserted { - logger.debug("inserted, recursing") + logger.debug("edge to: \(edge.to)") depthFirst(node: edge.to) } else { logger.debug("edge already in visited: \(visited)") @@ -92,6 +73,19 @@ class DependencyGraph { depthFirst(node: node) return chain } + + func toDot(_ path: String) throws { + var contents = "digraph DependencyGraph {\n" + + for node in nodes.values { + for edge in node.edges.filter({ $0.relationship == .dependency }) { + contents.append("\"\(node.valueName)\" -> \"\(edge.to.valueName)\"\n") + } + } + + contents.append("}") + try contents.write(toFile: path, atomically: true, encoding: .utf8) + } } extension DependencyGraph: CustomStringConvertible { diff --git a/Sources/GenIR/PIFCache.swift b/Sources/GenIR/PIFCache.swift index d8e22b3..6737d8a 100644 --- a/Sources/GenIR/PIFCache.swift +++ b/Sources/GenIR/PIFCache.swift @@ -16,7 +16,7 @@ class PIFCache { self.pifCachePath = try Self.pifCachePath(in: buildCache) do { - let cache = try PIFParser(cachePath: pifCachePath) + let cache = try PIFParser(cachePath: pifCachePath, logger: logger) workspace = cache.workspace } catch { throw Error.pifError(error) diff --git a/Sources/GenIR/Target.swift b/Sources/GenIR/Target.swift index d32fcf6..0f26a8a 100644 --- a/Sources/GenIR/Target.swift +++ b/Sources/GenIR/Target.swift @@ -39,7 +39,7 @@ extension Target { extension Target: NodeValue { var value: Self { self } - var valueName: String { name } + var valueName: String { productName } } extension Target: Hashable { From 9b86fedfa1dc9450f9dee8dfec46c8b1a25122b2 Mon Sep 17 00:00:00 2001 From: NinjaLikesCheez Date: Fri, 24 May 2024 13:55:21 +0200 Subject: [PATCH 10/21] Allow multiple workspaces. Check modification time to grab the most recent one. --- .github/workflows/build.yml | 4 +- PIF/Sources/PIFSupport/PIFSupport.swift | 29 +- Sources/GenIR/PIFCache.swift | 59 ++- .../SPMTest/MyBinaryDependency/.gitignore | 8 + .../SPMTest/MyBinaryDependency/Package.swift | 25 ++ .../Sources/_Stub/MyBinaryDependency.swift | 2 + .../MyBinaryDependencyTests.swift | 12 + .../SPMTest/MyCommonLibrary/.gitignore | 8 + .../SPMTest/MyCommonLibrary/Package.swift | 23 ++ .../MyCommonLibrary/MyCommonLibrary.swift | 6 + .../MyCommonLibraryTests.swift | 12 + .../PIFCaches/SPMTest/MyLibrary/.gitignore | 8 + .../PIFCaches/SPMTest/MyLibrary/Package.swift | 33 ++ .../Sources/MyLibrary/MyLibrary.swift | 10 + .../Tests/MyLibraryTests/MyLibraryTests.swift | 12 + .../SPMTest/MyTransitiveLibrary/.gitignore | 8 + .../SPMTest/MyTransitiveLibrary/Package.swift | 33 ++ .../MyTransitiveLibrary.swift | 10 + .../MyTransitiveLibraryTests.swift | 12 + ...hash=217e2718d5748fe803db10bf7703f87e-json | 192 +++++++++ ...hash=28350dce3dbf6c3324256d2ef84c721e-json | 207 ++++++++++ ...hash=60e18114455190230d62ad086fd6a1d5-json | 221 ++++++++++ ...hash=d061ee81d8927d5b63443e777e83938a-json | 192 +++++++++ ...ins=1OJSG6M1FOV3XYQCBH7Z29RZ0FPR9XDE1-json | 192 +++++++++ ...hash=d5012c60a72570c6ac682d8666f0e4a1-json | 137 +++++++ ...hash=27eb3a98abb5101fe49563a2824e9794-json | 128 ++++++ ...hash=322b71ca67a33085a0152ac83f23a35f-json | 92 +++++ ...hash=5c5fac44a4728a01cbe089b857eb0636-json | 128 ++++++ ...hash=74b26d184242037107e47412d7443f08-json | 68 +++ ...hash=7fe91a1e2b8f90031d23796421a46a09-json | 50 +++ ...hash=85e5507488bfe17e9e4e18b38ecf6a38-json | 110 +++++ ...hash=8de09bc5397dcfbae930a9e8ab952e62-json | 125 ++++++ ...hash=9b39f33b643d76adea258dfa2de06201-json | 137 +++++++ ...hash=c5eeab26a0e301a2419ee7e6d1254b52-json | 85 ++++ ...hash=cd6e9e040a1e22f4a8571a7087b8628e-json | 103 +++++ ...hash=da546fca3b585ddf35a5a7d6d76bf04c-json | 57 +++ ...hash=e0ad90d36af3737ec5215a32f000294d-json | 125 ++++++ ...hash=e6cf06600384b639e73079abd6a4553c-json | 150 +++++++ ...hash=ec86d6a4683c2028c68c9a06f9947bf3-json | 157 +++++++ ...hash=f6b1fc5ff8b0c87e4d17a05b612118a8-json | 150 +++++++ ...hash=fb7de2da6bb6f870c73ba0c83581f638-json | 75 ++++ ...ects=f62a50b6ced3d59774fc6ba5a392ffaa-json | 12 + .../SPMTest/SPMTest.xcodeproj/project.pbxproj | 387 ++++++++++++++++++ .../contents.xcworkspacedata | 7 + .../xcshareddata/IDEWorkspaceChecks.plist | 8 + .../AccentColor.colorset/Contents.json | 11 + .../AppIcon.appiconset/Contents.json | 13 + .../SPMTest/Assets.xcassets/Contents.json | 6 + .../SPMTest/SPMTest/ContentView.swift | 26 ++ .../Preview Assets.xcassets/Contents.json | 6 + .../SPMTest/SPMTest/SPMTestApp.swift | 17 + Tests/GenIRTests/PIFCacheTests.swift | 35 ++ 52 files changed, 3683 insertions(+), 40 deletions(-) create mode 100644 TestAssets/PIFCaches/SPMTest/MyBinaryDependency/.gitignore create mode 100644 TestAssets/PIFCaches/SPMTest/MyBinaryDependency/Package.swift create mode 100644 TestAssets/PIFCaches/SPMTest/MyBinaryDependency/Sources/_Stub/MyBinaryDependency.swift create mode 100644 TestAssets/PIFCaches/SPMTest/MyBinaryDependency/Tests/MyBinaryDependencyTests/MyBinaryDependencyTests.swift create mode 100644 TestAssets/PIFCaches/SPMTest/MyCommonLibrary/.gitignore create mode 100644 TestAssets/PIFCaches/SPMTest/MyCommonLibrary/Package.swift create mode 100644 TestAssets/PIFCaches/SPMTest/MyCommonLibrary/Sources/MyCommonLibrary/MyCommonLibrary.swift create mode 100644 TestAssets/PIFCaches/SPMTest/MyCommonLibrary/Tests/MyCommonLibraryTests/MyCommonLibraryTests.swift create mode 100644 TestAssets/PIFCaches/SPMTest/MyLibrary/.gitignore create mode 100644 TestAssets/PIFCaches/SPMTest/MyLibrary/Package.swift create mode 100644 TestAssets/PIFCaches/SPMTest/MyLibrary/Sources/MyLibrary/MyLibrary.swift create mode 100644 TestAssets/PIFCaches/SPMTest/MyLibrary/Tests/MyLibraryTests/MyLibraryTests.swift create mode 100644 TestAssets/PIFCaches/SPMTest/MyTransitiveLibrary/.gitignore create mode 100644 TestAssets/PIFCaches/SPMTest/MyTransitiveLibrary/Package.swift create mode 100644 TestAssets/PIFCaches/SPMTest/MyTransitiveLibrary/Sources/MyTransitiveLibrary/MyTransitiveLibrary.swift create mode 100644 TestAssets/PIFCaches/SPMTest/MyTransitiveLibrary/Tests/MyTransitiveLibraryTests/MyTransitiveLibraryTests.swift create mode 100644 TestAssets/PIFCaches/SPMTest/PIFCache/project/PACKAGE@v12_hash=217e2718d5748fe803db10bf7703f87e-json create mode 100644 TestAssets/PIFCaches/SPMTest/PIFCache/project/PACKAGE@v12_hash=28350dce3dbf6c3324256d2ef84c721e-json create mode 100644 TestAssets/PIFCaches/SPMTest/PIFCache/project/PACKAGE@v12_hash=60e18114455190230d62ad086fd6a1d5-json create mode 100644 TestAssets/PIFCaches/SPMTest/PIFCache/project/PACKAGE@v12_hash=d061ee81d8927d5b63443e777e83938a-json create mode 100644 TestAssets/PIFCaches/SPMTest/PIFCache/project/PROJECT@v11_mod=1716537774.576404_hash=0f958f39499d9f0b6b454948d2be549bplugins=1OJSG6M1FOV3XYQCBH7Z29RZ0FPR9XDE1-json create mode 100644 TestAssets/PIFCaches/SPMTest/PIFCache/target/TARGET@v11_hash=d5012c60a72570c6ac682d8666f0e4a1-json create mode 100644 TestAssets/PIFCaches/SPMTest/PIFCache/target/TARGET@v12_hash=27eb3a98abb5101fe49563a2824e9794-json create mode 100644 TestAssets/PIFCaches/SPMTest/PIFCache/target/TARGET@v12_hash=322b71ca67a33085a0152ac83f23a35f-json create mode 100644 TestAssets/PIFCaches/SPMTest/PIFCache/target/TARGET@v12_hash=5c5fac44a4728a01cbe089b857eb0636-json create mode 100644 TestAssets/PIFCaches/SPMTest/PIFCache/target/TARGET@v12_hash=74b26d184242037107e47412d7443f08-json create mode 100644 TestAssets/PIFCaches/SPMTest/PIFCache/target/TARGET@v12_hash=7fe91a1e2b8f90031d23796421a46a09-json create mode 100644 TestAssets/PIFCaches/SPMTest/PIFCache/target/TARGET@v12_hash=85e5507488bfe17e9e4e18b38ecf6a38-json create mode 100644 TestAssets/PIFCaches/SPMTest/PIFCache/target/TARGET@v12_hash=8de09bc5397dcfbae930a9e8ab952e62-json create mode 100644 TestAssets/PIFCaches/SPMTest/PIFCache/target/TARGET@v12_hash=9b39f33b643d76adea258dfa2de06201-json create mode 100644 TestAssets/PIFCaches/SPMTest/PIFCache/target/TARGET@v12_hash=c5eeab26a0e301a2419ee7e6d1254b52-json create mode 100644 TestAssets/PIFCaches/SPMTest/PIFCache/target/TARGET@v12_hash=cd6e9e040a1e22f4a8571a7087b8628e-json create mode 100644 TestAssets/PIFCaches/SPMTest/PIFCache/target/TARGET@v12_hash=da546fca3b585ddf35a5a7d6d76bf04c-json create mode 100644 TestAssets/PIFCaches/SPMTest/PIFCache/target/TARGET@v12_hash=e0ad90d36af3737ec5215a32f000294d-json create mode 100644 TestAssets/PIFCaches/SPMTest/PIFCache/target/TARGET@v12_hash=e6cf06600384b639e73079abd6a4553c-json create mode 100644 TestAssets/PIFCaches/SPMTest/PIFCache/target/TARGET@v12_hash=ec86d6a4683c2028c68c9a06f9947bf3-json create mode 100644 TestAssets/PIFCaches/SPMTest/PIFCache/target/TARGET@v12_hash=f6b1fc5ff8b0c87e4d17a05b612118a8-json create mode 100644 TestAssets/PIFCaches/SPMTest/PIFCache/target/TARGET@v12_hash=fb7de2da6bb6f870c73ba0c83581f638-json create mode 100644 TestAssets/PIFCaches/SPMTest/PIFCache/workspace/WORKSPACE@v11_hash=e6e1ff1ce1d8441dff7731e4f843ec43_subobjects=f62a50b6ced3d59774fc6ba5a392ffaa-json create mode 100644 TestAssets/PIFCaches/SPMTest/SPMTest.xcodeproj/project.pbxproj create mode 100644 TestAssets/PIFCaches/SPMTest/SPMTest.xcodeproj/project.xcworkspace/contents.xcworkspacedata create mode 100644 TestAssets/PIFCaches/SPMTest/SPMTest.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist create mode 100644 TestAssets/PIFCaches/SPMTest/SPMTest/Assets.xcassets/AccentColor.colorset/Contents.json create mode 100644 TestAssets/PIFCaches/SPMTest/SPMTest/Assets.xcassets/AppIcon.appiconset/Contents.json create mode 100644 TestAssets/PIFCaches/SPMTest/SPMTest/Assets.xcassets/Contents.json create mode 100644 TestAssets/PIFCaches/SPMTest/SPMTest/ContentView.swift create mode 100644 TestAssets/PIFCaches/SPMTest/SPMTest/Preview Content/Preview Assets.xcassets/Contents.json create mode 100644 TestAssets/PIFCaches/SPMTest/SPMTest/SPMTestApp.swift create mode 100644 Tests/GenIRTests/PIFCacheTests.swift diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index d4c66cc..f0ee536 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -7,11 +7,11 @@ on: jobs: build: - runs-on: macos-13 + runs-on: macos-latest steps: - uses: actions/checkout@v3 - name: 🔨 Build run: | - sudo xcode-select -s /Applications/Xcode_14.2.app/ + sudo xcode-select -s /Applications/Xcode_15.4.app/ swift build \ No newline at end of file diff --git a/PIF/Sources/PIFSupport/PIFSupport.swift b/PIF/Sources/PIFSupport/PIFSupport.swift index 46b1857..dedfd15 100644 --- a/PIF/Sources/PIFSupport/PIFSupport.swift +++ b/PIF/Sources/PIFSupport/PIFSupport.swift @@ -9,6 +9,7 @@ public class PIFParser { public enum Error: Swift.Error { case workspaceNotFound + case filesystemError(Swift.Error) } public init(cachePath: URL, logger log: Logger) throws { @@ -25,13 +26,33 @@ public class PIFParser { let workspaces = try FileManager.default.contentsOfDirectory(at: path, includingPropertiesForKeys: [.isRegularFileKey]) .filter { $0.lastPathComponent.starts(with: "WORKSPACE@") } - precondition(workspaces.count == 1, "Encountered more than one workspace - it is expected that a single workspace exists: \(workspaces)") - - guard workspaces.count > 0 else { + guard !workspaces.isEmpty else { throw Error.workspaceNotFound } - return workspaces[0] + if workspaces.count == 1 { + return workspaces[0] + } + + // If multiple workspaces exist, it's because the something in the project changed between builds. Sort workspaces by the most recent. + func modificationDate(_ path: URL) -> Date { + (try? FileManager.default.attributesOfItem(atPath: path.path)[.modificationDate] as? Date) ?? Date() + } + + logger.debug("Found multiple workspaces, sorting by modification date and returning most recently modified workspace") + + let workspacesAndDates = workspaces + .map { + (modificationDate($0), $0) + } + + logger.debug("Comparing dates and workspaces: ") + workspacesAndDates.forEach { logger.debug("\($0) - \($1)") } + + return workspacesAndDates + .sorted { + $0.0 > $1.0 + }[0].1 } } diff --git a/Sources/GenIR/PIFCache.swift b/Sources/GenIR/PIFCache.swift index 6737d8a..e24f0e2 100644 --- a/Sources/GenIR/PIFCache.swift +++ b/Sources/GenIR/PIFCache.swift @@ -53,19 +53,6 @@ class PIFCache { workspace.projects } - // private lazy var projectsByGUID: [GUID: PIF.Project] = { - // workspace - // .projects - // .reduce(into: [GUID: PIF.Project]()) { result, element in - // result[element.guid] = element - // } - // }() - - // func project(for guid: GUID) -> PIF.Project? { - // projectsByGUID[guid] - // } - - // TODO: We cab possibly filter out some targets here for performance var targets: [PIF.BaseTarget] { workspace .projects @@ -110,35 +97,22 @@ class PIFCache { let frameworkFileReferences = projects .flatMap { fileReferences(for: $0) } .filter { $0.fileType == "wrapper.framework" } - // TODO: do we filter on sourceTree == "BUILT_PRODUCTS_DIR" here too? // Now, stupidly, we have to do a name lookup on the path and use that to look up a target let frameworks = targets .compactMap { $0 as? PIF.Target } .filter { $0.productType == .framework } .reduce(into: [String: PIF.Target]()) { partial, target in - partial[target.productName] = target + let key = target.productName.isEmpty ? target.guid : target.productName + partial[key] = target } return frameworkFileReferences - // .compactMap { frameworks[$0.path] } // TODO: I think we should get the last path component as the key here - check that .reduce(into: [PIF.GUID: PIF.Target]()) { partial, fileReference in - // partial[target.guid] = target // Use the _file reference_ GUID as the key here - we're looking up frameworks by their file reference and not target GUID! partial[fileReference.guid] = frameworks[fileReference.path] } }() - - // private lazy var targetsByGUID: [GUID: PIF.BaseTarget] = { - // targets - // .reduce(into: [GUID: PIF.BaseTarget]()) { result, element in - // result[element.guid] = element - // } - // }() - - // func target(for guid: GUID) -> PIF.BaseTarget? { - // targetsByGUID[guid] - // } } extension PIF.BaseTarget: Hashable { @@ -166,20 +140,33 @@ struct PIFDependencyProvider: DependencyProviding { } } - private func resolveSwiftPackage(_ packageGUID: PIF.GUID) -> PIF.GUID { + private func resolveSwiftPackage(_ packageGUID: PIF.GUID) -> PIF.GUID? { let productToken = "PACKAGE-PRODUCT:" let targetToken = "PACKAGE-TARGET:" guard packageGUID.starts(with: productToken), let product = guidToTargets[packageGUID] else { return packageGUID } let productName = String(packageGUID.dropFirst(productToken.count)) - // TODO: should this also use the framework build phase to determine a dependency? - let packageTargetDependencies = product + // TODO: should this also use the framework build phase to determine a dependency? Currently not needed because Gen IR doesn't care about prebuilt frameworks + // but for the completeness of the graph this could be a nice to have... + let dependencies = product .baseTarget .dependencies .filter { $0.targetGUID.starts(with: targetToken) } + + let packageTargetDependencies = dependencies .filter { $0.targetGUID.dropFirst(targetToken.count) == productName } + if packageTargetDependencies.isEmpty && !dependencies.isEmpty { + // We likely have a stub target here (i.e. a precompiled framework) + // see https://github.com/apple/swift-package-manager/issues/6069 for more + logger.debug("Resolving Swift Package (\(productName) - \(packageGUID)) resulted in no targets. Possible stub target in: \(dependencies)") + return nil + } else if packageTargetDependencies.isEmpty && dependencies.isEmpty { + logger.debug("Resolving Swift Package (\(productName) - \(packageGUID)) resulted in no targets.") + return nil + } + precondition(packageTargetDependencies.count == 1, "Expecting one matching package target - found \(packageTargetDependencies.count): \(packageTargetDependencies). Returning first match") return packageTargetDependencies.first?.targetGUID ?? packageGUID @@ -191,7 +178,7 @@ struct PIFDependencyProvider: DependencyProviding { .baseTarget .dependencies .map { $0.targetGUID } - .map { resolveSwiftPackage($0) } + .compactMap { resolveSwiftPackage($0) } // Framework build phase dependencies let frameworkBuildPhases = value @@ -199,7 +186,7 @@ struct PIFDependencyProvider: DependencyProviding { .buildPhases .compactMap { $0 as? PIF.FrameworksBuildPhase } - let frameworkGUIDs = frameworkBuildPhases + let referenceGUIDs = frameworkBuildPhases .flatMap { $0.buildFiles } .compactMap { switch $0.reference { @@ -207,7 +194,11 @@ struct PIFDependencyProvider: DependencyProviding { case .target: return nil // TODO: is this fine? I think so since we're looking for .framework file references here not targets which should be a dependency } } - .compactMap { cache.frameworks[$0]?.guid } + + let frameworkGUIDs = referenceGUIDs + .compactMap { + cache.frameworks[$0]?.guid + } let dependencyTargets = (dependencyTargetGUIDs + frameworkGUIDs).compactMap { guidToTargets[$0] } diff --git a/TestAssets/PIFCaches/SPMTest/MyBinaryDependency/.gitignore b/TestAssets/PIFCaches/SPMTest/MyBinaryDependency/.gitignore new file mode 100644 index 0000000..0023a53 --- /dev/null +++ b/TestAssets/PIFCaches/SPMTest/MyBinaryDependency/.gitignore @@ -0,0 +1,8 @@ +.DS_Store +/.build +/Packages +xcuserdata/ +DerivedData/ +.swiftpm/configuration/registries.json +.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata +.netrc diff --git a/TestAssets/PIFCaches/SPMTest/MyBinaryDependency/Package.swift b/TestAssets/PIFCaches/SPMTest/MyBinaryDependency/Package.swift new file mode 100644 index 0000000..a3dd03d --- /dev/null +++ b/TestAssets/PIFCaches/SPMTest/MyBinaryDependency/Package.swift @@ -0,0 +1,25 @@ +// swift-tools-version: 5.10 +// The swift-tools-version declares the minimum version of Swift required to build this package. + +import PackageDescription + +let lottieXCFramework = Target.binaryTarget( + name: "MyBinaryDependency", + url: "https://github.com/airbnb/lottie-ios/releases/download/4.4.3/Lottie-Xcode-15.2.xcframework.zip", + checksum: "546b7e718ed806646b84645ecfb1e1d6a65ac0387ff3f8ecb92dbaf2116cd62c") + +let package = Package( + name: "MyBinaryDependency", + products: [ + // Products define the executables and libraries a package produces, making them visible to other packages. + .library( + name: "MyBinaryDependency", + targets: ["MyBinaryDependency"/*, "_Stub"*/]), + ], + targets: [ + // Targets are the basic building blocks of a package, defining a module or a test suite. + // Targets can depend on other targets in this package and products from dependencies. + lottieXCFramework, +// .target(name: "_Stub"), + ] +) diff --git a/TestAssets/PIFCaches/SPMTest/MyBinaryDependency/Sources/_Stub/MyBinaryDependency.swift b/TestAssets/PIFCaches/SPMTest/MyBinaryDependency/Sources/_Stub/MyBinaryDependency.swift new file mode 100644 index 0000000..08b22b8 --- /dev/null +++ b/TestAssets/PIFCaches/SPMTest/MyBinaryDependency/Sources/_Stub/MyBinaryDependency.swift @@ -0,0 +1,2 @@ +// The Swift Programming Language +// https://docs.swift.org/swift-book diff --git a/TestAssets/PIFCaches/SPMTest/MyBinaryDependency/Tests/MyBinaryDependencyTests/MyBinaryDependencyTests.swift b/TestAssets/PIFCaches/SPMTest/MyBinaryDependency/Tests/MyBinaryDependencyTests/MyBinaryDependencyTests.swift new file mode 100644 index 0000000..ff3f482 --- /dev/null +++ b/TestAssets/PIFCaches/SPMTest/MyBinaryDependency/Tests/MyBinaryDependencyTests/MyBinaryDependencyTests.swift @@ -0,0 +1,12 @@ +import XCTest +@testable import MyBinaryDependency + +final class MyBinaryDependencyTests: XCTestCase { + func testExample() throws { + // XCTest Documentation + // https://developer.apple.com/documentation/xctest + + // Defining Test Cases and Test Methods + // https://developer.apple.com/documentation/xctest/defining_test_cases_and_test_methods + } +} diff --git a/TestAssets/PIFCaches/SPMTest/MyCommonLibrary/.gitignore b/TestAssets/PIFCaches/SPMTest/MyCommonLibrary/.gitignore new file mode 100644 index 0000000..0023a53 --- /dev/null +++ b/TestAssets/PIFCaches/SPMTest/MyCommonLibrary/.gitignore @@ -0,0 +1,8 @@ +.DS_Store +/.build +/Packages +xcuserdata/ +DerivedData/ +.swiftpm/configuration/registries.json +.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata +.netrc diff --git a/TestAssets/PIFCaches/SPMTest/MyCommonLibrary/Package.swift b/TestAssets/PIFCaches/SPMTest/MyCommonLibrary/Package.swift new file mode 100644 index 0000000..1729829 --- /dev/null +++ b/TestAssets/PIFCaches/SPMTest/MyCommonLibrary/Package.swift @@ -0,0 +1,23 @@ +// swift-tools-version: 5.10 +// The swift-tools-version declares the minimum version of Swift required to build this package. + +import PackageDescription + +let package = Package( + name: "MyCommonLibrary", + products: [ + // Products define the executables and libraries a package produces, making them visible to other packages. + .library( + name: "MyCommonLibrary", + targets: ["MyCommonLibrary"]), + ], + targets: [ + // Targets are the basic building blocks of a package, defining a module or a test suite. + // Targets can depend on other targets in this package and products from dependencies. + .target( + name: "MyCommonLibrary"), + .testTarget( + name: "MyCommonLibraryTests", + dependencies: ["MyCommonLibrary"]), + ] +) diff --git a/TestAssets/PIFCaches/SPMTest/MyCommonLibrary/Sources/MyCommonLibrary/MyCommonLibrary.swift b/TestAssets/PIFCaches/SPMTest/MyCommonLibrary/Sources/MyCommonLibrary/MyCommonLibrary.swift new file mode 100644 index 0000000..5cf8945 --- /dev/null +++ b/TestAssets/PIFCaches/SPMTest/MyCommonLibrary/Sources/MyCommonLibrary/MyCommonLibrary.swift @@ -0,0 +1,6 @@ +// The Swift Programming Language +// https://docs.swift.org/swift-book + +public struct MyCommonLibrary { + public static let common = "CommonLibrary" +} diff --git a/TestAssets/PIFCaches/SPMTest/MyCommonLibrary/Tests/MyCommonLibraryTests/MyCommonLibraryTests.swift b/TestAssets/PIFCaches/SPMTest/MyCommonLibrary/Tests/MyCommonLibraryTests/MyCommonLibraryTests.swift new file mode 100644 index 0000000..9bf54fe --- /dev/null +++ b/TestAssets/PIFCaches/SPMTest/MyCommonLibrary/Tests/MyCommonLibraryTests/MyCommonLibraryTests.swift @@ -0,0 +1,12 @@ +import XCTest +@testable import MyCommonLibrary + +final class MyCommonLibraryTests: XCTestCase { + func testExample() throws { + // XCTest Documentation + // https://developer.apple.com/documentation/xctest + + // Defining Test Cases and Test Methods + // https://developer.apple.com/documentation/xctest/defining_test_cases_and_test_methods + } +} diff --git a/TestAssets/PIFCaches/SPMTest/MyLibrary/.gitignore b/TestAssets/PIFCaches/SPMTest/MyLibrary/.gitignore new file mode 100644 index 0000000..0023a53 --- /dev/null +++ b/TestAssets/PIFCaches/SPMTest/MyLibrary/.gitignore @@ -0,0 +1,8 @@ +.DS_Store +/.build +/Packages +xcuserdata/ +DerivedData/ +.swiftpm/configuration/registries.json +.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata +.netrc diff --git a/TestAssets/PIFCaches/SPMTest/MyLibrary/Package.swift b/TestAssets/PIFCaches/SPMTest/MyLibrary/Package.swift new file mode 100644 index 0000000..9b5dea4 --- /dev/null +++ b/TestAssets/PIFCaches/SPMTest/MyLibrary/Package.swift @@ -0,0 +1,33 @@ +// swift-tools-version: 5.10 +// The swift-tools-version declares the minimum version of Swift required to build this package. + +import PackageDescription + +let package = Package( + name: "MyLibrary", + platforms: [.iOS(.v14)], + products: [ + // Products define the executables and libraries a package produces, making them visible to other packages. + .library( + name: "MyLibrary", + targets: ["MyLibrary"]), + ], + dependencies: [ + .package(path: "../MyTransitiveLibrary"), + .package(path: "../MyCommonLibrary") + ], + targets: [ + // Targets are the basic building blocks of a package, defining a module or a test suite. + // Targets can depend on other targets in this package and products from dependencies. + .target( + name: "MyLibrary", + dependencies: [ + .product(name: "MyTransitiveLibrary", package: "MyTransitiveLibrary"), + .product(name: "MyCommonLibrary", package: "MyCommonLibrary") + ] + ), + .testTarget( + name: "MyLibraryTests", + dependencies: ["MyLibrary"]), + ] +) diff --git a/TestAssets/PIFCaches/SPMTest/MyLibrary/Sources/MyLibrary/MyLibrary.swift b/TestAssets/PIFCaches/SPMTest/MyLibrary/Sources/MyLibrary/MyLibrary.swift new file mode 100644 index 0000000..2b9ac7a --- /dev/null +++ b/TestAssets/PIFCaches/SPMTest/MyLibrary/Sources/MyLibrary/MyLibrary.swift @@ -0,0 +1,10 @@ +// The Swift Programming Language +// https://docs.swift.org/swift-book + +import MyTransitiveLibrary +import MyCommonLibrary + +public struct MyLibrary { + public static let version = "1.0.0, \(MyTransitiveLibrary.test) - \(MyCommonLibrary.common)" + public static let view = MyTransitiveLibrary.lottie +} diff --git a/TestAssets/PIFCaches/SPMTest/MyLibrary/Tests/MyLibraryTests/MyLibraryTests.swift b/TestAssets/PIFCaches/SPMTest/MyLibrary/Tests/MyLibraryTests/MyLibraryTests.swift new file mode 100644 index 0000000..22e954f --- /dev/null +++ b/TestAssets/PIFCaches/SPMTest/MyLibrary/Tests/MyLibraryTests/MyLibraryTests.swift @@ -0,0 +1,12 @@ +import XCTest +@testable import MyLibrary + +final class MyLibraryTests: XCTestCase { + func testExample() throws { + // XCTest Documentation + // https://developer.apple.com/documentation/xctest + + // Defining Test Cases and Test Methods + // https://developer.apple.com/documentation/xctest/defining_test_cases_and_test_methods + } +} diff --git a/TestAssets/PIFCaches/SPMTest/MyTransitiveLibrary/.gitignore b/TestAssets/PIFCaches/SPMTest/MyTransitiveLibrary/.gitignore new file mode 100644 index 0000000..0023a53 --- /dev/null +++ b/TestAssets/PIFCaches/SPMTest/MyTransitiveLibrary/.gitignore @@ -0,0 +1,8 @@ +.DS_Store +/.build +/Packages +xcuserdata/ +DerivedData/ +.swiftpm/configuration/registries.json +.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata +.netrc diff --git a/TestAssets/PIFCaches/SPMTest/MyTransitiveLibrary/Package.swift b/TestAssets/PIFCaches/SPMTest/MyTransitiveLibrary/Package.swift new file mode 100644 index 0000000..924288c --- /dev/null +++ b/TestAssets/PIFCaches/SPMTest/MyTransitiveLibrary/Package.swift @@ -0,0 +1,33 @@ +// swift-tools-version: 5.10 +// The swift-tools-version declares the minimum version of Swift required to build this package. + +import PackageDescription + +let package = Package( + name: "MyTransitiveLibrary", + platforms: [.iOS(.v14)], + products: [ + // Products define the executables and libraries a package produces, making them visible to other packages. + .library( + name: "MyTransitiveLibrary", + targets: ["MyTransitiveLibrary"]), + ], + dependencies: [ + .package(path: "../MyCommonLibrary"), + .package(path: "../MyBinaryDependency") + ], + targets: [ + // Targets are the basic building blocks of a package, defining a module or a test suite. + // Targets can depend on other targets in this package and products from dependencies. + .target( + name: "MyTransitiveLibrary", + dependencies: [ + .product(name: "MyCommonLibrary", package: "MyCommonLibrary"), + .product(name: "MyBinaryDependency", package: "MyBinaryDependency") + ] + ), + .testTarget( + name: "MyTransitiveLibraryTests", + dependencies: ["MyTransitiveLibrary"]), + ] +) diff --git a/TestAssets/PIFCaches/SPMTest/MyTransitiveLibrary/Sources/MyTransitiveLibrary/MyTransitiveLibrary.swift b/TestAssets/PIFCaches/SPMTest/MyTransitiveLibrary/Sources/MyTransitiveLibrary/MyTransitiveLibrary.swift new file mode 100644 index 0000000..993d620 --- /dev/null +++ b/TestAssets/PIFCaches/SPMTest/MyTransitiveLibrary/Sources/MyTransitiveLibrary/MyTransitiveLibrary.swift @@ -0,0 +1,10 @@ +// The Swift Programming Language +// https://docs.swift.org/swift-book + +import MyCommonLibrary +@_exported import Lottie + +public struct MyTransitiveLibrary { + public static let test = "This is a test \(MyCommonLibrary.common)" + public static let lottie = LottieAnimationView() +} diff --git a/TestAssets/PIFCaches/SPMTest/MyTransitiveLibrary/Tests/MyTransitiveLibraryTests/MyTransitiveLibraryTests.swift b/TestAssets/PIFCaches/SPMTest/MyTransitiveLibrary/Tests/MyTransitiveLibraryTests/MyTransitiveLibraryTests.swift new file mode 100644 index 0000000..08ffe2c --- /dev/null +++ b/TestAssets/PIFCaches/SPMTest/MyTransitiveLibrary/Tests/MyTransitiveLibraryTests/MyTransitiveLibraryTests.swift @@ -0,0 +1,12 @@ +import XCTest +@testable import MyTransitiveLibrary + +final class MyTransitiveLibraryTests: XCTestCase { + func testExample() throws { + // XCTest Documentation + // https://developer.apple.com/documentation/xctest + + // Defining Test Cases and Test Methods + // https://developer.apple.com/documentation/xctest/defining_test_cases_and_test_methods + } +} diff --git a/TestAssets/PIFCaches/SPMTest/PIFCache/project/PACKAGE@v12_hash=217e2718d5748fe803db10bf7703f87e-json b/TestAssets/PIFCaches/SPMTest/PIFCache/project/PACKAGE@v12_hash=217e2718d5748fe803db10bf7703f87e-json new file mode 100644 index 0000000..a6f0cc4 --- /dev/null +++ b/TestAssets/PIFCaches/SPMTest/PIFCache/project/PACKAGE@v12_hash=217e2718d5748fe803db10bf7703f87e-json @@ -0,0 +1,192 @@ +{ + "buildConfigurations": [ + { + "buildSettings": { + "CLANG_ENABLE_OBJC_ARC": "YES", + "CODE_SIGNING_REQUIRED": "NO", + "CODE_SIGN_IDENTITY": "", + "COPY_PHASE_STRIP": "NO", + "DEBUG_INFORMATION_FORMAT": "dwarf", + "DRIVERKIT_DEPLOYMENT_TARGET": "19.0", + "DYLIB_INSTALL_NAME_BASE": "@rpath", + "ENABLE_NS_ASSERTIONS": "YES", + "ENABLE_TESTABILITY": "YES", + "ENABLE_TESTING_SEARCH_PATHS": "YES", + "ENTITLEMENTS_REQUIRED": "NO", + "GCC_OPTIMIZATION_LEVEL": "0", + "GCC_PREPROCESSOR_DEFINITIONS": [ + "$(inherited)", + "SWIFT_PACKAGE", + "DEBUG=1" + ], + "IPHONEOS_DEPLOYMENT_TARGET": "12.0", + "IPHONEOS_DEPLOYMENT_TARGET[__platform_filter=ios-maccatalyst]": [ + "13.0" + ], + "KEEP_PRIVATE_EXTERNS": "NO", + "MACOSX_DEPLOYMENT_TARGET": "10.13", + "ONLY_ACTIVE_ARCH": "YES", + "OTHER_CFLAGS": [ + "$(inherited)", + "-DXcode" + ], + "OTHER_LDRFLAGS": [], + "OTHER_SWIFT_FLAGS": [ + "$(inherited)", + "-DXcode" + ], + "PRODUCT_NAME": "$(TARGET_NAME)", + "SKIP_CLANG_STATIC_ANALYZER": "YES", + "SKIP_INSTALL": "YES", + "SUPPORTED_PLATFORMS": [ + "$(AVAILABLE_PLATFORMS)" + ], + "SUPPRESS_WARNINGS": "YES", + "SWIFT_ACTIVE_COMPILATION_CONDITIONS": [ + "$(inherited)", + "SWIFT_PACKAGE", + "DEBUG" + ], + "SWIFT_INSTALL_OBJC_HEADER": "NO", + "SWIFT_OBJC_INTERFACE_HEADER_NAME": "", + "SWIFT_OPTIMIZATION_LEVEL": "-Onone", + "TARGETED_DEVICE_FAMILY": "1,2,3,4,6,7", + "TVOS_DEPLOYMENT_TARGET": "12.0", + "USE_HEADERMAP": "NO", + "WATCHOS_DEPLOYMENT_TARGET": "5.0", + "XROS_DEPLOYMENT_TARGET": "1.0" + }, + "guid": "PACKAGE:/Users/thedderwick/Desktop/SPMTest/MyLibrary::BUILDCONFIG_0", + "impartedBuildProperties": { + "buildSettings": {} + }, + "name": "Debug" + }, + { + "buildSettings": { + "CLANG_ENABLE_OBJC_ARC": "YES", + "CODE_SIGNING_REQUIRED": "NO", + "CODE_SIGN_IDENTITY": "", + "COPY_PHASE_STRIP": "YES", + "DEBUG_INFORMATION_FORMAT": "dwarf-with-dsym", + "DRIVERKIT_DEPLOYMENT_TARGET": "19.0", + "DYLIB_INSTALL_NAME_BASE": "@rpath", + "ENABLE_TESTING_SEARCH_PATHS": "YES", + "ENTITLEMENTS_REQUIRED": "NO", + "GCC_OPTIMIZATION_LEVEL": "s", + "GCC_PREPROCESSOR_DEFINITIONS": [ + "$(inherited)", + "SWIFT_PACKAGE" + ], + "IPHONEOS_DEPLOYMENT_TARGET": "12.0", + "IPHONEOS_DEPLOYMENT_TARGET[__platform_filter=ios-maccatalyst]": [ + "13.0" + ], + "KEEP_PRIVATE_EXTERNS": "NO", + "MACOSX_DEPLOYMENT_TARGET": "10.13", + "OTHER_CFLAGS": [ + "$(inherited)", + "-DXcode" + ], + "OTHER_LDRFLAGS": [], + "OTHER_SWIFT_FLAGS": [ + "$(inherited)", + "-DXcode" + ], + "PRODUCT_NAME": "$(TARGET_NAME)", + "SKIP_CLANG_STATIC_ANALYZER": "YES", + "SKIP_INSTALL": "YES", + "SUPPORTED_PLATFORMS": [ + "$(AVAILABLE_PLATFORMS)" + ], + "SUPPRESS_WARNINGS": "YES", + "SWIFT_ACTIVE_COMPILATION_CONDITIONS": [ + "$(inherited)", + "SWIFT_PACKAGE" + ], + "SWIFT_INSTALL_OBJC_HEADER": "NO", + "SWIFT_OBJC_INTERFACE_HEADER_NAME": "", + "SWIFT_OPTIMIZATION_LEVEL": "-Owholemodule", + "TARGETED_DEVICE_FAMILY": "1,2,3,4,6,7", + "TVOS_DEPLOYMENT_TARGET": "12.0", + "USE_HEADERMAP": "NO", + "WATCHOS_DEPLOYMENT_TARGET": "5.0", + "XROS_DEPLOYMENT_TARGET": "1.0" + }, + "guid": "PACKAGE:/Users/thedderwick/Desktop/SPMTest/MyLibrary::BUILDCONFIG_1", + "impartedBuildProperties": { + "buildSettings": {} + }, + "name": "Release" + } + ], + "defaultConfigurationName": "Release", + "groupTree": { + "children": [ + { + "children": [], + "guid": "PACKAGE:/Users/thedderwick/Desktop/SPMTest/MyLibrary::MAINGROUP::REF_0", + "name": "AdditionalFiles", + "path": "/", + "sourceTree": "", + "type": "group" + }, + { + "children": [], + "guid": "PACKAGE:/Users/thedderwick/Desktop/SPMTest/MyLibrary::MAINGROUP::REF_1", + "name": "Binaries", + "path": "/", + "sourceTree": "", + "type": "group" + }, + { + "children": [ + { + "fileType": "sourcecode.swift", + "guid": "PACKAGE:/Users/thedderwick/Desktop/SPMTest/MyLibrary::MAINGROUP::REF_2::REF_0", + "path": "MyLibrary.swift", + "sourceTree": "", + "type": "file" + } + ], + "guid": "PACKAGE:/Users/thedderwick/Desktop/SPMTest/MyLibrary::MAINGROUP::REF_2", + "name": "/Users/thedderwick/Desktop/SPMTest/MyLibrary/Sources/MyLibrary", + "path": "/Users/thedderwick/Desktop/SPMTest/MyLibrary/Sources/MyLibrary", + "sourceTree": "", + "type": "group" + }, + { + "children": [ + { + "fileType": "sourcecode.swift", + "guid": "PACKAGE:/Users/thedderwick/Desktop/SPMTest/MyLibrary::MAINGROUP::REF_3::REF_0", + "path": "MyLibrary.swift", + "sourceTree": "", + "type": "file" + } + ], + "guid": "PACKAGE:/Users/thedderwick/Desktop/SPMTest/MyLibrary::MAINGROUP::REF_3", + "name": "/Users/thedderwick/Desktop/SPMTest/MyLibrary/Sources/MyLibrary", + "path": "/Users/thedderwick/Desktop/SPMTest/MyLibrary/Sources/MyLibrary", + "sourceTree": "", + "type": "group" + } + ], + "guid": "PACKAGE:/Users/thedderwick/Desktop/SPMTest/MyLibrary::MAINGROUP", + "name": "", + "path": "", + "sourceTree": "", + "type": "group" + }, + "guid": "PACKAGE:/Users/thedderwick/Desktop/SPMTest/MyLibrary", + "path": "/Users/thedderwick/Desktop/SPMTest/MyLibrary/Package.swift", + "projectDirectory": "/Users/thedderwick/Desktop/SPMTest/MyLibrary", + "projectIsPackage": "true", + "projectName": "MyLibrary", + "targets": [ + "TARGET@v12_hash=74b26d184242037107e47412d7443f08", + "TARGET@v12_hash=cd6e9e040a1e22f4a8571a7087b8628e", + "TARGET@v12_hash=9b39f33b643d76adea258dfa2de06201", + "TARGET@v12_hash=e6cf06600384b639e73079abd6a4553c" + ] +} diff --git a/TestAssets/PIFCaches/SPMTest/PIFCache/project/PACKAGE@v12_hash=28350dce3dbf6c3324256d2ef84c721e-json b/TestAssets/PIFCaches/SPMTest/PIFCache/project/PACKAGE@v12_hash=28350dce3dbf6c3324256d2ef84c721e-json new file mode 100644 index 0000000..0c5bf2c --- /dev/null +++ b/TestAssets/PIFCaches/SPMTest/PIFCache/project/PACKAGE@v12_hash=28350dce3dbf6c3324256d2ef84c721e-json @@ -0,0 +1,207 @@ +{ + "buildConfigurations": [ + { + "buildSettings": { + "CLANG_ENABLE_OBJC_ARC": "YES", + "CODE_SIGNING_REQUIRED": "NO", + "CODE_SIGN_IDENTITY": "", + "COPY_PHASE_STRIP": "NO", + "DEBUG_INFORMATION_FORMAT": "dwarf", + "DRIVERKIT_DEPLOYMENT_TARGET": "19.0", + "DYLIB_INSTALL_NAME_BASE": "@rpath", + "ENABLE_NS_ASSERTIONS": "YES", + "ENABLE_TESTABILITY": "YES", + "ENABLE_TESTING_SEARCH_PATHS": "YES", + "ENTITLEMENTS_REQUIRED": "NO", + "GCC_OPTIMIZATION_LEVEL": "0", + "GCC_PREPROCESSOR_DEFINITIONS": [ + "$(inherited)", + "SWIFT_PACKAGE", + "DEBUG=1" + ], + "IPHONEOS_DEPLOYMENT_TARGET": "12.0", + "IPHONEOS_DEPLOYMENT_TARGET[__platform_filter=ios-maccatalyst]": [ + "13.0" + ], + "KEEP_PRIVATE_EXTERNS": "NO", + "MACOSX_DEPLOYMENT_TARGET": "10.13", + "ONLY_ACTIVE_ARCH": "YES", + "OTHER_CFLAGS": [ + "$(inherited)", + "-DXcode" + ], + "OTHER_LDRFLAGS": [], + "OTHER_SWIFT_FLAGS": [ + "$(inherited)", + "-DXcode" + ], + "PRODUCT_NAME": "$(TARGET_NAME)", + "SKIP_CLANG_STATIC_ANALYZER": "YES", + "SKIP_INSTALL": "YES", + "SUPPORTED_PLATFORMS": [ + "$(AVAILABLE_PLATFORMS)" + ], + "SUPPRESS_WARNINGS": "YES", + "SWIFT_ACTIVE_COMPILATION_CONDITIONS": [ + "$(inherited)", + "SWIFT_PACKAGE", + "DEBUG" + ], + "SWIFT_INSTALL_OBJC_HEADER": "NO", + "SWIFT_OBJC_INTERFACE_HEADER_NAME": "", + "SWIFT_OPTIMIZATION_LEVEL": "-Onone", + "TARGETED_DEVICE_FAMILY": "1,2,3,4,6,7", + "TVOS_DEPLOYMENT_TARGET": "12.0", + "USE_HEADERMAP": "NO", + "WATCHOS_DEPLOYMENT_TARGET": "5.0", + "XROS_DEPLOYMENT_TARGET": "1.0" + }, + "guid": "PACKAGE:/Users/thedderwick/Desktop/SPMTest/MyBinaryDependency::BUILDCONFIG_0", + "impartedBuildProperties": { + "buildSettings": {} + }, + "name": "Debug" + }, + { + "buildSettings": { + "CLANG_ENABLE_OBJC_ARC": "YES", + "CODE_SIGNING_REQUIRED": "NO", + "CODE_SIGN_IDENTITY": "", + "COPY_PHASE_STRIP": "YES", + "DEBUG_INFORMATION_FORMAT": "dwarf-with-dsym", + "DRIVERKIT_DEPLOYMENT_TARGET": "19.0", + "DYLIB_INSTALL_NAME_BASE": "@rpath", + "ENABLE_TESTING_SEARCH_PATHS": "YES", + "ENTITLEMENTS_REQUIRED": "NO", + "GCC_OPTIMIZATION_LEVEL": "s", + "GCC_PREPROCESSOR_DEFINITIONS": [ + "$(inherited)", + "SWIFT_PACKAGE" + ], + "IPHONEOS_DEPLOYMENT_TARGET": "12.0", + "IPHONEOS_DEPLOYMENT_TARGET[__platform_filter=ios-maccatalyst]": [ + "13.0" + ], + "KEEP_PRIVATE_EXTERNS": "NO", + "MACOSX_DEPLOYMENT_TARGET": "10.13", + "OTHER_CFLAGS": [ + "$(inherited)", + "-DXcode" + ], + "OTHER_LDRFLAGS": [], + "OTHER_SWIFT_FLAGS": [ + "$(inherited)", + "-DXcode" + ], + "PRODUCT_NAME": "$(TARGET_NAME)", + "SKIP_CLANG_STATIC_ANALYZER": "YES", + "SKIP_INSTALL": "YES", + "SUPPORTED_PLATFORMS": [ + "$(AVAILABLE_PLATFORMS)" + ], + "SUPPRESS_WARNINGS": "YES", + "SWIFT_ACTIVE_COMPILATION_CONDITIONS": [ + "$(inherited)", + "SWIFT_PACKAGE" + ], + "SWIFT_INSTALL_OBJC_HEADER": "NO", + "SWIFT_OBJC_INTERFACE_HEADER_NAME": "", + "SWIFT_OPTIMIZATION_LEVEL": "-Owholemodule", + "TARGETED_DEVICE_FAMILY": "1,2,3,4,6,7", + "TVOS_DEPLOYMENT_TARGET": "12.0", + "USE_HEADERMAP": "NO", + "WATCHOS_DEPLOYMENT_TARGET": "5.0", + "XROS_DEPLOYMENT_TARGET": "1.0" + }, + "guid": "PACKAGE:/Users/thedderwick/Desktop/SPMTest/MyBinaryDependency::BUILDCONFIG_1", + "impartedBuildProperties": { + "buildSettings": {} + }, + "name": "Release" + } + ], + "defaultConfigurationName": "Release", + "groupTree": { + "children": [ + { + "children": [], + "guid": "PACKAGE:/Users/thedderwick/Desktop/SPMTest/MyBinaryDependency::MAINGROUP::REF_0", + "name": "AdditionalFiles", + "path": "/", + "sourceTree": "", + "type": "group" + }, + { + "children": [ + { + "fileType": "wrapper.xcframework", + "guid": "PACKAGE:/Users/thedderwick/Desktop/SPMTest/MyBinaryDependency::MAINGROUP::REF_1::REF_0", + "path": "/Users/thedderwick/Library/Developer/Xcode/DerivedData/SPMTest-akjbvrfwkrsolpeqhugesmlhclvn/SourcePackages/artifacts/mybinarydependency/MyBinaryDependency/Lottie.xcframework", + "sourceTree": "", + "type": "file" + }, + { + "fileType": "wrapper.xcframework", + "guid": "PACKAGE:/Users/thedderwick/Desktop/SPMTest/MyBinaryDependency::MAINGROUP::REF_1::REF_1", + "path": "/Users/thedderwick/Library/Developer/Xcode/DerivedData/SPMTest-akjbvrfwkrsolpeqhugesmlhclvn/SourcePackages/artifacts/mybinarydependency/MyBinaryDependency/Lottie.xcframework", + "sourceTree": "", + "type": "file" + } + ], + "guid": "PACKAGE:/Users/thedderwick/Desktop/SPMTest/MyBinaryDependency::MAINGROUP::REF_1", + "name": "Binaries", + "path": "/", + "sourceTree": "", + "type": "group" + }, + { + "children": [ + { + "fileType": "sourcecode.swift", + "guid": "PACKAGE:/Users/thedderwick/Desktop/SPMTest/MyBinaryDependency::MAINGROUP::REF_2::REF_0", + "path": "MyBinaryDependency.swift", + "sourceTree": "", + "type": "file" + } + ], + "guid": "PACKAGE:/Users/thedderwick/Desktop/SPMTest/MyBinaryDependency::MAINGROUP::REF_2", + "name": "/Users/thedderwick/Desktop/SPMTest/MyBinaryDependency/Sources/_Stub", + "path": "/Users/thedderwick/Desktop/SPMTest/MyBinaryDependency/Sources/_Stub", + "sourceTree": "", + "type": "group" + }, + { + "children": [ + { + "fileType": "sourcecode.swift", + "guid": "PACKAGE:/Users/thedderwick/Desktop/SPMTest/MyBinaryDependency::MAINGROUP::REF_3::REF_0", + "path": "MyBinaryDependency.swift", + "sourceTree": "", + "type": "file" + } + ], + "guid": "PACKAGE:/Users/thedderwick/Desktop/SPMTest/MyBinaryDependency::MAINGROUP::REF_3", + "name": "/Users/thedderwick/Desktop/SPMTest/MyBinaryDependency/Sources/_Stub", + "path": "/Users/thedderwick/Desktop/SPMTest/MyBinaryDependency/Sources/_Stub", + "sourceTree": "", + "type": "group" + } + ], + "guid": "PACKAGE:/Users/thedderwick/Desktop/SPMTest/MyBinaryDependency::MAINGROUP", + "name": "", + "path": "", + "sourceTree": "", + "type": "group" + }, + "guid": "PACKAGE:/Users/thedderwick/Desktop/SPMTest/MyBinaryDependency", + "path": "/Users/thedderwick/Desktop/SPMTest/MyBinaryDependency/Package.swift", + "projectDirectory": "/Users/thedderwick/Desktop/SPMTest/MyBinaryDependency", + "projectIsPackage": "true", + "projectName": "MyBinaryDependency", + "targets": [ + "TARGET@v12_hash=da546fca3b585ddf35a5a7d6d76bf04c", + "TARGET@v12_hash=322b71ca67a33085a0152ac83f23a35f", + "TARGET@v12_hash=5c5fac44a4728a01cbe089b857eb0636", + "TARGET@v12_hash=8de09bc5397dcfbae930a9e8ab952e62" + ] +} diff --git a/TestAssets/PIFCaches/SPMTest/PIFCache/project/PACKAGE@v12_hash=60e18114455190230d62ad086fd6a1d5-json b/TestAssets/PIFCaches/SPMTest/PIFCache/project/PACKAGE@v12_hash=60e18114455190230d62ad086fd6a1d5-json new file mode 100644 index 0000000..8458f70 --- /dev/null +++ b/TestAssets/PIFCaches/SPMTest/PIFCache/project/PACKAGE@v12_hash=60e18114455190230d62ad086fd6a1d5-json @@ -0,0 +1,221 @@ +{ + "buildConfigurations": [ + { + "buildSettings": { + "CLANG_ENABLE_OBJC_ARC": "YES", + "CODE_SIGNING_REQUIRED": "NO", + "CODE_SIGN_IDENTITY": "", + "COPY_PHASE_STRIP": "NO", + "DEBUG_INFORMATION_FORMAT": "dwarf", + "DRIVERKIT_DEPLOYMENT_TARGET": "19.0", + "DYLIB_INSTALL_NAME_BASE": "@rpath", + "ENABLE_NS_ASSERTIONS": "YES", + "ENABLE_TESTABILITY": "YES", + "ENABLE_TESTING_SEARCH_PATHS": "YES", + "ENTITLEMENTS_REQUIRED": "NO", + "GCC_OPTIMIZATION_LEVEL": "0", + "GCC_PREPROCESSOR_DEFINITIONS": [ + "$(inherited)", + "SWIFT_PACKAGE", + "DEBUG=1" + ], + "IPHONEOS_DEPLOYMENT_TARGET": "12.0", + "IPHONEOS_DEPLOYMENT_TARGET[__platform_filter=ios-maccatalyst]": [ + "13.0" + ], + "KEEP_PRIVATE_EXTERNS": "NO", + "MACOSX_DEPLOYMENT_TARGET": "10.13", + "ONLY_ACTIVE_ARCH": "YES", + "OTHER_CFLAGS": [ + "$(inherited)", + "-DXcode" + ], + "OTHER_LDRFLAGS": [], + "OTHER_SWIFT_FLAGS": [ + "$(inherited)", + "-DXcode" + ], + "PRODUCT_NAME": "$(TARGET_NAME)", + "SKIP_CLANG_STATIC_ANALYZER": "YES", + "SKIP_INSTALL": "YES", + "SUPPORTED_PLATFORMS": [ + "$(AVAILABLE_PLATFORMS)" + ], + "SUPPRESS_WARNINGS": "YES", + "SWIFT_ACTIVE_COMPILATION_CONDITIONS": [ + "$(inherited)", + "SWIFT_PACKAGE", + "DEBUG" + ], + "SWIFT_INSTALL_OBJC_HEADER": "NO", + "SWIFT_OBJC_INTERFACE_HEADER_NAME": "", + "SWIFT_OPTIMIZATION_LEVEL": "-Onone", + "TARGETED_DEVICE_FAMILY": "1,2,3,4,6,7", + "TVOS_DEPLOYMENT_TARGET": "12.0", + "USE_HEADERMAP": "NO", + "WATCHOS_DEPLOYMENT_TARGET": "5.0", + "XROS_DEPLOYMENT_TARGET": "1.0" + }, + "guid": "PACKAGE:/Users/thedderwick/Desktop/SPMTest/MyTransitiveLibrary::BUILDCONFIG_0", + "impartedBuildProperties": { + "buildSettings": {} + }, + "name": "Debug" + }, + { + "buildSettings": { + "CLANG_ENABLE_OBJC_ARC": "YES", + "CODE_SIGNING_REQUIRED": "NO", + "CODE_SIGN_IDENTITY": "", + "COPY_PHASE_STRIP": "YES", + "DEBUG_INFORMATION_FORMAT": "dwarf-with-dsym", + "DRIVERKIT_DEPLOYMENT_TARGET": "19.0", + "DYLIB_INSTALL_NAME_BASE": "@rpath", + "ENABLE_TESTING_SEARCH_PATHS": "YES", + "ENTITLEMENTS_REQUIRED": "NO", + "GCC_OPTIMIZATION_LEVEL": "s", + "GCC_PREPROCESSOR_DEFINITIONS": [ + "$(inherited)", + "SWIFT_PACKAGE" + ], + "IPHONEOS_DEPLOYMENT_TARGET": "12.0", + "IPHONEOS_DEPLOYMENT_TARGET[__platform_filter=ios-maccatalyst]": [ + "13.0" + ], + "KEEP_PRIVATE_EXTERNS": "NO", + "MACOSX_DEPLOYMENT_TARGET": "10.13", + "OTHER_CFLAGS": [ + "$(inherited)", + "-DXcode" + ], + "OTHER_LDRFLAGS": [], + "OTHER_SWIFT_FLAGS": [ + "$(inherited)", + "-DXcode" + ], + "PRODUCT_NAME": "$(TARGET_NAME)", + "SKIP_CLANG_STATIC_ANALYZER": "YES", + "SKIP_INSTALL": "YES", + "SUPPORTED_PLATFORMS": [ + "$(AVAILABLE_PLATFORMS)" + ], + "SUPPRESS_WARNINGS": "YES", + "SWIFT_ACTIVE_COMPILATION_CONDITIONS": [ + "$(inherited)", + "SWIFT_PACKAGE" + ], + "SWIFT_INSTALL_OBJC_HEADER": "NO", + "SWIFT_OBJC_INTERFACE_HEADER_NAME": "", + "SWIFT_OPTIMIZATION_LEVEL": "-Owholemodule", + "TARGETED_DEVICE_FAMILY": "1,2,3,4,6,7", + "TVOS_DEPLOYMENT_TARGET": "12.0", + "USE_HEADERMAP": "NO", + "WATCHOS_DEPLOYMENT_TARGET": "5.0", + "XROS_DEPLOYMENT_TARGET": "1.0" + }, + "guid": "PACKAGE:/Users/thedderwick/Desktop/SPMTest/MyTransitiveLibrary::BUILDCONFIG_1", + "impartedBuildProperties": { + "buildSettings": {} + }, + "name": "Release" + } + ], + "defaultConfigurationName": "Release", + "groupTree": { + "children": [ + { + "children": [], + "guid": "PACKAGE:/Users/thedderwick/Desktop/SPMTest/MyTransitiveLibrary::MAINGROUP::REF_0", + "name": "AdditionalFiles", + "path": "/", + "sourceTree": "", + "type": "group" + }, + { + "children": [ + { + "fileType": "wrapper.xcframework", + "guid": "PACKAGE:/Users/thedderwick/Desktop/SPMTest/MyTransitiveLibrary::MAINGROUP::REF_1::REF_0", + "path": "/Users/thedderwick/Library/Developer/Xcode/DerivedData/SPMTest-akjbvrfwkrsolpeqhugesmlhclvn/SourcePackages/artifacts/mybinarydependency/MyBinaryDependency/Lottie.xcframework", + "sourceTree": "", + "type": "file" + }, + { + "fileType": "wrapper.xcframework", + "guid": "PACKAGE:/Users/thedderwick/Desktop/SPMTest/MyTransitiveLibrary::MAINGROUP::REF_1::REF_1", + "path": "/Users/thedderwick/Library/Developer/Xcode/DerivedData/SPMTest-akjbvrfwkrsolpeqhugesmlhclvn/SourcePackages/artifacts/mybinarydependency/MyBinaryDependency/Lottie.xcframework", + "sourceTree": "", + "type": "file" + }, + { + "fileType": "wrapper.xcframework", + "guid": "PACKAGE:/Users/thedderwick/Desktop/SPMTest/MyTransitiveLibrary::MAINGROUP::REF_1::REF_2", + "path": "/Users/thedderwick/Library/Developer/Xcode/DerivedData/SPMTest-akjbvrfwkrsolpeqhugesmlhclvn/SourcePackages/artifacts/mybinarydependency/MyBinaryDependency/Lottie.xcframework", + "sourceTree": "", + "type": "file" + }, + { + "fileType": "wrapper.xcframework", + "guid": "PACKAGE:/Users/thedderwick/Desktop/SPMTest/MyTransitiveLibrary::MAINGROUP::REF_1::REF_3", + "path": "/Users/thedderwick/Library/Developer/Xcode/DerivedData/SPMTest-akjbvrfwkrsolpeqhugesmlhclvn/SourcePackages/artifacts/mybinarydependency/MyBinaryDependency/Lottie.xcframework", + "sourceTree": "", + "type": "file" + } + ], + "guid": "PACKAGE:/Users/thedderwick/Desktop/SPMTest/MyTransitiveLibrary::MAINGROUP::REF_1", + "name": "Binaries", + "path": "/", + "sourceTree": "", + "type": "group" + }, + { + "children": [ + { + "fileType": "sourcecode.swift", + "guid": "PACKAGE:/Users/thedderwick/Desktop/SPMTest/MyTransitiveLibrary::MAINGROUP::REF_2::REF_0", + "path": "MyTransitiveLibrary.swift", + "sourceTree": "", + "type": "file" + } + ], + "guid": "PACKAGE:/Users/thedderwick/Desktop/SPMTest/MyTransitiveLibrary::MAINGROUP::REF_2", + "name": "/Users/thedderwick/Desktop/SPMTest/MyTransitiveLibrary/Sources/MyTransitiveLibrary", + "path": "/Users/thedderwick/Desktop/SPMTest/MyTransitiveLibrary/Sources/MyTransitiveLibrary", + "sourceTree": "", + "type": "group" + }, + { + "children": [ + { + "fileType": "sourcecode.swift", + "guid": "PACKAGE:/Users/thedderwick/Desktop/SPMTest/MyTransitiveLibrary::MAINGROUP::REF_3::REF_0", + "path": "MyTransitiveLibrary.swift", + "sourceTree": "", + "type": "file" + } + ], + "guid": "PACKAGE:/Users/thedderwick/Desktop/SPMTest/MyTransitiveLibrary::MAINGROUP::REF_3", + "name": "/Users/thedderwick/Desktop/SPMTest/MyTransitiveLibrary/Sources/MyTransitiveLibrary", + "path": "/Users/thedderwick/Desktop/SPMTest/MyTransitiveLibrary/Sources/MyTransitiveLibrary", + "sourceTree": "", + "type": "group" + } + ], + "guid": "PACKAGE:/Users/thedderwick/Desktop/SPMTest/MyTransitiveLibrary::MAINGROUP", + "name": "", + "path": "", + "sourceTree": "", + "type": "group" + }, + "guid": "PACKAGE:/Users/thedderwick/Desktop/SPMTest/MyTransitiveLibrary", + "path": "/Users/thedderwick/Desktop/SPMTest/MyTransitiveLibrary/Package.swift", + "projectDirectory": "/Users/thedderwick/Desktop/SPMTest/MyTransitiveLibrary", + "projectIsPackage": "true", + "projectName": "MyTransitiveLibrary", + "targets": [ + "TARGET@v12_hash=fb7de2da6bb6f870c73ba0c83581f638", + "TARGET@v12_hash=85e5507488bfe17e9e4e18b38ecf6a38", + "TARGET@v12_hash=f6b1fc5ff8b0c87e4d17a05b612118a8", + "TARGET@v12_hash=ec86d6a4683c2028c68c9a06f9947bf3" + ] +} diff --git a/TestAssets/PIFCaches/SPMTest/PIFCache/project/PACKAGE@v12_hash=d061ee81d8927d5b63443e777e83938a-json b/TestAssets/PIFCaches/SPMTest/PIFCache/project/PACKAGE@v12_hash=d061ee81d8927d5b63443e777e83938a-json new file mode 100644 index 0000000..8aedf87 --- /dev/null +++ b/TestAssets/PIFCaches/SPMTest/PIFCache/project/PACKAGE@v12_hash=d061ee81d8927d5b63443e777e83938a-json @@ -0,0 +1,192 @@ +{ + "buildConfigurations": [ + { + "buildSettings": { + "CLANG_ENABLE_OBJC_ARC": "YES", + "CODE_SIGNING_REQUIRED": "NO", + "CODE_SIGN_IDENTITY": "", + "COPY_PHASE_STRIP": "NO", + "DEBUG_INFORMATION_FORMAT": "dwarf", + "DRIVERKIT_DEPLOYMENT_TARGET": "19.0", + "DYLIB_INSTALL_NAME_BASE": "@rpath", + "ENABLE_NS_ASSERTIONS": "YES", + "ENABLE_TESTABILITY": "YES", + "ENABLE_TESTING_SEARCH_PATHS": "YES", + "ENTITLEMENTS_REQUIRED": "NO", + "GCC_OPTIMIZATION_LEVEL": "0", + "GCC_PREPROCESSOR_DEFINITIONS": [ + "$(inherited)", + "SWIFT_PACKAGE", + "DEBUG=1" + ], + "IPHONEOS_DEPLOYMENT_TARGET": "12.0", + "IPHONEOS_DEPLOYMENT_TARGET[__platform_filter=ios-maccatalyst]": [ + "13.0" + ], + "KEEP_PRIVATE_EXTERNS": "NO", + "MACOSX_DEPLOYMENT_TARGET": "10.13", + "ONLY_ACTIVE_ARCH": "YES", + "OTHER_CFLAGS": [ + "$(inherited)", + "-DXcode" + ], + "OTHER_LDRFLAGS": [], + "OTHER_SWIFT_FLAGS": [ + "$(inherited)", + "-DXcode" + ], + "PRODUCT_NAME": "$(TARGET_NAME)", + "SKIP_CLANG_STATIC_ANALYZER": "YES", + "SKIP_INSTALL": "YES", + "SUPPORTED_PLATFORMS": [ + "$(AVAILABLE_PLATFORMS)" + ], + "SUPPRESS_WARNINGS": "YES", + "SWIFT_ACTIVE_COMPILATION_CONDITIONS": [ + "$(inherited)", + "SWIFT_PACKAGE", + "DEBUG" + ], + "SWIFT_INSTALL_OBJC_HEADER": "NO", + "SWIFT_OBJC_INTERFACE_HEADER_NAME": "", + "SWIFT_OPTIMIZATION_LEVEL": "-Onone", + "TARGETED_DEVICE_FAMILY": "1,2,3,4,6,7", + "TVOS_DEPLOYMENT_TARGET": "12.0", + "USE_HEADERMAP": "NO", + "WATCHOS_DEPLOYMENT_TARGET": "5.0", + "XROS_DEPLOYMENT_TARGET": "1.0" + }, + "guid": "PACKAGE:/Users/thedderwick/Desktop/SPMTest/MyCommonLibrary::BUILDCONFIG_0", + "impartedBuildProperties": { + "buildSettings": {} + }, + "name": "Debug" + }, + { + "buildSettings": { + "CLANG_ENABLE_OBJC_ARC": "YES", + "CODE_SIGNING_REQUIRED": "NO", + "CODE_SIGN_IDENTITY": "", + "COPY_PHASE_STRIP": "YES", + "DEBUG_INFORMATION_FORMAT": "dwarf-with-dsym", + "DRIVERKIT_DEPLOYMENT_TARGET": "19.0", + "DYLIB_INSTALL_NAME_BASE": "@rpath", + "ENABLE_TESTING_SEARCH_PATHS": "YES", + "ENTITLEMENTS_REQUIRED": "NO", + "GCC_OPTIMIZATION_LEVEL": "s", + "GCC_PREPROCESSOR_DEFINITIONS": [ + "$(inherited)", + "SWIFT_PACKAGE" + ], + "IPHONEOS_DEPLOYMENT_TARGET": "12.0", + "IPHONEOS_DEPLOYMENT_TARGET[__platform_filter=ios-maccatalyst]": [ + "13.0" + ], + "KEEP_PRIVATE_EXTERNS": "NO", + "MACOSX_DEPLOYMENT_TARGET": "10.13", + "OTHER_CFLAGS": [ + "$(inherited)", + "-DXcode" + ], + "OTHER_LDRFLAGS": [], + "OTHER_SWIFT_FLAGS": [ + "$(inherited)", + "-DXcode" + ], + "PRODUCT_NAME": "$(TARGET_NAME)", + "SKIP_CLANG_STATIC_ANALYZER": "YES", + "SKIP_INSTALL": "YES", + "SUPPORTED_PLATFORMS": [ + "$(AVAILABLE_PLATFORMS)" + ], + "SUPPRESS_WARNINGS": "YES", + "SWIFT_ACTIVE_COMPILATION_CONDITIONS": [ + "$(inherited)", + "SWIFT_PACKAGE" + ], + "SWIFT_INSTALL_OBJC_HEADER": "NO", + "SWIFT_OBJC_INTERFACE_HEADER_NAME": "", + "SWIFT_OPTIMIZATION_LEVEL": "-Owholemodule", + "TARGETED_DEVICE_FAMILY": "1,2,3,4,6,7", + "TVOS_DEPLOYMENT_TARGET": "12.0", + "USE_HEADERMAP": "NO", + "WATCHOS_DEPLOYMENT_TARGET": "5.0", + "XROS_DEPLOYMENT_TARGET": "1.0" + }, + "guid": "PACKAGE:/Users/thedderwick/Desktop/SPMTest/MyCommonLibrary::BUILDCONFIG_1", + "impartedBuildProperties": { + "buildSettings": {} + }, + "name": "Release" + } + ], + "defaultConfigurationName": "Release", + "groupTree": { + "children": [ + { + "children": [], + "guid": "PACKAGE:/Users/thedderwick/Desktop/SPMTest/MyCommonLibrary::MAINGROUP::REF_0", + "name": "AdditionalFiles", + "path": "/", + "sourceTree": "", + "type": "group" + }, + { + "children": [], + "guid": "PACKAGE:/Users/thedderwick/Desktop/SPMTest/MyCommonLibrary::MAINGROUP::REF_1", + "name": "Binaries", + "path": "/", + "sourceTree": "", + "type": "group" + }, + { + "children": [ + { + "fileType": "sourcecode.swift", + "guid": "PACKAGE:/Users/thedderwick/Desktop/SPMTest/MyCommonLibrary::MAINGROUP::REF_2::REF_0", + "path": "MyCommonLibrary.swift", + "sourceTree": "", + "type": "file" + } + ], + "guid": "PACKAGE:/Users/thedderwick/Desktop/SPMTest/MyCommonLibrary::MAINGROUP::REF_2", + "name": "/Users/thedderwick/Desktop/SPMTest/MyCommonLibrary/Sources/MyCommonLibrary", + "path": "/Users/thedderwick/Desktop/SPMTest/MyCommonLibrary/Sources/MyCommonLibrary", + "sourceTree": "", + "type": "group" + }, + { + "children": [ + { + "fileType": "sourcecode.swift", + "guid": "PACKAGE:/Users/thedderwick/Desktop/SPMTest/MyCommonLibrary::MAINGROUP::REF_3::REF_0", + "path": "MyCommonLibrary.swift", + "sourceTree": "", + "type": "file" + } + ], + "guid": "PACKAGE:/Users/thedderwick/Desktop/SPMTest/MyCommonLibrary::MAINGROUP::REF_3", + "name": "/Users/thedderwick/Desktop/SPMTest/MyCommonLibrary/Sources/MyCommonLibrary", + "path": "/Users/thedderwick/Desktop/SPMTest/MyCommonLibrary/Sources/MyCommonLibrary", + "sourceTree": "", + "type": "group" + } + ], + "guid": "PACKAGE:/Users/thedderwick/Desktop/SPMTest/MyCommonLibrary::MAINGROUP", + "name": "", + "path": "", + "sourceTree": "", + "type": "group" + }, + "guid": "PACKAGE:/Users/thedderwick/Desktop/SPMTest/MyCommonLibrary", + "path": "/Users/thedderwick/Desktop/SPMTest/MyCommonLibrary/Package.swift", + "projectDirectory": "/Users/thedderwick/Desktop/SPMTest/MyCommonLibrary", + "projectIsPackage": "true", + "projectName": "MyCommonLibrary", + "targets": [ + "TARGET@v12_hash=7fe91a1e2b8f90031d23796421a46a09", + "TARGET@v12_hash=c5eeab26a0e301a2419ee7e6d1254b52", + "TARGET@v12_hash=27eb3a98abb5101fe49563a2824e9794", + "TARGET@v12_hash=e0ad90d36af3737ec5215a32f000294d" + ] +} diff --git a/TestAssets/PIFCaches/SPMTest/PIFCache/project/PROJECT@v11_mod=1716537774.576404_hash=0f958f39499d9f0b6b454948d2be549bplugins=1OJSG6M1FOV3XYQCBH7Z29RZ0FPR9XDE1-json b/TestAssets/PIFCaches/SPMTest/PIFCache/project/PROJECT@v11_mod=1716537774.576404_hash=0f958f39499d9f0b6b454948d2be549bplugins=1OJSG6M1FOV3XYQCBH7Z29RZ0FPR9XDE1-json new file mode 100644 index 0000000..6ce343f --- /dev/null +++ b/TestAssets/PIFCaches/SPMTest/PIFCache/project/PROJECT@v11_mod=1716537774.576404_hash=0f958f39499d9f0b6b454948d2be549bplugins=1OJSG6M1FOV3XYQCBH7Z29RZ0FPR9XDE1-json @@ -0,0 +1,192 @@ +{ + "appPreferencesBuildSettings": {}, + "buildConfigurations": [ + { + "buildSettings": { + "ALWAYS_SEARCH_USER_PATHS": "NO", + "ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS": "YES", + "CLANG_ANALYZER_NONNULL": "YES", + "CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION": "YES_AGGRESSIVE", + "CLANG_CXX_LANGUAGE_STANDARD": "gnu++20", + "CLANG_ENABLE_MODULES": "YES", + "CLANG_ENABLE_OBJC_ARC": "YES", + "CLANG_ENABLE_OBJC_WEAK": "YES", + "CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING": "YES", + "CLANG_WARN_BOOL_CONVERSION": "YES", + "CLANG_WARN_COMMA": "YES", + "CLANG_WARN_CONSTANT_CONVERSION": "YES", + "CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS": "YES", + "CLANG_WARN_DIRECT_OBJC_ISA_USAGE": "YES_ERROR", + "CLANG_WARN_DOCUMENTATION_COMMENTS": "YES", + "CLANG_WARN_EMPTY_BODY": "YES", + "CLANG_WARN_ENUM_CONVERSION": "YES", + "CLANG_WARN_INFINITE_RECURSION": "YES", + "CLANG_WARN_INT_CONVERSION": "YES", + "CLANG_WARN_NON_LITERAL_NULL_CONVERSION": "YES", + "CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF": "YES", + "CLANG_WARN_OBJC_LITERAL_CONVERSION": "YES", + "CLANG_WARN_OBJC_ROOT_CLASS": "YES_ERROR", + "CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER": "YES", + "CLANG_WARN_RANGE_LOOP_ANALYSIS": "YES", + "CLANG_WARN_STRICT_PROTOTYPES": "YES", + "CLANG_WARN_SUSPICIOUS_MOVE": "YES", + "CLANG_WARN_UNGUARDED_AVAILABILITY": "YES_AGGRESSIVE", + "CLANG_WARN_UNREACHABLE_CODE": "YES", + "CLANG_WARN__DUPLICATE_METHOD_MATCH": "YES", + "COPY_PHASE_STRIP": "NO", + "DEBUG_INFORMATION_FORMAT": "dwarf", + "ENABLE_STRICT_OBJC_MSGSEND": "YES", + "ENABLE_TESTABILITY": "YES", + "ENABLE_USER_SCRIPT_SANDBOXING": "YES", + "GCC_C_LANGUAGE_STANDARD": "gnu17", + "GCC_DYNAMIC_NO_PIC": "NO", + "GCC_NO_COMMON_BLOCKS": "YES", + "GCC_OPTIMIZATION_LEVEL": "0", + "GCC_PREPROCESSOR_DEFINITIONS": "DEBUG=1 $(inherited)", + "GCC_WARN_64_TO_32_BIT_CONVERSION": "YES", + "GCC_WARN_ABOUT_RETURN_TYPE": "YES_ERROR", + "GCC_WARN_UNDECLARED_SELECTOR": "YES", + "GCC_WARN_UNINITIALIZED_AUTOS": "YES_AGGRESSIVE", + "GCC_WARN_UNUSED_FUNCTION": "YES", + "GCC_WARN_UNUSED_VARIABLE": "YES", + "IPHONEOS_DEPLOYMENT_TARGET": "17.4", + "LOCALIZATION_PREFERS_STRING_CATALOGS": "YES", + "MTL_ENABLE_DEBUG_INFO": "INCLUDE_SOURCE", + "MTL_FAST_MATH": "YES", + "ONLY_ACTIVE_ARCH": "YES", + "SDKROOT": "iphoneos", + "SWIFT_ACTIVE_COMPILATION_CONDITIONS": "DEBUG $(inherited)", + "SWIFT_OPTIMIZATION_LEVEL": "-Onone" + }, + "guid": "0f958f39499d9f0b6b454948d2be549b42f5c53a127a9a1a044740c8d5a00ed1", + "name": "Debug" + }, + { + "buildSettings": { + "ALWAYS_SEARCH_USER_PATHS": "NO", + "ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS": "YES", + "CLANG_ANALYZER_NONNULL": "YES", + "CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION": "YES_AGGRESSIVE", + "CLANG_CXX_LANGUAGE_STANDARD": "gnu++20", + "CLANG_ENABLE_MODULES": "YES", + "CLANG_ENABLE_OBJC_ARC": "YES", + "CLANG_ENABLE_OBJC_WEAK": "YES", + "CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING": "YES", + "CLANG_WARN_BOOL_CONVERSION": "YES", + "CLANG_WARN_COMMA": "YES", + "CLANG_WARN_CONSTANT_CONVERSION": "YES", + "CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS": "YES", + "CLANG_WARN_DIRECT_OBJC_ISA_USAGE": "YES_ERROR", + "CLANG_WARN_DOCUMENTATION_COMMENTS": "YES", + "CLANG_WARN_EMPTY_BODY": "YES", + "CLANG_WARN_ENUM_CONVERSION": "YES", + "CLANG_WARN_INFINITE_RECURSION": "YES", + "CLANG_WARN_INT_CONVERSION": "YES", + "CLANG_WARN_NON_LITERAL_NULL_CONVERSION": "YES", + "CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF": "YES", + "CLANG_WARN_OBJC_LITERAL_CONVERSION": "YES", + "CLANG_WARN_OBJC_ROOT_CLASS": "YES_ERROR", + "CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER": "YES", + "CLANG_WARN_RANGE_LOOP_ANALYSIS": "YES", + "CLANG_WARN_STRICT_PROTOTYPES": "YES", + "CLANG_WARN_SUSPICIOUS_MOVE": "YES", + "CLANG_WARN_UNGUARDED_AVAILABILITY": "YES_AGGRESSIVE", + "CLANG_WARN_UNREACHABLE_CODE": "YES", + "CLANG_WARN__DUPLICATE_METHOD_MATCH": "YES", + "COPY_PHASE_STRIP": "NO", + "DEBUG_INFORMATION_FORMAT": "dwarf-with-dsym", + "ENABLE_NS_ASSERTIONS": "NO", + "ENABLE_STRICT_OBJC_MSGSEND": "YES", + "ENABLE_USER_SCRIPT_SANDBOXING": "YES", + "GCC_C_LANGUAGE_STANDARD": "gnu17", + "GCC_NO_COMMON_BLOCKS": "YES", + "GCC_WARN_64_TO_32_BIT_CONVERSION": "YES", + "GCC_WARN_ABOUT_RETURN_TYPE": "YES_ERROR", + "GCC_WARN_UNDECLARED_SELECTOR": "YES", + "GCC_WARN_UNINITIALIZED_AUTOS": "YES_AGGRESSIVE", + "GCC_WARN_UNUSED_FUNCTION": "YES", + "GCC_WARN_UNUSED_VARIABLE": "YES", + "IPHONEOS_DEPLOYMENT_TARGET": "17.4", + "LOCALIZATION_PREFERS_STRING_CATALOGS": "YES", + "MTL_ENABLE_DEBUG_INFO": "NO", + "MTL_FAST_MATH": "YES", + "SDKROOT": "iphoneos", + "SWIFT_COMPILATION_MODE": "wholemodule", + "VALIDATE_PRODUCT": "YES" + }, + "guid": "0f958f39499d9f0b6b454948d2be549bfc0d140c3c428c4be4300c4ad84f502b", + "name": "Release" + } + ], + "classPrefix": "", + "defaultConfigurationName": "Release", + "developmentRegion": "en", + "groupTree": { + "children": [ + { + "children": [ + { + "fileType": "sourcecode.swift", + "guid": "0f958f39499d9f0b6b454948d2be549bfecf073ab7da34165ac491b2cb2608da", + "path": "SPMTestApp.swift", + "sourceTree": "", + "type": "file" + }, + { + "fileType": "sourcecode.swift", + "guid": "0f958f39499d9f0b6b454948d2be549bb774bcb839cea612c4808e51e76f1d8a", + "path": "ContentView.swift", + "sourceTree": "", + "type": "file" + }, + { + "fileType": "folder.assetcatalog", + "guid": "0f958f39499d9f0b6b454948d2be549b63f219360fcbccc1f13e995b6d2c55bd", + "path": "Assets.xcassets", + "sourceTree": "", + "type": "file" + }, + { + "children": [ + { + "fileType": "folder.assetcatalog", + "guid": "0f958f39499d9f0b6b454948d2be549be0988c6a6200faa66fc4e43736a838c5", + "path": "Preview Assets.xcassets", + "sourceTree": "", + "type": "file" + } + ], + "guid": "0f958f39499d9f0b6b454948d2be549b2f11424f01062300b0bebb6c9130131b", + "name": "Preview Content", + "path": "Preview Content", + "sourceTree": "", + "type": "group" + } + ], + "guid": "0f958f39499d9f0b6b454948d2be549b435ba3b373201685aef000e70e51db4d", + "name": "SPMTest", + "path": "SPMTest", + "sourceTree": "", + "type": "group" + }, + { + "guid": "0f958f39499d9f0b6b454948d2be549befe9d7653bff13bf45841ede70f783cd", + "name": "Products", + "path": "", + "sourceTree": "", + "type": "group" + } + ], + "guid": "0f958f39499d9f0b6b454948d2be549b33bedd8701761212ef2c3a2c378a8e0b", + "name": "SPMTest", + "path": "", + "sourceTree": "", + "type": "group" + }, + "guid": "0f958f39499d9f0b6b454948d2be549b", + "path": "/Users/thedderwick/Desktop/SPMTest/SPMTest.xcodeproj", + "projectDirectory": "/Users/thedderwick/Desktop/SPMTest", + "targets": [ + "TARGET@v11_hash=d5012c60a72570c6ac682d8666f0e4a1" + ] +} diff --git a/TestAssets/PIFCaches/SPMTest/PIFCache/target/TARGET@v11_hash=d5012c60a72570c6ac682d8666f0e4a1-json b/TestAssets/PIFCaches/SPMTest/PIFCache/target/TARGET@v11_hash=d5012c60a72570c6ac682d8666f0e4a1-json new file mode 100644 index 0000000..4cc5168 --- /dev/null +++ b/TestAssets/PIFCaches/SPMTest/PIFCache/target/TARGET@v11_hash=d5012c60a72570c6ac682d8666f0e4a1-json @@ -0,0 +1,137 @@ +{ + "buildConfigurations": [ + { + "buildSettings": { + "ASSETCATALOG_COMPILER_APPICON_NAME": "AppIcon", + "ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME": "AccentColor", + "CODE_SIGN_IDENTITY": "", + "CODE_SIGN_STYLE": "Manual", + "CURRENT_PROJECT_VERSION": "1", + "DEVELOPMENT_ASSET_PATHS": "\"SPMTest/Preview Content\"", + "DEVELOPMENT_TEAM": "", + "ENABLE_PREVIEWS": "YES", + "GENERATE_INFOPLIST_FILE": "YES", + "INFOPLIST_KEY_UIApplicationSceneManifest_Generation": "YES", + "INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents": "YES", + "INFOPLIST_KEY_UILaunchScreen_Generation": "YES", + "INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad": "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight", + "INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone": "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight", + "LD_RUNPATH_SEARCH_PATHS": "$(inherited) @executable_path/Frameworks", + "MARKETING_VERSION": "1.0", + "PRODUCT_BUNDLE_IDENTIFIER": "com.veracode.SPMTest", + "PRODUCT_NAME": "$(TARGET_NAME)", + "PROVISIONING_PROFILE_SPECIFIER": "", + "SWIFT_EMIT_LOC_STRINGS": "YES", + "SWIFT_VERSION": "5.0", + "TARGETED_DEVICE_FAMILY": "1,2" + }, + "guid": "0f958f39499d9f0b6b454948d2be549b4cca86b4d9d5847565233659a29761c4", + "name": "Debug" + }, + { + "buildSettings": { + "ASSETCATALOG_COMPILER_APPICON_NAME": "AppIcon", + "ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME": "AccentColor", + "CODE_SIGN_IDENTITY": "", + "CODE_SIGN_STYLE": "Manual", + "CURRENT_PROJECT_VERSION": "1", + "DEVELOPMENT_ASSET_PATHS": "\"SPMTest/Preview Content\"", + "DEVELOPMENT_TEAM": "", + "ENABLE_PREVIEWS": "YES", + "GENERATE_INFOPLIST_FILE": "YES", + "INFOPLIST_KEY_UIApplicationSceneManifest_Generation": "YES", + "INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents": "YES", + "INFOPLIST_KEY_UILaunchScreen_Generation": "YES", + "INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad": "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight", + "INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone": "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight", + "LD_RUNPATH_SEARCH_PATHS": "$(inherited) @executable_path/Frameworks", + "MARKETING_VERSION": "1.0", + "PRODUCT_BUNDLE_IDENTIFIER": "com.veracode.SPMTest", + "PRODUCT_NAME": "$(TARGET_NAME)", + "PROVISIONING_PROFILE_SPECIFIER": "", + "SWIFT_EMIT_LOC_STRINGS": "YES", + "SWIFT_VERSION": "5.0", + "TARGETED_DEVICE_FAMILY": "1,2" + }, + "guid": "0f958f39499d9f0b6b454948d2be549bb821c619cfb8b2e724c5328e54f54e17", + "name": "Release" + } + ], + "buildPhases": [ + { + "buildFiles": [ + { + "fileReference": "0f958f39499d9f0b6b454948d2be549bb774bcb839cea612c4808e51e76f1d8a", + "guid": "0f958f39499d9f0b6b454948d2be549b7a45127807cf63f6589a4a0de9d1e716" + }, + { + "fileReference": "0f958f39499d9f0b6b454948d2be549bfecf073ab7da34165ac491b2cb2608da", + "guid": "0f958f39499d9f0b6b454948d2be549bd0950f9139cbf08f13714ef301b71636" + } + ], + "guid": "0f958f39499d9f0b6b454948d2be549b1112b312486e6592252467de83ac12fa", + "type": "com.apple.buildphase.sources" + }, + { + "buildFiles": [ + { + "guid": "0f958f39499d9f0b6b454948d2be549bae8c757dc1f71dc80677176e88f57513", + "targetReference": "PACKAGE-PRODUCT:MyLibrary" + }, + { + "guid": "0f958f39499d9f0b6b454948d2be549b1990e4c3872d798fb604b5b6dde1e9d7", + "targetReference": "PACKAGE-PRODUCT:MyCommonLibrary" + } + ], + "guid": "0f958f39499d9f0b6b454948d2be549bc68ad644ebd05301e2324acbeb00d9ce", + "type": "com.apple.buildphase.frameworks" + }, + { + "buildFiles": [ + { + "fileReference": "0f958f39499d9f0b6b454948d2be549be0988c6a6200faa66fc4e43736a838c5", + "guid": "0f958f39499d9f0b6b454948d2be549b2bdba7efbecb87014e782e630cad919b" + }, + { + "fileReference": "0f958f39499d9f0b6b454948d2be549b63f219360fcbccc1f13e995b6d2c55bd", + "guid": "0f958f39499d9f0b6b454948d2be549b011725fb4c17cd464e9eeb9cd6e70496" + } + ], + "guid": "0f958f39499d9f0b6b454948d2be549b42f6470020217a2716154a23e1ad07ff", + "type": "com.apple.buildphase.resources" + } + ], + "buildRules": [], + "dependencies": [ + { + "guid": "PACKAGE-PRODUCT:MyLibrary", + "name": "MyLibrary" + }, + { + "guid": "PACKAGE-PRODUCT:MyCommonLibrary", + "name": "MyCommonLibrary" + } + ], + "guid": "0f958f39499d9f0b6b454948d2be549be609cf86f8e7120d04a2edc980fbcfa8", + "name": "SPMTest", + "predominantSourceCodeLanguage": "Xcode.SourceCodeLanguage.Swift", + "productReference": { + "guid": "0f958f39499d9f0b6b454948d2be549b6201de4660da2a235e2bcde1105d0dfd", + "name": "SPMTest.app", + "type": "product" + }, + "productTypeIdentifier": "com.apple.product-type.application", + "provisioningSourceData": [ + { + "bundleIdentifierFromInfoPlist": "$(PRODUCT_BUNDLE_IDENTIFIER)", + "configurationName": "Debug", + "provisioningStyle": 1 + }, + { + "bundleIdentifierFromInfoPlist": "$(PRODUCT_BUNDLE_IDENTIFIER)", + "configurationName": "Release", + "provisioningStyle": 1 + } + ], + "type": "standard" +} diff --git a/TestAssets/PIFCaches/SPMTest/PIFCache/target/TARGET@v12_hash=27eb3a98abb5101fe49563a2824e9794-json b/TestAssets/PIFCaches/SPMTest/PIFCache/target/TARGET@v12_hash=27eb3a98abb5101fe49563a2824e9794-json new file mode 100644 index 0000000..d0beab8 --- /dev/null +++ b/TestAssets/PIFCaches/SPMTest/PIFCache/target/TARGET@v12_hash=27eb3a98abb5101fe49563a2824e9794-json @@ -0,0 +1,128 @@ +{ + "approvedByUser": "true", + "buildConfigurations": [ + { + "buildSettings": { + "CLANG_COVERAGE_MAPPING_LINKER_ARGS": "NO", + "CLANG_ENABLE_MODULES": "YES", + "DEFINES_MODULE": "YES", + "EXECUTABLE_NAME": "MyCommonLibrary.o", + "GENERATE_MASTER_OBJECT_FILE": "NO", + "MACH_O_TYPE": "mh_object", + "MODULEMAP_FILE_CONTENTS": "module MyCommonLibrary {\nheader \"MyCommonLibrary-Swift.h\"\nexport *\n}", + "MODULEMAP_PATH": "$(GENERATED_MODULEMAP_DIR)/MyCommonLibrary.modulemap", + "PACKAGE_RESOURCE_TARGET_KIND": "regular", + "PRODUCT_BUNDLE_IDENTIFIER": "MyCommonLibrary", + "PRODUCT_MODULE_NAME": "MyCommonLibrary", + "PRODUCT_NAME": "$(TARGET_NAME)", + "SDKROOT": "auto", + "SDK_VARIANT": "auto", + "STRIP_INSTALLED_PRODUCT": "NO", + "SUPPORTS_TEXT_BASED_API": "NO", + "SWIFT_ENABLE_BARE_SLASH_REGEX": "NO", + "SWIFT_OBJC_INTERFACE_HEADER_DIR": "$(GENERATED_MODULEMAP_DIR)", + "SWIFT_OBJC_INTERFACE_HEADER_NAME": "MyCommonLibrary-Swift.h", + "SWIFT_PACKAGE_NAME": "mycommonlibrary", + "SWIFT_VERSION": "5", + "TAPI_DYLIB_INSTALL_NAME": "MyCommonLibrary", + "TARGET_NAME": "MyCommonLibrary" + }, + "guid": "PACKAGE-TARGET:MyCommonLibrary::BUILDCONFIG_0", + "impartedBuildProperties": { + "buildSettings": { + "FRAMEWORK_SEARCH_PATHS": [ + "$(BUILT_PRODUCTS_DIR)/PackageFrameworks", + "$(inherited)" + ], + "LD_RUNPATH_SEARCH_PATHS": [ + "$(BUILT_PRODUCTS_DIR)/PackageFrameworks", + "$(inherited)" + ], + "OTHER_CFLAGS": [ + "-fmodule-map-file=$(GENERATED_MODULEMAP_DIR)/MyCommonLibrary.modulemap", + "$(inherited)" + ], + "OTHER_LDFLAGS": [ + "-Wl,-no_warn_duplicate_libraries", + "$(inherited)" + ], + "OTHER_LDRFLAGS": [] + } + }, + "name": "Debug" + }, + { + "buildSettings": { + "CLANG_COVERAGE_MAPPING_LINKER_ARGS": "NO", + "CLANG_ENABLE_MODULES": "YES", + "DEFINES_MODULE": "YES", + "EXECUTABLE_NAME": "MyCommonLibrary.o", + "GENERATE_MASTER_OBJECT_FILE": "NO", + "MACH_O_TYPE": "mh_object", + "MODULEMAP_FILE_CONTENTS": "module MyCommonLibrary {\nheader \"MyCommonLibrary-Swift.h\"\nexport *\n}", + "MODULEMAP_PATH": "$(GENERATED_MODULEMAP_DIR)/MyCommonLibrary.modulemap", + "PACKAGE_RESOURCE_TARGET_KIND": "regular", + "PRODUCT_BUNDLE_IDENTIFIER": "MyCommonLibrary", + "PRODUCT_MODULE_NAME": "MyCommonLibrary", + "PRODUCT_NAME": "$(TARGET_NAME)", + "SDKROOT": "auto", + "SDK_VARIANT": "auto", + "STRIP_INSTALLED_PRODUCT": "NO", + "SUPPORTS_TEXT_BASED_API": "NO", + "SWIFT_ENABLE_BARE_SLASH_REGEX": "NO", + "SWIFT_OBJC_INTERFACE_HEADER_DIR": "$(GENERATED_MODULEMAP_DIR)", + "SWIFT_OBJC_INTERFACE_HEADER_NAME": "MyCommonLibrary-Swift.h", + "SWIFT_PACKAGE_NAME": "mycommonlibrary", + "SWIFT_VERSION": "5", + "TAPI_DYLIB_INSTALL_NAME": "MyCommonLibrary", + "TARGET_NAME": "MyCommonLibrary" + }, + "guid": "PACKAGE-TARGET:MyCommonLibrary::BUILDCONFIG_1", + "impartedBuildProperties": { + "buildSettings": { + "FRAMEWORK_SEARCH_PATHS": [ + "$(BUILT_PRODUCTS_DIR)/PackageFrameworks", + "$(inherited)" + ], + "OTHER_CFLAGS": [ + "-fmodule-map-file=$(GENERATED_MODULEMAP_DIR)/MyCommonLibrary.modulemap", + "$(inherited)" + ], + "OTHER_LDFLAGS": [ + "-Wl,-no_warn_duplicate_libraries", + "$(inherited)" + ], + "OTHER_LDRFLAGS": [] + } + }, + "name": "Release" + } + ], + "buildPhases": [ + { + "buildFiles": [ + { + "codeSignOnCopy": "false", + "fileReference": "PACKAGE:/Users/thedderwick/Desktop/SPMTest/MyCommonLibrary::MAINGROUP::REF_2::REF_0", + "guid": "PACKAGE-TARGET:MyCommonLibrary::BUILDPHASE_0::0", + "platformFilters": [], + "removeHeadersOnCopy": "false" + } + ], + "guid": "PACKAGE-TARGET:MyCommonLibrary::BUILDPHASE_0", + "type": "com.apple.buildphase.sources" + } + ], + "buildRules": [], + "dependencies": [], + "dynamicTargetVariantGuid": "PACKAGE-TARGET:MyCommonLibrary--138526C95691DCED-dynamic", + "guid": "PACKAGE-TARGET:MyCommonLibrary", + "name": "MyCommonLibrary", + "productReference": { + "guid": "PRODUCTREF-PACKAGE-TARGET:MyCommonLibrary", + "name": "MyCommonLibrary.o", + "type": "file" + }, + "productTypeIdentifier": "com.apple.product-type.objfile", + "type": "standard" +} diff --git a/TestAssets/PIFCaches/SPMTest/PIFCache/target/TARGET@v12_hash=322b71ca67a33085a0152ac83f23a35f-json b/TestAssets/PIFCaches/SPMTest/PIFCache/target/TARGET@v12_hash=322b71ca67a33085a0152ac83f23a35f-json new file mode 100644 index 0000000..bfc0190 --- /dev/null +++ b/TestAssets/PIFCaches/SPMTest/PIFCache/target/TARGET@v12_hash=322b71ca67a33085a0152ac83f23a35f-json @@ -0,0 +1,92 @@ +{ + "approvedByUser": "true", + "buildConfigurations": [ + { + "buildSettings": { + "CLANG_ENABLE_MODULES": "YES", + "CURRENT_PROJECT_VERSION": "1", + "EXECUTABLE_NAME": "MyBinaryDependency_-5B0EB316CCB8BC4_PackageProduct", + "GENERATE_INFOPLIST_FILE": "YES", + "MARKETING_VERSION": "1.0", + "PRODUCT_BUNDLE_IDENTIFIER": "MyBinaryDependency", + "PRODUCT_MODULE_NAME": "MyBinaryDependency", + "PRODUCT_NAME": "MyBinaryDependency_-5B0EB316CCB8BC4_PackageProduct", + "SDKROOT": "auto", + "SDK_VARIANT": "auto", + "SWIFT_PACKAGE_NAME": "mybinarydependency", + "TARGET_BUILD_DIR": "$(TARGET_BUILD_DIR)/PackageFrameworks", + "TARGET_NAME": "MyBinaryDependency product", + "USES_SWIFTPM_UNSAFE_FLAGS": "NO" + }, + "guid": "PACKAGE-PRODUCT:MyBinaryDependency--5B0EB316CCB8BC4-dynamic::BUILDCONFIG_0", + "impartedBuildProperties": { + "buildSettings": {} + }, + "name": "Debug" + }, + { + "buildSettings": { + "CLANG_ENABLE_MODULES": "YES", + "CURRENT_PROJECT_VERSION": "1", + "EXECUTABLE_NAME": "MyBinaryDependency_-5B0EB316CCB8BC4_PackageProduct", + "GENERATE_INFOPLIST_FILE": "YES", + "MARKETING_VERSION": "1.0", + "PRODUCT_BUNDLE_IDENTIFIER": "MyBinaryDependency", + "PRODUCT_MODULE_NAME": "MyBinaryDependency", + "PRODUCT_NAME": "MyBinaryDependency_-5B0EB316CCB8BC4_PackageProduct", + "SDKROOT": "auto", + "SDK_VARIANT": "auto", + "SWIFT_PACKAGE_NAME": "mybinarydependency", + "TARGET_BUILD_DIR": "$(TARGET_BUILD_DIR)/PackageFrameworks", + "TARGET_NAME": "MyBinaryDependency product", + "USES_SWIFTPM_UNSAFE_FLAGS": "NO" + }, + "guid": "PACKAGE-PRODUCT:MyBinaryDependency--5B0EB316CCB8BC4-dynamic::BUILDCONFIG_1", + "impartedBuildProperties": { + "buildSettings": {} + }, + "name": "Release" + } + ], + "buildPhases": [ + { + "buildFiles": [ + { + "codeSignOnCopy": "true", + "fileReference": "PACKAGE:/Users/thedderwick/Desktop/SPMTest/MyBinaryDependency::MAINGROUP::REF_1::REF_1", + "guid": "PACKAGE-PRODUCT:MyBinaryDependency--5B0EB316CCB8BC4-dynamic::BUILDPHASE_0::0", + "platformFilters": [], + "removeHeadersOnCopy": "true" + }, + { + "guid": "PACKAGE-PRODUCT:MyBinaryDependency--5B0EB316CCB8BC4-dynamic::BUILDPHASE_0::1", + "platformFilters": [], + "targetReference": "PACKAGE-TARGET:_Stub" + } + ], + "guid": "PACKAGE-PRODUCT:MyBinaryDependency--5B0EB316CCB8BC4-dynamic::BUILDPHASE_0", + "type": "com.apple.buildphase.frameworks" + }, + { + "buildFiles": [], + "guid": "PACKAGE-PRODUCT:MyBinaryDependency--5B0EB316CCB8BC4-dynamic::BUILDPHASE_1", + "type": "com.apple.buildphase.sources" + } + ], + "buildRules": [], + "dependencies": [ + { + "guid": "PACKAGE-TARGET:_Stub", + "platformFilters": [] + } + ], + "guid": "PACKAGE-PRODUCT:MyBinaryDependency--5B0EB316CCB8BC4-dynamic", + "name": "MyBinaryDependency", + "productReference": { + "guid": "PRODUCTREF-PACKAGE-PRODUCT:MyBinaryDependency--5B0EB316CCB8BC4-dynamic", + "name": "MyBinaryDependency_-5B0EB316CCB8BC4_PackageProduct.framework", + "type": "file" + }, + "productTypeIdentifier": "com.apple.product-type.framework", + "type": "standard" +} diff --git a/TestAssets/PIFCaches/SPMTest/PIFCache/target/TARGET@v12_hash=5c5fac44a4728a01cbe089b857eb0636-json b/TestAssets/PIFCaches/SPMTest/PIFCache/target/TARGET@v12_hash=5c5fac44a4728a01cbe089b857eb0636-json new file mode 100644 index 0000000..bf7f212 --- /dev/null +++ b/TestAssets/PIFCaches/SPMTest/PIFCache/target/TARGET@v12_hash=5c5fac44a4728a01cbe089b857eb0636-json @@ -0,0 +1,128 @@ +{ + "approvedByUser": "true", + "buildConfigurations": [ + { + "buildSettings": { + "CLANG_COVERAGE_MAPPING_LINKER_ARGS": "NO", + "CLANG_ENABLE_MODULES": "YES", + "DEFINES_MODULE": "YES", + "EXECUTABLE_NAME": "_Stub.o", + "GENERATE_MASTER_OBJECT_FILE": "NO", + "MACH_O_TYPE": "mh_object", + "MODULEMAP_FILE_CONTENTS": "module _Stub {\nheader \"_Stub-Swift.h\"\nexport *\n}", + "MODULEMAP_PATH": "$(GENERATED_MODULEMAP_DIR)/_Stub.modulemap", + "PACKAGE_RESOURCE_TARGET_KIND": "regular", + "PRODUCT_BUNDLE_IDENTIFIER": "_Stub", + "PRODUCT_MODULE_NAME": "_Stub", + "PRODUCT_NAME": "$(TARGET_NAME)", + "SDKROOT": "auto", + "SDK_VARIANT": "auto", + "STRIP_INSTALLED_PRODUCT": "NO", + "SUPPORTS_TEXT_BASED_API": "NO", + "SWIFT_ENABLE_BARE_SLASH_REGEX": "NO", + "SWIFT_OBJC_INTERFACE_HEADER_DIR": "$(GENERATED_MODULEMAP_DIR)", + "SWIFT_OBJC_INTERFACE_HEADER_NAME": "_Stub-Swift.h", + "SWIFT_PACKAGE_NAME": "mybinarydependency", + "SWIFT_VERSION": "5", + "TAPI_DYLIB_INSTALL_NAME": "_Stub", + "TARGET_NAME": "_Stub" + }, + "guid": "PACKAGE-TARGET:_Stub::BUILDCONFIG_0", + "impartedBuildProperties": { + "buildSettings": { + "FRAMEWORK_SEARCH_PATHS": [ + "$(BUILT_PRODUCTS_DIR)/PackageFrameworks", + "$(inherited)" + ], + "LD_RUNPATH_SEARCH_PATHS": [ + "$(BUILT_PRODUCTS_DIR)/PackageFrameworks", + "$(inherited)" + ], + "OTHER_CFLAGS": [ + "-fmodule-map-file=$(GENERATED_MODULEMAP_DIR)/_Stub.modulemap", + "$(inherited)" + ], + "OTHER_LDFLAGS": [ + "-Wl,-no_warn_duplicate_libraries", + "$(inherited)" + ], + "OTHER_LDRFLAGS": [] + } + }, + "name": "Debug" + }, + { + "buildSettings": { + "CLANG_COVERAGE_MAPPING_LINKER_ARGS": "NO", + "CLANG_ENABLE_MODULES": "YES", + "DEFINES_MODULE": "YES", + "EXECUTABLE_NAME": "_Stub.o", + "GENERATE_MASTER_OBJECT_FILE": "NO", + "MACH_O_TYPE": "mh_object", + "MODULEMAP_FILE_CONTENTS": "module _Stub {\nheader \"_Stub-Swift.h\"\nexport *\n}", + "MODULEMAP_PATH": "$(GENERATED_MODULEMAP_DIR)/_Stub.modulemap", + "PACKAGE_RESOURCE_TARGET_KIND": "regular", + "PRODUCT_BUNDLE_IDENTIFIER": "_Stub", + "PRODUCT_MODULE_NAME": "_Stub", + "PRODUCT_NAME": "$(TARGET_NAME)", + "SDKROOT": "auto", + "SDK_VARIANT": "auto", + "STRIP_INSTALLED_PRODUCT": "NO", + "SUPPORTS_TEXT_BASED_API": "NO", + "SWIFT_ENABLE_BARE_SLASH_REGEX": "NO", + "SWIFT_OBJC_INTERFACE_HEADER_DIR": "$(GENERATED_MODULEMAP_DIR)", + "SWIFT_OBJC_INTERFACE_HEADER_NAME": "_Stub-Swift.h", + "SWIFT_PACKAGE_NAME": "mybinarydependency", + "SWIFT_VERSION": "5", + "TAPI_DYLIB_INSTALL_NAME": "_Stub", + "TARGET_NAME": "_Stub" + }, + "guid": "PACKAGE-TARGET:_Stub::BUILDCONFIG_1", + "impartedBuildProperties": { + "buildSettings": { + "FRAMEWORK_SEARCH_PATHS": [ + "$(BUILT_PRODUCTS_DIR)/PackageFrameworks", + "$(inherited)" + ], + "OTHER_CFLAGS": [ + "-fmodule-map-file=$(GENERATED_MODULEMAP_DIR)/_Stub.modulemap", + "$(inherited)" + ], + "OTHER_LDFLAGS": [ + "-Wl,-no_warn_duplicate_libraries", + "$(inherited)" + ], + "OTHER_LDRFLAGS": [] + } + }, + "name": "Release" + } + ], + "buildPhases": [ + { + "buildFiles": [ + { + "codeSignOnCopy": "false", + "fileReference": "PACKAGE:/Users/thedderwick/Desktop/SPMTest/MyBinaryDependency::MAINGROUP::REF_2::REF_0", + "guid": "PACKAGE-TARGET:_Stub::BUILDPHASE_0::0", + "platformFilters": [], + "removeHeadersOnCopy": "false" + } + ], + "guid": "PACKAGE-TARGET:_Stub::BUILDPHASE_0", + "type": "com.apple.buildphase.sources" + } + ], + "buildRules": [], + "dependencies": [], + "dynamicTargetVariantGuid": "PACKAGE-TARGET:_Stub-F15A07A8D42-dynamic", + "guid": "PACKAGE-TARGET:_Stub", + "name": "_Stub", + "productReference": { + "guid": "PRODUCTREF-PACKAGE-TARGET:_Stub", + "name": "_Stub.o", + "type": "file" + }, + "productTypeIdentifier": "com.apple.product-type.objfile", + "type": "standard" +} diff --git a/TestAssets/PIFCaches/SPMTest/PIFCache/target/TARGET@v12_hash=74b26d184242037107e47412d7443f08-json b/TestAssets/PIFCaches/SPMTest/PIFCache/target/TARGET@v12_hash=74b26d184242037107e47412d7443f08-json new file mode 100644 index 0000000..a3b683f --- /dev/null +++ b/TestAssets/PIFCaches/SPMTest/PIFCache/target/TARGET@v12_hash=74b26d184242037107e47412d7443f08-json @@ -0,0 +1,68 @@ +{ + "approvedByUser": "true", + "buildConfigurations": [ + { + "buildSettings": { + "SDKROOT": "auto", + "SDK_VARIANT": "auto", + "USES_SWIFTPM_UNSAFE_FLAGS": "NO" + }, + "guid": "PACKAGE-PRODUCT:MyLibrary::BUILDCONFIG_0", + "impartedBuildProperties": { + "buildSettings": {} + }, + "name": "Debug" + }, + { + "buildSettings": { + "SDKROOT": "auto", + "SDK_VARIANT": "auto", + "USES_SWIFTPM_UNSAFE_FLAGS": "NO" + }, + "guid": "PACKAGE-PRODUCT:MyLibrary::BUILDCONFIG_1", + "impartedBuildProperties": { + "buildSettings": {} + }, + "name": "Release" + } + ], + "dependencies": [ + { + "guid": "PACKAGE-TARGET:MyLibrary", + "platformFilters": [] + }, + { + "guid": "PACKAGE-PRODUCT:MyTransitiveLibrary", + "platformFilters": [] + }, + { + "guid": "PACKAGE-PRODUCT:MyCommonLibrary", + "platformFilters": [] + } + ], + "dynamicTargetVariantGuid": "PACKAGE-PRODUCT:MyLibrary--71ED4B7C0CDEC55C-dynamic", + "frameworksBuildPhase": { + "buildFiles": [ + { + "guid": "PACKAGE-PRODUCT:MyLibrary::BUILDPHASE_0::0", + "platformFilters": [], + "targetReference": "PACKAGE-TARGET:MyLibrary" + }, + { + "guid": "PACKAGE-PRODUCT:MyLibrary::BUILDPHASE_0::1", + "platformFilters": [], + "targetReference": "PACKAGE-PRODUCT:MyTransitiveLibrary" + }, + { + "guid": "PACKAGE-PRODUCT:MyLibrary::BUILDPHASE_0::2", + "platformFilters": [], + "targetReference": "PACKAGE-PRODUCT:MyCommonLibrary" + } + ], + "guid": "PACKAGE-PRODUCT:MyLibrary::BUILDPHASE_0", + "type": "com.apple.buildphase.frameworks" + }, + "guid": "PACKAGE-PRODUCT:MyLibrary", + "name": "MyLibrary", + "type": "packageProduct" +} diff --git a/TestAssets/PIFCaches/SPMTest/PIFCache/target/TARGET@v12_hash=7fe91a1e2b8f90031d23796421a46a09-json b/TestAssets/PIFCaches/SPMTest/PIFCache/target/TARGET@v12_hash=7fe91a1e2b8f90031d23796421a46a09-json new file mode 100644 index 0000000..c869509 --- /dev/null +++ b/TestAssets/PIFCaches/SPMTest/PIFCache/target/TARGET@v12_hash=7fe91a1e2b8f90031d23796421a46a09-json @@ -0,0 +1,50 @@ +{ + "approvedByUser": "true", + "buildConfigurations": [ + { + "buildSettings": { + "SDKROOT": "auto", + "SDK_VARIANT": "auto", + "USES_SWIFTPM_UNSAFE_FLAGS": "NO" + }, + "guid": "PACKAGE-PRODUCT:MyCommonLibrary::BUILDCONFIG_0", + "impartedBuildProperties": { + "buildSettings": {} + }, + "name": "Debug" + }, + { + "buildSettings": { + "SDKROOT": "auto", + "SDK_VARIANT": "auto", + "USES_SWIFTPM_UNSAFE_FLAGS": "NO" + }, + "guid": "PACKAGE-PRODUCT:MyCommonLibrary::BUILDCONFIG_1", + "impartedBuildProperties": { + "buildSettings": {} + }, + "name": "Release" + } + ], + "dependencies": [ + { + "guid": "PACKAGE-TARGET:MyCommonLibrary", + "platformFilters": [] + } + ], + "dynamicTargetVariantGuid": "PACKAGE-PRODUCT:MyCommonLibrary--138526C95691DCED-dynamic", + "frameworksBuildPhase": { + "buildFiles": [ + { + "guid": "PACKAGE-PRODUCT:MyCommonLibrary::BUILDPHASE_0::0", + "platformFilters": [], + "targetReference": "PACKAGE-TARGET:MyCommonLibrary" + } + ], + "guid": "PACKAGE-PRODUCT:MyCommonLibrary::BUILDPHASE_0", + "type": "com.apple.buildphase.frameworks" + }, + "guid": "PACKAGE-PRODUCT:MyCommonLibrary", + "name": "MyCommonLibrary", + "type": "packageProduct" +} diff --git a/TestAssets/PIFCaches/SPMTest/PIFCache/target/TARGET@v12_hash=85e5507488bfe17e9e4e18b38ecf6a38-json b/TestAssets/PIFCaches/SPMTest/PIFCache/target/TARGET@v12_hash=85e5507488bfe17e9e4e18b38ecf6a38-json new file mode 100644 index 0000000..c28cc3c --- /dev/null +++ b/TestAssets/PIFCaches/SPMTest/PIFCache/target/TARGET@v12_hash=85e5507488bfe17e9e4e18b38ecf6a38-json @@ -0,0 +1,110 @@ +{ + "approvedByUser": "true", + "buildConfigurations": [ + { + "buildSettings": { + "CLANG_ENABLE_MODULES": "YES", + "CURRENT_PROJECT_VERSION": "1", + "EXECUTABLE_NAME": "MyTransitiveLibrary_29D6C569F63C2FD7_PackageProduct", + "GENERATE_INFOPLIST_FILE": "YES", + "MARKETING_VERSION": "1.0", + "PRODUCT_BUNDLE_IDENTIFIER": "MyTransitiveLibrary", + "PRODUCT_MODULE_NAME": "MyTransitiveLibrary", + "PRODUCT_NAME": "MyTransitiveLibrary_29D6C569F63C2FD7_PackageProduct", + "SDKROOT": "auto", + "SDK_VARIANT": "auto", + "SWIFT_PACKAGE_NAME": "mytransitivelibrary", + "TARGET_BUILD_DIR": "$(TARGET_BUILD_DIR)/PackageFrameworks", + "TARGET_NAME": "MyTransitiveLibrary product", + "USES_SWIFTPM_UNSAFE_FLAGS": "NO" + }, + "guid": "PACKAGE-PRODUCT:MyTransitiveLibrary-29D6C569F63C2FD7-dynamic::BUILDCONFIG_0", + "impartedBuildProperties": { + "buildSettings": {} + }, + "name": "Debug" + }, + { + "buildSettings": { + "CLANG_ENABLE_MODULES": "YES", + "CURRENT_PROJECT_VERSION": "1", + "EXECUTABLE_NAME": "MyTransitiveLibrary_29D6C569F63C2FD7_PackageProduct", + "GENERATE_INFOPLIST_FILE": "YES", + "MARKETING_VERSION": "1.0", + "PRODUCT_BUNDLE_IDENTIFIER": "MyTransitiveLibrary", + "PRODUCT_MODULE_NAME": "MyTransitiveLibrary", + "PRODUCT_NAME": "MyTransitiveLibrary_29D6C569F63C2FD7_PackageProduct", + "SDKROOT": "auto", + "SDK_VARIANT": "auto", + "SWIFT_PACKAGE_NAME": "mytransitivelibrary", + "TARGET_BUILD_DIR": "$(TARGET_BUILD_DIR)/PackageFrameworks", + "TARGET_NAME": "MyTransitiveLibrary product", + "USES_SWIFTPM_UNSAFE_FLAGS": "NO" + }, + "guid": "PACKAGE-PRODUCT:MyTransitiveLibrary-29D6C569F63C2FD7-dynamic::BUILDCONFIG_1", + "impartedBuildProperties": { + "buildSettings": {} + }, + "name": "Release" + } + ], + "buildPhases": [ + { + "buildFiles": [ + { + "guid": "PACKAGE-PRODUCT:MyTransitiveLibrary-29D6C569F63C2FD7-dynamic::BUILDPHASE_0::0", + "platformFilters": [], + "targetReference": "PACKAGE-TARGET:MyTransitiveLibrary" + }, + { + "guid": "PACKAGE-PRODUCT:MyTransitiveLibrary-29D6C569F63C2FD7-dynamic::BUILDPHASE_0::1", + "platformFilters": [], + "targetReference": "PACKAGE-PRODUCT:MyCommonLibrary" + }, + { + "guid": "PACKAGE-PRODUCT:MyTransitiveLibrary-29D6C569F63C2FD7-dynamic::BUILDPHASE_0::2", + "platformFilters": [], + "targetReference": "PACKAGE-PRODUCT:MyBinaryDependency" + }, + { + "codeSignOnCopy": "true", + "fileReference": "PACKAGE:/Users/thedderwick/Desktop/SPMTest/MyTransitiveLibrary::MAINGROUP::REF_1::REF_1", + "guid": "PACKAGE-PRODUCT:MyTransitiveLibrary-29D6C569F63C2FD7-dynamic::BUILDPHASE_0::3", + "platformFilters": [], + "removeHeadersOnCopy": "true" + } + ], + "guid": "PACKAGE-PRODUCT:MyTransitiveLibrary-29D6C569F63C2FD7-dynamic::BUILDPHASE_0", + "type": "com.apple.buildphase.frameworks" + }, + { + "buildFiles": [], + "guid": "PACKAGE-PRODUCT:MyTransitiveLibrary-29D6C569F63C2FD7-dynamic::BUILDPHASE_1", + "type": "com.apple.buildphase.sources" + } + ], + "buildRules": [], + "dependencies": [ + { + "guid": "PACKAGE-TARGET:MyTransitiveLibrary", + "platformFilters": [] + }, + { + "guid": "PACKAGE-PRODUCT:MyCommonLibrary", + "platformFilters": [] + }, + { + "guid": "PACKAGE-PRODUCT:MyBinaryDependency", + "platformFilters": [] + } + ], + "guid": "PACKAGE-PRODUCT:MyTransitiveLibrary-29D6C569F63C2FD7-dynamic", + "name": "MyTransitiveLibrary", + "productReference": { + "guid": "PRODUCTREF-PACKAGE-PRODUCT:MyTransitiveLibrary-29D6C569F63C2FD7-dynamic", + "name": "MyTransitiveLibrary_29D6C569F63C2FD7_PackageProduct.framework", + "type": "file" + }, + "productTypeIdentifier": "com.apple.product-type.framework", + "type": "standard" +} diff --git a/TestAssets/PIFCaches/SPMTest/PIFCache/target/TARGET@v12_hash=8de09bc5397dcfbae930a9e8ab952e62-json b/TestAssets/PIFCaches/SPMTest/PIFCache/target/TARGET@v12_hash=8de09bc5397dcfbae930a9e8ab952e62-json new file mode 100644 index 0000000..34f0e82 --- /dev/null +++ b/TestAssets/PIFCaches/SPMTest/PIFCache/target/TARGET@v12_hash=8de09bc5397dcfbae930a9e8ab952e62-json @@ -0,0 +1,125 @@ +{ + "approvedByUser": "true", + "buildConfigurations": [ + { + "buildSettings": { + "CLANG_ENABLE_MODULES": "YES", + "CURRENT_PROJECT_VERSION": "1", + "DEFINES_MODULE": "YES", + "EXECUTABLE_NAME": "_Stub", + "GENERATE_INFOPLIST_FILE": "YES", + "MARKETING_VERSION": "1.0", + "MODULEMAP_FILE_CONTENTS": "module _Stub {\nheader \"_Stub-Swift.h\"\nexport *\n}", + "MODULEMAP_PATH": "$(GENERATED_MODULEMAP_DIR)/_Stub.modulemap", + "PACKAGE_RESOURCE_TARGET_KIND": "regular", + "PRODUCT_BUNDLE_IDENTIFIER": "_Stub", + "PRODUCT_MODULE_NAME": "_Stub", + "PRODUCT_NAME": "_Stub", + "SDKROOT": "auto", + "SDK_VARIANT": "auto", + "SUPPORTS_TEXT_BASED_API": "NO", + "SWIFT_ENABLE_BARE_SLASH_REGEX": "NO", + "SWIFT_OBJC_INTERFACE_HEADER_DIR": "$(GENERATED_MODULEMAP_DIR)", + "SWIFT_OBJC_INTERFACE_HEADER_NAME": "_Stub-Swift.h", + "SWIFT_PACKAGE_NAME": "mybinarydependency", + "SWIFT_VERSION": "5", + "TARGET_BUILD_DIR": "$(TARGET_BUILD_DIR)/PackageFrameworks", + "TARGET_NAME": "_Stub" + }, + "guid": "PACKAGE-TARGET:_Stub-F15A07A8D42-dynamic::BUILDCONFIG_0", + "impartedBuildProperties": { + "buildSettings": { + "FRAMEWORK_SEARCH_PATHS": [ + "$(BUILT_PRODUCTS_DIR)/PackageFrameworks", + "$(inherited)" + ], + "LD_RUNPATH_SEARCH_PATHS": [ + "$(BUILT_PRODUCTS_DIR)/PackageFrameworks", + "$(inherited)" + ], + "OTHER_CFLAGS": [ + "-fmodule-map-file=$(GENERATED_MODULEMAP_DIR)/_Stub.modulemap", + "$(inherited)" + ], + "OTHER_LDFLAGS": [ + "-Wl,-no_warn_duplicate_libraries", + "$(inherited)" + ], + "OTHER_LDRFLAGS": [] + } + }, + "name": "Debug" + }, + { + "buildSettings": { + "CLANG_ENABLE_MODULES": "YES", + "CURRENT_PROJECT_VERSION": "1", + "DEFINES_MODULE": "YES", + "EXECUTABLE_NAME": "_Stub", + "GENERATE_INFOPLIST_FILE": "YES", + "MARKETING_VERSION": "1.0", + "MODULEMAP_FILE_CONTENTS": "module _Stub {\nheader \"_Stub-Swift.h\"\nexport *\n}", + "MODULEMAP_PATH": "$(GENERATED_MODULEMAP_DIR)/_Stub.modulemap", + "PACKAGE_RESOURCE_TARGET_KIND": "regular", + "PRODUCT_BUNDLE_IDENTIFIER": "_Stub", + "PRODUCT_MODULE_NAME": "_Stub", + "PRODUCT_NAME": "_Stub", + "SDKROOT": "auto", + "SDK_VARIANT": "auto", + "SUPPORTS_TEXT_BASED_API": "NO", + "SWIFT_ENABLE_BARE_SLASH_REGEX": "NO", + "SWIFT_OBJC_INTERFACE_HEADER_DIR": "$(GENERATED_MODULEMAP_DIR)", + "SWIFT_OBJC_INTERFACE_HEADER_NAME": "_Stub-Swift.h", + "SWIFT_PACKAGE_NAME": "mybinarydependency", + "SWIFT_VERSION": "5", + "TARGET_BUILD_DIR": "$(TARGET_BUILD_DIR)/PackageFrameworks", + "TARGET_NAME": "_Stub" + }, + "guid": "PACKAGE-TARGET:_Stub-F15A07A8D42-dynamic::BUILDCONFIG_1", + "impartedBuildProperties": { + "buildSettings": { + "FRAMEWORK_SEARCH_PATHS": [ + "$(BUILT_PRODUCTS_DIR)/PackageFrameworks", + "$(inherited)" + ], + "OTHER_CFLAGS": [ + "-fmodule-map-file=$(GENERATED_MODULEMAP_DIR)/_Stub.modulemap", + "$(inherited)" + ], + "OTHER_LDFLAGS": [ + "-Wl,-no_warn_duplicate_libraries", + "$(inherited)" + ], + "OTHER_LDRFLAGS": [] + } + }, + "name": "Release" + } + ], + "buildPhases": [ + { + "buildFiles": [ + { + "codeSignOnCopy": "false", + "fileReference": "PACKAGE:/Users/thedderwick/Desktop/SPMTest/MyBinaryDependency::MAINGROUP::REF_3::REF_0", + "guid": "PACKAGE-TARGET:_Stub-F15A07A8D42-dynamic::BUILDPHASE_0::0", + "platformFilters": [], + "removeHeadersOnCopy": "false" + } + ], + "guid": "PACKAGE-TARGET:_Stub-F15A07A8D42-dynamic::BUILDPHASE_0", + "type": "com.apple.buildphase.sources" + } + ], + "buildRules": [], + "dependencies": [], + "guid": "PACKAGE-TARGET:_Stub-F15A07A8D42-dynamic", + "name": "_Stub", + "productReference": { + "guid": "PRODUCTREF-PACKAGE-TARGET:_Stub-F15A07A8D42-dynamic", + "name": "_Stub.framework", + "type": "file" + }, + "productTypeIdentifier": "com.apple.product-type.framework", + "type": "standard" +} diff --git a/TestAssets/PIFCaches/SPMTest/PIFCache/target/TARGET@v12_hash=9b39f33b643d76adea258dfa2de06201-json b/TestAssets/PIFCaches/SPMTest/PIFCache/target/TARGET@v12_hash=9b39f33b643d76adea258dfa2de06201-json new file mode 100644 index 0000000..93fdf4d --- /dev/null +++ b/TestAssets/PIFCaches/SPMTest/PIFCache/target/TARGET@v12_hash=9b39f33b643d76adea258dfa2de06201-json @@ -0,0 +1,137 @@ +{ + "approvedByUser": "true", + "buildConfigurations": [ + { + "buildSettings": { + "CLANG_COVERAGE_MAPPING_LINKER_ARGS": "NO", + "CLANG_ENABLE_MODULES": "YES", + "DEFINES_MODULE": "YES", + "EXECUTABLE_NAME": "MyLibrary.o", + "GENERATE_MASTER_OBJECT_FILE": "NO", + "MACH_O_TYPE": "mh_object", + "MODULEMAP_FILE_CONTENTS": "module MyLibrary {\nheader \"MyLibrary-Swift.h\"\nexport *\n}", + "MODULEMAP_PATH": "$(GENERATED_MODULEMAP_DIR)/MyLibrary.modulemap", + "PACKAGE_RESOURCE_TARGET_KIND": "regular", + "PRODUCT_BUNDLE_IDENTIFIER": "MyLibrary", + "PRODUCT_MODULE_NAME": "MyLibrary", + "PRODUCT_NAME": "$(TARGET_NAME)", + "SDKROOT": "auto", + "SDK_VARIANT": "auto", + "STRIP_INSTALLED_PRODUCT": "NO", + "SUPPORTS_TEXT_BASED_API": "NO", + "SWIFT_ENABLE_BARE_SLASH_REGEX": "NO", + "SWIFT_OBJC_INTERFACE_HEADER_DIR": "$(GENERATED_MODULEMAP_DIR)", + "SWIFT_OBJC_INTERFACE_HEADER_NAME": "MyLibrary-Swift.h", + "SWIFT_PACKAGE_NAME": "mylibrary", + "SWIFT_VERSION": "5", + "TAPI_DYLIB_INSTALL_NAME": "MyLibrary", + "TARGET_NAME": "MyLibrary" + }, + "guid": "PACKAGE-TARGET:MyLibrary::BUILDCONFIG_0", + "impartedBuildProperties": { + "buildSettings": { + "FRAMEWORK_SEARCH_PATHS": [ + "$(BUILT_PRODUCTS_DIR)/PackageFrameworks", + "$(inherited)" + ], + "LD_RUNPATH_SEARCH_PATHS": [ + "$(BUILT_PRODUCTS_DIR)/PackageFrameworks", + "$(inherited)" + ], + "OTHER_CFLAGS": [ + "-fmodule-map-file=$(GENERATED_MODULEMAP_DIR)/MyLibrary.modulemap", + "$(inherited)" + ], + "OTHER_LDFLAGS": [ + "-Wl,-no_warn_duplicate_libraries", + "$(inherited)" + ], + "OTHER_LDRFLAGS": [] + } + }, + "name": "Debug" + }, + { + "buildSettings": { + "CLANG_COVERAGE_MAPPING_LINKER_ARGS": "NO", + "CLANG_ENABLE_MODULES": "YES", + "DEFINES_MODULE": "YES", + "EXECUTABLE_NAME": "MyLibrary.o", + "GENERATE_MASTER_OBJECT_FILE": "NO", + "MACH_O_TYPE": "mh_object", + "MODULEMAP_FILE_CONTENTS": "module MyLibrary {\nheader \"MyLibrary-Swift.h\"\nexport *\n}", + "MODULEMAP_PATH": "$(GENERATED_MODULEMAP_DIR)/MyLibrary.modulemap", + "PACKAGE_RESOURCE_TARGET_KIND": "regular", + "PRODUCT_BUNDLE_IDENTIFIER": "MyLibrary", + "PRODUCT_MODULE_NAME": "MyLibrary", + "PRODUCT_NAME": "$(TARGET_NAME)", + "SDKROOT": "auto", + "SDK_VARIANT": "auto", + "STRIP_INSTALLED_PRODUCT": "NO", + "SUPPORTS_TEXT_BASED_API": "NO", + "SWIFT_ENABLE_BARE_SLASH_REGEX": "NO", + "SWIFT_OBJC_INTERFACE_HEADER_DIR": "$(GENERATED_MODULEMAP_DIR)", + "SWIFT_OBJC_INTERFACE_HEADER_NAME": "MyLibrary-Swift.h", + "SWIFT_PACKAGE_NAME": "mylibrary", + "SWIFT_VERSION": "5", + "TAPI_DYLIB_INSTALL_NAME": "MyLibrary", + "TARGET_NAME": "MyLibrary" + }, + "guid": "PACKAGE-TARGET:MyLibrary::BUILDCONFIG_1", + "impartedBuildProperties": { + "buildSettings": { + "FRAMEWORK_SEARCH_PATHS": [ + "$(BUILT_PRODUCTS_DIR)/PackageFrameworks", + "$(inherited)" + ], + "OTHER_CFLAGS": [ + "-fmodule-map-file=$(GENERATED_MODULEMAP_DIR)/MyLibrary.modulemap", + "$(inherited)" + ], + "OTHER_LDFLAGS": [ + "-Wl,-no_warn_duplicate_libraries", + "$(inherited)" + ], + "OTHER_LDRFLAGS": [] + } + }, + "name": "Release" + } + ], + "buildPhases": [ + { + "buildFiles": [ + { + "codeSignOnCopy": "false", + "fileReference": "PACKAGE:/Users/thedderwick/Desktop/SPMTest/MyLibrary::MAINGROUP::REF_2::REF_0", + "guid": "PACKAGE-TARGET:MyLibrary::BUILDPHASE_0::0", + "platformFilters": [], + "removeHeadersOnCopy": "false" + } + ], + "guid": "PACKAGE-TARGET:MyLibrary::BUILDPHASE_0", + "type": "com.apple.buildphase.sources" + } + ], + "buildRules": [], + "dependencies": [ + { + "guid": "PACKAGE-PRODUCT:MyTransitiveLibrary", + "platformFilters": [] + }, + { + "guid": "PACKAGE-PRODUCT:MyCommonLibrary", + "platformFilters": [] + } + ], + "dynamicTargetVariantGuid": "PACKAGE-TARGET:MyLibrary--71ED4B7C0CDEC55C-dynamic", + "guid": "PACKAGE-TARGET:MyLibrary", + "name": "MyLibrary", + "productReference": { + "guid": "PRODUCTREF-PACKAGE-TARGET:MyLibrary", + "name": "MyLibrary.o", + "type": "file" + }, + "productTypeIdentifier": "com.apple.product-type.objfile", + "type": "standard" +} diff --git a/TestAssets/PIFCaches/SPMTest/PIFCache/target/TARGET@v12_hash=c5eeab26a0e301a2419ee7e6d1254b52-json b/TestAssets/PIFCaches/SPMTest/PIFCache/target/TARGET@v12_hash=c5eeab26a0e301a2419ee7e6d1254b52-json new file mode 100644 index 0000000..d7daddd --- /dev/null +++ b/TestAssets/PIFCaches/SPMTest/PIFCache/target/TARGET@v12_hash=c5eeab26a0e301a2419ee7e6d1254b52-json @@ -0,0 +1,85 @@ +{ + "approvedByUser": "true", + "buildConfigurations": [ + { + "buildSettings": { + "CLANG_ENABLE_MODULES": "YES", + "CURRENT_PROJECT_VERSION": "1", + "EXECUTABLE_NAME": "MyCommonLibrary_-138526C95691DCED_PackageProduct", + "GENERATE_INFOPLIST_FILE": "YES", + "MARKETING_VERSION": "1.0", + "PRODUCT_BUNDLE_IDENTIFIER": "MyCommonLibrary", + "PRODUCT_MODULE_NAME": "MyCommonLibrary", + "PRODUCT_NAME": "MyCommonLibrary_-138526C95691DCED_PackageProduct", + "SDKROOT": "auto", + "SDK_VARIANT": "auto", + "SWIFT_PACKAGE_NAME": "mycommonlibrary", + "TARGET_BUILD_DIR": "$(TARGET_BUILD_DIR)/PackageFrameworks", + "TARGET_NAME": "MyCommonLibrary product", + "USES_SWIFTPM_UNSAFE_FLAGS": "NO" + }, + "guid": "PACKAGE-PRODUCT:MyCommonLibrary--138526C95691DCED-dynamic::BUILDCONFIG_0", + "impartedBuildProperties": { + "buildSettings": {} + }, + "name": "Debug" + }, + { + "buildSettings": { + "CLANG_ENABLE_MODULES": "YES", + "CURRENT_PROJECT_VERSION": "1", + "EXECUTABLE_NAME": "MyCommonLibrary_-138526C95691DCED_PackageProduct", + "GENERATE_INFOPLIST_FILE": "YES", + "MARKETING_VERSION": "1.0", + "PRODUCT_BUNDLE_IDENTIFIER": "MyCommonLibrary", + "PRODUCT_MODULE_NAME": "MyCommonLibrary", + "PRODUCT_NAME": "MyCommonLibrary_-138526C95691DCED_PackageProduct", + "SDKROOT": "auto", + "SDK_VARIANT": "auto", + "SWIFT_PACKAGE_NAME": "mycommonlibrary", + "TARGET_BUILD_DIR": "$(TARGET_BUILD_DIR)/PackageFrameworks", + "TARGET_NAME": "MyCommonLibrary product", + "USES_SWIFTPM_UNSAFE_FLAGS": "NO" + }, + "guid": "PACKAGE-PRODUCT:MyCommonLibrary--138526C95691DCED-dynamic::BUILDCONFIG_1", + "impartedBuildProperties": { + "buildSettings": {} + }, + "name": "Release" + } + ], + "buildPhases": [ + { + "buildFiles": [ + { + "guid": "PACKAGE-PRODUCT:MyCommonLibrary--138526C95691DCED-dynamic::BUILDPHASE_0::0", + "platformFilters": [], + "targetReference": "PACKAGE-TARGET:MyCommonLibrary" + } + ], + "guid": "PACKAGE-PRODUCT:MyCommonLibrary--138526C95691DCED-dynamic::BUILDPHASE_0", + "type": "com.apple.buildphase.frameworks" + }, + { + "buildFiles": [], + "guid": "PACKAGE-PRODUCT:MyCommonLibrary--138526C95691DCED-dynamic::BUILDPHASE_1", + "type": "com.apple.buildphase.sources" + } + ], + "buildRules": [], + "dependencies": [ + { + "guid": "PACKAGE-TARGET:MyCommonLibrary", + "platformFilters": [] + } + ], + "guid": "PACKAGE-PRODUCT:MyCommonLibrary--138526C95691DCED-dynamic", + "name": "MyCommonLibrary", + "productReference": { + "guid": "PRODUCTREF-PACKAGE-PRODUCT:MyCommonLibrary--138526C95691DCED-dynamic", + "name": "MyCommonLibrary_-138526C95691DCED_PackageProduct.framework", + "type": "file" + }, + "productTypeIdentifier": "com.apple.product-type.framework", + "type": "standard" +} diff --git a/TestAssets/PIFCaches/SPMTest/PIFCache/target/TARGET@v12_hash=cd6e9e040a1e22f4a8571a7087b8628e-json b/TestAssets/PIFCaches/SPMTest/PIFCache/target/TARGET@v12_hash=cd6e9e040a1e22f4a8571a7087b8628e-json new file mode 100644 index 0000000..2b5a912 --- /dev/null +++ b/TestAssets/PIFCaches/SPMTest/PIFCache/target/TARGET@v12_hash=cd6e9e040a1e22f4a8571a7087b8628e-json @@ -0,0 +1,103 @@ +{ + "approvedByUser": "true", + "buildConfigurations": [ + { + "buildSettings": { + "CLANG_ENABLE_MODULES": "YES", + "CURRENT_PROJECT_VERSION": "1", + "EXECUTABLE_NAME": "MyLibrary_-71ED4B7C0CDEC55C_PackageProduct", + "GENERATE_INFOPLIST_FILE": "YES", + "MARKETING_VERSION": "1.0", + "PRODUCT_BUNDLE_IDENTIFIER": "MyLibrary", + "PRODUCT_MODULE_NAME": "MyLibrary", + "PRODUCT_NAME": "MyLibrary_-71ED4B7C0CDEC55C_PackageProduct", + "SDKROOT": "auto", + "SDK_VARIANT": "auto", + "SWIFT_PACKAGE_NAME": "mylibrary", + "TARGET_BUILD_DIR": "$(TARGET_BUILD_DIR)/PackageFrameworks", + "TARGET_NAME": "MyLibrary product", + "USES_SWIFTPM_UNSAFE_FLAGS": "NO" + }, + "guid": "PACKAGE-PRODUCT:MyLibrary--71ED4B7C0CDEC55C-dynamic::BUILDCONFIG_0", + "impartedBuildProperties": { + "buildSettings": {} + }, + "name": "Debug" + }, + { + "buildSettings": { + "CLANG_ENABLE_MODULES": "YES", + "CURRENT_PROJECT_VERSION": "1", + "EXECUTABLE_NAME": "MyLibrary_-71ED4B7C0CDEC55C_PackageProduct", + "GENERATE_INFOPLIST_FILE": "YES", + "MARKETING_VERSION": "1.0", + "PRODUCT_BUNDLE_IDENTIFIER": "MyLibrary", + "PRODUCT_MODULE_NAME": "MyLibrary", + "PRODUCT_NAME": "MyLibrary_-71ED4B7C0CDEC55C_PackageProduct", + "SDKROOT": "auto", + "SDK_VARIANT": "auto", + "SWIFT_PACKAGE_NAME": "mylibrary", + "TARGET_BUILD_DIR": "$(TARGET_BUILD_DIR)/PackageFrameworks", + "TARGET_NAME": "MyLibrary product", + "USES_SWIFTPM_UNSAFE_FLAGS": "NO" + }, + "guid": "PACKAGE-PRODUCT:MyLibrary--71ED4B7C0CDEC55C-dynamic::BUILDCONFIG_1", + "impartedBuildProperties": { + "buildSettings": {} + }, + "name": "Release" + } + ], + "buildPhases": [ + { + "buildFiles": [ + { + "guid": "PACKAGE-PRODUCT:MyLibrary--71ED4B7C0CDEC55C-dynamic::BUILDPHASE_0::0", + "platformFilters": [], + "targetReference": "PACKAGE-TARGET:MyLibrary" + }, + { + "guid": "PACKAGE-PRODUCT:MyLibrary--71ED4B7C0CDEC55C-dynamic::BUILDPHASE_0::1", + "platformFilters": [], + "targetReference": "PACKAGE-PRODUCT:MyTransitiveLibrary" + }, + { + "guid": "PACKAGE-PRODUCT:MyLibrary--71ED4B7C0CDEC55C-dynamic::BUILDPHASE_0::2", + "platformFilters": [], + "targetReference": "PACKAGE-PRODUCT:MyCommonLibrary" + } + ], + "guid": "PACKAGE-PRODUCT:MyLibrary--71ED4B7C0CDEC55C-dynamic::BUILDPHASE_0", + "type": "com.apple.buildphase.frameworks" + }, + { + "buildFiles": [], + "guid": "PACKAGE-PRODUCT:MyLibrary--71ED4B7C0CDEC55C-dynamic::BUILDPHASE_1", + "type": "com.apple.buildphase.sources" + } + ], + "buildRules": [], + "dependencies": [ + { + "guid": "PACKAGE-TARGET:MyLibrary", + "platformFilters": [] + }, + { + "guid": "PACKAGE-PRODUCT:MyTransitiveLibrary", + "platformFilters": [] + }, + { + "guid": "PACKAGE-PRODUCT:MyCommonLibrary", + "platformFilters": [] + } + ], + "guid": "PACKAGE-PRODUCT:MyLibrary--71ED4B7C0CDEC55C-dynamic", + "name": "MyLibrary", + "productReference": { + "guid": "PRODUCTREF-PACKAGE-PRODUCT:MyLibrary--71ED4B7C0CDEC55C-dynamic", + "name": "MyLibrary_-71ED4B7C0CDEC55C_PackageProduct.framework", + "type": "file" + }, + "productTypeIdentifier": "com.apple.product-type.framework", + "type": "standard" +} diff --git a/TestAssets/PIFCaches/SPMTest/PIFCache/target/TARGET@v12_hash=da546fca3b585ddf35a5a7d6d76bf04c-json b/TestAssets/PIFCaches/SPMTest/PIFCache/target/TARGET@v12_hash=da546fca3b585ddf35a5a7d6d76bf04c-json new file mode 100644 index 0000000..8401536 --- /dev/null +++ b/TestAssets/PIFCaches/SPMTest/PIFCache/target/TARGET@v12_hash=da546fca3b585ddf35a5a7d6d76bf04c-json @@ -0,0 +1,57 @@ +{ + "approvedByUser": "true", + "buildConfigurations": [ + { + "buildSettings": { + "SDKROOT": "auto", + "SDK_VARIANT": "auto", + "USES_SWIFTPM_UNSAFE_FLAGS": "NO" + }, + "guid": "PACKAGE-PRODUCT:MyBinaryDependency::BUILDCONFIG_0", + "impartedBuildProperties": { + "buildSettings": {} + }, + "name": "Debug" + }, + { + "buildSettings": { + "SDKROOT": "auto", + "SDK_VARIANT": "auto", + "USES_SWIFTPM_UNSAFE_FLAGS": "NO" + }, + "guid": "PACKAGE-PRODUCT:MyBinaryDependency::BUILDCONFIG_1", + "impartedBuildProperties": { + "buildSettings": {} + }, + "name": "Release" + } + ], + "dependencies": [ + { + "guid": "PACKAGE-TARGET:_Stub", + "platformFilters": [] + } + ], + "dynamicTargetVariantGuid": "PACKAGE-PRODUCT:MyBinaryDependency--5B0EB316CCB8BC4-dynamic", + "frameworksBuildPhase": { + "buildFiles": [ + { + "codeSignOnCopy": "true", + "fileReference": "PACKAGE:/Users/thedderwick/Desktop/SPMTest/MyBinaryDependency::MAINGROUP::REF_1::REF_0", + "guid": "PACKAGE-PRODUCT:MyBinaryDependency::BUILDPHASE_0::0", + "platformFilters": [], + "removeHeadersOnCopy": "true" + }, + { + "guid": "PACKAGE-PRODUCT:MyBinaryDependency::BUILDPHASE_0::1", + "platformFilters": [], + "targetReference": "PACKAGE-TARGET:_Stub" + } + ], + "guid": "PACKAGE-PRODUCT:MyBinaryDependency::BUILDPHASE_0", + "type": "com.apple.buildphase.frameworks" + }, + "guid": "PACKAGE-PRODUCT:MyBinaryDependency", + "name": "MyBinaryDependency", + "type": "packageProduct" +} diff --git a/TestAssets/PIFCaches/SPMTest/PIFCache/target/TARGET@v12_hash=e0ad90d36af3737ec5215a32f000294d-json b/TestAssets/PIFCaches/SPMTest/PIFCache/target/TARGET@v12_hash=e0ad90d36af3737ec5215a32f000294d-json new file mode 100644 index 0000000..4b1f821 --- /dev/null +++ b/TestAssets/PIFCaches/SPMTest/PIFCache/target/TARGET@v12_hash=e0ad90d36af3737ec5215a32f000294d-json @@ -0,0 +1,125 @@ +{ + "approvedByUser": "true", + "buildConfigurations": [ + { + "buildSettings": { + "CLANG_ENABLE_MODULES": "YES", + "CURRENT_PROJECT_VERSION": "1", + "DEFINES_MODULE": "YES", + "EXECUTABLE_NAME": "MyCommonLibrary", + "GENERATE_INFOPLIST_FILE": "YES", + "MARKETING_VERSION": "1.0", + "MODULEMAP_FILE_CONTENTS": "module MyCommonLibrary {\nheader \"MyCommonLibrary-Swift.h\"\nexport *\n}", + "MODULEMAP_PATH": "$(GENERATED_MODULEMAP_DIR)/MyCommonLibrary.modulemap", + "PACKAGE_RESOURCE_TARGET_KIND": "regular", + "PRODUCT_BUNDLE_IDENTIFIER": "MyCommonLibrary", + "PRODUCT_MODULE_NAME": "MyCommonLibrary", + "PRODUCT_NAME": "MyCommonLibrary", + "SDKROOT": "auto", + "SDK_VARIANT": "auto", + "SUPPORTS_TEXT_BASED_API": "NO", + "SWIFT_ENABLE_BARE_SLASH_REGEX": "NO", + "SWIFT_OBJC_INTERFACE_HEADER_DIR": "$(GENERATED_MODULEMAP_DIR)", + "SWIFT_OBJC_INTERFACE_HEADER_NAME": "MyCommonLibrary-Swift.h", + "SWIFT_PACKAGE_NAME": "mycommonlibrary", + "SWIFT_VERSION": "5", + "TARGET_BUILD_DIR": "$(TARGET_BUILD_DIR)/PackageFrameworks", + "TARGET_NAME": "MyCommonLibrary" + }, + "guid": "PACKAGE-TARGET:MyCommonLibrary--138526C95691DCED-dynamic::BUILDCONFIG_0", + "impartedBuildProperties": { + "buildSettings": { + "FRAMEWORK_SEARCH_PATHS": [ + "$(BUILT_PRODUCTS_DIR)/PackageFrameworks", + "$(inherited)" + ], + "LD_RUNPATH_SEARCH_PATHS": [ + "$(BUILT_PRODUCTS_DIR)/PackageFrameworks", + "$(inherited)" + ], + "OTHER_CFLAGS": [ + "-fmodule-map-file=$(GENERATED_MODULEMAP_DIR)/MyCommonLibrary.modulemap", + "$(inherited)" + ], + "OTHER_LDFLAGS": [ + "-Wl,-no_warn_duplicate_libraries", + "$(inherited)" + ], + "OTHER_LDRFLAGS": [] + } + }, + "name": "Debug" + }, + { + "buildSettings": { + "CLANG_ENABLE_MODULES": "YES", + "CURRENT_PROJECT_VERSION": "1", + "DEFINES_MODULE": "YES", + "EXECUTABLE_NAME": "MyCommonLibrary", + "GENERATE_INFOPLIST_FILE": "YES", + "MARKETING_VERSION": "1.0", + "MODULEMAP_FILE_CONTENTS": "module MyCommonLibrary {\nheader \"MyCommonLibrary-Swift.h\"\nexport *\n}", + "MODULEMAP_PATH": "$(GENERATED_MODULEMAP_DIR)/MyCommonLibrary.modulemap", + "PACKAGE_RESOURCE_TARGET_KIND": "regular", + "PRODUCT_BUNDLE_IDENTIFIER": "MyCommonLibrary", + "PRODUCT_MODULE_NAME": "MyCommonLibrary", + "PRODUCT_NAME": "MyCommonLibrary", + "SDKROOT": "auto", + "SDK_VARIANT": "auto", + "SUPPORTS_TEXT_BASED_API": "NO", + "SWIFT_ENABLE_BARE_SLASH_REGEX": "NO", + "SWIFT_OBJC_INTERFACE_HEADER_DIR": "$(GENERATED_MODULEMAP_DIR)", + "SWIFT_OBJC_INTERFACE_HEADER_NAME": "MyCommonLibrary-Swift.h", + "SWIFT_PACKAGE_NAME": "mycommonlibrary", + "SWIFT_VERSION": "5", + "TARGET_BUILD_DIR": "$(TARGET_BUILD_DIR)/PackageFrameworks", + "TARGET_NAME": "MyCommonLibrary" + }, + "guid": "PACKAGE-TARGET:MyCommonLibrary--138526C95691DCED-dynamic::BUILDCONFIG_1", + "impartedBuildProperties": { + "buildSettings": { + "FRAMEWORK_SEARCH_PATHS": [ + "$(BUILT_PRODUCTS_DIR)/PackageFrameworks", + "$(inherited)" + ], + "OTHER_CFLAGS": [ + "-fmodule-map-file=$(GENERATED_MODULEMAP_DIR)/MyCommonLibrary.modulemap", + "$(inherited)" + ], + "OTHER_LDFLAGS": [ + "-Wl,-no_warn_duplicate_libraries", + "$(inherited)" + ], + "OTHER_LDRFLAGS": [] + } + }, + "name": "Release" + } + ], + "buildPhases": [ + { + "buildFiles": [ + { + "codeSignOnCopy": "false", + "fileReference": "PACKAGE:/Users/thedderwick/Desktop/SPMTest/MyCommonLibrary::MAINGROUP::REF_3::REF_0", + "guid": "PACKAGE-TARGET:MyCommonLibrary--138526C95691DCED-dynamic::BUILDPHASE_0::0", + "platformFilters": [], + "removeHeadersOnCopy": "false" + } + ], + "guid": "PACKAGE-TARGET:MyCommonLibrary--138526C95691DCED-dynamic::BUILDPHASE_0", + "type": "com.apple.buildphase.sources" + } + ], + "buildRules": [], + "dependencies": [], + "guid": "PACKAGE-TARGET:MyCommonLibrary--138526C95691DCED-dynamic", + "name": "MyCommonLibrary", + "productReference": { + "guid": "PRODUCTREF-PACKAGE-TARGET:MyCommonLibrary--138526C95691DCED-dynamic", + "name": "MyCommonLibrary.framework", + "type": "file" + }, + "productTypeIdentifier": "com.apple.product-type.framework", + "type": "standard" +} diff --git a/TestAssets/PIFCaches/SPMTest/PIFCache/target/TARGET@v12_hash=e6cf06600384b639e73079abd6a4553c-json b/TestAssets/PIFCaches/SPMTest/PIFCache/target/TARGET@v12_hash=e6cf06600384b639e73079abd6a4553c-json new file mode 100644 index 0000000..b254780 --- /dev/null +++ b/TestAssets/PIFCaches/SPMTest/PIFCache/target/TARGET@v12_hash=e6cf06600384b639e73079abd6a4553c-json @@ -0,0 +1,150 @@ +{ + "approvedByUser": "true", + "buildConfigurations": [ + { + "buildSettings": { + "CLANG_ENABLE_MODULES": "YES", + "CURRENT_PROJECT_VERSION": "1", + "DEFINES_MODULE": "YES", + "EXECUTABLE_NAME": "MyLibrary", + "GENERATE_INFOPLIST_FILE": "YES", + "MARKETING_VERSION": "1.0", + "MODULEMAP_FILE_CONTENTS": "module MyLibrary {\nheader \"MyLibrary-Swift.h\"\nexport *\n}", + "MODULEMAP_PATH": "$(GENERATED_MODULEMAP_DIR)/MyLibrary.modulemap", + "PACKAGE_RESOURCE_TARGET_KIND": "regular", + "PRODUCT_BUNDLE_IDENTIFIER": "MyLibrary", + "PRODUCT_MODULE_NAME": "MyLibrary", + "PRODUCT_NAME": "MyLibrary", + "SDKROOT": "auto", + "SDK_VARIANT": "auto", + "SUPPORTS_TEXT_BASED_API": "NO", + "SWIFT_ENABLE_BARE_SLASH_REGEX": "NO", + "SWIFT_OBJC_INTERFACE_HEADER_DIR": "$(GENERATED_MODULEMAP_DIR)", + "SWIFT_OBJC_INTERFACE_HEADER_NAME": "MyLibrary-Swift.h", + "SWIFT_PACKAGE_NAME": "mylibrary", + "SWIFT_VERSION": "5", + "TARGET_BUILD_DIR": "$(TARGET_BUILD_DIR)/PackageFrameworks", + "TARGET_NAME": "MyLibrary" + }, + "guid": "PACKAGE-TARGET:MyLibrary--71ED4B7C0CDEC55C-dynamic::BUILDCONFIG_0", + "impartedBuildProperties": { + "buildSettings": { + "FRAMEWORK_SEARCH_PATHS": [ + "$(BUILT_PRODUCTS_DIR)/PackageFrameworks", + "$(inherited)" + ], + "LD_RUNPATH_SEARCH_PATHS": [ + "$(BUILT_PRODUCTS_DIR)/PackageFrameworks", + "$(inherited)" + ], + "OTHER_CFLAGS": [ + "-fmodule-map-file=$(GENERATED_MODULEMAP_DIR)/MyLibrary.modulemap", + "$(inherited)" + ], + "OTHER_LDFLAGS": [ + "-Wl,-no_warn_duplicate_libraries", + "$(inherited)" + ], + "OTHER_LDRFLAGS": [] + } + }, + "name": "Debug" + }, + { + "buildSettings": { + "CLANG_ENABLE_MODULES": "YES", + "CURRENT_PROJECT_VERSION": "1", + "DEFINES_MODULE": "YES", + "EXECUTABLE_NAME": "MyLibrary", + "GENERATE_INFOPLIST_FILE": "YES", + "MARKETING_VERSION": "1.0", + "MODULEMAP_FILE_CONTENTS": "module MyLibrary {\nheader \"MyLibrary-Swift.h\"\nexport *\n}", + "MODULEMAP_PATH": "$(GENERATED_MODULEMAP_DIR)/MyLibrary.modulemap", + "PACKAGE_RESOURCE_TARGET_KIND": "regular", + "PRODUCT_BUNDLE_IDENTIFIER": "MyLibrary", + "PRODUCT_MODULE_NAME": "MyLibrary", + "PRODUCT_NAME": "MyLibrary", + "SDKROOT": "auto", + "SDK_VARIANT": "auto", + "SUPPORTS_TEXT_BASED_API": "NO", + "SWIFT_ENABLE_BARE_SLASH_REGEX": "NO", + "SWIFT_OBJC_INTERFACE_HEADER_DIR": "$(GENERATED_MODULEMAP_DIR)", + "SWIFT_OBJC_INTERFACE_HEADER_NAME": "MyLibrary-Swift.h", + "SWIFT_PACKAGE_NAME": "mylibrary", + "SWIFT_VERSION": "5", + "TARGET_BUILD_DIR": "$(TARGET_BUILD_DIR)/PackageFrameworks", + "TARGET_NAME": "MyLibrary" + }, + "guid": "PACKAGE-TARGET:MyLibrary--71ED4B7C0CDEC55C-dynamic::BUILDCONFIG_1", + "impartedBuildProperties": { + "buildSettings": { + "FRAMEWORK_SEARCH_PATHS": [ + "$(BUILT_PRODUCTS_DIR)/PackageFrameworks", + "$(inherited)" + ], + "OTHER_CFLAGS": [ + "-fmodule-map-file=$(GENERATED_MODULEMAP_DIR)/MyLibrary.modulemap", + "$(inherited)" + ], + "OTHER_LDFLAGS": [ + "-Wl,-no_warn_duplicate_libraries", + "$(inherited)" + ], + "OTHER_LDRFLAGS": [] + } + }, + "name": "Release" + } + ], + "buildPhases": [ + { + "buildFiles": [ + { + "codeSignOnCopy": "false", + "fileReference": "PACKAGE:/Users/thedderwick/Desktop/SPMTest/MyLibrary::MAINGROUP::REF_3::REF_0", + "guid": "PACKAGE-TARGET:MyLibrary--71ED4B7C0CDEC55C-dynamic::BUILDPHASE_0::0", + "platformFilters": [], + "removeHeadersOnCopy": "false" + } + ], + "guid": "PACKAGE-TARGET:MyLibrary--71ED4B7C0CDEC55C-dynamic::BUILDPHASE_0", + "type": "com.apple.buildphase.sources" + }, + { + "buildFiles": [ + { + "guid": "PACKAGE-TARGET:MyLibrary--71ED4B7C0CDEC55C-dynamic::BUILDPHASE_1::0", + "platformFilters": [], + "targetReference": "PACKAGE-PRODUCT:MyTransitiveLibrary" + }, + { + "guid": "PACKAGE-TARGET:MyLibrary--71ED4B7C0CDEC55C-dynamic::BUILDPHASE_1::1", + "platformFilters": [], + "targetReference": "PACKAGE-PRODUCT:MyCommonLibrary" + } + ], + "guid": "PACKAGE-TARGET:MyLibrary--71ED4B7C0CDEC55C-dynamic::BUILDPHASE_1", + "type": "com.apple.buildphase.frameworks" + } + ], + "buildRules": [], + "dependencies": [ + { + "guid": "PACKAGE-PRODUCT:MyTransitiveLibrary", + "platformFilters": [] + }, + { + "guid": "PACKAGE-PRODUCT:MyCommonLibrary", + "platformFilters": [] + } + ], + "guid": "PACKAGE-TARGET:MyLibrary--71ED4B7C0CDEC55C-dynamic", + "name": "MyLibrary", + "productReference": { + "guid": "PRODUCTREF-PACKAGE-TARGET:MyLibrary--71ED4B7C0CDEC55C-dynamic", + "name": "MyLibrary.framework", + "type": "file" + }, + "productTypeIdentifier": "com.apple.product-type.framework", + "type": "standard" +} diff --git a/TestAssets/PIFCaches/SPMTest/PIFCache/target/TARGET@v12_hash=ec86d6a4683c2028c68c9a06f9947bf3-json b/TestAssets/PIFCaches/SPMTest/PIFCache/target/TARGET@v12_hash=ec86d6a4683c2028c68c9a06f9947bf3-json new file mode 100644 index 0000000..9eaaa2e --- /dev/null +++ b/TestAssets/PIFCaches/SPMTest/PIFCache/target/TARGET@v12_hash=ec86d6a4683c2028c68c9a06f9947bf3-json @@ -0,0 +1,157 @@ +{ + "approvedByUser": "true", + "buildConfigurations": [ + { + "buildSettings": { + "CLANG_ENABLE_MODULES": "YES", + "CURRENT_PROJECT_VERSION": "1", + "DEFINES_MODULE": "YES", + "EXECUTABLE_NAME": "MyTransitiveLibrary", + "GENERATE_INFOPLIST_FILE": "YES", + "MARKETING_VERSION": "1.0", + "MODULEMAP_FILE_CONTENTS": "module MyTransitiveLibrary {\nheader \"MyTransitiveLibrary-Swift.h\"\nexport *\n}", + "MODULEMAP_PATH": "$(GENERATED_MODULEMAP_DIR)/MyTransitiveLibrary.modulemap", + "PACKAGE_RESOURCE_TARGET_KIND": "regular", + "PRODUCT_BUNDLE_IDENTIFIER": "MyTransitiveLibrary", + "PRODUCT_MODULE_NAME": "MyTransitiveLibrary", + "PRODUCT_NAME": "MyTransitiveLibrary", + "SDKROOT": "auto", + "SDK_VARIANT": "auto", + "SUPPORTS_TEXT_BASED_API": "NO", + "SWIFT_ENABLE_BARE_SLASH_REGEX": "NO", + "SWIFT_OBJC_INTERFACE_HEADER_DIR": "$(GENERATED_MODULEMAP_DIR)", + "SWIFT_OBJC_INTERFACE_HEADER_NAME": "MyTransitiveLibrary-Swift.h", + "SWIFT_PACKAGE_NAME": "mytransitivelibrary", + "SWIFT_VERSION": "5", + "TARGET_BUILD_DIR": "$(TARGET_BUILD_DIR)/PackageFrameworks", + "TARGET_NAME": "MyTransitiveLibrary" + }, + "guid": "PACKAGE-TARGET:MyTransitiveLibrary-29D6C569F63C2FD7-dynamic::BUILDCONFIG_0", + "impartedBuildProperties": { + "buildSettings": { + "FRAMEWORK_SEARCH_PATHS": [ + "$(BUILT_PRODUCTS_DIR)/PackageFrameworks", + "$(inherited)" + ], + "LD_RUNPATH_SEARCH_PATHS": [ + "$(BUILT_PRODUCTS_DIR)/PackageFrameworks", + "$(inherited)" + ], + "OTHER_CFLAGS": [ + "-fmodule-map-file=$(GENERATED_MODULEMAP_DIR)/MyTransitiveLibrary.modulemap", + "$(inherited)" + ], + "OTHER_LDFLAGS": [ + "-Wl,-no_warn_duplicate_libraries", + "$(inherited)" + ], + "OTHER_LDRFLAGS": [] + } + }, + "name": "Debug" + }, + { + "buildSettings": { + "CLANG_ENABLE_MODULES": "YES", + "CURRENT_PROJECT_VERSION": "1", + "DEFINES_MODULE": "YES", + "EXECUTABLE_NAME": "MyTransitiveLibrary", + "GENERATE_INFOPLIST_FILE": "YES", + "MARKETING_VERSION": "1.0", + "MODULEMAP_FILE_CONTENTS": "module MyTransitiveLibrary {\nheader \"MyTransitiveLibrary-Swift.h\"\nexport *\n}", + "MODULEMAP_PATH": "$(GENERATED_MODULEMAP_DIR)/MyTransitiveLibrary.modulemap", + "PACKAGE_RESOURCE_TARGET_KIND": "regular", + "PRODUCT_BUNDLE_IDENTIFIER": "MyTransitiveLibrary", + "PRODUCT_MODULE_NAME": "MyTransitiveLibrary", + "PRODUCT_NAME": "MyTransitiveLibrary", + "SDKROOT": "auto", + "SDK_VARIANT": "auto", + "SUPPORTS_TEXT_BASED_API": "NO", + "SWIFT_ENABLE_BARE_SLASH_REGEX": "NO", + "SWIFT_OBJC_INTERFACE_HEADER_DIR": "$(GENERATED_MODULEMAP_DIR)", + "SWIFT_OBJC_INTERFACE_HEADER_NAME": "MyTransitiveLibrary-Swift.h", + "SWIFT_PACKAGE_NAME": "mytransitivelibrary", + "SWIFT_VERSION": "5", + "TARGET_BUILD_DIR": "$(TARGET_BUILD_DIR)/PackageFrameworks", + "TARGET_NAME": "MyTransitiveLibrary" + }, + "guid": "PACKAGE-TARGET:MyTransitiveLibrary-29D6C569F63C2FD7-dynamic::BUILDCONFIG_1", + "impartedBuildProperties": { + "buildSettings": { + "FRAMEWORK_SEARCH_PATHS": [ + "$(BUILT_PRODUCTS_DIR)/PackageFrameworks", + "$(inherited)" + ], + "OTHER_CFLAGS": [ + "-fmodule-map-file=$(GENERATED_MODULEMAP_DIR)/MyTransitiveLibrary.modulemap", + "$(inherited)" + ], + "OTHER_LDFLAGS": [ + "-Wl,-no_warn_duplicate_libraries", + "$(inherited)" + ], + "OTHER_LDRFLAGS": [] + } + }, + "name": "Release" + } + ], + "buildPhases": [ + { + "buildFiles": [ + { + "codeSignOnCopy": "false", + "fileReference": "PACKAGE:/Users/thedderwick/Desktop/SPMTest/MyTransitiveLibrary::MAINGROUP::REF_3::REF_0", + "guid": "PACKAGE-TARGET:MyTransitiveLibrary-29D6C569F63C2FD7-dynamic::BUILDPHASE_0::0", + "platformFilters": [], + "removeHeadersOnCopy": "false" + } + ], + "guid": "PACKAGE-TARGET:MyTransitiveLibrary-29D6C569F63C2FD7-dynamic::BUILDPHASE_0", + "type": "com.apple.buildphase.sources" + }, + { + "buildFiles": [ + { + "guid": "PACKAGE-TARGET:MyTransitiveLibrary-29D6C569F63C2FD7-dynamic::BUILDPHASE_1::0", + "platformFilters": [], + "targetReference": "PACKAGE-PRODUCT:MyCommonLibrary" + }, + { + "guid": "PACKAGE-TARGET:MyTransitiveLibrary-29D6C569F63C2FD7-dynamic::BUILDPHASE_1::1", + "platformFilters": [], + "targetReference": "PACKAGE-PRODUCT:MyBinaryDependency" + }, + { + "codeSignOnCopy": "true", + "fileReference": "PACKAGE:/Users/thedderwick/Desktop/SPMTest/MyTransitiveLibrary::MAINGROUP::REF_1::REF_3", + "guid": "PACKAGE-TARGET:MyTransitiveLibrary-29D6C569F63C2FD7-dynamic::BUILDPHASE_1::2", + "platformFilters": [], + "removeHeadersOnCopy": "true" + } + ], + "guid": "PACKAGE-TARGET:MyTransitiveLibrary-29D6C569F63C2FD7-dynamic::BUILDPHASE_1", + "type": "com.apple.buildphase.frameworks" + } + ], + "buildRules": [], + "dependencies": [ + { + "guid": "PACKAGE-PRODUCT:MyCommonLibrary", + "platformFilters": [] + }, + { + "guid": "PACKAGE-PRODUCT:MyBinaryDependency", + "platformFilters": [] + } + ], + "guid": "PACKAGE-TARGET:MyTransitiveLibrary-29D6C569F63C2FD7-dynamic", + "name": "MyTransitiveLibrary", + "productReference": { + "guid": "PRODUCTREF-PACKAGE-TARGET:MyTransitiveLibrary-29D6C569F63C2FD7-dynamic", + "name": "MyTransitiveLibrary.framework", + "type": "file" + }, + "productTypeIdentifier": "com.apple.product-type.framework", + "type": "standard" +} diff --git a/TestAssets/PIFCaches/SPMTest/PIFCache/target/TARGET@v12_hash=f6b1fc5ff8b0c87e4d17a05b612118a8-json b/TestAssets/PIFCaches/SPMTest/PIFCache/target/TARGET@v12_hash=f6b1fc5ff8b0c87e4d17a05b612118a8-json new file mode 100644 index 0000000..c3a4089 --- /dev/null +++ b/TestAssets/PIFCaches/SPMTest/PIFCache/target/TARGET@v12_hash=f6b1fc5ff8b0c87e4d17a05b612118a8-json @@ -0,0 +1,150 @@ +{ + "approvedByUser": "true", + "buildConfigurations": [ + { + "buildSettings": { + "CLANG_COVERAGE_MAPPING_LINKER_ARGS": "NO", + "CLANG_ENABLE_MODULES": "YES", + "DEFINES_MODULE": "YES", + "EXECUTABLE_NAME": "MyTransitiveLibrary.o", + "GENERATE_MASTER_OBJECT_FILE": "NO", + "MACH_O_TYPE": "mh_object", + "MODULEMAP_FILE_CONTENTS": "module MyTransitiveLibrary {\nheader \"MyTransitiveLibrary-Swift.h\"\nexport *\n}", + "MODULEMAP_PATH": "$(GENERATED_MODULEMAP_DIR)/MyTransitiveLibrary.modulemap", + "PACKAGE_RESOURCE_TARGET_KIND": "regular", + "PRODUCT_BUNDLE_IDENTIFIER": "MyTransitiveLibrary", + "PRODUCT_MODULE_NAME": "MyTransitiveLibrary", + "PRODUCT_NAME": "$(TARGET_NAME)", + "SDKROOT": "auto", + "SDK_VARIANT": "auto", + "STRIP_INSTALLED_PRODUCT": "NO", + "SUPPORTS_TEXT_BASED_API": "NO", + "SWIFT_ENABLE_BARE_SLASH_REGEX": "NO", + "SWIFT_OBJC_INTERFACE_HEADER_DIR": "$(GENERATED_MODULEMAP_DIR)", + "SWIFT_OBJC_INTERFACE_HEADER_NAME": "MyTransitiveLibrary-Swift.h", + "SWIFT_PACKAGE_NAME": "mytransitivelibrary", + "SWIFT_VERSION": "5", + "TAPI_DYLIB_INSTALL_NAME": "MyTransitiveLibrary", + "TARGET_NAME": "MyTransitiveLibrary" + }, + "guid": "PACKAGE-TARGET:MyTransitiveLibrary::BUILDCONFIG_0", + "impartedBuildProperties": { + "buildSettings": { + "FRAMEWORK_SEARCH_PATHS": [ + "$(BUILT_PRODUCTS_DIR)/PackageFrameworks", + "$(inherited)" + ], + "LD_RUNPATH_SEARCH_PATHS": [ + "$(BUILT_PRODUCTS_DIR)/PackageFrameworks", + "$(inherited)" + ], + "OTHER_CFLAGS": [ + "-fmodule-map-file=$(GENERATED_MODULEMAP_DIR)/MyTransitiveLibrary.modulemap", + "$(inherited)" + ], + "OTHER_LDFLAGS": [ + "-Wl,-no_warn_duplicate_libraries", + "$(inherited)" + ], + "OTHER_LDRFLAGS": [] + } + }, + "name": "Debug" + }, + { + "buildSettings": { + "CLANG_COVERAGE_MAPPING_LINKER_ARGS": "NO", + "CLANG_ENABLE_MODULES": "YES", + "DEFINES_MODULE": "YES", + "EXECUTABLE_NAME": "MyTransitiveLibrary.o", + "GENERATE_MASTER_OBJECT_FILE": "NO", + "MACH_O_TYPE": "mh_object", + "MODULEMAP_FILE_CONTENTS": "module MyTransitiveLibrary {\nheader \"MyTransitiveLibrary-Swift.h\"\nexport *\n}", + "MODULEMAP_PATH": "$(GENERATED_MODULEMAP_DIR)/MyTransitiveLibrary.modulemap", + "PACKAGE_RESOURCE_TARGET_KIND": "regular", + "PRODUCT_BUNDLE_IDENTIFIER": "MyTransitiveLibrary", + "PRODUCT_MODULE_NAME": "MyTransitiveLibrary", + "PRODUCT_NAME": "$(TARGET_NAME)", + "SDKROOT": "auto", + "SDK_VARIANT": "auto", + "STRIP_INSTALLED_PRODUCT": "NO", + "SUPPORTS_TEXT_BASED_API": "NO", + "SWIFT_ENABLE_BARE_SLASH_REGEX": "NO", + "SWIFT_OBJC_INTERFACE_HEADER_DIR": "$(GENERATED_MODULEMAP_DIR)", + "SWIFT_OBJC_INTERFACE_HEADER_NAME": "MyTransitiveLibrary-Swift.h", + "SWIFT_PACKAGE_NAME": "mytransitivelibrary", + "SWIFT_VERSION": "5", + "TAPI_DYLIB_INSTALL_NAME": "MyTransitiveLibrary", + "TARGET_NAME": "MyTransitiveLibrary" + }, + "guid": "PACKAGE-TARGET:MyTransitiveLibrary::BUILDCONFIG_1", + "impartedBuildProperties": { + "buildSettings": { + "FRAMEWORK_SEARCH_PATHS": [ + "$(BUILT_PRODUCTS_DIR)/PackageFrameworks", + "$(inherited)" + ], + "OTHER_CFLAGS": [ + "-fmodule-map-file=$(GENERATED_MODULEMAP_DIR)/MyTransitiveLibrary.modulemap", + "$(inherited)" + ], + "OTHER_LDFLAGS": [ + "-Wl,-no_warn_duplicate_libraries", + "$(inherited)" + ], + "OTHER_LDRFLAGS": [] + } + }, + "name": "Release" + } + ], + "buildPhases": [ + { + "buildFiles": [ + { + "codeSignOnCopy": "false", + "fileReference": "PACKAGE:/Users/thedderwick/Desktop/SPMTest/MyTransitiveLibrary::MAINGROUP::REF_2::REF_0", + "guid": "PACKAGE-TARGET:MyTransitiveLibrary::BUILDPHASE_0::0", + "platformFilters": [], + "removeHeadersOnCopy": "false" + } + ], + "guid": "PACKAGE-TARGET:MyTransitiveLibrary::BUILDPHASE_0", + "type": "com.apple.buildphase.sources" + }, + { + "buildFiles": [ + { + "codeSignOnCopy": "false", + "fileReference": "PACKAGE:/Users/thedderwick/Desktop/SPMTest/MyTransitiveLibrary::MAINGROUP::REF_1::REF_2", + "guid": "PACKAGE-TARGET:MyTransitiveLibrary::BUILDPHASE_1::0", + "platformFilters": [], + "removeHeadersOnCopy": "false" + } + ], + "guid": "PACKAGE-TARGET:MyTransitiveLibrary::BUILDPHASE_1", + "type": "com.apple.buildphase.resources" + } + ], + "buildRules": [], + "dependencies": [ + { + "guid": "PACKAGE-PRODUCT:MyCommonLibrary", + "platformFilters": [] + }, + { + "guid": "PACKAGE-PRODUCT:MyBinaryDependency", + "platformFilters": [] + } + ], + "dynamicTargetVariantGuid": "PACKAGE-TARGET:MyTransitiveLibrary-29D6C569F63C2FD7-dynamic", + "guid": "PACKAGE-TARGET:MyTransitiveLibrary", + "name": "MyTransitiveLibrary", + "productReference": { + "guid": "PRODUCTREF-PACKAGE-TARGET:MyTransitiveLibrary", + "name": "MyTransitiveLibrary.o", + "type": "file" + }, + "productTypeIdentifier": "com.apple.product-type.objfile", + "type": "standard" +} diff --git a/TestAssets/PIFCaches/SPMTest/PIFCache/target/TARGET@v12_hash=fb7de2da6bb6f870c73ba0c83581f638-json b/TestAssets/PIFCaches/SPMTest/PIFCache/target/TARGET@v12_hash=fb7de2da6bb6f870c73ba0c83581f638-json new file mode 100644 index 0000000..0f6cb43 --- /dev/null +++ b/TestAssets/PIFCaches/SPMTest/PIFCache/target/TARGET@v12_hash=fb7de2da6bb6f870c73ba0c83581f638-json @@ -0,0 +1,75 @@ +{ + "approvedByUser": "true", + "buildConfigurations": [ + { + "buildSettings": { + "SDKROOT": "auto", + "SDK_VARIANT": "auto", + "USES_SWIFTPM_UNSAFE_FLAGS": "NO" + }, + "guid": "PACKAGE-PRODUCT:MyTransitiveLibrary::BUILDCONFIG_0", + "impartedBuildProperties": { + "buildSettings": {} + }, + "name": "Debug" + }, + { + "buildSettings": { + "SDKROOT": "auto", + "SDK_VARIANT": "auto", + "USES_SWIFTPM_UNSAFE_FLAGS": "NO" + }, + "guid": "PACKAGE-PRODUCT:MyTransitiveLibrary::BUILDCONFIG_1", + "impartedBuildProperties": { + "buildSettings": {} + }, + "name": "Release" + } + ], + "dependencies": [ + { + "guid": "PACKAGE-TARGET:MyTransitiveLibrary", + "platformFilters": [] + }, + { + "guid": "PACKAGE-PRODUCT:MyCommonLibrary", + "platformFilters": [] + }, + { + "guid": "PACKAGE-PRODUCT:MyBinaryDependency", + "platformFilters": [] + } + ], + "dynamicTargetVariantGuid": "PACKAGE-PRODUCT:MyTransitiveLibrary-29D6C569F63C2FD7-dynamic", + "frameworksBuildPhase": { + "buildFiles": [ + { + "guid": "PACKAGE-PRODUCT:MyTransitiveLibrary::BUILDPHASE_0::0", + "platformFilters": [], + "targetReference": "PACKAGE-TARGET:MyTransitiveLibrary" + }, + { + "guid": "PACKAGE-PRODUCT:MyTransitiveLibrary::BUILDPHASE_0::1", + "platformFilters": [], + "targetReference": "PACKAGE-PRODUCT:MyCommonLibrary" + }, + { + "guid": "PACKAGE-PRODUCT:MyTransitiveLibrary::BUILDPHASE_0::2", + "platformFilters": [], + "targetReference": "PACKAGE-PRODUCT:MyBinaryDependency" + }, + { + "codeSignOnCopy": "true", + "fileReference": "PACKAGE:/Users/thedderwick/Desktop/SPMTest/MyTransitiveLibrary::MAINGROUP::REF_1::REF_0", + "guid": "PACKAGE-PRODUCT:MyTransitiveLibrary::BUILDPHASE_0::3", + "platformFilters": [], + "removeHeadersOnCopy": "true" + } + ], + "guid": "PACKAGE-PRODUCT:MyTransitiveLibrary::BUILDPHASE_0", + "type": "com.apple.buildphase.frameworks" + }, + "guid": "PACKAGE-PRODUCT:MyTransitiveLibrary", + "name": "MyTransitiveLibrary", + "type": "packageProduct" +} diff --git a/TestAssets/PIFCaches/SPMTest/PIFCache/workspace/WORKSPACE@v11_hash=e6e1ff1ce1d8441dff7731e4f843ec43_subobjects=f62a50b6ced3d59774fc6ba5a392ffaa-json b/TestAssets/PIFCaches/SPMTest/PIFCache/workspace/WORKSPACE@v11_hash=e6e1ff1ce1d8441dff7731e4f843ec43_subobjects=f62a50b6ced3d59774fc6ba5a392ffaa-json new file mode 100644 index 0000000..1a2d122 --- /dev/null +++ b/TestAssets/PIFCaches/SPMTest/PIFCache/workspace/WORKSPACE@v11_hash=e6e1ff1ce1d8441dff7731e4f843ec43_subobjects=f62a50b6ced3d59774fc6ba5a392ffaa-json @@ -0,0 +1,12 @@ +{ + "guid": "1548de4dda862e04fcc1cb773900f3bf", + "name": "SPMTest", + "path": "/Users/thedderwick/Desktop/SPMTest/SPMTest.xcodeproj/project.xcworkspace", + "projects": [ + "PROJECT@v11_mod=1716537774.576404_hash=0f958f39499d9f0b6b454948d2be549bplugins=1OJSG6M1FOV3XYQCBH7Z29RZ0FPR9XDE1", + "PACKAGE@v12_hash=28350dce3dbf6c3324256d2ef84c721e", + "PACKAGE@v12_hash=d061ee81d8927d5b63443e777e83938a", + "PACKAGE@v12_hash=217e2718d5748fe803db10bf7703f87e", + "PACKAGE@v12_hash=60e18114455190230d62ad086fd6a1d5" + ] +} diff --git a/TestAssets/PIFCaches/SPMTest/SPMTest.xcodeproj/project.pbxproj b/TestAssets/PIFCaches/SPMTest/SPMTest.xcodeproj/project.pbxproj new file mode 100644 index 0000000..3044785 --- /dev/null +++ b/TestAssets/PIFCaches/SPMTest/SPMTest.xcodeproj/project.pbxproj @@ -0,0 +1,387 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 60; + objects = { + +/* Begin PBXBuildFile section */ + CEE341292C007A7E00BBA9C3 /* SPMTestApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = CEE341282C007A7E00BBA9C3 /* SPMTestApp.swift */; }; + CEE3412B2C007A7E00BBA9C3 /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = CEE3412A2C007A7E00BBA9C3 /* ContentView.swift */; }; + CEE3412D2C007A7F00BBA9C3 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = CEE3412C2C007A7F00BBA9C3 /* Assets.xcassets */; }; + CEE341302C007A7F00BBA9C3 /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = CEE3412F2C007A7F00BBA9C3 /* Preview Assets.xcassets */; }; + CEE341382C007CA900BBA9C3 /* MyLibrary in Frameworks */ = {isa = PBXBuildFile; productRef = CEE341372C007CA900BBA9C3 /* MyLibrary */; }; + CEE3413B2C00812D00BBA9C3 /* MyCommonLibrary in Frameworks */ = {isa = PBXBuildFile; productRef = CEE3413A2C00812D00BBA9C3 /* MyCommonLibrary */; }; +/* End PBXBuildFile section */ + +/* Begin PBXFileReference section */ + CEE341252C007A7E00BBA9C3 /* SPMTest.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = SPMTest.app; sourceTree = BUILT_PRODUCTS_DIR; }; + CEE341282C007A7E00BBA9C3 /* SPMTestApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SPMTestApp.swift; sourceTree = ""; }; + CEE3412A2C007A7E00BBA9C3 /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = ""; }; + CEE3412C2C007A7F00BBA9C3 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; + CEE3412F2C007A7F00BBA9C3 /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + CEE341222C007A7E00BBA9C3 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + CEE341382C007CA900BBA9C3 /* MyLibrary in Frameworks */, + CEE3413B2C00812D00BBA9C3 /* MyCommonLibrary in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + CEE3411C2C007A7E00BBA9C3 = { + isa = PBXGroup; + children = ( + CEE341272C007A7E00BBA9C3 /* SPMTest */, + CEE341262C007A7E00BBA9C3 /* Products */, + ); + sourceTree = ""; + }; + CEE341262C007A7E00BBA9C3 /* Products */ = { + isa = PBXGroup; + children = ( + CEE341252C007A7E00BBA9C3 /* SPMTest.app */, + ); + name = Products; + sourceTree = ""; + }; + CEE341272C007A7E00BBA9C3 /* SPMTest */ = { + isa = PBXGroup; + children = ( + CEE341282C007A7E00BBA9C3 /* SPMTestApp.swift */, + CEE3412A2C007A7E00BBA9C3 /* ContentView.swift */, + CEE3412C2C007A7F00BBA9C3 /* Assets.xcassets */, + CEE3412E2C007A7F00BBA9C3 /* Preview Content */, + ); + path = SPMTest; + sourceTree = ""; + }; + CEE3412E2C007A7F00BBA9C3 /* Preview Content */ = { + isa = PBXGroup; + children = ( + CEE3412F2C007A7F00BBA9C3 /* Preview Assets.xcassets */, + ); + path = "Preview Content"; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + CEE341242C007A7E00BBA9C3 /* SPMTest */ = { + isa = PBXNativeTarget; + buildConfigurationList = CEE341332C007A7F00BBA9C3 /* Build configuration list for PBXNativeTarget "SPMTest" */; + buildPhases = ( + CEE341212C007A7E00BBA9C3 /* Sources */, + CEE341222C007A7E00BBA9C3 /* Frameworks */, + CEE341232C007A7E00BBA9C3 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = SPMTest; + packageProductDependencies = ( + CEE341372C007CA900BBA9C3 /* MyLibrary */, + CEE3413A2C00812D00BBA9C3 /* MyCommonLibrary */, + ); + productName = SPMTest; + productReference = CEE341252C007A7E00BBA9C3 /* SPMTest.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + CEE3411D2C007A7E00BBA9C3 /* Project object */ = { + isa = PBXProject; + attributes = { + BuildIndependentTargetsInParallel = 1; + LastSwiftUpdateCheck = 1530; + LastUpgradeCheck = 1530; + TargetAttributes = { + CEE341242C007A7E00BBA9C3 = { + CreatedOnToolsVersion = 15.3; + }; + }; + }; + buildConfigurationList = CEE341202C007A7E00BBA9C3 /* Build configuration list for PBXProject "SPMTest" */; + compatibilityVersion = "Xcode 14.0"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = CEE3411C2C007A7E00BBA9C3; + packageReferences = ( + CEE341362C007CA900BBA9C3 /* XCLocalSwiftPackageReference "MyLibrary" */, + CEE341392C00812D00BBA9C3 /* XCLocalSwiftPackageReference "MyCommonLibrary" */, + ); + productRefGroup = CEE341262C007A7E00BBA9C3 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + CEE341242C007A7E00BBA9C3 /* SPMTest */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + CEE341232C007A7E00BBA9C3 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + CEE341302C007A7F00BBA9C3 /* Preview Assets.xcassets in Resources */, + CEE3412D2C007A7F00BBA9C3 /* Assets.xcassets in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + CEE341212C007A7E00BBA9C3 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + CEE3412B2C007A7E00BBA9C3 /* ContentView.swift in Sources */, + CEE341292C007A7E00BBA9C3 /* SPMTestApp.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin XCBuildConfiguration section */ + CEE341312C007A7F00BBA9C3 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + ENABLE_USER_SCRIPT_SANDBOXING = YES; + GCC_C_LANGUAGE_STANDARD = gnu17; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 17.4; + LOCALIZATION_PREFERS_STRING_CATALOGS = YES; + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; + MTL_FAST_MATH = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = iphoneos; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG $(inherited)"; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + }; + name = Debug; + }; + CEE341322C007A7F00BBA9C3 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_USER_SCRIPT_SANDBOXING = YES; + GCC_C_LANGUAGE_STANDARD = gnu17; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 17.4; + LOCALIZATION_PREFERS_STRING_CATALOGS = YES; + MTL_ENABLE_DEBUG_INFO = NO; + MTL_FAST_MATH = YES; + SDKROOT = iphoneos; + SWIFT_COMPILATION_MODE = wholemodule; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + CEE341342C007A7F00BBA9C3 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + CODE_SIGN_IDENTITY = ""; + CODE_SIGN_STYLE = Manual; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_ASSET_PATHS = "\"SPMTest/Preview Content\""; + DEVELOPMENT_TEAM = ""; + ENABLE_PREVIEWS = YES; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES; + INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; + INFOPLIST_KEY_UILaunchScreen_Generation = YES; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = com.veracode.SPMTest; + PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + CEE341352C007A7F00BBA9C3 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + CODE_SIGN_IDENTITY = ""; + CODE_SIGN_STYLE = Manual; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_ASSET_PATHS = "\"SPMTest/Preview Content\""; + DEVELOPMENT_TEAM = ""; + ENABLE_PREVIEWS = YES; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES; + INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; + INFOPLIST_KEY_UILaunchScreen_Generation = YES; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = com.veracode.SPMTest; + PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + CEE341202C007A7E00BBA9C3 /* Build configuration list for PBXProject "SPMTest" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + CEE341312C007A7F00BBA9C3 /* Debug */, + CEE341322C007A7F00BBA9C3 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + CEE341332C007A7F00BBA9C3 /* Build configuration list for PBXNativeTarget "SPMTest" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + CEE341342C007A7F00BBA9C3 /* Debug */, + CEE341352C007A7F00BBA9C3 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + +/* Begin XCLocalSwiftPackageReference section */ + CEE341362C007CA900BBA9C3 /* XCLocalSwiftPackageReference "MyLibrary" */ = { + isa = XCLocalSwiftPackageReference; + relativePath = MyLibrary; + }; + CEE341392C00812D00BBA9C3 /* XCLocalSwiftPackageReference "MyCommonLibrary" */ = { + isa = XCLocalSwiftPackageReference; + relativePath = MyCommonLibrary; + }; +/* End XCLocalSwiftPackageReference section */ + +/* Begin XCSwiftPackageProductDependency section */ + CEE341372C007CA900BBA9C3 /* MyLibrary */ = { + isa = XCSwiftPackageProductDependency; + productName = MyLibrary; + }; + CEE3413A2C00812D00BBA9C3 /* MyCommonLibrary */ = { + isa = XCSwiftPackageProductDependency; + productName = MyCommonLibrary; + }; +/* End XCSwiftPackageProductDependency section */ + }; + rootObject = CEE3411D2C007A7E00BBA9C3 /* Project object */; +} diff --git a/TestAssets/PIFCaches/SPMTest/SPMTest.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/TestAssets/PIFCaches/SPMTest/SPMTest.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000..919434a --- /dev/null +++ b/TestAssets/PIFCaches/SPMTest/SPMTest.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/TestAssets/PIFCaches/SPMTest/SPMTest.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/TestAssets/PIFCaches/SPMTest/SPMTest.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 0000000..18d9810 --- /dev/null +++ b/TestAssets/PIFCaches/SPMTest/SPMTest.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/TestAssets/PIFCaches/SPMTest/SPMTest/Assets.xcassets/AccentColor.colorset/Contents.json b/TestAssets/PIFCaches/SPMTest/SPMTest/Assets.xcassets/AccentColor.colorset/Contents.json new file mode 100644 index 0000000..eb87897 --- /dev/null +++ b/TestAssets/PIFCaches/SPMTest/SPMTest/Assets.xcassets/AccentColor.colorset/Contents.json @@ -0,0 +1,11 @@ +{ + "colors" : [ + { + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/TestAssets/PIFCaches/SPMTest/SPMTest/Assets.xcassets/AppIcon.appiconset/Contents.json b/TestAssets/PIFCaches/SPMTest/SPMTest/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 0000000..13613e3 --- /dev/null +++ b/TestAssets/PIFCaches/SPMTest/SPMTest/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,13 @@ +{ + "images" : [ + { + "idiom" : "universal", + "platform" : "ios", + "size" : "1024x1024" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/TestAssets/PIFCaches/SPMTest/SPMTest/Assets.xcassets/Contents.json b/TestAssets/PIFCaches/SPMTest/SPMTest/Assets.xcassets/Contents.json new file mode 100644 index 0000000..73c0059 --- /dev/null +++ b/TestAssets/PIFCaches/SPMTest/SPMTest/Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/TestAssets/PIFCaches/SPMTest/SPMTest/ContentView.swift b/TestAssets/PIFCaches/SPMTest/SPMTest/ContentView.swift new file mode 100644 index 0000000..2e83bfa --- /dev/null +++ b/TestAssets/PIFCaches/SPMTest/SPMTest/ContentView.swift @@ -0,0 +1,26 @@ +// +// ContentView.swift +// SPMTest +// +// Created by Thomas Hedderwick on 24/05/2024. +// + +import SwiftUI +import MyLibrary + +struct ContentView: View { + var body: some View { + VStack { + Image(systemName: "globe") + .imageScale(.large) + .foregroundStyle(.tint) + Text("VERSION: \(MyLibrary.version)") + Text("view: \(MyLibrary.view)") + } + .padding() + } +} + +#Preview { + ContentView() +} diff --git a/TestAssets/PIFCaches/SPMTest/SPMTest/Preview Content/Preview Assets.xcassets/Contents.json b/TestAssets/PIFCaches/SPMTest/SPMTest/Preview Content/Preview Assets.xcassets/Contents.json new file mode 100644 index 0000000..73c0059 --- /dev/null +++ b/TestAssets/PIFCaches/SPMTest/SPMTest/Preview Content/Preview Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/TestAssets/PIFCaches/SPMTest/SPMTest/SPMTestApp.swift b/TestAssets/PIFCaches/SPMTest/SPMTest/SPMTestApp.swift new file mode 100644 index 0000000..716f46c --- /dev/null +++ b/TestAssets/PIFCaches/SPMTest/SPMTest/SPMTestApp.swift @@ -0,0 +1,17 @@ +// +// SPMTestApp.swift +// SPMTest +// +// Created by Thomas Hedderwick on 24/05/2024. +// + +import SwiftUI + +@main +struct SPMTestApp: App { + var body: some Scene { + WindowGroup { + ContentView() + } + } +} diff --git a/Tests/GenIRTests/PIFCacheTests.swift b/Tests/GenIRTests/PIFCacheTests.swift new file mode 100644 index 0000000..47bdcea --- /dev/null +++ b/Tests/GenIRTests/PIFCacheTests.swift @@ -0,0 +1,35 @@ +import XCTest +@testable import gen_ir +import PIFSupport + +final class PIFCacheTests: XCTestCase { + let testPath: URL = { + TestContext.testAssetPath + .appendingPathComponent("PIFCaches") + .appendingPathComponent("SPMTest") + .appendingPathComponent("SPMTest.xcodeproj") + }() + let scheme = "SPMTest" + var cachePath: URL { + testPath + .deletingLastPathComponent() + .appendingPathComponent("PIFCache") + } + + func testSPMTestChain() throws { + let context = TestContext() + try context.build(test: testPath, scheme: scheme) + let graph = context.graph + + let appTarget = try XCTUnwrap(context.targets.first(where: { $0.productName == "SPMTest.app" })) + let node = try XCTUnwrap(graph.findNode(for: appTarget)) + + XCTAssertEqual(node.edges.count, 2) + let chain = graph.chain(for: appTarget) + let nameSet = Set(chain.map { $0.value.name }) + + let expectedNameSet = Set(["SPMTest", "MyLibrary", "MyCommonLibrary", "MyTransitiveLibrary"]) + let nameDifference = nameSet.symmetricDifference(expectedNameSet) + XCTAssertTrue(nameDifference.isEmpty, "Difference found in name set (\(nameSet)) and expected name set: \(expectedNameSet) - difference: \(nameDifference)") + } +} From 07c0b9675f928213bae4579a093a17ebad90724a Mon Sep 17 00:00:00 2001 From: NinjaLikesCheez Date: Mon, 27 May 2024 14:40:07 +0200 Subject: [PATCH 11/21] Clean up unused code, unused CLI options, etc --- .swiftlint.yml | 17 +++- .../Extensions/FileManager+Extension.swift | 21 ----- .../GenIR/Extensions/String+Extension.swift | 10 --- Sources/GenIR/GenIR.swift | 84 ++++++------------- Sources/GenIR/OutputPostprocessor.swift | 14 +--- Sources/GenIR/PIFCache.swift | 57 +++++-------- .../OutputPostprocessorFileMoverTests.swift | 1 - Tests/GenIRTests/UmbrellaTests.swift | 2 - Tests/GenIRTests/WorkspaceTests.swift | 1 - 9 files changed, 64 insertions(+), 143 deletions(-) diff --git a/.swiftlint.yml b/.swiftlint.yml index 993bb96..d40d541 100644 --- a/.swiftlint.yml +++ b/.swiftlint.yml @@ -1,10 +1,10 @@ excluded: - - .build/ - .swiftpm/ - .vscode/ - # https://github.com/realm/SwiftLint/issues/2329 doesn't support recursive globs yet - - GenIRLogging/.build/ + - "**/.build/*" + - .build/ - TestAssets/ + - PIF/.build/ disabled_rules: - todo @@ -13,3 +13,14 @@ disabled_rules: line_length: warning: 200 ignores_comments: true + +missing_docs: + warning: + - private + - open + - fileprivate + - public + - internal + +opt_in_rules: + - missing_docs diff --git a/Sources/GenIR/Extensions/FileManager+Extension.swift b/Sources/GenIR/Extensions/FileManager+Extension.swift index 76a544c..b85b6b4 100644 --- a/Sources/GenIR/Extensions/FileManager+Extension.swift +++ b/Sources/GenIR/Extensions/FileManager+Extension.swift @@ -102,27 +102,6 @@ extension FileManager { try moveItem(at: source, to: destination) } - /// Copies an item, merging with the existing path. Replacement of existing paths is performed if specified. - /// - Parameters: - /// - source: the item to copy - /// - destination: the destination of the copy - /// - replacing: should existing items be replaced? - func copyItemMerging(at source: URL, to destination: URL, replacing: Bool = false) throws { - let sourceFiles = try contentsOfDirectory(at: source, includingPropertiesForKeys: nil) - - for sourceFile in sourceFiles { - let path = destination.appendingPathComponent(sourceFile.lastPathComponent) - - if replacing && fileExists(atPath: path.filePath) { - try removeItem(at: path) - } - - let destinationFile = uniqueFilename(directory: destination, filename: sourceFile.lastPathComponent) - - try copyItem(at: sourceFile, to: destinationFile) - } - } - /// Generates a unique filename for a file at the given directory. This attempts to emulates finders style of appending a 'version' number at the end of the filename /// - Parameters: /// - directory: the directory the file would exist in diff --git a/Sources/GenIR/Extensions/String+Extension.swift b/Sources/GenIR/Extensions/String+Extension.swift index ca6e499..4f6df5b 100644 --- a/Sources/GenIR/Extensions/String+Extension.swift +++ b/Sources/GenIR/Extensions/String+Extension.swift @@ -61,16 +61,6 @@ extension String { return results } - - func deletingPathExtension() -> String { - if let index = self.firstIndexWithEscapes(of: ".") { - var newSelf = self - newSelf.removeSubrange(index.. \ log.txt - $ \(programName) log.txt output_folder/ --project-path MyProject.xcodeproj + $ \(programName) log.txt x.xcarchive/ Example with pipe: $ xcodebuild clean && xcodebuild build -project MyProject.xcodeproj -configuration Debug -scheme MyScheme 2>&1 \ - | \(programName) - output_folder/ --project-path MyProject.xcodeproj + | \(programName) - x.xcarchive/ """, version: "v\(Versions.version)" ) @@ -45,8 +45,8 @@ struct IREmitterCommand: ParsableCommand { var xcarchivePath: URL /// Path to xcodeproj or xcworkspace file - @Option(help: "Path to your Xcode Project or Workspace file") - var projectPath: URL! + @Option(help: "DEPRECATED: This Option is deprecated and will go away in a future version.") + var projectPath: URL? /// Enables enhanced debug logging @Flag(help: "Enables debug level logging") @@ -63,17 +63,13 @@ struct IREmitterCommand: ParsableCommand { var dumpDependencyGraph = false mutating func validate() throws { + // This will run before run() so set this here if debug { logger.logLevel = .debug } - // Version 0.2.x and below didn't require a project. Attempt to default this value if we can - if projectPath == nil { - projectPath = try findProjectPath() - } - - if !FileManager.default.fileExists(atPath: projectPath.filePath) { - throw ValidationError("Project doesn't exist at path: \(projectPath.filePath)") + if projectPath != nil { + logger.warning("--project-path has been deprecated and will go away in a future version. Please remove it from your invocation.") } // Version 0.2.x and below allowed the output folder to be any arbitrary folder. @@ -94,7 +90,6 @@ struct IREmitterCommand: ParsableCommand { mutating func run() throws { try run( - project: projectPath, log: logPath, archive: xcarchivePath, level: logger.logLevel, @@ -104,41 +99,26 @@ struct IREmitterCommand: ParsableCommand { } mutating func run( - project: URL, log: String, archive: URL, level: Logger.Level, dryRun: Bool, dumpDependencyGraph: Bool ) throws { + logger.logLevel = level let output = archive.appendingPathComponent("IR") - // let project = try ProjectParser(path: project, logLevel: level) - // var targets = Targets(for: project) let log = try logParser(for: log) try log.parse() // Find and parse the PIF cache let pifCache = try PIFCache(buildCache: log.buildCachePath) - - let buildCacheManipulator = try BuildCacheManipulator( - buildCachePath: log.buildCachePath, - buildSettings: log.settings, - archive: archive, - dryRun: dryRun - ) - let targets = Target.targets(from: pifCache.targets, with: log.targetCommands) - let runner = CompilerCommandRunner( - output: output, - buildCacheManipulator: buildCacheManipulator, - dryRun: dryRun + let builder = DependencyGraphBuilder( + provider: .init(targets: targets, cache: pifCache), + values: targets ) - try runner.run(targets: targets) - - let provider = PIFDependencyProvider(targets: targets, cache: pifCache) - let builder = DependencyGraphBuilder(provider: provider, values: targets) let graph = builder.graph if dumpDependencyGraph { @@ -153,6 +133,20 @@ struct IREmitterCommand: ParsableCommand { } } + let buildCacheManipulator = try BuildCacheManipulator( + buildCachePath: log.buildCachePath, + buildSettings: log.settings, + archive: archive, + dryRun: dryRun + ) + + let runner = CompilerCommandRunner( + output: output, + buildCacheManipulator: buildCacheManipulator, + dryRun: dryRun + ) + try runner.run(targets: targets) + let postprocessor = try OutputPostprocessor( archive: archive, output: output, @@ -206,34 +200,6 @@ struct IREmitterCommand: ParsableCommand { } } -extension IREmitterCommand { - /// Attempt to automatically determine the Xcode workspace or project path - /// - Returns: the path to the first xcworkspace or xcodeproj found in the current directory - private func findProjectPath() throws -> URL { - let cwd = FileManager.default.currentDirectoryPath.fileURL - // First, xcworkspace, then xcodeproj - let xcworkspace = try FileManager.default.directories(at: cwd, recursive: false) - .filter { $0.pathExtension == "xcworkspace" } - - if xcworkspace.count == 1 { - return xcworkspace[0] - } - - let xcodeproj = try FileManager.default.directories(at: cwd, recursive: false) - .filter { $0.pathExtension == "xcodeproj" } - - if xcodeproj.count == 1 { - return xcodeproj[0] - } - - throw ValidationError( - """ - Couldn't automatically determine path to xcodeproj or xcworkspace. Please use --project-path to provide it. - """ - ) - } -} - extension URL: ExpressibleByArgument { public init?(argument: String) { self = argument.fileURL.absoluteURL diff --git a/Sources/GenIR/OutputPostprocessor.swift b/Sources/GenIR/OutputPostprocessor.swift index acba0ac..964ea02 100644 --- a/Sources/GenIR/OutputPostprocessor.swift +++ b/Sources/GenIR/OutputPostprocessor.swift @@ -14,9 +14,6 @@ private typealias SizeAndCreation = (Int, Date) /// The `OutputPostprocessor` attempts to remedy this by using the `ProjectParser` to detect dependencies between products AND /// parsing the `xcarchive` to determine if something was statically linked, and if so, copies the IR for that product into the linker's IR folder. class OutputPostprocessor { - /// The to the output IR folders that will be processed - let output: URL - /// The archive path, this should be the parent path of `output` let archive: URL @@ -32,7 +29,6 @@ class OutputPostprocessor { private let manager: FileManager = .default init(archive: URL, output: URL, graph: DependencyGraph, targets: [Target]) throws { - self.output = output self.archive = archive self.graph = graph self.targets = targets @@ -65,19 +61,15 @@ class OutputPostprocessor { /// - Parameter targets: the targets to operate on func process() throws { // TODO: remove 'static' deps so we don't duplicate them in the submission? - _ = try targetsToPaths - .map { try process(target: $0.key, at: $0.value) } + _ = try targets + .map { try process(target: $0) } } /// Processes an individual target /// - Parameters: /// - target: the target to process - /// - path: the output path /// - Returns: - private func process( - target: Target, - at path: URL - ) throws -> Set { + private func process(target: Target) throws -> Set { // TODO: we need better handling of swift package products and targets in the dependency graph or we fail to move dependencies here let chain = graph.chain(for: target) diff --git a/Sources/GenIR/PIFCache.swift b/Sources/GenIR/PIFCache.swift index e24f0e2..449b4e5 100644 --- a/Sources/GenIR/PIFCache.swift +++ b/Sources/GenIR/PIFCache.swift @@ -6,6 +6,16 @@ class PIFCache { private let pifCachePath: URL private let workspace: PIF.Workspace + var projects: [PIF.Project] { + workspace.projects + } + + var targets: [PIF.BaseTarget] { + workspace + .projects + .flatMap { $0.targets } + } + enum Error: Swift.Error { case nonexistentCache(String) case pifError(Swift.Error) @@ -49,31 +59,6 @@ class PIFCache { } } - var projects: [PIF.Project] { - workspace.projects - } - - var targets: [PIF.BaseTarget] { - workspace - .projects - .flatMap { $0.targets } - } - - private lazy var namesToTargets: [String: PIF.BaseTarget] = { - targets - .reduce(into: [String: PIF.BaseTarget]()) { partial, target in - partial[target.name] = target - } - }() - - private lazy var productNamesToTargets: [String: PIF.BaseTarget] = { - targets - .compactMap { $0 as? PIF.Target } - .reduce(into: [String: PIF.BaseTarget]()) { partial, target in - partial[target.productName] = target - } - }() - private func fileReferences(for project: PIF.Project) -> [PIF.FileReference] { func resolveChildren(starting children: [PIF.Reference], result: inout [PIF.FileReference]) { for child in children { @@ -147,29 +132,31 @@ struct PIFDependencyProvider: DependencyProviding { let productName = String(packageGUID.dropFirst(productToken.count)) - // TODO: should this also use the framework build phase to determine a dependency? Currently not needed because Gen IR doesn't care about prebuilt frameworks - // but for the completeness of the graph this could be a nice to have... - let dependencies = product + let productTargetDependencies = product .baseTarget .dependencies .filter { $0.targetGUID.starts(with: targetToken) } - let packageTargetDependencies = dependencies + let productUnderlyingTargets = productTargetDependencies .filter { $0.targetGUID.dropFirst(targetToken.count) == productName } - if packageTargetDependencies.isEmpty && !dependencies.isEmpty { + if productUnderlyingTargets.isEmpty && !productTargetDependencies.isEmpty { // We likely have a stub target here (i.e. a precompiled framework) // see https://github.com/apple/swift-package-manager/issues/6069 for more - logger.debug("Resolving Swift Package (\(productName) - \(packageGUID)) resulted in no targets. Possible stub target in: \(dependencies)") + logger.debug("Resolving Swift Package (\(productName) - \(packageGUID)) resulted in no targets. Possible stub target in: \(productTargetDependencies)") return nil - } else if packageTargetDependencies.isEmpty && dependencies.isEmpty { - logger.debug("Resolving Swift Package (\(productName) - \(packageGUID)) resulted in no targets.") + } else if productUnderlyingTargets.isEmpty && productTargetDependencies.isEmpty { + logger.debug("Resolving Swift Package (\(productName) - \(packageGUID)) resulted in no targets. Likely a prebuilt dependency") return nil } - precondition(packageTargetDependencies.count == 1, "Expecting one matching package target - found \(packageTargetDependencies.count): \(packageTargetDependencies). Returning first match") + guard productTargetDependencies.count == 1, let target = productTargetDependencies.first else { + logger.debug("Expecting one matching package target - found \(productTargetDependencies.count): \(productTargetDependencies). Returning first match if it exists") + return productTargetDependencies.first?.targetGUID + } - return packageTargetDependencies.first?.targetGUID ?? packageGUID + logger.debug("\(packageGUID) resolves to \(target.targetGUID)") + return target.targetGUID } func dependencies(for value: Target) -> [Target] { diff --git a/Tests/GenIRTests/OutputPostprocessorFileMoverTests.swift b/Tests/GenIRTests/OutputPostprocessorFileMoverTests.swift index acf034d..00497e3 100644 --- a/Tests/GenIRTests/OutputPostprocessorFileMoverTests.swift +++ b/Tests/GenIRTests/OutputPostprocessorFileMoverTests.swift @@ -15,7 +15,6 @@ final class OutputPostprocessorFileMoverTests: XCTestCase { var runner = IREmitterCommand() try runner.run( - project: testPath, log: context.buildLog.filePath, archive: context.archive, level: .debug, diff --git a/Tests/GenIRTests/UmbrellaTests.swift b/Tests/GenIRTests/UmbrellaTests.swift index eba625a..cd9f969 100644 --- a/Tests/GenIRTests/UmbrellaTests.swift +++ b/Tests/GenIRTests/UmbrellaTests.swift @@ -40,7 +40,6 @@ final class UmbrellaTests: XCTestCase { var genIR = gen_ir.IREmitterCommand() try genIR.run( - project: testPath, log: context.buildLog.filePath, archive: context.archive, level: .debug, @@ -70,7 +69,6 @@ final class UmbrellaTests: XCTestCase { var genIR = gen_ir.IREmitterCommand() try genIR.run( - project: testPath, log: context.buildLog.filePath, archive: context.archive, level: .debug, diff --git a/Tests/GenIRTests/WorkspaceTests.swift b/Tests/GenIRTests/WorkspaceTests.swift index a7844fb..b250b92 100644 --- a/Tests/GenIRTests/WorkspaceTests.swift +++ b/Tests/GenIRTests/WorkspaceTests.swift @@ -55,7 +55,6 @@ final class WorkspaceTests: XCTestCase { var genIR = gen_ir.IREmitterCommand() try genIR.run( - project: testPath, log: context.buildLog.filePath, archive: context.archive, level: .debug, From 8c687f003a535a0ba3b0d1d9e7085854c8410ce4 Mon Sep 17 00:00:00 2001 From: NinjaLikesCheez Date: Mon, 27 May 2024 14:40:47 +0200 Subject: [PATCH 12/21] Add support for CMake builds These have different PIF cache locations in the build cache --- Sources/GenIR/BuildCacheManipulator.swift | 1 + .../GenIR/Extensions/Process+Extension.swift | 13 +++- Sources/GenIR/PIFCache.swift | 25 +++----- Sources/GenIR/XcodeLogParser.swift | 26 +++++--- TestAssets/CMakeDiscoveryTest/CMakeLists.txt | 60 +++++++++++++++++++ .../CMakeDiscoveryTest/Source/App.swift | 10 ++++ Tests/GenIRTests/CMakeDiscoveryTests.swift | 54 +++++++++++++++++ Tests/GenIRTests/DependencyGraphTests.swift | 10 ++-- 8 files changed, 167 insertions(+), 32 deletions(-) create mode 100644 TestAssets/CMakeDiscoveryTest/CMakeLists.txt create mode 100644 TestAssets/CMakeDiscoveryTest/Source/App.swift create mode 100644 Tests/GenIRTests/CMakeDiscoveryTests.swift diff --git a/Sources/GenIR/BuildCacheManipulator.swift b/Sources/GenIR/BuildCacheManipulator.swift index b9a2e7a..43501db 100644 --- a/Sources/GenIR/BuildCacheManipulator.swift +++ b/Sources/GenIR/BuildCacheManipulator.swift @@ -38,6 +38,7 @@ struct BuildCacheManipulator { func manipulate() throws { if shouldDeploySkipInstallHack { let intermediatesPath = buildCachePath + .appendingPathComponent("Build") .appendingPathComponent("Intermediates.noindex") .appendingPathComponent("ArchiveIntermediates") diff --git a/Sources/GenIR/Extensions/Process+Extension.swift b/Sources/GenIR/Extensions/Process+Extension.swift index a5df895..7de2e80 100644 --- a/Sources/GenIR/Extensions/Process+Extension.swift +++ b/Sources/GenIR/Extensions/Process+Extension.swift @@ -54,7 +54,16 @@ extension Process { let process = Process() - let executable = command.replacingOccurrences(of: "\\", with: "") + let executable: String + let args: [String] + + if command.starts(with: ".") || command.starts(with: "/") { + executable = command.replacingOccurrences(of: "\\", with: "") + args = arguments + } else { + executable = "/usr/bin/env" + args = [command] + arguments + } if #available(macOS 10.13, *) { process.executableURL = executable.fileURL @@ -62,7 +71,7 @@ extension Process { process.launchPath = executable } - process.arguments = arguments.map { $0.replacingOccurrences(of: "\\", with: "") } + process.arguments = args.map { $0.replacingOccurrences(of: "\\", with: "") } process.standardOutput = stdoutPipe process.standardError = stderrPipe process.standardInput = FileHandle.nullDevice diff --git a/Sources/GenIR/PIFCache.swift b/Sources/GenIR/PIFCache.swift index 449b4e5..3325c3a 100644 --- a/Sources/GenIR/PIFCache.swift +++ b/Sources/GenIR/PIFCache.swift @@ -2,7 +2,6 @@ import Foundation import PIFSupport class PIFCache { - private let buildCache: URL private let pifCachePath: URL private let workspace: PIF.Workspace @@ -22,7 +21,6 @@ class PIFCache { } init(buildCache: URL) throws { - self.buildCache = buildCache self.pifCachePath = try Self.pifCachePath(in: buildCache) do { @@ -34,13 +32,12 @@ class PIFCache { } private static func pifCachePath(in buildCache: URL) throws -> URL { - // TODO: test this variation, because I haven't seen this personally let cmakePIFCachePath = buildCache - .deletingLastPathComponent() .appendingPathComponent("XCBuildData") .appendingPathComponent("PIFCache") let regularPIFCachePath = buildCache + .appendingPathComponent("Build") .appendingPathComponent("Intermediates.noindex") .appendingPathComponent("XCBuildData") .appendingPathComponent("PIFCache") @@ -111,12 +108,10 @@ extension PIF.BaseTarget: Hashable { } struct PIFDependencyProvider: DependencyProviding { - private let targets: [Target] private let cache: PIFCache private var guidToTargets: [PIF.GUID: Target] init(targets: [Target], cache: PIFCache) { - self.targets = targets self.cache = cache self.guidToTargets = targets @@ -168,24 +163,20 @@ struct PIFDependencyProvider: DependencyProviding { .compactMap { resolveSwiftPackage($0) } // Framework build phase dependencies - let frameworkBuildPhases = value + // NOTE: Previously we just cast this - all of a sudden with pods this is broken + // Not the end of the world - just as quick to do a dictionary lookup + let frameworkGUIDs = value .baseTarget .buildPhases - .compactMap { $0 as? PIF.FrameworksBuildPhase } - - let referenceGUIDs = frameworkBuildPhases .flatMap { $0.buildFiles } + // .compactMap { $0 as? PIF.FrameworksBuildPhase } .compactMap { switch $0.reference { - case .file(let guid): return guid - case .target: return nil // TODO: is this fine? I think so since we're looking for .framework file references here not targets which should be a dependency + case let .file(guid): return guid + case .target: return nil } } - - let frameworkGUIDs = referenceGUIDs - .compactMap { - cache.frameworks[$0]?.guid - } + .compactMap { cache.frameworks[$0]?.guid } let dependencyTargets = (dependencyTargetGUIDs + frameworkGUIDs).compactMap { guidToTargets[$0] } diff --git a/Sources/GenIR/XcodeLogParser.swift b/Sources/GenIR/XcodeLogParser.swift index ced8b74..c3495a5 100644 --- a/Sources/GenIR/XcodeLogParser.swift +++ b/Sources/GenIR/XcodeLogParser.swift @@ -92,14 +92,24 @@ class XcodeLogParser { guard let startIndex = line.firstIndex(of: ":") else { continue } let stripped = line[line.index(after: startIndex).. Date: Mon, 27 May 2024 15:10:46 +0200 Subject: [PATCH 13/21] PIF Package: Add Logger and update test --- PIF/Package.resolved | 15 +++++++++++++++ PIF/Package.swift | 8 ++++++-- PIF/Sources/pif-parser/pif-parser.swift | 5 +++-- 3 files changed, 24 insertions(+), 4 deletions(-) create mode 100644 PIF/Package.resolved diff --git a/PIF/Package.resolved b/PIF/Package.resolved new file mode 100644 index 0000000..d0d296f --- /dev/null +++ b/PIF/Package.resolved @@ -0,0 +1,15 @@ +{ + "originHash" : "12c22d4bea7efd3c5084cc3bac01b276e5f01c1011991fc15f93790ff260cb4b", + "pins" : [ + { + "identity" : "swift-log", + "kind" : "remoteSourceControl", + "location" : "https://github.com/apple/swift-log.git", + "state" : { + "revision" : "e97a6fcb1ab07462881ac165fdbb37f067e205d5", + "version" : "1.5.4" + } + } + ], + "version" : 3 +} diff --git a/PIF/Package.swift b/PIF/Package.swift index 9391feb..ff4790d 100644 --- a/PIF/Package.swift +++ b/PIF/Package.swift @@ -13,13 +13,17 @@ let package = Package( targets: ["PIFSupport"] ) ], - dependencies: [], + dependencies: [ + .package(url: "https://github.com/apple/swift-log.git", from: "1.0.0") + ], targets: [ // Targets are the basic building blocks of a package, defining a module or a test suite. // Targets can depend on other targets in this package and products from dependencies. .target( name: "PIFSupport", - dependencies: [] + dependencies: [ + .product(name: "Logging", package: "swift-log") + ] ), .testTarget( name: "PIFSupportTests", diff --git a/PIF/Sources/pif-parser/pif-parser.swift b/PIF/Sources/pif-parser/pif-parser.swift index 78db443..92cf019 100644 --- a/PIF/Sources/pif-parser/pif-parser.swift +++ b/PIF/Sources/pif-parser/pif-parser.swift @@ -7,6 +7,7 @@ import Foundation import PIFSupport +import Logging @main struct PIFParser { @@ -17,8 +18,8 @@ struct PIFParser { } let cachePath = URL(fileURLWithPath: CommandLine.arguments[1]) - let parser = PIFSupport.PIFParser(cachePath: cachePath) - let workspace = try parser.parse() + let parser = try PIFSupport.PIFParser(cachePath: cachePath, logger: .init(label: "com.veracode.pif-parser")) + let workspace = parser.workspace print("workspace: \(workspace.guid):") print("projects: \(workspace.projects.map { $0.guid }.joined(separator: "\n"))\n") From eb096ed71edadb8edbf46f2a1e41c923c6c0f7b1 Mon Sep 17 00:00:00 2001 From: NinjaLikesCheez Date: Mon, 27 May 2024 15:10:58 +0200 Subject: [PATCH 14/21] Remove version files from tests --- Tests/GenIRTests/UmbrellaTests.swift | 8 ++++---- Tests/GenIRTests/WorkspaceTests.swift | 12 ++++++------ 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/Tests/GenIRTests/UmbrellaTests.swift b/Tests/GenIRTests/UmbrellaTests.swift index cd9f969..13a40af 100644 --- a/Tests/GenIRTests/UmbrellaTests.swift +++ b/Tests/GenIRTests/UmbrellaTests.swift @@ -11,10 +11,10 @@ final class UmbrellaTests: XCTestCase { let scheme = "Umbrella" let targetsToFiles = [ - "Common.framework": ["Common_vers.bc", "Common-dummy.bc", "OrgModel.bc"].sorted(), - "Networking.framework": ["Networking_vers.bc", "Networking-dummy.bc", "Networking.bc"].sorted(), - "Pods_Umbrella.framework": ["Pods_Umbrella_vers.bc", "Pods-Umbrella-dummy.bc"].sorted(), - "Umbrella.framework": ["GetOrg.bc", "Umbrella_vers.bc"].sorted() + "Common.framework": ["OrgModel.bc"].sorted(), + "Networking.framework": ["Networking.bc"].sorted(), + "Pods_Umbrella.framework": [], + "Umbrella.framework": ["GetOrg.bc"].sorted() ] func testUmbrellaTargets() throws { diff --git a/Tests/GenIRTests/WorkspaceTests.swift b/Tests/GenIRTests/WorkspaceTests.swift index b250b92..d5abf84 100644 --- a/Tests/GenIRTests/WorkspaceTests.swift +++ b/Tests/GenIRTests/WorkspaceTests.swift @@ -11,8 +11,8 @@ final class WorkspaceTests: XCTestCase { let scheme = "App" static let appIRFiles: Set = ["AppApp.bc", "ContentView.bc", "GeneratedAssetSymbols.bc"] - static let commonIRFiles: Set = ["Common_vers.bc", "Model.bc"] - static let frameworkIRFiles: Set = ["Framework_vers.bc", "Framework.bc"] + static let commonIRFiles: Set = ["Model.bc"] + static let frameworkIRFiles: Set = ["Framework.bc"] static let sfSafeSymbolsIRFiles: Set = [ "NSImageExtension.bc", "SFSymbol+1.0.bc", @@ -94,9 +94,9 @@ final class WorkspaceTests: XCTestCase { let expectedSFSafeSymbolsIRFiles = Self.sfSafeSymbolsIRFiles .reduce(into: Set(), { $0.insert($1) }) - XCTAssertEqual(expectedAppIRFiles, appIRPathContents, "App IR expected contents didn't equal actual") - XCTAssertEqual(expectedFrameworkIRFiles, frameworkIRPathContents, "Framework IR expected contents didn't equal actual") - XCTAssertEqual(expectedCommonIRFiles, commonIRPathContents, "Common IR expected contents didn't equal actual") - XCTAssertEqual(expectedSFSafeSymbolsIRFiles, sfSafeSymbolsIRPathContents, "SFSafeSymbols IR expected contents didn't equal actual") + XCTAssertEqual(expectedAppIRFiles, appIRPathContents, "App IR expected contents didn't equal actual: \(expectedAppIRFiles.symmetricDifference(appIRPathContents))") + XCTAssertEqual(expectedFrameworkIRFiles, frameworkIRPathContents, "Framework IR expected contents didn't equal actual \(expectedFrameworkIRFiles.symmetricDifference(frameworkIRPathContents))") + XCTAssertEqual(expectedCommonIRFiles, commonIRPathContents, "Common IR expected contents didn't equal actual \(expectedCommonIRFiles.symmetricDifference(commonIRPathContents))") + XCTAssertEqual(expectedSFSafeSymbolsIRFiles, sfSafeSymbolsIRPathContents, "SFSafeSymbols IR expected contents didn't equal actual \(expectedSFSafeSymbolsIRFiles.symmetricDifference(sfSafeSymbolsIRPathContents))") } } From cba89fb7235adf5bfe782f68044c51783bdcaa37 Mon Sep 17 00:00:00 2001 From: NinjaLikesCheez Date: Tue, 28 May 2024 14:08:00 +0200 Subject: [PATCH 15/21] Add documentation, refactor the Graph ready for extraction --- .swiftlint.yml | 10 +-- PIF/Sources/PIFSupport/PIFSupport.swift | 57 +++++++------ PIF/Sources/pif-parser/pif-parser.swift | 4 +- Sources/GenIR/BuildCacheManipulator.swift | 37 ++++++--- Sources/GenIR/CompilerCommandRunner.swift | 10 ++- .../Dependency Graph/DependencyGraph.swift | 39 ++++----- .../DependencyGraphBuilder.swift | 23 +++++- Sources/GenIR/Dependency Graph/Edge.swift | 58 +++++++++----- Sources/GenIR/Dependency Graph/Node.swift | 48 ++++++----- .../GenIR/Extensions/Process+Extension.swift | 19 +++-- Sources/GenIR/GenIR.swift | 4 +- Sources/GenIR/OutputPostprocessor.swift | 35 +++++--- Sources/GenIR/PIFCache.swift | 79 +++++++++++++++---- Sources/GenIR/Target.swift | 17 ++++ Sources/GenIR/XcodeLogParser.swift | 1 + Tests/GenIRTests/TestContext.swift | 32 ++++++-- Tests/GenIRTests/WorkspaceTests.swift | 6 +- Tests/GenIRTests/gen_irTests.swift | 2 +- 18 files changed, 322 insertions(+), 159 deletions(-) diff --git a/.swiftlint.yml b/.swiftlint.yml index d40d541..aec2552 100644 --- a/.swiftlint.yml +++ b/.swiftlint.yml @@ -5,6 +5,9 @@ excluded: - .build/ - TestAssets/ - PIF/.build/ + - '**/Package.swift' + - Package.swift + - PIF/Sources/pif-parser/pif-parser.swift disabled_rules: - todo @@ -16,11 +19,8 @@ line_length: missing_docs: warning: - - private - - open - - fileprivate - - public - - internal + - private + - public opt_in_rules: - missing_docs diff --git a/PIF/Sources/PIFSupport/PIFSupport.swift b/PIF/Sources/PIFSupport/PIFSupport.swift index dedfd15..7ca3b8e 100644 --- a/PIF/Sources/PIFSupport/PIFSupport.swift +++ b/PIF/Sources/PIFSupport/PIFSupport.swift @@ -1,17 +1,22 @@ import Foundation import Logging +/// Global logger for the module var logger: Logger! -public class PIFParser { +/// PIFCacheParser is responsible for discovering the files in a PIF Cache and decoding them. +public class PIFCacheParser { + /// The path to the PIF Cache (in Xcode's DerivedData this is often under Build/Intermediates.noindex/XCBuildData/PIFCache) private let cachePath: URL + /// The most recent workspace in the cache public let workspace: PIF.Workspace public enum Error: Swift.Error { + /// A PIF Workspace was not found in the cache case workspaceNotFound - case filesystemError(Swift.Error) } + /// Creates an instance initialized with the cache data at the given path. public init(cachePath: URL, logger log: Logger) throws { logger = log self.cachePath = cachePath @@ -20,39 +25,33 @@ public class PIFParser { workspace = try PIF.PIFDecoder(cache: cachePath).decode(PIF.Workspace.self, from: data) } + /// Finds the most recent workspace in the cache + /// - Throws: a `workspaceNotFound` error when a workspace is not found + /// - Returns: the path to the most recent workspace file private static func workspacePath(in cachePath: URL) throws -> URL { - let path = cachePath.appendingPathComponent("workspace") - - let workspaces = try FileManager.default.contentsOfDirectory(at: path, includingPropertiesForKeys: [.isRegularFileKey]) - .filter { $0.lastPathComponent.starts(with: "WORKSPACE@") } - - guard !workspaces.isEmpty else { - throw Error.workspaceNotFound + let workspaces = try FileManager.default.contentsOfDirectory( + at: cachePath.appendingPathComponent("workspace"), + includingPropertiesForKeys: nil + ) + .filter { $0.lastPathComponent.starts(with: "WORKSPACE@") } + .map { + ( + workspace: $0, + modificationDate: (try? FileManager.default.attributesOfItem(atPath: $0.path)[.modificationDate] as? Date) ?? Date() + ) } - if workspaces.count == 1 { - return workspaces[0] + if workspaces.isEmpty { + throw Error.workspaceNotFound + } else if workspaces.count == 1, let workspace = workspaces.first?.workspace { + return workspace } // If multiple workspaces exist, it's because the something in the project changed between builds. Sort workspaces by the most recent. - func modificationDate(_ path: URL) -> Date { - (try? FileManager.default.attributesOfItem(atPath: path.path)[.modificationDate] as? Date) ?? Date() - } - - logger.debug("Found multiple workspaces, sorting by modification date and returning most recently modified workspace") - - let workspacesAndDates = workspaces - .map { - (modificationDate($0), $0) - } - - logger.debug("Comparing dates and workspaces: ") - workspacesAndDates.forEach { logger.debug("\($0) - \($1)") } - - return workspacesAndDates - .sorted { - $0.0 > $1.0 - }[0].1 + return workspaces + .sorted(using: KeyPathComparator(\.modificationDate)) + .first! + .workspace } } diff --git a/PIF/Sources/pif-parser/pif-parser.swift b/PIF/Sources/pif-parser/pif-parser.swift index 92cf019..b7b0c8b 100644 --- a/PIF/Sources/pif-parser/pif-parser.swift +++ b/PIF/Sources/pif-parser/pif-parser.swift @@ -10,7 +10,7 @@ import PIFSupport import Logging @main -struct PIFParser { +struct PIFCacheParser { static func main() throws { guard CommandLine.arguments.count == 2 else { print("USAGE: \(CommandLine.arguments.first!) [PIFCache path]") @@ -18,7 +18,7 @@ struct PIFParser { } let cachePath = URL(fileURLWithPath: CommandLine.arguments[1]) - let parser = try PIFSupport.PIFParser(cachePath: cachePath, logger: .init(label: "com.veracode.pif-parser")) + let parser = try PIFSupport.PIFCacheParser(cachePath: cachePath, logger: .init(label: "com.veracode.pif-parser")) let workspace = parser.workspace print("workspace: \(workspace.guid):") diff --git a/Sources/GenIR/BuildCacheManipulator.swift b/Sources/GenIR/BuildCacheManipulator.swift index 43501db..cc18684 100644 --- a/Sources/GenIR/BuildCacheManipulator.swift +++ b/Sources/GenIR/BuildCacheManipulator.swift @@ -8,6 +8,7 @@ struct BuildCacheManipulator { /// Build settings used as part of the build private let buildSettings: [String: String] + /// Run without doing any cache manipulation private let dryRun: Bool /// Should we run the SKIP_INSTALL hack? @@ -21,6 +22,12 @@ struct BuildCacheManipulator { case tooManyDirectories(String) } + /// Creates an instance of the cache manipulator + /// - Parameters: + /// - buildCachePath: the build cache to operate on + /// - buildSettings: the project build settings + /// - archive: the path to the xcarchive produced as part of the build + /// - dryRun: should be a dry run? init(buildCachePath: URL, buildSettings: [String: String], archive: URL, dryRun: Bool) throws { self.buildCachePath = buildCachePath self.buildSettings = buildSettings @@ -35,7 +42,10 @@ struct BuildCacheManipulator { } } + /// Start the build cache manipulator func manipulate() throws { + guard !dryRun else { return } + if shouldDeploySkipInstallHack { let intermediatesPath = buildCachePath .appendingPathComponent("Build") @@ -79,17 +89,19 @@ struct BuildCacheManipulator { } } - // This is a hack. Turn away now. - // - // When archiving frameworks with the SKIP_INSTALL=NO setting, frameworks will be evicted (see below) from the build cache. - // This means when we rerun commands to generate IR, the frameworks no longer exist on disk, and we fail with linker errors. - // - // This is how the build cache is (roughly) laid out: - // - // * Build/Intermediates.noindex/ArchiveIntermediates//BuildProductsPath/- - // * this contains a set of symlinks to elsewhere in the build cache. These links remain in place, but the items they point to are removed - // - // The idea here is simple, attempt to update the symlinks so they point to valid framework product. + /// This is a hack. Turn away now. + /// + /// When archiving frameworks with the SKIP_INSTALL=NO setting, frameworks will be evicted (see below) from the build cache. + /// This means when we rerun commands to generate IR, the frameworks no longer exist on disk, and we fail with linker errors. + /// + /// This is how the build cache is (roughly) laid out: + /// + /// * Build/Intermediates.noindex/ArchiveIntermediates//BuildProductsPath/- + /// * this contains a set of symlinks to elsewhere in the build cache. These links remain in place, but the items they point to are removed + /// + /// The idea here is simple, attempt to update the symlinks so they point to valid framework product. + /// + /// - Parameter archiveBuildProductsPath: build products path (see description) private func skipInstallHack(_ archiveBuildProductsPath: URL) throws { let symlinksToUpdate = FileManager.default.filteredContents(of: archiveBuildProductsPath) { $0.lastPathComponent.hasSuffix("framework") @@ -118,9 +130,10 @@ struct BuildCacheManipulator { } } + /// TODO: This could be more sensible and get the build configuration from the log and match that to a configuration in the PIF Cache /// Tries to find the xcode build configuration directory path inside the given path /// - Parameter path: the path to search - /// - Returns: + /// - Returns: the path to the build configuration directory, if found private static func findConfigurationDirectory(_ path: URL) -> URL? { let folders = (try? FileManager.default.directories(at: path, recursive: false)) ?? [] diff --git a/Sources/GenIR/CompilerCommandRunner.swift b/Sources/GenIR/CompilerCommandRunner.swift index e5586d5..b0c3657 100644 --- a/Sources/GenIR/CompilerCommandRunner.swift +++ b/Sources/GenIR/CompilerCommandRunner.swift @@ -20,8 +20,10 @@ struct CompilerCommandRunner { /// The directory to place the LLVM BC output private let output: URL + /// The cache manipulator, required to do fix ups on the build cache in very specific circumstances private let buildCacheManipulator: BuildCacheManipulator + /// Manager used to access the file system private let fileManager = FileManager.default enum Error: Swift.Error { @@ -29,11 +31,14 @@ struct CompilerCommandRunner { case failedToParse(String) } + /// Run without running the commands private let dryRun: Bool - /// Initializes a runner + /// Initializes the runner /// - Parameters: /// - output: The location to place the resulting LLVM IR + /// - buildCacheManipulator: the cache manipulator to perform fixups with + /// - dryRun: should run in dry run mode? init(output: URL, buildCacheManipulator: BuildCacheManipulator, dryRun: Bool) { self.output = output self.dryRun = dryRun @@ -41,6 +46,7 @@ struct CompilerCommandRunner { } /// Starts the runner + /// - Parameter targets: the targets holding the commands to run func run(targets: [Target]) throws { // Quick, do a hack! try buildCacheManipulator.manipulate() @@ -165,7 +171,7 @@ struct CompilerCommandRunner { /// Parses, and corrects, the executable name and arguments for a given command. /// - Parameter command: The command to parse and correct /// - Returns: A tuple of executable name and an array of arguments - private func parse(command: CompilerCommand) throws -> (String, [String]) { + private func parse(command: CompilerCommand) throws -> (executable: String, arguments: [String]) { let fixed = fixup(command: command.command) let (executable, arguments) = try split(command: fixed) let fixedArguments = fixup(arguments: arguments, for: command.compiler) diff --git a/Sources/GenIR/Dependency Graph/DependencyGraph.swift b/Sources/GenIR/Dependency Graph/DependencyGraph.swift index e1cd9e6..f6107f3 100644 --- a/Sources/GenIR/Dependency Graph/DependencyGraph.swift +++ b/Sources/GenIR/Dependency Graph/DependencyGraph.swift @@ -10,32 +10,32 @@ import Foundation /// A directed graph that maps dependencies between values (nodes) via edges (directions between nodes) class DependencyGraph { /// All the nodes in the graph - private(set) var nodes = [String: Node]() + private(set) var nodes = [String: Node]() - /// Adds a value to the graph - /// - Parameter value: the value to add - /// - Returns: the node added - func addNode(value: Value) -> Node { + /// Adds a node for the associated value to the graph + /// - Parameter value: the value associated with the node + /// - Returns: the node for the associated value. If the node already existed it is returned + func addNode(for value: Value) -> Node { if let node = findNode(for: value) { return node } - let node = Node(value) + let node = Node(value) nodes[value.valueName] = node return node } - /// Finds a value's node in the graph + /// Finds the node associated with a value /// - Parameter value: the value to look for - /// - Returns: the node for the given value, if found - func findNode(for value: Value) -> Node? { + /// - Returns: the node for which the value is associated, if found + func findNode(for value: Value) -> Node? { nodes[value.valueName] } - /// Builds a dependency 'chain' for a value using a depth-first search - /// - Parameter value: the value to get a chain for - /// - Returns: the chain of nodes, starting - func chain(for value: Value) -> [Node] { + /// Returns the dependency 'chain' for the value associated with a node in the graph using a depth-first search + /// - Parameter value: the associated value for a node to start the search with + /// - Returns: the chain of nodes, starting with the 'bottom' of the dependency subgraph + func chain(for value: Value) -> [Node] { guard let node = findNode(for: value) else { logger.debug("Couldn't find node for value: \(value.valueName)") return [] @@ -47,15 +47,16 @@ class DependencyGraph { /// Perform a depth-first search starting at the provided node /// - Parameter node: the node whose children to search through /// - Returns: an array of nodes ordered by a depth-first search approach - private func depthFirstSearch(startingAt node: Node) -> [Node] { + private func depthFirstSearch(startingAt node: Node) -> [Node] { logger.debug("----\nSearching for: \(node.value.valueName)") - var visited = Set>() - var chain = [Node]() + var visited = Set() + var chain = [Node]() - func depthFirst(node: Node) { + /// Visits node dependencies and adds them to the chain from the bottom up + /// - Parameter node: the node to search through + func depthFirst(node: Node) { logger.debug("inserting node: \(node.value.valueName)") visited.insert(node) - logger.debug("visited: \(visited)") for edge in node.edges where edge.relationship == .dependency { if visited.insert(edge.to).inserted { @@ -74,6 +75,8 @@ class DependencyGraph { return chain } + /// Writes a 'dot' graph file to disk + /// - Parameter path: the path to write the graph to func toDot(_ path: String) throws { var contents = "digraph DependencyGraph {\n" diff --git a/Sources/GenIR/Dependency Graph/DependencyGraphBuilder.swift b/Sources/GenIR/Dependency Graph/DependencyGraphBuilder.swift index 9e88715..429c0a2 100644 --- a/Sources/GenIR/Dependency Graph/DependencyGraphBuilder.swift +++ b/Sources/GenIR/Dependency Graph/DependencyGraphBuilder.swift @@ -5,17 +5,32 @@ // Created by Thomas Hedderwick on 28/08/2023. // +/// A type that provides dependency relationships between values protocol DependencyProviding { + /// A type that represents the value of a node associatedtype Value: NodeValue + + /// Returns the direct dependencies for a given value + /// - Parameter value: the value to get dependencies for + /// - Returns: a list of dependencies func dependencies(for value: Value) -> [Value] } +/// A builder for the DependencyGraph - you should _always_ use this class to build out the `DependencyGraph` class DependencyGraphBuilder where Value == Provider.Value { + /// The graph the builder will operate on + typealias Graph = DependencyGraph + + /// The dependency provider private let provider: Provider - let graph = DependencyGraph() + + /// The built graph + let graph = Graph() /// Inits the Graph Builder - /// - Parameter provider: the dependency provider for the values + /// - Parameters: + /// - provider: the dependency provider for the values + /// - values: the values to add to the graph init(provider: Provider, values: [Value]) { self.provider = provider values.forEach { add(value: $0) } @@ -25,7 +40,7 @@ class DependencyGraphBuilder wh /// - Parameters: /// - value: the value to add @discardableResult - private func add(value: Value) -> Node { + private func add(value: Value) -> Graph.Node { if let existingNode = graph.findNode(for: value) { return existingNode } @@ -33,7 +48,7 @@ class DependencyGraphBuilder wh logger.debug("Adding value: \(value.valueName) to graph") let dependencies = provider.dependencies(for: value) - let node = graph.addNode(value: value) + let node = graph.addNode(for: value) for dependency in dependencies { let dependencyNode = add(value: dependency) diff --git a/Sources/GenIR/Dependency Graph/Edge.swift b/Sources/GenIR/Dependency Graph/Edge.swift index c78de34..c9d8d41 100644 --- a/Sources/GenIR/Dependency Graph/Edge.swift +++ b/Sources/GenIR/Dependency Graph/Edge.swift @@ -7,36 +7,52 @@ // swiftlint:disable identifier_name /// An edge describes the relationship between two Nodes in a graph -class Edge { - /// The source node - let to: Node - /// The destination node - let from: Node - /// The relationship between the two nodes - let relationship: Relationship - /// Description of the relationships between two nodes - enum Relationship { - /// From depends on To - case dependency - /// From is a depender of To - case depender - } +extension DependencyGraph { + class Edge { + /// The source node + let to: DependencyGraph.Node + /// The destination node + let from: DependencyGraph.Node + /// The relationship between the two nodes + let relationship: Relationship + + /// Description of the relationships between two nodes + enum Relationship { + /// From depends on To + case dependency + /// From is a depender of To + case depender + } - init(to: Node, from: Node, relationship: Relationship) { - self.to = to - self.from = from - self.relationship = relationship + /// Initializes an edge between two nodes + /// - Parameters: + /// - to: the node this edge is pointing to + /// - from: the node this edge is pointing from + /// - relationship: the type of relationship this edge represents + init(to: DependencyGraph.Node, from: DependencyGraph.Node, relationship: Relationship) { + self.to = to + self.from = from + self.relationship = relationship + } } } -extension Edge: Equatable { - static func == (_ lhs: Edge, rhs: Edge) -> Bool { +extension DependencyGraph.Edge: Equatable { + static func == (_ lhs: DependencyGraph.Edge, rhs: DependencyGraph.Edge) -> Bool { lhs.to == rhs.to && lhs.from == rhs.from && lhs.relationship == rhs.relationship } } -extension Edge: CustomStringConvertible { +extension DependencyGraph.Edge: Hashable { + func hash(into hasher: inout Hasher) { + hasher.combine(to) + hasher.combine(from) + hasher.combine(relationship) + } +} + +extension DependencyGraph.Edge: CustomStringConvertible { var description: String { "[Edge from \(from) to \(to) relationship: \(relationship)]"} } // swiftlint:enable identifier_name diff --git a/Sources/GenIR/Dependency Graph/Node.swift b/Sources/GenIR/Dependency Graph/Node.swift index a2e9406..cf94ac8 100644 --- a/Sources/GenIR/Dependency Graph/Node.swift +++ b/Sources/GenIR/Dependency Graph/Node.swift @@ -12,36 +12,42 @@ protocol NodeValue: Hashable { var valueName: String { get } } -class Node { - /// The edges from and to this node - private(set) var edges = [Edge]() - /// The value this node represents - let value: Value - /// The name of this node - var valueName: String { - value.valueName - } +extension DependencyGraph { + class Node { + /// The edges from and to this node + private(set) var edges = [DependencyGraph.Edge]() - init(_ value: Value) { - self.value = value - } + /// The associated value of this node + let value: Value + + /// The name of this node + var valueName: String { + value.valueName + } + + /// Initializes a node with an associated value + /// - Parameter value: the value to associate with this node + init(_ value: Value) { + self.value = value + } - /// Adds an edge to this node - /// - Parameter edge: the edge to add - func add(edge: Edge) { - if edges.filter({ $0.to.valueName == edge.to.valueName }).count == 0 { - edges.append(edge) + /// Adds an edge to this node + /// - Parameter edge: the edge to add + func add(edge: DependencyGraph.Edge) { + if edges.filter({ $0.to.valueName == edge.to.valueName }).count == 0 { + edges.append(edge) + } } } } -extension Node: Equatable { - static func == (_ lhs: Node, rhs: Node) -> Bool { +extension DependencyGraph.Node: Equatable { + static func == (_ lhs: DependencyGraph.Node, rhs: DependencyGraph.Node) -> Bool { lhs.value == rhs.value && lhs.edges == rhs.edges } } -extension Node: CustomStringConvertible { +extension DependencyGraph.Node: CustomStringConvertible { var description: String { var description = "" @@ -55,7 +61,7 @@ extension Node: CustomStringConvertible { } } -extension Node: Hashable { +extension DependencyGraph.Node: Hashable { func hash(into hasher: inout Hasher) { hasher.combine(valueName) hasher.combine(value) diff --git a/Sources/GenIR/Extensions/Process+Extension.swift b/Sources/GenIR/Extensions/Process+Extension.swift index 7de2e80..c1d2c8e 100644 --- a/Sources/GenIR/Extensions/Process+Extension.swift +++ b/Sources/GenIR/Extensions/Process+Extension.swift @@ -8,7 +8,7 @@ import Foundation extension Process { - /// Presents the result of a Process + /// Represents the result of a Process struct ReturnValue { /// The stdout output of the process, if there was any let stdout: String? @@ -17,17 +17,22 @@ extension Process { /// The return code of the process let code: Int32 + /// Initializes a ReturnValue + /// - Parameters: + /// - stdout: the standard output + /// - stderr: the standard error + /// - code: the exit code init(stdout: String?, stderr: String?, code: Int32) { - if let stdout, stdout.isEmpty { - self.stdout = nil + self.stdout = if let stdout, stdout.isEmpty { + nil } else { - self.stdout = stdout + stdout } - if let stderr, stderr.isEmpty { - self.stderr = nil + self.stderr = if let stderr, stderr.isEmpty { + nil } else { - self.stderr = stderr + stderr } self.code = code diff --git a/Sources/GenIR/GenIR.swift b/Sources/GenIR/GenIR.swift index 61f8bb9..75e13d2 100644 --- a/Sources/GenIR/GenIR.swift +++ b/Sources/GenIR/GenIR.swift @@ -6,6 +6,7 @@ import PIFSupport /// Global logger object var logger = Logger(label: Bundle.main.bundleIdentifier ?? "com.veracode.gen-ir", factory: StdOutLogHandler.init) +/// The name of the program let programName = CommandLine.arguments.first! /// Command to emit LLVM IR from an Xcode build log @@ -150,8 +151,7 @@ struct IREmitterCommand: ParsableCommand { let postprocessor = try OutputPostprocessor( archive: archive, output: output, - graph: graph, - targets: targets + graph: graph ) try postprocessor.process() diff --git a/Sources/GenIR/OutputPostprocessor.swift b/Sources/GenIR/OutputPostprocessor.swift index 964ea02..6c7f754 100644 --- a/Sources/GenIR/OutputPostprocessor.swift +++ b/Sources/GenIR/OutputPostprocessor.swift @@ -7,8 +7,6 @@ import Foundation -private typealias SizeAndCreation = (Int, Date) - /// The `OutputPostprocessor` is responsible for trying to match the IR output of the `CompilerCommandRunner` with the products in the `xcarchive`. /// The `CompilerCommandRunner` will output IR with it's product name, but doesn't take into account the linking of products into each other. /// The `OutputPostprocessor` attempts to remedy this by using the `ProjectParser` to detect dependencies between products AND @@ -20,18 +18,23 @@ class OutputPostprocessor { /// Mapping of dynamic dependencies (inside the xcarchive) to their paths on disk private let dynamicDependencyToPath: [String: URL] + /// A dependency graph containing the targets in the output archive private let graph: DependencyGraph + + /// The targets in this archive private let targets: [Target] - private var seenConflictingFiles: [URL: [SizeAndCreation]] = [:] + /// A mapping of targets to their path on disk private let targetsToPaths: [Target: URL] + /// The manager to use for file system access private let manager: FileManager = .default - init(archive: URL, output: URL, graph: DependencyGraph, targets: [Target]) throws { + /// Initializes the postprocessor + init(archive: URL, output: URL, graph: DependencyGraph) throws { self.archive = archive self.graph = graph - self.targets = targets + self.targets = graph.nodes.map { $0.value.value } let namesToTargets = targets .reduce(into: [String: Target]()) { partial, target in @@ -44,7 +47,7 @@ class OutputPostprocessor { if let target = namesToTargets[path.lastPathComponent] { partial[target] = path } else { - logger.error("Failed to look up target for path: \(path)") + logger.error("Path (\(path.lastPathComponent)) wasn't found in namesToTargets: \(namesToTargets)") } } @@ -122,10 +125,11 @@ class OutputPostprocessor { func copyContentsOfDirectoryMergingDifferingFiles(at source: URL, to destination: URL) throws { let files = try manager.contentsOfDirectory(at: source, includingPropertiesForKeys: nil) + /// Source and destination paths + typealias SourceAndDestination = (source: URL, destination: URL) // Get two arrays of file paths of the source and destination file where: // 1) destination already exists // 2) destination doesn't already exist - typealias SourceAndDestination = (source: URL, destination: URL) let (existing, nonexisting) = files .map { ( @@ -154,6 +158,12 @@ class OutputPostprocessor { } } + /// The size and creation date of a file system item + private typealias SizeAndCreation = (Int, Date) + + /// A cache of seen files and their associated metadata + private var seenConflictingFiles: [URL: [SizeAndCreation]] = [:] + /// Copies a file, uniquing the path if it conflicts, _if_ the files they conflict with aren't the same size /// - Parameters: /// - source: source file path @@ -174,11 +184,7 @@ class OutputPostprocessor { let uniqueDestinationURL = manager.uniqueFilename(directory: destination.deletingLastPathComponent(), filename: source.lastPathComponent) - if seenConflictingFiles[source] == nil { - seenConflictingFiles[source] = [(sourceSize, sourceCreatedDate)] - } - - for (size, date) in seenConflictingFiles[source]! where size == destinationSize && date == destinationCreatedDate { + for (size, date) in seenConflictingFiles[source, default: [(sourceSize, sourceCreatedDate)]] where size == destinationSize && date == destinationCreatedDate { return } @@ -215,6 +221,9 @@ private func baseSearchPath(startingAt path: URL) -> URL { let applicationsPath = productsPath.appendingPathComponent("Applications") let frameworkPath = productsPath.appendingPathComponent("Library").appendingPathComponent("Framework") + /// Returns the first directory found at the given path + /// - Parameter path: the path to search for directories + /// - Returns: the first directory found in the path if one exists func firstDirectory(at path: URL) -> URL? { guard FileManager.default.directoryExists(at: path), @@ -225,7 +234,7 @@ private func baseSearchPath(startingAt path: URL) -> URL { } if contents.count > 1 { - logger.error("Expected one folder at: \(path). Found \(contents.count). Selecting \(contents.first!)") + logger.debug("Expected one folder at: \(path). Found \(contents.count). Selecting \(contents.first!)") } return contents.first! diff --git a/Sources/GenIR/PIFCache.swift b/Sources/GenIR/PIFCache.swift index 3325c3a..8fb6760 100644 --- a/Sources/GenIR/PIFCache.swift +++ b/Sources/GenIR/PIFCache.swift @@ -1,14 +1,22 @@ import Foundation import PIFSupport +/// The PIFCache provides a wrapper around a `PIF.Workspace`. +/// It includes a set of helper functions to make operating on the PIF Cache structures easier. +/// This class is used in conjunction with `PIFDependencyProvider` to enable building dependency relationships between the various targets class PIFCache { + /// The path to the PIF Cache private let pifCachePath: URL + + /// The most recent `PIF.Workspace` in the cache private let workspace: PIF.Workspace + /// All projects contained by the workspace var projects: [PIF.Project] { workspace.projects } + /// All targets contained by the workspace var targets: [PIF.BaseTarget] { workspace .projects @@ -20,17 +28,23 @@ class PIFCache { case pifError(Swift.Error) } + /// Initializes the PIF Cache from a build cache + /// - Parameter buildCache: path to the Xcode DerivedData Build Cache init(buildCache: URL) throws { self.pifCachePath = try Self.pifCachePath(in: buildCache) do { - let cache = try PIFParser(cachePath: pifCachePath, logger: logger) + let cache = try PIFCacheParser(cachePath: pifCachePath, logger: logger) workspace = cache.workspace } catch { throw Error.pifError(error) } } + /// Finds the PIF Cache in the Xcode Build Cache. This can vary depending on the build system used. + /// - Parameter buildCache: the Xcode build cache + /// - Throws: if no cache was found + /// - Returns: the path to the PIF Cache private static func pifCachePath(in buildCache: URL) throws -> URL { let cmakePIFCachePath = buildCache .appendingPathComponent("XCBuildData") @@ -56,24 +70,33 @@ class PIFCache { } } + /// Recursively gets all file references inside a given project + /// This will not return Groups, it will attempt to resolve them to the underlying file references + /// - Parameter project: the project to find file references in + /// - Returns: a list of file references private func fileReferences(for project: PIF.Project) -> [PIF.FileReference] { - func resolveChildren(starting children: [PIF.Reference], result: inout [PIF.FileReference]) { - for child in children { - if let fileReference = child as? PIF.FileReference { - result.append(fileReference) - } else if let group = child as? PIF.Group { + var result = [PIF.FileReference]() + /// Recursively resolve references to file references + /// - Parameters: + /// - children: the starting list of references + func resolveChildren(starting children: [PIF.Reference]) { + children.forEach { child in + switch child { + case let file as PIF.FileReference: + result.append(file) + case let group as PIF.Group: resolveChildren(starting: group.children, result: &result) - } else { - print("Unhandled reference type: \(child)") + default: + logger.debug("Unhandled reference type: \(child)") } } } - var result = [PIF.FileReference]() - resolveChildren(starting: project.groupTree.children, result: &result) + resolveChildren(starting: project.groupTree.children) return result } + /// A mapping of Framework `PIF.GUID`s to the framework `PIF.Target` lazy var frameworks: [PIF.GUID: PIF.Target] = { // Here we have to get all the wrapper.framework references from the group, and then attempt to map them to targets let frameworkFileReferences = projects @@ -107,10 +130,20 @@ extension PIF.BaseTarget: Hashable { } } +// TODO: think if there is a better type enforced way of defining the relationships here - should this be in the Targets file (and called TargetDependencyProvider) + +/// The PIFDependencyProvider is responsible for calculating dependency relationships between targets in the cache struct PIFDependencyProvider: DependencyProviding { + /// The `PIFCache` to provide dependency relationships for private let cache: PIFCache + + /// A mapping of `PIF.GUID` to the `Target` they represent private var guidToTargets: [PIF.GUID: Target] + // / Initializes the PIFDependencyProvider + /// - Parameters: + /// - targets: the list of targets to provide dependency relationships for + /// - cache: the cache that contains the targets init(targets: [Target], cache: PIFCache) { self.cache = cache @@ -120,12 +153,26 @@ struct PIFDependencyProvider: DependencyProviding { } } - private func resolveSwiftPackage(_ packageGUID: PIF.GUID) -> PIF.GUID? { + /// Attempts to resolve a Swift Package Product reference to it's Swift Package Target reference + /// When SPM generates PIF files relating to the package you (typically) end up with 4 PIF Targets: + /// - Package Product: + /// - This depends on the Package Target + /// - This has a dynamic target variant pointing to the Dynamic Package Product + /// - Dynamic Package Product + /// - This depends on the Package Target + /// - Package Target + /// - This is the object file that is the actual target we're looking for + /// - This has a dynamic target variant pointing to the Dynamic Package Target + /// - Dynamic Package Target + /// Typically, we want to take the Package Product and find the Package Target it depends on + /// - Parameter packageProductGUID: the swift product package guid + /// - Returns: the guid of the related Swift Package Target + private func resolveSwiftPackage(_ packageProductGUID: PIF.GUID) -> PIF.GUID? { let productToken = "PACKAGE-PRODUCT:" let targetToken = "PACKAGE-TARGET:" - guard packageGUID.starts(with: productToken), let product = guidToTargets[packageGUID] else { return packageGUID } + guard packageProductGUID.starts(with: productToken), let product = guidToTargets[packageProductGUID] else { return packageProductGUID } - let productName = String(packageGUID.dropFirst(productToken.count)) + let productName = String(packageProductGUID.dropFirst(productToken.count)) let productTargetDependencies = product .baseTarget @@ -138,10 +185,10 @@ struct PIFDependencyProvider: DependencyProviding { if productUnderlyingTargets.isEmpty && !productTargetDependencies.isEmpty { // We likely have a stub target here (i.e. a precompiled framework) // see https://github.com/apple/swift-package-manager/issues/6069 for more - logger.debug("Resolving Swift Package (\(productName) - \(packageGUID)) resulted in no targets. Possible stub target in: \(productTargetDependencies)") + logger.debug("Resolving Swift Package (\(productName) - \(packageProductGUID)) resulted in no targets. Possible stub target in: \(productTargetDependencies)") return nil } else if productUnderlyingTargets.isEmpty && productTargetDependencies.isEmpty { - logger.debug("Resolving Swift Package (\(productName) - \(packageGUID)) resulted in no targets. Likely a prebuilt dependency") + logger.debug("Resolving Swift Package (\(productName) - \(packageProductGUID)) resulted in no targets. Likely a prebuilt dependency") return nil } @@ -150,7 +197,7 @@ struct PIFDependencyProvider: DependencyProviding { return productTargetDependencies.first?.targetGUID } - logger.debug("\(packageGUID) resolves to \(target.targetGUID)") + logger.debug("\(packageProductGUID) resolves to \(target.targetGUID)") return target.targetGUID } diff --git a/Sources/GenIR/Target.swift b/Sources/GenIR/Target.swift index 0f26a8a..0453d1f 100644 --- a/Sources/GenIR/Target.swift +++ b/Sources/GenIR/Target.swift @@ -8,8 +8,13 @@ import Foundation import PIFSupport +/// A Target represents a product to build (app, framework, plugin, package) class Target { + /// The name of the target var name: String { baseTarget.name } + + /// The product name of the target, if one is available, otherwise the name + /// This can happen when the product is not directly buildable (such as a package product or aggregate) var productName: String { if let target = baseTarget as? PIF.Target, !target.productName.isEmpty { return target.productName @@ -17,11 +22,18 @@ class Target { return baseTarget.name } + // TODO: we need to handle SPM's insane naming scheme for products here ^ including the potential of a dynamic variant + /// The `PIF.BaseTarget` structure that backs this target let baseTarget: PIF.BaseTarget + /// The `CompilerCommands` related to building this target let commands: [CompilerCommand] + /// Initializes a target with the given backing target and commands + /// - Parameters: + /// - baseTarget: the underlying `PIF.BaseTarget` + /// - commands: the commands related to this target init(baseTarget: PIF.BaseTarget, commands: [CompilerCommand]) { self.baseTarget = baseTarget self.commands = commands @@ -29,6 +41,11 @@ class Target { } extension Target { + /// Helper function to map targets and commands to an array of targets + /// - Parameters: + /// - targets: the `PIF.BaseTarget`s that will back the new targets + /// - targetsToCommands: a mapping of target names to the `CompilerCommands` that relate to them + /// - Returns: the newly created targets static func targets(from targets: [PIF.BaseTarget], with targetsToCommands: [String: [CompilerCommand]]) -> [Target] { targets .map { diff --git a/Sources/GenIR/XcodeLogParser.swift b/Sources/GenIR/XcodeLogParser.swift index c3495a5..3f07a34 100644 --- a/Sources/GenIR/XcodeLogParser.swift +++ b/Sources/GenIR/XcodeLogParser.swift @@ -17,6 +17,7 @@ class XcodeLogParser { private(set) var settings: [String: String] = [:] /// The path to the Xcode build cache private(set) var buildCachePath: URL! + /// A mapping of target names to the compiler commands that relate to them private(set) var targetCommands: [String: [CompilerCommand]] = [:] enum Error: Swift.Error { diff --git a/Tests/GenIRTests/TestContext.swift b/Tests/GenIRTests/TestContext.swift index 8132ac5..a104aac 100644 --- a/Tests/GenIRTests/TestContext.swift +++ b/Tests/GenIRTests/TestContext.swift @@ -2,22 +2,29 @@ import Foundation @testable import gen_ir import XCTest +/// TestContext is a grouping of convenance functions and a context to ease the burden of testing Gen IR class TestContext { enum Error: Swift.Error { case commandFailed(Process.ReturnValue) case invalidArgument(String) - case notBuilt } - static let baseTestingPath: URL = { + /// The base folder path of the Gen IR project + static let baseProjectPath: URL = { // HACK: use the #file magic to get a path to the test case... Maybe there's a better way to do this? URL(fileURLWithPath: #file.replacingOccurrences(of: "Tests/GenIRTests/TestContext.swift", with: "")) }() + /// Path to the TestAssets folder static let testAssetPath: URL = { - baseTestingPath.appendingPathComponent("TestAssets") + baseProjectPath.appendingPathComponent("TestAssets") }() + /// Run xcodebuild's clean action + /// - Parameters: + /// - path: the path of the project to clean + /// - scheme: the name of the scheme to clean (required for workspaces) + /// - Returns: func clean(test path: URL, scheme: String) throws -> Process.ReturnValue { var arguments = ["clean"] @@ -38,8 +45,13 @@ class TestContext { ) } - @discardableResult - func build( + /// Run xcodebuild's archive action + /// - Parameters: + /// - path: the path of the project to build + /// - scheme: the name of the scheme to build + /// - additionalArguments: any additional arguments to passthrough to xcodebuild + /// - Returns: the result of running the action. + @discardableResult func build( test path: URL, scheme: String, additionalArguments: [String] = [] @@ -85,12 +97,18 @@ class TestContext { return process } + /// Path to the xcarchive (once built) let archive: URL + /// Path to the build log (once built) let buildLog: URL + /// Path to the temporary working directory for this context let temporaryDirectory: URL + /// Has the project been built? private(set) var built = false + /// Contents of the built build log private(set) var buildLogContents = [String]() + /// Initializes the test context init() { // swiftlint:disable force_try temporaryDirectory = try! FileManager.default.temporaryDirectory(named: "gen-ir-tests-\(UUID().uuidString)") @@ -103,6 +121,7 @@ class TestContext { // swiftlint:enable force_try } + /// Generate the log parser for this context lazy var logParser: XcodeLogParser = { XCTAssertTrue(built, "Requests a log parser without building the project") let parser = XcodeLogParser(log: buildLogContents) @@ -114,6 +133,7 @@ class TestContext { return parser }() + /// Generate the PIF Cache for this context lazy var pifCache: PIFCache = { do { return try PIFCache(buildCache: logParser.buildCachePath) @@ -122,10 +142,12 @@ class TestContext { } }() + /// Generate the Targets for this context lazy var targets: [Target] = { Target.targets(from: pifCache.targets, with: logParser.targetCommands) }() + /// Generate the dependency graph for this context lazy var graph: DependencyGraph = { DependencyGraphBuilder(provider: PIFDependencyProvider(targets: targets, cache: pifCache), values: targets).graph }() diff --git a/Tests/GenIRTests/WorkspaceTests.swift b/Tests/GenIRTests/WorkspaceTests.swift index d5abf84..711e66f 100644 --- a/Tests/GenIRTests/WorkspaceTests.swift +++ b/Tests/GenIRTests/WorkspaceTests.swift @@ -97,6 +97,10 @@ final class WorkspaceTests: XCTestCase { XCTAssertEqual(expectedAppIRFiles, appIRPathContents, "App IR expected contents didn't equal actual: \(expectedAppIRFiles.symmetricDifference(appIRPathContents))") XCTAssertEqual(expectedFrameworkIRFiles, frameworkIRPathContents, "Framework IR expected contents didn't equal actual \(expectedFrameworkIRFiles.symmetricDifference(frameworkIRPathContents))") XCTAssertEqual(expectedCommonIRFiles, commonIRPathContents, "Common IR expected contents didn't equal actual \(expectedCommonIRFiles.symmetricDifference(commonIRPathContents))") - XCTAssertEqual(expectedSFSafeSymbolsIRFiles, sfSafeSymbolsIRPathContents, "SFSafeSymbols IR expected contents didn't equal actual \(expectedSFSafeSymbolsIRFiles.symmetricDifference(sfSafeSymbolsIRPathContents))") + XCTAssertEqual( + expectedSFSafeSymbolsIRFiles, + sfSafeSymbolsIRPathContents, + "SFSafeSymbols IR expected contents didn't equal actual \(expectedSFSafeSymbolsIRFiles.symmetricDifference(sfSafeSymbolsIRPathContents))" + ) } } diff --git a/Tests/GenIRTests/gen_irTests.swift b/Tests/GenIRTests/gen_irTests.swift index 7f55708..fcfc920 100644 --- a/Tests/GenIRTests/gen_irTests.swift +++ b/Tests/GenIRTests/gen_irTests.swift @@ -4,7 +4,7 @@ import XCTest final class GenIRTests: XCTestCase { func testManyTargetTestTargets() throws { let context = TestContext() - let projectPath = TestContext.baseTestingPath + let projectPath = TestContext.baseProjectPath .appendingPathComponent("TestAssets") .appendingPathComponent("ManyTargetTest") .appendingPathComponent("ManyTargetTest.xcodeproj") From c0ff81967d01b3ce908efb5f12fb78d95c54f692 Mon Sep 17 00:00:00 2001 From: NinjaLikesCheez Date: Tue, 28 May 2024 16:24:29 +0200 Subject: [PATCH 16/21] Extract the DependencyGraph & Logger to packages --- .swiftlint.yml | 1 - Package.swift | 22 ++- .../DependencyGraph.swift | 13 +- .../DependencyGraphBuilder.swift | 12 +- .../Edge.swift | 17 +-- .../Node.swift | 17 +-- Sources/GenIR/BuildCacheManipulator.swift | 5 +- Sources/GenIR/CompilerCommandRunner.swift | 2 +- Sources/GenIR/GenIR.swift | 12 +- Sources/GenIR/OutputPostprocessor.swift | 136 +++++++++--------- Sources/GenIR/PIFCache.swift | 4 +- Sources/GenIR/StdoutLogHandler.swift | 82 ----------- Sources/GenIR/Target.swift | 1 + Sources/GenIR/XcodeLogParser.swift | 2 +- Sources/LogHandlers/.gitignore | 8 ++ .../LogHandlers/StdoutLogHandler.swift | 101 +++++++++++++ Tests/GenIRTests/DependencyGraphTests.swift | 1 + Tests/GenIRTests/TestContext.swift | 1 + 18 files changed, 244 insertions(+), 193 deletions(-) rename Sources/{GenIR/Dependency Graph => DependencyGraph}/DependencyGraph.swift (90%) rename Sources/{GenIR/Dependency Graph => DependencyGraph}/DependencyGraphBuilder.swift (84%) rename Sources/{GenIR/Dependency Graph => DependencyGraph}/Edge.swift (72%) rename Sources/{GenIR/Dependency Graph => DependencyGraph}/Node.swift (74%) delete mode 100644 Sources/GenIR/StdoutLogHandler.swift create mode 100644 Sources/LogHandlers/.gitignore create mode 100644 Sources/LogHandlers/Sources/LogHandlers/StdoutLogHandler.swift diff --git a/.swiftlint.yml b/.swiftlint.yml index aec2552..6b98756 100644 --- a/.swiftlint.yml +++ b/.swiftlint.yml @@ -19,7 +19,6 @@ line_length: missing_docs: warning: - - private - public opt_in_rules: diff --git a/Package.swift b/Package.swift index ac47272..43e6f30 100644 --- a/Package.swift +++ b/Package.swift @@ -8,6 +8,11 @@ let package = Package( platforms: [ .macOS(.v12) ], + products: [ + .library(name: "DependencyGraph", targets: ["DependencyGraph"]), + .library(name: "LogHandlers", targets: ["LogHandlers"]), + .executable(name: "gen-ir", targets: ["gen-ir"]) + ], dependencies: [ // Dependencies declare other packages that this package depends on. // .package(url: /* package url */, from: "1.0.0"), @@ -19,12 +24,27 @@ let package = Package( targets: [ // Targets are the basic building blocks of a package. A target can define a module or a test suite. // Targets can depend on other targets in this package, and on products in packages this package depends on. + .target( + name: "LogHandlers", + dependencies: [ + .product(name: "Logging", package: "swift-log") + ] + ), + .target( + name: "DependencyGraph", + dependencies: [ + .product(name: "Logging", package: "swift-log"), + .target(name: "LogHandlers") + ] + ), .executableTarget( name: "gen-ir", dependencies: [ .product(name: "ArgumentParser", package: "swift-argument-parser"), .product(name: "Logging", package: "swift-log"), - .product(name: "PIFSupport", package: "PIF") + .product(name: "PIFSupport", package: "PIF"), + .target(name: "DependencyGraph"), + .target(name: "LogHandlers") ], path: "Sources/GenIR" ), diff --git a/Sources/GenIR/Dependency Graph/DependencyGraph.swift b/Sources/DependencyGraph/DependencyGraph.swift similarity index 90% rename from Sources/GenIR/Dependency Graph/DependencyGraph.swift rename to Sources/DependencyGraph/DependencyGraph.swift index f6107f3..25296fd 100644 --- a/Sources/GenIR/Dependency Graph/DependencyGraph.swift +++ b/Sources/DependencyGraph/DependencyGraph.swift @@ -6,11 +6,12 @@ // import Foundation +import LogHandlers /// A directed graph that maps dependencies between values (nodes) via edges (directions between nodes) -class DependencyGraph { +public class DependencyGraph { /// All the nodes in the graph - private(set) var nodes = [String: Node]() + private(set) public var nodes = [String: Node]() /// Adds a node for the associated value to the graph /// - Parameter value: the value associated with the node @@ -28,14 +29,14 @@ class DependencyGraph { /// Finds the node associated with a value /// - Parameter value: the value to look for /// - Returns: the node for which the value is associated, if found - func findNode(for value: Value) -> Node? { + public func findNode(for value: Value) -> Node? { nodes[value.valueName] } /// Returns the dependency 'chain' for the value associated with a node in the graph using a depth-first search /// - Parameter value: the associated value for a node to start the search with /// - Returns: the chain of nodes, starting with the 'bottom' of the dependency subgraph - func chain(for value: Value) -> [Node] { + public func chain(for value: Value) -> [Node] { guard let node = findNode(for: value) else { logger.debug("Couldn't find node for value: \(value.valueName)") return [] @@ -77,7 +78,7 @@ class DependencyGraph { /// Writes a 'dot' graph file to disk /// - Parameter path: the path to write the graph to - func toDot(_ path: String) throws { + public func toDot(_ path: String) throws { var contents = "digraph DependencyGraph {\n" for node in nodes.values { @@ -92,7 +93,7 @@ class DependencyGraph { } extension DependencyGraph: CustomStringConvertible { - var description: String { + public var description: String { var description = "" nodes diff --git a/Sources/GenIR/Dependency Graph/DependencyGraphBuilder.swift b/Sources/DependencyGraph/DependencyGraphBuilder.swift similarity index 84% rename from Sources/GenIR/Dependency Graph/DependencyGraphBuilder.swift rename to Sources/DependencyGraph/DependencyGraphBuilder.swift index 429c0a2..74ee422 100644 --- a/Sources/GenIR/Dependency Graph/DependencyGraphBuilder.swift +++ b/Sources/DependencyGraph/DependencyGraphBuilder.swift @@ -5,8 +5,10 @@ // Created by Thomas Hedderwick on 28/08/2023. // +import LogHandlers + /// A type that provides dependency relationships between values -protocol DependencyProviding { +public protocol DependencyProviding { /// A type that represents the value of a node associatedtype Value: NodeValue @@ -17,21 +19,21 @@ protocol DependencyProviding { } /// A builder for the DependencyGraph - you should _always_ use this class to build out the `DependencyGraph` -class DependencyGraphBuilder where Value == Provider.Value { +public class DependencyGraphBuilder where Value == Provider.Value { /// The graph the builder will operate on - typealias Graph = DependencyGraph + public typealias Graph = DependencyGraph /// The dependency provider private let provider: Provider /// The built graph - let graph = Graph() + public let graph = Graph() /// Inits the Graph Builder /// - Parameters: /// - provider: the dependency provider for the values /// - values: the values to add to the graph - init(provider: Provider, values: [Value]) { + public init(provider: Provider, values: [Value]) { self.provider = provider values.forEach { add(value: $0) } } diff --git a/Sources/GenIR/Dependency Graph/Edge.swift b/Sources/DependencyGraph/Edge.swift similarity index 72% rename from Sources/GenIR/Dependency Graph/Edge.swift rename to Sources/DependencyGraph/Edge.swift index c9d8d41..595f57d 100644 --- a/Sources/GenIR/Dependency Graph/Edge.swift +++ b/Sources/DependencyGraph/Edge.swift @@ -9,16 +9,17 @@ /// An edge describes the relationship between two Nodes in a graph extension DependencyGraph { - class Edge { + /// An edge represents the connection between two nodes in the graph + public class Edge { /// The source node - let to: DependencyGraph.Node + public let to: DependencyGraph.Node /// The destination node - let from: DependencyGraph.Node + public let from: DependencyGraph.Node /// The relationship between the two nodes - let relationship: Relationship + public let relationship: Relationship /// Description of the relationships between two nodes - enum Relationship { + public enum Relationship { /// From depends on To case dependency /// From is a depender of To @@ -39,13 +40,13 @@ extension DependencyGraph { } extension DependencyGraph.Edge: Equatable { - static func == (_ lhs: DependencyGraph.Edge, rhs: DependencyGraph.Edge) -> Bool { + public static func == (_ lhs: DependencyGraph.Edge, rhs: DependencyGraph.Edge) -> Bool { lhs.to == rhs.to && lhs.from == rhs.from && lhs.relationship == rhs.relationship } } extension DependencyGraph.Edge: Hashable { - func hash(into hasher: inout Hasher) { + public func hash(into hasher: inout Hasher) { hasher.combine(to) hasher.combine(from) hasher.combine(relationship) @@ -53,6 +54,6 @@ extension DependencyGraph.Edge: Hashable { } extension DependencyGraph.Edge: CustomStringConvertible { - var description: String { "[Edge from \(from) to \(to) relationship: \(relationship)]"} + public var description: String { "[Edge from \(from) to \(to) relationship: \(relationship)]"} } // swiftlint:enable identifier_name diff --git a/Sources/GenIR/Dependency Graph/Node.swift b/Sources/DependencyGraph/Node.swift similarity index 74% rename from Sources/GenIR/Dependency Graph/Node.swift rename to Sources/DependencyGraph/Node.swift index cf94ac8..45f8456 100644 --- a/Sources/GenIR/Dependency Graph/Node.swift +++ b/Sources/DependencyGraph/Node.swift @@ -7,21 +7,22 @@ import Foundation -protocol NodeValue: Hashable { +public protocol NodeValue: Hashable { /// The name of this node, this should be unique var valueName: String { get } } extension DependencyGraph { - class Node { + /// A node holds an associated value and edges to other nodes in the graph + public class Node { /// The edges from and to this node - private(set) var edges = [DependencyGraph.Edge]() + private(set) public var edges = [DependencyGraph.Edge]() /// The associated value of this node - let value: Value + public let value: Value /// The name of this node - var valueName: String { + public var valueName: String { value.valueName } @@ -42,13 +43,13 @@ extension DependencyGraph { } extension DependencyGraph.Node: Equatable { - static func == (_ lhs: DependencyGraph.Node, rhs: DependencyGraph.Node) -> Bool { + public static func == (_ lhs: DependencyGraph.Node, rhs: DependencyGraph.Node) -> Bool { lhs.value == rhs.value && lhs.edges == rhs.edges } } extension DependencyGraph.Node: CustomStringConvertible { - var description: String { + public var description: String { var description = "" if !edges.isEmpty { @@ -62,7 +63,7 @@ extension DependencyGraph.Node: CustomStringConvertible { } extension DependencyGraph.Node: Hashable { - func hash(into hasher: inout Hasher) { + public func hash(into hasher: inout Hasher) { hasher.combine(valueName) hasher.combine(value) } diff --git a/Sources/GenIR/BuildCacheManipulator.swift b/Sources/GenIR/BuildCacheManipulator.swift index cc18684..e8f8489 100644 --- a/Sources/GenIR/BuildCacheManipulator.swift +++ b/Sources/GenIR/BuildCacheManipulator.swift @@ -1,4 +1,5 @@ import Foundation +import LogHandlers /// Manipulates the build cache, if and when needed, to fix up potential invalidations struct BuildCacheManipulator { @@ -78,7 +79,7 @@ struct BuildCacheManipulator { .appendingPathComponent("BuildProductsPath") guard - let archivePath = Self.findConfigurationDirectory(intermediatesBuildPath) + let archivePath = findConfigurationDirectory(intermediatesBuildPath) else { throw Error.directoryNotFound( "Couldn't find or determine a build configuration directory (expected inside of: \(intermediatesBuildPath))" @@ -134,7 +135,7 @@ struct BuildCacheManipulator { /// Tries to find the xcode build configuration directory path inside the given path /// - Parameter path: the path to search /// - Returns: the path to the build configuration directory, if found - private static func findConfigurationDirectory(_ path: URL) -> URL? { + private func findConfigurationDirectory(_ path: URL) -> URL? { let folders = (try? FileManager.default.directories(at: path, recursive: false)) ?? [] guard folders.count != 0 else { diff --git a/Sources/GenIR/CompilerCommandRunner.swift b/Sources/GenIR/CompilerCommandRunner.swift index b0c3657..087affc 100644 --- a/Sources/GenIR/CompilerCommandRunner.swift +++ b/Sources/GenIR/CompilerCommandRunner.swift @@ -6,7 +6,7 @@ // import Foundation -import Logging +import LogHandlers /// A model of the contents of an output file map json typealias OutputFileMap = [String: [String: String]] diff --git a/Sources/GenIR/GenIR.swift b/Sources/GenIR/GenIR.swift index 75e13d2..80cf6e6 100644 --- a/Sources/GenIR/GenIR.swift +++ b/Sources/GenIR/GenIR.swift @@ -2,16 +2,14 @@ import Foundation import ArgumentParser import Logging import PIFSupport - -/// Global logger object -var logger = Logger(label: Bundle.main.bundleIdentifier ?? "com.veracode.gen-ir", factory: StdOutLogHandler.init) +import DependencyGraph +import LogHandlers /// The name of the program let programName = CommandLine.arguments.first! /// Command to emit LLVM IR from an Xcode build log -@main -struct IREmitterCommand: ParsableCommand { +@main struct IREmitterCommand: ParsableCommand { static let configuration = CommandConfiguration( commandName: "", abstract: "Consumes an Xcode build log, and outputs LLVM IR, in the bitstream format, to the folder specified", @@ -175,8 +173,6 @@ struct IREmitterCommand: ParsableCommand { /// Reads stdin until an EOF is found /// - Returns: An array of Strings representing stdin split by lines private func readStdin() throws -> [String] { - logger.info("Collating input via pipe") - var results = [String]() while let line = readLine() { @@ -194,8 +190,6 @@ struct IREmitterCommand: ParsableCommand { print("\n\n") } - logger.info("Finished reading from pipe") - return results } } diff --git a/Sources/GenIR/OutputPostprocessor.swift b/Sources/GenIR/OutputPostprocessor.swift index 6c7f754..7a9d2b7 100644 --- a/Sources/GenIR/OutputPostprocessor.swift +++ b/Sources/GenIR/OutputPostprocessor.swift @@ -6,6 +6,8 @@ // import Foundation +import DependencyGraph +import LogHandlers /// The `OutputPostprocessor` is responsible for trying to match the IR output of the `CompilerCommandRunner` with the products in the `xcarchive`. /// The `CompilerCommandRunner` will output IR with it's product name, but doesn't take into account the linking of products into each other. @@ -16,7 +18,9 @@ class OutputPostprocessor { let archive: URL /// Mapping of dynamic dependencies (inside the xcarchive) to their paths on disk - private let dynamicDependencyToPath: [String: URL] + private lazy var dynamicDependencyToPath: [String: URL] = { + dynamicDependencies(in: archive) + }() /// A dependency graph containing the targets in the output archive private let graph: DependencyGraph @@ -25,23 +29,13 @@ class OutputPostprocessor { private let targets: [Target] /// A mapping of targets to their path on disk - private let targetsToPaths: [Target: URL] - - /// The manager to use for file system access - private let manager: FileManager = .default - - /// Initializes the postprocessor - init(archive: URL, output: URL, graph: DependencyGraph) throws { - self.archive = archive - self.graph = graph - self.targets = graph.nodes.map { $0.value.value } - + private lazy var targetsToPaths: [Target: URL] = { let namesToTargets = targets .reduce(into: [String: Target]()) { partial, target in partial[target.productName] = target } - targetsToPaths = try manager + return (try? manager .directories(at: output, recursive: false) .reduce(into: [Target: URL]()) { partial, path in if let target = namesToTargets[path.lastPathComponent] { @@ -49,15 +43,21 @@ class OutputPostprocessor { } else { logger.error("Path (\(path.lastPathComponent)) wasn't found in namesToTargets: \(namesToTargets)") } - } + }) ?? [:] + }() - let namedTargetSet = Set(namesToTargets.values) - let pathTargetSet = Set(targetsToPaths.keys) - let difference = namedTargetSet.symmetricDifference(pathTargetSet) + /// Path to the IR output folder + private let output: URL - logger.debug("target set difference: \(difference)") + /// The manager to use for file system access + private let manager: FileManager = .default - dynamicDependencyToPath = dynamicDependencies(in: self.archive) + /// Initializes the postprocessor + init(archive: URL, output: URL, graph: DependencyGraph) throws { + self.archive = archive + self.output = output + self.graph = graph + self.targets = graph.nodes.map { $0.value.value } } /// Starts the OutputPostprocessor @@ -188,64 +188,64 @@ class OutputPostprocessor { return } - seenConflictingFiles[source]!.append((destinationSize, destinationCreatedDate)) + seenConflictingFiles[source, default: [(sourceSize, sourceCreatedDate)]].append((destinationSize, destinationCreatedDate)) logger.debug("Copying source \(source) to destination: \(uniqueDestinationURL)") try manager.copyItem(at: source, to: uniqueDestinationURL) } -} -/// Returns a map of dynamic objects in the provided path -/// - Parameter xcarchive: the path to search through -/// - Returns: a mapping of filename to filepath for dynamic objects in the provided path -private func dynamicDependencies(in xcarchive: URL) -> [String: URL] { - let searchPath = baseSearchPath(startingAt: xcarchive) - logger.debug("Using search path for dynamic dependencies: \(searchPath)") - - let dynamicDependencyExtensions = ["framework", "appex", "app"] - - return FileManager.default.filteredContents(of: searchPath, filter: { path in - return dynamicDependencyExtensions.contains(path.pathExtension) - }) - .reduce(into: [String: URL]()) { partialResult, path in - // HACK: For now, insert both with an without extension to avoid any potential issues - partialResult[path.deletingPathExtension().lastPathComponent] = path - partialResult[path.lastPathComponent] = path + /// Returns a map of dynamic objects in the provided path + /// - Parameter xcarchive: the path to search through + /// - Returns: a mapping of filename to filepath for dynamic objects in the provided path + private func dynamicDependencies(in xcarchive: URL) -> [String: URL] { + let searchPath = baseSearchPath(startingAt: xcarchive) + logger.debug("Using search path for dynamic dependencies: \(searchPath)") + + let dynamicDependencyExtensions = ["framework", "appex", "app"] + + return FileManager.default.filteredContents(of: searchPath, filter: { path in + return dynamicDependencyExtensions.contains(path.pathExtension) + }) + .reduce(into: [String: URL]()) { partialResult, path in + // HACK: For now, insert both with an without extension to avoid any potential issues + partialResult[path.deletingPathExtension().lastPathComponent] = path + partialResult[path.lastPathComponent] = path + } } -} -/// Returns the base URL to start searching inside an xcarchive -/// - Parameter path: the original path, should be an xcarchive -/// - Returns: the path to start a dependency search from -private func baseSearchPath(startingAt path: URL) -> URL { - let productsPath = path.appendingPathComponent("Products") - let applicationsPath = productsPath.appendingPathComponent("Applications") - let frameworkPath = productsPath.appendingPathComponent("Library").appendingPathComponent("Framework") - - /// Returns the first directory found at the given path - /// - Parameter path: the path to search for directories - /// - Returns: the first directory found in the path if one exists - func firstDirectory(at path: URL) -> URL? { - guard - FileManager.default.directoryExists(at: path), - let contents = try? FileManager.default.directories(at: path, recursive: false), - contents.count < 0 - else { - return nil - } + /// Returns the base URL to start searching inside an xcarchive + /// - Parameter path: the original path, should be an xcarchive + /// - Returns: the path to start a dependency search from + private func baseSearchPath(startingAt path: URL) -> URL { + let productsPath = path.appendingPathComponent("Products") + let applicationsPath = productsPath.appendingPathComponent("Applications") + let frameworkPath = productsPath.appendingPathComponent("Library").appendingPathComponent("Framework") + + /// Returns the first directory found at the given path + /// - Parameter path: the path to search for directories + /// - Returns: the first directory found in the path if one exists + func firstDirectory(at path: URL) -> URL? { + guard + FileManager.default.directoryExists(at: path), + let contents = try? FileManager.default.directories(at: path, recursive: false), + contents.count < 0 + else { + return nil + } - if contents.count > 1 { - logger.debug("Expected one folder at: \(path). Found \(contents.count). Selecting \(contents.first!)") - } + if contents.count > 1 { + logger.debug("Expected one folder at: \(path). Found \(contents.count). Selecting \(contents.first!)") + } - return contents.first! - } + return contents.first! + } - for path in [applicationsPath, frameworkPath] { - if let directory = firstDirectory(at: path) { - return directory + for path in [applicationsPath, frameworkPath] { + if let directory = firstDirectory(at: path) { + return directory + } } - } - logger.debug("Couldn't determine the base search path for the xcarchive, using: \(productsPath)") - return productsPath + logger.debug("Couldn't determine the base search path for the xcarchive, using: \(productsPath)") + return productsPath + } } diff --git a/Sources/GenIR/PIFCache.swift b/Sources/GenIR/PIFCache.swift index 8fb6760..9783a89 100644 --- a/Sources/GenIR/PIFCache.swift +++ b/Sources/GenIR/PIFCache.swift @@ -1,5 +1,7 @@ import Foundation import PIFSupport +import DependencyGraph +import LogHandlers /// The PIFCache provides a wrapper around a `PIF.Workspace`. /// It includes a set of helper functions to make operating on the PIF Cache structures easier. @@ -85,7 +87,7 @@ class PIFCache { case let file as PIF.FileReference: result.append(file) case let group as PIF.Group: - resolveChildren(starting: group.children, result: &result) + resolveChildren(starting: group.children) default: logger.debug("Unhandled reference type: \(child)") } diff --git a/Sources/GenIR/StdoutLogHandler.swift b/Sources/GenIR/StdoutLogHandler.swift deleted file mode 100644 index f95a688..0000000 --- a/Sources/GenIR/StdoutLogHandler.swift +++ /dev/null @@ -1,82 +0,0 @@ -// -// File.swift -// -// -// Created by Thomas Hedderwick on 30/08/2022. -// - -import Foundation -import Logging - -struct StdOutLogHandler: LogHandler { - subscript(metadataKey key: String) -> Logging.Logger.Metadata.Value? { - get { - metadata[key] - } - set(newValue) { - metadata[key] = newValue - } - } - - var metadata: Logging.Logger.Metadata = [:] - - var logLevel: Logging.Logger.Level = .info - - init(_: String) { } - - // swiftlint:disable function_parameter_count - // periphery:ignore:parameters source - func log( - level: Logger.Level, - message: Logger.Message, - metadata: Logger.Metadata?, - source: String, - file: String, - function: String, - line: UInt - ) { - let levelPrefix = prefix(for: level) - let timestamp = timestamp(for: level) - let lineInfo = lineInfo(for: level, file: file, function: function, line: line) - - print("\(timestamp)\(lineInfo)\(levelPrefix)\(message)") - } - // swiftlint:enable function_parameter_count - - private func prefix(for level: Logger.Level) -> String { - switch level { - case .trace: - return "[TRACE] " - case .debug: - return "[DEBUG] " - case .info: - return "" - case .notice: - return "[~] " - case .warning: - return "[~] " - case .error: - return "[!] " - case .critical: - return "[!!!] " - } - } - - private func timestamp(for level: Logger.Level) -> String { - switch level { - case .trace, .debug, .notice, .warning, .error, .critical: - return "\(Date.now) " - case .info: - return "" - } - } - - private func lineInfo(for level: Logger.Level, file: String, function: String, line: UInt) -> String { - switch level { - case .trace, .debug, .notice, .warning, .error, .critical: - return "[\(file):\(line) \(function)] " - case .info: - return "" - } - } -} diff --git a/Sources/GenIR/Target.swift b/Sources/GenIR/Target.swift index 0453d1f..ed018d6 100644 --- a/Sources/GenIR/Target.swift +++ b/Sources/GenIR/Target.swift @@ -7,6 +7,7 @@ import Foundation import PIFSupport +import DependencyGraph /// A Target represents a product to build (app, framework, plugin, package) class Target { diff --git a/Sources/GenIR/XcodeLogParser.swift b/Sources/GenIR/XcodeLogParser.swift index 3f07a34..4f26553 100644 --- a/Sources/GenIR/XcodeLogParser.swift +++ b/Sources/GenIR/XcodeLogParser.swift @@ -6,7 +6,7 @@ // import Foundation -import Logging +import LogHandlers /// An XcodeLogParser extracts targets and their compiler commands from a given Xcode build log class XcodeLogParser { diff --git a/Sources/LogHandlers/.gitignore b/Sources/LogHandlers/.gitignore new file mode 100644 index 0000000..0023a53 --- /dev/null +++ b/Sources/LogHandlers/.gitignore @@ -0,0 +1,8 @@ +.DS_Store +/.build +/Packages +xcuserdata/ +DerivedData/ +.swiftpm/configuration/registries.json +.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata +.netrc diff --git a/Sources/LogHandlers/Sources/LogHandlers/StdoutLogHandler.swift b/Sources/LogHandlers/Sources/LogHandlers/StdoutLogHandler.swift new file mode 100644 index 0000000..1cd36d3 --- /dev/null +++ b/Sources/LogHandlers/Sources/LogHandlers/StdoutLogHandler.swift @@ -0,0 +1,101 @@ +// +// StdoutLogHandler.swift +// +// +// Created by Thomas Hedderwick on 30/08/2022. +// + +import Foundation +import Logging + +/// All module logger (yes because swift-log hasn't really solved for setting level globally without passing an instance around everywhere) +public var logger = Logger(label: "LogHandler", factory: StdIOStreamLogHandler.init) + +struct StdIOTextStream: TextOutputStream { + static let stdout = StdIOTextStream(file: Darwin.stdout) + static let stderr = StdIOTextStream(file: Darwin.stderr) + + let file: UnsafeMutablePointer + + func write(_ string: String) { + var string = string + string.makeContiguousUTF8() + string.utf8.withContiguousStorageIfAvailable { bytes in + flockfile(file) + defer { funlockfile(file) } + + fwrite(bytes.baseAddress!, 1, bytes.count, file) + + fflush(file) + } + } +} + +public struct StdIOStreamLogHandler: LogHandler { + internal typealias SendableTextOutputStream = TextOutputStream & Sendable + + private let stdout = StdIOTextStream.stdout + + public subscript(metadataKey key: String) -> Logging.Logger.Metadata.Value? { + get { metadata[key] } + set(newValue) { metadata[key] = newValue } + } + + public var metadata: Logging.Logger.Metadata = [:] + public var logLevel: Logging.Logger.Level = .info + + public init() {} + + public init(_: String) {} + + // periphery:ignore:parameters count + // swiftlint:disable:next function_parameter_count + public func log( + level: Logger.Level, + message: Logger.Message, + metadata: Logger.Metadata?, + source: String, + file: String, + function: String, + line: UInt + ) { + let lineInfo = lineInfo(for: level, file: file, function: function, line: line) + + stdout.write("\(timestamp)\(lineInfo)\(levelPrefix)\(message)\n") + } + + private var levelPrefix: String { + switch logLevel { + case .trace: + return "[TRACE] " + case .debug: + return "[DEBUG] " + case .info: + return "" + case .notice, .warning: + return "[~] " + case .error: + return "[!] " + case .critical: + return "[!!!] " + } + } + + private var timestamp: String { + switch logLevel { + case .trace, .debug, .notice, .warning, .error, .critical: + return "\(Date.now) " + case .info: + return "" + } + } + + private func lineInfo(for level: Logger.Level, file: String, function: String, line: UInt) -> String { + switch level { + case .trace, .debug, .notice, .warning, .error, .critical: + return "[\(file):\(line) \(function)] " + case .info: + return "" + } + } +} diff --git a/Tests/GenIRTests/DependencyGraphTests.swift b/Tests/GenIRTests/DependencyGraphTests.swift index 2fee28d..d0d4ef2 100644 --- a/Tests/GenIRTests/DependencyGraphTests.swift +++ b/Tests/GenIRTests/DependencyGraphTests.swift @@ -1,5 +1,6 @@ import XCTest @testable import gen_ir +@testable import DependencyGraph final class DependencyGraphTests: XCTestCase { let testPath: URL = { diff --git a/Tests/GenIRTests/TestContext.swift b/Tests/GenIRTests/TestContext.swift index a104aac..40f7230 100644 --- a/Tests/GenIRTests/TestContext.swift +++ b/Tests/GenIRTests/TestContext.swift @@ -1,5 +1,6 @@ import Foundation @testable import gen_ir +import DependencyGraph import XCTest /// TestContext is a grouping of convenance functions and a context to ease the burden of testing Gen IR From a942a8ed726c0014016fa298537d2b9d14b93fd5 Mon Sep 17 00:00:00 2001 From: bmxav <5422569+bmxav@users.noreply.github.com> Date: Wed, 10 Jul 2024 10:47:17 -0400 Subject: [PATCH 17/21] Specialize the responsibilities of some Gen-IR constructs. This attempts to more accurately define what Gen-IR's idea of a target is, which is a bit different than the PIF representation. Not every target is considered buildable, so we also split out the storage of commands and pass them directly to CompilerCommandRunner. --- Sources/GenIR/CompilerCommandRunner.swift | 33 ++++------ Sources/GenIR/GenIR.swift | 12 ++-- Sources/GenIR/OutputPostprocessor.swift | 78 +++++++++++++++-------- Sources/GenIR/PIFCache.swift | 34 ++++++---- Sources/GenIR/Target.swift | 77 +++++++++++----------- 5 files changed, 133 insertions(+), 101 deletions(-) diff --git a/Sources/GenIR/CompilerCommandRunner.swift b/Sources/GenIR/CompilerCommandRunner.swift index 087affc..ae304ba 100644 --- a/Sources/GenIR/CompilerCommandRunner.swift +++ b/Sources/GenIR/CompilerCommandRunner.swift @@ -17,6 +17,11 @@ typealias OutputFileMap = [String: [String: String]] /// /// > clang will emit LLVM BC to the current working directory in a named file. In this case, the runner will move the files from temporary storage to the output location struct CompilerCommandRunner { + enum Error: Swift.Error { + /// Command runner failed to parse the command for the required information + case failedToParse(String) + } + /// The directory to place the LLVM BC output private let output: URL @@ -26,11 +31,6 @@ struct CompilerCommandRunner { /// Manager used to access the file system private let fileManager = FileManager.default - enum Error: Swift.Error { - /// Command runner failed to parse the command for the required information - case failedToParse(String) - } - /// Run without running the commands private let dryRun: Bool @@ -47,31 +47,26 @@ struct CompilerCommandRunner { /// Starts the runner /// - Parameter targets: the targets holding the commands to run - func run(targets: [Target]) throws { + func run(targets: [Target], commands: [String: [CompilerCommand]]) throws { // Quick, do a hack! try buildCacheManipulator.manipulate() - let tempDirectory = try fileManager.temporaryDirectory(named: "gen-ir-\(UUID().uuidString)") - defer { try? fileManager.removeItem(at: tempDirectory) } - logger.debug("Using temp directory as working directory: \(tempDirectory.filePath)") - - let totalCommands = targets - .map { $0.commands.count } - .reduce(0, +) - + let totalCommands = commands.reduce(0) { $0 + $1.value.count } logger.info("Total commands to run: \(totalCommands)") var totalModulesRun = 0 - for target in targets { + for target in targets.filter({ $0.containsBitcode }) { + guard let targetCommands = commands[target.name] else { + continue + } + logger.info("Operating on target: \(target.name). Total modules processed: \(totalModulesRun)") - totalModulesRun += try run(commands: target.commands, for: target.productName, at: tempDirectory) + totalModulesRun += try run(commands: targetCommands, for: target.productName, at: output) } - try fileManager.moveItemReplacingExisting(from: tempDirectory, to: output) - - let uniqueModules = try fileManager.files(at: output, withSuffix: ".bc").count + let uniqueModules = Set(try fileManager.files(at: output, withSuffix: ".bc")).count logger.info("Finished compiling all targets. Unique modules: \(uniqueModules)") } diff --git a/Sources/GenIR/GenIR.swift b/Sources/GenIR/GenIR.swift index 80cf6e6..0c2d6af 100644 --- a/Sources/GenIR/GenIR.swift +++ b/Sources/GenIR/GenIR.swift @@ -112,7 +112,7 @@ let programName = CommandLine.arguments.first! // Find and parse the PIF cache let pifCache = try PIFCache(buildCache: log.buildCachePath) - let targets = Target.targets(from: pifCache.targets, with: log.targetCommands) + let targets = pifCache.targets.map { Target(from: $0) } let builder = DependencyGraphBuilder( provider: .init(targets: targets, cache: pifCache), @@ -139,16 +139,20 @@ let programName = CommandLine.arguments.first! dryRun: dryRun ) + let tempDirectory = try FileManager.default.temporaryDirectory(named: "gen-ir-\(UUID().uuidString)") + defer { try? FileManager.default.removeItem(at: tempDirectory) } + logger.info("Using temp directory as working directory: \(tempDirectory)") + let runner = CompilerCommandRunner( - output: output, + output: tempDirectory, buildCacheManipulator: buildCacheManipulator, dryRun: dryRun ) - try runner.run(targets: targets) + try runner.run(targets: targets, commands: log.targetCommands) let postprocessor = try OutputPostprocessor( archive: archive, - output: output, + output:tempDirectory, graph: graph ) diff --git a/Sources/GenIR/OutputPostprocessor.swift b/Sources/GenIR/OutputPostprocessor.swift index 7a9d2b7..a68ee15 100644 --- a/Sources/GenIR/OutputPostprocessor.swift +++ b/Sources/GenIR/OutputPostprocessor.swift @@ -28,6 +28,12 @@ class OutputPostprocessor { /// The targets in this archive private let targets: [Target] + private lazy var guidToTargets: [String: Target] = { + targets.reduce(into: [String: Target]()) { partial, target in + partial[target.guid] = target + } + }() + /// A mapping of targets to their path on disk private lazy var targetsToPaths: [Target: URL] = { let namesToTargets = targets @@ -63,58 +69,74 @@ class OutputPostprocessor { /// Starts the OutputPostprocessor /// - Parameter targets: the targets to operate on func process() throws { - // TODO: remove 'static' deps so we don't duplicate them in the submission? + var processed: Set = [] + _ = try targets - .map { try process(target: $0) } + .filter { $0.isPackageProduct } + .map { try process(target: $0, processed: &processed) } + + logger.info("Processed \(processed.count) top-level IR targets") + + try copyToArchive() } /// Processes an individual target /// - Parameters: /// - target: the target to process /// - Returns: - private func process(target: Target) throws -> Set { - // TODO: we need better handling of swift package products and targets in the dependency graph or we fail to move dependencies here + private func process(target: Target, processed: inout Set) throws { + guard processed.insert(target).inserted else { + return + } + + let targetDirectory = output.appendingPathComponent(target.productName) + + // This directory may already exist if the `CompilerCommandRunner` has already built an + // artifact at this location. + try? FileManager.default.createDirectory(at: targetDirectory, withIntermediateDirectories: false) + let chain = graph.chain(for: target) logger.debug("Chain for target: \(target.productName):\n\(chain.map { "\($0)\n" })") chain.forEach { logger.debug("\($0)") } - // We want to process the chain, visiting each node _shallowly_ and copy it's dependencies into it's parent - var processed = Set() - for node in chain { logger.debug("Processing Node: \(node.valueName)") // Ensure node is not a dynamic dependency guard dynamicDependencyToPath[node.value.productName] == nil else { continue } - // Only care about moving dependencies into dependers - check this node's edges to dependent relationships - let dependers = node.edges - .filter { $0.relationship == .depender } - .map { $0.to } + try process(target: node.value, processed: &processed) + + let nodeFolderPath = output.appendingPathComponent(node.value.productName) - // Move node's IR into depender's IR folder - guard let nodeFolderPath = targetsToPaths[node.value] else { - logger.debug("IR folder for node: \(node) is nil") - continue + do { + try copyContentsOfDirectoryMergingDifferingFiles(at: nodeFolderPath, to: targetDirectory) + } catch { + logger.debug("Copy error: \(error)") } + } + } - for depender in dependers { - guard let dependerFolderPath = targetsToPaths[depender.value] else { - logger.debug("IR folder for depender node \(depender) is nil") - continue - } + private func copyToArchive() throws { + let irDirectory = archive.appendingPathComponent("IR") + try? FileManager.default.createDirectory(at: irDirectory, withIntermediateDirectories: false) + + let nodes = targets.compactMap { graph.findNode(for: $0) } + + for node in nodes { + let dependers = node.edges.filter { $0.relationship == .depender } + let targetPath = output.appendingPathComponent(node.value.productName) + let archivePath = irDirectory.appendingPathComponent(node.value.productName) - // Move the dependency IR (the node) to the depender (the thing depending on this node) - do { - try copyContentsOfDirectoryMergingDifferingFiles(at: nodeFolderPath, to: dependerFolderPath) - } catch { - logger.debug("Copy error: \(error)") + if dynamicDependencyToPath[node.value.productName] != nil || (dependers.count == 0 && !node.value.isSwiftPackage) { + logger.debug("Copying \(node.value.productName) with name \(node.value.productName)") + if !FileManager.default.directoryExists(at: targetPath) { + logger.debug("No target found for target \(node.value.guid) with name \(node.value.productName)") + continue } - processed.insert(nodeFolderPath) + try FileManager.default.copyItem(at: targetPath, to: archivePath) } } - - return processed } // TODO: Write tests for this. diff --git a/Sources/GenIR/PIFCache.swift b/Sources/GenIR/PIFCache.swift index 9783a89..b8e4c11 100644 --- a/Sources/GenIR/PIFCache.swift +++ b/Sources/GenIR/PIFCache.swift @@ -19,11 +19,10 @@ class PIFCache { } /// All targets contained by the workspace - var targets: [PIF.BaseTarget] { - workspace - .projects - .flatMap { $0.targets } - } + let targets: [PIF.BaseTarget] + + /// Maps GUIDs to their respective targets for easy lookup. + private let guidToTargets: [PIF.GUID: PIF.BaseTarget] enum Error: Swift.Error { case nonexistentCache(String) @@ -33,7 +32,7 @@ class PIFCache { /// Initializes the PIF Cache from a build cache /// - Parameter buildCache: path to the Xcode DerivedData Build Cache init(buildCache: URL) throws { - self.pifCachePath = try Self.pifCachePath(in: buildCache) + pifCachePath = try Self.pifCachePath(in: buildCache) do { let cache = try PIFCacheParser(cachePath: pifCachePath, logger: logger) @@ -41,6 +40,15 @@ class PIFCache { } catch { throw Error.pifError(error) } + + targets = workspace.projects.flatMap { $0.targets } + guidToTargets = targets.reduce(into: [PIF.GUID: PIF.BaseTarget]()) { partial, target in + partial[target.guid] = target + } + } + + func target(guid: PIF.GUID) -> PIF.BaseTarget? { + guidToTargets[guid] } /// Finds the PIF Cache in the Xcode Build Cache. This can vary depending on the build system used. @@ -151,7 +159,7 @@ struct PIFDependencyProvider: DependencyProviding { self.guidToTargets = targets .reduce(into: [PIF.GUID: Target]()) { partial, target in - partial[target.baseTarget.guid] = target + partial[target.guid] = target } } @@ -176,10 +184,10 @@ struct PIFDependencyProvider: DependencyProviding { let productName = String(packageProductGUID.dropFirst(productToken.count)) - let productTargetDependencies = product - .baseTarget + let productTargetDependencies = cache.target(guid: product.guid)? .dependencies .filter { $0.targetGUID.starts(with: targetToken) } + ?? [] let productUnderlyingTargets = productTargetDependencies .filter { $0.targetGUID.dropFirst(targetToken.count) == productName } @@ -205,17 +213,16 @@ struct PIFDependencyProvider: DependencyProviding { func dependencies(for value: Target) -> [Target] { // Direct dependencies - let dependencyTargetGUIDs = value - .baseTarget + let dependencyTargetGUIDs = cache.target(guid: value.guid)? .dependencies .map { $0.targetGUID } .compactMap { resolveSwiftPackage($0) } + ?? [] // Framework build phase dependencies // NOTE: Previously we just cast this - all of a sudden with pods this is broken // Not the end of the world - just as quick to do a dictionary lookup - let frameworkGUIDs = value - .baseTarget + let frameworkGUIDs = cache.target(guid: value.guid)? .buildPhases .flatMap { $0.buildFiles } // .compactMap { $0 as? PIF.FrameworksBuildPhase } @@ -226,6 +233,7 @@ struct PIFDependencyProvider: DependencyProviding { } } .compactMap { cache.frameworks[$0]?.guid } + ?? [] let dependencyTargets = (dependencyTargetGUIDs + frameworkGUIDs).compactMap { guidToTargets[$0] } diff --git a/Sources/GenIR/Target.swift b/Sources/GenIR/Target.swift index ed018d6..e9ce651 100644 --- a/Sources/GenIR/Target.swift +++ b/Sources/GenIR/Target.swift @@ -9,50 +9,53 @@ import Foundation import PIFSupport import DependencyGraph -/// A Target represents a product to build (app, framework, plugin, package) +/// Represents a product to build (app, framework, plugin, package). It contains the identifying +/// information about a target and its output when it is built or archived. In the future the +/// `PIF` package will likely be modified so that it is usable within the context of Gen-IR +/// directly. class Target { - /// The name of the target - var name: String { baseTarget.name } + /// Target identifier. + let guid: String + /// Name of the target. + let name: String + /// The product name refers to the output of this target when it is built or copied into an archive. + let productName: String - /// The product name of the target, if one is available, otherwise the name - /// This can happen when the product is not directly buildable (such as a package product or aggregate) - var productName: String { - if let target = baseTarget as? PIF.Target, !target.productName.isEmpty { - return target.productName - } - - return baseTarget.name + /// Returns true if this target produces bitcode. This is used to determine if this target is buildable. + /// For packages we only use the `PACKAGE-TARGET` for the object file, since this is used by the + /// other targets. + var containsBitcode: Bool { + get { guid == "PACKAGE-TARGET:\(name)" || !guid.hasPrefix("PACKAGE-") } } - // TODO: we need to handle SPM's insane naming scheme for products here ^ including the potential of a dynamic variant - - /// The `PIF.BaseTarget` structure that backs this target - let baseTarget: PIF.BaseTarget - /// The `CompilerCommands` related to building this target - let commands: [CompilerCommand] - - /// Initializes a target with the given backing target and commands - /// - Parameters: - /// - baseTarget: the underlying `PIF.BaseTarget` - /// - commands: the commands related to this target - init(baseTarget: PIF.BaseTarget, commands: [CompilerCommand]) { - self.baseTarget = baseTarget - self.commands = commands + /// Returns true if this target produces a package product that will be included in the archive. + /// For simplicity we say this can be anything that is not a `PACKAGE-TARGET`, which will just be + /// an object file. The `dynamicTargetVariantGuid` of a `PACKAGE-TARGET` is technically a framework, + /// but we are using the `PACKAGE-PRODUCT` for that, which depends on it. + var isPackageProduct: Bool { + get { !guid.hasPrefix("PACKAGE-TARGET:") } } -} -extension Target { - /// Helper function to map targets and commands to an array of targets - /// - Parameters: - /// - targets: the `PIF.BaseTarget`s that will back the new targets - /// - targetsToCommands: a mapping of target names to the `CompilerCommands` that relate to them - /// - Returns: the newly created targets - static func targets(from targets: [PIF.BaseTarget], with targetsToCommands: [String: [CompilerCommand]]) -> [Target] { - targets - .map { - Target(baseTarget: $0, commands: targetsToCommands[$0.name] ?? []) - } + var isSwiftPackage: Bool { + get { guid.hasPrefix("PACKAGE-") } } + + init(from baseTarget: PIF.BaseTarget) { + guid = baseTarget.guid + name = baseTarget.name + productName = if let target = baseTarget as? PIF.Target, !target.productName.isEmpty { + target.productName + } else if baseTarget.guid == "PACKAGE-PRODUCT:\(baseTarget.name)" { + // The `PACKAGE-PRODUCT` for a package does not have a product reference name so + // we do not have a proper extension. For now we assume it is a framework and add + // the extension. A better way may be to follow the `dynamicTargetVariantGuid` of + // the `PACKAGE-TARGET` as this appears to provide the correct name if available. + baseTarget.name.appending(".framework") + } else { + // Fallback to the target's name if we are unable to determine a proper product name. + baseTarget.name + } + } } extension Target: NodeValue { From e1c0f455ef48bd12e32a77da2c8ea400e14b7f8b Mon Sep 17 00:00:00 2001 From: bmxav <5422569+bmxav@users.noreply.github.com> Date: Wed, 10 Jul 2024 10:47:18 -0400 Subject: [PATCH 18/21] Attempt to resolve Objective-C dependencies. --- Sources/GenIR/PIFCache.swift | 26 +++++++++++++++++--------- 1 file changed, 17 insertions(+), 9 deletions(-) diff --git a/Sources/GenIR/PIFCache.swift b/Sources/GenIR/PIFCache.swift index b8e4c11..1b84f7d 100644 --- a/Sources/GenIR/PIFCache.swift +++ b/Sources/GenIR/PIFCache.swift @@ -113,20 +113,29 @@ class PIFCache { .flatMap { fileReferences(for: $0) } .filter { $0.fileType == "wrapper.framework" } - // Now, stupidly, we have to do a name lookup on the path and use that to look up a target let frameworks = targets .compactMap { $0 as? PIF.Target } .filter { $0.productType == .framework } + + let frameworkGUIDs = frameworks .reduce(into: [String: PIF.Target]()) { partial, target in - let key = target.productName.isEmpty ? target.guid : target.productName - partial[key] = target + partial[target.guid] = target } - return frameworkFileReferences + // Map product names to targets + let frameworkProducts = frameworks + .reduce(into: [String: PIF.Target]()) { partial, target in + if !target.productName.isEmpty { + partial[target.productName] = target + } + } + + let frameworkReferenceTargets = frameworkFileReferences .reduce(into: [PIF.GUID: PIF.Target]()) { partial, fileReference in - // Use the _file reference_ GUID as the key here - we're looking up frameworks by their file reference and not target GUID! - partial[fileReference.guid] = frameworks[fileReference.path] + partial[fileReference.guid] = frameworkProducts[fileReference.path] } + + return frameworkGUIDs.merging(frameworkReferenceTargets) { _, new in new } }() } @@ -225,11 +234,10 @@ struct PIFDependencyProvider: DependencyProviding { let frameworkGUIDs = cache.target(guid: value.guid)? .buildPhases .flatMap { $0.buildFiles } - // .compactMap { $0 as? PIF.FrameworksBuildPhase } .compactMap { switch $0.reference { - case let .file(guid): return guid - case .target: return nil + case let .file(guid): return cache.frameworks[guid]?.guid + case .target(let guid): return guid } } .compactMap { cache.frameworks[$0]?.guid } From b6bccec2fbd0b78c8ccf53ee0ee14e5eb579e928 Mon Sep 17 00:00:00 2001 From: bmxav <5422569+bmxav@users.noreply.github.com> Date: Wed, 10 Jul 2024 10:47:18 -0400 Subject: [PATCH 19/21] Update unit tests. --- Tests/GenIRTests/MultipleAppTests.swift | 4 +-- Tests/GenIRTests/TestContext.swift | 2 +- Tests/GenIRTests/UmbrellaTests.swift | 8 ++--- Tests/GenIRTests/WorkspaceTests.swift | 43 +++++-------------------- 4 files changed, 15 insertions(+), 42 deletions(-) diff --git a/Tests/GenIRTests/MultipleAppTests.swift b/Tests/GenIRTests/MultipleAppTests.swift index 9791fa0..90b0042 100644 --- a/Tests/GenIRTests/MultipleAppTests.swift +++ b/Tests/GenIRTests/MultipleAppTests.swift @@ -18,7 +18,7 @@ final class MultipleAppTests: XCTestCase { let app = try XCTUnwrap(targets.first(where: { $0.name == "MultipleApp" })) let copy = try XCTUnwrap(targets.first(where: { $0.name == "MultipleApp Copy" })) - XCTAssertEqual(app.commands.count, 3) - XCTAssertEqual(copy.commands.count, 3) + XCTAssertEqual(context.logParser.targetCommands[app.name]?.count, 3) + XCTAssertEqual(context.logParser.targetCommands[copy.name]?.count, 3) } } diff --git a/Tests/GenIRTests/TestContext.swift b/Tests/GenIRTests/TestContext.swift index 40f7230..401fa92 100644 --- a/Tests/GenIRTests/TestContext.swift +++ b/Tests/GenIRTests/TestContext.swift @@ -145,7 +145,7 @@ class TestContext { /// Generate the Targets for this context lazy var targets: [Target] = { - Target.targets(from: pifCache.targets, with: logParser.targetCommands) + pifCache.targets.map { Target(from: $0) } }() /// Generate the dependency graph for this context diff --git a/Tests/GenIRTests/UmbrellaTests.swift b/Tests/GenIRTests/UmbrellaTests.swift index 13a40af..2b86381 100644 --- a/Tests/GenIRTests/UmbrellaTests.swift +++ b/Tests/GenIRTests/UmbrellaTests.swift @@ -11,10 +11,10 @@ final class UmbrellaTests: XCTestCase { let scheme = "Umbrella" let targetsToFiles = [ - "Common.framework": ["OrgModel.bc"].sorted(), - "Networking.framework": ["Networking.bc"].sorted(), - "Pods_Umbrella.framework": [], - "Umbrella.framework": ["GetOrg.bc"].sorted() + "Common.framework": ["Common-dummy.bc", "Common_vers.bc", "OrgModel.bc"].sorted(), + "Networking.framework": ["Networking-dummy.bc", "Networking.bc", "Networking_vers.bc"].sorted(), + "Pods_Umbrella.framework": ["Pods-Umbrella-dummy.bc", "Pods_Umbrella_vers.bc"], + "Umbrella.framework": ["GetOrg.bc", "Umbrella_vers.bc"].sorted() ] func testUmbrellaTargets() throws { diff --git a/Tests/GenIRTests/WorkspaceTests.swift b/Tests/GenIRTests/WorkspaceTests.swift index 711e66f..d9f56d3 100644 --- a/Tests/GenIRTests/WorkspaceTests.swift +++ b/Tests/GenIRTests/WorkspaceTests.swift @@ -11,8 +11,8 @@ final class WorkspaceTests: XCTestCase { let scheme = "App" static let appIRFiles: Set = ["AppApp.bc", "ContentView.bc", "GeneratedAssetSymbols.bc"] - static let commonIRFiles: Set = ["Model.bc"] - static let frameworkIRFiles: Set = ["Framework.bc"] + static let commonIRFiles: Set = ["Model.bc", "Common_vers.bc"] + static let frameworkIRFiles: Set = ["Framework.bc", "Framework_vers.bc"] static let sfSafeSymbolsIRFiles: Set = [ "NSImageExtension.bc", "SFSymbol+1.0.bc", @@ -62,45 +62,18 @@ final class WorkspaceTests: XCTestCase { dumpDependencyGraph: false ) - // Check dependencies made it to the right place + // Check dependencies made it to the right place. All dependencies should be statically + // linked and appear under the .app directory. let appIRPath = context.archive.appending(path: "IR/App.app/") - let commonIRPath = context.archive.appending(path: "IR/Common.framework/") - let frameworkIRPath = context.archive.appending(path: "IR/Framework.framework/") - let sfSafeSymbolsIRPath = context.archive.appending(path: "IR/SFSafeSymbols.framework/") - let appIRPathContents = try FileManager.default.contentsOfDirectory(at: appIRPath, includingPropertiesForKeys: nil) - .reduce(into: Set(), { $0.insert($1.lastPathComponent) }) - let commonIRPathContents = try FileManager.default.contentsOfDirectory(at: commonIRPath, includingPropertiesForKeys: nil) - .reduce(into: Set(), { $0.insert($1.lastPathComponent) }) - let frameworkIRPathContents = try FileManager.default.contentsOfDirectory(at: frameworkIRPath, includingPropertiesForKeys: nil) - .reduce(into: Set(), { $0.insert($1.lastPathComponent) }) - let sfSafeSymbolsIRPathContents = try FileManager.default.contentsOfDirectory(at: sfSafeSymbolsIRPath, includingPropertiesForKeys: nil) - .reduce(into: Set(), { $0.insert($1.lastPathComponent) }) - - let expectedAppIRFiles = Self.appIRFiles + let expectedIRFiles = Self.appIRFiles .union(Self.commonIRFiles) .union(Self.frameworkIRFiles) .union(Self.sfSafeSymbolsIRFiles) - .reduce(into: Set(), { $0.insert($1) }) - let expectedFrameworkIRFiles = Self.frameworkIRFiles - .union(Self.commonIRFiles) - .union(Self.sfSafeSymbolsIRFiles) - .reduce(into: Set(), { $0.insert($1) }) - - let expectedCommonIRFiles = Self.commonIRFiles - .reduce(into: Set(), { $0.insert($1) }) - - let expectedSFSafeSymbolsIRFiles = Self.sfSafeSymbolsIRFiles - .reduce(into: Set(), { $0.insert($1) }) + let appIRPathContents = try FileManager.default.contentsOfDirectory(at: appIRPath, includingPropertiesForKeys: nil) + .reduce(into: Set(), { $0.insert($1.lastPathComponent) }) - XCTAssertEqual(expectedAppIRFiles, appIRPathContents, "App IR expected contents didn't equal actual: \(expectedAppIRFiles.symmetricDifference(appIRPathContents))") - XCTAssertEqual(expectedFrameworkIRFiles, frameworkIRPathContents, "Framework IR expected contents didn't equal actual \(expectedFrameworkIRFiles.symmetricDifference(frameworkIRPathContents))") - XCTAssertEqual(expectedCommonIRFiles, commonIRPathContents, "Common IR expected contents didn't equal actual \(expectedCommonIRFiles.symmetricDifference(commonIRPathContents))") - XCTAssertEqual( - expectedSFSafeSymbolsIRFiles, - sfSafeSymbolsIRPathContents, - "SFSafeSymbols IR expected contents didn't equal actual \(expectedSFSafeSymbolsIRFiles.symmetricDifference(sfSafeSymbolsIRPathContents))" - ) + XCTAssertEqual(expectedIRFiles, appIRPathContents, "App IR expected contents didn't equal actual: \(expectedIRFiles.symmetricDifference(appIRPathContents))") } } From f442e1c0ede9aa4f46c323d7ca3f4ac88c4b56e1 Mon Sep 17 00:00:00 2001 From: bmxav <5422569+bmxav@users.noreply.github.com> Date: Wed, 10 Jul 2024 10:47:18 -0400 Subject: [PATCH 20/21] Cleanup some code and fix lint issues. --- Sources/GenIR/CompilerCommandRunner.swift | 6 ++- Sources/GenIR/GenIR.swift | 2 +- Sources/GenIR/PIFCache.swift | 2 +- Sources/GenIR/Target.swift | 64 +++++++++++------------ 4 files changed, 36 insertions(+), 38 deletions(-) diff --git a/Sources/GenIR/CompilerCommandRunner.swift b/Sources/GenIR/CompilerCommandRunner.swift index ae304ba..6e25247 100644 --- a/Sources/GenIR/CompilerCommandRunner.swift +++ b/Sources/GenIR/CompilerCommandRunner.swift @@ -51,12 +51,14 @@ struct CompilerCommandRunner { // Quick, do a hack! try buildCacheManipulator.manipulate() - let totalCommands = commands.reduce(0) { $0 + $1.value.count } + let totalCommands = commands + .map { $0.value.count } + .reduce(0, +) logger.info("Total commands to run: \(totalCommands)") var totalModulesRun = 0 - for target in targets.filter({ $0.containsBitcode }) { + for target in targets.filter({ $0.isBuildable }) { guard let targetCommands = commands[target.name] else { continue } diff --git a/Sources/GenIR/GenIR.swift b/Sources/GenIR/GenIR.swift index 0c2d6af..d996497 100644 --- a/Sources/GenIR/GenIR.swift +++ b/Sources/GenIR/GenIR.swift @@ -152,7 +152,7 @@ let programName = CommandLine.arguments.first! let postprocessor = try OutputPostprocessor( archive: archive, - output:tempDirectory, + output: tempDirectory, graph: graph ) diff --git a/Sources/GenIR/PIFCache.swift b/Sources/GenIR/PIFCache.swift index 1b84f7d..ad6500b 100644 --- a/Sources/GenIR/PIFCache.swift +++ b/Sources/GenIR/PIFCache.swift @@ -231,7 +231,7 @@ struct PIFDependencyProvider: DependencyProviding { // Framework build phase dependencies // NOTE: Previously we just cast this - all of a sudden with pods this is broken // Not the end of the world - just as quick to do a dictionary lookup - let frameworkGUIDs = cache.target(guid: value.guid)? + let frameworkGUIDs = cache.target(guid: value.guid)? .buildPhases .flatMap { $0.buildFiles } .compactMap { diff --git a/Sources/GenIR/Target.swift b/Sources/GenIR/Target.swift index e9ce651..e609d46 100644 --- a/Sources/GenIR/Target.swift +++ b/Sources/GenIR/Target.swift @@ -14,48 +14,44 @@ import DependencyGraph /// `PIF` package will likely be modified so that it is usable within the context of Gen-IR /// directly. class Target { - /// Target identifier. - let guid: String - /// Name of the target. - let name: String - /// The product name refers to the output of this target when it is built or copied into an archive. - let productName: String + /// Target identifier. + let guid: String + /// Name of the target. + let name: String + /// The product name refers to the output of this target when it is built or copied into an archive. + let productName: String - /// Returns true if this target produces bitcode. This is used to determine if this target is buildable. - /// For packages we only use the `PACKAGE-TARGET` for the object file, since this is used by the - /// other targets. - var containsBitcode: Bool { - get { guid == "PACKAGE-TARGET:\(name)" || !guid.hasPrefix("PACKAGE-") } - } + /// Returns true if this target is buildable. For packages we only use the `PACKAGE-TARGET` + /// for the object file, since this is used by the other targets. + let isBuildable: Bool /// Returns true if this target produces a package product that will be included in the archive. /// For simplicity we say this can be anything that is not a `PACKAGE-TARGET`, which will just be /// an object file. The `dynamicTargetVariantGuid` of a `PACKAGE-TARGET` is technically a framework, /// but we are using the `PACKAGE-PRODUCT` for that, which depends on it. - var isPackageProduct: Bool { - get { !guid.hasPrefix("PACKAGE-TARGET:") } - } + let isPackageProduct: Bool - var isSwiftPackage: Bool { - get { guid.hasPrefix("PACKAGE-") } - } + let isSwiftPackage: Bool - init(from baseTarget: PIF.BaseTarget) { - guid = baseTarget.guid - name = baseTarget.name - productName = if let target = baseTarget as? PIF.Target, !target.productName.isEmpty { - target.productName - } else if baseTarget.guid == "PACKAGE-PRODUCT:\(baseTarget.name)" { - // The `PACKAGE-PRODUCT` for a package does not have a product reference name so - // we do not have a proper extension. For now we assume it is a framework and add - // the extension. A better way may be to follow the `dynamicTargetVariantGuid` of - // the `PACKAGE-TARGET` as this appears to provide the correct name if available. - baseTarget.name.appending(".framework") - } else { - // Fallback to the target's name if we are unable to determine a proper product name. - baseTarget.name - } - } + init(from baseTarget: PIF.BaseTarget) { + guid = baseTarget.guid + name = baseTarget.name + productName = if let target = baseTarget as? PIF.Target, !target.productName.isEmpty { + target.productName + } else if baseTarget.guid == "PACKAGE-PRODUCT:\(baseTarget.name)" { + // The `PACKAGE-PRODUCT` for a package does not have a product reference name so + // we do not have a proper extension. For now we assume it is a framework and add + // the extension. A better way may be to follow the `dynamicTargetVariantGuid` of + // the `PACKAGE-TARGET` as this appears to provide the correct name if available. + baseTarget.name.appending(".framework") + } else { + // Fallback to the target's name if we are unable to determine a proper product name. + baseTarget.name + } + isBuildable = guid == "PACKAGE-TARGET:\(name)" || !guid.hasPrefix("PACKAGE-") + isPackageProduct = !guid.hasPrefix("PACKAGE-TARGET:") + isSwiftPackage = guid.hasPrefix("PACKAGE-") + } } extension Target: NodeValue { From 8f7ae61eb372a1f5397fb9ecb886f17190ec4a6a Mon Sep 17 00:00:00 2001 From: bmxav <5422569+bmxav@users.noreply.github.com> Date: Thu, 11 Jul 2024 13:39:03 -0400 Subject: [PATCH 21/21] Fix an issue where duplicate files were being generated. --- Sources/GenIR/GenIR.swift | 2 +- Sources/GenIR/OutputPostprocessor.swift | 199 +++++++----------------- 2 files changed, 58 insertions(+), 143 deletions(-) diff --git a/Sources/GenIR/GenIR.swift b/Sources/GenIR/GenIR.swift index d996497..50047cb 100644 --- a/Sources/GenIR/GenIR.swift +++ b/Sources/GenIR/GenIR.swift @@ -152,7 +152,7 @@ let programName = CommandLine.arguments.first! let postprocessor = try OutputPostprocessor( archive: archive, - output: tempDirectory, + build: tempDirectory, graph: graph ) diff --git a/Sources/GenIR/OutputPostprocessor.swift b/Sources/GenIR/OutputPostprocessor.swift index a68ee15..790c840 100644 --- a/Sources/GenIR/OutputPostprocessor.swift +++ b/Sources/GenIR/OutputPostprocessor.swift @@ -28,118 +28,84 @@ class OutputPostprocessor { /// The targets in this archive private let targets: [Target] - private lazy var guidToTargets: [String: Target] = { - targets.reduce(into: [String: Target]()) { partial, target in - partial[target.guid] = target - } - }() - - /// A mapping of targets to their path on disk - private lazy var targetsToPaths: [Target: URL] = { - let namesToTargets = targets - .reduce(into: [String: Target]()) { partial, target in - partial[target.productName] = target - } - - return (try? manager - .directories(at: output, recursive: false) - .reduce(into: [Target: URL]()) { partial, path in - if let target = namesToTargets[path.lastPathComponent] { - partial[target] = path - } else { - logger.error("Path (\(path.lastPathComponent)) wasn't found in namesToTargets: \(namesToTargets)") - } - }) ?? [:] - }() - - /// Path to the IR output folder - private let output: URL + /// Path to the folder containing build artifacts + private let build: URL /// The manager to use for file system access private let manager: FileManager = .default + /// Cache of all files that have been copied to the IR output folder. + /// This maps a destination to a set of source URLs. This is used to determine if a file has already + /// been copied without having to waste cycles comparing file contents. If multiple source files + /// map to the same destination, then they will be given unique file names when copied to the IR folder. + private var copiedFiles: [URL: Set] = [:] + /// Initializes the postprocessor - init(archive: URL, output: URL, graph: DependencyGraph) throws { + init(archive: URL, build: URL, graph: DependencyGraph) throws { self.archive = archive - self.output = output + self.build = build self.graph = graph self.targets = graph.nodes.map { $0.value.value } } /// Starts the OutputPostprocessor - /// - Parameter targets: the targets to operate on func process() throws { - var processed: Set = [] + let nodes = targets.compactMap { graph.findNode(for: $0) } + let output = archive.appendingPathComponent("IR") - _ = try targets - .filter { $0.isPackageProduct } - .map { try process(target: $0, processed: &processed) } + try manager.createDirectory(at: output, withIntermediateDirectories: false) - logger.info("Processed \(processed.count) top-level IR targets") + for node in nodes { + let dependers = node.edges.filter { $0.relationship == .depender } - try copyToArchive() - } + guard dynamicDependencyToPath[node.value.productName] != nil || (dependers.count == 0 && !node.value.isSwiftPackage) else { + continue + } - /// Processes an individual target - /// - Parameters: - /// - target: the target to process - /// - Returns: - private func process(target: Target, processed: inout Set) throws { - guard processed.insert(target).inserted else { - return - } + let irDirectory = output.appendingPathComponent(node.value.productName) + let buildDirectory = build.appendingPathComponent(node.value.productName) - let targetDirectory = output.appendingPathComponent(target.productName) + logger.debug("Copying \(node.value.guid) with name \(node.value.productName)") - // This directory may already exist if the `CompilerCommandRunner` has already built an - // artifact at this location. - try? FileManager.default.createDirectory(at: targetDirectory, withIntermediateDirectories: false) + // If there is a build directory for this target then copy the artifacts over to the IR + // folder. Otherwise we will create an empty directory and that will contain the artifacts + // of the dependency chain. + if manager.directoryExists(at: buildDirectory) { + try manager.copyItem(at: buildDirectory, to: irDirectory) + } else { + try manager.createDirectory(at: irDirectory, withIntermediateDirectories: false) + } - let chain = graph.chain(for: target) + // Copy over this target's static dependencies + var processed: Set = [] + try copyDependencies(for: node.value, to: irDirectory, processed: &processed) + } + } - logger.debug("Chain for target: \(target.productName):\n\(chain.map { "\($0)\n" })") - chain.forEach { logger.debug("\($0)") } + private func copyDependencies(for target: Target, to irDirectory: URL, processed: inout Set) throws { + guard processed.insert(target).inserted else { + return + } - for node in chain { + for node in graph.chain(for: target) { logger.debug("Processing Node: \(node.valueName)") - // Ensure node is not a dynamic dependency - guard dynamicDependencyToPath[node.value.productName] == nil else { continue } - try process(target: node.value, processed: &processed) - - let nodeFolderPath = output.appendingPathComponent(node.value.productName) - - do { - try copyContentsOfDirectoryMergingDifferingFiles(at: nodeFolderPath, to: targetDirectory) - } catch { - logger.debug("Copy error: \(error)") - } - } - } + // Do not copy dynamic dependencies + guard dynamicDependencyToPath[node.value.productName] == nil else { continue } - private func copyToArchive() throws { - let irDirectory = archive.appendingPathComponent("IR") - try? FileManager.default.createDirectory(at: irDirectory, withIntermediateDirectories: false) + try copyDependencies(for: node.value, to: irDirectory, processed: &processed) - let nodes = targets.compactMap { graph.findNode(for: $0) } - - for node in nodes { - let dependers = node.edges.filter { $0.relationship == .depender } - let targetPath = output.appendingPathComponent(node.value.productName) - let archivePath = irDirectory.appendingPathComponent(node.value.productName) - - if dynamicDependencyToPath[node.value.productName] != nil || (dependers.count == 0 && !node.value.isSwiftPackage) { - logger.debug("Copying \(node.value.productName) with name \(node.value.productName)") - if !FileManager.default.directoryExists(at: targetPath) { - logger.debug("No target found for target \(node.value.guid) with name \(node.value.productName)") - continue + let buildDirectory = build.appendingPathComponent(node.value.productName) + if manager.directoryExists(at: buildDirectory) { + do { + try copyContentsOfDirectoryMergingDifferingFiles(at: buildDirectory, to: irDirectory) + } catch { + logger.debug("Copy error: \(error)") } - try FileManager.default.copyItem(at: targetPath, to: archivePath) } } } - // TODO: Write tests for this. /// Copies the contents of a directory from source to destination, merging files that share the same name but differ in attributes /// - Parameters: /// - source: the source directory to copy the contents of @@ -147,72 +113,21 @@ class OutputPostprocessor { func copyContentsOfDirectoryMergingDifferingFiles(at source: URL, to destination: URL) throws { let files = try manager.contentsOfDirectory(at: source, includingPropertiesForKeys: nil) - /// Source and destination paths - typealias SourceAndDestination = (source: URL, destination: URL) - // Get two arrays of file paths of the source and destination file where: - // 1) destination already exists - // 2) destination doesn't already exist - let (existing, nonexisting) = files + let sourceAndDestinations = files .map { ( source: source.appendingPathComponent($0.lastPathComponent), destination: destination.appendingPathComponent($0.lastPathComponent) ) } - .reduce(into: (existing: [SourceAndDestination](), nonexisting: [SourceAndDestination]())) { (partialResult, sourceAndDestination) in - if manager.fileExists(atPath: sourceAndDestination.destination.filePath) { - partialResult.existing.append(sourceAndDestination) - } else { - partialResult.nonexisting.append(sourceAndDestination) - } - } - // Nonexisting files are easy - just move them - try nonexisting - .forEach { (source, destination) in - try manager.copyItem(at: source, to: destination) - } + for (source, destination) in sourceAndDestinations where copiedFiles[destination, default: []].insert(source).inserted { + // Avoid overwriting existing files with the same name. + let uniqueDestinationURL = manager.uniqueFilename(directory: destination.deletingLastPathComponent(), filename: source.lastPathComponent) - // Existing files require some additional checks and renaming - try existing - .forEach { - try copyFileUniquingConflictingFiles(source: $0.source, destination: $0.destination) - } - } - - /// The size and creation date of a file system item - private typealias SizeAndCreation = (Int, Date) - - /// A cache of seen files and their associated metadata - private var seenConflictingFiles: [URL: [SizeAndCreation]] = [:] - - /// Copies a file, uniquing the path if it conflicts, _if_ the files they conflict with aren't the same size - /// - Parameters: - /// - source: source file path - /// - destination: destination file path - private func copyFileUniquingConflictingFiles(source: URL, destination: URL) throws { - let destinationAttributes = try manager.attributesOfItem(atPath: destination.filePath) - let sourceAttributes = try manager.attributesOfItem(atPath: source.filePath) - - guard - let destinationSize = destinationAttributes[.size] as? Int, - let sourceSize = sourceAttributes[.size] as? Int, - let destinationCreatedDate = destinationAttributes[.creationDate] as? Date, - let sourceCreatedDate = sourceAttributes[.creationDate] as? Date - else { - logger.debug("Failed to get attributes for source: \(source) & destination: \(destination)") - return - } - - let uniqueDestinationURL = manager.uniqueFilename(directory: destination.deletingLastPathComponent(), filename: source.lastPathComponent) - - for (size, date) in seenConflictingFiles[source, default: [(sourceSize, sourceCreatedDate)]] where size == destinationSize && date == destinationCreatedDate { - return + logger.debug("Copying source \(source) to destination: \(uniqueDestinationURL)") + try manager.copyItem(at: source, to: uniqueDestinationURL) } - - seenConflictingFiles[source, default: [(sourceSize, sourceCreatedDate)]].append((destinationSize, destinationCreatedDate)) - logger.debug("Copying source \(source) to destination: \(uniqueDestinationURL)") - try manager.copyItem(at: source, to: uniqueDestinationURL) } /// Returns a map of dynamic objects in the provided path @@ -224,7 +139,7 @@ class OutputPostprocessor { let dynamicDependencyExtensions = ["framework", "appex", "app"] - return FileManager.default.filteredContents(of: searchPath, filter: { path in + return manager.filteredContents(of: searchPath, filter: { path in return dynamicDependencyExtensions.contains(path.pathExtension) }) .reduce(into: [String: URL]()) { partialResult, path in @@ -247,8 +162,8 @@ class OutputPostprocessor { /// - Returns: the first directory found in the path if one exists func firstDirectory(at path: URL) -> URL? { guard - FileManager.default.directoryExists(at: path), - let contents = try? FileManager.default.directories(at: path, recursive: false), + manager.directoryExists(at: path), + let contents = try? manager.directories(at: path, recursive: false), contents.count < 0 else { return nil